Переглянути джерело

Merge remote-tracking branch 'origin/master'

ct 1 тиждень тому
батько
коміт
913d8c8524
35 змінених файлів з 666 додано та 34 видалено
  1. 5 1
      fs-admin/src/main/java/com/fs/course/controller/FsCourseTrafficLogController.java
  2. 23 10
      fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.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. 17 17
      fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java
  6. 18 1
      fs-service/src/main/java/com/fs/course/vo/FsCourseTrafficLogListVO.java
  7. 29 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  8. 161 0
      fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java
  9. 3 0
      fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java
  10. 3 0
      fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java
  11. 7 0
      fs-service/src/main/java/com/fs/qw/domain/QwGroupChat.java
  12. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  13. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwGroupChatMapper.java
  14. 9 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  15. 45 0
      fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java
  16. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java
  17. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwGroupChatService.java
  18. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  19. 8 0
      fs-service/src/main/java/com/fs/qw/service/IQwWatchLogService.java
  20. 6 1
      fs-service/src/main/java/com/fs/qw/service/impl/AsyncChatSopService.java
  21. 14 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  22. 5 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwGroupChatServiceImpl.java
  23. 4 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  24. 45 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java
  25. 10 0
      fs-service/src/main/java/com/fs/sop/domain/QwSop.java
  26. 7 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  27. 3 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java
  28. 17 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxCreateRoomMsgDTO.java
  29. 11 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxCreateRoomRespDTO.java
  30. 1 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxWorkChatIdToRoomIdDTO.java
  31. 11 0
      fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceNew.java
  32. 33 0
      fs-service/src/main/resources/mapper/course/FsCourseTrafficLogMapper.xml
  33. 4 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  34. 14 0
      fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml
  35. 35 0
      fs-service/src/main/resources/mapper/sop/QwSopMapper.xml

+ 5 - 1
fs-admin/src/main/java/com/fs/course/controller/FsCourseTrafficLogController.java

@@ -70,7 +70,11 @@ public class FsCourseTrafficLogController extends BaseController
             param.setYear(Integer.parseInt(parts[0]));
             param.setMonth(Integer.parseInt(parts[1]));
         }
-        List<FsCourseTrafficLogListVO> list = fsCourseTrafficLogService.selectTrafficByCompany(param);
+        List<FsCourseTrafficLogListVO> list = fsCourseTrafficLogService.selectTrafficNew(param);
+        //格式化每个记录的流量为 GB 字符串
+        for (FsCourseTrafficLogListVO vo : list) {
+            vo.formatTraffic();
+        }
         ExcelUtil<FsCourseTrafficLogListVO> util = new ExcelUtil<FsCourseTrafficLogListVO>(FsCourseTrafficLogListVO.class);
         return util.exportExcel(list, "短链课程流量记录数据");
     }

+ 23 - 10
fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java

@@ -22,6 +22,7 @@ import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
 import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 import com.fs.sop.mapper.SopUserLogsMapper;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -46,6 +47,9 @@ public class FsQwCourseWatchLogController extends BaseController
     private SopUserLogsMapper sopUserLogsMapper;
     @Autowired
     private IQwWatchLogService qwWatchLogService;
+
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
     /**
      * 查询短链课程看课记录列表
      */
@@ -99,25 +103,34 @@ public class FsQwCourseWatchLogController extends BaseController
     }
 
     @GetMapping("/qwWatchLogStatisticsList")
