Parcourir la source

Merge remote-tracking branch 'origin/master'

ct il y a 1 semaine
Parent
commit
a30ea4af1c
62 fichiers modifiés avec 1109 ajouts et 405 suppressions
  1. 17 13
      fs-admin/src/main/java/com/fs/stats/SalesWatchStatisController.java
  2. 16 0
      fs-admin/src/main/java/com/fs/task/FsCourseTask.java
  3. 12 0
      fs-admin/src/main/java/com/fs/task/StoreTask.java
  4. 20 0
      fs-admin/src/test/java/com/fs/task/StoreTaskTest.java
  5. 24 0
      fs-company/src/main/java/com/fs/qw/QwMsgController.java
  6. 89 0
      fs-company/src/main/java/com/fs/stats/SalesWatchStatisController.java
  7. 57 3
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  8. 9 0
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyDeptMapper.java
  9. 5 0
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  10. 9 4
      fs-service-system/src/main/java/com/fs/company/service/ICompanyService.java
  11. 108 5
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  12. 3 0
      fs-service-system/src/main/java/com/fs/company/vo/CompanyUserQwListVO.java
  13. 22 0
      fs-service-system/src/main/java/com/fs/company/vo/DeptDataVO.java
  14. 3 1
      fs-service-system/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java
  15. 2 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  16. 3 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  17. 24 0
      fs-service-system/src/main/java/com/fs/course/param/FsCourseLinkMiniParam.java
  18. 2 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  19. 9 2
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java
  20. 129 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  21. 1 1
      fs-service-system/src/main/java/com/fs/fastGpt/service/AiHookService.java
  22. 5 6
      fs-service-system/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  23. 6 0
      fs-service-system/src/main/java/com/fs/qw/domain/QwCompany.java
  24. 27 0
      fs-service-system/src/main/java/com/fs/qw/enums/MsgType.java
  25. 7 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwCompanyMapper.java
  26. 7 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  27. 1 1
      fs-service-system/src/main/java/com/fs/qw/param/QwMsgSendParam.java
  28. 6 1
      fs-service-system/src/main/java/com/fs/qw/service/IQwCompanyService.java
  29. 28 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwCompanyServiceImpl.java
  30. 61 20
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwMsgServiceImpl.java
  31. 2 0
      fs-service-system/src/main/java/com/fs/qw/vo/QwContactListVO.java
  32. 2 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  33. 13 15
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java
  34. 3 3
      fs-service-system/src/main/java/com/fs/sop/vo/QwSopTask.java
  35. 7 2
      fs-service-system/src/main/java/com/fs/statis/domain/FsStatisPeriodWatch.java
  36. 8 0
      fs-service-system/src/main/java/com/fs/statis/domain/FsStatisSalerWatch.java
  37. 1 1
      fs-service-system/src/main/java/com/fs/statis/mapper/FsStatisPeriodWatchMapper.java
  38. 5 0
      fs-service-system/src/main/java/com/fs/statis/mapper/FsStatisSalerWatchMapper.java
  39. 0 47
      fs-service-system/src/main/java/com/fs/statis/service/FsStatisEveryDayWatchService.java
  40. 0 58
      fs-service-system/src/main/java/com/fs/statis/service/FsStatisPeriodWatchService.java
  41. 7 0
      fs-service-system/src/main/java/com/fs/statis/service/FsStatisSalerWatchService.java
  42. 0 71
      fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisEveryDayWatchServiceImpl.java
  43. 0 102
      fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisPeriodWatchServiceImpl.java
  44. 145 11
      fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisSalerWatchServiceImpl.java
  45. 1 1
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStorePaymentServiceImpl.java
  46. 13 0
      fs-service-system/src/main/java/com/fs/wxwork/dto/WxWorkMessageDTO.java
  47. 1 1
      fs-service-system/src/main/java/com/fs/wxwork/dto/WxWorkSendAppMsgDTO.java
  48. 1 1
      fs-service-system/src/main/java/com/fs/wxwork/service/WxWorkService.java
  49. 2 2
      fs-service-system/src/main/java/com/fs/wxwork/service/WxWorkServiceImpl.java
  50. 1 1
      fs-service-system/src/main/resources/mapper/company/CompanyMapper.xml
  51. 15 0
      fs-service-system/src/main/resources/mapper/company/CompanyUserMapper.xml
  52. 1 1
      fs-service-system/src/main/resources/mapper/course/FsCourseQuestionBankMapper.xml
  53. 1 1
      fs-service-system/src/main/resources/mapper/course/FsUserCourseMapper.xml
  54. 11 1
      fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  55. 1 1
      fs-service-system/src/main/resources/mapper/course/FsVideoResourceMapper.xml
  56. 11 1
      fs-service-system/src/main/resources/mapper/qw/QwCompanyMapper.xml
  57. 2 2
      fs-service-system/src/main/resources/mapper/qw/QwWatchLogMapper.xml
  58. 3 0
      fs-service-system/src/main/resources/mapper/sop/QwSopLogsMapper.xml
  59. 2 1
      fs-service-system/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml
  60. 4 3
      fs-service-system/src/main/resources/mapper/statis/FsStatisPeriodWatchMapper.xml
  61. 133 20
      fs-service-system/src/main/resources/mapper/statis/FsStatisSalerWatchMapper.xml
  62. 1 1
      fs-user-app/src/main/java/com/fs/core/config/MyBatisConfig.java

+ 17 - 13
fs-admin/src/main/java/com/fs/stats/SalesWatchStatisController.java

@@ -1,23 +1,20 @@
 package com.fs.stats;
 
 import com.fs.common.core.domain.R;
-import com.fs.common.core.page.TableDataInfo;
-import com.fs.company.mapper.CompanyMapper;
 import com.fs.company.service.ICompanyService;
-import com.fs.company.vo.CompanyDataVO;
+import com.fs.company.vo.DeptDataVO;
 import com.fs.sop.service.IQwSopService;
 import com.fs.sop.vo.QwSopTask;
-import com.fs.statis.domain.FsStatisPeriodWatch;
 import com.fs.statis.domain.FsStatisSalerWatch;
 import com.fs.statis.dto.StatsWatchLogPageListDTO;
-import com.fs.statis.service.FsStatisEveryDayWatchService;
-import com.fs.statis.service.FsStatisPeriodWatchService;
 import com.fs.statis.service.FsStatisSalerWatchService;
 import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 看课统计接口
