Selaa lähdekoodia

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_scrm_java

caoliqin 1 kuukausi sitten
vanhempi
commit
c3764e3fa5
34 muutettua tiedostoa jossa 1051 lisäystä ja 337 poistoa
  1. 33 7
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  2. 100 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseFinishTempController.java
  3. 5 0
      fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java
  4. 9 25
      fs-admin/src/main/java/com/fs/task/FsCourseTask.java
  5. 26 26
      fs-admin/src/test/java/com/fs/task/StoreTaskTest.java
  6. 37 6
      fs-common/src/main/java/com/fs/common/utils/DateUtils.java
  7. 12 0
      fs-company-app/src/main/java/com/fs/app/controller/QwWorkTaskController.java
  8. 7 6
      fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java
  9. 5 0
      fs-company/src/main/java/com/fs/qw/QwSopTempController.java
  10. 22 0
      fs-company/src/main/java/com/fs/users/controller/FsUserController.java
  11. 98 97
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  12. 12 0
      fs-service-system/src/main/java/com/fs/course/config/CourseConfig.java
  13. 3 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  14. 2 0
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseWatchLogTaskVO.java
  15. 61 0
      fs-service-system/src/main/java/com/fs/qw/domain/HyWorkTask.java
  16. 14 7
      fs-service-system/src/main/java/com/fs/qw/mapper/HyWorkTaskMapper.java
  17. 3 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwWorkTaskMapper.java
  18. 11 7
      fs-service-system/src/main/java/com/fs/qw/service/IHyWorkTaskService.java
  19. 3 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwUserService.java
  20. 100 20
      fs-service-system/src/main/java/com/fs/qw/service/impl/HyWorkTaskServiceImpl.java
  21. 11 4
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  22. 17 0
      fs-service-system/src/main/java/com/fs/qw/vo/UserVOs.java
  23. 5 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  24. 4 0
      fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java
  25. 2 0
      fs-service-system/src/main/java/com/fs/sop/service/IQwSopTempService.java
  26. 9 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  27. 89 0
      fs-service-system/src/main/java/com/fs/sop/util/TimeCalculator.java
  28. 5 0
      fs-service-system/src/main/java/com/fs/statis/dto/AnalysisPreviewQueryDTO.java
  29. 5 0
      fs-service-system/src/main/java/com/fs/statis/dto/DealerAggregatedDTO.java
  30. 6 6
      fs-service-system/src/main/java/com/fs/statis/service/IStatisticsService.java
  31. 192 97
      fs-service-system/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java
  32. 24 2
      fs-service-system/src/main/resources/mapper/qw/HyWorkTaskMapper.xml
  33. 7 0
      fs-service-system/src/main/resources/mapper/qw/QwWorkTaskMapper.xml
  34. 112 27
      fs-service-system/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

+ 33 - 7
fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java

@@ -3,7 +3,6 @@ package com.fs.api.controller;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.statis.StatisticsRedisConstant;
 import com.fs.statis.dto.*;
-import com.fs.statis.service.IStatisticsService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import com.fs.common.core.domain.R;
@@ -28,11 +27,16 @@ public class IndexStatisticsController {
     public R analysisPreview(@RequestBody AnalysisPreviewQueryDTO param){
         AnalysisPreviewDTO analysisPreviewDTO = null;
         Integer type = param.getType();
+        Integer userType = param.getUserType();
+
         if(type == null) {
             type = 0;
         }
 
-        analysisPreviewDTO = redisCache.getCacheObject(String.format("%s:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type));
+        if(userType == null) {
+            userType = 0;
+        }
+        analysisPreviewDTO = redisCache.getCacheObject(String.format("%s:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType));
 
         return R.ok().put("data",analysisPreviewDTO);
     }
@@ -63,7 +67,16 @@ public class IndexStatisticsController {
      */
     @PostMapping("/watchEndPlayTrend")
     public R watchEndPlayTrend(@RequestBody AnalysisPreviewQueryDTO param){
-        String key = String.format("%s:%d", DATA_OVERVIEW_DEALER_CHARTS, param.getType());
+        Integer type = param.getType();
+        Integer userType = param.getUserType();
+
+        if(type == null) {
+            type = 0;
+        }
+        if(userType == null){
+            userType = 0;
+        }
+        String key = String.format("%s:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType);
         List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(key);
         return R.ok().put("data", deaMemberTopTenDTOS);
     }
@@ -75,8 +88,16 @@ public class IndexStatisticsController {
     public R deaMemberTopTen(@RequestBody AnalysisPreviewQueryDTO param){
         Integer type = param.getType();
         Integer statisticalType = param.getStatisticalType();
+        Integer userType = param.getUserType();
 
-        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(String.format("%s:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType));
+        if(type == null) {
+            type = 0;
+        }
+        if(userType == null){
+            userType = 0;
+        }
+
+        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType,userType));
         if(deaMemberTopTenDTOS == null){
             deaMemberTopTenDTOS = new ArrayList<>();
         }
@@ -90,7 +111,9 @@ public class IndexStatisticsController {
     public R rewardMoneyTopTen(@RequestBody AnalysisPreviewQueryDTO param){
         Integer type = param.getType();
         Integer dataType = param.getDataType();
-        List<RewardMoneyTopTenDTO> rewardMoneyTopTenDTOS = redisCache.getCacheObject( String.format("%s:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType));
+        Integer userType = param.getUserType();
+
+        List<RewardMoneyTopTenDTO> rewardMoneyTopTenDTOS = redisCache.getCacheObject( String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType));
         return R.ok().put("data", rewardMoneyTopTenDTOS);
     }
 
@@ -100,7 +123,8 @@ public class IndexStatisticsController {
     @PostMapping("/rewardMoneyTrend")
     public R rewardMoneyTrend(@RequestBody AnalysisPreviewQueryDTO param){
         Integer type = param.getType();
-        List<RewardMoneyTrendDTO> rewardMoneyTrendDTOS = redisCache.getCacheObject( String.format("%s:%d", CHARTS_REWARD_MONEY_TREND, type));
+        Integer userType = param.getUserType();
+        List<RewardMoneyTrendDTO> rewardMoneyTrendDTOS = redisCache.getCacheObject( String.format("%s:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType));
         return R.ok().put("data", rewardMoneyTrendDTOS);
     }
 
@@ -112,7 +136,9 @@ public class IndexStatisticsController {
         Integer type = param.getType();
         String sort = param.getSort();
         Integer statisticalType = param.getStatisticalType();
-        List<CourseStatsDTO> courseStatsDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%s", CHARTS_WATCH_TOP_TEN, type,statisticalType,sort));
+        Integer userType = param.getUserType();
+
+        List<CourseStatsDTO> courseStatsDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%s", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort));
         return R.ok().put("data", courseStatsDTOS);
     }
 

+ 100 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseFinishTempController.java

@@ -0,0 +1,100 @@
+package com.fs.course.controller;
+
+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.poi.ExcelUtil;
+import com.fs.course.domain.FsCourseFinishTemp;
+import com.fs.course.service.IFsCourseFinishTempService;
+import com.fs.course.vo.FsCourseFinishTempListVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 完课模板Controller
+ *
+ * @author fs
+ * @date 2024-12-19
+ */
+@RestController
+@RequestMapping("/course/courseFinishTemp")
+public class FsCourseFinishTempController extends BaseController
+{
+    @Autowired
+    private IFsCourseFinishTempService fsCourseFinishTempService;
+
+    /**
+     * 查询完课模板列表
+     */
+    @PreAuthorize("@ss.hasPermi('courseFinishTemp:course:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsCourseFinishTemp fsCourseFinishTemp)
+    {
+        startPage();
+        List<FsCourseFinishTempListVO> list = fsCourseFinishTempService.selectFsCourseFinishTempListVO(fsCourseFinishTemp);
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 导出完课模板列表
+     */
+    @PreAuthorize("@ss.hasPermi('courseFinishTemp:course:export')")
+    @Log(title = "完课模板", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsCourseFinishTemp fsCourseFinishTemp)
+    {
+        List<FsCourseFinishTemp> list = fsCourseFinishTempService.selectFsCourseFinishTempList(fsCourseFinishTemp);
+        ExcelUtil<FsCourseFinishTemp> util = new ExcelUtil<FsCourseFinishTemp>(FsCourseFinishTemp.class);
+        return util.exportExcel(list, "完课模板数据");
+    }
+
+    /**
+     * 获取完课模板详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('courseFinishTemp:course:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+//        return AjaxResult.success(fsCourseFinishTempService.selectFsCourseFinishTempById(id));
+        return AjaxResult.success(fsCourseFinishTempService.selectFsCourseFinishTempByIdVO(id));
+    }
+
+    /**
+     * 新增完课模板
+     */
+    @PreAuthorize("@ss.hasPermi('courseFinishTemp:course:add')")
+    @Log(title = "完课模板", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsCourseFinishTemp fsCourseFinishTemp)
+    {
+        return toAjax(fsCourseFinishTempService.insertFsCourseFinishTemp(fsCourseFinishTemp));
+    }
+
+    /**
+     * 修改完课模板
+     */
+    @PreAuthorize("@ss.hasPermi('courseFinishTemp:course:edit')")
+    @Log(title = "完课模板", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsCourseFinishTemp fsCourseFinishTemp)
+    {
+        return toAjax(fsCourseFinishTempService.updateFsCourseFinishTemp(fsCourseFinishTemp));
+    }
+
+    /**
+     * 删除完课模板
+     */
+    @PreAuthorize("@ss.hasPermi('courseFinishTemp:course:remove')")
+    @Log(title = "完课模板", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsCourseFinishTempService.deleteFsCourseFinishTempByIds(ids));
+    }
+}

+ 5 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java

@@ -3,6 +3,7 @@ package com.fs.qw.controller;
 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.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
@@ -187,4 +188,8 @@ public class QwSopTempController extends BaseController
         qwSopTempService.updateRedPackage(data.getList());
         return AjaxResult.success();
     }
+    @GetMapping("/getSelectableRange")
+    public R getSelectableRange(){
+        return R.ok().put("data", qwSopTempService.getSelectableRange());
+    }
 }

+ 9 - 25
fs-admin/src/main/java/com/fs/task/FsCourseTask.java

@@ -27,38 +27,22 @@ public class FsCourseTask {
     }
 
     /**
-     * 会员任务定时更新
-     * @throws Exception
-     */
-    public void hyWorkTask1() throws Exception
-    {
-        hyWorkTaskService.addHyWorkByCourse4();
-        hyWorkTaskService.addHyWorkByCourseLastTime();
-    }
-    /**
-     * 会员待看课和先导课
-     * @throws Exception
-     */
-    public void hyWorkTask2() throws Exception
-    {
-        hyWorkTaskService.addHyWorkByCourse();
-        hyWorkTaskService.addHyWorkByFirstCourse();
-    }
-    /**
-     * 用户大小转
+     * 删除过期数据
      * @throws Exception
      */
-    public void hyWorkTask3() throws Exception
+    public void hyWorkTask4() throws Exception
     {
-        hyWorkTaskService.addHyWorkByConversionDay();
+        // 更新已完课的催课看板
+        hyWorkTaskService.delHyWorkTaskByOver();
     }
+
     /**
-     * 删除过期数据
+     * 会员查看催课面板 获取看课中断和待看的先导课
      * @throws Exception
      */
-    public void hyWorkTask4() throws Exception
-    {
-        hyWorkTaskService.delHyWorkTaskByOver();
+    public void hyWorkTask(){
+
+        hyWorkTaskService.hyWorkTask();
     }
 
 }

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

@@ -1,26 +1,26 @@
-//package com.fs.task;
-//
-//
-//import com.fs.FSAdminApplication;
-//import com.fs.course.service.IFsCourseWatchLogService;
-//import org.junit.Test;
-//import org.junit.runner.RunWith;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.boot.test.context.SpringBootTest;
-//
-//@RunWith(value = org.springframework.test.context.junit4.SpringRunner.class)
-//@SpringBootTest(classes = FSAdminApplication.class)
-//public class StoreTaskTest {
-//    @Autowired
-//    private IFsCourseWatchLogService fsCourseWatchLogService;
-//    @Autowired
-//    private FsCourseTask fsCourseTask;
-//    @Test
-//    public void addQwWatchLog() {
-//        fsCourseWatchLogService.addCourseWatchLogDayNew();
-//    }
-//    @Test
-//    public void test() throws Exception {
-//        fsCourseTask.qwWorkTask1();
-//    }
-//}
+package com.fs.task;
+
+
+import com.fs.FSAdminApplication;
+import com.fs.course.service.IFsCourseWatchLogService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@RunWith(value = org.springframework.test.context.junit4.SpringRunner.class)
+@SpringBootTest(classes = FSAdminApplication.class)
+public class StoreTaskTest {
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+    @Autowired
+    private FsCourseTask fsCourseTask;
+    @Test
+    public void addQwWatchLog() {
+        fsCourseWatchLogService.addCourseWatchLogDayNew();
+    }
+    @Test
+    public void test() throws Exception {
+        fsCourseTask.hyWorkTask();
+    }
+}

+ 37 - 6
fs-common/src/main/java/com/fs/common/utils/DateUtils.java

@@ -3,13 +3,17 @@ package com.fs.common.utils;
 import java.lang.management.ManagementFactory;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
 import java.util.Calendar;
 import java.util.Date;
 import org.apache.commons.lang3.time.DateFormatUtils;
 
 /**
  * 时间工具类
- * 
+ *
 
  */
 public class DateUtils extends org.apache.commons.lang3.time.DateUtils
@@ -23,15 +27,15 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
     public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
 
     public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
-    
+
     private static String[] parsePatterns = {
-            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
+            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
             "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
             "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
 
     /**
      * 获取当前Date型日期
-     * 
+     *
      * @return Date() 当前日期
      */
     public static Date getNowDate()
@@ -41,7 +45,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
 
     /**
      * 获取当前日期, 默认格式为yyyy-MM-dd
-     * 
+     *
      * @return String
      */
     public static String getDate()
@@ -122,7 +126,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
             return null;
         }
     }
-    
+
     /**
      * 获取服务器启动时间
      */
@@ -186,4 +190,31 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         }
         return age;
     }
+    /**
+     * 计算给定日期与当前日期相差的天数 (基于日历日期)。
+     * 例如:昨天 23:59 和 今天 00:01 相差 1 天。
+     *
+     * @param createTime 起始时间 (java.util.Date)
+     * @return 相差的天数 (long)。如果 createTime 为 null,返回 0。
+     *         如果 createTime 在当前时间之后,结果为负数。
+     */
+    public static long getDaysDifferenceFromNow(Date createTime) {
+        if (createTime == null) {
+            return 0; // 或者根据业务抛出异常或返回特定值
+        }
+        // 1. 将 java.util.Date 转换为 Instant (时间线上的一个点)
+        Instant createInstant = createTime.toInstant();
+        // 2. 获取当前时间的 Instant
+        Instant nowInstant = Instant.now();
+        // 3. 指定时区 (非常重要!否则会使用系统默认时区,可能导致不一致)
+        //    建议使用一个明确的时区,例如服务器所在时区或业务标准时区
+        ZoneId zoneId = ZoneId.systemDefault(); // 或者 ZoneId.of("Asia/Shanghai");
+        // 4. 将 Instant 转换为 LocalDate (只包含年月日,忽略时分秒)
+        LocalDate createDate = createInstant.atZone(zoneId).toLocalDate();
+        LocalDate nowDate = nowInstant.atZone(zoneId).toLocalDate(); // 使用相同的时区
+        // 5. 使用 ChronoUnit.DAYS.between 计算两个 LocalDate 之间的天数差
+        long daysBetween = ChronoUnit.DAYS.between(createDate, nowDate);
+        return daysBetween;
+    }
+
 }

+ 12 - 0
fs-company-app/src/main/java/com/fs/app/controller/QwWorkTaskController.java

@@ -8,6 +8,7 @@ import com.fs.qw.service.IQwUserService;
 import com.fs.qw.service.IQwWorkTaskService;
 import com.fs.app.param.QwWorkTaskQueryParam;
 import com.fs.qw.vo.QwOptionsVO;
+import com.fs.qw.vo.UserVOs;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -62,4 +63,15 @@ public class QwWorkTaskController extends AppBaseController {
         return R.ok().put("data", new PageInfo<>(list));
     }
 
+    @Login
+    @ApiOperation("催课看板会员列表")
+    @GetMapping("/getUserList")
+    public R getUserList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                      @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        Long userId = Long.parseLong(getUserId());
+        PageHelper.startPage(pageNum, pageSize);
+        List<UserVOs> list = qwUserService.getUserList(userId);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
 }

+ 7 - 6
fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java

@@ -12,6 +12,7 @@ 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.HyWorkTask;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
 import com.fs.qw.service.IHyWorkTaskService;
@@ -71,10 +72,10 @@ public class QwQwWorkTaskController extends BaseController
     @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:export')")
     @Log(title = "企微任务看板", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult export(QwWorkTask qwWorkTask)
+    public AjaxResult export(HyWorkTask qwWorkTask)
     {
-        List<QwWorkTask> list = hyWorkTaskService.selectHyWorkTaskList(qwWorkTask);
-        ExcelUtil<QwWorkTask> util = new ExcelUtil<QwWorkTask>(QwWorkTask.class);
+        List<HyWorkTask> list = hyWorkTaskService.selectHyWorkTaskList(qwWorkTask);
+        ExcelUtil<HyWorkTask> util = new ExcelUtil<HyWorkTask>(HyWorkTask.class);
         return util.exportExcel(list, "企微任务看板数据");
     }
 
@@ -94,7 +95,7 @@ public class QwQwWorkTaskController extends BaseController
     @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:add')")
     @Log(title = "企微任务看板", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody QwWorkTask qwWorkTask)
+    public AjaxResult add(@RequestBody HyWorkTask qwWorkTask)
     {
         return toAjax(hyWorkTaskService.insertHyWorkTask(qwWorkTask));
     }
@@ -105,9 +106,9 @@ public class QwQwWorkTaskController extends BaseController
     @PreAuthorize("@ss.hasPermi('qw:QwWorkTask:edit')")
     @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody QwWorkTask qwWorkTask)
+    public AjaxResult edit(@RequestBody HyWorkTask qwWorkTask)
     {
-        QwWorkTask task = new QwWorkTask();
+        HyWorkTask task = new HyWorkTask();
         task.setId(qwWorkTask.getId());
         task.setRemark(qwWorkTask.getRemark());
         task.setStatus(1);

+ 5 - 0
fs-company/src/main/java/com/fs/qw/QwSopTempController.java

@@ -193,4 +193,9 @@ public class QwSopTempController extends BaseController
         qwSopTempService.updateRedPackage(data.getList());
         return AjaxResult.success();
     }
+
+    @GetMapping("/getSelectableRange")
+    public R getSelectableRange(){
+        return R.ok().put("data", qwSopTempService.getSelectableRange());
+    }
 }

+ 22 - 0
fs-company/src/main/java/com/fs/users/controller/FsUserController.java

@@ -4,14 +4,18 @@ 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.domain.R;
+import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
 import com.fs.store.domain.FsUser;
+import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.vo.FsCompanyUserListQueryVO;
+import com.fs.store.vo.h5.FsUserPageListVO;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -118,4 +122,22 @@ public class FsUserController extends BaseController
         return toAjax(fsUserService.updateFsUser(fsUser));
     }
 
+    @PreAuthorize("@ss.hasPermi('users:user:darkRoomList')")
+    @GetMapping("/darkRoomList")
+    @ApiOperation("小黑屋")
+    public TableDataInfo darkRoomList(FsUserPageListParam param) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setUserId(loginUser.getUser().getUserId());
+        startPage();
+        List<FsUserPageListVO> list = fsUserService.selectFsUserPageList(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('users:user:darkRoomList')")
+    @PostMapping("/enabledUsers")
+    @ApiOperation("批量启用会员")
+    public ResponseResult<Boolean> enabledUsers(@RequestBody String[] ids) {
+        Boolean r = fsUserService.disabledUser(ids, true);
+        return ResponseResult.ok(r);
+    }
 }

+ 98 - 97
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -100,7 +100,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     @Autowired
     private SopUserLogsMapper sopUserLogsMapper;
     @Autowired
-    private QwSopTagMapper qwSopTagMapper ;
+    private QwSopTagMapper qwSopTagMapper;
     @Autowired
     private QwSopMapper sopMapper;
     @Autowired
@@ -190,7 +190,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
 
 
-
     private void startConsumers() {
         qwSopLogsExecutor = Executors.newSingleThreadExecutor(r -> {
             Thread t = new Thread(r, "QwSopLogsConsumer");
@@ -224,7 +223,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     // Scheduled tasks to refresh configurations and domain names periodically
     @Scheduled(fixedDelay = 60000) // 每60秒刷新一次
     public void refreshCourseConfig() {
-        synchronized(configLock) {
+        synchronized (configLock) {
             try {
                 String json = configService.selectConfigByKey("course.config");
                 CourseConfig newConfig = JSON.parseObject(json, CourseConfig.class);
@@ -241,7 +240,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
 
 
-
     @PreDestroy
     public void shutdownConsumers() {
         running = false;
@@ -272,7 +270,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         log.info("====== 开始选择和处理 SOP 用户日志 ======");
 
         CourseConfig config;
-        synchronized(configLock) {
+        synchronized (configLock) {
             config = cachedCourseConfig;
         }
         List<SopUserLogsVo> sopUserLogsVos = sopUserLogsMapper.selectSopUserLogsListByTime();
@@ -282,7 +280,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         }
         String[] array = sopUserLogsVos.stream().map(SopUserLogsVo::getChatId).filter(StringUtils::isNotEmpty).toArray(String[]::new);
         Map<String, QwGroupChat> groupChatMap = new HashMap<>();
-        if(array.length > 0){
+        if (array.length > 0) {
             List<QwGroupChat> qwGroupChatList = qwGroupChatService.selectQwGroupChatByChatIds(array);
             List<QwGroupChatUser> qwGroupChatUserList = qwGroupChatUserService.selectQwGroupChatUserByChatIds(array);
             Map<String, List<QwGroupChatUser>> chatUserMap = PubFun.listToMapByGroupList(qwGroupChatUserList, QwGroupChatUser::getChatId);
@@ -333,7 +331,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
             String sopId = entry.getKey();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
-            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap);
+            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch, currentTime, groupChatMap);
         }
 
         // 等待所有 SOP 分组处理完成
@@ -349,13 +347,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
     @Async("sopTaskExecutor")
     @Retryable(
-            value = { Exception.class },
+            value = {Exception.class},
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
     )
-    public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch , LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) {
+    public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) {
         try {
-            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap);
+            processSopGroup(sopId, userLogsVos, currentTime, groupChatMap);
         } catch (Exception e) {
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
         } finally {
@@ -413,13 +411,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
     @Async("sopTaskExecutor")
     @Retryable(
-            value = { Exception.class },
+            value = {Exception.class},
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
     )
     public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings, CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap) {
         try {
-            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap);
+            processUserLog(logVo, ruleTimeVO, tempSettings, currentTime, groupChatMap);
         } catch (Exception e) {
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
         } finally {
@@ -448,9 +446,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 return;
             }
             long day = daysBetween;
-            if(day == 0 && ruleTimeVO.getIsAutoSop() == 1){
+            if (day == 0 && ruleTimeVO.getIsAutoSop() == 1) {
                 day = 1;
-            }else{
+            } else {
                 day++;
             }
             List<QwSopTempSetting.Content> contents = getDay(tempSettings, day);
@@ -470,9 +468,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
             //获取企业微信员工的称呼//从redis里或者从库里取
-            QwUser qwUserByRedis = qwExternalContactService.getQwUserByRedis(logVo.getCorpId(),logVo.getQwUserId());
-            if (qwUserByRedis==null){
-                log.error("无企微员工信息 {} 跳过处理。:{}", logVo.getUserId(),logVo.getCorpId());
+            QwUser qwUserByRedis = qwExternalContactService.getQwUserByRedis(logVo.getCorpId(), logVo.getQwUserId());
+            if (qwUserByRedis == null) {
+                log.error("无企微员工信息 {} 跳过处理。:{}", logVo.getUserId(), logVo.getCorpId());
                 return;
             }
 
@@ -488,9 +486,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 daysBetween = ChronoUnit.DAYS.between(startDate, currentDate);
                 intervalDay = (int) (daysBetween / tempGap);
                 day = daysBetween;
-                if(day == 0 && ruleTimeVO.getIsAutoSop() == 1){
+                if (day == 0 && ruleTimeVO.getIsAutoSop() == 1) {
                     day = 1;
-                }else{
+                } else {
                     day++;
                 }
 
@@ -505,7 +503,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
             // 只有整倍数才做事
             if (daysBetween % tempGap != 0) {
-                log.error("天数差 {} 不是 tempGap {} 的整数倍,跳过操作,SopId {} ", daysBetween, tempGap,logVo.getSopId());
+                log.error("天数差 {} 不是 tempGap {} 的整数倍,跳过操作,SopId {} ", daysBetween, tempGap, logVo.getSopId());
                 return;
             }
 
@@ -544,16 +542,16 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 //                        userLogsInfo.setSopId(logVo.getSopId());
 //                        userLogsInfo.setUserLogsId(logVo.getId());
 
-                        SopUserLogsInfoParam logsInfoParam=new SopUserLogsInfoParam();
+                        SopUserLogsInfoParam logsInfoParam = new SopUserLogsInfoParam();
                         logsInfoParam.setSopId(logVo.getSopId());
                         logsInfoParam.setUserLogsId(logVo.getId());
                         logsInfoParam.setIsRegister(logVo.getIsRegister());
 
                         List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoListByIsRegister(logsInfoParam);
 
-                        if(logVo.getIsRegister() == 1){
+                        if (logVo.getIsRegister() == 1) {
                             List<Long> externalContactIdList = PubFun.listToNewList(sopUserLogsInfos, SopUserLogsInfo::getExternalId);
-                            if(!externalContactIdList.isEmpty()){
+                            if (!externalContactIdList.isEmpty()) {
                                 List<QwExternalContact> list = qwExternalContactService.list(new QueryWrapper<QwExternalContact>().isNotNull("fs_user_id").in("id", externalContactIdList));
                                 Map<Long, QwExternalContact> map = PubFun.listToMapByGroupObject(list, QwExternalContact::getId);
                                 sopUserLogsInfos = sopUserLogsInfos.stream().filter(e -> map.containsKey(e.getExternalId())).collect(Collectors.toList());
@@ -574,7 +572,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                         }
 
 //                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content);
-                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId, companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName(), groupChatMap);
+                        insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId, companyUserId, companyId, qwUserByRedis.getWelcomeText(), qwUserByRedis.getQwUserName(), groupChatMap);
 
 
                     }
@@ -589,7 +587,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
 
 
-    private List<QwSopTempSetting.Content> getDay(List<QwSopTempRules> tempSettings, long days){
+    private List<QwSopTempSetting.Content> getDay(List<QwSopTempRules> tempSettings, long days) {
         List<QwSopTempRules> collect = tempSettings.stream().filter(e -> e.getDayNum() == days).collect(Collectors.toList());
         AtomicInteger i = new AtomicInteger();
         return collect.stream().map(e -> {
@@ -625,10 +623,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         Long courseId = content.getCourseId();
         Long videoId = content.getVideoId();
 
-        Integer isOfficial = content.getIsOfficial() != null ? Integer.valueOf(content.getIsOfficial()) : 0;
+        Integer isOfficial = content.getIsOfficial() != null ? Integer.parseInt(content.getIsOfficial()) : 0;
 
         // 发送语言 start
-        if(content.getSetting() == null){
+        if (content.getSetting() == null) {
             return;
         }
 //        List<QwSopTempSetting.Content.Setting> setting = content.getSetting().stream().filter(e -> "7".equals(e.getContentType())).collect(Collectors.toList());
@@ -662,28 +660,28 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 //            });
 //        }
         // 发送语言 end
-        if (content.getType()==5){
-            sopAddTag(logVo,content,sendTime);
+        if (content.getType() == 5) {
+            sopAddTag(logVo, content, sendTime);
         }
 
-        if(StringUtils.isNotEmpty(logVo.getChatId())){
+        if (StringUtils.isNotEmpty(logVo.getChatId())) {
             QwGroupChat groupChat = groupChatMap.get(logVo.getChatId());
             ruleTimeVO.setSendType(6);
             ruleTimeVO.setType(2);
-            if(content.getIndex() == 0){
+            if (content.getIndex() == 0) {
                 QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null);
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
-                        type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText,qwUserName, null, true);
-            }else{
+                        type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName, null, true);
+            } else {
                 groupChat.getChatUserList().forEach(user -> {
                     ruleTimeVO.setSendType(2);
                     ruleTimeVO.setRemark("客户群催课");
                     QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, user.getUserId(), user.getName(), null);
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
-                            type, qwUserId, companyUserId, companyId, user.getUserId(), welcomeText,qwUserName, null, false);
+                            type, qwUserId, companyUserId, companyId, user.getUserId(), welcomeText, qwUserName, null, false);
                 });
             }
-        }else{
+        } else {
             // 处理每个 externalContactId
             sopUserLogsInfos.forEach(contactId -> {
                 try {
@@ -692,7 +690,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     Long fsUserId = contactId.getFsUserId();
                     QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId);
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
-                            type, qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId, false);
+                            type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false);
                 } catch (Exception e) {
                     log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
                 }
@@ -706,7 +704,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         String addTag = content.getAddTag();
         String delTag = content.getDelTag();
         String corpId = logVo.getCorpId();
-        if (addTag!=null || delTag!=null) {
+        if (addTag != null || delTag != null) {
             QwSopTag qwSopTag = new QwSopTag();
             qwSopTag.setAddTags(addTag);
             qwSopTag.setDelTags(delTag);
@@ -751,11 +749,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                       String qwUserName, Long fsUserId, boolean isGroupChat) {
         switch (type) {
             case 1:
-                handleNormalMessage(sopLogs, content,companyUserId);
+                handleNormalMessage(sopLogs, content, companyUserId);
                 break;
             case 2:
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
-                        qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName,fsUserId, isGroupChat);
+                        qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, isGroupChat);
                 break;
             case 3:
                 handleOrderMessage(sopLogs, content);
@@ -772,7 +770,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         }
     }
 
-    private void handleNormalMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,String companyUserId) {
+    private void handleNormalMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content, String companyUserId) {
 
         sopLogs.setContentJson(JSON.toJSONString(content));
         enqueueQwSopLogs(sopLogs);
@@ -814,7 +812,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 case "3":
                     if ("1".equals(setting.getIsBindUrl())) {
                         String link;
-                        if(isGroupChat){
+                        if (isGroupChat) {
                             FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
                             createParam.setCourseId(courseId);
                             createParam.setVideoId(videoId);
@@ -825,14 +823,14 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                             createParam.setQwUserId(qwUserId);
                             createParam.setDays(setting.getExpiresDays());
                             R createLink = courseLinkService.createRoomLinkUrl(createParam);
-                            if (createLink.get("code").equals(500)){
+                            if (createLink.get("code").equals(500)) {
                                 throw new BaseException("链接生成失败!");
                             }
                             link = (String) createLink.get("url");
-                        }else{
-                            addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
+                        } else {
+                            addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo);
                             link = generateShortLink(setting, logVo, sendTime, courseId, videoId,
-                                    qwUserId, companyUserId, companyId, externalId,fsUserId);
+                                    qwUserId, companyUserId, companyId, externalId, fsUserId);
                         }
 
                         if (StringUtils.isNotEmpty(link)) {
@@ -845,7 +843,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                 } else {
 //                                    setting.setValue(currentValue + "\n" + sortLink);
                                     setting.setValue(currentValue
-                                            .replaceAll("#销售称呼#",StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText)
+                                            .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
                                             + "\n" + link);
                                 }
                             }
@@ -853,36 +851,36 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                             log.error("生成短链失败,跳过设置 URL。");
                         }
 
-                    }else {
+                    } else {
                         if ("1".equals(setting.getContentType())) {
                             setting.setValue(setting.getValue()
-                                    .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText)?"":welcomeText));
+                                    .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText));
                         }
                     }
                     break;
                 //小程序单独
                 case "4":
 
-                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
+                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo);
 
                     String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId,
-                            qwUserId, companyUserId, companyId, externalId,fsUserId);
+                            qwUserId, companyUserId, companyId, externalId, fsUserId);
 
                     setting.setMiniprogramPage(sortLink.replaceAll("^[\\s\\u2005]+", ""));
 
                     try {
-                        setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl())?"https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png":setting.getMiniprogramPicUrl());
-                    }catch (Exception e){
-                        log.error("赋值-小程序封面地址失败-"+e);
+                        setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl()) ? "https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png" : setting.getMiniprogramPicUrl());
+                    } catch (Exception e) {
+                        log.error("赋值-小程序封面地址失败-" + e);
                     }
 
                     break;
                 //app
                 case "9":
-                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
+                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo);
 
                     QwCreateLinkByAppDTO linkByApp = createLinkByApp(setting, logVo, sendTime, courseId, videoId,
-                            qwUserId, companyUserId, companyId, externalId,sopLogs.getCorpId(),qwUserName,fsUserId);
+                            qwUserId, companyUserId, companyId, externalId, sopLogs.getCorpId(), qwUserName, fsUserId);
 
                     setting.setLinkUrl(linkByApp.getSortLink().replaceAll("^[\\s\\u2005]+", ""));
                     setting.setAppLinkUrl(linkByApp.getAppMsgLink().replaceAll("^[\\s\\u2005]+", ""));
@@ -892,13 +890,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 case "10":
                     // 获取缓存的配置
                     CourseConfig config;
-                    synchronized(configLock) {
+                    synchronized (configLock) {
                         config = cachedCourseConfig;
                     }
                     if (config != null) {
-                        String URL= config.getRegisterDomainName()+registerLink+externalId;
+                        String URL = config.getRegisterDomainName() + registerLink + externalId;
                         setting.setLinkUrl(URL.replaceAll("^[\\s\\u2005]+", ""));
-                    }else {
+                    } else {
                         log.error("获取缓存的配置为空:注册链接");
                     }
 
@@ -937,10 +935,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
     private QwCreateLinkByAppDTO createLinkByApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
                                                  Long courseId, Long videoId, String qwUserId,
-                                                 String companyUserId, String companyId, String externalId, String corpId, String qwUserName,Long fsUserId){
+                                                 String companyUserId, String companyId, String externalId, String corpId, String qwUserName, Long fsUserId) {
         // 获取缓存的配置
         CourseConfig config;
-        synchronized(configLock) {
+        synchronized (configLock) {
             config = cachedCourseConfig;
         }
 
@@ -952,7 +950,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 companyUserId, companyId, externalId, 4);
 
         FsCourseRealLink courseMap = new FsCourseRealLink();
-        BeanUtils.copyProperties(link,courseMap);
+        BeanUtils.copyProperties(link, courseMap);
         courseMap.setFsUserId(fsUserId);
 
         String courseJson = JSON.toJSONString(courseMap);
@@ -963,11 +961,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         link.setUpdateTime(updateTime);
 
-        String sortLink = appLink+link.getLink()+"&videoId="+videoId;
+        String sortLink = appLink + link.getLink() + "&videoId=" + videoId;
 
-        String appMsgLink=appRealLink+link.getLink();
+        String appMsgLink = appRealLink + link.getLink();
 
-        QwCreateLinkByAppDTO byAppDTO=new QwCreateLinkByAppDTO();
+        QwCreateLinkByAppDTO byAppDTO = new QwCreateLinkByAppDTO();
         byAppDTO.setSortLink(sortLink);
         byAppDTO.setAppMsgLink(appMsgLink);
 
@@ -983,11 +981,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
 
     public FsCourseSopAppLink createFsCourseSopAppLink(String link, Date sendTime, Date updateTime, String companyId,
-                                                       String companyUserId,String qwUserId,String qwUserName,String corpId,
-                                                       Long courseId,String linkTile,String linkImageUrl,Long videoId,
-                                                       String linkDescribe,String appMsgLink,String externalId){
+                                                       String companyUserId, String qwUserId, String qwUserName, String corpId,
+                                                       Long courseId, String linkTile, String linkImageUrl, Long videoId,
+                                                       String linkDescribe, String appMsgLink, String externalId) {
 
-        FsCourseSopAppLink sopAppLink=new FsCourseSopAppLink();
+        FsCourseSopAppLink sopAppLink = new FsCourseSopAppLink();
         sopAppLink.setLink(link);
         sopAppLink.setCreateTime(sendTime);
         sopAppLink.setUpdateTime(updateTime);
@@ -1007,12 +1005,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         return sopAppLink;
     }
+
     private String createLinkByMiniApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
                                        Long courseId, Long videoId, String qwUserId,
-                                       String companyUserId, String companyId, String externalId,Long fsUserId) {
+                                       String companyUserId, String companyId, String externalId, Long fsUserId) {
         // 获取缓存的配置
         CourseConfig config;
-        synchronized(configLock) {
+        synchronized (configLock) {
             config = cachedCourseConfig;
         }
 
@@ -1041,7 +1040,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
         FsCourseRealLink courseMap = new FsCourseRealLink();
-        BeanUtils.copyProperties(link,courseMap);
+        BeanUtils.copyProperties(link, courseMap);
         courseMap.setFsUserId(fsUserId);
 
         String courseJson = JSON.toJSONString(courseMap);
@@ -1056,7 +1055,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         // 使用 Java 8 时间 API 计算过期时间
         LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
-        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1);
+        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays - 1);
         expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
         Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
         link.setUpdateTime(updateTime);
@@ -1076,7 +1075,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         return content.clone();
     }
 
-    private Date createUpdateTime(QwSopTempSetting.Content.Setting setting,Date sendTime,CourseConfig config){
+    private Date createUpdateTime(QwSopTempSetting.Content.Setting setting, Date sendTime, CourseConfig config) {
 
         Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0)
                 ? config.getVideoLinkExpireDate()
@@ -1084,7 +1083,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 //         使用 Java 8 时间 API 计算过期时间
         LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
-        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1);
+        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays - 1);
         expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
         Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
 
@@ -1097,8 +1096,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         enqueueQwSopLogs(sopLogs);
     }
 
-    public FsCourseLink createFsCourseLink(String corpId, Date sendTime,Long courseId,Long videoId, String qwUserId,
-                                           String companyUserId, String companyId,String externalId,Integer type){
+    public FsCourseLink createFsCourseLink(String corpId, Date sendTime, Long courseId, Long videoId, String qwUserId,
+                                           String companyUserId, String companyId, String externalId, Integer type) {
         // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
         FsCourseLink link = new FsCourseLink();
         link.setCompanyId(Long.parseLong(companyId));
@@ -1122,7 +1121,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                      String companyUserId, String companyId, String externalId, Long fsUserId) {
         // 获取缓存的配置
         CourseConfig config;
-        synchronized(configLock) {
+        synchronized (configLock) {
             config = cachedCourseConfig;
         }
 
@@ -1167,7 +1166,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         // 使用 Java 8 时间 API 计算过期时间
         LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
-        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays-1);
+        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays - 1);
         expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
         Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
         link.setUpdateTime(updateTime);
@@ -1181,7 +1180,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
     private void addWatchLogIfNeeded(QwSopLogs sopLogs, Long videoId, Long courseId,
                                      Date sendTime, String qwUserId, String companyUserId,
-                                     String companyId, String externalId,SopUserLogsVo logsVo) {
+                                     String companyId, String externalId, SopUserLogsVo logsVo) {
         FsCourseWatchLog watchLog = new FsCourseWatchLog();
         watchLog.setVideoId(videoId != null ? videoId.longValue() : null);
         watchLog.setQwExternalContactId(externalId != null ? Long.valueOf(externalId) : null);
@@ -1192,20 +1191,21 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         watchLog.setCourseId(courseId != null ? courseId.longValue() : null);
         watchLog.setCompanyUserId(companyUserId != null ? Long.valueOf(companyUserId) : null);
         watchLog.setCompanyId(companyId != null ? Long.valueOf(companyId) : null);
-        watchLog.setCreateTime(convertStringToDate(sopLogs.getSendTime(),"yyyy-MM-dd HH:mm:ss"));
+        watchLog.setCreateTime(convertStringToDate(sopLogs.getSendTime(), "yyyy-MM-dd HH:mm:ss"));
         watchLog.setUpdateTime(new Date());
         watchLog.setLogType(3);
         watchLog.setUserId(sopLogs.getFsUserId());
-        watchLog.setCampPeriodTime(convertStringToDate(logsVo.getStartTime(),"yyyy-MM-dd"));
+        watchLog.setCampPeriodTime(convertStringToDate(logsVo.getStartTime(), "yyyy-MM-dd"));
         enqueueWatchLog(watchLog);
     }
 
     /**
      * 时间字符串转Date时间
+     *
      * @param dateString
      * @return
      */
-    public static Date convertStringToDate(String dateString,String pattern) {
+    public static Date convertStringToDate(String dateString, String pattern) {
         if (dateString == null || dateString.isEmpty()) {
             return null;
         }
@@ -1213,12 +1213,12 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         LocalDateTime localDateTime;
         LocalDate localDate;
         // 先解析成 LocalDate(只含年月日)
-        if (pattern.equals("yyyy-MM-dd")){
+        if (pattern.equals("yyyy-MM-dd")) {
             // 先解析成 LocalDate(只含年月日)
             localDate = LocalDate.parse(dateString, formatter);
             // 将 LocalDate 转为当天 00:00:00 的 LocalDateTime
             localDateTime = localDate.atStartOfDay();
-        }else {
+        } else {
             localDateTime = LocalDateTime.parse(dateString, formatter);
         }
         return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
@@ -1412,7 +1412,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
      */
     @Transactional
     @Retryable(
-            value = { Exception.class },
+            value = {Exception.class},
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
     )
@@ -1431,7 +1431,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
      */
     @Transactional
     @Retryable(
-            value = { Exception.class },
+            value = {Exception.class},
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
     )
@@ -1451,7 +1451,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
      */
     @Transactional
     @Retryable(
-            value = { Exception.class },
+            value = {Exception.class},
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
     )
@@ -1471,7 +1471,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
      */
     @Transactional
     @Retryable(
-            value = { Exception.class },
+            value = {Exception.class},
             maxAttempts = 3,
             backoff = @Backoff(delay = 2000)
     )
@@ -1489,7 +1489,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     @Override
     public void updateSopLogsByCancel() {
         List<QwSopLogs> sopLogs = qwSopLogsMapper.selectQwSopLogsByCancel();
-        log.info("补发过期完课消息总条数:{}",sopLogs.size());
+        log.info("补发过期完课消息总条数:{}", sopLogs.size());
         processUpdateQwSopLogs(sopLogs);
     }
 
@@ -1508,7 +1508,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             // 直接使用批次数据进行批量更新,不需要额外的 List
             try {
                 qwSopLogsMapper.batchUpdateQwSopLogsByCancel(batchList);
-                log.info("正在补发条数:{}",batchSize);
+                log.info("正在补发条数:{}", batchSize);
             } catch (Exception e) {
                 // 记录异常日志,方便后续排查问题
                 log.error("批量更新数据时发生异常,处理的批次起始索引为: " + i, e);
@@ -1554,7 +1554,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 //                sopLogs.setSopId(finishLog.getSopId());
 //                sopLogs.setExternalUserId(externalContact.getExternalUserId());
 //                sopLogs.setExternalUserName(externalContact.getName());
-////                    log.info("外部联系人名称:{}",externalContact.getName());
+
+    /// /                    log.info("外部联系人名称:{}",externalContact.getName());
 //                sopLogs.setFsUserId(finishLog.getUserId());
 //                //解析模板
 //                String jsonData = finishTemp.getSetting();
@@ -1606,7 +1607,6 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 //        QwSopTempSetting.Content content = JSON.parseObject(logs.getContentJson(), QwSopTempSetting.Content.class);
 //        handleNormalMessage(logs, content,null);
 //    }
-
     @Override
     public void createCourseFinishMsg() {
         // 查询所有需要处理的完课记录
@@ -1636,14 +1636,14 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
                 List<QwGroupChatUser> qwGroupChatUserList = qwGroupChatUserService.selectUserIsChat(externalContact.getExternalUserId());
-                if(!qwGroupChatUserList.isEmpty()){
-                    if(finishTemp != null){
+                if (!qwGroupChatUserList.isEmpty()) {
+                    if (finishTemp != null && StringUtils.isNotEmpty(finishTemp.getChatSetting())) {
                         List<SopUserLogs> sopLogsList = sopUserLogsMapper.selectSopUserLogByChatIds(PubFun.listToNewList(qwGroupChatUserList, QwGroupChatUser::getChatId));
                         List<QwGroupChat> qwGroupChatList = qwGroupChatService.selectQwGroupChatByChatIds(PubFun.listToNewList(sopLogsList, SopUserLogs::getChatId).toArray(new String[0]));
                         Map<String, QwGroupChat> groupChatMap = PubFun.listToMapByGroupObject(qwGroupChatList, QwGroupChat::getChatId);
                         sopLogsList.forEach(e -> {
                             QwGroupChat groupChat = groupChatMap.get(e.getChatId());
-                            if(groupChat != null){
+                            if (groupChat != null) {
                                 e.setChatName(groupChat.getName());
                             }
                         });
@@ -1652,7 +1652,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     }
                 }
                 if (finishTemp == null) {
-//                    log.warn("完课模板不存在: " + finishLog.getCompanyUserId() + ", " + finishLog.getVideoId());
+                    log.warn("完课模板不存在: " + finishLog.getCompanyUserId() + ", " + finishLog.getVideoId());
                     continue;
                 }
                 // 构建 sopLogs 对象
@@ -1703,6 +1703,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         }
     }
 
+    // 创建群聊完课消息
     private List<QwSopLogs> buildSopLogsChat(FsCourseWatchLog finishLog, FsCourseFinishTemp finishTemp, QwExternalContact externalContact, List<SopUserLogs> sopLogsList) {
         return sopLogsList.stream().map(e -> {
             QwSopChatTempSetting setting = new QwSopChatTempSetting();
@@ -1724,7 +1725,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             sopLogs.setSopId(e.getSopId());
             sopLogs.setExternalUserId(e.getChatId());
             sopLogs.setExternalUserName(e.getChatName());
-            if(finishTemp.getChatSetting() == null || finishTemp.getChatSetting().isEmpty()){
+            if (finishTemp.getChatSetting() == null || finishTemp.getChatSetting().isEmpty()) {
                 throw new BaseException("消息为空");
             }
             JSONArray list = new JSONArray();

+ 12 - 0
fs-service-system/src/main/java/com/fs/course/config/CourseConfig.java

@@ -1,9 +1,12 @@
 package com.fs.course.config;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.time.LocalTime;
+import java.util.List;
 
 @Data
 public class CourseConfig implements Serializable {
@@ -23,5 +26,14 @@ public class CourseConfig implements Serializable {
     private Integer redPacketMode;//红包模式 1总公司 2销售公司
     private BigDecimal moneyPri;//充值手续费百分比
     private BigDecimal redPackageMoney;//充值手续费百分比
+    private List<DisabledTimeVo> disabledTimeList;//充值手续费百分比
+
+    @Data
+    public static class DisabledTimeVo{
+        @JsonFormat(pattern = "HH:mm")
+        private LocalTime startDisabledTime;
+        @JsonFormat(pattern = "HH:mm")
+        private LocalTime endDisabledTime;
+    }
 
 }

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

@@ -139,6 +139,9 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
     @Select("select l.qw_external_contact_id,l.company_id,l.company_user_id,l.log_type,ext.`level`,l.qw_user_id FROM fs_course_watch_log l LEFT JOIN qw_external_contact ext ON ext.id =l.qw_external_contact_id  where l.sop_id=#{SopId} and  date(l.create_time)= CURDATE() and l.log_type =4 and l.video_id not in (select video_id from fs_user_course_video WHERE is_first=1 ) ")
     List<FsCourseWatchLogTaskVO> selectFsCourseWatchLogByDaySopId4(@Param("SopId")String SopId);
+
+    @Select("select l.user_id,l.company_id,l.company_user_id,l.log_type,ext.`level`,l.qw_user_id FROM fs_course_watch_log l LEFT JOIN fs_user ext ON ext.user_id =l.user_id  where l.sop_id=#{SopId} and  date(l.create_time)= CURDATE() and l.log_type =4 and l.video_id not in (select video_id from fs_user_course_video WHERE is_first=1 )")
+    List<FsCourseWatchLogTaskVO> selectFsCourseWatchLogByDaySopIdFsUser4(@Param("SopId")String SopId);
 //    @Select("   SELECT\n" +
 //            "            wl.log_type,\n" +
 //            "            ec.level\n" +

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

@@ -17,4 +17,6 @@ public class FsCourseWatchLogTaskVO {
     private Integer level;
 
     private Long qwUserId;
+
+    private Long userId;
 }

+ 61 - 0
fs-service-system/src/main/java/com/fs/qw/domain/HyWorkTask.java

@@ -0,0 +1,61 @@
+package com.fs.qw.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 个微任务看板对象 hy_work_task
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class HyWorkTask extends BaseEntity{
+
+    /** id */
+    private Long id;
+
+    /** 外部联系人id */
+    @Excel(name = "外部联系人id")
+    private Long extId;
+
+    /** 企微用户id */
+    @Excel(name = "企微用户id")
+    private Long qwUserId;
+
+    private Long userId;
+
+    /** 类别 1先导 2 课程 3 大小转 4 转人工 */
+    @Excel(name = "类别 1先导 2 课程 3 大小转 4 转人工")
+    private Integer type;
+
+    /** 状态 0 待处理 1 已处理 3 过期 */
+    @Excel(name = "状态 0 待处理 1 已处理 3 过期")
+    private Integer status;
+
+    /** 分值 */
+    @Excel(name = "分值")
+    private Integer score;
+
+    /** sopid */
+    @Excel(name = "sopid")
+    private String sopId;
+
+    /** 公司id */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long companyUserId;
+
+    private String title;
+
+    /**
+     * 营期id
+     */
+    private String userLogsId;
+}

+ 14 - 7
fs-service-system/src/main/java/com/fs/qw/mapper/HyWorkTaskMapper.java

@@ -1,6 +1,8 @@
 package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.qw.domain.HyWorkTask;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
 import com.fs.qw.vo.QwWorkTaskListVO;
@@ -17,14 +19,14 @@ import java.util.Map;
  * @author fs
  * @date 2025-03-18
  */
-public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
+public interface HyWorkTaskMapper extends BaseMapper<HyWorkTask>{
     /**
      * 查询企微任务看板
      *
      * @param id 企微任务看板主键
      * @return 企微任务看板
      */
-    QwWorkTask selectHyWorkTaskById(Long id);
+    HyWorkTask selectHyWorkTaskById(Long id);
 
     /**
      * 查询企微任务看板列表
@@ -32,7 +34,7 @@ public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
      * @param qwWorkTask 企微任务看板
      * @return 企微任务看板集合
      */
-    List<QwWorkTask> selectHyWorkTaskList(QwWorkTask qwWorkTask);
+    List<HyWorkTask> selectHyWorkTaskList(HyWorkTask qwWorkTask);
 
     /**
      * 新增企微任务看板
@@ -40,7 +42,7 @@ public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
      * @param qwWorkTask 企微任务看板
      * @return 结果
      */
-    int insertHyWorkTask(QwWorkTask qwWorkTask);
+    int insertHyWorkTask(HyWorkTask qwWorkTask);
 
     /**
      * 修改企微任务看板
@@ -48,7 +50,7 @@ public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
      * @param qwWorkTask 企微任务看板
      * @return 结果
      */
-    int updateHyWorkTask(QwWorkTask qwWorkTask);
+    int updateHyWorkTask(HyWorkTask qwWorkTask);
 
     /**
      * 删除企微任务看板
@@ -66,7 +68,7 @@ public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
      */
     int deleteHyWorkTaskByIds(Long[] ids);
 
-    void insertQwWorkTaskBatch(@Param("qwWorkTasks")List<QwWorkTask> qwWorkTasks);
+    void insertQwWorkTaskBatch(@Param("qwWorkTasks")List<HyWorkTask> qwWorkTasks);
 
     List<QwWorkTaskListVO> selectHyWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
 
@@ -106,7 +108,12 @@ public interface HyWorkTaskMapper extends BaseMapper<QwWorkTask>{
      * @param params 参数
      * @return list
      */
-    List<QwWorkTask> selectHyWorkTaskListByMap(@Param("params") Map<String, Object> params);
+    List<HyWorkTask> selectHyWorkTaskListByMap(@Param("params") Map<String, Object> params);
 
     Long selectHyWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
+
+    void hyWorkTask();
+
+    List<FsCourseWatchLog> hyWorkTaskGetInterruptedAndFirst();
+
 }

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

@@ -6,6 +6,7 @@ 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 com.fs.qw.vo.UserVOs;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -126,4 +127,6 @@ public interface QwWorkTaskMapper extends BaseMapper<QwWorkTask>{
     List<QwWorkTask> selectQwWorkTaskListByMap(@Param("params") Map<String, Object> params);
 
     Long selectQwWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
+
+    List<UserVOs> getUserList(@Param("userId") Long userId);
 }

+ 11 - 7
fs-service-system/src/main/java/com/fs/qw/service/IHyWorkTaskService.java

@@ -1,6 +1,7 @@
 package com.fs.qw.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.qw.domain.HyWorkTask;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
 import com.fs.qw.vo.QwWorkTaskListVO;
@@ -14,14 +15,14 @@ import java.util.Map;
  * @author fs
  * @date 2025-03-18
  */
-public interface IHyWorkTaskService extends IService<QwWorkTask>{
+public interface IHyWorkTaskService extends IService<HyWorkTask>{
     /**
      * 查询企微任务看板
      *
      * @param id 企微任务看板主键
      * @return 企微任务看板
      */
-    QwWorkTask selectHyWorkTaskById(Long id);
+    HyWorkTask selectHyWorkTaskById(Long id);
 
     /**
      * 查询企微任务看板列表
@@ -29,7 +30,7 @@ public interface IHyWorkTaskService extends IService<QwWorkTask>{
      * @param qwWorkTask 企微任务看板
      * @return 企微任务看板集合
      */
-    List<QwWorkTask> selectHyWorkTaskList(QwWorkTask qwWorkTask);
+    List<HyWorkTask> selectHyWorkTaskList(HyWorkTask qwWorkTask);
 
     /**
      * 新增企微任务看板
@@ -37,7 +38,7 @@ public interface IHyWorkTaskService extends IService<QwWorkTask>{
      * @param qwWorkTask 企微任务看板
      * @return 结果
      */
-    int insertHyWorkTask(QwWorkTask qwWorkTask);
+    int insertHyWorkTask(HyWorkTask qwWorkTask);
 
     /**
      * 修改企微任务看板
@@ -45,7 +46,7 @@ public interface IHyWorkTaskService extends IService<QwWorkTask>{
      * @param qwWorkTask 企微任务看板
      * @return 结果
      */
-    int updateHyWorkTask(QwWorkTask qwWorkTask);
+    int updateHyWorkTask(HyWorkTask qwWorkTask);
 
     /**
      * 批量删除企微任务看板
@@ -78,7 +79,7 @@ public interface IHyWorkTaskService extends IService<QwWorkTask>{
      * 根据转化日(或特定业务日期节点)情况,添加相应的企微任务。
      * 用于处理基于特定日期或转化阶段的任务创建逻辑。
      */
-    void addHyWorkByConversionDay();
+    void  addHyWorkByConversionDay();
 
     List<QwWorkTaskListVO> selectHyWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
     Long selectHyWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
@@ -105,5 +106,8 @@ public interface IHyWorkTaskService extends IService<QwWorkTask>{
      * @param params 参数
      * @return list
      */
-    List<QwWorkTask> selectHyWorkTaskListByMap(Map<String, Object> params);
+    List<HyWorkTask> selectHyWorkTaskListByMap(Map<String, Object> params);
+
+    void hyWorkTask();
+
 }

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

@@ -9,6 +9,7 @@ import com.fs.qw.param.QwUserParam;
 import com.fs.qw.vo.QwHookAuthVO;
 import com.fs.qw.vo.QwOptionsVO;
 import com.fs.qw.vo.QwUserVO;
+import com.fs.qw.vo.UserVOs;
 
 import java.util.List;
 
@@ -148,4 +149,6 @@ public interface IQwUserService
     QwUser getByQwUserIdAndCorId(String qwUserId, String corpId);
 
     R selectCloudByCompany(Long companyId, Long userId);
+
+    List<UserVOs> getUserList(Long userId);
 }

+ 100 - 20
fs-service-system/src/main/java/com/fs/qw/service/impl/HyWorkTaskServiceImpl.java

@@ -8,9 +8,11 @@ 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.domain.FsCourseWatchLog;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.vo.FsCourseWatchLogTaskVO;
 import com.fs.course.vo.FsQwCourseWatchLogVO;
+import com.fs.qw.domain.HyWorkTask;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.mapper.HyWorkTaskMapper;
@@ -25,8 +27,10 @@ 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 lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
@@ -42,7 +46,8 @@ import java.util.stream.Collectors;
  * @date 2025-03-18
  */
 @Service
-public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkTask> implements IHyWorkTaskService {
+@Slf4j
+public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, HyWorkTask> implements IHyWorkTaskService {
     @Autowired
     private FsCourseWatchLogMapper fsCourseWatchLogMapper;
     @Autowired
@@ -65,7 +70,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
      * @return 企微任务看板
      */
     @Override
-    public QwWorkTask selectHyWorkTaskById(Long id)
+    public HyWorkTask selectHyWorkTaskById(Long id)
     {
         return baseMapper.selectHyWorkTaskById(id);
     }
@@ -77,7 +82,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
      * @return 企微任务看板
      */
     @Override
-    public List<QwWorkTask> selectHyWorkTaskList(QwWorkTask qwWorkTask)
+    public List<HyWorkTask> selectHyWorkTaskList(HyWorkTask qwWorkTask)
     {
         return baseMapper.selectHyWorkTaskList(qwWorkTask);
     }
@@ -89,7 +94,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
      * @return 结果
      */
     @Override
-    public int insertHyWorkTask(QwWorkTask qwWorkTask)
+    public int insertHyWorkTask(HyWorkTask qwWorkTask)
     {
         qwWorkTask.setCreateTime(DateUtils.getNowDate());
         return baseMapper.insertHyWorkTask(qwWorkTask);
@@ -102,7 +107,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
      * @return 结果
      */
     @Override
-    public int updateHyWorkTask(QwWorkTask qwWorkTask)
+    public int updateHyWorkTask(HyWorkTask qwWorkTask)
     {
         qwWorkTask.setUpdateTime(DateUtils.getNowDate());
         return baseMapper.updateHyWorkTask(qwWorkTask);
@@ -135,9 +140,9 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
     @Override
     public void addHyWorkByFirstCourse() {
             List<FsQwCourseWatchLogVO> fsQwCourseWatchLogVOS = fsCourseWatchLogMapper.selectFsCourseWatchLogByVideoId();
-        ArrayList<QwWorkTask> qwWorkTasks = new ArrayList<>();
+        ArrayList<HyWorkTask> qwWorkTasks = new ArrayList<>();
         for (FsQwCourseWatchLogVO vo : fsQwCourseWatchLogVOS) {
-                QwWorkTask qwWorkTask = new QwWorkTask();
+                HyWorkTask qwWorkTask = new HyWorkTask();
                 qwWorkTask.setCreateTime(DateUtils.getNowDate());
                 qwWorkTask.setExtId(vo.getQwExternalContactId());
                 qwWorkTask.setCompanyId(vo.getCompanyId());
@@ -181,6 +186,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
         LocalDate today = LocalDate.now();
         List<Long> extIds = hyWorkTaskMapper.selectHyWorkTaskByType();
         Set<Long> extIdSet = new HashSet<>(extIds);
+        // 获取出执行sop的记录数
         for (QwSop qwSop : qwSops) {
             if (qwSop.getCourseDay()==null){
                 continue;
@@ -190,12 +196,12 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
             if (qwSopLogs==null || qwSopLogs.isEmpty()) {
                 continue;
             }
-
+            // 每次sop的观看记录
             List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogByDaySopId3(qwSop.getId());
             if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
                 continue;
             }
-            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            List<HyWorkTask> qwWorkTasks = new ArrayList<>();
             for (SopUserLogsInfo qwSopLog : qwSopLogs) {
                 Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
                         .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getQwExternalContactId, data -> data,(oldValue, newValue) -> newValue ));
@@ -214,7 +220,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
                 if (score==0){
                     continue;
                 }
-                QwWorkTask qwWorkTask = new QwWorkTask();
+                HyWorkTask qwWorkTask = new HyWorkTask();
                 qwWorkTask.setCreateTime(DateUtils.getNowDate());
                 qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
                 qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
@@ -240,10 +246,12 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
     public void addHyWorkByConversionDay() {
         List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRatingNotNull();
         Map<Integer, Integer> minMap = new HashMap<>();
+        // level->分值映射
         minMap.put(1, 10);
         minMap.put(2, 6);
         minMap.put(3, 2);
         Map<Integer, Integer> MaxMap = new HashMap<>();
+        // level->分值映射
         MaxMap.put(1, 25);
         MaxMap.put(2, 15);
         MaxMap.put(3, 5);
@@ -295,7 +303,9 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
 
     @Override
     public void addHyWorkByCourse4() {
+        // 获取sop模板
         List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        // 课程
         List<Long> extIds = hyWorkTaskMapper.selectHyWorkTaskByType();
         Set<Long> extIdSet = new HashSet<>(extIds);
         LocalDate today = LocalDate.now();
@@ -304,19 +314,19 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
                 continue;
             }
             Integer courseDay=qwSop.getCourseDay()-1;
-            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectSopUserLogsInfoBySopId(qwSop.getId());
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectFsUserIdSopUserLogsInfoBySopId(qwSop.getId());
             if (qwSopLogs==null || qwSopLogs.isEmpty()) {
                 continue;
             }
-            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogByDaySopId4(qwSop.getId());
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogByDaySopIdFsUser4(qwSop.getId());
             if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
                 continue;
             }
-            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            List<HyWorkTask> 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());
+                        .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getUserId, data -> data,(oldValue, newValue) -> newValue ));
+                FsCourseWatchLogTaskVO fsCourseWatchLog = map.get(qwSopLog.getFsUserId());
                 if (fsCourseWatchLog == null) {
                     continue;
                 }
@@ -333,7 +343,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
                 if (score==0){
                     continue;
                 }
-                QwWorkTask qwWorkTask = new QwWorkTask();
+                HyWorkTask qwWorkTask = new HyWorkTask();
                 qwWorkTask.setCreateTime(DateUtils.getNowDate());
                 qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
                 qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
@@ -395,7 +405,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
             if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
                 continue;
             }
-            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            List<HyWorkTask> qwWorkTasks = new ArrayList<>();
             for (SopUserLogsInfo qwSopLog : qwSopLogs) {
                 Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
                         .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getQwExternalContactId, data -> data,(oldValue, newValue) -> newValue ));
@@ -418,7 +428,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
                 if (score==0){
                     continue;
                 }
-                QwWorkTask qwWorkTask = new QwWorkTask();
+                HyWorkTask qwWorkTask = new HyWorkTask();
                 qwWorkTask.setCreateTime(DateUtils.getNowDate());
                 qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
                 qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
@@ -446,10 +456,80 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
      * @return list
      */
     @Override
-    public List<QwWorkTask> selectHyWorkTaskListByMap(Map<String, Object> params) {
+    public List<HyWorkTask> selectHyWorkTaskListByMap(Map<String, Object> params) {
         return hyWorkTaskMapper.selectHyWorkTaskListByMap(params);
     }
 
+    // 定义批处理大小常量
+    private static final int BATCH_SIZE = 1000;
+
+    @Override
+    public void hyWorkTask() {
+
+        // 获取看课中断和待看的先导课
+        List<FsCourseWatchLog> fsCourseWatchLogs = hyWorkTaskMapper.hyWorkTaskGetInterruptedAndFirst();
+        if(CollectionUtils.isEmpty(fsCourseWatchLogs)){
+            log.info("[获取看课中断和待看的先导课] 没有找到数据,已经跳过!");
+            return;
+        }
+        List<HyWorkTask> batchList = new ArrayList<>(BATCH_SIZE);
+        int totalProcessed = 0;
+        int batchCount = 0;
+        for (FsCourseWatchLog fsCourseWatchLog : fsCourseWatchLogs) {
+            HyWorkTask hyWorkTask = new HyWorkTask();
+            hyWorkTask.setCreateTime(DateUtils.getNowDate());
+            hyWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
+            hyWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
+            hyWorkTask.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+            hyWorkTask.setUserId(fsCourseWatchLog.getUserId());
+            if(fsCourseWatchLog.getPeriodId() != null) {
+                hyWorkTask.setUserLogsId(String.valueOf(fsCourseWatchLog.getPeriodId()));
+            }
+            hyWorkTask.setStatus(0);
+            hyWorkTask.setUserId(fsCourseWatchLog.getUserId());
+
+            // 看课中断
+            if(fsCourseWatchLog.getLogType()==4){
+                hyWorkTask.setType(2);
+                Date createTime = fsCourseWatchLog.getCreateTime();
+                long day = DateUtils.getDaysDifferenceFromNow(createTime);
+                hyWorkTask.setTitle("第"+day+"天"+"看课中断");
+            } else {
+                hyWorkTask.setType(1);
+                hyWorkTask.setTitle(fsCourseWatchLog.getLogType()==3?"先导课待看课":"先导课完课");
+            }
+            hyWorkTask.setScore(fsCourseWatchLog.getLogType()==3?4:3);
+
+            batchList.add(hyWorkTask);
+            totalProcessed++;
+
+            if (batchList.size() >= BATCH_SIZE) {
+                batchCount++;
+                log.info("处理到第 {} 条数据,开始插入第 {} 批 ({} 条)...", totalProcessed, batchCount, batchList.size());
+                try {
+                    hyWorkTaskMapper.insertQwWorkTaskBatch(batchList);
+                    log.info("第 {} 批插入成功.", batchCount);
+                } catch (Exception e) {
+                    log.error("第 {} 批插入时发生错误: {}", batchCount, e.getMessage(), e);
+                }
+                batchList.clear();
+            }
+        }
+        if (!batchList.isEmpty()) {
+            batchCount++;
+            log.info("处理完成,开始插入最后一批 ({} 条)...", batchList.size());
+            try {
+                hyWorkTaskMapper.insertQwWorkTaskBatch(batchList);
+                log.info("最后一批插入成功.");
+            } catch (Exception e) {
+                log.error("最后一批插入时发生错误: {}", e.getMessage(), e);
+            }
+            batchList.clear();
+        }
+        log.info("hyWorkTask 任务执行完毕,共处理 {} 条数据,分 {} 批插入.", totalProcessed, batchCount > 0 ? batchCount : (totalProcessed > 0 ? 1 : 0));
+
+    }
+
     /**
      * 根据SOP执行日志和特定条件,为符合要求的外部联系人添加企业微信工作任务。
      * <p>
@@ -493,7 +573,7 @@ public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, QwWorkT
 
 
     private void insertHyWorkTask(String sopId,QwExternalContact qwExternalContact, Integer type, String title, Integer score) {
-        QwWorkTask qwWorkTask = new QwWorkTask();
+        HyWorkTask qwWorkTask = new HyWorkTask();
         qwWorkTask.setCreateTime(DateUtils.getNowDate());
         qwWorkTask.setExtId(qwExternalContact.getId());
         qwWorkTask.setCompanyId(qwExternalContact.getCompanyId());

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

@@ -12,16 +12,14 @@ import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.dto.QwUserByToolDTO;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.mapper.QwWorkTaskMapper;
 import com.fs.qw.param.*;
 import com.fs.qw.result.QwFsServerBindResult;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwUserService;
 import com.fs.qw.utils.RSASignatureUtils;
 import com.fs.qw.utils.RSAUtils;
-import com.fs.qw.vo.QwCloudAPVO;
-import com.fs.qw.vo.QwHookAuthVO;
-import com.fs.qw.vo.QwOptionsVO;
-import com.fs.qw.vo.QwUserVO;
+import com.fs.qw.vo.*;
 import com.fs.qwApi.Result.QwOpenidResult;
 import com.fs.qwApi.Result.QwUploadImgResult;
 import com.fs.qwApi.domain.QwUserIdResult;
@@ -43,6 +41,7 @@ import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.Base64;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
@@ -76,6 +75,9 @@ public class QwUserServiceImpl implements IQwUserService
     @Autowired
     private ConfigUtil configUtil;
 
+    @Autowired
+    private QwWorkTaskMapper qwWorkTaskMapper;
+
 //    @Value("${hook.path}")
 //    private String hookPath;
 
@@ -906,6 +908,11 @@ public class QwUserServiceImpl implements IQwUserService
         return R.ok().put("data",qwUserByToolDTOS);
     }
 
+    @Override
+    public List<UserVOs> getUserList(Long userId) {
+        return qwWorkTaskMapper.getUserList(userId);
+    }
+
     public R GetIPAdminAndPassWord(String ipAddress ) throws Exception {
 
         String bodyKey = HttpRequest.get("http://watch.ylrzcloud.com/prod-api/server/getKey?serverIp="+ipAddress)

+ 17 - 0
fs-service-system/src/main/java/com/fs/qw/vo/UserVOs.java

@@ -0,0 +1,17 @@
+package com.fs.qw.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class UserVOs {
+    private String name;
+    private String avatar;
+    private String title;
+    private Integer score;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+    private Integer type;
+}

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

@@ -362,6 +362,11 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
     List<QwSop> selectWxSop();
     @DataSource(DataSourceType.SOP)
     List<QwSop> selectQwSopByIsRating();
+    @DataSource(DataSourceType.SOP)
+    List<QwSop> selectQwSopByIsRatingAndFsUser();
+
+
+
     @DataSource(DataSourceType.SOP)
     @Select("select * FROM qw_sop where is_rating = 1 and send_type in(2,3) and min_conversion_day is not null and max_conversion_day is not null order by create_time desc")
     List<QwSop> selectQwSopByIsRatingNotNull();

+ 4 - 0
fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java

@@ -200,6 +200,10 @@ public interface SopUserLogsInfoMapper {
     @Select("SELECT external_id,create_time  FROM sop_user_logs_info where sop_id = #{sopId} ")
     List<SopUserLogsInfo> selectSopUserLogsInfoBySopId(@Param("sopId")String sopId);
 
+    @DataSource(DataSourceType.SOP)
+    @Select("SELECT create_time,fs_user_id  FROM sop_user_logs_info where sop_id = #{sopId} ")
+    List<SopUserLogsInfo> selectFsUserIdSopUserLogsInfoBySopId(@Param("sopId")String sopId);
+
     @DataSource(DataSourceType.SOP)
     List<SopUserLogsInfo> repeatProject(@Param("projects") List<Integer> projects, @Param("externalUserID") String externalUserID);
     @DataSource(DataSourceType.SOP)

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

@@ -97,4 +97,6 @@ public interface IQwSopTempService
     List<QwSopTempRedPackageVo> redList(String id);
 
     void updateRedPackage(List<QwSopTempRedPackageVo> list);
+
+    List<String> getSelectableRange();
 }

+ 9 - 0
fs-service-system/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -26,6 +26,7 @@ import com.fs.sop.domain.QwSopTempRules;
 import com.fs.sop.mapper.QwSopTempMapper;
 import com.fs.sop.params.QwSopShareTempParam;
 import com.fs.sop.service.*;
+import com.fs.sop.util.TimeCalculator;
 import com.fs.sop.vo.QwSopTempRedPackageVo;
 import com.fs.sop.vo.VoiceVo;
 import com.fs.system.domain.SysConfig;
@@ -336,6 +337,14 @@ public class QwSopTempServiceImpl implements IQwSopTempService
         fsUserCourseVideoRedPackageService.batchSaveFsUserCourseVideoRedPackage(redPackage);
     }
 
+    @Override
+    public List<String> getSelectableRange() {
+        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("course.config");
+        CourseConfig courseConfig = JSON.parseObject(sysConfig.getConfigValue(), CourseConfig.class);
+        List<CourseConfig.DisabledTimeVo> disabledTimeList = courseConfig.getDisabledTimeList();
+        return TimeCalculator.calculateAvailableTimes(disabledTimeList);
+    }
+
     @Override
     @DataSource(DataSourceType.SOP)
     public int update(QwSopTemp qwSopTemp) {

+ 89 - 0
fs-service-system/src/main/java/com/fs/sop/util/TimeCalculator.java

@@ -0,0 +1,89 @@
+package com.fs.sop.util;
+
+import com.fs.course.config.CourseConfig;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TimeCalculator {
+
+    // 计算可用时间段的入口方法
+    public static List<String> calculateAvailableTimes(List<CourseConfig.DisabledTimeVo> disabledTimeList) {
+        List<CourseConfig.DisabledTimeVo> merged = mergeTimeRanges(disabledTimeList);
+        List<String> availableTimes = new ArrayList<>();
+        LocalTime prevEnd = LocalTime.MIN; // 初始化为00:00
+
+        for (CourseConfig.DisabledTimeVo interval : merged) {
+            LocalTime currentStart = interval.getStartDisabledTime();
+            LocalTime currentEnd = interval.getEndDisabledTime();
+
+            // 计算两个禁用时间段之间的间隙
+            if (currentStart.isAfter(prevEnd)) {
+                LocalTime availableStart = prevEnd.plusMinutes(1);
+                LocalTime availableEnd = currentStart.minusMinutes(1);
+                if (!availableStart.isAfter(availableEnd)) {
+                    availableTimes.add(formatTimeRange(availableStart, availableEnd));
+                }
+            }
+            // 更新prevEnd为较大的currentEnd
+            prevEnd = prevEnd.isBefore(currentEnd) ? currentEnd : prevEnd;
+        }
+
+        // 处理最后一个禁用时间段之后的时间(直到23:59)
+        LocalTime dayEnd = LocalTime.of(23, 59);
+        if (prevEnd.isBefore(dayEnd)) {
+            LocalTime availableStart = prevEnd.plusMinutes(1);
+            availableTimes.add(formatTimeRange(availableStart, dayEnd));
+        }
+
+        return availableTimes;
+    }
+
+    // 合并重叠或相邻的时间段
+    private static List<CourseConfig.DisabledTimeVo> mergeTimeRanges(List<CourseConfig.DisabledTimeVo> disabledTimeList) {
+        if (disabledTimeList.isEmpty()) return new ArrayList<>();
+
+        // 1. 按开始时间排序
+        List<CourseConfig.DisabledTimeVo> sorted = disabledTimeList.stream()
+                .sorted(Comparator.comparing(CourseConfig.DisabledTimeVo::getStartDisabledTime))
+                .collect(Collectors.toList());
+
+        // 2. 合并时间段
+        List<CourseConfig.DisabledTimeVo> merged = new ArrayList<>();
+        CourseConfig.DisabledTimeVo current = copy(sorted.get(0));
+        merged.add(current);
+
+        for (int i = 1; i < sorted.size(); i++) {
+            CourseConfig.DisabledTimeVo next = sorted.get(i);
+            // 如果下一个时间段的开始 <= 当前时间段的结束,合并
+            if (!next.getStartDisabledTime().isAfter(current.getEndDisabledTime())) {
+                LocalTime newEnd = next.getEndDisabledTime().isAfter(current.getEndDisabledTime())
+                        ? next.getEndDisabledTime()
+                        : current.getEndDisabledTime();
+                current.setEndDisabledTime(newEnd);
+            } else {
+                current = copy(next);
+                merged.add(current);
+            }
+        }
+        return merged;
+    }
+
+    // 辅助方法:复制对象以避免修改原始数据
+    private static CourseConfig.DisabledTimeVo copy(CourseConfig.DisabledTimeVo source) {
+        CourseConfig.DisabledTimeVo target = new CourseConfig.DisabledTimeVo();
+        target.setStartDisabledTime(source.getStartDisabledTime());
+        target.setEndDisabledTime(source.getEndDisabledTime());
+        return target;
+    }
+
+    // 格式化时间段为"HH:mm:ss - HH:mm:ss"
+    private static String formatTimeRange(LocalTime start, LocalTime end) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
+        return start.format(formatter) + " - " + end.format(formatter);
+    }
+}

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

@@ -37,4 +37,9 @@ public class AnalysisPreviewQueryDTO implements Serializable {
      */
     private Integer dataType;
 
+    /**
+     * 0 用户 1 企微
+     */
+    private Integer userType;
+
 }

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

@@ -27,4 +27,9 @@ public class DealerAggregatedDTO implements Serializable {
      */
     private Long blackNum;
 
+    /**
+     * 企微用户数
+     */
+    private Long qwMemberNum;
+
 }

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

@@ -17,30 +17,30 @@ public interface IStatisticsService {
     /**
      * 分析概览定时任务
      */
-    void analysisPreviewTask(Integer type);
+    void analysisPreviewTask(Integer type,Integer userType);
 
 
     /**
      * 会员观看、完播人数趋势图
      */
-    void watchEndPlayTrendTask(Integer type);
+    void watchEndPlayTrendTask(Integer type,Integer userType);
 
 
     /**
      * 经销商会员观看TOP10
      */
-    void companyWatchCourseTopTenTask(Integer type,Integer statisticalType);
+    void companyWatchCourseTopTenTask(Integer type,Integer statisticalType,Integer userType);
 
     /**
      * 课程观看TOP10
      */
-    void watchCourseTopTenTask(Integer type,Integer statisticalType,String sort);
+    void watchCourseTopTenTask(Integer type,Integer statisticalType,Integer userType,String sort);
 
     /**
      * 答题红包金额TOP10
      */
-    void rewardMoneyTopTenTask(Integer type,Integer dataType);
-    void rewardMoneyTradeTask(Integer type);
+    void rewardMoneyTopTenTask(Integer type,Integer dataType,Integer userType);
+    void rewardMoneyTradeTask(Integer type,Integer userType);
 
     /**
      * 分析概览 - 经销商统计

+ 192 - 97
fs-service-system/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java

@@ -60,20 +60,27 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
 
     public void analysisPreviewTask0(){
-        analysisPreviewTask(0);
+        analysisPreviewTask(0,1);
+        analysisPreviewTask(0,2);
     }
 
     public void analysisPreviewTask1(){
-        analysisPreviewTask(0);
-        analysisPreviewTask(1);
-        analysisPreviewTask(2);
-        analysisPreviewTask(3);
-        analysisPreviewTask(4);
+        analysisPreviewTask(0,1);
+        analysisPreviewTask(1,1);
+        analysisPreviewTask(2,1);
+        analysisPreviewTask(3,1);
+        analysisPreviewTask(4,1);
+
+        analysisPreviewTask(0,2);
+        analysisPreviewTask(1,2);
+        analysisPreviewTask(2,2);
+        analysisPreviewTask(3,2);
+        analysisPreviewTask(4,2);
     }
 
 
     @Override
-    public void analysisPreviewTask(Integer type) {
+    public void analysisPreviewTask(Integer type,Integer userType) {
         // 根据type计算出时间范围
         String startDate = "";
         String endDate = "";
@@ -108,30 +115,33 @@ public class StatisticsServiceImpl implements IStatisticsService {
         param.setStartTime(startDate);
         param.setEndTime(endDate);
         param.setType(type);
+        param.setUserType(userType);
 
         AnalysisPreviewDTO analysisPreviewDTO = this.analysisPreview(param);
 
-        redisCache.setCacheObject(String.format("%s:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type), analysisPreviewDTO);
+        redisCache.setCacheObject(String.format("%s:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType), analysisPreviewDTO);
     }
 
     public void watchEndPlayTrendTask0(){
-        this.watchEndPlayTrendTask(0);
-        this.watchEndPlayTrendTask(1);
-        this.watchEndPlayTrendTask(2);
-        this.watchEndPlayTrendTask(3);
-        this.watchEndPlayTrendTask(4);
+        this.watchEndPlayTrendTask(0,1);
+        this.watchEndPlayTrendTask(0,2);
     }
 
     public void watchEndPlayTrendTask1(){
-        this.watchEndPlayTrendTask(1);
-        this.watchEndPlayTrendTask(2);
-        this.watchEndPlayTrendTask(3);
-        this.watchEndPlayTrendTask(4);
+        this.watchEndPlayTrendTask(1,1);
+        this.watchEndPlayTrendTask(2,1);
+        this.watchEndPlayTrendTask(3,1);
+        this.watchEndPlayTrendTask(4,1);
+
+        this.watchEndPlayTrendTask(1,2);
+        this.watchEndPlayTrendTask(2,2);
+        this.watchEndPlayTrendTask(3,2);
+        this.watchEndPlayTrendTask(4,2);
     }
 
 
     @Override
-    public void watchEndPlayTrendTask(Integer type) {
+    public void watchEndPlayTrendTask(Integer type,Integer userType) {
         // 根据type计算出时间范围
         String startDate = "";
         String endDate = "";
@@ -167,35 +177,47 @@ public class StatisticsServiceImpl implements IStatisticsService {
         param.setEndTime(endDate);
         param.setType(type);
 
+        param.setUserType(userType);
         List<WatchEndPlayTrendDTO> watchEndPlayTrendDTOS = this.watchEndPlayTrend(param);
 
-        redisCache.setCacheObject(String.format("%s:%d",DATA_OVERVIEW_DEALER_CHARTS,type),watchEndPlayTrendDTOS);
+        redisCache.setCacheObject(String.format("%s:%d:%d",DATA_OVERVIEW_DEALER_CHARTS,type,userType),watchEndPlayTrendDTOS);
 
     }
 
 
     public void companyWatchCourseTopTenTask0(){
-        companyWatchCourseTopTenTask(0,0);
-        companyWatchCourseTopTenTask(0,1);
-    }
+        companyWatchCourseTopTenTask(0,0,1);
+        companyWatchCourseTopTenTask(0,1,1);
 
-    public void companyWatchCourseTopTenTask1(){
-        companyWatchCourseTopTenTask(1,0);
-        companyWatchCourseTopTenTask(1,1);
 
-        companyWatchCourseTopTenTask(2,0);
-        companyWatchCourseTopTenTask(2,1);
+        companyWatchCourseTopTenTask(0,0,2);
+        companyWatchCourseTopTenTask(0,1,2);
+    }
 
+    public void companyWatchCourseTopTenTask1(){
+        companyWatchCourseTopTenTask(1,0,1);
+        companyWatchCourseTopTenTask(1,1,1);
+        companyWatchCourseTopTenTask(2,0,1);
+        companyWatchCourseTopTenTask(2,1,1);
+        companyWatchCourseTopTenTask(3,0,1);
+        companyWatchCourseTopTenTask(3,1,1);
+        companyWatchCourseTopTenTask(4,0,1);
+        companyWatchCourseTopTenTask(4,1,1);
+
+        companyWatchCourseTopTenTask(1,0,2);
+        companyWatchCourseTopTenTask(1,1,2);
+        companyWatchCourseTopTenTask(2,0,2);
+        companyWatchCourseTopTenTask(2,1,2);
+        companyWatchCourseTopTenTask(3,0,2);
+        companyWatchCourseTopTenTask(3,1,2);
+        companyWatchCourseTopTenTask(4,0,2);
+        companyWatchCourseTopTenTask(4,1,2);
 
-        companyWatchCourseTopTenTask(3,0);
-        companyWatchCourseTopTenTask(3,1);
 
-        companyWatchCourseTopTenTask(4,0);
-        companyWatchCourseTopTenTask(4,1);
     }
 
     @Override
-    public void companyWatchCourseTopTenTask(Integer type,Integer statisticalType) {
+    public void companyWatchCourseTopTenTask(Integer type,Integer statisticalType,Integer userType) {
 
         AnalysisPreviewQueryDTO dto = new AnalysisPreviewQueryDTO();
         dto.setType(type);
@@ -232,13 +254,14 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
         dto.setStartTime(startDate);
         dto.setEndTime(endDate);
+        dto.setUserType(userType);
 
         List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = deaMemberTopTen(dto);
-        redisCache.setCacheObject(String.format("%s:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type,statisticalType), deaMemberTopTenDTOS);
+        redisCache.setCacheObject(String.format("%s:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type,statisticalType,userType), deaMemberTopTenDTOS);
     }
 
     @Override
-    public void watchCourseTopTenTask(Integer type,Integer statisticalType,String sort) {
+    public void watchCourseTopTenTask(Integer type,Integer statisticalType,Integer userType,String sort) {
         AnalysisPreviewQueryDTO dto = new AnalysisPreviewQueryDTO();
         dto.setType(type);
         dto.setStatisticalType(statisticalType);
@@ -275,111 +298,181 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
         dto.setStartTime(startDate);
         dto.setEndTime(endDate);
-
+        dto.setUserType(userType);
         List<CourseStatsDTO> courseStatsDTOS = watchCourseTopTen(dto);
 
-        redisCache.setCacheObject( String.format("%s:%d:%d:%s", CHARTS_WATCH_TOP_TEN, type,statisticalType,sort), courseStatsDTOS);
+        redisCache.setCacheObject( String.format("%s:%d:%d:%d:%s", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort), courseStatsDTOS);
     }
 
     public void watchCourseTopTenTask0(){
-        watchCourseTopTenTask(0,0,"DESC");
-        watchCourseTopTenTask(0,0,"ASC");
+        watchCourseTopTenTask(0,0,1,"DESC");
+        watchCourseTopTenTask(0,0,1,"ASC");
+        watchCourseTopTenTask(0,1,1,"DESC");
+        watchCourseTopTenTask(0,1,1,"ASC");
+        watchCourseTopTenTask(0,2,1,"DESC");
+        watchCourseTopTenTask(0,2,1,"ASC");
+        watchCourseTopTenTask(0,3,1,"DESC");
+        watchCourseTopTenTask(0,3,1,"ASC");
+
+        watchCourseTopTenTask(0,0,1,"DESC");
+        watchCourseTopTenTask(0,0,1,"ASC");
+        watchCourseTopTenTask(0,1,1,"DESC");
+        watchCourseTopTenTask(0,1,1,"ASC");
+        watchCourseTopTenTask(0,2,1,"DESC");
+        watchCourseTopTenTask(0,2,1,"ASC");
+        watchCourseTopTenTask(0,3,1,"DESC");
+        watchCourseTopTenTask(0,3,1,"ASC");
+    }
 
-        watchCourseTopTenTask(0,1,"DESC");
-        watchCourseTopTenTask(0,1,"ASC");
+    public void watchCourseTopTenTask1(){
+        watchCourseTopTenTask(1,0,1,"DESC");
+        watchCourseTopTenTask(1,0,1,"ASC");
 
-        watchCourseTopTenTask(0,2,"DESC");
-        watchCourseTopTenTask(0,2,"ASC");
+        watchCourseTopTenTask(1,0,2,"DESC");
+        watchCourseTopTenTask(1,0,2,"ASC");
 
-        watchCourseTopTenTask(0,3,"DESC");
-        watchCourseTopTenTask(0,3,"ASC");
-    }
 
-    public void watchCourseTopTenTask1(){
-        watchCourseTopTenTask(1,0,"DESC");
-        watchCourseTopTenTask(1,0,"ASC");
+        watchCourseTopTenTask(1,1,1,"DESC");
+        watchCourseTopTenTask(1,1,1,"ASC");
+
+        watchCourseTopTenTask(1,1,2,"DESC");
+        watchCourseTopTenTask(1,1,2,"ASC");
+
+        watchCourseTopTenTask(1,2,1,"DESC");
+        watchCourseTopTenTask(1,2,1,"ASC");
+
+        watchCourseTopTenTask(1,2,2,"DESC");
+        watchCourseTopTenTask(1,2,2,"ASC");
 
-        watchCourseTopTenTask(1,1,"DESC");
-        watchCourseTopTenTask(1,1,"ASC");
 
-        watchCourseTopTenTask(1,2,"DESC");
-        watchCourseTopTenTask(1,2,"ASC");
+        watchCourseTopTenTask(1,3,1,"DESC");
+        watchCourseTopTenTask(1,3,1,"ASC");
 
-        watchCourseTopTenTask(1,3,"DESC");
-        watchCourseTopTenTask(1,3,"ASC");
+        watchCourseTopTenTask(1,3,2,"DESC");
+        watchCourseTopTenTask(1,3,2,"ASC");
 
+        watchCourseTopTenTask(2,0,1,"DESC");
+        watchCourseTopTenTask(2,0,1,"ASC");
 
-        watchCourseTopTenTask(2,0,"DESC");
-        watchCourseTopTenTask(2,0,"ASC");
+        watchCourseTopTenTask(2,0,2,"DESC");
+        watchCourseTopTenTask(2,0,2,"ASC");
 
-        watchCourseTopTenTask(2,1,"DESC");
-        watchCourseTopTenTask(2,1,"ASC");
+        watchCourseTopTenTask(2,1,1,"DESC");
+        watchCourseTopTenTask(2,1,1,"ASC");
 
-        watchCourseTopTenTask(2,2,"DESC");
-        watchCourseTopTenTask(2,2,"ASC");
+        watchCourseTopTenTask(2,1,2,"DESC");
+        watchCourseTopTenTask(2,1,2,"ASC");
 
-        watchCourseTopTenTask(2,3,"DESC");
-        watchCourseTopTenTask(2,3,"ASC");
+        watchCourseTopTenTask(2,2,1,"DESC");
+        watchCourseTopTenTask(2,2,1,"ASC");
 
+        watchCourseTopTenTask(2,2,2,"DESC");
+        watchCourseTopTenTask(2,2,2,"ASC");
 
-        watchCourseTopTenTask(3,0,"DESC");
-        watchCourseTopTenTask(3,0,"ASC");
 
-        watchCourseTopTenTask(3,1,"DESC");
-        watchCourseTopTenTask(3,1,"ASC");
+        watchCourseTopTenTask(2,3,1,"DESC");
+        watchCourseTopTenTask(2,3,1,"ASC");
 
-        watchCourseTopTenTask(3,2,"DESC");
-        watchCourseTopTenTask(3,2,"ASC");
+        watchCourseTopTenTask(2,3,2,"DESC");
+        watchCourseTopTenTask(2,3,2,"ASC");
 
-        watchCourseTopTenTask(3,3,"DESC");
-        watchCourseTopTenTask(3,3,"ASC");
+        watchCourseTopTenTask(3,0,1,"DESC");
+        watchCourseTopTenTask(3,0,1,"ASC");
 
+        watchCourseTopTenTask(3,0,2,"DESC");
+        watchCourseTopTenTask(3,0,2,"ASC");
 
-        watchCourseTopTenTask(4,0,"DESC");
-        watchCourseTopTenTask(4,0,"ASC");
+        watchCourseTopTenTask(3,1,1,"DESC");
+        watchCourseTopTenTask(3,1,1,"ASC");
 
-        watchCourseTopTenTask(4,1,"DESC");
-        watchCourseTopTenTask(4,1,"ASC");
+        watchCourseTopTenTask(3,1,2,"DESC");
+        watchCourseTopTenTask(3,1,2,"ASC");
 
-        watchCourseTopTenTask(4,2,"DESC");
-        watchCourseTopTenTask(4,2,"ASC");
+        watchCourseTopTenTask(3,2,1,"DESC");
+        watchCourseTopTenTask(3,2,1,"ASC");
 
-        watchCourseTopTenTask(4,3,"DESC");
-        watchCourseTopTenTask(4,3,"ASC");
+        watchCourseTopTenTask(3,2,2,"DESC");
+        watchCourseTopTenTask(3,2,2,"ASC");
+
+        watchCourseTopTenTask(3,3,1,"DESC");
+        watchCourseTopTenTask(3,3,1,"ASC");
+
+        watchCourseTopTenTask(3,3,2,"DESC");
+        watchCourseTopTenTask(3,3,2,"ASC");
+
+        watchCourseTopTenTask(4,0,1,"DESC");
+        watchCourseTopTenTask(4,0,1,"ASC");
+
+        watchCourseTopTenTask(4,0,2,"DESC");
+        watchCourseTopTenTask(4,0,2,"ASC");
+
+        watchCourseTopTenTask(4,1,1,"DESC");
+        watchCourseTopTenTask(4,1,1,"ASC");
+        watchCourseTopTenTask(4,2,1,"DESC");
+        watchCourseTopTenTask(4,2,1,"ASC");
+        watchCourseTopTenTask(4,3,1,"DESC");
+        watchCourseTopTenTask(4,3,1,"ASC");
+
+        watchCourseTopTenTask(4,1,2,"DESC");
+        watchCourseTopTenTask(4,1,2,"ASC");
+        watchCourseTopTenTask(4,2,2,"DESC");
+        watchCourseTopTenTask(4,2,2,"ASC");
+        watchCourseTopTenTask(4,3,2,"DESC");
+        watchCourseTopTenTask(4,3,2,"ASC");
     }
 
 
     public void rewardMoneyTask15Minutes(){
-        rewardMoneyTopTenTask(0,0);
-        rewardMoneyTopTenTask(0,1);
+        rewardMoneyTopTenTask(0,0,1);
+        rewardMoneyTopTenTask(0,1,1);
 
-        rewardMoneyTradeTask(0);
+        rewardMoneyTradeTask(0,1);
+
+        rewardMoneyTopTenTask(0,0,2);
+        rewardMoneyTopTenTask(0,1,2);
+
+        rewardMoneyTradeTask(0,2);
     }
 
 
     public void rewardMoneyTaskEveryday(){
-        rewardMoneyTopTenTask(1,0);
-        rewardMoneyTopTenTask(1,1);
+        rewardMoneyTopTenTask(1,0,1);
+        rewardMoneyTopTenTask(1,1,1);
+
+
+        rewardMoneyTopTenTask(2,0,1);
+        rewardMoneyTopTenTask(2,1,1);
+
+        rewardMoneyTopTenTask(3,0,1);
+        rewardMoneyTopTenTask(3,1,1);
 
+        rewardMoneyTopTenTask(4,0,1);
+        rewardMoneyTopTenTask(4,1,1);
 
-        rewardMoneyTopTenTask(2,0);
-        rewardMoneyTopTenTask(2,1);
 
-        rewardMoneyTopTenTask(3,0);
-        rewardMoneyTopTenTask(3,1);
+        rewardMoneyTradeTask(1,1);
+        rewardMoneyTradeTask(2,1);
+        rewardMoneyTradeTask(3,1);
+        rewardMoneyTradeTask(4,1);
 
-        rewardMoneyTopTenTask(4,0);
-        rewardMoneyTopTenTask(4,1);
 
+        rewardMoneyTopTenTask(1,0,2);
+        rewardMoneyTopTenTask(1,1,2);
+        rewardMoneyTopTenTask(2,0,2);
+        rewardMoneyTopTenTask(2,1,2);
+        rewardMoneyTopTenTask(3,0,2);
+        rewardMoneyTopTenTask(3,1,2);
+        rewardMoneyTopTenTask(4,0,2);
+        rewardMoneyTopTenTask(4,1,2);
 
-        rewardMoneyTradeTask(1);
-        rewardMoneyTradeTask(2);
-        rewardMoneyTradeTask(3);
-        rewardMoneyTradeTask(4);
+        rewardMoneyTradeTask(1,2);
+        rewardMoneyTradeTask(2,2);
+        rewardMoneyTradeTask(3,2);
+        rewardMoneyTradeTask(4,2);
     }
 
     @Override
-    public void rewardMoneyTopTenTask(Integer type,Integer dataType) {
+    public void rewardMoneyTopTenTask(Integer type,Integer dataType,Integer userType) {
         AnalysisPreviewQueryDTO dto = new AnalysisPreviewQueryDTO();
         dto.setType(type);
         dto.setDataType(dataType);
@@ -415,15 +508,16 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
         dto.setStartTime(startDate);
         dto.setEndTime(endDate);
+        dto.setUserType(userType);
 
         List<RewardMoneyTopTenDTO> rewardMoneyTopTenDTOS = rewardMoneyTopTen(dto);
 
-        redisCache.setCacheObject( String.format("%s:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType), rewardMoneyTopTenDTOS);
+        redisCache.setCacheObject( String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType), rewardMoneyTopTenDTOS);
 
     }
 
     @Override
-    public void rewardMoneyTradeTask(Integer type) {
+    public void rewardMoneyTradeTask(Integer type,Integer userType) {
         AnalysisPreviewQueryDTO dto = new AnalysisPreviewQueryDTO();
         dto.setType(type);
 
@@ -458,8 +552,9 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
         dto.setStartTime(startDate);
         dto.setEndTime(endDate);
+        dto.setUserType(userType);
         List<RewardMoneyTrendDTO> rewardMoneyTrendDTOS = rewardMoneyTrendDTO(dto);
-        redisCache.setCacheObject( String.format("%s:%d", CHARTS_REWARD_MONEY_TREND, type), rewardMoneyTrendDTOS);
+        redisCache.setCacheObject( String.format("%s:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType), rewardMoneyTrendDTOS);
 
     }
 

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

@@ -90,6 +90,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
         order by t.score desc,t.id desc
     </select>
+    <select id="hyWorkTask">
+
+    </select>
+    <select id="hyWorkTaskGetInterruptedAndFirst" resultType="com.fs.course.domain.FsCourseWatchLog">
+        SELECT
+            fcl.*
+        FROM
+            fs_course_watch_log fcl
+                INNER JOIN
+            fs_user_course_video fucv ON fcl.video_id = fucv.video_id
+        WHERE
+            DATE(fcl.create_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+          AND (
+            fcl.log_type = 4 -- 看课中断
+           OR
+            (fcl.log_type = 3 AND fucv.is_first = 1) -- 待看的先导课 (根据 is_first 标志判断)
+            )
+    </select>
 
     <insert id="insertHyWorkTask" parameterType="QwWorkTask" useGeneratedKeys="true" keyProperty="id">
         insert into hy_work_task
@@ -154,7 +172,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         company_id,
         company_user_id,
         create_time,
-        update_time
+        update_time,
+        user_logs_id,
+        user_id
         )
         VALUES
         <foreach collection="qwWorkTasks" item="log" separator=",">
@@ -170,7 +190,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{log.companyId},
             #{log.companyUserId},
             #{log.createTime},
-            #{log.updateTime}
+            #{log.updateTime},
+            #{log.userLogsId},
+            #{log.userId}
             )
         </foreach>
     </insert>

+ 7 - 0
fs-service-system/src/main/resources/mapper/qw/QwWorkTaskMapper.xml

@@ -184,4 +184,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <select id="getUserList" resultType="com.fs.qw.vo.UserVOs">
+        select b.name,b.avatar,a.title,a.score,a.create_time,a.type from
+        qw_work_task a
+        inner join qw_external_contact b on a.ext_id = b.id
+        where a.company_user_id = #{userId}
+    </select>
 </mapper>

+ 112 - 27
fs-service-system/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

@@ -16,7 +16,8 @@
                 (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 COUNT(*) FROM FS_USER WHERE STATUS=0) AS black_num,
+                (select COUNT(*) FROM qw_user) AS qw_member_num
     </select>
     <select id="analysisPreview" resultType="com.fs.statis.dto.AnalysisPreviewDTO">
         -- 观看人数
@@ -37,31 +38,96 @@
                (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 count(log_id) from fs_course_watch_log
+        <where>
+            <if test="userType != null">
+                and send_type=${userType}
+            </if>
+            <if test="startTime != null and endTime != null">
+                and create_time between #{startTime} and #{endTime}
+            </if>
+        </where>
     </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 count(DISTINCT user_id) from fs_course_watch_log
+        <where>
+            finish_time is not null
+            <if test="userType != null">
+                and send_type=${userType}
+            </if>
+            <if test="startTime != null and endTime != null">
+                and create_time between #{startTime} and #{endTime}
+            </if>
+        </where>
     </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 count(distinct user_id) from fs_course_watch_log
+        <where>
+            <if test="userType != null">
+                and send_type=${userType}
+            </if>
+            <if test="startTime != null and endTime != null">
+                and create_time between #{startTime} and #{endTime}
+            </if>
+
+        </where>
     </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}
+         <where>
+             finish_time IS NOT NULL
+             <if test="startTime != null and endTime != null">
+                 AND create_time BETWEEN #{startTime} AND #{endTime}
+             </if>
+             <if test="userType != null">
+                 and send_type=${userType}
+             </if>
+         </where>
     </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}
+       <where>
+           <if test="startTime != null and endTime != null">
+               create_time BETWEEN #{startTime} AND #{endTime}
+           </if>
+       </where>
     </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}
+        <where>
+            is_right = 1
+            <if test="startTime != null and endTime != null">
+                and create_time BETWEEN #{startTime} AND #{endTime}
+            </if>
+        </where>
     </select>
     <select id="queryRewardCount" resultType="java.lang.Long">
-        select count(*) from company_red_package_logs where operate_type=1 AND create_time BETWEEN #{startTime} AND #{endTime}
+        select count(*) from company_red_package_logs rpl
+            left join fs_course_watch_log log
+            on rpl.watch_log_id=log.log_id
+        <where>
+            rpl.operate_type=1
+            <if test="startTime != null and endTime != null">
+                and rpl.create_time BETWEEN #{startTime} AND #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
+            </if>
+        </where>
     </select>
     <select id="queryRewardMoney" resultType="java.math.BigDecimal">
-        select sum(up_money) from company_red_package_logs where operate_type=1 AND create_time BETWEEN #{startTime} AND #{endTime}
+        select sum(up_money) from company_red_package_logs rpl
+        left join fs_course_watch_log log
+        on rpl.watch_log_id=log.log_id
+        <where>
+            rpl.operate_type=1
+            <if test="startTime != null and endTime != null">
+                and rpl.create_time BETWEEN #{startTime} AND #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
+            </if>
+        </where>
     </select>
     <select id="smsBalance" resultType="java.lang.Long">
         select sum(remain_sms_count) from company_sms
@@ -90,6 +156,9 @@
             <if test="endTime != null">
                 AND create_time <![CDATA[<]]> #{endTime}
             </if>
+            <if test="userType != null">
+                AND send_type = ${userType}
+            </if>
         </where>
         GROUP BY
         start_date
@@ -114,6 +183,9 @@
             <if test="endTime != null">
                 AND create_time <![CDATA[<]]> #{endTime}
             </if>
+            <if test="userType != null">
+                AND send_type = ${userType}
+            </if>
         </where>
         GROUP BY company_id
         limit 10
@@ -136,6 +208,9 @@
             <if test="endTime != null">
                 AND w.create_time <![CDATA[<]]> #{endTime}
             </if>
+            <if test="userType != null">
+                AND send_type = ${userType}
+            </if>
         </where>
         GROUP BY
         w.course_id
@@ -160,31 +235,36 @@
         SELECT
             -- 按公司
             <if test="dataType == 0">
-                company_id,
+                rpl.company_id as company_id,
             </if>
             -- 按课程
             <if test="dataType == 1">
-                course_id,
+                rpl.course_id as course_id,
             </if>
-            SUM(money) as rewardMoney
+            SUM(rpl.money) as rewardMoney
         FROM
-            company_red_package_logs
+            company_red_package_logs rpl
+        left join fs_course_watch_log log
+        on rpl.watch_log_id=log.log_id
         <where>
-            operate_type = 1
-            AND status = 1
+            rpl.operate_type = 1
+            AND rpl.status = 1
             <if test="startTime != null">
-                AND create_time <![CDATA[>=]]> #{startTime}
+                AND rpl.create_time <![CDATA[>=]]> #{startTime}
             </if>
             <if test="endTime != null">
-                AND create_time <![CDATA[<]]> #{endTime}
+                AND rpl.create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
             </if>
         </where>
         GROUP BY
             <if test="dataType == 0">
-                company_id
+                rpl.company_id
             </if>
             <if test="dataType == 1">
-                course_id
+                rpl.course_id
             </if>
         ORDER BY
             rewardMoney DESC
@@ -194,23 +274,28 @@
         select
         --             今日/昨日 小时
         <if test="type == 0 or type == 1">
-            DATE_FORMAT(create_time, '%H') AS start_date,
+            DATE_FORMAT(rpl.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,
+            DATE_FORMAT(rpl.create_time, '%Y-%m-%d') AS start_date,
         </if>
-               SUM(money) as rewardMoney
-        from company_red_package_logs
+               SUM(rpl.money) as rewardMoney
+        from company_red_package_logs rpl
+        left join fs_course_watch_log log
+        on rpl.watch_log_id=log.log_id
         <where>
             <if test="startTime != null">
-                create_time <![CDATA[>=]]> #{startTime}
+                rpl.create_time <![CDATA[>=]]> #{startTime}
             </if>
             <if test="endTime != null">
-                AND create_time <![CDATA[<]]> #{endTime}
+                AND rpl.create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
             </if>
-            AND operate_type = 1
-            AND status = 1
+            AND rpl.operate_type = 1
+            AND rpl.status = 1
         </where>
         group by start_date
     </select>