xw před 1 měsícem
rodič
revize
df7b1d2caf
100 změnil soubory, kde provedl 5743 přidání a 341 odebrání
  1. 139 0
      deploy - test.sh
  2. 1 1
      fs-ad-new-api/Dockerfile
  3. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/controller/CallbackController.java
  4. 3 4
      fs-ad-new-api/src/main/java/com/fs/app/controller/LandingPageController.java
  5. 14 10
      fs-ad-new-api/src/main/java/com/fs/app/controller/TestController.java
  6. 26 2
      fs-ad-new-api/src/main/java/com/fs/app/controller/TrackingController.java
  7. 61 30
      fs-ad-new-api/src/main/java/com/fs/app/controller/WeChatController.java
  8. 10 3
      fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeService.java
  9. 74 42
      fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java
  10. 3 1
      fs-ad-new-api/src/main/java/com/fs/app/facade/ConversionServiceImpl.java
  11. 2 14
      fs-ad-new-api/src/main/java/com/fs/app/mq/consumer/ConversionTrackingMessageConsumer.java
  12. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/task/ConversionRetryTask.java
  13. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java
  14. 110 0
      fs-ad-new-api/src/main/java/com/fs/framework/aspectj/ControllerLogAspect.java
  15. 54 0
      fs-ad-new-api/src/main/java/com/fs/framework/aspectj/RocketMQTraceIdAspect.java
  16. 10 0
      fs-ad-new-api/src/main/resources/application.yml
  17. 5 5
      fs-admin/src/main/java/com/fs/company/controller/CompanyController.java
  18. 13 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyDeptController.java
  19. 10 1
      fs-admin/src/main/java/com/fs/company/controller/CompanyMoneyLogsController.java
  20. 20 0
      fs-admin/src/main/java/com/fs/company/controller/CompanySmsTempController.java
  21. 46 7
      fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java
  22. 4 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyUserAllController.java
  23. 40 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyUserController.java
  24. 60 0
      fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java
  25. 42 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java
  26. 7 4
      fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java
  27. 35 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCategoryController.java
  28. 8 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseController.java
  29. 19 1
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  30. 35 1
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java
  31. 4 2
      fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java
  32. 23 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  33. 18 0
      fs-admin/src/main/java/com/fs/crm/controller/CrmCustomerController.java
  34. 133 0
      fs-admin/src/main/java/com/fs/fastGpt/FastGptChatMsgController.java
  35. 116 0
      fs-admin/src/main/java/com/fs/fastGpt/FastGptChatMsgLogsController.java
  36. 121 0
      fs-admin/src/main/java/com/fs/fastGpt/FastGptChatSessionController.java
  37. 2 2
      fs-admin/src/main/java/com/fs/fastGpt/FastgptEventLogTotalController.java
  38. 20 0
      fs-admin/src/main/java/com/fs/fastGpt/GptRoleController.java
  39. 163 0
      fs-admin/src/main/java/com/fs/his/controller/FsAiWorkflowController.java
  40. 94 4
      fs-admin/src/main/java/com/fs/his/controller/FsIntegralOrderController.java
  41. 4 1
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  42. 40 4
      fs-admin/src/main/java/com/fs/his/controller/FsStorePaymentController.java
  43. 3 0
      fs-admin/src/main/java/com/fs/his/controller/FsUserAddressController.java
  44. 13 8
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  45. 51 8
      fs-admin/src/main/java/com/fs/his/task/CompanyBalanceTask.java
  46. 93 8
      fs-admin/src/main/java/com/fs/his/task/Task.java
  47. 2 2
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java
  48. 3 2
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  49. 11 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderItemScrmController.java
  50. 57 3
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  51. 9 2
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java
  52. 11 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  53. 2 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreStatisticsScrmController.java
  54. 101 6
      fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java
  55. 40 4
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  56. 74 0
      fs-admin/src/main/java/com/fs/live/controller/FsWxExpressTaskController.java
  57. 1 1
      fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java
  58. 15 1
      fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java
  59. 28 0
      fs-admin/src/main/java/com/fs/live/controller/LiveMsgController.java
  60. 74 25
      fs-admin/src/main/java/com/fs/live/controller/OrderController.java
  61. 136 0
      fs-admin/src/main/java/com/fs/qw/controller/QwAutoTagsController.java
  62. 132 0
      fs-admin/src/main/java/com/fs/qw/controller/QwAutoTagsLogsController.java
  63. 4 4
      fs-admin/src/main/java/com/fs/qw/controller/QwCompanyController.java
  64. 56 5
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  65. 155 0
      fs-admin/src/main/java/com/fs/qw/controller/QwGroupChatController.java
  66. 71 0
      fs-admin/src/main/java/com/fs/qw/controller/QwGroupChatUserController.java
  67. 160 0
      fs-admin/src/main/java/com/fs/qw/controller/QwGroupMsgController.java
  68. 118 0
      fs-admin/src/main/java/com/fs/qw/controller/QwGroupMsgUserController.java
  69. 43 0
      fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java
  70. 11 10
      fs-admin/src/main/java/com/fs/qw/controller/QwSopTempController.java
  71. 83 0
      fs-admin/src/main/java/com/fs/qw/controller/QwTagGroupController.java
  72. 713 83
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  73. 328 0
      fs-admin/src/main/java/com/fs/qw/controller/QwUserVoiceLogController.java
  74. 241 0
      fs-admin/src/main/java/com/fs/qw/controller/QwWorkTaskNewController.java
  75. 12 0
      fs-admin/src/main/java/com/fs/qw/qwTask/qwTask.java
  76. 186 0
      fs-admin/src/main/java/com/fs/user/controller/FsUserIntegralController.java
  77. 1 1
      fs-common-api/src/main/resources/application.yml
  78. 5 0
      fs-common/src/main/java/com/fs/common/annotation/RateLimiter.java
  79. 2 0
      fs-common/src/main/java/com/fs/common/constant/LiveKeysConstant.java
  80. 16 0
      fs-common/src/main/java/com/fs/common/constant/RedisConstant.java
  81. 12 0
      fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java
  82. 172 0
      fs-common/src/main/java/com/fs/common/core/redis/service/StockDeductService.java
  83. 1 0
      fs-common/src/main/java/com/fs/common/enums/BizResponseEnum.java
  84. 367 0
      fs-common/src/main/java/com/fs/common/utils/HsCryptoUtil.java
  85. 154 0
      fs-common/src/main/java/com/fs/common/utils/StringToMapUtil.java
  86. 23 0
      fs-common/src/main/java/com/fs/common/utils/poi/ExcelUtil.java
  87. 7 6
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  88. 13 3
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  89. 1 1
      fs-company-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java
  90. 38 0
      fs-company/src/main/java/com/fs/company/controller/common/CommonController.java
  91. 117 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyAiWorkflowController.java
  92. 9 1
      fs-company/src/main/java/com/fs/company/controller/company/CompanyMoneyLogsController.java
  93. 1 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyRedPacketBalanceLogsController.java
  94. 40 3
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  95. 89 5
      fs-company/src/main/java/com/fs/company/controller/company/IndexStatisticsController.java
  96. 3 0
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  97. 11 3
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java
  98. 13 1
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  99. 9 0
      fs-company/src/main/java/com/fs/company/controller/fastGpt/FastGptRoleController.java
  100. 10 2
      fs-company/src/main/java/com/fs/company/controller/live/LiveController.java

+ 139 - 0
deploy - test.sh

@@ -0,0 +1,139 @@
+#!/bin/bash
+
+# 各服务对应的远程服务器配置 公司本地
+declare -A SERVER_CONFIG=(
+    # 服务名:IP地址
+    ["fs-live-app"]="129.28.193.135"
+)
+
+# 通用配置(所有服务器相同)
+REMOTE_USER="root"
+REMOTE_BASE_DIR="/home/software"
+
+# 本地 JAR 包路径
+LOCAL_FS_LIVE_SOCKET_JAR="./fs-live-app/target/fs-live-app.jar"
+
+# 函数:检查本地文件是否存在
+check_local_file() {
+    if [ ! -f "$1" ]; then
+        echo "错误: 本地文件 $1 不存在。"
+        exit 1
+    fi
+}
+
+# 停止远程服务器上可能正在运行的旧版本
+stop_remote_app() {
+    local remote_user=$1
+    local remote_host=$2
+    local app_name=$3
+
+    echo "正在停止 $remote_host 上的 $app_name 服务..."
+    ssh "$remote_user@$remote_host" "pkill -f $app_name || echo '没有找到运行中的进程'"
+}
+
+# 检查服务器连通性
+check_server_connectivity() {
+    local remote_user=$1
+    local remote_host=$2
+
+    if ! ssh -o ConnectTimeout=5 "$remote_user@$remote_host" "echo '连接成功'" &>/dev/null; then
+        echo "错误: 无法连接到服务器 $remote_host"
+        return 1
+    fi
+    return 0
+}
+
+# 部署单个 JAR 包到指定服务器
+deploy_jar() {
+    local local_jar=$1
+    local service_name=$2  # 服务名,用于确定目标服务器
+    local remote_dir=$3     # 远程目录名
+
+    # 获取服务对应的服务器IP
+    local remote_host="${SERVER_CONFIG[$service_name]}"
+    if [ -z "$remote_host" ]; then
+        echo "错误: 未找到服务 $service_name 的服务器配置"
+        return 1
+    fi
+
+    local app_name=$(basename "$local_jar" .jar)
+
+    echo "========================================"
+    echo "开始部署 $service_name 到服务器 $remote_host"
+    echo "本地文件: $local_jar"
+    echo "远程目录: $REMOTE_BASE_DIR/$remote_dir"
+    echo "========================================"
+
+    # 检查本地文件
+    check_local_file "$local_jar"
+
+    # 检查服务器连通性
+    if ! check_server_connectivity "$REMOTE_USER" "$remote_host"; then
+        return 1
+    fi
+
+    # 创建远程目录(如果不存在)
+    echo "创建远程目录..."
+    ssh "$REMOTE_USER@$remote_host" "mkdir -p $REMOTE_BASE_DIR/$remote_dir"
+
+    # 停止旧版本
+    stop_remote_app "$REMOTE_USER" "$remote_host" "$app_name"
+
+    # 上传 JAR 包
+    echo "上传 JAR 包..."
+    scp "$local_jar" "$REMOTE_USER@$remote_host:$REMOTE_BASE_DIR/$remote_dir/"
+
+    # 在后台启动 JAR 包
+    echo "启动服务..."
+    ssh -f "$REMOTE_USER@$remote_host" \
+    "cd $REMOTE_BASE_DIR/$remote_dir && \
+     nohup java -jar -Xms1g -Xmx2g -XX:+UseG1GC -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 $app_name.jar --spring.profiles.active=druid-ylrz --server.port=7114  \
+     >> $app_name.log 2>&1 &"
+
+    # 检查进程是否启动成功
+    if ssh "$REMOTE_USER@$remote_host" "pgrep -f $app_name" &>/dev/null; then
+        echo "✓ $service_name 部署成功到 $remote_host"
+    else
+        echo "✗ $service_name 启动失败,请检查日志: $REMOTE_BASE_DIR/$remote_dir/$app_name.log"
+        return 1
+    fi
+
+    echo ""
+}
+
+# 主要部署流程
+echo "开始多服务器分布式部署..."
+echo "部署配置:"
+for service in "${!SERVER_CONFIG[@]}"; do
+    echo "  $service -> ${SERVER_CONFIG[$service]}"
+done
+echo ""
+
+# 部署 fs-live-app
+deploy_jar "$LOCAL_FS_LIVE_SOCKET_JAR" "fs-live-app" "fs-live-app"
+
+# 部署 fs-sync (注意:这里使用了不同的JAR命名方式)
+#deploy_jar "$LOCAL_FS_SYNC_APP_JAR" "fs-sync" "fs-sync"
+
+echo "========================================"
+echo "分布式部署完成!"
+echo "各服务部署状态:"
+for service in "${!SERVER_CONFIG[@]}"; do
+    remote_host="${SERVER_CONFIG[$service]}"
+    echo "  $service -> $remote_host"
+done
+echo "========================================"
+
+# 可选:显示各服务进程状态
+#echo "服务进程状态检查:"
+#for service in "${!SERVER_CONFIG[@]}"; do
+#    remote_host="${SERVER_CONFIG[$service]}"
+#    app_name="$service"  # 简化处理,实际可能需要根据JAR文件名调整
+#    if ssh "$REMOTE_USER@$remote_host" "pgrep -f $app_name" &>/dev/null; then
+#        echo "  ✓ $service 在 $remote_host 上运行正常"
+#    else
+#        echo "  ✗ $service 在 $remote_host 上未运行"
+#    fi
+#done
+
+# 251105 0953

+ 1 - 1
fs-ad-new-api/Dockerfile

@@ -1,4 +1,4 @@
-FROM openjdk:8-jre
+FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/openjdk:8-8.6
 # java版本,最好使用openjdk,而不是类似于Java:1.8
 # java版本,最好使用openjdk,而不是类似于Java:1.8
 COPY ./target/fs-ad-new-api.jar fs-ad-new-api.jar
 COPY ./target/fs-ad-new-api.jar fs-ad-new-api.jar
 # 向外暴露的接口,最好与项目yml文件中的端口一致
 # 向外暴露的接口,最好与项目yml文件中的端口一致

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/controller/CallbackController.java

@@ -111,7 +111,7 @@ public class CallbackController {
         IAccessTokenClient tokenClient = (IAccessTokenClient) apiClient;
         IAccessTokenClient tokenClient = (IAccessTokenClient) apiClient;
         AccessTokenVo accessToken = tokenClient.getAccessTokenByAuthCode(AccessTokenByAuthCodeVo
         AccessTokenVo accessToken = tokenClient.getAccessTokenByAuthCode(AccessTokenByAuthCodeVo
                 .builder()
                 .builder()
-                .userId(byId.getAdAccountId())
+                .adAccountId(byId.getAdAccountId())
                 .appId(byId.getAppId())
                 .appId(byId.getAppId())
                 .authCode(authCode)
                 .authCode(authCode)
                 .appSecret(byId.getAppSecret())
                 .appSecret(byId.getAppSecret())

+ 3 - 4
fs-ad-new-api/src/main/java/com/fs/app/controller/LandingPageController.java

@@ -31,18 +31,17 @@ public class LandingPageController {
      * 落地页访问
      * 落地页访问
      */
      */
     @PostMapping("/h5/home")
     @PostMapping("/h5/home")
-    public Result<LandingIndexRes> track(
+    public Result<LandingIndexRes> h5Home(
             @RequestBody LandingIndexReq req) {
             @RequestBody LandingIndexReq req) {
-        log.info("落地页访问追踪:req={}", req);
         // 查询落地页模板
         // 查询落地页模板
-        return Result.success(facadeService.getLandingIndexBySiteId(req.getAllParams()));
+        return Result.success(facadeService.getLandingIndexBySiteId(req.getViewUrl(),req.getAllParams()));
     }
     }
 
 
     /**
     /**
      * 小程序访问
      * 小程序访问
      */
      */
     @PostMapping("/mini/home")
     @PostMapping("/mini/home")
-    public Result<LandingIndexRes> track(@RequestBody WeChatLandingIndexReq req) {
+    public Result<LandingIndexRes> miniHome(@RequestBody WeChatLandingIndexReq req) {
         return Result.success(facadeService.getWxLandingIndexBySiteId(req));
         return Result.success(facadeService.getWxLandingIndexBySiteId(req));
     }
     }
 
 

+ 14 - 10
fs-ad-new-api/src/main/java/com/fs/app/controller/TestController.java

@@ -1,15 +1,13 @@
 package com.fs.app.controller;
 package com.fs.app.controller;
 
 
+import com.fs.newAdv.dto.req.TraceIdDto;
 import com.fs.newAdv.integration.client.advertiser.BaiduApiClient;
 import com.fs.newAdv.integration.client.advertiser.BaiduApiClient;
 import com.fs.newAdv.enums.SystemEventTypeEnum;
 import com.fs.newAdv.enums.SystemEventTypeEnum;
 import com.fs.newAdv.event.ConversionEventPublisher;
 import com.fs.newAdv.event.ConversionEventPublisher;
 import com.fs.newAdv.service.IPromotionAccountService;
 import com.fs.newAdv.service.IPromotionAccountService;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 
 /**
 /**
  * 广告商监测链接
  * 广告商监测链接
@@ -28,15 +26,21 @@ public class TestController {
     @Autowired
     @Autowired
     private IPromotionAccountService promotionAccountService;
     private IPromotionAccountService promotionAccountService;
 
 
-    @GetMapping("/test1/{traceId}")
-    public void test1(@PathVariable("traceId") String traceId) {
+    @PostMapping("/test1")
+    public void test1(@RequestBody TraceIdDto traceId) {
         log.info("模拟 当日加群 事件完成");
         log.info("模拟 当日加群 事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.GROUP_TODAY);
+        conversionEventPublisher.publishConversionEvent(traceId.getTraceId(), SystemEventTypeEnum.GROUP_TODAY);
     }
     }
 
 
-    @GetMapping("/test2/{traceId}")
-    public void test2(@PathVariable("traceId") String traceId) {
+    @PostMapping("/test2")
+    public void test2(@RequestBody TraceIdDto traceId) {
         log.info("模拟 当日加微 事件完成");
         log.info("模拟 当日加微 事件完成");
-        conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.WEI_CHAT_TODAY);
+        conversionEventPublisher.publishConversionEvent(traceId.getTraceId(), SystemEventTypeEnum.WEI_CHAT_TODAY);
+    }
+
+    @PostMapping("/test3")
+    public void test3(@RequestBody TraceIdDto traceId) {
+        log.info("模拟 微信授权 事件完成");
+        conversionEventPublisher.publishConversionEvent(traceId.getTraceId(), SystemEventTypeEnum.AUTH_TODAY_CREATE);
     }
     }
 }
 }

+ 26 - 2
fs-ad-new-api/src/main/java/com/fs/app/controller/TrackingController.java

@@ -1,6 +1,10 @@
 package com.fs.app.controller;
 package com.fs.app.controller;
 
 
 import com.fs.app.facade.CallbackProcessingFacadeService;
 import com.fs.app.facade.CallbackProcessingFacadeService;
+import com.fs.app.facade.IConversionService;
+import com.fs.common.result.Result;
+import com.fs.newAdv.dto.req.QwExternalIdBindTrackReq;
+import com.fs.newAdv.enums.SystemEventTypeEnum;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
@@ -22,6 +26,8 @@ public class TrackingController {
     private CallbackProcessingFacadeService facadeService;
     private CallbackProcessingFacadeService facadeService;
 
 
 
 
+    @Autowired
+    private IConversionService conversionService;
     /**
     /**
      * 监测链接端口
      * 监测链接端口
      *
      *
@@ -29,14 +35,14 @@ public class TrackingController {
      * @param response  HTTP响应
      * @param response  HTTP响应
      */
      */
     @GetMapping("/click/{advertiserCode}")
     @GetMapping("/click/{advertiserCode}")
-    public void trackBaidu(
+    public void track(
             @RequestParam Map<String, String> allParams,
             @RequestParam Map<String, String> allParams,
             @PathVariable("advertiserCode") Long advertiserCode,
             @PathVariable("advertiserCode") Long advertiserCode,
             HttpServletResponse response) {
             HttpServletResponse response) {
         log.info("接收监测请求 | params={}", allParams);
         log.info("接收监测请求 | params={}", allParams);
         try {
         try {
             // 2. 保存点击追踪记录
             // 2. 保存点击追踪记录
-            facadeService.saveClickTrace(advertiserCode,allParams);
+            facadeService.saveClickTrace(advertiserCode, allParams);
             // 3. 返回 200 OK
             // 3. 返回 200 OK
             response.setStatus(HttpServletResponse.SC_OK);
             response.setStatus(HttpServletResponse.SC_OK);
         } catch (Exception e) {
         } catch (Exception e) {
@@ -45,4 +51,22 @@ public class TrackingController {
         }
         }
     }
     }
 
 
+    /**
+     * 绑定企微用户与线索
+     */
+    @PostMapping("/bind/qwTrack")
+    public Result<String> qwExternalIdBindTrack(@RequestBody QwExternalIdBindTrackReq req) {
+        facadeService.qwExternalIdBindTrack(req);
+        return Result.success();
+    }
+
+    /**
+     * 绑定企微用户与线索
+     */
+    @PostMapping("/bind/qwTrack/test/{id1}/{id2}")
+    public Result<String> qwExternalIdBindTrack1(@PathVariable String id1, @PathVariable String id2) {
+        boolean success = conversionService.reportTrackingToAdvertiser(SystemEventTypeEnum.getByCode(id1), id2);
+        return Result.success();
+    }
+
 }
 }

+ 61 - 30
fs-ad-new-api/src/main/java/com/fs/app/controller/WeChatController.java

@@ -4,18 +4,21 @@ import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.fs.app.facade.CallbackProcessingFacadeService;
 import com.fs.app.facade.CallbackProcessingFacadeService;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.result.Result;
 import com.fs.common.result.Result;
+import com.fs.newAdv.domain.AdvMiniConfig;
+import com.fs.newAdv.dto.req.updateNickNameReq;
+import com.fs.newAdv.service.IAdvMiniConfigService;
 import com.fs.wx.miniapp.config.WxMaProperties;
 import com.fs.wx.miniapp.config.WxMaProperties;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 
+import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
@@ -34,34 +37,62 @@ public class WeChatController {
     @Autowired
     @Autowired
     private WxMaProperties properties;
     private WxMaProperties properties;
 
 
+    @Autowired
+    private IAdvMiniConfigService advMiniConfigService;
+
     @GetMapping("/getSchemeUrl")
     @GetMapping("/getSchemeUrl")
     public Result<String> getSchemeUrl(@RequestParam(value = "traceId") String traceId) {
     public Result<String> getSchemeUrl(@RequestParam(value = "traceId") String traceId) {
-        String appId = "wx0447a16ef6199f03";
-        String secret = "f063fcd818e31d4c89013a67f5123990";
-        HttpResponse execute2 = HttpRequest.get("https://api.weixin.qq.com/cgi-bin/token")
-                .form("grant_type", "client_credential")
-                .form("appid", appId)
-                .form("secret", secret)
-                .timeout(SystemConstant.API_TIMEOUT)
-                .execute();
-        JSONObject obj = JSONObject.parseObject(execute2.body());
-        String access_token = obj.getString("access_token");
+        List<AdvMiniConfig> list = advMiniConfigService.list(new LambdaQueryWrapper<AdvMiniConfig>()
+                .eq(AdvMiniConfig::getStatus, 1));
+        for (AdvMiniConfig advMiniConfig : list) {
+            try {
+                String access_token = advMiniConfig.getAccessToken();
+                // 判断token是否过期
+                if (LocalDateTime.now().plusMinutes(10).isAfter(advMiniConfig.getExpiresIn())) {
+                    // 提前10分钟刷新Token
+                    HttpResponse execute2 = HttpRequest.get("https://api.weixin.qq.com/cgi-bin/token")
+                            .form("grant_type", "client_credential")
+                            .form("appid", advMiniConfig.getAppId())
+                            .form("secret", advMiniConfig.getAppSecret())
+                            .timeout(SystemConstant.API_TIMEOUT)
+                            .execute();
+                    JSONObject obj = JSONObject.parseObject(execute2.body());
+                    access_token = obj.getString("access_token");
+                    log.info("getSchemeUrl:{}", obj);
+                    advMiniConfig.setAccessToken(access_token);
+                    advMiniConfig.setExpiresIn(LocalDateTime.now().plusSeconds(obj.getInteger("expires_in")));
+                    advMiniConfigService.updateById(advMiniConfig);
+                }
+                Map<String, Object> map = new HashMap<>();
+                Map<String, Object> map2 = new HashMap<>();
+                map2.put("path", "/pages_ad/index");
+                map2.put("query", "traceId=" + traceId);
+                map2.put("env_version", "trial");
+                map.put("jump_wxa", map2);
+                map.put("is_expire", false);
+                HttpResponse execute = HttpRequest.post("https://api.weixin.qq.com/wxa/generatescheme?access_token=" + access_token)
+                        .header("Content-Type", "application/json")
+                        .body(JSONUtil.toJsonStr(map))
+                        .timeout(SystemConstant.API_TIMEOUT)
+                        .execute();
+                log.info("getSchemeUrl:{}", execute.body());
+                JSONObject jsonObject = JSONObject.parseObject(execute.body());
+                //response.addHeader("Access-Control-Allow-Origin", "*");
+                return Result.success(jsonObject.getString("openlink"));
+            }catch (Exception e){
+                log.error("getSchemeUrl error:{}",advMiniConfig.getAppId(), e);
+            }
+        }
+        return Result.success("");
+    }
 
 
-        Map<String, Object> map = new HashMap<>();
-        Map<String, Object> map2 = new HashMap<>();
-        map2.put("path", "/pages/shopping/productDetails");
-        map2.put("query", "traceId=" + traceId);
-        //map2.put("env_version", "trial");
-        map.put("jump_wxa", map2);
-        map.put("is_expire", false);
-        HttpResponse execute = HttpRequest.post("https://api.weixin.qq.com/wxa/generatescheme?access_token=" + access_token)
-                .header("Content-Type", "application/json")
-                .body(JSONUtil.toJsonStr(map))
-                .timeout(SystemConstant.API_TIMEOUT)
-                .execute();
-        log.info("getSchemeUrl:{}", execute.body());
-        obj = JSONObject.parseObject(execute.body());
-        //response.addHeader("Access-Control-Allow-Origin", "*");
-        return Result.success(obj.getString("openlink"));
+    /**
+     * 更新用户昵称
+     * @return
+     */
+    @PostMapping("/updateNickName")
+    public Result<String> updateNickName(@RequestBody updateNickNameReq req) {
+        facadeService.updateNickName(req);
+        return Result.success("");
     }
     }
 }
 }

+ 10 - 3
fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeService.java

@@ -1,6 +1,8 @@
 package com.fs.app.facade;
 package com.fs.app.facade;
 
 
+import com.fs.newAdv.dto.req.QwExternalIdBindTrackReq;
 import com.fs.newAdv.dto.req.WeChatLandingIndexReq;
 import com.fs.newAdv.dto.req.WeChatLandingIndexReq;
+import com.fs.newAdv.dto.req.updateNickNameReq;
 import com.fs.newAdv.dto.res.LandingIndexRes;
 import com.fs.newAdv.dto.res.LandingIndexRes;
 
 
 import java.util.Map;
 import java.util.Map;
@@ -10,20 +12,25 @@ public interface CallbackProcessingFacadeService {
 
 
     /**
     /**
      * 链化追踪点击
      * 链化追踪点击
+     *
      * @param allParams
      * @param allParams
      */
      */
-    void saveClickTrace(Long advertiserCode,Map<String,String> allParams);
+    void saveClickTrace(Long advertiserCode, Map<String, String> allParams);
+
     /**
     /**
      * 根据站点ID获取落地页信息
      * 根据站点ID获取落地页信息
      *
      *
-     * @param siteId
      * @return
      * @return
      */
      */
-    LandingIndexRes getLandingIndexBySiteId(Map<String, String> allParams);
+    LandingIndexRes getLandingIndexBySiteId(String viewUrl,Map<String, String> allParams);
 
 
     //----------------------code回调---------------------------------
     //----------------------code回调---------------------------------
     void gdtGetAuthCode(Integer code, Long state);
     void gdtGetAuthCode(Integer code, Long state);
 
 
 
 
     LandingIndexRes getWxLandingIndexBySiteId(WeChatLandingIndexReq req);
     LandingIndexRes getWxLandingIndexBySiteId(WeChatLandingIndexReq req);
+
+    void qwExternalIdBindTrack(QwExternalIdBindTrackReq req);
+
+    void updateNickName(updateNickNameReq req);
 }
 }

+ 74 - 42
fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java

@@ -1,22 +1,24 @@
 package com.fs.app.facade;
 package com.fs.app.facade;
 
 
+import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.fs.newAdv.integration.adapter.IAdvertiserAdapter;
-import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
-import com.fs.common.exception.base.BusinessException;
 import com.fs.common.utils.RedisUtil;
 import com.fs.common.utils.RedisUtil;
+import com.fs.common.utils.SnowflakeUtil;
 import com.fs.newAdv.domain.LandingPageTemplate;
 import com.fs.newAdv.domain.LandingPageTemplate;
 import com.fs.newAdv.domain.Lead;
 import com.fs.newAdv.domain.Lead;
 import com.fs.newAdv.domain.Site;
 import com.fs.newAdv.domain.Site;
+import com.fs.newAdv.dto.req.QwExternalIdBindTrackReq;
 import com.fs.newAdv.dto.req.WeChatLandingIndexReq;
 import com.fs.newAdv.dto.req.WeChatLandingIndexReq;
+import com.fs.newAdv.dto.req.updateNickNameReq;
 import com.fs.newAdv.dto.res.LandingIndexRes;
 import com.fs.newAdv.dto.res.LandingIndexRes;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
 import com.fs.newAdv.enums.AdvertiserTypeEnum;
+import com.fs.newAdv.integration.adapter.IAdvertiserAdapter;
+import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
 import com.fs.newAdv.service.ILandingPageTemplateService;
 import com.fs.newAdv.service.ILandingPageTemplateService;
 import com.fs.newAdv.service.ILeadService;
 import com.fs.newAdv.service.ILeadService;
 import com.fs.newAdv.service.ISiteService;
 import com.fs.newAdv.service.ISiteService;
@@ -72,50 +74,53 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
     public void saveClickTrace(Long advertiserCode, Map<String, String> allParams) {
     public void saveClickTrace(Long advertiserCode, Map<String, String> allParams) {
         IAdvertiserAdapter advertiserAdapter = handlerFactory.getAdapter(AdvertiserTypeEnum.getByCode(advertiserCode));
         IAdvertiserAdapter advertiserAdapter = handlerFactory.getAdapter(AdvertiserTypeEnum.getByCode(advertiserCode));
         Lead lead = advertiserAdapter.adaptCallbackData(allParams);
         Lead lead = advertiserAdapter.adaptCallbackData(allParams);
-        Lead byTraceId = leadService.getByTraceId(lead.getTraceId());
-        if (ObjectUtil.isNotEmpty(byTraceId)) {
-            throw new BusinessException("监测信息已存在: " + lead.getTraceId());
+        Lead byClickId = leadService.getByClickId(lead.getTraceId());
+        if (ObjectUtil.isNotEmpty(byClickId)) {
+            log.info("线索已存在:{}", lead.getTraceId());
+            return;
         }
         }
         lead.setStatus(0);
         lead.setStatus(0);
         lead.setClickTrigger(1);
         lead.setClickTrigger(1);
         lead.setTraceRawParams(JSONUtil.toJsonStr(allParams));
         lead.setTraceRawParams(JSONUtil.toJsonStr(allParams));
+        lead.setTraceId(SnowflakeUtil.randomUUID());
         boolean saved = leadService.save(lead);
         boolean saved = leadService.save(lead);
         if (!saved) {
         if (!saved) {
             log.error("线索保存失败:{}", lead);
             log.error("线索保存失败:{}", lead);
-            throw new RuntimeException("线索保存失败");
         }
         }
     }
     }
 
 
     private String getTraceIdByAdvertiser(AdvertiserTypeEnum byCode, Map<String, String> allParams) {
     private String getTraceIdByAdvertiser(AdvertiserTypeEnum byCode, Map<String, String> allParams) {
-        String traceId;
+        String clickId;
         switch (byCode) {
         switch (byCode) {
             case OCEANENGINE:
             case OCEANENGINE:
             case TENCENT:
             case TENCENT:
+                clickId = allParams.get("clickid");
+                break;
             case OPPO:
             case OPPO:
-                traceId = allParams.get("click_id");
+                clickId = allParams.get("tid");
                 break;
                 break;
             case BAIDU:
             case BAIDU:
-                traceId = allParams.get("bd_vid");
+                clickId = allParams.get("bd_vid");
                 break;
                 break;
             case VIVO:
             case VIVO:
-                traceId = allParams.get("requestId");
+                clickId = allParams.get("requestId");
                 break;
                 break;
             case IQIYI:
             case IQIYI:
-                traceId = allParams.get("traceId");
+                clickId = allParams.get("traceId");
                 break;
                 break;
             default:
             default:
-                traceId = "ylrz_test";
+                clickId = "ylrz_test";
         }
         }
-        if (StrUtil.isEmpty(traceId)) {
-            traceId = "ylrz_test";
+        if (StrUtil.isEmpty(clickId)) {
+            clickId = "ylrz_test" + IdUtil.randomUUID();
         }
         }
 
 
-        return traceId;
+        return clickId;
     }
     }
 
 
     @Override
     @Override
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
-    public LandingIndexRes getLandingIndexBySiteId(Map<String, String> allParams) {
+    public LandingIndexRes getLandingIndexBySiteId(String viewUrl, Map<String, String> allParams) {
         // 站点信息
         // 站点信息
         String paramsSiteId = allParams.get("siteId");
         String paramsSiteId = allParams.get("siteId");
         if (ObjectUtil.isEmpty(paramsSiteId)) {
         if (ObjectUtil.isEmpty(paramsSiteId)) {
@@ -131,29 +136,29 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
         // 广告商信息
         // 广告商信息
         Long advertiserId = byId.getAdvertiserId();
         Long advertiserId = byId.getAdvertiserId();
         // 访问链路id
         // 访问链路id
-        String traceId = getTraceIdByAdvertiser(Objects.requireNonNull(AdvertiserTypeEnum.getByCode(advertiserId)), allParams);
+        String clickId = getTraceIdByAdvertiser(Objects.requireNonNull(AdvertiserTypeEnum.getByCode(advertiserId)), allParams);
         // 线索信息
         // 线索信息
-        Lead byTraceId = leadService.getByTraceId(traceId);
-        boolean isNewLead = ObjectUtil.isEmpty(byTraceId);
+        Lead lead = leadService.getByClickId(clickId);
+        boolean isNewLead = ObjectUtil.isEmpty(lead);
         if (isNewLead) {
         if (isNewLead) {
             IAdvertiserAdapter advertiserAdapter = handlerFactory.getAdapter(AdvertiserTypeEnum.getByCode(advertiserId));
             IAdvertiserAdapter advertiserAdapter = handlerFactory.getAdapter(AdvertiserTypeEnum.getByCode(advertiserId));
-            byTraceId = advertiserAdapter.adaptCallbackData(allParams);
-            byTraceId.setAdvertiserId(advertiserId);
-            byTraceId.setSiteId(siteId);
-            byTraceId.setTraceId(traceId);
+            lead = advertiserAdapter.adaptCallbackData(allParams);
+            lead.setAdvertiserId(advertiserId);
+            lead.setSiteId(siteId);
+            lead.setClickId(clickId);
             // 设置站点和落地页的关联
             // 设置站点和落地页的关联
-            setSiteByIdeaId(siteId, byTraceId.getIdeaId());
+            setSiteByIdeaId(siteId, lead.getIdeaId());
         } else {
         } else {
             // 检查站点和广告商信息是否异常
             // 检查站点和广告商信息是否异常
-            if (!Objects.equals(byTraceId.getSiteId(), siteId)) {
-                log.info("落地页站点信息异常:{}---{}", byTraceId.getSiteId(), siteId);
+            if (!Objects.equals(lead.getSiteId(), siteId)) {
+                log.info("落地页站点信息异常:{}---{}", lead.getSiteId(), siteId);
             }
             }
-            if (!Objects.equals(byTraceId.getAdvertiserId(), advertiserId)) {
-                log.info("落地页广告商信息异常:{}---{}", byTraceId.getAdvertiserId(), advertiserId);
+            if (!Objects.equals(lead.getAdvertiserId(), advertiserId)) {
+                log.info("落地页广告商信息异常:{}---{}", lead.getAdvertiserId(), advertiserId);
             }
             }
         }
         }
         // 模板缓存
         // 模板缓存
-        Object ca = redisUtil.get(TEMPLATE_DATA + traceId);
+/*        Object ca = redisUtil.get(TEMPLATE_DATA + traceId);
         String templateData;
         String templateData;
         if (ca != null) {
         if (ca != null) {
             templateData = String.valueOf(ca);
             templateData = String.valueOf(ca);
@@ -164,26 +169,34 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
             // 替换二维码链接
             // 替换二维码链接
             updateQrCodeInTemplate(jsonObject, traceId, byId, byTraceId);
             updateQrCodeInTemplate(jsonObject, traceId, byId, byTraceId);
             templateData = JSONUtil.toJsonStr(jsonObject);
             templateData = JSONUtil.toJsonStr(jsonObject);
-        }
+        }*/
 
 
+        // 查询模板数据
+        LandingPageTemplate landingPageTemplate = landingPageTemplateService.getById(byId.getLaunchPageId());
+        JSONObject jsonObject = JSONUtil.parseObj(landingPageTemplate.getTemplateData());
+        // 替换二维码链接
+        updateQrCodeInTemplate(jsonObject, byId, lead);
+        String templateData = JSONUtil.toJsonStr(jsonObject);
 
 
         // 保存或更新 线索信息
         // 保存或更新 线索信息
         LocalDateTime now = LocalDateTime.now();
         LocalDateTime now = LocalDateTime.now();
-        byTraceId.setLandingPageRawParams(JSONUtil.toJsonStr(allParams));
-        byTraceId.setLandingPageTrigger(1);
-        byTraceId.setLandingPageTs(now);
-        byTraceId.setUpdateTime(now);
+        lead.setLandingPageRawParams(JSONUtil.toJsonStr(allParams));
+        lead.setLandingPageTrigger(1);
+        lead.setLandingPageTs(now);
+        lead.setUpdateTime(now);
+        lead.setViewUrl(viewUrl);
         if (isNewLead) {
         if (isNewLead) {
-            leadService.save(byTraceId);
+            lead.setTraceId(SnowflakeUtil.randomUUID());
+            leadService.save(lead);
         } else {
         } else {
-            leadService.updateById(byTraceId);
+            leadService.updateById(lead);
         }
         }
 
 
         // 封装返回结果
         // 封装返回结果
         LandingIndexRes res = new LandingIndexRes();
         LandingIndexRes res = new LandingIndexRes();
-        redisUtil.set(TEMPLATE_DATA + traceId, templateData, 24, TimeUnit.HOURS);
+        redisUtil.set(TEMPLATE_DATA + lead.getTraceId(), templateData, 24, TimeUnit.HOURS);
         res.setTemplateData(templateData);
         res.setTemplateData(templateData);
-        res.setTraceId(byTraceId.getTraceId());
+        res.setTraceId(lead.getTraceId());
         return res;
         return res;
     }
     }
 
 
@@ -196,7 +209,7 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
     /**
     /**
      * 更新模板中的二维码信息
      * 更新模板中的二维码信息
      */
      */
-    private void updateQrCodeInTemplate(JSONObject templateData, String traceId, Site site, Lead lead) {
+    private void updateQrCodeInTemplate(JSONObject templateData, Site site, Lead lead) {
         JSONArray configList = templateData.getJSONArray("configList");
         JSONArray configList = templateData.getJSONArray("configList");
         if (configList == null || configList.isEmpty()) {
         if (configList == null || configList.isEmpty()) {
             return;
             return;
@@ -221,6 +234,7 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
                                                Integer allocationRule,
                                                Integer allocationRule,
                                                Long allocationRuleId,
                                                Long allocationRuleId,
                                                Lead byTraceId) {
                                                Lead byTraceId) {
+        log.info("开始获取广告二维码: {} {} {} {}", launchType, allocationRule, allocationRuleId, byTraceId);
         // 二维码
         // 二维码
         String qrCode = "";
         String qrCode = "";
         if (allocationRule == 1) {
         if (allocationRule == 1) {
@@ -278,10 +292,28 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
     @Override
     @Override
     public LandingIndexRes getWxLandingIndexBySiteId(WeChatLandingIndexReq req) {
     public LandingIndexRes getWxLandingIndexBySiteId(WeChatLandingIndexReq req) {
         // 更新授权页访问信息记录
         // 更新授权页访问信息记录
-        leadService.updateAuthIndex(req.getTraceId(),req.getType());
+        leadService.updateAuthIndex(req.getTraceId(), req.getType());
         String templateData = String.valueOf(redisUtil.get(TEMPLATE_DATA + req.getTraceId()));
         String templateData = String.valueOf(redisUtil.get(TEMPLATE_DATA + req.getTraceId()));
         LandingIndexRes landingIndexRes = new LandingIndexRes();
         LandingIndexRes landingIndexRes = new LandingIndexRes();
         landingIndexRes.setTemplateData(templateData);
         landingIndexRes.setTemplateData(templateData);
         return landingIndexRes;
         return landingIndexRes;
     }
     }
+
+    @Override
+    public void qwExternalIdBindTrack(QwExternalIdBindTrackReq req) {
+        // 广告线索处理
+        leadService.updateAddMemberLead(req.getQwExternalId(), req.getUnionid());
+    }
+
+    @Override
+    public void updateNickName(updateNickNameReq req) {
+        Lead byTraceId = leadService.getByTraceId(req.getTraceId());
+        if (ObjectUtil.isEmpty(byTraceId)) {
+            log.error("更新昵称失败,未找到线索:{}", req);
+        }
+        Lead update = new Lead();
+        update.setId(byTraceId.getId());
+        update.setWeiChatName(req.getNickName());
+        leadService.updateById(update);
+    }
 }
 }

+ 3 - 1
fs-ad-new-api/src/main/java/com/fs/app/facade/ConversionServiceImpl.java

@@ -96,7 +96,7 @@ public class ConversionServiceImpl implements IConversionService {
             return false;
             return false;
         }
         }
 
 
-        Map<String, Object> params = JSONUtil.toBean(lead.getTraceRawParams(), Map.class);
+        Map<String, Object> params = JSONUtil.toBean(lead.getLandingPageRawParams(), Map.class);
         // 构建回传参数
         // 构建回传参数
         Map<String, Object> conversionData = new HashMap<>();
         Map<String, Object> conversionData = new HashMap<>();
         // ------------------------------通用参数----------
         // ------------------------------通用参数----------
@@ -109,6 +109,8 @@ public class ConversionServiceImpl implements IConversionService {
         conversionData.put("traceId", traceId);
         conversionData.put("traceId", traceId);
         conversionData.put("eventType", advertiserEventType.getAdvertiserEventType());
         conversionData.put("eventType", advertiserEventType.getAdvertiserEventType());
         conversionData.put("timestamp", System.currentTimeMillis() / 1000);
         conversionData.put("timestamp", System.currentTimeMillis() / 1000);
+        conversionData.put("viewUrl", lead.getViewUrl());
+
         // 添加平台适应参数
         // 添加平台适应参数
         IAdvertiserAdapter adapter = advertiserHandlerFactory.getAdapter(AdvertiserTypeEnum.getByCode(callbackAccount.getAdvertiserId()));
         IAdvertiserAdapter adapter = advertiserHandlerFactory.getAdapter(AdvertiserTypeEnum.getByCode(callbackAccount.getAdvertiserId()));
         adapter.uploadConversionData(conversionData, params);
         adapter.uploadConversionData(conversionData, params);

+ 2 - 14
fs-ad-new-api/src/main/java/com/fs/app/mq/consumer/ConversionTrackingMessageConsumer.java

@@ -2,17 +2,13 @@ package com.fs.app.mq.consumer;
 
 
 import com.fs.app.facade.IConversionService;
 import com.fs.app.facade.IConversionService;
 import com.fs.common.annotation.DistributeLock;
 import com.fs.common.annotation.DistributeLock;
-import com.fs.common.utils.RedisUtil;
-import com.fs.common.utils.TraceIdUtil;
 import com.fs.newAdv.constant.ConversionTrackingMessage;
 import com.fs.newAdv.constant.ConversionTrackingMessage;
 import com.fs.newAdv.constant.MqTopicConstant;
 import com.fs.newAdv.constant.MqTopicConstant;
 import com.fs.newAdv.enums.SystemEventTypeEnum;
 import com.fs.newAdv.enums.SystemEventTypeEnum;
-import com.fs.newAdv.mapper.ConversionLogMapper;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.rocketmq.spring.annotation.ConsumeMode;
 import org.apache.rocketmq.spring.annotation.ConsumeMode;
 import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
 import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
 import org.apache.rocketmq.spring.core.RocketMQListener;
 import org.apache.rocketmq.spring.core.RocketMQListener;
-import org.jboss.logging.MDC;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
@@ -25,23 +21,17 @@ import org.springframework.stereotype.Component;
 @Component
 @Component
 @RocketMQMessageListener(
 @RocketMQMessageListener(
         topic = MqTopicConstant.CONVERSION_TRACKING_TOPIC,
         topic = MqTopicConstant.CONVERSION_TRACKING_TOPIC,
-        consumerGroup = MqTopicConstant.CONVERSION_TRACKING_TOPIC_CONSUMER_GROUP,
+        consumerGroup = MqTopicConstant.CONVERSION_TRACKING_GROUP,
         // 并发消费模式(多线程并发消费,线程数由RocketMQ自动管理)
         // 并发消费模式(多线程并发消费,线程数由RocketMQ自动管理)
         consumeMode = ConsumeMode.CONCURRENTLY,
         consumeMode = ConsumeMode.CONCURRENTLY,
         // 最大重试次数(RocketMQ默认16次)
         // 最大重试次数(RocketMQ默认16次)
-        maxReconsumeTimes = 16
+        maxReconsumeTimes = 3
 )
 )
 public class ConversionTrackingMessageConsumer implements RocketMQListener<ConversionTrackingMessage> {
 public class ConversionTrackingMessageConsumer implements RocketMQListener<ConversionTrackingMessage> {
 
 
     @Autowired
     @Autowired
     private IConversionService conversionService;
     private IConversionService conversionService;
 
 
-    @Autowired
-    private RedisUtil redisUtil;
-
-    @Autowired
-    private ConversionLogMapper conversionLogMapper;
-
     /**
     /**
      * 消费转化消息
      * 消费转化消息
      *
      *
@@ -50,8 +40,6 @@ public class ConversionTrackingMessageConsumer implements RocketMQListener<Conve
     @Override
     @Override
     @DistributeLock(scene = "mq", keyExpression = "#message.traceId", waitTime = 0, errorMsg = "重复消费")
     @DistributeLock(scene = "mq", keyExpression = "#message.traceId", waitTime = 0, errorMsg = "重复消费")
     public void onMessage(ConversionTrackingMessage message) {
     public void onMessage(ConversionTrackingMessage message) {
-        TraceIdUtil.put(message.getTrackId());
-
         String traceId = message.getTraceId();
         String traceId = message.getTraceId();
         SystemEventTypeEnum eventType = message.getEventType();
         SystemEventTypeEnum eventType = message.getEventType();
 
 

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/task/ConversionRetryTask.java

@@ -37,7 +37,7 @@ public class ConversionRetryTask {
      * 转化回传重试任务
      * 转化回传重试任务
      * cron: 每10分钟执行
      * cron: 每10分钟执行
      */
      */
-    @Scheduled(cron = "0 */5 * * * ?")
+    // @Scheduled(cron = "0 */5 * * * ?")
     @DistributeLock(scene = "task", key = "conversion_retry", waitTime = 0, errorMsg = "conversion_retry任务已执行")
     @DistributeLock(scene = "task", key = "conversion_retry", waitTime = 0, errorMsg = "conversion_retry任务已执行")
     public void execute() {
     public void execute() {
         // 查询待重试的转化记录
         // 查询待重试的转化记录

+ 1 - 1
fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java

@@ -59,7 +59,7 @@ public class DataSyncTask {
      * 数据同步任务->当日数据
      * 数据同步任务->当日数据
      * cron: 每1小时统计站点数据
      * cron: 每1小时统计站点数据
      */
      */
-    @Scheduled(cron = "0 0/1 * * * ?")
+    @Scheduled(cron = "0 0 0/1 * * ?")
     @DistributeLock(scene = "task", key = "sync_today_data", waitTime = 0, errorMsg = "sync_today_data任务已执行")
     @DistributeLock(scene = "task", key = "sync_today_data", waitTime = 0, errorMsg = "sync_today_data任务已执行")
     public void syncTodayData() throws InterruptedException {
     public void syncTodayData() throws InterruptedException {
         String batchNo = DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd");
         String batchNo = DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd");

+ 110 - 0
fs-ad-new-api/src/main/java/com/fs/framework/aspectj/ControllerLogAspect.java

@@ -0,0 +1,110 @@
+
+package com.fs.framework.aspectj;
+
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+@Slf4j
+@Aspect
+@Component
+public class ControllerLogAspect {
+
+    @Pointcut("execution(* com.fs.app.controller..*.*(..))")
+    public void controllerPointcut() {
+    }
+
+    @Around("controllerPointcut()")
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        long startTime = System.currentTimeMillis();
+
+        String className = joinPoint.getTarget().getClass().getName();
+        String methodName = joinPoint.getSignature().getName();
+        String fullMethodName = className + "." + methodName;
+
+        Object[] args = joinPoint.getArgs();
+        String requestParams = getRequestParams(args);
+
+        log.info("========== 接口调用开始 ==========");
+        log.info("接口方法: {}", fullMethodName);
+        log.info("请求参数: {}", requestParams);
+
+        Object result = null;
+        try {
+            result = joinPoint.proceed();
+
+            long endTime = System.currentTimeMillis();
+            long costTime = endTime - startTime;
+
+            log.info("返回结果: {}", JSON.toJSONString(result));
+            log.info("接口耗时: {} ms", costTime);
+            log.info("========== 接口调用结束 ==========");
+
+            return result;
+        } catch (Throwable e) {
+            long endTime = System.currentTimeMillis();
+            long costTime = endTime - startTime;
+
+            log.error("接口异常: {}", e.getMessage());
+            log.error("接口耗时: {} ms", costTime);
+            log.error("========== 接口调用异常 ==========");
+
+            throw e;
+        }
+    }
+
+    private String getRequestParams(Object[] args) {
+        if (args == null || args.length == 0) {
+            return "无参数";
+        }
+
+        StringBuilder params = new StringBuilder();
+        for (int i = 0; i < args.length; i++) {
+            if (args[i] != null && !isFilterObject(args[i])) {
+                try {
+                    Object jsonObj = JSON.toJSON(args[i]);
+                    params.append(jsonObj.toString());
+                    if (i < args.length - 1) {
+                        params.append(", ");
+                    }
+                } catch (Exception e) {
+                    params.append(args[i].getClass().getSimpleName());
+                }
+            }
+        }
+
+        return params.length() > 0 ? params.toString() : "无参数";
+    }
+
+    private boolean isFilterObject(final Object o) {
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray()) {
+            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+        } else if (Collection.class.isAssignableFrom(clazz)) {
+            Collection collection = (Collection) o;
+            for (Iterator iter = collection.iterator(); iter.hasNext();) {
+                return iter.next() instanceof MultipartFile;
+            }
+        } else if (Map.class.isAssignableFrom(clazz)) {
+            Map map = (Map) o;
+            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
+                Map.Entry entry = (Map.Entry) iter.next();
+                return entry.getValue() instanceof MultipartFile;
+            }
+        }
+        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+                || o instanceof BindingResult;
+    }
+}

+ 54 - 0
fs-ad-new-api/src/main/java/com/fs/framework/aspectj/RocketMQTraceIdAspect.java

@@ -0,0 +1,54 @@
+package com.fs.framework.aspectj;
+
+import com.fs.common.utils.TraceIdUtil;
+import com.fs.newAdv.constant.ConversionTrackingMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+/**
+ * RocketMQ消费者链路ID切面
+ * 在消费消息前设置链路ID到MDC,保证整个消费链路的日志可追踪
+ *
+ * @author zhangqin
+ */
+@Aspect
+@Component
+@Slf4j
+@Order(1) // 优先于 DistributeLockAspect 执行
+public class RocketMQTraceIdAspect {
+
+    @Pointcut("execution(* com.fs.app.mq.consumer.*.onMessage(..))")
+    public void mqConsumerPointcut() {
+    }
+
+    @Around("mqConsumerPointcut()")
+    public Object around(ProceedingJoinPoint pjp) throws Throwable {
+        String trackId = null;
+        try {
+            // 从消息参数中提取 trackId
+            Object[] args = pjp.getArgs();
+            if (args != null && args.length > 0) {
+                Object message = args[0];
+                if (message instanceof ConversionTrackingMessage) {
+                    trackId = ((ConversionTrackingMessage) message).getTrackId();
+                }
+            }
+            // 设置链路ID到MDC
+            if (trackId != null && !trackId.isEmpty()) {
+                TraceIdUtil.put(trackId);
+            } else {
+                // 如果没有 trackId,则生成一个新的
+                TraceIdUtil.init();
+            }
+            return pjp.proceed();
+        } finally {
+            // 清理MDC,避免线程复用时链路ID污染
+            TraceIdUtil.clear();
+        }
+    }
+}

+ 10 - 0
fs-ad-new-api/src/main/resources/application.yml

@@ -0,0 +1,10 @@
+# 开发环境配置
+server:
+  port: 7775
+# Spring配置
+spring:
+  profiles:
+    active: druid-ylrz
+#    active: druid-ylrz
+
+#

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

@@ -134,7 +134,7 @@ public class CompanyController extends BaseController
      * 新增企业
      * 新增企业
      */
      */
     @PreAuthorize("@ss.hasPermi('company:company:add')")
     @PreAuthorize("@ss.hasPermi('company:company:add')")
-    @Log(title = "企业", businessType = BusinessType.INSERT)
+    @Log(title = "企业", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping
     @PostMapping
     public R add(@RequestBody Company company)
     public R add(@RequestBody Company company)
     {
     {
@@ -153,7 +153,7 @@ public class CompanyController extends BaseController
      * 修改企业
      * 修改企业
      */
      */
     @PreAuthorize("@ss.hasPermi('company:company:edit')")
     @PreAuthorize("@ss.hasPermi('company:company:edit')")
-    @Log(title = "企业", businessType = BusinessType.UPDATE)
+    @Log(title = "企业", businessType = BusinessType.UPDATE, isStoreLog = true)
     @PutMapping
     @PutMapping
     public AjaxResult edit(@RequestBody Company company)
     public AjaxResult edit(@RequestBody Company company)
     {
     {
@@ -186,7 +186,7 @@ public class CompanyController extends BaseController
      * 删除企业
      * 删除企业
      */
      */
     @PreAuthorize("@ss.hasPermi('company:company:remove')")
     @PreAuthorize("@ss.hasPermi('company:company:remove')")
-    @Log(title = "企业", businessType = BusinessType.DELETE)
+    @Log(title = "企业", businessType = BusinessType.DELETE, isStoreLog = true)
 	@DeleteMapping("/{companyIds}")
 	@DeleteMapping("/{companyIds}")
     public AjaxResult remove(@PathVariable Long[] companyIds)
     public AjaxResult remove(@PathVariable Long[] companyIds)
     {
     {
@@ -244,7 +244,7 @@ public class CompanyController extends BaseController
 
 
 
 
     @PreAuthorize("@ss.hasPermi('company:company:recharge')")
     @PreAuthorize("@ss.hasPermi('company:company:recharge')")
-    @Log(title = "企业转账", businessType = BusinessType.INSERT)
+    @Log(title = "企业转账", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping(value = "/recharge")
     @PostMapping(value = "/recharge")
     @Transactional
     @Transactional
     @RepeatSubmit
     @RepeatSubmit
@@ -270,7 +270,7 @@ public class CompanyController extends BaseController
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('company:company:deduct')")
     @PreAuthorize("@ss.hasPermi('company:company:deduct')")
-    @Log(title = "企业扣款", businessType = BusinessType.INSERT)
+    @Log(title = "企业扣款", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping(value = "/deduct")
     @PostMapping(value = "/deduct")
     @Transactional
     @Transactional
     @RepeatSubmit
     @RepeatSubmit

+ 13 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyDeptController.java

@@ -100,4 +100,17 @@ public class CompanyDeptController extends BaseController
         List<CompanyDept> depts = companyDeptService.selectCompanyDeptList(dept);
         List<CompanyDept> depts = companyDeptService.selectCompanyDeptList(dept);
         return AjaxResult.success(companyDeptService.buildDeptTreeSelect(depts));
         return AjaxResult.success(companyDeptService.buildDeptTreeSelect(depts));
     }
     }
+
+    /**
+     * 获取部门下拉树列表
+     */
+    @GetMapping("/treeselectByCompanyId/{companyId}")
+    public AjaxResult treeselectByCompanyId(@PathVariable("companyId") Long companyId)
+    {
+        CompanyDept dept = new CompanyDept();
+        dept.setStatus("0");
+        dept.setCompanyId(companyId);
+        List<CompanyDept> depts = companyDeptService.selectCompanyDeptList(dept);
+        return AjaxResult.success(companyDeptService.buildDeptTreeSelect(depts));
+    }
 }
 }

+ 10 - 1
fs-admin/src/main/java/com/fs/company/controller/CompanyMoneyLogsController.java

@@ -73,6 +73,7 @@ public class CompanyMoneyLogsController extends BaseController
         }
         }
         if(companyMoneyLogs.getLogsType()!=null){
         if(companyMoneyLogs.getLogsType()!=null){
             if(companyMoneyLogs.getLogsType()==3 || companyMoneyLogs.getLogsType()==4 || companyMoneyLogs.getLogsType()==5 || companyMoneyLogs.getLogsType()==6 || companyMoneyLogs.getLogsType()==13|| companyMoneyLogs.getLogsType()==14){
             if(companyMoneyLogs.getLogsType()==3 || companyMoneyLogs.getLogsType()==4 || companyMoneyLogs.getLogsType()==5 || companyMoneyLogs.getLogsType()==6 || companyMoneyLogs.getLogsType()==13|| companyMoneyLogs.getLogsType()==14){
+                // 根据type字段判断,支持商城订单(type=0)和直播订单(type=1)
                 List<CompanyMoneyLogsVO> list = companyMoneyLogsService.selectCompanyMoneyLogsMallVOList(companyMoneyLogs);
                 List<CompanyMoneyLogsVO> list = companyMoneyLogsService.selectCompanyMoneyLogsMallVOList(companyMoneyLogs);
                 return getDataTable(list);
                 return getDataTable(list);
             }
             }
@@ -156,7 +157,15 @@ public class CompanyMoneyLogsController extends BaseController
             task.setStatus(0);
             task.setStatus(0);
             task.setStartTime(new Date());
             task.setStartTime(new Date());
             task.setSysType(1);
             task.setSysType(1);
-            task.setRemark("导出商城订单明细");
+            String remark = "导出订单明细";
+            if (param.getOrderType() != null) {
+                if (param.getOrderType() == 0) {
+                    remark = "导出商城订单明细";
+                } else if (param.getOrderType() == 1) {
+                    remark = "导出直播订单明细";
+                }
+            }
+            task.setRemark(remark);
             task.setUserId(SecurityUtils.getUserId());
             task.setUserId(SecurityUtils.getUserId());
             exportTaskService.insertFsExportTask(task);
             exportTaskService.insertFsExportTask(task);
             param.setTaskId(task.getTaskId());
             param.setTaskId(task.getTaskId());

+ 20 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanySmsTempController.java

@@ -8,7 +8,9 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanySmsTemp;
 import com.fs.company.domain.CompanySmsTemp;
+import com.fs.company.param.CompanySmsTempListQueryParam;
 import com.fs.company.service.ICompanySmsTempService;
 import com.fs.company.service.ICompanySmsTempService;
+import com.fs.company.vo.CompanySmsTempListQueryVO;
 import com.fs.company.vo.CompanySmsTempListVO;
 import com.fs.company.vo.CompanySmsTempListVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -112,4 +114,22 @@ public class CompanySmsTempController extends BaseController
             return R.error("操作失败");
             return R.error("操作失败");
         }
         }
     }
     }
+
+    /**
+     * 总后台取不到companyId,全部展示
+     * @return
+     */
+    @GetMapping("/getSmsTempList")
+    public R getSmsTempList()
+    {
+        CompanySmsTempListQueryParam maps=new CompanySmsTempListQueryParam();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        maps.setCompanyId(loginUser.getCompany().getCompanyId());
+        maps.setStatus(1);
+        maps.setIsAudit(1);
+//        List<CompanySmsTempListQueryVO> list = companySmsTempService.selectCompanySmsTempListQuery(maps);
+        List<CompanySmsTempListQueryVO> list = companySmsTempService.selectCompanySmsTempListQueryForAdmin(maps);
+
+        return R.ok().put("data",list);
+    }
 }
 }

+ 46 - 7
fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java

@@ -1,9 +1,12 @@
 package com.fs.company.controller;
 package com.fs.company.controller;
 
 
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.common.annotation.Excel;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.TimeUtils;
 import com.fs.common.utils.TimeUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
@@ -14,6 +17,10 @@ import com.fs.company.service.ICompanySmsLogsService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyVoiceLogsService;
 import com.fs.company.service.ICompanyVoiceLogsService;
 import com.fs.company.vo.*;
 import com.fs.company.vo.*;
+import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.service.IFinishCourseStatisticsSyncService;
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.vo.FsCourseReportVO;
 import com.fs.crm.param.CrmCustomerStatisticsParam;
 import com.fs.crm.param.CrmCustomerStatisticsParam;
 import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.service.ICrmCustomerVisitService;
 import com.fs.crm.service.ICrmCustomerVisitService;
@@ -29,15 +36,11 @@ import com.fs.his.vo.FsStoreOrderAmountStatsVo;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 
+import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -76,6 +79,12 @@ public class CompanyStatisticsController extends BaseController
     //app商城订单接口Service
     //app商城订单接口Service
     @Autowired
     @Autowired
     private IFsStoreOrderScrmService fsStoreOrderScrmService;
     private IFsStoreOrderScrmService fsStoreOrderScrmService;
+
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+
+    @Autowired
+    private IFinishCourseStatisticsSyncService finishCourseStatisticsSyncService;
     @GetMapping("/storeOrder")
     @GetMapping("/storeOrder")
     public R storeOrder(FsStoreStatisticsParam param)
     public R storeOrder(FsStoreStatisticsParam param)
     {
     {
@@ -751,4 +760,34 @@ public class CompanyStatisticsController extends BaseController
         return AjaxResult.success(scrmStatsVo);
         return AjaxResult.success(scrmStatsVo);
     }
     }
 
 
+    /**
+     * 木易华康特殊处理 课程完课统计数据
+     */
+    @GetMapping("/courseReport")
+    public TableDataInfo selectFsCourseReportVO(FsCourseWatchLogStatisticsListParam param) {
+        startPage();
+        List<FsCourseReportVO> fsCourseReportVOS = fsCourseWatchLogService.selectFsCourseReportVO(param);
+        return getDataTable(fsCourseReportVOS);
+    }
+
+    @GetMapping("/exportFsCourseReportVO")
+    public AjaxResult exportFsCourseReportVO(FsCourseWatchLogStatisticsListParam param) {
+        List<FsCourseReportVO> list = fsCourseWatchLogService.selectFsCourseReportVO(param);
+        List<String> allFields = Arrays.stream(FsCourseReportVO.class.getDeclaredFields())
+                .filter(field -> field.isAnnotationPresent(Excel.class))
+                .map(Field::getName)
+                .collect(Collectors.toList());
+        ExcelUtil<FsCourseReportVO> util = new ExcelUtil<FsCourseReportVO>(FsCourseReportVO.class);
+        return util.exportExcelSelectedColumns(list, "完课统计报表", allFields);
+    }
+
+    @PostMapping("/syncYesterday")
+    public AjaxResult syncYesterday() {
+        try {
+            finishCourseStatisticsSyncService.syncMultiDimensionStatistics();
+            return AjaxResult.success("同步昨天数据成功");
+        } catch (Exception e) {
+            return AjaxResult.error("同步失败:" + e.getMessage());
+        }
+    }
 }
 }

+ 4 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyUserAllController.java

@@ -415,6 +415,8 @@ public class CompanyUserAllController extends BaseController {
     /**
     /**
      * 批量修改 销售的所属区域(临时的)
      * 批量修改 销售的所属区域(临时的)
      */
      */
+    @PreAuthorize("@ss.hasPermi('company:user:updateCompanyUserAreaList')")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @PostMapping("/updateCompanyUserAreaList")
     @PostMapping("/updateCompanyUserAreaList")
     public R updateCompanyUserAreaList(@RequestBody CompanyUserAreaParam param)
     public R updateCompanyUserAreaList(@RequestBody CompanyUserAreaParam param)
     {
     {
@@ -432,6 +434,7 @@ public class CompanyUserAllController extends BaseController {
         return  R.ok().put("data",subDomain);
         return  R.ok().put("data",subDomain);
     }
     }
 
 
+    @PreAuthorize("@ss.hasPermi('company:user:setRegister')")
     @Log(title = "设置是否需要单独注册会员", businessType = BusinessType.UPDATE)
     @Log(title = "设置是否需要单独注册会员", businessType = BusinessType.UPDATE)
     @PutMapping("/setRegister")
     @PutMapping("/setRegister")
     public AjaxResult setIsRegisterMember(@RequestParam Boolean status, @RequestBody List<Long> userIds) {
     public AjaxResult setIsRegisterMember(@RequestParam Boolean status, @RequestBody List<Long> userIds) {
@@ -443,6 +446,7 @@ public class CompanyUserAllController extends BaseController {
         }
         }
     }
     }
 
 
+    @PreAuthorize("@ss.hasPermi('company:user:allowedAllRegister')")
     @Log(title = "是否允许所有方式注册会员", businessType = BusinessType.UPDATE)
     @Log(title = "是否允许所有方式注册会员", businessType = BusinessType.UPDATE)
     @PutMapping("/allowedAllRegister")
     @PutMapping("/allowedAllRegister")
     public AjaxResult isAllowedAllRegister(@RequestParam Boolean status, @RequestBody List<Long> userIds) {
     public AjaxResult isAllowedAllRegister(@RequestParam Boolean status, @RequestBody List<Long> userIds) {

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

@@ -9,14 +9,21 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
+import com.fs.his.vo.OptionsVO;
 import com.fs.qw.dto.UserProjectDTO;
 import com.fs.qw.dto.UserProjectDTO;
+import com.fs.qw.vo.QwUserVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 /**
 /**
  * 企业员工信息Controller
  * 企业员工信息Controller
@@ -173,5 +180,38 @@ public class CompanyUserController extends BaseController
         List<CompanyUser> list = companyUserService.getCompanyUserList(user);
         List<CompanyUser> list = companyUserService.getCompanyUserList(user);
         return getDataTable(list);
         return getDataTable(list);
     }
     }
+    /**
+     * 根据销售名称模糊查询
+     * @param name  名称
+     * @return  list
+     */
+    @GetMapping("/getCompanyUserListLikeName")
+    public R getCompanyUserListLikeName(@RequestParam(required = false) String name,
+                                        @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                        @RequestParam(required = false, defaultValue = "10") Integer pageSize,
+                                        @RequestParam(required = false) Long companyId) {
+        Map<String,Object> params = new HashMap<>();
+        params.put("nickName", name);
+        //查询多条数据传入公司
+//        if (pageSize>=200){
+        params.put("companyId", companyId);
+//        }
+        PageHelper.startPage(pageNum, pageSize);
+        List<OptionsVO> companyUserList = companyUserService.selectCompanyUserListByMap(params);
+        return R.ok().put("data", new PageInfo<>(companyUserList));
+    }
+    @GetMapping("/getQwAllUserList/{id}/{companyId}")
+    public R getQwAllUserList(@PathVariable("id") String corpId,@PathVariable("companyId") Long companyId)
+    {
+
+        List<QwUserVO> list = companyUserService.selectCompanyQwUserList(corpId,companyId);
+        return  R.ok().put("data",list);
+    }
+    @GetMapping("/getCompanyList/{id}")
+    public R getCompanyList(@PathVariable("id") String corpId)
+    {
 
 
+        List<Company> list = companyUserService.getCompanyList(corpId);
+        return  R.ok().put("data",list);
+    }
 }
 }

+ 60 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java

@@ -1,6 +1,7 @@
 package com.fs.course.controller;
 package com.fs.course.controller;
 
 
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -19,13 +20,17 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
+import com.fs.course.enums.MiniProgramAgreementEnum;
 import com.fs.course.param.FsCoursePlaySourceConfigCreateParam;
 import com.fs.course.param.FsCoursePlaySourceConfigCreateParam;
 import com.fs.course.param.FsCoursePlaySourceConfigEditParam;
 import com.fs.course.param.FsCoursePlaySourceConfigEditParam;
+import com.fs.course.param.MiniProgramAgreementParam;
 import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.course.service.IFsCoursePlaySourceConfigService;
 import com.fs.course.vo.FsCoursePlaySourceConfigVO;
 import com.fs.course.vo.FsCoursePlaySourceConfigVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.domain.MerchantAppConfig;
 import com.fs.his.domain.MerchantAppConfig;
 import com.fs.his.service.IMerchantAppConfigService;
 import com.fs.his.service.IMerchantAppConfigService;
+import com.fs.his.domain.MiniProgramAgreement;
+import com.fs.his.service.MiniProgramAgreementService;
 import com.fs.system.service.ISysConfigService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
@@ -44,6 +49,7 @@ public class FsCoursePlaySourceConfigController extends BaseController {
     private final IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService;
     private final IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService;
     private final TokenService tokenService;
     private final TokenService tokenService;
     private final ISysConfigService configService;
     private final ISysConfigService configService;
+    private final MiniProgramAgreementService miniProgramAgreementService;
     private final IMerchantAppConfigService merchantAppConfigService;
     private final IMerchantAppConfigService merchantAppConfigService;
 
 
 //    @PreAuthorize("@ss.hasPermi('course:playSourceConfig:list')")
 //    @PreAuthorize("@ss.hasPermi('course:playSourceConfig:list')")
@@ -282,4 +288,58 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         }
         }
         return R.ok().put("data", fsCoursePlaySourceConfigService.list(queryWrapper));
         return R.ok().put("data", fsCoursePlaySourceConfigService.list(queryWrapper));
     }
     }
+
+
+    @PreAuthorize("@ss.hasPermi('course:playSourceConfig:agreement')")
+    @Log(title = "小程序协议配置", businessType = BusinessType.UPDATE)
+    @PostMapping("/updateAgreementConfig")
+    public AjaxResult updateAgreementConfig(@RequestBody MiniProgramAgreementParam agreement) {
+        Map<String, String> map = (Map<String, String>) JSON.parse(agreement.getAgreementData());
+
+        List<MiniProgramAgreement> list = new ArrayList<>();
+        for (MiniProgramAgreementEnum agreementEnum : MiniProgramAgreementEnum.values()) {
+            String content = map.get(agreementEnum.getCode());
+            if (content != null) {
+                MiniProgramAgreement update = new MiniProgramAgreement();
+                update.setAppId(agreement.getAppId());
+                update.setAgreementType(agreementEnum.getCode());
+                update.setAgreementContent(content);
+
+                list.add(update);
+            }
+        }
+
+        if (!list.isEmpty()) {
+            miniProgramAgreementService.batchUpsertAgreements(list);
+        }
+
+        return AjaxResult.success();
+    }
+
+
+
+    @PreAuthorize("@ss.hasPermi('course:playSourceConfig:agreement')")
+    @GetMapping("/queryAgreementConfig")
+    public AjaxResult queryAgreementConfig(@RequestParam(required = true) String appid) {
+        // 查询协议数据
+        QueryWrapper<MiniProgramAgreement> queryWrapper = new QueryWrapper<MiniProgramAgreement>()
+                .eq("app_id", appid);
+        List<MiniProgramAgreement> agreements = miniProgramAgreementService.list(queryWrapper);
+
+        // 按agreementType分组
+        Map<String, String> agreementMap = new HashMap<>();
+        for (MiniProgramAgreement agreement : agreements) {
+            agreementMap.put(agreement.getAgreementType(), agreement.getAgreementContent());
+        }
+
+        // 转换为JSON串返回
+        String agreementData = JSON.toJSONString(agreementMap);
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("agreementData", agreementData);
+        result.put("appId", appid);
+
+        return AjaxResult.success(result);
+    }
+
 }
 }

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

@@ -1,5 +1,6 @@
 package com.fs.course.controller;
 package com.fs.course.controller;
 
 
+import java.util.*;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.util.*;
 import java.util.*;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
@@ -17,6 +18,7 @@ import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.FsCourseRedPacketLogParam;
 import com.fs.course.param.FsCourseRedPacketLogParam;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.vo.FsCourseRedPacketLogListPVO;
 import com.fs.course.vo.FsCourseRedPacketLogListPVO;
+import com.fs.course.vo.FsCourseRedPacketLogListVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
@@ -235,6 +237,13 @@ public class FsCourseRedPacketLogController extends BaseController
         return R.ok().put("list", optionsVOS);
         return R.ok().put("list", optionsVOS);
     }
     }
 
 
+    @GetMapping("/courseListByCompanyId/{companyId}")
+    public R courseListByCompanyId(@PathVariable("companyId") Long companyId)
+    {
+        List<OptionsVO> optionsVOS = fsUserCourseMapper.selectFsUserCourseByCompany(companyId);
+        return R.ok().put("list", optionsVOS);
+    }
+
 
 
     @GetMapping(value = "/qwvideoList/{id}")
     @GetMapping(value = "/qwvideoList/{id}")
     public R qwvideoList(@PathVariable("id") Long id)
     public R qwvideoList(@PathVariable("id") Long id)
@@ -286,4 +295,37 @@ public class FsCourseRedPacketLogController extends BaseController
         return rspData;
         return rspData;
     }
     }
 
 
+
+
+    /**
+    * 红包消耗统计
+    */
+    @PreAuthorize("@ss.hasPermi('course:courseRedPacketLog:countList')")
+    @PostMapping("/getRedPacketLogCount")
+    public R getRedPacketLogCount(@RequestBody FsCourseRedPacketLogParam fsCourseRedPacketLog){
+
+        PageHelper.startPage(fsCourseRedPacketLog.getPageNum(), fsCourseRedPacketLog.getPageSize());
+
+        List<FsCourseRedPacketLogListVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListCountVO(fsCourseRedPacketLog);
+
+
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
+
+    /**
+     * 导出短链课程看课记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseRedPacketLog:countExport')")
+    @Log(title = "红包消耗统计导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/countExport")
+    public AjaxResult countExport(@RequestBody FsCourseRedPacketLogParam fsCourseRedPacketLog)
+    {
+
+        List<FsCourseRedPacketLogListVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListCountVO(fsCourseRedPacketLog);
+
+        ExcelUtil<FsCourseRedPacketLogListVO> util = new ExcelUtil<FsCourseRedPacketLogListVO>(FsCourseRedPacketLogListVO.class);
+        return util.exportExcel(list, "红包消耗统计导出");
+    }
+
 }
 }

+ 7 - 4
fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java

@@ -10,6 +10,7 @@ import java.util.stream.Collectors;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.course.domain.StatisticsTable;
 import com.fs.course.domain.StatisticsTable;
@@ -17,6 +18,8 @@ import com.fs.course.param.FsCourseOverParam;
 import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.param.FsCourseWatchLogParam;
 import com.fs.course.param.FsCourseWatchLogParam;
 import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
 import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.service.IFsUserCoursePeriodDaysService;
+import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.service.IStatisticsTableService;
 import com.fs.course.service.IStatisticsTableService;
 import com.fs.course.vo.FsCoureseWatchLogVO;
 import com.fs.course.vo.FsCoureseWatchLogVO;
 import com.fs.course.vo.FsCourseOverVO;
 import com.fs.course.vo.FsCourseOverVO;
@@ -66,8 +69,8 @@ public class FsCourseWatchLogController extends BaseController
      * 查询短链课程看课记录列表
      * 查询短链课程看课记录列表
      */
      */
     @PreAuthorize("@ss.hasPermi('course:courseWatchLog:list')")
     @PreAuthorize("@ss.hasPermi('course:courseWatchLog:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(FsCourseWatchLogListParam param)
+    @PostMapping("/list")
+    public R list(@RequestBody FsCourseWatchLogListParam param)
     {
     {
         startPage();
         startPage();
         if(CollectionUtil.isNotEmpty(param.getUserIds())){
         if(CollectionUtil.isNotEmpty(param.getUserIds())){
@@ -78,7 +81,7 @@ public class FsCourseWatchLogController extends BaseController
             );
             );
         }
         }
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
-        return getDataTable(list);
+        return R.ok().put("data", new PageInfo<>(list));
     }
     }
 
 
     /**
     /**
@@ -98,7 +101,7 @@ public class FsCourseWatchLogController extends BaseController
     {
     {
         logger.info("会员课程数据汇总 参数: {}",param);
         logger.info("会员课程数据汇总 参数: {}",param);
 
 
-        if(param.getCompanyId() == null){
+        if(param.getCompanyId() == null && (param.getUserIds() == null || param.getUserIds().isEmpty())){
             throw new CustomException("必须选择公司!");
             throw new CustomException("必须选择公司!");
         }
         }
         if (param.getSTime()==null||param.getETime()==null){
         if (param.getSTime()==null||param.getETime()==null){

+ 35 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCategoryController.java

@@ -5,6 +5,7 @@ import java.util.List;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
+import com.fs.course.dto.FsCourseCategoryImportDTO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.domain.FsStoreProductCategory;
 import com.fs.his.domain.FsStoreProductCategory;
 import com.fs.his.vo.FsStoreProductCategoryVO;
 import com.fs.his.vo.FsStoreProductCategoryVO;
@@ -33,6 +34,7 @@ import com.fs.course.domain.FsUserCourseCategory;
 import com.fs.course.service.IFsUserCourseCategoryService;
 import com.fs.course.service.IFsUserCourseCategoryService;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
 
 
 /**
 /**
  * 课堂分类Controller
  * 课堂分类Controller
@@ -174,4 +176,37 @@ public class FsUserCourseCategoryController extends BaseController
         List<OptionsVO> list = fsUserCourseCategoryService.selectCateListByPid(pid);
         List<OptionsVO> list = fsUserCourseCategoryService.selectCateListByPid(pid);
         return R.ok().put("data", list);
         return R.ok().put("data", list);
     }
     }
+
+    // 下载模板
+    @GetMapping("/importTemplate")
+    public AjaxResult importTemplate() {
+        ExcelUtil<FsCourseCategoryImportDTO> util = new ExcelUtil<>(FsCourseCategoryImportDTO.class);
+        return util.importTemplateExcel("课堂分类导入模板");
+    }
+
+    @Log(title = "导入", businessType = BusinessType.IMPORT)
+    @PreAuthorize("@ss.hasPermi('course:userCourseCategory:importData')")
+    @PostMapping("/importData")
+    public AjaxResult importData(MultipartFile file) throws Exception {
+        ExcelUtil<FsCourseCategoryImportDTO> util = new ExcelUtil<>(FsCourseCategoryImportDTO.class);
+        List<FsCourseCategoryImportDTO> list = util.importExcel(file.getInputStream());
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isEmpty(config.getIsBound()) || !config.getIsBound()){
+            userId = null;
+        }
+
+        return AjaxResult.success(fsUserCourseCategoryService.importData(list, userId));
+    }
+
+    @PreAuthorize("@ss.hasPermi('course:userCourseCategory:exportFail')")
+    @Log(title = "课堂分类", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportFail")
+    public AjaxResult exportFail(@RequestBody List<FsCourseCategoryImportDTO> list) {
+        ExcelUtil<FsCourseCategoryImportDTO> util = new ExcelUtil<>(FsCourseCategoryImportDTO.class);
+        return util.exportExcel(list, "课堂分类错误数据");
+    }
 }
 }

+ 8 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseController.java

@@ -346,4 +346,12 @@ public class FsUserCourseController extends BaseController {
         sopTempService.syncTemplate(courseId);
         sopTempService.syncTemplate(courseId);
         return toAjax(1);
         return toAjax(1);
     }
     }
+
+    /**
+     * 课程下拉列表
+     */
+    @GetMapping("/selectCourseOptionsList")
+    public R getCourseList() {
+        return R.ok().put("data",fsUserCourseService.selectCourseOptionsList());
+    }
 }
 }

+ 19 - 1
fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java

@@ -5,8 +5,10 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriodDays;
 import com.fs.course.domain.FsUserCoursePeriodDays;
@@ -21,6 +23,7 @@ import com.fs.course.vo.FsPeriodCountVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import com.fs.course.vo.PeriodRedPacketVO;
 import com.fs.course.vo.PeriodRedPacketVO;
 import com.fs.course.vo.UpdateCourseTimeVo;
 import com.fs.course.vo.UpdateCourseTimeVo;
+import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
@@ -66,7 +69,22 @@ public class FsUserCoursePeriodController extends BaseController {
         List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsUserCoursePeriodList(fsUserCoursePeriod);
         List<FsUserCoursePeriod> list = fsUserCoursePeriodService.selectFsUserCoursePeriodList(fsUserCoursePeriod);
         return getDataTable(list);
         return getDataTable(list);
     }
     }
-
+    /**
+     * @Description: 营期key value 值
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/11/18 14:59
+     */
+    @GetMapping("/listLabel/{companyId}")
+    public TableDataInfo listLabel(@PathVariable("companyId") Long companyId)
+    {
+        FsUserCoursePeriod fsUserCoursePeriod = new FsUserCoursePeriod();
+        fsUserCoursePeriod.setCompanyId(companyId + "");
+        startPage();
+        List<SysDictData> list = fsUserCoursePeriodService.selectFsUserCoursePeriodListLabel(fsUserCoursePeriod);
+        return getDataTable(list);
+    }
     @PreAuthorize("@ss.hasPermi('course:period:list')")
     @PreAuthorize("@ss.hasPermi('course:period:list')")
     @PostMapping("/page")
     @PostMapping("/page")
     @ApiOperation("自定义查询主列表分页")
     @ApiOperation("自定义查询主列表分页")

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

@@ -20,6 +20,7 @@ import com.fs.course.dto.CoursePeriodSyncResultDTO;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
 import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
+import com.fs.course.param.BatchEditCoverParam;
 import com.fs.course.param.BatchRedUpdate;
 import com.fs.course.param.BatchRedUpdate;
 import com.fs.course.param.BatchTitleUpdate;
 import com.fs.course.param.BatchTitleUpdate;
 import com.fs.course.param.BatchVideoSvae;
 import com.fs.course.param.BatchVideoSvae;
@@ -31,12 +32,13 @@ import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.vo.FsUserCourseVideoChooseVO;
 import com.fs.course.vo.FsUserCourseVideoChooseVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
-import com.fs.qw.vo.SortDayVo;
 import com.fs.system.service.ISysConfigService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.time.LocalTime;
 import java.time.LocalTime;
@@ -348,4 +350,36 @@ public class FsUserCourseVideoController extends BaseController
         List<FsUserCourseVideoChooseVO> list = fsUserCourseVideoService.getChooseCourseVideoListByMap(params);
         List<FsUserCourseVideoChooseVO> list = fsUserCourseVideoService.getChooseCourseVideoListByMap(params);
         return R.ok().put("data", new PageInfo<>(list));
         return R.ok().put("data", new PageInfo<>(list));
     }
     }
+
+    @ApiOperation("视频下架")
+    @PreAuthorize("@ss.hasPermi('course:userCourseVideo:batchDown')")
+    @Log(title = "课堂视频", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchDown/{videoIds}")
+    public AjaxResult batchDown(@PathVariable String[] videoIds) {
+        return toAjax(fsUserCourseVideoService.batchDown(videoIds));
+    }
+
+    @ApiOperation("视频上架")
+    @PreAuthorize("@ss.hasPermi('course:userCourseVideo:batchUp')")
+    @Log(title = "课堂视频", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchUp/{videoIds}")
+    public AjaxResult batchUp(@PathVariable String[] videoIds) {
+        return toAjax(fsUserCourseVideoService.batchUp(videoIds));
+    }
+
+    @ApiOperation("批量修改视频封面图")
+    @PreAuthorize("@ss.hasPermi('course:userCourseVideo:batchEditCover')")
+    @Log(title = "课堂视频", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchEditCover")
+    public AjaxResult batchEditCover(@Validated @RequestBody BatchEditCoverParam param) {
+        return toAjax(fsUserCourseVideoService.batchEditCover(param));
+    }
+
+    /**
+     * 获取课程视频选项列表
+     */
+    @GetMapping("/getCourseVideoOptions")
+    public R getCourseVideoOptions(Long courseId) {
+        return R.ok().put("data", fsUserCourseVideoService.selectVideoOptionsByCourseId(courseId));
+    }
 }
 }

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

@@ -20,6 +20,8 @@ import com.fs.course.domain.FsVideoResource;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserVideoService;
 import com.fs.course.service.IFsUserVideoService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.service.IFsUserVideoService;
+import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsVideoResourceService;
 import com.fs.course.service.IFsVideoResourceService;
 import com.fs.course.vo.FsVideoResourceVO;
 import com.fs.course.vo.FsVideoResourceVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
@@ -45,8 +47,6 @@ import java.util.stream.Collectors;
 public class FsVideoResourceController extends BaseController {
 public class FsVideoResourceController extends BaseController {
 
 
     private final IFsVideoResourceService fsVideoResourceService;
     private final IFsVideoResourceService fsVideoResourceService;
-    @Autowired
-    private IFsUserCourseVideoService fsUserCourseVideoService;
 
 
     private final TokenService tokenService;
     private final TokenService tokenService;
 
 
@@ -56,6 +56,8 @@ public class FsVideoResourceController extends BaseController {
 
 
     private final FsVideoResourceBusinessService videoResourceBusinessService;
     private final FsVideoResourceBusinessService videoResourceBusinessService;
     @Autowired
     @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+    @Autowired
     private IFsUserVideoService fsUserVideoService;
     private IFsUserVideoService fsUserVideoService;
 
 
     /**
     /**

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

@@ -22,13 +22,18 @@ import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.service.IQwWatchLogService;
 import com.fs.qw.service.IQwWatchLogService;
 import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
 import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
+import com.fs.sop.domain.QwSop;
+import com.fs.sop.service.IQwSopService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 
 /**
 /**
  * 短链课程看课记录Controller
  * 短链课程看课记录Controller
@@ -48,6 +53,9 @@ public class QwFsCourseWatchLogController extends BaseController
 
 
     @Autowired
     @Autowired
     private IQwWatchLogService qwWatchLogService;
     private IQwWatchLogService qwWatchLogService;
+
+    @Autowired
+    private IQwSopService qwSopService;
     /**
     /**
      * 查询短链课程看课记录列表
      * 查询短链课程看课记录列表
      */
      */
@@ -217,4 +225,19 @@ public class QwFsCourseWatchLogController extends BaseController
         List<FsCourseOverVO> list = fsCourseWatchLogService.selectFsCourseWatchLogOverStatisticsListVO(param);
         List<FsCourseOverVO> list = fsCourseWatchLogService.selectFsCourseWatchLogOverStatisticsListVO(param);
         return getDataTable(list);
         return getDataTable(list);
     }
     }
+
+    /**
+     * 查询档期列表
+     */
+    @GetMapping("/fetchScheduleList")
+    public R fetchScheduleList(String sopName) {
+        List<QwSop> list = qwSopService.fetchScheduleList(sopName);
+        List<Map<String, Object>> result = list.stream().map(sop -> {
+            Map<String, Object> map = new HashMap<>();
+            map.put("qwSopId", sop.getId()); // 重命名id为qwSopId
+            map.put("qwSopName", sop.getName());
+            return map;
+        }).collect(Collectors.toList());
+        return R.ok().put("rows", result);
+    }
 }
 }

+ 18 - 0
fs-admin/src/main/java/com/fs/crm/controller/CrmCustomerController.java

@@ -23,6 +23,8 @@ import com.fs.crm.vo.CrmCustomerListVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
 import com.fs.system.service.ISysRoleService;
 import com.fs.system.service.ISysRoleService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -30,6 +32,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
@@ -324,5 +327,20 @@ public class CrmCustomerController extends BaseController
         return R.ok().put("data",customerList);
         return R.ok().put("data",customerList);
     }
     }
 
 
+    @ApiOperation("获取客户详情")
+    @GetMapping("/getCustomerDetails")
+    @PreAuthorize("@ss.hasPermi('crm:customer:query')")
+    public R getCustomerDetails(
+            HttpServletRequest request,
+            @ApiParam(required = true, name = "customerId", value = "客户ID") @RequestParam(value = "customerId", required = false) Long customerId
+    ){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        CrmCustomer customer=crmCustomerService.selectCrmCustomerById(customerId);
+        Boolean isReceive=false;
+        if(customer.getIsReceive()!=null&&customer.getIsReceive()==1&&customer.getReceiveUserId()!=null&&loginUser.getUser().getUserId().equals(customer.getReceiveUserId())){
+            isReceive=true;
+        }
+        return R.ok().put("customer",customer).put("isReceive",isReceive);
 
 
+    }
 }
 }

+ 133 - 0
fs-admin/src/main/java/com/fs/fastGpt/FastGptChatMsgController.java

@@ -0,0 +1,133 @@
+package com.fs.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptChatMsg;
+import com.fs.fastGpt.param.FastGptChatMsgListCParam;
+import com.fs.fastGpt.service.IFastGptChatMsgLogsService;
+import com.fs.fastGpt.service.IFastGptChatMsgService;
+import com.fs.fastGpt.vo.FastGptChatMsgListCVO;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 聊天记录Controller
+ *
+ * @author fs
+ * @date 2024-10-10
+ */
+@RestController
+@RequestMapping("/fastGpt/fastGptChatMsg")
+public class FastGptChatMsgController extends BaseController
+{
+    @Autowired
+    private IFastGptChatMsgService fastGptChatMsgService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    @Autowired
+    private IFastGptChatMsgLogsService iFastGptChatMsgLogsService;
+
+    /**
+     * 查询聊天记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsg:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FastGptChatMsgListCParam param)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        param.setCompanyId(loginUser.getCompany().getCompanyId());
+
+        List<FastGptChatMsgListCVO> list = fastGptChatMsgService.selectFastGptChatMsgListCVO(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出聊天记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsg:export')")
+    @Log(title = "聊天记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastGptChatMsg fastGptChatMsg)
+    {
+        List<FastGptChatMsg> list = fastGptChatMsgService.selectFastGptChatMsgList(fastGptChatMsg);
+        ExcelUtil<FastGptChatMsg> util = new ExcelUtil<FastGptChatMsg>(FastGptChatMsg.class);
+        return util.exportExcel(list, "聊天记录数据");
+    }
+
+    /**
+     * 获取聊天记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsg:query')")
+    @GetMapping(value = "/{msgId}")
+    public AjaxResult getInfo(@PathVariable("msgId") Long msgId)
+    {
+        return AjaxResult.success(fastGptChatMsgService.selectFastGptChatMsgVOByMsgId(msgId));
+    }
+
+//    /**
+//     * 新增聊天记录
+//     */
+//    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsg:add')")
+//    @Log(title = "聊天记录", businessType = BusinessType.INSERT)
+//    @PostMapping
+//    public AjaxResult add(@RequestBody FastGptChatMsg fastGptChatMsg)
+//    {
+//        return toAjax(fastGptChatMsgService.insertFastGptChatMsg(fastGptChatMsg));
+//    }
+
+//    /**
+//     * 修改聊天记录
+//     */
+//    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsg:edit')")
+//    @Log(title = "聊天记录", businessType = BusinessType.UPDATE)
+//    @PutMapping
+//    public AjaxResult edit(@RequestBody FastGptChatMsg param)
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        FastGptChatMsg fastGptChatMsg = fastGptChatMsgService.selectFastGptChatMsgByMsgId(param.getMsgId());
+//
+//        FastGptChatMsgLogs logs = new FastGptChatMsgLogs();
+//
+//        logs.setMsgId(param.getMsgId());
+//        logs.setLogsType(2);
+//        if (param.getContent()!=null&&!param.getContent().equals("")){
+//            logs.setContent(param.getContent());
+//        }
+//        if (param.getStatus()!=null){
+//            logs.setLogsType(1);
+//        }
+//        String userContent = fastGptChatMsgService.selectUserContent(fastGptChatMsg.getMsgId(), fastGptChatMsg.getUserId(),fastGptChatMsg.getRoleId());
+//        logs.setSContent(fastGptChatMsg.getContent());
+//        logs.setUserContent(userContent);
+//        logs.setCompanyUserId(loginUser.getUser().getUserId());
+//        logs.setCreateBy(loginUser.getUser().getNickName());
+//        logs.setCompanyId(loginUser.getCompany().getCompanyId());
+//
+//        iFastGptChatMsgLogsService.insertFastGptChatMsgLogs(logs);
+//
+//        return toAjax(fastGptChatMsgService.updateFastGptChatMsg(param));
+//    }
+
+//    /**
+//     * 删除聊天记录
+//     */
+//    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsg:remove')")
+//    @Log(title = "聊天记录", businessType = BusinessType.DELETE)
+//	@DeleteMapping("/{msgIds}")
+//    public AjaxResult remove(@PathVariable Long[] msgIds)
+//    {
+//        return toAjax(fastGptChatMsgService.deleteFastGptChatMsgByMsgIds(msgIds));
+//    }
+}

+ 116 - 0
fs-admin/src/main/java/com/fs/fastGpt/FastGptChatMsgLogsController.java

@@ -0,0 +1,116 @@
+package com.fs.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptChatMsgLogs;
+import com.fs.fastGpt.param.FastGptChatMsgLogsListParam;
+import com.fs.fastGpt.service.IFastGptChatMsgLogsService;
+import com.fs.fastGpt.vo.FastGptChatMsgLogsListCVO;
+import com.fs.fastGpt.vo.FastGptChatMsgLogsVO;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 聊天记录日志Controller
+ *
+ * @author fs
+ * @date 2024-10-10
+ */
+@RestController
+@RequestMapping("/fastGpt/fastGptChatMsgLogs")
+public class FastGptChatMsgLogsController extends BaseController
+{
+    @Autowired
+    private IFastGptChatMsgLogsService fastGptChatMsgLogsService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    /**
+     * 查询聊天记录日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsgLogs:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FastGptChatMsgLogs fastGptChatMsgLogs)
+    {
+        startPage();
+        List<FastGptChatMsgLogsVO> list = fastGptChatMsgLogsService.selectFastGptChatMsgLogsListVO(fastGptChatMsgLogs);
+        return getDataTable(list);
+    }
+
+//    @GetMapping("/logsList")
+//    public R list(FastGptChatMsgLogsListParam param)
+//    {
+//        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        param.setCompanyId(loginUser.getCompany().getCompanyId());
+//
+//        List<FastGptChatMsgLogsListCVO> list= fastGptChatMsgLogsService.selectFastGptChatMsgLogsListCVO(param);
+//        PageInfo<FastGptChatMsgLogsListCVO> listPageInfo=new PageInfo<>(list);
+//        return R.ok().put("data",listPageInfo);
+//    }
+    /**
+     * 导出聊天记录日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsgLogs:export')")
+    @Log(title = "聊天记录日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastGptChatMsgLogsListParam param)
+    {
+        List<FastGptChatMsgLogsListCVO> list = fastGptChatMsgLogsService.selectFastGptChatMsgLogsListCVO(param);
+        ExcelUtil<FastGptChatMsgLogsListCVO> util = new ExcelUtil<FastGptChatMsgLogsListCVO>(FastGptChatMsgLogsListCVO.class);
+        return util.exportExcel(list, "聊天记录日志数据");
+    }
+
+    /**
+     * 获取聊天记录日志详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsgLogs:query')")
+    @GetMapping(value = "/{logsId}")
+    public AjaxResult getInfo(@PathVariable("logsId") Long logsId)
+    {
+        return AjaxResult.success(fastGptChatMsgLogsService.selectFastGptChatMsgLogsByLogsId(logsId));
+    }
+
+    /**
+     * 新增聊天记录日志
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsgLogs:add')")
+    @Log(title = "聊天记录日志", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastGptChatMsgLogs fastGptChatMsgLogs)
+    {
+        return toAjax(fastGptChatMsgLogsService.insertFastGptChatMsgLogs(fastGptChatMsgLogs));
+    }
+
+    /**
+     * 修改聊天记录日志
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsgLogs:edit')")
+    @Log(title = "聊天记录日志", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastGptChatMsgLogs fastGptChatMsgLogs)
+    {
+        return toAjax(fastGptChatMsgLogsService.updateFastGptChatMsgLogs(fastGptChatMsgLogs));
+    }
+
+    /**
+     * 删除聊天记录日志
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatMsgLogs:remove')")
+    @Log(title = "聊天记录日志", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{logsIds}")
+    public AjaxResult remove(@PathVariable Long[] logsIds)
+    {
+        return toAjax(fastGptChatMsgLogsService.deleteFastGptChatMsgLogsByLogsIds(logsIds));
+    }
+}

+ 121 - 0
fs-admin/src/main/java/com/fs/fastGpt/FastGptChatSessionController.java

@@ -0,0 +1,121 @@
+package com.fs.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptChatSession;
+import com.fs.fastGpt.param.FastGptChatSessionParam;
+import com.fs.fastGpt.service.IFastGptChatMsgService;
+import com.fs.fastGpt.service.IFastGptChatSessionService;
+import com.fs.fastGpt.vo.FastGptChatMsgCVO;
+import com.fs.fastGpt.vo.FastGptChatSessionCVO;
+import com.fs.fastGpt.vo.FastGptChatSessionListCVO;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 对话关系Controller
+ *
+ * @author fs
+ * @date 2024-10-10
+ */
+@RestController
+@RequestMapping("/fastGpt/fastGptChatSession")
+public class FastGptChatSessionController extends BaseController
+{
+    @Autowired
+    private IFastGptChatSessionService fastGptChatSessionService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    @Autowired
+    private IFastGptChatMsgService fastGptChatMsgService;
+
+
+    /**
+     * 查询对话关系列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatSession:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FastGptChatSessionParam param)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<FastGptChatSessionListCVO> list = fastGptChatSessionService.selectFastGptChatSessionListCVO(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出对话关系列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatSession:export')")
+    @Log(title = "对话关系", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastGptChatSession fastGptChatSession)
+    {
+        List<FastGptChatSession> list = fastGptChatSessionService.selectFastGptChatSessionList(fastGptChatSession);
+        ExcelUtil<FastGptChatSession> util = new ExcelUtil<FastGptChatSession>(FastGptChatSession.class);
+        return util.exportExcel(list, "对话关系数据");
+    }
+
+    /**
+     * 获取对话关系详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatSession:query')")
+    @GetMapping(value = "/{sessionId}")
+    public R getInfo(@PathVariable("sessionId") Long sessionId)
+    {
+        FastGptChatSessionCVO sessionCVO = fastGptChatSessionService.selectFastGptChatSessionCVOBySessionId(sessionId);
+
+        List<FastGptChatMsgCVO> list = fastGptChatMsgService.selectFastGptChatMsgCVOBySessionId(sessionId,sessionCVO.getUserId());
+
+        return R.ok().put("data",sessionCVO).put("list",list);
+    }
+
+    /**
+     * 新增对话关系
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatSession:add')")
+    @Log(title = "对话关系", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastGptChatSession fastGptChatSession)
+    {
+        return toAjax(fastGptChatSessionService.insertFastGptChatSession(fastGptChatSession));
+    }
+
+    /**
+     * 修改对话关系
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatSession:edit')")
+    @Log(title = "对话关系", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastGptChatSession fastGptChatSession)
+    {
+        FastGptChatSession session = new FastGptChatSession();
+        session.setSessionId(fastGptChatSession.getSessionId());
+        session.setIsArtificial(fastGptChatSession.getIsArtificial());
+        return toAjax(fastGptChatSessionService.updateFastGptChatSession(session));
+    }
+
+    /**
+     * 删除对话关系
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatSession:remove')")
+    @Log(title = "对话关系", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{sessionIds}")
+    public AjaxResult remove(@PathVariable Long[] sessionIds)
+    {
+        return toAjax(fastGptChatSessionService.deleteFastGptChatSessionBySessionIds(sessionIds));
+    }
+}

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

@@ -89,8 +89,8 @@ public class FastgptEventLogTotalController extends BaseController
     @GetMapping("/export")
     @GetMapping("/export")
     public AjaxResult export(FastgptEventLogTotal fastgptEventLogTotal)
     public AjaxResult export(FastgptEventLogTotal fastgptEventLogTotal)
     {
     {
-        List<FastgptEventLogTotal> list = fastgptEventLogTotalService.selectFastgptEventLogTotalList(fastgptEventLogTotal);
-        ExcelUtil<FastgptEventLogTotal> util = new ExcelUtil<FastgptEventLogTotal>(FastgptEventLogTotal.class);
+        List<FastgptEventLogTotalVo> list = fastgptEventLogTotalService.selectFastgptEventLogTotalExport(fastgptEventLogTotal);
+        ExcelUtil<FastgptEventLogTotalVo> util = new ExcelUtil<FastgptEventLogTotalVo>(FastgptEventLogTotalVo.class);
         return util.exportExcel(list, "ai事件埋点统计数据");
         return util.exportExcel(list, "ai事件埋点统计数据");
     }
     }
 
 

+ 20 - 0
fs-admin/src/main/java/com/fs/fastGpt/GptRoleController.java

@@ -6,6 +6,7 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.service.IFastGptRoleService;
 import com.fs.fastGpt.service.IFastGptRoleService;
@@ -49,6 +50,25 @@ public class GptRoleController extends BaseController
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
+    /**
+     * 查询应用列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptRole:newList')")
+    @GetMapping("/newList")
+    public TableDataInfo newList(FastGptRole fastGptRole)
+    {
+        startPage();
+        fastGptRole.setCompanyId(fastGptRole.getCompanyId());
+        List<FastGptRoleVO> list = fastGptRoleService.selectFastGptRoleListVONew(fastGptRole);
+        for (FastGptRoleVO fastGptRoleVO : list) {
+            String reminderWords = fastGptRoleVO.getReminderWords();
+            if (reminderWords!=null && reminderWords.length()>110) {
+                fastGptRoleVO.setReminderWords(reminderWords.substring(0,110)+"...");
+            }
+        }
+        return getDataTable(list);
+    }
+
     /**
     /**
      * 导出应用列表
      * 导出应用列表
      */
      */

+ 163 - 0
fs-admin/src/main/java/com/fs/his/controller/FsAiWorkflowController.java

@@ -0,0 +1,163 @@
+package com.fs.his.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.his.domain.FsAiWorkflow;
+import com.fs.his.domain.FsAiWorkflowNodeType;
+import com.fs.his.param.FsAiWorkflowSaveParam;
+import com.fs.his.param.FsAiWorkflowUpdateBindWCParam;
+import com.fs.his.service.IFsAiWorkflowService;
+import com.fs.his.vo.FsAiWorkflowVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI工作流Controller
+ *
+ * @author fs
+ * @date 2026-01-06
+ */
+@RestController
+@RequestMapping("/his/aiWorkflow")
+public class FsAiWorkflowController extends BaseController {
+
+    @Autowired
+    private IFsAiWorkflowService fsAiWorkflowService;
+
+    /**
+     * 查询AI工作流列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(FsAiWorkflow fsAiWorkflow) {
+        startPage();
+        List<FsAiWorkflow> list = fsAiWorkflowService.selectFsAiWorkflowList(fsAiWorkflow);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出AI工作流列表
+     */
+    @Log(title = "AI工作流", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsAiWorkflow fsAiWorkflow) {
+        List<FsAiWorkflow> list = fsAiWorkflowService.selectFsAiWorkflowList(fsAiWorkflow);
+        ExcelUtil<FsAiWorkflow> util = new ExcelUtil<FsAiWorkflow>(FsAiWorkflow.class);
+        return util.exportExcel(list, "AI工作流数据");
+    }
+
+    /**
+     * 获取AI工作流详细信息(包含节点和连线)
+     */
+    @GetMapping(value = "/{workflowId}")
+    public AjaxResult getInfo(@PathVariable("workflowId") Long workflowId) {
+        return AjaxResult.success(fsAiWorkflowService.selectFsAiWorkflowById(workflowId));
+    }
+
+    /**
+     * 保存AI工作流(新增或更新)
+     */
+    @Log(title = "AI工作流", businessType = BusinessType.INSERT)
+    @PostMapping("/save")
+    public AjaxResult save(@RequestBody FsAiWorkflowSaveParam param) {
+        Long workflowId = fsAiWorkflowService.saveFsAiWorkflow(param);
+        return AjaxResult.success(workflowId);
+    }
+
+    /**
+     * 修改AI工作流状态
+     */
+    @Log(title = "AI工作流", businessType = BusinessType.UPDATE)
+    @PutMapping("/status/{workflowId}/{status}")
+    public AjaxResult updateStatus(@PathVariable("workflowId") Long workflowId,
+                                   @PathVariable("status") Integer status) {
+        return toAjax(fsAiWorkflowService.updateFsAiWorkflowStatus(workflowId, status));
+    }
+
+    /**
+     * 删除AI工作流
+     */
+    @Log(title = "AI工作流", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{workflowIds}")
+    public AjaxResult remove(@PathVariable Long[] workflowIds) {
+        return toAjax(fsAiWorkflowService.deleteFsAiWorkflowByIds(workflowIds));
+    }
+
+    /**
+     * 复制AI工作流
+     */
+    @Log(title = "AI工作流", businessType = BusinessType.INSERT)
+    @PostMapping("/copy/{workflowId}")
+    public AjaxResult copy(@PathVariable("workflowId") Long workflowId) {
+        Long newWorkflowId = fsAiWorkflowService.copyFsAiWorkflow(workflowId);
+        if (newWorkflowId != null) {
+            return AjaxResult.success(newWorkflowId);
+        }
+        return AjaxResult.error("复制失败,工作流不存在");
+    }
+
+    /**
+     * 获取所有启用的节点类型
+     */
+    @GetMapping("/nodeTypes")
+    public AjaxResult getNodeTypes() {
+        List<FsAiWorkflowNodeType> list = fsAiWorkflowService.selectAllEnabledNodeTypes();
+        return AjaxResult.success(list);
+    }
+    /**
+     * 导出工作流流程图JSON
+     * 包含节点信息、连接顺序、节点类型
+     */
+//    @Log(title = "AI工作流", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportJson/{workflowId}")
+    public AjaxResult exportJson(@PathVariable("workflowId") Long workflowId) {
+        return AjaxResult.success(fsAiWorkflowService.exportWorkflowJson(workflowId));
+    }
+    /**
+     * 分页销售
+     */
+    @GetMapping("/listCompanyUser")
+    public AjaxResult listCompanyUser() {
+        return AjaxResult.success(fsAiWorkflowService.listCompanyUser());
+    }
+
+    /**
+     * 查销售
+     */
+    @GetMapping("/getCompanyUserById/{companyUserId}")
+    public AjaxResult getCompanyUserById(@PathVariable("companyUserId") Long companyUserId) {
+        return AjaxResult.success(fsAiWorkflowService.getCompanyUserById(companyUserId));
+    }
+
+//    /**
+//     * 查销售是否已经被绑定
+//     */
+//    @GetMapping("/checkCompanyUserBeUsed/{companyUserId}")
+//    public AjaxResult checkCompanyUserBeUsed(@PathVariable("companyUserId") Long companyUserId) {
+//        return AjaxResult.success(fsAiWorkflowService.checkCompanyUserBeUsed(companyUserId));
+//    }
+
+    /**
+     * 查工作流已绑定的销售
+     */
+    @GetMapping("/getBindCompanyUserByWorkflowId/{workflowId}")
+    public AjaxResult getBindCompanyUserByWorkflowId(@PathVariable("workflowId") Long workflowId) {
+        return AjaxResult.success(fsAiWorkflowService.getBindCompanyUserByWorkflowId(workflowId));
+    }
+
+    /**
+     * 修改工作流绑定的销售
+     */
+    @PostMapping("/updateWorkflowBindCompanyUser")
+    public AjaxResult updateWorkflowBindCompanyUser(@RequestBody FsAiWorkflowUpdateBindWCParam param) {
+        return fsAiWorkflowService.updateWorkflowBindCompanyUser(param);
+    }
+
+}

+ 94 - 4
fs-admin/src/main/java/com/fs/his/controller/FsIntegralOrderController.java

@@ -3,6 +3,7 @@ package com.fs.his.controller;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
@@ -143,7 +144,7 @@ public class FsIntegralOrderController extends BaseController
     }
     }
 
 
     /**
     /**
-     * 导出积分商品订单列表
+     * 导出积分商品订单列表 卓美个性化导出
      */
      */
     @PreAuthorize("@ss.hasPermi('his:integralOrder:export')")
     @PreAuthorize("@ss.hasPermi('his:integralOrder:export')")
     @Log(title = "积分商品订单", businessType = BusinessType.EXPORT)
     @Log(title = "积分商品订单", businessType = BusinessType.EXPORT)
@@ -162,6 +163,89 @@ public class FsIntegralOrderController extends BaseController
         }
         }
         SysRole sysRole = isCheckPermission();
         SysRole sysRole = isCheckPermission();
         // 处理商品名称:解析item_json并设置goodsName
         // 处理商品名称:解析item_json并设置goodsName
+        List<FsIntegralOrderListVO> newFsIntegralOrderListVOS=new ArrayList<>();
+        for (FsIntegralOrderListVO vo : fsIntegralOrderListVOS) {
+            if (!(sysRole.getIsCheckPhone()==1)){
+                // 加密手机号
+                vo.setUserPhone(decryptAutoPhoneMk(vo.getUserPhone()));
+            }
+            if (StringUtils.isNotEmpty(vo.getItemJson())) {
+                try {
+                    // 尝试解析JSON格式的商品信息
+                    if (vo.getItemJson().startsWith("[")) {
+                        // 数组格式,遍历所有商品,用换行符分隔
+                        com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(vo.getItemJson());
+                        if (jsonArray != null && !jsonArray.isEmpty()) {
+                            for (int i = 0; i < jsonArray.size(); i++) {
+                                FsIntegralOrderListVO newVo = BeanCopyUtils.copy(vo,FsIntegralOrderListVO.class);
+                                if (newVo==null){
+                                    continue;
+                                }
+                                com.alibaba.fastjson.JSONObject goods = jsonArray.getJSONObject(i);
+
+                                // 处理商品名称和数量
+                                if (goods != null && goods.getString("goodsName") != null) {
+                                    String name = goods.getString("goodsName");
+                                    String num = goods.getString("num");
+                                    newVo.setNum(num);
+                                    newVo.setGoodsName(name);
+                                }
+
+                                // 处理商品编码
+                                if (goods != null && goods.getString("barCode") != null) {
+                                    String barCode = goods.getString("barCode");
+                                    newVo.setBarCode(barCode);
+                                }
+                                newFsIntegralOrderListVOS.add(newVo);
+                            }
+                        }else{
+                            newFsIntegralOrderListVOS.add(vo);
+                        }
+                    } else if (vo.getItemJson().startsWith("{")) {
+                        // 对象格式
+                        com.alibaba.fastjson.JSONObject goods = com.alibaba.fastjson.JSONObject.parseObject(vo.getItemJson());
+
+                        // 处理商品名称和数量
+                        if (goods != null && goods.getString("goodsName") != null) {
+                            String name = goods.getString("goodsName");
+                            String num = goods.getString("num");
+                            vo.setNum(num);
+                            vo.setGoodsName(name);
+                        }
+
+                        // 处理商品编码
+                        if (goods != null && goods.getString("barCode") != null) {
+                            vo.setBarCode(goods.getString("barCode"));
+                        }
+                        newFsIntegralOrderListVOS.add(vo);
+                    }
+                } catch (Exception e) {
+                    // 解析失败时保持goodsName为空,避免导出出错
+                    log.warn("解析商品信息失败,订单编号:{}, 商品信息:{}", vo.getOrderCode(), vo.getItemJson());
+                }
+            }else {
+                newFsIntegralOrderListVOS.add(vo);
+            }
+
+        }
+        ExcelUtil<FsIntegralOrderListVO> util = new ExcelUtil<>(FsIntegralOrderListVO.class);
+        return util.exportExcel(new ArrayList<>(newFsIntegralOrderListVOS), "积分商品订单数据");
+    }
+
+    /*public AjaxResult export(FsIntegralOrderParam fsIntegralOrder) {
+        List<FsIntegralOrderListVO> fsIntegralOrderListVOS = new ArrayList<>();
+        if (CloudHostUtils.hasCloudHostName("金牛明医")){
+            *//*目前只有金牛有状态为6的查询,其他项目避免使用6状态码*//*
+            if (fsIntegralOrder.getStatus() != null && fsIntegralOrder.getStatus().equals("6")) {
+                fsIntegralOrder.setStatus("1");
+                fsIntegralOrder.setIsPush(0);
+            }
+            fsIntegralOrderListVOS = fsIntegralOrderService.selectFsIntegralOrderListByJn(fsIntegralOrder);
+        } else {
+            fsIntegralOrderListVOS = fsIntegralOrderService.selectFsIntegralOrderListVO(fsIntegralOrder);
+        }
+        SysRole sysRole = isCheckPermission();
+        // 处理商品名称:解析item_json并设置goodsName
         for (FsIntegralOrderListVO vo : fsIntegralOrderListVOS) {
         for (FsIntegralOrderListVO vo : fsIntegralOrderListVOS) {
             if (StringUtils.isNotEmpty(vo.getItemJson())) {
             if (StringUtils.isNotEmpty(vo.getItemJson())) {
                 try {
                 try {
@@ -174,6 +258,9 @@ public class FsIntegralOrderController extends BaseController
                             if (goods != null && goods.getString("goodsName") != null) {
                             if (goods != null && goods.getString("goodsName") != null) {
                                 vo.setGoodsName(goods.getString("goodsName"));
                                 vo.setGoodsName(goods.getString("goodsName"));
                             }
                             }
+                            if (goods != null && goods.getString("barCode") != null) {
+                                vo.setBarCode(goods.getString("barCode"));
+                            }
                         }
                         }
                     } else if (vo.getItemJson().startsWith("{")) {
                     } else if (vo.getItemJson().startsWith("{")) {
                         // 对象格式
                         // 对象格式
@@ -181,6 +268,9 @@ public class FsIntegralOrderController extends BaseController
                         if (goods != null && goods.getString("goodsName") != null) {
                         if (goods != null && goods.getString("goodsName") != null) {
                             vo.setGoodsName(goods.getString("goodsName"));
                             vo.setGoodsName(goods.getString("goodsName"));
                         }
                         }
+                        if (goods != null && goods.getString("barCode") != null) {
+                            vo.setGoodsName(goods.getString("barCode"));
+                        }
                     }
                     }
                 } catch (Exception e) {
                 } catch (Exception e) {
                     // 解析失败时保持goodsName为空,避免导出出错
                     // 解析失败时保持goodsName为空,避免导出出错
@@ -195,7 +285,7 @@ public class FsIntegralOrderController extends BaseController
         }
         }
         ExcelUtil<FsIntegralOrderListVO> util = new ExcelUtil<>(FsIntegralOrderListVO.class);
         ExcelUtil<FsIntegralOrderListVO> util = new ExcelUtil<>(FsIntegralOrderListVO.class);
         return util.exportExcel(new ArrayList<>(fsIntegralOrderListVOS), "积分商品订单数据");
         return util.exportExcel(new ArrayList<>(fsIntegralOrderListVOS), "积分商品订单数据");
-    }
+    }*/
     /**
     /**
      * 发货
      * 发货
      */
      */
@@ -464,8 +554,8 @@ public class FsIntegralOrderController extends BaseController
     {
     {
         ExcelUtil<FsIntegralOrderExcelVO> util = new ExcelUtil<>(FsIntegralOrderExcelVO.class);
         ExcelUtil<FsIntegralOrderExcelVO> util = new ExcelUtil<>(FsIntegralOrderExcelVO.class);
         List<FsIntegralOrderExcelVO> list = util.importExcel(file.getInputStream());
         List<FsIntegralOrderExcelVO> list = util.importExcel(file.getInputStream());
-        String message = fsIntegralOrderService.importOrderStatusData(list);
-        return AjaxResult.success(message);
+        FsIntegralOrderImportResultVO result = fsIntegralOrderService.importOrderStatusData(list);
+        return AjaxResult.success(result);
     }
     }
 
 
     @GetMapping("/importUpdateOrderTemplate")
     @GetMapping("/importUpdateOrderTemplate")

+ 4 - 1
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -42,6 +42,7 @@ import com.fs.his.param.FsStoreOrderSetErpPhoneParam;
 import com.fs.his.service.*;
 import com.fs.his.service.*;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.*;
 import com.fs.his.vo.*;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysRoleService;
 import com.fs.system.service.ISysRoleService;
@@ -82,6 +83,8 @@ public class FsStoreOrderController extends BaseController
     @Autowired
     @Autowired
     private IFsStoreOrderService fsStoreOrderService;
     private IFsStoreOrderService fsStoreOrderService;
     @Autowired
     @Autowired
+    private IFsStoreOrderScrmService fsStoreOrderScrmService;
+    @Autowired
     private IFsExpressService expressService;
     private IFsExpressService expressService;
     @Autowired
     @Autowired
     private ICompanyMoneyLogsService moneyLogsService;
     private ICompanyMoneyLogsService moneyLogsService;
@@ -458,7 +461,7 @@ public class FsStoreOrderController extends BaseController
     @GetMapping("/payment/{orderId}")
     @GetMapping("/payment/{orderId}")
     public AjaxResult getPayInfo(@PathVariable("orderId") Long orderId)
     public AjaxResult getPayInfo(@PathVariable("orderId") Long orderId)
     {
     {
-        return AjaxResult.success(fsStoreOrderService.selectFsStorePaymentByOrderId(orderId));
+        return AjaxResult.success(fsStoreOrderScrmService.selectFsStorePaymentByOrderId(orderId));
     }
     }
     /**
     /**
      * 获取处方详细信息
      * 获取处方详细信息

+ 40 - 4
fs-admin/src/main/java/com/fs/his/controller/FsStorePaymentController.java

@@ -5,10 +5,10 @@ import java.util.List;
 
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
-import com.fs.his.domain.FsExportTask;
+import com.fs.his.domain.*;
 import com.fs.his.mapper.FsPrescribeMapper;
 import com.fs.his.mapper.FsPrescribeMapper;
 import com.fs.his.param.FsStorePaymentParam;
 import com.fs.his.param.FsStorePaymentParam;
-import com.fs.his.service.IFsExportTaskService;
+import com.fs.his.service.*;
 import com.fs.his.vo.FsStorePaymentExcelVO;
 import com.fs.his.vo.FsStorePaymentExcelVO;
 import com.fs.his.vo.FsStorePaymentVO;
 import com.fs.his.vo.FsStorePaymentVO;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
@@ -26,8 +26,6 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
-import com.fs.his.domain.FsStorePayment;
-import com.fs.his.service.IFsStorePaymentService;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 
 
@@ -44,11 +42,17 @@ public class FsStorePaymentController extends BaseController
 {
 {
     @Autowired
     @Autowired
     private IFsStorePaymentService fsStorePaymentService;
     private IFsStorePaymentService fsStorePaymentService;
+
+    @Autowired
+    private IFsStorePaymentErrorService fsStorePaymentErrorService;
     @Autowired
     @Autowired
     FsPrescribeMapper fsPrescribeMapper;
     FsPrescribeMapper fsPrescribeMapper;
 
 
     @Autowired
     @Autowired
     private IFsExportTaskService exportTaskService;
     private IFsExportTaskService exportTaskService;
+    @Autowired
+    private IFsPackageOrderService fsPackageOrderService;
+
     /**
     /**
      * 查询支付明细列表
      * 查询支付明细列表
      */
      */
@@ -129,6 +133,21 @@ public class FsStorePaymentController extends BaseController
 
 
         return AjaxResult.success( fsStorePaymentService.updateFsStorePaymentByDecryptForm(paymentId));
         return AjaxResult.success( fsStorePaymentService.updateFsStorePaymentByDecryptForm(paymentId));
     }
     }
+
+    /**
+     * 批量互医同步支付明细(临时)
+     */
+    @PostMapping(value = "/batchUpdate")
+    public AjaxResult batchUpdate(@RequestBody List<Long> paymentIds)
+    {
+        if(!paymentIds.isEmpty()){
+            for (Long paymentId : paymentIds) {
+                fsStorePaymentService.updateFsStorePaymentByDecryptForm(paymentId);
+            }
+        }
+        return AjaxResult.success("成功");
+    }
+
     @PreAuthorize("@ss.hasPermi('his:storePayment:refund')")
     @PreAuthorize("@ss.hasPermi('his:storePayment:refund')")
     @GetMapping(value = "refund/{paymentId}")
     @GetMapping(value = "refund/{paymentId}")
     public R refund(@PathVariable("paymentId") Long paymentId)
     public R refund(@PathVariable("paymentId") Long paymentId)
@@ -169,4 +188,21 @@ public class FsStorePaymentController extends BaseController
     {
     {
         return toAjax(fsStorePaymentService.deleteFsStorePaymentByPaymentIds(paymentIds));
         return toAjax(fsStorePaymentService.deleteFsStorePaymentByPaymentIds(paymentIds));
     }
     }
+
+    /**
+     * 查询支付错误明细
+     */
+    @GetMapping("/error/list")
+    public TableDataInfo list(FsStorePaymentError fsStorePaymentError)
+    {
+        startPage();
+        List<FsStorePaymentError> list = fsStorePaymentErrorService.selectFsStorePaymentErrorList(fsStorePaymentError);
+        for (FsStorePaymentError vo : list){
+            if (vo.getBusinessType() != null && vo.getBusinessType()==3 &&  vo.getOrderId() != null) {
+                FsPackageOrder fsPackageOrder = fsPackageOrderService.selectFsPackageOrderByOrderId(vo.getOrderId());
+                vo.setOrderNo(fsPackageOrder.getOrderSn());
+            }
+        }
+        return getDataTable(list);
+    }
 }
 }

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

@@ -4,6 +4,9 @@ import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.core.domain.entity.SysRole;
+import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.his.dto.AddressInfoDTO;
 import com.fs.his.dto.AddressInfoDTO;

+ 13 - 8
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -10,6 +10,7 @@ import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
@@ -253,7 +254,9 @@ public class FsUserController extends BaseController
     public AjaxResult getInfo(@PathVariable("userId") Long userId)
     public AjaxResult getInfo(@PathVariable("userId") Long userId)
     {
     {
         FsUser fsUser = fsUserService.selectFsUserByUserId(userId);
         FsUser fsUser = fsUserService.selectFsUserByUserId(userId);
-        fsUser.setPhone(decryptAutoPhoneMk(fsUser.getPhone()));
+        if(!CloudHostUtils.hasCloudHostName("广州郑多燕")) {
+            fsUser.setPhone(decryptAutoPhoneMk(fsUser.getPhone()));
+        }
         return AjaxResult.success(fsUser);
         return AjaxResult.success(fsUser);
     }
     }
 
 
@@ -262,16 +265,17 @@ public class FsUserController extends BaseController
     {
     {
 
 
         List<FsUserAddress> fsUserAddresses = fsUserService.selectFsUserAddressByUserId(userId);
         List<FsUserAddress> fsUserAddresses = fsUserService.selectFsUserAddressByUserId(userId);
-        for (FsUserAddress fsUserAddress : fsUserAddresses) {
-            if (fsUserAddress.getPhone()!=null&&fsUserAddress.getPhone()!=""){
-                if (fsUserAddress.getPhone().length()>11){
-                    fsUserAddress.setPhone(decryptPhoneMk(fsUserAddress.getPhone()));
-                }else {
-                    fsUserAddress.setPhone(fsUserAddress.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+        if(!CloudHostUtils.hasCloudHostName("广州郑多燕")) {
+            for (FsUserAddress fsUserAddress : fsUserAddresses) {
+                if (fsUserAddress.getPhone() != null && fsUserAddress.getPhone() != "") {
+                    if (fsUserAddress.getPhone().length() > 11) {
+                        fsUserAddress.setPhone(decryptPhoneMk(fsUserAddress.getPhone()));
+                    } else {
+                        fsUserAddress.setPhone(fsUserAddress.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                    }
                 }
                 }
             }
             }
         }
         }
-
         return AjaxResult.success(fsUserAddresses);
         return AjaxResult.success(fsUserAddresses);
     }
     }
 
 
@@ -338,6 +342,7 @@ public class FsUserController extends BaseController
     public R listBySearch(FsUser user)
     public R listBySearch(FsUser user)
     {
     {
         if (Objects.nonNull(user) && StringUtils.isNotBlank(user.getPhone())){
         if (Objects.nonNull(user) && StringUtils.isNotBlank(user.getPhone())){
+            user.setPhoneUnencrypted(user.getPhone());
             user.setPhone(PhoneUtil.encryptPhone(user.getPhone()));
             user.setPhone(PhoneUtil.encryptPhone(user.getPhone()));
         }
         }
         List<FsUser> list = fsUserService.selectFsUserList(user);
         List<FsUser> list = fsUserService.selectFsUserList(user);

+ 51 - 8
fs-admin/src/main/java/com/fs/his/task/CompanyBalanceTask.java

@@ -1,13 +1,15 @@
 package com.fs.his.task;
 package com.fs.his.task;
 
 
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyService;
-import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.service.BalanceRollbackErrorService;
 import com.fs.course.service.BalanceRollbackErrorService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
-import java.util.List;
+import java.util.Date;
 
 
 /**
 /**
  * @description: 公司余额同步定时任务 (红包余额)
  * @description: 公司余额同步定时任务 (红包余额)
@@ -16,6 +18,7 @@ import java.util.List;
  * @version: 1.0
  * @version: 1.0
  */
  */
 @Component("companyBalanceTask")
 @Component("companyBalanceTask")
+@Slf4j
 public class CompanyBalanceTask {
 public class CompanyBalanceTask {
 
 
 
 
@@ -58,19 +61,30 @@ public class CompanyBalanceTask {
      */
      */
     public void initCompanyBalance() {
     public void initCompanyBalance() {
         balanceRollbackErrorService.initCompanyBalance();
         balanceRollbackErrorService.initCompanyBalance();
-
     }
     }
 
 
     /**
     /**
-     * @Description: 红包余额回滚(回滚的是客户没领取的红包),红包记录表中,两天没领取的记录不会再发送
+     * @Description: 优化成回滚前查询记录,一笔一笔回滚
      * @Param: 每天0点执行一次
      * @Param: 每天0点执行一次
      * @Return:
      * @Return:
      * @Author xgb
      * @Author xgb
      * @Date 2025/11/7 9:48
      * @Date 2025/11/7 9:48
      */
      */
-    public void rollbackRedPacketMoney() throws Exception {
-        // 这个地方真加的是company money字段 xgb 红包余额独立后这个方法弃用
-        companyService.rollbackRedPacketMoney();
+    public void rollbackRedPacketMoney(String time) throws Exception {
+        // 默认是前两天时间
+        String createSTime;
+        String createETime;
+        if (StringUtils.isNotBlank(time)) {
+            Date date = DateUtils.parseDate(time);
+            createSTime = time+" 00:00:00";
+            createETime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(date, 1))+" 00:00:00";
+        } else {
+            createSTime = DateUtils.parseDateToStr( DateUtils.YYYY_MM_DD,DateUtils.addDays(new Date(), -2))+" 00:00:00";
+            createETime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(new Date(), -1))+" 00:00:00";
+        }
+
+        // 这个地方真加的是company money字段 xgb
+        companyService.rollbackRedPacketMoney(createSTime, createETime);
     }
     }
 
 
     /**
     /**
@@ -86,6 +100,35 @@ public class CompanyBalanceTask {
 
 
 
 
 
 
+    /**
+     * @Description: 更具批次号查询转账结果
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/12/25 10:31
+     */
+    public void checkMchTransferStatus(String outBatchNo,String companyIdStr) {
+        Long companyId=Long.parseLong(companyIdStr);
+        String result=companyService.checkMchTransferStatus(outBatchNo,companyId);
+        log.info("查询商户转账结果:{}",result);
+    }
+
+
+
+    /**
+     * @Description: 更具批次号查询转账结果
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/12/25 10:31
+     */
+    public void checkMchTransferStatusByBatchID(String batchId,String companyIdStr) {
+        Long companyId=Long.parseLong(companyIdStr);
+        R result=companyService.checkMchTransferStatusByBatchID(batchId,companyId);
+        log.info("查询商户转账结果:{}",result);
+    }
+
+
 
 
 
 
 }
 }

+ 93 - 8
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -1,13 +1,15 @@
 package com.fs.his.task;
 package com.fs.his.task;
 
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.service.impl.SmsServiceImpl;
 import com.fs.common.service.impl.SmsServiceImpl;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DateUtils;
@@ -23,16 +25,11 @@ import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.dto.BatchSendCourseAllDTO;
 import com.fs.course.dto.BatchSendCourseAllDTO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.IFsCourseWatchLogService;
-import com.fs.course.service.IFsUserCourseOrderService;
 import com.fs.course.service.ITencentCloudCosService;
 import com.fs.course.service.ITencentCloudCosService;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpDeliverys;
-import com.fs.erp.domain.ErpOrder;
 import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.domain.ErpOrderQuery;
-import com.fs.erp.domain.FsErpFinishPush;
 import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.dto.ErpOrderQueryResponse;
-import com.fs.erp.dto.ErpOrderResponse;
-import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.fastGpt.domain.*;
 import com.fs.fastGpt.domain.*;
 import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
 import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
@@ -41,7 +38,6 @@ import com.fs.fastGpt.service.AiHookService;
 import com.fs.fastGpt.service.IFastgptEventLogTotalService;
 import com.fs.fastGpt.service.IFastgptEventLogTotalService;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.fastgptApi.vo.AudioVO;
-import com.fs.gtPush.mapper.PushLogMapper;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.*;
 import com.fs.his.domain.*;
@@ -52,12 +48,18 @@ import com.fs.his.mapper.*;
 import com.fs.his.param.FsInquiryOrderFinishParam;
 import com.fs.his.param.FsInquiryOrderFinishParam;
 import com.fs.his.param.FsPackageOrderCancelParam;
 import com.fs.his.param.FsPackageOrderCancelParam;
 import com.fs.his.service.*;
 import com.fs.his.service.*;
-import com.fs.his.service.impl.FsPackageOrderServiceImpl;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
 import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
+import com.fs.hisStore.service.IFsStorePaymentScrmService;
+import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
+import com.fs.huifuPay.service.HuiFuService;
+import com.fs.hisStore.domain.FsStorePaymentScrm;
+import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
 import com.fs.hisStore.service.IFsStorePaymentScrmService;
 import com.fs.hisStore.service.IFsStorePaymentScrmService;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
@@ -89,12 +91,15 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.math.RoundingMode;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletableFuture;
@@ -241,7 +246,11 @@ public class Task {
     @Autowired
     @Autowired
     private AiHookService aiHookService;
     private AiHookService aiHookService;
 
 
+    @Autowired
+    private IFsStorePaymentScrmService paymentScrmService;
 
 
+    @Autowired
+    private IFsStoreOrderScrmService orderScrmService;
     /**
     /**
      * 定时任务,处理ai禁止回复之后的消息
      * 定时任务,处理ai禁止回复之后的消息
      */
      */
@@ -616,6 +625,52 @@ public class Task {
 
 
     }
     }
 
 
+    public void paymentScrm() {
+
+        List<FsStorePaymentScrm> fsStorePaymentScrms = paymentScrmService.selectFsStorePaymentByAll();
+        fsStorePaymentScrms.forEach(payment -> {
+            try {
+                String payMode = payment.getPayMode();
+                logger.info("手动查询"+payment);
+                if (payMode.equals("hf")){
+                    V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+                    request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+                    request.setOrgHfSeqId(payment.getTradeNo());
+                    request.setAppId(payment.getAppId());
+                    HuiFuQueryOrderResult o = null;
+                    try {
+                        o = huiFuService.queryOrder(request);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                    logger.info("汇付返回"+o);
+                    if ("00000000".equals(o.getResp_code())) {
+                        if (o.getTrans_stat().equals("S")) {
+                            String[] order=o.getOrg_req_seq_id().split("-");
+                            switch (order[0]) {
+                                case "store":
+                                    orderScrmService.payConfirm(1,null,order[1], o.getOrg_hf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
+                                    break;
+                                case "store_remain":
+                                    orderScrmService.payRemainConfirm( order[1], o.getOrg_hf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
+                                    break;
+                                case "payment":
+                                    fsStorePaymentService.payConfirm(order[1],o.getOrg_hf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }catch (Exception e){
+                logger.error("当前订单同步失败-"+payment+":"+e.getMessage());
+            }
+
+        });
+
+
+    }
+
+
     public void addQwUserName() {
     public void addQwUserName() {
         QwCompany qwCompany = new QwCompany();
         QwCompany qwCompany = new QwCompany();
         List<QwCompany> companyList = qwCompanyService.selectQwCompanyList(qwCompany);
         List<QwCompany> companyList = qwCompanyService.selectQwCompanyList(qwCompany);
@@ -924,6 +979,35 @@ public class Task {
         }
         }
     }
     }
 
 
+    /**
+     * 推送河山医院
+     */
+    public void puSubStoreOrderHsyy() {
+        // 获取时间并格式化为字符串
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+        String todayStartStr = LocalDateTime.now()
+                .withHour(0)
+                .withMinute(0)
+                .withSecond(0)
+                .withNano(0)
+                .format(formatter);
+
+        String yesterdayStartStr = LocalDateTime.now()
+                .minusDays(1)
+                .withHour(0)
+                .withMinute(0)
+                .withSecond(0)
+                .withNano(0)
+                .format(formatter);
+
+        List<FsStoreSubOrder> list = fsStoreSubOrderService.selectFsStoreSubOrderListByCreateTime(yesterdayStartStr, todayStartStr);
+        log.info("------>>>>>>"+todayStartStr+"推送" + yesterdayStartStr+"的子订单数据共:" + list.size() + "条");
+        if(CollectionUtil.isNotEmpty(list)){
+            fsStoreSubOrderService.pushHsyy(list);
+        }
+    }
+
     public void refundOp() {
     public void refundOp() {
         List<FsStoreAfterSales> list = fsStoreAfterSalesService.selectFsStoreAfterSalesByDoAudit();
         List<FsStoreAfterSales> list = fsStoreAfterSalesService.selectFsStoreAfterSalesByDoAudit();
         if (list != null) {
         if (list != null) {
@@ -1163,6 +1247,7 @@ public class Task {
                 V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                 V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgHfSeqId(payment.getTradeNo());
                 request.setOrgHfSeqId(payment.getTradeNo());
+                request.setAppId(payment.getAppId());
                 HuiFuQueryOrderResult o = null;
                 HuiFuQueryOrderResult o = null;
                 try {
                 try {
                     o = huiFuService.queryOrder(request);
                     o = huiFuService.queryOrder(request);

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

@@ -100,7 +100,7 @@ public class FsStoreAfterSalesScrmController extends BaseController
             return AjaxResult.error("请筛选数据导出");
             return AjaxResult.error("请筛选数据导出");
         }
         }
 
 
-        List<FsStoreAfterSalesVO> list = fsStoreAfterSalesService.selectFsStoreAfterSalesListVO(fsStoreAfterSales);
+        List<FsStoreAfterSalesVO> list = fsStoreAfterSalesService.selectFsStoreAfterSalesListVOExport(fsStoreAfterSales);
         if("北京卓美".equals(signProjectName)){
         if("北京卓美".equals(signProjectName)){
             List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
             List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
                     .map(vo -> {
                     .map(vo -> {
@@ -123,7 +123,7 @@ public class FsStoreAfterSalesScrmController extends BaseController
                             zmvo.setRealName(vo.getUserName());
                             zmvo.setRealName(vo.getUserName());
                             zmvo.setUserPhone(vo.getUserPhone());
                             zmvo.setUserPhone(vo.getUserPhone());
                             zmvo.setUserAddress(vo.getUserAddress());
                             zmvo.setUserAddress(vo.getUserAddress());
-                            zmvo.setCreateTime(vo.getCreateTime());
+                            zmvo.setCreateTime(vo.getOrderCreateTime());
                             zmvo.setPayTime(vo.getOrderPayTime());
                             zmvo.setPayTime(vo.getOrderPayTime());
                             zmvo.setDeliverySn(vo.getOrderDeliverySn());
                             zmvo.setDeliverySn(vo.getOrderDeliverySn());
                             zmvo.setDeliveryName(vo.getOrderDeliveryName());
                             zmvo.setDeliveryName(vo.getOrderDeliveryName());

+ 3 - 2
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java

@@ -35,6 +35,7 @@ import com.fs.hisStore.vo.FsStoreOrderExportVO;
 import com.fs.hisStore.vo.FsStoreOrderItemExportVO;
 import com.fs.hisStore.vo.FsStoreOrderItemExportVO;
 import com.fs.hisStore.vo.FsStoreOrderVO;
 import com.fs.hisStore.vo.FsStoreOrderVO;
 import com.fs.hisStore.vo.*;
 import com.fs.hisStore.vo.*;
+import com.github.pagehelper.PageHelper;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
@@ -123,10 +124,10 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         if (list != null) {
         if (list != null) {
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
             for (FsStoreOrderVO vo : list) {
             for (FsStoreOrderVO vo : list) {
-                if(vo.getPhone()!=null){
+                if(StringUtils.isNotEmpty(vo.getPhone())){
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 }
-                if(vo.getUserPhone()!=null){
+                if (StringUtils.isNotEmpty(vo.getUserPhone())){
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 }
                 if (CloudHostUtils.hasCloudHostName("康年堂")){
                 if (CloudHostUtils.hasCloudHostName("康年堂")){

+ 11 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderItemScrmController.java

@@ -94,4 +94,15 @@ public class FsStoreOrderItemScrmController extends BaseController
     {
     {
         return toAjax(fsStoreOrderItemService.deleteFsStoreOrderItemByIds(itemIds));
         return toAjax(fsStoreOrderItemService.deleteFsStoreOrderItemByIds(itemIds));
     }
     }
+
+    /**
+     * 修改订单数量及总价
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeOrderItem:updateNum')")
+    @Log(title = "订单详情修改订单数量", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateNum")
+    public AjaxResult updateNum(@RequestBody FsStoreOrderItemScrm fsStoreOrderItem)
+    {
+        return toAjax(fsStoreOrderItemService.updateFsStoreOrderItemNum(fsStoreOrderItem));
+    }
 }
 }

+ 57 - 3
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -1,6 +1,7 @@
 package com.fs.hisStore.controller;
 package com.fs.hisStore.controller;
 
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
@@ -21,6 +22,7 @@ import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.param.CompanyStoreOrderMoneyLogsListParam;
 import com.fs.company.param.CompanyStoreOrderMoneyLogsListParam;
 import com.fs.company.service.ICompanyMoneyLogsService;
 import com.fs.company.service.ICompanyMoneyLogsService;
 import com.fs.company.vo.CompanyStoreOrderMoneyLogsVO;
 import com.fs.company.vo.CompanyStoreOrderMoneyLogsVO;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryRequert;
@@ -44,6 +46,7 @@ import com.fs.his.vo.FsStoreOrderListVO;
 import com.fs.hisStore.config.FsErpConfig;
 import com.fs.hisStore.config.FsErpConfig;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.domain.*;
 import com.fs.his.dto.ExpressInfoDTO;
 import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.hisStore.dto.FsStoreOrderPayDeliveryDTO;
 import com.fs.hisStore.dto.StoreOrderExpressExportDTO;
 import com.fs.hisStore.dto.StoreOrderExpressExportDTO;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
 import com.fs.hisStore.enums.ShipperCodeEnum;
 import com.fs.hisStore.enums.ShipperCodeEnum;
@@ -102,6 +105,9 @@ public class FsStoreOrderScrmController extends BaseController {
     @Autowired
     @Autowired
     IFsStorePaymentScrmService paymentService;
     IFsStorePaymentScrmService paymentService;
 
 
+    @Autowired
+    private CloudHostProper cloudHostProper;
+
     @Autowired
     @Autowired
     private ICompanyMoneyLogsService moneyLogsService;
     private ICompanyMoneyLogsService moneyLogsService;
     @Autowired
     @Autowired
@@ -212,7 +218,7 @@ public class FsStoreOrderScrmController extends BaseController {
                 if(vo.getPhone()!=null){
                 if(vo.getPhone()!=null){
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 }
-                if(vo.getUserPhone()!=null){
+                if(ObjectUtil.isNotEmpty(vo.getUserPhone())){
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 }
                 if (CloudHostUtils.hasCloudHostName("康年堂")){
                 if (CloudHostUtils.hasCloudHostName("康年堂")){
@@ -699,7 +705,7 @@ public class FsStoreOrderScrmController extends BaseController {
         ExpressInfoDTO expressInfoDTO = null;
         ExpressInfoDTO expressInfoDTO = null;
         if (StringUtils.isNotEmpty(order.getDeliveryId())) {
         if (StringUtils.isNotEmpty(order.getDeliveryId())) {
             String lastFourNumber = "";
             String lastFourNumber = "";
-            if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+            if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue()) || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
                 lastFourNumber = order.getUserPhone();
                 lastFourNumber = order.getUserPhone();
                 if (lastFourNumber.length() == 11) {
                 if (lastFourNumber.length() == 11) {
                     lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
                     lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -729,7 +735,14 @@ public class FsStoreOrderScrmController extends BaseController {
     public AjaxResult edit(@RequestBody FsStoreOrderScrm fsStoreOrder) {
     public AjaxResult edit(@RequestBody FsStoreOrderScrm fsStoreOrder) {
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
     }
     }
-
+    /**
+     * 修改订单itemJson
+     */
+    @Log(title = "修改订单itemJson", businessType = BusinessType.UPDATE)
+    @GetMapping("/updateStoreOrderItemJson/{orderId}/{backendEditProductType}")
+    public AjaxResult updateStoreOrderItemJson(@PathVariable("orderId") Long orderId,@PathVariable("backendEditProductType") Integer backendEditProductType) {
+        return toAjax(fsStoreOrderService.updateStoreOrderItemJson(orderId,backendEditProductType));
+    }
     /**
     /**
      * 修改物流
      * 修改物流
      * @param fsStoreOrder
      * @param fsStoreOrder
@@ -1167,6 +1180,47 @@ public class FsStoreOrderScrmController extends BaseController {
         return R.ok();
         return R.ok();
     }
     }
 
 
+    @ApiOperation("批量审核订单")
+    @Log(title = "订单管理", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('store:storeOrder:batchAudit')")
+    @PostMapping("/batchAudit")
+    public R batchAuditOrder(@Validated @RequestBody FsStoreOrderBatchAuditParam param) {
+        if (param.getOrderIds() == null || param.getOrderIds().isEmpty()) {
+            return R.error("订单ID列表不能为空");
+        }
+        if (param.getIsAudit() == null) {
+            return R.error("审核状态不能为空");
+        }
+        int count = fsStoreOrderService.batchAuditOrder(param);
+        return R.ok("成功审核 " + count + " 条订单");
+    }
+
+    @ApiOperation("订单备注")
+    @Log(title = "订单管理", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('store:storeOrder:remark')")
+    @PostMapping("/remark")
+    public R remark(@Validated @RequestBody FsStoreOrderScrm param) {
+        if (param.getId() == null || param.getId() < 1) {
+            return R.error("订单ID错误");
+        }
+        if (StringUtils.isEmpty(param.getOrderRemark())) {
+            return R.error("订单备注不能为空");
+        }
+        return fsStoreOrderService.orderRemark(param);
+    }
+
+    /**
+     * 修改订单的代收金额
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeOrder:editPayDelivery')")
+    @Log(title = "订单-修改代收金额", businessType = BusinessType.UPDATE)
+    @PutMapping("/editPayDelivery")
+    public AjaxResult editPayDelivery(@RequestBody FsStoreOrderPayDeliveryDTO fsStoreOrderPayDeliveryDTO) {
+        FsStoreOrderScrm fsStoreOrderScrm = new FsStoreOrderScrm();
+        BeanUtils.copyProperties(fsStoreOrderPayDeliveryDTO, fsStoreOrderScrm);
+        return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrderScrm));
+    }
+
     private FsStoreOrderDf getDFInfo(String loginAccount) {
     private FsStoreOrderDf getDFInfo(String loginAccount) {
         //查询订单账户 判断是否存在该订单账户
         //查询订单账户 判断是否存在该订单账户
         List<FsDfAccount> erpAccounts = fsDfAccountService.selectFsDfAccountList(null);
         List<FsDfAccount> erpAccounts = fsDfAccountService.selectFsDfAccountList(null);

+ 9 - 2
fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java

@@ -60,6 +60,7 @@ import org.springframework.transaction.interceptor.TransactionAspectSupport;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -250,7 +251,7 @@ public class FsStorePaymentScrmController extends BaseController
     }
     }
 
 
     @PreAuthorize("@ss.hasPermi('store:storePayment:refund')")
     @PreAuthorize("@ss.hasPermi('store:storePayment:refund')")
-    @PostMapping("refundStorePayment")
+    @PostMapping("/refundStorePayment")
     @Transactional
     @Transactional
     public R refundStorePayment(@RequestBody FsStorePaymentScrm fsStorePayment)
     public R refundStorePayment(@RequestBody FsStorePaymentScrm fsStorePayment)
     {
     {
@@ -297,7 +298,13 @@ public class FsStorePaymentScrmController extends BaseController
                 }
                 }
                 V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
                 V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
                 request.setHuifuId(huifuId);
                 request.setHuifuId(huifuId);
-                request.setOrdAmt(payment.getPayMoney().toString());
+
+                if (("易行健".equals(cloudHostProper.getCompanyName()))){
+                    request.setOrdAmt(fsStorePayment.getRefundMoney().setScale(2, RoundingMode.DOWN).toString());
+                }else {
+                    request.setOrdAmt(payment.getPayMoney().toString());
+                }
+
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setReqSeqId("refund-"+payment.getPayCode());
                 request.setReqSeqId("refund-"+payment.getPayCode());
                 request.setAppId(payment.getAppId());
                 request.setAppId(payment.getAppId());

+ 11 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java

@@ -177,6 +177,17 @@ public class FsStoreProductScrmController extends BaseController
     }
     }
 
 
 
 
+    @PostMapping(value = "/updateCache")
+    public R updateCache(@RequestParam Long productId)
+    {
+        if (productId == null) {
+            return R.error();
+        }
+
+        return fsStoreProductService.updateCache(productId);
+    }
+
+
 
 
 
 
     /**
     /**

+ 2 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreStatisticsScrmController.java

@@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -76,7 +77,7 @@ public class FsStoreStatisticsScrmController extends BaseController
             List<JSONObject> jsonObjectList = storeOrderService.selectFsStoreOrderCounts(timeEntity.toMap());
             List<JSONObject> jsonObjectList = storeOrderService.selectFsStoreOrderCounts(timeEntity.toMap());
             List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
             List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
             List<Integer> orderCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("orderCount")).collect(Collectors.toList());
             List<Integer> orderCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("orderCount")).collect(Collectors.toList());
-            List<Integer> payPrice = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("payPrice")).collect(Collectors.toList());
+            List<BigDecimal> payPrice = jsonObjectList.stream().map(jsonObject -> jsonObject.getBigDecimal("payPrice")).collect(Collectors.toList());
             //表格数据
             //表格数据
             List<FsStoreOrderCountsVO> tableData = storeOrderService.selectFsStoreOrderCountsByDept(timeEntity.toMap(),param.getDeptId());
             List<FsStoreOrderCountsVO> tableData = storeOrderService.selectFsStoreOrderCountsByDept(timeEntity.toMap(),param.getDeptId());
             return R.ok().put("dates",dates).put("orderCount",orderCount).put("payPrice",payPrice).put("tableData",tableData);
             return R.ok().put("dates",dates).put("orderCount",orderCount).put("payPrice",payPrice).put("tableData",tableData);

+ 101 - 6
fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java

@@ -40,6 +40,7 @@ import com.fs.live.param.LiveAfterSalesAudit1Param;
 import com.fs.live.param.LiveAfterSalesParam;
 import com.fs.live.param.LiveAfterSalesParam;
 import com.fs.live.param.LiveAfterSalesProductParam;
 import com.fs.live.param.LiveAfterSalesProductParam;
 import com.fs.live.service.*;
 import com.fs.live.service.*;
+import com.fs.live.utils.redis.RedisBatchHandler;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.service.IPayService;
 import com.fs.pay.service.IPayService;
 import com.fs.store.config.StoreConfig;
 import com.fs.store.config.StoreConfig;
@@ -51,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -65,6 +67,7 @@ import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletableFuture;
 
 
 import static com.fs.hisStore.constants.StoreConstants.DELIVERY;
 import static com.fs.hisStore.constants.StoreConstants.DELIVERY;
+import static com.fs.live.utils.redis.RedisBatchHandler.CONSUME_INTERVAL;
 
 
 /**
 /**
  * 定时任务调度测试
  * 定时任务调度测试
@@ -163,6 +166,92 @@ public class LiveTask {
 
 
     @Autowired
     @Autowired
     private FsJstAftersalePushScrmService fsJstAftersalePushScrmService;
     private FsJstAftersalePushScrmService fsJstAftersalePushScrmService;
+    @Autowired
+    public RedisBatchHandler redisBatchHandler;
+
+    /**
+     * 查询被拆分的订单,然后查询拆分订单的物流信息
+     */
+    public void querySplitOrderDelivery() {
+        try {
+            // 查询状态为6(被拆分)的订单
+            List<LiveOrder> splitOrders = liveOrderMapper.selectSplitOrders();
+            if (splitOrders == null || splitOrders.isEmpty()) {
+                log.debug("没有找到被拆分的订单");
+                return;
+            }
+
+            log.info("找到 {} 个被拆分的订单,开始查询拆分订单的物流信息", splitOrders.size());
+
+            IErpOrderService erpOrderService = getErpOrderService();
+            if (erpOrderService == null) {
+                log.warn("ERP服务未配置,无法查询拆分订单物流信息");
+                return;
+            }
+
+            for (LiveOrder splitOrder : splitOrders) {
+                try {
+                    // 查询该订单的所有拆分订单(通过原订单号查询)
+                    List<LiveOrder> childOrders = liveOrderMapper.selectChildOrdersByParentOrderCode(splitOrder.getOrderCode());
+                    if (childOrders == null || childOrders.isEmpty()) {
+                        log.debug("订单 {} 没有找到拆分订单", splitOrder.getOrderCode());
+                        continue;
+                    }
+
+                    // 遍历拆分订单,查询物流信息
+                    for (LiveOrder childOrder : childOrders) {
+                        if (StringUtils.isEmpty(childOrder.getExtendOrderId())) {
+                            log.debug("拆分订单 {} 没有扩展订单ID,跳过", childOrder.getOrderCode());
+                            continue;
+                        }
+
+                        // 查询ERP订单信息
+                        ErpOrderQueryRequert request = new ErpOrderQueryRequert();
+                        request.setCode(childOrder.getExtendOrderId());
+                        ErpOrderQueryResponse response = erpOrderService.getLiveOrder(request);
+
+                        if (!response.getSuccess()) {
+                            if ("429".equals(response.getCode())) {
+                                log.warn("ERP接口限流,停止查询");
+                                break;
+                            }
+                            log.warn("查询拆分订单物流信息失败, orderCode={}, error={}", childOrder.getOrderCode(), response.getCode());
+                            continue;
+                        }
+
+                        // 更新物流信息
+                        if (response.getOrders() != null && !response.getOrders().isEmpty()) {
+                            for (ErpOrderQuery orderQuery : response.getOrders()) {
+                                if (orderQuery.getDeliverys() != null && !orderQuery.getDeliverys().isEmpty()) {
+                                    for (ErpDeliverys delivery : orderQuery.getDeliverys()) {
+                                        if (delivery.getDelivery() && StringUtils.isNotEmpty(delivery.getMail_no())) {
+                                            // 更新订单物流信息
+                                            childOrder.setDeliverySn(delivery.getMail_no());
+                                            childOrder.setDeliveryCode(delivery.getExpress_code());
+                                            childOrder.setDeliveryName(delivery.getExpress_name());
+                                            if (childOrder.getStatus() == 2) { // 待发货状态
+                                                childOrder.setStatus(3); // 更新为待收货
+                                            }
+                                            childOrder.setUpdateTime(new Date());
+                                            liveOrderMapper.updateLiveOrder(childOrder);
+                                            log.info("拆分订单物流信息已更新, orderCode={}, deliverySn={}, expressName={}",
+                                                    childOrder.getOrderCode(), delivery.getMail_no(), delivery.getExpress_name());
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("处理拆分订单物流信息异常, orderCode={}", splitOrder.getOrderCode(), e);
+                }
+            }
+
+            log.info("拆分订单物流信息查询完成");
+        } catch (Exception e) {
+            log.error("查询拆分订单物流信息任务异常", e);
+        }
+    }
 
 
     // 聚水潭 推送售后信息
     // 聚水潭 推送售后信息
     public void pushJst(){
     public void pushJst(){
@@ -182,6 +271,7 @@ public class LiveTask {
                 V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                 V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgHfSeqId(payment.getTradeNo());
                 request.setOrgHfSeqId(payment.getTradeNo());
+                request.setAppId(payment.getAppId());
                 HuiFuQueryOrderResult o = null;
                 HuiFuQueryOrderResult o = null;
                 try {
                 try {
                     o = huiFuService.queryOrder(request);
                     o = huiFuService.queryOrder(request);
@@ -246,17 +336,14 @@ public class LiveTask {
     public void deliveryOp() {
     public void deliveryOp() {
         List<LiveOrder> list = liveOrderService.selectUpdateExpress();
         List<LiveOrder> list = liveOrderService.selectUpdateExpress();
         if(list == null || list.isEmpty()) return;
         if(list == null || list.isEmpty()) return;
-
+        Date now = new Date();
         for (LiveOrder order : list) {
         for (LiveOrder order : list) {
-            order.setUpdateTime(new Date());
-            liveOrderService.updateLiveOrder(order);
+            order.setUpdateTime(now);
+            liveOrderService.updateTime(order);
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());
             request.setCode(order.getExtendOrderId());
             IErpOrderService erpOrderService = getErpOrderService();
             IErpOrderService erpOrderService = getErpOrderService();
             ErpOrderQueryResponse response = erpOrderService.getLiveOrder(request);
             ErpOrderQueryResponse response = erpOrderService.getLiveOrder(request);
-            if(!response.getSuccess() && "429".equals(response.getCode())){
-                break;
-            }
             if (erpOrderService != dfOrderService) {
             if (erpOrderService != dfOrderService) {
                 if (response.getOrders() != null && !response.getOrders().isEmpty()) {
                 if (response.getOrders() != null && !response.getOrders().isEmpty()) {
                     for (ErpOrderQuery orderQuery : response.getOrders()) {
                     for (ErpOrderQuery orderQuery : response.getOrders()) {
@@ -581,4 +668,12 @@ public class LiveTask {
              }
              }
         }
         }
     }
     }
+    /**
+     * 定时流量入库
+     */
+    @Scheduled(fixedRate = CONSUME_INTERVAL)
+    public void insertLiveTrralog() {
+        redisBatchHandler.consumeBatchData();
+    }
+
 }
 }

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

@@ -34,6 +34,11 @@ import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.*;
 import com.fs.hisStore.service.*;
+import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
+import com.fs.huifuPay.service.HuiFuService;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.domain.LiveOrderPayment;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.domain.LiveOrder;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.service.IPayService;
 import com.fs.pay.service.IPayService;
@@ -50,6 +55,7 @@ import org.springframework.stereotype.Component;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.text.ParseException;
 import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.time.LocalTime;
 import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.Date;
@@ -160,6 +166,39 @@ public class MallStoreTask
 
 
     //@Autowired
     //@Autowired
     //private IFsUserOnlineStateService fsUserOnlineStateService;
     //private IFsUserOnlineStateService fsUserOnlineStateService;
+    @Autowired
+    private HuiFuService huiFuService;
+
+    // 订单银行回调数据丢失补偿
+    public void recoveryBankOrder() {
+        // 查询出来最近30分钟的订单 待支付 未退款
+        List<FsStoreOrderScrm> list = fsStoreOrderMapper.selectBankOrder();
+        if(list == null || list.isEmpty()) return;
+        for (FsStoreOrderScrm order : list) {
+            List<FsStorePaymentScrm> orderPayments = fsStorePaymentMapper.selectNoPayByOrderId(order.getId());
+            if(orderPayments == null || orderPayments.isEmpty()) continue;
+            for (FsStorePaymentScrm payment : orderPayments) {
+                V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+                request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+                request.setOrgHfSeqId(payment.getTradeNo());
+                request.setAppId(payment.getAppId());
+                HuiFuQueryOrderResult o = null;
+                try {
+                    o = huiFuService.queryOrder(request);
+                } catch (Exception e) {
+                    log.error("查询失败:"+e.getMessage());
+                    continue;
+                }
+                log.info("汇付返回"+o);
+                if ("00000000".equals(o.getResp_code()) && "S".equals(o.getTrans_stat())) {
+                    String[] orderSpilt=o.getOrg_req_seq_id().split("-");
+                    if ("store".equals(orderSpilt[0])) {
+                        orderService.payConfirm(1, null, orderSpilt[1], o.getOrg_hf_seq_id(), o.getOut_trans_id(), o.getParty_order_id());
+                    }
+                }
+            }
+        }
+    }
 
 
     public void PushErp() throws ParseException {
     public void PushErp() throws ParseException {
         List<Long> ids;
         List<Long> ids;
@@ -229,9 +268,6 @@ public class MallStoreTask
             request.setCode(order.getExtendOrderId());
             request.setCode(order.getExtendOrderId());
             IErpOrderService erpOrderService = getErpOrderService();
             IErpOrderService erpOrderService = getErpOrderService();
             ErpOrderQueryResponse response = erpOrderService.getScrmOrder(request);
             ErpOrderQueryResponse response = erpOrderService.getScrmOrder(request);
-            if(!response.getSuccess() && "429".equals(response.getCode())){
-                break;
-            }
             if (erpOrderService != dfOrderService) {
             if (erpOrderService != dfOrderService) {
                 if(response.getOrders()!=null && !response.getOrders().isEmpty()){
                 if(response.getOrders()!=null && !response.getOrders().isEmpty()){
                     for(ErpOrderQuery orderQuery : response.getOrders()){
                     for(ErpOrderQuery orderQuery : response.getOrders()){
@@ -449,7 +485,7 @@ public class MallStoreTask
     //每天执行一次
     //每天执行一次
     public void syncExpress()
     public void syncExpress()
     {
     {
-        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIds();
+        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIdsNoDate();
 
 
         for (Long id : ids) {
         for (Long id : ids) {
             FsStoreOrderExpressEditParam param =new FsStoreOrderExpressEditParam();
             FsStoreOrderExpressEditParam param =new FsStoreOrderExpressEditParam();

+ 74 - 0
fs-admin/src/main/java/com/fs/live/controller/FsWxExpressTaskController.java

@@ -0,0 +1,74 @@
+package com.fs.live.controller;
+
+import com.alibaba.fastjson.JSONObject;
+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.system.service.ISysConfigService;
+import com.fs.wx.order.domain.FsWxExpressTask;
+import com.fs.wx.order.service.IFsWxExpressTaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 微信快递任务Controller
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@RestController
+@RequestMapping("/live/wxExpressTask")
+public class FsWxExpressTaskController extends BaseController {
+
+    @Autowired
+    private IFsWxExpressTaskService fsWxExpressTaskService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    /**
+     * 查询微信快递任务列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:wxExpressTask:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsWxExpressTask fsWxExpressTask) {
+        startPage();
+        List<FsWxExpressTask> list = fsWxExpressTaskService.selectFsWxExpressTaskList(fsWxExpressTask);
+        return getDataTable(list);
+    }
+
+    /**
+     * 获取当前使用小程序的微信上传失败订单数量
+     */
+    @GetMapping("/getFailedCount")
+    public AjaxResult getFailedCount() {
+        try {
+            // 获取his.store配置
+            String configJson = configService.selectConfigByKey("his.config");
+            if (configJson == null || configJson.isEmpty()) {
+                return AjaxResult.success(0);
+            }
+            
+            // 解析JSON获取appId
+            JSONObject config = JSONObject.parseObject(configJson);
+            String appId = config.getString("appid");
+            
+            if (appId == null || appId.isEmpty()) {
+                return AjaxResult.success(0);
+            }
+            
+            // 查询失败订单数量(status=3)
+            int count = fsWxExpressTaskService.countFailedTasksByAppId(appId);
+            return AjaxResult.success(count);
+        } catch (Exception e) {
+            return AjaxResult.error("查询失败订单数量失败:" + e.getMessage());
+        }
+    }
+}

+ 1 - 1
fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java

@@ -114,7 +114,7 @@ public class LiveAfterSalesController extends BaseController
     {
     {
         PageHelper.clearPage();
         PageHelper.clearPage();
         PageHelper.startPage(1, 10000, "");
         PageHelper.startPage(1, 10000, "");
-        List<LiveAfterSalesVo> list = liveAfterSalesService.selectLiveAfterSalesVoList(liveAfterSales);
+        List<LiveAfterSalesVo> list = liveAfterSalesService.selectLiveAfterSalesVoListExport(liveAfterSales);
         if("北京卓美".equals(signProjectName)){
         if("北京卓美".equals(signProjectName)){
             List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
             List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
                     .map(vo -> {
                     .map(vo -> {

+ 15 - 1
fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java

@@ -8,6 +8,8 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyUser;
+import com.fs.framework.web.service.TokenService;
 import com.fs.live.domain.LiveData;
 import com.fs.live.domain.LiveData;
 import com.fs.live.param.LiveDataParam;
 import com.fs.live.param.LiveDataParam;
 import com.fs.live.service.ILiveDataService;
 import com.fs.live.service.ILiveDataService;
@@ -29,6 +31,8 @@ public class LiveDataController extends BaseController {
 
 
     @Autowired
     @Autowired
     private ILiveDataService liveDataService;
     private ILiveDataService liveDataService;
+    @Autowired
+    private TokenService tokenService;
 
 
     /**
     /**
      * 直播数据页面卡片数据
      * 直播数据页面卡片数据
@@ -128,11 +132,21 @@ public class LiveDataController extends BaseController {
     /**
     /**
      * 查询直播间用户详情列表(SQL方式)
      * 查询直播间用户详情列表(SQL方式)
      * @param liveId 直播间ID
      * @param liveId 直播间ID
+     * @param pageNum 页码
+     * @param pageSize 每页大小
      * @return 用户详情列表
      * @return 用户详情列表
      */
      */
     @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
     @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
     @GetMapping("/getLiveUserDetailListBySql")
     @GetMapping("/getLiveUserDetailListBySql")
-    public R getLiveUserDetailListBySql(@RequestParam Long liveId) {
+    public R getLiveUserDetailListBySql(@RequestParam Long liveId,
+                                        @RequestParam(defaultValue = "1") Integer pageNum,
+                                        @RequestParam(defaultValue = "100") Integer pageSize) {
+        // 限制最大每页查询条数为1000
+        if (pageSize > 1000) {
+            pageSize = 1000;
+        }
+
+        PageHelper.startPage(pageNum, pageSize);
         return liveDataService.getLiveUserDetailListBySql(liveId,null,null);
         return liveDataService.getLiveUserDetailListBySql(liveId,null,null);
     }
     }
 
 

+ 28 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveMsgController.java

@@ -3,11 +3,16 @@ package com.fs.live.controller;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.framework.web.service.TokenService;
 import com.fs.live.domain.LiveMsg;
 import com.fs.live.domain.LiveMsg;
 import com.fs.live.service.ILiveMsgService;
 import com.fs.live.service.ILiveMsgService;
+import com.fs.live.vo.LiveMsgExportVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
@@ -26,6 +31,9 @@ public class LiveMsgController extends BaseController
 {
 {
     @Autowired
     @Autowired
     private ILiveMsgService liveMsgService;
     private ILiveMsgService liveMsgService;
+    
+    @Autowired
+    private TokenService tokenService;
 
 
     /**
     /**
      * 查询直播讨论列表
      * 查询直播讨论列表
@@ -102,4 +110,24 @@ public class LiveMsgController extends BaseController
     {
     {
         return toAjax(liveMsgService.deleteLiveMsgByMsgIds(msgIds));
         return toAjax(liveMsgService.deleteLiveMsgByMsgIds(msgIds));
     }
     }
+
+    /**
+     * 导出直播评论
+     */
+    @PreAuthorize("@ss.hasPermi('live:liveMsg:export')")
+    @Log(title = "直播评论导出", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportComments/{liveId}")
+    public AjaxResult exportComments(@PathVariable("liveId") Long liveId)
+    {
+        try {
+            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+            Long userId = loginUser.getUser().getUserId();
+            
+            List<LiveMsgExportVO> list = liveMsgService.exportLiveMsgComments(liveId, userId);
+            ExcelUtil<LiveMsgExportVO> util = new ExcelUtil<LiveMsgExportVO>(LiveMsgExportVO.class);
+            return util.exportExcel(list, "直播评论数据");
+        } catch (Exception e) {
+            return AjaxResult.error("导出失败:" + e.getMessage());
+        }
+    }
 }
 }

+ 74 - 25
fs-admin/src/main/java/com/fs/live/controller/OrderController.java

@@ -7,10 +7,13 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.constant.HttpStatus;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
@@ -51,7 +54,7 @@ public class OrderController extends BaseController
     @Autowired
     @Autowired
     private IMergedOrderService mergedOrderService;
     private IMergedOrderService mergedOrderService;
     // 设置最大导出数量限制为20000条
     // 设置最大导出数量限制为20000条
-    private static final int maxExportCount = 20000;
+    private static final int maxExportCount = 50000;
 
 
 
 
     @Autowired
     @Autowired
@@ -64,7 +67,33 @@ public class OrderController extends BaseController
     @GetMapping("/list")
     @GetMapping("/list")
     public TableDataInfo list(MergedOrderQueryParam param)
     public TableDataInfo list(MergedOrderQueryParam param)
     {
     {
-        startPage();
+        // 从请求参数中获取分页信息
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        // 设置分页参数到 param
+        if (pageNum == null || pageNum < 1) {
+            pageNum = 1;
+            param.setPageNum(1);
+        } else {
+            param.setPageNum(pageNum);
+        }
+        if (pageSize == null || pageSize < 1) {
+            pageSize = 10;
+            param.setPageSize(10);
+        } else {
+            param.setPageSize(pageSize);
+        }
+
+        // 在外面计算 offset,然后放在请求参数中
+        Integer offset = (pageNum - 1) * pageSize;
+        param.setOffset(offset);
+
+        // 先查询总数(不分页)
+        long total = mergedOrderService.countMergedOrderList(param);
+
+        // 查询分页数据
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         for (MergedOrderVO vo : list) {
         for (MergedOrderVO vo : list) {
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
@@ -73,7 +102,13 @@ public class OrderController extends BaseController
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             vo.setCost(BigDecimal.ZERO);
             vo.setCost(BigDecimal.ZERO);
         }
         }
-        return getDataTable(list);
+
+        TableDataInfo dataTable = new TableDataInfo();
+        dataTable.setCode(HttpStatus.SUCCESS);
+        dataTable.setMsg("查询成功");
+        dataTable.setRows(list);
+        dataTable.setTotal(total);
+        return dataTable;
     }
     }
 
 
     /**
     /**
@@ -86,13 +121,16 @@ public class OrderController extends BaseController
     public AjaxResult export(MergedOrderQueryParam param)
     public AjaxResult export(MergedOrderQueryParam param)
     {
     {
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         // 先查询数据,限制查询20001条,用于判断是否超过限制
-        PageHelper.startPage(1, maxExportCount + 1);
+        param.setExportFlag(1);
+        param.setPageNum(1);
+        param.setPageSize(maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
-        
         // 如果查询结果超过20000条,返回错误提示
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
         if (list != null && list.size() > maxExportCount) {
             return AjaxResult.error("导出数据量超过限制,最多只能导出" + maxExportCount + "条数据,请缩小查询范围后重试");
             return AjaxResult.error("导出数据量超过限制,最多只能导出" + maxExportCount + "条数据,请缩小查询范围后重试");
         }
         }
+        list = list.stream().filter(item -> StringUtils.isNotEmpty(item.getBankTransactionId())).collect(Collectors.toList());
+
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
 
 
         for (MergedOrderVO vo : list) {
         for (MergedOrderVO vo : list) {
@@ -108,7 +146,7 @@ public class OrderController extends BaseController
 
 
         // 转换为导出VO
         // 转换为导出VO
         List<MergedOrderExportVO> exportList = convertToExportVO(list, false,loginUser);
         List<MergedOrderExportVO> exportList = convertToExportVO(list, false,loginUser);
-        
+
         // 如果数据量在限制范围内,正常导出
         // 如果数据量在限制范围内,正常导出
         ExcelUtil<MergedOrderExportVO> util = new ExcelUtil<>(MergedOrderExportVO.class);
         ExcelUtil<MergedOrderExportVO> util = new ExcelUtil<>(MergedOrderExportVO.class);
         return util.exportExcel(exportList, "合并订单数据");
         return util.exportExcel(exportList, "合并订单数据");
@@ -124,13 +162,16 @@ public class OrderController extends BaseController
     public AjaxResult exportDetails(MergedOrderQueryParam param)
     public AjaxResult exportDetails(MergedOrderQueryParam param)
     {
     {
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         // 先查询数据,限制查询20001条,用于判断是否超过限制
-        PageHelper.startPage(1, maxExportCount + 1);
+        param.setExportFlag(1);
+        param.setPageNum(1);
+        param.setPageSize(maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
-
         // 如果查询结果超过20000条,返回错误提示
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
         if (list != null && list.size() > maxExportCount) {
             return AjaxResult.error("导出数据量超过限制,最多只能导出" + maxExportCount + "条数据,请缩小查询范围后重试");
             return AjaxResult.error("导出数据量超过限制,最多只能导出" + maxExportCount + "条数据,请缩小查询范围后重试");
         }
         }
+        list = list.stream().filter(item -> StringUtils.isNotEmpty(item.getBankTransactionId())).collect(Collectors.toList());
+
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
 
 
         for (MergedOrderVO vo : list) {
         for (MergedOrderVO vo : list) {
@@ -163,7 +204,9 @@ public class OrderController extends BaseController
     public AjaxResult exportItems(MergedOrderQueryParam param)
     public AjaxResult exportItems(MergedOrderQueryParam param)
     {
     {
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         // 先查询数据,限制查询20001条,用于判断是否超过限制
-        PageHelper.startPage(1, maxExportCount + 1);
+        param.setExportFlag(1);
+        param.setPageNum(1);
+        param.setPageSize(maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
 
 
         // 如果查询结果超过20000条,返回错误提示
         // 如果查询结果超过20000条,返回错误提示
@@ -188,7 +231,9 @@ public class OrderController extends BaseController
     public AjaxResult exportItemsDetails(MergedOrderQueryParam param)
     public AjaxResult exportItemsDetails(MergedOrderQueryParam param)
     {
     {
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         // 先查询数据,限制查询20001条,用于判断是否超过限制
-        PageHelper.startPage(1, maxExportCount + 1);
+        param.setExportFlag(1);
+        param.setPageNum(1);
+        param.setPageSize(maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
 
 
         // 如果查询结果超过20000条,返回错误提示
         // 如果查询结果超过20000条,返回错误提示
@@ -209,7 +254,9 @@ public class OrderController extends BaseController
     public AjaxResult exportShipping(MergedOrderQueryParam param)
     public AjaxResult exportShipping(MergedOrderQueryParam param)
     {
     {
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         // 先查询数据,限制查询20001条,用于判断是否超过限制
-        PageHelper.startPage(1, maxExportCount + 1);
+        param.setExportFlag(1);
+        param.setPageNum(1);
+        param.setPageSize(maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         // 如果查询结果超过20000条,返回错误提示
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
         if (list != null && list.size() > maxExportCount) {
@@ -233,22 +280,23 @@ public class OrderController extends BaseController
 
 
         return list.stream().map(vo -> {
         return list.stream().map(vo -> {
             MergedOrderExportVO exportVO = new MergedOrderExportVO();
             MergedOrderExportVO exportVO = new MergedOrderExportVO();
-            
+
             // 订单基本信息(参考 FsStoreOrderItemExportVO 的顺序)
             // 订单基本信息(参考 FsStoreOrderItemExportVO 的顺序)
+            exportVO.setOrderTypeName(vo.getOrderTypeName());
             exportVO.setOrderCode(vo.getOrderCode());
             exportVO.setOrderCode(vo.getOrderCode());
             exportVO.setStatus(vo.getStatus() != null ? String.valueOf(vo.getStatus()) : null);
             exportVO.setStatus(vo.getStatus() != null ? String.valueOf(vo.getStatus()) : null);
             exportVO.setUserId(vo.getUserId());
             exportVO.setUserId(vo.getUserId());
-            
+
             // 产品信息
             // 产品信息
-            exportVO.setProductName(vo.getProductName());
+            exportVO.setProductName(StringUtils.isEmpty(vo.getProductName()) ? "产品被删除" : vo.getProductName());
             exportVO.setBarCode(vo.getBarCode());
             exportVO.setBarCode(vo.getBarCode());
             exportVO.setProductSpec(StringUtils.isEmpty(vo.getProductSpec()) ? "默认" : vo.getProductSpec());
             exportVO.setProductSpec(StringUtils.isEmpty(vo.getProductSpec()) ? "默认" : vo.getProductSpec());
             exportVO.setTotalNum(vo.getTotalNum());
             exportVO.setTotalNum(vo.getTotalNum());
             exportVO.setPrice(vo.getTotalPrice()); // 产品价格使用订单总价
             exportVO.setPrice(vo.getTotalPrice()); // 产品价格使用订单总价
-            exportVO.setCost(vo.getCost());
+            exportVO.setCost(vo.getCost() != null ? vo.getCost() : BigDecimal.ZERO);
             exportVO.setFPrice(vo.getCost() != null ? vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())) : BigDecimal.ZERO); // 结算价,合并订单暂无此字段
             exportVO.setFPrice(vo.getCost() != null ? vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())) : BigDecimal.ZERO); // 结算价,合并订单暂无此字段
             exportVO.setPayPostage(vo.getPayDelivery());
             exportVO.setPayPostage(vo.getPayDelivery());
-            exportVO.setCateName(vo.getCateName());
+            exportVO.setCateName(StringUtils.isEmpty(vo.getCateName()) ? "产品被删除" : vo.getCateName());
             // 收货信息
             // 收货信息
             exportVO.setRealName(vo.getRealName());
             exportVO.setRealName(vo.getRealName());
             if (isPlainText) {
             if (isPlainText) {
@@ -258,34 +306,35 @@ public class OrderController extends BaseController
                 exportVO.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
                 exportVO.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
                 exportVO.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                 exportVO.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             }
             }
-            
+
             // 时间信息
             // 时间信息
             exportVO.setCreateTime(vo.getCreateTime());
             exportVO.setCreateTime(vo.getCreateTime());
             exportVO.setPayTime(vo.getPayTime());
             exportVO.setPayTime(vo.getPayTime());
-            
+            exportVO.setHfshh(vo.getHfshh());
+
             // 物流信息
             // 物流信息
             exportVO.setDeliverySn(vo.getDeliveryCode()); // 快递公司编号,合并订单暂无此字段
             exportVO.setDeliverySn(vo.getDeliveryCode()); // 快递公司编号,合并订单暂无此字段
             exportVO.setDeliveryName(vo.getDeliveryName()); // 快递公司,合并订单暂无此字段
             exportVO.setDeliveryName(vo.getDeliveryName()); // 快递公司,合并订单暂无此字段
             exportVO.setDeliveryId(vo.getDeliveryId());
             exportVO.setDeliveryId(vo.getDeliveryId());
-            
+
             // 公司和销售信息
             // 公司和销售信息
             exportVO.setCompanyName(vo.getCompanyName());
             exportVO.setCompanyName(vo.getCompanyName());
             exportVO.setCompanyUserNickName(vo.getCompanyUserNickName());
             exportVO.setCompanyUserNickName(vo.getCompanyUserNickName());
-            
+
             // 套餐信息
             // 套餐信息
             exportVO.setPackageName(null); // 套餐名称,合并订单暂无此字段
             exportVO.setPackageName(null); // 套餐名称,合并订单暂无此字段
             exportVO.setGroupBarCode(null); // 组合码,合并订单暂无此字段
             exportVO.setGroupBarCode(null); // 组合码,合并订单暂无此字段
-            
+
             // 凭证信息
             // 凭证信息
             exportVO.setIsUpload(null); // 是否上传凭证,合并订单暂无此字段
             exportVO.setIsUpload(null); // 是否上传凭证,合并订单暂无此字段
             exportVO.setUploadTime(null); // 上传时间,合并订单暂无此字段
             exportVO.setUploadTime(null); // 上传时间,合并订单暂无此字段
-            
+
             // 档期信息
             // 档期信息
             exportVO.setScheduleName(null); // 归属档期,合并订单暂无此字段
             exportVO.setScheduleName(null); // 归属档期,合并订单暂无此字段
-            
+
             // 银行交易流水号
             // 银行交易流水号
             exportVO.setBankTransactionId(vo.getBankTransactionId());
             exportVO.setBankTransactionId(vo.getBankTransactionId());
-            
+
             // 金额信息
             // 金额信息
             exportVO.setTotalPrice(vo.getTotalPrice());
             exportVO.setTotalPrice(vo.getTotalPrice());
             exportVO.setPayPrice(vo.getPayPrice());
             exportVO.setPayPrice(vo.getPayPrice());
@@ -300,7 +349,7 @@ public class OrderController extends BaseController
                 vo.setFPrice(BigDecimal.ZERO);
                 vo.setFPrice(BigDecimal.ZERO);
                 vo.setBankTransactionId("");
                 vo.setBankTransactionId("");
             }
             }
-            
+
             return exportVO;
             return exportVO;
         }).collect(Collectors.toList());
         }).collect(Collectors.toList());
     }
     }

+ 136 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwAutoTagsController.java

@@ -0,0 +1,136 @@
+package com.fs.qw.controller;
+
+
+import ch.qos.logback.core.util.ContextUtil;
+import com.alibaba.fastjson.JSON;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwAutoTags;
+import com.fs.qw.param.QwAutoTagsParam;
+import com.fs.qw.param.QwAutoTagsRulesTags;
+import com.fs.qw.param.QwTagSearchParam;
+import com.fs.qw.service.IQwAutoTagsService;
+import com.fs.qw.service.IQwTagService;
+import com.fs.qw.vo.QwAutoTagsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * 自动打标签主Controller
+ *
+ * @author fs
+ * @date 2024-07-20
+ */
+@RestController
+@RequestMapping("/qw/autoTags")
+public class QwAutoTagsController extends BaseController
+{
+    @Autowired
+    private IQwAutoTagsService qwAutoTagsService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    @Autowired
+    private IQwTagService iQwTagService;
+    /**
+     * 查询自动打标签主列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwAutoTagsParam qwAutoTags)
+    {
+        startPage();
+        List<QwAutoTagsVO> list = qwAutoTagsService.selectQwAutoTagsListVO(qwAutoTags);
+        list.forEach(item->{
+
+            QwTagSearchParam param = new QwTagSearchParam();
+
+            Set<String> combinedTagsSet = new HashSet<>();
+            List<QwAutoTagsRulesTags> qwAutoTagsRulesTagsList = JSON.parseArray(item.getRulesTags(), QwAutoTagsRulesTags.class);
+            for (QwAutoTagsRulesTags rulesTags : qwAutoTagsRulesTagsList) {
+                List<String> tagsItem = rulesTags.getTags();
+
+                combinedTagsSet.addAll(tagsItem);
+            }
+
+            param.setTagIds(new ArrayList<>(combinedTagsSet));
+            item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+
+        });
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出自动打标签主列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:export')")
+    @Log(title = "自动打标签主", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwAutoTagsParam qwAutoTags)
+    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwAutoTags.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<QwAutoTagsVO> list = qwAutoTagsService.selectQwAutoTagsListVO(qwAutoTags);
+        ExcelUtil<QwAutoTagsVO> util = new ExcelUtil<QwAutoTagsVO>(QwAutoTagsVO.class);
+        return util.exportExcel(list, "自动打标签主数据");
+    }
+
+    /**
+     * 获取自动打标签主详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:query')")
+    @GetMapping(value = "/{id}")
+    public R getInfo(@PathVariable("id") Long id)
+    {
+        return qwAutoTagsService.selectQwAutoTagsByIdVO(id);
+    }
+
+    /**
+     * 新增自动打标签主
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:add')")
+    @Log(title = "自动打标签主", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwAutoTags qwAutoTags)
+    {
+        LoginUser loginUser = getLoginUser();
+        qwAutoTags.setCreateName(loginUser.getUser().getNickName());
+        qwAutoTags.setCreateTime(new Date());
+        return toAjax(qwAutoTagsService.insertQwAutoTags(qwAutoTags));
+    }
+
+    /**
+     * 修改自动打标签主
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:edit')")
+    @Log(title = "自动打标签主", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwAutoTags qwAutoTags)
+    {
+        return toAjax(qwAutoTagsService.updateQwAutoTags(qwAutoTags));
+    }
+
+    /**
+     * 删除自动打标签主
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:remove')")
+    @Log(title = "自动打标签主", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwAutoTagsService.deleteQwAutoTagsByIds(ids));
+    }
+}

+ 132 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwAutoTagsLogsController.java

@@ -0,0 +1,132 @@
+package com.fs.qw.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;
+import com.fs.common.utils.poi.ExcelUtil;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwAutoTagsLogs;
+import com.fs.qw.param.QwAutoTagsLogsParams;
+import com.fs.qw.param.QwTagSearchParam;
+import com.fs.qw.service.IQwAutoTagsLogsService;
+import com.fs.qw.service.IQwTagService;
+import com.fs.qw.vo.QwAutoTagsLogsVO;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 自动打标签的日志Controller
+ *
+ * @author fs
+ * @date 2024-07-20
+ */
+@RestController
+@RequestMapping("/qw/autoTagsLogs")
+public class QwAutoTagsLogsController extends BaseController
+{
+    @Autowired
+    private IQwAutoTagsLogsService qwAutoTagsLogsService;
+
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    @Autowired
+    private IQwTagService iQwTagService;
+
+    /**
+     * 查询自动打标签的日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:query')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwAutoTagsLogsParams params)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        params.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<QwAutoTagsLogsVO> list = qwAutoTagsLogsService.selectQwAutoTagsLogsListVO(params);
+        list.forEach(item->{
+
+            if (!Objects.equals(item.getEffectiveRules(), "[]") && item.getEffectiveRules()!=null) {
+                QwTagSearchParam param = new QwTagSearchParam();
+                Gson gson = new Gson();
+                List<String> tagIds = gson.fromJson(
+                        item.getEffectiveRules(),
+                        new TypeToken<List<String>>() {
+                        }.getType()
+                );
+
+                param.setTagIds(tagIds);
+
+                item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+            }
+        });
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出自动打标签的日志列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:export')")
+    @Log(title = "自动打标签的日志", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwAutoTagsLogs qwAutoTagsLogs)
+    {
+        List<QwAutoTagsLogs> list = qwAutoTagsLogsService.selectQwAutoTagsLogsList(qwAutoTagsLogs);
+        ExcelUtil<QwAutoTagsLogs> util = new ExcelUtil<QwAutoTagsLogs>(QwAutoTagsLogs.class);
+        return util.exportExcel(list, "自动打标签的日志数据");
+    }
+
+    /**
+     * 获取自动打标签的日志详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwAutoTagsLogsService.selectQwAutoTagsLogsById(id));
+    }
+
+    /**
+     * 新增自动打标签的日志
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:add')")
+    @Log(title = "自动打标签的日志", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwAutoTagsLogs qwAutoTagsLogs)
+    {
+        return toAjax(qwAutoTagsLogsService.insertQwAutoTagsLogs(qwAutoTagsLogs));
+    }
+
+    /**
+     * 修改自动打标签的日志
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:edit')")
+    @Log(title = "自动打标签的日志", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwAutoTagsLogs qwAutoTagsLogs)
+    {
+        return toAjax(qwAutoTagsLogsService.updateQwAutoTagsLogs(qwAutoTagsLogs));
+    }
+
+    /**
+     * 删除自动打标签的日志
+     */
+    @PreAuthorize("@ss.hasPermi('qw:autoTags:remove')")
+    @Log(title = "自动打标签的日志", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwAutoTagsLogsService.deleteQwAutoTagsLogsByIds(ids));
+    }
+}

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

@@ -95,7 +95,7 @@ public class QwCompanyController extends BaseController
      * 导出企微主体列表
      * 导出企微主体列表
      */
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:export')")
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:export')")
-    @Log(title = "企微主体", businessType = BusinessType.EXPORT)
+    @Log(title = "企微主体", businessType = BusinessType.EXPORT, isStoreLog = true)
     @GetMapping("/export")
     @GetMapping("/export")
     public AjaxResult export(QwCompany qwCompany)
     public AjaxResult export(QwCompany qwCompany)
     {
     {
@@ -118,7 +118,7 @@ public class QwCompanyController extends BaseController
      * 新增企微主体
      * 新增企微主体
      */
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:add')")
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:add')")
-    @Log(title = "企微主体", businessType = BusinessType.INSERT)
+    @Log(title = "企微主体", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping
     @PostMapping
     public AjaxResult add(@RequestBody QwCompany qwCompany)
     public AjaxResult add(@RequestBody QwCompany qwCompany)
     {
     {
@@ -133,7 +133,7 @@ public class QwCompanyController extends BaseController
      * 修改企微主体
      * 修改企微主体
      */
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:edit')")
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:edit')")
-    @Log(title = "企微主体", businessType = BusinessType.UPDATE)
+    @Log(title = "企微主体", businessType = BusinessType.UPDATE, isStoreLog = true)
     @PutMapping
     @PutMapping
     public AjaxResult edit(@RequestBody QwCompany qwCompany)
     public AjaxResult edit(@RequestBody QwCompany qwCompany)
     {
     {
@@ -189,7 +189,7 @@ public class QwCompanyController extends BaseController
      * 删除企微主体
      * 删除企微主体
      */
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:remove')")
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:remove')")
-    @Log(title = "企微主体", businessType = BusinessType.DELETE)
+    @Log(title = "企微主体", businessType = BusinessType.DELETE, isStoreLog = true)
 	@DeleteMapping("/{ids}")
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     public AjaxResult remove(@PathVariable Long[] ids)
     {
     {

+ 56 - 5
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java

@@ -2,13 +2,18 @@ package com.fs.qw.controller;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Objects;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.ServletUtils;
 import com.fs.qw.param.QwExternalContactParam;
 import com.fs.qw.param.QwExternalContactParam;
 import com.fs.qw.param.QwTagSearchParam;
 import com.fs.qw.param.QwTagSearchParam;
+import com.fs.qw.param.ResignedTransferParam;
+import com.fs.qw.param.TransferParam;
+import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwTagService;
 import com.fs.qw.service.IQwTagService;
 import com.fs.qw.vo.QwExternalContactUnionIdExportVO;
 import com.fs.qw.vo.QwExternalContactUnionIdExportVO;
@@ -16,6 +21,8 @@ import com.fs.qw.vo.QwExternalContactVO;
 import com.google.gson.Gson;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -93,10 +100,6 @@ public class QwExternalContactController extends BaseController
     @GetMapping("/export")
     @GetMapping("/export")
     public AjaxResult export(QwExternalContactParam qwExternalContact)
     public AjaxResult export(QwExternalContactParam qwExternalContact)
     {
     {
-        if (qwExternalContact.getCompanyId() == null) {
-            return AjaxResult.success();
-        }
-
         List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
         List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
         list.forEach(item->{
         list.forEach(item->{
 
 
@@ -174,11 +177,59 @@ public class QwExternalContactController extends BaseController
     {
     {
         return toAjax(qwExternalContactService.deleteQwExternalContactByIds(ids));
         return toAjax(qwExternalContactService.deleteQwExternalContactByIds(ids));
     }
     }
-
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:getUserInfo')")
     @GetMapping(value = "getUserInfo/{id}")
     @GetMapping(value = "getUserInfo/{id}")
     public R getUserInfo(@PathVariable("id") Long id)
     public R getUserInfo(@PathVariable("id") Long id)
     {
     {
         return R.ok().put("data",qwExternalContactInfoService.selectQwExternalContactInfoByExternalContactId(id));
         return R.ok().put("data",qwExternalContactInfoService.selectQwExternalContactInfoByExternalContactId(id));
     }
     }
 
 
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:addUnassigned')")
+    @Log(title = "同步待转接", businessType = BusinessType.INSERT)
+    @PostMapping("/addUnassigned")
+    public R addUnassigned(@RequestBody QwExternalContact qwExternalContact)
+    {
+        return qwExternalContactService.syncQwExternalContactUnassigned(qwExternalContact.getCorpId());
+    }
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:transfer')")
+    @Log(title = "企业微信客户", businessType = BusinessType.UPDATE)
+    @PutMapping("/resignedTransfer")
+    public R resignedTransfer(@RequestBody ResignedTransferParam param)
+    {
+        if (ObjectUtil.isNotEmpty(param.getQwUserName())&&ObjectUtil.isNotEmpty(param.getType())&&param.getType().equals("1")){
+            QwExternalContactParam qwExternalContact =new QwExternalContactParam();
+            qwExternalContact.setQwUserName(param.getQwUserName());
+
+//            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//            qwExternalContact.setCompanyId(loginUser.getCompany().getCompanyId());
+            List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
+            if (!CollectionUtils.isEmpty(list)){
+                List<Long> ids = list.stream().map(QwExternalContactVO::getId).collect(Collectors.toList());
+                param.setIds(ids);
+            }
+        }
+
+        return qwExternalContactService.resignedTransfer(param);
+    }
+    /**
+     * 在职转接 分配客户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:transfer')")
+    @Log(title = "企业微信客户", businessType = BusinessType.UPDATE)
+    @PutMapping("/transfer")
+    public R transfer(@RequestBody TransferParam param)
+    {
+        if (ObjectUtil.isNotEmpty(param.getQwUserName())&&ObjectUtil.isNotEmpty(param.getType())&&param.getType().equals("1")){
+            QwExternalContactParam qwExternalContact =new QwExternalContactParam();
+            qwExternalContact.setQwUserName(param.getQwUserName());
+//            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//            qwExternalContact.setCompanyId(loginUser.getCompany().getCompanyId());
+            List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
+            if (!CollectionUtils.isEmpty(list)){
+                List<Long> ids = list.stream().map(QwExternalContactVO::getId).collect(Collectors.toList());
+                param.setIds(ids);
+            }
+        }
+        return qwExternalContactService.transfer(param);
+    }
 }
 }

+ 155 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwGroupChatController.java

@@ -0,0 +1,155 @@
+package com.fs.qw.controller;
+
+import com.fs.common.core.controller.BaseController;
+//import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+//import com.fs.common.utils.ServletUtils;
+//import com.fs.company.service.impl.CompanyDeptServiceImpl;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.param.QwGroupChatParam;
+import com.fs.qw.service.IQwGroupChatService;
+//import com.fs.qw.service.IQwUserService;
+//import com.fs.qw.vo.QwGroupChatOptionsVO;
+import com.fs.qw.vo.QwGroupChatVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 客户群详情Controller
+ *
+ * @author fs
+ * @date 2024-06-20
+ */
+@RestController
+@RequestMapping("/qw/groupChat")
+public class QwGroupChatController extends BaseController
+{
+    @Autowired
+    private IQwGroupChatService qwGroupChatService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+//    @Autowired
+//    private CompanyDeptServiceImpl companyDeptService;
+//
+//
+//    @Autowired
+//    private IQwUserService iQwUserService;
+
+    /**
+     * 查询客户群详情列表总后台查看
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupChat:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwGroupChatParam qwGroupChat)
+    {
+        startPage();
+        List<QwGroupChatVO> list = qwGroupChatService.selectQwGroupChatListAdmin(qwGroupChat);
+        return getDataTable(list);
+    }
+
+//    /**
+//     * 查询我的部门客户群详情列表
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:groupChat:deptList')")
+//    @GetMapping("/deptList")
+//    public TableDataInfo deptList(QwGroupChatParam qwGroupChat)
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//
+//        List<Long> combinedList = new ArrayList<>();
+//        //本部门
+//        Long deptId = loginUser.getUser().getDeptId();
+//        if (deptId!=null){
+//            combinedList.add(deptId);
+//        }
+//        //本部门的下级部门
+//        List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
+//        if (!deptList.isEmpty()){
+//            combinedList.addAll(deptList);
+//        }
+//        qwGroupChat.setCuDeptIdList(combinedList);
+//        qwGroupChat.setUserType(loginUser.getUser().getUserType());
+//
+//
+//        startPage();
+//        List<QwGroupChatVO> list = qwGroupChatService.selectQwGroupChatList(qwGroupChat);
+//        return getDataTable(list);
+//    }
+
+
+//    /**
+//     * 我的-查询客户群详情列表
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:groupChat:myList')")
+//    @GetMapping("/myList")
+//    public TableDataInfo myList(QwGroupChatParam qwGroupChat)
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwGroupChat.setCompanyId(loginUser.getCompany().getCompanyId());
+//        qwGroupChat.setCompanyUserId(loginUser.getUser().getUserId());
+//        startPage();
+//        List<QwGroupChatVO> list = qwGroupChatService.selectQwGroupChatList(qwGroupChat);
+//        return getDataTable(list);
+//    }
+
+
+//    /**
+//     * 获取客户群详情详细信息
+//     */
+////    @PreAuthorize("@ss.hasPermi('qw:groupChat:query')")
+//    @GetMapping(value = "/{chatId}")
+//    public AjaxResult getInfo(@PathVariable("chatId") String chatId)
+//    {
+//        return AjaxResult.success(qwGroupChatService.selectQwGroupChatByChatId(chatId));
+//    }
+
+    /**
+     *  同步 插入客户群信息
+     */
+    @GetMapping("/cogradientGroupChat/{corpId}")
+    public R cogradientGroupChat(@PathVariable("corpId") String corpId) throws Exception {
+
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        Long companyId = loginUser.getCompany().getCompanyId();
+        List<String> qwUserIdList=new ArrayList<>();
+        return qwGroupChatService.cogradientGroupChat(corpId,qwUserIdList);
+    }
+
+//    /**
+//     *  同步 我的客户群信息
+//     */
+//    @GetMapping("/cogradientMyGroupChat/{corpId}")
+//    public R cogradientMyGroupChat(@PathVariable("corpId") String corpId) throws Exception {
+//
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//
+//        List<String> qwUserIdList = iQwUserService.selectQwUserListByCompanyUserId(loginUser.getUser().getUserId(), corpId);
+//
+//        return qwGroupChatService.cogradientGroupChat(corpId,qwUserIdList);
+//    }
+
+//    @GetMapping("/allList/{corpId}")
+//    public AjaxResult allList(@PathVariable("corpId") String corpId)
+//    {
+////        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+////        Long companyId = loginUser.getCompany().getCompanyId();
+//        List<QwGroupChatOptionsVO> list = qwGroupChatService.selectGroupChatOptionsVOList(corpId);
+//        return AjaxResult.success(list);
+//    }
+//    @GetMapping("/listAll")
+//    public AjaxResult listAll(String qwUserIds, String corpId, String sopId){
+//        List<QwGroupChatOptionsVO> list = qwGroupChatService.listAllByQwUserList(qwUserIds, corpId, sopId);
+//        return AjaxResult.success(list);
+//    }
+}

+ 71 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwGroupChatUserController.java

@@ -0,0 +1,71 @@
+package com.fs.qw.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwGroupChatUser;
+import com.fs.qw.param.QwGroupChatUserDataType;
+import com.fs.qw.param.QwInsertGroupChatUserParam;
+import com.fs.qw.service.IQwGroupChatUserService;
+import com.fs.qw.vo.QwGroupChatUserVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 客户群成员列Controller
+ *
+ * @author fs
+ * @date 2024-06-20
+ */
+@RestController
+@RequestMapping("/qw/group_chat_user")
+public class QwGroupChatUserController extends BaseController
+{
+    @Autowired
+    private IQwGroupChatUserService qwGroupChatUserService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    /**
+     * 查询客户群成员列列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(QwGroupChatUser qwGroupChatUser)
+    {
+        startPage();
+        List<QwGroupChatUserVO> list = qwGroupChatUserService.selectQwGroupChatUserList(qwGroupChatUser);
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 获取客户群成员列详细信息
+     */
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwGroupChatUserService.selectQwGroupChatUserById(id));
+    }
+
+
+    /** 获取日星月的入群退群统计 */
+    @GetMapping("/getDataWeekMonthCount")
+    public R getDataWeekMonthCount(QwGroupChatUserDataType qwGroupChatUserDataType){
+
+        return qwGroupChatUserService.getDataWeekMonthCount(qwGroupChatUserDataType);
+    }
+
+    /**
+     *  同步 插入某个客户群信息
+     */
+    @PutMapping("/cogradientGroupChatUser")
+    public R cogradientGroupChatUser(@RequestBody QwInsertGroupChatUserParam param) {
+
+        return qwGroupChatUserService.cogradientGroupChatUser(param.getCorpId(), param.getChatId());
+    }
+}

+ 160 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwGroupMsgController.java

@@ -0,0 +1,160 @@
+package com.fs.qw.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwGroupMsg;
+import com.fs.qw.param.QwGroupMsgDetailsParam;
+import com.fs.qw.service.IQwGroupMsgService;
+import com.fs.qw.vo.QwGroupMsgDetailsVO;
+import com.fs.qw.vo.QwGroupMsgVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 客户群发记录主Controller
+ *
+ * @author fs
+ * @date 2024-06-20
+ */
+@RestController
+@RequestMapping("/qw/groupMsg")
+public class QwGroupMsgController extends BaseController
+{
+    @Autowired
+    private IQwGroupMsgService qwGroupMsgService;
+
+//    @Autowired
+//    private TokenService tokenService;
+    /**
+     * 查询客户群发记录主列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsg:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwGroupMsg qwGroupMsg)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwGroupMsg.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwGroupMsgVO> list = qwGroupMsgService.selectQwGroupMsgListVO(qwGroupMsg);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询客户群发记录主列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsg:myList')")
+    @GetMapping("/myList")
+    public TableDataInfo myList(QwGroupMsg qwGroupMsg)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwGroupMsg.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwGroupMsgVO> list = qwGroupMsgService.selectQwGroupMsgListMyVO(qwGroupMsg);
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 导出客户群发记录主列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsg:export')")
+    @Log(title = "客户群发记录主", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwGroupMsg qwGroupMsg)
+    {
+        List<QwGroupMsgVO> list = qwGroupMsgService.selectQwGroupMsgListVO(qwGroupMsg);
+        ExcelUtil<QwGroupMsgVO> util = new ExcelUtil<QwGroupMsgVO>(QwGroupMsgVO.class);
+        return util.exportExcel(list, "客户群发记录主数据");
+    }
+
+    /**
+     * 获取客户群发记录主详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsg:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwGroupMsgService.selectQwGroupMsgByIdVO(id));
+    }
+
+//    /**
+//     * 新增客户群发/客户群群发记录
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:groupMsg:add')")
+//    @Log(title = "客户群发记录", businessType = BusinessType.INSERT)
+//    @PostMapping
+//    public R add(@RequestBody QwGroupMsgParam qwGroupMsgParam) throws Exception {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+////        Long companyId = loginUser.getCompany().getCompanyId();
+//        qwGroupMsgParam.setCreateName(loginUser.getUser().getNickName());
+//        return qwGroupMsgService.insertQwGroupMsg(qwGroupMsgParam);
+//    }
+
+    /** 统计数据详情,已发送,未发送,已接收,未接收等
+     *  groupMsgId 消息主表的主键
+     * */
+    @GetMapping("/getCountGroupMsgUser/{groupMsgId}")
+    public R getCountGroupMsgUser(@PathVariable("groupMsgId") Long groupMsgId){
+
+        return qwGroupMsgService.getCountGroupMsgUser(groupMsgId);
+    }
+
+    /** 统计数据详情,已发送群主,送达群聊,未发送群主,未送达群聊等 */
+    @GetMapping("/getCountGroupMsgBaseUser/{groupMsgId}")
+    public R getCountGroupMsgBaseUser(@PathVariable("groupMsgId") Long groupMsgId){
+
+        return qwGroupMsgService.getCountGroupMsgBaseUser(groupMsgId);
+    }
+
+    /**
+     * 客户群发 发送/接收的数据详情
+     */
+    @GetMapping("/getCountGroupMsgUserDetails")
+    public TableDataInfo getCountGroupMsgUserDetails(QwGroupMsgDetailsParam qwGroupMsgDetailsParam){
+
+        startPage();
+        List<QwGroupMsgDetailsVO> list = qwGroupMsgService.getCountGroupMsgUserDetails(qwGroupMsgDetailsParam);
+        return getDataTable(list);
+    }
+
+    /** 提醒成员群发 */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsg:remindGroupMsg')")
+    @GetMapping("/remindGroupMsg")
+    public R remindGroupMsg(QwGroupMsgDetailsParam qwGroupMsgDetailsParam){
+
+
+        return  qwGroupMsgService.remindGroupMsg(qwGroupMsgDetailsParam);
+    }
+
+//    /**
+//     * 修改客户群发记录主
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:groupMsg:edit')")
+//    @Log(title = "客户群发记录主", businessType = BusinessType.UPDATE)
+//    @PutMapping
+//    public AjaxResult edit(@RequestBody QwGroupMsg qwGroupMsg)
+//    {
+//        return toAjax(qwGroupMsgService.updateQwGroupMsg(qwGroupMsg));
+//    }
+
+    /**
+     * 删除客户群发记录主
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsg:remove')")
+    @Log(title = "客户群发记录主", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwGroupMsgService.deleteQwGroupMsgByIds(ids));
+    }
+}

+ 118 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwGroupMsgUserController.java

@@ -0,0 +1,118 @@
+package com.fs.qw.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwGroupMsgUser;
+import com.fs.qw.service.IQwGroupMsgUserService;
+import com.fs.qw.vo.QwGroupMsgDetailsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 群发成员发送任务及执行结果反馈记录Controller
+ *
+ * @author fs
+ * @date 2024-06-20
+ */
+@RestController
+@RequestMapping("/qw/groupMsgUser")
+public class QwGroupMsgUserController extends BaseController
+{
+    @Autowired
+    private IQwGroupMsgUserService qwGroupMsgUserService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    /**
+     * 查询群发成员发送任务及执行结果反馈记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsgUser:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwGroupMsgUser qwGroupMsgUser)
+    {
+        startPage();
+        List<QwGroupMsgUser> list = qwGroupMsgUserService.selectQwGroupMsgUserList(qwGroupMsgUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出群发成员发送任务及执行结果反馈记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsgUser:export')")
+    @Log(title = "群发成员发送任务及执行结果反馈记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwGroupMsgUser qwGroupMsgUser)
+    {
+        List<QwGroupMsgUser> list = qwGroupMsgUserService.selectQwGroupMsgUserList(qwGroupMsgUser);
+        ExcelUtil<QwGroupMsgUser> util = new ExcelUtil<QwGroupMsgUser>(QwGroupMsgUser.class);
+        return util.exportExcel(list, "群发成员发送任务及执行结果反馈记录数据");
+    }
+
+    /**
+     * 获取群发成员发送任务及执行结果反馈记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsgUser:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwGroupMsgUserService.selectQwGroupMsgUserById(id));
+    }
+
+    /**
+     * 新增群发成员发送任务及执行结果反馈记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsgUser:add')")
+    @Log(title = "群发成员发送任务及执行结果反馈记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwGroupMsgUser qwGroupMsgUser)
+    {
+        return toAjax(qwGroupMsgUserService.insertQwGroupMsgUser(qwGroupMsgUser));
+    }
+
+    /**
+     * 修改群发成员发送任务及执行结果反馈记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsgUser:edit')")
+    @Log(title = "群发成员发送任务及执行结果反馈记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwGroupMsgUser qwGroupMsgUser)
+    {
+        return toAjax(qwGroupMsgUserService.updateQwGroupMsgUser(qwGroupMsgUser));
+    }
+
+    /**
+     * 删除群发成员发送任务及执行结果反馈记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:groupMsgUser:remove')")
+    @Log(title = "群发成员发送任务及执行结果反馈记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwGroupMsgUserService.deleteQwGroupMsgUserByIds(ids));
+    }
+
+    /** 刷新/同步 客户群群发 结果 */
+    @PostMapping("/refreshResultsGroupMsgUser")
+    public R refreshResultsGroupMsgUser(@RequestBody List<QwGroupMsgDetailsVO> list){
+
+
+        return  qwGroupMsgUserService.refreshResultsGroupMsgUser(list);
+    }
+
+    /** 刷新/同步 客户群发 结果 */
+    @PostMapping("/refreshResultsMsgUser")
+    public R refreshResultsMsgUser(@RequestBody List<QwGroupMsgDetailsVO> list,String corpId){
+        QwGroupMsgDetailsVO qwGroupMsgDetailsVO = list.get(0);
+        return  qwGroupMsgUserService.refreshResultsMsgUser(list,qwGroupMsgDetailsVO.getCorpId() );
+    }
+}

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

@@ -10,6 +10,8 @@ import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.fastGpt.domain.FastGptPushTokenTotal;
 import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.fastGpt.param.FastGptPushTokenDeptTotalParam;
+import com.fs.fastGpt.vo.FastGptPushTokenDeptTotalVO;
 import com.fs.qw.domain.QwPushCount;
 import com.fs.qw.domain.QwPushCount;
 import com.fs.qw.service.IQwPushCountService;
 import com.fs.qw.service.IQwPushCountService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -199,6 +201,47 @@ public class QwPushCountController extends BaseController {
 
 
 
 
 
 
+        list.add(sumTotal); // 将合计行添加到列表末尾
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
+    }
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:tokenListDept')")
+    @GetMapping("/tokenListDept")
+    public TableDataInfo tokenListDept(FastGptPushTokenDeptTotalParam pushTokenInfo) {
+        List<FastGptPushTokenDeptTotalVO> list = qwPushCountService.selectFastGptPushTokenTotalDeptList(pushTokenInfo);
+
+
+        // 计算总和
+        FastGptPushTokenDeptTotalVO sumTotal = new FastGptPushTokenDeptTotalVO();
+        sumTotal.setCompanyName("合计"); // 假设有一个字段用于显示“合计”标签,具体字段名根据实际情况替换
+        Long sum = list.stream().mapToLong(FastGptPushTokenDeptTotalVO::getCount).sum(); // 假设有一个数字字段需要求和,具体字段名根据实际情况替换
+        sumTotal.setCount(sum); // 设置合计值,具体字段名根据实际情况替换
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
         list.add(sumTotal); // 将合计行添加到列表末尾
         list.add(sumTotal); // 将合计行添加到列表末尾
 
 
         // 构造返回结果
         // 构造返回结果

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

@@ -58,6 +58,15 @@ public class QwSopTempController extends BaseController
     {
     {
 
 
 //        List<QwSopTemp> list = qwSopTempService.selectQwSopTempList(qwSopTemp);
 //        List<QwSopTemp> list = qwSopTempService.selectQwSopTempList(qwSopTemp);
+
+        if(qwSopTemp.getPageNum() == null) {
+            qwSopTemp.setPageNum(1);
+        }
+        if(qwSopTemp.getPageSize() == null) {
+            qwSopTemp.setPageSize(10);
+        }
+
+        PageHelper.startPage(qwSopTemp.getPageNum(), qwSopTemp.getPageSize());
         List<QwSopTemp> list = qwSopTempService.selectQwSopTempListNew(qwSopTemp);
         List<QwSopTemp> list = qwSopTempService.selectQwSopTempListNew(qwSopTemp);
         // 收集所有需要查询的用户ID
         // 收集所有需要查询的用户ID
         Set<Long> userIds = list.stream()
         Set<Long> userIds = list.stream()
@@ -87,14 +96,6 @@ public class QwSopTempController extends BaseController
             });
             });
         }
         }
 
 
-        if(qwSopTemp.getPageNum() == null) {
-            qwSopTemp.setPageNum(1);
-        }
-        if(qwSopTemp.getPageSize() == null) {
-            qwSopTemp.setPageSize(10);
-        }
-
-        PageHelper.startPage(qwSopTemp.getPageNum(), qwSopTemp.getPageSize());
 
 
         return R.ok().put("data",new PageInfo<>(list));
         return R.ok().put("data",new PageInfo<>(list));
     }
     }
@@ -289,11 +290,11 @@ public class QwSopTempController extends BaseController
         if (!loginUser.isAdmin()) {
         if (!loginUser.isAdmin()) {
             return AjaxResult.error("只有总后台管理员才能创建总后台模板");
             return AjaxResult.error("只有总后台管理员才能创建总后台模板");
         }
         }
-        
+
         // 设置为总后台模板
         // 设置为总后台模板
         qwSopTemp.setTemplateType(1);
         qwSopTemp.setTemplateType(1);
         qwSopTemp.setCreateBy(loginUser.getUser().getUserId().toString());
         qwSopTemp.setCreateBy(loginUser.getUser().getUserId().toString());
-        
+
         int i = qwSopTempService.addNew(qwSopTemp);
         int i = qwSopTempService.addNew(qwSopTemp);
         if(qwSopTemp.getSendType() == 11){
         if(qwSopTemp.getSendType() == 11){
             //筛选选课程数据
             //筛选选课程数据

+ 83 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwTagGroupController.java

@@ -1,12 +1,19 @@
 package com.fs.qw.controller;
 package com.fs.qw.controller;
 
 
+import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.service.IQwTagGroupService;
 import com.fs.qw.service.IQwTagGroupService;
+import com.fs.qw.vo.QwTagGroupAddParam;
 import com.fs.qw.vo.QwTagGroupListVO;
 import com.fs.qw.vo.QwTagGroupListVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.util.List;
 import java.util.List;
@@ -50,4 +57,80 @@ public class QwTagGroupController extends BaseController
         List<QwTagGroupListVO> list = qwTagGroupService.selectQwTagGroupListVO(qwTagGroup);
         List<QwTagGroupListVO> list = qwTagGroupService.selectQwTagGroupListVO(qwTagGroup);
         return getDataTable(list);
         return getDataTable(list);
     }
     }
+
+    @PreAuthorize("@ss.hasPermi('qw:tagGroup:sync')")
+    @Log(title = "同步标签", businessType = BusinessType.INSERT)
+    @PostMapping("/syncTag/{corpId}")
+    public R syncTag(@PathVariable("corpId") String corpId)
+    {
+
+        return qwTagGroupService.syncQwTagGroup(corpId);
+    }
+    /**
+     * 查询企微客户标签组列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:tagGroup:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwTagGroup qwTagGroup)
+    {
+        startPage();
+
+        List<QwTagGroupListVO> list = qwTagGroupService.selectQwTagGroupListVO(qwTagGroup);
+        return getDataTable(list);
+    }
+    /**
+     * 获取企微客户标签组详细信息
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:tagGroup:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwTagGroupService.selectQwTagGroupByIdVO(id));
+    }
+    /**
+     * 删除企微客户标签组
+     */
+    @PreAuthorize("@ss.hasPermi('qw:tagGroup:remove')")
+    @Log(title = "企微客户标签组", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R remove(@PathVariable Long[] ids)
+    {
+
+        return qwTagGroupService.deleteQwTagGroupByIds(ids);
+    }
+    /**
+     * 新增企微客户标签组
+     */
+    @PreAuthorize("@ss.hasPermi('qw:tagGroup:add')")
+    @Log(title = "企微客户标签组", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwTagGroupAddParam qwTagGroup)
+    {
+
+        return toAjax(qwTagGroupService.insertQwTagGroupParam(qwTagGroup));
+    }
+    /**
+     * 修改企微客户标签组
+     */
+    @PreAuthorize("@ss.hasPermi('qw:tagGroup:edit')")
+    @Log(title = "企微客户标签组", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwTagGroupAddParam qwTagGroup)
+    {
+
+        return toAjax(qwTagGroupService.updateQwTagGroupParam(qwTagGroup));
+    }
+    /**
+     * 导出企微客户标签组列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:tagGroup:export')")
+    @Log(title = "企微客户标签组", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwTagGroup qwTagGroup)
+    {
+
+        List<QwTagGroup> list = qwTagGroupService.selectQwTagGroupList(qwTagGroup);
+        ExcelUtil<QwTagGroup> util = new ExcelUtil<QwTagGroup>(QwTagGroup.class);
+        return util.exportExcel(list, "企微客户标签组数据");
+    }
 }
 }

+ 713 - 83
fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java

@@ -1,32 +1,39 @@
 package com.fs.qw.controller;
 package com.fs.qw.controller;
 
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.constant.Constants;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
-import com.fs.common.utils.ServletUtils;
-import com.fs.company.domain.Company;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.exception.user.UserPasswordNotMatchException;
+import com.fs.common.utils.MessageUtils;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.impl.CompanyDeptServiceImpl;
+import com.fs.fastGpt.domain.FastGptRole;
+import com.fs.fastGpt.mapper.FastGptRoleMapper;
+import com.fs.framework.manager.AsyncManager;
+import com.fs.framework.manager.factory.AsyncFactory;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwExternalContact;
-import com.fs.qw.domain.QwExternalContactTransferCompanyAudit;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
-import com.fs.qw.param.QwFsUserParam;
-import com.fs.qw.param.QwUserBingParam;
-import com.fs.qw.param.QwUserListParam;
+import com.fs.qw.param.*;
 import com.fs.qw.service.IQwDeptService;
 import com.fs.qw.service.IQwDeptService;
 import com.fs.qw.service.IQwExternalContactTransferCompanyAuditService;
 import com.fs.qw.service.IQwExternalContactTransferCompanyAuditService;
 import com.fs.qw.service.IQwUserService;
 import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.QwOptionsVO;
 import com.fs.qw.vo.QwOptionsVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.qw.vo.QwUserVO;
+import com.fs.qw.vo.UpdateSendTypeVo;
 import com.fs.qwApi.domain.QwExternalContactAllListResult;
 import com.fs.qwApi.domain.QwExternalContactAllListResult;
 import com.fs.qwApi.domain.inner.ExternalContact;
 import com.fs.qwApi.domain.inner.ExternalContact;
 import com.fs.qwApi.domain.inner.ExternalContactInfo;
 import com.fs.qwApi.domain.inner.ExternalContactInfo;
@@ -34,14 +41,18 @@ import com.fs.qwApi.domain.inner.FollowInfo;
 import com.fs.qwApi.param.QwExternalListParam;
 import com.fs.qwApi.param.QwExternalListParam;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.voice.utils.StringUtil;
 import com.fs.voice.utils.StringUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import javax.annotation.Resource;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -77,26 +88,639 @@ public class QwUserController extends BaseController {
     @Autowired
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
     private QwExternalContactMapper qwExternalContactMapper;
 
 
-    @GetMapping("/getQwUserAll")
-    public AjaxResult getQwUserAll(){
-        return AjaxResult.success(qwUserService.getQwUserAll());
+    @Autowired
+    private CompanyDeptServiceImpl companyDeptService;
+
+    @Resource
+    private AuthenticationManager authenticationManager;
+    @Autowired
+    private FastGptRoleMapper fastGptRoleMapper;
+
+    /**
+     * 查询企微员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:staffList')")
+    @GetMapping("/staffList")
+    public TableDataInfo staffList(QwUserListParam qwUser) {
+
+        // 添加企微部门查询条件
+        Long deptId = qwUser.getDeptId();
+        if(deptId!=null && qwUser.getCorpId()!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            if (deptId!=null){
+                qwDeptIdList.add(deptId);
+            }
+            // 本部门的下级部门
+            List<Long> deptList = qwUserService.selectDeptByParentId(deptId,qwUser.getCorpId());
+            if (!deptList.isEmpty()){
+                qwDeptIdList.addAll(deptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
     }
     }
 
 
     /**
     /**
-     * 获取企微信息
-     * **/
-    @GetMapping("/getQwUserInfo")
-    public R getQwUserInfo(QwFsUserParam param){
-        return R.ok().put("data",qwUserService.getQwUserInfo(param));
+     * 查询企微员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:staffListPost')")
+    @GetMapping("/staffListPost")
+    public TableDataInfo staffListPost(QwUserListParam qwUser) {
+        // 添加企微部门查询条件
+        Long deptId = qwUser.getDeptId();
+        if(deptId!=null && qwUser.getCorpId()!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            qwDeptIdList.add(deptId);
+            // 本部门的下级部门
+            List<Long> deptList = qwUserService.selectDeptByParentId(deptId,qwUser.getCorpId());
+            if (!deptList.isEmpty()){
+                qwDeptIdList.addAll(deptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询我的企微员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:myStaffList')")
+    @GetMapping("/myStaffList")
+    public TableDataInfo myStaffList(QwUserListParam qwUser) {
+        startPage();
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 导出企微员工列表
+     * @param qwUser
+     * @return AjaxResult
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:export')")
+    @Log(title = "企微员工", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportStaff")
+    public AjaxResult export(QwUserListParam qwUser) {
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        ExcelUtil<QwUserVO> util = new ExcelUtil<QwUserVO>(QwUserVO.class);
+        return util.exportExcel(list, "企微员工数据");
+    }
+
+    /**
+     * 导出企微用户列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:export')")
+    @Log(title = "企微用户", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwUser qwUser) {
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        List<QwUser> list = qwUserService.selectQwUserList(qwUser);
+        ExcelUtil<QwUser> util = new ExcelUtil<QwUser>(QwUser.class);
+        return util.exportExcel(list, "企微用户数据");
+    }
+
+
+    /**
+     * 查询我的部门 企业微信员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:myDepartList')")
+    @GetMapping("/myDepartList")
+    public TableDataInfo myDepartList(QwUserListParam qwUser)
+    {
+
+        qwUser.setCompanyId(qwUser.getCompanyId());
+//        qwUser.setUserType(loginUser.getUser().getUserType());
+        List<Long> combinedList = new ArrayList<>();
+        //本部门
+        Long deptId = getLoginUser().getUser().getDeptId();
+        if (deptId!=null){
+            combinedList.add(deptId);
+        }
+        //本部门的下级部门
+        List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
+        if (!deptList.isEmpty()){
+            combinedList.addAll(deptList);
+        }
+
+        // 添加企微部门查询条件
+        Long qwDeptId = qwUser.getDeptId();
+        if(qwDeptId!=null && qwUser.getCorpId()!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            if (qwDeptId!=null){
+                qwDeptIdList.add(qwDeptId);
+            }
+            // 本部门的下级部门
+            List<Long> qwDeptList = qwUserService.selectDeptByParentId(qwDeptId,qwUser.getCorpId());
+            if (!qwDeptList.isEmpty()){
+                qwDeptIdList.addAll(qwDeptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+
+
+        qwUser.setCuDeptIdList(combinedList);
+//        qwUser.setUserType(loginUser.getUser().getUserType());
+
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwIpad")
+    public R loginQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.loginQwIpad(loginParam);
+    }
+
+
+    /**
+     * 查询部门下的 企业微信账号
+     */
+    @PostMapping("/getQwUserByDept")
+    public R getQwUserByDept(@RequestBody QwUserByDeptParam deptParam){
+        deptParam.setCompanyId(deptParam.getCompanyId());
+        return R.ok().put("data",qwUserService.getQwUserByDept(deptParam)) ;
+    }
+
+
+
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/getQwIpad")
+    @RepeatSubmit
+    public R getQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getQwIpad(loginParam);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/delQwIpad")
+    @RepeatSubmit
+    public R delQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.delQwIpad(loginParam);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/qrCodeStatus")
+    public R qrCodeStatus(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.qrCodeStatus(loginParam);
+    }
+    //输入验证码
+    @PostMapping("/qrCodeVerify")
+    public R qrCodeVerify(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.qrCodeVerify(loginParam);
+    }
+
+    @PostMapping("/outLoginQwIpad")
+    public R outLoginQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.outLoginQwIpad(loginParam);
+    }
+
+    @PostMapping("/twoCode")
+    public R twoCode(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getTwoCode(loginParam);
+    }
+    @PostMapping("/twoCodeStatus")
+    public R TwoCodeStatus(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getTwoCodeStatus(loginParam);
+    }
+
+    @PostMapping("/getQwIpadStatus")
+    public R getQwIpadStatus(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getLoginQwIpadStatus(loginParam);
+    }
+    /**
+     * 直接授权key
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:authAppKey')")
+    @PostMapping("/authAppKey")
+    public R authAppKey(@RequestBody QwUser param){
+        return qwUserService.authAppKey(param);
+    }
+
+    /**
+     * 输入授权key
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:authAppKey')")
+    @PostMapping("/handleInputAuthAppKey")
+    public R handleInputAuthAppKey(@RequestBody QwUser param){
+        return qwUserService.handleInputAuthAppKey(param);
+    }
+
+
+    /**
+     * 登录企业微信(发起登录)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwCode")
+    public R loginQwCode(@RequestBody QwLoginParam loginParam){
+        return qwUserService.loginQwCode(loginParam);
+    }
+
+    /**
+     * 登录请求-刷新获取二维码
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwCodeUrl")
+    public R loginQwCodeUrl(@RequestBody QwLoginParam loginParam){
+        return qwUserService.loginQwCodeUrl(loginParam);
+    }
+    /**
+     * 取redis里的登录二维码
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/getQwCodeUrl")
+    public R getQwCodeUrl(@RequestBody QwLoginParam loginParam) throws InterruptedException {
+        return qwUserService.getQwCodeUrl(loginParam);
+    }
+
+    /**
+     * 登录企业微信(传输验证信息)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwCodeMsg")
+    public R loginQwCodeMsg(@RequestBody QwLoginParam loginParam){
+        return qwUserService.loginQwCodeMsg(loginParam);
+    }
+
+    /**
+     * 退出企业微信(退出插件)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/logoutQwLogout")
+    public R logoutQwLogout(@RequestBody QwLoginParam loginParam){
+        return qwUserService.logoutQwLogout(loginParam);
+    }
+
+//    /**
+//     * 企业微信(修改登录状态)
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+//    @PostMapping("/modifyLoginQwStatus")
+//    public R modifyLoginQwStatus(@RequestBody QwLoginParam loginParam){
+//        return qwUserService.modifyLoginQwStatus(loginParam);
+//    }
+//
+    /**
+     * 查询企业微信登录状态
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/getLoginQwStatus")
+    public R getLoginQwStatus(@RequestBody QwLoginParam loginParam){
+        return qwUserService.getLoginQwStatus(loginParam);
+    }
+
+    @PutMapping
+    public AjaxResult updateUser(@RequestBody QwUser qwUser){
+        return toAjax(qwUserService.updateQwUser(qwUser));
+    }
+
+    /**
+     * 自动发课启用禁用
+     * @param qwUser
+     * @return
+     */
+    @PostMapping("/updateIsAuto")
+    @PreAuthorize("@ss.hasPermi('qw:user:isauto')")
+    public AjaxResult updateIsAuto(@RequestBody QwUser qwUser){
+        return toAjax(qwUserService.updateQwUser(qwUser));
+    }
+
+    /**
+     * 企业微信员工账号 绑定 云主机
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:loginIp')")
+    @Log(title = "绑定 云主机", businessType = BusinessType.INSERT)
+    @GetMapping("/qwBindCloudHost/{appKey}")
+    public R qwBindCloudHost(@PathVariable("appKey") String appKey){
+        return qwUserService.qwBindCloudHost(appKey);
+    }
+
+    /**
+     * 获取云主机的账密
+     *
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:cloudAP')")
+    @PostMapping("/selectCloudAP")
+    public R selectCloudAP(@RequestBody QwCloudAPParam param) throws Exception {
+        return qwUserService.selectCloudAP(param);
+    }
+
+    /**
+     * 根据销售账号密码 获取 他的所有企业微信账号以及云主机和账号密码
+     */
+    @PostMapping("/selectCloudByCompany")
+    public R selectCloudByCompany(@RequestBody QwCloudIPByCompanyParam param) throws Exception {
+
+        // 用户验证
+        Authentication authentication = null;
+        try
+        {
+            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
+            authentication = authenticationManager
+                    .authenticate(new UsernamePasswordAuthenticationToken(param.getCompanyAdmin(), param.getCompanyPassWord()));
+        }
+        catch (Exception e)
+        {
+            if (e instanceof BadCredentialsException)
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(param.getCompanyAdmin(), Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
+                throw new UserPasswordNotMatchException();
+            }
+            else
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(param.getCompanyAdmin(), Constants.LOGIN_FAIL, e.getMessage()));
+                throw new ServiceException(e.getMessage());
+            }
+        }
+        LoginUser loginUser=(LoginUser) authentication.getPrincipal();
+
+        return qwUserService.selectCloudByCompany(loginUser.getUser().getCompanyId(),loginUser.getUser().getUserId());
+    }
+
+
+    /**
+     * 企业微信员工账号 绑定 云主机
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:bindIp')")
+    @GetMapping("/qwBindCloudHostByIp/{appKey}/{IP}")
+    public R qwBindCloudHostByIp(@PathVariable("appKey") String appKey,@PathVariable("IP") String IP){
+        return qwUserService.qwBindCloudHostByIp(appKey,IP);
+    }
+
+    /**
+     * 企业微信员工账号 解除绑定 云主机
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:loginIpOut')")
+    @Log(title = "解除绑定 云主机", businessType = BusinessType.UPDATE)
+    @GetMapping("/qwUnbindCloudHost/{appKey}")
+    public R qwUnbindCloudHost(@PathVariable("appKey") String appKey){
+        return qwUserService.qwUnbindCloudHost(appKey);
+    }
+
+    /**
+     * 根据销售名称模糊查询
+     * @param qwUserName  名称
+     * @return  list
+     */
+    @GetMapping("/getQwUserListLikeName")
+    public R getQwUserListLikeName(@RequestParam(required = false) String qwUserName,
+                                   @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                   @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        Map<String,Object> params = new HashMap<>();
+        params.put("qwUserName", qwUserName);
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<QwOptionsVO> qwUserList = companyUserService.selectQwUserListLikeName(params);
+        return R.ok().put("data", new PageInfo<>(qwUserList));
+    }
+
+    /**
+     * 查询企微用户列表
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwUserListParam qwUser)
+    {
+        startPage();
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        if (ObjectUtil.isNotEmpty(qwUser.getIsRemark())&&qwUser.getIsRemark().equals("1")){
+            qwUser.setCompanyUserId(getLoginUser().getUser().getUserId());
+        }else if (ObjectUtil.isNotEmpty(qwUser.getIsRemark())&&qwUser.getIsRemark().equals("2")){
+            qwUser.setDeptId(getLoginUser().getDeptId());
+            qwUser.setCorpId(null);
+        }
+
+        List<QwUserVO> list = qwUserService.selectQwUserListVO(qwUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询企微用户列表
+     */
+
+    @GetMapping("/userList")
+    public TableDataInfo userList(QwUserListParam qwUser)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+
+        List<QwUserVO> list = qwUserService.selectAllQwUserListVO(qwUser);
+        return getDataTable(list);
+    }
+    //    /**
+//     * 查询我的企微用户列表
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:myList')")
+//    @GetMapping("/myList")
+//    public TableDataInfo myList(QwUserParam qwUser)
+//    {
+//        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+//        qwUser.setCompanyUserId(loginUser.getCompany().getUserId());
+//        List<QwUserVO> list = qwUserService.selectQwUserListVO(qwUser);
+//        return getDataTable(list);
+//    }
+    @GetMapping("/getQwAllUserList")
+    public R getQwAllUserList(@RequestParam String corpId, @RequestParam Long companyId)
+    {
+        List<QwUserVO> list = companyUserService.selectCompanyQwUserList(corpId, companyId);
+        return  R.ok().put("data",list);
+    }
+    /**
+     * 查询企微用户列表-下拉框
+     */
+    @GetMapping("/qwList")
+    public TableDataInfo getQwList(QwUser qwUser)
+    {
+        startPage();
+
+        if(ObjectUtil.notEqual(qwUser.getDisableCompanyId(),1)){
+            qwUser.setCompanyId(qwUser.getCompanyId());
+        }
+        List<QwUser> list = qwUserService.selectQwUserList(qwUser);
+        return getDataTable(list);
+    }
+    /**
+     * 查询企微用户列表-下拉框
+     */
+    @PostMapping("/qwCompanyList")
+    public TableDataInfo qwCompanyList(@RequestBody QwUser qwUser)
+    {
+        startPage();
+        List<QwUser> list = qwUserService.selectQwUserList(qwUser);
+        return getDataTable(list);
+    }
+    /**
+     * 查询企微员工列表-用于员工管理绑定
+     */
+    @GetMapping("/getQwUserList")
+    public R getQwUserList()
+    {
+        QwUserParam qwUser = new QwUserParam();
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        qwUser.setCorpId(strings);
+        if (strings==null||strings.size()==0){
+            return  R.ok().put("data",null);
+        }
+        qwUser.setIsDel(0);
+        List<QwUserVO> list = qwUserService.selectQwUserListBindVO(qwUser);
+        return  R.ok().put("data",list);
+    }
+
+    @GetMapping("/getMyQwUserList")
+    public R getMyQwUserList()
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwUserListOptionsVOByCompanyUserId(getLoginUser().getUser().getUserId());
+        return  R.ok().put("data",list);
+    }
+    @GetMapping("/getMyQwCompanyList/{companyId}")
+    public R getMyQwCompanyList(@PathVariable Long companyId)
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOByCompanyId(companyId);
+        return  R.ok().put("data",list);
     }
     }
 
 
-   @GetMapping("/getMyQwCompanyList")
-    public R getMyQwCompanyList()
+    @GetMapping("/getMyQwCompanyListAll")
+    public R getMyQwCompanyListAll()
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOAll();
+        return  R.ok().put("data",list);
+    }
+
+
+    @GetMapping("/getQwCompanyList")
+    public R getQwCompanyList()
     {
     {
         List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOAll();
         List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOAll();
         return  R.ok().put("data",list);
         return  R.ok().put("data",list);
     }
     }
 
 
+    /**
+     * 获取企微用户详细信息
+     */
+
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwUserService.selectQwUserVOById(id));
+    }
+    /**
+     * 批量查询 企微用户详细信息
+     */
+    @GetMapping(value = "/getInfo/{ids}")
+    public AjaxResult getInfoByIds(@PathVariable("ids") Long[] ids)
+    {
+        return AjaxResult.success(qwUserService.selectQwUserVOByIds(ids));
+    }
+
+
+//    /**
+//     * 新增企微用户
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:add')")
+//    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+//    @PostMapping
+//    public R add()
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//
+//        return R.ok(qwUserService.syncQwUser(loginUser.getCompany().getCompanyId()));
+//    }
+
+
+
+    /**
+     * 同步企微用户
+     */
+    @RepeatSubmit
+    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
+    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+    @PostMapping("sync/{corpId}")
+    public R sync(@PathVariable String corpId)
+    {
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        for (String string : strings) {
+
+            if (string.equals(corpId)){
+                qwUserService.syncQwUser(string);
+                qwDeptService.insertOrUpdateQwDept(string);
+                logger.info("同步完成");
+            }
+        }
+        return R.ok();
+    }
+    @RepeatSubmit
+    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
+    @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
+    @PostMapping("syncName/{corpId}")
+    public R syncName(@PathVariable String corpId)
+    {
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        for (String string : strings) {
+            if (string.equals(corpId)){
+                qwUserService.syncQwUserName(string);
+            }
+        }
+        return R.ok();
+    }
+    /**
+     * 绑定AI客服
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:bindAi')")
+    @Log(title = "企微用户", businessType = BusinessType.UPDATE)
+    @PutMapping("/bindAi")
+    @RepeatSubmit
+    public R edit(@RequestBody QwUserBindAi param)
+    {
+        QwUser qwUser=new QwUser();
+        qwUser.setId(param.getId());
+        qwUser.setFastGptRoleId(param.getFastGptRoleId());
+        FastGptRole role = fastGptRoleMapper.selectFastGptRoleByRoleId(param.getFastGptRoleId());
+
+        if (role.getBindCorpId()!=null){
+            if (role.getBindCorpId().equals(param.getCorpId())){
+                qwUserService.updateQwUser(qwUser);
+            }else {
+                return R.error("该角色已绑定其他企业");
+            }
+        }else {
+            int i = qwUserService.updateQwUser(qwUser);
+
+            FastGptRole fastGptRole=new FastGptRole();
+            fastGptRole.setRoleId(param.getFastGptRoleId());
+            fastGptRole.setBindCorpId(param.getCorpId());
+            fastGptRoleMapper.updateFastGptRole(fastGptRole);
+
+            if (i>0){
+                return R.ok();
+            }else {
+                return R.error("绑定失败");
+            }
+        }
+
+        return R.ok();
+    }
+
+    /**
+     * 解除应用绑定
+     */
+//    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptRole:relieve')")
+    @Log(title = "解除应用", businessType = BusinessType.UPDATE)
+    @GetMapping("/relieveFastGptRoleById/{id}")
+    public R relieveFastGptRoleById(@PathVariable("id") Long id)
+    {
+        return qwUserService.relieveFastGptRoleById(id);
+    }
+
     /**
     /**
      * 绑定企微用户
      * 绑定企微用户
      */
      */
@@ -188,6 +812,7 @@ public class QwUserController extends BaseController {
         }
         }
         return R.error("绑定失败");
         return R.error("绑定失败");
 
 
+
     }
     }
 
 
 
 
@@ -201,6 +826,52 @@ public class QwUserController extends BaseController {
         syncMyQwExternalContact(qu,null);
         syncMyQwExternalContact(qu,null);
     }
     }
 
 
+    /** 修改企微用户的欢迎语 */
+    @PostMapping("/weclomeQwUser")
+    public R weclomeQwUser(@RequestBody QwUser qwUser) throws Exception {
+
+
+        return  qwUserService.weclomeQwUser(qwUser);
+    }
+
+    /**
+     * 删除企微用户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:remove')")
+    @Log(title = "企微用户", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwUserService.deleteQwUserByIds(ids));
+    }
+
+
+    /**
+     * 获取企业微信用户列表
+     */
+    @GetMapping("/qwUserList/{corpId}")
+    public TableDataInfo qwUserList(@PathVariable String corpId)
+    {
+
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        Long companyId = loginUser.getCompany().getCompanyId();
+
+        List<QwOptionsVO> list = qwUserService.selectQwUserListOptionsVO(corpId);
+        return getDataTable(list);
+    }
+    @GetMapping("/getQwUserAll")
+    public AjaxResult getQwUserAll(){
+        return AjaxResult.success(qwUserService.getQwUserAll());
+    }
+
+    /**
+     * 获取企微信息
+     * **/
+    @GetMapping("/getQwUserInfo")
+    public R getQwUserInfo(QwFsUserParam param){
+        return R.ok().put("data",qwUserService.getQwUserInfo(param));
+    }
+
     public R  syncMyQwExternalContact(QwUser qwUser,String getNextCursor )  {
     public R  syncMyQwExternalContact(QwUser qwUser,String getNextCursor )  {
 
 
         String qwUserId = qwUser.getQwUserId();
         String qwUserId = qwUser.getQwUserId();
@@ -286,79 +957,38 @@ public class QwUserController extends BaseController {
         return R.ok();
         return R.ok();
     }
     }
 
 
-
-    /**
-     * 同步企微用户
-     */
-    @RepeatSubmit
-    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
-    @Log(title = "企微用户", businessType = BusinessType.INSERT)
-    @PostMapping("sync/{corpId}")
-    public R sync(@PathVariable("corpId") String corpId)
-    {
-
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByAll();
-        for (String string : strings) {
-
-            if (string.equals(corpId)){
-                qwUserService.syncQwUser(string);
-                qwDeptService.insertOrUpdateQwDept(string);
-                logger.info("同步完成");
-            }
-        }
-        return R.ok();
-    }
-
-    /**
-     * 获取企微用户详细信息
-     */
-
-    @GetMapping(value = "/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id)
-    {
-        return AjaxResult.success(qwUserService.selectQwUserVOById(id));
-    }
-    /**
-     * 批量查询 企微用户详细信息
-     */
-    @GetMapping(value = "/getInfo/{ids}")
-    public AjaxResult getInfoByIds(@PathVariable("ids") Long[] ids)
-    {
-        return AjaxResult.success(qwUserService.selectQwUserVOByIds(ids));
+    //    /**
+//     * 重启云主机
+//     * @return
+//     */
+//    @PutMapping("/restartHost")
+//    public R restartCloudHost(@RequestParam String serverIp) {
+//        return qwUserService.restartCloudHost(serverIp);
+//    }
+    @PostMapping("/updateSendType")
+    public R updateSendType(@RequestBody UpdateSendTypeVo vo) {
+        return qwUserService.updateSendType(vo);
     }
     }
 
 
-    @RepeatSubmit
-    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
-    @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
-    @PostMapping("syncName/{corpId}")
-    public R syncName(@PathVariable("corpId") String corpId)
-    {
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByAll();
-        for (String string : strings) {
-            if (string.equals(corpId)){
-                qwUserService.syncQwUserName(string);
-            }
-        }
+    @GetMapping("/changeVideoStatus")
+    public R changeVideoStatus(Long id) {
+        qwUserService.changeVideoStatus(id);
         return R.ok();
         return R.ok();
     }
     }
 
 
-
-    /**
-     * 查询企微用户列表
-     */
-
-    @GetMapping("/userList")
-    public TableDataInfo userList(QwUserListParam qwUser)
+    @GetMapping("/companyQwUserlist")
+    public TableDataInfo companyQwUserlist(@RequestParam Long companyId,
+                                           @RequestParam String corpId,
+                                           @RequestParam(required = false) String nickName)
     {
     {
         startPage();
         startPage();
-        List<QwUserVO> list = qwUserService.selectAllQwUserListVO(qwUser);
+        List<QwUserVO> list = qwUserService.selectQwUserListByCompanyIdAndCorpIdAndNickName(companyId, corpId, nickName);
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
-    @GetMapping("/getQwAllUserList")
-    public R getQwAllUserList(@RequestParam String corpId, @RequestParam Long companyId)
+    @GetMapping("/updateFastGptRoleStatusById/{id}")
+    public R updateFastGptRoleStatusById(@PathVariable Long id)
     {
     {
-        List<QwUserVO> list = companyUserService.selectCompanyQwUserList(corpId, companyId);
-        return  R.ok().put("data",list);
+        return qwUserService.updateQwUserFastGptRoleStatusById(id);
     }
     }
 }
 }

+ 328 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwUserVoiceLogController.java

@@ -0,0 +1,328 @@
+package com.fs.qw.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.service.ICompanyUserService;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwUserVoiceLog;
+import com.fs.qw.param.QwTagSearchParam;
+import com.fs.qw.service.IQwTagService;
+import com.fs.qw.service.IQwUserVoiceLogService;
+import com.fs.qw.vo.QwUserVoiceLogTotalVo;
+import com.fs.qw.vo.QwUserVoiceLogVo;
+import com.github.pagehelper.PageHelper;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 企微用户通话记录Controller
+ *
+ * @author fs
+ * @date 2025-05-08
+ */
+@RestController
+@RequestMapping("/qw/qwUserVoiceLog")
+public class QwUserVoiceLogController extends BaseController
+{
+    @Autowired
+    private IQwUserVoiceLogService qwUserVoiceLogService;
+
+//    @Autowired
+//    private TokenService tokenService;
+
+    @Autowired
+    private IQwTagService iQwTagService;
+
+    @Autowired
+    private ICompanyUserService userService;
+
+//    /**
+//     * 查询企微用户通话记录列表
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:list')")
+//    @GetMapping("/list")
+//    public TableDataInfo list(QwUserVoiceLogVo qwUserVoiceLog)
+//    {
+//        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+//        List<QwUserVoiceLogVo> list = qwUserVoiceLogService.selectQwUserVoiceLogList(qwUserVoiceLog);
+//        return getDataTable(list);
+//    }
+
+
+//    /**
+//     * 查询企微用户通话记录列表
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:list')")
+//    @PostMapping("/newList")
+//    public TableDataInfo newList(@RequestBody QwUserVoiceLogVo qwUserVoiceLog)
+//    {
+//        PageHelper.startPage(qwUserVoiceLog.getPageNum(), qwUserVoiceLog.getPageSize());
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+//        List<QwUserVoiceLogVo> list = qwUserVoiceLogService.selectQwUserVoiceLogList(qwUserVoiceLog);
+//        list.forEach(item->{
+//
+//            if (!Objects.equals(item.getQwExternalContact().getTagIds(), "[]") && item.getQwExternalContact().getTagIds()!=null) {
+//                QwTagSearchParam param = new QwTagSearchParam();
+//                Gson gson = new Gson();
+//                List<String> tagIds = gson.fromJson(
+//                        item.getQwExternalContact().getTagIds(),
+//                        new TypeToken<List<String>>() {
+//                        }.getType()
+//                );
+//                param.setTagIds(tagIds);
+//                item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+//            }
+//        });
+//        return getDataTable(list);
+//    }
+
+//    /**
+//     * 查询我的通话记录列表
+//     * @param qwUserVoiceLog
+//     * @return
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:myList')")
+//    @GetMapping("/myList")
+//    public TableDataInfo myList(QwUserVoiceLogVo qwUserVoiceLog)
+//    {
+//        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyUserId(loginUser.getUser().getUserId());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+//        List<QwUserVoiceLogVo> list = qwUserVoiceLogService.selectQwUserVoiceLogList(qwUserVoiceLog);
+//        return getDataTable(list);
+//    }
+
+//    /**
+//     * 导出我的通话记录
+//     */
+//
+//    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:myExport')")
+//    @Log(title = "我的通话记录", businessType = BusinessType.EXPORT)
+//    @GetMapping("/myExport")
+//    public AjaxResult myExport(QwUserVoiceLogVo qwUserVoiceLog)
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyUserId(loginUser.getUser().getUserId());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+//        List<QwUserVoiceLogVo> list = qwUserVoiceLogService.selectQwUserVoiceLogList(qwUserVoiceLog);
+//        list.forEach(m-> {
+//            m.setCompanyUserName(m.getCompanyUser().getUserName());
+//            m.setCompanyName(m.getCompany().getCompanyName());
+//            m.setExtName(m.getQwExternalContact().getName());
+//            m.setQwUserName(m.getQwUser().getQwUserName());
+//        });
+//        ExcelUtil<QwUserVoiceLogVo> util = new ExcelUtil<QwUserVoiceLogVo>(QwUserVoiceLogVo.class);
+//        return util.exportExcel(list, "企微用户通话记录数据");
+//    }
+
+
+    /**
+     * 统计查询企微用户通话记录
+     * @param qwUserVoiceLog
+     * @return
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:totalList')")
+    @GetMapping("/totalList")
+    public TableDataInfo totalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
+    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+        qwUserVoiceLog.setQwUserId(1L);
+        List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
+        list.forEach(item->{
+            if(item.getQwExternalContact() != null){
+                if (item.getQwExternalContact().getTagIds() != null && !Objects.equals(item.getQwExternalContact().getTagIds(), "[]")) {
+                    QwTagSearchParam param = new QwTagSearchParam();
+                    Gson gson = new Gson();
+                    List<String> tagIds = gson.fromJson(
+                            item.getQwExternalContact().getTagIds(),
+                            new TypeToken<List<String>>() {
+                            }.getType()
+                    );
+                    param.setTagIds(tagIds);
+                    item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+                }
+            }
+        });
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
+    }
+
+
+    @GetMapping("/sellTotalList")
+    public TableDataInfo sellTotalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
+    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:sellTotalExport')")
+    @Log(title = "企微用户通话记录统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/sellTotalExport")
+    public AjaxResult sellTotalExport(QwUserVoiceLogTotalVo qwUserVoiceLog)
+    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
+        list.forEach(m-> {
+            m.setQwUserName(m.getQwUser().getQwUserName());
+        });
+        ExcelUtil<QwUserVoiceLogTotalVo> util = new ExcelUtil<QwUserVoiceLogTotalVo>(QwUserVoiceLogTotalVo.class);
+        return util.exportExcel(list, "企微用户通话记录数据");
+    }
+
+//    /**
+//     * 导出统计企微用户通话记录
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:totalExport')")
+//    @Log(title = "企微用户通话记录统计", businessType = BusinessType.EXPORT)
+//    @GetMapping("/totalExport")
+//    public AjaxResult totalExport(QwUserVoiceLogTotalVo qwUserVoiceLog)
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
+//        qwUserVoiceLog.setQwUserId(1L);
+//        List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
+//        list.forEach(m-> {
+//            m.setQwUserName(m.getQwUser().getQwUserName());
+//        });
+//        ExcelUtil<QwUserVoiceLogTotalVo> util = new ExcelUtil<QwUserVoiceLogTotalVo>(QwUserVoiceLogTotalVo.class);
+//        return util.exportExcel(list, "企微用户通话记录数据");
+//    }
+
+
+    /**
+     * 导出企微用户通话记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:export')")
+    @Log(title = "企微用户通话记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwUserVoiceLogVo qwUserVoiceLog)
+    {
+        List<QwUserVoiceLogVo> list = qwUserVoiceLogService.selectQwUserVoiceLogList(qwUserVoiceLog);
+        list.forEach(m-> {
+            m.setCompanyUserName(m.getCompanyUser().getUserName());
+            m.setCompanyName(m.getCompany().getCompanyName());
+            m.setExtName(m.getQwExternalContact().getName());
+            m.setQwUserName(m.getQwUser().getQwUserName());
+        });
+        ExcelUtil<QwUserVoiceLogVo> util = new ExcelUtil<QwUserVoiceLogVo>(QwUserVoiceLogVo.class);
+        return util.exportExcel(list, "企微用户通话记录数据");
+    }
+
+    /**
+     * 获取企微用户通话记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwUserVoiceLogService.selectQwUserVoiceLogById(id));
+    }
+
+    /**
+     * 新增企微用户通话记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:add')")
+    @Log(title = "企微用户通话记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwUserVoiceLog qwUserVoiceLog)
+    {
+        return toAjax(qwUserVoiceLogService.insertQwUserVoiceLog(qwUserVoiceLog));
+    }
+
+    /**
+     * 修改企微用户通话记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:edit')")
+    @Log(title = "企微用户通话记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwUserVoiceLog qwUserVoiceLog)
+    {
+        return toAjax(qwUserVoiceLogService.updateQwUserVoiceLog(qwUserVoiceLog));
+    }
+
+    /**
+     * 删除企微用户通话记录
+     */
+    @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:remove')")
+    @Log(title = "企微用户通话记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwUserVoiceLogService.deleteQwUserVoiceLogByIds(ids));
+    }
+}

+ 241 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwWorkTaskNewController.java

@@ -0,0 +1,241 @@
+package com.fs.qw.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;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.service.impl.CompanyDeptServiceImpl;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskAllListVO;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import com.fs.qwApi.domain.QwExternalContactRemarkResult;
+import com.fs.qwApi.param.QwExternalContactRemarkParam;
+import com.fs.qwApi.service.QwApiService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 企微任务看板Controller
+ *
+ * @author fs
+ * @date 2025-03-25
+ */
+@RestController
+@RequestMapping("/qw/QwWorkTaskNew")
+public class QwWorkTaskNewController extends BaseController
+{
+    @Autowired
+    private IQwWorkTaskService qwWorkTaskService;
+//    @Autowired
+//    private TokenService tokenService;
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+
+    @Autowired
+    private CompanyDeptServiceImpl companyDeptService;
+
+    /**
+     * 查询企微任务看板列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwWorkTaskListParam qwWorkTask)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+//        qwWorkTask.setCompanyUserId(loginUser.getUser().getUserId());
+        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
+
+        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询企微任务部门催课看板列表
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:deptList')")
+//    @GetMapping("/deptList")
+//    public TableDataInfo deptList(QwWorkTaskListParam qwWorkTask)
+//    {
+//
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+//
+//        List<Long> combinedList = new ArrayList<>();
+//        //本部门
+//        Long deptId = loginUser.getUser().getDeptId();
+//        if (deptId!=null){
+//            combinedList.add(deptId);
+//        }
+//        //本部门的下级部门
+//        List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
+//        if (!deptList.isEmpty()){
+//            combinedList.addAll(deptList);
+//        }
+//
+//        qwWorkTask.setCuDeptIdList(combinedList);
+//        qwWorkTask.setUserType(loginUser.getUser().getUserType());
+//
+//
+//        startPage();
+//        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
+//        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+//            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+//        }
+//        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);
+    }
+    /**
+     * 导出企微任务看板列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:export')")
+    @Log(title = "企微任务看板", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwWorkTask qwWorkTask)
+    {
+        List<QwWorkTask> list = qwWorkTaskService.selectQwWorkTaskList(qwWorkTask);
+        ExcelUtil<QwWorkTask> util = new ExcelUtil<QwWorkTask>(QwWorkTask.class);
+        return util.exportExcel(list, "企微任务看板数据");
+    }
+
+    /**
+     * 获取企微任务看板详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwWorkTaskService.selectQwWorkTaskById(id));
+    }
+
+    /**
+     * 新增企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:add')")
+    @Log(title = "企微任务看板", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwWorkTask qwWorkTask)
+    {
+        return toAjax(qwWorkTaskService.insertQwWorkTask(qwWorkTask));
+    }
+    @Autowired
+    QwApiService qwApiService;
+    @Autowired
+    IQwExternalContactService qwExternalContactService;
+    @Autowired
+    QwExternalContactMapper qwExternalContactMapper;
+    /**
+     * 修改企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setStatus(1);
+        task.setTrackType(qwWorkTask.getTrackType());
+        task.setDescription(qwWorkTask.getDescription());
+        task.setUpdateTime(new Date());
+        if (task.getDescription()!=null&& !task.getDescription().isEmpty()){
+
+            QwExternalContact qwExternalContact = qwExternalContactService.selectQwExternalContactById(qwWorkTask.getExtId());
+            if (qwExternalContact!=null){
+                QwExternalContactRemarkParam param = new QwExternalContactRemarkParam();
+                param.setUserid(qwExternalContact.getUserId());
+                param.setExternal_userid(qwExternalContact.getExternalUserId());
+                param.setDescription(task.getDescription());
+
+                QwExternalContactRemarkResult qwExternalContactRemarkResult = qwApiService.externalcontactRemark(param, qwExternalContact.getCorpId());
+                logger.info("QwExternalContactRemarkResult206:" + qwExternalContactRemarkResult);
+                if (qwExternalContactRemarkResult.getErrcode() == 0) {
+                    QwExternalContact ext = new QwExternalContact();
+                    ext.setId(qwExternalContact.getId());
+                    ext.setDescription(task.getDescription());
+                    qwExternalContactMapper.updateQwExternalContact(ext);
+                }
+            }
+        }
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping("/edit2")
+    public AjaxResult edit2(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setStatus(1);
+        task.setUpdateTime(new Date());
+        task.setTrackType(2);
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping("/edit3")
+    public AjaxResult edit3(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setStatus(1);
+        task.setUpdateTime(new Date());
+        task.setTrackType(3);
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+    /**
+     * 删除企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:remove')")
+    @Log(title = "企微任务看板", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwWorkTaskService.deleteQwWorkTaskByIds(ids));
+    }
+}

+ 12 - 0
fs-admin/src/main/java/com/fs/qw/qwTask/qwTask.java

@@ -1,5 +1,6 @@
 package com.fs.qw.qwTask;
 package com.fs.qw.qwTask;
 
 
+import com.fs.course.service.IFinishCourseStatisticsSyncService;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserService;
 import com.fs.qw.domain.QwIpadServerLog;
 import com.fs.qw.domain.QwIpadServerLog;
@@ -86,6 +87,10 @@ public class qwTask {
     private IQwCompanyService iQwCompanyService;
     private IQwCompanyService iQwCompanyService;
 
 
 
 
+    @Autowired
+    private IFinishCourseStatisticsSyncService finishCourseStatisticsSyncService;
+
+
     //正在使用
     //正在使用
     public void qwExternalContact()
     public void qwExternalContact()
     {
     {
@@ -382,4 +387,11 @@ public class qwTask {
 
 
         }
         }
     }
     }
+
+    /**
+     * 特殊处理(木易完课统计数据)
+     */
+    public  void  syncMultiDimensionStatistics(){
+        finishCourseStatisticsSyncService.syncMultiDimensionStatistics();
+    }
 }
 }

+ 186 - 0
fs-admin/src/main/java/com/fs/user/controller/FsUserIntegralController.java

@@ -0,0 +1,186 @@
+package com.fs.user.controller;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.StringUtils;
+import com.fs.his.domain.FsUser;
+import com.fs.his.domain.FsUserIntegralLogs;
+import com.fs.his.mapper.FsUserIntegralLogsMapper;
+import com.fs.his.mapper.FsUserMapper;
+import com.fs.his.service.IFsUserIntegralLogsService;
+import com.fs.his.service.IFsUserService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 添加积分参数
+ */
+class AddIntegralParam {
+    private Long userId;
+    private Integer logType;
+    private Long integral;
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Integer getLogType() {
+        return logType;
+    }
+
+    public void setLogType(Integer logType) {
+        this.logType = logType;
+    }
+
+    public Long getIntegral() {
+        return integral;
+    }
+
+    public void setIntegral(Long integral) {
+        this.integral = integral;
+    }
+}
+
+/**
+ * 用户积分管理Controller 提供给卓美 完成直播积分发放
+ *
+ * @author fs
+ * @date 2025-01-01
+ */
+@Api("用户积分管理")
+@RestController
+@RequestMapping("/user/integral")
+public class FsUserIntegralController {
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    private IFsUserIntegralLogsService integralLogsService;
+
+    @Autowired
+    private FsUserMapper userMapper;
+
+    @Autowired
+    private FsUserIntegralLogsMapper integralLogsMapper;
+
+    /**
+     * 查询用户列表(支持手机号和昵称筛选)
+     */
+    @ApiOperation("查询用户列表")
+    @GetMapping("/list")
+    public TableDataInfo list(@RequestParam(required = false) String phone,
+                              @RequestParam(required = false) String nickName,
+                              @RequestParam(defaultValue = "1") Integer pageNum,
+                              @RequestParam(defaultValue = "10") Integer pageSize) {
+        PageHelper.startPage(pageNum, pageSize);
+        
+        // 构建查询条件
+        FsUser queryUser = new FsUser();
+        if (StringUtils.isNotEmpty(phone)) {
+            queryUser.setPhone(phone);
+        }
+        if (StringUtils.isNotEmpty(nickName)) {
+            queryUser.setNickName(nickName);
+            queryUser.setNickname(nickName);
+        }
+        
+        // 查询用户列表(只返回需要的字段)
+        List<FsUser> userList = userMapper.selectFsUserListForIntegral(queryUser);
+        
+        PageInfo<FsUser> pageInfo = new PageInfo<>(userList);
+        TableDataInfo dataTable = new TableDataInfo();
+        dataTable.setCode(200);
+        dataTable.setMsg("查询成功");
+        dataTable.setRows(pageInfo.getList());
+        dataTable.setTotal(pageInfo.getTotal());
+        return dataTable;
+    }
+
+    /**
+     * 查询用户积分明细
+     */
+    @ApiOperation("查询用户积分明细")
+    @GetMapping("/logs/{userId}")
+    public R getIntegralLogs(@PathVariable Long userId,
+                             @RequestParam(defaultValue = "1") Integer pageNum,
+                             @RequestParam(defaultValue = "10") Integer pageSize,
+                             @RequestParam(required = false) Integer logType) {
+        PageHelper.startPage(pageNum, pageSize);
+        FsUserIntegralLogs queryLogs = new FsUserIntegralLogs();
+        queryLogs.setUserId(userId);
+        if (logType != null) {
+            queryLogs.setLogType(logType);
+        }
+        // 按创建时间倒序查询(Mapper XML 中已添加排序)
+        List<FsUserIntegralLogs> logsList = integralLogsService.selectFsUserIntegralLogsList(queryLogs);
+        PageInfo<FsUserIntegralLogs> pageInfo = new PageInfo<>(logsList);
+        return R.ok().put("data", pageInfo.getList()).put("total", pageInfo.getTotal());
+    }
+
+    /**
+     * 添加积分
+     */
+    @ApiOperation("添加积分")
+    @PostMapping("/add")
+    public R addIntegral(@RequestBody AddIntegralParam param) {
+        Long userId = param.getUserId();
+        Integer logType = param.getLogType();
+        Long integral = param.getIntegral();
+        // 查询用户信息
+        FsUser user = userService.selectFsUserByUserId(userId);
+        if (user == null) {
+            return R.error("用户不存在");
+        }
+
+        // 查询用户最新的积分记录
+        FsUserIntegralLogs latestLog = integralLogsMapper.selectLatestIntegralLogByUserId(userId);
+        
+        // 计算新的积分余额
+        Long currentIntegral = user.getIntegral() != null ? user.getIntegral() : 0L;
+        Long newIntegral = currentIntegral + integral;
+        
+        // 创建时间默认往后移动2个小时,使用整点的创建时间和更新时间
+        Date now = new Date();
+        LocalDateTime localDateTime = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());
+        // 往后移动2小时
+        localDateTime = localDateTime.plusHours(2);
+        // 设置为整点(分钟和秒都设为0)
+        localDateTime = localDateTime.withMinute(0).withSecond(0).withNano(0);
+        Date createTime = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+        Date updateTime = createTime;
+
+        // 更新用户积分
+        user.setIntegral(newIntegral);
+
+        userService.increaseIntegral(Collections.singletonList(user.getUserId()),integral);
+
+        // 创建积分记录
+        FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+        integralLogs.setUserId(userId);
+        integralLogs.setLogType(logType);
+        integralLogs.setIntegral(integral);
+        integralLogs.setBalance(newIntegral);
+        integralLogs.setCreateTime(createTime);
+        integralLogs.setUpdateTime(updateTime);
+        integralLogsService.insertFsUserIntegralLogs(integralLogs);
+
+        return R.ok("添加积分成功");
+    }
+}

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

@@ -5,4 +5,4 @@ server:
 spring:
 spring:
   profiles:
   profiles:
 #    active: dev
 #    active: dev
-    active: druid-bjczwh-test
+    active: druid-ylrz

+ 5 - 0
fs-common/src/main/java/com/fs/common/annotation/RateLimiter.java

@@ -37,4 +37,9 @@ public @interface RateLimiter
      * 限流类型
      * 限流类型
      */
      */
     public LimitType limitType() default LimitType.DEFAULT;
     public LimitType limitType() default LimitType.DEFAULT;
+
+    /**
+     * 消息提示
+     */
+    public String msg() default "访问过于频繁,请稍后再试";
 }
 }

+ 2 - 0
fs-common/src/main/java/com/fs/common/constant/LiveKeysConstant.java

@@ -37,6 +37,8 @@ public class LiveKeysConstant {
     public static final Integer PRODUCT_DETAIL_CACHE_EXPIRE = 300; //商品详情缓存过期时间(秒)
     public static final Integer PRODUCT_DETAIL_CACHE_EXPIRE = 300; //商品详情缓存过期时间(秒)
 
 
     public static final String LIVE_TAG_MARK_CACHE = "live:tag:mark:%s"; //直播间打标签缓存,存储直播间ID、开始时间和视频时长
     public static final String LIVE_TAG_MARK_CACHE = "live:tag:mark:%s"; //直播间打标签缓存,存储直播间ID、开始时间和视频时长
+    //记录用户观看直播间信息 直播间id、用户id、外部联系人id、qwUserId
+    public static final String LIVE_USER_WATCH_LOG_CACHE = "live:user:watch:log:%s:%s:%s:%s";
 
 
 
 
 }
 }

+ 16 - 0
fs-common/src/main/java/com/fs/common/constant/RedisConstant.java

@@ -0,0 +1,16 @@
+package com.fs.common.constant;
+/**
+ * 库存与锁相关常量(Java 8 静态常量优化)
+ */
+public class RedisConstant {
+    // 库存Key前缀
+    public static final String STOCK_KEY_PREFIX = "product:stock:";
+    // 分布式锁Key前缀
+    public static final String LOCK_KEY_PREFIX = "product:lock:";
+    // 锁过期时间(30秒,避免死锁,大于业务执行时间)
+    public static final long LOCK_EXPIRE_SECONDS = 3L;
+    // 锁重试间隔(50毫秒,非阻塞重试,避免线程阻塞)
+    public static final long LOCK_RETRY_INTERVAL = 100L;
+    // 锁最大重试次数(3次,避免无限重试)
+    public static final int LOCK_MAX_RETRY = 20;
+}

+ 12 - 0
fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java

@@ -446,6 +446,18 @@ public class RedisCache
         redisTemplate.opsForHash().put(key, hashKey, value);
         redisTemplate.opsForHash().put(key, hashKey, value);
     }
     }
 
 
+    /**
+     * 向哈希表中添加键值对(仅当字段不存在时,原子操作,保证幂等性)
+     *
+     * @param key     哈希表键
+     * @param hashKey 哈希键
+     * @param value   哈希值
+     * @return true=设置成功(字段不存在),false=字段已存在
+     */
+    public Boolean hashPutIfAbsent(String key, String hashKey, Object value) {
+        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
+    }
+
     /**
     /**
      * 向哈希表中添加键值对
      * 向哈希表中添加键值对
      *
      *

+ 172 - 0
fs-common/src/main/java/com/fs/common/core/redis/service/StockDeductService.java

@@ -0,0 +1,172 @@
+package com.fs.common.core.redis.service;
+
+import com.fs.common.constant.RedisConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+/**
+ * 高并发库存扣减服务(Java 8 + Redis分布式锁)
+ */
+@Slf4j
+@Service
+public class StockDeductService {
+
+    // 注入RedisTemplate
+    public final RedisTemplate<String, Object> redisTemplate;
+
+    // 构造器注入(Spring 推荐,Java 8 支持)
+    public StockDeductService(RedisTemplate<String, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    // 库存扣减Lua脚本(预编译,提升高并发性能)
+    private static final DefaultRedisScript<Long> STOCK_DEDUCT_SCRIPT;
+
+    // 库存扣减Lua脚本(优化后,增强健壮性)
+    static {
+        // 初始化库存扣减脚本
+        STOCK_DEDUCT_SCRIPT = new DefaultRedisScript<>();
+        STOCK_DEDUCT_SCRIPT.setScriptText("if redis.call('exists', KEYS[1]) ~= 1 then " + "return -2; " + "end " + "local stock_str = redis.call('get', KEYS[1]); " + "local stock = tonumber(stock_str); " + "if stock == nil then " + "return -3; " + "end " + "local deductNum_str = ARGV[1]; " + "local deductNum = tonumber(deductNum_str); " + "if deductNum == nil or deductNum <= 0 then " + "return -4; " + "end " + "if stock >= deductNum then " + "return redis.call('decrby', KEYS[1], deductNum); " + "else " + "return -1; " + "end");
+        STOCK_DEDUCT_SCRIPT.setResultType(Long.class);
+    }
+
+    /**
+     * 初始化商品库存(Redis)
+     *
+     * @param productId 商品ID
+     * @param initStock 初始库存
+     */
+    public void initStock(Long productId, Long liveId, Integer initStock) {
+        String stockKey = RedisConstant.STOCK_KEY_PREFIX + liveId + ":" + productId;
+        redisTemplate.opsForValue().set(stockKey, initStock);
+        log.info("商品" + productId + "库存初始化完成,初始库存:" + initStock);
+    }
+
+    /**
+     * 同步获取商品库存(基础版,适用于低并发/实时性要求高的场景)
+     *
+     * @param productId 商品ID
+     * @param liveId    直播间ID(贴合原有库存Key的拆分粒度)
+     * @return 库存数量(库存不存在/非数字时返回0)
+     */
+    public Integer getStock(Long productId, Long liveId) {
+        // 1. 参数合法性校验(避免空ID导致Redis Key异常)
+        if (productId == null || liveId == null) {
+            log.warn("获取库存失败:商品ID/直播间ID为空");
+            return 0;
+        }
+
+        // 2. 拼接库存Key(与初始化/扣减逻辑保持一致)
+        String stockKey = RedisConstant.STOCK_KEY_PREFIX + liveId + ":" + productId;
+
+        try {
+            // 3. 读取Redis库存值(Java 8 Optional处理空值)
+            Object stockObj = redisTemplate.opsForValue().get(stockKey);
+            Integer stock = Optional.ofNullable(stockObj)
+                    // 处理Redis值非数字的情况(如脏数据)
+                    .map(val -> {
+                        if (val instanceof Integer) {
+                            return (Integer) val;
+                        } else if (val instanceof String) {
+                            try {
+                                return Integer.parseInt((String) val);
+                            } catch (NumberFormatException e) {
+                                log.error("库存值格式异常,Key:{},值:{}", stockKey, val, e);
+                                return 0;
+                            }
+                        } else {
+                            log.error("库存值类型不支持,Key:{},类型:{}", stockKey, val.getClass().getName());
+                            return 0;
+                        }
+                    })
+                    // 库存Key不存在时返回0
+                    .orElse(0);
+
+            log.info("获取商品{}库存成功,直播间{},当前库存:{}", productId, liveId, stock);
+            return stock;
+        } catch (Exception e) {
+            // 4. 异常兜底(Redis连接异常/超时等场景)
+            log.error("获取商品{}库存异常,直播间{}", productId, liveId, e);
+            return 0;
+        }
+    }
+
+    /**
+     * 异步获取商品库存(高并发版,适配原有异步扣减逻辑)
+     *
+     * @param productId 商品ID
+     * @param liveId    直播间ID
+     * @return 异步结果:库存数量(异常/空值返回0)
+     */
+    public CompletableFuture<Integer> getStockAsync(Long productId, Long liveId) {
+        // 复用CompletableFuture异步化,避免主线程阻塞(Java 8特性)
+        return CompletableFuture.supplyAsync(() -> getStock(productId, liveId));
+    }
+
+    /**
+     * 高并发库存扣减(核心方法,落地Java 8特性)
+     *
+     * @param productId 商品ID
+     * @param deductNum 扣减数量(默认1)
+     * @return 扣减结果:true=成功,false=失败
+     */
+    public CompletableFuture<Boolean> deductStockAsync(Long productId, Long liveId, Integer deductNum, Long userId) {
+        // Java 8 CompletableFuture 异步处理,提升高并发吞吐量
+        return CompletableFuture.supplyAsync(() -> {
+            // 1. 参数校验(Java 8 Optional 空值处理)
+            Integer num = Optional.ofNullable(deductNum).orElse(1);
+            String stockKey = RedisConstant.STOCK_KEY_PREFIX + liveId + ":" + productId;
+            try {
+                // 5. 执行库存扣减Lua脚本(原子操作,防超卖)
+                // 新增日志:打印当前库存值和扣减数量
+                Integer currentStockStr = (Integer) redisTemplate.opsForValue().get(stockKey);
+                log.info("拿到锁成功 → 库存Key:{},当前库存值:{},扣减数量:{}", stockKey, currentStockStr, num);
+
+                // 执行库存扣减Lua脚本
+                Long remainingStock = redisTemplate.execute(STOCK_DEDUCT_SCRIPT, Collections.singletonList(stockKey), 1);
+
+                // 新增日志:打印Lua返回结果
+                log.info("Lua脚本返回值:{}", remainingStock);
+
+                // 6. 判断扣减结果
+                if (remainingStock >= 0) {
+                    log.info("商品{}库存扣减成功,剩余库存:{}", productId, remainingStock);
+                    return true;
+                } else {
+                    String errorMsg = "";
+                    switch (remainingStock.intValue()) {
+                        case -1:
+                            errorMsg = "库存不足";
+                            break;
+                        case -2:
+                            errorMsg = "库存Key不存在";
+                            break;
+                        case -3:
+                            errorMsg = "库存值非数字";
+                            break;
+                        case -4:
+                            errorMsg = "扣减数量无效";
+                            break;
+                        default:
+                            errorMsg = "未知错误,错误码:" + remainingStock;
+                    }
+                    log.info("商品{}扣减失败:{}", productId, errorMsg);
+                    return false;
+                }
+            }catch (Exception e) {
+                log.error("支付失败获取失败", e);
+                return false;
+            }
+        });
+    }
+}

+ 1 - 0
fs-common/src/main/java/com/fs/common/enums/BizResponseEnum.java

@@ -9,6 +9,7 @@ public enum BizResponseEnum {
     FAIL(500, "操作失败"),
     FAIL(500, "操作失败"),
     PARAM_ERROR(400, "参数错误"),
     PARAM_ERROR(400, "参数错误"),
     DATA_NOT_EXIST(1002, "数据不存在"),
     DATA_NOT_EXIST(1002, "数据不存在"),
+    WATCH_LATEST_COURSE(482, "请观看最新的课程项目"),
     WAIT_APPROVAL(505, "等待审核");
     WAIT_APPROVAL(505, "等待审核");
 
 
     private final Integer code;
     private final Integer code;

+ 367 - 0
fs-common/src/main/java/com/fs/common/utils/HsCryptoUtil.java

@@ -0,0 +1,367 @@
+package com.fs.common.utils;
+
+import cn.hutool.json.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * 红杉健康平台加解密工具类
+ */
+public class HsCryptoUtil {
+
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+    private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
+    private static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
+
+    /**
+     * 加密方法
+     * @param data 待加密的数据对象
+     * @param appSecret 应用密钥,用于AES加密的IV
+     * @param publicKeyStr RSA公钥(Base64编码)
+     * @return 加密结果Map,包含content、key、sign三个字段
+     */
+    public static Map<String, String> encrypt(JSONObject data, String appSecret, String publicKeyStr) {
+        try {
+//            公钥解码base64
+            String publicKey = new String( Base64.decodeBase64(publicKeyStr), StandardCharsets.UTF_8);
+            publicKey = publicKey.replace("-----BEGIN PUBLIC KEY-----\n", "").replace(
+                    "\n" +
+                            "-----END PUBLIC KEY-----\n", ""
+            );
+            // 1. 将对象转换为按ASCII码排序的Map
+
+            Map<String, Object> sortedParams = new TreeMap<>();
+            /*处理掉空值,否则转jsonString要报错*/
+            for (Map.Entry<String, Object> entry : data.entrySet()) {
+                if ( entry.getValue() != null && !(entry.getValue() instanceof cn.hutool.json.JSONNull)) {
+                    Object processedValue = processNestedObjects(entry.getValue());
+                    sortedParams.put(entry.getKey(), processedValue);
+                }
+            }
+            String contentStr = objectMapper.writeValueAsString(sortedParams);
+//            // 直接转换 JSONObject 为 Map
+//            Map<String, Object> sortedParams = convertToSortedMap(data);
+//
+//            // 2. 转换为JSON字符串(contentStr)
+//            String contentStr = objectMapper.writeValueAsString(sortedParams);
+
+            // 3. 生成32位随机密钥(randStr)
+            String randStr = generateRandStr(32);
+
+            // 4. AES-256-CBC加密contentStr
+            String encryptedContent = aesEncrypt(contentStr, randStr, appSecret);
+
+            // 5. 使用RSA公钥加密randStr
+            String encryptedKey = rsaEncrypt(randStr, publicKey);
+
+            // 6. 对contentStr进行MD5签名
+            String sign = DigestUtils.md5Hex(contentStr);
+
+            // 7. 构造返回结果
+            Map<String, String> result = new TreeMap<>();
+            result.put("content", encryptedContent);
+            result.put("key", encryptedKey);
+            result.put("sign", sign);
+
+            return result;
+        } catch (Exception e) {
+            throw new RuntimeException("加密失败", e);
+        }
+    }
+
+    /**
+     * 递归处理嵌套的JSON对象 的jsonNull和null
+     */
+    private static Object processNestedObjects(Object value) {
+        if (value instanceof cn.hutool.json.JSONObject) {
+            // 处理嵌套的JSONObject
+            cn.hutool.json.JSONObject jsonObj = (cn.hutool.json.JSONObject) value;
+            Map<String, Object> nestedMap = new TreeMap<>();
+            for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {
+                if (entry.getValue() != null && !(entry.getValue() instanceof cn.hutool.json.JSONNull)) {
+                    nestedMap.put(entry.getKey(), processNestedObjects(entry.getValue()));
+                }
+            }
+            return nestedMap;
+        } else if (value instanceof cn.hutool.json.JSONArray) {
+            // 处理JSONArray
+            cn.hutool.json.JSONArray jsonArray = (cn.hutool.json.JSONArray) value;
+            List<Object> list = new ArrayList<>();
+            for (Object item : jsonArray) {
+                list.add(processNestedObjects(item));
+            }
+            return list;
+        } else {
+            // 基本类型直接返回
+            return value;
+        }
+    }
+
+    /**
+     * 将对象转换为按ASCII码排序的Map
+     */
+    private static Map<String, Object> convertToSortedMap(Object obj) throws Exception {
+        Map<String, Object> map = objectMapper.convertValue(obj, Map.class);
+        return new TreeMap<>(map);
+    }
+
+    /**
+     * 生成指定长度的随机字符串
+     */
+    private static String generateRandStr(int length) {
+        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        SecureRandom random = new SecureRandom();
+        StringBuilder sb = new StringBuilder(length);
+
+        for (int i = 0; i < length; i++) {
+            sb.append(chars.charAt(random.nextInt(chars.length())));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * AES-256-CBC加密
+     */
+    private static String aesEncrypt(String content, String randStr, String appSecret) throws Exception {
+        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
+        SecretKeySpec keySpec = new SecretKeySpec(randStr.getBytes(), "AES");
+        // 使用appSecret的前16位作为IV
+        IvParameterSpec ivSpec = new IvParameterSpec(appSecret.substring(0, 16).getBytes());
+        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
+        byte[] encrypted = cipher.doFinal(content.getBytes());
+        return Base64.encodeBase64String(encrypted);
+    }
+
+    /**
+     * RSA加密
+     */
+    private static String rsaEncrypt(String randStr, String publicKey) throws Exception {
+        byte[] decodedPublicKey = Base64.decodeBase64(publicKey);
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedPublicKey);
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey pubKey = keyFactory.generatePublic(keySpec);
+
+        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
+        byte[] encrypted = cipher.doFinal(randStr.getBytes());
+        return Base64.encodeBase64String(encrypted);
+    }
+
+    /**
+     * 解密方法
+     * @param encryptedContent AES加密的内容(Base64编码)
+     * @param encryptedKey RSA加密的密钥(Base64编码)
+     * @param sign 签名(MD5)
+     * @param appSecret 应用密钥,用于AES解密的IV
+     * @param privateKeyStr RSA私钥(PEM格式,Base64编码)
+     * @return 解密后的原始数据对象
+     */
+    public static Map<String, Object> decrypt(String encryptedContent,
+                                              String encryptedKey,
+                                              String sign,
+                                              String appSecret,
+                                              String privateKeyStr) {
+        try {
+            // 1. Base64解码私钥字符串,得到PEM格式的私钥
+            String privateKeyPEM = new String(
+                    Base64.decodeBase64(privateKeyStr),
+                    StandardCharsets.UTF_8
+            );
+            // 1. RSA解密获取AES密钥(randStr)
+            String randStr = rsaDecrypt(encryptedKey, privateKeyPEM);
+
+            // 2. AES解密获取contentStr
+            String contentStr = aesDecrypt(encryptedContent, randStr, appSecret);
+
+            // 3. 验证签名
+            String calculatedSign = DigestUtils.md5Hex(contentStr);
+            if (!calculatedSign.equals(sign)) {
+                throw new RuntimeException("签名验证失败,数据可能被篡改");
+            }
+
+            // 4. 将contentStr解析为Map
+            return objectMapper.readValue(contentStr, Map.class);
+
+        } catch (Exception e) {
+            throw new RuntimeException("解密失败", e);
+        }
+    }
+
+    /**
+     * 解密并验证的完整方法
+     * @param encryptedData 加密数据Map,包含content、key、sign三个字段
+     * @param appSecret 应用密钥
+     * @param privateKeyStr RSA私钥
+     * @return 解密后的原始数据Map
+     */
+    public static Map<String, Object> decrypt(JsonNode encryptedData,
+                                              String appSecret,
+                                              String privateKeyStr) {
+        return decrypt(
+                encryptedData.get("content").asText(),
+                encryptedData.get("key").asText(),
+                encryptedData.get("sign").asText(),
+                appSecret,
+                privateKeyStr
+        );
+    }
+
+    /**
+     * RSA解密 - 使用私钥解密AES密钥
+     */
+    private static String rsaDecrypt(String encryptedKey, String privateKeyStr) throws Exception {
+//    String privateKey = new String( Base64.decodeBase64(privateKeyStr), StandardCharsets.UTF_8);
+
+        // 处理PEM格式的私钥
+        String privateKeyPEM = privateKeyStr
+                .replace("-----BEGIN PRIVATE KEY-----", "")
+                .replace("-----END PRIVATE KEY-----", "")
+                .replaceAll("\\s+", "");
+
+        byte[] decodedPrivateKey = Base64.decodeBase64(privateKeyPEM);
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedPrivateKey);
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+
+        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+        cipher.init(Cipher.DECRYPT_MODE, privateKey);
+
+        byte[] encryptedBytes = Base64.decodeBase64(encryptedKey);
+        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+
+        return new String(decryptedBytes, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * AES-256-CBC解密
+     */
+    private static String aesDecrypt(String encryptedContent,
+                                     String randStr,
+                                     String appSecret) throws Exception {
+        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
+        SecretKeySpec keySpec = new SecretKeySpec(randStr.getBytes(StandardCharsets.UTF_8), "AES");
+
+        // 使用appSecret的前16位作为IV(与加密时一致)
+        String iv = appSecret.length() >= 16 ? appSecret.substring(0, 16) : appSecret;
+        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+
+        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+
+        byte[] encryptedBytes = Base64.decodeBase64(encryptedContent);
+        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+
+        return new String(decryptedBytes, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * 验证签名(不进行解密)
+     */
+    public static boolean verifySignature(String contentStr, String sign) {
+        String calculatedSign = DigestUtils.md5Hex(contentStr);
+        return calculatedSign.equals(sign);
+    }
+
+    /**
+     * 单独解密AES密钥(用于调试)
+     */
+    public static String decryptKeyOnly(String encryptedKey, String privateKeyStr) throws Exception {
+        return rsaDecrypt(encryptedKey, privateKeyStr);
+    }
+
+    /**
+     * 单独解密内容(需要提供AES密钥)
+     */
+    public static String decryptContentOnly(String encryptedContent,
+                                            String randStr,
+                                            String appSecret) throws Exception {
+        return aesDecrypt(encryptedContent, randStr, appSecret);
+    }
+
+
+//    /**
+//     * 解密方法
+//     * @param encryptedData 加密的数据Map,包含content、key、sign三个字段
+//     * @param appSecret 应用密钥,用于AES解密的IV
+//     * @param privateKeyStr RSA私钥(Base64编码)
+//     * @return 解密后的原始数据对象
+//     */
+//    public static Map<String, Object> decrypt(JsonNode encryptedData, String appSecret, String privateKeyStr) {
+//        //            私钥解码base64
+//    String privateKey = new String( Base64.decodeBase64(privateKeyStr), StandardCharsets.UTF_8);
+
+//        privateKey = privateKey.replace("-----BEGIN PRIVATE KEY-----\n", "").replace(
+//                "\n" +
+//                        "-----END PRIVATE KEY-----", ""
+//        );
+//        try {
+//            String content = String.valueOf(encryptedData.get("content"));
+//            String key = String.valueOf(encryptedData.get("key"));
+//            String sign = String.valueOf(encryptedData.get("sign"));
+//
+//            // 1. 使用RSA私钥解密key字段,得到randStr
+//            String randStr = rsaDecrypt(key, privateKey);
+//
+//            // 2. 使用AES-256-CBC解密content字段
+//            String decryptedContent = aesDecrypt(content, randStr, appSecret);
+//
+//            // 3. 验证签名
+//            String calculatedSign = DigestUtils.md5Hex(decryptedContent);
+//            if (!calculatedSign.equals(sign)) {
+//                throw new RuntimeException("签名验证失败");
+//            }
+//
+//            // 4. 将解密后的JSON字符串转换为Map对象
+//            @SuppressWarnings("unchecked")
+//            Map<String, Object> result = objectMapper.readValue(decryptedContent, Map.class);
+//
+//            return result;
+//        } catch (Exception e) {
+//            throw new RuntimeException("解密失败", e);
+//        }
+//    }
+//
+//    /**
+//     * AES-256-CBC解密
+//     */
+//    private static String aesDecrypt(String content, String randStr, String appSecret) throws Exception {
+//        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
+//        SecretKeySpec keySpec = new SecretKeySpec(randStr.getBytes(), "AES");
+//        // 使用appSecret的前16位作为IV
+//        IvParameterSpec ivSpec = new IvParameterSpec(appSecret.substring(0, 16).getBytes());
+//        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+//        byte[] decrypted = cipher.doFinal(Base64.decodeBase64(content));
+//        return new String(decrypted);
+//    }
+//
+//    /**
+//     * RSA解密
+//     */
+//    private static String rsaDecrypt(String encryptedKey, String privateKey) throws Exception {
+//        byte[] decodedPrivateKey = Base64.decodeBase64(privateKey);
+//        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedPrivateKey);
+//        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+//        PrivateKey privKey = keyFactory.generatePrivate(keySpec);
+//
+//        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+//        cipher.init(Cipher.DECRYPT_MODE, privKey);
+//        byte[] decrypted = cipher.doFinal(Base64.decodeBase64(encryptedKey));
+//        return new String(decrypted);
+//    }
+}

+ 154 - 0
fs-common/src/main/java/com/fs/common/utils/StringToMapUtil.java

@@ -0,0 +1,154 @@
+package com.fs.common.utils;
+
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.json.JSONUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class StringToMapUtil {
+    public static Map<String, Object> toMap(String input) {
+        if (input == null || input.trim().isEmpty()) {
+            return new HashMap<>();
+        }
+
+        try {
+            // 尝试JSON解析
+            return JSONUtil.toBean(input, new TypeReference<Map<String, Object>>() {}, false);
+        } catch (Exception e) {
+            // 如果失败,尝试Map.toString()格式解析
+            try {
+                // 检查是否是Map.toString()格式
+                if (input.trim().startsWith("{") && input.trim().endsWith("}") &&
+                        input.contains("=")) {
+                    return parseMapToString(input);
+                }
+            } catch (Exception ex) {
+                // 忽略,抛出原始异常
+            }
+            throw e;
+        }
+    }
+    /**
+     * 将Map.toString()格式的字符串转换为Map
+     * @param mapString 类似 {data={book_no=10381008622, ...}, msg=success, state=0} 的字符串
+     * @return 转换后的Map对象
+     */
+    private static Map<String, Object> parseMapToString(String mapString) {
+        Map<String, Object> resultMap = new HashMap<>();
+
+        // 移除外层大括号
+        String content = mapString.trim();
+        if (content.startsWith("{") && content.endsWith("}")) {
+            content = content.substring(1, content.length() - 1);
+        }
+
+        // 分割顶层键值对
+        List<String> pairs = splitTopLevelPairs(content);
+
+        for (String pair : pairs) {
+            int eqIndex = pair.indexOf('=');
+            if (eqIndex > 0) {
+                String key = pair.substring(0, eqIndex).trim();
+                String value = pair.substring(eqIndex + 1).trim();
+
+                if (value.startsWith("{") && value.endsWith("}")) {
+                    // 嵌套Map对象
+                    resultMap.put(key, parseNestedMap(value));
+                } else {
+                    // 简单值
+                    resultMap.put(key, value);
+                }
+            }
+        }
+
+        return resultMap;
+    }
+
+    /**
+     * 分割顶层键值对,避免分割嵌套对象内的逗号
+     */
+    private static List<String> splitTopLevelPairs(String content) {
+        List<String> pairs = new ArrayList<>();
+        int level = 0;
+        int start = 0;
+
+        for (int i = 0; i < content.length(); i++) {
+            char c = content.charAt(i);
+            if (c == '{') {
+                level++;
+            } else if (c == '}') {
+                level--;
+            } else if (c == ',' && level == 0) {
+                pairs.add(content.substring(start, i));
+                start = i + 1;
+            }
+        }
+
+        // 添加最后一个元素
+        if (start < content.length()) {
+            pairs.add(content.substring(start));
+        }
+
+        return pairs;
+    }
+
+    /**
+     * 解析嵌套的Map对象
+     */
+    private static Map<String, Object> parseNestedMap(String mapStr) {
+        Map<String, Object> nestedMap = new HashMap<>();
+
+        // 移除外层大括号
+        String content = mapStr.substring(1, mapStr.length() - 1);
+
+        List<String> pairs = splitTopLevelPairs(content);
+
+        for (String pair : pairs) {
+            int eqIndex = pair.indexOf('=');
+            if (eqIndex > 0) {
+                String key = pair.substring(0, eqIndex).trim();
+                String value = pair.substring(eqIndex + 1).trim();
+
+                // 尝试转换为合适的类型
+                nestedMap.put(key, convertValueType(value));
+            }
+        }
+
+        return nestedMap;
+    }
+
+    /**
+     * 根据值的特点转换为合适的类型
+     */
+    private static Object convertValueType(String value) {
+        // 数字
+        if (value.matches("-?\\d+")) {
+            try {
+                return Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                // 忽略,继续处理
+            }
+        }
+
+        // 布尔值
+        if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
+            return Boolean.parseBoolean(value);
+        }
+
+        // 嵌套对象
+        if (value.startsWith("{") && value.endsWith("}")) {
+            return parseNestedMap(value);
+        }
+
+        // 数组或列表(简化处理)
+        if (value.startsWith("[") && value.endsWith("]")) {
+            return value.substring(1, value.length() - 1).split(",");
+        }
+
+        // 默认作为字符串
+        return value;
+    }
+}

+ 23 - 0
fs-common/src/main/java/com/fs/common/utils/poi/ExcelUtil.java

@@ -19,6 +19,8 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
+
+import com.fs.common.core.domain.entity.SysDictData;
 import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
 import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
 import org.apache.poi.hssf.usermodel.HSSFPicture;
 import org.apache.poi.hssf.usermodel.HSSFPicture;
 import org.apache.poi.hssf.usermodel.HSSFPictureData;
 import org.apache.poi.hssf.usermodel.HSSFPictureData;
@@ -705,6 +707,27 @@ public class ExcelUtil<T>
             // 这里默认设了2-101列只能选择不能输入.
             // 这里默认设了2-101列只能选择不能输入.
             setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
             setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
         }
         }
+        // 如果设置了dictType属性,则从字典中获取选项并设置下拉列表
+        if (StringUtils.isNotEmpty(attr.dictType()))
+        {
+            try
+            {
+                List<SysDictData> dictDatas = DictUtils.getDictCache(attr.dictType());
+                if (dictDatas != null && !dictDatas.isEmpty())
+                {
+                    // 提取字典标签列表
+                    String[] dictLabels = dictDatas.stream()
+                            .map(SysDictData::getDictLabel)
+                            .toArray(String[]::new);
+                    // 这里默认设了2-101列只能选择不能输入.
+                    setXSSFValidation(sheet, dictLabels, 1, 100, column, column);
+                }
+            }
+            catch (Exception e)
+            {
+                log.warn("设置字典下拉选项失败,dictType: {}, error: {}", attr.dictType(), e.getMessage());
+            }
+        }
     }
     }
 
 
     /**
     /**

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

@@ -45,10 +45,7 @@ import java.io.InputStream;
 import java.time.LocalDate;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 
 import static com.fs.his.utils.PhoneUtil.encryptPhone;
 import static com.fs.his.utils.PhoneUtil.encryptPhone;
 
 
@@ -216,7 +213,9 @@ public class FsUserController extends AppBaseController {
             @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
             @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
     ) {
     ) {
         long userId = Long.parseLong(getUserId());
         long userId = Long.parseLong(getUserId());
-        return ResponseResult.ok(fsUserService.userRanking(userId, startTime, endTime, periodId, videoId, order, type));
+        // 中康的数据太多太卡不要这个
+        return ResponseResult.ok(Collections.emptyList());
+//        return ResponseResult.ok(fsUserService.userRanking(userId, startTime, endTime, periodId, videoId, order, type));
     }
     }
 
 
     @Login
     @Login
@@ -231,7 +230,9 @@ public class FsUserController extends AppBaseController {
             @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
             @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
     ) {
     ) {
         long userId = Long.parseLong(getUserId());
         long userId = Long.parseLong(getUserId());
-        return ResponseResult.ok(fsUserService.courseRanking(userId, startTime, endTime, courseId, videoId, order, type));
+        // 中康的数据太多太卡不要这个
+        return ResponseResult.ok(Collections.emptyList());
+//        return ResponseResult.ok(fsUserService.courseRanking(userId, startTime, endTime, courseId, videoId, order, type));
     }
     }
 
 
     @Login
     @Login

+ 13 - 3
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -17,6 +17,7 @@ import com.fs.company.service.ICompanyMiniappService;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.dto.BatchSendCourseDTO;
 import com.fs.course.dto.BatchSendCourseDTO;
 import com.fs.course.dto.BatchUrgeCourseDTO;
 import com.fs.course.dto.BatchUrgeCourseDTO;
@@ -45,11 +46,13 @@ import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
+import java.io.IOException;
 import javax.validation.Valid;
 import javax.validation.Valid;
 
 
 import java.io.InputStream;
 import java.io.InputStream;
@@ -100,6 +103,9 @@ public class FsUserCourseVideoController extends AppBaseController {
     @Autowired
     @Autowired
     private ICompanyMiniappService companyMiniappService;
     private ICompanyMiniappService companyMiniappService;
 
 
+    @Autowired
+    private CloudHostProper cloudHostProper;
+
     @Autowired
     @Autowired
     private ISysConfigService configService;
     private ISysConfigService configService;
 
 
@@ -194,8 +200,8 @@ public class FsUserCourseVideoController extends AppBaseController {
     @GetMapping("/participationRecord")
     @GetMapping("/participationRecord")
     public ResponseResult<Object> participationRecord(@RequestParam Long videoId,
     public ResponseResult<Object> participationRecord(@RequestParam Long videoId,
                                                       @RequestParam Integer type,
                                                       @RequestParam Integer type,
-                                                      @RequestParam(required = false) String keyword,
                                                       @RequestParam(required = false) Long periodId,
                                                       @RequestParam(required = false) Long periodId,
+                                                      @RequestParam(required = false) String keyword,
                                                       @RequestParam(required = false, defaultValue = "1") Integer pageNum,
                                                       @RequestParam(required = false, defaultValue = "1") Integer pageNum,
                                                       @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
                                                       @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
         log.debug("参与记录 videoId:{}, type:{}, keyword: {}, pageNum: {}, pageSize: {}", videoId, type, keyword, pageNum, pageSize);
         log.debug("参与记录 videoId:{}, type:{}, keyword: {}, pageNum: {}, pageSize: {}", videoId, type, keyword, pageNum, pageSize);
@@ -215,7 +221,6 @@ public class FsUserCourseVideoController extends AppBaseController {
         } else {
         } else {
             params.put("companyUserId", companyUser.getUserId());
             params.put("companyUserId", companyUser.getUserId());
         }
         }
-
         PageHelper.startPage(pageNum, pageSize);
         PageHelper.startPage(pageNum, pageSize);
         List<FsUserCourseParticipationRecordVO> record = fsUserCourseService.getParticipationRecordByMap(params);
         List<FsUserCourseParticipationRecordVO> record = fsUserCourseService.getParticipationRecordByMap(params);
         return ResponseResult.ok(new PageInfo<>(record));
         return ResponseResult.ok(new PageInfo<>(record));
@@ -337,7 +342,12 @@ public class FsUserCourseVideoController extends AppBaseController {
     @Login
     @Login
     @GetMapping("/getGotoWxAppLink")
     @GetMapping("/getGotoWxAppLink")
     @ApiOperation("获取跳转微信小程序的链接地址")
     @ApiOperation("获取跳转微信小程序的链接地址")
-    public ResponseResult<String> getGotoWxAppLink(String linkStr,String appid) {
+    public ResponseResult<String> getGotoWxAppLink(String linkStr,String appid) throws IOException, WxErrorException {
+
+        if ("河山医院".equals(cloudHostProper.getCompanyName())){
+            return ResponseResult.ok(courseLinkService.generateShortLinkFull(appid,linkStr,false));
+        }
+
         return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
         return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
     }
     }
 
 

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

@@ -171,7 +171,7 @@ public class WxCompanyUserController extends AppBaseController {
      */
      */
     private FsUser createUser(LoginMaWxParam param, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo, Company company, CompanyUser companyUser) {
     private FsUser createUser(LoginMaWxParam param, WxMaJscode2SessionResult session, WxMaPhoneNumberInfo phoneNoInfo, Company company, CompanyUser companyUser) {
         FsUser user = new FsUser();
         FsUser user = new FsUser();
-        user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
+        user.setStatus(1);
         user.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
         user.setUnionId(session.getUnionid() == null ? "" : session.getUnionid());
         user.setCreateTime(new Date());
         user.setCreateTime(new Date());
         if (param.getAuthType() == 1 && phoneNoInfo != null) {
         if (param.getAuthType() == 1 && phoneNoInfo != null) {

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

@@ -6,10 +6,12 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.file.OssException;
 import com.fs.common.exception.file.OssException;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.file.FileUploadUtils;
 import com.fs.common.utils.file.FileUploadUtils;
 import com.fs.common.utils.file.FileUtils;
 import com.fs.common.utils.file.FileUtils;
+import com.fs.company.service.ICompanyService;
 import com.fs.company.utils.AudioUtils;
 import com.fs.company.utils.AudioUtils;
 import com.fs.company.vo.WangUploadVO;
 import com.fs.company.vo.WangUploadVO;
 import com.fs.course.service.ITencentCloudCosService;
 import com.fs.course.service.ITencentCloudCosService;
@@ -40,6 +42,7 @@ import java.awt.image.BufferedImage;
 import java.io.ByteArrayOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
@@ -71,6 +74,9 @@ public class CommonController
     @Autowired
     @Autowired
     private OpenIMService openIMService;
     private OpenIMService openIMService;
 
 
+    @Autowired
+    private ICompanyService companyService;
+
     @Autowired
     @Autowired
     private TokenService tokenService;
     private TokenService tokenService;
 //    @Autowired
 //    @Autowired
@@ -93,6 +99,38 @@ public class CommonController
 //        qwWorkTaskService.addQwWorkByConversionDay();
 //        qwWorkTaskService.addQwWorkByConversionDay();
       return R.ok();
       return R.ok();
     }
     }
+
+    @GetMapping("common/test2")
+    public R test2(String time) throws Exception
+    {
+        // 默认是前两天时间
+        String createSTime;
+        String createETime;
+        if (StringUtils.isNotBlank(time)) {
+            Date date = DateUtils.parseDate(time);
+            createSTime = time+" 00:00:00";
+            createETime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(date, 1))+" 00:00:00";
+        } else {
+            createSTime = DateUtils.parseDateToStr( DateUtils.YYYY_MM_DD,DateUtils.addDays(new Date(), -2))+" 00:00:00";
+            createETime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(new Date(), -1))+" 00:00:00";
+        }
+
+        // 这个地方真加的是company money字段 xgb
+        companyService.rollbackRedPacketMoney(createSTime, createETime);
+        return R.ok();
+    }
+
+    @GetMapping("common/test3")
+    public R test2(String companyIdStr,String outBatchNo) throws Exception
+    {
+        // 默认是前两天时间
+        Long companyId=Long.parseLong(companyIdStr);
+        R result=companyService.checkMchTransferStatusByBatchID(outBatchNo,companyId);
+        log.info("查询商户转账结果:{}",result);
+        return R.ok();
+    }
+
+
     /**
     /**
      * 通用下载请求
      * 通用下载请求
      *
      *

+ 117 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyAiWorkflowController.java

@@ -0,0 +1,117 @@
+package com.fs.company.controller.company;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.param.CompanyUserAiWorkflowTtsVoiceParam;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.his.domain.FsAiWorkflow;
+import com.fs.his.domain.FsAiWorkflowNode;
+import com.fs.his.service.IFsAiWorkflowService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 企业AI工作流Controller
+ *
+ * @author fs
+ * @date 2026-01-09
+ */
+@RestController
+@RequestMapping("/company/aiWorkflow")
+public class CompanyAiWorkflowController extends BaseController {
+
+    @Autowired
+    private IFsAiWorkflowService fsAiWorkflowService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 分页查询AI工作流列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(FsAiWorkflow fsAiWorkflow) {
+        startPage();
+        List<FsAiWorkflow> list = fsAiWorkflowService.selectFsAiWorkflowList(fsAiWorkflow);
+        return getDataTable(list);
+    }
+
+    /**
+     * 根据企业用户ID获取工作流的关键节点
+     * 节点类型: start(开始), end(结束), ai_chat(AI对话)
+     */
+    @GetMapping("/nodes/{companyUserId}")
+    public R getWorkflowNodes(@PathVariable("companyUserId") Long companyUserId) {
+        // 定义要查询的节点类型
+        List<String> nodeTypes = Arrays.asList("start", "end", "ai_chat");
+        List<FsAiWorkflowNode> nodes = fsAiWorkflowService.selectWorkflowNodesByCompanyUserId(companyUserId, nodeTypes);
+        return R.ok().put("data", nodes);
+    }
+
+    /**
+     * 获取当前登录企业用户的工作流关键节点
+     * 节点类型: start(开始), end(结束), ai_chat(AI对话)
+     */
+//    @GetMapping("/myNodes")
+    public R getMyWorkflowNode() {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (loginUser == null || loginUser.getUser() == null) {
+            return R.error("用户信息错误");
+        }
+        Long companyUserId = loginUser.getUser().getUserId();
+        List<String> nodeTypes = Arrays.asList("start", "end", "ai_chat");
+        List<FsAiWorkflowNode> nodes = fsAiWorkflowService.selectWorkflowNodesByCompanyUserId(companyUserId, nodeTypes);
+        return R.ok().put("data", nodes);
+    }
+
+    /**
+     * 获取当前登录企业用户的工作流及需要录音的节点
+     */
+    @GetMapping("/myNodes")
+    public R getMyWorkflowNodes() {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (loginUser == null || loginUser.getUser() == null) {
+            return R.error("用户信息错误");
+        }
+        Long companyUserId = loginUser.getUser().getUserId();
+        return R.ok().put("data", fsAiWorkflowService.getMyWorkflowNodes(companyUserId));
+    }
+
+//    @PostMapping("/ttsVoice")
+//    public R ttsVoice(@RequestBody CompanyUserAiWorkflowTtsVoiceParam param) {
+//        return R.ok().put("data", fsAiWorkflowService.ttsVoice(param));
+//    }
+
+//    /**
+//     * 更新节点的语音URL
+//     * @param nodeKey 节点ID
+//     * @param voiceUrl 语音URL
+//     */
+//    @PutMapping("/node/voiceUrl")
+//    public R updateNodeVoiceUrl(@RequestParam("nodeKey") String nodeKey,
+//                                @RequestParam("voiceUrl") String voiceUrl,
+//                                @RequestParam("workflowId") Long workflowId) {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        if (loginUser == null || loginUser.getUser() == null) {
+//            return R.error("用户信息错误");
+//        }
+//        Long companyUserId = loginUser.getUser().getUserId();
+//
+//        int result = fsAiWorkflowService.updateNodeVoiceUrl(nodeKey, voiceUrl, companyUserId,workflowId);
+//        if (result == -1) {
+//            return R.error("节点不存在");
+//        } else if (result == -2) {
+//            return R.error("无权限修改该节点");
+//        } else if (result > 0) {
+//            return R.ok("更新成功");
+//        }
+//        return R.error("更新失败");
+//    }
+}

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

@@ -126,7 +126,15 @@ public class CompanyMoneyLogsController extends BaseController
             task.setTaskType(7);
             task.setTaskType(7);
             task.setStatus(0);
             task.setStatus(0);
             task.setStartTime(new Date());
             task.setStartTime(new Date());
-            task.setRemark("药品订单导出");
+            String remark = "导出订单明细";
+            if (param.getOrderType() != null) {
+                if (param.getOrderType() == 0) {
+                    remark = "导出商城订单明细";
+                } else if (param.getOrderType() == 1) {
+                    remark = "导出直播订单明细";
+                }
+            }
+            task.setRemark(remark);
             task.setSysType(2);
             task.setSysType(2);
             task.setCompanyUserId(userId);
             task.setCompanyUserId(userId);
             exportTaskService.insertFsExportTask(task);
             exportTaskService.insertFsExportTask(task);

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

@@ -108,6 +108,7 @@ public class CompanyRedPacketBalanceLogsController extends BaseController {
         redRecharge.setRemark(param.getRemark());
         redRecharge.setRemark(param.getRemark());
         redRecharge.setPayType(3);
         redRecharge.setPayType(3);
         redRecharge.setBusinessType(1);// 红包充值
         redRecharge.setBusinessType(1);// 红包充值
+        redRecharge.setImgs(param.getImgs());// 红包充值
         rechargeService.insertCompanyRecharge(redRecharge);
         rechargeService.insertCompanyRecharge(redRecharge);
         return R.ok("提交成功,等待审核");
         return R.ok("提交成功,等待审核");
 
 

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

@@ -135,6 +135,7 @@ public class CompanyUserController extends BaseController {
     /**
     /**
      * 获取用户列表
      * 获取用户列表
      */
      */
+    @PreAuthorize("@ss.hasPermi('company:user:deptList')")
     @GetMapping("/deptList")
     @GetMapping("/deptList")
     public TableDataInfo deptList(CompanyUser user)
     public TableDataInfo deptList(CompanyUser user)
     {
     {
@@ -160,7 +161,6 @@ public class CompanyUserController extends BaseController {
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
-
     @GetMapping("/getUserList")
     @GetMapping("/getUserList")
     public R getUserList()
     public R getUserList()
     {
     {
@@ -199,7 +199,44 @@ public class CompanyUserController extends BaseController {
                     }
                     }
                     //是否绑定
                     //是否绑定
                     if(QwStatusEnum.BOUND.getCode() == companyUserQwListVO.getQwStatus()){
                     if(QwStatusEnum.BOUND.getCode() == companyUserQwListVO.getQwStatus()){
-                        if(!companyUserQwListVO.getQwUserId().isEmpty()){
+                        if(!StringUtil.strIsNullOrEmpty(companyUserQwListVO.getQwUserId())){
+                            Long[] ids = Arrays.stream(companyUserQwListVO.getQwUserId().split(","))
+                                    .map(Long::parseLong)
+                                    .toArray(Long[]::new);
+                            List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(ids);
+                            companyUserQwListVO.setQwUsers(qwUserVOS);
+                        }
+                    }
+                });
+                futures.add(future);
+            }
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+        }
+        return getDataTable(list);
+    }
+
+    @GetMapping("/myQwList")
+    public TableDataInfo myQwList(CompanyUserQwParam user) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        user.setCompanyId(loginUser.getCompany().getCompanyId());
+        user.setUserId(loginUser.getUser().getUserId());
+        List<CompanyUserQwListVO> list = companyUserService.selectCompanyUserQwListVO(user);
+        if (!list.isEmpty()){
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+            Long sendDelayTime = config.getSendDelayTime();
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            for (CompanyUserQwListVO companyUserQwListVO : list) {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(companyUserQwListVO.getCompanyId(), companyUserQwListVO.getUserId());
+                    if (ObjectUtil.isEmpty(companyUserDelayTime)) {
+                        companyUserQwListVO.setSendDelayTime(sendDelayTime);
+                    } else {
+                        companyUserQwListVO.setSendDelayTime(companyUserDelayTime.getSendDelayTime());
+                    }
+                    //是否绑定
+                    if(QwStatusEnum.BOUND.getCode() == companyUserQwListVO.getQwStatus()){
+                        if(!StringUtil.strIsNullOrEmpty(companyUserQwListVO.getQwUserId())){
                             Long[] ids = Arrays.stream(companyUserQwListVO.getQwUserId().split(","))
                             Long[] ids = Arrays.stream(companyUserQwListVO.getQwUserId().split(","))
                                     .map(Long::parseLong)
                                     .map(Long::parseLong)
                                     .toArray(Long[]::new);
                                     .toArray(Long[]::new);
@@ -414,7 +451,7 @@ public class CompanyUserController extends BaseController {
     /**
     /**
      * 重置密码
      * 重置密码
      */
      */
-    @PreAuthorize("@ss.hasPermi('company:user:edit')")
+    @PreAuthorize("@ss.hasPermi('company:user:resetPwd')")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @PutMapping("/resetPwd")
     @PutMapping("/resetPwd")
     public AjaxResult resetPwd(@RequestBody CompanyUser user)
     public AjaxResult resetPwd(@RequestBody CompanyUser user)

+ 89 - 5
fs-company/src/main/java/com/fs/company/controller/company/IndexStatisticsController.java

@@ -3,6 +3,9 @@ package com.fs.company.controller.company;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.Company;
+import com.fs.company.service.ICompanyService;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import com.fs.framework.service.TokenService;
 import com.fs.statis.StatisticsRedisConstant;
 import com.fs.statis.StatisticsRedisConstant;
@@ -12,7 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 
 import static com.fs.statis.StatisticsRedisConstant.*;
 import static com.fs.statis.StatisticsRedisConstant.*;
 
 
@@ -31,6 +37,11 @@ public class IndexStatisticsController {
     @Autowired
     @Autowired
     private IStatisticsService statisticsService;
     private IStatisticsService statisticsService;
 
 
+    @Autowired
+    private ICompanyService companyService;
+
+    @Autowired
+    CloudHostProper cloudHostProper;
     /**
     /**
      * 分析概览
      * 分析概览
      */
      */
@@ -94,12 +105,85 @@ public class IndexStatisticsController {
             userType = 0;
             userType = 0;
         }
         }
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        Long companyId = loginUser.getCompany().getCompanyId();
-        param.setCompanyId(companyId);
 
 
-        String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,param.getCompanyId());
-        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(key);
-        return R.ok().put("data", deaMemberTopTenDTOS);
+        if("四川致医".equals(cloudHostProper.getCompanyName())){
+            Long companyId1 = loginUser.getCompany().getCompanyId();
+            Company company = loginUser.getCompany();
+            param.setCompanyId(companyId1);
+            List<WatchEndPlayTrendDTO> watchEndPlayTrendDTOS;
+
+            // 参考watchCourseTopTen方法的处理逻辑
+            if ((param.getCompanyId() == null && param.getDeptId() == null) || (param.getCompanyId() == null && param.getDeptId() == 1)){
+                String key = String.format("%s:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType);
+                watchEndPlayTrendDTOS = redisCache.getCacheObject(key);
+            }else if(param.getCompanyId() != null){
+                String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,param.getCompanyId());
+                watchEndPlayTrendDTOS = redisCache.getCacheObject(key);
+            }else{
+                Long[] companyIds = companyService.selectCompanyList(company).stream().map(Company::getCompanyId).toArray(Long[]::new);
+                List<WatchEndPlayTrendDTO> tempDTOS = new ArrayList<>();
+                for(Long companyId : companyIds){
+                    String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,companyId);
+                    List<WatchEndPlayTrendDTO> companyData = redisCache.getCacheObject(key);
+                    if (companyData != null) {
+                        tempDTOS.addAll(companyData);
+                    }
+                }
+                // 根据startDate 和 x 分组,合并watchUserCount和completedUserCount 限制最多返回10条记录
+                watchEndPlayTrendDTOS = tempDTOS.stream()
+                        .collect(Collectors.groupingBy(
+                                dto -> dto.getStartDate() + ":" + dto.getX(),  // 根据startDate和x分组
+                                Collectors.reducing(new WatchEndPlayTrendDTO(), (dto1, dto2) -> {
+                                    // 合并watchUserCount和completedUserCount
+                                    WatchEndPlayTrendDTO result = new WatchEndPlayTrendDTO();
+                                    // 复制分组标识字段
+                                    if (dto2 != null && dto2.getStartDate() != null) {
+                                        result.setStartDate(dto2.getStartDate());
+                                    } else if (dto1 != null) {
+                                        result.setStartDate(dto1.getStartDate());
+                                    }
+
+                                    if (dto2 != null && dto2.getX() != null) {
+                                        result.setX(dto2.getX());
+                                    } else if (dto1 != null) {
+                                        result.setX(dto1.getX());
+                                    }
+
+                                    // 合并数值字段
+                                    result.setWatchUserCount(
+                                            (dto1 == null || dto1.getWatchUserCount() == null ? 0 : dto1.getWatchUserCount()) +
+                                                    (dto2 == null || dto2.getWatchUserCount() == null ? 0 : dto2.getWatchUserCount())
+                                    );
+
+                                    result.setCompletedUserCount(
+                                            (dto1 == null || dto1.getCompletedUserCount() == null ? 0 : dto1.getCompletedUserCount()) +
+                                                    (dto2 == null || dto2.getCompletedUserCount() == null ? 0 : dto2.getCompletedUserCount())
+                                    );
+
+                                    return result;
+                                })
+                        ))
+                        .values()
+                        .stream()
+                        .filter(Objects::nonNull)  // 过滤掉null值
+                        .limit(10)
+                        .sorted(Comparator.comparing(WatchEndPlayTrendDTO::getX))
+                        .collect(Collectors.toList());
+            }
+
+            if(watchEndPlayTrendDTOS == null){
+                watchEndPlayTrendDTOS = new ArrayList<>();
+            }
+
+            return R.ok().put("data", watchEndPlayTrendDTOS);
+        }else{
+            Long companyId = loginUser.getCompany().getCompanyId();
+            param.setCompanyId(companyId);
+
+            String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,param.getCompanyId());
+            List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(key);
+            return R.ok().put("data", deaMemberTopTenDTOS);
+        }
     }
     }
 
 
     /**
     /**

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

@@ -26,6 +26,7 @@ import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
 import com.fs.system.service.ISysConfigService;
 import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
@@ -56,6 +57,8 @@ public class FsCourseRedPacketLogController extends BaseController
 
 
     @Autowired
     @Autowired
     private IFsUserCoursePeriodService fsUserCoursePeriodService;
     private IFsUserCoursePeriodService fsUserCoursePeriodService;
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
     /**
     /**
      * 查询短链课程看课记录列表
      * 查询短链课程看课记录列表
      */
      */

+ 11 - 3
fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java

@@ -110,11 +110,19 @@ public class FsCourseWatchLogController extends BaseController
 
 
 
 
         List<Long> combinedList = new ArrayList<>();
         List<Long> combinedList = new ArrayList<>();
-        //本部门
-        Long deptId = loginUser.getUser().getDeptId();
-        if (deptId!=null){
+        Long deptId;
+
+        if(param.getDeptId() != null){
+            deptId = param.getDeptId();
             combinedList.add(deptId);
             combinedList.add(deptId);
+        }else{
+            //本部门
+            deptId = loginUser.getUser().getDeptId();
+            if (deptId!=null){
+                combinedList.add(deptId);
+            }
         }
         }
+
         //本部门的下级部门
         //本部门的下级部门
         List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
         List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
         if (!deptList.isEmpty()){
         if (!deptList.isEmpty()){

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

@@ -31,6 +31,8 @@ import com.fs.framework.service.TokenService;
 import com.fs.his.vo.OptionsVO;
 import com.fs.his.vo.OptionsVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
+import com.hc.openapi.tool.fastjson.JSON;
+import com.hc.openapi.tool.fastjson.JSONObject;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
@@ -115,6 +117,15 @@ public class FsUserCoursePeriodController extends BaseController {
             } else {
             } else {
                 vo.setIsNeedRegisterMember("0");
                 vo.setIsNeedRegisterMember("0");
             }
             }
+
+            // 看课休息判断
+            if(StringUtils.isNotBlank(vo.getIsOpenRestFlag())){
+                JSONObject  jsonObject= JSON.parseObject(vo.getIsOpenRestFlag());
+                vo.setIsOpenRestReminder(Integer.parseInt(jsonObject.get(currentCompanyId.toString()).toString()));
+            }else {
+                vo.setIsOpenRestReminder(null);
+            }
+
         }
         }
         PageInfo<FsUserCoursePeriodVO> pageInfo = new PageInfo<>(list);
         PageInfo<FsUserCoursePeriodVO> pageInfo = new PageInfo<>(list);
         Map<String, Object> result = new HashMap<>();
         Map<String, Object> result = new HashMap<>();
@@ -200,7 +211,8 @@ public class FsUserCoursePeriodController extends BaseController {
     @PutMapping("/updatePeriodIsOpenRestReminder")
     @PutMapping("/updatePeriodIsOpenRestReminder")
     public AjaxResult updatePeriodIsOpenRestReminder(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
     public AjaxResult updatePeriodIsOpenRestReminder(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
     {
     {
-        return toAjax(fsUserCoursePeriodService.updatePeriod(fsUserCoursePeriod));
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        return toAjax(fsUserCoursePeriodService.updatePeriod(fsUserCoursePeriod,loginUser.getCompany().getCompanyId()));
 
 
     }
     }
 
 

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

@@ -8,6 +8,7 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.service.IFastGptRoleService;
 import com.fs.fastGpt.service.IFastGptRoleService;
 import com.fs.fastGpt.vo.FastGptRoleVO;
 import com.fs.fastGpt.vo.FastGptRoleVO;
@@ -78,6 +79,14 @@ public class FastGptRoleController extends BaseController
         return util.exportExcel(list, "应用数据");
         return util.exportExcel(list, "应用数据");
     }
     }
 
 
+    @GetMapping("/getAllCourseList")
+    public R getAllCourseList()
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        List<FsUserCourseVideo> list = fastGptRoleService.selectAllCourseList(loginUser.getCompany().getCompanyId());
+        return R.ok().put("data",list);
+    }
+
     /**
     /**
      * 获取应用详细信息
      * 获取应用详细信息
      */
      */

+ 10 - 2
fs-company/src/main/java/com/fs/company/controller/live/LiveController.java

@@ -15,6 +15,7 @@ import com.fs.company.domain.CompanyUser;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
 import com.fs.framework.service.TokenService;
+import com.fs.his.domain.FsPayConfig;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.service.HuiFuService;
 import com.fs.huifuPay.service.HuiFuService;
@@ -28,7 +29,10 @@ import com.fs.live.service.ILiveCompanyCodeService;
 import com.fs.live.service.ILiveOrderService;
 import com.fs.live.service.ILiveOrderService;
 import com.fs.live.service.ILiveService;
 import com.fs.live.service.ILiveService;
 import com.fs.live.vo.LiveListVo;
 import com.fs.live.vo.LiveListVo;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.oss.OSSFactory;
+import com.fs.wx.miniapp.config.WxMaProperties;
 import com.google.common.reflect.TypeToken;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
 import com.google.gson.Gson;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
@@ -343,6 +347,9 @@ public class LiveController extends BaseController
         }
         }
     }
     }
 
 
+    @Autowired
+    private WxMaProperties properties;
+
     @ApiOperation("生成微信小程序码")
     @ApiOperation("生成微信小程序码")
     @GetMapping("/getWxaCodeUnLimit")
     @GetMapping("/getWxaCodeUnLimit")
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
@@ -350,9 +357,10 @@ public class LiveController extends BaseController
         String url="https://api.weixin.qq.com/cgi-bin/stable_token";
         String url="https://api.weixin.qq.com/cgi-bin/stable_token";
         HashMap<String, String> map = new HashMap<>();
         HashMap<String, String> map = new HashMap<>();
         map.put("grant_type","client_credential");
         map.put("grant_type","client_credential");
+
         // 百域承品
         // 百域承品
-        map.put("appid","wx44beed5640bcb1ba");
-        map.put("secret","1bfcfa420f741801575a74d94752d014");
+        map.put("appid",properties.getConfigs().get(0).getAppid());
+        map.put("secret",properties.getConfigs().get(0).getSecret());
         String accessToken = HttpUtils.endApi(url, null, map);
         String accessToken = HttpUtils.endApi(url, null, map);
         // 创建Gson对象
         // 创建Gson对象
         Gson gson = new Gson();
         Gson gson = new Gson();

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů