|
@@ -12,28 +12,31 @@ import com.fs.company.domain.CompanyUser;
|
|
|
import com.fs.company.service.ICompanyUserService;
|
|
|
import com.fs.course.config.CourseConfig;
|
|
|
import com.fs.course.domain.*;
|
|
|
-import com.fs.course.mapper.FsCourseDomainNameMapper;
|
|
|
-import com.fs.course.mapper.FsCourseFinishTempMapper;
|
|
|
-import com.fs.course.mapper.FsCourseLinkMapper;
|
|
|
-import com.fs.course.mapper.FsCourseWatchLogMapper;
|
|
|
+import com.fs.course.mapper.*;
|
|
|
import com.fs.fastgptApi.util.AudioUtils;
|
|
|
import com.fs.fastgptApi.vo.AudioVO;
|
|
|
import com.fs.qw.domain.QwExternalContact;
|
|
|
+import com.fs.qw.domain.QwUser;
|
|
|
import com.fs.qw.mapper.QwExternalContactMapper;
|
|
|
import com.fs.qw.mapper.QwUserMapper;
|
|
|
import com.fs.qw.service.IQwExternalContactService;
|
|
|
+import com.fs.qw.service.impl.QwExternalContactServiceImpl;
|
|
|
import com.fs.qw.vo.QwSopCourseFinishTempSetting;
|
|
|
import com.fs.qw.vo.QwSopRuleTimeVO;
|
|
|
import com.fs.qw.vo.QwSopTempSetting;
|
|
|
import com.fs.sop.domain.*;
|
|
|
+import com.fs.sop.dto.QwCreateLinkByAppDTO;
|
|
|
import com.fs.sop.mapper.*;
|
|
|
+import com.fs.sop.params.SopUserLogsInfoParam;
|
|
|
import com.fs.sop.service.IQwSopLogsService;
|
|
|
import com.fs.sop.service.IQwSopTempContentService;
|
|
|
import com.fs.sop.service.IQwSopTempRulesService;
|
|
|
import com.fs.sop.service.IQwSopTempVoiceService;
|
|
|
import com.fs.sop.vo.SopUserLogsVo;
|
|
|
import com.fs.system.service.ISysConfigService;
|
|
|
+import com.fs.voice.utils.StringUtil;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.retry.annotation.Backoff;
|
|
|
import org.springframework.retry.annotation.Retryable;
|
|
@@ -65,6 +68,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
private static final String QWSOP_KEY_PREFIX = "qwsop:";
|
|
|
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ private static final String miniappRealLink = "/pages_course/video.html?course=";
|
|
|
+ private static final String appRealLink = "/pages/courseAnswer/index?link=";
|
|
|
+ private static final String appLink = "https://jump.ylrztop.com/jumpapp/pages/index/index?link=";
|
|
|
|
|
|
// Cached configurations and domain names
|
|
|
private CourseConfig cachedCourseConfig;
|
|
@@ -83,8 +89,12 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
private QwSopTagMapper qwSopTagMapper ;
|
|
|
@Autowired
|
|
|
private QwSopMapper sopMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IQwExternalContactService iQwExternalContactService;
|
|
|
+
|
|
|
@Autowired
|
|
|
- private IQwExternalContactService qwExternalContactService;
|
|
|
+ private QwExternalContactServiceImpl qwExternalContactService;
|
|
|
|
|
|
@Autowired
|
|
|
private FsCourseWatchLogMapper fsCourseWatchLogMapper;
|
|
@@ -97,6 +107,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
|
|
|
@Autowired
|
|
|
private FsCourseLinkMapper fsCourseLinkMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsCourseSopAppLinkMapper fsCourseSopAppLinkMapper;
|
|
|
+
|
|
|
@Autowired
|
|
|
private ISysConfigService configService;
|
|
|
|
|
@@ -118,11 +132,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
private final BlockingQueue<QwSopLogs> qwSopLogsQueue = new LinkedBlockingQueue<>(20000);
|
|
|
private final BlockingQueue<FsCourseWatchLog> watchLogsQueue = new LinkedBlockingQueue<>(20000);
|
|
|
private final BlockingQueue<FsCourseLink> linkQueue = new LinkedBlockingQueue<>(20000);
|
|
|
+ private final BlockingQueue<FsCourseSopAppLink> sopAppLinks = new LinkedBlockingQueue<>(20000);
|
|
|
|
|
|
// Executors for consumer threads
|
|
|
private ExecutorService qwSopLogsExecutor;
|
|
|
private ExecutorService watchLogsExecutor;
|
|
|
private ExecutorService courseLinkExecutor;
|
|
|
+ private ExecutorService courseSopAppLinkExecutor;
|
|
|
|
|
|
// Shutdown flags
|
|
|
private volatile boolean running = true;
|
|
@@ -181,9 +197,17 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
return t;
|
|
|
});
|
|
|
|
|
|
+ courseSopAppLinkExecutor = Executors.newSingleThreadExecutor(r -> {
|
|
|
+ Thread t = new Thread(r, "courseSopAppLinkConsumer");
|
|
|
+ t.setDaemon(true);
|
|
|
+ return t;
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
qwSopLogsExecutor.submit(this::consumeQwSopLogs);
|
|
|
watchLogsExecutor.submit(this::consumeWatchLogs);
|
|
|
courseLinkExecutor.submit(this::consumeCourseLink);
|
|
|
+ courseSopAppLinkExecutor.submit(this::consumeCourseSopAppLink);
|
|
|
}
|
|
|
|
|
|
// Scheduled tasks to refresh configurations and domain names periodically
|
|
@@ -332,7 +356,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
}
|
|
|
QwSopTemp qwSopTemp = qwSopTempMapper.selectQwSopTempById(ruleTimeVO.getTempId());
|
|
|
if (qwSopTemp == null) {
|
|
|
- sopUserLogsMapper.deleteSopUserLogsBySopId(sopId);
|
|
|
+// sopUserLogsMapper.deleteSopUserLogsBySopId(sopId);
|
|
|
log.info("SOP ID {} 模板不存在,相关日志已清除。", sopId);
|
|
|
return;
|
|
|
}
|
|
@@ -340,10 +364,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
ruleTimeVO.setTempGap(qwSopTemp.getGap());
|
|
|
|
|
|
if (ruleTimeVO.getStatus() == 0 || "0".equals(ruleTimeVO.getTempStatus())) {
|
|
|
- SopUserLogs sopUserLogs = new SopUserLogs();
|
|
|
- sopUserLogs.setSopId(sopId);
|
|
|
- sopUserLogs.setStatus(2);
|
|
|
- sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs);
|
|
|
+// SopUserLogs sopUserLogs = new SopUserLogs();
|
|
|
+// sopUserLogs.setSopId(sopId);
|
|
|
+// sopUserLogs.setStatus(2);
|
|
|
+// sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs);
|
|
|
log.info("SOP ID {} 的状态为停用,相关日志状态已更新。", sopId);
|
|
|
return;
|
|
|
}
|
|
@@ -417,9 +441,22 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ String[] userKey = logVo.getUserId().split("\\|");
|
|
|
+ if (userKey.length < 3) {
|
|
|
+ log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String qwUserId = userKey[0].trim();
|
|
|
+ String companyUserId = userKey[1].trim();
|
|
|
+ String companyId = userKey[2].trim();
|
|
|
+
|
|
|
|
|
|
- //寻找时间
|
|
|
-// LocalDateTime currentTime = LocalDateTime.of(2024, 12, 25,23 , 40);
|
|
|
+ //获取企业微信员工的称呼//从redis里或者从库里取
|
|
|
+ QwUser qwUserByRedis = qwExternalContactService.getQwUserByRedis(logVo.getCorpId(),logVo.getQwUserId());
|
|
|
+ if (qwUserByRedis==null){
|
|
|
+ log.error("无企微员工信息 {} 跳过处理。:{}", logVo.getUserId(),logVo.getCorpId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
// 先算好 60分钟后 ~ 再60分钟后的时间段
|
|
|
LocalDateTime startRangeFirst = currentTime.plusMinutes(60);
|
|
@@ -438,17 +475,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
}else{
|
|
|
day++;
|
|
|
}
|
|
|
-//
|
|
|
-// // 再次验证 intervalDay 是否在范围内
|
|
|
-// if (intervalDay < 0 || intervalDay >= tempSettings.size()) {
|
|
|
-// log.info("跨天后,intervalDay={} 超出 TempSettings 范围,跳过。", intervalDay);
|
|
|
-// return;
|
|
|
-// }
|
|
|
-//
|
|
|
-// if (daysBetween % tempGap != 0) {
|
|
|
-// log.error("天数差 {} 不是 tempGap {} 的整数倍,跳过操作,SopId {} ", daysBetween, tempGap,logVo.getSopId());
|
|
|
-// return;
|
|
|
-// }
|
|
|
|
|
|
// 重新拿新的 “天” 的 Setting
|
|
|
contents = getDay(tempSettings, day);
|
|
@@ -497,32 +523,40 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
// 将 LocalDateTime 转换为 Date
|
|
|
Date sendTime = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
|
|
|
- SopUserLogsInfo userLogsInfo=new SopUserLogsInfo();
|
|
|
- userLogsInfo.setSopId(logVo.getSopId());
|
|
|
- userLogsInfo.setUserLogsId(logVo.getId());
|
|
|
+// SopUserLogsInfo userLogsInfo=new SopUserLogsInfo();
|
|
|
+// userLogsInfo.setSopId(logVo.getSopId());
|
|
|
+// userLogsInfo.setUserLogsId(logVo.getId());
|
|
|
|
|
|
- List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoList(userLogsInfo);
|
|
|
- if(logVo.getIsRegister() == 1){
|
|
|
- List<Long> externalContactIdList = PubFun.listToNewList(sopUserLogsInfos, SopUserLogsInfo::getExternalId);
|
|
|
- List<QwExternalContact> list = qwExternalContactService.list(new QueryWrapper<QwExternalContact>().isNotNull("fs_user_id").in("id", externalContactIdList));
|
|
|
- Map<Long, QwExternalContact> map = PubFun.listToMapByGroupObject(list, QwExternalContact::getId);
|
|
|
- sopUserLogsInfos = sopUserLogsInfos.stream().filter(e -> map.containsKey(e.getExternalId())).collect(Collectors.toList());
|
|
|
- }
|
|
|
+ SopUserLogsInfoParam logsInfoParam=new SopUserLogsInfoParam();
|
|
|
+ logsInfoParam.setSopId(logVo.getSopId());
|
|
|
+ logsInfoParam.setUserLogsId(logVo.getId());
|
|
|
+ logsInfoParam.setIsRegister(logVo.getIsRegister());
|
|
|
+
|
|
|
+ List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoListByIsRegister(logsInfoParam);
|
|
|
+
|
|
|
+// if(logVo.getIsRegister() == 1){
|
|
|
+// List<Long> externalContactIdList = PubFun.listToNewList(sopUserLogsInfos, SopUserLogsInfo::getExternalId);
|
|
|
+// List<QwExternalContact> list = qwExternalContactService.list(new QueryWrapper<QwExternalContact>().isNotNull("fs_user_id").in("id", externalContactIdList));
|
|
|
+// Map<Long, QwExternalContact> map = PubFun.listToMapByGroupObject(list, QwExternalContact::getId);
|
|
|
+// sopUserLogsInfos = sopUserLogsInfos.stream().filter(e -> map.containsKey(e.getExternalId())).collect(Collectors.toList());
|
|
|
+// }
|
|
|
|
|
|
|
|
|
// 获取fsUserId
|
|
|
- Set<Long> externalIds = sopUserLogsInfos.stream().map(SopUserLogsInfo::getExternalId).collect(Collectors.toSet());
|
|
|
- if (!externalIds.isEmpty()) {
|
|
|
- List<QwExternalContact> externalContactList = qwExternalContactService.list(Wrappers.<QwExternalContact>lambdaQuery().in(QwExternalContact::getId, externalIds));
|
|
|
- sopUserLogsInfos.forEach(s -> {
|
|
|
- QwExternalContact qwExternalContact = externalContactList.stream().filter(e -> Objects.equals(s.getExternalId(), e.getId())).findFirst().orElse(null);
|
|
|
- if (Objects.nonNull(qwExternalContact)) {
|
|
|
- s.setFsUserId(qwExternalContact.getFsUserId());
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+// Set<Long> externalIds = sopUserLogsInfos.stream().map(SopUserLogsInfo::getExternalId).collect(Collectors.toSet());
|
|
|
+// if (!externalIds.isEmpty()) {
|
|
|
+// List<QwExternalContact> externalContactList = qwExternalContactService.list(Wrappers.<QwExternalContact>lambdaQuery().in(QwExternalContact::getId, externalIds));
|
|
|
+// sopUserLogsInfos.forEach(s -> {
|
|
|
+// QwExternalContact qwExternalContact = externalContactList.stream().filter(e -> Objects.equals(s.getExternalId(), e.getId())).findFirst().orElse(null);
|
|
|
+// if (Objects.nonNull(qwExternalContact)) {
|
|
|
+// s.setFsUserId(qwExternalContact.getFsUserId());
|
|
|
+// }
|
|
|
+// });
|
|
|
+// }
|
|
|
+
|
|
|
+// insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content);
|
|
|
+ insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId, companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName());
|
|
|
|
|
|
- insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content);
|
|
|
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
@@ -561,7 +595,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
|
|
|
//消息处理
|
|
|
private void insertSopUserLogs(List<SopUserLogsInfo> sopUserLogsInfos, SopUserLogsVo logVo, Date sendTime,
|
|
|
- QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content) {
|
|
|
+ QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content,
|
|
|
+ String qwUserId,String companyUserId,String companyId,String welcomeText,String qwUserName) {
|
|
|
+
|
|
|
String formattedSendTime = sendTime.toInstant()
|
|
|
.atZone(ZoneId.systemDefault())
|
|
|
.format(DATE_TIME_FORMATTER);
|
|
@@ -569,24 +605,17 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
Long courseId = content.getCourseId();
|
|
|
Long videoId = content.getVideoId();
|
|
|
|
|
|
- String[] userKey = logVo.getUserId().split("\\|");
|
|
|
- if (userKey.length < 3) {
|
|
|
- log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId());
|
|
|
- return;
|
|
|
- }
|
|
|
- String qwUserId = userKey[0].trim();
|
|
|
- String companyUserId = userKey[1].trim();
|
|
|
- String companyId = userKey[2].trim();
|
|
|
+ Integer isOfficial = content.getIsOfficial() != null ? Integer.valueOf(content.getIsOfficial()) : 0;
|
|
|
|
|
|
// 发送语言 start
|
|
|
if(content.getSetting() == null){
|
|
|
return;
|
|
|
}
|
|
|
- List<QwSopTempSetting.Content.Setting> setting = content.getSetting().stream().filter(e -> "7".equals(e.getContentType())).collect(Collectors.toList());
|
|
|
- if(!setting.isEmpty()){
|
|
|
- List<String> valuesList = PubFun.listToNewList(setting, QwSopTempSetting.Content.Setting::getValue);
|
|
|
- List<QwSopTempVoice> voiceList = qwSopTempVoiceService.getVoiceByText(Long.parseLong(companyUserId), valuesList);
|
|
|
- Map<Long, Map<String, Optional<QwSopTempVoice>>> collect = voiceList.stream().collect(Collectors.groupingBy(QwSopTempVoice::getCompanyUserId, Collectors.groupingBy(QwSopTempVoice::getVoiceTxt, Collectors.reducing((e, e1) -> e))));
|
|
|
+// List<QwSopTempSetting.Content.Setting> setting = content.getSetting().stream().filter(e -> "7".equals(e.getContentType())).collect(Collectors.toList());
|
|
|
+// if(!setting.isEmpty()){
|
|
|
+// List<String> valuesList = PubFun.listToNewList(setting, QwSopTempSetting.Content.Setting::getValue);
|
|
|
+// List<QwSopTempVoice> voiceList = qwSopTempVoiceService.getVoiceByText(Long.parseLong(companyUserId), valuesList);
|
|
|
+// Map<Long, Map<String, Optional<QwSopTempVoice>>> collect = voiceList.stream().collect(Collectors.groupingBy(QwSopTempVoice::getCompanyUserId, Collectors.groupingBy(QwSopTempVoice::getVoiceTxt, Collectors.reducing((e, e1) -> e))));
|
|
|
// Consumer<QwSopTempSetting.Content.Setting> buildVoid = st -> {
|
|
|
// try {
|
|
|
// AudioVO audioVO = AudioUtils.transferAudioSilkFromText(st.getValue(), Long.valueOf(companyUserId), false);
|
|
@@ -596,22 +625,22 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
// log.info("音频生成失败-: "+companyUserId);
|
|
|
// }
|
|
|
// };
|
|
|
- setting.parallelStream().filter(e -> "7".equals(e.getContentType())).forEach(st -> {
|
|
|
- Map<String, Optional<QwSopTempVoice>> map = collect.get(Long.parseLong(companyUserId));
|
|
|
- if(map == null) {
|
|
|
-// buildVoid.accept(st);
|
|
|
- return;
|
|
|
- }
|
|
|
- Optional<QwSopTempVoice> optional = map.get(st.getValue());
|
|
|
- if(!optional.isPresent()) {
|
|
|
-// buildVoid.accept(st);
|
|
|
- return;
|
|
|
- }
|
|
|
- QwSopTempVoice voice = optional.get();
|
|
|
- st.setVoiceUrl(voice.getVoiceUrl());
|
|
|
- st.setVoiceDuration(voice.getDuration()+"");
|
|
|
- });
|
|
|
- }
|
|
|
+// setting.parallelStream().filter(e -> "7".equals(e.getContentType())).forEach(st -> {
|
|
|
+// Map<String, Optional<QwSopTempVoice>> map = collect.get(Long.parseLong(companyUserId));
|
|
|
+// if(map == null) {
|
|
|
+//// buildVoid.accept(st);
|
|
|
+// return;
|
|
|
+// }
|
|
|
+// Optional<QwSopTempVoice> optional = map.get(st.getValue());
|
|
|
+// if(!optional.isPresent()) {
|
|
|
+//// buildVoid.accept(st);
|
|
|
+// return;
|
|
|
+// }
|
|
|
+// QwSopTempVoice voice = optional.get();
|
|
|
+// st.setVoiceUrl(voice.getVoiceUrl());
|
|
|
+// st.setVoiceDuration(voice.getDuration()+"");
|
|
|
+// });
|
|
|
+// }
|
|
|
// 发送语言 end
|
|
|
if (content.getType()==5){
|
|
|
sopAddTag(logVo,content,sendTime);
|
|
@@ -626,7 +655,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
Long fsUserId = contactId.getFsUserId();
|
|
|
QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId);
|
|
|
handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
- type, qwUserId, companyUserId, companyId, externalId, fsUserId);
|
|
|
+ type, qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId);
|
|
|
} catch (Exception e) {
|
|
|
log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
|
|
|
}
|
|
@@ -678,14 +707,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content,
|
|
|
SopUserLogsVo logVo, Date sendTime, Long courseId,
|
|
|
Long videoId, int type, String qwUserId,
|
|
|
- String companyUserId, String companyId, String externalId, Long fsUserId) {
|
|
|
+ String companyUserId, String companyId, String externalId,String welcomeText,
|
|
|
+ String qwUserName,Long fsUserId) {
|
|
|
switch (type) {
|
|
|
case 1:
|
|
|
handleNormalMessage(sopLogs, content,companyUserId);
|
|
|
break;
|
|
|
case 2:
|
|
|
handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
- qwUserId, companyUserId, companyId, externalId, fsUserId);
|
|
|
+ qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId);
|
|
|
break;
|
|
|
case 3:
|
|
|
handleOrderMessage(sopLogs, content);
|
|
@@ -717,7 +747,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
|
|
|
SopUserLogsVo logVo, Date sendTime, Long courseId,
|
|
|
Long videoId, String qwUserId, String companyUserId,
|
|
|
- String companyId, String externalId, Long fsUserId) {
|
|
|
+ String companyId, String externalId,String welcomeText,
|
|
|
+ String qwUserName,Long fsUserId) {
|
|
|
// 深拷贝 Content 对象,避免使用 JSON
|
|
|
QwSopTempSetting.Content clonedContent = deepCopyContent(content);
|
|
|
if (clonedContent == null) {
|
|
@@ -737,30 +768,231 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
|
|
|
// 顺序处理每个 Setting,避免过多的并行导致线程开销
|
|
|
for (QwSopTempSetting.Content.Setting setting : settings) {
|
|
|
- if ("1".equals(setting.getIsBindUrl())&&("3".equals(setting.getContentType())||"1".equals(setting.getContentType()))) {
|
|
|
- addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
- String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId,
|
|
|
- qwUserId, companyUserId, companyId, externalId, fsUserId);
|
|
|
- if (StringUtils.isNotEmpty(sortLink)) {
|
|
|
- if ("3".equals(setting.getContentType())) {
|
|
|
- setting.setLinkUrl(sortLink);
|
|
|
- } else {
|
|
|
- String currentValue = setting.getValue();
|
|
|
- if (currentValue == null) {
|
|
|
- setting.setValue(sortLink);
|
|
|
+ switch (setting.getContentType()) {
|
|
|
+ //文字和短链一起
|
|
|
+ case "1":
|
|
|
+ case "3":
|
|
|
+ if ("1".equals(setting.getIsBindUrl())) {
|
|
|
+
|
|
|
+ addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
+ String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId,
|
|
|
+ qwUserId, companyUserId, companyId, externalId,fsUserId);
|
|
|
+ if (StringUtils.isNotEmpty(sortLink)) {
|
|
|
+ if ("3".equals(setting.getContentType())) {
|
|
|
+ setting.setLinkUrl(sortLink);
|
|
|
+ } else {
|
|
|
+ String currentValue = setting.getValue();
|
|
|
+ if (currentValue == null) {
|
|
|
+ setting.setValue(sortLink);
|
|
|
+ } else {
|
|
|
+// setting.setValue(currentValue + "\n" + sortLink);
|
|
|
+ setting.setValue(currentValue
|
|
|
+ .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)
|
|
|
+ + "\n" + sortLink);
|
|
|
+ }
|
|
|
+ }
|
|
|
} else {
|
|
|
- setting.setValue(currentValue + "\n" + sortLink);
|
|
|
+ log.error("生成短链失败,跳过设置 URL。");
|
|
|
+ }
|
|
|
+
|
|
|
+ }else {
|
|
|
+ if ("1".equals(setting.getContentType())) {
|
|
|
+ setting.setValue(setting.getValue()
|
|
|
+ .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText));
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- log.error("生成短链失败,跳过设置 URL。");
|
|
|
- }
|
|
|
+ break;
|
|
|
+ //小程序单独
|
|
|
+ case "4":
|
|
|
+
|
|
|
+ addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
+
|
|
|
+ String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId,
|
|
|
+ qwUserId, companyUserId, companyId, externalId,fsUserId);
|
|
|
+
|
|
|
+ setting.setMiniprogramPage(sortLink);
|
|
|
+
|
|
|
+ try {
|
|
|
+ setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl())?"https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png":setting.getMiniprogramPicUrl());
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("赋值-小程序封面地址失败-"+e);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ //app
|
|
|
+ case "9":
|
|
|
+ addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
+
|
|
|
+ QwCreateLinkByAppDTO linkByApp = createLinkByApp(setting, logVo, sendTime, courseId, videoId,
|
|
|
+ qwUserId, companyUserId, companyId, externalId,sopLogs.getCorpId(),qwUserName,fsUserId);
|
|
|
+
|
|
|
+ setting.setLinkUrl(linkByApp.getSortLink());
|
|
|
+ setting.setAppLinkUrl(linkByApp.getAppMsgLink());
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
+// if ("1".equals(setting.getIsBindUrl())&&("3".equals(setting.getContentType())||"1".equals(setting.getContentType()))) {
|
|
|
+//
|
|
|
+// addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
+// String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId,
|
|
|
+// qwUserId, companyUserId, companyId, externalId, fsUserId);
|
|
|
+//
|
|
|
+// if (StringUtils.isNotEmpty(sortLink)) {
|
|
|
+// if ("3".equals(setting.getContentType())) {
|
|
|
+// setting.setLinkUrl(sortLink);
|
|
|
+// } else {
|
|
|
+// String currentValue = setting.getValue();
|
|
|
+// if (currentValue == null) {
|
|
|
+// setting.setValue(sortLink);
|
|
|
+// } else {
|
|
|
+// setting.setValue(currentValue
|
|
|
+// .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)
|
|
|
+// + "\n" + sortLink);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// log.error("生成短链失败,跳过设置 URL。");
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
}
|
|
|
sopLogs.setContentJson(JSON.toJSONString(clonedContent));
|
|
|
enqueueQwSopLogs(sopLogs);
|
|
|
}
|
|
|
|
|
|
+ private QwCreateLinkByAppDTO createLinkByApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
|
|
|
+ Long courseId, Long videoId, String qwUserId,
|
|
|
+ String companyUserId, String companyId, String externalId, String corpId, String qwUserName,Long fsUserId){
|
|
|
+ // 获取缓存的配置
|
|
|
+ CourseConfig config;
|
|
|
+ synchronized(configLock) {
|
|
|
+ config = cachedCourseConfig;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (config == null) {
|
|
|
+ log.error("CourseConfig is not loaded.");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, qwUserId,
|
|
|
+ companyUserId, companyId, externalId, 4);
|
|
|
+
|
|
|
+ FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
+ BeanUtils.copyProperties(link,courseMap);
|
|
|
+ courseMap.setFsUserId(fsUserId);
|
|
|
+
|
|
|
+ String courseJson = JSON.toJSONString(courseMap);
|
|
|
+ String realLinkFull = REAL_LINK_PREFIX + courseJson;
|
|
|
+ link.setRealLink(realLinkFull);
|
|
|
+
|
|
|
+ Date updateTime = createUpdateTime(setting, sendTime, config);
|
|
|
+
|
|
|
+ link.setUpdateTime(updateTime);
|
|
|
+
|
|
|
+ String sortLink = appLink+link.getLink()+"&videoId="+videoId;
|
|
|
+
|
|
|
+ String appMsgLink=appRealLink+link.getLink();
|
|
|
+
|
|
|
+ QwCreateLinkByAppDTO byAppDTO=new QwCreateLinkByAppDTO();
|
|
|
+ byAppDTO.setSortLink(sortLink);
|
|
|
+ byAppDTO.setAppMsgLink(appMsgLink);
|
|
|
+
|
|
|
+ FsCourseSopAppLink fsCourseSopAppLink = createFsCourseSopAppLink(link.getLink(), sendTime, updateTime, companyId, companyUserId, qwUserId,
|
|
|
+ qwUserName, corpId, courseId, setting.getLinkTitle(), setting.getLinkImageUrl(), videoId,
|
|
|
+ setting.getLinkDescribe(), appMsgLink, externalId);
|
|
|
+
|
|
|
+ enqueueCourseSopAppLink(fsCourseSopAppLink);
|
|
|
+
|
|
|
+ enqueueCourseLink(link);
|
|
|
+
|
|
|
+ return byAppDTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ public FsCourseSopAppLink createFsCourseSopAppLink(String link, Date sendTime, Date updateTime, String companyId,
|
|
|
+ String companyUserId,String qwUserId,String qwUserName,String corpId,
|
|
|
+ Long courseId,String linkTile,String linkImageUrl,Long videoId,
|
|
|
+ String linkDescribe,String appMsgLink,String externalId){
|
|
|
+
|
|
|
+ FsCourseSopAppLink sopAppLink=new FsCourseSopAppLink();
|
|
|
+ sopAppLink.setLink(link);
|
|
|
+ sopAppLink.setCreateTime(sendTime);
|
|
|
+ sopAppLink.setUpdateTime(updateTime);
|
|
|
+ sopAppLink.setCompanyId(Long.parseLong(companyId));
|
|
|
+ sopAppLink.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
+ sopAppLink.setQwUserId(Long.parseLong(qwUserId));
|
|
|
+ sopAppLink.setQwUserName(qwUserName);
|
|
|
+ sopAppLink.setCorpId(corpId);
|
|
|
+ sopAppLink.setCourseId(courseId);
|
|
|
+ sopAppLink.setCourseTitle(linkTile);
|
|
|
+ sopAppLink.setCourseUrl(linkImageUrl);
|
|
|
+ sopAppLink.setVideoId(videoId);
|
|
|
+ sopAppLink.setVideoTitle(linkDescribe);
|
|
|
+ sopAppLink.setAppRealLink(appMsgLink);
|
|
|
+ sopAppLink.setQwExternalId(Long.parseLong(externalId));
|
|
|
+
|
|
|
+
|
|
|
+ return sopAppLink;
|
|
|
+ }
|
|
|
+ private String createLinkByMiniApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
|
|
|
+ Long courseId, Long videoId, String qwUserId,
|
|
|
+ String companyUserId, String companyId, String externalId,Long fsUserId) {
|
|
|
+ // 获取缓存的配置
|
|
|
+ CourseConfig config;
|
|
|
+ synchronized(configLock) {
|
|
|
+ config = cachedCourseConfig;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (config == null) {
|
|
|
+ log.error("CourseConfig is not loaded.");
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+// if (StringUtils.isEmpty(config.getMiniprogramPage())){
|
|
|
+// log.error("miniprogramPage is not loaded.");
|
|
|
+// return "";
|
|
|
+// }
|
|
|
+
|
|
|
+ // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
|
|
|
+ FsCourseLink link = new FsCourseLink();
|
|
|
+ link.setCompanyId(Long.parseLong(companyId));
|
|
|
+ link.setQwUserId(qwUserId);
|
|
|
+ link.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
+ link.setVideoId(videoId.longValue());
|
|
|
+ link.setCorpId(logVo.getCorpId());
|
|
|
+ link.setCourseId(courseId.longValue());
|
|
|
+ link.setQwExternalId(Long.parseLong(externalId));
|
|
|
+ link.setLinkType(3); //正常链接
|
|
|
+ String randomString = generateRandomStringWithLock();
|
|
|
+ link.setLink(randomString);
|
|
|
+ link.setCreateTime(sendTime);
|
|
|
+
|
|
|
+
|
|
|
+ FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
+ BeanUtils.copyProperties(link,courseMap);
|
|
|
+ courseMap.setFsUserId(fsUserId);
|
|
|
+
|
|
|
+ String courseJson = JSON.toJSONString(courseMap);
|
|
|
+ String realLinkFull = miniappRealLink + courseJson;
|
|
|
+// String realLinkFull = config.getMiniprogramPage() + courseJson;
|
|
|
+ link.setRealLink(realLinkFull);
|
|
|
+
|
|
|
+
|
|
|
+ Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0)
|
|
|
+ ? config.getVideoLinkExpireDate()
|
|
|
+ : setting.getExpiresDays();
|
|
|
+
|
|
|
+ // 使用 Java 8 时间 API 计算过期时间
|
|
|
+ LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
+ LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1);
|
|
|
+ expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
|
|
|
+ Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
+ link.setUpdateTime(updateTime);
|
|
|
+
|
|
|
+ //存短链-
|
|
|
+ enqueueCourseLink(link);
|
|
|
+ return link.getRealLink();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 深拷贝 Content 对象,避免使用 JSON 进行序列化和反序列化
|
|
|
*/
|
|
@@ -771,12 +1003,45 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
return content.clone();
|
|
|
}
|
|
|
|
|
|
+ private Date createUpdateTime(QwSopTempSetting.Content.Setting setting,Date sendTime,CourseConfig config){
|
|
|
+
|
|
|
+ Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0)
|
|
|
+ ? config.getVideoLinkExpireDate()
|
|
|
+ : setting.getExpiresDays();
|
|
|
+
|
|
|
+// 使用 Java 8 时间 API 计算过期时间
|
|
|
+ LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
+ LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1);
|
|
|
+ expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
|
|
|
+ Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
+
|
|
|
+ return updateTime;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
private void handleOrderMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content) {
|
|
|
sopLogs.setContentJson(JSON.toJSONString(content));
|
|
|
enqueueQwSopLogs(sopLogs);
|
|
|
}
|
|
|
|
|
|
+ public FsCourseLink createFsCourseLink(String corpId, Date sendTime,Long courseId,Long videoId, String qwUserId,
|
|
|
+ String companyUserId, String companyId,String externalId,Integer type){
|
|
|
+ // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
|
|
|
+ FsCourseLink link = new FsCourseLink();
|
|
|
+ link.setCompanyId(Long.parseLong(companyId));
|
|
|
+ link.setQwUserId(qwUserId);
|
|
|
+ link.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
+ link.setVideoId(videoId.longValue());
|
|
|
+ link.setCorpId(corpId);
|
|
|
+ link.setCourseId(courseId.longValue());
|
|
|
+ link.setQwExternalId(Long.parseLong(externalId));
|
|
|
+ link.setLinkType(type); //小程序
|
|
|
+ String randomString = generateRandomStringWithLock();
|
|
|
+ link.setLink(randomString);
|
|
|
+ link.setCreateTime(sendTime);
|
|
|
|
|
|
+ return link;
|
|
|
+ }
|
|
|
|
|
|
|
|
|
private String generateShortLink(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
|
|
@@ -934,6 +1199,23 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 FsCourseSopAppLing 放入队列
|
|
|
+ */
|
|
|
+ private void enqueueCourseSopAppLink(FsCourseSopAppLink sopAppLink) {
|
|
|
+ try {
|
|
|
+ boolean offered = sopAppLinks.offer(sopAppLink, 5, TimeUnit.SECONDS);
|
|
|
+ if (!offered) {
|
|
|
+ log.error("FsCourseSopAppLink 队列已满,无法添加日志: {}", JSON.toJSONString(sopAppLink));
|
|
|
+ // 处理队列已满的情况,例如记录到失败队列或持久化存储
|
|
|
+ }
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ log.error("插入 FsCourseLink 队列时被中断: {}", e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 消费 QwSopLogs 队列并进行批量插入
|
|
|
*/
|
|
@@ -1021,6 +1303,36 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 消费 FsCourseSopAppLink 队列并进行批量插入
|
|
|
+ */
|
|
|
+ private void consumeCourseSopAppLink() {
|
|
|
+ List<FsCourseSopAppLink> batch = new ArrayList<>(BATCH_SIZE);
|
|
|
+ while (running || !sopAppLinks.isEmpty()) {
|
|
|
+ try {
|
|
|
+ FsCourseSopAppLink courseSopAppLink = sopAppLinks.poll(1, TimeUnit.SECONDS);
|
|
|
+ if (courseSopAppLink != null) {
|
|
|
+ batch.add(courseSopAppLink);
|
|
|
+ }
|
|
|
+ if (batch.size() >= BATCH_SIZE || (!batch.isEmpty() && courseSopAppLink == null)) {
|
|
|
+ if (!batch.isEmpty()) {
|
|
|
+ batchInsertFsCourseSopAppLink(new ArrayList<>(batch));
|
|
|
+ batch.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ log.error("FsCourseSopAppLink 消费线程被中断: {}", e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理剩余的数据
|
|
|
+ if (!batch.isEmpty()) {
|
|
|
+ batchInsertFsCourseSopAppLink(batch);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 批量插入 QwSopLogs
|
|
|
*/
|
|
@@ -1080,6 +1392,26 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
+ * 批量插入 FsCourseSopAppLink
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Retryable(
|
|
|
+ value = { Exception.class },
|
|
|
+ maxAttempts = 3,
|
|
|
+ backoff = @Backoff(delay = 2000)
|
|
|
+ )
|
|
|
+ public void batchInsertFsCourseSopAppLink(List<FsCourseSopAppLink> courseSopAppLinkToInsert) {
|
|
|
+ try {
|
|
|
+ fsCourseSopAppLinkMapper.insertFsCourseSopAppLinkBatch(courseSopAppLinkToInsert);
|
|
|
+ log.info("批量插入 FsCourseSopAppLink 完成,共插入 {} 条记录。", courseSopAppLinkToInsert.size());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("批量插入 FsCourseSopAppLink 失败: {}", e.getMessage(), e);
|
|
|
+ // 可选:将失败的数据记录到失败队列或持久化存储以便后续重试
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
@Override
|
|
|
public void updateSopLogsByCancel() {
|
|
|
List<QwSopLogs> sopLogs = qwSopLogsMapper.selectQwSopLogsByCancel();
|