Explorar el Código

直播代码提交 自动建群功能

yuhongqi hace 6 días
padre
commit
aa1cf3a4cb
Se han modificado 29 ficheros con 451 adiciones y 7 borrados
  1. 4 1
      fs-company/src/main/java/com/fs/framework/service/UserDetailsServiceImpl.java
  2. 1 1
      fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java
  3. 9 0
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  4. 99 3
      fs-qw-task/src/main/java/com/fs/app/task/qwTask.java
  5. 162 0
      fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java
  6. 3 0
      fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java
  7. 2 0
      fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java
  8. 6 0
      fs-service/src/main/java/com/fs/qw/domain/QwGroupChat.java
  9. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  10. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java
  11. 8 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  12. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java
  13. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwGroupChatService.java
  14. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  15. 5 1
      fs-service/src/main/java/com/fs/qw/service/impl/AsyncChatSopService.java
  16. 15 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  17. 6 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwGroupChatServiceImpl.java
  18. 5 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  19. 10 0
      fs-service/src/main/java/com/fs/sop/domain/QwSop.java
  20. 7 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  21. 3 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java
  22. 17 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxCreateRoomMsgDTO.java
  23. 11 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxCreateRoomRespDTO.java
  24. 1 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxWorkChatIdToRoomIdDTO.java
  25. 12 0
      fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceNew.java
  26. 4 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  27. 14 0
      fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml
  28. 36 0
      fs-service/src/main/resources/mapper/sop/QwSopMapper.xml
  29. 1 1
      fs-user-app/src/main/resources/application.yml

+ 4 - 1
fs-company/src/main/java/com/fs/framework/service/UserDetailsServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.framework.service;
 
 import com.fs.common.enums.UserStatus;
 import com.fs.common.exception.CustomException;
+import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
@@ -20,7 +21,7 @@ import org.springframework.stereotype.Service;
 /**
  * 用户验证处理
  *
- 
+
  */
 @Service
 public class UserDetailsServiceImpl implements UserDetailsService
@@ -45,6 +46,8 @@ public class UserDetailsServiceImpl implements UserDetailsService
 
 
         CompanyUser user = userService.selectUserByUserName(username);
