Преглед изворни кода

Merge branch 'master' into 企微聊天

# Conflicts:
#	fs-service/src/main/java/com/fs/qw/domain/QwCompany.java
#	fs-service/src/main/resources/mapper/qw/QwCompanyMapper.xml
ct пре 2 дана
родитељ
комит
4f24005364
95 измењених фајлова са 1692 додато и 280 уклоњено
  1. 139 0
      deploy - test.sh
  2. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/controller/CallbackController.java
  3. 48 27
      fs-ad-new-api/src/main/java/com/fs/app/controller/WeChatController.java
  4. 9 2
      fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java
  5. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/task/ConversionRetryTask.java
  6. 1 1
      fs-ad-new-api/src/main/java/com/fs/app/task/DataSyncTask.java
  7. 110 0
      fs-ad-new-api/src/main/java/com/fs/framework/aspectj/ControllerLogAspect.java
  8. 1 0
      fs-ad-new-api/src/main/resources/application.yml
  9. 1 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java
  10. 3 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  11. 84 0
      fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java
  12. 1 1
      fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java
  13. 15 1
      fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java
  14. 9 5
      fs-admin/src/main/java/com/fs/live/controller/OrderController.java
  15. 5 4
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  16. 13 1
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  17. 12 3
      fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java
  18. 7 3
      fs-company/src/main/java/com/fs/company/controller/live/OrderController.java
  19. 5 4
      fs-company/src/main/java/com/fs/company/controller/qw/QwCustomerLinkController.java
  20. 13 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempController.java
  21. 5 3
      fs-live-app/src/main/java/com/fs/live/task/Task.java
  22. 2 2
      fs-live-app/src/main/java/com/fs/live/websocket/auth/WebSocketConfigurator.java
  23. 63 59
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  24. 6 0
      fs-qw-api/Dockerfile
  25. 91 9
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  26. 2 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java
  27. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
  28. 21 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  29. 13 4
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  30. 3 0
      fs-service/src/main/java/com/fs/course/vo/FsCoursePlaySourceConfigVO.java
  31. 2 0
      fs-service/src/main/java/com/fs/course/vo/FsUserCoursePeriodVO.java
  32. 161 5
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  33. 1 1
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  34. 2 2
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  35. 3 3
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  36. 68 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java
  37. 1 1
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderCreateParam.java
  38. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreAfterSalesScrmService.java
  39. 41 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  40. 38 12
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  41. 2 2
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportRefundZMVO.java
  42. 1 1
      fs-service/src/main/java/com/fs/live/domain/LiveOrder.java
  43. 17 0
      fs-service/src/main/java/com/fs/live/domain/LiveWatchLog.java
  44. 2 0
      fs-service/src/main/java/com/fs/live/mapper/LiveAfterSalesMapper.java
  45. 21 0
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  46. 2 0
      fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java
  47. 2 0
      fs-service/src/main/java/com/fs/live/service/ILiveAfterSalesService.java
  48. 54 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  49. 11 7
      fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java
  50. 14 12
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  51. 17 12
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  52. 5 5
      fs-service/src/main/java/com/fs/live/service/impl/LiveVideoServiceImpl.java
  53. 1 1
      fs-service/src/main/java/com/fs/live/vo/LiveUserDetailExportVO.java
  54. 20 11
      fs-service/src/main/java/com/fs/live/vo/MergedOrderExportVO.java
  55. 2 0
      fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java
  56. 18 0
      fs-service/src/main/java/com/fs/newAdv/controller/AdvMiniConfigController.java
  57. 58 0
      fs-service/src/main/java/com/fs/newAdv/domain/AdvMiniConfig.java
  58. 2 2
      fs-service/src/main/java/com/fs/newAdv/domain/PromotionAccount.java
  59. 2 2
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/BaiduApiClient.java
  60. 44 6
      fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/TencentApiClient.java
  61. 18 0
      fs-service/src/main/java/com/fs/newAdv/mapper/AdvMiniConfigMapper.java
  62. 16 0
      fs-service/src/main/java/com/fs/newAdv/service/IAdvMiniConfigService.java
  63. 1 1
      fs-service/src/main/java/com/fs/newAdv/service/ILeadService.java
  64. 20 0
      fs-service/src/main/java/com/fs/newAdv/service/impl/AdvMiniConfigServiceImpl.java
  65. 6 4
      fs-service/src/main/java/com/fs/newAdv/service/impl/LeadServiceImpl.java
  66. 1 1
      fs-service/src/main/java/com/fs/newAdv/vo/AccessTokenByAuthCodeVo.java
  67. 3 3
      fs-service/src/main/java/com/fs/qw/domain/QwCompany.java
  68. 5 0
      fs-service/src/main/java/com/fs/qw/param/QwGroupChatParam.java
  69. 4 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  70. 4 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopTempRules.java
  71. 6 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java
  72. 22 0
      fs-service/src/main/java/com/fs/sop/params/BatchOpenOrCloseOfficialParam.java
  73. 5 0
      fs-service/src/main/java/com/fs/sop/params/SendUserLogsInfoMsgParam.java
  74. 10 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempService.java
  75. 30 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  76. 23 1
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  77. 7 0
      fs-service/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java
  78. 8 3
      fs-service/src/main/resources/application-config-druid-bjzm-test.yml
  79. 2 2
      fs-service/src/main/resources/application-config-druid-bjzm.yml
  80. 1 1
      fs-service/src/main/resources/application-config-druid-cfryt.yml
  81. 2 2
      fs-service/src/main/resources/application-config-druid-ylrz.yml
  82. 2 2
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  83. 16 4
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  84. 2 1
      fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml
  85. 2 2
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
  86. 20 8
      fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml
  87. 53 0
      fs-service/src/main/resources/mapper/live/LiveAfterSalesMapper.xml
  88. 1 1
      fs-service/src/main/resources/mapper/live/LiveCompletionPointsRecordMapper.xml
  89. 7 7
      fs-service/src/main/resources/mapper/live/LiveDataMapper.xml
  90. 2 0
      fs-service/src/main/resources/mapper/live/LiveUserRedRecordMapper.xml
  91. 16 16
      fs-service/src/main/resources/mapper/qw/QwCompanyMapper.xml
  92. 3 0
      fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml
  93. 26 0
      fs-service/src/main/resources/mapper/sop/QwSopTempRulesMapper.xml
  94. 48 0
      fs-user-app/src/main/java/com/fs/app/controller/live/LiveRedController.java
  95. 1 1
      fs-user-app/src/main/java/com/fs/app/controller/store/WxUserScrmController.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/src/main/java/com/fs/app/controller/CallbackController.java

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

+ 48 - 27
fs-ad-new-api/src/main/java/com/fs/app/controller/WeChatController.java

@@ -4,9 +4,12 @@ import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.fs.app.facade.CallbackProcessingFacadeService;
 import com.fs.common.constant.SystemConstant;
 import com.fs.common.result.Result;
+import com.fs.newAdv.domain.AdvMiniConfig;
+import com.fs.newAdv.service.IAdvMiniConfigService;
 import com.fs.wx.miniapp.config.WxMaProperties;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -15,7 +18,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.time.LocalDateTime;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -34,34 +39,50 @@ public class WeChatController {
     @Autowired
     private WxMaProperties properties;
 
+    @Autowired
+    private IAdvMiniConfigService advMiniConfigService;
+
     @GetMapping("/getSchemeUrl")
     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");
-
-        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"));
+        List<AdvMiniConfig> list = advMiniConfigService.list(new LambdaQueryWrapper<AdvMiniConfig>()
+                .eq(AdvMiniConfig::getStatus, 1));
+        for (AdvMiniConfig advMiniConfig : list) {
+            try {
+                String access_token = advMiniConfig.getAccessToken();
+                // 判断token是否过期
+                if (advMiniConfig.getExpiresIn().isBefore(LocalDateTime.now().plusMinutes(10))) {
+                    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");
+                    advMiniConfig.setAccessToken(access_token);
+                    advMiniConfig.setExpiresIn(LocalDateTime.now().plusSeconds(obj.getLong("expires_in")));
+                    advMiniConfigService.updateById(advMiniConfig);
+                }
+                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());
+                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("");
     }
 }

+ 9 - 2
fs-ad-new-api/src/main/java/com/fs/app/facade/CallbackProcessingFacadeServiceImpl.java

@@ -153,7 +153,7 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
             }
         }
         // 模板缓存
-        Object ca = redisUtil.get(TEMPLATE_DATA + traceId);
+/*        Object ca = redisUtil.get(TEMPLATE_DATA + traceId);
         String templateData;
         if (ca != null) {
             templateData = String.valueOf(ca);
@@ -164,8 +164,14 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
             // 替换二维码链接
             updateQrCodeInTemplate(jsonObject, traceId, byId, byTraceId);
             templateData = JSONUtil.toJsonStr(jsonObject);
-        }
+        }*/
 
+        // 查询模板数据
+        LandingPageTemplate landingPageTemplate = landingPageTemplateService.getById(byId.getLaunchPageId());
+        JSONObject jsonObject = JSONUtil.parseObj(landingPageTemplate.getTemplateData());
+        // 替换二维码链接
+        updateQrCodeInTemplate(jsonObject, traceId, byId, byTraceId);
+        String templateData = JSONUtil.toJsonStr(jsonObject);
 
         // 保存或更新 线索信息
         LocalDateTime now = LocalDateTime.now();
