Browse Source

Merge branch 'master' into bly_store

# Conflicts:
#	fs-service/src/main/java/com/fs/erp/dto/sdk/wangdian/api/WdtClient.java
#	fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java
#	fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreCartScrmServiceImpl.java
#	fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
#	fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
yfh 1 ngày trước cách đây
mục cha
commit
dbbfbd9a30
100 tập tin đã thay đổi với 3232 bổ sung339 xóa
  1. 1 0
      DirectoryV3.xml
  2. 19 0
      fs-ad-api/src/main/java/com/fs/framework/config/RedisConfig.java
  3. 682 36
      fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java
  4. 41 1
      fs-admin/src/main/java/com/fs/company/controller/CompanyController.java
  5. 103 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficController.java
  6. 49 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficLogController.java
  7. 39 3
      fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java
  8. 5 5
      fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java
  9. 1 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java
  10. 1 2
      fs-admin/src/main/java/com/fs/fastGpt/FastgptExtUserTagController.java
  11. 25 3
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java
  12. 12 12
      fs-admin/src/main/java/com/fs/his/controller/FsHisComplaintController.java
  13. 19 0
      fs-admin/src/main/java/com/fs/his/controller/FsPatientController.java
  14. 24 0
      fs-admin/src/main/java/com/fs/hisStore/FsHisStoreLogController.java
  15. 134 0
      fs-admin/src/main/java/com/fs/hisStore/FsStoreSCRMController.java
  16. 74 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  17. 83 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  18. 14 2
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  19. 108 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreRecommendScrmController.java
  20. 20 3
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreScrmController.java
  21. 11 1
      fs-admin/src/main/java/com/fs/hisStore/controller/SysOperlogScrmController.java
  22. 94 0
      fs-admin/src/main/java/com/fs/qw/FsCourseTask.java
  23. 33 1
      fs-admin/src/main/java/com/fs/qw/controller/QwCompanyController.java
  24. 39 0
      fs-admin/src/main/java/com/fs/task/FsStoreTask.java
  25. 106 0
      fs-admin/src/main/java/com/fs/user/controller/FsUserComplaintController.java
  26. 109 0
      fs-admin/src/main/java/com/fs/user/controller/FsUserComplaintMsgController.java
  27. 14 0
      fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java
  28. 1 1
      fs-admin/src/main/resources/application.yml
  29. 18 40
      fs-common-api/src/main/java/com/fs/framework/config/MyBatisConfig.java
  30. 19 0
      fs-common-api/src/main/java/com/fs/framework/config/RedisConfig.java
  31. 2 2
      fs-common-api/src/main/resources/application.yml
  32. 6 2
      fs-common/src/main/java/com/fs/common/core/domain/model/LoginUser.java
  33. 9 5
      fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java
  34. 7 2
      fs-common/src/main/java/com/fs/common/enums/BusinessType.java
  35. 21 0
      fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java
  36. 1 1
      fs-company/src/main/java/com/fs/company/controller/company/CompanyController.java
  37. 3 2
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java
  38. 3 2
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  39. 2 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  40. 11 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java
  41. 3 1
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopLogsController.java
  42. 80 4
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserVoiceLogController.java
  43. 2 2
      fs-company/src/main/java/com/fs/company/controller/qw/SopUserLogsController.java
  44. 18 0
      fs-company/src/main/java/com/fs/framework/config/RedisConfig.java
  45. 2 2
      fs-company/src/main/resources/application.yml
  46. 1 14
      fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java
  47. 21 0
      fs-doctor-app/src/main/java/com/fs/framework/config/RedisConfig.java
  48. 25 2
      fs-framework/src/main/java/com/fs/framework/aspectj/LogAspect.java
  49. 21 0
      fs-framework/src/main/java/com/fs/framework/config/RedisConfig.java
  50. 7 1
      fs-framework/src/main/java/com/fs/framework/manager/factory/AsyncFactory.java
  51. 21 0
      fs-hospital/src/main/java/com/fs/framework/config/RedisConfig.java
  52. 26 6
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  53. 2 2
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  54. 22 0
      fs-ipad-task/src/main/java/com/fs/framework/config/RedisConfig.java
  55. 20 0
      fs-live-app/src/main/java/com/fs/framework/config/RedisConfig.java
  56. 38 37
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  57. 21 0
      fs-qw-api-msg/src/main/java/com/fs/framework/config/RedisConfig.java
  58. 21 0
      fs-qw-api/src/main/java/com/fs/framework/config/RedisConfig.java
  59. 20 0
      fs-qw-mq/src/main/java/com/fs/framework/config/RedisConfig.java
  60. 16 9
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  61. 21 0
      fs-qw-task/src/main/java/com/fs/framework/config/RedisConfig.java
  62. 19 0
      fs-qw-voice/src/main/java/com/fs/framework/config/RedisConfig.java
  63. 21 0
      fs-qwhook-msg/src/main/java/com/fs/framework/config/RedisConfig.java
  64. 36 44
      fs-qwhook-sop/src/main/java/com/fs/framework/config/MyBatisConfig.java
  65. 20 0
      fs-qwhook-sop/src/main/java/com/fs/framework/config/RedisConfig.java
  66. 21 0
      fs-qwhook/src/main/java/com/fs/framework/config/RedisConfig.java
  67. 20 0
      fs-repeat-api/src/main/java/com/fs/framework/config/RedisConfig.java
  68. 5 0
      fs-service/src/main/java/com/fs/company/constant/CompanyTrafficConstants.java
  69. 4 0
      fs-service/src/main/java/com/fs/company/domain/Company.java
  70. 41 0
      fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecord.java
  71. 45 0
      fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecordLog.java
  72. 12 6
      fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java
  73. 10 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordLogMapper.java
  74. 28 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordMapper.java
  75. 3 32
      fs-service/src/main/java/com/fs/company/param/CompanyParam.java
  76. 23 0
      fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordChargeParam.java
  77. 21 0
      fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordLogQueryParam.java
  78. 17 0
      fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordQueryParam.java
  79. 2 0
      fs-service/src/main/java/com/fs/company/service/ICompanyConfigService.java
  80. 1 1
      fs-service/src/main/java/com/fs/company/service/ICompanyService.java
  81. 16 0
      fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordLogService.java
  82. 25 0
      fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordService.java
  83. 38 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyConfigServiceImpl.java
  84. 2 2
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  85. 40 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordLogServiceImpl.java
  86. 272 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordServiceImpl.java
  87. 4 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  88. 4 0
      fs-service/src/main/java/com/fs/company/vo/CompanyVO.java
  89. 28 29
      fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java
  90. 10 9
      fs-service/src/main/java/com/fs/core/utils/OrderCodeUtils.java
  91. 5 0
      fs-service/src/main/java/com/fs/course/config/CourseConfig.java
  92. 4 0
      fs-service/src/main/java/com/fs/course/domain/FsCoursePlaySourceConfig.java
  93. 9 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java
  94. 35 5
      fs-service/src/main/java/com/fs/course/dto/FsOrderDeliveryNoteDTO.java
  95. 6 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java
  96. 5 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  97. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java
  98. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  99. 4 1
      fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigCreateParam.java
  100. 3 0
      fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java

+ 1 - 0
DirectoryV3.xml

@@ -5,4 +5,5 @@
      <tree path="/fs-admin/src/main/java/com/fs/company/controller" title="公司"/>
      <tree path="/fs-admin/src/main/java/com/fs/live/controller" title="直播"/>
      <tree path="/fs-admin/src/main/java/com/fs/qw" title="企微"/>
+     <tree path="/fs-ad-api" title="广告回传"/>
  </trees>

+ 19 - 0
fs-ad-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -90,7 +92,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 682 - 36
fs-admin/src/main/java/com/fs/api/controller/IndexStatisticsController.java

@@ -1,16 +1,32 @@
 package com.fs.api.controller;
 
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysDept;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.company.constant.CompanyTrafficConstants;
+import com.fs.company.domain.Company;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyTrafficRecordService;
+import com.fs.company.service.impl.CompanyTrafficRecordServiceImpl;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.hisStore.config.MedicalMallConfig;
 import com.fs.statis.StatisticsRedisConstant;
 import com.fs.statis.dto.*;
+import com.fs.statis.param.StatisticsDeptCompanyParam;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
+import com.fs.system.service.ISysDeptService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.util.*;
+import java.util.function.BinaryOperator;
+import java.util.stream.Collectors;
 
 import static com.fs.statis.StatisticsRedisConstant.*;
 
@@ -19,18 +35,31 @@ import static com.fs.statis.StatisticsRedisConstant.*;
  */
 @RestController
 @RequestMapping("/index/statistics")