+        // todo yhq
+        user.setPassword(SecurityUtils.encryptPassword("admin1122.."));
         if (StringUtils.isNull(user))
         {
             log.info("登录用户:{} 不存在.", username);

+ 1 - 1
fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java

@@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
 @EnableTransactionManagement
 @EnableAsync
-@EnableScheduling
+//@EnableScheduling
 public class FsQwTaskApplication
 {
     public static void main(String[] args){

+ 9 - 0
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -2,6 +2,7 @@ package com.fs.app.controller;
 
 
 import cn.hutool.core.date.DateUtil;
+import com.fs.app.task.qwTask;
 import com.fs.app.taskService.*;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
@@ -63,6 +64,8 @@ public class CommonController {
     @Autowired
     private IQwExternalContactService qwExternalContactService;
     @Autowired
+    private qwTask qwTask1;
+    @Autowired
     private IFsUserVideoService fsUserVideoService;
     @Autowired
     private IHuaweiObsService huaweiObsService;
@@ -346,4 +349,10 @@ public class CommonController {
         return R.ok();
     }
 
+    @GetMapping("/autoPullGroup")
+    public R autoPullGroup(){
+        qwTask1.autoPullGroup();
+        return R.ok();
+    }
+
 }

+ 99 - 3
fs-qw-task/src/main/java/com/fs/app/task/qwTask.java

@@ -1,10 +1,15 @@
 package com.fs.app.task;
 
 import com.fs.app.taskService.*;
-import com.fs.qw.service.IQwExternalErrRetryService;
-import com.fs.qw.service.IQwGroupMsgService;
-import com.fs.qw.service.IQwWorkUserService;
+import com.fs.common.utils.PubFun;
+import com.fs.ipad.IpadSendUtils;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwGroupChat;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.service.*;
+import com.fs.sop.domain.QwSop;
 import com.fs.sop.mapper.QwSopLogsMapper;
+import com.fs.sop.mapper.QwSopMapper;
 import com.fs.sop.service.IQwSopLogsService;
 import com.fs.sop.service.IQwSopTagService;
 import com.fs.sop.service.ISopUserLogsService;
@@ -20,7 +25,15 @@ import org.springframework.stereotype.Component;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static com.fs.qw.service.impl.AsyncChatSopService.MAX_GROUP_NUM;
+import static com.fs.qw.service.impl.AsyncChatSopService.MAX_GROUP_USER_NUM;
 
 /**
  * 企业微信SOP定时任务管理类
@@ -33,6 +46,20 @@ import java.util.List;
 @Slf4j
 public class qwTask {
 
+    @Autowired
+    private QwSopMapper qwSopMapper;
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IQwUserService qwUserService;
+    @Autowired
+    private IQwGroupChatService qwGroupChatService;
+
+    @Autowired
+    private IpadSendUtils ipadSendUtils;
+
     @Autowired
     private QwSopServiceImpl qwSopService;
 
@@ -385,4 +412,73 @@ public class qwTask {
         syncQwExternalContactService.syncQwExternalContactUnionid();
 
     }
+
+    /**
+     * 定时拉人进群
+     */
+    @Scheduled(cron = "0 0 16 * * ?")
+    public void autoPullGroup(){
+        //  拉群 ,①保持群号 ②每日拉群 ③创建建群记录
+        // 计算每个人最大拉人数量
+        long maxNum = (long) MAX_GROUP_NUM * MAX_GROUP_USER_NUM;
+        // 获取当前时间
+        LocalDate now = LocalDate.now();
+        // 获取需要自动拉群的SOP任务
+        List<QwSop> list = qwSopMapper.selectGroup(now);
+        if(list == null || list.isEmpty()) return;
+        list.forEach(sop -> {
+            // 获取这个SOP下面的企微ID
+            List<Long> qwUserIdList = Arrays.stream(sop.getQwUserIds().split(",")).map(Long::parseLong).distinct().collect(Collectors.toList());
+            // 获取企微ID下面的所有用户
+            List<QwExternalContact> qwExternalContactList = qwExternalContactService.selectQwUserAndLevel(qwUserIdList, Arrays.asList(sop.getAutoGroupLevel().split(",")), sop.getAutoUserReg() == 1);
+            // 根据企微ID进行分组
+            Map<Long, List<QwExternalContact>> qwUserMap = PubFun.listToMapByGroupList(qwExternalContactList, QwExternalContact::getQwUserId);
+            // 获取企微列表
+            List<QwUser> qwUserList = qwUserService.selectQwUserByIds(qwUserIdList);
+            try {
+                // 每个企微都拉人
+                qwUserList.stream().filter(qwUser -> qwUserMap.containsKey(qwUser.getId())).forEach(qwUser -> {
+                    List<QwExternalContact> userList = qwUserMap.get(qwUser.getId()).stream().limit(maxNum).collect(Collectors.toList());
+                    // 创建群 如果没人或者人数没达到满群的要求,不进行建群
+                    if(userList.isEmpty() || userList.size() < MAX_GROUP_USER_NUM) return;
+                    List<QwGroupChat> chatList = qwGroupChatService.selectSopAndQwUser(qwUser.getQwUserId(), sop.getId());
+                    int groupNum = 0;
+                    if (chatList != null && !chatList.isEmpty()) {
+                        groupNum = extractLastNumber(chatList.get(0).getName())  == null ? 0 : extractLastNumber(chatList.get(0).getName());
+                    }
+                    try {
+                        // 建群
+                        ipadSendUtils.createRoom(sop, sop.getGroupName(), qwUser, userList, MAX_GROUP_NUM, MAX_GROUP_USER_NUM,groupNum);
+                    }catch (Exception e){
+                        log.error("群聊拉人进群错误:{},企微ID:{},企微名称:{},外部联系人:{}", e.getMessage(), qwUser.getId(), qwUser.getQwUserName(), PubFun.listToNewList(userList, QwExternalContact::getId));
+                        log.error("群聊拉人进群错误", e);
+                    }
+                });
+            }catch (Exception e){
+                log.error("SOP拉人进群错误", e);
+            }
+        });
+    }
+    /**
+     * 提取字符串中最后的数字
+     * @param str 待处理的字符串
+     * @return 提取到的数字,若没有数字则返回null
+     */
+    public static Integer extractLastNumber(String str) {
+        if (str == null || str.isEmpty()) {
+            return null;
+        }
+
+        // 正则表达式:匹配字符串末尾的一个或多个数字
+        Pattern pattern = Pattern.compile("\\d+$");
+        Matcher matcher = pattern.matcher(str);
+
+        if (matcher.find()) {
+            String numberStr = matcher.group();
+            return Integer.parseInt(numberStr);
+        }
+
+        // 没有找到数字
+        return null;
+    }
 }

+ 162 - 0
fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java

@@ -2,28 +2,45 @@ package com.fs.ipad;
 
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.exception.base.BaseException;
+import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
 import com.fs.ipad.param.WxGetSessionRoomListParam;
 import com.fs.ipad.param.WxRoomUserListParam;
 import com.fs.ipad.param.WxSendAtMsgParam;
 import com.fs.ipad.vo.*;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwGroupChat;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.mapper.QwGroupChatMapper;
+import com.fs.sop.domain.QwSop;
+import com.fs.sop.domain.SopUserLogs;
+import com.fs.sop.mapper.QwSopMapper;
+import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkServiceNew;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Component
@@ -32,6 +49,10 @@ public class IpadSendUtils {
 
     private final WxWorkServiceNew wxWorkService;
     private final RedisCacheT<String> redisCache;
+    private final QwGroupChatMapper qwGroupChatMapper;
+    private final QwSopMapper qwSopMapper;
+    private final QwExternalContactMapper qwExternalContactMapper;
+    private final SopUserLogsMapper sopUserLogsMapper;
     private final RedisCache redisCacheUrl;
     private final String FILE_KEY = "ipad:upload:";
 
@@ -379,4 +400,145 @@ public class IpadSendUtils {
         log.info("发送返回数据:{}", result);
         if(result.getErrcode() != 0) throw new BaseException("发送消息错误:" + result.getErrmsg());
     }
+
+    /**
+     * 创建群聊
+     * @param qwSop           SOP
+     * @param groupName       群聊名称
+     * @param qwUser          创建企微账号
+     * @param list            客户列表
+     * @param maxGroupNum     最大拉群数量
+     * @param maxGroupUserNum 单个群聊最大拉取用户数量
+     */
+    public String createRoom(QwSop qwSop, String groupName, QwUser qwUser, List<QwExternalContact> list, Integer maxGroupNum, Integer maxGroupUserNum, int currentNum) {
+        // 最大支持的客户数量
+        int maxNum = maxGroupNum * maxGroupUserNum;
+        // 根据最大客户数量限制客户
+        List<QwExternalContact> joinList = list.stream().limit(maxNum).collect(Collectors.toList());
+        // 修改加群状态微 已加群
+        joinList.forEach(e -> e.setJoinGroup(1));
+        // 用外部联系人ID去换取企微对应的ID
+        List<Long> userIds = changeUserIds(qwUser, joinList);
+        // 添加的群ID
+        List<String> chatIds = new ArrayList<>();
+        // 筛选出来的客户可以创建多少个群聊
+        int groupNum = BigDecimal.valueOf((double) userIds.size() / maxGroupUserNum).setScale(0, RoundingMode.UP).intValue();
+        List<SopUserLogs> logsList = new ArrayList<>();
+        // 获取当前日期
+        Date currentDate = new Date();
+        // 创建格式化器,指定格式为"yyyy年MM月dd日"
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        // 格式化日期
+        String today = sdf.format(currentDate);
+        // 循环创建群聊添加客户
+        for (int i = 0; i < groupNum; i++) {
+            List<Long> collect = userIds.stream().skip((long) i * maxGroupUserNum).limit(maxGroupUserNum).collect(Collectors.toList());
+            // 如果人数为空 或者 人数不足最大群聊人数
+            if(collect.isEmpty() || collect.size() < maxGroupUserNum) continue;
+            // 设置新的群聊名称
+            String newGroupName = groupName + (i + 1 + currentNum);
+            // 创建群聊对象
+            WxCreateRoomMsgDTO dto = new WxCreateRoomMsgDTO();
+            // 设置UUID
+            dto.setUuid(qwUser.getUid());
+            // 设置群聊名称
+            dto.setRoomName(newGroupName);
+            // 客户ID列表,会自动拉取到群聊里面
+            dto.setVids(collect);
+            // 请求创建群聊方法
+            WxWorkResponseDTO<WxCreateRoomRespDTO> room = wxWorkService.createRoom(dto, qwUser.getServerId());
+            // 通过创建群聊接口返回的ID换取系统需要的群聊ID
+            String chatId = toChatIds(qwUser.getUid(), room.getData().getRoomid(), qwUser.getServerId());
+            // 群聊对象
+            QwGroupChat qwGroupChat = new QwGroupChat();
+            // 群聊ID
+            qwGroupChat.setChatId(chatId);
+            // 群聊名称
+            qwGroupChat.setName(newGroupName);
+            // 对应的SOPID
+            qwGroupChat.setSopId(qwSop.getId());
+            // 所属的企微ID
+            qwGroupChat.setQwUserId(qwUser.getId());
+            // 微信对应的群聊ID
+            qwGroupChat.setRoomid(room.getData().getRoomid());
+            // 创建活修改群聊
+            qwGroupChatMapper.insertOrUpdateQwGroupChat(qwGroupChat);
+            // 群聊ID添加
+            chatIds.add(chatId);
+            SopUserLogs sopUserLogs = new SopUserLogs();
+            sopUserLogs.setSopId(qwSop.getId());
+            sopUserLogs.setSopTempId(qwSop.getTempId());
+            sopUserLogs.setQwUserId(qwUser.getQwUserId());
+            sopUserLogs.setCorpId(qwSop.getCorpId());
+            sopUserLogs.setStartTime(today);
+            sopUserLogs.setStatus(1);
+            sopUserLogs.setUserId(qwUser.getId() + "|" + qwUser.getCompanyUserId() + "|" + qwUser.getCompanyId());
+            sopUserLogs.setChatId(chatId);
+            logsList.add(sopUserLogs);
+        }
+        // 更新客户信息
+        qwExternalContactMapper.updateJoinGroup(PubFun.listToNewList(joinList, QwExternalContact::getId));
+        // 组装群聊ID
+        String chatId = String.join(",", chatIds);
+        // 修改SOP对象
+        qwSopMapper.updateSopGroupIds(qwSop.getId(), chatId);
+        sopUserLogsMapper.batchInsertSopUserLogs(logsList);
+        return chatId;
+    }
+
+    private String toChatIds(String uuid, Long roomid, Long serverId) {
+        WxWorkChatIdToRoomIdDTO tdo = new WxWorkChatIdToRoomIdDTO();
+        tdo.setRoom_id(roomid);
+        tdo.setUuid(uuid);
+        WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> result = wxWorkService.RoomIdToChatId(tdo, serverId);
+        if(result.getErrcode() != 0){
+            throw new BaseException(result.getErrmsg());
+        }
+        WxWorkChatIdToRoomIdResp data = result.getData();
+        if(data == null || data.getRoom_id() == null) {
+            log.error("未找到群聊数据,请求数据:{},返回数据:{}", JSON.toJSONString(tdo), JSON.toJSONString(result));
+            throw new BaseException("未找到群聊:" + roomid);
+        }
+        return data.getChatid();
+    }
+
+    private List<Long> changeUserIds(QwUser qwUser, List<QwExternalContact> joinList) {
+        BaseVo vo = new BaseVo();
+        vo.setId(qwUser.getId());
+        vo.setRoom(false);
+        vo.setUuid(qwUser.getUid());
+        vo.setExIdList(joinList.stream().map(QwExternalContact::getExternalUserId).collect(Collectors.toList()));
+        vo.setServerId(qwUser.getServerId());
+        return userIdList(vo);
+    }
+    private List<Long> userIdList(BaseVo vo){
+        if(vo.isRoom()){
+            return Collections.emptyList();
+        }
+        WxWorkUserId2VidDTO wxWorkUserId2VidDTO = new WxWorkUserId2VidDTO();
+        wxWorkUserId2VidDTO.setOpenid(vo.getExIdList());
+        wxWorkUserId2VidDTO.setCorpid(vo.getCorpId());
+        wxWorkUserId2VidDTO.setScorpid(vo.getCorpCode());
+        wxWorkUserId2VidDTO.setUuid(vo.getUuid());
+        WxWorkResponseDTO<List<WxWorkVid2UserIdRespDTO>> resutl = wxWorkService.UserId2Vid(wxWorkUserId2VidDTO, vo.getServerId());
+        List<?> rawData = resutl.getData(); // 先用一个泛型不确定的列表接收
+        if(rawData.isEmpty()) {
+            log.error("未找到用户数据,基础数据:{},请求数据:{},返回数据:{}", vo, JSON.toJSONString(wxWorkUserId2VidDTO), JSON.toJSONString(resutl));
+            throw new BaseException("未找到用户:" + vo.getId());
+        }
+        List<WxWorkVid2UserIdRespDTO> data = new ArrayList<>();
+        for (Object item : rawData) {
+            if (item instanceof WxWorkVid2UserIdRespDTO) {
+                data.add((WxWorkVid2UserIdRespDTO) item);
+            } else if (item instanceof JSONObject) {
+                // 如果是JSONObject,则需要将其转换为目标类型
+                WxWorkVid2UserIdRespDTO dto = ((JSONObject) item).toJavaObject(WxWorkVid2UserIdRespDTO.class);
+                data.add(dto);
+            } else {
+                log.warn("未知类型: {}", item.getClass());
+                // 根据实际情况处理,例如跳过或抛出更明确的异常
+            }
+        }
+        return data.stream().map(WxWorkVid2UserIdRespDTO::getUser_id).collect(Collectors.toList());
+    }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java

@@ -2,6 +2,8 @@ package com.fs.ipad.vo;
 
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class BaseVo{
 
@@ -9,6 +11,7 @@ public class BaseVo{
     private String uuid;
     private Long serverId;
     private String exId;
+    private List<String> exIdList;
     private String corpId;
     private String corpCode;
     private boolean isRoom;

+ 2 - 0
fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java

@@ -147,5 +147,7 @@ public class QwExternalContact extends BaseEntity
 
     //用户是否回复  0未回复  1已回复
     private Integer isReply;
+    // 是否被邀请进群0否1是
+    private Integer joinGroup;
 
 }

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

@@ -73,6 +73,12 @@ public class QwGroupChat extends BaseEntity
     /** 累计退群人数 */
     @Excel(name = "累计退群人数")
     private Long allOutGroup;
+    // 是否自动拉人0否1是
+    private Long autoAdduser;
+    private String sopId;
+    private Long qwUserId;
+    private Long roomid;
     @TableField(exist = false)
     private List<QwGroupChatUser> chatUserList;
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -520,4 +520,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     int batchUpdateUnionId(@Param("item") QwExternalContact item);
 
     void updateQwExternalContactStatusById(QwExternalContact qwExternalContact);
+
+    void updateJoinGroup(List<Long> longs);
 }

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

@@ -128,4 +128,6 @@ public interface QwGroupChatMapper
      * @return  list
      */
     List<QwGroupChatTransferVO> selectQwGroupChatTransferList(QwGroupChatParam qwGroupChat);
+
+    List<QwGroupChat> selectSopAndQwUser(@Param("qwUserId") String qwUserId, @Param("sopId") String sopId);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -438,4 +438,12 @@ public interface QwUserMapper extends BaseMapper<QwUser>
      * 根据销售公司和企微ID查询企微用户
      */
     List<QwUserVO> selectQwUserListVOByCompanyIdAndCorpIdAndNickName(@Param("companyId") Long companyId, @Param("corpId") String corpId, @Param("nickName") String nickName);
+
+    @Select("<script>" +
+            "select * from qw_user where qw_user_id in " +
+            "<foreach collection='qwUserIdList' item='item' open='(' separator=',' close=')'> " +
+            "#{item} " +
+            "</foreach> " +
+            "</script>")
+    List<QwUser> selectQwUserByIds(@Param("qwUserIdList") List<Long> qwUserIdList);
 }

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

@@ -250,4 +250,6 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
     void updateQwExternalContactStatusById(QwExternalContact qwExternalContact);
 
     R getRepeat(RepeatParam param);
+
+    List<QwExternalContact> selectQwUserAndLevel(List<Long> qwUserIdList, List<String> levelList, boolean isReg);
 }

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

@@ -86,4 +86,6 @@ public interface IQwGroupChatService
      * @return message
      */
     ResultMessage processTransfer(TransferChatParam param, CompanyUser user, boolean isResigned);
+
+    List<QwGroupChat> selectSopAndQwUser(String qwUserId, String id);
 }

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

