Browse Source

Merge branch 'master' into bjcz_his_scrm

# Conflicts:
#	fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
#	fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
#	fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java
#	fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
#	fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java
#	fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
#	fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
#	fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
#	fs-service/src/main/java/com/fs/his/service/IFsStorePaymentService.java
#	fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
#	fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
#	fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
#	fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
#	fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml
#	fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
#	fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
吴树波 1 day ago
parent
commit
8c95f93512
100 changed files with 5178 additions and 707 deletions
  1. 6 0
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  2. 13 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  3. 6 6
      fs-admin/src/main/java/com/fs/his/controller/FsArticleController.java
  4. 26 4
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java
  5. 103 0
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyDivItemController.java
  6. 103 0
      fs-admin/src/main/java/com/fs/his/controller/FsDfAccountController.java
  7. 6 2
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  8. 49 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  9. 92 13
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  10. 300 14
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  11. 1 4
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  12. 9 0
      fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java
  13. 11 0
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  14. 2 2
      fs-admin/src/main/resources/application.yml
  15. 47 0
      fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java
  16. 214 7
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  17. 28 32
      fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java
  18. 73 0
      fs-company-app/src/main/java/com/fs/core/aspectj/DataSourceAspect.java
  19. 244 0
      fs-company-app/src/main/java/com/fs/core/aspectj/LogAspect.java
  20. 94 0
      fs-company-app/src/main/java/com/fs/core/config/DataSourceConfig.java
  21. 123 123
      fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java
  22. 3 3
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java
  23. 4 5
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java
  24. 56 0
      fs-company-app/src/main/java/com/fs/core/manager/AsyncManager.java
  25. 40 0
      fs-company-app/src/main/java/com/fs/core/manager/ShutdownManager.java
  26. 103 0
      fs-company-app/src/main/java/com/fs/core/manager/factory/AsyncFactory.java
  27. 1 1
      fs-company-app/src/main/resources/application.yml
  28. 7 12
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDeptController.java
  29. 27 19
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  30. 1 1
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java
  31. 1 1
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  32. 65 6
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java
  33. 32 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  34. 4 2
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferLogController.java
  35. 17 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  36. 23 7
      fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  37. 9 1
      fs-qw-api/src/main/java/com/fs/app/service/QwDataCallbackService.java
  38. 11 0
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  39. 293 54
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  40. 43 2
      fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisQwSopController.java
  41. 48 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDivConfig.java
  42. 55 0
      fs-service/src/main/java/com/fs/company/domain/CompanyDivItem.java
  43. 61 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDivConfigMapper.java
  44. 65 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyDivItemMapper.java
  45. 19 0
      fs-service/src/main/java/com/fs/company/param/CompanyAcctInfo.java
  46. 40 0
      fs-service/src/main/java/com/fs/company/param/CompanyDivConfigUpdateParam.java
  47. 4 0
      fs-service/src/main/java/com/fs/company/param/CompanyUserQwParam.java
  48. 66 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDivConfigService.java
  49. 65 0
      fs-service/src/main/java/com/fs/company/service/ICompanyDivItemService.java
  50. 179 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDivConfigServiceImpl.java
  51. 104 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyDivItemServiceImpl.java
  52. 11 9
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  53. 5 5
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  54. 41 0
      fs-service/src/main/java/com/fs/company/vo/CompanyDivConfigVo.java
  55. 76 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCompanyBind.java
  56. 8 1
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  57. 66 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCompanyBindMapper.java
  58. 13 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  59. 10 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java
  60. 18 1
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  61. 8 7
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  62. 84 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCompanyBindService.java
  63. 4 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodDaysService.java
  64. 0 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
  65. 86 23
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  66. 247 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCompanyBindServiceImpl.java
  67. 5 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java
  68. 14 4
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  69. 4 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java
  70. 67 68
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  71. 7 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogListVO.java
  72. 42 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListByCompanyVO.java
  73. 30 0
      fs-service/src/main/java/com/fs/course/vo/UserWatchLogListVo.java
  74. 8 18
      fs-service/src/main/java/com/fs/erp/dto/sdk/df/DfClient.java
  75. 254 70
      fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java
  76. 22 0
      fs-service/src/main/java/com/fs/fastGpt/domain/FastGptPushTokenTotal.java
  77. 1 0
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  78. 2 2
      fs-service/src/main/java/com/fs/fastgptApi/util/EventLogUtils.java
  79. 74 0
      fs-service/src/main/java/com/fs/his/domain/FsDfAccount.java
  80. 61 0
      fs-service/src/main/java/com/fs/his/mapper/FsDfAccountMapper.java
  81. 14 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  82. 61 0
      fs-service/src/main/java/com/fs/his/service/IFsDfAccountService.java
  83. 1 0
      fs-service/src/main/java/com/fs/his/service/IFsStorePaymentService.java
  84. 94 0
      fs-service/src/main/java/com/fs/his/service/impl/FsDfAccountServiceImpl.java
  85. 8 0
      fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderServiceImpl.java
  86. 9 0
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java
  87. 165 53
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java
  88. 45 1
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  89. 3 0
      fs-service/src/main/java/com/fs/his/vo/FsStoreOrderExcelVO.java
  90. 2 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsPrescribeScrm.java
  91. 1 4
      fs-service/src/main/java/com/fs/hisStore/enums/SysConfigEnum.java
  92. 38 3
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  93. 261 103
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  94. 22 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java
  95. 14 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderScrmSetErpPhoneParam.java
  96. 14 1
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
  97. 119 8
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  98. 9 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java
  99. 24 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderErpExportVO.java
  100. 10 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java

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

@@ -1,5 +1,6 @@
 package com.fs.api.controller;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysDept;
 import com.fs.common.core.redis.RedisCache;
@@ -168,6 +169,11 @@ public class IndexStatisticsController {
                 );
             }
         }
+        BigDecimal redPacketCompanyMoney = redisCache.getCacheObject("redpacket_money");
+        if(ObjectUtils.isNull(redPacketCompanyMoney)){
+            redPacketCompanyMoney = BigDecimal.ZERO;
+        }
+        consumptionBalanceDataDTO.setRunTianBalance(redPacketCompanyMoney);
 
         return R.ok().put("data", consumptionBalanceDataDTO);
     }

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

@@ -3,6 +3,7 @@ package com.fs.course.controller.qw;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.CustomException;
@@ -16,6 +17,7 @@ import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.vo.FsCourseOverVO;
 import com.fs.course.vo.FsCourseWatchLogListVO;
+import com.fs.course.vo.FsCourseWatchLogStatisticsListByCompanyVO;
 import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.service.IQwWatchLogService;
@@ -78,6 +80,17 @@ public class QwFsCourseWatchLogController extends BaseController
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
         return getDataTable(list);
     }
