|
|
@@ -1,2336 +0,0 @@
|
|
|
-package com.fs.app.taskService.impl;
|
|
|
-
|
|
|
-import cn.hutool.core.util.ObjectUtil;
|
|
|
-import com.alibaba.fastjson.JSON;
|
|
|
-import com.alibaba.fastjson.JSONArray;
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
-import com.fs.app.taskService.SopLogsTaskService;
|
|
|
-import com.fs.common.config.FSSysConfig;
|
|
|
-import com.fs.common.utils.PubFun;
|
|
|
-import com.fs.common.utils.StringUtils;
|
|
|
-import com.fs.company.domain.Company;
|
|
|
-import com.fs.company.domain.CompanyMiniapp;
|
|
|
-import com.fs.company.domain.CompanyUser;
|
|
|
-import com.fs.company.mapper.CompanyMapper;
|
|
|
-import com.fs.company.service.ICompanyMiniappService;
|
|
|
-import com.fs.company.service.ICompanyUserService;
|
|
|
-import com.fs.config.cloud.CloudHostProper;
|
|
|
-import com.fs.course.config.CourseConfig;
|
|
|
-import com.fs.course.domain.*;
|
|
|
-import com.fs.course.mapper.*;
|
|
|
-import com.fs.course.service.IFsCourseLinkService;
|
|
|
-import com.fs.course.service.IFsUserCompanyBindService;
|
|
|
-import com.fs.qw.domain.*;
|
|
|
-import com.fs.qw.mapper.QwExternalContactMapper;
|
|
|
-import com.fs.qw.mapper.QwUserMapper;
|
|
|
-import com.fs.qw.service.IQwCompanyService;
|
|
|
-import com.fs.qw.service.IQwGroupChatService;
|
|
|
-import com.fs.qw.service.IQwGroupChatUserService;
|
|
|
-import com.fs.qw.service.impl.QwExternalContactServiceImpl;
|
|
|
-import com.fs.qw.vo.GroupUserExternalVo;
|
|
|
-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.mapper.*;
|
|
|
-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.QwCreateLinkByAppVO;
|
|
|
-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;
|
|
|
-import org.springframework.scheduling.annotation.Async;
|
|
|
-import org.springframework.scheduling.annotation.Scheduled;
|
|
|
-import org.springframework.stereotype.Service;
|
|
|
-import org.springframework.transaction.annotation.Transactional;
|
|
|
-
|
|
|
-import javax.annotation.PostConstruct;
|
|
|
-import javax.annotation.PreDestroy;
|
|
|
-import java.time.LocalDate;
|
|
|
-import java.time.LocalDateTime;
|
|
|
-import java.time.LocalTime;
|
|
|
-import java.time.ZoneId;
|
|
|
-import java.time.format.DateTimeFormatter;
|
|
|
-import java.time.temporal.ChronoUnit;
|
|
|
-import java.util.*;
|
|
|
-import java.util.concurrent.*;
|
|
|
-import java.util.concurrent.atomic.AtomicInteger;
|
|
|
-import java.util.stream.Collectors;
|
|
|
-
|
|
|
-import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
|
|
|
-
|
|
|
-@Service
|
|
|
-@Slf4j
|
|
|
-public class SopLogsTaskServiceImpl implements SopLogsTaskService {
|
|
|
-
|
|
|
-
|
|
|
- private static final String REAL_LINK_PREFIX = "/courseH5/pages/course/learning?course=";
|
|
|
- private static final String SHORT_LINK_PREFIX = "/courseH5/pages/course/learning?s=";
|
|
|
- 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=";
|
|
|
-
|
|
|
-// private static final String miniappRealLink = "/pages/index/index?course=";
|
|
|
-
|
|
|
- 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 DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd 07:00:00");
|
|
|
-
|
|
|
-
|
|
|
- // Cached configurations and domain names
|
|
|
- private CourseConfig cachedCourseConfig;
|
|
|
- private final Object configLock = new Object();
|
|
|
-
|
|
|
- private List<FsCourseDomainName> cachedDomainNames;
|
|
|
- private final Object domainLock = new Object();
|
|
|
-
|
|
|
-
|
|
|
- // Batch size for database inserts, configurable via application.properties
|
|
|
- private final int BATCH_SIZE = 500;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private IFsCourseLinkService courseLinkService;
|
|
|
- @Autowired
|
|
|
- private SopUserLogsMapper sopUserLogsMapper;
|
|
|
- @Autowired
|
|
|
- private QwSopTagMapper qwSopTagMapper ;
|
|
|
- @Autowired
|
|
|
- private QwSopMapper sopMapper;
|
|
|
-
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private QwExternalContactServiceImpl qwExternalContactService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private FsCourseWatchLogMapper fsCourseWatchLogMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private IQwSopLogsService qwSopLogsService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private QwSopLogsMapper qwSopLogsMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private FsCourseLinkMapper fsCourseLinkMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private FsCourseSopAppLinkMapper fsCourseSopAppLinkMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private ISysConfigService configService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private FsCourseDomainNameMapper fsCourseDomainNameMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private SopUserLogsInfoMapper sopUserLogsInfoMapper;
|
|
|
- @Autowired
|
|
|
- private QwUserMapper qwUserMapper;
|
|
|
- @Autowired
|
|
|
- private IQwSopTempRulesService qwSopTempRulesService;
|
|
|
- @Autowired
|
|
|
- private IQwSopTempContentService qwSopTempContentService;
|
|
|
- @Autowired
|
|
|
- private IQwSopTempVoiceService qwSopTempVoiceService;
|
|
|
- @Autowired
|
|
|
- private CloudHostProper cloudHostProper;
|
|
|
-
|
|
|
- // Blocking queues with bounded capacity to implement backpressure
|
|
|
- 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;
|
|
|
- @Autowired
|
|
|
- private IQwGroupChatService qwGroupChatService;
|
|
|
- @Autowired
|
|
|
- private IQwGroupChatUserService qwGroupChatUserService;
|
|
|
- @Autowired
|
|
|
- private ICompanyMiniappService companyMiniappService;
|
|
|
- // Shutdown flags
|
|
|
- private volatile boolean running = true;
|
|
|
- @Autowired
|
|
|
- private QwSopTempMapper qwSopTempMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private ICompanyUserService companyUserService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private IQwCompanyService iQwCompanyService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private CompanyMapper companyMapper;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private AsyncCourseWatchFinishService asyncCourseWatchFinishService;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private IFsUserCompanyBindService fsUserCompanyBindService;
|
|
|
-
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private IQwSopTempVoiceService sopTempVoiceService;
|
|
|
-
|
|
|
- @PostConstruct
|
|
|
- public void init() {
|
|
|
- loadCourseConfig();
|
|
|
- startConsumers();
|
|
|
- }
|
|
|
-
|
|
|
- private void loadCourseConfig() {
|
|
|
- try {
|
|
|
- String json = configService.selectConfigByKey("course.config");
|
|
|
- CourseConfig config = JSON.parseObject(json, CourseConfig.class);
|
|
|
- if (config != null) {
|
|
|
- cachedCourseConfig = config;
|
|
|
- log.info("Loaded course.config successfully.");
|
|
|
- } else {
|
|
|
- log.error("Failed to load course.config from configService.");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("Exception while loading course.config: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- private void startConsumers() {
|
|
|
- qwSopLogsExecutor = Executors.newSingleThreadExecutor(r -> {
|
|
|
- Thread t = new Thread(r, "QwSopLogsConsumer");
|
|
|
- t.setDaemon(true);
|
|
|
- return t;
|
|
|
- });
|
|
|
- watchLogsExecutor = Executors.newSingleThreadExecutor(r -> {
|
|
|
- Thread t = new Thread(r, "WatchLogsConsumer");
|
|
|
- t.setDaemon(true);
|
|
|
- return t;
|
|
|
- });
|
|
|
- courseLinkExecutor = Executors.newSingleThreadExecutor(r -> {
|
|
|
- Thread t = new Thread(r, "courseLinkConsumer");
|
|
|
- t.setDaemon(true);
|
|
|
- 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
|
|
|
- @Scheduled(fixedDelay = 60000) // 每60秒刷新一次
|
|
|
- public void refreshCourseConfig() {
|
|
|
- synchronized(configLock) {
|
|
|
- try {
|
|
|
- String json = configService.selectConfigByKey("course.config");
|
|
|
- CourseConfig newConfig = JSON.parseObject(json, CourseConfig.class);
|
|
|
- if (newConfig != null) {
|
|
|
- cachedCourseConfig = newConfig;
|
|
|
- log.info("Refreshed course.config.");
|
|
|
- } else {
|
|
|
- log.error("Failed to refresh course.config.");
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("Exception while refreshing course.config: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- @PreDestroy
|
|
|
- public void shutdownConsumers() {
|
|
|
- running = false;
|
|
|
- qwSopLogsExecutor.shutdown();
|
|
|
- watchLogsExecutor.shutdown();
|
|
|
- courseLinkExecutor.shutdown();
|
|
|
- courseSopAppLinkExecutor.shutdown();
|
|
|
- try {
|
|
|
- if (!qwSopLogsExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
|
- qwSopLogsExecutor.shutdownNow();
|
|
|
- }
|
|
|
- if (!watchLogsExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
|
- watchLogsExecutor.shutdownNow();
|
|
|
- }
|
|
|
- if (!courseLinkExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
|
- courseLinkExecutor.shutdownNow();
|
|
|
- }
|
|
|
- if (!courseSopAppLinkExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
|
- courseSopAppLinkExecutor.shutdownNow();
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- qwSopLogsExecutor.shutdownNow();
|
|
|
- watchLogsExecutor.shutdownNow();
|
|
|
- courseLinkExecutor.shutdownNow();
|
|
|
- courseSopAppLinkExecutor.shutdownNow();
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void selectSopUserLogsListByTime(LocalDateTime currentTime, List<String> sopidList) throws Exception {
|
|
|
- long startTimeMillis = System.currentTimeMillis();
|
|
|
- log.info("====== 开始选择和处理 SOP 用户日志 ======");
|
|
|
-
|
|
|
- // 获取缓存的配置
|
|
|
- CourseConfig config;
|
|
|
- synchronized(configLock) {
|
|
|
- config = cachedCourseConfig;
|
|
|
- }
|
|
|
-
|
|
|
- List<SopUserLogsVo> sopUserLogsVos = sopUserLogsMapper.selectSopUserLogsListByTime(sopidList);
|
|
|
- if (sopUserLogsVos.isEmpty()) {
|
|
|
- log.info("没有需要处理的 SOP 用户日志。");
|
|
|
- return;
|
|
|
- }
|
|
|
- sopUserLogsVos = sopUserLogsVos.stream().filter(e -> e.getFilterMode() == 1 || (e.getFilterMode() == 2 && StringUtils.isNotEmpty(e.getChatId()))).collect(Collectors.toList());
|
|
|
-
|
|
|
- String[] array = sopUserLogsVos.stream().map(SopUserLogsVo::getChatId).filter(StringUtils::isNotEmpty).toArray(String[]::new);
|
|
|
- Map<String, QwGroupChat> groupChatMap = new HashMap<>();
|
|
|
- if (array.length > 0) {
|
|
|
- List<QwGroupChat> qwGroupChatList = qwGroupChatService.selectQwGroupChatByChatIds(array);
|
|
|
- List<QwGroupChatUser> qwGroupChatUserList = qwGroupChatUserService.selectQwGroupChatUserByChatIds(array);
|
|
|
- List<String> groupChatUserIds = PubFun.listToNewList(qwGroupChatUserList, QwGroupChatUser::getUserId);
|
|
|
- if(!groupChatUserIds.isEmpty()){
|
|
|
- List<GroupUserExternalVo> userList = qwExternalContactMapper.selectByGroupUser(groupChatUserIds);
|
|
|
- Map<String, List<GroupUserExternalVo>> userMap = PubFun.listToMapByGroupList(userList, GroupUserExternalVo::getExternalUserId);
|
|
|
- qwGroupChatUserList.forEach(e -> {
|
|
|
- e.setUserList(userMap.getOrDefault(e.getUserId(), Collections.emptyList()));
|
|
|
- });
|
|
|
- }
|
|
|
- Map<String, List<QwGroupChatUser>> chatUserMap = PubFun.listToMapByGroupList(qwGroupChatUserList, QwGroupChatUser::getChatId);
|
|
|
- qwGroupChatList.stream().filter(e -> chatUserMap.containsKey(e.getChatId())).forEach(e -> e.setChatUserList(chatUserMap.get(e.getChatId())));
|
|
|
- groupChatMap = PubFun.listToMapByGroupObject(qwGroupChatList, QwGroupChat::getChatId);
|
|
|
- }
|
|
|
-
|
|
|
- Map<String, List<SopUserLogsVo>> sopLogsGroupedById = sopUserLogsVos.stream()
|
|
|
- .collect(Collectors.groupingBy(SopUserLogsVo::getSopId));
|
|
|
-
|
|
|
- // 查询公司关联小程序数据
|
|
|
- List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
|
|
|
-
|
|
|
- Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
|
|
|
-
|
|
|
-
|
|
|
- List<Company> companies = companyMapper.selectCompanyAllList();
|
|
|
-
|
|
|
- log.info("共分组 {} 个 SOP ID 进行处理。", sopLogsGroupedById.size());
|
|
|
-
|
|
|
- CountDownLatch sopGroupLatch = new CountDownLatch(sopLogsGroupedById.size());
|
|
|
-
|
|
|
- for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
|
|
|
- String sopId = entry.getKey();
|
|
|
- List<SopUserLogsVo> userLogsVos = entry.getValue();
|
|
|
- processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap,config,miniMap,companies);
|
|
|
- }
|
|
|
-
|
|
|
- // 等待所有 SOP 分组处理完成
|
|
|
- sopGroupLatch.await();
|
|
|
-
|
|
|
- // 触发批量插入(可选,如果需要立即插入队列中的数据)
|
|
|
- // batchInsertQwSopLogs();
|
|
|
- // batchInsertFsCourseWatchLogs();
|
|
|
-
|
|
|
- long endTimeMillis = System.currentTimeMillis();
|
|
|
- log.info("====== SOP 用户日志处理完成,耗时 {} 毫秒 ======", (endTimeMillis - startTimeMillis));
|
|
|
- }
|
|
|
-
|
|
|
- @Async("sopTaskExecutor")
|
|
|
- @Retryable(
|
|
|
- value = { Exception.class },
|
|
|
- maxAttempts = 3,
|
|
|
- backoff = @Backoff(delay = 2000)
|
|
|
- )
|
|
|
- public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch ,LocalDateTime currentTime,
|
|
|
- Map<String, QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
- List<Company> companies) {
|
|
|
- try {
|
|
|
- processSopGroup(sopId, userLogsVos,currentTime, groupChatMap, config,miniMap,companies);
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
|
|
|
- } finally {
|
|
|
- latch.countDown();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos,LocalDateTime currentTime, Map<String,
|
|
|
- QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
- List<Company> companies) throws Exception {
|
|
|
- QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
|
|
|
-
|
|
|
- if (ruleTimeVO == null) {
|
|
|
-// sopUserLogsMapper.deleteSopUserLogsBySopId(sopId);
|
|
|
- log.error("SOP ID {} 已删除或不存在,相关日志已清除。", sopId);
|
|
|
- return;
|
|
|
- }
|
|
|
- QwSopTemp qwSopTemp = qwSopTempMapper.selectQwSopTempById(ruleTimeVO.getTempId());
|
|
|
- if (qwSopTemp == null) {
|
|
|
-// sopUserLogsMapper.deleteSopUserLogsBySopId(sopId);
|
|
|
- log.error("SOP ID {} 模板不存在,相关日志已清除。", sopId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- ruleTimeVO.setTempStatus(qwSopTemp.getStatus());
|
|
|
- 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);
|
|
|
- log.error("SOP ID {} 的状态为停用,相关日志状态已更新。", sopId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- List<QwSopTempRules> rulesList = qwSopTempRulesService.listByTempId(ruleTimeVO.getTempId());
|
|
|
- if (rulesList.isEmpty()) {
|
|
|
- log.error("SOP ID {} 的 TempSetting 为空,跳过处理。", sopId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(ruleTimeVO.getCorpId());
|
|
|
-
|
|
|
- if (qwCompany == null ) {
|
|
|
- log.error("SOP ID {} 的 公司信息为空 为空,跳过处理。", sopId);
|
|
|
- return ;
|
|
|
- }
|
|
|
-
|
|
|
- CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
|
|
|
- for (SopUserLogsVo logVo : userLogsVos) {
|
|
|
- processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime, groupChatMap,qwCompany.getMiniAppId(),
|
|
|
- config,miniMap,companies);
|
|
|
- }
|
|
|
-
|
|
|
- // 等待所有用户日志处理完成
|
|
|
- try {
|
|
|
- userLogsLatch.await();
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("等待用户日志处理完成时被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- log.info("SOP ID {} 的所有用户日志已处理完毕。", sopId);
|
|
|
- }
|
|
|
-
|
|
|
- @Async("sopTaskExecutor")
|
|
|
- @Retryable(
|
|
|
- value = { Exception.class },
|
|
|
- maxAttempts = 3,
|
|
|
- backoff = @Backoff(delay = 2000)
|
|
|
- )
|
|
|
- public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
|
|
|
- CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,
|
|
|
- String miniAppId,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
- List<Company> companies) {
|
|
|
- try {
|
|
|
- processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap, miniAppId, config,miniMap,companies);
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
|
|
|
- } finally {
|
|
|
- latch.countDown();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void processUserLog(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
|
|
|
- LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,String miniAppId,
|
|
|
- CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
- List<Company> companies) {
|
|
|
- try {
|
|
|
-
|
|
|
- LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
|
|
|
- LocalDate currentDate = currentTime.toLocalDate();
|
|
|
-
|
|
|
- long daysBetween = ChronoUnit.DAYS.between(startDate, currentDate);
|
|
|
- int tempGap = ruleTimeVO.getTempGap();
|
|
|
-
|
|
|
- if (tempGap <= 0) {
|
|
|
- log.error("SOP ID {} 的 TempGap {} 无效,跳过处理。", logVo.getSopId(), tempGap);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- int intervalDay = (int) (daysBetween / tempGap);
|
|
|
- if (intervalDay < 0 || intervalDay >= tempSettings.size()) {
|
|
|
- log.info("用户日志 {} 的 intervalDay {} 超出 TempSettings 范围,跳过处理。", logVo.getId(), intervalDay);
|
|
|
- return;
|
|
|
- }
|
|
|
- long day = daysBetween;
|
|
|
- if(day == 0 && ruleTimeVO.getIsAutoSop() == 1){
|
|
|
- day = 1;
|
|
|
- }else{
|
|
|
- day++;
|
|
|
- }
|
|
|
- List<QwSopTempSetting.Content> contents = getDay(tempSettings, day);
|
|
|
- if (contents == null || contents.isEmpty()) {
|
|
|
- log.error("SOP ID {} 的 TempSetting 内容为空,跳过处理。天数 {}", logVo.getSopId(),day);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- //获取企业微信员工的称呼//从redis里或者从库里取
|
|
|
- QwUser qwUserByRedis = qwExternalContactService.getQwUserByRedis(logVo.getCorpId(),logVo.getQwUserId());
|
|
|
- if (qwUserByRedis==null){
|
|
|
- log.error("无企微员工信息 {} 跳过处理。:{}", logVo.getUserId(),logVo.getCorpId());
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- String qwUserId = String.valueOf(qwUserByRedis.getId()).trim();
|
|
|
- String companyUserId = String.valueOf(qwUserByRedis.getCompanyUserId()).trim();
|
|
|
- String companyId = String.valueOf(qwUserByRedis.getCompanyId()).trim();
|
|
|
- Integer sendMsgType = qwUserByRedis.getSendMsgType();
|
|
|
-
|
|
|
- if (StringUtil.strIsNullOrEmpty(companyUserId) || StringUtil.strIsNullOrEmpty(companyId) || "null".equals(companyUserId)) {
|
|
|
- log.error("员工未绑定销售账号或公司,跳过处理:"+qwUserId);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- CompanyUser companyUser = companyUserService.selectCompanyUserByIdForRedis(Long.valueOf(companyUserId));
|
|
|
- if (Objects.nonNull(companyUser)) {
|
|
|
- if (!StringUtil.strIsNullOrEmpty(companyUser.getDomain())) {
|
|
|
- logVo.setDomain(companyUser.getDomain().trim());
|
|
|
- } else {
|
|
|
- logVo.setDomain(config.getRealLinkDomainName().trim());
|
|
|
- }
|
|
|
- } else {
|
|
|
- logVo.setDomain(config.getRealLinkDomainName().trim());
|
|
|
- }
|
|
|
-
|
|
|
- //寻找时间
|
|
|
-// LocalDateTime currentTime = LocalDateTime.of(2024, 12, 25,23 , 40);
|
|
|
-
|
|
|
- // 先算好 60分钟后 ~ 再60分钟后的时间段
|
|
|
- LocalDateTime startRangeFirst = currentTime.plusMinutes(60);
|
|
|
-
|
|
|
- // 如果发现已经跨天
|
|
|
- if (!startRangeFirst.toLocalDate().equals(currentDate)) {
|
|
|
- // 更新 currentDate
|
|
|
- currentDate = startRangeFirst.toLocalDate();
|
|
|
-
|
|
|
- // 重新计算 daysBetween
|
|
|
- daysBetween = ChronoUnit.DAYS.between(startDate, currentDate);
|
|
|
- intervalDay = (int) (daysBetween / tempGap);
|
|
|
- day = daysBetween;
|
|
|
- if(day == 0 && ruleTimeVO.getIsAutoSop() == 1){
|
|
|
- day = 1;
|
|
|
- }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);
|
|
|
- if (contents == null || contents.isEmpty()) {
|
|
|
- log.error("跨天-SOP ID {} 的 TempSetting 内容为空,跳过处理。", logVo.getSopId());
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // 只有整倍数才做事
|
|
|
- if (daysBetween % tempGap != 0) {
|
|
|
- log.error("天数差 {} 不是 tempGap {} 的整数倍,跳过操作,SopId {} ", daysBetween, tempGap,logVo.getSopId());
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- for (QwSopTempSetting.Content content : contents) {
|
|
|
- try {
|
|
|
-
|
|
|
- LocalTime elementLocalTime = LocalTime.parse(content.getTime());
|
|
|
- LocalDateTime elementDateTime = LocalDateTime.of(currentTime.toLocalDate(), elementLocalTime);
|
|
|
-
|
|
|
- // 动态调整 elementDateTime 的日期
|
|
|
- if (elementLocalTime.isBefore(currentTime.toLocalTime())) {
|
|
|
- elementDateTime = elementDateTime.plusDays(1);
|
|
|
- }
|
|
|
-
|
|
|
- LocalDateTime startRange = currentTime.plusMinutes(60);
|
|
|
- LocalDateTime endRange = startRange.plusMinutes(60);
|
|
|
-
|
|
|
- // 跨天逻辑修正:仅当 startRange 的时间晚于 endRange 的时间时调整
|
|
|
- if (startRange.toLocalTime().isAfter(endRange.toLocalTime())
|
|
|
- && startRange.toLocalDate().equals(endRange.toLocalDate())) {
|
|
|
- endRange = endRange.plusDays(1); // 将 endRange 调整为第二天
|
|
|
- }
|
|
|
- if (!elementDateTime.isBefore(startRange) && !elementDateTime.isAfter(endRange.minusMinutes(1))) {
|
|
|
-
|
|
|
- // 如果时间差在目标范围内,更新记录
|
|
|
- // 组合年月日和element的时间
|
|
|
- LocalDate targetDate = startDate.plusDays(intervalDay * tempGap);
|
|
|
-
|
|
|
- // 将 targetDate 和 elementTime 组合成 LocalDateTime
|
|
|
- LocalDateTime dateTime = LocalDateTime.of(targetDate, elementLocalTime);
|
|
|
-
|
|
|
- // 将 LocalDateTime 转换为 Date
|
|
|
- Date sendTime = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
-
|
|
|
- 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);
|
|
|
- if (!externalContactIdList.isEmpty()) {
|
|
|
- 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 TODO
|
|
|
-// 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, qwUserId,
|
|
|
- companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName(),
|
|
|
- groupChatMap, miniAppId,config,miniMap, sendMsgType,companies);
|
|
|
-
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("解析模板内容 {} 失败: {}", content.getTime(), e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("解析解析模板 {} 失败: {}", logVo.getStartTime(), e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private List<QwSopTempSetting.Content> getDay(List<QwSopTempRules> tempSettings, long days){
|
|
|
- List<QwSopTempRules> collect = tempSettings.stream().filter(e -> e.getDayNum() == days && e.getTime() != null).collect(Collectors.toList());
|
|
|
- AtomicInteger i = new AtomicInteger();
|
|
|
- return collect.stream().map(e -> {
|
|
|
- QwSopTempSetting.Content content = new QwSopTempSetting.Content();
|
|
|
- content.setId(e.getId());
|
|
|
- content.setType(e.getType());
|
|
|
- content.setContentType(e.getContentType() != null ? e.getContentType().toString() : null);
|
|
|
- content.setSetting(e.getSettingList().stream().map(s -> {
|
|
|
- QwSopTempSetting.Content.Setting setting = JSON.parseObject(s.getContent(), QwSopTempSetting.Content.Setting.class);
|
|
|
- setting.setId(s.getId());
|
|
|
- return setting;
|
|
|
- }).collect(Collectors.toList()));
|
|
|
- content.setAddTag(e.getAddTag());
|
|
|
- content.setDelTag(e.getDelTag());
|
|
|
- content.setTime(e.getTime());
|
|
|
- content.setIsOfficial(e.getIsOfficial());
|
|
|
- content.setCourseId(e.getCourseId());
|
|
|
- content.setVideoId(e.getVideoId());
|
|
|
- content.setCourseType(e.getCourseType());
|
|
|
- content.setAiTouch(e.getAiTouch());
|
|
|
- return content;
|
|
|
- }).sorted(Comparator.comparing(e -> LocalTime.parse(e.getTime() + ":00"))).peek(e -> e.setIndex(i.getAndIncrement())).collect(Collectors.toList());
|
|
|
- }
|
|
|
-
|
|
|
- //消息处理
|
|
|
- private void insertSopUserLogs(List<SopUserLogsInfo> sopUserLogsInfos, SopUserLogsVo logVo, Date sendTime,
|
|
|
- QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content,
|
|
|
- String qwUserId,String companyUserId,String companyId,String welcomeText,String qwUserName,
|
|
|
- Map<String, QwGroupChat> groupChatMap,String miniAppId,CourseConfig config,
|
|
|
- Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap, Integer sendMsgType,
|
|
|
- List<Company> companies) {
|
|
|
- String formattedSendTime = sendTime.toInstant()
|
|
|
- .atZone(ZoneId.systemDefault())
|
|
|
- .format(DATE_TIME_FORMATTER);
|
|
|
- int type = content.getType();
|
|
|
- Long courseId = content.getCourseId();
|
|
|
- Long videoId = content.getVideoId();
|
|
|
- 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);
|
|
|
- if (valuesList != null && !valuesList.isEmpty()) {
|
|
|
- try {
|
|
|
- List<QwSopTempVoice> voiceList = qwSopTempVoiceService.getVoiceByText(Long.parseLong(companyUserId), valuesList);
|
|
|
- if (voiceList != null && !voiceList.isEmpty()) {
|
|
|
- Map<String, QwSopTempVoice> collect = voiceList.stream().collect(Collectors.toMap(QwSopTempVoice::getVoiceTxt, e -> e));
|
|
|
- setting.parallelStream().filter(e -> "7".equals(e.getContentType())).forEach(st -> {
|
|
|
- QwSopTempVoice voice = collect.get(st.getValue());
|
|
|
- if (voice.getVoiceUrl() == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- st.setVoiceUrl(voice.getVoiceUrl());
|
|
|
- st.setVoiceDuration(voice.getDuration() + "");
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (NumberFormatException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-// // 发送语音 end
|
|
|
- if (content.getType()==5){
|
|
|
- sopAddTag(logVo,content,sendTime);
|
|
|
- }
|
|
|
-
|
|
|
- //当语音模板的qw_sop_temp_voice中无对应语音,就不生成qw_sop_logs记录
|
|
|
- if (content.getType() == 7 && content.getSetting() != null && !content.getSetting().isEmpty()) {
|
|
|
- if (content.getSetting().get(0).getVoiceUrl() == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (StringUtils.isNotEmpty(logVo.getChatId())) {
|
|
|
- QwGroupChat groupChat = groupChatMap.get(logVo.getChatId());
|
|
|
- ruleTimeVO.setSendType(6);
|
|
|
- ruleTimeVO.setType(2);
|
|
|
- if (groupChat.getChatUserList() != null && !groupChat.getChatUserList().isEmpty()) {
|
|
|
- QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null,null);
|
|
|
- handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
- type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName,
|
|
|
- null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies);
|
|
|
- }
|
|
|
-// if (content.getIndex() == 0) {
|
|
|
-// QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null);
|
|
|
-// handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
-// type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName,
|
|
|
-// null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies);
|
|
|
-// } else {
|
|
|
-// if(groupChat.getChatUserList() != null && !groupChat.getChatUserList().isEmpty()){
|
|
|
-// groupChat.getChatUserList().forEach(user -> {
|
|
|
-// ruleTimeVO.setSendType(2);
|
|
|
-// ruleTimeVO.setRemark("客户群催课");
|
|
|
-// QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, user.getUserId(), user.getName(), null, isOfficial, null);
|
|
|
-// handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
-// type, qwUserId, companyUserId, companyId, user.getId().toString(), welcomeText, qwUserName,
|
|
|
-// null, false, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies);
|
|
|
-// });
|
|
|
-// }
|
|
|
-// }
|
|
|
- } else {
|
|
|
- // 处理每个 externalContactId
|
|
|
- sopUserLogsInfos.forEach(contactId -> {
|
|
|
- try {
|
|
|
- String externalId = contactId.getExternalId().toString();
|
|
|
- String externalUserName = contactId.getExternalUserName();
|
|
|
- Long fsUserId = contactId.getFsUserId();
|
|
|
- Integer grade = contactId.getGrade();
|
|
|
- QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId(),contactId.getIsDaysNotStudy());
|
|
|
- handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
- type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId,
|
|
|
- null,config, miniMap, grade, sendMsgType,companies);
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-// // 处理每个 externalContactId
|
|
|
-// sopUserLogsInfos.forEach(contactId -> {
|
|
|
-// try {
|
|
|
-// String externalId = contactId.getExternalId().toString();
|
|
|
-// String externalUserName = contactId.getExternalUserName();
|
|
|
-// Long fsUserId = contactId.getFsUserId();
|
|
|
-// QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId,isOfficial,contactId.getExternalId());
|
|
|
-// handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
-// type, qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName);
|
|
|
-// } catch (Exception e) {
|
|
|
-// log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
|
|
|
-// }
|
|
|
-// });
|
|
|
- }
|
|
|
-
|
|
|
- private void sopAddTag(SopUserLogsVo logVo, QwSopTempSetting.Content content, Date sendTime) {
|
|
|
- String id = logVo.getId();
|
|
|
- String addTag = content.getAddTag();
|
|
|
- String delTag = content.getDelTag();
|
|
|
- String corpId = logVo.getCorpId();
|
|
|
- if (addTag!=null || delTag!=null) {
|
|
|
- QwSopTag qwSopTag = new QwSopTag();
|
|
|
- qwSopTag.setAddTags(addTag);
|
|
|
- qwSopTag.setDelTags(delTag);
|
|
|
- qwSopTag.setCorpId(corpId);
|
|
|
- qwSopTag.setSopUserLogsId(id);
|
|
|
- qwSopTag.setType(1);
|
|
|
- qwSopTag.setStatus(1);
|
|
|
- qwSopTag.setSendTime(sendTime);
|
|
|
- qwSopTag.setCreateTime(new Date());
|
|
|
- qwSopTagMapper.insertQwSopTag(qwSopTag);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private QwSopLogs createBaseLog(String formattedSendTime, SopUserLogsVo logVo,
|
|
|
- QwSopRuleTimeVO ruleTimeVO, String externalContactId,
|
|
|
- String externalUserName, Long fsUserId,Integer isOfficial,
|
|
|
- Long externalId,Integer isDaysNotStudy) {
|
|
|
- QwSopLogs sopLogs = new QwSopLogs();
|
|
|
- sopLogs.setSendTime(formattedSendTime);
|
|
|
- sopLogs.setQwUserid(logVo.getQwUserId());
|
|
|
- sopLogs.setCorpId(logVo.getCorpId());
|
|
|
- sopLogs.setLogType(ruleTimeVO.getType());
|
|
|
- sopLogs.setTakeRecords(0);
|
|
|
-
|
|
|
- if (isOfficial != 1 && Integer.valueOf(1).equals(isDaysNotStudy)) {
|
|
|
- sopLogs.setSendStatus(5L);
|
|
|
- sopLogs.setRemark("E级客户不发送");
|
|
|
- }else {
|
|
|
- sopLogs.setSendStatus(3L);
|
|
|
- }
|
|
|
-
|
|
|
- sopLogs.setReceivingStatus(0L);
|
|
|
-
|
|
|
- if (isOfficial == 1) {
|
|
|
-
|
|
|
- if (logVo.getIsSampSend()== 1) {
|
|
|
- if (fsUserId == null || Long.valueOf(0L).equals(fsUserId)) {
|
|
|
- sopLogs.setSendType(2);
|
|
|
- sopLogs.setRemark("未绑定小程序用户,单链补发");
|
|
|
- //时间设置成固定8点
|
|
|
- LocalDateTime dateTime = LocalDateTime.parse(formattedSendTime, DATE_TIME_FORMATTER);
|
|
|
- sopLogs.setSendTime(OUTPUT_FORMATTER.format(dateTime));
|
|
|
- } else {
|
|
|
- sopLogs.setSendType(1);
|
|
|
- }
|
|
|
-
|
|
|
- }else {
|
|
|
- if (fsUserId == null || Long.valueOf(0L).equals(fsUserId)) {
|
|
|
- sopLogs.setTakeRecords(1);
|
|
|
- sopLogs.setSendType(1);
|
|
|
- }else {
|
|
|
- sopLogs.setSendType(1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- } else if (isOfficial == 0) {
|
|
|
- sopLogs.setSendType(ruleTimeVO.getSendType() == 1 ? 2 : ruleTimeVO.getSendType());
|
|
|
- } else {
|
|
|
- sopLogs.setSendType(ruleTimeVO.getSendType());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- String[] userKey = logVo.getUserId().split("\\|");
|
|
|
- sopLogs.setCompanyId(Long.valueOf(userKey[2].trim()));
|
|
|
- if (StringUtils.isNotEmpty(userKey[0].trim())){
|
|
|
- sopLogs.setQwUserKey(Long.valueOf(userKey[0].trim()));
|
|
|
- }
|
|
|
- sopLogs.setSopId(logVo.getSopId());
|
|
|
- sopLogs.setSort(Integer.valueOf(logVo.getStartTime().replaceAll("-","")));
|
|
|
- sopLogs.setExternalUserId(externalContactId);
|
|
|
- sopLogs.setExternalId(externalId);
|
|
|
- sopLogs.setExternalUserName(externalUserName);
|
|
|
- sopLogs.setFsUserId(fsUserId);
|
|
|
- sopLogs.setUserLogsId(logVo.getId());
|
|
|
-
|
|
|
- if (ObjectUtil.isNotEmpty(logVo.getActualQwId())){
|
|
|
- sopLogs.setQwUserKey(logVo.getActualQwId());
|
|
|
- }
|
|
|
- return sopLogs;
|
|
|
- }
|
|
|
-
|
|
|
- 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, String welcomeText,
|
|
|
- String qwUserName, Long fsUserId, boolean isGroupChat, String miniAppId,
|
|
|
- QwGroupChat groupChat,CourseConfig config,
|
|
|
- Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
- Integer grade, Integer sendMsgType ,List<Company> companies ) {
|
|
|
- switch (type) {
|
|
|
- case 1:
|
|
|
- handleNormalMessage(sopLogs, content,companyUserId);
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
|
|
|
- qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName, fsUserId,
|
|
|
- isGroupChat, miniAppId, groupChat,config,miniMap, grade, sendMsgType,companies);
|
|
|
- break;
|
|
|
- case 3:
|
|
|
- handleOrderMessage(sopLogs, content);
|
|
|
- break;
|
|
|
- case 4:
|
|
|
-// handleAIMessage(sopLogs, content);
|
|
|
- break;
|
|
|
- case 5:
|
|
|
-// handleTagMessage(sopLogs, content);
|
|
|
- break;
|
|
|
- case 7:
|
|
|
- handleVoiceMessage(sopLogs, content, companyUserId);
|
|
|
- break;
|
|
|
- default:
|
|
|
- log.error("未知的消息类型 {},跳过处理。", type);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- private void handleVoiceMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content, String companyUserId) {
|
|
|
- sopLogs.setContentJson(JSON.toJSONString(content));
|
|
|
- enqueueQwSopLogs(sopLogs);
|
|
|
- }
|
|
|
-
|
|
|
- private void handleNormalMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,String companyUserId) {
|
|
|
-
|
|
|
- sopLogs.setContentJson(JSON.toJSONString(content));
|
|
|
- enqueueQwSopLogs(sopLogs);
|
|
|
- }
|
|
|
-
|
|
|
- private void handleAIMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content) {
|
|
|
- sopLogs.setContentJson(JSON.toJSONString(content));
|
|
|
- sopLogs.setSort(3);
|
|
|
- enqueueQwSopLogs(sopLogs);
|
|
|
- }
|
|
|
-
|
|
|
- private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
|
|
|
- SopUserLogsVo logVo, Date sendTime, Long courseId, Long videoId, String qwUserId, String companyUserId,
|
|
|
- String companyId, String externalId, String welcomeText, String qwUserName,
|
|
|
- Long fsUserId, boolean isGroupChat, String miniAppId, QwGroupChat groupChat,CourseConfig config,Map<Long,
|
|
|
- Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType,
|
|
|
- List<Company> companies) {
|
|
|
- QwExternalContact contact = null;
|
|
|
- if(logVo.getExternalId() != null){
|
|
|
- contact = qwExternalContactMapper.selectById(logVo.getExternalId());
|
|
|
- }
|
|
|
- // 深拷贝 Content 对象,避免使用 JSON
|
|
|
- QwSopTempSetting.Content clonedContent = deepCopyContent(content);
|
|
|
- if (clonedContent == null) {
|
|
|
- log.error("Failed to clone content, skipping handleCourseMessage.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-//
|
|
|
-// Integer courseType = clonedContent.getCourseType();
|
|
|
-
|
|
|
- String isOfficial = clonedContent.getIsOfficial();
|
|
|
-
|
|
|
- List<QwSopTempSetting.Content.Setting> settings = clonedContent.getSetting();
|
|
|
- if (settings == null || settings.isEmpty()) {
|
|
|
- log.error("Cloned content settings are empty, skipping.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 顺序处理每个 Setting,避免过多的并行导致线程开销
|
|
|
- for (QwSopTempSetting.Content.Setting setting : settings) {
|
|
|
- switch (setting.getContentType()) {
|
|
|
- //文字和短链一起
|
|
|
- case "1":
|
|
|
- case "3":
|
|
|
-// if ("1".equals(setting.getIsBindUrl())) {
|
|
|
-// String link;
|
|
|
-// if (isGroupChat) {
|
|
|
-// FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
|
|
|
-// createParam.setCourseId(courseId);
|
|
|
-// createParam.setVideoId(videoId);
|
|
|
-// createParam.setCorpId(logVo.getCorpId());
|
|
|
-// createParam.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
-// createParam.setCompanyId(Long.parseLong(companyId));
|
|
|
-// createParam.setChatId(logVo.getChatId());
|
|
|
-// createParam.setQwUserId(Long.valueOf(qwUserId));
|
|
|
-// createParam.setDays(setting.getExpiresDays());
|
|
|
-// R createLink = courseLinkService.createRoomLinkUrl(createParam);
|
|
|
-// if (createLink.get("code").equals(500)) {
|
|
|
-// throw new BaseException("链接生成失败!");
|
|
|
-// }
|
|
|
-// try {
|
|
|
-// groupChat.getChatUserList().stream().filter(e -> e.getUserList() != null && !e.getUserList().isEmpty()).forEach(e -> {
|
|
|
-// Map<String, GroupUserExternalVo> userMap = PubFun.listToMapByGroupObject(e.getUserList(), GroupUserExternalVo::getUserId);
|
|
|
-// GroupUserExternalVo vo = userMap.get(groupChat.getOwner());
|
|
|
-// if (vo != null && vo.getId() != null) {
|
|
|
-// sopLogs.setFsUserId(vo.getFsUserId());
|
|
|
-// addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, vo.getId().toString(), logVo);
|
|
|
-// }
|
|
|
-// });
|
|
|
-// } catch (Exception e) {
|
|
|
-// log.error("群聊创建看课记录失败!", e);
|
|
|
-// }
|
|
|
-// link = (String) createLink.get("url");
|
|
|
-// } else {
|
|
|
-// addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo);
|
|
|
-// link = generateShortLink(setting, logVo, sendTime, courseId, videoId,
|
|
|
-// qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId());
|
|
|
-// }
|
|
|
-
|
|
|
-// if (StringUtils.isNotEmpty(link)) {
|
|
|
-// if ("3".equals(setting.getContentType())) {
|
|
|
-// setting.setLinkUrl(link);
|
|
|
-// } else {
|
|
|
-// String currentValue = setting.getValue();
|
|
|
-// if (currentValue == null) {
|
|
|
-// setting.setValue(link);
|
|
|
-// } else {
|
|
|
-// setting.setValue(currentValue
|
|
|
-// .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
|
|
|
-// .replaceAll("#客户称呼#", contact == null || StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus())
|
|
|
-// + "\n" + link);
|
|
|
-// }
|
|
|
-// }
|
|
|
-// } else {
|
|
|
-// log.error("生成短链失败,跳过设置 URL。");
|
|
|
-// }
|
|
|
-
|
|
|
-// } else {
|
|
|
- if ("1".equals(setting.getContentType())) {
|
|
|
- String defaultName = "同学";
|
|
|
- if(contact != null && StringUtils.isNotEmpty(contact.getName()) && !"待同步客户".equals(contact.getName())){
|
|
|
- defaultName = contact.getName();
|
|
|
- }
|
|
|
- setting.setValue(setting.getValue()
|
|
|
- .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
|
|
|
- .replaceAll("#客户称呼#", contact == null || StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?defaultName:contact.getStageStatus()));
|
|
|
- }
|
|
|
-// }
|
|
|
- break;
|
|
|
- //小程序单独
|
|
|
- case "4":
|
|
|
- if (isGroupChat) {
|
|
|
- try {
|
|
|
- groupChat.getChatUserList().stream().filter(e -> e.getUserList() != null && !e.getUserList().isEmpty()).forEach(e -> {
|
|
|
- Map<String, GroupUserExternalVo> userMap = PubFun.listToMapByGroupObject(e.getUserList(), GroupUserExternalVo::getUserId);
|
|
|
- GroupUserExternalVo vo = userMap.get(groupChat.getOwner());
|
|
|
- if (vo != null && vo.getId() != null) {
|
|
|
- sopLogs.setFsUserId(vo.getFsUserId());
|
|
|
- addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, vo.getId().toString(), logVo);
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("群聊创建看课记录失败!", e);
|
|
|
- }
|
|
|
- } else {
|
|
|
- addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
- }
|
|
|
-
|
|
|
- String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId,
|
|
|
- qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId(), isGroupChat ? groupChat.getChatId() : null);
|
|
|
-
|
|
|
- if(sopLogs.getSendType()==1){
|
|
|
- setting.setMiniprogramAppid(miniAppId);
|
|
|
- }else {
|
|
|
- int miniType = getLevel(grade);
|
|
|
- //算主备小程序
|
|
|
- String finalAppId = getAppIdFromMiniMap(miniMap, companyId, sendMsgType, grade);
|
|
|
-
|
|
|
- if (StringUtil.strIsNullOrEmpty(finalAppId)) {
|
|
|
- finalAppId = miniAppId;
|
|
|
- }
|
|
|
-
|
|
|
- setting.setMiniType(miniType);
|
|
|
- if (!StringUtil.strIsNullOrEmpty(finalAppId)) {
|
|
|
- setting.setMiniprogramAppid(finalAppId);
|
|
|
- } else {
|
|
|
- log.error("公司的小程序id为空:采用了前端传的固定值" + sopLogs.getSopId());
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- setting.setMiniprogramPage(sortLink.replaceAll("^[\\s\\u2005]+", ""));
|
|
|
-
|
|
|
- try {
|
|
|
- setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl())? config.getSidebarImageUrl():setting.getMiniprogramPicUrl());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("赋值-小程序封面地址失败-" + e);
|
|
|
- }
|
|
|
-
|
|
|
- break;
|
|
|
- //app
|
|
|
- case "9":
|
|
|
- addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
-
|
|
|
- QwCreateLinkByAppVO linkByApp = createLinkByApp(setting, logVo, sendTime, courseId, videoId,
|
|
|
- qwUserId, companyUserId, companyId, externalId,sopLogs.getCorpId(),qwUserName);
|
|
|
-
|
|
|
- setting.setLinkUrl(linkByApp.getSortLink().replaceAll("^[\\s\\u2005]+", ""));
|
|
|
- setting.setAppLinkUrl(linkByApp.getAppMsgLink().replaceAll("^[\\s\\u2005]+", ""));
|
|
|
- setting.setCourseUrl(setting.getLinkImageUrl());
|
|
|
- setting.setTitle(setting.getLinkDescribe()); //小节名称
|
|
|
-
|
|
|
- break;
|
|
|
- //自定义小程序
|
|
|
- case "10":
|
|
|
- addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
|
|
|
-
|
|
|
- Optional<Company> matchedCompany = companies.stream()
|
|
|
- .filter(company -> String.valueOf(company.getCompanyId()).equals(companyId))
|
|
|
- .findFirst();
|
|
|
- if (matchedCompany.isPresent()) {
|
|
|
- Company company = matchedCompany.get();
|
|
|
-
|
|
|
- String customMiniAppId = company.getCustomMiniAppId();
|
|
|
-
|
|
|
- if (customMiniAppId != null && !customMiniAppId.trim().isEmpty()) {
|
|
|
- setting.setMiniprogramAppid(customMiniAppId);
|
|
|
- } else {
|
|
|
- setting.setMiniprogramAppid("该公司未配置自定义小程序:"+companyId);
|
|
|
- }
|
|
|
- } else {
|
|
|
- setting.setMiniprogramAppid("未找到匹配的公司的自定义小程序:"+companyId);
|
|
|
- }
|
|
|
-
|
|
|
- break;
|
|
|
- //直播小程序单独
|
|
|
- case "12":
|
|
|
- String sortLiveLink;
|
|
|
- sortLiveLink = "/pages_course/living?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + setting.getLiveId();
|
|
|
-
|
|
|
-
|
|
|
- String miniprogramLiveTitle = setting.getMiniprogramTitle();
|
|
|
- int maxLiveLength = 17;
|
|
|
- setting.setMiniprogramTitle(miniprogramLiveTitle.length() > maxLiveLength ? miniprogramLiveTitle.substring(0, maxLiveLength) + "..." : miniprogramLiveTitle);
|
|
|
- String json = configService.selectConfigByKey("his.config");
|
|
|
- FSSysConfig sysConfig= JSON.parseObject(json,FSSysConfig.class);
|
|
|
- setting.setMiniprogramAppid(sysConfig.getAppId());
|
|
|
- setting.setMiniprogramPage(sortLiveLink);
|
|
|
- setting.setContentType("4");
|
|
|
- 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;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- clonedContent.getSetting().stream().filter(e -> "1".equals(e.getIsBindUrl())).forEach(e -> {
|
|
|
- e.setIsBindUrl("0");
|
|
|
-// e.setLinkDescribe(null);
|
|
|
- e.setLinkUrl(null);
|
|
|
-// e.setLinkImageUrl(null);
|
|
|
- });
|
|
|
- sopLogs.setContentJson(JSON.toJSONString(clonedContent));
|
|
|
- enqueueQwSopLogs(sopLogs);
|
|
|
- }
|
|
|
-
|
|
|
- private String getAppIdFromMiniMap(Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
|
|
|
- String companyId,
|
|
|
- int sendMsgType,
|
|
|
- Integer grade) {
|
|
|
- if (miniMap.isEmpty() || sendMsgType != 1) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- Map<Integer, List<CompanyMiniapp>> gradeMap = miniMap.get(Long.valueOf(companyId));
|
|
|
- if (gradeMap == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- int listIndex = getLevel(grade);
|
|
|
- List<CompanyMiniapp> miniapps = gradeMap.get(listIndex);
|
|
|
-
|
|
|
- if (miniapps == null || miniapps.isEmpty()) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- CompanyMiniapp companyMiniapp = miniapps.get(0);
|
|
|
- return (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId()))
|
|
|
- ? companyMiniapp.getAppId()
|
|
|
- : null;
|
|
|
- }
|
|
|
-
|
|
|
- private static int getLevel(Integer grade) {
|
|
|
- int effectiveGrade = (grade == null) ? 5 : grade;
|
|
|
- int listIndex = (effectiveGrade == 1 || effectiveGrade == 2) ? 0 : 1;
|
|
|
- return listIndex;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 深拷贝 Content 对象,避免使用 JSON 进行序列化和反序列化
|
|
|
- */
|
|
|
- private QwSopTempSetting.Content deepCopyContent(QwSopTempSetting.Content content) {
|
|
|
- if (content == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- return content.clone();
|
|
|
- }
|
|
|
-
|
|
|
- private void handleOrderMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content) {
|
|
|
- sopLogs.setContentJson(JSON.toJSONString(content));
|
|
|
- enqueueQwSopLogs(sopLogs);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- private String generateShortLink(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
|
|
|
- Long courseId, Long videoId, String qwUserId,
|
|
|
- String companyUserId, String companyId, String externalId,String isOfficial, Long fsUserId) {
|
|
|
- // 获取缓存的配置
|
|
|
- CourseConfig config;
|
|
|
- synchronized(configLock) {
|
|
|
- config = cachedCourseConfig;
|
|
|
- }
|
|
|
-
|
|
|
- if (config == null) {
|
|
|
- log.error("CourseConfig is not loaded.");
|
|
|
- return "";
|
|
|
- }
|
|
|
-
|
|
|
- // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
|
|
|
- FsCourseLink link = new FsCourseLink();
|
|
|
- link.setCompanyId(Long.parseLong(companyId));
|
|
|
- link.setQwUserId(Long.parseLong(qwUserId));
|
|
|
- link.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
- link.setVideoId(videoId.longValue());
|
|
|
- link.setCorpId(logVo.getCorpId());
|
|
|
- link.setCourseId(courseId.longValue());
|
|
|
- link.setQwExternalId(Long.parseLong(externalId));
|
|
|
-
|
|
|
- if (StringUtil.strIsNullOrEmpty(isOfficial)){
|
|
|
- link.setLinkType(0);
|
|
|
- }else {
|
|
|
- if (isOfficial.equals("1")) {
|
|
|
- if (fsUserId== null || Long.valueOf(0L).equals(fsUserId)){
|
|
|
- link.setLinkType(0);
|
|
|
- }else {
|
|
|
- link.setLinkType(5);
|
|
|
- }
|
|
|
- }else if (isOfficial.equals("0")){
|
|
|
- link.setLinkType(0);
|
|
|
- }else{
|
|
|
- link.setLinkType(0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
- courseMap.setCompanyId(link.getCompanyId());
|
|
|
- courseMap.setQwUserId(link.getQwUserId());
|
|
|
- courseMap.setCompanyUserId(link.getCompanyUserId());
|
|
|
- courseMap.setVideoId(link.getVideoId());
|
|
|
- courseMap.setCorpId(link.getCorpId());
|
|
|
- courseMap.setCourseId(link.getCourseId());
|
|
|
- courseMap.setQwExternalId(link.getQwExternalId());
|
|
|
- courseMap.setFsUserId(fsUserId);
|
|
|
-
|
|
|
- if (StringUtil.strIsNullOrEmpty(isOfficial)){
|
|
|
- courseMap.setLinkType(0);
|
|
|
- }else {
|
|
|
- if (isOfficial.equals("1")) {
|
|
|
- if (fsUserId== null || Long.valueOf(0L).equals(fsUserId)){
|
|
|
- courseMap.setLinkType(0);
|
|
|
- }else {
|
|
|
- courseMap.setLinkType(5);
|
|
|
- }
|
|
|
- }else if (isOfficial.equals("0")){
|
|
|
- courseMap.setLinkType(0);
|
|
|
- }else{
|
|
|
- courseMap.setLinkType(0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- String courseJson = JSON.toJSONString(courseMap);
|
|
|
- String realLinkFull = REAL_LINK_PREFIX + courseJson;
|
|
|
- link.setRealLink(realLinkFull);
|
|
|
-
|
|
|
- String randomString = generateRandomStringWithLock();
|
|
|
- if (StringUtil.strIsNullOrEmpty(randomString)){
|
|
|
- link.setLink(UUID.randomUUID().toString().replace("-", ""));
|
|
|
- }else {
|
|
|
- link.setLink(randomString);
|
|
|
- }
|
|
|
-
|
|
|
- link.setCreateTime(sendTime);
|
|
|
-
|
|
|
- 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);
|
|
|
-
|
|
|
- //取销售绑定的二级域名
|
|
|
- String sortLink = logVo.getDomain() + SHORT_LINK_PREFIX + link.getLink();
|
|
|
- enqueueCourseLink(link);
|
|
|
- return sortLink.replaceAll("^[\\s\\u2005]+", "");
|
|
|
- }
|
|
|
-
|
|
|
- private QwCreateLinkByAppVO 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){
|
|
|
- // 获取缓存的配置
|
|
|
- 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);
|
|
|
-
|
|
|
- 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();
|
|
|
-
|
|
|
- QwCreateLinkByAppVO byAppVO=new QwCreateLinkByAppVO();
|
|
|
- byAppVO.setSortLink(sortLink);
|
|
|
- byAppVO.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 byAppVO;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- 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(Long.parseLong(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();
|
|
|
- if (StringUtil.strIsNullOrEmpty(randomString)){
|
|
|
- link.setLink(UUID.randomUUID().toString().replace("-", ""));
|
|
|
- }else {
|
|
|
- link.setLink(randomString);
|
|
|
- }
|
|
|
-
|
|
|
- link.setCreateTime(sendTime);
|
|
|
-
|
|
|
- return link;
|
|
|
- }
|
|
|
-
|
|
|
- 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 String createLinkByMiniApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
|
|
|
- Long courseId, Long videoId, String qwUserId,
|
|
|
- String companyUserId, String companyId, String externalId,String isOfficial,Long fsUserId, String chatId) {
|
|
|
- // 获取缓存的配置
|
|
|
- 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(Long.parseLong(qwUserId));
|
|
|
- link.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
- link.setVideoId(videoId);
|
|
|
- link.setCorpId(logVo.getCorpId());
|
|
|
- link.setCourseId(courseId);
|
|
|
- if(StringUtils.isEmpty(chatId)){
|
|
|
- link.setQwExternalId(Long.parseLong(externalId));
|
|
|
- }
|
|
|
- link.setProjectCode(cloudHostProper.getProjectCode());
|
|
|
- link.setChatId(chatId);
|
|
|
-
|
|
|
- if (StringUtil.strIsNullOrEmpty(isOfficial)){
|
|
|
- link.setLinkType(3);
|
|
|
- }else {
|
|
|
- if (isOfficial.equals("1")) {
|
|
|
- if (fsUserId== null || Long.valueOf(0L).equals(fsUserId)){
|
|
|
- link.setLinkType(3);
|
|
|
- }else {
|
|
|
- link.setLinkType(5);
|
|
|
- }
|
|
|
- }else if (isOfficial.equals("0")){
|
|
|
- link.setLinkType(3);
|
|
|
- }else{
|
|
|
- link.setLinkType(3);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- String randomString = generateRandomStringWithLock();
|
|
|
- if (StringUtil.strIsNullOrEmpty(randomString)){
|
|
|
- link.setLink(UUID.randomUUID().toString().replace("-", ""));
|
|
|
- }else {
|
|
|
- link.setLink(randomString);
|
|
|
- }
|
|
|
-
|
|
|
- link.setCreateTime(sendTime);
|
|
|
-
|
|
|
- FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
- BeanUtils.copyProperties(link,courseMap);
|
|
|
-
|
|
|
- String courseJson = JSON.toJSONString(courseMap);
|
|
|
- String realLinkFull = miniappRealLink + 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().replaceAll("^[\\s\\u2005]+", "");
|
|
|
- }
|
|
|
-
|
|
|
- private void addWatchLogIfNeeded(QwSopLogs sopLogs, Long videoId, Long courseId,
|
|
|
- Date sendTime, String qwUserId, String companyUserId,
|
|
|
- String companyId, String externalId,SopUserLogsVo logsVo) {
|
|
|
- FsCourseWatchLog watchLog = new FsCourseWatchLog();
|
|
|
- watchLog.setVideoId(videoId != null ? videoId.longValue() : null);
|
|
|
- watchLog.setQwExternalContactId(externalId != null ? Long.valueOf(externalId) : null);
|
|
|
- watchLog.setSendType(2);
|
|
|
- watchLog.setQwUserId(Long.parseLong(qwUserId));
|
|
|
- watchLog.setSopId(sopLogs.getSopId());
|
|
|
- watchLog.setDuration(0L);
|
|
|
- watchLog.setCourseId(courseId != null ? courseId.longValue() : null);
|
|
|
- watchLog.setCompanyUserId(companyUserId != null ? Long.valueOf(companyUserId) : null);
|
|
|
- watchLog.setCompanyId(companyId != null ? Long.valueOf(companyId) : null);
|
|
|
- watchLog.setCreateTime(convertStringToDate(sopLogs.getSendTime(),"yyyy-MM-dd HH:mm:ss"));
|
|
|
- watchLog.setUpdateTime(new Date());
|
|
|
- watchLog.setLogType(3);
|
|
|
- watchLog.setUserId(sopLogs.getFsUserId());
|
|
|
- watchLog.setCampPeriodTime(convertStringToDate(logsVo.getStartTime(),"yyyy-MM-dd"));
|
|
|
- enqueueWatchLog(watchLog);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 时间字符串转Date时间
|
|
|
- * @param dateString
|
|
|
- * @return
|
|
|
- */
|
|
|
- public static Date convertStringToDate(String dateString,String pattern) {
|
|
|
- if (dateString == null || dateString.isEmpty()) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
|
|
- LocalDateTime localDateTime;
|
|
|
- LocalDate localDate;
|
|
|
- // 先解析成 LocalDate(只含年月日)
|
|
|
- if (pattern.equals("yyyy-MM-dd")){
|
|
|
- // 先解析成 LocalDate(只含年月日)
|
|
|
- localDate = LocalDate.parse(dateString, formatter);
|
|
|
- // 将 LocalDate 转为当天 00:00:00 的 LocalDateTime
|
|
|
- localDateTime = localDate.atStartOfDay();
|
|
|
- }else {
|
|
|
- localDateTime = LocalDateTime.parse(dateString, formatter);
|
|
|
- }
|
|
|
- return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 将 QwSopLogs 放入队列
|
|
|
- */
|
|
|
- private void enqueueQwSopLogs(QwSopLogs sopLogs) {
|
|
|
- try {
|
|
|
- boolean offered = qwSopLogsQueue.offer(sopLogs, 5, TimeUnit.SECONDS);
|
|
|
- if (!offered) {
|
|
|
- log.error("QwSopLogs 队列已满,无法添加日志: {}", JSON.toJSONString(sopLogs));
|
|
|
- // 处理队列已满的情况,例如记录到失败队列或持久化存储
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("插入 QwSopLogs 队列时被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将 FsCourseWatchLog 放入队列
|
|
|
- */
|
|
|
- private void enqueueWatchLog(FsCourseWatchLog watchLog) {
|
|
|
- try {
|
|
|
- boolean offered = watchLogsQueue.offer(watchLog, 5, TimeUnit.SECONDS);
|
|
|
- if (!offered) {
|
|
|
- log.error("FsCourseWatchLog 队列已满,无法添加日志: {}", JSON.toJSONString(watchLog));
|
|
|
- // 处理队列已满的情况,例如记录到失败队列或持久化存储
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("插入 FsCourseWatchLog 队列时被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将 FsCourseWatchLog 放入队列
|
|
|
- */
|
|
|
- private void enqueueCourseLink(FsCourseLink courseLink) {
|
|
|
- try {
|
|
|
- boolean offered = linkQueue.offer(courseLink, 5, TimeUnit.SECONDS);
|
|
|
- if (!offered) {
|
|
|
- log.error("FsCourseLink 队列已满,无法添加日志: {}", JSON.toJSONString(courseLink));
|
|
|
- // 处理队列已满的情况,例如记录到失败队列或持久化存储
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("插入 FsCourseLink 队列时被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将 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 队列并进行批量插入
|
|
|
- */
|
|
|
- private void consumeQwSopLogs() {
|
|
|
- List<QwSopLogs> batch = new ArrayList<>(BATCH_SIZE);
|
|
|
- while (running || !qwSopLogsQueue.isEmpty()) {
|
|
|
- try {
|
|
|
- QwSopLogs log = qwSopLogsQueue.poll(1, TimeUnit.SECONDS);
|
|
|
- if (log != null) {
|
|
|
- batch.add(log);
|
|
|
- }
|
|
|
- if (batch.size() >= BATCH_SIZE || (!batch.isEmpty() && log == null)) {
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- batchInsertQwSopLogs(new ArrayList<>(batch));
|
|
|
- batch.clear();
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("QwSopLogs 消费线程被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理剩余的数据
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- batchInsertQwSopLogs(batch);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 消费 FsCourseWatchLog 队列并进行批量插入
|
|
|
- */
|
|
|
- private void consumeCourseLink() {
|
|
|
- List<FsCourseLink> batch = new ArrayList<>(BATCH_SIZE);
|
|
|
- while (running || !linkQueue.isEmpty()) {
|
|
|
- try {
|
|
|
- FsCourseLink courseLink = linkQueue.poll(1, TimeUnit.SECONDS);
|
|
|
- if (courseLink != null) {
|
|
|
- batch.add(courseLink);
|
|
|
- }
|
|
|
- if (batch.size() >= BATCH_SIZE || (!batch.isEmpty() && courseLink == null)) {
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- batchInsertFsCourseLink(new ArrayList<>(batch));
|
|
|
- batch.clear();
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("FsCourseLink 消费线程被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理剩余的数据
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- batchInsertFsCourseLink(batch);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 消费 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);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 消费 FsCourseWatchLog 队列并进行批量插入
|
|
|
- */
|
|
|
- private void consumeWatchLogs() {
|
|
|
- List<FsCourseWatchLog> batch = new ArrayList<>(BATCH_SIZE);
|
|
|
- while (running || !watchLogsQueue.isEmpty()) {
|
|
|
- try {
|
|
|
- FsCourseWatchLog watchLog = watchLogsQueue.poll(1, TimeUnit.SECONDS);
|
|
|
- if (watchLog != null) {
|
|
|
- batch.add(watchLog);
|
|
|
- }
|
|
|
- if (batch.size() >= BATCH_SIZE || (!batch.isEmpty() && watchLog == null)) {
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- batchInsertFsCourseWatchLogs(new ArrayList<>(batch));
|
|
|
- batch.clear();
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("FsCourseWatchLog 消费线程被中断: {}", e.getMessage(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理剩余的数据
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- batchInsertFsCourseWatchLogs(batch);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 批量插入 QwSopLogs
|
|
|
- */
|
|
|
- @Transactional
|
|
|
- @Retryable(
|
|
|
- value = { Exception.class },
|
|
|
- maxAttempts = 3,
|
|
|
- backoff = @Backoff(delay = 2000)
|
|
|
- )
|
|
|
- public void batchInsertQwSopLogs(List<QwSopLogs> logsToInsert) {
|
|
|
- try {
|
|
|
- qwSopLogsService.batchInsertQwSopLogs(logsToInsert);
|
|
|
- log.info("批量插入 QwSopLogs 完成,共插入 {} 条记录。", logsToInsert.size());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("批量插入 QwSopLogs 失败: {}", e.getMessage(), e);
|
|
|
- // 可选:将失败的数据记录到失败队列或持久化存储以便后续重试
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 批量插入 FsCourseWatchLog
|
|
|
- */
|
|
|
- @Transactional
|
|
|
- @Retryable(
|
|
|
- value = { Exception.class },
|
|
|
- maxAttempts = 3,
|
|
|
- backoff = @Backoff(delay = 2000)
|
|
|
- )
|
|
|
- public void batchInsertFsCourseWatchLogs(List<FsCourseWatchLog> watchLogsToInsert) {
|
|
|
- try {
|
|
|
- fsCourseWatchLogMapper.insertFsCourseWatchLogBatch(watchLogsToInsert);
|
|
|
- log.info("批量插入 FsCourseWatchLog 完成,共插入 {} 条记录。", watchLogsToInsert.size());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("批量插入 FsCourseWatchLog 失败: {}", e.getMessage(), e);
|
|
|
- // 可选:将失败的数据记录到失败队列或持久化存储以便后续重试
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 批量插入 FsCourseLink
|
|
|
- */
|
|
|
- @Transactional
|
|
|
- @Retryable(
|
|
|
- value = { Exception.class },
|
|
|
- maxAttempts = 3,
|
|
|
- backoff = @Backoff(delay = 2000)
|
|
|
- )
|
|
|
- public void batchInsertFsCourseLink(List<FsCourseLink> courseLinkToInsert) {
|
|
|
- try {
|
|
|
- fsCourseLinkMapper.insertFsCourseLinkBatch(courseLinkToInsert);
|
|
|
- log.info("批量插入 FsCourseLink 完成,共插入 {} 条记录。", courseLinkToInsert.size());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("批量插入 FsCourseLink 失败: {}", e.getMessage(), e);
|
|
|
- // 可选:将失败的数据记录到失败队列或持久化存储以便后续重试
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 批量插入 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();
|
|
|
- log.info("补发过期完课消息总条数:{}",sopLogs.size());
|
|
|
- processUpdateQwSopLogs(sopLogs);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // 定义一个方法来批量处理插入逻辑,支持每 500 条数据一次的批量插入
|
|
|
- private void processUpdateQwSopLogs(List<QwSopLogs> sopLogs) {
|
|
|
- // 定义批量插入的大小
|
|
|
- int batchSize = 500;
|
|
|
-
|
|
|
- // 循环处理外部用户 ID,每次处理批量大小的子集
|
|
|
- for (int i = 0; i < sopLogs.size(); i += batchSize) {
|
|
|
-
|
|
|
- int endIndex = Math.min(i + batchSize, sopLogs.size());
|
|
|
- List<QwSopLogs> batchList = sopLogs.subList(i, endIndex); // 获取当前批次的子集
|
|
|
-
|
|
|
- // 直接使用批次数据进行批量更新,不需要额外的 List
|
|
|
- try {
|
|
|
- qwSopLogsMapper.batchUpdateQwSopLogsByCancel(batchList);
|
|
|
- log.info("正在补发条数:{}",batchSize);
|
|
|
- } catch (Exception e) {
|
|
|
- // 记录异常日志,方便后续排查问题
|
|
|
- log.error("批量更新数据时发生异常,处理的批次起始索引为: " + i, e);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private FsCourseFinishTempMapper fsCourseFinishTempMapper;
|
|
|
- @Autowired
|
|
|
- private QwExternalContactMapper qwExternalContactMapper;
|
|
|
-
|
|
|
-
|
|
|
-// @Override
|
|
|
-// @Transactional
|
|
|
-// public void creatMessMessage(QwSopLogs logs) {
|
|
|
-// // qwSopLogsMapper.insertQwSopLogs(logs);
|
|
|
-// QwSopTempSetting.Content content = JSON.parseObject(logs.getContentJson(), QwSopTempSetting.Content.class);
|
|
|
-// handleNormalMessage(logs, content,null);
|
|
|
-// }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void createCourseFinishMsg() {
|
|
|
- long startTime = System.currentTimeMillis();
|
|
|
- log.info("创建完课消息 - 定时任务开始 {}", startTime);
|
|
|
-
|
|
|
- // 线程池配置
|
|
|
- int threadPoolSize = 4;
|
|
|
- ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
|
|
|
-
|
|
|
- // 用于收集所有处理结果的队列
|
|
|
- BlockingQueue<List<FsCourseWatchLog>> batchQueue = new LinkedBlockingQueue<>();
|
|
|
-
|
|
|
- try {
|
|
|
- // 查询当天日期范围
|
|
|
- LocalDate today = LocalDate.now();
|
|
|
- Date startDate = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
|
|
- Date endDate = Date.from(today.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
|
|
|
-
|
|
|
- // 启动生产者线程 - 流式分批查询数据
|
|
|
- executorService.submit(() -> {
|
|
|
- try {
|
|
|
- int batchSize = 1000;
|
|
|
- long maxId = 0;
|
|
|
- boolean hasMore = true;
|
|
|
-
|
|
|
- while (hasMore) {
|
|
|
- // 查询当前批次数据
|
|
|
- List<FsCourseWatchLog> batch = fsCourseWatchLogMapper.selectFsCourseWatchLogFinishBatchByDate(
|
|
|
- startDate, endDate, maxId, batchSize);
|
|
|
-
|
|
|
- if (!batch.isEmpty()) {
|
|
|
- // 将批次放入队列
|
|
|
- batchQueue.put(batch);
|
|
|
- // 更新maxId为当前批次的最后一个ID
|
|
|
- maxId = batch.get(batch.size() - 1).getLogId();
|
|
|
- log.debug("已生产批次数据,最后logId: {}, 数量: {}", maxId, batch.size());
|
|
|
- }
|
|
|
-
|
|
|
- if (batch.size() < batchSize) {
|
|
|
- hasMore = false;
|
|
|
- batchQueue.put(Collections.emptyList());// 结束标志
|
|
|
- log.info("数据生产完成,最后logId: {}", maxId);
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("生产数据时出错", e);
|
|
|
- try {
|
|
|
- batchQueue.put(Collections.emptyList()); // 确保消费者能退出
|
|
|
- } catch (InterruptedException ie) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 消费者线程处理数据
|
|
|
- List<Future<?>> futures = new ArrayList<>();
|
|
|
- for (int i = 0; i < threadPoolSize; i++) {
|
|
|
- futures.add(executorService.submit(() -> {
|
|
|
- try {
|
|
|
- while (true) {
|
|
|
- List<FsCourseWatchLog> batch = batchQueue.take();
|
|
|
-
|
|
|
- // 空列表表示处理结束
|
|
|
- if (batch.isEmpty()) {
|
|
|
- batchQueue.put(Collections.emptyList()); // 传递给其他消费者
|
|
|
- break;
|
|
|
- }
|
|
|
- log.info("开始处理批次数据");
|
|
|
- processBatch(batch); // 处理批次数据
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- log.error("处理数据时被中断", e);
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("处理数据时出错", e);
|
|
|
- }
|
|
|
- }));
|
|
|
- }
|
|
|
-
|
|
|
- // 等待所有任务完成
|
|
|
- for (Future<?> future : futures) {
|
|
|
- try {
|
|
|
- future.get();
|
|
|
- } catch (InterruptedException | ExecutionException e) {
|
|
|
- log.error("等待任务完成时出错", e);
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- log.info("所有批次处理完成,总耗时: {}ms", System.currentTimeMillis() - startTime);
|
|
|
-
|
|
|
- } finally {
|
|
|
- executorService.shutdown();
|
|
|
- try {
|
|
|
- if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
|
- executorService.shutdownNow();
|
|
|
- }
|
|
|
- } catch (InterruptedException e) {
|
|
|
- executorService.shutdownNow();
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理单个批次的方法
|
|
|
- private void processBatch(List<FsCourseWatchLog> batch) {
|
|
|
- List<FsCourseWatchLog> finishLogsToUpdate = new ArrayList<>();
|
|
|
- List<QwSopLogs> sopLogsToInsert = new ArrayList<>();
|
|
|
- log.info("开始执行处理批次方法-数量:{}",batch.size());
|
|
|
- for (FsCourseWatchLog finishLog : batch) {
|
|
|
- try {
|
|
|
-
|
|
|
- try {
|
|
|
-
|
|
|
- asyncCourseWatchFinishService.executeCourseWatchFinish(finishLog);
|
|
|
-
|
|
|
- }catch (Exception e){
|
|
|
- log.error("添加完课打备注失败",e);
|
|
|
- }
|
|
|
-
|
|
|
- // 查询外部联系人信息
|
|
|
- QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(finishLog.getQwExternalContactId());
|
|
|
- if (externalContact == null) {
|
|
|
- log.error("外部联系人不存在: {}", finishLog.getQwExternalContactId());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 查询完课模板信息
|
|
|
- FsCourseFinishTemp finishTemp = fsCourseFinishTempMapper.selectFsCourseFinishTempByCompanyId(finishLog.getCompanyUserId(),finishLog.getCompanyId(), finishLog.getVideoId());
|
|
|
-
|
|
|
- // 设置 finishLog 为已发送状态,并加入批量更新列表
|
|
|
- finishLog.setSendFinishMsg(1);
|
|
|
- finishLogsToUpdate.add(finishLog);
|
|
|
-
|
|
|
- if (finishTemp == null) {
|
|
|
-// log.error("完课模板不存在: " + finishLog.getQwUserId() + ", " + finishLog.getVideoId());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 构建 sopLogs 对象
|
|
|
- QwSopLogs sopLogs = buildSopLogs(finishLog, externalContact, finishTemp);
|
|
|
- if (sopLogs == null) {
|
|
|
- log.error("生成完课发送记录为空-:{}", finishLog.getQwExternalContactId());
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 如果客户状态有效,则加入批量插入列表
|
|
|
- if (isValidExternalContact(externalContact)) {
|
|
|
- sopLogsToInsert.add(sopLogs);
|
|
|
- } else {
|
|
|
- log.info("完课消息-客户信息有误,不生成完课消息: {}", finishLog.getQwExternalContactId());
|
|
|
- }
|
|
|
- try {
|
|
|
- fsUserCompanyBindService.finish(externalContact.getFsUserId(), externalContact.getQwUserId(), externalContact.getCompanyUserId(), finishLog);
|
|
|
- }catch (Exception e){
|
|
|
- log.error("更新重粉看课状态失败",e);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("处理完课记录失败: {}", finishLog.getLogId(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 批量更新和插入
|
|
|
- if (!finishLogsToUpdate.isEmpty()) {
|
|
|
- try {
|
|
|
- fsCourseWatchLogMapper.batchUpdateWatchLogSendMsg(finishLogsToUpdate);
|
|
|
- log.info("批量更新 finishLog 成功,数量: {}", finishLogsToUpdate.size());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("批量更新 finishLog 失败", e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!sopLogsToInsert.isEmpty()) {
|
|
|
- try {
|
|
|
- qwSopLogsService.batchInsertQwSopLogs(sopLogsToInsert);
|
|
|
- log.info("批量插入 sopLogs 成功,数量: {}", sopLogsToInsert.size());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("批量插入 sopLogs 失败", e);
|
|
|
- }
|
|
|
- }
|
|
|
- log.info("结束处理批次方法-数量:{}",batch.size());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 构建 QwSopLogs 对象
|
|
|
- */
|
|
|
- private QwSopLogs buildSopLogs(FsCourseWatchLog finishLog, QwExternalContact externalContact, FsCourseFinishTemp finishTemp) {
|
|
|
- QwSopCourseFinishTempSetting setting = new QwSopCourseFinishTempSetting();
|
|
|
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
- LocalDateTime currentTime = LocalDateTime.now();
|
|
|
- LocalDateTime newTime = currentTime.plusMinutes(3);
|
|
|
- String newTimeString = newTime.format(formatter);
|
|
|
-
|
|
|
- QwSopLogs sopLogs = new QwSopLogs();
|
|
|
- sopLogs.setSendTime(newTimeString);
|
|
|
- sopLogs.setQwUserid(externalContact.getUserId());
|
|
|
- sopLogs.setCorpId(externalContact.getCorpId());
|
|
|
- sopLogs.setLogType(2);
|
|
|
- sopLogs.setSendType(3);
|
|
|
- sopLogs.setSendStatus(3L);
|
|
|
- sopLogs.setReceivingStatus(0L);
|
|
|
- sopLogs.setSort(40000000);
|
|
|
- sopLogs.setCompanyId(finishLog.getCompanyId());
|
|
|
- sopLogs.setSopId(finishLog.getSopId());
|
|
|
- sopLogs.setExternalUserId(externalContact.getExternalUserId());
|
|
|
- sopLogs.setExternalUserName(externalContact.getName());
|
|
|
- sopLogs.setFsUserId(finishLog.getUserId() != null ? finishLog.getUserId() : null );
|
|
|
- sopLogs.setExternalId(finishLog.getQwExternalContactId());
|
|
|
- sopLogs.setUserLogsId("-");
|
|
|
-
|
|
|
- sopLogs.setQwUserKey(finishLog.getQwUserId() != null ? finishLog.getQwUserId() : null);
|
|
|
-
|
|
|
- // 解析模板设置
|
|
|
- List<QwSopCourseFinishTempSetting.Setting> settings = parseSettings(finishTemp.getSetting());
|
|
|
- if (settings == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- //完课后若是小程序发送另外一堂课
|
|
|
- saveWacthLogOfCourseLink(settings,sopLogs,newTimeString,finishLog,finishTemp);
|
|
|
- // 处理音频内容
|
|
|
- for (QwSopCourseFinishTempSetting.Setting st : settings) {
|
|
|
- if (st.getContentType().equals("7")) {
|
|
|
- Long companyUserId = finishLog.getCompanyUserId();
|
|
|
- QwSopTempVoice qwSopTempVoice = sopTempVoiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId, st.getValue());
|
|
|
- if (qwSopTempVoice != null && qwSopTempVoice.getVoiceUrl() != null && qwSopTempVoice.getRecordType() == 1) {
|
|
|
- st.setVoiceUrl(qwSopTempVoice.getVoiceUrl());
|
|
|
- st.setVoiceDuration(String.valueOf(qwSopTempVoice.getDuration()));
|
|
|
- } else if (qwSopTempVoice == null) {
|
|
|
- if(companyUserId != null && st.getValue() != null){
|
|
|
- qwSopTempVoice = new QwSopTempVoice();
|
|
|
- qwSopTempVoice.setCompanyUserId(companyUserId);
|
|
|
- qwSopTempVoice.setVoiceTxt(st.getValue());
|
|
|
- qwSopTempVoice.setRecordType(0);
|
|
|
- sopTempVoiceService.insertQwSopTempVoice(qwSopTempVoice);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-// for (QwSopCourseFinishTempSetting.Setting st : settings) {
|
|
|
-// if (st.getContentType().equals("7")) {
|
|
|
-// try {
|
|
|
-// AudioVO audioVO = AudioUtils.transferAudioSilkFromText(st.getValue(), finishLog.getCompanyUserId(), false);
|
|
|
-// st.setVoiceUrl(audioVO.getUrl());
|
|
|
-// st.setVoiceDuration(audioVO.getDuration() + "");
|
|
|
-// } catch (Exception e) {
|
|
|
-// log.error("音频生成失败: " + finishLog.getCompanyUserId(), e);
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
- setting.setSetting(settings);
|
|
|
- sopLogs.setContentJson(JSON.toJSONString(setting));
|
|
|
- return sopLogs;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判定小程序的话新增创建看课记录,以及fsCourseLink
|
|
|
- *
|
|
|
- * @param settings
|
|
|
- */
|
|
|
- public void saveWacthLogOfCourseLink(List<QwSopCourseFinishTempSetting.Setting> settings, QwSopLogs sopLogs, String newTimeString, FsCourseWatchLog finishLog, FsCourseFinishTemp finishTemp){
|
|
|
- String json = configService.selectConfigByKey("course.config");
|
|
|
- CourseConfig config = JSON.parseObject(json, CourseConfig.class);
|
|
|
- Date dataTime = new Date();
|
|
|
- List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
|
|
|
- Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
|
|
|
-
|
|
|
- QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(sopLogs.getCorpId());
|
|
|
- QwUser qwUser = qwExternalContactService.getQwUserByRedis(sopLogs.getCorpId(), sopLogs.getQwUserid());
|
|
|
- if (qwUser == null){
|
|
|
- return;
|
|
|
- }
|
|
|
- for (QwSopCourseFinishTempSetting.Setting st : settings) {
|
|
|
- switch (st.getContentType()) {
|
|
|
- //小程序单独
|
|
|
- case "4":
|
|
|
- addWatchLogIfNeeded(sopLogs.getSopId(), st.getVideoId().intValue(), st.getCourseId().intValue(), sopLogs.getFsUserId(), String.valueOf(qwUser.getId()),qwUser.getCompanyUserId().toString(), qwUser.getCompanyId().toString(),
|
|
|
- sopLogs.getExternalId(), newTimeString.substring(0, 10), dataTime);
|
|
|
-
|
|
|
- String linkByMiniApp = createLinkByMiniApp(st, sopLogs.getCorpId(), dataTime, finishTemp.getCourseId().intValue(), Integer.valueOf(st.getVideoId().toString()),
|
|
|
- String.valueOf(qwUser.getId()), qwUser.getCompanyUserId().toString(), qwUser.getCompanyId().toString(), sopLogs.getExternalId(), config);
|
|
|
-
|
|
|
-
|
|
|
- String miniAppId = null;
|
|
|
- if (!miniMap.isEmpty() && qwUser.getSendMsgType() == 1) {
|
|
|
- Map<Integer, List<CompanyMiniapp>> integerListMap = miniMap.get(Long.valueOf(qwUser.getCompanyId()));
|
|
|
- if (integerListMap != null) {
|
|
|
- int listIndex = 0;
|
|
|
- List<CompanyMiniapp> miniapps = integerListMap.get(listIndex);
|
|
|
-
|
|
|
- if (miniapps != null && !miniapps.isEmpty()) {
|
|
|
- CompanyMiniapp companyMiniapp = miniapps.get(0);
|
|
|
- if (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId())) {
|
|
|
- miniAppId = companyMiniapp.getAppId();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (StringUtil.strIsNullOrEmpty(miniAppId) && !StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())) {
|
|
|
- miniAppId = qwCompany.getMiniAppId();
|
|
|
- }
|
|
|
-
|
|
|
- if (!StringUtil.strIsNullOrEmpty(miniAppId)) {
|
|
|
- st.setMiniprogramAppid(miniAppId);
|
|
|
- } else {
|
|
|
- log.error("企业未配置小程序-" + sopLogs.getCorpId());
|
|
|
- }
|
|
|
-
|
|
|
- String miniprogramTitle = st.getMiniprogramTitle();
|
|
|
- int maxLength = 17;
|
|
|
- st.setMiniprogramTitle(miniprogramTitle.length() > maxLength ? miniprogramTitle.substring(0, maxLength)+"..." : miniprogramTitle);
|
|
|
- st.setMiniprogramPage(linkByMiniApp);
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- private Date processDate(String sendTimeParam) {
|
|
|
- // 1. 获取当前日期(年月日)
|
|
|
- LocalDate currentDate = LocalDate.now();
|
|
|
-
|
|
|
- // 2. 解析传入的时分(支持 "HH:mm" 或 "H:mm")
|
|
|
- LocalTime sendTime = LocalTime.parse(sendTimeParam);
|
|
|
-
|
|
|
- // 3. 合并为 LocalDateTime
|
|
|
- LocalDateTime dateTime = LocalDateTime.of(currentDate, sendTime);
|
|
|
-
|
|
|
- // 4. 转换为 Date(需通过 Instant 和系统默认时区)
|
|
|
- Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
-
|
|
|
- return date;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 新增courseLink
|
|
|
- *
|
|
|
- * @param setting
|
|
|
- * @param corpId
|
|
|
- * @param sendTime
|
|
|
- * @param courseId
|
|
|
- * @param videoId
|
|
|
- * @param qwUserId
|
|
|
- * @param companyUserId
|
|
|
- * @param companyId
|
|
|
- * @param externalId
|
|
|
- * @param config
|
|
|
- * @return
|
|
|
- */
|
|
|
- private String createLinkByMiniApp(QwSopCourseFinishTempSetting.Setting setting, String corpId, Date sendTime,
|
|
|
- Integer courseId, Integer videoId, String qwUserId,
|
|
|
- String companyUserId, String companyId, Long externalId, CourseConfig config) {
|
|
|
-
|
|
|
- FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, qwUserId,
|
|
|
- companyUserId, companyId, externalId, 3, null);
|
|
|
-
|
|
|
- FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
- BeanUtils.copyProperties(link, courseMap);
|
|
|
-
|
|
|
- String courseJson = JSON.toJSONString(courseMap);
|
|
|
- String realLinkFull = miniappRealLink + courseJson;
|
|
|
- link.setRealLink(realLinkFull);
|
|
|
-
|
|
|
- Date updateTime = createUpdateTime(setting, sendTime, config);
|
|
|
-
|
|
|
- link.setUpdateTime(updateTime);
|
|
|
- //存短链-
|
|
|
- fsCourseLinkMapper.insertFsCourseLink(link);
|
|
|
- return link.getRealLink();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 创建courselink
|
|
|
- * @param corpId
|
|
|
- * @param sendTime
|
|
|
- * @param courseId
|
|
|
- * @param videoId
|
|
|
- * @param qwUserId
|
|
|
- * @param companyUserId
|
|
|
- * @param companyId
|
|
|
- * @param externalId
|
|
|
- * @param type
|
|
|
- * @param chatId
|
|
|
- * @return
|
|
|
- */
|
|
|
- public FsCourseLink createFsCourseLink(String corpId, Date sendTime, Integer courseId, Integer videoId, String qwUserId,
|
|
|
- String companyUserId, String companyId, Long externalId, Integer type, String chatId) {
|
|
|
- // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
|
|
|
- FsCourseLink link = new FsCourseLink();
|
|
|
- link.setCompanyId(Long.parseLong(companyId));
|
|
|
- link.setQwUserId(Long.valueOf(qwUserId));
|
|
|
- link.setCompanyUserId(Long.parseLong(companyUserId));
|
|
|
- link.setVideoId(videoId.longValue());
|
|
|
- link.setCorpId(corpId);
|
|
|
- link.setCourseId(courseId.longValue());
|
|
|
- link.setChatId(chatId);
|
|
|
- link.setQwExternalId(externalId);
|
|
|
- link.setLinkType(type); //小程序
|
|
|
- link.setUNo(UUID.randomUUID().toString());
|
|
|
- link.setProjectCode(cloudHostProper.getProjectCode());
|
|
|
- String randomString = generateRandomStringWithLock();
|
|
|
- if (StringUtil.strIsNullOrEmpty(randomString)) {
|
|
|
- link.setLink(UUID.randomUUID().toString().replace("-", ""));
|
|
|
- } else {
|
|
|
- link.setLink(randomString);
|
|
|
- }
|
|
|
-
|
|
|
- link.setCreateTime(sendTime);
|
|
|
-
|
|
|
- return link;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 计算过期时间
|
|
|
- * @param setting
|
|
|
- * @param sendTime
|
|
|
- * @param config
|
|
|
- * @return
|
|
|
- */
|
|
|
- private Date createUpdateTime(QwSopCourseFinishTempSetting.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;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 增加看课记录
|
|
|
- *
|
|
|
- * @param sopId
|
|
|
- * @param videoId
|
|
|
- * @param courseId
|
|
|
- * @param fsUserId
|
|
|
- * @param qwUserId
|
|
|
- * @param companyUserId
|
|
|
- * @param companyId
|
|
|
- * @param externalId
|
|
|
- * @param startTime
|
|
|
- * @param createTime
|
|
|
- * @return
|
|
|
- */
|
|
|
- private Long addWatchLogIfNeeded(String sopId, Integer videoId, Integer courseId,
|
|
|
- Long fsUserId, String qwUserId, String companyUserId,
|
|
|
- String companyId, Long externalId, String startTime, Date createTime) {
|
|
|
-
|
|
|
- try {
|
|
|
- FsCourseWatchLog watchLog = new FsCourseWatchLog();
|
|
|
- watchLog.setVideoId(videoId != null ? videoId.longValue() : null);
|
|
|
- watchLog.setQwExternalContactId(externalId);
|
|
|
- watchLog.setSendType(2);
|
|
|
- watchLog.setQwUserId(Long.valueOf(qwUserId));
|
|
|
- watchLog.setSopId(sopId);
|
|
|
- watchLog.setDuration(0L);
|
|
|
- watchLog.setCourseId(courseId != null ? courseId.longValue() : null);
|
|
|
- watchLog.setCompanyUserId(companyUserId != null ? Long.valueOf(companyUserId) : null);
|
|
|
- watchLog.setCompanyId(companyId != null ? Long.valueOf(companyId) : null);
|
|
|
- watchLog.setCreateTime(createTime);
|
|
|
- watchLog.setUpdateTime(createTime);
|
|
|
- watchLog.setLogType(3);
|
|
|
- watchLog.setUserId(fsUserId);
|
|
|
- watchLog.setCampPeriodTime(convertStringToDate(startTime, "yyyy-MM-dd"));
|
|
|
-
|
|
|
- //存看课记录
|
|
|
- int i = fsCourseWatchLogMapper.insertOrUpdateFsCourseWatchLog(watchLog);
|
|
|
- return watchLog.getLogId();
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("插入观看记录失败:" + e.getMessage());
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * 解析模板设置
|
|
|
- */
|
|
|
- private List<QwSopCourseFinishTempSetting.Setting> parseSettings(String jsonData) {
|
|
|
- try {
|
|
|
- if (jsonData.startsWith("[") && jsonData.endsWith("]")) {
|
|
|
- return JSONArray.parseArray(jsonData, QwSopCourseFinishTempSetting.Setting.class);
|
|
|
- } else {
|
|
|
- String fixedJson = JSON.parseObject(jsonData, String.class);
|
|
|
- return JSONArray.parseArray(fixedJson, QwSopCourseFinishTempSetting.Setting.class);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("解析模板设置失败", e);
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查外部联系人状态是否有效
|
|
|
- */
|
|
|
- private boolean isValidExternalContact(QwExternalContact externalContact) {
|
|
|
- return externalContact.getStatus() == 0 || externalContact.getStatus() == 2 || externalContact.getStatus() == 3;
|
|
|
- }
|
|
|
-}
|