Quellcode durchsuchen

Merge branch 'refs/heads/master' into master_yzt_20250707

xdd vor 1 Woche
Ursprung
Commit
5ebf2d6443
100 geänderte Dateien mit 1558 neuen und 986 gelöschten Zeilen
  1. 21 4
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  2. 2 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseTrainingCampController.java
  3. 10 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  4. 39 0
      fs-admin/src/main/java/com/fs/his/controller/FsUserOperationLogController.java
  5. 15 2
      fs-admin/src/main/java/com/fs/his/task/Task.java
  6. 7 0
      fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java
  7. 2 1
      fs-common/src/main/java/com/fs/common/enums/DataSourceType.java
  8. 24 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java
  9. 62 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserOperationLogController.java
  10. 18 1
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  11. 2 0
      fs-company-app/src/main/java/com/fs/app/param/LoginParam.java
  12. 17 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyConfigController.java
  13. 29 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyLoginController.java
  14. 13 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyProfileController.java
  15. 15 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  16. 2 1
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCourseTrainingCampController.java
  17. 35 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserOperationLogController.java
  18. 10 4
      fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java
  19. 7 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempController.java
  20. 6 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  21. 0 38
      fs-ipad-task/src/main/java/com/fs/app/controller/CommonController.java
  22. 6 5
      fs-ipad-task/src/main/java/com/fs/app/exception/FSExceptionHandler.java
  23. 4 2
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  24. 0 140
      fs-ipad-task/src/main/java/com/fs/app/task/QwMsgTask.java
  25. 16 12
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  26. 0 93
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsgTest.java
  27. 0 149
      fs-ipad-task/src/main/resources/application-dev.yml
  28. 0 149
      fs-ipad-task/src/main/resources/application-druid-ylrz.yml
  29. 0 151
      fs-ipad-task/src/main/resources/application-druid.yml
  30. 5 166
      fs-ipad-task/src/main/resources/application.yml
  31. 13 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  32. 70 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisQwSopController.java
  33. 18 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwSopController.java
  34. 67 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwUserController.java
  35. 69 0
      fs-qwhook/src/main/java/com/fs/app/controller/QwUserController.java
  36. 7 0
      fs-service/src/main/java/com/fs/common/param/LoginMaWxParam.java
  37. 3 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDeptMapper.java
  38. 8 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyPostMapper.java
  39. 7 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyRoleMapper.java
  40. 2 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyTagUserMapper.java
  41. 6 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  42. 15 0
      fs-service/src/main/java/com/fs/company/service/ICompanyRoleService.java
  43. 7 0
      fs-service/src/main/java/com/fs/company/service/ICompanyTagUserService.java
  44. 6 1
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  45. 10 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyRoleServiceImpl.java
  46. 32 3
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  47. 11 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyTagUserServiceImpl.java
  48. 67 2
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  49. 5 3
      fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java
  50. 2 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRealLink.java
  51. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourse.java
  52. 4 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourseComplaintRecord.java
  53. 2 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  54. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseComplaintRecordMapper.java
  55. 6 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  56. 12 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  57. 6 0
      fs-service/src/main/java/com/fs/course/param/FsUserCourseParam.java
  58. 32 0
      fs-service/src/main/java/com/fs/course/param/PeriodStatisticCountParam.java
  59. 4 1
      fs-service/src/main/java/com/fs/course/param/UserCourseComplaintRecordParam.java
  60. 7 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  61. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseComplaintRecordService.java
  62. 19 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
  63. 5 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  64. 27 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseComplaintRecordServiceImpl.java
  65. 50 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  66. 159 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  67. 28 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseStaticsCountVO.java
  68. 7 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogVO.java
  69. 3 0
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseComplaintRecordPageListVO.java
  70. 13 2
      fs-service/src/main/java/com/fs/his/domain/FsUserWx.java
  71. 3 0
      fs-service/src/main/java/com/fs/his/mapper/FsDoctorMapper.java
  72. 19 9
      fs-service/src/main/java/com/fs/his/mapper/FsUserOperationLogMapper.java
  73. 20 0
      fs-service/src/main/java/com/fs/his/param/FsUserOperationLogQueryParam.java
  74. 14 9
      fs-service/src/main/java/com/fs/his/service/IFsUserOperationLogService.java
  75. 2 0
      fs-service/src/main/java/com/fs/his/service/IFsUserWxService.java
  76. 74 11
      fs-service/src/main/java/com/fs/his/service/impl/FsUserOperationLogServiceImpl.java
  77. 11 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserWxServiceImpl.java
  78. 31 0
      fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogPageVo.java
  79. 19 0
      fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogParamVo.java
  80. 19 0
      fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogVo.java
  81. 7 1
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  82. 1 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  83. 3 4
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  84. 2 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  85. 12 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  86. 1 0
      fs-service/src/main/java/com/fs/qw/vo/QwUserVO.java
  87. 13 0
      fs-service/src/main/java/com/fs/qw/vo/UpdateSendTypeVo.java
  88. 4 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopLogs.java
  89. 8 2
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  90. 1 1
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  91. 10 2
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  92. 3 3
      fs-service/src/main/resources/application-config-druid-drk.yml
  93. 1 1
      fs-service/src/main/resources/application-config-druid-sft.yml
  94. 2 2
      fs-service/src/main/resources/application-config-druid-xfk.yml
  95. 4 0
      fs-service/src/main/resources/mapper/company/CompanyPostMapper.xml
  96. 4 0
      fs-service/src/main/resources/mapper/company/CompanyRoleMapper.xml
  97. 15 0
      fs-service/src/main/resources/mapper/company/CompanyTagUserMapper.xml
  98. 13 0
      fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml
  99. 38 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  100. 21 6
      fs-service/src/main/resources/mapper/course/FsUserCourseComplaintRecordMapper.xml

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

@@ -13,13 +13,11 @@ import com.fs.course.domain.FsUserCourseVideoRedPackage;
 import com.fs.course.param.CompanyRedPacketParam;
 import com.fs.course.param.FsBatchPeriodRedPackageParam;
 import com.fs.course.param.PeriodCountParam;
+import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.service.IFsUserCoursePeriodDaysService;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IFsUserCourseVideoRedPackageService;
-import com.fs.course.vo.FsPeriodCountVO;
-import com.fs.course.vo.FsUserCoursePeriodVO;
-import com.fs.course.vo.PeriodRedPacketVO;
-import com.fs.course.vo.UpdateCourseTimeVo;
+import com.fs.course.vo.*;
 import com.fs.his.vo.OptionsVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
@@ -29,9 +27,12 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 
 /**
@@ -45,6 +46,7 @@ import java.util.Map;
 @RequestMapping("/course/period")
 @Slf4j
 public class FsUserCoursePeriodController extends BaseController {
+
     private final IFsUserCoursePeriodService fsUserCoursePeriodService;
     private final IFsUserCoursePeriodDaysService fsUserCoursePeriodDaysService;
     private final IFsUserCourseVideoRedPackageService fsUserCourseVideoRedPackageService;
@@ -254,4 +256,19 @@ public class FsUserCoursePeriodController extends BaseController {
         return R.ok();
     }
 
+    @PostMapping("/periodCourseStatisticCount")
+    @ApiOperation("会员详情训练营数据总览")
+    public R periodCourseStatisticCount(@RequestBody PeriodStatisticCountParam param) {
+        if (param == null) {
+            return R.error("请求参数不能为空!");
+        }
+        return R.ok().put("data", fsUserCoursePeriodService.periodCourseStatisticCount(param));
+    }
+
+    @PostMapping("/periodlist")
+    public R periodList(@RequestBody PeriodStatisticCountParam param)
+    {
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsPeriodlist(param);
+        return R.ok().put("data", list);
+    }
 }

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

@@ -36,6 +36,7 @@ public class FsUserCourseTrainingCampController {
     @PreAuthorize("@ss.hasPermi('course:trainingCamp:list')")
     @GetMapping("/list")
     public AjaxResult list(@RequestParam(required = false) String trainingCampName,
+                           @RequestParam(required = false) String userId,
                            @RequestParam String scs,
                            @RequestParam(required = false, defaultValue = "1") Integer pageNum,
                            @RequestParam(required = false, defaultValue = "10") Integer pageSize)
@@ -43,6 +44,7 @@ public class FsUserCourseTrainingCampController {
         Map<String, Object> params = new HashMap<>();
         params.put("trainingCampName", trainingCampName);
         params.put("scs", SortUtils.parseSort(scs));
+        params.put("userId", userId);
 
         PageHelper.startPage(pageNum, pageSize);
         List<FsUserCourseTrainingCampVO> list = fsUserCourseTrainingCampService.selectFsUserCourseTrainingCampVOListByMap(params);

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

@@ -10,6 +10,7 @@ import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.vo.FsCourseWatchLogListVO;
 import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
@@ -139,4 +140,13 @@ public class QwFsCourseWatchLogController extends BaseController
     {
         return toAjax(fsCourseWatchLogService.deleteFsCourseWatchLogByLogIds(logIds));
     }
+
+//    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:list')")
+    @GetMapping("/listBytrainingCampId")
+    public TableDataInfo listBytrainingCampId(PeriodStatisticCountParam param)
+    {
+        startPage();
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectListBytrainingCampId(param);
+        return getDataTable(list);
+    }
 }

+ 39 - 0
fs-admin/src/main/java/com/fs/his/controller/FsUserOperationLogController.java

@@ -0,0 +1,39 @@
+package com.fs.his.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.his.domain.FsUserOperationLog;
+import com.fs.his.service.IFsUserOperationLogService;
+import com.fs.his.vo.FsUserOperationLogParamVo;
+import com.fs.his.vo.FsUserOperationLogVo;
+import com.hc.openapi.tool.fastjson.JSON;
+import org.springframework.beans.BeanUtils;
+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.ArrayList;
+import java.util.List;
+
+/**
+ * 用户操作日志对象
+ */
+@RestController
+@RequestMapping("/his/userOperationLog")
+public class FsUserOperationLogController extends BaseController {
+
+    @Autowired
+    private IFsUserOperationLogService fsUserOperationLogService;
+
+//    @PreAuthorize("@ss.hasPermi('his:userOperationLog:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserOperationLog fsUserOperationLog)
+    {
+        startPage();
+        List<FsUserOperationLogVo> list = fsUserOperationLogService.selectFsUserOperationLogByList(fsUserOperationLog);
+
+        return getDataTable(list);
+    }
+}

+ 15 - 2
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -38,8 +38,8 @@ import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
-import com.fs.qw.service.IQwAppContactWayService;
-import com.fs.qw.service.IQwExternalContactTransferLogService;
+import com.fs.qw.domain.QwCompany;
+import com.fs.qw.service.*;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
@@ -144,6 +144,19 @@ public class Task {
     ITencentCloudCosService tencentCloudCosService;
     @Autowired
     private ConfigUtil configUtil;
+    @Autowired
+    private IQwCompanyService qwCompanyService;
+    @Autowired
+    private IQwUserService qwUserService;
+
+    public void addQwUserName(){
+        QwCompany qwCompany = new QwCompany();
+        List<QwCompany> companyList = qwCompanyService.selectQwCompanyList(qwCompany);
+        for (QwCompany company : companyList) {
+            qwUserService.syncQwUserName(company.getCorpId());
+        }
+    }
+
     public void videoTranscode() throws Exception
     {
 

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

@@ -1,5 +1,6 @@
 package com.fs.qw.controller;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -21,8 +22,10 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * sop模板Controller
@@ -129,6 +132,10 @@ public class QwSopTempController extends BaseController
         qwSopTemp.setCreateBy(loginUser.getUser().getUserId().toString());
         int i = qwSopTempService.addNew(qwSopTemp);
         if(qwSopTemp.getSendType() == 11){
+            //筛选选课程数据
+            if(ObjectUtils.isNotNull(qwSopTemp.getTimeList()) && !qwSopTemp.getTimeList().isEmpty()){
+                qwSopTemp.setTimeList(new ArrayList<>(qwSopTemp.getTimeList().stream().filter(t->ObjectUtils.isNotNull(t) && !t.isEmpty()).collect(Collectors.toSet())));
+            }
             new Thread(() -> qwSopTempService.createSopTempRules(qwSopTemp)).start();
         }
         return toAjax(i);

+ 2 - 1
fs-common/src/main/java/com/fs/common/enums/DataSourceType.java

@@ -18,5 +18,6 @@ public enum DataSourceType
     /**
      * 从库
      */
-    SLAVE
+    SLAVE,
+    SopREAD
 }

+ 24 - 0
fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java

@@ -6,8 +6,11 @@ import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyTag;
+import com.fs.company.domain.CompanyTagUser;
 import com.fs.company.service.ICompanyTagService;
 import com.fs.company.service.ICompanyTagUserService;
+import com.fs.company.vo.CompanyTagUserVO;
+import com.fs.store.vo.h5.CompanyUserTagListVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -86,4 +89,25 @@ public class CompanyTagController extends AppBaseController {
         params.put("companyId", getCompanyId());
         return R.ok().put("data", companyTagUserService.selectUserListByMap(params));
     }
+
+    @Login
+    @GetMapping("/getTagByUser")
+    @ApiOperation("会员的标签")
+    public R getTagByUser(@RequestParam("userId") Long userId) {
+        CompanyTagUser map = new CompanyTagUser();
+        map.setUserId(userId);
+        map.setCompanyId(getCompanyId());
+        map.setCompanyUserId(Long.valueOf(getUserId()));
+        List<CompanyUserTagListVO> vos = companyTagUserService.getTagByUser(map);
+        StringBuilder tags = new StringBuilder();
+        if (vos != null && !vos.isEmpty()) {
+            for (CompanyUserTagListVO vo : vos) {
+                tags.append(vo.getTagName()).append(",");
+            }
+            if (tags.length() > 0) {
+                tags.deleteCharAt(tags.length() - 1);
+            }
+        }
+        return R.ok().put("data",tags.length()>0?tags.toString():null);
+    }
 }

+ 62 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserOperationLogController.java

