浏览代码

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
#	fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
#	fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
yfh 1 天之前
父节点
当前提交
abc8422382
共有 100 个文件被更改,包括 6407 次插入1486 次删除
  1. 130 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyDomainBindController.java
  2. 99 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyDomainBindUserController.java
  3. 189 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyDomainController.java
  4. 9 3
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  5. 15 28
      fs-admin/src/main/java/com/fs/his/controller/FsInquiryOrderReportController.java
  6. 15 4
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  7. 15 89
      fs-admin/src/main/java/com/fs/his/task/Task.java
  8. 138 0
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  9. 41 0
      fs-admin/src/main/java/com/fs/qw/controller/QwTagGroupController.java
  10. 10 0
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  11. 5 0
      fs-common/src/main/java/com/fs/common/config/FSConfig.java
  12. 2 1
      fs-common/src/main/java/com/fs/common/enums/BizResponseEnum.java
  13. 3 0
      fs-company-app/src/main/java/com/fs/app/annotation/Login.java
  14. 18 0
      fs-company-app/src/main/java/com/fs/app/config/ImageStorageConfig.java
  15. 26 1
      fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java
  16. 209 43
      fs-company-app/src/main/java/com/fs/app/controller/CommonController.java
  17. 89 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyTagController.java
  18. 311 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  19. 334 0
      fs-company-app/src/main/java/com/fs/app/controller/CrmController.java
  20. 111 0
      fs-company-app/src/main/java/com/fs/app/controller/CrmEventController.java
  21. 107 0
      fs-company-app/src/main/java/com/fs/app/controller/CrmMsgController.java
  22. 339 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  23. 224 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  24. 65 0
      fs-company-app/src/main/java/com/fs/app/controller/IndexController.java
  25. 96 0
      fs-company-app/src/main/java/com/fs/app/controller/QwCompanyUserController.java
  26. 91 0
      fs-company-app/src/main/java/com/fs/app/controller/QwWorkTaskController.java
  27. 104 0
      fs-company-app/src/main/java/com/fs/app/controller/SmsController.java
  28. 322 0
      fs-company-app/src/main/java/com/fs/app/controller/StatisticsController.java
  29. 140 0
      fs-company-app/src/main/java/com/fs/app/controller/StorePaymentController.java
  30. 85 0
      fs-company-app/src/main/java/com/fs/app/controller/StoreProductPackageController.java
  31. 62 0
      fs-company-app/src/main/java/com/fs/app/controller/TestController.java
  32. 650 162
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  33. 206 0
      fs-company-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java
  34. 134 0
      fs-company-app/src/main/java/com/fs/app/controller/WxH5MpController.java
  35. 44 0
      fs-company-app/src/main/java/com/fs/app/controller/WxMpController.java
  36. 26 0
      fs-company-app/src/main/java/com/fs/app/param/ChangeUserDeptAndPostParam.java
  37. 29 0
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserChangeApplyParam.java
  38. 32 0
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserParam.java
  39. 27 0
      fs-company-app/src/main/java/com/fs/app/param/CompanyUserUpdateParam.java
  40. 13 0
      fs-company-app/src/main/java/com/fs/app/param/CrmDoReadMsgParam.java
  41. 12 0
      fs-company-app/src/main/java/com/fs/app/param/FsUserLoginByMpParam.java
  42. 22 0
      fs-company-app/src/main/java/com/fs/app/param/FsUserTagUpdateParam.java
  43. 26 0
      fs-company-app/src/main/java/com/fs/app/param/FsUserUpdateParam.java
  44. 34 0
      fs-company-app/src/main/java/com/fs/app/param/LoginMaWxParam.java
  45. 2 0
      fs-company-app/src/main/java/com/fs/app/param/LoginParam.java
  46. 39 0
      fs-company-app/src/main/java/com/fs/app/service/IAppService.java
  47. 155 0
      fs-company-app/src/main/java/com/fs/app/service/impl/AppServiceImpl.java
  48. 15 0
      fs-company-app/src/main/java/com/fs/app/vo/CompanyRoleVO.java
  49. 32 0
      fs-company-app/src/main/java/com/fs/app/vo/CompanySubUserVO.java
  50. 17 0
      fs-company-app/src/main/java/com/fs/app/vo/UserPostVO.java
  51. 21 39
      fs-company-app/src/main/java/com/fs/app/vo/UserVO.java
  52. 78 0
      fs-company-app/src/main/java/com/fs/core/PermissionService.java
  53. 33 29
      fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java
  54. 2 2
      fs-company-app/src/main/java/com/fs/core/config/ApplicationConfig.java
  55. 3 3
      fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java
  56. 2 2
      fs-company-app/src/main/java/com/fs/core/config/FastJson2JsonRedisSerializer.java
  57. 6 4
      fs-company-app/src/main/java/com/fs/core/config/FilterConfig.java
  58. 38 65
      fs-company-app/src/main/java/com/fs/core/config/MyBatisConfig.java
  59. 4 40
      fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java
  60. 21 8
      fs-company-app/src/main/java/com/fs/core/config/ResourcesConfig.java
  61. 4 15
      fs-company-app/src/main/java/com/fs/core/config/SecurityConfig.java
  62. 2 2
      fs-company-app/src/main/java/com/fs/core/config/ServerConfig.java
  63. 9 7
      fs-company-app/src/main/java/com/fs/core/config/SwaggerConfig.java
  64. 2 2
      fs-company-app/src/main/java/com/fs/core/config/ThreadPoolConfig.java
  65. 1 1
      fs-company-app/src/main/java/com/fs/core/config/properties/DruidProperties.java
  66. 2 2
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java
  67. 3 3
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java
  68. 1 1
      fs-company-app/src/main/java/com/fs/core/interceptor/RepeatSubmitInterceptor.java
  69. 16 12
      fs-company-app/src/main/java/com/fs/core/interceptor/impl/SameUrlDataInterceptor.java
  70. 50 0
      fs-company-app/src/main/java/com/fs/core/security/SecurityUtils.java
  71. 0 73
      fs-company-app/src/main/java/com/fs/framework/aspectj/DataSourceAspect.java
  72. 0 245
      fs-company-app/src/main/java/com/fs/framework/aspectj/LogAspect.java
  73. 0 117
      fs-company-app/src/main/java/com/fs/framework/aspectj/RateLimiterAspect.java
  74. 0 85
      fs-company-app/src/main/java/com/fs/framework/config/CaptchaConfig.java
  75. 0 76
      fs-company-app/src/main/java/com/fs/framework/config/KaptchaTextCreator.java
  76. 0 20
      fs-company-app/src/main/java/com/fs/framework/config/WebSocketConfig.java
  77. 0 56
      fs-company-app/src/main/java/com/fs/framework/manager/AsyncManager.java
  78. 0 40
      fs-company-app/src/main/java/com/fs/framework/manager/ShutdownManager.java
  79. 0 103
      fs-company-app/src/main/java/com/fs/framework/manager/factory/AsyncFactory.java
  80. 1 2
      fs-company-app/src/main/resources/application.yml
  81. 10 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyController.java
  82. 135 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDomainBindController.java
  83. 108 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDomainBindUserController.java
  84. 6 2
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java
  85. 256 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  86. 13 4
      fs-company/src/main/java/com/fs/company/controller/store/FsUserController.java
  87. 1 1
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  88. 1 1
      fs-company/src/main/resources/application.yml
  89. 0 3
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  90. 26 22
      fs-qw-task/src/main/java/com/fs/app/task/CourseWatchLogScheduler.java
  91. 37 0
      fs-qw-task/src/main/java/com/fs/app/task/UserCourseWatchCountTask.java
  92. 5 5
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  93. 38 18
      fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java
  94. 1 45
      fs-qwhook-sop/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  95. 39 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwStatisticsController.java
  96. 69 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwUserController.java
  97. 92 0
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwWorkTaskController.java
  98. 132 0
      fs-qwhook/src/main/java/com/fs/app/controller/ApisCommonController.java
  99. 181 0
      fs-qwhook/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java
  100. 39 0
      fs-qwhook/src/main/java/com/fs/app/controller/ApisQwStatisticsController.java

+ 130 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyDomainBindController.java

@@ -0,0 +1,130 @@
+package com.fs.company.controller;
+
+import java.util.List;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.api.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.param.CompanyDomainBindParam;
+import com.fs.company.vo.CompanyDomainBindVo;
+import com.fs.framework.web.service.TokenService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyDomainBind;
+import com.fs.company.service.ICompanyDomainBindService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 域名绑定销售公司Controller
+ * 
+ * @author fs
+ * @date 2025-06-17
+ */
+@RestController
+@RequestMapping("/company/companyDomainBind")
+public class CompanyDomainBindController extends BaseController
+{
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ICompanyDomainBindService companyDomainBindService;
+
+    /**
+     * 查询域名绑定销售公司列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyDomainBindParam companyDomainBind)
+    {
+        startPage();
+        List<CompanyDomainBindVo> list = companyDomainBindService.selectCompanyDomainBindList(companyDomainBind);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出域名绑定销售公司列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:export')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyDomainBindParam companyDomainBind)
+    {
+        List<CompanyDomainBindVo> list = companyDomainBindService.selectCompanyDomainBindList(companyDomainBind);
+        ExcelUtil<CompanyDomainBindVo> util = new ExcelUtil<CompanyDomainBindVo>(CompanyDomainBindVo.class);
+        return util.exportExcel(list, "域名绑定销售公司数据");
+    }
+
+    /**
+     * 获取域名绑定销售公司详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(companyDomainBindService.selectCompanyDomainBindById(id));
+    }
+
+    /**
+     * 新增域名绑定销售公司
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:add')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyDomainBind companyDomainBind)
+    {
+        return toAjax(companyDomainBindService.insertCompanyDomainBind(companyDomainBind));
+    }
+
+    /**
+     * 修改域名绑定销售公司
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:edit')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyDomainBind companyDomainBind)
+    {
+        return toAjax(companyDomainBindService.updateCompanyDomainBind(companyDomainBind));
+    }
+
+    /**
+     * 删除域名绑定销售公司
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:remove')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(companyDomainBindService.deleteCompanyDomainBindByIds(ids));
+    }
+
+    /**
+     * 域名批量绑定
+     **/
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:domainBatchBinding')")
+    @PostMapping("/domainBatchBinding")
+    public R domainBatchBinding(@RequestBody CompanyDomainBindParam param) {
+        if (ObjectUtil.isEmpty(param.getCompanyIds()) || param.getCompanyIds().isEmpty()) {
+            return R.failed("绑定失败,至少有一条域名分配信息!");
+        }
+        if (ObjectUtil.isEmpty(param.getCompanyUserIds()) || param.getCompanyUserIds().isEmpty()) {
+            return R.failed("绑定失败,至少有一条分配人员信息!");
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setUserId(loginUser.getUser().getUserId().toString());
+        return companyDomainBindService.domainBatchBinding(param);
+    }
+}

+ 99 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyDomainBindUserController.java

@@ -0,0 +1,99 @@
+package com.fs.company.controller;
+
+import java.util.List;
+
+import com.fs.company.param.CompanyDomainBindUserParam;
+import com.fs.company.vo.CompanyDomainBindUserVo;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyDomainBindUser;
+import com.fs.company.service.ICompanyDomainBindUserService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 企业域名分配中间表Controller
+ *
+ * @author fs
+ * @date 2025-06-19
+ */
+@RestController
+@RequestMapping("/company/companyBindUser")
+public class CompanyDomainBindUserController extends BaseController {
+    @Autowired
+    private ICompanyDomainBindUserService companyDomainBindUserService;
+
+    /**
+     * 查询企业域名分配中间表列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyDomainBindUserParam companyDomainBindUser) {
+        startPage();
+        List<CompanyDomainBindUserVo> list = companyDomainBindUserService.selectCompanyDomainBindUserList(companyDomainBindUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出企业域名分配中间表列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:export')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyDomainBindUserParam companyDomainBindUser) {
+        List<CompanyDomainBindUserVo> list = companyDomainBindUserService.selectCompanyDomainBindUserList(companyDomainBindUser);
+        ExcelUtil<CompanyDomainBindUserVo> util = new ExcelUtil<CompanyDomainBindUserVo>(CompanyDomainBindUserVo.class);
+        return util.exportExcel(list, "企业域名分配中间表数据");
+    }
+
+    /**
+     * 获取企业域名分配中间表详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(companyDomainBindUserService.selectCompanyDomainBindUserById(id));
+    }
+
+    /**
+     * 新增企业域名分配中间表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:add')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyDomainBindUser companyDomainBindUser) {
+        return toAjax(companyDomainBindUserService.insertCompanyDomainBindUser(companyDomainBindUser));
+    }
+
+    /**
+     * 修改企业域名分配中间表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:edit')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyDomainBindUser companyDomainBindUser) {
+        return toAjax(companyDomainBindUserService.updateCompanyDomainBindUser(companyDomainBindUser));
+    }
+
+    /**
+     * 删除企业域名分配中间表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:remove')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(companyDomainBindUserService.deleteCompanyDomainBindUserByIds(ids));
+    }
+}

+ 189 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyDomainController.java

@@ -0,0 +1,189 @@
+package com.fs.company.controller;
+
+import java.util.List;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.api.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.param.CompanyDomainParam;
+import com.fs.company.vo.CompanyDomainExportTemplateVo;
+import com.fs.company.vo.CompanyDomainVo;
+import com.fs.framework.web.service.TokenService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyDomain;
+import com.fs.company.service.ICompanyDomainService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 域名管路Controller
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@RestController
+@RequestMapping("/company/companyDomain")
+public class CompanyDomainController extends BaseController {
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ICompanyDomainService companyDomainService;
+
+    // 允许的文件扩展名
+    private static final String[] ALLOWED_EXCEL_EXTENSIONS = {".xlsx", ".xls"};
+    // 最大文件大小(5MB)
+    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+
+    /**
+     * 查询域名管路列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyDomainParam companyDomain) {
+        startPage();
+        List<CompanyDomainVo> list = companyDomainService.selectCompanyDomainList(companyDomain);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出域名管路列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:export')")
+    @Log(title = "域名管理", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyDomainParam companyDomain) {
+        List<CompanyDomainVo> list = companyDomainService.selectCompanyDomainList(companyDomain);
+        ExcelUtil<CompanyDomainVo> util = new ExcelUtil<CompanyDomainVo>(CompanyDomainVo.class);
+        return util.exportExcel(list, "域名管路数据");
+    }
+
+    /**
+     * 获取域名管路详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(companyDomainService.selectCompanyDomainById(id));
+    }
+
+    /**
+     * 新增域名管路
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:add')")
+    @Log(title = "域名管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyDomain companyDomain) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyDomain.setCreateBy(loginUser.getUser().getUserId().toString());
+        return toAjax(companyDomainService.insertCompanyDomain(companyDomain));
+    }
+
+    /**
+     * 修改域名管路
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:edit')")
+    @Log(title = "域名管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyDomain companyDomain) {
+        if (ObjectUtil.isEmpty(companyDomain.getId())) {
+            throw new ServiceException("修改关键信息不能为空!");
+        }
+        return toAjax(companyDomainService.updateCompanyDomain(companyDomain));
+    }
+
+    /**
+     * 删除域名管路
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:remove')")
+    @Log(title = "域名管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        if (ObjectUtil.isEmpty(ids)) {
+            throw new ServiceException("删除关键信息不能为空!");
+        }
+        return toAjax(companyDomainService.deleteCompanyDomainByIds(ids));
+    }
+
+    /**
+     * 域名批量绑定
+     **/
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:domainBatchBinding')")
+    @PostMapping("/domainBatchBinding")
+    public R domainBatchBinding(@RequestBody CompanyDomainParam param) {
+        if (ObjectUtil.isEmpty(param.getDomainIds()) || param.getDomainIds().isEmpty()) {
+            return R.failed("绑定失败,至少有一条绑定域名信息!");
+        }
+        if (ObjectUtil.isEmpty(param.getCompanyIds()) || param.getCompanyIds().isEmpty()) {
+            return R.failed("绑定失败,至少有一条绑定公司信息!");
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setUserId(loginUser.getUser().getUserId().toString());
+        return companyDomainService.domainBatchBinding(param);
+    }
+
+    /**
+     * 导出模板
+     **/
+    @GetMapping("/exportTemplate")
+    public AjaxResult exportTemplate() {
+        ExcelUtil<CompanyDomainExportTemplateVo> util = new ExcelUtil<>(CompanyDomainExportTemplateVo.class);
+        return util.importTemplateExcel("域名导入模板");
+    }
+
+    @Log(title = "域名导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/importDomainData")
+    public R importDomainData(MultipartFile file) {
+        // 1. 检查文件是否为空
+        if (file.isEmpty()) {
+            return R.failed("上传的文件不能为空");
+        }
+
+        // 2. 检查文件大小
+        if (file.getSize() > MAX_FILE_SIZE) {
+            return R.failed("文件大小不能超过5MB");
+        }
+
+        // 3. 检查文件扩展名
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || !isValidExcelFile(fileName)) {
+            return R.failed("请上传Excel文件(.xlsx或.xls格式)");
+        }
+        CompanyDomainParam param = new CompanyDomainParam();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setUserId(loginUser.getUser().getUserId().toString());
+        ExcelUtil<CompanyDomainExportTemplateVo> util = new ExcelUtil<>(CompanyDomainExportTemplateVo.class);
+        try {
+            List<CompanyDomainExportTemplateVo> companyDomainList = util.importExcel(file.getInputStream());
+            return companyDomainService.importDomainData(param, companyDomainList);
+        } catch (Exception e) {
+            e.getStackTrace();
+            return R.failed("导入失败!");
+        }
+    }
+
+    // 检查文件是否为有效的Excel文件
+    private boolean isValidExcelFile(String fileName) {
+        for (String ext : ALLOWED_EXCEL_EXTENSIONS) {
+            if (fileName.toLowerCase().endsWith(ext)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 9 - 3
fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java

@@ -133,10 +133,16 @@ public class FsUserCoursePeriodController extends BaseController {
     }
 
     @GetMapping("/getDays")
-    public TableDataInfo getDays(FsUserCoursePeriodDays fsUserCoursePeriodDays){
-        startPage();
+    public R getDays(FsUserCoursePeriodDays fsUserCoursePeriodDays){
+//        startPage();
+        PageHelper.startPage(fsUserCoursePeriodDays.getPageNum(), fsUserCoursePeriodDays.getPageSize());
         List<FsUserCoursePeriodDays> list = fsUserCoursePeriodDaysService.selectFsUserCoursePeriodDaysList(fsUserCoursePeriodDays);
-        return getDataTable(list);
+
+        PageInfo<FsUserCoursePeriodDays> pageInfo = new PageInfo<>(list);
+        Map<String, Object> result = new HashMap<>();
+        result.put("rows", pageInfo.getList());
+        result.put("total", pageInfo.getTotal());
+        return R.ok(result);
     }
 
     @PreAuthorize("@ss.hasPermi('course:period:addCourse')")

+ 15 - 28
fs-admin/src/main/java/com/fs/his/controller/FsInquiryOrderReportController.java

@@ -1,46 +1,33 @@
 package com.fs.his.controller;
 
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
 import com.alibaba.fastjson.JSON;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.his.domain.FsExportTask;
-import com.fs.his.domain.FsInquiryDisease;
+import com.fs.his.domain.FsInquiryOrderReport;
 import com.fs.his.param.FsInquiryOrderReportParam;
 import com.fs.his.service.IFsExportTaskService;
-import com.fs.his.service.IFsInquiryDiseaseService;
-import com.fs.his.vo.FsInquiryDiseaseVO;
+import com.fs.his.service.IFsInquiryOrderReportService;
 import com.fs.his.vo.FsInquiryOrderReportListVO;
 import com.fs.his.vo.FsInquiryOrderReportVO;
-import com.qiniu.util.Json;
-import com.sun.media.jfxmedia.logging.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import com.fs.common.annotation.Log;
-import com.fs.common.core.controller.BaseController;
-import com.fs.common.core.domain.AjaxResult;
-import com.fs.common.enums.BusinessType;
-import com.fs.his.domain.FsInquiryOrderReport;
-import com.fs.his.service.IFsInquiryOrderReportService;
-import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.common.core.page.TableDataInfo;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
 
-import static com.fs.his.utils.PhoneUtil.*;
+import static com.fs.his.utils.PhoneUtil.decryptPhone;
+import static com.fs.his.utils.PhoneUtil.decryptPhoneMk;
 
 /**
  * 问诊报告Controller

+ 15 - 4
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -1,6 +1,8 @@
 package com.fs.his.controller;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
@@ -14,6 +16,8 @@ import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.UserVo;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.vo.h5.FsUserPageListVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -190,10 +194,17 @@ public class FsUserController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:user:darkRoomList')")
     @GetMapping("/darkRoomList")
     @ApiOperation("小黑屋")
-    public TableDataInfo darkRoomList(FsUserPageListParam param) {
-        startPage();
-        List<FsUserPageListVO> list = fsUserService.selectFsUserPageList(param);
-        return getDataTable(list);
+    public R darkRoomList(FsUserPageListParam param) {
+//        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
+        for (FsUserPageListVO fsUserPageListVO : fsUserPageListVOPageInfo.getList()) {
+            fsUserPageListVO.setPhone(ParseUtils.parsePhone(fsUserPageListVO.getPhone()));
+        }
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("rows", fsUserPageListVOPageInfo.getList());
+        map.put("total", fsUserPageListVOPageInfo.getList().size());
+        return R.ok(map);
     }
 
     @PreAuthorize("@ss.hasPermi('his:user:enabledUsers')")

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

@@ -1,130 +1,56 @@
 package com.fs.his.task;
 
-import cn.binarywang.wx.miniapp.api.WxMaService;
-import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
-import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 import cn.hutool.core.date.DateTime;
-import cn.hutool.core.net.URLDecoder;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.http.HttpUtil;
-import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fs.common.annotation.Log;
-import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
-import com.fs.common.exception.CustomException;
 import com.fs.common.service.impl.SmsServiceImpl;
-import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.SecurityUtils;
-import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.company.domain.*;
+import com.fs.company.domain.CompanyMoneyLogs;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.domain.CompanyVoiceCaller;
 import com.fs.company.mapper.*;
 import com.fs.company.service.ICompanyService;
-import com.fs.company.service.impl.CompanyMoneyLogsServiceImpl;
 import com.fs.company.vo.RedPacketMoneyVO;
-import com.fs.core.config.WxMaConfiguration;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.ITencentCloudCosService;
-import com.fs.crm.param.SmsSendParam;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpOrderQuery;
-import com.fs.erp.dto.*;
+import com.fs.erp.dto.ErpOrderQueryRequert;
+import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.service.IErpOrderService;
-import com.fs.event.TemplateBean;
-import com.fs.event.TemplateEvent;
-import com.fs.event.TemplateListenEnum;
-import com.fs.fastGpt.config.ModeConfig;
-import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
-import com.fs.fastgptApi.param.ChatParam;
-import com.fs.fastgptApi.param.VoiceParam;
-import com.fs.fastgptApi.result.ChatDetailTStreamFResult;
-import com.fs.fastgptApi.service.Impl.AudioServiceImpl;
-import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.his.config.StoreConfig;
-import com.fs.his.domain.*;
-import com.fs.his.dto.*;
+import com.fs.his.domain.FsInquiryOrder;
+import com.fs.his.domain.FsStoreAfterSales;
+import com.fs.his.domain.FsStoreOrder;
+import com.fs.his.domain.FsUser;
+import com.fs.his.dto.FsInquiryOrderPatientDTO;
 import com.fs.his.enums.FsStoreOrderLogEnum;
 import com.fs.his.enums.FsStoreOrderStatusEnum;
-import com.fs.his.enums.ShipperCodeEnum;
 import com.fs.his.mapper.*;
-import com.fs.his.param.FsFollowMsgParam;
 import com.fs.his.param.FsInquiryOrderFinishParam;
-import com.fs.his.param.FsPackageOrderDoPayParam;
 import com.fs.his.service.*;
-import com.fs.his.service.impl.*;
-import com.fs.his.vo.FsStoreProductExcelVO;
-import com.fs.his.vo.FsStoreProductVO;
+import com.fs.his.service.impl.FsPackageOrderServiceImpl;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
-import com.fs.qw.mapper.QwAppContactWayMapper;
-import com.fs.qw.mapper.QwExternalContactTransferLogMapper;
 import com.fs.qw.service.IQwAppContactWayService;
 import com.fs.qw.service.IQwExternalContactTransferLogService;
-import com.fs.qw.service.impl.QwAppContactWayServiceImpl;
-import com.fs.qwApi.Result.QwSendMsgResult;
-import com.fs.qwApi.param.QwSendMsgParam;
 import com.fs.qwApi.service.QwApiService;
-import com.fs.qwApi.service.impl.QwApiServiceImpl;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
-import com.fs.system.oss.CloudStorageService;
-import com.fs.system.oss.OSSFactory;
-import com.fs.tzBankPay.TzBankService.TzBankService;
-import com.fs.tzBankPay.doman.*;
 import com.google.gson.Gson;
-import com.qiniu.util.Json;
-import com.tencentcloudapi.mongodb.v20180408.models.SpecItem;
-import io.lettuce.core.ScriptOutputType;
-import javafx.scene.effect.Bloom;
-import jdk.nashorn.internal.scripts.JS;
-import org.apache.catalina.Store;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.http.HttpEntity;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
-import javax.imageio.ImageIO;
-import javax.sound.sampled.Line;
-import java.awt.image.BufferedImage;
-import java.io.*;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.text.ParseException;
-import java.time.Duration;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import static com.fs.his.utils.PhoneUtil.decryptPhone;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
 
 @Component("task")
 public class Task {

+ 138 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java

@@ -0,0 +1,138 @@
+package com.fs.qw.controller;
+
+import java.util.List;
+import java.util.Objects;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.exception.ServiceException;
+import com.fs.qw.param.QwExternalContactParam;
+import com.fs.qw.param.QwTagSearchParam;
+import com.fs.qw.service.IQwTagService;
+import com.fs.qw.vo.QwExternalContactVO;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 企业微信客户Controller
+ * 
+ * @author fs
+ * @date 2025-06-13
+ */
+@RestController
+@RequestMapping("/qw/externalContact")
+public class QwExternalContactController extends BaseController
+{
+    private IQwExternalContactService qwExternalContactService;
+    private IQwTagService iQwTagService;
+
+    QwExternalContactController(IQwExternalContactService qwExternalContactService,IQwTagService iQwTagService){
+        this.qwExternalContactService=qwExternalContactService;
+        this.iQwTagService=iQwTagService;
+    }
+
+
+    /**
+     * 查询企业微信客户列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwExternalContactParam qwExternalContact)
+    {
+        if(ObjectUtil.isEmpty(qwExternalContact.getCompanyId())){
+            throw new ServiceException("操作失败,请选择企业!");
+        }
+        startPage();
+        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
+        list.forEach(item->{
+
+            if (!Objects.equals(item.getTagIds(), "[]") && item.getTagIds()!=null) {
+                QwTagSearchParam param = new QwTagSearchParam();
+                Gson gson = new Gson();
+                List<String> tagIds = gson.fromJson(
+                        item.getTagIds(),
+                        new TypeToken<List<String>>() {
+                        }.getType()
+                );
+
+                param.setTagIds(tagIds);
+
+                item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+            }
+        });
+
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出企业微信客户列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:export')")
+    @Log(title = "企业微信客户", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwExternalContact qwExternalContact)
+    {
+        List<QwExternalContact> list = qwExternalContactService.selectQwExternalContactList(qwExternalContact);
+        ExcelUtil<QwExternalContact> util = new ExcelUtil<QwExternalContact>(QwExternalContact.class);
+        return util.exportExcel(list, "企业微信客户数据");
+    }
+
+    /**
+     * 获取企业微信客户详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwExternalContactService.selectQwExternalContactById(id));
+    }
+
+    /**
+     * 新增企业微信客户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:add')")
+    @Log(title = "企业微信客户", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwExternalContact qwExternalContact)
+    {
+        return toAjax(qwExternalContactService.insertQwExternalContact(qwExternalContact));
+    }
+
+    /**
+     * 修改企业微信客户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:edit')")
+    @Log(title = "企业微信客户", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwExternalContact qwExternalContact)
+    {
+        return toAjax(qwExternalContactService.updateQwExternalContact(qwExternalContact));
+    }
+
+    /**
+     * 删除企业微信客户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:remove')")
+    @Log(title = "企业微信客户", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwExternalContactService.deleteQwExternalContactByIds(ids));
+    }
+}

+ 41 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwTagGroupController.java

@@ -0,0 +1,41 @@
+package com.fs.qw.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.qw.domain.QwTagGroup;
+import com.fs.qw.service.IQwTagGroupService;
+import com.fs.qw.vo.QwTagGroupListVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 企微客户标签组Controller
+ *
+ * @author fs
+ * @date 2024-06-20
+ */
+@RestController
+@RequestMapping("/qw/tagGroup")
+public class QwTagGroupController extends BaseController
+{
+    @Autowired
+    private IQwTagGroupService qwTagGroupService;
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    /**
+    * 所有标签列表
+    */
+    @GetMapping("/allList")
+    public TableDataInfo allList(QwTagGroup qwTagGroup)
+    {
+        startPage();
+
+        List<QwTagGroupListVO> list = qwTagGroupService.selectQwTagGroupListVO(qwTagGroup);
+        return getDataTable(list);
+    }
+}

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

@@ -1,7 +1,9 @@
 package com.fs.qw.controller;
 
+import com.baomidou.mybatisplus.extension.api.R;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.service.IQwUserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -24,4 +26,12 @@ public class QwUserController extends BaseController {
     public AjaxResult getQwUserAll(){
         return AjaxResult.success(qwUserService.getQwUserAll());
     }
+
+    /**
+     * 获取企微信息
+     * **/
+    @GetMapping("/getQwUserInfo")
+    public R getQwUserInfo(QwFsUserParam param){
+        return R.ok(qwUserService.getQwUserInfo(param));
+    }
 }

+ 5 - 0
fs-common/src/main/java/com/fs/common/config/FSConfig.java

@@ -148,4 +148,9 @@ public class FSConfig
     {
         return getProfile() + "/upload";
     }
+
+    public static String getQrPath()
+    {
+        return getProfile() + "/qr";
+    }
 }

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

@@ -8,7 +8,8 @@ public enum BizResponseEnum {
     SUCCESS(200, "操作成功"),
     FAIL(500, "操作失败"),
     PARAM_ERROR(400, "参数错误"),
-    DATA_NOT_EXIST(1002, "数据不存在");
+    DATA_NOT_EXIST(1002, "数据不存在"),
+    WAIT_APPROVAL(505, "等待审核");
 
     private final Integer code;
     private final String msg;

+ 3 - 0
fs-company-app/src/main/java/com/fs/app/annotation/Login.java

@@ -9,4 +9,7 @@ import java.lang.annotation.*;
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface Login {
+    // 添加一个判断 是否是小程序登录
+    boolean isMiniLogin() default false;
+
 }

+ 18 - 0
fs-company-app/src/main/java/com/fs/app/config/ImageStorageConfig.java

@@ -0,0 +1,18 @@
+package com.fs.app.config;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Getter
+@Configuration
+public class ImageStorageConfig {
+
+    // 直接返回配置的路径
+    @Value("${image.storage.local-path}")
+    private String localPath;
+
+    @Value("${image.storage.server-path}")
+    private String serverPath;
+
+}

+ 26 - 1
fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java

@@ -1,9 +1,14 @@
 package com.fs.app.controller;
 
 
+import com.fs.app.exception.FSException;
 import com.fs.app.utils.JwtUtils;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
 import io.jsonwebtoken.Claims;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -14,6 +19,17 @@ public class AppBaseController {
 	@Autowired
 	RedisCache redisCache;
 
+	@Autowired
+	private ICompanyUserService companyUserService;
+	@Autowired
+	private IFsUserService userService;
+	public Long getCompanyId() {
+		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
+		Claims claims=jwtUtils.getClaimByToken(headValue);
+		String userId = claims.getSubject().toString();
+		Long companyId =(Long)redisCache.getCacheObject("companyId:"+userId);
+		return companyId;
+	}
 	public String getUserId()
 	{
 		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
@@ -21,6 +37,15 @@ public class AppBaseController {
 		String userId = claims.getSubject().toString();
 		return userId;
 	}
-
+	//获取商城手机号
+	public Long getUserId(Long companyUserId)
+	{
+		CompanyUser companyUser=companyUserService.selectCompanyUserById(companyUserId);
+		FsUser user=userService.selectFsUserByPhone(companyUser.getPhonenumber());
+		if(user==null){
+			throw new FSException("未授权,请用员工手机号登录商城");
+		}
+		return user.getUserId();
+	}
 
 }

+ 209 - 43
fs-company-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,65 +1,231 @@
 package com.fs.app.controller;
 
 
-
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.XmlUtil;
-import cn.hutool.json.JSONUtil;
-import com.baidubce.appbuilder.model.agentbuilder.AgentBuilderResult;
-import com.fs.ai.service.IBaiduAIService;
+import cn.jiguang.common.resp.APIConnectionException;
+import cn.jiguang.common.resp.APIRequestException;
+import com.fs.app.annotation.Login;
+import com.fs.app.utils.CityTreeUtil;
 import com.fs.app.utils.JwtUtils;
+import com.fs.app.vo.CityVO;
+import com.fs.common.config.FSConfig;
 
-import com.fs.chat.config.QwConfig;
-import com.fs.chat.config.WxConfig;
-import com.fs.chat.domain.*;
-import com.fs.chat.service.*;
-import com.fs.common.core.redis.RedisCache;
-import com.fs.company.domain.CompanyConfig;
-import com.fs.company.service.ICompanyConfigService;
-import com.fs.his.service.*;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.file.OssException;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.file.FileUploadUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanySmsLogsService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.ICompanyVoiceLogsService;
+import com.fs.core.config.ServerConfig;
+import com.fs.crm.service.*;
+import com.fs.his.domain.FsCity;
+import com.fs.his.service.IFsAppVersionService;
+import com.fs.his.service.IFsCityService;
+import com.fs.jpush.service.JpushService;
 
-import com.fs.his.utils.ConfigUtil;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
 import com.fs.system.service.ISysDictDataService;
-import com.fs.voice.utils.StringUtil;
-import com.fs.wx.kf.dto.*;
-import com.fs.wx.kf.service.IWeixinKfService;
-import com.fs.wx.kf.vo.WeixinKfMsgItemVO;
-import com.fs.wx.kf.vo.WeixinKfMsgVO;
-import com.fs.wx.kf.vo.WeixinKfUserItemVO;
-import com.fs.wx.kf.vo.WeixinKfUserVO;
-import com.google.code.kaptcha.Producer;
-import com.qq.weixin.mp.aes.AesException;
-import com.qq.weixin.mp.aes.WXBizMsgCrypt;
+import com.fs.system.vo.DictVO;
+import com.fs.voice.service.IVoiceService;
+import com.google.common.collect.Lists;
 import io.swagger.annotations.Api;
-import org.apache.commons.lang3.StringUtils;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.web.bind.annotation.*;
-import org.w3c.dom.Element;
+import org.springframework.web.multipart.MultipartFile;
 
-import javax.annotation.Resource;
-import java.util.ArrayList;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-import static com.fs.common.constant.FsConstants.REDIS_CHAT_NEXTCURSOR;
-import static com.fs.common.constant.FsConstants.REDIS_CHAT_SESSION;
+import java.util.Map;
 
 
 @Api("公共接口")
 @RestController
 @RequestMapping(value="/app/common")
-public class CommonController {
-
-    @Autowired
+public class CommonController extends AppBaseController {
+	@Autowired
     JwtUtils jwtUtils;
-    @Autowired
-    private ISysDictDataService dictDataService;
-    @Autowired
-    ConfigUtil configUtil;
+	@Autowired
+	private ServerConfig serverConfig;
+	@Autowired
+	private ISysDictDataService dictDataService;
+
+	@Autowired
+	ICrmCustomerUserService crmCustomerUserService;
+	@Autowired
+	ICrmCustomerService crmCustomerService;
+
+	@Autowired
+	private ICrmMsgService msgService;
+
+	@Autowired
+	private ISysConfigService configService;
+
+	@Autowired
+	private ICrmEventService eventService;
+
+	@Autowired
+	private IVoiceService voiceService;
+
+	@Autowired
+	ICompanyUserService userService;
+	@Autowired
+	private IFsAppVersionService fsAppVersionService;
+	@Autowired
+	private IFsCityService fsCityService;
+	@Autowired
+	private JpushService jpushService;
+
+	@Autowired
+	private ICrmCustomerHisOrderService crmCustomerHisOrderService;
+
+
+
+	@Autowired
+	private ICompanySmsLogsService smsLogsService;
+
+	@Autowired
+	private ICompanyVoiceLogsService voiceLogsService;
+
+	@Autowired
+	private ICompanyUserService companyUserService;
+
+	@Autowired
+	private ICrmCustomerContactsService crmCustomerContactsService;
+
+	public static final Logger LOGGER = LoggerFactory.getLogger(CommonController.class);
+
+
+	@ApiOperation("获取城市数据")
+	@GetMapping("/getCitys")
+	@Cacheable("cityData")
+	public R getCitys(){
+		List<FsCity> list=fsCityService.selectFsCityList(new FsCity());
+		List<CityVO> cityVOS = Lists.newArrayList();
+		for (FsCity city : list){
+			CityVO cityVO = new CityVO();
+			cityVO.setV(city.getCityId());
+			cityVO.setN(city.getCityName());
+			cityVO.setPid(city.getParentId());
+			cityVOS.add(cityVO);
+		}
+		return R.ok().put("data", CityTreeUtil.list2TreeConverter(cityVOS, "0"));
+
+	}
+
+	/**
+	 * 通用上传请求
+	 */
+	@ApiOperation("上传")
+	@PostMapping("/upload")
+	public R uploadFile(MultipartFile file) throws Exception
+	{
+		try
+		{
+			// 上传文件路径
+			String filePath = FSConfig.getUploadPath();
+			// 上传并返回新文件名称
+			String fileName = FileUploadUtils.upload(filePath, file);
+			String url = serverConfig.getUrl() + fileName;
+			AjaxResult ajax = AjaxResult.success();
+			ajax.put("fileName", fileName);
+			ajax.put("url", url);
+			return R.ok().put("fileName",fileName).put("url",url);
+		}
+		catch (Exception e)
+		{
+			return R.error(e.getMessage());
+		}
+	}
+
+
+	@ApiOperation("获取数据字典")
+	@GetMapping("/getDicts")
+//	@Cacheable(value= "dicts")
+	public R getDicts(
+	){
+		return R.ok();
+
+
+	}
+	@ApiOperation("获取数据字典")
+	@GetMapping("/getDictByKey")
+	public R getDictByKey(@ApiParam(required = false, name = "key", value = "key") @RequestParam(value = "key", required = false) String key){
+		List<DictVO> dictVOS=dictDataService.selectDictDataListByType(key);
+		return R.ok().put("data",dictVOS);
+	}
+
+	@Login
+	@ApiOperation("推送测试")
+	@GetMapping(value = "/pushText")
+	public R pushText()
+	{
+		CompanyUser companyUser=userService.selectCompanyUserById(Long.parseLong(getUserId()));
+		if(StringUtils.isEmpty(companyUser.getJpushId())){
+			return R.error("推送ID不存在");
+		}
+		Map<String, String> extrasMap=new HashMap<>();
+		extrasMap.put("customerId","1111");
+		extrasMap.put("type","1");
+		try {
+			jpushService.sendRegisterIdPush("客户消息","您有一个客户消息",extrasMap,companyUser.getJpushId());
+		} catch (APIConnectionException e) {
+			e.printStackTrace();
+			return R.error(e.getMessage());
+		} catch (APIRequestException e) {
+			e.printStackTrace();
+			return R.error(e.getMessage());
+		}
+		return R.ok();
+	}
+
+	@ApiOperation("获取系统配置")
+	@GetMapping(value = "/getConfig")
+	public R getConfig()
+	{
+		String sysConfig=configService.selectConfigByKey("sys:config");
+		return R.ok().put("sysConfig",sysConfig);
+	}
+	@ApiOperation("获取选择员工")
+	@GetMapping("/getSelectUserList")
+	public R getSelectUserList(HttpServletRequest request,
+	@ApiParam(required = false, name = "nickName", value = "nickName") @RequestParam(value = "nickName", required = false) String nickName
+	)
+	{
+		CompanyUser map=new CompanyUser();
+		map.setNickName(nickName);
+		map.setCompanyId(getCompanyId());
+		map.setIsDel(0);
+		map.setStatus("0");
+		List<CompanyUser> list = userService.selectCompanyUserList(map);
+		return R.ok().put("data",list);
+	}
+
+	@ApiOperation("上传")
+	@PostMapping("uploadOSS")
+	public R uploadOSS(@RequestParam("file") MultipartFile file) throws Exception
+	{
+
+		if (file.isEmpty())
+		{
+			throw new OssException("上传文件不能为空");
+		}
+		// 上传文件
+		String fileName = file.getOriginalFilename();
+		String suffix = fileName.substring(fileName.lastIndexOf("."));
+		CloudStorageService storage = OSSFactory.build();
+		String url = storage.uploadSuffix(file.getBytes(), suffix);
+		return R.ok().put("url",url);
+	}
 
 
 

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

@@ -0,0 +1,89 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyTag;
+import com.fs.company.service.ICompanyTagService;
+import com.fs.company.service.ICompanyTagUserService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = "企业标签接口")
+@RestController
+@RequestMapping("/app/companyTag")
+@AllArgsConstructor
+public class CompanyTagController extends AppBaseController {
+
+    private final ICompanyTagService companyTagService;
+    private final ICompanyTagUserService companyTagUserService;
+
+    /**
+     * 查询公司标签列表
+     */
+    @Login
+    @GetMapping("/list")
+    @ApiOperation("查询公司标签列表")
+    public R list(@RequestParam(required = false) String keyword,
+                  @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                  @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("查询公司标签列表 keyword: {}, pageNum: {}, pageSize: {}", keyword, pageNum, pageSize);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("companyId", getCompanyId());
+        if (StringUtils.isNotBlank(keyword)) {
+            params.put("keyword", keyword.split(","));
+        }
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<CompanyTag> list = companyTagService.selectCompanyTagListByMap(params);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+    /**
+     * 新增公司标签
+     */
+    @Login
+    @PostMapping("/add")
+    @ApiOperation("新增公司标签")
+    public R add(@RequestBody CompanyTag companyTag) throws CustomException {
+        log.debug("新增公司标签 companyTag: {}", JSON.toJSONString(companyTag));
+        companyTag.setCompanyId(getCompanyId());
+        companyTagService.insertCompanyTag(companyTag);
+        return R.ok();
+    }
+
+    /**
+     * 删除公司标签
+     */
+    @Login
+    @PostMapping("/delete")
+    @ApiOperation("删除公司标签")
+    public R remove(@RequestParam List<Long> tagIds) {
+        log.debug("删除公司标签 tagIds: {}", tagIds);
+        companyTagService.deleteCompanyTagByTagIds(tagIds);
+        return R.ok();
+    }
+
+    @Login
+    @GetMapping("/tagSubUsers")
+    @ApiOperation("标签下会员列表")
+    public R tagSubUsers(@RequestParam List<Long> tagId) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("tagIds", tagId);
+        params.put("companyId", getCompanyId());
+        return R.ok().put("data", companyTagUserService.selectUserListByMap(params));
+    }
+}

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

@@ -0,0 +1,311 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.app.annotation.Login;
+import com.fs.app.param.ChangeUserDeptAndPostParam;
+import com.fs.app.param.CompanyUserChangeApplyParam;
+import com.fs.app.param.CompanyUserParam;
+import com.fs.app.param.CompanyUserUpdateParam;
+import com.fs.app.service.IAppService;
+import com.fs.app.vo.CompanySubUserVO;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.constant.UserConstants;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.PatternUtils;
+import com.fs.common.utils.bean.BeanUtils;
+import com.fs.company.domain.*;
+import com.fs.company.mapper.CompanyRoleMapper;
+import com.fs.company.service.ICompanyDeptService;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserChangeApplyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.company.vo.CompanyUserChangeApplyVO;
+import com.fs.core.security.SecurityUtils;
+import com.fs.course.service.IFsCourseRedPacketLogService;
+import com.fs.course.service.IFsCourseWatchLogService;
+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;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Api(tags = "企业用户接口")
+@RestController
+@RequestMapping("/app/companyUser")
+@AllArgsConstructor
+public class CompanyUserController extends AppBaseController {
+
+    private final ICompanyUserService companyUserService;
+    private final ICompanyService companyService;
+    private final ICompanyDeptService companyDeptService;
+    private final IFsCourseWatchLogService courseWatchLogService;
+    private final IFsCourseRedPacketLogService courseRedPacketLogService;
+    private final ICompanyUserChangeApplyService companyUserChangeApplyService;
+    private final CompanyRoleMapper companyRoleMapper;
+    private final IAppService appService;
+
+    @Login
+    @ApiOperation("查询用户列表")
+    @GetMapping("/getCompanyUserList")
+    public R getCompanyUserList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("查询用户列表 pageNum: {}, pageSize: {}", pageNum, pageSize);
+
+        Long userId = Long.parseLong(getUserId());
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(userId);
+        List<CompanyRole> companyRoles = companyRoleMapper.selectRolePermissionByUserId(userId);
+        List<CompanyUser> companyUsers;
+
+        // 判断是否管理员 或者包含 1:全部数据权限
+        if (companyUser.isAdmin() || companyRoles.stream().anyMatch(r -> "1".equals(r.getDataScope()))) {
+            PageHelper.startPage(pageNum, pageSize);
+            companyUsers = companyUserService.getCompanyUserListByDeptId(null);
+        }
+        // 判断是否包含 3:本部门数据权限 4:本部门及以下数据权限
+        else if (companyRoles.stream().anyMatch(r -> "3".equals(r.getDataScope()) || "4".equals(r.getDataScope()))) {
+            PageHelper.startPage(pageNum, pageSize);
+            companyUsers = companyUserService.getCompanyUserListByDeptId(companyUser.getDeptId());
+        }
+        // 默认空 -- 判断是否包含 5:仅可查看本人
+        else {
+            companyUsers = new ArrayList<>();
+        }
+        PageInfo<CompanyUser> page = new PageInfo<>(companyUsers);
+
+        // 转换对象
+        List<CompanySubUserVO> users = page.getList().stream().map(u -> {
+            CompanySubUserVO vo = new CompanySubUserVO();
+            BeanUtils.copyProperties(u, vo);
+
+            Map<String, Object> params = new HashMap<>();
+            params.put("date", LocalDate.now());
+            params.put("companyUserId", u.getUserId());
+            params.put("logTypes", Arrays.asList(1,2,4));
+
+            // 今日观看人数
+            int count = courseWatchLogService.countByMap(params);
+            vo.setTodayWatchCount(count);
+
+            // 今日完播人数
+            params.put("logTypes", Collections.singletonList(2));
+            int finishCount = courseWatchLogService.countByMap(params);
+            vo.setTodayFinishCount(finishCount);
+
+            // 今日完播率
+            BigDecimal rate = count > 0 ? BigDecimal.valueOf(finishCount)
+                    .divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO;
+            vo.setTodayFinishRate(rate);
+
+            // 今日红包金额
+            BigDecimal amount = courseRedPacketLogService.getSumByCompanyUserIdId(u.getUserId());
+            vo.setTodayRedPacketAmount(amount);
+
+            // 是否存在会员转移申请
+            Wrapper<CompanyUserChangeApply> applyWrapper = Wrappers.<CompanyUserChangeApply>lambdaQuery()
+                    .eq(CompanyUserChangeApply::getFrom, u.getUserId())
+                    .orderByDesc(CompanyUserChangeApply::getApplyTime)
+                    .last("limit 1");
+            CompanyUserChangeApply apply = companyUserChangeApplyService.getOne(applyWrapper);
+            int applyStatus = 3; // 不存在
+            if (Objects.nonNull(apply)) {
+                applyStatus = apply.getStatus();
+            }
+            vo.setApplyStatus(applyStatus);
+
+            return vo;
+        }).collect(Collectors.toList());
+
+        PageInfo<CompanySubUserVO> pageInfo = new PageInfo<>();
+        BeanUtils.copyProperties(page, pageInfo);
+        pageInfo.setList(users);
+
+        return R.ok().put("data", pageInfo);
+    }
+
+    @Login
+    @ApiOperation("获取销售信息")
+    @GetMapping("/getCompanyUserInfo")
+    public R getCompanyUserInfo(@ApiParam(name = "companyUserId", value = "销售用户ID", required = true)
+                                @RequestParam Long companyUserId) {
+        return R.ok().put("data", companyUserService.selectCompanyUserById(companyUserId));
+    }
+
+    @Login
+    @ApiOperation("修改用户信息")
+    @PostMapping("/updateUserInfo")
+    public R updateUserInfo(@Valid @RequestBody CompanyUserUpdateParam param) {
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getUserId());
+        if (Objects.isNull(companyUser)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        companyUser.setUserId(param.getUserId());
+        companyUser.setNickName(param.getNickName());
+        companyUser.setPhonenumber(param.getPhoneNumber());
+        companyUser.setRemark(param.getRemark());
+        companyUserService.updateCompanyUser(companyUser);
+        return R.ok();
+    }
+
+    @ApiOperation("注册")
+    @PostMapping("/resisterCompanyUser")
+    public R resisterCompanyUser(@Valid @RequestBody CompanyUserParam param) {
+        Company company = companyService.selectCompanyById(param.getCompanyId());
+        if (Objects.isNull(company)) {
+            return R.error("公司不存在");
+        }
+
+        // 判断用户数量是否已达到上线
+        Integer count = companyUserService.selectCompanyUserCountByCompanyId(param.getCompanyId());
+        if(count > company.getLimitUserCount()) {
+            return R.error("用户数量已达到上限");
+        }
+
+        if (UserConstants.NOT_UNIQUE.equals(String.valueOf(companyUserService.checkUserName(param.getPhoneNumber())))) {
+            return R.error("注册用户'" + param.getPhoneNumber() + "'失败,登录账号已存在");
+        }
+
+        // 密码校验
+        if (!PatternUtils.checkPassword(param.getPassword())) {
+            return R.error("密码格式不正确,需包含字母、数字和特殊字符,长度为 8-20位");
+        }
+
+        // 组装参数
+        CompanyUser companyUser = new CompanyUser();
+        BeanUtils.copyProperties(param, companyUser);
+
+        companyUser.setUserName(param.getPhoneNumber());
+        companyUser.setPhonenumber(param.getPhoneNumber());
+        companyUser.setPassword(SecurityUtils.encryptPassword(companyUser.getPassword()));
+        companyUser.setCreateTime(new Date());
+        companyUser.setIsAudit(0);
+
+        // 部门
+        CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(param.getCompanyId());
+        if (Objects.nonNull(dept)) {
+            companyUser.setDeptId(dept.getDeptId());
+        }
+
+        companyUserService.insertUser(companyUser);
+        return R.ok();
+    }
+
+    @Login
+    @ApiOperation("接收群管列表")
+    @GetMapping("/companyUserListByCompanyId")
+    public R CompanyUserListByCompanyId(){
+        // 查询公司下销售
+        CompanyUser companyUser = new CompanyUser();
+        companyUser.setCompanyId(getCompanyId());
+        List<CompanyUser> companyUsers = companyUserService.selectCompanyUserList(companyUser);
+        return R.ok().put("data",companyUsers);
+    }
+
+    @Login
+    @RepeatSubmit
+    @ApiOperation("更换会员归属申请")
+    @PostMapping("/changeUserParentApply")
+    public R changeVipUser(@Valid @RequestBody CompanyUserChangeApplyParam param) {
+        // 参数校验
+        CompanyUser fromUser = companyUserService.selectCompanyUserById(param.getFrom());
+        if (Objects.isNull(fromUser)) {
+            return R.error("原归属销售不存在");
+        }
+
+        CompanyUser toUser = companyUserService.selectCompanyUserById(param.getTo());
+        if (Objects.isNull(toUser)) {
+            throw new ServiceException("申请更换归属销售不存在");
+        }
+
+        if (param.getType() != 0 && param.getType() != 1) {
+            throw new ServiceException("类型不正确");
+        }
+
+        if (param.getType() == 1 && (Objects.isNull(param.getIds()) || param.getIds().isEmpty())) {
+            throw new ServiceException("请先选择会员");
+        }
+
+        // 存在待审核的申请不能再次申请
+        Wrapper<CompanyUserChangeApply> applyWrapper = Wrappers.<CompanyUserChangeApply>lambdaQuery()
+                .eq(CompanyUserChangeApply::getFrom, fromUser.getUserId())
+                .eq(CompanyUserChangeApply::getStatus, 0);
+        if (companyUserChangeApplyService.count(applyWrapper) > 0) {
+            throw new ServiceException("存在待审核申请");
+        }
+
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+
+        // 添加申请
+        companyUserChangeApplyService.apply(param.getFrom(), param.getTo(), param.getType(), param.getIds(), companyUser.getCompanyId(), companyUser.getUserName());
+        return R.ok();
+    }
+
+    @Login
+    @ApiOperation("申请列表")
+    @GetMapping("/applyList")
+    public R applyList(@RequestParam(required = false) Integer status,
+                       @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                       @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("申请列表 status: {}, pageNum: {}, pageSize: {}", status, pageNum, pageSize);
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("status", status);
+        map.put("companyId", getCompanyId());
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<CompanyUserChangeApplyVO> list = companyUserChangeApplyService.selectApplyListByMap(map);
+        PageInfo<CompanyUserChangeApplyVO> page = new PageInfo<>(list);
+        return R.ok().put("data", page);
+    }
+
+    @Login
+    @ApiOperation("部门树列表")
+    @GetMapping("/deptList")
+    public R deptList() {
+        return R.ok().put("data", appService.getDeptTreeSelect(getCompanyId()));
+    }
+
+    @Login
+    @ApiOperation("岗位列表")
+    @GetMapping("/postList")
+    public R postList() {
+        return R.ok().put("data", appService.postList(getCompanyId()));
+    }
+
+    @Login
+    @ApiOperation("角色列表")
+    @GetMapping("/roleList")
+    public R roleList() {
+        return R.ok().put("data", appService.roleList(getCompanyId()));
+    }
+
+    @Login
+    @ApiOperation("修改用户部门和岗位")
+    @PostMapping("/changeUserDeptAndPost")
+    public R changeUserDeptAndPost(@Valid @RequestBody ChangeUserDeptAndPostParam param) {
+        log.debug("changeUserDeptAndPost param: {}", JSON.toJSONString(param));
+
+        CompanyUser companyUser = companyUserService.selectCompanyUserByUserId(Long.parseLong(getUserId()));
+        if (!companyUser.isAdmin()) {
+            return R.error("没有权限");
+        }
+
+        appService.changeUserDeptAndPost(param);
+        return R.ok();
+    }
+}

+ 334 - 0
fs-company-app/src/main/java/com/fs/app/controller/CrmController.java

@@ -0,0 +1,334 @@
+package com.fs.app.controller;
+
+
+import com.fs.app.annotation.Login;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.OrderUtils;
+import com.fs.common.annotation.DataScope;
+import com.fs.common.core.domain.R;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.param.CompanySmsLogsListQueryParam;
+import com.fs.company.param.CompanyVoiceLogsListQueryParam;
+import com.fs.company.service.ICompanySmsLogsService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.ICompanyVoiceLogsService;
+import com.fs.company.vo.CompanySmsLogsListQueryVO;
+import com.fs.company.vo.CompanyVoiceLogsListVO;
+import com.fs.crm.domain.CrmCustomer;
+import com.fs.crm.domain.CrmCustomerContacts;
+import com.fs.crm.domain.CrmCustomerExt;
+import com.fs.crm.param.*;
+import com.fs.crm.service.*;
+import com.fs.crm.vo.*;
+import com.fs.his.service.IFsStoreOrderService;
+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;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+import static com.fs.common.constant.Constants.PAGE_SIZE;
+
+
+@Api("客户接口")
+@RestController
+@RequestMapping(value="/app/crm")
+public class CrmController extends  AppBaseController {
+	@Autowired
+	private ICrmCustomerContactsService crmCustomerContactsService;
+	@Autowired
+	private ICrmCustomerExtService crmCustomerExtService;
+	@Autowired
+    JwtUtils jwtUtils;
+	@Autowired
+	ICrmCustomerService crmCustomerService;
+	@Autowired
+	ICrmCustomerUserService crmCustomerUserService;
+	@Autowired
+	ICompanyUserService companyUserService;
+	@Autowired
+	private ICrmCustomerLogsService crmCustomerLogsService;
+	@Autowired
+	private ICrmCustomerVisitService crmCustomerVisitService;
+	@Autowired
+	ICompanyVoiceLogsService companyVoiceLogsService;
+	@Autowired
+	ICompanySmsLogsService companySmsLogsService;
+	@Autowired
+	IFsStoreOrderService storeOrderService;
+
+	@Login
+	@ApiOperation("获取公海客户列表")
+	@GetMapping("/getFullCustomerList")
+	@DataScope(deptAlias = "c",userAlias = "c")
+	public R getFullCustomerList(CrmFullCustomerListQueryParam param){
+		PageHelper.startPage(param.getPage(), param.getPageSize());
+		param.setCompanyId(getCompanyId());
+		if(getCompanyId()==116){   // 河北湘银信息咨询服务有限公司(JZ-1)客户假删除不显示
+			param.setCompanyId(0L);
+		}
+		List<CrmFullCustomerListQueryVO> list = crmCustomerService.selectCrmFullCustomerListQuery(param);
+		PageInfo<CrmFullCustomerListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+	@Login
+	@ApiOperation("获取我的客户列表")
+	@GetMapping("/getMyCustomerList")
+	public R getMyCustomerList(CrmMyCustomerListQueryParam param){
+		PageHelper.startPage(param.getPage(), param.getPageSize());
+		param.setCompanyId(getCompanyId());
+		if(getCompanyId()==116){   // 河北湘银信息咨询服务有限公司(JZ-1)客户假删除不显示
+			param.setCompanyId(0L);
+		}
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		List<CrmMyCustomerListQueryVO> list = crmCustomerService.selectCrmMyCustomerListQuery(param);
+		PageInfo<CrmMyCustomerListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+	@Login
+	@ApiOperation("获取客户详情")
+	@GetMapping("/getCustomerDetails")
+	public R getMyCustomerDetails(
+			HttpServletRequest request,
+			@ApiParam(required = true, name = "customerId", value = "customerId")
+			@RequestParam(value = "customerId", required = false) Long customerId){
+		CrmCustomer customer=crmCustomerService.selectCrmCustomerById(customerId);
+		Boolean isReceive=false;
+		if(customer.getIsReceive()!=null&&customer.getIsReceive()==1&&customer.getReceiveUserId()!=null&&getUserId().equals(customer.getReceiveUserId().toString())){
+			isReceive=true;
+		}
+		return R.ok().put("data",customer).put("isReceive",isReceive);
+	}
+	/**
+	 * 修改客户
+	 */
+	@Login
+	@ApiOperation("编辑客户")
+	@PostMapping("/edit")
+	public R edit(@RequestBody CrmCustomer crmCustomer)
+	{
+		if(crmCustomerService.updateCrmCustomer(crmCustomer)>0){
+			return R.ok();
+		}
+		else{
+			return R.error();
+		}
+	}
+	@Login
+	@ApiOperation("添加客户")
+	@PostMapping("/add")
+	public R add(@RequestBody CrmCustomer crmCustomer)
+	{
+		CompanyUser companyUser=companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+		crmCustomer.setDeptId(companyUser.getDeptId());
+		crmCustomer.setCustomerCode(OrderUtils.getOrderNo());
+		crmCustomer.setIsLine(0);
+		crmCustomer.setIsDel(0);
+		crmCustomer.setIsReceive(0);
+		crmCustomer.setStatus(1);
+		crmCustomer.setDeptId(companyUser.getDeptId());
+		crmCustomer.setCompanyId(getCompanyId());
+		crmCustomer.setCreateUserId(Long.parseLong(getUserId()));
+		if(crmCustomerService.insertCrmCustomer(crmCustomer)>0){
+			CrmCustomeReceiveParam param=new CrmCustomeReceiveParam();
+			String operName = companyUser.getNickName();
+			param.setCompanyId(companyUser.getCompanyId());
+			param.setCompanyUserId(companyUser.getUserId());
+			param.setCustomerId(crmCustomer.getCustomerId());
+			crmCustomerService.receive(param,operName);
+			return R.ok();
+		}
+		else{
+			return R.error();
+		}
+	}
+
+	//认领
+	@Login
+	@ApiOperation("认领")
+	@PostMapping("/receive")
+	public R receive(@RequestBody CrmCustomeReceiveParam param)
+	{
+		CompanyUser companyUser=companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+		String operName = companyUser.getNickName();
+		param.setCompanyId(companyUser.getCompanyId());
+		param.setCompanyUserId(companyUser.getUserId());
+		return crmCustomerService.receive(param,operName);
+
+	}
+	//回收
+	@Login
+	@PostMapping("/recover")
+	@ApiOperation("回收公海")
+	public R recover(@RequestBody CrmCustomeRecoverParam param)
+	{
+		CompanyUser companyUser=companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+		String operName = companyUser.getNickName();
+		param.setCompanyId(companyUser.getCompanyId());
+		param.setCompanyUserId(companyUser.getUserId());
+		return crmCustomerService.recover(param,operName);
+
+	}
+
+	@Login
+	@PostMapping("/assignUser")
+	@ApiOperation("转移用户")
+	public R assignUser(@RequestBody CrmCustomeAssignUserParam param)
+	{
+		CompanyUser companyUser=companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+		String operName = companyUser.getNickName();
+		return crmCustomerService.assignUser(param,operName);
+
+	}
+
+	@GetMapping("/getCustomerLogsList")
+	@ApiOperation("获取客户日志")
+	public R getCustomerLogsList(CrmCustomerLogsListQueryParam param)
+	{
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		List<CrmCustomerLogsListQueryVO> list = crmCustomerLogsService.selectCrmCustomerLogsListQuery(param);
+		PageInfo<CrmCustomerLogsListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+	@GetMapping("/getCustomerVisitList")
+	@ApiOperation("获取客户跟进记录")
+	public R getCustomerVisitList(CrmCustomerVisitListQueryParam param)
+	{
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		param.setCompanyId(getCompanyId());
+		List<CrmCustomerVisitListVO> list = crmCustomerVisitService.selectCrmCustomerVisitListQuery(param);
+		PageInfo<CrmCustomerVisitListVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+	@Login
+	@ApiOperation("提交跟进")
+	@PostMapping("/addVisit")
+	public R addVisit(@RequestBody CrmCustomerVisitAddParam param) {
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		return crmCustomerVisitService.addVisit(param);
+	}
+
+
+	@Login
+	@GetMapping("/getCustomerVoiceLogsList")
+	@ApiOperation("获取客户通话记录")
+	public R getCustomerVoiceLogsList(CompanyVoiceLogsListQueryParam param) {
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		param.setCompanyId(getCompanyId());
+		List<CompanyVoiceLogsListVO> list = companyVoiceLogsService.selectCompanyVoiceLogsListQuery(param);
+		PageInfo<CompanyVoiceLogsListVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+	@Login
+	@GetMapping("/getCustomerSmsLogsList")
+	@ApiOperation("获取客户短信记录")
+	public R getCustomerSmsLogsList(CompanySmsLogsListQueryParam param) {
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		param.setCompanyId(getCompanyId());
+		List<CompanySmsLogsListQueryVO> list = companySmsLogsService.selectCompanySmsLogsListQuery(param);
+		PageInfo<CompanySmsLogsListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+	@Login
+	@PostMapping("/editTags")
+	@ApiOperation("修改标签")
+	public R editTags(@RequestBody CrmCustomerEditTagsParam param) {
+		return crmCustomerService.editTags(param);
+	}
+
+
+	@Login
+	@ApiOperation("获取自定义字段列表")
+	@GetMapping("/getCustomerExt")
+	public R getCustomerExt(CrmCustomerExt crmCustomerExt) {
+		CrmCustomerExt map=new CrmCustomerExt();
+		map.setStatus(1);
+		map.setCompanyId(getCompanyId());
+		List<CrmCustomerExt> list = crmCustomerExtService.selectCrmCustomerExtList(map);
+		return R.ok().put("data",list);
+	}
+
+
+
+ 	@Login
+	@ApiOperation("获取客户联系人")
+	@GetMapping("/getCustomerContacts")
+	public R getCustomerContacts(CrmCustomerContactsListQueryParam param) {
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		param.setCompanyId(getCompanyId());
+		List<CrmCustomerContactsListQueryVO> list = crmCustomerContactsService.selectCrmCustomerContactsListQuery(param);
+		PageInfo<CrmCustomerContactsListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+
+	@Login
+	@ApiOperation("获取客户联系人详情")
+	@GetMapping(value = "/getCustomerContactsDetails")
+	public R getCustomerContactsDetails(@ApiParam(required = true, name = "contactsId", value = "contactsId") @RequestParam(value = "contactsId", required = false) Long contactsId)
+	{
+		CrmCustomerContacts contacts=crmCustomerContactsService.selectCrmCustomerContactsById(contactsId);
+		return R.ok().put("data",contacts);
+	}
+
+
+	@Login
+	@ApiOperation("新增联系人")
+	@PostMapping("/addContacts")
+	public R addContacts(@RequestBody CrmCustomerContacts crmCustomerContacts)
+	{
+		crmCustomerContacts.setCompanyId(getCompanyId());
+		if(crmCustomerContactsService.insertCrmCustomerContacts(crmCustomerContacts)>0){
+			return R.ok();
+		}
+		else{
+			return R.error();
+		}
+	}
+
+	@Login
+	@ApiOperation("修改联系人")
+	@PostMapping("/editContacts")
+	public R editContacts(@RequestBody CrmCustomerContacts crmCustomerContacts)
+	{
+		if(crmCustomerContactsService.updateCrmCustomerContacts(crmCustomerContacts)>0){
+			return R.ok();
+		}
+		else{
+			return R.error();
+		}
+	}
+	@Login
+	@ApiOperation("删除联系人")
+	@PostMapping("/delContacts")
+	public R delContacts(@RequestBody CrmCustomerContacts crmCustomerContacts)
+	{
+		if(crmCustomerContactsService.deleteCrmCustomerContactsById(crmCustomerContacts.getContactsId())>0){
+			return R.ok();
+		}
+		else{
+			return R.error();
+		}
+	}
+
+	@Login
+	@ApiOperation("查询客户")
+	@GetMapping("/getCustomerListBySearch")
+	public R getCustomerListBySearch(CrmCustomerSearchParam param){
+		param.setCompanyId(getCompanyId());
+		List<CrmCustomer> list = crmCustomerService.selectCrmCustomerListBySearch(param);
+		return R.ok().put("data",list);
+	}
+
+
+}

+ 111 - 0
fs-company-app/src/main/java/com/fs/app/controller/CrmEventController.java

@@ -0,0 +1,111 @@
+package com.fs.app.controller;
+
+
+import com.fs.app.annotation.Login;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.crm.domain.CrmEvent;
+import com.fs.crm.param.CrmAddEventParam;
+import com.fs.crm.param.CrmDelEventParam;
+import com.fs.crm.param.CrmDoEventParam;
+import com.fs.crm.param.CrmEventListQueryParam;
+import com.fs.crm.service.ICrmEventService;
+import com.fs.crm.vo.CrmEventListQueryVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static com.fs.common.constant.Constants.PAGE_SIZE;
+
+
+@Api("代办事项接口")
+@RestController
+@RequestMapping(value="/app/crmEvent")
+public class CrmEventController extends  AppBaseController {
+	@Autowired
+    JwtUtils jwtUtils;
+	@Autowired
+	ICrmEventService eventService;
+
+	@Login
+	@GetMapping("/getMyCrmDoEventList")
+	@ApiOperation("获取我代办事件列表")
+	public R getMyCrmDoEventList()
+	{
+		CrmEventListQueryParam param=new CrmEventListQueryParam();
+		param.setCompanyId(getCompanyId());
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		param.setStatus(0);
+		List<CrmEventListQueryVO> list = eventService.selectCrmEventListQuery(param);
+		PageInfo<CrmEventListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+	@Login
+	@GetMapping("/getMyCrmEventList")
+	@ApiOperation("获取我的事件列表")
+	public R getMyCrmEventList(CrmEventListQueryParam param)
+	{
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		param.setCompanyId(getCompanyId());
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		List<CrmEventListQueryVO> list = eventService.selectCrmEventListQuery(param);
+		PageInfo<CrmEventListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+	@Login
+	@PostMapping("/addCrmEvent")
+	@ApiOperation("添加事件")
+	public R addCrmEvent(@RequestBody CrmAddEventParam param)
+	{
+		CrmEvent event=new CrmEvent();
+		BeanUtils.copyProperties(param,event);
+		event.setCompanyId(getCompanyId());
+		event.setCompanyUserId(Long.parseLong(getUserId()));
+		if(eventService.insertCrmEvent(event)>0){
+			return R.ok("操作成功");
+		}
+		else{
+			return R.error("操作失败");
+		}
+	}
+
+	@Login
+	@PostMapping("/doEvent")
+	@ApiOperation("完成事件")
+	public R doEvent(@RequestBody CrmDoEventParam param)
+	{
+		CrmEvent event=eventService.selectCrmEventById(param.getEventId());
+		if(event.getStatus()!=0){
+			return R.error("此事件已完成");
+		}
+		event.setStatus(1);
+		if(eventService.updateCrmEvent(event)>0){
+			return R.ok("操作成功");
+		}
+		else{
+			return R.error("操作失败");
+		}
+	}
+
+	@Login
+	@PostMapping("/delCrmEvent")
+	@ApiOperation("删除事件")
+	public R addCrmEvent(@RequestBody CrmDelEventParam param)
+	{
+		if(eventService.deleteCrmEventById(param.getEventId())>0){
+			return R.ok("操作成功");
+		}
+		else{
+			return R.error("操作失败");
+		}
+	}
+
+
+}

+ 107 - 0
fs-company-app/src/main/java/com/fs/app/controller/CrmMsgController.java

@@ -0,0 +1,107 @@
+package com.fs.app.controller;
+
+
+import com.fs.app.annotation.Login;
+import com.fs.app.param.CrmDoReadMsgParam;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.crm.param.CrmMsgListQueryParam;
+import com.fs.crm.service.ICrmMsgService;
+import com.fs.crm.vo.CrmMsgListQueryVO;
+import com.fs.crm.vo.CrmMsgTypeVO;
+import com.fs.system.service.ISysDictDataService;
+import com.fs.system.vo.DictVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.fs.common.constant.Constants.PAGE_SIZE;
+
+
+@Api("消息接口")
+@RestController
+@RequestMapping(value="/app/crmMsg")
+public class CrmMsgController extends  AppBaseController {
+	@Autowired
+    JwtUtils jwtUtils;
+	@Autowired
+	ICrmMsgService msgService;
+	@Autowired
+	private ISysDictDataService dictDataService;
+	@Login
+	@ApiOperation("获取消息数量")
+	@GetMapping("/getMsgCount")
+	public R getMsgCount(HttpServletRequest request){
+		Long count= msgService.selectCrmMsgCountByUserId(Long.parseLong(getUserId()));
+		return R.ok().put("counts",count);
+
+	}
+	@Login
+	@ApiOperation("获取消息")
+	@GetMapping("/getMsg")
+	public R getMsg(HttpServletRequest request){
+		//获取用户未读总数
+		//获取所有类型
+		List<DictVO> types=dictDataService.selectDictDataListByType("crm_msg_type");
+		List<CrmMsgTypeVO> counts=new ArrayList<>();
+		for(DictVO v:types){
+			Long count= msgService.selectCrmMsgCountByUserId(Long.parseLong(getUserId()),Integer.parseInt(v.getDictValue()));
+			String title=msgService.selectCrmNewMsgByUserId(Long.parseLong(getUserId()),Integer.parseInt(v.getDictValue()));
+			CrmMsgTypeVO typeVO=new CrmMsgTypeVO();
+			typeVO.setMsgType(Integer.parseInt(v.getDictValue()));
+			typeVO.setTotal(count);
+			if(title!=null){
+				typeVO.setTitle(title);
+			}
+			typeVO.setMsgTypeName(v.getDictLabel());
+			counts.add(typeVO);
+		}
+		return R.ok().put("counts",counts);
+	}
+
+
+	@Login
+	@GetMapping("/getMsgList")
+	@ApiOperation("获取我的消息列表")
+	public R getMsgList(CrmMsgListQueryParam param)
+	{
+		PageHelper.startPage(param.getPage(), PAGE_SIZE);
+		param.setCompanyId(getCompanyId());
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		List<CrmMsgListQueryVO> list = msgService.selectCrmMsgListQuery(param);
+		PageInfo<CrmMsgListQueryVO> listPageInfo=new PageInfo<>(list);
+		return R.ok().put("data",listPageInfo);
+	}
+
+
+
+	@Login
+	@PostMapping("/doRead")
+	@ApiOperation("已读")
+	public R doRead(@RequestBody CrmDoReadMsgParam param)
+	{
+		msgService.setReadByType(Long.parseLong(getUserId()),param.getMsgType());
+		return R.ok("操作成功");
+	}
+
+
+	@Login
+	@ApiOperation("全部已读")
+	@PostMapping("/setAllRead")
+	public R setAllRead()
+	{
+		msgService.setAllRead(Long.parseLong(getUserId()));
+		return R.ok();
+	}
+
+
+
+
+}

+ 339 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -0,0 +1,339 @@
+package com.fs.app.controller;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.app.annotation.Login;
+import com.fs.app.config.ImageStorageConfig;
+
+import com.fs.app.param.FsUserTagUpdateParam;
+import com.fs.app.param.FsUserUpdateParam;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyTagUserService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.course.param.CourseAnalysisParam;
+import com.fs.course.param.newfs.FsUserCourseBeMemberImageParam;
+import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.vo.newfs.FsCourseAnalysisVO;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import com.fs.store.param.h5.FsUserPageListParam;
+import com.fs.store.param.h5.TagListParam;
+import com.fs.store.param.h5.UserStatisticsCommonParam;
+import com.fs.store.service.IFsUserCourseCountService;
+import com.fs.store.vo.h5.*;
+import com.fs.system.service.ISysConfigService;
+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;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.util.*;
+
+@Slf4j
+@Api(tags = "用户会员相关接口")
+@RestController
+@RequestMapping("/app/fs/user")
+public class FsUserController extends AppBaseController {
+
+    @Autowired
+    private IFsUserService fsUserService;
+
+    @Autowired
+    private ICompanyUserService companyUserService;
+
+    @Autowired
+    private ICompanyTagUserService companyTagUserService;
+
+    @Autowired
+    private ImageStorageConfig imageConfig;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IFsUserCourseCountService userCourseCountService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Login
+    @PostMapping("/pageList")
+    @ApiOperation("用户会员分页列表")
+    public ResponseResult<PageInfo<FsUserPageListVO>> pageList(@RequestBody FsUserPageListParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
+//        PageInfo<FsUserPageListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(fsUserPageListVOPageInfo);
+    }
+
+    @Login
+    @GetMapping("/allCompanyUser")
+    @ApiOperation("获取所有公司销售")
+    public ResponseResult<List<CompanyUser>> getAllCompanyUser() {
+        List<CompanyUser> companyUsers = companyUserService.selectAllCompanyUserAndSelf(Long.parseLong(getUserId()));
+        return ResponseResult.ok(companyUsers);
+    }
+
+    @Login
+    @GetMapping("/totalNumber")
+    @ApiOperation("用户会员数量统计")
+    public ResponseResult<UserListPageVO> getTotalNumber() {
+        UserListPageVO userNumber = fsUserService.getUserNumber(Long.parseLong(getUserId()));
+        return ResponseResult.ok(userNumber);
+    }
+
+    @Login
+    @GetMapping("/details")
+    @ApiOperation("用户会员详情")
+    public ResponseResult<UserDetailsVO> getUserDetails(@ApiParam(value = "用户id", required = true) @RequestParam Long userId,
+                                                        @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) @RequestParam(required = false) String dateTag) {
+        UserDetailsVO userDetails = fsUserService.getUserDetails(Long.parseLong(getUserId()), userId, dateTag);
+        return ResponseResult.ok(userDetails);
+    }
+
+    @Login
+    @GetMapping("/tagList")
+    @ApiOperation("用户会员标签列表")
+    public ResponseResult<PageInfo<CompanyUserTagListVO>> getTagList(TagListParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<CompanyUserTagListVO> tagList = companyTagUserService.getTagList(param);
+        PageInfo<CompanyUserTagListVO> pageInfo = new PageInfo<>(tagList);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @PostMapping("/disabled")
+    @ApiOperation("批量禁用会员")
+    public ResponseResult<Boolean> disabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
+        Boolean r = fsUserService.disabledUser(ids, false);
+        return ResponseResult.ok(r);
+    }
+
+    @Login
+    @PostMapping("/enabled")
+    @ApiOperation("批量启用会员")
+    public ResponseResult<Boolean> enabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
+        // 如果存在重粉的数据,则禁止启用,需要提示
+        long companyUserId = Long.parseLong(getUserId());
+        Integer count = fsUserService.selectFsUserByUserIds(ids, companyUserId);
+        if(count > 0){
+            return ResponseResult.fail(400, "重粉会员不能移除小黑屋");
+        }
+        Boolean r = fsUserService.disabledUser(ids, true);
+        return ResponseResult.ok(r);
+    }
+
+    @Login
+    @GetMapping("/firstPage/summaryCount")
+    @ApiOperation("首页数据-顶部汇总统计")
+    public ResponseResult<FsUserSummaryCountVO> userSummaryCount() {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok(fsUserService.userSummaryCount(userId));
+    }
+
+    @Login
+    @GetMapping("/firstPage/statistics")
+    @ApiOperation("首页数据-课程/答题/红包统计")
+    public ResponseResult<FsUserStatisticsVO> userStatistics(@ApiParam(value = "开始时间", required = true) @RequestParam String startTime,
+                                                             @ApiParam(value = "结束时间", required = true) @RequestParam String endTime) {
+        long userId = Long.parseLong(getUserId());
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String nowDate = dateFormat.format(new Date());
+        /*---------- 如果传入的日期是今天 ----------*/
+        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+        param.setUserId(userId).setStartTime(startTime).setEndTime(endTime);
+        FsUserStatisticsVO vo = fsUserService.userStatistics(param);
+        if (nowDate.compareTo(startTime) > 0 && nowDate.compareTo(endTime) < 0) {
+            String yesterday = LocalDate.now().minusDays(1).toString();
+            UserStatisticsCommonParam paramYes = new UserStatisticsCommonParam();
+            paramYes.setUserId(userId).setStartTime(yesterday + " 00:00:00").setEndTime(yesterday + " 23:59:59");
+            FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatistics(paramYes);
+            vo.setYesterdayVO(fsUserStatisticsVO);
+        }
+        return ResponseResult.ok(vo);
+    }
+
+    @Login
+    @GetMapping("/statistics/details")
+    @ApiOperation("数据统计详情-课程/答题/红包统计")
+    public ResponseResult<FsUserStatisticsVO> userStatisticsDetails(@ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId) {
+        long userId = Long.parseLong(getUserId());
+        long companyId = getCompanyId();
+        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+        param.setUserId(userId).setPeriodId(periodId).setVideoId(videoId).setCompanyId(companyId);
+        FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatisticsDetails(param);
+        return ResponseResult.ok(fsUserStatisticsVO);
+    }
+
+    @Login
+    @GetMapping("/firstPage/userRanking")
+    @ApiOperation("首页数据/详情-销售排行榜统计")
+    public ResponseResult<List<FsUserRankingVO>> userRanking(
+            @ApiParam(value = "开始时间") @RequestParam(required = false) String startTime,
+            @ApiParam(value = "结束时间") @RequestParam(required = false) String endTime,
+            @ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+            @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+            @ApiParam(value = "asc-正序,desc-倒序", required = true) @RequestParam String order,
+            @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
+    ) {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok(fsUserService.userRanking(userId, startTime, endTime, periodId, videoId, order, type));
+    }
+
+    @Login
+    @GetMapping("/firstPage/courseRanking")
+    @ApiOperation("首页数据/详情-课程排行榜统计")
+    public ResponseResult<List<FsCourseRankingVO>> courseRanking(
+            @ApiParam(value = "开始时间") @RequestParam(required = false) String startTime,
+            @ApiParam(value = "结束时间") @RequestParam(required = false) String endTime,
+            @ApiParam(value = "课程id") @RequestParam(required = false) String courseId,
+            @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+            @ApiParam(value = "asc-正序,desc-倒序", required = true) @RequestParam String order,
+            @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
+    ) {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok(fsUserService.courseRanking(userId, startTime, endTime, courseId, videoId, order, type));
+    }
+
+    @Login
+    @GetMapping("/firstPage/graphic")
+    @ApiOperation("首页数据-转化漏斗图")
+    public ResponseResult<List<FsUserGraphicStatisticsVO>> graphicStatistics(@ApiParam(value = "开始时间", required = true) @RequestParam String startTime,
+                                                                             @ApiParam(value = "结束时间", required = true) @RequestParam String endTime) {
+        long userId = Long.parseLong(getUserId());
+        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+        param.setUserId(userId).setStartTime(startTime).setEndTime(endTime);
+        List<FsUserGraphicStatisticsVO> list = fsUserService.graphicStatistics(param);
+        return ResponseResult.ok(list);
+    }
+
+    @Login
+    @GetMapping("/graphic/details")
+    @ApiOperation("详情-转化漏斗图")
+    public ResponseResult<List<FsUserGraphicStatisticsVO>> graphicStatisticsDetails(@ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+                                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId) {
+        long userId = Long.parseLong(getUserId());
+        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+        param.setUserId(userId).setPeriodId(periodId).setVideoId(videoId);
+        List<FsUserGraphicStatisticsVO> list = fsUserService.graphicStatistics(param);
+        return ResponseResult.ok(list);
+    }
+
+    @Login
+    @ApiOperation("修改用户备注、姓名")
+    @PostMapping("/changeUserInfo")
+    public ResponseResult<Object> changeUserInfo(@Valid @RequestBody FsUserUpdateParam param) {
+        log.debug("修改用户备注、姓名 param:{}", JSON.toJSONString(param));
+        FsUser fsUser = fsUserService.selectFsUserById(param.getFsUserId());
+        if (Objects.isNull(fsUser)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        fsUser.setNickName(param.getNickName());
+        fsUser.setRemark(param.getRemark());
+        fsUserService.updateFsUser(fsUser);
+        return ResponseResult.ok();
+    }
+
+    @Login
+    @ApiOperation("修改用户标签")
+    @PostMapping("/changeUserTags")
+    public ResponseResult<Object> changeUserTags(@Valid @RequestBody FsUserTagUpdateParam param) {
+        companyTagUserService.changeUserTags(param.getFsUserIds(), param.getTagIds());
+        return ResponseResult.ok();
+    }
+
+    @Login
+    @GetMapping("/courseAnalysis")
+    @ApiOperation("管理-课程分析-分页列表查询")
+    public ResponseResult<PageInfo<FsCourseAnalysisVO>> courseAnalysisList(CourseAnalysisParam param) {
+        param.setCompanyId(getCompanyId());
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseAnalysisVO> list = fsUserService.courseAnalysis(param);
+        PageInfo<FsCourseAnalysisVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @GetMapping("/companyUser/details")
+    @ApiOperation("管理-群管数据-根据销售id,群管数据统计")
+    public ResponseResult<FsUserStatisticsVO> companyUserStatistics(@ApiParam(value = "营期id") @RequestParam(required = false) String periodId,
+                                                                    @ApiParam(value = "视频id") @RequestParam(required = false) String videoId,
+                                                                    @ApiParam(value = "销售用户id", required = true) @RequestParam String companyUserId) {
+        UserStatisticsCommonParam param = new UserStatisticsCommonParam();
+        param.setUserId(Long.parseLong(getUserId()))
+                .setPeriodId(periodId).setVideoId(videoId)
+                .setCompanyId(getCompanyId())
+                .setCompanyUserId(companyUserId);
+        FsUserStatisticsVO fsUserStatisticsVO = fsUserService.userStatisticsDetails(param);
+        return ResponseResult.ok(fsUserStatisticsVO);
+    }
+
+    @Login
+    @GetMapping("/companyUser/summaryCount")
+    @ApiOperation("管理-群管数据-顶部会员统计")
+    public ResponseResult<CompanyUserSummaryCountVO> companyUserSummaryCount(@ApiParam(value = "销售用户id", required = true) @RequestParam String companyUserId) {
+        long userId = Long.parseLong(getUserId());
+        return ResponseResult.ok(fsUserService.companyUserSummaryCount(userId, companyUserId));
+    }
+
+    @Login
+    @ApiOperation("会员关联绑定销售")
+    @PostMapping("/beMember")
+    public ResponseResult<Boolean> becomeMember(@Valid @RequestBody FsUserCourseBeMemberParam param) {
+        return fsUserService.becomeMember(param);
+    }
+
+    @Login
+    @PostMapping("/userImage")
+    @ApiOperation("生成分享会员海报")
+    public R createCourseImage(@RequestBody FsUserCourseBeMemberImageParam param) {
+        try {
+            //获取用户头像
+//            FsUser fsUser = fsUserService.selectFsUserById(param.getUserId());
+//            String url = "";
+//            if(fsUser != null) {
+//                url = fsUser.getAvatar();
+//            }
+            String path = imageConfig.getServerPath();
+            InputStream inputStream = fsUserCourseService.handleImage("", path);
+
+            // 背景图片使用的后台商城配置的会员海报,如果没有配置则使用默认的logo图片
+            String config = configService.selectConfigByKey("store.config");
+            JSONObject jsonObject = JSONObject.parseObject(config);
+            String userPosterImage = jsonObject.getString("userPosterImage");
+            String backgroundImagePath;
+            if(StringUtils.isEmpty(userPosterImage)){
+                backgroundImagePath = "https://fbylive.obs.cn-southwest-2.myhuaweicloud.com/fs/20250430/1745980979886.png";
+            } else {
+                backgroundImagePath = userPosterImage;
+            }
+            String base64Image = fsUserCourseService.createUserImageQR(param.getRealLink(), backgroundImagePath, inputStream, "png", param.getCompanyUserId());
+            // 返回Base64编码的图片字符串
+            Map<String, Object> map = new HashMap<>();
+            map.put("url", base64Image);
+            return R.ok().put("posterImage", map);
+        } catch (Exception e) {
+            log.error("生成海报失败,param:{}", param);
+            return R.error("生成海报失败!");
+        }
+    }
+
+}

+ 224 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -0,0 +1,224 @@
+package com.fs.app.controller;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.config.ImageStorageConfig;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.utils.StringUtils;
+import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsWatchCourseTimeParam;
+import com.fs.course.param.newfs.FsCourseSortLinkParam;
+import com.fs.course.param.newfs.FsUserCourseListParam;
+import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCoursePeriodService;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsUserCourseParticipationRecordVO;
+import com.fs.course.vo.newfs.FsUserCourseListVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
+import com.fs.course.vo.newfs.FsUserVideoListVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.InputStream;
+import java.time.LocalDate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+
+@Api("课程库相关接口")
+@RestController
+@RequestMapping("/app/fs/course")
+@Slf4j
+public class FsUserCourseVideoController extends AppBaseController {
+
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @Autowired
+    private ImageStorageConfig imageConfig;
+
+    @Autowired
+    private IFsUserCoursePeriodService fsUserCoursePeriodService;
+
+    @Login
+    @GetMapping("/pageList")
+    @ApiOperation("课程分页列表")
+    public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> list(UserCourseVideoPageParam param) {
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(getCompanyId());
+        param.setCompanyUserId(Long.parseLong(getUserId()));
+        List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.pageListCourseVideo(param);
+        PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @ApiOperation("课程视频详情")
+    @GetMapping(value = "/videoDetails")
+    public ResponseResult<FsUserCourseVideoDetailsVO> getVideoDetails(Long videoId) {
+        return fsUserCourseVideoService.getVideoDetails(videoId);
+    }
+
+    @Login
+    @GetMapping("/courseList")
+    @ApiOperation("获取课程下拉列表")
+    public ResponseResult<PageInfo<FsUserCourseListVO>> getAllCourseList(FsUserCourseListParam param) {
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(getCompanyId());
+        List<FsUserCourseListVO> fsUserCourseList = fsUserCourseService.getFsUserCourseList(param);
+        PageInfo<FsUserCourseListVO> pageInfo = new PageInfo<>(fsUserCourseList);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @GetMapping("/videoList")
+    @ApiOperation("获取视频下拉列表")
+    public ResponseResult<PageInfo<FsUserVideoListVO>> getAllVideoList(UserCourseVideoPageParam param) {
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyId(getCompanyId());
+        List<FsUserVideoListVO> listCourseVideo = fsUserCourseVideoService.getListCourseVideo(param);
+        PageInfo<FsUserVideoListVO> result = new PageInfo<>(listCourseVideo);
+        return ResponseResult.ok(result);
+    }
+
+    @Login
+    @ApiOperation("参与记录")
+    @GetMapping("/participationRecord")
+    public ResponseResult<Object> participationRecord(@RequestParam Long videoId,
+                                                      @RequestParam Integer type,
+                                                      @RequestParam(required = false) String keyword,
+                                                      @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                                      @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        log.debug("参与记录 keyword: {}, pageNum: {}, pageSize: {}", keyword, pageNum, pageSize);
+        Map<String, Object> params = new HashMap<>();
+        params.put("videoId", videoId);
+        params.put("type", type);
+        params.put("keyword", keyword);
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<FsUserCourseParticipationRecordVO> record = fsUserCourseService.getParticipationRecordByMap(params);
+        return ResponseResult.ok(new PageInfo<>(record));
+    }
+
+    @Login
+    @PostMapping("/courseSortLink")
+    @ApiOperation("生成课程分享短链")
+    public R createCourseSortLink(@RequestBody FsCourseSortLinkParam param) {
+        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+        BeanUtils.copyProperties(param, fsCourseLinkCreateParam);
+
+        R courseSortLink = fsUserCourseService.createCourseSortLink(fsCourseLinkCreateParam);
+        String url = courseSortLink.get("url").toString();
+        Map<String, Object> map = new HashMap<>();
+        map.put("url", url);
+        return R.ok(map);
+    }
+
+    @Login
+    @PostMapping("/courseImage")
+    @ApiOperation("生成课程海报")
+    public R createCourseImage(@RequestBody FsCourseSortLinkParam param) {
+        // 获取生成链接
+        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+        BeanUtils.copyProperties(param, fsCourseLinkCreateParam);
+
+        R courseSortLink = fsUserCourseService.createCourseSortLink(fsCourseLinkCreateParam);
+        String link = courseSortLink.get("link").toString();
+        R r = courseLinkService.getRealLink(link);
+        String realLink = r.get("realLink").toString();
+        try {
+            String path = imageConfig.getServerPath();
+            log.info("获取的logo图片路径,fileUrl:{}", path);
+            InputStream inputStream = fsUserCourseService.handleImage("", path);
+
+            // 获取营期的课程风格url
+            String imgUrl;
+            FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(param.getPeriodId());
+            if (fsUserCoursePeriod != null) {
+                imgUrl = fsUserCoursePeriod.getCourseStyle();
+            } else {
+                imgUrl = param.getImgUrl();
+            }
+            if(StringUtils.isEmpty(imgUrl)){
+                return R.error(400, "营期风格图片或课程封面不能为空!");
+            }
+
+            String base64Image = fsUserCourseService.createCourseImageQR(realLink, imgUrl, inputStream, "png", param.getTitle(), param.getDuration());
+            // 返回Base64编码的图片字符串
+            Map<String, Object> map = new HashMap<>();
+            map.put("url", base64Image);
+            return R.ok().put("posterImage", map);
+        } catch (Exception e) {
+            log.error("生成海报失败,param:{}", param);
+            return R.error("生成海报失败!");
+        }
+    }
+
+
+//    @Login
+//    @PostMapping("/sortLink/courseMember")
+//    @ApiOperation("生成分享成为会员短链")
+//    public R createCourseMemberSortLink(@RequestBody FsUserCourseBeMemberParam param) {
+//        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+//        BeanUtils.copyProperties(param, fsCourseLinkCreateParam);
+//
+//        R courseSortLink = fsUserCourseService.createCourseMemberSortLink(fsCourseLinkCreateParam);
+//        String url = courseSortLink.get("url").toString();
+//        Map<String, Object> map = new HashMap<>();
+//        map.put("url", url);
+//        return R.ok(map);
+//    }
+
+    @Login
+    @GetMapping("/todayCourseList")
+    @ApiOperation("今日课程")
+    public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> todayCourseList(@RequestParam(defaultValue = "1") Integer pageNum,
+                                                                                 @RequestParam(defaultValue = "10") Integer pageSize) {
+        Long companyId = getCompanyId();
+        if (Objects.isNull(companyId)) {
+            ResponseResult.fail(400, "未获取到公司ID,请重新登录后再试");
+        }
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("companyId", companyId);
+        params.put("dayDate", LocalDate.now());
+        params.put("companyUserId", Long.parseLong(getUserId()));
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.selectCourseVideoListByMap(params);
+        PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @PostMapping("/setWatchTime")
+    @ApiOperation("销售设置课程时间")
+    public ResponseResult<Boolean> setWatchCourseTime(@RequestBody List<FsWatchCourseTimeParam> paramList) {
+        List<FsWatchCourseTimeParam> collect = paramList.stream().peek(v -> {
+            v.setCompanyUserId(Long.parseLong(getUserId()));
+            v.setCompanyId(getCompanyId());
+        }).collect(Collectors.toList());
+        return fsUserCourseVideoService.setWatchCourseTime(collect);
+    }
+
+
+}

+ 65 - 0
fs-company-app/src/main/java/com/fs/app/controller/IndexController.java

@@ -0,0 +1,65 @@
+package com.fs.app.controller;
+
+
+import com.fs.app.annotation.Login;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.company.domain.Company;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.crm.param.CrmFullCustomerListQueryParam;
+import com.fs.crm.service.ICrmCustomerService;
+import com.fs.crm.service.ICrmCustomerUserService;
+import com.fs.his.service.IFsStoreOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+@Api("首页接口")
+@RestController
+@RequestMapping(value="/app/index")
+public class IndexController extends  AppBaseController {
+	@Autowired
+    JwtUtils jwtUtils;
+	@Autowired
+	ICrmCustomerService crmCustomerService;
+	@Autowired
+	IFsStoreOrderService storeOrderService;
+	@Autowired
+	ICompanyUserService companyUserService;
+	@Autowired
+	ICrmCustomerUserService crmCustomerUserService;
+	@Autowired
+	private ICompanyService companyService;
+	@Login
+	@ApiOperation("获取首页数据")
+	@GetMapping("/getIndexData")
+	public R getIndexData(CrmFullCustomerListQueryParam param){
+		Company company=companyService.selectCompanyById(getCompanyId());
+		//获取统计数据
+		Integer newCustomerCounts=crmCustomerService.selectCrmCustomerCountByType(company.getCompanyId(),1);
+		Integer fullCustomerCounts=crmCustomerService.selectCrmCustomerCountByType(company.getCompanyId(),2);
+		Integer myCustomerCounts=crmCustomerUserService.selectCrmMyCustomerUserCount(company.getCompanyId(),Long.parseLong(getUserId()));
+
+		Integer dayOrderCounts=storeOrderService.selectFsStoreOrderCountByType(company.getCompanyId(),Long.parseLong(getUserId()),1);
+		Integer doBuyOrderCounts=storeOrderService.selectFsStoreOrderCountByType(company.getCompanyId(),Long.parseLong(getUserId()),2);
+		Map<String,Object> data=new HashMap<>();
+		data.put("company",company);
+		data.put("newCustomerCounts",newCustomerCounts);
+		data.put("fullCustomerCounts",fullCustomerCounts);
+		data.put("myCustomerCounts",myCustomerCounts);
+		data.put("dayOrderCounts",dayOrderCounts);
+		data.put("doBuyOrderCounts",doBuyOrderCounts);
+
+		return R.ok().put("data",data);
+	}
+
+
+}

+ 96 - 0
fs-company-app/src/main/java/com/fs/app/controller/QwCompanyUserController.java

@@ -0,0 +1,96 @@
+package com.fs.app.controller;
+
+
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.qw.param.ExternalContactDetailsParam;
+import com.fs.qw.param.newparam.ContactTagListParam;
+import com.fs.qw.param.newparam.ExternalContactPageListParam;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwTagService;
+import com.fs.qw.vo.ExternalContactDetailsVO;
+import com.fs.qw.vo.QwTagVO;
+import com.fs.qw.vo.newvo.ExternalContactListVO;
+import com.fs.qw.vo.newvo.ExternalContactPageVO;
+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;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+
+@Api(tags = "企微用户相关接口")
+@RestController
+@RequestMapping("/app/company/user")
+public class QwCompanyUserController extends AppBaseController {
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IQwTagService tagService;
+
+    @Login
+    @GetMapping("/pageList")
+    @ApiOperation("会员分页列表")
+    public ResponseResult<PageInfo<ExternalContactListVO>> pageList(ExternalContactPageListParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<ExternalContactListVO> list = qwExternalContactService.externalContactPageList(param);
+        PageInfo<ExternalContactListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @GetMapping("/totalNumber")
+    @ApiOperation("会员数量统计")
+    public ResponseResult<ExternalContactPageVO> getTotalNumber() {
+        ExternalContactPageVO contactNumber = qwExternalContactService.getContactNumber(Long.parseLong(getUserId()));
+        return ResponseResult.ok(contactNumber);
+    }
+
+    @Login
+    @GetMapping("/details")
+    @ApiOperation("会员详情")
+    public ResponseResult<ExternalContactDetailsVO> getUserDetails(@ApiParam(value = "外部联系人id", required = true) @RequestParam Long contactId,
+                                                                   @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) @RequestParam String dateTag) {
+        ExternalContactDetailsParam externalContactDetailsParam = new ExternalContactDetailsParam();
+        externalContactDetailsParam.setUserId(Long.parseLong(getUserId()));
+        externalContactDetailsParam.setContactId(contactId);
+        externalContactDetailsParam.setDateTag(dateTag);
+        return ResponseResult.ok(qwExternalContactService.getUserDetails(externalContactDetailsParam));
+    }
+
+    @Login
+    @GetMapping("/tagList")
+    @ApiOperation("会员标签列表")
+    public ResponseResult<PageInfo<QwTagVO>> getTagList(ContactTagListParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<QwTagVO> tagList = tagService.getTagListByUserId(param);
+        PageInfo<QwTagVO> pageInfo = new PageInfo<>(tagList);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @PostMapping("/disabled")
+    @ApiOperation("批量禁用会员")
+    public ResponseResult<Boolean> disabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
+        Boolean r = qwExternalContactService.disabledUser(ids, false);
+        return ResponseResult.ok(r);
+    }
+
+    @Login
+    @PostMapping("/enabled")
+    @ApiOperation("批量启用会员")
+    public ResponseResult<Boolean> enabledUser(@ApiParam(value = "联系人id集合", required = true) @RequestBody String[] ids) {
+        Boolean r = qwExternalContactService.disabledUser(ids, true);
+        return ResponseResult.ok(r);
+    }
+
+
+}

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

@@ -0,0 +1,91 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.param.QwWorkTaskQueryParam;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwOptionsVO;
+import com.fs.qw.vo.UserVOs;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = "企微任务看板接口")
+@RestController
+@RequestMapping("/app/qwWorkTask")
+@AllArgsConstructor
+public class QwWorkTaskController extends AppBaseController {
+
+    private final IQwWorkTaskService qwWorkTaskService;
+    private final IQwUserService qwUserService;
+
+    @Login
+    @ApiOperation("企微任务看板列表")
+    @GetMapping("/list")
+    public R list(@Valid QwWorkTaskQueryParam param) {
+        log.debug("企微任务看板列表:{}", JSON.toJSONString(param));
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("qwUserId", param.getQwUserId());
+        params.put("companyId", getCompanyId());
+        params.put("companyUserId", getUserId());
+        params.put("date", param.getDate());
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<QwWorkTask> list = qwWorkTaskService.selectQwWorkTaskListByMap(params);
+
+        PageInfo<QwWorkTask> pageInfo = new PageInfo<>(list);
+        return R.ok().put("data", pageInfo);
+    }
+
+    @Login
+    @ApiOperation("销售下的企微用户")
+    @GetMapping("/getMyQwUserList")
+    public R userList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                      @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        Long userId = Long.parseLong(getUserId());
+        PageHelper.startPage(pageNum, pageSize);
+        List<QwOptionsVO> list = qwUserService.selectQwUserListOptionsVOByCompanyUserId(userId);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+    @Login
+    @ApiOperation("催课看板会员列表")
+    @GetMapping("/getUserList")
+    public R getUserList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                         @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        Long userId = Long.parseLong(getUserId());
+        PageHelper.startPage(pageNum, pageSize);
+        List<UserVOs> list = qwUserService.getUserList(userId);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+    @Login
+    @ApiOperation("催课看板企微会员列表")
+    @GetMapping("/getQwUserList")
+    public R getQwUserList(@RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                           @RequestParam(required = false, defaultValue = "10") Integer pageSize,
+                           @RequestParam(required = false, defaultValue = "10") String qwUserId) {
+        Long userId = Long.parseLong(getUserId());
+        PageHelper.startPage(pageNum, pageSize);
+        List<UserVOs> list = qwUserService.getQwUserList(userId, qwUserId);
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+}

+ 104 - 0
fs-company-app/src/main/java/com/fs/app/controller/SmsController.java

@@ -0,0 +1,104 @@
+package com.fs.app.controller;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.R;
+import com.fs.common.service.ISmsService;
+import com.fs.company.domain.CompanySms;
+import com.fs.company.param.CompanySmsTempListQueryParam;
+import com.fs.company.service.ICompanySmsLogsService;
+import com.fs.company.service.ICompanySmsService;
+import com.fs.company.service.ICompanySmsTempService;
+import com.fs.company.vo.CompanySmsTempListQueryVO;
+import com.fs.crm.param.SmsSendBatchParam;
+import com.fs.crm.param.SmsSendParam;
+import com.fs.crm.service.ICrmCustomerService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+
+@Api("短信接口")
+@RestController
+@RequestMapping(value="/app/sms")
+public class SmsController extends  AppBaseController {
+	Logger logger = LoggerFactory.getLogger(getClass());
+	@Autowired
+    JwtUtils jwtUtils;
+
+	@Autowired
+	private ICompanySmsTempService smsTempService;
+
+	@Autowired
+	private ICompanySmsService companySmsService;
+
+	@Autowired
+	private ICompanySmsLogsService smsLogsService;
+
+	@Autowired
+	private ICrmCustomerService crmCustomerService;
+
+	@Autowired
+	private ISmsService smsService;
+
+	@Login
+	@ApiOperation("获取短信模版列表")
+	@GetMapping("/getSmsTempList")
+	public R getSmsTempList(){
+		CompanySmsTempListQueryParam map=new CompanySmsTempListQueryParam();
+		map.setStatus(1);
+		map.setIsAudit(1);
+		map.setCompanyId(getCompanyId());
+		List<CompanySmsTempListQueryVO> smsTemp=smsTempService.selectCompanySmsTempListQuery(map);
+		return R.ok().put("data",smsTemp);
+
+	}
+
+	@Login
+	@ApiOperation("发送短信")
+	@PostMapping("/sendCustomerMsg")
+	@RepeatSubmit
+	@Transactional
+	public  R sendCustomerMsg(@Validated @RequestBody SmsSendParam param){
+		param.setCompanyId(getCompanyId());
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		return smsService.sendSms(param);
+	}
+
+	@Login
+	@ApiOperation("发送短信")
+	@PostMapping("/sendCustomerBatchMsg")
+	@RepeatSubmit
+	@Transactional
+	public  R sendCustomerBatchMsg(@Validated @RequestBody SmsSendBatchParam param){
+		param.setCompanyId(getCompanyId());
+		param.setCompanyUserId(Long.parseLong(getUserId()));
+		return smsService.sendBatchSms(param);
+	}
+
+
+	@PostMapping("/notify")
+	public  String notify(@RequestBody String json){
+		logger.info("smsNotify");
+		logger.info(json);
+		return smsService.smsNotify(json);
+	}
+
+
+	@Login
+	@ApiOperation("获取短信数量")
+	@GetMapping("/getCompanySmsCount")
+	public R getCompanySmsCount( ) {
+		CompanySms companySms=companySmsService.selectCompanySmsByCompanyId(getCompanyId());
+		return R.ok().put("data",companySms);
+	}
+
+}

+ 322 - 0
fs-company-app/src/main/java/com/fs/app/controller/StatisticsController.java

@@ -0,0 +1,322 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.TimeUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.param.CompanyStatisticsParam;
+import com.fs.company.param.FsStoreStatisticsParam;
+import com.fs.company.service.ICompanySmsLogsService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.ICompanyVoiceLogsService;
+import com.fs.company.vo.CompanySmsLogsStatisticsVO;
+import com.fs.company.vo.CompanyVoiceLogsStatisticsVO;
+import com.fs.company.vo.FsStoreOrderStatisticsVO;
+import com.fs.company.vo.FsStorePaymentStatisticsVO;
+import com.fs.crm.param.CrmCustomerStatisticsParam;
+import com.fs.crm.service.ICrmCustomerService;
+import com.fs.crm.service.ICrmCustomerVisitService;
+import com.fs.crm.vo.CrmCustomerStatisticsVO;
+import com.fs.crm.vo.CrmCustomerVisitStatisticsVO;
+import com.fs.his.service.IFsStoreOrderService;
+import com.fs.his.service.IFsStorePaymentService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+@Api("统计")
+@RestController
+@RequestMapping("/app/statistics")
+public class StatisticsController extends AppBaseController
+{
+
+    @Autowired
+    private ICompanyUserService userService;
+
+    @Autowired
+    private ICrmCustomerService crmCustomerService;
+    @Autowired
+    private ICrmCustomerVisitService crmCustomerVisitService;
+    @Autowired
+    private IFsStoreOrderService storeOrderService;
+    @Autowired
+    private IFsStorePaymentService storePaymentService;
+    @Autowired
+    private ICompanyVoiceLogsService voiceLogsService;
+    @Autowired
+    private ICompanySmsLogsService smsLogsService;
+
+    @Login
+    @ApiOperation("客户统计")
+    @GetMapping("/getCustomerStatistics")
+    public R getCustomerStatistics(CrmCustomerStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<CrmCustomerStatisticsVO> list= crmCustomerService.selectCrmCustomerStatisticsList(param);
+            TimeUtils.TimeEntity timeEntity=TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = crmCustomerService.selectCrmCustomerCounts(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> receiveCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("receiveCount")).collect(Collectors.toList());
+            List<Integer> poolCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("poolCount")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("receiveCount",receiveCount).put("poolCount",poolCount);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+    @ApiOperation("客户跟进统计")
+    @GetMapping("/getCustomerVisitStatistics")
+    public R getCustomerVisitStatistics(CrmCustomerStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<CrmCustomerVisitStatisticsVO> list= crmCustomerVisitService.selectCrmCustomerVisitStatisticsList(param);
+
+            TimeUtils.TimeEntity timeEntity=TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = crmCustomerVisitService.selectCrmCustomerVisitCounts(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> customerCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("customerCount")).collect(Collectors.toList());
+            List<Integer> visitCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("visitCount")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("customerCount",customerCount).put("visitCount",visitCount);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+
+    @ApiOperation("订单统计")
+    @GetMapping("/getStoreOrderStatistics")
+    public R getStoreOrderStatistics(FsStoreStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<FsStoreOrderStatisticsVO> list= storeOrderService.selectFsStoreOrderStatisticsList(param);
+
+            TimeUtils.TimeEntity timeEntity=TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = storeOrderService.selectFsStoreOrderCounts(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> orderCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("orderCount")).collect(Collectors.toList());
+            List<Integer> payPrice = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("payPrice")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("orderCount",orderCount).put("payPrice",payPrice);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+    @ApiOperation("收款订单统计")
+    @GetMapping("/getStorePaymentStatistics")
+    public R getStorePaymentStatistics(FsStoreStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<FsStorePaymentStatisticsVO> list= storePaymentService.selectFsStorePaymentStatisticsList(param);
+            TimeUtils.TimeEntity timeEntity=TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = storePaymentService.selectFsStorePaymentCounts(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> orderCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("orderCount")).collect(Collectors.toList());
+            List<Integer> payMoney = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("payMoney")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("orderCount",orderCount).put("payMoney",payMoney);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+
+    @ApiOperation("通话统计")
+    @GetMapping("/getVoiceLogsStatistics")
+    public R getVoiceLogsStatistics(CompanyStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<CompanyVoiceLogsStatisticsVO> list= voiceLogsService.selectVoiceLogsStatisticsList(param);
+            if(list!=null){
+                for(CompanyVoiceLogsStatisticsVO vo:list){
+                    double f1 = new BigDecimal((float)vo.getCallSuccessCount()/vo.getCallCount()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue()*100;
+                    vo.setCallRate(f1);
+                }
+            }
+            TimeUtils.TimeEntity timeEntity=TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = voiceLogsService.selectVoiceLogsTotalCount(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> callCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("callCount")).collect(Collectors.toList());
+            List<Integer> callSuccessCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("callSuccessCount")).collect(Collectors.toList());
+            List<Integer> times = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("times")).collect(Collectors.toList());
+            List<Integer> billingTime = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("billingTime")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("callCount",callCount).put("callSuccessCount",callSuccessCount).put("times",times).put("billingTime",billingTime);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+
+    @ApiOperation("短信统计")
+    @GetMapping("/getSmsLogsStatistics")
+    public R smsLogs(CompanyStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<CompanySmsLogsStatisticsVO> list= smsLogsService.selectSmsLogsStatisticsList(param);
+
+            TimeUtils.TimeEntity timeEntity=TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = smsLogsService.selectSmsLogsCounts(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> smsCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("smsCount")).collect(Collectors.toList());
+            List<Integer> successCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("successCount")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("smsCount",smsCount).put("successCount",successCount);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+
+}

+ 140 - 0
fs-company-app/src/main/java/com/fs/app/controller/StorePaymentController.java

@@ -0,0 +1,140 @@
+package com.fs.app.controller;
+
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.extra.qrcode.QrCodeUtil;
+import com.fs.app.annotation.Login;
+import com.fs.common.annotation.DataScope;
+import com.fs.common.config.FSConfig;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.his.param.FsStorePaymentParam;
+import com.fs.his.service.IFsStorePaymentService;
+import com.fs.his.vo.FsStorePaymentVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.io.FileUtils;
+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;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+
+@RestController
+@RequestMapping("/app/storePayment")
+public class StorePaymentController extends AppBaseController
+{
+    @Autowired
+    private FSConfig fsConfig;
+    @Autowired
+    private IFsStorePaymentService fsStorePaymentService;
+    @Autowired
+    private ICompanyUserService companyUserService;
+    @Login
+    @GetMapping("/getPaymenyList")
+    @DataScope(deptAlias = "p",userAlias = "cu")
+    public R list(FsStorePaymentParam param)
+    {
+        PageHelper.startPage(ServletUtils.getParameterToInt("page"), ServletUtils.getParameterToInt("pageSize"));
+        param.setBusinessType(1);
+        param.setStatus(1);
+        param.setCompanyId(getCompanyId());
+        List<FsStorePaymentVO> list = fsStorePaymentService.selectFsStorePaymentListQueryVO(param);
+        PageInfo<FsStorePaymentVO> listPageInfo=new PageInfo<>(list);
+        return R.ok().put("data",listPageInfo);
+    }
+
+
+    @Login
+    @ApiOperation("获取支付宝收款码")
+    @GetMapping("/getAlipayQrImg")
+    public R getAlipayQrImg(  HttpServletRequest request){
+        Long userId=Long.parseLong(getUserId());
+        try {
+            CompanyUser companyUser=companyUserService.selectCompanyUserById(userId);
+            File newFile = new File("qr.jpg");
+            File newFileT = new File("simsunb.ttf");
+            try {
+                InputStream stream =  getClass().getClassLoader().getResourceAsStream("qr.jpg");
+                FileUtils.copyInputStreamToFile(stream, newFile);
+                // if(!newFile.exists()){
+                //     InputStream stream =  getClass().getClassLoader().getResourceAsStream("fx.jpg");
+                //     FileUtils.copyInputStreamToFile(stream, newFile);
+                // }
+                if(!newFileT.exists()){
+                    InputStream streamT =  getClass().getClassLoader()
+                            .getResourceAsStream("simsunb.ttf");
+                    FileUtils.copyInputStreamToFile(streamT, newFileT);
+                }
+            } catch (IOException e) {
+
+                throw new CustomException(e.getMessage());
+            }
+
+            try {
+                String url=fsConfig.getQrPath()+"/qr-"+userId+".jpg";
+                File outputFile = new File(url);
+                if(!outputFile.exists())
+                {
+                    try {
+                        outputFile.createNewFile();
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+//                Font font =  Font.createFont(Font.TRUETYPE_FONT, newFileT);
+//                Font f= font.deriveFont(Font.PLAIN,20);
+//                ImgUtil.pressText(//
+//                        newFile,
+//                        outputFile,
+//                        companyUser.getNickName()+" 为您服务",
+//                        Color.BLACK,
+//                        f, //字体
+//                        65, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
+//                        100, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
+//                        0.8f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
+//                );
+                File qr = new File(fsConfig.getQrPath()+"/qr-"+userId+".png");
+                if(!qr.exists())
+                {
+                    try {
+                        qr.createNewFile();
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+                QrCodeUtil.generate( "http://alipay.yjf.runtzh.com/#/?companyId="+companyUser.getCompanyId()+"&companyUserId="+companyUser.getUserId(), 200, 200,
+                        FileUtil.file(fsConfig.getQrPath()+"/qr-"+userId+".png"));
+                ImgUtil.pressImage(
+                        newFile,
+                        outputFile,
+                        ImgUtil.read(qr), //QR图片
+                        0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
+                        20, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
+                        1f
+                );
+                return R.ok().put("url","profile/qr/qr-"+userId+".jpg");
+
+            } catch (Exception e) {
+                e.printStackTrace();
+                return R.error("操作异常");
+            }
+        } catch (Exception e){
+
+            return R.error("操作异常");
+        }
+    }
+
+}

+ 85 - 0
fs-company-app/src/main/java/com/fs/app/controller/StoreProductPackageController.java

@@ -0,0 +1,85 @@
+package com.fs.app.controller;
+
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsStoreProduct;
+import com.fs.his.domain.FsStoreProductAttrValue;
+import com.fs.his.domain.FsStoreProductPackage;
+import com.fs.his.dto.StoreOrderProductDTO;
+import com.fs.his.dto.StorePackageProductDTO;
+import com.fs.his.param.FsStoreProductPackageQueryParam;
+import com.fs.his.service.IFsStoreProductAttrValueService;
+import com.fs.his.service.IFsStoreProductPackageService;
+import com.fs.his.service.IFsStoreProductService;
+import com.fs.his.vo.FsStoreProductPacketVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Api("套餐")
+@RestController
+@RequestMapping(value="/app/storeProductPackage")
+public class StoreProductPackageController extends  AppBaseController {
+
+    @Autowired
+    private IFsStoreProductPackageService productPackageService;
+    @Autowired
+    private IFsStoreProductAttrValueService attrValueService;
+    @Autowired
+    private IFsStoreProductService storeProductService;
+    @Login
+    @ApiOperation("获取套餐列表")
+    @GetMapping("/getStoreProductPackage")
+    public R getStoreProductPackage(FsStoreProductPackageQueryParam param, HttpServletRequest request){
+        PageHelper.startPage(param.getPage(), param.getPageSize());
+        List<FsStoreProductPacketVO> list=productPackageService.selectFsStoreProductPackageListQueryVO(param);
+        PageInfo<FsStoreProductPacketVO> listPageInfo=new PageInfo<>(list);
+        return R.ok().put("data",listPageInfo);
+    }
+    @Login
+    @ApiOperation("获取套餐详情")
+    @GetMapping("/getStoreProductPackageDetails")
+    public R getStoreProductPackageDetails(@RequestParam("packageId") Long packageId, HttpServletRequest request){
+        FsStoreProductPackage storeProductPackage=productPackageService.selectFsStoreProductPackageById(packageId);
+        List<StoreOrderProductDTO> productList=new ArrayList<>();
+        JSONArray jsonArray= JSONUtil.parseArray(storeProductPackage.getProducts());
+        List<StorePackageProductDTO> goodsList=JSONUtil.toList(jsonArray, StorePackageProductDTO.class);
+        for(StorePackageProductDTO dto:goodsList){
+            StoreOrderProductDTO productDTO=new StoreOrderProductDTO();
+            FsStoreProductAttrValue attrValue=attrValueService.selectFsStoreProductAttrValueById(dto.getId());
+            if(attrValue!=null){
+                FsStoreProduct product=storeProductService.selectFsStoreProductById(attrValue.getProductId());
+                if(product!=null){
+                    productDTO.setProductId(attrValue.getProductId());
+                    productDTO.setId(dto.getId());
+                    productDTO.setBarCode(attrValue.getBarCode());
+                    productDTO.setPrice(attrValue.getPrice());
+                    productDTO.setCount(dto.getCount());
+                    productDTO.setSku(attrValue.getSku());
+                    productDTO.setImage(attrValue.getImage());
+                    productDTO.setProductName(product.getProductName());
+                    productList.add(productDTO);
+                }
+
+            }
+
+        }
+        storeProductPackage.setProductList(productList);
+        return R.ok().put("data",storeProductPackage);
+    }
+
+}

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

@@ -0,0 +1,62 @@
+package com.fs.app.controller;
+
+
+import com.fs.wx.cp.config.WxCpConfiguration;
+import io.swagger.annotations.ApiOperation;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.WxCpExternalContactService;
+import me.chanjar.weixin.cp.api.WxCpMessageService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
+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;
+
+import java.util.List;
+
+
+@RestController
+@RequestMapping(value="/")
+public class TestController extends AppBaseController {
+
+
+	@Autowired
+	private WxCpConfiguration wxCpConfiguration;
+
+	@ApiOperation("获取数据字典")
+	@GetMapping("/WW_verify_vFu5z0l4wIXrSMbr.txt")
+	public String getDicts(
+	){
+
+		return "vFu5z0l4wIXrSMbr";
+
+	}
+
+	@ApiOperation("test")
+	@GetMapping("/sendTest")
+	public WxCpMessageSendResult sendTest(
+	) throws WxErrorException {
+		WxCpService cpService = wxCpConfiguration.getCpService(1000002);
+		WxCpExternalContactService externalContactService=cpService.getExternalContactService();
+		List<String> list=externalContactService.listExternalContacts("YuZhongXin");
+		WxCpMessage message=new WxCpMessage();
+		message.setAgentId(1000002);
+		message.setToUser("YuZhongXin");
+		message.setContent("test");
+		message.setMsgType("text");
+		WxCpMessageService messageService=cpService.getMessageService();
+		cpService.getExternalContactService();
+		WxCpMessageSendResult data=messageService.send(message);
+		return data;
+
+	}
+
+
+
+
+
+
+
+}

+ 650 - 162
fs-company-app/src/main/java/com/fs/app/controller/UserController.java

@@ -1,195 +1,683 @@
 package com.fs.app.controller;
 
-
-import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.lang.Validator;
 import cn.hutool.json.JSONUtil;
-import com.fs.ai.service.IBaiduAIService;
-import com.fs.ai.vo.BaiduAIMsgResultVO;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.app.annotation.Login;
-import com.fs.app.param.AICallChatParam;
-import com.fs.app.param.EditChatMsgParam;
 import com.fs.app.param.LoginParam;
+import com.fs.app.utils.JwtUtils;
+import com.fs.app.vo.CompanyRoleVO;
 import com.fs.app.vo.UserListVO;
+import com.fs.app.vo.UserPostVO;
 import com.fs.app.vo.UserVO;
-import com.fs.chat.config.WxConfig;
-import com.fs.chat.domain.ChatMsg;
-import com.fs.chat.domain.ChatMsgLogs;
-import com.fs.chat.domain.ChatRole;
-import com.fs.chat.domain.ChatSession;
-import com.fs.chat.param.ChatMsgListAParam;
-import com.fs.chat.service.IChatMsgLogsService;
-import com.fs.chat.service.IChatMsgService;
-import com.fs.chat.service.IChatRoleService;
-import com.fs.chat.service.IChatSessionService;
-import com.fs.chat.vo.ChatMsgListAVO;
-import com.fs.common.annotation.Log;
-import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.config.FSConfig;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.exception.base.BaseException;
+import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.PinYinUtil;
-import com.fs.common.utils.SecurityUtils;
-import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.sign.Md5Utils;
-import com.fs.common.utils.spring.SpringUtils;
+import com.fs.common.utils.bean.BeanUtils;
+import com.fs.common.utils.http.HttpUtils;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
-import com.fs.company.service.ICompanyPostService;
-import com.fs.company.service.ICompanyUserService;
+import com.fs.company.domain.CompanyUserCard;
+import com.fs.company.param.CompanyUserCardQueryParam;
+import com.fs.company.param.EditPwdParam;
+import com.fs.company.param.EditUserInfoParam;
+import com.fs.company.param.EditUserQrCodeParam;
+import com.fs.company.service.*;
+import com.fs.company.vo.CompanyUserAppVO;
 import com.fs.company.vo.CompanyUserVO;
+import com.fs.core.security.SecurityUtils;
+import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.his.service.IFsUserService;
+import com.fs.wx.miniapp.config.WxMaProperties;
 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;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
-import static com.fs.common.constant.FsConstants.REDIS_CHAT_SESSION;
-
+import static com.fs.common.constant.Constants.PAGE_SIZE;
 
+@Slf4j
+@Api("个人中心")
 @RestController
-@RequestMapping(value="/app/user")
+@RequestMapping(value = "/app/user")
 public class UserController extends AppBaseController {
-	@Autowired
-	ICompanyPostService postService;
-	@Autowired
-	IChatRoleService chatRoleService;
-	@Autowired
-	ICompanyUserService companyUserService;
-
-	@Autowired
-	RedisCache redisCache;
-	@PostMapping("/login")
-	public R login(@RequestBody LoginParam param)
-	{
-		CompanyUser user=companyUserService.selectUserByUserName(param.getAccount());
-		if(user==null){
-			return R.error("帐号不存在");
-		}
-		if(!user.getStatus().equals("0")){
-			return R.error("帐号已被禁用");
-		}
-		if(!SecurityUtils.matchesPassword(param.getPassword(),user.getPassword())){
-			return R.error("密码不正确");
-		}
-		String token = jwtUtils.generateToken(user.getUserId());
-//		redisCache.setCacheObject("doctorToken:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
-		redisCache.setCacheObject("AiChatToken:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
-
-		Map<String,Object> map=new HashMap<>();
-		map.put("token",token);
-		map.put("user",user);
-
-		return R.ok().put("data",map);
-	}
-
-
- 	@Login
-	@GetMapping("getCompanyUser")
-	public R getCompanyUser()
-	{
-		CompanyUser user = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-		return R.ok().put("data",user);
-	}
-
-	@ApiOperation("检测是否登录")
-	@GetMapping("/checkLogin")
-	public R checkLogin(HttpServletRequest request){
-		if(StringUtils.isEmpty(getUserId())){
-			//未登录
-			return R.error("未登录");
-		}
-		else{
-			//登录
-			String token = jwtUtils.generateToken(Long.parseLong(getUserId()));
-			Map<String,Object> map=new HashMap<>();
-			map.put("token",token);
-			return R.ok("认证成功").put("userId",getUserId()).put("token",token);
-		}
-	}
-
-	@Login
-	@ApiOperation("获取通讯录")
-	@GetMapping("/getAllUsers")
-	public R getAllUsers(HttpServletRequest request, @ApiParam(required = false, name = "searchKey", value = "searchKey") @RequestParam(value = "searchKey", required = false) String searchKey){
-		CompanyUser user=new CompanyUser();
-		user.setNickName(searchKey);
-		CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-		user.setCompanyId(companyUser.getCompanyId());
-		user.setIsDel(0);
-		List<CompanyUserVO> list = companyUserService.selectMyUserList(user);
-		List<UserVO> users=new ArrayList<>();
-		for(CompanyUserVO u:list){
-			UserVO vo=new UserVO();
-			vo.setDeptName(u.getDeptName());
-			vo.setFirstLetter(PinYinUtil.cn2py(u.getNickName().substring(0,1)));
-			vo.setNickName(u.getNickName());
-			vo.setUserId(u.getUserId());
-			vo.setAvatar(u.getAvatar());
-			users.add(vo);
-		}
-		Map<Object, List<UserVO>> res = users.parallelStream().collect(
-				Collectors.groupingBy(
-						item->{return Character.toUpperCase(item.getFirstLetter().charAt(0));},//根据首字母分组
-						TreeMap::new,//有序map实现排序
-						Collectors.toList()
-				)
-		);
-		List<UserListVO> vos=res.entrySet().stream().map(e -> new UserListVO(e.getKey().toString(),e.getValue())).collect(Collectors.toList());
-		return R.ok( ).put("users",vos);
-	}
-
-	/**
-	 * 获取用户信息
-	 * @param request
-	 * @return
-	 */
-	@Login
-	@ApiOperation("获取用户信息")
-	@GetMapping("/getUserInfo")
-	public R getUserInfo(HttpServletRequest request){
-		try {
-			CompanyUser companyUser=companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
-			List<String> postList=postService.selectPostNameListByUserId(Long.parseLong(getUserId()));
-			if(companyUser==null){
-				return R.error(40001,"用户不存在");
-			}
-			if(companyUser.getStatus().equals("1")){
-				return R.error(40002,"用户已停用");
-			}
-			return R.ok().put("user",companyUser).put("post",postList);
-		} catch (Exception e){
-
-			return R.error("操作异常");
-		}
-	}
-	@Login
-	@ApiOperation("获取用户信息ByUserId")
-	@GetMapping("/getUserInfoByUserId")
-	public R getUserInfoByUserId(
-			@ApiParam(required = true, name = "userId", value = "用户ID") @RequestParam(value = "userId", required = false) Long userId,
-			HttpServletRequest request){
-		try {
-			CompanyUser user=companyUserService.selectCompanyUserById(userId);
-			List<String> postList=postService.selectPostNameListByUserId(userId);
-			if(user==null){
-				return R.error(40001,"用户不存在");
-			}
-			if(user.getStatus().equals("1")){
-				return R.error(40002,"用户已停用");
-			}
-			return R.ok().put("user",user).put("post",postList);
-		} catch (Exception e){
-
-			return R.error("操作异常");
-		}
-	}
 
+    @Autowired
+    private FSConfig fsConfig;
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    ICompanyUserService userService;
+    @Autowired
+    ICompanyPostService postService;
+    @Autowired
+    ICompanyMenuService menuService;
+    @Autowired
+    private ICompanyService companyService;
+    @Autowired
+    RedisCache redisCache;
+    @Autowired
+    ICompanyUserService companyUserService;
+
+    @Autowired
+    ICompanyUserCardService companyUserCardService;
+    @Autowired
+    private IFsUserService fsUserService;
+    @Autowired
+    private IFsCourseRedPacketLogService courseRedPacketLogService;
+
+    @Autowired
+    private WxMaProperties properties;
+    private String aifabuAppId = "7b471be905ab17e00f3b858c6710dd117601d008";
+
+//	@PostMapping("/loginByPwd")
+//	@ApiOperation("密码登录")
+//	public R loginByPwd(@Validated @RequestBody LoginParam param){
+//		try {
+//
+//			CompanyUser companyUser=userService.selectUserByUserName(param.getUserName());
+//			if(companyUser==null){
+//				return R.error("工号不存在");
+//			}
+//			if(companyUser.getStatus().equals("1")){
+//				return R.error("用户已禁用");
+//			}
+//			if (!SecurityUtils.matchesPassword(param.getPassword(), companyUser.getPassword())) {
+//				return R.error("密码错误");
+//			}
+//			Company company=companyService.selectCompanyById(companyUser.getCompanyId()) ;
+//			if(company==null||company.getStatus()==0||company.getIsDel()==1){
+//				throw new BaseException("此用户所属公司不存在或已停用");
+//			}
+//			if(StringUtils.isNotEmpty(param.getJpushId())){
+//				companyUser.setJpushId(param.getJpushId());
+//				userService.updateUserProfile(companyUser);
+//			}
+//			//生成token
+//			String token = jwtUtils.generateToken(companyUser.getUserId());
+//			//调用REDIS生成用户的TOKEN
+//
+//			//redisCache.setCacheObject("token:"+companyUser.getUserId(),token,2592000, TimeUnit.SECONDS);
+//			//redisCache.setCacheObject("companyId:"+companyUser.getUserId(),companyUser.getCompanyId(),2592000, TimeUnit.SECONDS);
+//
+//			redisCache.setCacheObject("token:"+companyUser.getUserId(),token,604800, TimeUnit.SECONDS);
+//			redisCache.setCacheObject("companyId:"+companyUser.getUserId(),companyUser.getCompanyId(),604800, TimeUnit.SECONDS);
+//
+//			//获取权限
+//			Set<String> perms = new HashSet<String>();
+//			// 管理员拥有所有权限
+//			if (companyUser.isAdmin())
+//			{
+//				perms.add("*:*:*");
+//			}
+//			else
+//			{
+//				perms.addAll(menuService.selectMenuPermsByUserId(companyUser.getUserId()));
+//			}
+//
+//			//redisCache.setCacheObject("perms:"+companyUser.getUserId(), JSONUtil.toJsonStr(perms),2592000, TimeUnit.SECONDS);
+//			redisCache.setCacheObject("perms:"+companyUser.getUserId(), JSONUtil.toJsonStr(perms),604800, TimeUnit.SECONDS);
+//
+//			Map<String ,Object> result=new HashMap<>();
+//			result.put("token",token);
+//			result.put("user",companyUser);
+//			result.put("perms",perms);
+//			return R.ok("登录成功").put("data",result);
+//		} catch (Exception e) {
+//			return R.error("登录异常");
+//		}
+//	}
+
+    @PostMapping("/login")
+    @ApiOperation("密码登录")
+    public R login(@Validated @RequestBody LoginParam param) {
+
+        // 密码校验
+        if (!PatternUtils.checkPassword(param.getPassword())) {
+            return R.error("密码格式不正确,需包含字母、数字和特殊字符,长度为 8-20位");
+        }
+
+        try {
+            //判断用户基本规则
+            CompanyUser companyUser = userService.selectUserByUserName(param.getAccount());
+            if (companyUser == null) {
+                return R.error("工号不存在");
+            }
+            if (companyUser.getStatus().equals("1")) {
+                return R.error("用户已禁用");
+            }
+            if (!SecurityUtils.matchesPassword(param.getPassword(), companyUser.getPassword())) {
+                return R.error("密码错误");
+            }
+            if (companyUser.getIsAudit() == 0) {
+                return R.error("用户未审核");
+            }
+
+            Company company = companyService.selectCompanyById(companyUser.getCompanyId());
+            if (company == null || company.getStatus() == 0 || company.getIsDel() == 1) {
+                throw new BaseException("此用户所属公司不存在或已停用");
+            }
+
+            // 公司名称
+            companyUser.setCompanyName(company.getCompanyName());
+            // 岗位
+            companyUser.setPosts(postService.selectCompanyPostListByUserId(companyUser.getUserId()));
+
+            if (StringUtils.isNotEmpty(param.getJpushId())) {
+                companyUser.setJpushId(param.getJpushId());
+                userService.updateUserProfile(companyUser);
+            }
+            //生成token
+            String token = jwtUtils.generateToken(companyUser.getUserId());
+            //调用REDIS生成用户的TOKEN
+
+            redisCache.setCacheObject("token:" + companyUser.getUserId(), token, 604800, TimeUnit.SECONDS);
+            redisCache.setCacheObject("companyId:" + companyUser.getUserId(), companyUser.getCompanyId(), 604800, TimeUnit.SECONDS);
+
+            //获取权限
+            Set<String> perms = new HashSet<String>();
+            // 管理员拥有所有权限
+            if (companyUser.isAdmin()) {
+                perms.add("*:*:*");
+            } else {
+                perms.addAll(menuService.selectMenuPermsByUserId(companyUser.getUserId()));
+            }
+
+            //redisCache.setCacheObject("perms:"+companyUser.getUserId(), JSONUtil.toJsonStr(perms),2592000, TimeUnit.SECONDS);
+            redisCache.setCacheObject("perms:" + companyUser.getUserId(), JSONUtil.toJsonStr(perms), 604800, TimeUnit.SECONDS);
+
+            Map<String, Object> result = new HashMap<>();
+            result.put("token", token);
+            result.put("user", companyUser);
+            result.put("perms", perms);
+            return R.ok("登录成功").put("data", result);
+        } catch (Exception e) {
+            return R.error("登录异常");
+        }
+    }
+
+//    /**
+//     * 获取用户信息
+//     *
+//     * @param request
+//     * @return
+//     */
+//    @Login
+//    @ApiOperation("获取用户信息")
+//    @GetMapping("/getUserInfo")
+//    public R getUserInfo(HttpServletRequest request) {
+//        try {
+//            CompanyUser companyUser = userService.selectCompanyUserById(Long.parseLong(getUserId()));
+//            List<String> postList = postService.selectPostNameListByUserId(Long.parseLong(getUserId()));
+//            if (companyUser == null) {
+//                return R.error(40001, "用户不存在");
+//            }
+//            if (companyUser.getStatus().equals("1")) {
+//                return R.error(40002, "用户已停用");
+//            }
+//            return R.ok().put("user", companyUser).put("post", postList);
+//        } catch (Exception e) {
+//
+//            return R.error("操作异常");
+//        }
+//    }
+
+    @Login
+    @ApiOperation("获取用户信息")
+    @GetMapping("/getCompanyUser")
+    public R getCompanyUser() {
+        try {
+            CompanyUser companyUser = userService.selectCompanyUserById(Long.parseLong(getUserId()));
+            if (companyUser == null) {
+                return R.error(40001, "用户不存在");
+            }
+            if (companyUser.getStatus().equals("1")) {
+                return R.error(40002, "用户已停用");
+            }
+            // 公司名称
+            Company company = companyService.selectCompanyById(companyUser.getCompanyId());
+            companyUser.setCompanyName(company.getCompanyName());
+            // 岗位
+            companyUser.setPosts(postService.selectCompanyPostListByUserId(companyUser.getUserId()));
+            return R.ok().put("user", companyUser);
+        } catch (Exception e) {
+
+            return R.error("操作异常");
+        }
+    }
+
+    @Login
+    @ApiOperation("检测是否登录")
+    @GetMapping("/checkLogin")
+    public R checkLogin(HttpServletRequest request) {
+        return R.ok("认证成功");
+    }
+
+
+    @Login
+    @ApiOperation("获取通讯录")
+    @GetMapping("/getAllUsers")
+    public R getAllUsers(HttpServletRequest request, @ApiParam(required = false, name = "searchKey", value = "searchKey") @RequestParam(value = "searchKey", required = false) String searchKey,
+                         @RequestParam Integer isAudit) {
+        log.debug("获取通讯录 searchKey:{}, isAudit: {}", searchKey, isAudit);
+        CompanyUser user = new CompanyUser();
+        user.setNickName(searchKey);
+        user.setCompanyId(getCompanyId());
+        user.setIsAudit(isAudit);
+        user.setIsDel(0);
+        List<CompanyUserVO> list = userService.selectMyUserList(user);
+        List<UserVO> users = new ArrayList<>();
+        for (CompanyUserVO u : list) {
+            UserVO vo = new UserVO();
+            if (Objects.nonNull(u.getDept())) {
+                vo.setDeptId(u.getDept().getDeptId());
+                vo.setDeptName(u.getDept().getDeptName());
+            }
+            vo.setFirstLetter(PinYinUtil.cn2py(u.getNickName().substring(0, 1)));
+            vo.setNickName(u.getNickName());
+            vo.setUserId(u.getUserId());
+            vo.setAvatar(u.getAvatar());
+            if (Objects.nonNull(u.getCreateTime())) {
+                vo.setRegisterTime(u.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
+            }
+            vo.setStatus(u.getStatus());
+            vo.setPhoneNumber(u.getPhonenumber());
+            vo.setIsAudit(u.getIsAudit());
+
+            // 岗位信息
+            if (Objects.nonNull(u.getPosts())) {
+                vo.setPost(u.getPosts().stream().map(p -> {
+                    UserPostVO post = new UserPostVO();
+                    post.setPostId(p.getPostId());
+                    post.setPostName(p.getPostName());
+                    return post;
+                }).collect(Collectors.toList()));
+            }
+
+            // 角色信息
+            if (Objects.nonNull(u.getRoles())) {
+                vo.setRoles(u.getRoles().stream().map(r -> {
+                    CompanyRoleVO role = new CompanyRoleVO();
+                    role.setRoleId(r.getRoleId());
+                    role.setRoleName(r.getRoleName());
+                    return role;
+                }).collect(Collectors.toList()));
+            }
+
+            users.add(vo);
+        }
+        Map<Object, List<UserVO>> res = users.parallelStream().collect(
+                Collectors.groupingBy(
+                        item -> {
+                            return Character.toUpperCase(item.getFirstLetter().charAt(0));
+                        },//根据首字母分组
+                        TreeMap::new,//有序map实现排序
+                        Collectors.toList()
+                )
+        );
+        List<UserListVO> vos = res.entrySet().stream().map(e -> new UserListVO(e.getKey().toString(), e.getValue())).collect(Collectors.toList());
+        return R.ok().put("users", vos);
+    }
+
+//    @Login
+    @ApiOperation("获取用户信息ByUserId")
+    @GetMapping("/getUserInfoByUserId")
+    public R getUserInfoByUserId(
+            @ApiParam(required = true, name = "userId", value = "用户ID") @RequestParam(value = "userId", required = false) Long userId,
+            HttpServletRequest request) {
+        try {
+            log.debug("获取用户信息ByUserId userId:{}", userId);
+            CompanyUser user = userService.selectCompanyUserById(userId);
+            List<String> postList = postService.selectPostNameListByUserId(userId);
+            if (user == null) {
+                return R.error(40001, "用户不存在");
+            }
+            if (user.getStatus().equals("1")) {
+                return R.error(40002, "用户已停用");
+            }
+
+
+            // 查询用户会员数、今日新增用户数
+            Map<String, Long> result = fsUserService.getUserVipCountByCompanyUserId(userId);
+            // 红包数
+            int redPackCount = courseRedPacketLogService.getCountByCompanyUserIdId(userId);
+            // 新用户红包金额
+            BigDecimal newVipRedPackAmount = courseRedPacketLogService.getNewVipRedPackAmountByCompanyUserIdId(userId);
+
+            CompanyUserAppVO userVO = new CompanyUserAppVO();
+            BeanUtils.copyProperties(user, userVO);
+
+            userVO.setVipCount(result.getOrDefault("vipCount", 0L));
+            userVO.setNewVipCount(result.getOrDefault("newVipCount", 0L));
+            userVO.setRedPacketCount(redPackCount);
+            userVO.setNewVipRedPackAmount(newVipRedPackAmount);
+
+            return R.ok().put("user", userVO).put("post", postList);
+        } catch (Exception e) {
+
+            return R.error("操作异常");
+        }
+    }
+
+    @Login
+    @ApiOperation("修改密码")
+    @PostMapping("/setPwd")
+    public R setPwd(HttpServletRequest request, @RequestBody EditPwdParam param) {
+        try {
+
+            CompanyUser user = userService.selectCompanyUserById(Long.parseLong(getUserId()));
+            if (!SecurityUtils.matchesPassword(param.getOldPassword(), user.getPassword())) {
+                return R.error("旧密码错误");
+            }
+            if (SecurityUtils.matchesPassword(param.getOldPassword(), param.getPassword())) {
+                return R.error("新密码不能与旧密码相同");
+            }
+            if (userService.resetUserPwd(user.getUserName(), SecurityUtils.encryptPassword(param.getPassword())) > 0) {
+                return R.ok("密码修改成功");
+            } else {
+                return R.error("密码修改失败");
+            }
+        } catch (Exception e) {
+
+            return R.error("操作异常" + e.getMessage());
+        }
+    }
+
+    /**
+     * 修改头像
+     *
+     * @param request
+     * @param headImg
+     * @return
+     */
+    @Login
+    @ApiOperation("修改头像")
+    @PostMapping("/setHeadImg")
+    public R setHeadImg(HttpServletRequest request,
+                        @ApiParam(required = true, name = "headImg", value = "用户头像") @RequestParam(value = "headImg", required = false) String headImg) {
+        try {
+            CompanyUser user = userService.selectCompanyUserById(Long.parseLong(getUserId()));
+            if (user == null) {
+                return R.error("用户不存在");
+            }
+            if (user.getStatus().equals("1")) {
+                return R.error("用户已停用");
+            }
+            CompanyUser map = new CompanyUser();
+            map.setUserId(user.getUserId());
+            if (userService.updateUserAvatar(user.getUserName(), headImg) > 0) {
+                return R.ok("修改成功");
+            } else {
+                return R.error("修改失败");
+            }
+        } catch (Exception e) {
+
+            return R.error("操作异常");
+        }
+    }
+
+
+    @Login
+    @ApiOperation("修改用户信息")
+    @PostMapping("/setUserInfo")
+    public R setUserInfo(HttpServletRequest request, @RequestBody EditUserInfoParam param) {
+        try {
+
+            CompanyUser user = userService.selectCompanyUserById(Long.parseLong(getUserId()));
+            if (user == null) {
+                return R.error("用户不存在");
+            }
+            if (user.getStatus().equals("1")) {
+                return R.error("用户已停用");
+            }
+            if (param == null) {
+                return R.error("修改参数对象不能为空");
+            }
+            if (!Validator.isMobile(param.getMobile())) {
+                return R.error("手机号不正确");
+            }
+            if (!Validator.isEmail(param.getEmail())) {
+                return R.error("邮箱格式不正确");
+            }
+            user.setAvatar(param.getHeadImg());
+            user.setPhonenumber(param.getMobile());
+            user.setEmail(param.getEmail());
+            user.setSex(param.getSex());
+            user.setNickName(param.getNickName());
+            user.setQrCodeWeixin(param.getQrCodeWeixin());
+            if (userService.updateUserProfile(user) > 0) {
+                return R.ok("修改成功");
+            } else {
+                return R.error("修改失败");
+            }
+        } catch (Exception e) {
+            return R.error("操作异常" + e.getMessage());
+        }
+    }
+
+    @Login
+    @ApiOperation("修改二维码")
+    @PostMapping("/editQrCode")
+    @RepeatSubmit
+    public R editQrCode(HttpServletRequest request, @RequestBody EditUserQrCodeParam param) {
+        try {
+            CompanyUser user = userService.selectCompanyUserById(Long.parseLong(getUserId()));
+            if (user == null) {
+                return R.error("用户不存在");
+            }
+            if (user.getStatus().equals("1")) {
+                return R.error("用户已禁用");
+            }
+            //个人微信
+            CompanyUserCard companyUserCard = null;
+            if (param.getCardId() == null) {   //添加名片
+                companyUserCard = new CompanyUserCard();
+                companyUserCard.setUserId(Long.parseLong(getUserId()));
+                companyUserCard.setCompanyId(getCompanyId());
+                companyUserCard.setTitle(param.getTitle());
+                companyUserCard.setCreateTime(new Date());
+                companyUserCard.setType((long) param.getType());
+                companyUserCard.setImageUrl(param.getQrCodeWeixin());
+                int rows = companyUserCardService.insertCompanyUserCard(companyUserCard);
+                if (rows > 0) {
+                    String shorUrl = this.getShortUrlAction(companyUserCard.getId(), param.getTitle());
+                    companyUserCard.setShortUrl(shorUrl);
+                    if (companyUserCardService.updateUserCardShortUrlById(companyUserCard.getId(), shorUrl) > 0) {
+                        return R.ok("修改成功");
+                    }
+                }
+                return R.error("修改失败");
+            } else {
+                companyUserCard = companyUserCardService.selectCompanyUserCardById(param.getCardId());
+                companyUserCard.setImageUrl(param.getQrCodeWeixin());
+                companyUserCard.setUpdateTime(new Date());
+                if (companyUserCardService.updateCompanyUserCard(companyUserCard) > 0) {
+                    return R.ok("修改成功");
+                } else {
+                    return R.error("修改失败");
+                }
+            }
+
+        } catch (Exception e) {
+            return R.error("操作异常" + e.getMessage());
+        }
+    }
+
+    @Login
+    @ApiOperation("获取我的名片列表")
+    @GetMapping("/getMyCardList")
+    public R getMyCardList(CompanyUserCardQueryParam param) {
+        PageHelper.startPage(param.getPage(), PAGE_SIZE);
+        param.setCompanyId(getCompanyId());
+        param.setCompanyUserId(Long.parseLong(getUserId()));
+        List<CompanyUserCard> list = companyUserCardService.selectMyCardListQuery(param);
+        PageInfo<CompanyUserCard> listPageInfo = new PageInfo<>(list);
+        return R.ok().put("data", listPageInfo);
+    }
+
+
+    @Login
+    @ApiOperation("删除名片")
+    @PostMapping("/delMyCard")
+    public R delMyCard(@RequestParam("cardId") Long cardId) {
+        try {
+            CompanyUserCard companyUserCard = companyUserCardService.selectCompanyUserCardById(cardId);
+            String shortUrl = companyUserCard.getShortUrl();
+            String[] chainArr = shortUrl.split(".cn/");
+            String chain = "";
+            if (chainArr.length == 2) {
+                chain = chainArr[1];
+            }
+            if (StringUtils.isNotEmpty(chain)) {
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("apikey", aifabuAppId);
+                String[] chains = new String[1];
+                chains[0] = chain;
+                jsonObject.put("chains", "[" + chain + "]");
+                String paramStr = jsonObject.toJSONString();
+                String postUrl = "https://openapi.aifabu.com/v1/chain/delChain?apikey=" + aifabuAppId + "&chains=" + "[" + chain + "]";
+                String postStr = HttpUtils.sendPost(postUrl, paramStr);
+                JSONObject obj = JSONObject.parseObject(postStr);
+//				Integer code= obj.getInteger("code");
+//				if(code==1){
+//
+//				}
+            }
+            if (companyUserCardService.deleteCompanyUserCardById(cardId) > 0) {
+                return R.ok("操作成功");
+            } else {
+                return R.error("操作失败");
+            }
+        } catch (Exception e) {
+            return R.error("操作异常" + e.getMessage());
+        }
+    }
+
+
+    @ApiOperation("查询微信urlScheme")
+    @GetMapping("/getAppletScheme")
+    public R getAppletScheme(@RequestParam(value = "cardId") Long cardId) {
+        try {
+            String appId = properties.getConfigs().get(0).getAppid();
+            String secret = properties.getConfigs().get(0).getSecret();
+            String rspStr = HttpUtils.sendGet("https://api.weixin.qq.com/cgi-bin/token", "grant_type=client_credential&" + "appid=" + appId + "&secret=" + secret);
+            JSONObject obj = JSONObject.parseObject(rspStr);
+            String access_token = obj.getString("access_token");
+            JSONObject jsonObject = new JSONObject();
+            JSONObject jump_wxaObj = new JSONObject();
+            jump_wxaObj.put("path", "/pages_company/card");
+            jump_wxaObj.put("query", "id=" + cardId);
+            jsonObject.put("jump_wxa", jump_wxaObj);
+            jsonObject.put("is_expire", false);
+            String paramStr = jsonObject.toJSONString();
+            String postStr = HttpUtils.sendPost("https://api.weixin.qq.com/wxa/generatescheme?access_token=" + access_token, paramStr);
+            obj = JSONObject.parseObject(postStr);
+            //response.addHeader("Access-Control-Allow-Origin", "*");
+            return R.ok().put("result", obj);
+        } catch (Exception e) {
+            return R.error("操作失败");
+        }
+    }
+
+
+    private String getShortUrlAction(Long cardId, String title) {
+        try {
+
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("apikey", aifabuAppId);
+            String url = "http://company.yjf.runtzh.com/wx.html?id=" + cardId;
+            jsonObject.put("target_url", url);
+            jsonObject.put("group_id", 308195);
+            if (StringUtils.isNotEmpty(title)) {
+                jsonObject.put("chain_title", title);
+            }
+            String paramStr = jsonObject.toJSONString();
+            String postStr = HttpUtils.sendPost("https://openapi.aifabu.com/v1/chain/createChain", paramStr);
+            JSONObject obj = JSONObject.parseObject(postStr);
+            Integer code = obj.getInteger("code");
+            String shortUrl = "";
+            if (code == 1) {
+                JSONObject result = obj.getJSONObject("result");
+                shortUrl = result.getString("render_url");
+            }
+            return shortUrl;
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    @Login
+    @ApiOperation("获取短链接")
+    @GetMapping("/getShortUrl")
+    public R getShortUrl(@RequestParam(value = "url") String url, @RequestParam(value = "title") String title) {
+
+        try {
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("apikey", aifabuAppId);
+            jsonObject.put("target_url", url);
+            jsonObject.put("group_id", 308195);
+            if (StringUtils.isNotEmpty(title)) {
+                jsonObject.put("chain_title", title);
+            }
+            String paramStr = jsonObject.toJSONString();
+            String postStr = HttpUtils.sendPost("https://openapi.aifabu.com/v1/chain/createChain", paramStr);
+            JSONObject obj = JSONObject.parseObject(postStr);
+            return R.ok().put("data", obj);
+        } catch (Exception e) {
+            return R.error("操作失败");
+        }
+    }
+
+    @GetMapping("/getUserList")
+    public R getUserList() {
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+        CompanyUser map = new CompanyUser();
+        map.setCompanyId(companyUser.getCompanyId());
+        List<CompanyUser> list = companyUserService.selectCompanyUserList(map);
+        return R.ok().put("data", list);
+    }
+
+    @Login
+    @ApiOperation("修改用户状态")
+    @PostMapping("/changeUserState")
+    public R changeUserState(@RequestParam Long userId) {
+        log.debug("修改用户状态 userId :{}", userId);
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(userId);
+        if (Objects.isNull(companyUser)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        String state = "0".equals(companyUser.getStatus()) ? "1" : "0";
+        companyUser.setStatus(state);
+        companyUserService.updateCompanyUser(companyUser);
+        return R.ok();
+    }
+
+    @Login
+    @ApiOperation("批量审核用户")
+    @PostMapping("/audit")
+    public R auditUser(@RequestParam List<Long> userIds) {
+        log.debug("批量审核用户 userIds :{}", userIds);
+        companyUserService.auditUsers(userIds);
+        return R.ok();
+    }
 
 }

+ 206 - 0
fs-company-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java

@@ -0,0 +1,206 @@
+package com.fs.app.controller;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
+import cn.hutool.core.date.DateTime;
+import com.alibaba.fastjson.JSON;
+import com.fs.app.annotation.Login;
+import com.fs.app.param.LoginMaWxParam;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyDeptService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.core.config.WxMaConfiguration;
+import com.fs.course.config.CourseMaConfig;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import com.fs.wx.miniapp.config.WxMaProperties;
+import io.jsonwebtoken.Claims;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+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.*;
+
+import java.util.Date;
+import java.util.List;
+
+@Api("微信小程序相关接口(暂废弃,后面再删除)")
+@RestController
+@RequestMapping(value = "/app/wx/miniapp")
+@Slf4j
+public class WxCompanyUserController extends AppBaseController {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private WxMaProperties maProperties;
+
+    @Autowired
+    JwtUtils jwtUtils;
+
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    private ICompanyUserService companyUserService;
+
+    @Autowired
+    private ICompanyDeptService companyDeptService;
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+
+    @ApiOperation("小程序-授权登录")
+    @PostMapping("/loginByMa")
+    public R login(@RequestBody LoginMaWxParam 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("小程序配置为空");
+        }
+        CourseMaConfig courseMaConfig = courseMaConfigs.get(0);
+        //获取第二个小程序配置,序号从0开始
+        final WxMaService wxService = WxMaConfiguration.getMaService(courseMaConfig.getAppid());
+        try {
+            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+            this.logger.info(session.getSessionKey());
+            this.logger.info(session.getOpenid());
+            // 解密
+            WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+            WxMaUserInfo userInfo = wxService.getUserService().getUserInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+
+            //以下暂时注释,不需要往销售表添加数据
+//            CompanyUser companyUser = companyUserService.getCompanyUserByOpenId(session.getOpenid());
+//            String ip = IpUtil.getRequestIp();
+//
+////            // 如果公司id为空(表示可能是该公司的第一位销售管理员),则需要根据电话号码判断是否存在销售,如果不存在则提示
+////            if (param.getCompanyId() == null) {
+////                if (checkPhone == null) {
+////                    throw new CustomException("由于不是管理员,不能直接登录", 401);
+////                }
+////            }
+//            if (companyUser == null) {
+//                CompanyUser checkPhone = companyUserService.getCompanyUserByPhone(phoneNoInfo.getPhoneNumber());
+//                if (checkPhone != null) {
+//                    if (checkPhone.getMaOpenId() == null) {
+//                        companyUser = checkPhone;
+//                        companyUser.setMaOpenId(session.getOpenid());
+//                        companyUser.setUserId(companyUser.getUserId());
+//                        companyUser.setUpdateTime(new DateTime());
+//                        companyUser.setLoginIp(ip);
+//                        companyUserService.updateUserProfile(companyUser);
+//                    } else {
+//                        throw new CustomException("此手机号用户已存在");
+//                    }
+//                } else {
+//                    //新增
+//                    companyUser = new CompanyUser();
+//                    companyUser.setUserName(phoneNoInfo.getPhoneNumber());
+//                    companyUser.setNickName(userInfo.getNickName() == null ? "微信用户" : userInfo.getNickName());
+//                    companyUser.setPhonenumber(phoneNoInfo.getPhoneNumber());
+//                    companyUser.setSex(userInfo.getGender());
+//                    //密码初始化为123456
+//                    String pw = "123456";
+//                    companyUser.setPassword(SecurityUtils.encryptPassword(param.getPassword() == null ? pw : param.getPassword()));
+//                    companyUser.setCreateTime(new Date());
+//                    companyUser.setCompanyId(param.getCompanyId());
+//                    companyUser.setParentId(param.getParentCompanyUseId());
+//                    companyUser.setMaOpenId(session.getOpenid());
+//
+//                    //部门信息
+//                    CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(param.getCompanyId());
+//                    if (Objects.nonNull(dept)) {
+//                        companyUser.setDeptId(dept.getDeptId());
+//                    }
+//                    companyUserService.insertUser(companyUser);
+//                }
+//            } else {
+//                CompanyUser companyUserMp = new CompanyUser();
+//                companyUserMp.setPhonenumber(phoneNoInfo.getPhoneNumber());
+//                companyUserMp.setUserId(companyUser.getUserId());
+//                companyUserMp.setUpdateTime(new DateTime());
+//                companyUserMp.setLoginIp(ip);
+//                companyUserService.updateUserProfile(companyUser);
+//            }
+
+            // 添加会员表数据
+            FsUser user = userService.selectFsUserByMpOpenId(session.getOpenid());
+            if (user != null) {
+                //修改
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setMpOpenId(session.getOpenid());
+                userMap.setUnionId(session.getUnionid());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setNickName(userInfo.getNickName() != null ? userInfo.getNickName() : "微信用户");
+                userMap.setAvatar(userInfo.getAvatarUrl() != null ? userInfo.getAvatarUrl() : null);
+                userMap.setPhone(phoneNoInfo.getPhoneNumber());
+                userService.updateFsUser(userMap);
+            } else {
+                //新增
+                user = new FsUser();
+                user.setNickName(userInfo.getNickName() != null ? userInfo.getNickName() : "微信用户");
+                user.setAvatar(userInfo.getAvatarUrl() != null ? userInfo.getAvatarUrl() : null);
+                user.setStatus(1);
+                user.setMpOpenId(session.getOpenid());
+                user.setUnionId(session.getUnionid());
+                user.setCreateTime(new Date());
+                user.setPhone(phoneNoInfo.getPhoneNumber());
+                userService.insertFsUser(user);
+            }
+            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);
+        } catch (WxErrorException e) {
+            this.logger.error(e.getMessage(), e);
+            return R.error("授权失败," + e.getMessage());
+        }
+    }
+
+    @Login(isMiniLogin = true)
+    @ApiOperation("获取销售通过小程序登录后的用户信息")
+    @GetMapping("/getMaUser")
+    public R getUserInfo() {
+        try {
+            CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+            if (companyUser == null) {
+                return R.error(401, "用户信息不存在");
+            }
+            return R.ok().put("user", companyUser);
+        } catch (Exception e) {
+            return R.error("操作异常");
+        }
+    }
+
+    /**
+     * 特殊要求:销售小程序临时登录,登录后页面中还有一个之前常用的登录,所以为了区分,token名称不能跟之前的一样
+     *
+     * @return 用户id
+     */
+    public String getUserId() {
+        String headValue = ServletUtils.getRequest().getHeader("UserToken");
+        Claims claims = jwtUtils.getClaimByToken(headValue);
+        String userId = claims.getSubject().toString();
+        return userId;
+    }
+
+
+}

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

@@ -0,0 +1,134 @@
+package com.fs.app.controller;
+
+import cn.hutool.core.date.DateTime;
+import com.fs.app.param.FsUserLoginByMpParam;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.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;
+
+
+    @ApiOperation("微信授权登录成为会员")
+    @PostMapping("/loginByMp")
+    public R loginByMp(@Valid @RequestBody FsUserLoginByMpParam param) {
+        try {
+            //获取微信用户信息
+            WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+            WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+//            FsUser user = userService.selectFsUserByUnionid(wxMpUser.getUnionId());
+            FsUser user;
+            if(StringUtils.isNotEmpty(wxMpUser.getUnionId())) {
+                user = userService.selectFsUserByUnionId(wxMpUser.getUnionId());
+            } else {
+                user = userService.selectFsUserByMpOpenId(wxMpUser.getOpenid());
+            }
+            if (user != null) {
+                //修改
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setMpOpenId(wxMpUser.getOpenid());
+                userMap.setUnionId(wxMpUser.getUnionId());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setNickName(wxMpUser.getNickname());
+                userMap.setAvatar(wxMpUser.getHeadImgUrl());
+                userService.updateFsUser(userMap);
+            } else {
+                //新增
+                user = new FsUser();
+                user.setNickName(wxMpUser.getNickname());
+                user.setAvatar(wxMpUser.getHeadImgUrl());
+                user.setStatus(1);
+                user.setMpOpenId(wxMpUser.getOpenid());
+                user.setUnionId(wxMpUser.getUnionId());
+                user.setCreateTime(new Date());
+                userService.insertFsUser(user);
+            }
+            log.info("用户信息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());
+            }
+        }
+
+    }
+
+    @ApiOperation("单独返回用户头像和昵称的接口,不保存用户数据")
+    @PostMapping("/userInfo")
+    public R mpGetUserInfo(@Valid @RequestBody FsUserLoginByMpParam param) {
+        try {
+            //获取微信用户信息
+            WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+            WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+            String nickname = wxMpUser.getNickname();
+            String headImgUrl = wxMpUser.getHeadImgUrl();
+            log.info("仅仅获取用户昵称和头像,nickname: {},headImgUrl:{}", nickname, headImgUrl);
+            Map<String, Object> map = new HashMap<>();
+            map.put("nickname", nickname);
+            map.put("headImgUrl", headImgUrl);
+            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());
+            }
+        }
+
+    }
+
+
+}

+ 44 - 0
fs-company-app/src/main/java/com/fs/app/controller/WxMpController.java

@@ -0,0 +1,44 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.domain.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.Synchronized;
+import me.chanjar.weixin.common.bean.WxJsapiSignature;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+
+@RestController
+@Api("微信配置")
+@RequestMapping("/app/wx/mp")
+public class WxMpController {
+
+    @Autowired
+    private WxMpService wxMpService;
+
+
+    @GetMapping("/getWeiXinConfig")
+    @ApiOperation("微信url验证")
+    @Synchronized
+    public R getWxConfig(@RequestParam String url) throws WxErrorException {
+        try {
+            String sLink = URLDecoder.decode(url, "UTF-8");
+            final WxJsapiSignature jsapiSignature = wxMpService.createJsapiSignature(sLink);
+            return R.ok().put("data", jsapiSignature);
+        } catch (UnsupportedEncodingException e) {
+            // URL解码异常
+            return R.error(e.getMessage());
+        }
+    }
+
+
+}

+ 26 - 0
fs-company-app/src/main/java/com/fs/app/param/ChangeUserDeptAndPostParam.java

@@ -0,0 +1,26 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class ChangeUserDeptAndPostParam {
+    /**
+     * 销售ID
+     */
+    @NotBlank(message = "销售ID不能为空")
+    private String companyUserIds;
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+    /**
+     * 岗位ID
+     */
+    private String postIds;
+    /**
+     * 角色ID
+     */
+    private String roleIds;
+}

+ 29 - 0
fs-company-app/src/main/java/com/fs/app/param/CompanyUserChangeApplyParam.java

@@ -0,0 +1,29 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+public class CompanyUserChangeApplyParam {
+    /**
+     * 原归属销售
+     */
+    @NotNull(message = "原归属销售ID不能为空")
+    private Long from;
+    /**
+     * 申请归属销售
+     */
+    @NotNull(message = "申请更换归属销售ID不能为空")
+    private Long to;
+    /**
+     * 类型  0全部 1部分
+     */
+    @NotNull(message = "类型不能为空")
+    private Integer type;
+    /**
+     * 需更换归属会员id集合
+     */
+    private List<Long> ids;
+}

+ 32 - 0
fs-company-app/src/main/java/com/fs/app/param/CompanyUserParam.java

@@ -0,0 +1,32 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class CompanyUserParam {
+
+    /**
+     * 公司ID
+     */
+    @NotNull(message = "公司ID不能为空")
+    private Long companyId;
+    /**
+     * 手机号码
+     */
+    @NotBlank(message = "手机号码不能为空")
+    private String phoneNumber;
+    /**
+     * 昵称
+     */
+    @NotBlank(message = "昵称不能为空")
+    private String nickName;
+    /**
+     * 密码
+     */
+    @NotBlank(message = "用户密码不能为空")
+    private String password;
+
+}

+ 27 - 0
fs-company-app/src/main/java/com/fs/app/param/CompanyUserUpdateParam.java

@@ -0,0 +1,27 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class CompanyUserUpdateParam {
+
+    /**
+     * 用户ID
+     */
+    @NotNull(message = "用户ID不能为空")
+    private Long userId;
+    /**
+     * 昵称
+     */
+    private String nickName;
+    /**
+     * 电话号码
+     */
+    private String phoneNumber;
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 13 - 0
fs-company-app/src/main/java/com/fs/app/param/CrmDoReadMsgParam.java

@@ -0,0 +1,13 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class CrmDoReadMsgParam implements Serializable {
+
+
+    private Integer msgType;
+
+}

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

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

+ 22 - 0
fs-company-app/src/main/java/com/fs/app/param/FsUserTagUpdateParam.java

@@ -0,0 +1,22 @@
+package com.fs.app.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+@Data
+public class FsUserTagUpdateParam {
+    /**
+     * 用户ID
+     */
+    @ApiModelProperty("用户ID集合")
+    @NotEmpty(message = "用户ID不能为空")
+    private List<Long> fsUserIds;
+    /**
+     * 标签ID
+     */
+    @ApiModelProperty("标签ID集合")
+    private List<Long> tagIds;
+}

+ 26 - 0
fs-company-app/src/main/java/com/fs/app/param/FsUserUpdateParam.java

@@ -0,0 +1,26 @@
+package com.fs.app.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class FsUserUpdateParam {
+    /**
+     * 用户ID
+     */
+    @ApiModelProperty("用户ID")
+    @NotNull(message = "用户ID不能为空")
+    private Long fsUserId;
+    /**
+     * 用户昵称
+     */
+    @ApiModelProperty("用户昵称")
+    private String nickName;
+    /**
+     * 用户备注
+     */
+    @ApiModelProperty("用户备注")
+    private String remark;
+}

+ 34 - 0
fs-company-app/src/main/java/com/fs/app/param/LoginMaWxParam.java

@@ -0,0 +1,34 @@
+package com.fs.app.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@Data
+public class LoginMaWxParam implements Serializable {
+
+    @NotBlank(message = "code参数缺失")
+    @ApiModelProperty(value = "小程序登陆code")
+    private String code;
+
+    @ApiModelProperty(value = "小程序完整用户信息的加密数据")
+    private String encryptedData;
+
+    @ApiModelProperty(value = "小程序加密算法的初始向量")
+    private String iv;
+
+//    @ApiModelProperty(value = "公司id,如果不是第一位销售,都需要传")
+//    private Long companyId;
+
+//    @ApiModelProperty(value = "电话号码")
+//    private String phoneNumber;
+
+//    @ApiModelProperty(value = "上级销售id,如果没有则不传")
+//    private Long parentCompanyUseId;
+//
+//    @ApiModelProperty(value = "用户密码")
+//    private String password;
+
+}

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

@@ -12,4 +12,6 @@ public class LoginParam {
     private String account;
     @NotBlank(message = "请填写密码")
     private String password;
+
+    private String jpushId;
 }

+ 39 - 0
fs-company-app/src/main/java/com/fs/app/service/IAppService.java

@@ -0,0 +1,39 @@
+package com.fs.app.service;
+
+import com.fs.app.param.ChangeUserDeptAndPostParam;
+import com.fs.app.vo.CompanyRoleVO;
+import com.fs.app.vo.UserPostVO;
+import com.fs.company.domain.CompanyDeptTreeSelect;
+
+import java.util.List;
+
+
+public interface IAppService {
+
+    /**
+     * 修改用户部门和岗位
+     * @param param 参数
+     */
+    void changeUserDeptAndPost(ChangeUserDeptAndPostParam param);
+
+    /**
+     * 获取公司部门树
+     * @param companyId 公司ID
+     * @return  list
+     */
+    List<CompanyDeptTreeSelect> getDeptTreeSelect(Long companyId);
+
+    /**
+     * 获取公司岗位列表
+     * @param companyId 公司ID
+     * @return list
+     */
+    List<UserPostVO> postList(Long companyId);
+
+    /**
+     * 获取公司角色列表
+     * @param companyId 公司ID
+     * @return  list
+     */
+    List<CompanyRoleVO> roleList(Long companyId);
+}

+ 155 - 0
fs-company-app/src/main/java/com/fs/app/service/impl/AppServiceImpl.java

@@ -0,0 +1,155 @@
+package com.fs.app.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.param.ChangeUserDeptAndPostParam;
+import com.fs.app.service.IAppService;
+import com.fs.app.vo.CompanyRoleVO;
+import com.fs.app.vo.UserPostVO;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.*;
+import com.fs.company.service.*;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class AppServiceImpl implements IAppService {
+
+    private final ICompanyUserService companyUserService;
+    private final ICompanyDeptService companyDeptService;
+    private final ICompanyPostService companyPostService;
+    private final ICompanyUserPostService companyUserPostService;
+    private final ICompanyRoleService companyRoleService;
+    private final ICompanyUserRoleService companyUserRoleService;
+
+
+    @Override
+    public void changeUserDeptAndPost(ChangeUserDeptAndPostParam param) {
+        log.debug("change user dept and post param: {}", JSON.toJSONString(param));
+        Set<Long> companyUserIds = Arrays.stream(param.getCompanyUserIds().split(",")).map(Long::new).collect(Collectors.toSet());
+
+        List<CompanyUser> companyUsers = companyUserService.selectCompanyUserByIds(companyUserIds);
+        if (Objects.isNull(companyUsers) || companyUsers.isEmpty()) {
+            throw new CustomException("销售不存在");
+        }
+
+        // 修改部门
+        if (Objects.nonNull(param.getDeptId())) {
+            CompanyDept companyDept = companyDeptService.selectCompanyDeptById(param.getDeptId());
+            if (Objects.isNull(companyDept)) {
+                throw new CustomException("部门不存在");
+            }
+
+            companyUsers.forEach(companyUser -> {
+                companyUser.setDeptId(param.getDeptId());
+                companyUserService.updateCompanyUser(companyUser);
+            });
+        }
+
+        // 修改岗位
+        if (StringUtils.isNotBlank(param.getPostIds())) {
+            List<Long> postIds = Arrays.stream(param.getPostIds().split(",")).map(Long::new).collect(Collectors.toList());
+            List<CompanyPost> companyPosts =  companyPostService.selectCompanyPostByIds(postIds);
+            if (Objects.nonNull(companyPosts) && !companyPosts.isEmpty()) {
+                companyUsers.forEach(companyUser -> {
+                    companyUserPostService.deleteCompanyUserPostById(companyUser.getUserId());
+                    companyUserPostService.batchInsertCompanyUserPost(companyPosts.stream().map(p -> {
+                        CompanyUserPost companyUserPost = new CompanyUserPost();
+                        companyUserPost.setPostId(p.getPostId());
+                        companyUserPost.setUserId(companyUser.getUserId());
+                        return companyUserPost;
+                    }).collect(Collectors.toList()));
+                });
+            }
+        }
+
+        // 修改角色
+        if (StringUtils.isNotBlank(param.getRoleIds())) {
+            List<Long> roleIds = Arrays.stream(param.getRoleIds().split(",")).map(Long::new).collect(Collectors.toList());
+            List<CompanyRole> CompanyRoles =  companyRoleService.selectCompanyRoleByIds(roleIds);
+            if (Objects.nonNull(CompanyRoles) && !CompanyRoles.isEmpty()) {
+                companyUsers.forEach(companyUser -> {
+                    companyUserRoleService.deleteCompanyUserRoleById(companyUser.getUserId());
+                    companyUserRoleService.batchInsertCompanyUserRole(CompanyRoles.stream().map(p -> {
+                        CompanyUserRole companyUserRole = new CompanyUserRole();
+                        companyUserRole.setRoleId(p.getRoleId());
+                        companyUserRole.setUserId(companyUser.getUserId());
+                        return companyUserRole;
+                    }).collect(Collectors.toList()));
+                });
+            }
+        }
+    }
+
+    /**
+     * 获取公司部门树
+     * @param companyId 公司ID
+     * @return  list
+     */
+    @Override
+    public List<CompanyDeptTreeSelect> getDeptTreeSelect(Long companyId) {
+        log.debug("getDeptTreeSelect companyId: {}", companyId);
+        if (Objects.isNull(companyId)) {
+            throw new CustomException("获取companyId失败, 请重新登录");
+        }
+        CompanyDept dept = new CompanyDept();
+        dept.setCompanyId(companyId);
+        dept.setStatus("0");
+        List<CompanyDept> depts = companyDeptService.selectCompanyDeptList(dept);
+        return companyDeptService.buildDeptTreeSelect(depts);
+    }
+
+    /**
+     * 获取公司岗位列表
+     * @param companyId 公司ID
+     * @return list
+     */
+    @Override
+    public List<UserPostVO> postList(Long companyId) {
+        log.debug("postList companyId: {}", companyId);
+        if (Objects.isNull(companyId)) {
+            throw new CustomException("获取companyId失败, 请重新登录");
+        }
+        CompanyPost companyPost = new CompanyPost();
+        companyPost.setCompanyId(companyId);
+        return companyPostService.selectCompanyPostList(companyPost).stream().map(p -> {
+            UserPostVO userPostVO = new UserPostVO();
+            userPostVO.setPostId(p.getPostId());
+            userPostVO.setPostName(p.getPostName());
+            return userPostVO;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 获取公司角色列表
+     * @param companyId 公司ID
+     * @return  list
+     */
+    @Override
+    public List<CompanyRoleVO> roleList(Long companyId) {
+        log.debug("roleList companyId: {}", companyId);
+        if (Objects.isNull(companyId)) {
+            throw new CustomException("获取companyId失败, 请重新登录");
+        }
+        CompanyRole companyRole = new CompanyRole();
+        companyRole.setCompanyId(companyId);
+        companyRole.setStatus("0");
+        companyRole.setDelFlag("0");
+        return companyRoleService.selectCompanyRoleList(companyRole).stream().map(r -> {
+            CompanyRoleVO companyRoleVO = new CompanyRoleVO();
+            companyRoleVO.setRoleId(r.getRoleId());
+            companyRoleVO.setRoleName(r.getRoleName());
+            return companyRoleVO;
+        }).collect(Collectors.toList());
+    }
+}

+ 15 - 0
fs-company-app/src/main/java/com/fs/app/vo/CompanyRoleVO.java

@@ -0,0 +1,15 @@
+package com.fs.app.vo;
+
+import lombok.Data;
+
+@Data
+public class CompanyRoleVO {
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+    /**
+     * 角色名称
+     */
+    private String roleName;
+}

+ 32 - 0
fs-company-app/src/main/java/com/fs/app/vo/CompanySubUserVO.java

@@ -0,0 +1,32 @@
+package com.fs.app.vo;
+
+import com.fs.company.domain.CompanyUser;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class CompanySubUserVO extends CompanyUser {
+    /**
+     * 今日观看人数
+     */
+    private Integer todayWatchCount;
+    /**
+     * 今日完播人数
+     */
+    private Integer todayFinishCount;
+    /**
+     * 今日完播率
+     */
+    private BigDecimal todayFinishRate;
+    /**
+     * 今日红包金额
+     */
+    private BigDecimal todayRedPacketAmount;
+    /**
+     * 审核状态
+     */
+    private Integer applyStatus;
+}

+ 17 - 0
fs-company-app/src/main/java/com/fs/app/vo/UserPostVO.java

@@ -0,0 +1,17 @@
+package com.fs.app.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class UserPostVO {
+    /**
+     * 岗位ID
+     */
+    private Long postId;
+    /**
+     * 岗位名称
+     */
+    private String postName;
+}

+ 21 - 39
fs-company-app/src/main/java/com/fs/app/vo/UserVO.java

@@ -1,49 +1,31 @@
 package com.fs.app.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Data
 public class UserVO {
     String nickName;
     String firstLetter;
     String deptName;
+    Long deptId;
     Long userId;
     String avatar;
 
-    public String getAvatar() {
-        return avatar;
-    }
-
-    public void setAvatar(String avatar) {
-        this.avatar = avatar;
-    }
-
-    public String getNickName() {
-        return nickName;
-    }
-
-    public void setNickName(String nickName) {
-        this.nickName = nickName;
-    }
-
-    public String getFirstLetter() {
-        return firstLetter;
-    }
-
-    public void setFirstLetter(String firstLetter) {
-        this.firstLetter = firstLetter;
-    }
-
-    public String getDeptName() {
-        return deptName;
-    }
-
-    public void setDeptName(String deptName) {
-        this.deptName = deptName;
-    }
-
-    public Long getUserId() {
-        return userId;
-    }
-
-    public void setUserId(Long userId) {
-        this.userId = userId;
-    }
+    // 注册时间
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate registerTime;
+    // 禁用状态 0正常 1停用
+    private String status;
+    // 电话号码
+    private String phoneNumber;
+    // 审核状态 0未审核 1已审核
+    private Integer isAudit;
+    // 岗位
+    private List<UserPostVO> post;
+    // 角色
+    private List<CompanyRoleVO> roles;
 }

+ 78 - 0
fs-company-app/src/main/java/com/fs/core/PermissionService.java

@@ -0,0 +1,78 @@
+package com.fs.core;
+
+import com.alibaba.druid.support.json.JSONUtils;
+import com.fs.app.utils.JwtUtils;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import io.jsonwebtoken.Claims;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * 自定义权限实现,ss取自SpringSecurity首字母
+ */
+@Service("ss")
+public class PermissionService
+{
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+    /** 所有权限标识 */
+    private static final String ALL_PERMISSION = "*:*:*";
+
+
+
+    /**
+     * 验证用户是否具备某权限
+     *
+     * @param permission 权限字符串
+     * @return 用户是否具备某权限
+     */
+    public boolean hasPermi(String permission)
+    {
+
+        String headValue = ServletUtils.getRequest().getHeader("APPToken");
+        Claims claims=jwtUtils.getClaimByToken(headValue);
+        String userId = claims.getSubject().toString();
+        //获取用户所有的权限
+        String permss=redisCache.getCacheObject("perms:"+userId);
+        if(StringUtils.isNotEmpty(permss)){
+            List<String> perms= (List<String>) JSONUtils.parse(permss);
+            if (StringUtils.isEmpty(permission))
+            {
+                return false;
+            }
+            if (StringUtils.isNull(userId) || CollectionUtils.isEmpty(perms))
+            {
+                return false;
+            }
+            return hasPermissions(perms, permission);
+        }
+        else{
+            return false;
+        }
+
+
+    }
+
+
+
+    /**
+     * 判断是否包含权限
+     *
+     * @param permissions 权限列表
+     * @param permission 权限字符串
+     * @return 用户是否具备某权限
+     */
+    private boolean hasPermissions(List<String> permissions, String permission)
+    {
+        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
+    }
+
+
+}

+ 33 - 29
fs-company-app/src/main/java/com/fs/framework/aspectj/DataScopeAspect.java → fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java

@@ -1,20 +1,27 @@
-package com.fs.framework.aspectj;
+package com.fs.core.aspectj;
 
+import com.fs.app.utils.JwtUtils;
 import com.fs.common.annotation.DataScope;
 import com.fs.common.core.domain.BaseEntity;
-import com.fs.common.core.domain.entity.SysRole;
-import com.fs.common.core.domain.entity.SysUser;
-import com.fs.common.core.domain.model.LoginUser;
-import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.spring.SpringUtils;
+import com.fs.company.domain.CompanyRole;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
+import io.jsonwebtoken.Claims;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.Signature;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
 import org.aspectj.lang.annotation.Pointcut;
 import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
 
+import javax.servlet.http.HttpServletRequest;
 import java.lang.reflect.Method;
 
 /**
@@ -26,6 +33,9 @@ import java.lang.reflect.Method;
 @Component
 public class DataScopeAspect
 {
+    @Autowired
+    JwtUtils jwtUtils;
+
     /**
      * 全部数据权限
      */
@@ -65,7 +75,6 @@ public class DataScopeAspect
     @Before("dataScopePointCut()")
     public void doBefore(JoinPoint point) throws Throwable
     {
-        clearDataScope(point);
         handleDataScope(point);
     }
 
@@ -77,15 +86,22 @@ public class DataScopeAspect
         {
             return;
         }
+        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
+        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
+        HttpServletRequest request = sra.getRequest();
+
+        String headValue = request.getHeader("APPToken");
+        Claims claims = jwtUtils.getClaimByToken(headValue);
+        Long userId =Long.parseLong( claims.getSubject().toString());
+
         // 获取当前的用户
-        LoginUser loginUser = SecurityUtils.getLoginUser();
-        if (StringUtils.isNotNull(loginUser))
+        CompanyUser user = SpringUtils.getBean(ICompanyUserService.class).selectCompanyUserById(userId);
+        if (StringUtils.isNotNull(user))
         {
-            SysUser currentUser = loginUser.getUser();
             // 如果是超级管理员,则不过滤数据
-            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
+            if (StringUtils.isNotNull(user) && !user.isAdmin())
             {
-                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
+                dataScopeFilter(joinPoint, user, controllerDataScope.deptAlias(),
                         controllerDataScope.userAlias());
             }
         }
@@ -98,11 +114,11 @@ public class DataScopeAspect
      * @param user 用户
      * @param userAlias 别名
      */
-    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
+    public static void dataScopeFilter(JoinPoint joinPoint, CompanyUser user, String deptAlias, String userAlias)
     {
         StringBuilder sqlString = new StringBuilder();
 
-        for (SysRole role : user.getRoles())
+        for (CompanyRole role : user.getRoles())
         {
             String dataScope = role.getDataScope();
             if (DATA_SCOPE_ALL.equals(dataScope))
@@ -113,7 +129,7 @@ public class DataScopeAspect
             else if (DATA_SCOPE_CUSTOM.equals(dataScope))
             {
                 sqlString.append(StringUtils.format(
-                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
+                        " OR {}.dept_id IN ( SELECT dept_id FROM company_role_dept WHERE role_id = {} ) ", deptAlias,
                         role.getRoleId()));
             }
             else if (DATA_SCOPE_DEPT.equals(dataScope))
@@ -123,7 +139,7 @@ public class DataScopeAspect
             else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
             {
                 sqlString.append(StringUtils.format(
-                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
+                        " OR {}.dept_id IN ( SELECT dept_id FROM company_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                         deptAlias, user.getDeptId(), user.getDeptId()));
             }
             else if (DATA_SCOPE_SELF.equals(dataScope))
@@ -135,7 +151,8 @@ public class DataScopeAspect
                 else
                 {
                     // 数据权限为仅本人且没有userAlias别名不查询任何数据
-                    sqlString.append(" OR 1=0 ");
+//                    sqlString.append(" OR 1=0 ");
+                    sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
                 }
             }
         }
@@ -166,17 +183,4 @@ public class DataScopeAspect
         }
         return null;
     }
-
-    /**
-     * 拼接权限sql前先清空params.dataScope参数防止注入
-     */
-    private void clearDataScope(final JoinPoint joinPoint)
-    {
-        Object params = joinPoint.getArgs()[0];
-        if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
-        {
-            BaseEntity baseEntity = (BaseEntity) params;
-            baseEntity.getParams().put(DATA_SCOPE, "");
-        }
-    }
 }

+ 2 - 2
fs-company-app/src/main/java/com/fs/framework/config/ApplicationConfig.java → fs-company-app/src/main/java/com/fs/core/config/ApplicationConfig.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
@@ -11,7 +11,7 @@ import java.util.TimeZone;
 /**
  * 程序注解配置
  *
-
+ 
  */
 @Configuration
 // 表示通过aop框架暴露该代理对象,AopContext能够访问

+ 3 - 3
fs-company-app/src/main/java/com/fs/framework/config/DruidConfig.java → fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.alibaba.druid.pool.DruidDataSource;
 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
@@ -6,8 +6,8 @@ import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatPropertie
 import com.alibaba.druid.util.Utils;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.utils.spring.SpringUtils;
-import com.fs.framework.config.properties.DruidProperties;
-import com.fs.framework.datasource.DynamicDataSource;
+import com.fs.core.config.properties.DruidProperties;
+import com.fs.core.datasource.DynamicDataSource;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;

+ 2 - 2
fs-company-app/src/main/java/com/fs/framework/config/FastJson2JsonRedisSerializer.java → fs-company-app/src/main/java/com/fs/core/config/FastJson2JsonRedisSerializer.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.parser.ParserConfig;
@@ -15,7 +15,7 @@ import java.nio.charset.Charset;
 /**
  * Redis使用FastJson序列化
  * 
-
+ 
  */
 public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
 {

+ 6 - 4
fs-company-app/src/main/java/com/fs/framework/config/FilterConfig.java → fs-company-app/src/main/java/com/fs/core/config/FilterConfig.java

@@ -1,10 +1,9 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.fs.common.filter.RepeatableFilter;
 import com.fs.common.filter.XssFilter;
 import com.fs.common.utils.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -16,12 +15,14 @@ import java.util.Map;
 /**
  * Filter配置
  *
-
+ 
  */
 @Configuration
-@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
 public class FilterConfig
 {
+    @Value("${xss.enabled}")
+    private String enabled;
+
     @Value("${xss.excludes}")
     private String excludes;
 
@@ -40,6 +41,7 @@ public class FilterConfig
         registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
         Map<String, String> initParameters = new HashMap<String, String>();
         initParameters.put("excludes", excludes);
+        initParameters.put("enabled", enabled);
         registration.setInitParameters(initParameters);
         return registration;
     }

+ 38 - 65
fs-company-app/src/main/java/com/fs/framework/config/MyBatisConfig.java → fs-company-app/src/main/java/com/fs/core/config/MyBatisConfig.java

@@ -1,9 +1,8 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
-import com.fs.common.utils.StringUtils;
+import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
 import org.apache.ibatis.io.VFS;
 import org.apache.ibatis.session.SqlSessionFactory;
-import org.mybatis.spring.SqlSessionFactoryBean;
 import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
@@ -21,113 +20,87 @@ import org.springframework.util.ClassUtils;
 import javax.sql.DataSource;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
 /**
  * Mybatis支持*匹配扫描包
- * 
-
  */
 @Configuration
-public class MyBatisConfig
-{
+public class MyBatisConfig {
     @Autowired
     private Environment env;
 
     static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
 
-    public static String setTypeAliasesPackage(String typeAliasesPackage)
-    {
+    public static String setTypeAliasesPackage(String typeAliasesPackage) {
         ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
         MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
         List<String> allResult = new ArrayList<String>();
-        try
-        {
-            for (String aliasesPackage : typeAliasesPackage.split(","))
-            {
+        try {
+            for (String aliasesPackage : typeAliasesPackage.split(",")) {
                 List<String> result = new ArrayList<String>();
                 aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                         + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                 Resource[] resources = resolver.getResources(aliasesPackage);
-                if (resources != null && resources.length > 0)
-                {
+                if (resources != null && resources.length > 0) {
                     MetadataReader metadataReader = null;
-                    for (Resource resource : resources)
-                    {
-                        if (resource.isReadable())
-                        {
+                    for (Resource resource : resources) {
+                        if (resource.isReadable()) {
                             metadataReader = metadataReaderFactory.getMetadataReader(resource);
-                            try
-                            {
+                            try {
                                 result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
-                            }
-                            catch (ClassNotFoundException e)
-                            {
+                            } catch (ClassNotFoundException e) {
                                 e.printStackTrace();
                             }
                         }
                     }
                 }
-                if (result.size() > 0)
-                {
+                if (result.size() > 0) {
                     HashSet<String> hashResult = new HashSet<String>(result);
                     allResult.addAll(hashResult);
                 }
             }
-            if (allResult.size() > 0)
-            {
+            if (allResult.size() > 0) {
                 typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
-            }
-            else
-            {
+            } else {
                 throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
             }
-        }
-        catch (IOException e)
-        {
+        } catch (IOException e) {
             e.printStackTrace();
         }
         return typeAliasesPackage;
     }
 
-    public Resource[] resolveMapperLocations(String[] mapperLocations)
-    {
-        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
-        List<Resource> resources = new ArrayList<Resource>();
-        if (mapperLocations != null)
-        {
-            for (String mapperLocation : mapperLocations)
-            {
-                try
-                {
-                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
-                    resources.addAll(Arrays.asList(mappers));
-                }
-                catch (IOException e)
-                {
-                    // ignore
-                }
-            }
-        }
-        return resources.toArray(new Resource[resources.size()]);
-    }
-
+    //    @Bean
+//    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
+//    {
+//        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
+//        String mapperLocations = env.getProperty("mybatis.mapperLocations");
+//        String configLocation = env.getProperty("mybatis.configLocation");
+//        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+//        VFS.addImplClass(SpringBootVFS.class);
+//
+//        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+//        sessionFactory.setDataSource(dataSource);
+//        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+//        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
+//        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+//        return sessionFactory.getObject();
+//    }
     @Bean
-    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
-    {
-        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
-        String mapperLocations = env.getProperty("mybatis.mapperLocations");
-        String configLocation = env.getProperty("mybatis.configLocation");
+    public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception {
+        String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
+        String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
+        String configLocation = env.getProperty("mybatis-plus.configLocation");
         typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
         VFS.addImplClass(SpringBootVFS.class);
 
-        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+        final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
         sessionFactory.setDataSource(dataSource);
         sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
-        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
+        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
         sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
         return sessionFactory.getObject();
     }
-}
+}

+ 4 - 40
fs-company-app/src/main/java/com/fs/framework/config/RedisConfig.java → fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java

@@ -1,17 +1,14 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
 import org.springframework.cache.annotation.CachingConfigurerSupport;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
@@ -35,17 +32,12 @@ public class RedisConfig extends CachingConfigurerSupport
 
         ObjectMapper mapper = new ObjectMapper();
         mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         serializer.setObjectMapper(mapper);
 
+        template.setValueSerializer(serializer);
         // 使用StringRedisSerializer来序列化和反序列化redis的key值
         template.setKeySerializer(new StringRedisSerializer());
-        template.setValueSerializer(serializer);
-
-        // Hash的key也采用StringRedisSerializer的序列化方式
-        template.setHashKeySerializer(new StringRedisSerializer());
-        template.setHashValueSerializer(serializer);
-
         template.afterPropertiesSet();
         return template;
     }
@@ -76,7 +68,7 @@ public class RedisConfig extends CachingConfigurerSupport
 
         ObjectMapper mapper = new ObjectMapper();
         mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         serializer.setObjectMapper(mapper);
 
         // 使用StringRedisSerializer来序列化和反序列化redis的key值
@@ -90,32 +82,4 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
-
-    @Bean
-    public DefaultRedisScript<Long> limitScript()
-    {
-        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
-        redisScript.setScriptText(limitScriptText());
-        redisScript.setResultType(Long.class);
-        return redisScript;
-    }
-
-    /**
-     * 限流脚本
-     */
-    private String limitScriptText()
-    {
-        return "local key = KEYS[1]\n" +
-                "local count = tonumber(ARGV[1])\n" +
-                "local time = tonumber(ARGV[2])\n" +
-                "local current = redis.call('get', key);\n" +
-                "if current and tonumber(current) > count then\n" +
-                "    return current;\n" +
-                "end\n" +
-                "current = redis.call('incr', key)\n" +
-                "if tonumber(current) == 1 then\n" +
-                "    redis.call('expire', key, time)\n" +
-                "end\n" +
-                "return current;";
-    }
 }

+ 21 - 8
fs-company-app/src/main/java/com/fs/framework/config/ResourcesConfig.java → fs-company-app/src/main/java/com/fs/core/config/ResourcesConfig.java

@@ -1,22 +1,22 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.fs.common.config.FSConfig;
 import com.fs.common.constant.Constants;
-import com.fs.framework.interceptor.RepeatSubmitInterceptor;
+import com.fs.core.interceptor.RepeatSubmitInterceptor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 /**
  * 通用配置
- * 
-
+ *
  */
 @Configuration
 public class ResourcesConfig implements WebMvcConfigurer
@@ -31,7 +31,8 @@ public class ResourcesConfig implements WebMvcConfigurer
         registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + FSConfig.getProfile() + "/");
 
         /** swagger配置 */
-        registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
+        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
     }
 
     /**
@@ -47,8 +48,7 @@ public class ResourcesConfig implements WebMvcConfigurer
      * 跨域配置
      */
     @Bean
-    public CorsFilter corsFilter()
-    {
+    public CorsFilter corsFilter() {
         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
         CorsConfiguration config = new CorsConfiguration();
         config.setAllowCredentials(true);
@@ -62,4 +62,17 @@ public class ResourcesConfig implements WebMvcConfigurer
         source.registerCorsConfiguration("/**", config);
         return new CorsFilter(source);
     }
-}
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+                .allowedOrigins("*")
+                //.allowedOriginPatterns("*")
+                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
+                .allowCredentials(true)
+                .maxAge(3600)
+                .allowedHeaders("*");
+    }
+
+}
+

+ 4 - 15
fs-company-app/src/main/java/com/fs/framework/config/SecurityConfig.java → fs-company-app/src/main/java/com/fs/core/config/SecurityConfig.java

@@ -1,15 +1,13 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
-import org.springframework.context.annotation.Bean;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.config.BeanIds;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 
+
 /**
  * spring security配置
- * 
+ *
 
  */
 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@@ -32,20 +30,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
      * authenticated       |   用户登录后可访问
      */
     @Override
-    protected void configure(HttpSecurity http) throws Exception
-    {
+    protected void configure(HttpSecurity http) throws Exception {
         http.authorizeRequests()
                 .antMatchers("/**").permitAll()
-                .antMatchers("/app/crmCustomer/**").anonymous()
                 .anyRequest().authenticated()
                 .and().csrf().disable();
     }
 
-    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
-    @Override
-    public AuthenticationManager authenticationManagerBean() throws Exception {
-        return super.authenticationManagerBean();
-    }
-
-
 }

+ 2 - 2
fs-company-app/src/main/java/com/fs/framework/config/ServerConfig.java → fs-company-app/src/main/java/com/fs/core/config/ServerConfig.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.fs.common.utils.ServletUtils;
 import org.springframework.stereotype.Component;
@@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletRequest;
 /**
  * 服务相关配置
  * 
-
+ 
  */
 @Component
 public class ServerConfig

+ 9 - 7
fs-company-app/src/main/java/com/fs/framework/config/SwaggerConfig.java → fs-company-app/src/main/java/com/fs/core/config/SwaggerConfig.java

@@ -1,8 +1,8 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.fs.common.config.FSConfig;
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
 import io.swagger.annotations.ApiOperation;
-import io.swagger.models.auth.In;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
@@ -14,16 +14,19 @@ import springfox.documentation.service.*;
 import springfox.documentation.spi.DocumentationType;
 import springfox.documentation.spi.service.contexts.SecurityContext;
 import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Swagger2的接口配置
- * 
+ *
 
  */
 @Configuration
+@EnableSwagger2
+@EnableSwaggerBootstrapUI
 public class SwaggerConfig
 {
     /** 系统基础配置 */
@@ -70,8 +73,7 @@ public class SwaggerConfig
     private List<ApiKey> securitySchemes()
     {
         List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
-//        apiKeyList.add(new ApiKey("Authorization", "AppToken", "header"));
-        apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
+        apiKeyList.add(new ApiKey("Authorization", "AppToken", "header"));
         return apiKeyList;
     }
 
@@ -110,9 +112,9 @@ public class SwaggerConfig
         // 用ApiInfoBuilder进行定制
         return new ApiInfoBuilder()
                 // 设置标题
-                .title("标题:FS管理系统_接口文档")
+                .title("标题:销售APP_接口文档")
                 // 描述
-                .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
+                .description("描述:销售APP接口文档")
                 // 作者信息
                 .contact(new Contact(fsConfig.getName(), null, null))
                 // 版本

+ 2 - 2
fs-company-app/src/main/java/com/fs/framework/config/ThreadPoolConfig.java → fs-company-app/src/main/java/com/fs/core/config/ThreadPoolConfig.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config;
+package com.fs.core.config;
 
 import com.fs.common.utils.Threads;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
@@ -13,7 +13,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 /**
  * 线程池配置
  *
-
+ 
  **/
 @Configuration
 public class ThreadPoolConfig

+ 1 - 1
fs-company-app/src/main/java/com/fs/framework/config/properties/DruidProperties.java → fs-company-app/src/main/java/com/fs/core/config/properties/DruidProperties.java

@@ -1,4 +1,4 @@
-package com.fs.framework.config.properties;
+package com.fs.core.config.properties;
 
 import com.alibaba.druid.pool.DruidDataSource;
 import org.springframework.beans.factory.annotation.Value;

+ 2 - 2
fs-company-app/src/main/java/com/fs/framework/datasource/DynamicDataSource.java → fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java

@@ -1,4 +1,4 @@
-package com.fs.framework.datasource;
+package com.fs.core.datasource;
 
 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
@@ -8,7 +8,7 @@ import java.util.Map;
 /**
  * 动态数据源
  * 
-
+ 
  */
 public class DynamicDataSource extends AbstractRoutingDataSource
 {

+ 3 - 3
fs-company-app/src/main/java/com/fs/framework/datasource/DynamicDataSourceContextHolder.java → fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java

@@ -1,4 +1,4 @@
-package com.fs.framework.datasource;
+package com.fs.core.datasource;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -6,7 +6,7 @@ import org.slf4j.LoggerFactory;
 /**
  * 数据源切换处理
  * 
-
+ 
  */
 public class DynamicDataSourceContextHolder
 {
@@ -23,7 +23,7 @@ public class DynamicDataSourceContextHolder
      */
     public static void setDataSourceType(String dsType)
     {
-//        log.info("切换到{}数据源", dsType);
+        log.info("切换到{}数据源", dsType);
         CONTEXT_HOLDER.set(dsType);
     }
 

+ 1 - 1
fs-company-app/src/main/java/com/fs/framework/interceptor/RepeatSubmitInterceptor.java → fs-company-app/src/main/java/com/fs/core/interceptor/RepeatSubmitInterceptor.java

@@ -1,4 +1,4 @@
-package com.fs.framework.interceptor;
+package com.fs.core.interceptor;
 
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.RepeatSubmit;

+ 16 - 12
fs-company-app/src/main/java/com/fs/framework/interceptor/impl/SameUrlDataInterceptor.java → fs-company-app/src/main/java/com/fs/core/interceptor/impl/SameUrlDataInterceptor.java

@@ -1,12 +1,13 @@
-package com.fs.framework.interceptor.impl;
+package com.fs.core.interceptor.impl;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.filter.RepeatedlyRequestWrapper;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.http.HttpHelper;
-import com.fs.framework.interceptor.RepeatSubmitInterceptor;
+import com.fs.core.interceptor.RepeatSubmitInterceptor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
@@ -14,23 +15,24 @@ import org.springframework.stereotype.Component;
 import javax.servlet.http.HttpServletRequest;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
 
 /**
  * 判断请求url和数据是否和上一次相同,
  * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
- * 
+ *
 
  */
 @Component
 public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
 {
-    public final String REPEAT_PARAMS = "repeatParams";
+    private final String REPEAT_PARAMS = "repeatParams";
 
-    public final String REPEAT_TIME = "repeatTime";
+    private final String REPEAT_TIME = "repeatTime";
 
     // 令牌自定义标识
-    @Value("${token.header}")
+    @Value("${fs.jwt.header}")
     private String header;
 
     @Autowired
@@ -38,7 +40,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
 
     /**
      * 间隔时间,单位:秒 默认10秒
-     * 
+     *
      * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
      */
     private int intervalTime = 10;
@@ -62,7 +64,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
         // body参数为空,获取Parameter的数据
         if (StringUtils.isEmpty(nowParams))
         {
-            nowParams = JSONObject.toJSONString(request.getParameterMap());
+            // TreeMap保证有序
+            Map<String, String[]> sortedParams = new TreeMap<>(request.getParameterMap());
+            nowParams = JSON.toJSONString(sortedParams, SerializerFeature.WriteMapNullValue);
         }
         Map<String, Object> nowDataMap = new HashMap<String, Object>();
         nowDataMap.put(REPEAT_PARAMS, nowParams);
@@ -79,9 +83,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
         }
 
         // 唯一标识(指定key + 消息头)
-        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
+        String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;
 
-        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
+        Object sessionObj = redisCache.getCacheObject(cache_repeat_key);
         if (sessionObj != null)
         {
             Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
@@ -96,7 +100,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
         }
         Map<String, Object> cacheMap = new HashMap<String, Object>();
         cacheMap.put(url, nowDataMap);
-        redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS);
+        redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);
         return false;
     }
 

+ 50 - 0
fs-company-app/src/main/java/com/fs/core/security/SecurityUtils.java

@@ -0,0 +1,50 @@
+package com.fs.core.security;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+
+/**
+ * 安全服务工具类
+ *
+
+ */
+public class SecurityUtils
+{
+
+    /**
+     * 生成BCryptPasswordEncoder密码
+     *
+     * @param password 密码
+     * @return 加密字符串
+     */
+    public static String encryptPassword(String password)
+    {
+
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        return passwordEncoder.encode(password);
+    }
+
+    /**
+     * 判断密码是否相同
+     *
+     * @param rawPassword 真实密码
+     * @param encodedPassword 加密后字符
+     * @return 结果
+     */
+    public static boolean matchesPassword(String rawPassword, String encodedPassword)
+    {
+        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        return passwordEncoder.matches(rawPassword, encodedPassword);
+    }
+
+    /**
+     * 是否为管理员
+     *
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public static boolean isAdmin(Long userId)
+    {
+        return userId != null && 1L == userId;
+    }
+}

+ 0 - 73
fs-company-app/src/main/java/com/fs/framework/aspectj/DataSourceAspect.java

@@ -1,73 +0,0 @@
-package com.fs.framework.aspectj;
-
-import com.fs.common.annotation.DataSource;
-import com.fs.common.utils.StringUtils;
-import com.fs.framework.datasource.DynamicDataSourceContextHolder;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-import java.util.Objects;
-
-/**
- * 多数据源处理
- * 
-
- */
-@Aspect
-@Order(1)
-@Component
-public class DataSourceAspect
-{
-    protected Logger logger = LoggerFactory.getLogger(getClass());
-
-    @Pointcut("@annotation(com.fs.common.annotation.DataSource)"
-            + "|| @within(com.fs.common.annotation.DataSource)")
-    public void dsPointCut()
-    {
-
-    }
-
-    @Around("dsPointCut()")
-    public Object around(ProceedingJoinPoint point) throws Throwable
-    {
-        DataSource dataSource = getDataSource(point);
-
-        if (StringUtils.isNotNull(dataSource))
-        {
-            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
-        }
-
-        try
-        {
-            return point.proceed();
-        }
-        finally
-        {
-            // 销毁数据源 在执行方法之后
-            DynamicDataSourceContextHolder.clearDataSourceType();
-        }
-    }
-
-    /**
-     * 获取需要切换的数据源
-     */
-    public DataSource getDataSource(ProceedingJoinPoint point)
-    {
-        MethodSignature signature = (MethodSignature) point.getSignature();
-        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
-        if (Objects.nonNull(dataSource))
-        {
-            return dataSource;
-        }
-
-        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
-    }
-}

+ 0 - 245
fs-company-app/src/main/java/com/fs/framework/aspectj/LogAspect.java

@@ -1,245 +0,0 @@
-package com.fs.framework.aspectj;
-
-import com.alibaba.fastjson.JSON;
-import com.fs.common.annotation.Log;
-import com.fs.common.core.domain.model.LoginUser;
-import com.fs.common.enums.BusinessStatus;
-import com.fs.common.enums.HttpMethod;
-import com.fs.common.utils.SecurityUtils;
-import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.ip.IpUtils;
-
-import com.fs.framework.manager.AsyncManager;
-import com.fs.framework.manager.factory.AsyncFactory;
-import com.fs.system.domain.SysOperLog;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.Signature;
-import org.aspectj.lang.annotation.AfterReturning;
-import org.aspectj.lang.annotation.AfterThrowing;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-import org.springframework.validation.BindingResult;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.HandlerMapping;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * 操作日志记录处理
- * 
-
- */
-@Aspect
-@Component
-public class LogAspect
-{
-    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
-
-    // 配置织入点
-    @Pointcut("@annotation(com.fs.common.annotation.Log)")
-    public void logPointCut()
-    {
-    }
-
-    /**
-     * 处理完请求后执行
-     *
-     * @param joinPoint 切点
-     */
-    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
-    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
-    {
-        handleLog(joinPoint, null, jsonResult);
-    }
-
-    /**
-     * 拦截异常操作
-     * 
-     * @param joinPoint 切点
-     * @param e 异常
-     */
-    @AfterThrowing(value = "logPointCut()", throwing = "e")
-    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
-    {
-        handleLog(joinPoint, e, null);
-    }
-
-    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
-    {
-        try
-        {
-            // 获得注解
-            Log controllerLog = getAnnotationLog(joinPoint);
-            if (controllerLog == null)
-            {
-                return;
-            }
-
-            // 获取当前的用户
-            LoginUser loginUser = SecurityUtils.getLoginUser();
-
-            // *========数据库日志=========*//
-            SysOperLog operLog = new SysOperLog();
-            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
-            // 请求的地址
-            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
-            operLog.setOperIp(ip);
-            // 返回参数
-            operLog.setJsonResult(JSON.toJSONString(jsonResult));
-
-            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
-            if (loginUser != null)
-            {
-                operLog.setOperName(loginUser.getUsername());
-            }
-
-            if (e != null)
-            {
-                operLog.setStatus(BusinessStatus.FAIL.ordinal());
-                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
-            }
-            // 设置方法名称
-            String className = joinPoint.getTarget().getClass().getName();
-            String methodName = joinPoint.getSignature().getName();
-            operLog.setMethod(className + "." + methodName + "()");
-            // 设置请求方式
-            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
-            // 处理设置注解上的参数
-            getControllerMethodDescription(joinPoint, controllerLog, operLog);
-            // 保存数据库
-            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
-        }
-        catch (Exception exp)
-        {
-            // 记录本地异常日志
-            log.error("==前置通知异常==");
-            log.error("异常信息:{}", exp.getMessage());
-            exp.printStackTrace();
-        }
-    }
-
-    /**
-     * 获取注解中对方法的描述信息 用于Controller层注解
-     * 
-     * @param log 日志
-     * @param operLog 操作日志
-     * @throws Exception
-     */
-    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception
-    {
-        // 设置action动作
-        operLog.setBusinessType(log.businessType().ordinal());
-        // 设置标题
-        operLog.setTitle(log.title());
-        // 设置操作人类别
-        operLog.setOperatorType(log.operatorType().ordinal());
-        // 是否需要保存request,参数和值
-        if (log.isSaveRequestData())
-        {
-            // 获取参数的信息,传入到数据库中。
-            setRequestValue(joinPoint, operLog);
-        }
-    }
-
-    /**
-     * 获取请求的参数,放到log中
-     * 
-     * @param operLog 操作日志
-     * @throws Exception 异常
-     */
-    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
-    {
-        String requestMethod = operLog.getRequestMethod();
-        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
-        {
-            String params = argsArrayToString(joinPoint.getArgs());
-            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
-        }
-        else
-        {
-            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
-            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
-        }
-    }
-
-    /**
-     * 是否存在注解,如果存在就获取
-     */
-    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
-    {
-        Signature signature = joinPoint.getSignature();
-        MethodSignature methodSignature = (MethodSignature) signature;
-        Method method = methodSignature.getMethod();
-
-        if (method != null)
-        {
-            return method.getAnnotation(Log.class);
-        }
-        return null;
-    }
-
-    /**
-     * 参数拼装
-     */
-    private String argsArrayToString(Object[] paramsArray)
-    {
-        String params = "";
-        if (paramsArray != null && paramsArray.length > 0)
-        {
-            for (int i = 0; i < paramsArray.length; i++)
-            {
-                if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
-                {
-                    Object jsonObj = JSON.toJSON(paramsArray[i]);
-                    params += jsonObj.toString() + " ";
-                }
-            }
-        }
-        return params.trim();
-    }
-
-    /**
-     * 判断是否需要过滤的对象。
-     * 
-     * @param o 对象信息。
-     * @return 如果是需要过滤的对象,则返回true;否则返回false。
-     */
-    @SuppressWarnings("rawtypes")
-    public boolean isFilterObject(final Object o)
-    {
-        Class<?> clazz = o.getClass();
-        if (clazz.isArray())
-        {
-            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
-        }
-        else if (Collection.class.isAssignableFrom(clazz))
-        {
-            Collection collection = (Collection) o;
-            for (Iterator iter = collection.iterator(); iter.hasNext();)
-            {
-                return iter.next() instanceof MultipartFile;
-            }
-        }
-        else if (Map.class.isAssignableFrom(clazz))
-        {
-            Map map = (Map) o;
-            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
-            {
-                Map.Entry entry = (Map.Entry) iter.next();
-                return entry.getValue() instanceof MultipartFile;
-            }
-        }
-        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
-                || o instanceof BindingResult;
-    }
-}

+ 0 - 117
fs-company-app/src/main/java/com/fs/framework/aspectj/RateLimiterAspect.java

@@ -1,117 +0,0 @@
-package com.fs.framework.aspectj;
-
-import com.fs.common.annotation.RateLimiter;
-import com.fs.common.enums.LimitType;
-import com.fs.common.exception.ServiceException;
-import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.ip.IpUtils;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.Signature;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Before;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.core.script.RedisScript;
-import org.springframework.stereotype.Component;
-
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * 限流处理
- *
-
- */
-@Aspect
-@Component
-public class RateLimiterAspect
-{
-    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
-
-    private RedisTemplate<Object, Object> redisTemplate;
-
-    private RedisScript<Long> limitScript;
-
-    @Autowired
-    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
-    {
-        this.redisTemplate = redisTemplate;
-    }
-
-    @Autowired
-    public void setLimitScript(RedisScript<Long> limitScript)
-    {
-        this.limitScript = limitScript;
-    }
-
-    // 配置织入点
-    @Pointcut("@annotation(com.fs.common.annotation.RateLimiter)")
-    public void rateLimiterPointCut()
-    {
-    }
-
-    @Before("rateLimiterPointCut()")
-    public void doBefore(JoinPoint point) throws Throwable
-    {
-        RateLimiter rateLimiter = getAnnotationRateLimiter(point);
-        String key = rateLimiter.key();
-        int time = rateLimiter.time();
-        int count = rateLimiter.count();
-
-        String combineKey = getCombineKey(rateLimiter, point);
-        List<Object> keys = Collections.singletonList(combineKey);
-        try
-        {
-            Long number = redisTemplate.execute(limitScript, keys, count, time);
-            if (StringUtils.isNull(number) || number.intValue() > count)
-            {
-                throw new ServiceException("访问过于频繁,请稍后再试");
-            }
-            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
-        }
-        catch (ServiceException e)
-        {
-            throw e;
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException("服务器限流异常,请稍后再试");
-        }
-    }
-
-    /**
-     * 是否存在注解,如果存在就获取
-     */
-    private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint)
-    {
-        Signature signature = joinPoint.getSignature();
-        MethodSignature methodSignature = (MethodSignature) signature;
-        Method method = methodSignature.getMethod();
-
-        if (method != null)
-        {
-            return method.getAnnotation(RateLimiter.class);
-        }
-        return null;
-    }
-
-    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
-    {
-        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
-        if (rateLimiter.limitType() == LimitType.IP)
-        {
-            stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest()));
-        }
-        MethodSignature signature = (MethodSignature) point.getSignature();
-        Method method = signature.getMethod();
-        Class<?> targetClass = method.getDeclaringClass();
-        stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName());
-        return stringBuffer.toString();
-    }
-}

+ 0 - 85
fs-company-app/src/main/java/com/fs/framework/config/CaptchaConfig.java

@@ -1,85 +0,0 @@
-package com.fs.framework.config;
-
-import com.google.code.kaptcha.impl.DefaultKaptcha;
-import com.google.code.kaptcha.util.Config;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import java.util.Properties;
-
-import static com.google.code.kaptcha.Constants.*;
-
-/**
- * 验证码配置
- * 
-
- */
-@Configuration
-public class CaptchaConfig
-{
-    @Bean(name = "captchaProducer")
-    public DefaultKaptcha getKaptchaBean()
-    {
-        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
-        Properties properties = new Properties();
-        // 是否有边框 默认为true 我们可以自己设置yes,no
-        properties.setProperty(KAPTCHA_BORDER, "yes");
-        // 验证码文本字符颜色 默认为Color.BLACK
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
-        // 验证码图片宽度 默认为200
-        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
-        // 验证码图片高度 默认为50
-        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
-        // 验证码文本字符大小 默认为40
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
-        // KAPTCHA_SESSION_KEY
-        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
-        // 验证码文本字符长度 默认为5
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
-        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
-        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
-        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
-        Config config = new Config(properties);
-        defaultKaptcha.setConfig(config);
-        return defaultKaptcha;
-    }
-
-    @Bean(name = "captchaProducerMath")
-    public DefaultKaptcha getKaptchaBeanMath()
-    {
-        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
-        Properties properties = new Properties();
-        // 是否有边框 默认为true 我们可以自己设置yes,no
-        properties.setProperty(KAPTCHA_BORDER, "yes");
-        // 边框颜色 默认为Color.BLACK
-        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
-        // 验证码文本字符颜色 默认为Color.BLACK
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
-        // 验证码图片宽度 默认为200
-        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
-        // 验证码图片高度 默认为50
-        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
-        // 验证码文本字符大小 默认为40
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
-        // KAPTCHA_SESSION_KEY
-        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
-        // 验证码文本生成器
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.fs.framework.config.KaptchaTextCreator");
-        // 验证码文本字符间距 默认为2
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
-        // 验证码文本字符长度 默认为5
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
-        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
-        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
-        // 验证码噪点颜色 默认为Color.BLACK
-        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
-        // 干扰实现类
-        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
-        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
-        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
-        Config config = new Config(properties);
-        defaultKaptcha.setConfig(config);
-        return defaultKaptcha;
-    }
-}

+ 0 - 76
fs-company-app/src/main/java/com/fs/framework/config/KaptchaTextCreator.java

@@ -1,76 +0,0 @@
-package com.fs.framework.config;
-
-import com.google.code.kaptcha.text.impl.DefaultTextCreator;
-
-import java.util.Random;
-
-/**
- * 验证码文本生成器
- * 
-
- */
-public class KaptchaTextCreator extends DefaultTextCreator
-{
-    private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
-
-    @Override
-    public String getText()
-    {
-        Integer result = 0;
-        Random random = new Random();
-        int x = random.nextInt(10);
-        int y = random.nextInt(10);
-        StringBuilder suChinese = new StringBuilder();
-        int randomoperands = (int) Math.round(Math.random() * 2);
-        if (randomoperands == 0)
-        {
-            result = x * y;
-            suChinese.append(CNUMBERS[x]);
-            suChinese.append("*");
-            suChinese.append(CNUMBERS[y]);
-        }
-        else if (randomoperands == 1)
-        {
-            if (!(x == 0) && y % x == 0)
-            {
-                result = y / x;
-                suChinese.append(CNUMBERS[y]);
-                suChinese.append("/");
-                suChinese.append(CNUMBERS[x]);
-            }
-            else
-            {
-                result = x + y;
-                suChinese.append(CNUMBERS[x]);
-                suChinese.append("+");
-                suChinese.append(CNUMBERS[y]);
-            }
-        }
-        else if (randomoperands == 2)
-        {
-            if (x >= y)
-            {
-                result = x - y;
-                suChinese.append(CNUMBERS[x]);
-                suChinese.append("-");
-                suChinese.append(CNUMBERS[y]);
-            }
-            else
-            {
-                result = y - x;
-                suChinese.append(CNUMBERS[y]);
-                suChinese.append("-");
-                suChinese.append(CNUMBERS[x]);
-            }
-        }
-        else
-        {
-            result = x + y;
-            suChinese.append(CNUMBERS[x]);
-            suChinese.append("+");
-            suChinese.append(CNUMBERS[y]);
-        }
-        suChinese.append("=?@" + result);
-        return suChinese.toString();
-    }
-}

+ 0 - 20
fs-company-app/src/main/java/com/fs/framework/config/WebSocketConfig.java

@@ -1,20 +0,0 @@
-package com.fs.framework.config;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.socket.server.standard.ServerEndpointExporter;
-
-@Configuration
-public class WebSocketConfig {
-    /**
-     * ServerEndpointExporter 作用
-     *
-     * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
-     *
-     * @return
-     */
-    @Bean
-    public ServerEndpointExporter serverEndpointExporter() {
-        return new ServerEndpointExporter();
-    }
-}

+ 0 - 56
fs-company-app/src/main/java/com/fs/framework/manager/AsyncManager.java

@@ -1,56 +0,0 @@
-package com.fs.framework.manager;
-
-import com.fs.common.utils.Threads;
-import com.fs.common.utils.spring.SpringUtils;
-
-import java.util.TimerTask;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/**
- * 异步任务管理器
- * 
-
- */
-public class AsyncManager
-{
-    /**
-     * 操作延迟10毫秒
-     */
-    private final int OPERATE_DELAY_TIME = 10;
-
-    /**
-     * 异步操作任务调度线程池
-     */
-    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
-
-    /**
-     * 单例模式
-     */
-    private AsyncManager(){}
-
-    private static AsyncManager me = new AsyncManager();
-
-    public static AsyncManager me()
-    {
-        return me;
-    }
-
-    /**
-     * 执行任务
-     * 
-     * @param task 任务
-     */
-    public void execute(TimerTask task)
-    {
-        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
-    }
-
-    /**
-     * 停止任务线程池
-     */
-    public void shutdown()
-    {
-        Threads.shutdownAndAwaitTermination(executor);
-    }
-}

+ 0 - 40
fs-company-app/src/main/java/com/fs/framework/manager/ShutdownManager.java

@@ -1,40 +0,0 @@
-package com.fs.framework.manager;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PreDestroy;
-
-/**
- * 确保应用退出时能关闭后台线程
- *
-
- */
-@Component
-public class ShutdownManager
-{
-    private static final Logger logger = LoggerFactory.getLogger("sys-user");
-
-    @PreDestroy
-    public void destroy()
-    {
-        shutdownAsyncManager();
-    }
-
-    /**
-     * 停止异步执行任务
-     */
-    private void shutdownAsyncManager()
-    {
-        try
-        {
-            logger.info("====关闭后台任务任务线程池====");
-            AsyncManager.me().shutdown();
-        }
-        catch (Exception e)
-        {
-            logger.error(e.getMessage(), e);
-        }
-    }
-}

+ 0 - 103
fs-company-app/src/main/java/com/fs/framework/manager/factory/AsyncFactory.java

@@ -1,103 +0,0 @@
-package com.fs.framework.manager.factory;
-
-import com.fs.common.constant.Constants;
-import com.fs.common.utils.LogUtils;
-import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.ip.AddressUtils;
-import com.fs.common.utils.ip.IpUtils;
-import com.fs.common.utils.spring.SpringUtils;
-import com.fs.system.domain.SysLogininfor;
-import com.fs.system.domain.SysOperLog;
-import com.fs.system.service.ISysLogininforService;
-import com.fs.system.service.ISysOperLogService;
-import eu.bitwalker.useragentutils.UserAgent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.TimerTask;
-
-/**
- * 异步工厂(产生任务用)
- * 
-
- */
-public class AsyncFactory
-{
-    private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
-
-    /**
-     * 记录登录信息
-     * 
-     * @param username 用户名
-     * @param status 状态
-     * @param message 消息
-     * @param args 列表
-     * @return 任务task
-     */
-    public static TimerTask recordLogininfor(final String username, final String status, final String message,
-            final Object... args)
-    {
-        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
-        final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
-        return new TimerTask()
-        {
-            @Override
-            public void run()
-            {
-                String address = AddressUtils.getRealAddressByIP(ip);
-                StringBuilder s = new StringBuilder();
-                s.append(LogUtils.getBlock(ip));
-                s.append(address);
-                s.append(LogUtils.getBlock(username));
-                s.append(LogUtils.getBlock(status));
-                s.append(LogUtils.getBlock(message));
-                // 打印信息到日志
-                sys_user_logger.info(s.toString(), args);
-                // 获取客户端操作系统
-                String os = userAgent.getOperatingSystem().getName();
-                // 获取客户端浏览器
-                String browser = userAgent.getBrowser().getName();
-                // 封装对象
-                SysLogininfor logininfor = new SysLogininfor();
-                logininfor.setUserName(username);
-                logininfor.setIpaddr(ip);
-                logininfor.setLoginLocation(address);
-                logininfor.setBrowser(browser);
-                logininfor.setOs(os);
-                logininfor.setMsg(message);
-                // 日志状态
-                if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
-                {
-                    logininfor.setStatus(Constants.SUCCESS);
-                }
-                else if (Constants.LOGIN_FAIL.equals(status))
-                {
-                    logininfor.setStatus(Constants.FAIL);
-                }
-                // 插入数据
-                SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
-            }
-        };
-    }
-
-    /**
-     * 操作日志记录
-     * 
-     * @param operLog 操作日志信息
-     * @return 任务task
-     */
-    public static TimerTask recordOper(final SysOperLog operLog)
-    {
-        return new TimerTask()
-        {
-            @Override
-            public void run()
-            {
-                // 远程查询操作地点
-                operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
-                SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
-            }
-        };
-    }
-}

+ 1 - 2
fs-company-app/src/main/resources/application.yml

@@ -5,5 +5,4 @@ server:
 # Spring配置
 spring:
   profiles:
-#    active: dev
-    active: druid-sxjz
+    active: druid-fcky-test

+ 10 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyController.java

@@ -15,8 +15,10 @@ import com.fs.company.vo.CompanyNameVO;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import com.fs.his.vo.OptionsVO;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -109,4 +111,12 @@ public class CompanyController extends BaseController
         return getDataTable(list);
     }
 
+    @ApiOperation("配置销售审核")
+    @PostMapping(value = "/configUserCheck")
+    public R configUserCheck(boolean userIsDefaultBlack) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyService.configUserCheck(loginUser.getCompany().getCompanyId(), userIsDefaultBlack ? 1 : 0);
+        return R.ok();
+    }
+
 }

+ 135 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyDomainBindController.java

@@ -0,0 +1,135 @@
+package com.fs.company.controller.company;
+
+import java.util.List;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.api.R;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.param.CompanyDomainBindParam;
+import com.fs.company.vo.CompanyDomainBindVo;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyDomainBind;
+import com.fs.company.service.ICompanyDomainBindService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 域名绑定销售公司Controller
+ * 
+ * @author fs
+ * @date 2025-06-17
+ */
+@RestController
+@RequestMapping("/company/companyDomainBind")
+public class CompanyDomainBindController extends BaseController
+{
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ICompanyDomainBindService companyDomainBindService;
+
+    /**
+     * 查询域名绑定销售公司列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyDomainBindParam companyDomainBind)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyDomainBind.setCompanyId(loginUser.getUser().getCompanyId());
+        startPage();
+        List<CompanyDomainBindVo> list = companyDomainBindService.selectCompanyDomainBindList(companyDomainBind);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出域名绑定销售公司列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:export')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyDomainBindParam companyDomainBind)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyDomainBind.setCompanyId(loginUser.getUser().getCompanyId());
+        List<CompanyDomainBindVo> list = companyDomainBindService.selectCompanyDomainBindList(companyDomainBind);
+        ExcelUtil<CompanyDomainBindVo> util = new ExcelUtil<CompanyDomainBindVo>(CompanyDomainBindVo.class);
+        return util.exportExcel(list, "域名绑定销售公司数据");
+    }
+
+    /**
+     * 获取域名绑定销售公司详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(companyDomainBindService.selectCompanyDomainBindById(id));
+    }
+
+    /**
+     * 新增域名绑定销售公司
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:add')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyDomainBind companyDomainBind)
+    {
+        return toAjax(companyDomainBindService.insertCompanyDomainBind(companyDomainBind));
+    }
+
+    /**
+     * 修改域名绑定销售公司
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:edit')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyDomainBind companyDomainBind)
+    {
+        return toAjax(companyDomainBindService.updateCompanyDomainBind(companyDomainBind));
+    }
+
+    /**
+     * 删除域名绑定销售公司
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyDomainBind:remove')")
+    @Log(title = "域名绑定销售公司", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(companyDomainBindService.deleteCompanyDomainBindByIds(ids));
+    }
+
+    /**
+     * 域名批量绑定
+     **/
+    @PreAuthorize("@ss.hasPermi('company:companyDomain:domainBatchBinding')")
+    @PostMapping("/domainBatchBinding")
+    public R domainBatchBinding(@RequestBody CompanyDomainBindParam param) {
+        if (ObjectUtil.isEmpty(param.getCompanyIds()) || param.getCompanyIds().isEmpty()) {
+            return R.failed("绑定失败,至少有一条域名分配信息!");
+        }
+        if (ObjectUtil.isEmpty(param.getCompanyUserIds()) || param.getCompanyUserIds().isEmpty()) {
+            return R.failed("绑定失败,至少有一条分配人员信息!");
+        }
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setUserId(loginUser.getUser().getUserId().toString());
+        return companyDomainBindService.domainBatchBinding(param);
+    }
+}

+ 108 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyDomainBindUserController.java

@@ -0,0 +1,108 @@
+package com.fs.company.controller.company;
+
+import java.util.List;
+
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.param.CompanyDomainBindUserParam;
+import com.fs.company.vo.CompanyDomainBindUserVo;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyDomainBindUser;
+import com.fs.company.service.ICompanyDomainBindUserService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 企业域名分配中间表Controller
+ *
+ * @author fs
+ * @date 2025-06-19
+ */
+@RestController
+@RequestMapping("/company/companyBindUser")
+public class CompanyDomainBindUserController extends BaseController {
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ICompanyDomainBindUserService companyDomainBindUserService;
+
+    /**
+     * 查询企业域名分配中间表列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyDomainBindUserParam companyDomainBindUser) {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyDomainBindUser.setCompanyId(loginUser.getUser().getCompanyId());
+        List<CompanyDomainBindUserVo> list = companyDomainBindUserService.selectCompanyDomainBindUserList(companyDomainBindUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出企业域名分配中间表列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:export')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyDomainBindUserParam companyDomainBindUser) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyDomainBindUser.setCompanyId(loginUser.getUser().getCompanyId());
+        List<CompanyDomainBindUserVo> list = companyDomainBindUserService.selectCompanyDomainBindUserList(companyDomainBindUser);
+        ExcelUtil<CompanyDomainBindUserVo> util = new ExcelUtil<CompanyDomainBindUserVo>(CompanyDomainBindUserVo.class);
+        return util.exportExcel(list, "企业域名分配中间表数据");
+    }
+
+    /**
+     * 获取企业域名分配中间表详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(companyDomainBindUserService.selectCompanyDomainBindUserById(id));
+    }
+
+    /**
+     * 新增企业域名分配中间表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:add')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CompanyDomainBindUser companyDomainBindUser) {
+        return toAjax(companyDomainBindUserService.insertCompanyDomainBindUser(companyDomainBindUser));
+    }
+
+    /**
+     * 修改企业域名分配中间表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:edit')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyDomainBindUser companyDomainBindUser) {
+        return toAjax(companyDomainBindUserService.updateCompanyDomainBindUser(companyDomainBindUser));
+    }
+
+    /**
+     * 删除企业域名分配中间表
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyBindUser:remove')")
+    @Log(title = "企业域名分配中间表", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(companyDomainBindUserService.deleteCompanyDomainBindUserByIds(ids));
+    }
+}

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

@@ -44,11 +44,14 @@ public class FsCourseAnswerLogsController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(FsCourseAnswerLogsParam param)
     {
-        startPage();
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
             param.setPhone(encryptPhone(param.getPhoneMk()));
         }
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
 
+        startPage();
         List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVO(param);
         return getDataTable(list);
     }
@@ -60,7 +63,7 @@ public class FsCourseAnswerLogsController extends BaseController
     @GetMapping("/myList")
     public TableDataInfo myList(FsCourseAnswerLogsParam param)
     {
-        startPage();
+
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
@@ -68,6 +71,7 @@ public class FsCourseAnswerLogsController extends BaseController
         if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
             param.setPhone(encryptPhone(param.getPhoneMk()));
         }
+        startPage();
 
         List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVO(param);
         return getDataTable(list);

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

@@ -0,0 +1,256 @@
+package com.fs.company.controller.course;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.domain.FsUserCoursePeriodDays;
+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.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.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.his.vo.OptionsVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 会员营期Controller
+ *
+ * @author fs
+ * @date 2025-04-11
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/course/period")
+@Slf4j
+public class FsUserCoursePeriodController extends BaseController {
+    private final IFsUserCoursePeriodService fsUserCoursePeriodService;
+    private final IFsUserCoursePeriodDaysService fsUserCoursePeriodDaysService;
+    private final IFsUserCourseVideoRedPackageService fsUserCourseVideoRedPackageService;
+    private final TokenService tokenService;
+
+    /**
+     * 查询会员营期列表
+     */
+//    @PreAuthorize("@ss.hasPermi('course:period:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsUserCoursePeriod.setCompanyId(loginUser.getCompany().getCompanyId().toString());
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsUserCoursePeriodList(fsUserCoursePeriod);
+        return getDataTable(list);
+    }
+
+    @PostMapping("/page")
+    @ApiOperation("自定义查询主列表分页")
+    public R pageList(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
+    {
+//        startPage();
+        PageHelper.startPage(fsUserCoursePeriod.getPageNum(), fsUserCoursePeriod.getPageSize());
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsUserCoursePeriod.setCompanyIdList(Collections.singletonList(loginUser.getCompany().getCompanyId()));
+        List<FsUserCoursePeriodVO> list = fsUserCoursePeriodService.selectFsUserCoursePeriodPage(fsUserCoursePeriod);
+        PageInfo<FsUserCoursePeriodVO> pageInfo = new PageInfo<>(list);
+        Map<String, Object> result = new HashMap<>();
+        result.put("rows", pageInfo.getList());
+        result.put("total", pageInfo.getTotal());
+        return R.ok(result);
+//        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员营期列表
+     */
+//    @PreAuthorize("@ss.hasPermi('course:period:export')")
+    @Log(title = "会员营期", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsUserCoursePeriod.setCompanyId(loginUser.getCompany().getCompanyId().toString());
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsUserCoursePeriodList(fsUserCoursePeriod);
+        ExcelUtil<FsUserCoursePeriod> util = new ExcelUtil<FsUserCoursePeriod>(FsUserCoursePeriod.class);
+        return util.exportExcel(list, "userCoursePeriod");
+    }
+
+    /**
+     * 获取会员营期详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:period:query')")
+    @GetMapping(value = "/{periodId}")
+    public AjaxResult getInfo(@PathVariable("periodId") Long periodId)
+    {
+        return AjaxResult.success(fsUserCoursePeriodService.selectFsUserCoursePeriodById(periodId));
+    }
+
+    /**
+     * 新增会员营期
+     */
+    @PreAuthorize("@ss.hasPermi('course:period:add')")
+    @Log(title = "会员营期", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsUserCoursePeriod.setCompanyId(loginUser.getCompany().getCompanyId().toString());
+        return toAjax(fsUserCoursePeriodService.insertFsUserCoursePeriod(fsUserCoursePeriod));
+    }
+
+    /**
+     * 修改会员营期
+     */
+    @PreAuthorize("@ss.hasPermi('course:period:edit')")
+    @Log(title = "会员营期", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsUserCoursePeriod.setCompanyId(loginUser.getCompany().getCompanyId().toString());
+        return toAjax(fsUserCoursePeriodService.updateFsUserCoursePeriod(fsUserCoursePeriod));
+    }
+
+    /**
+     * 删除会员营期
+     */
+    @PreAuthorize("@ss.hasPermi('course:period:remove')")
+    @Log(title = "会员营期", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{periodIds}")
+    public AjaxResult remove(@PathVariable Long[] periodIds)
+    {
+        return toAjax(fsUserCoursePeriodService.deleteFsUserCoursePeriodByIds(periodIds));
+    }
+
+    @GetMapping("/getDays")
+    public TableDataInfo getDays(FsUserCoursePeriodDays fsUserCoursePeriodDays){
+        startPage();
+        FsUserCoursePeriod period = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsUserCoursePeriodDays.getPeriodId());
+        fsUserCoursePeriodDays.setMaxDate(LocalDate.now().plusDays(period.getMaxViewNum()));
+        List<FsUserCoursePeriodDays> list = fsUserCoursePeriodDaysService.selectFsUserCoursePeriodDaysList(fsUserCoursePeriodDays);
+        return getDataTable(list);
+    }
+
+    @PostMapping("/addCourse")
+    public R addCourse(@RequestBody FsUserCoursePeriodDays entity){
+        return fsUserCoursePeriodDaysService.addCourse(entity);
+    }
+    @PostMapping("/updateCourseTime")
+    public R updateCourseTime(@RequestBody UpdateCourseTimeVo vo){
+        return fsUserCoursePeriodDaysService.updateCourseTime(vo);
+    }
+    @PostMapping("/updateCourseDate")
+    public R updateCourseDate(@RequestBody UpdateCourseTimeVo vo){
+        return fsUserCoursePeriodDaysService.updateCourseDate(vo);
+    }
+    @PostMapping("/updateListCourseData")
+    public R updateListCourseData(@RequestBody List<FsUserCoursePeriodDays> entity){
+        return fsUserCoursePeriodDaysService.updateListCourseData(entity);
+    }
+
+    @ApiOperation("根据营期id获取公司红包金额列表(暂废弃)")
+    @GetMapping("/companyList")
+    public R getCompanyByPeriod(Long periodId, Long videoId) {
+        List<CompanyRedPacketParam> companyList = fsUserCoursePeriodDaysService.getCompanyByPeriod(periodId, videoId);
+        return R.ok().put("data", companyList);
+    }
+
+    @ApiOperation("获取设置红包金额列表")
+    @GetMapping("/redPacketList")
+    public R getPeriodRedPacketList(Long periodId, Long companyId) {
+        List<PeriodRedPacketVO> periodRedPacketList = fsUserCoursePeriodDaysService.getPeriodRedPacketList(periodId, companyId);
+        return R.ok().put("data", periodRedPacketList);
+    }
+
+    @ApiOperation("按课程批量保存设置红包金额")
+    @PostMapping("/batchRedPacket")
+    public R batchRedPacketMoney(@RequestBody List<FsUserCourseVideoRedPackage> videoRedPackageList) {
+        try {
+            fsUserCourseVideoRedPackageService.batchSaveCompanyRedPackage(videoRedPackageList);
+        } catch (Exception e) {
+            logger.error("按课程批量保存设置红包金额-失败!,营期id:{}", videoRedPackageList.get(0).getPeriodId());
+            return R.error("保存失败!");
+        }
+        return R.ok();
+    }
+
+    @ApiOperation("按营期批量保存设置红包金额")
+    @PostMapping("/batchRedPacket/byPeriod")
+    public R batchRedPacketByPeriod(@RequestBody List<FsBatchPeriodRedPackageParam> periodRedPackageList) {
+        try {
+            fsUserCourseVideoRedPackageService.batchRedPacketByPeriod(periodRedPackageList);
+        } catch (Exception e) {
+            logger.error("按营期批量保存设置红包金额-失败!,入参:{}", periodRedPackageList);
+            return R.error("保存失败!");
+        }
+        return R.ok();
+    }
+
+    @PostMapping("/periodCount")
+    @ApiOperation("营期统计")
+    public R periodCourseCount(@RequestBody PeriodCountParam param) {
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        FsUserCoursePeriod period = fsUserCoursePeriodService.selectFsUserCoursePeriodById(param.getPeriodId());
+        param.setMaxDate(LocalDate.now().plusDays(period.getMaxViewNum()));
+        List<FsPeriodCountVO> list = fsUserCoursePeriodDaysService.periodCourseCount(param);
+        PageInfo<FsPeriodCountVO> pageInfo = new PageInfo<>(list);
+        Map<String, Object> result = new HashMap<>();
+        result.put("rows", pageInfo.getList());
+        result.put("total", pageInfo.getTotal());
+        return R.ok(result);
+    }
+
+    @GetMapping("/getPeriodListLikeName")
+    public R getPeriodListLikeName(@RequestParam(required = false) String name,
+                                   @RequestParam(required = false) Long campId,
+                                   @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                   @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        Map<String,Object> params = new HashMap<>();
+        params.put("name", name);
+        params.put("campId", campId);
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<OptionsVO> periodList = fsUserCoursePeriodDaysService.selectPeriodListByMap(params);
+        return R.ok().put("data", new PageInfo<>(periodList));
+    }
+
+    @ApiOperation("营期课程-上移/下移")
+    @PutMapping("/courseMove")
+    public R periodCourseMove(Long id, Long targetId) {
+        return fsUserCoursePeriodDaysService.periodCourseMove(id, targetId);
+    }
+
+    @ApiOperation("结束营期")
+    @PostMapping("/closePeriod")
+    public R closePeriod(Long id) {
+        fsUserCoursePeriodService.closePeriod(id);
+        return R.ok();
+    }
+
+}

+ 13 - 4
fs-company/src/main/java/com/fs/company/controller/store/FsUserController.java

@@ -3,6 +3,7 @@ package com.fs.company.controller.store;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
@@ -20,12 +21,16 @@ import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.UserVo;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.vo.h5.FsUserPageListVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import static com.fs.his.utils.PhoneUtil.decryptAutoPhoneMk;
 import static com.fs.his.utils.PhoneUtil.encryptPhone;
@@ -226,12 +231,16 @@ public class FsUserController extends BaseController
     @PreAuthorize("@ss.hasPermi('users:user:darkRoomList')")
     @GetMapping("/darkRoomList")
     @ApiOperation("小黑屋")
-    public TableDataInfo darkRoomList(FsUserPageListParam param) {
+    public R darkRoomList(FsUserPageListParam param) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setUserId(loginUser.getUser().getUserId());
-        startPage();
-        List<FsUserPageListVO> list = fsUserService.selectFsUserPageList(param);
-        return getDataTable(list);
+//        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("rows", fsUserPageListVOPageInfo.getList());
+        map.put("total", fsUserPageListVOPageInfo.getList().size());
+        return R.ok(map);
     }
 
     @PreAuthorize("@ss.hasPermi('users:user:enabledUsers')")

+ 1 - 1
fs-company/src/main/java/com/fs/user/FsUserAdminController.java

@@ -38,7 +38,7 @@ public class FsUserAdminController extends BaseController {
     @PostMapping("/list")
     @ApiOperation("会员列表(与移动端使用的相同查询)")
     public TableDataInfo pageList(@RequestBody FsUserPageListParam param) {
-//        startPage();
+        //startPage();
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId(loginUser.getCompany().getCompanyId());

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

@@ -3,7 +3,7 @@ server:
 # Spring配置
 spring:
   profiles:
-    active: dev
+    active: druid-fcky-test
 #    active: druid-jzzx
 #    active: druid-hdt
 #    active: druid-sxjz

+ 0 - 3
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -5,16 +5,13 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.uuid.IdUtils;
 import com.fs.fastGpt.service.AiHookService;
 import com.fs.qw.domain.QwUser;
-import com.fs.qw.domain.QwUserVideo;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwUserVoiceLogService;
-import com.fs.qw.vo.QwMessageListVO;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkService;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
 import org.json.JSONObject;
-import org.omg.CORBA.LongHolder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 

+ 26 - 22
fs-qw-task/src/main/java/com/fs/app/task/CourseWatchLogScheduler.java

@@ -23,6 +23,9 @@ public class CourseWatchLogScheduler {
     private final AtomicBoolean isRunning2 = new AtomicBoolean(false);
 
     private final AtomicBoolean isRunning3 = new AtomicBoolean(false);
+
+    private final AtomicBoolean isRunning4 = new AtomicBoolean(false);
+
     @Autowired
     private FsCourseWatchLogMapper courseWatchLogMapper;
 
@@ -44,9 +47,6 @@ public class CourseWatchLogScheduler {
     @Autowired
     private SopLogsTaskService sopLogsTaskService;
 
-//    @Autowired
-//    private IFsCourseRedPacketRetryService redPacketRetryService;
-
 //    // 定时任务批量更新到数据库
 //    @Scheduled(fixedRate = 60000) // 每分钟执行一次
 //    public void scheduleBatchUpdateToDatabase() {
@@ -56,9 +56,9 @@ public class CourseWatchLogScheduler {
 
 
     /**
-     * 检查看课状态(先导课只有30秒)
+     * 检查看课状态
      */
-    @Scheduled(fixedRate = 20000) // 每15秒执行一次
+    @Scheduled(fixedRate = 60000) // 每分钟执行一次
     public void checkWatchStatus() {
         // 尝试设置标志为 true,表示任务开始执行
         if (!isRunning1.compareAndSet(false, true)) {
@@ -84,7 +84,7 @@ public class CourseWatchLogScheduler {
     /**
      * 创建完课消息
      */
-    @Scheduled(cron = "0 */5 * * * ?") // 每五分钟执行一次
+    @Scheduled(fixedRate = 300000) // 每五分钟执行一次
     public void createCourseFinishMsg() {
         // 尝试设置标志为 true,表示任务开始执行
         if (!isRunning3.compareAndSet(false, true)) {
@@ -101,7 +101,6 @@ public class CourseWatchLogScheduler {
         } finally {
             // 重置标志为 false,表示任务已完成
             isRunning3.set(false);
-
         }
 
     }
@@ -115,7 +114,7 @@ public class CourseWatchLogScheduler {
     /**
      * 每天删除过期短链
      */
-    @Scheduled(cron = "0 5 0 * * ?")  // 0点5分0秒执行
+    @Scheduled(cron = "0 0 0 * * ?")  // 0点0分0秒执行
     public void delCourseExpireLink() {
         try {
             log.info("删除过期短链 - 定时任务开始");
@@ -127,21 +126,26 @@ public class CourseWatchLogScheduler {
 
     }
 
-    /**
-     * 红包补发定时任务
-     */
-//    @Scheduled(cron = "0 */5 * * * ?")  //每5分钟执行一次
-//    public void redPacketRetry() {
-//        try {
-//            log.info("红包补发任务开始");
-//            redPacketRetryService.redPacketRetry();
-//            log.info("红包补发任务开始");
-//        } catch (Exception e) {
-//            log.error("红包补发 - 定时任务执行失败", e);
-//        }
-
-//    }
+    @Scheduled(fixedRate = 60000) // 每分钟执行一次
+    public void checkFsUserWatchStatus() {
+        // 尝试设置标志为 true,表示任务开始执行
+        if (!isRunning4.compareAndSet(false, true)) {
+            log.warn("WXH5-检查会员看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
+            return;
+        }
+        try {
+            log.info("WXH5-检查会员看课中任务执行>>>>>>>>>>>>");
+            courseWatchLogService.scheduleUpdateDurationToDatabase();
+            courseWatchLogService.checkFsUserWatchStatus();
+            log.info("WXH5-检查会员看课中任务执行完成>>>>>>>>>>>>");
+        }catch (Exception e) {
+            log.error("WXH5-检查会员看课中任务执行完成 - 定时任务执行失败", e);
+        } finally {
+            // 重置标志为 false,表示任务已完成
+            isRunning4.set(false);
+        }
 
+    }
 
 
 

+ 37 - 0
fs-qw-task/src/main/java/com/fs/app/task/UserCourseWatchCountTask.java

@@ -0,0 +1,37 @@
+package com.fs.app.task;
+
+import com.fs.store.service.IFsUserCourseCountService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class UserCourseWatchCountTask {
+    @Autowired
+    private IFsUserCourseCountService userCourseCountService;
+
+
+    /**
+     * 每天两点进行会员看课统计
+     */
+    @Scheduled(cron = "0 0 2 * * ?")  // 2点0分0秒执行
+    public void userCourseCountTask() {
+        try {
+            log.info("==============会员看课统计任务执行===============开始");
+            long startTime = System.currentTimeMillis();
+
+            userCourseCountService.insertFsUserCourseCountTask();
+
+            log.info("会员看课统计任务执行==============结束");
+            long endTime = System.currentTimeMillis();
+            log.info("会员看课统计任务执行----------执行时长:{}", (endTime - startTime));
+        } catch (Exception e) {
+            log.error("会员看课统计任务执行----------定时任务执行失败", e);
+        }
+
+    }
+
+
+}

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

@@ -889,7 +889,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                             createParam.setCompanyUserId(Long.parseLong(companyUserId));
                             createParam.setCompanyId(Long.parseLong(companyId));
                             createParam.setChatId(logVo.getChatId());
-                            createParam.setQwUserId(qwUserId);
+                            createParam.setQwUserId(Long.parseLong(qwUserId));
                             createParam.setDays(setting.getExpiresDays());
                             R createLink = courseLinkService.createRoomLinkUrl(createParam);
                             if (createLink.get("code").equals(500)) {
@@ -1016,7 +1016,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
         FsCourseLink link = new FsCourseLink();
         link.setCompanyId(Long.parseLong(companyId));
-        link.setQwUserId(qwUserId);
+        link.setQwUserId(Long.parseLong(qwUserId));
         link.setCompanyUserId(Long.parseLong(companyUserId));
         link.setVideoId(videoId.longValue());
         link.setCorpId(logVo.getCorpId());
@@ -1174,7 +1174,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
         FsCourseLink link = new FsCourseLink();
         link.setCompanyId(Long.parseLong(companyId));
-        link.setQwUserId(qwUserId);
+        link.setQwUserId(Long.parseLong(qwUserId));
         link.setCompanyUserId(Long.parseLong(companyUserId));
         link.setVideoId(videoId.longValue());
         link.setCorpId(corpId);
@@ -1230,7 +1230,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
         FsCourseLink link = new FsCourseLink();
         link.setCompanyId(Long.parseLong(companyId));
-        link.setQwUserId(qwUserId);
+        link.setQwUserId(Long.parseLong(qwUserId));
         link.setCompanyUserId(Long.parseLong(companyUserId));
         link.setVideoId(videoId.longValue());
         link.setCorpId(logVo.getCorpId());
@@ -1293,7 +1293,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         watchLog.setVideoId(videoId != null ? videoId.longValue() : null);
         watchLog.setQwExternalContactId(externalId != null ? Long.valueOf(externalId) : null);
         watchLog.setSendType(2);
-        watchLog.setQwUserId(qwUserId);
+        watchLog.setQwUserId(Long.parseLong(qwUserId));
         watchLog.setSopId(sopLogs.getSopId());
         watchLog.setDuration(0L);
         watchLog.setCourseId(courseId != null ? courseId.longValue() : null);

+ 38 - 18
fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java

@@ -1,10 +1,15 @@
 package com.fs.app.controller;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
+import com.fs.course.domain.FsUserCourse;
+import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.param.FsCourseLinkMiniParam;
+import com.fs.course.param.FsCourseLinkRoomParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.service.IFsCourseLinkService;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseVideoService;
@@ -44,26 +49,10 @@ public class ApisFsUserCourseVideoController extends BaseController {
     @Autowired
     private IQwExternalContactService qwExternalContactService;
 
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
 
 
-    @PostMapping("/pageList")
-    @ApiOperation("课程分页列表")
-    public R list(@RequestBody UserCourseVideoPageParam param) {
-
-        QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(), param.getQwUserId().trim());
-
-        if (qwUser == null || qwUser.getCompanyId() == null) {
-            return R.error("员工未绑定 销售公司 或 未获取到员工信息,请重试!");
-        }
-        param.setCompanyId(qwUser.getCompanyId());
-
-
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.pageListCourseVideo(param);
-        PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
-        return R.ok().put("data",pageInfo);
-    }
-
 
     @ApiOperation("课程视频详情")
     @GetMapping(value = "/videoDetails")
@@ -158,4 +147,35 @@ public class ApisFsUserCourseVideoController extends BaseController {
         return fsUserCourseVideoService.createCartLink(param);
     }
 
+    @GetMapping("/createRoomLink")
+    @ApiOperation("创建发群链接")
+    public R createRoomLink(FsCourseLinkRoomParam param) {
+        QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(),param.getQwUserId().trim());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return R.error("无权限");
+        }
+        FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
+        createParam.setCourseId(param.getCourseId());
+        createParam.setVideoId(param.getVideoId());
+        createParam.setCorpId(param.getCorpId());
+        createParam.setCompanyUserId(qwUser.getCompanyUserId());
+        createParam.setCompanyId(qwUser.getCompanyId());
+        createParam.setQwUserId(qwUser.getId());
+        String linkUrl;
+        R createLink = courseLinkService.createRoomLinkUrl(createParam);
+        if (createLink.get("code").equals(500)){
+            return R.error("链接生成失败!");
+        }
+        linkUrl = (String) createLink.get("url");
+
+        FsUserCourse course = fsUserCourseService.selectFsUserCourseByCourseId(param.getCourseId());
+
+        JSONObject news = new JSONObject(true); // true 表示保持字段顺序
+        news.put("link", linkUrl);
+        news.put("title", course.getCourseName());
+        news.put("desc", param.getTitle());
+        news.put("imgUrl", course.getImgUrl());
+        return R.ok().put("news",news);
+    }
+
 }

+ 1 - 45
fs-qwhook-sop/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -53,21 +53,6 @@ public class FsUserCourseVideoController {
     @Autowired
     private IQwExternalContactService qwExternalContactService;
 
-    @GetMapping("/pageList")
-    @ApiOperation("课程分页列表")
-    public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> list(UserCourseVideoPageParam param) {
-
-        QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(), param.getQwUserId().trim());
-
-        if (qwUser==null||qwUser.getCompanyId()==null){
-            return ResponseResult.fail(500,"无权限");
-        }
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        param.setCompanyId(qwUser.getCompanyId());
-        List<FsUserCourseVideoPageListVO> list = fsUserCourseVideoService.pageListCourseVideo(param);
-        PageInfo<FsUserCourseVideoPageListVO> pageInfo = new PageInfo<>(list);
-        return ResponseResult.ok(pageInfo);
-    }
 
     @ApiOperation("课程视频详情")
     @GetMapping(value = "/videoDetails")
@@ -75,35 +60,6 @@ public class FsUserCourseVideoController {
         return fsUserCourseVideoService.getVideoDetails(videoId);
     }
 
-    @GetMapping("/courseList")
-    @ApiOperation("获取课程下拉列表")
-    public ResponseResult<PageInfo<FsUserCourseListVO>> getAllCourseList(FsUserCourseListParam param) {
-        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
-        if (qwUser==null||qwUser.getCompanyId()==null){
-            return ResponseResult.fail(500,"无权限");
-        }
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        param.setCompanyId(qwUser.getCompanyId());
-        List<FsUserCourseListVO> fsUserCourseList = fsUserCourseService.getFsUserCourseList(param);
-        PageInfo<FsUserCourseListVO> pageInfo = new PageInfo<>(fsUserCourseList);
-        return ResponseResult.ok(pageInfo);
-    }
-
-    @GetMapping("/videoList")
-    @ApiOperation("获取视频下拉列表")
-    public ResponseResult<PageInfo<FsUserVideoListVO>> getAllVideoList(UserCourseVideoPageParam param) {
-        QwUser qwUser = qwUserService.getByQwUserIdAndCorId(param.getQwUserId(), param.getCorpId());
-        if (qwUser==null||qwUser.getCompanyId()==null){
-            return ResponseResult.fail(500,"无权限");
-        }
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        param.setCompanyId(qwUser.getCompanyId());
-        List<FsUserVideoListVO> listCourseVideo = fsUserCourseVideoService.getListCourseVideo(param);
-        PageInfo<FsUserVideoListVO> result = new PageInfo<>(listCourseVideo);
-        return ResponseResult.ok(result);
-    }
-
-
     @PostMapping("/getFsCourseListBySidebar")
     @ApiOperation("获取视频课程下拉列表 侧边栏")
     public R getFsCourseListBySidebar(@RequestBody FsCourseListBySidebarParam param) {
@@ -151,7 +107,7 @@ public class FsUserCourseVideoController {
         createParam.setCorpId(param.getCorpId());
         createParam.setCompanyUserId(qwUser.getCompanyUserId());
         createParam.setCompanyId(qwUser.getCompanyId());
-        createParam.setQwUserId(qwUser.getId().toString());
+        createParam.setQwUserId(qwUser.getId());
         String linkUrl;
         R createLink = courseLinkService.createRoomLinkUrl(createParam);
         if (createLink.get("code").equals(500)){

+ 39 - 0
fs-qwhook-sop/src/main/java/com/fs/app/controller/QwStatisticsController.java

@@ -0,0 +1,39 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.CustomException;
+import com.fs.statistics.dto.WatchCourseStatisticsDTO;
+import com.fs.statistics.param.WatchCourseStatisticsParam;
+import com.fs.statistics.service.IStatisticsService;
+import io.swagger.annotations.ApiOperation;
+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;
+
+/**
+ * 企微统计数据
+ */
+@RestController
+@RequestMapping("/app/qw/statistics")
+public class QwStatisticsController extends BaseController {
+    @Autowired
+    private IStatisticsService statisticsService;
+
+
+//    @Login
+    @PostMapping("/course/watch")
+    @ApiOperation("会员看课详情")
+    public R queryCourseWatchStatistics(@RequestBody WatchCourseStatisticsParam param) {
+        if(param.getQwExternalContactId() == null) {
+            throw new CustomException("外部联系人id为空!");
+        }
+
+        WatchCourseStatisticsDTO watchCourseStatisticsDTO = statisticsService.queryWatchCourse(param);
+
+        return R.ok().put("data",watchCourseStatisticsDTO);
+    }
+
+}

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

@@ -0,0 +1,69 @@
+package com.fs.app.controller;
+
+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.param.ExternalContactDetailsParam;
+import com.fs.qw.service.IQwExternalContactInfoService;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.vo.ExternalContactDetailsVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = "企微会员相关接口")
+@RestController
+@RequestMapping("/app/qw/user")
+public class QwUserController extends BaseController {
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IQwExternalContactInfoService qwExternalContactInfoService;
+
+    @GetMapping("/details")
+    @ApiOperation("会员看课详情")
+    public R getUserDetails(@ApiParam(value = "外部联系人id", required = true) @RequestParam Long contactId,
+                            @ApiParam(value = "时间tab,不传表示查询全部,分别是:今天、昨天、前天、近七天", required = true) String dateTag) {
+        ExternalContactDetailsParam param = new ExternalContactDetailsParam();
+        param.setUserId(getUserId());
+        param.setContactId(contactId);
+        param.setDateTag(dateTag);
+        ExternalContactDetailsVO userDetails = qwExternalContactService.getUserDetails(param);
+        Map<String, Object> map = new HashMap<>();
+        map.put("userDetails", userDetails);
+        return R.ok(map);
+    }
+
+
+    @GetMapping("/getQwUserInfo")
+    @ApiOperation("获取企微用户信息")
+    public R getQwUserInfo(@RequestParam(value = "qwExternalContactId",required = false) Long qwExternalContactId){
+        if(qwExternalContactId == null) {
+            throw new CustomException("企微外部联系人id不能为空!");
+        }
+
+        QwExternalContactInfo qwExternalContactInfo = qwExternalContactInfoService.selectQwExternalContactInfoByExternalContactId(qwExternalContactId);
+        return R.ok().put("data",qwExternalContactService.selectQwExternalContactById(qwExternalContactId)).put("moreInfo",qwExternalContactInfo);
+    }
+
+    @PostMapping("/updateQwUserInfo")
+    @ApiOperation("更新企微用户信息")
+    public R updateQwUserInfo(@RequestBody QwExternalContactInfo qwExternalContactInfo){
+        if(qwExternalContactInfo.getExternalContactId() == null) {
+            throw new CustomException("企微外部联系人id不能为空!");
+        }
+        qwExternalContactInfoService.updateQwExternalContactInfoByExternalContactId(qwExternalContactInfo);
+        return R.ok();
+    }
+
+}

+ 92 - 0
fs-qwhook-sop/src/main/java/com/fs/app/controller/QwWorkTaskController.java

@@ -0,0 +1,92 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.param.SelectQwWorkTaskListParam;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.service.IQwWorkTaskService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@Api(tags = "企微任务看板接口")
+@RestController
+@RequestMapping("/app/qw/workTask")
+@AllArgsConstructor
+public class QwWorkTaskController extends BaseController {
+
+    private final IQwWorkTaskService qwWorkTaskService;
+    private final IQwUserService qwUserService;
+
+    @Autowired
+    ICompanyUserService companyUserService;
+
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @PostMapping("/list")
+    @ApiOperation("企微任务看板列表")
+    public R selectQwWorkTaskList(@RequestBody SelectQwWorkTaskListParam param) {
+
+        log.info("企微任务看板列表: {}",param);
+
+        QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(), param.getQwUserId().trim());
+
+        if (qwUser == null || qwUser.getCompanyId() == null) {
+            return R.error("员工未绑定 销售公司 或 未获取到员工信息,请重试!");
+        }
+
+        param.setUserId(qwUser.getId());
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<QwWorkTask> list = qwUserService.selectQwWorkTaskList(param);
+
+        for (QwWorkTask qwWorkTask : list) {
+            List<Integer> logs = fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTask.getExtId());
+            qwWorkTask.setLogs(logs);
+        }
+
+        PageInfo<QwWorkTask> pageInfo = new PageInfo<>(list);
+        return R.ok().put("data",pageInfo);
+    }
+
+    /**
+     * 处理催课
+     */
+    @GetMapping("/updateWorkTaskStatus/{id}")
+    public R updateWorkTaskStatus(@PathVariable("id") Long id) {
+
+        try {
+            QwWorkTask qwWorkTask=new QwWorkTask();
+            qwWorkTask.setId(id);
+            qwWorkTask.setStatus(1);
+            int i = qwWorkTaskService.updateQwWorkTask(qwWorkTask);
+            if (i>0) {
+                return R.ok();
+            }else {
+                return R.error("更新失败,请重试!");
+            }
+        }catch (Exception e){
+            return R.error("更新失败,请重试!");
+        }
+    }
+
+
+
+}

+ 132 - 0
fs-qwhook/src/main/java/com/fs/app/controller/ApisCommonController.java

@@ -0,0 +1,132 @@
+package com.fs.app.controller;
+
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.course.mapper.FsUserCourseVideoMapper;
+import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
+import com.fs.his.domain.FsAppVersion;
+import com.fs.his.service.IFsAppVersionService;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.mapper.QwCompanyMapper;
+import com.fs.qw.mapper.QwExternalContactCrmMapper;
+import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.param.QwConfigSignatureParam;
+import com.fs.qw.service.IQwJsApiService;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.service.IQwUserVideoService;
+import com.fs.qw.vo.QwHookAuthVO;
+import com.fs.qwApi.param.QwExternalContactHParam;
+import com.fs.qwApi.service.QwApiService;
+import com.fs.qwHookApi.param.QwHookSendMsgParam;
+import com.fs.qwHookApi.service.QwHookApiService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+@Api("公共接口")
+@RestController
+@RequestMapping(value="/apis/app/common")
+@Slf4j
+public class ApisCommonController {
+
+    @Autowired
+    private QwHookApiService qwHookApiService;
+
+    @Autowired
+    private IQwUserService qwUserService;
+
+    @Autowired
+    QwApiService qwApiService;
+    @Autowired
+    QwCompanyMapper qwCompanyMapper;
+
+    @Autowired
+    FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    FsUserCourseVideoMapper fsUserCourseVideoMapper;
+    @Autowired
+    FsCourseWatchLogMapper   watchLogMapper;
+    @Autowired
+    IQwJsApiService qwGetJsapiTicketService;
+
+    @Autowired
+    QwUserMapper qwUserMapper;
+    @Autowired
+    FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
+
+    @Autowired
+    QwExternalContactCrmMapper qwExternalContactCrmMapper;
+    @Autowired
+    private IFsAppVersionService appVersionService;
+    @Autowired
+    RedisCache redisCache;
+
+
+    @Autowired
+    private IQwUserVideoService qwUserVideoService;
+
+
+    @PostMapping("/qwHookSendMsg")
+    public R qwHookSendMsg(@RequestBody QwHookSendMsgParam param ) {
+        param.setClientId(2);
+        return qwHookApiService.sendMsg(param);
+    }
+
+    @GetMapping("/qwHookAuth")
+    public R qwHookAuth(@RequestParam(value = "key", required = false) String key) {
+        QwHookAuthVO qwHookAuthVO = qwUserService.selectQwUserByAppKeyAuth(key);
+        if(qwHookAuthVO!=null){
+            return R.ok().put("data",qwHookAuthVO);
+        }
+        else {
+            return R.error("查询到相关成员信息");
+        }
+    }
+
+    @GetMapping("/qwHookCheck")
+    public R qwHookCheckCorpId(@RequestParam(value = "key", required = false) String key,
+                               @RequestParam(value = "qwHookId", required = false) String qwHookId,
+                               @RequestParam(value = "corpId", required = false) String corpId) {
+        QwUser user=qwUserService.selectQwUserByAppKey(key);
+        if(user.getCorpId().equals(corpId)){
+            if(user.getQwHookId().equals(qwHookId)){
+                return R.ok();
+            }
+            else{
+                return R.error("此帐号绑定的企业微信未授权");
+            }
+        }
+        else{
+           return R.error("此帐号绑定的企业微信未授权");
+        }
+    }
+
+    //获取企业微信签名
+    @PostMapping("/getConfigSignature")
+    public R getConfigSignature(@RequestBody QwConfigSignatureParam qwConfigSignatureParam) throws Exception {
+        return qwGetJsapiTicketService.getQwJsapiTicket(qwConfigSignatureParam);
+    }
+
+    //根据userid和外部联系人id获取到客户详情
+    @PostMapping("/getQwExternalContactDetails")
+    public R getQwExternalContactDetails(@RequestBody QwExternalContactHParam param){
+        return qwGetJsapiTicketService.getQwExternalContactDetails(param);
+    }
+
+    @ApiOperation("获取最新版本")
+    @GetMapping("/getNewAppVersion")
+    public R getNewAppVersion(@RequestParam("appType")Integer appType)
+    {
+        FsAppVersion version=appVersionService.getNewVersion(appType,3);
+        return R.ok().put("data",version);
+    }
+
+
+
+
+}

+ 181 - 0
fs-qwhook/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java

@@ -0,0 +1,181 @@
+package com.fs.app.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.course.domain.FsUserCourse;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsCourseLinkMiniParam;
+import com.fs.course.param.FsCourseLinkRoomParam;
+import com.fs.course.param.FsCourseListBySidebarParam;
+import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCoursePeriodService;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsCourseListBySidebarVO;
+import com.fs.course.vo.FsCourseVideoListBySidebarVO;
+import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
+import com.fs.qw.domain.QwUser;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.voice.utils.StringUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+
+@Api("课程库相关接口")
+@RestController
+@RequestMapping("/apis/app/fs/course")
+@Slf4j
+public class ApisFsUserCourseVideoController extends BaseController {
+
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IFsUserCoursePeriodService fsUserCoursePeriodService;
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+
+    @ApiOperation("课程视频详情")
+    @GetMapping(value = "/videoDetails")
+    public R getVideoDetails(Long videoId) {
+        return R.ok().put("data",fsUserCourseVideoService.getVideoDetails(videoId));
+    }
+
+
+    @PostMapping("/getFsCourseListBySidebar")
+    @ApiOperation("获取视频课程下拉列表 侧边栏")
+    public R getFsCourseListBySidebar(@RequestBody FsCourseListBySidebarParam param) {
+
+        QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(),param.getQwUserId().trim());
+
+        if (qwUser == null || qwUser.getCompanyId() == null) {
+            return R.error("员工未绑定 销售公司 或 未获取到员工信息,请重试!");
+        }
+        param.setCompanyId(qwUser.getCompanyId());
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseListBySidebarVO> fsCourseListBySidebar = fsUserCourseService.getFsCourseListBySidebar(param);
+        PageInfo<FsCourseListBySidebarVO> result = new PageInfo<>(fsCourseListBySidebar);
+        return R.ok().put("data", result);
+    }
+
+    @PostMapping("/getFsCourseVideoListBySidebar")
+    @ApiOperation("获取视频课程的课节下拉列表 侧边栏")
+    public R getFsCourseVideoListBySidebar(@RequestBody FsCourseListBySidebarParam param) {
+
+        if (param.getCourseId()==null){
+            return R.error("课程id不能为空");
+        }
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseVideoListBySidebarVO> videoListBySidebar = fsUserCourseVideoService.getFsCourseVideoListBySidebar(param);
+        PageInfo<FsCourseVideoListBySidebarVO> result = new PageInfo<>(videoListBySidebar);
+        return R.ok().put("data", result);
+    }
+
+    /**
+    * 创建 发客户小程序
+    */
+
+    @RepeatSubmit
+    @PostMapping("/createMiniLink")
+    public R createMiniLink(@RequestBody FsCourseLinkMiniParam param) {
+
+        if (param.getCourseId()==null){
+            return R.error("课程id不能为空");
+        }
+        if (param.getVideoId()==null){
+            return R.error("视频id不能为空");
+        }
+        if (StringUtil.strIsNullOrEmpty(param.getQwUserId())){
+            return R.error("用户id不能为空");
+        }
+        if (StringUtil.strIsNullOrEmpty(param.getCorpId())){
+            return R.error("企业id不能为空");
+        }
+
+        if (param.getExternalUserId()==null){
+            return R.error("客户id不能为空");
+        }
+
+        return fsUserCourseVideoService.createMiniLink(param);
+    }
+
+    /**
+    * 创建发卡片
+    */
+    @RepeatSubmit
+    @PostMapping("/createCartLink")
+    public R createCartLink(@RequestBody  FsCourseLinkMiniParam param) {
+
+        if (param.getCourseId()==null){
+            return R.error("课程id不能为空");
+        }
+        if (param.getVideoId()==null){
+            return R.error("视频id不能为空");
+        }
+        if (StringUtil.strIsNullOrEmpty(param.getQwUserId())){
+            return R.error("用户id不能为空");
+        }
+        if (StringUtil.strIsNullOrEmpty(param.getCorpId())){
+            return R.error("企业id不能为空");
+        }
+
+        if (param.getExternalUserId()==null){
+            return R.error("客户id不能为空");
+        }
+
+        return fsUserCourseVideoService.createCartLink(param);
+
+    }
+
+    @GetMapping("/createRoomLink")
+    @ApiOperation("创建发群链接")
+    public R createRoomLink(FsCourseLinkRoomParam param) {
+        QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(),param.getQwUserId().trim());
+        if (qwUser==null||qwUser.getCompanyId()==null){
+            return R.error("无权限");
+        }
+        FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
+        createParam.setCourseId(param.getCourseId());
+        createParam.setVideoId(param.getVideoId());
+        createParam.setCorpId(param.getCorpId());
+        createParam.setCompanyUserId(qwUser.getCompanyUserId());
+        createParam.setCompanyId(qwUser.getCompanyId());
+        createParam.setQwUserId(qwUser.getId());
+        String linkUrl;
+        R createLink = courseLinkService.createRoomLinkUrl(createParam);
+        if (createLink.get("code").equals(500)){
+            return R.error("链接生成失败!");
+        }
+        linkUrl = (String) createLink.get("url");
+
+        FsUserCourse course = fsUserCourseService.selectFsUserCourseByCourseId(param.getCourseId());
+
+        JSONObject news = new JSONObject(true); // true 表示保持字段顺序
+        news.put("link", linkUrl);
+        news.put("title", course.getCourseName());
+        news.put("desc", param.getTitle());
+        news.put("imgUrl", course.getImgUrl());
+        return R.ok().put("news",news);
+    }
+
+}

+ 39 - 0
fs-qwhook/src/main/java/com/fs/app/controller/ApisQwStatisticsController.java

@@ -0,0 +1,39 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.CustomException;
+import com.fs.statistics.dto.WatchCourseStatisticsDTO;
+import com.fs.statistics.param.WatchCourseStatisticsParam;
+import com.fs.statistics.service.IStatisticsService;
+import io.swagger.annotations.ApiOperation;
+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;
+
+/**
+ * 企微统计数据
+ */
+@RestController
+@RequestMapping("/apis/app/qw/statistics")
+public class ApisQwStatisticsController extends BaseController {
+    @Autowired
+    private IStatisticsService statisticsService;
+
+
+//    @Login
+    @PostMapping("/course/watch")
+    @ApiOperation("会员看课详情")
+    public R queryCourseWatchStatistics(@RequestBody WatchCourseStatisticsParam param) {
+        if(param.getQwExternalContactId() == null) {
+            throw new CustomException("外部联系人id为空!");
+        }
+
+        WatchCourseStatisticsDTO watchCourseStatisticsDTO = statisticsService.queryWatchCourse(param);
+
+        return R.ok().put("data",watchCourseStatisticsDTO);
+    }
+
+}

部分文件因为文件数量过多而无法显示