Parcourir la source

Merge branch 'master' into ScrmStores

# Conflicts:
#	fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
#	fs-admin/src/main/java/com/fs/his/task/Task.java
#	fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
#	fs-admin/src/main/resources/application.yml
#	fs-company/src/main/resources/application.yml
#	fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java
#	fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
#	fs-qw-task/src/main/java/com/fs/app/task/qwTask.java
#	fs-service/src/main/java/com/fs/company/service/ICompanyService.java
#	fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
#	fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
#	fs-service/src/main/java/com/fs/his/mapper/FsDoctorMapper.java
#	fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
#	fs-service/src/main/java/com/fs/his/service/IFsPrescribeService.java
#	fs-service/src/main/java/com/fs/his/service/impl/FsFollowReportServiceImpl.java
#	fs-service/src/main/java/com/fs/his/service/impl/FsInquiryOrderMsgServiceImpl.java
#	fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
#	fs-service/src/main/java/com/fs/hisStore/domain/FsStorePaymentScrm.java
#	fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
#	fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java
#	fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
#	fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductScrmService.java
#	fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
#	fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
#	fs-service/src/main/resources/application-dev.yml
#	fs-service/src/main/resources/application-druid-yjb.yml
#	fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml
#	fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
#	fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
#	fs-service/src/main/resources/mapper/qw/QwWatchLogMapper.xml
#	fs-user-app/src/main/java/com/fs/app/controller/store/StoreAfterSalesScrmController.java
#	fs-user-app/src/main/java/com/fs/app/controller/store/StoreOrderScrmController.java
#	fs-user-app/src/main/resources/application.yml
吴树波 il y a 1 semaine
Parent
commit
e2ee4f8f42
100 fichiers modifiés avec 4566 ajouts et 670 suppressions
  1. 11 6
      README.md
  2. 4 0
      fs-admin/pom.xml
  3. 6 0
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  4. 27 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java
  5. 41 0
      fs-admin/src/main/java/com/fs/course/controller/CourseRedPacketStatisticsController.java
  6. 3 6
      fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java
  7. 13 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  8. 64 0
      fs-admin/src/main/java/com/fs/course/task/CompanyBalanceTask.java
  9. 37 0
      fs-admin/src/main/java/com/fs/course/task/RedPacketLogsTask.java
  10. 15 13
      fs-admin/src/main/java/com/fs/crm/controller/CrmCustomerController.java
  11. 6 6
      fs-admin/src/main/java/com/fs/his/controller/FsArticleController.java
  12. 26 4
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java
  13. 103 0
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyDivItemController.java
  14. 103 0
      fs-admin/src/main/java/com/fs/his/controller/FsDfAccountController.java
  15. 2 2
      fs-admin/src/main/java/com/fs/his/controller/FsInquiryOrderController.java
  16. 8 0
      fs-admin/src/main/java/com/fs/his/controller/FsIntegralOrderController.java
  17. 31 25
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  18. 4 0
      fs-admin/src/main/java/com/fs/his/controller/FsUserAddressController.java
  19. 26 41
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  20. 345 148
      fs-admin/src/main/java/com/fs/his/task/Task.java
  21. 93 14
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  22. 308 19
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  23. 29 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java
  24. 37 4
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  25. 1 4
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  26. 53 0
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactTransferCompanyAuditController.java
  27. 9 0
      fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java
  28. 1 1
      fs-admin/src/main/java/com/fs/qw/controller/QwSopController.java
  29. 103 0
      fs-admin/src/main/java/com/fs/qw/controller/QwUserComplainRecordController.java
  30. 17 1
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  31. 27 0
      fs-admin/src/main/java/com/fs/task/SgTestController.java
  32. 25 0
      fs-admin/src/main/java/com/fs/task/SyncTuLinStudentInfoTask.java
  33. 79 4
      fs-admin/src/main/java/com/fs/web/controller/common/CommonController.java
  34. 26 7
      fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java
  35. 2 2
      fs-admin/src/main/resources/application.yml
  36. 2 2
      fs-common-api/src/main/resources/application.yml
  37. 5 0
      fs-common/src/main/java/com/fs/common/constant/FsConstants.java
  38. 7 0
      fs-common/src/main/java/com/fs/common/enums/ImTypeEnum.java
  39. 37 0
      fs-company-app/src/main/java/com/fs/app/controller/AppBaseController.java
  40. 222 6
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  41. 1 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  42. 107 12
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  43. 28 32
      fs-company-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java
  44. 73 0
      fs-company-app/src/main/java/com/fs/core/aspectj/DataSourceAspect.java
  45. 244 0
      fs-company-app/src/main/java/com/fs/core/aspectj/LogAspect.java
  46. 94 0
      fs-company-app/src/main/java/com/fs/core/config/DataSourceConfig.java
  47. 123 123
      fs-company-app/src/main/java/com/fs/core/config/DruidConfig.java
  48. 4 0
      fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java
  49. 3 3
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java
  50. 4 5
      fs-company-app/src/main/java/com/fs/core/datasource/DynamicDataSourceContextHolder.java
  51. 56 0
      fs-company-app/src/main/java/com/fs/core/manager/AsyncManager.java
  52. 40 0
      fs-company-app/src/main/java/com/fs/core/manager/ShutdownManager.java
  53. 103 0
      fs-company-app/src/main/java/com/fs/core/manager/factory/AsyncFactory.java
  54. 1 1
      fs-company-app/src/main/resources/application.yml
  55. 39 0
      fs-company/src/main/java/com/fs/company/controller/common/CommonController.java
  56. 222 9
      fs-company/src/main/java/com/fs/company/controller/common/Test.java
  57. 6 4
      fs-company/src/main/java/com/fs/company/controller/company/CompanyController.java
  58. 7 12
      fs-company/src/main/java/com/fs/company/controller/company/CompanyDeptController.java
  59. 9 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyProfileController.java
  60. 72 21
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  61. 1 1
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java
  62. 1 1
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  63. 65 6
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java
  64. 8 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  65. 106 1
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  66. 59 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferCompanyAuditController.java
  67. 4 2
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferLogController.java
  68. 86 4
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java
  69. 10 1
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopLogsController.java
  70. 33 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  71. 71 0
      fs-company/src/main/java/com/fs/company/controller/store/FsPrescribeController.java
  72. 2 2
      fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java
  73. 29 0
      fs-company/src/main/java/com/fs/company/utils/QwStatusEnum.java
  74. 1 1
      fs-company/src/main/java/com/fs/framework/config/SecurityConfig.java
  75. 23 7
      fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  76. 26 0
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  77. 2 2
      fs-company/src/main/resources/application.yml
  78. 19 6
      fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java
  79. 5 0
      fs-doctor-app/src/main/java/com/fs/app/controller/DiagnosisController.java
  80. 8 3
      fs-doctor-app/src/main/java/com/fs/app/controller/DoctorController.java
  81. 24 6
      fs-doctor-app/src/main/java/com/fs/app/controller/DrugReportController.java
  82. 50 11
      fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java
  83. 108 1
      fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java
  84. 18 2
      fs-doctor-app/src/main/java/com/fs/framework/config/MyBatisConfig.java
  85. 2 2
      fs-doctor-app/src/main/resources/mybatis/mybatis-config.xml
  86. 1 1
      fs-ipad-task/src/main/resources/application.yml
  87. 29 1
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  88. 35 2
      fs-qw-api/src/main/java/com/fs/app/controller/CommonController.java
  89. 46 0
      fs-qw-api/src/main/java/com/fs/app/controller/QwUserComplainRecordController.java
  90. 9 1
      fs-qw-api/src/main/java/com/fs/app/service/QwDataCallbackService.java
  91. 36 1
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  92. 11 0
      fs-qw-task/src/main/java/com/fs/app/task/qwTask.java
  93. 10 0
      fs-qw-task/src/main/java/com/fs/app/taskService/SyncQwExternalContactService.java
  94. 302 56
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  95. 78 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SyncQwExternalContactServiceImpl.java
  96. 8 6
      fs-qw-voice/src/main/java/com/fs/app/mq/RocketMQConsumerService.java
  97. 43 2
      fs-qwhook-sop/src/main/java/com/fs/app/controller/ApisQwSopController.java
  98. 1 1
      fs-qwhook-sop/src/main/resources/application.yml
  99. 1 1
      fs-qwhook/src/main/resources/application.yml
  100. 1 1
      fs-repeat-api/src/main/java/com/fs/app/mq/RocketMQConsumerService.java

+ 11 - 6
README.md

@@ -3,21 +3,26 @@
 #### 介绍
 问诊平台
 
-#### 软件架构
-软件架构说明
+#### 软件模块说明
+
+| 模块名称         | 模块描述         | 对应前端项目                  |
+|--------------|--------------|-------------------------|
+| fs-admin     | 总后台服务        | ylrz_his_scrm_adminUI   |
+| fs-company   | 销售端          | ylrz_his_scrm_companyUI |
+| fs-user-app  | 微信小程序端       | 对应某个微信小程序(前端蒲瑶清楚)       |
+| fs-framework | 主要依赖包,核心包    | /                       |
+| fs-service   | 所有的链接配置文件都在里面 | /                       |
 
 
 #### 安装教程
 
 1.  xxxx
 2.  xxxx
-3.  xxxx
 
 #### 使用说明
 
-1.  xxxx
-2.  xxxx
-3.  xxxx
+1.  注意调整Memory的大小,以及堆内存大小
+2.  对于maven仓库缺少的jar包引用,需要拷贝现有的文件(拷贝后依然出现错误,直接删除错误包下的_remote.repositories文件)。
 
 #### 参与贡献
 

+ 4 - 0
fs-admin/pom.xml

@@ -94,6 +94,10 @@
             <artifactId>clickhouse-jdbc</artifactId>
             <version>0.4.6</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
 
     </dependencies>
 

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

@@ -1,6 +1,7 @@
 package com.fs.api.controller;
 
 import com.alibaba.fastjson.JSON;
+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;
@@ -173,6 +174,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);
     }

+ 27 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java

@@ -19,9 +19,14 @@ import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.service.ICrmCustomerVisitService;
 import com.fs.crm.vo.CrmCustomerStatisticsVO;
 import com.fs.crm.vo.CrmCustomerVisitStatisticsVO;
+import com.fs.his.dto.FsStoreOrderAmountScrmStatsQueryDto;
+import com.fs.his.dto.FsStoreOrderAmountStatsQueryDto;
 import com.fs.his.service.IFsStoreAfterSalesService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.his.service.IFsStorePaymentService;
+import com.fs.his.vo.FsStoreOrderAmountScrmStatsVo;
+import com.fs.his.vo.FsStoreOrderAmountStatsVo;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -67,6 +72,10 @@ public class CompanyStatisticsController extends BaseController
     private ICrmCustomerService crmCustomerService;
     @Autowired
     private ICrmCustomerVisitService crmCustomerVisitService;
+
+    //app商城订单接口Service
+    @Autowired
+    private IFsStoreOrderScrmService fsStoreOrderScrmService;
     @GetMapping("/storeOrder")
     public R storeOrder(FsStoreStatisticsParam param)
     {
@@ -724,4 +733,22 @@ public class CompanyStatisticsController extends BaseController
         return util.exportExcel(qwIpadTotalVos, "visit");
     }
 
+    /**
+     * 获取互联网医院订单统计数据
+     * */
+    @GetMapping("/hisOrderCountStats")
+    public AjaxResult getHisOrderCount(FsStoreOrderAmountStatsQueryDto statsQueryDto){
+        FsStoreOrderAmountStatsVo fsStoreOrderAmountStatsVo = storeOrderService.selectFsStoreOrderAmountStats(statsQueryDto);
+        return AjaxResult.success(fsStoreOrderAmountStatsVo);
+    }
+
+    /**
+     * 获取App商城订单统计数据
+     * */
+    @GetMapping("/appOrderCountStats")
+    public AjaxResult getAppOrderCount(FsStoreOrderAmountScrmStatsQueryDto statsQueryDto){
+        FsStoreOrderAmountScrmStatsVo scrmStatsVo = fsStoreOrderScrmService.selectFsStoreOrderAmountScrmStats(statsQueryDto);
+        return AjaxResult.success(scrmStatsVo);
+    }
+
 }

+ 41 - 0
fs-admin/src/main/java/com/fs/course/controller/CourseRedPacketStatisticsController.java

@@ -0,0 +1,41 @@
+package com.fs.course.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.course.dto.CourseRedPacketStatisticsDTO;
+import com.fs.course.param.CourseRedPacketStatisticsParam;
+import com.fs.course.service.CourseRedPacketStatisticsService;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.ApiModelProperty;
+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;
+
+/**
+ * @description: 看客红包发送统计
+ * @author: Xgb
+ * @createDate: 2025/10/14
+ * @version: 1.0
+ */
+@RestController
+@RequestMapping("/course/courseRedPacketStatistics")
+public class CourseRedPacketStatisticsController extends BaseController {
+
+    @Autowired
+    private CourseRedPacketStatisticsService courseRedPacketStatisticsService;
+
+    @ApiModelProperty("看客红包发送统计")
+    @GetMapping("/list")
+    public R list(CourseRedPacketStatisticsParam param) {
+
+        startPage();
+        // 看客红包发送统计
+        List<CourseRedPacketStatisticsDTO> list = courseRedPacketStatisticsService.statistics(param);
+
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+}

+ 3 - 6
fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java

@@ -103,13 +103,10 @@ public class FsCourseWatchLogController extends BaseController
         if (param.getSTime()==null||param.getETime()==null){
             throw new CustomException("必须选择开始时间和结束时间!");
         }
+        startPage();
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVONew(param);
-        TableDataInfo rspData = new TableDataInfo();
-        rspData.setCode(HttpStatus.SUCCESS);
-        rspData.setMsg("查询成功");
-        rspData.setRows(list);
-        rspData.setTotal(fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVONewCount(param));
-        return rspData;
+
+        return getDataTable(list);
     }
 
     /**

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

+ 64 - 0
fs-admin/src/main/java/com/fs/course/task/CompanyBalanceTask.java

@@ -0,0 +1,64 @@
+package com.fs.course.task;
+
+import com.fs.company.service.ICompanyService;
+import com.fs.course.service.BalanceRollbackErrorService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * @description: 公司余额同步定时任务
+ * @author: Xgb
+ * @createDate: 2025/10/22
+ * @version: 1.0
+ */
+@Component("companyBalanceTask")
+public class CompanyBalanceTask {
+
+
+    @Autowired
+    private BalanceRollbackErrorService balanceRollbackErrorService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+
+    /**
+     * @Description: 每10分钟从缓存获取余额同步到公司账户中
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/22 10:56
+     */
+    public void syncCompanyBalance() {
+        companyService.syncCompanyBalance();
+    }
+
+    /**
+     * @Description: 定时回滚公司余额数据
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/22 11:48
+     */
+    public void processBatchRollbackByCompanyId() {
+        balanceRollbackErrorService.processBatchRollbackByCompanyId();
+    }
+
+    /**
+     * @Description: spring启动执行 initCompanyBalance 查询余额报存到缓存中
+     * 如果新增公司可以重启admin或者定时配置一下这个方法手动执行一次
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/22 11:52
+     */
+    public void initCompanyBalance() {
+        balanceRollbackErrorService.initCompanyBalance();
+
+    }
+
+
+
+}

+ 37 - 0
fs-admin/src/main/java/com/fs/course/task/RedPacketLogsTask.java

@@ -0,0 +1,37 @@
+package com.fs.course.task;
+
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
+import com.fs.course.service.IFsCourseRedPacketLogService;
+import com.fs.course.service.impl.FsCourseRedPacketLogServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+/**
+ * @description: 微信红包转账回调部分没有接收到,主动自己去查询
+ * @author: Xgb
+ * @createDate: 2025/10/15
+ * @version: 1.0
+ */
+@Component("redPacketLogsTask")
+public class RedPacketLogsTask {
+
+    @Autowired
+    private IFsCourseRedPacketLogService fsCourseRedPacketLogService;
+
+    /**
+     * @Description: 查询微信红包转账结果 每10分钟查询上一个10分钟区间的红包转账结果
+     * (定时调取失败 可以启动fs-qw-task 中的 CommonController queryRedPacketResult 手动调取) 仅给内部人员使用
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/15 16:32
+     */
+    public void queryRedPacketResult() {
+
+        // 查询RedPacketLog表,
+        fsCourseRedPacketLogService.queryRedPacketResult(null, null);
+    }
+
+}

+ 15 - 13
fs-admin/src/main/java/com/fs/crm/controller/CrmCustomerController.java

@@ -151,14 +151,14 @@ public class CrmCustomerController extends BaseController
         crmCustomer.setIsLine(0);
         List<CrmCustomerListVO> list = crmCustomerService.selectCrmCustomerListVO(crmCustomer);
         List<CrmCustomerExportVO> exportList=new ArrayList<>();
