|
|
@@ -1,6 +1,27 @@
|
|
|
package com.fs.qw.service.impl;
|
|
|
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import com.fs.common.core.domain.R;
|
|
|
+import com.fs.common.core.redis.RedisCache;
|
|
|
+import com.fs.common.exception.CustomException;
|
|
|
+import com.fs.common.service.ISmsService;
|
|
|
+import com.fs.common.utils.StringUtils;
|
|
|
+import com.fs.company.domain.CompanySmsTemp;
|
|
|
+import com.fs.company.service.ICompanySmsTempService;
|
|
|
+import com.fs.his.dto.SendResultDetailDTO;
|
|
|
+import com.fs.qw.bo.SendMsgLogBo;
|
|
|
+import com.fs.qw.domain.QwAcquisitionAssistant;
|
|
|
+import com.fs.qw.dto.BatchAddAcquisitionLinkDTO;
|
|
|
+import com.fs.qw.enums.SmsLogType;
|
|
|
+import com.fs.qw.mapper.QwAcquisitionAssistantMapper;
|
|
|
+import com.fs.qw.utils.UniqueStringUtil;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import com.fs.qw.mapper.QwAcquisitionLinkInfoMapper;
|
|
|
@@ -15,15 +36,36 @@ import static com.fs.his.utils.PhoneUtil.encryptPhone;
|
|
|
* @author fs
|
|
|
* @date 2026-03-27
|
|
|
*/
|
|
|
+@Slf4j
|
|
|
@Service
|
|
|
public class QwAcquisitionLinkInfoServiceImpl implements IQwAcquisitionLinkInfoService
|
|
|
{
|
|
|
+ @Autowired
|
|
|
+ private ISmsService smsService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisCache redisCache;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ICompanySmsTempService smsTempService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private QwAcquisitionAssistantMapper acquisitionAssistantMapper;
|
|
|
+
|
|
|
@Autowired
|
|
|
private QwAcquisitionLinkInfoMapper qwAcquisitionLinkInfoMapper;
|
|
|
|
|
|
- //组装完整链接后缀(这个后面拼接加密后的手机字符串)
|
|
|
+ //拼接电话号码的链接后缀(这个后面拼接加密后的手机字符串)
|
|
|
private static final String LINK_SUFFIX = "?customer_channel=up:";
|
|
|
|
|
|
+ // 企微加好友链接-url的key
|
|
|
+ private static final String QW_FRIEND_LINK_URL_KEY = "qw_friend_link_url:";
|
|
|
+
|
|
|
+ //获客链接短信模板code
|
|
|
+ private static final String SMS_LINK_TEMPLATE_CODE = "获客链接短信模板";
|
|
|
+
|
|
|
+ //访问链接域名
|
|
|
+ private static final String LINK_DOMAIN = "https://c.ysyd.top/";
|
|
|
/**
|
|
|
* 查询获客链接-号码链接生成记录
|
|
|
*
|
|
|
@@ -81,6 +123,12 @@ public class QwAcquisitionLinkInfoServiceImpl implements IQwAcquisitionLinkInfoS
|
|
|
@Override
|
|
|
public int deleteQwAcquisitionLinkInfoByIds(Long[] ids)
|
|
|
{
|
|
|
+ if (ids.length == 0){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ List<QwAcquisitionLinkInfo> toBeDeletedList = qwAcquisitionLinkInfoMapper.selectAcquisitionLinkInfoListByIds(ids);
|
|
|
+ //循环删除 Redis 缓存
|
|
|
+ batchDeleteLinkCatch(toBeDeletedList);
|
|
|
return qwAcquisitionLinkInfoMapper.deleteQwAcquisitionLinkInfoByIds(ids);
|
|
|
}
|
|
|
|
|
|
@@ -92,6 +140,12 @@ public class QwAcquisitionLinkInfoServiceImpl implements IQwAcquisitionLinkInfoS
|
|
|
*/
|
|
|
@Override
|
|
|
public int deleteQwAcquisitionLinkInfoByQwAcquisitionAssistantIds(Long[] qwAcquisitionAssistantIds){
|
|
|
+ if (qwAcquisitionAssistantIds.length == 0){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ List<QwAcquisitionLinkInfo> toBeDeletedList = qwAcquisitionLinkInfoMapper.selectAcquisitionLinkInfoListByAcquisitionAssistantIds(qwAcquisitionAssistantIds);
|
|
|
+ //循环删除 Redis 缓存
|
|
|
+ batchDeleteLinkCatch(toBeDeletedList);
|
|
|
return qwAcquisitionLinkInfoMapper.deleteQwAcquisitionLinkInfoByQwAcquisitionAssistantIds(qwAcquisitionAssistantIds);
|
|
|
}
|
|
|
|
|
|
@@ -104,13 +158,164 @@ public class QwAcquisitionLinkInfoServiceImpl implements IQwAcquisitionLinkInfoS
|
|
|
@Override
|
|
|
public int deleteQwAcquisitionLinkInfoById(Long id)
|
|
|
{
|
|
|
+ QwAcquisitionLinkInfo existAcquisitionLinkInfo = qwAcquisitionLinkInfoMapper.selectQwAcquisitionLinkInfoById(id);
|
|
|
+ if (existAcquisitionLinkInfo==null){
|
|
|
+ throw new CustomException("数据不存在");
|
|
|
+ }
|
|
|
+ // ========== 删除Redis缓存 ==========
|
|
|
+ try {
|
|
|
+ // 1. 删除pageParam对应的URL缓存
|
|
|
+ if (StringUtils.isNotEmpty(existAcquisitionLinkInfo.getRandomStr())) {
|
|
|
+ String urlCacheKey = QW_FRIEND_LINK_URL_KEY + existAcquisitionLinkInfo.getRandomStr();
|
|
|
+ redisCache.deleteObject(urlCacheKey);
|
|
|
+ log.info("删除获客链接URL缓存成功, pageParam: {}, key: {}",
|
|
|
+ existAcquisitionLinkInfo.getRandomStr(), urlCacheKey);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 缓存删除失败不应该影响主流程,但需要记录日志
|
|
|
+ log.error("删除获客链接缓存失败, id: {}, qwAcquisitionAssistantId: {}, pageParam: {}",
|
|
|
+ existAcquisitionLinkInfo.getId(), existAcquisitionLinkInfo.getQwAcquisitionAssistantId(),
|
|
|
+ existAcquisitionLinkInfo.getRandomStr(), e);
|
|
|
+ }
|
|
|
return qwAcquisitionLinkInfoMapper.deleteQwAcquisitionLinkInfoById(id);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public SendResultDetailDTO sendMessageLink(String phone, Long qwAcquisitionId, SendMsgLogBo sendMsgLogBo) {
|
|
|
+ CompanySmsTemp temp = smsTempService.selectCompanySmsTempByCode(SMS_LINK_TEMPLATE_CODE);
|
|
|
+ if (temp == null) {
|
|
|
+ log.info("获客链接-未找到短信模板:{}", SMS_LINK_TEMPLATE_CODE);
|
|
|
+ throw new CustomException("获客链接-未找到短信模板");
|
|
|
+ }
|
|
|
+ String originalContent = temp.getContent();
|
|
|
+ //获取获客链接管理信息
|
|
|
+ QwAcquisitionAssistant acquisitionAssistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(qwAcquisitionId);
|
|
|
+ if (acquisitionAssistant == null){
|
|
|
+ log.info("获客链接-未找到获客链接id:{}", qwAcquisitionId);
|
|
|
+ throw new CustomException("获客链接-未找到获客链接信息");
|
|
|
+ }
|
|
|
+ String randomStr = generateUniqueRandomStr();
|
|
|
+ String replaceText=LINK_DOMAIN+randomStr;
|
|
|
+ String content = originalContent.replace("${sms.friendLink}", replaceText);
|
|
|
+ try {
|
|
|
+ sendMsgLogBo.setQwAcquisitionId(acquisitionAssistant.getId());
|
|
|
+ R r = smsService.simpleSmsSend(phone, content, temp, SmsLogType.ACQUISITION_LINK, sendMsgLogBo);
|
|
|
+
|
|
|
+ if (r != null && "200".equals(String.valueOf(r.get("code")))) {
|
|
|
+
|
|
|
+ //新增号码-链接生成记录
|
|
|
+ addAcquisitionLinkInfo(acquisitionAssistant.getId(), phone, acquisitionAssistant.getUrl(),randomStr);
|
|
|
+
|
|
|
+ return new SendResultDetailDTO(true, null, null);
|
|
|
+ } else {
|
|
|
+ String msg = r != null && r.get("msg") != null ? r.get("msg").toString() : "未知错误";
|
|
|
+ log.warn("短信发送失败 获客链接id={}, phone={}, msg={}", qwAcquisitionId, phone, msg);
|
|
|
+ return new SendResultDetailDTO(false, msg, null);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("发送异常 获客链接id=" + qwAcquisitionId, e);
|
|
|
+ return new SendResultDetailDTO(false, e.getMessage(), null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String selectQwAcquisitionUrlByRandomStr(String randomStr) {
|
|
|
+ log.error("-------------------------------------进入selectQwAcquisitionUrlByRandomStr-----------------------------------");
|
|
|
+ String key = QW_FRIEND_LINK_URL_KEY + randomStr;
|
|
|
+ String fullLink = null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ Object cacheObj = redisCache.getCacheObject(key);
|
|
|
+ if (cacheObj instanceof String) {
|
|
|
+ fullLink = (String) cacheObj;
|
|
|
+ // 处理缓存空值的情况
|
|
|
+ if ("NULL".equals(fullLink)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ log.debug("从缓存获取完整获客链接url成功,randomStr:{}", randomStr);
|
|
|
+ return fullLink;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("从缓存获取完整获客链接url异常, 将重新获取, randomStr:{}", randomStr);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 缓存中没有,查询数据库
|
|
|
+ fullLink = qwAcquisitionLinkInfoMapper.selectQwAcquisitionUrlByRandomStr(randomStr);
|
|
|
+
|
|
|
+ // 缓存处理(包括空值缓存)
|
|
|
+ if (fullLink == null) {
|
|
|
+ int nullCacheExpire = 10; // 10秒
|
|
|
+ redisCache.setCacheObject(key, "NULL", nullCacheExpire, TimeUnit.SECONDS);
|
|
|
+ log.info("完整获客链接URL不存在,缓存空值10秒, randomStr:{}", randomStr);
|
|
|
+ return null;
|
|
|
+ } else {
|
|
|
+ // 正常值仍缓存10天
|
|
|
+ Integer cacheExpire = 10;
|
|
|
+ redisCache.setCacheObject(key, fullLink, cacheExpire, TimeUnit.DAYS);
|
|
|
+ log.info("完整获客链接URL缓存成功, randomStr:{}", randomStr);
|
|
|
+ }
|
|
|
+
|
|
|
+ return fullLink;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int batchCreateMessageLink(BatchAddAcquisitionLinkDTO batchAddAcquisitionLinkDTO) {
|
|
|
+ Long qwAcquisitionAssistantId = batchAddAcquisitionLinkDTO.getQwAcquisitionAssistantId();
|
|
|
+ String qwAcquisitionAssistantUrl = batchAddAcquisitionLinkDTO.getQwAcquisitionAssistantUrl();
|
|
|
+ List<String> phoneList = batchAddAcquisitionLinkDTO.getPhoneList();
|
|
|
+ int result = 0;
|
|
|
+ for (String phone : phoneList) {
|
|
|
+ try {
|
|
|
+ //新增号码-链接生成记录
|
|
|
+ String randomStr = generateUniqueRandomStr();
|
|
|
+ int addResult = addAcquisitionLinkInfo(qwAcquisitionAssistantId, phone, qwAcquisitionAssistantUrl, randomStr);
|
|
|
+ result += addResult;
|
|
|
+ // 可以在这里根据 addResult 判断单次是否成功,并记录日志
|
|
|
+ if (addResult > 0) {
|
|
|
+ log.debug("成功为手机号 {} 创建获客链接", phone);
|
|
|
+ } else {
|
|
|
+ log.warn("为手机号 {} 创建获客链接失败", phone);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 捕获异常,记录错误,但不中断整个循环
|
|
|
+ log.error("为手机号 {} 创建获客链接时发生异常", phone, e);
|
|
|
+ // 可以选择在此处继续下一次循环,或者根据业务要求决定是否中断
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("批量创建获客链接完成,总计尝试 {}, 成功 {}", phoneList.size(), result);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String extractLink(Long qwAcquisitionAssistantId, String originalPhone, String originalLink) {
|
|
|
+ String randomStr = generateUniqueRandomStr();
|
|
|
+ QwAcquisitionLinkInfo qwAcquisitionLinkInfo=new QwAcquisitionLinkInfo();
|
|
|
+ qwAcquisitionLinkInfo.setQwAcquisitionAssistantId(qwAcquisitionAssistantId);
|
|
|
+ qwAcquisitionLinkInfo.setPhone(originalPhone);//这里存储原始手机号
|
|
|
+ //加密手机号
|
|
|
+ String phonePlus = encryptPhone(originalPhone);
|
|
|
+ String linkPlus=originalLink+LINK_SUFFIX+ phonePlus;
|
|
|
+ qwAcquisitionLinkInfo.setLink(linkPlus);
|
|
|
+ qwAcquisitionLinkInfo.setRandomStr(randomStr);
|
|
|
+ int addResult=qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
|
|
|
+ // ========== 缓存URL,便于后续通过randomStr访问 ==========
|
|
|
+ try {
|
|
|
+ String cacheKey = QW_FRIEND_LINK_URL_KEY + randomStr;
|
|
|
+ Integer cacheExpire = 10; // 默认缓存10天
|
|
|
+ redisCache.setCacheObject(cacheKey, linkPlus, cacheExpire, TimeUnit.DAYS);
|
|
|
+ log.info("获客链接URL缓存成功, pageParam: {}, url: {}", randomStr, linkPlus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 缓存失败不影响主流程,但需要记录日志
|
|
|
+ log.error("获客链接URL缓存失败, pageParam: {}", randomStr, e);
|
|
|
+ }
|
|
|
+ // 返回域名+随机字符串
|
|
|
+ return LINK_DOMAIN+randomStr;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 添加链接生成记录
|
|
|
* */
|
|
|
- public int buildQwAcquisitionLinkInfoAdd(Long qwAcquisitionAssistantId,String originalPhone,String originalLink){
|
|
|
+ public int addAcquisitionLinkInfo(Long qwAcquisitionAssistantId,String originalPhone,String originalLink,String randomStr){
|
|
|
QwAcquisitionLinkInfo qwAcquisitionLinkInfo=new QwAcquisitionLinkInfo();
|
|
|
qwAcquisitionLinkInfo.setQwAcquisitionAssistantId(qwAcquisitionAssistantId);
|
|
|
qwAcquisitionLinkInfo.setPhone(originalPhone);//这里存储原始手机号
|
|
|
@@ -118,6 +323,74 @@ public class QwAcquisitionLinkInfoServiceImpl implements IQwAcquisitionLinkInfoS
|
|
|
String phonePlus = encryptPhone(originalPhone);
|
|
|
String linkPlus=originalLink+LINK_SUFFIX+ phonePlus;
|
|
|
qwAcquisitionLinkInfo.setLink(linkPlus);
|
|
|
- return qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
|
|
|
+ qwAcquisitionLinkInfo.setRandomStr(randomStr);
|
|
|
+ int addResult=qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
|
|
|
+ // ========== 缓存URL,便于后续通过randomStr访问 ==========
|
|
|
+ try {
|
|
|
+ String cacheKey = QW_FRIEND_LINK_URL_KEY + randomStr;
|
|
|
+ Integer cacheExpire = 10; // 默认缓存10天
|
|
|
+ redisCache.setCacheObject(cacheKey, linkPlus, cacheExpire, TimeUnit.DAYS);
|
|
|
+ log.info("获客链接URL缓存成功, pageParam: {}, url: {}", randomStr, linkPlus);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 缓存失败不影响主流程,但需要记录日志
|
|
|
+ log.error("获客链接URL缓存失败, pageParam: {}", randomStr, e);
|
|
|
+ }
|
|
|
+ return addResult;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成唯一的页面参数
|
|
|
+ */
|
|
|
+ private String generateUniqueRandomStr() {
|
|
|
+ // 获取所有已存在的pageParam(只取需要的字段)
|
|
|
+ List<String> existingParams = qwAcquisitionLinkInfoMapper.selectAllRandomStr();
|
|
|
+ //使用Set,提高查找效率 O(1)
|
|
|
+ Set<String> paramSet = new HashSet<>(existingParams);
|
|
|
+
|
|
|
+ int maxAttempts = 10; // 设置最大尝试次数
|
|
|
+ int attempt = 0;
|
|
|
+
|
|
|
+ while (attempt < maxAttempts) {
|
|
|
+ // 生成7位随机码
|
|
|
+ String candidate = UniqueStringUtil.generateTimeBasedUnique(7);
|
|
|
+
|
|
|
+ // 使用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(8);
|
|
|
+ log.warn("多次尝试后使用7位参数: {}", finalParam);
|
|
|
+ return finalParam;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量删除缓存
|
|
|
+ * */
|
|
|
+ private void batchDeleteLinkCatch(List<QwAcquisitionLinkInfo> toBeDeletedList) {
|
|
|
+ if (!toBeDeletedList.isEmpty()) {
|
|
|
+ for (QwAcquisitionLinkInfo record : toBeDeletedList) {
|
|
|
+ try {
|
|
|
+ // 检查 randomStr 是否为空,避免处理无效的缓存 key
|
|
|
+ if (StringUtils.isNotEmpty(record.getRandomStr())) {
|
|
|
+ String urlCacheKey = QW_FRIEND_LINK_URL_KEY + record.getRandomStr();
|
|
|
+ redisCache.deleteObject(urlCacheKey);
|
|
|
+ log.info("批量删除获客链接URL缓存成功, randomStr: {}, key: {}",
|
|
|
+ record.getRandomStr(), urlCacheKey);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 缓存删除失败不应影响主数据库删除流程,但需要记录日志
|
|
|
+ log.error("批量删除获客链接缓存失败, id: {}, qwAcquisitionAssistantId: {}, randomStr: {}",
|
|
|
+ record.getId(), record.getQwAcquisitionAssistantId(), record.getRandomStr(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|