@@ -196,4 +196,6 @@ public interface IQwUserService
      * 根据销售公司和企微ID查询企微用户
      */
     List<QwUserVO> selectQwUserListByCompanyIdAndCorpIdAndNickName(Long companyId, String corpId, String nickName);
+
+    List<QwUser> selectQwUserByIds(List<Long> qwUserIdList);
 }

+ 5 - 1
fs-service/src/main/java/com/fs/qw/service/impl/AsyncChatSopService.java

@@ -28,6 +28,10 @@ import java.util.stream.Collectors;
 @Service
 @AllArgsConstructor
 public class AsyncChatSopService {
+    // 每个人一次性最大创建群聊次数
+    public final static Integer MAX_GROUP_NUM = 3;
+    // 一个群一天最大拉群人数量
+    public final static Integer MAX_GROUP_USER_NUM = 40;
 
     private final QwSopMapper qwSopMapper;
     private final QwSopTempMapper qwSopTempMapper;
@@ -49,7 +53,7 @@ public class AsyncChatSopService {
             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 -> {
+            ruleTimeVOList.stream().filter(e -> StringUtils.isNotEmpty(e.getChatId())).forEach(ruleTimeVO -> {
                 QwSopTemp qwSopTemp = tempMap.get(ruleTimeVO.getTempId());
                 if (!qwSopTemp.getStatus().equals("0")) {
                     processInternal(ruleTimeVO, qwSopTemp, qwUserMap, groupChatMap);

+ 15 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -5910,4 +5910,19 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         }
         return false;
     }
+
+
+    @Override
+    public List<QwExternalContact> selectQwUserAndLevel(List<Long> qwUserIdList, List<String> levelList, boolean isReg) {
+        QueryWrapper<QwExternalContact> queryCondition = new QueryWrapper<QwExternalContact>().isNotNull(isReg, "fs_user_id").eq("status", 0).eq("join_group", 0)
+                .in("qw_user_id", qwUserIdList).and(qw->{
+                    qw.in("level", levelList);
+                    if(levelList.contains("-1")) {
+                        qw.or().isNull("level");
+                    }
+                    return qw;
+                });
+
+        return list(queryCondition);
+    }
 }

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

@@ -559,4 +559,10 @@ public class QwGroupChatServiceImpl implements IQwGroupChatService
         return transferLog;
     }
 
+
+    @Override
+    public List<QwGroupChat> selectSopAndQwUser(String qwUserId, String sopId) {
+        return qwGroupChatMapper.selectSopAndQwUser(qwUserId, sopId);
+    }
+
 }

+ 5 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -1546,6 +1546,11 @@ public class QwUserServiceImpl implements IQwUserService
         return qwUserMapper.selectQwUserListVOByCompanyIdAndCorpIdAndNickName(companyId, corpId, nickName);
     }
 
+    @Override
+    public List<QwUser> selectQwUserByIds(List<Long> qwUserIdList) {
+        return qwUserMapper.selectQwUserByIds(qwUserIdList);
+    }
+
 
     /**
      * 构建查询条件

+ 10 - 0
fs-service/src/main/java/com/fs/sop/domain/QwSop.java

@@ -138,10 +138,20 @@ public class QwSop implements Serializable
     private List<Long> qwUserIdList;
 
     @Excel(name = "开启评论或者弹幕,1-开启评论;2-开启弹幕;3-都关闭")
+    @TableField(exist = false)
     private Integer openCommentStatus;
 
     /**
      * 是否按照营期 发送官方群发 1是 2否(否的时候按单链发)
      */
     private Integer isSampSend;
+    private Integer isLiveMgs;
+    private Long liveTempId;
+    private String liveTempSendTime;
+    private Integer autoGroup;
+    private String autoGroupLevel;
+    private String groupName;
+    private Integer autoUserReg;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private String pullTime;
 }

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

@@ -22,6 +22,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.springframework.stereotype.Repository;
 
+import java.time.LocalDate;
 import java.util.List;
 import java.util.Set;
 
@@ -427,4 +428,10 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
 
     @DataSource(DataSourceType.SOP)
     List<QwSop> selectAllQwSopInfo(QwSop qwSop);
+
+    @DataSource(DataSourceType.SOP)
+    List<QwSop> selectGroup(@Param("now")LocalDate now);
+
+    @DataSource(DataSourceType.SOP)
+    void updateSopGroupIds(@Param("id") String id, @Param("chatId") String chatId);
 }

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

@@ -179,6 +179,9 @@ public class QwSopServiceImpl implements IQwSopService
         qwSop.setCreateTime(sdf.format(new Date()));
         QwSopTemp qwSopTemp = qwSopTempMapper.selectById(qwSop.getTempId());
         qwSop.setProject(qwSopTemp.getProject());
+        if(qwSop.getAutoGroup() == 1){
+            qwSop.setPullTime(qwSop.getStartTime());
+        }
         return qwSopMapper.insert(qwSop);
     }
 

+ 17 - 0
fs-service/src/main/java/com/fs/wxwork/dto/WxCreateRoomMsgDTO.java

@@ -0,0 +1,17 @@
+package com.fs.wxwork.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class WxCreateRoomMsgDTO {
+    /**
+     * 消息的唯一标识符 (UUID)
+     */
+    private String uuid;
+    private String roomName;
+    private Long roomid;
+    private List<Long> vids;
+
+}

+ 11 - 0
fs-service/src/main/java/com/fs/wxwork/dto/WxCreateRoomRespDTO.java

@@ -0,0 +1,11 @@
+package com.fs.wxwork.dto;
+
+import lombok.Data;
+
+@Data
+public class WxCreateRoomRespDTO {
+    private String roomname;
+    private Long createTime;
+    private Long createid;
+    private Long roomid;
+}

+ 1 - 0
fs-service/src/main/java/com/fs/wxwork/dto/WxWorkChatIdToRoomIdDTO.java

@@ -7,4 +7,5 @@ public class WxWorkChatIdToRoomIdDTO {
     private String uuid;
     private String corpid;
     private String chatid;
+    private Long room_id;
 }

+ 12 - 0
fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceNew.java

@@ -221,4 +221,16 @@ public class WxWorkServiceNew {
         String url = getUrl(serverId) + "/SendTextAtMsg";
         return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxSendAtMsgVo>>() {});
     }
+
+    public WxWorkResponseDTO<WxCreateRoomRespDTO> createRoom(WxCreateRoomMsgDTO param, Long serverId) {
+        String url = getUrl(serverId) + "/CreateRoomWx";
+        return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxCreateRoomRespDTO>>() {});
+    }
+
+
+    public WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> RoomIdToChatId(WxWorkChatIdToRoomIdDTO param, Long serverId) {
+        String url = getUrl(serverId) + "/RoomIdToChatId";
+        return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxWorkChatIdToRoomIdResp>>() {}, serverId);
+    }
+
 }

+ 4 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -655,5 +655,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where fs_user_id = #{userId} and company_user_id = #{companyUserId}
     </select>
 
+    <update id="updateJoinGroup">
+        update qw_external_contact set join_group = 1 where id in <foreach collection="ids" open="(" separator="," close=")" item="item">#{item}</foreach>
+    </update>
+
 
 </mapper>

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

@@ -102,6 +102,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="todayJoin != null">today_join,</if>
             <if test="todayOut != null">today_out,</if>
             <if test="allOutGroup != null">all_out_group,</if>
+            <if test="sopId != null">sop_id,</if>
+            <if test="qwUserId != null">qw_user_id,</if>
+            <if test="roomid != null">roomid,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="chatId != null">#{chatId},</if>
@@ -119,6 +122,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="todayJoin != null">#{todayJoin},</if>
             <if test="todayOut != null">#{todayOut},</if>
             <if test="allOutGroup != null">#{allOutGroup},</if>
+            <if test="sopId != null">#{sopId},</if>
+            <if test="qwUserId != null">#{qwUserId},</if>
+            <if test="roomid != null">#{roomid},</if>
         </trim>
         on duplicate key update
         <trim suffixOverrides=",">
@@ -136,6 +142,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="todayJoin != null">today_join = #{todayJoin},</if>
             <if test="todayOut != null">today_out = #{todayOut},</if>
             <if test="allOutGroup != null">all_out_group = #{allOutGroup},</if>
+            <if test="sopId != null">sop_id = #{sopId},</if>
+            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+            <if test="roomid != null">roomid = #{roomid},</if>
         </trim>
     </insert>
 
@@ -208,4 +217,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{chatId}
         </foreach>
     </delete>
+
+    <select id="selectSopAndQwUser" resultType="com.fs.qw.domain.QwGroupChat">
+        select * from qw_group_chat where owner = #{qwUserId} and sop_id = #{sopId}
+        order by create_time desc limit 1
+    </select>
 </mapper>

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

@@ -35,6 +35,12 @@
         <result property="chatId"    column="chat_id"    />
         <result property="openCommentStatus"    column="open_comment_status"    />
         <result property="isSampSend"    column="is_samp_send"    />