@@ -0,0 +1,62 @@
+package com.fs.app.controller;
+
+import java.util.List;
+
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
+import com.fs.his.param.FsUserOperationLogQueryParam;
+import com.fs.his.vo.FsUserOperationLogPageVo;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+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.his.domain.FsUserOperationLog;
+import com.fs.his.service.IFsUserOperationLogService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 用户操作日志Controller
+ *
+ * @author fs
+ * @date 2025-07-04
+ */
+@RestController
+@RequestMapping("/app/operationLog")
+public class FsUserOperationLogController extends AppBaseController
+{
+    @Autowired
+    private IFsUserOperationLogService fsUserOperationLogService;
+
+    /**
+     * 查询用户操作日志列表
+     */
+    @PostMapping("/page")
+    @Login
+    public ResponseResult<PageInfo<FsUserOperationLogPageVo>> list(@RequestBody FsUserOperationLogQueryParam param)
+    {
+        if (param.getUserId() == null){
+            return ResponseResult.fail(500,"未选择会员");
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsUserOperationLogPageVo> list = fsUserOperationLogService.selectFsUserOperationLogList(param);
+        PageInfo<FsUserOperationLogPageVo> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+
+}

+ 18 - 1
fs-company-app/src/main/java/com/fs/app/controller/UserController.java

@@ -1,6 +1,7 @@
 package com.fs.app.controller;
 
 import cn.hutool.core.lang.Validator;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.app.annotation.Login;
@@ -156,6 +157,7 @@ public class UserController extends AppBaseController {
         }
 
         try {
+
             //判断用户基本规则
             CompanyUser companyUser = userService.selectUserByUserName(param.getAccount());
             if (companyUser == null) {
@@ -176,6 +178,10 @@ public class UserController extends AppBaseController {
                 throw new BaseException("此用户所属公司不存在或已停用");
             }
 
+            if (StringUtils.isNotEmpty(company.getCourseMiniAppId()) && !company.getCourseMiniAppId().equals(param.getAppId())){
+                return R.error("登录用户不属于该小程序");
+            }
+
             // 公司名称
             companyUser.setCompanyName(company.getCompanyName());
             // 岗位
@@ -676,7 +682,18 @@ public class UserController extends AppBaseController {
     @PostMapping("/audit")
     public R auditUser(@RequestParam List<Long> userIds) {
         log.debug("批量审核用户 userIds :{}", userIds);
-        companyUserService.auditUsers(userIds);
+        if(ObjectUtil.isNotNull(userIds) && !userIds.isEmpty() && userIds.size() > 20){
+            return R.error("审核用户一次性不能大于20人!");
+        }
+
+        CompanyUser companyUser = companyUserService.selectCompanyUserByUserId(Long.parseLong(getUserId()));
+        if (ObjectUtil.isEmpty(companyUser)) {
+            return R.error("当前操作用户不存在!");
+        }else if (!companyUser.isAdmin()) {
+            return R.error("没有权限");
+        }
+
+        companyUserService.auditUsers(userIds,companyUser.getCompanyId());
         return R.ok();
     }
 

+ 2 - 0
fs-company-app/src/main/java/com/fs/app/param/LoginParam.java

@@ -14,4 +14,6 @@ public class LoginParam {
     private String password;
 
     private String jpushId;
+
+    private String appId;
 }

+ 17 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyConfigController.java

@@ -1,12 +1,15 @@
 package com.fs.company.controller.company;
 
 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.utils.ServletUtils;
 import com.fs.company.domain.CompanyConfig;
 import com.fs.company.service.ICompanyConfigService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -28,6 +31,9 @@ public class CompanyConfigController extends BaseController
     @Autowired
     private TokenService tokenService;
 
+    @Autowired
+    private ISysConfigService configService;
+
 
 
     @PreAuthorize("@ss.hasPermi('company:config:edit')")
@@ -40,6 +46,17 @@ public class CompanyConfigController extends BaseController
         return R.ok("操作成功");
     }
 
+    /**
+     * 根据参数键名查询总后台参数值
+     * @param configKey
+     * @return
+     */
+    @GetMapping(value = "/getConfigByKey/{configKey}")
+    public AjaxResult getConfigByKey(@PathVariable String configKey)
+    {
+        SysConfig config=configService.selectConfigByConfigKey(configKey);
+        return AjaxResult.success(config);
+    }
 
     /**
      * 根据参数键名查询参数值

+ 29 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyLoginController.java

@@ -1,5 +1,6 @@
 package com.fs.company.controller.company;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.constant.Constants;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.redis.RedisCache;
@@ -13,6 +14,8 @@ import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.CompanyLoginService;
 import com.fs.framework.service.CompanyPermissionService;
 import com.fs.framework.service.TokenService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -43,6 +46,8 @@ public class CompanyLoginController
     private TokenService tokenService;
     @Autowired
     private RedisCache redisCache;
+    @Autowired
+    private ISysConfigService configService;
 
     /**
      * 登录方法
@@ -104,5 +109,29 @@ public class CompanyLoginController
         return AjaxResult.success(menuService.buildMenus(menus));
     }
 
+
+    /**
+     * 根据系统配置去查询是否需要首次登录,并开启校验
+     * @return
+     */
+    @GetMapping("getFirstLogin")
+    public AjaxResult getFirstLogin()
+    {
+        SysConfig config = configService.selectConfigByConfigKey("his.login");
+        if(config != null && config.getConfigValue() != null){
+            String configValue = config.getConfigValue();
+            JSONObject jsonObject = JSONObject.parseObject(configValue);
+            Boolean disabled = jsonObject.getBoolean("disabled");
+            if(disabled){
+                LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+                Long userId = redisCache.getCacheObject("newCompanyUser:" + loginUser.getUser().getCompanyId() + ":" + loginUser.getUser().getUserName());
+                if (loginUser.getUser().getUserId().equals(userId)) {
+                    return AjaxResult.success(true);
+                }
+            }
+        }
+        return AjaxResult.success(false);
+    }
+
 }
 

+ 13 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyProfileController.java

@@ -4,6 +4,7 @@ import com.fs.common.annotation.Log;
 import com.fs.common.config.FSConfig;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.ServletUtils;
@@ -14,6 +15,7 @@ import com.fs.company.service.ICompanyUserService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -34,6 +36,8 @@ public class CompanyProfileController extends BaseController
 
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private RedisCache redisCache;
 
     /**
      * 个人信息
@@ -103,11 +107,20 @@ public class CompanyProfileController extends BaseController
             // 更新缓存用户密码
             loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
             tokenService.setLoginUser(loginUser);
+            cleanFirstLogin(loginUser);
             return AjaxResult.success();
         }
         return AjaxResult.error("修改密码异常,请联系管理员");
     }
 
+    /**
+     * 去除首次登录的标志
+     * @param loginUser
+     */
+    private void cleanFirstLogin(LoginUser loginUser) {
+        redisCache.deleteObject("newCompanyUser:" + loginUser.getUser().getCompanyId() + ":" + loginUser.getUser().getUserName());
+    }
+
     /**
      * 头像上传
      */

+ 15 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java

@@ -14,6 +14,7 @@ import com.fs.course.domain.FsUserCourseVideoRedPackage;
 import com.fs.course.param.CompanyRedPacketParam;
 import com.fs.course.param.FsBatchPeriodRedPackageParam;
 import com.fs.course.param.PeriodCountParam;
+import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.service.IFsUserCoursePeriodDaysService;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IFsUserCourseVideoRedPackageService;
@@ -252,5 +253,19 @@ public class FsUserCoursePeriodController extends BaseController {
         fsUserCoursePeriodService.closePeriod(id);
         return R.ok();
     }
+    @PostMapping("/periodCourseStatisticCount")
+    @ApiOperation("会员详情训练营数据总览")
+    public R periodCourseStatisticCount(@RequestBody PeriodStatisticCountParam param) {
+        if (param == null) {
+            return R.error("请求参数不能为空!");
+        }
+        return R.ok().put("data", fsUserCoursePeriodService.periodCourseStatisticCount(param));
+    }
 
+    @PostMapping("/periodlist")
+    public R periodList(@RequestBody PeriodStatisticCountParam param)
+    {
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsPeriodlist(param);
+        return R.ok().put("data", list);
+    }
 }

+ 2 - 1
fs-company/src/main/java/com/fs/company/controller/course/FsUserCourseTrainingCampController.java

@@ -39,6 +39,7 @@ public class FsUserCourseTrainingCampController {
     @PreAuthorize("@ss.hasPermi('course:trainingCamp:list')")
     @GetMapping("/list")
     public AjaxResult list(@RequestParam(required = false) String trainingCampName,
+                           @RequestParam(required = false) String userId,
                            @RequestParam String scs,
                            @RequestParam(required = false, defaultValue = "1") Integer pageNum,
                            @RequestParam(required = false, defaultValue = "10") Integer pageSize)
@@ -48,7 +49,7 @@ public class FsUserCourseTrainingCampController {
         params.put("trainingCampName", trainingCampName);
         params.put("scs", SortUtils.parseSort(scs));
         params.put("companyId", loginUser.getCompany().getCompanyId());
-
+        params.put("userId", userId);
         PageHelper.startPage(pageNum, pageSize);
         List<FsUserCourseTrainingCampVO> list = fsUserCourseTrainingCampService.selectFsUserCourseTrainingCampVOListByMap(params);
         return AjaxResult.success(new PageInfo<>(list));

+ 35 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsUserOperationLogController.java

@@ -0,0 +1,35 @@
+package com.fs.company.controller.course;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.his.domain.FsUserOperationLog;
+import com.fs.his.service.IFsUserOperationLogService;
+import com.fs.his.vo.FsUserOperationLogVo;
+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;
+
+/**
+ * 用户操作日志对象
+ */
+@RestController
+@RequestMapping("/his/userOperationLog")
+public class FsUserOperationLogController extends BaseController {
+
+    @Autowired
+    private IFsUserOperationLogService fsUserOperationLogService;
+
+    @PreAuthorize("@ss.hasPermi('his:userOperationLog:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserOperationLog fsUserOperationLog)
+    {
+        startPage();
+        List<FsUserOperationLogVo> list = fsUserOperationLogService.selectFsUserOperationLogByList(fsUserOperationLog);
+
+        return getDataTable(list);
+    }
+}

+ 10 - 4
fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java

@@ -8,10 +8,7 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 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.param.*;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.vo.FsCourseOverVO;
 import com.fs.course.vo.FsCourseUserStatisticsListVO;
@@ -259,4 +256,13 @@ public class FsQwCourseWatchLogController extends BaseController
     {
         return toAjax(fsCourseWatchLogService.deleteFsCourseWatchLogByLogIds(logIds));
     }
+
+    //    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:list')")
+    @GetMapping("/listBytrainingCampId")
+    public TableDataInfo listBytrainingCampId(PeriodStatisticCountParam param)
+    {
+        startPage();
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectListBytrainingCampId(param);
+        return getDataTable(list);
+    }
 }

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

@@ -1,5 +1,6 @@
 package com.fs.company.controller.qw;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -21,8 +22,10 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * sop模板Controller
@@ -136,6 +139,10 @@ public class QwSopTempController extends BaseController
         qwSopTemp.setCreateBy(loginUser.getUser().getUserId().toString());
         int i = qwSopTempService.addNew(qwSopTemp);
         if(qwSopTemp.getSendType() == 11){
+            //筛选选课程数据
+            if(ObjectUtils.isNotNull(qwSopTemp.getTimeList()) && !qwSopTemp.getTimeList().isEmpty()){
+                qwSopTemp.setTimeList(new ArrayList<>(qwSopTemp.getTimeList().stream().filter(t->ObjectUtils.isNotNull(t) && !t.isEmpty()).collect(Collectors.toSet())));
+            }
             new Thread(() -> qwSopTempService.createSopTempRules(qwSopTemp)).start();
         }
         return toAjax(i);

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

@@ -35,6 +35,7 @@ import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.QwOptionsVO;
 import com.fs.qw.vo.QwUserVO;
+import com.fs.qw.vo.UpdateSendTypeVo;
 import com.fs.qwApi.domain.QwExternalContactAllListResult;
 import com.fs.qwApi.domain.inner.ExternalContact;
 import com.fs.qwApi.domain.inner.ExternalContactInfo;
@@ -826,4 +827,9 @@ public class QwUserController extends BaseController
     public R restartCloudHost(@RequestParam String serverIp) {
         return qwUserService.restartCloudHost(serverIp);
     }
+    @PostMapping("/updateSendType")
+    public R updateSendType(@RequestBody UpdateSendTypeVo vo) {
+        return qwUserService.updateSendType(vo);
+    }
+
 }

+ 0 - 38
fs-ipad-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,38 +0,0 @@
-package com.fs.app.controller;
-
-
-import com.fs.app.task.QwMsgTask;
-import com.fs.common.core.redis.RedisCache;
-import com.fs.course.mapper.FsCourseWatchLogMapper;
-import com.fs.course.service.*;
-import com.fs.qw.mapper.QwExternalContactMapper;
-import com.fs.qw.service.IQwExternalContactService;
-import com.fs.sop.mapper.QwSopLogsMapper;
-import com.fs.sop.mapper.QwSopMapper;
-import com.fs.sop.mapper.SopUserLogsMapper;
-import com.fs.sop.service.IQwSopLogsService;
-import com.fs.sop.service.IQwSopTempRulesService;
-import com.fs.sop.service.IQwSopTempVoiceService;
-import com.fs.sop.service.ISopUserLogsService;
-import com.fs.system.service.ISysConfigService;
-import io.swagger.annotations.Api;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@Api("公共接口")
-@RestController
-@RequestMapping(value="/app/common")
-@Slf4j
-public class CommonController {
-
-    @Autowired
-    private QwMsgTask qwMsgTask;
-
-    @GetMapping("/msg")
-    public void msg() throws InterruptedException {
-        qwMsgTask.sendMsg();
-    }
-}

+ 6 - 5
fs-ipad-task/src/main/java/com/fs/app/exception/FSExceptionHandler.java

@@ -5,6 +5,7 @@ package com.fs.app.exception;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
+import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.dao.DuplicateKeyException;
@@ -20,9 +21,9 @@ import org.springframework.web.servlet.NoHandlerFoundException;
 /**
  * 异常处理器
  */
+@Slf4j
 @RestControllerAdvice
 public class FSExceptionHandler {
-	private Logger logger = LoggerFactory.getLogger(getClass());
 
 	/**
 	 * 处理自定义异常
@@ -38,25 +39,25 @@ public class FSExceptionHandler {
 
 	@ExceptionHandler(NoHandlerFoundException.class)
 	public R handlerNoFoundException(Exception e) {
-		logger.error(e.getMessage(), e);
+		log.error(e.getMessage(), e);
 		return R.error(404, "路径不存在,请检查路径是否正确");
 	}
 
 	@ExceptionHandler(DuplicateKeyException.class)
 	public R handleDuplicateKeyException(DuplicateKeyException e){
-		logger.error(e.getMessage(), e);
+		log.error(e.getMessage(), e);
 		return R.error("数据库中已存在该记录");
 	}
 
 
 	@ExceptionHandler(Exception.class)
 	public R handleException(Exception e){
-		logger.error(e.getMessage(), e);
+		log.error(e.getMessage(), e);
 		return R.error();
 	}
 	@ExceptionHandler(AccessDeniedException.class)
 	public R handleAccessDeniedException(AccessDeniedException e){
-		logger.error(e.getMessage(), e);
+		log.error(e.getMessage(), e);
 		return R.error("没有权限");
 	}
 

+ 4 - 2
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -200,7 +200,10 @@ public class IpadSendServer {
     }
 
     public boolean isSendLogs(QwSopLogs qwSopLogs, QwSopCourseFinishTempSetting setting, QwUser qwUser) {
-
+        if(qwSopLogs.getSendStatus() != 3){
+            log.info("状态异常不发送:{}, LOGID: {}", qwUser.getQwUserName(), qwSopLogs.getId());
+            return false;
+        }
         if(redisCache.getCacheObject("qw:user:id:" + qwUser.getId()) != null){
             log.info("频率异常不发送:{}", qwUser.getQwUserName());
             return false;
@@ -292,7 +295,6 @@ public class IpadSendServer {
                     // 图片
                     sendImg(vo, content);
                     break;
-                case "9":
                 case "3":
                     // 链接
                     sendLink(vo, content);

+ 0 - 140
fs-ipad-task/src/main/java/com/fs/app/task/QwMsgTask.java

@@ -1,140 +0,0 @@
-package com.fs.app.task;
-
-
-import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.fs.common.utils.PubFun;
-import com.fs.common.utils.date.DateUtil;
-import com.fs.course.config.CourseConfig;
-import com.fs.qw.domain.QwUser;
-import com.fs.qw.mapper.QwUserMapper;
-import com.fs.qw.vo.QwSopLogMqVo;
-import com.fs.sop.domain.QwSopLogs;
-import com.fs.sop.mapper.QwSopLogsMapper;
-import com.fs.sop.service.IQwSopLogsService;
-import com.fs.system.domain.SysConfig;
-import com.fs.system.mapper.SysConfigMapper;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.rocketmq.spring.core.RocketMQTemplate;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
-
-@Component
-@Slf4j
-@AllArgsConstructor
-public class QwMsgTask {
-
-    private final QwUserMapper qwUserMapper;
-    private final QwSopLogsMapper qwSopLogsMapper;
-    private final IQwSopLogsService qwSopLogsService;
-    private final SysConfigMapper sysConfigMapper;
-    private final RocketMQTemplate rocketMQTemplate;
-
-
-//    @Scheduled(fixedDelay = 1000)
-    public void sendMsg() throws InterruptedException {
-        long startTimeFun = System.currentTimeMillis();
-        log.info("============= 开始处理ipad发送逻辑 =============");
-        LocalDateTime now = LocalDateTime.now();
-        LocalDateTime dateTime = LocalDateTime.now().plusMinutes(15);
-
-        SysConfig courseConfig = sysConfigMapper.selectConfigByConfigKey("course.config");
-        CourseConfig config = JSON.parseObject(courseConfig.getConfigValue(), CourseConfig.class);
-        // 消息发送延迟
-        int delay;
-        if(config.getDelayStart() == null){
-            log.info("消息发送延迟为空手动设置1000ms");
-            delay = 1000;
-        }else {
-            delay = config.getDelayStart();
-        }
-        log.info("消息发送延迟:{}ms", delay);
-        List<QwUser> qwUsers = qwUserMapper.selectList(new QueryWrapper<QwUser>().eq("send_msg_type", 1).eq("server_status", 1).eq("ipad_status", 1).isNotNull("server_id"));
-        Map<String, QwUser> qwUserMap = PubFun.listToMapByGroupObject(qwUsers, e -> e.getQwUserId() + e.getCorpId());
-        log.info("获取到需要发送ipad企微数量:{}", qwUsers.size());
-        List<List<QwUser>> groupList = new ArrayList<>();
-        PubFun.batchProcessing(10, qwUsers, groupList::add);
-
-        CountDownLatch sopGroupLatch = new CountDownLatch(groupList.size());
-        int sum = groupList.parallelStream().mapToInt(e -> processSopGroupFun(e, qwUserMap, delay, now, dateTime, sopGroupLatch)).sum();
-        // 等待所有 SOP 分组处理完成
-        sopGroupLatch.await();
-        long endTime = System.currentTimeMillis();
-        log.info("============= 处理结束ipad发送逻辑:{}ms   报错记录:{}条=============", endTime - startTimeFun, sum);
-    }
-
-
-    @Async("sopIpadTaskExecutor")
-    @Retryable(
-            value = {Exception.class},
-            maxAttempts = 3,
-            backoff = @Backoff(delay = 2000)
-    )
-    public int processSopGroupFun(List<QwUser> qwUserList, Map<String, QwUser> qwUserMap, int delay, LocalDateTime now, LocalDateTime dateTime, CountDownLatch latch) {
-        try {
-            return processSopGroupAsync(qwUserList, qwUserMap, delay, dateTime, now);
-        } catch (Exception e) {
-            log.error("处理IPAD代发送错误", e);
-        } finally {
-            latch.countDown();
-        }
-        return 0;
-    }
-    public int processSopGroupAsync(List<QwUser> qwUserList, Map<String, QwUser> qwUserMap, int delay, LocalDateTime now, LocalDateTime dateTime) {
-//        log.info("开始分批处理企微:{}", qwUserList.size());
-        List<Long> qwUserIds = PubFun.listToNewList(qwUserList, QwUser::getId);
-        List<QwSopLogs> qwSopLogs = qwSopLogsMapper.selectByQwUserIdIn(qwUserIds, DateUtil.formatLocalDateTime(dateTime));
-//        log.info("获取到企微:{}代发送记录条数:{}", PubFun.listToNewList(qwUserList, QwUser::getQwUserName), qwSopLogs.size());
-        Map<String, List<QwSopLogs>> qwSopLogMap = PubFun.listToMapByGroupList(qwSopLogs, QwSopLogs::getQwUserid);
-        int num = 0;
-        qwSopLogMap.forEach((k, v) -> {
-            AtomicLong i = new AtomicLong(1);
-            AtomicLong lastTime = new AtomicLong(0);
-            v.stream().filter(e -> qwUserMap.containsKey(e.getQwUserid() + e.getCorpId())).sorted(Comparator.comparing(e -> DateUtil.toInstant(DateUtil.stringToLocalDateTime(e.getSendTime())) + e.getSort())).forEach(e -> {
-                long darkTime = delay * i.getAndIncrement();
-                LocalDateTime startTime = DateUtil.stringToLocalDateTime(e.getSendTime());
-                long between = ChronoUnit.MILLIS.between(now, startTime);
-                if(between < 0) between = 0;
-//                if(lastTime.get() + darkTime + delay < between){
-//                    darkTime = between;
-//                }else{
-                    darkTime += between;
-//                }
-                e.setSend(true);
-
-                rocketMQTemplate.syncSendDelayTimeMills("ipad-send-new", JSON.toJSONString(QwSopLogMqVo.builder().id(e.getId()).build()), darkTime);
-                lastTime.set(darkTime);
-                e.setMs(darkTime);
-            });
-        });
-        List<String> saveBath = qwSopLogs.stream().filter(QwSopLogs::isSend).map(QwSopLogs::getId).collect(Collectors.toList());
-        if(!saveBath.isEmpty()){
-            log.info("企微:{}保存发送记录条数:{}", PubFun.listToNewList(qwUserList, QwUser::getQwUserName), saveBath.size());
-//                qwSopLogsMapper.batchUpdateQwSopLogsIpadSendStatus(saveBath);
-
-            qwSopLogsService.updateBatch(qwSopLogs.stream().map(e -> {
-                QwSopLogs l = new QwSopLogs();
-                l.setId(e.getId());
-                l.setSendStatus(6L);
-                l.setMs(e.getMs());
-                return l;
-            }).collect(Collectors.toList()));
-            return qwSopLogs.size();
-        }
-        return 0;
-    }
-}

+ 16 - 12
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -14,7 +14,6 @@ import com.fs.qw.mapper.QwIpadServerMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.impl.AsyncSopTestService;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
-import com.fs.qw.vo.QwSopTempSetting;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.service.IQwSopLogsService;
@@ -29,8 +28,9 @@ import org.springframework.util.StringUtils;
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
 import java.util.*;
-import java.util.concurrent.*;
-import java.util.function.Function;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 @Component
@@ -42,6 +42,7 @@ public class SendMsg {
     private final IpadSendServer sendServer;
     private final SysConfigMapper sysConfigMapper;
     private final IQwSopLogsService qwSopLogsService;
+    private final AsyncSopTestService asyncSopTestService;
     private final RedisCacheT<Long> redisCache;
     private final QwIpadServerMapper qwIpadServerMapper;
 
@@ -51,12 +52,13 @@ public class SendMsg {
     private final Map<Long, Long> qwMap = new ConcurrentHashMap<>();
 
 
-    public SendMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, RedisCacheT<Long> redisCache, QwIpadServerMapper qwIpadServerMapper) {
+    public SendMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, AsyncSopTestService asyncSopTestService, RedisCacheT<Long> redisCache, QwIpadServerMapper qwIpadServerMapper) {
         this.qwUserMapper = qwUserMapper;
         this.qwSopLogsMapper = qwSopLogsMapper;
         this.sendServer = sendServer;
         this.sysConfigMapper = sysConfigMapper;
         this.qwSopLogsService = qwSopLogsService;
+        this.asyncSopTestService = asyncSopTestService;
         this.redisCache = redisCache;
         this.qwIpadServerMapper = qwIpadServerMapper;
     }
@@ -86,7 +88,7 @@ public class SendMsg {
         qwUserList.clear();
     }
 
-    @Scheduled(fixedRate = 10000) // 每10秒执行一次
+    @Scheduled(fixedRate = 20000) // 每10秒执行一次
     public void sendMsg2() {
         log.info("执行日志:{}", LocalDateTime.now());
         if (StringUtils.isEmpty(groupNo)) {
@@ -108,10 +110,9 @@ public class SendMsg {
         }
         Map<String, CourseMaConfig> miniMap = getMiniMap();
 
-        List<QwUser> qwUsers = getQwUserList();
+        List<QwUser> qwUsers = getQwUserList().stream().parallel().filter(e -> !qwMap.containsKey(e.getId())).collect(Collectors.toList());
         qwUsers.parallelStream().forEach(e -> {
             if (qwMap.putIfAbsent(e.getId(), System.currentTimeMillis()) == null) {
-//                log.info("提交销售任务:{}", e.getQwUserName());
                 // 判断企微是否发送消息,是否为ipad发送并且判断是否真实在线
                 new Thread(() -> {
                     try {
@@ -127,6 +128,11 @@ public class SendMsg {
     }
 
     private void processUser(QwUser qwUser, int delayStart, int delayEnd, Map<String, CourseMaConfig> miniMap) {
+        if (!qwMap.containsKey(qwUser.getId())) {
+            log.warn("用户:{}已在处理中,跳过重复执行", qwUser.getQwUserName());
+            return;
+        }
+        qwMap.put(qwUser.getId(), System.currentTimeMillis());
         long start1 = System.currentTimeMillis();
         List<QwSopLogs> qwSopLogList = qwSopLogsMapper.selectByQwUserId(qwUser.getId());
         if(qwSopLogList.isEmpty()){
@@ -138,7 +144,7 @@ public class SendMsg {
             qwMap.remove(user.getId());
             return;
         }
-        log.info("销售:{}, 消息:{}, 耗时: {}", user.getQwUserName(), qwSopLogList.size(), end1 - start1);
+        log.info("销售:{}, 消息:{}, 耗时: {}, 时间:{}", user.getQwUserName(), qwSopLogList.size(), end1 - start1, qwMap.get(qwUser.getId()));
         long start3 = System.currentTimeMillis();
         for (QwSopLogs qwSopLogs : qwSopLogList) {
 
@@ -200,10 +206,8 @@ public class SendMsg {
             updateQwSop.setContentJson(JSON.toJSONString(setting));
 
             long end2 = System.currentTimeMillis();
-            new Thread(() -> {
-                int i = qwSopLogsService.updateQwSopLogsSendType(updateQwSop);
-                log.info("销售:{}, 修改条数{}, 发送方消息完成:{}, 耗时: {}", user.getQwUserName(), i, qwSopLogs.getId(),end2 - start2);
-            }).start();
+            int i = qwSopLogsService.updateQwSopLogsSendType(updateQwSop);
+            log.info("销售:{}, 修改条数{}, 发送方消息完成:{}, 耗时: {}", user.getQwUserName(), i, qwSopLogs.getId(),end2 - start2);
             try {
                 int delay = ThreadLocalRandom.current().nextInt(delayStart, delayEnd);
                 log.debug("等待:{}ms", delay);

+ 0 - 93
fs-ipad-task/src/main/java/com/fs/app/task/SendMsgTest.java

@@ -1,93 +0,0 @@
-package com.fs.app.task;
-
-
-import com.alibaba.fastjson.JSON;
-import com.fs.app.service.IpadSendServer;
-import com.fs.common.core.redis.RedisCacheT;
-import com.fs.course.config.CourseConfig;
-import com.fs.qw.mapper.QwUserMapper;
-import com.fs.qw.service.impl.AsyncSopTestService;
-import com.fs.sop.mapper.QwSopLogsMapper;
-import com.fs.sop.service.IQwSopLogsService;
-import com.fs.system.domain.SysConfig;
-import com.fs.system.mapper.SysConfigMapper;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-
-import java.time.LocalDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.function.Function;
-
-@Component
-@Slf4j
-public class SendMsgTest {
-
-    private final List<Integer> qwUserList = Collections.synchronizedList(Arrays.asList(
-            1,
-            12,
-            13,
-            14,
-            15,
-            16,
-            17,
-            18,
-            19,
-            10,
-            111,
-            112,
-            113,
-            114,
-            115,
-            116,
-            117,
-            118,
-            119,
-            121,
-            122,
-            123,
-            124,
-            125,
-            126,
-            127
-    ));
-    // 使用固定大小线程池防止资源耗尽
-    private final ExecutorService executor = Executors.newFixedThreadPool(400);
-    private final Map<Integer, Long> qwMap = new ConcurrentHashMap<>();
-
-
-//    @Scheduled(fixedRate = 10000) // 每10秒执行一次
-    public void sendMsg2() {
-        qwUserList.forEach(e -> {
-            if (qwMap.putIfAbsent(e, System.currentTimeMillis()) == null) {
-                log.info("提交销售任务:{}", e);
-                executor.submit(() -> {
-                    try {
-                        processUser();
-                    } finally {
-                        qwMap.remove(e);
-                    }
-                });
-            }
-        });
-    }
-
-    private void processUser() {
-        try {
-            int delay = ThreadLocalRandom.current().nextInt(10000, 30000);
-            Thread.sleep(delay);
-        } catch (InterruptedException e) {
-            log.error("线程等待错误!");
-        }
-    }
-
-}

+ 0 - 149
fs-ipad-task/src/main/resources/application-dev.yml

@@ -1,149 +0,0 @@
-# 数据源配置
-spring:
-    # redis 配置
-    redis:
-        # 地址
-        host: localhost
-        # 端口,默认为6379
-        port: 6379
-        # 数据库索引
-        database: 0
-        # 密码
-        password:
-        # 连接超时时间
-        timeout: 20s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
-    datasource:
-        clickhouse:
-            type: com.alibaba.druid.pool.DruidDataSource
-            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
-            url: jdbc:clickhouse://1.14.104.71:8123/sop_test?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
-            username: default
-            password: rt2024
-            initialSize: 10
-            maxActive: 100
-            minIdle: 10
-            maxWait: 6000
-        mysql:
-            type: com.alibaba.druid.pool.DruidDataSource
-            driverClassName: com.mysql.cj.jdbc.Driver
-            druid:
-                # 主库数据源
-                master:
-                    url: jdbc:mysql://42.194.245.189:3306/rt_fs_his_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: root
-                    password: YJF_2024
-                # 从库数据源
-                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://42.194.245.189:3306/test_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: root
-                    password: YJF_2024
-                # 初始连接数
-                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: common-group
-        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
-        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 0 - 149
fs-ipad-task/src/main/resources/application-druid-ylrz.yml

@@ -1,149 +0,0 @@
-# 数据源配置
-spring:
-    # redis 配置
-    redis:
-        # 地址
-        host: localhost
-        # 端口,默认为6379
-        port: 6379
-        # 数据库索引
-        database: 0
-        # 密码
-        password:
-        # 连接超时时间
-        timeout: 20s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
-    datasource:
-        #        clickhouse:
-        #            type: com.alibaba.druid.pool.DruidDataSource
-        ##            driverClassName: ru.yandex.clickhouse.ClickHouseDriver
-        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
-        #            url: jdbc:clickhouse://139.186.211.165:8123/sop_test?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
-        #            username: default
-        #            password: rt2024
-        #            initialSize: 10
-        #            maxActive: 100
-        #            minIdle: 10
-        #            maxWait: 6000
-        mysql:
-            type: com.alibaba.druid.pool.DruidDataSource
-            driverClassName: com.mysql.cj.jdbc.Driver
-            druid:
-                # 主库数据源
-                master:
-                    url: jdbc:mysql://42.194.245.189:3306/rt_fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: root
-                    password: YJF_2024
-                # 从库数据源
-                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://42.194.245.189:3306/test_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: root
-                    password: YJF_2024
-                # 初始连接数
-                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: common-group
-        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
-        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 0 - 151
fs-ipad-task/src/main/resources/application-druid.yml

@@ -1,151 +0,0 @@
-# 数据源配置
-spring:
-    # redis 配置
-    redis:
-        # 地址  localhost
-        host: 127.0.0.1
-        # 端口,默认为6379
-        port: 6379
-        # 数据库索引
-        database: 0
-        # 密码
-        password:
-        #        password:
-        # 连接超时时间
-        timeout: 10s
-        lettuce:
-            pool:
-                # 连接池中的最小空闲连接
-                min-idle: 0
-                # 连接池中的最大空闲连接
-                max-idle: 8
-                # 连接池的最大数据库连接数
-                max-active: 8
-                # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
-    datasource:
-#        clickhouse:
-#            type: com.alibaba.druid.pool.DruidDataSource
-#            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
-#            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
-#            username: rt_2024
-#            password: Yzx_19860213
-#            initialSize: 10
-#            maxActive: 100
-#            minIdle: 10
-#            maxWait: 6000
-        mysql:
-            type: com.alibaba.druid.pool.DruidDataSource
-            driverClassName: com.mysql.cj.jdbc.Driver
-            druid:
-                # 主库数据源
-                master:
-                    url: jdbc:mysql://gz-cdb-f32osvnt.sql.tencentcdb.com:25835/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtyy_2023
-                    password: test_123456
-                # 从库数据源
-                slave:
-                    url: jdbc:mysql://gz-cdb-f32osvnt.sql.tencentcdb.com:25835/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: Rtyy_2023
-                    password: test_123456
-                # 初始连接数
-                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://gz-cdb-of55khc9.sql.tencentcdb.com:23620/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: root
-                    password: Rtyy_2023
-                read:
-                    url: jdbc:mysql://gz-cdb-of55khc9-readonly.sql.tencentcdb.com:22522/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-                    username: root
-                    password: Rtyy_2023
-                # 初始连接数
-                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: common-group
-        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
-        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 5 - 166
fs-ipad-task/src/main/resources/application.yml

@@ -1,173 +1,12 @@
-# 项目相关配置
-fs:
-  # 名称
-  name: fs
-  # 版本
-  version: 1.1.0
-  # 版权年份
-  copyrightYear: 2021
-  # 实例演示开关
-  demoEnabled: true
-  # 文件路径 示例( Windows配置D:/fs/uploadPath,Linux配置 /home/fs/uploadPath)
-  profile: c:/fs/uploadPath
-  # 获取ip地址开关
-  addressEnabled: false
-  # 验证码类型 math 数组计算 char 字符验证
-  captchaType: math
-
-# 开发环境配置
 server:
   # 服务器的HTTP端口,默认为8080
-  port: 7006
-  servlet:
-    # 应用的访问路径
-    context-path: /
-  tomcat:
-    # tomcat的URI编码
-    uri-encoding: UTF-8
-    # tomcat最大线程数,默认为200
-    max-threads: 800
-    # Tomcat启动初始化的线程数,默认值25
-    min-spare-threads: 30
-
-# 日志配置
-logging:
-  level:
-    com.fs: info
-    org.springframework: warn
-
+  port: 9000
 # Spring配置
 spring:
-  # 资源信息
-  messages:
-    # 国际化资源文件路径
-    basename: i18n/messages
   profiles:
-#    active: druid-yjf
     active: dev
-#    active: druid-ylrz
-#    active: druid
-    include: config
-  # 文件上传
-  servlet:
-    multipart:
-      # 单个文件大小
-      max-file-size:  10MB
-      # 设置总上传的文件大小
-      max-request-size:  20MB
-  # 服务模块
-  devtools:
-    restart:
-      # 热部署开关
-      enabled: true
-
-# token配置
-token:
-  # 令牌自定义标识
-  header: Authorization
-  # 令牌密钥
-  secret: abcdefghijklmnopqrstuvwxyz
-  # 令牌有效期(默认30分钟)
-  expireTime: 180
-mybatis-plus:
-  # 搜索指定包别名
-  typeAliasesPackage: com.fs.**.domain,com.fs.**.bo
-  # 配置mapper的扫描,找到所有的mapper.xml映射文件
-  mapperLocations: classpath*:/mapper/**/*.xml
-  configLocation: classpath:mybatis/mybatis-config.xml
-  # 全局配置
-  global-config:
-    db-config:
-      # 主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
-      idType: AUTO
-      # 字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
-      fieldStrategy: NOT_EMPTY
-    banner: false
-    # 配置
-  configuration:
-    # 驼峰式命名
-    mapUnderscoreToCamelCase: true
-    # 全局映射器启用缓存
-    cacheEnabled: true
-    # 配置默认的执行器
-    defaultExecutorType: REUSE
-    # 允许 JDBC 支持自动生成主键
-    useGeneratedKeys: true
-
-# MyBatis配置
-mybatis:
-  # 搜索指定包别名
-  typeAliasesPackage: com.fs.**.domain
-  # 配置mapper的扫描,找到所有的mapper.xml映射文件
-  mapperLocations: classpath*:mapper/**/*Mapper.xml
-  # 加载全局的配置文件
-  configLocation: classpath:mybatis/mybatis-config.xml
-
-# PageHelper分页插件
-pagehelper:
-  helperDialect: mysql
-  supportMethodsArguments: true
-  params: count=countSql
-
-# Swagger配置
-swagger:
-  # 是否开启swagger
-  enabled: false
-  # 请求前缀
-  pathMapping: /dev-api
-# 防止XSS攻击
-xss:
-  # 过滤开关
-  enabled: true
-  # 排除链接(多个用逗号分隔)
-  excludes: /system/notice,/system/config/*
-  # 匹配链接
-  urlPatterns: /system/*,/monitor/*,/tool/*
-wx:
-  miniapp:
-    configs:
-      - appid: wxbe53e91d9ad11ca6
-        secret: 447135e6ca602fa4745b81216f600615
-        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
-        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
-        msgDataFormat: JSON
-  pay:
-    appId: wx782bacb12a6b5d4f #微信公众号或者小程序等的appid
-    mchId: 1518509741 #微信支付商户号
-    mchKey: Jinrichengzhang88888888888888888 #微信支付商户密钥
-    subAppId:  #服务商模式下的子商户公众账号ID
-    subMchId:  #服务商模式下的子商户号
-    keyPath: c:\tools\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
-    notifyUrl: https://api.haitujia.com/app/wxpay/wxPayNotify
+#    active: druid-hdt
+#    active: druid-yzt
+#    active: druid-sxjz
+#    active: druid-sft
 group-no: 1
-
-  
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww7bdf13931a6442d6 --server.port=9001   自动发消息(布偶猫舍)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wweb0666cc79d79da5 --server.port=9002    自动发消息(茶园新区课堂)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww51717e2b71d5e2d3 --server.port=9003    自动发消息(云联融智)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww5a88c4f879f204c5 --server.port=9004    自动发消息(芳华未来守护您的健康)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww8fddae4df911c6aa --server.port=9005    自动发消息(御君方管理)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww44239b22628b206c --server.port=9006    自动发消息(芳华未来)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwdbd998fbb222fd91 --server.port=9007    自动发消息(百草鉴养生)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwa594e16ad105f9b7 --server.port=9008    自动发消息(润心智慧云医茶园新区智慧 )
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww6b20c4d6ac4a7298 --server.port=9009    自动发消息(碧康达生活)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww6a7548362b9cad48 --server.port=9010    自动发消息(芳华未来与您健康同行)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwf15af5d781bee541 --server.port=9011    自动发消息(御君方好课)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww6b789509f8db4e2d --server.port=9012    自动发消息(康与合健康管理)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwe6e3a3fd25eae04e --server.port=9013    自动发消息(御君方养生)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwdaf789c25f04d18f --server.port=9014    自动发消息(康源泰)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww91088a1e17d81d6d --server.port=9015    自动发消息(御君方课堂)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww96c0b2ad89546e5a --server.port=9016    自动发消息(御君方生活智慧)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww3a7bc63475e9f3dd --server.port=9017    自动发消息(济合昌技术)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww0590bdbaca51d96e --server.port=9018    自动发消息(润心智慧云医青南学堂)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwf49d502737165389 --server.port=9019    自动发消息(润心智慧云医米兰学堂)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwdf650a12d59fd4d9 --server.port=9020    自动发消息(百草鉴智慧)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww98294fb364da9d9c --server.port=9021    自动发消息(百草鉴健康)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww6c34c3f89517d4c4 --server.port=9022    自动发消息(百草鉴生活)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=wwa425b9bfad14ba55 --server.port=9023    自动发消息(润心智慧云医通江大道学堂)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww4a42ea8c821dc033 --server.port=9024    自动发消息(百草鉴学堂)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww5b5506c754838051 --server.port=9025    自动发消息(百草鉴学院)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww12ce922187cbbd36 --server.port=9026    自动发消息(彩虹惠医重庆)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww23ea983d9201c7ad --server.port=9027    自动发消息(同泰大药房茶园旗舰店)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww70ac72e824957fc9 --server.port=9028    自动发消息(御君方互联网医院)
-#  --spring.profiles.active=druid-yjf --spring.redis.password=Yzx19860213 --corp-id=ww76ab1028d66d7432 --server.port=9029    自动发消息(蔡汉皋药房)

+ 13 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -775,6 +775,14 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setCorpId(logVo.getCorpId());
         sopLogs.setLogType(ruleTimeVO.getType());
         sopLogs.setTakeRecords(0);
+        try {
+            if(StringUtils.isNotEmpty(logVo.getUserId())){
+                String[] split = logVo.getUserId().split("\\|");
+                sopLogs.setQwUserKey(Long.parseLong(split[0]));
+            }
+        }catch (Exception e){
+            log.error("设置qwUserId异常", e);
+        }
 
         if (isOfficial == 1) {
 
@@ -1840,6 +1848,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setExternalUserName(externalContact.getName());
         sopLogs.setFsUserId(finishLog.getUserId() != null ? finishLog.getUserId() : null );
         sopLogs.setUserLogsId("-");
+        try {
+            sopLogs.setQwUserKey(finishLog.getQwUserId());
+        }catch (Exception e){
+            log.error("设置qwUserId异常", e);
+        }
         // 解析模板设置
         List<QwSopCourseFinishTempSetting.Setting> settings = parseSettings(finishTemp.getSetting());
         if (settings == null) {

+ 70 - 0
fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisQwSopController.java

@@ -1,17 +1,28 @@
 package com.fs.app.controller;
 
 import com.fs.app.params.SopLogsEditParam;
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.fastGpt.service.IFastGptChatSessionService;
+import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.param.SopMsgParam;
+import com.fs.qw.param.sidebar.ExternalContactInfoParam;
+import com.fs.qw.param.sidebar.TagGroupListParam;
+import com.fs.qw.param.sidebar.TagGroupUpdateParam;
 import com.fs.qw.result.QwExternalContactByQwResult;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwTagGroupService;
+import com.fs.qw.vo.QwTagGroupListVO;
+import com.fs.qw.vo.sidebar.ExternalContactInfoVO;
+import com.fs.qw.vo.sidebar.ExternalContactTagVO;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.params.GetQwSopLogsByJsApiParam;
 import com.fs.sop.params.SendSopParamDetailsC;
 import com.fs.sop.service.IQwSopLogsService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -30,6 +41,13 @@ public class ApisQwSopController {
     IFastGptChatSessionService fastGptChatSessionService;
     @Autowired
     private IQwSopLogsService qwSopLogsService;
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IQwTagGroupService qwTagGroupService;
+
     /**
      * 更新AI发送状态
      */
@@ -87,4 +105,56 @@ public class ApisQwSopController {
         return R.ok().put("data",listPageInfo);
     }
 
+    @GetMapping("/externalContact")
+    @ApiOperation("获取侧边栏外部联系人信息")
+    public R getExternalContactInfo(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        ExternalContactInfoVO externalContactInfo = qwExternalContactService.getExternalContactInfo(qwExternalContactId);
+        return R.ok().put("data", externalContactInfo);
+    }
+
+
+    @GetMapping("/externalContact/tag")
+    @ApiOperation("获取侧边栏外部联系人标签")
+    public R getExternalContactTag(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        List<ExternalContactTagVO> tagList = qwExternalContactService.getExternalContactTag(qwExternalContactId);
+        return R.ok().put("data", tagList);
+    }
+
+    @PutMapping("/externalContact")
+    @ApiOperation("编辑外部联系人信息")
+    public R updateExternalContactInfo(@RequestBody ExternalContactInfoParam param) {
+        return qwExternalContactService.updateExternalContactInfo(param);
+    }
+
+    @GetMapping("/tagGroupList")
+    @ApiOperation("获取所有标签组和其下标签")
+    public R getTagGroupList(TagGroupListParam param) {
+        QwTagGroup qwTagGroup = new QwTagGroup();
+        BeanCopyUtils.copy(param, qwTagGroup);
+        qwTagGroup.setName(param.getTagName());
+
+        PageHelper.startPage(qwTagGroup.getPageNum(), qwTagGroup.getPageSize());
+        List<QwTagGroupListVO> list = qwTagGroupService.selectQwGroupTagList(qwTagGroup);
+
+        PageInfo<QwTagGroupListVO> result = new PageInfo<>(list);
+        return R.ok().put("data", result);
+    }
+
+//    @GetMapping("/searchTags")
+//    @ApiOperation("搜索标签-跟管理端保持一致")
+//    public R searchTagList(QwTagParam param) {
+//        List<QwTagGroupListVO> list = qwTagService.searchTags(param);
+//        return R.ok().put("data", list);
+//    }
+
+    @PutMapping("/externalContact/tag")
+    @ApiOperation("编辑标签")
+    public R updateExternalContactTag(@RequestBody TagGroupUpdateParam param) {
+        int i = qwExternalContactService.updateExternalContactTag(param);
+        if (i > 0) {
+            return R.ok();
+        }
+        return R.error();
+    }
+
 }

+ 18 - 0
fs-qwhook-sop/src/main/java/com/fs/app/controller/QwSopController.java

@@ -1,17 +1,28 @@
 package com.fs.app.controller;
 
 import com.fs.app.params.SopLogsEditParam;
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.fastGpt.service.IFastGptChatSessionService;
+import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.param.SopMsgParam;
+import com.fs.qw.param.sidebar.ExternalContactInfoParam;
+import com.fs.qw.param.sidebar.TagGroupListParam;
+import com.fs.qw.param.sidebar.TagGroupUpdateParam;
 import com.fs.qw.result.QwExternalContactByQwResult;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwTagGroupService;
+import com.fs.qw.vo.QwTagGroupListVO;
+import com.fs.qw.vo.sidebar.ExternalContactInfoVO;
+import com.fs.qw.vo.sidebar.ExternalContactTagVO;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.params.GetQwSopLogsByJsApiParam;
 import com.fs.sop.params.SendSopParamDetailsC;
 import com.fs.sop.service.IQwSopLogsService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -31,6 +42,13 @@ public class QwSopController {
     IFastGptChatSessionService fastGptChatSessionService;
     @Autowired
     private IQwSopLogsService qwSopLogsService;
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IQwTagGroupService qwTagGroupService;
+
     /**
      * 更新AI发送状态
      */

+ 67 - 0
fs-qwhook-sop/src/main/java/com/fs/app/controller/QwUserController.java

@@ -1,13 +1,24 @@
 package com.fs.app.controller;
 
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.qw.domain.QwExternalContactInfo;
+import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.param.ExternalContactDetailsParam;
+import com.fs.qw.param.sidebar.ExternalContactInfoParam;
+import com.fs.qw.param.sidebar.TagGroupListParam;
+import com.fs.qw.param.sidebar.TagGroupUpdateParam;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwTagGroupService;
 import com.fs.qw.vo.ExternalContactDetailsVO;
+import com.fs.qw.vo.QwTagGroupListVO;
+import com.fs.qw.vo.sidebar.ExternalContactInfoVO;
+import com.fs.qw.vo.sidebar.ExternalContactTagVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -16,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @Slf4j
@@ -30,6 +42,9 @@ public class QwUserController extends BaseController {
     @Autowired
     private IQwExternalContactInfoService qwExternalContactInfoService;
 
+    @Autowired
+    private IQwTagGroupService qwTagGroupService;
+
     @GetMapping("/details")
     @ApiOperation("会员看课详情")
     public R getUserDetails(@ApiParam(value = "外部联系人id", required = true) @RequestParam Long contactId,
@@ -66,4 +81,56 @@ public class QwUserController extends BaseController {
         return R.ok();
     }
 
+    @GetMapping("/externalContact")
+    @ApiOperation("获取侧边栏外部联系人信息")
+    public R getExternalContactInfo(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        ExternalContactInfoVO externalContactInfo = qwExternalContactService.getExternalContactInfo(qwExternalContactId);
+        return R.ok().put("data", externalContactInfo);
+    }
+
+    @GetMapping("/externalContact/tag")
+    @ApiOperation("获取侧边栏外部联系人标签")
+    public R getExternalContactTag(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        List<ExternalContactTagVO> tagList = qwExternalContactService.getExternalContactTag(qwExternalContactId);
+        return R.ok().put("data", tagList);
+    }
+
+    @PutMapping("/externalContact")
+    @ApiOperation("编辑外部联系人信息")
+    public R updateExternalContactInfo(@RequestBody ExternalContactInfoParam param) {
+        return qwExternalContactService.updateExternalContactInfo(param);
+    }
+
+    @GetMapping("/tagGroupList")
+    @ApiOperation("获取所有标签组和其下标签")
+    public R getTagGroupList(TagGroupListParam param) {
+        QwTagGroup qwTagGroup = new QwTagGroup();
+        BeanCopyUtils.copy(param, qwTagGroup);
+        qwTagGroup.setName(param.getTagName());
+
+        PageHelper.startPage(qwTagGroup.getPageNum(), qwTagGroup.getPageSize());
+        List<QwTagGroupListVO> list = qwTagGroupService.selectQwGroupTagList(qwTagGroup);
+
+        PageInfo<QwTagGroupListVO> result = new PageInfo<>(list);
+        return R.ok().put("data", result);
+    }
+
+//    @GetMapping("/searchTags")
+//    @ApiOperation("搜索标签-跟管理端保持一致")
+//    public R searchTagList(QwTagParam param) {
+//        List<QwTagGroupListVO> list = qwTagService.searchTags(param);
+//        return R.ok().put("data", list);
+//    }
+
+    @PutMapping("/externalContact/tag")
+    @ApiOperation("编辑标签")
+    public R updateExternalContactTag(@RequestBody TagGroupUpdateParam param) {
+        int i = qwExternalContactService.updateExternalContactTag(param);
+        if (i > 0) {
+            return R.ok();
+        }
+        return R.error();
+    }
+
+
 }

+ 69 - 0
fs-qwhook/src/main/java/com/fs/app/controller/QwUserController.java

@@ -1,13 +1,24 @@
 package com.fs.app.controller;
 
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.qw.domain.QwExternalContactInfo;
+import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.param.ExternalContactDetailsParam;
+import com.fs.qw.param.sidebar.ExternalContactInfoParam;
+import com.fs.qw.param.sidebar.TagGroupListParam;
+import com.fs.qw.param.sidebar.TagGroupUpdateParam;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwTagGroupService;
 import com.fs.qw.vo.ExternalContactDetailsVO;
+import com.fs.qw.vo.QwTagGroupListVO;
+import com.fs.qw.vo.sidebar.ExternalContactInfoVO;
+import com.fs.qw.vo.sidebar.ExternalContactTagVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -16,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @Slf4j
@@ -30,6 +42,9 @@ public class QwUserController extends BaseController {
     @Autowired
     private IQwExternalContactInfoService qwExternalContactInfoService;
 
+    @Autowired
+    private IQwTagGroupService qwTagGroupService;
+
     @GetMapping("/details")
     @ApiOperation("会员看课详情")
     public R getUserDetails(@ApiParam(value = "外部联系人id", required = true) @RequestParam Long contactId,
@@ -66,4 +81,58 @@ public class QwUserController extends BaseController {
         return R.ok();
     }
 
+
+    @GetMapping("/externalContact")
+    @ApiOperation("获取侧边栏外部联系人信息")
+    public R getExternalContactInfo(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        ExternalContactInfoVO externalContactInfo = qwExternalContactService.getExternalContactInfo(qwExternalContactId);
+        return R.ok().put("data", externalContactInfo);
+    }
+
+
+    @GetMapping("/externalContact/tag")
+    @ApiOperation("获取侧边栏外部联系人标签")
+    public R getExternalContactTag(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId) {
+        List<ExternalContactTagVO> tagList = qwExternalContactService.getExternalContactTag(qwExternalContactId);
+        return R.ok().put("data", tagList);
+    }
+
+    @PutMapping("/externalContact")
+    @ApiOperation("编辑外部联系人信息")
+    public R updateExternalContactInfo(@RequestBody ExternalContactInfoParam param) {
+        return qwExternalContactService.updateExternalContactInfo(param);
+    }
+
+    @GetMapping("/tagGroupList")
+    @ApiOperation("获取所有标签组和其下标签")
+    public R getTagGroupList(TagGroupListParam param) {
+        QwTagGroup qwTagGroup = new QwTagGroup();
+        BeanCopyUtils.copy(param, qwTagGroup);
+        qwTagGroup.setName(param.getTagName());
+
+        PageHelper.startPage(qwTagGroup.getPageNum(), qwTagGroup.getPageSize());
+        List<QwTagGroupListVO> list = qwTagGroupService.selectQwGroupTagList(qwTagGroup);
+
+        PageInfo<QwTagGroupListVO> result = new PageInfo<>(list);
+        return R.ok().put("data", result);
+    }
+
+//    @GetMapping("/searchTags")
+//    @ApiOperation("搜索标签-跟管理端保持一致")
+//    public R searchTagList(QwTagParam param) {
+//        List<QwTagGroupListVO> list = qwTagService.searchTags(param);
+//        return R.ok().put("data", list);
+//    }
+
+    @PutMapping("/externalContact/tag")
+    @ApiOperation("编辑标签")
+    public R updateExternalContactTag(@RequestBody TagGroupUpdateParam param) {
+        int i = qwExternalContactService.updateExternalContactTag(param);
+        if (i > 0) {
+            return R.ok();
+        }
+        return R.error();
+    }
+
+
 }

+ 7 - 0
fs-service/src/main/java/com/fs/common/param/LoginMaWxParam.java

@@ -46,4 +46,11 @@ public class LoginMaWxParam implements Serializable {
 //    @ApiModelProperty(value = "用户密码")
 //    private String password;
 
+    /**
+     * 0:静默授权  1:手机号授权
+     */
+    @NotNull(message = "授权类型缺失")
+    @ApiModelProperty(value = "小程序授权类型")
+    private Integer authType;
+
 }

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

@@ -113,4 +113,7 @@ public interface CompanyDeptMapper
 
     @Select("select dept_name from company_dept where dept_id=${deptId} limit 1")
     String selectDeptNameById(@Param("deptId") Long deptId);
+
+    @Select("select dept_id,dept_name from company_dept where dept_name=#{deptName} limit 1")
+    CompanyDept selectDeptNameBydeptName(@Param("deptName") String deptName);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyPostMapper.java

@@ -83,4 +83,12 @@ public interface CompanyPostMapper
      */
     List<CompanyPost> selectCompanyPostByIds(@Param("postIds") List<Long> postIds);
 
+
+    /**
+     * 获取默认岗位
+     * @param postCode 岗位编码
+     * @return 信息
+     * **/
+    CompanyPost selectCompanyPostCode(@Param("postCode") String postCode,@Param("companyId") Long companyId);
+
 }

+ 7 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyRoleMapper.java

@@ -77,4 +77,11 @@ public interface CompanyRoleMapper
      * @return  list
      */
     List<CompanyRole> selectRoleListByIds(@Param("roleIds") List<Long> roleIds);
+
+    /**
+     * 获取默认角色
+     * @param roleKey 角色key
+     * @return 角色信息
+     * **/
+    CompanyRole selectCompanyRoleByRoleKey(@Param("roleKey") String roleKey);
 }

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

@@ -79,4 +79,6 @@ public interface CompanyTagUserMapper
      * @param params    条件
      */
     void deleteCompanyTagUserByMap(@Param("params") Map<String, Object> params);
+
+    List<CompanyUserTagListVO> getTagByUser(@Param("params")CompanyTagUser param);
 }

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

@@ -295,4 +295,10 @@ public interface CompanyUserMapper
     void batchUpdateUserDomain(@Param("ids") List<Long> ids,@Param("domain") String domain);
 
     List<QwOptionsVO> selectQwUserListLikeName(@Param("params") Map<String, Object> params);
+
+    /**
+     * 批量更新用户部门数据
+     * @param companyUserList 用户信息
+     * **/
+    void batchUpdateUserDept(@Param("companyUserList") List<CompanyUser> companyUserList);
 }

+ 15 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyRoleService.java

@@ -94,4 +94,19 @@ public interface ICompanyRoleService
      * @return  list
      */
     List<CompanyRole> selectCompanyRoleByIds(List<Long> roleIds);
+
+    /**
+     * 获取默认角色
+     * @param roleKey 角色key
+     * @return 角色信息
+     * **/
+    CompanyRole selectCompanyRoleByRoleKey(String roleKey);
+
+
+    /**
+     * 插入默认角色
+     * @param role 角色对象
+     * @return
+     * **/
+    int insertDefaultRole(CompanyRole role);
 }

+ 7 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyTagUserService.java

@@ -84,4 +84,11 @@ public interface ICompanyTagUserService
      * @param tagIds   标签ID集合
      */
     void changeUserTags(List<Long> fsUserIds, List<Long> tagIds);
+
+    /**
+     * 查询会员标签
+     * @param param 分页参数
+     * @return
+     */
+    List<CompanyUserTagListVO> getTagByUser(CompanyTagUser param);
 }