+@Slf4j
 public class IndexStatisticsController {
     @Autowired
     private RedisCache redisCache;
 
     @Autowired
     private ISysConfigService sysConfigService;
+
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    private ISysDeptService deptService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    @Autowired
+    private MedicalMallConfig medicalMallConfig;
     /**
      * 分析概览
      */
     @PostMapping("/analysisPreview")
     public R analysisPreview(@RequestBody AnalysisPreviewQueryDTO param){
-        AnalysisPreviewDTO analysisPreviewDTO = null;
+        AnalysisPreviewDTO analysisPreviewDTO = new AnalysisPreviewDTO();
         Integer type = param.getType();
         Integer userType = param.getUserType();
 
@@ -41,7 +70,69 @@ public class IndexStatisticsController {
         if(userType == null) {
             userType = 0;
         }
-        analysisPreviewDTO = redisCache.getCacheObject(String.format("%s:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType));
+        if(medicalMallConfig.isStatics()|| (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            analysisPreviewDTO = redisCache.getCacheObject(String.format("%s:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType));
+        }else if(param.getCompanyId() != null){
+            analysisPreviewDTO = redisCache.getCacheObject(String.format("%s:%d:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType,param.getCompanyId()));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            for(Long companyId : companyIds){
+                AnalysisPreviewDTO clildDTO = redisCache.getCacheObject(String.format("%s:%d:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType,companyId));
+                if (clildDTO != null) {
+                    //将除了completedRate、correctRate和watchRate之外的数据相加,并计算completedRate、correctRate和watchRate
+                    analysisPreviewDTO.setWatchUserCount(
+                            analysisPreviewDTO.getWatchUserCount() == 0 ? 0 : analysisPreviewDTO.getWatchUserCount()
+                                +(clildDTO.getWatchUserCount() == 0 ? 0 : clildDTO.getWatchUserCount())
+                    );
+                    analysisPreviewDTO.setCompletedUserCount(
+                            analysisPreviewDTO.getCompletedUserCount() == 0 ? 0 : analysisPreviewDTO.getCompletedUserCount()
+                                +(clildDTO.getCompletedUserCount() == 0 ? 0 : clildDTO.getCompletedUserCount())
+                    );
+                    analysisPreviewDTO.setCompletedCount(
+                            analysisPreviewDTO.getCompletedCount() == 0 ? 0 : analysisPreviewDTO.getCompletedCount()
+                                +(clildDTO.getCompletedCount() == 0 ? 0 : clildDTO.getCompletedCount())
+                    );
+                    analysisPreviewDTO.setWatchCount(
+                            analysisPreviewDTO.getWatchCount() == 0 ? 0 : analysisPreviewDTO.getWatchCount()
+                                +(clildDTO.getWatchCount() == 0 ? 0 : clildDTO.getWatchCount())
+                    );
+                    analysisPreviewDTO.setAnswerMemberCount(
+                            analysisPreviewDTO.getAnswerMemberCount() == 0 ? 0 : analysisPreviewDTO.getAnswerMemberCount()
+                                +(clildDTO.getAnswerMemberCount() == 0 ? 0 : clildDTO.getAnswerMemberCount())
+                    );
+                    analysisPreviewDTO.setCorrectUserCount(
+                            analysisPreviewDTO.getCorrectUserCount() == 0 ? 0 : analysisPreviewDTO.getCorrectUserCount()
+                                +(clildDTO.getCorrectUserCount() == 0 ? 0 : clildDTO.getCorrectUserCount())
+                    );
+                    analysisPreviewDTO.setRewardCount(
+                            analysisPreviewDTO.getRewardCount() == 0 ? 0 : analysisPreviewDTO.getRewardCount()
+                                +(clildDTO.getRewardCount() == 0 ? 0 : clildDTO.getRewardCount())
+                    );
+                    analysisPreviewDTO.setRewardMoney(
+                            analysisPreviewDTO.getRewardMoney() == null ? BigDecimal.ZERO : analysisPreviewDTO.getRewardMoney()
+                                .add(clildDTO.getRewardMoney() == null ? BigDecimal.ZERO : clildDTO.getRewardMoney())
+                    );
+                }
+            }
+            if(analysisPreviewDTO.getAnswerMemberCount() != 0){
+                analysisPreviewDTO.setCorrectRate(
+                        analysisPreviewDTO.getCorrectUserCount() == 0 ? "0" : String.format("%.2f", analysisPreviewDTO.getCorrectUserCount() * 100.0 / analysisPreviewDTO.getAnswerMemberCount())
+                );
+            }
+            if(analysisPreviewDTO.getWatchUserCount() != 0){
+                analysisPreviewDTO.setCompletedRate(
+                        analysisPreviewDTO.getCompletedUserCount() == 0 ? "0" : String.format("%.2f", analysisPreviewDTO.getCompletedUserCount() * 100.0 / analysisPreviewDTO.getWatchUserCount())
+                );
+            }
+            if(analysisPreviewDTO.getWatchCount() != 0){
+                analysisPreviewDTO.setWatchRate(
+                        analysisPreviewDTO.getCompletedCount() == 0 ? "0" : String.format("%.2f", analysisPreviewDTO.getCompletedCount() * 100.0 / analysisPreviewDTO.getWatchCount())
+                );
+            }
+
+        }
 
         return R.ok().put("data",analysisPreviewDTO);
     }
@@ -51,8 +142,32 @@ public class IndexStatisticsController {
      * 消费余额
      */
     @GetMapping("/rechargeComsumption")
-    public R rechargeComsumption(){
-        ConsumptionBalanceDataDTO consumptionBalanceDataDTO = redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_BALANCE);
+    public R rechargeComsumption(StatisticsDeptCompanyParam param){
+        ConsumptionBalanceDataDTO consumptionBalanceDataDTO = new ConsumptionBalanceDataDTO();
+        if(medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)) {
+            consumptionBalanceDataDTO = redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_BALANCE);
+        }else if(param.getCompanyId() != null){
+            consumptionBalanceDataDTO = redisCache.getCacheObject(String.format("%s:%d",DATA_OVERVIEW_DEALER_BALANCE,param.getCompanyId()));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            for(Long companyId : companyIds){
+                ConsumptionBalanceDataDTO clildDTO = redisCache.getCacheObject(String.format("%s:%d",DATA_OVERVIEW_DEALER_BALANCE,companyId));
+                consumptionBalanceDataDTO.setBalance(
+                    (consumptionBalanceDataDTO.getBalance() == null ? BigDecimal.ZERO : consumptionBalanceDataDTO.getBalance())
+                    .add(clildDTO.getBalance() == null ? BigDecimal.ZERO : clildDTO.getBalance())
+                );
+                consumptionBalanceDataDTO.setTodayComsumption(
+                    (consumptionBalanceDataDTO.getTodayComsumption() == null ? BigDecimal.ZERO : consumptionBalanceDataDTO.getTodayComsumption())
+                    .add(clildDTO.getTodayComsumption() == null ? BigDecimal.ZERO : clildDTO.getTodayComsumption())
+                );
+                consumptionBalanceDataDTO.setYesterdayComsumption(
+                    (consumptionBalanceDataDTO.getYesterdayComsumption() == null ? BigDecimal.ZERO : consumptionBalanceDataDTO.getYesterdayComsumption())
+                    .add(clildDTO.getYesterdayComsumption() == null ? BigDecimal.ZERO : clildDTO.getYesterdayComsumption())
+                );
+            }
+        }
 
         return R.ok().put("data", consumptionBalanceDataDTO);
     }
@@ -62,15 +177,58 @@ public class IndexStatisticsController {
      * @return
      */
     @GetMapping("/trafficLog")
-    public R getTrafficLog(){
-        TrafficLogDTO trafficLogDTO = redisCache.getCacheObject(DATA_OVERVIEW_TRAFFIC_LOG);
-        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("redPacket.Traffic.config");
-        if(trafficLogDTO == null || sysConfig == null) {
-            return null;
+    public R getTrafficLog(StatisticsDeptCompanyParam  param){
+        TrafficLogDTO result = new TrafficLogDTO();
+        if(!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)) {
+            result = redisCache.getCacheObject(DATA_OVERVIEW_TRAFFIC_LOG);
+            if (!medicalMallConfig.isStatics()) {
+                SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("redPacket.Traffic.config");
+                if(result == null || sysConfig == null) {
+                    return null;
+                }
+                String configValue = sysConfig.getConfigValue();
+                result.setTraffic(configValue);
+            } else {
+                //所有部门流量之和
+                SysDept dept = new SysDept();
+                dept.setParentId(1L);
+                Long[] deptIds = deptService.selectDeptList(dept).stream().map(SysDept::getDeptId).toArray(Long[]::new);
+                long trafficTemp = 0L;
+                for(Long deptId : deptIds){
+                    Object clildTraffic = redisCache.getCacheObject(CompanyTrafficConstants.CACHE_KEY+":"+deptId);
+                    if(clildTraffic != null){
+                        trafficTemp += Long.parseLong(clildTraffic.toString());
+                    }
+                }
+                result.setTraffic(String.format("%.2f",trafficTemp * 1024.0));
+            }
+        }else if(param.getCompanyId() != null){
+            Company company = companyService.selectCompanyById(param.getCompanyId());
+            getTrafficLogResult(result,CompanyTrafficConstants.CACHE_KEY+":"+company.getDeptId()+":"+param.getCompanyId());
+        }else{
+            getTrafficLogResult(result,CompanyTrafficConstants.CACHE_KEY+":"+param.getDeptId());
         }
-        String configValue = sysConfig.getConfigValue();
-        trafficLogDTO.setTraffic(configValue);
-        return R.ok().put("data",trafficLogDTO);
+        return R.ok().put("data",result);
+    }
+
+    private void getTrafficLogResult(TrafficLogDTO result, String key){
+        //昨天
+        LocalDate yesterday = LocalDate.now().minusDays(1);
+        Object yesterdayCount = redisCache.getCacheObject(key+":"+yesterday);
+        Object todayCount = redisCache.getCacheObject(key+":"+LocalDate.now());
+        Object thisMonthCount = redisCache.getCacheObject(key+":"+YearMonth.now());
+        Object traffic = redisCache.getCacheObject(key);
+        result.setYesterday(parseRedisNumberValueToLong(yesterdayCount));
+        //今天
+        result.setToday(parseRedisNumberValueToLong(todayCount));
+        //本月
+        result.setThisMonth(parseRedisNumberValueToLong(thisMonthCount));
+        //剩余
+        result.setTraffic(traffic == null?"0":traffic.toString());
+    }
+
+    private Long parseRedisNumberValueToLong(Object value){
+        return value == null ? 0 : Long.parseLong(value.toString());
     }
 
     /**
@@ -87,9 +245,73 @@ public class IndexStatisticsController {
         if(userType == null){
             userType = 0;
         }
-        String key = String.format("%s:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType);
-        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(key);
-        return R.ok().put("data", deaMemberTopTenDTOS);
+        List<WatchEndPlayTrendDTO> watchEndPlayTrendDTOS;
+        // 参考watchCourseTopTen方法的处理逻辑
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            String key = String.format("%s:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType);
+            watchEndPlayTrendDTOS = redisCache.getCacheObject(key);
+        }else if(param.getCompanyId() != null){
+            String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,param.getCompanyId());
+            watchEndPlayTrendDTOS = redisCache.getCacheObject(key);
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<WatchEndPlayTrendDTO> tempDTOS = new ArrayList<>();
+            for(Long companyId : companyIds){
+                String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,companyId);
+                List<WatchEndPlayTrendDTO> companyData = redisCache.getCacheObject(key);
+                if (companyData != null) {
+                    tempDTOS.addAll(companyData);
+                }
+            }
+            // 根据startDate 和 x 分组,合并watchUserCount和completedUserCount 限制最多返回10条记录
+            watchEndPlayTrendDTOS = tempDTOS.stream()
+                    .collect(Collectors.groupingBy(
+                            dto -> dto.getStartDate() + ":" + dto.getX(),  // 根据startDate和x分组
+                            Collectors.reducing(new WatchEndPlayTrendDTO(), (dto1, dto2) -> {
+                                // 合并watchUserCount和completedUserCount
+                                WatchEndPlayTrendDTO result = new WatchEndPlayTrendDTO();
+                                // 复制分组标识字段
+                                if (dto2 != null && dto2.getStartDate() != null) {
+                                    result.setStartDate(dto2.getStartDate());
+                                } else if (dto1 != null) {
+                                    result.setStartDate(dto1.getStartDate());
+                                }
+
+                                if (dto2 != null && dto2.getX() != null) {
+                                    result.setX(dto2.getX());
+                                } else if (dto1 != null) {
+                                    result.setX(dto1.getX());
+                                }
+
+                                // 合并数值字段
+                                result.setWatchUserCount(
+                                        (dto1 == null || dto1.getWatchUserCount() == null ? 0 : dto1.getWatchUserCount()) +
+                                                (dto2 == null || dto2.getWatchUserCount() == null ? 0 : dto2.getWatchUserCount())
+                                );
+
+                                result.setCompletedUserCount(
+                                        (dto1 == null || dto1.getCompletedUserCount() == null ? 0 : dto1.getCompletedUserCount()) +
+                                                (dto2 == null || dto2.getCompletedUserCount() == null ? 0 : dto2.getCompletedUserCount())
+                                );
+
+                                return result;
+                            })
+                    ))
+                    .values()
+                    .stream()
+                    .filter(Objects::nonNull)  // 过滤掉null值
+                    .limit(10)
+                    .sorted(Comparator.comparing(WatchEndPlayTrendDTO::getX))
+                    .collect(Collectors.toList());
+        }
+
+        if(watchEndPlayTrendDTOS == null){
+            watchEndPlayTrendDTOS = new ArrayList<>();
+        }
+
+        return R.ok().put("data", watchEndPlayTrendDTOS);
     }
 
     /**
@@ -107,8 +329,49 @@ public class IndexStatisticsController {
         if(userType == null){
             userType = 0;
         }
+        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = new ArrayList<>();
+        // 参考deaMemberTopTen方法处理逻辑
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            String key = String.format("%s:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType,userType);
+            deaMemberTopTenDTOS =  redisCache.getCacheObject(key);
+        }else if(param.getCompanyId() != null){
+            String key = String.format("%s:%d:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType,userType,param.getCompanyId());
+            deaMemberTopTenDTOS = redisCache.getCacheObject(key);
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<DeaMemberTopTenDTO> tempDTOS = new ArrayList<>();
+            for(Long companyId : companyIds){
+                String key = String.format("%s:%d:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType,userType,companyId);
+                List<DeaMemberTopTenDTO> companyData = redisCache.getCacheObject(key);
+                if (companyData != null) {
+                    tempDTOS.addAll(companyData);
+                }
+            }
+            // companyId 和 companyName 分组,合并watchUserCount 限制最多返回10条记录
+            deaMemberTopTenDTOS = tempDTOS.stream()
+                    .collect(Collectors.groupingBy(
+                            dto -> dto.getCompanyId() + ":" + dto.getCompanyName(),  // 根据companyId和companyName分组
+                            Collectors.reducing(new DeaMemberTopTenDTO(), (dto1, dto2) -> {
+                                DeaMemberTopTenDTO result = new DeaMemberTopTenDTO();
+                                result.setCompanyId(dto1.getCompanyId());
+                                result.setCompanyName(dto1.getCompanyName());
+                                result.setWatchUserCount(
+                                        (dto1.getWatchUserCount() == null ? 0 : dto1.getWatchUserCount()) +
+                                                (dto2 == null || dto2.getWatchUserCount() == null ? 0 : dto2.getWatchUserCount())
+                                );
+                                return result;
+                            })
+                    ))
+                    .values()
+                    .stream()
+                    .filter(Objects::nonNull)  // 过滤掉null值
+                    .limit(10)
+                    .sorted(Comparator.comparing(DeaMemberTopTenDTO::getWatchUserCount, Comparator.nullsLast(Comparator.reverseOrder())))
+                    .collect(Collectors.toList());
+        }
 
-        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType,userType));
         if(deaMemberTopTenDTOS == null){
             deaMemberTopTenDTOS = new ArrayList<>();
         }
@@ -123,8 +386,50 @@ public class IndexStatisticsController {
         Integer type = param.getType();
         Integer dataType = param.getDataType();
         Integer userType = param.getUserType();
+        List<RewardMoneyTopTenDTO> rewardMoneyTopTenDTOS = new ArrayList<>();
+        // 参考rewardMoneyTopTen方法处理逻辑
+        if(!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            String key = String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType);
+            rewardMoneyTopTenDTOS = redisCache.getCacheObject(key);
+        }else if(param.getCompanyId() != null){
+            String key = String.format("%s:%d:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType,param.getCompanyId());
+            rewardMoneyTopTenDTOS = redisCache.getCacheObject(key);
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<RewardMoneyTopTenDTO> tempDTOS = new ArrayList<>();
+            for(Long companyId : companyIds){
+                String key = String.format("%s:%d:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType,companyId);
+                List<RewardMoneyTopTenDTO> companyData = redisCache.getCacheObject(key);
+                if (companyData != null) {
+                    tempDTOS.addAll(companyData);
+                }
+            }
+            rewardMoneyTopTenDTOS = tempDTOS.stream()
+                    .collect(Collectors.groupingBy(
+                            dto -> dto.getCompanyId() + ":" + dto.getCompanyName(),  // 根据companyId和companyName分组
+                            Collectors.reducing(new RewardMoneyTopTenDTO(), (dto1, dto2) -> {
+                                RewardMoneyTopTenDTO result = new RewardMoneyTopTenDTO();
+                                result.setCompanyId(dto1.getCompanyId());
+                                result.setCompanyName(dto1.getCompanyName());
+                                result.setRewardMoney(
+                                        (dto1.getRewardMoney() == null ? BigDecimal.ZERO : dto1.getRewardMoney())
+                                        .add(
+                                                (dto2 == null || dto2.getRewardMoney() == null ? BigDecimal.ZERO : dto2.getRewardMoney())
+                                        )
+                                );
+                                return result;
+                            })
+                    ))
+                    .values()
+                    .stream()
+                    .filter(Objects::nonNull)  // 过滤掉null值
+                    .limit(10)
+                    .sorted(Comparator.comparing(RewardMoneyTopTenDTO::getRewardMoney, Comparator.nullsLast(Comparator.reverseOrder())))
+                    .collect(Collectors.toList());
+        }
 
-        List<RewardMoneyTopTenDTO> rewardMoneyTopTenDTOS = redisCache.getCacheObject( String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType));
         return R.ok().put("data", rewardMoneyTopTenDTOS);
     }
 
@@ -135,7 +440,47 @@ public class IndexStatisticsController {
     public R rewardMoneyTrend(@RequestBody AnalysisPreviewQueryDTO param){
         Integer type = param.getType();
         Integer userType = param.getUserType();
-        List<RewardMoneyTrendDTO> rewardMoneyTrendDTOS = redisCache.getCacheObject( String.format("%s:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType));
+        List<RewardMoneyTrendDTO> rewardMoneyTrendDTOS = new ArrayList<>();
+        // 参考rewardMoneyTrend方法处理逻辑
+        if(!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            String key = String.format("%s:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType);
+            rewardMoneyTrendDTOS = redisCache.getCacheObject(key);
+        }else if(param.getCompanyId() != null){
+            String key = String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType,param.getCompanyId());
+            rewardMoneyTrendDTOS = redisCache.getCacheObject(key);
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<RewardMoneyTrendDTO> tempDTOS = new ArrayList<>();
+            for(Long companyId : companyIds){
+                String key = String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType,companyId);
+                List<RewardMoneyTrendDTO> companyData = redisCache.getCacheObject(key);
+                if (companyData != null) {
+                    tempDTOS.addAll(companyData);
+                }
+            }
+            //根据startDate和x分组,合并rewardMoney,根据x排序,限制10
+            rewardMoneyTrendDTOS = tempDTOS.stream()
+                    .collect(Collectors.groupingBy(dto -> dto.getStartDate() + ":" + dto.getX(),
+                            Collectors.reducing(new RewardMoneyTrendDTO(), (dto1, dto2) -> {
+                        RewardMoneyTrendDTO result = new RewardMoneyTrendDTO();
+                        result.setStartDate(dto1.getStartDate());
+                        result.setX(dto1.getX());
+                        result.setRewardMoney(
+                                (dto1.getRewardMoney() == null ? BigDecimal.ZERO : dto1.getRewardMoney())
+                                .add(
+                                        (dto2 == null || dto2.getRewardMoney() == null ? BigDecimal.ZERO : dto2.getRewardMoney())
+                                )
+                        );
+                        return result;
+                    })))
+                    .values()
+                    .stream()
+                    .sorted(Comparator.comparing(RewardMoneyTrendDTO::getX))
+                    .limit(10)
+                    .collect(Collectors.toList());
+        }
         return R.ok().put("data", rewardMoneyTrendDTOS);
     }
 
@@ -148,29 +493,196 @@ public class IndexStatisticsController {
         String sort = param.getSort();
         Integer statisticalType = param.getStatisticalType();
         Integer userType = param.getUserType();
+        List<CourseStatsDTO> courseStatsDTOS;
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            courseStatsDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%s", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort));
+        }else if(param.getCompanyId() != null){
+            courseStatsDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%s:%d", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort,param.getCompanyId()));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<CourseStatsDTO> tempDTOS = new ArrayList<>();
+            for(Long companyId : companyIds){
+                List<CourseStatsDTO> companyDTO = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%s:%d", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort,companyId));
+                if (companyDTO != null) {
+                    tempDTOS.addAll(companyDTO);
+                }
+            }
+            // courseId和courseName分组,合并watchUserCount、completedUserCount、answerUserCount、correctUserCount 限制最多返回10条记录
+            courseStatsDTOS = tempDTOS.stream()
+                    .collect(Collectors.groupingBy(dto -> dto.getCourseId() + ":" + dto.getCourseName()))
+                    .values()
+                    .stream()
+                    .map(group -> {
+                        if (group.isEmpty()) {
+                            return null;
+                        }
+
+                        // 取第一个作为基础对象
+                        CourseStatsDTO result = new CourseStatsDTO();
+                        CourseStatsDTO first = group.get(0);
+                        result.setCourseId(first.getCourseId());
+                        result.setCourseName(first.getCourseName());
+
+                        // 合并所有数值字段
+                        int watchUserCount = 0;
+                        int completedUserCount = 0;
+                        int answerUserCount = 0;
+                        int correctUserCount = 0;
+
+                        for (CourseStatsDTO dto : group) {
+                            watchUserCount += (dto.getWatchUserCount() == null ? 0 : dto.getWatchUserCount());
+                            completedUserCount += (dto.getCompletedUserCount() == null ? 0 : dto.getCompletedUserCount());
+                            answerUserCount += (dto.getAnswerUserCount() == null ? 0 : dto.getAnswerUserCount());
+                            correctUserCount += (dto.getCorrectUserCount() == null ? 0 : dto.getCorrectUserCount());
+                        }
+
+                        result.setWatchUserCount(watchUserCount);
+                        result.setCompletedUserCount(completedUserCount);
+                        result.setAnswerUserCount(answerUserCount);
+                        result.setCorrectUserCount(correctUserCount);
+
+                        return result;
+                    })
+                    .filter(Objects::nonNull)
+                    .sorted(Comparator.comparing(CourseStatsDTO::getWatchUserCount, Comparator.nullsLast(Comparator.reverseOrder())))
+                    .limit(10)
+                    .collect(Collectors.toList());
+
+        }
 
-        List<CourseStatsDTO> courseStatsDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%s", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort));
         return R.ok().put("data", courseStatsDTOS);
     }
 
+    private List<CourseStatsDTO> groupAndSumCourseStatsWithStream(List<CourseStatsDTO> courseStatsList) {
+        // 定义合并逻辑
+        BinaryOperator<CourseStatsDTO> mergeFunction = (s1, s2) -> {
+            s1.setWatchUserCount(
+                    (s1.getWatchUserCount() == null ? 0 : s1.getWatchUserCount()) +
+                            (s2.getWatchUserCount() == null ? 0 : s2.getWatchUserCount())
+            );
+
+            s1.setCompletedUserCount(
+                    (s1.getCompletedUserCount() == null ? 0 : s1.getCompletedUserCount()) +
+                            (s2.getCompletedUserCount() == null ? 0 : s2.getCompletedUserCount())
+            );
+
+            s1.setAnswerUserCount(
+                    (s1.getAnswerUserCount() == null ? 0 : s1.getAnswerUserCount()) +
+                            (s2.getAnswerUserCount() == null ? 0 : s2.getAnswerUserCount())
+            );
+
+            s1.setCorrectUserCount(
+                    (s1.getCorrectUserCount() == null ? 0 : s1.getCorrectUserCount()) +
+                            (s2.getCorrectUserCount() == null ? 0 : s2.getCorrectUserCount())
+            );
+
+            return s1;
+        };
+
+        // 分组、合并并排序、限制10条
+        return courseStatsList.stream()
+                .collect(Collectors.toMap(
+                        CourseStatsDTO::getCourseId,
+                        dto -> {
+                            CourseStatsDTO copy = new CourseStatsDTO();
+                            copy.setCourseId(dto.getCourseId());
+                            copy.setCourseName(dto.getCourseName());
+                            copy.setWatchUserCount(dto.getWatchUserCount() == null ? 0 : dto.getWatchUserCount());
+                            copy.setCompletedUserCount(dto.getCompletedUserCount() == null ? 0 : dto.getCompletedUserCount());
+                            copy.setAnswerUserCount(dto.getAnswerUserCount() == null ? 0 : dto.getAnswerUserCount());
+                            copy.setCorrectUserCount(dto.getCorrectUserCount() == null ? 0 : dto.getCorrectUserCount());
+                            return copy;
+                        },
+                        mergeFunction,
+                        LinkedHashMap::new
+                ))
+                .values()
+                .stream()
+                .sorted(Comparator.comparing(CourseStatsDTO::getWatchUserCount, Comparator.nullsLast(Comparator.reverseOrder())))
+                .limit(10)
+                .collect(Collectors.toList());
+    }
+
     /**
      * 数据概览
      */
     @GetMapping("/dealerAggregated")
-    public R dealerAggregated(){
-        DealerAggregatedDTO dealerAggregatedDTO = redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_AGGREGATED);
+    public R dealerAggregated(StatisticsDeptCompanyParam param){
+        DealerAggregatedDTO result = new DealerAggregatedDTO();
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)) {
+            result = redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_AGGREGATED);
+        }else if (param.getCompanyId() != null) {
+            result = redisCache.getCacheObject(String.format("%s:%d", DATA_OVERVIEW_DEALER_AGGREGATED, param.getCompanyId()));
+        //没选中销售公司,部门不为总公司
+        }else{
+            //Long padMaxNum = deptLimiteService.selectById(param.getDeptId()).getMaxPadNum();
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            for(Long companyId : companyIds) {
+                DealerAggregatedDTO dealerAggregatedDTO = redisCache.getCacheObject(String.format("%s:%d", DATA_OVERVIEW_DEALER_AGGREGATED, companyId));
+                // 添加空值检查
+                if (dealerAggregatedDTO != null) {
+                    result.setDealderCount((result.getDealderCount() == null ? 0 : result.getDealderCount())
+                            + (dealerAggregatedDTO.getDealderCount() == null ? 0 : dealerAggregatedDTO.getDealderCount()));
+                    result.setGroupMgrCount((result.getGroupMgrCount() == null ? 0 : result.getGroupMgrCount())
+                            + (dealerAggregatedDTO.getGroupMgrCount() == null ? 0 : dealerAggregatedDTO.getGroupMgrCount()));
+                    result.setMemberCount((result.getMemberCount() == null ? 0 : result.getMemberCount())
+                            + (dealerAggregatedDTO.getMemberCount() == null ? 0 : dealerAggregatedDTO.getMemberCount()));
+                    result.setNormalNum((result.getNormalNum() == null ? 0 : result.getNormalNum())
+                            + (dealerAggregatedDTO.getNormalNum() == null ? 0 : dealerAggregatedDTO.getNormalNum()));
+                    result.setBlackNum((result.getBlackNum() == null ? 0 : result.getBlackNum())
+                            + (dealerAggregatedDTO.getBlackNum() == null ? 0 : dealerAggregatedDTO.getBlackNum()));
+                    result.setQwMemberNum((result.getQwMemberNum() == null ? 0 : result.getQwMemberNum())
+                            + (dealerAggregatedDTO.getQwMemberNum() == null ? 0 : dealerAggregatedDTO.getQwMemberNum()));
+                    result.setTodayIncreaseUserNum((result.getTodayIncreaseUserNum() == null ? 0 : result.getTodayIncreaseUserNum())
+                            + (dealerAggregatedDTO.getTodayIncreaseUserNum() == null ? 0 : dealerAggregatedDTO.getTodayIncreaseUserNum()));
+                    result.setOrderTotalNum((result.getOrderTotalNum() == null ? 0 : result.getOrderTotalNum())
+                            + (dealerAggregatedDTO.getOrderTotalNum() == null ? 0 : dealerAggregatedDTO.getOrderTotalNum()));
+                    result.setTodayOrderNum((result.getTodayOrderNum() == null ? 0 : result.getTodayOrderNum())
+                            + (dealerAggregatedDTO.getTodayOrderNum() == null ? 0 : dealerAggregatedDTO.getTodayOrderNum()));
+                    result.setRecvTodayNum((result.getRecvTodayNum() == null ? 0 : result.getRecvTodayNum())
+                            + (dealerAggregatedDTO.getRecvTodayNum() == null ? 0 : dealerAggregatedDTO.getRecvTodayNum()));
+                    result.setRecvTotalNum((result.getRecvTotalNum() == null ? 0 : result.getRecvTotalNum())
+                            + (dealerAggregatedDTO.getRecvTotalNum() == null ? 0 : dealerAggregatedDTO.getRecvTotalNum()));
+                    result.setGoodsTotalNum((result.getGoodsTotalNum() == null ? 0 : result.getGoodsTotalNum())
+                            + (dealerAggregatedDTO.getGoodsTotalNum() == null ? 0 : dealerAggregatedDTO.getGoodsTotalNum()));
+                    result.setTodayGoodsNum((result.getTodayGoodsNum() == null ? 0 : result.getTodayGoodsNum())
+                            + (dealerAggregatedDTO.getTodayGoodsNum() == null ? 0 : dealerAggregatedDTO.getTodayGoodsNum()));
+                    result.setPadUsedNum((result.getPadUsedNum() == null ? 0 : result.getPadUsedNum())
+                            + (dealerAggregatedDTO.getPadUsedNum() == null ? 0 : dealerAggregatedDTO.getPadUsedNum()));
+                }
+            }
+        }
 
-        return R.ok().put("data",dealerAggregatedDTO);
+        return R.ok().put("data",result);
     }
 
+
     /**
      * 短信余额
      */
     @GetMapping("/smsBalance")
-    public R smsBalance(){
-        Long smsBalance = redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_SMS_BALANCE);
-
-        return R.ok().put("data", smsBalance);
+    public R smsBalance(StatisticsDeptCompanyParam param){
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            return R.ok().put("data", redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_SMS_BALANCE));
+        }else if(param.getCompanyId() != null){
+            return R.ok().put("data", redisCache.getCacheObject(String.format("%s:%d", DATA_OVERVIEW_DEALER_SMS_BALANCE, param.getCompanyId())));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            long smsBalance = 0L;
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            for(Long companyId : companyIds) {
+                Long smsBalanceCompany = redisCache.getCacheObject(String.format("%s:%d", DATA_OVERVIEW_DEALER_SMS_BALANCE, companyId));
+                if (smsBalanceCompany != null) {
+                    smsBalance += smsBalanceCompany;
+                }
+            }
+            return R.ok().put("data", smsBalance);
+        }
     }
 
 
@@ -178,8 +690,33 @@ public class IndexStatisticsController {
      * 授权信息
      */
     @GetMapping("/authorizationInfo")
-    public R authorizationInfo(){
-        AuthorizationInfoDTO authorizationInfoDTO = redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_AUTHORIZATION_INFO);
+    public R authorizationInfo(StatisticsDeptCompanyParam  param){
+        AuthorizationInfoDTO authorizationInfoDTO = new AuthorizationInfoDTO();
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            return R.ok().put("data", redisCache.getCacheObject(StatisticsRedisConstant.DATA_OVERVIEW_DEALER_AUTHORIZATION_INFO));
+        }else if(param.getCompanyId() != null){
+            return R.ok().put("data", redisCache.getCacheObject(String.format("%s:%d", DATA_OVERVIEW_DEALER_AUTHORIZATION_INFO, param.getCompanyId())));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            AuthorizationInfoDTO authorizationInfoDTO1 = new AuthorizationInfoDTO();
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            for(Long companyId : companyIds) {
+                AuthorizationInfoDTO companyDTO = redisCache.getCacheObject(String.format("%s:%d", DATA_OVERVIEW_DEALER_AUTHORIZATION_INFO, companyId));
+                log.info("授权信息:{}", authorizationInfoDTO);
+                if (companyDTO != null) {
+                    authorizationInfoDTO1.setTodayWatchUserCount(
+                            (authorizationInfoDTO1.getTodayWatchUserCount() == null ? 0 : authorizationInfoDTO1.getTodayWatchUserCount()) +
+                                    (companyDTO.getTodayWatchUserCount() == null ? 0 : companyDTO.getTodayWatchUserCount())
+                    );
+
+                    authorizationInfoDTO1.setVersionLimit(
+                            (authorizationInfoDTO1.getVersionLimit() == null ? 0 : authorizationInfoDTO1.getVersionLimit()) +
+                                    (companyDTO.getVersionLimit() == null ? 0 : companyDTO.getVersionLimit())
+                    );
+                }
+            }
+        }
 
         return R.ok().put("data", authorizationInfoDTO);
     }
@@ -190,9 +727,29 @@ public class IndexStatisticsController {
      * @return
      */
     @GetMapping("/thisMonthOrderCount")
-    public R thisMonthOrderCount(){
-        R result = redisCache.getCacheObject(StatisticsRedisConstant.THIS_MONTH_ORDER_COUNT);
-        return result;
+    public R thisMonthOrderCount(StatisticsDeptCompanyParam  param){
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            return redisCache.getCacheObject(StatisticsRedisConstant.THIS_MONTH_ORDER_COUNT);
+        }else if(param.getCompanyId() != null){
+            return redisCache.getCacheObject(String.format("%s:%d", THIS_MONTH_ORDER_COUNT, param.getCompanyId()));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<String> datesList = new ArrayList<>();
+            List<Integer> orderCountList = new ArrayList<>();
+            List<Integer> payPriceList = new ArrayList<>();
+            for(Long companyId : companyIds) {
+                R result = redisCache.getCacheObject(String.format("%s:%d", THIS_MONTH_ORDER_COUNT, companyId));
+                Object datas = result.get("datas");
+                Object orderCount = result.get("orderCount");
+                Object payPrice = result.get("payPrice");
+                if(datas != null){
+                    mergeDataListsForInteger(datesList, orderCountList, payPriceList, (List<String>)datas, (List<Integer>)orderCount, (List<Integer>)payPrice);
+                }
+            }
+            return R.ok().put("dates", datesList).put("orderCount", orderCountList).put("payPrice", payPriceList);
+        }
     }
 
     /**
@@ -201,8 +758,97 @@ public class IndexStatisticsController {
      */
 
     @GetMapping("/thisMonthRecvCount")
-    public R thisMonthRecvCount(){
-        R result = redisCache.getCacheObject(StatisticsRedisConstant.THIS_MONTH_RECV_COUNT);
-        return result;
+    public R thisMonthRecvCount(StatisticsDeptCompanyParam  param){
+        if (!medicalMallConfig.isStatics() || (param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+            return redisCache.getCacheObject(StatisticsRedisConstant.THIS_MONTH_RECV_COUNT);
+        }else if(param.getCompanyId() != null){
+            return redisCache.getCacheObject(String.format("%s:%d", THIS_MONTH_RECV_COUNT, param.getCompanyId()));
+        }else{
+            Company company = new Company();
+            company.setDeptId(param.getDeptId());
+            Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+            List<String> datesList = new ArrayList<>();
+            List<Integer> orderCountList = new ArrayList<>();
+            List<Float> payMoneyList = new ArrayList<>();
+            for(Long companyId : companyIds) {
+               R result = redisCache.getCacheObject(String.format("%s:%d", THIS_MONTH_RECV_COUNT, companyId));
+               Object datas = result.get("datas");
+               Object orderCount = result.get("orderCount");
+               Object payMoney = result.get("payMoney");
+               if(datas != null){
+                   mergeDataLists(datesList, orderCountList, payMoneyList, (List<String>)datas, (List<Integer>)orderCount, (List<Float>)payMoney);
+               }
+            }
+            return R.ok().put("dates", datesList).put("orderCount", orderCountList).put("payMoney", payMoneyList);
+        }
+    }
+
+    private void mergeDataLists(List<String> datasList, List<Integer> orderCountList, List<Float> payMoneyList,
+                               List<String> datas, List<Integer> orderCount, List<Float> payMoney) {
+
+        // 遍历新数据
+        for (int i = 0; i < datas.size(); i++) {
+            String newData = datas.get(i);
+            Integer newOrderCount = orderCount.get(i);
+            Float newPayMoney = payMoney.get(i);
+
+            // 查找在现有列表中的位置
+            int existingIndex = datasList.indexOf(newData);
+
+            if (existingIndex != -1) {
+                // 如果存在,将orderCount和payMoney相加
+                Integer existingOrderCount = orderCountList.get(existingIndex);
+                Float existingPayMoney = payMoneyList.get(existingIndex);
+
+                // 累加orderCount
+                orderCountList.set(existingIndex,
+                        (existingOrderCount == null ? 0 : existingOrderCount) +
+                                (newOrderCount == null ? 0 : newOrderCount));
+
+                // 累加payMoney
+                payMoneyList.set(existingIndex,
+                        (existingPayMoney == null ? 0.0f : existingPayMoney) +
+                                (newPayMoney == null ? 0.0f : newPayMoney));
+            } else {
+                // 如果不存在,直接添加新项
+                datasList.add(newData);
+                orderCountList.add(newOrderCount);
+                payMoneyList.add(newPayMoney);
+            }
+        }
+    }
+
+    private void mergeDataListsForInteger(List<String> datasList, List<Integer> orderCountList, List<Integer> payMoneyList,
+                                          List<String> datas, List<Integer> orderCount, List<Integer> payMoney) {
+        // 遍历新数据
+        for (int i = 0; i < datas.size(); i++) {
+            String newData = datas.get(i);
+            Integer newOrderCount = orderCount.get(i);
+            Integer newPayMoney = payMoney.get(i);
+
+            // 查找在现有列表中的位置
+            int existingIndex = datasList.indexOf(newData);
+
+            if (existingIndex != -1) {
+                // 如果存在,将orderCount和payMoney相加
+                Integer existingOrderCount = orderCountList.get(existingIndex);
+                Integer existingPayMoney = payMoneyList.get(existingIndex);
+
+                // 累加orderCount
+                orderCountList.set(existingIndex,
+                        (existingOrderCount == null ? 0 : existingOrderCount) +
+                                (newOrderCount == null ? 0 : newOrderCount));
+
+                // 累加payMoney
+                payMoneyList.set(existingIndex,
+                        (existingPayMoney == null ? 0 : existingPayMoney) +
+                                (newPayMoney == null ? 0 : newPayMoney));
+            } else {
+                // 如果不存在,直接添加新项
+                datasList.add(newData);
+                orderCountList.add(newOrderCount);
+                payMoneyList.add(newPayMoney);
+            }
+        }
     }
 }

+ 41 - 1
fs-admin/src/main/java/com/fs/company/controller/CompanyController.java

@@ -1,6 +1,7 @@
 package com.fs.company.controller;
 
 import cn.hutool.core.util.IdUtil;
+import cn.hutool.json.JSONUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
@@ -24,8 +25,10 @@ import com.fs.company.vo.CompanyCrmVO;
 import com.fs.company.vo.CompanyVO;
 import com.fs.company.vo.CompanyVoiceCallerListVO;
 import com.fs.core.utils.OrderCodeUtils;
+import com.fs.course.config.CourseConfig;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.vo.OptionsVO;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
@@ -55,6 +58,8 @@ public class CompanyController extends BaseController
     private ICompanyDeductService deductService;
     @Autowired
     private ICompanyVoiceCallerService callerService;
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询企业列表
      */
@@ -63,6 +68,12 @@ public class CompanyController extends BaseController
     public TableDataInfo list(CompanyParam param)
     {
         startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            param.setDeptId(loginUser.getDeptId());
+        }
         List<CompanyVO> list = companyService.selectCompanyVOList(param);
         return getDataTable(list);
     }
@@ -75,6 +86,12 @@ public class CompanyController extends BaseController
     @GetMapping("/export")
     public AjaxResult export(CompanyParam company)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            company.setDeptId(loginUser.getDeptId());
+        }
         List<CompanyVO> list = companyService.selectCompanyVOList(company);
         ExcelUtil<CompanyVO> util = new ExcelUtil<CompanyVO>(CompanyVO.class);
         return util.exportExcel(list, "company");
@@ -98,6 +115,10 @@ public class CompanyController extends BaseController
     @PostMapping
     public R add(@RequestBody Company company)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if(company.getDeptId() != null){
+            company.setDeptId(loginUser.getDeptId());
+        }
         company.setPassword(SecurityUtils.encryptPassword(company.getPassword()));
         company.setAppId(Md5Utils.hash(company.getUserName()));
         company.setAppKey(Md5Utils.hash(company.getPassword()));
@@ -154,6 +175,12 @@ public class CompanyController extends BaseController
     {
         Company map=new Company();
         map.setIsDel(0);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            map.setDeptId(loginUser.getDeptId());
+        }
         List<Company> list = companyService.selectCompanyList(map);
         return R.ok().put("data",list);
     }
@@ -164,6 +191,12 @@ public class CompanyController extends BaseController
     public TableDataInfo companyCrmDayCountList(CompanyParam param)
     {
         startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            param.setDeptId(loginUser.getDeptId());
+        }
         List<CompanyCrmVO> list = companyService.selectCompanyCrmDayCountList(param);
         return getDataTable(list);
     }
@@ -239,7 +272,14 @@ public class CompanyController extends BaseController
     @GetMapping("/allList")
     public TableDataInfo getHospital()
     {
-        List<OptionsVO> list = companyService.selectAllCompanyList();
+        Long deptId = null;
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            deptId = loginUser.getDeptId();
+        }
+        List<OptionsVO> list = companyService.selectAllCompanyList(deptId);
         return getDataTable(list);
     }
 }

+ 103 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficController.java

@@ -0,0 +1,103 @@
+package com.fs.company.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+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.ServletUtils;
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.param.CompanyTrafficRecordChargeParam;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+import com.fs.company.service.ICompanyTrafficRecordService;
+import com.fs.framework.web.service.TokenService;
+import com.fs.hisStore.config.MedicalMallConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+
+@RestController
+@RequestMapping("/company/traffic")
+public class CompanyTrafficController extends BaseController {
+
+    @Autowired
+    private ICompanyTrafficRecordService companyTrafficRecordService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private MedicalMallConfig medicalMallConfig;
+
+    /**
+     * 每天扣除流量余额
+     * */
+    @Scheduled(cron = "0 1 0 * * ?")
+    public void refreshTraffic(){
+        if(medicalMallConfig.isStatics())
+            companyTrafficRecordService.refreshTraffic();
+    }
+
+    /**
+     * 重启时重新计算并更新公司缓存
+     * */
+    @PostConstruct
+    public void init() {
+        if(medicalMallConfig.isStatics())
+            companyTrafficRecordService.init();
+    }
+
+    /** 充值流量 */
+    @PreAuthorize("@ss.hasPermi('company:traffic:charge')")
+    @Log(title = "公司流量充值", businessType = BusinessType.INSERT)
+    @PostMapping(value = "/rechargeTraffic")
+    public R rechargeTraffic(@RequestBody CompanyTrafficRecordChargeParam companyTrafficRecord) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyTrafficRecord.setUserId(loginUser.getUser().getUserId());
+        companyTrafficRecord.setUserName(loginUser.getUser().getUserName());
+        companyTrafficRecordService.recharge(companyTrafficRecord);
+        return R.ok();
+    }
+
+    /** 流量充值记录查询 */
+    @PreAuthorize("@ss.hasPermi('company:traffic:list')")
+    @GetMapping(value = "/list")
+    public TableDataInfo list(CompanyTrafficRecordQueryParam param) {
+        startPage();
+        List<CompanyTrafficRecord> list = companyTrafficRecordService.selectList(param);
+        return getDataTable(list);
+    }
+
+    /** 流量充值详情 */
+    @PreAuthorize("@ss.hasPermi('company:traffic:detail')")
+    @GetMapping(value = "/detail/{logId}")
+    public R detail(@PathVariable("logId") Long logId) {
+        CompanyTrafficRecord record = companyTrafficRecordService.selectById(logId);
+        return R.ok().put("data", record);
+    }
+
+    /** 流量换算*/
+    @GetMapping(value = "/trafficConversion")
+    public R trafficConversion(@RequestParam("traffic") Long amount) {
+        Long trafficKB = companyTrafficRecordService.trafficConversion(amount);
+        return R.ok().put("data", trafficKB);
+    }
+
+    /** 刷新单价*/
+    @GetMapping(value = "/refreshPrice")
+    public R refreshPrice() {
+        companyTrafficRecordService.refreshPrice();
+        return R.ok();
+    }
+
+    /** 流量统计*/
+    @GetMapping(value = "/trafficStatistics")
+    public R trafficStatistics(CompanyTrafficRecord record) {
+        return R.ok().put("tatol","").put("dept","").put("company","");
+    }
+}

+ 49 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficLogController.java

@@ -0,0 +1,49 @@
+package com.fs.company.controller;
+
+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.poi.ExcelUtil;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.param.CompanyTrafficRecordLogQueryParam;
+import com.fs.company.service.ICompanyTrafficRecordLogService;
+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("/company/trafficLog")
+public class CompanyTrafficLogController extends BaseController {
+
+    @Autowired
+    private ICompanyTrafficRecordLogService companyTrafficRecordLogService;
+
+
+    /** 流量充值记录查询 */
+    @PreAuthorize("@ss.hasPermi('company:trafficLog:list')")
+    @GetMapping(value = "/list")
+    public TableDataInfo list(CompanyTrafficRecordLogQueryParam param) {
+        startPage();
+        List<CompanyTrafficRecordLog> list = companyTrafficRecordLogService.selectList(param);
+        return getDataTable(list);
+    }
+
+
+    /** 导出 */
+    @PreAuthorize("@ss.hasPermi('company:trafficLog:export')")
+    @Log(title = "流量充值记录导出", businessType = BusinessType.EXPORT)
+    @GetMapping(value = "/export")
+    public AjaxResult export(CompanyTrafficRecordLogQueryParam param) {
+        List<CompanyTrafficRecordLog> list = companyTrafficRecordLogService.selectList(param);
+        ExcelUtil<CompanyTrafficRecordLog> util = new ExcelUtil<>(CompanyTrafficRecordLog.class);
+        return util.exportExcel(list, "流量充值记录");
+    }
+
+
+
+}

+ 39 - 3
fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java

@@ -1,21 +1,29 @@
 package com.fs.course.controller;
 
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.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.ServletUtils;
 import com.fs.common.utils.bean.BeanUtils;
+import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.param.FsCoursePlaySourceConfigCreateParam;
 import com.fs.course.param.FsCoursePlaySourceConfigEditParam;
 import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.course.vo.FsCoursePlaySourceConfigVO;
+import com.fs.framework.web.service.TokenService;
+import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import lombok.AllArgsConstructor;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -31,6 +39,8 @@ import java.util.*;
 public class FsCoursePlaySourceConfigController extends BaseController {
 
     private final IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService;
+    private final TokenService tokenService;
+    private final ISysConfigService configService;
 
     @PreAuthorize("@ss.hasPermi('course:playSourceConfig:list')")
     @GetMapping("/list")
@@ -41,6 +51,19 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         Map<String, Object> params = new HashMap<>();
         params.put("name", name);
         params.put("appid", appid);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        Long userId = null;
+        Long deptId = null;
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            deptId = loginUser.getDeptId();
+            if(config.getDept() == null || !config.getDept()){
+                userId = loginUser.getUserId();
+            }
+        }
+        params.put("userId", userId);
+        params.put("deptId", deptId);
 
         PageHelper.startPage(pageNum, pageSize);
         List<FsCoursePlaySourceConfigVO> list = fsCoursePlaySourceConfigService.selectCoursePlaySourceConfigVOListByMap(params);
@@ -70,8 +93,10 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         if (fsCoursePlaySourceConfigService.count(queryWrapper) > 0) {
             return AjaxResult.error("appid已存在");
         }
-
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         FsCoursePlaySourceConfig config = new FsCoursePlaySourceConfig();
+        config.setCreateUserId(loginUser.getUserId());
+        config.setCreateDeptId(loginUser.getDeptId());
         BeanUtils.copyProperties(param, config);
 
         config.setIsDel(0);
@@ -114,8 +139,19 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         fsCoursePlaySourceConfigService.update(updateWrapper);
         return AjaxResult.success();
     }
+
     @GetMapping("/listAll")
-    public R listAll() {
-        return R.ok().put("data", fsCoursePlaySourceConfigService.list(new QueryWrapper<FsCoursePlaySourceConfig>().eq("is_del", 0)));
+    public R listAll(Long companyId) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        QueryWrapper<FsCoursePlaySourceConfig> queryWrapper = new QueryWrapper<FsCoursePlaySourceConfig>().eq("is_del", 0);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            queryWrapper.eq("create_user_id", loginUser.getUserId()).eq(config.getDept() == null || !config.getDept(), "create_dept_id", loginUser.getDeptId());
+        }
+        if(companyId != null){
+            queryWrapper.and(e -> e.eq("company_id", companyId).or().isNull("company_id"));
+        }
+        return R.ok().put("data", fsCoursePlaySourceConfigService.list(queryWrapper));
     }
 }

+ 5 - 5
fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java

@@ -53,11 +53,11 @@ public class FsCourseRedPacketLogController extends BaseController
     @Autowired
     FsUserCourseMapper fsUserCourseMapper;
     @Autowired
-    FsUserCourseVideoMapper fsUserCourseVideoMapper;
+    TokenService tokenService;
     @Autowired
-    private TokenService tokenService;
+    ISysConfigService configService;
     @Autowired
-    private ISysConfigService configService;
+    FsUserCourseVideoMapper fsUserCourseVideoMapper;
     /**
      * 查询短链课程看课记录列表
      */
@@ -149,10 +149,10 @@ public class FsCourseRedPacketLogController extends BaseController
         Long userId = loginUser.getUser().getUserId();
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        List<OptionsVO> optionsVOS = new ArrayList<>();
+        List<OptionsVO> optionsVOS;
         if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
             optionsVOS = fsUserCourseMapper.selectFsUserCourseAllListByUserId(userId);
-        }else {
+        }else{
             optionsVOS = fsUserCourseMapper.selectFsUserCourseAllList();
         }
         return R.ok().put("list", optionsVOS);

+ 1 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java

@@ -202,6 +202,7 @@ public class FsUserCourseVideoController extends BaseController
         return R.ok();
     }
     @PostMapping("/batchUpdateRed")
+    @Log(title = "按课程批量保存设置红包金额", businessType = BusinessType.UPDATE)
     public R batchUpdateRed(@RequestBody List<BatchRedUpdate> list){
         fsUserCourseVideoService.batchUpdateRed(list);
         return R.ok();

+ 1 - 2
fs-admin/src/main/java/com/fs/fastGpt/FastgptExtUserTagController.java

@@ -4,7 +4,6 @@ import java.util.List;
 
 import com.fs.common.core.domain.R;
 import com.fs.fastGpt.vo.FastgptExtUserTagVO;
-import com.fs.framework.web.service.TokenService;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.vo.QwOptionsVO;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -64,7 +63,7 @@ public class FastgptExtUserTagController extends BaseController
     @GetMapping("/getMyQwUserList")
     public R getMyQwUserList()
     {
-        List<QwOptionsVO> list = qwCompanyService.selectQwCompanyListOptionsVO();
+        List<QwOptionsVO> list = qwCompanyService.selectQwCompanyListOptionsVO(null, null);
         return  R.ok().put("data",list);
     }
 

+ 25 - 3
fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java

@@ -2,7 +2,7 @@ package com.fs.his.controller;
 
 import java.util.List;
 
-import cn.hutool.core.util.IdUtil;
+import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
@@ -22,10 +22,11 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.CompanyVO;
 import com.fs.core.utils.OrderCodeUtils;
+import com.fs.course.config.CourseConfig;
 import com.fs.framework.web.service.TokenService;
-import com.fs.his.domain.FsDoctor;
 import com.fs.his.mapper.FsDoctorMapper;
 import com.fs.his.vo.OptionsVO;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -67,6 +68,8 @@ public class FsCompanyController extends BaseController
     private FsDoctorMapper fsDoctorMapper;
     @Autowired
     private ICompanyDeductService deductService;
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询诊所管理列表
      */
@@ -75,6 +78,12 @@ public class FsCompanyController extends BaseController
     public TableDataInfo list(Company company)
     {
         startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            company.setDeptId(loginUser.getDeptId());
+        }
         List<CompanyVO> list = companyService.selectCompanyListVO(company);
         return getDataTable(list);
     }
@@ -83,7 +92,14 @@ public class FsCompanyController extends BaseController
     public R companyList()
     {
 
-        List<OptionsVO> list = companyService.selectAllCompanyList();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long depId = null;
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            depId = loginUser.getDeptId();
+        }
+        List<OptionsVO> list = companyService.selectAllCompanyList(depId);
         return R.ok().put("data",list);
     }
     /**
@@ -133,6 +149,12 @@ public class FsCompanyController extends BaseController
     @PostMapping
     public R add(@RequestBody Company company)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            company.setDeptId(loginUser.getDeptId());
+        }
         return companyService.insertCompany(company);
     }
 

+ 12 - 12
fs-admin/src/main/java/com/fs/his/controller/FsUserComplaintController.java → fs-admin/src/main/java/com/fs/his/controller/FsHisComplaintController.java

@@ -6,8 +6,8 @@ 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.his.domain.FsUserComplaint;
-import com.fs.his.service.IFsUserComplaintService;
+import com.fs.his.domain.FsHisComplaint;
+import com.fs.his.service.IFsHisComplaintService;
 import com.fs.his.vo.FsUserComplaintVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -23,20 +23,20 @@ import java.util.List;
  */
 @RestController
 @RequestMapping("/his/complaint")
-public class FsUserComplaintController extends BaseController
+public class FsHisComplaintController extends BaseController
 {
     @Autowired
-    private IFsUserComplaintService fsUserComplaintService;
+    private IFsHisComplaintService fsUserComplaintService;
 
     /**
      * 查询用户投诉列表
      */
     @PreAuthorize("@ss.hasPermi('his:complaint:list')")
     @GetMapping("/list")
-    public TableDataInfo list(FsUserComplaint fsUserComplaint)
+    public TableDataInfo list(FsHisComplaint fsHisComplaint)
     {
         startPage();
-        List<FsUserComplaintVo> list = fsUserComplaintService.selectFsUserComplaintList(fsUserComplaint);
+        List<FsUserComplaintVo> list = fsUserComplaintService.selectFsUserComplaintList(fsHisComplaint);
         return getDataTable(list);
     }
 
@@ -46,9 +46,9 @@ public class FsUserComplaintController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:complaint:export')")
     @Log(title = "用户投诉", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult export(FsUserComplaint fsUserComplaint)
+    public AjaxResult export(FsHisComplaint fsHisComplaint)
     {
-        List<FsUserComplaintVo> list = fsUserComplaintService.selectFsUserComplaintList(fsUserComplaint);
+        List<FsUserComplaintVo> list = fsUserComplaintService.selectFsUserComplaintList(fsHisComplaint);
         ExcelUtil<FsUserComplaintVo> util = new ExcelUtil<FsUserComplaintVo>(FsUserComplaintVo.class);
         return util.exportExcel(list, "用户投诉数据");
     }
@@ -69,9 +69,9 @@ public class FsUserComplaintController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:complaint:add')")
     @Log(title = "用户投诉", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody FsUserComplaint fsUserComplaint)
+    public AjaxResult add(@RequestBody FsHisComplaint fsHisComplaint)
     {
-        return toAjax(fsUserComplaintService.insertFsUserComplaint(fsUserComplaint));
+        return toAjax(fsUserComplaintService.insertFsUserComplaint(fsHisComplaint));
     }
 
     /**
@@ -80,9 +80,9 @@ public class FsUserComplaintController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:complaint:edit')")
     @Log(title = "用户投诉", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody FsUserComplaint fsUserComplaint)
+    public AjaxResult edit(@RequestBody FsHisComplaint fsHisComplaint)
     {
-        return toAjax(fsUserComplaintService.updateFsUserComplaint(fsUserComplaint));
+        return toAjax(fsUserComplaintService.updateFsUserComplaint(fsHisComplaint));
     }
 
     /**

+ 19 - 0
fs-admin/src/main/java/com/fs/his/controller/FsPatientController.java

@@ -67,6 +67,25 @@ public class FsPatientController extends BaseController
         return getDataTable(list);
     }
 
+    @GetMapping("/userList")
+    public TableDataInfo userList(FsPatientVO fsPatient)
+    {
+        startPage();
+        List<FsPatientVO> list = fsPatientService.selectFsPatientListVO(fsPatient);
+        for (FsPatientVO fsPatientVO : list){
+            if (fsPatientVO.getPhone()!=null){
+                fsPatientVO.setPhone(fsPatientVO.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            }
+            if (fsPatientVO.getIdCard()!=null){
+                fsPatientVO.setIdCard(fsPatientVO.getIdCard().replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1**********$2"));
+            }
+            if (fsPatientVO.getMobile()!=null){
+                fsPatientVO.setMobile(fsPatientVO.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            }
+        }
+        return getDataTable(list);
+    }
+
     /**
      * 导出病人列表
      */

+ 24 - 0
fs-admin/src/main/java/com/fs/hisStore/FsHisStoreLogController.java

@@ -0,0 +1,24 @@
+package com.fs.hisStore;
+
+import com.fs.common.core.domain.R;
+import com.fs.hisStore.domain.SysOperLogScrm;
+import com.fs.hisStore.service.IFsHisStoreAuditLogScrmService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/store/hisStoreLog")
+public class FsHisStoreLogController {
+
+    @Autowired
+    private IFsHisStoreAuditLogScrmService fsHisStoreLogScrmService;
+    @RequestMapping("/{mainId}")
+    public R getInfo(@PathVariable("mainId") Long mainId) {
+        List<SysOperLogScrm> sysOperLogScrms = fsHisStoreLogScrmService.selectOperLogByMainId(mainId);
+        return R.ok().put("data",sysOperLogScrms);
+    }
+}

+ 134 - 0
fs-admin/src/main/java/com/fs/hisStore/FsStoreSCRMController.java

@@ -0,0 +1,134 @@
+package com.fs.hisStore;
+
+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.ParseUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.his.domain.FsStore;
+import com.fs.his.param.FsStoreAuditParam;
+import com.fs.his.service.IFsStoreService;
+import com.fs.hisStore.domain.FsStoreScrm;
+import com.fs.hisStore.service.IFsStoreScrmService;
+import com.fs.hisStore.vo.FsStoreScrmVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 店铺管理Controller
+ *
+ * @author fs
+ * @date 2023-06-15
+ */
+@RestController
+@RequestMapping("/store/store")
+public class FsStoreSCRMController extends BaseController
+{
+    @Autowired
+    private IFsStoreScrmService fsStoreService;
+
+    /**
+     * 查询店铺管理列表
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsStoreScrm fsStore)
+    {
+        startPage();
+        List<FsStoreScrm> list = fsStoreService.selectFsStoreList(fsStore);
+        for (FsStoreScrm store : list) {
+            store.setPhone(ParseUtils.parsePhone(store.getPhone()));
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出店铺管理列表
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:export')")
+    @Log(title = "店铺管理", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsStoreScrm fsStore)
+    {
+        List<FsStoreScrm> list = fsStoreService.selectFsStoreList(fsStore);
+        for (FsStoreScrm store : list) {
+            store.setPhone(ParseUtils.parsePhone(store.getPhone()));
+        }
+        ExcelUtil<FsStoreScrm> util = new ExcelUtil<FsStoreScrm>(FsStoreScrm.class);
+        return util.exportExcel(list, "店铺管理数据");
+    }
+
+    /**
+     * 获取店铺管理详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:query')")
+    @GetMapping(value = "/{storeId}")
+    public AjaxResult getInfo(@PathVariable("storeId") Long storeId)
+    {
+        FsStoreScrm fsStore = fsStoreService.selectFsStoreByStoreId(storeId);
+        fsStore.setPhone(ParseUtils.parsePhone(fsStore.getPhone()));
+        return AjaxResult.success(fsStore);
+    }
+
+    /**
+     * 新增店铺管理
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:add')")
+    @Log(title = "店铺管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsStoreScrm fsStore)
+    {
+        return AjaxResult.success(fsStoreService.insertFsStore(fsStore));
+    }
+
+    /**
+     * 修改店铺管理
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:edit')")
+    @Log(title = "店铺管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsStoreScrm fsStore)
+    {
+
+        if (fsStore.getPhone()!=null&&fsStore.getPhone().contains("*")){
+            fsStore.setPhone(null);
+        }
+        return toAjax(fsStoreService.updateFsStore(fsStore));
+    }
+
+    /**
+     * 删除店铺管理
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:remove')")
+    @Log(title = "店铺管理", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{storeIds}")
+    public AjaxResult remove(@PathVariable Long[] storeIds)
+    {
+        return toAjax(fsStoreService.deleteFsStoreByStoreIds(storeIds));
+    }
+
+    /**
+     * 店铺审核
+     */
+    @PreAuthorize("@ss.hasPermi('store:store:audit')")
+    @Log(title = "店铺管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/audit")
+    public AjaxResult audit(@RequestBody FsStoreAuditParam fsStore)
+    {
+        return toAjax(fsStoreService.updateFsStoreAudit(fsStore));
+    }
+
+    @GetMapping("/storeList")
+    public R storeList(){
+        List<FsStoreScrmVO> list = fsStoreService.selectAllStore();
+        return R.ok().put("data",list);
+    }
+
+
+}

+ 74 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java

@@ -135,6 +135,38 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         return util.exportExcel(list, "订单数据");
     }
 
+    /**
+     * 导出健康商城列表(明文)
+     */
+    @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:export:details')")
+    @Log(title = "健康商城订单", businessType = BusinessType.EXPORT)
+    @GetMapping("/healthExportDetails")
+    public AjaxResult healthExportDetails(FsStoreOrderParam param) {
+        if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
+            param.setBeginTime(null);
+            param.setEndTime(null);
+        }
+        if (fsStoreOrderService.isEntityNull(param)){
+            return AjaxResult.error("请筛选数据导出");
+        }
+        if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+            param.setCreateTimeList(param.getCreateTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getPayTimeRange())){
+            param.setPayTimeList(param.getPayTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getDeliverySendTimeRange())){
+            param.setDeliverySendTimeList(param.getDeliverySendTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
+            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, "订单数据");
+    }
+
 
     @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:exportItems')")
     @Log(title = "商城订单明细导出", businessType = BusinessType.EXPORT)
@@ -181,6 +213,47 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         return util.exportExcel(list, "订单明细数据");
     }
 
+    @PreAuthorize("@ss.hasPermi('store:healthStoreOrder:exportItems:details')")
+    @Log(title = "商城订单明细导出", businessType = BusinessType.EXPORT)
+    @GetMapping("/healthExportItemsDetails")
+    public AjaxResult healthExportItemsDetails(FsStoreOrderParam param) {
+        if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
+            param.setBeginTime(null);
+            param.setEndTime(null);
+        }
+        if (fsStoreOrderService.isEntityNull(param)){
+            return AjaxResult.error("请筛选数据导出");
+        }
+        if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+            param.setCreateTimeList(param.getCreateTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getPayTimeRange())){
+            param.setPayTimeList(param.getPayTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getDeliverySendTimeRange())){
+            param.setDeliverySendTimeList(param.getDeliverySendTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
+            param.setDeliveryImportTimeList(param.getDeliveryImportTimeRange().split("--"));
+        }
+        param.setIsHealth("1");
+        List<FsStoreOrderItemExportVO> list = orderItemService.selectFsStoreOrderItemListExportVO(param);
+        //对手机号脱敏
+        if (list != null) {
+            for (FsStoreOrderItemExportVO vo : list) {
+                if (!StringUtils.isEmpty(vo.getJsonInfo())) {
+                    try {
+                        StoreOrderProductDTO orderProductDTO = JSONObject.parseObject(vo.getJsonInfo(), StoreOrderProductDTO.class);
+                        BeanUtil.copyProperties(orderProductDTO, vo);
+                    } catch (Exception e) {
+                    }
+                }
+            }
+        }
+        ExcelUtil<FsStoreOrderItemExportVO> util = new ExcelUtil<FsStoreOrderItemExportVO>(FsStoreOrderItemExportVO.class);
+        return util.exportExcel(list, "订单明细数据");
+    }
+
     //订单发货批量导入
     @Log(title = "发货同步导入", businessType = BusinessType.IMPORT)
     @PostMapping("/importDeliveryNoteExpress")
@@ -203,7 +276,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         try {
             List<FsOrderDeliveryNoteDTO> dtoList = util.importExcel(file.getInputStream());
             if(!dtoList.isEmpty()){
-                fsStoreOrderService.importDeliveryNoteExpress(dtoList);
+               return fsStoreOrderService.importDeliveryNoteExpress(dtoList);
             }else {
                 R.error("操作失败,导入数据不能小于1条!");
             }

+ 83 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -7,9 +7,11 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.param.CompanyStoreOrderMoneyLogsListParam;
@@ -20,6 +22,7 @@ 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.framework.web.service.TokenService;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsUserService;
@@ -101,6 +104,9 @@ public class FsStoreOrderScrmController extends BaseController {
     @Autowired
     private IFsStoreOrderAuditLogScrmService orderAuditLogService;
 
+    @Autowired
+    private TokenService tokenService;
+
     private IErpOrderService getErpService(){
         //判断是否开启erp
         IErpOrderService erpOrderService = null;
@@ -255,6 +261,9 @@ public class FsStoreOrderScrmController extends BaseController {
         List<FsStoreOrderExportVO> list = fsStoreOrderService.selectFsStoreOrderListVOByExport(param);
         //对手机号脱敏
         if (list != null) {
+            //获取当前账号角色权限
+            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+
             for (FsStoreOrderExportVO vo : list) {
                 if (vo.getPhone() != null) {
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
@@ -272,6 +281,37 @@ public class FsStoreOrderScrmController extends BaseController {
     }
 
 
+    /**
+     * 导出订单列表(明文)
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeOrder:export:details')")
+    @Log(title = "订单", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportDetails")
+    public AjaxResult exportDetails(FsStoreOrderParam param) {
+        if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
+            param.setBeginTime(null);
+            param.setEndTime(null);
+        }
+        if (fsStoreOrderService.isEntityNull(param)){
+            return AjaxResult.error("请筛选数据导出");
+        }
+        if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+            param.setCreateTimeList(param.getCreateTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getPayTimeRange())){
+            param.setPayTimeList(param.getPayTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getDeliverySendTimeRange())){
+            param.setDeliverySendTimeList(param.getDeliverySendTimeRange().split("--"));
+        }
+        if(!StringUtils.isEmpty(param.getDeliveryImportTimeRange())){
+            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, "订单数据");
+    }
 
     @PreAuthorize("@ss.hasPermi('store:storeOrder:exportItems')")
     @Log(title = "订单明细导出", businessType = BusinessType.EXPORT)
@@ -321,6 +361,49 @@ public class FsStoreOrderScrmController extends BaseController {
         return util.exportExcel(list, "订单明细数据");
     }
 
+    /**
+     * 订单明细导出(明文)
+     * **/
+    @PreAuthorize("@ss.hasPermi('store:storeOrder:exportItems:details')")
+    @Log(title = "订单明细导出", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportItemsDetails")
+    public AjaxResult exportItemsDetails(FsStoreOrderParam param) {
+        if ("".equals(param.getBeginTime()) && "".equals(param.getEndTime())){
+            param.setBeginTime(null);
+            param.setEndTime(null);
+        }
+        if (fsStoreOrderService.isEntityNull(param)){
+            return AjaxResult.error("请筛选数据导出");
+        }
+        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<FsStoreOrderItemExportVO> list = orderItemService.selectFsStoreOrderItemListExportVO(param);
+        //对手机号脱敏
+        if (list != null) {
+            for (FsStoreOrderItemExportVO vo : list) {
+                if (!StringUtils.isEmpty(vo.getJsonInfo())) {
+                    try {
+                        StoreOrderProductDTO orderProductDTO = JSONObject.parseObject(vo.getJsonInfo(), StoreOrderProductDTO.class);
+                        BeanUtil.copyProperties(orderProductDTO, vo);
+                    } catch (Exception e) {
+                    }
+                }
+            }
+        }
+        ExcelUtil<FsStoreOrderItemExportVO> util = new ExcelUtil<FsStoreOrderItemExportVO>(FsStoreOrderItemExportVO.class);
+        return util.exportExcel(list, "订单明细数据");
+    }
 
     @GetMapping("/orderItemsNum")
     public R orderItemsNum(FsStoreOrderParam param) {

+ 14 - 2
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java

@@ -21,6 +21,7 @@ import com.fs.hisStore.param.FsStoreTuiProductAttrValueParam;
 import com.fs.hisStore.service.IFsStoreProductAttrScrmService;
 import com.fs.hisStore.service.IFsStoreProductAttrValueScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
+import com.fs.statis.dto.ProductAuditDTO;
 import com.mysql.cj.util.StringUtils;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
@@ -69,6 +70,17 @@ public class FsStoreProductScrmController extends BaseController
         return R.ok();
     }
 
+    @PreAuthorize("@ss.hasPermi('store:storeProduct:list')")
+    @PostMapping("/batchAudit")
+    @Log(title = "商品审核", businessType = BusinessType.AUDIT,isStoreLog = true,logParam = {"商品","批量审核商品信息"},
+    logParamExpression = "#p0.getProductIds().size()>1?"
+            +"(#p0.isAudit==1?new String[]{'商品','商品批量审核通过'}: new String[]{'商品','商品批量审核退回'}):" +
+            "(#p0.isAudit==1?new String[]{'商品','商品审核通过'}: new String[]{'商品','商品审核退回'})")
+    public R batchAudit(@RequestBody ProductAuditDTO auditDTO){
+        fsStoreProductService.batchAudit(auditDTO);
+        return R.ok();
+    }
+
     /**
      * 查询商品列表
      */
@@ -169,8 +181,8 @@ public class FsStoreProductScrmController extends BaseController
 
     @ApiOperation(value = "生成属性")
     @PostMapping(value = "/genFormatAttr/{productId}")
-    public ResponseEntity genFormatAttr(@PathVariable Long productId, @RequestBody String jsonStr){
-        return new ResponseEntity<>(fsStoreProductService.getFormatAttr(productId,jsonStr), HttpStatus.OK);
+    public ResponseEntity genFormatAttr(@PathVariable Long productId, @RequestBody String jsonStr,Long[] stores){
+        return new ResponseEntity<>(fsStoreProductService.getFormatAttr(productId,jsonStr, stores), HttpStatus.OK);
     }
 
 

+ 108 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreRecommendScrmController.java

@@ -0,0 +1,108 @@
+package com.fs.hisStore.controller;
+
+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.utils.poi.ExcelUtil;
+import com.fs.hisStore.domain.FsStoreRecommendScrm;
+import com.fs.hisStore.service.IFsStoreRecommendScrmService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 店铺推荐Controller
+ *
+ * @author fs
+ * @date 2023-06-15
+ */
+@RestController
+@RequestMapping("/store/recommend")
+public class FsStoreRecommendScrmController extends BaseController
+{
+    @Autowired
+    private IFsStoreRecommendScrmService fsStoreRecommendService;
+
+    /**
+     * 查询店铺推荐列表
+     */
+    @PreAuthorize("@ss.hasPermi('store:recommend:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsStoreRecommendScrm fsStoreRecommendScrm)
+    {
+        startPage();
+        List<FsStoreRecommendScrm> list = fsStoreRecommendService.selectFsStoreRecommendScrmList(fsStoreRecommendScrm);
+        return getDataTable(list);
+    }
+
+    /**
+     * 获取店铺推荐详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('store:recommend:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsStoreRecommendService.selectFsStoreRecommendScrmById(id));
+    }
+
+    /**
+     * 新增店铺推荐
+     */
+    @PreAuthorize("@ss.hasPermi('store:recommend:add')")
+    @Log(title = "店铺推荐", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsStoreRecommendScrm fsStoreRecommendScrm)
+    {
+        return toAjax(fsStoreRecommendService.insertFsStoreRecommendScrm(fsStoreRecommendScrm));
+    }
+
+    /**
+     * 修改店铺推荐
+     */
+    @PreAuthorize("@ss.hasPermi('store:recommend:edit')")
+    @Log(title = "店铺推荐", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsStoreRecommendScrm fsStoreRecommendScrm)
+    {
+        return toAjax(fsStoreRecommendService.updateFsStoreRecommendScrm(fsStoreRecommendScrm));
+    }
+
+    /**
+     * 删除店铺推荐
+     */
+    @PreAuthorize("@ss.hasPermi('store:recommend:delete')")
+    @Log(title = "店铺推荐", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsStoreRecommendService.deleteFsStoreRecommendScrmByIds(ids));
+    }
+
+    /**
+     * 查询有效的推荐店铺列表(用于前端展示)
+     */
+    @GetMapping("/validList")
+    public TableDataInfo validList(FsStoreRecommendScrm fsStoreRecommendScrm)
+    {
+        startPage();
+        List<FsStoreRecommendScrm> list = fsStoreRecommendService.selectValidRecommendList(fsStoreRecommendScrm);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出
+     * */
+    @PreAuthorize("@ss.hasPermi('store:recommend:export')")
+    @Log(title = "店铺推荐", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsStoreRecommendScrm fsStoreRecommendScrm)
+        {
+        List<FsStoreRecommendScrm> list = fsStoreRecommendService.selectFsStoreRecommendScrmList(fsStoreRecommendScrm);
+        ExcelUtil<FsStoreRecommendScrm> util = new ExcelUtil<>(FsStoreRecommendScrm.class);
+        return util.exportExcel(list, "店铺推荐数据");
+    }
+}

+ 20 - 3
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreScrmController.java

@@ -3,6 +3,7 @@ package com.fs.hisStore.controller;
 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.ParseUtils;
@@ -10,6 +11,7 @@ import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.hisStore.domain.FsStoreScrm;
 import com.fs.his.param.FsStoreAuditParam;
 import com.fs.hisStore.service.IFsStoreScrmService;
+import com.fs.hisStore.utils.StoreAuditLogUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -29,6 +31,9 @@ public class FsStoreScrmController extends BaseController
     @Autowired
     private IFsStoreScrmService fsStoreService;
 
+    @Autowired
+    private StoreAuditLogUtil storeAuditLogUtil;
+
     /**
      * 查询店铺管理列表
      */
@@ -52,6 +57,7 @@ public class FsStoreScrmController extends BaseController
     @GetMapping("/export")
     public AjaxResult export(FsStoreScrm fsStore)
     {
+        storeAuditLogUtil.generateOperId();
         List<FsStoreScrm> list = fsStoreService.selectFsStoreList(fsStore);
         for (FsStoreScrm store : list) {
             store.setPhone(ParseUtils.parsePhone(store.getPhone()));
@@ -80,7 +86,8 @@ public class FsStoreScrmController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody FsStoreScrm fsStore)
     {
-        return toAjax(fsStoreService.insertFsStore(fsStore));
+        storeAuditLogUtil.addOperLog(fsStoreService.insertFsStore(fsStore));
+        return AjaxResult.success();
     }
 
     /**
@@ -113,7 +120,8 @@ public class FsStoreScrmController extends BaseController
      * 店铺审核
      */
     @PreAuthorize("@ss.hasPermi('his:store:audit')")
-    @Log(title = "店铺审核", businessType = BusinessType.UPDATE,logParam = {"店铺","店铺审核"},isStoreLog = true)
+    @Log(title = "店铺审核", businessType = BusinessType.AUDIT,logParam = {"店铺","店铺审核"},isStoreLog = true
+    ,logParamExpression = "#p0.getIsAudit()==1?new String[]{'店铺','店铺审核通过'}: new String[]{'店铺','店铺审核退回'}")
     @PutMapping("/audit")
     public AjaxResult audit(@RequestBody FsStoreAuditParam fsStore)
     {
@@ -126,9 +134,18 @@ public class FsStoreScrmController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:store:refresh')")
     @Log(title = "店铺管理", businessType = BusinessType.UPDATE,logParam = {"店铺","重置店铺密码"},isStoreLog = true)
     @PutMapping("/refresh/{storeId}")
-    public AjaxResult refresh(Long storeId)
+    public AjaxResult refresh(@PathVariable Long storeId)
     {
         return toAjax(fsStoreService.refreshFsStore(storeId));
     }
 
+
+    /**
+     * 店铺审核日志
+     * */
+    @PreAuthorize("@ss.hasPermi('his:store:auditLog')")
+    @GetMapping("/auditLog/{storeId}")
+    public R auditLog(@PathVariable Long storeId){
+        return R.ok().put("auditLog",storeAuditLogUtil.selectOperLogByMainId(storeId));
+    }
 }

+ 11 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/SysOperlogScrmController.java

@@ -3,11 +3,14 @@ package com.fs.hisStore.controller;
 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.poi.ExcelUtil;
+import com.fs.hisStore.param.StoreOperMainQueryParam;
 import com.fs.hisStore.service.ISysOperLogScrmService;
 import com.fs.hisStore.domain.SysOperLogScrm;
+import com.fs.hisStore.vo.StoreOperMainVO;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -28,7 +31,7 @@ public class SysOperlogScrmController extends BaseController
         this.operLogService = operLogService;
     }
 
-    @PreAuthorize("@ss.hasPermi('his:storeLog:export')")
+    @PreAuthorize("@ss.hasPermi('his:storeLog:list')")
     @GetMapping("/list")
     public TableDataInfo list(SysOperLogScrm operLog)
     {
@@ -36,6 +39,13 @@ public class SysOperlogScrmController extends BaseController
         List<SysOperLogScrm> list = operLogService.selectOperLogList(operLog);
         return getDataTable(list);
     }
+    @PreAuthorize("@ss.hasPermi('his:storeLog:list')")
+    @GetMapping("/getMains")
+    public R getMains(StoreOperMainQueryParam operLog)
+    {
+        List<StoreOperMainVO> list = operLogService.getMains(operLog);
+        return R.ok().put("data", list);
+    }
 
     @Log(title = "操作日志", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('his:storeLog:export')")

+ 94 - 0
fs-admin/src/main/java/com/fs/qw/FsCourseTask.java

@@ -1,11 +1,27 @@
 package com.fs.qw;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import io.jsonwebtoken.lang.Assert;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.http.util.Asserts;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
 /**
  * 后台统计相关 定时任务
  */
@@ -16,7 +32,85 @@ public class FsCourseTask {
     private IFsCourseWatchLogService fsCourseWatchLogService;
     @Autowired
     private IQwWorkTaskService qwWorkTaskService;
+    @Autowired
+    private RedisCache redisCache;
+    private static final String REDPACKET_COMPANY_MONEY_CHANGE = "redpacket_money_CHANGE";
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+    private static final String REDPACKET_POOL_LOCK = "redpacket_pool_lock";
+    private static final String REDPACKET_COMPANY_MONEY = "redpacket_money";
+
+    @Autowired
+    private RedissonClient redissonClient;
+
+    /**
+     * 润天公司账户充值
+     * @param chargeMoney 充值金额
+     */
+    public void rechargeRedpacketMoney(String chargeMoney){
+        Assert.notNull(chargeMoney,"充值金额不能为空!");
+
+        log.info("润天公司账户充值 充值金额: {}",chargeMoney);
+
+        RLock lock = redissonClient.getLock(REDPACKET_POOL_LOCK);
+        try{
+            boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
+
+            if (!locked) {
+                log.error("获取锁失败...");
+                return;
+            }
+
+            BigDecimal redPacketCompanyMoney = redisCache.getCacheObject(REDPACKET_COMPANY_MONEY);
+            if(ObjectUtils.isNull(redPacketCompanyMoney)){
+                redPacketCompanyMoney = BigDecimal.ZERO;
+            }
+            BigDecimal value = redPacketCompanyMoney.add(new BigDecimal(chargeMoney));
+
+            log.info("润天公司账户充值成功 目前余额: {}",value);
 
+            redisCache.setCacheObject(REDPACKET_COMPANY_MONEY,value);
+
+
+            // 保存到数据库
+            SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("company.money");
+
+            sysConfig.setConfigValue(value.setScale(4, RoundingMode.HALF_UP).toPlainString());
+            sysConfig.setConfigKey("company.money");
+            sysConfigMapper.updateConfig(sysConfig);
+
+        }catch (Exception e){
+            log.error("充值失败 原因:{}", ExceptionUtils.getFullStackTrace(e),e);
+            throw new RuntimeException(e);
+        }finally {
+            if (lock.isHeldByCurrentThread()) {
+                lock.unlock();
+            }
+        }
+    }
+    /**
+     * 公司红包金额变更
+     */
+    public void redpacketCompanyMoneyChange(){
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("company.money");
+        Asserts.notNull(sysConfig,"公司账户配置不能为空!");
+
+        String configValue = sysConfig.getConfigValue();
+        Asserts.notNull(configValue,"公司账户余额不能为空");
+
+        BigDecimal oldBigDecimal = new BigDecimal(configValue);
+
+        BigDecimal redPacketCompanyMoney = redisCache.getCacheObject(REDPACKET_COMPANY_MONEY);
+
+        log.info("公司账户红包余额变更 {} -> {}",oldBigDecimal,redPacketCompanyMoney);
+
+        // 如果两者不一致才同步到数据库
+        if(oldBigDecimal.compareTo(redPacketCompanyMoney) != 0){
+            sysConfig.setConfigValue(redPacketCompanyMoney.setScale(4, RoundingMode.HALF_UP).toPlainString());
+            sysConfig.setConfigKey("company.money");
+            sysConfigMapper.updateConfig(sysConfig);
+        }
+    }
     /**
      * 添加企微观看日志
      * @throws Exception

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

@@ -2,8 +2,14 @@ package com.fs.qw.controller;
 
 import java.util.List;
 
+import cn.hutool.json.JSONUtil;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.ServletUtils;
+import com.fs.course.config.CourseConfig;
+import com.fs.framework.web.service.TokenService;
 import com.fs.qw.vo.QwOptionsVO;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -35,6 +41,10 @@ public class QwCompanyController extends BaseController
 {
     @Autowired
     private IQwCompanyService qwCompanyService;
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ISysConfigService configService;
 
     /**
      * 查询企微主体列表
@@ -44,6 +54,13 @@ public class QwCompanyController extends BaseController
     public TableDataInfo list(QwCompany qwCompany)
     {
         startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            qwCompany.setCreateDeptId(loginUser.getDeptId());
+            qwCompany.setCreateUserId(loginUser.getUserId());
+        }
         List<QwCompany> list = qwCompanyService.selectQwCompanyList(qwCompany);
         return getDataTable(list);
     }
@@ -52,7 +69,18 @@ public class QwCompanyController extends BaseController
     @GetMapping("/all")
     public R all()
     {
-        List<QwOptionsVO> list = qwCompanyService.selectQwCompanyListOptionsVO();
+        Long userId = null;
+        Long deptId = null;
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+            deptId = loginUser.getDeptId();
+            if(config.getDept() == null || !config.getDept()){
+                userId = loginUser.getUserId();
+            }
+        }
+        List<QwOptionsVO> list = qwCompanyService.selectQwCompanyListOptionsVO(userId, deptId);
         return R.ok().put("data", list);
     }
     /**
@@ -86,6 +114,10 @@ public class QwCompanyController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody QwCompany qwCompany)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+
+        qwCompany.setCreateUserId(loginUser.getUserId());
+        qwCompany.setCreateDeptId(loginUser.getDeptId());
         return toAjax(qwCompanyService.insertQwCompany(qwCompany));
     }
 

+ 39 - 0
fs-admin/src/main/java/com/fs/task/FsStoreTask.java

@@ -0,0 +1,39 @@
+package com.fs.task;
+
+import com.fs.hisStore.domain.FsStoreScrm;
+import com.fs.hisStore.service.IFsStoreScrmService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+import com.fs.common.utils.PubFun;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@AllArgsConstructor
+@Component("storeTaskScrm")
+public class FsStoreTask {
+
+    private final IFsStoreScrmService fsStoreScrmService;
+
+
+    public void store(){
+        LocalDate now = LocalDate.now();
+        List<FsStoreScrm> storeList = fsStoreScrmService.selectOverList();
+        List<FsStoreScrm> collect = storeList.stream().filter(e -> {
+            // 过期判定:任一证件到期日为 null 或 早于今天
+            return e.getBusinessLicenseExpireEnd() == null
+                    || e.getDrugLicenseExpiryEnd() == null
+                    || e.getMedicalLicenseExpiryEnd() == null
+                    || e.getBusinessLicenseExpireEnd().isBefore(now)
+                    || e.getDrugLicenseExpiryEnd().isBefore(now)
+                    || e.getMedicalLicenseExpiryEnd().isBefore(now);
+        }).collect(Collectors.toList());
+
+        if (!collect.isEmpty()) {
+            List<Long> ids = PubFun.listToNewList(collect, FsStoreScrm::getStoreId);
+            fsStoreScrmService.batchUpdateStatusByIds(ids, 0);
+        }
+    }
+
+}

+ 106 - 0
fs-admin/src/main/java/com/fs/user/controller/FsUserComplaintController.java

@@ -0,0 +1,106 @@
+package com.fs.user.controller;
+
+import java.util.List;
+
+import com.fs.user.domain.param.FsUserComplaintParam;
+import com.fs.user.domain.vo.FsUserComplaintVo;
+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.user.domain.FsUserComplaint;
+import com.fs.user.service.IFsUserComplaintService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 用户投诉Controller
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+@RestController
+@RequestMapping("/user/complaint")
+public class FsUserComplaintController extends BaseController
+{
+    @Autowired
+    private IFsUserComplaintService fsUserComplaintService;
+
+    /**
+     * 查询用户投诉列表
+     */
+    @PreAuthorize("@ss.hasPermi('user:complaint:list')")
+    @PostMapping("/list")
+    public TableDataInfo list(@RequestBody FsUserComplaintParam param)
+    {
+        startPage();
+        List<FsUserComplaintVo> list = fsUserComplaintService.selectFsUserComplaintVoList(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出用户投诉列表
+     */
+    @PreAuthorize("@ss.hasPermi('user:complaint:export')")
+    @Log(title = "用户投诉", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserComplaint fsUserComplaint)
+    {
+        List<FsUserComplaint> list = fsUserComplaintService.selectFsUserComplaintList(fsUserComplaint);
+        ExcelUtil<FsUserComplaint> util = new ExcelUtil<FsUserComplaint>(FsUserComplaint.class);
+        return util.exportExcel(list, "用户投诉数据");
+    }
+
+    /**
+     * 获取用户投诉详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('user:complaint:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserComplaintService.selectFsUserComplaintVoById(id));
+    }
+
+    /**
+     * 新增用户投诉
+     */
+    @PreAuthorize("@ss.hasPermi('user:complaint:add')")
+    @Log(title = "用户投诉", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserComplaint fsUserComplaint)
+    {
+        return toAjax(fsUserComplaintService.insertFsUserComplaint(fsUserComplaint));
+    }
+
+    /**
+     * 修改用户投诉
+     */
+    @PreAuthorize("@ss.hasPermi('user:complaint:edit')")
+    @Log(title = "用户投诉", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserComplaint fsUserComplaint)
+    {
+        return toAjax(fsUserComplaintService.updateFsUserComplaint(fsUserComplaint));
+    }
+
+    /**
+     * 删除用户投诉
+     */
+    @PreAuthorize("@ss.hasPermi('user:complaint:remove')")
+    @Log(title = "用户投诉", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserComplaintService.deleteFsUserComplaintByIds(ids));
+    }
+}

+ 109 - 0
fs-admin/src/main/java/com/fs/user/controller/FsUserComplaintMsgController.java

@@ -0,0 +1,109 @@
+package com.fs.user.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.user.domain.FsUserComplaintMsg;
+import com.fs.user.service.IFsUserComplaintMsgService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 投诉消息记录Controller
+ *
+ * @author fs
+ * @date 2025-09-08
+ */
+@RestController
+@RequestMapping("/user/msg")
+public class FsUserComplaintMsgController extends BaseController
+{
+    @Autowired
+    private IFsUserComplaintMsgService fsUserComplaintMsgService;
+
+    /**
+     * 查询投诉消息记录列表
+     */
+//    @PreAuthorize("@ss.hasPermi('user:msg:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserComplaintMsg fsUserComplaintMsg)
+    {
+        startPage();
+        List<FsUserComplaintMsg> list = fsUserComplaintMsgService.selectFsUserComplaintMsgList(fsUserComplaintMsg);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出投诉消息记录列表
+     */
+//    @PreAuthorize("@ss.hasPermi('user:msg:export')")
+    @Log(title = "投诉消息记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserComplaintMsg fsUserComplaintMsg)
+    {
+        List<FsUserComplaintMsg> list = fsUserComplaintMsgService.selectFsUserComplaintMsgList(fsUserComplaintMsg);
+        ExcelUtil<FsUserComplaintMsg> util = new ExcelUtil<FsUserComplaintMsg>(FsUserComplaintMsg.class);
+        return util.exportExcel(list, "投诉消息记录数据");
+    }
+
+    /**
+     * 获取投诉消息记录详细信息
+     */
+//    @PreAuthorize("@ss.hasPermi('user:msg:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserComplaintMsgService.selectFsUserComplaintMsgById(id));
+    }
+
+    /**
+     * 新增投诉消息记录
+     */
+    @PreAuthorize("@ss.hasPermi('user:msg:add')")
+    @Log(title = "投诉消息记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserComplaintMsg fsUserComplaintMsg)
+    {
+        if (fsUserComplaintMsg.getComplaintId() == null){
+            return AjaxResult.error("未查询到相关投诉!");
+        }
+        Long userId = getUserId();
+        fsUserComplaintMsg.setUserId(userId);
+        fsUserComplaintMsg.setSendType(2); //平台
+        return toAjax(fsUserComplaintMsgService.insertFsUserComplaintMsg(fsUserComplaintMsg));
+    }
+
+    /**
+     * 修改投诉消息记录
+     */
+    @PreAuthorize("@ss.hasPermi('user:msg:edit')")
+    @Log(title = "投诉消息记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserComplaintMsg fsUserComplaintMsg)
+    {
+        return toAjax(fsUserComplaintMsgService.updateFsUserComplaintMsg(fsUserComplaintMsg));
+    }
+
+    /**
+     * 删除投诉消息记录
+     */
+    @PreAuthorize("@ss.hasPermi('user:msg:remove')")
+    @Log(title = "投诉消息记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserComplaintMsgService.deleteFsUserComplaintMsgByIds(ids));
+    }
+}

+ 14 - 0
fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java

@@ -3,9 +3,12 @@ package com.fs.web.controller.system;
 import java.util.List;
 import java.util.Set;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.utils.PatternUtils;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.hisStore.config.MedicalMallConfig;
 import com.fs.system.service.ISysRoleService;
 import lombok.Synchronized;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -45,6 +48,12 @@ public class SysLoginController
     @Autowired
     private ISysRoleService roleService;
 
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    private MedicalMallConfig medicalMallConfig;
+
     /**
      * 登录方法
      *
@@ -78,7 +87,12 @@ public class SysLoginController
         Set<String> roles = permissionService.getRolePermission(user);
         // 权限集合
         Set<String> permissions = permissionService.getMenuPermission(user);
+        //药品商城参数
         AjaxResult ajax = AjaxResult.success();
+
+        if(medicalMallConfig!=null){
+            ajax.put("medicalMallConfig", medicalMallConfig);
+        }
         ajax.put("user", user);
         Integer isAdmin = 0;
         if (permissions.contains("*:*:*")){

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

@@ -10,5 +10,5 @@ spring:
 #    active: druid-sxjz
 #    active: druid-sft
 #    active: druid-fby
-    active: dev
+    active: dev-yjb
 

+ 18 - 40
fs-common-api/src/main/java/com/fs/framework/config/MyBatisConfig.java

@@ -32,82 +32,60 @@ import java.util.List;
 
  */
 @Configuration
-public class MyBatisConfig
-{
+public class MyBatisConfig {
     @Autowired
     private Environment env;
 
     static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
 
-    public static String setTypeAliasesPackage(String typeAliasesPackage)
-    {
+    public static String setTypeAliasesPackage(String typeAliasesPackage) {
         ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
         MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
         List<String> allResult = new ArrayList<String>();
-        try
-        {
-            for (String aliasesPackage : typeAliasesPackage.split(","))
-            {
+        try {
+            for (String aliasesPackage : typeAliasesPackage.split(",")) {
                 List<String> result = new ArrayList<String>();
                 aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                         + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                 Resource[] resources = resolver.getResources(aliasesPackage);
-                if (resources != null && resources.length > 0)
-                {
+                if (resources != null && resources.length > 0) {
                     MetadataReader metadataReader = null;
-                    for (Resource resource : resources)
-                    {
-                        if (resource.isReadable())
-                        {
+                    for (Resource resource : resources) {
+                        if (resource.isReadable()) {
                             metadataReader = metadataReaderFactory.getMetadataReader(resource);
-                            try
-                            {
+                            try {
                                 result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
-                            }
-                            catch (ClassNotFoundException e)
-                            {
+                            } catch (ClassNotFoundException e) {
                                 e.printStackTrace();
                             }
                         }
                     }
                 }
-                if (result.size() > 0)
-                {
+                if (result.size() > 0) {
                     HashSet<String> hashResult = new HashSet<String>(result);
                     allResult.addAll(hashResult);
                 }
             }
-            if (allResult.size() > 0)
-            {
+            if (allResult.size() > 0) {
                 typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
-            }
-            else
-            {
+            } else {
                 throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
             }
-        }
-        catch (IOException e)
-        {
+        } catch (IOException e) {
             e.printStackTrace();
         }
         return typeAliasesPackage;
     }
 
-    public Resource[] resolveMapperLocations(String[] mapperLocations)
-    {
+    public Resource[] resolveMapperLocations(String[] mapperLocations) {
         ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
         List<Resource> resources = new ArrayList<Resource>();
-        if (mapperLocations != null)
-        {
-            for (String mapperLocation : mapperLocations)
-            {
-                try
-                {
+        if (mapperLocations != null) {
+            for (String mapperLocation : mapperLocations) {
+                try {
                     Resource[] mappers = resourceResolver.getResources(mapperLocation);
                     resources.addAll(Arrays.asList(mappers));
-                }
-                catch (IOException e)
-                {
+                } catch (IOException e) {
                     // ignore
                 }
             }

+ 19 - 0
fs-common-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -90,7 +92,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

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

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

+ 6 - 2
fs-common/src/main/java/com/fs/common/core/domain/model/LoginUser.java

@@ -1,7 +1,7 @@
 package com.fs.common.core.domain.model;
 
-import java.util.Collection;
-import java.util.Set;
+import java.util.*;
+
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -14,6 +14,7 @@ import com.fs.common.core.domain.entity.SysUser;
  */
 public class LoginUser implements UserDetails
 {
+    private static final List<String> ADMIN_USER_NAME = Collections.singletonList("admin");
     private static final long serialVersionUID = 1L;
 
     /**
@@ -254,6 +255,9 @@ public class LoginUser implements UserDetails
     {
         return user;
     }
+    public boolean isAdmin(){
+        return ADMIN_USER_NAME.contains(user.getUserName());
+    }
 
     public void setUser(SysUser user)
     {

+ 9 - 5
fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java

@@ -1,10 +1,6 @@
 package com.fs.common.core.redis;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.BoundSetOperations;
@@ -236,4 +232,12 @@ public class RedisCache
     public Boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
         return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
     }
+
+    public Long decr(final String key,final Long delta) {
+        return redisTemplate.opsForValue().decrement(key, delta);
+    }
+
+    public Long incr(final String key,final Long delta) {
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
 }

+ 7 - 2
fs-common/src/main/java/com/fs/common/enums/BusinessType.java

@@ -2,7 +2,7 @@ package com.fs.common.enums;
 
 /**
  * 业务操作类型
- * 
+ *
 
  */
 public enum BusinessType
@@ -51,9 +51,14 @@ public enum BusinessType
      * 生成代码
      */
     GENCODE,
-    
+
     /**
      * 清空数据
      */
     CLEAN,
+
+    /**
+     * 审核
+     * */
+    AUDIT,
 }

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

@@ -12,6 +12,8 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -62,6 +64,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {

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

@@ -107,7 +107,7 @@ public class CompanyController extends BaseController
     @GetMapping("/allList")
     public TableDataInfo getHospital()
     {
-        List<OptionsVO> list = companyService.selectAllCompanyList();
+        List<OptionsVO> list = companyService.selectAllCompanyList(null);
         return getDataTable(list);
     }
 

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

@@ -49,8 +49,9 @@ public class FsCourseAnswerLogsController extends BaseController
             param.setPhone(param.getPhoneMk());
         }
 
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        param.setCompanyId( loginUser.getCompany().getCompanyId());
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        // 如果他可以看两个销售公司就会查不出来
+//        param.setCompanyId( loginUser.getCompany().getCompanyId());
 
         List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVONew(param);
         TableDataInfo rspData = new TableDataInfo();

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

@@ -57,8 +57,9 @@ public class FsCourseRedPacketLogController extends BaseController
         if (fsCourseRedPacketLog.getPhoneMk() != null && fsCourseRedPacketLog.getPhoneMk() != "") {
             fsCourseRedPacketLog.setPhone(encryptPhone(fsCourseRedPacketLog.getPhoneMk()));
         }
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        // 如果他可以看两个销售公司就会查不出来
+//        fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {

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

@@ -191,6 +191,7 @@ public class FsUserCoursePeriodController extends BaseController {
 
     @ApiOperation("按课程批量保存设置红包金额")
     @PostMapping("/batchRedPacket")
+    @Log(title = "按课程批量保存设置红包金额", businessType = BusinessType.UPDATE)
     public R batchRedPacketMoney(@RequestBody List<FsUserCourseVideoRedPackage> videoRedPackageList) {
         try {
             fsUserCourseVideoRedPackageService.batchSaveCompanyRedPackage(videoRedPackageList);
@@ -203,6 +204,7 @@ public class FsUserCoursePeriodController extends BaseController {
 
     @ApiOperation("按营期批量保存设置红包金额")
     @PostMapping("/batchRedPacket/byPeriod")
+    @Log(title = "按营期批量保存设置红包金额", businessType = BusinessType.UPDATE)
     public R batchRedPacketByPeriod(@RequestBody List<FsBatchPeriodRedPackageParam> periodRedPackageList) {
         try {
             fsUserCourseVideoRedPackageService.batchRedPacketByPeriod(periodRedPackageList);

+ 11 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java

@@ -278,6 +278,17 @@ public class QwSopController extends BaseController
         return toAjax(qwSopService.deleteQwSopByIds(ids));
     }
 
+    /**
+     * 批量删除SOP 任务下面的执行记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:sopLogs:removeAll')")
+    @Log(title = "企微sop", businessType = BusinessType.DELETE)
+	@DeleteMapping("/delSopLogs/{ids}")
+    public AjaxResult removeAll(@PathVariable String[] ids)
+    {
+        return toAjax(qwSopService.deleteQwSopLogsBySopIds(ids));
+    }
+
 
     /**
      * 批量执行SOP

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

@@ -149,7 +149,9 @@ public class QwSopLogsController extends BaseController
      * 我的自动化任务 和部门自动化任务 就只显示自己的或者部门的
      */
     private List<Long> getQwUserKeyList(QwSopLogsParam param, LoginUser loginUser) {
-
+        if(param.getFilterSopType() == null){
+            return new ArrayList<>(); // 返回空列表而不是null
+        }
         switch (param.getFilterSopType()) {
             case 2:
                 CompanyUser companyUser = iCompanyUserService.selectCompanyUserById(loginUser.getUser().getUserId());

+ 80 - 4
fs-company/src/main/java/com/fs/company/controller/qw/QwUserVoiceLogController.java

@@ -1,12 +1,18 @@
 package com.fs.company.controller.qw;
 
 import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+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.enums.BusinessType;
 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.ICompanyUserService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import com.fs.qw.domain.QwUserVoiceLog;
@@ -22,8 +28,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * 企微用户通话记录Controller
@@ -44,6 +52,9 @@ public class QwUserVoiceLogController extends BaseController
     @Autowired
     private IQwTagService iQwTagService;
 
+    @Autowired
+    private ICompanyUserService userService;
+
     /**
      * 查询企微用户通话记录列表
      */
@@ -137,23 +148,88 @@ public class QwUserVoiceLogController extends BaseController
     @GetMapping("/totalList")
     public TableDataInfo totalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         qwUserVoiceLog.setQwUserId(1L);
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
-        return getDataTable(list);
+        list.forEach(item->{
+            if(item.getQwExternalContact() != null){
+                if (item.getQwExternalContact().getTagIds() != null && !Objects.equals(item.getQwExternalContact().getTagIds(), "[]")) {
+                    QwTagSearchParam param = new QwTagSearchParam();
+                    Gson gson = new Gson();
+                    List<String> tagIds = gson.fromJson(
+                            item.getQwExternalContact().getTagIds(),
+                            new TypeToken<List<String>>() {
+                            }.getType()
+                    );
+                    param.setTagIds(tagIds);
+                    item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+                }
+            }
+        });
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
     }
 
 
     @GetMapping("/sellTotalList")
     public TableDataInfo sellTotalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
-        return getDataTable(list);
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
     }
 
     @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:sellTotalExport')")

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

@@ -83,10 +83,10 @@ public class SopUserLogsController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
 
         List<String> qwUserIdList=null;
-        if (sopUserLogs.getType()==2){
+        if (sopUserLogs.getType() != null && sopUserLogs.getType()==2){
             qwUserIdList = iQwUserService.selectQwUserListByCompanyUserId(loginUser.getUser().getUserId(), sopUserLogs.getCorpId());
         }
-        if (qwUserIdList!=null&& !qwUserIdList.isEmpty() && sopUserLogs.getType()==2 ){
+        if (qwUserIdList!=null&& !qwUserIdList.isEmpty() && sopUserLogs.getType() != null &&  sopUserLogs.getType()==2 ){
             sopUserLogs.setQwIdsList(qwUserIdList);
         }
 

+ 18 - 0
fs-company/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -19,6 +19,7 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
 import java.util.Arrays;
 
 /**
@@ -96,7 +97,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

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

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

+ 1 - 14
fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java

@@ -1,27 +1,19 @@
 package com.fs.app.controller;
 
 
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.app.annotation.Login;
-import com.fs.app.param.InquiryOrderMsgListParam;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.Log;
-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.SecurityUtils;
 import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.ip.IpUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.core.config.WxPayProperties;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.his.domain.*;
 import com.fs.his.dto.FsInquiryOrderPatientDTO;
-import com.fs.his.enums.FsInquiryOrderStatusEnum;
 import com.fs.his.param.*;
 import com.fs.his.service.*;
 import com.fs.his.vo.*;
@@ -30,10 +22,6 @@ 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.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;
@@ -42,7 +30,6 @@ 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.*;
@@ -128,7 +115,7 @@ public class InquiryOrderController extends  AppBaseController {
     @GetMapping("/getCompanyList")
     public R getCompanyList()
     {
-        List<OptionsVO> list = companyService.selectAllCompanyList();
+        List<OptionsVO> list = companyService.selectAllCompanyList(null);
         return R.ok().put("list",list);
     }
     @Login

+ 21 - 0
fs-doctor-app/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -92,6 +94,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 25 - 2
fs-framework/src/main/java/com/fs/framework/aspectj/LogAspect.java

@@ -1,6 +1,8 @@
 package com.fs.framework.aspectj;
 
 import java.lang.reflect.Method;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map;
@@ -11,6 +13,8 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.framework.web.domain.server.Sys;
 import com.fs.hisStore.domain.SysOperLogScrm;
+import com.fs.statis.dto.ProductAuditDTO;
+import com.fs.statis.dto.StoreAuditDTO;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.Signature;
@@ -18,6 +22,7 @@ import org.aspectj.lang.annotation.*;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -39,6 +44,8 @@ import com.fs.framework.manager.AsyncManager;
 import com.fs.framework.manager.factory.AsyncFactory;
 import com.fs.system.domain.SysOperLog;
 
+import static com.fs.common.utils.SecurityUtils.getUserId;
+
 /**
  * 操作日志记录处理
  *
@@ -123,9 +130,17 @@ public class LogAspect
             getControllerMethodDescription(joinPoint, controllerLog, operLog);
             // 保存数据库
             AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
-            if(controllerLog.isStoreLog()){
+            if(controllerLog.isStoreLog() ){
                 SysOperLogScrm operLogScrm = new SysOperLogScrm();
+                //插入operId
+                String operId = MDC.get("operIds");
                 BeanUtils.copyProperties(operLog, operLogScrm);
+                Long[] operIds = new Long[operId.split(",").length];
+                String[] operIdStrs = operId.split(",");
+                for (int i = 0; i < operIdStrs.length; i++) {
+                    operIds[i] = Long.parseLong(operIdStrs[i]);
+                }
+                operLogScrm.setOperIds(operIds);
                 resolveParam((ProceedingJoinPoint)joinPoint,operLogScrm);
                 AsyncManager.me().execute(AsyncFactory.recordOperScrm(operLogScrm));
             }
@@ -258,7 +273,7 @@ public class LogAspect
 
     private final ExpressionParser parser = new SpelExpressionParser();
 
-    public void resolveParam(ProceedingJoinPoint joinPoint, SysOperLogScrm operLog) throws Throwable {
+    public void resolveParam(ProceedingJoinPoint joinPoint, SysOperLogScrm operLog) {
         // 1. 获取注解实例
         Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
         Log annotation = method.getAnnotation(Log.class);
@@ -300,4 +315,12 @@ public class LogAspect
         operLog.setMainType(logParam[0]);
         operLog.setDes(logParam[1]);
     }
+
+
+    @After("logPointCut()")
+    public void after(JoinPoint joinPoint) throws Throwable {
+        // 移除 MDC 中的 logId
+        MDC.remove("operIds");
+    }
+
 }

+ 21 - 0
fs-framework/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -92,6 +94,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 7 - 1
fs-framework/src/main/java/com/fs/framework/manager/factory/AsyncFactory.java

@@ -118,8 +118,14 @@ public class AsyncFactory
             {
                 // 远程查询操作地点
                 operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
-                SpringUtils.getBean(ISysOperLogScrmService.class).insertOperlog(operLog);
+                SpringUtils.getBean(ISysOperLogScrmService.class).updateOperLog(operLog);
             }
         };
     }
+
+    /**
+     * 生成日志记录
+     * */
+
+
 }

+ 21 - 0
fs-hospital/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -16,6 +16,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -93,6 +95,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 26 - 6
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -1,9 +1,12 @@
 package com.fs.app.service;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;
-import com.fs.course.config.CourseMaConfig;
+import com.fs.company.domain.CompanyMiniapp;
+import com.fs.company.service.ICompanyMiniappService;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.service.IFsCourseWatchLogService;
@@ -15,7 +18,6 @@ import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.IQwUserService;
 import com.fs.qw.service.IQwUserVideoService;
-import com.fs.qw.service.impl.QwUserServiceImpl;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qwApi.param.QwExternalContactHParam;
 import com.fs.sop.domain.QwSopLogs;
@@ -27,6 +29,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
+import java.util.List;
 import java.util.Map;
 
 @Slf4j
@@ -42,9 +45,26 @@ public class IpadSendServer {
     private final IFsCourseWatchLogService watchLogService;
     private final IQwUserVideoService qwUserVideoService;
     private final RedisCache redisCache;
+    private final ICompanyMiniappService companyMiniappService;
 
-    private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap) {
-        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(content.getMiniprogramAppid());
+    private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
+        String appid = content.getMiniprogramAppid();
+        if(companyId != null && content.getMiniType() != null){
+            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", content.getMiniType()));
+            if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
+                appid = list.get(0).getAppId();
+            }
+        }
+        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(appid);
+        if(courseMaConfig == null){
+            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", 1));
+            if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
+                courseMaConfig = miniMap.get(list.get(0).getAppId());
+            }
+        }
+        if(courseMaConfig == null){
+            throw new BaseException("未找到小程序配置:{}", appid);
+        }
         // 小程序
         MiniProgramVo miniProgramVo = MiniProgramVo.builder()
                 .desc(content.getMiniprogramTitle())
@@ -53,7 +73,7 @@ public class IpadSendServer {
                 .imgUrl(content.getMiniprogramPicUrl())
                 .username(courseMaConfig.getOriginalId() + "@app")
                 .pagepath(content.getMiniprogramPage())
-                .appid(content.getMiniprogramAppid())
+                .appid(courseMaConfig.getAppid())
                 .build();
         miniProgramVo.setBase(vo);
         WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> resp = ipadSendUtils.sendMiniProgram(miniProgramVo);
@@ -337,7 +357,7 @@ public class IpadSendServer {
                     break;
                 case "4":
                 case "10":
-                    sendMiniProgram(vo, content, miniMap);
+                    sendMiniProgram(vo, content, miniMap, qwUser.getCompanyId());
                     break;
                 case "5":
                     // 文件

+ 2 - 2
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -92,8 +92,8 @@ public class SendMsg {
 
     private Map<String, FsCoursePlaySourceConfig> getMiniMap() {
         List<FsCoursePlaySourceConfig> list = fsCoursePlaySourceConfigService.list(new QueryWrapper<FsCoursePlaySourceConfig>().ne("type", 2).eq("is_del", 0));
-//        SysConfig maConfig = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
-//        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(maConfig.getConfigValue(), CourseMaConfig.class);
+        log.info("获取到的小程序配置:{}", JSON.toJSONString(list));
+        log.info("获取到的小程序配置:{}", JSON.toJSONString(list));
         return PubFun.listToMapByGroupObject(list, FsCoursePlaySourceConfig::getAppid);
     }
 

+ 22 - 0
fs-ipad-task/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,26 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 20 - 0
fs-live-app/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 38 - 37
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -206,43 +206,43 @@ public class QwMsgController {
         }
         switch (type){
             case 100001:
-                System.out.println("扫码返回");
+                log.info("id:{}, 扫码返回", id);
                 break;
             case 100002:
-                System.out.println("取消扫码");
+                log.info("id:{}, 取消扫码", id);
               //  redisCache.setCacheObject("qrCodeUid:"+wxWorkMsgResp.getUuid(),100002,10, TimeUnit.MINUTES);
                 break;
             case 100003:
-                System.out.println("确认扫码返回");
+                log.info("id:{}, 确认扫码返回", id);
 
                 break;
             case 104001:
-                System.out.println("登录成功");
+                log.info("id:{}, 登录成功", id);
 
                 JSONObject jsonObject = new JSONObject(wxWorkMsgResp.getJson());
                 QwUser qu = qwUserMapper.selectQwUserById(id);
                 Long corpId = jsonObject.getLong("Corpid");
-                System.out.println("回调中的"+corpId);
+                log.info("id:{}, 回调中的"+corpId, id);
                 String sCorpId = wxWorkService.getCorpId(wxWorkMsgResp.getUuid(), corpId,serverId);
                 if (sCorpId==null&& sCorpId.isEmpty()){
                     break;
                 }
                 if (!qu.getCorpId().equals(sCorpId)){
                     redisCache.setCacheObject("qrCodeUid:"+wxWorkMsgResp.getUuid(),22,10, TimeUnit.MINUTES);
-                    System.out.println("公司不匹配不给登录");
+                    log.info("id:{}, 公司不匹配不给登录", id);
                     WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
                     wxWorkGetQrCodeDTO.setUuid(wxWorkMsgResp.getUuid());
                     wxWorkService.LoginOut(wxWorkGetQrCodeDTO,serverId);
-                    log.info("调用退出登录");
+                    log.info("id:{}, 调用退出登录", id);
                     break;
                 }
                 if (!qu.getQwUserId().equals(jsonObject.get("acctid"))){
                     redisCache.setCacheObject("qrCodeUid:"+wxWorkMsgResp.getUuid(),23,10, TimeUnit.MINUTES);
-                    System.out.println("账号不匹配不给登录");
+                    log.info("id:{}, 账号不匹配不给登录", id);
                     WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
                     wxWorkGetQrCodeDTO.setUuid(wxWorkMsgResp.getUuid());
                     wxWorkService.LoginOut(wxWorkGetQrCodeDTO,serverId);
-                    log.info("调用退出登录");
+                    log.info("id:{}, 调用退出登录", id);
                     break;
                 }
                 QwUser qwUser = new QwUser();
@@ -250,44 +250,44 @@ public class QwMsgController {
                 qwUser.setVid(jsonObject.get("Vid").toString());
                 qwUser.setIpadStatus(1);
                 qwUserMapper.updateQwUser(qwUser);
-                System.out.println("存Vid");
+                log.info("id:{}, 存Vid", id);
                 redisCache.setCacheObject("qrCodeUid:"+wxWorkMsgResp.getUuid(),104001,10, TimeUnit.MINUTES);
                 break;
             case 100006:
 
                 break;
             case 100004:
-                System.out.println("需要验证二维码消息");
+                log.info("id:{}, 需要验证二维码消息", id);
                 redisCache.setCacheObject("qrCodeUid:"+wxWorkMsgResp.getUuid(),100004,10, TimeUnit.MINUTES);
                 break;
             case 100012:
-                log.info("需要二次验证:"+wxWorkMsgResp.getJson());
+                log.info("id:{}, 需要二次验证:"+wxWorkMsgResp.getJson(), id);
 
                 redisCache.setCacheObject("qrCodeUid:"+wxWorkMsgResp.getUuid(),100012,10, TimeUnit.MINUTES);
 
                 break;
             case 100005:
 
-                log.info("手机端结束登录:"+wxWorkMsgResp.getJson());
+                log.info("id:{}, 手机端结束登录:"+wxWorkMsgResp.getJson(), id);
                 JSONObject jsonObject1 = new JSONObject(wxWorkMsgResp.getJson());
                 qwUserStatus(wxWorkMsgResp.getUuid(),0, jsonObject1.getString("msg"));
                 break;
             case 100008:
                 QwUser vidUser = qwUserMapper.selectQwUserById(id);
                 if (vidUser.getUid().equals(wxWorkMsgResp.getUuid())){
-                    log.info("当前账号在其他设备登录:"+wxWorkMsgResp.getJson());
+                    log.info("id:{}, 当前账号在其他设备登录:"+wxWorkMsgResp.getJson(), id);
                     JSONObject jsonObject2 = new JSONObject(wxWorkMsgResp.getJson());
                     qwUserStatus(wxWorkMsgResp.getUuid(),0, jsonObject2.getString("msg"));
                 }
-                log.info("当前账号重新登录:"+wxWorkMsgResp.getJson());
+                log.info("id:{}, 当前账号重新登录:"+wxWorkMsgResp.getJson(), id);
                 break;
             case 100007:
-                log.info("异常断开:"+wxWorkMsgResp.getJson());
+                log.info("id:{}, 异常断开:"+wxWorkMsgResp.getJson(), id);
                 JSONObject jsonObject3 = new JSONObject(wxWorkMsgResp.getJson());
                 qwUserStatus(wxWorkMsgResp.getUuid(),0, "异常断开 - " + jsonObject3.getString("msg"));
                 break;
             case 100009:
-                log.info("二次验证:"+wxWorkMsgResp.getJson());
+                log.info("id:{}, 二次验证:"+wxWorkMsgResp.getJson(), id);
                 JSONObject jsonObject4 = new JSONObject(wxWorkMsgResp.getJson());
                 qwUserStatus(wxWorkMsgResp.getUuid(),0, "二次验证 - " + jsonObject4.getString("msg"));
                 break;
@@ -307,9 +307,9 @@ public class QwMsgController {
                 if (wxWorkMessageDTO.getMsgtype()==2||wxWorkMessageDTO.getMsgtype()==0||wxWorkMessageDTO.getMsgtype()==16||wxWorkMessageDTO.getMsgtype() == 101||wxWorkMessageDTO.getMsgtype() == 104){
 
                     String content = wxWorkMessageDTO.getContent();
-                    System.out.println("接收人:"+wxWorkMessageDTO.getReceiver());
-                    System.out.println("发送人:"+wxWorkMessageDTO.getSender());
-                    System.out.println("内容:"+content);
+                    log.info("id:{}, 接收人:"+wxWorkMessageDTO.getReceiver(), id);
+                    log.info("id:{}, 发送人:"+wxWorkMessageDTO.getSender(), id);
+                    log.info("id:{}, 内容:"+content, id);
                     Long receiver = wxWorkMessageDTO.getReceiver();
                     Long sender = wxWorkMessageDTO.getSender();
                     if(wxWorkMessageDTO.getMsgtype()==16){
@@ -323,32 +323,32 @@ public class QwMsgController {
                                 TimeUnit.SECONDS.sleep(1); // 阻塞1秒
                             } catch (InterruptedException e) {
                                 Thread.currentThread().interrupt(); // 处理中断异常
-                                System.out.println("第一次语音转换失败");
+                                log.info("id:{}, 第一次语音转换失败", id);
                             }
                             dto = wxWorkService.SpeechToTextEntity(ste, serverId);
                         }
                         WxwSpeechToTextEntityRespDTO data = dto.getData();
                         content = data.getText();
-                        System.out.println("语音消息"+content);
                         if(content == null || content.isEmpty()){
                             content = "==语音转换失败==";
                         }
+                        log.info("id:{}, 语音消息"+content, id);
                     }
                     else if (wxWorkMessageDTO.getMsgtype() == 101){
                         content = processImageMessage(serverId, wxWorkMessageDTO, wxWorkMsgResp);
-                        System.out.println("用户发送图片"+content);
+                        log.info("id:{}, 用户发送图片"+content, id);
                     }
                     // gif 表情消息
                     else if (wxWorkMessageDTO.getMsgtype() == 104){
                         content = wxWorkMessageDTO.getUrl();
-                        System.out.println("用户发送表情"+content);
+                        log.info("id:{}, 用户发送表情"+content, id);
                     }
 
                     if (2000000000000000L-receiver>0){
-                        System.out.println("客户发送");
+                        log.info("id:{}, 客户发送", id);
                         aiHookService.qwHookNotifyAiReply(id,sender,content,wxWorkMsgResp.getUuid(),wxWorkMessageDTO.getMsgtype());
                     }else {
-                        System.out.println("销售发送");
+                        log.info("id:{}, 销售发送", id);
                         aiHookService.qwHookNotifyAddMsg(id,receiver,content,wxWorkMsgResp.getUuid());
                     }
 
@@ -360,12 +360,12 @@ public class QwMsgController {
                     }
                     Long receiver = wxWorkMessageDTO.getReceiver();
                     Long extId=null;
-                    Long totalSeconds=null;
+                    long totalSeconds=0L;
                     if (2000000000000000L-receiver>0){
                          extId = wxWorkMessageDTO.getSender();
-                        System.out.println("客户发起");
+                        log.info("id:{}, 客户发起", id);
                     }else {
-                        System.out.println("销售发起");
+                        log.info("id:{}, 销售发起", id);
                         extId = wxWorkMessageDTO.getReceiver();
                     }
                     Integer recordType = wxWorkMessageDTO.getRecordtype();
@@ -392,11 +392,11 @@ public class QwMsgController {
                                     seconds = Integer.parseInt(matcher.group(2));
                                 }
                                 totalSeconds = hours * 3600L + minutes * 60L + seconds;
-                                System.out.println("总通话秒数: " + totalSeconds);
+                                log.info("id:{}, 总通话秒数: " + totalSeconds, id);
                             }
                         }
                     } else if (recordType==2){
-                        System.out.println("通话挂断");
+                        log.info("id:{}, 通话挂断", id);
                     }else {
                         break;
                     }
@@ -425,15 +425,15 @@ public class QwMsgController {
         QwUser user = qwUserMapper.selectQwUserById(qwUserId);
         //查询接收人
         if(user==null){
-            System.out.println("查询接收人为空");
+            log.info("查询接收人为空");
         }
         if(user.getFastGptRoleId()==null){
-            System.out.println("未绑定角色");
+            log.info("未绑定角色");
         }
         Long serverId = user.getServerId();
-        System.out.println("服务器id"+serverId);
+        log.info("服务器id"+serverId);
         if (serverId == null) {
-            System.out.println("服务id为空");
+            log.info("服务id为空");
         }
 
         WxWorkVid2UserIdDTO wxWorkVid2UserIdDTO = new WxWorkVid2UserIdDTO();
@@ -444,13 +444,13 @@ public class QwMsgController {
         List<WxWorkVid2UserIdRespDTO> data = WxWorkVid2UserIdRespDTO.getData();
         if (data==null|| data.isEmpty()){
 
-            System.out.println("未获取到extId"+wxWorkVid2UserIdDTO);
+            log.info("未获取到extId"+wxWorkVid2UserIdDTO);
         }
         com.fs.wxwork.dto.WxWorkVid2UserIdRespDTO dto = data.get(0);
 
         QwExternalContact qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByExternalUserIdAndQwUserId(dto.getOpenid(), user.getCorpId(),user.getQwUserId());
         if (qwExternalContacts==null){
-            System.out.println("没有外部联系人");
+            log.info("没有外部联系人");
         }
 
         //处理拉黑的
@@ -519,6 +519,7 @@ public class QwMsgController {
         Long id = redisCache.getCacheObject("qrCode:uuid:"+uid);
         QwUser qwUser = new QwUser();
         qwUser.setId(id);
+        qwUser.setRemark(msg);
         qwUser.setIpadStatus(status);
         qwUserMapper.updateQwUser(qwUser);
         QwUser qwUser1 = qwUserMapper.selectQwUserById(id);

+ 21 - 0
fs-qw-api-msg/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {

+ 21 - 0
fs-qw-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 20 - 0
fs-qw-mq/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -90,6 +92,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     public DefaultRedisScript<Long> limitScript()

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

@@ -367,7 +367,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
     private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos,LocalDateTime currentTime, Map<String,
-            QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
+                                         QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
                                  List<Company> companies) throws Exception {
         QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
 
@@ -861,7 +861,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                      SopUserLogsVo logVo, Date sendTime, Long courseId, Long videoId, String qwUserId, String companyUserId,
                                      String companyId, String externalId, String welcomeText, String qwUserName,
                                      Long fsUserId, boolean isGroupChat, String miniAppId, QwGroupChat groupChat,CourseConfig config,Map<Long,
-                                     Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType,
+                    Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType,
                                      List<Company> companies) {
         // 深拷贝 Content 对象,避免使用 JSON
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
@@ -957,6 +957,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     if(sopLogs.getSendType()==1){
                         setting.setMiniprogramAppid(miniAppId);
                     }else {
+                        int miniType = getLevel(grade);
                         //算主备小程序
                         String finalAppId = getAppIdFromMiniMap(miniMap, companyId, sendMsgType, grade);
 
@@ -964,6 +965,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                             finalAppId = miniAppId;
                         }
 
+                        setting.setMiniType(miniType);
                         if (!StringUtil.strIsNullOrEmpty(finalAppId)) {
                             setting.setMiniprogramAppid(finalAppId);
                         } else {
@@ -1036,8 +1038,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             return null;
         }
 
-        int effectiveGrade = (grade == null) ? 5 : grade;
-        int listIndex = (effectiveGrade == 1 || effectiveGrade == 2) ? 0 : 1;
+        int listIndex = getLevel(grade);
         List<CompanyMiniapp> miniapps = gradeMap.get(listIndex);
 
         if (miniapps == null || miniapps.isEmpty()) {
@@ -1050,6 +1051,12 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 : null;
     }
 
+    private static int getLevel(Integer grade) {
+        int effectiveGrade = (grade == null) ? 5 : grade;
+        int listIndex = (effectiveGrade == 1 || effectiveGrade == 2) ? 0 : 1;
+        return listIndex;
+    }
+
     /**
      * 深拷贝 Content 对象,避免使用 JSON 进行序列化和反序列化
      */
@@ -1213,9 +1220,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
     public FsCourseSopAppLink createFsCourseSopAppLink(String link, Date sendTime, Date updateTime, String companyId,
-                                         String companyUserId,String qwUserId,String qwUserName,String corpId,
-                                         Long courseId,String linkTile,String linkImageUrl,Long videoId,
-                                         String linkDescribe,String appMsgLink,String externalId){
+                                                       String companyUserId,String qwUserId,String qwUserName,String corpId,
+                                                       Long courseId,String linkTile,String linkImageUrl,Long videoId,
+                                                       String linkDescribe,String appMsgLink,String externalId){
 
         FsCourseSopAppLink sopAppLink=new FsCourseSopAppLink();
         sopAppLink.setLink(link);
@@ -1279,8 +1286,8 @@ 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) {
+                                       Long courseId, Long videoId, String qwUserId,
+                                       String companyUserId, String companyId, String externalId,String isOfficial,Long fsUserId) {
         // 获取缓存的配置
         CourseConfig config;
         synchronized(configLock) {

+ 21 - 0
fs-qw-task/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 19 - 0
fs-qw-voice/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -90,7 +92,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 21 - 0
fs-qwhook-msg/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 36 - 44
fs-qwhook-sop/src/main/java/com/fs/framework/config/MyBatisConfig.java

@@ -29,85 +29,62 @@ import java.util.List;
 /**
  * Mybatis支持*匹配扫描包
  *
-
  */
 @Configuration
-public class MyBatisConfig
-{
+public class MyBatisConfig {
     @Autowired
     private Environment env;
 
     static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
 
-    public static String setTypeAliasesPackage(String typeAliasesPackage)
-    {
+    public static String setTypeAliasesPackage(String typeAliasesPackage) {
         ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
         MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
         List<String> allResult = new ArrayList<String>();
-        try
-        {
-            for (String aliasesPackage : typeAliasesPackage.split(","))
-            {
+        try {
+            for (String aliasesPackage : typeAliasesPackage.split(",")) {
                 List<String> result = new ArrayList<String>();
                 aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                         + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                 Resource[] resources = resolver.getResources(aliasesPackage);
-                if (resources != null && resources.length > 0)
-                {
+                if (resources != null && resources.length > 0) {
                     MetadataReader metadataReader = null;
-                    for (Resource resource : resources)
-                    {
-                        if (resource.isReadable())
-                        {
+                    for (Resource resource : resources) {
+                        if (resource.isReadable()) {
                             metadataReader = metadataReaderFactory.getMetadataReader(resource);
-                            try
-                            {
+                            try {
                                 result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
-                            }
-                            catch (ClassNotFoundException e)
-                            {
+                            } catch (ClassNotFoundException e) {
                                 e.printStackTrace();
                             }
                         }
                     }
                 }
-                if (result.size() > 0)
-                {
+                if (result.size() > 0) {
                     HashSet<String> hashResult = new HashSet<String>(result);
                     allResult.addAll(hashResult);
                 }
             }
-            if (allResult.size() > 0)
-            {
+            if (allResult.size() > 0) {
                 typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
-            }
-            else
-            {
+            } else {
                 throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
             }
-        }
-        catch (IOException e)
-        {
+        } catch (IOException e) {
             e.printStackTrace();
         }
         return typeAliasesPackage;
     }
 
-    public Resource[] resolveMapperLocations(String[] mapperLocations)
-    {
+    public Resource[] resolveMapperLocations(String[] mapperLocations) {
         ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
         List<Resource> resources = new ArrayList<Resource>();
-        if (mapperLocations != null)
-        {
-            for (String mapperLocation : mapperLocations)
-            {
-                try
-                {
+        if (mapperLocations != null) {
+            for (String mapperLocation : mapperLocations) {
+                try {
                     Resource[] mappers = resourceResolver.getResources(mapperLocation);
                     resources.addAll(Arrays.asList(mappers));
-                }
-                catch (IOException e)
-                {
+                } catch (IOException e) {
                     // ignore
                 }
             }
@@ -115,9 +92,24 @@ 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
-    {
+    public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception {
         String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
         String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
         String configLocation = env.getProperty("mybatis-plus.configLocation");
@@ -131,4 +123,4 @@ public class MyBatisConfig
         sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
         return sessionFactory.getObject();
     }
-}
+}

+ 20 - 0
fs-qwhook-sop/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 21 - 0
fs-qwhook/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 20 - 0
fs-repeat-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -91,6 +93,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 5 - 0
fs-service/src/main/java/com/fs/company/constant/CompanyTrafficConstants.java

@@ -0,0 +1,5 @@
+package com.fs.company.constant;
+
+public class CompanyTrafficConstants {
+    public static final String CACHE_KEY = "company:traffic:record";
+}

+ 4 - 0
fs-service/src/main/java/com/fs/company/domain/Company.java

@@ -131,5 +131,9 @@ public class Company extends BaseEntity
 
     /** 后台制单是否需要付款 默认1 0-否 1-是*/
     private Integer isPay;
+    /** 限制销售公司Pad数量 -1表示不做限制*/
+    private Integer maxPadNum;
+    /** 所属部门id */
+    private Long deptId;
 
 }

+ 41 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecord.java

@@ -0,0 +1,41 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 销售公司流量记录对象 company_traffic_record
+ *
+ * */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class CompanyTrafficRecord {
+
+    @Excel(name = "id")
+    @TableId
+    private Long id;
+    @Excel(name = "公司id")
+    private Long companyId;
+    @Excel(name = "部门id")
+    private Long deptId;
+    @Excel(name="公司名称")
+    private String companyName;
+    @Excel(name = "流量余额")
+    private Long balance;     // 单位: KB
+    @Excel(name = "创建时间")
+    private Date createTime;
+    @Excel(name = "更新时间")
+    private Date updateTime;
+    @Excel(name = "创建人")
+    private Long createBy;
+    @Excel(name = "更新人")
+    private Long updateBy;
+}

+ 45 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecordLog.java

@@ -0,0 +1,45 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 销售公司流量记录对象 company_traffic_record_log
+ *
+ * */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class CompanyTrafficRecordLog {
+
+    @Excel(name = "id")
+    @TableId
+    private Long id;
+    @Excel(name = "公司id")
+    private Long companyId;
+    @Excel(name = "用户id")
+    private Long userId;
+    @Excel(name = "用户名称")
+    private String userName;
+    @Excel(name = "操作类型")
+    private Integer operationType; // 1-充值 2-扣费
+    @Excel(name = "流量变动数")
+    private Long trafficAmount;    // 单位: KB
+    @Excel(name = "充值金额")
+    private Long chargeAmount;    // 单位: 元
+    @Excel(name = "流量余额")
+    private Long balance;     // 单位: KB
+    @Excel(name = "备注")
+    private String remark;
+    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+}

+ 12 - 6
fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java

@@ -11,7 +11,6 @@ import com.fs.company.vo.CompanyCrmVO;
 import com.fs.company.vo.CompanyNameVO;
 import com.fs.company.vo.CompanyVO;
 import com.fs.his.vo.OptionsVO;
-import com.fs.huifuPay.sdk.opps.core.utils.StringUtil;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -81,6 +80,9 @@ public interface CompanyMapper
             "<if test = 'maps.companyName != null and  maps.companyName !=\"\"     '> " +
             "and c.company_name like CONCAT('%',#{maps.companyName},'%') " +
             "</if>" +
+            "<if test = 'maps.deptId != null'> " +
+            "and c.dept_id = #{maps.deptId} " +
+            "</if>" +
 
             "<if test = 'maps.status != null   '> " +
             "and c.status = #{maps.status}" +
@@ -118,6 +120,9 @@ public interface CompanyMapper
             "<if test = 'maps.companyName != null and  maps.companyName !=\"\"     '> " +
             "and c.company_name like CONCAT('%',#{maps.companyName},'%') " +
             "</if>" +
+            "<if test = 'maps.deptId != null'> " +
+            "and c.dept_id = #{maps.deptId} " +
+            "</if>" +
 
             "<if test = 'maps.phonenumber != null and  maps.phonenumber !=\"\"     '> " +
             "and c.company_mobile like CONCAT('%',#{maps.phonenumber},'%') " +
@@ -149,8 +154,9 @@ public interface CompanyMapper
 
 
     @Select({"<script> " +
-            "select c.*,cu.user_name FROM company c LEFT JOIN company_user cu ON c.user_id =cu.user_id  " +
-            "where is_del=0 " +
+            "select c.*,cu.user_name,qu.used_num FROM company c LEFT JOIN company_user cu ON c.user_id =cu.user_id  " +
+            "LEFT JOIN (select company_id, count(id) as used_num from qw_user where server_id is not null group by company_id) qu ON qu.company_id = c.company_id " +
+            "where c.is_del=0 " +
             "            <if test=\"companyName != null  and companyName != ''\"> and c.company_name like concat('%', #{companyName}, '%')</if>\n" +
             "            <if test=\"companyMobile != null  and companyMobile != ''\"> and c.company_mobile = #{companyMobile}</if>\n" +
             "            <if test=\"companyAddress != null  and companyAddress != ''\"> and c.company_address = #{companyAddress}</if>\n" +
@@ -163,13 +169,13 @@ public interface CompanyMapper
             "            <if test=\"linkName != null  and linkName != ''\"> and c.link_name like concat('%', #{linkName}, '%')</if>\n" +
             "            <if test=\"limitUserCount != null \"> and c.limit_user_count = #{limitUserCount}</if>\n" +
             "            <if test=\"isDel != null \"> and c.is_del = #{isDel}</if>\n" +
+            "            <if test=\"deptId != null \"> and c.dept_id = #{deptId}</if> " +
             "        " +
-            "order by company_id desc" +
+            " order by company_id desc" +
             "</script>"})
     List<CompanyVO> selectCompanyListVO(Company param);
 
-    @Select("select company_id dictValue,company_name dictLabel from company where is_del= 0")
-    List<OptionsVO> selectAllCompanyList();
+    List<OptionsVO> selectAllCompanyList(@Param("deptId") Long deptId);
 
     @Select("select company_id from company")
     List<Long> selectCompanyIds();

+ 10 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordLogMapper.java

@@ -0,0 +1,10 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface CompanyTrafficRecordLogMapper extends BaseMapper<CompanyTrafficRecordLog> {
+
+}

+ 28 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordMapper.java

@@ -0,0 +1,28 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface CompanyTrafficRecordMapper extends BaseMapper<CompanyTrafficRecord> {
+
+    @Select("SELECT SUM(ctr.balance) FROM company_traffic_record ctr inner join company c on ctr.company_id = c.company_id and c.dept_id = #{deptId}")
+    Long calculateTotalTrafficByDeptId(@Param("deptId") Long deptId);
+
+    @Select({"<script>"+
+                "SELECT ctr.id,c.company_id,c.company_name,ctr.balance from company c " +
+                "LEFT JOIN company_traffic_record ctr on ctr.company_id = c.company_id " +
+                "WHERE c.is_del = 0 "+
+                "<if test = 'maps.companyIds != null'> " +
+                "and c.company_id IN " +
+                "<foreach collection='maps.companyIds'  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach> "+
+                "</if> " +
+            "</script>"})
+    List<CompanyTrafficRecord> selectListWithUncharged(@Param("maps") CompanyTrafficRecordQueryParam record);
+}

+ 3 - 32
fs-service/src/main/java/com/fs/company/param/CompanyParam.java

@@ -1,10 +1,12 @@
 package com.fs.company.param;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
 
 import java.util.Date;
 
 
+@Data
 public class CompanyParam
 {
 
@@ -12,36 +14,5 @@ public class CompanyParam
     private Long companyId;
     private Integer status;
     private String phonenumber;
-
-    public String getCompanyName() {
-        return companyName;
-    }
-
-    public void setCompanyName(String companyName) {
-        this.companyName = companyName;
-    }
-
-    public Long getCompanyId() {
-        return companyId;
-    }
-
-    public void setCompanyId(Long companyId) {
-        this.companyId = companyId;
-    }
-
-    public Integer getStatus() {
-        return status;
-    }
-
-    public void setStatus(Integer status) {
-        this.status = status;
-    }
-
-    public String getPhonenumber() {
-        return phonenumber;
-    }
-
-    public void setPhonenumber(String phonenumber) {
-        this.phonenumber = phonenumber;
-    }
+    private Long deptId;
 }

+ 23 - 0
fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordChargeParam.java

@@ -0,0 +1,23 @@
+package com.fs.company.param;
+
+import com.fs.company.domain.Company;
+import lombok.Data;
+
+@Data
+public class CompanyTrafficRecordChargeParam {
+
+    private Long companyId;
+    private Integer operationType; // 1-充值 2-扣费
+    //充值金额
+    private Long chargeAmount;
+    //扣除流量
+    private Long changeTraffic;
+    private String remark;
+    private Long userId;
+    private String userName;
+
+    /**
+     *公司实例
+     * */
+    private Company company;
+}

+ 21 - 0
fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordLogQueryParam.java

@@ -0,0 +1,21 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class CompanyTrafficRecordLogQueryParam {
+
+
+    private Long id;
+    private Long companyId;
+    private List<Long> companyIds;
+    private Long userId;
+    private String userName;
+    private Integer operationType; // 1-充值 2-扣费
+    private Date createTime;
+    private Date createTimeStart;
+    private Date createTimeEnd;
+}

+ 17 - 0
fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordQueryParam.java

@@ -0,0 +1,17 @@
+package com.fs.company.param;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class CompanyTrafficRecordQueryParam {
+
+
+    private Long id;
+    private Long companyId;
+    private List<Long> companyIds;
+    private Long createBy;
+}

+ 2 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyConfigService.java

@@ -67,4 +67,6 @@ public interface ICompanyConfigService
     String selectConfigByKey(String configKey);
 
     CompanyConfig selectCompanyConfigByServerKey(String key);
+
+    String selectRedPacketConfigByKey(Long companyId);
 }

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

@@ -28,7 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
  */
 public interface ICompanyService
 {
-     List<OptionsVO> selectAllCompanyList() ;
+     List<OptionsVO> selectAllCompanyList(Long deptId) ;
 
     /**
      * 查询企业

+ 16 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordLogService.java

@@ -0,0 +1,16 @@
+package com.fs.company.service;
+
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.param.CompanyTrafficRecordLogQueryParam;
+
+import java.util.List;
+
+public interface ICompanyTrafficRecordLogService {
+    //查询流量记录列表
+    List<CompanyTrafficRecordLog> selectList(CompanyTrafficRecordLogQueryParam record);
+    //查询流量记录
+    CompanyTrafficRecordLog selectById(Long id);
+    //保存流量记录
+    boolean save(CompanyTrafficRecordLog entity);
+
+}

+ 25 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordService.java

@@ -0,0 +1,25 @@
+package com.fs.company.service;
+
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.param.CompanyTrafficRecordChargeParam;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+
+import java.util.List;
+
+public interface ICompanyTrafficRecordService {
+    //启动时初始化流量
+    void init();
+
+    void refreshTraffic();
+
+    //添加流量记录
+    boolean recharge(CompanyTrafficRecordChargeParam record);
+    //查询流量记录列表
+    List<CompanyTrafficRecord> selectList(CompanyTrafficRecordQueryParam record);
+    //查询流量记录
+    CompanyTrafficRecord selectById(Long id);
+
+    Long trafficConversion(Long amount);
+
+    void refreshPrice();
+}

+ 38 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyConfigServiceImpl.java

@@ -8,10 +8,15 @@ import com.fs.company.domain.CompanyConfig;
 import com.fs.company.mapper.CompanyConfigMapper;
 import com.fs.company.service.ICompanyConfigService;
 import com.fs.system.domain.SysConfig;
+import org.apache.http.util.Asserts;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 import static com.fs.common.utils.DictUtils.getCacheKey;
 
@@ -28,6 +33,10 @@ public class CompanyConfigServiceImpl implements ICompanyConfigService
     private RedisCache redisCache;
     @Autowired
     private CompanyConfigMapper companyConfigMapper;
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate; // 注入RedisTemplate
+    private static final String REDIS_KEY_PREFIX = "red_packet_config:";
+    private static final long CACHE_TIMEOUT = 24 * 60 * 60;
 
     /**
      * 查询参数配置
@@ -133,4 +142,33 @@ public class CompanyConfigServiceImpl implements ICompanyConfigService
     public CompanyConfig selectCompanyConfigByServerKey(String key) {
         return companyConfigMapper.selectCompanyConfigByServerKey(key);
     }
+
+    @Override
+    public String selectRedPacketConfigByKey(Long companyId) {
+        Asserts.notNull(companyId,"公司id不能为空!");
+        String redisKey = REDIS_KEY_PREFIX + companyId;
+        String cachedConfig = redisTemplate.opsForValue().get(redisKey);
+        if (cachedConfig != null) {
+            return cachedConfig;
+        }
+        synchronized (getSynchronizationObject(companyId)) {
+            cachedConfig = redisTemplate.opsForValue().get(redisKey);
+
+            if (cachedConfig != null) {
+                return cachedConfig;
+            }
+            String configFromDb = companyConfigMapper.selectRedPacketConfigByKey(companyId);
+            if (configFromDb != null) {
+                redisTemplate.opsForValue().set(redisKey, configFromDb, CACHE_TIMEOUT, TimeUnit.SECONDS);
+            } else {
+                redisTemplate.opsForValue().set(redisKey, "", 5 * 60, TimeUnit.SECONDS);
+            }
+            return configFromDb;
+        }
+    }
+
+    private static final ConcurrentHashMap<Long, Object> LOCKS = new ConcurrentHashMap<>();
+    private static Object getSynchronizationObject(Long companyId) {
+        return LOCKS.computeIfAbsent(companyId, k -> new Object());
+    }
 }

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

@@ -97,8 +97,8 @@ public class CompanyServiceImpl implements ICompanyService
     private CompanyUserMapper companyUserMapper;
 
     @Override
-    public List<OptionsVO> selectAllCompanyList() {
-        return companyMapper.selectAllCompanyList();
+    public List<OptionsVO> selectAllCompanyList(Long deptId) {
+        return companyMapper.selectAllCompanyList(deptId);
     }
 
     /**

+ 40 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordLogServiceImpl.java

@@ -0,0 +1,40 @@
+package com.fs.company.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.mapper.CompanyTrafficRecordLogMapper;
+import com.fs.company.param.CompanyTrafficRecordLogQueryParam;
+import com.fs.company.service.ICompanyTrafficRecordLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@Slf4j
+public class CompanyTrafficRecordLogServiceImpl extends ServiceImpl<CompanyTrafficRecordLogMapper, CompanyTrafficRecordLog> implements ICompanyTrafficRecordLogService {
+
+
+    @Override
+    public List<CompanyTrafficRecordLog> selectList(CompanyTrafficRecordLogQueryParam record) {
+        return baseMapper.selectList(new LambdaQueryWrapper<CompanyTrafficRecordLog>()
+                .eq(record.getCompanyId() != null ,CompanyTrafficRecordLog::getCompanyId, record.getCompanyId())
+                .in(record.getCompanyIds() != null,CompanyTrafficRecordLog::getCompanyId, record.getCompanyIds())
+                .eq(record.getUserId() != null ,CompanyTrafficRecordLog::getUserId, record.getUserId())
+                .eq(record.getOperationType() != null ,CompanyTrafficRecordLog::getOperationType, record.getOperationType())
+                .eq(record.getCreateTime() != null ,CompanyTrafficRecordLog::getCreateTime, record.getCreateTime())
+                .between(record.getCreateTimeStart() != null && record.getCreateTimeEnd() != null,CompanyTrafficRecordLog::getCreateTime, record.getCreateTimeStart(), record.getCreateTimeEnd())
+                .orderByDesc(CompanyTrafficRecordLog::getCreateTime));
+    }
+
+    @Override
+    public CompanyTrafficRecordLog selectById(Long id) {
+        return baseMapper.selectById(id);
+    }
+
+    @Override
+    public boolean save(CompanyTrafficRecordLog entity) {
+        return baseMapper.insert(entity)==1;
+    }
+}

+ 272 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordServiceImpl.java

@@ -0,0 +1,272 @@
+package com.fs.company.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.company.constant.CompanyTrafficConstants;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.mapper.CompanyTrafficRecordMapper;
+import com.fs.company.param.CompanyTrafficRecordChargeParam;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyTrafficRecordLogService;
+import com.fs.company.service.ICompanyTrafficRecordService;
+import com.fs.course.mapper.FsCourseTrafficLogMapper;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.service.ISysConfigService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class CompanyTrafficRecordServiceImpl extends ServiceImpl<CompanyTrafficRecordMapper, CompanyTrafficRecord> implements ICompanyTrafficRecordService {
+
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    private ISysConfigService iSysConfigService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    private SysConfig config;
+
+    @Autowired
+    private ICompanyTrafficRecordLogService companyTrafficRecordLogService;
+
+    @Autowired
+    private FsCourseTrafficLogMapper fsCourseTrafficLogMapper;
+
+    //启动时初始化部门流量缓存
+    @Override
+    public void init() {
+        List<CompanyTrafficRecord> companyTrafficRecords = baseMapper.selectList(new LambdaQueryWrapper<>());
+        Map<Long,Long> deptTrafficMap = new HashMap<>();
+        for (CompanyTrafficRecord companyTrafficRecord : companyTrafficRecords) {
+            // 获取公司ID
+            Long companyId = companyTrafficRecord.getCompanyId();
+            // 部门ID
+            Long deptId = companyService.selectCompanyById(companyId).getDeptId();
+            companyTrafficRecord.setDeptId(deptId);
+
+            // 公司流量 - 消耗流量
+            Long balance = companyTrafficRecord.getBalance();
+            //消耗的流量
+            Long totalInternetTraffic = fsCourseTrafficLogMapper.sumTrafficByCompany(companyId) == null?0L:fsCourseTrafficLogMapper.sumTrafficByCompany(companyId);
+            balance -= totalInternetTraffic;
+            //部门流量记录
+            Long deptTrafficAdd = (deptTrafficMap.get(deptId) == null ? 0L : deptTrafficMap.get(deptId)) + totalInternetTraffic;
+            deptTrafficMap.put(deptId, deptTrafficAdd);
+            //更新缓存
+            redisCache.deleteObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + companyId);
+            redisCache.incr(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + companyId, balance);
+            //删除前天流量缓存(如果存在;保留两天)
+            redisCache.deleteObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + companyId + ":" + LocalDate.now().minusDays(2));
+            //删除上上个月流量缓存
+            redisCache.deleteObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + companyId + ":" + YearMonth.now().minusMonths(2));
+        }
+
+        // 缓存 key=CACHE_KEY:deptId  value=部门ID
+        calculateTotalTrafficByDeptId(deptTrafficMap);
+    }
+
+    @Override
+    public void refreshTraffic() {
+        log.info("【定时更新流量】:{}", "开始");
+        List<CompanyTrafficRecord> companyTrafficRecords = baseMapper.selectList(new LambdaQueryWrapper<>());
+        for (CompanyTrafficRecord companyTrafficRecord : companyTrafficRecords) {
+            doReduce(companyTrafficRecord,"1");
+        }
+    }
+
+    //通过redis更新表流量并添加扣除日志
+    private void doReduce(CompanyTrafficRecord companyTrafficRecord, String doType) {
+
+        //根据fs_course_traffic_log表获取公司昨天流量
+        Long trafficAmountYesterday = fsCourseTrafficLogMapper.sumTrafficByCompanyYesterday(companyTrafficRecord.getCompanyId());
+        if(trafficAmountYesterday!=null) {
+            long balance = companyTrafficRecord.getBalance() - trafficAmountYesterday;
+            companyTrafficRecord.setBalance(balance);
+            log.info("【更新流量】:{}", "部门ID:" + companyTrafficRecord.getDeptId() +"公司ID:" + companyTrafficRecord.getCompanyId() +  "消耗流量:" + trafficAmountYesterday+"剩余流量:"+balance);
+            if (trafficAmountYesterday > 0) {
+                baseMapper.updateById(CompanyTrafficRecord.builder()
+                        .id(companyTrafficRecord.getId())
+                        .balance(balance)
+                        .updateTime(new Date())
+                        .updateBy(1L).build());
+                companyTrafficRecordLogService.save(CompanyTrafficRecordLog.builder()
+                        .balance(balance)
+                        .trafficAmount(trafficAmountYesterday)
+                        .companyId(companyTrafficRecord.getCompanyId())
+                        .createTime(new Date())
+                        .operationType(2)
+                        .userId(1L)//表示amdin
+                        .remark("0".equals(doType)?"重启同步流量":"定时更新扣除流量")
+                        .userName("admin")
+                        .build());
+            }
+        }
+        init();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean recharge(CompanyTrafficRecordChargeParam record) {
+        CompanyTrafficRecord companyTrafficRecord = new CompanyTrafficRecord();
+        //校验参数companyId
+        if (record.getCompanyId() == null || !validCompany(record))
+            throw new IllegalArgumentException("销售公司异常");
+
+        //查询公司流量记录
+        CompanyTrafficRecord companyRecord = baseMapper.selectOne(new LambdaQueryWrapper<CompanyTrafficRecord>()
+                .eq(CompanyTrafficRecord::getCompanyId, record.getCompanyId()));
+
+        //充值
+        if (record.getOperationType() == 1) {//获取转换后的流量
+            Long trafficAmount = trafficConversion(record.getChargeAmount());
+            record.setChangeTraffic(trafficAmount);//用于处理日志
+            if (companyRecord != null) {
+                //不是第一次充值  增加流量
+                companyTrafficRecord.setBalance(companyRecord.getBalance() + trafficAmount);
+                companyTrafficRecord.setId(companyRecord.getId());
+                companyTrafficRecord.setUpdateTime(new Date());
+                companyTrafficRecord.setUpdateBy(record.getUserId());
+                baseMapper.updateById(companyTrafficRecord);
+            } else {
+                companyTrafficRecord.setBalance(trafficAmount);
+                companyTrafficRecord.setCompanyId(record.getCompanyId());
+                companyTrafficRecord.setDeptId(record.getCompany().getDeptId());
+                companyTrafficRecord.setCompanyName(record.getCompany().getCompanyName());
+                companyTrafficRecord.setCreateTime(new Date());
+                companyTrafficRecord.setCreateBy(record.getUserId());
+                baseMapper.insert(companyTrafficRecord);
+            }
+                /*//扣除
+            case 2:
+                //扣除流量
+                *//*if (companyRecord.getBalance() < record.getChangeTraffic())
+                    throw new IllegalArgumentException("公司流量余额不足");*//*
+                companyTrafficRecord.setBalance(companyTrafficRecord.getBalance() - record.getChangeTraffic());
+                companyTrafficRecord.setId(companyRecord.getId());
+                companyTrafficRecord.setUpdateTime(new Date());
+                companyTrafficRecord.setUpdateBy(record.getUserId());
+                baseMapper.updateById(companyTrafficRecord);
+                break;*/
+        } else {
+            throw new IllegalArgumentException("非法的操作类型");
+        }
+        // 更新Redis缓存
+        doRefresh(record, companyTrafficRecord);
+
+        return doLog(companyTrafficRecord, record);
+    }
+
+    private boolean validCompany(CompanyTrafficRecordChargeParam record) {
+        Company company = new Company();
+        company.setCompanyId(record.getCompanyId());
+        company.setIsDel(0);
+        List<Company> companies = companyService.selectCompanyList(company);
+        if (companies != null && companies.size() == 1) {
+            record.setCompany(companies.get(0));
+            return true;
+        } else {
+            return false;
+        }
+    }
+    /**
+     * 更新Redis缓存
+     * @param record               充值参数
+     * @param companyTrafficRecord 公司流量记录
+     */
+    private void doRefresh(CompanyTrafficRecordChargeParam record, CompanyTrafficRecord companyTrafficRecord) {
+        if (record.getCompany() != null && record.getCompany().getDeptId() != null) {
+            // 更新缓存 key=CACHE_KEY:deptId:companyId  value=剩余流量
+            redisCache.incr(CompanyTrafficConstants.CACHE_KEY + ":" + record.getCompany().getDeptId() + ":" + record.getCompanyId(),
+                    companyTrafficRecord.getBalance());
+
+            // 部门新增流量
+            calculateTrafficByDeptId(record.getCompany().getDeptId(),companyTrafficRecord.getBalance());
+        } else {
+            // 抛出异常
+            throw new IllegalArgumentException("缓存ID异常");
+        }
+    }
+    /**
+     * 部门下所有公司流量总和减去使用的流量
+     */
+    private void calculateTrafficByDeptId(Long deptId,Long traffic) {
+        redisCache.incr(CompanyTrafficConstants.CACHE_KEY + ":" + deptId, traffic);
+    }
+
+    /**
+     * 部门下所有公司流量总和减去使用的流量
+     */
+    private void calculateTotalTrafficByDeptId(Map<Long,Long> deptTrafficMap) {
+        deptTrafficMap.forEach((deptId,totalTraffic) -> {
+            Long total = baseMapper.calculateTotalTrafficByDeptId(deptId);
+            redisCache.deleteObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId);
+            redisCache.incr(CompanyTrafficConstants.CACHE_KEY + ":" + deptId, total-totalTraffic);
+            //删除前天缓存
+            redisCache.deleteObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + LocalDate.now().minusDays(2));
+            //删除上上月缓存
+            redisCache.deleteObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + YearMonth.now().minusMonths(2));
+        });
+    }
+
+    private boolean doLog(CompanyTrafficRecord companyTrafficRecord, CompanyTrafficRecordChargeParam record) {
+        return companyTrafficRecordLogService.save(CompanyTrafficRecordLog.builder()
+                .balance(companyTrafficRecord.getBalance())
+                .trafficAmount(record.getChangeTraffic())
+                .chargeAmount(record.getChargeAmount())
+                .companyId(record.getCompanyId())
+                .createTime(new Date())
+                .operationType(record.getOperationType())
+                .userId(record.getUserId())
+                .remark(record.getRemark())
+                .userName(record.getUserName())
+                .build());
+    }
+
+    @Override
+    public List<CompanyTrafficRecord> selectList(CompanyTrafficRecordQueryParam record) {
+        return baseMapper.selectListWithUncharged(record);
+    }
+
+    @Override
+    public CompanyTrafficRecord selectById(Long id) {
+        return baseMapper.selectById(id);
+    }
+
+
+    /**
+     * 充值金额转换流量KB
+     */
+    @Override
+    public Long trafficConversion(Long amount) {
+        if (this.config == null) {
+            this.config = iSysConfigService.selectConfigByConfigKey("statis.config");
+        }
+        JSONObject jsonObject = JSONObject.parseObject(this.config.getConfigValue());
+        float trafficPrice = jsonObject.getFloatValue("trafficPrice");
+        double trafficGB = amount / trafficPrice;
+        return (long) (trafficGB * 1024 * 1024);
+    }
+
+    @Override
+    public void refreshPrice() {
+        this.config = iSysConfigService.selectConfigByConfigKey("statis.config");
+    }
+}

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

@@ -12,7 +12,10 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.file.OssException;
-import com.fs.common.utils.*;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.PatternUtils;
+import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyUserAreaParam;

+ 4 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyVO.java

@@ -91,4 +91,8 @@ public class CompanyVO implements Serializable
     private String restartTime;
     private List<String> miniAppMaster;
     private List<String> miniAppServer;
+    /** 已占用 */
+    private Integer usedNum;
+    /** 所属部门id */
+    private Long deptId;
 }

+ 28 - 29
fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java

@@ -9,11 +9,13 @@ import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
 import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.utils.spring.SpringUtils;
 import com.fs.course.config.CourseMaConfig;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
+import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.google.common.collect.Lists;
@@ -33,8 +35,10 @@ import org.yaml.snakeyaml.events.Event;
 import javax.annotation.PostConstruct;
 import java.io.File;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -45,6 +49,7 @@ public class WxMaConfiguration {
 
     private static final Map<String, WxMaMessageRouter> routers = Maps.newHashMap();
     private static Map<String, WxMaService> maServices;
+    private static IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService;
 
     @Autowired
     public WxMaConfiguration(SysConfigMapper sysConfigMapper, FsCoursePlaySourceConfigMapper configMapper) {
@@ -65,14 +70,19 @@ public class WxMaConfiguration {
 //            c.add(config2);
 //        }
         //看课小程序加载
-        SysConfig sysConfig3 = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
-        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(sysConfig3.getConfigValue(), CourseMaConfig.class);
-        if (courseMaConfigs!=null&& !courseMaConfigs.isEmpty()){
-            for (CourseMaConfig courseMaConfig : courseMaConfigs) {
+//        SysConfig sysConfig3 = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
+//        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(sysConfig3.getConfigValue(), CourseMaConfig.class);
+        // 使用 SpringUtils 获取服务实例,而不是依赖注入
+        IFsCoursePlaySourceConfigService coursePlaySourceConfigService = SpringUtils.getBean(IFsCoursePlaySourceConfigService.class);
+
+        // 看课小程序加载
+        List<FsCoursePlaySourceConfig> list = coursePlaySourceConfigService.list(new QueryWrapper<FsCoursePlaySourceConfig>().ne("type", 2).eq("is_del", 0));
+        if (list!=null&& !list.isEmpty()){
+            for (FsCoursePlaySourceConfig courseMaConfig : list) {
                 if (appid.equals(courseMaConfig.getAppid())) {
                     continue;
                 }
-                if (courseMaConfig.getType() != null && courseMaConfig.getType().equals("1")){
+                if (courseMaConfig.getType() != null && courseMaConfig.getType() == 1){
                     WxMaConfig.Config wxMaConfig = new WxMaConfig.Config();
                     BeanUtils.copyProperties(courseMaConfig, wxMaConfig);
                     c.add(wxMaConfig);
@@ -97,20 +107,7 @@ public class WxMaConfiguration {
     }
 
     public static WxMaService getMaService(String appid) {
-        // 从缓存获取
-        WxMaService wxService = maServices.get(appid);
-        if (wxService != null) {
-            return wxService;
-        }
-
-        // 缓存未命中,查询数据库
-        synchronized (WxMaConfiguration.class) {
-            // 双重检查
-            wxService = maServices.get(appid);
-            if (wxService != null) {
-                return wxService;
-            }
-
+        return maServices.computeIfAbsent(appid,e->{
             // 查询数据库
             FsCoursePlaySourceConfigMapper configMapper = SpringUtils.getBean(FsCoursePlaySourceConfigMapper.class);
             Wrapper<FsCoursePlaySourceConfig> queryWrapper = Wrappers.<FsCoursePlaySourceConfig>lambdaQuery()
@@ -121,11 +118,8 @@ public class WxMaConfiguration {
                 throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
             }
 
-            WxMaService service = getWxMaService(config.getAppid(), config.getSecret(), config.getToken(), config.getAesKey(), config.getMsgDataFormat());
-            maServices.put(appid, service);
-            log.info("Initialized WxMaService for appid: {}", appid);
-            return service;
-        }
+            return getWxMaService(config.getAppid(), config.getSecret(), config.getToken(), config.getAesKey(), config.getMsgDataFormat());
+        });
     }
 
     /**
@@ -156,11 +150,16 @@ public class WxMaConfiguration {
         }
 
         maServices = configs.stream()
-            .map(a -> {
-                WxMaService service = getWxMaService(a.getAppid(), a.getSecret(), a.getToken(), a.getAesKey(), a.getMsgDataFormat());
-                routers.put(a.getAppid(), this.newRouter(service));
-                return service;
-            }).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
+                .map(a -> {
+                    WxMaService service = getWxMaService(a.getAppid(), a.getSecret(), a.getToken(), a.getAesKey(), a.getMsgDataFormat());
+                    routers.put(a.getAppid(), this.newRouter(service));
+                    return service;
+                }).collect(Collectors.toMap(
+                        s -> s.getWxMaConfig().getAppid(),
+                        a -> a,
+                        (existing, replacement) -> replacement,
+                        ConcurrentHashMap::new
+                ));
     }
 
     private WxMaMessageRouter newRouter(WxMaService service) {

+ 10 - 9
fs-service/src/main/java/com/fs/core/utils/OrderCodeUtils.java

@@ -44,15 +44,16 @@ public class OrderCodeUtils {
 
     }
     public static String getOrderSn(){
-        String url= FSConfig.getCommonApi()+ "/app/common/genOrderCode";
-//        String url= "42.194.245.189:8010/app/common/genOrderCode";
-        String json = HttpRequest.get(url)
-                .execute().body();
-        OrderCodeVO vo= JSONUtil.toBean(json, OrderCodeVO.class);
-        if(vo.getCode()==200){
-            return vo.getOrderCode();
-        }
-        else return null;
+//        String url= FSConfig.getCommonApi()+ "/app/common/genOrderCode";
+////        String url= "42.194.245.189:8010/app/common/genOrderCode";
+//        String json = HttpRequest.get(url)
+//                .execute().body();
+//        OrderCodeVO vo= JSONUtil.toBean(json, OrderCodeVO.class);
+//        if(vo.getCode()==200){
+//            return vo.getOrderCode();
+//        }
+//        else return null;
+        return OrderCodeUtils.genOrderSn();
 
     }
 

+ 5 - 0
fs-service/src/main/java/com/fs/course/config/CourseConfig.java

@@ -62,6 +62,11 @@ public class CourseConfig implements Serializable {
      * 是否绑定
      */
     private Boolean isBound;
+    private Boolean dept;
+    /**
+     * 是否单销售观看(只能在第一次绑定的销售头上看课)
+     */
+    private boolean oneCompanyCourse;
 
 
     @Data

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

@@ -86,4 +86,8 @@ public class FsCoursePlaySourceConfig {
      * 销售ID
      */
     private Long companyUserId;
+    // 创建人
+    private Long createUserId;
+    // 创建部门
+    private Long createDeptId;
 }

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

@@ -64,4 +64,13 @@ public class FsCourseRedPacketLog extends BaseEntity
 
     private String appId;//小程序appId
 
+    /**
+     * 账户余额扣减前
+     */
+    private BigDecimal accBalanceBefore;
+    /**
+     * 账户余额扣减后
+     */
+    private BigDecimal accBalanceAfter;
+
 }

+ 35 - 5
fs-service/src/main/java/com/fs/course/dto/FsOrderDeliveryNoteDTO.java

@@ -3,6 +3,8 @@ package com.fs.course.dto;
 import com.fs.common.annotation.Excel;
 import lombok.Data;
 
+import java.math.BigDecimal;
+
 /**
  * 订单发货下载模板
  * **/
@@ -11,12 +13,40 @@ public class FsOrderDeliveryNoteDTO {
     /**
      * 系统订单号
      * **/
-    @Excel(name = "系统订单号(必填)",width = 40,sort = 1)
+    @Excel(name = "系统订单号(必填)",width = 20,sort = 1)
     private String orderNumber;
 
-    /**
-     * 发货状态
-     * **/
-    @Excel(name = "系统订单号(1:待发货、2:待收货、3:交易完成,-3:已取消)填写对应数字",width = 90,sort = 2)
+    @Excel(name = "物流公司编号(必填)(SF:顺丰、EMS:邮政、ZTO:中通、JD:京东、DBL:德邦)",width = 30,sort = 2)
+    private String deliverySn;
+
+    private String deliveryName;
+
+    @Excel(name = "快递单号(必填)",width = 20,sort = 3)
+    private String deliveryId;
+
+    @Excel(name = "物流状态(0:暂无信息、1:已揽收、2:在途中、3:签收、4:问题件)",width = 40,sort = 4)
+    private Integer deliveryStatus;
+
+    @Excel(name = "物流结算费用",width = 20,sort = 5)
+    private BigDecimal deliveryPayMoney;
+
+    @Excel(name = "物流跟踪状态(311:快递柜或驿站签收、304:派件异常后最终签收、301:正常签收、211:已放入快递柜或驿站、202:派件中、201:到达派件城市、401:发货无信息、412:快递柜或驿站超时未取、407:退货未签收)",width = 40,sort = 6)
+    private Integer deliveryType;
+
+    @Excel(name = "物流结算状态(1:已结算、2:冻结、3:解冻、4:退回运费、5.调账)",width = 20,sort = 7)
+    private Integer deliveryPayStatus;
+
+    @Excel(name = "快递账单日期",width = 20,sort = 8)
+    private String deliveryTime;
+
+    @Excel(name = "快递结算日期",width = 20,sort = 9)
+    private String deliveryPayTime;
+
+//    /**
+//     * 发货状态
+//     * **/
+//    @Excel(name = "系统订单号(1:待发货、2:待收货、3:交易完成,-3:已取消)填写对应数字",width = 40,sort = 7)
+//    private Integer deliveryNoteStatus;
+
     private Integer deliveryNoteStatus;
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java

@@ -188,5 +188,11 @@ public interface FsCourseTrafficLogMapper
             @Param("offset") int offset,
             @Param("pageSize") int pageSize);
 
+    @Select("SELECT SUM(T.internet_traffic)/1024 AS totalInternetTraffic FROM fs_course_traffic_log T " +
+            "WHERE DATE(T.create_time) = DATE(CURDATE()) AND T.company_id = #{companyId} GROUP BY T.company_id ")
+    Long sumTrafficByCompany(Long companyId);
 
+    @Select("SELECT SUM(T.internet_traffic)/1024 AS totalInternetTraffic FROM fs_course_traffic_log T " +
+            "WHERE DATE(T.create_time) = DATE(CURDATE() - INTERVAL 1 DAY) AND T.company_id = #{companyId} GROUP BY T.company_id ")
+    Long sumTrafficByCompanyYesterday(Long companyId);
 }

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

@@ -519,4 +519,9 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
     Long selectByWatchlxDay(@Param("userId") Long userId,@Param("projectId")  Long projectId);
 
     List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOexport(@Param("maps") FsCourseWatchLogListParam param);
+
+    /**
+     * 查询训练营看课人数
+     */
+    Integer getUserCountByCampId(@Param("trainingCampId") Long trainingCampId);
 }

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

@@ -85,7 +85,7 @@ public interface FsUserCourseCategoryMapper
     /**
      * 查询所有分类
      */
-    @Select("select cate_id as cateId, cate_name as cateName, pid from fs_user_course_category where is_del=0")
+    @Select("select cate_id as cateId, concat(cate_name,'-',pid) as cateName, pid from fs_user_course_category where is_del=0")
     @MapKey("cateName")
     Map<String,FsUserCourseCategory> queryAllCategoryData();
 

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

@@ -233,7 +233,7 @@ public interface FsUserCourseMapper
 
     @Select("select course_id dict_value, course_name dict_label,img_url dict_imgUrl  from fs_user_course where is_del = 0 and is_private = 1 ")
     List<OptionsVO> selectFsUserCourseAllList();
-
+    
     @Select("select course_id dict_value, course_name dict_label,img_url dict_imgUrl  from fs_user_course where is_del = 0 and is_private = 1" +
             " and find_in_set(#{companyId},company_ids) ")
     List<OptionsVO> selectFsUserCourseByCompany(@Param("companyId") Long companyId);

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

@@ -40,6 +40,9 @@ public class FsCoursePlaySourceConfigCreateParam {
     private String msgDataFormat;
 
     @NotNull(message = "类型不能为空")
-    @ApiModelProperty("类型 1小程序 2公众号")
+    @ApiModelProperty("类型 1主要小程序 2公众号 3炮灰小程序")
     private Integer type;
+
+    @ApiModelProperty("所属公司")
+    private Long companyId;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java

@@ -39,4 +39,7 @@ public class FsCoursePlaySourceConfigEditParam {
 
     @ApiModelProperty("类型 1小程序 2公众号")
     private Integer type;
+
+    @ApiModelProperty("所属公司")
+    private Long companyId;
 }

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác