Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/master'

zyp 1 nedēļu atpakaļ
vecāks
revīzija
88582e2a05
100 mainītis faili ar 3102 papildinājumiem un 617 dzēšanām
  1. 2 4
      fs-admin/src/main/java/com/fs/course/controller/FsCourseQuestionBankController.java
  2. 1 1
      fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java
  3. 42 2
      fs-admin/src/main/java/com/fs/store/controller/FsStoreOrderController.java
  4. 56 2
      fs-admin/src/main/java/com/fs/task/StoreTask.java
  5. 93 0
      fs-admin/src/main/resources/logback.xml
  6. 0 1
      fs-common/pom.xml
  7. 106 106
      fs-common/src/main/java/com/fs/common/config/FSSysConfig.java
  8. 17 5
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  9. 4 2
      fs-company-app/src/main/resources/application.yml
  10. 93 0
      fs-company-app/src/main/resources/logback.xml
  11. 115 0
      fs-company/src/main/java/com/fs/crm/controller/CrmCustomerAssistController.java
  12. 36 0
      fs-company/src/main/java/com/fs/crm/controller/CrmCustomerController.java
  13. 103 0
      fs-company/src/main/java/com/fs/qw/QwMsgController.java
  14. 1 0
      fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java
  15. 1 1
      fs-company/src/main/java/com/fs/qw/QwSopTempController.java
  16. 28 0
      fs-company/src/main/java/com/fs/qw/qw/QwWorkTaskController.java
  17. 0 34
      fs-company/src/main/java/com/fs/qw/vo/QwMessageListVO.java
  18. 0 5
      fs-company/src/main/java/com/fs/users/controller/FsUserController.java
  19. 93 0
      fs-company/src/main/resources/logback.xml
  20. 68 4
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  21. 160 0
      fs-qw-api-msg/src/main/java/com/fs/app/util/AudioUtils.java
  22. 93 0
      fs-qw-api/src/main/resources/logback.xml
  23. 5 2
      fs-qw-task/src/main/java/com/fs/app/task/CourseWatchLogScheduler.java
  24. 1 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  25. 93 0
      fs-qw-task/src/main/resources/logback.xml
  26. 93 0
      fs-qwhook-msg/src/main/resources/logback.xml
  27. 4 16
      fs-qwhook-sop/src/main/java/com/fs/app/controller/testController.java
  28. 93 0
      fs-qwhook-sop/src/main/resources/logback.xml
  29. 93 0
      fs-qwhook/src/main/resources/logback.xml
  30. 5 3
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  31. 1 0
      fs-service-system/src/main/java/com/fs/company/service/ICompanyService.java
  32. 20 0
      fs-service-system/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  33. 23 11
      fs-service-system/src/main/java/com/fs/course/dto/FsCourseQuestionBankImportDTO.java
  34. 11 0
      fs-service-system/src/main/java/com/fs/course/dto/WatchLogDTO.java
  35. 58 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  36. 11 4
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java
  37. 8 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  38. 13 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  39. 32 0
      fs-service-system/src/main/java/com/fs/course/param/FsCourseListBySidebarParam.java
  40. 2 1
      fs-service-system/src/main/java/com/fs/course/param/newfs/FsCourseSortLinkParam.java
  41. 8 2
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java
  42. 1 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseService.java
  43. 3 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  44. 324 102
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java
  45. 9 3
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java
  46. 7 2
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  47. 15 5
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  48. 26 0
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseListBySidebarVO.java
  49. 37 0
      fs-service-system/src/main/java/com/fs/course/vo/FsCourseVideoListBySidebarVO.java
  50. 36 0
      fs-service-system/src/main/java/com/fs/crm/domain/CrmCustomerAssist.java
  51. 2 1
      fs-service-system/src/main/java/com/fs/crm/enums/CustomerLogEnum.java
  52. 69 0
      fs-service-system/src/main/java/com/fs/crm/mapper/CrmCustomerAssistMapper.java
  53. 1 57
      fs-service-system/src/main/java/com/fs/crm/mapper/CrmCustomerMapper.java
  54. 11 0
      fs-service-system/src/main/java/com/fs/crm/param/CrmCustomerAssistDeLParam.java
  55. 64 0
      fs-service-system/src/main/java/com/fs/crm/service/ICrmCustomerAssistService.java
  56. 4 0
      fs-service-system/src/main/java/com/fs/crm/service/ICrmCustomerService.java
  57. 104 0
      fs-service-system/src/main/java/com/fs/crm/service/impl/CrmCustomerAssistServiceImpl.java
  58. 86 5
      fs-service-system/src/main/java/com/fs/crm/service/impl/CrmCustomerServiceImpl.java
  59. 4 0
      fs-service-system/src/main/java/com/fs/crm/vo/CrmMyCustomerListQueryVO.java
  60. 6 0
      fs-service-system/src/main/java/com/fs/erp/converter/ErpWdtToErpOrderMapper.java
  61. 2 1
      fs-service-system/src/main/java/com/fs/erp/domain/ErpOrder.java
  62. 3 1
      fs-service-system/src/main/java/com/fs/erp/domain/ErpRefundOrder.java
  63. 72 0
      fs-service-system/src/main/java/com/fs/erp/domain/FsErpFinishPush.java
  64. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KbStockRequest.java
  65. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KbStockResponse.java
  66. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderD1Data.java
  67. 1 2
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderData.java
  68. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderRequest.java
  69. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderResponse.java
  70. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosRefundOrderD1Data.java
  71. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosRefundOrderData.java
  72. 1 1
      fs-service-system/src/main/java/com/fs/erp/domain/KingbosRefundOrderRequest.java
  73. 22 24
      fs-service-system/src/main/java/com/fs/erp/dto/sdk/wangdian/api/WdtClient.java
  74. 2 2
      fs-service-system/src/main/java/com/fs/erp/enums/KbOrderStatusEnum.java
  75. 18 13
      fs-service-system/src/main/java/com/fs/erp/service/impl/ErpGoodsServiceImpl.java
  76. 28 21
      fs-service-system/src/main/java/com/fs/erp/service/impl/ErpOrderServiceImpl.java
  77. 8 5
      fs-service-system/src/main/java/com/fs/erp/service/impl/ErpShopServiceImpl.java
  78. 8 5
      fs-service-system/src/main/java/com/fs/erp/service/impl/ErpUserServiceImpl.java
  79. 168 54
      fs-service-system/src/main/java/com/fs/erp/service/impl/K9OrderServiceImpl.java
  80. 92 0
      fs-service-system/src/main/java/com/fs/erp/service/impl/K9StockServiceImpl.java
  81. 14 2
      fs-service-system/src/main/java/com/fs/erp/service/impl/WdtErpOrderServiceImpl.java
  82. 2 2
      fs-service-system/src/main/java/com/fs/erp/utils/SignUtils.java
  83. 2 2
      fs-service-system/src/main/java/com/fs/erp/utils/UrlUtils.java
  84. 14 1
      fs-service-system/src/main/java/com/fs/fastGpt/service/AiHookService.java
  85. 27 1
      fs-service-system/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  86. 30 0
      fs-service-system/src/main/java/com/fs/his/config/FsErpConfig.java
  87. 3 3
      fs-service-system/src/main/java/com/fs/his/param/WxSendRedPacketParam.java
  88. 13 0
      fs-service-system/src/main/java/com/fs/his/utils/ConfigUtil.java
  89. 0 18
      fs-service-system/src/main/java/com/fs/kingbos/service/K9OrderService.java
  90. 0 19
      fs-service-system/src/main/java/com/fs/kingbos/service/K9StockService.java
  91. 0 49
      fs-service-system/src/main/java/com/fs/kingbos/service/impl/K9StockServiceImpl.java
  92. 2 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwExternalContactInfoMapper.java
  93. 4 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  94. 7 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  95. 19 0
      fs-service-system/src/main/java/com/fs/qw/mapper/QwWorkTaskMapper.java
  96. 20 0
      fs-service-system/src/main/java/com/fs/qw/param/QwWorkTaskListParam.java
  97. 3 1
      fs-service-system/src/main/java/com/fs/qw/service/IQwExternalContactInfoService.java
  98. 7 2
      fs-service-system/src/main/java/com/fs/qw/service/IQwExternalContactService.java
  99. 3 0
      fs-service-system/src/main/java/com/fs/qw/service/IQwWorkTaskService.java
  100. 5 0
      fs-service-system/src/main/java/com/fs/qw/service/impl/QwExternalContactInfoServiceImpl.java

+ 2 - 4
fs-admin/src/main/java/com/fs/course/controller/FsCourseQuestionBankController.java

@@ -3,7 +3,6 @@ package com.fs.course.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.ServletUtils;
@@ -13,7 +12,6 @@ import com.fs.core.web.service.TokenService;
 import com.fs.course.domain.FsCourseQuestionBank;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.service.IFsCourseQuestionBankService;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -57,8 +55,8 @@ public class FsCourseQuestionBankController extends BaseController
     @GetMapping("/export")
     public AjaxResult export(FsCourseQuestionBank fsCourseQuestionBank)
     {
-        List<FsCourseQuestionBank> list = fsCourseQuestionBankService.selectFsCourseQuestionBankListExport(fsCourseQuestionBank);
-        ExcelUtil<FsCourseQuestionBank> util = new ExcelUtil<FsCourseQuestionBank>(FsCourseQuestionBank.class);
+        List<FsCourseQuestionBankImportDTO> list = fsCourseQuestionBankService.exportData(fsCourseQuestionBank);
+        ExcelUtil<FsCourseQuestionBankImportDTO> util = new ExcelUtil<>(FsCourseQuestionBankImportDTO.class);
         return util.exportExcel(list, "题库数据");
     }
 

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

@@ -129,7 +129,7 @@ public class QwSopTempController extends BaseController
         qwSopTemp.setCreateBy(loginUser.getUser().getUserId().toString());
         int i = qwSopTempService.addNew(qwSopTemp);
         if(qwSopTemp.getSendType() == 11){
-            qwSopTempService.createSopTempRules(qwSopTemp);
+            new Thread(() -> qwSopTempService.createSopTempRules(qwSopTemp)).start();
         }
         return toAjax(i);
     }

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

@@ -24,6 +24,8 @@ 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.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.store.domain.*;
 import com.fs.store.dto.ExpressInfoDTO;
 import com.fs.store.dto.FsStoreCartDTO;
@@ -34,6 +36,7 @@ import com.fs.store.param.*;
 import com.fs.store.service.*;
 import com.fs.store.vo.*;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