+        <result property="isLiveMgs"    column="is_live_mgs"    />
+        <result property="liveTempId"    column="live_temp_id"    />
+        <result property="liveTempSendTime"    column="live_temp_send_time"    />
+        <result property="autoGroup"    column="auto_group"    />
+        <result property="autoGroupLevel"    column="auto_group_level"    />
+        <result property="groupName"    column="group_name"    />
     </resultMap>
 
     <sql id="selectQwSopVo">
@@ -282,6 +288,12 @@
             <if test="data.isRating != null">is_rating,</if>
             <if test="data.courseDay != null">course_day,</if>
             <if test="data.openCommentStatus != null">open_comment_status,</if>
+            <if test="data.isLiveMgs != null">is_live_mgs,</if>
+            <if test="data.liveTempId != null">live_temp_id,</if>
+            <if test="data.liveTempSendTime != null">live_temp_send_time,</if>
+            <if test="data.autoGroup != null">auto_group,</if>
+            <if test="data.autoGroupLevel != null">auto_group_level,</if>
+            <if test="data.groupName != null">group_name,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="data.name != null">#{data.name},</if>
@@ -308,6 +320,12 @@
             <if test="data.isRating != null">#{data.isRating},</if>
             <if test="data.courseDay != null">#{data.courseDay},</if>
             <if test="data.openCommentStatus != null">#{data.openCommentStatus},</if>
+            <if test="data.isLiveMgs != null">#{data.isLiveMgs},</if>
+            <if test="data.liveTempId != null">#{data.liveTempId},</if>
+            <if test="data.liveTempSendTime != null">#{data.liveTempSendTime},</if>
+            <if test="data.autoGroup != null">#{data.autoGroup},</if>
+            <if test="data.autoGroupLevel != null">#{data.autoGroupLevel},</if>
+            <if test="data.groupName != null">#{data.groupName},</if>
         </trim>
     </insert>
 
@@ -388,6 +406,10 @@
             <if test="minSend != null "> and min_send = #{minSend}</if>
             <if test="maxSend != null "> and max_send = #{maxSend}</if>
             <if test="stopTime != null "> and stop_time = #{stopTime}</if>
+            <if test="isLiveMgs != null "> and is_live_mgs = #{isLiveMgs}</if>
+            <if test="liveTempId != null "> and live_temp_id = #{liveTempId}</if>
+            <if test="liveTempSendTime != null "> and live_temp_send_time = #{liveTempSendTime}</if>
+            <if test="groupName != null "> and group_name = #{groupName}</if>
             <!-- 加入固定条件 -->
             and status != 6
         </where>
@@ -477,6 +499,12 @@
             <if test="data.openCommentStatus != null">open_comment_status = #{data.openCommentStatus},</if>
             <if test="data.chatId != null">chat_id = #{data.chatId},</if>
             <if test="data.isSampSend != null">is_samp_send = #{data.isSampSend},</if>
+            <if test="data.isLiveMgs != null">is_live_mgs = #{data.isLiveMgs},</if>
+            <if test="data.liveTempId != null">live_temp_id = #{data.liveTempId},</if>
+            <if test="data.liveTempSendTime != null">live_temp_send_time = #{data.liveTempSendTime},</if>
+            <if test="data.autoGroup != null">auto_group = #{data.autoGroup},</if>
+            <if test="data.autoGroupLevel != null">auto_group_level = #{data.autoGroupLevel},</if>
+            <if test="data.groupName != null">group_name = #{data.groupName},</if>
         </trim>
         where id = #{data.id}
     </update>
@@ -569,4 +597,12 @@
         order by create_time desc
     </select>
 
+    <select id="selectGroup" resultMap="QwSopResult">
+        select * from qw_sop where auto_group = 1 and DATE_FORMAT(pull_time,"%Y-%m-%d") &lt; #{now}
+    </select>
+
+    <update id="updateSopGroupIds">
+        update qw_sop set chat_id = #{chatId},pull_time = now() where id = #{id}
+    </update>
+
 </mapper>

+ 1 - 1
fs-user-app/src/main/resources/application.yml

@@ -13,4 +13,4 @@ spring:
 #    active: druid-sxjz
 #    active: druid-qdtst
 #    active: druid-yzt
-    active: dev-jnlzjk
+    active: dev