Explorar o código

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java
zyp hai 1 mes
pai
achega
0bfd9e9939
Modificáronse 52 ficheiros con 1120 adicións e 108 borrados
  1. 2 3
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  2. 5 0
      fs-company/src/main/java/com/fs/qw/QwGroupChatController.java
  3. 1 1
      fs-company/src/main/resources/application.yml
  4. 99 42
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  5. 1 0
      fs-service-system/src/main/java/com/fs/course/domain/FsCourseLink.java
  6. 112 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java
  7. 61 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  8. 4 0
      fs-service-system/src/main/java/com/fs/course/param/FsCourseLinkCreateParam.java
  9. 2 15
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsCourseSortLinkParam.java
  10. 23 0
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseVideoUParam.java
  11. 61 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
  12. 8 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  13. 1 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  14. 96 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  15. 8 8
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  16. 37 1
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  17. 6 0
      fs-service-system/src/main/java/com/fs/qw/domain/QwGroupChat.java
  18. 4 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java
  19. 1 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwGroupChatUserMapper.java
  20. 4 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwGroupChatService.java
  21. 2 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwGroupChatUserService.java
  22. 90 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/AsyncChatSopService.java
  23. 0 1
      fs-service-system/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java
  24. 11 4
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwGroupChatServiceImpl.java
  25. 6 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwGroupChatUserServiceImpl.java
  26. 93 0
      fs-service-system/src/main/java/com/fs/qw/vo/ChatSopRuleTimeVO.java
  27. 1 0
      fs-service-system/src/main/java/com/fs/qw/vo/QwSopRuleTimeVO.java
  28. 1 0
      fs-service-system/src/main/java/com/fs/qw/vo/QwSopTempSetting.java
  29. 1 0
      fs-service-system/src/main/java/com/fs/qw/vo/WxSopRuleTimeVO.java
  30. 25 14
      fs-service-system/src/main/java/com/fs/sop/domain/QwSop.java
  31. 2 0
      fs-service-system/src/main/java/com/fs/sop/domain/QwSopLogs.java
  32. 2 0
      fs-service-system/src/main/java/com/fs/sop/domain/QwSopTemp.java
  33. 1 0
      fs-service-system/src/main/java/com/fs/sop/domain/SopUserLogs.java
  34. 2 1
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  35. 5 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  36. 1 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java
  37. 4 0
      fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java
  38. 2 0
      fs-service-system/src/main/java/com/fs/sop/service/IQwSopTempRulesService.java
  39. 3 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  40. 32 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java
  41. 6 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempRulesServiceImpl.java
  42. 22 1
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  43. 1 0
      fs-service-system/src/main/java/com/fs/sop/vo/SopUserLogsVo.java
  44. 2 0
      fs-service-system/src/main/resources/mapper/course/FsCourseLinkMapper.xml
  45. 87 0
      fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  46. 3 0
      fs-service-system/src/main/resources/mapper/qw/QwGroupChatMapper.xml
  47. 3 0
      fs-service-system/src/main/resources/mapper/qw/QwGroupChatUserMapper.xml
  48. 15 1
      fs-service-system/src/main/resources/mapper/sop/QwSopLogsMapper.xml
  49. 26 0
      fs-service-system/src/main/resources/mapper/sop/QwSopMapper.xml
  50. 21 1
      fs-service-system/src/main/resources/mapper/sop/SopUserLogsMapper.xml
  51. 11 15
      fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java
  52. 103 0
      fs-user-app/src/main/java/com/fs/app/controller/FsUserCoursePeriodController.java

+ 2 - 3
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -100,11 +100,10 @@ public class FsUserCourseVideoController extends AppBaseController {
     }
     }
 
 
     @Login
     @Login
-    @GetMapping("/courseSortLink")
+    @PostMapping("/courseSortLink")
     @ApiOperation("生成课程分享短链")
     @ApiOperation("生成课程分享短链")
-    public R createCourseSortLink(FsCourseSortLinkParam param) {
+    public R createCourseSortLink(@RequestBody FsCourseSortLinkParam param) {
         FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
         FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
-        // todo 入参需要确认调整
         BeanUtils.copyProperties(param, fsCourseLinkCreateParam);
         BeanUtils.copyProperties(param, fsCourseLinkCreateParam);
 
 
         R courseSortLink = fsUserCourseService.createCourseSortLink(fsCourseLinkCreateParam);
         R courseSortLink = fsUserCourseService.createCourseSortLink(fsCourseLinkCreateParam);

+ 5 - 0
fs-company/src/main/java/com/fs/qw/QwGroupChatController.java

@@ -90,4 +90,9 @@ public class QwGroupChatController extends BaseController
         List<QwGroupChatOptionsVO> list = qwGroupChatService.selectGroupChatOptionsVOList(corpId);
         List<QwGroupChatOptionsVO> list = qwGroupChatService.selectGroupChatOptionsVOList(corpId);
         return AjaxResult.success(list);
         return AjaxResult.success(list);
     }
     }
+    @GetMapping("/listAll")
+    public AjaxResult listAll(String qwUserIds){
+        List<QwGroupChatOptionsVO> list = qwGroupChatService.listAllByQwUserList(qwUserIds);
+        return AjaxResult.success(list);
+    }
 }
 }

+ 1 - 1
fs-company/src/main/resources/application.yml

@@ -34,7 +34,7 @@ server:
 # 日志配置
 # 日志配置
 logging:
 logging:
   level:
   level:
-    com.fs: info
+    com.fs: debug
     org.springframework: warn
     org.springframework: warn
 # Spring配置
 # Spring配置
 spring:
 spring:

+ 99 - 42
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -2,24 +2,31 @@ package com.fs.app.taskService.impl;
 
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONArray;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.app.taskService.SopLogsTaskService;
 import com.fs.app.taskService.SopLogsTaskService;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.BatchUtils;
 import com.fs.common.utils.BatchUtils;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.date.DateUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.*;
 import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
 import com.fs.course.mapper.*;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.service.IFsCourseLinkService;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwGroupChat;
+import com.fs.qw.domain.QwGroupChatUser;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwGroupChatService;
+import com.fs.qw.service.IQwGroupChatUserService;
 import com.fs.qw.service.impl.QwExternalContactServiceImpl;
 import com.fs.qw.service.impl.QwExternalContactServiceImpl;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qw.vo.QwSopRuleTimeVO;
 import com.fs.qw.vo.QwSopRuleTimeVO;
@@ -55,6 +62,7 @@ import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
 import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
@@ -92,9 +100,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private QwSopTagMapper qwSopTagMapper ;
     private QwSopTagMapper qwSopTagMapper ;
     @Autowired
     @Autowired
     private QwSopMapper sopMapper;
     private QwSopMapper sopMapper;
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
 
 
     @Autowired
     @Autowired
     private IQwExternalContactService iQwExternalContactService;
     private IQwExternalContactService iQwExternalContactService;
+    @Autowired
+    private IQwGroupChatService qwGroupChatService;
+    @Autowired
+    private IQwGroupChatUserService qwGroupChatUserService;
 
 
     @Autowired
     @Autowired
     private QwExternalContactServiceImpl qwExternalContactService;
     private QwExternalContactServiceImpl qwExternalContactService;
@@ -263,7 +277,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             log.info("没有需要处理的 SOP 用户日志。");
             log.info("没有需要处理的 SOP 用户日志。");
             return;
             return;
         }
         }
-
+        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);
+            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);
+        }
         // 查询销售二级域名
         // 查询销售二级域名
         Set<Long> ids = sopUserLogsVos.stream().map(s -> {
         Set<Long> ids = sopUserLogsVos.stream().map(s -> {
             String[] userKey = s.getUserId().split("\\|");
             String[] userKey = s.getUserId().split("\\|");
@@ -308,7 +330,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
             String sopId = entry.getKey();
             String sopId = entry.getKey();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
-            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime);
+            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap);
         }
         }
 
 
         // 等待所有 SOP 分组处理完成
         // 等待所有 SOP 分组处理完成
@@ -328,9 +350,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             maxAttempts = 3,
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
             backoff = @Backoff(delay = 2000)
     )
     )
-    public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch ,LocalDateTime currentTime) {
+    public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch , LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) {
         try {
         try {
-            processSopGroup(sopId, userLogsVos,currentTime);
+            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap);
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
         } finally {
         } finally {
@@ -339,7 +361,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
     }
 
 
 
 
-    private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos,LocalDateTime currentTime) throws Exception {
+    private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) throws Exception {
         QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
         QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
 
 
         if (ruleTimeVO == null) {
         if (ruleTimeVO == null) {
@@ -373,7 +395,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
         CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
         CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
         for (SopUserLogsVo logVo : userLogsVos) {
         for (SopUserLogsVo logVo : userLogsVos) {
-            processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime);
+            processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime, groupChatMap);
         }
         }
 
 
         // 等待所有用户日志处理完成
         // 等待所有用户日志处理完成
@@ -392,9 +414,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             maxAttempts = 3,
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
             backoff = @Backoff(delay = 2000)
     )
     )
-    public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings, CountDownLatch latch, LocalDateTime currentTime) {
+    public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings, CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) {
         try {
         try {
-            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime);
+            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap);
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
         } finally {
         } finally {
@@ -403,7 +425,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
     }
 
 
 
 
-    private void processUserLog(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings, LocalDateTime currentTime) {
+    private void processUserLog(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) {
         try {
         try {
             LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
             LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
             LocalDate currentDate = currentTime.toLocalDate();
             LocalDate currentDate = currentTime.toLocalDate();
@@ -429,11 +451,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 day++;
                 day++;
             }
             }
             List<QwSopTempSetting.Content> contents = getDay(tempSettings, day);
             List<QwSopTempSetting.Content> contents = getDay(tempSettings, day);
+
             if (contents == null || contents.isEmpty()) {
             if (contents == null || contents.isEmpty()) {
                 log.error("SOP ID {} 的 TempSetting 内容为空,跳过处理。", logVo.getSopId());
                 log.error("SOP ID {} 的 TempSetting 内容为空,跳过处理。", logVo.getSopId());
                 return;
                 return;
             }
             }
-
             String[] userKey = logVo.getUserId().split("\\|");
             String[] userKey = logVo.getUserId().split("\\|");
             if (userKey.length < 3) {
             if (userKey.length < 3) {
                 log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId());
                 log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId());
@@ -487,7 +509,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
             for (QwSopTempSetting.Content content : contents) {
             for (QwSopTempSetting.Content content : contents) {
                 try {
                 try {
-
                     LocalTime elementLocalTime = LocalTime.parse(content.getTime());
                     LocalTime elementLocalTime = LocalTime.parse(content.getTime());
                     LocalDateTime elementDateTime = LocalDateTime.of(currentTime.toLocalDate(), elementLocalTime);
                     LocalDateTime elementDateTime = LocalDateTime.of(currentTime.toLocalDate(), elementLocalTime);
 
 
@@ -508,7 +529,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
                         // 如果时间差在目标范围内,更新记录
                         // 如果时间差在目标范围内,更新记录
                         // 组合年月日和element的时间
                         // 组合年月日和element的时间
-                        LocalDate targetDate = startDate.plusDays(intervalDay * tempGap);
+                        LocalDate targetDate = startDate.plusDays((long) intervalDay * tempGap);
 
 
                         // 将 targetDate 和 elementTime 组合成 LocalDateTime
                         // 将 targetDate 和 elementTime 组合成 LocalDateTime
                         LocalDateTime dateTime = LocalDateTime.of(targetDate, elementLocalTime);
                         LocalDateTime dateTime = LocalDateTime.of(targetDate, elementLocalTime);
@@ -548,7 +569,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 //                        }
 //                        }
 
 
 //                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content);
 //                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content);
-                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId, companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName());
+                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId, companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName(), groupChatMap);
 
 
 
 
                     }
                     }
@@ -565,6 +586,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
     private List<QwSopTempSetting.Content> getDay(List<QwSopTempRules> tempSettings, long days){
     private List<QwSopTempSetting.Content> getDay(List<QwSopTempRules> tempSettings, long days){
         List<QwSopTempRules> collect = tempSettings.stream().filter(e -> e.getDayNum() == days).collect(Collectors.toList());
         List<QwSopTempRules> collect = tempSettings.stream().filter(e -> e.getDayNum() == days).collect(Collectors.toList());
+        AtomicInteger i = new AtomicInteger();
         return collect.stream().map(e -> {
         return collect.stream().map(e -> {
             QwSopTempSetting.Content content = new QwSopTempSetting.Content();
             QwSopTempSetting.Content content = new QwSopTempSetting.Content();
             content.setId(e.getId());
             content.setId(e.getId());
@@ -583,13 +605,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             content.setCourseType(e.getCourseType());
             content.setCourseType(e.getCourseType());
             content.setAiTouch(e.getAiTouch());
             content.setAiTouch(e.getAiTouch());
             return content;
             return content;
-        }).collect(Collectors.toList());
+        }).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,
     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) {
+                                   QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content,
+                                   String qwUserId, String companyUserId, String companyId, String welcomeText, String qwUserName, Map<String, QwGroupChat> groupChatMap) {
 
 
         String formattedSendTime = sendTime.toInstant()
         String formattedSendTime = sendTime.toInstant()
                 .atZone(ZoneId.systemDefault())
                 .atZone(ZoneId.systemDefault())
@@ -639,20 +661,37 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             sopAddTag(logVo,content,sendTime);
             sopAddTag(logVo,content,sendTime);
         }
         }
 
 
-
-        // 处理每个 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);
+        if(StringUtils.isNotEmpty(logVo.getChatId())){
+            QwGroupChat groupChat = groupChatMap.get(logVo.getChatId());
+            ruleTimeVO.setSendType(6);
+            ruleTimeVO.setType(2);
+            if(content.getIndex() == 0){
+                QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null);
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
-                        type, qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId);
-            } catch (Exception e) {
-                log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
+                        type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText,qwUserName, null, true);
+            }else{
+                groupChat.getChatUserList().forEach(user -> {
+                    QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, user.getUserId(), user.getName(), null);
+                    handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
+                            type, qwUserId, companyUserId, companyId, user.getUserId(), welcomeText,qwUserName, null, false);
+                });
             }
             }
-        });
+        }else{
+            // 处理每个 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);
+                    handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
+                            type, qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId, false);
+                } catch (Exception e) {
+                    log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
+                }
+            });
+        }
+
     }
     }
 
 
     private void sopAddTag(SopUserLogsVo logVo, QwSopTempSetting.Content content, Date sendTime) {
     private void sopAddTag(SopUserLogsVo logVo, QwSopTempSetting.Content content, Date sendTime) {
@@ -700,15 +739,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content,
     private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content,
                                       SopUserLogsVo logVo, Date sendTime, Long courseId,
                                       SopUserLogsVo logVo, Date sendTime, Long courseId,
                                       Long videoId, int type, String qwUserId,
                                       Long videoId, int type, String qwUserId,
-                                      String companyUserId, String companyId, String externalId,String welcomeText,
-                                      String qwUserName,Long fsUserId) {
+                                      String companyUserId, String companyId, String externalId, String welcomeText,
+                                      String qwUserName, Long fsUserId, boolean isGroupChat) {
         switch (type) {
         switch (type) {
             case 1:
             case 1:
                 handleNormalMessage(sopLogs, content,companyUserId);
                 handleNormalMessage(sopLogs, content,companyUserId);
                 break;
                 break;
             case 2:
             case 2:
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
-                        qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId);
+                        qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId, isGroupChat);
                 break;
                 break;
             case 3:
             case 3:
                 handleOrderMessage(sopLogs, content);
                 handleOrderMessage(sopLogs, content);
@@ -740,8 +779,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
     private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
                                      SopUserLogsVo logVo, Date sendTime, Long courseId,
                                      SopUserLogsVo logVo, Date sendTime, Long courseId,
                                      Long videoId, String qwUserId, String companyUserId,
                                      Long videoId, String qwUserId, String companyUserId,
-                                     String companyId, String externalId,String welcomeText,
-                                     String qwUserName,Long fsUserId) {
+                                     String companyId, String externalId, String welcomeText,
+                                     String qwUserName, Long fsUserId, boolean isGroupChat) {
         // 深拷贝 Content 对象,避免使用 JSON
         // 深拷贝 Content 对象,避免使用 JSON
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         if (clonedContent == null) {
         if (clonedContent == null) {
@@ -766,22 +805,40 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 case "1":
                 case "1":
                 case "3":
                 case "3":
                     if ("1".equals(setting.getIsBindUrl())) {
                     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(qwUserId);
+                            createParam.setDays(setting.getExpiresDays());
+                            R createLink = courseLinkService.createRoomLinkUrl(createParam);
+                            if (createLink.get("code").equals(500)){
+                                throw new BaseException("链接生成失败!");
+                            }
+                            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,fsUserId);
+                        }
 
 
-                        addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
-                        String sortLink = generateShortLink(setting, logVo, sendTime, courseId, videoId,
-                                qwUserId, companyUserId, companyId, externalId,fsUserId);
-                        if (StringUtils.isNotEmpty(sortLink)) {
+                        if (StringUtils.isNotEmpty(link)) {
                             if ("3".equals(setting.getContentType())) {
                             if ("3".equals(setting.getContentType())) {
-                                setting.setLinkUrl(sortLink);
+                                setting.setLinkUrl(link);
                             } else {
                             } else {
                                 String currentValue = setting.getValue();
                                 String currentValue = setting.getValue();
                                 if (currentValue == null) {
                                 if (currentValue == null) {
-                                    setting.setValue(sortLink);
+                                    setting.setValue(link);
                                 } else {
                                 } else {
 //                                    setting.setValue(currentValue + "\n" + sortLink);
 //                                    setting.setValue(currentValue + "\n" + sortLink);
                                     setting.setValue(currentValue
                                     setting.setValue(currentValue
                                             .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)
                                             .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)
-                                            + "\n" + sortLink);
+                                            + "\n" + link);
                                 }
                                 }
                             }
                             }
                         } else {
                         } else {

+ 1 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsCourseLink.java

@@ -57,5 +57,6 @@ public class FsCourseLink extends BaseEntity
 //    private String link_uuid;
 //    private String link_uuid;
 
 
     private Integer isRoom;//是否发群
     private Integer isRoom;//是否发群
+    private String chatId;//是否发群
 
 
 }
 }

+ 112 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java

@@ -0,0 +1,112 @@
+package com.fs.course.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 会员营期对象 fs_user_course_period
+ *
+ * @author fs
+ * @date 2025-04-11
+ */
+public class FsUserCoursePeriod extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 营期id */
+    private Long periodId;
+
+    /** 营期名称 */
+    @Excel(name = "营期名称")
+    private String periodName;
+
+    /** 公司id */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    /** 课程id */
+    @Excel(name = "课程id")
+    private Long courseId;
+
+    /** 视频id */
+    @Excel(name = "视频id")
+    private Long videoId;
+
+    /** 开课时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "开课时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date startingTime;
+
+    public void setPeriodId(Long periodId)
+    {
+        this.periodId = periodId;
+    }
+
+    public Long getPeriodId()
+    {
+        return periodId;
+    }
+    public void setPeriodName(String periodName)
+    {
+        this.periodName = periodName;
+    }
+
+    public String getPeriodName()
+    {
+        return periodName;
+    }
+    public void setCompanyId(Long companyId)
+    {
+        this.companyId = companyId;
+    }
+
+    public Long getCompanyId()
+    {
+        return companyId;
+    }
+    public void setCourseId(Long courseId)
+    {
+        this.courseId = courseId;
+    }
+
+    public Long getCourseId()
+    {
+        return courseId;
+    }
+    public void setVideoId(Long videoId)
+    {
+        this.videoId = videoId;
+    }
+
+    public Long getVideoId()
+    {
+        return videoId;
+    }
+    public void setStartingTime(Date startingTime)
+    {
+        this.startingTime = startingTime;
+    }
+
+    public Date getStartingTime()
+    {
+        return startingTime;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("periodId", getPeriodId())
+            .append("periodName", getPeriodName())
+            .append("companyId", getCompanyId())
+            .append("courseId", getCourseId())
+            .append("videoId", getVideoId())
+            .append("startingTime", getStartingTime())
+            .append("createTime", getCreateTime())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}

+ 61 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java

@@ -0,0 +1,61 @@
+package com.fs.course.mapper;
+
+import java.util.List;
+import com.fs.course.domain.FsUserCoursePeriod;
+
+/**
+ * 会员营期Mapper接口
+ * 
+ * @author fs
+ * @date 2025-04-11
+ */
+public interface FsUserCoursePeriodMapper 
+{
+    /**
+     * 查询会员营期
+     * 
+     * @param periodId 会员营期ID
+     * @return 会员营期
+     */
+    public FsUserCoursePeriod selectFsUserCoursePeriodById(Long periodId);
+
+    /**
+     * 查询会员营期列表
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 会员营期集合
+     */
+    public List<FsUserCoursePeriod> selectFsUserCoursePeriodList(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 新增会员营期
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 结果
+     */
+    public int insertFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 修改会员营期
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 结果
+     */
+    public int updateFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 删除会员营期
+     * 
+     * @param periodId 会员营期ID
+     * @return 结果
+     */
+    public int deleteFsUserCoursePeriodById(Long periodId);
+
+    /**
+     * 批量删除会员营期
+     * 
+     * @param periodIds 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteFsUserCoursePeriodByIds(Long[] periodIds);
+}

+ 4 - 0
fs-service-system/src/main/java/com/fs/course/param/FsCourseLinkCreateParam.java

@@ -1,5 +1,6 @@
 package com.fs.course.param;
 package com.fs.course.param;
 
 
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.Data;
 
 
 import java.util.Date;
 import java.util.Date;
@@ -26,6 +27,9 @@ public class FsCourseLinkCreateParam {
     private Integer sendType;
     private Integer sendType;
 
 
     private Date sendTime;
     private Date sendTime;
+    private String chatId;
 
 
+    @ApiModelProperty(value = "链接有效时长(分钟)")
+    private Integer effectiveDuration;
 
 
 }
 }

+ 2 - 15
fs-service-system/src/main/java/com/fs/course/param/newfs/FsCourseSortLinkParam.java

@@ -11,10 +11,6 @@ public class FsCourseSortLinkParam {
     @ApiModelProperty(value = "视频id")
     @ApiModelProperty(value = "视频id")
     private Long videoId;
     private Long videoId;
 
 
-    private String qwUserId;
-
-    private String corpId;
-
     @ApiModelProperty(value = "公司id")
     @ApiModelProperty(value = "公司id")
     private Long companyId;
     private Long companyId;
 
 
@@ -24,16 +20,7 @@ public class FsCourseSortLinkParam {
     @ApiModelProperty(value = "课程id")
     @ApiModelProperty(value = "课程id")
     private Long courseId;
     private Long courseId;
 
 
-    @ApiModelProperty(value = "标题")
-    private String title;
-
-    @ApiModelProperty(value = "封面")
-    private String imageUrl;
-
-    @ApiModelProperty(value = "描述")
-    private String desc;
-
-//    @ApiModelProperty(value = "跳转的链接地址,格式举例:/course/pages/xxx?course=")
-//    private String realLink;
+    @ApiModelProperty(value = "链接有效时长")
+    private Integer effectiveDuration;
 
 
 }
 }

+ 23 - 0
fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseVideoUParam.java

@@ -0,0 +1,23 @@
+package com.fs.course.param.newfs;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Data
+@ApiModel(value = "更新看课时长的入参")
+public class FsUserCourseVideoUParam implements Serializable {
+    @NotNull(message = "时长不能为空")
+    private Long duration;
+
+    @NotNull(message = "视频id不能为空")
+    private Long videoId;
+
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+
+    private Integer linkType;
+
+}

+ 61 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java

@@ -0,0 +1,61 @@
+package com.fs.course.service;
+
+import java.util.List;
+import com.fs.course.domain.FsUserCoursePeriod;
+
+/**
+ * 会员营期Service接口
+ * 
+ * @author fs
+ * @date 2025-04-11
+ */
+public interface IFsUserCoursePeriodService 
+{
+    /**
+     * 查询会员营期
+     * 
+     * @param periodId 会员营期ID
+     * @return 会员营期
+     */
+    public FsUserCoursePeriod selectFsUserCoursePeriodById(Long periodId);
+
+    /**
+     * 查询会员营期列表
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 会员营期集合
+     */
+    public List<FsUserCoursePeriod> selectFsUserCoursePeriodList(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 新增会员营期
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 结果
+     */
+    public int insertFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 修改会员营期
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 结果
+     */
+    public int updateFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 批量删除会员营期
+     * 
+     * @param periodIds 需要删除的会员营期ID
+     * @return 结果
+     */
+    public int deleteFsUserCoursePeriodByIds(Long[] periodIds);
+
+    /**
+     * 删除会员营期信息
+     * 
+     * @param periodId 会员营期ID
+     * @return 结果
+     */
+    public int deleteFsUserCoursePeriodById(Long periodId);
+}

+ 8 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -6,6 +6,7 @@ import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.param.*;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
+import com.fs.course.param.newfs.FsUserCourseVideoUParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
@@ -136,4 +137,11 @@ public interface IFsUserCourseVideoService
 
 
     R addWatchLogByLink(FsUserCourseAddCompanyUserParam param);
     R addWatchLogByLink(FsUserCourseAddCompanyUserParam param);
 
 
+    /**
+     * 更新看课时长
+     * @param param 入参
+     * @return
+     */
+    R updateWatchDurationWx(FsUserCourseVideoUParam param);
+
 }
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -211,6 +211,7 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         link.setRealLink(realLink+course);
         link.setRealLink(realLink+course);
         String randomString = generateRandomString();
         String randomString = generateRandomString();
         link.setLink(randomString);
         link.setLink(randomString);
+        link.setChatId(param.getChatId());
         link.setCreateTime(new Date());
         link.setCreateTime(new Date());
         Integer expireDays = 0;
         Integer expireDays = 0;
         if (param.getDays() == null || param.getDays() == 0){
         if (param.getDays() == null || param.getDays() == 0){

+ 96 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -0,0 +1,96 @@
+package com.fs.course.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.course.mapper.FsUserCoursePeriodMapper;
+import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.service.IFsUserCoursePeriodService;
+
+/**
+ * 会员营期Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-04-11
+ */
+@Service
+public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService 
+{
+    @Autowired
+    private FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
+
+    /**
+     * 查询会员营期
+     * 
+     * @param periodId 会员营期ID
+     * @return 会员营期
+     */
+    @Override
+    public FsUserCoursePeriod selectFsUserCoursePeriodById(Long periodId)
+    {
+        return fsUserCoursePeriodMapper.selectFsUserCoursePeriodById(periodId);
+    }
+
+    /**
+     * 查询会员营期列表
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 会员营期
+     */
+    @Override
+    public List<FsUserCoursePeriod> selectFsUserCoursePeriodList(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        return fsUserCoursePeriodMapper.selectFsUserCoursePeriodList(fsUserCoursePeriod);
+    }
+
+    /**
+     * 新增会员营期
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        fsUserCoursePeriod.setCreateTime(DateUtils.getNowDate());
+        return fsUserCoursePeriodMapper.insertFsUserCoursePeriod(fsUserCoursePeriod);
+    }
+
+    /**
+     * 修改会员营期
+     * 
+     * @param fsUserCoursePeriod 会员营期
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        fsUserCoursePeriod.setUpdateTime(DateUtils.getNowDate());
+        return fsUserCoursePeriodMapper.updateFsUserCoursePeriod(fsUserCoursePeriod);
+    }
+
+    /**
+     * 批量删除会员营期
+     * 
+     * @param periodIds 需要删除的会员营期ID
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserCoursePeriodByIds(Long[] periodIds)
+    {
+        return fsUserCoursePeriodMapper.deleteFsUserCoursePeriodByIds(periodIds);
+    }
+
+    /**
+     * 删除会员营期信息
+     * 
+     * @param periodId 会员营期ID
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserCoursePeriodById(Long periodId)
+    {
+        return fsUserCoursePeriodMapper.deleteFsUserCoursePeriodById(periodId);
+    }
+}

+ 8 - 8
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -58,8 +58,8 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
     @Autowired
     @Autowired
     private CompanyUserMapper companyUserMapper;
     private CompanyUserMapper companyUserMapper;
 
 
-    // todo 链接url需要调整
-    private static final String realLink = "/courseh5/pages/course/learning?course=";
+    private static final String realLink = "/courseH5/pages/course/learning?course=";
+    private static final String shortLink = "/courseH5/pages/course/learning?s=";
 
 
     /**
     /**
      * 查询课程
      * 查询课程
@@ -423,23 +423,23 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         int i = fsCourseLinkMapper.insertFsCourseLink(link);
         int i = fsCourseLinkMapper.insertFsCourseLink(link);
         if (i > 0){
         if (i > 0){
             String domainName = getDomainName(param.getCompanyUserId(), config);
             String domainName = getDomainName(param.getCompanyUserId(), config);
-            String sortLink = domainName + "/s/" + link.getLink();
+            String sortLink = domainName + shortLink + link.getLink();
             return R.ok().put("url", sortLink);
             return R.ok().put("url", sortLink);
         }
         }
         return R.error("生成链接失败!");
         return R.error("生成链接失败!");
     }
     }
 
 
     private static Calendar getExpireDay(FsCourseLinkCreateParam param, CourseConfig config, Date createTime) {
     private static Calendar getExpireDay(FsCourseLinkCreateParam param, CourseConfig config, Date createTime) {
-        Integer expireDays;
-        if (param.getDays() == null || param.getDays() == 0){
-            expireDays = config.getVideoLinkExpireDate();
+        Integer expireDuration;
+        if (param.getEffectiveDuration() == null || param.getEffectiveDuration() == 0){
+            expireDuration = config.getVideoLinkExpireDate();
         }else {
         }else {
-            expireDays = param.getDays();
+            expireDuration = param.getEffectiveDuration();
         }
         }
         // 设置过期时间
         // 设置过期时间
         Calendar calendar = Calendar.getInstance();
         Calendar calendar = Calendar.getInstance();
         calendar.setTime(createTime);
         calendar.setTime(createTime);
-        calendar.add(Calendar.DAY_OF_MONTH, expireDays);
+        calendar.add(Calendar.MINUTE, expireDuration);
         return calendar;
         return calendar;
     }
     }
 
 

+ 37 - 1
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -19,6 +19,7 @@ import com.fs.course.mapper.*;
 import com.fs.course.param.*;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
+import com.fs.course.param.newfs.FsUserCourseVideoUParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
@@ -945,7 +946,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             ResponseResult.ok(vo);
             ResponseResult.ok(vo);
         }
         }
         // 从Redis中获取用户目前的观看时长
         // 从Redis中获取用户目前的观看时长
-        //todo 需要在修改看课记录中设置此key
         String redisKey = "h5wxuser:watch:duration:" + param.getFsUserId() + ":" + param.getVideoId();
         String redisKey = "h5wxuser:watch:duration:" + param.getFsUserId() + ":" + param.getVideoId();
         String durationCurrent = redisCache.getCacheObject(redisKey);
         String durationCurrent = redisCache.getCacheObject(redisKey);
 
 
@@ -981,4 +981,40 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
 
         return null;
         return null;
     }
     }
+
+    @Override
+    public R updateWatchDurationWx(FsUserCourseVideoUParam param) {
+        //临时短链不做记录
+        if (param.getLinkType() != null && param.getLinkType() == 1){
+            return R.ok();
+        }
+
+        // 从Redis中获取观看时长
+        String redisKey = "h5wxuser:watch:duration:" + param.getUserId() + ":" + param.getVideoId();
+        try {
+            String durationStr = redisCache.getCacheObject(redisKey);
+            long duration = durationStr != null ? Long.parseLong(durationStr) : 0L;
+
+            // 更新Redis中的观看时长
+            if (param.getDuration() != null && param.getDuration() > duration) {
+                //24小时过期
+                redisCache.setCacheObject(redisKey, param.getDuration().toString(),2,TimeUnit.HOURS);
+            }
+
+            //更新缓存中的心跳时间
+            updateHeartbeatWx(param);
+            return R.ok();
+        } catch (Exception e) {
+            logger.error("更新看课时长失败:{}", redisKey, e.getMessage());
+            return R.error("更新看课时长失败");
+        }
+    }
+
+    //会员-更新心跳时间
+    public void updateHeartbeatWx(FsUserCourseVideoUParam param) {
+        String redisKey = "h5wxuser:watch:heartbeat:" + param.getUserId() + ":" + param.getVideoId();
+        redisCache.setCacheObject(redisKey, LocalDateTime.now().toString());
+        // 设置 Redis 记录的过期时间(例如 5 分钟)
+        redisCache.expire(redisKey, 300, TimeUnit.SECONDS);
+    }
 }
 }

+ 6 - 0
fs-service-system/src/main/java/com/fs/qw/domain/QwGroupChat.java

@@ -1,9 +1,12 @@
 package com.fs.qw.domain;
 package com.fs.qw.domain;
 
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fs.common.annotation.Excel;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 import lombok.Data;
 
 
+import java.util.List;
+
 /**
 /**
  * 客户群详情对象 qw_group_chat
  * 客户群详情对象 qw_group_chat
  *
  *
@@ -71,4 +74,7 @@ public class QwGroupChat extends BaseEntity
     @Excel(name = "累计退群人数")
     @Excel(name = "累计退群人数")
     private Long allOutGroup;
     private Long allOutGroup;
 
 
+    @TableField(exist = false)
+    private List<QwGroupChatUser> chatUserList;
+
 }
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java

@@ -110,4 +110,8 @@ public interface QwGroupChatMapper
     @Select("select chat_id,name from qw_group_chat where  corp_id=#{corpId}")
     @Select("select chat_id,name from qw_group_chat where  corp_id=#{corpId}")
     List<QwGroupChatOptionsVO> selectGroupChatOptionsVOList(String corpId);
     List<QwGroupChatOptionsVO> selectGroupChatOptionsVOList(String corpId);
 
 
+    @Select("select chat_id,name from qw_group_chat where  find_in_set(owner,#{qwUserIds})")
+    List<QwGroupChatOptionsVO> listAllByQwUserList(String qwUserIds);
+
+    List<QwGroupChat> selectQwGroupChatByChatIds(@Param("ids") String[] ids);
 }
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwGroupChatUserMapper.java

@@ -139,4 +139,5 @@ public interface QwGroupChatUserMapper
     @Delete("delete from qw_group_chat_user where chat_id=#{chatId} and corp_id=#{corpId}")
     @Delete("delete from qw_group_chat_user where chat_id=#{chatId} and corp_id=#{corpId}")
     public int deleteQwGroupChatUserByIdAndCompanyId(@Param("chatId") String chatId,@Param("corpId") String corpId);
     public int deleteQwGroupChatUserByIdAndCompanyId(@Param("chatId") String chatId,@Param("corpId") String corpId);
 
 
+    List<QwGroupChatUser> selectQwGroupChatUserByChatIds(@Param("ids") String[] ids);
 }
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/qw/service/IQwGroupChatService.java

@@ -62,4 +62,8 @@ public interface IQwGroupChatService
     public int deleteQwGroupChatByChatIdAndCompanyId(String chatId,String corpId);
     public int deleteQwGroupChatByChatIdAndCompanyId(String chatId,String corpId);
 
 
     List<QwGroupChatOptionsVO> selectGroupChatOptionsVOList(String corpId);
     List<QwGroupChatOptionsVO> selectGroupChatOptionsVOList(String corpId);
+
+    List<QwGroupChatOptionsVO> listAllByQwUserList(String qwUserIds);
+
+    List<QwGroupChat> selectQwGroupChatByChatIds(String[] ids);
 }
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/qw/service/IQwGroupChatUserService.java

@@ -46,4 +46,6 @@ public interface IQwGroupChatUserService
     /** 根据成员id和公司corpId等查询群成员信息 */
     /** 根据成员id和公司corpId等查询群成员信息 */
 
 
     public QwGroupChatUser selectQwGroupChatUserByExternalUserId(QwGroupChatUser qwGroupChatUser);
     public QwGroupChatUser selectQwGroupChatUserByExternalUserId(QwGroupChatUser qwGroupChatUser);
+
+    List<QwGroupChatUser> selectQwGroupChatUserByChatIds(String[] array);
 }
 }

+ 90 - 0
fs-service-system/src/main/java/com/fs/qw/service/impl/AsyncChatSopService.java

@@ -0,0 +1,90 @@
+package com.fs.qw.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fs.common.utils.PubFun;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.date.DateUtil;
+import com.fs.qw.domain.QwGroupChat;
+import com.fs.qw.service.IQwGroupChatService;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.ChatSopRuleTimeVO;
+import com.fs.qw.vo.QwUserVO;
+import com.fs.sop.domain.QwSop;
+import com.fs.sop.domain.QwSopTemp;
+import com.fs.sop.domain.SopUserLogs;
+import com.fs.sop.mapper.QwSopMapper;
+import com.fs.sop.mapper.QwSopTempMapper;
+import com.fs.sop.mapper.SopUserLogsMapper;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class AsyncChatSopService {
+
+    private final QwSopMapper qwSopMapper;
+    private final QwSopTempMapper qwSopTempMapper;
+    private final SopUserLogsMapper sopUserLogsMapper;
+    private final IQwUserService qwUserService;
+    private final IQwGroupChatService qwGroupChatService;
+
+    /**
+     * 立即执行SOP任务
+     */
+    @Async("scheduledExecutorService")
+    public void executeChatSopByIds(String[] ids) {
+        try {
+            List<ChatSopRuleTimeVO> ruleTimeVOList = qwSopMapper.executeSopChatByIds(ids);
+            List<QwSopTemp> tempList = qwSopTempMapper.selectListByIds(PubFun.listToNewList(ruleTimeVOList, ChatSopRuleTimeVO::getTempId));
+            Map<String, QwSopTemp> tempMap = PubFun.listToMapByGroupObject(tempList, QwSopTemp::getId);
+            ruleTimeVOList = ruleTimeVOList.stream().filter(e -> tempMap.containsKey(e.getTempId())).collect(Collectors.toList());
+            List<QwUserVO> qwUserVOList = qwUserService.selectQwUserVOByIds(ruleTimeVOList.stream().flatMap(e -> Arrays.stream(e.getQwUserIds().split(",")).map(Long::parseLong)).toArray(Long[]::new));
+            Map<String, QwUserVO> qwUserMap = PubFun.listToMapByGroupObject(qwUserVOList, QwUserVO::getQwUserId);
+            List<QwGroupChat> qwGroupChatList = qwGroupChatService.selectQwGroupChatByChatIds(ruleTimeVOList.stream().flatMap(e -> Arrays.stream(e.getChatId().split(","))).toArray(String[]::new));
+            Map<String, QwGroupChat> groupChatMap = PubFun.listToMapByGroupObject(qwGroupChatList, QwGroupChat::getChatId);
+            ruleTimeVOList.forEach(ruleTimeVO -> {
+                QwSopTemp qwSopTemp = tempMap.get(ruleTimeVO.getTempId());
+                if (!qwSopTemp.getStatus().equals("0")) {
+                    processInternal(ruleTimeVO, qwSopTemp, qwUserMap, groupChatMap);
+                }
+                QwSop qwSop = new QwSop();
+                qwSop.setStatus(3L);
+            });
+            qwSopMapper.updateStatusQwSopById2(PubFun.listToNewList(ruleTimeVOList, ChatSopRuleTimeVO::getId));
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("立即执行执行失败", e);
+        }
+    }
+
+    private void processInternal(ChatSopRuleTimeVO timeVO, QwSopTemp temp, Map<String, QwUserVO> qwUserMap, Map<String, QwGroupChat> groupChatMap) {
+        if(StringUtils.isNotEmpty(timeVO.getChatId())){
+            List<SopUserLogs> list = Arrays.stream(timeVO.getChatId().split(",")).map(e -> {
+                SopUserLogs sopUserLogs = new SopUserLogs();
+                sopUserLogs.setSopId(timeVO.getId());
+                sopUserLogs.setSopTempId(temp.getId());
+                QwGroupChat groupChat = groupChatMap.get(e);
+                sopUserLogs.setQwUserId(groupChat.getOwner());
+                sopUserLogs.setChatId(e);
+                sopUserLogs.setCorpId(timeVO.getCorpId());
+                sopUserLogs.setStartTime(DateUtil.formatLocalDate(timeVO.getStartTime()));
+                sopUserLogs.setStatus(1);
+                QwUserVO qwUserVO = qwUserMap.get(groupChat.getOwner());
+                if(qwUserVO != null){
+                    sopUserLogs.setUserId(qwUserVO.getId() + "|" + qwUserVO.getCompanyUserId() + "|" + qwUserVO.getCompanyId());
+                }
+                return sopUserLogs;
+            }).collect(Collectors.toList());
+            sopUserLogsMapper.batchInsertSopUserLogs(list);
+        }
+    }
+
+}

+ 0 - 1
fs-service-system/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java

@@ -512,5 +512,4 @@ public class AsyncSopTestService {
         });
         });
 
 
     }
     }
-
 }
 }

+ 11 - 4
fs-service-system/src/main/java/com/fs/qw/service/impl/QwGroupChatServiceImpl.java

@@ -30,10 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 
 /**
 /**
  * 客户群详情Service业务层处理
  * 客户群详情Service业务层处理
@@ -290,6 +287,16 @@ public class QwGroupChatServiceImpl implements IQwGroupChatService
         return qwGroupChatMapper.selectGroupChatOptionsVOList(corpId);
         return qwGroupChatMapper.selectGroupChatOptionsVOList(corpId);
     }
     }
 
 
+    @Override
+    public List<QwGroupChatOptionsVO> listAllByQwUserList(String qwUserIds) {
+        return qwGroupChatMapper.listAllByQwUserList(qwUserIds);
+    }
+
+    @Override
+    public List<QwGroupChat> selectQwGroupChatByChatIds(String[] ids) {
+        return qwGroupChatMapper.selectQwGroupChatByChatIds(ids);
+    }
+
     /**
     /**
      *  同步客户群列表
      *  同步客户群列表
      */
      */

+ 6 - 0
fs-service-system/src/main/java/com/fs/qw/service/impl/QwGroupChatUserServiceImpl.java

@@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
@@ -214,4 +215,9 @@ public class QwGroupChatUserServiceImpl implements IQwGroupChatUserService
         return qwGroupChatUserMapper.selectQwGroupChatUserByExternalUserId(qwGroupChatUser);
         return qwGroupChatUserMapper.selectQwGroupChatUserByExternalUserId(qwGroupChatUser);
     }
     }
 
 
+    @Override
+    public List<QwGroupChatUser> selectQwGroupChatUserByChatIds(String[] array) {
+        return qwGroupChatUserMapper.selectQwGroupChatUserByChatIds(array);
+    }
+
 }
 }

+ 93 - 0
fs-service-system/src/main/java/com/fs/qw/vo/ChatSopRuleTimeVO.java

@@ -0,0 +1,93 @@
+package com.fs.qw.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+//查出要执行的SOP
+@Data
+public class ChatSopRuleTimeVO extends BaseEntity {
+
+    /** id */
+    private String id;
+
+    private String corpId;
+
+    /** 规则名称 */
+    @Excel(name = "规则名称")
+    private String name;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private Long status;
+
+    /** 类别 1个人 2企业微信 */
+    @Excel(name = "类别 1个人 2企业微信")
+    private Integer type;
+
+    /** qw_user主表的主键 */
+    @Excel(name = "qw_user主表的主键")
+    private String qwUserIds;
+
+    @Excel(name = "公司编号")
+    private Long companyId;
+
+    /**
+     发送类型 1定时接口发送 2 Ai接口发送  3wanke  4AIduihua
+     **/
+    private Integer sendType;
+
+    /**
+    * 筛选的标签
+    */
+    private String tags;
+
+    /**
+    * 排除的标签
+    */
+    private String excludeTags;
+
+    /**
+    * 开始时间
+    */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startTime;
+    /**
+     *  是否开启新客户添加自动创建sop 1 否 2 是
+     */
+    private Integer isAutoSop;
+    /**
+     *   自动添加SOP的时间段
+     */
+    private String autoSopTime;
+
+    /**
+    * 模板id
+    */
+    private String tempId;
+
+    /**
+     * 过滤类别 1 包含全部 2 包含任意
+     */
+    private Integer filterType;
+
+    /**
+    * 模板内容
+    */
+//    private String tempSetting;
+
+    /**
+    * 模板状态 正常  停用
+    */
+    private String tempStatus;
+
+    /**
+    * 间隔天数
+    */
+    private Integer tempGap;
+    private String chatId;
+
+}

+ 1 - 0
fs-service-system/src/main/java/com/fs/qw/vo/QwSopRuleTimeVO.java

@@ -89,5 +89,6 @@ public class QwSopRuleTimeVO extends BaseEntity {
     */
     */
     private Integer tempGap;
     private Integer tempGap;
     private Integer project;
     private Integer project;
+    private String chatId;
 
 
 }
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/qw/vo/QwSopTempSetting.java

@@ -21,6 +21,7 @@ public class QwSopTempSetting implements Serializable{
         //普通/课程/订单/ai
         //普通/课程/订单/ai
         private Long id;
         private Long id;
         private int type;
         private int type;
+        private int index;
 
 
         private String contentType;
         private String contentType;
 
 

+ 1 - 0
fs-service-system/src/main/java/com/fs/qw/vo/WxSopRuleTimeVO.java

@@ -89,5 +89,6 @@ public class WxSopRuleTimeVO extends BaseEntity {
     * 间隔天数
     * 间隔天数
     */
     */
     private Integer tempGap;
     private Integer tempGap;
+    private String chatId;
 
 
 }
 }

+ 25 - 14
fs-service-system/src/main/java/com/fs/sop/domain/QwSop.java

@@ -15,26 +15,35 @@ import java.io.Serializable;
  * @date 2024-07-31
  * @date 2024-07-31
  */
  */
 @Data
 @Data