@@ -72,13 +75,48 @@ public class FsStoreOrderController extends BaseController {
     @Autowired
     private IFsStoreOrderStatusService orderStatusService;
 
-    @Autowired
-    IErpOrderService erpOrderService;
     @Autowired
     IFsStorePaymentService paymentService;
 
     @Autowired
     private ICompanyMoneyLogsService moneyLogsService;
+    @Autowired
+    @Qualifier("erpOrderServiceImpl")
+    private IErpOrderService gyOrderService;
+
+    @Autowired
+    @Qualifier("wdtErpOrderServiceImpl")
+    private IErpOrderService wdtOrderService;
+
+    @Autowired
+    @Qualifier("k9OrderServiceImpl")
+    private IErpOrderService k9OrderService;
+    @Autowired
+    private ConfigUtil configUtil;
+
+    private IErpOrderService getErpService(){
+        //判断是否开启erp
+        IErpOrderService erpOrderService = null;
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        Integer erpOpen = erpConfig.getErpOpen();
+        if (erpOpen != null && erpOpen == 1) {
+            //判断erp类型
+            Integer erpType = erpConfig.getErpType();
+            if (erpType != null) {
+                if (erpType == 1) {
+                    //管易
+                    erpOrderService = gyOrderService;
+                } else if (erpType == 2) {
+                    //旺店通
+                    erpOrderService = wdtOrderService;
+                } else if (erpType == 3) {
+                    //旺店通
+                    erpOrderService = k9OrderService;
+                }
+            }
+        }
+        return erpOrderService;
+    }
 
     /**
      * 查询订单列表
@@ -391,6 +429,7 @@ public class FsStoreOrderController extends BaseController {
     @PreAuthorize("@ss.hasPermi('store:storeOrder:updateErpOrder')")
     @PostMapping("/updateErpOrder")
     public R updateErpOrder(@Validated @RequestBody FsStoreOrderExpressEditParam param) {
+        IErpOrderService erpOrderService = getErpService();
         FsStoreOrder order = fsStoreOrderService.selectFsStoreOrderById(param.getOrderId());
         ErpOrderQueryRequert request = new ErpOrderQueryRequert();
         request.setCode(order.getExtendOrderId());
@@ -482,6 +521,7 @@ public class FsStoreOrderController extends BaseController {
     @PreAuthorize("@ss.hasPermi('store:storeOrder:getEroOrder')")
     @GetMapping("/getEroOrder")
     public R getEroOrder(@RequestParam("extendOrderId") String extendOrderId) {
+        IErpOrderService erpOrderService = getErpService();
         ErpOrderQueryRequert request = new ErpOrderQueryRequert();
         request.setCode(extendOrderId);
         ErpOrderQueryResponse response = erpOrderService.getOrder(request);

+ 56 - 2
fs-admin/src/main/java/com/fs/task/StoreTask.java

@@ -5,6 +5,8 @@ import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.company.service.ICompanyService;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpGoods;
@@ -15,6 +17,8 @@ import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.service.IErpGoodsService;
 import com.fs.erp.service.IErpOrderService;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.pay.pay.domain.OrderResult;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.service.PayService;
@@ -32,6 +36,7 @@ import com.fs.store.service.*;
 import com.fs.system.service.ISysConfigService;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
@@ -64,8 +69,6 @@ public class StoreTask
     @Autowired
     private IFsUserService userService;
 
-    @Autowired
-    private IErpOrderService erpOrderService;
 
     @Autowired
     private PayService ybPayService;
@@ -105,6 +108,24 @@ public class StoreTask
     @Autowired
     private IFsStoreAfterSalesService fsStoreAfterSalesService;
 
+    @Autowired
+    @Qualifier("erpOrderServiceImpl")
+    private IErpOrderService gyOrderService;
+
+    @Autowired
+    @Qualifier("wdtErpOrderServiceImpl")
+    private IErpOrderService wdtOrderService;
+
+    @Autowired
+    @Qualifier("k9OrderServiceImpl")
+    private IErpOrderService k9OrderService;
+
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
+
     public void PushErp() throws ParseException {
         List<Long> ids = fsStoreOrderMapper.selectFsStoreOrderNoCreateOms();
         for (Long id : ids) {
@@ -114,10 +135,18 @@ public class StoreTask
 
     }
 
+    public void redPacketSubMoney() throws Exception
+    {
+        List<RedPacketMoneyVO> redPacketMoneyVOS = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByCompany();
+        for (RedPacketMoneyVO redPacketMoneyVO : redPacketMoneyVOS) {
+            companyService.subtractCompanyMoney(redPacketMoneyVO.getMoney(),redPacketMoneyVO.getCompanyId());
+        }
+    }
 
     //每5分钟执行一次
     public void deliveryOp()
     {
+        IErpOrderService erpOrderService = getErpOrderService();
         Set<String> orders=redisTemplate.keys(DELIVERY+":*");
         for(String extndOrderId:orders){
             String orderCode=(String)redisTemplate.opsForValue().get(extndOrderId);
@@ -230,6 +259,7 @@ public class StoreTask
     }
 
     public void returnDeliveryId(){
+        IErpOrderService erpOrderService = getErpOrderService();
         List<String> list = fsStoreOrderMapper.selectErpCode();
         for (String s : list) {
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
@@ -373,6 +403,30 @@ public class StoreTask
 
     }
 
+    private IErpOrderService getErpOrderService(){
+        //判断是否开启erp
+        IErpOrderService erpOrderService = null;
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        Integer erpOpen = erpConfig.getErpOpen();
+        if (erpOpen != null && erpOpen == 1) {
+            //判断erp类型
+            Integer erpType = erpConfig.getErpType();
+            if (erpType != null) {
+                if (erpType == 1) {
+                    //管易
+                    erpOrderService = gyOrderService;
+                } else if (erpType == 2) {
+                    //旺店通
+                    erpOrderService = wdtOrderService;
+                } else if (erpType == 3) {
+                    //旺店通
+                    erpOrderService = k9OrderService;
+                }
+            }
+        }
+        return erpOrderService;
+    }
+
 
 
 

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

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

+ 0 - 1
fs-common/pom.xml

@@ -51,7 +51,6 @@
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
-            <version>2.10.5.1</version>
         </dependency>
 
         <!-- 阿里JSON解析器 -->

+ 106 - 106
fs-common/src/main/java/com/fs/common/config/FSSysConfig.java

@@ -28,12 +28,12 @@ public class FSSysConfig
     String manuId;
     String callbackUrl;
     //erp接口
-    Integer erpOpen;//是否开启ERP
-    String erpAppKey;
-    String erpSessionKey;
-    String erpSecret;
-    String erpUrl;
-    String erpShopCode;//店铺CODE
+//    Integer erpOpen;//是否开启ERP
+//    String erpAppKey;
+//    String erpSessionKey;
+//    String erpSecret;
+//    String erpUrl;
+//    String erpShopCode;//店铺CODE
     //支付接口
     Integer payOpen;//是否开启
     String payPartnerId;
@@ -42,12 +42,12 @@ public class FSSysConfig
     String payNotifyUrl;
     String refundNotifyUrl;
     //金博erp
-    private String kingbosan;//账套名称
-    private String kingbosSecret;//金博密钥
-    private String kingbosUrl;//金博地址
-    private String corgid;//机构编码
-    private String cwarehouseCode;
-    private String cwarehouseName;
+//    private String kingbosan;//账套名称
+//    private String kingbosSecret;//金博密钥
+//    private String kingbosUrl;//金博地址
+//    private String corgid;//机构编码
+//    private String cwarehouseCode;
+//    private String cwarehouseName;
 
     public String getKdnId() {
         return kdnId;
@@ -145,53 +145,53 @@ public class FSSysConfig
         this.callbackUrl = callbackUrl;
     }
 
-    public Integer getErpOpen() {
-        return erpOpen;
-    }
-
-    public void setErpOpen(Integer erpOpen) {
-        this.erpOpen = erpOpen;
-    }
-
-    public String getErpAppKey() {
-        return erpAppKey;
-    }
-
-    public void setErpAppKey(String erpAppKey) {
-        this.erpAppKey = erpAppKey;
-    }
-
-    public String getErpSessionKey() {
-        return erpSessionKey;
-    }
-
-    public void setErpSessionKey(String erpSessionKey) {
-        this.erpSessionKey = erpSessionKey;
-    }
-
-    public String getErpSecret() {
-        return erpSecret;
-    }
-
-    public void setErpSecret(String erpSecret) {
-        this.erpSecret = erpSecret;
-    }
-
-    public String getErpUrl() {
-        return erpUrl;
-    }
-
-    public void setErpUrl(String erpUrl) {
-        this.erpUrl = erpUrl;
-    }
-
-    public String getErpShopCode() {
-        return erpShopCode;
-    }
-
-    public void setErpShopCode(String erpShopCode) {
-        this.erpShopCode = erpShopCode;
-    }
+//    public Integer getErpOpen() {
+//        return erpOpen;
+//    }
+//
+//    public void setErpOpen(Integer erpOpen) {
+//        this.erpOpen = erpOpen;
+//    }
+//
+//    public String getErpAppKey() {
+//        return erpAppKey;
+//    }
+//
+//    public void setErpAppKey(String erpAppKey) {
+//        this.erpAppKey = erpAppKey;
+//    }
+//
+//    public String getErpSessionKey() {
+//        return erpSessionKey;
+//    }
+//
+//    public void setErpSessionKey(String erpSessionKey) {
+//        this.erpSessionKey = erpSessionKey;
+//    }
+//
+//    public String getErpSecret() {
+//        return erpSecret;
+//    }
+//
+//    public void setErpSecret(String erpSecret) {
+//        this.erpSecret = erpSecret;
+//    }
+//
+//    public String getErpUrl() {
+//        return erpUrl;
+//    }
+//
+//    public void setErpUrl(String erpUrl) {
+//        this.erpUrl = erpUrl;
+//    }
+//
+//    public String getErpShopCode() {
+//        return erpShopCode;
+//    }
+//
+//    public void setErpShopCode(String erpShopCode) {
+//        this.erpShopCode = erpShopCode;
+//    }
 
     public Integer getPayOpen() {
         return payOpen;
@@ -241,51 +241,51 @@ public class FSSysConfig
         this.refundNotifyUrl = refundNotifyUrl;
     }
 
-    public String getKingbosan() {
-        return kingbosan;
-    }
-
-    public void setKingbosan(String kingbosan) {
-        this.kingbosan = kingbosan;
-    }
-
-    public String getCorgid() {
-        return corgid;
-    }
-
-    public void setCorgid(String corgid) {
-        this.corgid = corgid;
-    }
-
-    public String getKingbosUrl() {
-        return kingbosUrl;
-    }
-
-    public void setKingbosUrl(String kingbosUrl) {
-        this.kingbosUrl = kingbosUrl;
-    }
-
-    public String getKingbosSecret() {
-        return kingbosSecret;
-    }
-
-    public void setKingbosSecret(String kingbosSecret) {
-        this.kingbosSecret = kingbosSecret;
-    }
-
-    public String getCwarehouseCode() {
-        return cwarehouseCode;
-    }
-
-    public void setCwarehouseCode(String cwarehouseCode) {
-        this.cwarehouseCode = cwarehouseCode;
-    }
-
-    public String getCwarehouseName() {
-        return cwarehouseName;
-    }
-
-    public void setCwarehouseName(String cwarehouseName) {
-        this.cwarehouseName = cwarehouseName;
-    }
+//    public String getKingbosan() {
+//        return kingbosan;
+//    }
+//
+//    public void setKingbosan(String kingbosan) {
+//        this.kingbosan = kingbosan;
+//    }
+//
+//    public String getCorgid() {
+//        return corgid;
+//    }
+//
+//    public void setCorgid(String corgid) {
+//        this.corgid = corgid;
+//    }
+//
+//    public String getKingbosUrl() {
+//        return kingbosUrl;
+//    }
+//
+//    public void setKingbosUrl(String kingbosUrl) {
+//        this.kingbosUrl = kingbosUrl;
+//    }
+//
+//    public String getKingbosSecret() {
+//        return kingbosSecret;
+//    }
+//
+//    public void setKingbosSecret(String kingbosSecret) {
+//        this.kingbosSecret = kingbosSecret;
+//    }
+//
+//    public String getCwarehouseCode() {
+//        return cwarehouseCode;
+//    }
+//
+//    public void setCwarehouseCode(String cwarehouseCode) {
+//        this.cwarehouseCode = cwarehouseCode;
+//    }
+//
+//    public String getCwarehouseName() {
+//        return cwarehouseName;
+//    }
+//
+//    public void setCwarehouseName(String cwarehouseName) {
+//        this.cwarehouseName = cwarehouseName;
+//    }
 }

+ 17 - 5
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -5,11 +5,13 @@ import com.fs.app.config.ImageStorageConfig;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.utils.StringUtils;
+import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.param.newfs.FsCourseSortLinkParam;
 import com.fs.course.param.newfs.FsUserCourseListParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.vo.FsUserCourseParticipationRecordVO;
@@ -25,8 +27,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
-
-import java.io.File;
 import java.io.InputStream;
 import java.util.HashMap;
 import java.util.List;
@@ -51,6 +51,9 @@ public class FsUserCourseVideoController extends AppBaseController {
     @Autowired
     private ImageStorageConfig imageConfig;
 
+    @Autowired
+    private IFsUserCoursePeriodService fsUserCoursePeriodService;
+
     @Login
     @GetMapping("/pageList")
     @ApiOperation("课程分页列表")
@@ -141,10 +144,19 @@ public class FsUserCourseVideoController extends AppBaseController {
             log.info("获取的logo图片路径,fileUrl:{}", path);
             InputStream inputStream = fsUserCourseService.handleImage("", path);
 
-            if (StringUtils.isEmpty(param.getImgUrl())) {
-                return R.error(400, "课程封面不能为空!");
+            // 获取营期的课程风格url
+            String imgUrl;
+            FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(param.getPeriodId());
+            if (fsUserCoursePeriod != null) {
+                imgUrl = fsUserCoursePeriod.getCourseStyle();
+            } else {
+                imgUrl = param.getImgUrl();
             }
-            String base64Image = fsUserCourseService.createCourseImageQR(realLink, param.getImgUrl(), inputStream, "png", param.getTitle(), param.getDuration());
+            if(StringUtils.isEmpty(imgUrl)){
+                return R.error(400, "营期风格图片或课程封面不能为空!");
+            }
+
+            String base64Image = fsUserCourseService.createCourseImageQR(realLink, imgUrl, inputStream, "png", param.getTitle(), param.getDuration());
             // 返回Base64编码的图片字符串
             Map<String, Object> map = new HashMap<>();
             map.put("url", base64Image);

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

@@ -4,5 +4,7 @@ server:
 
 spring:
   profiles:
-    active: dev
-    include: common,config-dev
+#    active: dev
+#    include: common,config-dev
+    active: druid-fby
+    include: common,config-fby

+ 93 - 0
fs-company-app/src/main/resources/logback.xml

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

+ 115 - 0
fs-company/src/main/java/com/fs/crm/controller/CrmCustomerAssistController.java

@@ -0,0 +1,115 @@
+package com.fs.crm.controller;
+
+import java.util.List;
+
+import com.fs.crm.param.CrmCustomerAssistDeLParam;
+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.crm.domain.CrmCustomerAssist;
+import com.fs.crm.service.ICrmCustomerAssistService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 客户员工协作Controller
+ *
+ * @author fs
+ * @date 2025-05-27
+ */
+@RestController
+@RequestMapping("/crm/assist")
+public class CrmCustomerAssistController extends BaseController
+{
+    @Autowired
+    private ICrmCustomerAssistService crmCustomerAssistService;
+
+    /**
+     * 查询客户员工协作列表
+     */
+    @PreAuthorize("@ss.hasPermi('crm:assist:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CrmCustomerAssist crmCustomerAssist)
+    {
+        startPage();
+        List<CrmCustomerAssist> list = crmCustomerAssistService.selectCrmCustomerAssistList(crmCustomerAssist);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出客户员工协作列表
+     */
+    @PreAuthorize("@ss.hasPermi('crm:assist:export')")
+    @Log(title = "客户员工协作", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CrmCustomerAssist crmCustomerAssist)
+    {
+        List<CrmCustomerAssist> list = crmCustomerAssistService.selectCrmCustomerAssistList(crmCustomerAssist);
+        ExcelUtil<CrmCustomerAssist> util = new ExcelUtil<CrmCustomerAssist>(CrmCustomerAssist.class);
+        return util.exportExcel(list, "assist");
+    }
+
+    /**
+     * 获取客户员工协作详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('crm:assist:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(crmCustomerAssistService.selectCrmCustomerAssistById(id));
+    }
+
+    /**
+     * 新增客户员工协作
+     */
+    @PreAuthorize("@ss.hasPermi('crm:assist:add')")
+    @Log(title = "客户员工协作", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody CrmCustomerAssist crmCustomerAssist)
+    {
+        return toAjax(crmCustomerAssistService.insertCrmCustomerAssist(crmCustomerAssist));
+    }
+
+    /**
+     * 修改客户员工协作
+     */
+    @PreAuthorize("@ss.hasPermi('crm:assist:edit')")
+    @Log(title = "客户员工协作", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CrmCustomerAssist crmCustomerAssist)
+    {
+        return toAjax(crmCustomerAssistService.updateCrmCustomerAssist(crmCustomerAssist));
+    }
+
+    /**
+     * 删除客户员工协作
+     */
+    @Log(title = "客户员工协作", businessType = BusinessType.DELETE)
+    @PostMapping("/remove")
+    public AjaxResult removeByCustomer(@RequestBody CrmCustomerAssistDeLParam param)
+    {
+        return toAjax(crmCustomerAssistService.removeByCustomer(param));
+    }
+
+    /**
+     * 删除客户员工协作
+     */
+    @PreAuthorize("@ss.hasPermi('crm:assist:remove')")
+    @Log(title = "客户员工协作", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(crmCustomerAssistService.deleteCrmCustomerAssistByIds(ids));
+    }
+}

+ 36 - 0
fs-company/src/main/java/com/fs/crm/controller/CrmCustomerController.java

@@ -119,6 +119,29 @@ public class CrmCustomerController extends BaseController
 
     }
 
+    @ApiOperation("获取我的协作客户列表")
+    @PreAuthorize("@ss.hasPermi('crm:customer:assistList')")
+    @GetMapping("/getMyAssistList")
+    public TableDataInfo getMyAssistList(CrmMyCustomerListQueryParam param){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setCompanyUserId(loginUser.getUser().getUserId());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        if(!StringUtils.isEmpty(param.getCreateTimeRange())){
+            param.setCustomerCreateTime(param.getCreateTimeRange().split("--"));
+        }
+        List<CrmMyCustomerListQueryVO> list = crmCustomerService.selectCrmMyAssistListQuery(param);
+        if (list != null) {
+            for (CrmMyCustomerListQueryVO vo : list) {
+                if(vo.getMobile()!=null){
+                    vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                }
+            }
+        }
+        return getDataTable(list);
+
+    }
+
     @ApiOperation("获取客户列表")
     @GetMapping("/getCustomerList")
     @PreAuthorize("@ss.hasPermi('crm:customer:list')")
@@ -212,6 +235,19 @@ public class CrmCustomerController extends BaseController
         return crmCustomerService.assignToUser(loginUser.getUsername(),loginUser.getUser().getUserId(),param);
     }
 
+    //添加协作人
+    @PreAuthorize("@ss.hasPermi('crm:customer:assistToUser')")
+    @PostMapping("/assistToUser")
+    public R assistToUser(@RequestBody CrmCustomeAssignParam param)
+    {
+        if(param.getCustomerIds().size()>1000){
+            return R.error("分配数据超出范围,最大1000条");
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        return crmCustomerService.assistToUser(loginUser.getUsername(),loginUser.getUser().getUserId(),param);
+    }
+
 
     @PreAuthorize("@ss.hasPermi('crm:customer:add')")
     @Log(title = "创建客户", businessType = BusinessType.INSERT)

+ 103 - 0
fs-company/src/main/java/com/fs/qw/QwMsgController.java

@@ -6,18 +6,32 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
+import com.fs.course.param.FsCourseListBySidebarParam;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsCourseListBySidebarVO;
+import com.fs.course.vo.FsCourseVideoListBySidebarVO;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwExternalContactInfo;
 import com.fs.qw.domain.QwMsg;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.param.QwMsgSendParam;
 import com.fs.qw.param.QwSessionParam;
+import com.fs.qw.service.IQwExternalContactInfoService;
+import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwMsgService;
+import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.QwContactListVO;
 import com.fs.qw.vo.QwMessageListVO;
+import com.fs.statis.dto.WatchCourseStatisticsDTO;
+import com.fs.statis.param.WatchCourseStatisticsParam;
+import com.fs.statis.service.IStatisticsService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -44,6 +58,18 @@ public class QwMsgController extends BaseController
     private IQwMsgService qwMsgService;
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private IQwExternalContactInfoService qwExternalContactInfoService;
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+    @Autowired
+    private IQwUserService qwUserService;
+    @Autowired
+    private IStatisticsService statisticsService;
+    @Autowired
+    private IFsUserCourseService fsUserCourseService;
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
 
     /**
      * 查询企微聊天记录列表
@@ -160,4 +186,81 @@ public class QwMsgController extends BaseController
         QwContactListVO data = qwMsgService.selectQwSessionBycId(param.getConversationId(),param.getUserId());
         return R.ok().put("data",data);
     }
+
+    @GetMapping("/getQwExternalContactDetails")
+    public R getQwExternalContactDetails(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId){
+        return R.ok().put("data", qwExternalContactService.getQwExternalContactDetailsById(qwExternalContactId));
+    }
+
+    @GetMapping("/getQwUserInfo")
+    @ApiOperation("获取企微用户信息")
+    public R getQwUserInfo(@RequestParam(value = "qwExternalContactId") Long qwExternalContactId){
+        if(qwExternalContactId == null) {
+            throw new CustomException("企微外部联系人id不能为空!");
+        }
+        QwExternalContactInfo contactInfo = qwExternalContactInfoService.selectQwExternalContactInfoByExternalContactId(qwExternalContactId);
+        if (contactInfo==null){
+
+            contactInfo = new QwExternalContactInfo();
+            contactInfo.setExternalContactId(qwExternalContactId);
+            qwExternalContactInfoService.insertQwExternalContactInfo(contactInfo);
+
+        }
+
+        return R.ok().put("moreInfo",contactInfo);
+    }
+
+    @PostMapping("/updateQwUserInfo")
+    @ApiOperation("更新企微用户信息")
+    public R updateQwUserInfo(@RequestBody QwExternalContactInfo qwExternalContactInfo){
+        if(qwExternalContactInfo.getExternalContactId() == null) {
+            throw new CustomException("企微外部联系人id不能为空!");
+        }
+        qwExternalContactInfoService.updateQwExternalContactInfoByExternalContactId(qwExternalContactInfo);
+        return R.ok();
+    }
+
+    @PostMapping("/course/watch")
+    @ApiOperation("查询看课记录")
+    public R queryCourseWatchStatistics(@RequestBody WatchCourseStatisticsParam param) {
+        if(param.getQwExternalContactId() == null) {
+            throw new CustomException("外部联系人id为空!");
+        }
+
+        WatchCourseStatisticsDTO watchCourseStatisticsDTO = statisticsService.queryWatchCourse(param);
+
+        return R.ok().put("data",watchCourseStatisticsDTO);
+    }
+
+    @PostMapping("/getFsCourseListBySidebar")
+    @ApiOperation("获取视频课程下拉列表 侧边栏")
+    public R getFsCourseListBySidebar(@RequestBody FsCourseListBySidebarParam param) {
+
+        QwExternalContact externalContact = qwExternalContactService.getById(param.getExtId());
+        QwUser qwUser = qwUserService.selectQwUserById(externalContact.getQwUserId());
+
+        if (qwUser == null || qwUser.getCompanyId() == null) {
+            return R.error("员工未绑定 销售公司 或 未获取到员工信息,请重试!");
+        }
+        param.setCompanyId(qwUser.getCompanyId());
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseListBySidebarVO> fsCourseListBySidebar = fsUserCourseService.getFsCourseListBySidebar(param);
+        PageInfo<FsCourseListBySidebarVO> result = new PageInfo<>(fsCourseListBySidebar);
+        return R.ok().put("data", result);
+    }
+
+    @PostMapping("/getFsCourseVideoListBySidebar")
+    @ApiOperation("获取视频课程的课节下拉列表 侧边栏")
+    public R getFsCourseVideoListBySidebar(@RequestBody FsCourseListBySidebarParam param) {
+
+        if (param.getCourseId()==null){
+            return R.error("课程id不能为空");
+        }
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseVideoListBySidebarVO> videoListBySidebar = fsUserCourseVideoService.getFsCourseVideoListBySidebar(param);
+        PageInfo<FsCourseVideoListBySidebarVO> result = new PageInfo<>(videoListBySidebar);
+        return R.ok().put("data", result);
+    }
 }

+ 1 - 0
fs-company/src/main/java/com/fs/qw/QwQwWorkTaskController.java

@@ -17,6 +17,7 @@ import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
 import com.fs.qw.service.IHyWorkTaskService;
 import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskAllListVO;
 import com.fs.qw.vo.QwWorkTaskListVO;
 import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;

+ 1 - 1
fs-company/src/main/java/com/fs/qw/QwSopTempController.java

@@ -137,7 +137,7 @@ public class QwSopTempController extends BaseController
         qwSopTemp.setCreateBy(loginUser.getUser().getUserId().toString());
         int i = qwSopTempService.addNew(qwSopTemp);
         if(qwSopTemp.getSendType() == 11){
-            qwSopTempService.createSopTempRules(qwSopTemp);
+            new Thread(() -> qwSopTempService.createSopTempRules(qwSopTemp)).start();
         }
         return toAjax(i);
     }

+ 28 - 0
fs-company/src/main/java/com/fs/qw/qw/QwWorkTaskController.java

@@ -14,6 +14,7 @@ import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
 import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskAllListVO;
 import com.fs.qw.vo.QwWorkTaskListVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -58,6 +59,33 @@ public class QwWorkTaskController extends BaseController
         return getDataTable(list);
     }
 
+    @GetMapping("/glList")
+    public TableDataInfo glList(QwWorkTaskListParam qwWorkTask)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
+        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+        }
+        return getDataTable(list);
+    }
+
+    @GetMapping("/allList")
+    public TableDataInfo allList(QwWorkTaskListParam qwWorkTask)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+        if (qwWorkTask.getSTime()==null||qwWorkTask.getETime()==null){
+            return new TableDataInfo();
+        }
+        List<QwWorkTaskAllListVO> list = qwWorkTaskService.selectQwWorkTaskAllListVO(qwWorkTask);
+
+        return getDataTable(list);
+    }
+
     /**
      * 导出企微任务看板列表
      */

+ 0 - 34
fs-company/src/main/java/com/fs/qw/vo/QwMessageListVO.java

@@ -1,34 +0,0 @@
-package com.fs.qw.vo;
-
-import com.fs.qw.domain.QWFromUser;
-import com.fs.qw.domain.QwMsg;
-import lombok.Data;
-
-@Data
-public class QwMessageListVO {
-    private String id;
-    private String status;
-    private String type;
-    private Long sendTime;
-    private String content;
-    private String url;
-    private Long fileSize;
-    private String fileName;
-    private Integer fileShowType;
-    private String imageUrl;
-    private Integer duration; //时长
-    private String toContactId;
-    private QWFromUser fromUser;
-
-    //获取fromUser
-    public  QWFromUser getQwFromUser(Long senderId,QwMsg qwMsg){
-        QWFromUser fromUser=new QWFromUser();
-        fromUser.setId(senderId);
-        if(qwMsg!=null){
-            fromUser.setAvatar(qwMsg.getAvatar());
-            fromUser.setDisplayName(qwMsg.getAvatar());
-        }
-        return fromUser;
-    }
-
-}

+ 0 - 5
fs-company/src/main/java/com/fs/users/controller/FsUserController.java

@@ -140,9 +140,4 @@ public class FsUserController extends BaseController
         Boolean r = fsUserService.disabledUser(ids, true);
         return ResponseResult.ok(r);
     }
-
-    @GetMapping("/getUserInfoBySessionId")
-    public R getUserInfoBySessionId(@RequestParam Long sessionId) {
-        return R.ok().put("data", fsUserService.getUserInfoBySessionId(sessionId));
-    }
 }

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

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

+ 68 - 4
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -2,7 +2,9 @@ package com.fs.app.controller;
 
 import com.alibaba.fastjson.JSON;
 import com.fs.app.socket.QwImSocket;
+import com.fs.app.util.AudioUtils;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.uuid.IdUtils;
 import com.fs.fastGpt.service.AiHookService;
 import com.fs.qw.domain.QwUser;
@@ -182,10 +184,14 @@ public class QwMsgController {
                         aiHookService.qwHookNotifyAddMsg(id,receiver,content,wxWorkMsgResp.getUuid());
                     }
 