+ 6 - 1
fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -194,7 +194,12 @@ public interface ICompanyUserService {
      */
     Boolean setIsRegisterMember(boolean status,  List<Long> userIds);
 
-    void auditUsers(List<Long> userIds);
+    /**
+     * 批量审核接口
+     * @param companyId 企业ID
+     * @param userIds 用户Id
+     * **/
+    void auditUsers(List<Long> userIds,Long companyId);
 
     CompanyUser selectCompanyUserByPhone(String phone);
 

+ 10 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyRoleServiceImpl.java

@@ -271,4 +271,14 @@ public class CompanyRoleServiceImpl implements ICompanyRoleService
     public List<CompanyRole> selectCompanyRoleByIds(List<Long> roleIds) {
         return companyRoleMapper.selectRoleListByIds(roleIds);
     }
+
+    @Override
+    public CompanyRole selectCompanyRoleByRoleKey(String roleKey) {
+        return companyRoleMapper.selectCompanyRoleByRoleKey(roleKey);
+    }
+
+    @Override
+    public int insertDefaultRole(CompanyRole role) {
+        return companyRoleMapper.insertCompanyRole(role);
+    }
 }

+ 32 - 3
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -139,6 +139,18 @@ public class CompanyServiceImpl implements ICompanyService
             dept.setCreateTime(new Date());
             dept.setDeptName(company.getCompanyName());
             deptMapper.insertCompanyDept(dept);
+
+            //创建销售部门
+            CompanyDept saleDept=new CompanyDept();
+            saleDept.setParentId(0l);
+            saleDept.setAncestors("0");
+            saleDept.setStatus("0");
+            saleDept.setDeptName("销售(默认部门)");
+            saleDept.setCreateTime(new Date());
+            saleDept.setParentId(dept.getDeptId());
+            saleDept.setCompanyId(company.getCompanyId());
+            deptMapper.insertCompanyDept(saleDept);
+
             //创建岗位
             CompanyPost post=new CompanyPost();
             post.setCompanyId(company.getCompanyId());
@@ -147,6 +159,16 @@ public class CompanyServiceImpl implements ICompanyService
             post.setPostSort(0);
             post.setStatus("0");
             postMapper.insertCompanyPost(post);
+
+            //创建销售岗位
+            CompanyPost salePost=new CompanyPost();
+            salePost.setCompanyId(company.getCompanyId());
+            salePost.setPostCode("default_sale");
+            salePost.setPostName("销售");
+            salePost.setPostSort(0);
+            salePost.setStatus("0");
+            postMapper.insertCompanyPost(salePost);
+
             //创建角色
             // 创建管理员角色(拥有全部权限)
             CompanyRole adminRole = new CompanyRole();
@@ -165,18 +187,25 @@ public class CompanyServiceImpl implements ICompanyService
             salesRole.setRoleSort(1);
             salesRole.setDataScope("5");
             salesRole.setStatus("0");
-
             //增加销售角色菜单权限
             try {
                 String json = configService.selectConfigByKey("companymenu.config");
-                if (StringUtils.isNotEmpty(json)) {
+                if (StringUtils.isNotEmpty(json) && !json.equals("")) {
                     CompanyMenuConfig config = JSONUtil.toBean(json, CompanyMenuConfig.class);
                     salesRole.setMenuIds(config.getMenuIds());
-                    roleService.insertRole(salesRole);
+                    if(config.getMenuIds().length > 0){
+                        roleService.insertRole(salesRole);
+                    }else {
+                        roleService.insertDefaultRole(salesRole);
+                    }
+                }else {
+                    roleService.insertDefaultRole(salesRole);
                 }
+
             } catch (Exception e) {
                 logger.error("获取菜单配置失败", e);
             }
+
             //添加用户
             CompanyUser user=new CompanyUser();
             user.setCompanyId(company.getCompanyId());

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

@@ -176,4 +176,15 @@ public class CompanyTagUserServiceImpl implements ICompanyTagUserService
             }
         });
     }
+
+    @Override
+    public List<CompanyUserTagListVO> getTagByUser(CompanyTagUser param) {
+        // 判断是否是管理员
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getCompanyUserId());
+        if (companyUser.isAdmin()) {
+            param.setCompanyUserId(null);
+        }
+
+        return companyTagUserMapper.getTagByUser(param);
+    }
 }

+ 67 - 2
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.company.service.impl;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.DataScope;
 import com.fs.common.core.domain.R;
@@ -56,6 +57,9 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     @Autowired
     private QwUserMapper qwUserMapper;
 
+    @Autowired
+    private CompanyDeptMapper companyDeptMapper;
+
     @Autowired
     private CompanyUserRoleMapper userRoleMapper;
 
@@ -247,6 +251,8 @@ public class CompanyUserServiceImpl implements ICompanyUserService
         insertUserPost(user);
         // 新增用户与角色管理
         insertUserRole(user);
+        //新增用户需要修改密码
+        redisCache.setCacheObject("newCompanyUser:" + user.getCompanyId() + ":" +user.getUserName(),user.getUserId());
         return rows;
     }
 
@@ -515,11 +521,70 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     }
 
     @Override
-    public void auditUsers(List<Long> userIds) {
+    public void auditUsers(List<Long> userIds,Long companyId) {
         if (userIds.isEmpty()) {
             return;
         }
-        companyUserMapper.auditUsers(userIds);
+        //获取用户信息
+        List<CompanyUser> companyUserList=companyUserMapper.getUserInfoByUserIds(userIds);
+
+        if(ObjectUtil.isNotNull(companyUserList) && !companyUserList.isEmpty()){
+
+            //获取默认角色信息
+            CompanyRole role=roleMapper.selectCompanyRoleByRoleKey(companyId+"_sales");
+
+            //获取默认部门
+            CompanyDept dept= companyDeptMapper.selectDeptNameBydeptName("销售(默认部门)");
+
+            //获取默认岗位
+            CompanyPost companyPost= postMapper.selectCompanyPostCode("default_sale",companyId);
+
+            //存储用户角色中间表
+            List<CompanyUserRole> companyUserRoleList=new LinkedList<>();
+
+            //存储岗位
+            List<CompanyUserPost> companyUserPosts=new LinkedList<>();
+            companyUserList.stream().forEach(c->{
+                //判断角色是否为空
+                if(ObjectUtil.isNotNull(role)){
+                    CompanyUserRole userRole=new CompanyUserRole();
+                    userRole.setRoleId(role.getRoleId());
+                    userRole.setUserId(c.getUserId());
+                    companyUserRoleList.add(userRole);
+                }
+
+                //判断部门
+                if(ObjectUtil.isNotNull(dept)){
+                    c.setDeptId(dept.getDeptId());
+                }
+
+                //判断岗位
+                if(ObjectUtil.isNotNull(companyPost)){
+                    //添加用户岗位表
+                    CompanyUserPost userPost=new CompanyUserPost();
+                    userPost.setPostId(companyPost.getPostId());
+                    userPost.setUserId(c.getUserId());
+                    companyUserPosts.add(userPost);
+                }
+
+            });
+
+            //批量插入角色用户中间表
+            if(!companyUserRoleList.isEmpty()){
+                userRoleMapper.batchUserRole(companyUserRoleList);
+            }
+
+            //批量插入用户岗位中间表
+            if(!companyUserPosts.isEmpty()){
+                userPostMapper.batchUserPost(companyUserPosts);
+            }
+
+            //批量更新用户部门信息
+            companyUserMapper.batchUpdateUserDept(companyUserList);
+
+            companyUserMapper.auditUsers(userIds);
+        }
+
     }
 
     @Override

+ 5 - 3
fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java

@@ -65,9 +65,11 @@ public class WxMaConfiguration {
                 if (appid.equals(courseMaConfig.getAppid())) {
                     continue;
                 }
-                WxMaConfig.Config wxMaConfig = new WxMaConfig.Config();
-                BeanUtils.copyProperties(courseMaConfig, wxMaConfig);
-                c.add(wxMaConfig);
+                if (courseMaConfig.getType().equals("1")){
+                    WxMaConfig.Config wxMaConfig = new WxMaConfig.Config();
+                    BeanUtils.copyProperties(courseMaConfig, wxMaConfig);
+                    c.add(wxMaConfig);
+                }
             }
         }
         wx.setConfigs(c);

+ 2 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseRealLink.java

@@ -43,4 +43,6 @@ public class FsCourseRealLink implements Serializable
     private Long id;
 
     private String chatId;
+
+    private Long projectId;//项目ID
 }

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

@@ -1,6 +1,8 @@
 package com.fs.course.domain;
 
 import java.math.BigDecimal;
+
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
@@ -145,4 +147,7 @@ public class FsUserCourse extends BaseEntity
 
     private String companyIds;
 
+    @TableField(exist = false)
+    private Long[] companyIdsList;
+
 }

+ 4 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCourseComplaintRecord.java

@@ -44,6 +44,10 @@ public class FsUserCourseComplaintRecord extends BaseEntity{
     @Excel(name = "投诉内容")
     private String complaintContent;
 
+    /** 投诉上传图片 */
+    @Excel(name = "投诉上传图片")
+    private String complaintUrl;
+
     /** 课程id */
     @Excel(name = "课程id")
     private Long courseId;

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

@@ -419,4 +419,6 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "    qec.corp_id"+
             "</script>")
     List<QwExternalContact> selectQwWatchLogFomExtContact(@Param("logIds") List<Long> logIds);
+
+    List<FsCourseWatchLogListVO> selectListBytrainingCampId(PeriodStatisticCountParam param);
 }

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

@@ -18,7 +18,7 @@ public interface FsUserCourseComplaintRecordMapper extends BaseMapper<FsUserCour
      * @param recordId 看课投诉记录主键
      * @return 看课投诉记录
      */
-    FsUserCourseComplaintRecord selectFsUserCourseComplaintRecordByRecordId(Long recordId);
+    FsUserCourseComplaintRecordPageListVO selectFsUserCourseComplaintRecordByRecordId(Long recordId);
 
     /**
      * 查询看课投诉记录列表

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

@@ -149,6 +149,9 @@ public interface FsUserCourseMapper
             "<if test = ' maps.isShow !=null '> " +
             "and c.is_show = #{maps.isShow} " +
             "</if>" +
+            "<if test ='maps.companyIdsList != null and maps.companyIdsList.length !=0 '> " +
+            " and <foreach collection='maps.companyIdsList'  item='item' index='index'  open='( 1=2 ' separator='' close=')'> or find_in_set(  #{item} , REGEXP_REPLACE ( c.company_ids, '[\"\\\\[\\\\]]', '' ) )  </foreach> " +
+            "</if> " +
             " order by c.sort, c.course_id  "+
             "</script>"})
     List<FsUserCourseListPVO> selectFsUserCourseListPVO(@Param("maps") FsUserCourse param);
@@ -217,6 +220,9 @@ public interface FsUserCourseMapper
             "<if test = ' maps.isShow !=null '> " +
             "and c.is_show = #{maps.isShow} " +
             "</if>" +
+            "<if test ='maps.companyIdsList != null and maps.companyIdsList.length !=0 '> " +
+            " and <foreach collection='maps.companyIdsList'  item='item' index='index'  open='( 1=2 ' separator='' close=')'> or find_in_set(  #{item} , REGEXP_REPLACE ( c.company_ids, '[\"\\\\[\\\\]]', '' ) )  </foreach> " +
+            "</if> " +
             " order by c.sort,c.course_id  "+
             "</script>"})
     List<FsUserCourseListPVO> selectFsUserCourseListCompanyPVO(@Param("maps")FsUserCourseParam fsUserCourse);

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

@@ -2,6 +2,7 @@ package com.fs.course.mapper;
 
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.param.CompanyRedPacketParam;
+import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import com.fs.course.vo.PeriodRedPacketVO;
 import org.apache.ibatis.annotations.MapKey;
@@ -9,6 +10,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
@@ -145,5 +147,15 @@ public interface FsUserCoursePeriodMapper
     int updateBatchDelFlag(@Param("ids") Long [] ids, @Param("delFlag") Integer delFlag);
 
 
+    Long setlectCourseCompleteNum(PeriodStatisticCountParam param);
 
+    Long setlectCourseWatchNum(PeriodStatisticCountParam param);
+
+    Long setlectRedPacketCount(PeriodStatisticCountParam param);
+
+    BigDecimal setlectRedPacketAmount(PeriodStatisticCountParam param);
+
+    Long setlectCorrectAnswerNum(PeriodStatisticCountParam param);
+
+    List<FsUserCoursePeriod> selectFsPeriodlist(PeriodStatisticCountParam param);
 }

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

@@ -1,5 +1,6 @@
 package com.fs.course.param;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fs.common.annotation.Excel;
 import lombok.Data;
 
@@ -137,4 +138,9 @@ public class FsUserCourseParam {
     private Integer isPrivate; //是否私域
 
     private Long companyId;
+
+    private String companyIds;
+
+    @TableField(exist = false)
+    private Long[] companyIdsList;
 }

+ 32 - 0
fs-service/src/main/java/com/fs/course/param/PeriodStatisticCountParam.java

@@ -0,0 +1,32 @@
+package com.fs.course.param;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 营期统计入参
+ */
+@Data
+public class PeriodStatisticCountParam implements Serializable {
+
+    @ApiModelProperty(value = "营期id")
+    private Long periodId;
+
+    /**
+     * 训练营Id
+     */
+    private Long trainingCampId;
+
+    /**
+     * 用户Id
+     */
+    private Long userId;
+
+}
+

+ 4 - 1
fs-service/src/main/java/com/fs/course/param/UserCourseComplaintRecordParam.java

@@ -12,9 +12,12 @@ public class UserCourseComplaintRecordParam {
     @ApiModelProperty(value = "投诉类型id")
     private Long complaintTypeId;
 
-    @ApiModelProperty(value = "投诉内容(暂时没有,只是保留这个字段)")
+    @ApiModelProperty(value = "投诉内容")
     private String complaintContent;
 
+    @ApiModelProperty(value = "投诉上传图片")
+    private String complaintUrl;
+
     @ApiModelProperty(value = "课程id")
     private Long courseId;
 

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

@@ -121,4 +121,11 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
     void scheduleUpdateDurationToDatabase();
 
     void checkFsUserWatchStatus();
+
+    /**
+     * 查询看课记录数据信息
+     * @param param
+     * @return
+     */
+    List<FsCourseWatchLogListVO> selectListBytrainingCampId(PeriodStatisticCountParam param);
 }

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

@@ -19,7 +19,7 @@ public interface IFsUserCourseComplaintRecordService extends IService<FsUserCour
      * @param recordId 看课投诉记录主键
      * @return 看课投诉记录
      */
-    FsUserCourseComplaintRecord selectFsUserCourseComplaintRecordByRecordId(Long recordId);
+    FsUserCourseComplaintRecordPageListVO selectFsUserCourseComplaintRecordByRecordId(Long recordId);
 
     /**
      * 查询看课投诉记录列表

+ 19 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java

@@ -1,8 +1,11 @@
 package com.fs.course.service;
 
 import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.param.PeriodStatisticCountParam;
+import com.fs.course.vo.FsCourseStaticsCountVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -78,4 +81,20 @@ public interface IFsUserCoursePeriodService
      * 更新营期状态
      */
     void changePeriodStatus();
+
+
+    /**
+     * 查询数据信息(查询总学习时长、正确答题数、答题红包金额、答题红包数)
+     * @param param
+     * @return
+     */
+    FsCourseStaticsCountVO periodCourseStatisticCount(PeriodStatisticCountParam param);
+
+    /**
+     * 查询自己的所有营期
+     *
+     * @param param
+     * @return
+     */
+    List<FsUserCoursePeriod> selectFsPeriodlist(PeriodStatisticCountParam param);
 }

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

@@ -447,6 +447,11 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     }
 
+    @Override
+    public List<FsCourseWatchLogListVO> selectListBytrainingCampId(PeriodStatisticCountParam param) {
+        return baseMapper.selectListBytrainingCampId(param);
+    }
+
 
     public void batchUpdateFsUserCourseWatchLog(List<FsCourseWatchLog> logs, int batchSize) {
         if (logs == null || logs.isEmpty()) {

+ 27 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseComplaintRecordServiceImpl.java

@@ -1,11 +1,18 @@
 package com.fs.course.service.impl;
 
 import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 import com.fs.common.utils.DateUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.course.param.UserCourseComplaintRecordParam;
 import com.fs.course.vo.FsUserCourseComplaintRecordPageListVO;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwExternalContactMapper;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.course.mapper.FsUserCourseComplaintRecordMapper;
 import com.fs.course.domain.FsUserCourseComplaintRecord;
@@ -20,6 +27,9 @@ import com.fs.course.service.IFsUserCourseComplaintRecordService;
 @Service
 public class FsUserCourseComplaintRecordServiceImpl extends ServiceImpl<FsUserCourseComplaintRecordMapper, FsUserCourseComplaintRecord> implements IFsUserCourseComplaintRecordService {
 
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+
     /**
      * 查询看课投诉记录
      *
@@ -27,7 +37,7 @@ public class FsUserCourseComplaintRecordServiceImpl extends ServiceImpl<FsUserCo
      * @return 看课投诉记录
      */
     @Override
-    public FsUserCourseComplaintRecord selectFsUserCourseComplaintRecordByRecordId(Long recordId)
+    public FsUserCourseComplaintRecordPageListVO selectFsUserCourseComplaintRecordByRecordId(Long recordId)
     {
         return baseMapper.selectFsUserCourseComplaintRecordByRecordId(recordId);
     }
@@ -41,7 +51,22 @@ public class FsUserCourseComplaintRecordServiceImpl extends ServiceImpl<FsUserCo
     @Override
     public List<FsUserCourseComplaintRecordPageListVO> selectFsUserCourseComplaintRecordList(FsUserCourseComplaintRecord fsUserCourseComplaintRecord)
     {
-        return baseMapper.selectFsUserCourseComplaintRecordPageList(fsUserCourseComplaintRecord);
+        List<FsUserCourseComplaintRecordPageListVO> list = baseMapper.selectFsUserCourseComplaintRecordPageList(fsUserCourseComplaintRecord);
+
+        // 获取外部联系人看课状态
+        List<Long> userIds = list.stream().map(FsUserCourseComplaintRecordPageListVO::getUserId).collect(Collectors.toList());
+        List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectExternalByFsUserIds(userIds);
+        Map<Long, QwExternalContact> qwExternalContactMap = qwExternalContacts.stream().collect(Collectors.toMap(QwExternalContact::getFsUserId, Function.identity(), (v1, v2) -> v1));
+
+        for (FsUserCourseComplaintRecordPageListVO vo : list) {
+            QwExternalContact contact = qwExternalContactMap.get(vo.getUserId());
+            if(contact != null) {
+                vo.setStatus(contact.getCommentStatus() != null && contact.getCommentStatus() == 1 ? "已拉黑" : "正常");
+            } else {
+                vo.setStatus("正常");
+            }
+        }
+        return list;
     }
 
     /**

+ 50 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -1,14 +1,20 @@
 package com.fs.course.service.impl;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.StringUtils;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriodDays;
 import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
 import com.fs.course.mapper.FsUserCoursePeriodMapper;
 import com.fs.course.mapper.FsUserCourseVideoRedPackageMapper;
+import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.service.IFsUserCoursePeriodService;
+import com.fs.course.vo.FsCourseStaticsCountVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,6 +22,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
@@ -23,6 +30,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -37,6 +45,8 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     @Autowired
     private FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
     @Resource
+    RedisCache redisCache;
+    @Resource
     private FsUserCoursePeriodDaysMapper fsUserCoursePeriodDaysMapper;
     @Resource
     private FsUserCourseVideoRedPackageMapper fsUserCourseVideoRedPackageMapper;
@@ -241,4 +251,44 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
         // 关营
         fsUserCoursePeriodMapper.endPeriod(LocalDate.now());
     }
+    @Override
+    public FsCourseStaticsCountVO periodCourseStatisticCount(PeriodStatisticCountParam param) {
+        String redisKey = "user:trainingCampId:periodId:" + param.getUserId() + ":" + param.getTrainingCampId();
+        if (ObjectUtil.isNotEmpty(param.getPeriodId())){
+            redisKey=redisKey+":"+param.getPeriodId();
+        }
+        FsCourseStaticsCountVO durationStr = redisCache.getCacheObject(redisKey);
+        if (ObjectUtil.isNotEmpty(durationStr)){
+            return durationStr;
+        }
+        FsCourseStaticsCountVO fsCourseStaticsCountVO = new FsCourseStaticsCountVO();
+        try {
+            Long courseCompleteNum = fsUserCoursePeriodMapper.setlectCourseCompleteNum(param);
+            fsCourseStaticsCountVO.setCourseCompleteNum(courseCompleteNum != null ? courseCompleteNum : 0L);
+
+            Long courseWatchNum = fsUserCoursePeriodMapper.setlectCourseWatchNum(param);
+            fsCourseStaticsCountVO.setCourseWatchNum(courseWatchNum != null ? courseWatchNum : 0L);
+
+            Long redPacketCount = fsUserCoursePeriodMapper.setlectRedPacketCount(param);
+            fsCourseStaticsCountVO.setRedPacketCount(redPacketCount != null ? redPacketCount : 0L);
+
+            BigDecimal redPacketAmount = fsUserCoursePeriodMapper.setlectRedPacketAmount(param);
+            fsCourseStaticsCountVO.setRedPacketAmount(redPacketAmount != null ? redPacketAmount : BigDecimal.ZERO);
+
+            Long correctAnswerNum = fsUserCoursePeriodMapper.setlectCorrectAnswerNum(param);
+            fsCourseStaticsCountVO.setCorrectAnswerNum(correctAnswerNum != null ? correctAnswerNum : 0L);
+            redisCache.setCacheObject(redisKey,fsCourseStaticsCountVO);
+            // 设置 Redis 记录的过期时间(例如 5 分钟)
+            redisCache.expire(redisKey, 600, TimeUnit.SECONDS);
+            return fsCourseStaticsCountVO;
+
+        } catch (Exception e) {
+            return fsCourseStaticsCountVO;
+        }
+    }
+
+    @Override
+    public List<FsUserCoursePeriod> selectFsPeriodlist(PeriodStatisticCountParam param) {
+        return fsUserCoursePeriodMapper.selectFsPeriodlist(param);
+    }
 }

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

@@ -914,7 +914,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         switch (config.getRewardType()) {
             // 红包奖励
             case 1:
-                return sendRedPacketReward(param, user, log, video, config);
+                return sendRedPacketRewardFsUser(param, user, log, video, config);
             // 积分奖励
             case 2:
                 return sendIntegralReward(param,user, log, config);
@@ -1073,6 +1073,164 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
     }
 
+    /**
+     * 发放红包奖励
+     *
+     * @param param 请求参数
+     * @param user 用户信息
+     * @param log 观看日志
+     * @param video 视频信息
+     * @param config 配置信息
+     * @return 处理结果
+     */
+    private R sendRedPacketRewardFsUser(FsCourseSendRewardUParam param, FsUser user, FsCourseWatchLog log, FsUserCourseVideo video, CourseConfig config) {
+        // 判断是否属于领取红包时间(会员看课发放红包)
+        if (param.getPeriodId()!=null && param.getPeriodId()>0) {
+            FsUserCoursePeriodDays periodDays = new FsUserCoursePeriodDays();
+            periodDays.setVideoId(param.getVideoId());
+            periodDays.setPeriodId(param.getPeriodId());
+            //正常情况是只能查询到一条,之前可能存在重复的脏数据,暂使用查询list的方式
+            List<FsUserCoursePeriodDays> fsUserCoursePeriodDays = fsUserCoursePeriodDaysMapper.selectFsUserCoursePeriodDaysList(periodDays);
+            if(fsUserCoursePeriodDays != null && !fsUserCoursePeriodDays.isEmpty()){
+                periodDays = fsUserCoursePeriodDays.get(0);
+            }
+            if(periodDays != null && periodDays.getLastJoinTime() !=null && LocalDateTime.now().isAfter(periodDays.getLastJoinTime())) {
+                return R.error(403,"已超过领取红包时间");
+            }
+        }
+
+        // 确定红包金额
+        BigDecimal amount = BigDecimal.ZERO;
+        FsUserCourseVideoRedPackage redPackage = fsUserCourseVideoRedPackageMapper.selectRedPacketByCompanyId(param.getVideoId(), param.getCompanyId(), param.getPeriodId());
+
+        if (redPackage != null) {
+            amount = redPackage.getRedPacketMoney();
+        } else if (video != null) {
+            amount = video.getRedPacketMoney();
+        }
+
+        // 准备发送红包参数
+        WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
+//        packetParam.setOpenId(getOpenId(user.getUserId(), param.getCompanyId(), param.getSource()));
+        packetParam.setOpenId(user.getMpOpenId());
+        // 来源是小程序切换openId
+        if (param.getSource() == 2) {
+            //处理多小程序问题
+            Company company = companyMapper.selectCompanyById(param.getCompanyId());
+            if (company.getCourseMiniAppId()==null){
+                return R.error("销售公司参数错误,未绑定小程序");
+            }
+            FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(company.getCourseMiniAppId(),user.getUserId(),1);
+            if (fsUserWx ==null || fsUserWx.getOpenId()==null){
+                return R.error("小程序openId参数缺失");
+            }
+
+            System.out.println("小程序id"+user.getCourseMaOpenId());
+            //查出公司绑定openid并赋值
+            packetParam.setOpenId(fsUserWx.getOpenId());
+        }
+        packetParam.setAmount(amount);
+        packetParam.setSource(param.getSource());
+        packetParam.setRedPacketMode(config.getRedPacketMode());
+        packetParam.setCompanyId(param.getCompanyId());
+
+        System.out.println("红包金额"+amount);
+        System.out.println("红包商户号"+packetParam);
+        //2025.6.19 红包金额为0的时候
+        if (amount.compareTo(BigDecimal.ZERO)>0){
+
+            Company company = companyMapper.selectCompanyByIdForUpdate(param.getCompanyId());
+            BigDecimal money = company.getMoney();
+            BigDecimal subtract = money.subtract(amount);
+            if (subtract.compareTo(BigDecimal.ZERO)<0){
+                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                redPacketLog.setCourseId(param.getCourseId());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(2);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+                return R.error("销售公司余额不足");
+            }
+            // 发送红包
+            R sendRedPacket = paymentService.sendRedPacket(packetParam);
+            if (sendRedPacket.get("code").equals(200)) {
+                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                TransferBillsResult transferBillsResult;
+                if (sendRedPacket.get("isNew").equals(1)){
+                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                }else {
+                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                }
+                // 添加红包记录
+                redPacketLog.setCourseId(param.getCourseId());
+//            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(0);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+                // 更新观看记录的奖励类型
+                log.setRewardType(config.getRewardType());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+                company.setMoney(subtract);
+                companyMapper.updateCompany(company);
+
+                CompanyMoneyLogs logs=new CompanyMoneyLogs();
+                logs.setCompanyId(company.getCompanyId());
+                logs.setRemark("扣除红包金额");
+                logs.setMoney(amount.multiply(new BigDecimal(-1)));
+                logs.setLogsType(15);
+                logs.setBalance(company.getMoney());
+                logs.setCreateTime(new Date());
+                moneyLogsMapper.insertCompanyMoneyLogs(logs);
+
+                return sendRedPacket;
+            } else {
+                return R.error("奖励发送失败,请联系客服");
+            }
+        } else {
+            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+            // 添加红包记录
+            redPacketLog.setCourseId(param.getCourseId());
+//            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+            redPacketLog.setCompanyId(param.getCompanyId());
+            redPacketLog.setUserId(param.getUserId());
+            redPacketLog.setVideoId(param.getVideoId());
+            redPacketLog.setStatus(0);
+            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+            redPacketLog.setCompanyUserId(param.getCompanyUserId());
+            redPacketLog.setCreateTime(new Date());
+            redPacketLog.setAmount(BigDecimal.ZERO);
+            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+            redPacketLog.setPeriodId(param.getPeriodId());
+            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+            // 更新观看记录的奖励类型
+//            if (param.getLinkType() == null || param.getLinkType() == 0) {
+            log.setRewardType(config.getRewardType());
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
+//            }
+            return R.ok("红包发送成功");
+        }
+
+    }
+
     /**
      * 获取用户openId
      *

+ 28 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseStaticsCountVO.java

@@ -0,0 +1,28 @@
+package com.fs.course.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+
+@Data
+public class FsCourseStaticsCountVO{
+
+    @ApiModelProperty(value = "总学习时长")
+    private Long courseWatchNum;
+
+    @ApiModelProperty(value = "到课数")
+    private Long courseCompleteNum;
+
+    @ApiModelProperty(value = "答题红包数")
+    private Long redPacketCount;
+
+    @ApiModelProperty(value = "答题红包金额")
+    private BigDecimal redPacketAmount;
+
+    @ApiModelProperty(value = "正确答题数")
+    private Long correctAnswerNum;
+
+}

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

@@ -4,6 +4,9 @@ import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 
+import java.time.LocalDateTime;
+import java.util.Date;
+
 @Data
 public class FsCourseWatchLogVO extends BaseEntity  {
 
@@ -54,4 +57,8 @@ public class FsCourseWatchLogVO extends BaseEntity  {
     private String title;
 
     private String courseName;
+
+    private String videoName;
+    private Date finishTime;
+    private LocalDateTime lastHeartbeatTime;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCourseComplaintRecordPageListVO.java

@@ -30,6 +30,9 @@ public class FsUserCourseComplaintRecordPageListVO extends BaseEntity{
 //    @Excel(name = "投诉内容")
     private String complaintContent;
 
+    @Excel(name = "投诉上传图片")
+    private String complaintUrl;
+
     @Excel(name = "课程名称")
     private String courseName;
 

+ 13 - 2
fs-service/src/main/java/com/fs/his/domain/FsUserWx.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.*;
 import lombok.Data;
 
 import java.time.LocalDateTime;
+import java.util.Date;
 
 @Data
 @TableName("fs_user_wx")
@@ -17,6 +18,11 @@ public class FsUserWx {
      * 用户ID
      */
     private Long fsUserId;
+
+    /**
+     * 公司ID
+     */
+    private Long companyId;
     /**
      * 小程序/公众号appId
      */
@@ -29,12 +35,17 @@ public class FsUserWx {
      * 微信openId
      */
     private String openId;
+
+    /**
+     * 1:小程序 2:服务号
+     */
+    private Integer type;
     /**
      * 创建时间
      */
-    private LocalDateTime createTime;
+    private Date createTime;
     /**
      * 修改时间
      */
-    private LocalDateTime updateTime;
+    private Date updateTime;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/mapper/FsDoctorMapper.java

@@ -175,10 +175,13 @@ public interface FsDoctorMapper
     Long selectFsDoctorType2Ids(Integer type);
     @Select("select * from fs_doctor where doctor_id=#{doctorId} for update")
     FsDoctor selectFsDoctorByDoctorIdForUpdate(Long doctorId);
+
     @Select("select * from fs_doctor where doctor_type=2 and  `status`=1 and is_audit=1 and sign_url is not null and audit_type LIKE CONCAT('%', #{type}, '%')  ORDER BY RAND() LIMIT 1")
     FsDoctor selectPackageFsDoctorType2Ids(Integer type);
+
     @Select("select doctor_id from fs_doctor where doctor_type=1 and  `status`=1 and is_audit=1 and is_agreement_prescribe_doctor=1  and sign_url is not null")
     List<Long>  selectFsDoctorDoctorByPackage();
+
     @Select("select doctor_id from fs_doctor where doctor_type=1 and  `status`=1 and is_audit=1 and dept_id=39 and is_follow=1  ORDER BY RAND() LIMIT 1")
     Long selectFollowDoctorDoctorByPackage();
     @Select("select * from fs_doctor where doctor_type=1 and  `status`=1 and is_audit=1 and dept_id=39 ")

+ 19 - 9
fs-service/src/main/java/com/fs/his/mapper/FsUserOperationLogMapper.java

@@ -3,33 +3,43 @@ package com.fs.his.mapper;
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.his.domain.FsUserOperationLog;
+import com.fs.his.param.FsUserOperationLogQueryParam;
+import com.fs.his.vo.FsUserOperationLogVo;
 
 /**
  * 用户操作日志Mapper接口
- * 
+ *
  * @author fs
  * @date 2025-07-04
  */
 public interface FsUserOperationLogMapper extends BaseMapper<FsUserOperationLog>{
     /**
      * 查询用户操作日志
-     * 
+     *
      * @param logId 用户操作日志主键
      * @return 用户操作日志
      */
-    FsUserOperationLog selectFsUserOperationLogByLogId(Long logId);
+    FsUserOperationLogVo selectFsUserOperationLogByLogId(Long logId);
 
     /**
      * 查询用户操作日志列表
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 用户操作日志集合
      */
-    List<FsUserOperationLog> selectFsUserOperationLogList(FsUserOperationLog fsUserOperationLog);
+    List<FsUserOperationLog> selectFsUserOperationLogList(FsUserOperationLogQueryParam fsUserOperationLog);
+
+    /**
+     * 查询用户操作日志列表
+     *
+     * @param fsUserOperationLog 用户操作日志
+     * @return 用户操作日志集合
+     */
+    List<FsUserOperationLogVo> selectFsUserOperationLogByList(FsUserOperationLog fsUserOperationLog);
 
     /**
      * 新增用户操作日志
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 结果
      */
@@ -37,7 +47,7 @@ public interface FsUserOperationLogMapper extends BaseMapper<FsUserOperationLog>
 
     /**
      * 修改用户操作日志
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 结果
      */
@@ -45,7 +55,7 @@ public interface FsUserOperationLogMapper extends BaseMapper<FsUserOperationLog>
 
     /**
      * 删除用户操作日志
-     * 
+     *
      * @param logId 用户操作日志主键
      * @return 结果
      */
@@ -53,7 +63,7 @@ public interface FsUserOperationLogMapper extends BaseMapper<FsUserOperationLog>
 
     /**
      * 批量删除用户操作日志
-     * 
+     *
      * @param logIds 需要删除的数据主键集合
      * @return 结果
      */

+ 20 - 0
fs-service/src/main/java/com/fs/his/param/FsUserOperationLogQueryParam.java

@@ -0,0 +1,20 @@
+package com.fs.his.param;
+
+import com.fs.common.param.BaseQueryParam;
+import lombok.Data;
+
+@Data
+public class FsUserOperationLogQueryParam extends BaseQueryParam {
+    /** 用户id */
+    private Long userId;
+
+    /** 操作类型 */
+    private String operationType;
+
+    /** 详情 */
+    private String details;
+
+    private Long companyId;
+
+    private Long companyUserId;
+}

+ 14 - 9
fs-service/src/main/java/com/fs/his/service/IFsUserOperationLogService.java

@@ -3,33 +3,36 @@ package com.fs.his.service;
 import java.util.List;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.his.domain.FsUserOperationLog;
+import com.fs.his.param.FsUserOperationLogQueryParam;
+import com.fs.his.vo.FsUserOperationLogPageVo;
+import com.fs.his.vo.FsUserOperationLogVo;
 
 /**
  * 用户操作日志Service接口
- * 
+ *
  * @author fs
  * @date 2025-07-04
  */
 public interface IFsUserOperationLogService extends IService<FsUserOperationLog>{
     /**
      * 查询用户操作日志
-     * 
+     *
      * @param logId 用户操作日志主键
      * @return 用户操作日志
      */
-    FsUserOperationLog selectFsUserOperationLogByLogId(Long logId);
+    FsUserOperationLogVo selectFsUserOperationLogByLogId(Long logId);
 
     /**
      * 查询用户操作日志列表
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 用户操作日志集合
      */
-    List<FsUserOperationLog> selectFsUserOperationLogList(FsUserOperationLog fsUserOperationLog);
+    List<FsUserOperationLogPageVo> selectFsUserOperationLogList(FsUserOperationLogQueryParam fsUserOperationLog);
 
     /**
      * 新增用户操作日志
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 结果
      */
@@ -37,7 +40,7 @@ public interface IFsUserOperationLogService extends IService<FsUserOperationLog>
 
     /**
      * 修改用户操作日志
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 结果
      */
@@ -45,7 +48,7 @@ public interface IFsUserOperationLogService extends IService<FsUserOperationLog>
 
     /**
      * 批量删除用户操作日志
-     * 
+     *
      * @param logIds 需要删除的用户操作日志主键集合
      * @return 结果
      */
@@ -53,9 +56,11 @@ public interface IFsUserOperationLogService extends IService<FsUserOperationLog>
 
     /**
      * 删除用户操作日志信息
-     * 
+     *
      * @param logId 用户操作日志主键
      * @return 结果
      */
     int deleteFsUserOperationLogByLogId(Long logId);
+
+    List<FsUserOperationLogVo> selectFsUserOperationLogByList(FsUserOperationLog fsUserOperationLog);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserWxService.java

@@ -10,4 +10,6 @@ public interface IFsUserWxService extends IService<FsUserWx> {
      * @param wx    配置信息
      */
     void saveOrUpdateByUniqueKey(FsUserWx wx);
+
+    FsUserWx selectByAppIdAndUserId(String appId, Long userId,Integer type);
 }

+ 74 - 11
fs-service/src/main/java/com/fs/his/service/impl/FsUserOperationLogServiceImpl.java

@@ -1,8 +1,21 @@
 package com.fs.his.service.impl;
 
+import java.util.ArrayList;
 import java.util.List;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
 import com.fs.common.utils.DateUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.StringUtils;
+import com.fs.course.domain.FsCourseRedPacketLog;
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
+import com.fs.course.vo.FsUserCourseVO;
+import com.fs.his.param.FsUserOperationLogQueryParam;
+import com.fs.his.vo.FsUserOperationLogPageVo;
+import com.fs.his.vo.FsUserOperationLogParamVo;
+import com.fs.his.vo.FsUserOperationLogVo;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.his.mapper.FsUserOperationLogMapper;
@@ -11,40 +24,75 @@ import com.fs.his.service.IFsUserOperationLogService;
 
 /**
  * 用户操作日志Service业务层处理
- * 
+ *
  * @author fs
  * @date 2025-07-04
  */
 @Service
 public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLogMapper, FsUserOperationLog> implements IFsUserOperationLogService {
-
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
     /**
      * 查询用户操作日志
-     * 
+     *
      * @param logId 用户操作日志主键
      * @return 用户操作日志
      */
     @Override
-    public FsUserOperationLog selectFsUserOperationLogByLogId(Long logId)
+    public FsUserOperationLogVo selectFsUserOperationLogByLogId(Long logId)
     {
         return baseMapper.selectFsUserOperationLogByLogId(logId);
     }
 
     /**
      * 查询用户操作日志列表
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 用户操作日志
      */
     @Override
-    public List<FsUserOperationLog> selectFsUserOperationLogList(FsUserOperationLog fsUserOperationLog)
+    public List<FsUserOperationLogPageVo> selectFsUserOperationLogList(FsUserOperationLogQueryParam fsUserOperationLog)
     {
-        return baseMapper.selectFsUserOperationLogList(fsUserOperationLog);
+        List<FsUserOperationLog> logs = baseMapper.selectFsUserOperationLogList(fsUserOperationLog);
+        ArrayList<FsUserOperationLogPageVo> vos = new ArrayList<>();
+        if(logs!=null&& !logs.isEmpty()){
+            for (FsUserOperationLog log : logs) {
+                FsUserOperationLogPageVo vo = new FsUserOperationLogPageVo();
+                BeanUtils.copyProperties(log, vo);
+                StringBuilder richText = new StringBuilder();
+                richText.append("<div>")
+                        .append("<div style='font-weight: bold;'>")
+                        .append(log.getOperationType())
+                        .append("<div>");
+                String param = log.getParam();
+                if (StringUtils.isNotBlank(param)){
+                    FsUserCourseVO courseVO = JSON.parseObject(param, FsUserCourseVO.class);
+                    if(courseVO!=null){
+                        if (StringUtils.isNotBlank(courseVO.getCourseName())){
+                            richText.append("<div style='font-weight: bold;'>").append("课节名称").append("</div>");
+                            richText.append("<p>").append(courseVO.getCourseName()).append("<p>");
+                        }
+                        if (StringUtils.isNotBlank(courseVO.getTrainingCampName())){
+                            richText.append("<div style='font-weight: bold;'>").append("所属训练营").append("</div>");
+                            richText.append("<p>").append(courseVO.getTrainingCampName()).append("<p>");
+                        }
+                        if (StringUtils.isNotBlank(courseVO.getTrainingCampName())){
+                            richText.append("<div style='font-weight: bold;'>").append("营期名称").append("</div>");
+                            richText.append("<p>").append(courseVO.getPeriodName()).append("<p>");
+                        }
+                    }
+                }
+                richText.append("</div>");
+                vo.setText(richText.toString());
+                vos.add(vo);
+            }
+        }
+        return vos;
     }
 
     /**
      * 新增用户操作日志
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 结果
      */
@@ -57,7 +105,7 @@ public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLo
 
     /**
      * 修改用户操作日志
-     * 
+     *
      * @param fsUserOperationLog 用户操作日志
      * @return 结果
      */
@@ -69,7 +117,7 @@ public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLo
 
     /**
      * 批量删除用户操作日志
-     * 
+     *
      * @param logIds 需要删除的用户操作日志主键
      * @return 结果
      */
@@ -81,7 +129,7 @@ public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLo
 
     /**
      * 删除用户操作日志信息
-     * 
+     *
      * @param logId 用户操作日志主键
      * @return 结果
      */
@@ -90,4 +138,19 @@ public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLo
     {
         return baseMapper.deleteFsUserOperationLogByLogId(logId);
     }
+
+    @Override
+    public List<FsUserOperationLogVo> selectFsUserOperationLogByList(FsUserOperationLog fsUserOperationLog) {
+        List<FsUserOperationLogVo> list = baseMapper.selectFsUserOperationLogByList(fsUserOperationLog);
+        list.forEach(e->{
+            FsUserOperationLogParamVo fsUserOperationLogParamVo = JSON.parseObject(e.getParam(), FsUserOperationLogParamVo.class);
+            e.setParamVo(fsUserOperationLogParamVo);
+            if (ObjectUtil.isEmpty(fsUserOperationLogParamVo)){
+                return;
+            }
+            FsCourseRedPacketLog fsCourseRedPacketLog = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByTemporary(fsUserOperationLogParamVo.getVideoId(),e.getUserId());
+            e.setFsCourseRedPacketLog(fsCourseRedPacketLog);
+        });
+        return list;
+    }
 }

+ 11 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserWxServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.his.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.his.domain.FsUserWx;
 import com.fs.his.mapper.FsUserWxMapper;
@@ -19,4 +20,14 @@ public class FsUserWxServiceImpl extends ServiceImpl<FsUserWxMapper, FsUserWx> i
     public void saveOrUpdateByUniqueKey(FsUserWx wx) {
         baseMapper.insertOrUpdateByUniqueKey(wx);
     }
+
+    @Override
+    public FsUserWx selectByAppIdAndUserId(String appId, Long userId,Integer type) {
+        return this.baseMapper.selectOne(
+                new QueryWrapper<FsUserWx>()
+                        .eq("app_id", appId)
+                        .eq("fs_user_id", userId)
+                        .eq("type", type)
+        );
+    }
 }

+ 31 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogPageVo.java

@@ -0,0 +1,31 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsUserOperationLogPageVo {
+    private Long logId;
+
+
+
+    /** 操作类型 */
+    @Excel(name = "操作类型")
+    private String operationType;
+
+    /** 详情 */
+    @Excel(name = "详情")
+    private String details;
+
+    @Excel(name = "参数")
+    private String param;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+
+    private String text;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogParamVo.java

@@ -0,0 +1,19 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 用户操作日志对象返回子参数
+ */
+@Data
+public class FsUserOperationLogParamVo {
+    private Integer courseId;
+    private String courseName;
+    private Integer periodId;
+    private String periodName;
+    private String title;
+    private Integer trainingCampId;
+    private String trainingCampName;
+    private Long videoId;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogVo.java

@@ -0,0 +1,19 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fs.common.annotation.Excel;
+import com.fs.course.domain.FsCourseRedPacketLog;
+import com.fs.course.vo.FsCourseRedPacketLogListPVO;
+import com.fs.his.domain.FsUserOperationLog;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 用户操作日志对象返回参数
+ */
+@Data
+public class FsUserOperationLogVo extends FsUserOperationLog{
+
+    public FsUserOperationLogParamVo paramVo;
+    public FsCourseRedPacketLog fsCourseRedPacketLog;
+}

+ 7 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -233,7 +233,11 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "            <if test=\"description != null  and description != ''\"> and ec.description = #{description}</if>\n" +
 
             "<if test ='tagIds!=null and tagIds.size()!=0 '> " +
-            " and <foreach collection='tagIds'  item='item' index='index'  open='( 1=2 ' separator='' close=')'> or find_in_set(  #{item} , REGEXP_REPLACE ( ec.tag_ids, '[\"\\\\[\\\\]]', '' ) )  </foreach> " +
+            "    and (\n" +
+            "    <foreach collection='tagIds' item='item' index='index' separator=' AND '>\n" +
+            "        find_in_set(#{item}, REGEXP_REPLACE(ec.tag_ids, '[\"\\\\[\\\\]]', ''))\n" +
+            "    </foreach>\n" +
+            "    )"+
             "</if> " +
 
             "            <if test=\"remarkMobiles != null  and remarkMobiles != ''\"> and ec.remark_mobiles like concat( #{remarkMobiles}, '%')</if>\n" +
@@ -404,4 +408,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     @Select("select sum(amount) as redPacketAmount from fs_course_red_packet_log where user_id = #{userId}")
     BigDecimal getRedPacketAmount(@Param("userId") Long userId);
 
+    List<QwExternalContact> selectExternalByFsUserIds(@Param("userIds")List<Long> userIds);
+
 }

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

@@ -423,4 +423,5 @@ public interface QwUserMapper extends BaseMapper<QwUser>
      */
     List<ExternalContactQwUserVO> selectQwUserByFsUserId(@Param("fsUserId") Long fsUserId);
 
+    void updateSendType(UpdateSendTypeVo vo);
 }

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

@@ -5,10 +5,7 @@ import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.dto.QwUserKeyDTO;
 import com.fs.qw.param.*;
-import com.fs.qw.vo.QwHookAuthVO;
-import com.fs.qw.vo.QwOptionsVO;
-import com.fs.qw.vo.QwUserVO;
-import com.fs.qw.vo.UserVOs;
+import com.fs.qw.vo.*;
 import com.fs.sop.domain.QwSop;
 import org.apache.commons.lang3.tuple.Pair;
 
@@ -188,4 +185,6 @@ public interface IQwUserService
     List<UserVOs> getUserList(Long userId);
 
     List<UserVOs> getQwUserList(Long userId, String qwUserId);
+
+    R updateSendType(UpdateSendTypeVo vo);
 }

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

@@ -5418,6 +5418,8 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
             } else {
                 externalContactInfoVO.setContactLevel("未注册会员");
             }
+        } else {
+            externalContactInfoVO.setContactLevel("未注册会员");
         }
 
         // 设置红包和答题

+ 12 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -1387,6 +1387,18 @@ public class QwUserServiceImpl implements IQwUserService
         return qwWorkTaskMapper.getQwUserList(userId, qwUserId);
     }
 
+    @Override
+    public R updateSendType(UpdateSendTypeVo vo) {
+        if(vo.getIds() == null || vo.getIds().isEmpty()) return R.error("修改人不能为空");
+        qwUserMapper.updateSendType(vo);
+        List<QwUser> qwUsers = qwUserMapper.selectBatchIds(vo.getIds());
+        qwUsers.forEach(e -> {
+            redisCache.setCacheObject("qwUserRd:"+e.getCorpId()+":"+e.getQwUserId() ,JSON.toJSONString(e),1, TimeUnit.HOURS);
+        });
+        return R.ok();
+    }
+
+
     /**
      * 构建查询条件
      * **/

+ 1 - 0
fs-service/src/main/java/com/fs/qw/vo/QwUserVO.java

@@ -74,6 +74,7 @@ public class QwUserVO {
     private Integer ipadStatus;
     private Long serverId;
     private Integer serverStatus;
+    private Integer sendMsgType;
 
     /**
      * 是否自动发课 00、禁用,01、启用

+ 13 - 0
fs-service/src/main/java/com/fs/qw/vo/UpdateSendTypeVo.java

@@ -0,0 +1,13 @@
+package com.fs.qw.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class UpdateSendTypeVo {
+
+    private List<Long> ids;
+    private Integer type;
+
+}

+ 4 - 0
fs-service/src/main/java/com/fs/sop/domain/QwSopLogs.java

@@ -87,6 +87,10 @@ public class QwSopLogs implements Serializable {
     private Integer sort;
     private Long ms;
     private Long qwUserKey;
+
+    /**
+     * 官方群发的限制 1官方+补发   2官方+一对一通连
+     */
     @TableField(exist = false)
     private Integer takeRecords;
 

+ 8 - 2
fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -666,17 +666,23 @@ public class QwSopLogsServiceImpl extends ServiceImpl<QwSopLogsMapper, QwSopLogs
                 switch (matched.getStatus()) {
                     case 0:
                         log.setSendStatus(5L);
-                        log.setRemark("员工未发送,已作废");
+                        log.setRemark("员工未在规定时间发送");
                         break;
                     case 1:
                         log.setSendStatus(1L);
                         log.setReceivingStatus(1L);
                         break;
                     case 2:
+                        log.setSendType(2);
+                        log.setSendStatus(3L);
+                        log.setRemark("因客户不是好友导致发送失败,补发");
+                        log.setReceivingStatus(0L);
+                        log.setSendTime(now);
+                        log.setSort(30000001);
                     case 3:
                         log.setSendType(2);
                         log.setSendStatus(3L);
-                        log.setRemark("客户无法接收,补发");
+                        log.setRemark("客户已经收到其他群发消息,补发");
                         log.setReceivingStatus(0L);
                         log.setSendTime(now);
                         log.setSort(30000001);

+ 1 - 1
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -460,7 +460,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService
             FsUserCourseVideoRedPackage red = new FsUserCourseVideoRedPackage();
             red.setCompanyId(temp.getCompanyId());
             red.setVideoId(e.getVideoId());
-            red.setRedPacketMoney(redMap.getOrDefault(e.getVideoId(), Optional.of(courseConfig.getRedPackageMoney())).orElse(BigDecimal.ZERO));
+            red.setRedPacketMoney(redMap.getOrDefault(e.getVideoId(), Optional.ofNullable(courseConfig.getRedPackageMoney())).orElse(BigDecimal.ZERO));
             red.setRuleId(e.getId());
             red.setDataType(3);
             return red;

+ 10 - 2
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -834,7 +834,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 setting.setVideoId(param.getVideoId());
                 setting.setCourseId(param.getCourseId());
                 setting.setCourseType(param.getCourseType());
-
+                try {
+                    sopLogs.setQwUserKey(Long.valueOf(qwUserId));
+                }catch (Exception e){
+                    log.error("设置qwUserId异常", e);
+                }
                 sopLogs.setContentJson(JSON.toJSONString(setting));
 
                 sopLogsList.add(sopLogs);
@@ -1025,7 +1029,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             setting.setVideoId(param.getVideoId());
             setting.setCourseId(param.getCourseId());
             setting.setCourseType(param.getCourseType());
-
+            try {
+                sopLogs.setQwUserKey(qwUser.getId());
+            }catch (Exception e){
+                log.error("设置qwUserId异常", e);
+            }
             sopLogs.setContentJson(JSON.toJSONString(setting));
 
             sopLogsList.add(sopLogs);

+ 3 - 3
fs-service/src/main/resources/application-config-druid-drk.yml

@@ -16,10 +16,10 @@ wx:
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON
   cp:
-    corpId: wwb2a1055fb6c9a7c2
+    corpId: wwde3be9a4a7004380 #德瑞康企业id
     appConfigs:
-      - agentId: 1000005
-        secret: ec7okROXJqkNafq66-L6aKNv0asTzQIG0CYrj3vyBbo
+      - agentId: 1000006 #德瑞康应用id
+        secret: prclvyFLiZD9XEns9ltt6vFMszuFc_wgm3MTnfY6ZEk #德瑞康
         token: PPKOdAlCoMO
         aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
   pay:

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

@@ -60,7 +60,7 @@ watch:
   password3: v9xsKuqn_$d2y
 
 fs :
-  commonApi: http://172.16.0.16:8010
+  commonApi: http://172.30.0.11:8010
   h5CommonApi: http://119.29.195.254:8010
 nuonuo:
   key: 10924508

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

@@ -42,8 +42,8 @@ wx:
       port: 6379
       timeout: 2000
     configs:
-      - appId: wx5d516e5b575bae3c # 第一个公众号的appid  //公众号名称:成都九州在线互联网医院
-        secret: c911f6671909a81337c6a15cf45e57fc # 公众号的appsecret
+      - appId: wx47ccce1e1779bcc4 # 第一个公众号的appid  //公众号名称:首研生物
+        secret: 792adec2a722d57bfdfaa0953ed2b5ed # 公众号的appsecret
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
 aifabu:  #爱链接

+ 4 - 0
fs-service/src/main/resources/mapper/company/CompanyPostMapper.xml

@@ -129,4 +129,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             </if>
         </where>
     </select>
+    <select id="selectCompanyPostCode" resultType="com.fs.company.domain.CompanyPost">
+        <include refid="selectCompanyPostVo"/>
+        where post_code=#{postCode} AND company_id = #{companyId}
+    </select>
 </mapper>

+ 4 - 0
fs-service/src/main/resources/mapper/company/CompanyRoleMapper.xml

@@ -177,4 +177,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{roleId}
         </foreach>
     </select>
+
+    <select id="selectCompanyRoleByRoleKey" resultType="com.fs.company.domain.CompanyRole">
+        SELECT * FROM company_role WHERE del_flag=0 AND role_key = #{roleKey}
+    </select>
 </mapper>

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

@@ -113,5 +113,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             find_in_set(#{tagId}, ctu.tag_ids)
         </foreach>
     </select>
+    <select id="getTagByUser" resultType="com.fs.store.vo.h5.CompanyUserTagListVO">
+        SELECT distinct
+        company_tag.tag as tagName,
+        company_tag.tag_id
+        FROM
+        company_tag_user
+        right JOIN company_tag ON FIND_IN_SET( company_tag.tag_id, company_tag_user.tag_ids ) > 0
+        where company_tag_user.user_id = #{params.userId}
+            <if test="params.companyId != null">
+                and company_tag_user.company_id = #{params.companyId}
+            </if>
+            <if test="params.companyUserId != null">
+                and company_tag_user.company_user_id = #{params.companyUserId}
+            </if>
+    </select>
 
 </mapper>

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

@@ -510,4 +510,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <update id="batchUpdateUserDomain">
         update company_user set domain=#{domain} where user_id in <foreach collection="ids"  item="item" index="index" open="(" separator="," close=")">#{item}</foreach>
     </update>
+
+    <update id="batchUpdateUserDept">
+        UPDATE company_user
+        SET dept_id = CASE user_id
+        <foreach collection="companyUserList" item="item" separator=" ">
+            WHEN #{item.userId} THEN #{item.deptId}
+        </foreach>
+        END
+        WHERE user_id IN
+        <foreach collection="companyUserList" item="item" open="(" separator="," close=")">
+            #{item.userId}
+        </foreach>
+    </update>
 </mapper>

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

@@ -625,4 +625,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             (#{item.videoId}, #{item.userId}, #{item.companyUserId})
         </foreach>
     </update>
+
+
+    <select id="selectListBytrainingCampId" resultType="com.fs.course.vo.FsCourseWatchLogVO">
+        select
+        uc.course_name,v.title as video_name,
+        watch.log_id,
+        watch.user_id,
+        watch.finish_time,
+        watch.send_finish_msg,
+        watch.sop_id,
+        watch.video_id,
+        watch.reward_type,
+        watch.log_type,
+        watch.create_time,
+        watch.update_time,
+        watch.qw_external_contact_id,
+        watch.duration,
+        watch.qw_user_id,
+        watch.company_user_id,
+        watch.company_id,
+        watch.course_id,
+        watch.camp_period_time
+        from
+        fs_user_course_training_camp camp
+        left join fs_user_course_period period on
+        camp.training_camp_id = period.training_camp_id
+        left join fs_course_watch_log watch on
+        period.period_id = watch.period_id
+        left join fs_user_course uc on uc.course_id = watch.course_id
+        left join fs_user_course_video v on v.video_id = watch.video_id
+        <where>
+            `period`.del_flag = '0' and watch.log_type &lt;&gt; 3
+            <if test="trainingCampId != null">and camp.training_camp_id = #{trainingCampId}</if>
+            <if test="userId != null">and watch.user_id = #{userId}</if>
+            <if test="periodId != null">and `period`.period_id = #{periodId}</if>
+        </where>
+    </select>
+
 </mapper>

+ 21 - 6
fs-service/src/main/resources/mapper/course/FsUserCourseComplaintRecordMapper.xml

@@ -9,13 +9,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="userId"    column="user_id"    />
         <result property="complaintTypeId"    column="complaint_type_id"    />
         <result property="complaintContent"    column="complaint_content"    />
+        <result property="complaintUrl"    column="complaint_url"    />
         <result property="courseId"    column="course_id"    />
         <result property="videoId"    column="video_id"    />
         <result property="createTime"    column="create_time"    />
     </resultMap>
 
     <sql id="selectFsUserCourseComplaintRecordVo">
-        select record_id, user_id, complaint_type_id, complaint_content, course_id, video_id, create_time from fs_user_course_complaint_record
+        select record_id, user_id, complaint_type_id, complaint_content, complaint_url, course_id, video_id, create_time from fs_user_course_complaint_record
     </sql>
 
     <select id="selectFsUserCourseComplaintRecordList" parameterType="FsUserCourseComplaintRecord" resultMap="FsUserCourseComplaintRecordResult">
@@ -35,15 +36,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         ct.complaint_type_name,
         uc.course_name,
         ucv.title,
-        fs_user.nick_name,
-        if(ec.comment_status = 1,'已拉黑','正常') as status
+        fs_user.nick_name
+--         if(ec.comment_status = 1,'已拉黑','正常') as status
         FROM
         fs_user_course_complaint_record cr
         LEFT JOIN fs_user_course_complaint_type ct ON ct.complaint_type_id = cr.complaint_type_id
         LEFT JOIN fs_user_course uc ON uc.course_id = cr.course_id
         LEFT JOIN fs_user_course_video ucv ON ucv.video_id = cr.video_id
         LEFT JOIN fs_user ON fs_user.user_id = cr.user_id
-        left join qw_external_contact ec on cr.user_id = ec.fs_user_id
+--         left join qw_external_contact ec on cr.user_id = ec.fs_user_id
         <where>
             <if test="nickName != null and nickName != '' ">
                 and fs_user.nick_name like concat('%', #{nickName}, '%')
@@ -51,8 +52,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
     </select>
 
-    <select id="selectFsUserCourseComplaintRecordByRecordId" parameterType="Long" resultMap="FsUserCourseComplaintRecordResult">
-        <include refid="selectFsUserCourseComplaintRecordVo"/>
+    <select id="selectFsUserCourseComplaintRecordByRecordId" parameterType="Long" resultType="com.fs.course.vo.FsUserCourseComplaintRecordPageListVO">
+        SELECT
+        cr.*,
+        ct.complaint_type_name,
+        uc.course_name,
+        ucv.title,
+        fs_user.nick_name
+        FROM
+        fs_user_course_complaint_record cr
+        LEFT JOIN fs_user_course_complaint_type ct ON ct.complaint_type_id = cr.complaint_type_id
+        LEFT JOIN fs_user_course uc ON uc.course_id = cr.course_id
+        LEFT JOIN fs_user_course_video ucv ON ucv.video_id = cr.video_id
+        LEFT JOIN fs_user ON fs_user.user_id = cr.user_id
         where record_id = #{recordId}
     </select>
 
@@ -62,6 +74,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userId != null">user_id,</if>
             <if test="complaintTypeId != null">complaint_type_id,</if>
             <if test="complaintContent != null">complaint_content,</if>
+            <if test="complaintUrl != null">complaint_url,</if>
             <if test="courseId != null">course_id,</if>
             <if test="videoId != null">video_id,</if>
             <if test="createTime != null">create_time,</if>
@@ -70,6 +83,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userId != null">#{userId},</if>
             <if test="complaintTypeId != null">#{complaintTypeId},</if>
             <if test="complaintContent != null">#{complaintContent},</if>
+            <if test="complaintUrl != null">#{complaintUrl},</if>
             <if test="courseId != null">#{courseId},</if>
             <if test="videoId != null">#{videoId},</if>
             <if test="createTime != null">#{createTime},</if>
@@ -82,6 +96,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userId != null">user_id = #{userId},</if>
             <if test="complaintTypeId != null">complaint_type_id = #{complaintTypeId},</if>
             <if test="complaintContent != null">complaint_content = #{complaintContent},</if>
+            <if test="complaintUrl != null">complaint_url = #{complaintUrl},</if>
             <if test="courseId != null">course_id = #{courseId},</if>
             <if test="videoId != null">video_id = #{videoId},</if>
             <if test="createTime != null">create_time = #{createTime},</if>

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.