瀏覽代碼

Merge branch 'refs/heads/master_feat_20250411'

# Conflicts:
#	fs-service-system/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
xdd 1 月之前
父節點
當前提交
609a4485c6
共有 52 個文件被更改,包括 3448 次插入64 次删除
  1. 111 0
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  2. 0 1
      fs-admin/src/main/java/com/fs/task/FsCourseTask.java
  3. 64 0
      fs-admin/src/main/java/com/fs/task/qw/FsCourseTask.java
  4. 1 1
      fs-admin/src/main/resources/application.yml
  5. 80 0
      fs-common/src/main/java/com/fs/common/utils/PhoneUtil.java
  6. 10 2
      fs-company/src/main/java/com/fs/course/controller/FsCourseAnswerLogsController.java
  7. 3 3
      fs-company/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java
  8. 153 0
      fs-company/src/main/java/com/fs/course/controller/qw/QwFsCourseAnswerLogsController.java
  9. 267 0
      fs-company/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  10. 128 0
      fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java
  11. 3 3
      fs-company/src/main/java/com/fs/qw/qw/QwWorkTaskController.java
  12. 3 0
      fs-service-system/src/main/java/com/fs/company/cache/impl/ICompanyCacheServiceImpl.java
  13. 1 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java
  14. 1 13
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  15. 1 20
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  16. 2 0
      fs-service-system/src/main/java/com/fs/course/param/FsCourseAnswerLogsParam.java
  17. 1 0
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseAnswerLogsService.java
  18. 2 0
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  19. 5 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseAnswerLogsServiceImpl.java
  20. 66 4
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  21. 4 4
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseOverVO.java
  22. 2 2
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java
  23. 127 0
      fs-service-system/src/main/java/com/fs/qw/mapper/HyWorkTaskMapper.java
  24. 222 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwWatchLogExMapper.java
  25. 2 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwWorkTaskMapper.java
  26. 3 0
      fs-service-system/src/main/java/com/fs/qw/param/QwWorkTaskListParam.java
  27. 110 0
      fs-service-system/src/main/java/com/fs/qw/service/IHyWorkTaskService.java
  28. 1 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwWorkTaskService.java
  29. 518 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/HyWorkTaskServiceImpl.java
  30. 5 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwWorkTaskServiceImpl.java
  31. 3 0
      fs-service-system/src/main/java/com/fs/qw/vo/QwWorkTaskListVO.java
  32. 38 0
      fs-service-system/src/main/java/com/fs/statis/cache/IStatisticsCacheService.java
  33. 80 0
      fs-service-system/src/main/java/com/fs/statis/cache/impl/StatisticsCacheServiceImpl.java
  34. 91 0
      fs-service-system/src/main/java/com/fs/statis/dto/AnalysisPreviewDTO.java
  35. 37 0
      fs-service-system/src/main/java/com/fs/statis/dto/AnalysisPreviewQueryDTO.java
  36. 17 0
      fs-service-system/src/main/java/com/fs/statis/dto/AuthorizationInfoDTO.java
  37. 28 0
      fs-service-system/src/main/java/com/fs/statis/dto/ConsumptionBalanceDataDTO.java
  38. 63 0
      fs-service-system/src/main/java/com/fs/statis/dto/CourseStatsDTO.java
  39. 22 0
      fs-service-system/src/main/java/com/fs/statis/dto/DeaMemberTopTenDTO.java
  40. 30 0
      fs-service-system/src/main/java/com/fs/statis/dto/DealerAggregatedDTO.java
  41. 29 0
      fs-service-system/src/main/java/com/fs/statis/dto/WatchEndPlayTrendDTO.java
  42. 107 0
      fs-service-system/src/main/java/com/fs/statis/mapper/ConsumptionBalanceMapper.java
  43. 58 0
      fs-service-system/src/main/java/com/fs/statis/service/IStatisticsService.java
  44. 177 0
      fs-service-system/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java
  45. 96 0
      fs-service-system/src/main/java/com/fs/statis/service/utils/TrendDataFiller.java
  46. 27 0
      fs-service-system/src/main/resources/mapper/course/FsCourseAnswerLogsMapper.xml
  47. 14 0
      fs-service-system/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  48. 23 0
      fs-service-system/src/main/resources/mapper/course/FsUserCourseMapper.xml
  49. 187 0
      fs-service-system/src/main/resources/mapper/qw/HyWorkTaskMapper.xml
  50. 232 0
      fs-service-system/src/main/resources/mapper/qw/QwWatchLogExMapper.xml
  51. 33 11
      fs-service-system/src/main/resources/mapper/qw/QwWorkTaskMapper.xml
  52. 160 0
      fs-service-system/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

+ 111 - 0
fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java

@@ -0,0 +1,111 @@
+package com.fs.api.controller;
+
+import com.fs.statis.cache.IStatisticsCacheService;
+import com.fs.statis.dto.*;
+import com.fs.statis.service.IStatisticsService;
+import com.hc.openapi.tool.util.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import com.fs.common.core.domain.R;
+
+import java.util.List;
+
+/**
+ * 首页-统计
+ */
+@RestController
+@RequestMapping("/index/statistics")
+public class IndexStatisticsController {
+    @Autowired
+    private IStatisticsCacheService statisticsService;
+    @Autowired
+    private IStatisticsService statisticsService1;
+    /**
+     * 分析概览
+     */
+    @PostMapping("/analysisPreview")
+    public R analysisPreview(@RequestBody AnalysisPreviewQueryDTO param){
+        AnalysisPreviewDTO analysisPreviewDTO = statisticsService1.analysisPreview(param);
+        return R.ok().put("data",analysisPreviewDTO);
+    }
+
+    /**
+     * 消费余额
+     */
+    @GetMapping("/rechargeComsumption")
+    public R rechargeComsumption(){
+        ConsumptionBalanceDataDTO consumptionBalanceDataDTO = statisticsService.rechargeConsumption();
+        return R.ok().put("data", consumptionBalanceDataDTO);
+    }
+
+    /**
+     * 观看趋势
+     */
+    @PostMapping("/watchEndPlayTrend")
+    public R watchEndPlayTrend(@RequestBody AnalysisPreviewQueryDTO param){
+        List<WatchEndPlayTrendDTO> watchEndPlayTrendDTOS = statisticsService.watchEndPlayTrend(param);
+        return R.ok().put("data", watchEndPlayTrendDTOS);
+    }
+
+    /**
+     * 经销商会员观看
+     */
+    @PostMapping("/deaMemberTopTen")
+    public R deaMemberTopTen(@RequestBody AnalysisPreviewQueryDTO param){
+        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = statisticsService.deaMemberTopTen(param);
+        return R.ok().put("data", deaMemberTopTenDTOS);
+    }
+
+    /**
+     * 奖励金额top10
+     */
+    @GetMapping("/rewardMoneyTopTen")
+    public void rewardMoneyTopTen(){
+
+    }
+
+    /**
+     * 答题红包金额趋势图
+     */
+    @GetMapping("/rewardMoneyTrend")
+    public void rewardMoneyTrend(){
+
+    }
+
+    /**
+     * 课程观看top10
+     */
+    @PostMapping("/watchCourseTopTen")
+    public R watchCourseTopTen(@RequestBody AnalysisPreviewQueryDTO param){
+        List<CourseStatsDTO> courseStatsDTOS = statisticsService1.watchCourseTopTen(param);
+        return R.ok().put("data", courseStatsDTOS);
+    }
+
+    /**
+     * 数据概览
+     */
+    @GetMapping("/dealerAggregated")
+    public R dealerAggregated(){
+        DealerAggregatedDTO dealerAggregatedDTO = statisticsService.dealerAggregated();
+        return R.ok().put("data",dealerAggregatedDTO);
+    }
+
+    /**
+     * 短信余额
+     */
+    @GetMapping("/smsBalance")
+    public R smsBalance(){
+        Long smsBalance = statisticsService1.smsBalance();
+        return R.ok().put("data", smsBalance);
+    }
+
+
+    /**
+     * 授权信息
+     */
+    @GetMapping("/authorizationInfo")
+    public R authorizationInfo(){
+        AuthorizationInfoDTO authorizationInfoDTO = statisticsService1.authorizationInfo();
+        return R.ok().put("data", authorizationInfoDTO);
+    }
+}

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

@@ -26,7 +26,6 @@ public class FsCourseTask {
         fsCourseWatchLogService.addCourseWatchLogDayNew();
     }
 
-
     /**
      * 企微任务定时更新
      * @throws Exception

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

@@ -0,0 +1,64 @@
+package com.fs.task.qw;
+
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.qw.service.IQwWorkTaskService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 后台统计相关 定时任务
+ */
+@Slf4j
+@Service("qwFsCourseTask")
+public class FsCourseTask {
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+    @Autowired
+    private IQwWorkTaskService qwWorkTaskService;
+
+    /**
+     * 添加企微观看日志
+     * @throws Exception
+     */
+    public void addQwWatchLog() throws Exception
+    {
+        fsCourseWatchLogService.addCourseWatchLogDay();
+    }
+
+    /**
+     * 企微任务定时更新
+     * @throws Exception
+     */
+    public void qwWorkTask1() throws Exception
+    {
+        qwWorkTaskService.addQwWorkByCourse4();
+        qwWorkTaskService.addQwWorkByCourseLastTime();
+    }
+    /**
+     * 企微待看课和先导课
+     * @throws Exception
+     */
+    public void qwWorkTask2() throws Exception
+    {
+        qwWorkTaskService.addQwWorkByCourse();
+        qwWorkTaskService.addQwWorkByFirstCourse();
+    }
+    /**
+     * 用户大小转
+     * @throws Exception
+     */
+    public void qwWorkTask3() throws Exception
+    {
+        qwWorkTaskService.addQwWorkByConversionDay();
+    }
+    /**
+     * 删除过期数据
+     * @throws Exception
+     */
+    public void qwWorkTask4() throws Exception
+    {
+        qwWorkTaskService.delQwWorkTaskByOver();
+    }
+
+}

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

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

+ 80 - 0
fs-common/src/main/java/com/fs/common/utils/PhoneUtil.java

@@ -0,0 +1,80 @@
+package com.fs.common.utils;
+
+import com.fs.common.utils.ParseUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Base64;
+
+public class PhoneUtil {
+
+    public static String encryptPhone(String text) {
+        String encryptedText=null;
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec("AESAabCdeREssREA".getBytes(), "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            // Encryption
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+            byte[] encryptedBytes = cipher.doFinal(text.getBytes());
+            encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return encryptedText;
+    }
+    /**
+    * 解密
+    */
+    public static String decryptPhone(String encryptedText) {
+        String text=null;
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec("AESAabCdeREssREA".getBytes(), "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            cipher.init(Cipher.DECRYPT_MODE, secretKey);
+            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
+            text = new String(decryptedBytes);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return text;
+    }
+    /**
+    * 解密加*
+    */
+    public static String decryptPhoneMk(String encryptedText) {
+        String text=null;
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec("AESAabCdeREssREA".getBytes(), "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+            cipher.init(Cipher.DECRYPT_MODE, secretKey);
+            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
+            text = new String(decryptedBytes);
+            text =text.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return text;
+    }
+
+    public static String decryptAutoPhoneMk(String encryptedText) {
+        String text=null;
+        if (encryptedText!=null&&encryptedText!="") {
+            if (encryptedText.length()>11){
+                try {
+                    SecretKeySpec secretKey = new SecretKeySpec("AESAabCdeREssREA".getBytes(), "AES");
+                    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+                    cipher.init(Cipher.DECRYPT_MODE, secretKey);
+                    byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
+                    text = new String(decryptedBytes);
+                    text =text.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }else {
+                text =  ParseUtils.parsePhone(encryptedText);
+            }
+        }
+
+        return text;
+    }
+}

+ 10 - 2
fs-company/src/main/java/com/fs/course/controller/FsCourseAnswerLogsController.java

@@ -1,6 +1,7 @@
 package com.fs.course.controller;
 
 import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.page.TableDataInfo;
@@ -13,6 +14,7 @@ import com.fs.course.service.IFsCourseAnswerLogsService;
 import com.fs.course.vo.FsCourseAnswerLogsListVO;
 import com.fs.common.annotation.Log;
 import com.fs.core.web.service.TokenService;
+import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -59,7 +61,6 @@ public class FsCourseAnswerLogsController extends BaseController
     @GetMapping("/myList")
     public TableDataInfo myList(FsCourseAnswerLogsParam param)
     {
-        startPage();
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
@@ -69,7 +70,14 @@ public class FsCourseAnswerLogsController extends BaseController
         }
 
         List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVONew(param);
-        return getDataTable(list);
+
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVONewCount(param));
+
+        return rspData;
     }
     /**
      * 导出答题日志列表

+ 3 - 3
fs-company/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java

@@ -62,7 +62,7 @@ public class FsCourseWatchLogController extends BaseController
     {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
-        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVONew(param);
         TableDataInfo rspData = new TableDataInfo();
         rspData.setCode(HttpStatus.SUCCESS);
         rspData.setMsg("查询成功");
@@ -145,7 +145,7 @@ public class FsCourseWatchLogController extends BaseController
         if (param.getSTime()==null||param.getETime()==null){
             return getDataTable(new ArrayList<>());
         }
-        List<FsCourseOverVO> list = fsCourseWatchLogService.selectFsCourseWatchLogOverStatisticsListVO(param);
+        List<FsCourseOverVO> list = fsCourseWatchLogService.selectFsCourseWatchLogOverStatisticsListVONew(param);
         return getDataTable(list);
     }
     @GetMapping("/watchLogStatisticsExport")
@@ -187,7 +187,7 @@ public class FsCourseWatchLogController extends BaseController
     {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
-        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVONew(param);
         TableDataInfo rspData = new TableDataInfo();
         rspData.setCode(HttpStatus.SUCCESS);
         rspData.setMsg("查询成功");

+ 153 - 0
fs-company/src/main/java/com/fs/course/controller/qw/QwFsCourseAnswerLogsController.java

@@ -0,0 +1,153 @@
+package com.fs.course.controller.qw;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+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.FsCourseAnswerLogsParam;
+import com.fs.course.service.IFsCourseAnswerLogsService;
+import com.fs.course.vo.FsCourseAnswerLogsListVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+import static com.fs.common.utils.PhoneUtil.encryptPhone;
+
+/**
+ * 答题日志Controller
+ *
+ * @author fs
+ * @date 2024-10-26
+ */
+@RestController
+@RequestMapping("/qw/course/courseAnswerLog")
+public class QwFsCourseAnswerLogsController extends BaseController
+{
+    @Autowired
+    private IFsCourseAnswerLogsService fsCourseAnswerLogsService;
+
+    @Autowired
+    private TokenService tokenService;
+    /**
+     * 查询答题日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsCourseAnswerLogsParam param)
+    {
+        startPage();
+        if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
+            param.setPhone(encryptPhone(param.getPhoneMk()));
+        }
+
+        List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVO(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询答题日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:myList')")
+    @GetMapping("/myList")
+    public TableDataInfo myList(FsCourseAnswerLogsParam param)
+    {
+        startPage();
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+
+        if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
+            param.setPhone(encryptPhone(param.getPhoneMk()));
+        }
+
+        List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVO(param);
+        return getDataTable(list);
+    }
+    /**
+     * 导出答题日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:export')")
+    @Log(title = "答题日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsCourseAnswerLogsParam param)
+    {
+
+        if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
+            param.setPhone(encryptPhone(param.getPhoneMk()));
+        }
+        List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVO(param);
+        ExcelUtil<FsCourseAnswerLogsListVO> util = new ExcelUtil<FsCourseAnswerLogsListVO>(FsCourseAnswerLogsListVO.class);
+        return util.exportExcel(list, "答题日志数据");
+    }
+
+    /**
+     * 导出答题日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:myExport')")
+    @Log(title = "答题日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/myExport")
+    public AjaxResult myExport(FsCourseAnswerLogsParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+
+        if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
+            param.setPhone(encryptPhone(param.getPhoneMk()));
+        }
+        List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVO(param);
+        ExcelUtil<FsCourseAnswerLogsListVO> util = new ExcelUtil<FsCourseAnswerLogsListVO>(FsCourseAnswerLogsListVO.class);
+        return util.exportExcel(list, "答题日志数据");
+    }
+
+//    /**
+//     * 获取答题日志详细信息
+//     */
+//    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:query')")
+//    @GetMapping(value = "/{logId}")
+//    public AjaxResult getInfo(@PathVariable("logId") Long logId)
+//    {
+//        return AjaxResult.success(fsCourseAnswerLogsService.selectFsCourseAnswerLogsByLogId(logId));
+//    }
+
+//    /**
+//     * 新增答题日志
+//     */
+//    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:add')")
+//    @Log(title = "答题日志", businessType = BusinessType.INSERT)
+//    @PostMapping
+//    public AjaxResult add(@RequestBody FsCourseAnswerLogs fsCourseAnswerLogs)
+//    {
+//        return toAjax(fsCourseAnswerLogsService.insertFsCourseAnswerLogs(fsCourseAnswerLogs));
+//    }
+//
+//    /**
+//     * 修改答题日志
+//     */
+//    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:edit')")
+//    @Log(title = "答题日志", businessType = BusinessType.UPDATE)
+//    @PutMapping
+//    public AjaxResult edit(@RequestBody FsCourseAnswerLogs fsCourseAnswerLogs)
+//    {
+//        return toAjax(fsCourseAnswerLogsService.updateFsCourseAnswerLogs(fsCourseAnswerLogs));
+//    }
+//
+//    /**
+//     * 删除答题日志
+//     */
+//    @PreAuthorize("@ss.hasPermi('course:courseAnswerLog:remove')")
+//    @Log(title = "答题日志", businessType = BusinessType.DELETE)
+//	@DeleteMapping("/{logIds}")
+//    public AjaxResult remove(@PathVariable Long[] logIds)
+//    {
+//        return toAjax(fsCourseAnswerLogsService.deleteFsCourseAnswerLogsByLogIds(logIds));
+//    }
+}

+ 267 - 0
fs-company/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java

@@ -0,0 +1,267 @@
+package com.fs.course.controller.qw;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.core.security.LoginUser;
+import com.fs.core.web.service.TokenService;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.param.FsCourseOverParam;
+import com.fs.course.param.FsCourseUserStatisticsListParam;
+import com.fs.course.param.FsCourseWatchLogListParam;
+import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.vo.FsCourseOverVO;
+import com.fs.course.vo.FsCourseUserStatisticsListVO;
+import com.fs.course.vo.FsCourseWatchLogListVO;
+import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
+import com.fs.qw.param.QwWatchLogStatisticsListParam;
+import com.fs.qw.service.IQwWatchLogService;
+import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
+import com.fs.qw.vo.QwWatchLogStatisticsListVO;
+import com.fs.sop.mapper.SopUserLogsMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 短链课程看课记录Controller
+ *
+ * @author fs
+ * @date 2024-10-24
+ */
+@RestController
+@RequestMapping("/qw/course/courseWatchLog")
+public class QwFsCourseWatchLogController extends BaseController
+{
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private SopUserLogsMapper sopUserLogsMapper;
+    @Autowired
+    private IQwWatchLogService qwWatchLogService;
+    /**
+     * 查询短链课程看课记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsCourseWatchLogListParam param)
+    {
+
+//        if (param.getScheduleStartTime() != null && param.getScheduleEndTime() != null){
+//            List<String> sopUserLogsVOS = sopUserLogsMapper.selectSopUserLogsByDate(param.getScheduleStartTime(), param.getScheduleEndTime());
+//            param.setSopIds(sopUserLogsVOS);
+//            if (sopUserLogsVOS==null||sopUserLogsVOS.size()==0){
+//                return getDataTable(new ArrayList<>());
+//            }
+//        }
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:statisticsList')")
+    @GetMapping("/statisticsList")
+    public TableDataInfo statisticsList(FsCourseWatchLogStatisticsListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
+        return getDataTable(list);
+    }
+
+    @GetMapping("/qwWatchLogStatisticsList")
+    public TableDataInfo qwWatchLogStatisticsList(QwWatchLogStatisticsListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<QwWatchLogStatisticsListVO> list = qwWatchLogService.selectQwWatchLogStatisticsListVO(param);
+        return getDataTable(list);
+    }
+    @GetMapping("/myQwWatchLogStatisticsList")
+    public TableDataInfo myQwWatchLogStatisticsList(QwWatchLogStatisticsListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        param.setCompanyUserId(loginUser.getUser().getUserId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<QwWatchLogStatisticsListVO> list = qwWatchLogService.selectQwWatchLogStatisticsListVO(param);
+        return getDataTable(list);
+    }
+
+
+    @GetMapping("/qwWatchLogAllStatisticsList")
+    public TableDataInfo qwWatchLogAllStatisticsList(QwWatchLogStatisticsListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<QwWatchLogAllStatisticsListVO> list = qwWatchLogService.selectQwWatchLogAllStatisticsListVO(param);
+        return getDataTable(list);
+    }
+    @GetMapping("/myQwWatchLogAllStatisticsList")
+    public TableDataInfo myQwWatchLogAllStatisticsList(QwWatchLogStatisticsListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        param.setCompanyUserId(loginUser.getUser().getUserId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<QwWatchLogAllStatisticsListVO> list = qwWatchLogService.selectQwWatchLogAllStatisticsListVO(param);
+        return getDataTable(list);
+    }
+    @GetMapping("/watchLogStatistics")
+    public TableDataInfo watchLogStatistics(FsCourseOverParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<FsCourseOverVO> list = fsCourseWatchLogService.selectFsCourseWatchLogOverStatisticsListVO(param);
+        return getDataTable(list);
+    }
+    @GetMapping("/watchLogStatisticsExport")
+    public AjaxResult watchLogStatisticsExport(FsCourseOverParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return AjaxResult.error("请选择时间");
+        }
+        List<FsCourseOverVO> list = fsCourseWatchLogService.selectFsCourseWatchLogOverStatisticsListVO(param);
+        ExcelUtil<FsCourseOverVO> util = new ExcelUtil<FsCourseOverVO>(FsCourseOverVO.class);
+        return util.exportExcel(list, "完课数据");
+    }
+
+
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:userStatisticsList')")
+    @GetMapping("/userStatisticsList")
+    public TableDataInfo userStatisticsList(FsCourseUserStatisticsListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return getDataTable(new ArrayList<>());
+        }
+        List<FsCourseUserStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseUserStatisticsListVO(param);
+        return getDataTable(list);
+    }
+
+
+
+    /**
+     * 查询短链课程看课记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:myList')")
+    @GetMapping("/myList")
+    public TableDataInfo myList(FsCourseWatchLogListParam param)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyUserId( loginUser.getUser().getUserId());
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出短链课程看课记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:export')")
+    @Log(title = "短链课程看课记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsCourseWatchLogListParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        ExcelUtil<FsCourseWatchLogListVO> util = new ExcelUtil<FsCourseWatchLogListVO>(FsCourseWatchLogListVO.class);
+        return util.exportExcel(list, "短链课程看课记录数据");
+    }
+
+    /**
+     * 导出短链课程看课记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:myExport')")
+    @Log(title = "短链课程看课记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/myExport")
+    public AjaxResult myExport(FsCourseWatchLogListParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        ExcelUtil<FsCourseWatchLogListVO> util = new ExcelUtil<FsCourseWatchLogListVO>(FsCourseWatchLogListVO.class);
+        return util.exportExcel(list, "短链课程看课记录数据");
+    }
+    /**
+     * 获取短链课程看课记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:query')")
+    @GetMapping(value = "/{logId}")
+    public AjaxResult getInfo(@PathVariable("logId") Long logId)
+    {
+        return AjaxResult.success(fsCourseWatchLogService.selectFsCourseWatchLogByLogId(logId));
+    }
+
+    /**
+     * 新增短链课程看课记录
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:add')")
+    @Log(title = "短链课程看课记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsCourseWatchLog fsCourseWatchLog)
+    {
+        return toAjax(fsCourseWatchLogService.insertFsCourseWatchLog(fsCourseWatchLog));
+    }
+
+    /**
+     * 修改短链课程看课记录
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:edit')")
+    @Log(title = "短链课程看课记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsCourseWatchLog fsCourseWatchLog)
+    {
+        return toAjax(fsCourseWatchLogService.updateFsCourseWatchLog(fsCourseWatchLog));
+    }
+
+    /**
+     * 删除短链课程看课记录
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:remove')")
+    @Log(title = "短链课程看课记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{logIds}")
+    public AjaxResult remove(@PathVariable Long[] logIds)
+    {
+        return toAjax(fsCourseWatchLogService.deleteFsCourseWatchLogByLogIds(logIds));
+    }
+}

+ 128 - 0
fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java

@@ -0,0 +1,128 @@
+package com.fs.qw;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.core.security.LoginUser;
+import com.fs.core.web.service.TokenService;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.service.IHyWorkTaskService;
+import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 企微任务看板Controller
+ *
+ * @author fs
+ * @date 2025-03-25
+ */
+@RestController
+@RequestMapping("/qw/QwWorkTask")
+public class QwQwWorkTaskController extends BaseController
+{
+    @Autowired
+    private IHyWorkTaskService qwWorkTaskService;
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    /**
+     * 查询企微任务看板列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwWorkTaskListParam qwWorkTask)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+        if(ObjectUtils.isNull(qwWorkTask.getCompanyUserId())) {
+            qwWorkTask.setCompanyUserId(loginUser.getUser().getUserId());
+        }
+        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVONew(qwWorkTask);
+        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+        }
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(qwWorkTaskService.selectQwWorkTaskListVONewCount(qwWorkTask));
+        return rspData;
+    }
+
+    /**
+     * 导出企微任务看板列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:export')")
+    @Log(title = "企微任务看板", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwWorkTask qwWorkTask)
+    {
+        List<QwWorkTask> list = qwWorkTaskService.selectQwWorkTaskList(qwWorkTask);
+        ExcelUtil<QwWorkTask> util = new ExcelUtil<QwWorkTask>(QwWorkTask.class);
+        return util.exportExcel(list, "企微任务看板数据");
+    }
+
+    /**
+     * 获取企微任务看板详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwWorkTaskService.selectQwWorkTaskById(id));
+    }
+
+    /**
+     * 新增企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:add')")
+    @Log(title = "企微任务看板", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwWorkTask qwWorkTask)
+    {
+        return toAjax(qwWorkTaskService.insertQwWorkTask(qwWorkTask));
+    }
+
+    /**
+     * 修改企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setRemark(qwWorkTask.getRemark());
+        task.setStatus(1);
+        task.setUpdateTime(new Date());
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+
+    /**
+     * 删除企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:remove')")
+    @Log(title = "企微任务看板", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwWorkTaskService.deleteQwWorkTaskByIds(ids));
+    }
+}

+ 3 - 3
fs-company/src/main/java/com/fs/qw/QwWorkTaskController.java → fs-company/src/main/java/com/fs/qw/qw/QwWorkTaskController.java

@@ -1,4 +1,4 @@
-package com.fs.qw;
+package com.fs.qw.qw;
 
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.annotation.Log;
@@ -29,7 +29,7 @@ import java.util.List;
  * @date 2025-03-25
  */
 @RestController
-@RequestMapping("/qw/QwWorkTask")
+@RequestMapping("/qw/qw/QwWorkTask")
 public class QwWorkTaskController extends BaseController
 {
     @Autowired
@@ -51,7 +51,7 @@ public class QwWorkTaskController extends BaseController
         if(ObjectUtils.isNull(qwWorkTask.getCompanyUserId())) {
             qwWorkTask.setCompanyUserId(loginUser.getUser().getUserId());
         }
-        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVONew(qwWorkTask);
+        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
         for (QwWorkTaskListVO qwWorkTaskListVO : list) {
             qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
         }

+ 3 - 0
fs-service-system/src/main/java/com/fs/company/cache/impl/ICompanyCacheServiceImpl.java

@@ -32,6 +32,9 @@ public class ICompanyCacheServiceImpl implements ICompanyCacheService {
 
     @Override
     public String selectCompanyNameById(Long companyId) {
+        if(companyId == null){
+            return "未找到";
+        }
         return COMPANY_NAME_CACHE.get(companyId, e-> {
             Company company = companyService.selectCompanyById(companyId);
             if(company == null){

+ 1 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java

@@ -128,4 +128,5 @@ public interface FsCourseAnswerLogsMapper
     int selectErrorCountByCourseVideo(@Param("videoId") Long videoId,@Param("userId") Long userId,@Param("qwUserId") String qwUserId);
 
     List<FsCourseAnswerLogsListVO> selectFsCourseAnswerLogsListVONew(FsCourseAnswerLogsParam param);
+    Long selectFsCourseAnswerLogsListVONewCount(FsCourseAnswerLogsParam param);
 }

+ 1 - 13
fs-service-system/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -231,19 +231,7 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
     List<FsCourseUserStatisticsListVO> selectFsCourseUserStatisticsListVO(FsCourseUserStatisticsListParam param);
 
-    @Select({"<script> " +
-            "select MIN(o.create_time) createTime,ANY_VALUE(qu.qw_user_name) qwUserName ,ext.name externalUserName,ext.create_time userCreateTime from fs_course_watch_log o LEFT JOIN qw_user qu on qu.id=o.qw_user_id LEFT JOIN qw_external_contact ext ON ext.id =  o.qw_external_contact_id where log_type=2 and o.company_id=#{companyId} " +
-            "<if test= 'sTime != null '> " +
-            "       and DATE(o.create_time) &gt;= DATE(#{sTime})\n" +
-            "</if>\n" +
-            "<if test='eTime != null '> " +
-            "      and DATE(o.create_time) &lt;= DATE(#{eTime})\n" +
-            "</if>" +
-            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
-            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
-            "</if>"+
-            " GROUP BY o.qw_external_contact_id"+
-            "</script>"})
+
     List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVO(FsCourseOverParam param);
 
 

+ 1 - 20
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java

@@ -198,26 +198,7 @@ public interface FsUserCourseMapper
             "where v.video_id = #{videoId}")
     FsUserCourseVideoH5DVO selectFsUserCourseVideoH5DVOByVideoId(@Param("videoId") Long videoId);
 
-    @Select({"<script> " +
-            "select c.*,cc.cate_name,ucc.cate_name as sub_cate_name from fs_user_course  c " +
-            " left join fs_user_course_category cc on c.cate_id=cc.cate_id" +
-            " left join fs_user_course_category ucc on  ucc.cate_id = c.sub_cate_id " +
-            "where c.is_del = 0 and " +
-            " FIND_IN_SET(#{maps.companyId}, company_ids) " +
-            "<if test = ' maps.cateId !=null '> " +
-            "and (cc.cate_id =#{maps.cateId} or cc.pid=#{maps.cateId} )" +
-            "</if>" +
-            "<if test = ' maps.courseName!=null and maps.courseName != \"\" '> " +
-            "and c.course_name like concat('%', #{maps.courseName}, '%') " +
-            "</if>" +
-            "<if test = ' maps.isPrivate !=null '> " +
-            "and c.is_private = #{maps.isPrivate} " +
-            "</if>" +
-            "<if test = ' maps.isShow !=null '> " +
-            "and c.is_show = #{maps.isShow} " +
-            "</if>" +
-            " order by c.course_id  "+
-            "</script>"})
+
     List<FsUserCourseListPVO> selectFsUserCourseListCompanyPVO(@Param("maps")FsUserCourseParam fsUserCourse);
 
     @Select("select course_id dict_value, course_name dict_label,img_url dict_imgUrl  from fs_user_course where is_del = 0 and is_private = 1" +

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

@@ -58,4 +58,6 @@ public class FsCourseAnswerLogsParam  extends BaseEntity  {
 
     private String sTime;
 
+    private Long pageNum;
+    private Long pageSize;
 }

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

@@ -31,6 +31,7 @@ public interface IFsCourseAnswerLogsService
     public List<FsCourseAnswerLogs> selectFsCourseAnswerLogsList(FsCourseAnswerLogs fsCourseAnswerLogs);
     public List<FsCourseAnswerLogsListVO> selectFsCourseAnswerLogsListVO(FsCourseAnswerLogsParam param);
     public List<FsCourseAnswerLogsListVO> selectFsCourseAnswerLogsListVONew(FsCourseAnswerLogsParam param);
+    public Long selectFsCourseAnswerLogsListVONewCount(FsCourseAnswerLogsParam param);
 
     /**
      * 新增答题日志

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

@@ -82,6 +82,7 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
 
 
     List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param);
+    List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVONew(FsCourseWatchLogListParam param);
     Long selectFsCourseWatchLogListVOCount(FsCourseWatchLogListParam param);
     List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOExport(FsCourseWatchLogListParam param);
 
@@ -99,6 +100,7 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
     List<FsCourseUserStatisticsListVO> selectFsCourseUserStatisticsListVO(FsCourseUserStatisticsListParam param);
 
     List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVO(FsCourseOverParam param);
+    List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVONew(FsCourseOverParam param);
 
     void addCourseWatchLogDayNew();
 

+ 5 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseAnswerLogsServiceImpl.java

@@ -129,6 +129,11 @@ public class FsCourseAnswerLogsServiceImpl implements IFsCourseAnswerLogsService
         return data;
     }
 
+    @Override
+    public Long selectFsCourseAnswerLogsListVONewCount(FsCourseAnswerLogsParam param) {
+        return fsCourseAnswerLogsMapper.selectFsCourseAnswerLogsListVONewCount(param);
+    }
+
     /**
      * 新增答题日志
      *

+ 66 - 4
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -230,6 +230,67 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Override
     public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param) {
+        if(ObjectUtils.isNull(param.getPageNum())){
+            param.setPageNum(1L);
+        }
+        if(ObjectUtils.isNull(param.getPageSize())){
+            param.setPageSize(10L);
+        }
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
+        for (FsCourseWatchLogListVO item : list) {
+            // 项目
+            if(ObjectUtils.isNotNull(item.getProject())) {
+                String sysCourseProject = DictUtils.getDictLabel("sys_course_project", String.valueOf(item.getProject()));
+                if(StringUtils.isNotBlank(sysCourseProject)){
+                    item.setProjectName(sysCourseProject);
+                }
+            }
+            // 用户名
+            if(ObjectUtils.isNotNull(item.getUserId())) {
+                FsUser fsUser = fsUserCacheService.selectFsUserById(item.getUserId());
+                if(ObjectUtils.isNotNull(fsUser)){
+                    item.setUserName(String.format("%s_%d",fsUser.getNickname(),fsUser.getUserId()));
+                    item.setFsNickName(fsUser.getNickname());
+                    item.setFsAvatar(fsUser.getAvatar());
+                }
+            }
+            // 公司名
+            if(ObjectUtils.isNotNull(item.getCompanyId())){
+                Company company = companyCacheService.selectCompanyById(Long.valueOf(item.getCompanyId()));
+                if(ObjectUtils.isNotNull(company)){
+                    item.setCompanyName(String.format("%s_%d", company.getCompanyName(), company.getCompanyId()));
+                }
+            }
+
+            // 销售名
+            if(ObjectUtils.isNotNull(item.getCompanyUserId())){
+                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getCompanyUserId());
+                if(ObjectUtils.isNotNull(companyUser)){
+                    item.setCompanyUserName(String.format("%s_%d", companyUser.getNickName(), companyUser.getUserId()));
+                }
+            }
+
+            // 课程
+            if(ObjectUtils.isNotNull(item.getCourseId())){
+                FsUserCourse course = fsUserCourseCacheService.selectFsUserCourseByCourseId(item.getCourseId());
+                if(ObjectUtils.isNotNull(course)){
+                    item.setCourseName(course.getCourseName());
+                }
+            }
+            // 小节
+            if(ObjectUtils.isNotNull(item.getVideoId())){
+                FsUserCourseVideo fsUserCourseVideo = fsUserCourseVideoCacheService.selectFsUserCourseVideoByVideoId(item.getVideoId());
+                if(ObjectUtils.isNotNull(fsUserCourseVideo)){
+                    item.setVideoName(fsUserCourseVideo.getTitle());
+                }
+            }
+
+        }
+        return list;
+    }
+
+    @Override
+    public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVONew(FsCourseWatchLogListParam param) {
         if(ObjectUtils.isNull(param.getPageNum())){
             param.setPageNum(1L);
         }
@@ -535,6 +596,11 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Override
     public List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVO(FsCourseOverParam param) {
+        return fsCourseWatchLogMapper.selectFsCourseWatchLogOverStatisticsListVO(param);
+    }
+
+    @Override
+    public List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVONew(FsCourseOverParam param) {
         QueryWrapper<FsCourseWatchLog> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("MIN(create_time) as createTime","company_user_id as companyUserId");
         if(param.getCompanyId() != null) {
@@ -690,9 +756,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                         qwWatchLog.setStatus(fsCourseWatchLog.getLogType()==3?0:fsCourseWatchLog.getLogType()==2?2:1);
                         qwWatchLog.setProject(project);
                         qwWatchLog.setCreateTime(fsCourseWatchLog.getCreateTime());
-                        qwWatchLog.setCompanyId(fsCourseWatchLog.getCompanyId());
-                        qwWatchLog.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
-                        qwWatchLog.setFsUserId(fsCourseWatchLog.getUserId());
                         QwWatchLogs.add(qwWatchLog);
                     }
                     if (!QwWatchLogs.isEmpty()){
@@ -706,7 +769,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
             }
         }
 
-
     }
 
 

+ 4 - 4
fs-service-system/src/main/java/com/fs/course/vo/FsCourseOverVO.java

@@ -10,10 +10,10 @@ import java.util.Date;
 public class FsCourseOverVO {
 
 
-//    @Excel(name = "企微客户")
-//    private String qwUserName;
-//    @Excel(name = "企业微信员工名称")
-//    private String externalUserName;
+    @Excel(name = "企微客户")
+    private String qwUserName;
+    @Excel(name = "企业微信员工名称")
+    private String externalUserName;
     @Excel(name = "用户名")
     private String userName;
 

+ 2 - 2
fs-service-system/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java

@@ -30,8 +30,8 @@ public class FsCourseWatchLogStatisticsListVO {
     private String type3;
     @Excel(name = "看课中断")
     private String type4;
-//    @Excel(name = "企业微信员工名称")
-//    private String qwUserName;
+    @Excel(name = "企业微信员工名称")
+    private String qwUserName;
 
     private Long userId;
     @Excel(name = "员工名称")

+ 127 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/HyWorkTaskMapper.java

@@ -0,0 +1,127 @@
+package com.fs.qw.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 企微任务看板Mapper接口
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
+    /**
+     * 查询企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 企微任务看板
+     */
+    QwWorkTask selectQwWorkTaskById(Long id);
+
+    /**
+     * 查询企微任务看板列表
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 企微任务看板集合
+     */
+    List<QwWorkTask> selectQwWorkTaskList(QwWorkTask qwWorkTask);
+
+    /**
+     * 新增企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int insertQwWorkTask(QwWorkTask qwWorkTask);
+
+    /**
+     * 修改企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int updateQwWorkTask(QwWorkTask qwWorkTask);
+
+    /**
+     * 删除企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 结果
+     */
+    int deleteQwWorkTaskById(Long id);
+
+    /**
+     * 批量删除企微任务看板
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteQwWorkTaskByIds(Long[] ids);
+
+    void insertQwWorkTaskBatch(@Param("qwWorkTasks")List<QwWorkTask> qwWorkTasks);
+    @Select({"<script> " +
+             "select t.*,e.`name`,qw.qw_user_name from qw_work_task t LEFT JOIN qw_external_contact e on e.id=t.ext_id  LEFT JOIN qw_user qw ON qw.id = t.qw_user_id  "+
+             "where DATE(t.create_time) = CURDATE()" +
+            "            <if test=\"extId != null \"> and t.ext_id = #{extId}</if>\n" +
+            "            <if test=\"qwUserId != null \"> and t.qw_user_id = #{qwUserId}</if>\n" +
+            "            <if test=\"type != null \"> and t.type = #{type}</if>\n" +
+            "            <if test=\"status != null \"> and t.status = #{status}</if>\n" +
+            "            <if test=\"score != null \"> and t.score = #{score}</if>\n" +
+            "            <if test=\"sopId != null  and sopId != ''\"> and t.sop_id = #{sopId}</if>\n" +
+            "            <if test=\"companyId != null \"> and t.company_id = #{companyId}</if>\n" +
+            "            <if test=\"companyUserId != null \"> and t.company_user_id = #{companyUserId}</if>\n" +
+            " " +
+            " " +
+            "order by t.score desc,t.id desc "+
+            "</script>"})
+    List<QwWorkTaskListVO> selectQwWorkTaskListVO(QwWorkTaskListParam qwWorkTask);
+    List<QwWorkTaskListVO> selectQwWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
+
+    @Select("select ext_id from qw_work_task where type=2 and DATE(create_time) = CURDATE() ")
+    List<Long> selectQwWorkTaskByType();
+
+    @Select("select id,ext_id from qw_work_task where type=2 and status=0 and DATE(create_time) = CURDATE() ")
+    List<QwWorkTask> selectQwWorkTaskByTypeStatus();
+    @Update({
+            "<script>",
+            "UPDATE qw_work_task",
+            "SET status = 2",
+            "WHERE id IN",
+            "<foreach item='id' collection='overIds' open='(' separator=',' close=')'>",
+            "   #{id}",
+            "</foreach>",
+            "</script>"
+    })
+    void updateQwWorkTaskStatus(@Param("overIds")List<Long> overIds);
+
+    @Select("select id,ext_id from qw_work_task where type=2 and status=1 and DATE(create_time) = CURDATE() ")
+    List<QwWorkTask> selectQwWorkTaskByTypeStatus1();
+    @Update({
+            "<script>",
+            "UPDATE qw_work_task",
+            "SET status = 3",
+            "WHERE id IN",
+            "<foreach item='id' collection='overIds' open='(' separator=',' close=')'>",
+            "   #{id}",
+            "</foreach>",
+            "</script>"
+    })
+    void updateQwWorkTaskStatus1(List<Long> overIds1);
+
+    /**
+     * 查询企微任务看板
+     * @param params 参数
+     * @return list
+     */
+    List<QwWorkTask> selectQwWorkTaskListByMap(@Param("params") Map<String, Object> params);
+
+    Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
+}

+ 222 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwWatchLogExMapper.java

@@ -0,0 +1,222 @@
+package com.fs.qw.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.qw.domain.QwWatchLog;
+import com.fs.qw.param.QwWatchLogStatisticsListParam;
+import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
+import com.fs.qw.vo.QwWatchLogStatisticsListVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 企微看课Mapper接口
+ *
+ * @author fs
+ * @date 2025-03-08
+ */
+public interface QwWatchLogExMapper extends BaseMapper<QwWatchLog>{
+    /**
+     * 查询企微看课
+     *
+     * @param id 企微看课主键
+     * @return 企微看课
+     */
+    QwWatchLog selectQwWatchLogById(Long id);
+
+    /**
+     * 查询企微看课列表
+     *
+     * @param qwWatchLog 企微看课
+     * @return 企微看课集合
+     */
+    List<QwWatchLog> selectQwWatchLogList(QwWatchLog qwWatchLog);
+
+    /**
+     * 新增企微看课
+     *
+     * @param qwWatchLog 企微看课
+     * @return 结果
+     */
+    int insertQwWatchLog(QwWatchLog qwWatchLog);
+    void insertQwWatchLogBatch(@Param("watchLogs")List<QwWatchLog> qwWatchLog);
+    /**
+     * 修改企微看课
+     *
+     * @param qwWatchLog 企微看课
+     * @return 结果
+     */
+    int updateQwWatchLog(QwWatchLog qwWatchLog);
+
+    /**
+     * 删除企微看课
+     *
+     * @param id 企微看课主键
+     * @return 结果
+     */
+    int deleteQwWatchLogById(Long id);
+
+    /**
+     * 批量删除企微看课
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteQwWatchLogByIds(Long[] ids);
+    @Select({"<script> " +
+            "SELECT\n" +
+            "    qec.qw_user_id id,\n" +
+            "    qu.qw_user_name AS qw_user_name, \n" +
+            "    DATE(qec.create_time) AS create_time, \n" +
+            "    COUNT(1) AS line,\n" +
+            "    COUNT(CASE WHEN qec.is_interact = 1 THEN 1 END) AS interact,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 1 THEN 1 END) AS A,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 2 THEN 1 END) AS B,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 3 THEN 1 END) AS C,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 4 THEN 1 END) AS D,\n" +
+            "    COUNT(CASE WHEN qec.fs_user_id IS NOT NULL THEN 1 END) AS sign,\n" +
+            "    COUNT(CASE WHEN qec.`status` =3 THEN 1 END) AS los,\n" +
+            "    COUNT(CASE WHEN qec.`status` IN (4, 5,6) THEN 1 END) AS del\n" +
+            "FROM\n" +
+            "    qw_external_contact qec\n" +
+            "JOIN\n" +
+            "    qw_user qu ON qec.qw_user_id = qu.id \n" +
+            "WHERE\n" +
+            "    DATE(qec.create_time) &gt;= DATE(#{sTime}) and  DATE(qec.create_time) &lt;= DATE(#{eTime}) and qec.company_id =#{companyId} " +
+            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
+            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "<if test ='ids !=null and ids!=\"\"'>\n" +
+            "   and qec.qw_user_id in (${ids})\n" +
+            "</if>" +
+            "GROUP BY\n" +
+            "    qec.qw_user_id, DATE(qec.create_time) \n" +
+            "ORDER BY\n" +
+            "    DATE(qec.create_time) "+
+            "</script>"})
+    List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAnd(QwWatchLogStatisticsListParam param);
+    @Select("select \n" +
+            "COUNT(CASE WHEN day = 0 and status in (1,2) THEN 1 END) AS firstOnline,\n" +
+            "COUNT(CASE WHEN day = 0 and status=2 THEN 1 END) AS firstOver,\n" +
+            "COUNT(CASE WHEN day = 1 and status in (1,2) THEN 1 END) AS d1Online,\n" +
+            "COUNT(CASE WHEN day = 1 and status=2 THEN 1 END) AS d1Over\n" +
+            " from qw_watch_log where qw_user_id=#{id} and DATE(line_time) = DATE(#{createTime})")
+    QwWatchLogStatisticsListVO selectQwWatchLogByQwUserId(@Param("id")Long id,@Param("createTime") Date createTime);
+
+    List<QwWatchLogStatisticsListVO> selectQwWatchLogByCompanyUserId(
+            @Param("companyId") Long companyId,
+            @Param("companyUserId") Long companyUserId,
+                                                                     @Param("sTime") Date sTime,
+                                                                     @Param("dTime") Date dTime,
+                                                                     @Param("project") Long project,
+                                                                     @Param("courseId") Long courseId,
+                                                                     @Param("videoId") Long videoId
+    );
+
+
+    @Select("SELECT count(1) from qw_watch_log where ext_id=#{id} and `day`=0 ")
+    int selectQwWatchLogIsFirst(Long id);
+
+    @Select("SELECT count(1) from qw_watch_log where fs_user_id=#{userId} and `day`=0")
+    int selectQwWatchLogIsFirstByUserId(Long userId);
+
+    @Select("select \n" +
+            "COUNT(CASE WHEN day = 0 and status in (1,2) THEN 1 END) AS firstOnline,\n" +
+            "COUNT(CASE WHEN day = 0 and status=2 THEN 1 END) AS firstOver,\n" +
+            "COUNT(CASE WHEN day = 1 and status in (1,2) THEN 1 END) AS d1Online,\n" +
+            "COUNT(CASE WHEN day = 1 and status=2 THEN 1 END) AS d1Over,\n" +
+            "COUNT(CASE WHEN day = 2 and status in (1,2) THEN 1 END) AS d2Online,\n" +
+            "COUNT(CASE WHEN day = 2 and status=2 THEN 1 END) AS d2Over,\n" +
+            "COUNT(CASE WHEN day = 3 and status in (1,2) THEN 1 END) AS d3Online,\n" +
+            "COUNT(CASE WHEN day = 3 and status=2 THEN 1 END) AS d3Over,\n" +
+            "COUNT(CASE WHEN day = 4 and status in (1,2) THEN 1 END) AS d4Online,\n" +
+            "COUNT(CASE WHEN day = 4 and status=2 THEN 1 END) AS d4Over,\n" +
+            "COUNT(CASE WHEN day = 5 and status in (1,2) THEN 1 END) AS d5Online,\n" +
+            "COUNT(CASE WHEN day = 5 and status=2 THEN 1 END) AS d5Over,\n" +
+            "COUNT(CASE WHEN day = 6 and status in (1,2) THEN 1 END) AS d6Online,\n" +
+            "COUNT(CASE WHEN day = 6 and status=2 THEN 1 END) AS d6Over,\n" +
+            "COUNT(CASE WHEN day = 7 and status in (1,2) THEN 1 END) AS d7Online,\n" +
+            "COUNT(CASE WHEN day = 7 and status=2 THEN 1 END) AS d7Over,\n" +
+            "COUNT(CASE WHEN day = 8 and status in (1,2) THEN 1 END) AS d8Online,\n" +
+            "COUNT(CASE WHEN day = 8 and status=2 THEN 1 END) AS d8Over,\n" +
+            "COUNT(CASE WHEN day = 9 and status in (1,2) THEN 1 END) AS d9Online,\n" +
+            "COUNT(CASE WHEN day = 9 and status=2 THEN 1 END) AS d9Over,\n" +
+            "COUNT(CASE WHEN day = 10 and status in (1,2) THEN 1 END) AS d10Online,\n" +
+            "COUNT(CASE WHEN day = 10 and status=2 THEN 1 END) AS d10Over,\n" +
+            "COUNT(CASE WHEN day = 11 and status in (1,2) THEN 1 END) AS d11Online,\n" +
+            "COUNT(CASE WHEN day = 11 and status=2 THEN 1 END) AS d11Over,\n" +
+            "COUNT(CASE WHEN day = 12 and status in (1,2) THEN 1 END) AS d12Online,\n" +
+            "COUNT(CASE WHEN day = 12 and status=2 THEN 1 END) AS d12Over,\n" +
+            "COUNT(CASE WHEN day = 13 and status in (1,2) THEN 1 END) AS d13Online,\n" +
+            "COUNT(CASE WHEN day = 13 and status=2 THEN 1 END) AS d13Over,\n" +
+            "COUNT(CASE WHEN day = 14 and status in (1,2) THEN 1 END) AS d14Online,\n" +
+            "COUNT(CASE WHEN day = 14 and status=2 THEN 1 END) AS d14Over,\n" +
+            "COUNT(CASE WHEN day = 15 and status in (1,2) THEN 1 END) AS d15Online,\n" +
+            "COUNT(CASE WHEN day = 15 and status=2 THEN 1 END) AS d15Over,\n" +
+            "COUNT(CASE WHEN day = 16 and status in (1,2) THEN 1 END) AS d16Online,\n" +
+            "COUNT(CASE WHEN day = 16 and status=2 THEN 1 END) AS d16Over,\n" +
+            "COUNT(CASE WHEN day = 17 and status in (1,2) THEN 1 END) AS d17Online,\n" +
+            "COUNT(CASE WHEN day = 17 and status=2 THEN 1 END) AS d17Over,\n" +
+            "COUNT(CASE WHEN day = 18 and status in (1,2) THEN 1 END) AS d18Online,\n" +
+            "COUNT(CASE WHEN day = 18 and status=2 THEN 1 END) AS d18Over,\n" +
+            "COUNT(CASE WHEN day = 19 and status in (1,2) THEN 1 END) AS d19Online,\n" +
+            "COUNT(CASE WHEN day = 19 and status=2 THEN 1 END) AS d19Over,\n" +
+            "COUNT(CASE WHEN day = 20 and status in (1,2) THEN 1 END) AS d20Online,\n" +
+            "COUNT(CASE WHEN day = 20 and status=2 THEN 1 END) AS d20Over,\n" +
+            "COUNT(CASE WHEN day = 21 and status in (1,2) THEN 1 END) AS d21Online,\n" +
+            "COUNT(CASE WHEN day = 21 and status=2 THEN 1 END) AS d21Over,\n" +
+            "COUNT(CASE WHEN day = 22 and status in (1,2) THEN 1 END) AS d22Online,\n" +
+            "COUNT(CASE WHEN day = 22 and status=2 THEN 1 END) AS d22Over,\n" +
+            "COUNT(CASE WHEN day = 23 and status in (1,2) THEN 1 END) AS d23Online,\n" +
+            "COUNT(CASE WHEN day = 23 and status=2 THEN 1 END) AS d23Over,\n" +
+            "COUNT(CASE WHEN day = 24 and status in (1,2) THEN 1 END) AS d24Online,\n" +
+            "COUNT(CASE WHEN day = 24 and status=2 THEN 1 END) AS d24Over,\n" +
+            "COUNT(CASE WHEN day = 25 and status in (1,2) THEN 1 END) AS d25Online,\n" +
+            "COUNT(CASE WHEN day = 25 and status=2 THEN 1 END) AS d25Over,\n" +
+            "COUNT(CASE WHEN day = 26 and status in (1,2) THEN 1 END) AS d26Online,\n" +
+            "COUNT(CASE WHEN day = 26 and status=2 THEN 1 END) AS d26Over,\n" +
+            "COUNT(CASE WHEN day = 27 and status in (1,2) THEN 1 END) AS d27Online,\n" +
+            "COUNT(CASE WHEN day = 27 and status=2 THEN 1 END) AS d27Over,\n" +
+            "COUNT(CASE WHEN day = 28 and status in (1,2) THEN 1 END) AS d28Online,\n" +
+            "COUNT(CASE WHEN day = 28 and status=2 THEN 1 END) AS d28Over,\n" +
+            "COUNT(CASE WHEN day = 29 and status in (1,2) THEN 1 END) AS d29Online,\n" +
+            "COUNT(CASE WHEN day = 29 and status=2 THEN 1 END) AS d29Over,\n" +
+            "COUNT(CASE WHEN day = 30 and status in (1,2) THEN 1 END) AS d30Online,\n" +
+            "COUNT(CASE WHEN day = 30 and status=2 THEN 1 END) AS d30Over" +
+            " from qw_watch_log where qw_user_id=#{id} and DATE(line_time) = DATE(#{createTime})")
+    QwWatchLogAllStatisticsListVO selectQwWatchLogAllStatisticsListVO(@Param("id")Long id,@Param("createTime") Date createTime);
+
+
+    List<QwWatchLogAllStatisticsListVO> selectQwWatchLogAllStatisticsListVONew(@Param("companyUserId")Long companyUserId,
+                                                                               @Param("companyId")Long companyId,
+                                                                               @Param("sDate") Date sDate, @Param("eDate") Date eDate,
+                                                                               @Param("project") Long project,
+                                                                               @Param("courseId") Long courseId,
+                                                                               @Param("videoId") Long videoId);
+    @Select({"<script> " +
+            "SELECT\n" +
+            "    qec.qw_user_id id,\n" +
+            "    qu.qw_user_name AS qw_user_name, \n" +
+            "    DATE(qec.create_time) AS create_time, \n" +
+            "    COUNT(1) AS line\n" +
+            "FROM\n" +
+            "    qw_external_contact qec\n" +
+            "JOIN\n" +
+            "    qw_user qu ON qec.qw_user_id = qu.id \n" +
+            "WHERE\n" +
+            "    DATE(qec.create_time) &gt;= DATE(#{sTime}) and  DATE(qec.create_time) &lt;= DATE(#{eTime}) and qec.company_id =#{companyId} " +
+            "<if test ='ids !=null and ids!=\"\"'>\n" +
+            "   and qec.qw_user_id in (${ids})\n" +
+            "</if>" +
+            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
+            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "GROUP BY\n" +
+            "    qec.qw_user_id, DATE(qec.create_time) \n" +
+            "ORDER BY\n" +
+            "    DATE(qec.create_time) "+
+            "</script>"})
+    List<QwWatchLogAllStatisticsListVO> selectQwExtCountByDayAndLine(QwWatchLogStatisticsListParam param);
+}

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

@@ -124,4 +124,6 @@ public interface QwWorkTaskMapper extends BaseMapper<QwWorkTask>{
      * @return list
      */
     List<QwWorkTask> selectQwWorkTaskListByMap(@Param("params") Map<String, Object> params);
+
+    Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/qw/param/QwWorkTaskListParam.java

@@ -40,4 +40,7 @@ public class QwWorkTaskListParam {
     private Long companyUserId;
 
     private String title;
+
+    private Long pageNum;
+    private Long pageSize;
 }

+ 110 - 0
fs-service-system/src/main/java/com/fs/qw/service/IHyWorkTaskService.java

@@ -0,0 +1,110 @@
+package com.fs.qw.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.vo.QwWorkTaskListVO;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 企微任务看板Service接口
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+public interface IHyWorkTaskService extends IService<QwWorkTask>{
+    /**
+     * 查询企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 企微任务看板
+     */
+    QwWorkTask selectQwWorkTaskById(Long id);
+
+    /**
+     * 查询企微任务看板列表
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 企微任务看板集合
+     */
+    List<QwWorkTask> selectQwWorkTaskList(QwWorkTask qwWorkTask);
+
+    /**
+     * 新增企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int insertQwWorkTask(QwWorkTask qwWorkTask);
+
+    /**
+     * 修改企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int updateQwWorkTask(QwWorkTask qwWorkTask);
+
+    /**
+     * 批量删除企微任务看板
+     *
+     * @param ids 需要删除的企微任务看板主键集合
+     * @return 结果
+     */
+    int deleteQwWorkTaskByIds(Long[] ids);
+
+    /**
+     * 删除企微任务看板信息
+     *
+     * @param id 企微任务看板主键
+     * @return 结果
+     */
+    int deleteQwWorkTaskById(Long id);
+    /**
+     * 根据用户首次上课情况,添加相应的企微任务。
+     * 用于处理首次课程触发的任务创建逻辑。
+     */
+    void addQwWorkByFirstCourse();
+
+    /**
+     * 根据用户上课情况(通用逻辑),添加相应的企微任务。
+     * 用于处理一般课程事件触发的任务创建逻辑。
+     */
+    void addQwWorkByCourse();
+
+    /**
+     * 根据转化日(或特定业务日期节点)情况,添加相应的企微任务。
+     * 用于处理基于特定日期或转化阶段的任务创建逻辑。
+     */
+    void addQwWorkByConversionDay();
+
+    List<QwWorkTaskListVO> selectQwWorkTaskListVO(QwWorkTaskListParam qwWorkTask);
+    List<QwWorkTaskListVO> selectQwWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
+    Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
+
+    /**
+     * 根据特定课程逻辑(可能与课程编号4或第四阶段相关)添加企微任务。
+     * 用于处理与特定课程标识(如'4')相关的任务创建逻辑。
+     */
+    void addQwWorkByCourse4();
+
+    /**
+     * 删除已完成、过期或不再需要的企微任务。
+     * 用于定期清理或处理生命周期结束的任务。
+     */
+    void delQwWorkTaskByOver();
+    /**
+     * 根据用户最后一次上课时间或课程结束时间情况,添加相应的企微任务。
+     * 用于处理基于课程结束或最后活动时间的任务创建逻辑。
+     */
+    void addQwWorkByCourseLastTime();
+
+    /**
+     * 查询企微任务看板
+     * @param params 参数
+     * @return list
+     */
+    List<QwWorkTask> selectQwWorkTaskListByMap(Map<String, Object> params);
+}

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

@@ -82,6 +82,7 @@ public interface IQwWorkTaskService extends IService<QwWorkTask>{
 
     List<QwWorkTaskListVO> selectQwWorkTaskListVO(QwWorkTaskListParam qwWorkTask);
     List<QwWorkTaskListVO> selectQwWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
+    Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
 
     /**
      * 根据特定课程逻辑(可能与课程编号4或第四阶段相关)添加企微任务。

+ 518 - 0
fs-service-system/src/main/java/com/fs/qw/service/impl/HyWorkTaskServiceImpl.java

@@ -0,0 +1,518 @@
+package com.fs.qw.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.DictUtils;
+import com.fs.company.cache.ICompanyCacheService;
+import com.fs.company.cache.ICompanyUserCacheService;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.course.vo.FsCourseWatchLogTaskVO;
+import com.fs.course.vo.FsQwCourseWatchLogVO;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.mapper.HyWorkTaskMapper;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.mapper.QwWorkTaskMapper;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.service.IHyWorkTaskService;
+import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import com.fs.sop.domain.QwSop;
+import com.fs.sop.domain.SopUserLogsInfo;
+import com.fs.sop.mapper.QwSopMapper;
+import com.fs.sop.mapper.SopUserLogsInfoMapper;
+import com.hc.openapi.tool.util.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 企微任务看板Service业务层处理
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+@Service
+public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkTask> implements IHyWorkTaskService {
+    @Autowired
+    private HyWorkTaskMapper qwWorkTaskMapper;
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    private QwSopMapper qwSopMapper;
+    @Autowired
+    private SopUserLogsInfoMapper sopUserLogsInfoMapper;
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+    @Autowired
+    private ICompanyCacheService companyCacheService;
+    @Autowired
+    private ICompanyUserCacheService companyUserCacheService;
+    /**
+     * 查询企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 企微任务看板
+     */
+    @Override
+    public QwWorkTask selectQwWorkTaskById(Long id)
+    {
+        return baseMapper.selectQwWorkTaskById(id);
+    }
+
+    /**
+     * 查询企微任务看板列表
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 企微任务看板
+     */
+    @Override
+    public List<QwWorkTask> selectQwWorkTaskList(QwWorkTask qwWorkTask)
+    {
+        return baseMapper.selectQwWorkTaskList(qwWorkTask);
+    }
+
+    /**
+     * 新增企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    @Override
+    public int insertQwWorkTask(QwWorkTask qwWorkTask)
+    {
+        qwWorkTask.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertQwWorkTask(qwWorkTask);
+    }
+
+    /**
+     * 修改企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    @Override
+    public int updateQwWorkTask(QwWorkTask qwWorkTask)
+    {
+        qwWorkTask.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateQwWorkTask(qwWorkTask);
+    }
+
+    /**
+     * 批量删除企微任务看板
+     *
+     * @param ids 需要删除的企微任务看板主键
+     * @return 结果
+     */
+    @Override
+    public int deleteQwWorkTaskByIds(Long[] ids)
+    {
+        return baseMapper.deleteQwWorkTaskByIds(ids);
+    }
+
+    /**
+     * 删除企微任务看板信息
+     *
+     * @param id 企微任务看板主键
+     * @return 结果
+     */
+    @Override
+    public int deleteQwWorkTaskById(Long id)
+    {
+        return baseMapper.deleteQwWorkTaskById(id);
+    }
+
+    @Override
+    public void addQwWorkByFirstCourse() {
+            List<FsQwCourseWatchLogVO> fsQwCourseWatchLogVOS = fsCourseWatchLogMapper.selectFsCourseWatchLogByVideoId();
+        ArrayList<QwWorkTask> qwWorkTasks = new ArrayList<>();
+        for (FsQwCourseWatchLogVO vo : fsQwCourseWatchLogVOS) {
+                QwWorkTask qwWorkTask = new QwWorkTask();
+                qwWorkTask.setCreateTime(DateUtils.getNowDate());
+                qwWorkTask.setExtId(vo.getQwExternalContactId());
+                qwWorkTask.setCompanyId(vo.getCompanyId());
+                qwWorkTask.setCompanyUserId(vo.getCompanyUserId());
+                qwWorkTask.setQwUserId(Long.parseLong(vo.getQwUserId()));
+                qwWorkTask.setType(1);
+                qwWorkTask.setStatus(0);
+                qwWorkTask.setTitle(vo.getLogType()==3?"先导课待看课":"先导课完课");
+                qwWorkTask.setScore(vo.getLogType()==3?4:3);
+                qwWorkTasks.add(qwWorkTask);
+            }
+        if (!qwWorkTasks.isEmpty()) {
+            qwWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+        }
+
+    }
+    Integer getQwWorkCourseScore(Integer type, Integer day,Integer leve){
+
+            switch (day){
+                case 1:
+                   return type == 4 ? 12 : 11;
+                case 2:
+                case 3:
+                    return type == 4 ? 9 : 8;
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                    return type == 4 ? 3 : 2;
+                default:
+
+                    return leve==null?0: leve==1?5:leve==2?3:leve==3?1:0;
+            }
+
+
+    }
+    @Autowired
+    FsCourseWatchLogMapper courseWatchLogMapper;
+
+    //
+    @Override
+    public void addQwWorkByCourse() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        LocalDate today = LocalDate.now();
+        List<Long> extIds = qwWorkTaskMapper.selectQwWorkTaskByType();
+        Set<Long> extIdSet = new HashSet<>(extIds);
+        for (QwSop qwSop : qwSops) {
+            if (qwSop.getCourseDay()==null){
+                continue;
+            }
+            Integer courseDay=qwSop.getCourseDay()-1;
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectSopUserLogsInfoBySopId(qwSop.getId());
+            if (qwSopLogs==null || qwSopLogs.isEmpty()) {
+                continue;
+            }
+
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = courseWatchLogMapper.selectFsCourseWatchLogByDaySopId3(qwSop.getId());
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
+                        .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getQwExternalContactId, data -> data,(oldValue, newValue) -> newValue ));
+                FsCourseWatchLogTaskVO fsCourseWatchLog = map.get(qwSopLog.getExternalId());
+                if (fsCourseWatchLog == null) {
+                    continue;
+                }
+                if (extIdSet.contains(fsCourseWatchLog.getQwExternalContactId())) {
+                    continue;
+                }
+                String createTime = qwSopLog.getCreateTime();
+                LocalDate createDate = LocalDate.parse(createTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                // 计算两个日期之间的天数差
+                Integer day = (Math.toIntExact(ChronoUnit.DAYS.between(createDate, today))) + 1 - courseDay;
+                Integer score = getQwWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                QwWorkTask qwWorkTask = new QwWorkTask();
+                qwWorkTask.setCreateTime(DateUtils.getNowDate());
+                qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
+                qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
+                qwWorkTask.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+                qwWorkTask.setSopId(qwSop.getId());
+                qwWorkTask.setQwUserId(fsCourseWatchLog.getQwUserId());
+                qwWorkTask.setType(2);
+                qwWorkTask.setStatus(0);
+                qwWorkTask.setTitle("第"+day+"天"+"待看课");
+                qwWorkTask.setScore(score);
+                qwWorkTasks.add(qwWorkTask);
+            }
+            if (!qwWorkTasks.isEmpty()){
+                qwWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
+
+
+    @Override
+    public void addQwWorkByConversionDay() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRatingNotNull();
+        Map<Integer, Integer> minMap = new HashMap<>();
+        minMap.put(1, 10);
+        minMap.put(2, 6);
+        minMap.put(3, 2);
+        Map<Integer, Integer> MaxMap = new HashMap<>();
+        MaxMap.put(1, 25);
+        MaxMap.put(2, 15);
+        MaxMap.put(3, 5);
+        for (QwSop qwSop : qwSops) {
+            Integer min= qwSop.getMinConversionDay();
+            Integer max = qwSop.getMaxConversionDay();
+            LocalDate today = LocalDate.now();
+            addQwWorkTask(today, min, qwSop, "用户小转",minMap);
+            addQwWorkTask(today, max, qwSop, "用户大转",MaxMap);
+        }
+    }
+
+    @Override
+    public List<QwWorkTaskListVO> selectQwWorkTaskListVO(QwWorkTaskListParam qwWorkTask) {
+        return qwWorkTaskMapper.selectQwWorkTaskListVO(qwWorkTask);
+    }
+
+    @Override
+    public List<QwWorkTaskListVO> selectQwWorkTaskListVONew(QwWorkTaskListParam qwWorkTask) {
+        List<QwWorkTaskListVO> list = qwWorkTaskMapper.selectQwWorkTaskListVONew(qwWorkTask);
+        for (QwWorkTaskListVO item : list) {
+            if(ObjectUtils.isNotNull(item.getCompanyId())){
+                Company company = companyCacheService.selectCompanyById(item.getCompanyId());
+                if(ObjectUtils.isNotNull(company)){
+                    item.setCompanyName(String.format("%s_%d",company.getCompanyName(),company.getCompanyId()));
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getCompanyUserId())){
+                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getCompanyUserId());
+                if(ObjectUtils.isNotNull(companyUser)){
+                    item.setCompanyUserName(String.format("%s_%d", companyUser.getUserName(), companyUser.getUserId()));
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getType())){
+                String kbBusinessType = DictUtils.getDictLabel("sys_qw_work_task_type", String.valueOf(item.getType()));
+                if(StringUtils.isNotBlank(kbBusinessType)){
+                    item.setTypeText(kbBusinessType);
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getStatus())){
+                String kbProcessingStatus = DictUtils.getDictLabel("sys_qw_work_task_status", String.valueOf(item.getStatus()));
+                if(StringUtils.isNotBlank(kbProcessingStatus)){
+                    item.setStatusText(kbProcessingStatus);
+                }
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask) {
+        return qwWorkTaskMapper.selectQwWorkTaskListVONewCount(qwWorkTask);
+    }
+
+    @Override
+    public void addQwWorkByCourse4() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        List<Long> extIds = qwWorkTaskMapper.selectQwWorkTaskByType();
+        Set<Long> extIdSet = new HashSet<>(extIds);
+        LocalDate today = LocalDate.now();
+        for (QwSop qwSop : qwSops) {
+            if (qwSop.getCourseDay()==null){
+                continue;
+            }
+            Integer courseDay=qwSop.getCourseDay()-1;
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectSopUserLogsInfoBySopId(qwSop.getId());
+            if (qwSopLogs==null || qwSopLogs.isEmpty()) {
+                continue;
+            }
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = courseWatchLogMapper.selectFsCourseWatchLogByDaySopId4(qwSop.getId());
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
+                        .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getQwExternalContactId, data -> data,(oldValue, newValue) -> newValue ));
+                FsCourseWatchLogTaskVO fsCourseWatchLog = map.get(qwSopLog.getExternalId());
+                if (fsCourseWatchLog == null) {
+                    continue;
+                }
+                if (extIdSet.contains(fsCourseWatchLog.getQwExternalContactId())) {
+                    continue;
+                }
+                String createTime = qwSopLog.getCreateTime();
+                LocalDate createDate = LocalDate.parse(createTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                Integer day = (Math.toIntExact(ChronoUnit.DAYS.between(createDate, today))) + 1 - courseDay;
+                if (day>7){
+                    continue;
+                }
+                Integer score = getQwWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                QwWorkTask qwWorkTask = new QwWorkTask();
+                qwWorkTask.setCreateTime(DateUtils.getNowDate());
+                qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
+                qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
+                qwWorkTask.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+                qwWorkTask.setSopId(qwSop.getId());
+                qwWorkTask.setQwUserId(fsCourseWatchLog.getQwUserId());
+                qwWorkTask.setType(2);
+                qwWorkTask.setStatus(0);
+                qwWorkTask.setTitle("第"+day+"天"+"看课中断");
+                qwWorkTask.setScore(score);
+                qwWorkTasks.add(qwWorkTask);
+            }
+            if (!qwWorkTasks.isEmpty()){
+                qwWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
+    @Override
+    public void delQwWorkTaskByOver() {
+        List<Long> longs = fsCourseWatchLogMapper.selectFsCourseWatchLogByFinish();
+
+        List<QwWorkTask> qwWorkTasks = qwWorkTaskMapper.selectQwWorkTaskByTypeStatus();
+
+        Set<Long> targetIds = new HashSet<>(longs);
+
+        List<Long> overIds = qwWorkTasks.stream()
+                .map(QwWorkTask::getId)
+                .filter(targetIds::contains)
+                .collect(Collectors.toList());
+        if (overIds.isEmpty()){
+            return;
+        }
+        qwWorkTaskMapper.updateQwWorkTaskStatus(overIds);
+
+    }
+
+    @Override
+    public void addQwWorkByCourseLastTime() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        List<Long> extIds = qwWorkTaskMapper.selectQwWorkTaskByType();
+        SimpleDateFormat sdf = new SimpleDateFormat("HHmm"); // 24小时制,如 1100
+        String timeStr = sdf.format(new Date());
+        int lastTime = Integer.parseInt(timeStr);
+        Set<Long> extIdSet = new HashSet<>(extIds);
+        LocalDate today = LocalDate.now();
+        for (QwSop qwSop : qwSops) {
+            if (qwSop.getCourseDay()==null){
+                continue;
+            }
+            Integer courseDay=qwSop.getCourseDay()-1;
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectSopUserLogsInfoBySopId(qwSop.getId());
+            if (qwSopLogs==null || qwSopLogs.isEmpty()) {
+                continue;
+            }
+
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = courseWatchLogMapper.selectFsCourseWatchLogByDaySopId3LastTime(qwSop.getId(),lastTime);
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
+                        .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getQwExternalContactId, data -> data,(oldValue, newValue) -> newValue ));
+                FsCourseWatchLogTaskVO fsCourseWatchLog = map.get(qwSopLog.getExternalId());
+
+                if (fsCourseWatchLog == null) {
+                    continue;
+                }
+                if (extIdSet.contains(fsCourseWatchLog.getQwExternalContactId())) {
+                    continue;
+                }
+                String createTime = qwSopLog.getCreateTime();
+                LocalDate createDate = LocalDate.parse(createTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+                Integer day = (Math.toIntExact(ChronoUnit.DAYS.between(createDate, today))) + 1 - courseDay;
+                if (day<=7){
+                    continue;
+                }
+                Integer score = getQwWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                QwWorkTask qwWorkTask = new QwWorkTask();
+                qwWorkTask.setCreateTime(DateUtils.getNowDate());
+                qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
+                qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
+                qwWorkTask.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+                qwWorkTask.setSopId(qwSop.getId());
+                qwWorkTask.setQwUserId(fsCourseWatchLog.getQwUserId());
+                qwWorkTask.setType(2);
+                qwWorkTask.setStatus(0);
+                qwWorkTask.setTitle("第"+day+"天"+"待看课");
+                qwWorkTask.setScore(score);
+                qwWorkTasks.add(qwWorkTask);
+
+            }
+            if (!qwWorkTasks.isEmpty()){
+
+                qwWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
+    /**
+     * 查询企微任务看板
+     * @param params 参数
+     * @return list
+     */
+    @Override
+    public List<QwWorkTask> selectQwWorkTaskListByMap(Map<String, Object> params) {
+        return qwWorkTaskMapper.selectQwWorkTaskListByMap(params);
+    }
+
+    /**
+     * 根据SOP执行日志和特定条件,为符合要求的外部联系人添加企业微信工作任务。
+     * <p>
+     * 此方法仅在传入的 `day` 参数大于7时执行。
+     * 它会查询指定 `QwSop` 在过去 `day` 天内的用户执行日志 (`SopUserLogsInfo`)。
+     * 遍历日志,获取关联的外部联系人 (`QwExternalContact`)。
+     * 对联系人进行筛选:必须存在,且其级别 (`level`) 不能为 null、0 或 4。
+     * 对于通过筛选的联系人,调用内部的 `insertQwWorkTask` 方法来创建任务。
+     * </p>
+     *
+     * @param today   当前日期,用于计算查询日志的起始日期。
+     * @param day     回溯的天数。只有当 `day` 大于 7 时,才会执行添加任务的逻辑。
+     * @param qwSop   企业微信SOP(标准操作流程)对象,包含需要查询日志的SOP ID。
+     * @param title   要创建的企业微信工作任务的标题。
+     * @param map     一个映射表,键可能是外部联系人的级别 (`level`),值可能是传递给 `insertQwWorkTask` 的参数(例如优先级或特定配置)。
+     *                如果联系人级别在map中不存在,则使用默认值0。
+     * @author xdd
+     * @version 1.0
+     * @since yyyy-MM-dd // 建议替换为实际的编写或修改日期
+     */
+    private void addQwWorkTask(LocalDate today, Integer day, QwSop qwSop, String title,Map<Integer, Integer> map) {
+        if (day>7){
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            String minDay = today.minusDays(day).format(formatter);
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectDayBySopId(qwSop.getId(), minDay);
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactById(qwSopLog.getExternalId());
+                if (qwExternalContact==null){
+                    continue;
+                }
+                if (qwExternalContact.getLevel()==null||qwExternalContact.getLevel()==4||qwExternalContact.getLevel()==0){
+                    continue;
+                }
+                System.out.println(qwExternalContact.getId()+"ok");
+
+
+                insertQwWorkTask(qwSopLog.getId(),qwExternalContact,3,title,map.getOrDefault(qwExternalContact.getLevel(), 0));
+            }
+        }
+    }
+
+
+    private void insertQwWorkTask(String sopId,QwExternalContact qwExternalContact, Integer type, String title, Integer score) {
+        QwWorkTask qwWorkTask = new QwWorkTask();
+        qwWorkTask.setCreateTime(DateUtils.getNowDate());
+        qwWorkTask.setExtId(qwExternalContact.getId());
+        qwWorkTask.setCompanyId(qwExternalContact.getCompanyId());
+        qwWorkTask.setCompanyUserId(qwExternalContact.getCompanyUserId());
+        qwWorkTask.setQwUserId(qwExternalContact.getQwUserId());
+        qwWorkTask.setSopId(sopId);
+        qwWorkTask.setType(type);
+        qwWorkTask.setStatus(0);
+        qwWorkTask.setTitle(title);
+        qwWorkTask.setScore(score);
+        baseMapper.insertQwWorkTask(qwWorkTask);
+    }
+
+
+}

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

@@ -294,6 +294,11 @@ public class QwWorkTaskServiceImpl extends ServiceImpl<QwWorkTaskMapper, QwWorkT
         return list;
     }
 
+    @Override
+    public Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask) {
+        return qwWorkTaskMapper.selectQwWorkTaskListVONewCount(qwWorkTask);
+    }
+
     @Override
     public void addQwWorkByCourse4() {
         List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();

+ 3 - 0
fs-service-system/src/main/java/com/fs/qw/vo/QwWorkTaskListVO.java

@@ -49,4 +49,7 @@ public class QwWorkTaskListVO implements Serializable {
     private String companyUserName;
 
     private String title;
+
+    private Long pageNum;
+    private Long pageSize;
 }

+ 38 - 0
fs-service-system/src/main/java/com/fs/statis/cache/IStatisticsCacheService.java

@@ -0,0 +1,38 @@
+package com.fs.statis.cache;
+
+import com.fs.statis.dto.*;
+
+import java.util.List;
+
+public interface IStatisticsCacheService {
+    /**
+     * 分析概览 - 经销商统计
+     */
+    DealerAggregatedDTO dealerAggregated();
+
+    /**
+     * 消费余额
+     */
+    ConsumptionBalanceDataDTO rechargeConsumption();
+
+    /**
+     * 分析概览
+     */
+    AnalysisPreviewDTO analysisPreview(AnalysisPreviewQueryDTO param);
+
+
+    /**
+     * 会员观看、完播人数趋势图
+     * @param param 请求参数
+     * @return 会员观看、完播人数趋势图
+     */
+    List<WatchEndPlayTrendDTO> watchEndPlayTrend(AnalysisPreviewQueryDTO param);
+
+    /**
+     * 经销商会员观看TOP10
+     * @param param 请求参数
+     * @return TOP10
+     * @return
+     */
+    List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param);
+}

+ 80 - 0
fs-service-system/src/main/java/com/fs/statis/cache/impl/StatisticsCacheServiceImpl.java

@@ -0,0 +1,80 @@
+package com.fs.statis.cache.impl;
+
+import com.fs.statis.cache.IStatisticsCacheService;
+import com.fs.statis.dto.*;
+import com.fs.statis.service.IStatisticsService;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class StatisticsCacheServiceImpl implements IStatisticsCacheService {
+    @Autowired
+    private IStatisticsService statisticsService;
+
+    private static final Cache<Integer, DealerAggregatedDTO> DEALER_AGGREGATED_DTO_CACHE = Caffeine.newBuilder()
+            .maximumSize(1000)
+            .expireAfterWrite(10, TimeUnit.MINUTES)
+            .build();
+
+    private static final Cache<Integer, ConsumptionBalanceDataDTO> RECHARGE_CONSUMPTION = Caffeine.newBuilder()
+            .maximumSize(1000)
+            .expireAfterWrite(10, TimeUnit.MINUTES)
+            .build();
+
+    private static final Cache<Integer, AnalysisPreviewDTO> ANALYSIS_PREVIEW_CONSUMPTION = Caffeine.newBuilder()
+            .maximumSize(1000)
+            .expireAfterWrite(10, TimeUnit.MINUTES)
+            .build();
+
+    private static final Cache<Integer, List<WatchEndPlayTrendDTO>> WATCH_END_PLAY_TREND = Caffeine.newBuilder()
+            .maximumSize(1000)
+            .expireAfterWrite(10, TimeUnit.MINUTES)
+            .build();
+
+    private static final Cache<Integer, List<DeaMemberTopTenDTO>> DEA_MEMBER_TOP_TEN = Caffeine.newBuilder()
+            .maximumSize(1000)
+            .expireAfterWrite(10, TimeUnit.MINUTES)
+            .build();
+
+    @Override
+    public DealerAggregatedDTO dealerAggregated() {
+        return DEALER_AGGREGATED_DTO_CACHE.get(1,e->{
+            return statisticsService.dealerAggregated();
+        });
+    }
+
+    @Override
+    public ConsumptionBalanceDataDTO rechargeConsumption() {
+        return RECHARGE_CONSUMPTION.get(1,e->{
+            return statisticsService.rechargeConsumption();
+        });
+    }
+
+    @Override
+    public AnalysisPreviewDTO analysisPreview(AnalysisPreviewQueryDTO param) {
+        return ANALYSIS_PREVIEW_CONSUMPTION.get(1,e->{
+            return statisticsService.analysisPreview(param);
+        });
+    }
+
+    @Override
+    public List<WatchEndPlayTrendDTO> watchEndPlayTrend(AnalysisPreviewQueryDTO param) {
+        return WATCH_END_PLAY_TREND.get(param.getType(),e->{
+            return statisticsService.watchEndPlayTrend(param);
+        });
+    }
+
+    @Override
+    public List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param) {
+        return DEA_MEMBER_TOP_TEN.get(param.getType(),e->{
+            return statisticsService.deaMemberTopTen(param);
+        });
+    }
+
+}

+ 91 - 0
fs-service-system/src/main/java/com/fs/statis/dto/AnalysisPreviewDTO.java

@@ -0,0 +1,91 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 用户活动统计数据传输对象
+ * <p>
+ * 该类用于封装和传输用户活动相关的统计数据,
+ * 包括观看人数、完成人数、完成率、观看次数、完成次数、
+ * 答题人数、答对人数、正确率、奖励次数和奖励金额等信息。
+ * </p>
+ *
+ * @author xdd
+ * @date 2025-02-27
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AnalysisPreviewDTO implements Serializable {
+
+    /**
+     * 观看人数
+     * <p>统计周期内,观看了活动内容的不重复用户数量。</p>
+     */
+    private long watchUserCount;
+
+    /**
+     * 完播人数
+     * <p>统计周期内,完成了指定活动目标的不重复用户数量。</p>
+     */
+    private long completedUserCount;
+
+    /**
+     * 完播率
+     * <p>通常指 完成用户数 / 观看用户数 * 100%。注意:此处原始数据类型为字符串,可能需要业务层进行转换或特殊处理。</p>
+     */
+    private String completedRate;
+
+    /**
+     * 观看次数
+     * <p>统计周期内,活动内容被观看的总次数(可能包含重复观看)。</p>
+     */
+    private long watchCount;
+
+    /**
+     * 完播次数
+     * <p>统计周期内,活动目标被完成的总次数。</p>
+     */
+    private long completedCount;
+
+    /**
+     * 视频完播率
+     */
+    private String watchRate;
+
+    /**
+     * 答题人数 (或参与成员数)
+     * <p>统计周期内,参与了答题环节的不重复用户数量。</p>
+     */
+    private long answerMemberCount;
+
+    /**
+     * 正确人数
+     * <p>统计周期内,至少答对一题的不重复用户数量(具体定义需根据业务确认)。</p>
+     */
+    private long correctUserCount;
+
+    /**
+     * 正确率
+     * <p>通常指 答对题目总数 / 回答题目总数 * 100% 或其他业务定义。注意:此处原始数据类型为字符串,可能需要业务层进行转换或特殊处理。</p>
+     */
+    private String correctRate;
+
+    /**
+     * 答题红包个数
+     * <p>统计周期内,发放奖励的总次数或总数量。</p>
+     */
+    private long rewardCount;
+
+    /**
+     * 答题红包金额(元)
+     * <p>统计周期内,发放奖励的总金额。具体单位(如元、分)需根据业务确定。</p>
+     */
+    private BigDecimal rewardMoney;
+
+}

+ 37 - 0
fs-service-system/src/main/java/com/fs/statis/dto/AnalysisPreviewQueryDTO.java

@@ -0,0 +1,37 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Data
+@Setter
+public class AnalysisPreviewQueryDTO implements Serializable {
+
+    /**
+     * 0 今日,1昨日,2本周,3本月,4上月
+     */
+    private Integer type;
+
+    /**
+     * 0 观看人数,1 完播人数,2 答题人数,3 正确人数
+     */
+    private Integer statisticalType;
+    /**
+     * 开始时间
+     */
+    private String startTime;
+    /**
+     * 结束时间
+     */
+    private String endTime;
+
+    /**
+     * 排序 DESC ASC
+     */
+    private String sort;
+
+    private Integer dateType;
+
+}

+ 17 - 0
fs-service-system/src/main/java/com/fs/statis/dto/AuthorizationInfoDTO.java

@@ -0,0 +1,17 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class AuthorizationInfoDTO implements Serializable {
+    /**
+     * 平台今日看课人数
+     */
+    private Long todayWatchUserCount;
+    /**
+     * 版本上限
+     */
+    private Long versionLimit;
+}

+ 28 - 0
fs-service-system/src/main/java/com/fs/statis/dto/ConsumptionBalanceDataDTO.java

@@ -0,0 +1,28 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 消费余额
+ */
+@Data
+public class ConsumptionBalanceDataDTO implements Serializable {
+    /**
+     * 当前账户余额。
+     * 数值表示,单位可能是分或其他约定单位。
+     */
+    private Long balance;
+    /**
+     * 今日消费金额。
+     * 当天的总消费数额。
+     */
+    private Integer todayComsumption;
+    /**
+     * 昨日消费金额。
+     * 昨天的总消费数额。
+     */
+    private Integer yesterdayComsumption;
+
+}

+ 63 - 0
fs-service-system/src/main/java/com/fs/statis/dto/CourseStatsDTO.java

@@ -0,0 +1,63 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+
+import java.io.Serializable; // DTOs 通常建议实现 Serializable
+
+/**
+ * 课程统计数据传输对象 (DTO).
+ * <p>
+ * 用于封装课程相关的用户统计数据,如观看人数、完成人数等,以及课程的基本信息。
+ * </p>
+ *
+ * @author xdd
+ * @since 2025-02-27 // 通常使用 @since 标记版本或首次添加日期
+ * @version 1.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class CourseStatsDTO implements Serializable {
+
+    private static final long serialVersionUID = 1L; // Serializable 建议添加 serialVersionUID
+
+    /**
+     * 观看用户数
+     * <p>统计有多少不同的用户观看了该课程(或其部分内容)。</p>
+     */
+    private Integer watchUserCount;
+
+    /**
+     * 完成用户数
+     * <p>统计有多少不同的用户完成了该课程的学习要求。</p>
+     */
+    private Integer completedUserCount;
+
+    /**
+     * 答题用户数
+     * <p>统计有多少不同的用户参与了该课程相关的答题活动。</p>
+     */
+    private Integer answerUserCount;
+
+    /**
+     * 答对用户数
+     * <p>统计有多少不同的用户在答题活动中至少答对了一定数量或比例的题目(具体定义取决于业务逻辑)。</p>
+     * <p><b>注意:</b>也可能指完全答对所有题目的用户数,需根据实际业务场景确认。</p>
+     */
+    private Integer correctUserCount;
+
+    /**
+     * 课程ID
+     * <p>课程的唯一标识符。</p>
+     */
+    private Long courseId; // 假设课程ID是长整型,如果可能是其他类型(如String),请修改
+
+    /**
+     * 课程名称
+     * <p>课程的标题或名称。</p>
+     */
+    private String courseName;
+
+}

+ 22 - 0
fs-service-system/src/main/java/com/fs/statis/dto/DeaMemberTopTenDTO.java

@@ -0,0 +1,22 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DeaMemberTopTenDTO implements Serializable {
+    /**
+     * 公司id
+     */
+    private Long companyId;
+    /**
+     * 公司名称
+     */
+    private String companyName;
+
+    /**
+     * 观看人数
+     */
+    private Long watchUserCount;
+}

+ 30 - 0
fs-service-system/src/main/java/com/fs/statis/dto/DealerAggregatedDTO.java

@@ -0,0 +1,30 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DealerAggregatedDTO implements Serializable {
+    /**
+     * 经销商数量
+     */
+    private Long dealderCount;
+    /**
+     * 群管数量
+     */
+    private Long groupMgrCount;
+    /**
+     * 会员总数量
+     */
+    private Long memberCount;
+    /**
+     * 正常会员数量
+     */
+    private Long normalNum;
+    /**
+     * 黑名单会员数量
+     */
+    private Long blackNum;
+
+}

+ 29 - 0
fs-service-system/src/main/java/com/fs/statis/dto/WatchEndPlayTrendDTO.java

@@ -0,0 +1,29 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 会员观看、完播人数趋势图
+ */
+@Data
+public class WatchEndPlayTrendDTO implements Serializable {
+
+    /**
+     * 日期 如果是今日,昨日,都是小时
+     */
+    private String startDate;
+    /**
+     * x轴
+     */
+    private String x;
+    /**
+     * 总数
+     */
+    private Long watchUserCount;
+    /**
+     * 完播数
+     */
+    private Long completedUserCount;
+}

+ 107 - 0
fs-service-system/src/main/java/com/fs/statis/mapper/ConsumptionBalanceMapper.java

@@ -0,0 +1,107 @@
+package com.fs.statis.mapper;
+
+import com.fs.statis.dto.*;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public interface ConsumptionBalanceMapper {
+    /**
+     * 分析概览 - 经销商统计
+     */
+    AnalysisPreviewDTO analysisPreview(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 消费余额
+     */
+    ConsumptionBalanceDataDTO rechargeConsumption();
+
+    DealerAggregatedDTO dealerAggregated();
+
+    /**
+     * 观看人数
+     * @param queryDTO 请求参数
+     * @return 观看人数
+     */
+    Long queryWatchUserCount(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 完播人数
+     * @param queryDTO 请求参数
+     * @return  完播人数
+     */
+    Long queryCompletedUserCount(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 观看次数
+     * @param queryDTO 请求参数
+     * @return 观看次数
+     */
+    Long queryWatchCount(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 完播次数
+     * @param queryDTO 请求参数
+     * @return 完播次数
+     */
+    Long queryCompletedCount(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 答题人数
+     * @param queryDTO 请求参数
+     * @return 答题人数
+     */
+    Long queryAnswerMemberCount(AnalysisPreviewQueryDTO queryDTO);
+
+
+    /**
+     * 正确人数
+     * @param queryDTO 请求参数
+     * @return 正确人数
+     */
+    Long queryCorrectUserCount(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 答题红包个数
+     * @param queryDTO 请求参数
+     * @return 奖励人数
+     * @return
+     */
+    Long queryRewardCount(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 答题红包金额
+     * @param queryDTO 请求参数
+     * @return 奖励金额
+     */
+    BigDecimal queryRewardMoney(AnalysisPreviewQueryDTO queryDTO);
+
+    /**
+     * 短信余额
+     * @return 短信余额
+     */
+    Long smsBalance();
+
+    AuthorizationInfoDTO authorizationInfo();
+
+    /**
+     * 会员观看、完播人数趋势图
+     * @param param 请求参数
+     * @return 观看完播趋势
+     */
+    List<WatchEndPlayTrendDTO> watchEndPlayTrend(AnalysisPreviewQueryDTO param);
+
+    /**
+     * 经销商会员观看TOP10
+     * @param param 请求参数
+     * @return TOP10
+     */
+    List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param);
+
+    /**
+     * 课程观看TOP10
+     * @param param 请求参数
+     * @return TOP10
+     */
+    List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param);
+}

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

@@ -0,0 +1,58 @@
+package com.fs.statis.service;
+
+import com.fs.statis.dto.*;
+
+import java.util.List;
+
+/**
+ * 统计接口
+ */
+public interface IStatisticsService {
+    /**
+     * 分析概览 - 经销商统计
+     */
+    DealerAggregatedDTO dealerAggregated();
+
+    /**
+     * 消费余额
+     */
+    ConsumptionBalanceDataDTO rechargeConsumption();
+
+    /**
+     * 分析概览
+     */
+    AnalysisPreviewDTO analysisPreview(AnalysisPreviewQueryDTO param);
+
+    /**
+     * 获取短信余额
+     * @return
+     */
+    Long smsBalance();
+
+    /**
+     * 授权信息
+     * @return
+     */
+    AuthorizationInfoDTO authorizationInfo();
+
+    /**
+     * 会员观看、完播人数趋势图
+     * @param param 请求参数
+     * @return 观看完播趋势
+     */
+    List<WatchEndPlayTrendDTO> watchEndPlayTrend(AnalysisPreviewQueryDTO param);
+
+    /**
+     * 经销商会员观看TOP10
+     * @param param
+     * @return
+     */
+    List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param);
+
+    /**
+     * 课程观看TOP10
+     * @param param
+     * @return
+     */
+    List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param);
+}

+ 177 - 0
fs-service-system/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java

@@ -0,0 +1,177 @@
+package com.fs.statis.service.impl;
+
+import com.fs.company.cache.ICompanyCacheService;
+import com.fs.statis.dto.*;
+import com.fs.statis.mapper.ConsumptionBalanceMapper;
+import com.fs.statis.service.IStatisticsService;
+import com.fs.statis.service.utils.TrendDataFiller;
+import com.fs.store.service.cache.IFsUserCourseCacheService;
+import com.hc.openapi.tool.util.ObjectUtils;
+import com.hc.openapi.tool.util.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class StatisticsServiceImpl implements IStatisticsService {
+    @Autowired
+    private ConsumptionBalanceMapper consumptionBalanceMapper;
+
+    @Autowired
+    private IFsUserCourseCacheService fsUserCourseCacheService;
+
+    @Autowired
+    private ICompanyCacheService companyCacheService;
+    @Override
+    public DealerAggregatedDTO dealerAggregated() {
+        return consumptionBalanceMapper.dealerAggregated();
+    }
+
+    @Override
+    public ConsumptionBalanceDataDTO rechargeConsumption() {
+        return consumptionBalanceMapper.rechargeConsumption();
+    }
+
+    @Override
+    public AnalysisPreviewDTO analysisPreview(AnalysisPreviewQueryDTO param) {
+        AnalysisPreviewDTO dto = new AnalysisPreviewDTO();
+
+        Long watchUserCount = consumptionBalanceMapper.queryWatchUserCount(param);
+        Long completedUserCount = consumptionBalanceMapper.queryCompletedUserCount(param);
+
+        if(watchUserCount == null){
+            watchUserCount = 0L;
+        }
+        if(completedUserCount == null){
+            completedUserCount = 0L;
+        }
+
+        // 观看人数
+        dto.setWatchUserCount(watchUserCount);
+        // 完播人数
+        dto.setCompletedUserCount(completedUserCount);
+
+        // 完播率
+        if(!ObjectUtils.equals(watchUserCount,0L)){
+            BigDecimal multiply = (BigDecimal.valueOf(completedUserCount).divide(BigDecimal.valueOf(watchUserCount))).multiply(BigDecimal.valueOf(100));
+
+            dto.setCompletedRate(multiply.setScale(2,BigDecimal.ROUND_HALF_UP).toPlainString());
+        } else {
+            dto.setCompletedRate("0");
+        }
+
+        Long watchCount = consumptionBalanceMapper.queryWatchCount(param);
+        Long completedCount = consumptionBalanceMapper.queryCompletedCount(param);
+
+        if(watchCount == 0){
+            watchCount = 0L;
+        }
+        if(completedCount == 0){
+            completedCount = 0L;
+        }
+
+        // 观看次数
+        dto.setWatchCount(watchCount);
+        // 完播次数
+        dto.setCompletedCount(completedCount);
+        // 视频完播率
+        if(!ObjectUtils.equals(watchCount, 0L)){
+            BigDecimal multiply = BigDecimal.valueOf(completedCount)
+                    .divide(BigDecimal.valueOf(watchCount))
+                    .multiply(BigDecimal.valueOf(100));
+
+            dto.setCompletedRate(multiply.setScale(2,BigDecimal.ROUND_HALF_UP).toPlainString());
+        } else {
+            dto.setCompletedRate("0");
+        }
+
+        Long answerMemberCount = consumptionBalanceMapper.queryAnswerMemberCount(param);
+        Long correctUserCount = consumptionBalanceMapper.queryCorrectUserCount(param);
+
+        if(answerMemberCount == 0){
+            answerMemberCount = 0L;
+        }
+        if(correctUserCount == 0){
+            correctUserCount = 0L;
+        }
+        // 答题人数
+        dto.setAnswerMemberCount(answerMemberCount);
+        // 正确人数
+        dto.setCorrectUserCount(correctUserCount);
+        // 正确比例
+        if(!ObjectUtils.equals(answerMemberCount, 0L)){
+            BigDecimal multiply = BigDecimal.valueOf(correctUserCount).divide(BigDecimal.valueOf(answerMemberCount)).multiply(BigDecimal.valueOf(100));
+
+            dto.setCorrectRate(multiply.setScale(2, RoundingMode.HALF_UP).toPlainString());
+        } else {
+            dto.setCorrectRate("0");
+        }
+        Long rewardCount = consumptionBalanceMapper.queryRewardCount(param);
+        if(rewardCount == null) {
+            rewardCount = 0L;
+        }
+        BigDecimal rewardMoney = consumptionBalanceMapper.queryRewardMoney(param);
+        if(rewardMoney == null) {
+            rewardMoney = BigDecimal.ZERO;
+        }
+        // 答题红包个数
+        dto.setRewardCount(rewardCount);
+        // 答题红包金额
+        dto.setRewardMoney(rewardMoney);
+
+        return dto;
+    }
+
+    @Override
+    public Long smsBalance() {
+        return consumptionBalanceMapper.smsBalance();
+    }
+
+    @Override
+    public AuthorizationInfoDTO authorizationInfo() {
+        return consumptionBalanceMapper.authorizationInfo();
+    }
+
+    @Override
+    public List<WatchEndPlayTrendDTO> watchEndPlayTrend(AnalysisPreviewQueryDTO param) {
+        List<WatchEndPlayTrendDTO> watchEndPlayTrendDTOS = consumptionBalanceMapper.watchEndPlayTrend(param);
+        // 今日,昨日 格式为24小时
+        if(ObjectUtils.equals(param.getType(),0) || ObjectUtils.equals(param.getType(),1)){
+            watchEndPlayTrendDTOS = TrendDataFiller.fillHourData(watchEndPlayTrendDTOS);
+            // 否则都是按天为维度的时间范围
+        } else {
+            watchEndPlayTrendDTOS = TrendDataFiller.fillDateSegmentData(watchEndPlayTrendDTOS, param.getStartTime(), param.getEndTime());
+        }
+
+        return watchEndPlayTrendDTOS;
+    }
+
+    @Override
+    public List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param) {
+        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = consumptionBalanceMapper.deaMemberTopTen(param);
+        for (DeaMemberTopTenDTO dto : deaMemberTopTenDTOS) {
+            Long companyId = dto.getCompanyId();
+            String companyName = companyCacheService.selectCompanyNameById(companyId);
+            if(StringUtils.isNotBlank(companyName)){
+                dto.setCompanyName(companyName);
+            }
+        }
+        return deaMemberTopTenDTOS;
+    }
+
+    @Override
+    public List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param) {
+        List<CourseStatsDTO> courseStatsDTOS = consumptionBalanceMapper.watchCourseTopTen(param);
+        for (CourseStatsDTO courseStatsDTO : courseStatsDTOS) {
+            String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
+            if(StringUtils.isNotBlank(courseName)){
+                courseStatsDTO.setCourseName(courseName);
+            }
+        }
+        return courseStatsDTOS;
+    }
+}

+ 96 - 0
fs-service-system/src/main/java/com/fs/statis/service/utils/TrendDataFiller.java

@@ -0,0 +1,96 @@
+package com.fs.statis.service.utils;
+
+import com.fs.statis.dto.WatchEndPlayTrendDTO;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * 时序数据补全工具类
+ */
+public class TrendDataFiller {
+
+    /**
+     * 填充24小时类数据
+     * @param data
+     * @return
+     */
+    public static List<WatchEndPlayTrendDTO> fillHourData(List<WatchEndPlayTrendDTO> data){
+
+        boolean[] hourExists = new boolean[24];
+
+        for (WatchEndPlayTrendDTO dto : data) {
+            int hour = Integer.parseInt(dto.getStartDate());
+            if (hour >= 0 && hour < 24) {
+                hourExists[hour] = true;
+                dto.setX(String.valueOf(hour));
+            }
+        }
+
+        // 填充不存在的小时数据
+        for (int i = 0; i < 24; i++) {
+            if (!hourExists[i]) {
+                WatchEndPlayTrendDTO dto = new WatchEndPlayTrendDTO();
+                dto.setX(String.valueOf(i));
+                dto.setWatchUserCount(0L);
+                dto.setCompletedUserCount(0L);
+                data.add(dto);
+            }
+        }
+
+        data.sort((a, b) -> {
+            int intA = Integer.parseInt(a.getX());
+            int intB = Integer.parseInt(b.getX());
+            return Integer.compare(intA, intB);
+        });
+        return data;
+    }
+
+    /**
+     * 填充日期段数据
+     * @param data
+     * @return
+     */
+    public static List<WatchEndPlayTrendDTO> fillDateSegmentData(List<WatchEndPlayTrendDTO> data,String startDate,String endDate){
+        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        LocalDate start = LocalDate.parse(startDate, dateTimeFormatter);
+        LocalDate end = LocalDate.parse(endDate, dateTimeFormatter);
+
+        LocalDate current = start;
+        while (!current.isAfter(end)) {
+            String dateStr = current.format(dateFormatter);
+            boolean found = false;
+
+            for (WatchEndPlayTrendDTO dto : data) {
+                if (dateStr.equals(dto.getStartDate())) {
+                    found = true;
+                    dto.setX(dateStr);
+                    break;
+                }
+            }
+
+            if (!found) {
+                WatchEndPlayTrendDTO dto = new WatchEndPlayTrendDTO();
+                dto.setStartDate(dateStr);
+                dto.setX(dateStr);
+                dto.setWatchUserCount(0L);
+                dto.setCompletedUserCount(0L);
+                data.add(dto);
+            }
+
+            current = current.plusDays(1);
+        }
+
+        data.sort((a, b) -> {
+            LocalDate dateA = LocalDate.parse(a.getX(), dateFormatter);
+            LocalDate dateB = LocalDate.parse(b.getX(), dateFormatter);
+            return dateA.compareTo(dateB);
+        });
+
+        return data;
+    }
+}

+ 27 - 0
fs-service-system/src/main/resources/mapper/course/FsCourseAnswerLogsMapper.xml

@@ -62,6 +62,33 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sTime != null and eTime != null">
                 AND cal.create_time BETWEEN #{sTime} AND #{eTime}
             </if>
+
+        </where>
+
+    </select>
+    <select id="selectFsCourseAnswerLogsListVONewCount" resultType="java.lang.Long">
+        select count(1) from fs_course_answer_logs cal
+        left join fs_user_course uc on cal.course_id=uc.course_id
+        <where>
+            <if test="courseId != null">
+                cal.course_id = #{courseId}
+            </if>
+            <if test="companyUserId != null">
+                AND cal.company_user_id = #{companyUserId}
+            </if>
+            <if test="companyId != null">
+                AND cal.company_id = #{companyId}
+            </if>
+            <if test="isRight != null">
+                AND is_right = #{isRight}
+            </if>
+            <if test="project != null">
+                AND uc.project = #{project}
+            </if>
+            <if test="sTime != null and eTime != null">
+                AND cal.create_time BETWEEN #{sTime} AND #{eTime}
+            </if>
+
         </where>
     </select>
 

+ 14 - 0
fs-service-system/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -627,6 +627,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
         </where>
     </select>
+    <select id="selectFsCourseWatchLogOverStatisticsListVO" resultType="com.fs.course.vo.FsCourseOverVO">
+        select MIN(o.create_time) createTime,ANY_VALUE(qu.qw_user_name) qwUserName ,ext.name externalUserName,ext.create_time userCreateTime from fs_course_watch_log o LEFT JOIN qw_user qu on qu.id=o.qw_user_id LEFT JOIN qw_external_contact ext ON ext.id =  o.qw_external_contact_id where log_type=2 and o.company_id=#{companyId}
+        <if test= 'sTime != null '>
+            and DATE(o.create_time) &gt;= DATE(#{sTime})
+        </if>
+        <if test='eTime != null '>
+            and DATE(o.create_time) &lt;= DATE(#{eTime})
+        </if>
+        <if test ='nickName !=null and nickName!=""'>
+            and qu.qw_user_name like concat( #{nickName}, '%')
+        </if>
+        GROUP BY o.qw_external_contact_id
+    </select>
+
 
     <select id="getWatchLogByFsUser" resultType="com.fs.course.domain.FsCourseWatchLog">
         SELECT

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

@@ -313,4 +313,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </otherwise>
         </choose>
     </select>
+    <select id="selectFsUserCourseListCompanyPVO" resultType="com.fs.course.vo.FsUserCourseListPVO">
+        select c.*,cc.cate_name,ucc.cate_name as sub_cate_name from fs_user_course  c
+        left join fs_user_course_category cc on c.cate_id=cc.cate_id
+        left join fs_user_course_category ucc on  ucc.cate_id = c.sub_cate_id
+        where c.is_del = 0 and
+        FIND_IN_SET(#{maps.companyId}, company_ids)
+        <if test = ' maps.cateId !=null '>
+            and (cc.cate_id =#{maps.cateId} or cc.pid=#{maps.cateId} )
+        </if>
+        <if test = ' maps.courseName!=null and maps.courseName != "" '>
+            and c.course_name like concat('%', #{maps.courseName}, '%')
+        </if>
+        <if test = ' maps.isPrivate !=null '>
+            and c.is_private = #{maps.isPrivate}
+        </if>
+        <if test = ' maps.isShow !=null '>
+            and c.is_show = #{maps.isShow}
+        </if>
+        <if test="maps.project != null">
+            and c.project = #{maps.project}
+        </if>
+        order by c.course_id
+    </select>
 </mapper>

+ 187 - 0
fs-service-system/src/main/resources/mapper/qw/HyWorkTaskMapper.xml

@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.qw.mapper.HyWorkTaskMapper">
+
+    <resultMap type="QwWorkTask" id="QwWorkTaskResult">
+        <result property="id"    column="id"    />
+        <result property="extId"    column="ext_id"    />
+        <result property="qwUserId"    column="qw_user_id"    />
+        <result property="type"    column="type"    />
+        <result property="status"    column="status"    />
+        <result property="remark"    column="remark"    />
+        <result property="score"    column="score"    />
+        <result property="sopId"    column="sop_id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="title"    column="title"    />
+    </resultMap>
+
+    <sql id="selectQwWorkTaskVo">
+        select id, ext_id, qw_user_id, type,title, status, remark, score, sop_id, company_id, company_user_id, create_time, update_time from hy_work_task
+    </sql>
+
+    <select id="selectQwWorkTaskList" parameterType="QwWorkTask" resultMap="QwWorkTaskResult">
+        <include refid="selectQwWorkTaskVo"/>
+        <where>
+            <if test="extId != null "> and ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and type = #{type}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="score != null "> and score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and sop_id = #{sopId}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+        </where>
+    </select>
+
+    <select id="selectQwWorkTaskById" parameterType="Long" resultMap="QwWorkTaskResult">
+        <include refid="selectQwWorkTaskVo"/>
+        where id = #{id}
+    </select>
+    <select id="selectQwWorkTaskListVONew" resultType="com.fs.qw.vo.QwWorkTaskListVO">
+        SELECT t.*
+        FROM hy_work_task t
+        INNER JOIN (
+        SELECT t_inner.id
+        FROM qw_work_task t_inner
+        <where>
+            DATE(t_inner.create_time) = CURDATE()
+            <if test="extId != null "> and t_inner.ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and t_inner.qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and t_inner.type = #{type}</if>
+            <if test="status != null "> and t_inner.status = #{status}</if>
+            <if test="score != null "> and t_inner.score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and t_inner.sop_id = #{sopId}</if>
+            <if test="companyId != null "> and t_inner.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and t_inner.company_user_id = #{companyUserId}</if>
+        </where>
+        ORDER BY t_inner.score DESC, t_inner.id DESC
+        LIMIT ${(pageNum-1)*pageSize}, ${pageSize}
+        ) AS filtered_ids ON t.id = filtered_ids.id
+        ORDER BY t.score DESC, t.id DESC
+    </select>
+
+    <select id="selectQwWorkTaskListByMap" resultType="com.fs.qw.domain.QwWorkTask">
+        select
+            qwt.*
+        from hy_work_task qwt
+        where qwt.qw_user_id = #{params.qwUserId}
+          and date(qwt.create_time) = #{params.date}
+          and qwt.company_id = #{params.companyId}
+          and qwt.company_user_id = #{params.companyUserId}
+          and qwt.status = 0
+    </select>
+    <select id="selectQwWorkTaskListVONewCount" resultType="java.lang.Long">
+        select count(1) from hy_work_task t
+        <where>
+            DATE(t.create_time) = CURDATE()
+            <if test="extId != null "> and t.ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and t.qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and t.type = #{type}</if>
+            <if test="status != null "> and t.status = #{status}</if>
+            <if test="score != null "> and t.score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and t.sop_id = #{sopId}</if>
+            <if test="companyId != null "> and t.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and t.company_user_id = #{companyUserId}</if>
+        </where>
+        order by t.score desc,t.id desc
+    </select>
+
+    <insert id="insertQwWorkTask" parameterType="QwWorkTask" useGeneratedKeys="true" keyProperty="id">
+        insert into hy_work_task
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="extId != null">ext_id,</if>
+            <if test="qwUserId != null">qw_user_id,</if>
+            <if test="type != null">type,</if>
+            <if test="status != null">status,</if>
+            <if test="remark != null">remark,</if>
+            <if test="score != null">score,</if>
+            <if test="sopId != null">sop_id,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="title != null">title,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="extId != null">#{extId},</if>
+            <if test="qwUserId != null">#{qwUserId},</if>
+            <if test="type != null">#{type},</if>
+            <if test="status != null">#{status},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="score != null">#{score},</if>
+            <if test="sopId != null">#{sopId},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="title != null">#{title},</if>
+         </trim>
+    </insert>
+
+    <update id="updateQwWorkTask" parameterType="QwWorkTask">
+        update hy_work_task
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="extId != null">ext_id = #{extId},</if>
+            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="score != null">score = #{score},</if>
+            <if test="sopId != null">sop_id = #{sopId},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="title != null">title = #{title},</if>
+        </trim>
+        where id = #{id}
+    </update>
+    <insert id="insertQwWorkTaskBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO hy_work_task (
+        ext_id,
+        qw_user_id,
+        status,
+        type,
+        title,
+        remark,
+        score,
+        sop_id,
+        company_id,
+        company_user_id,
+        create_time,
+        update_time
+        )
+        VALUES
+        <foreach collection="qwWorkTasks" item="log" separator=",">
+            (
+            #{log.extId},
+            #{log.qwUserId},
+            #{log.status},
+            #{log.type},
+            #{log.title},
+            #{log.remark},
+            #{log.score},
+            #{log.sopId},
+            #{log.companyId},
+            #{log.companyUserId},
+            #{log.createTime},
+            #{log.updateTime}
+            )
+        </foreach>
+    </insert>
+    <delete id="deleteQwWorkTaskById" parameterType="Long">
+        delete from hy_work_task where id = #{id}
+    </delete>
+
+    <delete id="deleteQwWorkTaskByIds" parameterType="String">
+        delete from hy_work_task where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 232 - 0
fs-service-system/src/main/resources/mapper/qw/QwWatchLogExMapper.xml

@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.qw.mapper.QwWatchLogExMapper">
+
+    <resultMap type="QwWatchLog" id="QwWatchLogResult">
+        <result property="id"    column="id"    />
+        <result property="extId"    column="ext_id"    />
+        <result property="qwUserId"    column="qw_user_id"    />
+        <result property="status"    column="status"    />
+        <result property="day"    column="day"    />
+        <result property="project"    column="project"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="lineTime"    column="line_time"    />
+    </resultMap>
+
+    <sql id="selectQwWatchLogVo">
+        select id, ext_id, qw_user_id,line_time, status, day, project, create_time from qw_watch_log_ex
+    </sql>
+
+    <select id="selectQwWatchLogList" parameterType="QwWatchLog" resultMap="QwWatchLogResult">
+        <include refid="selectQwWatchLogVo"/>
+        <where>
+            <if test="extId != null "> and ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and qw_user_id = #{qwUserId}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="day != null "> and day = #{day}</if>
+            <if test="project != null "> and project = #{project}</if>
+        </where>
+    </select>
+
+    <select id="selectQwWatchLogById" parameterType="Long" resultMap="QwWatchLogResult">
+        <include refid="selectQwWatchLogVo"/>
+        where id = #{id}
+    </select>
+    <select id="selectQwWatchLogByCompanyUserId" resultType="com.fs.qw.vo.QwWatchLogStatisticsListVO">
+            select
+            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,
+            COUNT(CASE WHEN day = 1 and status=2 THEN 1 END) AS d1Over,
+            COUNT(1) AS line,project,course_id,video_id
+             from qw_watch_log_ex
+             <where>
+                 <if test="companyUserId">
+                     and company_user_id = #{companyUserId}
+                 </if>
+                 <if test="companyId">
+                     and company_id = #{companyId}
+                 </if>
+                 <if test="project != null">
+                     and project=#{project}
+                 </if>
+                 <if test="courseId != null">
+                     and course_id=#{courseId}
+                 </if>
+                 <if test="videoId != null">
+                     and video_id=#{videoId}
+                 </if>
+                 and DATE(line_time) between #{sTime} and #{dTime}
+             </where>
+            group by project,course_id,video_id
+    </select>
+    <select id="selectQwWatchLogAllStatisticsListVONew"
+            resultType="com.fs.qw.vo.QwWatchLogAllStatisticsListVO">
+            select
+            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,
+            COUNT(CASE WHEN day = 1 and status=2 THEN 1 END) AS d1Over,
+            COUNT(CASE WHEN day = 2 and status in (1,2) THEN 1 END) AS d2Online,
+            COUNT(CASE WHEN day = 2 and status=2 THEN 1 END) AS d2Over,
+            COUNT(CASE WHEN day = 3 and status in (1,2) THEN 1 END) AS d3Online,
+            COUNT(CASE WHEN day = 3 and status=2 THEN 1 END) AS d3Over,
+            COUNT(CASE WHEN day = 4 and status in (1,2) THEN 1 END) AS d4Online,
+            COUNT(CASE WHEN day = 4 and status=2 THEN 1 END) AS d4Over,
+            COUNT(CASE WHEN day = 5 and status in (1,2) THEN 1 END) AS d5Online,
+            COUNT(CASE WHEN day = 5 and status=2 THEN 1 END) AS d5Over,
+            COUNT(CASE WHEN day = 6 and status in (1,2) THEN 1 END) AS d6Online,
+            COUNT(CASE WHEN day = 6 and status=2 THEN 1 END) AS d6Over,
+            COUNT(CASE WHEN day = 7 and status in (1,2) THEN 1 END) AS d7Online,
+            COUNT(CASE WHEN day = 7 and status=2 THEN 1 END) AS d7Over,
+            COUNT(CASE WHEN day = 8 and status in (1,2) THEN 1 END) AS d8Online,
+            COUNT(CASE WHEN day = 8 and status=2 THEN 1 END) AS d8Over,
+            COUNT(CASE WHEN day = 9 and status in (1,2) THEN 1 END) AS d9Online,
+            COUNT(CASE WHEN day = 9 and status=2 THEN 1 END) AS d9Over,
+            COUNT(CASE WHEN day = 10 and status in (1,2) THEN 1 END) AS d10Online,
+            COUNT(CASE WHEN day = 10 and status=2 THEN 1 END) AS d10Over,
+            COUNT(CASE WHEN day = 11 and status in (1,2) THEN 1 END) AS d11Online,
+            COUNT(CASE WHEN day = 11 and status=2 THEN 1 END) AS d11Over,
+            COUNT(CASE WHEN day = 12 and status in (1,2) THEN 1 END) AS d12Online,
+            COUNT(CASE WHEN day = 12 and status=2 THEN 1 END) AS d12Over,
+            COUNT(CASE WHEN day = 13 and status in (1,2) THEN 1 END) AS d13Online,
+            COUNT(CASE WHEN day = 13 and status=2 THEN 1 END) AS d13Over,
+            COUNT(CASE WHEN day = 14 and status in (1,2) THEN 1 END) AS d14Online,
+            COUNT(CASE WHEN day = 14 and status=2 THEN 1 END) AS d14Over,
+            COUNT(CASE WHEN day = 15 and status in (1,2) THEN 1 END) AS d15Online,
+            COUNT(CASE WHEN day = 15 and status=2 THEN 1 END) AS d15Over,
+            COUNT(CASE WHEN day = 16 and status in (1,2) THEN 1 END) AS d16Online,
+            COUNT(CASE WHEN day = 16 and status=2 THEN 1 END) AS d16Over,
+            COUNT(CASE WHEN day = 17 and status in (1,2) THEN 1 END) AS d17Online,
+            COUNT(CASE WHEN day = 17 and status=2 THEN 1 END) AS d17Over,
+            COUNT(CASE WHEN day = 18 and status in (1,2) THEN 1 END) AS d18Online,
+            COUNT(CASE WHEN day = 18 and status=2 THEN 1 END) AS d18Over,
+            COUNT(CASE WHEN day = 19 and status in (1,2) THEN 1 END) AS d19Online,
+            COUNT(CASE WHEN day = 19 and status=2 THEN 1 END) AS d19Over,
+            COUNT(CASE WHEN day = 20 and status in (1,2) THEN 1 END) AS d20Online,
+            COUNT(CASE WHEN day = 20 and status=2 THEN 1 END) AS d20Over,
+            COUNT(CASE WHEN day = 21 and status in (1,2) THEN 1 END) AS d21Online,
+            COUNT(CASE WHEN day = 21 and status=2 THEN 1 END) AS d21Over,
+            COUNT(CASE WHEN day = 22 and status in (1,2) THEN 1 END) AS d22Online,
+            COUNT(CASE WHEN day = 22 and status=2 THEN 1 END) AS d22Over,
+            COUNT(CASE WHEN day = 23 and status in (1,2) THEN 1 END) AS d23Online,
+            COUNT(CASE WHEN day = 23 and status=2 THEN 1 END) AS d23Over,
+            COUNT(CASE WHEN day = 24 and status in (1,2) THEN 1 END) AS d24Online,
+            COUNT(CASE WHEN day = 24 and status=2 THEN 1 END) AS d24Over,
+            COUNT(CASE WHEN day = 25 and status in (1,2) THEN 1 END) AS d25Online,
+            COUNT(CASE WHEN day = 25 and status=2 THEN 1 END) AS d25Over,
+            COUNT(CASE WHEN day = 26 and status in (1,2) THEN 1 END) AS d26Online,
+            COUNT(CASE WHEN day = 26 and status=2 THEN 1 END) AS d26Over,
+            COUNT(CASE WHEN day = 27 and status in (1,2) THEN 1 END) AS d27Online,
+            COUNT(CASE WHEN day = 27 and status=2 THEN 1 END) AS d27Over,
+            COUNT(CASE WHEN day = 28 and status in (1,2) THEN 1 END) AS d28Online,
+            COUNT(CASE WHEN day = 28 and status=2 THEN 1 END) AS d28Over,
+            COUNT(CASE WHEN day = 29 and status in (1,2) THEN 1 END) AS d29Online,
+            COUNT(CASE WHEN day = 29 and status=2 THEN 1 END) AS d29Over,
+            COUNT(CASE WHEN day = 30 and status in (1,2) THEN 1 END) AS d30Online,
+            COUNT(CASE WHEN day = 30 and status=2 THEN 1 END) AS d30Over,
+            COUNT(1) AS line,project,course_id,video_id
+             from qw_watch_log_ex
+            <where>
+                 <if test="companyUserId">
+                     and company_user_id=#{companyUserId}
+                 </if>
+                <if test="companyId">
+                    and company_id = #{companyId}
+                </if>
+                <if test="project != null">
+                    and project = #{project}
+                </if>
+                <if test="courseId != null">
+                    and course_id = #{courseId}
+                </if>
+                <if test="videoId != null">
+                    and video_id =#{videoId}
+                </if>
+                and DATE(line_time) between #{sDate} AND #{eDate} group by project,course_id,video_id
+            </where>
+    </select>
+
+    <insert id="insertQwWatchLog" parameterType="QwWatchLog" useGeneratedKeys="true" keyProperty="id">
+        insert into qw_watch_log_ex
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="extId != null">ext_id,</if>
+            <if test="qwUserId != null">qw_user_id,</if>
+            <if test="status != null">status,</if>
+            <if test="day != null">day,</if>
+            <if test="project != null">project,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="lineTime != null">line_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="extId != null">#{extId},</if>
+            <if test="qwUserId != null">#{qwUserId},</if>
+            <if test="status != null">#{status},</if>
+            <if test="day != null">#{day},</if>
+            <if test="project != null">#{project},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="lineTime != null">#{lineTime},</if>
+         </trim>
+    </insert>
+
+    <insert id="insertQwWatchLogBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO qw_watch_log_ex (
+        ext_id,
+        qw_user_id,
+        status,
+        day,
+        project,
+        create_time,
+        line_time,
+        fs_user_id,
+        company_id,
+        company_user_id,
+        course_id,
+        video_id
+        )
+        VALUES
+    <foreach collection="watchLogs" item="log" separator=",">
+            (
+            #{log.extId},
+            #{log.qwUserId},
+            #{log.status},
+            #{log.day},
+            #{log.project},
+            #{log.createTime},
+            #{log.lineTime},
+             #{log.fsUserId},
+             #{log.companyId},
+             #{log.companyUserId},
+             #{log.courseId},
+             #{log.videoId}
+            )
+    </foreach>
+    </insert>
+
+    <update id="updateQwWatchLog" parameterType="QwWatchLog">
+        update qw_watch_log_ex
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="extId != null">ext_id = #{extId},</if>
+            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="day != null">day = #{day},</if>
+            <if test="project != null">project = #{project},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="lineTime != null">line_time = #{lineTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteQwWatchLogById" parameterType="Long">
+        delete from qw_watch_log_ex where id = #{id}
+    </delete>
+
+    <delete id="deleteQwWatchLogByIds" parameterType="String">
+        delete from qw_watch_log_ex where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 33 - 11
fs-service-system/src/main/resources/mapper/qw/QwWorkTaskMapper.xml

@@ -43,19 +43,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </select>
     <select id="selectQwWorkTaskListVONew" resultType="com.fs.qw.vo.QwWorkTaskListVO">
-        select t.* from qw_work_task t
+        SELECT t.*
+        FROM qw_work_task t
+        INNER JOIN (
+        SELECT t_inner.id
+        FROM qw_work_task t_inner
         <where>
-            DATE(t.create_time) = CURDATE()
-            <if test="extId != null "> and t.ext_id = #{extId}</if>
-            <if test="qwUserId != null "> and t.qw_user_id = #{qwUserId}</if>
-            <if test="type != null "> and t.type = #{type}</if>
-            <if test="status != null "> and t.status = #{status}</if>
-            <if test="score != null "> and t.score = #{score}</if>
-            <if test="sopId != null  and sopId != ''"> and t.sop_id = #{sopId}</if>
-            <if test="companyId != null "> and t.company_id = #{companyId}</if>
-            <if test="companyUserId != null "> and t.company_user_id = #{companyUserId}</if>
+            DATE(t_inner.create_time) = CURDATE()
+            <if test="extId != null "> and t_inner.ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and t_inner.qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and t_inner.type = #{type}</if>
+            <if test="status != null "> and t_inner.status = #{status}</if>
+            <if test="score != null "> and t_inner.score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and t_inner.sop_id = #{sopId}</if>
+            <if test="companyId != null "> and t_inner.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and t_inner.company_user_id = #{companyUserId}</if>
         </where>
-        order by t.score desc,t.id desc
+        ORDER BY t_inner.score DESC, t_inner.id DESC
+        LIMIT ${(pageNum-1)*pageSize}, ${pageSize}
+        ) AS filtered_ids ON t.id = filtered_ids.id
+        ORDER BY t.score DESC, t.id DESC
     </select>
 
     <select id="selectQwWorkTaskListByMap" resultType="com.fs.qw.domain.QwWorkTask">
@@ -68,6 +75,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           and qwt.company_user_id = #{params.companyUserId}
           and qwt.status = 0
     </select>
+    <select id="selectQwWorkTaskListVONewCount" resultType="java.lang.Long">
+        select count(1) from qw_work_task t
+        <where>
+            DATE(t.create_time) = CURDATE()
+            <if test="extId != null "> and t.ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and t.qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and t.type = #{type}</if>
+            <if test="status != null "> and t.status = #{status}</if>
+            <if test="score != null "> and t.score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and t.sop_id = #{sopId}</if>
+            <if test="companyId != null "> and t.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and t.company_user_id = #{companyUserId}</if>
+        </where>
+        order by t.score desc,t.id desc
+    </select>
 
     <insert id="insertQwWorkTask" parameterType="QwWorkTask" useGeneratedKeys="true" keyProperty="id">
         insert into qw_work_task

+ 160 - 0
fs-service-system/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.statis.mapper.ConsumptionBalanceMapper">
+
+    <select id="rechargeConsumption" resultType="com.fs.statis.dto.ConsumptionBalanceDataDTO">
+        select
+            14371277 AS balance,
+            103100 AS today_comsumption,
+            195280 AS yesterday_comsumption
+    </select>
+    <select id="dealerAggregated" resultType="com.fs.statis.dto.DealerAggregatedDTO">
+        SELECT
+                (SELECT COUNT(*) FROM COMPANY) AS dealder_count,
+                (SELECT COUNT(*) FROM COMPANY_USER) AS group_mgr_count,
+                (SELECT COUNT(*) FROM FS_USER) AS member_count,
+                (SELECT COUNT(*) FROM FS_USER WHERE STATUS=1) AS normal_num,
+                (SELECT COUNT(*) FROM FS_USER WHERE STATUS=0) AS black_num
+    </select>
+    <select id="analysisPreview" resultType="com.fs.statis.dto.AnalysisPreviewDTO">
+        -- 观看人数
+        select (select count(*) from fs_course_watch_log where create_time between #{startTime} and #{endTime} group by user_id) as watch_count,
+-- 完播人数
+               (select count(*) from fs_course_watch_log where finish_time is not null and create_time between #{startTime} and #{endTime} group by user_id)
+                                                                                                                                                     as completed_user_count,
+-- 观看次数
+               (select count(*) from fs_course_watch_log where create_time between #{startTime} and #{endTime}) as watch_count,
+-- 完播次数
+               (select count(*) from fs_course_watch_log where create_time between #{startTime} and #{endTime}) as completed_count,
+-- 答题人数
+               (select count(*) from fs_course_answer_logs where create_time between #{startTime} and #{endTime}) as answer_member_count,
+-- 正确人数
+               (select count(*) from fs_course_answer_logs where is_right=1 AND create_time between #{startTime} and #{endTime}) as correct_user_count,
+-- 答题红包个数
+               (select 0) as reward_type,
+               (select 0) as reward_money
+    </select>
+    <select id="queryWatchCount" resultType="java.lang.Long">
+        select count(log_id) from fs_course_watch_log where create_time between #{startTime} and #{endTime}
+    </select>
+    <select id="queryCompletedUserCount" resultType="java.lang.Long">
+        select count(DISTINCT user_id) from fs_course_watch_log where finish_time is not null and create_time between #{startTime} and #{endTime}
+    </select>
+    <select id="queryWatchUserCount" resultType="java.lang.Long">
+        select count(distinct user_id) from fs_course_watch_log where create_time between #{startTime} and #{endTime}
+    </select>
+    <select id="queryCompletedCount" resultType="java.lang.Long">
+        SELECT COUNT(log_id) FROM fs_course_watch_log
+        WHERE finish_time IS NOT NULL AND create_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+    <select id="queryAnswerMemberCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT user_id) FROM fs_course_answer_logs
+        WHERE create_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+    <select id="queryCorrectUserCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT user_id) FROM fs_course_answer_logs
+        WHERE is_right = 1 AND create_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+    <select id="queryRewardCount" resultType="java.lang.Long">
+        select count(*) from company_red_package_logs where operate_type=1
+    </select>
+    <select id="queryRewardMoney" resultType="java.math.BigDecimal">
+        select sum(up_money) from company_red_package_logs where operate_type=1
+    </select>
+    <select id="smsBalance" resultType="java.lang.Long">
+        select sum(remain_sms_count) from company_sms
+    </select>
+    <select id="authorizationInfo" resultType="com.fs.statis.dto.AuthorizationInfoDTO">
+        select version_limit,today_watch_user_count from fs_statistics_index limit 1
+    </select>
+    <select id="watchEndPlayTrend" resultType="com.fs.statis.dto.WatchEndPlayTrendDTO">
+        SELECT
+--             今日/昨日 小时
+            <if test="type == 0 or type == 1">
+                DATE_FORMAT(create_time, '%H') AS start_date,
+            </if>
+--                 本周/本月/上月 天
+            <if test="type == 2 or type == 3 or type == 4">
+                DATE_FORMAT(create_time, '%Y-%m-%d') AS start_date,
+            </if>
+        COUNT(DISTINCT user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS completed_user_count
+        FROM
+        fs_course_watch_log
+        <where>
+            <if test="startTime != null">
+                create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND create_time <![CDATA[<]]> #{endTime}
+            </if>
+        </where>
+        GROUP BY
+        start_date
+        ORDER BY
+        start_date
+    </select>
+    <select id="deaMemberTopTen" resultType="com.fs.statis.dto.DeaMemberTopTenDTO">
+        SELECT
+        company_id,
+        <if test="statisticalType == 0">
+            count(DISTINCT user_id) AS watch_user_count
+        </if>
+        <if test="statisticalType == 1">
+            COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS watch_user_count
+        </if>
+        FROM
+        fs_course_watch_log
+        <where>
+            <if test="startTime != null">
+                create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND create_time <![CDATA[<]]> #{endTime}
+            </if>
+        </where>
+        GROUP BY company_id
+        limit 10
+    </select>
+    <select id="watchCourseTopTen" resultType="com.fs.statis.dto.CourseStatsDTO">
+        SELECT
+        w.course_id AS course_id,
+        COUNT(DISTINCT w.user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END) AS completed_user_count,
+        COUNT(DISTINCT a.user_id) AS answer_user_count,
+        COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count
+        FROM
+        fs_course_watch_log w
+        LEFT JOIN
+        fs_course_answer_logs a ON w.video_id = a.video_id AND w.user_id = a.user_id
+        <where>
+            <if test="startTime != null">
+                w.create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND w.create_time <![CDATA[<]]> #{endTime}
+            </if>
+        </where>
+        GROUP BY
+        w.course_id
+        ORDER BY
+            -- 观看人数
+            <if test="statisticalType == 0">
+                COUNT(DISTINCT w.user_id)
+            </if>
+            <if test="statisticalType == 1">
+                COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END)
+            </if>
+            <if test="statisticalType == 2">
+                COUNT(DISTINCT a.user_id)
+            </if>
+            <if test="statisticalType == 3">
+                COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END)
+            </if>
+        ${sort}
+        LIMIT 10
+    </select>
+
+</mapper>