瀏覽代碼

Merge remote-tracking branch 'origin/master'

zyp 1 周之前
父節點
當前提交
f16ad0ee15
共有 84 個文件被更改,包括 2125 次插入463 次删除
  1. 16 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseController.java
  2. 4 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  3. 21 0
      fs-admin/src/main/java/com/fs/course/params/FsUserCourseConfigParam.java
  4. 24 0
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java
  5. 18 13
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyDeductController.java
  6. 1 1
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyRechargeController.java
  7. 15 0
      fs-admin/src/main/java/com/fs/his/task/CompanyBalanceTask.java
  8. 1 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  9. 6 6
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  10. 37 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  11. 6 1
      fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java
  12. 74 2
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  13. 4 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwTagController.java
  14. 104 0
      fs-company/src/main/java/com/fs/company/controller/tag/FsVideoCourseTagController.java
  15. 2 2
      fs-ipad-task/src/test/java/com/fs/app/task/SendMsgTest.java
  16. 29 25
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  17. 31 18
      fs-service/src/main/java/com/fs/company/domain/CompanyDeduct.java
  18. 3 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDeductMapper.java
  19. 19 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  20. 3 1
      fs-service/src/main/java/com/fs/company/service/ICompanyService.java
  21. 2 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  22. 75 11
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  23. 11 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  24. 3 0
      fs-service/src/main/java/com/fs/company/vo/CompanyDeductVO.java
  25. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourse.java
  26. 0 27
      fs-service/src/main/java/com/fs/course/domain/FsUserCourseVideo.java
  27. 9 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  28. 8 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  29. 3 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java
  30. 7 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  31. 6 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseService.java
  32. 15 2
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  33. 2 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java
  34. 8 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  35. 1 28
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  36. 5 0
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseListPVO.java
  37. 2 2
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoH5VO.java
  38. 0 39
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoVO.java
  39. 1 1
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  40. 5 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  41. 47 0
      fs-service/src/main/java/com/fs/hisStore/enums/CompanyEnum.java
  42. 1 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductCategoryScrmMapper.java
  43. 6 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java
  44. 5 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductQueryParam.java
  45. 13 9
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  46. 20 2
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  47. 5 0
      fs-service/src/main/java/com/fs/qw/mapper/QwCompanyMapper.java
  48. 5 2
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  49. 9 7
      fs-service/src/main/java/com/fs/qw/mapper/QwTagMapper.java
  50. 64 1
      fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java
  51. 5 0
      fs-service/src/main/java/com/fs/qw/param/QwExternalContactParam.java
  52. 5 0
      fs-service/src/main/java/com/fs/qw/param/QwExternalContactUpdateNoteParam.java
  53. 5 0
      fs-service/src/main/java/com/fs/qw/param/QwTagParam.java
  54. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwWatchLogService.java
  55. 3 3
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  56. 1 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwTagServiceImpl.java
  57. 64 8
      fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java
  58. 3 0
      fs-service/src/main/java/com/fs/qw/vo/QwTagVO.java
  59. 22 6
      fs-service/src/main/java/com/fs/statis/mapper/ConsumptionBalanceMapper.java
  60. 81 4
      fs-service/src/main/java/com/fs/statis/service/impl/StatisticsCompanyServiceImpl.java
  61. 79 4
      fs-service/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java
  62. 16 10
      fs-service/src/main/java/com/fs/tag/domain/FsTagUpdateQueue.java
  63. 96 0
      fs-service/src/main/java/com/fs/tag/domain/FsVideoCourseTag.java
  64. 5 104
      fs-service/src/main/java/com/fs/tag/mapper/FsTagUpdateQueueMapper.java
  65. 73 0
      fs-service/src/main/java/com/fs/tag/mapper/FsVideoCourseTagMapper.java
  66. 61 0
      fs-service/src/main/java/com/fs/tag/service/IFsVideoCourseTagService.java
  67. 76 41
      fs-service/src/main/java/com/fs/tag/service/impl/FsTagUpdateServiceImpl.java
  68. 145 0
      fs-service/src/main/java/com/fs/tag/service/impl/FsVideoCourseTagServiceImpl.java
  69. 1 1
      fs-service/src/main/resources/application-config-druid-hat.yml
  70. 2 2
      fs-service/src/main/resources/application-config-druid-knt.yml
  71. 102 0
      fs-service/src/main/resources/application-config-druid-knt2.yml
  72. 2 2
      fs-service/src/main/resources/application-druid-knt.yml
  73. 166 0
      fs-service/src/main/resources/application-druid-knt2.yml
  74. 11 8
      fs-service/src/main/resources/mapper/company/CompanyDeductMapper.xml
  75. 11 5
      fs-service/src/main/resources/mapper/course/FsCourseTrafficLogMapper.xml
  76. 4 4
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  77. 7 4
      fs-service/src/main/resources/mapper/course/FsUserCourseMapper.xml
  78. 0 6
      fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml
  79. 15 5
      fs-service/src/main/resources/mapper/course/FsUserCourseVideoRedPackageMapper.xml
  80. 2 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  81. 6 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductCategoryScrmMapper.xml
  82. 0 1
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
  83. 174 42
      fs-service/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml
  84. 119 0
      fs-service/src/main/resources/mapper/tag/FsVideoCourseTagMapper.xml

+ 16 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseController.java

@@ -8,6 +8,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
+import com.fs.course.params.FsUserCourseConfigParam;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.vo.FsUserCourseListPVO;
 import com.fs.course.vo.FsUserCourseListPVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
@@ -341,4 +342,19 @@ public class FsUserCourseController extends BaseController
         redisCacheUtil.delRedisKey("getCourseList");
         redisCacheUtil.delRedisKey("getCourseList");
         return toAjax(1);
         return toAjax(1);
     }
     }
+
+    /**
+     * 修改配置
+     **/
+    @PreAuthorize("@ss.hasPermi('course:userCourse:editConfig')")
+    @Log(title = "课程配置", businessType = BusinessType.UPDATE)
+    @PostMapping("/editConfig")
+    public R editConfig(@RequestBody FsUserCourseConfigParam params){
+        fsUserCourseService.editConfig(params.getId(), params.getConfigJson());
+        redisCacheUtil.delRedisKey("getCourseList");
+        redisCacheUtil.delRedisKey("h5user:course:video:list:all");
+        redisCacheUtil.delRedisKey("h5user:course:list:all");
+        redisCacheUtil.delRedisKey("cache:video");
+        return R.ok();
+    }
 }
 }

+ 4 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java

@@ -161,14 +161,17 @@ public class FsUserCoursePeriodController extends BaseController {
 
 
     @PreAuthorize("@ss.hasPermi('course:period:updateCourseTime')")
     @PreAuthorize("@ss.hasPermi('course:period:updateCourseTime')")
     @PostMapping("/updateCourseTime")
     @PostMapping("/updateCourseTime")
+    @Log(title = "会员营期修改课程时间", businessType = BusinessType.UPDATE)
     public R updateCourseTime(@RequestBody UpdateCourseTimeVo vo){
     public R updateCourseTime(@RequestBody UpdateCourseTimeVo vo){
         return fsUserCoursePeriodDaysService.updateCourseTime(vo);
         return fsUserCoursePeriodDaysService.updateCourseTime(vo);
     }
     }
     @PostMapping("/updateCourseDate")
     @PostMapping("/updateCourseDate")
+    @Log(title = "会员营期修改CourseDate", businessType = BusinessType.UPDATE)
     public R updateCourseDate(@RequestBody UpdateCourseTimeVo vo){
     public R updateCourseDate(@RequestBody UpdateCourseTimeVo vo){
         return fsUserCoursePeriodDaysService.updateCourseDate(vo);
         return fsUserCoursePeriodDaysService.updateCourseDate(vo);
     }
     }
     @PostMapping("/updateListCourseData")
     @PostMapping("/updateListCourseData")
+    @Log(title = "会员营期修改ListCourseData", businessType = BusinessType.UPDATE)
     public R updateListCourseData(@RequestBody List<FsUserCoursePeriodDays> entity){
     public R updateListCourseData(@RequestBody List<FsUserCoursePeriodDays> entity){
         return fsUserCoursePeriodDaysService.updateListCourseData(entity);
         return fsUserCoursePeriodDaysService.updateListCourseData(entity);
     }
     }
@@ -275,6 +278,7 @@ public class FsUserCoursePeriodController extends BaseController {
 
 
     @PreAuthorize("@ss.hasPermi('course:period:setCompanyRedPacket')")
     @PreAuthorize("@ss.hasPermi('course:period:setCompanyRedPacket')")
     @ApiOperation("按公司批量保存设置红包金额")
     @ApiOperation("按公司批量保存设置红包金额")
+    @Log(title = "按公司批量保存设置红包金额", businessType = BusinessType.UPDATE)
     @PostMapping("/batchRedPacket/byCompany")
     @PostMapping("/batchRedPacket/byCompany")
     public R batchRedPacketByCompany(@Validated @RequestBody BatchCompanyRedPackageParam param) {
     public R batchRedPacketByCompany(@Validated @RequestBody BatchCompanyRedPackageParam param) {
         try {
         try {

+ 21 - 0
fs-admin/src/main/java/com/fs/course/params/FsUserCourseConfigParam.java

@@ -0,0 +1,21 @@
+package com.fs.course.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@ApiModel("过程页配置参数实体")
+@Data
+public class FsUserCourseConfigParam {
+
+    @NotNull(message = "课程ID不能为空")
+    @ApiModelProperty("课程ID")
+    private Long id;
+
+    @NotBlank(message = "配置信息不能为空")
+    @ApiModelProperty("配置信息")
+    private String configJson;
+}

+ 24 - 0
fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java

@@ -252,6 +252,30 @@ public class FsCompanyController extends BaseController {
 
 
     }
     }
 
 
+    @PreAuthorize("@ss.hasPermi('his:company:redDeduct')")
+    @Log(title = "企业扣款", businessType = BusinessType.INSERT)
+    @PostMapping(value = "/redDeduct")
+    @Transactional
+    @RepeatSubmit
+    public R redDeduct(@RequestBody CompanyDeductParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        CompanyDeduct deduct=new CompanyDeduct();
+        String orderSn =  OrderCodeUtils.getOrderSn();
+        if(StringUtils.isEmpty(orderSn)){
+            return R.error("订单生成失败,请重试");
+        }
+        deduct.setDeductNo(orderSn);
+        deduct.setCompanyId(param.getCompanyId());
+        deduct.setMoney(param.getMoney());
+        deduct.setCreateUserId(loginUser.getUser().getUserId());
+        deduct.setIsAudit(0);
+        deduct.setRemark(param.getRemark());
+        deduct.setBusinessType(1); // 红包扣款
+        deductService.insertCompanyDeduct(deduct);
+        return R.ok("提交成功,等待审核");
+    }
+
     @PreAuthorize("@ss.hasPermi('his:company:deduct')")
     @PreAuthorize("@ss.hasPermi('his:company:deduct')")
     @Log(title = "企业扣款", businessType = BusinessType.INSERT)
     @Log(title = "企业扣款", businessType = BusinessType.INSERT)
     @PostMapping(value = "/deduct")
     @PostMapping(value = "/deduct")

+ 18 - 13
fs-admin/src/main/java/com/fs/his/controller/FsCompanyDeductController.java

@@ -139,19 +139,24 @@ public class FsCompanyDeductController extends BaseController
         deduct.setRemark(param.getRemark());
         deduct.setRemark(param.getRemark());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         if(deduct.getIsAudit()==1){
         if(deduct.getIsAudit()==1){
-            Company company=companyService.selectCompanyByIdForUpdate(deduct.getCompanyId());
-            company.setMoney(company.getMoney().subtract(deduct.getMoney()));
-            deduct.setBalance(company.getMoney());
-            companyService.updateCompany(company);
-            //生成流水
-            CompanyMoneyLogs log=new CompanyMoneyLogs();
-            log.setCompanyId(deduct.getCompanyId());
-            log.setMoney(deduct.getMoney().multiply(new BigDecimal(-1)));
-            log.setRemark(deduct.getRemark());
-            log.setLogsType(2);
-            log.setBalance(company.getMoney());
-            log.setCreateTime(new Date());
-            moneyLogsService.insertCompanyMoneyLogs(log);
+            if(1==param.getBusinessType()){// 红包充值
+                // 更新红包充值余额redis字段
+                companyService.redPacketTopUpCompany(deduct.getCompanyId(),deduct.getMoney(),"2");
+            }else {
+                Company company=companyService.selectCompanyByIdForUpdate(deduct.getCompanyId());
+                company.setMoney(company.getMoney().subtract(deduct.getMoney()));
+                deduct.setBalance(company.getMoney());
+                companyService.updateCompany(company);
+                //生成流水
+                CompanyMoneyLogs log=new CompanyMoneyLogs();
+                log.setCompanyId(deduct.getCompanyId());
+                log.setMoney(deduct.getMoney().multiply(new BigDecimal(-1)));
+                log.setRemark(deduct.getRemark());
+                log.setLogsType(2);
+                log.setBalance(company.getMoney());
+                log.setCreateTime(new Date());
+                moneyLogsService.insertCompanyMoneyLogs(log);
+            }
         }
         }
         deduct.setAuditTime(new Date());
         deduct.setAuditTime(new Date());
         deduct.setAuditUserId(loginUser.getUser().getUserId());
         deduct.setAuditUserId(loginUser.getUser().getUserId());

+ 1 - 1
fs-admin/src/main/java/com/fs/his/controller/FsCompanyRechargeController.java

@@ -139,7 +139,7 @@ public class FsCompanyRechargeController extends BaseController
         if(companyRecharge.getIsAudit()==1){
         if(companyRecharge.getIsAudit()==1){
             if(1==companyRecharge.getBusinessType()){// 红包充值
             if(1==companyRecharge.getBusinessType()){// 红包充值
                 // 更新红包充值余额redis字段
                 // 更新红包充值余额redis字段
-                companyService.redPacketTopUpCompany(companyRecharge.getCompanyId(),companyRecharge.getMoney());
+                companyService.redPacketTopUpCompany(companyRecharge.getCompanyId(),companyRecharge.getMoney(),"1");
             }else {
             }else {
                 Company company=companyService.selectCompanyByIdForUpdate(companyRecharge.getCompanyId());
                 Company company=companyService.selectCompanyByIdForUpdate(companyRecharge.getCompanyId());
                 company.setMoney(company.getMoney().add(companyRecharge.getMoney()));
                 company.setMoney(company.getMoney().add(companyRecharge.getMoney()));

+ 15 - 0
fs-admin/src/main/java/com/fs/his/task/CompanyBalanceTask.java

@@ -1,11 +1,14 @@
 package com.fs.his.task;
 package com.fs.his.task;
 
 
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyService;
+import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.service.BalanceRollbackErrorService;
 import com.fs.course.service.BalanceRollbackErrorService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
+import java.util.List;
+
 /**
 /**
  * @description: 公司余额同步定时任务 (红包余额)
  * @description: 公司余额同步定时任务 (红包余额)
  * @author: Xgb
  * @author: Xgb
@@ -58,6 +61,18 @@ public class CompanyBalanceTask {
 
 
     }
     }
 
 
+    /**
+     * @Description: 红包余额回滚(回滚的是客户没领取的红包),红包记录表中,两天没领取的记录不会再发送
+     * @Param: 每天0点执行一次
+     * @Return:
+     * @Author xgb
+     * @Date 2025/11/7 9:48
+     */
+    public void rollbackRedPacketMoney() throws Exception {
+        // 这个地方真加的是company money字段 xgb 红包余额独立后这个方法弃用
+        companyService.rollbackRedPacketMoney();
+    }
+
     /**
     /**
      * @Description: 每天晚上0点定时获取获取公司余额,记录到数据库中
      * @Description: 每天晚上0点定时获取获取公司余额,记录到数据库中
      * @Param:
      * @Param:

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

@@ -603,6 +603,7 @@ public class Task {
 
 
 
 
     public void redPacketAddMoney() throws Exception {
     public void redPacketAddMoney() throws Exception {
+        // 这个地方真加的是company money字段 xgb 红包余额独立后这个方法弃用
         List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseAddRedPacketLogByCompany();
         List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseAddRedPacketLogByCompany();
         for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
         for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
             companyService.addRedPacketCompanyMoney(redPacketMoneyVO.getMoney(), redPacketMoneyVO.getCompanyId());
             companyService.addRedPacketCompanyMoney(redPacketMoneyVO.getMoney(), redPacketMoneyVO.getCompanyId());

+ 6 - 6
fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java

@@ -293,11 +293,11 @@ public class QwUserController extends BaseController {
     @RepeatSubmit
     @RepeatSubmit
     @PreAuthorize("@ss.hasPermi('qw:user:sync')")
     @PreAuthorize("@ss.hasPermi('qw:user:sync')")
     @Log(title = "企微用户", businessType = BusinessType.INSERT)
     @Log(title = "企微用户", businessType = BusinessType.INSERT)
-    @PostMapping("sync/{corpId}/{companyId}")
-    public R sync(@PathVariable("corpId") String corpId,@PathVariable("companyId") Long companyId)
+    @PostMapping("sync/{corpId}")
+    public R sync(@PathVariable("corpId") String corpId)
     {
     {
 
 
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(companyId);
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByAll();
         for (String string : strings) {
         for (String string : strings) {
 
 
             if (string.equals(corpId)){
             if (string.equals(corpId)){
@@ -330,10 +330,10 @@ public class QwUserController extends BaseController {
     @RepeatSubmit
     @RepeatSubmit
     @PreAuthorize("@ss.hasPermi('qw:user:sync')")
     @PreAuthorize("@ss.hasPermi('qw:user:sync')")
     @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
     @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
-    @PostMapping("syncName/{corpId}/{companyId}")
-    public R syncName(@PathVariable("corpId") String corpId,@PathVariable("companyId") Long companyId)
+    @PostMapping("syncName/{corpId}")
+    public R syncName(@PathVariable("corpId") String corpId)
     {
     {
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(companyId);
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByAll();
         for (String string : strings) {
         for (String string : strings) {
             if (string.equals(corpId)){
             if (string.equals(corpId)){
                 qwUserService.syncQwUserName(string);
                 qwUserService.syncQwUserName(string);

+ 37 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -21,6 +21,7 @@ import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserCodeParam;
 import com.fs.company.param.CompanyUserCodeParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.service.*;
 import com.fs.company.service.*;
+import com.fs.company.service.impl.CompanyDeptServiceImpl;
 import com.fs.company.utils.DomainUtil;
 import com.fs.company.utils.DomainUtil;
 import com.fs.company.utils.QwStatusEnum;
 import com.fs.company.utils.QwStatusEnum;
 import com.fs.company.vo.BatchUserRolesVO;
 import com.fs.company.vo.BatchUserRolesVO;
@@ -100,6 +101,10 @@ public class CompanyUserController extends BaseController {
     @Autowired
     @Autowired
     IQwCompanyService iQwCompanyService;
     IQwCompanyService iQwCompanyService;
 
 
+    @Autowired
+    private CompanyDeptServiceImpl companyDeptService;
+
+
     @Autowired
     @Autowired
     private IQwUserService qwUserService;
     private IQwUserService qwUserService;
 
 
@@ -475,6 +480,38 @@ public class CompanyUserController extends BaseController {
         return  R.ok().put("data",list);
         return  R.ok().put("data",list);
     }
     }
 
 
+    @GetMapping("/getQwMyUserList/{id}")
+    public R getQwMyUserList(@PathVariable("id") String corpId)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        List<QwUserVO> list = companyUserService.selectCompanyQwUserListByMy(corpId,loginUser.getCompany().getCompanyId(),loginUser.getUser().getUserId());
+        return  R.ok().put("data",list);
+    }
+
+    @GetMapping("/getQwDeptUserList/{id}")
+    public R getQwDeptUserList(@PathVariable("id") String corpId)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+
+        List<Long> combinedList = new ArrayList<>();
+        //本部门
+        Long deptId = loginUser.getUser().getDeptId();
+        if (deptId!=null){
+            combinedList.add(deptId);
+        }
+        //本部门的下级部门
+        List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
+        if (!deptList.isEmpty()){
+            combinedList.addAll(deptList);
+        }
+        String userType = loginUser.getUser().getUserType();
+
+        List<QwUserVO> list = companyUserService.selectCompanyQwUserListByDept(corpId,loginUser.getCompany().getCompanyId(),combinedList,userType);
+        return  R.ok().put("data",list);
+    }
+
+
+
     /**
     /**
      * 根据部门的id获取到企业微信的qwuserid
      * 根据部门的id获取到企业微信的qwuserid
      */
      */

+ 6 - 1
fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java

@@ -225,8 +225,13 @@ public class FsQwCourseWatchLogController extends BaseController
     {
     {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<QwWatchLogStatisticsListVO> list = new ArrayList<>();
 
 
-        List<QwWatchLogStatisticsListVO> list = qwWatchLogService.selectQwWatchLogStatisticsListVOExport(param);
+        if ("济南联志健康".equals(signProjectName)) {
+            list = qwWatchLogService.selectQwWatchLogStatisticsListVOExportExcludeTransfer(param);
+        }else{
+            list = qwWatchLogService.selectQwWatchLogStatisticsListVOExport(param);
+        }
 
 
         ExcelUtil<QwWatchLogStatisticsListVO> util = new ExcelUtil<QwWatchLogStatisticsListVO>(QwWatchLogStatisticsListVO.class);
         ExcelUtil<QwWatchLogStatisticsListVO> util = new ExcelUtil<QwWatchLogStatisticsListVO>(QwWatchLogStatisticsListVO.class);
         return util.exportExcel(list, "短链课程看课记录数据");
         return util.exportExcel(list, "短链课程看课记录数据");

+ 74 - 2
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -13,9 +13,10 @@ import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.impl.CompanyDeptServiceImpl;
 import com.fs.company.service.impl.CompanyDeptServiceImpl;
+import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.param.FsUserCourseListUParam;
 import com.fs.course.param.FsUserCourseListUParam;
-import com.fs.course.service.IFsUserCompanyBindService;
-import com.fs.course.service.IFsUserCourseStudyService;
+import com.fs.course.service.*;
+import com.fs.course.vo.FsCourseWatchLogListVO;
 import com.fs.course.vo.FsUserCourseStudyListUVO;
 import com.fs.course.vo.FsUserCourseStudyListUVO;
 import com.fs.course.vo.UserWatchLogListVo;
 import com.fs.course.vo.UserWatchLogListVo;
 import com.fs.crm.param.CrmMyCustomerListQueryParam;
 import com.fs.crm.param.CrmMyCustomerListQueryParam;
@@ -718,7 +719,78 @@ public class QwExternalContactController extends BaseController
         return R.ok("正在批量修改备注中");
         return R.ok("正在批量修改备注中");
     }
     }
 
 
+    /**
+     * copy批量修改备注方法,该方法提供给我的看课记录里面使用
+     * @param param
+     * @return
+     * @throws JSONException
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:edit')")
+    @Log(title = "批量修改备注", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchUpdateExternalContactNotesByWatchLog")
+    public R batchUpdateExternalContactNotesByWatchLog(@RequestBody QwExternalContactUpdateNoteParam param) throws JSONException {
+        if(param.isFilter()){
+            param.setWatchLogIds(getWatchLogIds(param.getFromMyList(), param.getWatchLogParam()));
+        }
+        //查询
+        if(null != param.getWatchLogIds() && !param.getWatchLogIds().isEmpty()){
+            param.setUserIds(getUserIdsByWatchLogIds(param.getWatchLogIds()));
+        }
+
+        if(param.getUserIds() == null || param.getUserIds().isEmpty()){
+            return R.error("修改用户为空");
+        }
+        qwUserServiceAsyncHelper.batchUpdateExternalContactNotes(param);
+        return R.ok("正在批量修改备注中");
+    }
+
+    @Autowired
+    private IFsUserCoursePeriodService userCoursePeriodService;
 
 
+    @Autowired
+    private IFsUserCoursePeriodDaysService userCoursePeriodDaysService;
+
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+    /**
+     * 用于适配看课批量修改标签【根据过滤条件版】
+     * @param fromMyList
+     * @param watchLogParam
+     * @return
+     */
+    private List<Long> getWatchLogIds(Integer fromMyList,FsCourseWatchLogListParam watchLogParam){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if(Integer.valueOf(0).equals(fromMyList)){
+            watchLogParam.setCompanyId( loginUser.getCompany().getCompanyId());
+        }else{
+            watchLogParam.setCompanyUserId( loginUser.getUser().getUserId());
+        }
+
+        if (watchLogParam.getSendType()==1&& watchLogParam.getPeriodETime()!=null && watchLogParam.getPeriodSTime()!=null) {
+            List<Long> periodIds = userCoursePeriodDaysService.selectFsUserCoursePeriodDaysByTime(watchLogParam.getPeriodSTime(), watchLogParam.getPeriodETime());
+
+            if (!periodIds.isEmpty()){
+                List<Long> longs = userCoursePeriodService.selectFsUserCoursePeriodListByPeriodId(periodIds, loginUser.getCompany().getCompanyId());
+                if (!longs.isEmpty()){
+                    watchLogParam.setPeriodIds(longs);
+                }else {
+                    return new ArrayList<>();
+                }
+            }else {
+                return new ArrayList<>();
+            }
+
+        }
+
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(watchLogParam);
+        List<Long> collect = list.stream().map(FsCourseWatchLogListVO::getLogId).collect(Collectors.toList());
+        return collect;
+    }
+    private List<Long> getUserIdsByWatchLogIds(List<Long> watchLogIds){
+        List<Long> exContactIdsIdsByWatchLogIds = fsCourseWatchLogService.getExContactIdsIdsByWatchLogIds(watchLogIds);
+        Set<Long> set = new HashSet<>(exContactIdsIdsByWatchLogIds);
+        return new ArrayList<>(set);
+    }
 
 
     private List<Long> getList(Integer addType, QwExternalContactParam param){
     private List<Long> getList(Integer addType, QwExternalContactParam param){
         if(addType == null){
         if(addType == null){

+ 4 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwTagController.java

@@ -5,7 +5,9 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import com.fs.framework.service.TokenService;
 import com.fs.qw.domain.QwTag;
 import com.fs.qw.domain.QwTag;
 import com.fs.qw.param.QwTagParam;
 import com.fs.qw.param.QwTagParam;
@@ -45,6 +47,8 @@ public class QwTagController extends BaseController
     @PostMapping("/searchTags")
     @PostMapping("/searchTags")
     public TableDataInfo  searchTags(@RequestBody QwTagParam tagParam)
     public TableDataInfo  searchTags(@RequestBody QwTagParam tagParam)
     {
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        tagParam.setCompanyId(loginUser.getCompany().getCompanyId());
         return getDataTable(qwTagService.searchTags(tagParam));
         return getDataTable(qwTagService.searchTags(tagParam));
     }
     }
     /**
     /**

+ 104 - 0
fs-company/src/main/java/com/fs/company/controller/tag/FsVideoCourseTagController.java

@@ -0,0 +1,104 @@
+package com.fs.company.controller.tag;
+
+import java.util.List;
+
+import com.fs.tag.domain.FsVideoCourseTag;
+import com.fs.tag.service.IFsVideoCourseTagService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 视频小节看课标签关联Controller
+ *
+ * @author fs
+ * @date 2025-11-05
+ */
+@RestController
+@RequestMapping("/shop/tag")
+public class FsVideoCourseTagController extends BaseController
+{
+    @Autowired
+    private IFsVideoCourseTagService fsVideoCourseTagService;
+
+    /**
+     * 查询视频小节看课标签关联列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:tag:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsVideoCourseTag fsVideoCourseTag)
+    {
+        startPage();
+        List<FsVideoCourseTag> list = fsVideoCourseTagService.selectFsVideoCourseTagList(fsVideoCourseTag);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出视频小节看课标签关联列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:tag:export')")
+    @Log(title = "视频小节看课标签关联", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsVideoCourseTag fsVideoCourseTag)
+    {
+        List<FsVideoCourseTag> list = fsVideoCourseTagService.selectFsVideoCourseTagList(fsVideoCourseTag);
+        ExcelUtil<FsVideoCourseTag> util = new ExcelUtil<FsVideoCourseTag>(FsVideoCourseTag.class);
+        return util.exportExcel(list, "视频小节看课标签关联数据");
+    }
+
+    /**
+     * 获取视频小节看课标签关联详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('shop:tag:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsVideoCourseTagService.selectFsVideoCourseTagById(id));
+    }
+
+    /**
+     * 新增视频小节看课标签关联
+     */
+    @PreAuthorize("@ss.hasPermi('shop:tag:add')")
+    @Log(title = "视频小节看课标签关联", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsVideoCourseTag fsVideoCourseTag)
+    {
+        return toAjax(fsVideoCourseTagService.insertFsVideoCourseTag(fsVideoCourseTag));
+    }
+
+    /**
+     * 修改视频小节看课标签关联
+     */
+    @PreAuthorize("@ss.hasPermi('shop:tag:edit')")
+    @Log(title = "视频小节看课标签关联", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsVideoCourseTag fsVideoCourseTag)
+    {
+        return toAjax(fsVideoCourseTagService.updateFsVideoCourseTag(fsVideoCourseTag));
+    }
+
+    /**
+     * 删除视频小节看课标签关联
+     */
+    @PreAuthorize("@ss.hasPermi('shop:tag:remove')")
+    @Log(title = "视频小节看课标签关联", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsVideoCourseTagService.deleteFsVideoCourseTagByIds(ids));
+    }
+}

+ 2 - 2
fs-ipad-task/src/test/java/com/fs/app/task/SendMsgTest.java

@@ -37,11 +37,11 @@ public class SendMsgTest {
 
 
     @Test
     @Test
     public void testLogWrite(){
     public void testLogWrite(){
-        List<Long> testLogIds = Arrays.asList(177L,180L,182L,183L,184L,185L);
+        List<Long> testLogIds = Arrays.asList(194L,195L);
 
 
         for(Long logId : testLogIds){
         for(Long logId : testLogIds){
             FsCourseWatchLog fsCourseWatchLog = fsCourseWatchLogMapper.selectFsCourseWatchLogByLogId(logId);
             FsCourseWatchLog fsCourseWatchLog = fsCourseWatchLogMapper.selectFsCourseWatchLogByLogId(logId);
-            fsTagUpdateService.onCourseWatchingBatch(Collections.singletonList(fsCourseWatchLog));
+            fsTagUpdateService.onCourseWatchFinishedBatch(Collections.singletonList(fsCourseWatchLog));
         }
         }
     }
     }
 
 

+ 29 - 25
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -308,7 +308,7 @@ public class QwMsgController {
                 if (wxWorkMessageDTO.getReferid()!=0){
                 if (wxWorkMessageDTO.getReferid()!=0){
                     break;
                     break;
                 }
                 }
-                if (wxWorkMessageDTO.getMsgtype()==2||wxWorkMessageDTO.getMsgtype()==0||wxWorkMessageDTO.getMsgtype()==16||wxWorkMessageDTO.getMsgtype() == 101||wxWorkMessageDTO.getMsgtype() == 104||wxWorkMessageDTO.getMsgtype()==141){
+                if (wxWorkMessageDTO.getMsgtype()==2||wxWorkMessageDTO.getMsgtype()==0||wxWorkMessageDTO.getMsgtype()==16||wxWorkMessageDTO.getMsgtype() == 101||wxWorkMessageDTO.getMsgtype() == 104){
 
 
                     String content = wxWorkMessageDTO.getContent();
                     String content = wxWorkMessageDTO.getContent();
                     log.info("id:{}, 接收人:"+wxWorkMessageDTO.getReceiver(), id);
                     log.info("id:{}, 接收人:"+wxWorkMessageDTO.getReceiver(), id);
@@ -347,30 +347,6 @@ public class QwMsgController {
                         content = wxWorkMessageDTO.getUrl();
                         content = wxWorkMessageDTO.getUrl();
                         log.info("id:{}, 用户发送表情"+content, id);
                         log.info("id:{}, 用户发送表情"+content, id);
                     }//视频号
                     }//视频号
-                    else if (wxWorkMessageDTO.getMsgtype()==141){
-                        QwUser qwUserByAppKey = qwUserMapper.selectQwUserById(id);
-                        if(qwUserByAppKey.getVideoGetStatus() != null && qwUserByAppKey.getVideoGetStatus() == 1){
-                            QwUserVideo qwUserVideo = qwUserVideoService.selectByObjectId(wxWorkMessageDTO.getObjectId(), qwUserByAppKey.getId());
-                            if(qwUserVideo == null){
-                                QwUserVideo userVideo=new QwUserVideo();
-                                userVideo.setSenderName(wxWorkMessageDTO.getSender_name());
-                                userVideo.setAppKey(qwUserByAppKey.getAppKey());
-                                userVideo.setNickName(wxWorkMessageDTO.getNickname());
-                                userVideo.setObjectId(wxWorkMessageDTO.getObjectId());
-                                userVideo.setCoverUrl(wxWorkMessageDTO.getCover_url());
-                                userVideo.setThumbUrl(wxWorkMessageDTO.getThumb_url());
-                                userVideo.setAvatar(wxWorkMessageDTO.getAvatar());
-                                userVideo.setDesc(wxWorkMessageDTO.getDesc());
-                                userVideo.setUrl(wxWorkMessageDTO.getUrl());
-                                userVideo.setExtras(wxWorkMessageDTO.getExtras());
-                                userVideo.setObjectNonceId(wxWorkMessageDTO.getObjectNonceId());
-                                userVideo.setQwUserId(qwUserByAppKey.getId());
-                                userVideo.setCompanyUserId(qwUserByAppKey.getCompanyUserId());
-                                userVideo.setCompanyId(qwUserByAppKey.getCompanyId());
-                                qwUserVideoService.insertQwUserVideo(userVideo);
-                            }
-                        }
-                    }
 
 
                     if (2000000000000000L-receiver>0){
                     if (2000000000000000L-receiver>0){
                         log.info("id:{}, 客户发送", id);
                         log.info("id:{}, 客户发送", id);
@@ -381,6 +357,34 @@ public class QwMsgController {
                     }
                     }
 
 
                 }
                 }
+                //视频号
+                if (wxWorkMessageDTO.getMsgtype()==141){
+                    QwUser qwUserByAppKey = qwUserMapper.selectQwUserById(id);
+                    log.info("进入到视频号:{}",qwUserByAppKey);
+
+                    if(qwUserByAppKey.getVideoGetStatus() != null && qwUserByAppKey.getVideoGetStatus() == 1){
+                        QwUserVideo qwUserVideo = qwUserVideoService.selectByObjectId(wxWorkMessageDTO.getObjectId(), qwUserByAppKey.getId());
+                        if(qwUserVideo == null){
+                            QwUserVideo userVideo=new QwUserVideo();
+                            userVideo.setSenderName(wxWorkMessageDTO.getSender_name());
+                            userVideo.setAppKey(qwUserByAppKey.getAppKey());
+                            userVideo.setNickName(wxWorkMessageDTO.getNickname());
+                            userVideo.setObjectId(wxWorkMessageDTO.getObjectId());
+                            userVideo.setCoverUrl(wxWorkMessageDTO.getCover_url());
+                            userVideo.setThumbUrl(wxWorkMessageDTO.getThumb_url());
+                            userVideo.setAvatar(wxWorkMessageDTO.getAvatar());
+                            userVideo.setDesc(wxWorkMessageDTO.getDesc());
+                            userVideo.setUrl(wxWorkMessageDTO.getUrl());
+                            userVideo.setExtras(wxWorkMessageDTO.getExtras());
+                            userVideo.setObjectNonceId(wxWorkMessageDTO.getObjectNonceId());
+                            userVideo.setQwUserId(qwUserByAppKey.getId());
+                            userVideo.setCompanyUserId(qwUserByAppKey.getCompanyUserId());
+                            userVideo.setCompanyId(qwUserByAppKey.getCompanyId());
+                            qwUserVideoService.insertQwUserVideo(userVideo);
+                            log.info("存储完成:userVideo={}",userVideo);
+                        }
+                    }
+                }
                 //语音通话
                 //语音通话
                 if (wxWorkMessageDTO.getMsgtype()==40){
                 if (wxWorkMessageDTO.getMsgtype()==40){
                     if (wxWorkMessageDTO.getRecordtype()==null){
                     if (wxWorkMessageDTO.getRecordtype()==null){

+ 31 - 18
fs-service/src/main/java/com/fs/company/domain/CompanyDeduct.java

@@ -10,7 +10,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 
 
 /**
 /**
  * 扣款对象 company_deduct
  * 扣款对象 company_deduct
- * 
+ *
  * @author fs
  * @author fs
  * @date 2023-02-27
  * @date 2023-02-27
  */
  */
@@ -57,6 +57,17 @@ public class CompanyDeduct extends BaseEntity
     @Excel(name = "备注")
     @Excel(name = "备注")
     private String remark;
     private String remark;
 
 
+    @Excel(name = "业务类型")
+    private Integer businessType;
+
+    public Integer getBusinessType() {
+        return businessType;
+    }
+
+    public void setBusinessType(Integer businessType) {
+        this.businessType = businessType;
+    }
+
     @Override
     @Override
     public String getRemark() {
     public String getRemark() {
         return remark;
         return remark;
@@ -72,79 +83,79 @@ public class CompanyDeduct extends BaseEntity
         this.deductId = deductId;
         this.deductId = deductId;
     }
     }
 
 
-    public Long getDeductId() 
+    public Long getDeductId()
     {
     {
         return deductId;
         return deductId;
     }
     }
-    public void setDeductNo(String deductNo) 
+    public void setDeductNo(String deductNo)
     {
     {
         this.deductNo = deductNo;
         this.deductNo = deductNo;
     }
     }
 
 
-    public String getDeductNo() 
+    public String getDeductNo()
     {
     {
         return deductNo;
         return deductNo;
     }
     }
-    public void setCompanyId(Long companyId) 
+    public void setCompanyId(Long companyId)
     {
     {
         this.companyId = companyId;
         this.companyId = companyId;
     }
     }
 
 
-    public Long getCompanyId() 
+    public Long getCompanyId()
     {
     {
         return companyId;
         return companyId;
     }
     }
-    public void setMoney(BigDecimal money) 
+    public void setMoney(BigDecimal money)
     {
     {
         this.money = money;
         this.money = money;
     }
     }
 
 
-    public BigDecimal getMoney() 
+    public BigDecimal getMoney()
     {
     {
         return money;
         return money;
     }
     }
-    public void setBalance(BigDecimal balance) 
+    public void setBalance(BigDecimal balance)
     {
     {
         this.balance = balance;
         this.balance = balance;
     }
     }
 
 
-    public BigDecimal getBalance() 
+    public BigDecimal getBalance()
     {
     {
         return balance;
         return balance;
     }
     }
-    public void setCreateUserId(Long createUserId) 
+    public void setCreateUserId(Long createUserId)
     {
     {
         this.createUserId = createUserId;
         this.createUserId = createUserId;
     }
     }
 
 
-    public Long getCreateUserId() 
+    public Long getCreateUserId()
     {
     {
         return createUserId;
         return createUserId;
     }
     }
-    public void setIsAudit(Integer isAudit) 
+    public void setIsAudit(Integer isAudit)
     {
     {
         this.isAudit = isAudit;
         this.isAudit = isAudit;
     }
     }
 
 
-    public Integer getIsAudit() 
+    public Integer getIsAudit()
     {
     {
         return isAudit;
         return isAudit;
     }
     }
-    public void setAuditUserId(Long auditUserId) 
+    public void setAuditUserId(Long auditUserId)
     {
     {
         this.auditUserId = auditUserId;
         this.auditUserId = auditUserId;
     }
     }
 
 
-    public Long getAuditUserId() 
+    public Long getAuditUserId()
     {
     {
         return auditUserId;
         return auditUserId;
     }
     }
-    public void setAuditTime(Date auditTime) 
+    public void setAuditTime(Date auditTime)
     {
     {
         this.auditTime = auditTime;
         this.auditTime = auditTime;
     }
     }
 
 
-    public Date getAuditTime() 
+    public Date getAuditTime()
     {
     {
         return auditTime;
         return auditTime;
     }
     }
@@ -162,6 +173,8 @@ public class CompanyDeduct extends BaseEntity
             .append("isAudit", getIsAudit())
             .append("isAudit", getIsAudit())
             .append("auditUserId", getAuditUserId())
             .append("auditUserId", getAuditUserId())
             .append("auditTime", getAuditTime())
             .append("auditTime", getAuditTime())
+                .append("remark", getRemark())
+                .append("businessType", getBusinessType())
             .toString();
             .toString();
     }
     }
 }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDeductMapper.java

@@ -108,6 +108,9 @@ public interface CompanyDeductMapper
             "<if test = 'maps.createTime != null  '> " +
             "<if test = 'maps.createTime != null  '> " +
             "and   DATE(d.create_time) = DATE(#{maps.createTime})" +
             "and   DATE(d.create_time) = DATE(#{maps.createTime})" +
             "</if>" +
             "</if>" +
+            "<if test = 'maps.businessType != null  '> " +
+            "and   d.business_type = #{maps.businessType}" +
+            "</if>" +
             "<if test = 'maps.companyName != null  '> " +
             "<if test = 'maps.companyName != null  '> " +
             "and c.company_name like concat('%', #{maps.companyName }, '%')" +
             "and c.company_name like concat('%', #{maps.companyName }, '%')" +
             "</if>" +
             "</if>" +

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

@@ -151,6 +151,25 @@ public interface CompanyUserMapper
     @Select("select * from  qw_user  where corp_id=#{corpId} and company_id=#{companyId}")
     @Select("select * from  qw_user  where corp_id=#{corpId} and company_id=#{companyId}")
     List<QwUserVO> selectCompanyQwUserList(@Param("corpId") String corpId,@Param("companyId")Long companyId);
     List<QwUserVO> selectCompanyQwUserList(@Param("corpId") String corpId,@Param("companyId")Long companyId);
 
 
+    @Select("select * from  qw_user  where corp_id=#{corpId} and company_id=#{companyId} and company_user_id=#{userId}")
+    List<QwUserVO> selectCompanyQwUserListByMy(@Param("corpId") String corpId,@Param("companyId")Long companyId,@Param("userId")Long userId);
+
+    @Select("<script>" +
+            "select qu.* from qw_user qu " +
+            "left join company_user cu on cu.user_id = qu.company_user_id " +
+            "where 1=1 " +
+            "and qu.corp_id = #{corpId} " +
+            "and qu.company_id = #{companyId} " +
+            "<if test=\"combinedList != null and !combinedList.isEmpty() and  userType != '00' \">" +
+            "and cu.dept_id IN " +
+            "<foreach collection='combinedList' item='deptId' open='(' separator=',' close=')'>" +
+            "#{deptId}" +
+            "</foreach>" +
+            "</if>" +
+            "</script>")
+    List<QwUserVO> selectCompanyQwUserListByDept(@Param("corpId") String corpId,@Param("companyId")Long companyId,
+                                                 @Param("combinedList") List<Long> combinedList,@Param("userType")String userType);
+
 
 
     List<CompanyUserQwListVO> selectCompanyUserQwListVO(CompanyUserQwParam user);
     List<CompanyUserQwListVO> selectCompanyUserQwListVO(CompanyUserQwParam user);
 
 

+ 3 - 1
fs-service/src/main/java/com/fs/company/service/ICompanyService.java

@@ -170,7 +170,7 @@ public interface ICompanyService
 
 
     void syncCompanyBalance();
     void syncCompanyBalance();
 
 
-    void redPacketTopUpCompany(Long companyId, BigDecimal money);
+    void redPacketTopUpCompany(Long companyId, BigDecimal money,String type);
 
 
     void asyncRecordBalanceLog(Long companyId, BigDecimal money,Integer logType, BigDecimal balance, String remark);
     void asyncRecordBalanceLog(Long companyId, BigDecimal money,Integer logType, BigDecimal balance, String remark);
 
 
@@ -181,4 +181,6 @@ public interface ICompanyService
      * @param list 公司列表
      * @param list 公司列表
      */
      */
     void batchUpdateCompany(List<Company> list);
     void batchUpdateCompany(List<Company> list);
+
+    void rollbackRedPacketMoney();
 }
 }

+ 2 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -129,6 +129,8 @@ public interface ICompanyUserService {
     List<CitysAreaVO> getCitysAreaList();
     List<CitysAreaVO> getCitysAreaList();
 
 
     List<QwUserVO> selectCompanyQwUserList(String corpId,Long companyId);
     List<QwUserVO> selectCompanyQwUserList(String corpId,Long companyId);
+    List<QwUserVO> selectCompanyQwUserListByMy(String corpId,Long companyId,Long userId);
+    List<QwUserVO> selectCompanyQwUserListByDept(String corpId,Long companyId,List<Long> combinedList,String userType);
 
 
     List<CompanyUserQwListVO> selectCompanyUserQwListVO(CompanyUserQwParam user);
     List<CompanyUserQwListVO> selectCompanyUserQwListVO(CompanyUserQwParam user);
 
 

+ 75 - 11
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -21,10 +21,8 @@ import com.fs.company.param.CompanyParam;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.company.service.ICompanyProfitService;
 import com.fs.company.service.ICompanyProfitService;
 import com.fs.company.service.ICompanyRoleService;
 import com.fs.company.service.ICompanyRoleService;
-import com.fs.company.vo.CompanyCrmVO;
-import com.fs.company.vo.CompanyNameVO;
-import com.fs.company.vo.CompanyVO;
-import com.fs.company.vo.DeptDataVO;
+import com.fs.company.vo.*;
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.FsInquiryOrder;
 import com.fs.his.domain.FsInquiryOrder;
 import com.fs.his.domain.FsStoreOrder;
 import com.fs.his.domain.FsStoreOrder;
@@ -110,6 +108,9 @@ public class CompanyServiceImpl implements ICompanyService
     @Autowired
     @Autowired
     private RedissonClient redissonClient;
     private RedissonClient redissonClient;
 
 
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
+
     @Autowired
     @Autowired
     private TransactionTemplate transactionTemplate;
     private TransactionTemplate transactionTemplate;
 
 
@@ -1392,13 +1393,13 @@ public class CompanyServiceImpl implements ICompanyService
 
 
     /**
     /**
      * @Description: 红包充值
      * @Description: 红包充值
-     * @Param:
+     * @Param: type 充值类型 1 充值 2 扣款
      * @Return:
      * @Return:
      * @Author xgb
      * @Author xgb
      * @Date 2025/11/3 14:01
      * @Date 2025/11/3 14:01
      */
      */
     @Override
     @Override
-    public void redPacketTopUpCompany(Long companyId, BigDecimal money) {
+    public void redPacketTopUpCompany(Long companyId, BigDecimal money,String type) {
 
 
         String companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + companyId;
         String companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + companyId;
 
 
@@ -1415,23 +1416,31 @@ public class CompanyServiceImpl implements ICompanyService
                     originalMoney = new BigDecimal(moneyStr);
                     originalMoney = new BigDecimal(moneyStr);
                 }else {
                 }else {
                     // 缓存没有值,重启系统恢复redis数据 保证数据正确性
                     // 缓存没有值,重启系统恢复redis数据 保证数据正确性
-                    logger.error("红包余额充值获取redis余额缓存异常,异常请求参数companyId:{},money:{}",companyId,money);
+                    logger.error("红包余额充值获取redis余额缓存异常,异常请求参数companyId:{},money:{},type:{}",companyId,money, type);
                     throw new RuntimeException("红包余额充值获取redis余额缓存异常");
                     throw new RuntimeException("红包余额充值获取redis余额缓存异常");
                 }
                 }
 
 
+                if(type.equals("1")){// 充值
+
+                }else if(type.equals("2")) {// 扣减
+                    money=money.multiply(new BigDecimal(-1));
+                }else {
+                    logger.error("红包余额充值参数异常,异常请求参数companyId:{},money:{},type:{}",companyId,money, type);
+                    throw new RuntimeException("红包余额充值参数异常");
+                }
                 // 增加余额
                 // 增加余额
                 BigDecimal newMoney = originalMoney.add(money);
                 BigDecimal newMoney = originalMoney.add(money);
                 redisCache.setCacheObject(companyMoneyKey, newMoney.toString());
                 redisCache.setCacheObject(companyMoneyKey, newMoney.toString());
 
 
                 // 异步登记余额添加日志
                 // 异步登记余额添加日志
-                asyncRecordBalanceLog(companyId,money,16,newMoney,"红包充值");
+                asyncRecordBalanceLog(companyId,money,16,newMoney,"红包充值(负数为扣款)");
 
 
             } else {
             } else {
-                logger.error("获取redis锁失败,异常请求参数companyId:{},money:{}",companyId,money);
+                logger.error("获取redis锁失败,异常请求参数companyId:{},money:{},type:{}",companyId,money, type);
                 throw new RuntimeException("服务繁忙,请稍后重试");
                 throw new RuntimeException("服务繁忙,请稍后重试");
             }
             }
         } catch (Exception e) {
         } catch (Exception e) {
-            logger.error("增加余额失败: 异常请求参数companyId:{},money:{}",companyId,money, e);
+            logger.error("增加余额失败: 异常请求参数companyId:{},money:{},type:{}",companyId,money, type, e);
             throw new RuntimeException("系统异常,请稍后重试");
             throw new RuntimeException("系统异常,请稍后重试");
         }finally {
         }finally {
             // 只有在成功获取锁的情况下才释放锁
             // 只有在成功获取锁的情况下才释放锁
@@ -1439,7 +1448,7 @@ public class CompanyServiceImpl implements ICompanyService
                 try {
                 try {
                     lock1.unlock();
                     lock1.unlock();
                 } catch (IllegalMonitorStateException e) {
                 } catch (IllegalMonitorStateException e) {
-                    logger.error("尝试释放非当前线程持有的锁: 异常请求参数companyId:{},money:{}",companyId,money);
+                    logger.error("尝试释放非当前线程持有的锁: 异常请求参数companyId:{},money:{},type:{}",companyId,money, type);
                 }
                 }
             }
             }
         }
         }
@@ -1516,4 +1525,59 @@ public class CompanyServiceImpl implements ICompanyService
         companyMapper.batchUpdateCompany(list);
         companyMapper.batchUpdateCompany(list);
     }
     }
 
 
+    /**
+     * @Description: 红包余额回滚(回滚的是客户没领取的红包),红包记录表中,两天没领取的记录不会再发送
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/11/7 9:53
+     */
+    @Override
+    public void rollbackRedPacketMoney() {
+        List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseAddRedPacketLogByCompany();
+        for(RedPacketMoneyVO company:redPacketMoneyVOS){
+            logger.info("红包余额回滚开始:{}",company);
+        }
+        Optional.ofNullable(redPacketMoneyVOS).ifPresent(list -> list.forEach(company -> {
+
+            if(company.getCompanyId()==null){
+                logger.error("红包记录表中存在公司id为null的异常数据");
+                return;
+            }
+            String companyMoneyKey = FsConstants.COMPANY_MONEY_KEY + company.getCompanyId();
+            // 加锁,与看课发放红包的加锁保持一致
+            RLock lock = redissonClient.getLock(FsConstants.COMPANY_MONEY_LOCK + company.getCompanyId());
+            boolean lockAcquired = false;
+            try {
+                lockAcquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
+                if (lockAcquired) {
+                    BigDecimal redisMoney;
+                    // 获取当前余额
+                    String moneyStr = redisCache.getCacheObject(companyMoneyKey);
+                    if (StringUtils.isNotEmpty(moneyStr)) {
+                        redisMoney = new BigDecimal(moneyStr);
+                    }else {
+                        logger.error("缓存公司id:{}的余额不存在,回滚金额{}",company.getCompanyId(),company.getMoney());
+                        return;
+                    }
+                    BigDecimal newMoney = redisMoney.add(company.getMoney());
+                    redisCache.setCacheObject(companyMoneyKey, newMoney.toString());
+
+                    String remark = "执行时间:"+DateUtils.getTime()+",T2天客户未领取红包退回,金额: " + company.getMoney();
+                    asyncRecordBalanceLog(company.getCompanyId(),company.getMoney(),16,newMoney,remark);
+                }
+            } catch (Exception e) {
+                logger.error("退回的红包同步增加到缓存和数据表,参数错误,请求异常,异常信息:{}", e.getMessage(), e);
+            } finally {
+                if (lockAcquired && lock.isHeldByCurrentThread()) {
+                    try {
+                        lock.unlock();
+                    } catch (IllegalMonitorStateException e) {
+                        logger.warn("尝试释放非当前线程持有的锁: companyId:{}", company.getCompanyId());
+                    }
+                }
+            }
+        }));
+    }
+
 }
 }

+ 11 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -663,6 +663,17 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     public List<QwUserVO> selectCompanyQwUserList(String corpId,Long companyId) {
     public List<QwUserVO> selectCompanyQwUserList(String corpId,Long companyId) {
         return companyUserMapper.selectCompanyQwUserList(corpId,companyId);
         return companyUserMapper.selectCompanyQwUserList(corpId,companyId);
     }
     }
+
+    @Override
+    public List<QwUserVO> selectCompanyQwUserListByMy(String corpId, Long companyId, Long userId) {
+        return companyUserMapper.selectCompanyQwUserListByMy(corpId,companyId,userId);
+    }
+
+    @Override
+    public List<QwUserVO> selectCompanyQwUserListByDept(String corpId, Long companyId, List<Long> combinedList, String userType) {
+        return companyUserMapper.selectCompanyQwUserListByDept(corpId,companyId,combinedList,userType);
+    }
+
     @Override
     @Override
     @DataScope(deptAlias = "u", userAlias = "u")
     @DataScope(deptAlias = "u", userAlias = "u")
     public List<CompanyUserQwListVO> selectCompanyUserQwListVO(CompanyUserQwParam user) {
     public List<CompanyUserQwListVO> selectCompanyUserQwListVO(CompanyUserQwParam user) {

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

@@ -55,6 +55,9 @@ public class CompanyDeductVO implements Serializable {
     @Excel(name = "审核人")
     @Excel(name = "审核人")
     private String auditUserNickName;
     private String auditUserNickName;
 
 
+    @Excel(name = "业务类型(0-普通,1-红包扣款)")
+    private Integer businessType;
+
 }
 }
 
 
 
 

+ 5 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCourse.java

@@ -152,4 +152,9 @@ public class FsUserCourse extends BaseEntity
     @TableField(exist = false)
     @TableField(exist = false)
     private Long[] companyIdsList;
     private Long[] companyIdsList;
 
 
+    /**
+     * 课堂配置
+     */
+    private String configJson;
+
 }
 }

+ 0 - 27
fs-service/src/main/java/com/fs/course/domain/FsUserCourseVideo.java

@@ -112,31 +112,4 @@ public class FsUserCourseVideo extends BaseEntity
 
 
     private Long listingEndTime;//商品结束售卖时间
     private Long listingEndTime;//商品结束售卖时间
 
 
-
-    /**
-     * 看课中标签ID
-     */
-    private String watchingTagId;
-    /**
-     * 完课标签ID
-     */
-    private String watchedTagId;
-    /**
-     * 标签组ID
-     */
-    private String tagGroupId;
-
-    /**
-     * 标签组表中的ID
-     */
-    private Long tgId;
-    /**
-     * 看课标签 表中的ID
-     */
-    private Long watchingTgId;
-    /**
-     * 完课标签 表中的ID
-     */
-    private Long watchedTgId;
-
 }
 }

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

@@ -570,4 +570,13 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
 
     // 统计当天各公司的观看人数和完播人数, 存到redis中,定时任务每 ? 分钟执行一次
     // 统计当天各公司的观看人数和完播人数, 存到redis中,定时任务每 ? 分钟执行一次
     List<WatchCourseStatisticsResultDTO> watchCourseStatisticsGroupByCompany(@Param("params") Map<String, Object> params);
     List<WatchCourseStatisticsResultDTO> watchCourseStatisticsGroupByCompany(@Param("params") Map<String, Object> params);
+
+    @Select({"<script>" +
+            " select qw_external_contact_id from fs_course_watch_log where log_id in  " +
+            "        <foreach collection=\"watchLogIds\" item=\"id\" open=\"(\" separator=\",\" close=\")\">\n" +
+               "    #{id} " +
+               "</foreach>" +
+            "</script>"
+    })
+    List<Long> getExContactIdsIdsByWatchLogIds(@Param("watchLogIds")List<Long> watchLogIds);
 }
 }

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

@@ -193,7 +193,7 @@ public interface FsUserCourseMapper
 
 
 
 
     @Select("select v.video_id,v.title,v.course_id,v.video_url,v.question_bank_id," +
     @Select("select v.video_id,v.title,v.course_id,v.video_url,v.question_bank_id," +
-            "SEC_TO_TIME(c.duration) as total_duration,c.views,c.course_name,c.description,c.img_url " +
+            "SEC_TO_TIME(c.duration) as total_duration,c.views,c.course_name,c.description,c.img_url,c.config_json  " +
             " from fs_user_course_video v " +
             " from fs_user_course_video v " +
             "left join fs_user_course c on v.course_id = c.course_id " +
             "left join fs_user_course c on v.course_id = c.course_id " +
             "where v.video_id = #{videoId}")
             "where v.video_id = #{videoId}")
@@ -328,4 +328,11 @@ public interface FsUserCourseMapper
 
 
     @Select("select course_id,course_name,description,img_url,second_img secondImg,views from fs_user_course where course_id = #{courseId} and is_del = 0")
     @Select("select course_id,course_name,description,img_url,second_img secondImg,views from fs_user_course where course_id = #{courseId} and is_del = 0")
     List<FsUserCourseVideoAppletVO> selectFsUserCourseVideoAppletListByCourseId(@Param("courseId") Long courseId);
     List<FsUserCourseVideoAppletVO> selectFsUserCourseVideoAppletListByCourseId(@Param("courseId") Long courseId);
+
+
+    /**
+     * 修改课堂配置
+     */
+    @Update("update fs_user_course set config_json = #{configJson} where course_id = #{id}")
+    void editConfig(@Param("id") Long id, @Param("configJson") String configJson);
 }
 }

+ 3 - 1
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java

@@ -67,7 +67,9 @@ public interface FsUserCourseVideoRedPackageMapper
      * @return 结果
      * @return 结果
      */
      */
     public int deleteFsUserCourseVideoRedPackageByIds(Long[] ids);
     public int deleteFsUserCourseVideoRedPackageByIds(Long[] ids);
-    public int deleteFsUserCourseVideoRedPackageByVedioIds(Long[] ids);
+    public int deleteFsUserCourseVideoRedPackageByVedioIds(
+            @Param("videoIds") Long[] videoIds,
+            @Param("periodIds") Long[] periodIds);
     @Update("INSERT INTO fs_user_course_video_red_package (company_id, video_id, red_packet_money) " +
     @Update("INSERT INTO fs_user_course_video_red_package (company_id, video_id, red_packet_money) " +
             "VALUES (#{companyId}, #{videoId}, #{redPacketMoney}) " +
             "VALUES (#{companyId}, #{videoId}, #{redPacketMoney}) " +
             "ON DUPLICATE KEY UPDATE red_packet_money = VALUES(red_packet_money);")
             "ON DUPLICATE KEY UPDATE red_packet_money = VALUES(red_packet_money);")

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

@@ -139,4 +139,11 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
      * 看课统计
      * 看课统计
      * */
      * */
     List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param);
     List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param);
+
+    /**
+     * 根据看课记录id获取所有的外部联系人ids
+     * @param watchLogIds
+     * @return
+     */
+    List<Long> getExContactIdsIdsByWatchLogIds(List<Long> watchLogIds);
 }
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseService.java

@@ -13,6 +13,7 @@ import com.fs.course.vo.*;
 import com.fs.course.vo.newfs.FsUserCourseListVO;
 import com.fs.course.vo.newfs.FsUserCourseListVO;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
 
 
+import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.NotNull;
 
 
 /**
 /**
@@ -134,4 +135,9 @@ public interface IFsUserCourseService
     List<FsUserCourseVideoAppletVO> selectFsUserCourseVideoAppletListByCourseId(Long courseId);
     List<FsUserCourseVideoAppletVO> selectFsUserCourseVideoAppletListByCourseId(Long courseId);
 
 
     R createAppCourseSortLink(FsCourseLinkCreateParam fsCourseLinkCreateParam);
     R createAppCourseSortLink(FsCourseLinkCreateParam fsCourseLinkCreateParam);
+
+    /**
+     * 修改课堂配置
+     */
+    void editConfig(Long id, String configJson);
 }
 }

+ 15 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -645,8 +645,10 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param) {
     public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param) {
 
 
         // 因为selectFsCourseWatchLogListVO 这个方法中查询了看课日志记录表 需要限制创建时间必传
         // 因为selectFsCourseWatchLogListVO 这个方法中查询了看课日志记录表 需要限制创建时间必传
-        if(StringUtils.isEmpty(param.getSTime()) || StringUtils.isEmpty(param.getETime())){
-            throw new RuntimeException("请输入创建时间");
+        if((StringUtils.isEmpty(param.getSTime()) || StringUtils.isEmpty(param.getETime())) &&
+                (StringUtils.isEmpty(param.getUpSTime()) || StringUtils.isEmpty(param.getUpETime())) &&
+                (StringUtils.isEmpty(param.getScheduleEndTime()) || StringUtils.isEmpty(param.getScheduleStartTime()))){
+            throw new RuntimeException("请输入创建时间或营期时间或更新时间其中一个");
         }
         }
 
 
         // 待看课-未注册
         // 待看课-未注册
@@ -1287,5 +1289,16 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return fsCourseWatchLogMapper.selectQwFsCourseWatchLogStatisticsListVO(param);
         return fsCourseWatchLogMapper.selectQwFsCourseWatchLogStatisticsListVO(param);
     }
     }
 
 
+    /**
+     * 根据看课记录id获取所有的外部联系人ids
+     * @param watchLogIds
+     * @return
+     */
+    @Override
+    public List<Long> getExContactIdsIdsByWatchLogIds(List<Long> watchLogIds){
+        return fsCourseWatchLogMapper.getExContactIdsIdsByWatchLogIds(watchLogIds);
+    }
+
+
 
 
 }
 }

+ 2 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java

@@ -129,11 +129,12 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
         int flag = 0;
         int flag = 0;
         List<FsUserCoursePeriodDays> fsUserCoursePeriodDays = fsUserCoursePeriodDaysMapper.selectBatchIds(Arrays.asList(ids));
         List<FsUserCoursePeriodDays> fsUserCoursePeriodDays = fsUserCoursePeriodDaysMapper.selectBatchIds(Arrays.asList(ids));
         List<Long> periodDayIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getId).collect(Collectors.toList());
         List<Long> periodDayIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getId).collect(Collectors.toList());
+        List<Long> getPeriodIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getPeriodId).collect(Collectors.toList());
         List<Long> videoIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getVideoId).collect(Collectors.toList());
         List<Long> videoIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getVideoId).collect(Collectors.toList());
         if(!periodDayIds.isEmpty()){
         if(!periodDayIds.isEmpty()){
             flag = fsUserCoursePeriodDaysMapper.updateBatchDelFlag(periodDayIds.toArray(new Long[0]),1);
             flag = fsUserCoursePeriodDaysMapper.updateBatchDelFlag(periodDayIds.toArray(new Long[0]),1);
             //删除红包记录
             //删除红包记录
-            fsUserCourseVideoRedPackageMapper.deleteFsUserCourseVideoRedPackageByVedioIds(videoIds.toArray(new Long[0]));
+            fsUserCourseVideoRedPackageMapper.deleteFsUserCourseVideoRedPackageByVedioIds(videoIds.toArray(new Long[0]),getPeriodIds.toArray(new Long[0]));
         }
         }
         return flag;
         return flag;
     }
     }

+ 8 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -762,6 +762,14 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         return R.error("生成链接失败!");
         return R.error("生成链接失败!");
     }
     }
 
 
+    /**
+     * 修改课堂配置
+     */
+    @Override
+    public void editConfig(Long id, String configJson) {
+        fsUserCourseMapper.editConfig(id, configJson);
+    }
+
 
 
     private Graphics2D initializeGraphics(BufferedImage combined) {
     private Graphics2D initializeGraphics(BufferedImage combined) {
         Graphics2D graphics = combined.createGraphics();
         Graphics2D graphics = combined.createGraphics();

+ 1 - 28
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -942,30 +942,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
 
     @Override
     @Override
     public List<FsUserCourseVideoVO> selectFsUserCourseVideoListByCourseIdAndCompany(FsUserCourseVideoParam fsUserCourseVideo) {
     public List<FsUserCourseVideoVO> selectFsUserCourseVideoListByCourseIdAndCompany(FsUserCourseVideoParam fsUserCourseVideo) {
-        List<FsUserCourseVideoVO> fsUserCourseVideoVOS = fsUserCourseVideoMapper.selectFsUserCourseVideoListByCourseIdAndCompany(fsUserCourseVideo);
-        for (FsUserCourseVideoVO item : fsUserCourseVideoVOS) {
-            if(ObjectUtils.isNotNull(item.getTgId())){
-                QwTagGroup qwTagGroup = qwTagGroupMapper.selectQwTagGroupById(item.getTgId());
-                if(ObjectUtils.isNotNull(qwTagGroup)){
-                    item.setTagGroupName(qwTagGroup.getName());
-                }
-            }
-
-            if(ObjectUtils.isNotNull(item.getWatchingTgId())){
-                QwTag qwTag = qwTagMapper.selectQwTagById(item.getWatchingTgId());
-                if(ObjectUtils.isNotNull(qwTag)){
-                    item.setWatchingTagName(qwTag.getName());
-                }
-            }
-
-            if(ObjectUtils.isNotNull(item.getWatchedTgId())) {
-                QwTag qwTag = qwTagMapper.selectQwTagById(item.getWatchedTgId());
-                if(ObjectUtils.isNotNull(qwTag)){
-                    item.setWatchedTagName(qwTag.getName());
-                }
-            }
-        }
-        return fsUserCourseVideoVOS;
+        return fsUserCourseVideoMapper.selectFsUserCourseVideoListByCourseIdAndCompany(fsUserCourseVideo);
     }
     }
 
 
     @Override
     @Override
@@ -2730,10 +2707,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         }else {
         }else {
             link.setLinkType(3);
             link.setLinkType(3);
         }
         }
-        link.setProjectCode(cloudHostProper.getProjectCode());
-
-        link.setProjectCode(cloudHostProper.getProjectCode());
-
         String randomString = generateRandomStringWithLock();
         String randomString = generateRandomStringWithLock();
         if (StringUtil.strIsNullOrEmpty(randomString)){
         if (StringUtil.strIsNullOrEmpty(randomString)){
             link.setLink(UUID.randomUUID().toString().replace("-", ""));
             link.setLink(UUID.randomUUID().toString().replace("-", ""));

+ 5 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCourseListPVO.java

@@ -52,4 +52,9 @@ public class FsUserCourseListPVO extends BaseEntity
      * 项目名称
      * 项目名称
      */
      */
     private String projectName;
     private String projectName;
+
+    /**
+     * 课堂配置
+     */
+    private String configJson;
 }
 }

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

@@ -35,7 +35,7 @@ public class FsUserCourseVideoH5VO extends BaseEntity
     /** 总播放量 */
     /** 总播放量 */
     private Long views;
     private Long views;
 
 
-
-
+    /** 课堂配置 **/
+    private String configJson;
 
 
 }
 }

+ 0 - 39
fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoVO.java

@@ -68,43 +68,4 @@ public class FsUserCourseVideoVO extends BaseEntity {
     private String redPacketMoney;
     private String redPacketMoney;
 
 
     private String companyRedPacketMoney;
     private String companyRedPacketMoney;
-    /**
-     * 标签组表中的ID
-     */
-    private Long tgId;
-    /**
-     * 看课标签 表中的ID
-     */
-    private Long watchingTgId;
-    /**
-     * 完课标签 表中的ID
-     */
-    private Long watchedTgId;
-
-    /**
-     * 看课中标签ID
-     */
-    private String watchingTagId;
-    /**
-     * 完课标签ID
-     */
-    private String watchedTagId;
-    /**
-     * 标签组ID
-     */
-    private String tagGroupId;
-
-    /**
-     * 标签组名称
-     */
-    private String tagGroupName;
-    /**
-     * 看课标签
-     */
-    private String watchingTagName;
-    /**
-     * 完课标签
-     */
-    private String watchedTagName;
-
 }
 }

+ 1 - 1
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -1511,7 +1511,7 @@ public class AiHookServiceImpl implements AiHookService {
                             }
                             }
                             if (value != null) {
                             if (value != null) {
                                 str += fieldName + ": " + value.toString() + "\n";
                                 str += fieldName + ": " + value.toString() + "\n";
-                            }else {
+                            }else if("交流状态".equals(fieldName) || "学习到的章节".equals(fieldName)){
                                 str += fieldName + ":  \n";
                                 str += fieldName + ":  \n";
                             }
                             }
                         }
                         }

+ 5 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -243,6 +243,11 @@ public class FsUserServiceImpl implements IFsUserService {
         } else {
         } else {
             fsUser.setPhone(null);
             fsUser.setPhone(null);
         }
         }
+        if (ObjectUtils.isNotEmpty(fsUser.getLevel())&&fsUser.getLevel().equals(1)){
+            fsUser.setIsShow(1);
+        }else {
+            fsUser.setIsShow(0);
+        }
         return fsUserMapper.updateFsUser(fsUser);
         return fsUserMapper.updateFsUser(fsUser);
     }
     }
 
 

+ 47 - 0
fs-service/src/main/java/com/fs/hisStore/enums/CompanyEnum.java

@@ -0,0 +1,47 @@
+package com.fs.hisStore.enums;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 设置单店铺的商城
+ */
+public enum CompanyEnum {
+    YISHANYUAN("益善缘"),
+    KANGNIAN_TANG("康年堂"),
+    SIFU_TANG("四福堂"),
+    NMG_MYT("内蒙古一贴"),
+    CQ_TYT("重庆泰医堂"),
+    CHUNZHENG_TANG("纯正堂");
+
+    private final String companyName;
+
+    CompanyEnum(String companyName) {
+        this.companyName = companyName;
+    }
+
+    public String getCompanyName() {
+        return companyName;
+    }
+
+    /**
+     * 静态集合,避免每次调用都重新创建
+     */
+    private static final Set<String> COMPANY_NAMES = Collections.unmodifiableSet(
+            Arrays.stream(values())
+                    .map(CompanyEnum::getCompanyName)
+                    .collect(Collectors.toSet())
+    );
+
+    /**
+     * 比较是否存在
+     *
+     * @param companyName
+     * @return
+     */
+    public static boolean contains(String companyName) {
+        return COMPANY_NAMES.contains(companyName);
+    }
+}

+ 1 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductCategoryScrmMapper.java

@@ -112,4 +112,5 @@ public interface FsStoreProductCategoryScrmMapper
     @Select("select cate_id dict_value, cate_name dict_label,is_del status from fs_store_product_category_scrm WHERE pid = 0 and is_del=0 ")
     @Select("select cate_id dict_value, cate_name dict_label,is_del status from fs_store_product_category_scrm WHERE pid = 0 and is_del=0 ")
     List<OptionsVO> selectFsStoreProductPidList();
     List<OptionsVO> selectFsStoreProductPidList();
 
 
+    List<Long> selectCateIdsByName(FsStoreProductCategoryScrm fsStoreProductCategoryScrm);
 }
 }

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java

@@ -215,6 +215,12 @@ public interface FsStoreProductScrmMapper
             "<if test = 'maps.salesOrder != null  and maps.salesOrder==\"asc\" '> " +
             "<if test = 'maps.salesOrder != null  and maps.salesOrder==\"asc\" '> " +
             "order by p.sales asc " +
             "order by p.sales asc " +
             "</if>" +
             "</if>" +
+            " <if test = 'maps.cateIds != null and maps.cateIds.size() > 0'>\n" +
+            "        and p.cate_id in \n" +
+            "        <foreach collection='maps.cateIds' item='cateId' open='(' separator=',' close=')'>\n" +
+            "            #{cateId}\n" +
+            "        </foreach>\n" +
+            "    </if>"+
             "<if test = 'maps.newOrder != null and maps.newOrder==\"desc\" '> " +
             "<if test = 'maps.newOrder != null and maps.newOrder==\"desc\" '> " +
             "and p.is_new =1 order by p.create_time desc  " +
             "and p.is_new =1 order by p.create_time desc  " +
             "</if>" +
             "</if>" +

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductQueryParam.java

@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.Data;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
+import java.util.List;
 
 
 @Data
 @Data
 public class FsStoreProductQueryParam extends BaseQueryParam implements Serializable
 public class FsStoreProductQueryParam extends BaseQueryParam implements Serializable
@@ -23,6 +24,8 @@ public class FsStoreProductQueryParam extends BaseQueryParam implements Serializ
     private String salesOrder;
     private String salesOrder;
     @ApiModelProperty(value = "1 只显示商城展示的商品 0展示所有")
     @ApiModelProperty(value = "1 只显示商城展示的商品 0展示所有")
     private Integer isDisplay=1;
     private Integer isDisplay=1;
+    // 1-家庭应急
+    private String productFlag;
 
 
     // token 需用户端传入
     // token 需用户端传入
     private String token;
     private String token;
@@ -35,4 +38,6 @@ public class FsStoreProductQueryParam extends BaseQueryParam implements Serializ
 
 
     //是否多店铺 1是
     //是否多店铺 1是
     private Integer isStores;
     private Integer isStores;
+
+    List<Long> cateIds;
 }
 }

+ 13 - 9
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -983,17 +983,21 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             redisCache.setCacheObject("orderAmount:" + storeOrder.getId(), storeOrder.getPayMoney(), 24, TimeUnit.HOURS);//物流代收自定义金额
             redisCache.setCacheObject("orderAmount:" + storeOrder.getId(), storeOrder.getPayMoney(), 24, TimeUnit.HOURS);//物流代收自定义金额
             //删除推荐订单KEY
             //删除推荐订单KEY
             String createOrderKey = param.getCreateOrderKey();
             String createOrderKey = param.getCreateOrderKey();
-            if (StringUtils.isNotEmpty(createOrderKey)) {
-                if (config.getIsBrushOrders() == null || !(config.getIsBrushOrders() && param.getCompanyUserId() != null)) {//未开启刷单
-                    redisCache.deleteObject("createOrderKey:" + createOrderKey);
-                    redisCache.deleteObject("orderCarts:" + createOrderKey);
-                    redisCache.deleteObject("createOrderMoney:" + createOrderKey);
-                }
-
-                //货到付款自定义金额 key改为id存储
+            if("鸿森堂".equals(cloudHostProper.getCompanyName())){
                 BigDecimal amount = redisCache.getCacheObject("createOrderAmount:" + createOrderKey);
                 BigDecimal amount = redisCache.getCacheObject("createOrderAmount:" + createOrderKey);
-                redisCache.deleteObject("createOrderAmount:" + createOrderKey);
                 redisCache.setCacheObject("orderAmount:" + storeOrder.getId(), amount, 24, TimeUnit.HOURS);//物流代收自定义金额
                 redisCache.setCacheObject("orderAmount:" + storeOrder.getId(), amount, 24, TimeUnit.HOURS);//物流代收自定义金额
+            }else {
+                if (StringUtils.isNotEmpty(createOrderKey)) {
+                    if (config.getIsBrushOrders() == null || !(config.getIsBrushOrders() && param.getCompanyUserId() != null)) {//未开启刷单
+                        redisCache.deleteObject("createOrderKey:" + createOrderKey);
+                        redisCache.deleteObject("orderCarts:" + createOrderKey);
+                        redisCache.deleteObject("createOrderMoney:" + createOrderKey);
+                    }
+
+                    //货到付款自定义金额 key改为id存储
+                    BigDecimal amount = redisCache.getCacheObject("createOrderAmount:" + createOrderKey);
+                    redisCache.deleteObject("createOrderAmount:" + createOrderKey);
+                }
             }
             }
             return R.ok().put("order", storeOrder).put("payLimitTime", payLimitTime);
             return R.ok().put("order", storeOrder).put("payLimitTime", payLimitTime);
         } else {
         } else {

+ 20 - 2
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -28,6 +28,7 @@ import com.fs.his.vo.FsStoreProductListSVO;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
 import com.fs.hisStore.config.MedicalMallConfig;
 import com.fs.hisStore.config.MedicalMallConfig;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.domain.*;
+import com.fs.hisStore.enums.CompanyEnum;
 import com.fs.hisStore.mapper.*;
 import com.fs.hisStore.mapper.*;
 import com.fs.hisStore.utils.StoreAuditLogUtil;
 import com.fs.hisStore.utils.StoreAuditLogUtil;
 import com.fs.statis.dto.ModifyMoreDTO;
 import com.fs.statis.dto.ModifyMoreDTO;
@@ -97,6 +98,8 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
 
 
     @Autowired
     @Autowired
     private CloudHostProper cloudHostProper;
     private CloudHostProper cloudHostProper;
+    @Autowired
+    private FsStoreProductCategoryScrmMapper fsStoreProductCategoryScrmMapper;
 
 
     /**
     /**
      * 查询商品
      * 查询商品
@@ -338,7 +341,7 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         product.setStoreId(param.getStoreId());
         product.setStoreId(param.getStoreId());
         product.setIsDrug(param.getIsDrug().toString());
         product.setIsDrug(param.getIsDrug().toString());
         //校验店铺资质信息
         //校验店铺资质信息
-        if(!("益善缘".equals(cloudHostProper.getCompanyName())) && !("康年堂".equals(cloudHostProper.getCompanyName())) && !("纯正堂".equals(cloudHostProper.getCompanyName()))){
+        if (!CompanyEnum.contains(cloudHostProper.getCompanyName())) {
             //获取店铺
             //获取店铺
             FsStoreScrm store = fsStoreScrmService.selectFsStoreByStoreId(product.getStoreId());
             FsStoreScrm store = fsStoreScrmService.selectFsStoreByStoreId(product.getStoreId());
             if(store == null || 1 != store.getStatus()){
             if(store == null || 1 != store.getStatus()){
@@ -416,7 +419,6 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         return R.ok();
         return R.ok();
     }
     }
 
 
-
     private void addProductAttr(Long productId, List<ProductArrtDTO> items, List<FsStoreProductAttrValueScrm> values){
     private void addProductAttr(Long productId, List<ProductArrtDTO> items, List<FsStoreProductAttrValueScrm> values){
         //清空attr
         //清空attr
         fsStoreProductAttrMapper.clear(productId);
         fsStoreProductAttrMapper.clear(productId);
@@ -690,6 +692,22 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     public List<FsStoreProductListQueryVO> selectFsStoreProductListQuery(FsStoreProductQueryParam param) {
     public List<FsStoreProductListQueryVO> selectFsStoreProductListQuery(FsStoreProductQueryParam param) {
         boolean stores = medicalMallConfig.isStores();
         boolean stores = medicalMallConfig.isStores();
         param.setIsStores(stores?1:0);
         param.setIsStores(stores?1:0);
+        // 蒙一堂特殊产品展示
+        List<Long>  cateIds;
+        if("1".equals(param.getProductFlag())){// 家庭应急
+            // fs_store_product_category_scrm
+            FsStoreProductCategoryScrm fsStoreProductCategoryScrm = new FsStoreProductCategoryScrm();
+            fsStoreProductCategoryScrm.setPid(0L);
+            fsStoreProductCategoryScrm.setCateName("家庭应急");
+            cateIds =fsStoreProductCategoryScrmMapper.selectCateIdsByName(fsStoreProductCategoryScrm);
+            if(cateIds!= null && !cateIds.isEmpty()){
+                param.setCateIds(cateIds);
+            }else {
+                // 不让查询出来
+                return new ArrayList<>();
+            }
+        }
+
         return fsStoreProductMapper.selectFsStoreProductListQuery(param);
         return fsStoreProductMapper.selectFsStoreProductListQuery(param);
     }
     }
 
 

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

@@ -62,9 +62,14 @@ public interface QwCompanyMapper
      * @return 结果
      * @return 结果
      */
      */
     public int deleteQwCompanyByIds(Long[] ids);
     public int deleteQwCompanyByIds(Long[] ids);
+
     @Select("SELECT corp_id from qw_company where FIND_IN_SET(#{companyId}, company_ids)")
     @Select("SELECT corp_id from qw_company where FIND_IN_SET(#{companyId}, company_ids)")
     List<String> selectQwCompanyCorpIdListByCompanyId(Long companyId);
     List<String> selectQwCompanyCorpIdListByCompanyId(Long companyId);
 
 
+    @Select("SELECT corp_id from qw_company where status = 1 ")
+    List<String> selectQwCompanyCorpIdListByAll();
+
+
     @Select("SELECT * from qw_company where corp_id=#{corpId} limit 1")
     @Select("SELECT * from qw_company where corp_id=#{corpId} limit 1")
     QwCompany selectQwCompanyByCorpId(String corpId);
     QwCompany selectQwCompanyByCorpId(String corpId);
 
 

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

@@ -251,8 +251,11 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "    </foreach>\n" +
             "    </foreach>\n" +
             "    )"+
             "    )"+
             "</if> " +
             "</if> " +
-
-//            "            <if test=\"remarkMobiles != null  and remarkMobiles != ''\"> and ec.remark_mobiles like concat( #{remarkMobiles}, '%')</if>\n" +
+            "<if test='outTagIds !=null'>\n" +
+            "    AND <foreach collection=\"outTagIds\" item=\"item\" open=\"(\" separator=\" AND \" close=\")\">\n" +
+            "        NOT find_in_set(#{item}, REGEXP_REPLACE(ec.tag_ids, '[\\\"\\\\[\\\\]]', ''))\n" +
+            "    </foreach>\n" +
+            "</if>"+
             "<if test=\"remarkMobiles != null and remarkMobiles != ''\">\n" +
             "<if test=\"remarkMobiles != null and remarkMobiles != ''\">\n" +
             "    AND ec.search_mobile LIKE concat(#{remarkMobiles}, '%')\n" +
             "    AND ec.search_mobile LIKE concat(#{remarkMobiles}, '%')\n" +
             "</if>" +
             "</if>" +

+ 9 - 7
fs-service/src/main/java/com/fs/qw/mapper/QwTagMapper.java

@@ -70,14 +70,16 @@ public interface QwTagMapper
      */
      */
     public int deleteQwTagByIds(Long[] ids);
     public int deleteQwTagByIds(Long[] ids);
     @Select({"<script> " +
     @Select({"<script> " +
-            "select * from qw_tag "+
+            " select t1.*,t2.corp_name from qw_tag t1 "+
+            " inner join qw_company t2 on t1.corp_id = t2.corp_id " +
             "<where>\n" +
             "<where>\n" +
-            "            <if test=\"tagId != null  and tagId != ''\"> and tag_id = #{tagId}</if>\n" +
-            "            <if test=\"name != null  and name != ''\"> and name like concat('%', #{name}, '%')</if>\n" +
-            "            <if test=\"groupId != null  and groupId != ''\"> and group_id = #{groupId}</if>\n" +
-            "            <if test=\"order != null  and order != ''\"> and `order` = #{order}</if>\n" +
-            "            <if test=\"corpId != null  and corpId != ''\"> and corp_id = #{corpId}</if>\n" +
-            "            <if test=\"companyId != null \"> and company_id = #{companyId}</if>\n" +
+            "            <if test=\"tagId != null  and tagId != ''\"> and t1.tag_id = #{tagId}</if>\n" +
+            "            <if test=\"name != null  and name != ''\"> and t1.name like concat('%', #{name}, '%')</if>\n" +
+            "            <if test=\"groupId != null  and groupId != ''\"> and t1.group_id = #{groupId}</if>\n" +
+            "            <if test=\"order != null  and order != ''\"> and t1.`order` = #{order}</if>\n" +
+            "            <if test=\"corpId != null  and corpId != ''\"> and t1.corp_id = #{corpId}</if>\n" +
+//            "            <if test=\"companyId != null \"> and t1.company_id = #{companyId}</if>\n" +
+            "            <if test=\"companyId != null \">and FIND_IN_SET(#{companyId},t2.company_ids) </if>\n" +
             "</where>"+
             "</where>"+
             "</script>"})
             "</script>"})
     List<QwTagVO> selectQwTagListVO(QwTag qwTag);
     List<QwTagVO> selectQwTagListVO(QwTag qwTag);

+ 64 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java

@@ -145,7 +145,7 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
             "<if test ='nickName !=null and nickName!=\"\"'>\n" +
             "<if test ='nickName !=null and nickName!=\"\"'>\n" +
             "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
             "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
             "</if>" +
             "</if>" +
-            "<if test ='deptId !=null and deptId!=\"\"'>\n" +
+            "<if test ='(deptIds ==null or deptIds.size = 0) and deptId !=null and deptId!=\"\"'>\n" +
             "   and cu.dept_id = #{deptId}\n" +
             "   and cu.dept_id = #{deptId}\n" +
             "</if>" +
             "</if>" +
             " <if test=\"filterDeptIds != null and filterDeptIds.size() != 0\">\n" +
             " <if test=\"filterDeptIds != null and filterDeptIds.size() != 0\">\n" +
@@ -154,6 +154,12 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
             "         ${item}\n" +
             "         ${item}\n" +
             "      </foreach>\n" +
             "      </foreach>\n" +
             " </if> " +
             " </if> " +
+            "<if test ='deptIds !=null and deptIds.size > 0'>\n" +
+            "and cu.dept_id in" +
+            "<foreach collection=\"deptIds\" item=\"deptId\" index=\"index\"  separator=\",\" open=\"(\" close=\")\">\n" +
+            "     #{deptId}\n" +
+            "</foreach>" +
+            "</if>" +
             "<if test ='ids !=null and ids!=\"\"'>\n" +
             "<if test ='ids !=null and ids!=\"\"'>\n" +
             "   and qec.qw_user_id in (${ids})\n" +
             "   and qec.qw_user_id in (${ids})\n" +
             "</if>" +
             "</if>" +
@@ -212,6 +218,63 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
             "    DATE(qec.create_time) "+
             "    DATE(qec.create_time) "+
             "</script>"})
             "</script>"})
     List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAndOther(FsCourseWatchLogListParam param);
     List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAndOther(FsCourseWatchLogListParam param);
+
+    @Select({"<script> " +
+            "SELECT\n" +
+            "    qec.qw_user_id id,\n" +
+            "    qu.qw_user_name AS qw_user_name, \n" +
+            "    DATE(qec.create_time) AS create_time, \n" +
+            "    COUNT(1) AS line,\n" +
+            "    COUNT(CASE WHEN qec.is_interact = 1 THEN 1 END) AS interact,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 1 THEN 1 END) AS A,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 2 THEN 1 END) AS B,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 3 THEN 1 END) AS C,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 4 THEN 1 END) AS D,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 5 THEN 1 END) AS E,\n" +
+            "    COUNT(CASE WHEN qec.fs_user_id IS NOT NULL THEN 1 END) AS sign,\n" +
+            "    COUNT(CASE WHEN qec.`status` =3 THEN 1 END) AS los,\n" +
+            "    COUNT(CASE WHEN qec.`status` IN (4, 5,6) THEN 1 END) AS del\n" +
+            "FROM\n" +
+            "    qw_external_contact qec\n" +
+            "JOIN\n" +
+            "    qw_user qu ON qec.qw_user_id = qu.id \n" +
+            "left join company_user cu on qec.company_user_id = cu.user_id "+
+            "WHERE\n" +
+            "    DATE(qec.create_time) &gt;= DATE(#{sTime}) and  DATE(qec.create_time) &lt;= DATE(#{eTime}) and qec.company_id =#{companyId} " +
+            " and NOT EXISTS (\n" +
+            " SELECT 1 \n" +
+            " FROM qw_external_contact_transfer_log t " +
+            " WHERE t.external_contact_id = qec.id " +
+            " AND DATE(t.create_time) &gt;= DATE(#{sTime}) \n" +
+            " AND DATE(t.create_time) &lt;= DATE(#{eTime})\n" +
+            " )" +
+            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
+            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "<if test ='deptIds !=null and deptIds.size > 0'>\n" +
+            "and cu.dept_id in" +
+            "<foreach collection=\"deptIds\" item=\"deptId\" index=\"index\"  separator=\",\" open=\"(\" close=\")\">\n" +
+            "     #{deptId}\n" +
+            "</foreach>" +
+            "</if>" +
+            "<if test ='(deptIds == null or deptIds.size == 0) and deptId !=null and deptId!=\"\"'>\n" +
+            "   and cu.dept_id = #{deptId}\n" +
+            "</if>" +
+            " <if test=\"deptIds != null and deptIds.size() != 0\">\n" +
+            "     and cu.dept_id  in\n" +
+            "      <foreach collection=\"deptIds\" item=\"item\" open=\"(\" close=\")\" separator=\",\">\n" +
+            "         ${item}\n" +
+            "      </foreach>\n" +
+            " </if> " +
+            "<if test ='ids !=null and ids!=\"\"'>\n" +
+            "   and qec.qw_user_id in (${ids})\n" +
+            "</if>" +
+            "GROUP BY\n" +
+            "    qec.qw_user_id, DATE(qec.create_time) \n" +
+            "ORDER BY\n" +
+            "    DATE(qec.create_time) "+
+            "</script>"})
+    List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAndOtherExcludeTransfer(FsCourseWatchLogListParam param);
     @Select("select \n" +
     @Select("select \n" +
             "COUNT(CASE WHEN day = 0 and status in (1,2) THEN 1 END) AS firstOnline,\n" +
             "COUNT(CASE WHEN day = 0 and status in (1,2) THEN 1 END) AS firstOnline,\n" +
             "COUNT(CASE WHEN day = 0 and status=2 THEN 1 END) AS firstOver,\n" +
             "COUNT(CASE WHEN day = 0 and status=2 THEN 1 END) AS firstOver,\n" +

+ 5 - 0
fs-service/src/main/java/com/fs/qw/param/QwExternalContactParam.java

@@ -70,6 +70,11 @@ public class QwExternalContactParam {
     @Excel(name = "标签id")
     @Excel(name = "标签id")
     private List<String> tagIds;
     private List<String> tagIds;
 
 
+
+    /** 排除的标签id */
+    @Excel(name = "排除标签id")
+    private List<String> outTagIds;
+
     private String remark;
     private String remark;
 
 
     /** 备注电话号码 */
     /** 备注电话号码 */

+ 5 - 0
fs-service/src/main/java/com/fs/qw/param/QwExternalContactUpdateNoteParam.java

@@ -1,5 +1,6 @@
 package com.fs.qw.param;
 package com.fs.qw.param;
 
 
+import com.fs.course.param.FsCourseWatchLogListParam;
 import lombok.Data;
 import lombok.Data;
 
 
 import java.util.List;
 import java.util.List;
@@ -13,4 +14,8 @@ public class QwExternalContactUpdateNoteParam {
     private Integer addType;
     private Integer addType;
     private boolean filter;
     private boolean filter;
     private QwExternalContactParam param;
     private QwExternalContactParam param;
+    private List<Long> watchLogIds;
+    //来源于我的看课记录 1 来源于看课记录 0
+    private Integer fromMyList;
+    private FsCourseWatchLogListParam watchLogParam;
 }
 }

+ 5 - 0
fs-service/src/main/java/com/fs/qw/param/QwTagParam.java

@@ -12,4 +12,9 @@ public class QwTagParam  {
     /** 企业id */
     /** 企业id */
     @Excel(name = "企业id")
     @Excel(name = "企业id")
     private String corpId;
     private String corpId;
+
+    /**
+     * 公司id
+     */
+    private Long companyId;
 }
 }

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

@@ -81,4 +81,6 @@ public interface IQwWatchLogService extends IService<QwWatchLog>{
     TableDataInfo selectQwWatchLogStatisticsListVONew(QwWatchLogStatisticsListParam param);
     TableDataInfo selectQwWatchLogStatisticsListVONew(QwWatchLogStatisticsListParam param);
 
 
     List<QwWatchLogStatisticsListVO> selectQwWatchLogStatisticsListVOExport(FsCourseWatchLogListParam param);
     List<QwWatchLogStatisticsListVO> selectQwWatchLogStatisticsListVOExport(FsCourseWatchLogListParam param);
+
+    List<QwWatchLogStatisticsListVO> selectQwWatchLogStatisticsListVOExportExcludeTransfer(FsCourseWatchLogListParam param);
 }
 }

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

@@ -4337,9 +4337,9 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
     @Override
     @Override
     public void deletefollowUserByExternalUserId(String externalUserID, String userID, String corpId) {
     public void deletefollowUserByExternalUserId(String externalUserID, String userID, String corpId) {
 
 
-        //客户删除销售-找这个销售的sop任务中所有的营期里有没有这个客户,删了(暂时不要删-这种流失的有些一样能发消息)
-//        sopUserLogsInfoMapper.deleteByQwUserIdAndCorpIdToContactId(userID,corpId, externalUserID);
-//        logger.error("客户删除销售-"+"|"+externalUserID+"|"+userID+"|"+corpId);
+        //客户删除销售-找这个销售的sop任务中所有的营期里有没有这个客户-流失客户删除
+        sopUserLogsInfoMapper.deleteByQwUserIdAndCorpIdToContactId(userID,corpId, externalUserID);
+        logger.error("客户删除销售-"+"|"+externalUserID+"|"+userID+"|"+corpId);
 
 
         QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactUserIdAndExternalIdAndCompanyId(externalUserID, userID, corpId);
         QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactUserIdAndExternalIdAndCompanyId(externalUserID, userID, corpId);
         if (qwExternalContact != null) {
         if (qwExternalContact != null) {

+ 1 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwTagServiceImpl.java

@@ -74,6 +74,7 @@ public class QwTagServiceImpl implements IQwTagService
         QwTag qwTag = new QwTag();
         QwTag qwTag = new QwTag();
         qwTag.setName(tagParam.getName());
         qwTag.setName(tagParam.getName());
         qwTag.setCorpId(tagParam.getCorpId());
         qwTag.setCorpId(tagParam.getCorpId());
+        qwTag.setCompanyId(tagParam.getCompanyId());
         List<QwTagVO> qwTags = qwTagMapper.selectQwTagListVO(qwTag);
         List<QwTagVO> qwTags = qwTagMapper.selectQwTagListVO(qwTag);
 
 
         // Step 2: 提取 GroupId 列表(去重)
         // Step 2: 提取 GroupId 列表(去重)

+ 64 - 8
fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java

@@ -183,6 +183,44 @@ public class QwWatchLogServiceImpl extends ServiceImpl<QwWatchLogMapper, QwWatch
         }
         }
         return vos;
         return vos;
     }
     }
+    @Override
+    public List<QwWatchLogStatisticsListVO> selectQwWatchLogStatisticsListVOExportExcludeTransfer(FsCourseWatchLogListParam param) {
+        List<Long> deptIds = param.getDeptIds();
+        if (deptIds !=null && !deptIds.isEmpty()){
+            List<CompanyDept> companyDeptList  = companyDeptMapper.selectCompanyDeptByIds(deptIds);
+            if (companyDeptList!=null && !companyDeptList.isEmpty()){
+                for (CompanyDept c : companyDeptList) {
+                    if (c.getParentId() == 0L) {
+                        param.setDeptId(null);
+                        param.setDeptIds(null);
+                        break;
+                    }
+                }
+            }
+        } else {
+            CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());
+            if (ObjectUtils.isNotEmpty(companyDept)&&companyDept.getParentId()==0L){
+                param.setDeptId(null);
+            }
+        }
+
+
+        if (param.getCompanyUserId()!=null){
+            param.setIds(companyUserMapper.selectQwUserIdsByCompany(param.getCompanyUserId()));
+        }
+//        List<QwWatchLogStatisticsListVO> vos = qwWatchLogMapper.selectQwExtCountByDayAndOther(param);
+        List<QwWatchLogStatisticsListVO> vos = qwWatchLogMapper.selectQwExtCountByDayAndOtherExcludeTransfer(param);
+        for (QwWatchLogStatisticsListVO vo : vos) {
+            Long id = vo.getId();
+            Date createTime = vo.getCreateTime();
+            QwWatchLogStatisticsListVO stat = qwWatchLogMapper.selectQwWatchLogByQwUserId(id, createTime);
+            vo.setD1Online(stat.getD1Online());
+            vo.setD1Over(stat.getD1Over());
+            vo.setFirstOnline(stat.getFirstOnline());
+            vo.setFirstOver(stat.getFirstOver());
+        }
+        return vos;
+    }
 
 
     @Override
     @Override
     public TableDataInfo selectQwWatchLogStatisticsListVO(QwWatchLogStatisticsListParam param) {
     public TableDataInfo selectQwWatchLogStatisticsListVO(QwWatchLogStatisticsListParam param) {
@@ -246,14 +284,32 @@ public class QwWatchLogServiceImpl extends ServiceImpl<QwWatchLogMapper, QwWatch
      */
      */
     @Override
     @Override
     public TableDataInfo selectQwWatchLogStatisticsListVOExcludeTransfer(QwWatchLogStatisticsListParam param){
     public TableDataInfo selectQwWatchLogStatisticsListVOExcludeTransfer(QwWatchLogStatisticsListParam param){
-        CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());
-        if (ObjectUtils.isNotEmpty(companyDept)&&companyDept.getParentId()==0L){
-            param.setDeptId(null);
-        }else {
-            //通过部门id去找到所有子部门id
-            List<Long> currentDeptIdDownTreeIds = companyDeptMapper.getCurrentDeptIdDownTreeIds(param.getDeptId());
-            param.setFilterDeptIds(currentDeptIdDownTreeIds);
-            param.setDeptId(null);
+//        CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());
+//        if (ObjectUtils.isNotEmpty(companyDept)&&companyDept.getParentId()==0L){
+//            param.setDeptId(null);
+//        }else {
+//            //通过部门id去找到所有子部门id
+//            List<Long> currentDeptIdDownTreeIds = companyDeptMapper.getCurrentDeptIdDownTreeIds(param.getDeptId());
+//            param.setFilterDeptIds(currentDeptIdDownTreeIds);
+//            param.setDeptId(null);
+//        }
+        List<Long> deptIds = param.getDeptIds();
+        if (deptIds !=null && !deptIds.isEmpty()){
+            List<CompanyDept> companyDeptList  = companyDeptMapper.selectCompanyDeptByIds(deptIds);
+            if (companyDeptList!=null && !companyDeptList.isEmpty()){
+                for (CompanyDept c : companyDeptList) {
+                    if (c.getParentId() == 0L) {
+                        param.setDeptId(null);
+                        param.setDeptIds(null);
+                        break;
+                    }
+                }
+            }
+        } else {
+            CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());
+            if (ObjectUtils.isNotEmpty(companyDept)&&companyDept.getParentId()==0L){
+                param.setDeptId(null);
+            }
         }
         }
         TableDataInfo rspData = new TableDataInfo();
         TableDataInfo rspData = new TableDataInfo();
         rspData.setCode(HttpStatus.SUCCESS);
         rspData.setCode(HttpStatus.SUCCESS);

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

@@ -28,5 +28,8 @@ public class QwTagVO {
      */
      */
     private Integer tagFrom;
     private Integer tagFrom;
 
 
+    /** 主体名称 */
+    private String corpName;
+
 
 
 }
 }

+ 22 - 6
fs-service/src/main/java/com/fs/statis/mapper/ConsumptionBalanceMapper.java

@@ -100,12 +100,28 @@ public interface ConsumptionBalanceMapper {
      */
      */
     List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param);
     List<DeaMemberTopTenDTO> deaMemberTopTen(AnalysisPreviewQueryDTO param);
 
 
-    /**
-     * 课程观看TOP10
-     * @param param 请求参数
-     * @return TOP10
-     */
-    List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param);
+    // 按观看人数排序(只查观看数据)
+    List<CourseStatsDTO> watchCourseTopTenByWatch(AnalysisPreviewQueryDTO param);
+
+    // 按完成人数排序(只查观看数据)
+    List<CourseStatsDTO> watchCourseTopTenByComplete(AnalysisPreviewQueryDTO param);
+
+    // 按答题人数排序(需要关联答题表)
+    List<CourseStatsDTO> watchCourseTopTenByAnswer(AnalysisPreviewQueryDTO param);
+
+    // 按正确人数排序(需要关联答题表)
+    List<CourseStatsDTO> watchCourseTopTenByCorrect(AnalysisPreviewQueryDTO param);
+
+    // 批量补充答题数据
+    List<CourseStatsDTO> getAnswerStatsByCourseIds(@Param("courseIds") List<Long> courseIds, @Param("param") AnalysisPreviewQueryDTO param);
+
+
+//    /**
+//     * 课程观看TOP10
+//     * @param param 请求参数
+//     * @return TOP10
+//     */
+//    List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param);
 
 
     /**
     /**
      * 奖励金额TOP10
      * 奖励金额TOP10

+ 81 - 4
fs-service/src/main/java/com/fs/statis/service/impl/StatisticsCompanyServiceImpl.java

@@ -40,6 +40,8 @@ import java.time.format.DateTimeFormatter;
 import java.time.temporal.TemporalAdjusters;
 import java.time.temporal.TemporalAdjusters;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 import static com.fs.statis.StatisticsRedisConstant.*;
 import static com.fs.statis.StatisticsRedisConstant.*;
@@ -983,14 +985,89 @@ public class StatisticsCompanyServiceImpl implements IStatisticsCompanyService {
 
 
     @Override
     @Override
     public List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param) {
     public List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param) {
-        List<CourseStatsDTO> courseStatsDTOS = consumptionBalanceMapper.watchCourseTopTen(param);
-        for (CourseStatsDTO courseStatsDTO : courseStatsDTOS) {
+//        List<CourseStatsDTO> courseStatsDTOS = consumptionBalanceMapper.watchCourseTopTen(param);
+//        for (CourseStatsDTO courseStatsDTO : courseStatsDTOS) {
+//            String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
+//            if(StringUtils.isNotBlank(courseName)){
+//                courseStatsDTO.setCourseName(courseName);
+//            }
+//        }
+//        return courseStatsDTOS;
+        //2025.11.07 优化
+        // 1. 根据统计类型获取基础数据
+        List<CourseStatsDTO> result = getBaseCourseStats(param);
+
+        // 2. 如果查询的是观看或完成数据,需要补充答题数据
+        if (param.getStatisticalType() == 0 || param.getStatisticalType() == 1) {
+            supplementAnswerData(result, param);
+        }
+
+        // 3. 设置课程名称
+        setCourseNames(result);
+
+        return result;
+    }
+
+    /**
+     * 获取基础课程统计数据
+     */
+    private List<CourseStatsDTO> getBaseCourseStats(AnalysisPreviewQueryDTO param) {
+        Integer statisticalType = param.getStatisticalType();
+        switch (statisticalType) {
+            case 0: // 观看人数
+                return consumptionBalanceMapper.watchCourseTopTenByWatch(param);
+            case 1: // 完成人数
+                return consumptionBalanceMapper.watchCourseTopTenByComplete(param);
+            case 2: // 答题人数
+                return consumptionBalanceMapper.watchCourseTopTenByAnswer(param);
+            case 3: // 正确人数
+                return consumptionBalanceMapper.watchCourseTopTenByCorrect(param);
+            default:
+                return consumptionBalanceMapper.watchCourseTopTenByWatch(param);
+        }
+    }
+
+    /**
+     * 补充答题数据(针对观看和完成统计)
+     */
+    private void supplementAnswerData(List<CourseStatsDTO> result, AnalysisPreviewQueryDTO param) {
+        if (result == null || result.isEmpty()) {
+            return;
+        }
+
+        // 提取课程ID列表
+        List<Long> courseIds = result.stream()
+                .map(CourseStatsDTO::getCourseId)
+                .collect(Collectors.toList());
+
+        // 批量查询答题数据
+        List<CourseStatsDTO> answerStats = consumptionBalanceMapper.getAnswerStatsByCourseIds(courseIds, param);
+
+        // 构建课程ID到答题数据的映射
+        Map<Long, CourseStatsDTO> answerMap = answerStats.stream()
+                .collect(Collectors.toMap(CourseStatsDTO::getCourseId, Function.identity()));
+
+        // 合并答题数据到结果中
+        for (CourseStatsDTO dto : result) {
+            CourseStatsDTO answerStat = answerMap.get(dto.getCourseId());
+            if (answerStat != null) {
+                dto.setAnswerUserCount(answerStat.getAnswerUserCount());
+                dto.setCorrectUserCount(answerStat.getCorrectUserCount());
+            }
+            // 如果没有答题数据,保持原来的0值
+        }
+    }
+
+    /**
+     * 设置课程名称
+     */
+    private void setCourseNames(List<CourseStatsDTO> result) {
+        for (CourseStatsDTO courseStatsDTO : result) {
             String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
             String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
-            if(StringUtils.isNotBlank(courseName)){
+            if (StringUtils.isNotBlank(courseName)) {
                 courseStatsDTO.setCourseName(courseName);
                 courseStatsDTO.setCourseName(courseName);
             }
             }
         }
         }
-        return courseStatsDTOS;
     }
     }
 
 
     @Override
     @Override

+ 79 - 4
fs-service/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java

@@ -46,6 +46,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 import com.fs.statistics.dto.WatchCourseStatisticsDTO;
 import com.fs.statistics.dto.WatchCourseStatisticsDTO;
 
 
@@ -897,14 +898,88 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
 
     @Override
     @Override
     public List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param) {
     public List<CourseStatsDTO> watchCourseTopTen(AnalysisPreviewQueryDTO param) {
-        List<CourseStatsDTO> courseStatsDTOS = consumptionBalanceMapper.watchCourseTopTen(param);
-        for (CourseStatsDTO courseStatsDTO : courseStatsDTOS) {
+//        List<CourseStatsDTO> courseStatsDTOS = consumptionBalanceMapper.watchCourseTopTen(param);
+//        for (CourseStatsDTO courseStatsDTO : courseStatsDTOS) {
+//            String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
+//            if(StringUtils.isNotBlank(courseName)){
+//                courseStatsDTO.setCourseName(courseName);
+//            }
+//        }
+//        return courseStatsDTOS;
+        // 1. 根据统计类型获取基础数据
+        List<CourseStatsDTO> result = getBaseCourseStats(param);
+
+        // 2. 如果查询的是观看或完成数据,需要补充答题数据
+        if (param.getStatisticalType() == 0 || param.getStatisticalType() == 1) {
+            supplementAnswerData(result, param);
+        }
+
+        // 3. 设置课程名称
+        setCourseNames(result);
+
+        return result;
+    }
+
+    /**
+     * 获取基础课程统计数据
+     */
+    private List<CourseStatsDTO> getBaseCourseStats(AnalysisPreviewQueryDTO param) {
+        Integer statisticalType = param.getStatisticalType();
+        switch (statisticalType) {
+            case 0: // 观看人数
+                return consumptionBalanceMapper.watchCourseTopTenByWatch(param);
+            case 1: // 完成人数
+                return consumptionBalanceMapper.watchCourseTopTenByComplete(param);
+            case 2: // 答题人数
+                return consumptionBalanceMapper.watchCourseTopTenByAnswer(param);
+            case 3: // 正确人数
+                return consumptionBalanceMapper.watchCourseTopTenByCorrect(param);
+            default:
+                return consumptionBalanceMapper.watchCourseTopTenByWatch(param);
+        }
+    }
+
+    /**
+     * 补充答题数据(针对观看和完成统计)
+     */
+    private void supplementAnswerData(List<CourseStatsDTO> result, AnalysisPreviewQueryDTO param) {
+        if (result == null || result.isEmpty()) {
+            return;
+        }
+
+        // 提取课程ID列表
+        List<Long> courseIds = result.stream()
+                .map(CourseStatsDTO::getCourseId)
+                .collect(Collectors.toList());
+
+        // 批量查询答题数据
+        List<CourseStatsDTO> answerStats = consumptionBalanceMapper.getAnswerStatsByCourseIds(courseIds, param);
+
+        // 构建课程ID到答题数据的映射
+        Map<Long, CourseStatsDTO> answerMap = answerStats.stream()
+                .collect(Collectors.toMap(CourseStatsDTO::getCourseId, Function.identity()));
+
+        // 合并答题数据到结果中
+        for (CourseStatsDTO dto : result) {
+            CourseStatsDTO answerStat = answerMap.get(dto.getCourseId());
+            if (answerStat != null) {
+                dto.setAnswerUserCount(answerStat.getAnswerUserCount());
+                dto.setCorrectUserCount(answerStat.getCorrectUserCount());
+            }
+            // 如果没有答题数据,保持原来的0值
+        }
+    }
+
+    /**
+     * 设置课程名称
+     */
+    private void setCourseNames(List<CourseStatsDTO> result) {
+        for (CourseStatsDTO courseStatsDTO : result) {
             String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
             String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(courseStatsDTO.getCourseId());
-            if(StringUtils.isNotBlank(courseName)){
+            if (StringUtils.isNotBlank(courseName)) {
                 courseStatsDTO.setCourseName(courseName);
                 courseStatsDTO.setCourseName(courseName);
             }
             }
         }
         }
-        return courseStatsDTOS;
     }
     }
 
 
     @Override
     @Override

+ 16 - 10
fs-service/src/main/java/com/fs/tag/domain/FsTagUpdateQueue.java

@@ -1,5 +1,6 @@
 package com.fs.tag.domain;
 package com.fs.tag.domain;
 
 
+import com.fs.common.annotation.Excel;
 import lombok.Data;
 import lombok.Data;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
@@ -27,11 +28,6 @@ public class FsTagUpdateQueue {
     /** 课程ID */
     /** 课程ID */
     private Long courseId;
     private Long courseId;
 
 
-    /** 标签id */
-    private String tagId;
-
-    /** 标签名称 */
-    private String tagName;
 
 
     /** 操作类型(0 ADD 1 REMOVE) 默认0 */
     /** 操作类型(0 ADD 1 REMOVE) 默认0 */
     private Integer operationType;
     private Integer operationType;
@@ -96,18 +92,28 @@ public class FsTagUpdateQueue {
      */
      */
     private String watchedTagId;
     private String watchedTagId;
     /**
     /**
-     * 标签组ID
+     * 看课标签组ID
      */
      */
-    private String tagGroupId;
+    private String watchingTgGroupId;
 
 
     /**
     /**
-     * 标签组表中的ID
+     * 完课标签组ID
      */
      */
-    private Long tgId;
+    private String watchedTgGroupId;
+    /** 看课中-标签组ID */
+    private Long watchingGroupId;
+
     /**
     /**
-     * 看课标签 表中的ID
+     * 完课标签组-标签组表中的ID
+     */
+    private Long watchedTagGroupId;
+    /**
+     * 看课中标签-表中的ID
      */
      */
     private Long watchingTgId;
     private Long watchingTgId;
+
+    /** 完课-标签组ID */
+    private Long watchedGroupId;
     /**
     /**
      * 完课标签 表中的ID
      * 完课标签 表中的ID
      */
      */

+ 96 - 0
fs-service/src/main/java/com/fs/tag/domain/FsVideoCourseTag.java

@@ -0,0 +1,96 @@
+package com.fs.tag.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 视频小节看课标签关联对象 fs_video_course_tag
+ *
+ * @author fs
+ * @date 2025-11-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsVideoCourseTag extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** 企微公司 */
+    @Excel(name = "企微公司")
+    private String corpId;
+    /**
+     * 企微公司名
+     */
+    private String qwCompanyName;
+    /** 视频ID */
+    @Excel(name = "视频ID")
+    private Long videoId;
+    /**
+     * 小节名称
+     */
+    private String videoName;
+
+    /** 看课中-标签组ID */
+    @Excel(name = "看课中-标签组ID")
+    private Long watchingGroupId;
+    /**
+     * 看课中-标签组名
+     */
+    private String watchingGroupName;
+
+    /** 完课-标签组ID */
+    @Excel(name = "完课-标签组ID")
+    private Long watchedGroupId;
+    /**
+     * 完课-标签组名
+     */
+    private String watchedGroupName;
+
+    /** 看课中-标签组ID */
+    @Excel(name = "看课中-标签组ID")
+    private Long watchingTgId;
+    /**
+     * 看课中标签名
+     */
+    private String watchingTgName;
+
+    /** 完课-标签组ID */
+    @Excel(name = "完课-标签组ID")
+    private Long watchedTgId;
+
+    /**
+     * 完课-标签名
+     */
+    private String watchedTgName;
+
+    /** 看课中-标签组ID */
+    @Excel(name = "看课中-标签组ID")
+    private String watchingGroupTgId;
+
+
+    /** 完课-标签组ID */
+    @Excel(name = "完课-标签组ID")
+    private String watchedGroupTgId;
+
+
+    /** 看课中-标签ID */
+    @Excel(name = "看课中-标签ID")
+    private String watchingTagId;
+
+    /** 完课-标签组 */
+    @Excel(name = "完课-标签组")
+    private String watchedTagId;
+
+
+    /**
+     * corp_id, '_', video_id
+     */
+    @TableField(exist = false)
+    private String compositeKey;
+
+}

+ 5 - 104
fs-service/src/main/java/com/fs/tag/mapper/FsTagUpdateQueueMapper.java

@@ -11,123 +11,26 @@ import java.util.List;
 @Mapper
 @Mapper
 public interface FsTagUpdateQueueMapper {
 public interface FsTagUpdateQueueMapper {
 
 
-    @Select("select * from fs_tag_update_queue where retry_count < 3 and status in (0,3) and (next_execute_time < now() or next_execute_time is null) limit 500")
+    @Select("select * from fs_tag_update_queue where retry_count < 3 and status in (0,3) and (next_execute_time < now() or next_execute_time is null) limit 1000")
     List<FsTagUpdateQueue> selectPending();
     List<FsTagUpdateQueue> selectPending();
 
 
-    @Select("<script>" +
-            "SELECT * FROM fs_tag_update_queue " +
-            "<where>" +
-            "<if test='id != null'> AND id = #{id} </if>" +
-            "<if test='courseLogId != null'> AND course_log_id = #{courseLogId} </if>" +
-            "<if test='courseId != null'> AND course_id = #{courseId} </if>" +
-            "<if test='tagId != null'> AND tag_id = #{tagId} </if>" +
-            "<if test='tagName != null'> AND tag_name = #{tagName} </if>" +
-            "<if test='operationType != null'> AND operation_type = #{operationType} </if>" +
-            "<if test='videoId != null'> AND video_id = #{videoId} </if>" +
-            "<if test='status != null'> AND status = #{status} </if>" +
-            "<if test='retryCount != null'> AND retry_count = #{retryCount} </if>" +
-            "<if test='corpId != null'> AND corp_id = #{corpId} </if>" +
-            "<if test='qwUserId != null'> AND qw_user_id = #{qwUserId} </if>" +
-            "</where>" +
-            "</script>")
-    List<FsTagUpdateQueue> selectByConditions(FsTagUpdateQueue condition);
-
-    @Insert("<script>" +
-            "INSERT INTO fs_tag_update_queue " +
-            "(course_log_id, course_id, tag_id, tag_name, operation_type, video_id, status, retry_count, corp_id, qw_user_id, fail_msg, payload, response, create_time, update_time, update_by, create_by,log_type) " +
-            "VALUES " +
-            "<trim prefix='(' suffix=')' suffixOverrides=','>" +
-            "<if test='courseLogId != null'>course_log_id,</if>" +
-            "<if test='courseId != null'>course_id,</if>" +
-            "<if test='tagId != null'>tag_id,</if>" +
-            "<if test='tagName != null'>tag_name,</if>" +
-            "<if test='operationType != null'>operation_type,</if>" +
-            "<if test='videoId != null'>video_id,</if>" +
-            "<if test='status != null'>status,</if>" +
-            "<if test='retryCount != null'>retry_count,</if>" +
-            "<if test='corpId != null'>corp_id,</if>" +
-            "<if test='qwUserId != null'>qw_user_id,</if>" +
-            "<if test='qwExternalContactId != null'>qw_external_contact_id,</if>" +
-            "<if test='failMsg != null'>fail_msg,</if>" +
-            "<if test='payload != null'>payload,</if>" +
-            "<if test='response != null'>response,</if>" +
-            "<if test='createTime != null'>create_time,</if>" +
-            "<if test='updateTime != null'>update_time,</if>" +
-            "<if test='updateBy != null'>update_by,</if>" +
-            "<if test='createBy != null'>create_by,</if>" +
-            "<if test='logType != null'>log_type,</if>" +
-            "</trim>" +
-            "<trim prefix='VALUES (' suffix=')' suffixOverrides=','>" +
-            "<if test='courseLogId != null'>#{courseLogId},</if>" +
-            "<if test='courseId != null'>#{courseId},</if>" +
-            "<if test='tagId != null'>#{tagId},</if>" +
-            "<if test='tagName != null'>#{tagName},</if>" +
-            "<if test='operationType != null'>#{operationType},</if>" +
-            "<if test='videoId != null'>#{videoId},</if>" +
-            "<if test='status != null'>#{status},</if>" +
-            "<if test='retryCount != null'>#{retryCount},</if>" +
-            "<if test='corpId != null'>#{corpId},</if>" +
-            "<if test='qwExternalContactId != null'>#{qwExternalContactId},</if>" +
-            "<if test='qwUserId != null'>#{qwUserId},</if>" +
-            "<if test='failMsg != null'>#{failMsg},</if>" +
-            "<if test='payload != null'>#{payload},</if>" +
-            "<if test='response != null'>#{response},</if>" +
-            "<if test='createTime != null'>#{createTime},</if>" +
-            "<if test='updateTime != null'>#{updateTime},</if>" +
-            "<if test='updateBy != null'>#{updateBy},</if>" +
-            "<if test='createBy != null'>#{createBy},</if>" +
-            "<if test='log_type != null'>#{logType},</if>" +
-            "</trim>" +
-            "</script>")
-    @Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
-    int insertSelective(FsTagUpdateQueue record);
-
-
 
 
     @Insert("<script>" +
     @Insert("<script>" +
             "INSERT IGNORE INTO fs_tag_update_queue (" +
             "INSERT IGNORE INTO fs_tag_update_queue (" +
-            "course_log_id, is_first, course_id, tag_id, tag_name, operation_type, video_id, status, retry_count, " +
-            "corp_id, qw_user_id, qw_external_contact_id, fail_msg, payload, response, create_time, update_time, update_by, create_by, log_type,tg_id,watching_tg_id,watched_tg_id,watching_tag_id,watched_tag_id,tag_group_id" +
+            "course_log_id, is_first, course_id,  operation_type, video_id, status, retry_count, " +
+            "corp_id, qw_user_id, qw_external_contact_id, fail_msg, payload, response, create_time, update_time, update_by, create_by, log_type,watching_tg_id,watched_tg_id,watching_tag_id,watched_tag_id,watching_tg_group_id,watched_tg_group_id" +
             ") VALUES " +
             ") VALUES " +
             "<foreach collection='list' item='item' separator=','>" +
             "<foreach collection='list' item='item' separator=','>" +
             "(" +
             "(" +
-            "#{item.courseLogId}, #{item.isFirst}, #{item.courseId}, #{item.tagId}, #{item.tagName}, #{item.operationType}, #{item.videoId}, #{item.status}, #{item.retryCount}, " +
+            "#{item.courseLogId}, #{item.isFirst}, #{item.courseId},  #{item.operationType}, #{item.videoId}, #{item.status}, #{item.retryCount}, " +
             "#{item.corpId}, #{item.qwUserId}, #{item.qwExternalContactId}, #{item.failMsg}, #{item.payload}, #{item.response}, #{item.createTime}," +
             "#{item.corpId}, #{item.qwUserId}, #{item.qwExternalContactId}, #{item.failMsg}, #{item.payload}, #{item.response}, #{item.createTime}," +
-            " #{item.updateTime}, #{item.updateBy}, #{item.createBy}, #{item.logType},#{item.tgId},#{item.watchingTgId},#{item.watchedTgId},#{item.watchingTagId},#{item.watchedTagId},#{item.tagGroupId}" +
+            " #{item.updateTime}, #{item.updateBy}, #{item.createBy}, #{item.logType},#{item.watchingTgId},#{item.watchedTgId},#{item.watchingTagId},#{item.watchedTagId},#{item.watchingTgGroupId},#{item.watchedTgGroupId}" +
             ")" +
             ")" +
             "</foreach>" +
             "</foreach>" +
             "</script>")
             "</script>")
     int batchInsert(@Param("list") List<FsTagUpdateQueue> list);
     int batchInsert(@Param("list") List<FsTagUpdateQueue> list);
 
 
 
 
-    @Update("<script>" +
-            "UPDATE fs_tag_update_queue " +
-            "<set>" +
-            "<if test='courseLogId != null'>course_log_id = #{courseLogId},</if>" +
-            "<if test='courseId != null'>course_id = #{courseId},</if>" +
-            "<if test='tagId != null'>tag_id = #{tagId},</if>" +
-            "<if test='tagName != null'>tag_name = #{tagName},</if>" +
-            "<if test='operationType != null'>operation_type = #{operationType},</if>" +
-            "<if test='videoId != null'>video_id = #{videoId},</if>" +
-            "<if test='status != null'>status = #{status},</if>" +
-            "<if test='retryCount != null'>retry_count = #{retryCount},</if>" +
-            "<if test='corpId != null'>corp_id = #{corpId},</if>" +
-            "<if test='qwUserId != null'>qw_user_id = #{qwUserId},</if>" +
-            "<if test='failMsg != null'>fail_msg = #{failMsg},</if>" +
-            "<if test='payload != null'>payload = #{payload},</if>" +
-            "<if test='response != null'>response = #{response},</if>" +
-            "<if test='createTime != null'>create_time = #{createTime},</if>" +
-            "<if test='updateTime != null'>update_time = #{updateTime},</if>" +
-            "<if test='updateBy != null'>update_by = #{updateBy},</if>" +
-            "<if test='createBy != null'>create_by = #{createBy},</if>" +
-            "<if test='logType != null'>log_type = #{logType},</if>" +
-            "</set> " +
-            "WHERE id = #{id}" +
-            "</script>")
-    int updateSelective(FsTagUpdateQueue record);
-
-
-
     @Update("<script>" +
     @Update("<script>" +
             "<foreach collection='list' item='item' separator=';'>" +
             "<foreach collection='list' item='item' separator=';'>" +
             "UPDATE fs_tag_update_queue" +
             "UPDATE fs_tag_update_queue" +
@@ -135,8 +38,6 @@ public interface FsTagUpdateQueueMapper {
             "<if test='item.courseLogId != null'>course_log_id = #{item.courseLogId},</if>" +
             "<if test='item.courseLogId != null'>course_log_id = #{item.courseLogId},</if>" +
             "<if test='item.isFirst != null'>is_first = #{item.isFirst},</if>" +
             "<if test='item.isFirst != null'>is_first = #{item.isFirst},</if>" +
             "<if test='item.courseId != null'>course_id = #{item.courseId},</if>" +
             "<if test='item.courseId != null'>course_id = #{item.courseId},</if>" +
-            "<if test='item.tagId != null'>tag_id = #{item.tagId},</if>" +
-            "<if test='item.tagName != null'>tag_name = #{item.tagName},</if>" +
             "<if test='item.operationType != null'>operation_type = #{item.operationType},</if>" +
             "<if test='item.operationType != null'>operation_type = #{item.operationType},</if>" +
             "<if test='item.videoId != null'>video_id = #{item.videoId},</if>" +
             "<if test='item.videoId != null'>video_id = #{item.videoId},</if>" +
             "<if test='item.status != null'>status = #{item.status},</if>" +
             "<if test='item.status != null'>status = #{item.status},</if>" +

+ 73 - 0
fs-service/src/main/java/com/fs/tag/mapper/FsVideoCourseTagMapper.java

@@ -0,0 +1,73 @@
+package com.fs.tag.mapper;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.tag.domain.FsVideoCourseTag;
+import org.apache.ibatis.annotations.MapKey;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * 视频小节看课标签关联Mapper接口
+ *
+ * @author fs
+ * @date 2025-11-05
+ */
+public interface FsVideoCourseTagMapper extends BaseMapper<FsVideoCourseTag>{
+    /**
+     * 查询视频小节看课标签关联
+     *
+     * @param id 视频小节看课标签关联主键
+     * @return 视频小节看课标签关联
+     */
+    FsVideoCourseTag selectFsVideoCourseTagById(Long id);
+
+    /**
+     * 查询视频小节看课标签关联列表
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 视频小节看课标签关联集合
+     */
+    List<FsVideoCourseTag> selectFsVideoCourseTagList(FsVideoCourseTag fsVideoCourseTag);
+
+    /**
+     * 新增视频小节看课标签关联
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 结果
+     */
+    int insertFsVideoCourseTag(FsVideoCourseTag fsVideoCourseTag);
+
+    /**
+     * 修改视频小节看课标签关联
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 结果
+     */
+    int updateFsVideoCourseTag(FsVideoCourseTag fsVideoCourseTag);
+
+    /**
+     * 删除视频小节看课标签关联
+     *
+     * @param id 视频小节看课标签关联主键
+     * @return 结果
+     */
+    int deleteFsVideoCourseTagById(Long id);
+
+    /**
+     * 批量删除视频小节看课标签关联
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsVideoCourseTagByIds(Long[] ids);
+
+    @Select("select * from fs_video_course_tag where corp_id=#{corpId} and video_id=#{videoId}")
+    FsVideoCourseTag selectFsVideoCourseTagByCorpIdAndVideoId(@Param("corpId") String corpId, @Param("videoId") Long videoId);
+
+    @Select("SELECT id,corp_id,video_id,watching_group_id,watched_group_id,watching_tg_id,watched_tg_id,watching_group_tag_id as watching_group_tg_id,watched_group_tag_id as watched_group_tg_id,watching_tag_id,watched_tag_id, CONCAT(corp_id, '_', video_id) AS composite_key FROM fs_video_course_tag")
+    @MapKey("compositeKey")
+    Map<String,FsVideoCourseTag> selectAll();
+}

+ 61 - 0
fs-service/src/main/java/com/fs/tag/service/IFsVideoCourseTagService.java

@@ -0,0 +1,61 @@
+package com.fs.tag.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.tag.domain.FsVideoCourseTag;
+
+/**
+ * 视频小节看课标签关联Service接口
+ *
+ * @author fs
+ * @date 2025-11-05
+ */
+public interface IFsVideoCourseTagService extends IService<FsVideoCourseTag>{
+    /**
+     * 查询视频小节看课标签关联
+     *
+     * @param id 视频小节看课标签关联主键
+     * @return 视频小节看课标签关联
+     */
+    FsVideoCourseTag selectFsVideoCourseTagById(Long id);
+
+    /**
+     * 查询视频小节看课标签关联列表
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 视频小节看课标签关联集合
+     */
+    List<FsVideoCourseTag> selectFsVideoCourseTagList(FsVideoCourseTag fsVideoCourseTag);
+
+    /**
+     * 新增视频小节看课标签关联
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 结果
+     */
+    int insertFsVideoCourseTag(FsVideoCourseTag fsVideoCourseTag);
+
+    /**
+     * 修改视频小节看课标签关联
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 结果
+     */
+    int updateFsVideoCourseTag(FsVideoCourseTag fsVideoCourseTag);
+
+    /**
+     * 批量删除视频小节看课标签关联
+     *
+     * @param ids 需要删除的视频小节看课标签关联主键集合
+     * @return 结果
+     */
+    int deleteFsVideoCourseTagByIds(Long[] ids);
+
+    /**
+     * 删除视频小节看课标签关联信息
+     *
+     * @param id 视频小节看课标签关联主键
+     * @return 结果
+     */
+    int deleteFsVideoCourseTagById(Long id);
+}

+ 76 - 41
fs-service/src/main/java/com/fs/tag/service/impl/FsTagUpdateServiceImpl.java

@@ -2,6 +2,7 @@ package com.fs.tag.service.impl;
 
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.domain.FsCourseWatchLog;
@@ -28,7 +29,9 @@ import com.fs.qwApi.param.QwAddTagParam;
 import com.fs.qwApi.param.QwEditUserTagParam;
 import com.fs.qwApi.param.QwEditUserTagParam;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.tag.domain.FsTagUpdateQueue;
 import com.fs.tag.domain.FsTagUpdateQueue;
+import com.fs.tag.domain.FsVideoCourseTag;
 import com.fs.tag.mapper.FsTagUpdateQueueMapper;
 import com.fs.tag.mapper.FsTagUpdateQueueMapper;
+import com.fs.tag.mapper.FsVideoCourseTagMapper;
 import com.fs.tag.service.FsTagUpdateService;
 import com.fs.tag.service.FsTagUpdateService;
 import com.google.common.util.concurrent.RateLimiter;
 import com.google.common.util.concurrent.RateLimiter;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
@@ -65,17 +68,7 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
     private QwApiService qwApiService;
     private QwApiService qwApiService;
 
 
     @Autowired
     @Autowired
-    private QwTagMapper qwTagMapper;
-
-    @Autowired
-    private QwTagGroupMapper qwTagGroupMapper;
-
-    @Autowired
-    private IQwTagGroupService qwTagGroupService;
-
-    @Autowired
-    private FsUserCourseMapper fsUserCourseMapper;
-
+    private FsVideoCourseTagMapper fsVideoCourseTagMapper;
     @Autowired
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
     private QwExternalContactMapper qwExternalContactMapper;
 
 
@@ -84,10 +77,6 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
 
 
     @Value("${tag.rate.limit:30}")
     @Value("${tag.rate.limit:30}")
     private Integer RATE_LIMIT_NUM;
     private Integer RATE_LIMIT_NUM;
-    /**
-     * 标签组最大数量
-     */
-    private static final Integer TAG_MAX_NUM = 100;
 
 
     /**
     /**
      * 接口限流
      * 接口限流
@@ -115,6 +104,8 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
         Map<Long, FsUserCourseVideo> courseVideoMap = fsUserCourseVideoMapper.selectAllMap();
         Map<Long, FsUserCourseVideo> courseVideoMap = fsUserCourseVideoMapper.selectAllMap();
         // 用户(这里用户用的是企微外部联系人ID)+videoId+status 唯一
         // 用户(这里用户用的是企微外部联系人ID)+videoId+status 唯一
 
 
+        Map<String, FsVideoCourseTag> fsVideoCourseTagMap = fsVideoCourseTagMapper.selectAll();
+
         // 先导课看课记录
         // 先导课看课记录
         List<FsTagUpdateQueue> batchData = new ArrayList<>();
         List<FsTagUpdateQueue> batchData = new ArrayList<>();
         for (FsCourseWatchLog item : logs) {
         for (FsCourseWatchLog item : logs) {
@@ -122,8 +113,6 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
             task.setCourseId(item.getCourseId());
             task.setCourseId(item.getCourseId());
             task.setVideoId(item.getVideoId());
             task.setVideoId(item.getVideoId());
             task.setCourseLogId(item.getLogId());
             task.setCourseLogId(item.getLogId());
-            task.setTagId(null);
-            task.setTagName(null);
 
 
             task.setLogType(0);
             task.setLogType(0);
             task.setOperationType(0);
             task.setOperationType(0);
@@ -150,12 +139,31 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
                 batchData.add(task);
                 batchData.add(task);
                 continue;
                 continue;
             }
             }
-            task.setTagGroupId(fsUserCourseVideo.getTagGroupId());
-            task.setTgId(fsUserCourseVideo.getTgId());
-            task.setWatchingTagId(fsUserCourseVideo.getWatchingTagId());
-            task.setWatchedTagId(fsUserCourseVideo.getWatchedTagId());
-            task.setWatchingTgId(fsUserCourseVideo.getWatchingTgId());
-            task.setWatchedTgId(fsUserCourseVideo.getWatchedTgId());
+            String compositeKey = String.format("%s_%s", corpId, fsUserCourseVideo.getVideoId());
+
+            FsVideoCourseTag fsVideoCourseTag = fsVideoCourseTagMap.get(compositeKey);
+            if(ObjectUtil.isNull(fsVideoCourseTag)) {
+                log.info("{},对应记录不存在!",compositeKey);
+                continue;
+            }
+            // 看课中-标签ID(表中的)
+            task.setWatchingTgId(fsVideoCourseTag.getWatchingTgId());
+            // 看课中-标签组ID(表中的)
+            task.setWatchingGroupId(fsVideoCourseTag.getWatchingGroupId());
+            // 完课-标签组ID(表中的)
+            task.setWatchedGroupId(fsVideoCourseTag.getWatchedGroupId());
+            // 完课-完课ID(表中的)
+            task.setWatchedTgId(fsVideoCourseTag.getWatchedTgId());
+
+            // 看课中-标签ID(企微)
+            task.setWatchingTagId(fsVideoCourseTag.getWatchingTagId());
+            // 看课中-标签组ID(企微)
+            task.setWatchingTgGroupId(fsVideoCourseTag.getWatchingGroupTgId());
+
+            // 完课-完课标签ID(企微)
+            task.setWatchedTagId(fsVideoCourseTag.getWatchedTagId());
+            // 完课-标签组ID(企微)
+            task.setWatchedTgGroupId(fsVideoCourseTag.getWatchedGroupTgId());
 
 
             if(ObjectUtil.equal(fsUserCourseVideo.getIsFirst(),1)) {
             if(ObjectUtil.equal(fsUserCourseVideo.getIsFirst(),1)) {
                 task.setIsFirst(1);
                 task.setIsFirst(1);
@@ -167,7 +175,9 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
         }
         }
 
 
 
 
-        fsTagUpdateQueueMapper.batchInsert(batchData);
+        if(CollectionUtils.isNotEmpty(batchData)){
+            fsTagUpdateQueueMapper.batchInsert(batchData);
+        }
     }
     }
 
 
 
 
@@ -178,6 +188,7 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
             return;
             return;
         }
         }
         Map<Long, FsUserCourseVideo> courseVideoMap = fsUserCourseVideoMapper.selectAllMap();
         Map<Long, FsUserCourseVideo> courseVideoMap = fsUserCourseVideoMapper.selectAllMap();
+        Map<String, FsVideoCourseTag> fsVideoCourseTagMap = fsVideoCourseTagMapper.selectAll();
 
 
         // 先导课看课记录
         // 先导课看课记录
         List<FsTagUpdateQueue> batchData = new ArrayList<>();
         List<FsTagUpdateQueue> batchData = new ArrayList<>();
@@ -186,8 +197,7 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
             task.setCourseId(item.getCourseId());
             task.setCourseId(item.getCourseId());
             task.setVideoId(item.getVideoId());
             task.setVideoId(item.getVideoId());
             task.setCourseLogId(item.getLogId());
             task.setCourseLogId(item.getLogId());
-            task.setTagId(null);
-            task.setTagName(null);
+
             task.setOperationType(0);
             task.setOperationType(0);
             task.setStatus(0);
             task.setStatus(0);
             task.setRetryCount(0);
             task.setRetryCount(0);
@@ -213,12 +223,31 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
                 batchData.add(task);
                 batchData.add(task);
                 continue;
                 continue;
             }
             }
-            task.setTagGroupId(fsUserCourseVideo.getTagGroupId());
-            task.setTgId(fsUserCourseVideo.getTgId());
-            task.setWatchingTagId(fsUserCourseVideo.getWatchingTagId());
-            task.setWatchedTagId(fsUserCourseVideo.getWatchedTagId());
-            task.setWatchingTgId(fsUserCourseVideo.getWatchingTgId());
-            task.setWatchedTgId(fsUserCourseVideo.getWatchedTgId());
+            String compositeKey = String.format("%s_%s", corpId, fsUserCourseVideo.getVideoId());
+
+            FsVideoCourseTag fsVideoCourseTag = fsVideoCourseTagMap.get(compositeKey);
+            if(ObjectUtil.isNull(fsVideoCourseTag)) {
+                log.info("{},对应记录不存在!",compositeKey);
+                continue;
+            }
+            // 看课中-标签ID(表中的)
+            task.setWatchingTgId(fsVideoCourseTag.getWatchingTgId());
+            // 看课中-标签组ID(表中的)
+            task.setWatchingGroupId(fsVideoCourseTag.getWatchingGroupId());
+            // 完课-标签组ID(表中的)
+            task.setWatchedGroupId(fsVideoCourseTag.getWatchedGroupId());
+            // 完课-完课ID(表中的)
+            task.setWatchedTgId(fsVideoCourseTag.getWatchedTgId());
+
+            // 看课中-标签ID(企微)
+            task.setWatchingTagId(fsVideoCourseTag.getWatchingTagId());
+            // 看课中-标签组ID(企微)
+            task.setWatchingTgGroupId(fsVideoCourseTag.getWatchingGroupTgId());
+
+            // 完课-完课标签ID(企微)
+            task.setWatchedTagId(fsVideoCourseTag.getWatchedTagId());
+            // 完课-标签组ID(企微)
+            task.setWatchedTgGroupId(fsVideoCourseTag.getWatchedGroupTgId());
 
 
             if(ObjectUtil.equal(fsUserCourseVideo.getIsFirst(),1)) {
             if(ObjectUtil.equal(fsUserCourseVideo.getIsFirst(),1)) {
                 task.setIsFirst(1);
                 task.setIsFirst(1);
@@ -230,13 +259,15 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
         }
         }
 
 
 
 
-        fsTagUpdateQueueMapper.batchInsert(batchData);
+        if(CollectionUtils.isNotEmpty(batchData)){
+            fsTagUpdateQueueMapper.batchInsert(batchData);
+        }
     }
     }
 
 
     @Override
     @Override
     public void handleData() {
     public void handleData() {
         List<FsTagUpdateQueue> tasks = fsTagUpdateQueueMapper.selectPending();
         List<FsTagUpdateQueue> tasks = fsTagUpdateQueueMapper.selectPending();
-        if(CollectionUtils.isEmpty(tasks)){
+        if (CollectionUtils.isEmpty(tasks)) {
             log.info("找不到可处理的任务,已跳过!");
             log.info("找不到可处理的任务,已跳过!");
             return;
             return;
         }
         }
@@ -276,7 +307,7 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
             Thread.currentThread().interrupt();
             Thread.currentThread().interrupt();
         }
         }
 
 
-        if(CollectionUtils.isNotEmpty(tasks)){
+        if (CollectionUtils.isNotEmpty(tasks)) {
             fsTagUpdateQueueMapper.batchUpdateSelective(tasks);
             fsTagUpdateQueueMapper.batchUpdateSelective(tasks);
         }
         }
 
 
@@ -288,7 +319,7 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
             QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
             QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
             QwExternalContact qwExternalContact = qwExternalContactMapper
             QwExternalContact qwExternalContact = qwExternalContactMapper
                     .selectQwExternalContactById(fsTagUpdateQueue.getQwExternalContactId());
                     .selectQwExternalContactById(fsTagUpdateQueue.getQwExternalContactId());
-            if(qwExternalContact == null) {
+            if (qwExternalContact == null) {
                 throw new IllegalArgumentException(String.format("企微外部联系人 %s 未找到!", fsTagUpdateQueue.getQwExternalContactId()));
                 throw new IllegalArgumentException(String.format("企微外部联系人 %s 未找到!", fsTagUpdateQueue.getQwExternalContactId()));
             }
             }
             qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
             qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
@@ -297,7 +328,7 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
             rateLimiter.acquire();
             rateLimiter.acquire();
 
 
             // 如果是看课中
             // 如果是看课中
-            if(ObjectUtil.equal(fsTagUpdateQueue.getLogType(),0)){
+            if (ObjectUtil.equal(fsTagUpdateQueue.getLogType(), 0)) {
                 qwEditUserTagParam.setAdd_tag(Collections.singletonList(fsTagUpdateQueue.getWatchingTagId()));
                 qwEditUserTagParam.setAdd_tag(Collections.singletonList(fsTagUpdateQueue.getWatchingTagId()));
             } else {
             } else {
                 // 已完课
                 // 已完课
@@ -305,19 +336,23 @@ public class FsTagUpdateServiceImpl implements FsTagUpdateService {
                 qwEditUserTagParam.setRemove_tag(Collections.singletonList(fsTagUpdateQueue.getWatchingTagId()));
                 qwEditUserTagParam.setRemove_tag(Collections.singletonList(fsTagUpdateQueue.getWatchingTagId()));
             }
             }
 
 
+            if (ObjectUtil.isNull(fsTagUpdateQueue.getCorpId())) {
+                throw new IllegalArgumentException("corpId为空!请检查一下");
+            }
             QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, fsTagUpdateQueue.getCorpId());
             QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, fsTagUpdateQueue.getCorpId());
+            log.info("返回结果: {}", JSON.toJSONString(qwResult));
             fsTagUpdateQueue.setPayload(JSON.toJSONString(qwEditUserTagParam));
             fsTagUpdateQueue.setPayload(JSON.toJSONString(qwEditUserTagParam));
             fsTagUpdateQueue.setResponse(JSON.toJSONString(qwResult));
             fsTagUpdateQueue.setResponse(JSON.toJSONString(qwResult));
             // 打标签成功
             // 打标签成功
-            if(ObjectUtil.equal(qwResult.getErrcode(),0)) {
+            if (ObjectUtil.equal(qwResult.getErrcode(), 0)) {
                 fsTagUpdateQueue.setStatus(2);
                 fsTagUpdateQueue.setStatus(2);
-                fsTagUpdateQueue.setRetryCount(0);
+                fsTagUpdateQueue.setFailMsg("");
             } else {
             } else {
                 throw new RuntimeException(String.format("打标签失败 原因: %s", JSON.toJSONString(qwResult)));
                 throw new RuntimeException(String.format("打标签失败 原因: %s", JSON.toJSONString(qwResult)));
             }
             }
-        } catch (Exception e){
+        } catch (Exception e) {
             fsTagUpdateQueue.setStatus(3);
             fsTagUpdateQueue.setStatus(3);
-            fsTagUpdateQueue.setRetryCount(fsTagUpdateQueue.getRetryCount()+1);
+            fsTagUpdateQueue.setRetryCount(fsTagUpdateQueue.getRetryCount() + 1);
             fsTagUpdateQueue.setFailMsg(ExceptionUtils.getFullStackTrace(e));
             fsTagUpdateQueue.setFailMsg(ExceptionUtils.getFullStackTrace(e));
             fsTagUpdateQueue.setNextExecuteTime(LocalDateTime.now().plusHours(1));
             fsTagUpdateQueue.setNextExecuteTime(LocalDateTime.now().plusHours(1));
         }
         }

+ 145 - 0
fs-service/src/main/java/com/fs/tag/service/impl/FsVideoCourseTagServiceImpl.java

@@ -0,0 +1,145 @@
+package com.fs.tag.service.impl;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.course.vo.FsUserCourseVideoVO;
+import com.fs.qw.domain.QwTag;
+import com.fs.qw.domain.QwTagGroup;
+import com.fs.qw.mapper.QwTagGroupMapper;
+import com.fs.qw.mapper.QwTagMapper;
+import com.fs.tag.domain.FsVideoCourseTag;
+import com.fs.tag.mapper.FsVideoCourseTagMapper;
+import com.fs.tag.service.IFsVideoCourseTagService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+
+/**
+ * 视频小节看课标签关联Service业务层处理
+ *
+ * @author fs
+ * @date 2025-11-05
+ */
+@Slf4j
+@Service
+public class FsVideoCourseTagServiceImpl extends ServiceImpl<FsVideoCourseTagMapper, FsVideoCourseTag> implements IFsVideoCourseTagService {
+
+    /**
+     * 查询视频小节看课标签关联
+     *
+     * @param id 视频小节看课标签关联主键
+     * @return 视频小节看课标签关联
+     */
+    @Override
+    public FsVideoCourseTag selectFsVideoCourseTagById(Long id)
+    {
+        return baseMapper.selectFsVideoCourseTagById(id);
+    }
+
+
+    @Autowired
+    private QwTagGroupMapper qwTagGroupMapper;
+
+    @Autowired
+    private QwTagMapper qwTagMapper;
+    /**
+     * 查询视频小节看课标签关联列表
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 视频小节看课标签关联
+     */
+    @Override
+    public List<FsVideoCourseTag> selectFsVideoCourseTagList(FsVideoCourseTag fsVideoCourseTag)
+    {
+        List<FsVideoCourseTag> fsVideoCourseTags = baseMapper.selectFsVideoCourseTagList(fsVideoCourseTag);
+        for (FsVideoCourseTag item : fsVideoCourseTags) {
+            if(ObjectUtils.isNotNull(item.getWatchingGroupId())){
+                QwTagGroup qwTagGroup = qwTagGroupMapper.selectQwTagGroupById(item.getWatchingGroupId());
+                if(ObjectUtils.isNotNull(qwTagGroup)){
+                    item.setWatchingGroupName(qwTagGroup.getName());
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getWatchedGroupId())){
+                QwTagGroup qwTagGroup = qwTagGroupMapper.selectQwTagGroupById(item.getWatchedGroupId());
+                if(ObjectUtils.isNotNull(qwTagGroup)){
+                    item.setWatchedGroupName(qwTagGroup.getName());
+                }
+            }
+
+            if(ObjectUtils.isNotNull(item.getWatchingTgId())){
+                QwTag qwTag = qwTagMapper.selectQwTagById(item.getWatchingTgId());
+                if(ObjectUtils.isNotNull(qwTag)){
+                    item.setWatchingTgName(qwTag.getName());
+                }
+            }
+
+            if(ObjectUtils.isNotNull(item.getWatchedTgId())) {
+                QwTag qwTag = qwTagMapper.selectQwTagById(item.getWatchedTgId());
+                if(ObjectUtils.isNotNull(qwTag)){
+                    item.setWatchedTgName(qwTag.getName());
+                }
+            }
+        }
+
+        return fsVideoCourseTags;
+    }
+
+    /**
+     * 新增视频小节看课标签关联
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 结果
+     */
+    @Override
+    public int insertFsVideoCourseTag(FsVideoCourseTag fsVideoCourseTag)
+    {
+        try{
+            fsVideoCourseTag.setCreateTime(DateUtils.getNowDate());
+            return baseMapper.insertFsVideoCourseTag(fsVideoCourseTag);
+        }catch (DuplicateKeyException e){
+            log.info("同一主体的小节添加标签不允许重复!");
+            throw new RuntimeException("同一主体的小节添加标签不允许重复!");
+        }
+    }
+
+    /**
+     * 修改视频小节看课标签关联
+     *
+     * @param fsVideoCourseTag 视频小节看课标签关联
+     * @return 结果
+     */
+    @Override
+    public int updateFsVideoCourseTag(FsVideoCourseTag fsVideoCourseTag)
+    {
+        fsVideoCourseTag.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateFsVideoCourseTag(fsVideoCourseTag);
+    }
+
+    /**
+     * 批量删除视频小节看课标签关联
+     *
+     * @param ids 需要删除的视频小节看课标签关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsVideoCourseTagByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsVideoCourseTagByIds(ids);
+    }
+
+    /**
+     * 删除视频小节看课标签关联信息
+     *
+     * @param id 视频小节看课标签关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsVideoCourseTagById(Long id)
+    {
+        return baseMapper.deleteFsVideoCourseTagById(id);
+    }
+}

+ 1 - 1
fs-service/src/main/resources/application-config-druid-hat.yml

@@ -91,7 +91,7 @@ cloud_host:
 headerImg:
 headerImg:
   imgUrl: https://hat-1323137866.cos.ap-chongqing.myqcloud.com/fs/20250928/hatlogo.png
   imgUrl: https://hat-1323137866.cos.ap-chongqing.myqcloud.com/fs/20250928/hatlogo.png
 ipad:
 ipad:
-  ipadUrl: http://ipad.****.cn
+  ipadUrl: http://hatipad.ylrzcloud.com
   aiApi: http://62:3000/api
   aiApi: http://62:3000/api
   voiceApi:
   voiceApi:
   commonApi:
   commonApi:

+ 2 - 2
fs-service/src/main/resources/application-config-druid-knt.yml

@@ -58,8 +58,8 @@ watch:
   password3: v9xsKuqn_$d2y
   password3: v9xsKuqn_$d2y
 
 
 fs :
 fs :
-  commonApi: http://192.168.0.196:7771
-  h5CommonApi: http://192.168.0.196:7771
+  commonApi: http://192.168.0.114:7771
+  h5CommonApi: http://192.168.0.114:7771
   jwt:
   jwt:
     # 加密秘钥
     # 加密秘钥
     secret: f4e2e52034348f86b67cde581c0f9eb5
     secret: f4e2e52034348f86b67cde581c0f9eb5

+ 102 - 0
fs-service/src/main/resources/application-config-druid-knt2.yml

@@ -0,0 +1,102 @@
+baidu:
+  token: 12313231232
+  back-domain: https://www.xxxx.com
+#配置
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  miniapp:
+    configs:
+      - appid:
+        secret:
+        token:
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+  cp:
+    corpId: wxd2edd379beb6581b
+    appConfigs:
+      - agentId: 1000005
+        secret: ec7okROXJqkNafq66-L6aKNv0asTzQIG0CYrj3vyBbo
+        token: PPKOdAlCoMO
+        aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  pay:
+    appId: wxd2edd379beb6581b #微信公众号或者小程序等的appid
+    mchId: 1723480901 #微信支付商户号:陕西康年堂医药连锁有限公司
+    mchKey: 8cab128997a3547c1363b0898b877f38 #微信支付商户密钥
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl: https://userappB.kangniantangyiyao.top/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+      - appId: wx6ee517a8d8743f88  # 第一个公众号的appid
+        secret: 1fac75465a61f9259a0fe19795d9e80d # 公众号的appsecret
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+  open:
+    appId: wxd2edd379beb6581b
+    secret: 99e8312f6f7297c7d41196f5bb045053
+aifabu:  #爱链接
+  appKey: 7b471be905ab17e00f3b858c6710dd117601d008
+watch:
+  watchUrl: watch.ylrzcloud.com/prod-api
+  #  account: tcloud
+  #  password: mdf-m2h_6yw2$hq
+  account1: ccif #866655060138751
+  password1: cp-t5or_6xw7$mt
+  account2: tcloud #rt500台
+  password2: mdf-m2h_6yw2$hq
+  account3: whr
+  password3: v9xsKuqn_$d2y
+
+fs :
+  commonApi: http://192.168.0.114:7771
+  h5CommonApi: http://192.168.0.114:7771
+  jwt:
+    # 加密秘钥
+    secret: f4e2e52034348f86b67cde581c0f9eb5
+    # token有效时长,7天,单位秒
+    expire: 31536000
+    header: AppToken
+nuonuo:
+  key: 10924508
+  secret: A2EB20764D304D16
+
+# 存储捅配置
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: jnmy-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: jnmy
+tmp_secret_config:
+  secret_id: AKIDCj7NSNAovtqeJpBau8GZ4CGB71thXIx
+  secret_key: lTB5zwqqz7CNhzDOWivFWedgfTBgxgB
+  bucket: fs-131972100
+  app_id: 1319721001
+  region: ap-chongqing
+  proxy: fs
+cloud_host:
+  company_name: 康年堂
+  projectCode: KNT
+headerImg:
+  imgUrl: https
+ipad:
+  ipadUrl: http://qwipad.jnmyunl.com
+  aiApi: http://49.232.181.28:3000/api
+  voiceApi: http://139.186.176.122:8009
+  commonApi:
+wx_miniapp_temp:
+  pay_order_temp_id: -SjnK9K6cNKASa6AD9Q_c0YT7J1lPTEpPIpqbMJF8F0
+  inquiry_temp_id: hwFXVh0AWqeasBsZpa0-urb3CrPeYEwBiy3P6AMMGFQ
+
+

+ 2 - 2
fs-service/src/main/resources/application-druid-knt.yml

@@ -96,11 +96,11 @@ spring:
             druid:
             druid:
                 # 主库数据源
                 # 主库数据源
                 master:
                 master:
-                    url: jdbc:mysql://192.168.0.171:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    url: jdbc:mysql://192.168.0.171:3306/knt_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                     username: root
                     username: root
                     password: Ylrztek250218!3@.
                     password: Ylrztek250218!3@.
                 read:
                 read:
-                    url: jdbc:mysql://192.168.0.171:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    url: jdbc:mysql://192.168.0.171:3306/knt_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                     username: root
                     username: root
                     password: Ylrztek250218!3@.
                     password: Ylrztek250218!3@.
                 # 初始连接数
                 # 初始连接数

+ 166 - 0
fs-service/src/main/resources/application-druid-knt2.yml

@@ -0,0 +1,166 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-knt2,common
+    # redis 配置
+    redis:
+        # 地址
+        host: 192.168.0.131
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 1
+        # 密码
+        password: Ylrztek250218!3@.
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.171:3306/fs_knt_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.171:3306/knt_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                read:
+                    url: jdbc:mysql://192.168.0.171:3306/knt_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: 192.168.0.176:8100 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: jnmyunl # 替换为实际的 accessKey
+        secret-key: 73a!ul~xQl@-6u1 # 替换为实际的 secretKey
+        tls-enable: false
+    consumer:
+        topic: course-finish-notes
+        group: course-finish-group
+        access-key: jnmyunl # 替换为实际的 accessKey
+        secret-key: 73a!ul~xQl@-6u1 # 替换为实际的 secretKey
+        tls-enable: false
+openIM:
+    secret: openIM123
+    userID: imAdmin
+    url: https://web.im.fbylive.com/api
+#是否使用新im
+im:
+    type: TENCENT
+#是否为新商户,新商户不走mpOpenId
+isNewWxMerchant: true

+ 11 - 8
fs-service/src/main/resources/mapper/company/CompanyDeductMapper.xml

@@ -3,7 +3,7 @@
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.fs.company.mapper.CompanyDeductMapper">
 <mapper namespace="com.fs.company.mapper.CompanyDeductMapper">
-    
+
     <resultMap type="CompanyDeduct" id="CompanyDeductResult">
     <resultMap type="CompanyDeduct" id="CompanyDeductResult">
         <result property="deductId"    column="deduct_id"    />
         <result property="deductId"    column="deduct_id"    />
         <result property="deductNo"    column="deduct_no"    />
         <result property="deductNo"    column="deduct_no"    />
@@ -16,15 +16,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="auditUserId"    column="audit_user_id"    />
         <result property="auditUserId"    column="audit_user_id"    />
         <result property="auditTime"    column="audit_time"    />
         <result property="auditTime"    column="audit_time"    />
         <result property="remark"    column="remark"    />
         <result property="remark"    column="remark"    />
+        <result property="businessType"    column="business_type"    />
     </resultMap>
     </resultMap>
 
 
     <sql id="selectCompanyDeductVo">
     <sql id="selectCompanyDeductVo">
-        select deduct_id, deduct_no, company_id, money, balance, create_time, create_user_id, is_audit, audit_user_id, audit_time,remark from company_deduct
+        select deduct_id, deduct_no, company_id, money, balance, create_time, create_user_id, is_audit, audit_user_id, audit_time,remark, business_type from company_deduct
     </sql>
     </sql>
 
 
     <select id="selectCompanyDeductList" parameterType="CompanyDeduct" resultMap="CompanyDeductResult">
     <select id="selectCompanyDeductList" parameterType="CompanyDeduct" resultMap="CompanyDeductResult">
         <include refid="selectCompanyDeductVo"/>
         <include refid="selectCompanyDeductVo"/>
-        <where>  
+        <where>
             <if test="deductNo != null  and deductNo != ''"> and deduct_no = #{deductNo}</if>
             <if test="deductNo != null  and deductNo != ''"> and deduct_no = #{deductNo}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>
             <if test="money != null "> and money = #{money}</if>
             <if test="money != null "> and money = #{money}</if>
@@ -35,12 +36,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="auditTime != null "> and audit_time = #{auditTime}</if>
             <if test="auditTime != null "> and audit_time = #{auditTime}</if>
         </where>
         </where>
     </select>
     </select>
-    
+
     <select id="selectCompanyDeductById" parameterType="Long" resultMap="CompanyDeductResult">
     <select id="selectCompanyDeductById" parameterType="Long" resultMap="CompanyDeductResult">
         <include refid="selectCompanyDeductVo"/>
         <include refid="selectCompanyDeductVo"/>
         where deduct_id = #{deductId}
         where deduct_id = #{deductId}
     </select>
     </select>
-        
+
     <insert id="insertCompanyDeduct" parameterType="CompanyDeduct" useGeneratedKeys="true" keyProperty="deductId">
     <insert id="insertCompanyDeduct" parameterType="CompanyDeduct" useGeneratedKeys="true" keyProperty="deductId">
         insert into company_deduct
         insert into company_deduct
         <trim prefix="(" suffix=")" suffixOverrides=",">
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -54,6 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="auditUserId != null">audit_user_id,</if>
             <if test="auditUserId != null">audit_user_id,</if>
             <if test="auditTime != null">audit_time,</if>
             <if test="auditTime != null">audit_time,</if>
             <if test="remark != null">remark,</if>
             <if test="remark != null">remark,</if>
+            <if test="businessType != null">business_type,</if>
          </trim>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="deductNo != null">#{deductNo},</if>
             <if test="deductNo != null">#{deductNo},</if>
@@ -66,6 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="auditUserId != null">#{auditUserId},</if>
             <if test="auditUserId != null">#{auditUserId},</if>
             <if test="auditTime != null">#{auditTime},</if>
             <if test="auditTime != null">#{auditTime},</if>
             <if test="remark != null">#{remark},</if>
             <if test="remark != null">#{remark},</if>
+            <if test="businessType != null">#{businessType},</if>
          </trim>
          </trim>
     </insert>
     </insert>
 
 
@@ -91,10 +94,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </delete>
     </delete>
 
 
     <delete id="deleteCompanyDeductByIds" parameterType="String">
     <delete id="deleteCompanyDeductByIds" parameterType="String">
-        delete from company_deduct where deduct_id in 
+        delete from company_deduct where deduct_id in
         <foreach item="deductId" collection="array" open="(" separator="," close=")">
         <foreach item="deductId" collection="array" open="(" separator="," close=")">
             #{deductId}
             #{deductId}
         </foreach>
         </foreach>
     </delete>
     </delete>
-    
-</mapper>
+
+</mapper>

+ 11 - 5
fs-service/src/main/resources/mapper/course/FsCourseTrafficLogMapper.xml

@@ -60,7 +60,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         FROM
         FROM
         fs_course_traffic_log
         fs_course_traffic_log
         <where>
         <where>
-            DATE(create_time) = CURDATE()
+            <!-- DATE(create_time) = CURDATE() -->
+            create_time >= CURDATE()
+            AND create_time &lt; CURDATE() + INTERVAL 1 DAY
             <if test="companyId != null">
             <if test="companyId != null">
                 AND company_id = ${companyId}
                 AND company_id = ${companyId}
             </if>
             </if>
@@ -85,8 +87,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         FROM
         FROM
         fs_course_traffic_log
         fs_course_traffic_log
         <where>
         <where>
-            YEAR(create_time) = YEAR(CURDATE())
-            AND MONTH(create_time) = MONTH(CURDATE())
+            <!-- YEAR(create_time) = YEAR(CURDATE()) -->
+            <!-- AND MONTH(create_time) = MONTH(CURDATE()) -->
+            create_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
+            AND create_time &lt; DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01')
             <if test="companyId != null">
             <if test="companyId != null">
                 AND company_id = ${companyId}
                 AND company_id = ${companyId}
             </if>
             </if>
@@ -114,8 +118,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         FROM
         FROM
             fs_course_traffic_log
             fs_course_traffic_log
         WHERE
         WHERE
-            YEAR(create_time) = YEAR(CURDATE())
-          AND MONTH(create_time) = MONTH(CURDATE())
+        <!-- YEAR(create_time) = YEAR(CURDATE()) -->
+        <!-- AND MONTH(create_time) = MONTH(CURDATE()) -->
+        create_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01')
+        AND create_time &lt; DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01')
     </select>
     </select>
 
 
     <insert id="insertFsCourseTrafficLog" parameterType="FsCourseTrafficLog" useGeneratedKeys="true" keyProperty="logId">
     <insert id="insertFsCourseTrafficLog" parameterType="FsCourseTrafficLog" useGeneratedKeys="true" keyProperty="logId">

+ 4 - 4
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -128,16 +128,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and l.create_time &lt;= #{maps.eTime}
                 and l.create_time &lt;= #{maps.eTime}
             </if>
             </if>
             <if test= 'maps.scheduleStartTime != null '>
             <if test= 'maps.scheduleStartTime != null '>
-                and DATE(l.camp_period_time) &gt;= DATE(#{maps.scheduleStartTime})
+                and l.camp_period_time &gt;= #{maps.scheduleStartTime}
             </if>
             </if>
             <if test='maps.scheduleEndTime != null '>
             <if test='maps.scheduleEndTime != null '>
-                and DATE(l.camp_period_time) &lt;= DATE(#{maps.scheduleEndTime})
+                and l.camp_period_time &lt;= #{maps.scheduleEndTime}
             </if>
             </if>
             <if test= 'maps.upSTime != null '>
             <if test= 'maps.upSTime != null '>
-                and DATE(l.update_time) &gt;= DATE(#{maps.upSTime})
+                and l.update_time &gt;= #{maps.upSTime}
             </if>
             </if>
             <if test='maps.upETime != null '>
             <if test='maps.upETime != null '>
-                and DATE(l.update_time) &lt;= DATE(#{maps.upETime})
+                and l.update_time &lt;= #{maps.upETime}
             </if>
             </if>
             <if test="maps.sopIds != null and maps.sopIds.size() > 0">
             <if test="maps.sopIds != null and maps.sopIds.size() > 0">
                 and l.sop_id in
                 and l.sop_id in

+ 7 - 4
fs-service/src/main/resources/mapper/course/FsUserCourseMapper.xml

@@ -43,11 +43,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="isPrivate"    column="is_private"    />
         <result property="isPrivate"    column="is_private"    />
         <result property="secondImg"    column="second_img"    />
         <result property="secondImg"    column="second_img"    />
         <result property="companyIds"    column="company_ids"    />
         <result property="companyIds"    column="company_ids"    />
-
+        <result property="configJson"    column="config_json"    />
     </resultMap>
     </resultMap>
 
 
     <sql id="selectFsUserCourseVo">
     <sql id="selectFsUserCourseVo">
-        select course_id,is_private,company_ids,is_next,talent_id,second_img,is_del, cate_id,sub_cate_id, course_name, title, img_url, sort, create_time, update_time, status, is_vip, is_hot, is_show, views, duration, description, hot_ranking, integral, price, sell_price, project, tags, likes, favorite_num, shares, is_auto_play, is_fast, is_best, is_tui, hot_num, is_integral, course_type from fs_user_course
+        select course_id,is_private,company_ids,is_next,talent_id,second_img,is_del, cate_id,sub_cate_id, course_name, title, img_url, sort, create_time, update_time, status, is_vip, is_hot, is_show, views, duration, description, hot_ranking, integral, price, sell_price, project, tags, likes, favorite_num, shares, is_auto_play, is_fast, is_best, is_tui, hot_num, is_integral, course_type, config_json from fs_user_course
     </sql>
     </sql>
 
 
     <select id="selectFsUserCourseList" parameterType="FsUserCourse" resultMap="FsUserCourseResult">
     <select id="selectFsUserCourseList" parameterType="FsUserCourse" resultMap="FsUserCourseResult">
@@ -86,6 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isNext != null "> and is_next = #{isNext}</if>
             <if test="isNext != null "> and is_next = #{isNext}</if>
             <if test="isPrivate != null "> and is_private = #{isPrivate}</if>
             <if test="isPrivate != null "> and is_private = #{isPrivate}</if>
             <if test="userId != null "> and user_id = #{userId}</if>
             <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="configJson != null "> and config_json = #{configJson}</if>
         </where>
         </where>
     </select>
     </select>
 
 
@@ -159,7 +160,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         inner join fs_user_course_video ucv on ucv.video_id = cwl.video_id
         inner join fs_user_course_video ucv on ucv.video_id = cwl.video_id
         inner join fs_user_course uc on uc.course_id = ucv.course_id
         inner join fs_user_course uc on uc.course_id = ucv.course_id
         where cwl.user_id = #{userId} and uc.project = #{projectId}
         where cwl.user_id = #{userId} and uc.project = #{projectId}
-          and cwl.create_time between curdate() and date_add(curdate(), interval 1 day)
+          and cwl.create_time between curdate() and date_add(curdate(), interval 1 day) and cwl.send_type = 1
     </select>
     </select>
 
 
 
 
@@ -204,6 +205,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="secondImg != null">second_img,</if>
             <if test="secondImg != null">second_img,</if>
             <if test="companyIds != null">company_ids,</if>
             <if test="companyIds != null">company_ids,</if>
             <if test="userId != null">user_id,</if>
             <if test="userId != null">user_id,</if>
+            <if test="configJson != null">config_json,</if>
         </trim>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="cateId != null">#{cateId},</if>
             <if test="cateId != null">#{cateId},</if>
@@ -244,7 +246,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="secondImg != null">#{secondImg},</if>
             <if test="secondImg != null">#{secondImg},</if>
             <if test="companyIds != null">#{companyIds},</if>
             <if test="companyIds != null">#{companyIds},</if>
             <if test="userId != null">#{userId},</if>
             <if test="userId != null">#{userId},</if>
-         </trim>
+            <if test="configJson != null">config_json = #{configJson},</if>
+        </trim>
     </insert>
     </insert>
 
 
     <update id="updateFsUserCourse" parameterType="FsUserCourse">
     <update id="updateFsUserCourse" parameterType="FsUserCourse">

+ 0 - 6
fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -233,12 +233,6 @@
             <if test="listingEndTime != null">listing_end_time = #{listingEndTime},</if>
             <if test="listingEndTime != null">listing_end_time = #{listingEndTime},</if>
             <if test="projectId != null">project_id = #{projectId},</if>
             <if test="projectId != null">project_id = #{projectId},</if>
             <if test="isFirst != null">is_first = #{isFirst},</if>
             <if test="isFirst != null">is_first = #{isFirst},</if>
-            <if test="tagGroupId != null">tag_group_id = #{tagGroupId},</if>
-            <if test="watchingTagId != null">watching_tag_id = #{watchingTagId},</if>
-            <if test="watchedTagId != null">watched_tag_id = #{watchedTagId},</if>
-            <if test="tgId != null">tg_id = #{tgId},</if>
-            <if test="watchingTgId != null">watching_tg_id = #{watchingTgId},</if>
-            <if test="watchedTgId != null">watched_tg_id = #{watchedTgId},</if>
         </trim>
         </trim>
         where video_id = #{videoId}
         where video_id = #{videoId}
     </update>
     </update>

+ 15 - 5
fs-service/src/main/resources/mapper/course/FsUserCourseVideoRedPackageMapper.xml

@@ -63,11 +63,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
             #{id}
         </foreach>
         </foreach>
     </delete>
     </delete>
-    <delete id="deleteFsUserCourseVideoRedPackageByVedioIds" parameterType="String">
-        delete from fs_user_course_video_red_package where video_id in
-        <foreach item="id" collection="array" open="(" separator="," close=")">
-            #{id}
-        </foreach>
+    <delete id="deleteFsUserCourseVideoRedPackageByVedioIds">
+        DELETE FROM fs_user_course_video_red_package
+        WHERE 1=1
+        <if test="videoIds != null and videoIds.length > 0">
+            AND video_id IN
+            <foreach item="id" collection="videoIds" open="(" separator="," close=")">
+                #{id}
+            </foreach>
+        </if>
+        <if test="periodIds != null and periodIds.length > 0">
+            AND period_id IN
+            <foreach item="id" collection="periodIds" open="(" separator="," close=")">
+                #{id}
+            </foreach>
+        </if>
     </delete>
     </delete>
     <!-- 批量查询匹配的红包数据 -->
     <!-- 批量查询匹配的红包数据 -->
     <select id="selectByParamsList" resultMap="FsUserCourseVideoRedPackageResult">
     <select id="selectByParamsList" resultMap="FsUserCourseVideoRedPackageResult">

+ 2 - 0
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -624,12 +624,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isDel != null">is_del = #{isDel},</if>
             <if test="isDel != null">is_del = #{isDel},</if>
             <if test="userCode != null">user_code = #{userCode},</if>
             <if test="userCode != null">user_code = #{userCode},</if>
             <if test="remark != null">remark = #{remark},</if>
             <if test="remark != null">remark = #{remark},</if>
+            <if test="level != null">level = #{level},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="lastIp != null">last_ip = #{lastIp},</if>
             <if test="lastIp != null">last_ip = #{lastIp},</if>
             <if test="balance != null">balance = #{balance},</if>
             <if test="balance != null">balance = #{balance},</if>
             <if test="integralStatus != null">integral_status = #{integralStatus},</if>
             <if test="integralStatus != null">integral_status = #{integralStatus},</if>
             <if test="isBuy != null">is_buy = #{isBuy},</if>
             <if test="isBuy != null">is_buy = #{isBuy},</if>
+            <if test="isShow != null">is_show = #{isShow},</if>
             <if test="password != null">password = #{password},</if>
             <if test="password != null">password = #{password},</if>
             <if test="jpushId != null">jpush_id = #{jpushId},</if>
             <if test="jpushId != null">jpush_id = #{jpushId},</if>
             <if test="isVip != null">is_vip = #{isVip},</if>
             <if test="isVip != null">is_vip = #{isVip},</if>

+ 6 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreProductCategoryScrmMapper.xml

@@ -38,6 +38,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectFsStoreProductCategoryVo"/>
         <include refid="selectFsStoreProductCategoryVo"/>
         where cate_id = #{cateId}
         where cate_id = #{cateId}
     </select>
     </select>
+    <select id="selectCateIdsByName" resultType="java.lang.Long">
+        select cate_id from fs_store_product_category_scrm where pid in (
+        select cate_id from fs_store_product_category_scrm where cate_name = #{cateName} and pid=0
+        )
+
+    </select>
 
 
     <insert id="insertFsStoreProductCategory" parameterType="FsStoreProductCategoryScrm" useGeneratedKeys="true" keyProperty="cateId">
     <insert id="insertFsStoreProductCategory" parameterType="FsStoreProductCategoryScrm" useGeneratedKeys="true" keyProperty="cateId">
         insert into fs_store_product_category_scrm
         insert into fs_store_product_category_scrm

+ 0 - 1
fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml

@@ -92,7 +92,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                p.unit_price,p.batch_number,p.mah,p.mah_address,p.manufacturer,p.manufacturer_address,p.indications,p.dosage,
                p.unit_price,p.batch_number,p.mah,p.mah_address,p.manufacturer,p.manufacturer_address,p.indications,p.dosage,
                p.adverse_reactions,p.contraindications,p.precautions,p.is_audit,p.store_id
                p.adverse_reactions,p.contraindications,p.precautions,p.is_audit,p.store_id
         from fs_store_product_scrm p
         from fs_store_product_scrm p
-        select product_id, video, image, slider_image, product_name, product_info, keyword, bar_code, cate_id, price, vip_price, ot_price, agent_price, postage, unit_name, sort, sales, stock, is_show, is_hot, is_benefit, is_best, is_new, description, create_time, update_time, is_postage, is_del, give_integral, cost, is_good, browse, code_path, temp_id, spec_type, is_integral, integral, product_type, prescribe_code, prescribe_spec, prescribe_factory, prescribe_name, is_display, tui_cate_id, company_ids, store_id, is_drug, drug_image, drug_reg_cert_no, common_name, dosage_form, unit_price, batch_number, mah, mah_address, manufacturer, manufacturer_address, indications, dosage, adverse_reactions, contraindications, precautions, is_audit from fs_store_product_scrm
     </sql>
     </sql>
 
 
     <select id="selectFsStoreProductByProductId" parameterType="Long" resultMap="FsStoreProductResult">
     <select id="selectFsStoreProductByProductId" parameterType="Long" resultMap="FsStoreProductResult">

+ 174 - 42
fs-service/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

@@ -168,14 +168,16 @@
     </select>
     </select>
     <select id="watchEndPlayTrend" resultType="com.fs.statis.dto.WatchEndPlayTrendDTO">
     <select id="watchEndPlayTrend" resultType="com.fs.statis.dto.WatchEndPlayTrendDTO">
         SELECT
         SELECT
---             今日/昨日 小时
-            <if test="type == 0 or type == 1">
+        <choose>
+            <!-- 按小时分组 -->
+            <when test="type == 0 or type == 1">
                 DATE_FORMAT(create_time, '%H') AS start_date,
                 DATE_FORMAT(create_time, '%H') AS start_date,
-            </if>
---                 本周/本月/上月 天
-            <if test="type == 2 or type == 3 or type == 4">
+            </when>
+            <!-- 按天分组 -->
+            <when test="type == 2 or type == 3 or type == 4">
                 DATE_FORMAT(create_time, '%Y-%m-%d') AS start_date,
                 DATE_FORMAT(create_time, '%Y-%m-%d') AS start_date,
-            </if>
+            </when>
+        </choose>
         COUNT(DISTINCT user_id) AS watch_user_count,
         COUNT(DISTINCT user_id) AS watch_user_count,
         COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS completed_user_count
         COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS completed_user_count
         FROM
         FROM
@@ -192,7 +194,10 @@
             </if>
             </if>
         </where>
         </where>
         GROUP BY
         GROUP BY
-        start_date
+        <choose>
+            <when test="type == 0 or type == 1">DATE_FORMAT(create_time, '%H')</when>
+            <when test="type == 2 or type == 3 or type == 4">DATE_FORMAT(create_time, '%Y-%m-%d')</when>
+        </choose>
         ORDER BY
         ORDER BY
         start_date
         start_date
     </select>
     </select>
@@ -224,50 +229,177 @@
         GROUP BY company_id
         GROUP BY company_id
         limit 10
         limit 10
     </select>
     </select>
-    <select id="watchCourseTopTen" resultType="com.fs.statis.dto.CourseStatsDTO">
+<!--    <select id="watchCourseTopTen" resultType="com.fs.statis.dto.CourseStatsDTO">-->
+<!--        SELECT-->
+<!--        w.course_id AS course_id,-->
+<!--        COUNT(DISTINCT w.user_id) AS watch_user_count,-->
+<!--        COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END) AS completed_user_count,-->
+<!--        COUNT(DISTINCT a.user_id) AS answer_user_count,-->
+<!--        COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count-->
+<!--        FROM-->
+<!--        fs_course_watch_log w-->
+<!--        LEFT JOIN-->
+<!--        fs_course_answer_logs a ON w.video_id = a.video_id AND w.user_id = a.user_id-->
+<!--        <where>-->
+<!--            <if test="startTime != null">-->
+<!--                w.create_time <![CDATA[>=]]> #{startTime}-->
+<!--            </if>-->
+<!--            <if test="endTime != null">-->
+<!--                AND w.create_time <![CDATA[<]]> #{endTime}-->
+<!--            </if>-->
+<!--            <if test="userType != null">-->
+<!--                AND send_type = ${userType}-->
+<!--            </if>-->
+<!--            <if test="companyId != null">-->
+<!--                AND w.company_id = ${companyId}-->
+<!--            </if>-->
+<!--        </where>-->
+<!--        GROUP BY-->
+<!--        w.course_id-->
+<!--        ORDER BY-->
+<!--            &#45;&#45; 观看人数-->
+<!--            <if test="statisticalType == 0">-->
+<!--                COUNT(DISTINCT w.user_id)-->
+<!--            </if>-->
+<!--            <if test="statisticalType == 1">-->
+<!--                COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END)-->
+<!--            </if>-->
+<!--            <if test="statisticalType == 2">-->
+<!--                COUNT(DISTINCT a.user_id)-->
+<!--            </if>-->
+<!--            <if test="statisticalType == 3">-->
+<!--                COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END)-->
+<!--            </if>-->
+<!--        ${sort}-->
+<!--        LIMIT 10-->
+<!--    </select>-->
+
+    <!-- 1. 按观看人数排序 -->
+    <select id="watchCourseTopTenByWatch" resultType="com.fs.statis.dto.CourseStatsDTO">
+        SELECT
+        course_id AS courseId,
+        COUNT(DISTINCT user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS completed_user_count,
+        0 AS answer_user_count,
+        0 AS correct_user_count
+        FROM fs_course_watch_log
+        <where>
+            <if test="startTime != null">create_time <![CDATA[>=]]> #{startTime}</if>
+            <if test="endTime != null">AND create_time <![CDATA[<]]> #{endTime}</if>
+            <if test="userType != null">AND send_type = #{userType}</if>
+            <if test="companyId != null">AND company_id = #{companyId}</if>
+        </where>
+        GROUP BY course_id
+        ORDER BY watch_user_count
+        <choose>
+            <when test="sort != null and sort == 'DESC'">DESC</when>
+            <otherwise>ASC</otherwise>
+        </choose>
+        LIMIT 10
+    </select>
+
+    <!-- 2. 按完成人数排序 -->
+    <select id="watchCourseTopTenByComplete" resultType="com.fs.statis.dto.CourseStatsDTO">
         SELECT
         SELECT
-        w.course_id AS course_id,
+        course_id AS courseId,
+        COUNT(DISTINCT user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS completed_user_count,
+        0 AS answer_user_count,
+        0 AS correct_user_count
+        FROM fs_course_watch_log
+        <where>
+            <if test="startTime != null">create_time <![CDATA[>=]]> #{startTime}</if>
+            <if test="endTime != null">AND create_time <![CDATA[<]]> #{endTime}</if>
+            <if test="userType != null">AND send_type = #{userType}</if>
+            <if test="companyId != null">AND company_id = #{companyId}</if>
+        </where>
+        GROUP BY course_id
+        ORDER BY completed_user_count
+        <choose>
+            <when test="sort != null and sort == 'DESC'">DESC</when>
+            <otherwise>ASC</otherwise>
+        </choose>
+        LIMIT 10
+    </select>
+
+    <!-- 3. 按答题人数排序 -->
+    <select id="watchCourseTopTenByAnswer" resultType="com.fs.statis.dto.CourseStatsDTO">
+        SELECT
+        w.course_id AS courseId,
         COUNT(DISTINCT w.user_id) AS watch_user_count,
         COUNT(DISTINCT w.user_id) AS watch_user_count,
         COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END) AS completed_user_count,
         COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END) AS completed_user_count,
         COUNT(DISTINCT a.user_id) AS answer_user_count,
         COUNT(DISTINCT a.user_id) AS answer_user_count,
         COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count
         COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count
-        FROM
-        fs_course_watch_log w
-        LEFT JOIN
-        fs_course_answer_logs a ON w.video_id = a.video_id AND w.user_id = a.user_id
+        FROM fs_course_watch_log w
+        LEFT JOIN fs_course_answer_logs a ON w.video_id = a.video_id AND w.user_id = a.user_id
         <where>
         <where>
-            <if test="startTime != null">
-                w.create_time <![CDATA[>=]]> #{startTime}
-            </if>
-            <if test="endTime != null">
-                AND w.create_time <![CDATA[<]]> #{endTime}
-            </if>
-            <if test="userType != null">
-                AND send_type = ${userType}
-            </if>
-            <if test="companyId != null">
-                AND w.company_id = ${companyId}
-            </if>
+            <if test="startTime != null">w.create_time <![CDATA[>=]]> #{startTime}</if>
+            <if test="endTime != null">AND w.create_time <![CDATA[<]]> #{endTime}</if>
+            <if test="userType != null">AND w.send_type = #{userType}</if>
+            <if test="companyId != null">AND w.company_id = #{companyId}</if>
+<!--            <if test="startTime != null">AND a.create_time <![CDATA[>=]]> #{startTime}</if> -->
+<!--            <if test="endTime != null">AND a.create_time <![CDATA[<]]> #{endTime}</if> -->
         </where>
         </where>
-        GROUP BY
-        w.course_id
-        ORDER BY
-            -- 观看人数
-            <if test="statisticalType == 0">
-                COUNT(DISTINCT w.user_id)
-            </if>
-            <if test="statisticalType == 1">
-                COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END)
-            </if>
-            <if test="statisticalType == 2">
-                COUNT(DISTINCT a.user_id)
-            </if>
-            <if test="statisticalType == 3">
-                COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END)
-            </if>
-        ${sort}
+        GROUP BY w.course_id
+        ORDER BY answer_user_count
+        <choose>
+            <when test="sort != null and sort == 'DESC'">DESC</when>
+            <otherwise>ASC</otherwise>
+        </choose>
+        LIMIT 10
+    </select>
+
+    <!-- 4. 按正确人数排序 -->
+    <select id="watchCourseTopTenByCorrect" resultType="com.fs.statis.dto.CourseStatsDTO">
+        SELECT
+        w.course_id AS courseId,
+        COUNT(DISTINCT w.user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END) AS completed_user_count,
+        COUNT(DISTINCT a.user_id) AS answer_user_count,
+        COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count
+        FROM fs_course_watch_log w
+        LEFT JOIN fs_course_answer_logs a ON w.video_id = a.video_id AND w.user_id = a.user_id
+        <where>
+            <if test="startTime != null">w.create_time <![CDATA[>=]]> #{startTime}</if>
+            <if test="endTime != null">AND w.create_time <![CDATA[<]]> #{endTime}</if>
+            <if test="userType != null">AND w.send_type = #{userType}</if>
+            <if test="companyId != null">AND w.company_id = #{companyId}</if>
+            <if test="startTime != null">AND a.create_time <![CDATA[>=]]> #{startTime}</if>
+            <if test="endTime != null">AND a.create_time <![CDATA[<]]> #{endTime}</if>
+        </where>
+        GROUP BY w.course_id
+        ORDER BY correct_user_count
+        <choose>
+            <when test="sort != null and sort == 'DESC'">DESC</when>
+            <otherwise>ASC</otherwise>
+        </choose>
         LIMIT 10
         LIMIT 10
     </select>
     </select>
+
+    <!-- 5. 批量补充答题数据 -->
+    <select id="getAnswerStatsByCourseIds" resultType="com.fs.statis.dto.CourseStatsDTO">
+        SELECT
+        w.course_id AS courseId,
+        COUNT(DISTINCT a.user_id) AS answer_user_count,
+        COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count
+        FROM fs_course_answer_logs a
+        INNER JOIN fs_course_watch_log w ON a.video_id = w.video_id AND a.user_id = w.user_id
+        <where>
+            w.course_id IN
+            <foreach collection="courseIds" item="courseId" open="(" close=")" separator=",">
+                #{courseId}
+            </foreach>
+            <if test="param.startTime != null">AND w.create_time <![CDATA[>=]]> #{param.startTime}</if>
+            <if test="param.endTime != null">AND w.create_time <![CDATA[<]]> #{param.endTime}</if>
+            <if test="param.userType != null">AND w.send_type = #{param.userType}</if>
+            <if test="param.companyId != null">AND w.company_id = #{param.companyId}</if>
+            <if test="param.startTime != null">AND a.create_time <![CDATA[>=]]> #{param.startTime}</if>
+            <if test="param.endTime != null">AND a.create_time <![CDATA[<]]> #{param.endTime}</if>
+        </where>
+        GROUP BY w.course_id
+    </select>
+
+
     <select id="rewardMoneyTopTen" resultType="com.fs.statis.dto.RewardMoneyTopTenDTO">
     <select id="rewardMoneyTopTen" resultType="com.fs.statis.dto.RewardMoneyTopTenDTO">
         SELECT
         SELECT
             -- 按公司
             -- 按公司

+ 119 - 0
fs-service/src/main/resources/mapper/tag/FsVideoCourseTagMapper.xml

@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.tag.mapper.FsVideoCourseTagMapper">
+
+    <resultMap type="FsVideoCourseTag" id="FsVideoCourseTagResult">
+        <result property="id"    column="id"    />
+        <result property="corpId"    column="corp_id"    />
+        <result property="videoId"    column="video_id"    />
+        <result property="watchingGroupId"    column="watching_group_id"    />
+        <result property="watchedGroupId"    column="watched_group_id"    />
+        <result property="watchingTgId"    column="watching_tg_id"    />
+        <result property="watchedTgId"    column="watched_tg_id"    />
+        <result property="watchingGroupTgId"    column="watching_group_tag_id"    />
+        <result property="watchedGroupTgId"    column="watched_group_tag_id"    />
+        <result property="watchingTagId"    column="watching_tag_id"    />
+        <result property="watchedTagId"    column="watched_tag_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="updateBy"    column="update_by"    />
+    </resultMap>
+
+    <sql id="selectFsVideoCourseTagVo">
+        select id, corp_id, video_id, watching_group_id, watched_group_id, watching_tg_id, watched_tg_id, watching_group_tag_id, watched_group_tag_id, watching_tag_id, watched_tag_id, create_time, update_time, create_by, update_by from fs_video_course_tag
+    </sql>
+
+    <select id="selectFsVideoCourseTagList" parameterType="FsVideoCourseTag" resultMap="FsVideoCourseTagResult">
+        <include refid="selectFsVideoCourseTagVo"/>
+        <where>
+            <if test="corpId != null  and corpId != ''"> and corp_id = #{corpId}</if>
+            <if test="videoId != null "> and video_id = #{videoId}</if>
+            <if test="watchingGroupId != null "> and watching_group_id = #{watchingGroupId}</if>
+            <if test="watchedGroupId != null "> and watched_group_id = #{watchedGroupId}</if>
+            <if test="watchingTgId != null "> and watching_tg_id = #{watchingTgId}</if>
+            <if test="watchedTgId != null "> and watched_tg_id = #{watchedTgId}</if>
+            <if test="watchingGroupTgId != null  and watchingGroupTgId != ''"> and watching_group_tag_id = #{watchingGroupTgId}</if>
+            <if test="watchedGroupTgId != null  and watchedGroupTgId != ''"> and watched_group_tag_id = #{watchedGroupTgId}</if>
+            <if test="watchingTagId != null  and watchingTagId != ''"> and watching_tag_id = #{watchingTagId}</if>
+            <if test="watchedTagId != null  and watchedTagId != ''"> and watched_tag_id = #{watchedTagId}</if>
+        </where>
+    </select>
+
+    <select id="selectFsVideoCourseTagById" parameterType="Long" resultMap="FsVideoCourseTagResult">
+        <include refid="selectFsVideoCourseTagVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsVideoCourseTag" parameterType="FsVideoCourseTag">
+        insert into fs_video_course_tag
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="corpId != null">corp_id,</if>
+            <if test="videoId != null">video_id,</if>
+            <if test="watchingGroupId != null">watching_group_id,</if>
+            <if test="watchedGroupId != null">watched_group_id,</if>
+            <if test="watchingTgId != null">watching_tg_id,</if>
+            <if test="watchedTgId != null">watched_tg_id,</if>
+            <if test="watchingGroupTgId != null">watching_group_tag_id,</if>
+            <if test="watchedGroupTgId != null">watched_group_tag_id,</if>
+            <if test="watchingTagId != null">watching_tag_id,</if>
+            <if test="watchedTagId != null">watched_tag_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="updateBy != null">update_by,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="corpId != null">#{corpId},</if>
+            <if test="videoId != null">#{videoId},</if>
+            <if test="watchingGroupId != null">#{watchingGroupId},</if>
+            <if test="watchedGroupId != null">#{watchedGroupId},</if>
+            <if test="watchingTgId != null">#{watchingTgId},</if>
+            <if test="watchedTgId != null">#{watchedTgId},</if>
+            <if test="watchingGroupTgId != null">#{watchingGroupTgId},</if>
+            <if test="watchedGroupTgId != null">#{watchedGroupTgId},</if>
+            <if test="watchingTagId != null">#{watchingTagId},</if>
+            <if test="watchedTagId != null">#{watchedTagId},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+        </trim>
+    </insert>
+
+    <update id="updateFsVideoCourseTag" parameterType="FsVideoCourseTag">
+        update fs_video_course_tag
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="corpId != null">corp_id = #{corpId},</if>
+            <if test="videoId != null">video_id = #{videoId},</if>
+            <if test="watchingGroupId != null">watching_group_id = #{watchingGroupId},</if>
+            <if test="watchedGroupId != null">watched_group_id = #{watchedGroupId},</if>
+            <if test="watchingTgId != null">watching_tg_id = #{watchingTgId},</if>
+            <if test="watchedTgId != null">watched_tg_id = #{watchedTgId},</if>
+            <if test="watchingGroupTgId != null">watching_group_tag_id = #{watchingGroupTgId},</if>
+            <if test="watchedGroupTgId != null">watched_group_tag_id = #{watchedGroupTgId},</if>
+            <if test="watchingTagId != null">watching_tag_id = #{watchingTagId},</if>
+            <if test="watchedTagId != null">watched_tag_id = #{watchedTagId},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsVideoCourseTagById" parameterType="Long">
+        delete from fs_video_course_tag where id = #{id}
+    </delete>
+
+    <delete id="deleteFsVideoCourseTagByIds" parameterType="String">
+        delete from fs_video_course_tag where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>