@@ -31,13 +28,10 @@ public class SalesWatchStatisController {
     private FsStatisSalerWatchService fsStatisSalerWatchService;
 
     @Autowired
-    private FsStatisPeriodWatchService fsStatisPeriodWatchService;
+    private IQwSopService qwSopService;
 
     @Autowired
-    private FsStatisEveryDayWatchService fsStatisEveryDayWatchService;
-
-    @Autowired
-    private IQwSopService qwSopService;
+    private ICompanyService companyService;
 
     /**
      * 销售完播统计查询
@@ -57,7 +51,7 @@ public class SalesWatchStatisController {
      */
     @PostMapping("/period/pageList")
     public R periodQueryList(@RequestBody StatsWatchLogPageListDTO param){
-        List<FsStatisPeriodWatch> list = fsStatisPeriodWatchService.queryList(param);
+        List<FsStatisSalerWatch> list = fsStatisSalerWatchService.queryPeriodList(param);
         return R.ok().put("data", list);
     }
 
@@ -68,7 +62,7 @@ public class SalesWatchStatisController {
      */
     @PostMapping("/everyDay/pageList")
     public R everyDayQueryList(@RequestBody StatsWatchLogPageListDTO param){
-        List<FsStatisSalerWatch> list = fsStatisEveryDayWatchService.queryList(param);
+        List<FsStatisSalerWatch> list = fsStatisSalerWatchService.queryTodayList(param);
         return R.ok().put("data", list);
     }
 
@@ -83,4 +77,14 @@ public class SalesWatchStatisController {
         return R.ok().put("data",qwSopTaskList);
     }
 
+    /**
+     * 获取部门数据
+     * @return
+     */
+    @GetMapping("/getDeptData")
+    public R getDeptData(Long companyId){
+        List<DeptDataVO> data = companyService.getDeptData(companyId);
+        return R.ok().put("data",data);
+    }
+
 }

+ 16 - 0
fs-admin/src/main/java/com/fs/task/FsCourseTask.java

@@ -3,6 +3,7 @@ package com.fs.task;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.qw.service.IHyWorkTaskService;
 import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.statis.service.FsStatisSalerWatchService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -17,6 +18,8 @@ public class FsCourseTask {
     private IFsCourseWatchLogService fsCourseWatchLogService;
     @Autowired
     private IHyWorkTaskService hyWorkTaskService;
+    @Autowired
+    private FsStatisSalerWatchService fsStatisSalerWatchService;
     /**
      * 添加会员观看日志
      * @throws Exception
@@ -45,4 +48,17 @@ public class FsCourseTask {
         hyWorkTaskService.hyWorkTask();
     }
 
+    /**
+     * 看课统计
+     */
+    public void watchCourseStatis(){
+        fsStatisSalerWatchService.writeData();
+    }
+
+    /**
+     * 看课统计-统计当天的-每15分钟统计一次
+     */
+    public void watchCourseStatisToday(){
+        fsStatisSalerWatchService.writeDataToday();
+    }
 }

+ 12 - 0
fs-admin/src/main/java/com/fs/task/StoreTask.java

@@ -5,6 +5,8 @@ import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.company.service.ICompanyService;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpGoods;
@@ -121,6 +123,9 @@ public class StoreTask
     @Autowired
     private ConfigUtil configUtil;
 
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
+
     public void PushErp() throws ParseException {
         List<Long> ids = fsStoreOrderMapper.selectFsStoreOrderNoCreateOms();
         for (Long id : ids) {
@@ -130,6 +135,13 @@ public class StoreTask
 
     }
 
+    public void redPacketSubMoney() throws Exception
+    {
+        List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByCompany();
+        for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
+            companyService.subtractCompanyMoney(redPacketMoneyVO.getMoney(),redPacketMoneyVO.getCompanyId());
+        }
+    }
 
     //每5分钟执行一次
     public void deliveryOp()

+ 20 - 0
fs-admin/src/test/java/com/fs/task/StoreTaskTest.java

@@ -2,6 +2,9 @@ package com.fs.task;
 
 
 import com.fs.FSAdminApplication;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.statis.service.FsStatisSalerWatchService;
 import org.junit.Test;
@@ -9,6 +12,8 @@ import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 
+import java.util.List;
+
 @RunWith(value = org.springframework.test.context.junit4.SpringRunner.class)
 @SpringBootTest(classes = FSAdminApplication.class)
 public class StoreTaskTest {
@@ -18,6 +23,12 @@ public class StoreTaskTest {
     private FsCourseTask fsCourseTask;
     @Autowired
     private FsStatisSalerWatchService fsStatisSalerWatchService;
+
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
+
+    @Autowired
+    private ICompanyService companyService;
     @Test
     public void addQwWatchLog() {
         fsCourseWatchLogService.addCourseWatchLogDayNew();
@@ -31,4 +42,13 @@ public class StoreTaskTest {
     public void testWriteData(){
         fsStatisSalerWatchService.writeData();
     }
+
+    @Test
+    public void redPacketSubMoney() throws Exception
+    {
+        List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByCompanyFix();
+        for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
+            companyService.subtractCompanyMoney(redPacketMoneyVO.getMoney(),redPacketMoneyVO.getCompanyId());
+        }
+    }
 }

+ 24 - 0
fs-company/src/main/java/com/fs/qw/QwMsgController.java

@@ -1,6 +1,7 @@
 package com.fs.qw;
 
 import com.fs.common.annotation.Log;
+import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
@@ -12,6 +13,7 @@ import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
+import com.fs.course.param.FsCourseLinkMiniParam;
 import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseVideoService;
@@ -42,6 +44,7 @@ import org.springframework.web.bind.annotation.*;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * 企微聊天记录Controller
@@ -263,4 +266,25 @@ public class QwMsgController extends BaseController
         PageInfo<FsCourseVideoListBySidebarVO> result = new PageInfo<>(videoListBySidebar);
         return R.ok().put("data", result);
     }
+
+    /**
+     * 创建 发客户小程序
+     */
+    @RepeatSubmit
+    @PostMapping("/createMiniLink")
+    public R createMiniLink(@RequestBody FsCourseLinkMiniParam param) {
+
+        if (Objects.isNull(param.getCourseId())){
+            return R.error("课程id不能为空");
+        }
+        if (Objects.isNull(param.getVideoId())){
+            return R.error("视频id不能为空");
+        }
+
+        if (Objects.isNull(param.getExtId())){
+            return R.error("客户id不能为空");
+        }
+
+        return fsUserCourseVideoService.createMiniLink(param);
+    }
 }

+ 89 - 0
fs-company/src/main/java/com/fs/stats/SalesWatchStatisController.java

@@ -0,0 +1,89 @@
+package com.fs.stats;
+
+import com.fs.common.core.domain.R;
+import com.fs.company.mapper.CompanyMapper;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.vo.DeptDataVO;
+import com.fs.sop.service.IQwSopService;
+import com.fs.sop.vo.QwSopTask;
+import com.fs.statis.domain.FsStatisSalerWatch;
+import com.fs.statis.dto.StatsWatchLogPageListDTO;
+import com.fs.statis.service.FsStatisSalerWatchService;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 看课统计接口
+ */
+@RestController
+@RequestMapping("/stats")
+@AllArgsConstructor
+public class SalesWatchStatisController {
+
+    @Autowired
+    private FsStatisSalerWatchService fsStatisSalerWatchService;
+
+    @Autowired
+    private IQwSopService qwSopService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    /**
+     * 销售完播统计查询
+     * @param param param
+     * @return R
+     */
+    @PostMapping("/seller/pageList")
+    public R sellerQueryList(@RequestBody StatsWatchLogPageListDTO param){
+        List<FsStatisSalerWatch> list = fsStatisSalerWatchService.queryList(param);
+        return R.ok().put("data",list);
+    }
+
+    /**
+     * 训练营完播统计查询
+     * @param param param
+     * @return R
+     */
+    @PostMapping("/period/pageList")
+    public R periodQueryList(@RequestBody StatsWatchLogPageListDTO param){
+        List<FsStatisSalerWatch> list = fsStatisSalerWatchService.queryPeriodList(param);
+        return R.ok().put("data", list);
+    }
+
+    /**
+     * 每日完播统计查询
+     * @param param param
+     * @return R
+     */
+    @PostMapping("/everyDay/pageList")
+    public R everyDayQueryList(@RequestBody StatsWatchLogPageListDTO param){
+        List<FsStatisSalerWatch> list = fsStatisSalerWatchService.queryTodayList(param);
+        return R.ok().put("data", list);
+    }
+
+    /**
+     * 获取SOP任务数据
+     * @return
+     */
+    @GetMapping("/sopTaskData")
+    public R getSOPTaskData(){
+        List<QwSopTask> qwSopTaskList = qwSopService.getQwSopTaskList();
+
+        return R.ok().put("data",qwSopTaskList);
+    }
+
+    /**
+     * 获取部门数据
+     * @return
+     */
+    @GetMapping("/getDeptData")
+    public R getDeptData(Long companyId){
+        List<DeptDataVO> data = companyService.getDeptData(companyId);
+        return R.ok().put("data",data);
+    }
+
+}

+ 57 - 3
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -1,6 +1,7 @@
 package com.fs.app.controller;
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.app.socket.QwImSocket;
 import com.fs.app.util.AudioUtils;
 import com.fs.common.core.redis.RedisCache;
@@ -9,13 +10,13 @@ import com.fs.common.utils.uuid.IdUtils;
 import com.fs.fastGpt.service.AiHookService;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwUserVoiceLogService;
 import com.fs.qw.vo.QwMessageListVO;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkService;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
-import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -41,6 +42,8 @@ public class QwMsgController {
     WxWorkService wxWorkService;
     @Autowired
     IQwUserVoiceLogService qwUserVoiceLogService;
+    @Autowired
+    private IQwCompanyService qwCompanyService;
 
     @PostMapping("/callback/{serverId}")
     @ResponseBody
@@ -70,7 +73,7 @@ public class QwMsgController {
             case 104001:
                 System.out.println("登录成功");
 
-                JSONObject jsonObject = new JSONObject(wxWorkMsgResp.getJson());
+                JSONObject jsonObject = JSON.parseObject(wxWorkMsgResp.getJson());
                 QwUser qu = qwUserMapper.selectQwUserById(id);
                 Long corpId = jsonObject.getLong("Corpid");
                 System.out.println("回调中的"+corpId);
@@ -237,7 +240,10 @@ public class QwMsgController {
                 else if (wxWorkMessageDTO.getMsgtype() == 104){
                     processEmotionDynamicMessage(wxWorkMessageDTO, wxWorkMsgResp, id, userId, sendType);
                 }
-
+                // 小程序消息
+                else if (wxWorkMessageDTO.getMsgtype() == 78) {
+                    processMiniAppMessage(serverId, wxWorkMessageDTO, wxWorkMsgResp, id, userId, sendType);
+                }
                 break;
 
         }
@@ -248,6 +254,54 @@ public class QwMsgController {
         return map;
     }
 
+    /**
+     * 小程序消息处理
+     * @param serverId          服务器ID
+     * @param wxWorkMessageDTO  消息DTO
+     * @param wxWorkMsgResp     回调信息对象
+     * @param id                企微用户ID
+     * @param userId            消息发送者ID
+     * @param sendType          发送者类型 1客户 2销售
+     */
+    private void processMiniAppMessage(Long serverId, WxWorkMessageDTO wxWorkMessageDTO, WxWorkMsgResp wxWorkMsgResp, Long id, Long userId, int sendType) {
+        String thumbName = IdUtils.fastSimpleUUID() + ".jpg";
+        WxWorkResponseDTO<String> fileUrlResp =
+                aiHookService.getFileUrl(wxWorkMsgResp.getUuid(), wxWorkMessageDTO.getThumbFileId(), wxWorkMessageDTO.getThumbAESKey(), 1, thumbName, wxWorkMessageDTO.getSize(), serverId);
+        if (fileUrlResp.getErrcode() != 0) {
+            log.warn("获取图片地址失败: {}", fileUrlResp.getErrmsg());
+            return;
+        }
+
+        JSONObject json = new JSONObject();
+        json.put("appid", wxWorkMessageDTO.getAppid());
+        json.put("appName", wxWorkMessageDTO.getAppName());
+        json.put("weappIconUrl", wxWorkMessageDTO.getAppid());
+        json.put("desc", wxWorkMessageDTO.getDesc());
+        json.put("pagepath", wxWorkMessageDTO.getPagepath());
+        json.put("title", wxWorkMessageDTO.getTitle());
+        json.put("thumbnail", fileUrlResp.getData());
+
+        // 储存发送参数
+        JSONObject params = new JSONObject();
+        params.put("desc", wxWorkMessageDTO.getDesc());
+        params.put("appName", wxWorkMessageDTO.getAppName());
+        params.put("title", wxWorkMessageDTO.getTitle());
+        params.put("weappIconUrl", wxWorkMessageDTO.getWeappIconUrl());
+        params.put("pagepath", wxWorkMessageDTO.getPagepath());
+        params.put("username", wxWorkMessageDTO.getUsername());
+        params.put("appid", wxWorkMessageDTO.getAppid());
+        params.put("cdnkey", wxWorkMessageDTO.getThumbFileId());
+        params.put("md5", wxWorkMessageDTO.getThumbMD5());
+        params.put("aeskey", wxWorkMessageDTO.getThumbAESKey());
+        params.put("fileSize", wxWorkMessageDTO.getSize());
+        params.put("thumbnail", fileUrlResp.getData());
+        qwCompanyService.saveSendParams(params);
+
+        // 保存聊天消息
+        QwMessageListVO message = aiHookService.saveQwMsg(id, userId, json.toString(), wxWorkMsgResp.getUuid(), sendType, wxWorkMsgResp.getJson(), 5);
+        QwImSocket.broadcast(message);
+    }
+
     /**
      * 处理文本消息
      * @param id                企微用户ID

+ 9 - 0
fs-service-system/src/main/java/com/fs/company/mapper/CompanyDeptMapper.java

@@ -90,4 +90,13 @@ public interface CompanyDeptMapper
      */
     @Select("select cd.* from company_dept cd where cd.company_id = #{companyId} and cd.dept_name = '默认' and cd.parent_id = 0 limit 1")
     CompanyDept getTopCompanyDeptByCompanyId(@Param("companyId") Long companyId);
+
+    /**
+     * 获取所有部门的数据
+     * @param companyId 公司id
+     * @return 部门数据
+     */
+    @Select("select * from company_dept where company_id = ${companyId}")
+    List<CompanyDept> selectDeptDataByCompanyId(@Param("companyId") Long companyId);
+
 }

+ 5 - 0
fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java

@@ -276,4 +276,9 @@ public interface CompanyUserMapper
 
     Long queryCompanyUserWatchCountCompleted(@Param("companyUserId") Long companyUserId,
                                              @Param("previousDay") LocalDate previousDay);
+
+    Long queryCompanyUserInterruptCount(@Param("companyUserId") Long companyUserId,
+                                        @Param("previousDay") LocalDate previousDay);
+
+    List<CompanyUser> selectCompanyUserByDeptId(@Param("deptId") Long deptId);
 }

+ 9 - 4
fs-service-system/src/main/java/com/fs/company/service/ICompanyService.java

@@ -3,10 +3,7 @@ package com.fs.company.service;
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.Company;
 import com.fs.company.param.CompanyParam;
-import com.fs.company.vo.CompanyCrmVO;
-import com.fs.company.vo.CompanyDataVO;
-import com.fs.company.vo.CompanyNameVO;
-import com.fs.company.vo.CompanyVO;
+import com.fs.company.vo.*;
 import com.fs.his.vo.OptionsVO;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStorePayment;
@@ -117,4 +114,12 @@ public interface ICompanyService
     List<OptionsVO> selectCompanyListByMap(Map<String, Object> params);
 
 
+    void subtractCompanyMoney(BigDecimal money, Long companyId);
+
+    /**
+     * 获取部门数据
+     * @param companyId
+     * @return
+     */
+    List<DeptDataVO> getDeptData(Long companyId);
 }

+ 108 - 5
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -7,10 +7,7 @@ import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.service.ICompanyService;
-import com.fs.company.vo.CompanyCrmVO;
-import com.fs.company.vo.CompanyDataVO;
-import com.fs.company.vo.CompanyNameVO;
-import com.fs.company.vo.CompanyVO;
+import com.fs.company.vo.*;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.his.vo.OptionsVO;
 import com.fs.store.config.StoreConfig;
@@ -18,6 +15,7 @@ import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStorePayment;
 import com.fs.store.mapper.FsStoreOrderMapper;
 import com.fs.system.service.ISysConfigService;
+import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 企业Service业务层处理
@@ -37,7 +36,7 @@ import java.util.*;
 @Service
 public class CompanyServiceImpl implements ICompanyService
 {
-    Logger logger= LoggerFactory.getLogger(getClass());;
+    Logger logger= LoggerFactory.getLogger(getClass());
     @Autowired
     private CompanyMapper companyMapper;
     @Autowired
@@ -65,6 +64,9 @@ public class CompanyServiceImpl implements ICompanyService
     @Autowired
     private CompanyDeptMapper companyDeptMapper;
 
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+
 
     @Override
     public List<OptionsVO> selectAllCompanyList() {
@@ -463,6 +465,107 @@ public class CompanyServiceImpl implements ICompanyService
         return companyMapper.selectCompanyListByMap(params);
     }
 
+    @Override
+    @Transactional
+    public void subtractCompanyMoney(BigDecimal money, Long companyId) {
+        if(companyId!=null&&companyId>0){
+            Company company=companyMapper.selectCompanyByIdForUpdate(companyId);
+            if(company!=null){
+                logger.info("扣除红包金额:"+money);
+                company.setMoney(company.getMoney().subtract(money));
+                companyMapper.updateCompany(company);
+                CompanyMoneyLogs log=new CompanyMoneyLogs();
+                log.setCompanyId(company.getCompanyId());
+                log.setRemark("扣除昨日红包金额");
+                log.setMoney(money.multiply(new BigDecimal(-1)));
+                log.setLogsType(15);
+                log.setBalance(company.getMoney());
+                log.setCreateTime(new Date());
+                moneyLogsMapper.insertCompanyMoneyLogs(log);
+            }
+        }
+    }
+
+    @Override
+    public List<DeptDataVO> getDeptData(Long companyId) {
+        List<DeptDataVO> result = new ArrayList<>();
+
+        if (companyId != null) {
+            Company company = companyMapper.selectCompanyById(companyId);
+            if (company != null) {
+                DeptDataVO companyNode = buildCompanyNode(company);
+                result.add(companyNode);
+            }
+        } else {
+            List<Company> companies = companyMapper.selectCompanyAllList();
+
+            if (companies != null && !companies.isEmpty()) {
+                for (Company company : companies) {
+                    DeptDataVO companyNode = buildCompanyNode(company);
+                    result.add(companyNode);
+                }
+            }
+        }
+
+        return result.stream().filter(e -> CollectionUtils.isNotEmpty(e.getChildren())).collect(Collectors.toList());
+    }
+    /**
+     * 构建公司节点,包含其下属部门和用户
+     * @param company 公司对象
+     * @return 公司节点
+     */
+    private DeptDataVO buildCompanyNode(Company company) {
+        DeptDataVO companyNode = new DeptDataVO();
+        companyNode.setLabel(company.getCompanyName());
+        companyNode.setId(company.getCompanyId());
+
+        List<DeptDataVO> deptDataList = getCompanyDeptUserData(company.getCompanyId());
+
+        companyNode.setChildren(deptDataList.isEmpty() ? null : deptDataList);
+
+        return companyNode;
+    }
+    /**
+     * 获取公司,以及公司部门、部门下面的销售数据
+     * @return
+     */
+    private List<DeptDataVO> getCompanyDeptUserData(Long companyId){
+        List<CompanyDept> companyDepts = companyDeptMapper.selectDeptDataByCompanyId(companyId);
+
+        if (companyDepts == null || companyDepts.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        List<DeptDataVO> result = new ArrayList<>();
+
+        for (CompanyDept companyDept : companyDepts) {
+            DeptDataVO deptNode = new DeptDataVO();
+            deptNode.setLabel(companyDept.getDeptName());
+            deptNode.setId(companyDept.getDeptId());
+
+            List<CompanyUser> companyUsers =
+                    companyUserMapper.selectCompanyUserByDeptId(companyDept.getDeptId());
+
+            List<DeptDataVO> userNodes = new ArrayList<>();
+
+            if (companyUsers != null && !companyUsers.isEmpty()) {
+                for (CompanyUser companyUser : companyUsers) {
+                    DeptDataVO userNode = new DeptDataVO();
+                    userNode.setLabel(companyUser.getUserName());
+                    userNode.setId(companyUser.getUserId());
+                    userNode.setChildren(null);
+
+                    userNodes.add(userNode);
+                }
+            }
+
+            deptNode.setChildren(userNodes.isEmpty() ? null : userNodes);
+
+            result.add(deptNode);
+        }
+
+        return result;
+    }
 
 
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/company/vo/CompanyUserQwListVO.java

@@ -116,4 +116,7 @@ public class CompanyUserQwListVO extends BaseEntity {
 
     private Integer qwStatus;
     private String addressId;
+
+    /** 是否需要单独注册会员,1-是,0-否*/
+    private Integer isNeedRegisterMember;
 }

+ 22 - 0
fs-service-system/src/main/java/com/fs/company/vo/DeptDataVO.java

@@ -0,0 +1,22 @@
+package com.fs.company.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class DeptDataVO implements Serializable {
+    /**
+     * 节点名称
+     */
+    private String label;
+    /**
+     * 节点id
+     */
+    private Long id;
+    /**
+     * 子节点
+     */
+    private List<DeptDataVO> children;
+}

+ 3 - 1
fs-service-system/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java

@@ -19,7 +19,9 @@ public class FsCourseRedPacketLog extends BaseEntity
     /** 日志Id */
     private Long logId;
 
-    private String outBatchNo;
+    private String outBatchNo;//商家批次单号
+
+    private String transferBillNo;//微信批次单号
 
     /** 课程id */
     @Excel(name = "课程id")

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java

@@ -126,6 +126,8 @@ public interface FsCourseRedPacketLogMapper
 
     @Select("SELECT company_id,sum(amount) as money from fs_course_red_packet_log  WHERE status = 1 and  DATE(create_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY) GROUP BY company_id")
     List<RedPacketMoneyVO> selectFsCourseRedPacketLogByCompany();
+    @Select("SELECT company_id,sum(amount) as money from fs_course_red_packet_log  WHERE status = 1 GROUP BY company_id")
+    List<RedPacketMoneyVO> selectFsCourseRedPacketLogByCompanyFix();
 
     List<FsCourseRedPacketLogListPVO> selectFsCourseRedPacketLogListVONew(FsCourseRedPacketLogParam fsCourseRedPacketLog);
 

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

@@ -133,4 +133,7 @@ public interface FsUserCoursePeriodMapper
     @Select("select * from fs_user_course_period where find_in_set(${companyId},company_id) and #{previousDay} >= period_starting_time and #{previousDay} <= period_end_time ")
     List<Long> queryPeriod(@Param("companyId") Long companyId,
                            @Param("previousDay") LocalDate previousDay);
+
+    List<FsUserCoursePeriod> selectPeriodListByTrainingCampIds(@Param("trainingCampIds") Long[] trainingCampIds);
+
 }

+ 24 - 0
fs-service-system/src/main/java/com/fs/course/param/FsCourseLinkMiniParam.java

@@ -0,0 +1,24 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+@Data
+public class FsCourseLinkMiniParam {
+    /**
+     * 小节ID
+     */
+    private Long videoId;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+    /**
+     * 视频标题
+     */
+    private String title;
+    /**
+    * 外部联系人主键
+    */
+    private Long extId;
+
+}

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

@@ -160,4 +160,6 @@ public interface IFsUserCourseVideoService
     List<OptionsVO> selectVideoListByMap(Map<String, Object> params);
 
     List<FsCourseVideoListBySidebarVO> getFsCourseVideoListBySidebar(FsCourseListBySidebarParam param);
+
+    R createMiniLink(FsCourseLinkMiniParam param);
 }

+ 9 - 2
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java

@@ -69,12 +69,19 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void del(Long[] ids) {
-        // 检查是否存在进行中的营期
+        // 检查是否存在进行中或者已结束的营期
         if (checkPeriodStatus(ids)) {
-            throw new ServiceException("存在进行中的营期,请营期结束后再操作");
+            throw new ServiceException("存在进行中或已结束的营期,不允许删除");
         }
 
         baseMapper.deleteBatchIds(Arrays.asList(ids));
+
+        // 删除训练营的同时需要删除营期
+        List<FsUserCoursePeriod> fsUserCoursePeriods = fsUserCoursePeriodMapper.selectPeriodListByTrainingCampIds(ids);
+        List<Long> periodIds = fsUserCoursePeriods.stream().map(FsUserCoursePeriod::getPeriodId).collect(Collectors.toList());
+        if(!periodIds.isEmpty()){
+            fsUserCoursePeriodMapper.deleteFsUserCoursePeriodByIds(periodIds.toArray(new Long[0]));
+        }
     }
 
     /**

+ 129 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -29,10 +29,12 @@ import com.fs.course.vo.FsUserCourseVideoVO;
 import com.fs.course.vo.newfs.*;
 import com.fs.his.param.WxSendRedPacketParam;
 import com.fs.his.vo.OptionsVO;
+import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.service.IQwCompanyService;
 import com.fs.qwApi.Result.QwAddContactWayResult;
 import com.fs.qwApi.param.QwAddContactWayParam;
 import com.fs.qwApi.service.QwApiService;
@@ -49,6 +51,7 @@ import com.fs.store.service.IFsStorePaymentService;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.service.cache.IFsUserCourseCacheService;
 import com.fs.system.service.ISysConfigService;
+import com.fs.voice.utils.StringUtil;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
@@ -61,11 +64,14 @@ import org.springframework.transaction.annotation.Transactional;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
 
+import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
+
 /**
  * 课堂视频Service业务层处理
  *
@@ -77,6 +83,11 @@ import java.util.stream.Collectors;
 public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 {
     private static final Logger logger = LoggerFactory.getLogger(FsUserCourseVideoServiceImpl.class);
+
+    private static final String miniappRealLink = "/pages_course/video.html?course=";
+    private static final String REAL_LINK_PREFIX = "/courseH5/pages/course/learning?course=";
+    private static final String SHORT_LINK_PREFIX = "/courseH5/pages/course/learning?s=";
+
     @Autowired
     private FsUserCourseVideoMapper fsUserCourseVideoMapper;
     @Autowired
@@ -153,6 +164,12 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     private FsUserCompanyUserMapper fsUserCompanyUserMapper;
     @Autowired
     private IFsUserCourseCacheService fsUserCourseCacheService;
+    @Autowired
+    private IQwCompanyService qwCompanyService;
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    private FsCourseLinkMapper fsCourseLinkMapper;
 
     /**
      * 查询课堂视频
@@ -1198,6 +1215,118 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return fsUserCourseVideoMapper.getFsCourseVideoListBySidebar(param);
     }
 
+    @Override
+    public R createMiniLink(FsCourseLinkMiniParam param) {
+        QwExternalContact qwExternalContact = qwExternalContactMapper.selectById(param.getExtId());
+        if (Objects.isNull(qwExternalContact)) {
+            return R.error("客户不存在");
+        }
+        QwUser qwUser = qwUserMapper.selectById(qwExternalContact.getQwUserId());
+        if (Objects.isNull(qwUser) || Objects.isNull(qwUser.getCompanyId()) || Objects.isNull(qwUser.getCompanyUserId())){
+            return R.error("员工未绑定 销售公司 或 销售 请先绑定");
+        }
+
+        QwCompany qwCompany = qwCompanyService.selectQwCompanyByCorpId(qwUser.getCorpId());
+        if (Objects.isNull(qwCompany)) {
+            return R.error().put("msg","企业不存在,请联系管理员");
+        }
+
+        //看课记录
+        addWatchLogIfNeeded(param.getVideoId(), param.getCourseId(), qwExternalContact.getFsUserId(), qwUser, qwExternalContact.getId());
+
+        //生成小程序链接
+        String linkByMiniApp = createLinkByMiniApp(new Date(), param.getCourseId(), param.getVideoId(), qwUser, qwExternalContact.getId(),2,null);
+        return R.ok().put("data", linkByMiniApp);
+    }
+
+    private String createLinkByMiniApp(Date sendTime, Long courseId, Long videoId,
+                                       QwUser qwUser, Long externalId,int type,String domainName) {
+        FsCourseLink link = new FsCourseLink();
+        link.setCompanyId(qwUser.getCompanyId());
+        link.setQwUserId(String.valueOf(qwUser.getId()));
+        link.setCompanyUserId(qwUser.getCompanyUserId());
+        link.setVideoId(videoId);
+        link.setCorpId(qwUser.getCorpId());
+        link.setCourseId(courseId);
+        link.setQwExternalId(externalId);
+
+        if (type == 1) {
+            link.setLinkType(0);
+        }else {
+            link.setLinkType(3);
+        }
+
+        String randomString = generateRandomStringWithLock();
+        if (StringUtil.strIsNullOrEmpty(randomString)){
+            link.setLink(UUID.randomUUID().toString().replace("-", ""));
+        }else {
+            link.setLink(randomString);
+        }
+
+        link.setCreateTime(sendTime);
+
+        FsCourseRealLink courseMap = new FsCourseRealLink();
+        BeanUtils.copyProperties(link,courseMap);
+
+        String courseJson = JSON.toJSONString(courseMap);
+
+        String realLinkFull;
+
+        if (type == 1) {
+            realLinkFull = REAL_LINK_PREFIX + courseJson;
+        }else {
+            realLinkFull = miniappRealLink + courseJson;
+        }
+
+        link.setRealLink(realLinkFull);
+
+        // 使用 Java 8 时间 API 计算过期时间
+        LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        LocalDateTime expireDateTime = sendDateTime.plusDays(0);
+        expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
+        Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
+        link.setUpdateTime(updateTime);
+
+        //存短链-
+        fsCourseLinkMapper.insertFsCourseLink(link);
+
+        if (type==1){
+            return domainName + SHORT_LINK_PREFIX + link.getLink();
+        }else {
+            return link.getRealLink();
+        }
+    }
+
+    // 插入观看记录
+    private void addWatchLogIfNeeded(Long videoId, Long courseId,
+                                     Long fsUserId, QwUser qwUser,Long externalId) {
+
+        try {
+            FsCourseWatchLog watchLog = new FsCourseWatchLog();
+            watchLog.setVideoId(videoId);
+            watchLog.setQwExternalContactId(externalId);
+            watchLog.setSendType(2);
+            watchLog.setQwUserId(String.valueOf(qwUser.getId()));
+            watchLog.setDuration(0L);
+            watchLog.setCourseId(courseId);
+            watchLog.setCompanyUserId(qwUser.getCompanyUserId());
+            watchLog.setCompanyId(qwUser.getCompanyId());
+            watchLog.setCreateTime(new Date());
+            watchLog.setUpdateTime(new Date());
+            watchLog.setLogType(3);
+
+            if (fsUserId == null) {
+                fsUserId=0L;
+            }
+            watchLog.setUserId(fsUserId);
+
+            //存看课记录
+            fsCourseWatchLogMapper.insertOrUpdateFsCourseWatchLog(watchLog);
+        }catch (Exception e){
+            logger.error("一键群发失败-插入观看记录失败:"+e.getMessage());
+        }
+    }
+
     //会员-更新心跳时间
     public void updateHeartbeatWx(FsUserCourseVideoUParam param) {
         String redisKey = "h5wxuser:watch:heartbeat:" + param.getUserId() + ":" + param.getVideoId() + ":" + param.getCompanyUserId();

+ 1 - 1
fs-service-system/src/main/java/com/fs/fastGpt/service/AiHookService.java

@@ -28,7 +28,7 @@ public interface AiHookService {
      * @param uuid     UUID
      * @param sendType 发送者类型 1用户 2客服
      * @param json     消息json
-     * @param msgType  消息类型 1文本 2图片 3动态表情 4语音
+     * @param msgType  消息类型 1文本 2图片 3动态表情 4语音 5小程序
      */
     QwMessageListVO saveQwMsg(Long qwUserId, Long userId, String content, String uuid, int sendType, String json, int msgType);
 