-        boolean checkPhone = isCheckPhone();
+        SysRole sysRole = isCheckPermission();
         for(CrmCustomerListVO customer:list){
             CrmCustomerExportVO vo=new CrmCustomerExportVO();
             if(customer.getSource()!=null){
                 vo.setSource(customer.getSource().toString());
             }
             BeanUtils.copyProperties(customer,vo);
-            if(customer.getMobile()!=null && !checkPhone){
+            if(customer.getMobile()!=null && !(sysRole.getIsCheckPhone()==1)){
                 vo.setMobile(customer.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
             }
             exportList.add(vo);
@@ -197,9 +197,9 @@ public class CrmCustomerController extends BaseController
         }
         List<CrmCustomerListVO> list = crmCustomerService.selectCrmCustomerListQueryParam(crmCustomer);
         if (list != null) {
-            boolean checkPhone = isCheckPhone();
+            SysRole sysRole = isCheckPermission();
             for (CrmCustomerListVO vo : list) {
-                if(vo.getMobile()!=null && !checkPhone){
+                if(vo.getMobile()!=null && !(sysRole.getIsCheckPhone()==1)){
                     vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
 
@@ -211,19 +211,21 @@ public class CrmCustomerController extends BaseController
 
     @Autowired
     private ISysRoleService sysRoleService;
-    private boolean isCheckPhone() {
+    private SysRole isCheckPermission() {
+        SysRole sysRole = new SysRole();
         SysUser user = getLoginUser().getUser();
         boolean flag = user.isAdmin();
         if (flag) {
-            return true;
-        }
-        List<SysRole> roles = user.getRoles();
-        if (roles != null && !roles.isEmpty()) {
-            Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
-            return sysRoleService.getIsCheckPhone(roleIds);
+            sysRole.setIsCheckPhone(1);
+            sysRole.setIsCheckAddress(1);
+        } else {
+            List<SysRole> roles = user.getRoles();
+            if (roles != null && !roles.isEmpty()) {
+                Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+                return sysRoleService.getIsCheckPermission(roleIds);
+            }
         }
-
-        return false;
+        return sysRole;
     }
 
 

+ 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;
     /**
      * 查询诊所管理列表
      */
@@ -248,4 +248,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));
+    }
+}

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

@@ -1,6 +1,7 @@
 package com.fs.his.controller;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
@@ -155,8 +156,7 @@ public class FsInquiryOrderController extends BaseController
 
     @PreAuthorize("@ss.hasPermi('his:inquiryOrder:sendMsg')")
     @GetMapping(value = "/sendMsg/{orderId}")
-    public AjaxResult sendMsg(@PathVariable("orderId") Long orderId)
-    {
+    public AjaxResult sendMsg(@PathVariable("orderId") Long orderId) throws JsonProcessingException {
 
         return AjaxResult.success(fsInquiryOrderService.sendStartMsg(orderId));
     }

+ 8 - 0
fs-admin/src/main/java/com/fs/his/controller/FsIntegralOrderController.java

@@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
+import java.util.Map;
 
 import static com.fs.his.utils.PhoneUtil.decryptAutoPhoneMk;
 import static com.fs.his.utils.PhoneUtil.decryptPhone;
@@ -170,4 +171,11 @@ public class FsIntegralOrderController extends BaseController
     {
         return toAjax(fsIntegralOrderService.deleteFsIntegralOrderByOrderIds(orderIds));
     }
+    @PreAuthorize("@ss.hasPermi('his:integralOrder:cancel')")
+    @Log(title = "积分商品订单", businessType = BusinessType.UPDATE)
+    @PostMapping("/cancelOrder")
+    public AjaxResult cancelOrder(@RequestBody Map<String, String> requestBody){
+        String orderCode = requestBody.get("orderCode");
+        return toAjax(fsIntegralOrderService.cancelOrder(orderCode));
+    }
 }

+ 31 - 25
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -122,6 +122,9 @@ public class FsStoreOrderController extends BaseController
 
     @Autowired
     private IFsStoreOrderLogsService fsStoreOrderLogsService;
+
+    @Autowired
+    private IFsDfAccountService fsDfAccountService;
     /**
      * 查询订单列表
      */
@@ -257,8 +260,9 @@ public class FsStoreOrderController extends BaseController
         task.setUserId(SecurityUtils.getUserId());
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
-        boolean checkPhone = isCheckPhone();
-        exportTaskService.exportStore1Data(param,checkPhone, filterList);
+
+        SysRole sysRole = isCheckPermission();
+        exportTaskService.exportStore1Data(param,sysRole.getIsCheckPhone() == 1,sysRole.getIsCheckAddress() == 1, filterList);
 
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
 
@@ -267,19 +271,21 @@ public class FsStoreOrderController extends BaseController
 
     @Autowired
     private ISysRoleService sysRoleService;
-    private boolean isCheckPhone() {
+    private SysRole isCheckPermission() {
+        SysRole sysRole = new SysRole();
         SysUser user = getLoginUser().getUser();
         boolean flag = user.isAdmin();
         if (flag) {
-            return true;
-        }
-        List<SysRole> roles = user.getRoles();
-        if (roles != null && !roles.isEmpty()) {
-            Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
-            return sysRoleService.getIsCheckPhone(roleIds);
+            sysRole.setIsCheckPhone(1);
+            sysRole.setIsCheckAddress(1);
+        } else {
+            List<SysRole> roles = user.getRoles();
+            if (roles != null && !roles.isEmpty()) {
+                Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+                return sysRoleService.getIsCheckPermission(roleIds);
+            }
         }
-
-        return false;
+        return sysRole;
     }
 
     @GetMapping("/importTemplate")
@@ -764,19 +770,16 @@ public class FsStoreOrderController extends BaseController
 
     private FsStoreOrderDf getDFInfo(String loginAccount) {
         //查询订单账户 判断是否存在该订单账户
-        List<DFConfigVo> erpAccounts = fsStoreOrderService.getErpAccount();
+        FsDfAccount erpAccount = fsDfAccountService.selectFsDfAccountByAccount(loginAccount);
         FsStoreOrderDf df = new FsStoreOrderDf();
-        for (DFConfigVo 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;
-            }
+        if (erpAccount != null){
+            //添加df记录
+            df.setAppKey(erpAccount.getDfAppKey());
+            df.setAppSecret(erpAccount.getDfAppsecret());
+            df.setLoginAccount(loginAccount);
+            df.setMonthlyCard(erpAccount.getMonthlyCard());
+            df.setExpressProductCode(erpAccount.getExpressProductCode());
+            df.setStatus(0);
         }
         return df;
     }
@@ -954,8 +957,11 @@ 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<FsDfAccount> erpAccounts = fsDfAccountService.selectFsDfAccountList(null);
+                list = erpAccounts.stream().map(FsDfAccount::getLoginAccount).collect(Collectors.toList());
+        }
         return R.ok().put("data", list);
     }
 }

+ 4 - 0
fs-admin/src/main/java/com/fs/his/controller/FsUserAddressController.java

@@ -99,6 +99,10 @@ public class FsUserAddressController extends BaseController
         String kdnAddress = fsUserAddressService.getKdnAddress(address);
         AddressInfoDTO addressInfoDTO = JSON.parseObject(kdnAddress, AddressInfoDTO.class);
         AddressInfoDTO.AddressData data = addressInfoDTO.getData();
+        logger.info("快递鸟返回:"+kdnAddress);
+        if (data==null){
+            return AjaxResult.error("解析地址失败请输入正确地址");
+        }
         String provinceName = data.getProvinceName();
         if (!provinceName.contains("省") && !provinceName.contains("区")){
             data.setProvinceName(data.getProvinceName()+"市");

+ 26 - 41
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -4,7 +4,7 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.entity.SysRole;
@@ -13,9 +13,10 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
-import com.fs.course.domain.FsUserWatchStatistics;
-import com.fs.course.mapper.FsUserWatchStatisticsMapper;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.service.IFsUserCompanyUserService;
+import com.fs.course.service.IFsUserCourseService;
 import com.fs.his.domain.FsUserAddress;
 import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.FsUserMapper;
@@ -27,6 +28,8 @@ import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.UserVo;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
 import com.fs.qw.dto.UserProjectDTO;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.vo.h5.FsUserPageListVO;
@@ -39,6 +42,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.session.ExecutorType;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.BeanUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -97,10 +101,10 @@ public class FsUserController extends BaseController
             fsUser.setPhone(encryptPhone(fsUser.getPhone()));
         }
         List<FsUserVO> list = fsUserService.selectFsUserListVO(fsUser);
-        boolean checkPhone = isCheckPhone();
+        SysRole sysRole = isCheckPermission();
         for (FsUserVO fsUserVO : list) {
             if(fsUserVO.getPhone() != null&&fsUserVO.getPhone()!=""){
-                if (!checkPhone){
+                if (!(sysRole.getIsCheckPhone()==1)){
                     if (fsUserVO.getPhone().length()>11){
                         fsUserVO.setPhone(decryptPhoneMk(fsUserVO.getPhone()));
                     }else {
@@ -119,19 +123,21 @@ public class FsUserController extends BaseController
 
     @Autowired
     private ISysRoleService sysRoleService;
-    private boolean isCheckPhone() {
+    private SysRole isCheckPermission() {
+        SysRole sysRole = new SysRole();
         SysUser user = getLoginUser().getUser();
         boolean flag = user.isAdmin();
         if (flag) {
-            return true;
-        }
-        List<SysRole> roles = user.getRoles();
-        if (roles != null && !roles.isEmpty()) {
-            Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
-            return sysRoleService.getIsCheckPhone(roleIds);
+            sysRole.setIsCheckPhone(1);
+            sysRole.setIsCheckAddress(1);
+        } else {
+            List<SysRole> roles = user.getRoles();
+            if (roles != null && !roles.isEmpty()) {
+                Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+                return sysRoleService.getIsCheckPermission(roleIds);
+            }
         }
-
-        return false;
+        return sysRole;
     }
 
     @PreAuthorize("@ss.hasPermi('his:user:list')")
@@ -143,10 +149,10 @@ public class FsUserController extends BaseController
             fsUser.setPhone(encryptPhone(fsUser.getPhone()));
         }
         List<FsUserVO> list = fsUserService.selectFsUserVOListByProject(fsUser);
-        boolean checkPhone = isCheckPhone();
+        SysRole sysRole = isCheckPermission();
         for (FsUserVO fsUserVO : list) {
             if(fsUserVO.getPhone() != null&&fsUserVO.getPhone()!=""){
-                if (!checkPhone){
+                if (!(sysRole.getIsCheckPhone()==1)){
                     if (fsUserVO.getPhone().length()>11){
                         fsUserVO.setPhone(decryptPhoneMk(fsUserVO.getPhone()));
                     }else {
@@ -170,10 +176,10 @@ public class FsUserController extends BaseController
             fsUser.setPhone(encryptPhone(fsUser.getPhone()));
         }
         List<FsUserVO> list = fsUserService.selectFsUserVOListByProject(fsUser);
-        boolean checkPhone = isCheckPhone();
+        SysRole sysRole = isCheckPermission();
         for (FsUserVO fsUserVO : list) {
             if(fsUserVO.getPhone() != null&&fsUserVO.getPhone()!=""){
-                if (!checkPhone){
+                if (!(sysRole.getIsCheckPhone()==1)){
                     if (fsUserVO.getPhone().length()>11){
                         fsUserVO.setPhone(decryptPhoneMk(fsUserVO.getPhone()));
                     }else {
@@ -207,9 +213,9 @@ public class FsUserController extends BaseController
             return AjaxResult.error("导出数据不可超过1w条");
         }
         List<FsUserExportListVO> list = fsUserService.selectFsUserExportListVO(fsUser);
-        boolean checkPhone = isCheckPhone();
+        SysRole sysRole = isCheckPermission();
         for (FsUserExportListVO vo : list) {
-            if (vo.getMobile()!=null && !checkPhone){
+            if (vo.getMobile()!=null && !(sysRole.getIsCheckPhone()==1)){
                 vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
             } else {
                 if (vo.getMobile().length()>11){
@@ -384,26 +390,5 @@ public class FsUserController extends BaseController
         return userIntegralLogsService.addIntegralTemplate(integralTemplateParam);
     }
 
-//    @PutMapping("/encryptPhoneTemp")
-//    @ApiOperation("临时接口")
-//    public void encryptPhoneTemp(){
-//        FsUser fsUser = new FsUser();
-//        List<FsUser> list = fsUserService.selectFsUserList(fsUser);
-//        List<FsUser> fsUserList = list.stream().peek(v -> v.setPhone(encryptPhone(v.getPhone()))).collect(Collectors.toList());
-//
-//        // 分批次处理,一次提交500条
-//        List<List<FsUser>> batches = Lists.partition(fsUserList, 500);
-//        batches.forEach(batch -> {
-//            SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
-//            try {
-//                FsUserMapper mapper = sqlSession.getMapper(FsUserMapper.class);
-//                batch.forEach(mapper::updateFsUser);
-//                sqlSession.commit();
-//            } finally {
-//                sqlSession.close();
-//            }
-//        });
-//
-//    }
 
 }

+ 345 - 148
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -15,6 +15,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.QwIpadTotalVo;
 import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.dto.BatchSendCourseAllDTO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.ITencentCloudCosService;
@@ -28,9 +29,15 @@ 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;
+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;
@@ -47,11 +54,16 @@ import com.fs.his.service.impl.FsPackageOrderServiceImpl;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.hisStore.service.IFsStoreScrmService;
+import com.fs.hisStore.domain.FsStoreOrderScrm;
 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;
+import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.google.gson.Gson;
@@ -60,10 +72,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Component("task")
@@ -86,7 +102,7 @@ public class Task {
     private IFsFollowService fsFollowService;
     @Autowired
     private IFsStoreAfterSalesService fsStoreAfterSalesService;
-//    @Autowired
+    //    @Autowired
 //    IErpOrderService erpOrderService;
     @Autowired
     FsIntegralOrderMapper integralOrderMapper;
@@ -127,7 +143,7 @@ public class Task {
     FsPackageOrderServiceImpl packageOrderService;
     @Autowired
     private IFsStoreOrderLogsService fsStoreOrderLogsService;
-    org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
+    org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
     @Autowired
     IFsDoctorService doctorService;
     @Autowired
@@ -159,6 +175,10 @@ public class Task {
     private IQwCompanyService qwCompanyService;
     @Autowired
     private IQwUserService qwUserService;
+    @Autowired
+    private OpenIMService openIMService;
+    @Autowired
+    public RedisTemplate redisTemplate;
 
     @Autowired
     private ICompanyUserService userService;
@@ -166,6 +186,97 @@ public class Task {
     @Autowired
     private IFastgptEventLogTotalService fastgptEventLogTotalService;
 
+    @Autowired
+    private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
+    @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);
+        }
+    }
+
+    /**
+     * 一键生成语音定时任务
+     */
+    public void ConsumerSopTempVoice() {
+        try {
+            Long newCompanyUserId = redisCache.popVoiceKey(SOP_TEMP_VOICE_KEY);
+            if (newCompanyUserId != null) {
+                List<QwSopTempVoice> sopTempVoices = redisCache.getVoiceAllList(SOP_TEMP_VOICE_KEY + ":" + newCompanyUserId);
+                if (sopTempVoices != null && !sopTempVoices.isEmpty()) {
+                    try {
+                        for (QwSopTempVoice qwSopTempVoice : sopTempVoices) {
+                            try {
+                                AudioVO audioVO = new AudioVO();
+                                List<FastgptChatVoiceHomo> homos = fastgptChatVoiceHomoMapper.selectFastgptChatVoiceHomoList(new FastgptChatVoiceHomo());
+                                audioVO = AudioUtils.createUserUrlAndUrl(homos, qwSopTempVoice.getCompanyUserId(), qwSopTempVoice.getVoiceTxt());
+                                if (audioVO != null && audioVO.getWavUrl() != null && audioVO.getUrl() != null) {
+                                    qwSopTempVoice.setVoiceUrl(audioVO.getUrl());
+                                    qwSopTempVoice.setUserVoiceUrl(audioVO.getWavUrl());
+                                    qwSopTempVoice.setDuration(audioVO.getDuration());
+                                    qwSopTempVoice.setRecordType(1);
+                                    qwSopTempVoiceService.updateQwSopTempVoice(qwSopTempVoice);
+                                }
+                            } catch (Exception e) {
+
+                            }
+                        }
+                    } finally {
+                        redisCache.deleteObject(SOP_TEMP_VOICE_KEY + ":" + newCompanyUserId);
+                    }
+                }
+            } else {
+                log.info("没有需要生成的语音");
+            }
+        } catch (Exception e) {
+            log.error("生成语音定时任务执行异常", e);
+        }
+    }
+
     @Autowired
     private IFsStoreScrmService fsStoreScrmService;
 
@@ -233,7 +344,7 @@ public class Task {
                         FastgptEventLogTotal eventLogTotal = new FastgptEventLogTotal();
                         eventLogTotal.setId(info.getId());
                         eventLogTotal.setCount(newCount);
-                        if(!processedKeys.contains(uniqueKey)) {
+                        if (!processedKeys.contains(uniqueKey)) {
                             toUpdateList.add(eventLogTotal);
                             // 标记为已处理
                             processedKeys.add(uniqueKey);
@@ -241,7 +352,7 @@ public class Task {
                     }
                 } else {
                     total.setStatTime(dateTime);
-                    if(!processedKeys.contains(uniqueKey)) {
+                    if (!processedKeys.contains(uniqueKey)) {
                         toInsertList.add(total);
                         // 标记为已处理
                         processedKeys.add(uniqueKey);
@@ -344,7 +455,7 @@ public class Task {
                         FastgptEventLogTotal eventLogTotalNew = new FastgptEventLogTotal();
                         eventLogTotalNew.setId(info.getId());
                         eventLogTotalNew.setCount(totalCount);
-                        if(!processedKeys.contains(uniqueKey)){
+                        if (!processedKeys.contains(uniqueKey)) {
                             toUpdateList.add(eventLogTotalNew);
                             // 标记为已处理
                             processedKeys.add(uniqueKey);
@@ -361,7 +472,7 @@ public class Task {
                     eventLogTotal.setQwUserId(tokenLog.getQwUserId());
                     eventLogTotal.setStatTime(dateTime);
 
-                    if(!processedKeys.contains(uniqueKey)) {
+                    if (!processedKeys.contains(uniqueKey)) {
                         toInsertList.add(eventLogTotal);
                         // 标记为已处理
                         processedKeys.add(uniqueKey);
@@ -387,23 +498,23 @@ public class Task {
 
 
     //定时查询ipad主机使用情况,建议每天凌晨1点执行一次
-    public void totalIpadTask(){
+    public void totalIpadTask() {
         String dateTime = DateUtils.addDateDays(-1); // 昨天
         List<QwIpadTotalVo> qwIpadTotalVos = userService.selectCompanyByIpadStatusCount();
-        if(qwIpadTotalVos != null && !qwIpadTotalVos.isEmpty()){
+        if (qwIpadTotalVos != null && !qwIpadTotalVos.isEmpty()) {
             qwIpadTotalVos.forEach(qwIpadTotalVo ->
                     qwIpadTotalVo.setStatTime(dateTime)
             );
             int a = userService.insertQwIpadTotal(qwIpadTotalVos);
-            if(a == 0){
+            if (a == 0) {
                 log.error("插入ipad主机失败");
             }
-        }else{
+        } else {
             log.error("查询没有数据");
         }
     }
 
-    public void addQwUserName(){
+    public void addQwUserName() {
         QwCompany qwCompany = new QwCompany();
         List<QwCompany> companyList = qwCompanyService.selectQwCompanyList(qwCompany);
         for (QwCompany company : companyList) {
@@ -411,50 +522,56 @@ public class Task {
         }
     }
 
-    public void videoTranscode() throws Exception
-    {
+    public void videoTranscode() throws Exception {
 
         tencentCloudCosService.videoTranscode();
     }
 
-    public void updateUrl() throws Exception
-    {
+    public void updateUrl() throws Exception {
 
         tencentCloudCosService.updateUrl();
     }
-    public void addPrescribeImg() throws Exception
-    {
-       List<Long> ids= fsPrescribeService.selectFsPrescribeByPrescribeIdByOrderType();
+
+    public void addPrescribeImg() throws Exception {
+        List<Long> ids = fsPrescribeService.selectFsPrescribeByPrescribeIdByOrderType();
         for (Long id : ids) {
             System.out.println(id);
             fsPrescribeService.PrescribeStoreImg(id);
         }
     }
 
-    public void addQwWatchLog() throws Exception
-    {
+    public void addQwWatchLog() throws Exception {
+        LocalDateTime now = LocalDateTime.now().withSecond(0);
         fsCourseWatchLogService.addCourseWatchLogDay();
     }
 
 
-    public void transferLog() throws Exception
-    {
+    public void addQwWatchLogMinute(Integer minute) throws Exception{
+        log.info("定时更新看课111");
+        if(minute == null) minute = 30;
+        LocalDateTime now = LocalDateTime.now().withSecond(0);
+        LocalDateTime start = now.minusMinutes(minute);
+        LocalDateTime end = now.minusSeconds(1);
+        fsCourseWatchLogService.addCourseWatchLogDayMinute(start, end);
+    }
+
+    public void transferLog() throws Exception {
         qwExternalContactTransferLogService.updateQwExternalContactTransferLogByStatus();
 
 
     }
 
-    public void isArtificial() throws Exception
-    {
+    public void isArtificial() throws Exception {
         fastGptChatSessionMapper.updateFastGptChatSessionByIsReply();
 
     }
-    public void expirationQwAppCountWay(){
+
+    public void expirationQwAppCountWay() {
         qwAppContactWayService.expirationQwAppCountWay();
 
     }
-    public void sendOrderMsg() throws Exception
-    {
+
+    public void sendOrderMsg() throws Exception {
         List<FsStoreOrder> fsStoreOrders = fsStoreOrderMapper.selectStoreOrderIdByFollow();
         for (FsStoreOrder fsStoreOrder : fsStoreOrders) {
 
@@ -462,50 +579,47 @@ public class Task {
         }
 
     }
-    public void redPacketSubMoney() throws Exception
-    {
+
+    public void redPacketSubMoney() throws Exception {
         List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByCompany();
         for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
-            companyService.subtractCompanyMoney(redPacketMoneyVO.getMoney(),redPacketMoneyVO.getCompanyId());
+            companyService.subtractCompanyMoney(redPacketMoneyVO.getMoney(), redPacketMoneyVO.getCompanyId());
         }
     }
 
 
-    public void redPacketAddMoney() throws Exception
-    {
+    public void redPacketAddMoney() throws Exception {
         List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseAddRedPacketLogByCompany();
         for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
-            companyService.addRedPacketCompanyMoney(redPacketMoneyVO.getMoney(),redPacketMoneyVO.getCompanyId());
+            companyService.addRedPacketCompanyMoney(redPacketMoneyVO.getMoney(), redPacketMoneyVO.getCompanyId());
         }
     }
 
-    public void updateCompanyUserStatus()
-    {
+    public void updateCompanyUserStatus() {
         CompanyUser user = new CompanyUser();
         user.setStatus("0");
         user.setDelFlag("0");
         List<CompanyUser> companyUsers = companyUserMapper.selectCompanyUserList(user);
         for (CompanyUser companyUser : companyUsers) {
-            if(SecurityUtils.matchesPassword("123456", companyUser.getPassword())){
+            if (SecurityUtils.matchesPassword("123456", companyUser.getPassword())) {
                 companyUser.setStatus("1");
                 companyUserMapper.updateCompanyUser(companyUser);
-                logger.info("密码为123456 停用账号:"+companyUser.getUserId()+":"+companyUser.getNickName());
+                logger.info("密码为123456 停用账号:" + companyUser.getUserId() + ":" + companyUser.getNickName());
             }
         }
     }
-    public void couponStatus()
-    {
+
+    public void couponStatus() {
         fsUserCouponMapper.updateFsUserCouponStatusByLimtTime();
     }
 
     //每10秒执行一次
-    public void auditPrescribe()
-    {
+    public void auditPrescribe() {
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.inquiryConfig");
         String configValue = sysConfig.getConfigValue();
         Map<String, Object> config = (Map<String, Object>) JSON.parse(configValue);
-        boolean isAudit = (boolean)config.get("isAutoPrescribeAudit");
-        if (isAudit){
+        boolean isAudit = (boolean) config.get("isAutoPrescribeAudit");
+        if (isAudit) {
             fsPrescribeService.auditPrescribe();
 
             try {
@@ -516,24 +630,52 @@ public class Task {
         }
     }
 
-    public void deliveryOp()
-    {
+    public void deliveryOp() {
         IErpOrderService erpOrderService = getErpService();
         List<FsStoreOrder> orders = null;
-        if (erpOrderService == gyOrderService){
+        if (erpOrderService == gyOrderService) {
             orders = fsStoreOrderMapper.selectOmsOrderdeliveryOp();
-        } else if (erpOrderService == wdtOrderService || erpOrderService == dfOrderService || erpOrderService == jSTOrderService){
+        } else if (erpOrderService == wdtOrderService || erpOrderService == dfOrderService || erpOrderService == jSTOrderService) {
             orders = fsStoreOrderMapper.selectWdtOmsOrderdeliveryOp();
         }
-
-
         for(FsStoreOrder order:orders){
-
             ErpOrderQueryRequert request=new ErpOrderQueryRequert();
+            request.setCode(order.getExtendOrderId());
+            if (erpOrderService != null) {
+                ErpOrderQueryResponse response = erpOrderService.getOrder(request);
+                if (erpOrderService != dfOrderService) {
+                    if (response.getOrders() != null && response.getOrders().size() > 0) {
+                        for (ErpOrderQuery orderQuery : response.getOrders()) {
+                            if (orderQuery.getDeliverys() != null && orderQuery.getDeliverys().size() > 0) {
+                                for (ErpDeliverys delivery : orderQuery.getDeliverys()) {
+                                    if (delivery.getDelivery() && StringUtils.isNotEmpty(delivery.getMail_no())) {
+                                        //更新商订单状态 删除REDIS
+                                        fsStoreOrderService.deliveryOrder(order.getOrderCode(), delivery.getMail_no(), delivery.getExpress_code(), delivery.getExpress_name());
+                                        redisCache.deleteObject("delivery" + ":" + order.getExtendOrderId());
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 
+    public void deliveryOpScrm()
+    {
+        IErpOrderService erpOrderService = getErpService();
+        List<FsStoreOrderScrm> orders = null;
+        if (erpOrderService == gyOrderService){
+            orders = fsStoreOrderMapper.selectOmsOrderdeliveryOpScrm();
+        } else if (erpOrderService == wdtOrderService || erpOrderService == dfOrderService || erpOrderService == jSTOrderService){
+            orders = fsStoreOrderMapper.selectWdtOmsOrderdeliveryOpScrm();
+        }
+        for(FsStoreOrderScrm order:orders){
+            ErpOrderQueryRequert request=new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());
             if (erpOrderService != null){
-                ErpOrderQueryResponse response=erpOrderService.getOrder(request);
+                ErpOrderQueryResponse response=erpOrderService.getScrmOrder(request);
                 if (erpOrderService != dfOrderService){
                     if(response.getOrders()!=null&&response.getOrders().size()>0){
                         for(ErpOrderQuery orderQuery : response.getOrders()){
@@ -541,21 +683,18 @@ public class Task {
                                 for(ErpDeliverys delivery:orderQuery.getDeliverys()){
                                     if(delivery.getDelivery()&& StringUtils.isNotEmpty(delivery.getMail_no())){
                                         //更新商订单状态 删除REDIS
-                                        fsStoreOrderService.deliveryOrder(order.getOrderCode(),delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
+                                        fsStoreOrderService.deliveryOrderScrm(order.getOrderCode(),delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
                                         redisCache.deleteObject("delivery"+":"+order.getExtendOrderId());
                                     }
                                 }
-
                             }
                         }
                     }
                 }
             }
-
         }
 
 
-
     }
 
 
@@ -563,11 +702,11 @@ public class Task {
     {
         IErpOrderService erpOrderService = getErpService();
         List<FsStoreOrder> orders = null;
-        if (erpOrderService !=null && erpOrderService == dfOrderService){
+        if (erpOrderService != null && erpOrderService == dfOrderService) {
             orders = fsStoreOrderMapper.selectShippedOrder();
-            if(orders!=null&& !orders.isEmpty()){
+            if (orders != null && !orders.isEmpty()) {
                 List<CompletableFuture<Void>> futures = new ArrayList<>();
-                for(FsStoreOrder order:orders){
+                for (FsStoreOrder order : orders) {
                     CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                         erpOrderService.getOrderDeliveryStatus(order);
                     });
@@ -578,15 +717,14 @@ public class Task {
         }
     }
 
-    public void CreateOmsAndHis()
-    {
+    public void CreateOmsAndHis() {
         List<Long> omsList = fsStoreOrderMapper.selectFsStoreOrderNoCreateOms();
-        logger.info("推送订单id====>{}",omsList);
+        logger.info("推送订单id====>{}", omsList);
         for (Long l : omsList) {
             try {
                 fsStoreOrderService.createOmsOrder(l);
             } catch (Exception e) {
-                logger.error("推送订单异常:",e);
+                logger.error("推送订单异常:", e);
             }
         }
 //        List<Long> tuiOrderList = fsStoreOrderMapper.selectFsStoreOrderNoTuiOrder();
@@ -599,31 +737,30 @@ public class Task {
 //        }
     }
 
-    public void createFollow()
-    {
+    public void createFollow() {
         List<FsStoreOrder> orders = fsStoreOrderMapper.selectStoreOrderIdByFollow();
         for (FsStoreOrder order : orders) {
             try {
 
                 fsStoreOrderService.addFsFollowByStoreOrder(order);
-            }catch (Exception e){
-                logger.info("创建随访错误:"+order);
+            } catch (Exception e) {
+                logger.info("创建随访错误:" + order);
             }
         }
     }
+
     @Autowired
     IFsStoreSubOrderService fsStoreSubOrderService;
 
 
-    public void puSubStoreOrder()
-    {
+    public void puSubStoreOrder() {
         List<Long> longs = fsStoreSubOrderService.selectFsStoreSubOrderByNoPush();
-        int i=0;
+        int i = 0;
         for (Long aLong : longs) {
             FsSubOrderResultVO fsSubOrderResultVO = fsStoreSubOrderService.TuiFsStoreSubOrderByStoreOrder(aLong);
-            if (fsSubOrderResultVO!=null&&fsSubOrderResultVO.getCode()!=null&&fsSubOrderResultVO.getCode()==1){
+            if (fsSubOrderResultVO != null && fsSubOrderResultVO.getCode() != null && fsSubOrderResultVO.getCode() == 1) {
                 i++;
-                if (i>65){
+                if (i > 65) {
                     return;
                 }
             }
@@ -631,14 +768,13 @@ public class Task {
         }
     }
 
-    public void refundOp()
-    {
-        List<FsStoreAfterSales> list=fsStoreAfterSalesService.selectFsStoreAfterSalesByDoAudit();
-        if(list!=null){
-            for(FsStoreAfterSales afterSales:list){
+    public void refundOp() {
+        List<FsStoreAfterSales> list = fsStoreAfterSalesService.selectFsStoreAfterSalesByDoAudit();
+        if (list != null) {
+            for (FsStoreAfterSales afterSales : list) {
                 try {
                     fsStoreAfterSalesService.auditing(afterSales);
-                }catch (Exception e){
+                } catch (Exception e) {
 
                 }
 
@@ -646,16 +782,17 @@ public class Task {
         }
 
     }
+
     public void isAfterSales() {
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.store");
         String configValue = sysConfig.getConfigValue();
         Map<String, Object> config = (Map<String, Object>) JSON.parse(configValue);
-        Integer storeAfterSalesDay = (Integer)config.get("storeAfterSalesDay");
+        Integer storeAfterSalesDay = (Integer) config.get("storeAfterSalesDay");
         List<FsStoreOrder> fsStoreOrders = fsStoreOrderMapper.selectFsStoreOrderNoIsAfterSales(storeAfterSalesDay);
         for (FsStoreOrder fsStoreOrder : fsStoreOrders) {
             try {
                 fsStoreOrderService.addIntegralAndShareByStoreOrder(fsStoreOrder);
-            }catch (Exception e) {
+            } catch (Exception e) {
                 logger.info("分账错误: " + fsStoreOrder.getOrderCode());
             }
         }
@@ -663,26 +800,25 @@ public class Task {
     }
 
 
-    public void integralOrderStatus(){
+    public void integralOrderStatus() {
         integralOrderMapper.updatePackageOrderStatusByDeliveryTime();
     }
 
-    public void packageStatus()
-    {
+    public void packageStatus() {
         fsPackageOrderMapper.updatePackageOrderStatusByFinishTime();
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.store");
         String configValue = sysConfig.getConfigValue();
         Map<String, Object> config = (Map<String, Object>) JSON.parse(configValue);
-        Integer unPayTime = (Integer)config.get("unPayTime");
+        Integer unPayTime = (Integer) config.get("unPayTime");
         fsPackageOrderMapper.updatePackageOrderStatusByStatus(unPayTime);
     }
 
-    public void endFollow(){
+    public void endFollow() {
         fsStoreOrderService.endFollow();
     }
 
 
-    public void erDelivery(){
+    public void erDelivery() {
         fsStoreOrderService.endDeliveryOrder();
     }
 
@@ -690,15 +826,16 @@ public class Task {
 
     @Autowired
     private ICompanyService companyService;
-    public void addCompanyMoney(){
+
+    public void addCompanyMoney() {
         List<FsStoreOrder> orders = fsStoreOrderMapper.selectOrderIds();
-        for (FsStoreOrder order : orders){
+        for (FsStoreOrder order : orders) {
             CompanyMoneyLogs moneyLog1 = moneyLogsMapper.selectCompanyMoneyLogsByOrderId(order.getOrderId(), 5);
-            CompanyMoneyLogs moneyLog2 = moneyLogsMapper.selectCompanyMoneyLogsByOrderId(order.getOrderId(),3);
-            if (moneyLog1==null){
+            CompanyMoneyLogs moneyLog2 = moneyLogsMapper.selectCompanyMoneyLogsByOrderId(order.getOrderId(), 3);
+            if (moneyLog1 == null) {
                 companyService.subtractCompanyMoney(order);
             }
-            if (moneyLog2==null){
+            if (moneyLog2 == null) {
                 companyService.addCompanyMoney(order);
                 FsStoreOrder orderMap = new FsStoreOrder();
                 orderMap.setOrderId(order.getOrderId());
@@ -709,28 +846,26 @@ public class Task {
     }
 
     //每天执行一次
-    public void syncExpress()
-    {
-        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIds();
+    public void syncExpress() {
+        List<Long> ids = fsStoreOrderMapper.selectSyncExpressIds();
         for (Long id : ids) {
             fsStoreOrderService.syncExpress(id);
         }
 
     }
 
-    public void refundCompanyMoney(){
+    public void refundCompanyMoney() {
         List<FsStoreOrder> list = fsStoreOrderMapper.selectOrders();
-        for (FsStoreOrder order : list ){
+        for (FsStoreOrder order : list) {
             companyService.refundCompanyMoney(order);
         }
     }
 
-    public void subIntegral()
-    {
+    public void subIntegral() {
         fsUserIntegralLogsService.subFsUserIntegralLogsByOrder5();
     }
 
-    public void finishInquiry(){
+    public void finishInquiry() {
         List<FsInquiryOrder> orders = inquiryOrderMapper.selectFsInquiryOrderByFinish();
         for (FsInquiryOrder order : orders) {
             // 订单已超过48小时,执行关闭操作
@@ -739,20 +874,20 @@ public class Task {
             param.setDoctorId(order.getDoctorId());
             try {
                 iFsInquiryOrderService.autoFinishOrder(param);
-            }catch (Exception e){
-                logger.info("订单已超过48小时关闭异常"+param);
+            } catch (Exception e) {
+                logger.info("订单已超过48小时关闭异常" + param);
             }
 
         }
     }
 
 
-    public void finishStoreOrderByXN(){
+    public void finishStoreOrderByXN() {
         List<FsStoreOrder> orders = fsStoreOrderMapper.selectFinishStoreOrderByXN();
-        if (orders!=null&&orders.size()>0){
+        if (orders != null && orders.size() > 0) {
             for (FsStoreOrder o : orders) {
                 FsStoreOrder order = fsStoreOrderMapper.selectFsStoreOrderByOrderId(o.getOrderId());
-                if (order.getStatus()!= 2) {
+                if (order.getStatus() != 2) {
                     continue;
                 }
                 FsStoreOrder o1 = new FsStoreOrder();
@@ -763,20 +898,20 @@ public class Task {
                 int i = fsStoreOrderMapper.updateFsStoreOrder(o1);
                 fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.FINISH_ORDER.getValue(),
                         FsStoreOrderLogEnum.FINISH_ORDER.getDesc());
-                if (order.getCompanyId()!=null&&order.getTuiMoneyStatus()==0&&order.getPayType()==1){
+                if (order.getCompanyId() != null && order.getTuiMoneyStatus() == 0 && order.getPayType() == 1) {
                     companyService.addCompanyMoney(order);
                 }
             }
         }
     }
 
-    public void inquirySendSms(){
+    public void inquirySendSms() {
         List<FsInquiryOrder> orders = inquiryOrderMapper.selectFsInquiryOrderBySendSms();
-        for (FsInquiryOrder order : orders){
-            FsInquiryOrderPatientDTO patientDTO = JSON.parseObject(order.getPatientJson(),FsInquiryOrderPatientDTO.class);
-            if (patientDTO!=null&&patientDTO.getPatientName()!=null){
+        for (FsInquiryOrder order : orders) {
+            FsInquiryOrderPatientDTO patientDTO = JSON.parseObject(order.getPatientJson(), FsInquiryOrderPatientDTO.class);
+            if (patientDTO != null && patientDTO.getPatientName() != null) {
                 FsUser fsUser = fsUserMapper.selectFsUserByUserId(order.getUserId());
-                if (fsUser!=null&&fsUser.getPhone()!=null){
+                if (fsUser != null && fsUser.getPhone() != null) {
                     smsService.sendUserSms(fsUser.getPhone(), patientDTO.getPatientName(), "2");
                     order.setIsSendSms(1);
                     inquiryOrderMapper.updateFsInquiryOrder(order);
@@ -787,9 +922,9 @@ public class Task {
     }
 
     //处理30天问题件
-    public void clearProblemOrder(){
+    public void clearProblemOrder() {
         List<FsStoreOrder> orders = fsStoreOrderMapper.selectFsStoreOrderByProblemOrder();
-        for (FsStoreOrder order : orders){
+        for (FsStoreOrder order : orders) {
             FsStoreOrder map = new FsStoreOrder();
             map.setStatus(4);
             map.setOrderId(order.getOrderId());
@@ -800,14 +935,14 @@ public class Task {
 
 
     //30天无通话记录回收坐席
-    public void recoverCompanyCaller(){
+    public void recoverCompanyCaller() {
         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.store");
         StoreConfig fsPayConfig = new Gson().fromJson(sysConfig.getConfigValue(), StoreConfig.class);
         Integer days = fsPayConfig.getStoreCall();
         List<CompanyVoiceCaller> list = companyVoiceCallerMapper.selectCompanyVoiceCallerByRecover(days);
-        for (CompanyVoiceCaller caller : list){
-            Long count = companyVoiceLogsMapper.selectCompanyVoiceLogsCountByCallerNo(caller.getCallerNo(),caller.getBindTime());
-            if (count==0){
+        for (CompanyVoiceCaller caller : list) {
+            Long count = companyVoiceLogsMapper.selectCompanyVoiceLogsCountByCallerNo(caller.getCallerNo(), caller.getBindTime());
+            if (count == 0) {
                 caller.setCompanyId(0l);
                 caller.setCompanyUserId(0l);
                 caller.setMobile("");
@@ -820,32 +955,27 @@ public class Task {
 
 
     public void tb() {
-        packageOrderService.payConfirm("", "1780763211956486144", "1075999515888117190", "14", 1,null,null);
+        packageOrderService.payConfirm("", "1780763211956486144", "1075999515888117190", "14", 1, null, null);
     }
 
 
-
-
-
-
-
-    public void addSend(){
-       String userId="4048905872";
-       String doctorId="147";
-        String storeOrderId="470920";
-        String followId="1062986";
+    public void addSend() {
+        String userId = "4048905872";
+        String doctorId = "147";
+        String storeOrderId = "470920";
+        String followId = "1062986";
         //发送给用户
-        MsgDTO msgDTO=new MsgDTO();
-        MsgCustomDTO customDTO=new MsgCustomDTO();
+        MsgDTO msgDTO = new MsgDTO();
+        MsgCustomDTO customDTO = new MsgCustomDTO();
         customDTO.setType("startDrugReport");
         customDTO.setImType(2);
         customDTO.setOrderId(storeOrderId);
         customDTO.setFollowId(followId);
         msgDTO.setCloudCustomData(JSONUtil.toJsonStr(customDTO));
-        msgDTO.setFrom_Account("U-"+userId);
-        msgDTO.setTo_Account("D-"+doctorId);
-        List<MsgDataDTO> msgs=new ArrayList<>();
-        MsgDataDTO msg=new MsgDataDTO();
+        msgDTO.setFrom_Account("U-" + userId);
+        msgDTO.setTo_Account("D-" + doctorId);
+        List<MsgDataDTO> msgs = new ArrayList<>();
+        MsgDataDTO msg = new MsgDataDTO();
         msg.setMsgType("TIMTextElem");
         msg.setMsgContent(new MsgDataFormatDTO("您好"));
         msgs.add(msg);
@@ -1131,7 +1261,7 @@ public class Task {
 //                iFsPackageOrderService.updateFsPackageOrder(order);
 //                num++;
 //            }
-////
+    /// /
 //        }
 //
 //        logger.info("所有订单同步完成:"+num);
@@ -1207,7 +1337,7 @@ public class Task {
 //}
 
 
-        //同步流水
+    //同步流水
 //    public void task(){
 //        CompanyMoneyLogs item = moneyLogsMapper.selectCompanyMoneyLogsById(248884L);
 //        String logsId=item.getLogsId().toString();
@@ -1310,27 +1440,27 @@ public class Task {
     private IErpOrderService getErpService() {
         FsSysConfig sysConfig = configUtil.getSysConfig();
         Integer erpOpen = sysConfig.getErpOpen();
-        if (erpOpen != null && erpOpen == 1){
+        if (erpOpen != null && erpOpen == 1) {
             //判断erp类型
             Integer erpType = sysConfig.getErpType();
-            if (erpType != null){
+            if (erpType != null) {
                 IErpOrderService erpOrderService = null;
-                if (erpType == 1){
+                if (erpType == 1) {
                     //管易
-                    erpOrderService =  gyOrderService;
-                } else if (erpType == 2){
+                    erpOrderService = gyOrderService;
+                } else if (erpType == 2) {
                     //旺店通
-                    erpOrderService =  wdtOrderService;
-                } else if (erpType == 3){
+                    erpOrderService = wdtOrderService;
+                } else if (erpType == 3) {
                     //
-                    erpOrderService =  hzOMSErpOrderService;
-                } else if (erpType == 4){
+                    erpOrderService = hzOMSErpOrderService;
+                } else if (erpType == 4) {
                     //代服
-                    erpOrderService =  dfOrderService;
-                }else if(erpType == 5){
-                    erpOrderService=jSTOrderService;
-                }else if(erpType == 6){
-                    erpOrderService=k9OrderService;
+                    erpOrderService = dfOrderService;
+                } else if (erpType == 5) {
+                    erpOrderService = jSTOrderService;
+                } else if (erpType == 6) {
+                    erpOrderService = k9OrderService;
                 }
                 return erpOrderService;
 
@@ -1339,4 +1469,71 @@ public class Task {
         }
         return null;
     }
+
+
+    /**
+     * 定时任务-im会员定时发课,每一分钟执行一次
+     */
+    public void sendOpenImCourse(){
+        String redisKey = "openIm:batchSendMsg:sendCourse";
+        Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+        if(cacheMap == null || cacheMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在对应的redisKey==================");
+            return;
+        }
+        List<Map.Entry<String, BatchSendCourseAllDTO>> toSendMap = cacheMap.entrySet().parallelStream().filter((v) -> {
+            String[] split = v.getKey().split(":");
+            long timestamp = Long.parseLong(split[2]);
+            return timestamp < System.currentTimeMillis();
+        }).collect(Collectors.toList());
+
+        if(toSendMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在可执行的发课任务==================");
+            return;
+        }
+        for (Map.Entry<String, BatchSendCourseAllDTO> entry : toSendMap) {
+            //执行发送消息任务
+            BatchSendCourseAllDTO batchSendCourseAllDTO = entry.getValue();
+            openIMService.batchSendCourseTask(batchSendCourseAllDTO.getBatchSendCourseDTO(), batchSendCourseAllDTO.getOpenImBatchMsgDTO(), batchSendCourseAllDTO.getProject(), batchSendCourseAllDTO.getImMsgSendDetailList());
+
+            // 执行结束,删除
+            this.redisTemplate.<String, BatchSendCourseAllDTO>opsForHash().delete(redisKey, entry.getKey());
+
+        }
+
+    }
+
+
+    /**
+     * 定时任务-im 会员催课,每一分钟执行一次
+     */
+    public void urgeOpenImCourse(){
+        String redisKey = "openIm:batchSendMsg:urgeCourse";
+        Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+        if(cacheMap == null || cacheMap.isEmpty()){
+            logger.info("===================== 会员-IM发消息催课,不存在对应的redisKey==================");
+            return;
+        }
+        List<Map.Entry<String, BatchSendCourseAllDTO>> toSendMap = cacheMap.entrySet().parallelStream().filter((v) -> {
+            String[] split = v.getKey().split(":");
+            long timestamp = Long.parseLong(split[2]);
+            return timestamp < System.currentTimeMillis();
+        }).collect(Collectors.toList());
+
+        if(toSendMap.isEmpty()){
+            logger.info("===================== 会员-IM发消息催课,不存在可发送的消息==================");
+            return;
+        }
+        for (Map.Entry<String, BatchSendCourseAllDTO> entry : toSendMap) {
+            //执行发送消息任务
+            BatchSendCourseAllDTO batchSendCourseAllDTO = entry.getValue();
+            openIMService.batchUrgeCourseTask(batchSendCourseAllDTO.getOpenImBatchMsgDTO(), batchSendCourseAllDTO.getImMsgSendDetailList());
+
+            // 执行结束,删除
+            this.redisTemplate.<String, BatchSendCourseAllDTO>opsForHash().delete(redisKey, entry.getKey());
+
+        }
+    }
+
+
 }

+ 93 - 14
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java

@@ -9,24 +9,34 @@ 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.dto.StoreOrderProductDTO;
 import com.fs.hisStore.param.FsStoreOrderParam;
 import com.fs.hisStore.service.*;
+import com.fs.hisStore.vo.FsStoreOrderErpExportVO;
 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.*;
 import org.springframework.web.multipart.MultipartFile;
 
+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")
@@ -52,6 +62,9 @@ public class FsStoreHealthOrderScrmController extends BaseController {
     @Autowired
     private ICompanyMoneyLogsService moneyLogsService;
 
+    @Autowired
+    private IFsStoreOrderDfService fsStoreOrderDfService;
+
     // 允许的文件扩展名
     private static final String[] ALLOWED_EXCEL_EXTENSIONS = {".xlsx", ".xls"};
 
@@ -61,9 +74,9 @@ public class FsStoreHealthOrderScrmController extends BaseController {
     /**
      * 查询健康商城订单列表
      */
-    @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:list')")
-    @GetMapping("/healthList")
-    public TableDataInfo healthStoreList(FsStoreOrderParam param) {
+//    @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:list')")
+      @PostMapping("/healthList")
+      public TableDataInfo healthStoreList(@RequestBody FsStoreOrderParam param) {
         startPage();
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
@@ -79,16 +92,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;
     }
 
     /**
@@ -96,8 +143,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);
@@ -118,7 +165,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) {
@@ -131,8 +178,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, "订单数据");
+        }
     }
 
     /**
@@ -140,8 +203,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);
@@ -162,9 +225,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, "订单数据");
+        }
     }
 
 

+ 308 - 19
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,14 +23,24 @@ 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.FsStoreOrderScrmComment;
 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.IFsStoreOrderScrmCommentService;
 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.FsStoreOrderItemScrm;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
@@ -41,6 +53,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;
@@ -51,9 +65,12 @@ import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.fs.his.utils.PhoneUtil.encryptPhone;
 
 /**
  * 订单Controller
@@ -109,6 +126,18 @@ public class FsStoreOrderScrmController extends BaseController {
     @Autowired
     private TokenService tokenService;
 
+    @Autowired
+    SysConfigMapper sysConfigMapper;
+
+    @Autowired
+    private IFsDfAccountService fsDfAccountService;
+
+    @Autowired
+    private IFsStoreOrderDfService fsStoreOrderDfService;
+
+    @Autowired
+    private IFsStoreOrderLogsScrmService fsStoreOrderLogsService;
+
     private IErpOrderService getErpService(){
         //判断是否开启erp
         IErpOrderService erpOrderService = null;
@@ -144,8 +173,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 +190,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 +301,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,10 +323,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) {
-            for (FsStoreOrderExportVO vo : list) {
+            //获取当前账号角色权限
+            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+
+            for (FsStoreOrderErpExportVO vo : list) {
                 if (vo.getPhone() != null) {
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
@@ -275,8 +341,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, "订单数据");
+        }
     }
 
 
@@ -285,8 +367,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);
@@ -307,9 +389,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')")
@@ -553,7 +651,7 @@ public class FsStoreOrderScrmController extends BaseController {
         IErpOrderService erpOrderService = getErpService();
         ErpOrderQueryRequert request = new ErpOrderQueryRequert();
         request.setCode(order.getExtendOrderId());
-        ErpOrderQueryResponse response = erpOrderService.getOrder(request);
+        ErpOrderQueryResponse response = erpOrderService.getScrmOrder(request);
         if(response.getOrders()!=null&&response.getOrders().size()>0){
             for(ErpOrderQuery orderQuery : response.getOrders()){
                 if(orderQuery.getDeliverys()!=null&&orderQuery.getDeliverys().size()>0){
@@ -644,7 +742,7 @@ public class FsStoreOrderScrmController extends BaseController {
         IErpOrderService erpOrderService = getErpService();
         ErpOrderQueryRequert request = new ErpOrderQueryRequert();
         request.setCode(extendOrderId);
-        ErpOrderQueryResponse response = erpOrderService.getOrder(request);
+        ErpOrderQueryResponse response = erpOrderService.getScrmOrder(request);
         return R.ok().put("data",response);
     }
 
@@ -751,4 +849,195 @@ 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<String> list = new ArrayList<>();
+        if (CloudHostUtils.hasCloudHostName("金牛明医","康年堂")) {
+            List<FsDfAccount> erpAccounts = fsDfAccountService.selectFsDfAccountList(null);
+            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;
+    }
 }

+ 29 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java

@@ -1,6 +1,7 @@
 package com.fs.hisStore.controller;
 
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.domain.AlipayTradeRefundModel;
 import com.fs.bean.AliPayBean;
@@ -13,7 +14,12 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.utils.spring.SpringUtils;
 import com.fs.company.service.ICompanyService;
+import com.fs.config.cloud.CloudHostProper;
+import com.fs.his.domain.FsHfpayConfig;
+import com.fs.his.mapper.FsHfpayConfigMapper;
+import com.fs.hisStore.domain.FsPayConfigScrm;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.domain.HuiFuRefundResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
@@ -24,6 +30,7 @@ import com.fs.hisStore.param.FsStorePaymentParam;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStorePaymentScrmService;
 import com.fs.hisStore.vo.FsStorePaymentVO;
+import com.fs.system.service.ISysConfigService;
 import com.ijpay.alipay.AliPayApi;
 import com.ijpay.alipay.AliPayApiConfig;
 import com.ijpay.alipay.AliPayApiConfigKit;
@@ -63,6 +70,10 @@ public class FsStorePaymentScrmController extends BaseController
     private IFsStoreOrderScrmService orderService;
     @Autowired
     HuiFuService huiFuService;
+    @Autowired
+    private CloudHostProper cloudHostProper;
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询支付明细列表
      */
@@ -179,10 +190,27 @@ public class FsStorePaymentScrmController extends BaseController
         if(payment.getPayMoney().compareTo(fsStorePayment.getRefundMoney())==-1){
             return R.error("退款金额必须小于等于付款金额");
         }
-        if(payment.getPayTypeCode().equals("weixin")){
 
+        if(payment.getPayTypeCode().equals("weixin")){
+            String json = configService.selectConfigByKey("store.pay");
+            FsPayConfigScrm fsPayConfig = JSON.parseObject(json, FsPayConfigScrm.class);
             if (payment.getPayMode()!=null&&payment.getPayMode().equals("hf")){
+                String huifuId="";
+                FsHfpayConfigMapper fsHfpayConfigMapper = SpringUtils.getBean(FsHfpayConfigMapper.class);
+                if (payment.getAppId() != null) {
+                    FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(payment.getAppId());
+                    huifuId = fsHfpayConfig.getHuifuId();
+                } else {
+                    if (("益善缘".equals(cloudHostProper.getCompanyName()))) {
+
+                        FsHfpayConfig fsPayConfig2 = fsHfpayConfigMapper.selectByAppId("wx0d1a3dd485268521");
+                        huifuId = fsPayConfig2.getHuifuId();
+                    }else {
+                        huifuId=fsPayConfig.getHuifuId();
+                    }
+                }
                 V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
+                request.setHuifuId(huifuId);
                 request.setOrdAmt(payment.getPayMoney().toString());
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setReqSeqId("refund-"+payment.getPayCode());

+ 37 - 4
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -17,7 +17,9 @@ import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.service.IErpGoodsService;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.his.config.FsSysConfig;
+import com.fs.his.domain.FsStoreProductAttrValue;
 import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.his.mapper.FsStoreProductAttrValueMapper;
 import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.ConfigUtil;
@@ -93,7 +95,7 @@ public class MallStoreTask
     @Autowired
     private ISysConfigService configService;
     @Autowired
-    private FsStoreProductAttrValueScrmMapper fsStoreProductAttrValueMapper;
+    private FsStoreProductAttrValueMapper fsStoreProductAttrValueMapper;
     @Autowired
     private FsStorePaymentScrmMapper paymentMapper;
     @Autowired
@@ -232,8 +234,8 @@ public class MallStoreTask
         String json=configService.selectConfigByKey("store.config");
         StoreConfig config=JSONUtil.toBean(json,StoreConfig.class);
 
-        List<FsStoreProductAttrValueScrm> values = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueList(new FsStoreProductAttrValueScrm());
-        for (FsStoreProductAttrValueScrm value : values) {
+        List<FsStoreProductAttrValue> values = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueList(new FsStoreProductAttrValue());
+        for (FsStoreProductAttrValue value : values) {
             ErpGoodsQueryRequert query = new ErpGoodsQueryRequert();
             query.setCode(value.getBarCode());
             ErpGoodsQueryResponse goods = erpGoodsService.getGoods(query);
@@ -246,7 +248,7 @@ public class MallStoreTask
                     BigDecimal divide = salesPrice.multiply(new BigDecimal(config.getSalesPriceRate())).divide(new BigDecimal("100"));
                     System.out.println("代理价格"+divide);
                     System.out.println("成本价"+salesPrice);
-                    FsStoreProductAttrValueScrm va = new FsStoreProductAttrValueScrm();
+                    FsStoreProductAttrValue va = new FsStoreProductAttrValue();
                     va.setCost(salesPrice);
                     va.setAgentPrice(divide);
                     va.setId(value.getId());
@@ -255,9 +257,40 @@ public class MallStoreTask
             }
             System.out.println(goods);
         }
+    }
+
 
+    @Autowired
+    private FsStoreProductAttrValueScrmMapper fsStoreProductAttrValueScrmMapper;
+
+    public void storeProdUpdateCostPriceScrm()
+    {
+        String json=configService.selectConfigByKey("store.config");
+        StoreConfig config=JSONUtil.toBean(json,StoreConfig.class);
 
+        List<FsStoreProductAttrValueScrm> values = fsStoreProductAttrValueScrmMapper.selectFsStoreProductAttrValueList(new FsStoreProductAttrValueScrm());
+        for (FsStoreProductAttrValueScrm value : values) {
+            ErpGoodsQueryRequert query = new ErpGoodsQueryRequert();
+            query.setCode(value.getBarCode());
+            ErpGoodsQueryResponse goods = erpGoodsService.getGoodsScrm(query);
+            List<ErpGoods> items = goods.getItems();
 
+            if (items!=null&&items.size()>0){
+                ErpGoods erpGoods = items.get(0);
+                BigDecimal salesPrice = erpGoods.getSales_price();
+                if (salesPrice!=null&&salesPrice.compareTo(BigDecimal.ZERO) != 0){
+                    BigDecimal divide = salesPrice.multiply(new BigDecimal(config.getSalesPriceRate())).divide(new BigDecimal("100"));
+                    System.out.println("代理价格"+divide);
+                    System.out.println("成本价"+salesPrice);
+                    FsStoreProductAttrValueScrm va = new FsStoreProductAttrValueScrm();
+                    va.setCost(salesPrice);
+                    va.setAgentPrice(divide);
+                    va.setId(value.getId());
+                    fsStoreProductAttrValueScrmMapper.updateFsStoreProductAttrValue(va);
+                }
+            }
+            System.out.println(goods);
+        }
     }
     public void couponOp()
     {

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

+ 53 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactTransferCompanyAuditController.java

@@ -0,0 +1,53 @@
+package com.fs.qw.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.SecurityUtils;
+import com.fs.qw.domain.QwExternalContactTransferCompanyAudit;
+import com.fs.qw.domain.QwExternalContactTransferCompanyAuditUser;
+import com.fs.qw.dto.CompanyTransferAuditDTO;
+import com.fs.qw.service.IQwExternalContactTransferCompanyAuditService;
+import com.fs.qw.service.IQwExternalContactTransferCompanyAuditUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@RestController
+@RequestMapping("/qw/externalContactTransferCompanyAudit")
+public class QwExternalContactTransferCompanyAuditController extends BaseController {
+
+    @Autowired
+    private IQwExternalContactTransferCompanyAuditService auditService;
+    @Autowired
+    private IQwExternalContactTransferCompanyAuditUserService auditUserService;
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContactTransferCompanyAudit:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwExternalContactTransferCompanyAudit param) {
+        startPage();
+        List<QwExternalContactTransferCompanyAudit> list = auditService.selectQwExternalContactTransferCompanyAuditList(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContactTransferCompanyAudit:detail')")
+    @GetMapping("/detail/{auditId}")
+    public TableDataInfo detail(@PathVariable Long auditId) {
+        startPage();
+        List<QwExternalContactTransferCompanyAuditUser> listByAuditId = auditUserService.getListByAuditId(auditId);
+        return getDataTable(listByAuditId);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContactTransferCompanyAudit:audit')")
+    @PostMapping("/audit")
+    public AjaxResult audit(@Valid @RequestBody CompanyTransferAuditDTO param) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        auditService.audit(param, loginUser.getUser().getUserName());
+        return AjaxResult.success();
+    }
+
+}

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

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

@@ -52,6 +52,7 @@ public class QwSopController extends BaseController
     private FsUserCourseMapper fsUserCourseMapper;
     @Autowired
     private FsUserCourseVideoMapper fsUserCourseVideoMapper;
+
     /**
      * 查询企微sop列表
      */
@@ -133,7 +134,6 @@ public class QwSopController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwSop.setCreateBy(loginUser.getUser().getNickName());
         qwSop.setCreateTime(sdf.format(new Date()));
-
         return toAjax(qwSopService.insertQwSop(qwSop));
 
     }

+ 103 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwUserComplainRecordController.java

@@ -0,0 +1,103 @@
+package com.fs.qw.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.qw.domain.QwUserComplainRecord;
+import com.fs.qw.service.IQwUserComplainRecordService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 企微员工投诉记录Controller
+ *
+ * @author fs
+ * @date 2025-10-22
+ */
+@RestController
+@RequestMapping("/qw/record")
+public class QwUserComplainRecordController extends BaseController
+{
+    @Autowired
+    private IQwUserComplainRecordService qwUserComplainRecordService;
+
+    /**
+     * 查询企微员工投诉记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:record:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwUserComplainRecord qwUserComplainRecord)
+    {
+        startPage();
+        List<QwUserComplainRecord> list = qwUserComplainRecordService.selectQwUserComplainRecordList(qwUserComplainRecord);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出企微员工投诉记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:record:export')")
+    @Log(title = "企微员工投诉记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwUserComplainRecord qwUserComplainRecord)
+    {
+        List<QwUserComplainRecord> list = qwUserComplainRecordService.selectQwUserComplainRecordList(qwUserComplainRecord);
+        ExcelUtil<QwUserComplainRecord> util = new ExcelUtil<QwUserComplainRecord>(QwUserComplainRecord.class);
+        return util.exportExcel(list, "企微员工投诉记录数据");
+    }
+
+    /**
+     * 获取企微员工投诉记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:record:query')")
+    @GetMapping(value = "/{recordId}")
+    public AjaxResult getInfo(@PathVariable("recordId") Long recordId)
+    {
+        return AjaxResult.success(qwUserComplainRecordService.selectQwUserComplainRecordByRecordId(recordId));
+    }
+
+    /**
+     * 新增企微员工投诉记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:record:add')")
+    @Log(title = "企微员工投诉记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwUserComplainRecord qwUserComplainRecord)
+    {
+        return toAjax(qwUserComplainRecordService.insertQwUserComplainRecord(qwUserComplainRecord));
+    }
+
+    /**
+     * 修改企微员工投诉记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:record:edit')")
+    @Log(title = "企微员工投诉记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwUserComplainRecord qwUserComplainRecord)
+    {
+        return toAjax(qwUserComplainRecordService.updateQwUserComplainRecord(qwUserComplainRecord));
+    }
+
+    /**
+     * 删除企微员工投诉记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:record:remove')")
+    @Log(title = "企微员工投诉记录", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{recordIds}")
+    public AjaxResult remove(@PathVariable Long[] recordIds)
+    {
+        return toAjax(qwUserComplainRecordService.deleteQwUserComplainRecordByRecordIds(recordIds));
+    }
+}

+ 17 - 1
fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java

@@ -3,13 +3,21 @@ package com.fs.qw.controller;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.ServletUtils;
+import com.fs.qw.domain.QwExternalContactTransferCompanyAudit;
 import com.fs.qw.param.QwFsUserParam;
+import com.fs.qw.service.IQwExternalContactTransferCompanyAuditService;
 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.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 /**
  * 企微用户Controller
  *
@@ -21,7 +29,8 @@ import org.springframework.web.bind.annotation.RestController;
 public class QwUserController extends BaseController {
     @Autowired
     private IQwUserService qwUserService;
-
+    @Autowired
+    private IQwExternalContactTransferCompanyAuditService auditService;
     @GetMapping("/getQwUserAll")
     public AjaxResult getQwUserAll(){
         return AjaxResult.success(qwUserService.getQwUserAll());
@@ -34,4 +43,11 @@ 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);
+    }
 }

+ 27 - 0
fs-admin/src/main/java/com/fs/task/SgTestController.java

@@ -0,0 +1,27 @@
+//package com.fs.task;
+//
+//import org.springframework.web.bind.annotation.RequestMapping;
+//import org.springframework.web.bind.annotation.RestController;
+//
+//import javax.annotation.Resource;
+//
+///**
+// * @description:
+// * @author: Guos
+// * @time: 2025/10/23 下午2:18
+// */
+//@RestController
+//@RequestMapping("/sg/test")
+//public class SgTestController {
+//
+//    @Resource
+//    private SyncTuLinStudentInfoTask syncTuLinStudentInfoTask;
+//
+//
+//    @RequestMapping("/execute")
+//    public void execute(){
+//        syncTuLinStudentInfoTask.execute();
+//    }
+//
+//
+//}

+ 25 - 0
fs-admin/src/main/java/com/fs/task/SyncTuLinStudentInfoTask.java

@@ -0,0 +1,25 @@
+package com.fs.task;
+
+import com.fs.tulin.service.ITulinInfoSyncLogService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+import javax.annotation.Resource;
+
+/**
+ * @description: 同步TuLin学生信息定时任务
+ * @author: Guos
+ * @time: 2025/10/23 上午10:26
+ */
+@AllArgsConstructor
+@Component("syncTuLinStudentInfoTask")
+public class SyncTuLinStudentInfoTask {
+
+
+    @Resource
+    private final ITulinInfoSyncLogService tulinInfoSyncLogService;
+
+    public void execute() {
+        tulinInfoSyncLogService.syncInfo();
+    }
+
+}

+ 79 - 4
fs-admin/src/main/java/com/fs/web/controller/common/CommonController.java

@@ -3,16 +3,15 @@ package com.fs.web.controller.common;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.file.OssException;
-import com.fs.course.service.IHuaweiObsService;
-import com.fs.course.service.IHuaweiVodService;
+import com.fs.course.dto.BatchSendCourseAllDTO;;
 import com.fs.course.service.ITencentCloudCosService;
-import com.fs.course.service.impl.HuaweiObsServiceImpl;
 import com.fs.framework.config.ServerConfig;
 import com.fs.his.domain.FsExportTask;
 import com.fs.his.service.IFsExportTaskService;
+import com.fs.im.service.OpenIMService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 
@@ -22,6 +21,7 @@ import com.huaweicloud.sdk.vod.v1.model.BaseInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -36,6 +36,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 import static com.fs.course.service.impl.HuaweiObsServiceImpl.fileUrlMap;
 import static com.fs.course.service.impl.HuaweiObsServiceImpl.uploadProgress;
@@ -60,6 +61,17 @@ public class CommonController
     private ITencentCloudCosService tencentCloudCosService;
     @Autowired
     private IFsExportTaskService exportTaskService;
+
+    @Autowired
+    private OpenIMService openIMService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
     @GetMapping(value = "common/getTask/{taskId}")
     public R getTask(@PathVariable("taskId") Long taskId)
     {
@@ -227,7 +239,70 @@ public class CommonController
         return tencentCloudCosService.getKeyAndCredentials();
     }
 
+    /**
+     * 测试接口
+     */
+    @PostMapping("/common/im/testTask/course")
+    public void testIMCourseTask(){
+        String redisKey = "openIm:batchSendMsg:sendCourse";
+        Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+        if(cacheMap == null || cacheMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在对应的redisKey==================");
+            return;
+        }
+        List<Map.Entry<String, BatchSendCourseAllDTO>> toSendMap = cacheMap.entrySet().parallelStream().filter((v) -> {
+            String[] split = v.getKey().split(":");
+            long timestamp = Long.parseLong(split[2]);
+            return timestamp < System.currentTimeMillis();
+        }).collect(Collectors.toList());
 
+        if(toSendMap.isEmpty()){
+            logger.info("=====================会员IM定时发课,不存在可执行的发课任务==================");
+            return;
+        }
+        for (Map.Entry<String, BatchSendCourseAllDTO> entry : toSendMap) {
+            //执行发送消息任务
+            BatchSendCourseAllDTO batchSendCourseAllDTO = entry.getValue();
+            openIMService.batchSendCourseTask(batchSendCourseAllDTO.getBatchSendCourseDTO(), batchSendCourseAllDTO.getOpenImBatchMsgDTO(), batchSendCourseAllDTO.getProject(), batchSendCourseAllDTO.getImMsgSendDetailList());
+
+            // 执行结束,删除
+            this.redisTemplate.<String, BatchSendCourseAllDTO>opsForHash().delete(redisKey, entry.getKey());
 
+        }
+
+    }
+
+    /**
+     * 测试接口
+     */
+    @PostMapping("/common/im/testTask/urge")
+    public void testIMUrgeTask(){
+        String redisKey = "openIm:batchSendMsg:urgeCourse";
+        Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+        if(cacheMap == null || cacheMap.isEmpty()){
+            logger.info("===================== 会员-IM发消息催课,不存在对应的redisKey==================");
+            return;
+        }
+        List<Map.Entry<String, BatchSendCourseAllDTO>> toSendMap = cacheMap.entrySet().parallelStream().filter((v) -> {
+            String[] split = v.getKey().split(":");
+            long timestamp = Long.parseLong(split[2]);
+            return timestamp < System.currentTimeMillis();
+        }).collect(Collectors.toList());
+
+        if(toSendMap.isEmpty()){
+            logger.info("===================== 会员-IM发消息催课,不存在可发送的消息==================");
+            return;
+        }
+        for (Map.Entry<String, BatchSendCourseAllDTO> entry : toSendMap) {
+            //执行发送消息任务
+            BatchSendCourseAllDTO batchSendCourseAllDTO = entry.getValue();
+            openIMService.batchUrgeCourseTask(batchSendCourseAllDTO.getOpenImBatchMsgDTO(), batchSendCourseAllDTO.getImMsgSendDetailList());
+
+            // 执行结束,删除
+            this.redisTemplate.<String, BatchSendCourseAllDTO>opsForHash().delete(redisKey, entry.getKey());
+
+        }
+
+    }
 
 }

+ 26 - 7
fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java

@@ -88,20 +88,39 @@ public class SysLoginController
         AjaxResult ajax = AjaxResult.success();
 
         ajax.put("user", user);
-        Integer isAdmin = 0;
+        SysRole isAdmin = new SysRole();
+        getIsAdmin(isAdmin, permissions, user);
+        ajax.put("isAdmin", isAdmin);
+        ajax.put("roles", roles);
+        ajax.put("permissions", permissions);
+
+        return ajax;
+    }
+
+    /**
+     * 获取电话地址权限
+     * @param isAdmin
+     * @param permissions
+     * @param user
+     */
+    private void getIsAdmin(SysRole isAdmin, Set<String> permissions, SysUser user) {
+        isAdmin.setIsCheckAddress(0);
+        isAdmin.setIsCheckPhone(0);
         if (permissions.contains("*:*:*")){
-            isAdmin = 1;
+            isAdmin.setIsCheckAddress(1);
+            isAdmin.setIsCheckPhone(1);
         }else {
             List<SysRole> roleList = user.getRoles();
             if (roleList != null && !roleList.isEmpty()) {
                 Long[] roleIds = roleList.stream().map(SysRole::getRoleId).toArray(Long[]::new);
-                isAdmin = roleService.getIsCheckPhone(roleIds)?1:0;
+//                isAdmin = roleService.getIsCheckPhone(roleIds)?1:0;
+                SysRole isCheckPermission = roleService.getIsCheckPermission(roleIds);
+                if (isCheckPermission != null) {
+                    isAdmin.setIsCheckPhone(isCheckPermission.getIsCheckPhone());
+                    isAdmin.setIsCheckAddress(isCheckPermission.getIsCheckAddress());
+                }
             }
         }
-        ajax.put("roles", roles);
-        ajax.put("permissions", permissions);
-        ajax.put("isAdmin", isAdmin);
-        return ajax;
     }
 
     /**

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

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

+ 2 - 2
fs-common-api/src/main/resources/application.yml

@@ -4,5 +4,5 @@ server:
 # Spring配置
 spring:
   profiles:
-    active: dev-yjb
-#    active: druid-jnmy
+#    active: dev
+    active: druid-myhk-test

+ 5 - 0
fs-common/src/main/java/com/fs/common/constant/FsConstants.java

@@ -11,4 +11,9 @@ public interface FsConstants {
 
     String FRIEND_WELCOME_VIDEO_KEY = "friend:welcome:";
     String REDIS_INTEGRAL_ORDER_UNPAY = "integral:order:unpay:";
+
+    // 公司余额redis key "company:money:" + company.getCompanyId()
+    String COMPANY_MONEY_KEY = "company:money:";
+    // 公司余额redis 锁
+    String COMPANY_MONEY_LOCK = "company_money_lock:";
 }

+ 7 - 0
fs-common/src/main/java/com/fs/common/enums/ImTypeEnum.java

@@ -0,0 +1,7 @@
+package com.fs.common.enums;
+
+public enum ImTypeEnum {
+    NONE,
+    TENCENT,
+    OPENIM;
+}

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

@@ -4,15 +4,24 @@ 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;
 
 
@@ -67,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;
+    }
 }

+ 222 - 6
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,4 +420,194 @@ public class CompanyUserController extends AppBaseController {
                 .collect(Collectors.toList());
         return R.ok().put("data",filteredDictVOS);
     }
+
+    @ApiOperation("查询所有项目")
+    @GetMapping("/getDictProject")
+    public R getDictProject(){
+        List<DictVO> dictVOS = dictDataService.selectDictDataListByType("sys_course_project");
+        return R.ok().put("data",dictVOS);
+    }
+
+    /**
+     * 当只有模板文字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;
+    }
 }

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

@@ -90,6 +90,7 @@ public class FsUserController extends AppBaseController {
         log.debug("用户会员分页列表 param: {}", JSON.toJSONString(param));
         param.setUserId(Long.parseLong(getUserId()));
 //        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setIsHidePhoneMiddle(false);
         PageInfo<FsUserPageListVO> fsUserPageListVOPageInfo = fsUserService.selectFsUserPageList(param);
 //        PageInfo<FsUserPageListVO> pageInfo = new PageInfo<>(list);
         return ResponseResult.ok(fsUserPageListVOPageInfo);

+ 107 - 12
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -1,27 +1,36 @@
 package com.fs.app.controller;
 
+import cn.hutool.core.date.DateUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.Login;
 import com.fs.app.config.ImageStorageConfig;
+import com.fs.common.annotation.Log;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsUserCoursePeriod;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.dto.BatchUrgeCourseDTO;
 import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.param.FsWatchCourseTimeParam;
 import com.fs.course.param.newfs.FsCourseSortLinkParam;
+import com.fs.course.param.newfs.FsCourseWatchAppParam;
 import com.fs.course.param.newfs.FsUserCourseListParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
-import com.fs.course.service.IFsCourseLinkService;
-import com.fs.course.service.IFsUserCoursePeriodService;
-import com.fs.course.service.IFsUserCourseService;
-import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.service.*;
+import com.fs.course.vo.FsCourseWatchLogListVO;
 import com.fs.course.vo.FsUserCourseParticipationRecordVO;
-import com.fs.course.vo.newfs.FsUserCourseListVO;
-import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
-import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
-import com.fs.course.vo.newfs.FsUserVideoListVO;
+import com.fs.course.vo.newfs.*;
+import com.fs.im.domain.FsImMsgSendLog;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.IFsImMsgSendDetailService;
+import com.fs.im.service.IFsImMsgSendLogService;
+import com.fs.im.service.OpenIMService;
+import com.fs.im.vo.FsImMsgSendLogVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -32,10 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.io.InputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
 
@@ -62,6 +68,15 @@ public class FsUserCourseVideoController extends AppBaseController {
     @Autowired
     private ICompanyUserService companyUserService;
 
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+
+    @Autowired
+    private OpenIMService openIMService;
+
+    @Autowired
+    private IFsImMsgSendLogService imMsgSendLogService;
+
     @Login
     @GetMapping("/pageList")
     @ApiOperation("课程分页列表")
@@ -253,4 +268,84 @@ public class FsUserCourseVideoController extends AppBaseController {
         return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
     }
 
+    @ApiOperation("会员批量发送课程消息")
+    @PostMapping("/batchSendCourse")
+    public OpenImResponseDTO batchSendCourse(@RequestBody BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {
+        // 生成看课短链
+        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+        BeanUtils.copyProperties(batchSendCourseDTO, fsCourseLinkCreateParam);
+        R courseSortLink = fsUserCourseService.createAppCourseSortLink(fsCourseLinkCreateParam);
+        String url = courseSortLink.get("url").toString();
+        batchSendCourseDTO.setUrl(url);
+
+        return openIMService.batchSendCourse(batchSendCourseDTO);
+    }
+
+    @ApiOperation("会员一键催课")
+    @PostMapping("/batchUrgeCourse")
+    public OpenImResponseDTO batchUrgeCourse(@RequestBody BatchUrgeCourseDTO batchUrgeCourseDTO) throws JsonProcessingException {
+        // 查询生成短链需要的内容
+        Map<String, Object> params = new HashMap<>();
+        params.put("logDetailIds", batchUrgeCourseDTO.getImMsgSendDetailId());
+        List<FsImMsgSendLog> fsImMsgSendLogs = imMsgSendLogService.selectSendLogListByDetailId(params);
+        OpenImResponseDTO openImResponseDTO = null;
+        for (FsImMsgSendLog fsImMsgSendLog : fsImMsgSendLogs) {
+            FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+            BeanUtils.copyProperties(fsImMsgSendLog, fsCourseLinkCreateParam);
+            fsCourseLinkCreateParam.setId(fsImMsgSendLog.getPeriodDaysId());
+            R courseSortLink = fsUserCourseService.createAppCourseSortLink(fsCourseLinkCreateParam);
+            String url = courseSortLink.get("url").toString();
+            openImResponseDTO = openIMService.batchUrgeCourse(batchUrgeCourseDTO, fsImMsgSendLog, url);
+        }
+        return openImResponseDTO;
+    }
+
+    @Login
+    @ApiOperation("app-看课记录(包含今日完课、今日催课)")
+    @GetMapping("/courseWatchLog")
+    public ResponseResult<PageInfo<FsCourseWatchLogListVO>> getCourseWatchLog(FsCourseWatchAppParam fsCourseWatchAppParam)
+    {
+        FsCourseWatchLogListParam param = new FsCourseWatchLogListParam();
+        BeanUtils.copyProperties(fsCourseWatchAppParam, param);
+        param.setCompanyUserId(Long.parseLong(getUserId()));
+        param.setCompanyId(getCompanyId());
+        param.setSTime(DateUtil.formatDate(DateUtil.beginOfDay(new Date())));
+        param.setETime(DateUtil.formatDate(DateUtil.endOfDay(new Date())));
+//        startPage();
+        PageHelper.startPage(fsCourseWatchAppParam.getPageNum (), fsCourseWatchAppParam.getPageSize());
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        PageInfo<FsCourseWatchLogListVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @Login
+    @ApiOperation("任务列表")
+    @GetMapping("/im/sendLog")
+    public ResponseResult<PageInfo<FsImSendLogVO>> imSendLog(@RequestParam(defaultValue = "1") Integer pageNum,
+                                                             @RequestParam(defaultValue = "10") Integer pageSize) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("companyId", getCompanyId());
+        params.put("companyUserId", Long.parseLong(getUserId()));
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<FsImSendLogVO> list = imMsgSendLogService.selectFsImSendLogList(params);
+        PageInfo<FsImSendLogVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+    @ApiOperation("任务详情")
+    @GetMapping("/im/sendLog/detail")
+    public ResponseResult<FsImMsgSendLogVO> imSendLogDetail(@RequestParam Long logId) {
+        FsImMsgSendLogVO fsImMsgSendLogVO = imMsgSendLogService.selectFsImMsgSendLogDetail(logId);
+        return ResponseResult.ok(fsImMsgSendLogVO);
+    }
+
+    @Login
+    @ApiOperation("删除任务")
+    @DeleteMapping("/im/sendLog")
+    @Log(title = "任务-删除任务", businessType = BusinessType.DELETE)
+    public ResponseResult<Boolean> deleteImSendLog(@RequestParam Long logId) {
+        return imMsgSendLogService.deleteFsImMsgSendLogAndDetail(logId);
+    }
+
 }

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

+ 4 - 0
fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java

@@ -40,6 +40,10 @@ public class RedisConfig extends CachingConfigurerSupport
         template.setValueSerializer(serializer);
         // 使用StringRedisSerializer来序列化和反序列化redis的key值
         template.setKeySerializer(new StringRedisSerializer());
+
+        // Hash的key也采用StringRedisSerializer的序列化方式 这个才是redis的hash值的序列化方式 一直都没有序列化进去
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(serializer);
         template.afterPropertiesSet();
 
         // Hash的key也采用StringRedisSerializer的序列化方式 这个才是redis的hash值的序列化方式 一直都没有序列化进去

+ 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

+ 39 - 0
fs-company/src/main/java/com/fs/company/controller/common/CommonController.java

@@ -1,22 +1,30 @@
 package com.fs.company.controller.common;
 
+import cn.hutool.json.JSONUtil;
 import com.fs.common.config.FSConfig;
 import com.fs.common.constant.Constants;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.file.OssException;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.file.FileUploadUtils;
 import com.fs.common.utils.file.FileUtils;
 import com.fs.company.utils.AudioUtils;
 import com.fs.company.vo.WangUploadVO;
 import com.fs.framework.config.ServerConfig;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
 import com.fs.his.domain.FsExportTask;
+import com.fs.his.dto.InquiryConfigDTO;
 import com.fs.his.service.IFsExportTaskService;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
 import com.fs.qw.service.IQwWorkTaskService;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
+import com.fs.system.service.ISysConfigService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -32,6 +40,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 通用请求处理
@@ -53,7 +62,14 @@ public class CommonController
     private QwApiService qwApiService;
 
 
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private OpenIMService openIMService;
 
+    @Autowired
+    private TokenService tokenService;
 //    @Autowired
 //    private TestTwoService testService2;
 ////
@@ -276,6 +292,29 @@ public class CommonController
         }
     }
 
+    @GetMapping(value = "/common/getInquiryConfig")
+    public AjaxResult getInquiryConfig()
+    {
+        String json=configService.selectConfigByKey("his.inquiryConfig");
+        InquiryConfigDTO configDTO= JSONUtil.toBean(json, InquiryConfigDTO.class);
+        String inquirySubType = configDTO.getInquirySubType();
+        return AjaxResult.success(inquirySubType);
+    }
+
+    //分享会诊
+    @PostMapping(value = "/common/sendInquiry")
+    public AjaxResult sendInquiry(@RequestBody Map<String,String> map){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyUserId = loginUser.getUser().getUserId();
+        Long companyId = loginUser.getCompany().getCompanyId();
+        String inquiryName = map.get("inquiryName");
+        String type = map.get("type");
+        String sendID = map.get("sendID");
+        String recvID = map.get("recvID");
+        String doctorId = map.get("doctorId");
+        OpenImResponseDTO inquirySelect = openIMService.sendInquiryUtil(sendID, recvID, 110, "inquirySelect", inquiryName, type,companyId,companyUserId,doctorId);
+        return AjaxResult.success(inquirySelect);
+    }
 
 
 }

+ 222 - 9
fs-company/src/main/java/com/fs/company/controller/common/Test.java

@@ -4,19 +4,56 @@ import com.alibaba.fastjson.JSON;
 import com.fs.ad.enums.AdUploadType;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.DataSourceType;
+import com.fs.common.service.impl.SmsServiceImpl;
+import com.fs.company.mapper.*;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.service.ITencentCloudCosService;
+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.service.IErpOrderService;
+import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
+import com.fs.fastGpt.service.IFastgptEventLogTotalService;
+import com.fs.his.config.FsSysConfig;
+import com.fs.his.mapper.*;
+import com.fs.his.service.*;
+import com.fs.his.service.impl.FsPackageOrderServiceImpl;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.qrcode.QRCodeUtils;
+import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
+import com.fs.qw.service.IQwAppContactWayService;
+import com.fs.qw.service.IQwCompanyService;
+import com.fs.qw.service.IQwExternalContactTransferLogService;
+import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.AdUploadVo;
+import com.fs.qwApi.service.QwApiService;
 import com.fs.sop.service.IQwSopTempContentService;
 import com.fs.sop.service.IQwSopTempDayService;
 import com.fs.sop.service.IQwSopTempRulesService;
 import com.fs.sop.service.IQwSopTempService;
+import com.fs.system.mapper.SysConfigMapper;
 import com.google.zxing.WriterException;
 import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
@@ -24,18 +61,194 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 @RestController
-@AllArgsConstructor
 public class Test {
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
+    @Autowired
+    private ICompanyService companyService;
+    @Autowired
+    @Qualifier("erpOrderServiceImpl")
+    private IErpOrderService gyOrderService;
+    @Autowired
+    private FsUserCouponMapper fsUserCouponMapper;
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+    @Autowired
+    private IFsPrescribeService fsPrescribeService;
+    @Autowired
+    IFsStoreOrderService fsStoreOrderService;
+    @Autowired
+    FsStoreOrderMapper fsStoreOrderMapper;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private FsPackageOrderMapper fsPackageOrderMapper;
+    @Autowired
+    private IFsFollowService fsFollowService;
+    @Autowired
+    private IFsStoreAfterSalesService fsStoreAfterSalesService;
+    //    @Autowired
+//    IErpOrderService erpOrderService;
+    @Autowired
+    FsIntegralOrderMapper integralOrderMapper;
+    @Autowired
+    IFsInquiryOrderService iFsInquiryOrderService;
+    @Autowired
+    FsPackageMapper fsPackageMapper;
+    @Autowired
+    FsStoreProductMapper fsStoreProductMapper;
+    @Autowired
+    CompanyMapper companyMapper;
+    @Autowired
+    private CompanyMoneyLogsMapper moneyLogsMapper;
+    @Autowired
+    FsUserMapper fsUserMapper;
+    @Autowired
+    IFsUserIntegralLogsService fsUserIntegralLogsService;
+    @Autowired
+    SmsServiceImpl smsService;
+    @Autowired
+    FsStoreOrderItemMapper fsStoreOrderItemMapper;
+
+    @Autowired
+    FsPatientMapper fsPatientMapper;
+    @Autowired
+    FsPrescribeDrugMapper fsPrescribeDrugMapper;
+    @Autowired
+    FsFollowMapper fsFollowMapper;
+    @Autowired
+    private IImService imService;
+    @Autowired
+    private FsInquiryOrderMapper inquiryOrderMapper;
+    @Autowired
+    private CompanyVoiceCallerMapper companyVoiceCallerMapper;
+    @Autowired
+    private CompanyVoiceLogsMapper companyVoiceLogsMapper;
+    @Autowired
+    FsPackageOrderServiceImpl packageOrderService;
+    @Autowired
+    private IFsStoreOrderLogsService fsStoreOrderLogsService;
+    org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
+    @Autowired
+    IFsDoctorService doctorService;
+    @Autowired
+    CompanyUserMapper companyUserMapper;
+    @Autowired
+    FsInquiryOrderMapper fsInquiryOrderMapper;
+    @Autowired
+    QwApiService qwApiService;
+    //每天执行一次
+    @Autowired
+    IQwAppContactWayService qwAppContactWayService;
+
+    @Autowired
+    FastGptChatSessionMapper fastGptChatSessionMapper;
+
+    @Autowired
+    IQwExternalContactTransferLogService qwExternalContactTransferLogService;
+
+
+
+    @Autowired
+    ITencentCloudCosService tencentCloudCosService;
+
 
-    private final IQwSopTempService qwSopTempService;
-    private final IQwSopTempRulesService qwSopTempRulesService;
-    private final IQwSopTempDayService qwSopTempDayService;
-    private final IQwSopTempContentService qwSopTempContentService;
-    private final RocketMQTemplate rocketMQTemplate;
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    @Autowired
+    @Qualifier("wdtErpOrderServiceImpl")
+    private IErpOrderService wdtOrderService;
+    @Autowired
+    @Qualifier("hzOMSErpOrderServiceImpl")
+    private IErpOrderService hzOMSErpOrderService;
+    @Autowired
+    @Qualifier("dfOrderServiceImpl")
+    private IErpOrderService dfOrderService;
+
+    @Autowired
+    @Qualifier("JSTErpOrderServiceImpl")
+    private IErpOrderService jSTOrderService;
+    @Autowired
+    @Qualifier("k9OrderScrmServiceImpl")
+    private IErpOrderService k9OrderService;
+    @Autowired
+    IErpOrderService erpOrderService;
     @GetMapping("test")
-    @DataSource(DataSourceType.SOP)
-    public void fileDownload(){
-        rocketMQTemplate.syncSend("ad-upload", JSON.toJSONString(AdUploadVo.builder().state("测试").type(AdUploadType.ADD_WX).build()));
+    public void fileDownload() {
+        deliveryOpScrm();
+    }
+    private IErpOrderService getErpService() {
+
+        FsSysConfig sysConfig = configUtil.getSysConfig();
+        Integer erpOpen = sysConfig.getErpOpen();
+        if (erpOpen != null && erpOpen == 1){
+            //判断erp类型
+            Integer erpType = sysConfig.getErpType();
+            if (erpType != null){
+                IErpOrderService erpOrderService = null;
+                if (erpType == 1){
+                    //管易
+                    erpOrderService =  gyOrderService;
+                } else if (erpType == 2){
+                    //旺店通
+                    erpOrderService =  wdtOrderService;
+                } else if (erpType == 3){
+                    //
+                    erpOrderService =  hzOMSErpOrderService;
+                } else if (erpType == 4){
+                    //代服
+                    erpOrderService =  dfOrderService;
+                }else if(erpType == 5){
+                    erpOrderService=jSTOrderService;
+                }else if(erpType == 6){
+                    erpOrderService=k9OrderService;
+                }
+                return erpOrderService;
+
+
+            }
+        }
+        return null;
+    }
+    public void deliveryOpScrm()
+    {
+        System.out.println("deliveryOpScrm");
+        IErpOrderService erpOrderService = getErpService();
+        List<FsStoreOrderScrm> orders = null;
+        if (erpOrderService == gyOrderService){
+            orders = fsStoreOrderMapper.selectOmsOrderdeliveryOpScrm();
+        } else if (erpOrderService == wdtOrderService || erpOrderService == dfOrderService || erpOrderService == jSTOrderService){
+            orders = fsStoreOrderMapper.selectWdtOmsOrderdeliveryOpScrm();
+        }
+        logger.info("查询到的"+orders.size());
+        for(FsStoreOrderScrm order:orders){
+            ErpOrderQueryRequert request=new ErpOrderQueryRequert();
+            request.setCode(order.getExtendOrderId());
+            if (erpOrderService != null){
+                ErpOrderQueryResponse response=erpOrderService.getOrder(request);
+                if (erpOrderService != dfOrderService){
+                    if(response.getOrders()!=null&&response.getOrders().size()>0){
+                        for(ErpOrderQuery orderQuery : response.getOrders()){
+                            if(orderQuery.getDeliverys()!=null&&orderQuery.getDeliverys().size()>0){
+                                for(ErpDeliverys delivery:orderQuery.getDeliverys()){
+                                    if(delivery.getDelivery()&& StringUtils.isNotEmpty(delivery.getMail_no())){
+                                        //更新商订单状态 删除REDIS
+                                        logger.info("查询到的"+delivery);
+
+                                        fsStoreOrderService.deliveryOrderScrm(order.getOrderCode(),delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
+                                        redisCache.deleteObject("delivery"+":"+order.getExtendOrderId());
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
     }
 
 }

+ 6 - 4
fs-company/src/main/java/com/fs/company/controller/company/CompanyController.java

@@ -17,10 +17,7 @@ import com.fs.framework.service.TokenService;
 import com.fs.his.vo.OptionsVO;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
@@ -119,4 +116,9 @@ public class CompanyController extends BaseController
         return R.ok();
     }
 
+    @GetMapping("/getCompanyListByCorId/{corpId}")
+    public R getCompanyListByCorId(@PathVariable String corpId) {
+        List<OptionsVO> list = companyService.getCompanyListByCorpId(corpId);
+        return R.ok().put("data",list);
+    }
 }

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

@@ -163,21 +163,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());

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

@@ -6,6 +6,7 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.enums.ImTypeEnum;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.file.FileUploadUtils;
@@ -15,6 +16,8 @@ import com.fs.company.service.ICompanyUserService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
+import com.fs.im.config.ImTypeConfig;
+import com.fs.im.service.OpenIMService;
 import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -39,6 +42,8 @@ public class CompanyProfileController extends BaseController
     @Autowired
     private RedisCache redisCache;
 
+    @Autowired
+    private OpenIMService openIMService;
     /**
      * 个人信息
      */
@@ -67,6 +72,10 @@ public class CompanyProfileController extends BaseController
 
         if (userService.updateUserProfile(user) > 0)
         {
+            //修改im用户信息
+            if (ImTypeConfig.IMTYPE== ImTypeEnum.OPENIM){
+                openIMService.updateUserInfo(user);
+            }
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
             // 更新缓存用户信息
             loginUser.getUser().setNickName(user.getNickName());

+ 72 - 21
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -22,6 +22,8 @@ import com.fs.company.param.CompanyUserCodeParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.service.*;
 import com.fs.company.utils.DomainUtil;
+import com.fs.company.utils.QwStatusEnum;
+import com.fs.company.vo.BatchUserRolesVO;
 import com.fs.company.vo.CompanyUserImportVO;
 import com.fs.company.vo.CompanyUserQwListVO;
 import com.fs.company.vo.CompanyUserVO;
@@ -32,9 +34,12 @@ import com.fs.framework.service.TokenService;
 import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.his.vo.OptionsVO;
 import com.fs.hisStore.vo.FsStoreProductExportVO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.dto.OpenImResponseDTO;
 import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.service.IQwCompanyService;
+import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.system.service.ISysConfigService;
@@ -46,6 +51,7 @@ import org.json.JSONArray;
 import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.Assert;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -53,18 +59,16 @@ 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;
 
 
 /**
  * 用户信息
- *
-
  */
 @RestController
 @RequestMapping("/company/user")
-public class CompanyUserController extends BaseController
-{
+public class CompanyUserController extends BaseController {
 
     @Autowired
     private ICompanyRoleService roleService;
@@ -77,19 +81,28 @@ public class CompanyUserController extends BaseController
 
     @Autowired
     private ICompanyUserService companyUserService;
+
     @Autowired
     private ICompanyService companyService;
+
     @Autowired
     private ICompanyUserDelayTimeService companyUserDelayTimeService;
+
     @Autowired
     private ISysConfigService configService;
+
     @Autowired
     private RedisCache redisCache;
+
     @Autowired
     private OpenIMService openIMService;
+
     @Autowired
     IQwCompanyService iQwCompanyService;
 
+    @Autowired
+    private IQwUserService qwUserService;
+
     private static final String appLink = "https://jump.ylrztop.com/jumpapp/pages/index/index?link=";
 
     /**
@@ -123,24 +136,41 @@ public class CompanyUserController extends BaseController
         return getDataTable(list);
     }
     @GetMapping("/qwList")
-    public TableDataInfo qwList(CompanyUserQwParam user)
-    {
+    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 (!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);
     }
+
     @Log(title = "用户管理导出", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('company:user:export')")
     @GetMapping("/export")
@@ -565,6 +595,20 @@ public class CompanyUserController extends BaseController
         return companyUserService.bindDoctor(companyUser);
     }
 
+    /**
+     * 批量修改角色
+     * @param batchUserRolesVO
+     */
+    @PreAuthorize("@ss.hasPermi('company:user:edit')")
+    @Log(title = "批量修改角色", businessType = BusinessType.UPDATE)
+    @PostMapping("/updateBatchUserRoles")
+    public R updateBatchUserRoles(@RequestBody BatchUserRolesVO batchUserRolesVO){
+        Assert.notEmpty(batchUserRolesVO.getRoleIds(), "角色不能为空");
+        Assert.notEmpty(batchUserRolesVO.getUserIds(), "用户不能为空");
+        return companyUserService.updateBatchUserRoles(batchUserRolesVO);
+    }
+
+
     @ApiOperation("校验客服是否注册新的im")
     @PostMapping("/accountCheck")
     public R accountCheck(@RequestBody Map<String, String> userIdMap){
@@ -579,7 +623,7 @@ public class CompanyUserController extends BaseController
             requestBody = new JSONObject();
             userIds.add(userId);
             requestBody.put("checkUserIDs", userIds);
-            String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/account_check")
+            String body = HttpRequest.post(IMConfig.URL+"/user/account_check")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(requestBody.toString())
@@ -600,13 +644,13 @@ public class CompanyUserController extends BaseController
                     ArrayList<Object> users = new ArrayList<>();
                     HashMap<String, String> map = new HashMap<>();
                     map.put("userID",userId);
-                    map.put("nickname",companyUser.getImNickName());
+                    map.put("nickname",companyUser.getNickName());
                     map.put("faceURL",companyUser.getAvatar());
                     users.add(map);
                     requestBody = new JSONObject();
-                    userIds.add(userId);
+                    //userIds.add(userId);
                     requestBody.put("users", users);
-                    HttpRequest.post("https://web.im.cdwjyyh.com/api/user/user_register")
+                    String registerBody = HttpRequest.post(IMConfig.URL+"/user/user_register")
                             .header("operationID", String.valueOf(System.currentTimeMillis()))
                             .header("token", adminToken).body(requestBody.toString()).execute().body();
                 }
@@ -619,7 +663,7 @@ public class CompanyUserController extends BaseController
             requestBody = new JSONObject();
             requestBody.put("platformID",5);
             requestBody.put("userID",userId);
-            String body1 = HttpRequest.post("https://web.im.cdwjyyh.com/api/auth/get_user_token")
+            String body1 = HttpRequest.post(IMConfig.URL+"/auth/get_user_token")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(requestBody.toString()).execute().body();
@@ -631,5 +675,12 @@ public class CompanyUserController extends BaseController
             return R.error("获取管理员token失败");
         }
     }
-
+    @ApiOperation("添加好友")
+    @PostMapping("/importFriend")
+    public R importFriend(@RequestBody HashMap<String,Object> map){
+        String ownerUserID = (String) map.get("ownerUserID");
+        List<String> friendUserIDs = (List<String>)map.get("friendUserIDs");
+        OpenImResponseDTO openImResponseDTO = openIMService.importFriend(ownerUserID, friendUserIDs);
+        return R.ok().put("data",openImResponseDTO);
+    }
 }

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

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

@@ -1,11 +1,13 @@
 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;
 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;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsUserCoursePeriod;
@@ -74,6 +76,9 @@ public class FsUserCoursePeriodController extends BaseController {
     @ApiOperation("自定义查询主列表分页")
     public R pageList(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
     {
+        if (ObjectUtil.isNotEmpty(fsUserCoursePeriod.getTrainingCampId())){
+            throw new CustomException("训练营不能为空!");
+        }
 //        startPage();
         PageHelper.startPage(fsUserCoursePeriod.getPageNum(), fsUserCoursePeriod.getPageSize());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
@@ -120,6 +125,9 @@ public class FsUserCoursePeriodController extends BaseController {
     @PostMapping
     public AjaxResult add(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
     {
+        if (ObjectUtil.isNotEmpty(fsUserCoursePeriod.getTrainingCampId())){
+            throw new CustomException("训练营不能为空!");
+        }
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         fsUserCoursePeriod.setCompanyId(loginUser.getCompany().getCompanyId().toString());
         return toAjax(fsUserCoursePeriodService.insertFsUserCoursePeriod(fsUserCoursePeriod));

+ 106 - 1
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -11,19 +11,24 @@ import com.fs.common.utils.PubFun;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 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;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
+import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
 import com.fs.qw.domain.QwContactWay;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwTag;
+import com.fs.qw.dto.CompanyTransferDTO;
 import com.fs.qw.param.*;
 import com.fs.qw.service.*;
 import com.fs.qw.vo.QwExternalContactVO;
@@ -34,12 +39,14 @@ import com.github.pagehelper.PageHelper;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.CollectionUtils;
 import org.codehaus.jettison.json.JSONException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import javax.validation.Valid;
 import java.io.IOException;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -53,6 +60,7 @@ import static com.fs.his.utils.PhoneUtil.encryptPhone;
  * @author fs
  * @date 2024-06-20
  */
+@Slf4j
 @RestController
 @RequestMapping("/qw/externalContact")
 public class QwExternalContactController extends BaseController
@@ -80,10 +88,15 @@ public class QwExternalContactController extends BaseController
 
     @Autowired
     private CompanyDeptServiceImpl companyDeptService;
+    @Autowired
+    private IQwExternalContactTransferCompanyAuditService auditService;
 
     @Autowired
     private IQwContactWayService qwContactWayService;
 
+    @Autowired
+    private IFsUserCompanyBindService fsUserCompanyBindService;
+
     /**
      * 查询企业微信客户列表
      */
@@ -267,10 +280,34 @@ public class QwExternalContactController extends BaseController
             if (!StringUtil.strIsNullOrEmpty(item.getState()) && !wayList.isEmpty()) {
                 item.setState(item.getState()+"-"+getContactWayNameStream(item.getState(), wayList));
             }
+            //获取用户下单次数
+            try {
+                fillOrderCount(item);
+            }catch (Exception e){
+                log.error("获取用户下单次数异常:{}",e.getMessage());
+            }
+
         });
 
         return getDataTable(list);
     }
+    /**
+     * 根据用户ID查询并设置下单次数(若用户不存在或ID无效,则设为0)
+     */
+    private void fillOrderCount(QwExternalContactVO item){
+        Long fsUserId = item.getFsUserId();
+        if (fsUserId==null){
+            item.setOrderCount(0L);
+            return;
+        }
+        FsUser fsUser = fsUserService.selectFsUserById(fsUserId);
+        if (fsUser == null) {
+            item.setOrderCount(0L);
+            return;
+        }
+        Long orderCount = fsUser.getOrderCount();
+        item.setOrderCount(orderCount != null ? orderCount : 0);
+    }
 
 
     @Log(title = "同步我的企业微信客户", businessType = BusinessType.INSERT)
@@ -289,7 +326,7 @@ public class QwExternalContactController extends BaseController
     /**
      * 导出企业微信客户列表
      */
-    @PreAuthorize("@ss.hasPermi('qw:externalContact:export')")
+    @PreAuthorize("@ss.hasAnyPermi('qw:externalContact:export,qw:externalContact:companyExport')")
     @Log(title = "企业微信客户", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(QwExternalContactParam qwExternalContact)
@@ -552,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();
+    }
     /**
      * 删除企业微信客户
      */
@@ -728,4 +773,64 @@ public class QwExternalContactController extends BaseController
         return util.exportExcel(qwUserDelLossLogVOS, "企微用户删除流失统计");
     }
 
+    /**
+     * 公司客户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:companyExtList')")
+    @GetMapping("/companyExtList")
+    public TableDataInfo companyExtList(QwExternalContactParam qwExternalContact)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        qwExternalContact.setCompanyId(loginUser.getCompany().getCompanyId());
+
+        startPage();
+        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
+        list.forEach(item->{
+
+            if (!Objects.equals(item.getTagIds(), "[]") && item.getTagIds()!=null) {
+                QwTagSearchParam param = new QwTagSearchParam();
+                Gson gson = new Gson();
+                List<String> tagIds = gson.fromJson(
+                        item.getTagIds(),
+                        new TypeToken<List<String>>() {
+                        }.getType()
+                );
+
+                param.setTagIds(tagIds);
+
+                item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+            }
+        });
+
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:companyTransfer')")
+    @PostMapping("/companyTransfer")
+    public R companyTransfer(@Valid @RequestBody CompanyTransferDTO param) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        CompanyUser user = loginUser.getUser();
+        auditService.addAudit(param, user.getCompanyId(), user.getUserName());
+        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);
+    }
+
 }

+ 59 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferCompanyAuditController.java

@@ -0,0 +1,59 @@
+package com.fs.company.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.utils.ServletUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwExternalContactTransferCompanyAudit;
+import com.fs.qw.domain.QwExternalContactTransferCompanyAuditUser;
+import com.fs.qw.service.IQwExternalContactTransferCompanyAuditService;
+import com.fs.qw.service.IQwExternalContactTransferCompanyAuditUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/qw/externalContactTransferCompanyAudit")
+public class QwExternalContactTransferCompanyAuditController extends BaseController {
+
+    @Autowired
+    private IQwExternalContactTransferCompanyAuditService auditService;
+    @Autowired
+    private IQwExternalContactTransferCompanyAuditUserService auditUserService;
+    @Autowired
+    private TokenService tokenService;
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContactTransferCompanyAudit:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwExternalContactTransferCompanyAudit param) {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        param.setOperCompanyId(companyId);
+        List<QwExternalContactTransferCompanyAudit> list = auditService.selectQwExternalContactTransferCompanyAuditList(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContactTransferCompanyAudit:sync')")
+    @Log(title = "公司转接同步", businessType = BusinessType.INSERT)
+    @PostMapping("/sync/{auditId}")
+    public R sync(@PathVariable("auditId") Long auditId) {
+        auditService.syncTransfer(auditId);
+        return R.ok();
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContactTransferCompanyAudit:detail')")
+    @GetMapping("/detail/{auditId}")
+    public TableDataInfo detail(@PathVariable Long auditId) {
+        startPage();
+        List<QwExternalContactTransferCompanyAuditUser> list = auditUserService.getListByAuditId(auditId);
+        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();
     }
     /**
      * 获取转接记录详细信息

+ 86 - 4
fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java

@@ -1,5 +1,6 @@
 package com.fs.company.controller.qw;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
@@ -17,21 +18,29 @@ import com.fs.framework.service.TokenService;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.domain.QwSopUpdateStatus;
 import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.QwUserVO;
 import com.fs.sop.domain.QwSop;
+import com.fs.sop.domain.QwSopTempContent;
+import com.fs.sop.domain.QwSopTempVoice;
 import com.fs.sop.params.QwSopAutoTime;
 import com.fs.sop.params.QwSopEditQwUserParam;
 import com.fs.sop.service.ICompanySopRoleService;
 import com.fs.sop.service.IQwSopService;
+import com.fs.sop.service.IQwSopTempContentService;
+import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.sop.vo.SopVoiceListVo;
+import org.apache.commons.beanutils.ConvertUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 企微sopController
@@ -64,6 +73,13 @@ public class QwSopController extends BaseController
     @Autowired
     private IQwUserService iQwUserService;
 
+
+    @Autowired
+    private IQwSopTempContentService qwSopTempContentService;
+
+    @Autowired
+    private IQwSopTempVoiceService voiceService;
+
     /**
      * 查询企微sop列表
      */
@@ -228,8 +244,13 @@ public class QwSopController extends BaseController
         qwSop.setCompanyId(companyId);
         qwSop.setCreateBy(loginUser.getUser().getNickName());
         qwSop.setCreateTime(sdf.format(new Date()));
-
-        return toAjax(qwSopService.insertQwSop(qwSop));
+        int count = qwSopService.insertQwSop(qwSop);
+        if(count > 0){
+            if(qwSop.getQwUserIds() != null){
+                updateTempVoiceInfo(qwSop);
+            }
+        }
+        return toAjax(count);
 
     }
 
@@ -241,7 +262,14 @@ public class QwSopController extends BaseController
     @PutMapping
     public R edit(@RequestBody QwSop qwSop)
     {
-        return qwSopService.updateQwSop(qwSop);
+        R sop = qwSopService.updateQwSop(qwSop);
+        String code = sop.get("code").toString();
+        if(code.equals("200")){
+            if(qwSop != null && qwSop.getQwUserIds() != null){
+                updateTempVoiceInfo(qwSop);
+            }
+        }
+        return sop;
     }
 
 
@@ -267,6 +295,52 @@ public class QwSopController extends BaseController
         return toAjax(qwSopService.updateAutoSopTime(param));
     }
 
+    /**
+     * 修改qwSop任务,新增或者删除qwUser
+     * @param qwSop
+     */
+    private void updateTempVoiceInfo(QwSop qwSop) {
+        try {
+            String tempId = qwSop.getTempId();
+            String[] split = qwSop.getQwUserIds().split(",");
+            Long[] qwUserIds = (Long[]) ConvertUtils.convert(split, Long.class);
+
+            List<QwSopTempContent> qwSopTempContentList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+            if(qwSopTempContentList != null && !qwSopTempContentList.isEmpty()){
+                for (QwSopTempContent qwSopTemp : qwSopTempContentList) {
+                    if(qwSopTemp != null && qwSopTemp.getContent() != null){
+                        String content = qwSopTemp.getContent();
+                        JSONObject jsonObject = JSONObject.parseObject(content);
+                        String voiceTxt = jsonObject.getString("value");
+                        String contentType = jsonObject.getString("contentType");
+                        if("7".equals(contentType)){
+                            List<QwUserVO> qwUserVoS = qwUserService.selectQwUserVOByIds(qwUserIds);
+                            if(qwUserVoS != null && !qwUserVoS.isEmpty()){
+                                List<Long> companyUserIdList = qwUserVoS.stream().map(QwUserVO::getCompanyUserId).collect(Collectors.toList());
+                                for (Long companyUserId : companyUserIdList) {
+                                    QwSopTempVoice qwSopTempVoice = voiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,voiceTxt);
+                                    if(qwSopTempVoice == null){
+                                        if(companyUserId != null && voiceTxt != null){
+                                            QwSopTempVoice tempVoice = new QwSopTempVoice();
+                                            tempVoice.setVoiceTxt(voiceTxt);
+                                            tempVoice.setCompanyUserId(companyUserId);
+                                            tempVoice.setRecordType(0);
+                                            tempVoice.setCreateTime(LocalDateTime.now());
+                                            tempVoice.setTempId(tempId);
+                                            voiceService.insertQwSopTempVoice(tempVoice);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error("修改企微sop任务,新增或者删除qwUser异常:" + e.getMessage());
+        }
+    }
+
     /**
      * 删除企微sop
      */
@@ -307,7 +381,15 @@ public class QwSopController extends BaseController
     @PreAuthorize("@ss.hasPermi('qw:sop:updateSopQwUser')")
     public R updateSopQwUser(@RequestBody QwSopEditQwUserParam param)
     {
-        return qwSopService.updateSopQwUser(param);
+        R sop = qwSopService.updateSopQwUser(param);
+        String code = sop.get("code").toString();
+        if(code.equals("200")){
+            QwSop qwSop = qwSopService.selectQwSopById(param.getId());
+            if(qwSop != null && qwSop.getQwUserIds() != null){
+                updateTempVoiceInfo(qwSop);
+            }
+        }
+        return sop;
     }
 
     /**

+ 10 - 1
fs-company/src/main/java/com/fs/company/controller/qw/QwSopLogsController.java

@@ -1,10 +1,12 @@
 package com.fs.company.controller.qw;
 
+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;
 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.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
@@ -60,7 +62,8 @@ public class QwSopLogsController extends BaseController
 
     @Autowired
     private ICompanyUserService iCompanyUserService;
-
+    @Autowired
+    private RedisCache redisCache;
     @Autowired
     private CompanyDeptServiceImpl companyDeptService;
 
@@ -266,6 +269,12 @@ public class QwSopLogsController extends BaseController
     @PutMapping("/editCourseQwSopLogs/{ids}")
     public AjaxResult editCourseQwSopLogs(@PathVariable String[] ids)
     {
+       if (ObjectUtil.isNotEmpty(ids)){
+           for (String id : ids) {
+               String key = "qw:logs:pad:send:id:" + id;
+               redisCache.deleteObject(key);
+           }
+       }
         return toAjax(iQwSopLogsService.editCourseQwSopLogs(ids));
     }
 

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

@@ -1,5 +1,6 @@
 package com.fs.company.controller.qw;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
@@ -392,6 +393,12 @@ public class QwUserController extends BaseController
         startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+        if (ObjectUtil.isNotEmpty(qwUser.getIsRemark())&&qwUser.getIsRemark().equals("1")){
+            qwUser.setCompanyUserId(loginUser.getUser().getUserId());
+        }else if (ObjectUtil.isNotEmpty(qwUser.getIsRemark())&&qwUser.getIsRemark().equals("2")){
+            qwUser.setDeptId(loginUser.getUser().getDeptId());
+            qwUser.setCorpId(null);
+        }
 
         List<QwUserVO> list = qwUserService.selectQwUserListVO(qwUser);
         return getDataTable(list);
@@ -442,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);
+    }
     /**
     * 查询企微员工列表-用于员工管理绑定
     */
@@ -842,4 +866,13 @@ public class QwUserController extends BaseController
         return R.ok();
     }
 
+    @GetMapping("/companyQwUserlist")
+    public TableDataInfo companyQwUserlist(@RequestParam Long companyId,
+                                           @RequestParam String corpId,
+                                           @RequestParam(required = false) String nickName)
+    {
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListByCompanyIdAndCorpIdAndNickName(companyId, corpId, nickName);
+        return getDataTable(list);
+    }
 }

+ 71 - 0
fs-company/src/main/java/com/fs/company/controller/store/FsPrescribeController.java

@@ -1,15 +1,20 @@
 package com.fs.company.controller.store;
 
+import cn.hutool.json.JSONObject;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.enums.ImTypeEnum;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.his.domain.FsExportTask;
 import com.fs.his.domain.FsPrescribe;
+import com.fs.his.dto.PayloadDTO;
 import com.fs.his.param.FsPrescribeParam;
 import com.fs.his.service.IFsExportTaskService;
 import com.fs.his.service.IFsPrescribeService;
@@ -17,6 +22,11 @@ import com.fs.his.vo.FsMessageFeedbackExportListVO;
 import com.fs.his.vo.FsPrescribeExcelListVO;
 import com.fs.his.vo.FsPrescribeListVO;
 import com.fs.his.vo.FsPrescribeVO;
+import com.fs.im.config.ImTypeConfig;
+import com.fs.im.dto.OpenImMsgDTO;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
@@ -31,6 +41,7 @@ import java.util.List;
  * @author fs
  * @date 2023-06-13
  */
+@Slf4j
 @RestController
 @RequestMapping("/store/prescribe")
 public class FsPrescribeController extends BaseController
@@ -39,6 +50,8 @@ public class FsPrescribeController extends BaseController
     private IFsPrescribeService fsPrescribeService;
     @Autowired
     private IFsExportTaskService exportTaskService;
+    @Autowired
+    private OpenIMService openIMService;
     /**
      * 查询处方列表
      */
@@ -72,6 +85,64 @@ public class FsPrescribeController extends BaseController
         return getDataTable(list);
     }
 
+    @GetMapping("/myImList")
+    public TableDataInfo myImList(FsPrescribeParam fsPrescribe)
+    {
+        startPage();
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        //fsPrescribe.setCompanyUserId(loginUser.getUser().getUserId());
+        List<FsPrescribeListVO> list = fsPrescribeService.selectFsPrescribeListVO(fsPrescribe);
+        for (FsPrescribeListVO vo : list) {
+            if (vo.getPatientTel()!=null){
+                vo.setPatientTel(vo.getPatientTel().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            }
+        }
+        return getDataTable(list);
+    }
+
+    @PostMapping("/sendPrescribe")
+    public AjaxResult sendPrescribe(@RequestBody FsPrescribeParam fsPrescribe) throws JsonProcessingException {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if(ImTypeConfig.IMTYPE== ImTypeEnum.OPENIM){
+            log.info("拼接消息体");
+            //部署到正式环境以后下面这段代码要注释,使用定时任务执行auditPrescribe审核处方以后发送消息
+            ObjectMapper objectMapper = new ObjectMapper();
+            OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+            openImMsgDTO.setSendID("C"+loginUser.getUser().getUserId());
+            openImMsgDTO.setRecvID("U"+fsPrescribe.getUserId());
+            openImMsgDTO.setContentType(110);
+            openImMsgDTO.setSenderPlatformID(5);
+            openImMsgDTO.setSessionType(1);
+            OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+            //content.setContent(ext);
+            PayloadDTO payload = new PayloadDTO();
+            payload.setData("prescribe");
+            PayloadDTO.Extension extension = new PayloadDTO.Extension();
+            extension.setDiagnose(fsPrescribe.getDiagnose());
+            extension.setPrescribeId(fsPrescribe.getPrescribeId().toString());
+            payload.setExtension(extension);
+            extension.setStatus(fsPrescribe.getStatus());
+            OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+
+            imData.setPayload(payload);
+
+            String imJson = objectMapper.writeValueAsString(imData);
+            content.setData(imJson);
+            openImMsgDTO.setContent(content);
+
+            JSONObject jsonObject = new JSONObject(openImMsgDTO);
+            log.info("开始发送消息");
+//            OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
+//            offlinePushInfo.setDesc("处方单");
+//            offlinePushInfo.setTitle(doctor.getDoctorName());
+//            offlinePushInfo.setIOSBadgeCount(true);
+//            offlinePushInfo.setIOSPushSound("");
+//            openImMsgDTO.setOfflinePushInfo(offlinePushInfo);
+            OpenImResponseDTO openImResponseDTO = openIMService.openIMSendMsg(openImMsgDTO);
+            return AjaxResult.success(openImResponseDTO);
+        }
+        return AjaxResult.success(null);
+    }
 
     /**
      * 导出处方列表

+ 2 - 2
fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java

@@ -130,7 +130,7 @@ public class FsStoreOrderController extends BaseController
         task.setCompanyUserId(userId);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param,false, null);
+        exportTaskService.exportStore1Data(param,false, false,null);
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
 
 
@@ -172,7 +172,7 @@ public class FsStoreOrderController extends BaseController
         task.setCompanyUserId(userId);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param,false, null);
+        exportTaskService.exportStore1Data(param,false,false, null);
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
     }
     /**

+ 29 - 0
fs-company/src/main/java/com/fs/company/utils/QwStatusEnum.java

@@ -0,0 +1,29 @@
+package com.fs.company.utils;
+
+/**
+ * @description:
+ * @author: Guos
+ * @time: 2025/10/16 下午3:00
+ */
+public enum QwStatusEnum {
+
+    BOUND(1, "已绑定"),
+    UNBOUND(0, "未绑定");
+
+    private Integer code;
+
+    private String detail;
+
+    QwStatusEnum(Integer code, String detail) {
+        this.code = code;
+        this.detail = detail;
+    }
+
+    public int getCode(){
+        return this.code;
+    }
+
+    public String getDetail() {
+        return this.detail;
+    }
+}

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

@@ -110,7 +110,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                         "/**/*.js",
                         "/profile/**"
                 ).permitAll()
-
+                .antMatchers("/test").anonymous()
                 .antMatchers("**/callerResult").anonymous()
                 .antMatchers("/qw/getJsapiTicket/**").anonymous()
                 .antMatchers("/msg/**").anonymous()

+ 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, "订单数据");
+        }
     }
 
 

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

@@ -1,6 +1,7 @@
 package com.fs.user;
 
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -10,12 +11,17 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.cache.ICompanyUserCacheService;
+import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.service.IFsUserCourseService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.PhoneUtil;
+import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.CustomerTransferApproval;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.service.ICustomerTransferApprovalService;
@@ -24,6 +30,7 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+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.*;
@@ -51,6 +58,12 @@ public class FsUserAdminController extends BaseController {
     @Autowired
     private ICustomerTransferApprovalService transferApprovalService;
 
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+
+    @Autowired
+    private OpenIMService openIMService;
+
     @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @PostMapping("/list")
     @ApiOperation("会员列表(与移动端使用的相同查询)")
@@ -145,4 +158,17 @@ public class FsUserAdminController extends BaseController {
     }
 
 
+    @ApiOperation("后台会员批量发送课程消息")
+    @PostMapping("/batchSendCourse")
+    public OpenImResponseDTO batchSendCourse(@RequestBody BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {
+        // 生成看课短链
+        FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+        BeanUtils.copyProperties(batchSendCourseDTO, fsCourseLinkCreateParam);
+        R courseSortLink = fsUserCourseService.createAppCourseSortLink(fsCourseLinkCreateParam);
+        String url = courseSortLink.get("url").toString();
+        batchSendCourseDTO.setUrl(url);
+
+        return openIMService.batchSendCourse(batchSendCourseDTO);
+    }
+
 }

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

@@ -4,11 +4,11 @@ server:
 spring:
   profiles:
 #    active: druid-fcky-test
-#    active: druid-jnmy-test
+    active: druid-jnmy-test
 #    active: druid-jzzx-test
 #    active: druid-hdt
 #    active: druid-sxjz
 #    active: druid-yzt
 #    active: druid-myhk
 #    active: druid-sft
-    active: dev-yjb
+#    active: dev-yjb

+ 19 - 6
fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,8 +1,9 @@
 package com.fs.app.controller;
 
 
-
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.Login;
 import com.fs.app.param.SignParam;
 import com.fs.app.utils.CityTreeUtil;
@@ -25,11 +26,10 @@ import com.fs.his.param.ImMsgParam;
 import com.fs.his.service.*;
 
 import com.fs.his.utils.ConfigUtil;
-import com.fs.im.dto.MsgCustomDTO;
-import com.fs.im.dto.MsgDTO;
-import com.fs.im.dto.MsgDataDTO;
-import com.fs.im.dto.MsgDataFormatDTO;
+import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
+import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
@@ -60,7 +60,6 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 
-
 @Api("公共接口")
 @RestController
 @RequestMapping(value="/app/common")
@@ -102,6 +101,8 @@ public class CommonController {
 	private IFsFollowService followService;
 	@Autowired
 	private IImService imService;
+	@Autowired
+	private OpenIMService openIMService;
 	@ApiOperation("测试")
 	@GetMapping(value = "/getTest")
 	public AjaxResult getTest()
@@ -239,6 +240,18 @@ public class CommonController {
 		vo.setActionStatus("OK");
 		return vo;
 	}
+	@ApiOperation("openIm聊天数据回调")
+	@PostMapping(value = "/callbackAfterSendSingleMsgCommand")
+	public OpenImMsgCallBackResponse openImMsgCallBack(@RequestBody String body, HttpServletRequest request) throws JsonProcessingException {
+
+		Gson gson = new Gson();
+		OpenImMsgCallBackVO messageInfo = gson.fromJson(body, OpenImMsgCallBackVO.class);
+
+		//openIMService.AiAutoReply(messageInfo);
+
+		//log.info("收到的参数{}", JSON.toJSONString(messageInfo));
+		return inquiryOrderMsgService.openImSaveMsg(messageInfo);
+	}
 	/**
 	 * 生成验证码
 	 */

+ 5 - 0
fs-doctor-app/src/main/java/com/fs/app/controller/DiagnosisController.java

@@ -33,4 +33,9 @@ public class DiagnosisController extends AppBaseController{
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return diagnosisService.fill(param);
     }
+
+    @GetMapping("/{id}")
+    public R detail(@PathVariable("id") Long id){
+        return R.ok().put("data", diagnosisService.selectFsFirstDiagnosisById(id));
+    }
 }

+ 8 - 3
fs-doctor-app/src/main/java/com/fs/app/controller/DoctorController.java

@@ -21,6 +21,8 @@ import com.fs.his.param.FsDoctorExtractListSParam;
 import com.fs.his.service.*;
 import com.fs.his.vo.FsDoctorBillListSVO;
 import com.fs.his.vo.FsDoctorExtractListSVO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.service.OpenIMService;
 import com.fs.im.service.OpenIMService;
 import com.fs.sms.service.SmsService;
 import com.fs.system.service.ISysConfigService;
@@ -29,6 +31,7 @@ import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.util.StringUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.json.JSONArray;
 import org.json.JSONObject;
@@ -43,6 +46,7 @@ import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 
+@Slf4j
 @Api("个人中心")
 @RestController
 @RequestMapping(value="/app/doctor")
@@ -117,7 +121,7 @@ public class DoctorController extends  AppBaseController {
             requestBody = new JSONObject();
             userIds.add(userId);
             requestBody.put("checkUserIDs", userIds);
-            String body = HttpRequest.post("https://web.im.cdwjyyh.com/api/user/account_check")
+            String body = HttpRequest.post(IMConfig.URL+"/user/account_check")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(requestBody.toString())
@@ -144,9 +148,10 @@ public class DoctorController extends  AppBaseController {
                     requestBody = new JSONObject();
                     userIds.add(userId);
                     requestBody.put("users", users);
-                    HttpRequest.post("https://web.im.cdwjyyh.com/api/user/user_register")
+                    String body1 = HttpRequest.post(IMConfig.URL+"/user/user_register")
                             .header("operationID", String.valueOf(System.currentTimeMillis()))
                             .header("token", adminToken).body(requestBody.toString()).execute().body();
+                    log.info("注册结果:"+body1);
                 }
             } else {
                 return R.error("返回结果为空");
@@ -157,7 +162,7 @@ public class DoctorController extends  AppBaseController {
             requestBody = new JSONObject();
             requestBody.put("platformID",5);
             requestBody.put("userID",userId);
-            String body1 = HttpRequest.post("https://web.im.cdwjyyh.com/api/auth/get_user_token")
+            String body1 = HttpRequest.post(IMConfig.URL+"/auth/get_user_token")
                     .header("operationID", String.valueOf(System.currentTimeMillis()))
                     .header("token", adminToken)
                     .body(requestBody.toString()).execute().body();

+ 24 - 6
fs-doctor-app/src/main/java/com/fs/app/controller/DrugReportController.java

@@ -2,10 +2,13 @@ package com.fs.app.controller;
 
 
 import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.param.DrugReportAddParam;
 import com.fs.app.param.DrugReportFinishParam;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
+import com.fs.common.enums.ImTypeEnum;
 import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsDrugReport;
 import com.fs.his.domain.FsFollow;
@@ -19,8 +22,11 @@ import com.fs.his.service.IFsFollowService;
 import com.fs.his.vo.FsDrugReportDVO;
 import com.fs.his.vo.FsDrugReportListDVO;
 import com.fs.his.vo.FsFollowListDVO;
+import com.fs.im.config.IMConfig;
+import com.fs.im.config.ImTypeConfig;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -53,7 +59,8 @@ public class DrugReportController extends AppBaseController {
     private IImService imService;
     @Autowired
     private IFsDrugReportCountService fsDrugReportCountService;
-
+    @Autowired
+    private OpenIMService openIMService;
     @ApiOperation("获取报告列表")
     @GetMapping("/getDrugReportList")
     public R getDrugReportList(FsDrugReportListDParam param)
@@ -74,7 +81,7 @@ public class DrugReportController extends AppBaseController {
 
     @ApiOperation("提交报告")
     @PostMapping("/addReport")
-    public R addReport(@Validated @RequestBody DrugReportAddParam param, HttpServletRequest request){
+    public R addReport(@Validated @RequestBody DrugReportAddParam param, HttpServletRequest request) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
 
         FsDrugReport report=new FsDrugReport();
@@ -104,8 +111,13 @@ public class DrugReportController extends AppBaseController {
             msg.setMsgContent(new MsgDataFormatDTO("drugReport",ext,report.getReportId().toString()));
             msgs.add(msg);
             msgDTO.setMsgBody(msgs);
-            imService.sendMsg(msgDTO);
-
+            if (ImTypeConfig.IMTYPE== ImTypeEnum.OPENIM){
+                ObjectMapper objectMapper = new ObjectMapper();
+                String ex = objectMapper.writeValueAsString(customDTO);
+                openIMService.sendUtil("D"+follow.getDoctorId(),"U"+follow.getUserId(),110,"drugReport","","","",report.getReportId().toString(),ex);
+            }else if (ImTypeConfig.IMTYPE== ImTypeEnum.TENCENT){
+                imService.sendMsg(msgDTO);
+            }
             fsDrugReportCountService.addReportCountByUserIdAndDoctorId(follow.getUserId(),follow.getDoctorId());
 
             return R.ok();
@@ -118,7 +130,7 @@ public class DrugReportController extends AppBaseController {
     @ApiOperation("完成咨询")
     @PostMapping("/finishDrugReport")
     @RepeatSubmit
-    public R finishDrugReport(@Validated @RequestBody DrugReportFinishParam param, HttpServletRequest request){
+    public R finishDrugReport(@Validated @RequestBody DrugReportFinishParam param, HttpServletRequest request) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
         //发送给用户
         MsgDTO msgDTO=new MsgDTO();
@@ -136,7 +148,13 @@ public class DrugReportController extends AppBaseController {
         msg.setMsgContent(new MsgDataFormatDTO("您的用药咨询报告已出具,本次咨询结束"));
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        if (ImTypeConfig.IMTYPE== ImTypeEnum.OPENIM){
+            ObjectMapper objectMapper = new ObjectMapper();
+            String ex = objectMapper.writeValueAsString(customDTO);
+            openIMService.sendUtil("D"+follow.getDoctorId(),"U"+follow.getUserId(),110,"finishDrugReport","","您的用药咨询报告已出具,本次咨询结束","","",ex);
+        }else if (ImTypeConfig.IMTYPE== ImTypeEnum.TENCENT){
+            imService.sendMsg(msgDTO);
+        }
         redisTemplate.delete("DrugReport:doctorId:" + follow.getDoctorId() + ":userId:" + follow.getUserId());
         return R.ok();
 

+ 50 - 11
fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java

@@ -3,11 +3,15 @@ package com.fs.app.controller;
 
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.annotation.Login;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.domain.R;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.enums.ImTypeEnum;
+import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.core.config.WxPayProperties;
@@ -17,19 +21,27 @@ import com.fs.his.dto.FsInquiryOrderPatientDTO;
 import com.fs.his.param.*;
 import com.fs.his.service.*;
 import com.fs.his.vo.*;
+import com.fs.im.config.ImTypeConfig;
 import com.fs.im.dto.MsgCustomDTO;
 import com.fs.im.dto.MsgDTO;
 import com.fs.im.dto.MsgDataDTO;
 import com.fs.im.dto.MsgDataFormatDTO;
 import com.fs.im.service.IImService;
+import com.fs.im.service.OpenIMService;
+import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import io.reactivex.internal.operators.parallel.ParallelRunOn;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -71,7 +83,8 @@ public class InquiryOrderController extends  AppBaseController {
     private IFsInquiryOrderReportService orderReportService;
     @Autowired
     private IImService imService;
-
+    @Autowired
+    private OpenIMService openIMService;
     @Login
     @GetMapping("/getInquiryOrderList")
     public R getInquiryOrderList(FsInquiryOrderListPDParam param)
@@ -79,11 +92,7 @@ public class InquiryOrderController extends  AppBaseController {
         FsDoctor doctor=doctorService.selectFsDoctorByDoctorId(Long.parseLong(getDoctorId()));
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
 
-        if (Objects.nonNull(param.getUserId())) {
-            param.setDoctorId(null);
-        } else if (Objects.nonNull(param.getDoctorId())) {
-            param.setDoctorId(Long.parseLong(getDoctorId()));
-        }
+        param.setDoctorId(Long.parseLong(getDoctorId()));
 
         param.setIsAccept(doctor.getIsAccept());
         param.setIsSelf(doctor.getIsSelf());
@@ -164,7 +173,7 @@ public class InquiryOrderController extends  AppBaseController {
     @Login
     @ApiOperation("抢单")
     @PostMapping("/acceptOrder")
-    public R acceptOrder(@Validated @RequestBody FsInquiryOrderAcceptParam param, HttpServletRequest request){
+    public R acceptOrder(@Validated @RequestBody FsInquiryOrderAcceptParam param, HttpServletRequest request) throws JsonProcessingException {
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return inquiryOrderService.acceptOrder(param);
     }
@@ -172,7 +181,7 @@ public class InquiryOrderController extends  AppBaseController {
     @Login
     @ApiOperation("接单")
     @PostMapping("/receiveOrder")
-    public R receiveOrder(@Validated @RequestBody FsInquiryOrderReceiveParam param, HttpServletRequest request){
+    public R receiveOrder(@Validated @RequestBody FsInquiryOrderReceiveParam param, HttpServletRequest request) throws JsonProcessingException {
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return inquiryOrderService.receiveOrder(param);
     }
@@ -188,7 +197,7 @@ public class InquiryOrderController extends  AppBaseController {
     @Login
     @ApiOperation("完成订单")
     @PostMapping("/finishOrder")
-    public R finishOrder(@Validated @RequestBody FsInquiryOrderFinishParam param, HttpServletRequest request){
+    public R finishOrder(@Validated @RequestBody FsInquiryOrderFinishParam param, HttpServletRequest request) throws JsonProcessingException {
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return inquiryOrderService.finishOrder(param);
     }
@@ -241,7 +250,7 @@ public class InquiryOrderController extends  AppBaseController {
     @ApiOperation("提交诊断报告")
     @PostMapping("/submitInquiryOrderReport")
     @Transactional
-    public R submitInquiryOrderReport(@Validated @RequestBody FsInquiryOrderReportSubmitDParam param, HttpServletRequest request){
+    public R submitInquiryOrderReport(@Validated @RequestBody FsInquiryOrderReportSubmitDParam param, HttpServletRequest request) throws JsonProcessingException {
         FsInquiryOrderReport report=null;
         if(param.getReportId()!=null&&param.getReportId()>0){
             report=orderReportService.selectFsInquiryOrderReportByReportId(param.getReportId());
@@ -299,7 +308,30 @@ public class InquiryOrderController extends  AppBaseController {
         msg.setMsgType("TIMCustomElem");//TIMCustomElem
         msgs.add(msg);
         msgDTO.setMsgBody(msgs);
-        imService.sendMsg(msgDTO);
+        if (ImTypeConfig.IMTYPE== ImTypeEnum.OPENIM){
+            ObjectMapper objectMapper = new ObjectMapper();
+            String ex = objectMapper.writeValueAsString(customDTO);
+            openIMService.sendUtil("D"+report.getDoctorId(),"U"+report.getUserId(),110,"report","","","",report.getOrderId().toString(),ex);
+        }else if (ImTypeConfig.IMTYPE== ImTypeEnum.TENCENT){
+            imService.sendMsg(msgDTO);
+        }
+//        JSONObject jsonObject = new JSONObject();
+//        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+//        openImMsgDTO.setSendID("D"+report.getDoctorId().toString());
+//        openImMsgDTO.setRecvID("U"+report.getUserId().toString());
+//        openImMsgDTO.setContentType(110);
+//        openImMsgDTO.setSenderPlatformID(5);
+//        openImMsgDTO.setSessionType(1);
+//
+//        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+//        PayloadDTO payloadDTO = new PayloadDTO();
+//        payloadDTO.setData("startInquiry");
+//        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+//        extension.setTitle();
+//        //content.setContent(ext);
+//        openImMsgDTO.setContent(content);
+//        openImMsgDTO.setEx(customDTO);
+//        openIMService.openIMSendMsg(openImMsgDTO);
         return R.ok("操作成功");
     }
 
@@ -330,4 +362,11 @@ public class InquiryOrderController extends  AppBaseController {
         }
         return R.ok().put("data","");
     }
+
+    @PostMapping("/closeOrder")
+    public R closeOrder(@RequestBody Long orderId){
+        inquiryOrderService.closeOrder(orderId);
+        logger.info("closeOrder: {}", orderId);
+        return R.ok();
+    }
 }

+ 108 - 1
fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java

@@ -4,17 +4,23 @@ package com.fs.app.controller;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.R;
+import com.fs.common.enums.ImTypeEnum;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.his.domain.*;
 import com.fs.his.dto.FsInquiryOrderPatientDTO;
+import com.fs.his.dto.PayloadDTO;
 import com.fs.his.param.*;
 import com.fs.his.service.*;
 import com.fs.his.vo.FsDoctorPrescribeListDVO;
@@ -27,14 +33,22 @@ import com.fs.hospital580.entity.Hospital580PrescriptionScrmEntity;
 import com.fs.hospital580.service.Hospital580PrescriptionMedicineScrmService;
 import com.fs.hospital580.service.Hospital580PrescriptionScrmService;
 import com.fs.hospital580.vo.res.PrescriptionListRes;
+import com.fs.im.config.IMConfig;
+import com.fs.im.config.ImTypeConfig;
+import com.fs.im.dto.MsgCustomDTO;
+import com.fs.im.dto.OpenImMsgDTO;
+import com.fs.im.dto.OpenImResponseDTO;
 import com.fs.im.service.IImService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
+import com.fs.im.service.OpenIMService;
+import com.fs.qw.domain.QwExternalContact;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.Synchronized;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -42,11 +56,13 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.*;
 import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
 
 
+@Slf4j
 @Api("处方接口")
 @RestController
 @RequestMapping(value="/app/prescribe")
@@ -81,6 +97,8 @@ public class PrescribeController extends  AppBaseController {
 
     @Autowired
     private IFsStoreProductScrmService productScrmService;
+    @Autowired
+    private OpenIMService openIMService;
     @Login
     @GetMapping("/getDoctorPrescribeList")
     public R getDoctorPrescribeList(FsDoctorPrescribeListDParam param)
@@ -277,7 +295,7 @@ public class PrescribeController extends  AppBaseController {
     @ApiOperation("创建处方")
     @Transactional
     @PostMapping("/createPrescribe")
-    public R createPrescribe(@Validated @RequestBody FsPrescribeCreateParam param, HttpServletRequest request){
+    public R createPrescribe(@Validated @RequestBody FsPrescribeCreateParam param, HttpServletRequest request) throws JsonProcessingException {
 
         //获取问诊单
         FsInquiryOrder inquiryOrder=inquiryOrderService.selectFsInquiryOrderByOrderId(param.getOrderId());
@@ -344,6 +362,44 @@ public class PrescribeController extends  AppBaseController {
                     prescribeDrug.setPrescribeId(prescribe.getPrescribeId());
                     prescribeDrugService.insertFsPrescribeDrug(prescribeDrug);
                 }
+                if(ImTypeConfig.IMTYPE== ImTypeEnum.OPENIM){
+                    log.info("拼接消息体");
+                    //部署到正式环境以后下面这段代码要注释,使用定时任务执行auditPrescribe审核处方以后发送消息
+                    ObjectMapper objectMapper = new ObjectMapper();
+                    OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+                    openImMsgDTO.setSendID("D"+inquiryOrder.getDoctorId());
+                    openImMsgDTO.setRecvID("U"+inquiryOrder.getUserId());
+                    openImMsgDTO.setContentType(110);
+                    openImMsgDTO.setSenderPlatformID(5);
+                    openImMsgDTO.setSessionType(1);
+                    OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+                    //content.setContent(ext);
+                    PayloadDTO payload = new PayloadDTO();
+                    payload.setData("prescribe");
+                    PayloadDTO.Extension extension = new PayloadDTO.Extension();
+                    extension.setDiagnose(inquiryOrder.getTitle());
+                    extension.setPrescribeId(prescribe.getPrescribeId().toString());
+                    payload.setExtension(extension);
+                    extension.setStatus(prescribe.getStatus());
+                    OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+
+                    imData.setPayload(payload);
+
+                    String imJson = objectMapper.writeValueAsString(imData);
+                    content.setData(imJson);
+                    openImMsgDTO.setContent(content);
+
+                    JSONObject jsonObject = new JSONObject(openImMsgDTO);
+                    log.info("开始发送消息");
+                    OpenImMsgDTO.OfflinePushInfo offlinePushInfo = new OpenImMsgDTO.OfflinePushInfo();
+                    offlinePushInfo.setDesc("处方单");
+                    offlinePushInfo.setTitle(doctor.getDoctorName());
+                    offlinePushInfo.setIOSBadgeCount(true);
+                    offlinePushInfo.setIOSPushSound("");
+                    openImMsgDTO.setOfflinePushInfo(offlinePushInfo);
+                    OpenImResponseDTO openImResponseDTO = openIMService.openIMSendMsg(openImMsgDTO);
+                }
+
 
                 return R.ok("操作成功").put("prescribe",prescribe);
             }
@@ -366,4 +422,55 @@ public class PrescribeController extends  AppBaseController {
         String url=prescribeService.getPrescribeCodeUrl(prescribeId);
         return R.ok().put("url",url);
     }
+    @PostMapping("/test")
+    public R test(@RequestBody HashMap<String,String> map) throws JsonProcessingException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        openImMsgDTO.setSendID("D"+79);
+        openImMsgDTO.setRecvID("U"+map.get("userId"));
+        openImMsgDTO.setContentType(110);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        //content.setContent(ext);
+        PayloadDTO payload = new PayloadDTO();
+        payload.setData("prescribe");
+        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+        extension.setDiagnose("感冒发烧,胸闷");
+        payload.setExtension(extension);
+        //log.info("payload:{}",payload);
+        OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+
+        imData.setPayload(payload);
+
+        String imJson = objectMapper.writeValueAsString(imData);
+        content.setData(imJson);
+        openImMsgDTO.setContent(content);
+        //log.info("openImMsgDTO:{}",openImMsgDTO);
+        JSONObject jsonObject = new JSONObject(openImMsgDTO);
+        //log.info("jsonObject:{}",jsonObject);
+        MsgCustomDTO customDTO=new MsgCustomDTO();
+        //customDTO.setType("startInquiry");
+        customDTO.setType(map.get("payloadDAata").toString());
+        customDTO.setOrderType(2);
+        customDTO.setOrderId(map.get("orderId"));
+        String imtype = map.get("imtype").toString();
+
+        customDTO.setImType(Integer.parseInt(imtype));
+        //openImMsgDTO.setEx(payload);
+        String ex = objectMapper.writeValueAsString(customDTO);
+        QwExternalContact qwExternalContact = new QwExternalContact();
+        qwExternalContact.setId(1l);
+        qwExternalContact.setFsUserId(1l);
+        //WatchSleepData last = watchSleepDataMapper.getLast("861389060165680");
+        //watchAudioMsgLogMapper.insert(watchAudioMsgLog);
+        //qwExternalContactMapper.selectRemarkByCompanyUserAndFsUser(map.get("sendId"), map.get("userId"),"wwwwww");
+        //fsUserCouponService.updateFsUserCouponStatusByLimtType2();
+//        openIMService.checkAndImportFriendByDianBo(Long.parseLong(map.get("sendId")),map.get("userId"),qwExternalContact.getCorpId());
+        //OpenImResponseDTO openImResponseDTO = openIMService.sendCourse(Long.parseLong(map.get("userId")), Long.parseLong(map.get("sendId")), "/pages/courseAnswer/index?link=1932017457275338752", "《五仙传医2.0》","https://cos.his.cdwjyyh.com/fs/20241108/a8ed49ae9a264c7483cec5bdcbcf6060.png");
+        log.info("请求地址{}",IMConfig.URL);
+//        log.info("前缀{}",IMConfig.PREFIX);
+        //OpenImResponseDTO openImResponseDTO = openIMService.sendUtil("D" +map.get("sendId"), "U"+map.get("userId").toString(), 110, map.get("payloadDAata").toString(), "", map.get("title").toString(), "", "3135749",ex);
+        return R.ok().put("data",null);
+    }
 }

+ 18 - 2
fs-doctor-app/src/main/java/com/fs/framework/config/MyBatisConfig.java

@@ -28,7 +28,7 @@ import java.util.List;
 
 /**
  * Mybatis支持*匹配扫描包
- * 
+ *
 
  */
 @Configuration
@@ -115,6 +115,22 @@ public class MyBatisConfig
         return resources.toArray(new Resource[resources.size()]);
     }
 
+    //    @Bean
+//    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
+//    {
+//        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
+//        String mapperLocations = env.getProperty("mybatis.mapperLocations");
+//        String configLocation = env.getProperty("mybatis.configLocation");
+//        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+//        VFS.addImplClass(SpringBootVFS.class);
+//
+//        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+//        sessionFactory.setDataSource(dataSource);
+//        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+//        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
+//        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+//        return sessionFactory.getObject();
+//    }
     @Bean
     public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception
     {
@@ -131,4 +147,4 @@ public class MyBatisConfig
         sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
         return sessionFactory.getObject();
     }
-}
+}

+ 2 - 2
fs-doctor-app/src/main/resources/mybatis/mybatis-config.xml

@@ -3,7 +3,7 @@
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
-	
+
 	<settings>
 		<setting name="cacheEnabled"             value="true" />  <!-- 全局映射器启用缓存 -->
 		<setting name="useGeneratedKeys"         value="true" />  <!-- 允许 JDBC 支持自动生成主键 -->
@@ -11,5 +11,5 @@ PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 		<setting name="logImpl"                  value="SLF4J" /> <!-- 指定 MyBatis 所用日志的具体实现 -->
 		 <setting name="mapUnderscoreToCamelCase" value="true"/>
 	</settings>
-	
+
 </configuration>

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

@@ -9,4 +9,4 @@ spring:
 #    active: druid-yzt
 #    active: druid-sxjz
 #    active: druid-sft
-group-no: 1
+group-no: 2

+ 29 - 1
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -16,10 +16,12 @@ import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
+import com.fs.qw.domain.QwUserVideo;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwUserService;
+import com.fs.qw.service.IQwUserVideoService;
 import com.fs.qw.service.IQwUserVoiceLogService;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
@@ -57,6 +59,8 @@ public class QwMsgController {
     @Autowired
     IQwUserService qwUserService;
     @Autowired
+    IQwUserVideoService qwUserVideoService;
+    @Autowired
     IQwUserVoiceLogService qwUserVoiceLogService;
     @Autowired
     IFsStoreOrderService fsStoreOrderService;
@@ -304,7 +308,7 @@ public class QwMsgController {
                 if (wxWorkMessageDTO.getReferid()!=0){
                     break;
                 }
-                if (wxWorkMessageDTO.getMsgtype()==2||wxWorkMessageDTO.getMsgtype()==0||wxWorkMessageDTO.getMsgtype()==16||wxWorkMessageDTO.getMsgtype() == 101||wxWorkMessageDTO.getMsgtype() == 104){
+                if (wxWorkMessageDTO.getMsgtype()==2||wxWorkMessageDTO.getMsgtype()==0||wxWorkMessageDTO.getMsgtype()==16||wxWorkMessageDTO.getMsgtype() == 101||wxWorkMessageDTO.getMsgtype() == 104||wxWorkMessageDTO.getMsgtype()==141){
 
                     String content = wxWorkMessageDTO.getContent();
                     log.info("id:{}, 接收人:"+wxWorkMessageDTO.getReceiver(), id);
@@ -342,6 +346,30 @@ public class QwMsgController {
                     else if (wxWorkMessageDTO.getMsgtype() == 104){
                         content = wxWorkMessageDTO.getUrl();
                         log.info("id:{}, 用户发送表情"+content, id);
+                    }//视频号
+                    else if (wxWorkMessageDTO.getMsgtype()==141){
+                        QwUser qwUserByAppKey = qwUserMapper.selectQwUserById(id);
+                        if(qwUserByAppKey.getVideoGetStatus() != null && qwUserByAppKey.getVideoGetStatus() == 1){
+                            QwUserVideo qwUserVideo = qwUserVideoService.selectByObjectId(wxWorkMessageDTO.getObjectId(), qwUserByAppKey.getId());
+                            if(qwUserVideo == null){
+                                QwUserVideo userVideo=new QwUserVideo();
+                                userVideo.setSenderName(wxWorkMessageDTO.getSender_name());
+                                userVideo.setAppKey(qwUserByAppKey.getAppKey());
+                                userVideo.setNickName(wxWorkMessageDTO.getNickname());
+                                userVideo.setObjectId(wxWorkMessageDTO.getObjectId());
+                                userVideo.setCoverUrl(wxWorkMessageDTO.getCover_url());
+                                userVideo.setThumbUrl(wxWorkMessageDTO.getThumb_url());
+                                userVideo.setAvatar(wxWorkMessageDTO.getAvatar());
+                                userVideo.setDesc(wxWorkMessageDTO.getDesc());
+                                userVideo.setUrl(wxWorkMessageDTO.getUrl());
+                                userVideo.setExtras(wxWorkMessageDTO.getExtras());
+                                userVideo.setObjectNonceId(wxWorkMessageDTO.getObjectNonceId());
+                                userVideo.setQwUserId(qwUserByAppKey.getId());
+                                userVideo.setCompanyUserId(qwUserByAppKey.getCompanyUserId());
+                                userVideo.setCompanyId(qwUserByAppKey.getCompanyId());
+                                qwUserVideoService.insertQwUserVideo(userVideo);
+                            }
+                        }
                     }
 
                     if (2000000000000000L-receiver>0){

+ 35 - 2
fs-qw-api/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,8 +1,21 @@
 package com.fs.app.controller;
 
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.file.OssException;
+import com.fs.course.param.UserCourseComplaintRecordParam;
+import com.fs.course.service.IFsUserCourseComplaintTypeService;
+import com.fs.course.vo.FsUserCourseComplaintTypeListVO;
+import com.fs.qw.param.QwUserComplaintRecordParam;
+import com.fs.qw.service.IQwUserComplainRecordService;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
 import io.swagger.annotations.Api;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
 
 
 @Api("公共接口")
@@ -10,6 +23,26 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping(value="/app/common")
 public class CommonController {
 
+    @Autowired
+    private IQwUserComplainRecordService qwUserComplainRecordService;
+
+    @Autowired
+    private IFsUserCourseComplaintTypeService fsUserCourseComplaintTypeService;
+
+    @PostMapping("uploadOSS")
+    public R uploadOSS(@RequestParam("file") MultipartFile file) throws Exception
+    {
 
+        if (file.isEmpty())
+        {
+            throw new OssException("上传文件不能为空");
+        }
+        // 上传文件
+        String fileName = file.getOriginalFilename();
+        String suffix = fileName.substring(fileName.lastIndexOf("."));
+        CloudStorageService storage = OSSFactory.build();
+        String url = storage.uploadSuffix(file.getBytes(), suffix);
+        return R.ok().put("url",url);
+    }
 
 }

+ 46 - 0
fs-qw-api/src/main/java/com/fs/app/controller/QwUserComplainRecordController.java

@@ -0,0 +1,46 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.domain.R;
+import com.fs.course.service.IFsUserCourseComplaintTypeService;
+import com.fs.course.vo.FsUserCourseComplaintTypeListVO;
+import com.fs.qw.param.QwUserComplaintRecordParam;
+import com.fs.qw.service.IQwUserComplainRecordService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Api("企微员工投诉接口")
+@RestController
+@RequestMapping(value="/app/record")
+public class QwUserComplainRecordController {
+
+
+    @Autowired
+    private IQwUserComplainRecordService qwUserComplainRecordService;
+
+    @Autowired
+    private IFsUserCourseComplaintTypeService fsUserCourseComplaintTypeService;
+
+
+    @ApiOperation("获取投诉类型")
+    @GetMapping("/getTypeTree")
+    public R getTypeTree() {
+        List<FsUserCourseComplaintTypeListVO> allComplaintTypeTree = fsUserCourseComplaintTypeService.getAllComplaintTypeTree();
+        return R.ok().put("data", allComplaintTypeTree);
+    }
+
+
+    @ApiOperation("提交企微员工反馈记录")
+    @PostMapping("/recordByQwUser")
+    public R submitByQwUser(@RequestBody QwUserComplaintRecordParam param) {
+        int i = qwUserComplainRecordService.submitRecordByQwUser(param);
+        if (i > 0) {
+            return R.ok("提交成功");
+        } else {
+            return R.error("提交失败");
+        }
+    }
+}

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

+ 36 - 1
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -2,6 +2,7 @@ package com.fs.app.controller;
 
 
 import cn.hutool.core.date.DateUtil;
+import com.fs.app.taskService.*;
 import com.alibaba.fastjson.JSON;
 import com.fs.app.taskService.QwExternalContactRatingService;
 import com.fs.app.taskService.SopLogsChatTaskService;
@@ -9,6 +10,8 @@ import com.fs.app.taskService.SopLogsTaskService;
 import com.fs.app.taskService.SopWxLogsService;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyTrafficRecordService;
 import com.fs.company.vo.RedPacketMoneyVO;
@@ -24,10 +27,12 @@ import com.fs.his.service.IFsInquiryOrderService;
 import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.hisStore.config.MedicalMallConfig;
 import com.fs.qw.domain.QwCompany;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwMaterialService;
+import com.fs.qwApi.domain.QwExternalContactResult;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.QwSopMapper;
@@ -38,6 +43,7 @@ import com.fs.store.service.IFsUserCourseCountService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
 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;
@@ -49,6 +55,7 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -76,6 +83,8 @@ public class CommonController {
     private IFsCourseWatchLogService watchLogService;
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
+    @Autowired
+    private IFsCourseRedPacketLogService fsCourseRedPacketLogService;
 
     @Autowired
     private IQwSopLogsService qwSopLogsService;
@@ -121,7 +130,6 @@ public class CommonController {
     @Autowired
     private IQwMaterialService iQwMaterialService;
 
-
     @Autowired
     private IFsCourseLinkService iFsCourseLinkService;
     @Autowired
@@ -129,6 +137,20 @@ public class CommonController {
     @Autowired
     private ICompanyTrafficRecordService companyTrafficRecordService;
 
+    @Autowired
+    private SyncQwExternalContactService syncQwExternalContactService;
+
+
+
+    /**
+     * 获取跳转微信小程序的链接地址
+     */
+    @GetMapping("/getGotoWxAppLink")
+    @ApiOperation("获取跳转微信小程序的链接地址")
+    public ResponseResult<String> getGotoWxAppLink(String linkStr,String appid) {
+        return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
+    }
+
     /**
     * 发官方通连
     */
@@ -338,4 +360,17 @@ public class CommonController {
         }
         return R.ok();
     }
+
+    @GetMapping("/syncQwExternalContactUnionid")
+    public R syncQwExternalContactUnionid(){
+        return syncQwExternalContactService.syncQwExternalContactUnionid();
+    }
+
+
+    @GetMapping("/queryRedPacketResult")
+    public R queryRedPacketResult(String startTime , String  endTime) {
+        fsCourseRedPacketLogService.queryRedPacketResult(startTime, endTime);
+        return R.ok();
+    }
+
 }

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

@@ -63,6 +63,9 @@ public class qwTask {
     private ICompanyTrafficRecordService companyTrafficRecordService;
     private ISysConfigService configService;
 
+    @Autowired
+    private SyncQwExternalContactService syncQwExternalContactService;
+
     /**
      * 定时任务:检查SOP规则时间
      * 执行时间:每天凌晨 1:10:00
@@ -360,6 +363,14 @@ public class qwTask {
         log.info("====== 更新掉所有前一天的所有待发送,耗时 {} 毫秒 ======", (endTimeMillis - startTimeMillis));
     }
 
+    @Scheduled(cron = "0 1 0 */2 * ?")
+    public void updateQwExternalContactUnionid() {
+        long startTimeMillis = System.currentTimeMillis();
+        log.info("====== 同步外部联系人的UnionId ======");
+        syncQwExternalContactService.syncQwExternalContactUnionid();
+
+    }
+
     /**
      * 每天扣除流量余额
      * */

+ 10 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/SyncQwExternalContactService.java

@@ -0,0 +1,10 @@
+package com.fs.app.taskService;
+
+import com.fs.common.core.domain.R;
+
+public interface SyncQwExternalContactService {
+
+    R syncQwExternalContactUnionid();
+
+
+}

+ 302 - 56
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() {
@@ -899,6 +903,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                      Long fsUserId, boolean isGroupChat, String miniAppId, QwGroupChat groupChat,CourseConfig config,Map<Long,
                     Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType,
                                      List<Company> companies) {
+        QwExternalContact contact = null;
+        if(logVo.getExternalId() != null){
+            contact = qwExternalContactMapper.selectById(logVo.getExternalId());
+        }
         // 深拷贝 Content 对象,避免使用 JSON
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         if (clonedContent == null) {
@@ -923,64 +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)
-                                            + "\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("#销售称呼#", StringUtil.strIsNullOrEmpty(welcomeText) ? "" : welcomeText)
+                                    .replaceAll("#客户称呼#", contact == null || StringUtil.strIsNullOrEmpty(contact.getStageStatus())|| "0".equals(contact.getStageStatus())?"同学":contact.getStageStatus()));
                         }
-                    }
+//                    }
                     break;
                 //小程序单独
                 case "4":
@@ -988,7 +998,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
 
                     String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId,
-                            qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId());
+                            qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId(), isGroupChat ? groupChat.getChatId() : null);
 
                     if(sopLogs.getSendType()==1){
                         setting.setMiniprogramAppid(miniAppId);
@@ -1323,7 +1333,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
     private String createLinkByMiniApp(QwSopTempSetting.Content.Setting setting, SopUserLogsVo logVo, Date sendTime,
                                        Long courseId, Long videoId, String qwUserId,
-                                       String companyUserId, String companyId, String externalId,String isOfficial,Long fsUserId) {
+                                       String companyUserId, String companyId, String externalId,String isOfficial,Long fsUserId, String chatId) {
         // 获取缓存的配置
         CourseConfig config;
         synchronized(configLock) {
@@ -1351,6 +1361,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         link.setCourseId(courseId.longValue());
         link.setQwExternalId(Long.parseLong(externalId));
         link.setProjectCode(cloudHostProper.getProjectCode());
+        link.setChatId(chatId);
 
         if (StringUtil.strIsNullOrEmpty(isOfficial)){
             link.setLinkType(3);
@@ -1902,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);
             }
@@ -1952,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);
@@ -1961,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")) {
@@ -1980,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;
+        }
+    }
+
+
     /**
      * 解析模板设置
      */

+ 78 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SyncQwExternalContactServiceImpl.java

@@ -0,0 +1,78 @@
+package com.fs.app.taskService.impl;
+
+import com.fs.app.taskService.SyncQwExternalContactService;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qwApi.domain.QwExternalContactResult;
+import com.fs.qwApi.service.QwApiService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@Slf4j
+public class SyncQwExternalContactServiceImpl implements SyncQwExternalContactService {
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+    @Autowired
+    private QwApiService qwApiService;
+    @Override
+    public R syncQwExternalContactUnionid() {
+        // 测试环境需要在sql加上:and corp_id='ww51717e2b71d5e2d3'
+        // 查询这次同步的最大id
+        Long maxId = qwExternalContactMapper.selectSyncMaxId();
+        log.info("同步最大id值:"+maxId);
+        if (maxId == null) {
+            return R.ok("无需同步");
+        }
+        Long recordId = 0L;
+        String recordIdStr = redisCache.getCacheObject("syncQwExternalContactUnionId");
+        if (StringUtils.isNotEmpty(recordIdStr)) {
+            try {
+                recordId = Long.parseLong(recordIdStr);
+            } catch (NumberFormatException e) {
+                log.info("Failed to parse recordId from redis: {}", recordIdStr);
+                recordId = 0L;
+            }
+        }
+        log.info("开始同步的recordId值:"+recordId);
+        // 循环同步直到recordId等于maxId
+        while (recordId < maxId) {
+            // 每次查询500条数据
+            List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectSyncData(recordId, maxId);
+            if (qwExternalContacts.isEmpty()) {
+                break;
+            }
+            List<QwExternalContact> batchList = new ArrayList<>();
+            // 调用接口
+            for (QwExternalContact info : qwExternalContacts) {
+                QwExternalContactResult externalcontact = qwApiService.getExternalcontact(info.getExternalUserId(), info.getCorpId());
+                if (null!=externalcontact && null!=externalcontact.getExternal_contact() && null!=externalcontact.getExternal_contact().getUnionid() ) {
+                    info.setUnionid(externalcontact.getExternal_contact().getUnionid());
+                    batchList.add(info);
+                }
+            }
+            if (!batchList.isEmpty()) {
+                for (QwExternalContact qwExternalContact : batchList) {
+                    qwExternalContactMapper.batchUpdateUnionId(qwExternalContact);
+                }
+            }else{
+                log.info("集合为空:{recordId->"+recordId+";syncId->"+qwExternalContacts.get(qwExternalContacts.size() - 1).getId()+"}");
+            }
+            // 更新recordId为本次处理的最后一条记录的id
+            recordId = qwExternalContacts.get(qwExternalContacts.size() - 1).getId();
+            // 更新redis中的记录值
+            redisCache.setCacheObject("syncQwExternalContactUnionId", recordId.toString());
+        }
+        log.info("同步成功,同步完之后的recordId:"+recordId);
+        return R.ok();
+    }
+}

+ 8 - 6
fs-qw-voice/src/main/java/com/fs/app/mq/RocketMQConsumerService.java

@@ -77,12 +77,14 @@ public class RocketMQConsumerService implements RocketMQListener<String> {
                                             Long companyUserId = qwUserVO.getCompanyUserId();
                                             QwSopTempVoice qwSopTempVoice = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,text);
                                             if(qwSopTempVoice == null){
-                                                QwSopTempVoice sopTempVoice = new QwSopTempVoice();
-                                                sopTempVoice.setCompanyUserId(companyUserId);
-                                                sopTempVoice.setVoiceTxt(text);
-                                                sopTempVoice.setTempId(tempId);
-                                                sopTempVoice.setRecordType(0);
-                                                qwSopTempVoiceService.insertQwSopTempVoice(sopTempVoice);
+                                                if(companyUserId != null && text != null){
+                                                    QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+                                                    sopTempVoice.setCompanyUserId(companyUserId);
+                                                    sopTempVoice.setVoiceTxt(text);
+                                                    sopTempVoice.setTempId(tempId);
+                                                    sopTempVoice.setRecordType(0);
+                                                    qwSopTempVoiceService.insertQwSopTempVoice(sopTempVoice);
+                                                }
                                             }
                                         }
                                     }

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

+ 1 - 1
fs-qwhook-sop/src/main/resources/application.yml

@@ -10,4 +10,4 @@ spring:
 #    active: druid-yzt
 #    active: druid-hdt
 #    active: druid-sxjz
-    active: druid-myhk-test
+    active: dev

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

@@ -10,4 +10,4 @@ spring:
 #    active: druid-yzt
 #    active: druid-hdt
 #    active: druid-sxjz
-    active: druid-sft
+    active: dev

+ 1 - 1
fs-repeat-api/src/main/java/com/fs/app/mq/RocketMQConsumerService.java

@@ -20,7 +20,7 @@ import java.util.function.Function;
 @Slf4j
 @Service
 @AllArgsConstructor
-@RocketMQMessageListener(topic = "${rocketmq.consumer.topic}", consumerGroup = "${rocketmq.consumer.group}")
+@RocketMQMessageListener(topic = "repeat-upload", consumerGroup = "common-group")
 public class RocketMQConsumerService implements RocketMQListener<String> {
 
     private final RepeatService repeatService;

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff