Bladeren bron

Merge remote-tracking branch 'origin/master'

xdd 1 maand geleden
bovenliggende
commit
30c2996971
100 gewijzigde bestanden met toevoegingen van 3276 en 446 verwijderingen
  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. 25 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyUserController.java
  5. 11 7
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  6. 14 0
      fs-admin/src/main/java/com/fs/his/task/FsCourseTask.java
  7. 29 0
      fs-admin/src/main/java/com/fs/his/task/PeriodTask.java
  8. 93 0
      fs-common-api/src/main/resources/logback.xml
  9. 11 0
      fs-company-app/src/main/java/com/fs/app/controller/CommonController.java
  10. 3 2
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  11. 11 6
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  12. 16 1
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  13. 1 1
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  14. 135 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDomainBindController.java
  15. 108 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDomainBindUserController.java
  16. 1 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  17. 6 2
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java
  18. 1 1
      fs-company/src/main/java/com/fs/company/controller/store/FsUserController.java
  19. 1 0
      fs-company/src/main/java/com/fs/framework/config/DataSourceConfig.java
  20. 42 3
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  21. 1 0
      fs-framework/src/main/java/com/fs/framework/config/DataSourceConfig.java
  22. 1 0
      fs-qw-api/src/main/java/com/fs/framework/config/DataSourceConfig.java
  23. 5 2
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  24. 1 1
      fs-qw-task/src/main/java/com/fs/app/task/CourseWatchLogScheduler.java
  25. 2 2
      fs-qw-task/src/main/java/com/fs/app/task/UserCourseWatchCountTask.java
  26. 34 18
      fs-qw-task/src/main/java/com/fs/app/task/qwTask.java
  27. 21 9
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  28. 1 1
      fs-qw-task/src/main/resources/application.yml
  29. 6 23
      fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java
  30. 6 23
      fs-qwhook-sop/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  31. 1 0
      fs-qwhook-sop/src/main/java/com/fs/framework/config/DataSourceConfig.java
  32. 6 23
      fs-qwhook/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java
  33. 8 23
      fs-qwhook/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  34. 1 0
      fs-qwhook/src/main/java/com/fs/framework/config/DataSourceConfig.java
  35. 27 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDomain.java
  36. 35 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDomainBind.java
  37. 31 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDomainBindUser.java
  38. 83 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDomainBindMapper.java
  39. 75 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDomainBindUserMapper.java
  40. 79 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDomainMapper.java
  41. 5 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java
  42. 1 1
      fs-service/src/main/java/com/fs/company/mapper/CompanyTagMapper.java
  43. 14 2
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  44. 47 0
      fs-service/src/main/java/com/fs/company/param/CompanyDomainBindParam.java
  45. 36 0
      fs-service/src/main/java/com/fs/company/param/CompanyDomainBindUserParam.java
  46. 47 0
      fs-service/src/main/java/com/fs/company/param/CompanyDomainParam.java
  47. 74 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDomainBindService.java
  48. 63 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDomainBindUserService.java
  49. 83 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDomainService.java
  50. 4 1
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  51. 166 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDomainBindServiceImpl.java
  52. 95 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDomainBindUserServiceImpl.java
  53. 274 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDomainServiceImpl.java
  54. 1 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  55. 7 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyTagUserServiceImpl.java
  56. 16 3
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  57. 58 0
      fs-service/src/main/java/com/fs/company/vo/CompanyDomainBindUserVo.java
  58. 59 0
      fs-service/src/main/java/com/fs/company/vo/CompanyDomainBindVo.java
  59. 26 0
      fs-service/src/main/java/com/fs/company/vo/CompanyDomainExportTemplateVo.java
  60. 52 0
      fs-service/src/main/java/com/fs/company/vo/CompanyDomainVo.java
  61. 8 9
      fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java
  62. 3 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseAnswerLogs.java
  63. 6 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseLink.java
  64. 7 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRealLink.java
  65. 2 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java
  66. 11 2
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  67. 7 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  68. 1 0
      fs-service/src/main/java/com/fs/course/param/FsCourseLinkMiniParam.java
  69. 1 0
      fs-service/src/main/java/com/fs/course/param/FsCourseLinkRoomParam.java
  70. 1 0
      fs-service/src/main/java/com/fs/course/param/FsCourseQuestionAnswerUParam.java
  71. 2 0
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  72. 2 0
      fs-service/src/main/java/com/fs/course/param/FsUserCourseVideoAddKfUParam.java
  73. 4 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseLinkService.java
  74. 113 14
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  75. 1 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java
  76. 6 1
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java
  77. 1 1
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  78. 25 8
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  79. 143 43
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  80. 1 0
      fs-service/src/main/java/com/fs/his/mapper/FsDoctorMapper.java
  81. 2 0
      fs-service/src/main/java/com/fs/his/mapper/FsPackageMapper.java
  82. 4 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserDoctorMapper.java
  83. 12 10
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  84. 7 0
      fs-service/src/main/java/com/fs/his/service/IFsPackageService.java
  85. 3 0
      fs-service/src/main/java/com/fs/his/service/IFsUserDoctorService.java
  86. 6 0
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java
  87. 6 1
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  88. 6 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserDoctorServiceImpl.java
  89. 102 23
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  90. 10 0
      fs-service/src/main/java/com/fs/his/vo/FsUserVO.java
  91. 3 0
      fs-service/src/main/java/com/fs/qw/mapper/CustomerTransferApprovalMapper.java
  92. 5 0
      fs-service/src/main/java/com/fs/qw/service/ICustomerTransferApprovalService.java
  93. 13 0
      fs-service/src/main/java/com/fs/qw/service/impl/CustomerTransferApprovalServiceImpl.java
  94. 43 39
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  95. 4 0
      fs-service/src/main/java/com/fs/sop/domain/QwSop.java
  96. 4 1
      fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  97. 8 0
      fs-service/src/main/java/com/fs/sop/params/QwSopLogsParam.java
  98. 1 1
      fs-service/src/main/java/com/fs/sop/service/IQwSopLogsService.java
  99. 209 137
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  100. 5 0
      fs-service/src/main/java/com/fs/sop/vo/SopUserLogsVo.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;
+    }
+}

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

@@ -136,4 +136,29 @@ public class CompanyUserController extends BaseController
         List<CompanyUser> list = companyUserService.selectCompanyUserList(map);
         return  R.ok().put("data",list);
     }
+
+    /**
+     * 更换会员归属销售
+     * @return
+     */
+    @PreAuthorize("@ss.hasPermi('company:companyUser:change')")
+    @Log(title = "更换会员归属", businessType = BusinessType.OTHER)
+    @PostMapping("/changeCompanyUser")
+    public AjaxResult changeCompanyUser(@RequestBody List<Long> userIds, @RequestParam Long companyUserId, @RequestParam Long companyId)
+    {
+        return toAjax(companyUserService.changeCompanyUser(userIds, companyUserId, companyId));
+    }
+    /**
+     * 根据登录的用户公司获取所有的销售
+     * @return
+     */
+    @GetMapping("/getCompanyUserList")
+    public R getCompanyUserList(@RequestParam Long companyId)
+    {
+        CompanyUser cu = new CompanyUser();
+        cu.setCompanyId(companyId);
+        List<CompanyUser> list = companyUserService.selectCompanyUserList(cu);
+        return  R.ok().put("data",list);
+    }
+
 }

+ 11 - 7
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -4,6 +4,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.utils.ParseUtils;
@@ -196,15 +197,18 @@ public class FsUserController extends BaseController
     @ApiOperation("小黑屋")
     public R darkRoomList(FsUserPageListParam param) {
 //        startPage();
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+//        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
-        for (FsUserPageListVO fsUserPageListVO : fsUserPageListVOPageInfo.getList()) {
-            fsUserPageListVO.setPhone(ParseUtils.parsePhone(fsUserPageListVO.getPhone()));
+        if(ObjectUtils.isNotNull(fsUserPageListVOPageInfo)){
+            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);
         }
-        Map<String, Object> map = new HashMap<String, Object>();
-        map.put("rows", fsUserPageListVOPageInfo.getList());
-        map.put("total", fsUserPageListVOPageInfo.getList().size());
-        return R.ok(map);
+        return R.ok();
     }
 
     @PreAuthorize("@ss.hasPermi('his:user:enabledUsers')")

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

@@ -1,7 +1,9 @@
 package com.fs.his.task;
 
 import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.qw.service.ICustomerTransferApprovalService;
 import com.fs.qw.service.IHyWorkTaskService;
+import com.fs.statis.service.FsStatisSalerWatchService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -16,6 +18,11 @@ public class FsCourseTask {
     private IFsCourseWatchLogService fsCourseWatchLogService;
     @Autowired
     private IHyWorkTaskService hyWorkTaskService;
+
+    @Autowired
+    private FsStatisSalerWatchService fsStatisSalerWatchService;
+    @Autowired
+    private ICustomerTransferApprovalService customerTransferApprovalService;
     /**
      * 添加会员观看日志
      * @throws Exception
@@ -44,4 +51,11 @@ public class FsCourseTask {
         hyWorkTaskService.hyWorkTask();
     }
 
+    /**
+     * 客户转移审批自动通过
+     */
+    public void fsUserTransferAutoPass(){
+        customerTransferApprovalService.autoApprovePass();
+    }
+
 }

+ 29 - 0
fs-admin/src/main/java/com/fs/his/task/PeriodTask.java

@@ -0,0 +1,29 @@
+package com.fs.his.task;
+
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
+import com.fs.course.service.IFsUserCoursePeriodService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component("periodTask")
+public class PeriodTask {
+
+    @Autowired
+    private IFsUserCoursePeriodService userCoursePeriodService;
+    @Autowired
+    private IFsUserCoursePeriodDaysService userCoursePeriodDaysService;
+
+    /**
+     * 更新营期状态
+     */
+    public void refreshPeriod() {
+        userCoursePeriodService.changePeriodStatus();
+    }
+
+    /**
+     * 更新营期课程状态
+     */
+    public void refreshPeriodDays() {
+        userCoursePeriodDaysService.changePeriodCourseStatus();
+    }
+}

+ 93 - 0
fs-common-api/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-common-api/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 11 - 0
fs-company-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -25,6 +25,7 @@ import com.fs.his.service.IFsAppVersionService;
 import com.fs.his.service.IFsCityService;
 import com.fs.jpush.service.JpushService;
 
+import com.fs.store.service.IFsUserCourseCountService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
@@ -228,5 +229,15 @@ public class CommonController extends AppBaseController {
 	}
 
 
+	@Autowired
+	private IFsUserCourseCountService userCourseCountService;
+
+	@PostMapping("test")
+	public R test() throws Exception
+	{
+		userCourseCountService.insertFsUserCourseCountTask();
+		return R.ok();
+	}
+
 
 }

+ 3 - 2
fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -73,12 +73,12 @@ public class CompanyUserController extends AppBaseController {
         // 判断是否管理员 或者包含 1:全部数据权限
         if (companyUser.isAdmin() || companyRoles.stream().anyMatch(r -> "1".equals(r.getDataScope()))) {
             PageHelper.startPage(pageNum, pageSize);
-            companyUsers = companyUserService.getCompanyUserListByDeptId(null);
+            companyUsers = companyUserService.getCompanyUserListByCompanyIdAndDeptId(companyUser.getCompanyId(), 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());
+            companyUsers = companyUserService.getCompanyUserListByCompanyIdAndDeptId(companyUser.getCompanyId(), companyUser.getDeptId());
         }
         // 默认空 -- 判断是否包含 5:仅可查看本人
         else {
@@ -190,6 +190,7 @@ public class CompanyUserController extends AppBaseController {
         BeanUtils.copyProperties(param, companyUser);
 
         companyUser.setUserName(param.getPhoneNumber());
+        companyUser.setUserType("01");
         companyUser.setPhonenumber(param.getPhoneNumber());
         companyUser.setPassword(SecurityUtils.encryptPassword(companyUser.getPassword()));
         companyUser.setCreateTime(new Date());

+ 11 - 6
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -39,8 +39,9 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.io.InputStream;
-import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 
 @Slf4j
@@ -75,9 +76,8 @@ public class FsUserController extends AppBaseController {
     @ApiOperation("用户会员分页列表")
     public ResponseResult<PageInfo<FsUserPageListVO>> pageList(@RequestBody FsUserPageListParam param) {
         param.setUserId(Long.parseLong(getUserId()));
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+//        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
-//        PageInfo<FsUserPageListVO> pageInfo = new PageInfo<>(list);
         return ResponseResult.ok(fsUserPageListVOPageInfo);
     }
 
@@ -153,18 +153,23 @@ public class FsUserController extends AppBaseController {
     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) {
+
+        DateTimeFormatter dfm = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        LocalDate today = LocalDate.now();
+        LocalDateTime startDateTime = LocalDateTime.parse(startTime, dfm);
+        LocalDateTime endDateTime = LocalDateTime.parse(endTime, dfm);
+        if (startDateTime.toLocalDate().equals(today) && endDateTime.toLocalDate().equals(today)) {
             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);
+        } else {
+            vo.setYesterdayVO(new FsUserStatisticsVO());
         }
         return ResponseResult.ok(vo);
     }

+ 16 - 1
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -5,6 +5,8 @@ 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.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.param.FsWatchCourseTimeParam;
@@ -58,6 +60,8 @@ public class FsUserCourseVideoController extends AppBaseController {
 
     @Autowired
     private IFsUserCoursePeriodService fsUserCoursePeriodService;
+    @Autowired
+    private ICompanyUserService companyUserService;
 
     @Login
     @GetMapping("/pageList")
@@ -108,12 +112,23 @@ public class FsUserCourseVideoController extends AppBaseController {
                                                       @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);
+        log.debug("参与记录 videoId:{}, type:{}, keyword: {}, pageNum: {}, pageSize: {}", videoId, type, keyword, pageNum, pageSize);
         Map<String, Object> params = new HashMap<>();
         params.put("videoId", videoId);
         params.put("type", type);
         params.put("keyword", keyword);
 
+        // type 0 答题领奖记录----答题正确并且领取红包的
+        // type 1 完播----这堂课看完的人
+        // type 2 未完播---看课中断
+        // 管理员看整个公司 否则看自己的
+        CompanyUser companyUser = companyUserService.selectCompanyUserByUserId(Long.parseLong(getUserId()));
+        if (companyUser.isAdmin()) {
+            params.put("companyId", companyUser.getCompanyId());
+        } else {
+            params.put("companyUserId", companyUser.getUserId());
+        }
+
         PageHelper.startPage(pageNum, pageSize);
         List<FsUserCourseParticipationRecordVO> record = fsUserCourseService.getParticipationRecordByMap(params);
         return ResponseResult.ok(new PageInfo<>(record));

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

@@ -257,7 +257,7 @@ public class UserController extends AppBaseController {
             companyUser.setCompanyName(company.getCompanyName());
             // 岗位
             companyUser.setPosts(postService.selectCompanyPostListByUserId(companyUser.getUserId()));
-            return R.ok().put("user", companyUser);
+            return R.ok().put("user", companyUser).put("balance", company.getMoney());
         } catch (Exception e) {
 
             return R.error("操作异常");

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

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

@@ -180,6 +180,7 @@ public class CompanyUserController extends BaseController
         user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
         user.setCreateTime(new Date());
         user.setUserType("01");//一般用户
+        user.setIsAudit(1);
         return toAjax(companyUserService.insertUser(user));
     }
 

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

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

@@ -235,7 +235,7 @@ public class FsUserController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setUserId(loginUser.getUser().getUserId());
 //        startPage();
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+//        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());

+ 1 - 0
fs-company/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -40,6 +40,7 @@ public class DataSourceConfig {
     @Primary
     public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }

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

@@ -1,5 +1,6 @@
 package com.fs.user;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
@@ -7,11 +8,16 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
+
 import com.fs.his.service.IFsUserService;
+import com.fs.qw.domain.CustomerTransferApproval;
+import com.fs.qw.dto.FsUserTransferParamDTO;
+import com.fs.qw.service.ICustomerTransferApprovalService;
 import com.fs.store.param.h5.FsUserPageListParam;
 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.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -19,8 +25,11 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.Date;
+
 @Api(tags = "会员管理接口")
 @RestController
+@Slf4j
 @RequestMapping("/user/fsUser")
 @AllArgsConstructor
 public class FsUserAdminController extends BaseController {
@@ -34,6 +43,9 @@ public class FsUserAdminController extends BaseController {
     @Autowired
     private TokenService tokenService;
 
+    @Autowired
+    private ICustomerTransferApprovalService transferApprovalService;
+
     @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @PostMapping("/list")
     @ApiOperation("会员列表(与移动端使用的相同查询)")
@@ -41,9 +53,12 @@ public class FsUserAdminController extends BaseController {
         //startPage();
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        param.setCompanyId(loginUser.getCompany().getCompanyId());
-        param.setCompanyUserId(String.valueOf(loginUser.getUser().getUserId()));
-
+        if (param.isMyFsUser()){
+            param.setCompanyId(loginUser.getCompany().getCompanyId());
+            param.setCompanyUserId(String.valueOf(loginUser.getUser().getUserId()));
+        }else {
+            param.setCompanyId(loginUser.getCompany().getCompanyId());
+        }
         if(param.getCompanyUserId() == null) {
             throw new IllegalArgumentException("当前销售不存在!");
         }
@@ -60,5 +75,29 @@ public class FsUserAdminController extends BaseController {
         return R.error();
     }
 
+    /**
+     * 转移
+     * @param param
+     */
+    @PostMapping("/transfer")
+    public R transfer(@RequestBody FsUserTransferParamDTO param){
+        log.info("客户转移: {}",param);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setSourceCompanyUserId(loginUser.getUser().getUserId());
+        CustomerTransferApproval customerTransferApproval = new CustomerTransferApproval();
+        customerTransferApproval.setCorpId(String.valueOf(loginUser.getCompany().getCompanyId()));
+        customerTransferApproval.setCustomerIds(JSON.toJSONString(param.getUserIds()));
+        customerTransferApproval.setOriginalUserId(param.getSourceCompanyUserId());
+        customerTransferApproval.setTargetUserId(param.getTargetCompanyUserId());
+        customerTransferApproval.setInitiatorUserId(param.getSourceCompanyUserId());
+        customerTransferApproval.setContent(param.getContent());
+        customerTransferApproval.setCreatedAt(new Date());
+        customerTransferApproval.setUpdatedAt(new Date());
+        customerTransferApproval.setApprovalStatus(0);
+        customerTransferApproval.setTransferType(2);
+        transferApprovalService.insertCustomerTransferApproval(customerTransferApproval);
+        return R.ok("转移申请已经提交,等待转移成功!");
+    }
+
 
 }

+ 1 - 0
fs-framework/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -43,6 +43,7 @@ public class DataSourceConfig {
     @Primary
     public DynamicDataSource dataSource(@Qualifier("clickhouseDataSource") DataSource clickhouseDataSource,@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         targetDataSources.put(DataSourceType.CLICKHOUSE.name(), clickhouseDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);

+ 1 - 0
fs-qw-api/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -40,6 +40,7 @@ public class DataSourceConfig {
     @Primary
     public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }

+ 5 - 2
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -20,6 +20,7 @@ import com.fs.sop.mapper.QwSopMapper;
 import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.sop.service.*;
 import com.fs.sop.vo.QwSopLogsDoSendListTVO;
+import com.fs.store.service.IFsUserCourseCountService;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -82,6 +83,9 @@ public class CommonController {
     @Autowired
     private ISopUserLogsService iSopUserLogsService;
 
+    @Autowired
+    private IFsUserCourseCountService userCourseCountService;
+
 
     @GetMapping("/testSop")
     public R testSop() throws Exception {
@@ -194,8 +198,7 @@ public class CommonController {
     private SopLogsChatTaskService sopLogsChatTaskService;
     @GetMapping("/test2")
     public String selectChatSopUserLogsListByTime() throws Exception {
-        System.out.println("test2");
-       // sopLogsChatTaskService.createAiChatSopLogs();
+        userCourseCountService.insertFsUserCourseCountTask();
         return "s";
     }
 

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

@@ -126,7 +126,7 @@ public class CourseWatchLogScheduler {
 
     }
 
-    @Scheduled(fixedRate = 60000) // 每分钟执行一次
+    @Scheduled(fixedRate = 30000) // 每分钟执行一次
     public void checkFsUserWatchStatus() {
         // 尝试设置标志为 true,表示任务开始执行
         if (!isRunning4.compareAndSet(false, true)) {

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

@@ -14,9 +14,9 @@ public class UserCourseWatchCountTask {
 
 
     /**
-     * 每天两点进行会员看课统计
+     * 每15分钟执行一次
      */
-    @Scheduled(cron = "0 0 2 * * ?")  // 2点0分0秒执行
+    @Scheduled(cron = "0 */10 * * * ?")  // 每10分钟执行一次
     public void userCourseCountTask() {
         try {
             log.info("==============会员看课统计任务执行===============开始");

+ 34 - 18
fs-qw-task/src/main/java/com/fs/app/task/qwTask.java

@@ -28,7 +28,7 @@ import java.util.List;
 /**
  * 企业微信SOP定时任务管理类
  * 负责处理各种定时任务,包括SOP规则检查、消息发送、数据清理等
- * 
+ *
  * @author 系统
  * @version 1.0
  */
@@ -50,10 +50,10 @@ public class qwTask {
 
     @Autowired
     private ISopUserLogsService sopUserLogsService;
-    
+
     @Autowired
     private SopLogsTaskService sopLogsTaskService;
-    
+
     @Autowired
     private SopWxLogsService sopWxLogsService;
 
@@ -71,7 +71,7 @@ public class qwTask {
 
     @Autowired
     private QwSopLogsMapper qwSopLogsMapper;
-    
+
     @Autowired
     private IQwSopTagService qwSopTagService;
 
@@ -99,7 +99,7 @@ public class qwTask {
      * 定时任务:根据营期生成sopLogs待发记录
      * 执行时间:每小时的第5分钟执行
      * 功能:根据营期时间生成需要发送的SOP日志记录
-     * 
+     *
      * @throws Exception 执行异常
      */
     @Scheduled(cron = "0 5 * * * ?") // 每小时的第5分钟触发
@@ -118,7 +118,7 @@ public class qwTask {
      * 定时任务:微信SOP处理
      * 执行时间:每小时的第5分钟执行
      * 功能:处理微信相关的SOP日志
-     * 
+     *
      * @throws Exception 执行异常
      */
     @Scheduled(cron = "0 5 * * * ?") // 每小时的第5分钟触发
@@ -136,7 +136,7 @@ public class qwTask {
      * 定时任务:处理聊天SOP用户日志
      * 执行时间:已注释,原为每分钟的第5秒执行
      * 功能:将clickHouse的sopUserLogsChat(营期表)按每分钟巡回处理
-     * 
+     *
      * @throws Exception 执行异常
      */
 //    @Scheduled(cron = "5 0/1 * * * ?")
@@ -149,20 +149,36 @@ public class qwTask {
     }
 
     /**
-     * 定时任务:发送企业微信SOP群发消息(新版-按营期发送)
-     * 执行时间:每天凌晨 0:20:00
-     * 功能:通过调用企业微信接口发送SOP群发消息
+     * 定时 发送 通过调用 企业微信接口 发送的 SOP 群发消息(按单链发)
      */
-    @Scheduled(cron = "0 20 0 * * ?")
-    public void SendQwApiSopLogTimerNew() {
-        log.info("zyp \n【企微官方接口群发开始】");
-        
-        // 获取当前日期
+    @Scheduled(cron = "0 20 1 * * ?")
+    public void SendQwApiSopLogTimer(){
+        log.info("zyp \n【企微官方接口群发开始-单链】");
+//        qwSopLogsService.checkQwSopLogs();
         LocalDate localDate = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0).toLocalDate();
         String date = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
 
-        // 根据用户日志创建企业群发
-        qwSopLogsService.createCorpMassSendingByUserLogs(date);
+        qwSopLogsService.createCorpMassSending(date);
+    }
+
+    /**
+     * 定时 发送 通过调用 企业微信接口 发送的 SOP 群发消息(新版-安装营期发)
+     */
+    @Scheduled(cron = "0 10 0,1 * * ?")
+    public void SendQwApiSopLogTimerNew(){
+
+        log.info("zyp \n【企微官方接口群发开始】");
+//        qwSopLogsService.checkQwSopLogs();
+//        LocalDate localDate = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0).toLocalDate();
+//        String date = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+        int currentHour = LocalDateTime.now().getHour();
+        String taskStartTime = LocalDate.now().atTime(currentHour, 0, 0)
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        String taskEndTime = LocalDate.now().atTime(currentHour, 59, 59)
+                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+
+        qwSopLogsService.createCorpMassSendingByUserLogs(taskStartTime,taskEndTime);
     }
 
     /**
@@ -246,7 +262,7 @@ public class qwTask {
 
     /**
      * 批量处理插入逻辑,支持每500条数据一次的批量插入
-     * 
+     *
      * @param logsByJsApiNotExtId 需要处理的日志列表
      */
     private void processAndInsertQwSopLogs(List<QwSopLogsDoSendListTVO> logsByJsApiNotExtId) {

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

@@ -774,22 +774,33 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setQwUserid(logVo.getQwUserId());
         sopLogs.setCorpId(logVo.getCorpId());
         sopLogs.setLogType(ruleTimeVO.getType());
+        sopLogs.setTakeRecords(0);
 
         if (isOfficial == 1) {
 
-            if (fsUserId== null || Long.valueOf(0L).equals(fsUserId)){
-                sopLogs.setSendType(2);
-                sopLogs.setRemark("未绑定小程序用户,单链补发");
-                //时间设置成固定7点
-                LocalDateTime dateTime = LocalDateTime.parse(formattedSendTime, DATE_TIME_FORMATTER);
-                sopLogs.setSendTime(OUTPUT_FORMATTER.format(dateTime));
+            if (logVo.getIsSampSend()== 1) {
+                if (fsUserId == null || Long.valueOf(0L).equals(fsUserId)) {
+                    sopLogs.setSendType(2);
+                    sopLogs.setRemark("未绑定小程序用户,单链补发");
+                    //时间设置成固定8点
+                    LocalDateTime dateTime = LocalDateTime.parse(formattedSendTime, DATE_TIME_FORMATTER);
+                    sopLogs.setSendTime(OUTPUT_FORMATTER.format(dateTime));
+                } else {
+                    sopLogs.setSendType(1);
+                }
+
             }else {
-                sopLogs.setSendType(1);
+                if (fsUserId == null || Long.valueOf(0L).equals(fsUserId)) {
+                    sopLogs.setTakeRecords(1);
+                    sopLogs.setSendType(1);
+                }else {
+                    sopLogs.setSendType(1);
+                }
             }
 
-        }else if (isOfficial==0){
+        } else if (isOfficial == 0) {
             sopLogs.setSendType(ruleTimeVO.getSendType() == 1 ? 2 : ruleTimeVO.getSendType());
-        }else{
+        } else {
             sopLogs.setSendType(ruleTimeVO.getSendType());
         }
 
@@ -1828,6 +1839,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setExternalUserId(externalContact.getExternalUserId());
         sopLogs.setExternalUserName(externalContact.getName());
         sopLogs.setFsUserId(finishLog.getUserId() != null ? finishLog.getUserId() : null );
+        sopLogs.setUserLogsId("-");
         // 解析模板设置
         List<QwSopCourseFinishTempSetting.Setting> settings = parseSettings(finishTemp.getSetting());
         if (settings == null) {

+ 1 - 1
fs-qw-task/src/main/resources/application.yml

@@ -10,4 +10,4 @@ spring:
 #    active: druid-hcl
 #    active: druid-sxjz
 #    active: druid-hdt
-    active: druid-myhk
+    active: druid-fcky-test

+ 6 - 23
fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java

@@ -144,7 +144,11 @@ public class ApisFsUserCourseVideoController extends BaseController {
             return R.error("客户id不能为空");
         }
 
-        return fsUserCourseVideoService.createCartLink(param);
+        if (param.getType()==null || param.getType()==1){
+            return fsUserCourseVideoService.createCartLink(param);
+        }else {
+            return fsUserCourseVideoService.createMiniLink(param);
+        }
     }
 
     @GetMapping("/createRoomLink")
@@ -154,28 +158,7 @@ public class ApisFsUserCourseVideoController extends BaseController {
         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);
+        return courseLinkService.createRoomLink(param, qwUser);
     }
 
 }

+ 6 - 23
fs-qwhook-sop/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -101,28 +101,7 @@ public class FsUserCourseVideoController {
         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);
+        return courseLinkService.createRoomLink(param, qwUser);
     }
 
     /**
@@ -177,6 +156,10 @@ public class FsUserCourseVideoController {
             return R.error("客户id不能为空");
         }
 
-        return fsUserCourseVideoService.createCartLink(param);
+        if (param.getType()==null || param.getType()==1){
+            return fsUserCourseVideoService.createCartLink(param);
+        }else {
+            return fsUserCourseVideoService.createMiniLink(param);
+        }
     }
 }

+ 1 - 0
fs-qwhook-sop/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -40,6 +40,7 @@ public class DataSourceConfig {
     @Primary
     public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }

+ 6 - 23
fs-qwhook/src/main/java/com/fs/app/controller/ApisFsUserCourseVideoController.java

@@ -143,7 +143,11 @@ public class ApisFsUserCourseVideoController extends BaseController {
             return R.error("客户id不能为空");
         }
 
-        return fsUserCourseVideoService.createCartLink(param);
+        if (param.getType()==null || param.getType()==1){
+            return fsUserCourseVideoService.createCartLink(param);
+        }else {
+            return fsUserCourseVideoService.createMiniLink(param);
+        }
 
     }
 
@@ -154,28 +158,7 @@ public class ApisFsUserCourseVideoController extends BaseController {
         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);
+        return courseLinkService.createRoomLink(param, qwUser);
     }
 
 }

+ 8 - 23
fs-qwhook/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -92,6 +92,8 @@ public class FsUserCourseVideoController {
         return R.ok().put("data", result);
     }
 
+
+
     @Autowired
     private IFsCourseLinkService courseLinkService;
 
@@ -148,7 +150,11 @@ public class FsUserCourseVideoController {
             return R.error("客户id不能为空");
         }
 
-        return fsUserCourseVideoService.createCartLink(param);
+        if (param.getType()==null || param.getType()==1){
+            return fsUserCourseVideoService.createCartLink(param);
+        }else {
+            return fsUserCourseVideoService.createMiniLink(param);
+        }
 
     }
 
@@ -159,28 +165,7 @@ public class FsUserCourseVideoController {
         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);
+        return courseLinkService.createRoomLink(param, qwUser);
     }
 
 }

+ 1 - 0
fs-qwhook/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -40,6 +40,7 @@ public class DataSourceConfig {
     @Primary
     public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }

+ 27 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDomain.java

@@ -0,0 +1,27 @@
+package com.fs.company.domain;
+
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 域名管路对象 company_domain
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDomain extends BaseEntity{
+
+    /** 域名id */
+    private Long id;
+
+    /** 域名 */
+    private String domain;
+
+    /** 状态(1未启用、2启用) */
+    private Long status;
+
+
+}

+ 35 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDomainBind.java

@@ -0,0 +1,35 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 域名绑定销售公司对象 company_domain_bind
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDomainBind extends BaseEntity{
+
+    /** 域名绑定中间表 */
+    private Long id;
+
+    /** 销售公司ID */
+    @Excel(name = "销售公司ID")
+    private Long companyId;
+
+    /** 绑定域名ID */
+    @Excel(name = "绑定域名ID")
+    private Long domainId;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private Long status;
+
+
+}

+ 31 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDomainBindUser.java

@@ -0,0 +1,31 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 企业域名分配中间表对象 company_domain_bind_user
+ *
+ * @author fs
+ * @date 2025-06-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDomainBindUser extends BaseEntity{
+
+    /** 中间表id */
+    private Long id;
+
+    /** 企业销售id */
+    @Excel(name = "企业销售id")
+    private Long companyUserId;
+
+    /** 中间表id */
+    @Excel(name = "中间表id")
+    private Long bindId;
+
+
+}

+ 83 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDomainBindMapper.java

@@ -0,0 +1,83 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDomainBind;
+import com.fs.company.param.CompanyDomainBindParam;
+import com.fs.company.vo.CompanyDomainBindVo;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 域名绑定销售公司Mapper接口
+ * 
+ * @author fs
+ * @date 2025-06-17
+ */
+public interface CompanyDomainBindMapper extends BaseMapper<CompanyDomainBind>{
+    /**
+     * 查询域名绑定销售公司
+     * 
+     * @param id 域名绑定销售公司主键
+     * @return 域名绑定销售公司
+     */
+    CompanyDomainBind selectCompanyDomainBindById(Long id);
+
+    /**
+     * 查询域名绑定销售公司列表
+     * 
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 域名绑定销售公司集合
+     */
+    List<CompanyDomainBind> selectCompanyDomainBindList(CompanyDomainBindParam companyDomainBind);
+
+    /**
+     * 新增域名绑定销售公司
+     * 
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 结果
+     */
+    int insertCompanyDomainBind(CompanyDomainBind companyDomainBind);
+
+    /**
+     * 修改域名绑定销售公司
+     * 
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 结果
+     */
+    int updateCompanyDomainBind(CompanyDomainBind companyDomainBind);
+
+    /**
+     * 删除域名绑定销售公司
+     * 
+     * @param id 域名绑定销售公司主键
+     * @return 结果
+     */
+    int deleteCompanyDomainBindById(Long id);
+
+    /**
+     * 批量删除域名绑定销售公司
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainBindByIds(Long[] ids);
+
+    /**
+     * 批量插入绑定数据
+     * @param bindList 数据集合
+     * **/
+    void batchInsertBindInfo(@Param("bindList") List<CompanyDomainBind> bindList);
+
+    /**
+     * 批量删除域名绑定销售公司
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainBindBydomainIds(Long[] ids);
+
+    /**
+     * 查询中间表链接信息
+     * **/
+    List<CompanyDomainBindVo> selectJoinBindInfo(CompanyDomainBindParam companyDomainBind);
+}

+ 75 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDomainBindUserMapper.java

@@ -0,0 +1,75 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDomainBindUser;
+import com.fs.company.param.CompanyDomainBindUserParam;
+import com.fs.company.vo.CompanyDomainBindUserVo;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 企业域名分配中间表Mapper接口
+ * 
+ * @author fs
+ * @date 2025-06-19
+ */
+public interface CompanyDomainBindUserMapper extends BaseMapper<CompanyDomainBindUser>{
+    /**
+     * 查询企业域名分配中间表
+     * 
+     * @param id 企业域名分配中间表主键
+     * @return 企业域名分配中间表
+     */
+    CompanyDomainBindUser selectCompanyDomainBindUserById(Long id);
+
+    /**
+     * 查询企业域名分配中间表列表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 企业域名分配中间表集合
+     */
+    List<CompanyDomainBindUserVo> selectCompanyDomainBindUserList(CompanyDomainBindUserParam companyDomainBindUser);
+
+    /**
+     * 新增企业域名分配中间表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 结果
+     */
+    int insertCompanyDomainBindUser(CompanyDomainBindUser companyDomainBindUser);
+
+    /**
+     * 修改企业域名分配中间表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 结果
+     */
+    int updateCompanyDomainBindUser(CompanyDomainBindUser companyDomainBindUser);
+
+    /**
+     * 删除企业域名分配中间表
+     * 
+     * @param id 企业域名分配中间表主键
+     * @return 结果
+     */
+    int deleteCompanyDomainBindUserById(Long id);
+
+    /**
+     * 批量删除企业域名分配中间表
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainBindUserByIds(Long[] ids);
+
+    /**
+     * 批量插入
+     * @param bindUserList 插入对象
+     * **/
+    void batchInsertBindUserInfo(@Param("bindUserList") List<CompanyDomainBindUser> bindUserList);
+
+    /**
+     * 批量查询用户分配域名中间表接口
+     * **/
+    List<CompanyDomainBindUserVo> selectDoaminBindUserJoinInfo(CompanyDomainBindUserParam companyDomainBindUser);
+}

+ 79 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDomainMapper.java

@@ -0,0 +1,79 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDomain;
+import com.fs.company.param.CompanyDomainParam;
+import com.fs.company.vo.CompanyDomainVo;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 域名管路Mapper接口
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+public interface CompanyDomainMapper extends BaseMapper<CompanyDomain> {
+    /**
+     * 查询域名管路
+     *
+     * @param id 域名管路主键
+     * @return 域名管路
+     */
+    CompanyDomain selectCompanyDomainById(Long id);
+
+    /**
+     * 查询域名管路列表
+     *
+     * @param companyDomain 域名管路
+     * @return 域名管路集合
+     */
+    List<CompanyDomainVo> selectCompanyDomainList(CompanyDomainParam companyDomain);
+
+    /**
+     * 新增域名管路
+     *
+     * @param companyDomain 域名管路
+     * @return 结果
+     */
+    int insertCompanyDomain(CompanyDomain companyDomain);
+
+    /**
+     * 修改域名管路
+     *
+     * @param companyDomain 域名管路
+     * @return 结果
+     */
+    int updateCompanyDomain(CompanyDomain companyDomain);
+
+    /**
+     * 删除域名管路
+     *
+     * @param id 域名管路主键
+     * @return 结果
+     */
+    int deleteCompanyDomainById(Long id);
+
+    /**
+     * 批量删除域名管路
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainByIds(Long[] ids);
+
+    /**
+     * 获取路由链接列表数量查询接口
+     *
+     * @param companyDomain 参数
+     * @return 结果
+     **/
+    List<CompanyDomainVo> selectCompanyDomainJoinInfo(CompanyDomainParam companyDomain);
+
+    /**
+     * 批量插入
+     * @param list 插入数据
+     * **/
+    void batchDomainInfo(@Param("list") List<CompanyDomain> list);
+}

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

@@ -169,4 +169,9 @@ public interface CompanyMapper
 
     @Select("select company_id from company")
     List<Long> selectCompanyIds();
+
+    /**
+     * 通过企业id批量查询
+     * **/
+    List<Company> selectCompanyByIds(@Param("ids") List<Long> ids);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/company/mapper/CompanyTagMapper.java

@@ -78,6 +78,6 @@ public interface CompanyTagMapper
     List<CompanyTag> selectCompanyTagListByUserId(@Param("userId") Long userId);
 
     String findUserTagByUserId(@Param("userId") Long userId,@Param("companyUserId") Long companyUserId);
-    @MapKey("tag_id")
+    @MapKey("tagId")
     Map<Long,CompanyTag> queryAllTagMap();
 }

+ 14 - 2
fs-service/src/main/java/com/fs/company/mapper/CompanyUserMapper.java

@@ -1,5 +1,7 @@
 package com.fs.company.mapper;
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
@@ -212,7 +214,7 @@ public interface CompanyUserMapper
     @Select("select domain from company_user where user_id = #{userId}")
     String selectDomainByUserId(Long userId);
 
-    List<CompanyUser> selectAllCompanyUserAndSelf(@Param("userId") Long userId);
+    List<CompanyUser> selectAllCompanyUserAndSelf(@Param("userId") Long userId,@Param("companyId") Long companyId);
 
     @Select("select * from company_user where company_id=#{companyId} and del_flag=0")
     List<CompanyUser> selectCompanyUserByCompanyId(Long companyId);
@@ -231,10 +233,11 @@ public interface CompanyUserMapper
 
     /**
      * 根据部门ID查询销售列表
+     * @param companyId 公司ID
      * @param deptId    部门ID
      * @return  list
      */
-    List<CompanyUser> selectAllCompanyUserByDeptId(@Param("deptId") Long deptId);
+    List<CompanyUser> selectAllCompanyUserByCompanyIdAndDeptId(@Param("companyId") Long companyId, @Param("deptId") Long deptId);
 
     @Select("select * from company_user where ma_open_id = #{maOpenId}")
     CompanyUser getCompanyUserByOpenId(String openId);
@@ -252,4 +255,13 @@ public interface CompanyUserMapper
     int setIsRegisterMember(@Param("status") boolean status, @Param("userIds")List<Long> userIds);
 
     void auditUsers(@Param("userIds") List<Long> userIds);
+
+    /**
+     * 获取企业用户信息
+     * @param ids 数组
+     * @return 结果
+     * **/
+    List<CompanyUser> getUserInfoByUserIds(@Param("ids") List<Long> ids);
+    @DataSource(DataSourceType.MASTER)
+    CompanyUser selectCompanyUserByPhone(String phone);
 }

+ 47 - 0
fs-service/src/main/java/com/fs/company/param/CompanyDomainBindParam.java

@@ -0,0 +1,47 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 绑定域名管理中间表接收
+ **/
+@Data
+public class CompanyDomainBindParam implements Serializable {
+    /**
+     * 企业名称
+     * **/
+    private String companyName;
+
+    /**
+     * 域名
+     * **/
+    private String domain;
+
+    /**
+     * 绑定域名ID
+     * **/
+    private Long domainId;
+
+    /**
+     * 公司ID
+     * **/
+    private Long companyId;
+
+    /**
+     * 企业Id数组
+     **/
+    private List<Long> companyIds;
+
+    /**
+     * 企业用户ids
+     * **/
+    private List<Long> companyUserIds;
+
+    /**
+     * 用户id
+     * **/
+    private String userId;
+}

+ 36 - 0
fs-service/src/main/java/com/fs/company/param/CompanyDomainBindUserParam.java

@@ -0,0 +1,36 @@
+package com.fs.company.param;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class CompanyDomainBindUserParam implements Serializable {
+    /** 中间表id */
+    private Long id;
+
+    /** 企业销售id */
+    private Long companyUserId;
+
+    /** 中间表id */
+    private Long bindId;
+
+    /**
+     * 分配域名
+     **/
+    private String domain;
+
+    /**
+     * 分配人员
+     **/
+    private String userName;
+
+    /**
+     * 分配人员手机号
+     **/
+    private String phonenumber;
+
+    /**
+     * 企业ID
+     * **/
+    private Long companyId;
+}

+ 47 - 0
fs-service/src/main/java/com/fs/company/param/CompanyDomainParam.java

@@ -0,0 +1,47 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 域名管理接收
+ **/
+@Data
+public class CompanyDomainParam implements Serializable {
+    /**
+     * 域名id
+     */
+    private Long id;
+
+    /**
+     * 域名
+     */
+    private String domain;
+
+    /**
+     * 状态(1未启用、2启用)
+     */
+    private Long status;
+
+    /**
+     * 域名ID数组
+     **/
+    private List<Long> domainIds;
+
+    /**
+     * 企业Id数组
+     **/
+    private List<Long> companyIds;
+
+    /**
+     * 用户ID
+     * **/
+    private String userId;
+
+    /**
+     * 参数ids
+     * **/
+    private Long[] ids;
+}

+ 74 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDomainBindService.java

@@ -0,0 +1,74 @@
+package com.fs.company.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.api.R;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyDomainBind;
+import com.fs.company.param.CompanyDomainBindParam;
+import com.fs.company.vo.CompanyDomainBindVo;
+
+/**
+ * 域名绑定销售公司Service接口
+ * 
+ * @author fs
+ * @date 2025-06-17
+ */
+public interface ICompanyDomainBindService extends IService<CompanyDomainBind>{
+    /**
+     * 查询域名绑定销售公司
+     * 
+     * @param id 域名绑定销售公司主键
+     * @return 域名绑定销售公司
+     */
+    CompanyDomainBind selectCompanyDomainBindById(Long id);
+
+    /**
+     * 查询域名绑定销售公司列表
+     * 
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 域名绑定销售公司集合
+     */
+    List<CompanyDomainBindVo> selectCompanyDomainBindList(CompanyDomainBindParam companyDomainBind);
+
+    /**
+     * 新增域名绑定销售公司
+     * 
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 结果
+     */
+    int insertCompanyDomainBind(CompanyDomainBind companyDomainBind);
+
+    /**
+     * 修改域名绑定销售公司
+     * 
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 结果
+     */
+    int updateCompanyDomainBind(CompanyDomainBind companyDomainBind);
+
+    /**
+     * 批量删除域名绑定销售公司
+     * 
+     * @param ids 需要删除的域名绑定销售公司主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainBindByIds(Long[] ids);
+
+    /**
+     * 删除域名绑定销售公司信息
+     * 
+     * @param id 域名绑定销售公司主键
+     * @return 结果
+     */
+    int deleteCompanyDomainBindById(Long id);
+
+
+    /**
+     * 域名批量绑定接口
+     *
+     * @param param 接收参数
+     * @return R
+     **/
+    R domainBatchBinding(CompanyDomainBindParam param);
+}

+ 63 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDomainBindUserService.java

@@ -0,0 +1,63 @@
+package com.fs.company.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyDomainBindUser;
+import com.fs.company.param.CompanyDomainBindUserParam;
+import com.fs.company.vo.CompanyDomainBindUserVo;
+
+/**
+ * 企业域名分配中间表Service接口
+ * 
+ * @author fs
+ * @date 2025-06-19
+ */
+public interface ICompanyDomainBindUserService extends IService<CompanyDomainBindUser>{
+    /**
+     * 查询企业域名分配中间表
+     * 
+     * @param id 企业域名分配中间表主键
+     * @return 企业域名分配中间表
+     */
+    CompanyDomainBindUser selectCompanyDomainBindUserById(Long id);
+
+    /**
+     * 查询企业域名分配中间表列表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 企业域名分配中间表集合
+     */
+    List<CompanyDomainBindUserVo> selectCompanyDomainBindUserList(CompanyDomainBindUserParam companyDomainBindUser);
+
+    /**
+     * 新增企业域名分配中间表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 结果
+     */
+    int insertCompanyDomainBindUser(CompanyDomainBindUser companyDomainBindUser);
+
+    /**
+     * 修改企业域名分配中间表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 结果
+     */
+    int updateCompanyDomainBindUser(CompanyDomainBindUser companyDomainBindUser);
+
+    /**
+     * 批量删除企业域名分配中间表
+     * 
+     * @param ids 需要删除的企业域名分配中间表主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainBindUserByIds(Long[] ids);
+
+    /**
+     * 删除企业域名分配中间表信息
+     * 
+     * @param id 企业域名分配中间表主键
+     * @return 结果
+     */
+    int deleteCompanyDomainBindUserById(Long id);
+}

+ 83 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDomainService.java

@@ -0,0 +1,83 @@
+package com.fs.company.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.api.R;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyDomain;
+import com.fs.company.param.CompanyDomainParam;
+import com.fs.company.vo.CompanyDomainExportTemplateVo;
+import com.fs.company.vo.CompanyDomainVo;
+
+/**
+ * 域名管路Service接口
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+public interface ICompanyDomainService extends IService<CompanyDomain> {
+    /**
+     * 查询域名管路
+     *
+     * @param id 域名管路主键
+     * @return 域名管路
+     */
+    CompanyDomain selectCompanyDomainById(Long id);
+
+    /**
+     * 查询域名管路列表
+     *
+     * @param companyDomain 域名管路
+     * @return 域名管路集合
+     */
+    List<CompanyDomainVo> selectCompanyDomainList(CompanyDomainParam companyDomain);
+
+    /**
+     * 新增域名管路
+     *
+     * @param companyDomain 域名管路
+     * @return 结果
+     */
+    int insertCompanyDomain(CompanyDomain companyDomain);
+
+    /**
+     * 修改域名管路
+     *
+     * @param companyDomain 域名管路
+     * @return 结果
+     */
+    int updateCompanyDomain(CompanyDomain companyDomain);
+
+    /**
+     * 批量删除域名管路
+     *
+     * @param ids 需要删除的域名管路主键集合
+     * @return 结果
+     */
+    int deleteCompanyDomainByIds(Long[] ids);
+
+    /**
+     * 删除域名管路信息
+     *
+     * @param id 域名管路主键
+     * @return 结果
+     */
+    int deleteCompanyDomainById(Long id);
+
+    /**
+     * 域名批量绑定接口
+     *
+     * @param param 接收参数
+     * @return R
+     **/
+    R domainBatchBinding(CompanyDomainParam param);
+
+    /**
+     * 域名批量导入
+     *
+     * @param param      参数
+     * @param domainList 解析数组
+     * @return R
+     **/
+    R importDomainData(CompanyDomainParam param, List<CompanyDomainExportTemplateVo> domainList);
+}

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

@@ -151,10 +151,11 @@ public interface ICompanyUserService {
 
     /**
      * 查询销售列表
+     * @param companyId 公司ID
      * @param deptId 部门ID
      * @return list
      */
-    List<CompanyUser> getCompanyUserListByDeptId(Long deptId);
+    List<CompanyUser> getCompanyUserListByCompanyIdAndDeptId(Long companyId, Long deptId);
 
     /**
      * 根据openid获取销售
@@ -193,4 +194,6 @@ public interface ICompanyUserService {
     Boolean setIsRegisterMember(boolean status,  List<Long> userIds);
 
     void auditUsers(List<Long> userIds);
+
+    CompanyUser selectCompanyUserByPhone(String phone);
 }

+ 166 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDomainBindServiceImpl.java

@@ -0,0 +1,166 @@
+package com.fs.company.service.impl;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.api.R;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.CompanyDomainBindUser;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyDomainBindUserMapper;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.param.CompanyDomainBindParam;
+import com.fs.company.vo.CompanyDomainBindVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyDomainBindMapper;
+import com.fs.company.domain.CompanyDomainBind;
+import com.fs.company.service.ICompanyDomainBindService;
+
+/**
+ * 域名绑定销售公司Service业务层处理
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+@Service
+public class CompanyDomainBindServiceImpl extends ServiceImpl<CompanyDomainBindMapper, CompanyDomainBind> implements ICompanyDomainBindService {
+    @Autowired
+    private CompanyDomainBindMapper baseMapper;
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+
+    @Autowired
+    private CompanyDomainBindUserMapper companyDomainBindUserMapper;
+
+    /**
+     * 查询域名绑定销售公司
+     *
+     * @param id 域名绑定销售公司主键
+     * @return 域名绑定销售公司
+     */
+    @Override
+    public CompanyDomainBind selectCompanyDomainBindById(Long id) {
+        return baseMapper.selectCompanyDomainBindById(id);
+    }
+
+    /**
+     * 查询域名绑定销售公司列表
+     *
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 域名绑定销售公司
+     */
+    @Override
+    public List<CompanyDomainBindVo> selectCompanyDomainBindList(CompanyDomainBindParam companyDomainBind) {
+        return baseMapper.selectJoinBindInfo(companyDomainBind);
+    }
+
+    /**
+     * 新增域名绑定销售公司
+     *
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyDomainBind(CompanyDomainBind companyDomainBind) {
+        companyDomainBind.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDomainBind(companyDomainBind);
+    }
+
+    /**
+     * 修改域名绑定销售公司
+     *
+     * @param companyDomainBind 域名绑定销售公司
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyDomainBind(CompanyDomainBind companyDomainBind) {
+        companyDomainBind.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyDomainBind(companyDomainBind);
+    }
+
+    /**
+     * 批量删除域名绑定销售公司
+     *
+     * @param ids 需要删除的域名绑定销售公司主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDomainBindByIds(Long[] ids) {
+        return baseMapper.deleteCompanyDomainBindByIds(ids);
+    }
+
+    /**
+     * 删除域名绑定销售公司信息
+     *
+     * @param id 域名绑定销售公司主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDomainBindById(Long id) {
+        return baseMapper.deleteCompanyDomainBindById(id);
+    }
+
+    @Override
+    public R domainBatchBinding(CompanyDomainBindParam param) {
+        //验证分配企业信息是否存在
+        List<CompanyDomainBind> companyDomainBindList = baseMapper.selectList(new LambdaQueryWrapper<CompanyDomainBind>().in(CompanyDomainBind::getId, param.getCompanyIds()));
+
+        if (companyDomainBindList.isEmpty()) {
+            return R.failed("分配域名企业数据不存在!");
+        }
+        if (companyDomainBindList.size() != param.getCompanyIds().size()) {
+            return R.failed("分配域名中存在不存在的记录!");
+        }
+
+        //验证分配人员是否存在
+        List<CompanyUser> companyUserList = companyUserMapper.getUserInfoByUserIds(param.getCompanyUserIds());
+        if (companyUserList.isEmpty()) {
+            return R.failed("分配人员数据不存在!");
+        }
+
+        if (companyUserList.size() != param.getCompanyUserIds().size()) {
+            return R.failed("分配人员中存在不存在的记录!");
+        }
+
+        //获取分配关系
+        Set<String> existingBinds = companyDomainBindUserMapper.selectList(new LambdaQueryWrapper<CompanyDomainBindUser>().in(CompanyDomainBindUser::getBindId, param.getCompanyIds()).in(CompanyDomainBindUser::getCompanyUserId, param.getCompanyUserIds())).stream()
+                .map(bind -> bind.getBindId() + "key" + bind.getCompanyUserId())
+                .collect(Collectors.toSet());
+
+        List<CompanyDomainBindUser> insertUserList = new LinkedList<>();
+        String userId = param.getUserId();
+        for (Long bind : param.getCompanyIds()) {
+            for (Long bindUser : param.getCompanyUserIds()) {
+                String key = bind + "key" + bindUser;
+                if (!existingBinds.contains(key)) {
+                    CompanyDomainBindUser user = new CompanyDomainBindUser();
+                    user.setBindId(bind);
+                    user.setCompanyUserId(bindUser);
+                    user.setCreateTime(new Date());
+                    user.setCreateBy(userId);
+                    insertUserList.add(user);
+                }
+            }
+        }
+
+        //批量插入数据
+        if (!insertUserList.isEmpty()) {
+            batchBindInsert(insertUserList, 1000);
+        }
+        return R.ok("操作成功!");
+    }
+
+    // 分批插入方法
+    private void batchBindInsert(List<CompanyDomainBindUser> list, int batchSize) {
+        for (int i = 0; i < list.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, list.size());
+            companyDomainBindUserMapper.batchInsertBindUserInfo(list.subList(i, end));
+        }
+    }
+}

+ 95 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDomainBindUserServiceImpl.java

@@ -0,0 +1,95 @@
+package com.fs.company.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.param.CompanyDomainBindUserParam;
+import com.fs.company.vo.CompanyDomainBindUserVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyDomainBindUserMapper;
+import com.fs.company.domain.CompanyDomainBindUser;
+import com.fs.company.service.ICompanyDomainBindUserService;
+
+/**
+ * 企业域名分配中间表Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-06-19
+ */
+@Service
+public class CompanyDomainBindUserServiceImpl extends ServiceImpl<CompanyDomainBindUserMapper, CompanyDomainBindUser> implements ICompanyDomainBindUserService {
+
+    /**
+     * 查询企业域名分配中间表
+     * 
+     * @param id 企业域名分配中间表主键
+     * @return 企业域名分配中间表
+     */
+    @Override
+    public CompanyDomainBindUser selectCompanyDomainBindUserById(Long id)
+    {
+        return baseMapper.selectCompanyDomainBindUserById(id);
+    }
+
+    /**
+     * 查询企业域名分配中间表列表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 企业域名分配中间表
+     */
+    @Override
+    public List<CompanyDomainBindUserVo> selectCompanyDomainBindUserList(CompanyDomainBindUserParam companyDomainBindUser)
+    {
+        return baseMapper.selectDoaminBindUserJoinInfo(companyDomainBindUser);
+    }
+
+    /**
+     * 新增企业域名分配中间表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyDomainBindUser(CompanyDomainBindUser companyDomainBindUser)
+    {
+        companyDomainBindUser.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDomainBindUser(companyDomainBindUser);
+    }
+
+    /**
+     * 修改企业域名分配中间表
+     * 
+     * @param companyDomainBindUser 企业域名分配中间表
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyDomainBindUser(CompanyDomainBindUser companyDomainBindUser)
+    {
+        return baseMapper.updateCompanyDomainBindUser(companyDomainBindUser);
+    }
+
+    /**
+     * 批量删除企业域名分配中间表
+     * 
+     * @param ids 需要删除的企业域名分配中间表主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDomainBindUserByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyDomainBindUserByIds(ids);
+    }
+
+    /**
+     * 删除企业域名分配中间表信息
+     * 
+     * @param id 企业域名分配中间表主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDomainBindUserById(Long id)
+    {
+        return baseMapper.deleteCompanyDomainBindUserById(id);
+    }
+}

+ 274 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDomainServiceImpl.java

@@ -0,0 +1,274 @@
+package com.fs.company.service.impl;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.api.R;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyDomainBind;
+import com.fs.company.mapper.CompanyDomainBindMapper;
+import com.fs.company.mapper.CompanyMapper;
+import com.fs.company.param.CompanyDomainParam;
+import com.fs.company.vo.CompanyDomainExportTemplateVo;
+import com.fs.company.vo.CompanyDomainVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyDomainMapper;
+import com.fs.company.domain.CompanyDomain;
+import com.fs.company.service.ICompanyDomainService;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 域名管路Service业务层处理
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@Service
+public class CompanyDomainServiceImpl extends ServiceImpl<CompanyDomainMapper, CompanyDomain> implements ICompanyDomainService {
+    @Autowired
+    private CompanyMapper companyMapper;
+
+    @Autowired
+    private CompanyDomainMapper baseMapper;
+
+    @Autowired
+    private CompanyDomainBindMapper companyDomainBindMapper;
+
+    /**
+     * 查询域名管路
+     *
+     * @param id 域名管路主键
+     * @return 域名管路
+     */
+    @Override
+    public CompanyDomain selectCompanyDomainById(Long id) {
+        return baseMapper.selectCompanyDomainById(id);
+    }
+
+    /**
+     * 查询域名管路列表
+     *
+     * @param companyDomain 域名管路
+     * @return 域名管路
+     */
+    @Override
+    public List<CompanyDomainVo> selectCompanyDomainList(CompanyDomainParam companyDomain) {
+        return baseMapper.selectCompanyDomainJoinInfo(companyDomain);
+    }
+
+    /**
+     * 新增域名管路
+     *
+     * @param companyDomain 域名管路
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int insertCompanyDomain(CompanyDomain companyDomain) {
+        //验证是否域名重复
+        if (baseMapper.selectCount(new LambdaQueryWrapper<CompanyDomain>().eq(CompanyDomain::getDomain, companyDomain.getDomain()).eq(CompanyDomain::getStatus, 1)) > 0) {
+            throw new ServiceException("更新失败,当前域名" + companyDomain.getDomain() + "已存在!");
+        }
+
+        companyDomain.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDomain(companyDomain);
+    }
+
+    /**
+     * 修改域名管路
+     *
+     * @param companyDomain 域名管路
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int updateCompanyDomain(CompanyDomain companyDomain) {
+        //验证数据是否存在
+        CompanyDomain domain = baseMapper.selectById(companyDomain.getId());
+        if (ObjectUtil.isEmpty(domain)) {
+            throw new ServiceException("操作失败,更新数据不存在!");
+        }
+
+        //验证是否域名重复
+        if (baseMapper.selectCount(new LambdaQueryWrapper<CompanyDomain>().eq(CompanyDomain::getDomain, companyDomain.getDomain()).eq(CompanyDomain::getStatus, 1).ne(CompanyDomain::getId, companyDomain.getId())) > 0) {
+            throw new ServiceException("更新失败,当前域名" + companyDomain.getDomain() + "已存在!");
+        }
+
+        companyDomain.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyDomain(companyDomain);
+    }
+
+    /**
+     * 批量删除域名管路
+     *
+     * @param ids 需要删除的域名管路主键
+     * @return 结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int deleteCompanyDomainByIds(Long[] ids) {
+        //查询域名
+        CompanyDomainParam companyDomain = new CompanyDomainParam();
+        companyDomain.setIds(ids);
+        List<CompanyDomainVo> domainList = baseMapper.selectCompanyDomainJoinInfo(companyDomain);
+        if (domainList.isEmpty() || domainList.size() != ids.length) {
+            throw new ServiceException("域名数量不一致,请核对后再试!");
+        }
+
+//        Map<Long, CompanyDomainVo> checkMap = domainList.stream().collect(Collectors.toMap(CompanyDomainVo::getId, d -> d));
+//        //查询是否有分配
+//        CompanyDomainVo checkVo;
+//        for (Long id : ids) {
+//            checkVo = checkMap.get(id);
+//            if (ObjectUtil.isNotNull(checkVo)) {
+//                if (ObjectUtil.isNotNull(checkVo.getBindNum()) && checkVo.getBindNum() > 0) {
+//                    throw new ServiceException("删除失败,当前域名" + checkVo.getDomain() + "已绑定" + checkVo.getBindNum() + "家销售公司,请解绑后操作!");
+//                } else if (checkVo.getStatus() == 1) {
+//                    throw new ServiceException("删除失败,当前域名" + checkVo.getDomain() + "状态正常启用中,请关闭后操作!");
+//                }
+//            }
+//        }
+        //删除域名企业中间表数据
+        companyDomainBindMapper.deleteCompanyDomainBindBydomainIds(ids);
+        //删除企业销售中间表数据
+
+        return baseMapper.deleteCompanyDomainByIds(ids);
+    }
+
+    /**
+     * 删除域名管路信息
+     *
+     * @param id 域名管路主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDomainById(Long id) {
+        return baseMapper.deleteCompanyDomainById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R domainBatchBinding(CompanyDomainParam param) {
+        // 验证绑定域名是否存在(批量查询)
+        List<CompanyDomain> domainList = baseMapper.selectList(
+                new LambdaQueryWrapper<CompanyDomain>()
+                        .select(CompanyDomain::getId)
+                        .in(CompanyDomain::getId, param.getDomainIds())
+                        .eq(CompanyDomain::getStatus, 1)
+        );
+
+        if (domainList.isEmpty()) {
+            return R.failed("绑定域名数据不存在!");
+        }
+
+        if (domainList.size() != param.getDomainIds().size()) {
+            return R.failed("绑定域名中存在状态非激活或不存在的记录!");
+        }
+
+        // 验证绑定企业是否存在
+        List<Company> companyList = companyMapper.selectCompanyByIds(param.getCompanyIds());
+        if (companyList.isEmpty()) {
+            return R.failed("绑定企业数据不存在!");
+        }
+
+        if (companyList.size() != param.getCompanyIds().size()) {
+            return R.failed("绑定企业中存在不存在的记录!");
+        }
+
+        // 获取已存在的绑定关系
+        Set<String> existingBinds = companyDomainBindMapper.selectList(
+                        new LambdaQueryWrapper<CompanyDomainBind>()
+                                .select(CompanyDomainBind::getDomainId, CompanyDomainBind::getCompanyId)
+                                .in(CompanyDomainBind::getDomainId, param.getDomainIds())
+                                .in(CompanyDomainBind::getCompanyId, param.getCompanyIds())
+                )
+                .stream()
+                .map(bind -> bind.getDomainId() + "key" + bind.getCompanyId())
+                .collect(Collectors.toSet());
+
+        // 生成待插入的绑定关系(优化集合操作)
+        List<CompanyDomainBind> insertList = new ArrayList<>();
+        String userId = param.getUserId();  // 假设此处为userId
+
+        for (Long domainId : param.getDomainIds()) {
+            for (Company company : companyList) {
+                String key = domainId + "key" + company.getCompanyId();
+                if (!existingBinds.contains(key)) {
+                    insertList.add(createBind(domainId, company, userId));
+                }
+            }
+        }
+
+        // 批量插入数据(修正条件判断)
+        if (!insertList.isEmpty()) {
+            // 分批处理,每批1000条
+            batchBindInsert(insertList, 1000);
+        }
+
+        return R.ok("操作成功!");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R importDomainData(CompanyDomainParam param, List<CompanyDomainExportTemplateVo> domainList) {
+        if (!domainList.isEmpty()) {
+            //查询域名列表数据
+            List<String> domains = domainList.stream().map(CompanyDomainExportTemplateVo::getDomain).collect(Collectors.toList());
+            List<CompanyDomain> companyDomainList = baseMapper.selectList(new LambdaQueryWrapper<CompanyDomain>().select(CompanyDomain::getDomain).eq(CompanyDomain::getStatus, 1).in(CompanyDomain::getDomain, domains));
+            Map<String, Long> checkMap = companyDomainList.stream().collect(Collectors.toMap(CompanyDomain::getDomain, s -> 0L));
+            //便利数据
+            List<CompanyDomain> insertList = new LinkedList<>();
+            String userId = param.getUserId();
+            domainList.stream().filter(d -> ObjectUtil.isNotNull(d.getDomain())).forEach(f -> {
+                if (!checkMap.containsKey(f.getDomain())) {
+                    CompanyDomain domain = new CompanyDomain();
+                    domain.setStatus(1L);//默认启用
+                    domain.setCreateBy(userId);
+                    domain.setDomain(f.getDomain());
+                    domain.setRemark(f.getRemark());
+                    domain.setCreateTime(new Date());
+                    insertList.add(domain);
+                    checkMap.put(f.getDomain(), 0L);
+                }
+            });
+
+            //批量插入
+            if (!insertList.isEmpty()) {
+                batchDomainInsert(insertList, 1000);
+            }
+        }
+        return R.ok(null);
+    }
+
+    // 辅助方法:创建绑定对象
+    private CompanyDomainBind createBind(Long domainId, Company company, String userId) {
+        CompanyDomainBind bind = new CompanyDomainBind();
+        bind.setDomainId(domainId);
+        bind.setCompanyId(company.getCompanyId());
+        bind.setCreateBy(userId);
+        bind.setCreateTime(new Date());  // 补充创建时间
+        return bind;
+    }
+
+    // 分批插入方法
+    private void batchBindInsert(List<CompanyDomainBind> list, int batchSize) {
+        for (int i = 0; i < list.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, list.size());
+            companyDomainBindMapper.batchInsertBindInfo(list.subList(i, end));
+        }
+    }
+
+    // 分批插入方法
+    private void batchDomainInsert(List<CompanyDomain> list, int batchSize) {
+        for (int i = 0; i < list.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, list.size());
+            baseMapper.batchDomainInfo(list.subList(i, end));
+        }
+    }
+}

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

@@ -160,6 +160,7 @@ public class CompanyServiceImpl implements ICompanyService
             user.setSex("0");
             user.setStatus("0");
             user.setIsDel(0);
+            user.setIsAudit(1);
             userMapper.insertCompanyUser(user);
             //添加用户角色表
             CompanyUserRole userRole=new CompanyUserRole();

+ 7 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyTagUserServiceImpl.java

@@ -117,8 +117,14 @@ public class CompanyTagUserServiceImpl implements ICompanyTagUserService
                 keywords = param.getKeyword().split(",");
             }
         }
+        // 判断是否是管理员
+        Long companyId = null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param != null ? param.getUserId() : null);
+        if (companyUser != null && companyUser.isAdmin()){
+            companyId = companyUser.getCompanyId();
+        }
         //获取所有销售
-        List<CompanyUser> companyUsers = companyUserMapper.selectAllCompanyUserAndSelf(param != null ? param.getUserId() : null);
+        List<CompanyUser> companyUsers = companyUserMapper.selectAllCompanyUserAndSelf(param != null ? param.getUserId() : null, companyId);
         List<Long> userIds = Collections.emptyList();
         if(companyUsers != null && !companyUsers.isEmpty()){
             userIds = companyUsers.stream().map(CompanyUser::getUserId).collect(Collectors.toList());

+ 16 - 3
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -452,17 +452,25 @@ public class CompanyUserServiceImpl implements ICompanyUserService
 
     @Override
     public List<CompanyUser> selectAllCompanyUserAndSelf(Long userId) {
-        return companyUserMapper.selectAllCompanyUserAndSelf(userId);
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        List<CompanyUser> list = new ArrayList<>();
+        if (companyUser.isAdmin()){
+            list = companyUserMapper.selectAllCompanyUserAndSelf(userId,companyUser.getCompanyId());
+        }else {
+            list =   companyUserMapper.selectAllCompanyUserAndSelf(userId,null);
+        }
+        return list;
     }
 
     /**
      * 查询销售列表
+     * @param companyId 公司ID
      * @param deptId 部门ID
      * @return list
      */
     @Override
-    public List<CompanyUser> getCompanyUserListByDeptId(Long deptId) {
-        return companyUserMapper.selectAllCompanyUserByDeptId(deptId);
+    public List<CompanyUser> getCompanyUserListByCompanyIdAndDeptId(Long companyId, Long deptId) {
+        return companyUserMapper.selectAllCompanyUserByCompanyIdAndDeptId(companyId, deptId);
     }
 
     @Override
@@ -512,4 +520,9 @@ public class CompanyUserServiceImpl implements ICompanyUserService
         }
         companyUserMapper.auditUsers(userIds);
     }
+
+    @Override
+    public CompanyUser selectCompanyUserByPhone(String phone) {
+        return companyUserMapper.selectCompanyUserByPhone(phone);
+    }
 }

+ 58 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyDomainBindUserVo.java

@@ -0,0 +1,58 @@
+package com.fs.company.vo;
+
+import com.baidu.dev2.thirdparty.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 企业域名分配中间表对象 company_domain_bind_user
+ *
+ * @author fs
+ * @date 2025-06-19
+ */
+@Data
+public class CompanyDomainBindUserVo implements Serializable {
+
+    /**
+     * 中间表id
+     */
+    @Excel(name = "编号", sort = 0)
+    private Long id;
+
+    /**
+     * 企业销售id
+     */
+    private Long companyUserId;
+
+    /**
+     * 中间表id
+     */
+    private Long bindId;
+
+    /**
+     * 分配域名
+     **/
+    @Excel(name = "分配域名", sort = 1)
+    private String domain;
+
+    /**
+     * 分配人员
+     **/
+    @Excel(name = "分配人员", sort = 2)
+    private String userName;
+
+    /**
+     * 分配人员手机号
+     **/
+    @Excel(name = "分配人员手机号", sort = 3)
+    private String phonenumber;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @Excel(name = "绑定时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", sort = 4)
+    private Date createTime;
+}

+ 59 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyDomainBindVo.java

@@ -0,0 +1,59 @@
+package com.fs.company.vo;
+
+import com.baidu.dev2.thirdparty.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 域名绑定销售公司对象 company_domain_bind
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+@Data
+public class CompanyDomainBindVo implements Serializable {
+
+    /**
+     * 域名绑定中间表
+     */
+    @Excel(name = "编码", sort = 0)
+    private Long id;
+
+    /**
+     * 销售公司ID
+     */
+    @Excel(name = "销售公司编码", sort = 1)
+    private Long companyId;
+
+    /**
+     * 绑定域名ID
+     */
+    @Excel(name = "绑定域名编码", sort = 3)
+    private Long domainId;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @Excel(name = "绑定时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", sort = 5)
+    private Date createTime;
+
+    /**
+     * 域名名称
+     **/
+    @Excel(name = "域名", sort = 4)
+    private String domain;
+
+    /**
+     * 公司
+     **/
+    @Excel(name = "公司", sort = 2)
+    private String companyName;
+
+    /**
+     * 分配数量
+     * **/
+    @Excel(name = "分配数量", sort = 4)
+    private int allocationNum;
+
+}

+ 26 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyDomainExportTemplateVo.java

@@ -0,0 +1,26 @@
+package com.fs.company.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import java.io.Serializable;
+
+/**
+ * 域名管路导入模板对象 CompanyDomainExportTemplateVo
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@Data
+public class CompanyDomainExportTemplateVo implements Serializable {
+
+    /**
+     * 域名
+     */
+    @Excel(name = "域名", sort = 1,width = 30)
+    private String domain;
+    /**
+     * 备注
+     **/
+    @Excel(name = "备注", sort = 2,width = 50)
+    private String remark;
+}

+ 52 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyDomainVo.java

@@ -0,0 +1,52 @@
+package com.fs.company.vo;
+
+import com.baidu.dev2.thirdparty.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 域名管路对象 company_domain
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@Data
+public class CompanyDomainVo implements Serializable {
+
+    /**
+     * 域名id
+     */
+    @Excel(name = "编号", sort = 0)
+    private Long id;
+
+    /**
+     * 域名
+     */
+    @Excel(name = "域名", sort = 1)
+    private String domain;
+
+    /**
+     * 状态(0=停用、1正常)
+     */
+    @Excel(name = "状态", dictType = "sys_company_status", sort = 3)
+    private Long status;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", sort = 4)
+    private Date createTime;
+
+    /**
+     * 备注
+     **/
+    @Excel(name = "备注", sort = 5)
+    private String remark;
+
+    /**
+     * 绑定数量
+     **/
+    @Excel(name = "域名绑定企业数量", sort = 2)
+    private int bindNum;
+}

+ 8 - 9
fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java

@@ -47,10 +47,9 @@ public class WxMaConfiguration {
         WxMaConfig.Config config = JSON.parseObject(configValue, WxMaConfig.Config.class);
         WxMaConfig wx = new WxMaConfig();
         List<WxMaConfig.Config> c = new ArrayList<>();
-//        if (StringUtil.isNotEmpty(config.getAppid())){
-//            c.add(config);
-//        }
-        c.add(config);
+        if (StringUtil.isNotEmpty(config.getAppid())){
+            c.add(config);
+        }
         String appid = config.getAppid();
 //        SysConfig sysConfig2 = sysConfigMapper.selectConfigByConfigKey("store.config");
 //        if (sysConfig2!=null&&sysConfig2.getConfigValue()!=null&&sysConfig2.getConfigValue()!="") {
@@ -63,12 +62,12 @@ public class WxMaConfiguration {
         List<CourseMaConfig> courseMaConfigs = JSON.parseArray(sysConfig3.getConfigValue(), CourseMaConfig.class);
         if (courseMaConfigs!=null&& !courseMaConfigs.isEmpty()){
             for (CourseMaConfig courseMaConfig : courseMaConfigs) {
-                if (!appid.equals(courseMaConfig.getAppid())) {
-                    WxMaConfig.Config wxMaConfig = new WxMaConfig.Config();
-                    BeanUtils.copyProperties(courseMaConfig, wxMaConfig);
-                    c.add(wxMaConfig);
+                if (appid.equals(courseMaConfig.getAppid())) {
+                    continue;
                 }
-
+                WxMaConfig.Config wxMaConfig = new WxMaConfig.Config();
+                BeanUtils.copyProperties(courseMaConfig, wxMaConfig);
+                c.add(wxMaConfig);
             }
         }
         wx.setConfigs(c);

+ 3 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseAnswerLogs.java

@@ -66,4 +66,7 @@ public class FsCourseAnswerLogs extends BaseEntity {
     private String questionJson;
 
     private Long watchLogId;//绑定观看的记录id
+
+    /** 营期id */
+    private Long periodId;
 }

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

@@ -2,6 +2,7 @@ package com.fs.course.domain;
 
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 /**
@@ -57,6 +58,11 @@ public class FsCourseLink extends BaseEntity
     private Integer isRoom;//是否发群
     private String chatId;//是否发群
 
+    private Long periodId;
+
+    @ApiModelProperty(value = "营期课程id")
+    private Long id;
+
 //    private String link_uuid;
 
 }

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

@@ -2,6 +2,7 @@ package com.fs.course.domain;
 
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -36,4 +37,10 @@ public class FsCourseRealLink implements Serializable
     private Integer isRoom;
     private Long fsUserId;
 
+    private Long periodId;
+
+    @ApiModelProperty(value = "营期课程id")
+    private Long id;
+
+
 }

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

@@ -57,4 +57,6 @@ public class FsCourseRedPacketLog extends BaseEntity
     /** 营期id */
     private Long periodId;
 
+    private String result;
+
 }

+ 11 - 2
fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java

@@ -103,9 +103,10 @@ public interface FsCourseRedPacketLogMapper
     List<FsCourseRedPacketLogListPVO> selectRedPacketLogListVO(@Param("maps") FsCourseRedPacketLogParam param);
 
     @Select({"<script> " +
-            "select l.*,v.title,u.nick_name as fsNickName,u.avatar as fsAvatar,u.phone,cu.nick_name company_user_name,c.company_name,qu.qw_user_name  from fs_course_red_packet_log l  \n" +
+            "select l.*,v.title,u.nick_name as fsNickName,u.avatar as fsAvatar,u.phone,cu.nick_name company_user_name,c.company_name,qu.qw_user_name,fuc.course_name,u.phone as phoneNumber   from fs_course_red_packet_log l  \n" +
             "left join fs_user_course_video v on v.video_id = l.video_id \n" +
             "left join fs_user u on u.user_id = l.user_id \n" +
+            "left join fs_user_course fuc on fuc.course_id = l.course_id \n" +
             "left join company_user cu on cu.user_id=l.company_user_id \n" +
             "left join company c on c.company_id=l.company_id \n" +
             "LEFT JOIN qw_user qu on qu.id= l.qw_user_id  " +
@@ -118,7 +119,7 @@ public interface FsCourseRedPacketLogMapper
             "<if test = ' maps.courseId !=null '> and l.course_id = #{maps.courseId} </if>" +
             "<if test = ' maps.videoId !=null '> and l.video_id = #{maps.videoId} </if>" +
             "<if test = ' maps.status !=null '> and l.status = #{maps.status} </if>" +
-            "<if test = ' maps.phone !=null '> and u.phone = #{maps.phone} </if>" +
+            "<if test = \"maps.phone !=null and maps.phone != '' \"> and u.phone = #{maps.phone} </if>" +
             "<if test = ' maps.qwUserId !=null '> and l.qw_user_id = #{maps.qwUserId} </if>" +
             "<if test=\"maps.sTime != null \">  and DATE(l.create_time) &gt;= DATE(#{maps.sTime})</if>\n" +
             "<if test=\"maps.eTime != null \">  and DATE(l.create_time) &lt;= DATE(#{maps.eTime})</if>\n" +
@@ -143,4 +144,12 @@ public interface FsCourseRedPacketLogMapper
     int getCountByCompanyUserIdId(@Param("companyUserId") Long companyUserId);
 
     BigDecimal getNewVipRedPackAmountByCompanyUserIdId(@Param("companyUserId") Long companyUserId);
+
+    /**
+     * 查询红包金额数
+     * @param companyUserId 销售ID
+     * @return amount
+     */
+    @Select("select ifnull(sum(fcrpl.amount), 0) from fs_course_red_packet_log fcrpl where fcrpl.company_user_id = #{companyUserId}")
+    BigDecimal getSumByCompanyUserIdId(@Param("companyUserId") Long companyUserId);
 }

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

@@ -396,4 +396,11 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
     @Select("select * from fs_course_watch_log where user_id = #{userId} and video_id = #{videoId} and send_type = 1")
     FsCourseWatchLog getCourseWatchLogByUser(@Param("userId") Long userId, @Param("videoId") Long videoId);
+
+    /**
+     * 根据条件查询条数
+     * @param params    参数
+     * @return  count
+     */
+    int countByMap(@Param("params") Map<String, Object> params);
 }

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseLinkMiniParam.java

@@ -15,6 +15,7 @@ public class FsCourseLinkMiniParam {
 
     private String title;//视频标题
 
+    private Integer type;
     /**
     * 客户表的主键
     */

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseLinkRoomParam.java

@@ -14,5 +14,6 @@ public class FsCourseLinkRoomParam {
     private Long courseId;
 
     private String title;//视频标题
+    private String chatId;
 
 }

+ 1 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseQuestionAnswerUParam.java

@@ -30,5 +30,6 @@ public class FsCourseQuestionAnswerUParam implements Serializable
     private Long duration;
     private Long qwExternalId;
     private List<FsCourseQuestionBank> questions;
+    private Long periodId;
 
 }

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

@@ -35,6 +35,8 @@ public class FsCourseWatchLogListParam implements Serializable {
 
     private String sopDate;
 
+    private Integer sendType;
+
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date eTime;
 

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

@@ -61,5 +61,7 @@ public class FsUserCourseVideoAddKfUParam implements Serializable {
     private String link;
 
     private Integer isRoom;
+    private String chatId;
+    private String nickName;
 
 }

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

@@ -3,6 +3,8 @@ package com.fs.course.service;
 import com.fs.common.core.domain.R;
 import com.fs.course.domain.FsCourseLink;
 import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsCourseLinkRoomParam;
+import com.fs.qw.domain.QwUser;
 
 import java.util.List;
 
@@ -79,4 +81,6 @@ public interface IFsCourseLinkService
     R createRoomLinkUrl(FsCourseLinkCreateParam param);
 
     R getRealLinkH5(String link);
+
+    R createRoomLink(FsCourseLinkRoomParam param, QwUser qwUser);
 }

+ 113 - 14
fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -1,47 +1,51 @@
 package com.fs.course.service.impl;
 
-import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
-import com.fs.company.domain.CompanyUser;
+import com.fs.common.utils.date.DateUtil;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.FsCourseDomainName;
-import com.fs.course.domain.FsCourseLink;
-import com.fs.course.domain.FsCourseRealLink;
-import com.fs.course.domain.FsUserCourseVideo;
+import com.fs.course.domain.*;
 import com.fs.course.mapper.FsCourseDomainNameMapper;
 import com.fs.course.mapper.FsCourseLinkMapper;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.FsCourseLinkCreateParam;
-import com.fs.course.service.IFsCourseDomainNameService;
+import com.fs.course.param.FsCourseLinkRoomParam;
 import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.qw.domain.QwGroupChat;
+import com.fs.qw.domain.QwGroupChatUser;
 import com.fs.qw.domain.QwUser;
+import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.service.IQwGroupChatService;
+import com.fs.qw.service.IQwGroupChatUserService;
+import com.fs.qw.vo.GroupUserExternalVo;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
-import org.checkerframework.checker.units.qual.A;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.UnsupportedEncodingException;
-import java.net.URI;
 import java.net.URLDecoder;
-import java.security.SecureRandom;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.*;
 
 import static com.fs.course.utils.LinkUtil.generateRandomStringWithLock;
+import static com.fs.sop.service.impl.SopUserLogsInfoServiceImpl.convertStringToDate;
 
 /**
  * 短链Service业务层处理
@@ -60,6 +64,21 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
 
     @Autowired
     private FsCourseDomainNameMapper fsCourseDomainNameMapper;
+
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private IQwGroupChatService qwGroupChatService;
+
+    @Autowired
+    private IQwGroupChatUserService qwGroupChatUserService;
     @Autowired
     private QwUserMapper qwUserMapper;
     @Autowired
@@ -202,7 +221,7 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
     }
 
     @Override
-    public R createRoomLinkUrl(FsCourseLinkCreateParam param) {
+    public R  createRoomLinkUrl(FsCourseLinkCreateParam param) {
 //        QwUser qwUser;
 //        if(param.getQwUserId() != null){
 //            qwUser = qwUserMapper.selectById(param.getQwUserIdLong());
@@ -533,14 +552,14 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
                         return R.error("真实链接不存在").put("realLink", sLink);
                     }
                     log.info("链接仍然有效: {}", sLink);
-                    String json = configService.selectConfigByKey("h5.course.config");
+                    String json = configService.selectConfigByKey("course.config");
                     CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
                     String domainName = companyUserMapper.selectDomainByUserId(courseLink.getCompanyUserId());
                     if (StringUtils.isEmpty(domainName)){
                         domainName = config.getRealLinkDomainName();
                     }
                     String realLink = domainName+courseLink.getRealLink();
-                    String imgUrl = "";
+                    String imgUrl = cloudHostProper.getHeaderImg();
                     return R.ok().put("realLink",realLink ).put("config",config).put("headerImg",imgUrl);
                 }
             } else {
@@ -559,4 +578,84 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
             return R.error("发生未知错误,请稍后再试").put("realLink", link);
         }
     }
+
+    @Override
+    public R createRoomLink(FsCourseLinkRoomParam param, QwUser qwUser) {
+        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());
+        createParam.setChatId(param.getChatId());
+        String linkUrl;
+        R createLink = createRoomLinkUrl(createParam);
+        if (createLink.get("code").equals(500)){
+            return R.error("链接生成失败!");
+        }
+
+        QwGroupChat groupChat = qwGroupChatService.selectQwGroupChatByChatId(param.getChatId());
+        List<QwGroupChatUser> qwGroupChatUserList = qwGroupChatUserService.selectQwGroupChatUserByChatIds(new String[]{param.getChatId()});
+        List<String> groupChatUserIds = PubFun.listToNewList(qwGroupChatUserList, QwGroupChatUser::getUserId);
+        if(!groupChatUserIds.isEmpty()){
+            List<GroupUserExternalVo> userList = qwExternalContactMapper.selectByGroupUser(groupChatUserIds);
+            Map<String, List<GroupUserExternalVo>> userMap = PubFun.listToMapByGroupList(userList, GroupUserExternalVo::getExternalUserId);
+            qwGroupChatUserList.forEach(e -> {
+                e.setUserList(userMap.getOrDefault(e.getUserId(), Collections.emptyList()));
+            });
+        }
+        try {
+            qwGroupChatUserList.stream().filter(e -> e.getUserList() != null && !e.getUserList().isEmpty()).forEach(e -> {
+                Map<String, GroupUserExternalVo> userMap = PubFun.listToMapByGroupObject(e.getUserList(), GroupUserExternalVo::getUserId);
+                GroupUserExternalVo vo = userMap.get(groupChat.getOwner());
+                if (vo != null && vo.getId() != null) {
+                    addWatchLogIfNeeded(param.getVideoId(), param.getCourseId(), vo.getFsUserId(), qwUser.getId(), qwUser.getCompanyUserId(), qwUser.getCompanyId(), vo.getId(), new Date(), new Date());
+                }
+            });
+        } catch (Exception e) {
+            log.error("群聊创建看课记录失败!", e);
+        }
+
+        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);
+    }
+
+
+    private void addWatchLogIfNeeded(Long videoId, Long courseId,
+                                     Long fsUserId, Long qwUserId, Long companyUserId,
+                                     Long companyId, Long externalId, Date startTime,Date createTime) {
+
+        try {
+            FsCourseWatchLog watchLog = new FsCourseWatchLog();
+            watchLog.setVideoId(videoId);
+            watchLog.setQwExternalContactId(externalId);
+            watchLog.setSendType(2);
+            watchLog.setQwUserId(qwUserId);
+            watchLog.setDuration(0L);
+            watchLog.setCourseId(courseId);
+            watchLog.setCompanyUserId(companyUserId);
+            watchLog.setCompanyId(companyId);
+            watchLog.setCreateTime(createTime);
+            watchLog.setUpdateTime(createTime);
+            watchLog.setLogType(3);
+            watchLog.setUserId(fsUserId);
+            watchLog.setCampPeriodTime(startTime);
+            //存看课记录
+            fsCourseWatchLogMapper.insertOrUpdateFsCourseWatchLog(watchLog);
+        }catch (Exception e){
+            log.error("一键群发失败-插入观看记录失败:"+e.getMessage());
+        }
+
+    }
+
+
 }

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

@@ -230,6 +230,7 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
         logs.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null );
         logs.setQuestionJson(JSONObject.toJSONString(param.getQuestions()));
         logs.setCreateTime(new Date());
+        logs.setPeriodId(param.getPeriodId());
 
         if (thisRightCount == param.getQuestions().size()) {
             logs.setIsRight(1);

+ 6 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java

@@ -122,9 +122,14 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
         return fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogListVONew(fsCourseRedPacketLog);
     }
 
+    /**
+     * 查询红包金额数
+     * @param companyUserId 销售ID
+     * @return amount
+     */
     @Override
     public BigDecimal getSumByCompanyUserIdId(Long companyUserId) {
-        return null;
+        return fsCourseRedPacketLogMapper.getSumByCompanyUserIdId(companyUserId);
     }
 
     @Override

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

@@ -345,7 +345,7 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Override
     public int countByMap(Map<String, Object> params) {
-        return 0;
+        return baseMapper.countByMap(params);
     }
 
     @Override

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

@@ -650,20 +650,37 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         }
         return domainName;
     }
+
     public static String generateRandomString() {
         return FsCourseLinkServiceImpl.generateRandomString();
     }
+
     private static Calendar getExpireDay(FsCourseLinkCreateParam param, CourseConfig config, Date createTime) {
-        Integer expireDuration;
-        if (param.getEffectiveDuration() == null || param.getEffectiveDuration() == 0){
-            expireDuration = config.getVideoLinkExpireDate();
-        }else {
-            expireDuration = param.getEffectiveDuration();
+        if (param == null || config == null || createTime == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
         }
-        // 设置过期时间
+
         Calendar calendar = Calendar.getInstance();
-        calendar.setTime(createTime);
-        calendar.add(Calendar.MINUTE, expireDuration);
+
+        if (param.getEffectiveDuration() == null || param.getEffectiveDuration() == 0) {
+            // 未传时间分钟的走配置,当天24:00(次日0:00)过期
+            Integer expireDays = config.getVideoLinkExpireDate() != null ? config.getVideoLinkExpireDate() : 1; // 默认1天
+
+            Date baseDate = param.getSendTime() != null ? param.getSendTime() : createTime;
+            calendar.setTime(baseDate);
+            calendar.add(Calendar.DAY_OF_MONTH, expireDays);
+
+            // 设置为次日0:00(即当天的24:00)
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MILLISECOND, 0);
+        } else {
+            // 传入时间分钟的
+            calendar.setTime(createTime);
+            calendar.add(Calendar.MINUTE, param.getEffectiveDuration());
+        }
+
         return calendar;
     }
     /**

+ 143 - 43
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -51,6 +51,7 @@ import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qwApi.Result.QwAddContactWayResult;
+import com.fs.qwApi.Result.QwGroupChatDetailsResult;
 import com.fs.qwApi.param.QwAddContactWayParam;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.repeat.vo.RepeatUploadVo;
@@ -59,6 +60,7 @@ import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
+import com.fs.wx.cp.config.WxCpProperties;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
@@ -391,7 +393,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     }
 
     @Override
-    @Transactional
     public R isAddKf(FsUserCourseVideoAddKfUParam param) {
         logger.info("zyp \n【判断添加客服】:{}",param);
         //查询用户
@@ -433,35 +434,101 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     }
 
     private R handleRoom(FsUserCourseVideoAddKfUParam param,FsUser user) {
-        //查询客户列表
-        List<QwExternalContact> contacts = qwExternalContactMapper.selectQwExternalContactListVOByfsUserId(user.getUserId());
-        if (contacts.isEmpty()){
-            return R.error("未注册");
-        }
-        //找出对应销售匹配的客户
-        QwExternalContact matchedContact = contacts.stream()
-                .filter(contact -> contact.getQwUserId().equals(Long.parseLong(param.getQwUserId())))
-                .findFirst()
-                .orElse(null);
-
-        if (matchedContact==null){
-            return R.error("无权限观看,未添加本群主");
-        }
-
-        param.setQwExternalId(matchedContact.getId());
-//        //查询是否有添加客服
-//        QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(param.getQwExternalId());
-//        if (externalContact==null){
-//            return R.error("客户不存在!");
+//        //查询客户列表
+//        List<QwExternalContact> contacts = qwExternalContactMapper.selectQwExternalContactListVOByfsUserId(user.getUserId());
+//        if (contacts.isEmpty()){
+//            return R.error("未注册");
 //        }
-//        if (!externalContact.getQwUserId().equals(param.getUserId())){
-//            return R.error("无权限观看,添加群主非本群主");
+//        //找出对应销售匹配的客户
+//        QwExternalContact matchedContact = contacts.stream()
+//                .filter(contact -> contact.getQwUserId().equals(Long.parseLong(param.getQwUserId())))
+//                .findFirst()
+//                .orElse(null);
+//
+//        if (matchedContact==null){
+//            return R.error("无权限观看,未添加本群主");
 //        }
-        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(param.getQwExternalId(), param.getVideoId(),param.getQwUserId());
-        if (log==null){
-            createWatchLog(param);
+//
+//        param.setQwExternalId(matchedContact.getId());
+////        //查询是否有添加客服
+////        QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(param.getQwExternalId());
+////        if (externalContact==null){
+////            return R.error("客户不存在!");
+////        }
+////        if (!externalContact.getQwUserId().equals(param.getUserId())){
+////            return R.error("无权限观看,添加群主非本群主");
+////        }
+//        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(param.getQwExternalId(), param.getVideoId(),param.getQwUserId());
+//        if (log==null){
+//            createWatchLog(param);
+//        }
+//        return R.ok().put("qwExternalId",matchedContact.getId());
+        FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLink(param.getLink());
+        String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +
+                "\t\t\t\t\t<div style=\"color: #999;font-size: 14px;font-weight: bold;\">添加伴学助手免费领取会员权限</div>";
+        QwGroupChatDetailsResult result = qwApiService.groupChatDetails(courseLink.getChatId(), param.getCorpId());
+        if(result.getErrCode() != 0){
+            return R.error("企微接口请求失败,请联系管理员:" +result.getErrMsg());
+        }
+        List<QwGroupChatDetailsResult.Member> collect = result.getGroupChat().getMemberList().stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
+        if(collect.isEmpty()){
+            return addCustomerService(param.getQwUserId(),msg);
+        }
+        Optional<QwGroupChatDetailsResult.Member> optional = collect.stream().filter(e -> e.getName().equals(user.getNickName()) || e.getName().equals(param.getNickName())).findFirst();
+        if(!optional.isPresent()){
+            return addCustomerService(param.getQwUserId(),msg);
+        }
+        QwGroupChatDetailsResult.Member member = optional.get();
+        QwExternalContact qwExternalContact = qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>().eq("user_id", result.getGroupChat().getOwner()).eq("external_user_id", member.getUserId()));
+        if(qwExternalContact==null){
+            return addCustomerService(param.getQwUserId(),msg);
+        }
+        Long qwExternalId = qwExternalContact.getId();
+        log.info("外部联系人数据:{}", qwExternalContact);
+//        addCompanyCompanyFsUser(param);
+        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(),param.getQwUserId());
+        if (log==null ){
+            return addCustomerService(param.getQwUserId(),msg);
+        }
+        //判断外部联系人有没有绑定userId
+        if (qwExternalContact.getFsUserId()!=null){
+            //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
+            if (!qwExternalContact.getFsUserId().equals(param.getUserId())) {
+                return addCustomerService(param.getQwUserId(),msg);
+            }
+            List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());
+            //匹配客户公司id
+            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
+                return addCustomerService(param.getQwUserId(),msg);
+            }
+
+            //看课记录中userId为0绑定userId
+            if (log.getUserId()==null||log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())){
+                log.setUserId(param.getUserId());
+            }
+
+            log.setUpdateTime(new Date());
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+            iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId,param.getUserId());
+        }else {
+            //没绑定fsUser直接绑定fsUser
+            QwExternalContact contact = new QwExternalContact();
+            contact.setId(qwExternalId);
+            contact.setFsUserId(param.getUserId());
+            qwExternalContactMapper.updateQwExternalContact(contact);
+            FsUser fsUser = new FsUser();
+            fsUser.setUserId(user.getUserId());
+            fsUser.setIsAddQw(1);
+            fsUserMapper.updateFsUser(fsUser);
+            //绑定上之后 更新观看记录
+            //看课记录中userId为0绑定userId
+            log.setUserId(param.getUserId());
+            log.setUpdateTime(new Date());
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
         }
-        return R.ok().put("qwExternalId",matchedContact.getId());
+
+        return R.ok().put("qwExternalId", qwExternalContact.getId());
     }
 
     private void createWatchLog(FsUserCourseVideoAddKfUParam param) {
@@ -867,6 +934,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         packetParam.setOpenId(user.getMpOpenId());
         // 来源是小程序切换openId
         if (param.getSource() == 2) {
+            System.out.println("小程序id"+user.getCourseMaOpenId());
             packetParam.setOpenId(user.getCourseMaOpenId());
         }
         packetParam.setAmount(amount);
@@ -876,17 +944,45 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         System.out.println("红包商户号"+amount);
         System.out.println("红包商户号"+packetParam);
-        // 发送红包
-        R sendRedPacket = paymentService.sendRedPacket(packetParam);
-        if (sendRedPacket.get("code").equals(200)) {
-            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-            TransferBillsResult transferBillsResult;
-            if (sendRedPacket.get("isNew").equals(1)){
-                transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
-                redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-            }else {
-                redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+        //2025.6.19 红包金额为0的时候
+        if (amount.compareTo(BigDecimal.ZERO)>0){
+            // 发送红包
+            R sendRedPacket = paymentService.sendRedPacket(packetParam);
+            if (sendRedPacket.get("code").equals(200)) {
+                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                TransferBillsResult transferBillsResult;
+                if (sendRedPacket.get("isNew").equals(1)){
+                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                }else {
+                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                }
+                // 添加红包记录
+                redPacketLog.setCourseId(param.getCourseId());
+//            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(0);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+                // 更新观看记录的奖励类型
+//            if (param.getLinkType() == null || param.getLinkType() == 0) {
+                log.setRewardType(config.getRewardType());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+//            }
+                return sendRedPacket;
+            } else {
+                return R.error("奖励发送失败,请联系客服");
             }
+        } else {
+            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
             // 添加红包记录
             redPacketLog.setCourseId(param.getCourseId());
 //            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
@@ -897,7 +993,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
             redPacketLog.setCompanyUserId(param.getCompanyUserId());
             redPacketLog.setCreateTime(new Date());
-            redPacketLog.setAmount(amount);
+            redPacketLog.setAmount(BigDecimal.ZERO);
             redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
             redPacketLog.setPeriodId(param.getPeriodId());
             redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
@@ -907,10 +1003,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             log.setRewardType(config.getRewardType());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
 //            }
-            return sendRedPacket;
-        } else {
-            return R.error("奖励发送失败,请联系客服");
+            return R.ok("红包发送成功");
         }
+
     }
 
     /**
@@ -1093,8 +1188,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         // 如果开启了黑名单审核,需要提示
         if(fsUser.getStatus() == 0) {
-//            return ResponseResult.fail(505, "管理开启了会员审核,请等待审核");
-            return ResponseResult.fail(BizResponseEnum.WAIT_APPROVAL,getCompanyUserQRCode(companyUser));
+            return ResponseResult.fail(505, "请微信联系您的群主~");
+//            return ResponseResult.fail(BizResponseEnum.WAIT_APPROVAL,getCompanyUserQRCode(companyUser));
         }
 
         //查询看课记录
@@ -1128,6 +1223,11 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             fsCourseWatchLog.setLogType(1);
             fsCourseWatchLog.setProject(courseProject);
             courseWatchLogMapper.insertFsCourseWatchLog(fsCourseWatchLog);
+
+            String redisKey = "h5wxuser:watch:heartbeat:" + param.getUserId() + ":" + param.getVideoId() + ":" + param.getCompanyUserId();
+            redisCache.setCacheObject(redisKey, LocalDateTime.now().toString());
+            // 设置 Redis 记录的过期时间(例如 5 分钟)
+            redisCache.expire(redisKey, 300, TimeUnit.SECONDS);
         }
 
         // 添加会员销售关系表数据

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

@@ -166,6 +166,7 @@ public interface FsDoctorMapper
             " order by d.sort desc "+
             "</script>"})
     List<FsDoctorListUVO> selectFsDoctorListUVO(@Param("maps") FsDoctorListUParam param);
+
     @Select("select * from fs_doctor where account=#{account}")
     FsDoctor selectFsDoctorByAccount(String account);
     @Select("select * from fs_doctor where user_id=#{userId}")

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

@@ -139,4 +139,6 @@ public interface FsPackageMapper
             "</foreach>"+
             "</script>"})
     int updatePackagesStatus(@Param("packageIds")Long[] packageIds,@Param("status")Long status);
+
+    List<String> selectIcdNameByPackageId(@Param("packageId")Long packageId);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserDoctorMapper.java

@@ -3,6 +3,7 @@ package com.fs.his.mapper;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsUserDoctor;
 import com.fs.his.param.FsUserDoctorListUParam;
 import com.fs.his.vo.FsUserDoctorListUVO;
@@ -85,4 +86,7 @@ public interface FsUserDoctorMapper
     int checkFollow(@Param("doctorId") Long doctorId,@Param("userId")long userId);
     @Delete("delete from fs_user_doctor  where type=1 and doctor_id=#{doctorId} and user_id=#{userId} ")
     int deleteFollow(@Param("doctorId") Long doctorId,@Param("userId")long userId);
+
+    @Select("select doctor_name as doctorName,avatar as avatar from fs_doctor where is_agreement_prescribe_doctor = 1 ORDER BY RAND() LIMIT 1")
+    FsDoctor getAgreement();
 }

+ 12 - 10
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -76,7 +76,9 @@ public interface FsUserMapper
      */
     public int deleteFsUserByUserIds(Long[] userIds);
     @Select({"<script> " +
-                "select f1.*,f2.nick_name tui_name,f2.phone tui_phone FROM fs_user f1 LEFT JOIN fs_user f2 ON f1.tui_user_id =f2.user_id "+
+                "select f1.*,f2.nick_name tui_name,f2.phone tui_phone,cu.nick_name AS companyUserNickName,co.company_name FROM fs_user f1 LEFT JOIN fs_user f2 ON f1.tui_user_id =f2.user_id "+
+            " LEFT JOIN company_user cu ON cu.user_id = f1.company_user_id"+
+            " LEFT JOIN company co on co.company_id = f1.company_id"+
             " where f1.is_del=0 "+
             "  <if test=\"nickName != null  and nickName != ''\"> and f1.nick_name like concat( #{nickName}, '%')</if>\n" +
             "            <if test=\"avatar != null  and avatar != ''\"> and f1.avatar = #{avatar}</if>\n" +
@@ -297,7 +299,7 @@ public interface FsUserMapper
     @Select("select * from fs_user where union_id=#{unionId}")
     FsUser selectFsUserByUnionId(String unionId);
 
-    List<UserListCountVO> getUserNumber(@Param("userId") Long userId);
+    List<UserListCountVO> getUserNumber(@Param("userId") Long userId, @Param("companyId") Long companyId);
 
     int getRepeatUserNumber(@Param("userId") Long userId);
 
@@ -307,9 +309,9 @@ public interface FsUserMapper
 
     UserDetailsVO getCountRedPacket(@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
 
-    FsUserSummaryCountVO countUserSummary(@Param("userId") Long userId);
+    FsUserSummaryCountVO countUserSummary(@Param("userId") Long userId, @Param("companyId") Long companyId);
 
-    List<FsUserSummaryCountTagVO> countTag(@Param("userId") Long userId);
+    List<FsUserSummaryCountTagVO> countTag(@Param("userId") Long userId, @Param("companyId") Long companyId);
 
     Map<String, Long> countUserCourse(UserStatisticsCommonParam param);
 
@@ -317,21 +319,21 @@ public interface FsUserMapper
 
     Map<String, Long> countCourseDetails(UserStatisticsCommonParam param);
 
-    List<FsUserRankingVO> countUserRankingByComplete(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId") String periodId, @Param("videoId") String videoId, @Param("order") String order);
+    List<FsUserRankingVO> countUserRankingByComplete(@Param("userId") Long userId, @Param("companyId") Long companyId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId")String periodId, @Param("videoId")String videoId, @Param("order")String order);
 
-    List<FsUserRankingVO> countUserRankingByRight(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId") String periodId, @Param("videoId") String videoId, @Param("order") String order);
+    List<FsUserRankingVO> countUserRankingByRight(@Param("userId") Long userId, @Param("companyId") Long companyId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId")String periodId, @Param("videoId")String videoId, @Param("order")String order);
 
-    List<FsCourseRankingVO> countCourseRankingByComplete(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId") String periodId, @Param("videoId") String videoId, @Param("order") String order);
+    List<FsCourseRankingVO> countCourseRankingByComplete(@Param("userId") Long userId, @Param("companyId") Long companyId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId")String periodId, @Param("videoId")String videoId, @Param("order")String order);
 
-    List<FsCourseRankingVO> countCourseRankingByRight(@Param("userId") Long userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId") String periodId, @Param("videoId") String videoId, @Param("order") String order);
+    List<FsCourseRankingVO> countCourseRankingByRight(@Param("userId") Long userId, @Param("companyId") Long companyId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("periodId")String periodId, @Param("videoId")String videoId, @Param("order")String order);
 
     Map<String, Object> countUserRedPacket(UserStatisticsCommonParam param);
 
     List<FsCourseAnalysisCountVO> courseAnalysisCourseCount(CourseAnalysisParam param);
 
-    CompanyUserSummaryCountVO companyUserCount(@Param("companyUserId") String companyUserId);
+    CompanyUserSummaryCountVO companyUserCount(@Param("companyUserId")String companyUserId, @Param("companyId") Long companyId);
 
-    CompanyUserSummaryCountVO newUserRedPacketCount(@Param("companyUserId") String companyUserId);
+    CompanyUserSummaryCountVO newUserRedPacketCount(@Param("companyUserId")String companyUserId, @Param("companyId") Long companyId);
 
 
     @Select("select * from fs_user where course_ma_open_id=#{openId}")

+ 7 - 0
fs-service/src/main/java/com/fs/his/service/IFsPackageService.java

@@ -79,4 +79,11 @@ public interface IFsPackageService
     int updatePackagesStatus(Long[] packageIds, Long status);
 
     String convertImageToJpg(String imgUrl, Long packageId);
+
+    /**
+     * 根据套餐id查询icd名字
+     * @param packageId
+     * @return icdName
+     */
+    List<String> selectIcdNameByPackageId(Long packageId);
 }

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

@@ -3,6 +3,7 @@ package com.fs.his.service;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsUserDoctor;
 import com.fs.his.param.FsUserDoctorListUParam;
 import com.fs.his.vo.FsUserDoctorListUVO;
@@ -69,4 +70,6 @@ public interface IFsUserDoctorService
     R checkFollow(Long doctorId, long userId);
 
     int deleteFollow(Long doctorId, long userId);
+
+    FsDoctor getAgreement();
 }

+ 6 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java

@@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URL;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -293,5 +294,10 @@ public class FsPackageServiceImpl implements IFsPackageService {
         }
         return null;
     }
+
+    @Override
+    public List<String> selectIcdNameByPackageId(Long packageId) {
+        return fsPackageMapper.selectIcdNameByPackageId(packageId);
+    }
 }
 

+ 6 - 1
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -533,7 +533,12 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         TransferService transferService = wxPayService.getTransferService();
 
         TransferBatchesRequest request = new TransferBatchesRequest();
-        request.setAppid(config.getAppId());
+        if (param.getSource() == 2){
+            request.setAppid(config.getMiniappId());
+        }else {
+            request.setAppid(config.getAppId());
+        }
+
         String code = IdUtil.getSnowflake(0, 0).nextIdStr();
         request.setOutBatchNo("fsCourse" + code);
         request.setBatchRemark("课堂答题奖励");

+ 6 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserDoctorServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.his.service.impl;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsDoctor;
 import com.fs.his.param.FsUserDoctorListUParam;
 import com.fs.his.vo.FsUserDoctorListUVO;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -109,4 +110,9 @@ public class FsUserDoctorServiceImpl implements IFsUserDoctorService
     public int deleteFollow(Long doctorId, long userId) {
         return fsUserDoctorMapper.deleteFollow(doctorId,userId);
     }
+
+    @Override
+    public FsDoctor getAgreement() {
+        return fsUserDoctorMapper.getAgreement();
+    }
 }

+ 102 - 23
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -22,14 +22,8 @@ import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.cache.ICompanyTagCacheService;
 import com.fs.company.cache.ICompanyUserCacheService;
-import com.fs.company.domain.Company;
-import com.fs.company.domain.CompanyTag;
-import com.fs.company.domain.CompanyTagUser;
-import com.fs.company.domain.CompanyUser;
-import com.fs.company.mapper.CompanyMapper;
-import com.fs.company.mapper.CompanyTagMapper;
-import com.fs.company.mapper.CompanyTagUserMapper;
-import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.domain.*;
+import com.fs.company.mapper.*;
 import com.fs.company.service.ICompanyTagService;
 import com.fs.course.domain.FsUserCompanyUser;
 import com.fs.course.mapper.FsUserCompanyUserMapper;
@@ -127,6 +121,9 @@ public class FsUserServiceImpl implements IFsUserService
     @Autowired
     private IQwExternalContactCacheService qwExternalContactCacheService;
 
+    @Autowired
+    private CompanyRoleMapper companyRoleMapper;
+
 
     /**
      * 查询用户
@@ -512,8 +509,35 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public PageInfo<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param) {
-        List<FsUserPageListVO> fsUserPageListVOS = fsUserMapper.selectFsUserPageList(param);
 
+
+//        List<CompanyRole> companyRoles = companyRoleMapper.selectRolePermissionByUserId(param.getUserId());
+//        List<CompanyUser> companyUsers;
+//        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+//        // 判断是否管理员 或者包含 1:全部数据权限
+//        if (companyUser.isAdmin() || companyRoles.stream().anyMatch(r -> "1".equals(r.getDataScope()))) {
+//            companyUsers = companyUserService.getCompanyUserListByCompanyIdAndDeptId(companyUser.getCompanyId(), null);
+//        }
+//        // 判断是否包含 3:本部门数据权限 4:本部门及以下数据权限
+//        else if (companyRoles.stream().anyMatch(r -> "3".equals(r.getDataScope()) || "4".equals(r.getDataScope()))) {
+//            companyUsers = companyUserService.getCompanyUserListByCompanyIdAndDeptId(companyUser.getCompanyId(), companyUser.getDeptId());
+//        }
+//        // 默认空 -- 判断是否包含 5:仅可查看本人
+//        else {
+//            companyUsers = new ArrayList<>();
+//        }
+
+        if (param.getUserId() != null) {
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+                if (companyUser.isAdmin()){
+                    param.setUserId(0L);
+                    param.setCompanyId(companyUser.getCompanyId());
+                }
+        }
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+
+        List<FsUserPageListVO> fsUserPageListVOS = fsUserMapper.selectFsUserPageList(param);
         // 获取当前销售所有重粉会员
         List<FsUserCompanyUser> fsUserCompanyUsers = fsUserCompanyUserMapper.selectRepeatUser(param.getUserId());
 
@@ -537,6 +561,8 @@ public class FsUserServiceImpl implements IFsUserService
         } else {
             return new PageInfo<>(fsUserPageListVOS);
         }
+
+
     }
 
     private static StringBuilder removeRepeatFansTag(FsUserPageListVO fsUserPageListVO) {
@@ -676,7 +702,14 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public UserListPageVO getUserNumber(Long userId) {
-        List<UserListCountVO> list = fsUserMapper.getUserNumber(userId);
+        // 判断是否是管理员
+        Long companyId = null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        if (companyUser != null && companyUser.isAdmin()){
+            userId = 0L;
+            companyId = companyUser.getCompanyId();
+        }
+        List<UserListCountVO> list = fsUserMapper.getUserNumber(userId, companyId);
         Map<String, Integer> map = list.stream()
                 .collect(Collectors.toMap(UserListCountVO::getStatus, UserListCountVO::getNum, (v1, v2) -> v1));
         UserListPageVO pageVO = new UserListPageVO();
@@ -703,6 +736,8 @@ public class FsUserServiceImpl implements IFsUserService
         if (countRedPacket != null) {
             vo.setAnswerRedPacketTime(countRedPacket.getAnswerRedPacketTime());
             vo.setAnswerRedPacketAmount(countRedPacket.getAnswerRedPacketAmount());
+        } else {
+            vo.setAnswerRedPacketAmount(BigDecimal.ZERO);
         }
         if (countAnswer != null) {
             vo.setAnswerTime(countAnswer.getAnswerTime());
@@ -718,8 +753,15 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public FsUserSummaryCountVO userSummaryCount(Long userId) {
-        FsUserSummaryCountVO fsUserSummaryCountVO = fsUserMapper.countUserSummary(userId);
-        List<FsUserSummaryCountTagVO> countTagList = fsUserMapper.countTag(userId);
+        // 判断是否是管理员
+        Long companyId = null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        if (companyUser != null && companyUser.isAdmin()){
+            userId = 0L;
+            companyId = companyUser.getCompanyId();
+        }
+        FsUserSummaryCountVO fsUserSummaryCountVO = fsUserMapper.countUserSummary(userId, companyId);
+        List<FsUserSummaryCountTagVO> countTagList = fsUserMapper.countTag(userId, companyId);
         fsUserSummaryCountVO.setTagList(countTagList);
         return fsUserSummaryCountVO;
     }
@@ -733,6 +775,11 @@ public class FsUserServiceImpl implements IFsUserService
     public FsUserStatisticsVO userStatisticsDetails(UserStatisticsCommonParam param) {
         FsUserStatisticsVO userStatisticsVO = getUserStatistics(param);
 
+        // 判断是否是管理员
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+        if (companyUser != null && companyUser.isAdmin()){
+            param.setUserId(0L);
+        }
         //统计课程数据详情,在查询统计详情的时候需要显示
         Map<String, Long> courseDetailsMap = fsUserMapper.countCourseDetails(param);
         if(courseDetailsMap != null && courseDetailsMap.get("courseNum") != null && courseDetailsMap.get("videoNum") != null && courseDetailsMap.get("courseUserNum") != null){
@@ -746,25 +793,39 @@ public class FsUserServiceImpl implements IFsUserService
     @Override
     public List<FsUserRankingVO> userRanking(Long userId, String startTime, String endTime, String periodId, String videoId, String order, Integer type) {
         List<FsUserRankingVO> listVO = Collections.emptyList();
+        // 判断是否是管理员
+        Long companyId = null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        if (companyUser != null && companyUser.isAdmin()){
+            userId = 0L;
+            companyId = companyUser.getCompanyId();
+        }
         if(type == 1){
             //按完播率
-            listVO = fsUserMapper.countUserRankingByComplete(userId, startTime, endTime, periodId, videoId, order);
+            listVO = fsUserMapper.countUserRankingByComplete(userId, companyId, startTime, endTime, periodId, videoId, order);
         }
         if(type == 2){
             //按正确率
-            listVO = fsUserMapper.countUserRankingByRight(userId, startTime, endTime, periodId, videoId, order);
+            listVO = fsUserMapper.countUserRankingByRight(userId, companyId, startTime, endTime, periodId, videoId, order);
         }
         return listVO;
     }
 
     @Override
     public List<FsCourseRankingVO> courseRanking(Long userId, String startTime, String endTime, String periodId, String videoId, String order, Integer type) {
+        // 判断是否是管理员
+        Long companyId = null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        if (companyUser != null && companyUser.isAdmin()){
+            userId = 0L;
+            companyId = companyUser.getCompanyId();
+        }
         List<FsCourseRankingVO> list = Collections.emptyList();
         if(type == 1){
-            list = fsUserMapper.countCourseRankingByComplete(userId, startTime, endTime, periodId, videoId, order);
+            list = fsUserMapper.countCourseRankingByComplete(userId, companyId, startTime, endTime, periodId, videoId, order);
         }
         if(type == 2){
-            list = fsUserMapper.countCourseRankingByRight(userId, startTime, endTime, periodId, videoId, order);
+            list = fsUserMapper.countCourseRankingByRight(userId, companyId, startTime, endTime, periodId, videoId, order);
         }
 
         return list;
@@ -836,8 +897,15 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public CompanyUserSummaryCountVO companyUserSummaryCount(Long userId, String companyUserId) {
-        CompanyUserSummaryCountVO companyUserCount = fsUserMapper.companyUserCount(companyUserId);
-        CompanyUserSummaryCountVO newUserRedPacketCount = fsUserMapper.newUserRedPacketCount(companyUserId);
+        // 判断是否是管理员
+        Long companyId = null;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        if (companyUser != null && companyUser.isAdmin()){
+            companyUserId = String.valueOf(0);
+            companyId = companyUser.getCompanyId();
+        }
+        CompanyUserSummaryCountVO companyUserCount = fsUserMapper.companyUserCount(companyUserId, companyId);
+        CompanyUserSummaryCountVO newUserRedPacketCount = fsUserMapper.newUserRedPacketCount(companyUserId, companyId);
         CompanyUserSummaryCountVO vo = new CompanyUserSummaryCountVO();
         BeanUtils.copyProperties(companyUserCount, vo);
         vo.setUserRedPacketNum(newUserRedPacketCount.getUserRedPacketNum());
@@ -854,13 +922,18 @@ public class FsUserServiceImpl implements IFsUserService
             return ResponseResult.fail(404,"当前用户信息不存在");
         }
 
+        // 逻辑调整:如果会员已经绑定了销售,直接提示,不添加重粉数据了-2025年6月16日14点53分
+        if (fsUser.getCompanyUserId() != null && !param.getCompanyUserId().equals(fsUser.getCompanyUserId())){
+            return ResponseResult.fail(406,"该用户已成为其他销售会员");
+        }
+
         //判断该销售是否存在
         CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getCompanyUserId());
         if (Objects.isNull(companyUser)){
             return ResponseResult.fail(405,"销售不存在");
         }
 
-        // 判断是否绑定了销售
+        // 添加关系表数据
         FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
         QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
         Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
@@ -901,10 +974,10 @@ public class FsUserServiceImpl implements IFsUserService
             companyTagUserMapper.insertCompanyTagUser(companyTagUser);
         }
 
-        // 如果是重粉,直接打上重粉的标签
-        if(1 == fsUserCompanyUser.getIsRepeatFans()){
-            this.setRepeatFansTag(param);
-        }
+//        // 如果是重粉,直接打上重粉的标签
+//        if(1 == fsUserCompanyUser.getIsRepeatFans()){
+//            this.setRepeatFansTag(param);
+//        }
 
         //如果是设置了需要进入小黑屋,则需要返回提示,否则正常返回
         if(isDefaultBlack == 1){
@@ -917,6 +990,12 @@ public class FsUserServiceImpl implements IFsUserService
     private FsUserStatisticsVO getUserStatistics(UserStatisticsCommonParam param) {
         FsUserStatisticsVO fsUserStatisticsVO = new FsUserStatisticsVO();
 
+        // 判断是否是管理员
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+        if (companyUser != null && companyUser.isAdmin()){
+            param.setUserId(0L);
+            param.setCompanyId(companyUser.getCompanyId());
+        }
         // 获取课程统计
         Map<String, Long> couserMap = fsUserMapper.countUserCourse(param);
         if (couserMap != null) {

+ 10 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserVO.java

@@ -2,6 +2,7 @@ package com.fs.his.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.crypto.Cipher;
@@ -99,6 +100,15 @@ public class FsUserVO implements Serializable {
 
     private String source;//app来源
 
+
+    @ApiModelProperty(value = "销售名称")
+    @Excel(name = "所属销售", sort = 10)
+    private String companyUserNickName;
+
+    @ApiModelProperty(value = "所属公司")
+    @Excel(name = "所属公司", sort = 9)
+    private String companyName;
+
     public void setPhone(String phone)
     {
        this.phone = phone;

+ 3 - 0
fs-service/src/main/java/com/fs/qw/mapper/CustomerTransferApprovalMapper.java

@@ -59,4 +59,7 @@ public interface CustomerTransferApprovalMapper
      * @return 结果
      */
     public int deleteCustomerTransferApprovalByIds(Long[] ids);
+
+
+    List<CustomerTransferApproval> queryPendingData();
 }

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

@@ -59,4 +59,9 @@ public interface ICustomerTransferApprovalService
      * @return 结果
      */
     public int deleteCustomerTransferApprovalById(Long id);
+
+    /**
+     * 自动审批通过
+     */
+    public void autoApprovePass();
 }

+ 13 - 0
fs-service/src/main/java/com/fs/qw/service/impl/CustomerTransferApprovalServiceImpl.java

@@ -18,6 +18,7 @@ import com.fs.qw.vo.TransferCustomDTO;
 import com.fs.store.service.cache.IFsUserCacheService;
 import com.hc.openapi.tool.util.StringUtils;
 import org.apache.http.util.Asserts;
+import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.stereotype.Service;
@@ -263,4 +264,16 @@ public class CustomerTransferApprovalServiceImpl implements ICustomerTransferApp
     {
         return customerTransferApprovalMapper.deleteCustomerTransferApprovalById(id);
     }
+
+    @Override
+    public void autoApprovePass() {
+        List<CustomerTransferApproval> customerTransferApprovals = customerTransferApprovalMapper.queryPendingData();
+        for (CustomerTransferApproval approval : customerTransferApprovals) {
+            approval.setApprovalStatus(1);
+            approval.setApprovalRemark("自动审批");
+            approval.setApproverUserId(1L);
+            ICustomerTransferApprovalService service = (ICustomerTransferApprovalService) AopContext.currentProxy();
+            service.updateCustomerTransferApproval(approval);
+        }
+    }
 }

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

@@ -169,6 +169,9 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
     @Autowired
     private SopUserLogsInfoMapper sopUserLogsInfoMapper;
 
+    @Autowired
+    private IQwCompanyService iQwCompanyService;
+
     @Autowired
     private ISopUserLogsService sopUserLogsService;
 
@@ -3114,9 +3117,7 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
             return attachments;
         }
 
-        String json = configService.selectConfigByKey("course.config");
-        CourseConfig config = JSON.parseObject(json, CourseConfig.class);
-
+        QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(corpId);
 
         attachments.forEach(att -> {
             // 生成短链的条件判断
@@ -3161,51 +3162,54 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
 
                 try {
 
-                    String mediaId = (String) redisCache.getCacheObject("miniprogram:" + corpId + ":" + att.getMiniprogram().getCourseId());
-                    if (!StringUtil.strIsNullOrEmpty(mediaId)) {
-
-                        FsCourseLinkCreateParam param = new FsCourseLinkCreateParam();
-                        param.setVideoId(Long.valueOf(att.getMiniprogram().getVideoId()));
-                        param.setQwUserId(qwUser.getId());
-                        param.setDays(att.getMiniprogram().getExpiresDays());
-                        param.setCorpId(corpId);
-                        param.setCourseId(Long.valueOf(att.getMiniprogram().getCourseId()));
-                        param.setCompanyUserId(qwUser.getCompanyUserId());
-                        param.setCompanyId(qwUser.getCompanyId());
-                        param.setQwExternalId(qwExternalId);
-                        param.setSendTime(new Date());
+                    //小程序
+                    if("miniprogram".equals(att.getMsgtype())
+                            && !StringUtil.strIsNullOrEmpty(att.getMiniprogram().getCourseId())
+                            && !StringUtil.strIsNullOrEmpty(att.getMiniprogram().getVideoId())){
 
-                        String linkUrl = iFsCourseLinkService.createLinkUrlWcmini(param);
+                        try {
 
-                        if (!StringUtil.strIsNullOrEmpty(linkUrl)) {
-                            att.getMiniprogram().setPage(linkUrl);
-                        }
+                            String mediaId =(String)redisCache.getCacheObject("miniprogram:"+corpId+":"+att.getMiniprogram().getCourseId());
+                            if (!StringUtil.strIsNullOrEmpty(mediaId)){
 
-                        if (config!=null){
+                                FsCourseLinkCreateParam param = new FsCourseLinkCreateParam();
+                                param.setVideoId(Long.valueOf(att.getMiniprogram().getVideoId()));
+                                param.setQwUserId(qwUser.getId());
+                                param.setDays(att.getMiniprogram().getExpiresDays());
+                                param.setCorpId(corpId);
+                                param.setCourseId(Long.valueOf(att.getMiniprogram().getCourseId()));
+                                param.setCompanyUserId(qwUser.getCompanyUserId());
+                                param.setCompanyId(qwUser.getCompanyId());
+                                param.setQwExternalId(qwExternalId);
+                                param.setSendTime(new Date());
 
-                            if (StringUtil.strIsNullOrEmpty(config.getMiniprogramAppid())) {
-                                logger.error("配置中无小程序id,采用默认的");
-                                att.getMiniprogram().setAppid("wxc84c6f789ba7f176");
-                            } else {
+                                String linkUrl = iFsCourseLinkService.createLinkUrlWcmini(param);
 
-                                att.getMiniprogram().setAppid(config.getMiniprogramAppid());
+                                if (!StringUtil.strIsNullOrEmpty(linkUrl)) {
+                                    att.getMiniprogram().setPage(linkUrl);
+                                }
+                                if (!StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())){
+                                    att.getMiniprogram().setAppid(qwCompany.getMiniAppId());
+                                }else {
+                                    logger.error("欢迎语:企业未配置小程序id:采用默认appid"+corpId);
+                                }
+                                att.getMiniprogram().setPic_media_id(mediaId);
+
+                                //生成观看记录
+                                addWatchLogIfNeeded(Integer.valueOf(att.getMiniprogram().getVideoId()),
+                                        Integer.valueOf(att.getMiniprogram().getCourseId()),
+                                        String.valueOf(qwUser.getId()),
+                                        String.valueOf(qwUser.getCompanyUserId()),
+                                        String.valueOf(qwUser.getCompanyId()),
+                                        String.valueOf(qwExternalId));
+                            }else {
+                                logger.error("查到课程相关信息:"+corpId+":"+att.getMiniprogram().getCourseId());
                             }
 
+                        }  catch (Exception e) {
+                            logger.error("欢迎语生成小程序消息失败:"+e.getMessage()+"|"+corpId+"|"+att.getMiniprogram().getCourseId());
                         }
-
-                        att.getMiniprogram().setPic_media_id(mediaId);
-
-                        //生成观看记录
-                        addWatchLogIfNeeded(Integer.valueOf(att.getMiniprogram().getVideoId()),
-                                Integer.valueOf(att.getMiniprogram().getCourseId()),
-                                String.valueOf(qwUser.getId()),
-                                String.valueOf(qwUser.getCompanyUserId()),
-                                String.valueOf(qwUser.getCompanyId()),
-                                String.valueOf(qwExternalId));
-                    }else {
-                        logger.error("查到课程相关信息:"+corpId+":"+att.getMiniprogram().getCourseId());
                     }
-
                 }  catch (Exception e) {
                     logger.error("欢迎语生成小程序消息失败:"+e.getMessage()+"|"+corpId+"|"+att.getMiniprogram().getCourseId());
                 }

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

@@ -116,4 +116,8 @@ public class QwSop implements Serializable
     @Excel(name = "开启评论或者弹幕,1-开启评论;2-开启弹幕;3-都关闭")
     private Integer openCommentStatus;
 
+    /**
+     * 是否按照营期 发送官方群发 1是 2否(否的时候按单链发)
+     */
+    private Integer isSampSend;
 }

+ 4 - 1
fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java

@@ -197,7 +197,10 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
 
 
     @DataSource(DataSourceType.SOP)
-    public List<QwSopLogs> selectSopLogsByCreateCorpMassSending(@Param("date") String date);
+    public List<QwSopLogs> createCorpMassSending(@Param("date") String date);
+
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopLogs> selectSopLogsByCreateCorpMassSending(@Param("taskStartTime") String taskStartTime,@Param("taskEndTime") String taskEndTime);
 
     /**
     * 为了避免一直轮询无效(发送不了,给不了反馈的)数据,只查与定时的时间过后,之前3天内的的数据反馈

+ 8 - 0
fs-service/src/main/java/com/fs/sop/params/QwSopLogsParam.java

@@ -3,6 +3,8 @@ package com.fs.sop.params;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class QwSopLogsParam {
     /**
@@ -16,6 +18,7 @@ public class QwSopLogsParam {
     private String qwUserName;
 
     private String qwUserid;
+    private List<String> qwUseridByList;
 
     /**
      * 客户昵称
@@ -51,4 +54,9 @@ public class QwSopLogsParam {
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private String sendTime;
+
+    /**
+     * 手机号
+     */
+    private String phone;
 }

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

@@ -95,7 +95,7 @@ public interface IQwSopLogsService
     /**
      *  创建企业群发(按照营期发)
      */
-    public void createCorpMassSendingByUserLogs(String date);
+    public void createCorpMassSendingByUserLogs(String taskStartTime,String taskEndTime);
 
     /**
      *  检索执行符合条件的定时任务的结果回调(企业微信)

+ 209 - 137
fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -1,13 +1,17 @@
 package com.fs.sop.service.impl;
 
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.service.IFsUserCompanyUserService;
 import com.fs.fastGpt.service.IFastGptChatSessionService;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
@@ -37,6 +41,7 @@ import com.fs.sop.vo.QwSopLogsDoSendListTVO;
 import com.fs.sop.vo.QwSopLogsListCVO;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
+import org.apache.commons.collections.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -49,6 +54,7 @@ import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.function.Function;
+import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
 /**
@@ -99,6 +105,9 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
     @Autowired
     private IFastGptChatSessionService fastGptChatSessionService;
 
+    @Autowired
+    private ICompanyUserService companyUserService;
+
 
     /**
      * 查询企业微信SOP  定时任务
@@ -129,16 +138,42 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
      */
     @Override
     public List<QwSopLogsListCVO> selectQwSopLogsListBySopId(QwSopLogsParam param) {
+        if (param == null || param.getType() == null) {
+            return Collections.emptyList();
+        }
 
-        switch (param.getType()){
-            //个微
+        switch (param.getType()) {
+            // 个微
             case 1:
                 return qwSopLogsMapper.selectQwSopLogsListByGwSopId(param);
-            //企微
+            // 企微
             case 2:
-                return qwSopLogsMapper.selectQwSopLogsListByQwSopId(param);
+                return handleQwSopLogs(param);
+            default:
+                return Collections.emptyList();
         }
-       return null;
+    }
+
+    private List<QwSopLogsListCVO> handleQwSopLogs(QwSopLogsParam param) {
+        if (StringUtils.isNotEmpty(param.getPhone())) {
+            try {
+                CompanyUser companyUser = companyUserService.selectCompanyUserByPhone(param.getPhone());
+                if (companyUser == null) {
+                    return Collections.emptyList();
+                }
+                QwUser qwUser = new QwUser();
+                qwUser.setCompanyUserId(companyUser.getUserId());
+                List<QwUser> qwUsers = qwUserMapper.selectQwUserList(qwUser);
+                if (CollectionUtils.isNotEmpty(qwUsers)){
+                    List<String> qwUserIds = qwUsers.stream().map(QwUser::getQwUserId).collect(Collectors.toList());
+                    param.setQwUseridByList(qwUserIds);
+                }
+            } catch (Exception e) {
+                logger.error("根据手机号查询企业用户出现异常,手机号: {}, 异常信息: {}", param.getPhone(), e.getMessage(), e);
+                return Collections.emptyList();
+            }
+        }
+        return qwSopLogsMapper.selectQwSopLogsListByQwSopId(param);
     }
 
 
@@ -710,12 +745,44 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
             List<QwSopLogsDoSendListTVO> logsByJsApi = qwSopLogsMapper.getQwSopLogsByJsApiAll(param);
 
 
+//            // 优先返回 sendType == 8 的第一条记录
+//            List<QwSopLogsDoSendListTVO> result = logsByJsApi.stream()
+//                    .filter(log -> log.getSendType() == 8)
+//                    .findFirst()
+//                    .map(Collections::singletonList) // 单元素不可变 List
+//                    .orElse(logsByJsApi); // 如果没有匹配项,返回原列表
             // 优先返回 sendType == 8 的第一条记录
             List<QwSopLogsDoSendListTVO> result = logsByJsApi.stream()
-                    .filter(log -> log.getSendType() == 8)
-                    .findFirst()
-                    .map(Collections::singletonList) // 单元素不可变 List
-                    .orElse(logsByJsApi); // 如果没有匹配项,返回原列表
+                    .collect(Collector.of(
+                            () -> new Object() {
+                                QwSopLogsDoSendListTVO type8 = null;
+                                List<QwSopLogsDoSendListTVO> type9List = new ArrayList<>();
+                                List<QwSopLogsDoSendListTVO> otherList = new ArrayList<>();
+                            },
+                            (acc, log) -> {
+                                if (acc.type8 == null && log.getSendType() == 8) {
+                                    acc.type8 = log;
+                                } else if (acc.type8 == null) {
+                                    if (log.getSendType() == 9) {
+                                        acc.type9List.add(log);
+                                    } else {
+                                        acc.otherList.add(log);
+                                    }
+                                }
+                            },
+                            (acc1, acc2) -> { throw new UnsupportedOperationException(); },
+                            acc -> {
+                                if (acc.type8 != null) {
+                                    return Collections.singletonList(acc.type8);
+                                } else if (!acc.type9List.isEmpty()) {
+                                    List<QwSopLogsDoSendListTVO> combined = new ArrayList<>(acc.type9List.size() + acc.otherList.size());
+                                    combined.addAll(acc.type9List);
+                                    combined.addAll(acc.otherList);
+                                    return combined;
+                                }
+                                return logsByJsApi;
+                            }
+                    ));
 
             // 查询员工信息的id
             QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(),param.getQwUserId().trim());
@@ -1292,173 +1359,178 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
      * 该方法负责处理企业微信的群发消息创建和发送
      */
     @Override
-    public void createCorpMassSending(String date) {
+    public void createCorpMassSending(String date)  {
+
         long startTime = System.currentTimeMillis();
         logger.info("开始执行企业微信群发消息创建任务");
 
-        String json = configService.selectConfigByKey("course.config");
-        CourseConfig config = JSON.parseObject(json, CourseConfig.class);
-
-        if (config == null) {
-            logger.error("课程默认配置为空,不执行");
-            return;
-        }
-        // 获取需要发送的SOP日志记录
-        List<QwSopLogs> qwSopLogs = qwSopLogsMapper.selectSopLogsByCreateCorpMassSending(date);
-//        List<QwSopLogs> qwSopLogs = qwSopLogsMapper.checkQwSopLogs();
+        List<QwSopLogs> qwSopLogs = qwSopLogsMapper.createCorpMassSending(date);
         if (qwSopLogs.isEmpty()) {
             logger.error("zyp \n【企微官方群发记录为空】");
             return;
         }
 
-        // 按照企业员工ID、发送时间、SOP ID和企业ID进行分组
         Map<String, List<QwSopLogs>> groupedLogs = new HashMap<>();
         for (QwSopLogs log : qwSopLogs) {
             String key = log.getQwUserid() + "|" + log.getSendTime() + "|" + log.getSopId() + "|" + log.getCorpId();
             groupedLogs.computeIfAbsent(key, k -> new ArrayList<>()).add(log);
         }
 
-        // 创建线程池,使用固定大小的线程池以避免过多线程导致的资源竞争
         int threadCount = Math.min(10, Runtime.getRuntime().availableProcessors() + 1);
         ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
-
-        // 创建用于发送消息的嵌套线程池
         ExecutorService messageExecutorService = Executors.newFixedThreadPool(20);
-
-        // 用于存储需要批量更新的日志记录,使用线程安全的集合
         List<QwSopLogs> updateList = Collections.synchronizedList(new ArrayList<>());
 
-        // 使用CountDownLatch等待所有任务完成
         CountDownLatch latch = new CountDownLatch(groupedLogs.size());
 
-        // 处理每个分组
-        for (Map.Entry<String, List<QwSopLogs>> entry : groupedLogs.entrySet()) {
-            String key = entry.getKey();
-            List<QwSopLogs> logs = entry.getValue();
-            String[] keys = key.split("\\|");
-            String qwUserid = keys[0];
-            String corpId = keys[3];
-
-//            QwUser qwUser = qwUserMapper.selectQwUserByCorpIdAndUserId(corpId, qwUserid);
-            // 查询员工信息的id
-            QwUser qwUser = qwExternalContactService.getQwUserByRedis(corpId.trim(),qwUserid.trim());
-            if (qwUser != null && qwUser.getIsDel() == 0) {
-                // 提交到线程池处理每个分组
-                executorService.submit(() -> {
-                    try {
-                        // 按外部用户ID分组
-                        Map<String, List<QwSopLogs>> userLogsMap = new HashMap<>();
-                        for (QwSopLogs log : logs) {
-                            String externalUserId = log.getExternalUserId();
-                            userLogsMap.computeIfAbsent(externalUserId, k -> new ArrayList<>()).add(log);
-                        }
-
-                        // 使用嵌套的CountDownLatch等待所有消息发送完成
-                        CountDownLatch messageLatch = new CountDownLatch(userLogsMap.size());
-
-                        // 处理每个外部用户
-                        for (Map.Entry<String, List<QwSopLogs>> userEntry : userLogsMap.entrySet()) {
-                            String externalUserId = userEntry.getKey();
-                            List<QwSopLogs> userLogs = userEntry.getValue();
+        try {
+            for (Map.Entry<String, List<QwSopLogs>> entry : groupedLogs.entrySet()) {
+                String key = entry.getKey();
+                List<QwSopLogs> logs = entry.getValue();
+                String[] keys = key.split("\\|");
+                String qwUserid = keys[0];
+                String corpId = keys[3];
+
+                QwUser qwUser = qwExternalContactService.getQwUserByRedis(corpId.trim(), qwUserid.trim());
+                if (qwUser != null && qwUser.getIsDel() == 0) {
+                    executorService.submit(() -> {
+                        try {
+                            Map<String, List<QwSopLogs>> userLogsMap = new HashMap<>();
+                            for (QwSopLogs log : logs) {
+                                userLogsMap.computeIfAbsent(log.getExternalUserId(), k -> new ArrayList<>()).add(log);
+                            }
 
-                            // 提交到消息发送线程池
-                            messageExecutorService.submit(() -> {
-                                try {
-                                    QwMsgTemplateSop templateSop = new QwMsgTemplateSop();
-                                    templateSop.setChatType("single");
-                                    templateSop.setAllowSelect(false);
-                                    templateSop.setSender(qwUserid);
-                                    templateSop.setExternalUseridList(Collections.singletonList(externalUserId));
-
-                                    List<QwMsgTemplateSop.Attachment> attachments = new ArrayList<>();
-                                    boolean hasError = false;
-
-                                    for (QwSopLogs log : userLogs) {
-                                        try {
-                                            QwSopTempSetting.Content content = JSON.parseObject(log.getContentJson(), QwSopTempSetting.Content.class);
-                                            if (content == null || content.getSetting() == null) continue;
-                                            Long courseId = content.getCourseId();
-                                            for (QwSopTempSetting.Content.Setting set : content.getSetting()) {
-                                                processContent(set, corpId, templateSop, attachments, courseId);
+                            CountDownLatch messageLatch = new CountDownLatch(userLogsMap.size());
+                            for (Map.Entry<String, List<QwSopLogs>> userEntry : userLogsMap.entrySet()) {
+                                String externalUserId = userEntry.getKey();
+                                List<QwSopLogs> userLogs = userEntry.getValue();
+
+                                messageExecutorService.submit(() -> {
+                                    try {
+                                        QwMsgTemplateSop templateSop = new QwMsgTemplateSop();
+                                        templateSop.setChatType("single");
+                                        templateSop.setAllowSelect(false);
+                                        templateSop.setSender(qwUserid);
+                                        templateSop.setExternalUseridList(Collections.singletonList(externalUserId));
+
+                                        List<QwMsgTemplateSop.Attachment> attachments = new ArrayList<>();
+                                        boolean hasError = false;
+                                        for (QwSopLogs log : userLogs) {
+                                            try {
+                                                QwSopTempSetting.Content content = JSON.parseObject(log.getContentJson(), QwSopTempSetting.Content.class);
+                                                if (content == null || content.getSetting() == null) continue;
+                                                Long courseId = content.getCourseId();
+                                                for (QwSopTempSetting.Content.Setting set : content.getSetting()) {
+                                                    processContent(set, corpId, templateSop, attachments, courseId);
+                                                }
+                                            } catch (Exception e) {
+                                                logger.error("消息内容解析失败,logId:{},{},{}", log.getId(), e,key);
+                                                hasError = true;
                                             }
-                                        } catch (Exception e) {
-                                            logger.error("消息内容解析失败,logId:{}", log.getId(), e);
-                                            hasError = true;
                                         }
-                                    }
+                                        if (!hasError && (!attachments.isEmpty() || templateSop.getTextContent() != null)) {
+                                            templateSop.setAttachments(attachments);
+                                            try {
+                                                QwAddMsgTemplateResult result = qwApiService.addMsgTemplateBySop(templateSop, corpId);
+                                                if (result.getErrCode() == 0 || result.getErrCode() == 41063){
+                                                    for (QwSopLogs log : userLogs) {
+                                                        log.setSendStatus(1L);
+                                                        log.setMsgId(result.getMsgId());
+                                                        updateList.add(log);
+                                                    }
+                                                }else {
+
+                                                    for (QwSopLogs log : userLogs) {
+                                                        log.setSendType(2);
+                                                        log.setSendStatus(3L);
+                                                        log.setRemark("官方有误,sop补发");
+                                                        log.setReceivingStatus(0L);
+                                                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+                                                        LocalDateTime currentTime = LocalDateTime.now();
+                                                        String newTimeString = currentTime.format(formatter);
+                                                        log.setSendTime(newTimeString);
+                                                        log.setSort(30000001);
+                                                        updateList.add(log);
+                                                    }
 
-                                    if (!hasError && (!attachments.isEmpty() || templateSop.getTextContent() != null)) {
-                                        templateSop.setAttachments(attachments);
-                                        try {
-                                            QwAddMsgTemplateResult result = qwApiService.addMsgTemplateBySop(templateSop, corpId);
-                                            for (QwSopLogs log : userLogs) {
-                                                log.setSendStatus(result.getErrCode() == 0 || result.getErrCode() == 41063 ? 1L : 0L);
-                                                log.setMsgId(result.getMsgId());
-                                                updateList.add(log);
-                                            }
-                                            if (result.getErrCode() != 0 && result.getErrCode() != 41063){
-                                                logger.error("企业微信接口-消息发送失败,corpId:{},errCode:{},errMsg:{}", corpId, result.getErrCode(), result.getErrMsg());
-                                            }
-                                        } catch (Exception e) {
-                                            logger.error("消息发送失败,user:{}", externalUserId, e);
-                                            for (QwSopLogs log : userLogs) {
-                                                log.setSendStatus(0L);
-                                                updateList.add(log);
+                                                    logger.error("企业微信接口-消息发送失败-进入sop补偿,corpId:{},errCode:{},errMsg:{},key:{}", corpId, result.getErrCode(), result.getErrMsg(),key);
+                                                }
+                                            } catch (Exception e) {
+                                                logger.error("消息发送失败,user:{},{},{}", externalUserId, e,key);
+                                                for (QwSopLogs log : userLogs) {
+                                                    log.setSendStatus(0L);
+                                                    log.setRemark("信息异常");
+                                                    updateList.add(log);
+                                                }
                                             }
                                         }
+
+                                    } finally {
+                                        logger.info("执行结束-messageLatch-countDown:"+updateList.size());
+                                        messageLatch.countDown();
                                     }
-                                } finally {
-                                    messageLatch.countDown();
-                                }
-                            });
-                        }
+                                });
 
-                        // 等待所有消息发送完成
-                        try {
+                            }
+
+                            logger.info("messageExecutorService-updateList总量:"+updateList.size());
+
+                            // 等待所有消息发送完成
                             messageLatch.await();
+
                         } catch (InterruptedException e) {
-                            logger.error("等待消息发送完成时被中断", e);
+                            logger.info("messageExecutorService-Thread.currentThread().interrupt():"+updateList.size());
                             Thread.currentThread().interrupt();
+                        } finally {
+                            logger.info("finally-latch.countDown:"+updateList.size());
+                            latch.countDown();
                         }
-                    } finally {
-                        latch.countDown();
-                    }
-                });
-            }else {
-                logger.error("官方群发 员工信息有误:"+corpId+":"+qwUserid);
+                    });
+                } else {
+                    logger.error("员工信息无效-不存在或被删除,corpId:{}, userId:{}", corpId, qwUserid);
+
+                    latch.countDown(); // 确保每个分组都减少计数
+                }
             }
 
-        }
+            latch.await(); // 等待所有分组提交的任务完成
+            logger.info("关闭线程池并等待任务完成:"+updateList.size());
+            // 关闭线程池并等待任务完成
+            executorService.shutdown();
+            messageExecutorService.shutdown();
+            if (!executorService.awaitTermination(300, TimeUnit.SECONDS)) {
+                logger.error("ExecutorService未完全关闭");
+            }
+            if (!messageExecutorService.awaitTermination(300, TimeUnit.SECONDS)) {
+                logger.error("MessageExecutorService未完全关闭");
+            }
 
-        // 等待所有分组处理完成
-        try {
-            latch.await();
-        } catch (InterruptedException e) {
-            logger.error("等待分组处理完成时被中断", e);
-            Thread.currentThread().interrupt();
-        }
+            // 5. 同步块生成快照(终极防护),创建快照避免并发修改
+            List<QwSopLogs> batchList;
+            synchronized (updateList) { // 加锁确保无并发修改
+                batchList = new ArrayList<>(updateList);
+            }
 
-        // 批量更新发送状态,每500条一批
-        if (!updateList.isEmpty()) {
-            int batchSize = 500;
-            for (int i = 0; i < updateList.size(); i += batchSize) {
-                int endIndex = Math.min(i + batchSize, updateList.size());
-                List<QwSopLogs> batch = updateList.subList(i, endIndex);
-                try {
-                    qwSopLogsMapper.batchUpdateStatus(batch);
-                    logger.info("批量修改 sopLogs 成功,修改数量: " + batch.size());
-                } catch (Exception e) {
-                    logger.error("批量修改 sopLogs 失败", e);
+
+            logger.info("批量修改总数: {}", batchList.size());
+
+            if (!batchList.isEmpty()){
+                int batchSize = 1000;
+                for (int i = 0; i < batchList.size(); i += batchSize) {
+                    int end = Math.min(i + batchSize, batchList.size());
+                    List<QwSopLogs> subList = batchList.subList(i, end);
+                    qwSopLogsMapper.batchUpdateStatus(subList);
                 }
             }
-        }
 
-        // 关闭线程池
-        executorService.shutdown();
-        messageExecutorService.shutdown();
 
-        long endTime = System.currentTimeMillis();
-        logger.info("企业微信群发消息创建任务执行完成,总耗时: {} 毫秒", (endTime - startTime));
+        } catch (InterruptedException e) {
+            logger.error("线程中断异常", e);
+            Thread.currentThread().interrupt();
+        } finally {
+            long endTime = System.currentTimeMillis();
+            logger.info("任务完成,耗时: {} 毫秒", endTime - startTime);
+        }
     }
 
     // 处理不同类型的内容
@@ -1559,12 +1631,12 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
 //    }
 
     @Override
-    public void createCorpMassSendingByUserLogs(String date) {
+    public void createCorpMassSendingByUserLogs(String taskStartTime,String taskEndTime) {
 
         long startTime = System.currentTimeMillis();
         logger.info("开始执行企业微信群发消息创建任务");
 
-        List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectSopLogsByCreateCorpMassSending(date);
+        List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectSopLogsByCreateCorpMassSending(taskStartTime,taskEndTime);
         if (qwSopLogsList.isEmpty()) {
             logger.error("zyp \n【企微官方群发记录为空】");
             return;

+ 5 - 0
fs-service/src/main/java/com/fs/sop/vo/SopUserLogsVo.java

@@ -40,4 +40,9 @@ public class SopUserLogsVo  {
     private Integer isRegister;
     private String chatId;
 
+    /**
+     * 是否按照营期 发送官方群发 1 按照【营期+插件补发】的形式发  2 按照【营期+官方单链】的形式发
+     */
+    private Integer isSampSend;
+
 }

Some files were not shown because too many files changed in this diff