Przeglądaj źródła

Merge remote-tracking branch 'origin/master'

yjwang 4 dni temu
rodzic
commit
f057bd0e59
72 zmienionych plików z 1286 dodań i 1076 usunięć
  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. 2 1
      fs-common/src/main/java/com/fs/common/enums/DataSourceType.java
  6. 5 0
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  7. 2 0
      fs-company-app/src/main/java/com/fs/app/param/LoginParam.java
  8. 15 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  9. 2 1
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCourseTrainingCampController.java
  10. 35 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserOperationLogController.java
  11. 10 4
      fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java
  12. 6 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  13. 0 38
      fs-ipad-task/src/main/java/com/fs/app/controller/CommonController.java
  14. 6 5
      fs-ipad-task/src/main/java/com/fs/app/exception/FSExceptionHandler.java
  15. 4 2
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  16. 0 140
      fs-ipad-task/src/main/java/com/fs/app/task/QwMsgTask.java
  17. 16 12
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  18. 0 93
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsgTest.java
  19. 0 149
      fs-ipad-task/src/main/resources/application-dev.yml
  20. 0 149
      fs-ipad-task/src/main/resources/application-druid-ylrz.yml
  21. 0 151
      fs-ipad-task/src/main/resources/application-druid.yml
  22. 5 166
      fs-ipad-task/src/main/resources/application.yml
  23. 0 54
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwSopController.java
  24. 67 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwUserController.java
  25. 7 0
      fs-service/src/main/java/com/fs/common/param/LoginMaWxParam.java
  26. 2 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRealLink.java
  27. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourse.java
  28. 2 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  29. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseComplaintRecordMapper.java
  30. 4 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  31. 12 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  32. 32 0
      fs-service/src/main/java/com/fs/course/param/PeriodStatisticCountParam.java
  33. 7 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  34. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseComplaintRecordService.java
  35. 19 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
  36. 5 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  37. 27 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseComplaintRecordServiceImpl.java
  38. 50 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  39. 159 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  40. 28 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseStaticsCountVO.java
  41. 7 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogVO.java
  42. 3 0
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseComplaintRecordPageListVO.java
  43. 13 2
      fs-service/src/main/java/com/fs/his/domain/FsUserWx.java
  44. 10 2
      fs-service/src/main/java/com/fs/his/mapper/FsUserOperationLogMapper.java
  45. 4 1
      fs-service/src/main/java/com/fs/his/service/IFsUserOperationLogService.java
  46. 2 0
      fs-service/src/main/java/com/fs/his/service/IFsUserWxService.java
  47. 23 2
      fs-service/src/main/java/com/fs/his/service/impl/FsUserOperationLogServiceImpl.java
  48. 11 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserWxServiceImpl.java
  49. 19 0
      fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogParamVo.java
  50. 19 0
      fs-service/src/main/java/com/fs/his/vo/FsUserOperationLogVo.java
  51. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  52. 1 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  53. 3 4
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  54. 2 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  55. 12 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  56. 13 0
      fs-service/src/main/java/com/fs/qw/vo/UpdateSendTypeVo.java
  57. 1 1
      fs-service/src/main/resources/application-config-druid-sft.yml
  58. 38 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  59. 16 5
      fs-service/src/main/resources/mapper/course/FsUserCourseComplaintRecordMapper.xml
  60. 125 0
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  61. 4 0
      fs-service/src/main/resources/mapper/course/FsUserCourseTrainingCampMapper.xml
  62. 11 0
      fs-service/src/main/resources/mapper/his/FsUserOperationLogMapper.xml
  63. 8 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  64. 3 0
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml
  65. 6 2
      fs-user-course/src/main/java/com/fs/course/controller/CourseWxH5Controller.java
  66. 172 74
      fs-user-course/src/main/java/com/fs/course/controller/WxCompanyUserController.java
  67. 133 0
      fs-user-course/src/main/java/com/fs/course/controller/WxH5MpController.java
  68. 1 9
      fs-user-course/src/main/java/com/fs/course/controller/WxMpController.java
  69. 13 0
      fs-user-course/src/main/java/com/fs/course/param/FsUserLoginByMpParam.java
  70. BIN
      fs-user-course/src/main/resources/apiclient_cert.p12
  71. 2 0
      fs-user-course/src/main/resources/banner.txt
  72. 1 0
      pom.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);