+
+    @GetMapping("/statisticsListByCompany")
+    public R statisticsListByCompany(FsCourseWatchLogStatisticsListParam param)
+    {
+        if (param.getSTime()==null||param.getETime()==null){
+            return R.ok().put("rows", new ArrayList<>());
+        }
+        param.setSendType(2); //企微
+        List<FsCourseWatchLogStatisticsListByCompanyVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListByCompanyVO(param);
+        return R.ok().put("rows", list);
+    }
     @GetMapping("/qwWatchLogStatisticsList")
     public TableDataInfo qwWatchLogStatisticsList(QwWatchLogStatisticsListParam param)
     {

+ 6 - 6
fs-admin/src/main/java/com/fs/his/controller/FsArticleController.java

@@ -42,7 +42,7 @@ public class FsArticleController extends BaseController
     /**
      * 查询文章列表
      */
-    @PreAuthorize("@ss.hasPermi('his:article:list')")
+    @PreAuthorize("@ss.hasPermi('his:healthArticle:list')")
     @GetMapping("/list")
     public TableDataInfo list(FsArticle fsArticle)
     {
@@ -54,7 +54,7 @@ public class FsArticleController extends BaseController
     /**
      * 导出文章列表
      */
-    @PreAuthorize("@ss.hasPermi('his:article:export')")
+    @PreAuthorize("@ss.hasPermi('his:healthArticle:export')")
     @Log(title = "文章", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(FsArticle fsArticle)
@@ -67,7 +67,7 @@ public class FsArticleController extends BaseController
     /**
      * 获取文章详细信息
      */
-    @PreAuthorize("@ss.hasPermi('his:article:query')")
+    @PreAuthorize("@ss.hasPermi('his:healthArticle:query')")
     @GetMapping(value = "/{articleId}")
     public AjaxResult getInfo(@PathVariable("articleId") Long articleId)
     {
@@ -77,7 +77,7 @@ public class FsArticleController extends BaseController
     /**
      * 新增文章
      */
-    @PreAuthorize("@ss.hasPermi('his:article:add')")
+    @PreAuthorize("@ss.hasPermi('his:healthArticle:add')")
     @Log(title = "文章", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody FsArticle fsArticle)
@@ -94,7 +94,7 @@ public class FsArticleController extends BaseController
     /**
      * 修改文章
      */
-    @PreAuthorize("@ss.hasPermi('his:article:edit')")
+    @PreAuthorize("@ss.hasPermi('his:healthArticle:edit')")
     @Log(title = "文章", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody FsArticle fsArticle)
@@ -110,7 +110,7 @@ public class FsArticleController extends BaseController
     /**
      * 删除文章
      */
-    @PreAuthorize("@ss.hasPermi('his:article:remove')")
+    @PreAuthorize("@ss.hasPermi('his:healthArticle:remove')")
     @Log(title = "文章", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{articleIds}")
     public AjaxResult remove(@PathVariable Long[] articleIds)

+ 26 - 4
fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java

@@ -15,11 +15,9 @@ import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyDeduct;
 import com.fs.company.domain.CompanyRecharge;
 import com.fs.company.param.CompanyDeductParam;
+import com.fs.company.param.CompanyDivConfigUpdateParam;
 import com.fs.company.param.CompanyRechargeParam;
-import com.fs.company.service.ICompanyDeductService;
-import com.fs.company.service.ICompanyRechargeService;
-import com.fs.company.service.ICompanyService;
-import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.*;
 import com.fs.company.vo.CompanyVO;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.config.CourseConfig;
@@ -70,6 +68,8 @@ public class FsCompanyController extends BaseController
     private ICompanyDeductService deductService;
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private ICompanyDivConfigService companyDivConfigService;
     /**
      * 查询诊所管理列表
      */
@@ -241,4 +241,26 @@ public class FsCompanyController extends BaseController
         deductService.insertCompanyDeduct(deduct);
         return R.ok("提交成功,等待审核");
     }
+
+    /**
+     * 获取公司分账配置详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:companyDivConfig:query')")
+    @GetMapping(value = "/getDivConfig/{companyId}")
+    public AjaxResult getDivConfig(@PathVariable("companyId") Long companyId)
+    {
+        return AjaxResult.success(companyDivConfigService.selectCompanyDivConfigByCompanyId(companyId));
+    }
+
+    /**
+     * 公司分账配置
+     */
+    @PreAuthorize("@ss.hasPermi('his:companyDivConfig:set')")
+    @Log(title = "公司分账配置", businessType = BusinessType.UPDATE)
+    @PostMapping("/setDiv")
+    public R setDiv(@RequestBody CompanyDivConfigUpdateParam param)
+    {
+        return companyDivConfigService.setDiv(param);
+    }
+
 }

+ 103 - 0
fs-admin/src/main/java/com/fs/his/controller/FsCompanyDivItemController.java

@@ -0,0 +1,103 @@
+package com.fs.his.controller;
+
+import java.util.List;
+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.CompanyDivItem;
+import com.fs.company.service.ICompanyDivItemService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 分账明细Controller
+ *
+ * @author fs
+ * @date 2025-10-21
+ */
+@RestController
+@RequestMapping("/his/divItem")
+public class FsCompanyDivItemController extends BaseController
+{
+    @Autowired
+    private ICompanyDivItemService companyDivItemService;
+
+    /**
+     * 查询分账明细列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:divItem:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyDivItem companyDivItem)
+    {
+        startPage();
+        List<CompanyDivItem> list = companyDivItemService.selectCompanyDivItemList(companyDivItem);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出分账明细列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:divItem:export')")
+    @Log(title = "分账明细", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyDivItem companyDivItem)
+    {
+        List<CompanyDivItem> list = companyDivItemService.selectCompanyDivItemList(companyDivItem);
+        ExcelUtil<CompanyDivItem> util = new ExcelUtil<CompanyDivItem>(CompanyDivItem.class);
+        return util.exportExcel(list, "分账明细数据");
+    }
+
+    /**
+     * 获取分账明细详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:divItem:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(companyDivItemService.selectCompanyDivItemById(id));
+    }
+
+//    /**
+//     * 新增分账明细
+//     */
+//    @PreAuthorize("@ss.hasPermi('his:divItem:add')")
+//    @Log(title = "分账明细", businessType = BusinessType.INSERT)
+//    @PostMapping
+//    public AjaxResult add(@RequestBody CompanyDivItem companyDivItem)
+//    {
+//        return toAjax(companyDivItemService.insertCompanyDivItem(companyDivItem));
+//    }
+//
+//    /**
+//     * 修改分账明细
+//     */
+//    @PreAuthorize("@ss.hasPermi('his:divItem:edit')")
+//    @Log(title = "分账明细", businessType = BusinessType.UPDATE)
+//    @PutMapping
+//    public AjaxResult edit(@RequestBody CompanyDivItem companyDivItem)
+//    {
+//        return toAjax(companyDivItemService.updateCompanyDivItem(companyDivItem));
+//    }
+//
+//    /**
+//     * 删除分账明细
+//     */
+//    @PreAuthorize("@ss.hasPermi('his:divItem:remove')")
+//    @Log(title = "分账明细", businessType = BusinessType.DELETE)
+//	@DeleteMapping("/{ids}")
+//    public AjaxResult remove(@PathVariable Long[] ids)
+//    {
+//        return toAjax(companyDivItemService.deleteCompanyDivItemByIds(ids));
+//    }
+}

+ 103 - 0
fs-admin/src/main/java/com/fs/his/controller/FsDfAccountController.java

@@ -0,0 +1,103 @@
+package com.fs.his.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.his.domain.FsDfAccount;
+import com.fs.his.service.IFsDfAccountService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 代服账户Controller
+ *
+ * @author fs
+ * @date 2025-10-13
+ */
+@RestController
+@RequestMapping("/his/dfAccount")
+public class FsDfAccountController extends BaseController
+{
+    @Autowired
+    private IFsDfAccountService fsDfAccountService;
+
+    /**
+     * 查询代服账户列表
+     */
+
+    @GetMapping("/list")
+    public TableDataInfo list(FsDfAccount fsDfAccount)
+    {
+        startPage();
+        List<FsDfAccount> list = fsDfAccountService.selectFsDfAccountList(fsDfAccount);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出代服账户列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:dfAccount:export')")
+    @Log(title = "代服账户", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsDfAccount fsDfAccount)
+    {
+        List<FsDfAccount> list = fsDfAccountService.selectFsDfAccountList(fsDfAccount);
+        ExcelUtil<FsDfAccount> util = new ExcelUtil<FsDfAccount>(FsDfAccount.class);
+        return util.exportExcel(list, "代服账户数据");
+    }
+
+    /**
+     * 获取代服账户详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:dfAccount:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsDfAccountService.selectFsDfAccountById(id));
+    }
+
+    /**
+     * 新增代服账户
+     */
+    @PreAuthorize("@ss.hasPermi('his:dfAccount:add')")
+    @Log(title = "代服账户", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsDfAccount fsDfAccount)
+    {
+        return toAjax(fsDfAccountService.insertFsDfAccount(fsDfAccount));
+    }
+
+    /**
+     * 修改代服账户
+     */
+    @PreAuthorize("@ss.hasPermi('his:dfAccount:edit')")
+    @Log(title = "代服账户", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsDfAccount fsDfAccount)
+    {
+        return toAjax(fsDfAccountService.updateFsDfAccount(fsDfAccount));
+    }
+
+    /**
+     * 删除代服账户
+     */
+    @PreAuthorize("@ss.hasPermi('his:dfAccount:remove')")
+    @Log(title = "代服账户", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsDfAccountService.deleteFsDfAccountByIds(ids));
+    }
+}

+ 6 - 2
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -954,8 +954,12 @@ public class FsStoreOrderController extends BaseController
     @GetMapping("/getErpAccount")
     public R getErpAccount()
     {
-        List<DFConfigVo> erpAccounts = fsStoreOrderService.getErpAccount();
-        List<String> list = erpAccounts.stream().map(DFConfigVo::getLoginAccount).collect(Collectors.toList());
+        List<String> list = new ArrayList<>();
+        if (CloudHostUtils.hasCloudHostName("金牛明医","康年堂")){
+            List<DFConfigVo> erpAccounts = fsStoreOrderService.getErpAccount();
+            list = erpAccounts.stream().map(DFConfigVo::getLoginAccount).collect(Collectors.toList());
+        }
+
         return R.ok().put("data", list);
     }
 }

+ 49 - 0
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -29,6 +29,7 @@ import com.fs.erp.dto.ErpOrderResponse;
 import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
 import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
 import com.fs.fastGpt.domain.FastgptEventLogTotal;
 import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
@@ -36,6 +37,7 @@ import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
 import com.fs.fastGpt.service.IFastgptEventLogTotalService;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
+import com.fs.gtPush.mapper.PushLogMapper;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.FsInquiryOrder;
@@ -56,6 +58,7 @@ import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
 import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.QwCompany;
+import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
 import com.fs.qw.service.*;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.sop.domain.QwSopTempVoice;
@@ -191,8 +194,54 @@ public class Task {
     @Autowired
     private IQwSopTempVoiceService qwSopTempVoiceService;
 
+    @Autowired
+    private QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper;
+
     public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
 
+    /**
+     * sop任务token消耗统计
+     */
+    public void sopPushTokenTotal() {
+        // 判断是否是凌晨 00:00 - 00:59
+        boolean isEarlyMorning = isEarlyMorning();
+
+        // 获取日期字符串(今天或昨天)
+        String dateTime;
+        if (isEarlyMorning) {
+            dateTime = DateUtils.addDateDays(-1); // 昨天
+        } else {
+            dateTime = DateUtils.getDate(); // 今天
+        }
+        log.info("开始执行sop任务token消耗统计");
+        try {
+            List<FastGptPushTokenTotal> fastGptPushTotalList = qwRestrictionPushRecordMapper.selectFastgptPushTokenTotal(dateTime);
+            if (fastGptPushTotalList != null && !fastGptPushTotalList.isEmpty()) {
+                for (FastGptPushTokenTotal fastGptPushTotal : fastGptPushTotalList) {
+                    // 获取统计数据
+                    Integer type = fastGptPushTotal.getType();
+                    Long count = 0L;
+                    if(type == 7){
+                        count = fastGptPushTotal.getCount() * 450;
+                    }else{
+                        count = fastGptPushTotal.getCount() * 150;
+                    }
+                    fastGptPushTotal.setCount(count);
+                    FastGptPushTokenTotal pushTotal = qwRestrictionPushRecordMapper.selectFastGptPushTokenTotalByInfo(fastGptPushTotal);
+                    if(pushTotal == null){
+                        qwRestrictionPushRecordMapper.insertPushTokenTotal(fastGptPushTotal);
+                    }else{
+                        fastGptPushTotal.setId(pushTotal.getId());
+                        qwRestrictionPushRecordMapper.updatePushTokenTotal(fastGptPushTotal);
+                    }
+                }
+            }
+            log.info("结束执行sop任务token消耗统计");
+        } catch (Exception e) {
+            log.error("执行sop任务token消耗统计异常", e);
+        }
+    }
+
     /**
      * 一键生成语音定时任务
      */

+ 92 - 13
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java

@@ -9,20 +9,26 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.service.ICompanyMoneyLogsService;
 import com.fs.course.dto.FsOrderDeliveryNoteDTO;
 import com.fs.erp.service.IErpOrderService;
+import com.fs.his.domain.FsStoreOrderDf;
+import com.fs.his.service.IFsStoreOrderDfService;
 import com.fs.his.service.IFsUserService;
+import com.fs.his.vo.FsStoreOrderListAndStatisticsVo;
 import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
 import com.fs.hisStore.param.FsStoreOrderParam;
 import com.fs.hisStore.service.*;
+import com.fs.hisStore.vo.FsStoreOrderErpExportVO;
 import com.fs.hisStore.vo.FsStoreOrderDeliveryNoteExportVO;
 import com.fs.hisStore.vo.FsStoreOrderExportVO;
 import com.fs.hisStore.vo.FsStoreOrderItemExportVO;
 import com.fs.hisStore.vo.FsStoreOrderVO;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -30,7 +36,11 @@ import org.springframework.web.multipart.MultipartFile;
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 @RestController
 @RequestMapping("/store/store/storeOrder")
@@ -56,6 +66,9 @@ public class FsStoreHealthOrderScrmController extends BaseController {
     @Autowired
     private ICompanyMoneyLogsService moneyLogsService;
 
+    @Autowired
+    private IFsStoreOrderDfService fsStoreOrderDfService;
+
     // 允许的文件扩展名
     private static final String[] ALLOWED_EXCEL_EXTENSIONS = {".xlsx", ".xls"};
 
@@ -66,8 +79,8 @@ public class FsStoreHealthOrderScrmController extends BaseController {
      * 查询健康商城订单列表
      */
     @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:list')")
-    @GetMapping("/healthList")
-    public TableDataInfo healthStoreList(FsStoreOrderParam param) {
+    @PostMapping("/healthList")
+    public TableDataInfo healthStoreList(@RequestBody FsStoreOrderParam param) {
         startPage();
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
@@ -83,16 +96,50 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         }
         param.setIsHealth("1");
         List<FsStoreOrderVO> list = fsStoreOrderService.selectFsStoreOrderListVO(param);
+        //金牛需求 区别其他项目 status = 6 (金牛代服管家) ,其他项目请避免使用订单状态status = 6
+        TableDataInfo dataTable = getDataTable(list);
+        if (CloudHostUtils.hasCloudHostName("康年堂")){
+            dataTable.setMsg("knt");
+        }
         if (list != null) {
             for (FsStoreOrderVO vo : list) {
                 if(vo.getPhone()!=null){
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
+                if (CloudHostUtils.hasCloudHostName("康年堂")){
+                    //查询顺丰代服账号
+                    FsStoreOrderDf df = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(vo.getId());
+                    if (df != null){
+                        vo.setErpAccount(df.getLoginAccount());
+                    }
+                }
 
             }
         }
-        return getDataTable(list);
+        FsStoreOrderListAndStatisticsVo vo = new FsStoreOrderListAndStatisticsVo();
+        BeanUtils.copyProperties(dataTable, vo);
+        if (dataTable.getTotal()>0){
+            Map<String, BigDecimal> statistics= fsStoreOrderService.selectFsStoreOrderStatistics(param);
+            if (statistics != null && statistics.size() >= 3){
+                vo.setPayPriceTotal(statistics.get("pay_price").toString());
+                vo.setPayMoneyTotal(statistics.get("pay_money").toString());
+                vo.setPayRemainTotal(statistics.get("pay_remain").toString());
+            }else {
+                vo.setPayPriceTotal("0");
+                vo.setPayMoneyTotal("0");
+                vo.setPayRemainTotal("0");
+            }
+            //商品数量合计
+            String productStatistics= fsStoreOrderService.selectFsStoreOrderProductStatistics(param);
+            if (StringUtils.isNotBlank(productStatistics)){
+                vo.setProductInfo(productStatistics);
+            } else {
+                vo.setProductInfo("");
+            }
+
+        }
+        return vo;
     }
 
     /**
@@ -100,8 +147,8 @@ public class FsStoreHealthOrderScrmController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:export')")
     @Log(title = "健康商城订单", businessType = BusinessType.EXPORT)
-    @GetMapping("/healthExport")
-    public AjaxResult export1(FsStoreOrderParam param) {
+    @PostMapping("/healthExport")
+    public AjaxResult export1(@RequestBody FsStoreOrderParam param) {
         if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
             param.setBeginTime(null);
             param.setEndTime(null);
@@ -122,7 +169,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
             param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
         }
         param.setIsHealth("1");
-        List<FsStoreOrderExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
+        List<FsStoreOrderErpExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
         //对手机号脱敏
         if (list != null) {
             for (FsStoreOrderExportVO vo : list) {
@@ -135,8 +182,24 @@ public class FsStoreHealthOrderScrmController extends BaseController {
 
             }
         }
-        ExcelUtil<FsStoreOrderExportVO> util = new ExcelUtil<FsStoreOrderExportVO>(FsStoreOrderExportVO.class);
-        return util.exportExcel(list, "订单数据");
+        String filter = param.getFilter();
+        // 1. 处理filter参数:将逗号分隔的字符串拆分为ArrayList<String>
+        ArrayList<String> filterList = new ArrayList<>();
+        if (StringUtils.isNotBlank(filter)) {
+            // 按逗号拆分,同时去除可能的空格(如filter传"orderId, orderCode"时兼容)
+            String[] filterArr = filter.split("\\s*,\\s*");
+            filterList.addAll(Arrays.asList(filterArr));
+        }
+        // 动态导出:根据选中的字段生成Excel
+        ExcelUtil<FsStoreOrderErpExportVO> util = new ExcelUtil<FsStoreOrderErpExportVO>(FsStoreOrderErpExportVO.class);
+        AjaxResult result;
+        // 如果有选中的字段,只导出这些字段
+        if (filter != null && !filter.isEmpty()) {
+            return util.exportExcelSelectedColumns(list, "订单数据", filterList);
+        } else {
+            // 导出所有字段
+            return util.exportExcel(list, "订单数据");
+        }
     }
 
     /**
@@ -144,8 +207,8 @@ public class FsStoreHealthOrderScrmController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:export:details')")
     @Log(title = "健康商城订单", businessType = BusinessType.EXPORT)
-    @GetMapping("/healthExportDetails")
-    public AjaxResult healthExportDetails(FsStoreOrderParam param) {
+    @PostMapping("/healthExportDetails")
+    public AjaxResult healthExportDetails(@RequestBody FsStoreOrderParam param) {
         if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
             param.setBeginTime(null);
             param.setEndTime(null);
@@ -166,9 +229,25 @@ public class FsStoreHealthOrderScrmController extends BaseController {
             param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
         }
         param.setIsHealth("1");
-        List<FsStoreOrderExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
-        ExcelUtil<FsStoreOrderExportVO> util = new ExcelUtil<FsStoreOrderExportVO>(FsStoreOrderExportVO.class);
-        return util.exportExcel(list, "订单数据");
+        List<FsStoreOrderErpExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
+        String filter = param.getFilter();
+        // 1. 处理filter参数:将逗号分隔的字符串拆分为ArrayList<String>
+        ArrayList<String> filterList = new ArrayList<>();
+        if (StringUtils.isNotBlank(filter)) {
+            // 按逗号拆分,同时去除可能的空格(如filter传"orderId, orderCode"时兼容)
+            String[] filterArr = filter.split("\\s*,\\s*");
+            filterList.addAll(Arrays.asList(filterArr));
+        }
+        // 动态导出:根据选中的字段生成Excel
+        ExcelUtil<FsStoreOrderErpExportVO> util = new ExcelUtil<FsStoreOrderErpExportVO>(FsStoreOrderErpExportVO.class);
+        AjaxResult result;
+        // 如果有选中的字段,只导出这些字段
+        if (filter != null && !filter.isEmpty()) {
+            return util.exportExcelSelectedColumns(list, "订单数据", filterList);
+        } else {
+            // 导出所有字段
+            return util.exportExcel(list, "订单数据");
+        }
     }
 
 

+ 300 - 14
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -2,6 +2,7 @@ package com.fs.hisStore.controller;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
@@ -10,6 +11,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
@@ -21,12 +23,22 @@ import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryResponse;
+import com.fs.erp.dto.df.DFConfigVo;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.framework.web.service.TokenService;
+import com.fs.his.domain.FsDfAccount;
+import com.fs.his.domain.FsStoreOrderDf;
 import com.fs.his.domain.FsUser;
+import com.fs.his.enums.FsStoreOrderLogEnum;
+import com.fs.his.param.FsStoreOrderSetErpPhoneParam;
+import com.fs.his.service.IFsDfAccountService;
 import com.fs.his.service.IFsExpressService;
+import com.fs.his.service.IFsStoreOrderDfService;
 import com.fs.his.service.IFsUserService;
+import com.fs.his.service.impl.FsDfAccountServiceImpl;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.his.vo.FsStoreOrderListAndStatisticsVo;
+import com.fs.his.vo.FsStoreOrderListVO;
 import com.fs.hisStore.config.FsErpConfig;
 import com.fs.hisStore.domain.*;
 import com.fs.his.dto.ExpressInfoDTO;
@@ -36,6 +48,8 @@ import com.fs.hisStore.enums.ShipperCodeEnum;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.*;
 import com.fs.hisStore.vo.*;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -46,6 +60,12 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.fs.his.utils.PhoneUtil.encryptPhone;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -109,6 +129,18 @@ public class FsStoreOrderScrmController extends BaseController {
     @Autowired
     private IFsStoreProductScrmService productScrmService;
 
+    @Autowired
+    SysConfigMapper sysConfigMapper;
+
+    @Autowired
+    private IFsDfAccountService fsDfAccountService;
+
+    @Autowired
+    private IFsStoreOrderDfService fsStoreOrderDfService;
+
+    @Autowired
+    private IFsStoreOrderLogsScrmService fsStoreOrderLogsService;
+
     private IErpOrderService getErpService(){
         //判断是否开启erp
         IErpOrderService erpOrderService = null;
@@ -144,8 +176,8 @@ public class FsStoreOrderScrmController extends BaseController {
      * 查询订单列表
      */
     @PreAuthorize("@ss.hasPermi('store:storeOrder:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(FsStoreOrderParam param) {
+    @PostMapping("/list")
+    public TableDataInfo list(@RequestBody FsStoreOrderParam param) {
         startPage();
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
@@ -161,15 +193,49 @@ public class FsStoreOrderScrmController extends BaseController {
         }
         param.setNotHealth(1);
         List<FsStoreOrderVO> list = fsStoreOrderService.selectFsStoreOrderListVO(param);
+        //金牛需求 区别其他项目 status = 6 (金牛代服管家) ,其他项目请避免使用订单状态status = 6
+        TableDataInfo dataTable = getDataTable(list);
+        if (CloudHostUtils.hasCloudHostName("康年堂")){
+            dataTable.setMsg("knt");
+        }
         if (list != null) {
             for (FsStoreOrderVO vo : list) {
                 if(vo.getPhone()!=null){
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
+                if (CloudHostUtils.hasCloudHostName("康年堂")){
+                    //查询顺丰代服账号
+                    FsStoreOrderDf df = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(vo.getId());
+                    if (df != null){
+                        vo.setErpAccount(df.getLoginAccount());
+                    }
+                }
             }
         }
-        return getDataTable(list);
+        FsStoreOrderListAndStatisticsVo vo = new FsStoreOrderListAndStatisticsVo();
+        BeanUtils.copyProperties(dataTable, vo);
+        if (dataTable.getTotal()>0){
+            Map<String, BigDecimal> statistics= fsStoreOrderService.selectFsStoreOrderStatistics(param);
+            if (statistics != null && statistics.size() >= 3){
+                vo.setPayPriceTotal(statistics.get("pay_price").toString());
+                vo.setPayMoneyTotal(statistics.get("pay_money").toString());
+                vo.setPayRemainTotal(statistics.get("pay_remain").toString());
+            }else {
+                vo.setPayPriceTotal("0");
+                vo.setPayMoneyTotal("0");
+                vo.setPayRemainTotal("0");
+            }
+            //商品数量合计
+            String productStatistics= fsStoreOrderService.selectFsStoreOrderProductStatistics(param);
+            if (StringUtils.isNotBlank(productStatistics)){
+                vo.setProductInfo(productStatistics);
+            } else {
+                vo.setProductInfo("");
+            }
+
+        }
+        return vo;
     }
 
     @PreAuthorize("@ss.hasPermi('store:storeOrder:payRemainList')")
@@ -238,8 +304,8 @@ public class FsStoreOrderScrmController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('store:storeOrder:export')")
     @Log(title = "订单", businessType = BusinessType.EXPORT)
-    @GetMapping("/export")
-    public AjaxResult export(FsStoreOrderParam param) {
+    @PostMapping("/export")
+    public AjaxResult export(@RequestBody FsStoreOrderParam param) {
         if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
             param.setBeginTime(null);
             param.setEndTime(null);
@@ -260,13 +326,13 @@ public class FsStoreOrderScrmController extends BaseController {
             param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
         }
         param.setNotHealth(1);
-        List<FsStoreOrderExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
+        List<FsStoreOrderErpExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
         //对手机号脱敏
         if (list != null) {
             //获取当前账号角色权限
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
 
-            for (FsStoreOrderExportVO vo : list) {
+            for (FsStoreOrderErpExportVO vo : list) {
                 if (vo.getPhone() != null) {
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
@@ -278,8 +344,24 @@ public class FsStoreOrderScrmController extends BaseController {
                 }
             }
         }
-        ExcelUtil<FsStoreOrderExportVO> util = new ExcelUtil<FsStoreOrderExportVO>(FsStoreOrderExportVO.class);
-        return util.exportExcel(list, "订单数据");
+        String filter = param.getFilter();
+        // 1. 处理filter参数:将逗号分隔的字符串拆分为ArrayList<String>
+        ArrayList<String> filterList = new ArrayList<>();
+        if (StringUtils.isNotBlank(filter)) {
+            // 按逗号拆分,同时去除可能的空格(如filter传"orderId, orderCode"时兼容)
+            String[] filterArr = filter.split("\\s*,\\s*");
+            filterList.addAll(Arrays.asList(filterArr));
+        }
+        // 动态导出:根据选中的字段生成Excel
+        ExcelUtil<FsStoreOrderErpExportVO> util = new ExcelUtil<FsStoreOrderErpExportVO>(FsStoreOrderErpExportVO.class);
+        AjaxResult result;
+        // 如果有选中的字段,只导出这些字段
+        if (filter != null && !filter.isEmpty()) {
+            return util.exportExcelSelectedColumns(list, "订单数据", filterList);
+        } else {
+            // 导出所有字段
+            return util.exportExcel(list, "订单数据");
+        }
     }
 
 
@@ -288,8 +370,8 @@ public class FsStoreOrderScrmController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('store:storeOrder:export:details')")
     @Log(title = "订单", businessType = BusinessType.EXPORT)
-    @GetMapping("/exportDetails")
-    public AjaxResult exportDetails(FsStoreOrderParam param) {
+    @PostMapping("/exportDetails")
+    public AjaxResult exportDetails(@RequestBody FsStoreOrderParam param) {
         if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
             param.setBeginTime(null);
             param.setEndTime(null);
@@ -310,9 +392,25 @@ public class FsStoreOrderScrmController extends BaseController {
             param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
         }
         param.setNotHealth(1);
-        List<FsStoreOrderExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
-        ExcelUtil<FsStoreOrderExportVO> util = new ExcelUtil<FsStoreOrderExportVO>(FsStoreOrderExportVO.class);
-        return util.exportExcel(list, "订单数据");
+        List<FsStoreOrderErpExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
+        String filter = param.getFilter();
+        // 1. 处理filter参数:将逗号分隔的字符串拆分为ArrayList<String>
+        ArrayList<String> filterList = new ArrayList<>();
+        if (StringUtils.isNotBlank(filter)) {
+            // 按逗号拆分,同时去除可能的空格(如filter传"orderId, orderCode"时兼容)
+            String[] filterArr = filter.split("\\s*,\\s*");
+            filterList.addAll(Arrays.asList(filterArr));
+        }
+        // 动态导出:根据选中的字段生成Excel
+        ExcelUtil<FsStoreOrderErpExportVO> util = new ExcelUtil<FsStoreOrderErpExportVO>(FsStoreOrderErpExportVO.class);
+        AjaxResult result;
+        // 如果有选中的字段,只导出这些字段
+        if (filter != null && !filter.isEmpty()) {
+            return util.exportExcelSelectedColumns(list, "订单数据", filterList);
+        } else {
+            // 导出所有字段
+            return util.exportExcel(list, "订单数据");
+        }
     }
 
     @PreAuthorize("@ss.hasPermi('store:storeOrder:exportItems')")
@@ -778,4 +876,192 @@ public class FsStoreOrderScrmController extends BaseController {
         }
         return resultList;
     }
+
+    /**
+     * 查询erp默认手机号
+     * @return
+     */
+    @GetMapping(value = "/queryErpPhone")
+    public AjaxResult queryErpPhone()
+    {
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("erp.phone");
+        List<String> list = new ArrayList<>();
+        if(sysConfig!=null){
+            String configValue = sysConfig.getConfigValue();
+            if(StringUtils.isNotEmpty(configValue)){
+                list = JSON.parseArray(configValue, String.class);
+            }
+        }
+        return AjaxResult.success(list);
+    }
+
+    /**
+     * 设置erp默认手机号
+     * @param phoneList
+     * @return
+     */
+    @PostMapping(value = "/saveErpPhone")
+    public AjaxResult saveErpPhone(@RequestBody List<String> phoneList)
+    {
+        //去重
+        phoneList = phoneList.stream().distinct().collect(Collectors.toList());
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("erp.phone");
+        sysConfig.setConfigValue(JSON.toJSONString(phoneList));
+        return AjaxResult.success(sysConfigMapper.updateConfig(sysConfig));
+    }
+
+    /**
+     * 批量设置erp手机号
+     */
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:createErpOrder')")
+    @Log(title = "订单", businessType = BusinessType.UPDATE)
+    @PostMapping("/editErpPhone")
+    public AjaxResult editErpPhone(@RequestBody FsStoreOrderScrmSetErpPhoneParam param)
+    {
+        param.setOpeName(getLoginUser().getUser().getNickName());
+        List<String> erpPhone = param.getErpPhone();
+        if (erpPhone == null || erpPhone.isEmpty()) {
+            return AjaxResult.error("请选择手机号");
+        }
+        return toAjax(fsStoreOrderService.batchUpdateErpByOrderIds(param));
+    }
+
+    /**
+     * 获取erp账户
+     */
+    @GetMapping("/getErpAccount")
+    public R getErpAccount()
+    {
+        List<FsDfAccount> erpAccounts = fsDfAccountService.selectFsDfAccountList(null);
+        List<String> list = erpAccounts.stream().map(FsDfAccount::getLoginAccount).collect(Collectors.toList());
+        return R.ok().put("data", list);
+    }
+
+    @Log(title = "手动推管易", businessType = BusinessType.INSERT)
+    @ApiOperation("批量创建ERP订单")
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:createErpOrder')")
+    @PostMapping(value = "/batchCreateErpOrder")
+    public R batchCreateErpOrder(@RequestBody FsStoreOrderScrmSetErpPhoneParam param)
+    {
+        String nickName = getLoginUser().getUser().getNickName();
+        String loginAccount = param.getLoginAccount();
+        if (StringUtils.isBlank(loginAccount)){
+            return R.error("未选择推送erp账户");
+        }
+        FsStoreOrderDf df = getDFInfo(loginAccount);
+        if (df.getLoginAccount() == null){
+            return R.error("未查询到所选erp账户");
+        }
+        List<Long> orderIds = param.getOrderIds();
+        if (orderIds  == null || orderIds.isEmpty()) {
+            if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+                param.setCreateTimeList(param.getCreateTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getPayTimeRange())){
+                param.setPayTimeList(param.getPayTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
+                param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getDeliverySendTimeRange())){
+                param.setDeliverySendTimeList(param.getDeliverySendTimeRange().split("--"));
+            }
+            param.setNotHealth(1);
+            List<FsStoreOrderVO> list = fsStoreOrderService.selectFsStoreOrderListVO(param);
+            orderIds = list.stream().map(FsStoreOrderVO::getId).collect(Collectors.toList());
+        }
+        if (orderIds.isEmpty()){
+            return R.ok();
+        }
+        orderIds.forEach(orderId->{
+            try {
+                df.setOrderId(orderId);
+                FsStoreOrderDf temp = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(df.getOrderId());
+                if (temp == null){
+                    df.setParcelQuantity(param.getParcelQuantity()); //设置包裹数量
+                    fsStoreOrderDfService.insertFsStoreOrderDf(df);
+                    fsStoreOrderLogsService.create(orderId, FsStoreOrderLogEnum.SET_PUSH_ACCOUNT.getValue(),
+                            nickName + " " +FsStoreOrderLogEnum.SET_PUSH_ACCOUNT.getDesc() + ":" + df.getLoginAccount());
+                }
+                fsStoreOrderService.createOmsOrder(orderId);
+                fsStoreOrderLogsService.create(orderId, FsStoreOrderLogEnum.PUSH_ORDER_ERP.getValue(),
+                        nickName + " " +FsStoreOrderLogEnum.PUSH_ORDER_ERP.getDesc() + ":" + df.getLoginAccount());
+            } catch (ParseException e) {
+                throw new RuntimeException(e);
+            }
+
+        });
+        return R.ok();
+    }
+
+
+    @ApiOperation("批量设置订单账户")
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:createErpOrder')")
+    @PostMapping(value = "/batchSetErpOrder")
+    public R batchSetErpOrder(@RequestBody FsStoreOrderScrmSetErpPhoneParam param)
+    {
+        String nickName = getLoginUser().getUser().getNickName();
+        String loginAccount = param.getLoginAccount();
+        if (StringUtils.isBlank(loginAccount)){
+            return R.error("未选择erp账户");
+        }
+        FsStoreOrderDf df = getDFInfo(loginAccount);
+        if (df.getLoginAccount() == null){
+            return R.error("未查询到所选erp账户");
+        }
+        List<Long> orderIds = param.getOrderIds();
+        if (orderIds  == null || orderIds.isEmpty()) {
+            if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+                param.setCreateTimeList(param.getCreateTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getPayTimeRange())){
+                param.setPayTimeList(param.getPayTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
+                param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getDeliverySendTimeRange())){
+                param.setDeliverySendTimeList(param.getDeliverySendTimeRange().split("--"));
+            }
+            param.setNotHealth(1);
+            List<FsStoreOrderVO> list = fsStoreOrderService.selectFsStoreOrderListVO(param);
+            orderIds = list.stream().map(FsStoreOrderVO::getId).collect(Collectors.toList());
+        }
+        if (orderIds.isEmpty()){
+            return R.ok();
+        }
+        orderIds.forEach(orderId->{
+            df.setOrderId(orderId);
+            FsStoreOrderDf temp = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(df.getOrderId());
+            df.setParcelQuantity(param.getParcelQuantity());
+            if (temp != null){
+                df.setUpdateTime(new Date());
+                fsStoreOrderDfService.updateFsStoreOrderDf(df);
+            } else {
+                fsStoreOrderDfService.insertFsStoreOrderDf(df);
+            }
+            fsStoreOrderLogsService.create(orderId, FsStoreOrderLogEnum.SET_PUSH_ACCOUNT.getValue(),
+                    nickName + " " +FsStoreOrderLogEnum.SET_PUSH_ACCOUNT.getDesc() + ":" + df.getLoginAccount());
+        });
+        return R.ok();
+    }
+
+    private FsStoreOrderDf getDFInfo(String loginAccount) {
+        //查询订单账户 判断是否存在该订单账户
+        List<FsDfAccount> erpAccounts = fsDfAccountService.selectFsDfAccountList(null);
+        FsStoreOrderDf df = new FsStoreOrderDf();
+        for (FsDfAccount erpAccount : erpAccounts) {
+            if (loginAccount.equals(erpAccount.getLoginAccount())){
+                //添加df记录
+                df.setAppKey(erpAccount.getDfAppKey());
+                df.setAppSecret(erpAccount.getDfAppsecret());
+                df.setLoginAccount(loginAccount);
+                df.setMonthlyCard(erpAccount.getMonthlyCard());
+                df.setExpressProductCode(erpAccount.getExpressProductCode());
+                df.setStatus(0);
+                break;
+            }
+        }
+        return df;
+    }
 }

+ 1 - 4
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java

@@ -57,11 +57,8 @@ public class QwExternalContactController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(QwExternalContactParam qwExternalContact)
     {
-        if(ObjectUtil.isEmpty(qwExternalContact.getCompanyId())){
-            throw new ServiceException("操作失败,请选择企业!");
-        }
         startPage();
-        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
+        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVONewSys(qwExternalContact);
         list.forEach(item->{
 
             if (!Objects.equals(item.getTagIds(), "[]") && item.getTagIds()!=null) {

+ 9 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java

@@ -6,6 +6,7 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
 import com.fs.qw.domain.QwPushCount;
 import com.fs.qw.service.IQwPushCountService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -151,4 +152,12 @@ public class QwPushCountController extends BaseController {
     public AjaxResult remove(@PathVariable Long[] ids) {
         return toAjax(qwPushCountService.deleteQwPushCountByIds(ids));
     }
+
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:tokenList')")
+    @GetMapping("/tokenList")
+    public TableDataInfo tokenList(FastGptPushTokenTotal pushTokenInfo) {
+        startPage();
+        List<FastGptPushTokenTotal> list = qwPushCountService.selectFastGptPushTokenTotalList(pushTokenInfo);
+        return getDataTable(list);
+    }
 }

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

@@ -5,11 +5,14 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.QwOptionsVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 /**
  * 企微用户Controller
  *
@@ -34,4 +37,12 @@ public class QwUserController extends BaseController {
     public R getQwUserInfo(QwFsUserParam param){
         return R.ok().put("data",qwUserService.getQwUserInfo(param));
     }
+
+
+   @GetMapping("/getMyQwCompanyList")
+    public R getMyQwCompanyList()
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOBySys();
+        return  R.ok().put("data",list);
+    }
 }

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

@@ -4,10 +4,10 @@ server:
 # Spring配置
 spring:
   profiles:
-    active: dev
+#    active: dev
 #    active: druid-hdt
 #    active: druid-yzt
-#    active: druid-sxjz
+    active: druid-sxjz-test
 #    active: druid-sft
 #    active: druid-fby
 #    active: dev

+ 47 - 0
fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java

@@ -4,15 +4,26 @@ package com.fs.app.controller;
 import cn.hutool.core.util.ObjectUtil;
 import com.fs.app.exception.FSException;
 import com.fs.app.utils.JwtUtils;
+import com.fs.common.constant.HttpStatus;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.sql.SqlUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import io.jsonwebtoken.Claims;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
 
 public class AppBaseController {
 	@Autowired
@@ -27,8 +38,16 @@ public class AppBaseController {
 	public Long getCompanyId() {
 		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
 		Claims claims=jwtUtils.getClaimByToken(headValue);
+		if (ObjectUtil.isEmpty(claims)){
+			throw new FSException("未授权,请先登录!");
+		}
 		String userId = claims.getSubject().toString();
 		Long companyId =(Long)redisCache.getCacheObject("companyId:"+userId);
+		if (companyId==null){
+			CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(userId));
+			companyId = companyUser.getCompanyId();
+			redisCache.setCacheObject("companyId:" + companyUser.getUserId(), companyUser.getCompanyId(), 604800, TimeUnit.SECONDS);
+		}
 		return companyId;
 	}
 	public String getUserId()
@@ -57,5 +76,33 @@ public class AppBaseController {
 		}
 		return user.getUserId();
 	}
+    /**
+     * 设置请求分页数据
+     */
+    protected void startPage()
+    {
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+        if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
+        {
+            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+            Boolean reasonable = pageDomain.getReasonable();
+            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
+        }
+    }
 
+    /**
+     * 响应请求分页数据
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected TableDataInfo getDataTable(List<?> list)
+    {
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(new PageInfo(list).getTotal());
+        return rspData;
+    }
 }

+ 214 - 7
fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -2,6 +2,7 @@ package com.fs.app.controller;
 
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.app.annotation.Login;
@@ -11,19 +12,32 @@ import com.fs.app.vo.CompanySubUserVO;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.constant.UserConstants;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.CompanyRoleMapper;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.param.companyUserAddPrintParam;
 import com.fs.company.service.*;
 import com.fs.company.vo.CompanyTagUserVO;
 import com.fs.company.vo.CompanyUserChangeApplyVO;
+import com.fs.config.ai.AiHostProper;
 import com.fs.core.security.SecurityUtils;
 import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.IFsUserCompanyUserService;
+import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
+import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
+import com.fs.fastgptApi.util.AudioUtils;
+import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.qw.dto.UserProjectDTO;
+import com.fs.sop.domain.QwSopTempVoice;
+import com.fs.sop.service.IQwSopTempVoiceService;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysDictDataService;
 import com.fs.system.vo.DictVO;
 import com.github.pagehelper.PageHelper;
@@ -34,10 +48,19 @@ import io.swagger.annotations.ApiParam;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import java.io.File;
+import java.io.FileInputStream;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDate;
@@ -59,12 +82,15 @@ public class CompanyUserController extends AppBaseController {
     private final ICompanyUserChangeApplyService companyUserChangeApplyService;
     private final CompanyRoleMapper companyRoleMapper;
     private final IAppService appService;
-    @Autowired
-    private ISysDictDataService dictDataService;
-    @Autowired
-    private IFsUserCompanyUserService fsUserCompanyUserService;
-    @Autowired
-    private ICompanyTagUserService companyTagUserService;
+    private final RedisCache redisCache;
+    private final CompanyUserMapper companyUserMapper;
+    private final IQwSopTempVoiceService voiceService;
+    private final AiHostProper aiHostProper;
+    private final ISysDictDataService dictDataService;
+    private final IFsUserCompanyUserService fsUserCompanyUserService;
+    private final ICompanyTagUserService companyTagUserService;
+    private final FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
+    public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
 
     @Login
     @ApiOperation("查询用户列表")
@@ -394,6 +420,188 @@ public class CompanyUserController extends AppBaseController {
                 .collect(Collectors.toList());
         return R.ok().put("data",filteredDictVOS);
     }
+    /**
+     * 当只有模板文字text时,生成表中对应条的voice_url和user_voice_url
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/companyUserVoice")
+    public R companyUserVoice(@RequestParam("id") Long id){
+        AudioVO audioVO = new AudioVO();
+        Long companyUserId = getCompanyUserId();
+        List<QwSopTempVoice> sopTempVoices = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY + ":" + companyUserId);
+        if(sopTempVoices != null && !sopTempVoices.isEmpty()){
+            List<Long> collect = sopTempVoices.stream().map(QwSopTempVoice::getId).collect(Collectors.toList());
+            if (collect.contains(id)){
+                return R.ok().put("code",202).put("msg","该语音已进入转换,请完成后再试。");
+            }
+        }
+
+        if(companyUserId != null){
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserByCompanyUserId(companyUserId);
+            if(companyUser != null && companyUser.getVoicePrintUrl() == null){
+                return R.ok().put("code",201).put("msg","账号未录制声纹,请录制后再试!");
+            }
+        }
+
+        QwSopTempVoice qwSopTempVoice = voiceService.selectQwSopTempVoiceById(id);
+        if(qwSopTempVoice != null && qwSopTempVoice.getCompanyUserId() != null){
+            List<FastgptChatVoiceHomo> homos = fastgptChatVoiceHomoMapper.selectFastgptChatVoiceHomoList(new FastgptChatVoiceHomo());
+            audioVO = AudioUtils.createUserUrlAndUrl(homos,qwSopTempVoice.getCompanyUserId(), qwSopTempVoice.getVoiceTxt().replace(" ",""));
+            if(audioVO != null && audioVO.getWavUrl() != null &&  audioVO.getUrl() != null){
+                qwSopTempVoice.setVoiceUrl(audioVO.getUrl());
+                qwSopTempVoice.setUserVoiceUrl(audioVO.getWavUrl());
+                qwSopTempVoice.setDuration(audioVO.getDuration());
+                qwSopTempVoice.setRecordType(1);
+                voiceService.updateQwSopTempVoice(qwSopTempVoice);
+            }
+        }
+        return R.ok().put("data", audioVO);
+    }
+    @Login
+    @ApiOperation("上传声纹")
+    @PostMapping("/addVoicePrintUrl")
+    public R addVoicePrintUrl(@RequestBody companyUserAddPrintParam param) throws Exception {
+        Long userId=getCompanyUserId();
+        if(userId==null){
+            return R.error(403,"用户失效");
+        }
+        CompanyUser companyUser = new CompanyUser();
+        companyUser.setUserId(userId);
+        companyUser.setVoicePrintUrl(param.getVoicePrintUrl());
+
+        //转换音频格式 mp3-wav
+        String s = AudioUtils.audioWAVFromUrl(param.getVoicePrintUrl());
+
+        //保存文件并且上传存储桶
+        System.out.println(s);
+        File file = new File(s);
+        FileInputStream fileInputStream = new FileInputStream(file);
+        CloudStorageService storage = OSSFactory.build();
+        String wavUrl = storage.uploadSuffix(fileInputStream, ".wav");
+
+        //更新销售员工声纹
+        companyUser.setVoicePrintUrl(wavUrl);
+        companyUserMapper.updateCompanyUser(companyUser);
+
+        try {
+            CloseableHttpClient httpClient = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(aiHostProper.getCommonApi()+"/app/common/addCompanyAudio");
+            String json = "{\"url\":\""+wavUrl+"\",\"id\":\""+userId+"\"}";
+            StringEntity entity = new StringEntity(json);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-type", "application/json");
+            HttpResponse response = httpClient.execute(httpPost);
+
+            if (response.getStatusLine().getStatusCode() == 200) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+                JSONObject jsonObject = JSON.parseObject(responseBody);
+                Integer code = (Integer)jsonObject.get("code");
+                if (code==200){
+                    voiceService.insertQwSopTempVoiceModel(userId);
+                    return R.ok();
+                }
+            } else {
+                return R.error();
+            }
+
+            httpClient.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return R.error();
+
+    }
+
+    /**
+     * 当只有user_voice_url时,生成表中对应条的voice_url
+     * @param userVoiceUrl  wav格式的语音文件
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/companyUserVoiceNew")
+    public R companyUserVoiceNew( @RequestParam("id") Long id,@RequestParam("userVoiceUrl") String userVoiceUrl){
+
+        AudioVO audioVO = new AudioVO();
+        Long companyUserId = getCompanyUserId();
+        List<QwSopTempVoice> sopTempVoices = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY + ":" + companyUserId);
+        if(sopTempVoices != null && !sopTempVoices.isEmpty()){
+            List<Long> collect = sopTempVoices.stream().map(QwSopTempVoice::getId).collect(Collectors.toList());
+            if (collect.contains(id)){
+                return R.ok().put("code",202).put("msg","该语音已进入转换,请完成后再试。");
+            }
+        }
+
+        QwSopTempVoice qwSopTempVoice = voiceService.selectQwSopTempVoiceByIdAndUserVoiceUrl(id);
+        if(qwSopTempVoice != null && qwSopTempVoice.getId() != null){
+            audioVO = AudioUtils.createVoiceUrl(qwSopTempVoice.getCompanyUserId(), userVoiceUrl);
+            if(audioVO != null && audioVO.getUrl() != null){
+                qwSopTempVoice.setVoiceUrl(audioVO.getUrl());
+                qwSopTempVoice.setUserVoiceUrl(userVoiceUrl);
+                qwSopTempVoice.setDuration(audioVO.getDuration());
+                qwSopTempVoice.setRecordType(1);
+                voiceService.updateQwSopTempVoice(qwSopTempVoice);
+            }
+        }
+        return R.ok().put("data", audioVO);
+    }
+    @GetMapping("/querySopVoiceList")
+    public TableDataInfo querySopVoiceList(@Param("recordType") Integer recordType){
+        startPage();
+        QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+        sopTempVoice.setRecordType(recordType);
+        sopTempVoice.setCompanyUserId(getCompanyUserId());
+        List<QwSopTempVoice> sopTempVoices = voiceService.selectQwSopTempVoiceNewList(sopTempVoice);
+        return getDataTable(sopTempVoices);
+    }
+    @GetMapping("/query/{id}")
+    public R querySopVoiceById(@PathVariable("id") Long id){
+        QwSopTempVoice tempVoice = voiceService.selectQwSopTempVoiceById(id);
+        AudioVO audioVO = new AudioVO();
+        if(tempVoice != null){
+            audioVO.setId(tempVoice.getId());
+            audioVO.setVoiceTxt(tempVoice.getVoiceTxt());
+            audioVO.setUrl(tempVoice.getVoiceUrl());
+            audioVO.setWavUrl(tempVoice.getUserVoiceUrl());
+            audioVO.setDuration(tempVoice.getDuration());
+            audioVO.setRecordType(tempVoice.getRecordType());
+        }
+        return R.ok().put("data", audioVO);
+    }
+    /**
+     * 一键转换
+     * @return
+     */
+    @GetMapping("/createUserAllVoice")
+    public R createUserAllVoice(){
+        QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+        sopTempVoice.setRecordType(0);
+        Long companyUserId = getCompanyUserId();
+
+
+        if(companyUserId != null){
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserByCompanyUserId(companyUserId);
+            if(companyUser != null && companyUser.getVoicePrintUrl() == null){
+                return R.ok().put("code",201).put("msg","账号未录制声纹,请录制后再试!");
+            }
+        }
+
+        sopTempVoice.setCompanyUserId(companyUserId);
+        List<QwSopTempVoice> sopTempVoices = voiceService.selectQwSopTempVoiceNewList(sopTempVoice);
+        if(sopTempVoices != null && !sopTempVoices.isEmpty()){
+            List<Long> newCompanyUserId = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY);
+            if(newCompanyUserId != null && newCompanyUserId.contains(companyUserId)){
+                return R.error().put("code",202).put("msg","语音还未转换完成,请完成后再添加!");
+            }else{
+                redisCache.setVoice(SOP_TEMP_VOICE_KEY,companyUserId);
+                sopTempVoices.forEach(m -> m.setVoiceTxt(m.getVoiceTxt().replace(" ","")));
+                redisCache.setVoiceList(SOP_TEMP_VOICE_KEY + ":" + companyUserId, sopTempVoices);
+                return R.ok().put("msg","语音已加入队列进行转换,请耐心等待!");
+            }
+        }
+        return null;
+    }
 
     @ApiOperation("查询所有项目")
     @GetMapping("/getDictProject")
@@ -401,5 +609,4 @@ public class CompanyUserController extends AppBaseController {
         List<DictVO> dictVOS = dictDataService.selectDictDataListByType("sys_course_project");
         return R.ok().put("data",dictVOS);
     }
-
 }

+ 28 - 32
fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java

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

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

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

+ 244 - 0
fs-company-app/src/main/java/com/fs/core/aspectj/LogAspect.java

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

+ 94 - 0
fs-company-app/src/main/java/com/fs/core/config/DataSourceConfig.java

@@ -0,0 +1,94 @@
+package com.fs.core.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+import com.fs.common.enums.DataSourceType;
+import com.fs.core.datasource.DynamicDataSource;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.servlet.*;
+import javax.servlet.FilterConfig;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+public class DataSourceConfig {
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.sop.druid.master")
+    public DataSource sopDataSource() {
+        return new DruidDataSource();
+    }
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.mysql.druid.master")
+    public DataSource masterDataSource() {
+        return new DruidDataSource();
+    }
+
+
+
+    @Bean
+    @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);
+    }
+
+    /**
+     * 去除监控页面底部的广告
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
+    {
+        // 获取web监控页面的参数
+        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+        // 提取common.js的配置路径
+        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+        final String filePath = "support/http/resources/js/common.js";
+        // 创建filter进行过滤
+        Filter filter = new Filter()
+        {
+            @Override
+            public void init(FilterConfig filterConfig) throws ServletException
+            {
+            }
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+                    throws IOException, ServletException
+            {
+                chain.doFilter(request, response);
+                // 重置缓冲区,响应头不会被重置
+                response.resetBuffer();
+                // 获取common.js
+                String text = Utils.readFromResource(filePath);
+                // 正则替换banner, 除去底部的广告信息
+                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+                response.getWriter().write(text);
+            }
+            @Override
+            public void destroy()
+            {
+            }
+        };
+        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(filter);
+        registrationBean.addUrlPatterns(commonJsPattern);
+        return registrationBean;
+    }
+}

+ 123 - 123
fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java

@@ -1,123 +1,123 @@
-package com.fs.core.config;
-
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
-import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
-import com.alibaba.druid.util.Utils;
-import com.fs.common.enums.DataSourceType;
-import com.fs.common.utils.spring.SpringUtils;
-import com.fs.core.config.properties.DruidProperties;
-import com.fs.core.datasource.DynamicDataSource;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-
-import javax.servlet.*;
-import javax.sql.DataSource;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * druid 配置多数据源
- * 
-
- */
-@Configuration
-public class DruidConfig
-{
-    @Bean
-    @ConfigurationProperties("spring.datasource.mysql.druid.master")
-    public DataSource masterDataSource(DruidProperties druidProperties)
-    {
-        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
-        return druidProperties.dataSource(dataSource);
-    }
-
-    @Bean
-    @ConfigurationProperties("spring.datasource.mysql.druid.slave")
-    @ConditionalOnProperty(prefix = "spring.datasource.mysql.druid.slave", name = "enabled", havingValue = "true")
-    public DataSource slaveDataSource(DruidProperties druidProperties)
-    {
-        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
-        return druidProperties.dataSource(dataSource);
-    }
-
-    @Bean(name = "dynamicDataSource")
-    @Primary
-    public DynamicDataSource dataSource(DataSource masterDataSource)
-    {
-        Map<Object, Object> targetDataSources = new HashMap<>();
-        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
-        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
-        return new DynamicDataSource(masterDataSource, targetDataSources);
-    }
-    
-    /**
-     * 设置数据源
-     * 
-     * @param targetDataSources 备选数据源集合
-     * @param sourceName 数据源名称
-     * @param beanName bean名称
-     */
-    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
-    {
-        try
-        {
-            DataSource dataSource = SpringUtils.getBean(beanName);
-            targetDataSources.put(sourceName, dataSource);
-        }
-        catch (Exception e)
-        {
-        }
-    }
-
-    /**
-     * 去除监控页面底部的广告
-     */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    @Bean
-    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
-    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
-    {
-        // 获取web监控页面的参数
-        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
-        // 提取common.js的配置路径
-        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
-        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
-        final String filePath = "support/http/resources/js/common.js";
-        // 创建filter进行过滤
-        Filter filter = new Filter()
-        {
-            @Override
-            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
-            {
-            }
-            @Override
-            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-                    throws IOException, ServletException
-            {
-                chain.doFilter(request, response);
-                // 重置缓冲区,响应头不会被重置
-                response.resetBuffer();
-                // 获取common.js
-                String text = Utils.readFromResource(filePath);
-                // 正则替换banner, 除去底部的广告信息
-                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
-                text = text.replaceAll("powered.*?shrek.wang</a>", "");
-                response.getWriter().write(text);
-            }
-            @Override
-            public void destroy()
-            {
-            }
-        };
-        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
-        registrationBean.setFilter(filter);
-        registrationBean.addUrlPatterns(commonJsPattern);
-        return registrationBean;
-    }
-}
+//package com.fs.core.config;
+//
+//import com.alibaba.druid.pool.DruidDataSource;
+//import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+//import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+//import com.alibaba.druid.util.Utils;
+//import com.fs.common.enums.DataSourceType;
+//import com.fs.common.utils.spring.SpringUtils;
+//import com.fs.core.config.properties.DruidProperties;
+//import com.fs.core.datasource.DynamicDataSource;
+//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+//import org.springframework.boot.context.properties.ConfigurationProperties;
+//import org.springframework.boot.web.servlet.FilterRegistrationBean;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.context.annotation.Primary;
+//
+//import javax.servlet.*;
+//import javax.sql.DataSource;
+//import java.io.IOException;
+//import java.util.HashMap;
+//import java.util.Map;
+//
+///**
+// * druid 配置多数据源
+// *
+//
+// */
+//@Configuration
+//public class DruidConfig
+//{
+//    @Bean
+//    @ConfigurationProperties("spring.datasource.mysql.druid.master")
+//    public DataSource masterDataSource(DruidProperties druidProperties)
+//    {
+//        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+//        return druidProperties.dataSource(dataSource);
+//    }
+//
+//    @Bean
+//    @ConfigurationProperties("spring.datasource.mysql.druid.slave")
+//    @ConditionalOnProperty(prefix = "spring.datasource.mysql.druid.slave", name = "enabled", havingValue = "true")
+//    public DataSource slaveDataSource(DruidProperties druidProperties)
+//    {
+//        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+//        return druidProperties.dataSource(dataSource);
+//    }
+//
+//    @Bean(name = "dynamicDataSource")
+//    @Primary
+//    public DynamicDataSource dataSource(DataSource masterDataSource)
+//    {
+//        Map<Object, Object> targetDataSources = new HashMap<>();
+//        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
+//        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
+//        return new DynamicDataSource(masterDataSource, targetDataSources);
+//    }
+//
+//    /**
+//     * 设置数据源
+//     *
+//     * @param targetDataSources 备选数据源集合
+//     * @param sourceName 数据源名称
+//     * @param beanName bean名称
+//     */
+//    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
+//    {
+//        try
+//        {
+//            DataSource dataSource = SpringUtils.getBean(beanName);
+//            targetDataSources.put(sourceName, dataSource);
+//        }
+//        catch (Exception e)
+//        {
+//        }
+//    }
+//
+//    /**
+//     * 去除监控页面底部的广告
+//     */
+//    @SuppressWarnings({ "rawtypes", "unchecked" })
+//    @Bean
+//    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+//    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
+//    {
+//        // 获取web监控页面的参数
+//        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+//        // 提取common.js的配置路径
+//        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+//        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+//        final String filePath = "support/http/resources/js/common.js";
+//        // 创建filter进行过滤
+//        Filter filter = new Filter()
+//        {
+//            @Override
+//            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
+//            {
+//            }
+//            @Override
+//            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+//                    throws IOException, ServletException
+//            {
+//                chain.doFilter(request, response);
+//                // 重置缓冲区,响应头不会被重置
+//                response.resetBuffer();
+//                // 获取common.js
+//                String text = Utils.readFromResource(filePath);
+//                // 正则替换banner, 除去底部的广告信息
+//                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+//                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+//                response.getWriter().write(text);
+//            }
+//            @Override
+//            public void destroy()
+//            {
+//            }
+//        };
+//        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+//        registrationBean.setFilter(filter);
+//        registrationBean.addUrlPatterns(commonJsPattern);
+//        return registrationBean;
+//    }
+//}

+ 3 - 3
fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java

@@ -7,8 +7,8 @@ import java.util.Map;
 
 /**
  * 动态数据源
- * 
- 
+ *
+
  */
 public class DynamicDataSource extends AbstractRoutingDataSource
 {
@@ -24,4 +24,4 @@ public class DynamicDataSource extends AbstractRoutingDataSource
     {
         return DynamicDataSourceContextHolder.getDataSourceType();
     }
-}
+}

+ 4 - 5
fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java

@@ -5,11 +5,10 @@ import org.slf4j.LoggerFactory;
 
 /**
  * 数据源切换处理
- * 
- 
+ *
+
  */
-public class DynamicDataSourceContextHolder
-{
+public class DynamicDataSourceContextHolder {
     public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
 
     /**
@@ -23,7 +22,7 @@ public class DynamicDataSourceContextHolder
      */
     public static void setDataSourceType(String dsType)
     {
-        log.info("切换到{}数据源", dsType);
+//        log.info("切换到{}数据源", dsType);
         CONTEXT_HOLDER.set(dsType);
     }
 

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

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

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

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

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

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

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

@@ -6,4 +6,4 @@ server:
 spring:
   profiles:
 #    active: druid-fcky-test
-    active: dev
+    active: dev-jnlzjk

+ 7 - 12
fs-company/src/main/java/com/fs/company/controller/company/CompanyDeptController.java

@@ -149,21 +149,16 @@ public class CompanyDeptController extends BaseController
     @PreAuthorize("@ss.hasPermi('company:dept:edit')")
     @Log(title = "部门管理", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@Validated @RequestBody CompanyDept dept)
-    {
+    public AjaxResult edit(@Validated @RequestBody CompanyDept dept) {
+        if(dept.getParentId().equals(dept.getDeptId())) {
+            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
+        }
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         dept.setCompanyId(loginUser.getCompany().getCompanyId());
-        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
-        {
+        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
             return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
-        }
-        else if (dept.getParentId().equals(dept.getDeptId()))
-        {
-            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
-        }
-        else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
-                && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0)
-        {
+        } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
+                && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0) {
             return AjaxResult.error("该部门包含未停用的子部门!");
         }
         dept.setUpdateBy(SecurityUtils.getUsername());

+ 27 - 19
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -57,6 +57,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
 
 
@@ -136,27 +137,34 @@ public class CompanyUserController extends BaseController {
     public TableDataInfo qwList(CompanyUserQwParam user) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         user.setCompanyId(loginUser.getCompany().getCompanyId());
-        startPage();
         List<CompanyUserQwListVO> list = companyUserService.selectCompanyUserQwListVO(user);
-        for (CompanyUserQwListVO companyUserQwListVO : list) {
-            CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(companyUserQwListVO.getCompanyId(), companyUserQwListVO.getUserId());
-            if (ObjectUtil.isEmpty(companyUserDelayTime)) {
-                String json = configService.selectConfigByKey("course.config");
-                CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-                companyUserQwListVO.setSendDelayTime(config.getSendDelayTime());
-            } else {
-                companyUserQwListVO.setSendDelayTime(companyUserDelayTime.getSendDelayTime());
-            }
-            //是否绑定
-            if(QwStatusEnum.BOUND.getCode() == companyUserQwListVO.getQwStatus()){
-                if(!companyUserQwListVO.getQwUserId().isEmpty()){
-                    Long[] ids = Arrays.stream(companyUserQwListVO.getQwUserId().split(","))
-                            .map(Long::parseLong)
-                            .toArray(Long[]::new);
-                    List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(ids);
-                    companyUserQwListVO.setQwUsers(qwUserVOS);
-                }
+        if (!list.isEmpty()){
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+            Long sendDelayTime = config.getSendDelayTime();
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            for (CompanyUserQwListVO companyUserQwListVO : list) {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(companyUserQwListVO.getCompanyId(), companyUserQwListVO.getUserId());
+                    if (ObjectUtil.isEmpty(companyUserDelayTime)) {
+                        companyUserQwListVO.setSendDelayTime(sendDelayTime);
+                    } else {
+                        companyUserQwListVO.setSendDelayTime(companyUserDelayTime.getSendDelayTime());
+                    }
+                    //是否绑定
+                    if(QwStatusEnum.BOUND.getCode() == companyUserQwListVO.getQwStatus()){
+                        if(!companyUserQwListVO.getQwUserId().isEmpty()){
+                            Long[] ids = Arrays.stream(companyUserQwListVO.getQwUserId().split(","))
+                                    .map(Long::parseLong)
+                                    .toArray(Long[]::new);
+                            List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(ids);
+                            companyUserQwListVO.setQwUsers(qwUserVOS);
+                        }
+                    }
+                });
+                futures.add(future);
             }
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
         }
         return getDataTable(list);
     }

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

@@ -82,7 +82,7 @@ public class FsCourseAnswerLogsController extends BaseController
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
-        param.setCompanyUserId(loginUser.getUser().getUserId());
+        //param.setCompanyUserId(loginUser.getUser().getUserId());
         if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
             param.setPhone(param.getPhoneMk());
         }

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

@@ -89,7 +89,7 @@ public class FsCourseRedPacketLogController extends BaseController
         startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
-        fsCourseRedPacketLog.setCompanyUserId(loginUser.getUser().getUserId());
+        //fsCourseRedPacketLog.setCompanyUserId(loginUser.getUser().getUserId());
         if (fsCourseRedPacketLog.getPhoneMk() != null && fsCourseRedPacketLog.getPhoneMk() != "") {
             fsCourseRedPacketLog.setPhone(encryptPhone(fsCourseRedPacketLog.getPhoneMk()));
         }

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

@@ -1,5 +1,6 @@
 package com.fs.company.controller.course;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -9,11 +10,10 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.service.impl.CompanyDeptServiceImpl;
 import com.fs.course.domain.FsCourseWatchLog;
-import com.fs.course.param.FsCourseOverParam;
-import com.fs.course.param.FsCourseUserStatisticsListParam;
-import com.fs.course.param.FsCourseWatchLogListParam;
-import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.param.*;
 import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
+import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.vo.FsCourseOverVO;
 import com.fs.course.vo.FsCourseUserStatisticsListVO;
 import com.fs.course.vo.FsCourseWatchLogListVO;
@@ -54,6 +54,12 @@ public class FsCourseWatchLogController extends BaseController
     @Autowired
     private CompanyDeptServiceImpl companyDeptService;
 
+    @Autowired
+    private IFsUserCoursePeriodService userCoursePeriodService;
+
+    @Autowired
+    private IFsUserCoursePeriodDaysService userCoursePeriodDaysService;
+
     /**
      * 查询短链课程看课记录列表
      */
@@ -62,9 +68,26 @@ public class FsCourseWatchLogController extends BaseController
     public TableDataInfo list(FsCourseWatchLogListParam param)
     {
 
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
+
+        if (param.getSendType()==1&& param.getPeriodETime()!=null && param.getPeriodSTime()!=null) {
+            List<Long> periodIds = userCoursePeriodDaysService.selectFsUserCoursePeriodDaysByTime(param.getPeriodSTime(), param.getPeriodETime());
+
+            if (!periodIds.isEmpty()){
+                List<Long> longs = userCoursePeriodService.selectFsUserCoursePeriodListByPeriodId(periodIds, loginUser.getCompany().getCompanyId());
+                if (!longs.isEmpty()){
+                    param.setPeriodIds(longs);
+                }else {
+                    return getDataTable(new ArrayList<>());
+                }
+            }else {
+                return getDataTable(new ArrayList<>());
+            }
+
+        }
+
+        startPage();
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
         return getDataTable(list);
     }
@@ -96,6 +119,24 @@ public class FsCourseWatchLogController extends BaseController
         param.setUserType(loginUser.getUser().getUserType());
         param.setCompanyId(loginUser.getCompany().getCompanyId());
 
+
+        if (ObjectUtil.isNotEmpty(param.getSendType()) &&param.getSendType()==1&& param.getPeriodETime()!=null && param.getPeriodSTime()!=null) {
+            List<Long> periodIds = userCoursePeriodDaysService.selectFsUserCoursePeriodDaysByTime(param.getPeriodSTime(), param.getPeriodETime());
+
+            if (!periodIds.isEmpty()){
+                List<Long> longs = userCoursePeriodService.selectFsUserCoursePeriodListByPeriodId(periodIds, loginUser.getCompany().getCompanyId());
+                if (!longs.isEmpty()){
+                    param.setPeriodIds(longs);
+                }else {
+                    return getDataTable(new ArrayList<>());
+                }
+            }else {
+                return getDataTable(new ArrayList<>());
+            }
+
+        }
+
+
         startPage();
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
 
@@ -108,9 +149,27 @@ public class FsCourseWatchLogController extends BaseController
     @GetMapping("/myList")
     public TableDataInfo myList(FsCourseWatchLogListParam param)
     {
-        startPage();
+
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyUserId( loginUser.getUser().getUserId());
+
+        if (param.getSendType()==1&& param.getPeriodETime()!=null && param.getPeriodSTime()!=null) {
+            List<Long> periodIds = userCoursePeriodDaysService.selectFsUserCoursePeriodDaysByTime(param.getPeriodSTime(), param.getPeriodETime());
+            if (!periodIds.isEmpty()){
+                List<Long> longs = userCoursePeriodService.selectFsUserCoursePeriodListByPeriodId(periodIds, loginUser.getCompany().getCompanyId());
+                if (!longs.isEmpty()){
+                    param.setPeriodIds(longs);
+                }else {
+                    return getDataTable(new ArrayList<>());
+                }
+            }else {
+                return getDataTable(new ArrayList<>());
+            }
+
+        }
+
+        startPage();
+
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
         return getDataTable(list);
     }

+ 32 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -14,8 +14,10 @@ import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.impl.CompanyDeptServiceImpl;
 import com.fs.course.param.FsUserCourseListUParam;
+import com.fs.course.service.IFsUserCompanyBindService;
 import com.fs.course.service.IFsUserCourseStudyService;
 import com.fs.course.vo.FsUserCourseStudyListUVO;
+import com.fs.course.vo.UserWatchLogListVo;
 import com.fs.crm.param.CrmMyCustomerListQueryParam;
 import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.vo.CrmMyCustomerListQueryVO;
@@ -92,6 +94,9 @@ public class QwExternalContactController extends BaseController
     @Autowired
     private IQwContactWayService qwContactWayService;
 
+    @Autowired
+    private IFsUserCompanyBindService fsUserCompanyBindService;
+
     /**
      * 查询企业微信客户列表
      */
@@ -584,6 +589,14 @@ public class QwExternalContactController extends BaseController
     {
         return qwExternalContactService.updateQwExternalContactUnBindUserId(id);
     }
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:changeStatus')")
+    @Log(title = "修改用户状态", businessType = BusinessType.UPDATE)
+    @GetMapping("/status")
+    public R changeStatus(QwExternalContact qwExternalContact)
+    {
+        qwExternalContactService.updateQwExternalContactStatusById(qwExternalContact);
+        return R.ok();
+    }
     /**
      * 删除企业微信客户
      */
@@ -801,4 +814,23 @@ public class QwExternalContactController extends BaseController
         return R.ok();
     }
 
+    /**
+     * 重粉客服查询
+     */
+    @GetMapping("/getRepeat")
+    public R getRepeat(RepeatParam param){
+        return  qwExternalContactService.getRepeat(param);
+    }
+    /**
+     * 重粉看课记录查询
+     */
+    @GetMapping("/getWatchLogList")
+    public TableDataInfo getWatchLogList(UserWatchLogParam param){
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setUserId(loginUser.getUser().getUserId());
+        List<UserWatchLogListVo> list= fsUserCompanyBindService.getWatchLogList(param);
+        return getDataTable(list);
+    }
+
 }

+ 4 - 2
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferLogController.java

@@ -116,8 +116,10 @@ public class QwExternalContactTransferLogController extends BaseController
     @GetMapping("/sync/{corpId}")
     public R syncTag(@PathVariable("corpId") String corpId)
     {
-
-        return qwExternalContactTransferLogService.syncQwExternalContactTransferLog(corpId);
+//        transferService.syncQwExternalContactTransferLog(corpId);
+//
+         qwExternalContactTransferLogService.syncQwExternalContactTransferLog(corpId);
+        return R.ok();
     }
     /**
      * 获取转接记录详细信息

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

@@ -449,6 +449,23 @@ public class QwUserController extends BaseController
         ExcelUtil<QwUser> util = new ExcelUtil<QwUser>(QwUser.class);
         return util.exportExcel(list, "企微用户数据");
     }
+
+    /**
+     * 查询企微用户列表-下拉框
+     */
+    @GetMapping("/qwList")
+    public TableDataInfo getQwList(QwUser qwUser)
+    {
+        startPage();
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+
+        if(ObjectUtil.notEqual(qwUser.getDisableCompanyId(),1)){
+            qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+        }
+        List<QwUser> list = qwUserService.selectQwUserList(qwUser);
+        return getDataTable(list);
+    }
     /**
     * 查询企微员工列表-用于员工管理绑定
     */

+ 23 - 7
fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -35,10 +35,7 @@ import com.fs.hisStore.param.FsStoreOrderCreateUserParam;
 import com.fs.hisStore.param.FsStoreOrderFinishParam;
 import com.fs.hisStore.param.FsStoreOrderParam;
 import com.fs.hisStore.service.*;
-import com.fs.hisStore.vo.FsStoreOrderAuditLogVO;
-import com.fs.hisStore.vo.FsStoreOrderExportVO;
-import com.fs.hisStore.vo.FsStoreOrderItemExportVO;
-import com.fs.hisStore.vo.FsStoreOrderVO;
+import com.fs.hisStore.vo.*;
 import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -47,6 +44,8 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -171,7 +170,7 @@ public class FsStoreOrderScrmController extends BaseController
         if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
             param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
         }
-        List<FsStoreOrderExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
+        List<FsStoreOrderErpExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
         //对手机号脱敏
         if(list!=null){
             for(FsStoreOrderExportVO vo:list){
@@ -187,8 +186,25 @@ public class FsStoreOrderScrmController extends BaseController
 
             }
         }
-        ExcelUtil<FsStoreOrderExportVO> util = new ExcelUtil<FsStoreOrderExportVO>(FsStoreOrderExportVO.class);
-        return util.exportExcel(list,"订单数据");
+
+        String filter = param.getFilter();
+        // 1. 处理filter参数:将逗号分隔的字符串拆分为ArrayList<String>
+        ArrayList<String> filterList = new ArrayList<>();
+        if (StringUtils.isNotBlank(filter)) {
+            // 按逗号拆分,同时去除可能的空格(如filter传"orderId, orderCode"时兼容)
+            String[] filterArr = filter.split("\\s*,\\s*");
+            filterList.addAll(Arrays.asList(filterArr));
+        }
+        // 动态导出:根据选中的字段生成Excel
+        ExcelUtil<FsStoreOrderErpExportVO> util = new ExcelUtil<FsStoreOrderErpExportVO>(FsStoreOrderErpExportVO.class);
+        AjaxResult result;
+        // 如果有选中的字段,只导出这些字段
+        if (filter != null && !filter.isEmpty()) {
+            return util.exportExcelSelectedColumns(list, "订单数据", filterList);
+        } else {
+            // 导出所有字段
+            return util.exportExcel(list, "订单数据");
+        }
     }
 
 

+ 9 - 1
fs-qw-api/src/main/java/com/fs/app/service/QwDataCallbackService.java

@@ -20,6 +20,7 @@ import com.fs.qwApi.domain.QwResult;
 import com.fs.qwApi.param.QwEditUserTagParam;
 import com.fs.qwApi.param.QwOpenidByUserParams;
 import com.fs.qwApi.util.AesException;
+import com.fs.voice.utils.StringUtil;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.tencent.wework.Finance;
@@ -43,6 +44,7 @@ import java.security.PrivateKey;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 
 @Service
 @Slf4j
@@ -200,7 +202,13 @@ public class QwDataCallbackService {
                             if(WelcomeCodeList.getLength() > 0) {
                                 WelcomeCode = WelcomeCodeList.item(0).getTextContent();
                             }
-                            qwExternalContactService.insertQwExternalContactByExternalUserId(root.getElementsByTagName("ExternalUserID").item(0).getTextContent(),root.getElementsByTagName("UserID").item(0).getTextContent(),null,corpId,State,WelcomeCode);
+
+                            String qwApiExternal=redisCache.getCacheObject("qwApiExternal:"+root.getElementsByTagName("UserID").item(0).getTextContent()+":"+corpId+":"+root.getElementsByTagName("ExternalUserID").item(0).getTextContent());
+                            if (StringUtil.strIsNullOrEmpty(qwApiExternal)){
+                                redisCache.setCacheObject("qwApiExternal:"+root.getElementsByTagName("UserID").item(0).getTextContent()+":"+corpId+":"+root.getElementsByTagName("ExternalUserID").item(0).getTextContent() ,"1",10, TimeUnit.MINUTES);
+                                qwExternalContactService.insertQwExternalContactByExternalUserId(root.getElementsByTagName("ExternalUserID").item(0).getTextContent(),root.getElementsByTagName("UserID").item(0).getTextContent(),null,corpId,State,WelcomeCode);
+
+                            }
                             break;
                         case "edit_external_contact":
                             qwExternalContactService.updateQwExternalContactByExternalUserId(root.getElementsByTagName("ExternalUserID").item(0).getTextContent(),root.getElementsByTagName("UserID").item(0).getTextContent(),corpId);

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

@@ -31,6 +31,7 @@ import com.fs.sop.service.*;
 import com.fs.sop.vo.QwSopLogsDoSendListTVO;
 import com.fs.store.service.IFsUserCourseCountService;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -123,6 +124,16 @@ public class CommonController {
     private SyncQwExternalContactService syncQwExternalContactService;
 
 
+
+    /**
+     * 获取跳转微信小程序的链接地址
+     */
+    @GetMapping("/getGotoWxAppLink")
+    @ApiOperation("获取跳转微信小程序的链接地址")
+    public ResponseResult<String> getGotoWxAppLink(String linkStr,String appid) {
+        return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
+    }
+
     /**
     * 发官方通连
     */

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

@@ -20,6 +20,7 @@ import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
 import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCompanyBindService;
 import com.fs.qw.domain.*;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
@@ -76,6 +77,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private static final String miniappRealLink = "/pages_course/video.html?course=";
     private static final String appRealLink = "/pages/courseAnswer/index?link=";
     private static final String appLink = "https://jump.ylrztop.com/jumpapp/pages/index/index?link=";
+
 //    private static final String miniappRealLink = "/pages/index/index?course=";
 
     private static final String QWSOP_KEY_PREFIX = "qwsop:";
@@ -176,6 +178,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     @Autowired
     private AsyncCourseWatchFinishService asyncCourseWatchFinishService;
 
+    @Autowired
+    private IFsUserCompanyBindService fsUserCompanyBindService;
 
     @PostConstruct
     public void init() {
@@ -927,66 +931,66 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 //文字和短链一起
                 case "1":
                 case "3":
-                    if ("1".equals(setting.getIsBindUrl())) {
-                        String link;
-                        if (isGroupChat) {
-                            FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
-                            createParam.setCourseId(courseId);
-                            createParam.setVideoId(videoId);
-                            createParam.setCorpId(logVo.getCorpId());
-                            createParam.setCompanyUserId(Long.parseLong(companyUserId));
-                            createParam.setCompanyId(Long.parseLong(companyId));
-                            createParam.setChatId(logVo.getChatId());
-                            createParam.setQwUserId(Long.valueOf(qwUserId));
-                            createParam.setDays(setting.getExpiresDays());
-                            R createLink = courseLinkService.createRoomLinkUrl(createParam);
-                            if (createLink.get("code").equals(500)) {
-                                throw new BaseException("链接生成失败!");
-                            }
-                            try {
-                                groupChat.getChatUserList().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) {
-                                        sopLogs.setFsUserId(vo.getFsUserId());
-                                        addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, vo.getId().toString(), logVo);
-                                    }
-                                });
-                            } catch (Exception e) {
-                                log.error("群聊创建看课记录失败!", e);
-                            }
-                            link = (String) createLink.get("url");
-                        } else {
-                            addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo);
-                            link = generateShortLink(setting, logVo, sendTime, courseId, videoId,
-                                    qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId());
-                        }
+//                    if ("1".equals(setting.getIsBindUrl())) {
+//                        String link;
+//                        if (isGroupChat) {
+//                            FsCourseLinkCreateParam createParam = new FsCourseLinkCreateParam();
+//                            createParam.setCourseId(courseId);
+//                            createParam.setVideoId(videoId);
+//                            createParam.setCorpId(logVo.getCorpId());
+//                            createParam.setCompanyUserId(Long.parseLong(companyUserId));
+//                            createParam.setCompanyId(Long.parseLong(companyId));
+//                            createParam.setChatId(logVo.getChatId());
+//                            createParam.setQwUserId(Long.valueOf(qwUserId));
+//                            createParam.setDays(setting.getExpiresDays());
+//                            R createLink = courseLinkService.createRoomLinkUrl(createParam);
+//                            if (createLink.get("code").equals(500)) {
+//                                throw new BaseException("链接生成失败!");
+//                            }
+//                            try {
+//                                groupChat.getChatUserList().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) {
+//                                        sopLogs.setFsUserId(vo.getFsUserId());
+//                                        addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, vo.getId().toString(), logVo);
+//                                    }
+//                                });
+//                            } catch (Exception e) {
+//                                log.error("群聊创建看课记录失败!", e);
+//                            }
+//                            link = (String) createLink.get("url");
+//                        } else {
+//                            addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId, logVo);
+//                            link = generateShortLink(setting, logVo, sendTime, courseId, videoId,
+//                                    qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId());
+//                        }
 
-                        if (StringUtils.isNotEmpty(link)) {
-                            if ("3".equals(setting.getContentType())) {
-                                setting.setLinkUrl(link);
-                            } else {
-                                String currentValue = setting.getValue();
-                                if (currentValue == null) {
-                                    setting.setValue(link);
-                                } else {
-                                    setting.setValue(currentValue
-                                            .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
-                                            .replaceAll("#客户称呼#", contact == null || StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus())
-                                            + "\n" + link);
-                                }
-                            }
-                        } else {
-                            log.error("生成短链失败,跳过设置 URL。");
-                        }
+//                        if (StringUtils.isNotEmpty(link)) {
+//                            if ("3".equals(setting.getContentType())) {
+//                                setting.setLinkUrl(link);
+//                            } else {
+//                                String currentValue = setting.getValue();
+//                                if (currentValue == null) {
+//                                    setting.setValue(link);
+//                                } else {
+//                                    setting.setValue(currentValue
+//                                            .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
+//                                            .replaceAll("#客户称呼#", contact == null || StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus())
+//                                            + "\n" + link);
+//                                }
+//                            }
+//                        } else {
+//                            log.error("生成短链失败,跳过设置 URL。");
+//                        }
 
-                    } else {
+//                    } else {
                         if ("1".equals(setting.getContentType())) {
                             setting.setValue(setting.getValue()
                                     .replaceAll("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
                                     .replaceAll("#客户称呼#", contact == null || StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus()));
                         }
-                    }
+//                    }
                     break;
                 //小程序单独
                 case "4":
@@ -1909,6 +1913,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 } else {
                     log.info("完课消息-客户信息有误,不生成完课消息: {}", finishLog.getQwExternalContactId());
                 }
+                try {
+                    fsUserCompanyBindService.finish(externalContact.getFsUserId(), externalContact.getQwUserId(), externalContact.getCompanyUserId(), finishLog);
+                }catch (Exception e){
+                    log.error("更新重粉看课状态失败",e);
+                }
             } catch (Exception e) {
                 log.error("处理完课记录失败: {}", finishLog.getLogId(), e);
             }
@@ -1959,6 +1968,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setExternalUserId(externalContact.getExternalUserId());
         sopLogs.setExternalUserName(externalContact.getName());
         sopLogs.setFsUserId(finishLog.getUserId() != null ? finishLog.getUserId() : null );
+        sopLogs.setExternalId(finishLog.getQwExternalContactId());
         sopLogs.setUserLogsId("-");
 
         sopLogs.setQwUserKey(finishLog.getQwUserId() != null ? finishLog.getQwUserId() : null);
@@ -1968,7 +1978,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         if (settings == null) {
             return null;
         }
-
+        //完课后若是小程序发送另外一堂课
+        saveWacthLogOfCourseLink(settings,sopLogs,newTimeString,finishLog,finishTemp);
         // 处理音频内容
 //        for (QwSopCourseFinishTempSetting.Setting st : settings) {
 //            if (st.getContentType().equals("7")) {
@@ -1987,6 +1998,234 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         return sopLogs;
     }
 
+    /**
+     * 判定小程序的话新增创建看课记录,以及fsCourseLink
+     *
+     * @param settings
+     */
+    public void saveWacthLogOfCourseLink(List<QwSopCourseFinishTempSetting.Setting> settings, QwSopLogs sopLogs,  String newTimeString, FsCourseWatchLog finishLog, FsCourseFinishTemp finishTemp){
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSON.parseObject(json, CourseConfig.class);
+        Date dataTime = new Date();
+        List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
+        Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
+
+        QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(sopLogs.getCorpId());
+        QwUser qwUser = qwExternalContactService.getQwUserByRedis(sopLogs.getCorpId(), sopLogs.getQwUserid());
+        if (qwUser == null){
+            return;
+        }
+        for (QwSopCourseFinishTempSetting.Setting st : settings) {
+            switch (st.getContentType()) {
+                //小程序单独
+                case "4":
+                    addWatchLogIfNeeded(sopLogs.getSopId(), st.getVideoId().intValue(), st.getCourseId().intValue(), sopLogs.getFsUserId(),  String.valueOf(qwUser.getId()),qwUser.getCompanyUserId().toString(), qwUser.getCompanyId().toString(),
+                            sopLogs.getExternalId(), newTimeString.substring(0, 10), dataTime);
+
+                    String linkByMiniApp = createLinkByMiniApp(st, sopLogs.getCorpId(), dataTime, finishTemp.getCourseId().intValue(), Integer.valueOf(st.getVideoId().toString()),
+                            String.valueOf(qwUser.getId()), qwUser.getCompanyUserId().toString(), qwUser.getCompanyId().toString(), sopLogs.getExternalId(), config);
+
+
+                    String miniAppId = null;
+                    if (!miniMap.isEmpty() && qwUser.getSendMsgType() == 1) {
+                        Map<Integer, List<CompanyMiniapp>> integerListMap = miniMap.get(Long.valueOf(qwUser.getCompanyId()));
+                        if (integerListMap != null) {
+                            int listIndex = 0;
+                            List<CompanyMiniapp> miniapps = integerListMap.get(listIndex);
+
+                            if (miniapps != null && !miniapps.isEmpty()) {
+                                CompanyMiniapp companyMiniapp = miniapps.get(0);
+                                if (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId())) {
+                                    miniAppId = companyMiniapp.getAppId();
+                                }
+                            }
+                        }
+                    }
+
+                    if (StringUtil.strIsNullOrEmpty(miniAppId) && !StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())) {
+                        miniAppId = qwCompany.getMiniAppId();
+                    }
+
+                    if (!StringUtil.strIsNullOrEmpty(miniAppId)) {
+                        st.setMiniprogramAppid(miniAppId);
+                    } else {
+                        log.error("企业未配置小程序-" + sopLogs.getCorpId());
+                    }
+
+                    String miniprogramTitle = st.getMiniprogramTitle();
+                    int maxLength = 17;
+                    st.setMiniprogramTitle(miniprogramTitle.length() > maxLength ? miniprogramTitle.substring(0, maxLength)+"..." : miniprogramTitle);
+                    st.setMiniprogramPage(linkByMiniApp);
+                    break;
+                default:
+                    break;
+
+            }
+        }
+    }
+    private Date processDate(String sendTimeParam) {
+        // 1. 获取当前日期(年月日)
+        LocalDate currentDate = LocalDate.now();
+
+        // 2. 解析传入的时分(支持 "HH:mm" 或 "H:mm")
+        LocalTime sendTime = LocalTime.parse(sendTimeParam);
+
+        // 3. 合并为 LocalDateTime
+        LocalDateTime dateTime = LocalDateTime.of(currentDate, sendTime);
+
+        // 4. 转换为 Date(需通过 Instant 和系统默认时区)
+        Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
+
+        return date;
+    }
+
+    /**
+     * 新增courseLink
+     *
+     * @param setting
+     * @param corpId
+     * @param sendTime
+     * @param courseId
+     * @param videoId
+     * @param qwUserId
+     * @param companyUserId
+     * @param companyId
+     * @param externalId
+     * @param config
+     * @return
+     */
+    private String createLinkByMiniApp(QwSopCourseFinishTempSetting.Setting setting, String corpId, Date sendTime,
+                                       Integer courseId, Integer videoId, String qwUserId,
+                                       String companyUserId, String companyId, Long externalId, CourseConfig config) {
+
+        FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, qwUserId,
+                companyUserId, companyId, externalId, 3, null);
+
+        FsCourseRealLink courseMap = new FsCourseRealLink();
+        BeanUtils.copyProperties(link, courseMap);
+
+        String courseJson = JSON.toJSONString(courseMap);
+        String realLinkFull = miniappRealLink + courseJson;
+        link.setRealLink(realLinkFull);
+
+        Date updateTime = createUpdateTime(setting, sendTime, config);
+
+        link.setUpdateTime(updateTime);
+        //存短链-
+        fsCourseLinkMapper.insertFsCourseLink(link);
+        return link.getRealLink();
+    }
+
+    /**
+     * 创建courselink
+     * @param corpId
+     * @param sendTime
+     * @param courseId
+     * @param videoId
+     * @param qwUserId
+     * @param companyUserId
+     * @param companyId
+     * @param externalId
+     * @param type
+     * @param chatId
+     * @return
+     */
+    public FsCourseLink createFsCourseLink(String corpId, Date sendTime, Integer courseId, Integer videoId, String qwUserId,
+                                           String companyUserId, String companyId, Long externalId, Integer type, String chatId) {
+        // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
+        FsCourseLink link = new FsCourseLink();
+        link.setCompanyId(Long.parseLong(companyId));
+        link.setQwUserId(Long.valueOf(qwUserId));
+        link.setCompanyUserId(Long.parseLong(companyUserId));
+        link.setVideoId(videoId.longValue());
+        link.setCorpId(corpId);
+        link.setCourseId(courseId.longValue());
+        link.setChatId(chatId);
+        link.setQwExternalId(externalId);
+        link.setLinkType(type); //小程序
+        link.setUNo(UUID.randomUUID().toString());
+        link.setProjectCode(cloudHostProper.getProjectCode());
+        String randomString = generateRandomStringWithLock();
+        if (StringUtil.strIsNullOrEmpty(randomString)) {
+            link.setLink(UUID.randomUUID().toString().replace("-", ""));
+        } else {
+            link.setLink(randomString);
+        }
+
+        link.setCreateTime(sendTime);
+
+        return link;
+    }
+
+
+    /**
+     * 计算过期时间
+     * @param setting
+     * @param sendTime
+     * @param config
+     * @return
+     */
+    private Date createUpdateTime(QwSopCourseFinishTempSetting.Setting setting, Date sendTime, CourseConfig config) {
+
+        Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0)
+                ? config.getVideoLinkExpireDate()
+                : setting.getExpiresDays();
+
+//         使用 Java 8 时间 API 计算过期时间
+        LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays - 1);
+        expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
+        Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
+
+        return updateTime;
+    }
+
+    /**
+     * 增加看课记录
+     *
+     * @param sopId
+     * @param videoId
+     * @param courseId
+     * @param fsUserId
+     * @param qwUserId
+     * @param companyUserId
+     * @param companyId
+     * @param externalId
+     * @param startTime
+     * @param createTime
+     * @return
+     */
+    private Long addWatchLogIfNeeded(String sopId, Integer videoId, Integer courseId,
+                                     Long fsUserId, String qwUserId, String companyUserId,
+                                     String companyId, Long externalId, String startTime, Date createTime) {
+
+        try {
+            FsCourseWatchLog watchLog = new FsCourseWatchLog();
+            watchLog.setVideoId(videoId != null ? videoId.longValue() : null);
+            watchLog.setQwExternalContactId(externalId);
+            watchLog.setSendType(2);
+            watchLog.setQwUserId(Long.valueOf(qwUserId));
+            watchLog.setSopId(sopId);
+            watchLog.setDuration(0L);
+            watchLog.setCourseId(courseId != null ? courseId.longValue() : null);
+            watchLog.setCompanyUserId(companyUserId != null ? Long.valueOf(companyUserId) : null);
+            watchLog.setCompanyId(companyId != null ? Long.valueOf(companyId) : null);
+            watchLog.setCreateTime(createTime);
+            watchLog.setUpdateTime(createTime);
+            watchLog.setLogType(3);
+            watchLog.setUserId(fsUserId);
+            watchLog.setCampPeriodTime(convertStringToDate(startTime, "yyyy-MM-dd"));
+
+            //存看课记录
+            int i = fsCourseWatchLogMapper.insertOrUpdateFsCourseWatchLog(watchLog);
+            return watchLog.getLogId();
+        } catch (Exception e) {
+            log.error("插入观看记录失败:" + e.getMessage());
+            return null;
+        }
+    }
+
+
     /**
      * 解析模板设置
      */