-public class QwSop implements Serializable
-{
+public class QwSop implements Serializable {
 
 
-    /** id */
+    /**
+     * id
+     */
     @TableId(type = IdType.UUID)
     @TableId(type = IdType.UUID)
     private String id;
     private String id;
 
 
-    /** 规则名称 */
+    /**
+     * 规则名称
+     */
     @Excel(name = "规则名称")
     @Excel(name = "规则名称")
     private String name;
     private String name;
 
 
-    /** 状态 */
+    /**
+     * 状态
+     */
     @Excel(name = "状态")
     @Excel(name = "状态")
     private Long status;
     private Long status;
 
 
-    /** 类别 1个人 2企业微信 */
+    /**
+     * 类别 1个人 2企业微信
+     */
     @Excel(name = "类别 1个人 2企业微信")
     @Excel(name = "类别 1个人 2企业微信")
     private Integer type;
     private Integer type;
 
 
-    /** qw_user主表的主键 */
+    /**
+     * qw_user主表的主键
+     */
     @Excel(name = "qw_user主表的主键")
     @Excel(name = "qw_user主表的主键")
     private String qwUserIds;
     private String qwUserIds;
 
 
@@ -42,7 +51,7 @@ public class QwSop implements Serializable
     private Long companyId;
     private Long companyId;
 
 
     /**
     /**
-     发送类型 1定时接口发送 2 Ai接口发送 3完课 4ai新客对话
+     * 发送类型 1定时接口发送 2 Ai接口发送 3完课 4ai新客对话
      **/
      **/
     private Integer sendType;
     private Integer sendType;
 
 
@@ -68,12 +77,12 @@ public class QwSop implements Serializable
     private Integer expiryTime;
     private Integer expiryTime;
 
 
     /**
     /**
-    *  是否开启新客户添加自动创建sop 1 否 2 是
-    */
+     * 是否开启新客户添加自动创建sop 1 否 2 是
+     */
     private Integer isAutoSop;
     private Integer isAutoSop;
     /**
     /**
-    *   自动添加SOP的时间段
-    */
+     * 自动添加SOP的时间段
+     */
     private String autoSopTime;
     private String autoSopTime;
 
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -88,8 +97,8 @@ public class QwSop implements Serializable
     private Integer voice;
     private Integer voice;
 
 
     /**
     /**
-    * 是否开启评级 1否 2是
-    */
+     * 是否开启评级 1否 2是
+     */
     private Integer isRating;
     private Integer isRating;
 
 
     private Integer courseDay;
     private Integer courseDay;
@@ -110,5 +119,7 @@ public class QwSop implements Serializable
     // 新课对话模板
     // 新课对话模板
     private String newTemplateId;
     private String newTemplateId;
     private Integer project;
     private Integer project;
+    // 群聊ID
+    private String chatId;
 
 
 }
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/sop/domain/QwSopLogs.java

@@ -17,6 +17,8 @@ public class QwSopLogs implements Serializable {
 
 
     private String id;
     private String id;
 
 
+    private Integer type;
+
     private String corpId;
     private String corpId;
 
 
     private Long customerId;
     private Long customerId;

+ 2 - 0
fs-service-system/src/main/java/com/fs/sop/domain/QwSopTemp.java

@@ -65,6 +65,8 @@ public class QwSopTemp implements Serializable
     @TableField(exist = false)
     @TableField(exist = false)
     private List<Map<String, Object>> rules;
     private List<Map<String, Object>> rules;
     @TableField(exist = false)
     @TableField(exist = false)
+    private boolean cuoser;
+    @TableField(exist = false)
     private List<QwSopTempDay> list = new ArrayList<>();
     private List<QwSopTempDay> list = new ArrayList<>();
 
 
 }
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/sop/domain/SopUserLogs.java

@@ -13,6 +13,7 @@ public class SopUserLogs implements Serializable {
     private String sopTempId;
     private String sopTempId;
 
 
     private String qwUserId;
     private String qwUserId;
+    private String chatId;
     private String corpId;
     private String corpId;
     @JsonFormat(pattern = "yyyy-MM-dd")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private String startTime;
     private String startTime;

+ 2 - 1
fs-service-system/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java

@@ -274,5 +274,6 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     List<QwSopLogs> selectSopLogsByCreateCorpMassSendResult();
     List<QwSopLogs> selectSopLogsByCreateCorpMassSendResult();
-
+    @DataSource(DataSourceType.SOP)
+    List<QwSopLogsListCVO> selectQwSopLogsListByChatSopId(@Param("map") QwSopLogsParam param);
 }
 }

+ 5 - 0
fs-service-system/src/main/java/com/fs/sop/mapper/QwSopMapper.java

@@ -8,6 +8,7 @@ import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.enums.DataSourceType;
 import com.fs.qw.domain.QwSopUpdateStatus;
 import com.fs.qw.domain.QwSopUpdateStatus;
 import com.fs.qw.result.QwFilterSopCustomersResult;
 import com.fs.qw.result.QwFilterSopCustomersResult;
+import com.fs.qw.vo.ChatSopRuleTimeVO;
 import com.fs.qw.vo.QwSopRuleTimeVO;
 import com.fs.qw.vo.QwSopRuleTimeVO;
 import com.fs.qw.vo.WxSopRuleTimeVO;
 import com.fs.qw.vo.WxSopRuleTimeVO;
 import com.fs.sop.domain.QwSop;
 import com.fs.sop.domain.QwSop;
@@ -246,6 +247,8 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
     public List<QwSopRuleTimeVO> executeSopByIds(@Param("ids") String[] ids);
     public List<QwSopRuleTimeVO> executeSopByIds(@Param("ids") String[] ids);
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     public List<WxSopRuleTimeVO> executeSopWxByIds(@Param("ids") String[] ids);
     public List<WxSopRuleTimeVO> executeSopWxByIds(@Param("ids") String[] ids);
+    @DataSource(DataSourceType.SOP)
+    public List<ChatSopRuleTimeVO> executeSopChatByIds(@Param("ids") String[] ids);
 
 
 //    @Select("<script> " +
 //    @Select("<script> " +
 //            "SELECT\n" +
 //            "SELECT\n" +
@@ -271,6 +274,8 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     public int updateStatusQwSopById(@Param("ids") String[] ids);
     public int updateStatusQwSopById(@Param("ids") String[] ids);
+    @DataSource(DataSourceType.SOP)
+    public int updateStatusQwSopById2(@Param("ids") List<String> ids);
 
 
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)

+ 1 - 0
fs-service-system/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java

@@ -78,4 +78,5 @@ public interface QwSopTempRulesMapper extends BaseMapper<QwSopTempRules>{
     List<QwSopTempRules> listByTempIdAndNameAndDayNum(@Param("id") String id, @Param("dayNum") Integer dayNum);
     List<QwSopTempRules> listByTempIdAndNameAndDayNum(@Param("id") String id, @Param("dayNum") Integer dayNum);
 
 
     void deleteByIdList(@Param("ids") List<String> ids);
     void deleteByIdList(@Param("ids") List<String> ids);
+
 }
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java

@@ -17,6 +17,7 @@ import java.util.List;
 
 
 
 
 @Repository
 @Repository
+@DataSource(DataSourceType.SOP)
 public interface SopUserLogsMapper {
 public interface SopUserLogsMapper {
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
@@ -162,4 +163,7 @@ public interface SopUserLogsMapper {
             "order by start_time desc" +
             "order by start_time desc" +
             "</script>"})
             "</script>"})
     List<SopUserLogsVO> selectSopUserLogsListByParam(@Param("maps") SopUserLogsParam param);
     List<SopUserLogsVO> selectSopUserLogsListByParam(@Param("maps") SopUserLogsParam param);
+
+    @DataSource(DataSourceType.SOP)
+    void batchInsertSopUserLogs(@Param("list") List<SopUserLogs> list);
 }
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/sop/service/IQwSopTempRulesService.java

@@ -88,4 +88,6 @@ public interface IQwSopTempRulesService extends IService<QwSopTempRules>{
     List<QwSopTempRules> listById(List<Long> rulesIds);
     List<QwSopTempRules> listById(List<Long> rulesIds);
 
 
     void updateSiFenTemp();
     void updateSiFenTemp();
+
+    List<QwSopTempRules> selectByTemplateIds(List<String> ids);
 }
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -134,6 +134,9 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
             //企微
             //企微
             case 2:
             case 2:
                 return qwSopLogsMapper.selectQwSopLogsListByQwSopId(param);
                 return qwSopLogsMapper.selectQwSopLogsListByQwSopId(param);
+            //企微
+            case 3:
+                return qwSopLogsMapper.selectQwSopLogsListByChatSopId(param);
         }
         }
        return null;
        return null;
     }
     }

+ 32 - 0
fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java

@@ -16,6 +16,7 @@ import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.param.QwAutoSopTimeParam;
 import com.fs.qw.param.QwAutoSopTimeParam;
 import com.fs.qw.result.QwFilterSopCustomersResult;
 import com.fs.qw.result.QwFilterSopCustomersResult;
+import com.fs.qw.service.impl.AsyncChatSopService;
 import com.fs.qw.service.impl.AsyncSopService;
 import com.fs.qw.service.impl.AsyncSopService;
 import com.fs.qw.service.impl.AsyncSopTestService;
 import com.fs.qw.service.impl.AsyncSopTestService;
 import com.fs.qw.service.impl.AsyncWxSopService;
 import com.fs.qw.service.impl.AsyncWxSopService;
@@ -49,6 +50,7 @@ import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.*;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -92,6 +94,8 @@ public class QwSopServiceImpl implements IQwSopService
     private AsyncSopTestService asyncSopTestService;
     private AsyncSopTestService asyncSopTestService;
     @Autowired
     @Autowired
     private AsyncWxSopService asyncWxSopService;
     private AsyncWxSopService asyncWxSopService;
+    @Autowired
+    private AsyncChatSopService asyncChatSopService;
 
 
 
 
     @Autowired
     @Autowired
@@ -751,6 +755,7 @@ public class QwSopServiceImpl implements IQwSopService
         List<QwSop> qwSops = qwSopMapper.selectStatusQwSopById(ids);
         List<QwSop> qwSops = qwSopMapper.selectStatusQwSopById(ids);
         List<QwSop> qwSopList = qwSops.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
         List<QwSop> qwSopList = qwSops.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
         List<QwSop> wxSopList = qwSops.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
         List<QwSop> wxSopList = qwSops.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
+        List<QwSop> chatSopList = qwSops.stream().filter(e -> e.getType() == 3).collect(Collectors.toList());
 
 
         if(!wxSopList.isEmpty()){
         if(!wxSopList.isEmpty()){
             // 筛选出 status == 1 的 IDs
             // 筛选出 status == 1 的 IDs
@@ -806,6 +811,33 @@ public class QwSopServiceImpl implements IQwSopService
                 return R.error().put("err",areadyList);
                 return R.error().put("err",areadyList);
             }
             }
         }
         }
+        if(!chatSopList.isEmpty()){
+            // 筛选出 status == 1 的 IDs
+            String[] toBeSent = chatSopList.stream()
+                    .filter(qwSop -> qwSop.getStatus() == 1)
+                    .map(QwSop::getId)
+                    .toArray(String[]::new);
+
+            // 筛选出 status != 1 的 IDs
+            List<String> areadyList = chatSopList.stream()
+                    .filter(qwSop -> qwSop.getStatus() != 1)
+                    .map(QwSop::getId)
+                    .collect(Collectors.toList());
+
+            //异步执行
+            asyncChatSopService.executeChatSopByIds(toBeSent);
+
+            if (toBeSent.length > 0) {
+                int i = qwSopMapper.updateStatusQwSopById(toBeSent);
+                if (i > 0) {
+                    return R.ok().put("suc",toBeSent).put("err",areadyList);
+                }else {
+                    return R.error();
+                }
+            }else {
+                return R.error().put("err",areadyList);
+            }
+        }
         return R.error();
         return R.error();
     }
     }
 
 