+    }
+}

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

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

+ 5 - 0
fs-company-app/src/main/java/com/fs/app/controller/UserController.java

@@ -156,6 +156,7 @@ public class UserController extends AppBaseController {
         }
 
         try {
+
             //判断用户基本规则
             CompanyUser companyUser = userService.selectUserByUserName(param.getAccount());
             if (companyUser == null) {
@@ -176,6 +177,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());
             // 岗位

+ 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;
 }

+ 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);
+    }
 }

+ 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    自动发消息(蔡汉皋药房)

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

@@ -108,58 +108,4 @@ public class QwSopController {
         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();
-    }
-
-
 }

+ 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();
+    }
+
+
 }

+ 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;
+
 }

+ 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;
+
 }

+ 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);
 
     /**
      * 查询看课投诉记录列表

+ 4 - 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,7 @@ public interface FsUserCourseMapper
             "<if test = ' maps.isShow !=null '> " +
             "and c.is_show = #{maps.isShow} " +
             "</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);
 }

+ 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;
+
+}
+

+ 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;
 }

+ 10 - 2
fs-service/src/main/java/com/fs/his/mapper/FsUserOperationLogMapper.java

@@ -4,7 +4,7 @@ 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.FsUserOperationLogPageVo;
+import com.fs.his.vo.FsUserOperationLogVo;
 
 /**
  * 用户操作日志Mapper接口
@@ -19,7 +19,7 @@ public interface FsUserOperationLogMapper extends BaseMapper<FsUserOperationLog>
      * @param logId 用户操作日志主键
      * @return 用户操作日志
      */
-    FsUserOperationLog selectFsUserOperationLogByLogId(Long logId);
+    FsUserOperationLogVo selectFsUserOperationLogByLogId(Long logId);
 
     /**
      * 查询用户操作日志列表
@@ -29,6 +29,14 @@ public interface FsUserOperationLogMapper extends BaseMapper<FsUserOperationLog>
      */
     List<FsUserOperationLog> selectFsUserOperationLogList(FsUserOperationLogQueryParam fsUserOperationLog);
 
+    /**
+     * 查询用户操作日志列表
+     *
+     * @param fsUserOperationLog 用户操作日志
+     * @return 用户操作日志集合
+     */
+    List<FsUserOperationLogVo> selectFsUserOperationLogByList(FsUserOperationLog fsUserOperationLog);
+
     /**
      * 新增用户操作日志
      *

+ 4 - 1
fs-service/src/main/java/com/fs/his/service/IFsUserOperationLogService.java

@@ -5,6 +5,7 @@ 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接口
@@ -19,7 +20,7 @@ public interface IFsUserOperationLogService extends IService<FsUserOperationLog>
      * @param logId 用户操作日志主键
      * @return 用户操作日志
      */
-    FsUserOperationLog selectFsUserOperationLogByLogId(Long logId);
+    FsUserOperationLogVo selectFsUserOperationLogByLogId(Long logId);
 
     /**
      * 查询用户操作日志列表
@@ -60,4 +61,6 @@ public interface IFsUserOperationLogService extends IService<FsUserOperationLog>
      * @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);
 }

+ 23 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsUserOperationLogServiceImpl.java

@@ -3,13 +3,18 @@ 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;
@@ -25,7 +30,8 @@ import com.fs.his.service.IFsUserOperationLogService;
  */
 @Service
 public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLogMapper, FsUserOperationLog> implements IFsUserOperationLogService {
-
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
     /**
      * 查询用户操作日志
      *
@@ -33,7 +39,7 @@ public class FsUserOperationLogServiceImpl extends ServiceImpl<FsUserOperationLo
      * @return 用户操作日志
      */
     @Override
-    public FsUserOperationLog selectFsUserOperationLogByLogId(Long logId)
+    public FsUserOperationLogVo selectFsUserOperationLogByLogId(Long logId)
     {
         return baseMapper.selectFsUserOperationLogByLogId(logId);
     }
@@ -132,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)
+        );
+    }
 }

+ 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;
+}

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

@@ -404,4 +404,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();
+    }
+
+
     /**
      * 构建查询条件
      * **/

+ 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;
+
+}

+ 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

+ 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>

+ 16 - 5
fs-service/src/main/resources/mapper/course/FsUserCourseComplaintRecordMapper.xml

@@ -36,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}, '%')
@@ -52,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>
 

+ 125 - 0
fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -262,4 +262,129 @@
         </foreach>
     </select>
 
+
+    <select id="setlectCourseCompleteNum" resultType="java.lang.Long">
+        select
+        count(watch.log_id)
+        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
+        <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>
+
+
+    <select id="setlectCourseWatchNum" resultType="java.lang.Long">
+        select
+        sum(watch.duration)
+        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
+        <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>
+
+    <select id="setlectRedPacketCount" resultType="java.lang.Long">
+        select
+            count(packet.log_id)
+        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_red_packet_log packet on
+                period.period_id = packet.period_id
+        <where>
+            `period`.del_flag = '0' and packet.status =1
+            <if test="trainingCampId != null">and camp.training_camp_id = #{trainingCampId}</if>
+            <if test="userId != null">and packet.user_id = #{userId}</if>
+            <if test="periodId != null">and `period`.period_id = #{periodId}</if>
+        </where>
+    </select>
+
+    <select id="setlectRedPacketAmount" resultType="java.math.BigDecimal">
+        select
+            sum(packet.amount)
+        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_red_packet_log packet on
+                period.period_id = packet.period_id
+        <where>
+            `period`.del_flag = '0' and packet.status =1
+            <if test="trainingCampId != null">and camp.training_camp_id = #{trainingCampId}</if>
+            <if test="userId != null">and packet.user_id = #{userId}</if>
+            <if test="periodId != null">and `period`.period_id = #{periodId}</if>
+        </where>
+    </select>
+
+    <select id="setlectCorrectAnswerNum" resultType="java.lang.Long">
+        select
+            count(answer.log_id)
+        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_answer_logs answer on
+                period.period_id = answer.period_id
+        <where>
+            `period`.del_flag = '0' and answer.is_right =1
+            <if test="trainingCampId != null">and camp.training_camp_id = #{trainingCampId}</if>
+            <if test="userId != null">and answer.user_id = #{userId}</if>
+            <if test="periodId != null">and `period`.period_id = #{periodId}</if>
+        </where>
+    </select>
+
+
+    <select id="selectFsPeriodlist" resultMap="FsUserCoursePeriodResult">
+        select
+        period.period_id,
+        period.period_name,
+        period.company_id,
+        period.training_camp_id,
+        period.create_time,
+        period.update_time,
+        period.course_style,
+        period.live_room_style,
+        period.red_packet_grant_method,
+        period.period_type,
+        period.period_starting_time,
+        period.period_end_time,
+        period.period_status,
+        period.view_start_time,
+        period.view_end_time,
+        period.last_join_time,
+        period.max_view_num,
+        period.course_logo,
+        period.open_comment_status,
+        period.del_flag
+        from
+        fs_user_course_period period
+        left join fs_course_watch_log watch on
+        period.period_id = watch.period_id
+        <where>
+            `period`.del_flag = '0' and watch.log_type &lt;&gt; 3
+            <if test="trainingCampId != null">and `period`.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>
+        group by
+        `period`.period_id
+    </select>
 </mapper>

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

@@ -24,6 +24,10 @@
             <if test="params.companyId != null and params.companyId != ''">
                 and ctp.company_id like concat('%',#{params.companyId},'%')
             </if>
+
+            <if test="params.userId != null and params.userId != ''">
+                and cu.user_id = #{params.userId}
+            </if>
         </where>
         group by ctc.training_camp_id, ctc.training_camp_name, ctc.order_number
         order by

+ 11 - 0
fs-service/src/main/resources/mapper/his/FsUserOperationLogMapper.xml

@@ -27,6 +27,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         order by create_time desc
     </select>
 
+    <select id="selectFsUserOperationLogByList" parameterType="FsUserOperationLog" resultType="com.fs.his.vo.FsUserOperationLogVo">
+        <include refid="selectFsUserOperationLogVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="operationType != null  and operationType != ''"> and operation_type = #{operationType}</if>
+            <if test="details != null  and details != ''"> and details = #{details}</if>
+        </where>
+        order by create_time desc
+    </select>
+
+
     <select id="selectFsUserOperationLogByLogId" parameterType="Long" resultMap="FsUserOperationLogResult">
         <include refid="selectFsUserOperationLogVo"/>
         where log_id = #{logId}

+ 8 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -572,4 +572,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </update>
 
+    <select id="selectExternalByFsUserIds" resultType="QwExternalContact">
+        select * from qw_external_contact
+        where fs_user_id in
+        <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+    </select>
+
 </mapper>

+ 3 - 0
fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

@@ -248,4 +248,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             qw_user.qw_user_id IN ( SELECT user_id FROM qw_external_contact WHERE fs_user_id = #{fsUserId} )
     </select>
 
+    <update id="updateSendType">
+        update qw_user set send_msg_type = #{type} where id in <foreach collection="ids" open="(" close=")" separator="," item="item">#{item}</foreach>
+    </update>
 </mapper>

+ 6 - 2
fs-user-course/src/main/java/com/fs/course/controller/CourseWxH5Controller.java

@@ -1,10 +1,11 @@
 package com.fs.course.controller;
 
 
-import com.fs.course.annotation.Login;
+
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.annotation.Login;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
 import com.fs.course.param.FsCourseSendRewardUParam;
 import com.fs.course.param.FsUserCourseVideoFinishUParam;
@@ -36,6 +37,9 @@ public class CourseWxH5Controller extends AppBaseController {
     @Autowired
     private IFsUserCourseVideoService courseVideoService;
 
+    @Autowired
+    private ISysConfigService configService;
+
     @Autowired
     private IFsCourseLinkService courseLinkService;
 
@@ -106,7 +110,7 @@ public class CourseWxH5Controller extends AppBaseController {
         if (param.getDuration()==null){
             logger.info("zyp \n【未识别到时长】:{}",param.getUserId());
         }
-        return questionBankService.courseAnswer(param, true);
+        return questionBankService.courseAnswerByFsUser(param);
     }
 
     @ApiOperation("发放奖励")

+ 172 - 74
fs-user-course/src/main/java/com/fs/course/controller/WxCompanyUserController.java

@@ -5,7 +5,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 import cn.hutool.core.date.DateTime;
 import com.alibaba.fastjson.JSON;
-import com.fs.course.utils.JwtUtils;
+
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.param.LoginMaWxParam;
@@ -18,9 +18,12 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.core.config.WxMaConfiguration;
 import com.fs.course.config.CourseMaConfig;
+import com.fs.course.utils.JwtUtils;
 import com.fs.his.domain.FsUser;
+import com.fs.his.enums.FsUserOperationEnum;
 import com.fs.his.service.IFsUserService;
 
+import com.fs.his.service.IFsUserWxService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.wx.miniapp.config.WxMaProperties;
@@ -39,7 +42,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import com.fs.his.domain.FsUserWx;
 
 @Api("微信小程序相关接口")
 @RestController
@@ -72,105 +78,197 @@ public class WxCompanyUserController extends AppBaseController {
     @Autowired
     private SysConfigMapper sysConfigMapper;
 
+    @Autowired
+    private IFsUserWxService fsUserWxService;
+
     @ApiOperation("小程序-授权登录")
     @PostMapping("/loginByMa")
+//    @UserOperationLog(operationType = FsUserOperationEnum.MINLOGIN)
     public R login(@RequestBody LoginMaWxParam param) {
         log.info("=====================进入小程序授权登录, 入参: {}", param);
         if (StringUtils.isBlank(param.getCode())) {
             return R.error("code不存在");
         }
-        SysConfig sysConfig3 = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
-        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(sysConfig3.getConfigValue(), CourseMaConfig.class);
-        if (courseMaConfigs.isEmpty()){
-            return R.error("小程序配置为空");
+
+        Company company = companyService.selectCompanyById(param.getCompanyId());
+        if (company.getCourseMiniAppId() == null) {
+            return R.error("小程序参数错误!");
         }
-        CourseMaConfig courseMaConfig = courseMaConfigs.get(0);
-        //获取第二个小程序配置,序号从0开始
-        final WxMaService wxService = WxMaConfiguration.getMaService(courseMaConfig.getAppid());
+
+        final WxMaService wxService = WxMaConfiguration.getMaService(company.getCourseMiniAppId());
         try {
             WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
             this.logger.info(session.getSessionKey());
             this.logger.info(session.getOpenid());
             this.logger.info(session.getUnionid());
-            // 解密
-            WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
-            FsUser user = userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
-
-            // 特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
-            Company company = null;
-            if(param.getCompanyId() != null){
-                company = companyService.selectCompanyById(param.getCompanyId());
-            }
 
-            // 根据销售后台设置的  是否需要单独注册会员 来判断是否需要设置销售的值
-            CompanyUser companyUser = null;
-            if(param.getCompanyUserId() != null){
-                companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+            // 手机号信息
+            WxMaPhoneNumberInfo phoneNoInfo = null;
+            if (param.getAuthType()==1){
+                phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
             }
 
+            FsUser user = getUserByAuthType(param, wxService, session, phoneNoInfo);
+
+
+            // 2. 获取销售信息
+            CompanyUser companyUser = getCompanyUser(param);
+
+            // 3. 处理用户注册或更新
             String ip = IpUtil.getRequestIp();
-            if (user == null) {
-                if(StringUtils.isNotEmpty(session.getUnionid())){
-                    user = userService.selectFsUserByUnionId(session.getUnionid());
-                } else {
-                    user = userService.selectFsUserByCourseMaOpenId(session.getOpenid());
-                }
-                if (user != null) {
-                    //修改
-                    FsUser userMap = new FsUser();
-                    userMap.setUserId(user.getUserId());
-                    userMap.setCourseMaOpenId(session.getOpenid());
-                    userMap.setUnionId(session.getUnionid());
-                    userMap.setUpdateTime(new DateTime());
-                    userMap.setNickName(param.getNickname() != null ? param.getNickname() : "微信用户");
-                    userMap.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
-                    userMap.setPhone(phoneNoInfo.getPhoneNumber());
-                    // 逻辑调整:如果会员已经绑定了销售,直接提示,不让注册-2025年6月16日14点53分
-                    if (user.getCompanyUserId() != null && !param.getCompanyUserId().equals(user.getCompanyUserId())){
-                        return R.error(406, "该用户已成为其他销售会员");
-                    }
-                    if(companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
-                        user.setCompanyId(param.getCompanyId());
-                        user.setCompanyUserId(param.getCompanyUserId());
-                    }
-                    userService.updateFsUser(userMap);
-                } else {
-                    //新增
-                    user = new FsUser();
-                    user.setNickName(param.getNickname() != null ? param.getNickname() : "微信用户");
-                    user.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
-                    user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
-                    user.setCourseMaOpenId(session.getOpenid());
-                    user.setUnionId(session.getUnionid());
-                    user.setCreateTime(new Date());
-                    user.setPhone(phoneNoInfo.getPhoneNumber());
-                    if(companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
-                        user.setCompanyId(param.getCompanyId());
-                        user.setCompanyUserId(param.getCompanyUserId());
-                    }
-                    userService.insertFsUser(user);
-                }
-            } else {
-                FsUser userMap = new FsUser();
-                userMap.setUserId(user.getUserId());
-                userMap.setUpdateTime(new DateTime());
-                userMap.setPhone(phoneNoInfo.getPhoneNumber());
-                userMap.setLastIp(ip);
-                if (StringUtils.isNotEmpty(session.getUnionid())) {
-                    userMap.setUnionId(session.getUnionid());
-                }
-                userService.updateFsUser(userMap);
-            }
+            user = handleUserRegisterOrUpdate(user, param, session, phoneNoInfo, company, companyUser, ip);
+
+            // 4. 处理用户与小程序的绑定
+            handleFsUserWx(user, param, company, session);
+
             log.info("保存成功的用户信息user: {}, 用户id: {}", user, user.getUserId());
             String token = jwtUtils.generateToken(user.getUserId());
             // 返回一个写死的数据到前端
-            return R.ok("登录成功").put("token", token).put("phoneNumber", phoneNoInfo.getPhoneNumber()).put("nickName", "微信用户").put("user", user);
+            return R.ok("登录成功").put("token", token).put("user", user);
         } catch (WxErrorException e) {
             this.logger.error(e.getMessage(), e);
             return R.error("授权失败," + e.getMessage());
         }
     }
 
+    /**
+     * 根据authType获取用户信息
+     */
+    private FsUser getUserByAuthType(LoginMaWxParam param, WxMaService wxService, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo) throws WxErrorException {
+        FsUser user = null;
+        if (param.getAuthType() == 1) {
+            user = userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
+        } else {
+            // unionid判定唯一
+            if (StringUtils.isNotEmpty(session.getUnionid())) {
+                user = userService.selectFsUserByUnionId(session.getUnionid());
+            }
+        }
+        return user;
+    }
+
+    /**
+     * 获取销售信息
+     */
+    private CompanyUser getCompanyUser(LoginMaWxParam param) {
+        if (param.getCompanyUserId() != null) {
+            return companyUserService.selectCompanyUserById(param.getCompanyUserId());
+        }
+        return null;
+    }
+
+    /**
+     * 处理用户注册或更新
+     */
+    private FsUser handleUserRegisterOrUpdate(FsUser user, LoginMaWxParam param, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo, Company company, CompanyUser companyUser, String ip) {
+        if (user == null) {
+            user = userService.selectFsUserByCourseMaOpenId(session.getOpenid());
+            if (user != null) {
+                // 修改
+                return updateUser(user, param, session, phoneNoInfo, company, companyUser);
+            } else {
+                // 新增
+                return createUser(param, session, phoneNoInfo, company, companyUser);
+            }
+        } else {
+            // 已存在用户,更新信息
+            updateUserInfo(user, param, session, phoneNoInfo, ip);
+            return user;
+        }
+    }
+
+    /**
+     * 新增用户
+     */
+    private FsUser createUser(LoginMaWxParam param, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo, Company company, CompanyUser companyUser) {
+        FsUser user = new FsUser();
+        user.setNickName(param.getNickname() != null ? param.getNickname() : "微信用户");
+        user.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+        user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
+        // user.setCourseMaOpenId(session.getOpenid());
+        user.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
+        user.setCreateTime(new Date());
+        if (param.getAuthType() == 1 && phoneNoInfo != null) {
+            user.setPhone(phoneNoInfo.getPhoneNumber());
+        }
+        if (companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1) {
+            user.setCompanyId(param.getCompanyId());
+            user.setCompanyUserId(param.getCompanyUserId());
+        }
+        userService.insertFsUser(user);
+        return user;
+    }
+
+    /**
+     * 修改用户
+     */
+    private FsUser updateUser(FsUser user, LoginMaWxParam param, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo, Company company, CompanyUser companyUser) {
+        FsUser userMap = new FsUser();
+        userMap.setUserId(user.getUserId());
+        // userMap.setCourseMaOpenId(session.getOpenid());
+        userMap.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
+        userMap.setUpdateTime(new DateTime());
+        userMap.setNickName(param.getNickname() != null ? param.getNickname() : user.getNickName());
+        userMap.setAvatar(param.getAvatar() != null ? param.getAvatar() : user.getAvatar());
+        if (param.getAuthType() == 1 && phoneNoInfo != null) {
+            userMap.setPhone(phoneNoInfo.getPhoneNumber());
+        }
+        // 逻辑调整:如果会员已经绑定了销售,直接提示,不让注册-2025年6月16日14点53分
+        if (user.getCompanyUserId() != null && !param.getCompanyUserId().equals(user.getCompanyUserId())) {
+            throw new RuntimeException("该用户已成为其他销售会员");
+        }
+        if (companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1) {
+            userMap.setCompanyId(param.getCompanyId());
+            userMap.setCompanyUserId(param.getCompanyUserId());
+        }
+        userService.updateFsUser(userMap);
+        return userMap;
+    }
+
+    /**
+     * 更新已存在用户信息
+     */
+    private void updateUserInfo(FsUser user, LoginMaWxParam param, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo, String ip) {
+        FsUser userMap = new FsUser();
+        userMap.setUserId(user.getUserId());
+        userMap.setUpdateTime(new DateTime());
+        if (param.getAuthType() == 1 && phoneNoInfo != null) {
+            user.setPhone(phoneNoInfo.getPhoneNumber());
+        }
+        userMap.setLastIp(ip);
+        if (StringUtils.isNotEmpty(session.getUnionid())) {
+            userMap.setUnionId(session.getUnionid());
+        }
+        userService.updateFsUser(userMap);
+    }
+
+    /**
+     * 处理用户与小程序的绑定
+     */
+    private void handleFsUserWx(FsUser user, LoginMaWxParam param, Company company, WxMaJscode2SessionResult session) {
+        if (user == null) return;
+        FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(company.getCourseMiniAppId(), user.getUserId(), 1);
+        if (fsUserWx == null) {
+            fsUserWx = new FsUserWx();
+            fsUserWx.setType(1);
+            fsUserWx.setFsUserId(user.getUserId());
+            fsUserWx.setCompanyId(param.getCompanyId());
+            fsUserWx.setAppId(company.getCourseMiniAppId());
+            fsUserWx.setOpenId(session.getOpenid());
+            fsUserWx.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
+            fsUserWx.setCreateTime(new Date());
+            fsUserWxService.save(fsUserWx);
+        } else {
+            fsUserWx.setFsUserId(user.getUserId());
+            fsUserWx.setCompanyId(param.getCompanyId());
+            fsUserWx.setAppId(company.getCourseMiniAppId());
+            fsUserWx.setOpenId(session.getOpenid());
+            fsUserWx.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
+            fsUserWx.setUpdateTime(new Date());
+            fsUserWxService.updateById(fsUserWx);
+        }
+    }
+
 //    @Login(isMiniLogin = true)
 //    @ApiOperation("获取销售通过小程序登录后的用户信息")
 //    @GetMapping("/getMaUser")

+ 133 - 0
fs-user-course/src/main/java/com/fs/course/controller/WxH5MpController.java

@@ -0,0 +1,133 @@
+package com.fs.course.controller;
+
+import cn.hutool.core.date.DateTime;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.course.param.FsUserLoginByMpParam;
+import com.fs.course.utils.JwtUtils;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Api("会员-h5-微信相关接口(后面不需要这个接口了,再删除))")
+@RestController
+@RequestMapping("/app/wx/h5/mp")
+@Slf4j
+public class WxH5MpController {
+    Logger logger = LoggerFactory.getLogger(getClass());
+    @Autowired
+    private WxMpService wxMpService;
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    QwExternalContactMapper qwExternalContactMapper;
+    @Autowired
+    ICompanyService companyService;
+    @Autowired
+    ICompanyUserService companyUserService;
+
+
+    @ApiOperation("课程分享链接公众号登录")
+    @PostMapping("/loginByMp")
+    public R loginByMp(@Valid @RequestBody FsUserLoginByMpParam param) throws WxErrorException {
+//        try {
+            //获取微信用户信息
+            WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+            WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+            //1、特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
+            Company company = null;
+            if(param.getCompanyId() != null){
+                company = companyService.selectCompanyById(param.getCompanyId());
+            }
+            // 根据销售后台设置的  是否需要单独注册会员 来判断是否需要设置销售的值
+            CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+
+            FsUser user;
+        logger.info("当前微信信息---------------------------》{}",wxMpUser);
+            if(StringUtils.isNotEmpty(wxMpUser.getUnionId())) {
+                user = userService.selectFsUserByUnionId(wxMpUser.getUnionId());
+            } else {
+                logger.info("当前查询参数-----------》{}",wxMpUser.getOpenid());
+                user = userService.selectFsUserByOpenId(wxMpUser.getOpenid());
+            }
+        logger.info("当前用户信息-----------》{}",user);
+            if (user != null) {
+                //修改
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setMpOpenId(wxMpUser.getOpenid());
+                userMap.setUnionId(wxMpUser.getUnionId());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setAvatar(wxMpUser.getHeadImgUrl());
+                userMap.setNickName(wxMpUser.getNickname());
+                userService.updateFsUser(userMap);
+            } else {
+                //新增
+                user = new FsUser();
+                user.setNickName(wxMpUser.getNickname());
+                user.setAvatar(wxMpUser.getHeadImgUrl());
+                user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
+                user.setMpOpenId(wxMpUser.getOpenid());
+                user.setUnionId(wxMpUser.getUnionId());
+                user.setCreateTime(new Date());
+                if(companyUser.getIsNeedRegisterMember() != 1){
+                    user.setCompanyId(param.getCompanyId());
+                    user.setCompanyUserId(param.getCompanyUserId());
+                }
+                userService.insertFsUser(user);
+            }
+            log.error("用户信息user: {}, 用户id: {}", user, user.getUserId());
+            String token = jwtUtils.generateToken(user.getUserId());
+            redisCache.setCacheObject("token:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
+            Map<String, Object> map = new HashMap<>();
+            map.put("token", token);
+            map.put("user", user);
+            return R.ok(map);
+//        } catch (WxErrorException e) {
+//            if (e.getError().getErrorCode() == 40163) {
+//                return R.error(40163, e.getError().getErrorMsg());
+//            } else {
+//                return R.error("授权失败," + e.getMessage());
+//            }
+//        }
+
+    }
+
+
+}

+ 1 - 9
fs-user-course/src/main/java/com/fs/course/controller/WxMpController.java

@@ -161,18 +161,10 @@ public class WxMpController {
   @PostMapping("/loginByMp")
   @Transactional
   public R loginByMp( @RequestBody FsUserLoginByMpParam param) {
-
-    if (StringUtils.isBlank(param.getCode())) {
-      return R.error("code不存在");
-    }
     try{
       WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
       WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
-      WxMpUserService wxMpUserService = wxMpService.getUserService();
-      WxMpUser userInfo = wxMpUserService.userInfo(wxMpUser.getOpenid());
-//      if (!userInfo.getSubscribe()){
-//        return R.error("请关注公众号进行登录");
-//      }
+
       FsUser user=userService.selectFsUserByUnionid(wxMpUser.getUnionId());
       if(user!=null){
         FsUser userMap=new FsUser();

+ 13 - 0
fs-user-course/src/main/java/com/fs/course/param/FsUserLoginByMpParam.java

@@ -1,8 +1,10 @@
 package com.fs.course.param;
 
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 
 @Data
@@ -10,4 +12,15 @@ public class FsUserLoginByMpParam implements Serializable {
     @NotBlank(message = "code参数缺失")
     private String code;
     private Long videoId;
+
+    @NotNull(message = "公司id不能为空")
+    @ApiModelProperty(value = "公司id")
+    private Long companyId;
+
+    @NotNull(message = "销售id不能为空")
+    @ApiModelProperty(value = "销售id")
+    private Long companyUserId;
+
+    @NotBlank(message = "服务号参数缺失")
+    private String appId;//服务号APPID
 }

BIN
fs-user-course/src/main/resources/apiclient_cert.p12


+ 2 - 0
fs-user-course/src/main/resources/banner.txt

@@ -0,0 +1,2 @@
+Application Version: ${fs.version}
+Spring Boot Version: ${spring-boot.version}

+ 1 - 0
pom.xml

@@ -267,6 +267,7 @@
         <module>fs-qwhook-msg</module>
         <module>fs-repeat-api</module>
         <module>fs-ipad-task</module>
+        <module>fs-user-course</module>
     </modules>
 
     <packaging>pom</packaging>