@@ -221,6 +227,7 @@ public class CallbackProcessingFacadeServiceImpl implements CallbackProcessingFa
                                                Integer allocationRule,
                                                Long allocationRuleId,
                                                Lead byTraceId) {
+        log.info("开始获取广告二维码: {} {} {} {}", launchType, allocationRule, allocationRuleId ,byTraceId);
         // 二维码
         String qrCode = "";
         if (allocationRule == 1) {

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

@@ -37,7 +37,7 @@ public class ConversionRetryTask {
      * 转化回传重试任务
      * cron: 每10分钟执行
      */
-    @Scheduled(cron = "0 */5 * * * ?")
+    // @Scheduled(cron = "0 */5 * * * ?")
     @DistributeLock(scene = "task", key = "conversion_retry", waitTime = 0, errorMsg = "conversion_retry任务已执行")
     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小时统计站点数据
      */
-    @Scheduled(cron = "0 0/1 * * * ?")
+    @Scheduled(cron = "0 0 0/1 * * ?")
     @DistributeLock(scene = "task", key = "sync_today_data", waitTime = 0, errorMsg = "sync_today_data任务已执行")
     public void syncTodayData() throws InterruptedException {
         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;
+    }
+}

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

@@ -7,3 +7,4 @@ spring:
     active: dev
 #    active: druid-ylrz
 
+#

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

@@ -100,7 +100,7 @@ public class FsStoreAfterSalesScrmController extends BaseController
             return AjaxResult.error("请筛选数据导出");
         }
 
-        List<FsStoreAfterSalesVO> list = fsStoreAfterSalesService.selectFsStoreAfterSalesListVO(fsStoreAfterSales);
+        List<FsStoreAfterSalesVO> list = fsStoreAfterSalesService.selectFsStoreAfterSalesListVOExport(fsStoreAfterSales);
         if("北京卓美".equals(signProjectName)){
             List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
                     .map(vo -> {

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

@@ -106,8 +106,10 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         if (list != null) {
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
             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"));
+                }
+                if (StringUtils.isNotEmpty(vo.getUserPhone())){
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 if (CloudHostUtils.hasCloudHostName("康年堂")){

+ 84 - 0
fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java

@@ -164,6 +164,90 @@ public class LiveTask {
     @Autowired
     private FsJstAftersalePushScrmService fsJstAftersalePushScrmService;
 
+    /**
+     * 查询被拆分的订单,然后查询拆分订单的物流信息
+     */
+    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(){
         fsJstAftersalePushScrmService.pushJst();

+ 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.startPage(1, 10000, "");
-        List<LiveAfterSalesVo> list = liveAfterSalesService.selectLiveAfterSalesVoList(liveAfterSales);
+        List<LiveAfterSalesVo> list = liveAfterSalesService.selectLiveAfterSalesVoListExport(liveAfterSales);
         if("北京卓美".equals(signProjectName)){
             List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
                     .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.utils.SecurityUtils;
 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.param.LiveDataParam;
 import com.fs.live.service.ILiveDataService;
@@ -29,6 +31,8 @@ public class LiveDataController extends BaseController {
 
     @Autowired
     private ILiveDataService liveDataService;
+    @Autowired
+    private TokenService tokenService;
 
     /**
      * 直播数据页面卡片数据
@@ -128,11 +132,21 @@ public class LiveDataController extends BaseController {
     /**
      * 查询直播间用户详情列表(SQL方式)
      * @param liveId 直播间ID
+     * @param pageNum 页码
+     * @param pageSize 每页大小
      * @return 用户详情列表
      */
     @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
     @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);
     }
 

+ 9 - 5
fs-admin/src/main/java/com/fs/live/controller/OrderController.java

@@ -51,7 +51,7 @@ public class OrderController extends BaseController
     @Autowired
     private IMergedOrderService mergedOrderService;
     // 设置最大导出数量限制为20000条
-    private static final int maxExportCount = 20000;
+    private static final int maxExportCount = 50000;
 
 
     @Autowired
@@ -88,6 +88,7 @@ public class OrderController extends BaseController
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         PageHelper.startPage(1, maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        list = list.stream().filter(item -> StringUtils.isNotEmpty(item.getBankTransactionId())).collect(Collectors.toList());
         
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
@@ -126,6 +127,7 @@ public class OrderController extends BaseController
         // 先查询数据,限制查询20001条,用于判断是否超过限制
         PageHelper.startPage(1, maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        list = list.stream().filter(item -> StringUtils.isNotEmpty(item.getBankTransactionId())).collect(Collectors.toList());
 
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
@@ -235,20 +237,21 @@ public class OrderController extends BaseController
             MergedOrderExportVO exportVO = new MergedOrderExportVO();
             
             // 订单基本信息(参考 FsStoreOrderItemExportVO 的顺序)
+            exportVO.setOrderTypeName(vo.getOrderTypeName());
             exportVO.setOrderCode(vo.getOrderCode());
             exportVO.setStatus(vo.getStatus() != null ? String.valueOf(vo.getStatus()) : null);
             exportVO.setUserId(vo.getUserId());
             
             // 产品信息
-            exportVO.setProductName(vo.getProductName());
+            exportVO.setProductName(StringUtils.isEmpty(vo.getProductName()) ? "产品被删除" : vo.getProductName());
             exportVO.setBarCode(vo.getBarCode());
             exportVO.setProductSpec(StringUtils.isEmpty(vo.getProductSpec()) ? "默认" : vo.getProductSpec());
             exportVO.setTotalNum(vo.getTotalNum());
             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.setPayPostage(vo.getPayDelivery());
-            exportVO.setCateName(vo.getCateName());
+            exportVO.setCateName(StringUtils.isEmpty(vo.getCateName()) ? "产品被删除" : vo.getCateName());
             // 收货信息
             exportVO.setRealName(vo.getRealName());
             if (isPlainText) {
@@ -262,7 +265,8 @@ public class OrderController extends BaseController
             // 时间信息
             exportVO.setCreateTime(vo.getCreateTime());
             exportVO.setPayTime(vo.getPayTime());
-            
+            exportVO.setHfshh(vo.getHfshh());
+
             // 物流信息
             exportVO.setDeliverySn(vo.getDeliveryCode()); // 快递公司编号,合并订单暂无此字段
             exportVO.setDeliveryName(vo.getDeliveryName()); // 快递公司,合并订单暂无此字段

+ 5 - 4
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.LocalDateTime;
 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;
 
@@ -216,6 +213,8 @@ public class FsUserController extends AppBaseController {
             @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
     ) {
         long userId = Long.parseLong(getUserId());
+        // 中康的数据太多太卡不要这个
+//        return ResponseResult.ok(Collections.emptyList());
         return ResponseResult.ok(fsUserService.userRanking(userId, startTime, endTime, periodId, videoId, order, type));
     }
 
@@ -231,6 +230,8 @@ public class FsUserController extends AppBaseController {
             @ApiParam(value = "类型,1-按完播率,2-按正确率", required = true) @RequestParam Integer type
     ) {
         long userId = Long.parseLong(getUserId());
+        // 中康的数据太多太卡不要这个
+//        return ResponseResult.ok(Collections.emptyList());
         return ResponseResult.ok(fsUserService.courseRanking(userId, startTime, endTime, courseId, videoId, order, type));
     }
 

+ 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.github.pagehelper.PageHelper;
 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 lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -115,6 +117,15 @@ public class FsUserCoursePeriodController extends BaseController {
             } else {
                 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);
         Map<String, Object> result = new HashMap<>();
@@ -200,7 +211,8 @@ public class FsUserCoursePeriodController extends BaseController {
     @PutMapping("/updatePeriodIsOpenRestReminder")
     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()));
 
     }
 

+ 12 - 3
fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java

@@ -55,17 +55,26 @@ public class LiveDataController extends BaseController
     /**
      * 查询直播间用户详情列表(SQL方式)
      * @param liveId 直播间ID
+     * @param pageNum 页码
+     * @param pageSize 每页大小
      * @return 用户详情列表
      */
     @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
     @GetMapping("/getLiveUserDetailListBySql")
-    public R getLiveUserDetailListBySql(@RequestParam Long liveId, HttpServletRequest request) {
+    public R getLiveUserDetailListBySql(@RequestParam Long liveId, 
+                                        @RequestParam(defaultValue = "1") Integer pageNum,
+                                        @RequestParam(defaultValue = "100") Integer pageSize,
+                                        HttpServletRequest request) {
+        // 限制最大每页查询条数为1000
+        if (pageSize > 1000) {
+            pageSize = 1000;
+        }
+        PageHelper.startPage(pageNum, pageSize);
         CompanyUser user = tokenService.getLoginUser(request).getUser();
         if ("00".equals(user.getUserType())) {
             return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),null);
         }
-        return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),user.getUserId());
-    }
+        return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),user.getUserId());    }
 
     /**
      * 查询直播间详情数据(查询数据服务器处理方式)

+ 7 - 3
fs-company/src/main/java/com/fs/company/controller/live/OrderController.java

@@ -52,7 +52,7 @@ public class OrderController extends BaseController
     @Autowired
     private IMergedOrderService mergedOrderService;
     // 设置最大导出数量限制为20000条
-    private static final int maxExportCount = 20000;
+    private static final int maxExportCount = 50000;
 
 
 
@@ -96,6 +96,7 @@ public class OrderController extends BaseController
         param.setCompanyId(user.getCompanyId());
         PageHelper.startPage(1, maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        list = list.stream().filter(item -> StringUtils.isNotEmpty(item.getBankTransactionId())).collect(Collectors.toList());
 
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
@@ -138,6 +139,7 @@ public class OrderController extends BaseController
         param.setCompanyId(user.getCompanyId());
         PageHelper.startPage(1, maxExportCount + 1);
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        list = list.stream().filter(item -> StringUtils.isNotEmpty(item.getBankTransactionId())).collect(Collectors.toList());
 
         // 如果查询结果超过20000条,返回错误提示
         if (list != null && list.size() > maxExportCount) {
@@ -239,12 +241,13 @@ public class OrderController extends BaseController
             MergedOrderExportVO exportVO = new MergedOrderExportVO();
 
             // 订单基本信息(参考 FsStoreOrderItemExportVO 的顺序)
+            exportVO.setOrderTypeName(vo.getOrderTypeName());
             exportVO.setOrderCode(vo.getOrderCode());
             exportVO.setStatus(vo.getStatus() != null ? String.valueOf(vo.getStatus()) : null);
             exportVO.setUserId(vo.getUserId());
 
             // 产品信息
-            exportVO.setProductName(vo.getProductName());
+            exportVO.setProductName(StringUtils.isEmpty(vo.getProductName()) ? "产品被删除" : vo.getProductName());
             exportVO.setBarCode(vo.getBarCode());
             exportVO.setProductSpec(StringUtils.isEmpty(vo.getProductSpec()) ? "默认" : vo.getProductSpec());
             exportVO.setTotalNum(vo.getTotalNum());
@@ -252,7 +255,7 @@ public class OrderController extends BaseController
             exportVO.setCost(BigDecimal.ZERO);
             exportVO.setFPrice(BigDecimal.ZERO); // 结算价,合并订单暂无此字段
             exportVO.setPayPostage(vo.getPayDelivery());
-            exportVO.setCateName(vo.getCateName());
+            exportVO.setCateName(StringUtils.isEmpty(vo.getCateName()) ? "产品被删除" : vo.getCateName());
 
             // 收货信息
             exportVO.setRealName(vo.getRealName());
@@ -270,6 +273,7 @@ public class OrderController extends BaseController
             // 公司和销售信息
             exportVO.setCompanyName(vo.getCompanyName());
             exportVO.setCompanyUserNickName(vo.getCompanyUserNickName());
+            exportVO.setHfshh(vo.getHfshh());
 
             // 套餐信息
             exportVO.setPackageName(null); // 套餐名称,合并订单暂无此字段

+ 5 - 4
fs-company/src/main/java/com/fs/company/controller/qw/QwCustomerLinkController.java

@@ -17,6 +17,7 @@ import com.fs.qw.dto.QwCustomerLinkUserDto;
 import com.fs.qw.service.IQwCustomerLinkChannelService;
 import com.fs.qw.service.IQwCustomerLinkService;
 import com.fs.qw.service.IQwCustomerLinkUserService;
+import com.fs.qwApi.domain.QwLinkCreateResult;
 import com.fs.qwApi.param.QwLinkCreateParam;
 import com.fs.qwApi.service.QwApiService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -110,11 +111,11 @@ public class QwCustomerLinkController {
             // qwApiService.linkUpdate(qwLinkCreateParam, qwGroupLiveCode.getCorpId());
             success = qwCustomerLinkService.updateById(bean);
         } else {
-/*            QwLinkCreateResult qwLinkCreateResult = qwApiService.linkCreate(qwLinkCreateParam, qwGroupLiveCode.getCorpId());
+            QwLinkCreateResult qwLinkCreateResult = qwApiService.linkCreate(qwLinkCreateParam, qwGroupLiveCode.getCorpId());
             bean.setLinkId(qwLinkCreateResult.getLinkId());
-            bean.setUrl(qwLinkCreateResult.getUrl());*/
-            bean.setLinkId(IdUtil.randomUUID());
-            bean.setUrl("https://work.weixin.qq.com/ca/" + IdUtil.randomUUID());
+            bean.setUrl(qwLinkCreateResult.getUrl());
+/*            bean.setLinkId(IdUtil.randomUUID());
+            bean.setUrl("https://work.weixin.qq.com/ca/" + IdUtil.randomUUID())*/;
             success = qwCustomerLinkService.save(bean);
         }
 

+ 13 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempController.java

@@ -20,6 +20,7 @@ import com.fs.qw.vo.SortDayVo;
 import com.fs.sop.domain.QwSop;
 import com.fs.sop.domain.QwSopTemp;
 import com.fs.sop.domain.QwSopTempDay;
+import com.fs.sop.params.BatchOpenOrCloseOfficialParam;
 import com.fs.sop.params.QwSopShareTempParam;
 import com.fs.sop.service.IQwSopTempService;
 import com.fs.sop.vo.UpdateRedVo;
@@ -362,4 +363,16 @@ public class QwSopTempController extends BaseController
     public R getSelectableRange(){
         return R.ok().put("data", qwSopTempService.getSelectableRange());
     }
+
+    /**
+     * sop模板update一键开关官方群发
+     * @param param
+     * @return
+     */
+    @PreAuthorize("@ss.hasPermi('qw:sopTemp:edit') or @ss.hasPermi('qw:sopTemp:myEdit') or @ss.hasPermi('qw:sopTemp:deptEdit')")
+    @Log(title = "sop模板update一键开关官方群发", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchOpenOrCloseOfficial")
+    public R batchOpenOrCloseOfficial(@RequestBody BatchOpenOrCloseOfficialParam param){
+        return qwSopTempService.batchOpenOrCloseOfficial(param);
+    }
 }

+ 5 - 3
fs-live-app/src/main/java/com/fs/live/task/Task.java

@@ -415,7 +415,7 @@ public class Task {
         for (Live openRewardLive : openRewardLives) {
             String configJson = openRewardLive.getConfigJson();
             LiveWatchConfig config = JSON.parseObject(configJson, LiveWatchConfig.class);
-            if (config.getEnabled()) {
+            if (config.getEnabled() && 1 == config.getParticipateCondition()) {
                 List<LiveWatchUser> liveWatchUsers = liveWatchUserService.checkOnlineNoRewardUser(openRewardLive.getLiveId(), now);
                 if (liveWatchUsers == null || liveWatchUsers.isEmpty()) {
                     continue;
@@ -633,7 +633,7 @@ public class Task {
     }
 
     /**
-     * 定时扫描开启的直播间,检查是否到了打标签的时间
+     * 定时扫描开启的直播间,检查是否到了打标签的时间,然后把正在看直播的用户拆分为 直播用户和回放用户
      * 每10秒执行一次
      */
     @Scheduled(cron = "0/10 * * * * ?")
@@ -896,6 +896,9 @@ public class Task {
                             List<LiveWatchLog> batch = updateLog.subList(i, end);
                             liveWatchLogService.batchUpdateLiveWatchLog(batch);
                         }
+                        for (LiveWatchLog liveWatchLog : updateLog) {
+                            redisCache.setCacheObject("live:watch:log:cache:" + liveWatchLog.getLogId(), liveWatchLog, 1, TimeUnit.HOURS);
+                        }
                     }
                     
                 } catch (Exception e) {
@@ -923,7 +926,6 @@ public class Task {
             // 查询 LiveWatchLog
             LiveWatchLog queryLog = new LiveWatchLog();
             queryLog.setLiveId(liveId);
-            queryLog.setUserId(userId);
             queryLog.setQwUserId(String.valueOf(qwUserId));
             queryLog.setExternalContactId(exId);
 

+ 2 - 2
fs-live-app/src/main/java/com/fs/live/websocket/auth/WebSocketConfigurator.java

@@ -56,10 +56,10 @@ public class WebSocketConfigurator extends ServerEndpointConfig.Configurator {
             userProperties.put(AttrConstant.LOCATION, parameterMap.get(AttrConstant.LOCATION).get(0));
         }
         if (parameterMap.containsKey(AttrConstant.QW_USER_ID)) {
-            userProperties.put(AttrConstant.QW_USER_ID, parameterMap.get(AttrConstant.QW_USER_ID).get(0));
+            userProperties.put(AttrConstant.QW_USER_ID, Long.valueOf(parameterMap.get(AttrConstant.QW_USER_ID).get(0)));
         }
         if (parameterMap.containsKey(AttrConstant.EXTERNAL_CONTACT_ID)) {
-            userProperties.put(AttrConstant.EXTERNAL_CONTACT_ID, parameterMap.get(AttrConstant.EXTERNAL_CONTACT_ID).get(0));
+            userProperties.put(AttrConstant.EXTERNAL_CONTACT_ID, Long.valueOf(parameterMap.get(AttrConstant.EXTERNAL_CONTACT_ID).get(0)));
         }
 
         // 验证token

+ 63 - 59
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -83,7 +83,7 @@ public class WebSocketServer {
     private final ILiveVideoService liveVideoService = SpringUtils.getBean(ILiveVideoService.class);
     private final ILiveCompletionPointsRecordService completionPointsRecordService = SpringUtils.getBean(ILiveCompletionPointsRecordService.class);
     private static Random random = new Random();
-    
+
     // Redis key 前缀:用户进入直播间时间
     private static final String USER_ENTRY_TIME_KEY = "live:user:entry:time:%s:%s"; // liveId:userId
 
@@ -135,11 +135,17 @@ public class WebSocketServer {
 
             LiveWatchUser liveWatchUserVO = liveWatchUserService.join(fsUser,liveId, userId, location);
             room.put(userId, session);
-            
+
             // 存储用户进入直播间的时间到 Redis(用于计算在线时长)
+            // 如果已经存在进入时间,说明是重连,不应该覆盖,保持原来的进入时间
             String entryTimeKey = String.format(USER_ENTRY_TIME_KEY, liveId, userId);
-            redisCache.setCacheObject(entryTimeKey, System.currentTimeMillis(), 24, TimeUnit.HOURS);
-            
+            Long existingEntryTime = redisCache.getCacheObject(entryTimeKey);
+            if (existingEntryTime == null) {
+                // 首次连接,记录进入时间
+                redisCache.setCacheObject(entryTimeKey, System.currentTimeMillis(), 24, TimeUnit.HOURS);
+            }
+            // 如果是重连,不覆盖进入时间,保持原来的进入时间以便正确计算总时长
+
             // 直播间浏览量 +1
             redisCache.incr(PAGE_VIEWS_KEY + liveId, 1);
 
@@ -347,7 +353,7 @@ public class WebSocketServer {
                     long watchUserId = (long) userProperties.get("userId");
 
 
-                    
+
                     if (msg.getData() != null && !msg.getData().isEmpty()) {
                         try {
                             Long currentDuration = Long.parseLong(msg.getData());
@@ -356,7 +362,8 @@ public class WebSocketServer {
                             if (currentLive == null) {
                                 break;
                             }
-                            
+
+
                             // 判断直播是否已开始:status=2(直播中) 或 当前时间 >= 开播时间
                             boolean isLiveStarted = false;
                             if (currentLive.getStatus() != null && currentLive.getStatus() == 2) {
@@ -364,22 +371,21 @@ public class WebSocketServer {
                                 isLiveStarted = true;
                             } else if (currentLive.getStartTime() != null) {
                                 // 判断当前时间是否已超过开播时间
-                                LocalDateTime now = java.time.LocalDateTime.now();
+                                LocalDateTime now = LocalDateTime.now();
                                 isLiveStarted = now.isAfter(currentLive.getStartTime()) || now.isEqual(currentLive.getStartTime());
                             }
-                            
-                            if (!isLiveStarted) {
-                                log.debug("[心跳-观看时长] 直播未开始(开播倒计时中),不统计观看时长, liveId={}, status={}, startTime={}", 
-                                        liveId, currentLive.getStatus(), currentLive.getStartTime());
-                                break;
-                            }
-                            
-                            log.debug("[心跳-观看时长] 直播已开始,统计观看时长, liveId={}, userId={}, duration={}秒", 
-                                    liveId, watchUserId, currentDuration);
-                            
+
                             // 使用Hash结构存储:一个直播间一个Hash,包含所有用户的时长
                             String hashKey = "live:watch:duration:hash:" + liveId;
                             String userIdField = String.valueOf(watchUserId);
+
+                            if (!isLiveStarted) {
+                                redisCache.hashDelete(hashKey, userIdField);
+                                log.debug("[心跳-观看时长] 直播未开始,清除预播时长, liveId={}, userId={}", liveId, watchUserId);
+                                break;
+                            }
+
+                            // 直播已开始,记录观看时长
                             // 获取现有时长
                             Object existingDuration = redisCache.hashGet(hashKey, userIdField);
                             // 只有当新的时长更大时才更新
@@ -393,11 +399,11 @@ public class WebSocketServer {
 
                             }
                         } catch (Exception e) {
-                            log.error("[心跳-观看时长] 更新失败, liveId={}, userId={}, data={}", 
+                            log.error("[心跳-观看时长] 更新失败, liveId={}, userId={}, data={}",
                                     liveId, watchUserId, msg.getData(), e);
                         }
                     }
-                    
+
                     sendMessage(session, JSONObject.toJSONString(R.ok().put("data", msg)));
                     break;
                 case "sendMsg":
@@ -738,7 +744,7 @@ public class WebSocketServer {
      */
     public void broadcastWebMessage(Long liveId, String message) {
         ConcurrentHashMap<Long, Session> room = getRoom(liveId);
-        
+
         if (room.isEmpty()) {
             return;
         }
@@ -864,7 +870,7 @@ public class WebSocketServer {
         for (Map.Entry<Long, ConcurrentHashMap<Long, Session>> roomEntry : rooms.entrySet()) {
             Long liveId = roomEntry.getKey();
             ConcurrentHashMap<Long, Session> room = roomEntry.getValue();
-            
+
             // 如果房间为空,跳过
             if (room.isEmpty()) {
                 continue;
@@ -876,12 +882,12 @@ public class WebSocketServer {
             for (Map.Entry<Long, Session> userEntry : room.entrySet()) {
                 Long userId = userEntry.getKey();
                 Session session = userEntry.getValue();
-                
+
                 if (session == null) {
                     toRemove.add(userId);
                     continue;
                 }
-                
+
                 Long lastHeartbeat = heartbeatCache.get(session.getId());
                 if (lastHeartbeat != null && (currentTime - lastHeartbeat) > HEARTBEAT_TIMEOUT) {
                     toRemove.add(userId);
@@ -951,11 +957,11 @@ public class WebSocketServer {
      */
     public void broadcastLikeMessage(Long liveId, String message) {
         ConcurrentHashMap<Long, Session> room = getRoom(liveId);
-        
+
         if (room.isEmpty()) {
             return;
         }
-        
+
         // 使用快照遍历,避免并发修改
         for (Map.Entry<Long, Session> entry : room.entrySet()) {
             Session session = entry.getValue();
@@ -1116,31 +1122,31 @@ public class WebSocketServer {
             // 从 Redis 获取用户进入时间
             String entryTimeKey = String.format(USER_ENTRY_TIME_KEY, liveId, userId);
             Long entryTime = redisCache.getCacheObject(entryTimeKey);
-            
+
             if (entryTime == null) {
                 // 如果没有进入时间记录,可能是旧数据,跳过
                 return;
             }
-            
+
             long currentTimeMillis = System.currentTimeMillis();
             Date now = new Date();
-            
+
             // 计算在线时长(秒)
             long durationSeconds = (currentTimeMillis - entryTime) / 1000;
-            
+
             if (durationSeconds <= 0) {
                 return;
             }
-            
+
             // 获取当前直播/回放状态
             Map<String, Integer> flagMap = liveWatchUserService.getLiveFlagWithCache(liveId);
             Integer currentLiveFlag = flagMap.get("liveFlag");
             Integer currentReplayFlag = flagMap.get("replayFlag");
-            
+
             // 查询用户记录
             LiveWatchUserEntry liveWatchUser = liveWatchUserService.selectLiveWatchAndCompanyUserByFlag(
                     liveId, userId, currentLiveFlag, currentReplayFlag);
-            
+
             if (liveWatchUser != null) {
                 // 累加在线时长
                 Long onlineSeconds = liveWatchUser.getOnlineSeconds();
@@ -1149,7 +1155,7 @@ public class WebSocketServer {
                 }
                 liveWatchUser.setOnlineSeconds(onlineSeconds + durationSeconds);
                 liveWatchUser.setUpdateTime(now);
-                
+
                 // 更新数据库
                 liveWatchUserService.updateLiveWatchUserEntry(liveWatchUser);
                 // 如果 LiveWatchUserEntry 存在,并且当前是直播状态(liveFlag = 1),更新 LiveWatchLog
@@ -1161,15 +1167,15 @@ public class WebSocketServer {
 //                            liveWatchUser.getOnlineSeconds());
 //                }
             }
-            
+
             // 删除 Redis 中的进入时间记录
             redisCache.deleteObject(entryTimeKey);
         } catch (Exception e) {
-            log.error("更新用户在线时长异常:liveId={}, userId={}, error={}", 
+            log.error("更新用户在线时长异常:liveId={}, userId={}, error={}",
                     liveId, userId, e.getMessage(), e);
         }
     }
-    
+
     /**
      * 在连接时更新 LiveWatchLog 的 logType
      * 如果 logType 类型不是 2,修改 logType 类型为 1(看课中)
@@ -1178,10 +1184,9 @@ public class WebSocketServer {
         try {
             LiveWatchLog queryLog = new LiveWatchLog();
             queryLog.setLiveId(liveId);
-            queryLog.setUserId(userId);
             queryLog.setQwUserId(String.valueOf(qwUserId));
             queryLog.setExternalContactId(externalContactId);
-            
+
             List<LiveWatchLog> logs = liveWatchLogService.selectLiveWatchLogList(queryLog);
             if (logs != null && !logs.isEmpty()) {
                 for (LiveWatchLog log : logs) {
@@ -1193,11 +1198,11 @@ public class WebSocketServer {
                 }
             }
         } catch (Exception e) {
-            log.error("更新 LiveWatchLog logType 异常(连接时):liveId={}, userId={}, error={}", 
+            log.error("更新 LiveWatchLog logType 异常(连接时):liveId={}, userId={}, error={}",
                     liveId, userId, e.getMessage(), e);
         }
     }
-    
+
     /**
      * 实时更新用户看课状态(在心跳时调用)
      * 在直播期间实时更新用户的看课状态,而不是等到关闭 WebSocket 或清理无效会话时才更新
@@ -1210,36 +1215,36 @@ public class WebSocketServer {
             // 获取当前直播/回放状态
             Map<String, Integer> flagMap = liveWatchUserService.getLiveFlagWithCache(liveId);
             Integer currentLiveFlag = flagMap.get("liveFlag");
-            
+
             // 只在直播状态(liveFlag = 1)时更新
             if (currentLiveFlag == null || currentLiveFlag != 1) {
                 return;
             }
-            
+
             // 获取用户的 companyId 和 companyUserId(使用带缓存的查询方法)
             LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserIdWithCache(liveId, userId);
             if (liveUserFirstEntry == null) {
                 return;
             }
-            
+
             Long companyId = liveUserFirstEntry.getCompanyId();
             Long companyUserId = liveUserFirstEntry.getCompanyUserId();
-            
+
             // 如果 companyId 和 companyUserId 有效,则更新看课状态
             if (companyId != null && companyId > 0 && companyUserId != null && companyUserId > 0) {
                 // 检查是否达到关键观看时长节点,在这些节点实时更新
                 // 关键节点:3分钟(180秒)、20分钟(1200秒)、30分钟(1800秒)
                 boolean isKeyDuration = (watchDuration == 180 || watchDuration == 1200 || watchDuration == 1800) ||
                                        (watchDuration > 180 && watchDuration % 60 == 0); // 每分钟更新一次
-                
+
                 // 使用 Redis 缓存控制更新频率,避免频繁更新数据库
                 // 策略:在关键节点立即更新,其他时候每60秒更新一次
                 String updateLockKey = "live:watch:log:update:lock:" + liveId + ":" + userId;
                 String lastUpdateKey = "live:watch:log:last:duration:" + liveId + ":" + userId;
-                
+
                 // 获取上次更新的时长
                 Long lastUpdateDuration = redisCache.getCacheObject(lastUpdateKey);
-                
+
                 // 如果达到关键节点,或者距离上次更新已超过60秒,则更新
                 boolean shouldUpdate = false;
                 if (isKeyDuration) {
@@ -1249,11 +1254,11 @@ public class WebSocketServer {
                     // 每60秒更新一次
                     shouldUpdate = true;
                 }
-                
+
                 if (shouldUpdate) {
                     // 使用分布式锁,避免并发更新(锁超时时间10秒)
                     Boolean canUpdate = redisCache.setIfAbsent(updateLockKey, "1", 10, TimeUnit.SECONDS);
-                    
+
                     if (Boolean.TRUE.equals(canUpdate)) {
                         // 异步更新,避免阻塞心跳处理
                         CompletableFuture.runAsync(() -> {
@@ -1262,7 +1267,7 @@ public class WebSocketServer {
                                 // 更新上次更新的时长
                                 redisCache.setCacheObject(lastUpdateKey, watchDuration, 2, TimeUnit.HOURS);
                             } catch (Exception e) {
-                                log.error("实时更新看课状态异常:liveId={}, userId={}, error={}", 
+                                log.error("实时更新看课状态异常:liveId={}, userId={}, error={}",
                                         liveId, userId, e.getMessage(), e);
                             } finally {
                                 // 释放锁
@@ -1273,11 +1278,11 @@ public class WebSocketServer {
                 }
             }
         } catch (Exception e) {
-            log.error("实时更新看课状态异常:liveId={}, userId={}, error={}", 
+            log.error("实时更新看课状态异常:liveId={}, userId={}, error={}",
                     liveId, userId, e.getMessage(), e);
         }
     }
-    
+
     /**
      * 根据在线时长更新 LiveWatchLog 的 logType
      * @param liveId 直播间ID
@@ -1286,7 +1291,7 @@ public class WebSocketServer {
      * @param companyUserId 销售ID
      * @param onlineSeconds 在线时长(秒)
      */
-    private void updateLiveWatchLogTypeByDuration(Long liveId, Long userId, Long companyId, 
+    private void updateLiveWatchLogTypeByDuration(Long liveId, Long userId, Long companyId,
                                                    Long companyUserId, Long onlineSeconds) {
         try {
             // 获取直播视频总时长(videoType = 1 的视频,使用带缓存的查询方法)
@@ -1298,14 +1303,13 @@ public class WebSocketServer {
                         .mapToLong(LiveVideo::getDuration)
                         .sum();
             }
-            
+
             // 查询 LiveWatchLog
             LiveWatchLog queryLog = new LiveWatchLog();
             queryLog.setLiveId(liveId);
-            queryLog.setUserId(userId);
             queryLog.setCompanyId(companyId);
             queryLog.setCompanyUserId(companyUserId);
-            
+
             List<LiveWatchLog> logs = liveWatchLogService.selectLiveWatchLogList(queryLog);
             if (logs == null || logs.isEmpty()) {
                 return;
@@ -1314,7 +1318,7 @@ public class WebSocketServer {
             for (LiveWatchLog log : logs) {
                 boolean needUpdate = false;
                 Integer newLogType = log.getLogType();
-                
+
                 // ① 如果在线时长 <= 3分钟,修改 logType 为 4(看课中断)
                 if (onlineSeconds <= 180) { // 3分钟 = 180秒
                     newLogType = 4;
@@ -1332,7 +1336,7 @@ public class WebSocketServer {
                     log.setFinishTime(now);
                     needUpdate = true;
                 }
-                
+
                 // 如果 logType 已经是 2(完课),不再更新
                 if (needUpdate && (log.getLogType() == null || log.getLogType() != 2)) {
                     log.setLogType(newLogType);
@@ -1340,7 +1344,7 @@ public class WebSocketServer {
                 }
             }
         } catch (Exception e) {
-            log.error("根据在线时长更新 LiveWatchLog logType 异常:liveId={}, userId={}, error={}", 
+            log.error("根据在线时长更新 LiveWatchLog logType 异常:liveId={}, userId={}, error={}",
                     liveId, userId, e.getMessage(), e);
         }
     }

+ 6 - 0
fs-qw-api/Dockerfile

@@ -0,0 +1,6 @@
+FROM openjdk:8-jre
+# java版本,最好使用openjdk,而不是类似于Java:1.8
+COPY ./target/fs-qw-api.jar fs-qw-api.jar
+# 向外暴露的接口,最好与项目yml文件中的端口一致
+ENTRYPOINT ["java","-jar","fs-qw-api.jar"]
+# 执行启动命令java -jar

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

@@ -694,6 +694,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         int type = content.getType();
         Long courseId = content.getCourseId();
         Long videoId = content.getVideoId();
+        Long liveId = content.getLiveId();
         Integer isOfficial = content.getIsOfficial() != null ? Integer.valueOf(content.getIsOfficial()) : 0;
 
 
@@ -746,13 +747,13 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         if (StringUtils.isNotEmpty(logVo.getChatId())) {
             QwGroupChat groupChat = groupChatMap.get(logVo.getChatId());
-            ruleTimeVO.setSendType(6);
-            ruleTimeVO.setType(2);
             if (groupChat.getChatUserList() != null && !groupChat.getChatUserList().isEmpty()) {
                 QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null,null);
+                ruleTimeVO.setSendType(6);
+                ruleTimeVO.setType(2);
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                         type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName,
-                        null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies);
+                        null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies,liveId);
             }
 //            if (content.getIndex() == 0) {
 //                QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null);
@@ -782,7 +783,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId(),contactId.getIsDaysNotStudy());
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                             type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId,
-                            null,config, miniMap, grade, sendMsgType,companies);
+                            null,config, miniMap, grade, sendMsgType,companies,liveId);
                 } catch (Exception e) {
                     log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
                 }
@@ -898,7 +899,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                       String qwUserName, Long fsUserId, boolean isGroupChat, String miniAppId,
                                       QwGroupChat groupChat,CourseConfig config,
                                       Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
-                                      Integer grade, Integer sendMsgType ,List<Company> companies ) {
+                                      Integer grade, Integer sendMsgType ,List<Company> companies ,Long liveId) {
         switch (type) {
             case 1:
                 handleNormalMessage(sopLogs, content,companyUserId,companyId,isGroupChat,qwUserId,groupChat,externalId,logVo);
@@ -920,6 +921,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             case 7:
                 handleVoiceMessage(sopLogs, content, companyUserId);
                 break;
+            //直播间发送类型
+            case 20:
+                handleLiveMessage(sopLogs, content,companyUserId,companyId,isGroupChat,qwUserId,groupChat,externalId,logVo,liveId);
             default:
                 log.error("未知的消息类型 {},跳过处理。", type);
                 break;
@@ -1003,6 +1007,83 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         enqueueQwSopLogs(sopLogs);
     }
 
+    /**
+     * 处理直播消息
+     */
+    public void handleLiveMessage(QwSopLogs sopLogs,QwSopTempSetting.Content content, String companyUserId, String companyId,
+                                  boolean isGroupChat,String qwUserId,QwGroupChat groupChat,String externalId,SopUserLogsVo logVo,Long liveId){
+        // 深拷贝 Content 对象,避免使用 JSON
+        QwSopTempSetting.Content clonedContent = deepCopyContent(content);
+        if (clonedContent == null) {
+            log.error("Failed to clone content, skipping handleCourseMessage.");
+            return;
+        }
+        clonedContent.setLiveId(liveId);
+        List<QwSopTempSetting.Content.Setting> settings = clonedContent.getSetting();
+        if (settings == null || settings.isEmpty()) {
+            log.error("Cloned content settings are empty, skipping.");
+            return;
+        }
+
+        //直播发送类型
+        sopLogs.setSendType(20);
+
+        // 顺序处理每个 Setting,避免过多的并行导致线程开销
+        for (QwSopTempSetting.Content.Setting setting : settings) {
+            switch (setting.getContentType()) {
+                //直播小程序单独
+                case "12":
+                    clonedContent.setLiveId(setting.getLiveId());
+                    String sortLiveLink;
+                    sortLiveLink = "/pages_course/living.html?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + setting.getLiveId() + "&corpId=" + logVo.getCorpId()+"&qwUserId=" + qwUserId;
+                    String json = configService.selectConfigByKey("his.config");
+                    FSSysConfig sysConfig = JSON.parseObject(json, FSSysConfig.class);
+                    if (isGroupChat) {
+                        try {
+                            groupChat.getChatUserList().stream().filter(e -> e.getUserList() != null && !e.getUserList().isEmpty()).forEach(e -> {
+                                Map<String, GroupUserExternalVo> userMap = PubFun.listToMapByGroupObject(e.getUserList(), GroupUserExternalVo::getUserId);
+                                GroupUserExternalVo vo = userMap.get(groupChat.getOwner());
+                                if (vo != null && vo.getId() != null) {
+                                    sopLogs.setFsUserId(vo.getFsUserId());
+                                    //写入直播待看课记录
+                                    createLiveWatchLogAndEnQueue(companyId, companyUserId, vo.getId().toString(), setting.getLiveId(), sysConfig.getAppId(), 2, qwUserId,logVo.getCorpId());
+                                }
+                            });
+                            sortLiveLink += "&chatId=" + groupChat.getChatId();
+                        } catch (Exception e) {
+                            log.error("直播小程序群聊新增报错,{}", e.getMessage(), e);
+                        }
+                    } else {
+                        try {
+                            createLiveWatchLogAndEnQueue(companyId, companyUserId, externalId, setting.getLiveId(), sysConfig.getAppId(), 1, qwUserId,logVo.getCorpId());
+                            sortLiveLink += "&externalId=" + externalId;
+                        } catch (Exception e) {
+                            log.error("直播小程序个人新增报错,{}", e.getMessage(), e);
+                        }
+                    }
+
+                    String miniprogramLiveTitle = setting.getMiniprogramTitle();
+                    int maxLiveLength = 17;
+                    setting.setMiniprogramTitle(miniprogramLiveTitle.length() > maxLiveLength ? miniprogramLiveTitle.substring(0, maxLiveLength) + "..." : miniprogramLiveTitle);
+                    setting.setMiniprogramAppid(sysConfig.getAppId());
+                    setting.setMiniprogramPage(sortLiveLink);
+                    setting.setContentType("4");
+                    try {
+                        setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl()) ? "https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png" : setting.getMiniprogramPicUrl());
+                    } catch (Exception e) {
+                        log.error("赋值-小程序封面地址失败-" + e);
+                    }
+
+                    break;
+                default:
+                    break;
+            }
+        }
+        sopLogs.setContentJson(JSON.toJSONString(clonedContent));
+
+        enqueueQwSopLogs(sopLogs);
+    }
+
     private void handleAIMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content) {
         sopLogs.setContentJson(JSON.toJSONString(content));
         sopLogs.setSort(3);
@@ -1988,17 +2069,18 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     )
     public void batchInsertLiveWatchLog(List<LiveWatchLog> liveWatchLogToInsert) {
         try {
-            List<LiveWatchLog> lastInsertList = new ArrayList<>();
+            //更改为set 避免同一批生成的消息里面有重复数据 插入会报错
+            Set<LiveWatchLog> lastInsertSet = new HashSet<>();
             //判断是否存在数据 liveId + his_qw_external_contact_id + qwUserId 唯一
             for (LiveWatchLog liveWatchLog : liveWatchLogToInsert) {
                 //判断是否存在数据 存在的数据直接更新发送时间
                 if(liveWatchLogMapper.updateLiveWatchLogCondition(liveWatchLog) > 0){
                     continue;
                 }
-                lastInsertList.add(liveWatchLog);
+                lastInsertSet.add(liveWatchLog);
             }
-            if(!lastInsertList.isEmpty()){
-                liveWatchLogMapper.insertLiveWatchLogBatch(lastInsertList);
+            if(!lastInsertSet.isEmpty()){
+                liveWatchLogMapper.insertLiveWatchLogBatch(new ArrayList<>(lastInsertSet));
             }
 //            log.info("批量插入 LiveWatchLog 完成,共插入 {} 条记录。", liveWatchLogToInsert.size());
         } catch (Exception e) {

+ 2 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java

@@ -122,4 +122,6 @@ public class FsUserCoursePeriod
 
     // 控制休息提示是否打开要暂停  0-关闭 1-打开 null-默认打开
     private Integer IsOpenRestReminder;
+    //  控制休息提示是否打开要暂停  0-关闭 1-打开 Json串 key值为companyId
+    private String isOpenRestFlag;
 }

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

@@ -106,5 +106,5 @@ public interface IFsUserCoursePeriodService
 
     List<SysDictData> selectFsUserCoursePeriodListLabel(FsUserCoursePeriod fsUserCoursePeriod);
 
-    int updatePeriod(FsUserCoursePeriod fsUserCoursePeriod);
+    int updatePeriod(FsUserCoursePeriod fsUserCoursePeriod, Long companyId);
 }

+ 21 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -16,7 +16,10 @@ import com.fs.course.param.PeriodStatisticCountParam;
 import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.vo.FsCourseStaticsCountVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
+import com.hc.openapi.tool.fastjson.JSON;
+import com.hc.openapi.tool.fastjson.JSONObject;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -352,7 +355,24 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
      * @Date 2025/12/19 10:52
      */
     @Override
-    public int updatePeriod(FsUserCoursePeriod fsUserCoursePeriod) {
+    public int updatePeriod(FsUserCoursePeriod fsUserCoursePeriod, Long companyId) {
+        Integer flag=fsUserCoursePeriod.getIsOpenRestReminder(); // 0-关闭 1-打开
+        FsUserCoursePeriod period=fsUserCoursePeriodMapper.selectFsUserCoursePeriodById(fsUserCoursePeriod.getPeriodId());
+        if (period==null){
+            return 0;
+        }
+
+        JSONObject jsonObject;
+        if (StringUtils.isNotBlank(period.getIsOpenRestFlag())){
+            jsonObject= JSON.parseObject(period.getIsOpenRestFlag());
+            jsonObject.put(companyId.toString(),flag);
+        }else {
+            jsonObject=new JSONObject();
+            jsonObject.put(companyId.toString(),flag);
+        }
+
+        fsUserCoursePeriod.setIsOpenRestFlag(jsonObject.toJSONString());
+
         return fsUserCoursePeriodMapper.updateFsUserCoursePeriod(fsUserCoursePeriod);
     }
 }

+ 13 - 4
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -153,7 +153,7 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
     private static final String SHORT_LINK_PREFIX = "/courseH5/pages/course/learning?s=";
     // 排除看课数量限制的公司集合
     private static final Set<String> EXCLUDE_PROJECTS = new HashSet<>(Arrays.asList(
-            "福本源", "宽益堂", "叮当国医"
+            "福本源", "宽益堂", "叮当国医", "易行健"
     ));
     @Autowired
     ICompanyService companyService;
@@ -1014,6 +1014,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                     log.info("用户:" + param.getVideoId());
                     log.info("企微用户:" + param.getQwUserId());
                     param.setQwExternalId(UnionEXt.getId());
+//                    param.setQwUserId(String.valueOf(UnionEXt.getQwUserId()));
+//                    param.setCompanyUserId(UnionEXt.getCompanyUserId());
+//                    param.setCompanyId(UnionEXt.getCompanyId());
                     FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(UnionEXt.getId(), param.getVideoId(), param.getQwUserId());
                     if (log == null) {
                         param.setUserId(user.getUserId());
@@ -2540,6 +2543,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         if (fsUser == null) {
             return ResponseResult.fail(401, "当前用户信息不存在");
         }
+        if (fsUser.getStatus() == 0) {
+            return ResponseResult.fail(503, "会员被停用,无权限,请联系客服!");
+        }
         //公开课
         if (param.getIsOpenCourse() != null && param.getIsOpenCourse() == 1) {
             FsCourseWatchLog watchCourseVideo = courseWatchLogMapper.getCourseWatchLogByUser(param.getUserId(), param.getVideoId(), null);
@@ -2952,17 +2958,20 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
 
             if(watchLog.getPeriodId()!=null){
                 FsUserCoursePeriod period= fsUserCoursePeriodMapper.selectFsUserCoursePeriodById(watchLog.getPeriodId());
-                if(period!=null && period.getIsOpenRestReminder()!=null){
-                    if(period.getIsOpenRestReminder()==0){
+                if(period!=null && watchLog.getCompanyId()!=null && StringUtils.isNotBlank(period.getIsOpenRestFlag()) ){
+                    //  Json 字符串  key值公司id value值  0-关闭 1-打开 没有获取到不管
+                    JSONObject jsonObject= JSON.parseObject(period.getIsOpenRestFlag());
+                    Integer flag=(Integer) jsonObject.get(watchLog.getCompanyId().toString());
+                    if(flag==0){
                         result=false;
                     }else {
                         result=true;
                     }
+
                 }
             }
         }
 
-
         return result;
     }
 

+ 3 - 0
fs-service/src/main/java/com/fs/course/vo/FsCoursePlaySourceConfigVO.java

@@ -64,4 +64,7 @@ public class FsCoursePlaySourceConfigVO {
 
     @ApiModelProperty("小程序状态:0正常,1半封禁,2封禁")
     private Integer status;
+
+    @ApiModelProperty("小程序支付配置id")
+    private Long merchantConfigId;
 }

+ 2 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCoursePeriodVO.java

@@ -93,4 +93,6 @@ public class FsUserCoursePeriodVO implements Serializable {
 
     // 控制休息提示是否打开要暂停  0-关闭 1-打开 null-默认打开
     private Integer IsOpenRestReminder;
+
+    private String IsOpenRestFlag;
 }

+ 161 - 5
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -555,8 +555,6 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             List<ErpOrderQuery> erpOrders = query.getOrders().stream()
                     .map(this::convertToErpOrderQueryScrm)
                     .collect(Collectors.toList());
-            if ("Cancelled".equals(query.getOrders().get(0).getStatus()))
-                fsStoreOrderScrmService.cancelOrderByCode(query.getOrders().get(0).getOuterPayId());
             response.setOrders(erpOrders);
         } else {
             response.setOrders(Collections.emptyList());
@@ -603,9 +601,9 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             List<ErpOrderQuery> erpOrders = query.getOrders().stream()
                     .map(this::convertToErpOrderQueryLive)
                     .collect(Collectors.toList());
-            if ("Cancelled".equals(query.getOrders().get(0).getStatus())) {
-                liveOrderMapper.cancelOrderByCode(query.getOrders().get(0).getOuterPayId());
-            }
+//            if ("Split".equals(query.getOrders().get(0).getStatus())) {
+//                this.splitLiveOrder(query.getOrders().get(0));
+//            }
             response.setOrders(erpOrders);
         } else {
             response.setOrders(Collections.emptyList());
@@ -614,6 +612,164 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return response;
     }
 
+    private void splitLiveOrder(OrderQueryResponseDTO.Order order) {
+        // ① 首先查询原来的订单
+        LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderCode(order.getSoId());
+        if (liveOrder == null) {
+            log.error("直播拆分订单:未查询到数据,{}", order.getSoId());
+            return;
+        }
+
+        try {
+            // ② 重新组装请求,使用 OrderQueryRequestDTO 查询拆分订单
+            OrderQueryRequestDTO requestDTO = new OrderQueryRequestDTO();
+            requestDTO.setOrderItemFlds(Arrays.asList("status"));
+            requestDTO.setSoIds(Collections.singletonList(order.getSoId()));
+
+            // 调用ERP服务查询拆分订单
+            OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
+
+            if (query == null || query.getOrders() == null || query.getOrders().isEmpty()) {
+                log.error("直播拆分订单:查询拆分订单失败, orderCode={}", order.getSoId());
+                return;
+            }
+
+            // ③ 把原来的订单状态修改为 6(被拆分)
+            liveOrder.setStatus(6);
+            liveOrderMapper.updateLiveOrder(liveOrder);
+            log.info("直播拆分订单:原订单状态已更新为被拆分, orderCode={}, orderId={}", order.getSoId(), liveOrder.getOrderId());
+
+            // ④ 查询出来的订单里面,除了原来的订单,将查询出来的订单新增到数据库里面
+            for (OrderQueryResponseDTO.Order splitOrder : query.getOrders()) {
+                // 跳过原来的订单(状态为 Split 的订单)
+                if ("Split".equals(splitOrder.getStatus())) {
+                    continue;
+                }
+
+                // 检查是否已经存在该拆分订单
+                LiveOrder existingOrder = liveOrderMapper.selectLiveOrderByOrderCode(splitOrder.getSoId());
+                if (existingOrder != null) {
+                    log.info("直播拆分订单:拆分订单已存在,跳过, orderCode={}", splitOrder.getSoId());
+                    continue;
+                }
+
+                // 创建新的拆分订单
+                LiveOrder newOrder = new LiveOrder();
+                // 复制原订单的基本信息
+                newOrder.setLiveId(liveOrder.getLiveId());
+                newOrder.setStoreId(liveOrder.getStoreId());
+                newOrder.setOrderCode(splitOrder.getSoId()); // 保存订单ID(soId)
+                newOrder.setUserId(liveOrder.getUserId()); // buyerId -> userId
+                newOrder.setUserName(liveOrder.getUserName());
+                newOrder.setRealName(liveOrder.getRealName());
+                newOrder.setUserPhone(liveOrder.getUserPhone());
+                newOrder.setUserAddress(liveOrder.getUserAddress());
+                newOrder.setCompanyId(liveOrder.getCompanyId());
+                newOrder.setCompanyUserId(liveOrder.getCompanyUserId());
+
+                // 设置支付金额
+                if (splitOrder.getPayAmount() != null) {
+                    newOrder.setPayMoney(splitOrder.getPayAmount());
+                    newOrder.setPayPrice(splitOrder.getPayAmount());
+                }
+
+                // 根据订单状态枚举设置状态
+                ErpQueryOrderStatusEnum statusEnum = ErpQueryOrderStatusEnum.getByCode(splitOrder.getStatus());
+                if (statusEnum != null) {
+                    // 根据状态类型设置订单状态
+                    // WaitPay -> 1 (待支付)
+                    // Sent -> 3 (待收货)
+                    // Cancelled -> 0 (已取消)
+                    // 其他状态根据业务需求设置
+                    if ("WaitPay".equals(splitOrder.getStatus())) {
+                        newOrder.setStatus(1); // 待支付
+                    } else if ("Sent".equals(splitOrder.getStatus())) {
+                        newOrder.setStatus(3); // 待收货
+                    } else if ("Cancelled".equals(splitOrder.getStatus())) {
+//                        newOrder.setStatus(0); // 已取消
+                    } else {
+                        newOrder.setStatus(2); // 默认待发货
+                    }
+                } else {
+                    newOrder.setStatus(2); // 默认待发货
+                }
+
+                // 设置物流信息
+                if (StringUtils.isNotEmpty(splitOrder.getLogisticsCompany())) {
+                    newOrder.setDeliveryName(splitOrder.getLogisticsCompany());
+                }
+                if (StringUtils.isNotEmpty(splitOrder.getLId())) {
+                    newOrder.setDeliverySn(splitOrder.getLId());
+                }
+                if (StringUtils.isNotEmpty(splitOrder.getLcId())) {
+                    newOrder.setDeliveryCode(splitOrder.getLcId());
+                }
+
+                // 设置发货时间
+                if (StringUtils.isNotEmpty(splitOrder.getSendDate())) {
+                    try {
+                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                        Date sendDate = formatter.parse(splitOrder.getSendDate());
+                        newOrder.setDeliverySendTime(sendDate);
+                        newOrder.setDeliveryTime(splitOrder.getSendDate());
+                    } catch (Exception e) {
+                        log.error("解析发货时间失败: {}", splitOrder.getSendDate(), e);
+                    }
+                }
+
+                // 设置支付时间
+                if (StringUtils.isNotEmpty(splitOrder.getPayDate())) {
+                    try {
+                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                        Date payDate = formatter.parse(splitOrder.getPayDate());
+                        newOrder.setPayTime(payDate.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime());
+                    } catch (Exception e) {
+                        log.error("解析支付时间失败: {}", splitOrder.getPayDate(), e);
+                    }
+                }
+
+                // 设置扩展订单ID
+                newOrder.setExtendOrderId(String.valueOf(splitOrder.getOId()));
+
+                // 计算订单总数量
+                if (splitOrder.getItems() != null && !splitOrder.getItems().isEmpty()) {
+                    int totalQty = splitOrder.getItems().stream()
+                            .mapToInt(OrderQueryResponseDTO.OrderItem::getQty)
+                            .sum();
+                    newOrder.setTotalNum(String.valueOf(totalQty));
+                }
+
+                // 设置订单总价
+                if (splitOrder.getAmount() != null) {
+                    newOrder.setTotalPrice(splitOrder.getAmount());
+                }
+
+                // 设置创建时间和更新时间
+                newOrder.setCreateTime(new Date());
+                newOrder.setUpdateTime(new Date());
+
+                // 插入订单
+                liveOrderMapper.insertLiveOrder(newOrder);
+
+                // 保存订单项(SKU)
+                if (splitOrder.getItems() != null && !splitOrder.getItems().isEmpty()) {
+                    for (OrderQueryResponseDTO.OrderItem item : splitOrder.getItems()) {
+                        LiveOrderItem orderItem = new LiveOrderItem();
+                        orderItem.setOrderId(newOrder.getOrderId());
+                        orderItem.setOrderCode(splitOrder.getSoId());
+                        orderItem.setNum(item.getQty() != null ? item.getQty().longValue() : 0L);
+                        // 可以根据需要设置其他字段,如 productId, goodsId 等
+                        // 这里需要根据业务逻辑从 item 中获取或从原订单项中复制
+                        liveOrderItemMapper.insertLiveOrderItem(orderItem);
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("直播拆分订单处理异常, orderCode={}", order.getSoId(), e);
+        }
+    }
+
     /**
      * 将OrderQueryResponseDTO.Order转换为ErpOrderQuery
      *

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

@@ -847,7 +847,7 @@ public class AiHookServiceImpl implements AiHookService {
                                         .append("\uD83C\uDF39\uD83C\uDF39\uD83C\uDF39");
                             case 3:
                                 ExpressInfoDTO expressInfo = getExpress(fsStoreOrder.getOrderId());
-                                sBuilder.append("您购买的有一个包裹 ");
+                                sBuilder.append("您有一个包裹 ");
                                 sBuilder.append(" 已经查询到了,正在配送中了。\n");
                                 if(expressInfo != null && expressInfo.getTraces() != null && !expressInfo.getTraces().isEmpty()){
                                     List<TracesDTO> traces = expressInfo.getTraces();

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

@@ -312,9 +312,9 @@ public interface FsUserMapper
 
     UserDetailsVO getCountWatchCourse (@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag,@Param("userCompanyId")  Long userCompanyId);
 
-    UserDetailsVO getCountAnswer(@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
+    UserDetailsVO getCountAnswer(@Param("userCompanyId") Long userCompanyId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
 
-    UserDetailsVO getCountRedPacket(@Param("userId") Long userId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
+    UserDetailsVO getCountRedPacket(@Param("userCompanyId") Long userCompanyId, @Param("fsUserId") Long fsUserId, @Param("dateTag") String dateTag);
 
     FsUserSummaryCountVO countUserSummary(@Param("userId") Long userId, @Param("companyId") Long companyId);
 

+ 3 - 3
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -835,10 +835,10 @@ public class FsUserServiceImpl implements IFsUserService {
     @Override
     public UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag, Long userCompanyId) {
         UserDetailsVO countWatchCourse = fsUserMapper.getCountWatchCourse(userId, fsUserId, dateTag, userCompanyId);
-        FsUserCompanyUser fsUserCompanyUser = userCompanyUserService.selectFsUserCompanyUserById(userCompanyId);
+//        FsUserCompanyUser fsUserCompanyUser = userCompanyUserService.selectFsUserCompanyUserById(userCompanyId);
 
-        UserDetailsVO countAnswer = fsUserMapper.getCountAnswer(fsUserCompanyUser.getCompanyUserId(), fsUserId, dateTag);
-        UserDetailsVO countRedPacket = fsUserMapper.getCountRedPacket(fsUserCompanyUser.getCompanyUserId(), fsUserId, dateTag);
+        UserDetailsVO countAnswer = fsUserMapper.getCountAnswer(userCompanyId, fsUserId, dateTag);
+        UserDetailsVO countRedPacket = fsUserMapper.getCountRedPacket(userCompanyId, fsUserId, dateTag);
         UserDetailsVO vo = new UserDetailsVO();
         if (countWatchCourse != null) {
             BeanUtils.copyProperties(countWatchCourse, vo);

+ 68 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java

@@ -242,4 +242,72 @@ public interface FsStoreAfterSalesScrmMapper
 
     @Select(" SELECT id FROM fs_store_order_scrm WHERE order_code =#{id}")
     Long selectFsOrderIdByCode(Long id);
+
+    @Select({"<script> " +
+            "select s.*,o.delivery_status,o.delivery_id,u.phone as user_phone,c.company_name ,cu.nick_name as company_user_nick_name ," +
+            "cu.phonenumber as company_usere_phonenumber,o.pay_money,o.id as orderId,o.create_time as orderCreateTime,o.user_phone," +
+            "o.real_name as userName,o.item_json,o.user_address,o.pay_time as orderPayTime,o.pay_price,o.total_postage," +
+            "fsps.bank_serial_no,fsps.bank_transaction_id,o.delivery_id as orderDeliveryId,o.delivery_name as orderDeliveryName,o.delivery_sn as orderDeliverySn," +
+            "o.status as orderStatus,fsps.pay_code as payCode " +
+            " from fs_store_after_sales_scrm s " +
+            " INNER join fs_store_order_scrm o on o.order_code=s.order_code " +
+            " left join fs_user u on s.user_id=u.user_id " +
+            " left join company c on c.company_id=s.company_id " +
+            " left join company_user cu on cu.user_id=s.company_user_id " +
+            " left join fs_store_payment_scrm fsps on fsps.business_order_id = o.id and fsps.status in (-1,1) " +
+            " where 1=1 and s.status = 4 " +
+            "<if test =\"maps.hfOrderCode != null and  maps.hfOrderCode!='' \"> " +
+            "and fsps.pay_code = #{maps.hfOrderCode} " +
+            "</if>" +
+            "<if test = 'maps.status != null    '> " +
+            "and s.status = #{maps.status} " +
+            "</if>" +
+            "<if test = 'maps.salesStatus != null    '> " +
+            "and s.sales_status = #{maps.salesStatus} " +
+            "</if>" +
+            "<if test = 'maps.orderStatus != null    '> " +
+            "and s.order_status = #{maps.orderStatus} " +
+            "</if>" +
+            "<if test = 'maps.orderCode != null and  maps.orderCode !=  \"\" '> " +
+            "and o.order_code like concat('%', #{maps.orderCode}, '%') " +
+            "</if>" +
+            "<if test = 'maps.deliveryStatus != null    '> " +
+            "and o.delivery_status = #{maps.deliveryStatus} " +
+            "</if>" +
+            "<if test = 'maps.serviceType != null    '> " +
+            "and s.service_type = #{maps.serviceType} " +
+            "</if>" +
+            "<if test = 'maps.companyId != null    '> " +
+            "and s.company_id = #{maps.companyId} " +
+            "</if>" +
+            "<if test = 'maps.companyUserId != null    '> " +
+            "and s.company_user_id = #{maps.companyUserId} " +
+            "</if>" +
+            "<if test = 'maps.deliverySn != null and  maps.deliverySn !=  \"\" '> " +
+            " and ( o.delivery_id like concat('%', #{maps.deliverySn}, '%') or s.delivery_sn like concat('%', #{maps.deliverySn}, '%')) " +
+            "</if>" +
+            "<if test = 'maps.companyUserNickName != null and  maps.companyUserNickName !=  \"\" '> " +
+            "and cu.nick_name like concat('%', #{maps.companyUserNickName}, '%') " +
+            "</if>" +
+            "<if test = 'maps.params != null and maps.params != \"\"   '> " +
+            "<if test = 'maps.params.beginTime != null and maps.params.beginTime != \"\"   '> " +
+            " AND date_format(s.create_time,'%y%m%d') &gt;= date_format(#{maps.params.beginTime},'%y%m%d') " +
+            "</if>" +
+            "<if test = 'maps.params.endTime != null and maps.params.endTime != \"\"   '> " +
+            " AND date_format(s.create_time,'%y%m%d') &lt;= date_format(#{maps.params.endTime},'%y%m%d') " +
+            "</if>" +
+            "</if>" +
+            "<if test = 'maps.consigneePhone != null and  maps.consigneePhone !=\"\"     '> " +
+            "and o.user_phone like CONCAT('%',#{maps.consigneePhone},'%') " +
+            "</if>" +
+            "<if test = 'maps.productName != null and  maps.productName != \"\" '> " +
+            "and EXISTS (SELECT 1 FROM fs_store_order_item_scrm oi WHERE oi.order_id = o.id AND JSON_UNQUOTE(JSON_EXTRACT(oi.json_info, '$.productName')) LIKE CONCAT('%', #{maps.productName}, '%')) " +
+            "</if>" +
+            "<if test = 'maps.deptId != null    '> " +
+            "  AND (o.dept_id = #{maps.deptId} OR o.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) )) " +
+            "</if>" +
+            " ${maps.params.dataScope} "+
+            "order by s.create_time desc "+
+            "</script>"})
+    List<FsStoreAfterSalesVO> selectFsStoreAfterSalesListVOExport(@Param("maps") FsStoreAfterSalesScrm fsStoreAfterSales);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderCreateParam.java

@@ -19,7 +19,7 @@ public class FsStoreOrderCreateParam implements Serializable
     @NotNull(message = "orderKey不能为空")
     private String orderKey;
     @ApiModelProperty(value = "地址ID")
-    @NotNull(message = "地址不能为空")
+//    @NotNull(message = "地址不能为空")
     private Long addressId;
 
     @ApiModelProperty(value = "来源")

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

@@ -117,4 +117,6 @@ public interface IFsStoreAfterSalesScrmService
     int noAuditing(FsStoreAfterSalesScrm fsStoreAfterSales);
 
     int storeRefundMoney(FsStoreAfterSalesScrm fsStoreAfterSales);
+
+    List<FsStoreAfterSalesVO> selectFsStoreAfterSalesListVOExport(FsStoreAfterSalesScrm fsStoreAfterSales);
 }

+ 41 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -554,6 +554,47 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
         return R.ok();
     }
 
+    @Override
+    @DataScope(deptAlias = "cu", userAlias = "cu")
+    public List<FsStoreAfterSalesVO> selectFsStoreAfterSalesListVOExport(FsStoreAfterSalesScrm fsStoreAfterSales) {
+        List<FsStoreAfterSalesVO> fsStoreAfterSalesVOS = fsStoreAfterSalesMapper.selectFsStoreAfterSalesListVOExport(fsStoreAfterSales);
+        List<Long> orderIds = new ArrayList<>();
+        Map<Long, List<FsStoreOrderItemVO>> orderItemMap = new HashMap<>();
+        if(null != fsStoreAfterSalesVOS && !fsStoreAfterSalesVOS.isEmpty()){
+            orderIds = fsStoreAfterSalesVOS.stream().map(e -> e.getOrderId()).collect(Collectors.toList());
+            if(null != orderIds && !orderIds.isEmpty()){
+                List<FsStoreOrderItemVO> fsStoreOrderItemVOS = fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderIds(orderIds);
+                orderItemMap = fsStoreOrderItemVOS.stream()
+                        .collect(Collectors.groupingBy(FsStoreOrderItemVO::getOrderId));
+            }
+        }
+        boolean mapEmpty = orderItemMap.isEmpty();
+        if (null != fsStoreAfterSalesVOS && !fsStoreAfterSalesVOS.isEmpty()) {
+            for (FsStoreAfterSalesVO item : fsStoreAfterSalesVOS) {
+                if(!mapEmpty && orderItemMap.containsKey(item.getOrderId())){
+                    List<FsStoreOrderItemVO> orderItems = orderItemMap.get(item.getOrderId());
+                    for (FsStoreOrderItemVO orderItem : orderItems) {
+                        try {
+                            JSONObject jsO = JSONObject.parseObject(orderItem.getJsonInfo());
+                            item.setProductName(StringUtils.isNotBlank(item.getProductName()) ? item.getProductName() + "," + jsO.getString("productName") : jsO.getString("productName"));
+                            item.setProductBarCode(StringUtils.isNotBlank(item.getProductBarCode()) ? item.getProductBarCode() + "," + jsO.getString("barCode") : jsO.getString("barCode"));
+                            item.setSku(StringUtils.isNotBlank(item.getSku()) ? item.getSku() + "," + jsO.getString("sku") : jsO.getString("sku"));
+                            item.setNum(StringUtils.isNotBlank(item.getNum()) ? item.getNum() + "," + jsO.getString("num") : jsO.getString("num"));
+                            item.setPrice(StringUtils.isNotBlank(item.getPrice()) ? item.getPrice() + "," + jsO.getString("price") : jsO.getString("price"));
+                            item.setBarCode(StringUtils.isNotBlank(item.getBarCode()) ? item.getBarCode() + "," + jsO.getString("barCode") : jsO.getString("barCode"));
+                            item.setCost(StringUtils.isNotBlank(item.getCost()) ? item.getCost() + "," + orderItem.getCost() : orderItem.getCost());
+                            item.setCateName(StringUtils.isNotBlank(item.getCateName()) ? item.getCateName() + "," + orderItem.getCateName() : orderItem.getCateName());
+
+                        } catch (Exception ex) {
+                            logger.error("售后订单商品信息转换异常",ex);
+                        }
+                    }
+                }
+            }
+        }
+        return fsStoreAfterSalesVOS;
+    }
+
     @Override
     @DataScope(deptAlias = "cu", userAlias = "cu")
     public List<FsStoreAfterSalesVO> selectFsStoreAfterSalesListVO(FsStoreAfterSalesScrm fsStoreAfterSales) {

+ 38 - 12
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -629,8 +629,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     public List<FsStoreOrderVO> selectFsStoreOrderListVO(FsStoreOrderParam param) {
         List<FsStoreOrderVO> list = fsStoreOrderMapper.selectFsStoreOrderListVO(param);
         for (FsStoreOrderVO vo : list) {
-            String nickName = vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
-            vo.setNickname(nickName);
+            if (StringUtils.isNotEmpty(vo.getUserPhone())){
+                String nickName = vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
+                vo.setNickname(nickName);
+            }
+
             if (StringUtils.isNotEmpty(vo.getItemJson())) {
                 JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
                 List<FsStoreOrderItemVO> items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
@@ -648,8 +651,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     public List<FsStoreOrderVO> selectFsStoreOrderAllListVO(FsStoreOrderParam param) {
         List<FsStoreOrderVO> list = fsStoreOrderMapper.selectFsStoreOrderAllListVO(param);
         for (FsStoreOrderVO vo : list) {
-            String nickName = vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
-            vo.setNickname(nickName);
+            if (StringUtils.isNotEmpty(vo.getUserPhone())){
+                String nickName = vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
+                vo.setNickname(nickName);
+            }
+
             if (StringUtils.isNotEmpty(vo.getItemJson())) {
                 JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
                 List<FsStoreOrderItemVO> items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
@@ -829,6 +835,12 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     @Override
     @Transactional
     public R createOrder(long userId, FsStoreOrderCreateParam param) {
+        if (!CloudHostUtils.hasCloudHostName("鹤颜堂")){
+            log.error("进入到数据");
+            if (ObjectUtil.isEmpty(param.getAddressId())){
+                return R.error("地址不能为空!");
+            }
+        }
         FsStoreOrderComputedParam computedParam = new FsStoreOrderComputedParam();
         BeanUtils.copyProperties(param, computedParam);
         //计算金额
@@ -902,10 +914,12 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
 
             storeOrder.setUserId(userId);
             storeOrder.setOrderCode(orderSn);
-            storeOrder.setRealName(address.getRealName());
-            storeOrder.setUserPhone(address.getPhone());
-            storeOrder.setUserAddress(address.getProvince() + " " + address.getCity() +
-                    " " + address.getDistrict() + " " + address.getDetail().trim());
+            if (ObjectUtil.isNotEmpty(address)){
+                storeOrder.setRealName(address.getRealName());
+                storeOrder.setUserPhone(address.getPhone());
+                storeOrder.setUserAddress(address.getProvince() + " " + address.getCity() +
+                        " " + address.getDistrict() + " " + address.getDetail().trim());
+            }
             storeOrder.setCartId(cartIds);
             storeOrder.setTotalNum(Long.parseLong(String.valueOf(carts.size())));
             storeOrder.setTotalPrice(dto.getTotalPrice());
@@ -973,6 +987,10 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             if (param.getPayType().equals("1")) {
                 //全款支付
                 storeOrder.setStatus(0);
+                if("广州郑多燕".equals(cloudHostProper.getCompanyName())){
+                    BigDecimal amount = redisCache.getCacheObject("createOrderAmount:" + param.getCreateOrderKey());
+                    storeOrder.setPayDelivery(amount != null ? amount : BigDecimal.ZERO);
+                }
             } else if (param.getPayType().equals("2")) {
                 //物流代收
                 storeOrder.setStatus(1);
@@ -1749,6 +1767,10 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             storeOrder.setOrderCreateType(2);
             storeOrder.setPayType(storeProductPackage.getPayType().toString());
             storeOrder.setPayPrice(totalMoney);
+            if("广州郑多燕".equals(cloudHostProper.getCompanyName())){
+                BigDecimal amount = redisCache.getCacheObject("createOrderAmount:" + param.getOrderKey());
+                storeOrder.setPayDelivery(amount != null ? amount : BigDecimal.ZERO);
+            }
             storeOrder.setIsPackage(1);
             FsStoreProductPackageScrm productPackage = new FsStoreProductPackageScrm();
             productPackage.setTitle(storeProductPackage.getTitle());
@@ -2760,8 +2782,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 totalMoney = totalMoney.add(vo.getPrice().multiply(new BigDecimal(vo.getCartNum().toString())));
             }
         } else {
-            //套餐制单
-            totalMoney = carts.get(0).getPrice();
+            //套餐制单,这个金额是套餐的金额
+            totalMoney = redisCache.getCacheObject("createOrderMoney:" + createOrderKey);
         }
         if (money.compareTo(totalMoney) == 1) {
             throw new CustomException("价格不能大于商品总价", 501);
@@ -4205,7 +4227,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 if(param.getPayType().equals(1)){
                     order.setPayType("1");
                     order.setPayMoney(order.getPayPrice());
-                    order.setPayDelivery(BigDecimal.ZERO);
+                    if(!"广州郑多燕".equals(cloudHostProper.getCompanyName())){
+                        order.setPayDelivery(BigDecimal.ZERO);
+                    }
                 }
                 else if(param.getPayType().equals(2)){
                     order.setPayType("2");
@@ -4718,7 +4742,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 if(param.getPayType().equals(1)){
                     order.setPayType("1");
                     order.setPayMoney(order.getPayPrice());
-                    order.setPayDelivery(BigDecimal.ZERO);
+                    if(!"广州郑多燕".equals(cloudHostProper.getCompanyName())){
+                        order.setPayDelivery(BigDecimal.ZERO);
+                    }
                 }
                 else if(param.getPayType().equals(2)){
                     // 物流代收

+ 2 - 2
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportRefundZMVO.java

@@ -123,8 +123,8 @@ public class FsStoreOrderItemExportRefundZMVO implements Serializable  {
     @Excel(name = "银行交易流水号",sort = 240)
     private String bankTransactionId;
 
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @Excel(name = "退款时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 220)
+//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+//    @Excel(name = "退款时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 220)
     private Date refundTime;
 
     @Excel(name = "退款数量" ,sort = 230)

+ 1 - 1
fs-service/src/main/java/com/fs/live/domain/LiveOrder.java

@@ -96,7 +96,7 @@ public class LiveOrder extends BaseEntity {
     private String payType;
 
     /** 订单状态(-1 : 申请退款 -2 : 退货成功 0:已取消 1:待支付 2:待发货;3:待收货;4:待评价;5:已完成) */
-    @Excel(name = "订单状态", readConverterExp = "-=1,:=,申=请退款,-=2,:=,退=货成功,1=:待支付,2=:待发货;3:待收货;4:待评价;5:已完成")
+    @Excel(name = "订单状态", readConverterExp = "-=1,:=,申=请退款,-=2,:=,退=货成功,1=:待支付,2=:待发货;3:待收货;4:待评价;5:已完成;6:被拆分")
     private Integer status;
 
     /** 0 未退款 1 申请中 2 已退款 */

+ 17 - 0
fs-service/src/main/java/com/fs/live/domain/LiveWatchLog.java

@@ -1,6 +1,8 @@
 package com.fs.live.domain;
 
 import java.util.Date;
+import java.util.Objects;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
@@ -86,4 +88,19 @@ public class LiveWatchLog extends BaseEntity{
      */
     private Integer replayBuy;
 
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+        LiveWatchLog that = (LiveWatchLog) obj;
+        return Objects.equals(liveId, that.liveId) &&
+                Objects.equals(externalContactId, that.externalContactId) &&
+                Objects.equals(qwUserId, that.qwUserId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(liveId, externalContactId, qwUserId);
+    }
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveAfterSalesMapper.java

@@ -133,4 +133,6 @@ public interface LiveAfterSalesMapper {
 
     @Select(" select  * from  live_after_sales where order_id = #{orderId} and sales_status = 0 ")
     LiveAfterSales getLiveAfterSalesByOrderId(@Param("orderId") Long orderId);
+
+    List<LiveAfterSalesVo> selectLiveAfterSalesVoListExport(LiveAfterSalesVo liveAfterSales);
 }

+ 21 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java

@@ -126,6 +126,27 @@ public interface LiveOrderMapper {
     @Select("select * from live_order where order_code=#{orderCode} limit 1")
     LiveOrder selectLiveOrderByOrderCode(@Param("orderCode") String orderCode);
 
+    /**
+     * 查询状态为6(被拆分)的订单
+     */
+    @Select("select * from live_order where status = 6")
+    List<LiveOrder> selectSplitOrders();
+
+    /**
+     * 根据父订单号查询子订单(拆分订单)
+     * 通过用户ID、直播ID、公司ID等关联查找可能的拆分订单
+     */
+    @Select({"<script> " +
+            "select * from live_order " +
+            "where status != 6 " +
+            "and user_id = (select user_id from live_order where order_code = #{parentOrderCode} limit 1) " +
+            "and live_id = (select live_id from live_order where order_code = #{parentOrderCode} limit 1) " +
+            "and create_time >= (select create_time from live_order where order_code = #{parentOrderCode} limit 1) " +
+            "and order_code != #{parentOrderCode} " +
+            "order by create_time asc " +
+            "</script>"})
+    List<LiveOrder> selectChildOrdersByParentOrderCode(@Param("parentOrderCode") String parentOrderCode);
+
     List<LiveOrder> selectFsOutDateOrder();
 
 

+ 2 - 0
fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java

@@ -118,5 +118,7 @@ public class MergedOrderQueryParam extends BaseQueryParam implements Serializabl
 
     /** ERP电话 */
     private String erpPhoneNumber;
+    /** 汇付商户订单号 */
+    private String hfshh;
 }
 

+ 2 - 0
fs-service/src/main/java/com/fs/live/service/ILiveAfterSalesService.java

@@ -93,4 +93,6 @@ public interface ILiveAfterSalesService {
     Integer selectLiveAfterSalesCount(long l, int i);
 
     R handleImmediatelyRefund(Long orderId);
+
+    List<LiveAfterSalesVo> selectLiveAfterSalesVoListExport(LiveAfterSalesVo liveAfterSales);
 }

+ 54 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java

@@ -192,6 +192,59 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
 
 //    @Autowired
 //    private FsStoreDeliversService fsStoreDeliversService;
+
+
+    @Override
+    public List<LiveAfterSalesVo> selectLiveAfterSalesVoListExport(LiveAfterSalesVo liveAfterSales) {
+        List<LiveAfterSalesVo> liveAfterSalesVos = baseMapper.selectLiveAfterSalesVoListExport(liveAfterSales);
+        List<Long> orderIds = new ArrayList<>();
+        Map<Long, List<LiveOrderItemListUVO>> orderItemMap = new HashMap<>();
+        if(null != liveAfterSalesVos && !liveAfterSalesVos.isEmpty()){
+            orderIds = liveAfterSalesVos.stream().map(e -> e.getOrderId()).collect(Collectors.toList());
+            if(null != orderIds && !orderIds.isEmpty()){
+                List<LiveOrderItemListUVO> liveOrderItemListUVOS = liveOrderItemMapper.selectLiveOrderItemListUVOByOrderIds(orderIds);
+                orderItemMap = liveOrderItemListUVOS.stream()
+                        .collect(Collectors.groupingBy(LiveOrderItemListUVO::getOrderId));
+            }
+        }
+        boolean mapEmpty = orderItemMap.isEmpty();
+        for (LiveAfterSalesVo item : liveAfterSalesVos) {
+            if(ObjectUtil.isNotNull(item.getUserId())) {
+                FsUser fsUser = fsUserCacheService.selectFsUserById(item.getUserId());
+                if(ObjectUtil.isNotNull(fsUser)) {
+                    item.setUserName(String.format("%s_%s",fsUser.getUserId(),fsUser.getNickname()));
+                }
+            }
+
+            if(ObjectUtil.isNull(item.getCompanyUserNickName())) {
+                item.setCompanyUserNickName("-");
+            }
+
+            if(ObjectUtil.isNull(item.getCompanyName())){
+                item.setCompanyName("-");
+            }
+
+            if(!mapEmpty && orderItemMap.containsKey(item.getOrderId())){
+                List<LiveOrderItemListUVO> liveOrderItemListUVOS = orderItemMap.get(item.getOrderId());
+                for (LiveOrderItemListUVO liveOrderItemListUVO : liveOrderItemListUVOS) {
+                    try {
+                        JSONObject jsO = JSONObject.parseObject(liveOrderItemListUVO.getJsonInfo());
+                        item.setProductName(StringUtils.isNotBlank(item.getProductName()) ? item.getProductName() + "," + jsO.getString("productName") : jsO.getString("productName"));
+                        item.setProductBarCode(StringUtils.isNotBlank(item.getProductBarCode()) ? item.getProductBarCode() + "," + jsO.getString("barCode") : jsO.getString("barCode"));
+                        item.setSku(StringUtils.isNotBlank(item.getSku()) ? item.getSku() + "," + jsO.getString("sku") : jsO.getString("sku"));
+                        item.setNum(StringUtils.isNotBlank(item.getNum()) ? item.getNum() + "," + jsO.getString("num") : jsO.getString("num"));
+                        item.setPrice(StringUtils.isNotBlank(item.getPrice()) ? item.getPrice() + "," + jsO.getString("price") : jsO.getString("price"));
+                        item.setCost(StringUtils.isNotBlank(item.getCost()) ? item.getCost() + "," + liveOrderItemListUVO.getCost() : liveOrderItemListUVO.getCost());
+                        item.setCateName(StringUtils.isNotBlank(item.getCateName()) ? item.getCateName() + "," + liveOrderItemListUVO.getCateName() : liveOrderItemListUVO.getCateName());
+                    } catch (Exception ex) {
+                        log.error("售后订单商品信息转换异常",ex);
+                    }
+                }
+            }
+
+        }
+        return liveAfterSalesVos;
+    }
     /**
      * 查询售后记录列表
      *
@@ -1124,4 +1177,5 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
 
         return R.ok();
     }
+
 }

+ 11 - 7
fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java

@@ -26,6 +26,8 @@ import com.fs.his.mapper.FsUserMapper;
 import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import java.util.stream.Collectors;
+
+import com.github.pagehelper.PageInfo;
 import io.swagger.models.auth.In;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -697,7 +699,13 @@ public class LiveDataServiceImpl implements ILiveDataService {
     @Override
     public R getLiveUserDetailListBySql(Long liveId, Long companyId, Long companyUserId ) {
         List<LiveUserDetailVo> userDetailList = liveDataMapper.selectLiveUserDetailListBySql(liveId, companyId, companyUserId);
-        return R.ok().put("data", userDetailList);
+        // 使用 PageInfo 获取分页信息
+        PageInfo<LiveUserDetailVo> pageInfo = new PageInfo<>(userDetailList);
+        R data = R.ok().put("data", userDetailList);
+        if (pageInfo != null) {
+            data.put("total", pageInfo.getTotal());
+        }
+        return data;
     }
 
     @Override
@@ -1088,6 +1096,8 @@ public class LiveDataServiceImpl implements ILiveDataService {
             return new ArrayList<>();
         }
 
+
+
         // 转换为导出VO列表
         List<LiveUserDetailExportVO> exportList = new ArrayList<>();
         for (LiveUserDetailVo userDetail : userDetailList) {
@@ -1114,12 +1124,6 @@ public class LiveDataServiceImpl implements ILiveDataService {
             exportVO.setCompanyName(userDetail.getCompanyName());
             exportVO.setSalesName(userDetail.getSalesName());
 
-            // 是否完课(根据观看时长判断,假设30分钟以上为完课)
-            if (totalSeconds >= 1800) {
-                exportVO.setIsCompleted("是");
-            } else {
-                exportVO.setIsCompleted("否");
-            }
 
             exportList.add(exportVO);
         }

+ 14 - 12
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -783,21 +783,23 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             Map<String, Integer> liveFlagWithCache = liveWatchUserService.getLiveFlagWithCache(order.getLiveId());
             if (liveFlagWithCache != null && liveFlagWithCache.containsKey("liveFlag") && 1 == liveFlagWithCache.get("liveFlag")) {
                 try {
-                    LiveWatchLog queryLog = new LiveWatchLog();
-                    queryLog.setLiveId(order.getLiveId());
-                    queryLog.setUserId(Long.valueOf(order.getUserId()));
-                    queryLog.setCompanyId(order.getCompanyId());
-                    queryLog.setCompanyUserId(order.getCompanyUserId());
-
-                    List<LiveWatchLog> logs = liveWatchLogService.selectLiveWatchLogList(queryLog);
-                    if (logs != null && !logs.isEmpty()) {
-                        for (LiveWatchLog log : logs) {
-                            if (log.getLogType() == null || log.getLogType() != 2) {
-                                log.setLiveBuy(1);
-                                liveWatchLogService.updateLiveWatchLog(log);
+                    LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserId(order.getLiveId(), Long.parseLong(order.getUserId()));
+                    if (liveUserFirstEntry != null && liveUserFirstEntry.getExternalContactId()!=null && liveUserFirstEntry.getExternalContactId() > 0 &&  liveUserFirstEntry.getQwUserId()!=null && liveUserFirstEntry.getQwUserId() > 0) {
+                        LiveWatchLog queryLog = new LiveWatchLog();
+                        queryLog.setLiveId(order.getLiveId());
+                        queryLog.setQwUserId(String.valueOf(liveUserFirstEntry.getQwUserId()));
+                        queryLog.setExternalContactId(liveUserFirstEntry.getExternalContactId());
+                        List<LiveWatchLog> logs = liveWatchLogService.selectLiveWatchLogList(queryLog);
+                        if (logs != null && !logs.isEmpty()) {
+                            for (LiveWatchLog log : logs) {
+                                if (log.getLogType() == null || log.getLogType() != 2) {
+                                    log.setLiveBuy(1);
+                                    liveWatchLogService.updateLiveWatchLog(log);
+                                }
                             }
                         }
                     }
+
                 } catch (Exception e) {
                     log.error("更新 updateLiveWatchLog LiveWatchLog logType 异常(连接时):liveId={}, userId={}, error={}",
                             order.getLiveId(), order.getUserId(), e.getMessage(), e);

+ 17 - 12
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -319,6 +319,9 @@ public class LiveServiceImpl implements ILiveService
 
     @Override
     public R subNotifyLive(LiveNotifyParam param) {
+        if (StringUtils.isEmpty(param.getAppId())) {
+            return R.error("小程序订阅失败:appId为空!");
+        }
         LiveMiniprogramSubNotifyTask notifyTask = new LiveMiniprogramSubNotifyTask();
         notifyTask.setPage("pages_course/living?liveId=" + param.getLiveId());
         notifyTask.setTaskName("直播间预约提醒");
@@ -326,7 +329,7 @@ public class LiveServiceImpl implements ILiveService
         Long userId = param.getUserId();
         Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
                 .eq(FsUserWx::getFsUserId, userId)
-                .eq(FsUserWx::getAppId, StringUtils.isEmpty(param.getAppId()) ? "wx44beed5640bcb1ba" : param.getAppId()); // 卓美小程序
+                .eq(FsUserWx::getAppId, StringUtils.isEmpty(param.getAppId()) ? "wxd791d5933ed42218" : param.getAppId()); // 卓美小程序
         FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
         String maOpenId = "";
         if (fsUserWx == null) {
@@ -1223,9 +1226,9 @@ public class LiveServiceImpl implements ILiveService
         Map<Long, LiveAutoTask> goodsTaskMap = goodsTasks.stream()
                 .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
                         Function.identity(), (existing, replacement) -> existing));
-        Map<Long, LiveAutoTask> shelfTaskMap = shelfTasks.stream()
-                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
-                        Function.identity(), (existing, replacement) -> existing));
+        // 使用 groupingBy 支持同一个商品有多个上下架任务(不同时间点的上架/下架操作)
+        Map<Long, List<LiveAutoTask>> shelfTaskMap = shelfTasks.stream()
+                .collect(Collectors.groupingBy(task -> parseIdFromContent(task.getContent(), "goodsId")));
 
         LiveGoods queryParam = new LiveGoods();
         queryParam.setLiveId(existLiveId);
@@ -1265,14 +1268,16 @@ public class LiveServiceImpl implements ILiveService
                     liveAutoTaskService.directInsertLiveAutoTask(newTask);
                 }
 
-                // 复制上下架任务(taskType=6)
-                LiveAutoTask shelfTask = shelfTaskMap.get(liveGoods.getGoodsId());
-                if (shelfTask != null) {
-                    JSONObject contentJson = JSON.parseObject(shelfTask.getContent());
-                    contentJson.put("goodsId", newGoods.getGoodsId());
-                    LiveAutoTask newTask = createAutoTaskEntity(shelfTask, newLiveId, now,
-                            contentJson.toJSONString());
-                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
+                // 复制上下架任务(taskType=6)- 支持同一个商品有多个上下架任务
+                List<LiveAutoTask> shelfTaskList = shelfTaskMap.get(liveGoods.getGoodsId());
+                if (shelfTaskList != null && !shelfTaskList.isEmpty()) {
+                    for (LiveAutoTask shelfTask : shelfTaskList) {
+                        JSONObject contentJson = JSON.parseObject(shelfTask.getContent());
+                        contentJson.put("goodsId", newGoods.getGoodsId());
+                        LiveAutoTask newTask = createAutoTaskEntity(shelfTask, newLiveId, now,
+                                contentJson.toJSONString());
+                        liveAutoTaskService.directInsertLiveAutoTask(newTask);
+                    }
                 }
             }
         }

+ 5 - 5
fs-service/src/main/java/com/fs/live/service/impl/LiveVideoServiceImpl.java

@@ -84,13 +84,13 @@ public class LiveVideoServiceImpl implements ILiveVideoService
     @Override
     public int insertLiveVideo(LiveVideo liveVideo)
     {
-        if (LiveEnum.contains(cloudHostProper.getCompanyName())) {
-            liveVideo.setVideoUrl(liveVideo.getLineOne());
-            liveVideo.setFinishStatus(1);
-        }else {
+//        if (LiveEnum.contains(cloudHostProper.getCompanyName())) {
+//            liveVideo.setVideoUrl(liveVideo.getLineOne());
+//            liveVideo.setFinishStatus(1);
+//        }else {
             // 直播ID为-1,则新增 直播视频库
             liveVideo.setFinishStatus(0);
-        }
+//        }
 
         if (liveVideo.getLiveId() == -1) {
             liveVideo.setCreateTime(DateUtils.getNowDate());

+ 1 - 1
fs-service/src/main/java/com/fs/live/vo/LiveUserDetailExportVO.java

@@ -49,7 +49,7 @@ public class LiveUserDetailExportVO {
     private String salesName;
 
     /** 是否完课 */
-    @Excel(name = "是否完课")
+//    @Excel(name = "是否完课")
     private String isCompleted;
 
 }

+ 20 - 11
fs-service/src/main/java/com/fs/live/vo/MergedOrderExportVO.java

@@ -19,6 +19,10 @@ public class MergedOrderExportVO implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
+    /** 订单类型 */
+    @Excel(name = "订单类型")
+    private String orderTypeName;
+
     /** 订单号 */
     @Excel(name = "订单号")
     private String orderCode;
@@ -48,13 +52,25 @@ public class MergedOrderExportVO implements Serializable
     private Integer totalNum;
 
     /** 产品价格 */
-    @Excel(name = "产品价格")
+//    @Excel(name = "产品价格")
     private BigDecimal price;
 
     /** 成本价 */
     @Excel(name = "成本价")
     private BigDecimal cost;
 
+    /** 商品金额 */
+    @Excel(name = "商品金额")
+    private BigDecimal totalPrice;
+
+    /** 应付金额 */
+    @Excel(name = "应付金额")
+    private BigDecimal payPrice;
+
+    /** 实付金额 */
+    @Excel(name = "实付金额")
+    private BigDecimal payMoney;
+
     /** 结算价 */
     @Excel(name = "结算价")
     private BigDecimal FPrice;
@@ -137,18 +153,11 @@ public class MergedOrderExportVO implements Serializable
     /** 银行交易流水号 */
     @Excel(name = "银行交易流水号")
     private String bankTransactionId;
+    /** 汇付商户订单号 */
+    @Excel(name = "汇付商户订单号")
+    private String hfshh;
 
-    /** 商品金额 */
-    @Excel(name = "商品金额")
-    private BigDecimal totalPrice;
 
-    /** 应付金额 */
-    @Excel(name = "应付金额")
-    private BigDecimal payPrice;
-
-    /** 实付金额 */
-    @Excel(name = "实付金额")
-    private BigDecimal payMoney;
 
 
 }

+ 2 - 0
fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java

@@ -109,6 +109,8 @@ public class MergedOrderVO implements Serializable
 
     /** 订单类型:1-销售订单,2-商城订单,3-直播订单 */
     private Integer orderType;
+//    汇付商户订单号
+    private String hfshh;
 
     /** 订单类型名称 */
     private String orderTypeName;

+ 18 - 0
fs-service/src/main/java/com/fs/newAdv/controller/AdvMiniConfigController.java

@@ -0,0 +1,18 @@
+package com.fs.newAdv.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 广告微信小程序配置 前端控制器
+ * </p>
+ *
+ * @author zhangqin
+ * @since 2025-12-19
+ */
+@RestController
+@RequestMapping("/advMiniConfig")
+public class AdvMiniConfigController {
+
+}

+ 58 - 0
fs-service/src/main/java/com/fs/newAdv/domain/AdvMiniConfig.java

@@ -0,0 +1,58 @@
+package com.fs.newAdv.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 广告微信小程序配置
+ * </p>
+ *
+ * @author zhangqin
+ * @since 2025-12-19
+ */
+@Getter
+@Setter
+@TableName("adv_mini_config")
+@ApiModel(value = "AdvMiniConfig对象", description = "广告微信小程序配置")
+public class AdvMiniConfig extends Model<AdvMiniConfig> {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId("id")
+    private Integer id;
+
+    @TableField("app_id")
+    private String appId;
+
+    @TableField("app_secret")
+    private String appSecret;
+
+    @TableField("access_token")
+    private String accessToken;
+
+    @ApiModelProperty("小程序状态1启用 0停用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty("过期时间")
+    @TableField("expires_in")
+    private LocalDateTime expiresIn;
+
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    @Override
+    public Serializable pkVal() {
+        return this.id;
+    }
+}

+ 2 - 2
fs-service/src/main/java/com/fs/newAdv/domain/PromotionAccount.java

@@ -71,9 +71,9 @@ public class PromotionAccount implements Serializable {
     private Integer apiSwitch;
 
     /**
-     * 推广账户密码(加密存储)
+     * 推拓展字段
      */
-    private String accountPassword;
+    private String extendedField;
     /**
      * 1线上 2线下
      */

+ 2 - 2
fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/BaiduApiClient.java

@@ -123,7 +123,7 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
         Map<String, Object> map = new HashMap<>();
         Map<String, Object> header = new HashMap<>();
         header.put("accessToken", account.getAccessToken());
-        header.put("userName", account.getAccountShortName());
+        header.put("userName", account.getExtendedField());
         map.put("header", header);
         Map<String, Object> body = new HashMap<>();
         // 基础信息
@@ -197,7 +197,7 @@ public class BaiduApiClient extends AbstractApiClient implements IAccessTokenCli
             requestMap.put("secretKey", codeVo.getAppSecret());
             requestMap.put("authCode", codeVo.getAuthCode());
             requestMap.put("grantType", "access_token");
-            requestMap.put("userId", codeVo.getUserId());
+            requestMap.put("userId", codeVo.getAdAccountId());
             // 发送HTTP请求
             return HttpRequest.post(ACCESS_TOKEN_URL)
                     .header("Content-Type", "application/json")

+ 44 - 6
fs-service/src/main/java/com/fs/newAdv/integration/client/advertiser/TencentApiClient.java

@@ -1,6 +1,8 @@
 package com.fs.newAdv.integration.client.advertiser;
 
 import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.constant.SystemConstant;
@@ -15,11 +17,9 @@ import com.fs.newAdv.vo.AccessTokenVo;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * 腾讯广点通API客户端
@@ -34,6 +34,7 @@ public class TencentApiClient extends AbstractApiClient implements IAccessTokenC
 
     private static final String CONVERSION_API_URL = "https://api.e.qq.com/user_actions/add";
     private static final String TOKEN_API_URL = "https://api.e.qq.com/oauth/token";
+    private static final String REPORTS_API_URL = "https://api.e.qq.com/v3.0/daily_reports/get?access_token=%s&timestamp=%s&nonce=%s";
 
     /**
      * 回传转化数据
@@ -84,7 +85,44 @@ public class TencentApiClient extends AbstractApiClient implements IAccessTokenC
 
     @Override
     public SiteStatistics getDataReport(PromotionAccount account, String ideaId, String startDate, String endDate) {
-        return null;
+        // 构建请求参数
+        Map<String, Object> body = new HashMap<>();
+        // 基础信息
+        //报告名称: 信息流整体账户报告
+        //reportType: 2172649
+        //支持的时间单位: HOUR DAY WEEK MONTH SUMMARY
+        //支持的最大时间区间: 824天
+        body.put("account_id", account.getAdAccountId());
+        body.put("level", "REPORT_LEVEL_ADGROUP");
+        Map<String, Object> date_range = new HashMap<>();
+        date_range.put("start_date", startDate);
+        date_range.put("end_date", endDate);
+        body.put("date_range", date_range);
+
+        Map<String, Object> filtering = new HashMap<>();
+        filtering.put("field", "dynamic_creative_id");
+        filtering.put("operator", "IN");
+        filtering.put("values", Arrays.asList(ideaId));
+        body.put("filtering", date_range);
+
+        log.info("腾讯数据请求参数:{}", JSONUtil.toJsonStr(body));
+        HttpResponse execute = HttpRequest.get(String.format(REPORTS_API_URL, account.getAccessToken(), new Date().getTime(), SnowflakeUtil.randomUUID()))
+                .form(body)
+                .timeout(SystemConstant.API_TIMEOUT)
+                .execute();
+        JSONObject jsonObject = JSONUtil.parseObj(execute.body());
+        log.info("腾讯数据返回结果:{}", execute.body());
+        JSONObject data = jsonObject.getJSONObject("body").getJSONObject("data");
+        JSONArray rows = data.getJSONArray("list");
+        JSONObject jsonObject1 = rows.getJSONObject(0);
+        SiteStatistics siteStatistics = new SiteStatistics();
+        siteStatistics.setImpressionCount(Integer.valueOf(jsonObject1.getStr("view_count")));
+        siteStatistics.setClickCount(Integer.valueOf(jsonObject1.getStr("valid_click_count")));
+        siteStatistics.setActualCost(new BigDecimal(jsonObject1.getStr("cost")));
+        siteStatistics.setAccountCost(new BigDecimal(jsonObject1.getStr("cost")));
+        siteStatistics.setClickRate(new BigDecimal(jsonObject1.getStr("ctr")));
+        siteStatistics.setAvgClickPrice(new BigDecimal(jsonObject1.getStr("cpc")));
+        return siteStatistics;
     }
 
 
@@ -110,7 +148,7 @@ public class TencentApiClient extends AbstractApiClient implements IAccessTokenC
                     .form("client_secret", codeVo.getAppSecret())
                     .form("grant_type", "authorization_code")
                     .form("authorization_code", codeVo.getAuthCode())
-                    .form("redirect_uri", "authorization_code")
+                    // .form("redirect_uri", "authorization_code")
                     .timeout(SystemConstant.API_TIMEOUT)
                     .execute();
         });

+ 18 - 0
fs-service/src/main/java/com/fs/newAdv/mapper/AdvMiniConfigMapper.java

@@ -0,0 +1,18 @@
+package com.fs.newAdv.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.newAdv.domain.AdvMiniConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 广告微信小程序配置 Mapper 接口
+ * </p>
+ *
+ * @author zhangqin
+ * @since 2025-12-19
+ */
+@Mapper
+public interface AdvMiniConfigMapper extends BaseMapper<AdvMiniConfig> {
+
+}

+ 16 - 0
fs-service/src/main/java/com/fs/newAdv/service/IAdvMiniConfigService.java

@@ -0,0 +1,16 @@
+package com.fs.newAdv.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.newAdv.domain.AdvMiniConfig;
+
+/**
+ * <p>
+ * 广告微信小程序配置 服务类
+ * </p>
+ *
+ * @author zhangqin
+ * @since 2025-12-19
+ */
+public interface IAdvMiniConfigService extends IService<AdvMiniConfig> {
+
+}

+ 1 - 1
fs-service/src/main/java/com/fs/newAdv/service/ILeadService.java

@@ -41,7 +41,7 @@ public interface ILeadService extends IService<Lead> {
      * @param mpOpenId
      * @param phone
      */
-    void weChatAuthorizationLead(String traceId, String unionId, String mpOpenId, String phone);
+    void weChatAuthorizationLead(String traceId, String unionId, String maOpenId, String phone);
 
     /**
      * 小程序授权落地页访问线索处理

+ 20 - 0
fs-service/src/main/java/com/fs/newAdv/service/impl/AdvMiniConfigServiceImpl.java

@@ -0,0 +1,20 @@
+package com.fs.newAdv.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.newAdv.domain.AdvMiniConfig;
+import com.fs.newAdv.mapper.AdvMiniConfigMapper;
+import com.fs.newAdv.service.IAdvMiniConfigService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 广告微信小程序配置 服务实现类
+ * </p>
+ *
+ * @author zhangqin
+ * @since 2025-12-19
+ */
+@Service
+public class AdvMiniConfigServiceImpl extends ServiceImpl<AdvMiniConfigMapper, AdvMiniConfig> implements IAdvMiniConfigService {
+
+}

+ 6 - 4
fs-service/src/main/java/com/fs/newAdv/service/impl/LeadServiceImpl.java

@@ -118,20 +118,22 @@ public class LeadServiceImpl extends ServiceImpl<LeadMapper, Lead> implements IL
 
     @Override
     @Async
-    public void weChatAuthorizationLead(String traceId, String unionId, String mpOpenId, String phone) {
+    public void weChatAuthorizationLead(String traceId, String unionId, String maOpenId, String phone) {
        try{
+           log.info("用户微信授权线索信息:{}", traceId);
            Lead byTraceId = this.getByTraceId(traceId);
            if (byTraceId == null) {
                return;
            }
            this.update(new LambdaUpdateWrapper<Lead>()
                    .eq(Lead::getTraceId, traceId)
-                   .set(Lead::getUnionid, unionId)
-                   .set(Lead::getPhone, phone)
-                   .set(Lead::getOpenid, mpOpenId)
+                   .set(ObjectUtil.isNotEmpty(unionId),Lead::getUnionid, unionId)
+                   .set(ObjectUtil.isNotEmpty(phone),Lead::getPhone, phone)
+                   .set(ObjectUtil.isNotEmpty(maOpenId),Lead::getOpenid, maOpenId)
                    .set(Lead::getMiniAuth, 1));
            if (ObjectUtil.isNotEmpty(byTraceId.getLandingPageTs()) && byTraceId.getLandingPageTs().toLocalDate().isEqual(LocalDate.now())) {
                // 微信授权且当日创建事件
+               log.info("用户微信授权线索事件回传:{}", traceId);
                conversionEventPublisher.publishConversionEvent(traceId, SystemEventTypeEnum.AUTH_TODAY_CREATE);
            }
        }catch (Exception e){

+ 1 - 1
fs-service/src/main/java/com/fs/newAdv/vo/AccessTokenByAuthCodeVo.java

@@ -11,7 +11,7 @@ public class AccessTokenByAuthCodeVo implements Serializable {
     private String appId;
     private String appSecret;
     private String authCode;
-    private String userId;
+    private String adAccountId;
 
 
 

+ 3 - 3
fs-service/src/main/java/com/fs/qw/domain/QwCompany.java

@@ -89,11 +89,11 @@ public class QwCompany extends BaseEntity
     /**
      * 御君方云医小程序原始id
      */
-    private String yjfyyAppId;
+    private String shareAppId;
     /**
      * 御君方云医应用id
      */
-    private String yjfyyAgentId;
+    private String shareAgentId;
 
-    private String yjfyySchema;
+    private String shareSchema;
 }

+ 5 - 0
fs-service/src/main/java/com/fs/qw/param/QwGroupChatParam.java

@@ -60,4 +60,9 @@ public class QwGroupChatParam {
      */
     private String userType;
 
+    /**
+     * 员工账号
+     */
+    private String userName;
+
 }

+ 4 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -1309,6 +1309,10 @@ public class QwUserServiceImpl implements IQwUserService
         WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
         wxWorkGetQrCodeDTO.setUuid(qwUser.getUid());
         try {
+            redisCache.deleteObject("qrCode:uuid:"+qwUser.getUid());
+            redisCache.deleteObject("qrCodeUid:"+qwUser.getUid());
+            redisCache.deleteObject("qrCode:qwUserId:"+qwUser.getId());
+            redisCache.deleteObject("qrCodeUid:qwUserId:"+qwUser.getId());
             wxWorkService.LoginOut(wxWorkGetQrCodeDTO,qwUser.getServerId());
         }catch (Exception e){
 

+ 4 - 0
fs-service/src/main/java/com/fs/sop/domain/QwSopTempRules.java

@@ -86,4 +86,8 @@ public class QwSopTempRules{
     
      /**是否@所有人  1是0否**/
     private Integer isAtAll;
+    /**
+     * 直播间id
+     */
+    private Long liveId;
 }

+ 6 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java

@@ -99,4 +99,10 @@ public interface QwSopTempRulesMapper extends BaseMapper<QwSopTempRules> {
     List<QwSopTempRules> listByTempIdAndNameAndDayNum(@Param("id") String id, @Param("dayNum") Integer dayNum);
 
     void deleteByIdList(@Param("ids") List<String> ids);
+
+    List<Long> getTempOfficialIdsForOpen(@Param("tempId")String tempId);
+
+    List<Long> getTempOfficialIdsForClose(@Param("tempId") String tempId);
+
+    int updateTempRulesOfficialBatch(@Param("ids") List<Long> ids,@Param("official") Integer official);
 }

+ 22 - 0
fs-service/src/main/java/com/fs/sop/params/BatchOpenOrCloseOfficialParam.java

@@ -0,0 +1,22 @@
+package com.fs.sop.params;
+
+import lombok.Data;
+
+/**
+ * @author MixLiu
+ * @date 2025/12/22 上午10:30)
+ */
+@Data
+public class BatchOpenOrCloseOfficialParam {
+
+    /**
+     * 模板id
+     */
+    private String tempId;
+
+    /**
+     * 是否官方 0 关闭 1 开启
+     */
+    private Integer isOfficial;
+
+}

+ 5 - 0
fs-service/src/main/java/com/fs/sop/params/SendUserLogsInfoMsgParam.java

@@ -36,4 +36,9 @@ public class SendUserLogsInfoMsgParam {
 
     private List<String> qwUserIds; //当isMine为1 需要查询该主体下该员工的营期
 
+    /**
+     * 直播间id
+     */
+    private Long liveId;
+
 }

+ 10 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopTempService.java

@@ -1,8 +1,10 @@
 package com.fs.sop.service;
 
+import com.fs.common.core.domain.R;
 import com.fs.qw.vo.SortDayVo;
 import com.fs.sop.domain.QwSopTemp;
 import com.fs.sop.domain.QwSopTempDay;
+import com.fs.sop.params.BatchOpenOrCloseOfficialParam;
 import com.fs.sop.params.QwSopShareTempParam;
 import com.fs.sop.vo.QwSopTempRedPackageVo;
 
@@ -101,4 +103,12 @@ public interface IQwSopTempService {
     List<String> getSelectableRange();
 
     void syncTemplate(Long courseId);
+
+    /**
+     * sop模板update一键开关官方群发
+     * @param param
+     * @return
+     */
+    R batchOpenOrCloseOfficial(BatchOpenOrCloseOfficialParam param);
+
 }

+ 30 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -8,9 +8,11 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.DataSource;
+import com.fs.common.core.domain.R;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.service.ICompanyService;
 import com.fs.config.cloud.CloudHostProper;
@@ -30,6 +32,8 @@ import com.fs.qw.vo.QwUserVO;
 import com.fs.qw.vo.SortDayVo;
 import com.fs.sop.domain.*;
 import com.fs.sop.mapper.QwSopTempMapper;
+import com.fs.sop.mapper.QwSopTempRulesMapper;
+import com.fs.sop.params.BatchOpenOrCloseOfficialParam;
 import com.fs.sop.params.QwSopShareTempParam;
 import com.fs.sop.service.*;
 import com.fs.sop.vo.QwSopTempRedPackageVo;
@@ -84,6 +88,8 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
     private final ICompanyService companyService;
     private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
+    @Autowired
+    QwSopTempRulesMapper qwSopTempRulesMapper;
     /**
      * 查询sop模板
      *
@@ -788,4 +794,28 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
 //        }
     }
 
+    /**
+     * sop模板update一键开关官方群发
+     * @param param
+     * @return
+     */
+    public R batchOpenOrCloseOfficial(BatchOpenOrCloseOfficialParam param){
+        List<Long> updateIds = new ArrayList<>();
+        if(StringUtils.isBlank(param.getTempId())){
+            return R.error("参数错误,请确认模板id是否正确");
+        }
+        if(Integer.valueOf(1).equals(param.getIsOfficial())){
+            updateIds = qwSopTempRulesMapper.getTempOfficialIdsForOpen(param.getTempId());
+        }else if(Integer.valueOf(0).equals(param.getIsOfficial())){
+            updateIds = qwSopTempRulesMapper.getTempOfficialIdsForClose(param.getTempId());
+        }else{
+            return R.error("参数错误,请确认选择类型是否正确");
+        }
+
+        if(null != updateIds && !updateIds.isEmpty()){
+            qwSopTempRulesMapper.updateTempRulesOfficialBatch(updateIds, param.getIsOfficial());
+        }
+        return  R.ok("操作成功");
+    }
+
 }

+ 23 - 1
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -489,6 +489,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
     }
     @Override
     public R sendUserLogsInfoMsg(SendUserLogsInfoMsgParam param) {
+        Boolean sendLiveMsg = Boolean.FALSE;
+        if(null != param.getLiveId()){
+            sendLiveMsg = Boolean.TRUE;
+        }
+        Boolean sendLiveMsgFinal = sendLiveMsg;
         QwSop qwSop = qwSopMapper.selectQwSopById(param.getSopId());
         List<FastGptChatReplaceWords> words = fastGptChatReplaceWordsMapper.selectAllFastGptChatReplaceWords();
         String json = configService.selectConfigByKey("course.config");
@@ -578,6 +583,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     sopLogs.setCorpId(qwGroupChat.getCorpId());
                     sopLogs.setSort(30000001);
                     sopLogs.setSendType(2);
+                    if(sendLiveMsgFinal){
+                        sopLogs.setSendType(20);
+                    }
                     sopLogs.setExternalUserName(groupUser.getName());
                     sopLogs.setQwUserKey(qwUser.getId());
 
@@ -754,6 +762,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     setting.setVideoId(param.getVideoId());
                     setting.setCourseId(param.getCourseId());
                     setting.setCourseType(param.getCourseType());
+                    setting.setLiveId(param.getLiveId());
                     sopLogs.setContentJson(JSON.toJSONString(setting));
                     return sopLogs;
                 }).filter(Objects::nonNull).collect(Collectors.toList());
@@ -780,6 +789,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     sopLogs.setCorpId(groupChat.getCorpId());
                     sopLogs.setSort(2);
                     sopLogs.setSendType(6);
+                    if(sendLiveMsgFinal){
+                        sopLogs.setSendType(20);
+                    }
                     sopLogs.setExternalUserName(groupChat.getName());
                     sopLogs.setQwUserKey(qwUser.getId());
                     // 设置实际发送人
@@ -968,6 +980,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     setting.setVideoId(param.getVideoId());
                     setting.setCourseId(param.getCourseId());
                     setting.setCourseType(param.getCourseType());
+                    setting.setLiveId(param.getLiveId());
                     sopLogs.setContentJson(JSON.toJSONString(setting));
                     return sopLogs;
                 }).collect(Collectors.toList());
@@ -1027,6 +1040,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 sopLogs.setSort(30000000);
                 sopLogs.setSendType(5);
                 sopLogs.setExternalUserName(item.getExternalUserName());
+                   if(sendLiveMsgFinal){
+                       sopLogs.setSendType(20);
+                   }
 
                 QwExternalContact contact = qwExternalContactMapper.selectQwExternalContactByIdForStageStatus(item.getExternalId());
 
@@ -1164,7 +1180,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             st.setContentType("4");
                             String js = configService.selectConfigByKey("his.config");
                             FSSysConfig sysConfig= JSON.parseObject(js,FSSysConfig.class);
-                            //todo 发个人看课记录处理
+                            //发个人看课记录处理
                             try {
                                     createLiveWatchLogAndInsert(qwUser.getCompanyId().toString(), qwUser.getCompanyUserId().toString(),item.getExternalId().toString(),Long.valueOf(st.getLiveId()),sysConfig.getAppId(),2, qwUserId,param.getCorpId());
 
@@ -1299,6 +1315,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 setting.setVideoId(param.getVideoId());
                 setting.setCourseId(param.getCourseId());
                 setting.setCourseType(param.getCourseType());
+                setting.setLiveId(param.getLiveId());
 
                 try {
                     sopLogs.setQwUserKey(Long.valueOf(qwUserId));
@@ -1458,6 +1475,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
         // 遍历分组
         int finalSort = sort;
+        if(null != param.getLiveId()){
+            sendType = 20;
+        }
         int finalSendType = sendType;
         groupedLogs.forEach((key, logs) -> {
 
@@ -1546,6 +1566,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
             switch (finalSendType){
                 case 5:
+                case 20:
                     List<QwSopCourseFinishTempSetting.Setting> list = processSetting(item,qwUser, param, words, config, qwCompany,companyUserId,companyId,
                             contact,dataTime, finalDomainName,miniMap,companies,sopLogs);
                     setting.setSetting(list);
@@ -1574,6 +1595,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             setting.setVideoId(param.getVideoId());
             setting.setCourseId(param.getCourseId());
             setting.setCourseType(param.getCourseType());
+            setting.setLiveId(param.getLiveId());
             try {
                 sopLogs.setQwUserKey(qwUser.getId());
             }catch (Exception e){

+ 7 - 0
fs-service/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java

@@ -6,6 +6,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.Date;
 import java.util.Set;
 
 
@@ -89,6 +90,12 @@ public class FsUserPageListParam implements Serializable {
      */
     private Boolean isHidePhoneMiddle = Boolean.TRUE;
 
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date eTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date sTime;
+
 
 }
 

+ 8 - 3
fs-service/src/main/resources/application-config-druid-bjzm-test.yml

@@ -4,14 +4,19 @@ baidu:
 #配置
 logging:
   level:
+    com:
+      fs:
+        hisStore:
+            mapper: debug
+    com.fs.live: debug
     org.springframework.web: INFO
     com.github.binarywang.demo.wx.cp: DEBUG
     me.chanjar.weixin: DEBUG
 wx:
   miniapp:
     configs:
-      - appid: wx44beed5640bcb1ba   #北京卓美
-        secret: 1bfcfa420f741801575a74d94752d014 #北京卓美
+      - appid: wxd791d5933ed42218   #北京卓美
+        secret: 3d2e220de33d67aeb2140edeec692b73 #北京卓美
         token: cbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON
@@ -61,7 +66,7 @@ fs :
     # token有效时长,7天,单位秒
     expire: 31536000
     header: AppToken
-  commonApi: http://172.16.16.6:7771
+  commonApi: http://127.0.0.1:7771
   h5CommonApi: http://172.16.16.6:7771
 nuonuo:
   key: 10924508

+ 2 - 2
fs-service/src/main/resources/application-config-druid-bjzm.yml

@@ -10,8 +10,8 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wx44beed5640bcb1ba   #北京卓美
-        secret: 1bfcfa420f741801575a74d94752d014 #北京卓美
+      - appid: wxd791d5933ed42218   #北京卓美
+        secret: 3d2e220de33d67aeb2140edeec692b73 #北京卓美
         token: cbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON

+ 1 - 1
fs-service/src/main/resources/application-config-druid-cfryt.yml

@@ -89,7 +89,7 @@ headerImg:
   imgUrl: https://ysy-1329817240.cos.ap-guangzhou.myqcloud.com/ysy/20250820/2c47e4f105b641b4a49df50a77338e32.png
 ipad:
   ipadUrl: http://ipad.cfrytjkzx.cn
-  aiApi:
+  aiApi: http://49.232.181.28:3000/api
   voiceApi:
   commonApi:
 wx_miniapp_temp:

+ 2 - 2
fs-service/src/main/resources/application-config-druid-ylrz.yml

@@ -80,8 +80,8 @@ tencent_cloud_config:
 cloud_host:
   company_name: 云联融智
   projectCode: YLRZ
-  spaceName:
-  volcengineUrl: https://myhkvolcengine.ylrztop.com
+  spaceName: ylrz-2114522511
+  volcengineUrl: https://ylrzvolcengine.ylrztop.com
 headerImg:
   imgUrl:
 

+ 2 - 2
fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -56,7 +56,7 @@
         fs_user_course_period.update_time,
         fs_user_course_period.period_status,
         fs_user_course_period.max_view_num,
-        fs_user_course_period.is_open_rest_reminder,
+        fs_user_course_period.is_open_rest_flag,
         course_style,
         live_room_style,
         red_packet_grant_method,
@@ -167,7 +167,7 @@
             <if test="courseLogo != null and courseLogo !=''">course_logo = #{courseLogo},</if>
             <if test="openCommentStatus != null">open_comment_status = #{openCommentStatus},</if>
             <if test="periodLine != null">period_line = #{periodLine},</if>
-            <if test="isOpenRestReminder != null">is_open_rest_reminder = #{isOpenRestReminder},</if>
+            <if test="isOpenRestFlag != null">is_open_rest_flag = #{isOpenRestFlag},</if>
         </trim>
         where period_id = #{periodId}
     </update>

+ 16 - 4
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -401,6 +401,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="projectId != null">
                 AND ucu.project_id = #{projectId}
             </if>
+            <if test="eTime != null and sTime !=null">
+                AND fs_user.create_time &gt;= #{sTime} AND fs_user.create_time &lt;= #{eTime}
+            </if>
         </where>
         limit ${(pageNum-1)*pageSize},${pageSize}
     </select>
@@ -861,7 +864,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     </when>
                 </choose>
             </if>
-            and fs_user.user_id = #{fsUserId} and fucu.company_user_id =#{userId}
+            and fs_user.user_id = #{fsUserId}
+            <if test="userCompanyId!=null">
+                and fucu.id = #{userCompanyId}
+            </if>
         </where>
         GROUP BY
         fs_user.user_id
@@ -892,7 +898,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     </when>
                 </choose>
             </if>
-            and fs_user.user_id = #{fsUserId} and fucu.company_user_id =#{userId}
+            and fs_user.user_id = #{fsUserId}
+            <if test="userCompanyId!=null">
+                and fucu.id = #{userCompanyId}
+            </if>
         </where>
         GROUP BY
         fs_user.user_id
@@ -927,7 +936,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     </when>
                 </choose>
             </if>
-            and  fs_user.user_id = #{fsUserId} and fucu.company_user_id =#{userId}
+            and  fs_user.user_id = #{fsUserId}
+            <if test="userCompanyId!=null">
+                and fucu.id = #{userCompanyId}
+            </if>
         </where>
         GROUP BY
         fs_user.user_id
@@ -1991,7 +2003,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFsUserVOListByProjectNew" resultType="com.fs.his.vo.FsUserVO">
         SELECT
             u.user_id, u.nick_name, u.avatar, u.phone, u.integral, u.now_money,
-            ucu.project_id,ucu.company_user_id as companyUserId,ucu.create_time as bindTime,ucu.status,
+            ucu.project_id,ucu.id as companyUserId,ucu.create_time as bindTime,ucu.status,
             company.company_name,
             cu.nick_name   companyUserNickName
         FROM

+ 2 - 1
fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml

@@ -28,11 +28,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="orderId"    column="order_id"    />
         <result property="isPayRemain"    column="is_pay_remain"    />
         <result property="payMode"    column="pay_mode"    />
+        <result property="appId"    column="app_id"    />
         <result property="businessCode"    column="business_code"    />
     </resultMap>
 
     <sql id="selectFsStorePaymentVo">
-        select payment_id,pay_mode, pay_code, pay_type_code, pay_money, pay_time, create_time, trade_no, user_id, open_id, business_type, business_order_id, status,remark,company_id,company_user_id,dept_id,bank_transaction_id,bank_serial_no,refund_money,refund_time,order_id,is_pay_remain,business_code from fs_store_payment_scrm
+        select payment_id,app_id,pay_mode, pay_code, pay_type_code, pay_money, pay_time, create_time, trade_no, user_id, open_id, business_type, business_order_id, status,remark,company_id,company_user_id,dept_id,bank_transaction_id,bank_serial_no,refund_money,refund_time,order_id,is_pay_remain,business_code from fs_store_payment_scrm
 
     </sql>
 

+ 2 - 2
fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml

@@ -461,11 +461,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </update>
 
     <delete id="deleteFsStoreProductById" parameterType="Long">
-        delete from fs_store_product_scrm where product_id = #{productId}
+        update  fs_store_product_scrm set id_del = 1 where product_id = #{productId}
     </delete>
 
     <delete id="deleteFsStoreProductByIds" parameterType="String">
-        delete from fs_store_product_scrm where product_id in
+        update fs_store_product_scrm set id_del = 1 where product_id in
         <foreach item="productId" collection="array" open="(" separator="," close=")">
             #{productId}
         </foreach>

+ 20 - 8
fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml

@@ -61,7 +61,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       o.delivery_pay_status,
       o.total_price,
       csc.NAME AS mini_program_name,
-      sp_latest.bank_transaction_id
+      sp_latest.bank_transaction_id,
+        CONCAT('store-',sp_latest.pay_code) as hfshh
       FROM
       fs_store_order_scrm o
       left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
@@ -74,7 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
       LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
         LEFT JOIN company c ON c.company_id = cu.company_id
-      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id
       AND sp_latest.rn = 1
       LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
           WHERE  o.company_id IS NOT NULL
@@ -144,6 +145,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           <if test="maps.appId != null and maps.appId != ''">
             AND csc.appid = #{maps.appId}
           </if>
+        <if test="maps.hfshh != null and maps.hfshh != ''">
+            AND hfshh = #{maps.hfshh}
+        </if>
           group by o.id
           UNION ALL
           -- 商城订单(没有company_user_id的商城订单)
@@ -202,8 +206,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       o.delivery_pay_status,
       o.total_price,
       csc.NAME AS mini_program_name,
-      sp_latest.bank_transaction_id
-      FROM
+      sp_latest.bank_transaction_id,
+        CONCAT('store-',sp_latest.pay_code) as hfshh
+        FROM
       fs_store_order_scrm o
         left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
       LEFT JOIN fs_user u ON o.user_id = u.user_id
@@ -215,7 +220,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
       LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
         LEFT JOIN company c ON c.company_id = cu.company_id
-      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id
       AND sp_latest.rn = 1
       LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
           WHERE  o.company_id is null
@@ -285,6 +290,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           <if test="maps.appId != null and maps.appId != ''">
             AND csc.appid = #{maps.appId}
           </if>
+        <if test="maps.hfshh != null and maps.hfshh != ''">
+            AND hfshh = #{maps.hfshh}
+        </if>
         group by o.id
           UNION ALL
           -- 直播订单
@@ -343,7 +351,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       o.delivery_pay_status,
       o.total_price,
       csc.NAME AS mini_program_name,
-      sp_latest.bank_transaction_id
+      sp_latest.bank_transaction_id,
+        CONCAT('live-',sp_latest.pay_code) as hfshh
       FROM
       live_order o
       left join live_order_item loi on loi.order_id = o.order_id
@@ -359,10 +368,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         LEFT JOIN company c ON c.company_id = cu.company_id
       LEFT JOIN ( SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY t.order_id ORDER BY t.create_time DESC ) AS rn FROM live_after_sales t ) a ON o.order_id = a.order_id
       AND a.rn = 1
-      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM live_order_payment sp ) sp_latest ON sp_latest.business_code = o.order_code
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_id ORDER BY sp.create_time DESC ) AS rn FROM live_order_payment sp ) sp_latest ON sp_latest.business_id = o.order_id
       AND sp_latest.rn = 1
       LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
-          WHERE o.is_del = 0 and fspc.product_id IS NOT NULL
+          WHERE o.is_del = 0
           <if test="maps.status != null and maps.status != ''">
             AND o.status = #{maps.status}
           </if>
@@ -423,6 +432,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
             AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
           </if>
+        <if test="maps.hfshh != null and maps.hfshh != ''">
+            AND hfshh = #{maps.hfshh}
+        </if>
         group by o.order_id
         ) AS merged_orders
         WHERE 1=1

+ 53 - 0
fs-service/src/main/resources/mapper/live/LiveAfterSalesMapper.xml

@@ -115,6 +115,59 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
         order by las.create_time desc
     </select>
+    <select id="selectLiveAfterSalesVoListExport" parameterType="com.fs.live.vo.LiveAfterSalesVo" resultType="com.fs.live.vo.LiveAfterSalesVo">
+        select las.id, las.live_id, las.store_id, las.refund_amount,
+        las.refund_type, las.reasons, las.explains, las.explain_img, las.delivery_code, las.delivery_sn, las.delivery_name, las.status, las.sales_status,
+        las.order_status, las.create_time, las.is_del, las.user_id, las.consignee, las.phone_number, las.address, las.company_id, las.company_user_id, las.dept_id,
+        cu.nick_name as company_user_nick_name, c.company_name,lo.order_id,lo.order_code,lo.user_phone,lo.item_json,lo.pay_time as orderPayTime,
+        lo.user_address,lo.user_name,lo.pay_price,lo.total_postage,lop.bank_serial_no,lo.delivery_sn as orderDeliveryId,lo.delivery_name as orderDeliveryName,
+        lo.delivery_code as orderDeliverySn,lo.status as orderStatus,lop.bank_transaction_id,lo.pay_money,lop.pay_code as payCode
+        from live_after_sales las
+        left join live_order lo on lo.order_id = las.order_id
+        left join company_user cu on cu.user_id = las.company_user_id
+        left join company c on c.company_id = cu.company_id
+        left join live_order_payment lop on lop.business_id = lo.order_id and lop.status in (1,-1)
+        <if test="productName != null and productName != ''">
+        left join live_order_item loi on loi.order_id = lo.order_id
+        </if>
+
+        where 1=1 and las.status =4
+            <if test="hfOrderCode != null and hfOrderCode != ''"> and lop.pay_code = #{hfOrderCode}</if>
+            <if test="liveId != null and liveId != ''"> and las.live_id = #{liveId}</if>
+            <if test="companyUserNickName != null and companyUserNickName != ''"> and cu.nick_name like concat(#{companyUserNickName},'%')</if>
+            <if test="storeId != null and storeId != ''"> and las.store_id = #{storeId}</if>
+            <if test="orderCode != null and orderCode != ''"> and lo.order_code = #{orderCode}</if>
+            <if test="productNameQuery != null and productNameQuery != ''"> and JSON_UNQUOTE(JSON_EXTRACT(loi.json_info, '$.productName')) like concat('%', #{productNameQuery}, '%')</if>
+            <if test="refundAmount != null "> and las.refund_amount = #{refundAmount}</if>
+            <if test="refundType != null "> and las.refund_type = #{refundType}</if>
+            <if test="status != null "> and las.status = #{status}</if>
+            <if test="salesStatus != null "> and las.sales_status = #{salesStatus}</if>
+            <if test="orderStatus != null "> and las.order_status = #{orderStatus}</if>
+            <if test="reasons != null  and reasons != ''"> and las.reasons = #{reasons}</if>
+            <if test="explains != null  and explains != ''"> and las.explains = #{explains}</if>
+            <if test="explainImg != null  and explainImg != ''"> and las.explain_img = #{explainImg}</if>
+            <if test="deliveryCode != null  and deliveryCode != ''"> and las.delivery_code = #{deliveryCode}</if>
+            <if test="deliverySn != null  and deliverySn != ''"> and las.delivery_sn = #{deliverySn}</if>
+            <if test="deliveryName != null  and deliveryName != ''"> and las.delivery_name like concat('%', #{deliveryName}, '%')</if>
+            <if test="status != null "> and las.status = #{status}</if>
+            <if test="salesStatus != null "> and las.sales_status = #{salesStatus}</if>
+            <if test="orderStatus != null "> and las.order_status = #{orderStatus}</if>
+            <if test="deliveryStatus != null and deliveryStatus!= ''"> and las.order_status = #{deliveryStatus}</if>
+            <if test="isDel != null  and isDel != ''"> and las.is_del = #{isDel}</if>
+            <if test="userId != null "> and las.user_id = #{userId}</if>
+            <if test="consignee != null  and consignee != ''"> and las.consignee = #{consignee}</if>
+            <if test="phoneNumber != null  and phoneNumber != ''"> and las.phone_number = #{phoneNumber}</if>
+            <if test="address != null  and address != ''"> and las.address = #{address}</if>
+            <if test="companyId != null "> and las.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and las.company_user_id = #{companyUserId}</if>
+            <if test="deptId != null "> and cu.dept_id = #{deptId}</if>
+            <if test="userPhone != null "> and lo.user_phone like concat(#{userPhone},'%')</if>
+
+        <if test="productName != null and productName != ''">
+        group by las.id
+        </if>
+        order by las.create_time desc
+    </select>
 
     <select id="selectLiveAfterSalesById" parameterType="Long" resultMap="LiveAfterSalesResult">
         <include refid="selectLiveAfterSalesVo"/>

+ 1 - 1
fs-service/src/main/resources/mapper/live/LiveCompletionPointsRecordMapper.xml

@@ -101,7 +101,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <!-- 查询用户的完课积分领取记录列表 -->
     <select id="selectRecordsByUser" resultMap="LiveCompletionPointsRecordResult">
         SELECT * FROM live_completion_points_record
-        WHERE user_id = #{userId}
+        WHERE user_id = #{userId} and receive_status = 1
         ORDER BY current_completion_date DESC
     </select>
 

+ 7 - 7
fs-service/src/main/resources/mapper/live/LiveDataMapper.xml

@@ -414,7 +414,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             COALESCE(video_duration.total_duration, 0) AS videoDuration,
             COUNT(DISTINCT lwu.user_id) AS totalViewers,
             COUNT(DISTINCT CASE
-                WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                WHEN COALESCE(user_duration.total_duration, 0) >= 1200
                 THEN lwu.user_id
             END) AS totalCompletedCourses,
             CASE
@@ -425,30 +425,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     END) * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
                 ELSE 0
             END AS totalCompletionRate,
-            COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 and lwu.online_seconds > 0 THEN lwu.user_id END) AS liveViewers,
             COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) AS liveOver20Minutes,
             COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) AS liveOver30Minutes,
             CASE
                 WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) > 0 THEN
-                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END), 2)
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 and lwu.online_seconds > 0  THEN lwu.user_id END), 2)
                 ELSE 0
             END AS liveCompletionRate20,
             CASE
                 WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) > 0 THEN
-                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END), 2)
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 and lwu.online_seconds > 0  THEN lwu.user_id END), 2)
                 ELSE 0
             END AS liveCompletionRate30,
-            COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
+            COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 and lwu.online_seconds > 0 THEN lwu.user_id END) AS playbackViewers,
             COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) AS playbackOver20Minutes,
             COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) AS playbackOver30Minutes,
             CASE
                 WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END) > 0 THEN
-                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END), 2)
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1200 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 and lwu.online_seconds > 0  THEN lwu.user_id END), 2)
                 ELSE 0
             END AS playbackCompletionRate20,
             CASE
                 WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END) > 0 THEN
-                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END), 2)
+                    ROUND(COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 and lwu.online_seconds > 0  THEN lwu.user_id END), 2)
                 ELSE 0
             END AS playbackCompletionRate30,
             COALESCE(ld.peak_concurrent_viewers, 0) AS livePeak,

+ 2 - 0
fs-service/src/main/resources/mapper/live/LiveUserRedRecordMapper.xml

@@ -27,6 +27,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userId != null "> and user_id = #{userId}</if>
             <if test="createTime != null "> and create_time = #{createTime}</if>
         </where>
+
+        order by create_time desc
     </select>
 
     <select id="selectLiveUserRedRecordById" parameterType="Long" resultMap="LiveUserRedRecordResult">

+ 16 - 16
fs-service/src/main/resources/mapper/qw/QwCompanyMapper.xml

@@ -30,13 +30,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="msgPrivateKey"    column="msg_private_key"    />
         <result property="miniAppId"    column="mini_app_id"    />
         <result property="companyServerNum"    column="company_server_num"    />
-        <result property="yjfyyAppId"    column="yjfyy_app_id"    />
-        <result property="yjfyyAgentId"    column="yjfyy_agent_id"    />
-        <result property="yjfyySchema"    column="yjfyy_schema"    />
+        <result property="shareAppId"    column="share_app_id"    />
+        <result property="shareAgentId"    column="share_agent_id"    />
+        <result property="shareSchema"    column="share_schema"    />
     </resultMap>
 
     <sql id="selectQwCompanyVo">
-        select id, corp_id, corp_name, open_secret, open_corp_id, server_agent_id, server_book_corp_id, server_book_secret, token, encoding_aes_key, provider_secret, realm_name_url, notify_url, chat_toolbar, chat_toolbar_oauth, company_ids, status, create_time, update_time, create_by,is_buy,mini_app_id,company_server_num,yjfyy_app_id,yjfyy_agent_id,yjfyy_schema from qw_company
+        select id, corp_id, corp_name, open_secret, open_corp_id, server_agent_id, server_book_corp_id, server_book_secret, token, encoding_aes_key, provider_secret, realm_name_url, notify_url, chat_toolbar, chat_toolbar_oauth, company_ids, status, create_time, update_time, create_by,is_buy,mini_app_id,company_server_num,share_app_id,share_agent_id,share_schema from qw_company
     </sql>
 
     <select id="selectQwCompanyList" parameterType="QwCompany" resultMap="QwCompanyResult">
@@ -61,9 +61,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isBuy != null "> and isBuy = #{isBuy}</if>
             <if test="createDeptId != null "> and create_dept_id = #{createDeptId}</if>
             <if test="createUserId != null "> and create_user_id = #{createUserId}</if>
-            <if test="yjfyyAppId != null  and yjfyyAppId != ''"> and yjfyy_app_id = #{yjfyyAppId}</if>
-            <if test="yjfyyAgentId != null  and yjfyyAgentId != ''"> and yjfyy_agent_id = #{yjfyyAgentId}</if>
-            <if test="yjfyySchema != null  and yjfyySchema != ''"> and yjfyy_schema = #{yjfyySchema}</if>
+            <if test="shareAppId != null  and shareAppId != ''"> and share_app_id = #{shareAppId}</if>
+            <if test="shareAgentId != null  and shareAgentId != ''"> and share_agent_id = #{shareAgentId}</if>
+            <if test="shareSchema != null  and shareSchema != ''"> and share_schema = #{shareSchema}</if>
         </where>
     </select>
 
@@ -105,9 +105,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyServerNum != null">company_server_num,</if>
             <if test="createUserId != null != null">create_user_id,</if>
             <if test="createDeptId != null">create_dept_id,</if>
-            <if test="yjfyyAppId != null">yjfyy_app_id,</if>
-            <if test="yjfyyAgentId != null">yjfyy_agent_id,</if>
-            <if test="yjfyySchema != null">yjfyy_schema,</if>
+            <if test="shareAppId != null">share_app_id,</if>
+            <if test="shareAgentId != null">share_agent_id,</if>
+            <if test="shareSchema != null">share_schema,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="corpId != null">#{corpId},</if>
@@ -134,9 +134,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyServerNum != null">#{companyServerNum},</if>
             <if test="createUserId != null">#{createUserId},</if>
             <if test="createDeptId != null">#{createDeptId},</if>
-            <if test="yjfyyAppId != null">#{yjfyyAppId},</if>
-            <if test="yjfyyAgentId != null">#{yjfyyAgentId},</if>
-            <if test="yjfyySchema != null">#{yjfyySchema},</if>
+            <if test="shareAppId != null">#{shareAppId},</if>
+            <if test="shareAgentId != null">#{shareAgentId},</if>
+            <if test="shareSchema != null">#{shareSchema},</if>
          </trim>
     </insert>
 
@@ -165,9 +165,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isBuy != null">is_buy = #{isBuy},</if>
             <if test="miniAppId != null">mini_app_id = #{miniAppId},</if>
             <if test="companyServerNum != null">company_server_num = #{companyServerNum},</if>
-            <if test="yjfyyAppId != null">yjfyy_app_id = #{yjfyyAppId},</if>
-            <if test="yjfyyAgentId != null">yjfyy_agent_id = #{yjfyyAgentId},</if>
-            <if test="yjfyySchema != null">yjfyy_schema = #{yjfyySchema},</if>
+            <if test="shareAppId != null">share_app_id = #{shareAppId},</if>
+            <if test="shareAgentId != null">share_agent_id = #{shareAgentId},</if>
+            <if test="shareSchema != null">share_schema = #{shareSchema},</if>
         </trim>
         where id = #{id}
     </update>

+ 3 - 0
fs-service/src/main/resources/mapper/qw/QwGroupChatMapper.xml

@@ -63,6 +63,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         left join qw_user qu on qu.qw_user_id = gc.owner and qu.is_del = 0 and qu.corp_id = gc.corp_id
         left join company_user cu on cu.user_id = qu.company_user_id
         <where>
+            <if test="userName != null and userName != ''">
+                and cu.user_name like concat('%', #{userName}, '%')
+            </if>
             <if test="corpId != null and corpId != ''">
                 and gc.corp_id = #{corpId}
             </if>

+ 26 - 0
fs-service/src/main/resources/mapper/sop/QwSopTempRulesMapper.xml

@@ -134,4 +134,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             CAST(#{item} AS UUID)
         </foreach>
     </delete>
+
+    <select id="getTempOfficialIdsForOpen" parameterType="java.lang.String" resultType="java.lang.Long">
+        select tt.id from
+                         (
+                          select
+                              t.id,
+                              RANK() OVER (PARTITION by t.day_id ORDER BY t.sorts ) AS rank_num
+                          from qw_sop_temp_rules  t
+                          where t.temp_id = #{tempId}
+                          ) tt
+        where tt.rank_num = 1
+    </select>
+
+    <select id="getTempOfficialIdsForClose" parameterType="java.lang.String" resultType="java.lang.Long">
+        select id from qw_sop_temp_rules where temp_id = #{tempId}  and  is_official = 1
+    </select>
+
+    <update id="updateTempRulesOfficialBatch" >
+        update qw_sop_temp_rules
+        set is_official = #{official}
+        where id in
+        <foreach item="item" collection="ids" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+    </update>
+
 </mapper>

+ 48 - 0
fs-user-app/src/main/java/com/fs/app/controller/live/LiveRedController.java

@@ -4,13 +4,24 @@ import com.fs.app.annotation.Login;
 import com.fs.app.controller.AppBaseController;
 import com.fs.app.facade.LiveFacadeService;
 import com.fs.common.core.domain.R;
+import com.fs.common.utils.ServletUtils;
+import com.fs.live.domain.LiveUserRedRecord;
 import com.fs.live.param.RedPO;
+import com.fs.live.service.ILiveUserRedRecordService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
+@Slf4j
 @RestController
 @RequestMapping("/app/live/liveRed")
 public class LiveRedController extends AppBaseController {
@@ -18,6 +29,9 @@ public class LiveRedController extends AppBaseController {
     @Autowired
     private LiveFacadeService liveFacadeService;
 
+    @Autowired
+    private ILiveUserRedRecordService liveUserRedRecordService;
+
     /**
      * 领取红包
      */
@@ -28,4 +42,38 @@ public class LiveRedController extends AppBaseController {
         return liveFacadeService.redClaim(red);
     }
 
+    /**
+     * 查询用户自己领取直播间的红包记录
+     * @return 红包记录列表
+     */
+    @Login
+    @GetMapping("/list")
+    public R list() {
+        try {
+            String userId = getUserId();
+            if (userId == null || userId.isEmpty()) {
+                return R.error("用户未登录");
+            }
+            // 分页参数
+            String pageNum = ServletUtils.getParameter("pageNum");
+            String pageSize = ServletUtils.getParameter("pageSize");
+            int page = pageNum != null && !pageNum.isEmpty() ? Integer.parseInt(pageNum) : 1;
+            int size = pageSize != null && !pageSize.isEmpty() ? Integer.parseInt(pageSize) : 10;
+
+            // 构建查询条件
+            LiveUserRedRecord query = new LiveUserRedRecord();
+            query.setUserId(Long.parseLong(userId));
+
+            // 分页查询
+            PageHelper.startPage(page, size);
+            List<LiveUserRedRecord> list = liveUserRedRecordService.selectLiveUserRedRecordList(query);
+            PageInfo<LiveUserRedRecord> pageInfo = new PageInfo<>(list);
+
+            return R.ok().put("data", pageInfo);
+        } catch (Exception e) {
+            log.error("查询用户红包记录失败", e);
+            return R.error("查询失败:" + e.getMessage());
+        }
+    }
+
 }

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/controller/store/WxUserScrmController.java

@@ -371,7 +371,7 @@ public class WxUserScrmController extends AppBaseController {
             userService.handleFsUserWx(user,loginMaWxParam,session);
             String token = jwtUtils.generateToken(user.getUserId());
             // 广告线索
-            leadService.weChatAuthorizationLead(param.getTraceId(), user.getUnionId(),user.getMpOpenId(),user.getPhone());
+            leadService.weChatAuthorizationLead(param.getTraceId(), session.getUnionid(),session.getOpenid(),user.getPhone());
             return R.ok("登录成功").put("token",token).put("user", user);
         } catch (WxErrorException e) {
             //this.logger.error(e.getMessage(), e);