-                    // 保存聊天消息
-                    QwMessageListVO message = aiHookService.saveQwMsg(id, userId, content, wxWorkMsgResp.getUuid(), sendType, wxWorkMsgResp.getJson(), 1);
-                    QwImSocket.broadcast(message);
-
+                    // 处理文本消息
+                    if (wxWorkMessageDTO.getMsgtype() == 2 || wxWorkMessageDTO.getMsgtype() == 0) {
+                        processTextMessage(id, userId, content, wxWorkMsgResp, sendType);
+                    }
+                    // 语音消息
+                    if (wxWorkMessageDTO.getMsgtype() == 16) {
+                        processVoiceMessage(serverId, content, wxWorkMessageDTO, wxWorkMsgResp, id, userId, sendType);
+                    }
                 }
                 //语音通话
                 if (wxWorkMessageDTO.getMsgtype()==40){
@@ -242,6 +248,64 @@ public class QwMsgController {
         return map;
     }
 
+    /**
+     * 处理文本消息
+     * @param id                企微用户ID
+     * @param userId            消息发送者ID
+     * @param content           消息内容
+     * @param wxWorkMsgResp     回调信息对象
+     * @param sendType          发送者类型 1客户 2销售
+     */
+    private void processTextMessage(Long id, Long userId, String content, WxWorkMsgResp wxWorkMsgResp, Integer sendType) {
+        // 保存聊天消息
+        QwMessageListVO message = aiHookService.saveQwMsg(id, userId, content, wxWorkMsgResp.getUuid(), sendType, wxWorkMsgResp.getJson(), 1);
+        QwImSocket.broadcast(message);
+    }
+
+    /**
+     * 处理语音消息
+     * @param serverId          服务器ID
+     * @param wxWorkMessageDTO  消息DTO
+     * @param content           翻译后的内容
+     * @param wxWorkMsgResp     回调信息对象
+     * @param id                企微用户ID
+     * @param userId            消息发送者ID
+     * @param sendType          发送者类型 1客户 2销售
+     */
+    private void processVoiceMessage(Long serverId, String content, WxWorkMessageDTO wxWorkMessageDTO, WxWorkMsgResp wxWorkMsgResp, Long id, Long userId, Integer sendType) {
+        String voiceFileName = IdUtils.fastSimpleUUID() + ".silk";
+        WxWorkResponseDTO<String> fileUrlResp =
+                aiHookService.getFileUrl(wxWorkMsgResp.getUuid(), wxWorkMessageDTO.getVoice_id(), wxWorkMessageDTO.getAes_key(), 5, voiceFileName, wxWorkMessageDTO.getVoice_size(), serverId);
+        if (fileUrlResp.getErrcode() != 0) {
+            log.warn("获取语音地址失败: {}", fileUrlResp.getErrmsg());
+            return;
+        }
+
+        // silk转map3
+        String url = AudioUtils.convertSilk2Mp3(fileUrlResp.getData());
+        if (StringUtils.isBlank(url)) {
+            log.warn("转换silk语音格式失败");
+            return;
+        }
+
+        // 转换内容为空时再尝试一次
+        if (StringUtils.isBlank(content)) {
+            WxwSpeechToTextEntityDTO ste = new WxwSpeechToTextEntityDTO();
+            ste.setMsgid(wxWorkMessageDTO.getMsg_id());
+            ste.setUuid(wxWorkMsgResp.getUuid());
+            WxWorkResponseDTO<WxwSpeechToTextEntityRespDTO> dto = wxWorkService.SpeechToTextEntity(ste, serverId);
+            content = dto.getData().getText();
+        }
+
+        JSONObject json = new JSONObject();
+        json.put("url", url);
+        json.put("content", content);
+
+        // 保存聊天消息
+        QwMessageListVO message = aiHookService.saveQwMsg(id, userId, json.toString(), wxWorkMsgResp.getUuid(), sendType, wxWorkMsgResp.getJson(), 4);
+        QwImSocket.broadcast(message);
+    }
+
     /**
      * 处理图片消息
      * @param serverId          服务器ID

+ 160 - 0
fs-qw-api-msg/src/main/java/com/fs/app/util/AudioUtils.java

@@ -0,0 +1,160 @@
+package com.fs.app.util;
+
+import com.fs.common.utils.uuid.IdUtils;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.springframework.http.HttpStatus;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class AudioUtils {
+
+    /**
+     * silk转换为mp3
+     * @param silkUrl silk语音链接地址
+     * @return  mp3链接地址
+     */
+    public static String convertSilk2Mp3(String silkUrl) {
+        String uniqueId = IdUtils.fastSimpleUUID();
+        Path uploadDirPath = Paths.get(System.getProperty("java.io.tmpdir"), "/");
+        Path downloadedSilkFilePath = uploadDirPath.resolve(uniqueId + ".silk");
+        Path pcmFilePath = uploadDirPath.resolve(uniqueId + ".pcm");
+        Path mp3FilePath = uploadDirPath.resolve(uniqueId + ".mp3");
+
+        try {
+            // 1. 从网络下载 SILK 文件
+            downloadFile(silkUrl, downloadedSilkFilePath);
+            log.info("SILK file downloaded to: {}", downloadedSilkFilePath);
+
+            // 2. 使用 silk-v3-decoder 解码 SILK 到 PCM
+            List<String> silkDecodeCommand = new ArrayList<>();
+            silkDecodeCommand.add("silk_v3_decoder");
+            silkDecodeCommand.add(downloadedSilkFilePath.toString());
+            silkDecodeCommand.add(pcmFilePath.toString());
+
+            ProcessBuilder silkDecoderPb = new ProcessBuilder(silkDecodeCommand);
+            silkDecoderPb.redirectErrorStream(true); // 将错误流合并到标准输出
+            Process silkDecoderProcess = silkDecoderPb.start();
+
+            String silkDecoderOutput = readInputStreamToString(silkDecoderProcess.getInputStream());
+
+            boolean silkDecoderExited = silkDecoderProcess.waitFor(60, TimeUnit.SECONDS);
+            if (!silkDecoderExited || silkDecoderProcess.exitValue() != 0) {
+                log.error("silk conversion failed or timed out. error: {}", silkDecoderOutput);
+                return null;
+            }
+            log.info("SILK decoder to PCM successfully.");
+
+            // 3. 使用 FFmpeg 将 PCM 转码为 MP3
+            Process ffmpegProcess = getFfmpegProcess(pcmFilePath, mp3FilePath);
+            String ffmpegOutput = readInputStreamToString(ffmpegProcess.getInputStream());
+
+            boolean ffmpegExited = ffmpegProcess.waitFor(120, TimeUnit.SECONDS);
+            if (!ffmpegExited || ffmpegProcess.exitValue() != 0) {
+                log.error("ffmpeg conversion failed or timed out. error: {}", ffmpegOutput);
+                return null;
+            }
+            log.info("ffmpeg conversion to MP3 successfully.");
+
+            // 4. 上传oss
+            String fileName = mp3FilePath.getFileName().toString();
+            String suffix = fileName.substring(fileName.lastIndexOf("."));
+            CloudStorageService storage = OSSFactory.build();
+            return storage.uploadSuffix(Files.newInputStream(mp3FilePath), suffix);
+
+        } catch (IOException | InterruptedException | NullPointerException e) {
+            log.error("Conversion error: {}", e.getMessage());
+            return null;
+        } finally {
+            // 清理临时文件 (重要!)
+            try {
+                if (Files.exists(downloadedSilkFilePath)) Files.delete(downloadedSilkFilePath);
+                if (Files.exists(pcmFilePath)) Files.delete(pcmFilePath);
+                if (Files.exists(mp3FilePath)) Files.delete(mp3FilePath);
+            } catch (IOException e) {
+                log.error("Error cleaning up temporary files:: {}", e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * 执行ffmpeg
+     * @param pcmFilePath   pcm文件
+     * @param mp3FilePath   mp3地址
+     * @return  process
+     * @throws IOException exception
+     */
+    private static Process getFfmpegProcess(Path pcmFilePath, Path mp3FilePath) throws IOException {
+        List<String> ffmpegCommand = new ArrayList<>();
+        ffmpegCommand.add("ffmpeg");
+        ffmpegCommand.add("-y");
+        ffmpegCommand.add("-f");
+        ffmpegCommand.add("s16le");
+        ffmpegCommand.add("-ar");
+        ffmpegCommand.add("24000"); // 注意:这里假设是 24kHz,如果你的 SILK 文件是其他采样率,请调整
+        ffmpegCommand.add("-ac");
+        ffmpegCommand.add("1");
+        ffmpegCommand.add("-i");
+        ffmpegCommand.add(pcmFilePath.toString());
+        ffmpegCommand.add(mp3FilePath.toString());
+
+        ProcessBuilder ffmpegPb = new ProcessBuilder(ffmpegCommand);
+        ffmpegPb.redirectErrorStream(true);
+        return ffmpegPb.start();
+    }
+
+    /**
+     * 处理文件流
+     * @param is 输入流
+     * @return  输出
+     * @throws IOException exception
+     */
+    private static String readInputStreamToString(InputStream is) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int len;
+        while ((len = is.read(buffer)) != -1) {
+            bos.write(buffer, 0, len);
+        }
+        return bos.toString("UTF-8"); // 使用 UTF-8 编码
+    }
+
+    /**
+     * 下载网络文件
+     * @param fileUrl       网络文件
+     * @param destination   临时文件
+     * @throws IOException  exception
+     */
+    private static void downloadFile(String fileUrl, Path destination) throws IOException {
+        try (CloseableHttpClient httpClient = HttpClients.createDefault();
+             CloseableHttpResponse response = httpClient.execute(new HttpGet(fileUrl));
+             InputStream inputStream = response.getEntity().getContent();
+             FileOutputStream outputStream = new FileOutputStream(destination.toFile())) {
+
+            if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
+                throw new IOException("Failed to download file from " + fileUrl + ", HTTP Status: " + response.getStatusLine().getStatusCode());
+            }
+
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+        }
+    }
+}

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

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

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

@@ -23,6 +23,9 @@ public class CourseWatchLogScheduler {
     private final AtomicBoolean isRunning2 = new AtomicBoolean(false);
 
     private final AtomicBoolean isRunning3 = new AtomicBoolean(false);
+
+    private final AtomicBoolean isRunning4 = new AtomicBoolean(false);
+
     @Autowired
     private FsCourseWatchLogMapper courseWatchLogMapper;
 
@@ -126,7 +129,7 @@ public class CourseWatchLogScheduler {
     @Scheduled(fixedRate = 60000) // 每分钟执行一次
     public void checkFsUserWatchStatus() {
         // 尝试设置标志为 true,表示任务开始执行
-        if (!isRunning1.compareAndSet(false, true)) {
+        if (!isRunning4.compareAndSet(false, true)) {
             log.warn("WXH5-检查会员看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
             return;
         }
@@ -139,7 +142,7 @@ public class CourseWatchLogScheduler {
             log.error("WXH5-检查会员看课中任务执行完成 - 定时任务执行失败", e);
         } finally {
             // 重置标志为 false,表示任务已完成
-            isRunning1.set(false);
+            isRunning4.set(false);
         }
 
     }

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

@@ -724,6 +724,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setExternalUserId(externalId);
         sopLogs.setExternalUserName(externalUserName);
         sopLogs.setFsUserId(fsUserId);
+        sopLogs.setUserLogsId(logVo.getId());
 
         return sopLogs;
     }

+ 93 - 0
fs-qw-task/src/main/resources/logback.xml

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

+ 93 - 0
fs-qwhook-msg/src/main/resources/logback.xml

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

+ 4 - 16
fs-qwhook-sop/src/main/java/com/fs/app/controller/testController.java

@@ -8,6 +8,7 @@ import com.fs.course.mapper.FsCourseSopMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.service.IFsCourseLinkService;
 import com.fs.crm.mapper.CrmCustomerMapper;
+import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.store.mapper.FsUserMapper;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
@@ -57,27 +58,14 @@ public class testController {
     testService testService;
     @Autowired
     private RedisCache redisCache;
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
 
     @GetMapping("/qwHookNotify")
     public R qwHookNotify() {
 
-//        redisCache.setCacheObject("qwUserRd:"+12313+":"+12313 ,JSON.toJSONString(null),1, TimeUnit.HOURS);
-
-//
-//        List<QwUser> qwUserAllKey = qwUserMapper.getQwUserAllKey();
-//
-//
-//        int i=1;
-//
-//        for (QwUser qwUser : qwUserAllKey) {
-//            System.out.println(qwUser);
-//            i++;
-//            System.out.println("执行到第:"+i);
-//            testService.add(qwUser);
-//
-//        }
-
 
+        iSopUserLogsInfoService.updateSopUserInfoByExternalId(218L,123456L);
         return  R.ok();
     }
 

+ 93 - 0
fs-qwhook-sop/src/main/resources/logback.xml

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

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

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

+ 5 - 3
fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java

@@ -16,6 +16,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
+import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -268,10 +269,11 @@ public interface CompanyUserMapper
      * 获取对应的销售观看记录数
      *
      * @param companyUserId
-     * @param periodId
      * @return
      */
-    Long queryCompanyUserWatchCount(@Param("companyUserId") Long companyUserId, @Param("periodId") Long periodId);
+    Long queryCompanyUserWatchCount(@Param("companyUserId") Long companyUserId,
+                                    @Param("previousDay") LocalDate previousDay);
 
-    Long queryCompanyUserWatchCountCompleted(@Param("companyUserId") Long companyUserId,@Param("periodId") Long periodId);
+    Long queryCompanyUserWatchCountCompleted(@Param("companyUserId") Long companyUserId,
+                                             @Param("previousDay") LocalDate previousDay);
 }

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

@@ -117,4 +117,5 @@ public interface ICompanyService
     List<OptionsVO> selectCompanyListByMap(Map<String, Object> params);
 
 
+    void subtractCompanyMoney(BigDecimal money, Long companyId);
 }

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

@@ -463,6 +463,26 @@ public class CompanyServiceImpl implements ICompanyService
         return companyMapper.selectCompanyListByMap(params);
     }
 
+    @Override
+    @Transactional
+    public void subtractCompanyMoney(BigDecimal money, Long companyId) {
+        if(companyId!=null&&companyId>0){
+            Company company=companyMapper.selectCompanyByIdForUpdate(companyId);
+            if(company!=null){
+                logger.info("扣除红包金额:"+money);
+                company.setMoney(company.getMoney().subtract(money));
+                companyMapper.updateCompany(company);
+                CompanyMoneyLogs log=new CompanyMoneyLogs();
+                log.setCompanyId(company.getCompanyId());
+                log.setRemark("扣除昨日红包金额");
+                log.setMoney(money.multiply(new BigDecimal(-1)));
+                log.setLogsType(15);
+                log.setBalance(company.getMoney());
+                log.setCreateTime(new Date());
+                moneyLogsMapper.insertCompanyMoneyLogs(log);
+            }
+        }
+    }
 
 
 }

+ 23 - 11
fs-service-system/src/main/java/com/fs/course/dto/FsCourseQuestionBankImportDTO.java

@@ -6,24 +6,36 @@ import lombok.Data;
 @Data
 public class FsCourseQuestionBankImportDTO {
 
-    @Excel(name = "标题")
+    @Excel(name = "标题(必填)")
     private String title;
 
-    @Excel(name = "问题类别")
+    @Excel(name = "序号(必填)")
+    private Long sort;
+
+    @Excel(name = "类别(必填)")
+    private String type;
+
+    @Excel(name = "题目类别(非必填)")
     private String questionType;
 
-    @Excel(name = "题目子类别")
+    @Excel(name = "题目子类别(非必填)")
     private String questionSubTyp;
 
-    @Excel(name = "题目类型: 单选 多选")
-    private String type;
+    @Excel(name = "状态(非必填)")
+    private String status;
 
-    @Excel(name = "选项: 多个用 | 隔开")
-    private String question;
+    @Excel(name = "选项A(必填)")
+    private String questionA;
 
-    @Excel(name = "答案: 多个用 | 隔开")
-    private String answer;
+    @Excel(name = "选项B(必填)")
+    private String questionB;
+
+    @Excel(name = "选项C(必填)")
+    private String questionC;
 
-    @Excel(name = "序号")
-    private long sort;
+    @Excel(name = "选项D(非必填)")
+    private String questionD;
+
+    @Excel(name = "答案(必填)")
+    private String answer;
 }

+ 11 - 0
fs-service-system/src/main/java/com/fs/course/dto/WatchLogDTO.java

@@ -0,0 +1,11 @@
+package com.fs.course.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class WatchLogDTO implements Serializable {
+    private String date;
+    private Integer logType;
+}

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

@@ -2,6 +2,7 @@ package com.fs.course.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.dto.WatchLogDTO;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
 import com.fs.sop.vo.QwRatingVO;
@@ -323,4 +324,61 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
     @Select("select * from fs_course_watch_log where user_id = #{userId} and video_id = #{videoId} and send_type = 1")
     FsCourseWatchLog getCourseWatchLogByUser(@Param("userId") Long userId, @Param("videoId") Long videoId);
+
+    @Select("WITH date_series AS (\n" +
+            "  SELECT DATE_SUB(CURRENT_DATE(), INTERVAL 6-n DAY) AS report_date\n" +
+            "  FROM (\n" +
+            "    SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 \n" +
+            "    UNION SELECT 4 UNION SELECT 5 UNION SELECT 6\n" +
+            "  ) days\n" +
+            "  ORDER BY n\n" +
+            "),\n" +
+            "daily_data AS (\n" +
+            "  SELECT \n" +
+            "    DATE(create_time) AS log_date,\n" +
+            "    log_type,\n" +
+            "    ROW_NUMBER() OVER (PARTITION BY DATE(create_time) ORDER BY create_time DESC) AS rn\n" +
+            "  FROM fs_course_watch_log\n" +
+            "  WHERE qw_external_contact_id = #{extId}\n" +
+            "    AND create_time >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)\n" +
+            ")\n" +
+            "SELECT \n" +
+            "ds.report_date AS date,"+
+            "  IFNULL(dd.log_type, 0) AS log_type\n" +
+            "FROM date_series ds\n" +
+            "LEFT JOIN (\n" +
+            "  SELECT log_date, log_type FROM daily_data WHERE rn = 1\n" +
+            ") dd ON ds.report_date = dd.log_date\n" +
+            "ORDER BY ds.report_date ASC  ")
+    List<WatchLogDTO> selectFsCourseWatchLog7Day(@Param("extId") Long extId);
+
+    @Select("WITH date_series AS (\n" +
+            "  SELECT DATE_SUB(CURRENT_DATE(), INTERVAL 29-n DAY) AS report_date\n" +
+            "  FROM (\n" +
+            "    SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 \n" +
+            "    UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 \n" +
+            "    UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION SELECT 15 UNION SELECT 16 UNION SELECT 17 \n" +
+            "    UNION SELECT 18 UNION SELECT 19 UNION SELECT 20 UNION SELECT 21 UNION SELECT 22 UNION SELECT 23 \n" +
+            "    UNION SELECT 24 UNION SELECT 25 UNION SELECT 26 UNION SELECT 27 UNION SELECT 28 UNION SELECT 29\n" +
+            "  ) days\n" +
+            "  ORDER BY n\n" +
+            "),\n" +
+            "daily_data AS (\n" +
+            "  SELECT \n" +
+            "    DATE(create_time) AS log_date,\n" +
+            "    log_type,\n" +
+            "    ROW_NUMBER() OVER (PARTITION BY DATE(create_time) ORDER BY create_time DESC) AS rn\n" +
+            "  FROM fs_course_watch_log\n" +
+            "  WHERE qw_external_contact_id = #{extId}\n" +
+            "    AND create_time >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)\n" +
+            ")\n" +
+            "SELECT \n" +
+            "  ds.report_date AS date,\n" +
+            "  IFNULL(dd.log_type, 0) AS log_type\n" +
+            "FROM date_series ds\n" +
+            "LEFT JOIN (\n" +
+            "  SELECT log_date, log_type FROM daily_data WHERE rn = 1\n" +
+            ") dd ON ds.report_date = dd.log_date\n" +
+            "ORDER BY ds.report_date ASC")
+    List<WatchLogDTO> selectFsCourseWatchLog30DayByExtId(@Param("extId") Long extId);
 }

+ 11 - 4
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java

@@ -1,14 +1,14 @@
 package com.fs.course.mapper;
 
-import java.util.List;
-import java.util.Map;
-
 import com.fs.course.domain.FsUserCourseCategory;
 import com.fs.his.vo.OptionsVO;
 import org.apache.ibatis.annotations.MapKey;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 课堂分类Mapper接口
  *
@@ -86,7 +86,14 @@ public interface FsUserCourseCategoryMapper
      * 查询所有分类
      * @return
      */
-    @Select("select cate_id,cate_name from fs_user_course_category where is_del=0")
+    @Select("select cate_id,cate_name, pid from fs_user_course_category where is_del=0")
     @MapKey("cateName")
     Map<String,FsUserCourseCategory> queryAllCategoryData();
+
+    /**
+     * 查询所有分类
+     */
+    @Select("select cate_id as cateId,cate_name as cateName from fs_user_course_category where is_del = 0")
+    @MapKey("cateId")
+    Map<Long, FsUserCourseCategory> queryAllIdKeyCategoryData();
 }

+ 8 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java

@@ -1,6 +1,7 @@
 package com.fs.course.mapper;
 
 import com.fs.course.domain.FsUserCourse;
+import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.course.param.FsUserCourseAddStudyCourseParam;
 import com.fs.course.param.FsUserCourseListUParam;
 import com.fs.course.param.FsUserCourseParam;
@@ -223,4 +224,11 @@ public interface FsUserCourseMapper
      * @return  list
      */
     List<FsUserCourseParticipationRecordVO> getParticipationRecordByMap(@Param("params") Map<String, Object> params);
+
+    @Select("<script> " +
+            "select course_id,course_name,title,img_url from fs_user_course where is_del=0  and is_private = 1 " +
+            "and find_in_set(#{data.companyId},company_ids)" +
+            "order by course_id asc" +
+            "</script> ")
+    List<FsCourseListBySidebarVO> getFsCourseListBySidebar(@Param("data") FsCourseListBySidebarParam param);
 }

+ 13 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -2,9 +2,11 @@ package com.fs.course.mapper;
 
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.param.CourseVideoUpdates;
+import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.course.param.FsUserCourseVideoListUParam;
 import com.fs.course.param.FsUserCourseVideoParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.vo.FsCourseVideoListBySidebarVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
@@ -180,4 +182,15 @@ public interface FsUserCourseVideoMapper
      * @return  list
      */
     List<OptionsVO> selectVideoListByMap(@Param("params") Map<String, Object> params);
+
+    @Select("<script> " +
+            "select v.video_id,v.course_id,v.title,v.video_url,v.thumbnail,v.duration,v.create_time from fs_user_course_video v " +
+            "left join fs_user_course c on c.course_id = v.course_id " +
+            "where c.is_private = 1 and v.is_del = 0 and v.course_id=#{data.courseId} " +
+            "        <if test=\"data.keyword != null and data.keyword !='' \">\n" +
+            "            AND v.title LIKE concat('%',#{data.keyword},'%')\n" +
+            "        </if>" +
+            "order by v.video_id asc " +
+            "</script>")
+    List<FsCourseVideoListBySidebarVO> getFsCourseVideoListBySidebar(@Param("data") FsCourseListBySidebarParam param);
 }

+ 32 - 0
fs-service-system/src/main/java/com/fs/course/param/FsCourseListBySidebarParam.java

@@ -0,0 +1,32 @@
+package com.fs.course.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel
+public class FsCourseListBySidebarParam implements Serializable {
+
+    @ApiModelProperty(value = "页码,默认为1", required = true)
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10", required = true)
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "模糊搜索,通过视频名称来匹配")
+    private String keyword;
+
+    @ApiModelProperty(value = "公司id")
+    private Long companyId;
+    /**
+     * 外部联系人主键ID
+     */
+    private Long extId;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+}

+ 2 - 1
fs-service-system/src/main/java/com/fs/course/param/newfs/FsCourseSortLinkParam.java

@@ -3,6 +3,7 @@ package com.fs.course.param.newfs;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import javax.validation.constraints.NotBlank;
 
 @Data
 @ApiModel(description = "生成课程短链/海报入参")
@@ -32,7 +33,7 @@ public class FsCourseSortLinkParam {
     @ApiModelProperty(value = "视频时长,生成海报时必传")
     private String duration;
 
-    @ApiModelProperty(value = "营期id")
+    @ApiModelProperty(value = "营期id", required = true)
     private Long periodId;
 
     @ApiModelProperty(value = "营期课程id")

+ 8 - 2
fs-service-system/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java

@@ -5,7 +5,6 @@ import com.fs.course.domain.FsCourseQuestionBank;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
 
-import javax.validation.constraints.Size;
 import java.util.List;
 
 /**
@@ -82,7 +81,7 @@ public interface IFsCourseQuestionBankService
      * @param nickName 昵称
      * @return String
      */
-    String importData(List<FsCourseQuestionBankImportDTO> list, @Size String nickName);
+    String importData(List<FsCourseQuestionBankImportDTO> list, String nickName);
 
     /**
      * 根据ID查询题目
@@ -94,4 +93,11 @@ public interface IFsCourseQuestionBankService
     List<FsCourseQuestionBank> selectFsCourseQuestionBankListExport(FsCourseQuestionBank fsCourseQuestionBank);
 
     String selectNameByCateId(Long cateId);
+
+    /**
+     * 题目导出
+     * @param fsCourseQuestionBank  参数
+     * @return  list
+     */
+    List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank);
 }

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

@@ -166,4 +166,5 @@ public interface IFsUserCourseService
      */
     String createUserImageQR(String url, String backgroundImagePath, InputStream file, String outputFormat, Long companyUserId) throws Exception;
 
+    List<FsCourseListBySidebarVO> getFsCourseListBySidebar(FsCourseListBySidebarParam param);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -8,6 +8,7 @@ import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
 import com.fs.course.param.newfs.FsUserCourseVideoUParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.vo.FsCourseVideoListBySidebarVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoQVO;
 import com.fs.course.vo.FsUserCourseVideoVO;
@@ -157,4 +158,6 @@ public interface IFsUserCourseVideoService
      * @return  list
      */
     List<OptionsVO> selectVideoListByMap(Map<String, Object> params);
+
+    List<FsCourseVideoListBySidebarVO> getFsCourseVideoListBySidebar(FsCourseListBySidebarParam param);
 }

+ 324 - 102
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.course.service.impl;
 
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
@@ -10,7 +11,10 @@ import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.course.cache.FsUserCourseCategoryCacheService;
 import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.*;
+import com.fs.course.domain.FsCourseAnswerLogs;
+import com.fs.course.domain.FsCourseQuestionBank;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.domain.FsUserCourseCategory;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.mapper.*;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
@@ -21,15 +25,15 @@ import com.fs.store.mapper.FsUserMapper;
 import com.fs.store.service.IFsStorePaymentService;
 import com.fs.system.service.ISysConfigService;
 import com.google.gson.JsonParser;
-import com.hc.openapi.tool.fastjson.JSON;
 import jodd.util.StringUtil;
-import org.apache.commons.collections4.CollectionUtils;
+import lombok.Getter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.validation.constraints.Size;
 import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -360,134 +364,297 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
      * @return String
      */
     @Override
-    public String importData(List<FsCourseQuestionBankImportDTO> list, @Size String nickName) {
+    public String importData(List<FsCourseQuestionBankImportDTO> list, String nickName) {
         if (Objects.isNull(list) || list.isEmpty()) {
             throw new ServiceException("导入数据不能为空");
         }
 
-        int successNum = 0;
-        int failureNum = 0;
-        StringBuilder importSuccessMsg = new StringBuilder();
-        StringBuilder importErrorMsg = new StringBuilder();
-        StringBuilder importMsg = new StringBuilder();
-
+        ImportResult result = new ImportResult();
         List<FsCourseQuestionBank> importData = new ArrayList<>();
-
-        Map<String,FsUserCourseCategory> categoryData = courseCategoryMapper.queryAllCategoryData();
+        Map<String, FsUserCourseCategory> categoryData = courseCategoryMapper.queryAllCategoryData();
 
         for (FsCourseQuestionBankImportDTO importDTO : list) {
             try {
-                String title = importDTO.getTitle();
-                String type = importDTO.getType();
-                String questionType = importDTO.getQuestionType();
-                String questionSubTyp = importDTO.getQuestionSubTyp();
-
-                String question = importDTO.getQuestion();
-                String answer = importDTO.getAnswer();
-
-                if (StringUtils.isBlank(title) || StringUtils.isBlank(type) || StringUtils.isBlank(question) || StringUtils.isBlank(answer)) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:信息不完整";
-                    importErrorMsg.append(msg);
-                    failureNum++;
+                // 数据验证
+                ValidationResult validation = validateImportData(importDTO);
+                if (!validation.isValid()) {
+                    result.addFailure(importDTO.getTitle(), validation.getErrorMessage());
                     continue;
                 }
 
-                if (!type.contains("单") && !type.contains("多")) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:题目类型不正确";
-                    importErrorMsg.append(msg);
-                    failureNum++;
-                    continue;
-                }
+                // 构建题目对象
+                FsCourseQuestionBank questionBank = buildQuestionBank(importDTO, categoryData, nickName);
+                importData.add(questionBank);
+                result.addSuccess(importDTO.getTitle());
 
-                // 判断答案是否在选项里
-                String[] questions = question.split("\\|");
-                String[] answers = answer.split("\\|");
+            } catch (Exception e) {
+                result.addFailure(importDTO.getTitle(), "导入异常: " + e.getMessage());
+            }
+        }
 
-                if (type.contains("多") && answers.length < 2) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:多选题答案不能少于2个";
-                    importErrorMsg.append(msg);
-                    failureNum++;
-                    continue;
-                }
+        // 批量保存
+        if (!importData.isEmpty()) {
+            fsCourseQuestionBankMapper.insertFsCourseQuestionBankBatch(importData);
+        }
 
-                List<String> optionsList = Arrays.asList(questions);
-                boolean allAnswersInOptions = true;
+        return result.buildResultMessage();
+    }
 
-                // 遍历每一个正确答案文本
-                for (String correctAnswer : answers) {
-                    // 检查当前正确答案文本是否存在于选项列表中
-                    if (!optionsList.contains(correctAnswer.trim())) {
-                        allAnswersInOptions = false; // 发现一个不在选项里的答案
-                        break;
-                    }
-                }
+    /**
+     * 验证导入数据
+     */
+    private ValidationResult validateImportData(FsCourseQuestionBankImportDTO importDTO) {
+        // 基础字段验证
+        if (StringUtils.isBlank(importDTO.getTitle())) {
+            return ValidationResult.fail("标题不能为空");
+        }
+        if (StringUtils.isBlank(importDTO.getType())) {
+            return ValidationResult.fail("类别不能为空");
+        }
+        if (Objects.isNull(importDTO.getSort())) {
+            return ValidationResult.fail("排序不能为空");
+        }
+        if (StringUtils.isBlank(importDTO.getAnswer())) {
+            return ValidationResult.fail("答案不能为空");
+        }
 
-                if (!allAnswersInOptions) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:答案不在选项中";
-                    importErrorMsg.append(msg);
-                    failureNum++;
-                    continue;
-                }
+        // 选项验证
+        ValidationResult optionValidation = validateOptions(importDTO);
+        if (!optionValidation.isValid()) {
+            return optionValidation;
+        }
 
-                JSONArray questionArray = new JSONArray();
-                optionsList = Arrays.asList(answers);
-                for (int i = 0; i < questions.length; i++) {
-                    JSONObject optionObj = new JSONObject();
-                    optionObj.put("name", questions[i]);
-                    optionObj.put("isAnswer", optionsList.contains(questions[i].trim()) ? 1 : 0);
-                    optionObj.put("indexId", i);
-                    questionArray.add(optionObj);
-                }
+        // 题目类型验证
+        if (!isValidQuestionType(importDTO.getType())) {
+            return ValidationResult.fail("题目类型不正确,仅支持单选题和多选题");
+        }
 
-                // 分类
-                FsUserCourseCategory fsUserCourseCategory = categoryData.get(questionType);
-                Long questionTypeId = null;
-                if(fsUserCourseCategory != null) {
-                    questionTypeId = fsUserCourseCategory.getCateId();
-                }
+        // 答案验证
+        return validateAnswers(importDTO);
+    }
 
-                // 题目子分类
-                fsUserCourseCategory = categoryData.get(questionSubTyp);
-                Long questionSubTypeId = null;
-                if(fsUserCourseCategory != null) {
-                    questionSubTypeId = fsUserCourseCategory.getCateId();
-                }
+    /**
+     * 验证选项
+     */
+    private ValidationResult validateOptions(FsCourseQuestionBankImportDTO importDTO) {
+        if (StringUtils.isBlank(importDTO.getQuestionA()) ||
+                StringUtils.isBlank(importDTO.getQuestionB()) ||
+                StringUtils.isBlank(importDTO.getQuestionC())) {
+            return ValidationResult.fail("选项A、B、C不能为空");
+        }
+        return ValidationResult.success();
+    }
 
-                FsCourseQuestionBank questionBank = new FsCourseQuestionBank();
-                questionBank.setTitle(title);
-                questionBank.setType(type.contains("单") ? 1L : 2L);
-                questionBank.setQuestionType(questionTypeId);
-                questionBank.setStatus(1L);
-                questionBank.setSort(importDTO.getSort());
-                questionBank.setQuestion(JSON.toJSONString(questionArray));
-                questionBank.setAnswer(answers.length > 1 ? JSON.toJSONString(answers) : answer);
-                questionBank.setCreateTime(new Date());
-                questionBank.setCreateBy(nickName);
-                questionBank.setQuestionSubType(questionSubTypeId);
+    /**
+     * 验证题目类型
+     */
+    private boolean isValidQuestionType(String type) {
+        return type.contains("单") || type.contains("多");
+    }
 
-                importData.add(questionBank);
+    /**
+     * 验证答案
+     */
+    private ValidationResult validateAnswers(FsCourseQuestionBankImportDTO importDTO) {
+        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
 
+        // 多选题答案数量验证
+        if (importDTO.getType().contains("多") && answers.length < 2) {
+            return ValidationResult.fail("多选题答案不能少于2个");
+        }
 
-                importSuccessMsg.append("<br/>").append(successNum).append("、题目 ").append(title).append(" 导入成功");
-                successNum++;
-            } catch (Exception e) {
-                String msg = "<br/>" + failureNum + "、题目 " + importDTO.getTitle() + " 导入异常:";
-                importErrorMsg.append(msg).append(e.getMessage());
-                failureNum++;
+        // 答案选项验证
+        List<String> validOptions = getValidOptions(importDTO);
+        for (String answer : answers) {
+            if (!validOptions.contains(answer.trim())) {
+                return ValidationResult.fail("答案不在选项中");
             }
         }
 
-        if(CollectionUtils.isNotEmpty(importData)) {
-            fsCourseQuestionBankMapper.insertFsCourseQuestionBankBatch(importData);
+        return ValidationResult.success();
+    }
+
+    /**
+     * 获取有效选项列表
+     */
+    private List<String> getValidOptions(FsCourseQuestionBankImportDTO importDTO) {
+        List<String> options = new ArrayList<>(Arrays.asList("A", "B", "C"));
+        if (StringUtils.isNotBlank(importDTO.getQuestionD())) {
+            options.add("D");
         }
+        return options;
+    }
+
+    /**
+     * 构建题目对象
+     */
+    private FsCourseQuestionBank buildQuestionBank(FsCourseQuestionBankImportDTO importDTO,
+                                                   Map<String, FsUserCourseCategory> categoryData,
+                                                   String nickName) {
+        FsCourseQuestionBank questionBank = new FsCourseQuestionBank();
+
+        // 基础信息
+        questionBank.setTitle(importDTO.getTitle());
+        questionBank.setType(getQuestionTypeCode(importDTO.getType()));
+        questionBank.setSort(importDTO.getSort());
+        questionBank.setStatus(parseStatus(importDTO.getStatus()));
+        questionBank.setCreateTime(new Date());
+        questionBank.setCreateBy(nickName);
+
+        // 分类信息
+        setQuestionCategories(questionBank, importDTO, categoryData);
+
+        // 选项和答案
+        questionBank.setQuestion(buildQuestionOptions(importDTO));
+        questionBank.setAnswer(buildAnswer(importDTO));
+
+        return questionBank;
+    }
 
-        // 在所有导入处理完成后,构建最终的导入结果消息
-        importMsg.insert(0, "导入完成!成功" + successNum + " 条,失败" + failureNum + "条。");
-        importMsg.append(importErrorMsg);
-        importMsg.append(importSuccessMsg);
-        return importMsg.toString();
+    /**
+     * 获取题目类型编码
+     */
+    private Long getQuestionTypeCode(String type) {
+        return type.contains("单") ? 1L : 2L;
     }
 
+    /**
+     * 解析状态
+     */
+    private Long parseStatus(String statusStr) {
+        if (StringUtils.isBlank(statusStr)) {
+            return 1L;
+        }
+        try {
+            return statusStr.contains("正常") ? 1L : 0L;
+        } catch (Exception e) {
+            return 1L;
+        }
+    }
+
+    /**
+     * 设置题目分类
+     */
+    private void setQuestionCategories(FsCourseQuestionBank questionBank,
+                                       FsCourseQuestionBankImportDTO importDTO,
+                                       Map<String, FsUserCourseCategory> categoryData) {
+        if (StringUtils.isBlank(importDTO.getQuestionType())) {
+            return;
+        }
+
+        FsUserCourseCategory category = categoryData.get(importDTO.getQuestionType().trim());
+        if (category != null) {
+            questionBank.setQuestionType(category.getCateId());
+
+            // 子分类
+            if (StringUtils.isNotBlank(importDTO.getQuestionSubTyp())) {
+                FsUserCourseCategory subCategory = categoryData.get(importDTO.getQuestionSubTyp().trim());
+                if (subCategory != null && Objects.equals(subCategory.getPid(), category.getCateId())) {
+                    questionBank.setQuestionSubType(subCategory.getCateId());
+                }
+            }
+        }
+    }
+
+    /**
+     * 构建选项JSON
+     */
+    private String buildQuestionOptions(FsCourseQuestionBankImportDTO importDTO) {
+        JSONArray questionArray = new JSONArray();
+        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
+        List<String> answerList = Arrays.asList(answers);
+
+        // 构建选项
+        addOption(questionArray, importDTO.getQuestionA(), answerList.contains("A"), 0);
+        addOption(questionArray, importDTO.getQuestionB(), answerList.contains("B"), 1);
+        addOption(questionArray, importDTO.getQuestionC(), answerList.contains("C"), 2);
+
+        if (StringUtils.isNotBlank(importDTO.getQuestionD())) {
+            addOption(questionArray, importDTO.getQuestionD(), answerList.contains("D"), 3);
+        }
+
+        return JSON.toJSONString(questionArray);
+    }
+
+    /**
+     * 添加选项
+     */
+    private void addOption(JSONArray questionArray, String optionText,
+                           boolean isAnswer, int indexId) {
+        JSONObject option = new JSONObject();
+        option.put("name", optionText.trim());
+        option.put("isAnswer", isAnswer ? 1 : 0);
+        option.put("indexId", indexId);
+        questionArray.add(option);
+    }
+
+    /**
+     * 构建答案
+     */
+    private String buildAnswer(FsCourseQuestionBankImportDTO importDTO) {
+        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
+        List<String> answerList = Arrays.asList(answers);
+
+        Map<String, String> questionMap = new HashMap<>();
+        questionMap.put("A", importDTO.getQuestionA().trim());
+        questionMap.put("B", importDTO.getQuestionB().trim());
+        questionMap.put("C", importDTO.getQuestionC().trim());
+        questionMap.put("D", importDTO.getQuestionD().trim());
+
+        List<String> selectedAnswers = answerList.stream()
+                .map(questionMap::get)
+                .collect(Collectors.toList());
+
+        if (answerList.size() == 1) {
+            return selectedAnswers.get(0);
+        }
+
+        return JSON.toJSONString(selectedAnswers);
+    }
+
+    // 辅助类
+    @Getter
+    private static class ValidationResult {
+        private final boolean valid;
+        private final String errorMessage;
+
+        private ValidationResult(boolean valid, String errorMessage) {
+            this.valid = valid;
+            this.errorMessage = errorMessage;
+        }
+
+        public static ValidationResult success() {
+            return new ValidationResult(true, null);
+        }
+
+        public static ValidationResult fail(String message) {
+            return new ValidationResult(false, message);
+        }
+
+    }
+
+    private static class ImportResult {
+        private int successNum = 0;
+        private int failureNum = 0;
+        private final StringBuilder successMsg = new StringBuilder();
+        private final StringBuilder errorMsg = new StringBuilder();
+
+        public void addSuccess(String title) {
+            successNum++;
+            successMsg.append("<br/>").append(successNum).append("、题目 ").append(title).append(" 导入成功");
+        }
+
+        public void addFailure(String title, String error) {
+            failureNum++;
+            errorMsg.append("<br/>").append(failureNum).append("、题目 ").append(title).append(" 导入失败:").append(error);
+        }
+
+        public String buildResultMessage() {
+            return "导入完成!成功" + successNum + " 条,失败" + failureNum + "条。" + errorMsg + successMsg;
+        }
+    }
+
+
     /**
      * 根据ID查询题目
      * @param ids   ids
@@ -554,6 +721,61 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
         return fsCourseQuestionBankMapper.selectNameByCateId(cateId);
     }
 
+    /**
+     * 题目导出
+     * @param fsCourseQuestionBank  参数
+     * @return  list
+     */
+    @Override
+    public List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank) {
+        List<FsCourseQuestionBank> fsCourseQuestionBanks = fsCourseQuestionBankMapper.selectFsCourseQuestionBankList(fsCourseQuestionBank);
+        Map<Long, FsUserCourseCategory> categoryMap = courseCategoryMapper.queryAllIdKeyCategoryData();
+
+        // 辅助方法:根据ID从Map获取分类名称
+        Function<Long, String> getCategoryName = id -> Optional.ofNullable(categoryMap.get(id))
+                .map(FsUserCourseCategory::getCateName)
+                .orElse(null);
+
+        Map<Integer, BiConsumer<FsCourseQuestionBankImportDTO, String>> questionSetters = new HashMap<>();
+        questionSetters.put(0, FsCourseQuestionBankImportDTO::setQuestionA);
+        questionSetters.put(1, FsCourseQuestionBankImportDTO::setQuestionB);
+        questionSetters.put(2, FsCourseQuestionBankImportDTO::setQuestionC);
+        questionSetters.put(3, FsCourseQuestionBankImportDTO::setQuestionD);
+
+        Map<Integer, String> answerLettersMap = new HashMap<>();
+        answerLettersMap.put(0, "A");
+        answerLettersMap.put(1, "B");
+        answerLettersMap.put(2, "C");
+        answerLettersMap.put(3, "D");
+
+        return fsCourseQuestionBanks.stream().map(q -> {
+            FsCourseQuestionBankImportDTO dto = new FsCourseQuestionBankImportDTO();
+            dto.setTitle(q.getTitle());
+            dto.setSort(q.getSort());
+            dto.setType(q.getType() == 1 ? "单选" : "多选");
+            Optional.ofNullable(q.getQuestionType()).map(getCategoryName).ifPresent(dto::setQuestionType);
+            Optional.ofNullable(q.getQuestionSubType()).map(getCategoryName).ifPresent(dto::setQuestionSubTyp);
+            dto.setStatus(q.getStatus() == 1 ? "正常" : "停用");
+
+            StringBuilder answersBuilder = new StringBuilder();
+            JSONArray array = JSON.parseArray(q.getQuestion());
+            array.forEach(jsonObject -> {
+                JSONObject json = (JSONObject) jsonObject;
+                int index = json.getInteger("indexId");
+                String name = json.getString("name");
+                int isAnswer = json.getInteger("isAnswer");
+
+                questionSetters.get(index).accept(dto, name);
+                if (isAnswer == 1) {
+                    answersBuilder.append(answerLettersMap.get(index));
+                }
+
+            });
+            dto.setAnswer(answersBuilder .toString());
+            return dto;
+        }).collect(Collectors.toList());
+    }
+
 
     public static String[] convertStringToArray(String inputString) {
         String cleanString = inputString.replaceAll("[\\[\\]\"\\s]", "");

+ 9 - 3
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java

@@ -158,13 +158,19 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
             day.setLesson(dayList.size() + i.getAndIncrement());
             day.setDayDate(period.getPeriodStartingTime().plusDays(day.getLesson()));
             day.setCourseId(entity.getCourseId());
-            if(video.getViewStartTime() != null){
+            if(entity.getStartTime() != null){
+                day.setStartDateTime(LocalDateTime.of(day.getDayDate(), entity.getStartTime()));
+            }else if(video.getViewStartTime() != null){
                 day.setStartDateTime(LocalDateTime.of(day.getDayDate(), video.getViewStartTime()));
             }
-            if(video.getViewEndTime() != null){
+            if(entity.getEndTime1() != null){
+                day.setEndDateTime(LocalDateTime.of(day.getDayDate(), entity.getEndTime1()));
+            }else if(video.getViewEndTime() != null){
                 day.setEndDateTime(LocalDateTime.of(day.getDayDate(), video.getViewEndTime()));
             }
-            if(video.getLastJoinTime() != null){
+            if(entity.getJoinTime() != null){
+                day.setLastJoinTime(LocalDateTime.of(day.getDayDate(), entity.getJoinTime()));
+            }else if(video.getLastJoinTime() != null){
                 day.setLastJoinTime(LocalDateTime.of(day.getDayDate(), video.getLastJoinTime()));
             }
             day.setVideoId(e);

+ 7 - 2
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -79,8 +79,8 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
     @Autowired
     private FsCourseAnswerLogsMapper fsCourseAnswerLogsMapper;
 
-    private static final String realLink = "/pages/course/learning?course=";
-    private static final String shortLink = "/pages/course/learning?s=";
+    private static final String realLink = "/courseH5/pages/course/learning?course=";
+    private static final String shortLink = "/courseH5/pages/course/learning?s=";
     private static final String userRealLink = "/pages/user/users/becomeVIP?";
 
     /**
@@ -741,6 +741,11 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         return convertToBase64(combined, outputFormat);
     }
 
+    @Override
+    public List<FsCourseListBySidebarVO> getFsCourseListBySidebar(FsCourseListBySidebarParam param) {
+        return  fsUserCourseMapper.getFsCourseListBySidebar(param);
+    }
+
     private static Calendar getExpireDay(FsCourseLinkCreateParam param, CourseConfig config, Date createTime) {
         Integer expireDuration;
         if (param.getEffectiveDuration() == null || param.getEffectiveDuration() == 0){

+ 15 - 5
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -22,6 +22,7 @@ import com.fs.course.param.*;
 import com.fs.course.param.newfs.*;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsVideoResourceService;
+import com.fs.course.vo.FsCourseVideoListBySidebarVO;
 import com.fs.course.vo.FsUserCourseVideoListUVO;
 import com.fs.course.vo.FsUserCourseVideoQVO;
 import com.fs.course.vo.FsUserCourseVideoVO;
@@ -37,6 +38,7 @@ import com.fs.qwApi.param.QwAddContactWayParam;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
+import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.store.domain.FsUser;
 import com.fs.store.domain.FsUserCompanyUser;
 import com.fs.store.domain.FsUserIntegralLogs;
@@ -59,7 +61,6 @@ import org.springframework.transaction.annotation.Transactional;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
-import java.time.LocalTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
@@ -91,6 +92,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     private FsCourseWatchLogMapper courseWatchLogMapper;
 
+
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
+
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
     @Autowired
@@ -469,6 +474,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             }
             log.setUpdateTime(new Date());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+            iSopUserLogsInfoService.updateSopUserInfoByExternalId(externalContact.getId(),param.getUserId());
+
             return R.ok();
 
         }else {
@@ -483,10 +491,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             user.setQwExtId(param.getQwExternalId());
             fsUserMapper.updateFsUser(user);
 
-            List<String> list= sopUserLogsInfoMapper.selectSopUserLogsInfoByExtId(contact.getId());
-            if (list!=null&& !list.isEmpty()){
-                sopUserLogsInfoMapper.updateSopUserLogsInfoFsUserIdById(list,param.getUserId());
-            }
+            iSopUserLogsInfoService.updateSopUserInfoByExternalId(externalContact.getId(),param.getUserId());
 
             //绑定上之后 更新观看记录
             //看课记录中userId为0绑定userId
@@ -1188,6 +1193,11 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return fsUserCourseVideoMapper.selectVideoListByMap(params);
     }
 
+    @Override
+    public List<FsCourseVideoListBySidebarVO> getFsCourseVideoListBySidebar(FsCourseListBySidebarParam param) {
+        return fsUserCourseVideoMapper.getFsCourseVideoListBySidebar(param);
+    }
+
     //会员-更新心跳时间
     public void updateHeartbeatWx(FsUserCourseVideoUParam param) {
         String redisKey = "h5wxuser:watch:heartbeat:" + param.getUserId() + ":" + param.getVideoId() + ":" + param.getCompanyUserId();

+ 26 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsCourseListBySidebarVO.java

@@ -0,0 +1,26 @@
+package com.fs.course.vo;
+
+import lombok.Data;
+
+@Data
+public class FsCourseListBySidebarVO {
+    /**
+    * 课程id
+    */
+    private Long courseId;
+    /**
+    * 课程名称
+    */
+    private String courseName;
+
+    /**
+    * 标题
+    */
+    private String title;
+
+    /**
+    * 课程封面
+    */
+    private String imgUrl;
+
+}

+ 37 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsCourseVideoListBySidebarVO.java

@@ -0,0 +1,37 @@
+package com.fs.course.vo;
+
+import lombok.Data;
+
+@Data
+public class FsCourseVideoListBySidebarVO {
+
+    private Long videoId;
+
+    private Long courseId;
+
+    /**
+    * 视频标题
+    */
+    private String title;
+
+    /**
+    * 视频地址
+    */
+    private String videoUrl;
+
+    /**
+    * 时长
+    */
+    private Integer duration;
+
+    /**
+    * 视频缩略图
+    */
+    private String thumbnail;
+
+    /**
+    * 视频创建时间
+    */
+    private String createTime;
+
+}

+ 36 - 0
fs-service-system/src/main/java/com/fs/crm/domain/CrmCustomerAssist.java

@@ -0,0 +1,36 @@
+package com.fs.crm.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+/**
+ * 客户员工协作对象 crm_customer_assist
+ *
+ * @author fs
+ * @date 2025-05-27
+ */
+@Data
+public class CrmCustomerAssist extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** $column.columnComment */
+    @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+    private Long companyId;
+
+    /** 销售ID */
+    @Excel(name = "销售ID")
+    private Long companyUserId;
+
+    /** 销售名字 */
+    @Excel(name = "销售名字")
+    private String companyUserName;
+
+    /** 销售公司ID */
+    @Excel(name = "销售公司ID")
+    private Long customerId;
+}

+ 2 - 1
fs-service-system/src/main/java/com/fs/crm/enums/CustomerLogEnum.java

@@ -13,7 +13,8 @@ public enum CustomerLogEnum {
     RECEIVE(3,"认领"),
     RECOVER(4,"回收"),
     ASSIGN_USER(5,"分配员工"),
-    CANCEL_ASSIGN(6,"撤销分配");
+    CANCEL_ASSIGN(6,"撤销分配"),
+    ASSIST_USER(7,"添加协作人");
 
     private Integer value;
     private String desc;

+ 69 - 0
fs-service-system/src/main/java/com/fs/crm/mapper/CrmCustomerAssistMapper.java

@@ -0,0 +1,69 @@
+package com.fs.crm.mapper;
+
+import java.util.List;
+import com.fs.crm.domain.CrmCustomerAssist;
+import com.fs.crm.param.CrmCustomerAssistDeLParam;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 客户员工协作Mapper接口
+ *
+ * @author fs
+ * @date 2025-05-27
+ */
+public interface CrmCustomerAssistMapper
+{
+    /**
+     * 查询客户员工协作
+     *
+     * @param id 客户员工协作ID
+     * @return 客户员工协作
+     */
+    public CrmCustomerAssist selectCrmCustomerAssistById(Long id);
+
+    /**
+     * 查询客户员工协作列表
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 客户员工协作集合
+     */
+    public List<CrmCustomerAssist> selectCrmCustomerAssistList(CrmCustomerAssist crmCustomerAssist);
+
+    /**
+     * 新增客户员工协作
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 结果
+     */
+    public int insertCrmCustomerAssist(CrmCustomerAssist crmCustomerAssist);
+
+    /**
+     * 修改客户员工协作
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 结果
+     */
+    public int updateCrmCustomerAssist(CrmCustomerAssist crmCustomerAssist);
+
+    /**
+     * 删除客户员工协作
+     *
+     * @param id 客户员工协作ID
+     * @return 结果
+     */
+    public int deleteCrmCustomerAssistById(Long id);
+
+    /**
+     * 批量删除客户员工协作
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteCrmCustomerAssistByIds(Long[] ids);
+
+    List<String> selectCompanyUserNameByCustomerId(@Param("customerId") Long customerId);
+
+    int removeByCustomer(@Param("maps") CrmCustomerAssistDeLParam param);
+
+    int deleteCrmCustomerAssistByCustomerId(Long customerId);
+}

+ 1 - 57
fs-service-system/src/main/java/com/fs/crm/mapper/CrmCustomerMapper.java

@@ -272,63 +272,6 @@ public interface CrmCustomerMapper extends BaseMapper<CrmCustomer>
     List<CrmCustomerListVO> selectCrmCustomerListQueryParam(@Param("maps")CrmCustomerListQueryParam crmCustomer);
 
 
-    @Select({"<script> " +
-            "select cu.*,c.create_time as customer_create_time,c.visit_status,c.remark,c.register_desc,c.register_submit_time,c.customer_code,c.customer_name,c.mobile,c.sex,c.weixin,c.address,c.is_receive,c.customer_type,c.source,c.tags,c.receive_time from crm_customer_user cu inner join crm_customer c on c.customer_user_id=cu.customer_user_id " +
-            "where cu.is_pool=0 " +
-            "<if test = 'maps.companyId != null     '> " +
-            "and cu.company_id =#{maps.companyId} " +
-            "</if>" +
-            "<if test = 'maps.companyUserId != null     '> " +
-            "and cu.company_user_id =#{maps.companyUserId} " +
-            "</if>" +
-            "<if test = 'maps.address != null and  maps.address !=\"\"    '> " +
-            "and c.address like CONCAT('%',#{maps.address},'%') " +
-            "</if>" +
-            "<if test = 'maps.customerCode != null and  maps.customerCode !=\"\"    '> " +
-            "and c.customer_code like CONCAT('%',#{maps.customerCode},'%') " +
-            "</if>" +
-            "<if test = 'maps.customerName != null and  maps.customerName !=\"\"    '> " +
-            "and c.customer_name like CONCAT('%',#{maps.customerName},'%') " +
-            "</if>" +
-            "<if test = 'maps.mobile != null and  maps.mobile !=\"\"    '> " +
-            "and c.mobile like CONCAT('%',#{maps.mobile},'%') " +
-            "</if>" +
-            "<if test = 'maps.status != null and maps.status !=\"\"     '> " +
-            "and c.visit_status IN " +
-            "<foreach collection=\"maps.status.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach>"+
-            "</if>" +
-            "<if test = 'maps.isHisOrder != null and maps.isHisOrder==1      '> " +
-            "and (select ifnull(count(1),0) from crm_customer_his_order h where h.customer_id=c.customer_id )  &gt; 0 " +
-            "</if>" +
-            "<if test = 'maps.isHisOrder != null and maps.isHisOrder==0      '> " +
-            "and (select ifnull(count(1),0) from crm_customer_his_order h where h.customer_id=c.customer_id )  = 0 " +
-            "</if>" +
-            "<if test = 'maps.customerType != null      '> " +
-            "and c.customer_type IN " +
-            "<foreach collection=\"maps.customerType.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach>"+
-            "</if>" +
-            "<if test = 'maps.source != null and maps.source !=\"\"      '> " +
-            "and c.source IN " +
-            "<foreach collection=\"maps.source.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach>"+
-            "</if>" +
-            "<if test = 'maps.tags != null and maps.tags!=\"\"     '> " +
-            "and  " +
-            "<foreach collection=\"maps.tags.split(',')\" item=\"tag\"   open=\"(\" close=\")\" separator=\"OR\">" +
-            "find_in_set(#{tag},c.tags)" +
-            "</foreach> " +
-            "</if>" +
-            "<if test = 'maps.customerCreateTime != null    '> " +
-            " AND date_format(c.create_time,'%y%m%d') &gt;= date_format(#{maps.customerCreateTime[0]},'%y%m%d') " +
-            " AND date_format(c.create_time,'%y%m%d') &lt;= date_format(#{maps.customerCreateTime[1]},'%y%m%d') " +
-            "</if>" +
-            "<if test = 'maps.beginTime != null and maps.beginTime != \"\" '> " +
-            "and date_format(c.receive_time,'%y%m%d') &gt;= date_format(#{maps.beginTime},'%y%m%d') " +
-            "</if>" +
-            "<if test = 'maps.endTime != null and maps.endTime != \"\" '> " +
-            "and date_format(c.receive_time,'%y%m%d') &lt;= date_format(#{maps.endTime},'%y%m%d') " +
-            "</if>" +
-            " order by cu.customer_user_id desc "+
-            "</script>"})
     List<CrmMyCustomerListQueryVO> selectCrmMyCustomerListQuery(@Param("maps")CrmMyCustomerListQueryParam param);
 
 
@@ -669,4 +612,5 @@ public interface CrmCustomerMapper extends BaseMapper<CrmCustomer>
     @Select("select user_id  from  fs_user where phone=#{remarkMobile} limit 1")
     Long selectFsUserByCrmMobile(String remarkMobile);
 
+    List<CrmMyCustomerListQueryVO> selectCrmMyAssistListQuery(@Param("maps") CrmMyCustomerListQueryParam param);
 }

+ 11 - 0
fs-service-system/src/main/java/com/fs/crm/param/CrmCustomerAssistDeLParam.java

@@ -0,0 +1,11 @@
+package com.fs.crm.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CrmCustomerAssistDeLParam {
+    private List<Long> customerIds;
+    private Long companyUserId;
+}

+ 64 - 0
fs-service-system/src/main/java/com/fs/crm/service/ICrmCustomerAssistService.java

@@ -0,0 +1,64 @@
+package com.fs.crm.service;
+
+import java.util.List;
+import com.fs.crm.domain.CrmCustomerAssist;
+import com.fs.crm.param.CrmCustomerAssistDeLParam;
+
+/**
+ * 客户员工协作Service接口
+ *
+ * @author fs
+ * @date 2025-05-27
+ */
+public interface ICrmCustomerAssistService
+{
+    /**
+     * 查询客户员工协作
+     *
+     * @param id 客户员工协作ID
+     * @return 客户员工协作
+     */
+    public CrmCustomerAssist selectCrmCustomerAssistById(Long id);
+
+    /**
+     * 查询客户员工协作列表
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 客户员工协作集合
+     */
+    public List<CrmCustomerAssist> selectCrmCustomerAssistList(CrmCustomerAssist crmCustomerAssist);
+
+    /**
+     * 新增客户员工协作
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 结果
+     */
+    public int insertCrmCustomerAssist(CrmCustomerAssist crmCustomerAssist);
+
+    /**
+     * 修改客户员工协作
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 结果
+     */
+    public int updateCrmCustomerAssist(CrmCustomerAssist crmCustomerAssist);
+
+    /**
+     * 批量删除客户员工协作
+     *
+     * @param ids 需要删除的客户员工协作ID
+     * @return 结果
+     */
+    public int deleteCrmCustomerAssistByIds(Long[] ids);
+
+    /**
+     * 删除客户员工协作信息
+     *
+     * @param id 客户员工协作ID
+     * @return 结果
+     */
+    public int deleteCrmCustomerAssistById(Long id);
+
+    int removeByCustomer(CrmCustomerAssistDeLParam param);
+}

+ 4 - 0
fs-service-system/src/main/java/com/fs/crm/service/ICrmCustomerService.java

@@ -117,6 +117,8 @@ public interface ICrmCustomerService
 
     R assignCompanys(CrmCompanyAssignParam param, String operUserName,Long operUserId);
 
+    R assistToUser(String opeUserName, Long opeUserId, CrmCustomeAssignParam param);
+
 
     int updateCrmCustomerSource(CrmCustomerEditSourceParam param);
 
@@ -137,4 +139,6 @@ public interface ICrmCustomerService
     String importVisitCustomer(List<CrmCustomerVisitImportParam> list,Long companyId);
 
     public List<CrmCustomerListVO> selectCrmCustomer1();
+
+    List<CrmMyCustomerListQueryVO> selectCrmMyAssistListQuery(CrmMyCustomerListQueryParam param);
 }

+ 104 - 0
fs-service-system/src/main/java/com/fs/crm/service/impl/CrmCustomerAssistServiceImpl.java

@@ -0,0 +1,104 @@
+package com.fs.crm.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.fs.crm.param.CrmCustomerAssistDeLParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.crm.mapper.CrmCustomerAssistMapper;
+import com.fs.crm.domain.CrmCustomerAssist;
+import com.fs.crm.service.ICrmCustomerAssistService;
+
+/**
+ * 客户员工协作Service业务层处理
+ *
+ * @author fs
+ * @date 2025-05-27
+ */
+@Service
+public class CrmCustomerAssistServiceImpl implements ICrmCustomerAssistService
+{
+    @Autowired
+    private CrmCustomerAssistMapper crmCustomerAssistMapper;
+
+    /**
+     * 查询客户员工协作
+     *
+     * @param id 客户员工协作ID
+     * @return 客户员工协作
+     */
+    @Override
+    public CrmCustomerAssist selectCrmCustomerAssistById(Long id)
+    {
+        return crmCustomerAssistMapper.selectCrmCustomerAssistById(id);
+    }
+
+    /**
+     * 查询客户员工协作列表
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 客户员工协作
+     */
+    @Override
+    public List<CrmCustomerAssist> selectCrmCustomerAssistList(CrmCustomerAssist crmCustomerAssist)
+    {
+        return crmCustomerAssistMapper.selectCrmCustomerAssistList(crmCustomerAssist);
+    }
+
+    /**
+     * 新增客户员工协作
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 结果
+     */
+    @Override
+    public int insertCrmCustomerAssist(CrmCustomerAssist crmCustomerAssist)
+    {
+        crmCustomerAssist.setCreateTime(DateUtils.getNowDate());
+        return crmCustomerAssistMapper.insertCrmCustomerAssist(crmCustomerAssist);
+    }
+
+    /**
+     * 修改客户员工协作
+     *
+     * @param crmCustomerAssist 客户员工协作
+     * @return 结果
+     */
+    @Override
+    public int updateCrmCustomerAssist(CrmCustomerAssist crmCustomerAssist)
+    {
+        return crmCustomerAssistMapper.updateCrmCustomerAssist(crmCustomerAssist);
+    }
+
+    /**
+     * 批量删除客户员工协作
+     *
+     * @param ids 需要删除的客户员工协作ID
+     * @return 结果
+     */
+    @Override
+    public int deleteCrmCustomerAssistByIds(Long[] ids)
+    {
+        return crmCustomerAssistMapper.deleteCrmCustomerAssistByIds(ids);
+    }
+
+    /**
+     * 删除客户员工协作信息
+     *
+     * @param id 客户员工协作ID
+     * @return 结果
+     */
+    @Override
+    public int deleteCrmCustomerAssistById(Long id)
+    {
+        return crmCustomerAssistMapper.deleteCrmCustomerAssistById(id);
+    }
+
+    @Override
+    public int removeByCustomer(CrmCustomerAssistDeLParam param) {
+        if (param.getCustomerIds() == null || param.getCustomerIds().isEmpty()) {
+            return 0;
+        }
+        return crmCustomerAssistMapper.removeByCustomer(param);
+    }
+}

+ 86 - 5
fs-service-system/src/main/java/com/fs/crm/service/impl/CrmCustomerServiceImpl.java

@@ -21,10 +21,7 @@ import com.fs.crm.domain.*;
 import com.fs.crm.dto.CrmCustomerAssignCompanyDTO;
 import com.fs.crm.dto.CrmCustomerAssignUserDTO;
 import com.fs.crm.enums.CustomerLogEnum;
-import com.fs.crm.mapper.CrmCustomerAssignMapper;
-import com.fs.crm.mapper.CrmCustomerLogsMapper;
-import com.fs.crm.mapper.CrmCustomerMapper;
-import com.fs.crm.mapper.CrmCustomerUserMapper;
+import com.fs.crm.mapper.*;
 import com.fs.crm.param.*;
 import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.service.ICrmMsgService;
@@ -36,10 +33,12 @@ import com.fs.system.service.ISysDictTypeService;
 import lombok.Synchronized;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * 客户Service业务层处理
@@ -70,10 +69,14 @@ public class CrmCustomerServiceImpl extends ServiceImpl<CrmCustomerMapper, CrmCu
     @Autowired
     private CrmCustomerAssignMapper assignMapper;
     @Autowired
+    private CrmCustomerAssistMapper assistMapper;
+    @Autowired
     private JpushService jpushService;
 
     @Autowired
     private ISysDictTypeService dictTypeService;
+    @Autowired
+    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
     /**
      * 查询客户
      *
@@ -259,7 +262,21 @@ public class CrmCustomerServiceImpl extends ServiceImpl<CrmCustomerMapper, CrmCu
 
     @Override
     public List<CrmMyCustomerListQueryVO> selectCrmMyCustomerListQuery(CrmMyCustomerListQueryParam param) {
-        return crmCustomerMapper.selectCrmMyCustomerListQuery(param);
+        List<CrmMyCustomerListQueryVO> vos = crmCustomerMapper.selectCrmMyCustomerListQuery(param);
+        if (vos != null && !vos.isEmpty()){
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            vos.forEach(vo -> {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    List<String> companyUserName = assistMapper.selectCompanyUserNameByCustomerId(vo.getCustomerId());
+                    vo.setAssistUser(companyUserName);
+                }, threadPoolTaskExecutor);
+                futures.add(future);
+            });
+            // 等待所有任务完成
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+        }
+
+        return vos;
     }
 
     @Override
@@ -378,6 +395,8 @@ public class CrmCustomerServiceImpl extends ServiceImpl<CrmCustomerMapper, CrmCu
         customer.setPoolTime(new Date());
         customer.setCustomerUserId(0L);
         crmCustomerMapper.updateCrmCustomer(customer);
+        //删除该客户所有协作人
+        assistMapper.deleteCrmCustomerAssistByCustomerId(crmCustomerUser.getCustomerId());
         //写日志
         CrmCustomerLogs logs=new CrmCustomerLogs();
         logs.setCustomerId(customer.getCustomerId());
@@ -777,6 +796,46 @@ public class CrmCustomerServiceImpl extends ServiceImpl<CrmCustomerMapper, CrmCu
         return R.ok("分配成功");
     }
 
+    @Override
+    public R assistToUser(String opeUserName, Long opeUserId, CrmCustomeAssignParam param) {
+        Integer index=0;
+        for(CrmCustomerAssignUserDTO userDTO:param.getUsers()){
+            CompanyUser companyUser=companyUserMapper.selectCompanyUserById(userDTO.getCompanyUserId());
+            for(int i=0;i<userDTO.getCount();i++){
+                CrmCustomer customer=crmCustomerMapper.selectCrmCustomerById(param.getCustomerIds().get(index));
+                CrmCustomerAssist customerAssist=new CrmCustomerAssist();
+                customerAssist.setCompanyId(param.getCompanyId());
+                customerAssist.setCompanyUserId(userDTO.getCompanyUserId());
+                customerAssist.setCompanyUserName(companyUser.getNickName());
+                customerAssist.setCustomerId(customer.getCustomerId());
+                customerAssist.setCreateTime(new Date());
+                assistMapper.insertCrmCustomerAssist(customerAssist);
+                //写日志
+                CrmCustomerLogs logs=new CrmCustomerLogs();
+                logs.setCustomerId(customer.getCustomerId());
+                logs.setCreateTime(new Date());
+                logs.setLogsType(CustomerLogEnum.ASSIST_USER.getValue());
+                logs.setTitle(CustomerLogEnum.ASSIST_USER.getDesc());
+                logs.setRemark(opeUserName+"为客户"+customer.getCustomerName()+"添加协作人:" + companyUser.getNickName());
+                logs.setCompanyUserId(userDTO.getCompanyUserId());
+                logsMapper.insertCrmCustomerLogs(logs);
+
+                if(StringUtils.isNotEmpty(companyUser.getJpushId())){
+                    Map<String, String> extrasMap=new HashMap<>();
+                    extrasMap.put("customerId",customer.getCustomerId().toString());
+                    extrasMap.put("type","1");
+                    try {
+                        jpushService.sendRegisterIdPush("客户消息","您收到 一个客户协作邀请",extrasMap,companyUser.getJpushId());
+                    } catch (APIConnectionException e) {
+                    } catch (APIRequestException e) {
+                    }
+                }
+                index++;
+            }
+        }
+        return R.ok();
+    }
+
     @Override
     public int updateCrmCustomerSource(CrmCustomerEditSourceParam param) {
         return crmCustomerMapper.updateCrmCustomerSource(param);
@@ -870,4 +929,26 @@ public class CrmCustomerServiceImpl extends ServiceImpl<CrmCustomerMapper, CrmCu
    public List<CrmCustomerListVO> selectCrmCustomer1(){
         return crmCustomerMapper.selectCrmCustomer1();
    }
+
+    @Override
+    public List<CrmMyCustomerListQueryVO> selectCrmMyAssistListQuery(CrmMyCustomerListQueryParam param) {
+        List<CrmMyCustomerListQueryVO> vos = crmCustomerMapper.selectCrmMyAssistListQuery(param);
+
+        if (vos != null && !vos.isEmpty()){
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            vos.forEach(vo -> {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    CompanyUser companyUser = companyUserMapper.selectCompanyUserById(vo.getCompanyUserId());
+                    ArrayList<String> list = new ArrayList<>();
+                    list.add(companyUser.getNickName()+"(" + companyUser.getUserId() + ")");
+                    vo.setAssistUser(list);
+                }, threadPoolTaskExecutor);
+                futures.add(future);
+            });
+            // 等待所有任务完成
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+        }
+
+        return vos;
+    }
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/crm/vo/CrmMyCustomerListQueryVO.java

@@ -6,6 +6,7 @@ import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
+import java.util.List;
 
 @Data
 public class CrmMyCustomerListQueryVO implements Serializable
@@ -119,4 +120,7 @@ public class CrmMyCustomerListQueryVO implements Serializable
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date receiveTime;
 
+    private List<String> assistUser;
+    private Long companyUserId;
+
 }

+ 6 - 0
fs-service-system/src/main/java/com/fs/erp/converter/ErpWdtToErpOrderMapper.java

@@ -120,10 +120,16 @@ public interface ErpWdtToErpOrderMapper {
             erpOrderQuery.setExpress_code(source.getLogisticsNo());
         }
 
+        // 订单状态
+        if(source.getTradeStatus() != null) {
+            erpOrderQuery.setDelivery_state(source.getTradeStatus());
+        }
+
         ErpDeliverys erpDeliverys = new ErpDeliverys();
         erpDeliverys.setExpress_code(source.getLogisticsCode());
         erpDeliverys.setExpress_name(source.getLogisticsName());
         erpDeliverys.setMail_no(source.getLogisticsNo());
+        erpDeliverys.setDelivery(true);
 
         erpOrderQuery.setDeliverys(new ArrayList<>(Collections.singletonList(erpDeliverys)));
         return erpOrderQuery;

+ 2 - 1
fs-service-system/src/main/java/com/fs/erp/domain/ErpOrder.java

@@ -8,7 +8,7 @@ import java.util.List;
 public class ErpOrder {
     String shop_code;
     String vip_code;
-    String platform_code;
+    String platform_code; //订单编号
     Boolean repeat_allowed;
     String warehouse_code;
     String business_man_code;
@@ -30,5 +30,6 @@ public class ErpOrder {
     String receiver_address;
     String deal_datetime;
 
+    Integer isPackage;
     List<ErpOrderPayment> payments;
 }

+ 3 - 1
fs-service-system/src/main/java/com/fs/erp/domain/ErpRefundOrder.java

@@ -9,8 +9,10 @@ public class ErpRefundOrder {
     String type_code;
     String shop_code;
     String vip_code;
-    String trade_platform_code;
+    String trade_platform_code; //订单编号
     List<ErpOrderItem> details;
 
+    //运单号
+    String deliverySn;
 
 }

+ 72 - 0
fs-service-system/src/main/java/com/fs/erp/domain/FsErpFinishPush.java

@@ -0,0 +1,72 @@
+package com.fs.erp.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * erp订单完成状态推送
+ *
+ * @author AutoGenerated
+ * @date CurrentDateTime  // 这里通常会是生成日期
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FsErpFinishPush {
+
+    /**
+     * 主键,自增
+     */
+    private Long id;
+
+    /**
+     * 订单id,唯一标识订单
+     */
+    private Long orderId;
+
+    /**
+     * 任务状态:0-待处理,1-成功,2-失败,3-正在处理,4-已取消
+     */
+    private Integer taskStatus; // 或者使用 Byte
+
+    /**
+     * 重试次数,默认为 0
+     */
+    private Integer retryCount;
+
+    /**
+     * 上次执行时间,用于记录任务执行的最后时间
+     */
+    private Date lastExecuteTime;
+
+    /**
+     * 记录创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 记录更新时间
+     */
+    private Date updateTime;
+
+    /**
+     * 调用接口时传入的参数(JSON 格式)
+     */
+    private String params;
+
+    /**
+     * 调用接口返回的结果(JSON 格式)
+     */
+    private String result;
+
+    /**
+     * 错误信息(记录失败原因)
+     */
+    private String errorMessage;
+
+}

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KbStockRequest.java → fs-service-system/src/main/java/com/fs/erp/domain/KbStockRequest.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import com.alibaba.fastjson.annotation.JSONField;
 import lombok.Builder;

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KbStockResponse.java → fs-service-system/src/main/java/com/fs/erp/domain/KbStockResponse.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosOrderD1Data.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderD1Data.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import lombok.Data;
 

+ 1 - 2
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosOrderData.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderData.java

@@ -1,9 +1,8 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import lombok.Data;
 
 import java.math.BigDecimal;
-import java.util.Date;
 
 /**
  * 金博订单实体  data

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosOrderRequest.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderRequest.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import lombok.Data;
 

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosOrderResponse.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosOrderResponse.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import lombok.Data;
 

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosRefundOrderD1Data.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosRefundOrderD1Data.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import cn.hutool.core.annotation.Alias;
 import lombok.Data;

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosRefundOrderData.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosRefundOrderData.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import cn.hutool.core.annotation.Alias;
 import lombok.Data;

+ 1 - 1
fs-service-system/src/main/java/com/fs/kingbos/domian/KingbosRefundOrderRequest.java → fs-service-system/src/main/java/com/fs/erp/domain/KingbosRefundOrderRequest.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.domian;
+package com.fs.erp.domain;
 
 import lombok.Data;
 

+ 22 - 24
fs-service-system/src/main/java/com/fs/erp/dto/sdk/wangdian/api/WdtClient.java

@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.erp.dto.sdk.wangdian.utils.WebUtils;
 import com.fs.erp.dto.wdt.ErpWdtBusinessRequestParams;
+import com.fs.his.utils.ConfigUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -25,11 +26,24 @@ import java.util.Map;
 @Component
 @Slf4j
 public class WdtClient {
+	@Autowired
+	ConfigUtil configUtil;
+
+
+	private String getAppKey() {
+		return configUtil.getErpConfig().getErpWdAppKey();
+	}
+
+	private String getAppsecret() {
+		return configUtil.getErpConfig().getErpWdAppsecret();
+	}
+	private String getSid() {
+		return configUtil.getErpConfig().getErpWdSid();
+	}
+	private String getBaseUrl() {
+		return configUtil.getErpConfig().getErpWdBaseUrl();
+	}
 
-	private static String appkey;
-	private static String appsecret;
-	private static String sid;
-	private static String baseUrl;
 
 	private static volatile WdtClient wdtClient;
 
@@ -37,22 +51,6 @@ public class WdtClient {
 	private int connectTimeout = 3000;//3秒
 	private int readTimeout = 15000;//15秒
 
-	@Value("${fsConfig.erpWdAppKey}")
-	public void setAppkey(String appkey) {
-		WdtClient.appkey = appkey;
-	}
-	@Value("${fsConfig.erpWdAppsecret}")
-	public void setAppsecret(String appsecret) {
-		WdtClient.appsecret = appsecret;
-	}
-	@Value("${fsConfig.erpWdSid}")
-	public void setSid(String sid) {
-		WdtClient.sid = sid;
-	}
-	@Value("${fsConfig.erpWdBaseUrl}")
-	public void setBaseUrl(String baseUrl) {
-		WdtClient.baseUrl = baseUrl;
-	}
 
 	private WdtClient(){}
 
@@ -149,14 +147,14 @@ public class WdtClient {
 		log.info("开始执行请求,相对路径:{},请求参数:{}", relativeUrl, params);
 
 
-		params.put("appkey", appkey);
-		params.put("sid", sid);
+		params.put("appkey", getAppKey());
+		params.put("sid", getSid());
 		params.put("timestamp", Long.toString(System.currentTimeMillis()/1000));
 
-		String sign = signRequest(params, appsecret);
+		String sign = signRequest(params, getAppsecret());
 		params.put("sign", sign);
 		log.info("计算签名完成, 签名为:{}",sign);
-		String url = baseUrl + relativeUrl;
+		String url = getBaseUrl() + relativeUrl;
 		long startTime = System.currentTimeMillis();
 		String response = WebUtils.doPost(url, params, "UTF-8", connectTimeout, readTimeout, null);
 		long endTime = System.currentTimeMillis();

+ 2 - 2
fs-service-system/src/main/java/com/fs/kingbos/enums/OrderStatusEnum.java → fs-service-system/src/main/java/com/fs/erp/enums/KbOrderStatusEnum.java

@@ -1,4 +1,4 @@
-package com.fs.kingbos.enums;
+package com.fs.erp.enums;
 
 
 import lombok.AllArgsConstructor;
@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
 @Getter
 @NoArgsConstructor
 @AllArgsConstructor
-public enum OrderStatusEnum {
+public enum KbOrderStatusEnum {
 
     /**
      * 发货失败

+ 18 - 13
fs-service-system/src/main/java/com/fs/erp/service/impl/ErpGoodsServiceImpl.java

@@ -10,6 +10,8 @@ import com.fs.erp.dto.*;
 import com.fs.erp.service.IErpGoodsService;
 import com.fs.erp.service.IErpUserService;
 import com.fs.erp.utils.CommonUtils;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -23,38 +25,40 @@ public class ErpGoodsServiceImpl implements IErpGoodsService
 {
 
     @Autowired
-    FSSysConfig sysConfig;
+    ConfigUtil configUtil;
 
 
     @Override
     public BaseResponse addGoods(ErpGoods goods) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.item.add");
         param.set("code", goods.getCode());
         param.set("name", goods.getName());
         param.set("simple_name", goods.getSimple_name());
         param.set("item_add_attribute", goods.getItem_add_attribute());
         param.set("skus", goods.getSkus());
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         BaseResponse response=JSONUtil.toBean(result, BaseResponse.class);
         return response;
     }
 
     @Override
     public ErpGoodsQueryResponse getGoods(ErpGoodsQueryRequert requert) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.items.get");
         param.set("code", requert.getCode());
         param.set("page_no", requert.getPage_no());
         param.set("page_size", requert.getPage_size());
 
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         System.out.println(result);
         ErpGoodsQueryResponse response=JSONUtil.toBean(result, ErpGoodsQueryResponse.class);
         return response;
@@ -62,16 +66,17 @@ public class ErpGoodsServiceImpl implements IErpGoodsService
 
     @Override
     public ErpGoodsStockQueryResponse getGoodsStock(ErpGoodsStockQueryRequert requert) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.new.stock.get");
         param.set("barcode", requert.getBarcode());
         param.set("page_no", requert.getPage_no());
         param.set("page_size", requert.getPage_size());
 
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         ErpGoodsStockQueryResponse response=JSONUtil.toBean(result, ErpGoodsStockQueryResponse.class);
         return response;
     }

+ 28 - 21
fs-service-system/src/main/java/com/fs/erp/service/impl/ErpOrderServiceImpl.java

@@ -11,6 +11,8 @@ import com.fs.erp.dto.*;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.erp.service.IErpShopService;
 import com.fs.erp.utils.CommonUtils;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -21,12 +23,13 @@ public class ErpOrderServiceImpl implements IErpOrderService
 {
     public final static Logger LOGGER = LoggerFactory.getLogger(ErpOrderServiceImpl.class);
     @Autowired
-    FSSysConfig sysConfig;
+    ConfigUtil configUtil;
     @Override
     public ErpOrderResponse addOrder(ErpOrder order) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.trade.add");
         param.set("shop_code", order.getShop_code());
         param.set("vip_code", order.getVip_code());
@@ -48,8 +51,8 @@ public class ErpOrderServiceImpl implements IErpOrderService
         param.set("receiver_district", order.getReceiver_district());
         param.set("receiver_address", order.getReceiver_address());
         param.set("deal_datetime", order.getDeal_datetime());
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         System.out.println("erp-order:"+param.toString());
         LOGGER.info("erp-order:"+param.toString());
         LOGGER.error("erp-order-result:"+result);
@@ -59,9 +62,10 @@ public class ErpOrderServiceImpl implements IErpOrderService
 
     @Override
     public ErpOrderResponse refundOrder(ErpRefundOrder order) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.trade.return.add");
         param.set("type_code", order.getType_code());
         param.set("shop_code", order.getShop_code());
@@ -69,56 +73,59 @@ public class ErpOrderServiceImpl implements IErpOrderService
         // param.set("trade_code", order.getTrade_platform_code());
         param.set("trade_platform_code", order.getTrade_platform_code());
         param.set("item_detail", order.getDetails());
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         ErpOrderResponse response=JSONUtil.toBean(result, ErpOrderResponse.class);
         return response;
     }
 
     @Override
     public ErpDeliverysResponse getDeliver(ErpDeliverysRequest request) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.trade.deliverys.get");
         param.set("code", request.getCode());
         param.set("page_no", request.getPage_no());
         param.set("page_size", request.getPage_size());
         param.set("detail_flag", request.getDetail_flag());
 
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         ErpDeliverysResponse response=JSONUtil.toBean(result, ErpDeliverysResponse.class);
         return response;
     }
 
     @Override
     public ErpOrderQueryResponse getOrder(ErpOrderQueryRequert requert) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.trade.get");
         param.set("code", requert.getCode());
         param.set("page_no", requert.getPage_no());
         param.set("page_size", requert.getPage_size());
 
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         ErpOrderQueryResponse response=JSONUtil.toBean(result, ErpOrderQueryResponse.class);
         return response;
     }
 
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest request) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.trade.refund.update");
         param.set("tid", request.getTid());
         param.set("oid", request.getOid());
         param.set("refund_state", request.getRefund_state());
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         ErpOrderResponse response=JSONUtil.toBean(result, ErpOrderResponse.class);
         return response;
     }

+ 8 - 5
fs-service-system/src/main/java/com/fs/erp/service/impl/ErpShopServiceImpl.java

@@ -16,6 +16,8 @@ import com.fs.common.utils.StringUtils;
 import com.fs.erp.dto.ErpShopResponse;
 import com.fs.erp.service.IErpShopService;
 import com.fs.erp.utils.CommonUtils;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysConfigService;
@@ -36,16 +38,17 @@ public class ErpShopServiceImpl implements IErpShopService
 {
 
     @Autowired
-    FSSysConfig sysConfig;
+    ConfigUtil configUtil;
     @Override
     public ErpShopResponse getShop(String code) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.shop.get");
         param.set("code", code);
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         ErpShopResponse response=JSONUtil.toBean(result, ErpShopResponse.class);
         return response;
     }

+ 8 - 5
fs-service-system/src/main/java/com/fs/erp/service/impl/ErpUserServiceImpl.java

@@ -10,6 +10,8 @@ import com.fs.erp.dto.ErpShopResponse;
 import com.fs.erp.service.IErpShopService;
 import com.fs.erp.service.IErpUserService;
 import com.fs.erp.utils.CommonUtils;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import com.sun.org.apache.bcel.internal.generic.RETURN;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -24,21 +26,22 @@ public class ErpUserServiceImpl implements IErpUserService
 {
 
     @Autowired
-    FSSysConfig sysConfig;
+    ConfigUtil configUtil;
 
 
     @Override
     public BaseResponse addUser(ErpUser user) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
-        param.set("appkey", sysConfig.getErpAppKey());
-        param.set("sessionkey", sysConfig.getErpSessionKey());
+        param.set("appkey", erpConfig.getErpAppKey());
+        param.set("sessionkey", erpConfig.getErpSessionKey());
         param.set("method", "gy.erp.vip.add");
         param.set("code", user.getCode());
         param.set("name", user.getName());
         param.set("shop_code", user.getShop_code());
         param.set("receive_infos", user.getReceive_infos());
-        param.set("sign",CommonUtils.sign(param.toString(),sysConfig.getErpSecret()));
-        String result = HttpUtil.post(sysConfig.getErpUrl(), param.toString());
+        param.set("sign",CommonUtils.sign(param.toString(),erpConfig.getErpSecret()));
+        String result = HttpUtil.post(erpConfig.getErpUrl(), param.toString());
         BaseResponse response=JSONUtil.toBean(result, BaseResponse.class);
         return response;
     }

+ 168 - 54
fs-service-system/src/main/java/com/fs/kingbos/service/impl/K9OrderServiceImpl.java → fs-service-system/src/main/java/com/fs/erp/service/impl/K9OrderServiceImpl.java

@@ -1,23 +1,19 @@
-package com.fs.kingbos.service.impl;
+package com.fs.erp.service.impl;
 
 import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.PropertyNamingStrategy;
-import com.alibaba.fastjson.parser.ParserConfig;
 import com.fs.common.config.FSSysConfig;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.StringUtils;
-import com.fs.erp.dto.ErpOrderResponse;
-import com.fs.erp.dto.wdt.ErpWdtApiResponse;
-import com.fs.kingbos.domian.*;
-import com.fs.kingbos.enums.OrderStatusEnum;
-import com.fs.kingbos.service.K9OrderService;
-import com.fs.kingbos.util.SignUtil;
-import com.fs.kingbos.util.UrlUtil;
+import com.fs.erp.domain.*;
+import com.fs.erp.dto.*;
+import com.fs.erp.service.IErpOrderService;
+import com.fs.erp.utils.SignUtils;
+import com.fs.erp.utils.UrlUtils;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStoreOrderItem;
 import com.fs.store.dto.FsStoreCartDTO;
@@ -29,28 +25,24 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
-
-import java.io.IOException;
 import java.math.BigDecimal;
-import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static com.fs.kingbos.enums.OrderStatusEnum.CANCELLED;
-import static com.fs.kingbos.enums.OrderStatusEnum.NO_DELIVERED;
+import static com.fs.erp.enums.KbOrderStatusEnum.CANCELLED;
+import static com.fs.erp.enums.KbOrderStatusEnum.NO_DELIVERED;
+
 
 @Service
 @Slf4j
-public class K9OrderServiceImpl implements K9OrderService {
+public class K9OrderServiceImpl implements IErpOrderService {
     public final static Logger logger = LoggerFactory.getLogger(K9OrderServiceImpl.class);
     @Autowired
-    FSSysConfig sysConfig;
+    ConfigUtil configUtil;
     @Autowired
     private FsStoreOrderMapper fsStoreOrderMapper;
     @Autowired
@@ -58,43 +50,118 @@ public class K9OrderServiceImpl implements K9OrderService {
     @Autowired
     private FsStoreProductMapper productMapper;
     @Override
-    public KingbosOrderResponse addOrder(KingbosOrderRequest request) {
-        JSONObject param = JSONUtil.createObj();
-        param.set("key", "OL");
-        param.set("data", request.getData());
-        param.set("d1_data", request.getD1_data());
-        String timeStep = System.currentTimeMillis() + "";
-        logger.info("zyp \n【金博网络订单推送参数】:"+param);
-        String sign = SignUtil.sign(param.toString(),sysConfig.getKingbosSecret(), sysConfig.getKingbosan(), timeStep);
-        String url = UrlUtil.getUrl(sysConfig.getKingbosUrl(),sign, timeStep);
-        String json = param.toString();
-        String result = HttpUtil.post(url, json);
-        logger.info("zyp \n【金博网络订单推送返回】:"+result);
-        KingbosOrderResponse response = JSONUtil.toBean(result, KingbosOrderResponse.class);
-        return response;
+    /**
+     * 推送erp订单
+     */
+    public ErpOrderResponse addOrder(ErpOrder order) {
+        return addOmsOrder(order.getPlatform_code());
     }
 
 
-    //运单号已获取到
+    /**
+     * 退款
+     * @param order
+     * @return
+     */
+    @Override
+    public ErpOrderResponse refundOrder(ErpRefundOrder order){
+        if (order != null){
+            String deliverySn = order.getDeliverySn();
+            String orderCode = order.getTrade_platform_code();
+            if (StringUtils.isNotBlank(orderCode)){
+                if (StringUtils.isBlank(deliverySn)){
+                    //未获取订单号
+                    return refundOmsOrder2(orderCode);
+                } else {
+                    //已获取订单号
+                    return refundOrder1(orderCode);
+                }
+            }
+        }
+        return null;
+    }
+
+    private KingbosRefundOrderRequest getKingbosRefundOrderRequest(String orderCode) {
+        FsStoreOrder order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(orderCode);
+        if (order != null){
+            logger.debug("【金博网络退货单】开始构建订单数据,订单号: {}", orderCode);
+            KingbosRefundOrderRequest request = new KingbosRefundOrderRequest();
+            KingbosRefundOrderData data = new KingbosRefundOrderData();
+            data.setCorderSource("MALL");
+            String extendOrderId = order.getExtendOrderId();
+            data.setCasId(extendOrderId);
+            data.setCoId(extendOrderId);
+            request.setData(data);
+            // 3. 构建订单明细数据
+            List<KingbosRefundOrderD1Data> d1Datas = new ArrayList<>();
+
+
+            List<KingbosRefundOrderD1Data> d1_data = new ArrayList<>();
+            FsStoreOrderItem query = new FsStoreOrderItem();
+            query.setOrderId(order.getId());
+            List<FsStoreOrderItem> orderItems = storeOrderItemService.selectFsStoreOrderItemList(query);
+
+
+            d1_data = orderItems.stream()
+                    .map(item -> buildRefundOrderDetailItem(extendOrderId))
+                    .collect(Collectors.toList());
+            request.setD1_data(d1_data);
+            logger.info("【金博网络退货单】订单明细数据构建完成,明细数量: {}", d1Datas.size());
+        }
+        return null;
+    }
+
+    @Override
+    public ErpDeliverysResponse getDeliver(ErpDeliverysRequest param) {
+        return null;
+    }
+
     @Override
-    public KingbosOrderResponse refundOrder(KingbosRefundOrderRequest request) {
+    public ErpOrderQueryResponse getOrder(ErpOrderQueryRequert param) {
+        return null;
+    }
+
+    @Override
+    public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
+        return null;
+    }
+
+
+    /**
+     * 退款 运单号已获取到
+     * @param orderCode
+     * @return
+     */
+    private ErpOrderResponse refundOrder1(String orderCode) {
+        KingbosRefundOrderRequest request = getKingbosRefundOrderRequest(orderCode);
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         JSONObject param = JSONUtil.createObj();
         param.set("key", "WR");
         param.set("data", request.getData());
         param.set("d1_data", request.getD1_data());
         String timeStep = System.currentTimeMillis() + "";
-        String sign = SignUtil.sign(param.toString(),sysConfig.getKingbosSecret(), sysConfig.getKingbosan(), timeStep);
-        String url = UrlUtil.getUrl(sysConfig.getKingbosUrl(),sign, timeStep);
+        String sign = SignUtils.sign(param.toString(),erpConfig.getKingbosSecret(), erpConfig.getKingbosan(), timeStep);
+        String url = UrlUtils.getUrl(erpConfig.getKingbosUrl(),sign, timeStep);
         String result = HttpUtil.post(url, param.toString());
         logger.info("zyp \n【金博网络退货单】:"+result);
         KingbosOrderResponse response = JSONUtil.toBean(result, KingbosOrderResponse.class);
-        return response;
+        log.info("订单推送成功: {}", response);
+        ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
+        erpOrderResponse.setCode(response.getCbilid());
+        return erpOrderResponse;
     }
 
-    @Override
-    public ErpOrderResponse addOmsOrder(Long orderId) {
+
+
+
+    /**
+     * 退款 运单号未获取到  改状态
+     * @param orderCode 订单编号
+     * @return
+     */
+    private ErpOrderResponse refundOmsOrder2(String orderCode) {
         try {
-            KingbosOrderResponse response = this.createOmsOrder(orderId, NO_DELIVERED.getCode());
+            KingbosOrderResponse response = createOmsOrder(orderCode, CANCELLED.getCode());
             if (response.getIsSuccess()){
                 log.info("订单推送成功: {}", response);
                 ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
@@ -109,11 +176,14 @@ public class K9OrderServiceImpl implements K9OrderService {
         return new ErpOrderResponse();
     }
 
-    //运单号未获取到  改状态
-    @Override
-    public ErpOrderResponse refundOmsOrder(Long orderId) {
+    /**
+     * 通过订单编号推送订单 解析金博结果
+     * @param orderCode
+     * @return
+     */
+    private ErpOrderResponse addOmsOrder(String orderCode) {
         try {
-            KingbosOrderResponse response = this.createOmsOrder(orderId, CANCELLED.getCode());
+            KingbosOrderResponse response = this.createOmsOrder(orderCode, NO_DELIVERED.getCode());
             if (response.getIsSuccess()){
                 log.info("订单推送成功: {}", response);
                 ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
@@ -129,11 +199,18 @@ public class K9OrderServiceImpl implements K9OrderService {
     }
 
 
-    private KingbosOrderResponse createOmsOrder(Long orderId,String orderStatus){
-        logger.info("【金博网络订单】开始创建订单,订单ID: {}", orderId);
+    /**
+     * 组装参数得到金博结果
+     * @param orderCode
+     * @param orderStatus
+     * @return
+     */
+    private KingbosOrderResponse createOmsOrder(String orderCode,String orderStatus){
+        logger.info("【金博网络订单】开始创建订单,订单Code: {}", orderCode);
         try {
             // 1. 获取订单信息并验证
-            FsStoreOrder order = fsStoreOrderMapper.selectFsStoreOrderById(orderId);
+            FsStoreOrder order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(orderCode);
+            logger.info("【金博网络订单】订单信息获取成功,订单id: {}", order.getId());
             logger.info("【金博网络订单】订单信息获取成功,订单号: {}", order.getOrderCode());
 
             // 2. 构建金博订单数据
@@ -157,13 +234,20 @@ public class K9OrderServiceImpl implements K9OrderService {
 //                logger.error("【金博网络订单】创建订单失败,订单ID: {}, 错误信息: {}", orderId, response.getErrmsg());
 //            }
         } catch (Exception e) {
-            logger.error("【金博网络订单】创建订单失败,订单ID: {}, 错误信息: {}", orderId, e.getMessage(), e);
+            logger.error("【金博网络订单】创建订单失败,订单Code: {}, 错误信息: {}", orderCode, e.getMessage(), e);
             throw e;
         }
     }
 
 
+    /**
+     * 组装订单推送参数
+     * @param order
+     * @param orderStatus
+     * @return
+     */
     private KingbosOrderData buildKingbosOrderData(FsStoreOrder order,String orderStatus) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
         logger.debug("【金博网络订单】开始构建订单数据,订单号: {}", order.getOrderCode());
         KingbosOrderData data = new KingbosOrderData();
         // 设置基础信息
@@ -201,8 +285,8 @@ public class K9OrderServiceImpl implements K9OrderService {
         setReceiverInfo(data, order);
 
         // 设置仓库信息
-        data.setCwarehouse_code(sysConfig.getCwarehouseCode());
-        data.setCwarehouse_name(sysConfig.getCwarehouseName());
+        data.setCwarehouse_code(erpConfig.getCwarehouseCode());
+        data.setCwarehouse_name(erpConfig.getCwarehouseName());
 
         // 设置货到付款信息
         if (!order.getPayType().equals("1")) {
@@ -254,6 +338,13 @@ public class K9OrderServiceImpl implements K9OrderService {
         return d1Datas;
     }
 
+    private KingbosRefundOrderD1Data buildRefundOrderDetailItem(String orderId) {
+        KingbosRefundOrderD1Data item = new KingbosRefundOrderD1Data();
+        item.setCasId(orderId);
+        item.setCorderSource("MALL");
+        return item;
+    }
+
     private KingbosOrderD1Data buildOrderDetailItem(FsStoreOrderItem orderItem, String orderId) {
         FsStoreCartDTO cartDTO = JSONUtil.toBean(orderItem.getJsonInfo(), FsStoreCartDTO.class);
         BigDecimal quantity = new BigDecimal(orderItem.getNum());
@@ -274,6 +365,12 @@ public class K9OrderServiceImpl implements K9OrderService {
         return item;
     }
 
+    /**
+     * 发送请求
+     * @param data
+     * @param d1Datas
+     * @return
+     */
     private KingbosOrderResponse sendOrderRequest(KingbosOrderData data, List<KingbosOrderD1Data> d1Datas) {
         logger.info("【金博网络订单】开始发送订单请求,金博订单号: {}", data.getCo_id());
         KingbosOrderRequest request = new KingbosOrderRequest();
@@ -281,7 +378,7 @@ public class K9OrderServiceImpl implements K9OrderService {
         request.setD1_data(d1Datas);
 
         try {
-            KingbosOrderResponse response = this.addOrder(request);
+            KingbosOrderResponse response = addErpOrder(request);
             logger.info("【金博网络订单】订单请求发送成功,金博订单号: {}, 响应结果: {}", data.getCo_id(), response);
             return response;
         } catch (Exception e) {
@@ -289,4 +386,21 @@ public class K9OrderServiceImpl implements K9OrderService {
             throw e;
         }
     }
+
+    private KingbosOrderResponse addErpOrder(KingbosOrderRequest request) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        JSONObject param = JSONUtil.createObj();
+        param.set("key", "OL");
+        param.set("data", request.getData());
+        param.set("d1_data", request.getD1_data());
+        String timeStep = System.currentTimeMillis() + "";
+        logger.info("zyp \n【金博网络订单推送参数】:"+param);
+        String sign = SignUtils.sign(param.toString(),erpConfig.getKingbosSecret(), erpConfig.getKingbosan(), timeStep);
+        String url = UrlUtils.getUrl(erpConfig.getKingbosUrl(),sign, timeStep);
+        String json = param.toString();
+        String result = HttpUtil.post(url, json);
+        logger.info("zyp \n【金博网络订单推送返回】:"+result);
+        KingbosOrderResponse response = JSONUtil.toBean(result, KingbosOrderResponse.class);
+        return response;
+    }
 }

+ 92 - 0
fs-service-system/src/main/java/com/fs/erp/service/impl/K9StockServiceImpl.java

@@ -0,0 +1,92 @@
+package com.fs.erp.service.impl;
+
+import cn.hutool.crypto.SignUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.config.FSSysConfig;
+import com.fs.common.exception.CustomException;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.http.HttpUtils;
+import com.fs.erp.domain.ErpGoods;
+import com.fs.erp.domain.ErpGoodsStock;
+import com.fs.erp.domain.KbStockRequest;
+import com.fs.erp.domain.KbStockResponse;
+import com.fs.erp.dto.*;
+import com.fs.erp.service.IErpGoodsService;
+import com.fs.erp.utils.SignUtils;
+import com.fs.erp.utils.UrlUtils;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.utils.ConfigUtil;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class K9StockServiceImpl implements IErpGoodsService {
+
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Override
+    public BaseResponse addGoods(ErpGoods goods) {
+        return null;
+    }
+
+    @Override
+    public ErpGoodsQueryResponse getGoods(ErpGoodsQueryRequert param) {
+        return null;
+    }
+
+    @Override
+    public ErpGoodsStockQueryResponse getGoodsStock(ErpGoodsStockQueryRequert param) {
+        String barcode = param.getBarcode();
+        KbStockRequest request = KbStockRequest.builder().goodsCode(barcode).build();
+        KbStockResponse kbStockResponse = getStock(request);
+        ErpGoodsStockQueryResponse response = new ErpGoodsStockQueryResponse();
+
+        if(kbStockResponse.getSuccess()){
+            ArrayList<ErpGoodsStock> erpGoodsStocks = new ArrayList<>();
+            ErpGoodsStock erpGoodsStock = new ErpGoodsStock();
+            Integer stocks = kbStockResponse.getStock();
+            erpGoodsStock.setBarcode(barcode);
+            erpGoodsStock.setQty(stocks.toString());
+            erpGoodsStock.setSalable_qty(stocks.toString());
+            erpGoodsStocks.add(erpGoodsStock);
+            response.setStocks(erpGoodsStocks);
+        } else {
+            throw new CustomException("库存不足");
+        }
+        return response;
+    }
+
+    /**
+     * 查看库存
+     * @param request 参数
+     * @return  KbStockResponse
+     * @throws ServiceException 异常
+     */
+    private KbStockResponse getStock(KbStockRequest request) throws ServiceException {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        // 构造参数
+        String timeStep = System.currentTimeMillis() + "";
+        if (StringUtils.isBlank(request.getStockCode())) {
+            request.setStockCode(erpConfig.getCwarehouseCode());
+        }
+        String json = JSON.toJSONString(request);
+        String sign = SignUtils.sign(json, erpConfig.getKingbosSecret(), erpConfig.getKingbosan(), timeStep);
+        String url = erpConfig.getKingbosUrl().replace("do=k9save", "do=k9view");
+        url = UrlUtils.getUrl(url, sign, timeStep);
+
+        // 发送请求
+        log.info("\n【金博网络】: getStock send request url: {}", url);
+        String result = HttpUtils.doPost(url, json);
+        log.info("\n【金博网络】: getStock res:{}", result);
+        return JSONObject.parseObject(result, KbStockResponse.class);
+    }
+}

+ 14 - 2
fs-service-system/src/main/java/com/fs/erp/service/impl/WdtErpOrderServiceImpl.java

@@ -16,6 +16,9 @@ import com.fs.erp.dto.sdk.wangdian.api.WdtClient;
 import com.fs.erp.dto.sdk.wangdian.enums.*;
 import com.fs.erp.dto.wdt.*;
 import com.fs.erp.service.IErpOrderService;
+import com.fs.his.config.FsErpConfig;
+import com.fs.his.config.FsSysConfig;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStoreProduct;
 import com.fs.store.service.IFsStoreOrderItemService;
@@ -53,8 +56,11 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
     @Autowired
     private IFsStoreProductService fsStoreProductService;
 
-    @Value("${fsConfig.erpWdShopCode}")
-    private String shopCode;
+//    @Value("${fsConfig.erpWdShopCode}")
+//    private String shopCode;
+
+    @Autowired
+    ConfigUtil configUtil;
 
     /**
      * 普通推送
@@ -76,6 +82,9 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
      * @return
      */
     private ErpOrderResponse getErpOrderResponse(ErpOrder order,Integer type) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        String shopCode = erpConfig.getErpWdShopCode();
+
         WdtClient client = WdtClient.getInstance();
         //测试环境sid、appkey、密钥请到旺店通开放平台-自助对接-申请测试环境内查看,测试环境url=https://sandbox.wangdian.cn/openapi2/
         //调用正式环境时请将sid、appkey、appsecret切换为实际参数,参数在旺店通开放平台-自助对接-应用管理内应用状态为已上线的应用中查看,调用正式环境注意切换正式环境url=https://api.wangdian.cn/openapi2/
@@ -255,6 +264,9 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
 
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        String shopCode = erpConfig.getErpWdShopCode();
+
         log.info("退款单更新: {}", param);
         FsStoreOrder fsStoreOrder = fsStoreOrderService.selectFsStoreOrderByOrderCode(param.getTid());
 

+ 2 - 2
fs-service-system/src/main/java/com/fs/kingbos/util/SignUtil.java → fs-service-system/src/main/java/com/fs/erp/utils/SignUtils.java

@@ -1,9 +1,9 @@
-package com.fs.kingbos.util;
+package com.fs.erp.utils;
 
 
 import com.fs.ad.yk.utils.Md5Util;
 
-public class SignUtil {
+public class SignUtils {
 
     public static String sign(String data, String secret, String name, String timestamp) {
         String enValue = "kingbos" + secret + timestamp + name + "zy01" + data;

+ 2 - 2
fs-service-system/src/main/java/com/fs/kingbos/util/UrlUtil.java → fs-service-system/src/main/java/com/fs/erp/utils/UrlUtils.java

@@ -1,11 +1,11 @@
-package com.fs.kingbos.util;
+package com.fs.erp.utils;
 
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 
 
-public class UrlUtil {
+public class UrlUtils {
     public static String getUrl(String url, String sign, String timeStep) {
         String updatedUrl = updateURLParams(url, "timestep", timeStep);
         updatedUrl = updateURLParams(updatedUrl, "sign", sign);

+ 14 - 1
fs-service-system/src/main/java/com/fs/fastGpt/service/AiHookService.java

@@ -28,7 +28,7 @@ public interface AiHookService {
      * @param uuid     UUID
      * @param sendType 发送者类型 1用户 2客服
      * @param json     消息json
-     * @param msgType  消息类型 1文本 2图片 3动态表情
+     * @param msgType  消息类型 1文本 2图片 3动态表情 4语音
      */
     QwMessageListVO saveQwMsg(Long qwUserId, Long userId, String content, String uuid, int sendType, String json, int msgType);
 
@@ -44,4 +44,17 @@ public interface AiHookService {
      * @return  WxWorkResponseDTO
      */
     WxWorkResponseDTO<String> getFileUrl(String uuid, String fileId, String aesKey, String authKey, String fileName, Integer fileSize, Long serverId);
+
+    /**
+     * 获取文件地址
+     * @param uuid      uuid
+     * @param fileId    fileId
+     * @param aesKey    aesKey
+     * @param fileType  fileType
+     * @param fileName  fileName
+     * @param fileSize  fileSize
+     * @param serverId  serverId
+     * @return  WxWorkResponseDTO
+     */
+    WxWorkResponseDTO<String> getFileUrl(String uuid, String fileId, String aesKey, Integer fileType, String fileName, Integer fileSize, Long serverId);
 }

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

@@ -1269,7 +1269,7 @@ public class AiHookServiceImpl implements AiHookService {
      * @param uuid     UUID
      * @param sendType 发送者类型 1用户 2客服
      * @param json     消息json
-     * @param msgType  消息类型 1文本 2图片 3动态表情
+     * @param msgType  消息类型 1文本 2图片 3动态表情 4语音
      */
     @Transactional(rollbackFor = Exception.class)
     @Override
@@ -1349,9 +1349,12 @@ public class AiHookServiceImpl implements AiHookService {
             type = "image";
         } else if (msgType == 3) {
             type = "emotionDynamic";
+        } else if (msgType == 4) {
+            type = "voice";
         }
         listVO.setType(type);
         listVO.setStatus("succeed");
+        listVO.setExtId(qwMsg.getQwExtId());
         listVO.setFromUser(qwFromUser);
         listVO.setSendTime(qwMsg.getCreateTime().getTime());
         listVO.setId(qwMsg.getMsgId().toString());
@@ -1383,6 +1386,29 @@ public class AiHookServiceImpl implements AiHookService {
         return wxWorkService.downloadWeChatFile(weChatFileDTO, serverId);
     }
 
+    /**
+     * 获取文件地址
+     * @param uuid      uuid
+     * @param fileId    fileId
+     * @param aesKey    aesKey
+     * @param fileType  fileType
+     * @param fileName  fileName
+     * @param fileSize  fileSize
+     * @param serverId  serverId
+     * @return  WxWorkResponseDTO
+     */
+    @Override
+    public WxWorkResponseDTO<String> getFileUrl(String uuid, String fileId, String aesKey, Integer fileType, String fileName, Integer fileSize, Long serverId) {
+        WxDownloadFileDTO downloadFileDTO = new WxDownloadFileDTO();
+        downloadFileDTO.setUuid(uuid);
+        downloadFileDTO.setFileid(fileId);
+        downloadFileDTO.setAes_key(aesKey);
+        downloadFileDTO.setFiletype(fileType);
+        downloadFileDTO.setFile_name(fileName);
+        downloadFileDTO.setSize(fileSize);
+        return wxWorkService.downloadFile(downloadFileDTO, serverId);
+    }
+
     /**
      * 查询外部联系人
      * @param userId    用户ID

+ 30 - 0
fs-service-system/src/main/java/com/fs/his/config/FsErpConfig.java

@@ -0,0 +1,30 @@
+package com.fs.his.config;
+
+import lombok.Data;
+
+@Data
+public class FsErpConfig {
+    //erp接口
+    Integer erpOpen;//是否开启ERP
+    Integer erpType;//1:管易 2:旺店通 3:金博
+    //管易
+    String erpAppKey;
+    String erpSessionKey;
+    String erpSecret;
+    String erpUrl;
+    String erpShopCode;//店铺CODE
+    //erp旺店通
+    String erpWdAppKey;
+    String erpWdAppsecret;
+    String erpWdSid;
+    String erpWdShopCode;
+    String erpWdBaseUrl;
+    String erpWarehouseCode;
+    //金博
+    String kingbosan;//账套名称
+    String kingbosSecret;//金博密钥
+    String kingbosUrl;//金博地址
+    String corgid;//机构编码
+    String cwarehouseCode;
+    String cwarehouseName;
+}

+ 3 - 3
fs-service-system/src/main/java/com/fs/his/param/WxSendRedPacketParam.java

@@ -10,13 +10,13 @@ public class WxSendRedPacketParam implements Serializable {
 
     private String openId;
 
-    private BigDecimal amount;
+    private BigDecimal amount; //金额
 
-    private Long companyId;
+    private Long companyId; //公司id
 
     private Integer source=1;//来源 1:h5  2:看课小程序
 
-    private Integer redPacketMode;
+    private Integer redPacketMode; //红包模式
 
 
 }

+ 13 - 0
fs-service-system/src/main/java/com/fs/his/utils/ConfigUtil.java

@@ -1,6 +1,7 @@
 package com.fs.his.utils;
 
 import com.alibaba.fastjson.JSON;
+import com.fs.his.config.FsErpConfig;
 import com.fs.his.config.FsSysConfig;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
@@ -10,6 +11,7 @@ import org.springframework.stereotype.Service;
 @Service
 public class ConfigUtil {
     FsSysConfig fsSysConfig;
+    FsErpConfig fsErpConfig;
     @Autowired
     SysConfigMapper sysConfigMapper;
     public FsSysConfig getSysConfig(){
@@ -23,4 +25,15 @@ public class ConfigUtil {
             return this.fsSysConfig ;
         }
     }
+    public FsErpConfig getErpConfig(){
+        if (this.fsErpConfig==null){
+            SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("erp.config");
+            String configValue = sysConfig.getConfigValue();
+            FsErpConfig config = JSON.parseObject(configValue, FsErpConfig.class);
+            this.fsErpConfig=config;
+            return this.fsErpConfig;
+        }else {
+            return this.fsErpConfig ;
+        }
+    }
 }

+ 0 - 18
fs-service-system/src/main/java/com/fs/kingbos/service/K9OrderService.java

@@ -1,18 +0,0 @@
-package com.fs.kingbos.service;
-
-import com.fs.erp.dto.ErpOrderResponse;
-import com.fs.kingbos.domian.KingbosOrderRequest;
-import com.fs.kingbos.domian.KingbosOrderResponse;
-import com.fs.kingbos.domian.KingbosRefundOrderRequest;
-
-import java.text.ParseException;
-
-public interface K9OrderService {
-    KingbosOrderResponse addOrder(KingbosOrderRequest request);
-
-    KingbosOrderResponse refundOrder(KingbosRefundOrderRequest request);
-
-    ErpOrderResponse addOmsOrder(Long orderId);
-
-    ErpOrderResponse refundOmsOrder(Long orderId);
-}

+ 0 - 19
fs-service-system/src/main/java/com/fs/kingbos/service/K9StockService.java

@@ -1,19 +0,0 @@
-package com.fs.kingbos.service;
-
-import com.fs.common.exception.ServiceException;
-import com.fs.kingbos.domian.KbStockRequest;
-import com.fs.kingbos.domian.KbStockResponse;
-
-/**
- * 库存服务
- */
-public interface K9StockService {
-
-    /**
-     * 查看库存
-     * @param request 入参
-     * @return  KbStockResponse
-     * @throws ServiceException 异常
-     */
-    KbStockResponse getStock(KbStockRequest request) throws ServiceException;
-}

+ 0 - 49
fs-service-system/src/main/java/com/fs/kingbos/service/impl/K9StockServiceImpl.java

@@ -1,49 +0,0 @@
-package com.fs.kingbos.service.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.fs.common.config.FSSysConfig;
-import com.fs.common.exception.ServiceException;
-import com.fs.common.utils.StringUtils;
-import com.fs.common.utils.http.HttpUtils;
-import com.fs.kingbos.domian.KbStockRequest;
-import com.fs.kingbos.domian.KbStockResponse;
-import com.fs.kingbos.service.K9StockService;
-import com.fs.kingbos.util.SignUtil;
-import com.fs.kingbos.util.UrlUtil;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-@Service
-@Slf4j
-@AllArgsConstructor
-public class K9StockServiceImpl implements K9StockService {
-
-    private final FSSysConfig sysConfig;
-
-    /**
-     * 查看库存
-     * @param request 参数
-     * @return  KbStockResponse
-     * @throws ServiceException 异常
-     */
-    @Override
-    public KbStockResponse getStock(KbStockRequest request) throws ServiceException {
-        // 构造参数
-        String timeStep = System.currentTimeMillis() + "";
-        if (StringUtils.isBlank(request.getStockCode())) {
-            request.setStockCode(sysConfig.getCwarehouseCode());
-        }
-        String json = JSON.toJSONString(request);
-        String sign = SignUtil.sign(json, sysConfig.getKingbosSecret(), sysConfig.getKingbosan(), timeStep);
-        String url = sysConfig.getKingbosUrl().replace("do=k9save", "do=k9view");
-        url = UrlUtil.getUrl(url, sign, timeStep);
-
-        // 发送请求
-        log.info("\n【金博网络】: getStock send request url: {}", url);
-        String result = HttpUtils.doPost(url, json);
-        log.info("\n【金博网络】: getStock res:{}", result);
-        return JSONObject.parseObject(result, KbStockResponse.class);
-    }
-}

+ 2 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwExternalContactInfoMapper.java

@@ -69,4 +69,6 @@ public interface QwExternalContactInfoMapper
             "ON DUPLICATE KEY UPDATE " +
             "talk = VALUES(talk);")
     int updateQwExternalContactInfoByExtId(Long id);
+
+    void updateQwExternalContactInfoByExternalContactId(QwExternalContactInfo qwExternalContactInfo);
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -388,4 +388,8 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     List<FastgptChatArtificialWords> selectChatGptChatArtificialWords();
 
     List<GroupUserExternalVo> selectByGroupUser(@Param("ids") List<String> ids);
+
+    @Select("SELECT id,external_user_id,name,avatar,remark,description FROM qw_external_contact " +
+            " WHERE id = #{qwExternalContactId}")
+    QwExternalContact getQwExternalContactDetailsById(Long qwExternalContactId);
 }

+ 7 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -374,4 +374,11 @@ public interface QwUserMapper extends BaseMapper<QwUser>
     @Select("select qw_user_id from qw_user where company_user_id = ${companyUserId}")
     List<String> findQwUserIdListByCompanyUserId(@Param("companyUserId") Long companyUserId);
 
+    /**
+     * 根据企微用户id查询对应得销售id
+     * @param qwUserId
+     * @return
+     */
+    @Select("select company_user_id from qw_user where qw_user_id=${qwUserId}")
+    Long selectCompanyIdByQwUserId(@Param("qwUserId") String qwUserId);
 }

+ 19 - 0
fs-service-system/src/main/java/com/fs/qw/mapper/QwWorkTaskMapper.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.qw.domain.QwUserVoiceLog;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.vo.QwWorkTaskAllListVO;
 import com.fs.qw.vo.QwWorkTaskListVO;
 import com.fs.qw.vo.UserVOs;
 import org.apache.ibatis.annotations.Param;
@@ -132,4 +133,22 @@ public interface QwWorkTaskMapper extends BaseMapper<QwWorkTask>{
     List<QwWorkTask> selectQwWorkTaskByExtIdAndQwUserId(QwUserVoiceLog qwUserVoiceLog);
 
     List<UserVOs> getQwUserList(@Param("userId") Long userId, @Param("qwUserId") String qwUserId);
+
+    @Select({"<script> " +
+            "select t.qw_user_id,qw.qw_user_name,ANY_VALUE(cu.nick_name) companyUserName,DATE(t.create_time) createTime,\n" +
+            "\t\tSUM(CASE WHEN t.status = 0 THEN 1 ELSE 0 END) AS status0,\n" +
+            "    SUM(CASE WHEN t.status = 1 THEN 1 ELSE 0 END) AS status1,\n" +
+            "    SUM(CASE WHEN t.status = 2 THEN 1 ELSE 0 END) AS status2,\n" +
+            "    SUM(CASE WHEN t.status = 3 THEN 1 ELSE 0 END) AS status3\n" +
+            "\t\tfrom qw_work_task t  LEFT JOIN qw_user qw ON qw.id = t.qw_user_id LEFT JOIN company_user cu on cu.user_id=t.company_user_id  where  t.company_id=#{companyId} " +
+            "    <if test=\"companyUserId != null \"> and t.company_user_id = #{companyUserId}</if>\n" +
+            "    <if test=\"companyUserName != null and companyUserName != ''\"> and cu.nick_name = #{companyUserName}</if>\n" +
+            "    <if test=\"qwUserName != null and qwUserName != ''\"> and qw.qw_user_name = #{qwUserName}</if>\n" +
+            " " +
+            "    <if test=\"sTime != null \">  and DATE(t.create_time) &gt;= DATE(#{sTime})</if>\n" +
+            "    <if test=\"eTime != null \">  and DATE(t.create_time) &lt;= DATE(#{eTime})</if>\n" +
+            " " +
+            " GROUP BY t.qw_user_id,DATE(t.create_time) "+
+            "</script>"})
+    List<QwWorkTaskAllListVO> selectQwWorkTaskAllListVO(QwWorkTaskListParam qwWorkTask);
 }

+ 20 - 0
fs-service-system/src/main/java/com/fs/qw/param/QwWorkTaskListParam.java

@@ -1,8 +1,12 @@
 package com.fs.qw.param;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.Date;
+
 @Data
 public class QwWorkTaskListParam {
     private Long id;
@@ -41,6 +45,22 @@ public class QwWorkTaskListParam {
 
     private String title;
 
+    private String companyUserName;
+
+
+
+    private Long deptId;
+
+    private String qwUserName;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date eTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date sTime;
+
     private Long pageNum;
     private Long pageSize;
+
+
 }

+ 3 - 1
fs-service-system/src/main/java/com/fs/qw/service/IQwExternalContactInfoService.java

@@ -60,11 +60,13 @@ public interface IQwExternalContactInfoService
      */
     public int deleteQwExternalContactInfoById(Long id);
 
-    Object selectQwExternalContactInfoByExternalContactId(Long id);
+    QwExternalContactInfo selectQwExternalContactInfoByExternalContactId(Long id);
 
     int updateQwExternalContactInfoByIds(Long[] ids);
 
     void updateQwExternalContactInfoBytalk(String talkType, Long externalId);
 
     int updateQwExternalContactInfoByQwUserId(Long id);
+
+    void updateQwExternalContactInfoByExternalContactId(QwExternalContactInfo qwExternalContactInfo);
 }

+ 7 - 2
fs-service-system/src/main/java/com/fs/qw/service/IQwExternalContactService.java

@@ -3,7 +3,6 @@ package com.fs.qw.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.common.core.domain.R;
-import com.fs.common.core.domain.ResponseResult;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.dto.GetQwRepeatDataDTO;
 import com.fs.qw.param.*;
@@ -23,7 +22,6 @@ import java.io.IOException;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 /**
  * 企业微信客户Service接口
@@ -209,4 +207,11 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
     List<GetQwRepeatDataVO> getQwRepeatData(GetQwRepeatDataDTO dto);
 
     Integer selectQwIsRepeat(Long id);
+
+    /**
+     * 根据id查询外部联系人信息
+     * @param qwExternalContactId id
+     * @return QwExternalContact
+     */
+    QwExternalContact getQwExternalContactDetailsById(Long qwExternalContactId);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/qw/service/IQwWorkTaskService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwWorkTask;
 import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.vo.QwWorkTaskAllListVO;
 import com.fs.qw.vo.QwWorkTaskListVO;
 
 import java.util.List;
@@ -110,4 +111,6 @@ public interface IQwWorkTaskService extends IService<QwWorkTask>{
     List<QwWorkTask> selectQwWorkTaskListByMap(Map<String, Object> params);
 
     void addQwWorkByAiNotifyArtificial(QwUser user, Long extId, String content);
+
+    List<QwWorkTaskAllListVO> selectQwWorkTaskAllListVO(QwWorkTaskListParam qwWorkTask);
 }

+ 5 - 0
fs-service-system/src/main/java/com/fs/qw/service/impl/QwExternalContactInfoServiceImpl.java

@@ -136,4 +136,9 @@ public class QwExternalContactInfoServiceImpl implements IQwExternalContactInfoS
 
         return 1;
     }
+
+    @Override
+    public void updateQwExternalContactInfoByExternalContactId(QwExternalContactInfo qwExternalContactInfo) {
+        qwExternalContactInfoMapper.updateQwExternalContactInfoByExternalContactId(qwExternalContactInfo);
+    }
 }

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels