Ver código fonte

Merge remote-tracking branch 'origin/master'

三七 1 mês atrás
pai
commit
379c41c38b
100 arquivos alterados com 4663 adições e 929 exclusões
  1. 111 0
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  2. 128 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyRedPackageLogsController.java
  3. 10 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseTrafficLogController.java
  4. 24 4
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  5. 109 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseTrainingCampController.java
  6. 0 1
      fs-admin/src/main/java/com/fs/task/FsCourseTask.java
  7. 64 0
      fs-admin/src/main/java/com/fs/task/qw/FsCourseTask.java
  8. 9 9
      fs-admin/src/main/resources/application-druid-fby.yml
  9. 0 80
      fs-admin/src/main/resources/application-druid.yml
  10. 1 1
      fs-admin/src/main/resources/application.yml
  11. 26 26
      fs-admin/src/test/java/com/fs/task/StoreTaskTest.java
  12. 13 0
      fs-common/src/main/java/com/fs/common/core/domain/SortRule.java
  13. 80 0
      fs-common/src/main/java/com/fs/common/utils/PhoneUtil.java
  14. 39 0
      fs-common/src/main/java/com/fs/common/utils/SortUtils.java
  15. 47 7
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  16. 4 2
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  17. 101 0
      fs-company-app/src/main/java/com/fs/app/controller/WxH5MpController.java
  18. 11 0
      fs-company-app/src/main/java/com/fs/app/param/FsUserLoginByMpParam.java
  19. 11 11
      fs-company-app/src/main/resources/application-druid-fby.yml
  20. 0 80
      fs-company-app/src/main/resources/application-druid.yml
  21. 144 0
      fs-company/src/main/java/com/fs/company/controller/CompanyRedPackageLogsController.java
  22. 10 2
      fs-company/src/main/java/com/fs/course/controller/FsCourseAnswerLogsController.java
  23. 3 3
      fs-company/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java
  24. 153 0
      fs-company/src/main/java/com/fs/course/controller/qw/QwFsCourseAnswerLogsController.java
  25. 267 0
      fs-company/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  26. 128 0
      fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java
  27. 3 3
      fs-company/src/main/java/com/fs/qw/qw/QwWorkTaskController.java
  28. 19 11
      fs-company/src/main/resources/application-druid-fby.yml
  29. 137 0
      fs-qw-api/src/main/resources/application-druid-fby.yml
  30. 137 0
      fs-qw-task/src/main/resources/application-druid-fby.yml
  31. 137 0
      fs-qwhook/src/main/resources/application-druid-fby.yml
  32. 0 80
      fs-qwhook/src/main/resources/application-druid.yml
  33. 3 3
      fs-service-generator/src/main/resources/vm/java/controller.java.vm
  34. 10 37
      fs-service-generator/src/main/resources/vm/java/domain.java.vm
  35. 41 11
      fs-service-generator/src/main/resources/vm/java/mapper.java.vm
  36. 11 11
      fs-service-generator/src/main/resources/vm/java/service.java.vm
  37. 69 13
      fs-service-generator/src/main/resources/vm/java/serviceImpl.java.vm
  38. 73 0
      fs-service-generator/src/main/resources/vm/java/sub-domain.java.vm
  39. 6 6
      fs-service-generator/src/main/resources/vm/sql/sql.vm
  40. 83 46
      fs-service-generator/src/main/resources/vm/vue/index-tree.vue.vm
  41. 158 47
      fs-service-generator/src/main/resources/vm/vue/index.vue.vm
  42. 41 1
      fs-service-generator/src/main/resources/vm/xml/mapper.xml.vm
  43. 3 0
      fs-service-system/src/main/java/com/fs/company/cache/impl/ICompanyCacheServiceImpl.java
  44. 8 0
      fs-service-system/src/main/java/com/fs/company/domain/Company.java
  45. 75 0
      fs-service-system/src/main/java/com/fs/company/domain/CompanyRedPackageLogs.java
  46. 9 0
      fs-service-system/src/main/java/com/fs/company/domain/CompanyUser.java
  47. 1 0
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyMapper.java
  48. 61 0
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyRedPackageLogsMapper.java
  49. 68 0
      fs-service-system/src/main/java/com/fs/company/service/ICompanyRedPackageLogsService.java
  50. 2 0
      fs-service-system/src/main/java/com/fs/company/service/ICompanyService.java
  51. 141 0
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyRedPackageLogsServiceImpl.java
  52. 6 0
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  53. 1 0
      fs-service-system/src/main/java/com/fs/company/vo/CompanyVO.java
  54. 2 0
      fs-service-system/src/main/java/com/fs/course/config/CourseConfig.java
  55. 13 170
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java
  56. 78 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCoursePeriodDays.java
  57. 9 51
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseTrainingCamp.java
  58. 15 0
      fs-service-system/src/main/java/com/fs/course/dto/FsUserCourseTrainingCampDTO.java
  59. 20 0
      fs-service-system/src/main/java/com/fs/course/dto/FsUserCourseTrainingCampUpdateDTO.java
  60. 1 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java
  61. 1 13
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  62. 1 20
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  63. 63 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java
  64. 10 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  65. 17 43
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseTrainingCampMapper.java
  66. 2 0
      fs-service-system/src/main/java/com/fs/course/param/FsCourseAnswerLogsParam.java
  67. 2 2
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseAddCompanyUserParam.java
  68. 33 0
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseBeMemberImageParam.java
  69. 2 1
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseBeMemberParam.java
  70. 5 2
      fs-service-system/src/main/java/com/fs/course/param/newfs/UserCourseVideoPageParam.java
  71. 1 0
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseAnswerLogsService.java
  72. 1 0
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseLinkService.java
  73. 1 1
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java
  74. 2 0
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  75. 66 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCoursePeriodDaysService.java
  76. 25 2
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseService.java
  77. 22 37
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseTrainingCampService.java
  78. 5 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseAnswerLogsServiceImpl.java
  79. 53 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  80. 7 2
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java
  81. 66 4
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  82. 147 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java
  83. 3 2
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  84. 68 14
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  85. 81 56
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java
  86. 1 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  87. 4 4
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseOverVO.java
  88. 2 2
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java
  89. 4 3
      fs-service-system/src/main/java/com/fs/course/vo/FsUserCoursePeriodVO.java
  90. 35 0
      fs-service-system/src/main/java/com/fs/course/vo/FsUserCourseTrainingCampVO.java
  91. 10 4
      fs-service-system/src/main/java/com/fs/course/vo/newfs/FsUserCourseListVO.java
  92. 127 0
      fs-service-system/src/main/java/com/fs/qw/mapper/HyWorkTaskMapper.java
  93. 222 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwWatchLogExMapper.java
  94. 2 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwWorkTaskMapper.java
  95. 3 0
      fs-service-system/src/main/java/com/fs/qw/param/QwWorkTaskListParam.java
  96. 110 0
      fs-service-system/src/main/java/com/fs/qw/service/IHyWorkTaskService.java
  97. 1 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwWorkTaskService.java
  98. 518 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/HyWorkTaskServiceImpl.java
  99. 1 1
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwJsApiServiceImpl.java
  100. 5 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwWorkTaskServiceImpl.java

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

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

+ 128 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyRedPackageLogsController.java

@@ -0,0 +1,128 @@
+package com.fs.company.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyRedPackageLogs;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyRedPackageLogsService;
+import com.fs.core.security.LoginUser;
+import com.fs.core.web.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 公司红包余额日志Controller
+ * 
+ * @author 吴树波
+ * @date 2025-04-15
+ */
+@RestController
+@RequestMapping("/company/CompanyRedPackageLogs")
+public class CompanyRedPackageLogsController extends BaseController
+{
+    @Autowired
+    private ICompanyRedPackageLogsService companyRedPackageLogsService;
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 查询公司红包余额日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        startPage();
+        List<CompanyRedPackageLogs> list = companyRedPackageLogsService.selectCompanyRedPackageLogsList(companyRedPackageLogs);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出公司红包余额日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:export')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        List<CompanyRedPackageLogs> list = companyRedPackageLogsService.selectCompanyRedPackageLogsList(companyRedPackageLogs);
+        ExcelUtil<CompanyRedPackageLogs> util = new ExcelUtil<CompanyRedPackageLogs>(CompanyRedPackageLogs.class);
+        return util.exportExcel(list, "公司红包余额日志数据");
+    }
+
+    /**
+     * 获取公司红包余额日志详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(companyRedPackageLogsService.selectCompanyRedPackageLogsById(id));
+    }
+
+    /**
+     * 新增公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:add')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        return toAjax(companyRedPackageLogsService.insertCompanyRedPackageLogs(companyRedPackageLogs));
+    }
+
+    /**
+     * 修改公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:edit')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        return toAjax(companyRedPackageLogsService.updateCompanyRedPackageLogs(companyRedPackageLogs));
+    }
+
+    /**
+     * 删除公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:remove')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(companyRedPackageLogsService.deleteCompanyRedPackageLogsByIds(ids));
+    }
+
+    /**
+     * 删除公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:add')")
+    @Log(title = "充值余额", businessType = BusinessType.INSERT)
+	@PostMapping("/recharge")
+    public R recharge(@RequestBody CompanyRedPackageLogs companyRedPackageLogs){
+        companyRedPackageLogsService.recharge(companyRedPackageLogs);
+        return R.ok();
+    }
+
+
+
+    @GetMapping(value = "/getMoneyPri")
+    public R getMoneyPri(){
+        return R.ok().put("data", companyRedPackageLogsService.getMoneyPri());
+    }
+    // 充值审核
+    @PostMapping(value = "/rew")
+    public R rew(@RequestBody CompanyRedPackageLogs companyRedPackageLogs){
+        companyRedPackageLogsService.rew(companyRedPackageLogs);
+        return R.ok();
+    }
+}

+ 10 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseTrafficLogController.java

@@ -9,6 +9,7 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsCourseTrafficLog;
+import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.FsCourseTrafficLogParam;
 import com.fs.course.service.IFsCourseTrafficLogService;
 import com.fs.course.service.IFsUserCourseService;
@@ -40,6 +41,8 @@ public class FsCourseTrafficLogController extends BaseController
 
     @Autowired
     private IFsUserCourseService fsUserCourseMapper;
+    @Autowired
+    private FsUserCourseVideoMapper fsUserCourseVideoMapper;
     /**
      * 查询短链课程流量记录列表
      */
@@ -133,4 +136,11 @@ public class FsCourseTrafficLogController extends BaseController
         List<OptionsVO> optionsVOS = fsUserCourseMapper.selectFsUserCourseAllList();
         return R.ok().put("list", optionsVOS);
     }
+
+    @GetMapping(value = "/videoList/{id}")
+    public R videoList(@PathVariable("id") Long id)
+    {
+        List<OptionsVO> optionsVOS = fsUserCourseVideoMapper.selectFsUserCourseVodeAllList(id);
+        return R.ok().put("list", optionsVOS);
+    }
 }

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

@@ -2,8 +2,12 @@ package com.fs.course.controller;
 
 import java.util.List;
 
+import com.fs.common.core.domain.R;
+import com.fs.course.domain.FsUserCoursePeriodDays;
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -30,11 +34,11 @@ import com.fs.common.core.page.TableDataInfo;
  * @date 2025-04-11
  */
 @RestController
+@AllArgsConstructor
 @RequestMapping("/course/period")
-public class FsUserCoursePeriodController extends BaseController
-{
-    @Autowired
-    private IFsUserCoursePeriodService fsUserCoursePeriodService;
+public class FsUserCoursePeriodController extends BaseController {
+    private final IFsUserCoursePeriodService fsUserCoursePeriodService;
+    private final IFsUserCoursePeriodDaysService fsUserCoursePeriodDaysService;
 
     /**
      * 查询会员营期列表
@@ -112,4 +116,20 @@ public class FsUserCoursePeriodController extends BaseController
     {
         return toAjax(fsUserCoursePeriodService.deleteFsUserCoursePeriodByIds(periodIds));
     }
+
+    @GetMapping("/getDays")
+    public TableDataInfo getDays(FsUserCoursePeriodDays fsUserCoursePeriodDays){
+        startPage();
+        List<FsUserCoursePeriodDays> list = fsUserCoursePeriodDaysService.selectFsUserCoursePeriodDaysList(fsUserCoursePeriodDays);
+        return getDataTable(list);
+    }
+
+    @PostMapping("/addCourse")
+    public R addCourse(@RequestBody FsUserCoursePeriodDays entity){
+        return fsUserCoursePeriodDaysService.addCourse(entity);
+    }
+    @PostMapping("/updateListCourseData")
+    public R updateListCourseData(@RequestBody List<FsUserCoursePeriodDays> entity){
+        return fsUserCoursePeriodDaysService.updateListCourseData(entity);
+    }
 }

+ 109 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseTrainingCampController.java

@@ -0,0 +1,109 @@
+package com.fs.course.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.SortUtils;
+import com.fs.course.domain.FsUserCourseTrainingCamp;
+import com.fs.course.dto.FsUserCourseTrainingCampDTO;
+import com.fs.course.dto.FsUserCourseTrainingCampUpdateDTO;
+import com.fs.course.service.IFsUserCourseTrainingCampService;
+import com.fs.course.vo.FsUserCourseTrainingCampVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import lombok.AllArgsConstructor;
+import org.assertj.core.util.Arrays;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/course/trainingCamp")
+@AllArgsConstructor
+public class FsUserCourseTrainingCampController {
+
+    private final IFsUserCourseTrainingCampService fsUserCourseTrainingCampService;
+
+    /**
+     * 查询训练营列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:trainingCamp:list')")
+    @GetMapping("/list")
+    public AjaxResult list(@RequestParam(required = false) String trainingCampName,
+                           @RequestParam String scs,
+                           @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                           @RequestParam(required = false, defaultValue = "10") Integer pageSize)
+    {
+        Map<String, Object> params = new HashMap<>();
+        params.put("trainingCampName", trainingCampName);
+        params.put("scs", SortUtils.parseSort(scs));
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<FsUserCourseTrainingCampVO> list = fsUserCourseTrainingCampService.selectFsUserCourseTrainingCampVOListByMap(params);
+        return AjaxResult.success(new PageInfo<>(list));
+    }
+
+    /**
+     * 新增训练营
+     * @param params    参数
+     * @return  AjaxResult
+     */
+    @PreAuthorize("@ss.hasPermi('course:trainingCamp:add')")
+    @Log(title = "训练营", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Valid @RequestBody FsUserCourseTrainingCampDTO params) {
+        fsUserCourseTrainingCampService.add(params);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 修改训练营
+     * @param params    参数
+     * @return  AjaxResult
+     */
+    @PreAuthorize("@ss.hasPermi('course:trainingCamp:edit')")
+    @Log(title = "训练营", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Valid @RequestBody FsUserCourseTrainingCampUpdateDTO params) {
+        FsUserCourseTrainingCamp trainingCamp = fsUserCourseTrainingCampService.getById(params.getTrainingCampId());
+        if (Objects.isNull(trainingCamp)) {
+            return AjaxResult.error("训练营不存在");
+        }
+
+        trainingCamp.setTrainingCampName(params.getTrainingCampName());
+        fsUserCourseTrainingCampService.updateById(trainingCamp);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 删除训练营
+     * @param ids    参数
+     * @return  AjaxResult
+     */
+    @PreAuthorize("@ss.hasPermi('course:trainingCamp:remove')")
+    @Log(title = "训练营", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult del(@PathVariable Long[] ids) {
+        fsUserCourseTrainingCampService.del(ids);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 复制训练营
+     * @param id    参数
+     * @return  AjaxResult
+     */
+    @PreAuthorize("@ss.hasPermi('course:trainingCamp:add')")
+    @Log(title = "训练营", businessType = BusinessType.INSERT)
+    @PostMapping("/copy/{id}")
+    public AjaxResult copy(@PathVariable Long id) {
+        fsUserCourseTrainingCampService.copy(id);
+        return AjaxResult.success();
+    }
+
+}

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

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

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

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

+ 9 - 9
fs-qw-task/src/main/resources/application-druid.yml → fs-admin/src/main/resources/application-druid-fby.yml

@@ -3,11 +3,11 @@ spring:
     # redis 配置
     redis:
         # 地址
-        host: 127.0.0.1
+        host: 192.168.0.4
         # 端口,默认为6379
         port: 6379
         # 密码
-        password:
+        password: Ylrztek250218!3@.
         # 连接超时时间
         timeout: 30s
         lettuce:
@@ -28,9 +28,9 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/ylrz_scrm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://192.168.0.74:3306/fby_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
                 # 从库数据源
                 slave:
                     # 从数据源开关/默认关闭
@@ -83,9 +83,9 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://192.168.0.74:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
                 # 初始连接数
                 initialSize: 5
                 # 最小连接池数量
@@ -132,6 +132,6 @@ rocketmq:
         access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
         secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
     consumer:
-        group: my-consumer-group
+        group: test-group
         access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
         secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 0 - 80
fs-admin/src/main/resources/application-druid.yml

@@ -1,80 +0,0 @@
-# 数据源配置
-spring:
-    # redis 配置
-    redis:
-        # 地址
-        host: 127.0.0.1
-        # 端口,默认为6379
-        port: 6379
-        # 密码
-        password:
-        # 连接超时时间
-        timeout: 30s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
-        database: 0
-    datasource:
-        type: com.alibaba.druid.pool.DruidDataSource
-        driverClassName: com.mysql.cj.jdbc.Driver
-        druid:
-            # 主库数据源
-            master:
-                url: jdbc:mysql://139.186.77.83:3306/bly_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                username: root
-                password: bly@2025
-
-            # 从库数据源
-            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:
-                login-password:
-            filter:
-                stat:
-                    enabled: true
-                    # 慢SQL记录
-                    log-slow-sql: true
-                    slow-sql-millis: 1000
-                    merge-sql: true
-                wall:
-                    config:
-                        multi-statement-allow: true
-

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

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

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

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

+ 13 - 0
fs-common/src/main/java/com/fs/common/core/domain/SortRule.java

@@ -0,0 +1,13 @@
+package com.fs.common.core.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class SortRule {
+    private String column;
+    private String order;
+}

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

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

+ 39 - 0
fs-common/src/main/java/com/fs/common/utils/SortUtils.java

@@ -0,0 +1,39 @@
+package com.fs.common.utils;
+
+import com.fs.common.core.domain.SortRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SortUtils {
+
+    /**
+     * 解析排序规则
+     * @param sortStr   排序字符串  a(desc),b(asc),c(desc)
+     * @return list
+     */
+    public static List<SortRule> parseSort(String sortStr) {
+        List<SortRule> rules = new ArrayList<>();
+        if (StringUtils.isBlank(sortStr)) {
+            return rules;
+        }
+
+        String[] sortParts = sortStr.split(",");
+        for (String part : sortParts) {
+            String[] split = part.trim().split("\\(");
+            if (split.length != 2) {
+                continue;
+            }
+
+            String field = split[0].trim().replace(" ", ""); // 字段名:a, b, c
+
+            String direction = split[1].replace(")", "").trim(); // 方向:desc
+            if (!"asc".equalsIgnoreCase(direction) && !"desc".equalsIgnoreCase(direction)) {
+                continue;
+            }
+
+            rules.add(new SortRule(field, direction));
+        }
+        return rules;
+    }
+}

+ 47 - 7
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -3,13 +3,18 @@ package com.fs.app.controller;
 
 import com.alibaba.fastjson.JSON;
 import com.fs.app.annotation.Login;
+import com.fs.app.config.ImageStorageConfig;
 import com.fs.app.param.FsUserTagUpdateParam;
 import com.fs.app.param.FsUserUpdateParam;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.exception.ServiceException;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyTagUserService;
 import com.fs.company.service.ICompanyUserService;
+import com.fs.course.param.newfs.FsUserCourseBeMemberImageParam;
+import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
+import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
 import com.fs.store.param.h5.CourseAnalysisParam;
 import com.fs.store.domain.FsUser;
@@ -28,11 +33,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import java.io.InputStream;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
 @Slf4j
 @Api(tags = "用户会员相关接口")
@@ -40,7 +44,6 @@ import java.util.Objects;
 @RequestMapping("/app/fs/user")
 public class FsUserController extends AppBaseController {
 
-
     @Autowired
     private IFsUserService fsUserService;
 
@@ -49,9 +52,12 @@ public class FsUserController extends AppBaseController {
 
     @Autowired
     private ICompanyTagUserService companyTagUserService;
-//
-//    @Autowired
-//    private IQwTagService tagService;
+
+    @Autowired
+    private ImageStorageConfig imageConfig;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
 
     @Login
     @PostMapping("/pageList")
@@ -271,4 +277,38 @@ public class FsUserController extends AppBaseController {
         return ResponseResult.ok(fsUserService.companyUserSummaryCount(userId, companyUserId));
     }
 
+    @Login
+    @ApiOperation("会员关联绑定销售")
+    @PostMapping("/beMember")
+    public ResponseResult<Boolean> becomeMember(@Valid @RequestBody FsUserCourseBeMemberParam param) {
+        return fsUserService.becomeMember(param);
+    }
+
+    @Login
+    @PostMapping("/userImage")
+    @ApiOperation("生成分享会员海报")
+    public R createCourseImage(@RequestBody FsUserCourseBeMemberImageParam param) {
+        try {
+            //获取用户头像
+//            FsUser fsUser = fsUserService.selectFsUserById(param.getUserId());
+//            String url = "";
+//            if(fsUser != null) {
+//                url = fsUser.getAvatar();
+//            }
+            String path = imageConfig.getServerPath();
+            InputStream inputStream = fsUserCourseService.handleImage("", path);
+
+            // todo 为了给客户演示,生成会员的封面暂时随便找的课程封面,如果后期要修改的话就调整
+            String backgroundImagePath = "https://cos.his.cdwjyyh.com/fs/20240914/250af4b0041b46d1a36a34af1d5ccd54.jpg";
+            String base64Image = fsUserCourseService.createUserImageQR(param.getRealLink(), backgroundImagePath, inputStream, "png", param.getCompanyUserId());
+            // 返回Base64编码的图片字符串
+            Map<String, Object> map = new HashMap<>();
+            map.put("url", base64Image);
+            return R.ok().put("posterImage", map);
+        } catch (Exception e) {
+            log.error("生成海报失败,param:{}", param);
+            return R.error("生成海报失败!");
+        }
+    }
+
 }

+ 4 - 2
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.io.File;
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -138,11 +139,12 @@ public class FsUserCourseVideoController extends AppBaseController {
         try {
             String path = imageConfig.getServerPath();
             log.info("获取的logo图片路径,fileUrl:{}", path);
-            File file = new File(path);
+            InputStream inputStream = fsUserCourseService.handleImage("", path);
+
             if (StringUtils.isEmpty(param.getImgUrl())) {
                 return R.error(400, "课程封面不能为空!");
             }
-            String base64Image = fsUserCourseService.createCourseImageQR(realLink, param.getImgUrl(), file, "png", param.getTitle(), param.getDuration());
+            String base64Image = fsUserCourseService.createCourseImageQR(realLink, param.getImgUrl(), inputStream, "png", param.getTitle(), param.getDuration());
             // 返回Base64编码的图片字符串
             Map<String, Object> map = new HashMap<>();
             map.put("url", base64Image);

+ 101 - 0
fs-company-app/src/main/java/com/fs/app/controller/WxH5MpController.java

@@ -0,0 +1,101 @@
+package com.fs.app.controller;
+
+import cn.hutool.core.date.DateTime;
+import com.fs.app.param.FsUserLoginByMpParam;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.store.domain.FsUser;
+import com.fs.store.service.IFsUserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Api("会员-h5-微信相关接口")
+@RestController
+@RequestMapping("/app/wx/h5/mp")
+@Slf4j
+public class WxH5MpController {
+    Logger logger = LoggerFactory.getLogger(getClass());
+    @Autowired
+    private WxMpService wxMpService;
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    QwExternalContactMapper qwExternalContactMapper;
+
+
+    @ApiOperation("微信授权登录成为会员")
+    @PostMapping("/loginByMp")
+    public R loginByMp(@Valid @RequestBody FsUserLoginByMpParam param) {
+        try {
+            //获取微信用户信息
+            WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+            WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+            FsUser user = userService.selectFsUserByUnionid(wxMpUser.getUnionId());
+            if (user != null) {
+                //修改
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setMpOpenId(wxMpUser.getOpenid());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setNickname(wxMpUser.getNickname());
+                userService.updateFsUser(userMap);
+            } else {
+                //新增
+                user = new FsUser();
+                user.setNickname(wxMpUser.getNickname());
+                user.setAvatar(wxMpUser.getHeadImgUrl());
+                user.setStatus(1);
+                user.setMpOpenId(wxMpUser.getOpenid());
+                user.setUnionId(wxMpUser.getUnionId());
+                user.setCreateTime(new Date());
+                userService.insertFsUser(user);
+            }
+            log.error("用户信息user: {}, 用户id: {}", user, user.getUserId());
+            String token = jwtUtils.generateToken(user.getUserId());
+            redisCache.setCacheObject("token:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
+            Map<String, Object> map = new HashMap<>();
+            map.put("token", token);
+            map.put("user", user);
+            return R.ok(map);
+        } catch (WxErrorException e) {
+            if (e.getError().getErrorCode() == 40163) {
+                return R.error(40163, e.getError().getErrorMsg());
+            } else {
+                return R.error("授权失败," + e.getMessage());
+            }
+        }
+
+    }
+
+
+}

+ 11 - 0
fs-company-app/src/main/java/com/fs/app/param/FsUserLoginByMpParam.java

@@ -0,0 +1,11 @@
+package com.fs.app.param;
+
+import lombok.Data;
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@Data
+public class FsUserLoginByMpParam implements Serializable {
+    @NotBlank(message = "code参数缺失")
+    private String code;
+}

+ 11 - 11
fs-company/src/main/resources/application-druid.yml → fs-company-app/src/main/resources/application-druid-fby.yml

@@ -3,11 +3,11 @@ spring:
     # redis 配置
     redis:
         # 地址
-        host: 127.0.0.1
+        host: 192.168.0.4
         # 端口,默认为6379
         port: 6379
         # 密码
-        password:
+        password: Ylrztek250218!3@.
         # 连接超时时间
         timeout: 30s
         lettuce:
@@ -20,7 +20,7 @@ spring:
                 max-active: 8
                 # #连接池最大阻塞等待时间(使用负值表示没有限制)
                 max-wait: -1ms
-        database: 1
+        database: 0
     datasource:
         mysql:
             type: com.alibaba.druid.pool.DruidDataSource
@@ -28,9 +28,9 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/ylrz_scrm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://192.168.0.74:3306/fby_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
                 # 从库数据源
                 slave:
                     # 从数据源开关/默认关闭
@@ -65,8 +65,8 @@ spring:
                     allow:
                     url-pattern: /druid/*
                     # 控制台管理用户名和密码
-                    login-username:
-                    login-password:
+                    login-username: fs
+                    login-password: 123456
                 filter:
                     stat:
                         enabled: true
@@ -83,9 +83,9 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://192.168.0.74:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
                 # 初始连接数
                 initialSize: 5
                 # 最小连接池数量

+ 0 - 80
fs-company-app/src/main/resources/application-druid.yml

@@ -1,80 +0,0 @@
-# 数据源配置
-spring:
-    # redis 配置
-    redis:
-        # 地址
-        host: 127.0.0.1
-        # 端口,默认为6379
-        port: 6379
-        # 密码
-        password:
-        # 连接超时时间
-        timeout: 30s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
-        database: 0
-    datasource:
-        type: com.alibaba.druid.pool.DruidDataSource
-        driverClassName: com.mysql.cj.jdbc.Driver
-        druid:
-            # 主库数据源
-            master:
-                url: jdbc:mysql://139.186.77.83:3306/bly_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                username: root
-                password: bly@2025
-
-            # 从库数据源
-            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:
-                login-password:
-            filter:
-                stat:
-                    enabled: true
-                    # 慢SQL记录
-                    log-slow-sql: true
-                    slow-sql-millis: 1000
-                    merge-sql: true
-                wall:
-                    config:
-                        multi-statement-allow: true
-

+ 144 - 0
fs-company/src/main/java/com/fs/company/controller/CompanyRedPackageLogsController.java

@@ -0,0 +1,144 @@
+package com.fs.company.controller;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.core.security.LoginUser;
+import com.fs.core.web.service.TokenService;
+import com.fs.system.domain.SysConfig;
+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.company.domain.CompanyRedPackageLogs;
+import com.fs.company.service.ICompanyRedPackageLogsService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 公司红包余额日志Controller
+ * 
+ * @author 吴树波
+ * @date 2025-04-15
+ */
+@RestController
+@RequestMapping("/company/CompanyRedPackageLogs")
+public class CompanyRedPackageLogsController extends BaseController
+{
+    @Autowired
+    private ICompanyRedPackageLogsService companyRedPackageLogsService;
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 查询公司红包余额日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        CompanyUser user = loginUser.getUser();
+        companyRedPackageLogs.setCompanyId(user.getCompanyId());
+        List<CompanyRedPackageLogs> list = companyRedPackageLogsService.selectCompanyRedPackageLogsList(companyRedPackageLogs);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出公司红包余额日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:export')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        List<CompanyRedPackageLogs> list = companyRedPackageLogsService.selectCompanyRedPackageLogsList(companyRedPackageLogs);
+        ExcelUtil<CompanyRedPackageLogs> util = new ExcelUtil<CompanyRedPackageLogs>(CompanyRedPackageLogs.class);
+        return util.exportExcel(list, "公司红包余额日志数据");
+    }
+
+    /**
+     * 获取公司红包余额日志详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(companyRedPackageLogsService.selectCompanyRedPackageLogsById(id));
+    }
+
+    /**
+     * 新增公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:add')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        return toAjax(companyRedPackageLogsService.insertCompanyRedPackageLogs(companyRedPackageLogs));
+    }
+
+    /**
+     * 修改公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:edit')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        return toAjax(companyRedPackageLogsService.updateCompanyRedPackageLogs(companyRedPackageLogs));
+    }
+
+    /**
+     * 删除公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:remove')")
+    @Log(title = "公司红包余额日志", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(companyRedPackageLogsService.deleteCompanyRedPackageLogsByIds(ids));
+    }
+
+    /**
+     * 删除公司红包余额日志
+     */
+    @PreAuthorize("@ss.hasPermi('company:CompanyRedPackageLogs:add')")
+    @Log(title = "充值余额", businessType = BusinessType.INSERT)
+	@PostMapping("/recharge")
+    public R recharge(@RequestBody CompanyRedPackageLogs companyRedPackageLogs){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        CompanyUser user = loginUser.getUser();
+        companyRedPackageLogs.setCompanyId(user.getCompanyId());
+        companyRedPackageLogs.setCreateBy(user.getUserId().toString());
+        companyRedPackageLogsService.recharge(companyRedPackageLogs);
+        return R.ok();
+    }
+
+
+
+    @GetMapping(value = "/getMoneyPri")
+    public R getMoneyPri(){
+        return R.ok().put("data", companyRedPackageLogsService.getMoneyPri());
+    }
+    // 充值审核
+    @PostMapping(value = "/rew")
+    public R rew(@RequestBody CompanyRedPackageLogs companyRedPackageLogs){
+        companyRedPackageLogsService.rew(companyRedPackageLogs);
+        return R.ok();
+    }
+}

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

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

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

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

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

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

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

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

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

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

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

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

+ 19 - 11
fs-user-app/src/main/resources/application-druid.yml → fs-company/src/main/resources/application-druid-fby.yml

@@ -3,11 +3,11 @@ spring:
     # redis 配置
     redis:
         # 地址
-        host: 127.0.0.1
+        host: 192.168.0.4
         # 端口,默认为6379
         port: 6379
         # 密码
-        password:
+        password: Ylrztek250218!3@.
         # 连接超时时间
         timeout: 30s
         lettuce:
@@ -28,10 +28,9 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/bly_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    url: jdbc:mysql://192.168.0.74:3306/fby_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                     username: root
-                    password: bly@2025
-
+                    password: Ylrztek250218!3@.
                 # 从库数据源
                 slave:
                     # 从数据源开关/默认关闭
@@ -66,8 +65,8 @@ spring:
                     allow:
                     url-pattern: /druid/*
                     # 控制台管理用户名和密码
-                    login-username:
-                    login-password:
+                    login-username: fs
+                    login-password: 123456
                 filter:
                     stat:
                         enabled: true
@@ -84,9 +83,9 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://139.186.77.83:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtroot
-                    password: Rtroot
+                    url: jdbc:mysql://192.168.0.74:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
                 # 初始连接数
                 initialSize: 5
                 # 最小连接池数量
@@ -126,4 +125,13 @@ spring:
                     wall:
                         config:
                             multi-statement-allow: true
-
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 137 - 0
fs-qw-api/src/main/resources/application-druid-fby.yml

@@ -0,0 +1,137 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        # 地址
+        host: 192.168.0.4
+        # 端口,默认为6379
+        port: 6379
+        # 密码
+        password: Ylrztek250218!3@.
+        # 连接超时时间
+        timeout: 30s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+        database: 0
+    datasource:
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.74:3306/fby_store?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.74:3306/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: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 137 - 0
fs-qw-task/src/main/resources/application-druid-fby.yml

@@ -0,0 +1,137 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        # 地址
+        host: 192.168.0.4
+        # 端口,默认为6379
+        port: 6379
+        # 密码
+        password: Ylrztek250218!3@.
+        # 连接超时时间
+        timeout: 30s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+        database: 0
+    datasource:
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.74:3306/fby_store?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.74:3306/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: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 137 - 0
fs-qwhook/src/main/resources/application-druid-fby.yml

@@ -0,0 +1,137 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        # 地址
+        host: 192.168.0.4
+        # 端口,默认为6379
+        port: 6379
+        # 密码
+        password: Ylrztek250218!3@.
+        # 连接超时时间
+        timeout: 30s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+        database: 0
+    datasource:
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.74:3306/fby_store?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.74:3306/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: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 0 - 80
fs-qwhook/src/main/resources/application-druid.yml

@@ -1,80 +0,0 @@
-# 数据源配置
-spring:
-    # redis 配置
-    redis:
-        # 地址
-        host: 127.0.0.1
-        # 端口,默认为6379
-        port: 6379
-        # 密码
-        password:
-        # 连接超时时间
-        timeout: 30s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
-        database: 0
-    datasource:
-        type: com.alibaba.druid.pool.DruidDataSource
-        driverClassName: com.mysql.cj.jdbc.Driver
-        druid:
-            # 主库数据源
-            master:
-                url: jdbc:mysql://139.186.77.83:3306/bly_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                username: root
-                password: bly@2025
-
-            # 从库数据源
-            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:
-                login-password:
-            filter:
-                stat:
-                    enabled: true
-                    # 慢SQL记录
-                    log-slow-sql: true
-                    slow-sql-millis: 1000
-                    merge-sql: true
-                wall:
-                    config:
-                        multi-statement-allow: true
-

+ 3 - 3
fs-service-generator/src/main/resources/vm/java/controller.java.vm

@@ -18,7 +18,7 @@ import com.fs.common.enums.BusinessType;
 import ${packageName}.domain.${ClassName};
 import ${packageName}.service.I${ClassName}Service;
 import com.fs.common.utils.poi.ExcelUtil;
-#if($table.crud)
+#if($table.crud || $table.sub)
 import com.fs.common.core.page.TableDataInfo;
 #elseif($table.tree)
 #end
@@ -41,7 +41,7 @@ public class ${ClassName}Controller extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
     @GetMapping("/list")
-#if($table.crud)
+#if($table.crud || $table.sub)
     public TableDataInfo list(${ClassName} ${className})
     {
         startPage();
@@ -66,7 +66,7 @@ public class ${ClassName}Controller extends BaseController
     {
         List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
         ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
-        return util.exportExcel(list, "${businessName}");
+        return util.exportExcel(list, "${functionName}数据");
     }
 
     /**

+ 10 - 37
fs-service-generator/src/main/resources/vm/java/domain.java.vm

@@ -3,25 +3,29 @@ package ${packageName}.domain;
 #foreach ($import in $importList)
 import ${import};
 #end
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
-#if($table.crud)
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+#if($table.crud || $table.sub)
 #elseif($table.tree)
 #end
 
 /**
  * ${functionName}对象 ${tableName}
- * 
+ *
  * @author ${author}
  * @date ${datetime}
  */
-#if($table.crud)
+#if($table.crud || $table.sub)
 #set($Entity="BaseEntity")
 #elseif($table.tree)
 #set($Entity="TreeEntity")
 #end
-public class ${ClassName} extends ${Entity}
-{
-    private static final long serialVersionUID = 1L;
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ${ClassName} extends ${Entity}{
 
 #foreach ($column in $columns)
 #if(!$table.isSuperColumn($column.javaField))
@@ -46,36 +50,5 @@ public class ${ClassName} extends ${Entity}
 
 #end
 #end
-#foreach ($column in $columns)
-#if(!$table.isSuperColumn($column.javaField))
-#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
-#set($AttrName=$column.javaField)
-#else
-#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-#end
-    public void set${AttrName}($column.javaType $column.javaField) 
-    {
-        this.$column.javaField = $column.javaField;
-    }
 
-    public $column.javaType get${AttrName}() 
-    {
-        return $column.javaField;
-    }
-#end
-#end
-
-    @Override
-    public String toString() {
-        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
-#foreach ($column in $columns)
-#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
-#set($AttrName=$column.javaField)
-#else
-#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
-#end
-            .append("${column.javaField}", get${AttrName}())
-#end
-            .toString();
-    }
 }

+ 41 - 11
fs-service-generator/src/main/resources/vm/java/mapper.java.vm

@@ -1,7 +1,11 @@
 package ${packageName}.mapper;
 
 import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import ${packageName}.domain.${ClassName};
+#if($table.sub)
+import ${packageName}.domain.${subClassName};
+#end
 
 /**
  * ${functionName}Mapper接口
@@ -9,15 +13,14 @@ import ${packageName}.domain.${ClassName};
  * @author ${author}
  * @date ${datetime}
  */
-public interface ${ClassName}Mapper 
-{
+public interface ${ClassName}Mapper extends BaseMapper<${ClassName}>{
     /**
      * 查询${functionName}
      * 
-     * @param ${pkColumn.javaField} ${functionName}ID
+     * @param ${pkColumn.javaField} ${functionName}主键
      * @return ${functionName}
      */
-    public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+    ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
 
     /**
      * 查询${functionName}列表
@@ -25,7 +28,7 @@ public interface ${ClassName}Mapper
      * @param ${className} ${functionName}
      * @return ${functionName}集合
      */
-    public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+    List<${ClassName}> select${ClassName}List(${ClassName} ${className});
 
     /**
      * 新增${functionName}
@@ -33,7 +36,7 @@ public interface ${ClassName}Mapper
      * @param ${className} ${functionName}
      * @return 结果
      */
-    public int insert${ClassName}(${ClassName} ${className});
+    int insert${ClassName}(${ClassName} ${className});
 
     /**
      * 修改${functionName}
@@ -41,21 +44,48 @@ public interface ${ClassName}Mapper
      * @param ${className} ${functionName}
      * @return 结果
      */
-    public int update${ClassName}(${ClassName} ${className});
+    int update${ClassName}(${ClassName} ${className});
 
     /**
      * 删除${functionName}
      * 
-     * @param ${pkColumn.javaField} ${functionName}ID
+     * @param ${pkColumn.javaField} ${functionName}主键
      * @return 结果
      */
-    public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+    int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
 
     /**
      * 批量删除${functionName}
      * 
-     * @param ${pkColumn.javaField}s 需要删除的数据ID
+     * @param ${pkColumn.javaField}s 需要删除的数据主键集合
+     * @return 结果
+     */
+    int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+#if($table.sub)
+
+    /**
+     * 批量删除${subTable.functionName}
+     * 
+     * @param ${pkColumn.javaField}s 需要删除的数据主键集合
+     * @return 结果
+     */
+    int delete${subClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+    
+    /**
+     * 批量新增${subTable.functionName}
+     * 
+     * @param ${subclassName}List ${subTable.functionName}列表
+     * @return 结果
+     */
+    int batch${subClassName}(List<${subClassName}> ${subclassName}List);
+    
+
+    /**
+     * 通过${functionName}主键删除${subTable.functionName}信息
+     * 
+     * @param ${pkColumn.javaField} ${functionName}ID
      * @return 结果
      */
-    public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+    int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField});
+#end
 }

+ 11 - 11
fs-service-generator/src/main/resources/vm/java/service.java.vm

@@ -1,6 +1,7 @@
 package ${packageName}.service;
 
 import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
 import ${packageName}.domain.${ClassName};
 
 /**
@@ -9,15 +10,14 @@ import ${packageName}.domain.${ClassName};
  * @author ${author}
  * @date ${datetime}
  */
-public interface I${ClassName}Service 
-{
+public interface I${ClassName}Service extends IService<${ClassName}>{
     /**
      * 查询${functionName}
      * 
-     * @param ${pkColumn.javaField} ${functionName}ID
+     * @param ${pkColumn.javaField} ${functionName}主键
      * @return ${functionName}
      */
-    public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+    ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
 
     /**
      * 查询${functionName}列表
@@ -25,7 +25,7 @@ public interface I${ClassName}Service
      * @param ${className} ${functionName}
      * @return ${functionName}集合
      */
-    public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+    List<${ClassName}> select${ClassName}List(${ClassName} ${className});
 
     /**
      * 新增${functionName}
@@ -33,7 +33,7 @@ public interface I${ClassName}Service
      * @param ${className} ${functionName}
      * @return 结果
      */
-    public int insert${ClassName}(${ClassName} ${className});
+    int insert${ClassName}(${ClassName} ${className});
 
     /**
      * 修改${functionName}
@@ -41,21 +41,21 @@ public interface I${ClassName}Service
      * @param ${className} ${functionName}
      * @return 结果
      */
-    public int update${ClassName}(${ClassName} ${className});
+    int update${ClassName}(${ClassName} ${className});
 
     /**
      * 批量删除${functionName}
      * 
-     * @param ${pkColumn.javaField}s 需要删除的${functionName}ID
+     * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合
      * @return 结果
      */
-    public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+    int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
 
     /**
      * 删除${functionName}信息
      * 
-     * @param ${pkColumn.javaField} ${functionName}ID
+     * @param ${pkColumn.javaField} ${functionName}主键
      * @return 结果
      */
-    public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+    int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
 }

+ 69 - 13
fs-service-generator/src/main/resources/vm/java/serviceImpl.java.vm

@@ -7,8 +7,16 @@ import com.fs.common.utils.DateUtils;
 #break
 #end
 #end
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+#if($table.sub)
+import java.util.ArrayList;
+import com.fs.common.utils.StringUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.transaction.annotation.Transactional;
+import ${packageName}.domain.${subClassName};
+#end
 import ${packageName}.mapper.${ClassName}Mapper;
 import ${packageName}.domain.${ClassName};
 import ${packageName}.service.I${ClassName}Service;
@@ -20,21 +28,18 @@ import ${packageName}.service.I${ClassName}Service;
  * @date ${datetime}
  */
 @Service
-public class ${ClassName}ServiceImpl implements I${ClassName}Service 
-{
-    @Autowired
-    private ${ClassName}Mapper ${className}Mapper;
+public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
 
     /**
      * 查询${functionName}
      * 
-     * @param ${pkColumn.javaField} ${functionName}ID
+     * @param ${pkColumn.javaField} ${functionName}主键
      * @return ${functionName}
      */
     @Override
     public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField})
     {
-        return ${className}Mapper.select${ClassName}ById(${pkColumn.javaField});
+        return baseMapper.select${ClassName}ById(${pkColumn.javaField});
     }
 
     /**
@@ -46,7 +51,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
     @Override
     public List<${ClassName}> select${ClassName}List(${ClassName} ${className})
     {
-        return ${className}Mapper.select${ClassName}List(${className});
+        return baseMapper.select${ClassName}List(${className});
     }
 
     /**
@@ -55,6 +60,9 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
      * @param ${className} ${functionName}
      * @return 结果
      */
+#if($table.sub)
+    @Transactional
+#end
     @Override
     public int insert${ClassName}(${ClassName} ${className})
     {
@@ -63,7 +71,13 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
         ${className}.setCreateTime(DateUtils.getNowDate());
 #end
 #end
-        return ${className}Mapper.insert${ClassName}(${className});
+#if($table.sub)
+        int rows = baseMapper.insert${ClassName}(${className});
+        insert${subClassName}(${className});
+        return rows;
+#else
+        return baseMapper.insert${ClassName}(${className});
+#end
     }
 
     /**
@@ -72,6 +86,9 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
      * @param ${className} ${functionName}
      * @return 结果
      */
+#if($table.sub)
+    @Transactional
+#end
     @Override
     public int update${ClassName}(${ClassName} ${className})
     {
@@ -80,30 +97,69 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service
         ${className}.setUpdateTime(DateUtils.getNowDate());
 #end
 #end
-        return ${className}Mapper.update${ClassName}(${className});
+#if($table.sub)
+        baseMapper.delete${subClassName}By${subTableFkClassName}(${className}.getId());
+        insert${subClassName}(${className});
+#end
+        return baseMapper.update${ClassName}(${className});
     }
 
     /**
      * 批量删除${functionName}
      * 
-     * @param ${pkColumn.javaField}s 需要删除的${functionName}ID
+     * @param ${pkColumn.javaField}s 需要删除的${functionName}主键
      * @return 结果
      */
+#if($table.sub)
+    @Transactional
+#end
     @Override
     public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s)
     {
-        return ${className}Mapper.delete${ClassName}ByIds(${pkColumn.javaField}s);
+#if($table.sub)
+        baseMapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
+#end
+        return baseMapper.delete${ClassName}ByIds(${pkColumn.javaField}s);
     }
 
     /**
      * 删除${functionName}信息
      * 
-     * @param ${pkColumn.javaField} ${functionName}ID
+     * @param ${pkColumn.javaField} ${functionName}主键
      * @return 结果
      */
     @Override
     public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField})
     {
-        return ${className}Mapper.delete${ClassName}ById(${pkColumn.javaField});
+#if($table.sub)
+        baseMapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
+#end
+        return baseMapper.delete${ClassName}ById(${pkColumn.javaField});
     }
+#if($table.sub)
+
+    /**
+     * 新增${subTable.functionName}信息
+     * 
+     * @param ${className} ${functionName}对象
+     */
+    public void insert${subClassName}(${ClassName} ${className})
+    {
+        List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();
+        ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.getId();
+        if (StringUtils.isNotNull(${subclassName}List))
+        {
+            List<${subClassName}> list = new ArrayList<${subClassName}>();
+            for (${subClassName} ${subclassName} : ${subclassName}List)
+            {
+                ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});
+                list.add(${subclassName});
+            }
+            if (list.size() > 0)
+            {
+                baseMapper.batch${subClassName}(list);
+            }
+        }
+    }
+#end
 }

+ 73 - 0
fs-service-generator/src/main/resources/vm/java/sub-domain.java.vm

@@ -0,0 +1,73 @@
+package ${packageName}.domain;
+
+#foreach ($import in $subImportList)
+import ${import};
+#end
+import com.fs.common.annotation.Excel;
+
+/**
+ * ${subTable.functionName}对象 ${subTableName}
+ * 
+ * @author ${author}
+ * @date ${datetime}
+ */
+public class ${subClassName} extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+#foreach ($column in $subTable.columns)
+#if(!$table.isSuperColumn($column.javaField))
+    /** $column.columnComment */
+#if($column.list)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($parentheseIndex != -1)
+    @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+#elseif($column.javaType == 'Date')
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
+#else
+    @Excel(name = "${comment}")
+#end
+#end
+    private $column.javaType $column.javaField;
+
+#end
+#end
+#foreach ($column in $subTable.columns)
+#if(!$table.isSuperColumn($column.javaField))
+#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
+#set($AttrName=$column.javaField)
+#else
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#end
+    public void set${AttrName}($column.javaType $column.javaField) 
+    {
+        this.$column.javaField = $column.javaField;
+    }
+
+    public $column.javaType get${AttrName}() 
+    {
+        return $column.javaField;
+    }
+#end
+#end
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+#foreach ($column in $subTable.columns)
+#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
+#set($AttrName=$column.javaField)
+#else
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#end
+            .append("${column.javaField}", get${AttrName}())
+#end
+            .toString();
+    }
+}

+ 6 - 6
fs-service-generator/src/main/resources/vm/sql/sql.vm

@@ -1,22 +1,22 @@
 -- 菜单 SQL
 insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
-values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', '2018-03-01', 'admin', '2018-03-01', '${functionName}菜单');
+values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单');
 
 -- 按钮父菜单ID
 SELECT @parentId := LAST_INSERT_ID();
 
 -- 按钮 SQL
 insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
-values('${functionName}查询', @parentId, '1',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query',        '#', 'admin', '2018-03-01', 'admin', '2018-03-01', '');
+values('${functionName}查询', @parentId, '1',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query',        '#', 'admin', sysdate(), '', null, '');
 
 insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
-values('${functionName}新增', @parentId, '2',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add',          '#', 'admin', '2018-03-01', 'admin', '2018-03-01', '');
+values('${functionName}新增', @parentId, '2',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add',          '#', 'admin', sysdate(), '', null, '');
 
 insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
-values('${functionName}修改', @parentId, '3',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit',         '#', 'admin', '2018-03-01', 'admin', '2018-03-01', '');
+values('${functionName}修改', @parentId, '3',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit',         '#', 'admin', sysdate(), '', null, '');
 
 insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
-values('${functionName}删除', @parentId, '4',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove',       '#', 'admin', '2018-03-01', 'admin', '2018-03-01', '');
+values('${functionName}删除', @parentId, '4',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove',       '#', 'admin', sysdate(), '', null, '');
 
 insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
-values('${functionName}导出', @parentId, '5',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export',       '#', 'admin', '2018-03-01', 'admin', '2018-03-01', '');
+values('${functionName}导出', @parentId, '5',  '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export',       '#', 'admin', sysdate(), '', null, '');

+ 83 - 46
fs-service-generator/src/main/resources/vm/vue/index-tree.vue.vm

@@ -38,20 +38,33 @@
           <el-option label="请选择字典生成" value="" />
         </el-select>
       </el-form-item>
-#elseif($column.htmlType == "datetime")
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
       <el-form-item label="${comment}" prop="${column.javaField}">
-        <el-date-picker clearable size="small" style="width: 200px"
+        <el-date-picker clearable size="small"
           v-model="queryParams.${column.javaField}"
           type="date"
           value-format="yyyy-MM-dd"
           placeholder="选择${comment}">
         </el-date-picker>
       </el-form-item>
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+      <el-form-item label="${comment}">
+        <el-date-picker
+          v-model="daterange${AttrName}"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
 #end
 #end
 #end
       <el-form-item>
-	    <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+	    <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
@@ -60,6 +73,7 @@
       <el-col :span="1.5">
         <el-button
           type="primary"
+          plain
           icon="el-icon-plus"
           size="mini"
           @click="handleAdd"
@@ -92,10 +106,18 @@
         </template>
       </el-table-column>
 #elseif($column.list && "" != $column.dictType)
-      <el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
+      <el-table-column label="${comment}" align="center" prop="${javaField}">
+        <template slot-scope="scope">
+          <dict-tag :options="${javaField}Options" :value="scope.row.${javaField}"/>
+        </template>
+      </el-table-column>
 #elseif($column.list && "" != $javaField)
+#if(${foreach.index} == 1)
+      <el-table-column label="${comment}" prop="${javaField}" />
+#else
       <el-table-column label="${comment}" align="center" prop="${javaField}" />
 #end
+#end
 #end
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
@@ -106,6 +128,13 @@
             @click="handleUpdate(scope.row)"
             v-hasPermi="['${moduleName}:${businessName}:edit']"
           >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-plus"
+            @click="handleAdd(scope.row)"
+            v-hasPermi="['${moduleName}:${businessName}:add']"
+          >新增</el-button>
           <el-button
             size="mini"
             type="text"
@@ -139,6 +168,14 @@
         <el-form-item label="${comment}" prop="${field}">
           <el-input v-model="form.${field}" placeholder="请输入${comment}" />
         </el-form-item>
+#elseif($column.htmlType == "imageUpload")
+        <el-form-item label="${comment}">
+          <imageUpload v-model="form.${field}"/>
+        </el-form-item>
+#elseif($column.htmlType == "fileUpload")
+        <el-form-item label="${comment}">
+          <fileUpload v-model="form.${field}"/>
+        </el-form-item>
 #elseif($column.htmlType == "editor")
         <el-form-item label="${comment}">
           <editor v-model="form.${field}" :min-height="192"/>
@@ -197,7 +234,7 @@
         </el-form-item>
 #elseif($column.htmlType == "datetime")
         <el-form-item label="${comment}" prop="${field}">
-          <el-date-picker clearable size="small" style="width: 200px"
+          <el-date-picker clearable size="small"
             v-model="form.${field}"
             type="date"
             value-format="yyyy-MM-dd"
@@ -225,22 +262,10 @@
 import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
-#foreach($column in $columns)
-#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
-import Editor from '@/components/Editor';
-#break
-#end
-#end
 
 export default {
   name: "${BusinessName}",
   components: {
-#foreach($column in $columns)
-#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
-    Editor,
-#break
-#end
-#end
     Treeselect
   },
   data() {
@@ -267,6 +292,10 @@ export default {
 #if(${column.dictType} != '')
       // $comment字典
       ${column.javaField}Options: [],
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+      // $comment时间范围
+      daterange${AttrName}: [],
 #end
 #end
       // 查询参数
@@ -313,6 +342,21 @@ export default {
     /** 查询${functionName}列表 */
     getList() {
       this.loading = true;
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+      this.queryParams.params = {};
+#break
+#end
+#end
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+      if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
+        this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
+        this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
+      }
+#end
+#end
       list${BusinessName}(this.queryParams).then(response => {
         this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
         this.loading = false;
@@ -329,7 +373,7 @@ export default {
         children: node.children
       };
     },
-	/** 查询部门下拉树结构 */
+	/** 查询${functionName}下拉树结构 */
     getTreeselect() {
       list${BusinessName}().then(response => {
         this.${businessName}Options = [];
@@ -338,20 +382,6 @@ export default {
         this.${businessName}Options.push(data);
       });
     },
-#foreach ($column in $columns)
-#if(${column.dictType} != '')
-#set($parentheseIndex=$column.columnComment.indexOf("("))
-#if($parentheseIndex != -1)
-#set($comment=$column.columnComment.substring(0, $parentheseIndex))
-#else
-#set($comment=$column.columnComment)
-#end
-    // $comment字典翻译
-    ${column.javaField}Format(row, column) {
-      return this.selectDictLabel#if($column.htmlType == "checkbox")s#end(this.${column.javaField}Options, row.${column.javaField});
-    },
-#end
-#end
     // 取消按钮
     cancel() {
       this.open = false;
@@ -381,20 +411,31 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+      this.daterange${AttrName} = [];
+#end
+#end
       this.resetForm("queryForm");
       this.handleQuery();
     },
     /** 新增按钮操作 */
-    handleAdd() {
+    handleAdd(row) {
       this.reset();
-	  this.getTreeselect();
+      this.getTreeselect();
+      if (row != null && row.${treeCode}) {
+        this.form.${treeParentCode} = row.${treeCode};
+      } else {
+        this.form.${treeParentCode} = 0;
+      }
       this.open = true;
       this.title = "添加${functionName}";
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
-	  this.getTreeselect();
+      this.getTreeselect();
       if (row != null) {
         this.form.${treeParentCode} = row.${treeCode};
       }
@@ -420,19 +461,15 @@ export default {
 #end
           if (this.form.${pkColumn.javaField} != null) {
             update${BusinessName}(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("修改成功");
-                this.open = false;
-                this.getList();
-              }
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
             });
           } else {
             add${BusinessName}(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("新增成功");
-                this.open = false;
-                this.getList();
-              }
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
             });
           }
         }
@@ -449,7 +486,7 @@ export default {
         }).then(() => {
           this.getList();
           this.msgSuccess("删除成功");
-        }).catch(function() {});
+        }).catch(() => {});
     }
   }
 };

+ 158 - 47
fs-service-generator/src/main/resources/vm/vue/index.vue.vm

@@ -38,20 +38,33 @@
           <el-option label="请选择字典生成" value="" />
         </el-select>
       </el-form-item>
-#elseif($column.htmlType == "datetime")
+#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
       <el-form-item label="${comment}" prop="${column.javaField}">
-        <el-date-picker clearable size="small" style="width: 200px"
+        <el-date-picker clearable size="small"
           v-model="queryParams.${column.javaField}"
           type="date"
           value-format="yyyy-MM-dd"
           placeholder="选择${comment}">
         </el-date-picker>
       </el-form-item>
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+      <el-form-item label="${comment}">
+        <el-date-picker
+          v-model="daterange${AttrName}"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
 #end
 #end
 #end
       <el-form-item>
-        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
@@ -60,6 +73,7 @@
       <el-col :span="1.5">
         <el-button
           type="primary"
+          plain
           icon="el-icon-plus"
           size="mini"
           @click="handleAdd"
@@ -69,6 +83,7 @@
       <el-col :span="1.5">
         <el-button
           type="success"
+          plain
           icon="el-icon-edit"
           size="mini"
           :disabled="single"
@@ -79,6 +94,7 @@
       <el-col :span="1.5">
         <el-button
           type="danger"
+          plain
           icon="el-icon-delete"
           size="mini"
           :disabled="multiple"
@@ -89,16 +105,18 @@
       <el-col :span="1.5">
         <el-button
           type="warning"
+          plain
           icon="el-icon-download"
           size="mini"
+          :loading="exportLoading"
           @click="handleExport"
           v-hasPermi="['${moduleName}:${businessName}:export']"
         >导出</el-button>
       </el-col>
-	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
 #foreach($column in $columns)
 #set($javaField=$column.javaField)
@@ -117,7 +135,11 @@
         </template>
       </el-table-column>
 #elseif($column.list && "" != $column.dictType)
-      <el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
+      <el-table-column label="${comment}" align="center" prop="${javaField}">
+        <template slot-scope="scope">
+          <dict-tag :options="${javaField}Options" :value="scope.row.${javaField}"/>
+        </template>
+      </el-table-column>
 #elseif($column.list && "" != $javaField)
       <el-table-column label="${comment}" align="center" prop="${javaField}" />
 #end
@@ -141,7 +163,7 @@
         </template>
       </el-table-column>
     </el-table>
-    
+
     <pagination
       v-show="total>0"
       :total="total"
@@ -168,6 +190,14 @@
         <el-form-item label="${comment}" prop="${field}">
           <el-input v-model="form.${field}" placeholder="请输入${comment}" />
         </el-form-item>
+#elseif($column.htmlType == "imageUpload")
+        <el-form-item label="${comment}">
+          <imageUpload v-model="form.${field}"/>
+        </el-form-item>
+#elseif($column.htmlType == "fileUpload")
+        <el-form-item label="${comment}">
+          <fileUpload v-model="form.${field}"/>
+        </el-form-item>
 #elseif($column.htmlType == "editor")
         <el-form-item label="${comment}">
           <editor v-model="form.${field}" :min-height="192"/>
@@ -226,7 +256,7 @@
         </el-form-item>
 #elseif($column.htmlType == "datetime")
         <el-form-item label="${comment}" prop="${field}">
-          <el-date-picker clearable size="small" style="width: 200px"
+          <el-date-picker clearable size="small"
             v-model="form.${field}"
             type="date"
             value-format="yyyy-MM-dd"
@@ -240,6 +270,38 @@
 #end
 #end
 #end
+#end
+#if($table.sub)
+        <el-divider content-position="center">${subTable.functionName}信息</el-divider>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd${subClassName}">添加</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" icon="el-icon-delete" size="mini" @click="handleDelete${subClassName}">删除</el-button>
+          </el-col>
+        </el-row>
+        <el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
+          <el-table-column type="selection" width="50" align="center" />
+          <el-table-column label="序号" align="center" prop="index" width="50"/>
+#foreach($column in $subTable.columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk || $javaField == ${subTableFkclassName})
+#elseif($column.list && "" != $javaField)
+          <el-table-column label="$comment" prop="${javaField}">
+            <template slot-scope="scope">
+              <el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
+            </template>
+          </el-table-column>
+#end
+#end
+        </el-table>
 #end
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -252,27 +314,21 @@
 
 <script>
 import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
-#foreach($column in $columns)
-#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
-import Editor from '@/components/Editor';
-#break
-#end
-#end
 
 export default {
   name: "${BusinessName}",
-#foreach($column in $columns)
-#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
-  components: { Editor },
-#break
-#end
-#end
   data() {
     return {
       // 遮罩层
       loading: true,
+      // 导出遮罩层
+      exportLoading: false,
       // 选中数组
       ids: [],
+#if($table.sub)
+      // 子表选中数据
+      checked${subClassName}: [],
+#end
       // 非单个禁用
       single: true,
       // 非多个禁用
@@ -283,6 +339,10 @@ export default {
       total: 0,
       // ${functionName}表格数据
       ${businessName}List: [],
+#if($table.sub)
+      // ${subTable.functionName}表格数据
+      ${subclassName}List: [],
+#end
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -297,6 +357,10 @@ export default {
 #if(${column.dictType} != '')
       // $comment字典
       ${column.javaField}Options: [],
+#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+      // $comment时间范围
+      daterange${AttrName}: [],
 #end
 #end
       // 查询参数
@@ -345,26 +409,27 @@ export default {
     /** 查询${functionName}列表 */
     getList() {
       this.loading = true;
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+      this.queryParams.params = {};
+#break
+#end
+#end
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+      if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
+        this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
+        this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
+      }
+#end
+#end
       list${BusinessName}(this.queryParams).then(response => {
         this.${businessName}List = response.rows;
         this.total = response.total;
         this.loading = false;
       });
     },
-#foreach ($column in $columns)
-#if(${column.dictType} != '')
-#set($parentheseIndex=$column.columnComment.indexOf("("))
-#if($parentheseIndex != -1)
-#set($comment=$column.columnComment.substring(0, $parentheseIndex))
-#else
-#set($comment=$column.columnComment)
-#end
-    // $comment字典翻译
-    ${column.javaField}Format(row, column) {
-      return this.selectDictLabel#if($column.htmlType == "checkbox")s#end(this.${column.javaField}Options, row.${column.javaField});
-    },
-#end
-#end
     // 取消按钮
     cancel() {
       this.open = false;
@@ -386,6 +451,9 @@ export default {
 #end
 #end
       };
+#if($table.sub)
+      this.${subclassName}List = [];
+#end
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -395,6 +463,12 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
+#foreach ($column in $columns)
+#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+      this.daterange${AttrName} = [];
+#end
+#end
       this.resetForm("queryForm");
       this.handleQuery();
     },
@@ -420,6 +494,9 @@ export default {
 #if($column.htmlType == "checkbox")
         this.form.$column.javaField = this.form.${column.javaField}.split(",");
 #end
+#end
+#if($table.sub)
+        this.${subclassName}List = response.data.${subclassName}List;
 #end
         this.open = true;
         this.title = "修改${functionName}";
@@ -433,22 +510,21 @@ export default {
 #if($column.htmlType == "checkbox")
           this.form.$column.javaField = this.form.${column.javaField}.join(",");
 #end
+#end
+#if($table.sub)
+          this.form.${subclassName}List = this.${subclassName}List;
 #end
           if (this.form.${pkColumn.javaField} != null) {
             update${BusinessName}(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("修改成功");
-                this.open = false;
-                this.getList();
-              }
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
             });
           } else {
             add${BusinessName}(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("新增成功");
-                this.open = false;
-                this.getList();
-              }
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
             });
           }
         }
@@ -466,8 +542,41 @@ export default {
         }).then(() => {
           this.getList();
           this.msgSuccess("删除成功");
-        }).catch(function() {});
+        }).catch(() => {});
+    },
+#if($table.sub)
+	/** ${subTable.functionName}序号 */
+    row${subClassName}Index({ row, rowIndex }) {
+      row.index = rowIndex + 1;
+    },
+    /** ${subTable.functionName}添加按钮操作 */
+    handleAdd${subClassName}() {
+      let obj = {};
+#foreach($column in $subTable.columns)
+#if($column.pk || $column.javaField == ${subTableFkclassName})
+#elseif($column.list && "" != $javaField)
+      obj.$column.javaField = "";
+#end
+#end
+      this.${subclassName}List.push(obj);
+    },
+    /** ${subTable.functionName}删除按钮操作 */
+    handleDelete${subClassName}() {
+      if (this.checked${subClassName}.length == 0) {
+        this.msgError("请先选择要删除的${subTable.functionName}数据");
+      } else {
+        const ${subclassName}List = this.${subclassName}List;
+        const checked${subClassName} = this.checked${subClassName};
+        this.${subclassName}List = ${subclassName}List.filter(function(item) {
+          return checked${subClassName}.indexOf(item.index) == -1
+        });
+      }
     },
+    /** 复选框选中数据 */
+    handle${subClassName}SelectionChange(selection) {
+      this.checked${subClassName} = selection.map(item => item.index)
+    },
+#end
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
@@ -475,11 +584,13 @@ export default {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
           type: "warning"
-        }).then(function() {
+        }).then(() => {
+          this.exportLoading = true;
           return export${BusinessName}(queryParams);
         }).then(response => {
           this.download(response.msg);
-        }).catch(function() {});
+          this.exportLoading = false;
+        }).catch(() => {});
     }
   }
 };

+ 41 - 1
fs-service-generator/src/main/resources/vm/xml/mapper.xml.vm

@@ -9,6 +9,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="${column.javaField}"    column="${column.columnName}"    />
 #end
     </resultMap>
+#if($table.sub)
+
+    <resultMap id="${ClassName}${subClassName}Result" type="${ClassName}" extends="${ClassName}Result">
+        <collection property="${subclassName}List" notNullColumn="sub_${subTable.pkColumn.columnName}" javaType="java.util.List" resultMap="${subClassName}Result" />
+    </resultMap>
+
+    <resultMap type="${subClassName}" id="${subClassName}Result">
+#foreach ($column in $subTable.columns)
+        <result property="${column.javaField}"    column="sub_${column.columnName}"    />
+#end
+    </resultMap>
+#end
 
     <sql id="select${ClassName}Vo">
         select#foreach($column in $columns) $column.columnName#if($velocityCount != $columns.size()),#end#end from ${tableName}
@@ -46,9 +58,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
     </select>
     
-    <select id="select${ClassName}ById" parameterType="${pkColumn.javaType}" resultMap="${ClassName}Result">
+    <select id="select${ClassName}ById" parameterType="${pkColumn.javaType}" resultMap="#if($table.sub)${ClassName}${subClassName}Result#else${ClassName}Result#end">
+#if($table.crud || $table.tree)
         <include refid="select${ClassName}Vo"/>
         where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+#elseif($table.sub)
+        select#foreach($column in $columns) a.$column.columnName#if($velocityCount != $columns.size()),#end#end,
+           #foreach($column in $subTable.columns) b.$column.columnName as sub_$column.columnName#if($velocityCount != $subTable.columns.size()),#end#end
+
+        from ${tableName} a
+        left join ${subTableName} b on b.${subTableFkName} = a.${pkColumn.columnName}
+        where a.${pkColumn.columnName} = #{${pkColumn.javaField}}
+#end
     </select>
         
     <insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment) useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
@@ -91,5 +112,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{${pkColumn.javaField}}
         </foreach>
     </delete>
+#if($table.sub)
     
+    <delete id="delete${subClassName}By${subTableFkClassName}s" parameterType="String">
+        delete from ${subTableName} where ${subTableFkName} in 
+        <foreach item="${subTableFkclassName}" collection="array" open="(" separator="," close=")">
+            #{${subTableFkclassName}}
+        </foreach>
+    </delete>
+
+    <delete id="delete${subClassName}By${subTableFkClassName}" parameterType="${pkColumn.javaType}">
+        delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}}
+    </delete>
+
+    <insert id="batch${subClassName}">
+        insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($velocityCount != $subTable.columns.size()),#end#end) values
+		<foreach item="item" index="index" collection="list" separator=",">
+            (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($velocityCount != $subTable.columns.size()),#end#end)
+        </foreach>
+    </insert>
+#end
 </mapper>

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

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

+ 8 - 0
fs-service-system/src/main/java/com/fs/company/domain/Company.java

@@ -84,6 +84,8 @@ public class Company extends BaseEntity
 
     private String omsCode;
 
+    private BigDecimal redPackageMoney;
+
     public String getOmsCode() {
         return omsCode;
     }
@@ -281,5 +283,11 @@ public class Company extends BaseEntity
         return voiceApiId;
     }
 
+    public BigDecimal getRedPackageMoney() {
+        return redPackageMoney;
+    }
 
+    public void setRedPackageMoney(BigDecimal redPackageMoney) {
+        this.redPackageMoney = redPackageMoney;
+    }
 }

+ 75 - 0
fs-service-system/src/main/java/com/fs/company/domain/CompanyRedPackageLogs.java

@@ -0,0 +1,75 @@
+package com.fs.company.domain;
+
+import java.math.BigDecimal;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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;
+
+/**
+ * 公司红包余额日志对象 company_red_package_logs
+ *
+ * @author 吴树波
+ * @date 2025-04-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyRedPackageLogs extends BaseEntity{
+
+    /** id */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 销售公司 */
+    @Excel(name = "销售公司")
+    private Long companyId;
+
+    /** 操作类型;0充值1发红包 */
+    @Excel(name = "操作类型;0充值1发红包")
+    private Integer operateType;
+
+    /** 类型;0增加1减少 */
+    @Excel(name = "类型;0增加1减少")
+    private Integer type;
+
+    /** 充值金额 */
+    @Excel(name = "充值金额")
+    private BigDecimal upMoney;
+
+    /** 手续费 */
+    @Excel(name = "手续费")
+    private BigDecimal handlMoney;
+
+    /** 手续费比例 */
+    @Excel(name = "手续费比例")
+    private BigDecimal handlPri;
+
+    /** 到账金额 */
+    @Excel(name = "到账金额")
+    private BigDecimal money;
+
+    /** 状态;0待审核1审核通过2审核拒绝 */
+    @Excel(name = "状态;0待审核1审核通过2审核拒绝")
+    private Integer status;
+
+    /** 凭证照片 */
+    @Excel(name = "凭证照片")
+    private String images;
+
+    /** 拒绝愿意 */
+    @Excel(name = "拒绝愿意")
+    private String rejectMemo;
+
+    /** 拒绝图片 */
+    @Excel(name = "拒绝图片")
+    private String rejectImages;
+
+    @TableField(exist = false)
+    private String companyName;
+
+
+}

+ 9 - 0
fs-service-system/src/main/java/com/fs/company/domain/CompanyUser.java

@@ -130,6 +130,9 @@ public class CompanyUser extends BaseEntity
     /** 是否审核 */
     private Integer isAudit;
 
+    /** 会员是否默认黑名单,1-是;0-否(用于销售分享成为会员的操作) */
+    private Integer fsUserIsDefaultBlack;
+
     public Integer getIsAudit() {
         return isAudit;
     }
@@ -448,5 +451,11 @@ public class CompanyUser extends BaseEntity
         return isDel;
     }
 
+    public Integer getFsUserIsDefaultBlack() {
+        return fsUserIsDefaultBlack;
+    }
 
+    public void setStatusText(String statusText) {
+        this.statusText = statusText;
+    }
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/company/mapper/CompanyMapper.java

@@ -145,4 +145,5 @@ public interface CompanyMapper
     @Select("select company_id dictValue,company_name dictLabel from company")
     List<OptionsVO> selectAllCompanyList();
 
+    void updateMoney(@Param("id") Long id, @Param("money") BigDecimal money);
 }

+ 61 - 0
fs-service-system/src/main/java/com/fs/company/mapper/CompanyRedPackageLogsMapper.java

@@ -0,0 +1,61 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyRedPackageLogs;
+
+/**
+ * 公司红包余额日志Mapper接口
+ * 
+ * @author 吴树波
+ * @date 2025-04-15
+ */
+public interface CompanyRedPackageLogsMapper extends BaseMapper<CompanyRedPackageLogs>{
+    /**
+     * 查询公司红包余额日志
+     * 
+     * @param id 公司红包余额日志主键
+     * @return 公司红包余额日志
+     */
+    CompanyRedPackageLogs selectCompanyRedPackageLogsById(Long id);
+
+    /**
+     * 查询公司红包余额日志列表
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 公司红包余额日志集合
+     */
+    List<CompanyRedPackageLogs> selectCompanyRedPackageLogsList(CompanyRedPackageLogs companyRedPackageLogs);
+
+    /**
+     * 新增公司红包余额日志
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 结果
+     */
+    int insertCompanyRedPackageLogs(CompanyRedPackageLogs companyRedPackageLogs);
+
+    /**
+     * 修改公司红包余额日志
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 结果
+     */
+    int updateCompanyRedPackageLogs(CompanyRedPackageLogs companyRedPackageLogs);
+
+    /**
+     * 删除公司红包余额日志
+     * 
+     * @param id 公司红包余额日志主键
+     * @return 结果
+     */
+    int deleteCompanyRedPackageLogsById(Long id);
+
+    /**
+     * 批量删除公司红包余额日志
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyRedPackageLogsByIds(Long[] ids);
+}

+ 68 - 0
fs-service-system/src/main/java/com/fs/company/service/ICompanyRedPackageLogsService.java

@@ -0,0 +1,68 @@
+package com.fs.company.service;
+
+import java.math.BigDecimal;
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyRedPackageLogs;
+
+/**
+ * 公司红包余额日志Service接口
+ * 
+ * @author 吴树波
+ * @date 2025-04-15
+ */
+public interface ICompanyRedPackageLogsService extends IService<CompanyRedPackageLogs>{
+    /**
+     * 查询公司红包余额日志
+     * 
+     * @param id 公司红包余额日志主键
+     * @return 公司红包余额日志
+     */
+    CompanyRedPackageLogs selectCompanyRedPackageLogsById(Long id);
+
+    /**
+     * 查询公司红包余额日志列表
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 公司红包余额日志集合
+     */
+    List<CompanyRedPackageLogs> selectCompanyRedPackageLogsList(CompanyRedPackageLogs companyRedPackageLogs);
+
+    /**
+     * 新增公司红包余额日志
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 结果
+     */
+    int insertCompanyRedPackageLogs(CompanyRedPackageLogs companyRedPackageLogs);
+
+    /**
+     * 修改公司红包余额日志
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 结果
+     */
+    int updateCompanyRedPackageLogs(CompanyRedPackageLogs companyRedPackageLogs);
+
+    /**
+     * 批量删除公司红包余额日志
+     * 
+     * @param ids 需要删除的公司红包余额日志主键集合
+     * @return 结果
+     */
+    int deleteCompanyRedPackageLogsByIds(Long[] ids);
+
+    /**
+     * 删除公司红包余额日志信息
+     * 
+     * @param id 公司红包余额日志主键
+     * @return 结果
+     */
+    int deleteCompanyRedPackageLogsById(Long id);
+
+    void recharge(CompanyRedPackageLogs companyRedPackageLogs);
+
+    BigDecimal getMoneyPri();
+
+    void rew(CompanyRedPackageLogs entity);
+}

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

@@ -103,4 +103,6 @@ public interface ICompanyService
     List<Long> selectCompanyByOmsCode(String omsCode);
 
     Company selectCompanyByIdForUpdate(Long companyId);
+
+    void updateMoney(Long companyId, BigDecimal money);
 }

+ 141 - 0
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyRedPackageLogsServiceImpl.java

@@ -0,0 +1,141 @@
+package com.fs.company.service.impl;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.common.exception.base.BaseException;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.Company;
+import com.fs.company.service.ICompanyService;
+import com.fs.course.config.CourseConfig;
+import com.fs.system.service.ISysConfigService;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyRedPackageLogsMapper;
+import com.fs.company.domain.CompanyRedPackageLogs;
+import com.fs.company.service.ICompanyRedPackageLogsService;
+
+/**
+ * 公司红包余额日志Service业务层处理
+ * 
+ * @author 吴树波
+ * @date 2025-04-15
+ */
+@Service
+@AllArgsConstructor
+public class CompanyRedPackageLogsServiceImpl extends ServiceImpl<CompanyRedPackageLogsMapper, CompanyRedPackageLogs> implements ICompanyRedPackageLogsService {
+
+    private final ISysConfigService configService;
+    private final ICompanyService companyService;
+    /**
+     * 查询公司红包余额日志
+     * 
+     * @param id 公司红包余额日志主键
+     * @return 公司红包余额日志
+     */
+    @Override
+    public CompanyRedPackageLogs selectCompanyRedPackageLogsById(Long id)
+    {
+        return baseMapper.selectCompanyRedPackageLogsById(id);
+    }
+
+    /**
+     * 查询公司红包余额日志列表
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 公司红包余额日志
+     */
+    @Override
+    public List<CompanyRedPackageLogs> selectCompanyRedPackageLogsList(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        return baseMapper.selectCompanyRedPackageLogsList(companyRedPackageLogs);
+    }
+
+    /**
+     * 新增公司红包余额日志
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyRedPackageLogs(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        companyRedPackageLogs.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyRedPackageLogs(companyRedPackageLogs);
+    }
+
+    /**
+     * 修改公司红包余额日志
+     * 
+     * @param companyRedPackageLogs 公司红包余额日志
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyRedPackageLogs(CompanyRedPackageLogs companyRedPackageLogs)
+    {
+        companyRedPackageLogs.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyRedPackageLogs(companyRedPackageLogs);
+    }
+
+    /**
+     * 批量删除公司红包余额日志
+     * 
+     * @param ids 需要删除的公司红包余额日志主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyRedPackageLogsByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyRedPackageLogsByIds(ids);
+    }
+
+    /**
+     * 删除公司红包余额日志信息
+     * 
+     * @param id 公司红包余额日志主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyRedPackageLogsById(Long id)
+    {
+        return baseMapper.deleteCompanyRedPackageLogsById(id);
+    }
+
+    @Override
+    public void recharge(CompanyRedPackageLogs entity) {
+        if(entity.getUpMoney() == null) throw new BaseException("充值金额不能为空");
+        if(entity.getUpMoney().doubleValue() < 1) throw new BaseException("充值金额不能小于1元");
+        entity.setHandlPri(getMoneyPri());
+        entity.setHandlMoney(entity.getUpMoney().multiply(entity.getHandlPri().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)));
+        entity.setMoney(entity.getUpMoney().subtract(entity.getHandlMoney()));
+        entity.setType(0);
+        entity.setOperateType(0);
+        save(entity);
+    }
+
+    @Override
+    public BigDecimal getMoneyPri() {
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSON.parseObject(json, CourseConfig.class);
+        return config.getMoneyPri();
+    }
+
+    @Override
+    public void rew(CompanyRedPackageLogs entity) {
+        CompanyRedPackageLogs byId = getById(entity.getId());
+        if(byId != null) {
+            byId.setStatus(entity.getStatus());
+            byId.setRemark(entity.getRemark());
+            byId.setRejectMemo(entity.getRejectMemo());
+            byId.setRejectImages(entity.getRejectImages());
+            updateById(byId);
+            if(byId.getStatus() == 1){
+                companyService.updateMoney(byId.getCompanyId(), byId.getMoney());
+            }
+        }
+    }
+}

+ 6 - 0
fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -432,4 +432,10 @@ public class CompanyServiceImpl implements ICompanyService
     public Company selectCompanyByIdForUpdate(Long companyId) {
         return companyMapper.selectCompanyByIdForUpdate(companyId);
     }
+
+    @Override
+    public void updateMoney(Long companyId, BigDecimal money) {
+        Company company = selectCompanyById(companyId);
+        companyMapper.updateMoney(companyId, company.getRedPackageMoney().add(money));
+    }
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/company/vo/CompanyVO.java

@@ -73,6 +73,7 @@ public class CompanyVO implements Serializable
     private Integer limitUserCount;
 
     private Integer voiceCallerNumber;
+    private Integer redPackageMoney;
 
 
 

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

@@ -3,6 +3,7 @@ package com.fs.course.config;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Data
 public class CourseConfig implements Serializable {
@@ -20,5 +21,6 @@ public class CourseConfig implements Serializable {
     private String courseDomainName;//链接域名
     private Integer rewardType; // 奖励类型 1红包 2积分
     private Integer redPacketMode;//红包模式 1总公司 2销售公司
+    private BigDecimal moneyPri;//充值手续费百分比
 
 }

+ 13 - 170
fs-service-system/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java

@@ -1,10 +1,14 @@
 package com.fs.course.domain;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Date;
 import java.util.List;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
+import lombok.Data;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
@@ -14,6 +18,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
  * @author fs
  * @date 2025-04-15
  */
+@Data
 public class FsUserCoursePeriod
 {
     private static final long serialVersionUID = 1L;
@@ -27,19 +32,7 @@ public class FsUserCoursePeriod
 
     /** 公司id */
     @Excel(name = "公司id")
-    private Long companyId;
-
-    /** 公司id */
-    @Excel(name = "公司id,多个用逗号隔开")
-    private List<Long> companyIdList;
-
-    /** 课程id */
-    @Excel(name = "课程id")
-    private Long courseId;
-
-    /** 视频id */
-    @Excel(name = "视频id")
-    private Long videoId;
+    private String companyId;
 
     /** 训练营id */
     @Excel(name = "训练营id")
@@ -48,12 +41,12 @@ public class FsUserCoursePeriod
     /** 创建时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @Excel(name = "创建时间")
-    private Date createTime;
+    private LocalDateTime createTime;
 
     /** 更新时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @Excel(name = "更新时间")
-    private Date updateTime;
+    private LocalDateTime updateTime;
 
     /** 课程风格url */
     @Excel(name = "课程风格url")
@@ -74,163 +67,13 @@ public class FsUserCoursePeriod
     /** 开营日期-开始时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "开营日期-开始时间", width = 30, dateFormat = "yyyy-MM-dd")
-    private Date periodStartingTime;
+    private LocalDate periodStartingTime;
 
     /** 开营日期-结束时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "开营日期-结束时间", width = 30, dateFormat = "yyyy-MM-dd")
-    private Date periodEndTime;
-
-    public void setPeriodId(Long periodId)
-    {
-        this.periodId = periodId;
-    }
-
-    public Long getPeriodId()
-    {
-        return periodId;
-    }
-    public void setPeriodName(String periodName)
-    {
-        this.periodName = periodName;
-    }
-
-    public String getPeriodName()
-    {
-        return periodName;
-    }
-    public void setCompanyId(Long companyId)
-    {
-        this.companyId = companyId;
-    }
-
-    public Long getCompanyId()
-    {
-        return companyId;
-    }
-    public void setCourseId(Long courseId)
-    {
-        this.courseId = courseId;
-    }
-
-    public List<Long> getCompanyIdList() {
-        return companyIdList;
-    }
-
-    public void setCompanyIdList(List<Long> companyIdList) {
-        this.companyIdList = companyIdList;
-    }
-
-    public Long getCourseId()
-    {
-        return courseId;
-    }
-    public void setVideoId(Long videoId)
-    {
-        this.videoId = videoId;
-    }
-
-    public Long getVideoId()
-    {
-        return videoId;
-    }
-    public void setTrainingCampId(Long trainingCampId)
-    {
-        this.trainingCampId = trainingCampId;
-    }
-
-    public Long getTrainingCampId()
-    {
-        return trainingCampId;
-    }
-    public void setCourseStyle(String courseStyle)
-    {
-        this.courseStyle = courseStyle;
-    }
-
-    public Date getCreateTime() {
-        return createTime;
-    }
-
-    public Date getUpdateTime() {
-        return updateTime;
-    }
-
-    public void setCreateTime(Date createTime) {
-        this.createTime = createTime;
-    }
-
-    public void setUpdateTime(Date updateTime) {
-        this.updateTime = updateTime;
-    }
-
-    public String getCourseStyle()
-    {
-        return courseStyle;
-    }
-    public void setLiveRoomStyle(String liveRoomStyle)
-    {
-        this.liveRoomStyle = liveRoomStyle;
-    }
-
-    public String getLiveRoomStyle()
-    {
-        return liveRoomStyle;
-    }
-    public void setRedPacketGrantMethod(Long redPacketGrantMethod)
-    {
-        this.redPacketGrantMethod = redPacketGrantMethod;
-    }
-
-    public Long getRedPacketGrantMethod()
-    {
-        return redPacketGrantMethod;
-    }
-    public void setPeriodType(Long periodType)
-    {
-        this.periodType = periodType;
-    }
-
-    public Long getPeriodType()
-    {
-        return periodType;
-    }
-    public void setPeriodStartingTime(Date periodStartingTime)
-    {
-        this.periodStartingTime = periodStartingTime;
-    }
-
-    public Date getPeriodStartingTime()
-    {
-        return periodStartingTime;
-    }
-    public void setPeriodEndTime(Date periodEndTime)
-    {
-        this.periodEndTime = periodEndTime;
-    }
-
-    public Date getPeriodEndTime()
-    {
-        return periodEndTime;
-    }
-
-    @Override
-    public String toString() {
-        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-            .append("periodId", getPeriodId())
-            .append("periodName", getPeriodName())
-            .append("companyId", getCompanyId())
-            .append("courseId", getCourseId())
-            .append("videoId", getVideoId())
-            .append("trainingCampId", getTrainingCampId())
-            .append("createTime", getCreateTime())
-            .append("updateTime", getUpdateTime())
-            .append("courseStyle", getCourseStyle())
-            .append("liveRoomStyle", getLiveRoomStyle())
-            .append("redPacketGrantMethod", getRedPacketGrantMethod())
-            .append("periodType", getPeriodType())
-            .append("periodStartingTime", getPeriodStartingTime())
-            .append("periodEndTime", getPeriodEndTime())
-            .toString();
-    }
+    private LocalDate periodEndTime;
+
+    @TableField(exist = false)
+    private List<String> companyIdList;
 }

+ 78 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserCoursePeriodDays.java

@@ -0,0 +1,78 @@
+package com.fs.course.domain;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.List;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntityTow;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 营期课程对象 fs_user_course_period_days
+ *
+ * @author fs
+ * @date 2025-04-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserCoursePeriodDays extends BaseEntityTow {
+
+    /** id */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 营期ID */
+    @Excel(name = "营期ID")
+    private Long periodId;
+
+    /** 第几节课 */
+    @Excel(name = "第几节课")
+    private Integer lesson;
+
+    /** 开始时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate dayDate;
+
+    /** 开始时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime startDateTime;
+
+    /** 结束时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endDateTime;
+
+    /** 课程ID */
+    @Excel(name = "课程ID")
+    private Long courseId;
+
+    /** 视频ID */
+    @Excel(name = "视频ID")
+    private Long videoId;
+
+    /** 红包金额 */
+    @Excel(name = "红包金额")
+    private BigDecimal money;
+
+    /** 开课状态;0未开始1一开始2已结束 */
+    @Excel(name = "开课状态;0未开始1一开始2已结束")
+    private Integer status;
+
+    @TableField(exist = false)
+    private List<Long> videoIds;
+    @TableField(exist = false)
+    private String courseName;
+    @TableField(exist = false)
+    private String videoName;
+}

+ 9 - 51
fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseTrainingCamp.java

@@ -1,11 +1,13 @@
 package com.fs.course.domain;
 
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
+import lombok.Data;
 
-import java.util.Date;
+import java.time.LocalDateTime;
 
 /**
  * 会员训练营对象 fs_user_course_training_camp
@@ -13,11 +15,13 @@ import java.util.Date;
  * @author fs
  * @date 2025-04-15
  */
+@Data
+@TableName("fs_user_course_training_camp")
 public class FsUserCourseTrainingCamp
 {
-    private static final long serialVersionUID = 1L;
 
     /** id */
+    @TableId(type = IdType.AUTO)
     private Long trainingCampId;
 
     /** 训练营名称 */
@@ -31,51 +35,5 @@ public class FsUserCourseTrainingCamp
     /** 创建时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @Excel(name = "创建时间")
-    private Date createTime;
-
-    public void setTrainingCampId(Long trainingCampId)
-    {
-        this.trainingCampId = trainingCampId;
-    }
-
-    public Long getTrainingCampId()
-    {
-        return trainingCampId;
-    }
-    public void setTrainingCampName(String trainingCampName)
-    {
-        this.trainingCampName = trainingCampName;
-    }
-
-    public String getTrainingCampName()
-    {
-        return trainingCampName;
-    }
-    public void setOrderNumber(Long orderNumber)
-    {
-        this.orderNumber = orderNumber;
-    }
-
-    public Long getOrderNumber()
-    {
-        return orderNumber;
-    }
-
-    public Date getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(Date createTime) {
-        this.createTime = createTime;
-    }
-
-    @Override
-    public String toString() {
-        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
-            .append("training campId", getTrainingCampId())
-            .append("training campName", getTrainingCampName())
-            .append("orderNumber", getOrderNumber())
-            .append("createTime", getCreateTime())
-            .toString();
-    }
+    private LocalDateTime createTime;
 }

+ 15 - 0
fs-service-system/src/main/java/com/fs/course/dto/FsUserCourseTrainingCampDTO.java

@@ -0,0 +1,15 @@
+package com.fs.course.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class FsUserCourseTrainingCampDTO {
+
+    /**
+     * 训练营名称
+     */
+    @NotBlank(message = "训练营名称不能为空")
+    private String trainingCampName;
+}

+ 20 - 0
fs-service-system/src/main/java/com/fs/course/dto/FsUserCourseTrainingCampUpdateDTO.java

@@ -0,0 +1,20 @@
+package com.fs.course.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class FsUserCourseTrainingCampUpdateDTO {
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "ID不能为空")
+    private Long trainingCampId;
+    /**
+     * 训练营名称
+     */
+    @NotBlank(message = "训练营名称不能为空")
+    private String trainingCampName;
+}

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

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

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

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

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

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

+ 63 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java

@@ -0,0 +1,63 @@
+package com.fs.course.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsUserCoursePeriodDays;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * 营期课程Mapper接口
+ * 
+ * @author fs
+ * @date 2025-04-16
+ */
+public interface FsUserCoursePeriodDaysMapper extends BaseMapper<FsUserCoursePeriodDays>{
+    /**
+     * 查询营期课程
+     * 
+     * @param id 营期课程主键
+     * @return 营期课程
+     */
+    FsUserCoursePeriodDays selectFsUserCoursePeriodDaysById(Long id);
+
+    /**
+     * 查询营期课程列表
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 营期课程集合
+     */
+    List<FsUserCoursePeriodDays> selectFsUserCoursePeriodDaysList(FsUserCoursePeriodDays fsUserCoursePeriodDays);
+
+    /**
+     * 新增营期课程
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 结果
+     */
+    int insertFsUserCoursePeriodDays(FsUserCoursePeriodDays fsUserCoursePeriodDays);
+
+    /**
+     * 修改营期课程
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 结果
+     */
+    int updateFsUserCoursePeriodDays(FsUserCoursePeriodDays fsUserCoursePeriodDays);
+
+    /**
+     * 删除营期课程
+     * 
+     * @param id 营期课程主键
+     * @return 结果
+     */
+    int deleteFsUserCoursePeriodDaysById(Long id);
+
+    /**
+     * 批量删除营期课程
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserCoursePeriodDaysByIds(Long[] ids);
+}

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

@@ -3,6 +3,8 @@ package com.fs.course.mapper;
 import java.util.List;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.vo.FsUserCoursePeriodVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * 会员营期Mapper接口
@@ -65,4 +67,12 @@ public interface FsUserCoursePeriodMapper
      * @return
      */
     List<FsUserCoursePeriodVO> selectFsUserCoursePeriodPage(FsUserCoursePeriod fsUserCoursePeriod);
+
+    /**
+     * 查询训练营营期
+     * @param id    训练营ID
+     * @return  list
+     */
+    @Select("select * from fs_user_course_period where training_camp_id = #{id}")
+    List<FsUserCoursePeriod> selectFsUserCoursePeriodListByTrainingCampId(@Param("id") Long id);
 }

+ 17 - 43
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseTrainingCampMapper.java

@@ -1,7 +1,14 @@
 package com.fs.course.mapper;
 
-import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.course.domain.FsUserCourseTrainingCamp;
+import com.fs.course.vo.FsUserCourseTrainingCampVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+import java.util.Map;
 
 /**
  * 会员训练营Mapper接口
@@ -9,53 +16,20 @@ import com.fs.course.domain.FsUserCourseTrainingCamp;
  * @author fs
  * @date 2025-04-15
  */
-public interface FsUserCourseTrainingCampMapper
+public interface FsUserCourseTrainingCampMapper extends BaseMapper<FsUserCourseTrainingCamp>
 {
-    /**
-     * 查询会员训练营
-     *
-     * @param trainingCampId 会员训练营ID
-     * @return 会员训练营
-     */
-    public FsUserCourseTrainingCamp selectFsUserCourseTrainingCampById(Long trainingCampId);
-
-    /**
-     * 查询会员训练营列表
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 会员训练营集合
-     */
-    public List<FsUserCourseTrainingCamp> selectFsUserCourseTrainingCampList(FsUserCourseTrainingCamp fsUserCourseTrainingCamp);
-
-    /**
-     * 新增会员训练营
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 结果
-     */
-    public int insertFsUserCourseTrainingCamp(FsUserCourseTrainingCamp fsUserCourseTrainingCamp);
-
-    /**
-     * 修改会员训练营
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 结果
-     */
-    public int updateFsUserCourseTrainingCamp(FsUserCourseTrainingCamp fsUserCourseTrainingCamp);
 
     /**
-     * 删除会员训练营
-     *
-     * @param trainingCampId 会员训练营ID
-     * @return 结果
+     * 查询训练营列表
+     * @param params    参数
+     * @return  list
      */
-    public int deleteFsUserCourseTrainingCampById(Long trainingCampId);
+    List<FsUserCourseTrainingCampVO> selectFsUserCourseTrainingCampVOListByMap(@Param("params") Map<String, Object> params);
 
     /**
-     * 批量删除会员训练营
-     *
-     * @param trainingCampIds 需要删除的数据ID
-     * @return 结果
+     * 获取序号
+     * @return 序号
      */
-    public int deleteFsUserCourseTrainingCampByIds(Long[] trainingCampIds);
+    @Select("select ifnull(max(order_number), 0) + 1 from fs_user_course_training_camp")
+    Long getOrderNumber();
 }

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

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

+ 2 - 2
fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseAddCompanyUserParam.java

@@ -14,8 +14,8 @@ public class FsUserCourseAddCompanyUserParam implements Serializable {
     @ApiModelProperty(value = "视频id")
     private Long videoId;
 
-    @NotNull(message = "用户id不能为空")
-    @ApiModelProperty(value = "授权的用户id")
+//    @NotNull(message = "用户id不能为空")
+    @ApiModelProperty(value = "授权的用户id,不用传")
     private Long userId;
 
     @NotNull(message = "销售id不能为空")

+ 33 - 0
fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseBeMemberImageParam.java

@@ -0,0 +1,33 @@
+package com.fs.course.param.newfs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Data
+@ApiModel(value = "邀请成为会员海报入参")
+public class FsUserCourseBeMemberImageParam implements Serializable {
+
+    @ApiModelProperty(value = "授权的用户id")
+    private Long userId;
+
+    @NotNull(message = "销售id不能为空")
+    @ApiModelProperty(value = "销售id")
+    private Long companyUserId;
+
+    @NotNull(message = "公司id不能为空")
+    @ApiModelProperty(value = "公司id")
+    private Long companyId;
+
+    @ApiModelProperty(value = "标签ids,多个用逗号隔开")
+    private String tagIds;
+
+    @NotNull(message = "链接不能为空")
+    @ApiModelProperty(value = "跳转链接")
+    private String realLink;
+
+
+}

+ 2 - 1
fs-service-system/src/main/java/com/fs/course/param/newfs/FsUserCourseBeMemberParam.java

@@ -17,10 +17,11 @@ public class FsUserCourseBeMemberParam implements Serializable {
     @ApiModelProperty(value = "销售id")
     private Long companyUserId;
 
+    @NotNull(message = "公司id不能为空")
     @ApiModelProperty(value = "公司id")
     private Long companyId;
 
-    @ApiModelProperty(value = "标签ids,多个用逗号隔开")
+    @ApiModelProperty(value = "标签ids,数组格式")
     private String[] tagIds;
 
 

+ 5 - 2
fs-service-system/src/main/java/com/fs/course/param/newfs/UserCourseVideoPageParam.java

@@ -20,8 +20,11 @@ public class UserCourseVideoPageParam implements Serializable {
     @ApiModelProperty(value = "模糊搜索,通过视频名称来匹配")
     private String keyword;
 
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
+
+    @ApiModelProperty(value = "营期id")
+    private Long periodId;
 
     @ApiModelProperty(value = "公司id")
     private Long companyId;

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

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

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

@@ -78,4 +78,5 @@ public interface IFsCourseLinkService
 
     R createRoomLinkUrl(FsCourseLinkCreateParam param);
 
+    R getRealLinkH5(String link);
 }

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

@@ -65,7 +65,7 @@ public interface IFsCourseQuestionBankService
     /**
      * 企微客户答题
      */
-    R courseAnswer(FsCourseQuestionAnswerUParam param);
+    R courseAnswer(FsCourseQuestionAnswerUParam param, Boolean isH5User);
 
 
     /**

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

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

+ 66 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCoursePeriodDaysService.java

@@ -0,0 +1,66 @@
+package com.fs.course.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.course.domain.FsUserCoursePeriodDays;
+
+/**
+ * 营期课程Service接口
+ * 
+ * @author fs
+ * @date 2025-04-16
+ */
+public interface IFsUserCoursePeriodDaysService extends IService<FsUserCoursePeriodDays>{
+    /**
+     * 查询营期课程
+     * 
+     * @param id 营期课程主键
+     * @return 营期课程
+     */
+    FsUserCoursePeriodDays selectFsUserCoursePeriodDaysById(Long id);
+
+    /**
+     * 查询营期课程列表
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 营期课程集合
+     */
+    List<FsUserCoursePeriodDays> selectFsUserCoursePeriodDaysList(FsUserCoursePeriodDays fsUserCoursePeriodDays);
+
+    /**
+     * 新增营期课程
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 结果
+     */
+    int insertFsUserCoursePeriodDays(FsUserCoursePeriodDays fsUserCoursePeriodDays);
+
+    /**
+     * 修改营期课程
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 结果
+     */
+    int updateFsUserCoursePeriodDays(FsUserCoursePeriodDays fsUserCoursePeriodDays);
+
+    /**
+     * 批量删除营期课程
+     * 
+     * @param ids 需要删除的营期课程主键集合
+     * @return 结果
+     */
+    int deleteFsUserCoursePeriodDaysByIds(Long[] ids);
+
+    /**
+     * 删除营期课程信息
+     * 
+     * @param id 营期课程主键
+     * @return 结果
+     */
+    int deleteFsUserCoursePeriodDaysById(Long id);
+
+    R addCourse(FsUserCoursePeriodDays entity);
+
+    R updateListCourseData(List<FsUserCoursePeriodDays> entity);
+}

+ 25 - 2
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseService.java

@@ -1,6 +1,7 @@
 package com.fs.course.service;
 
-import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.List;
 import java.util.Map;
 
@@ -135,7 +136,7 @@ public interface IFsUserCourseService
      * @return
      * @throws Exception
      */
-    String createCourseImageQR(String url, String backgroundImagePath, File file, String outputFormat, String title, String duration) throws Exception;
+    String createCourseImageQR(String url, String backgroundImagePath, InputStream file, String outputFormat, String title, String duration) throws Exception;
 
     /**
      * 创建 分享成为会员 短链
@@ -143,4 +144,26 @@ public interface IFsUserCourseService
      * @return
      */
     R createCourseMemberSortLink(FsCourseLinkCreateParam param);
+
+    /**
+     * 获取链接图片或者本地图片
+     * @param url 链接地址
+     * @param path 本地图片路径
+     * @return
+     * @throws IOException
+     */
+    InputStream handleImage(String url, String path) throws IOException;
+
+    /**
+     * 生成分享成为会员海报
+     * @param url
+     * @param backgroundImagePath
+     * @param file
+     * @param outputFormat
+     * @param companyUserId
+     * @return
+     * @throws Exception
+     */
+    String createUserImageQR(String url, String backgroundImagePath, InputStream file, String outputFormat, Long companyUserId) throws Exception;
+
 }

+ 22 - 37
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseTrainingCampService.java

@@ -1,7 +1,14 @@
 package com.fs.course.service;
 
-import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.course.domain.FsUserCourseTrainingCamp;
+import com.fs.course.dto.FsUserCourseTrainingCampDTO;
+import com.fs.course.vo.FsUserCourseTrainingCampVO;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
 
 /**
  * 会员训练营Service接口
@@ -9,53 +16,31 @@ import com.fs.course.domain.FsUserCourseTrainingCamp;
  * @author fs
  * @date 2025-04-15
  */
-public interface IFsUserCourseTrainingCampService
+public interface IFsUserCourseTrainingCampService extends IService<FsUserCourseTrainingCamp>
 {
-    /**
-     * 查询会员训练营
-     *
-     * @param trainingCampId 会员训练营ID
-     * @return 会员训练营
-     */
-    public FsUserCourseTrainingCamp selectFsUserCourseTrainingCampById(Long trainingCampId);
-
-    /**
-     * 查询会员训练营列表
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 会员训练营集合
-     */
-    public List<FsUserCourseTrainingCamp> selectFsUserCourseTrainingCampList(FsUserCourseTrainingCamp fsUserCourseTrainingCamp);
 
     /**
-     * 新增会员训练营
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 结果
+     * 查询训练营列表
+     * @param params    参数
+     * @return  list
      */
-    public int insertFsUserCourseTrainingCamp(FsUserCourseTrainingCamp fsUserCourseTrainingCamp);
+    List<FsUserCourseTrainingCampVO> selectFsUserCourseTrainingCampVOListByMap(Map<String, Object> params);
 
     /**
-     * 修改会员训练营
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 结果
+     * 新增训练营
+     * @param params    参数
      */
-    public int updateFsUserCourseTrainingCamp(FsUserCourseTrainingCamp fsUserCourseTrainingCamp);
+    void add(FsUserCourseTrainingCampDTO params);
 
     /**
-     * 批量删除会员训练营
-     *
-     * @param trainingCampIds 需要删除的会员训练营ID
-     * @return 结果
+     * 删除训练营
+     * @param ids   ids
      */
-    public int deleteFsUserCourseTrainingCampByIds(Long[] trainingCampIds);
+    void del(Long[] ids);
 
     /**
-     * 删除会员训练营信息
-     *
-     * @param trainingCampId 会员训练营ID
-     * @return 结果
+     * 复制训练营
+     * @param id    id
      */
-    public int deleteFsUserCourseTrainingCampById(Long trainingCampId);
+    void copy(Long id);
 }

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

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

+ 53 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -232,6 +232,59 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         return R.error("短链生成失败!");
     }
 
+    @Override
+    public R getRealLinkH5(String link) {
+        try {
+            // URL解码
+            String sLink = URLDecoder.decode(link, "UTF-8");
+
+            // 从数据库中查找短链对应的真实链接
+            FsCourseLink courseLink = fsCourseLinkMapper.selectFsCourseLinkByLink(sLink);
+            if (courseLink != null) {
+                // 获取当前时间
+                Date currentTime = new Date();
+                // 获取链接的更新时间(假设这个字段代表过期时间)
+                Date updateTime = courseLink.getUpdateTime();
+
+                // 判断是否过期
+                if (currentTime.after(updateTime)) {
+                    // 链接已过期
+                    log.info("链接已过期: {}", sLink);
+                    return R.error("链接已过期").put("realLink", sLink);
+                } else {
+                    // 链接仍然有效,返回真实链接
+                    if (courseLink.getRealLink() == null) {
+                        log.error("真实链接不存在, link: {}", sLink);
+                        return R.error("真实链接不存在").put("realLink", sLink);
+                    }
+                    log.info("链接仍然有效: {}", sLink);
+                    String json = configService.selectConfigByKey("h5.course.config");
+                    CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+                    String domainName = companyUserMapper.selectDomainByUserId(courseLink.getCompanyUserId());
+                    if (StringUtils.isEmpty(domainName)){
+                        domainName = config.getRealLinkDomainName();
+                    }
+                    String realLink = domainName+courseLink.getRealLink();
+                    String imgUrl = "";
+                    return R.ok().put("realLink",realLink ).put("config",config).put("headerImg",imgUrl);
+                }
+            } else {
+                // 链接不存在或解析失败
+                log.warn("URL解析错误或链接不存在: {}", sLink);
+                return R.error("URL 解析错误或链接不存在").put("realLink", sLink);
+            }
+
+        } catch (UnsupportedEncodingException e) {
+            // URL解码异常
+            log.error("URL 解码失败: {}", e.getMessage(), e);
+            return R.error("URL 解码失败").put("realLink", link);
+        } catch (Exception e) {
+            // 捕获其他异常并记录
+            log.error("发生未知错误: {}", e.getMessage(), e);
+            return R.error("发生未知错误,请稍后再试").put("realLink", link);
+        }
+    }
+
     @Override
     public R createLinkUrlWc(FsCourseLinkCreateParam param) {
 

+ 7 - 2
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java

@@ -125,7 +125,7 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
 
     @Override
     @Transactional
-    public R courseAnswer(FsCourseQuestionAnswerUParam param) {
+    public R courseAnswer(FsCourseQuestionAnswerUParam param, Boolean isH5User) {
         FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
         if (StringUtils.isEmpty(user.getMpOpenId())){
             return R.error("未识别到领取信息");
@@ -152,7 +152,12 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
             errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),null);
 
         }else {
-            FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideo(param.getUserId(),param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
+            FsCourseWatchLog log;
+            if(isH5User){
+               log = courseWatchLogMapper.getWatchLogByFsUser(param.getVideoId(), param.getUserId());
+            } else {
+                log = courseWatchLogMapper.getWatchCourseVideo(param.getUserId(), param.getVideoId(), param.getQwUserId(), param.getQwExternalId());
+            }
             if (log==null){
                 return R.error("无记录");
             }

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

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

+ 147 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java

@@ -0,0 +1,147 @@
+package com.fs.course.service.impl;
+
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.base.BaseException;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.date.DateUtil;
+import com.fs.common.utils.date.TimeTypeEnum;
+import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.mapper.FsUserCoursePeriodMapper;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
+import com.fs.course.domain.FsUserCoursePeriodDays;
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
+
+/**
+ * 营期课程Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-04-16
+ */
+@Service
+@AllArgsConstructor
+public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCoursePeriodDaysMapper, FsUserCoursePeriodDays> implements IFsUserCoursePeriodDaysService {
+
+    private final FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
+
+    /**
+     * 查询营期课程
+     * 
+     * @param id 营期课程主键
+     * @return 营期课程
+     */
+    @Override
+    public FsUserCoursePeriodDays selectFsUserCoursePeriodDaysById(Long id)
+    {
+        return baseMapper.selectFsUserCoursePeriodDaysById(id);
+    }
+
+    /**
+     * 查询营期课程列表
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 营期课程
+     */
+    @Override
+    public List<FsUserCoursePeriodDays> selectFsUserCoursePeriodDaysList(FsUserCoursePeriodDays fsUserCoursePeriodDays)
+    {
+        return baseMapper.selectFsUserCoursePeriodDaysList(fsUserCoursePeriodDays);
+    }
+
+    /**
+     * 新增营期课程
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserCoursePeriodDays(FsUserCoursePeriodDays fsUserCoursePeriodDays)
+    {
+        fsUserCoursePeriodDays.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insert(fsUserCoursePeriodDays);
+    }
+
+    /**
+     * 修改营期课程
+     * 
+     * @param fsUserCoursePeriodDays 营期课程
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserCoursePeriodDays(FsUserCoursePeriodDays fsUserCoursePeriodDays)
+    {
+        fsUserCoursePeriodDays.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateById(fsUserCoursePeriodDays);
+    }
+
+    /**
+     * 批量删除营期课程
+     * 
+     * @param ids 需要删除的营期课程主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserCoursePeriodDaysByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsUserCoursePeriodDaysByIds(ids);
+    }
+
+    /**
+     * 删除营期课程信息
+     * 
+     * @param id 营期课程主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserCoursePeriodDaysById(Long id)
+    {
+        return baseMapper.deleteFsUserCoursePeriodDaysById(id);
+    }
+
+    @Override
+    public R addCourse(FsUserCoursePeriodDays entity) {
+        FsUserCoursePeriod period = fsUserCoursePeriodMapper.selectFsUserCoursePeriodById(entity.getPeriodId());
+        int num = count(new QueryWrapper<FsUserCoursePeriodDays>().eq("period_id", period.getPeriodId()));
+        long days;
+        if(period.getPeriodType() == 2){
+            days = 1;
+        }else{
+            days = DateUtil.differenceTime(period.getPeriodStartingTime(), period.getPeriodEndTime(), TimeTypeEnum.DAY);
+        }
+        days++;
+        if(num + entity.getVideoIds().size() > days) return R.error("课程不能超过营期范围");
+        AtomicInteger i = new AtomicInteger(0);
+        List<FsUserCoursePeriodDays> collect = entity.getVideoIds().stream().map(e -> {
+            FsUserCoursePeriodDays day = new FsUserCoursePeriodDays();
+            day.setPeriodId(entity.getPeriodId());
+            day.setLesson(num + i.getAndIncrement());
+            day.setDayDate(period.getPeriodStartingTime().plusDays(day.getLesson()));
+            day.setCourseId(entity.getCourseId());
+            day.setVideoId(e);
+            day.setCreateTime(new Date());
+            return day;
+        }).collect(Collectors.toList());
+        super.saveBatch(collect);
+        return R.ok();
+    }
+
+    @Override
+    public R updateListCourseData(List<FsUserCoursePeriodDays> entity) {
+        List<FsUserCoursePeriodDays> collect = entity.stream().map(e -> {
+            FsUserCoursePeriodDays day = new FsUserCoursePeriodDays();
+            day.setId(e.getId());
+            day.setMoney(e.getMoney());
+            return day;
+        }).collect(Collectors.toList());
+        super.updateBatchById(collect);
+        return R.ok();
+    }
+}

+ 3 - 2
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import com.fs.common.utils.DateUtils;
 import com.fs.course.vo.FsUserCoursePeriodVO;
@@ -54,7 +55,7 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     @Override
     public int insertFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod)
     {
-        fsUserCoursePeriod.setCreateTime(DateUtils.getNowDate());
+        fsUserCoursePeriod.setCreateTime(LocalDateTime.now());
         return fsUserCoursePeriodMapper.insertFsUserCoursePeriod(fsUserCoursePeriod);
     }
 
@@ -67,7 +68,7 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     @Override
     public int updateFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod)
     {
-        fsUserCoursePeriod.setUpdateTime(DateUtils.getNowDate());
+        fsUserCoursePeriod.setUpdateTime(LocalDateTime.now());
         return fsUserCoursePeriodMapper.updateFsUserCoursePeriod(fsUserCoursePeriod);
     }
 

+ 68 - 14
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -9,9 +9,8 @@ import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DictUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyTag;
-import com.fs.company.domain.CompanyTagUser;
+import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyTagMapper;
-import com.fs.company.mapper.CompanyTagUserMapper;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.*;
@@ -28,6 +27,7 @@ import com.google.zxing.EncodeHintType;
 import com.google.zxing.client.j2se.MatrixToImageWriter;
 import com.google.zxing.common.BitMatrix;
 import com.google.zxing.qrcode.QRCodeWriter;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -36,13 +36,11 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.imageio.ImageIO;
 import java.awt.*;
 import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.*;
 import java.util.List;
 
@@ -53,6 +51,7 @@ import java.util.List;
  * @date 2024-05-15
  */
 @Service
+@Slf4j
 public class FsUserCourseServiceImpl implements IFsUserCourseService
 {
     @Autowired
@@ -419,7 +418,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
 
     @Override
     public R createCourseSortLink(FsCourseLinkCreateParam param) {
-        String json = configService.selectConfigByKey("course.config");
+        String json = configService.selectConfigByKey("h5.course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
         //新增链接表信息
@@ -451,7 +450,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
 
 
     @Override
-    public String createCourseImageQR(String url, String backgroundImagePath, File file,
+    public String createCourseImageQR(String url, String backgroundImagePath, InputStream file,
                                       String outputFormat, String title, String duration) throws Exception {
         // 读取并缩放背景图片
         BufferedImage backgroundImage = createScaledBackgroundImage(backgroundImagePath);
@@ -470,7 +469,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         graphics.fillRect(0, scaledHeight, scaledWidth, bottomHeight);
 
         // 绘制推荐区域(图片和文字)
-        drawRecommendationArea(graphics, file, scaledHeight);
+        drawRecommendationArea(graphics, file, scaledHeight, "云联融智", "为您推荐");
 
         // 绘制二维码
         drawQRCode(graphics, url, scaledWidth, totalHeight);
@@ -529,7 +528,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
     /**
      * 绘制推荐区域
      */
-    private void drawRecommendationArea(Graphics2D graphics, File file, int scaledHeight) throws IOException {
+    private void drawRecommendationArea(Graphics2D graphics, InputStream file, int scaledHeight, String recommendName, String recommendContent) throws IOException {
         // 读取并绘制推荐图片
         BufferedImage referenceImg = ImageIO.read(file);
         int refHeight = 40;
@@ -546,16 +545,16 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         int textX = refX + refWidth + 10;
         int textY = refY + 25;
         graphics.setColor(new Color(51, 51, 51));
-        graphics.drawString("云联融智", textX, textY);
+        graphics.drawString(recommendName, textX, textY);
 
         // 绘制"为您推荐"
         FontMetrics metrics = graphics.getFontMetrics(recommendFont);
-        int brandWidth = metrics.stringWidth("云联融智");
+        int brandWidth = metrics.stringWidth(recommendName);
         int textX1 = textX + brandWidth + 5;
         graphics.setColor(new Color(102, 102, 102));
-        graphics.drawString("为您推荐", textX1, textY);
+        graphics.drawString(recommendContent, textX1, textY);
 //        FontMetrics metrics = graphics.getFontMetrics(recommendFont);
-        int reWidth = metrics.stringWidth("为您推荐");
+        int reWidth = metrics.stringWidth(recommendContent);
     }
 
 
@@ -665,6 +664,61 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         return R.error("生成链接失败!");
     }
 
+    @Override
+    public InputStream handleImage(String url, String path) throws IOException {
+        InputStream inputstream = null;
+        if(StringUtils.isNotEmpty(url)){
+            URL newUrl = new URL(url);
+            URLConnection connection = newUrl.openConnection();
+            // 设置用户代理,避免被服务器拒绝
+            connection.setRequestProperty("User-Agent", "Mozilla/5.0");
+            inputstream = connection.getInputStream();
+        } else {
+            File file = new File(path);
+            try{
+                inputstream = new FileInputStream(file);
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+                log.error("获取本地路径失败:{}", path);
+            }
+        }
+        log.info("头像/logo路径, url:{}, path:{}", url, path);
+        return inputstream;
+    }
+
+    @Override
+    public String createUserImageQR(String url, String backgroundImagePath, InputStream file, String outputFormat, Long companyUserId) throws Exception {
+        // 读取并缩放背景图片
+        BufferedImage backgroundImage = createScaledBackgroundImage(backgroundImagePath);
+        int scaledWidth = backgroundImage.getWidth();
+        int scaledHeight = backgroundImage.getHeight();
+
+        // 创建最终的合成图片,确保底部区域有足够空间
+        int bottomHeight = 200; // 增加底部区域高度,确保内容完全显示
+        int totalHeight = scaledHeight + bottomHeight;
+        BufferedImage combined = new BufferedImage(scaledWidth, totalHeight, BufferedImage.TYPE_INT_ARGB);
+        Graphics2D graphics = initializeGraphics(combined);
+
+        // 绘制背景和底部白色区域
+        graphics.drawImage(backgroundImage, 0, 0, null);
+        graphics.setColor(Color.WHITE);
+        graphics.fillRect(0, scaledHeight, scaledWidth, bottomHeight);
+
+        //获取销售名称
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
+
+        // 绘制推荐区域(图片和文字)
+        drawRecommendationArea(graphics, file, scaledHeight, companyUser != null ? companyUser.getUserName(): "云联融智", "邀请您成为会员");
+
+        // 绘制二维码
+        drawQRCode(graphics, url, scaledWidth, totalHeight);
+
+        graphics.dispose();
+
+        // 转换为Base64
+        return convertToBase64(combined, outputFormat);
+    }
+
     private static Calendar getExpireDay(FsCourseLinkCreateParam param, CourseConfig config, Date createTime) {
         Integer expireDuration;
         if (param.getEffectiveDuration() == null || param.getEffectiveDuration() == 0){

+ 81 - 56
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java

@@ -1,12 +1,30 @@
 package com.fs.course.service.impl;
 
-import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
-import org.springframework.beans.factory.annotation.Autowired;
+import com.fs.common.utils.bean.BeanUtils;
+import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.domain.FsUserCoursePeriodDays;
+import com.fs.course.dto.FsUserCourseTrainingCampDTO;
+import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
+import com.fs.course.mapper.FsUserCoursePeriodMapper;
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
+import com.fs.course.vo.FsUserCourseTrainingCampVO;
+import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import com.fs.course.mapper.FsUserCourseTrainingCampMapper;
 import com.fs.course.domain.FsUserCourseTrainingCamp;
 import com.fs.course.service.IFsUserCourseTrainingCampService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 会员训练营Service业务层处理
@@ -15,81 +33,88 @@ import com.fs.course.service.IFsUserCourseTrainingCampService;
  * @date 2025-04-15
  */
 @Service
-public class FsUserCourseTrainingCampServiceImpl implements IFsUserCourseTrainingCampService
+@AllArgsConstructor
+public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCourseTrainingCampMapper, FsUserCourseTrainingCamp> implements IFsUserCourseTrainingCampService
 {
-    @Autowired
-    private FsUserCourseTrainingCampMapper fsUserCourseTrainingCampMapper;
 
-    /**
-     * 查询会员训练营
-     *
-     * @param trainingCampId 会员训练营ID
-     * @return 会员训练营
-     */
-    @Override
-    public FsUserCourseTrainingCamp selectFsUserCourseTrainingCampById(Long trainingCampId)
-    {
-        return fsUserCourseTrainingCampMapper.selectFsUserCourseTrainingCampById(trainingCampId);
-    }
+    private final FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
+    private final IFsUserCoursePeriodDaysService fsUserCoursePeriodDaysService;
 
     /**
-     * 查询会员训练营列表
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 会员训练营
+     * 查询训练营列表
+     * @param params    参数
+     * @return  list
      */
     @Override
-    public List<FsUserCourseTrainingCamp> selectFsUserCourseTrainingCampList(FsUserCourseTrainingCamp fsUserCourseTrainingCamp)
-    {
-        return fsUserCourseTrainingCampMapper.selectFsUserCourseTrainingCampList(fsUserCourseTrainingCamp);
-    }
+    public List<FsUserCourseTrainingCampVO> selectFsUserCourseTrainingCampVOListByMap(Map<String, Object> params) {
+        List<FsUserCourseTrainingCampVO> voList = baseMapper.selectFsUserCourseTrainingCampVOListByMap(params);
 
-    /**
-     * 新增会员训练营
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 结果
-     */
-    @Override
-    public int insertFsUserCourseTrainingCamp(FsUserCourseTrainingCamp fsUserCourseTrainingCamp)
-    {
-        fsUserCourseTrainingCamp.setCreateTime(DateUtils.getNowDate());
-        return fsUserCourseTrainingCampMapper.insertFsUserCourseTrainingCamp(fsUserCourseTrainingCamp);
+        // TODO: 会员数处理
+        voList.forEach(v -> v.setVipCount(0));
+        return voList;
     }
 
     /**
-     * 修改会员训练营
-     *
-     * @param fsUserCourseTrainingCamp 会员训练营
-     * @return 结果
+     * 新增训练营
+     * @param params    参数
      */
     @Override
-    public int updateFsUserCourseTrainingCamp(FsUserCourseTrainingCamp fsUserCourseTrainingCamp)
-    {
-        return fsUserCourseTrainingCampMapper.updateFsUserCourseTrainingCamp(fsUserCourseTrainingCamp);
+    public void add(FsUserCourseTrainingCampDTO params) {
+        FsUserCourseTrainingCamp trainingCamp = new FsUserCourseTrainingCamp();
+        trainingCamp.setTrainingCampName(params.getTrainingCampName());
+        trainingCamp.setOrderNumber(baseMapper.getOrderNumber());
+        trainingCamp.setCreateTime(LocalDateTime.now());
+        baseMapper.insert(trainingCamp);
     }
 
     /**
-     * 批量删除会员训练营
-     *
-     * @param trainingCampIds 需要删除的会员训练营ID
-     * @return 结果
+     * 删除训练营
+     * @param ids   ids
      */
     @Override
-    public int deleteFsUserCourseTrainingCampByIds(Long[] trainingCampIds)
-    {
-        return fsUserCourseTrainingCampMapper.deleteFsUserCourseTrainingCampByIds(trainingCampIds);
+    public void del(Long[] ids) {
+        baseMapper.deleteBatchIds(Arrays.asList(ids));
     }
 
     /**
-     * 删除会员训练营信息
-     *
-     * @param trainingCampId 会员训练营ID
-     * @return 结果
+     * 复制训练营
+     * @param id    id
      */
     @Override
-    public int deleteFsUserCourseTrainingCampById(Long trainingCampId)
-    {
-        return fsUserCourseTrainingCampMapper.deleteFsUserCourseTrainingCampById(trainingCampId);
+    @Transactional(rollbackFor = Exception.class)
+    public void copy(Long id) {
+        FsUserCourseTrainingCamp fsUserCourseTrainingCamp = baseMapper.selectById(id);
+        if (Objects.isNull(fsUserCourseTrainingCamp)) {
+            throw new ServiceException("训练营不存在");
+        }
+
+        // 复制训练营
+        FsUserCourseTrainingCamp trainingCamp = new FsUserCourseTrainingCamp();
+        trainingCamp.setTrainingCampName(fsUserCourseTrainingCamp.getTrainingCampName() + " - 副本");
+        trainingCamp.setOrderNumber(baseMapper.getOrderNumber());
+        trainingCamp.setCreateTime(LocalDateTime.now());
+        baseMapper.insert(trainingCamp);
+
+        // 复制营期-课程
+        fsUserCoursePeriodMapper.selectFsUserCoursePeriodListByTrainingCampId(id).forEach(p -> {
+            // 复制营期
+            FsUserCoursePeriod copy = new FsUserCoursePeriod();
+            BeanUtils.copyProperties(p, copy, "periodId");
+            copy.setTrainingCampId(trainingCamp.getTrainingCampId());
+            fsUserCoursePeriodMapper.insertFsUserCoursePeriod(copy);
+
+            // 复制课程
+            Wrapper<FsUserCoursePeriodDays> queryWrapper = Wrappers.<FsUserCoursePeriodDays>lambdaQuery().eq(FsUserCoursePeriodDays::getPeriodId, p.getPeriodId());
+            List<FsUserCoursePeriodDays> collect = fsUserCoursePeriodDaysService.list(queryWrapper).stream().map(d -> {
+                FsUserCoursePeriodDays copyD = new FsUserCoursePeriodDays();
+                BeanUtils.copyProperties(d, copyD, "id");
+                copyD.setPeriodId(copy.getPeriodId());
+                copyD.setCreateTime(new Date());
+                return copyD;
+            }).collect(Collectors.toList());
+            if (!collect.isEmpty()) {
+                fsUserCoursePeriodDaysService.saveBatch(collect);
+            }
+        });
     }
 }

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

@@ -526,6 +526,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         try {
             FsCourseTrafficLog trafficLog = new FsCourseTrafficLog();
             trafficLog.setQwExternalContactId(param.getQwExternalId());
+            trafficLog.setUserId(param.getUserId());
             trafficLog.setCreateTime(new Date());
 //            trafficLog.setTime(new Date());
             BeanUtils.copyProperties(param, trafficLog);

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

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

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

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

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

@@ -5,6 +5,7 @@ import com.fs.common.annotation.Excel;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.time.LocalDate;
 import java.util.Date;
 
 /**
@@ -24,7 +25,7 @@ public class FsUserCoursePeriodVO implements Serializable {
 
     /** 公司id */
     @Excel(name = "公司id")
-    private Long companyId;
+    private String companyId;
 
     @Excel(name = "公司名称")
     private Long companyName;
@@ -74,11 +75,11 @@ public class FsUserCoursePeriodVO implements Serializable {
     /** 开营日期-开始时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "开营日期-开始时间", width = 30, dateFormat = "yyyy-MM-dd")
-    private Date periodStartingTime;
+    private LocalDate periodStartingTime;
 
     /** 开营日期-结束时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "开营日期-结束时间", width = 30, dateFormat = "yyyy-MM-dd")
-    private Date periodEndTime;
+    private LocalDate periodEndTime;
 
 }

+ 35 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsUserCourseTrainingCampVO.java

@@ -0,0 +1,35 @@
+package com.fs.course.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class FsUserCourseTrainingCampVO {
+    /**
+     * 主键ID
+     */
+    private Long trainingCampId;
+    /**
+     * 训练营名称
+     */
+    private String trainingCampName;
+    /**
+     * 序号
+     */
+    private Integer orderNumber;
+    /**
+     * 最近营期开课日期
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDateTime recentDate;
+    /**
+     * 营期数
+     */
+    private Integer periodCount;
+    /**
+     * 会员总数
+     */
+    private Integer vipCount;
+}

+ 10 - 4
fs-service-system/src/main/java/com/fs/course/vo/newfs/FsUserCourseListVO.java

@@ -9,10 +9,16 @@ import lombok.Data;
 @Data
 public class FsUserCourseListVO {
 
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
+//
+//    @ApiModelProperty(value = "课程名称")
+//    private String courseName;
 
-    @ApiModelProperty(value = "课程名称")
-    private String courseName;
+    @ApiModelProperty(value = "营期id")
+    private Long periodId;
+
+    @ApiModelProperty(value = "营期名称")
+    private String periodName;
 
 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 1 - 1
fs-service-system/src/main/java/com/fs/qw/service/impl/QwJsApiServiceImpl.java

@@ -63,7 +63,7 @@ public class QwJsApiServiceImpl implements IQwJsApiService {
                 useridResult = qwApiService.getQwUserid(qwConfigSignatureParam.getCode(), qwConfigSignatureParam.getCorpId(),appSecret);
             }
             //获取登录员工的OAuth2 后获取url
-
+            logger.info("获取企业当前登录人的userid"+useridResult);
             if (qwConfigSignatureParam.getIsGetUserId()==1 && useridResult.getErrcode()!=0){
                 logger.error("获取企业当前登录人的userid失败:"+useridResult.getErrmsg());
                 return R.error("获取企业当前登录人的userid:"+jsapiTicket.getErrmsg());

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

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

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff