吴树波 1 周之前
父节点
当前提交
83d57dd216
共有 39 个文件被更改,包括 512 次插入66 次删除
  1. 5 0
      fs-company/src/main/java/com/fs/qw/QwGroupChatController.java
  2. 1 1
      fs-company/src/main/resources/application.yml
  3. 99 42
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  4. 1 0
      fs-service-system/src/main/java/com/fs/course/domain/FsCourseLink.java
  5. 1 0
      fs-service-system/src/main/java/com/fs/course/param/FsCourseLinkCreateParam.java
  6. 1 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  7. 6 0
      fs-service-system/src/main/java/com/fs/qw/domain/QwGroupChat.java
  8. 4 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java
  9. 1 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwGroupChatUserMapper.java
  10. 4 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwGroupChatService.java
  11. 2 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwGroupChatUserService.java
  12. 96 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/AsyncChatSopService.java
  13. 0 1
      fs-service-system/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java
  14. 11 4
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwGroupChatServiceImpl.java
  15. 6 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwGroupChatUserServiceImpl.java
  16. 93 0
      fs-service-system/src/main/java/com/fs/qw/vo/ChatSopRuleTimeVO.java
  17. 1 0
      fs-service-system/src/main/java/com/fs/qw/vo/QwSopRuleTimeVO.java
  18. 1 0
      fs-service-system/src/main/java/com/fs/qw/vo/QwSopTempSetting.java
  19. 1 0
      fs-service-system/src/main/java/com/fs/qw/vo/WxSopRuleTimeVO.java
  20. 25 14
      fs-service-system/src/main/java/com/fs/sop/domain/QwSop.java
  21. 2 0
      fs-service-system/src/main/java/com/fs/sop/domain/QwSopLogs.java
  22. 2 0
      fs-service-system/src/main/java/com/fs/sop/domain/QwSopTemp.java
  23. 1 0
      fs-service-system/src/main/java/com/fs/sop/domain/SopUserLogs.java
  24. 2 1
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  25. 5 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  26. 1 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java
  27. 4 0
      fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java
  28. 2 0
      fs-service-system/src/main/java/com/fs/sop/service/IQwSopTempRulesService.java
  29. 3 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  30. 32 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java
  31. 6 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempRulesServiceImpl.java
  32. 22 1
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  33. 1 0
      fs-service-system/src/main/java/com/fs/sop/vo/SopUserLogsVo.java
  34. 2 0
      fs-service-system/src/main/resources/mapper/course/FsCourseLinkMapper.xml
  35. 3 0
      fs-service-system/src/main/resources/mapper/qw/QwGroupChatMapper.xml
  36. 3 0
      fs-service-system/src/main/resources/mapper/qw/QwGroupChatUserMapper.xml
  37. 15 1
      fs-service-system/src/main/resources/mapper/sop/QwSopLogsMapper.xml
  38. 26 0
      fs-service-system/src/main/resources/mapper/sop/QwSopMapper.xml
  39. 21 1
      fs-service-system/src/main/resources/mapper/sop/SopUserLogsMapper.xml

+ 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);
         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:
   level:
-    com.fs: info
+    com.fs: debug
     org.springframework: warn
 # 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.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.common.core.domain.R;
+import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.BatchUtils;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.date.DateUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.service.IFsCourseLinkService;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
 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.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
 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.vo.QwSopCourseFinishTempSetting;
 import com.fs.qw.vo.QwSopRuleTimeVO;
@@ -55,6 +62,7 @@ import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
@@ -92,9 +100,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private QwSopTagMapper qwSopTagMapper ;
     @Autowired
     private QwSopMapper sopMapper;
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
 
     @Autowired
     private IQwExternalContactService iQwExternalContactService;
+    @Autowired
+    private IQwGroupChatService qwGroupChatService;
+    @Autowired
+    private IQwGroupChatUserService qwGroupChatUserService;
 
     @Autowired
     private QwExternalContactServiceImpl qwExternalContactService;
@@ -263,7 +277,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             log.info("没有需要处理的 SOP 用户日志。");
             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 -> {
             String[] userKey = s.getUserId().split("\\|");
@@ -308,7 +330,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
             String sopId = entry.getKey();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
-            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime);
+            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap);
         }
 
         // 等待所有 SOP 分组处理完成
