|
@@ -19,6 +19,7 @@ import com.fs.qw.dto.acquisition.*;
|
|
|
import com.fs.qw.enums.SmsLogType;
|
|
import com.fs.qw.enums.SmsLogType;
|
|
|
import com.fs.qw.mapper.QwAcquisitionAssistantMapper;
|
|
import com.fs.qw.mapper.QwAcquisitionAssistantMapper;
|
|
|
import com.fs.qw.service.IQwAcquisitionAssistantService;
|
|
import com.fs.qw.service.IQwAcquisitionAssistantService;
|
|
|
|
|
+import com.fs.qw.service.IQwAcquisitionLinkInfoService;
|
|
|
import com.fs.qw.service.IQwCompanyService;
|
|
import com.fs.qw.service.IQwCompanyService;
|
|
|
import com.fs.qw.utils.UniqueStringUtil;
|
|
import com.fs.qw.utils.UniqueStringUtil;
|
|
|
import com.fs.qw.vo.AcquisitionAssistantDetailVO;
|
|
import com.fs.qw.vo.AcquisitionAssistantDetailVO;
|
|
@@ -42,7 +43,7 @@ import java.util.concurrent.TimeUnit;
|
|
|
public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistantService {
|
|
public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistantService {
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
- private QwAcquisitionAssistantMapper qwAcquisitionAssistantMapper;
|
|
|
|
|
|
|
+ private QwAcquisitionAssistantMapper acquisitionAssistantMapper;
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private IWeixinKfService weixinKfService;
|
|
private IWeixinKfService weixinKfService;
|
|
@@ -58,11 +59,13 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private ICompanySmsTempService smsTempService;
|
|
private ICompanySmsTempService smsTempService;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private IQwAcquisitionLinkInfoService linkInfoService;
|
|
|
|
|
|
|
|
// 获客链接管理-企微的ACCESS_TOKEN的key
|
|
// 获客链接管理-企微的ACCESS_TOKEN的key
|
|
|
private static final String QW_ACQUISITION_KEY_PREFIX = "qw:acquisition:key:";
|
|
private static final String QW_ACQUISITION_KEY_PREFIX = "qw:acquisition:key:";
|
|
|
|
|
|
|
|
-
|
|
|
|
|
// 获客链接-页面参数-url的key
|
|
// 获客链接-页面参数-url的key
|
|
|
private static final String QW_ACQUISITION_URL_KEY_PREFIX = "qw:acquisition:url:key:";
|
|
private static final String QW_ACQUISITION_URL_KEY_PREFIX = "qw:acquisition:url:key:";
|
|
|
|
|
|
|
@@ -72,174 +75,6 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
//访问链接域名
|
|
//访问链接域名
|
|
|
private static final String LINK_DOMAIN = "https://c.ysyd.top/";
|
|
private static final String LINK_DOMAIN = "https://c.ysyd.top/";
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 获取access_token并返回完整URL
|
|
|
|
|
- */
|
|
|
|
|
- private String buildApiUrl(String corpid, String corpsecret, String apiPath) {
|
|
|
|
|
- WeixinKfTokenVO token = getAccessToken(corpid, corpsecret);
|
|
|
|
|
-
|
|
|
|
|
- if (token == null || StringUtils.isEmpty(token.getAccess_token())) {
|
|
|
|
|
- log.error("获取access_token失败, corpid:{}", corpid);
|
|
|
|
|
- throw new CustomException("获客链接管理-获取企业微信access_token失败");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return apiPath + "?access_token=" + token.getAccess_token();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 获取access_token(使用实际有效期)
|
|
|
|
|
- */
|
|
|
|
|
- private WeixinKfTokenVO getAccessToken(String corpid, String corpsecret) {
|
|
|
|
|
- String key = QW_ACQUISITION_KEY_PREFIX + corpid;
|
|
|
|
|
-
|
|
|
|
|
- // 1. 先从缓存获取
|
|
|
|
|
- WeixinKfTokenVO token = null;
|
|
|
|
|
- try {
|
|
|
|
|
- Object cacheObj = redisCache.getCacheObject(key);
|
|
|
|
|
- if (cacheObj instanceof WeixinKfTokenVO) {
|
|
|
|
|
- token = (WeixinKfTokenVO) cacheObj;
|
|
|
|
|
- log.debug("从缓存获取token成功, corpid:{}", corpid);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.warn("从缓存获取token异常, 将重新获取, corpid:{}", corpid);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 2. 缓存中没有,则重新获取
|
|
|
|
|
- if (token == null) {
|
|
|
|
|
- log.info("缓存中没有token,重新获取, corpid:{}", corpid);
|
|
|
|
|
- try {
|
|
|
|
|
- // 获取新token
|
|
|
|
|
- token = weixinKfService.getToken(corpid, corpsecret);
|
|
|
|
|
-
|
|
|
|
|
- // 存入缓存,使用企业微信返回的实际有效期
|
|
|
|
|
- if (token != null && StringUtils.isNotEmpty(token.getAccess_token())) {
|
|
|
|
|
- Integer expiresIn = token.getExpires_in();
|
|
|
|
|
- if (expiresIn == null) {
|
|
|
|
|
- // 如果返回中没有expires_in,默认2小时
|
|
|
|
|
- expiresIn = 7200;
|
|
|
|
|
- log.warn("token返回中没有expires_in字段,使用默认值7200秒");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 实际缓存时间比有效期稍短(提前5分钟过期),避免边界问题
|
|
|
|
|
- Integer cacheExpire = expiresIn - 300; // 提前5分钟
|
|
|
|
|
- if (cacheExpire <= 0) {
|
|
|
|
|
- cacheExpire = expiresIn;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- redisCache.setCacheObject(key, token, cacheExpire, TimeUnit.SECONDS);
|
|
|
|
|
- log.info("token缓存成功, corpid:{}, 有效期:{}秒, 缓存时间:{}秒",
|
|
|
|
|
- corpid, expiresIn, cacheExpire);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("获取token失败, corpid:{}, error:{}", corpid, e.getMessage());
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return token;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 调用企微API并统一处理返回结果
|
|
|
|
|
- */
|
|
|
|
|
- private <T> T callQwApi(String qwApiUrl, Object request, Class<T> responseClass, String operationName) {
|
|
|
|
|
- log.info("调用企微{},参数:{}", operationName, JSON.toJSONString(request));
|
|
|
|
|
- String result = HttpUtil.sendAuthPost(request, qwApiUrl);
|
|
|
|
|
- log.info("企微{}响应:{}", operationName, result);
|
|
|
|
|
-
|
|
|
|
|
- if (StringUtils.isEmpty(result)) {
|
|
|
|
|
- throw new CustomException("调用企微API失败,返回为空");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- T response = JSON.parseObject(result, responseClass);
|
|
|
|
|
-
|
|
|
|
|
- // 通过反射获取errcode(所有响应都有这个字段)
|
|
|
|
|
- try {
|
|
|
|
|
- java.lang.reflect.Field errcodeField = responseClass.getDeclaredField("errcode");
|
|
|
|
|
- errcodeField.setAccessible(true);
|
|
|
|
|
- Integer errcode = (Integer) errcodeField.get(response);
|
|
|
|
|
-
|
|
|
|
|
- if (errcode != null && errcode != 0) {
|
|
|
|
|
- java.lang.reflect.Field errmsgField = responseClass.getDeclaredField("errmsg");
|
|
|
|
|
- errmsgField.setAccessible(true);
|
|
|
|
|
- String errmsg = (String) errmsgField.get(response);
|
|
|
|
|
- throw new CustomException("企微" + operationName + "失败:" + errmsg);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- if (e instanceof CustomException) {
|
|
|
|
|
- throw (CustomException) e;
|
|
|
|
|
- }
|
|
|
|
|
- log.error("解析企微响应失败", e);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return response;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 生成范围描述
|
|
|
|
|
- */
|
|
|
|
|
- private String generateRangeDesc(QwAcquisitionAssistant assistant) {
|
|
|
|
|
- StringBuilder desc = new StringBuilder();
|
|
|
|
|
- if (assistant.getUserListParam() != null && !assistant.getUserListParam().isEmpty()) {
|
|
|
|
|
- desc.append(assistant.getUserListParam().size()).append("名成员");
|
|
|
|
|
- }
|
|
|
|
|
- if (assistant.getDepartmentListParam() != null && !assistant.getDepartmentListParam().isEmpty()) {
|
|
|
|
|
- if (desc.length() > 0) desc.append(" + ");
|
|
|
|
|
- desc.append(assistant.getDepartmentListParam().size()).append("个部门");
|
|
|
|
|
- }
|
|
|
|
|
- return desc.length() > 0 ? desc.toString() : "未设置范围";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 生成scheme
|
|
|
|
|
- */
|
|
|
|
|
- private String generateScheme(String qwApiUrl) {
|
|
|
|
|
- if (StringUtils.isNotEmpty(qwApiUrl)) {
|
|
|
|
|
- try {
|
|
|
|
|
- String encodedUrl = java.net.URLEncoder.encode(qwApiUrl, "UTF-8");
|
|
|
|
|
- return "weixin://biz/ww/profile/" + encodedUrl;
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.warn("生成scheme失败", e);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 设置本地通用字段
|
|
|
|
|
- */
|
|
|
|
|
- private void setLocalFields(QwAcquisitionAssistant assistant, boolean isCreate) {
|
|
|
|
|
- if (isCreate) {
|
|
|
|
|
- assistant.setStatus(1);
|
|
|
|
|
- assistant.setDelFlag("0");
|
|
|
|
|
- assistant.setCreateTime(new Date());
|
|
|
|
|
- } else {
|
|
|
|
|
- assistant.setUpdateTime(new Date());
|
|
|
|
|
- }
|
|
|
|
|
- assistant.setSyncTime(DateUtils.getNowDate());
|
|
|
|
|
- assistant.buildJsonFields();
|
|
|
|
|
- assistant.setRangeDesc(generateRangeDesc(assistant));
|
|
|
|
|
-
|
|
|
|
|
- if (StringUtils.isEmpty(assistant.getScheme())) {
|
|
|
|
|
- assistant.setScheme(generateScheme(assistant.getUrl()));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // ==================== 列表相关方法 ====================
|
|
|
|
|
-
|
|
|
|
|
- @Override
|
|
|
|
|
- public AcquisitionListResponse getQwList(String corpid, String corpsecret, Integer limit, String cursor) {
|
|
|
|
|
- AcquisitionListRequest request = new AcquisitionListRequest();
|
|
|
|
|
- if (limit != null) {
|
|
|
|
|
- request.setLimit(limit > 100 ? 100 : limit);
|
|
|
|
|
- }
|
|
|
|
|
- request.setCursor(cursor);
|
|
|
|
|
-
|
|
|
|
|
- String qwApiUrl = buildApiUrl(corpid, corpsecret, QwApiConfig.listAcquisition);
|
|
|
|
|
- return callQwApi(qwApiUrl, request, AcquisitionListResponse.class, "获取获客链接列表");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 查询企微-获客链接管理列表
|
|
* 查询企微-获客链接管理列表
|
|
|
*
|
|
*
|
|
@@ -248,7 +83,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
*/
|
|
*/
|
|
|
@Override
|
|
@Override
|
|
|
public List<QwAcquisitionAssistant> selectQwAcquisitionAssistantList(QwAcquisitionAssistant qwAcquisitionAssistant) {
|
|
public List<QwAcquisitionAssistant> selectQwAcquisitionAssistantList(QwAcquisitionAssistant qwAcquisitionAssistant) {
|
|
|
- List<QwAcquisitionAssistant> list = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantList(qwAcquisitionAssistant);
|
|
|
|
|
|
|
+ List<QwAcquisitionAssistant> list = acquisitionAssistantMapper.selectQwAcquisitionAssistantList(qwAcquisitionAssistant);
|
|
|
if (CollectionUtils.isEmpty(list)) {
|
|
if (CollectionUtils.isEmpty(list)) {
|
|
|
return Collections.emptyList();
|
|
return Collections.emptyList();
|
|
|
}
|
|
}
|
|
@@ -260,6 +95,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
public SendResultDetailDTO sendMessageAcquisition(String phone, Long qwAcquisitionId, SendMsgLogBo sendMsgLogBo) {
|
|
public SendResultDetailDTO sendMessageAcquisition(String phone, Long qwAcquisitionId, SendMsgLogBo sendMsgLogBo) {
|
|
|
log.info("发送获客链接短信,号码:{}", phone);
|
|
log.info("发送获客链接短信,号码:{}", phone);
|
|
|
CompanySmsTemp temp = smsTempService.selectCompanySmsTempByCode(SMS_LINK_TEMPLATE_CODE);
|
|
CompanySmsTemp temp = smsTempService.selectCompanySmsTempByCode(SMS_LINK_TEMPLATE_CODE);
|
|
@@ -268,7 +104,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
throw new CustomException("获客链接-未找到短信模板");
|
|
throw new CustomException("获客链接-未找到短信模板");
|
|
|
}
|
|
}
|
|
|
String originalContent = temp.getContent();
|
|
String originalContent = temp.getContent();
|
|
|
- QwAcquisitionAssistant acquisitionAssistant = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantById(qwAcquisitionId);
|
|
|
|
|
|
|
+ QwAcquisitionAssistant acquisitionAssistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(qwAcquisitionId);
|
|
|
if (acquisitionAssistant == null){
|
|
if (acquisitionAssistant == null){
|
|
|
log.info("获客链接-未找到获客链接id:{}", qwAcquisitionId);
|
|
log.info("获客链接-未找到获客链接id:{}", qwAcquisitionId);
|
|
|
throw new CustomException("获客链接-未找到获客链接信息");
|
|
throw new CustomException("获客链接-未找到获客链接信息");
|
|
@@ -281,6 +117,10 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
R r = smsService.simpleSmsSend(phone, content, temp, SmsLogType.ACQUISITION_LINK, sendMsgLogBo);
|
|
R r = smsService.simpleSmsSend(phone, content, temp, SmsLogType.ACQUISITION_LINK, sendMsgLogBo);
|
|
|
|
|
|
|
|
if (r != null && "200".equals(String.valueOf(r.get("code")))) {
|
|
if (r != null && "200".equals(String.valueOf(r.get("code")))) {
|
|
|
|
|
+
|
|
|
|
|
+ //新增号码-链接生成记录
|
|
|
|
|
+ linkInfoService.buildQwAcquisitionLinkInfoAdd(acquisitionAssistant.getId(), phone, acquisitionAssistant.getUrl());
|
|
|
|
|
+
|
|
|
return new SendResultDetailDTO(true, null, null);
|
|
return new SendResultDetailDTO(true, null, null);
|
|
|
} else {
|
|
} else {
|
|
|
String msg = r != null && r.get("msg") != null ? r.get("msg").toString() : "未知错误";
|
|
String msg = r != null && r.get("msg") != null ? r.get("msg").toString() : "未知错误";
|
|
@@ -293,330 +133,94 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
- public String syncListFromQw(String corpid, String corpsecret) {
|
|
|
|
|
- String cursor = null;
|
|
|
|
|
- int totalCount = 0;
|
|
|
|
|
- int successCount = 0;
|
|
|
|
|
- int pageNum = 1;
|
|
|
|
|
|
|
+ // ==================== 获取详情方法 ====================
|
|
|
|
|
|
|
|
- log.info("开始同步企微获客链接列表");
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public AcquisitionAssistantDetailVO getDetailWithQw(String linkId) {
|
|
|
|
|
+ if (StringUtils.isEmpty(linkId)) {
|
|
|
|
|
+ throw new CustomException("链接ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 1. 查询本地记录
|
|
|
|
|
+ QwAcquisitionAssistant query = new QwAcquisitionAssistant();
|
|
|
|
|
+ query.setLinkId(linkId);
|
|
|
|
|
+ List<QwAcquisitionAssistant> localRecords = acquisitionAssistantMapper.selectQwAcquisitionAssistantList(query);
|
|
|
|
|
+ QwAcquisitionAssistant localData = localRecords.isEmpty() ? null : localRecords.get(0);
|
|
|
|
|
|
|
|
- do {
|
|
|
|
|
- log.info("正在同步第{}页", pageNum);
|
|
|
|
|
- AcquisitionListResponse listResponse = getQwList(corpid, corpsecret, 100, cursor);
|
|
|
|
|
|
|
+ // 2. 获取企业信息
|
|
|
|
|
+ String corpId = localData != null ? localData.getCorpId() : null;
|
|
|
|
|
+ QwCompany qwCompany = qwCompanyService.selectQwCompanyByCorpId(corpId);
|
|
|
|
|
+ if (qwCompany == null) {
|
|
|
|
|
+ throw new CustomException("未找到企业配置信息");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- List<String> linkIdList = listResponse.getLinkIdList();
|
|
|
|
|
- if (linkIdList == null || linkIdList.isEmpty()) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 3. 调用企微API获取详情
|
|
|
|
|
+ AcquisitionGetResponse qwDetail = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ AcquisitionGetRequest request = new AcquisitionGetRequest();
|
|
|
|
|
+ request.setLinkId(linkId);
|
|
|
|
|
|
|
|
- totalCount += linkIdList.size();
|
|
|
|
|
|
|
+ String qwApiUrl = buildApiUrl(qwCompany.getCorpId(), qwCompany.getOpenSecret(), QwApiConfig.getAcquisition);
|
|
|
|
|
+ qwDetail = callQwApi(qwApiUrl, request, AcquisitionGetResponse.class, "获取获客链接详情");
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("调用企微API失败", e);
|
|
|
|
|
+ // 如果API调用失败,就返回本地数据
|
|
|
|
|
+ }
|
|
|
|
|
+ // 4. 构建VO对象
|
|
|
|
|
+ return buildDetailVO(localData, qwDetail, qwCompany, linkId);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- for (String linkId : linkIdList) {
|
|
|
|
|
- try {
|
|
|
|
|
- AcquisitionAssistantDetailVO detail = getDetailWithQw(linkId);
|
|
|
|
|
- QwAcquisitionAssistant assistant = convertToLocal(linkId, detail);
|
|
|
|
|
|
|
+ // ==================== 新增方法 ====================
|
|
|
|
|
|
|
|
- QwAcquisitionAssistant existData = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantByLinkId(linkId);
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public QwAcquisitionAssistant createWithQw(String corpid, String corpsecret, QwAcquisitionAssistant assistant) {
|
|
|
|
|
+ // 参数校验
|
|
|
|
|
+ if (StringUtils.isEmpty(assistant.getLinkName())) {
|
|
|
|
|
+ throw new CustomException("链接名称不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (assistant.getLinkName().length() > 30) {
|
|
|
|
|
+ throw new CustomException("链接名称不能超过30个字符");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (existData != null) {
|
|
|
|
|
- assistant.setId(existData.getId());
|
|
|
|
|
- setLocalFields(assistant, false);
|
|
|
|
|
- qwAcquisitionAssistantMapper.updateQwAcquisitionAssistant(assistant);
|
|
|
|
|
- } else {
|
|
|
|
|
- setLocalFields(assistant, true);
|
|
|
|
|
- assistant.setCorpId(corpid);// 对于新增的获客链接默认corpId为当前传入的corpId
|
|
|
|
|
- qwAcquisitionAssistantMapper.insertQwAcquisitionAssistant(assistant);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 构建请求
|
|
|
|
|
+ JSONObject request = new JSONObject(); // 改用JSONObject以便灵活构建
|
|
|
|
|
+ request.put("link_name", assistant.getLinkName());
|
|
|
|
|
+ request.put("skip_verify", Boolean.parseBoolean(assistant.getSkipVerify()));
|
|
|
|
|
+ //request.put("mark_source", assistant.getMarkSource());
|
|
|
|
|
|
|
|
- successCount++;
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("同步链接详情失败,linkId:{}", linkId, e);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 构建range对象 - 即使没有值也要传空对象
|
|
|
|
|
+ JSONObject range = new JSONObject();
|
|
|
|
|
|
|
|
- cursor = listResponse.getNextCursor();
|
|
|
|
|
- pageNum++;
|
|
|
|
|
|
|
+ // 如果有成员列表
|
|
|
|
|
+ if (assistant.getUserListParam() != null && !assistant.getUserListParam().isEmpty()) {
|
|
|
|
|
+ range.put("user_list", assistant.getUserListParam());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } while (StringUtils.isNotEmpty(cursor));
|
|
|
|
|
|
|
+ // 如果有部门列表
|
|
|
|
|
+ if (assistant.getDepartmentListParam() != null && !assistant.getDepartmentListParam().isEmpty()) {
|
|
|
|
|
+ range.put("department_list", assistant.getDepartmentListParam());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- String resultMsg = String.format("同步完成,总链接数:%d,成功同步:%d", totalCount, successCount);
|
|
|
|
|
- log.info(resultMsg);
|
|
|
|
|
- return resultMsg;
|
|
|
|
|
- }
|
|
|
|
|
- /**
|
|
|
|
|
- * 将企微详情转换为本地实体
|
|
|
|
|
- */
|
|
|
|
|
- private QwAcquisitionAssistant convertToLocal(String linkId, AcquisitionAssistantDetailVO detail) {
|
|
|
|
|
- QwAcquisitionAssistant assistant = new QwAcquisitionAssistant();
|
|
|
|
|
- assistant.setLinkId(linkId);
|
|
|
|
|
|
|
+ // 无论是否有值,都添加range字段
|
|
|
|
|
+ request.put("range", range);
|
|
|
|
|
|
|
|
- assistant.setLinkName(detail.getLinkName());
|
|
|
|
|
- assistant.setUrl(detail.getUrl());
|
|
|
|
|
- assistant.setQwCreateTime(DateUtils.getNowDate());
|
|
|
|
|
- assistant.setSkipVerify(detail.getSkipVerify());
|
|
|
|
|
- //assistant.setMarkSource(detail.getLink().getMarkSource());
|
|
|
|
|
- assistant.setUserList(JSON.toJSONString(detail.getUserList()));
|
|
|
|
|
- assistant.setDepartmentList(JSON.toJSONString(detail.getDepartmentList()));
|
|
|
|
|
|
|
+ // 构建priority_option - 可选
|
|
|
|
|
+ if (assistant.getPriorityType() != null && assistant.getPriorityType() > 0) {
|
|
|
|
|
+ JSONObject priorityOption = new JSONObject();
|
|
|
|
|
+ priorityOption.put("priority_type", assistant.getPriorityType());
|
|
|
|
|
|
|
|
- assistant.setPriorityType(detail.getPriorityType());
|
|
|
|
|
- assistant.setPriorityUserList(JSON.toJSONString(detail.getPriorityUserList()));
|
|
|
|
|
|
|
+ // 如果priority_type为2,需要指定priority_userid_list
|
|
|
|
|
+ if (assistant.getPriorityType() == 2) {
|
|
|
|
|
+ if (assistant.getPriorityUserListParam() == null || assistant.getPriorityUserListParam().isEmpty()) {
|
|
|
|
|
+ throw new CustomException("优先分配类型为指定范围内时,优先分配成员不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ priorityOption.put("priority_userid_list", assistant.getPriorityUserListParam());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return assistant;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 构建统一的VO对象
|
|
|
|
|
- */
|
|
|
|
|
- private AcquisitionAssistantDetailVO buildDetailVO(QwAcquisitionAssistant localData,
|
|
|
|
|
- AcquisitionGetResponse qwDetail,
|
|
|
|
|
- QwCompany qwCompany,
|
|
|
|
|
- String linkId) {
|
|
|
|
|
- AcquisitionAssistantDetailVO vo = new AcquisitionAssistantDetailVO();
|
|
|
|
|
-
|
|
|
|
|
- // 处理企微API返回的数据
|
|
|
|
|
- if (qwDetail != null) {
|
|
|
|
|
- // 基础信息
|
|
|
|
|
- if (qwDetail.getLink() != null) {
|
|
|
|
|
- AcquisitionGetResponse.LinkDetail link = qwDetail.getLink();
|
|
|
|
|
- vo.setLinkId(linkId);
|
|
|
|
|
- vo.setLinkName(link.getLinkName());
|
|
|
|
|
- vo.setUrl(link.getUrl());
|
|
|
|
|
- vo.setSkipVerify(link.getSkipVerify());
|
|
|
|
|
- vo.setQwCreateTime(link.getCreateTime());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 范围信息处理
|
|
|
|
|
- if (qwDetail.getRange() != null) {
|
|
|
|
|
- AcquisitionGetResponse.RangeInfo range = qwDetail.getRange();
|
|
|
|
|
-
|
|
|
|
|
- // 将userList和departmentList组合成JSON字符串
|
|
|
|
|
- Map<String, Object> rangeUserListMap = new HashMap<>();
|
|
|
|
|
- Map<String, Object> rangeDepartmentListMap = new HashMap<>();
|
|
|
|
|
- rangeUserListMap.put("userList", range.getUserList() != null ? range.getUserList() : new ArrayList<>());
|
|
|
|
|
- rangeDepartmentListMap.put("departmentList", range.getDepartmentList() != null ? range.getDepartmentList() : new ArrayList<>());
|
|
|
|
|
- vo.setUserList(JSON.toJSONString(rangeUserListMap));
|
|
|
|
|
- vo.setDepartmentList(JSON.toJSONString(rangeDepartmentListMap));
|
|
|
|
|
-
|
|
|
|
|
- // 生成范围描述
|
|
|
|
|
- vo.setRangeDesc(buildRangeDesc(range));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 优先分配信息处理
|
|
|
|
|
- if (qwDetail.getPriorityOption() != null) {
|
|
|
|
|
- AcquisitionGetResponse.PriorityInfo priority = qwDetail.getPriorityOption();
|
|
|
|
|
- vo.setPriorityType(priority.getPriorityType() != null ? priority.getPriorityType() : 0);
|
|
|
|
|
-
|
|
|
|
|
- // 将优先分配成员列表转换为JSON字符串
|
|
|
|
|
- if (priority.getPriorityUseridList() != null) {
|
|
|
|
|
- vo.setPriorityUserList(JSON.toJSONString(priority.getPriorityUseridList()));
|
|
|
|
|
- } else {
|
|
|
|
|
- vo.setPriorityUserList("[]");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 补充本地数据
|
|
|
|
|
- if (localData != null) {
|
|
|
|
|
- // 如果企微数据中没有的字段,使用本地数据
|
|
|
|
|
- if (vo.getLinkId() == null) vo.setLinkId(localData.getLinkId());
|
|
|
|
|
- if (vo.getLinkName() == null) vo.setLinkName(localData.getLinkName());
|
|
|
|
|
- if (vo.getUrl() == null) vo.setUrl(localData.getUrl());
|
|
|
|
|
- if (vo.getSkipVerify() == null) vo.setSkipVerify(localData.getSkipVerify());
|
|
|
|
|
- if (vo.getUserList() == null) vo.setUserList(localData.getUserList());
|
|
|
|
|
- if (vo.getPriorityType() == null) vo.setPriorityType(localData.getPriorityType());
|
|
|
|
|
- if (vo.getPriorityUserList() == null) vo.setPriorityUserList(localData.getPriorityUserList());
|
|
|
|
|
- if (vo.getQwUserTableIdList() == null) vo.setQwUserTableIdList(localData.getQwUserTableIdList());
|
|
|
|
|
- if (vo.getRangeDesc() == null) vo.setRangeDesc(localData.getRangeDesc());
|
|
|
|
|
-
|
|
|
|
|
- // 本地记录特有的字段
|
|
|
|
|
- vo.setId(localData.getId());
|
|
|
|
|
- vo.setStatus(localData.getStatus());
|
|
|
|
|
- vo.setSyncTime(localData.getSyncTime());
|
|
|
|
|
- vo.setRemark(localData.getRemark());
|
|
|
|
|
- vo.setCorpId(localData.getCorpId());
|
|
|
|
|
- vo.setScheme(localData.getScheme());
|
|
|
|
|
- } else {
|
|
|
|
|
- // 纯企微数据,设置默认值
|
|
|
|
|
- vo.setStatus(1);
|
|
|
|
|
- vo.setCorpId(qwCompany.getCorpId());
|
|
|
|
|
- if (vo.getPriorityType() == null) vo.setPriorityType(0);
|
|
|
|
|
- if (vo.getPriorityUserList() == null) vo.setPriorityUserList("[]");
|
|
|
|
|
- if (vo.getUserList() == null) vo.setUserList("{\"userList\":[]}");
|
|
|
|
|
- if (vo.getDepartmentList() == null) vo.setDepartmentList("{\"departmentList\":[]}");
|
|
|
|
|
- if (vo.getRangeDesc() == null) vo.setRangeDesc("全企业");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return vo;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 生成范围描述 - 确保方法签名正确
|
|
|
|
|
- */
|
|
|
|
|
- private String buildRangeDesc(AcquisitionGetResponse.RangeInfo range) {
|
|
|
|
|
- if (range == null) {
|
|
|
|
|
- return "全企业";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- List<String> userList = range.getUserList();
|
|
|
|
|
- List<Integer> departmentList = range.getDepartmentList();
|
|
|
|
|
-
|
|
|
|
|
- StringBuilder desc = new StringBuilder();
|
|
|
|
|
-
|
|
|
|
|
- if (userList != null && !userList.isEmpty()) {
|
|
|
|
|
- desc.append("成员(").append(userList.size()).append("人)");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (departmentList != null && !departmentList.isEmpty()) {
|
|
|
|
|
- if (desc.length() > 0) {
|
|
|
|
|
- desc.append("、");
|
|
|
|
|
- }
|
|
|
|
|
- desc.append("部门(").append(departmentList.size()).append("个)");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return desc.length() > 0 ? desc.toString() : "全企业";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 生成唯一的页面参数(针对2000条数据的优化版本)
|
|
|
|
|
- */
|
|
|
|
|
- private String generateUniquePageParam() {
|
|
|
|
|
- // 获取所有已存在的pageParam(只取需要的字段)
|
|
|
|
|
- List<String> existingParams = qwAcquisitionAssistantMapper.selectAllPageParams();
|
|
|
|
|
- //使用Set,提高查找效率 O(1)
|
|
|
|
|
- Set<String> paramSet = new HashSet<>(existingParams);
|
|
|
|
|
-
|
|
|
|
|
- int maxAttempts = 10; // 设置最大尝试次数
|
|
|
|
|
- int attempt = 0;
|
|
|
|
|
-
|
|
|
|
|
- while (attempt < maxAttempts) {
|
|
|
|
|
- // 生成6位随机码
|
|
|
|
|
- String candidate = UniqueStringUtil.generateTimeBasedUnique(6);
|
|
|
|
|
-
|
|
|
|
|
- // 使用Set的contains方法,O(1)复杂度
|
|
|
|
|
- if (!paramSet.contains(candidate)) {
|
|
|
|
|
- log.debug("生成页面参数成功: {}, 尝试次数: {}", candidate, attempt + 1);
|
|
|
|
|
- return candidate;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- attempt++;
|
|
|
|
|
- log.debug("页面参数 {} 已存在,重新生成,第{}次尝试", candidate, attempt);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 如果多次尝试都失败,使用+1随机数方案
|
|
|
|
|
- String finalParam = UniqueStringUtil.generateTimeBasedUnique(7);
|
|
|
|
|
- log.warn("多次尝试后使用7位参数: {}", finalParam);
|
|
|
|
|
- return finalParam;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ==================== 获取详情方法 ====================
|
|
|
|
|
-
|
|
|
|
|
- @Override
|
|
|
|
|
- public AcquisitionAssistantDetailVO getDetailWithQw(String linkId) {
|
|
|
|
|
- if (StringUtils.isEmpty(linkId)) {
|
|
|
|
|
- throw new CustomException("链接ID不能为空");
|
|
|
|
|
- }
|
|
|
|
|
- // 1. 查询本地记录
|
|
|
|
|
- QwAcquisitionAssistant query = new QwAcquisitionAssistant();
|
|
|
|
|
- query.setLinkId(linkId);
|
|
|
|
|
- List<QwAcquisitionAssistant> localRecords = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantList(query);
|
|
|
|
|
- QwAcquisitionAssistant localData = localRecords.isEmpty() ? null : localRecords.get(0);
|
|
|
|
|
-
|
|
|
|
|
- // 2. 获取企业信息
|
|
|
|
|
- String corpId = localData != null ? localData.getCorpId() : null;
|
|
|
|
|
- QwCompany qwCompany = qwCompanyService.selectQwCompanyByCorpId(corpId);
|
|
|
|
|
- if (qwCompany == null) {
|
|
|
|
|
- throw new CustomException("未找到企业配置信息");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 3. 调用企微API获取详情
|
|
|
|
|
- AcquisitionGetResponse qwDetail = null;
|
|
|
|
|
- try {
|
|
|
|
|
- AcquisitionGetRequest request = new AcquisitionGetRequest();
|
|
|
|
|
- request.setLinkId(linkId);
|
|
|
|
|
-
|
|
|
|
|
- String qwApiUrl = buildApiUrl(qwCompany.getCorpId(), qwCompany.getOpenSecret(), QwApiConfig.getAcquisition);
|
|
|
|
|
- qwDetail = callQwApi(qwApiUrl, request, AcquisitionGetResponse.class, "获取获客链接详情");
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("调用企微API失败", e);
|
|
|
|
|
- // 如果API调用失败,就返回本地数据
|
|
|
|
|
- }
|
|
|
|
|
- // 4. 构建VO对象
|
|
|
|
|
- return buildDetailVO(localData, qwDetail, qwCompany, linkId);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- @Override
|
|
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
- public void syncDetailToLocal(String corpid, String corpsecret, String linkId) {
|
|
|
|
|
- AcquisitionAssistantDetailVO detail = getDetailWithQw(linkId);
|
|
|
|
|
- QwAcquisitionAssistant assistant = convertToLocal(linkId, detail);
|
|
|
|
|
-
|
|
|
|
|
- QwAcquisitionAssistant existData = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantByLinkId(linkId);
|
|
|
|
|
-
|
|
|
|
|
- if (existData != null) {
|
|
|
|
|
- assistant.setId(existData.getId());
|
|
|
|
|
- setLocalFields(assistant, false);
|
|
|
|
|
- qwAcquisitionAssistantMapper.updateQwAcquisitionAssistant(assistant);
|
|
|
|
|
- } else {
|
|
|
|
|
- setLocalFields(assistant, true);
|
|
|
|
|
- qwAcquisitionAssistantMapper.insertQwAcquisitionAssistant(assistant);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ==================== 新增方法 ====================
|
|
|
|
|
-
|
|
|
|
|
- @Override
|
|
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
- public QwAcquisitionAssistant createWithQw(String corpid, String corpsecret, QwAcquisitionAssistant assistant) {
|
|
|
|
|
- // 参数校验
|
|
|
|
|
- if (StringUtils.isEmpty(assistant.getLinkName())) {
|
|
|
|
|
- throw new CustomException("链接名称不能为空");
|
|
|
|
|
- }
|
|
|
|
|
- if (assistant.getLinkName().length() > 30) {
|
|
|
|
|
- throw new CustomException("链接名称不能超过30个字符");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 构建请求
|
|
|
|
|
- JSONObject request = new JSONObject(); // 改用JSONObject以便灵活构建
|
|
|
|
|
- request.put("link_name", assistant.getLinkName());
|
|
|
|
|
- request.put("skip_verify", Boolean.parseBoolean(assistant.getSkipVerify()));
|
|
|
|
|
- //request.put("mark_source", assistant.getMarkSource());
|
|
|
|
|
-
|
|
|
|
|
- // 构建range对象 - 即使没有值也要传空对象
|
|
|
|
|
- JSONObject range = new JSONObject();
|
|
|
|
|
-
|
|
|
|
|
- // 如果有成员列表
|
|
|
|
|
- if (assistant.getUserListParam() != null && !assistant.getUserListParam().isEmpty()) {
|
|
|
|
|
- range.put("user_list", assistant.getUserListParam());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 如果有部门列表
|
|
|
|
|
- if (assistant.getDepartmentListParam() != null && !assistant.getDepartmentListParam().isEmpty()) {
|
|
|
|
|
- range.put("department_list", assistant.getDepartmentListParam());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 无论是否有值,都添加range字段
|
|
|
|
|
- request.put("range", range);
|
|
|
|
|
-
|
|
|
|
|
- // 构建priority_option - 可选
|
|
|
|
|
- if (assistant.getPriorityType() != null && assistant.getPriorityType() > 0) {
|
|
|
|
|
- JSONObject priorityOption = new JSONObject();
|
|
|
|
|
- priorityOption.put("priority_type", assistant.getPriorityType());
|
|
|
|
|
-
|
|
|
|
|
- // 如果priority_type为2,需要指定priority_userid_list
|
|
|
|
|
- if (assistant.getPriorityType() == 2) {
|
|
|
|
|
- if (assistant.getPriorityUserListParam() == null || assistant.getPriorityUserListParam().isEmpty()) {
|
|
|
|
|
- throw new CustomException("优先分配类型为指定范围内时,优先分配成员不能为空");
|
|
|
|
|
- }
|
|
|
|
|
- priorityOption.put("priority_userid_list", assistant.getPriorityUserListParam());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- request.put("priority_option", priorityOption);
|
|
|
|
|
- }
|
|
|
|
|
- // 调用企微API
|
|
|
|
|
- String qwApiUrl = buildApiUrl(corpid, corpsecret, QwApiConfig.createAcquisition);
|
|
|
|
|
|
|
+ request.put("priority_option", priorityOption);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 调用企微API
|
|
|
|
|
+ String qwApiUrl = buildApiUrl(corpid, corpsecret, QwApiConfig.createAcquisition);
|
|
|
|
|
|
|
|
AcquisitionCreateResponse response = callQwApi(qwApiUrl, request, AcquisitionCreateResponse.class, "创建获客链接");
|
|
AcquisitionCreateResponse response = callQwApi(qwApiUrl, request, AcquisitionCreateResponse.class, "创建获客链接");
|
|
|
if (response.getLink() == null || StringUtils.isEmpty(response.getLink().getUrl())) {
|
|
if (response.getLink() == null || StringUtils.isEmpty(response.getLink().getUrl())) {
|
|
@@ -643,7 +247,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
|
|
|
|
|
// 设置本地字段并保存
|
|
// 设置本地字段并保存
|
|
|
setLocalFields(assistant, true);
|
|
setLocalFields(assistant, true);
|
|
|
- qwAcquisitionAssistantMapper.insertQwAcquisitionAssistant(assistant);
|
|
|
|
|
|
|
+ acquisitionAssistantMapper.insertQwAcquisitionAssistant(assistant);
|
|
|
|
|
|
|
|
// ========== 缓存URL,便于后续通过pageParam访问 ==========
|
|
// ========== 缓存URL,便于后续通过pageParam访问 ==========
|
|
|
try {
|
|
try {
|
|
@@ -672,7 +276,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 查询本地是否存在
|
|
// 查询本地是否存在
|
|
|
- QwAcquisitionAssistant existAssistant = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantById(assistant.getId());
|
|
|
|
|
|
|
+ QwAcquisitionAssistant existAssistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(assistant.getId());
|
|
|
if (existAssistant == null) {
|
|
if (existAssistant == null) {
|
|
|
throw new CustomException("获客链接不存在");
|
|
throw new CustomException("获客链接不存在");
|
|
|
}
|
|
}
|
|
@@ -704,7 +308,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
|
|
|
|
|
assistant.setPageParam(newPageParam);
|
|
assistant.setPageParam(newPageParam);
|
|
|
|
|
|
|
|
- int rows = qwAcquisitionAssistantMapper.updateQwAcquisitionAssistant(assistant);
|
|
|
|
|
|
|
+ int rows = acquisitionAssistantMapper.updateQwAcquisitionAssistant(assistant);
|
|
|
if (rows <= 0) {
|
|
if (rows <= 0) {
|
|
|
throw new CustomException("本地数据更新失败");
|
|
throw new CustomException("本地数据更新失败");
|
|
|
}
|
|
}
|
|
@@ -726,7 +330,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 查询本地是否存在(获取完整的记录,包括pageParam)
|
|
// 查询本地是否存在(获取完整的记录,包括pageParam)
|
|
|
- QwAcquisitionAssistant existAssistant = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantById(assistant.getId());
|
|
|
|
|
|
|
+ QwAcquisitionAssistant existAssistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(assistant.getId());
|
|
|
if (existAssistant == null) {
|
|
if (existAssistant == null) {
|
|
|
throw new CustomException("获客链接不存在");
|
|
throw new CustomException("获客链接不存在");
|
|
|
}
|
|
}
|
|
@@ -756,7 +360,12 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 删除本地记录
|
|
// 删除本地记录
|
|
|
- int rows = qwAcquisitionAssistantMapper.deleteQwAcquisitionAssistantById(assistant.getId());
|
|
|
|
|
|
|
+ int rows = acquisitionAssistantMapper.deleteQwAcquisitionAssistantById(assistant.getId());
|
|
|
|
|
+
|
|
|
|
|
+ //删除新增链接记录
|
|
|
|
|
+ Long[] qwAcquisitionAssistantIds = {assistant.getId()};
|
|
|
|
|
+ linkInfoService.deleteQwAcquisitionLinkInfoByQwAcquisitionAssistantIds(qwAcquisitionAssistantIds);
|
|
|
|
|
+
|
|
|
if (rows <= 0) {
|
|
if (rows <= 0) {
|
|
|
throw new CustomException("本地数据删除失败");
|
|
throw new CustomException("本地数据删除失败");
|
|
|
}
|
|
}
|
|
@@ -770,7 +379,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public QwAcquisitionAssistant selectQwAcquisitionAssistantById(Long id) {
|
|
public QwAcquisitionAssistant selectQwAcquisitionAssistantById(Long id) {
|
|
|
- QwAcquisitionAssistant assistant = qwAcquisitionAssistantMapper.selectQwAcquisitionAssistantById(id);
|
|
|
|
|
|
|
+ QwAcquisitionAssistant assistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(id);
|
|
|
if (assistant != null) {
|
|
if (assistant != null) {
|
|
|
assistant.parseJsonFields();
|
|
assistant.parseJsonFields();
|
|
|
}
|
|
}
|
|
@@ -798,7 +407,7 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 缓存中没有,查询数据库
|
|
// 缓存中没有,查询数据库
|
|
|
- friendUrl = qwAcquisitionAssistantMapper.selectQwAcquisitionUrlByPageParam(pageParam);
|
|
|
|
|
|
|
+ friendUrl = acquisitionAssistantMapper.selectQwAcquisitionUrlByPageParam(pageParam);
|
|
|
|
|
|
|
|
// 缓存处理(包括空值缓存)
|
|
// 缓存处理(包括空值缓存)
|
|
|
if (friendUrl == null) {
|
|
if (friendUrl == null) {
|
|
@@ -815,4 +424,301 @@ public class QwAcquisitionAssistantServiceImpl implements IQwAcquisitionAssistan
|
|
|
|
|
|
|
|
return friendUrl;
|
|
return friendUrl;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取access_token并返回完整URL
|
|
|
|
|
+ */
|
|
|
|
|
+ private String buildApiUrl(String corpid, String corpsecret, String apiPath) {
|
|
|
|
|
+ WeixinKfTokenVO token = getAccessToken(corpid, corpsecret);
|
|
|
|
|
+
|
|
|
|
|
+ if (token == null || StringUtils.isEmpty(token.getAccess_token())) {
|
|
|
|
|
+ log.error("获取access_token失败, corpid:{}", corpid);
|
|
|
|
|
+ throw new CustomException("获客链接管理-获取企业微信access_token失败");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return apiPath + "?access_token=" + token.getAccess_token();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取access_token(使用实际有效期)
|
|
|
|
|
+ */
|
|
|
|
|
+ private WeixinKfTokenVO getAccessToken(String corpid, String corpsecret) {
|
|
|
|
|
+ String key = QW_ACQUISITION_KEY_PREFIX + corpid;
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 先从缓存获取
|
|
|
|
|
+ WeixinKfTokenVO token = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object cacheObj = redisCache.getCacheObject(key);
|
|
|
|
|
+ if (cacheObj instanceof WeixinKfTokenVO) {
|
|
|
|
|
+ token = (WeixinKfTokenVO) cacheObj;
|
|
|
|
|
+ log.debug("从缓存获取token成功, corpid:{}", corpid);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("从缓存获取token异常, 将重新获取, corpid:{}", corpid);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 缓存中没有,则重新获取
|
|
|
|
|
+ if (token == null) {
|
|
|
|
|
+ log.info("缓存中没有token,重新获取, corpid:{}", corpid);
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 获取新token
|
|
|
|
|
+ token = weixinKfService.getToken(corpid, corpsecret);
|
|
|
|
|
+
|
|
|
|
|
+ // 存入缓存,使用企业微信返回的实际有效期
|
|
|
|
|
+ if (token != null && StringUtils.isNotEmpty(token.getAccess_token())) {
|
|
|
|
|
+ Integer expiresIn = token.getExpires_in();
|
|
|
|
|
+ if (expiresIn == null) {
|
|
|
|
|
+ // 如果返回中没有expires_in,默认2小时
|
|
|
|
|
+ expiresIn = 7200;
|
|
|
|
|
+ log.warn("token返回中没有expires_in字段,使用默认值7200秒");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 实际缓存时间比有效期稍短(提前5分钟过期),避免边界问题
|
|
|
|
|
+ Integer cacheExpire = expiresIn - 300; // 提前5分钟
|
|
|
|
|
+ if (cacheExpire <= 0) {
|
|
|
|
|
+ cacheExpire = expiresIn;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ redisCache.setCacheObject(key, token, cacheExpire, TimeUnit.SECONDS);
|
|
|
|
|
+ log.info("token缓存成功, corpid:{}, 有效期:{}秒, 缓存时间:{}秒",
|
|
|
|
|
+ corpid, expiresIn, cacheExpire);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("获取token失败, corpid:{}, error:{}", corpid, e.getMessage());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return token;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 调用企微API并统一处理返回结果
|
|
|
|
|
+ */
|
|
|
|
|
+ private <T> T callQwApi(String qwApiUrl, Object request, Class<T> responseClass, String operationName) {
|
|
|
|
|
+ log.info("调用企微{},参数:{}", operationName, JSON.toJSONString(request));
|
|
|
|
|
+ String result = HttpUtil.sendAuthPost(request, qwApiUrl);
|
|
|
|
|
+ log.info("企微{}响应:{}", operationName, result);
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtils.isEmpty(result)) {
|
|
|
|
|
+ throw new CustomException("调用企微API失败,返回为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ T response = JSON.parseObject(result, responseClass);
|
|
|
|
|
+
|
|
|
|
|
+ // 通过反射获取errcode(所有响应都有这个字段)
|
|
|
|
|
+ try {
|
|
|
|
|
+ java.lang.reflect.Field errcodeField = responseClass.getDeclaredField("errcode");
|
|
|
|
|
+ errcodeField.setAccessible(true);
|
|
|
|
|
+ Integer errcode = (Integer) errcodeField.get(response);
|
|
|
|
|
+
|
|
|
|
|
+ if (errcode != null && errcode != 0) {
|
|
|
|
|
+ java.lang.reflect.Field errmsgField = responseClass.getDeclaredField("errmsg");
|
|
|
|
|
+ errmsgField.setAccessible(true);
|
|
|
|
|
+ String errmsg = (String) errmsgField.get(response);
|
|
|
|
|
+ throw new CustomException("企微" + operationName + "失败:" + errmsg);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ if (e instanceof CustomException) {
|
|
|
|
|
+ throw (CustomException) e;
|
|
|
|
|
+ }
|
|
|
|
|
+ log.error("解析企微响应失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成范围描述
|
|
|
|
|
+ */
|
|
|
|
|
+ private String generateRangeDesc(QwAcquisitionAssistant assistant) {
|
|
|
|
|
+ StringBuilder desc = new StringBuilder();
|
|
|
|
|
+ if (assistant.getUserListParam() != null && !assistant.getUserListParam().isEmpty()) {
|
|
|
|
|
+ desc.append(assistant.getUserListParam().size()).append("名成员");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (assistant.getDepartmentListParam() != null && !assistant.getDepartmentListParam().isEmpty()) {
|
|
|
|
|
+ if (desc.length() > 0) desc.append(" + ");
|
|
|
|
|
+ desc.append(assistant.getDepartmentListParam().size()).append("个部门");
|
|
|
|
|
+ }
|
|
|
|
|
+ return desc.length() > 0 ? desc.toString() : "未设置范围";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成scheme
|
|
|
|
|
+ */
|
|
|
|
|
+ private String generateScheme(String qwApiUrl) {
|
|
|
|
|
+ if (StringUtils.isNotEmpty(qwApiUrl)) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String encodedUrl = java.net.URLEncoder.encode(qwApiUrl, "UTF-8");
|
|
|
|
|
+ return "weixin://biz/ww/profile/" + encodedUrl;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("生成scheme失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置本地通用字段
|
|
|
|
|
+ */
|
|
|
|
|
+ private void setLocalFields(QwAcquisitionAssistant assistant, boolean isCreate) {
|
|
|
|
|
+ if (isCreate) {
|
|
|
|
|
+ assistant.setStatus(1);
|
|
|
|
|
+ assistant.setDelFlag("0");
|
|
|
|
|
+ assistant.setCreateTime(new Date());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ assistant.setUpdateTime(new Date());
|
|
|
|
|
+ }
|
|
|
|
|
+ assistant.setSyncTime(DateUtils.getNowDate());
|
|
|
|
|
+ assistant.buildJsonFields();
|
|
|
|
|
+ assistant.setRangeDesc(generateRangeDesc(assistant));
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtils.isEmpty(assistant.getScheme())) {
|
|
|
|
|
+ assistant.setScheme(generateScheme(assistant.getUrl()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建统一的VO对象
|
|
|
|
|
+ */
|
|
|
|
|
+ private AcquisitionAssistantDetailVO buildDetailVO(QwAcquisitionAssistant localData,
|
|
|
|
|
+ AcquisitionGetResponse qwDetail,
|
|
|
|
|
+ QwCompany qwCompany,
|
|
|
|
|
+ String linkId) {
|
|
|
|
|
+ AcquisitionAssistantDetailVO vo = new AcquisitionAssistantDetailVO();
|
|
|
|
|
+
|
|
|
|
|
+ // 处理企微API返回的数据
|
|
|
|
|
+ if (qwDetail != null) {
|
|
|
|
|
+ // 基础信息
|
|
|
|
|
+ if (qwDetail.getLink() != null) {
|
|
|
|
|
+ AcquisitionGetResponse.LinkDetail link = qwDetail.getLink();
|
|
|
|
|
+ vo.setLinkId(linkId);
|
|
|
|
|
+ vo.setLinkName(link.getLinkName());
|
|
|
|
|
+ vo.setUrl(link.getUrl());
|
|
|
|
|
+ vo.setSkipVerify(link.getSkipVerify());
|
|
|
|
|
+ vo.setQwCreateTime(link.getCreateTime());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 范围信息处理
|
|
|
|
|
+ if (qwDetail.getRange() != null) {
|
|
|
|
|
+ AcquisitionGetResponse.RangeInfo range = qwDetail.getRange();
|
|
|
|
|
+
|
|
|
|
|
+ // 将userList和departmentList组合成JSON字符串
|
|
|
|
|
+ Map<String, Object> rangeUserListMap = new HashMap<>();
|
|
|
|
|
+ Map<String, Object> rangeDepartmentListMap = new HashMap<>();
|
|
|
|
|
+ rangeUserListMap.put("userList", range.getUserList() != null ? range.getUserList() : new ArrayList<>());
|
|
|
|
|
+ rangeDepartmentListMap.put("departmentList", range.getDepartmentList() != null ? range.getDepartmentList() : new ArrayList<>());
|
|
|
|
|
+ vo.setUserList(JSON.toJSONString(rangeUserListMap));
|
|
|
|
|
+ vo.setDepartmentList(JSON.toJSONString(rangeDepartmentListMap));
|
|
|
|
|
+
|
|
|
|
|
+ // 生成范围描述
|
|
|
|
|
+ vo.setRangeDesc(buildRangeDesc(range));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 优先分配信息处理
|
|
|
|
|
+ if (qwDetail.getPriorityOption() != null) {
|
|
|
|
|
+ AcquisitionGetResponse.PriorityInfo priority = qwDetail.getPriorityOption();
|
|
|
|
|
+ vo.setPriorityType(priority.getPriorityType() != null ? priority.getPriorityType() : 0);
|
|
|
|
|
+
|
|
|
|
|
+ // 将优先分配成员列表转换为JSON字符串
|
|
|
|
|
+ if (priority.getPriorityUseridList() != null) {
|
|
|
|
|
+ vo.setPriorityUserList(JSON.toJSONString(priority.getPriorityUseridList()));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ vo.setPriorityUserList("[]");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 补充本地数据
|
|
|
|
|
+ if (localData != null) {
|
|
|
|
|
+ // 如果企微数据中没有的字段,使用本地数据
|
|
|
|
|
+ if (vo.getLinkId() == null) vo.setLinkId(localData.getLinkId());
|
|
|
|
|
+ if (vo.getLinkName() == null) vo.setLinkName(localData.getLinkName());
|
|
|
|
|
+ if (vo.getUrl() == null) vo.setUrl(localData.getUrl());
|
|
|
|
|
+ if (vo.getSkipVerify() == null) vo.setSkipVerify(localData.getSkipVerify());
|
|
|
|
|
+ if (vo.getUserList() == null) vo.setUserList(localData.getUserList());
|
|
|
|
|
+ if (vo.getPriorityType() == null) vo.setPriorityType(localData.getPriorityType());
|
|
|
|
|
+ if (vo.getPriorityUserList() == null) vo.setPriorityUserList(localData.getPriorityUserList());
|
|
|
|
|
+ if (vo.getQwUserTableIdList() == null) vo.setQwUserTableIdList(localData.getQwUserTableIdList());
|
|
|
|
|
+ if (vo.getRangeDesc() == null) vo.setRangeDesc(localData.getRangeDesc());
|
|
|
|
|
+
|
|
|
|
|
+ // 本地记录特有的字段
|
|
|
|
|
+ vo.setId(localData.getId());
|
|
|
|
|
+ vo.setStatus(localData.getStatus());
|
|
|
|
|
+ vo.setSyncTime(localData.getSyncTime());
|
|
|
|
|
+ vo.setRemark(localData.getRemark());
|
|
|
|
|
+ vo.setCorpId(localData.getCorpId());
|
|
|
|
|
+ vo.setScheme(localData.getScheme());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 纯企微数据,设置默认值
|
|
|
|
|
+ vo.setStatus(1);
|
|
|
|
|
+ vo.setCorpId(qwCompany.getCorpId());
|
|
|
|
|
+ if (vo.getPriorityType() == null) vo.setPriorityType(0);
|
|
|
|
|
+ if (vo.getPriorityUserList() == null) vo.setPriorityUserList("[]");
|
|
|
|
|
+ if (vo.getUserList() == null) vo.setUserList("{\"userList\":[]}");
|
|
|
|
|
+ if (vo.getDepartmentList() == null) vo.setDepartmentList("{\"departmentList\":[]}");
|
|
|
|
|
+ if (vo.getRangeDesc() == null) vo.setRangeDesc("全企业");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return vo;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成范围描述 - 确保方法签名正确
|
|
|
|
|
+ */
|
|
|
|
|
+ private String buildRangeDesc(AcquisitionGetResponse.RangeInfo range) {
|
|
|
|
|
+ if (range == null) {
|
|
|
|
|
+ return "全企业";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<String> userList = range.getUserList();
|
|
|
|
|
+ List<Integer> departmentList = range.getDepartmentList();
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder desc = new StringBuilder();
|
|
|
|
|
+
|
|
|
|
|
+ if (userList != null && !userList.isEmpty()) {
|
|
|
|
|
+ desc.append("成员(").append(userList.size()).append("人)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (departmentList != null && !departmentList.isEmpty()) {
|
|
|
|
|
+ if (desc.length() > 0) {
|
|
|
|
|
+ desc.append("、");
|
|
|
|
|
+ }
|
|
|
|
|
+ desc.append("部门(").append(departmentList.size()).append("个)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return desc.length() > 0 ? desc.toString() : "全企业";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成唯一的页面参数(针对2000条数据的优化版本)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String generateUniquePageParam() {
|
|
|
|
|
+ // 获取所有已存在的pageParam(只取需要的字段)
|
|
|
|
|
+ List<String> existingParams = acquisitionAssistantMapper.selectAllPageParams();
|
|
|
|
|
+ //使用Set,提高查找效率 O(1)
|
|
|
|
|
+ Set<String> paramSet = new HashSet<>(existingParams);
|
|
|
|
|
+
|
|
|
|
|
+ int maxAttempts = 10; // 设置最大尝试次数
|
|
|
|
|
+ int attempt = 0;
|
|
|
|
|
+
|
|
|
|
|
+ while (attempt < maxAttempts) {
|
|
|
|
|
+ // 生成6位随机码
|
|
|
|
|
+ String candidate = UniqueStringUtil.generateTimeBasedUnique(6);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用Set的contains方法,O(1)复杂度
|
|
|
|
|
+ if (!paramSet.contains(candidate)) {
|
|
|
|
|
+ log.debug("生成页面参数成功: {}, 尝试次数: {}", candidate, attempt + 1);
|
|
|
|
|
+ return candidate;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ attempt++;
|
|
|
|
|
+ log.debug("页面参数 {} 已存在,重新生成,第{}次尝试", candidate, attempt);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果多次尝试都失败,使用+1随机数方案
|
|
|
|
|
+ String finalParam = UniqueStringUtil.generateTimeBasedUnique(7);
|
|
|
|
|
+ log.warn("多次尝试后使用7位参数: {}", finalParam);
|
|
|
|
|
+ return finalParam;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|