+ 6 - 0
fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempRulesServiceImpl.java

@@ -245,4 +245,10 @@ public class QwSopTempRulesServiceImpl extends ServiceImpl<QwSopTempRulesMapper,
             contentMapper.updateQwSopTempContent(content1);
             contentMapper.updateQwSopTempContent(content1);
         }
         }
     }
     }
+
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempRules> selectByTemplateIds(List<String> ids) {
+        return list(new QueryWrapper<QwSopTempRules>().in("temp_id", ids));
+    }
 }
 }

+ 22 - 1
fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -23,6 +23,7 @@ import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
+import java.time.LocalTime;
 import java.util.*;
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -262,7 +263,27 @@ public class QwSopTempServiceImpl implements IQwSopTempService
 
 
     @Override
     @Override
     public List<QwSopTemp> selectQwSopTempListNew(QwSopTemp qwSopTemp) {
     public List<QwSopTemp> selectQwSopTempListNew(QwSopTemp qwSopTemp) {
-        return qwSopTempMapper.selectQwSopTempListNew(qwSopTemp);
+        List<QwSopTemp> list = qwSopTempMapper.selectQwSopTempListNew(qwSopTemp);
+        if(qwSopTemp.isCuoser()){
+            List<QwSopTempRules> rulesList = qwSopTempRulesService.selectByTemplateIds(PubFun.listToNewList(list, QwSopTemp::getId));
+            rulesList.sort(Comparator.comparing(
+                    e -> {
+                        String timeStr = e.getTime();
+                        if (timeStr == null || timeStr.isEmpty()) {
+                            return null; // 返回 null 表示该条目需排在最后
+                        }
+                        return LocalTime.parse(timeStr + ":00"); // 正常解析有效时间
+                    },
+                    Comparator.nullsLast(Comparator.naturalOrder()) // 处理 null 并排序
+            ));
+            Map<String, List<QwSopTempRules>> ruleMap = PubFun.listToMapByGroupList(rulesList, QwSopTempRules::getTempId);
+            list.stream().filter(e -> ruleMap.containsKey(e.getId())).forEach(e -> {
+                List<QwSopTempRules> rulesListSub = ruleMap.get(e.getId());
+                Map<Long, QwSopTempRules> dayMap = PubFun.listToMapByGroupObject(rulesListSub, QwSopTempRules::getDayId);
+                e.setCuoser(dayMap.entrySet().stream().allMatch(d -> d.getValue().getContentType() == 2));
+            });
+        }
+        return list;
     }
     }
 
 
     @Override
     @Override

+ 1 - 0
fs-service-system/src/main/java/com/fs/sop/vo/SopUserLogsVo.java

@@ -38,5 +38,6 @@ public class SopUserLogsVo  {
     private Integer isFixed;
     private Integer isFixed;
     // 是否只发送注册用户
     // 是否只发送注册用户
     private Integer isRegister;
     private Integer isRegister;
+    private String chatId;
 
 
 }
 }

+ 2 - 0
fs-service-system/src/main/resources/mapper/course/FsCourseLinkMapper.xml

@@ -62,6 +62,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="qwExternalId != null">qw_external_id,</if>
             <if test="qwExternalId != null">qw_external_id,</if>
             <if test="linkType != null">link_type,</if>
             <if test="linkType != null">link_type,</if>
             <if test="isRoom != null">is_room,</if>
             <if test="isRoom != null">is_room,</if>
+            <if test="chatId != null">chat_id,</if>
          </trim>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="link != null">#{link},</if>
             <if test="link != null">#{link},</if>
@@ -77,6 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="qwExternalId != null">#{qwExternalId},</if>
             <if test="qwExternalId != null">#{qwExternalId},</if>
             <if test="linkType != null">#{linkType},</if>
             <if test="linkType != null">#{linkType},</if>
             <if test="isRoom != null">#{isRoom},</if>
             <if test="isRoom != null">#{isRoom},</if>
+            <if test="chatId != null">#{chatId},</if>
          </trim>
          </trim>
     </insert>
     </insert>
 
 

+ 87 - 0
fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsUserCoursePeriodMapper">
+    
+    <resultMap type="FsUserCoursePeriod" id="FsUserCoursePeriodResult">
+        <result property="periodId"    column="period_id"    />
+        <result property="periodName"    column="period_name"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="courseId"    column="course_id"    />
+        <result property="videoId"    column="video_id"    />
+        <result property="startingTime"    column="starting_time"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectFsUserCoursePeriodVo">
+        select period_id, period_name, company_id, course_id, video_id, starting_time, create_time, update_time from fs_user_course_period
+    </sql>
+
+    <select id="selectFsUserCoursePeriodList" parameterType="FsUserCoursePeriod" resultMap="FsUserCoursePeriodResult">
+        <include refid="selectFsUserCoursePeriodVo"/>
+        <where>  
+            <if test="periodName != null  and periodName != ''"> and period_name like concat('%', #{periodName}, '%')</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="courseId != null "> and course_id = #{courseId}</if>
+            <if test="videoId != null "> and video_id = #{videoId}</if>
+            <if test="startingTime != null "> and starting_time = #{startingTime}</if>
+        </where>
+    </select>
+    
+    <select id="selectFsUserCoursePeriodById" parameterType="Long" resultMap="FsUserCoursePeriodResult">
+        <include refid="selectFsUserCoursePeriodVo"/>
+        where period_id = #{periodId}
+    </select>
+        
+    <insert id="insertFsUserCoursePeriod" parameterType="FsUserCoursePeriod">
+        insert into fs_user_course_period
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">period_id,</if>
+            <if test="periodName != null">period_name,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="courseId != null">course_id,</if>
+            <if test="videoId != null">video_id,</if>
+            <if test="startingTime != null">starting_time,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">#{periodId},</if>
+            <if test="periodName != null">#{periodName},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="courseId != null">#{courseId},</if>
+            <if test="videoId != null">#{videoId},</if>
+            <if test="startingTime != null">#{startingTime},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsUserCoursePeriod" parameterType="FsUserCoursePeriod">
+        update fs_user_course_period
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="periodName != null">period_name = #{periodName},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="courseId != null">course_id = #{courseId},</if>
+            <if test="videoId != null">video_id = #{videoId},</if>
+            <if test="startingTime != null">starting_time = #{startingTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where period_id = #{periodId}
+    </update>
+
+    <delete id="deleteFsUserCoursePeriodById" parameterType="Long">
+        delete from fs_user_course_period where period_id = #{periodId}
+    </delete>
+
+    <delete id="deleteFsUserCoursePeriodByIds" parameterType="String">
+        delete from fs_user_course_period where period_id in 
+        <foreach item="periodId" collection="array" open="(" separator="," close=")">
+            #{periodId}
+        </foreach>
+    </delete>
+    
+</mapper>

+ 3 - 0
fs-service-system/src/main/resources/mapper/qw/QwGroupChatMapper.xml

@@ -49,6 +49,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectQwGroupChatVo"/>
         <include refid="selectQwGroupChatVo"/>
         where chat_id = #{chatId}
         where chat_id = #{chatId}
     </select>
     </select>
+    <select id="selectQwGroupChatByChatIds" resultType="com.fs.qw.domain.QwGroupChat">
+        select * from qw_group_chat where chat_id in <foreach collection="ids" open="(" separator="," close=")" item="item">#{item}</foreach>
+    </select>
     <insert id="insertOrUpdateQwGroupChat" parameterType="QwGroupChat">
     <insert id="insertOrUpdateQwGroupChat" parameterType="QwGroupChat">
         insert into qw_group_chat
         insert into qw_group_chat
         <trim prefix="(" suffix=")" suffixOverrides=",">
         <trim prefix="(" suffix=")" suffixOverrides=",">

+ 3 - 0
fs-service-system/src/main/resources/mapper/qw/QwGroupChatUserMapper.xml

@@ -56,6 +56,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectQwGroupChatUserVo"/>
         <include refid="selectQwGroupChatUserVo"/>
         where chat_id = #{chatId} AND user_id=#{userId}  AND corp_id=#{corpId}
         where chat_id = #{chatId} AND user_id=#{userId}  AND corp_id=#{corpId}
     </select>
     </select>
+    <select id="selectQwGroupChatUserByChatIds" resultType="com.fs.qw.domain.QwGroupChatUser">
+        select * from qw_group_chat_user where is_out = 1 and type = 2 and chat_id in <foreach collection="ids" open="(" separator="," close=")" item="item">#{item}</foreach>
+    </select>
 
 
     <insert id="insertOrUpdateQwGroupChatUser" parameterType="QwGroupChatUser" useGeneratedKeys="true" keyProperty="id">
     <insert id="insertOrUpdateQwGroupChatUser" parameterType="QwGroupChatUser" useGeneratedKeys="true" keyProperty="id">
         insert into qw_group_chat_user
         insert into qw_group_chat_user

+ 15 - 1
fs-service-system/src/main/resources/mapper/sop/QwSopLogsMapper.xml

@@ -510,7 +510,21 @@
         AND real_send_time < now()
         AND real_send_time < now()
         ]]>
         ]]>
     </select>
     </select>
-
+    <select id="selectQwSopLogsListByChatSopId" resultType="com.fs.sop.vo.QwSopLogsListCVO">
+        SELECT * FROM qw_sop_logs
+        <where>
+            <if test="map.sopId != null">sop_id = #{map.sopId}</if>
+            <if test="map.corpId != null">AND corp_id = #{map.corpId}</if>
+            <if test="map.sendType != null">AND send_type = #{map.sendType}</if>
+            <if test="map.sendStatus != null">AND send_status = #{map.sendStatus}</if>
+            <if test="map.receivingStatus != null">AND receiving_status = #{map.receivingStatus}</if>
+            <if test="map.qwUserid != null and map.qwUserid !='' ">AND qw_userid = #{map.qwUserid}</if>
+            <if test="map.externalUserName != null and map.externalUserName!= '' ">AND external_user_name = #{map.externalUserName}</if>
+            <if test="map.scheduleStartTime != null">AND send_time &gt;= #{map.scheduleStartTime}</if>
+            <if test="map.scheduleEndTime != null">AND send_time &lt;=  #{map.scheduleEndTime}</if>
+        </where>
+        ORDER BY send_time desc
+    </select>
 
 
 
 
     <!-- 批量更新 QwSopLogs -->
     <!-- 批量更新 QwSopLogs -->

+ 26 - 0
fs-service-system/src/main/resources/mapper/sop/QwSopMapper.xml

@@ -395,6 +395,24 @@
         select a.* from qw_sop a
         select a.* from qw_sop a
         where a.send_type != 4 and a.status in (2,3) and a.type = 1
         where a.send_type != 4 and a.status in (2,3) and a.type = 1
     </select>
     </select>
+    <select id="executeSopChatByIds" resultType="com.fs.qw.vo.ChatSopRuleTimeVO">
+
+        SELECT qs.*,
+        qst.name AS temp_name,
+        qst.setting AS temp_setting,
+        qst.status AS temp_status,
+        qst.gap AS temp_gap,
+        qst.sort AS temp_sort,
+        qst.create_time AS temp_create_time,
+        qst.create_by AS temp_create_by,
+        qst.corp_id AS temp_company_id
+        FROM qw_sop qs
+        LEFT JOIN qw_sop_temp qst ON qs.temp_id = qst.id
+        WHERE qs.id IN
+        <foreach item="id" collection="ids"  open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
     <update id="updateQwSop" parameterType="QwSop" useGeneratedKeys="false" keyProperty="id" >
     <update id="updateQwSop" parameterType="QwSop" useGeneratedKeys="false" keyProperty="id" >
         UPDATE  qw_sop
         UPDATE  qw_sop
         <trim prefix="SET" suffixOverrides=",">
         <trim prefix="SET" suffixOverrides=",">
@@ -456,6 +474,14 @@
             #{id}
             #{id}
         </foreach>
         </foreach>
     </update>
     </update>
+    <update id="updateStatusQwSopById2" useGeneratedKeys="false" keyProperty="id" >
+        UPDATE qw_sop
+        SET status = 3
+        WHERE id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
 
 
     <update id="updateMinSendStatus">
     <update id="updateMinSendStatus">
         UPDATE qw_sop
         UPDATE qw_sop

+ 21 - 1
fs-service-system/src/main/resources/mapper/sop/SopUserLogsMapper.xml

@@ -107,6 +107,26 @@
             <if test="data.userId != null  ">#{data.userId},</if>
             <if test="data.userId != null  ">#{data.userId},</if>
         </trim>
         </trim>
     </insert>
     </insert>
+    <insert id="batchInsertSopUserLogs">
+        INSERT INTO sop_user_logs
+        (
+        sop_id, sop_temp_id, qw_user_id,chat_id,corp_id, start_time,
+        status, user_id
+        )
+        VALUES
+        <foreach collection="list" item="log" separator=",">
+            (
+            #{log.sopId},
+            #{log.sopTempId},
+            #{log.qwUserId},
+            #{log.chatId},
+            #{log.corpId},
+            #{log.startTime},
+            #{log.status},
+            #{log.userId}
+            )
+        </foreach>
+    </insert>
 
 
     <update id="updateSopUserLogsByTempId" parameterType="String">
     <update id="updateSopUserLogsByTempId" parameterType="String">
         update  sop_user_logs
         update  sop_user_logs
@@ -158,7 +178,7 @@
 
 
 
 
     <select id="selectSopUserLogsListByTime" resultType="com.fs.sop.vo.SopUserLogsVo">
     <select id="selectSopUserLogsListByTime" resultType="com.fs.sop.vo.SopUserLogsVo">
-        select a.*,b.min_conversion_day,b.max_conversion_day,b.min_send,b.max_send,b.is_fixed,b.is_register from sop_user_logs a
+        select a.*,b.min_conversion_day,b.max_conversion_day,b.min_send,b.max_send,b.is_fixed,b.is_register,b.chat_id from sop_user_logs a
         inner join qw_sop b on a.sop_id = b.id
         inner join qw_sop b on a.sop_id = b.id
         where a.start_time &lt;= Now() and a.status = 1 and b.send_type != 4 and b.status in (2,3)
         where a.start_time &lt;= Now() and a.status = 1 and b.send_type != 4 and b.status in (2,3)
     </select>
     </select>

+ 11 - 15
fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java

@@ -3,13 +3,10 @@ package com.fs.app.controller;
 
 
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
 import com.fs.app.annotation.Login;
 import com.fs.app.annotation.Login;
-import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.domain.FsCourseWatchLog;
-import com.fs.course.param.FsCourseQuestionAnswerUParam;
-import com.fs.course.param.FsCourseSendRewardUParam;
 import com.fs.course.param.FsUserCourseVideoFinishUParam;
 import com.fs.course.param.FsUserCourseVideoFinishUParam;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
@@ -59,21 +56,21 @@ public class CourseWxH5Controller extends AppBaseController {
         return courseVideoService.isAddCompanyUser(param);
         return courseVideoService.isAddCompanyUser(param);
     }
     }
 
 
-    //    @ApiOperation("h5课程简介")
-//    @GetMapping("/getH5CourseByVideoId")
-//    public R getCourseByVideoId(@RequestParam("videoId") Long videoId,HttpServletRequest request)
-//    {
-//        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
-//        return R.ok().put("data",course);
-//    }
-//
+        @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId,HttpServletRequest request)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @Login
     @ApiOperation("H5课程详情")
     @ApiOperation("H5课程详情")
     @GetMapping("/videoDetails")
     @GetMapping("/videoDetails")
     public ResponseResult<FsUserCourseVideoLinkDetailsVO> getCourseVideoDetails(FsUserCourseVideoLinkParam param) {
     public ResponseResult<FsUserCourseVideoLinkDetailsVO> getCourseVideoDetails(FsUserCourseVideoLinkParam param) {
         return courseVideoService.getLinkCourseVideoDetails(param);
         return courseVideoService.getLinkCourseVideoDetails(param);
     }
     }
 
 
-
     @ApiOperation("获取真实链接")
     @ApiOperation("获取真实链接")
     @GetMapping("/getRealLink")
     @GetMapping("/getRealLink")
     public R getRealLink(@RequestParam("sortLink")String link)
     public R getRealLink(@RequestParam("sortLink")String link)
@@ -81,12 +78,11 @@ public class CourseWxH5Controller extends AppBaseController {
         return courseLinkService.getRealLink(link);
         return courseLinkService.getRealLink(link);
     }
     }
 
 
-
     @ApiOperation("更新看课时长")
     @ApiOperation("更新看课时长")
     @PostMapping("/updateWatchDuration")
     @PostMapping("/updateWatchDuration")
-    public R updateWatchDuration(@RequestBody FsUserCourseVideoFinishUParam param)
+    public R updateWatchDuration(@RequestBody FsUserCourseVideoUParam param)
     {
     {
-        return courseVideoService.updateWatchDuration(param);
+        return courseVideoService.updateWatchDurationWx(param);
     }
     }
 
 
 
 

+ 103 - 0
fs-user-app/src/main/java/com/fs/app/controller/FsUserCoursePeriodController.java

@@ -0,0 +1,103 @@
+package com.fs.app.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.service.IFsUserCoursePeriodService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 会员营期Controller
+ *
+ * @author fs
+ * @date 2025-04-11
+ */
+@RestController
+@RequestMapping("/course/userCoursePeriod")
+public class FsUserCoursePeriodController extends BaseController
+{
+    @Autowired
+    private IFsUserCoursePeriodService fsUserCoursePeriodService;
+
+    /**
+     * 查询会员营期列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCoursePeriod:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        startPage();
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsUserCoursePeriodList(fsUserCoursePeriod);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员营期列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCoursePeriod:export')")
+    @Log(title = "会员营期", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsUserCoursePeriodList(fsUserCoursePeriod);
+        ExcelUtil<FsUserCoursePeriod> util = new ExcelUtil<FsUserCoursePeriod>(FsUserCoursePeriod.class);
+        return util.exportExcel(list, "userCoursePeriod");
+    }
+
+    /**
+     * 获取会员营期详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCoursePeriod:query')")
+    @GetMapping(value = "/{periodId}")
+    public AjaxResult getInfo(@PathVariable("periodId") Long periodId)
+    {
+        return AjaxResult.success(fsUserCoursePeriodService.selectFsUserCoursePeriodById(periodId));
+    }
+
+    /**
+     * 新增会员营期
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCoursePeriod:add')")
+    @Log(title = "会员营期", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        return toAjax(fsUserCoursePeriodService.insertFsUserCoursePeriod(fsUserCoursePeriod));
+    }
+
+    /**
+     * 修改会员营期
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCoursePeriod:edit')")
+    @Log(title = "会员营期", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        return toAjax(fsUserCoursePeriodService.updateFsUserCoursePeriod(fsUserCoursePeriod));
+    }
+
+    /**
+     * 删除会员营期
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCoursePeriod:remove')")
+    @Log(title = "会员营期", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{periodIds}")
+    public AjaxResult remove(@PathVariable Long[] periodIds)
+    {
+        return toAjax(fsUserCoursePeriodService.deleteFsUserCoursePeriodByIds(periodIds));
+    }
+}