-    public TableDataInfo qwWatchLogStatisticsList(QwWatchLogStatisticsListParam param)
-    {
+    public TableDataInfo qwWatchLogStatisticsList(QwWatchLogStatisticsListParam param) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        param.setCompanyId( loginUser.getCompany().getCompanyId());
-        if (param.getSTime()==null||param.getETime()==null){
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        if (param.getSTime() == null || param.getETime() == null) {
             return getDataTable(new ArrayList<>());
         }
-        return qwWatchLogService.selectQwWatchLogStatisticsListVO(param);
+        //济南联志健康 数据排除转接用户
+        if ("济南联志健康".equals(signProjectName)) {
+            return qwWatchLogService.selectQwWatchLogStatisticsListVOExcludeTransfer(param);
+        } else {
+            return qwWatchLogService.selectQwWatchLogStatisticsListVO(param);
+        }
     }
+
     @GetMapping("/myQwWatchLogStatisticsList")
-    public TableDataInfo myQwWatchLogStatisticsList(QwWatchLogStatisticsListParam param)
-    {
+    public TableDataInfo myQwWatchLogStatisticsList(QwWatchLogStatisticsListParam param) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
         param.setCompanyUserId(loginUser.getUser().getUserId());
-        if (param.getSTime()==null||param.getETime()==null){
+        if (param.getSTime() == null || param.getETime() == null) {
             return getDataTable(new ArrayList<>());
         }
-        return qwWatchLogService.selectQwWatchLogStatisticsListVO(param);
+        //济南联志健康 数据排除转接用户
+        if ("济南联志健康".equals(signProjectName)) {
+            return qwWatchLogService.selectQwWatchLogStatisticsListVOExcludeTransfer(param);
+        } else {
+            return qwWatchLogService.selectQwWatchLogStatisticsListVO(param);
+        }
     }
 
 

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

@@ -40,6 +40,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import com.fs.app.task.qwTask;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
@@ -77,6 +78,8 @@ public class CommonController {
 
     @Autowired
     private IQwSopLogsService qwSopLogsService;
+    @Autowired
+    private qwTask qwTask1;
 
     @Autowired
     private QwSopMapper qwSopMapper;
@@ -382,4 +385,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_USER_NUM;
+import static com.fs.qw.service.impl.AsyncChatSopService.MAX_GROUP_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;
+    }
 }

+ 17 - 17
fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java

@@ -71,23 +71,23 @@ public interface FsCourseTrafficLogMapper
      */
     public int deleteFsCourseTrafficLogByLogIds(Long[] logIds);
 
-    @Select({"<script> " +
-            "select c.company_name, SUM(l.internet_traffic) AS total_internet_traffic" +
-            ",DATE_FORMAT(l.create_time, '%Y-%m') AS `month`  from fs_course_traffic_log l " +
-            "left join company c on c.company_id = l.company_id " +
-            "where 1 = 1 " +
-            "<if test= 'maps.year != null '>" +
-            "and YEAR(l.create_time) = #{maps.year} " +
-            "</if>" +
-            "<if test= 'maps.month != null '>"+
-            "and MONTH(l.create_time) = #{maps.month} " +
-            "</if>" +
-            "<if test = ' maps.companyId !=null '> " +
-            "and l.company_id = #{maps.companyId} " +
-            "</if>" +
-            "group by l.company_id,`month` "+
-            "</script>"})
-    List<FsCourseTrafficLogListVO> selectTrafficByCompany(@Param("maps") FsCourseTrafficLogParam param);
+//    @Select({"<script> " +
+//            "select c.company_name, SUM(l.internet_traffic) AS total_internet_traffic" +
+//            ",DATE_FORMAT(l.create_time, '%Y-%m') AS `month`  from fs_course_traffic_log l " +
+//            "left join company c on c.company_id = l.company_id " +
+//            "where 1 = 1 " +
+//            "<if test= 'maps.year != null '>" +
+//            "and YEAR(l.create_time) = #{maps.year} " +
+//            "</if>" +
+//            "<if test= 'maps.month != null '>"+
+//            "and MONTH(l.create_time) = #{maps.month} " +
+//            "</if>" +
+//            "<if test = ' maps.companyId !=null '> " +
+//            "and l.company_id = #{maps.companyId} " +
+//            "</if>" +
+//            "group by l.company_id,`month` "+
+//            "</script>"})
+    List<FsCourseTrafficLogListVO> selectTrafficByCompany(FsCourseTrafficLogParam param);
 
     @Select("select * from fs_course_traffic_log where uu_id = #{uuId}")
     FsCourseTrafficLog selectFsCourseTrafficLogByuuId(@Param("uuId") String uuId);

+ 18 - 1
fs-service/src/main/java/com/fs/course/vo/FsCourseTrafficLogListVO.java

@@ -15,15 +15,32 @@ import java.io.Serializable;
 @Data
 public class FsCourseTrafficLogListVO implements Serializable
 {
+    @Excel(name = "公司名称")
     private String companyName;
     private Long companyId;
+    //@Excel(name = "课程名称")
     private String courseName;
     private Long courseId;
+    //@Excel(name = "项目名称")
     private String projectName;
     private Long project;
-
+    // 原始字节数(不用于导出)
     private Long totalInternetTraffic;
 
+    @Excel(name = "总流量 (GB)")
+    private String formattedTotalTraffic;
+
+    @Excel(name = "统计月份")
     private String month;
 
+    // 工具方法:根据 totalInternetTraffic 自动计算 formattedTotalTraffic
+    public void formatTraffic() {
+        if (this.totalInternetTraffic == null) {
+            this.formattedTotalTraffic = "0.0000 GB";
+        } else {
+            double gb = this.totalInternetTraffic.doubleValue() / (1024 * 1024 * 1024);
+            this.formattedTotalTraffic = String.format("%.4f GB", gb);
+        }
+    }
+
 }

+ 29 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -749,7 +749,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             if(ObjectUtil.isEmpty(config.getOrderAttribution())
                     ||!config.getOrderAttribution().equals(1)){
                 if(param.getCompanyUserId()!=null){
-                    if (ObjectUtil.isNotEmpty(fsuser.getCompanyUserId())&&fsuser.getCompanyUserId()!=param.getCompanyUserId()){
+                    if (ObjectUtil.isNotEmpty(fsuser.getCompanyUserId())&&!fsuser.getCompanyUserId().equals(param.getCompanyUserId())){
                         CompanyUser companyUser=companyUserService.selectCompanyUserById(fsuser.getCompanyUserId());
                         return R.error(String.format("请联系%s销售进行购买商品!",companyUser.getNickName()));
                     }else {
@@ -3843,6 +3843,31 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     return R.error("导入失败,订单号为" + dto.getOrderNumber() + "物流编码异常,请核对后再导入!");
                 }
                 list.add(dto);
+
+
+                //批量导入物流单号 存在快递鸟时订阅下方参数,进行调整
+                if(CloudHostUtils.hasCloudHostName("内蒙古一贴")){
+                    FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(dto.getOrderNumber());
+                    //订阅物流回调
+                    String lastFourNumber = "";
+                    if (dto.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+                        lastFourNumber = order.getUserPhone();
+                        if (lastFourNumber.length() == 11) {
+                            lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+                        }
+                    }
+                    expressService.subscribeEspress(order.getOrderCode(), order.getDeliverySn(), order.getDeliveryId(), lastFourNumber);
+
+                    TemplateBean templateBean = TemplateBean.builder()
+                            .orderId(order.getId().toString())
+                            .orderCode(order.getOrderCode().toString())
+                            .deliveryId(order.getDeliveryId())
+                            .deliveryName(order.getDeliveryName())
+                            .userId(order.getUserId())
+                            .templateType(TemplateListenEnum.TYPE_2.getValue())
+                            .build();
+                    publisher.publishEvent(new TemplateEvent(this, templateBean));
+                }
             }
 
             //分批次处理
@@ -3863,6 +3888,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
     }
 
+    public static void main(String[] args) {
+        String lastFourNumber = "15808582581";
+    }
     @Override
     @Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
     public R pay(FsStoreOrderPayParam param) {

+ 161 - 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,144 @@ 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,12 +2,15 @@ package com.fs.ipad.vo;
 
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class BaseVo{
 
     private Long id;
     private String uuid;
     private Long serverId;
+    private List<String> exIdList;
     private String exId;
     private String corpId;
     private String corpCode;

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

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

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

@@ -75,4 +75,11 @@ public class QwGroupChat extends BaseEntity
     private Long allOutGroup;
     @TableField(exist = false)
     private List<QwGroupChatUser> chatUserList;
+
+
+    // 是否自动拉人0否1是
+    private Long autoAdduser;
+    private String sopId;
+    private Long qwUserId;
+    private Long roomid;
 }

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

@@ -534,4 +534,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
      * 根据qw_user_id+crop_id+external_user_id查询外部联系人信息
      * */
     QwExternalContact selectQwUserListVOByQwUserIdAndCorpIdAndExternalUserId(ExternalContactParam externalContactParam);
+
+    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);
 }

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

@@ -455,4 +455,13 @@ public interface QwUserMapper extends BaseMapper<QwUser>
 
     @Select("  select company_id from qw_user where qw_user_id = #{owner} and corp_id = #{corpId}  limit 1")
     Long getCompanyIdByCorpIdAndOwner(@Param("corpId")String corpId, @Param("owner")String owner);
+
+    @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);
+
 }

+ 45 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java

@@ -106,6 +106,51 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
             "</script>"})
     List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAnd(QwWatchLogStatisticsListParam param);
 
+    @Select({"<script> " +
+            "SELECT\n" +
+            "    qec.qw_user_id id,\n" +
+            "    qu.qw_user_name AS qw_user_name, \n" +
+            "    DATE(qec.create_time) AS create_time, \n" +
+            "    COUNT(1) AS line,\n" +
+            "    COUNT(CASE WHEN qec.is_interact = 1 THEN 1 END) AS interact,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 1 THEN 1 END) AS A,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 2 THEN 1 END) AS B,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 3 THEN 1 END) AS C,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 4 THEN 1 END) AS D,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 5 THEN 1 END) AS E,\n" +
+            "    COUNT(CASE WHEN qec.fs_user_id IS NOT NULL THEN 1 END) AS sign,\n" +
+            "    COUNT(CASE WHEN qec.`status` =3 THEN 1 END) AS los,\n" +
+            "    COUNT(CASE WHEN qec.`status` IN (4, 5,6) THEN 1 END) AS del,\n" +
+            "    COUNT(CASE WHEN qec.fs_user_id IS NOT NULL and qec.fs_user_id != 0 THEN 1 END) AS reg_num\n"+
+            "FROM\n" +
+            "    qw_external_contact qec\n" +
+            "JOIN\n" +
+            "    qw_user qu ON qec.qw_user_id = qu.id \n" +
+            "left join company_user cu on qec.company_user_id = cu.user_id "+
+            "WHERE\n" +
+            "    DATE(qec.create_time) &gt;= DATE(#{sTime}) and  DATE(qec.create_time) &lt;= DATE(#{eTime}) and qec.company_id =#{companyId} " +
+            " and NOT EXISTS (\n" +
+            " SELECT 1 \n" +
+            " FROM qw_external_contact_transfer_log t " +
+            " WHERE t.external_contact_id = qec.id " +
+            " AND DATE(t.create_time) &gt;= DATE(#{sTime}) \n" +
+            " AND DATE(t.create_time) &lt;= DATE(#{eTime})\n" +
+            " )" +
+            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
+            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "<if test ='deptId !=null and deptId!=\"\"'>\n" +
+            "   and cu.dept_id = #{deptId}\n" +
+            "</if>" +
+            "<if test ='ids !=null and ids!=\"\"'>\n" +
+            "   and qec.qw_user_id in (${ids})\n" +
+            "</if>" +
+            "GROUP BY\n" +
+            "    qec.qw_user_id, DATE(qec.create_time) \n" +
+            "ORDER BY\n" +
+            "    DATE(qec.create_time) "+
+            "</script>"})
+    List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAndExcludeTransfer(QwWatchLogStatisticsListParam param);
     @Select({"<script> " +
             "SELECT\n" +
             "    qec.qw_user_id id,\n" +

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

@@ -257,4 +257,6 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
      * 根据qw_user_id+crop_id+external_user_id查询外部联系人信息
      * */
     QwExternalContact selectQwUserListVOByQwUserIdAndCorpIdAndExternalUserId(ExternalContactParam externalContactParam);
+
+    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

@@ -200,4 +200,6 @@ public interface IQwUserService
     List<QwOptionsVO> selectQwCompanyListOptionsVOBySys();
 
     List<Long> selectDeptByParentId(Long deptId,String cropId);
+
+    List<QwUser> selectQwUserByIds(List<Long> qwUserIdList);
 }

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

@@ -66,6 +66,14 @@ public interface IQwWatchLogService extends IService<QwWatchLog>{
     int deleteQwWatchLogById(Long id);
 
     TableDataInfo selectQwWatchLogStatisticsListVO(QwWatchLogStatisticsListParam param);
+
+    /**
+     * 查询进线统计,排除掉转接的数据
+     * @param param
+     * @return
+     */
+    TableDataInfo selectQwWatchLogStatisticsListVOExcludeTransfer(QwWatchLogStatisticsListParam param);
+
     List<QwWatchLogAllStatisticsListVO> selectQwWatchLogAllStatisticsListVO(QwWatchLogStatisticsListParam param);
 
     TableDataInfo selectQwWatchLogAllStatisticsListVONew(QwWatchLogStatisticsListParam param);

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

@@ -29,6 +29,11 @@ import java.util.stream.Collectors;
 @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;
     private final SopUserLogsMapper sopUserLogsMapper;
@@ -49,7 +54,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);

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

@@ -5918,6 +5918,20 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         qwExternalContactMapper.updateQwExternalContactStatusById(qwExternalContact);
     }
 
+    @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);
+    }
+
     @Override
     public R getRepeat(RepeatParam param) {
         List<QwExternalContact> list = qwExternalContactMapper.selectList(new QueryWrapper<QwExternalContact>().eq("external_user_id", param.getExternalUserId()));

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

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

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

@@ -1546,6 +1546,10 @@ public class QwUserServiceImpl implements IQwUserService
         qwUserMapper.updateById(qwUser);
     }
 
+    @Override
+    public List<QwUser> selectQwUserByIds(List<Long> qwUserIdList) {
+        return qwUserMapper.selectQwUserByIds(qwUserIdList);
+    }
     /**
      * 根据销售公司和企微ID查询企微用户
      */

+ 45 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java

@@ -209,6 +209,51 @@ public class QwWatchLogServiceImpl extends ServiceImpl<QwWatchLogMapper, QwWatch
         return rspData;
     }
 
+    /**
+     * 查询进线统计,排除掉转接的数据
+     * @param param
+     * @return
+     */
+    @Override
+    public TableDataInfo selectQwWatchLogStatisticsListVOExcludeTransfer(QwWatchLogStatisticsListParam param){
+        CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());
+        if (ObjectUtils.isNotEmpty(companyDept)&&companyDept.getParentId()==0L){
+            param.setDeptId(null);
+        }
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(new ArrayList<>());
+        rspData.setTotal(0L);
+
+        Date sTime = param.getSTime();
+        Date eTime = param.getETime();
+        List<Date> datesBetween = getDatesBetween(sTime, eTime);
+        if (datesBetween.size() > 7) {
+            return rspData;
+        }
+        if (param.getCompanyUserId()!=null){
+            param.setIds(companyUserMapper.selectQwUserIdsByCompany(param.getCompanyUserId()));
+        }
+        List<QwWatchLogStatisticsListVO> vos = qwWatchLogMapper.selectQwExtCountByDayAndExcludeTransfer(param);
+        for (QwWatchLogStatisticsListVO vo : vos) {
+            Long id = vo.getId();
+            Date createTime = vo.getCreateTime();
+            QwWatchLogStatisticsListVO stat = qwWatchLogMapper.selectQwWatchLogByQwUserId(id, createTime);
+            vo.setD1Online(stat.getD1Online());
+            vo.setD1Over(stat.getD1Over());
+            vo.setFirstOnline(stat.getFirstOnline());
+            vo.setFirstOver(stat.getFirstOver());
+        }
+        if (StringUtils.isNotBlank(param.getIds())) {
+            String replace = param.getIds().replace("(", "").replace(")", "");
+            param.setIdsList(Arrays.asList(replace.split(",")));
+        }
+        Long total = qwWatchLogMapper.selectQwExtCountByDayAndCount(param);
+        rspData.setRows(vos);
+        rspData.setTotal(total);
+        return rspData;
+    }
     @Override
     public List<QwWatchLogAllStatisticsListVO> selectQwWatchLogAllStatisticsListVO(QwWatchLogStatisticsListParam param) {
         Date sTime = param.getSTime();

+ 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

@@ -198,6 +198,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;
 }

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

@@ -221,4 +221,15 @@ 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);
+    }
 }

+ 33 - 0
fs-service/src/main/resources/mapper/course/FsCourseTrafficLogMapper.xml

@@ -281,4 +281,37 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
     </select>
 
+    <select id="selectTrafficByCompany" parameterType="com.fs.course.param.FsCourseTrafficLogParam"
+            resultType="com.fs.course.vo.FsCourseTrafficLogListVO">
+        SELECT
+        c.company_name AS companyName,
+        l.company_id AS companyId,
+        '' AS courseName,
+        0 AS courseId,
+        '' AS projectName,
+        0 AS project,
+        SUM(l.internet_traffic) AS totalInternetTraffic,
+        DATE_FORMAT(l.create_time, '%Y-%m') AS month
+        FROM fs_course_traffic_log l
+        LEFT JOIN company c ON c.company_id = l.company_id
+        <where>
+            <if test="startDate != null and startDate != ''">
+                AND l.create_time >= STR_TO_DATE(#{startDate}, '%Y-%m-%d %H:%i:%s')
+            </if>
+            <if test="endDate != null and endDate != ''">
+                AND l.create_time &lt;= STR_TO_DATE(#{endDate}, '%Y-%m-%d %H:%i:%s')
+            </if>
+            <if test="companyId != null">
+                AND l.company_id = #{companyId}
+            </if>
+            <if test="common == null">
+                AND l.company_id IS NOT NULL
+            </if>
+            <if test="common != null">
+                AND l.company_id IS NULL
+            </if>
+        </where>
+        GROUP BY l.company_id, DATE_FORMAT(l.create_time, '%Y-%m')
+    </select>
+
 </mapper>

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

@@ -786,4 +786,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectQwExternalContactVo"/>
         where user_id = #{userId} and corp_id = #{corpId} and external_user_id = #{externalUserId}
     </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>

+ 35 - 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>
@@ -568,5 +596,12 @@
         </where>
         order by create_time desc
     </select>
+    <select id="selectGroup" resultMap="QwSopResult">
+        select * from qw_sop where auto_group = 1
+    </select>
+
+    <update id="updateSopGroupIds">
+        update qw_sop set chat_id = #{chatId},pull_time = now() where id = #{id}
+    </update>
 
 </mapper>