@@ -328,9 +350,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             maxAttempts = 3,
             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 {
-            processSopGroup(sopId, userLogsVos,currentTime);
+            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap);
         } catch (Exception e) {
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
         } 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);
 
         if (ruleTimeVO == null) {
@@ -373,7 +395,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
         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,
             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 {
-            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime);
+            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap);
         } catch (Exception e) {
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
         } 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 {
             LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
             LocalDate currentDate = currentTime.toLocalDate();
@@ -429,11 +451,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 day++;
             }
             List<QwSopTempSetting.Content> contents = getDay(tempSettings, day);
+
             if (contents == null || contents.isEmpty()) {
                 log.error("SOP ID {} 的 TempSetting 内容为空,跳过处理。", logVo.getSopId());
                 return;
             }
-
             String[] userKey = logVo.getUserId().split("\\|");
             if (userKey.length < 3) {
                 log.error("用户 ID {} 格式不正确,跳过处理。", logVo.getUserId());
@@ -487,7 +509,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
             for (QwSopTempSetting.Content content : contents) {
                 try {
-
                     LocalTime elementLocalTime = LocalTime.parse(content.getTime());
                     LocalDateTime elementDateTime = LocalDateTime.of(currentTime.toLocalDate(), elementLocalTime);
 
@@ -508,7 +529,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
                         // 如果时间差在目标范围内,更新记录
                         // 组合年月日和element的时间
-                        LocalDate targetDate = startDate.plusDays(intervalDay * tempGap);
+                        LocalDate targetDate = startDate.plusDays((long) intervalDay * tempGap);
 
                         // 将 targetDate 和 elementTime 组合成 LocalDateTime
                         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, 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){
         List<QwSopTempRules> collect = tempSettings.stream().filter(e -> e.getDayNum() == days).collect(Collectors.toList());
+        AtomicInteger i = new AtomicInteger();
         return collect.stream().map(e -> {
             QwSopTempSetting.Content content = new QwSopTempSetting.Content();
             content.setId(e.getId());
@@ -583,13 +605,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             content.setCourseType(e.getCourseType());
             content.setAiTouch(e.getAiTouch());
             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,
-                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()
                 .atZone(ZoneId.systemDefault())
@@ -639,20 +661,37 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             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,
-                        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) {
@@ -700,15 +739,15 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content,
                                       SopUserLogsVo logVo, Date sendTime, Long courseId,
                                       Long videoId, int type, String qwUserId,
-                                      String companyUserId, String companyId, String externalId,String welcomeText,
-                                      String qwUserName,Long fsUserId) {
+                                      String companyUserId, String companyId, String externalId, String welcomeText,
+                                      String qwUserName, Long fsUserId, boolean isGroupChat) {
         switch (type) {
             case 1:
                 handleNormalMessage(sopLogs, content,companyUserId);
                 break;
             case 2:
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
-                        qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId);
+                        qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId, isGroupChat);
                 break;
             case 3:
                 handleOrderMessage(sopLogs, content);
@@ -740,8 +779,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
                                      SopUserLogsVo logVo, Date sendTime, Long courseId,
                                      Long videoId, String qwUserId, String companyUserId,
-                                     String companyId, String externalId,String welcomeText,
-                                     String qwUserName,Long fsUserId) {
+                                     String companyId, String externalId, String welcomeText,
+                                     String qwUserName, Long fsUserId, boolean isGroupChat) {
         // 深拷贝 Content 对象,避免使用 JSON
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         if (clonedContent == null) {
@@ -766,22 +805,40 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 case "1":
                 case "3":
                     if ("1".equals(setting.getIsBindUrl())) {
+                        String link;
+                        if(isGroupChat){
+                            FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
+                            createParam.setCourseId(courseId);
+                            createParam.setVideoId(videoId);
+                            createParam.setCorpId(logVo.getCorpId());
+                            createParam.setCompanyUserId(Long.parseLong(companyUserId));
+                            createParam.setCompanyId(Long.parseLong(companyId));
+                            createParam.setChatId(logVo.getChatId());
+                            createParam.setQwUserId(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())) {
-                                setting.setLinkUrl(sortLink);
+                                setting.setLinkUrl(link);
                             } else {
                                 String currentValue = setting.getValue();
                                 if (currentValue == null) {
-                                    setting.setValue(sortLink);
+                                    setting.setValue(link);
                                 } else {
 //                                    setting.setValue(currentValue + "\n" + sortLink);
                                     setting.setValue(currentValue
                                             .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)
-                                            + "\n" + sortLink);
+                                            + "\n" + link);
                                 }
                             }
                         } 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 Integer isRoom;//是否发群
+    private String chatId;//是否发群
 
 }

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

@@ -26,6 +26,7 @@ public class FsCourseLinkCreateParam {
     private Integer sendType;
 
     private Date sendTime;
+    private String chatId;
 
 
 }

+ 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);
         String randomString = generateRandomString();
         link.setLink(randomString);
+        link.setChatId(param.getChatId());
         link.setCreateTime(new Date());
         Integer expireDays = 0;
         if (param.getDays() == null || param.getDays() == 0){

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

@@ -1,9 +1,12 @@
 package com.fs.qw.domain;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 客户群详情对象 qw_group_chat
  *
@@ -71,4 +74,7 @@ public class QwGroupChat extends BaseEntity
     @Excel(name = "累计退群人数")
     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}")
     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}")
     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);
 
     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等查询群成员信息 */
 
     public QwGroupChatUser selectQwGroupChatUserByExternalUserId(QwGroupChatUser qwGroupChatUser);
+
+    List<QwGroupChatUser> selectQwGroupChatUserByChatIds(String[] array);
 }

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

@@ -0,0 +1,96 @@
+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<String> updateIdList = ruleTimeVOList.stream().filter(e -> "0".equals(tempMap.get(e.getTempId()).getStatus())).map(ChatSopRuleTimeVO::getId).collect(Collectors.toList());
+            if(!updateIdList.isEmpty()){
+                QwSop qwSop = new QwSop();
+                qwSop.setStatus(0L);
+                qwSopMapper.update(qwSop, new QueryWrapper<QwSop>().in("id", updateIdList));
+            }
+            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 java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 /**
  * 客户群详情Service业务层处理
@@ -290,6 +287,16 @@ public class QwGroupChatServiceImpl implements IQwGroupChatService
         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 java.text.SimpleDateFormat;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -214,4 +215,9 @@ public class QwGroupChatUserServiceImpl implements IQwGroupChatUserService
         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 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
         private Long id;
         private int type;
+        private int index;
 
         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 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
  */
 @Data
-public class QwSop implements Serializable
-{
+public class QwSop implements Serializable {
 
-    /** id */
+    /**
+     * id
+     */
     @TableId(type = IdType.UUID)
     private String id;
 
-    /** 规则名称 */
+    /**
+     * 规则名称
+     */
     @Excel(name = "规则名称")
     private String name;
 
-    /** 状态 */
+    /**
+     * 状态
+     */
     @Excel(name = "状态")
     private Long status;
 
-    /** 类别 1个人 2企业微信 */
+    /**
+     * 类别 1个人 2企业微信
+     */
     @Excel(name = "类别 1个人 2企业微信")
     private Integer type;
 
-    /** qw_user主表的主键 */
+    /**
+     * qw_user主表的主键
+     */
     @Excel(name = "qw_user主表的主键")
     private String qwUserIds;
 
@@ -42,7 +51,7 @@ public class QwSop implements Serializable
     private Long companyId;
 
     /**
-     发送类型 1定时接口发送 2 Ai接口发送 3完课 4ai新客对话
+     * 发送类型 1定时接口发送 2 Ai接口发送 3完课 4ai新客对话
      **/
     private Integer sendType;
 
@@ -68,12 +77,12 @@ public class QwSop implements Serializable
     private Integer expiryTime;
 
     /**
-    *  是否开启新客户添加自动创建sop 1 否 2 是
-    */
+     * 是否开启新客户添加自动创建sop 1 否 2 是
+     */
     private Integer isAutoSop;
     /**
-    *   自动添加SOP的时间段
-    */
+     * 自动添加SOP的时间段
+     */
     private String autoSopTime;
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -88,8 +97,8 @@ public class QwSop implements Serializable
     private Integer voice;
 
     /**
-    * 是否开启评级 1否 2是
-    */
+     * 是否开启评级 1否 2是
+     */
     private Integer isRating;
 
     private Integer courseDay;
@@ -110,5 +119,7 @@ public class QwSop implements Serializable
     // 新课对话模板
     private String newTemplateId;
     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 Integer type;
+
     private String corpId;
 
     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)
     private List<Map<String, Object>> rules;
     @TableField(exist = false)
+    private boolean cuoser;
+    @TableField(exist = false)
     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 qwUserId;
+    private String chatId;
     private String corpId;
     @JsonFormat(pattern = "yyyy-MM-dd")
     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)
     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.qw.domain.QwSopUpdateStatus;
 import com.fs.qw.result.QwFilterSopCustomersResult;
+import com.fs.qw.vo.ChatSopRuleTimeVO;
 import com.fs.qw.vo.QwSopRuleTimeVO;
 import com.fs.qw.vo.WxSopRuleTimeVO;
 import com.fs.sop.domain.QwSop;
@@ -246,6 +247,8 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
     public List<QwSopRuleTimeVO> executeSopByIds(@Param("ids") String[] ids);
     @DataSource(DataSourceType.SOP)
     public List<WxSopRuleTimeVO> executeSopWxByIds(@Param("ids") String[] ids);
+    @DataSource(DataSourceType.SOP)
+    public List<ChatSopRuleTimeVO> executeSopChatByIds(@Param("ids") String[] ids);
 
 //    @Select("<script> " +
 //            "SELECT\n" +
@@ -271,6 +274,8 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
 
     @DataSource(DataSourceType.SOP)
     public int updateStatusQwSopById(@Param("ids") String[] ids);
+    @DataSource(DataSourceType.SOP)
+    public int updateStatusQwSopById2(@Param("ids") List<String> ids);
 
 
     @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);
 
     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
+@DataSource(DataSourceType.SOP)
 public interface SopUserLogsMapper {
 
     @DataSource(DataSourceType.SOP)
@@ -162,4 +163,7 @@ public interface SopUserLogsMapper {
             "order by start_time desc" +
             "</script>"})
     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);
 
     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:
                 return qwSopLogsMapper.selectQwSopLogsListByQwSopId(param);
+            //企微
+            case 3:
+                return qwSopLogsMapper.selectQwSopLogsListByChatSopId(param);
         }
        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.param.QwAutoSopTimeParam;
 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.AsyncSopTestService;
 import com.fs.qw.service.impl.AsyncWxSopService;
@@ -49,6 +50,7 @@ import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
@@ -92,6 +94,8 @@ public class QwSopServiceImpl implements IQwSopService
     private AsyncSopTestService asyncSopTestService;
     @Autowired
     private AsyncWxSopService asyncWxSopService;
+    @Autowired
+    private AsyncChatSopService asyncChatSopService;
 
 
     @Autowired
@@ -751,6 +755,7 @@ public class QwSopServiceImpl implements IQwSopService
         List<QwSop> qwSops = qwSopMapper.selectStatusQwSopById(ids);
         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> chatSopList = qwSops.stream().filter(e -> e.getType() == 3).collect(Collectors.toList());
 
         if(!wxSopList.isEmpty()){
             // 筛选出 status == 1 的 IDs
@@ -806,6 +811,33 @@ public class QwSopServiceImpl implements IQwSopService
                 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();
     }
 

+ 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);
         }
     }
+
+    @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 java.text.SimpleDateFormat;
+import java.time.LocalTime;
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -262,7 +263,27 @@ public class QwSopTempServiceImpl implements IQwSopTempService
 
     @Override
     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

+ 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 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="linkType != null">link_type,</if>
             <if test="isRoom != null">is_room,</if>
+            <if test="chatId != null">chat_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <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="linkType != null">#{linkType},</if>
             <if test="isRoom != null">#{isRoom},</if>
+            <if test="chatId != null">#{chatId},</if>
          </trim>
     </insert>
 

+ 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"/>
         where chat_id = #{chatId}
     </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 into qw_group_chat
         <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"/>
         where chat_id = #{chatId} AND user_id=#{userId}  AND corp_id=#{corpId}
     </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 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()
         ]]>
     </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 -->

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

@@ -395,6 +395,24 @@
         select a.* from qw_sop a
         where a.send_type != 4 and a.status in (2,3) and a.type = 1
     </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  qw_sop
         <trim prefix="SET" suffixOverrides=",">
@@ -456,6 +474,14 @@
             #{id}
         </foreach>
     </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 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>
         </trim>
     </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  sop_user_logs
@@ -158,7 +178,7 @@
 
 
     <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
         where a.start_time &lt;= Now() and a.status = 1 and b.send_type != 4 and b.status in (2,3)
     </select>