+ 43 - 2
fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisQwSopController.java

@@ -2,12 +2,17 @@ package com.fs.app.controller;
 
 import com.fs.app.params.SopLogsEditParam;
 import com.fs.common.BeanCopyUtils;
+import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
+import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
 import com.fs.fastGpt.param.FastGptChatSessionParam;
 import com.fs.fastGpt.service.IFastGptChatSessionService;
 import com.fs.qw.domain.QwTagGroup;
-import com.fs.qw.param.SopMsgParam;
+import com.fs.qw.param.*;
 import com.fs.qw.param.sidebar.ExternalContactInfoParam;
 import com.fs.qw.param.sidebar.TagGroupListParam;
 import com.fs.qw.param.sidebar.TagGroupUpdateParam;
@@ -21,6 +26,9 @@ import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.params.GetQwSopLogsByJsApiParam;
 import com.fs.sop.params.SendSopParamDetailsC;
 import com.fs.sop.service.IQwSopLogsService;
+import com.fs.sop.service.IQwSopService;
+import com.fs.store.vo.h5.ExternalUserStatsVO;
+import com.fs.store.vo.h5.FsUserStatisticsVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
@@ -28,13 +36,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.text.SimpleDateFormat;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
 
 @RestController
 @RequestMapping("/apis/app/qwSop")
-public class ApisQwSopController {
+public class ApisQwSopController  extends BaseController {
 
     @Autowired
     RedisCache redisCache;
@@ -49,6 +58,8 @@ public class ApisQwSopController {
     @Autowired
     private IQwTagGroupService qwTagGroupService;
 
+    @Autowired
+    private IQwSopService qwSopService;
     /**
      * 更新AI发送状态
      */
@@ -162,4 +173,34 @@ public class ApisQwSopController {
         return R.error();
     }
 
+    //员工看板 课程/答题/红包统计--侧边栏
+    @GetMapping("/boardCourseQuizRedEnvelopeStats")
+    @ApiOperation("员工看板 课程/答题/红包统计")
+    public ResponseResult<FsUserStatisticsVO> boardCourseQuizRedEnvelopeStats(CourseQuizRedEnvelopeStatsParam qwParam) {
+        if (StringUtils.isBlank(qwParam.getStartTime()) || StringUtils.isBlank(qwParam.getEndTime())) {
+            return ResponseResult.ok(new FsUserStatisticsVO());
+        }
+        FsUserStatisticsVO resultVo = qwSopService.boardCourseQuizRedEnvelopeStats(qwParam);
+        return ResponseResult.ok(resultVo);
+    }
+
+    //外部联系人答题/红包/看课统计--侧边栏
+    @GetMapping("/externalStatsList")
+    @ApiOperation("外部联系人答题/红包/看课统计")
+    public ResponseResult<ExternalUserStatsVO> externalStatsList(QwSidebarStatsParam qwParam) {
+        ExternalUserStatsVO vo = qwSopService.externalStatsList(qwParam);
+        return ResponseResult.ok(vo);
+    }
+
+    //外部联系人看课轨迹--侧边栏
+    @GetMapping("/externalWatchRecordStatsList")
+    @ApiOperation("用户看课轨迹")
+    public TableDataInfo externalWatchRecordStatsList(QwSidebarStatsParam qwParam) {
+        if (qwParam.getStartTime() == null || qwParam.getEndTime() == null) {
+            return getDataTable(Collections.emptyList());
+        }
+        List<FsCourseWatchLogStatisticsListVO> list = qwSopService.externalWatchRecordStatsList(qwParam);
+        return getDataTable(list);
+    }
+
 }

+ 48 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDivConfig.java

@@ -0,0 +1,48 @@
+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_div_config
+ *
+ * @author fs
+ * @date 2025-10-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDivConfig extends BaseEntity{
+
+    /** 公司id */
+    @TableId
+    private Long companyId;
+
+    /** 公司名称 */
+    @Excel(name = "公司名称")
+    private String companyName;
+
+    /** 是否开启分账 0:否 1:是 */
+    @Excel(name = "是否开启分账 0:否 1:是")
+    private Integer divFlag;
+
+    /** 分账模式 Y:延迟分账 N:实时分账 */
+    @Excel(name = "分账模式 Y:延迟分账 N:实时分账")
+    private String delayAcctFlag;
+
+    /** 是否使用百分比分账 */
+    @Excel(name = "是否使用百分比分账")
+    private String percentageFlag;
+
+    /** 是否净值分账 */
+    @Excel(name = "是否净值分账")
+    private String isCleanSplit;
+
+    /** 分账对象 */
+    @Excel(name = "分账对象")
+    private String acctInfos;
+
+
+}

+ 55 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyDivItem.java

@@ -0,0 +1,55 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 分账明细对象 company_div_item
+ *
+ * @author fs
+ * @date 2025-10-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyDivItem extends BaseEntity{
+
+    @TableId
+    private Long id;
+
+//    /** 订单id */
+//    @Excel(name = "订单id")
+//    private Long orderId;
+
+    /** 订单编号 */
+    @Excel(name = "订单编号")
+    private String orderCode;
+
+    /** 支付订单号 */
+    @Excel(name = "支付订单号")
+    private String payCode;
+
+    /** 分账明细 */
+    @Excel(name = "分账明细")
+    private String detail;
+
+    /** 分账明细 */
+    @Excel(name = "退款明细")
+    private String refundDetail;
+
+    /** 分账明细 */
+    @Excel(name = "是否支付成功 0否 1是")
+    private Integer isPay;
+
+    /** 是否延迟分账 0否 1是 */
+    @Excel(name = "是否延迟分账 0否 1是")
+    private Integer isDelay;
+
+    /** 是否退款 0否 1是 */
+    @Excel(name = "是否退款 0否 1是")
+    private Integer isRefund;
+
+
+}

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

@@ -0,0 +1,61 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDivConfig;
+
+/**
+ * 公司分账配置Mapper接口
+ *
+ * @author fs
+ * @date 2025-10-20
+ */
+public interface CompanyDivConfigMapper extends BaseMapper<CompanyDivConfig>{
+    /**
+     * 查询公司分账配置
+     *
+     * @param companyId 公司分账配置主键
+     * @return 公司分账配置
+     */
+    CompanyDivConfig selectCompanyDivConfigByCompanyId(Long companyId);
+
+    /**
+     * 查询公司分账配置列表
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 公司分账配置集合
+     */
+    List<CompanyDivConfig> selectCompanyDivConfigList(CompanyDivConfig companyDivConfig);
+
+    /**
+     * 新增公司分账配置
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 结果
+     */
+    int insertCompanyDivConfig(CompanyDivConfig companyDivConfig);
+
+    /**
+     * 修改公司分账配置
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 结果
+     */
+    int updateCompanyDivConfig(CompanyDivConfig companyDivConfig);
+
+    /**
+     * 删除公司分账配置
+     *
+     * @param companyId 公司分账配置主键
+     * @return 结果
+     */
+    int deleteCompanyDivConfigByCompanyId(Long companyId);
+
+    /**
+     * 批量删除公司分账配置
+     *
+     * @param companyIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDivConfigByCompanyIds(Long[] companyIds);
+}

+ 65 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDivItemMapper.java

@@ -0,0 +1,65 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyDivItem;
+
+/**
+ * 分账明细Mapper接口
+ *
+ * @author fs
+ * @date 2025-10-21
+ */
+public interface CompanyDivItemMapper extends BaseMapper<CompanyDivItem>{
+    /**
+     * 查询分账明细
+     *
+     * @param id 分账明细主键
+     * @return 分账明细
+     */
+    CompanyDivItem selectCompanyDivItemById(Long id);
+
+    /**
+     * 查询分账明细列表
+     *
+     * @param companyDivItem 分账明细
+     * @return 分账明细集合
+     */
+    List<CompanyDivItem> selectCompanyDivItemList(CompanyDivItem companyDivItem);
+
+    /**
+     * 新增分账明细
+     *
+     * @param companyDivItem 分账明细
+     * @return 结果
+     */
+    int insertCompanyDivItem(CompanyDivItem companyDivItem);
+
+    /**
+     * 修改分账明细
+     *
+     * @param companyDivItem 分账明细
+     * @return 结果
+     */
+    int updateCompanyDivItem(CompanyDivItem companyDivItem);
+
+    /**
+     * 删除分账明细
+     *
+     * @param id 分账明细主键
+     * @return 结果
+     */
+    int deleteCompanyDivItemById(Long id);
+
+    /**
+     * 批量删除分账明细
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyDivItemByIds(Long[] ids);
+
+    CompanyDivItem selectCompanyDivItemByOrderCode(String orderCode);
+
+    CompanyDivItem selectCompanyDivItemByPayCode(String payCode);
+}

+ 19 - 0
fs-service/src/main/java/com/fs/company/param/CompanyAcctInfo.java

@@ -0,0 +1,19 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+@Data
+public class CompanyAcctInfo {
+    //分账金额
+    private BigDecimal divAmt;
+    //分账接收方ID
+    @NotNull(message = "分账接收方ID不能为空")
+    private String huifuId;
+    //	账户号
+    private String acctId;
+    //分账百分比%
+    private Float percentageDiv;
+}

+ 40 - 0
fs-service/src/main/java/com/fs/company/param/CompanyDivConfigUpdateParam.java

@@ -0,0 +1,40 @@
+package com.fs.company.param;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CompanyDivConfigUpdateParam {
+    /** 公司id */
+    @TableId
+    private Long companyId;
+
+    /** 公司名称 */
+    @Excel(name = "公司名称")
+    private String companyName;
+
+    /** 是否开启分账 0:否 1:是 */
+    @Excel(name = "是否开启分账 0:否 1:是")
+    private Integer divFlag;
+
+    /** 分账模式 Y:延迟分账 N:实时分账 */
+    @Excel(name = "分账模式 Y:延迟分账 N:实时分账")
+    private String delayAcctFlag;
+
+    /** 是否使用百分比分账 */
+    @Excel(name = "是否使用百分比分账")
+    private String percentageFlag;
+
+    /** 是否净值分账 */
+    @Excel(name = "是否净值分账")
+    private String isCleanSplit;
+
+    /** 分账对象 */
+    @Excel(name = "分账对象")
+    private List<CompanyAcctInfo> acctInfos;
+
+    private Integer isAdd; //是否新增 1:是
+}

+ 4 - 0
fs-service/src/main/java/com/fs/company/param/CompanyUserQwParam.java

@@ -115,4 +115,8 @@ public class CompanyUserQwParam extends BaseEntity {
     private Long qwUserId;
 
     private Integer qwStatus;
+
+    private Integer pageNum;
+
+    private Integer pageSize;
 }

+ 66 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDivConfigService.java

@@ -0,0 +1,66 @@
+package com.fs.company.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.company.domain.CompanyDivConfig;
+import com.fs.company.param.CompanyDivConfigUpdateParam;
+import com.fs.company.vo.CompanyDivConfigVo;
+
+/**
+ * 公司分账配置Service接口
+ *
+ * @author fs
+ * @date 2025-10-20
+ */
+public interface ICompanyDivConfigService extends IService<CompanyDivConfig>{
+    /**
+     * 查询公司分账配置
+     *
+     * @param companyId 公司分账配置主键
+     * @return 公司分账配置
+     */
+    CompanyDivConfigVo selectCompanyDivConfigByCompanyId(Long companyId);
+
+    /**
+     * 查询公司分账配置列表
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 公司分账配置集合
+     */
+    List<CompanyDivConfig> selectCompanyDivConfigList(CompanyDivConfig companyDivConfig);
+
+    /**
+     * 新增公司分账配置
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 结果
+     */
+    int insertCompanyDivConfig(CompanyDivConfig companyDivConfig);
+
+    /**
+     * 修改公司分账配置
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 结果
+     */
+    int updateCompanyDivConfig(CompanyDivConfig companyDivConfig);
+
+    /**
+     * 批量删除公司分账配置
+     *
+     * @param companyIds 需要删除的公司分账配置主键集合
+     * @return 结果
+     */
+    int deleteCompanyDivConfigByCompanyIds(Long[] companyIds);
+
+    /**
+     * 删除公司分账配置信息
+     *
+     * @param companyId 公司分账配置主键
+     * @return 结果
+     */
+    int deleteCompanyDivConfigByCompanyId(Long companyId);
+
+    R setDiv(CompanyDivConfigUpdateParam param);
+}

+ 65 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyDivItemService.java

@@ -0,0 +1,65 @@
+package com.fs.company.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyDivItem;
+
+/**
+ * 分账明细Service接口
+ *
+ * @author fs
+ * @date 2025-10-21
+ */
+public interface ICompanyDivItemService extends IService<CompanyDivItem>{
+    /**
+     * 查询分账明细
+     *
+     * @param id 分账明细主键
+     * @return 分账明细
+     */
+    CompanyDivItem selectCompanyDivItemById(Long id);
+
+    /**
+     * 查询分账明细列表
+     *
+     * @param companyDivItem 分账明细
+     * @return 分账明细集合
+     */
+    List<CompanyDivItem> selectCompanyDivItemList(CompanyDivItem companyDivItem);
+
+    /**
+     * 新增分账明细
+     *
+     * @param companyDivItem 分账明细
+     * @return 结果
+     */
+    int insertCompanyDivItem(CompanyDivItem companyDivItem);
+
+    /**
+     * 修改分账明细
+     *
+     * @param companyDivItem 分账明细
+     * @return 结果
+     */
+    int updateCompanyDivItem(CompanyDivItem companyDivItem);
+
+    /**
+     * 批量删除分账明细
+     *
+     * @param ids 需要删除的分账明细主键集合
+     * @return 结果
+     */
+    int deleteCompanyDivItemByIds(Long[] ids);
+
+    /**
+     * 删除分账明细信息
+     *
+     * @param id 分账明细主键
+     * @return 结果
+     */
+    int deleteCompanyDivItemById(Long id);
+
+    CompanyDivItem selectCompanyDivItemByOrderCode(String orderCode);
+
+    CompanyDivItem selectCompanyDivItemByPayCode(String payCode);
+}

+ 179 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDivConfigServiceImpl.java

@@ -0,0 +1,179 @@
+package com.fs.company.service.impl;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyDivConfig;
+import com.fs.company.mapper.CompanyDivConfigMapper;
+import com.fs.company.param.CompanyAcctInfo;
+import com.fs.company.param.CompanyDivConfigUpdateParam;
+import com.fs.company.service.ICompanyDivConfigService;
+import com.fs.company.vo.CompanyDivConfigVo;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 公司分账配置Service业务层处理
+ *
+ * @author fs
+ * @date 2025-10-20
+ */
+@Service
+public class CompanyDivConfigServiceImpl extends ServiceImpl<CompanyDivConfigMapper, CompanyDivConfig> implements ICompanyDivConfigService {
+    @Autowired
+    private CompanyDivConfigMapper companyDivConfigMapper;
+
+    /**
+     * 查询公司分账配置
+     *
+     * @param companyId 公司分账配置主键
+     * @return 公司分账配置
+     */
+    @Override
+    public CompanyDivConfigVo selectCompanyDivConfigByCompanyId(Long companyId)
+    {
+        CompanyDivConfig companyDivConfig = baseMapper.selectCompanyDivConfigByCompanyId(companyId);
+        if (companyDivConfig == null){
+            return null;
+        }
+        CompanyDivConfigVo vo = new CompanyDivConfigVo();
+        BeanUtils.copyProperties(companyDivConfig, vo);
+        String acctInfosStr = companyDivConfig.getAcctInfos();
+        if (StringUtils.isNotBlank(acctInfosStr)){
+            List<CompanyAcctInfo> acctInfos = JSON.parseArray(acctInfosStr, CompanyAcctInfo.class);
+            vo.setAcctInfos(acctInfos);
+        }
+        return vo;
+    }
+
+    /**
+     * 查询公司分账配置列表
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 公司分账配置
+     */
+    @Override
+    public List<CompanyDivConfig> selectCompanyDivConfigList(CompanyDivConfig companyDivConfig)
+    {
+        return baseMapper.selectCompanyDivConfigList(companyDivConfig);
+    }
+
+    /**
+     * 新增公司分账配置
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyDivConfig(CompanyDivConfig companyDivConfig)
+    {
+        companyDivConfig.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDivConfig(companyDivConfig);
+    }
+
+    /**
+     * 修改公司分账配置
+     *
+     * @param companyDivConfig 公司分账配置
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyDivConfig(CompanyDivConfig companyDivConfig)
+    {
+        companyDivConfig.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyDivConfig(companyDivConfig);
+    }
+
+    /**
+     * 批量删除公司分账配置
+     *
+     * @param companyIds 需要删除的公司分账配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDivConfigByCompanyIds(Long[] companyIds)
+    {
+        return baseMapper.deleteCompanyDivConfigByCompanyIds(companyIds);
+    }
+
+    /**
+     * 删除公司分账配置信息
+     *
+     * @param companyId 公司分账配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDivConfigByCompanyId(Long companyId)
+    {
+        return baseMapper.deleteCompanyDivConfigByCompanyId(companyId);
+    }
+
+    @Override
+    public R setDiv(CompanyDivConfigUpdateParam param) {
+        if (param.getCompanyId() == null || StringUtils.isBlank(param.getCompanyName())){
+            return R.error("公司信息不能为空!");
+        }
+        Integer divFlag = param.getDivFlag();
+        String acctInfosStr = null;
+        //分账账户
+        List<CompanyAcctInfo> acctInfos = param.getAcctInfos();
+        if (divFlag == null){
+            divFlag = 0;
+        } else if (divFlag == 1){
+            String delayAcctFlag = param.getDelayAcctFlag();
+            if (StringUtils.isBlank(delayAcctFlag)){
+                return R.error("分账模式不能为空!");
+            }
+
+            if (acctInfos == null || acctInfos.isEmpty()){
+                return R.error("分账接收方不能为空!");
+            }
+            acctInfosStr = JSON.toJSONString(acctInfos);
+        }
+        CompanyDivConfig companyDivConfig = new CompanyDivConfig();
+        BeanUtils.copyProperties(param, companyDivConfig);
+        companyDivConfig.setAcctInfos(acctInfosStr);
+        String percentageFlag = companyDivConfig.getPercentageFlag();
+        if (acctInfos != null && !acctInfos.isEmpty()){
+            if (StringUtils.isNotBlank(percentageFlag) && percentageFlag.equals("Y")){
+                if (StringUtils.isBlank(companyDivConfig.getIsCleanSplit())){
+                    companyDivConfig.setIsCleanSplit("N");
+                }
+            }
+            Float total = 0f;
+            for (CompanyAcctInfo acctInfo : acctInfos) {
+                if ("N".equals(percentageFlag)){
+                    if (BigDecimal.valueOf(0.01).compareTo(acctInfo.getDivAmt())>0) {
+                        return R.error("分账接收方:"+acctInfo.getHuifuId()+"分账金额必须大于0.01!");
+                    }
+                } else if ("Y".equals(percentageFlag)){
+                    if (acctInfo.getPercentageDiv()>100) {
+                        return R.error("分账接收方:"+acctInfo.getHuifuId()+"分账百分比不能大于100!");
+                    }
+                    total = total + acctInfo.getPercentageDiv();
+                    if (total>100){
+                        return R.error("分账接收方百分比之和不能大于100!");
+                    }
+                }
+            }
+        }
+        int flag = 0;
+        if (param.getIsAdd()!= null && param.getIsAdd() == 1){
+            //新增
+            companyDivConfig.setCreateTime(DateUtils.getNowDate());
+            flag = companyDivConfigMapper.insertCompanyDivConfig(companyDivConfig);
+        } else {
+            //更新
+            companyDivConfig.setUpdateTime(DateUtils.getNowDate());
+            flag = companyDivConfigMapper.updateCompanyDivConfig(companyDivConfig);
+        }
+
+        return flag>0?R.ok():R.error();
+    }
+}

+ 104 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyDivItemServiceImpl.java

@@ -0,0 +1,104 @@
+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 org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyDivItemMapper;
+import com.fs.company.domain.CompanyDivItem;
+import com.fs.company.service.ICompanyDivItemService;
+
+/**
+ * 分账明细Service业务层处理
+ *
+ * @author fs
+ * @date 2025-10-21
+ */
+@Service
+public class CompanyDivItemServiceImpl extends ServiceImpl<CompanyDivItemMapper, CompanyDivItem> implements ICompanyDivItemService {
+
+    /**
+     * 查询分账明细
+     *
+     * @param id 分账明细主键
+     * @return 分账明细
+     */
+    @Override
+    public CompanyDivItem selectCompanyDivItemById(Long id)
+    {
+        return baseMapper.selectCompanyDivItemById(id);
+    }
+
+    /**
+     * 查询分账明细列表
+     *
+     * @param companyDivItem 分账明细
+     * @return 分账明细
+     */
+    @Override
+    public List<CompanyDivItem> selectCompanyDivItemList(CompanyDivItem companyDivItem)
+    {
+        return baseMapper.selectCompanyDivItemList(companyDivItem);
+    }
+
+    /**
+     * 新增分账明细
+     *
+     * @param companyDivItem 分账明细
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyDivItem(CompanyDivItem companyDivItem)
+    {
+        companyDivItem.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyDivItem(companyDivItem);
+    }
+
+    /**
+     * 修改分账明细
+     *
+     * @param companyDivItem 分账明细
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyDivItem(CompanyDivItem companyDivItem)
+    {
+        companyDivItem.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyDivItem(companyDivItem);
+    }
+
+    /**
+     * 批量删除分账明细
+     *
+     * @param ids 需要删除的分账明细主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDivItemByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyDivItemByIds(ids);
+    }
+
+    /**
+     * 删除分账明细信息
+     *
+     * @param id 分账明细主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyDivItemById(Long id)
+    {
+        return baseMapper.deleteCompanyDivItemById(id);
+    }
+
+    @Override
+    public CompanyDivItem selectCompanyDivItemByOrderCode(String orderCode) {
+        return baseMapper.selectCompanyDivItemByOrderCode(orderCode);
+    }
+
+    @Override
+    public CompanyDivItem selectCompanyDivItemByPayCode(String payCode) {
+        return baseMapper.selectCompanyDivItemByPayCode(payCode);
+    }
+}

+ 11 - 9
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -369,15 +369,17 @@ public class CompanyServiceImpl implements ICompanyService
                 String json =configService.selectConfigByKey("his.store");
                 StoreConfig config= JSONUtil.toBean(json,StoreConfig.class);
                 //支付金额-(订单金额*rate%)
-                Double rate=config.getTuiMoneyRate()/100d;
-                BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
-                logger.info("写入公司推广佣金:"+tuiMoney);
-                company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
-                companyMapper.updateCompany(company);
-                FsStoreOrderScrm storeOrderMap=new FsStoreOrderScrm();
-                storeOrderMap.setId(order.getId());
-                storeOrderMap.setTuiMoney(tuiMoney);
-                storeOrderScrmMapper.updateFsStoreOrder(storeOrderMap);
+                if (config.getTuiMoneyRate()!=null){
+                    Double rate=config.getTuiMoneyRate()/100d;
+                    BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                    logger.info("写入公司推广佣金:"+tuiMoney);
+                    company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
+                    companyMapper.updateCompany(company);
+                    FsStoreOrderScrm storeOrderMap=new FsStoreOrderScrm();
+                    storeOrderMap.setId(order.getId());
+                    storeOrderMap.setTuiMoney(tuiMoney);
+                    storeOrderScrmMapper.updateFsStoreOrder(storeOrderMap);
+                }
             }
         }
     }

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

@@ -52,6 +52,7 @@ import com.fs.system.service.ISysRoleService;
 import com.fs.system.service.ISysUserService;
 import com.fs.voice.utils.StringUtil;
 import com.fs.wxUser.domain.CompanyWxUser;
+import com.github.pagehelper.PageHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -670,14 +671,13 @@ public class CompanyUserServiceImpl implements ICompanyUserService
 //        if(!StringUtils.isEmpty(companyConfig.getConfigValue())){
 //            isAdminShow = Boolean.parseBoolean(companyConfig.getConfigValue());
 //        }
-        List<CompanyUserQwListVO> companyUserQwListVOS = companyUserMapper.selectCompanyUserQwListVO(user);
-        if(!isAdminShow){
+        if(isAdminShow){
             Company company = companyService.selectCompanyById(user.getCompanyId());
             Long userId = company.getUserId();
-            companyUserQwListVOS = companyUserQwListVOS.stream()
-                    .filter(vo -> !vo.getUserId().equals(userId))
-                    .collect(Collectors.toList());
+            user.setUserId(userId);
         }
+        PageHelper.startPage(user.getPageNum(), user.getPageSize());
+        List<CompanyUserQwListVO> companyUserQwListVOS = companyUserMapper.selectCompanyUserQwListVO(user);
         return companyUserQwListVOS;
     }
 

+ 41 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyDivConfigVo.java

@@ -0,0 +1,41 @@
+package com.fs.company.vo;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import com.fs.company.param.CompanyAcctInfo;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CompanyDivConfigVo {
+    /** 公司id */
+    @TableId
+    private Long companyId;
+
+    /** 公司名称 */
+    @Excel(name = "公司名称")
+    private String companyName;
+
+    /** 是否开启分账 0:否 1:是 */
+    @Excel(name = "是否开启分账 0:否 1:是")
+    private Integer divFlag;
+
+    /** 分账模式 Y:延迟分账 N:实时分账 */
+    @Excel(name = "分账模式 Y:延迟分账 N:实时分账")
+    private String delayAcctFlag;
+
+    /** 是否使用百分比分账 */
+    @Excel(name = "是否使用百分比分账")
+    private String percentageFlag;
+
+    /** 是否净值分账 */
+    @Excel(name = "是否净值分账")
+    private String isCleanSplit;
+
+    /** 分账对象 */
+    @Excel(name = "分账对象")
+    private List<CompanyAcctInfo> acctInfos;
+
+    private Integer isAdd; //是否新增 1:是
+}

+ 76 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCompanyBind.java

@@ -0,0 +1,76 @@
+package com.fs.course.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * 用户客服关联对象 fs_user_company_bind
+ *
+ * @author fs
+ * @date 2025-07-22
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserCompanyBind extends BaseEntity{
+
+    /** id */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long fsUserId;
+
+    /** 客服id */
+    @Excel(name = "客服id")
+    private Long companyUserId;
+
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+
+    /** 课程项目ID */
+    @Excel(name = "课程项目ID")
+    private Long projectId;
+
+    /** 企微用户ID */
+    @Excel(name = "企微用户ID")
+    private Long qwUserId;
+
+    /** 企微外部联系人ID */
+    @Excel(name = "企微外部联系人ID")
+    private Long qwExternalContactId;
+
+    /** 企微主体ID */
+    @Excel(name = "企微主体ID")
+    private Long qwCompanyId;
+
+    /** 课程ID */
+    @Excel(name = "课程ID")
+    private Long courseId;
+
+    /** 视频ID */
+    @Excel(name = "视频ID")
+    private Long videoId;
+
+    /** 看课记录ID */
+    private Long watchLogId;
+
+    /** 完课状态 */
+    @Excel(name = "完课状态")
+    private Integer logType;
+
+    /** 完课时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "完课时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDateTime finishTime;
+
+
+}

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

@@ -7,12 +7,12 @@ import com.fs.course.param.*;
 import com.fs.course.vo.*;
 import com.fs.im.dto.OpenImBatchResponseDataDTO;
 import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.sop.vo.QwRatingVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
-import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -532,4 +532,11 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
      * 查询训练营看课人数
      */
     Integer getUserCountByCampId(@Param("trainingCampId") Long trainingCampId);
+
+    List<FsCourseWatchLogStatisticsListByCompanyVO> selectFsCourseWatchLogStatisticsListByCompanyVO(FsCourseWatchLogStatisticsListParam param);
+
+    /**
+     * 看课统计
+     * */
+    List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param);
 }

+ 66 - 0
fs-service/src/main/java/com/fs/course/mapper/FsUserCompanyBindMapper.java

@@ -0,0 +1,66 @@
+package com.fs.course.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsUserCompanyBind;
+import com.fs.course.vo.UserWatchLogListVo;
+import com.fs.qw.param.UserWatchLogParam;
+
+import java.util.List;
+
+/**
+ * 用户客服关联Mapper接口
+ *
+ * @author fs
+ * @date 2025-07-22
+ */
+public interface FsUserCompanyBindMapper extends BaseMapper<FsUserCompanyBind>{
+    /**
+     * 查询用户客服关联
+     *
+     * @param id 用户客服关联主键
+     * @return 用户客服关联
+     */
+    FsUserCompanyBind selectFsUserCompanyBindById(Long id);
+
+    /**
+     * 查询用户客服关联列表
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 用户客服关联集合
+     */
+    List<FsUserCompanyBind> selectFsUserCompanyBindList(FsUserCompanyBind fsUserCompanyBind);
+
+    /**
+     * 新增用户客服关联
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 结果
+     */
+    int insertFsUserCompanyBind(FsUserCompanyBind fsUserCompanyBind);
+
+    /**
+     * 修改用户客服关联
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 结果
+     */
+    int updateFsUserCompanyBind(FsUserCompanyBind fsUserCompanyBind);
+
+    /**
+     * 删除用户客服关联
+     *
+     * @param id 用户客服关联主键
+     * @return 结果
+     */
+    int deleteFsUserCompanyBindById(Long id);
+
+    /**
+     * 批量删除用户客服关联
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserCompanyBindByIds(Long[] ids);
+
+    List<UserWatchLogListVo> getWatchLogList(UserWatchLogParam param);
+}

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

@@ -5,7 +5,6 @@ import com.fs.course.param.CompanyRedPacketParam;
 import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import com.fs.course.vo.PeriodRedPacketVO;
-import org.apache.ibatis.annotations.MapKey;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -158,4 +157,17 @@ public interface FsUserCoursePeriodMapper
     Long setlectCorrectAnswerNum(PeriodStatisticCountParam param);
 
     List<FsUserCoursePeriod> selectFsPeriodlist(PeriodStatisticCountParam param);
+
+    @Select("select cp.period_id from fs_user_course_period cp left where company_id=#{companyId}")
+    List<Long> selectCoursePeriodDaysByTime(@Param("periodSTime") String periodSTime,@Param("periodETime") String periodETime,@Param("companyId") Long companyId);
+
+    @Select("<script>" +
+            "select period_id from fs_user_course_period where " +
+            "FIND_IN_SET(#{companyId}, company_id) > 0 " +
+            " AND period_id IN" +
+            "<foreach collection='periodIds' item='periodId' open='(' separator=',' close=')'> " +
+            "#{periodId} " +
+            "</foreach> " +
+            "</script> ")
+    List<Long> selectFsUserCoursePeriodListByPeriodId(@Param("periodIds") List<Long> qwUserIds,@Param("companyId") Long companyId);
 }

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

@@ -2,6 +2,7 @@ package com.fs.course.mapper;
 
 import com.fs.course.domain.FsUserCourseVideoRedPackage;
 import com.fs.course.param.FsUserCourseVideoParam;
+import org.apache.ibatis.annotations.Delete;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -100,6 +101,15 @@ public interface FsUserCourseVideoRedPackageMapper
             "</script>")
     int updateBatchDelFlag(@Param("ids") Long [] ids, @Param("delFlag") Integer delFlag);
 
+    @Delete("<script>" +
+            "DELETE FROM fs_user_course_video_red_package WHERE video_id IN " +
+            "<foreach collection='ids' item='id' open='(' separator=',' close=')'>" +
+            "#{id}" +
+            "</foreach>" +
+            "</script>")
+    int deleteBatchByVideoIds(@Param("ids") Long[] ids);
+
+
     Integer selectRedPacketByCompanyCount(@Param("videoId") Long videoId,@Param("companyId") Long companyId, @Param("periodId") Long periodId);
 
     void batchInsertOrUpdate(List<FsUserCourseVideoRedPackage> list);

+ 18 - 1
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java

@@ -54,12 +54,21 @@ public class FsCourseWatchLogListParam implements Serializable {
     @JsonFormat(pattern = "yyyy-MM-dd")
     private String qecETime;
 
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private String periodSTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private String periodETime;
+
     private List<String> sopIds;
 
 
     private Long taskId;//任务ID
     private Long project;//任务ID
 
+    private List<Long> periodIds;//训练营期ID
+
     private String customPageStr;
 
     private Long lastId;
@@ -74,11 +83,19 @@ public class FsCourseWatchLogListParam implements Serializable {
      */
     private String userType;
 
-    //是否是会员
+    /**
+     * 是否是会员
+     */
     private Integer isVip;
 
     /**
      * sop主键id
      */
     private String sopId;
+
+    /**
+     * 企微名称
+     */
+    private String qwUserName;
+
 }

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

@@ -3,15 +3,10 @@ package com.fs.course.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.param.*;
-import com.fs.course.vo.FsCourseOverVO;
-import com.fs.course.vo.FsCourseUserStatisticsListVO;
-import com.fs.course.vo.FsCourseWatchLogListVO;
-import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
-import com.fs.qw.param.QwWatchLogStatisticsListParam;
-import com.fs.qw.vo.QwWatchLogStatisticsListVO;
+import com.fs.course.vo.*;
+import com.fs.qw.param.QwSidebarStatsParam;
 
 import java.time.LocalDateTime;
-import java.time.LocalTime;
 import java.util.List;
 import java.util.Map;
 
@@ -92,6 +87,7 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
     void testFinishMsg();
 
     List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param);
+    List<FsCourseWatchLogStatisticsListByCompanyVO> selectFsCourseWatchLogStatisticsListByCompanyVO(FsCourseWatchLogStatisticsListParam param);
 
     void scheduleBatchUpdateToDatabase();
 
@@ -137,4 +133,9 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
     void scheduleBatchUpdateToDatabaseIsOpen();
 
     List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOexport(FsCourseWatchLogListParam param);
+
+    /**
+     * 看课统计
+     * */
+    List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param);
 }