+ 5 - 6
fs-service-system/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -26,6 +26,7 @@ import com.fs.fastgptApi.service.ChatService;
 import com.fs.fastgptApi.service.Impl.AudioServiceImpl;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.qw.domain.*;
+import com.fs.qw.enums.MsgType;
 import com.fs.qw.mapper.*;
 import com.fs.qw.service.*;
 import com.fs.qw.vo.QwMessageListVO;
@@ -1345,13 +1346,11 @@ public class AiHookServiceImpl implements AiHookService {
 
         listVO.setCompanyId(qwUser.getCompanyId());
         String type = "text";
-        if (msgType == 2) {
-            type = "image";
-        } else if (msgType == 3) {
-            type = "emotionDynamic";
-        } else if (msgType == 4) {
-            type = "voice";
+        MsgType messageType = MsgType.getMsgType(msgType);
+        if (Objects.nonNull(messageType)){
+            type = messageType.getValue();
         }
+
         listVO.setType(type);
         listVO.setStatus("succeed");
         listVO.setExtId(qwMsg.getQwExtId());

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

@@ -77,4 +77,10 @@ public class QwCompany extends BaseEntity
     private String msgSecret;
 
     private String msgPrivateKey;
+
+    /** 小程序ID */
+    private String miniAppId;
+
+    /** 小程序消息发送参数 */
+    private String miniAppParam;
 }

+ 27 - 0
fs-service-system/src/main/java/com/fs/qw/enums/MsgType.java

@@ -0,0 +1,27 @@
+package com.fs.qw.enums;
+
+import lombok.Getter;
+
+import java.util.stream.Stream;
+
+@Getter
+public enum MsgType {
+    TEXT(1, "text"),
+    IMAGE(2, "image"),
+    EMOTION_DYNAMIC(3, "emotionDynamic"),
+    VOICE(4, "voice"),
+    MINI_PROGRAM(5, "miniprogram"),
+    ;
+
+    private final Integer code;
+    private final String value;
+
+    MsgType(Integer code, String value) {
+        this.code = code;
+        this.value = value;
+    }
+
+    public static MsgType getMsgType(Integer code) {
+        return Stream.of(values()).filter(t -> t.getCode().equals(code)).findFirst().orElse(null);
+    }
+}

+ 7 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwCompanyMapper.java

@@ -2,7 +2,9 @@ package com.fs.qw.mapper;
 
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.vo.QwOptionsVO;
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
 
 import java.util.List;
 
@@ -69,4 +71,9 @@ public interface QwCompanyMapper
     @Select("SELECT * from qw_company ")
     List<QwOptionsVO> selectQwCompanyListOptionsVO();
 
+    @Select("select count(1) from qw_company where mini_app_id = #{appid} and mini_app_param is null")
+    int countNotParamByMiniAppId(@Param("appid") String appid);
+
+    @Update("update qw_company set mini_app_param = #{json} where mini_app_id = #{appid}")
+    void updateParamByMiniAppId(@Param("appid") String appid, @Param("json") String json);
 }

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

@@ -374,4 +374,11 @@ public interface QwUserMapper extends BaseMapper<QwUser>
     @Select("select qw_user_id from qw_user where company_user_id = ${companyUserId}")
     List<String> findQwUserIdListByCompanyUserId(@Param("companyUserId") Long companyUserId);
 
+    /**
+     * 根据企微用户id查询对应得销售id
+     * @param qwUserId
+     * @return
+     */
+    @Select("select company_user_id from qw_user where qw_user_id=${qwUserId}")
+    Long selectCompanyIdByQwUserId(@Param("qwUserId") String qwUserId);
 }

+ 1 - 1
fs-service-system/src/main/java/com/fs/qw/param/QwMsgSendParam.java

@@ -9,6 +9,6 @@ public class QwMsgSendParam implements Serializable {
     private Long sessionId;
     private String content;//内容
     private String appKey;
-    // 消息类型 1文本 2图片
+    // 消息类型 1文本 2图片 3动态表情 4语音 5小程序
     private Integer msgType;
 }

+ 6 - 1
fs-service-system/src/main/java/com/fs/qw/service/IQwCompanyService.java

@@ -1,5 +1,6 @@
 package com.fs.qw.service;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.vo.QwOptionsVO;
 
@@ -64,5 +65,9 @@ public interface IQwCompanyService
 
     List<QwOptionsVO> selectQwCompanyListOptionsVO();
 
-
+    /**
+     * 保存小程序消息发送参数
+     * @param params    参数
+     */
+    void saveSendParams(JSONObject params);
 }

+ 28 - 0
fs-service-system/src/main/java/com/fs/qw/service/impl/QwCompanyServiceImpl.java

@@ -1,14 +1,18 @@
 package com.fs.qw.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.vo.QwOptionsVO;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.Objects;
 
 /**
  * 企微主体Service业务层处理
@@ -16,6 +20,7 @@ import java.util.List;
  * @author fs
  * @date 2024-10-09
  */
+@Slf4j
 @Service
 public class QwCompanyServiceImpl implements IQwCompanyService
 {
@@ -105,4 +110,27 @@ public class QwCompanyServiceImpl implements IQwCompanyService
     public List<QwOptionsVO> selectQwCompanyListOptionsVO() {
         return qwCompanyMapper.selectQwCompanyListOptionsVO();
     }
+
+    /**
+     * 保存小程序消息发送参数
+     * @param params    参数
+     */
+    @Override
+    public void saveSendParams(JSONObject params) {
+        if (Objects.isNull(params)) {
+            log.warn("保存小程序消息发送参数 参数为空");
+            return;
+        }
+
+        String appid = params.getString("appid");
+        if (StringUtils.isBlank(appid)) {
+            log.warn("保存小程序消息发送参数 appid为空");
+            return;
+        }
+
+        int count = qwCompanyMapper.countNotParamByMiniAppId(appid);
+        if (count > 0) {
+            qwCompanyMapper.updateParamByMiniAppId(appid, params.toString());
+        }
+    }
 }

+ 61 - 20
fs-service-system/src/main/java/com/fs/qw/service/impl/QwMsgServiceImpl.java

@@ -12,10 +12,8 @@ import com.fs.his.config.FsSysConfig;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.qw.Bean.MsgBean;
 import com.fs.qw.domain.*;
-import com.fs.qw.mapper.QwExternalContactMapper;
-import com.fs.qw.mapper.QwMsgMapper;
-import com.fs.qw.mapper.QwSessionMapper;
-import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.enums.MsgType;
+import com.fs.qw.mapper.*;
 import com.fs.qw.param.QwMsgSendParam;
 import com.fs.qw.param.QwSessionParam;
 import com.fs.qw.service.IQwMsgService;
@@ -49,6 +47,8 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
     private QwExternalContactMapper qwExternalContactMapper;
     @Autowired
     private QwUserMapper qwUserMapper;
+    @Autowired
+    private QwCompanyMapper qwCompanyMapper;
 
     @Autowired
     private ConfigUtil configUtil;
@@ -295,8 +295,9 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
         long sendUserId = listWxWorkResponseDTO.getData().get(0).getUser_id();
         String msgJson;
 
+        MsgType msgType = MsgType.getMsgType(param.getMsgType());
         // 发送消息  文本
-        if (param.getMsgType() == 1) {
+        if (MsgType.TEXT == msgType) {
             WxWorkSendTextMsgDTO textMsgDTO = new WxWorkSendTextMsgDTO();
             textMsgDTO.setUuid(uuid);
             textMsgDTO.setSend_userid(sendUserId);
@@ -311,7 +312,7 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
         }
 
         // 图片
-        else if (param.getMsgType() == 2) {
+        else if (MsgType.IMAGE == msgType) {
             WxCdnUploadImgLinkDTO linkDTO = new WxCdnUploadImgLinkDTO();
             linkDTO.setUuid(uuid);
             linkDTO.setUrl(param.getContent());
@@ -335,6 +336,53 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
                 return R.error(imgMsgResp.getErrmsg());
             }
             msgJson = JSONObject.toJSONString(imgMsgDTO);
+        }
+        // 小程序
+        else if (MsgType.MINI_PROGRAM == msgType) {
+            QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(qwUser.getCorpId());
+            if (Objects.isNull(qwCompany)) {
+                return R.error("企微主体不存在");
+            }
+
+            String miniAppParam = qwCompany.getMiniAppParam();
+            if (StringUtils.isBlank(miniAppParam)) {
+                return R.error("配置信息不存在");
+            }
+
+            String pagepath = param.getContent();
+            JSONObject paramJson = JSONObject.parseObject(miniAppParam);
+
+            // 发送小程序消息
+            WxWorkSendAppMsgDTO appMsgDTO = new WxWorkSendAppMsgDTO();
+            appMsgDTO.setUuid(uuid);
+            appMsgDTO.setSend_userid(sendUserId);
+            appMsgDTO.setDesc(paramJson.getString("title"));
+            appMsgDTO.setTitle(paramJson.getString("appName"));
+            appMsgDTO.setWeappIconUrl(paramJson.getString("weappIconUrl"));
+            appMsgDTO.setPagepath(pagepath);
+            appMsgDTO.setUsername(paramJson.getString("username"));
+            appMsgDTO.setAppid(paramJson.getString("appid"));
+            appMsgDTO.setCdnkey(paramJson.getString("cdnkey"));
+            appMsgDTO.setMd5(paramJson.getString("md5"));
+            appMsgDTO.setAeskey(paramJson.getString("aeskey"));
+            appMsgDTO.setFileSize(paramJson.getInteger("fileSize"));
+            appMsgDTO.setIsRoom(false);
+            WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> appMsgResp = wxWorkService.SendAppMsg(appMsgDTO, serverId);
+            if (appMsgResp.getErrcode() != 0) {
+                return R.error(appMsgResp.getErrmsg());
+            }
+
+            JSONObject json = new JSONObject();
+            json.put("appid", paramJson.getString("appid"));
+            json.put("appName", paramJson.getString("appName"));
+            json.put("weappIconUrl", paramJson.getString("weappIconUrl"));
+            json.put("desc", paramJson.getString("desc"));
+            json.put("pagepath", pagepath);
+            json.put("title", paramJson.getString("title"));
+            json.put("thumbnail", paramJson.getString("thumbnail"));
+
+            msgJson = json.toJSONString();
+            param.setContent(msgJson);
         } else {
             return R.error("暂不支持的消息类型");
         }
@@ -362,14 +410,7 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
         qwFromUser.setId(Long.parseLong(qwMsg.getQwUserId()));
         qwFromUser.setDisplayName(qwUser.getQwUserName());
         qwFromUser.setAvatar("https://cos.his.cdwjyyh.com/fs/20241231/22a765a96da247d1b83ea94fef438a41.png");
-
-        String type = "text";
-        if (param.getMsgType() == 2) {
-            type = "image";
-        } else if (param.getMsgType() == 3) {
-            type = "emotionDynamic";
-        }
-        listVO.setType(type);
+        listVO.setType(msgType.getValue());
         listVO.setStatus("succeed");
         listVO.setFromUser(qwFromUser);
         listVO.setSendTime(qwMsg.getCreateTime().getTime());
@@ -425,6 +466,7 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
         for (QwSession qwSession : qwSessions) {
             QwContactListVO listVO = new QwContactListVO();
             listVO.setId(userId);
+            listVO.setExtId(qwSession.getQwExtId());
             listVO.setAvatar(qwSession.getAvatar());
             listVO.setConversationId(qwSession.getSessionId().toString());
             listVO.setDisplayName(qwSession.getNickName());
@@ -450,6 +492,8 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
                 listVO.setType("emotionDynamic");
             } else if (qwMsg.getMsgType() == 4) {
                 listVO.setType("voice");
+            } else if (qwMsg.getMsgType() == 5) {
+                listVO.setType("miniprogram");
             }
             listVO.setMsgId(qwMsg.getMsgId());
             listVO.setLastContent(qwMsgs.get(0).getContent());
@@ -480,12 +524,9 @@ public class QwMsgServiceImpl extends ServiceImpl<QwMsgMapper, QwMsg> implements
         for (QwMsg record : list) {
             QwMessageListVO listVO = new QwMessageListVO();
             String type = "text";
-            if (record.getMsgType() == 2) {
-                type = "image";
-            } else if (record.getMsgType() == 3) {
-                type = "emotionDynamic";
-            } else if (record.getMsgType() == 4) {
-                type = "voice";
+            MsgType msgType = MsgType.getMsgType(record.getMsgType());
+            if (Objects.nonNull(record.getMsgType())) {
+                type = msgType.getValue();
             }
             listVO.setType(type);
             listVO.setStatus("succeed");

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

@@ -18,4 +18,6 @@ public class QwContactListVO {
     private Long msgId;
     // 消息类型
     private String type;
+    // 外部联系人ID
+    private String extId;
 }

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

@@ -294,4 +294,6 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
                                         @Param("periodId") String periodId,
                                         @Param("previousDay") LocalDate previousDay);
 
+    @DataSource(DataSourceType.SOP)
+    String queryPeriodNameById(@Param("periodId") String periodId);
 }

+ 13 - 15
fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java

@@ -36,6 +36,7 @@ import com.fs.voice.utils.StringUtil;
 import com.fs.wxUser.mapper.CompanyWxUserMapper;
 import com.fs.wxUser.param.CompanyWxUserSopParam;
 import org.apache.commons.collections.ArrayStack;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -112,30 +113,27 @@ public class QwSopServiceImpl implements IQwSopService
 
         for (QwSop qwSop : qwSopList) {
             QwSopTask qwSopTask = new QwSopTask();
-            qwSopTask.setTaskId(qwSop.getId());
-            qwSopTask.setTaskName(qwSop.getName());
+            qwSopTask.setId(qwSop.getId());
+            qwSopTask.setLabel(qwSop.getName());
 
-            List<QwSopTaskDetail> listData = new ArrayList<>();
+            List<QwSopTask> listData = new ArrayList<>();
             // 获取对应SOP任务执行记录
             List<SopUserLogs> sopUserLogs = sopUserLogsMapper.queryExecuteLogBySopId(qwSop.getId());
             for (SopUserLogs sopUserLog : sopUserLogs) {
-                QwSopTaskDetail qwSopTaskDetail = new QwSopTaskDetail();
-                qwSopTaskDetail.setTaskId(sopUserLog.getId());
-                qwSopTaskDetail.setQwUserId(sopUserLog.getQwUserId());
-                qwSopTaskDetail.setTaskExecDate(sopUserLog.getStartTime());
-
-                long daysDifferenceFromNow = DateUtils.getDaysDifferenceFromNow(DateUtils.dateTime("yyyy-MM-dd", sopUserLog.getStartTime()));
-
-                qwSopTaskDetail.setDays(daysDifferenceFromNow);
-
-                listData.add(qwSopTaskDetail);
+                QwSopTask child = new QwSopTask();
+                child.setLabel(sopUserLog.getQwUserId()+"_"+sopUserLog.getStartTime());
+                child.setId(sopUserLog.getId());
+                listData.add(child);
             }
-            qwSopTask.setTaskDetailList(listData);
+            qwSopTask.setChildren(listData);
 
             qwSopTaskList.add(qwSopTask);
         }
+        if(CollectionUtils.isEmpty(qwSopTaskList)){
+            return Collections.emptyList();
+        }
 
-        return qwSopTaskList;
+        return qwSopTaskList.stream().filter(e-> CollectionUtils.isNotEmpty(e.getChildren())).collect(Collectors.toList());
     }
 
     /**

+ 3 - 3
fs-service-system/src/main/java/com/fs/sop/vo/QwSopTask.java

@@ -14,15 +14,15 @@ public class QwSopTask implements Serializable {
     /**
      * 任务id
      */
-    private String taskId;
+    private String id;
 
     /**
      * 任务名称
      */
-    private String taskName;
+    private String label;
 
     /**
      * 任务详情
      */
-    private List<QwSopTaskDetail> taskDetailList;
+    private List<QwSopTask> children;
 }

+ 7 - 2
fs-service-system/src/main/java/com/fs/statis/domain/FsStatisPeriodWatch.java

@@ -1,9 +1,13 @@
 package com.fs.statis.domain;
 
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
+
+import java.time.LocalDate;
 import java.util.Date; // 或者使用 java.time.LocalDateTime
 
 /**
@@ -21,12 +25,13 @@ public class FsStatisPeriodWatch {
     /**
      * 主键ID (虽然注释中没有,但通常id为主键)
      */
+    @TableId(type = IdType.AUTO)
     private Integer id;
 
     /**
      * 训练营id
      */
-    private Integer periodId;
+    private String periodId;
 
     /**
      * 训练营人数
@@ -96,7 +101,7 @@ public class FsStatisPeriodWatch {
     /**
      * 数据日期
      */
-    private Date dataDate;
+    private LocalDate dataDate;
 
 
     /**

+ 8 - 0
fs-service-system/src/main/java/com/fs/statis/domain/FsStatisSalerWatch.java

@@ -57,6 +57,10 @@ public class FsStatisSalerWatch {
      * 完课人数
      */
     private Long completedNum;
+    /**
+     * 看课中断数
+     */
+    private Long interruptNum;
 
     /**
      * 报名率
@@ -113,6 +117,10 @@ public class FsStatisSalerWatch {
      */
     private String periodId;
 
+    /**
+     * 营期名称
+     */
+    private String periodName;
     /**
      * 数据日期
      */

+ 1 - 1
fs-service-system/src/main/java/com/fs/statis/mapper/FsStatisPeriodWatchMapper.java

@@ -103,5 +103,5 @@ public interface FsStatisPeriodWatchMapper {
      * 获取每个sop任务对应的营期记录
      * @return
      */
-    List<String> selectRecords(@Param("previousDay") LocalDate previousDay);
+    List<SopUserLogs> selectRecords(@Param("previousDay") LocalDate previousDay);
 }

+ 5 - 0
fs-service-system/src/main/java/com/fs/statis/mapper/FsStatisSalerWatchMapper.java

@@ -83,4 +83,9 @@ public interface FsStatisSalerWatchMapper {
     List<FsStatisSalerWatch> queryList(StatsWatchLogPageListDTO param);
 
     void batchSave(@Param("list") List<FsStatisSalerWatch> writeData);
+
+
+    List<FsStatisSalerWatch> queryPeriodList(StatsWatchLogPageListDTO param);
+    List<FsStatisSalerWatch> queryTodayList(StatsWatchLogPageListDTO param);
+
 }

+ 0 - 47
fs-service-system/src/main/java/com/fs/statis/service/FsStatisEveryDayWatchService.java

@@ -1,47 +0,0 @@
-package com.fs.statis.service;
-
-import com.fs.statis.domain.FsStatisEveryDayWatch;
-import com.fs.statis.domain.FsStatisSalerWatch;
-import com.fs.statis.dto.StatsWatchLogPageListDTO;
-
-import java.util.List;
-
-/**
- * 每日统计数据服务接口
- */
-public interface FsStatisEveryDayWatchService {
-
-    /**
-     * 根据主键ID查询每日统计数据
-     *
-     * @param id 主键ID
-     * @return 每日统计数据对象,如果不存在则返回null
-     */
-    FsStatisEveryDayWatch findById(Integer id);
-
-    /**
-     * 新增每日统计数据
-     *
-     * @param fsStatisEveryDayWatch 待插入的每日统计数据对象
-     * @return 影响的行数,通常是1表示成功
-     */
-    int create(FsStatisEveryDayWatch fsStatisEveryDayWatch);
-
-    /**
-     * 更新每日统计数据
-     *
-     * @param fsStatisEveryDayWatch 待更新的每日统计数据对象 (必须包含ID)
-     * @return 影响的行数,通常是1表示成功,0表示未找到对应记录
-     */
-    int update(FsStatisEveryDayWatch fsStatisEveryDayWatch);
-
-    /**
-     * 查询所有每日统计数据
-     *
-     * @return 每日统计数据列表
-     */
-    List<FsStatisEveryDayWatch> findAll();
-
-    List<FsStatisSalerWatch> queryList(StatsWatchLogPageListDTO param);
-
-}

+ 0 - 58
fs-service-system/src/main/java/com/fs/statis/service/FsStatisPeriodWatchService.java

@@ -1,58 +0,0 @@
-package com.fs.statis.service; // 假设Service接口放在此包下
-
-import com.fs.statis.domain.FsStatisPeriodWatch;
-import com.fs.statis.dto.StatsWatchLogPageListDTO;
-
-import java.util.List;
-
-/**
- * 训练营周期统计数据服务接口
- * 对应表 fs_statis_period_watch
- */
-public interface FsStatisPeriodWatchService {
-
-    /**
-     * 根据主键ID查询训练营周期统计数据
-     *
-     * @param id 主键ID
-     * @return 训练营周期统计数据对象,如果不存在则返回null
-     */
-    FsStatisPeriodWatch findById(Integer id);
-
-    /**
-     * 新增训练营周期统计数据
-     *
-     * @param fsStatisPeriodWatch 待插入的训练营周期统计数据对象
-     * @return 影响的行数,通常是1表示成功
-     */
-    int create(FsStatisPeriodWatch fsStatisPeriodWatch);
-
-    /**
-     * 根据主键更新训练营周期统计数据
-     * (只会更新实体中非null的字段)
-     *
-     * @param fsStatisPeriodWatch 待更新的训练营周期统计数据对象 (必须包含ID)
-     * @return 影响的行数,通常是1表示成功,0表示未找到对应记录或未更新任何字段
-     */
-    int updateById(FsStatisPeriodWatch fsStatisPeriodWatch);
-
-    /**
-     * 根据主键ID删除训练营周期统计数据
-     *
-     * @param id 主键ID
-     * @return 影响的行数
-     */
-    int deleteById(Integer id);
-
-    /**
-     * 查询所有训练营周期统计数据
-     *
-     * @return 训练营周期统计数据列表
-     */
-    List<FsStatisPeriodWatch> findAll();
-
-    List<FsStatisPeriodWatch> queryList(StatsWatchLogPageListDTO param);
-
-    void writeData();
-
-}

+ 7 - 0
fs-service-system/src/main/java/com/fs/statis/service/FsStatisSalerWatchService.java

@@ -50,10 +50,17 @@ public interface FsStatisSalerWatchService {
     boolean deleteById(Integer id);
 
     List<FsStatisSalerWatch> queryList(StatsWatchLogPageListDTO param);
+    List<FsStatisSalerWatch> queryPeriodList(StatsWatchLogPageListDTO param);
+    List<FsStatisSalerWatch> queryTodayList(StatsWatchLogPageListDTO param);
 
 
     /**
      * 写入数据 写入前一天的数据
      */
     void writeData();
+
+    /**
+     * 计算每天的数据
+     */
+    void writeDataToday();
 }

+ 0 - 71
fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisEveryDayWatchServiceImpl.java

@@ -1,71 +0,0 @@
-package com.fs.statis.service.impl;
-
-import com.fs.statis.domain.FsStatisEveryDayWatch;
-import com.fs.statis.domain.FsStatisSalerWatch;
-import com.fs.statis.dto.StatsWatchLogPageListDTO;
-import com.fs.statis.mapper.FsStatisEveryDayWatchMapper;
-import com.fs.statis.service.FsStatisEveryDayWatchService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional; // 引入事务注解
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * 每日统计数据服务实现类
- */
-@Service
-public class FsStatisEveryDayWatchServiceImpl implements FsStatisEveryDayWatchService {
-
-    private final FsStatisEveryDayWatchMapper fsStatisEveryDayWatchMapper;
-
-    /**
-     * 通过构造函数注入Mapper
-     * @param fsStatisEveryDayWatchMapper 每日统计数据Mapper
-     */
-    @Autowired
-    public FsStatisEveryDayWatchServiceImpl(FsStatisEveryDayWatchMapper fsStatisEveryDayWatchMapper) {
-        this.fsStatisEveryDayWatchMapper = fsStatisEveryDayWatchMapper;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public FsStatisEveryDayWatch findById(Integer id) {
-        return fsStatisEveryDayWatchMapper.findById(id);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @Transactional
-    public int create(FsStatisEveryDayWatch fsStatisEveryDayWatch) {
-        return fsStatisEveryDayWatchMapper.insert(fsStatisEveryDayWatch);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @Transactional
-    public int update(FsStatisEveryDayWatch fsStatisEveryDayWatch) {
-        return fsStatisEveryDayWatchMapper.update(fsStatisEveryDayWatch);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public List<FsStatisEveryDayWatch> findAll() {
-        return fsStatisEveryDayWatchMapper.findAll();
-    }
-
-    @Override
-    public List<FsStatisSalerWatch> queryList(StatsWatchLogPageListDTO param) {
-        return fsStatisEveryDayWatchMapper.queryList(param);
-    }
-
-}

+ 0 - 102
fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisPeriodWatchServiceImpl.java

@@ -1,102 +0,0 @@
-package com.fs.statis.service.impl; // 假设Service实现类放在此包下
-
-import com.fs.company.domain.CompanyUser;
-import com.fs.sop.domain.SopUserLogs;
-import com.fs.statis.domain.FsStatisPeriodWatch;
-import com.fs.statis.domain.FsStatisSalerWatch;
-import com.fs.statis.dto.StatsWatchLogPageListDTO;
-import com.fs.statis.mapper.FsStatisPeriodWatchMapper;
-import com.fs.statis.service.FsStatisPeriodWatchService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * 训练营周期统计数据服务实现类
- */
-@Service
-public class FsStatisPeriodWatchServiceImpl implements FsStatisPeriodWatchService {
-
-    private final FsStatisPeriodWatchMapper fsStatisPeriodWatchMapper;
-
-    /**
-     * 通过构造函数注入Mapper
-     * @param fsStatisPeriodWatchMapper 训练营周期统计数据Mapper
-     */
-    @Autowired
-    public FsStatisPeriodWatchServiceImpl(FsStatisPeriodWatchMapper fsStatisPeriodWatchMapper) {
-        this.fsStatisPeriodWatchMapper = fsStatisPeriodWatchMapper;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public FsStatisPeriodWatch findById(Integer id) {
-        return fsStatisPeriodWatchMapper.selectById(id);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @Transactional // 标记此方法需要事务管理
-    public int create(FsStatisPeriodWatch fsStatisPeriodWatch) {
-        // 在这里可以添加业务逻辑,例如参数校验等
-        return fsStatisPeriodWatchMapper.insert(fsStatisPeriodWatch);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @Transactional // 标记此方法需要事务管理
-    public int updateById(FsStatisPeriodWatch fsStatisPeriodWatch) {
-        // 在这里可以添加业务逻辑,例如检查记录是否存在,或在更新前进行特定校验
-        // 注意:Mapper中的updateById是动态SQL,只更新非null字段
-        return fsStatisPeriodWatchMapper.updateById(fsStatisPeriodWatch);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @Transactional // 标记此方法需要事务管理
-    public int deleteById(Integer id) {
-        // 在这里可以添加业务逻辑,例如检查关联数据等
-        return fsStatisPeriodWatchMapper.deleteById(id);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public List<FsStatisPeriodWatch> findAll() {
-        return fsStatisPeriodWatchMapper.selectAll();
-    }
-
-    @Override
-    public List<FsStatisPeriodWatch> queryList(StatsWatchLogPageListDTO param) {
-        return fsStatisPeriodWatchMapper.queryList(param);
-    }
-
-    @Override
-    public void writeData() {
-        LocalDate previousDay = LocalDate.now().minusDays(1);
-
-        // 获取每个sop任务对应的营期记录(执行记录) 当天的记录
-        List<String> list = fsStatisPeriodWatchMapper.selectRecords(previousDay);
-        for (String id : list) {
-            // 根据sop营期id 获取当前任务的企微id列表
-
-            // 转换企微id列表为对应的销售
-
-        }
-
-    }
-}

+ 145 - 11
fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisSalerWatchServiceImpl.java

@@ -25,6 +25,7 @@ import org.springframework.stereotype.Service;
 
 import java.time.LocalDate;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -142,6 +143,71 @@ public class FsStatisSalerWatchServiceImpl implements FsStatisSalerWatchService
                     item.setCompanyUserName(companyUserName);
                 }
             }
+
+            if(item.getPeriodId() != null) {
+                String periodName = qwSopLogsMapper.queryPeriodNameById(item.getPeriodId());
+                if(StringUtils.isNotEmpty(periodName)) {
+                    item.setPeriodName(periodName);
+                }
+            }
+
+
+        }
+        return fsStatisSalerWatches;
+    }
+
+    @Override
+    public List<FsStatisSalerWatch> queryPeriodList(StatsWatchLogPageListDTO param) {
+        List<FsStatisSalerWatch> fsStatisSalerWatches = fsStatisSalerWatchMapper.queryPeriodList(param);
+        for (FsStatisSalerWatch item : fsStatisSalerWatches) {
+            if(item.getDeptId() != null) {
+                CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(item.getDeptId());
+                if(ObjectUtils.isNotNull(companyDept)) {
+                    item.setDeptName(companyDept.getDeptName());
+                }
+            }
+            if(item.getCompanyUserId() != null) {
+                String companyUserName = companyUserCacheService.selectCompanyUserNameUserById(item.getCompanyUserId());
+                if(StringUtils.isNotEmpty(companyUserName)){
+                    item.setCompanyUserName(companyUserName);
+                }
+            }
+
+            if(item.getPeriodId() != null) {
+                String periodName = qwSopLogsMapper.queryPeriodNameById(item.getPeriodId());
+                if(StringUtils.isNotEmpty(periodName)) {
+                    item.setPeriodName(periodName);
+                }
+            }
+
+        }
+        return fsStatisSalerWatches;
+    }
+
+    @Override
+    public List<FsStatisSalerWatch> queryTodayList(StatsWatchLogPageListDTO param) {
+        List<FsStatisSalerWatch> fsStatisSalerWatches = fsStatisSalerWatchMapper.queryTodayList(param);
+        for (FsStatisSalerWatch item : fsStatisSalerWatches) {
+            if(item.getDeptId() != null) {
+                CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(item.getDeptId());
+                if(ObjectUtils.isNotNull(companyDept)) {
+                    item.setDeptName(companyDept.getDeptName());
+                }
+            }
+            if(item.getCompanyUserId() != null) {
+                String companyUserName = companyUserCacheService.selectCompanyUserNameUserById(item.getCompanyUserId());
+                if(StringUtils.isNotEmpty(companyUserName)){
+                    item.setCompanyUserName(companyUserName);
+                }
+            }
+
+            if(item.getPeriodId() != null) {
+                String periodName = qwSopLogsMapper.queryPeriodNameById(item.getPeriodId());
+                if(StringUtils.isNotEmpty(periodName)) {
+                    item.setPeriodName(periodName);
+                }
+            }
+
         }
         return fsStatisSalerWatches;
     }
@@ -171,31 +237,32 @@ public class FsStatisSalerWatchServiceImpl implements FsStatisSalerWatchService
             // 确定当前销售对应的sop任务的执行记录
             List<String> periodList = sopUserLogsMapper.selectSopUserLogsByQwUserIds(qwUserIdList);
 
+            if(CollectionUtils.isEmpty(periodList)){
+                log.info("当前销售 {} 没有相关的SOP营期!", companyUser.getUserId());
+                continue;
+            }
+
             for (String periodId : periodList) {
                 // 去sop记录表找对应的SOP发送记录,记录数作为营期人数
                 Long periodCount = qwSopLogsMapper.selectQwSopLogsCountByQwUserId(qwUserIdList,periodId,previousDay);
                 // 再去course_watch_log找对应的销售观看记录作为已报名数
                 Long registerCount = companyUserMapper.queryCompanyUserWatchCount(companyUser.getUserId(),previousDay);
                 Long completedCount = companyUserMapper.queryCompanyUserWatchCountCompleted(companyUser.getUserId(),previousDay);
-
+                // 当前销售当天的看课中断数量
+                Long interruptCount = companyUserMapper.queryCompanyUserInterruptCount(companyUser.getUserId(),previousDay);
 
                 FsStatisSalerWatch fsStatisSalerWatch = new FsStatisSalerWatch();
                 fsStatisSalerWatch.setDeptId(companyUser.getDeptId());
                 fsStatisSalerWatch.setCompanyUserId(companyUser.getUserId());
                 fsStatisSalerWatch.setTrainCampNum(periodCount);
-                fsStatisSalerWatch.setNotRegisteredNum(periodCount - registerCount);
+                fsStatisSalerWatch.setNotRegisteredNum(Math.max(0, periodCount - registerCount));
                 fsStatisSalerWatch.setRegisteredNum(registerCount);
                 fsStatisSalerWatch.setCompletedNum(completedCount);
+                fsStatisSalerWatch.setInterruptNum(interruptCount);
 
-                float regRate = 0.0f;
-                if(periodCount != 0) {
-                    regRate = registerCount / periodCount;
-                }
-                float finishedRate = 0.0f;
-                if(registerCount != 0) {
-                    finishedRate = completedCount / registerCount;
-                }
-
+                // 修复比率计算
+                float regRate = (periodCount > 0) ? (float) registerCount / periodCount : 0.0f;
+                float finishedRate = (registerCount > 0) ? (float) completedCount / registerCount : 0.0f;
 
                 fsStatisSalerWatch.setRegRate(regRate);
                 fsStatisSalerWatch.setFinishedRate(finishedRate);
@@ -211,4 +278,71 @@ public class FsStatisSalerWatchServiceImpl implements FsStatisSalerWatchService
             fsStatisSalerWatchMapper.batchSave(writeData);
         }
     }
+
+    @Override
+    public void writeDataToday() {
+        // 统计销售看课情况
+        // 获取前一天的时间
+        List<CompanyUser> companyUserList = this.companyUserMapper.selectAllCompanyUserList();
+
+        List<FsStatisSalerWatch> writeData = new ArrayList<>();
+
+        LocalDate nowDay = LocalDate.now();
+        for (CompanyUser companyUser : companyUserList) {
+
+            if(companyUser.getCompanyId() == null) {
+                log.info("销售{} 对应公司id {} 为空!",companyUser.getUserId(),companyUser.getCompanyId());
+                continue;
+            }
+            // 找到销售关联的 企微账号
+            List<String> qwUserIdList = qwUserMapper.findQwUserIdListByCompanyUserId(companyUser.getUserId());
+            if(CollectionUtils.isEmpty(qwUserIdList)){
+                log.info("当前销售 {} 没有关联企微账号!",companyUser.getUserId());
+                continue;
+            }
+
+            // 确定当前销售对应的sop任务的执行记录
+            List<String> periodList = sopUserLogsMapper.selectSopUserLogsByQwUserIds(qwUserIdList);
+
+            if(CollectionUtils.isEmpty(periodList)){
+                log.info("当前销售 {} 没有相关的SOP营期!", companyUser.getUserId());
+                continue;
+            }
+
+            for (String periodId : periodList) {
+                // 去sop记录表找对应的SOP发送记录,记录数作为营期人数
+                Long periodCount = qwSopLogsMapper.selectQwSopLogsCountByQwUserId(qwUserIdList,periodId,nowDay);
+                // 再去course_watch_log找对应的销售观看记录作为已报名数
+                Long registerCount = companyUserMapper.queryCompanyUserWatchCount(companyUser.getUserId(),nowDay);
+                Long completedCount = companyUserMapper.queryCompanyUserWatchCountCompleted(companyUser.getUserId(),nowDay);
+                // 当前销售当天的看课中断数量
+                Long interruptCount = companyUserMapper.queryCompanyUserInterruptCount(companyUser.getUserId(),nowDay);
+
+                FsStatisSalerWatch fsStatisSalerWatch = new FsStatisSalerWatch();
+                fsStatisSalerWatch.setDeptId(companyUser.getDeptId());
+                fsStatisSalerWatch.setCompanyUserId(companyUser.getUserId());
+                fsStatisSalerWatch.setTrainCampNum(periodCount);
+                fsStatisSalerWatch.setNotRegisteredNum(Math.max(0, periodCount - registerCount));
+                fsStatisSalerWatch.setRegisteredNum(registerCount);
+                fsStatisSalerWatch.setCompletedNum(completedCount);
+                fsStatisSalerWatch.setInterruptNum(interruptCount);
+
+                // 修复比率计算
+                float regRate = (periodCount > 0) ? (float) registerCount / periodCount : 0.0f;
+                float finishedRate = (registerCount > 0) ? (float) completedCount / registerCount : 0.0f;
+
+                fsStatisSalerWatch.setRegRate(regRate);
+                fsStatisSalerWatch.setFinishedRate(finishedRate);
+                fsStatisSalerWatch.setDataDate(nowDay);
+
+                fsStatisSalerWatch.setPeriodId(periodId);
+
+                writeData.add(fsStatisSalerWatch);
+            }
+        }
+
+        if(CollectionUtils.isNotEmpty(writeData)){
+            fsStatisSalerWatchMapper.batchSave(writeData);
+        }
+    }
 }

+ 1 - 1
fs-service-system/src/main/java/com/fs/store/service/impl/FsStorePaymentServiceImpl.java

@@ -548,7 +548,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService
                 return WxPayNotifyResponse.fail("");
             }
         } catch (WxPayException e) {
-            logger.error("zyp \n【转账回调异常】:{}", e.getReturnMsg());
+            logger.error("zyp \n【转账回调异常】:{}", e.getReturnMsg(),e);
             return WxPayNotifyResponse.fail(e.getMessage());
         }
     }

+ 13 - 0
fs-service-system/src/main/java/com/fs/wxwork/dto/WxWorkMessageDTO.java

@@ -33,4 +33,17 @@ public class WxWorkMessageDTO {
     // 语音
     private String voice_id;
     private Integer voice_size;
+
+    // 小程序
+    private String appid;
+    private String appName;
+    private String username;
+    private String desc;
+    private String weappIconUrl;
+    private String thumbFileId;
+    private String thumbMD5;
+    private String thumbAESKey;
+    private Integer size;
+    private String title;
+    private String pagepath;
 }

+ 1 - 1
fs-service-system/src/main/java/com/fs/wxwork/dto/WxWorkSendAppMsgDTO.java

@@ -11,7 +11,7 @@ public class WxWorkSendAppMsgDTO {
     /**
      * 发送者用户 ID
      */
-    private Long sendUserid;
+    private Long send_userid;
     /**
      * 消息描述
      */

+ 1 - 1
fs-service-system/src/main/java/com/fs/wxwork/service/WxWorkService.java

@@ -36,7 +36,7 @@ public interface WxWorkService {
      * @param param 参数
      * @return QwWorkResponseDTO
      */
-    WxWorkSendAppMsgRespDTO SendAppMsg(WxWorkSendAppMsgDTO param,Long serverId);
+    WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> SendAppMsg(WxWorkSendAppMsgDTO param,Long serverId);
 
     /**
      * 发送链接消息

+ 2 - 2
fs-service-system/src/main/java/com/fs/wxwork/service/WxWorkServiceImpl.java

@@ -66,9 +66,9 @@ public class WxWorkServiceImpl implements WxWorkService {
     }
 
     @Override
-    public WxWorkSendAppMsgRespDTO SendAppMsg(WxWorkSendAppMsgDTO param,Long serverId) {
+    public WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> SendAppMsg(WxWorkSendAppMsgDTO param,Long serverId) {
         String url = getUrl(serverId) + "/SendAppMsg";
-        return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkSendAppMsgRespDTO>() {
+        return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxWorkSendAppMsgRespDTO>>() {
         });
     }
 

+ 1 - 1
fs-service-system/src/main/resources/mapper/company/CompanyMapper.xml

@@ -63,7 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
     </select>
     <select id="selectCompanyAllList" resultType="com.fs.company.domain.Company">
-        select company_id,company_name where is_del=0
+        select company_id,company_name from company where is_del=0
     </select>
 
     <insert id="insertCompany" parameterType="Company" useGeneratedKeys="true" keyProperty="companyId">

+ 15 - 0
fs-service-system/src/main/resources/mapper/company/CompanyUserMapper.xml

@@ -525,6 +525,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
         </where>
     </select>
+    <select id="queryCompanyUserInterruptCount" resultType="java.lang.Long">
+        select count(1) from fs_course_watch_log
+        <where>
+            AND log_type = 4
+            <if test="companyUserId != null">
+                AND company_user_id = #{companyUserId}
+            </if>
+            <if test="previousDay != null">
+                and camp_period_time = ${previousDay}
+            </if>
+        </where>
+    </select>
+    <select id="selectCompanyUserByDeptId" resultType="com.fs.company.domain.CompanyUser">
+        select * from company_user where dept_id = ${deptId} and status='0'
+    </select>
 
     <update id="setIsRegisterMember" parameterType="Long">
         update company_user

+ 1 - 1
fs-service-system/src/main/resources/mapper/course/FsCourseQuestionBankMapper.xml

@@ -34,7 +34,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="question != null  and question != ''"> and question = #{question}</if>
             <if test="answer != null  and answer != ''"> and answer = #{answer}</if>
         </where>
-        order by create_time desc
+        order by sort,id
     </select>
 
     <select id="selectFsCourseQuestionBankById" parameterType="Long" resultMap="FsCourseQuestionBankResult">

+ 1 - 1
fs-service-system/src/main/resources/mapper/course/FsUserCourseMapper.xml

@@ -246,7 +246,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             )
         </if>
         ORDER BY
-        fcp.create_time desc
+        fcp.create_time desc, fcp.period_status asc
     </select>
 
     <!-- 查询用户参与记录 -->

+ 11 - 1
fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -243,8 +243,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
         <![CDATA[
-        and ucp.period_starting_time <= #{params.date}
+        and ((ucp.period_starting_time <= #{params.date}
         and ucp.period_end_time >= #{params.date}
+        ) or ucp.period_end_time < #{params.date})
         ]]>
     </select>
+
+    <select id="selectPeriodListByTrainingCampIds" resultType="FsUserCoursePeriod">
+        select * from fs_user_course_period
+         where training_camp_id in
+        <foreach collection="trainingCampIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+    </select>
+
 </mapper>

+ 1 - 1
fs-service-system/src/main/resources/mapper/course/FsVideoResourceMapper.xml

@@ -21,6 +21,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="params.typeSubId != null">
             and rr.type_sub_id = #{params.typeSubId}
         </if>
-        order by rr.create_time desc
+        order by rr.id
     </select>
 </mapper>

+ 11 - 1
fs-service-system/src/main/resources/mapper/qw/QwCompanyMapper.xml

@@ -26,10 +26,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="updateTime"    column="update_time"    />
         <result property="createBy"    column="create_by"    />
         <result property="isBuy"    column="is_buy"    />
+        <result property="miniAppId"    column="mini_app_id"    />
+        <result property="miniAppParam"    column="mini_app_param"    />
     </resultMap>
 
     <sql id="selectQwCompanyVo">
-        select id, corp_id, corp_name, open_secret, open_corp_id, server_agent_id, server_book_corp_id, server_book_secret, token, encoding_aes_key, provider_secret, realm_name_url, notify_url, chat_toolbar, chat_toolbar_oauth, company_ids, status, create_time, update_time, create_by,is_buy from qw_company
+        select id, corp_id, corp_name, open_secret, open_corp_id, server_agent_id, server_book_corp_id, server_book_secret, token, encoding_aes_key, provider_secret, realm_name_url, notify_url, chat_toolbar, chat_toolbar_oauth, company_ids, status, create_time, update_time, create_by,is_buy, mini_app_id, mini_app_param from qw_company
     </sql>
 
     <select id="selectQwCompanyList" parameterType="QwCompany" resultMap="QwCompanyResult">
@@ -52,6 +54,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyIds != null  and companyIds != ''"> and company_ids = #{companyIds}</if>
             <if test="status != null "> and status = #{status}</if>
             <if test="isBuy != null "> and isBuy = #{isBuy}</if>
+            <if test="miniAppId != null "> and mini_app_id = #{miniAppId}</if>
+            <if test="miniAppParam != null "> and mini_app_param = #{miniAppParam}</if>
         </where>
     </select>
     
@@ -89,6 +93,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updateTime != null">update_time,</if>
             <if test="createBy != null">create_by,</if>
             <if test="isBuy != null">is_buy,</if>
+            <if test="miniAppId != null">mini_app_id,</if>
+            <if test="miniAppParam != null">mini_app_param,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="corpId != null">#{corpId},</if>
@@ -111,6 +117,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updateTime != null">#{updateTime},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="isBuy != null">#{isBuy},</if>
+            <if test="miniAppId != null">#{miniAppId},</if>
+            <if test="miniAppParam != null">#{miniAppParam},</if>
          </trim>
     </insert>
 
@@ -137,6 +145,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="isBuy != null">is_buy = #{isBuy},</if>
+            <if test="miniAppId != null">mini_app_id = #{miniAppId},</if>
+            <if test="miniAppParam != null">mini_app_param = #{miniAppParam},</if>
         </trim>
         where id = #{id}
     </update>

+ 2 - 2
fs-service-system/src/main/resources/mapper/qw/QwWatchLogMapper.xml

@@ -67,7 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             resultType="com.fs.qw.vo.QwWatchLogAllStatisticsListVO">
             select
             any_value(company_id) as company_id,
-            company_user_id,line_time as create_time,
+            company_user_id,MIN(line_time) as create_time,
             COUNT(CASE WHEN day = 0 and status in (1,2) THEN 1 END) AS firstOnline,
             COUNT(CASE WHEN day = 0 and status=2 THEN 1 END) AS firstOver,
             COUNT(CASE WHEN day = 1 and status in (1,2) THEN 1 END) AS d1Online,
@@ -148,7 +148,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 <if test="videoId != null">
                     and video_id =#{videoId}
                 </if>
-                and DATE(line_time) between #{sDate} AND #{eDate} group by project,course_id,video_id,company_user_id
+                and DATE(line_time) between #{sDate} AND #{eDate} group by line_time,project,course_id,video_id,company_user_id
             </where>
     </select>
     <select id="selectQwWatchLogByCompanyUserIdCount" resultType="java.lang.Long">

+ 3 - 0
fs-service-system/src/main/resources/mapper/sop/QwSopLogsMapper.xml

@@ -569,6 +569,9 @@
         </where>
 
     </select>
+    <select id="queryPeriodNameById" resultType="java.lang.String">
+        select concat(qw_user_id,'-',start_time) from sop_user_logs where id=#{periodId}
+    </select>
 
 
     <!-- 批量更新 QwSopLogs -->

+ 2 - 1
fs-service-system/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

@@ -345,7 +345,7 @@
         group by start_date
     </select>
     <select id="getCurrentBalance" resultType="java.math.BigDecimal">
-        select sum(money) from company
+        select sum(money) from company where is_del=0
     </select>
     <select id="dealerAggregatedCompanyId" resultType="com.fs.statis.dto.DealerAggregatedDTO">
         SELECT
@@ -394,6 +394,7 @@
             <if test="companyId != null">
                 company_id = #{companyId}
             </if>
+            AND is_del=0
         </where>
     </select>
 

+ 4 - 3
fs-service-system/src/main/resources/mapper/statis/FsStatisPeriodWatchMapper.xml

@@ -56,11 +56,11 @@
             </if>
         </where>
     </select>
-    <select id="selectRecords" resultType="java.lang.String">
-        select id from sop_user_logs
+    <select id="selectRecords" resultType="com.fs.sop.domain.SopUserLogs">
+        select id,qw_user_id from sop_user_logs
         <where>
             <if test="status != null">
-               AND status = 1
+                AND status = 1
             </if>
             <if test="previousDay != null">
                 AND start_time = #{previousDay}
@@ -68,4 +68,5 @@
         </where>
     </select>
 
+
 </mapper>

+ 133 - 20
fs-service-system/src/main/resources/mapper/statis/FsStatisSalerWatchMapper.xml

@@ -31,28 +31,36 @@
     </sql>
 
     <select id="queryList" resultType="com.fs.statis.domain.FsStatisSalerWatch">
-        select
-                company_user_id,
-                ANY_VALUE(dept_id) as dept_id,
-               sum(train_camp_num) as train_camp_num,
-               sum(not_registered_num) as not_registered_num,
-               sum(registered_num) as registered_num,
-               sum(reg_rate) as reg_rate,
-               sum(finished_rate) as finished_rate,
-               sum(offline_total) as offline_total,
-               sum(offline_not_part) as offline_not_part,
-               sum(offline_not_watched) as offline_not_watched,
-               sum(online_total) as online_total,
-               sum(online_online_rate) as online_online_rate,
-               sum(online_playback_comple_rate) as online_playback_comple_rate,
-               sum(online_incomplete_playback) as online_incomplete_playback,
-               sum(online_complete_playback) as online_complete_playback
-               from fs_statis_saler_watch
+        SELECT
+        company_user_id,
+        ANY_VALUE(dept_id) as dept_id,
+        SUM(train_camp_num) as train_camp_num,
+        SUM(not_registered_num) as not_registered_num,
+        SUM(registered_num) as registered_num,
+        SUM(offline_total) as offline_total,
+        SUM(offline_not_part) as offline_not_part,
+        SUM(offline_not_watched) as offline_not_watched,
+        SUM(online_total) as online_total,
+        SUM(online_incomplete_playback) as online_incomplete_playback,
+        SUM(online_complete_playback) as online_complete_playback,
+        CASE WHEN SUM(train_camp_num) > 0
+        THEN ROUND(SUM(registered_num) * 1.0 / SUM(train_camp_num), 4)
+        ELSE 0 END as reg_rate,
+        CASE WHEN SUM(registered_num) > 0
+        THEN ROUND((SUM(registered_num) - SUM(not_registered_num)) * 1.0 / SUM(registered_num), 4)
+        ELSE 0 END as finished_rate,
+        CASE WHEN SUM(online_total) > 0
+        THEN ROUND(SUM(online_complete_playback) * 1.0 / SUM(online_total), 4)
+        ELSE 0 END as online_online_rate,
+        CASE WHEN SUM(online_total) > 0
+        THEN ROUND(SUM(online_complete_playback) * 1.0 / SUM(online_total), 4)
+        ELSE 0 END as online_playback_comple_rate
+        FROM fs_statis_saler_watch
         <where>
             <if test="userIds != null and userIds.size() > 0">
                 AND company_user_id IN
-                 <foreach collection="userIds" open="(" close=")" separator="," item="item">
-                     ${item}
+                <foreach collection="userIds" open="(" close=")" separator="," item="item">
+                    ${item}
                 </foreach>
             </if>
             <if test="periodList != null and periodList.size() > 0">
@@ -65,8 +73,99 @@
                 AND data_date BETWEEN #{startDate} AND #{endDate}
             </if>
         </where>
-        group by company_user_id,data_date
+        GROUP BY company_user_id
     </select>
+    <select id="queryPeriodList" resultType="com.fs.statis.domain.FsStatisSalerWatch">
+        SELECT
+        period_id,
+        ANY_VALUE(dept_id) as dept_id,
+        SUM(train_camp_num) as train_camp_num,
+        SUM(not_registered_num) as not_registered_num,
+        SUM(registered_num) as registered_num,
+        SUM(offline_total) as offline_total,
+        SUM(offline_not_part) as offline_not_part,
+        SUM(offline_not_watched) as offline_not_watched,
+        SUM(online_total) as online_total,
+        SUM(online_incomplete_playback) as online_incomplete_playback,
+        SUM(online_complete_playback) as online_complete_playback,
+        CASE WHEN SUM(train_camp_num) > 0
+        THEN ROUND(SUM(registered_num) * 1.0 / SUM(train_camp_num), 4)
+        ELSE 0 END as reg_rate,
+        CASE WHEN SUM(registered_num) > 0
+        THEN ROUND((SUM(registered_num) - SUM(not_registered_num)) * 1.0 / SUM(registered_num), 4)
+        ELSE 0 END as finished_rate,
+        CASE WHEN SUM(online_total) > 0
+        THEN ROUND(SUM(online_complete_playback) * 1.0 / SUM(online_total), 4)
+        ELSE 0 END as online_online_rate,
+        CASE WHEN SUM(online_total) > 0
+        THEN ROUND(SUM(online_complete_playback) * 1.0 / SUM(online_total), 4)
+        ELSE 0 END as online_playback_comple_rate
+        FROM fs_statis_saler_watch
+        <where>
+            <if test="userIds != null and userIds.size() > 0">
+                AND company_user_id IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="periodList != null and periodList.size() > 0">
+                AND period_id IN
+                <foreach collection="periodList" open="(" close=")" separator="," item="item">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="startDate != null and endDate != null">
+                AND data_date BETWEEN #{startDate} AND #{endDate}
+            </if>
+        </where>
+        GROUP BY period_id
+    </select>
+    <select id="queryTodayList" resultType="com.fs.statis.domain.FsStatisSalerWatch">
+        SELECT
+        data_date,
+        ANY_VALUE(dept_id) as dept_id,
+        SUM(train_camp_num) as train_camp_num,
+        SUM(not_registered_num) as not_registered_num,
+        SUM(registered_num) as registered_num,
+        SUM(offline_total) as offline_total,
+        SUM(offline_not_part) as offline_not_part,
+        SUM(offline_not_watched) as offline_not_watched,
+        SUM(online_total) as online_total,
+        SUM(online_incomplete_playback) as online_incomplete_playback,
+        SUM(online_complete_playback) as online_complete_playback,
+        CASE WHEN SUM(train_camp_num) > 0
+        THEN ROUND(SUM(registered_num) * 1.0 / SUM(train_camp_num), 4)
+        ELSE 0 END as reg_rate,
+        CASE WHEN SUM(registered_num) > 0
+        THEN ROUND((SUM(registered_num) - SUM(not_registered_num)) * 1.0 / SUM(registered_num), 4)
+        ELSE 0 END as finished_rate,
+        CASE WHEN SUM(online_total) > 0
+        THEN ROUND(SUM(online_complete_playback) * 1.0 / SUM(online_total), 4)
+        ELSE 0 END as online_online_rate,
+        CASE WHEN SUM(online_total) > 0
+        THEN ROUND(SUM(online_complete_playback) * 1.0 / SUM(online_total), 4)
+        ELSE 0 END as online_playback_comple_rate
+        FROM fs_statis_saler_watch
+        <where>
+            <if test="userIds != null and userIds.size() > 0">
+                AND company_user_id IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="periodList != null and periodList.size() > 0">
+                AND period_id IN
+                <foreach collection="periodList" open="(" close=")" separator="," item="item">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="startDate != null and endDate != null">
+                AND data_date BETWEEN #{startDate} AND #{endDate}
+            </if>
+        </where>
+        GROUP BY data_date
+    </select>
+
 
     <insert id="batchSave">
         INSERT INTO fs_statis_saler_watch (
@@ -98,6 +197,20 @@
                 #{item.dataDate,jdbcType=VARCHAR}
             )
         </foreach>
+        ON DUPLICATE KEY UPDATE
+        train_camp_num = VALUES(train_camp_num),
+        not_registered_num = VALUES(not_registered_num),
+        registered_num = VALUES(registered_num),
+        reg_rate = VALUES(reg_rate),
+        finished_rate = VALUES(finished_rate),
+        offline_total = VALUES(offline_total),
+        offline_not_part = VALUES(offline_not_part),
+        offline_not_watched = VALUES(offline_not_watched),
+        online_total = VALUES(online_total),
+        online_online_rate = VALUES(online_online_rate),
+        online_playback_comple_rate = VALUES(online_playback_comple_rate),
+        online_incomplete_playback = VALUES(online_incomplete_playback),
+        online_complete_playback = VALUES(online_complete_playback)
     </insert>
 
 </mapper>

+ 1 - 1
fs-user-app/src/main/java/com/fs/core/config/MyBatisConfig.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
 import com.fs.common.utils.StringUtils;