+ 84 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCompanyBindService.java

@@ -0,0 +1,84 @@
+package com.fs.course.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.domain.FsUserCompanyBind;
+import com.fs.course.vo.UserWatchLogListVo;
+import com.fs.qw.param.UserWatchLogParam;
+
+import java.util.List;
+
+/**
+ * 用户客服关联Service接口
+ *
+ * @author fs
+ * @date 2025-07-22
+ */
+public interface IFsUserCompanyBindService extends IService<FsUserCompanyBind>{
+    /**
+     * 查询用户客服关联
+     *
+     * @param id 用户客服关联主键
+     * @return 用户客服关联
+     */
+    FsUserCompanyBind selectFsUserCompanyBindById(Long id);
+
+    /**
+     * 查询用户客服关联列表
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 用户客服关联集合
+     */
+    List<FsUserCompanyBind> selectFsUserCompanyBindList(FsUserCompanyBind fsUserCompanyBind);
+
+    /**
+     * 新增用户客服关联
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 结果
+     */
+    int insertFsUserCompanyBind(FsUserCompanyBind fsUserCompanyBind);
+
+    /**
+     * 修改用户客服关联
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 结果
+     */
+    int updateFsUserCompanyBind(FsUserCompanyBind fsUserCompanyBind);
+
+    /**
+     * 批量删除用户客服关联
+     *
+     * @param ids 需要删除的用户客服关联主键集合
+     * @return 结果
+     */
+    int deleteFsUserCompanyBindByIds(Long[] ids);
+
+    /**
+     * 删除用户客服关联信息
+     *
+     * @param id 用户客服关联主键
+     * @return 结果
+     */
+    int deleteFsUserCompanyBindById(Long id);
+
+    /**
+     * 根据看课记录看课记录
+     * @param fsUserId       小程序用户ID
+     * @param externalUserId 外部联系人ID
+     * @param watchLogId     看课记录ID
+     */
+    boolean bindFsUser(Long fsUserId, Long externalUserId, Long watchLogId);
+
+    /**
+     * 更新完课装填
+     * @param fsUserId      小程序用户ID
+     * @param qwUserId      员工企微ID
+     * @param companyUserId 员工ID
+     * @param watchLog      看课记录
+     */
+    void finish(Long fsUserId, Long qwUserId, Long companyUserId, FsCourseWatchLog watchLog);
+
+    List<UserWatchLogListVo> getWatchLogList(UserWatchLogParam param);
+}

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

@@ -126,5 +126,8 @@ public interface IFsUserCoursePeriodDaysService extends IService<FsUserCoursePer
      */
     void changePeriodCourseStatus();
 
-        long periodCourseByCount(PeriodCountParam param);
+    long periodCourseByCount(PeriodCountParam param);
+
+    List<Long> selectFsUserCoursePeriodDaysByTime(String periodSTime,String periodETime);
+
     }

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

@@ -6,7 +6,6 @@ import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.vo.FsCourseStaticsCountVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 
-import java.math.BigDecimal;
 import java.util.List;
 
 /**

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

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
@@ -16,38 +17,33 @@ import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.FsCourseFinishTemp;
-import com.fs.course.domain.FsCourseWatchLog;
-import com.fs.course.domain.FsUserCourse;
-import com.fs.course.domain.FsUserCourseVideo;
+import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
 import com.fs.course.param.*;
 import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
+import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.cache.IFsUserCourseVideoCacheService;
 import com.fs.course.vo.*;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.ConfigUtil;
-import com.fs.his.vo.OptionsVO;
 import com.fs.qw.Bean.MsgBean;
 import com.fs.qw.cache.IQwExternalContactCacheService;
 import com.fs.qw.cache.IQwUserCacheService;
 import com.fs.qw.domain.QwExternalContact;
-import com.fs.qw.domain.QwExternalContactInfo;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwWatchLog;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.mapper.QwWatchLogMapper;
+import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.qw.param.SendSopParamDetails;
-import com.fs.qw.param.SopUserLogsVO;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qw.vo.QwSopTempSetting;
 import com.fs.sop.domain.QwSopLogs;
-import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.SopUserLogsMapper;
-import com.fs.sop.service.IQwSopLogsService;
 import com.fs.store.service.cache.IFsUserCacheService;
 import com.fs.store.service.cache.IFsUserCourseCacheService;
 import com.fs.system.mapper.SysDictDataMapper;
@@ -129,6 +125,12 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Autowired
     private HyWatchLogMapper hyWatchLogMapper;
 
+    @Autowired
+    private IFsUserCoursePeriodService userCoursePeriodService;
+
+    @Autowired
+    private IFsUserCoursePeriodDaysService userCoursePeriodDaysService;
+
     @Autowired
     private SysDictDataMapper sysDictDataMapper;
 
@@ -630,21 +632,38 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Override
     public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param) {
-        List<FsCourseWatchLogListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
-
-        List<DictVO> dictVOS = sysDictDataMapper.selectDictDataListByType("sys_course_project");
-        if(!dictVOS.isEmpty()){
-            Map<String, String> projectMap = dictVOS.stream().collect(Collectors.toMap(DictVO::getDictValue, DictVO::getDictLabel));
-            for (FsCourseWatchLogListVO watchLog : list) {
-                if (watchLog.getProject() != null) {
-                    String projectName = projectMap.get(watchLog.getProject().toString());
-                    if (projectName != null) {
-                        watchLog.setProjectName(projectName);
-                    }
-                }
-            }
+        // 待看课-未注册
+        if(ObjectUtil.equal(param.getLogType(),5)){
+            param.setLogType(3);
+            param.setIsVip(0);
         }
-        return list;
+
+        // 待看课-已注册
+        if(ObjectUtil.equal(param.getLogType(),6)){
+            param.setLogType(3);
+            param.setIsVip(1);
+        }
+
+        List<FsCourseWatchLogListVO> fsCourseWatchLogListVOS = fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
+        List<FsUserCoursePeriod> fsUserCoursePeriods = userCoursePeriodService.selectFsUserCoursePeriodList(new FsUserCoursePeriod());
+
+        // 将 fsUserCoursePeriods 转换为 Map,便于快速查找
+        Map<Long, String> periodIdToNameMap = fsUserCoursePeriods.stream()
+                .collect(Collectors.toMap(
+                        FsUserCoursePeriod::getPeriodId,
+                        FsUserCoursePeriod::getPeriodName,
+                        (existing, replacement) -> existing // 如果有重复key,保留现有的
+                ));
+
+        // 遍历并赋值
+        fsCourseWatchLogListVOS.forEach(vo -> {
+            String periodName = periodIdToNameMap.get(vo.getPeriodId());
+            if (periodName != null) {
+                vo.setPeriodIdName(periodName);
+            }
+        });
+
+        return fsCourseWatchLogListVOS;
     }
 
     @Override
@@ -726,6 +745,11 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVO(param);
     }
 
+    @Override
+    public List<FsCourseWatchLogStatisticsListByCompanyVO> selectFsCourseWatchLogStatisticsListByCompanyVO(FsCourseWatchLogStatisticsListParam param) {
+        return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListByCompanyVO(param);
+    }
+
 
     public void sendSocket(String cmd,String message,String appKey){
         MsgBean msgBean=new MsgBean();
@@ -1123,8 +1147,42 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Override
     public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOexport(FsCourseWatchLogListParam param) {
 
+
+        if (param.getSendType()==1&& param.getPeriodETime()!=null && param.getPeriodSTime()!=null) {
+            List<Long> periodIds = userCoursePeriodDaysService.selectFsUserCoursePeriodDaysByTime(param.getPeriodSTime(), param.getPeriodETime());
+
+            if (!periodIds.isEmpty()){
+                List<Long> longs = userCoursePeriodService.selectFsUserCoursePeriodListByPeriodId(periodIds, param.getCompanyId());
+                if (!longs.isEmpty()){
+                    param.setPeriodIds(longs);
+                }else {
+                    return new ArrayList<>();
+                }
+            }else {
+                return new ArrayList<>();
+            }
+
+        }
+
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogListVOexport(param);
+
+        //查询所有营期
+        List<FsUserCoursePeriod> fsUserCoursePeriods = userCoursePeriodService.selectFsUserCoursePeriodList(new FsUserCoursePeriod());
+
         for (FsCourseWatchLogListVO item : list) {
+            //营期名称
+            if (ObjectUtils.isNotNull(item.getPeriodId())) {
+                // 获取匹配的periodName
+                String periodName = fsUserCoursePeriods.stream()
+                        .filter(period -> item.getPeriodId().equals(period.getPeriodId()))
+                        .map(FsUserCoursePeriod::getPeriodName)
+                        .findFirst()
+                        .orElse("无营期名称");
+                item.setPeriodIdName(periodName);
+            }else {
+                item.setPeriodIdName("自动发课无营期名称");
+            }
+
             // 项目
             if(ObjectUtils.isNotNull(item.getProject())) {
                 String sysCourseProject = DictUtils.getDictLabel("sys_course_project", String.valueOf(item.getProject()));
@@ -1192,4 +1250,9 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return list;
     }
 
+    @Override
+    public List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param) {
+        return fsCourseWatchLogMapper.selectQwFsCourseWatchLogStatisticsListVO(param);
+    }
+
 }

+ 247 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCompanyBindServiceImpl.java

@@ -0,0 +1,247 @@
+package com.fs.course.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.PubFun;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyMapper;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.domain.FsUserCompanyBind;
+import com.fs.course.domain.FsUserCourse;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.course.mapper.FsUserCompanyBindMapper;
+import com.fs.course.mapper.FsUserCourseMapper;
+import com.fs.course.service.IFsUserCompanyBindService;
+import com.fs.course.vo.UserWatchLogListVo;
+import com.fs.his.mapper.FsUserMapper;
+import com.fs.qw.domain.QwCompany;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwCompanyMapper;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.param.UserWatchLogParam;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 用户客服关联Service业务层处理
+ *
+ * @author fs
+ * @date 2025-07-22
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class FsUserCompanyBindServiceImpl extends ServiceImpl<FsUserCompanyBindMapper, FsUserCompanyBind> implements IFsUserCompanyBindService {
+
+    private final CompanyUserMapper companyUserMapper;
+    private FsUserMapper fsUserMapper;
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    private QwExternalContactMapper qwExternalContactMapper;
+    private FsUserCourseMapper fsUserCourseMapper;
+    private QwCompanyMapper qwCompanyMapper;
+    private CompanyMapper companyMapper;
+
+    /**
+     * 查询用户客服关联
+     *
+     * @param id 用户客服关联主键
+     * @return 用户客服关联
+     */
+    @Override
+    public FsUserCompanyBind selectFsUserCompanyBindById(Long id) {
+        return baseMapper.selectFsUserCompanyBindById(id);
+    }
+
+    /**
+     * 查询用户客服关联列表
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 用户客服关联
+     */
+    @Override
+    public List<FsUserCompanyBind> selectFsUserCompanyBindList(FsUserCompanyBind fsUserCompanyBind) {
+        return baseMapper.selectFsUserCompanyBindList(fsUserCompanyBind);
+    }
+
+    /**
+     * 新增用户客服关联
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserCompanyBind(FsUserCompanyBind fsUserCompanyBind) {
+        fsUserCompanyBind.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsUserCompanyBind(fsUserCompanyBind);
+    }
+
+    /**
+     * 修改用户客服关联
+     *
+     * @param fsUserCompanyBind 用户客服关联
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserCompanyBind(FsUserCompanyBind fsUserCompanyBind) {
+        fsUserCompanyBind.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateFsUserCompanyBind(fsUserCompanyBind);
+    }
+
+    /**
+     * 批量删除用户客服关联
+     *
+     * @param ids 需要删除的用户客服关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserCompanyBindByIds(Long[] ids) {
+        return baseMapper.deleteFsUserCompanyBindByIds(ids);
+    }
+
+    /**
+     * 删除用户客服关联信息
+     *
+     * @param id 用户客服关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserCompanyBindById(Long id) {
+        return baseMapper.deleteFsUserCompanyBindById(id);
+    }
+
+    @Override
+    public boolean bindFsUser(Long fsUserId, Long externalUserId, Long watchLogId) {
+        try {
+            QwExternalContact qwExternalContact = qwExternalContactMapper.selectById(externalUserId);
+            FsCourseWatchLog watchLog = fsCourseWatchLogMapper.selectById(watchLogId);
+            FsUserCourse course = fsUserCourseMapper.selectFsUserCourseByCourseId(watchLog.getCourseId());
+            Company company = companyMapper.selectCompanyById(qwExternalContact.getCompanyId());
+            QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(qwExternalContact.getCorpId());
+            // 员工ID
+            Long companyUserId = qwExternalContact.getCompanyUserId();
+            // 公司ID
+            Long companyId = qwExternalContact.getCompanyId();
+            // 企微用户ID
+            Long qwUserId = qwExternalContact.getQwUserId();
+            // 项目
+            Long project = course.getProject() == null ? 0 : course.getProject();
+            // 企微主体ID
+            Long qwCompanyId = qwCompany.getId();
+            // 课程ID
+            Long courseId = watchLog.getCourseId();
+            // 视频ID
+            Long videoId = watchLog.getVideoId();
+            // 看课记录ID
+            Long logId = watchLog.getLogId();
+            // 看课状态
+            Integer logType = watchLog.getLogType();
+            if (company.getRepeat() == 1 && project != 0) {
+                List<FsUserCompanyBind> list = baseMapper.selectList(new QueryWrapper<FsUserCompanyBind>().eq("fs_user_id", fsUserId).eq("project_id", project).last(" GROUP BY qw_user_id"));
+                if (!list.isEmpty() && list.stream().noneMatch(e -> e.getQwUserId().equals(qwUserId))) {
+                    log.error("当前客户:{}已经有所属项目{}, 企微ID:{}, 当前企微ID:{}", fsUserId, project, PubFun.listToNewList(list, FsUserCompanyBind::getQwUserId), qwUserId);
+                    return false;
+                }
+            }
+            if (qwExternalContact.getUserRepeat() == 0) {
+                Integer i = baseMapper.selectCount(new QueryWrapper<FsUserCompanyBind>().eq("fs_user_id", fsUserId).ne("company_user_id", companyUserId));
+                if (i > 0) {
+                    qwExternalContact.setUserRepeat(1);
+                    qwExternalContactMapper.updateById(qwExternalContact);
+                }
+            }
+            // 当前登录账号名称
+            FsUserCompanyBind one = baseMapper.selectOne(new QueryWrapper<FsUserCompanyBind>()
+                    .eq("fs_user_id", fsUserId)
+                    .eq("project_id", project)
+                    .eq("course_id", courseId)
+                    .eq("qw_user_id", qwUserId)
+                    .eq("company_user_id", companyUserId)
+                    .eq("video_id", videoId));
+            if (one == null) {
+                FsUserCompanyBind bind = new FsUserCompanyBind();
+                bind.setFsUserId(fsUserId);
+                bind.setCompanyUserId(companyUserId);
+                bind.setCompanyId(companyId);
+                bind.setQwUserId(qwUserId);
+                bind.setQwExternalContactId(externalUserId);
+                bind.setProjectId(project);
+                bind.setQwCompanyId(qwCompanyId);
+                bind.setCourseId(courseId);
+                bind.setVideoId(videoId);
+                bind.setWatchLogId(logId);
+                bind.setLogType(logType);
+                bind.setCreateTime(new Date());
+                bind.setUpdateTime(new Date());
+                if (bind.getLogType() == 2) {
+                    bind.setFinishTime(LocalDateTime.now());
+                }
+                try {
+                    save(bind);
+                } catch (Exception e) {
+                    log.error("添加重粉失败", e);
+                }
+            }
+            return true;
+        }catch (Exception e){
+            log.error("绑定关系错误", e);
+            return false;
+        }
+    }
+
+    @Override
+    public void finish(Long fsUserId, Long qwUserId, Long companyUserId, FsCourseWatchLog watchLog) {
+        FsUserCourse course = fsUserCourseMapper.selectFsUserCourseByCourseId(watchLog.getCourseId());
+        FsUserCompanyBind one = baseMapper.selectOne(new QueryWrapper<FsUserCompanyBind>()
+                .eq("fs_user_id", fsUserId)
+                .eq("project_id", course.getProject())
+                .eq("course_id", watchLog.getCourseId())
+                .eq("qw_user_id", qwUserId)
+                .eq("company_user_id", companyUserId)
+                .eq("video_id", watchLog.getVideoId()));
+        if(one == null){
+            CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
+            QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(companyUser.getCorpId());
+            one = new FsUserCompanyBind();
+            one.setFsUserId(fsUserId);
+            one.setCompanyUserId(companyUserId);
+            one.setCompanyId(companyUser.getCompanyId());
+            one.setQwUserId(qwUserId);
+            one.setQwExternalContactId(watchLog.getQwExternalContactId());
+            one.setProjectId(course.getProject());
+            one.setQwCompanyId(qwCompany != null ? qwCompany.getId() : null);
+            one.setCourseId(watchLog.getCourseId());
+            one.setVideoId(watchLog.getVideoId());
+            one.setWatchLogId(watchLog.getLogId());
+            one.setLogType(2);
+            one.setFinishTime(LocalDateTime.now());
+            one.setCreateTime(new Date());
+            one.setUpdateTime(new Date());
+            try {
+                save(one);
+            } catch (Exception e) {
+                log.error("添加重粉失败", e);
+            }
+        }else{
+            one.setFinishTime(LocalDateTime.now());
+            one.setLogType(2);
+            updateById(one);
+        }
+    }
+
+    @Override
+    public List<UserWatchLogListVo> getWatchLogList(UserWatchLogParam param) {
+        if(param.getExternalUserId() == null &&  param.getFsUserId() == null){
+            return Collections.emptyList();
+        }
+        return baseMapper.getWatchLogList(param);
+    }
+}

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

@@ -643,6 +643,11 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
         return baseMapper.selectFsUserCoursePeriodDaysCount(fsUserCoursePeriodDays);
     }
 
+    @Override
+    public List<Long> selectFsUserCoursePeriodDaysByTime(String periodSTime, String periodETime) {
+        return fsUserCoursePeriodDaysMapper.selectFsUserCoursePeriodDaysByTime(periodSTime,periodETime);
+    }
+
     private static FsCourseAnalysisCountVO getCourseAnalysisCountVO(FsUserCoursePeriodDays v, Map<Long, FsCourseAnalysisCountVO> courseMap, Map<Long, FsCourseAnalysisCountVO> redPacketMap, Map<Long, FsCourseAnalysisCountVO> answerMap) {
         FsCourseAnalysisCountVO countVO = new FsCourseAnalysisCountVO();
         FsCourseAnalysisCountVO courseVO = courseMap.getOrDefault(v.getVideoId(), countVO);

+ 14 - 4
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -91,7 +91,7 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     public int insertFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod)
     {
         fsUserCoursePeriod.setCreateTime(LocalDateTime.now());
-        
+
         // 设置营期状态:如果开启自由模式,设置为进行中(status=2)
         if (fsUserCoursePeriod.getFreeMode() != null && fsUserCoursePeriod.getFreeMode() == 1) {
             // 自由模式:营期状态为进行中
@@ -195,8 +195,10 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
         List<Long> videoIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getVideoId).collect(Collectors.toList());
         if(!periodDayIds.isEmpty()){
             fsUserCoursePeriodDaysMapper.updateBatchDelFlag(periodDayIds.toArray(new Long[0]),1);
-            //删除红包记录
-            fsUserCourseVideoRedPackageMapper.updateBatchDelFlag(videoIds.toArray(new Long[0]),1);
+            //删除红包记录(修改状态)
+            //fsUserCourseVideoRedPackageMapper.updateBatchDelFlag(videoIds.toArray(new Long[0]),1);
+            //直接删除
+            fsUserCourseVideoRedPackageMapper.deleteBatchByVideoIds(videoIds.toArray(new Long[0]));
         }
         return flag;
     }
@@ -322,7 +324,6 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     public List<FsUserCoursePeriod> selectFsPeriodlist(PeriodStatisticCountParam param) {
         return fsUserCoursePeriodMapper.selectFsPeriodlist(param);
     }
-
     @Override
     public List<Long> selectCoursePeriodDaysByTime(String periodSTime, String periodETime, Long companyId) {
         return Collections.emptyList();
@@ -372,4 +373,13 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
         }
         return R.ok("营期及课程安排创建成功");
     }
+    @Override
+    public List<Long> selectCoursePeriodDaysByTime(String periodSTime, String periodETime,Long companyId) {
+        return fsUserCoursePeriodMapper.selectCoursePeriodDaysByTime(periodSTime,periodETime,companyId);
+    }
+
+    @Override
+    public List<Long> selectFsUserCoursePeriodListByPeriodId(List<Long> periodIds, Long companyId) {
+        return fsUserCoursePeriodMapper.selectFsUserCoursePeriodListByPeriodId(periodIds,companyId);
+    }
 }

+ 4 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java

@@ -100,8 +100,10 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
         List<Long> videoIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getVideoId).collect(Collectors.toList());
         if(!periodDayIds.isEmpty()){
             fsUserCoursePeriodDaysMapper.updateBatchDelFlag(periodDayIds.toArray(new Long[0]),1);
-            //删除红包记录
-            fsUserCourseVideoRedPackageMapper.updateBatchDelFlag(videoIds.toArray(new Long[0]),1);
+            //删除红包记录(修改状态)
+            //fsUserCourseVideoRedPackageMapper.updateBatchDelFlag(videoIds.toArray(new Long[0]),1);
+            //直接删除
+            fsUserCourseVideoRedPackageMapper.deleteBatchByVideoIds(videoIds.toArray(new Long[0]));
         }
     }
 

+ 67 - 68
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -32,6 +32,7 @@ import com.fs.course.dto.CoursePackageDTO;
 import com.fs.course.mapper.*;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.*;
+import com.fs.course.service.IFsUserCompanyBindService;
 import com.fs.course.service.IFsUserCompanyUserService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsVideoResourceService;
@@ -48,10 +49,10 @@ import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.OptionsVO;
-import com.fs.qw.domain.QwCompany;
-import com.fs.qw.domain.QwExternalContact;
-import com.fs.qw.domain.QwUser;
+import com.fs.qw.domain.*;
 import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.mapper.QwGroupChatMapper;
+import com.fs.qw.mapper.QwGroupChatUserMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwExternalContactService;
@@ -59,6 +60,7 @@ 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.sop.domain.SopUserLogsInfo;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import com.fs.sop.service.ISopUserLogsInfoService;
@@ -122,6 +124,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     private static final String miniappRealLink = "/pages_course/video.html?course=";
     private static final String REAL_LINK_PREFIX = "/courseH5/pages/course/learning?course=";
     private static final String SHORT_LINK_PREFIX = "/courseH5/pages/course/learning?s=";
+    // 排除看课数量限制的公司集合
+    private static final Set<String> EXCLUDE_PROJECTS = new HashSet<>(Arrays.asList(
+           "福本源","宽益堂","叮当国医"
+    ));
     @Autowired
     ICompanyService companyService;
     @Autowired
@@ -129,6 +135,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     private FsUserCourseVideoMapper fsUserCourseVideoMapper;
     @Autowired
+    private QwGroupChatMapper qwGroupChatMapper;
+    @Autowired
+    private QwGroupChatUserMapper qwGroupChatUserMapper;
+    @Autowired
     private IFsUserService fsUserService;
     @Autowired
     private FsUserCourseCompanyUserTimeMapper companyUserTimeMapper;
@@ -235,6 +245,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     ConfigUtil configUtil;
 
+    @Autowired
+    private IFsUserCompanyBindService fsUserCompanyBindService;
+
 
 
     /**
@@ -504,36 +517,27 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         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());
-        log.info("群聊参数:{},链接参数:{}, 企微返回:{}", JSON.toJSONString(param), JSON.toJSONString(courseLink), JSON.toJSONString(result));
-        if(result.getErrCode() != 0){
-            return R.error("企微接口请求失败,请联系管理员:" +result.getErrMsg());
+        QwGroupChat qwGroupChat = qwGroupChatMapper.selectQwGroupChatByChatId(courseLink.getChatId());
+        if(qwGroupChat == null){
+            return R.error("群参数异常");
+        }
+        SopUserLogsInfo sopUserLogsInfo =  new SopUserLogsInfo();
+        sopUserLogsInfo.setChatId(courseLink.getChatId());
+        List<QwGroupChatUser> qwGroupChatUsers = qwGroupChatUserMapper.selectByChatId(sopUserLogsInfo);
+        if(qwGroupChatUsers == null || qwGroupChatUsers.isEmpty()){
+            return R.error("群参数异常");
         }
-//            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(fsUser.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())
-//                    );
         QwExternalContact qwExternalContact =
                 qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
-                        .eq("user_id", result.getGroupChat().getOwner())
+                        .eq("user_id", qwGroupChat.getOwner())
                         .eq("fs_user_id", param.getUserId())
                         .eq("corp_id", param.getCorpId())
                         .eq("status",0));
         if(qwExternalContact==null){
             return addCustomerService(param.getQwUserId(),msg);
         }
-        log.info("外部联系人数据:{}", qwExternalContact);
-        if(result.getGroupChat().getMemberList().stream().noneMatch(e -> e.getUserId().equals(qwExternalContact.getExternalUserId()))){
+        if(qwGroupChatUsers.stream().noneMatch(e -> e.getUserId().equals(qwExternalContact.getExternalUserId()))){
+            log.error("客户不在群:{},里面:{}", qwGroupChat.getChatId(), qwExternalContact.getExternalUserId());
             return addCustomerService(param.getQwUserId(),msg);
         }
         Long qwExternalId = qwExternalContact.getId();
@@ -560,7 +564,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             }
 
             log.setUpdateTime(new Date());
-//            fsUserCompanyBindService.bindFsUser(fsUser.getUserId(), qwExternalId, log.getLogId());
+            //重粉逻辑
+            fsUserCompanyBindService.bindFsUser(param.getUserId(), qwExternalId, log.getLogId());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
 
         }else {
@@ -579,7 +584,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             log.setUpdateTime(new Date());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
         }
-//        fsUserCompanyBindService.bindFsUser(fsUser.getUserId(), qwExternalId, log.getLogId());
+        //重粉逻辑
+        fsUserCompanyBindService.bindFsUser(param.getUserId(), qwExternalId, log.getLogId());
         return R.error(567,"群聊通用链接").put("qwExternalId", qwExternalContact.getId());
     }
     private R handleRoom(FsUserCourseVideoAddKfUParam param,FsUser user) {
@@ -701,27 +707,28 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     }
 
     private R handleExt(FsUserCourseVideoAddKfUParam param, String msg, boolean oneCompanyCourse){
-        if (param.getLinkType()!=null&&param.getLinkType()==1){
-            if (param.getLinkId()!=null){
-                // 从数据库中查找短链对应的真实链接
-                FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLinkId(param.getLinkId());
-                if (courseLink != null) {
-                    // 获取当前时间
-                    Date currentTime = new Date();
-                    // 获取链接的更新时间(假设这个字段代表过期时间)
-                    Date updateTime = courseLink.getUpdateTime();
-
-                    // 判断是否过期
-                    if (currentTime.after(updateTime)) {
-                        // 链接已过期
-                        return R.error("链接已过期");
-                    }else {
-                        return R.ok();
-                    }
-                }
-            }
-            return R.error("链接过期");
-        }
+        //暂时注释应急短链逻辑
+//        if (param.getLinkType()!=null&&param.getLinkType()==1){
+//            if (param.getLinkId()!=null){
+//                // 从数据库中查找短链对应的真实链接
+//                FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLinkId(param.getLinkId());
+//                if (courseLink != null) {
+//                    // 获取当前时间
+//                    Date currentTime = new Date();
+//                    // 获取链接的更新时间(假设这个字段代表过期时间)
+//                    Date updateTime = courseLink.getUpdateTime();
+//
+//                    // 判断是否过期
+//                    if (currentTime.after(updateTime)) {
+//                        // 链接已过期
+//                        return R.error("链接已过期");
+//                    }else {
+//                        return R.ok();
+//                    }
+//                }
+//            }
+//            return R.error("链接过期");
+//        }
 
         Long qwExternalId = param.getQwExternalId();
 
@@ -781,6 +788,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
             iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId,param.getUserId());
 
+            //重粉逻辑
+            fsUserCompanyBindService.bindFsUser(param.getUserId(), qwExternalId, log.getLogId());
+
+
 
             if (param.getLinkType()!=null&&param.getLinkType()==5){
                 FsCourseLink fsCourseLink = fsCourseLinkMapper.selectExpireLinkByQwExternalId(param.getQwUserId(), param.getVideoId(), qwExternalId);
@@ -805,25 +816,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             user.setQwExtId(param.getQwExternalId());
             fsUserMapper.updateFsUser(user);
 
-
-//            //小访客特有
-//            SendXfkParam xfkParam=new SendXfkParam();
-//            xfkParam.setCorpId(externalContact.getCorpId());
-//            xfkParam.setUserId(externalContact.getUserId());
-//            xfkParam.setName(externalContact.getName());
-//            xfkParam.setAddWay(externalContact.getAddWay());
-//            xfkParam.setState(externalContact.getState());
-//            if (externalContact.getCreateTime()!=null){
-//                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-//                String formattedTime = sdf.format(externalContact.getCreateTime());
-//                xfkParam.setCreateTime(formattedTime);
-//            }else {
-//                xfkParam.setCreateTime("");
-//            }
-//
-//
-//            xfkService.executeSopByIds(xfkParam);
-
             iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId,param.getUserId());
 
             //绑定上之后 更新观看记录
@@ -835,6 +827,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             log.setUpdateTime(new Date());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
 
+            //重粉逻辑
+            fsUserCompanyBindService.bindFsUser(param.getUserId(), qwExternalId, log.getLogId());
+
+
             if (param.getLinkType()!=null&&param.getLinkType()==5){
                 FsCourseLink fsCourseLink = fsCourseLinkMapper.selectExpireLinkByQwExternalId(param.getQwUserId(), param.getVideoId(), qwExternalId);
                 return R.error(566,"官方群发通用链接").put("courseLink",fsCourseLink);
@@ -1494,7 +1490,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             Company company = companyMapper.selectCompanyById(param.getCompanyId());
             BigDecimal money = company.getMoney();
             if (money.compareTo(BigDecimal.ZERO)<=0) {
-                return R.error("服务商余额不足,请联系群主服务充值!");
+                return R.error("服务商余额不足,请联系群主服务充值!");
             }
 
             // 发送红包
@@ -1729,6 +1725,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         FsUserCourseVideoDetailsVO fsUserCourseVideoDetailsVO = new FsUserCourseVideoDetailsVO();
         BeanUtils.copyProperties(fsUserCourseVideo, fsUserCourseVideoDetailsVO);
 
+        //这里 改成取线路一值,返回给前端。VideoUrl 是原视频(用来算流量的),不要去改,lineOne是转码后的视频
+        fsUserCourseVideoDetailsVO.setVideoUrl(fsUserCourseVideo.getLineOne());
+
         // 获取课程相关的题库
         String questionBankId = fsUserCourseVideo.getQuestionBankId();
         List<FsUserVideoQuestionVO> questionList = Collections.emptyList();
@@ -1885,7 +1884,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             return ResponseResult.fail(504, "请观看最新的课程项目");
         }
         // 项目看课数限制
-        if(!"福本源".equals(signProjectName) && !"宽益堂".equals(signProjectName) && !CloudHostUtils.hasCloudHostName("弘德堂")) {
+        if(!EXCLUDE_PROJECTS.contains(signProjectName) && !CloudHostUtils.hasCloudHostName("弘德堂")) {
             Integer logCount = fsUserCourseMapper.selectTodayCourseWatchLogCountByUserIdAndProjectId(param.getUserId(), courseProject);
             if (Objects.isNull(watchCourseVideo) && logCount > 0) {
                 return ResponseResult.fail(504, "超过项目看课数量限制");

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

@@ -44,9 +44,16 @@ public class FsCourseWatchLogListVO extends BaseEntity
      * 项目
      */
     private Integer project;
+
+    /** 营期id */
+    private Long periodId;
+
     @Excel(name = "项目名称")
     private String projectName;
 
+    @Excel(name = "营期名称")
+    private String periodIdName;
+
     @Excel(name = "课程名称")
     private String courseName;
 

+ 42 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListByCompanyVO.java

@@ -0,0 +1,42 @@
+package com.fs.course.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsCourseWatchLogStatisticsListByCompanyVO {
+
+
+    @Excel(name = "公司名称")
+    private String companyName;
+    private Integer project;
+    @Excel(name = "项目名称")
+    private String projectName;
+
+    private Long courseId;
+    @Excel(name = "课程名称")
+    private String courseName;
+
+    private Long videoId;
+    @Excel(name = "小节名称")
+    private String videoName;
+
+    @Excel(name = "待看课")
+    private String type1;
+    @Excel(name = "看课中")
+    private String type2;
+    @Excel(name = "已完课")
+    private String type3;
+    @Excel(name = "看课中断")
+    private String type4;
+    @Excel(name = "上线数")
+    private Long onLineNum;
+
+    private Long userId;
+    @Excel(name = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date createTime;
+}

+ 30 - 0
fs-service/src/main/java/com/fs/course/vo/UserWatchLogListVo.java

@@ -0,0 +1,30 @@
+package com.fs.course.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 课程对象 fs_user_course
+ *
+ * @author fs
+ * @date 2024-05-15
+ */
+@Data
+public class UserWatchLogListVo {
+
+    private Integer logType;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+    private String fsUserName;
+    private String projectName;
+    private String courseName;
+    private String videoName;
+    private String qwUserName;
+
+}

+ 8 - 18
fs-service/src/main/java/com/fs/erp/dto/sdk/df/DfClient.java

@@ -5,6 +5,8 @@ import com.alibaba.fastjson.JSON;
 import com.fs.erp.dto.df.DFConfigVo;
 import com.fs.erp.dto.sdk.df.enums.RequestUrlEnum;
 import com.fs.his.config.FsSysConfig;
+import com.fs.his.domain.FsDfAccount;
+import com.fs.his.mapper.FsDfAccountMapper;
 import com.fs.his.utils.ConfigUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.binary.Hex;
@@ -43,26 +45,13 @@ import java.util.Objects;
 public class DfClient {
 	@Autowired
 	ConfigUtil configUtil;
+	@Autowired
+	private FsDfAccountMapper fsDfAccountMapper;
 	private final String baseUrl = "https://ds-api.sf-express.com/externalapi/";
 	public static final String CHARSET = "UTF-8";
 	public static final String CONTENT_TYPE = "application/json";
 
 
-	private String getAppKey(int i) {
-		FsSysConfig sysConfig = configUtil.getSysConfig();
-		String dfConfigVo = sysConfig.getDfAccounts();
-		List<DFConfigVo> dfConfigVos = JSON.parseArray(dfConfigVo, DFConfigVo.class);
-		return dfConfigVos.get(i).getDfAppKey();
-	}
-
-	private String getAppsecret(int i) {
-		FsSysConfig sysConfig = configUtil.getSysConfig();
-		String dfConfigVo = sysConfig.getDfAccounts();
-		List<DFConfigVo> dfConfigVos = JSON.parseArray(dfConfigVo, DFConfigVo.class);
-		return dfConfigVos.get(i).getDfAppsecret();
-	}
-
-
 
 
 	private DfClient(){}
@@ -94,9 +83,10 @@ public class DfClient {
 		return Hex.encodeHexString(bytes);
 	}
 
-	public String execute(RequestUrlEnum request, Map<String, Object> params,int i) throws IOException {
-		String appkey = getAppKey(i);
-		String appsecret = getAppsecret(i);
+	public String execute(RequestUrlEnum request, Map<String, Object> params,Long dfAccountId) throws IOException {
+		FsDfAccount dfAccount = fsDfAccountMapper.selectFsDfAccountById(dfAccountId);
+		String appkey = dfAccount.getDfAppKey();
+		String appsecret = dfAccount.getDfAppsecret();
 		String timestamp = String.valueOf(System.currentTimeMillis());
 		String relativeUrl = request.getUrl();
 		String type = request.getType();

+ 254 - 70
fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java

@@ -27,6 +27,13 @@ import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsStoreOrderLogsService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.hisStore.domain.FsStoreOrderItemScrm;
+import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.hisStore.domain.FsStoreProductScrm;
+import com.fs.hisStore.mapper.FsStoreOrderItemScrmMapper;
+import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
+import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
+import com.fs.hisStore.vo.FsStoreOrderItemVO;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.hc.openapi.tool.util.StringUtils;
@@ -46,18 +53,30 @@ import java.util.concurrent.atomic.AtomicBoolean;
 @Slf4j
 public class DfOrderServiceImpl implements IErpOrderService
 {
+//    @Autowired
+//    ConfigUtil configUtil;
+
     @Autowired
-    ConfigUtil configUtil;
+    FsDfAccountMapper fsDfAccountMapper;
 
     @Autowired
     private FsStoreOrderMapper fsStoreOrderMapper;
 
+    @Autowired
+    private FsStoreOrderScrmMapper fsStoreOrderScrmMapper;
+
     @Autowired
     private FsStoreOrderItemMapper fsStoreOrderItemMapper;
 
+    @Autowired
+    private FsStoreOrderItemScrmMapper fsStoreOrderItemScrmMapper;
+
     @Autowired
     private FsStoreProductMapper fsStoreProductMapper;
 
+    @Autowired
+    private FsStoreProductScrmMapper fsStoreProductScrmMapper;
+
     @Autowired
     private FsStoreOrderDfMapper fsStoreOrderDfMapper;
 
@@ -92,7 +111,7 @@ public class DfOrderServiceImpl implements IErpOrderService
 
     @Override
     public ErpOrderResponse addOrderScrm(ErpOrder order) {
-        return null;
+        return getScrmErpOrderResponse(order);
     }
 
     /**
@@ -110,19 +129,19 @@ public class DfOrderServiceImpl implements IErpOrderService
         if (df == null){
             return null;
         }
-        Integer sfAccountIndex = getSFAccountIndex(fsStoreOrder.getOrderId());
+        Long dfAccountId = getSFAccountIndex(fsStoreOrder.getOrderId());
         HashMap<String, Object> map = new HashMap<>();
         map.put("loginAccount", df.getLoginAccount());
-        DFConfigVo config = getconfig(sfAccountIndex);
-        if (config != null && StringUtils.isNotBlank(config.getCallBackUrl())) {
-            map.put("callBackUrl", config.getCallBackUrl());
+        FsDfAccount dfAccount = fsDfAccountMapper.selectFsDfAccountById(dfAccountId);
+        if (dfAccount != null && StringUtils.isNotBlank(dfAccount.getCallBackUrl())) {
+            map.put("callBackUrl", dfAccount.getCallBackUrl());
         }
         map.put("orderNumber", orderCode);
         map.put("mailNumber", fsStoreOrder.getDeliverySn());
         try {
             //2.请求
             log.info("开始取消订单,参数: {}", JSON.toJSONString(map));
-            String response = client.execute(RequestUrlEnum.ORDER_CANCEL, map, sfAccountIndex);
+            String response = client.execute(RequestUrlEnum.ORDER_CANCEL, map, dfAccountId);
             DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
             //3.处理请求结果
             if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
@@ -158,13 +177,13 @@ public class DfOrderServiceImpl implements IErpOrderService
             FsStoreOrder order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(orderCode);
             if (order != null) {
                 String mailNumber = order.getDeliverySn();
-                Integer sfAccountIndex = getSFAccountIndex(order.getOrderId());
-                if (StringUtils.isNotBlank(mailNumber) && sfAccountIndex > -1) {
+                Long dfAccountId = getSFAccountIndex(order.getOrderId());
+                if (StringUtils.isNotBlank(mailNumber) && dfAccountId != null) {
                     try {
                         Map<String, Object> map = new HashMap<>();
                         map.put("mailNumber", mailNumber);
                         log.info("开始查询路由结果,参数为: {}", JSON.toJSONString(map));
-                        String response = client.execute(RequestUrlEnum.ORDER_DELIVERY, map, sfAccountIndex);
+                        String response = client.execute(RequestUrlEnum.ORDER_DELIVERY, map, dfAccountId);
                         DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
                         if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
                             dfApiResponse.setCode(mailNumber);
@@ -224,12 +243,12 @@ public class DfOrderServiceImpl implements IErpOrderService
                 if(df == null){
                     return null;
                 }
-                Integer sfAccountIndex = getSFAccountIndex(order.getOrderId());
-                if (sfAccountIndex > -1) {
+                Long dfAccountId = getSFAccountIndex(order.getOrderId());
+                if (dfAccountId != null) {
                     Map<String, Object> orderResultQueryParam = new HashMap<>();
                     orderResultQueryParam.put("orderNumber", orderNumber);
                     orderResultQueryParam.put("exInterfaceType", df.getStatus());
-                    getOrderResult(orderResultQueryParam, sfAccountIndex);
+                    getOrderResult(orderResultQueryParam, dfAccountId);
                     return response;
                 }
             }
@@ -260,11 +279,11 @@ public class DfOrderServiceImpl implements IErpOrderService
     @Override
     public void getOrderDeliveryStatus(FsStoreOrder order) {
         Map<String, Object> map = new HashMap<>();
-        Integer sfAccountIndex = getSFAccountIndex(order.getOrderId());
+        Long dfAccountId = getSFAccountIndex(order.getOrderId());
         map.put("orderNumber", order.getOrderCode());
         map.put("mailNumber", order.getDeliverySn());
         try {
-            String response = client.execute(RequestUrlEnum.ORDER_DELIVERY_STATUS, map, sfAccountIndex);
+            String response = client.execute(RequestUrlEnum.ORDER_DELIVERY_STATUS, map, dfAccountId);
             DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
             if ("运单不存在".equals(dfApiResponse.getMsg())){
 
@@ -380,6 +399,56 @@ public class DfOrderServiceImpl implements IErpOrderService
         df.setUpdateTime(new Date());
         fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
     }
+    /**
+     * 获取erp推送参数
+     *
+     * @param order 订单参数
+     * @return
+     */
+    private ErpOrderResponse getScrmErpOrderResponse(ErpOrder order) {
+
+        FsStoreOrderScrm fsStoreOrder = fsStoreOrderScrmMapper.selectFsStoreOrderByOrderCode(order.getPlatform_code());
+        if (fsStoreOrder == null) {
+            return null;
+        }
+        Long dfAccountId = getSFAccountIndex(fsStoreOrder.getId());
+        //1.获取请求参数
+        ExternalOrderRequestVo vo = getCreateScrmOrderRequestParam(order, fsStoreOrder, dfAccountId);
+        if (vo == null) {
+            return null;
+        }
+        try {
+            Map<String, Object> map = JSON.parseObject(JSON.toJSONString(vo), Map.class);
+            //2.请求
+            log.info("开始推送订单,参数: {}", JSON.toJSONString(map));
+            String response = client.execute(RequestUrlEnum.CREAT_ORDER, map, dfAccountId);
+            DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
+            //3.处理请求结果
+            if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
+                //存储订单推送用的哪个账户
+                FsStoreOrderDf df = addDfOrderScrm(fsStoreOrder, dfAccountId);
+                log.info("订单推送成功: {}", response);
+                //可以回调 也可以查询订单
+                Map<String, Object> orderResultQueryParam = new HashMap<>();
+                orderResultQueryParam.put("orderNumber", order.getPlatform_code());
+                orderResultQueryParam.put("exInterfaceType", df.getStatus());
+                try {
+                    getOrderResult(orderResultQueryParam,dfAccountId);
+                } catch (Exception e) {
+                    log.info("推送订单完成,查询订单问题{}", e.getMessage());
+                }
+                ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
+                erpOrderResponse.setCode(order.getPlatform_code());
+                erpOrderResponse.setSuccess(true);
+                return erpOrderResponse;
+            } else {
+                throw new RuntimeException(String.format("订单推送失败,原因: %s", dfApiResponse.getMsg()));
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new ErpOrderResponse();
+    }
 
     /**
      * 获取erp推送参数
@@ -393,9 +462,9 @@ public class DfOrderServiceImpl implements IErpOrderService
         if (fsStoreOrder == null) {
             return null;
         }
-        int sfAccountIndex = getSFAccountIndex(fsStoreOrder.getOrderId());
+        Long dfAccountId = getSFAccountIndex(fsStoreOrder.getOrderId());
         //1.获取请求参数
-        ExternalOrderRequestVo vo = getCreateOrderRequestParam(order, fsStoreOrder, sfAccountIndex);
+        ExternalOrderRequestVo vo = getCreateOrderRequestParam(order, fsStoreOrder, dfAccountId);
         if (vo == null) {
             return null;
         }
@@ -403,19 +472,19 @@ public class DfOrderServiceImpl implements IErpOrderService
             Map<String, Object> map = JSON.parseObject(JSON.toJSONString(vo), Map.class);
             //2.请求
             log.info("开始推送订单,参数: {}", JSON.toJSONString(map));
-            String response = client.execute(RequestUrlEnum.CREAT_ORDER, map, sfAccountIndex);
+            String response = client.execute(RequestUrlEnum.CREAT_ORDER, map, dfAccountId);
             DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
             //3.处理请求结果
             if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
                 //存储订单推送用的哪个账户
-                FsStoreOrderDf df = addDfOrder(fsStoreOrder, sfAccountIndex);
+                FsStoreOrderDf df = addDfOrder(fsStoreOrder, dfAccountId);
                 log.info("订单推送成功: {}", response);
                 //可以回调 也可以查询订单
                 Map<String, Object> orderResultQueryParam = new HashMap<>();
                 orderResultQueryParam.put("orderNumber", order.getPlatform_code());
                 orderResultQueryParam.put("exInterfaceType", df.getStatus());
                 try {
-                    getOrderResult(orderResultQueryParam,sfAccountIndex);
+                    getOrderResult(orderResultQueryParam,dfAccountId);
                 } catch (Exception e) {
                     log.info("推送订单完成,查询订单问题{}", e.getMessage());
                 }
@@ -432,16 +501,42 @@ public class DfOrderServiceImpl implements IErpOrderService
         return new ErpOrderResponse();
     }
 
-    private @NotNull FsStoreOrderDf addDfOrder(FsStoreOrder fsStoreOrder, int sfAccountIndex) {
+    private @NotNull FsStoreOrderDf addDfOrderScrm(FsStoreOrderScrm fsStoreOrder, Long dfAccountId) {
+        FsStoreOrderDf df = new FsStoreOrderDf();
+        df.setOrderId(fsStoreOrder.getId());
+        df.setOrderCode(fsStoreOrder.getOrderCode());
+        FsDfAccount dfAccount = fsDfAccountMapper.selectFsDfAccountById(dfAccountId);
+        df.setAppKey(dfAccount.getDfAppKey());
+        df.setAppSecret(dfAccount.getDfAppsecret());
+        df.setLoginAccount(dfAccount.getLoginAccount());
+        df.setMonthlyCard(dfAccount.getMonthlyCard());
+        df.setExpressProductCode(dfAccount.getExpressProductCode());
+        df.setTotalPrice(fsStoreOrder.getPayMoney());
+        df.setPlatformPrice(fsStoreOrder.getPayPrice());
+        df.setStatus(1);
+        //查询是否存在
+        FsStoreOrderDf temp = fsStoreOrderDfMapper.selectFsStoreOrderDfByOrderId(df.getOrderId());
+        if (temp != null) {
+            //修改
+            df.setUpdateTime(DateUtils.getNowDate());
+            fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
+        } else {
+            df.setCreateTime(DateUtils.getNowDate());
+            fsStoreOrderDfMapper.insertFsStoreOrderDf(df);
+        }
+        return df;
+    }
+
+    private @NotNull FsStoreOrderDf addDfOrder(FsStoreOrder fsStoreOrder, Long dfAccountId) {
         FsStoreOrderDf df = new FsStoreOrderDf();
         df.setOrderId(fsStoreOrder.getOrderId());
         df.setOrderCode(fsStoreOrder.getOrderCode());
-        DFConfigVo config = getconfig(sfAccountIndex);
-        df.setAppKey(config.getDfAppKey());
-        df.setAppSecret(config.getDfAppsecret());
-        df.setLoginAccount(config.getLoginAccount());
-        df.setMonthlyCard(config.getMonthlyCard());
-        df.setExpressProductCode(config.getExpressProductCode());
+        FsDfAccount dfAccount = fsDfAccountMapper.selectFsDfAccountById(dfAccountId);
+        df.setAppKey(dfAccount.getDfAppKey());
+        df.setAppSecret(dfAccount.getDfAppsecret());
+        df.setLoginAccount(dfAccount.getLoginAccount());
+        df.setMonthlyCard(dfAccount.getMonthlyCard());
+        df.setExpressProductCode(dfAccount.getExpressProductCode());
         df.setTotalPrice(fsStoreOrder.getPayMoney());
         df.setPlatformPrice(fsStoreOrder.getPayPrice());
         df.setStatus(1);
@@ -458,25 +553,129 @@ public class DfOrderServiceImpl implements IErpOrderService
         return df;
     }
 
+    /**
+     * 通用erpOrderScrm获取创建订单参数
+     *
+     * @param order
+     * @return
+     */
+    private ExternalOrderRequestVo getCreateScrmOrderRequestParam(ErpOrder order, FsStoreOrderScrm fsStoreOrder, Long dfAccountId) {
+        ExternalOrderRequestVo vo = new ExternalOrderRequestVo();
+        FsDfAccount dfAccount = fsDfAccountMapper.selectFsDfAccountById(dfAccountId);
+        if (dfAccount == null) {
+            return null;
+        }
+        String loginAccount = dfAccount.getLoginAccount();
+        vo.setMonthlyCard(dfAccount.getMonthlyCard()); //月结卡号
+        vo.setExpressProductCode(dfAccount.getExpressProductCode()); //物流产品编码
+
+
+        vo.setLoginAccount(loginAccount); //代服系统登录账号
+        String callBackUrl = dfAccount.getCallBackUrl();
+        if (StringUtils.isNotBlank(callBackUrl)) {
+            vo.setCallBackUrl(callBackUrl); //订单下单后异步通知地址
+        }
+        FsStoreOrderDf temp = fsStoreOrderDfMapper.selectFsStoreOrderDfByOrderId(fsStoreOrder.getId());
+        if (temp != null) {
+            vo.setParcelQuantity(temp.getParcelQuantity());//包裹数量
+        }
+
+        vo.setOrderNumber(order.getPlatform_code()); //订单号(不能重复)
+
+        int orderPayMethod = 0;
+        BigDecimal couponPrice = fsStoreOrder.getCouponPrice();
+        if (couponPrice == null) {
+            couponPrice = BigDecimal.ZERO;
+        }
+
+        if (ObjectUtil.equal(1, fsStoreOrder.getPayType())) {
+            //在线支付
+            orderPayMethod = 1;
+        } else { // 如果是线上付款
+            orderPayMethod = 2;
+            // 货到付款金额 = 订单剩余支付金额
+            vo.setCollectingMoney(fsStoreOrder.getDeliveryPayMoney().doubleValue());
+            vo.setCollectionCardNumber(dfAccount.getMonthlyCard()); // 就是月结账号
+        }
+        //订单付款方式 1:在线支付 2:货到付款
+        //如果填了2,代收金额必须大于0,代收卡号必填
+        vo.setOrderPayMethod(orderPayMethod);
+
+        vo.setConsignmentNumber(Integer.valueOf(Math.toIntExact(fsStoreOrder.getTotalNum()))); //托寄物数量 必填
+
+        vo.setBuyerMessage(fsStoreOrder.getRemark()); //买家留言
+
+        vo.setSenderName(dfAccount.getSenderName()); //寄件人
+        vo.setSenderPhone(dfAccount.getSenderPhone()); //寄件人手机
+        vo.setSenderProvince(dfAccount.getSenderProvince());//寄件人省
+        vo.setSenderCity(dfAccount.getSenderCity());//寄件人市
+        vo.setSenderDistrict(dfAccount.getSenderDistrict());//寄件人区
+        vo.setSenderAddress(dfAccount.getSenderAddress());//寄件人地址
+        vo.setReceiverName(order.getReceiver_name());//收件人
+        vo.setReceiverPhone(order.getReceiver_mobile()); //收件人手机
+        vo.setReceiverTelephone(order.getReceiver_phone());//收件人电话 否
+        vo.setReceiverProvince(order.getReceiver_province()); //收件人省
+        vo.setReceiverCity(order.getReceiver_city()); //收件人市
+        vo.setReceiverDistrict(order.getReceiver_district()); //收件人区
+        vo.setReceiverAddress(order.getReceiver_address()); // 收件人地址
+        vo.setExpressPayMethod(1); //物流付款方式 1:寄付月结 2:寄付现结 3:收方付 4:第三方付
+        //订单sku集合
+        StringBuilder consignmentStr = new StringBuilder();
+        List<FsStoreOrderItemVO> items = fsStoreOrderItemScrmMapper.selectFsStoreOrderItemListByOrderId(fsStoreOrder.getId());
+        if (items != null && !items.isEmpty()) {
+            ArrayList<ExteriorOrderSkuVo> orderSkus = new ArrayList<>();
+            items.forEach(item -> {
+                ExteriorOrderSkuVo skuVo = new ExteriorOrderSkuVo();
+                FsStoreProductScrm product = fsStoreProductScrmMapper.selectFsStoreProductById(item.getProductId());
+                Asserts.check(ObjectUtils.isNotNull(product), "该产品不存在! 产品id: {} ", item.getProductId());
+                skuVo.setProductName(product.getProductName()); //商品名称
+                com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(item.getJsonInfo());
+                skuVo.setSkuCode(jsonObject.getString("sku")); //sku编码
+                skuVo.setProductCode(jsonObject.getString("barCode")); //商品编号
+                skuVo.setAttributeNames(jsonObject.getString("sku")); //商品规格,格式:颜色:红色,尺寸:L码....以此类推
+                skuVo.setProductNumber(Math.toIntExact(item.getNum())); //商品预定数量
+                skuVo.setPrice(product.getPrice().doubleValue()); //商品单价
+
+//                skuVo.setAdjustAmount(0d); //调整金额
+                // 优惠
+                skuVo.setSubAmount(product.getPrice().doubleValue() * item.getNum());
+
+                //组装寄托物
+                consignmentStr.append(product.getProductName()).append("*").append(item.getNum()).append(",");
+                orderSkus.add(skuVo);
+            });
+            if (consignmentStr.length() > 0) {
+                consignmentStr.deleteCharAt(consignmentStr.length() - 1);
+            }
+            if (consignmentStr.length() > 100) {
+                consignmentStr.delete(consignmentStr.length() - 4, consignmentStr.length());
+                consignmentStr.append("...");
+            }
+            vo.setOrderSkus(orderSkus);
+            vo.setConsignment(consignmentStr.toString()); //寄托物 必填
+        }
+        return vo;
+    }
+
     /**
      * 通用erpOrder获取创建订单参数
      *
      * @param order
      * @return
      */
-    private ExternalOrderRequestVo getCreateOrderRequestParam(ErpOrder order, FsStoreOrder fsStoreOrder, int index) {
+    private ExternalOrderRequestVo getCreateOrderRequestParam(ErpOrder order, FsStoreOrder fsStoreOrder, Long dfAccountId) {
         ExternalOrderRequestVo vo = new ExternalOrderRequestVo();
-        DFConfigVo config = getconfig(index);
-        if (config == null) {
+        FsDfAccount dfAccount = fsDfAccountMapper.selectFsDfAccountById(dfAccountId);
+        if (dfAccount == null) {
             return null;
         }
-        String loginAccount = config.getLoginAccount();
-        vo.setMonthlyCard(config.getMonthlyCard()); //月结卡号
-        vo.setExpressProductCode(config.getExpressProductCode()); //物流产品编码
+        String loginAccount = dfAccount.getLoginAccount();
+        vo.setMonthlyCard(dfAccount.getMonthlyCard()); //月结卡号
+        vo.setExpressProductCode(dfAccount.getExpressProductCode()); //物流产品编码
 
 
         vo.setLoginAccount(loginAccount); //代服系统登录账号
-        String callBackUrl = config.getCallBackUrl();
+        String callBackUrl = dfAccount.getCallBackUrl();
         if (StringUtils.isNotBlank(callBackUrl)) {
             vo.setCallBackUrl(callBackUrl); //订单下单后异步通知地址
         }
@@ -500,7 +699,7 @@ public class DfOrderServiceImpl implements IErpOrderService
             orderPayMethod = 2;
             // 货到付款金额 = 订单剩余支付金额
             vo.setCollectingMoney(fsStoreOrder.getPayRemain().doubleValue());
-            vo.setCollectionCardNumber(config.getMonthlyCard()); // 就是月结账号
+            vo.setCollectionCardNumber(dfAccount.getMonthlyCard()); // 就是月结账号
         }
         //订单付款方式 1:在线支付 2:货到付款
         //如果填了2,代收金额必须大于0,代收卡号必填
@@ -510,12 +709,12 @@ public class DfOrderServiceImpl implements IErpOrderService
 
         vo.setBuyerMessage(fsStoreOrder.getRemark()); //买家留言
 
-        vo.setSenderName(config.getSenderName()); //寄件人
-        vo.setSenderPhone(config.getSenderPhone()); //寄件人手机
-        vo.setSenderProvince(config.getSenderProvince());//寄件人省
-        vo.setSenderCity(config.getSenderCity());//寄件人市
-        vo.setSenderDistrict(config.getSenderDistrict());//寄件人区
-        vo.setSenderAddress(config.getSenderAddress());//寄件人地址
+        vo.setSenderName(dfAccount.getSenderName()); //寄件人
+        vo.setSenderPhone(dfAccount.getSenderPhone()); //寄件人手机
+        vo.setSenderProvince(dfAccount.getSenderProvince());//寄件人省
+        vo.setSenderCity(dfAccount.getSenderCity());//寄件人市
+        vo.setSenderDistrict(dfAccount.getSenderDistrict());//寄件人区
+        vo.setSenderAddress(dfAccount.getSenderAddress());//寄件人地址
         vo.setReceiverName(order.getReceiver_name());//收件人
         vo.setReceiverPhone(order.getReceiver_mobile()); //收件人手机
         vo.setReceiverTelephone(order.getReceiver_phone());//收件人电话 否
@@ -562,21 +761,6 @@ public class DfOrderServiceImpl implements IErpOrderService
         return vo;
     }
 
-    private @Nullable DFConfigVo getconfig(int index) {
-        List<DFConfigVo> dfConfigVos = getDfConfigVos();
-        DFConfigVo config = null;
-        if (dfConfigVos != null && !dfConfigVos.isEmpty()) {
-            config = dfConfigVos.get(index);
-        }
-        return config;
-    }
-
-    private @Nullable List<DFConfigVo> getDfConfigVos() {
-        FsSysConfig sysConfig = configUtil.getSysConfig();
-        String dfConfigVo = sysConfig.getDfAccounts();
-        List<DFConfigVo> dfConfigVos = JSON.parseArray(dfConfigVo, DFConfigVo.class);
-        return dfConfigVos;
-    }
 
 
     /**
@@ -584,34 +768,34 @@ public class DfOrderServiceImpl implements IErpOrderService
      *
      * @return
      */
-    private int getSFAccountIndex(Long orderId) {
+    private Long getSFAccountIndex(Long orderId) {
+        List<FsDfAccount> fsDfAccounts = fsDfAccountMapper.selectFsDfAccountList(null);
         if (orderId != null) {
             //查询是否选择erp账户
             FsStoreOrderDf temp = fsStoreOrderDfMapper.selectFsStoreOrderDfByOrderId(orderId);
             if (temp != null) {
-                FsSysConfig sysConfig = configUtil.getSysConfig();
-                String dfConfigVo = sysConfig.getDfAccounts();
-                List<DFConfigVo> dfConfigVos = JSON.parseArray(dfConfigVo, DFConfigVo.class);
-                if (dfConfigVos != null && !dfConfigVos.isEmpty()) {
-                    for (int i = 0; i < dfConfigVos.size(); i++) {
-                        if (temp.getLoginAccount().equals(dfConfigVos.get(i).getLoginAccount())) {
-                            return i;
+                if (fsDfAccounts != null && !fsDfAccounts.isEmpty()) {
+                    for (FsDfAccount fsDfAccount : fsDfAccounts) {
+                        if (temp.getLoginAccount().equals(fsDfAccount.getLoginAccount())) {
+                            return fsDfAccount.getId();
                         }
                     }
                 }
-                return dfConfigVos.indexOf(temp);
+                return null;
             }
         }
-        //默认用第一个
-        return 0;
-
+        if (fsDfAccounts != null && !fsDfAccounts.isEmpty()) {
+            return fsDfAccounts.get(0).getId();
+        } else {
+            return null;
+        }
     }
 
-    private void getOrderResult(Map<String, Object> map, Integer sfAccountIndex) {
+    private void getOrderResult(Map<String, Object> map, Long dfAccountId) {
         try {
             String status = map.get("exInterfaceType").toString();
             log.info("开始查询订单结果,参数为: {}", JSON.toJSONString(map));
-            String response = client.execute(RequestUrlEnum.ORDER_RESULT, map, sfAccountIndex);
+            String response = client.execute(RequestUrlEnum.ORDER_RESULT, map, dfAccountId);
             DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
             if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
                 log.info("查询订单结果,结果: {}", JSON.toJSONString(dfApiResponse));

+ 22 - 0
fs-service/src/main/java/com/fs/fastGpt/domain/FastGptPushTokenTotal.java

@@ -0,0 +1,22 @@
+package com.fs.fastGpt.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+@Data
+public class FastGptPushTokenTotal extends BaseEntity {
+
+    private Long id;
+    private Integer type;
+    private Long qwUserId;
+    private Long companyId;
+    @Excel(name = "公司名称")
+    private String companyName;
+    @Excel(name = "token消耗数")
+    private Long count;
+    private Integer status;
+    @Excel(name = "生成时间")
+    private String statTime;
+
+}

+ 1 - 0
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -974,6 +974,7 @@ public class AiHookServiceImpl implements AiHookService {
         wxwUploadCdnLinkFileDTO.setFilename(data.getUrl());
         wxwUploadCdnLinkFileDTO.setUuid(uuid);
         WxWorkResponseDTO<WxwUploadCdnLinkFileRespDTO> dto = wxWorkService.uploadCdnLinkFile(wxwUploadCdnLinkFileDTO, serverId);
+        log.info("语音数据:{}", JSON.toJSONString(dto));
         WxwUploadCdnLinkFileRespDTO voice = dto.getData();
         WxwSendCDNVoiceMsgDTO wxwSendCDNVoiceMsgDTO = new WxwSendCDNVoiceMsgDTO();
         wxwSendCDNVoiceMsgDTO.setSend_userid(sendId);

+ 2 - 2
fs-service/src/main/java/com/fs/fastgptApi/util/EventLogUtils.java

@@ -80,7 +80,7 @@ public class EventLogUtils {
 
 
         EventLogQueue.addEventLog(fastGptEventLog); // 入队
-        fastGptChatMsgService.insertFastGptEventLog(fastGptEventLog);
+        //fastGptChatMsgService.insertFastGptEventLog(fastGptEventLog);
     }
 
     /**
@@ -104,7 +104,7 @@ public class EventLogUtils {
         fastGptEventTokenLog.setQwUserId(user.getId());
         fastGptEventTokenLog.setCreateTime(new Date());
 
-        //EventLogQueue.addEventTokenLog(fastGptEventTokenLog); // 入队
+        EventLogQueue.addEventTokenLog(fastGptEventTokenLog); // 入队
         //fastGptChatMsgService.insertFastGptEventTokenLog(fastGptEventTokenLog);
     }
 

+ 74 - 0
fs-service/src/main/java/com/fs/his/domain/FsDfAccount.java

@@ -0,0 +1,74 @@
+package com.fs.his.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;
+
+import java.util.List;
+
+/**
+ * 代服账户对象 fs_df_account
+ *
+ * @author fs
+ * @date 2025-10-13
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsDfAccount extends BaseEntity{
+
+    @TableId
+    private Long id;
+
+    @Excel(name = "dfAppKey")
+    private String dfAppKey;
+
+    /** dfAppsecret */
+    @Excel(name = "dfAppsecret")
+    private String dfAppsecret;
+
+    /** 登录账号 */
+    @Excel(name = "登录账号")
+    private String loginAccount;
+
+    /** 回调地址 */
+    @Excel(name = "回调地址")
+    private String callBackUrl;
+
+    /** 月结账号 */
+    @Excel(name = "月结账号")
+    private String monthlyCard;
+
+    /** 物流产品编码 */
+    @Excel(name = "物流产品编码")
+    private String expressProductCode;
+
+    /** 寄件人姓名 */
+    @Excel(name = "寄件人姓名")
+    private String senderName;
+
+    /** 寄件人手机 */
+    @Excel(name = "寄件人手机")
+    private String senderPhone;
+
+    private String cityIds;
+
+    /** 寄件人省 */
+    @Excel(name = "寄件人省")
+    private String senderProvince;
+
+    /** 寄件人市 */
+    @Excel(name = "寄件人市")
+    private String senderCity;
+
+    /** 寄件人区 */
+    @Excel(name = "寄件人区")
+    private String senderDistrict;
+
+    /** 寄件人地址 */
+    @Excel(name = "寄件人地址")
+    private String senderAddress;
+
+
+}

+ 61 - 0
fs-service/src/main/java/com/fs/his/mapper/FsDfAccountMapper.java

@@ -0,0 +1,61 @@
+package com.fs.his.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsDfAccount;
+
+/**
+ * 代服账户Mapper接口
+ * 
+ * @author fs
+ * @date 2025-10-13
+ */
+public interface FsDfAccountMapper extends BaseMapper<FsDfAccount>{
+    /**
+     * 查询代服账户
+     * 
+     * @param id 代服账户主键
+     * @return 代服账户
+     */
+    FsDfAccount selectFsDfAccountById(Long id);
+
+    /**
+     * 查询代服账户列表
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 代服账户集合
+     */
+    List<FsDfAccount> selectFsDfAccountList(FsDfAccount fsDfAccount);
+
+    /**
+     * 新增代服账户
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 结果
+     */
+    int insertFsDfAccount(FsDfAccount fsDfAccount);
+
+    /**
+     * 修改代服账户
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 结果
+     */
+    int updateFsDfAccount(FsDfAccount fsDfAccount);
+
+    /**
+     * 删除代服账户
+     * 
+     * @param id 代服账户主键
+     * @return 结果
+     */
+    int deleteFsDfAccountById(Long id);
+
+    /**
+     * 批量删除代服账户
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsDfAccountByIds(Long[] ids);
+}

+ 14 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -410,4 +410,18 @@ public interface FsUserMapper
     Map<String, Object> countUserStats(
             UserStatisticsCommonParam param);
 
+    /**
+     * 统计用户领取红包数据(红包个数、红包金额)
+     * */
+    ExternalRedPacketStatsVO countExternalRedPacketStats(UserStatisticsCommonParam param);
+
+    /**
+     * 统计用户答题数据(答题次数、正确次数)
+     * */
+    ExternalAnswerStatsVO countExternalAnswerStats(UserStatisticsCommonParam param);
+
+    /**
+     * 统计用户看课数据(课程观看次数、课程完播次数)
+     * */
+    ExternalWatchStatsVO countExternalWatchStats(UserStatisticsCommonParam param);
 }

+ 61 - 0
fs-service/src/main/java/com/fs/his/service/IFsDfAccountService.java

@@ -0,0 +1,61 @@
+package com.fs.his.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsDfAccount;
+
+/**
+ * 代服账户Service接口
+ * 
+ * @author fs
+ * @date 2025-10-13
+ */
+public interface IFsDfAccountService extends IService<FsDfAccount>{
+    /**
+     * 查询代服账户
+     * 
+     * @param id 代服账户主键
+     * @return 代服账户
+     */
+    FsDfAccount selectFsDfAccountById(Long id);
+
+    /**
+     * 查询代服账户列表
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 代服账户集合
+     */
+    List<FsDfAccount> selectFsDfAccountList(FsDfAccount fsDfAccount);
+
+    /**
+     * 新增代服账户
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 结果
+     */
+    int insertFsDfAccount(FsDfAccount fsDfAccount);
+
+    /**
+     * 修改代服账户
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 结果
+     */
+    int updateFsDfAccount(FsDfAccount fsDfAccount);
+
+    /**
+     * 批量删除代服账户
+     * 
+     * @param ids 需要删除的代服账户主键集合
+     * @return 结果
+     */
+    int deleteFsDfAccountByIds(Long[] ids);
+
+    /**
+     * 删除代服账户信息
+     * 
+     * @param id 代服账户主键
+     * @return 结果
+     */
+    int deleteFsDfAccountById(Long id);
+}

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

@@ -125,4 +125,5 @@ public interface IFsStorePaymentService
 
 
     R payment(FsStorePaymentPayParam payParam);
+    String payConfirm(String payCode,String tradeNo,String bankTransactionId,String bankSerialNo);
 }

+ 94 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsDfAccountServiceImpl.java

@@ -0,0 +1,94 @@
+package com.fs.his.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.his.mapper.FsDfAccountMapper;
+import com.fs.his.domain.FsDfAccount;
+import com.fs.his.service.IFsDfAccountService;
+
+/**
+ * 代服账户Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-10-13
+ */
+@Service
+public class FsDfAccountServiceImpl extends ServiceImpl<FsDfAccountMapper, FsDfAccount> implements IFsDfAccountService {
+
+    /**
+     * 查询代服账户
+     * 
+     * @param id 代服账户主键
+     * @return 代服账户
+     */
+    @Override
+    public FsDfAccount selectFsDfAccountById(Long id)
+    {
+        return baseMapper.selectFsDfAccountById(id);
+    }
+
+    /**
+     * 查询代服账户列表
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 代服账户
+     */
+    @Override
+    public List<FsDfAccount> selectFsDfAccountList(FsDfAccount fsDfAccount)
+    {
+        return baseMapper.selectFsDfAccountList(fsDfAccount);
+    }
+
+    /**
+     * 新增代服账户
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 结果
+     */
+    @Override
+    public int insertFsDfAccount(FsDfAccount fsDfAccount)
+    {
+        fsDfAccount.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsDfAccount(fsDfAccount);
+    }
+
+    /**
+     * 修改代服账户
+     * 
+     * @param fsDfAccount 代服账户
+     * @return 结果
+     */
+    @Override
+    public int updateFsDfAccount(FsDfAccount fsDfAccount)
+    {
+        fsDfAccount.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateFsDfAccount(fsDfAccount);
+    }
+
+    /**
+     * 批量删除代服账户
+     * 
+     * @param ids 需要删除的代服账户主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsDfAccountByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsDfAccountByIds(ids);
+    }
+
+    /**
+     * 删除代服账户信息
+     * 
+     * @param id 代服账户主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsDfAccountById(Long id)
+    {
+        return baseMapper.deleteFsDfAccountById(id);
+    }
+}

+ 8 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderServiceImpl.java

@@ -16,6 +16,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.service.impl.SmsServiceImpl;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
@@ -815,6 +816,13 @@ public class FsInquiryOrderServiceImpl implements IFsInquiryOrderService
             map.setPayTime(new Date());
             map.setTradeNo(tradeNo);
             fsInquiryOrderMapper.updateFsInquiryOrder(map);
+            // 木易华康特殊处理(支付成功医生自动接单)
+            if (CloudHostUtils.hasCloudHostName("木易华康")&&order.getDoctorId()!=null) {
+                FsInquiryOrderReceiveParam fsInquiryOrderReceiveParam = new FsInquiryOrderReceiveParam();
+                fsInquiryOrderReceiveParam.setOrderId(order.getOrderId());
+                fsInquiryOrderReceiveParam.setDoctorId(order.getDoctorId());
+                receiveOrder(fsInquiryOrderReceiveParam);
+            }
             try {
                 FsUser fsUser = userMapper.selectFsUserByUserId(order.getUserId());
                 imService.addAccount(fsUser.getUserId(),fsUser.getAvatar(),fsUser.getNickName());

+ 9 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java

@@ -50,6 +50,7 @@ import com.fs.his.vo.FsPackageOrderListVO;
 import com.fs.his.vo.FsPackageOrderVO;
 import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
+import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
 import com.fs.huifuPay.service.HuiFuService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.oss.CloudStorageService;
@@ -1066,6 +1067,14 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
                         o.setTransAmt(storePayment.getPayMoney().toString());
                         o.setGoodsDesc("套餐包订单支付");
                         o.setAppId(appId);
+                        //公司分账
+                        try {
+                            HuiFuUtils.doDiv(o,fsPackageOrder.getCompanyId());
+                            //存储分账明细
+                            HuiFuUtils.saveDivItem(o, fsPackageOrder.getOrderSn(), storePayment.getPayCode());
+                        } catch (Exception e) {
+                            logger.error("-------------分账出错:{}", e.getMessage());
+                        }
                         HuifuCreateOrderResult result = huiFuService.createOrder(o);
                         logger.info("创建汇付支付:"+result);
                         FsStorePayment mt=new FsStorePayment();

+ 165 - 53
fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java

@@ -12,14 +12,20 @@ import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.utils.spring.SpringUtils;
+import com.fs.company.domain.CompanyDivItem;
 import com.fs.company.param.FsStoreStatisticsParam;
+import com.fs.company.service.ICompanyDivConfigService;
+import com.fs.company.service.ICompanyDivItemService;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.impl.CompanyServiceImpl;
 import com.fs.company.vo.FsStoreOrderStatisticsVO;
@@ -159,6 +165,9 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     @Autowired
     private IFsStoreOrderLogsService fsStoreOrderLogsService;
 
+    @Autowired
+    private ICompanyDivItemService companyDivItemService;
+
     /**
      * 查询售后记录
      *
@@ -288,7 +297,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
         //添加订单日志
         fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.PLATFORM_REVIEW_SALES.getValue(),
-                logs.getOperator()+" "+FsStoreOrderLogEnum.PLATFORM_REVIEW_SALES.getDesc());
+                logs.getOperator() + " " + FsStoreOrderLogEnum.PLATFORM_REVIEW_SALES.getDesc());
         return 1;
     }
 
@@ -317,7 +326,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
         //添加订单日志
         fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.PLATFORM_REVIEW_CANCEL.getValue(),
-                logs.getOperator() + " " +FsStoreOrderLogEnum.PLATFORM_REVIEW_CANCEL.getDesc());
+                logs.getOperator() + " " + FsStoreOrderLogEnum.PLATFORM_REVIEW_CANCEL.getDesc());
 //        FsStoreOrderLogs Logs = new FsStoreOrderLogs();
 //        Logs.setChangeMessage("已取消退款");
 //        Logs.setOrderId(order.getOrderId());
@@ -328,47 +337,50 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         or.setOrderId(order.getOrderId());
         or.setStatus(order.getOrderStatus());
         fsStoreOrderMapper.updateFsStoreOrder(or);
-        if (order.getOrderStatus() == 2) {
-            FsStoreOrder fsStoreOrder = fsStoreOrderMapper.selectFsStoreOrderByOrderId(order.getOrderId());
-            String newOrderSn = OrderCodeUtils.getOrderSn();
-            if (StringUtils.isEmpty(newOrderSn)) {
-                newOrderSn = OrderCodeUtils.getOrderSn();
-            }
-            FsStoreOrder newOrder = new FsStoreOrder();
-            newOrder.setOrderId(order.getOrderId());
-            newOrder.setOrderCode(newOrderSn);
-            fsStoreOrderMapper.updateFsStoreOrder(newOrder);
-            or.setOrderCode(newOrderSn);
-            if (fsStoreOrder.getPackageOrderId() != null) {
-                FsPackageOrder fsPackageOrder = new FsPackageOrder();
-                fsPackageOrder.setOrderId(fsStoreOrder.getPackageOrderId());
-                fsPackageOrder.setOrderSn(newOrderSn);
-                fsPackageOrderMapper.updateFsPackageOrder(fsPackageOrder);
-            }
-            if (fsStoreOrder.getInquiryOrderId() != null) {
-                FsInquiryOrder fsInquiryOrder = new FsInquiryOrder();
-                fsInquiryOrder.setOrderSn(newOrderSn);
-                fsInquiryOrder.setOrderId(fsStoreOrder.getInquiryOrderId());
-                fsInquiryOrderMapper.updateFsInquiryOrder(fsInquiryOrder);
-            }
+        //金牛订单取消售后审核不需要操作
+        if (!CloudHostUtils.hasCloudHostName("金牛名医", "康年堂")) {
+            if (order.getOrderStatus() == 2) {
+                FsStoreOrder fsStoreOrder = fsStoreOrderMapper.selectFsStoreOrderByOrderId(order.getOrderId());
+                String newOrderSn = OrderCodeUtils.getOrderSn();
+                if (StringUtils.isEmpty(newOrderSn)) {
+                    newOrderSn = OrderCodeUtils.getOrderSn();
+                }
+                FsStoreOrder newOrder = new FsStoreOrder();
+                newOrder.setOrderId(order.getOrderId());
+                newOrder.setOrderCode(newOrderSn);
+                fsStoreOrderMapper.updateFsStoreOrder(newOrder);
+                or.setOrderCode(newOrderSn);
+                if (fsStoreOrder.getPackageOrderId() != null) {
+                    FsPackageOrder fsPackageOrder = new FsPackageOrder();
+                    fsPackageOrder.setOrderId(fsStoreOrder.getPackageOrderId());
+                    fsPackageOrder.setOrderSn(newOrderSn);
+                    fsPackageOrderMapper.updateFsPackageOrder(fsPackageOrder);
+                }
+                if (fsStoreOrder.getInquiryOrderId() != null) {
+                    FsInquiryOrder fsInquiryOrder = new FsInquiryOrder();
+                    fsInquiryOrder.setOrderSn(newOrderSn);
+                    fsInquiryOrder.setOrderId(fsStoreOrder.getInquiryOrderId());
+                    fsInquiryOrderMapper.updateFsInquiryOrder(fsInquiryOrder);
+                }
 
-            List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2, fsStoreOrder.getOrderId());
-            if (fsStoreOrder.getPackageOrderId() != null) {
-                payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3, fsStoreOrder.getPackageOrderId());
-            }
-            for (FsStorePayment payment : payments) {
-                FsStorePayment fsStorePayment = new FsStorePayment();
-                fsStorePayment.setPaymentId(payment.getPaymentId());
-                fsStorePayment.setBusinessCode(newOrderSn);
-                fsStorePaymentMapper.updateFsStorePayment(fsStorePayment);
-            }
+                List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2, fsStoreOrder.getOrderId());
+                if (fsStoreOrder.getPackageOrderId() != null) {
+                    payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3, fsStoreOrder.getPackageOrderId());
+                }
+                for (FsStorePayment payment : payments) {
+                    FsStorePayment fsStorePayment = new FsStorePayment();
+                    fsStorePayment.setPaymentId(payment.getPaymentId());
+                    fsStorePayment.setBusinessCode(newOrderSn);
+                    fsStorePaymentMapper.updateFsStorePayment(fsStorePayment);
+                }
 
-            try {
-                fsStoreOrderService.createOmsOrder(order.getOrderId());
-            } catch (ParseException e) {
+                try {
+                    fsStoreOrderService.createOmsOrder(order.getOrderId());
+                } catch (ParseException e) {
 
-            }
+                }
 
+            }
         }
 
 
@@ -397,7 +409,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
         //添加订单日志
         fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.FINANCE_REVIEW_SALES.getValue(),
-                logs.getOperator() + " " +FsStoreOrderLogEnum.FINANCE_REVIEW_SALES.getDesc());
+                logs.getOperator() + " " + FsStoreOrderLogEnum.FINANCE_REVIEW_SALES.getDesc());
         //更改订单状态
         FsStoreOrder or = new FsStoreOrder();
         or.setOrderId(order.getOrderId());
@@ -459,7 +471,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         }
         if (payments != null && payments.size() > 0) {
             FsStorePayment payment = payments.get(0);
-            if (reMoney.compareTo(payment.getPayMoney())>0) {
+            if (reMoney.compareTo(payment.getPayMoney()) > 0) {
                 return 0; //退款金额不能大于实际支付金额
             }
             String json = configService.selectConfigByKey("his.pay");
@@ -545,6 +557,16 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
                 request.setReqSeqId("refund-" + payment.getPayCode());
                 Map<String, Object> extendInfoMap = new HashMap<>();
                 extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
+
+                //处理分账退款
+                //1.判断是否是全额退款
+                CompanyDivItem companyDivItem = null;
+                try {
+                    companyDivItem = doRefundDiv(fsStoreOrder, reMoney, payment, extendInfoMap);
+                } catch (Exception e) {
+                    logger.error("-----------------分账退款处理失败{}", e.getMessage());
+                }
+
                 request.setExtendInfo(extendInfoMap);
                 HuiFuRefundResult refund = huiFuService.refund(request);
                 logger.info("订单退款返回结果:退款订单id:" + order.getOrderId() + refund);
@@ -555,6 +577,15 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
                     paymentMap.setRefundTime(DateUtils.getNowDate());
                     paymentMap.setRefundMoney(payment.getPayMoney());
                     fsStorePaymentMapper.updateFsStorePayment(paymentMap);
+                    try {
+                        //处理分账退款明细表
+                        if (companyDivItem !=null && companyDivItem.getId() != null) {
+                            companyDivItem.setIsRefund(1);
+                            companyDivItemService.updateCompanyDivItem(companyDivItem);
+                        }
+                    } catch (Exception e) {
+                        logger.error("-----------------分账退款明细处理失败{}", e.getMessage());
+                    }
                 } else {
                     throw new CustomException("退款请求失败" + refund.getResp_desc());
                 }
@@ -571,17 +602,17 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
                     Integer erpType = sysConfig.getErpType();
                     if (erpType == 1) {
                         erpOrderService.refundUpdate(request);
-                    }else if (erpType == 2) {
+                    } else if (erpType == 2) {
                         //旺店通
                         wdtErpOrderService.refundUpdate(request);
                     } else if (erpType == 3) {
                         //瀚智
                         hzOMSerpOrderService.refundUpdate(request);
-                    }  else if (erpType == 4) {
+                    } else if (erpType == 4) {
                         dfOrderService.refundUpdate(request);
-                    }else if(erpType == 5){
+                    } else if (erpType == 5) {
                         jSTOrderService.refundUpdate(request);
-                    }else if(erpType == 6){
+                    } else if (erpType == 6) {
                         k9OrderService.refundUpdate(request);
                     }
                 }
@@ -598,6 +629,87 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         return i;
     }
 
+    /**
+     * 处理分账退款
+     *
+     * @param fsStoreOrder
+     * @param reMoney
+     * @param payment
+     * @param extendInfoMap
+     */
+    private CompanyDivItem doRefundDiv(FsStoreOrder fsStoreOrder, BigDecimal reMoney, FsStorePayment payment, Map<String, Object> extendInfoMap) {
+        CompanyDivItem companyDivItem = null;
+        Long companyId = fsStoreOrder.getCompanyId();
+        if (companyId != null) {
+            companyDivItem = companyDivItemService.selectCompanyDivItemByPayCode(payment.getPayCode());
+            if (fsStoreOrder.getPayPrice().compareTo(reMoney) > 0) {
+                //部分退款
+                if (companyDivItem != null) {
+                    SysConfigMapper sysConfigMapper = SpringUtils.getBean(SysConfigMapper.class);
+                    SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.pay");
+                    FsPayConfig fsPayConfig = new Gson().fromJson(sysConfig.getConfigValue(), FsPayConfig.class);
+                    String defaultHuiFuId = fsPayConfig.getHuifuId(); //默认汇付id
+
+                    String detail = companyDivItem.getDetail();
+                    if (StringUtils.isNotBlank(detail)) {
+                        JSONObject acctSplitBunch = JSONObject.parseObject(detail);
+                        if (acctSplitBunch.get("percentage_flag") != null) {
+                            List<Map<String, Object>> param = new ArrayList(); //传入汇付参数
+
+                            String percentageFlag = acctSplitBunch.get("percentage_flag").toString();
+                            Object acctInfosObj = acctSplitBunch.get("acct_infos");
+                            if (acctInfosObj != null) {
+                                if ("Y".equals(percentageFlag)) {
+                                    // 百分比分账
+                                    List<Map> acctInfos = JSON.parseArray(acctInfosObj.toString(), Map.class);
+                                    if (acctInfos != null && !acctInfos.isEmpty()) {
+                                        BigDecimal remainMoney = reMoney;
+
+                                        for (Map acctInfo : acctInfos) {
+                                            String percentageDiv = acctInfo.get("percentage_div").toString();
+                                            BigDecimal divAmt = reMoney.multiply(BigDecimal.valueOf(Float.valueOf(percentageDiv)).multiply(BigDecimal.valueOf(0.01))).setScale(2, BigDecimal.ROUND_HALF_UP);
+                                            if (divAmt.compareTo(BigDecimal.ZERO) <= 0) {
+                                                continue;
+                                            }
+                                            remainMoney = remainMoney.subtract(divAmt);
+                                            String huiFuId = acctInfo.get("huifu_id").toString();
+                                            Map<String, Object> map = new HashMap<>();
+                                            map.put("div_amt", divAmt);
+                                            map.put("huifu_id", huiFuId);
+//                                                part_loan_amt	垫资金额
+                                            param.add(map);
+                                            if (remainMoney.compareTo(BigDecimal.ZERO) >= 0) {
+                                                break;
+                                            }
+                                        }
+                                        if (remainMoney.compareTo(BigDecimal.ZERO) >= 0) {
+                                            BigDecimal divAmt = reMoney.subtract(remainMoney).setScale(2, BigDecimal.ROUND_HALF_UP);
+                                            Map<String, Object> map = new HashMap<>();
+                                            map.put("div_amt", divAmt);
+                                            map.put("huifu_id", defaultHuiFuId);
+                                            param.add(map);
+                                        }
+                                    }
+
+                                } else if ("N".equals(percentageFlag)) {
+                                    // 金额分账 目前按照排序从第一个开始扣 扣到满足退款金额
+                                }
+                                if (!param.isEmpty()) {
+                                    extendInfoMap.put("acct_split_bunch", JSON.toJSONString(param));
+                                }
+                            }
+
+                        }
+
+
+                    }
+                }
+            }
+        }
+        return companyDivItem;
+
+    }
+
     @Override
     public List<FsStoreAfterSalesExcelVO> selectFsStoreAfterSalesExcelListVO(FsStoreAfterSalesParam fsStoreAfterSales) {
         return fsStoreAfterSalesMapper.selectFsStoreAfterSalesExcelListVO(fsStoreAfterSales);
@@ -738,17 +850,17 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
             Integer erpType = sysConfig.getErpType();
             BaseResponse response = null;
             if (erpType == 1) {
-                response =  erpOrderService.refundUpdate(request);
-            }else if (erpType == 2) {
+                response = erpOrderService.refundUpdate(request);
+            } else if (erpType == 2) {
                 //旺店通
                 response = wdtErpOrderService.refundUpdate(request);
-            }  else if (erpType == 3) {
+            } else if (erpType == 3) {
                 //瀚智
-                response =  hzOMSerpOrderService.refundUpdate(request);
+                response = hzOMSerpOrderService.refundUpdate(request);
             } else if (erpType == 4) {
-                response =  dfOrderService.refundUpdate(request);
-            }else if(erpType == 5){
-                response=jSTOrderService.refundUpdate(request);
+                response = dfOrderService.refundUpdate(request);
+            } else if (erpType == 5) {
+                response = jSTOrderService.refundUpdate(request);
             }
             if (response.getSuccess()) {
                 return R.ok();
@@ -893,7 +1005,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
         //添加订单日志
         fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.WAREHOUSE_REVIEW_SALES.getValue(),
-                logs.getOperator() + " " +FsStoreOrderLogEnum.WAREHOUSE_REVIEW_SALES.getDesc());
+                logs.getOperator() + " " + FsStoreOrderLogEnum.WAREHOUSE_REVIEW_SALES.getDesc());
         return 1;
     }
 

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

@@ -70,6 +70,7 @@ import com.fs.huifuPay.domain.HuiFuRefundResult;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
+import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
 import com.fs.huifuPay.service.HuiFuService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
@@ -136,6 +137,8 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
     @Autowired
     private FsStorePaymentMapper fsStorePaymentMapper;
     @Autowired
+    private FsStorePaymentScrmMapper fsStorePaymentScrmMapper;
+    @Autowired
     private TzBankService tzBankService;
     @Autowired
     private IFsInquiryOrderService inquiryOrderService;
@@ -1484,7 +1487,40 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
             return R.error("微信接口调用失败: " + e.getMessage());
         }
     }
-
+    @Override
+    @Transactional
+    public String payConfirm(String payCode, String tradeNo, String bankTransactionId, String bankSerialNo) {
+        try {
+            //更新订单状态
+            FsStorePayment storePayment=fsStorePaymentMapper.selectFsStorePaymentByPaymentCode(payCode);
+            if(!storePayment.getStatus().equals(0)){
+                return "";
+            }
+            storePayment.setStatus(1);
+            storePayment.setPayTime(new Date());
+            storePayment.setTradeNo(tradeNo);
+            storePayment.setBankSerialNo(bankSerialNo);
+            storePayment.setBankTransactionId(bankTransactionId);
+            fsStorePaymentMapper.updateFsStorePayment(storePayment);
+            //增加佣金
+//        if(storePayment.getCompanyId()!=null&&storePayment.getCompanyId()>0){
+//            companyService.addCompanyPaymentMoney(storePayment);
+//        }
+        } catch (Exception e) {
+            //更新订单状态
+            FsStorePaymentScrm storePayment=fsStorePaymentScrmMapper.selectFsStorePaymentByPaymentCode(payCode);
+            if(!storePayment.getStatus().equals(0)){
+                return "";
+            }
+            storePayment.setStatus(1);
+            storePayment.setPayTime(new Date());
+            storePayment.setTradeNo(tradeNo);
+            storePayment.setBankSerialNo(bankSerialNo);
+            storePayment.setBankTransactionId(bankTransactionId);
+            fsStorePaymentScrmMapper.updateFsStorePayment(storePayment);
+        }
+        return "SUCCESS";
+    }
     @Override
     public R payment(FsStorePaymentPayParam payParam) {
         FsUser user = userMapper.selectFsUserById(payParam.getUserId());
@@ -1634,6 +1670,14 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         o.setTransAmt(storePayment.getPayMoney().toString());
         o.setGoodsDesc("商城订单支付");
         o.setAppId(appId);
+        //公司分账
+        try {
+            HuiFuUtils.doDiv(o,company.getCompanyId());
+            //存储分账明细
+            HuiFuUtils.saveDivItem(o, storePayment.getPayCode(), storePayment.getPayCode());
+        } catch (Exception e) {
+            logger.error("-------------微信收款分账出错:{}", e.getMessage());
+        }
         HuifuCreateOrderResult result = huiFuService.createOrder(o);
         if(result.getResp_code()!=null&&(result.getResp_code().equals("00000000")||result.getResp_code().equals("00000100"))){
             FsStorePayment mt=new FsStorePayment();

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

@@ -13,6 +13,9 @@ public class FsStoreOrderExcelVO {
     /** 订单号 */
     @Excel(name = "订单号")
     private String orderCode;
+    //小程序名称
+    @Excel(name = "小程序名称")
+    private String miniProgramName;
     @Excel(name = "处方单号")
     private String prescribeCode;
     @Excel(name = "公司名称")

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsPrescribeScrm.java

@@ -2,6 +2,7 @@ package com.fs.hisStore.domain;
 
 import java.util.Date;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
@@ -31,6 +32,7 @@ public class FsPrescribeScrm extends BaseEntity
 
     /** 订单ID */
     @Excel(name = "订单ID")
+    @TableField (exist = false)
     private Long orderId;
 
     /** 用户ID */

+ 1 - 4
fs-service/src/main/java/com/fs/hisStore/enums/SysConfigEnum.java

@@ -85,10 +85,7 @@ public enum SysConfigEnum {
     //红包流量,joinTime.switch.config
     JOIN_TIME_SWITCH_CONFIG("joinTime.switch.config", "红包流量"),
     //签到配置,store.sign
-    SIGN_CONFIG("store.sign", "签到配置"),
-    //商城支付配置,store.pay
-    STORE_PAY_CONFIG("store.pay", "商城支付配置");
-
+    SIGN_CONFIG("store.sign", "签到配置");
     private final String key;
     private final String name;
 

+ 38 - 3
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java

@@ -148,7 +148,27 @@ public interface FsStoreOrderItemScrmMapper
     @Select({"<script> " +
             "select count(0) from fs_store_order_item_scrm i left join fs_store_order_scrm o on o.id=i.order_id left join fs_user u on o.user_id=u.user_id  " +
             " left join fs_store_product_package_scrm p on o.package_id=p.package_id left join company c on c.company_id=o.company_id left join company_user cu on cu.user_id=o.company_user_id left join company_tcm_schedule cts on cts.id = o.schedule_id " +
+            "LEFT JOIN fs_store_order_df df on df.order_id=o.id\n" +
+            "        <if test=\"maps.coursePlaySourceConfigId != null\">\n" +
+            "            LEFT JOIN (\n" +
+            "            SELECT\n" +
+            "            sp.*,\n" +
+            "            ROW_NUMBER() OVER (PARTITION BY sp.business_code ORDER BY sp.create_time DESC) as rn\n" +
+            "            FROM fs_store_payment_scrm sp\n" +
+            "            WHERE sp.business_code IS NOT NULL\n" +
+            "            ) sp_latest ON sp_latest.business_code = o.order_code AND sp_latest.rn = 1\n" +
+            "            LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id\n" +
+            "        </if>" +
             "where 1=1 " +
+            "<if test=\"maps.coursePlaySourceConfigId != null\">\n" +
+            "                and csc.id = #{maps.coursePlaySourceConfigId}\n" +
+            "            </if>\n" +
+            "            <if test=\"maps.orderCodes != null  and maps.orderCodes.size > 0\">\n" +
+            "                and o.order_code in\n" +
+            "                <foreach collection=\"maps.orderCodes\" item=\"orderCode\" open=\"(\" close=\")\" separator=\",\">\n" +
+            "                    #{orderCode}\n" +
+            "                </foreach>\n" +
+            "            </if>" +
             "<if test = 'maps.orderCode != null and  maps.orderCode !=\"\"    '> " +
             "and o.order_code like CONCAT('%',#{maps.orderCode},'%') " +
             "</if>" +
@@ -167,9 +187,15 @@ public interface FsStoreOrderItemScrmMapper
             "<if test = 'maps.userPhone != null and  maps.userPhone !=\"\"     '> " +
             "and o.user_phone like CONCAT('%',#{maps.userPhone},'%') " +
             "</if>" +
-            "<if test = 'maps.status != null    '> " +
-            "and o.status =#{maps.status} " +
-            "</if>" +
+            "<if test=\"maps.status != null and maps.status != 6\">\n" +
+            "                and o.status = #{maps.status}\n" +
+            "            </if>\n" +
+            "            <if test=\"maps.status == 6\">\n" +
+            "                and o.`status`= 1\n" +
+            "\n" +
+            "                and  (o.extend_order_id is null or  o.extend_order_id like '')\n" +
+            "            </if>" +
+
             "<if test = 'maps.companyId != null    '> " +
             "and o.company_id =#{maps.companyId} " +
             "</if>" +
@@ -213,6 +239,15 @@ public interface FsStoreOrderItemScrmMapper
             "<if test = 'maps.scheduleId != null    '> " +
             "and o.schedule_id =#{maps.scheduleId} " +
             "</if>" +
+            "<if test=\"maps.erpPhoneNumber != null and maps.erpPhoneNumber != ''\">\n" +
+            "                and o.erp_phone like concat(#{maps.erpPhoneNumber},'%')\n" +
+            "            </if>\n" +
+            "            <if test=\"maps.erpAccount != null and maps.erpAccount != '未分拣' and maps.erpAccount != ''\">\n" +
+            "                and df.login_account like #{maps.erpAccount}\n" +
+            "            </if>\n" +
+            "            <if test=\"maps.erpAccount == '未分拣'\">\n" +
+            "                and ( df.login_account is null or df.login_account like '')\n" +
+            "            </if>" +
             " order by o.id desc "+
             "</script>"})
     Long itemsCount(@Param("maps")FsStoreOrderParam fsStoreOrder);

+ 261 - 103
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -1,5 +1,10 @@
 package com.fs.hisStore.mapper;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 import com.alibaba.fastjson.JSONObject;
 import com.fs.api.param.OrderListParam;
 import com.fs.api.vo.OrderListVO;
@@ -78,108 +83,108 @@ public interface FsStoreOrderScrmMapper
      */
     public int deleteFsStoreOrderByIds(Long[] ids);
 
-    @Select({"<script> " +
-            "select o.*,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber   from fs_store_order_scrm o left join fs_user u on o.user_id=u.user_id  left join company c on c.company_id=o.company_id left join company_user cu on cu.user_id=o.company_user_id  " +
-            "<if test = 'maps.productName != null and  maps.productName !=  \"\" '> " +
-            "left join fs_store_order_item_scrm oi on o.id = oi.order_id "+
-            "left join fs_store_product_scrm fsp on fsp.product_id = oi.product_id"+
-            "</if>" +
-            "where o.is_del = 0 " +
-            "<if test = 'maps.orderCode != null and  maps.orderCode !=\"\"    '> " +
-            "and o.order_code like CONCAT('%',#{maps.orderCode},'%') " +
-            "</if>" +
-            "<if test = 'maps.isPayRemain != null      '> " +
-            "and o.is_pay_remain =#{maps.isPayRemain} " +
-            "</if>" +
-            "<if test = 'maps.userId != null      '> " +
-            "and o.user_id =#{maps.userId} " +
-            "</if>" +
-            "<if test = 'maps.deliveryId != null  and  maps.deliveryId !=\"\"    '> " +
-            "and o.delivery_id =#{maps.deliveryId} " +
-            "</if>" +
-            "<if test = 'maps.nickname != null and  maps.nickname !=\"\"     '> " +
-            "and u.nickname like CONCAT('%',#{maps.nickname},'%') " +
-            "</if>" +
-            "<if test = 'maps.realName != null and  maps.realName !=\"\"     '> " +
-            "and o.real_name like CONCAT('%',#{maps.realName},'%') " +
-            "</if>" +
-            "<if test = 'maps.phone != null and  maps.phone !=\"\"     '> " +
-            "and u.phone like CONCAT('%',#{maps.phone},'%') " +
-            "</if>" +
-            "<if test = 'maps.userPhone != null and  maps.userPhone !=\"\"     '> " +
-            "and o.user_phone like CONCAT('%',#{maps.userPhone},'%') " +
-            "</if>" +
-            "<if test = 'maps.status != null    '> " +
-            "and o.status =#{maps.status} " +
-            "</if>" +
-            "<if test = 'maps.isUpload != null and maps.isUpload == 0    '> " +
-            "and o.certificates is null  " +
-            "</if>" +
-            "<if test = 'maps.isUpload != null and maps.isUpload == 1    '> " +
-            "and o.certificates is not null " +
-            "</if>" +
-            "<if test = 'maps.deliveryStatus != null    '> " +
-            "and o.delivery_status =#{maps.deliveryStatus} " +
-            "</if>" +
-            "<if test = 'maps.deliveryPayStatus != null    '> " +
-            "and o.delivery_pay_status =#{maps.deliveryPayStatus} " +
-            "</if>" +
-            "<if test = 'maps.companyId != null    '> " +
-            "and o.company_id =#{maps.companyId} " +
-            "</if>" +
-            "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
-            "and o.company_id is null " +
-            "</if>" +
-            "<if test = 'maps.notHealth != null '> " +
-            "and o.company_id is not null " +
-            "</if>" +
-            "<if test = 'maps.companyUserId != null    '> " +
-            "and o.company_user_id =#{maps.companyUserId} " +
-            "</if>" +
-            "<if test = 'maps.companyUserNickName != null and  maps.companyUserNickName !=  \"\" '> " +
-            "and cu.nick_name like concat('%', #{maps.companyUserNickName}, '%') " +
-            "</if>" +
-            "<if test = 'maps.productName != null and  maps.productName !=  \"\" '> " +
-            "and fsp.product_name like concat('%', #{maps.productName}, '%') " +
-            "</if>" +
-            "<if test = 'maps.orderType != null    '> " +
-            "and o.order_type =#{maps.orderType} " +
-            "</if>" +
-            "<if test = 'maps.payType != null    '> " +
-            "and o.pay_type =#{maps.payType} " +
-            "</if>" +
-            "<if test = 'maps.scheduleId != null    '> " +
-            "and o.schedule_id =#{maps.scheduleId} " +
-            "</if>" +
-            "<if test = 'maps.createTimeList != null    '> " +
-            " AND date_format(o.create_time,'%y%m%d') &gt;= date_format(#{maps.createTimeList[0]},'%y%m%d') " +
-            " AND date_format(o.create_time,'%y%m%d') &lt;= date_format(#{maps.createTimeList[1]},'%y%m%d') " +
-            "</if>" +
-
-            "<if test = 'maps.deliverySendTimeList != null    '> " +
-            " AND date_format(o.delivery_send_time,'%y%m%d') &gt;= date_format(#{maps.deliverySendTimeList[0]},'%y%m%d') " +
-            " AND date_format(o.delivery_send_time,'%y%m%d') &lt;= date_format(#{maps.deliverySendTimeList[1]},'%y%m%d') " +
-            "</if>" +
-            "<if test = 'maps.paidStatus != null    '> " +
-            "and o.paid =#{maps.paidStatus} " +
-            "</if>" +
-            "<if test = 'maps.payTimeList != null    '> " +
-            " AND date_format(o.pay_time,'%y%m%d') &gt;= date_format(#{maps.payTimeList[0]},'%y%m%d') " +
-            " AND date_format(o.pay_time,'%y%m%d') &lt;= date_format(#{maps.payTimeList[1]},'%y%m%d') " +
-            "</if>" +
-            "<if test = 'maps.deliveryImportTimeList != null    '> " +
-            " AND date_format(o.delivery_import_time,'%y%m%d') &gt;= date_format(#{maps.deliveryImportTimeList[0]},'%y%m%d') " +
-            " AND date_format(o.delivery_import_time,'%y%m%d') &lt;= date_format(#{maps.deliveryImportTimeList[1]},'%y%m%d') " +
-            "</if>" +
-            "<if test = 'maps.deptId != null    '> " +
-            "  AND (o.dept_id = #{maps.deptId} OR o.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) )) " +
-            "</if>" +
-            " ${maps.params.dataScope} "+
-            "<if test = 'maps.productName != null and  maps.productName !=  \"\" '> " +
-            " group by o.id "+
-            "</if>" +
-            " order by o.id desc "+
-            "</script>"})
+//    @Select({"<script> " +
+//            "select o.*,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber   from fs_store_order_scrm o left join fs_user u on o.user_id=u.user_id  left join company c on c.company_id=o.company_id left join company_user cu on cu.user_id=o.company_user_id  " +
+//            "<if test = 'maps.productName != null and  maps.productName !=  \"\" '> " +
+//            "left join fs_store_order_item_scrm oi on o.id = oi.order_id "+
+//            "left join fs_store_product_scrm fsp on fsp.product_id = oi.product_id"+
+//            "</if>" +
+//            "where 1=1 " +
+//            "<if test = 'maps.orderCode != null and  maps.orderCode !=\"\"    '> " +
+//            "and o.order_code like CONCAT('%',#{maps.orderCode},'%') " +
+//            "</if>" +
+//            "<if test = 'maps.isPayRemain != null      '> " +
+//            "and o.is_pay_remain =#{maps.isPayRemain} " +
+//            "</if>" +
+//            "<if test = 'maps.userId != null      '> " +
+//            "and o.user_id =#{maps.userId} " +
+//            "</if>" +
+//            "<if test = 'maps.deliveryId != null  and  maps.deliveryId !=\"\"    '> " +
+//            "and o.delivery_id =#{maps.deliveryId} " +
+//            "</if>" +
+//            "<if test = 'maps.nickname != null and  maps.nickname !=\"\"     '> " +
+//            "and u.nickname like CONCAT('%',#{maps.nickname},'%') " +
+//            "</if>" +
+//            "<if test = 'maps.realName != null and  maps.realName !=\"\"     '> " +
+//            "and o.real_name like CONCAT('%',#{maps.realName},'%') " +
+//            "</if>" +
+//            "<if test = 'maps.phone != null and  maps.phone !=\"\"     '> " +
+//            "and u.phone like CONCAT('%',#{maps.phone},'%') " +
+//            "</if>" +
+//            "<if test = 'maps.userPhone != null and  maps.userPhone !=\"\"     '> " +
+//            "and o.user_phone like CONCAT('%',#{maps.userPhone},'%') " +
+//            "</if>" +
+//            "<if test = 'maps.status != null    '> " +
+//            "and o.status =#{maps.status} " +
+//            "</if>" +
+//            "<if test = 'maps.isUpload != null and maps.isUpload == 0    '> " +
+//            "and o.certificates is null  " +
+//            "</if>" +
+//            "<if test = 'maps.isUpload != null and maps.isUpload == 1    '> " +
+//            "and o.certificates is not null " +
+//            "</if>" +
+//            "<if test = 'maps.deliveryStatus != null    '> " +
+//            "and o.delivery_status =#{maps.deliveryStatus} " +
+//            "</if>" +
+//            "<if test = 'maps.deliveryPayStatus != null    '> " +
+//            "and o.delivery_pay_status =#{maps.deliveryPayStatus} " +
+//            "</if>" +
+//            "<if test = 'maps.companyId != null    '> " +
+//            "and o.company_id =#{maps.companyId} " +
+//            "</if>" +
+//            "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
+//            "and o.company_id is null " +
+//            "</if>" +
+//            "<if test = 'maps.notHealth != null '> " +
+//            "and o.company_id is not null " +
+//            "</if>" +
+//            "<if test = 'maps.companyUserId != null    '> " +
+//            "and o.company_user_id =#{maps.companyUserId} " +
+//            "</if>" +
+//            "<if test = 'maps.companyUserNickName != null and  maps.companyUserNickName !=  \"\" '> " +
+//            "and cu.nick_name like concat('%', #{maps.companyUserNickName}, '%') " +
+//            "</if>" +
+//            "<if test = 'maps.productName != null and  maps.productName !=  \"\" '> " +
+//            "and fsp.product_name like concat('%', #{maps.productName}, '%') " +
+//            "</if>" +
+//            "<if test = 'maps.orderType != null    '> " +
+//            "and o.order_type =#{maps.orderType} " +
+//            "</if>" +
+//            "<if test = 'maps.payType != null    '> " +
+//            "and o.pay_type =#{maps.payType} " +
+//            "</if>" +
+//            "<if test = 'maps.scheduleId != null    '> " +
+//            "and o.schedule_id =#{maps.scheduleId} " +
+//            "</if>" +
+//            "<if test = 'maps.createTimeList != null    '> " +
+//            " AND date_format(o.create_time,'%y%m%d') &gt;= date_format(#{maps.createTimeList[0]},'%y%m%d') " +
+//            " AND date_format(o.create_time,'%y%m%d') &lt;= date_format(#{maps.createTimeList[1]},'%y%m%d') " +
+//            "</if>" +
+//
+//            "<if test = 'maps.deliverySendTimeList != null    '> " +
+//            " AND date_format(o.delivery_send_time,'%y%m%d') &gt;= date_format(#{maps.deliverySendTimeList[0]},'%y%m%d') " +
+//            " AND date_format(o.delivery_send_time,'%y%m%d') &lt;= date_format(#{maps.deliverySendTimeList[1]},'%y%m%d') " +
+//            "</if>" +
+//            "<if test = 'maps.paidStatus != null    '> " +
+//            "and o.paid =#{maps.paidStatus} " +
+//            "</if>" +
+//            "<if test = 'maps.payTimeList != null    '> " +
+//            " AND date_format(o.pay_time,'%y%m%d') &gt;= date_format(#{maps.payTimeList[0]},'%y%m%d') " +
+//            " AND date_format(o.pay_time,'%y%m%d') &lt;= date_format(#{maps.payTimeList[1]},'%y%m%d') " +
+//            "</if>" +
+//            "<if test = 'maps.deliveryImportTimeList != null    '> " +
+//            " AND date_format(o.delivery_import_time,'%y%m%d') &gt;= date_format(#{maps.deliveryImportTimeList[0]},'%y%m%d') " +
+//            " AND date_format(o.delivery_import_time,'%y%m%d') &lt;= date_format(#{maps.deliveryImportTimeList[1]},'%y%m%d') " +
+//            "</if>" +
+//            "<if test = 'maps.deptId != null    '> " +
+//            "  AND (o.dept_id = #{maps.deptId} OR o.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) )) " +
+//            "</if>" +
+//            " ${maps.params.dataScope} "+
+//            "<if test = 'maps.productName != null and  maps.productName !=  \"\" '> " +
+//            " group by o.id "+
+//            "</if>" +
+//            " order by o.id desc "+
+//            "</script>"})
     List<FsStoreOrderVO> selectFsStoreOrderListVO(@Param("maps")FsStoreOrderParam param);
 
 
@@ -732,7 +737,7 @@ public interface FsStoreOrderScrmMapper
             " ${maps.params.dataScope} "+
             " order by o.id desc limit 50000"+
             "</script>"})
-    List<FsStoreOrderExportVO> selectFsStoreOrderListVOByExport(@Param("maps") FsStoreOrderParam param);
+    List<FsStoreOrderErpExportVO> selectFsStoreOrderListVOByExport(@Param("maps") FsStoreOrderParam param);
 
     @Select({"<script> " +
             "select o.*, " +
@@ -1254,4 +1259,157 @@ public interface FsStoreOrderScrmMapper
      */
     Integer selectUserPurchasedCount(@Param("userId") Long userId,
                                      @Param("productAttrValueId") Long productAttrValueId);
+
+    List<FsStoreOrderVO> selectFsStoreOrderListVOByErpAccount(@Param("maps")FsStoreOrderParam param);
+
+    int batchUpdateErpByOrderIds(@Param("maps")ArrayList<Map<String, String>> maps);
+
+    Map<String, BigDecimal> selectFsStoreOrderStatistics(@Param("maps")FsStoreOrderParam param);
+
+    String selectFsStoreOrderProductStatistics(@Param("maps")FsStoreOrderParam param);
+
+    Long selectFsStoreOrderListVOByErpAccountByExportCount(@Param("maps")FsStoreOrderParam param);
+
+    @Select({"<script> " +
+            "select o.*,cts.name as scheduleName,u.nickname,u.phone,cc.push_code,cc.create_time as customer_create_time," +
+            "cc.source,cc.customer_code, c.company_name ,cu.nick_name as company_user_nick_name ," +
+            "cu.phonenumber as company_usere_phonenumber ,p.title as package_title ," +
+            "CASE WHEN o.certificates IS NULL OR o.certificates = '' THEN 0 ELSE 1 END AS is_upload " +
+            ",df.login_account as erp_account," +
+            "        csc.name miniProgramName " +
+            " from fs_store_order_scrm o  left JOIN fs_store_product_package_scrm p on o.package_id=p.package_id left join fs_user u on o.user_id=u.user_id  " +
+            " left join company c on c.company_id=o.company_id left join company_user cu on cu.user_id=o.company_user_id left join crm_customer cc on cc.customer_id=o.customer_id left join company_tcm_schedule cts on cts.id = o.schedule_id " +
+            " LEFT JOIN fs_store_order_df df on df.order_id=o.id " +
+            " LEFT JOIN ( " +
+            " SELECT " +
+            " sp.*, " +
+            "ROW_NUMBER() OVER (PARTITION BY sp.business_code ORDER BY sp.create_time DESC) as rn " +
+            "FROM fs_store_payment_scrm sp " +
+            " ) sp_latest ON sp_latest.business_code = o.order_code AND sp_latest.rn = 1 " +
+            " LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id " +
+            "where 1=1 " +
+            "<if test = 'maps.orderCode != null and  maps.orderCode !=\"\"    '> " +
+            "and o.order_code like CONCAT('%',#{maps.orderCode},'%') " +
+            "</if>" +
+            "<if test=\"maps.orderCodes != null  and maps.orderCodes.size > 0\">" +
+            "            and o.order_code in" +
+            "            <foreach collection=\"maps.orderCodes\" item=\"orderCode\" open=\"(\" close=\")\" separator=\",\">" +
+            "                #{orderCode}" +
+            "            </foreach>" +
+            "        </if>" +
+            "<if test = 'maps.userId != null      '> " +
+            "and o.user_id =#{maps.userId} " +
+            "</if>" +
+            "<if test = 'maps.deliveryId != null  and  maps.deliveryId !=\"\"    '> " +
+            "and o.delivery_id =#{maps.deliveryId} " +
+            "</if>" +
+            "<if test = 'maps.nickname != null and  maps.nickname !=\"\"     '> " +
+            "and u.nickname like CONCAT('%',#{maps.nickname},'%') " +
+            "</if>" +
+            "<if test = 'maps.realName != null and  maps.realName !=\"\"     '> " +
+            "and o.real_name like CONCAT('%',#{maps.realName},'%') " +
+            "</if>" +
+            "<if test = 'maps.phone != null and  maps.phone !=\"\"     '> " +
+            "and u.phone like CONCAT('%',#{maps.phone},'%') " +
+            "</if>" +
+            "<if test = 'maps.userPhone != null and  maps.userPhone !=\"\"     '> " +
+            "and o.user_phone like CONCAT('%',#{maps.userPhone},'%') " +
+            "</if>" +
+            "<if test=\"maps.status != null and maps.status != 6\">" +
+            "            AND o.status = #{maps.status}" +
+            "        </if>" +
+            "<if test=\"maps.status == 6\">" +
+            "            AND o.`status` = 1" +
+            "            AND (" +
+            "            o.store_id IN (SELECT store_id FROM fs_store WHERE delivery_type=2 OR delivery_type=1)" +
+            "            )" +
+            "            AND (o.extend_order_id IS NULL OR o.extend_order_id = '')" +
+            "        </if>" +
+            "<if test = 'maps.deliveryStatus != null    '> " +
+            "and o.delivery_status =#{maps.deliveryStatus} " +
+            "</if>" +
+            "<if test = 'maps.deliveryPayStatus != null    '> " +
+            "and o.delivery_pay_status =#{maps.deliveryPayStatus} " +
+            "</if>" +
+            "<if test = 'maps.companyId != null    '> " +
+            "and o.company_id =#{maps.companyId} " +
+            "</if>" +
+            "<if test = 'maps.isHealth != null and maps.isHealth !=  \"\"  '> " +
+            "and o.company_id is null " +
+            "</if>" +
+            "<if test = 'maps.notHealth != null and maps.notHealth !=  \"\"  '> " +
+            "and o.company_id is not null " +
+            "</if>" +
+            "<if test = 'maps.companyUserId != null    '> " +
+            "and o.company_user_id =#{maps.companyUserId} " +
+            "</if>" +
+            "<if test = 'maps.companyUserNickName != null and  maps.companyUserNickName !=  \"\" '> " +
+            "and cu.nick_name like concat('%', #{maps.companyUserNickName}, '%') " +
+            "</if>" +
+            "<if test = 'maps.orderType != null    '> " +
+            "and o.order_type =#{maps.orderType} " +
+            "</if>" +
+            "<if test = 'maps.payType != null    '> " +
+            "and o.pay_type =#{maps.payType} " +
+            "</if>" +
+            "<if test = 'maps.createTimeList != null    '> " +
+            " AND date_format(o.create_time,'%y%m%d') &gt;= date_format(#{maps.createTimeList[0]},'%y%m%d') " +
+            " AND date_format(o.create_time,'%y%m%d') &lt;= date_format(#{maps.createTimeList[1]},'%y%m%d') " +
+            "</if>" +
+            "<if test = 'maps.payTimeList != null    '> " +
+            " AND date_format(o.pay_time,'%y%m%d') &gt;= date_format(#{maps.payTimeList[0]},'%y%m%d') " +
+            " AND date_format(o.pay_time,'%y%m%d') &lt;= date_format(#{maps.payTimeList[1]},'%y%m%d') " +
+            "</if>" +
+            "<if test = 'maps.deliverySendTimeList != null    '> " +
+            " AND date_format(o.delivery_send_time,'%y%m%d') &gt;= date_format(#{maps.deliverySendTimeList[0]},'%y%m%d') " +
+            " AND date_format(o.delivery_send_time,'%y%m%d') &lt;= date_format(#{maps.deliverySendTimeList[1]},'%y%m%d') " +
+            "</if>" +
+            "<if test = 'maps.deliveryImportTimeList != null    '> " +
+            " AND date_format(o.delivery_import_time,'%y%m%d') &gt;= date_format(#{maps.deliveryImportTimeList[0]},'%y%m%d') " +
+            " AND date_format(o.delivery_import_time,'%y%m%d') &lt;= date_format(#{maps.deliveryImportTimeList[1]},'%y%m%d') " +
+            "</if>" +
+            "<if test = 'maps.deptId != null    '> " +
+            "  AND (o.dept_id = #{maps.deptId} OR o.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) )) " +
+            "</if>" +
+            "<if test = 'maps.isUpload != null and maps.isUpload == 0    '> " +
+            "and o.certificates is null  " +
+            "</if>" +
+            "<if test = 'maps.scheduleId != null    '> " +
+            "and o.schedule_id =#{maps.scheduleId} " +
+            "</if>" +
+            "<if test = 'maps.isUpload != null and maps.isUpload == 1    '> " +
+            "and o.certificates is not null " +
+            "</if>" +
+            " <if test=\"maps.erpPhoneNumber != null and maps.erpPhoneNumber != ''\">" +
+            " and o.erp_phone like concat(#{maps.erpPhoneNumber},'%')" +
+            "</if>" +
+            "        <if test=\"maps.erpAccount != null and maps.erpAccount != '未分拣' and maps.erpAccount != ''\">" +
+            "            and df.login_account like #{maps.erpAccount}" +
+            "        </if>" +
+            "        <if test=\"maps.erpAccount == '未分拣'\">" +
+            "            and ( df.login_account is null or df.login_account like '')" +
+            "        </if>" +
+            " ${maps.params.dataScope} "+
+            " ORDER BY " +
+            "<if test=\"maps.sortField == 'companyUserName'\"> " +
+            "    cu.nick_name" +
+            "</if>" +
+            "<if test=\"maps.sortField == 'packageName'\">" +
+            "    o.package_name" +
+            "</if>" +
+            "<if test=\"maps.sortField == 'payPrice'\">" +
+            "    o.pay_price" +
+            "</if>" +
+            "<if test=\"maps.sortField == 'payMoney'\">" +
+            "    o.pay_money" +
+            "</if>" +
+            "<if test=\"maps.sortOrder != null and maps.sortOrder != ''\">" +
+            "    ${maps.sortOrder}" +
+            "</if>" +
+            "<if test=\"maps.sortField == null or maps.sortField == ''\">" +
+            "    o.id desc " +
+            "</if> " +
+            " limit 50000 "+
+            "</script>"})
+    List<FsStoreOrderErpExportVO> selectFsStoreOrderErpListVOByExport(@Param("maps") FsStoreOrderParam param);
 }

+ 22 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java

@@ -5,18 +5,24 @@ import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.List;
 
 @Data
 public class FsStoreOrderParam extends BaseEntity implements Serializable
 {
+
     private String orderCode;
 
+    //多个订单号搜索
+    private List<String> orderCodes;
+
     private String nickname;
 
     private String phone;
 
     private String userPhone;
 
+    /** 订单状态(-1 : 申请退款 -2 : 退货成功 1:待支付 2:待发货;3:待收货;4:待评价;5:已完成) 6(金牛代服待推送,请避开6)*/
     private Integer status;
 
     private Long companyId;
@@ -91,4 +97,20 @@ public class FsStoreOrderParam extends BaseEntity implements Serializable
 
     private Long storeId;
 
+    //排序字段
+    private String sortField;
+    //排序规则
+    private String sortOrder;
+
+    //erp电话
+    private String erpPhoneNumber;
+
+    //小程序id
+    private Long coursePlaySourceConfigId;
+    //erp账户
+    private String erpAccount;
+
+    //导出字段
+    private String filter;
+
 }

+ 14 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderScrmSetErpPhoneParam.java

@@ -0,0 +1,14 @@
+package com.fs.hisStore.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FsStoreOrderScrmSetErpPhoneParam extends FsStoreOrderParam {
+    private List<String> erpPhone;
+    private List<Long> orderIds;
+    private String loginAccount;
+    private Integer parcelQuantity; //包裹数量
+    private String opeName; //操作人
+}

+ 14 - 1
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java

@@ -199,7 +199,7 @@ public interface IFsStoreOrderScrmService
 
     List<FsStoreOrderStatisticsVO> selectFsStoreOrderStatisticsList(FsStoreStatisticsParam param);
 
-    List<FsStoreOrderExportVO> selectFsStoreOrderListVOByExport(FsStoreOrderParam param);
+    List<FsStoreOrderErpExportVO> selectFsStoreOrderListVOByExport(FsStoreOrderParam param);
 
     List<FsStoreOrderPromotionExportVO> selectFsPromotionOrderListVOByExport(FsStoreOrderParam param);
 
@@ -318,4 +318,17 @@ public interface IFsStoreOrderScrmService
      * @return list
      * **/
     List<FsStoreOrderDeliveryNoteExportVO> getDeliveryNote(FsStoreOrderParam param);
+
+    /**
+     * 跟进代服账户查询订单
+     * @param param
+     * @return
+     */
+    List<FsStoreOrderVO> selectFsStoreOrderListVOByErpAccount(FsStoreOrderParam param);
+
+    int batchUpdateErpByOrderIds(FsStoreOrderScrmSetErpPhoneParam param);
+
+    Map<String, BigDecimal> selectFsStoreOrderStatistics(FsStoreOrderParam param);
+
+    String selectFsStoreOrderProductStatistics(FsStoreOrderParam param);
 }

+ 119 - 8
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -18,6 +18,7 @@ import com.fs.api.param.OrderListParam;
 import com.fs.api.vo.OrderListVO;
 import com.fs.api.vo.ProductListVO;
 import com.fs.common.config.FSSysConfig;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.event.TemplateBean;
@@ -28,6 +29,11 @@ import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyDept;
+import com.fs.company.domain.CompanyMoneyLogs;
+import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.CompanyDeptMapper;
 import com.fs.company.mapper.CompanyMapper;
@@ -71,6 +77,8 @@ import com.fs.his.vo.FsInquiryOrderVO;
 import com.fs.his.vo.FsPrescribeVO;
 import com.fs.his.vo.FsStoreOrderAmountScrmStatsVo;
 import com.fs.his.vo.FsStoreOrderExcelVO;
+import com.fs.his.vo.*;
+import com.fs.his.vo.FsPrescribeVO;
 import com.fs.hisStore.config.FsErpConfig;
 import com.fs.hisStore.config.StoreConfig;
 import com.fs.hisStore.config.StoreIntegralConfig;
@@ -82,6 +90,12 @@ import com.fs.hisStore.mapper.*;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.*;
 import com.fs.hisStore.vo.*;
+import com.fs.hisStore.vo.FsStoreOrderErpExportVO;
+import com.fs.hisStore.vo.FsStoreOrderExportVO;
+import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.hisStore.vo.FsStoreOrderVO;
+import com.fs.hisStore.vo.FsStoreProductAttrValueVO;
+import com.fs.hisStore.vo.FsStoreProductDeliverExcelVO;
 import com.fs.hisapi.domain.ApiResponse;
 import com.fs.hisapi.param.CreateOrderParam;
 import com.fs.hisapi.param.RecipeDetailParam;
@@ -138,6 +152,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import static com.fs.his.utils.PhoneUtil.decryptPhone;
+import static com.fs.his.utils.PhoneUtil.encryptPhone;
 import static com.fs.hisStore.constants.StoreConstants.DELIVERY;
 
 /**
@@ -2288,12 +2303,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //将钱退还给用户
             List<FsStorePaymentScrm> payments = paymentService.selectFsStorePaymentByOrderId(order.getId());
             if (payments != null) {
-                String json;
-                if (CloudHostUtils.hasCloudHostName("康年堂")) {
-                    json = configService.selectConfigByKey("his.pay");
-                } else {
-                    json = configService.selectConfigByKey("store.pay");
-                }
+                String json = configService.selectConfigByKey("his.pay");
+
                 FsPayConfigScrm fsPayConfig = JSON.parseObject(json, FsPayConfigScrm.class);
                 for (FsStorePaymentScrm payment : payments) {
                     if (payment.getPayMode() == null || payment.getPayMode().equals("wx")) {
@@ -2603,8 +2614,14 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     }
 
     @Override
-    public List<FsStoreOrderExportVO> selectFsStoreOrderListVOByExport(FsStoreOrderParam param) {
-        return fsStoreOrderMapper.selectFsStoreOrderListVOByExport(param);
+    public List<FsStoreOrderErpExportVO> selectFsStoreOrderListVOByExport(FsStoreOrderParam param) {
+        if (CloudHostUtils.hasCloudHostName("金牛明医","康年堂")){
+            return fsStoreOrderMapper.selectFsStoreOrderErpListVOByExport(param);
+
+        } else {
+            return fsStoreOrderMapper.selectFsStoreOrderListVOByExport(param);
+
+        }
     }
 
     @Override
@@ -4056,7 +4073,101 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             return null;
         }
     }
+    @Override
+    public List<FsStoreOrderVO> selectFsStoreOrderListVOByErpAccount(FsStoreOrderParam param) {
+        List<FsStoreOrderVO> list = fsStoreOrderMapper.selectFsStoreOrderListVOByErpAccount(param);
+        for (FsStoreOrderVO vo : list) {
+            String nickName = vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
+            vo.setNickname(nickName);
+            if (StringUtils.isNotEmpty(vo.getItemJson())) {
+                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                List<FsStoreOrderItemVO> items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
+                if (!items.isEmpty()) {
+                    vo.setItems(items);
+                }
+            }
+            //List<FsStoreOrderItemVO> items=storeOrderItemService.selectFsStoreOrderItemListByOrderId(vo.getId());
+            //vo.setItems(items);
+        }
+        return list;
+    }
+
+    @Override
+    @Transactional
+    public int batchUpdateErpByOrderIds(FsStoreOrderScrmSetErpPhoneParam param) {
+        //判断是根据orderId还是查询设置
+        List<Long> orderIds = param.getOrderIds();
+        if (orderIds == null || orderIds.isEmpty()) {
+            if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+                param.setCreateTimeList(param.getCreateTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getPayTimeRange())){
+                param.setPayTimeList(param.getPayTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
+                param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
+            }
+            if(!StringUtils.isEmpty(param.getDeliverySendTimeRange())){
+                param.setDeliverySendTimeList(param.getDeliverySendTimeRange().split("--"));
+            }
+            param.setNotHealth(1);
+            List<FsStoreOrderVO> list = fsStoreOrderMapper.selectFsStoreOrderListVO(param);
+            orderIds = list.stream().map(FsStoreOrderVO::getId).collect(Collectors.toList());
+            param.setOrderIds(orderIds);
+        }
+        if (orderIds.isEmpty()) {
+            return 0;
+        }
+        //分配手机号
+        //1.手机号大于/等于orderIds长度
+        List<String> erpPhones = param.getErpPhone();
+        int phoneSize = erpPhones.size();
+        int orderSize = orderIds.size();
+        ArrayList<Map<String, String>> maps = new ArrayList<>();
+        if (phoneSize >= orderSize) {
+            for (int i = 0; i < orderSize; i++) {
+                HashMap<String, String> map = new HashMap<>();
+                map.put("orderId", orderIds.get(i).toString());
+                map.put("erpPhone", erpPhones.get(i));
+                maps.add(map);
+                fsStoreOrderLogsService.create(orderIds.get(i), FsStoreOrderLogEnum.SET_PUSH_MOBILE.getValue(),
+                        param.getOpeName() + " " +FsStoreOrderLogEnum.SET_PUSH_MOBILE.getDesc() + ":" + erpPhones.get(i));
+            }
+        } else {
+            //2.手机号小于orderIds长度
+            int size = orderSize / phoneSize;
+            int num = orderSize % phoneSize;
+            if (num > 0) {
+                size = size + 1;
+            }
+            int orderIndex = 0;
+            for (String erpPhone : erpPhones) {
+                for (int i = 0; i < size; i++) {
+                    if (orderIndex > (orderSize - 1)) {
+                        break;
+                    }
+                    HashMap<String, String> map = new HashMap<>();
+                    map.put("orderId", orderIds.get(orderIndex).toString());
+                    map.put("erpPhone", erpPhone);
+                    maps.add(map);
+                    orderIndex++;
+                    fsStoreOrderLogsService.create(orderIds.get(i), FsStoreOrderLogEnum.SET_PUSH_MOBILE.getValue(),
+                            param.getOpeName() + " " +FsStoreOrderLogEnum.SET_PUSH_MOBILE.getDesc() + ":" + erpPhone);
+                }
+            }
+        }
+        return fsStoreOrderMapper.batchUpdateErpByOrderIds(maps);
+    }
 
+    @Override
+    public Map<String, BigDecimal> selectFsStoreOrderStatistics(FsStoreOrderParam param) {
+        return fsStoreOrderMapper.selectFsStoreOrderStatistics(param);
+    }
+
+    @Override
+    public String selectFsStoreOrderProductStatistics(FsStoreOrderParam param) {
+        return fsStoreOrderMapper.selectFsStoreOrderProductStatistics(param);
+    }
     public static String parseCstToDateOnlyString(String cstDateStr) {
         LocalDateTime dateTime = parseCstToLocalDateTime(cstDateStr);
         return dateTime != null ? dateTime.format(DateTimeFormatter

+ 9 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java

@@ -47,6 +47,7 @@ import com.fs.hisStore.vo.FsStoreScanPaymentStatDetailsVo;
 import com.fs.hisStore.vo.FsStoreScanPaymentStatVo;
 import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
+import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
 import com.fs.huifuPay.service.HuiFuService;
 import com.fs.pay.pay.dto.WxJspayDTO;
 import com.fs.pay.service.IPayService;
@@ -912,6 +913,14 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
         o.setTransAmt(storePayment.getPayMoney().toString());
         o.setGoodsDesc("商城订单支付");
         o.setAppId(param.getAppId());
+        //公司分账
+        try {
+            HuiFuUtils.doDiv(o,company.getCompanyId());
+            //存储分账明细
+            HuiFuUtils.saveDivItem(o, storePayment.getPayCode(), storePayment.getPayCode());
+        } catch (Exception e) {
+            logger.error("-------------微信收款分账出错:{}", e.getMessage());
+        }
         HuifuCreateOrderResult result = huiFuService.createOrder(o);
         if(result.getResp_code()!=null&&(result.getResp_code().equals("00000000")||result.getResp_code().equals("00000100"))){
             FsStorePaymentScrm mt=new FsStorePaymentScrm();

+ 24 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderErpExportVO.java

@@ -0,0 +1,24 @@
+package com.fs.hisStore.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 订单对象 fs_store_order
+ *
+ * @author fs
+ * @date 2022-03-15
+ */
+@Data
+public class FsStoreOrderErpExportVO extends FsStoreOrderExportVO
+{
+    @Excel(name = "ERP电话",sort = 1)
+    private String erpPhone;
+    @Excel(name = "ERP账户",sort = 2)
+    private String erpAccount;
+}

+ 10 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java

@@ -244,5 +244,15 @@ public class FsStoreOrderVO implements Serializable
 
     private String orderMedium;
 
+    //小程序名称
+    private String miniProgramName;
+
+
+    //erp推送号码
+    private String erpPhone;
+
+    //erp推送账号
+    private String erpAccount;
+
 
 }

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