Просмотр исходного кода

Merge remote-tracking branch 'origin/master_exclusive_shop_20250718' into master_exclusive_shop_20250718

zyy 1 месяц назад
Родитель
Сommit
a0abd9d9d9
53 измененных файлов с 800 добавлено и 714 удалено
  1. 8 0
      fs-admin/src/main/java/com/fs/live/controller/LiveController.java
  2. 12 10
      fs-admin/src/main/java/com/fs/task/LiveTask.java
  3. 17 0
      fs-common/src/main/java/com/fs/common/constant/RedisConstant.java
  4. 37 4
      fs-common/src/main/java/com/fs/common/core/redis/RedisUtil.java
  5. 139 0
      fs-common/src/main/java/com/fs/common/core/redis/service/StockDeductService.java
  6. 2 2
      fs-live-socket/src/main/java/com/fs/live/task/Task.java
  7. 2 1
      fs-live-socket/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  8. 1 1
      fs-live-socket/src/main/resources/application.yml
  9. 6 0
      fs-service-system/pom.xml
  10. 9 30
      fs-service-system/src/main/java/com/fs/erp/service/impl/ErpOrderServiceProxy.java
  11. 1 0
      fs-service-system/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  12. 4 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveCompletionPointsRecordMapper.java
  13. 5 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveCouponUserMapper.java
  14. 6 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveDataMapper.java
  15. 17 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveGoodsMapper.java
  16. 3 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveMapper.java
  17. 5 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  18. 3 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveOrderPaymentMapper.java
  19. 2 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveTrafficLogMapper.java
  20. 3 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveVideoMapper.java
  21. 4 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveWatchUserMapper.java
  22. 4 0
      fs-service-system/src/main/java/com/fs/live/service/ILiveGoodsService.java
  23. 2 0
      fs-service-system/src/main/java/com/fs/live/service/ILiveOrderService.java
  24. 2 0
      fs-service-system/src/main/java/com/fs/live/service/ILiveService.java
  25. 1 1
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveCompletionPointsRecordServiceImpl.java
  26. 20 4
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java
  27. 258 244
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  28. 10 2
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  29. 7 2
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveTrafficLogServiceImpl.java
  30. 2 2
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java
  31. 63 0
      fs-service-system/src/main/java/com/fs/live/utils/redis/RedisBatchHandler.java
  32. 1 1
      fs-service-system/src/main/java/com/fs/store/config/StoreConfig.java
  33. 4 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsShippingTemplatesMapper.java
  34. 5 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsShippingTemplatesRegionMapper.java
  35. 4 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsStoreCouponUserMapper.java
  36. 5 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsStoreProductAttrValueMapper.java
  37. 3 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsStoreProductMapper.java
  38. 4 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserAddressMapper.java
  39. 4 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java
  40. 1 0
      fs-service-system/src/main/java/com/fs/store/param/LiveOrderComputedParam.java
  41. 11 1
      fs-service-system/src/main/java/com/fs/store/service/impl/FsCityServiceImpl.java
  42. 11 0
      fs-service-system/src/main/resources/application-config.yml
  43. 24 0
      fs-service-system/src/main/resources/mapper/live/LiveGoodsMapper.xml
  44. 21 10
      fs-service-system/src/main/resources/mapper/live/LiveTrafficLogMapper.xml
  45. 3 0
      fs-user-app/src/main/java/com/fs/app/controller/CommonController.java
  46. 10 0
      fs-user-app/src/main/java/com/fs/app/controller/LiveDataController.java
  47. 0 1
      fs-user-app/src/main/java/com/fs/app/controller/LiveOrderController.java
  48. 13 0
      fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java
  49. 1 1
      fs-user-app/src/main/java/com/fs/app/facade/impl/LiveFacadeServiceImpl.java
  50. 15 392
      fs-user-app/src/main/java/com/fs/app/task/Task.java
  51. 1 1
      fs-user-app/src/main/java/com/fs/core/config/properties/DruidProperties.java
  52. 3 3
      fs-user-app/src/main/resources/application-dev.yml
  53. 1 1
      fs-user-app/src/main/resources/application.yml

+ 8 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveController.java

@@ -169,5 +169,13 @@ public class LiveController extends BaseController {
         return liveService.startLive(live);
     }
 
+    /**
+     * 清除直播间缓存
+     */
+    @Log(title = "直播", businessType = BusinessType.UPDATE)
+    @PostMapping("/clearCache/{liveId}")
+    public R clearCache(@PathVariable("liveId") Long liveId) {
+        return liveService.clearLiveCache(liveId);
+    }
 
 }

+ 12 - 10
fs-admin/src/main/java/com/fs/task/LiveTask.java

@@ -1,42 +1,35 @@
 package com.fs.task;
 
 import com.fs.common.annotation.QuartzRunnable;
-import com.fs.common.core.redis.RedisCache;
-import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.dto.ErpOrderQueryRequert;
 import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.service.FsJstAftersalePushService;
-import com.fs.erp.service.FsJstCodPushService;
-import com.fs.erp.service.IErpGoodsService;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.erp.utils.ErpContextHolder;
+import com.fs.live.utils.redis.RedisBatchHandler;
 import com.fs.live.domain.LiveAfterSales;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.service.*;
 import com.fs.store.domain.FsExpress;
-import com.fs.store.domain.FsStoreAfterSales;
-import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.enums.OrderLogEnum;
 import com.fs.store.mapper.*;
-import com.fs.store.param.FsStoreAfterSalesAudit1Param;
 import com.fs.store.param.LiveAfterSalesAudit1Param;
 import com.fs.store.service.*;
-import com.fs.system.service.ISysConfigService;
-import com.fs.wx.mapper.FsWxExpressTaskMapper;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang.ObjectUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import java.text.ParseException;
 import java.util.List;
 
+import static com.fs.live.utils.redis.RedisBatchHandler.CONSUME_INTERVAL;
+
 /**
  * 定时任务调度
  * @author fs
@@ -70,6 +63,8 @@ public class LiveTask {
 
     @Autowired
     public FsJstAftersalePushService fsJstAftersalePushService;
+    @Autowired
+    public RedisBatchHandler redisBatchHandler;
 
     /**
      * 超时订单自动取消
@@ -199,4 +194,11 @@ public class LiveTask {
         }
 
     }
+    /**
+     * 更新发货状态
+     */
+    @Scheduled(fixedRate = CONSUME_INTERVAL)
+    public void insertLiveTrralog() {
+        redisBatchHandler.consumeBatchData();
+    }
 }

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

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

+ 37 - 4
fs-common/src/main/java/com/fs/common/core/redis/RedisUtil.java

@@ -1,14 +1,13 @@
 package com.fs.common.core.redis;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.Cursor;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ScanOptions;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Component;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -233,6 +232,40 @@ public class RedisUtil {
         return redisTemplate.opsForHash().entries(key);
     }
 
+    /**
+     * 优化版:切片查询 Redis Hash 所有条目(分批 hscan)
+     * @param key Redis Hash 的 key
+     * @param batchSize 每次扫描的条数(建议 100-1000,根据业务调整)
+     * @return 完整的 Hash 键值对
+     */
+    public Map<Object, Object> hashEntriesByScan(String key, int batchSize) {
+        // 最终返回的完整Map(按需可改为边遍历边处理,不一次性存储)
+        Map<Object, Object> resultMap = new HashMap<>();
+
+        // 1. 构建 Scan 配置:count 指定每次扫描的建议条数(非绝对,Redis 内部优化)
+        ScanOptions scanOptions = ScanOptions.scanOptions()
+                .count(batchSize) // 每次扫描最多返回 batchSize 条
+                .match("*")       // 匹配所有字段(可指定前缀,如 "user_*" 过滤)
+                .build();
+
+        // 2. 获取游标,开始分批遍历(必须关闭游标,否则会泄漏 Redis 连接)
+        try (Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, scanOptions)) {
+            // 3. 迭代游标,分批读取数据
+            while (cursor.hasNext()) {
+                Map.Entry<Object, Object> entry = cursor.next();
+                resultMap.put(entry.getKey(), entry.getValue());
+
+                // 【可选】若不需要完整返回,可在此处边遍历边处理(比如写入数据库/缓存)
+                // processEntry(entry.getKey(), entry.getValue());
+            }
+        } catch (Exception e) {
+            // 异常处理:记录日志、告警,避免游标泄漏
+            throw new RuntimeException("Redis Hash 切片查询失败,key=" + key, e);
+        }
+
+        return resultMap;
+    }
+
     /**
      * 获取哈希表中数量
      *

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

@@ -0,0 +1,139 @@
+package com.fs.common.core.redis.service;
+
+import com.fs.common.constant.RedisConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+/**
+ * 高并发库存扣减服务(Java 8 + Redis分布式锁)
+ */
+@Slf4j
+@Service
+public class StockDeductService {
+
+    // 注入RedisTemplate
+    public final RedisTemplate<String, Object> redisTemplate;
+
+    // 构造器注入(Spring 推荐,Java 8 支持)
+    public StockDeductService(RedisTemplate<String, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    // 库存扣减Lua脚本(预编译,提升高并发性能)
+    private static final DefaultRedisScript<Long> STOCK_DEDUCT_SCRIPT;
+    // 锁释放Lua脚本(预编译)
+    private static final DefaultRedisScript<Long> LOCK_RELEASE_SCRIPT;
+
+    // 库存扣减Lua脚本(优化后,增强健壮性)
+    static {
+        // 初始化库存扣减脚本
+        STOCK_DEDUCT_SCRIPT = new DefaultRedisScript<>();
+        STOCK_DEDUCT_SCRIPT.setScriptText("if redis.call('exists', KEYS[1]) ~= 1 then " + "return -2; " + "end " + "local stock_str = redis.call('get', KEYS[1]); " + "local stock = tonumber(stock_str); " + "if stock == nil then " + "return -3; " + "end " + "local deductNum_str = ARGV[1]; " + "local deductNum = tonumber(deductNum_str); " + "if deductNum == nil or deductNum <= 0 then " + "return -4; " + "end " + "if stock >= deductNum then " + "return redis.call('decrby', KEYS[1], deductNum); " + "else " + "return -1; " + "end");
+        STOCK_DEDUCT_SCRIPT.setResultType(Long.class);
+
+        // 锁释放脚本保持不变
+        LOCK_RELEASE_SCRIPT = new DefaultRedisScript<>();
+        LOCK_RELEASE_SCRIPT.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then " + "return redis.call('del', KEYS[1]) " + "else return 0 end");
+        LOCK_RELEASE_SCRIPT.setResultType(Long.class);
+    }
+
+    /**
+     * 初始化商品库存(Redis)
+     *
+     * @param productId 商品ID
+     * @param initStock 初始库存
+     */
+    public void initStock(Long productId, Long liveId, Integer initStock) {
+        String stockKey = RedisConstant.STOCK_KEY_PREFIX + liveId + ":" + productId;
+        redisTemplate.opsForValue().set(stockKey, initStock, 24 * 60 * 60, TimeUnit.SECONDS);
+        log.info("商品" + productId + "库存初始化完成,初始库存:" + initStock);
+    }
+
+    /**
+     * 高并发库存扣减(核心方法,落地Java 8特性)
+     *
+     * @param productId 商品ID
+     * @param deductNum 扣减数量(默认1)
+     * @return 扣减结果:true=成功,false=失败
+     */
+    public CompletableFuture<Boolean> deductStockAsync(Long productId, Long liveId, Integer deductNum, Long userId) {
+        // Java 8 CompletableFuture 异步处理,提升高并发吞吐量
+        return CompletableFuture.supplyAsync(() -> {
+            // 1. 参数校验(Java 8 Optional 空值处理)
+            Integer num = Optional.ofNullable(deductNum).orElse(1);
+            String stockKey = RedisConstant.STOCK_KEY_PREFIX + liveId + ":" + productId;
+            String lockKey = RedisConstant.LOCK_KEY_PREFIX + liveId + ":" + productId;
+
+            // 3. 尝试获取分布式锁(非阻塞重试,Java 8 Stream API 实现重试)
+// 3. 尝试获取分布式锁(优化:加入随机延迟,避免惊群效应)
+            boolean isLockAcquired = IntStream.range(0, RedisConstant.LOCK_MAX_RETRY).anyMatch(retryCount -> {
+                Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, userId, RedisConstant.LOCK_EXPIRE_SECONDS, TimeUnit.SECONDS);
+                if (Boolean.TRUE.equals(result)) {
+                    return true;
+                }
+                try {
+                    // 随机延迟:50ms~150ms,避免所有请求同时重试
+                    long randomDelay = RedisConstant.LOCK_RETRY_INTERVAL + new Random().nextInt(100);
+                    TimeUnit.MILLISECONDS.sleep(randomDelay);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    return false;
+                }
+                return false;
+            });
+            // 4. 未获取到锁,直接返回失败
+            if (!isLockAcquired) {
+                System.err.println("商品" + productId + "获取锁失败,高并发限流中");
+                return false;
+            }
+
+            try {
+                // 5. 执行库存扣减Lua脚本(原子操作,防超卖)
+                // 新增日志:打印当前库存值和扣减数量
+                Integer currentStockStr = (Integer) redisTemplate.opsForValue().get(stockKey);
+                log.info("拿到锁成功 → 库存Key:{},当前库存值:{},扣减数量:{}", stockKey, currentStockStr, num);
+
+                // 执行库存扣减Lua脚本
+                Long remainingStock = redisTemplate.execute(
+                        STOCK_DEDUCT_SCRIPT,
+                        Collections.singletonList(stockKey),
+                        1
+                );
+
+                // 新增日志:打印Lua返回结果
+                log.info("Lua脚本返回值:{}", remainingStock);
+
+                // 6. 判断扣减结果
+                if (remainingStock >= 0) {
+                    log.info("商品{}库存扣减成功,剩余库存:{}", productId, remainingStock);
+                    return true;
+                } else {
+                    String errorMsg = "";
+                    switch (remainingStock.intValue()) {
+                        case -1: errorMsg = "库存不足"; break;
+                        case -2: errorMsg = "库存Key不存在"; break;
+                        case -3: errorMsg = "库存值非数字"; break;
+                        case -4: errorMsg = "扣减数量无效"; break;
+                        default: errorMsg = "未知错误,错误码:" + remainingStock;
+                    }
+                    log.info("商品{}扣减失败:{}", productId, errorMsg);
+                    return false;
+                }
+            } finally {
+                // 7. 释放分布式锁(Lua脚本保证原子性,仅释放自己持有的锁)
+                redisTemplate.execute(LOCK_RELEASE_SCRIPT, Collections.singletonList(lockKey), userId);
+                log.info("商品{}锁释放成功,持有者:{}", productId, userId);
+            }
+        });
+    }
+}

+ 2 - 2
fs-live-socket/src/main/java/com/fs/live/task/Task.java

@@ -208,8 +208,8 @@ public class Task {
             liveService.asyncToCache();
         }
     }
-    @Scheduled(cron = "0/1 * * * * ?")
-    @DistributeLock(key = "liveLotteryTask", scene = "task")
+//    @Scheduled(cron = "0/1 * * * * ?")
+//    @DistributeLock(key = "liveLotteryTask", scene = "task")
     public void liveLotteryTask() {
         long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
         String lotteryKey = "live:lottery_task:*";

+ 2 - 1
fs-live-socket/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -88,7 +88,7 @@ public class WebSocketServer {
         if (live == null) {
             throw new BaseException("未找到直播间");
         }
-        long companyId = live.getCompanyId() == null ? -1L : live.getCompanyId();
+        long companyId = -1L;
         long companyUserId = -1L;
         if (!Objects.isNull(userProperties.get("companyId"))) {
             companyId = (long) userProperties.get("companyId");
@@ -184,6 +184,7 @@ public class WebSocketServer {
                 liveUserFirstEntry.setUpdateTime( date);
                 liveUserFirstEntryService.insertLiveUserFirstEntry(liveUserFirstEntry);
             }
+//            redisCache.setCacheObject( "live:user:first:entry:" + liveId + ":" + userId, liveUserFirstEntry, 4, TimeUnit.HOURS);
 
 
         } else {

+ 1 - 1
fs-live-socket/src/main/resources/application.yml

@@ -58,7 +58,7 @@ server:
 # 日志配置
 logging:
   level:
-    com.fs: info
+#    com.fs: info
     org.springframework: warn
     org.springframework.web: info
 

+ 6 - 0
fs-service-system/pom.xml

@@ -191,5 +191,11 @@
             <artifactId>cos-sts_api</artifactId>
             <version>3.1.1</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.retry</groupId>
+            <artifactId>spring-retry</artifactId>
+            <version>1.3.1</version>
+        </dependency>
     </dependencies>
 </project>

+ 9 - 30
fs-service-system/src/main/java/com/fs/erp/service/impl/ErpOrderServiceProxy.java

@@ -26,7 +26,7 @@ import java.util.Map;
 @Service
 @Primary
 public class ErpOrderServiceProxy implements IErpOrderService {
-    private final RateLimiter rateLimiter = RateLimiter.create(5.0);
+    private final RateLimiter rateLimiter = RateLimiter.create(1.0);
 
     /**
      * 存储所有实现的ERP订单服务
@@ -76,41 +76,20 @@ public class ErpOrderServiceProxy implements IErpOrderService {
     @Override
     public ErpOrderQueryResponse getOrder(ErpOrderQueryRequert param) {
         if(StringUtils.equals(ErpContextHolder.getErpType(), ErpTypeConstant.JST_ERP)) {
-            // 最大重试次数
-            int maxRetries = 3;
-            // 当前重试次数
-            int retryCount = 0;
-
-            while (retryCount < maxRetries) {
-                if (rateLimiter.tryAcquire()) {
-                    // 获取到令牌,直接执行请求
-                    return getCurrentErpService().getOrder(param);
-                }
-
-                // 重试计数增加
-                retryCount++;
-
-                if (retryCount < maxRetries) {
-                    try {
-                        // 每次重试间隔时间递增:500ms, 1000ms, 1500ms...
-                        long waitTime = 1000 * retryCount;
-                        Thread.sleep(waitTime);
-                        log.info("ERP请求被限流,等待{}ms后第{}次重试", waitTime, retryCount);
-                    } catch (InterruptedException e) {
-                        Thread.currentThread().interrupt();
-                        break;
-                    }
-                }
-            }
-
-            // 达到最大重试次数后仍无法获取令牌,才抛出异常
-            throw new RuntimeException("请求过于频繁,请稍后再试");
+            // 阻塞等待直到获取到令牌
+            rateLimiter.acquire();
+            log.info("获取到令牌,执行ERP请求");
         }
         return getCurrentErpService().getOrder(param);
     }
 
     @Override
     public ErpOrderQueryResponse getOrderLive(ErpOrderQueryRequert param) {
+        if(StringUtils.equals(ErpContextHolder.getErpType(), ErpTypeConstant.JST_ERP)) {
+            // 阻塞等待直到获取到令牌
+            rateLimiter.acquire();
+            log.info("获取到令牌,执行ERP请求");
+        }
         return getCurrentErpService().getOrderLive(param);
     }
 

+ 1 - 0
fs-service-system/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -209,6 +209,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    
     @Override
     public ErpOrderQueryResponse getOrder(ErpOrderQueryRequert param) {
         // 1. 构建查询请求DTO

+ 4 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveCompletionPointsRecordMapper.java

@@ -1,5 +1,7 @@
 package com.fs.live.mapper;
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveCompletionPointsRecord;
 import org.apache.ibatis.annotations.Param;
 
@@ -36,6 +38,7 @@ public interface LiveCompletionPointsRecordMapper {
     /**
      * 查询用户在某直播间最近一次完课记录(不限制日期)
      */
+    @DataSource(DataSourceType.SLAVE)
     LiveCompletionPointsRecord selectLatestByUserAndLiveId(@Param("liveId") Long liveId, 
                                                             @Param("userId") Long userId);
 
@@ -48,6 +51,7 @@ public interface LiveCompletionPointsRecordMapper {
     /**
      * 查询用户的完课积分领取记录列表
      */
+    @DataSource(DataSourceType.SLAVE)
     List<LiveCompletionPointsRecord> selectRecordsByUser(@Param("liveId") Long liveId, 
                                                           @Param("userId") Long userId);
 

+ 5 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveCouponUserMapper.java

@@ -1,6 +1,9 @@
 package com.fs.live.mapper;
 
 import java.util.List;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveCouponUser;
 import com.fs.live.param.CouponPO;
 import org.apache.ibatis.annotations.Param;
@@ -20,6 +23,7 @@ public interface LiveCouponUserMapper
      * @param id 优惠券发放记录ID
      * @return 优惠券发放记录
      */
+    @DataSource(DataSourceType.SLAVE)
     public LiveCouponUser selectLiveCouponUserById(Long id);
 
     /**
@@ -71,5 +75,6 @@ public interface LiveCouponUserMapper
             " and lcu.goods_id= #{coupon.goodsId}" +
             " </if>" +
             "</script>")
+    @DataSource(DataSourceType.SLAVE)
     List<LiveCouponUser> curCoupon(@Param("coupon") CouponPO coupon);
 }

+ 6 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveDataMapper.java

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveData;
 import com.fs.live.vo.*;
 import org.apache.ibatis.annotations.Param;
@@ -130,6 +132,7 @@ public interface LiveDataMapper {
      * @param liveId 直播间ID
      * @return 详情数据
      */
+    @DataSource(DataSourceType.SLAVE)
     LiveDataDetailVo selectLiveDataDetailBySql(@Param("liveId") Long liveId);
 
     /**
@@ -137,6 +140,7 @@ public interface LiveDataMapper {
      * @param liveId 直播间ID
      * @return 用户详情列表
      */
+    @DataSource(DataSourceType.SLAVE)
     List<LiveUserDetailVo> selectLiveUserDetailListBySql(@Param("liveId") Long liveId,@Param("companyId") Long companyId,@Param("companyUserId") Long companyUserId);
 
     /**
@@ -144,6 +148,7 @@ public interface LiveDataMapper {
      * @param liveIds 直播间ID列表
      * @return 统计数据
      */
+    @DataSource(DataSourceType.SLAVE)
     LiveDataStatisticsVo selectLiveDataStatistics(@Param("liveIds") List<Long> liveIds);
 
     /**
@@ -151,6 +156,7 @@ public interface LiveDataMapper {
      * @param liveIds 直播间ID列表
      * @return 列表数据
      */
+    @DataSource(DataSourceType.SLAVE)
     List<LiveDataListVo> selectLiveDataListByLiveIds(@Param("liveIds") List<Long> liveIds);
 
 }

+ 17 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveGoodsMapper.java

@@ -1,5 +1,7 @@
 package com.fs.live.mapper;
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveGoods;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.vo.LiveGoodsListVo;
@@ -90,6 +92,7 @@ public interface LiveGoodsMapper {
      * @param liveGoods 直播商品
      * @return 商品信息集合
      */
+    @DataSource(DataSourceType.SLAVE)
     List<LiveGoodsVo> selectProductListByLiveId(LiveGoods liveGoods);
 
     /**
@@ -112,6 +115,7 @@ public interface LiveGoodsMapper {
     List<LiveGoodsVo> selectProductListByOrder(LiveOrder liveOrder);
 
     @Select("select * from live_goods where live_id = #{liveId} and product_id = #{productId}")
+    @DataSource(DataSourceType.SLAVE)
     LiveGoods selectLiveGoodsByProductId(@Param("liveId") Long liveId,@Param("productId") Long productId);
 
     /**
@@ -129,6 +133,7 @@ public interface LiveGoodsMapper {
 
     int handleDeleteSelectedAdmin(@Param("listVo") LiveGoodsListVo listVo);
 
+    @DataSource(DataSourceType.SLAVE)
     List<LiveGoodsVo> selectProductListByLiveIdAll(LiveGoods liveGoods);
 
     @Select("select count(1) from live_goods where is_show = true and live_id = #{liveId}")
@@ -141,6 +146,7 @@ public interface LiveGoodsMapper {
     LiveGoodsVo showGoods(@Param("liveId") Long liveId);
 
     @Select("select * from live_goods where live_id = #{liveId}")
+    @DataSource(DataSourceType.SLAVE)
     List<LiveGoods> selectLiveGoodsByLiveId(@Param("liveId") Long liveId);
 
     @Update({"<script>" +
@@ -156,4 +162,15 @@ public interface LiveGoodsMapper {
      */
     @Update("update live_goods set status = #{status} where goods_id = #{goodsId}")
     void updateLiveGoodsStatus(@Param("goodsId") Long goodsId, @Param("status") Integer status);
+
+    @Select("select * from live_goods")
+    List<LiveGoods> listAll();
+
+    /**
+     * 批量根据ID更新直播商品
+     *
+     * @param liveGoodsList 直播商品列表
+     * @return 结果
+     */
+    int updateBatchById(@Param("list") List<LiveGoods> liveGoodsList);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveMapper.java

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.Live;
 import com.fs.live.param.LiveDataParam;
 import com.fs.live.vo.LiveListVo;
@@ -26,6 +28,7 @@ public interface LiveMapper
      * @param liveId 直播主键
      * @return 直播
      */
+    @DataSource(DataSourceType.SLAVE)
     public Live selectLiveByLiveId(Long liveId);
 
     /**

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

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.vo.LiveOrderListVo;
 import com.fs.live.vo.LiveOrderQueryVO;
@@ -39,6 +41,7 @@ public interface LiveOrderMapper {
      * @param liveOrder 订单
      * @return 订单集合
      */
+    @DataSource(DataSourceType.SLAVE)
     List<LiveOrder> selectLiveOrderList(LiveOrderQueryVO liveOrder);
 
     /**
@@ -98,12 +101,14 @@ public interface LiveOrderMapper {
             "</where> " +
             "order by create_time desc" +
             "</script>"})
+    @DataSource(DataSourceType.SLAVE)
     List<LiveOrderListVo> selectLiveOrderListVo(@Param("userId") String userId,@Param("status") Integer status);
 
     @Select("select * from live_order where `status` = 3 AND TIMESTAMPDIFF(HOUR, start_time, NOW()) >= 48  ")
     List<LiveOrder> selectLiveOrderByFinish();
 
     @Select("select * from live_order where `status` = 2 and extend_order_id is not null ")
+    @DataSource(DataSourceType.SLAVE)
     List<LiveOrder> selectUpdateExpress();
 
     @Select("select order_id from live_order where `status` = 2")

+ 3 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveOrderPaymentMapper.java

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveOrderPayment;
 import com.fs.live.vo.LiveOrderPaymentVo;
 import org.apache.ibatis.annotations.MapKey;
@@ -24,6 +26,7 @@ public interface LiveOrderPaymentMapper {
      * @param paymentId 支付明细主键
      * @return 支付明细
      */
+    @DataSource(DataSourceType.SLAVE)
     LiveOrderPayment selectLiveOrderPaymentByPaymentId(Long paymentId);
 
     /**

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

@@ -83,4 +83,6 @@ public interface LiveTrafficLogMapper
     List<LiveTrafficLogListVO> selectTrafficByCompany(@Param("maps") LiveTrafficLogParam param);
 
     void insertOrUpdateLiveTrafficLog(LiveTrafficLog trafficLog);
+
+    void batchInsert(@Param("list") List<LiveTrafficLog> list);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveVideoMapper.java

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveVideo;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -83,6 +85,7 @@ public interface LiveVideoMapper
     List<LiveVideo> selectByLiveId(@Param("liveId")Long liveId);
 
     @Select("select * from live_video where live_id = #{liveId} and video_type = #{videoType}")
+    @DataSource(DataSourceType.SLAVE)
     List<LiveVideo> selectByLiveIdAndType(@Param("liveId")Long liveId,@Param("videoType") Integer videoType);
 
     @Select("select * from live_video where live_id = #{liveId}")

+ 4 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveWatchUserMapper.java

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveWatchUser;
 import com.fs.live.vo.LiveWatchUserStatistics;
 import com.fs.live.vo.LiveWatchUserVO;
@@ -81,6 +83,7 @@ public interface LiveWatchUserMapper {
      * @param params 参数
      * @return list
      */
+    @DataSource(DataSourceType.SLAVE)
     List<LiveWatchUserVO> selectWatchUserListByLiveId(@Param("params") Map<String, Object> params);
 
     /**
@@ -100,6 +103,7 @@ public interface LiveWatchUserMapper {
     @Select("select a.*,fu.nickname as nick_name from (select lws.* from live_watch_user lws where live_id=#{liveId} and online = 0 and " +
             "user_id in (select user_id from live_lottery_registration where live_id = #{liveId} and lottery_id=#{lotteryId} and registration_id >= " +
             "(SELECT FLOOR(RAND() * (SELECT MAX(registration_id) FROM live_lottery_registration)))) ) a left join fs_user fu on fu.user_id = a.user_id")
+    @DataSource(DataSourceType.SLAVE)
     List<LiveWatchUser> selectLiveWatchAndRegisterUser(@Param("liveId") Long liveId,@Param("lotteryId") Long lotteryId);
 
     @Select("select * from live_watch_user where live_id = #{liveId} and user_id = #{userId}")

+ 4 - 0
fs-service-system/src/main/java/com/fs/live/service/ILiveGoodsService.java

@@ -126,4 +126,8 @@ public interface ILiveGoodsService {
     R getStoreByLiveId(Long liveId, String key, String userId);
 
     void updateLiveGoodsStatus(Long goodsId, Integer status);
+
+    List<LiveGoods> listAll();
+
+    void updateBatchById(List<LiveGoods> logsToInsert);
 }

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

@@ -212,4 +212,6 @@ public interface ILiveOrderService {
     R createRewardLiveOrder(LiveOrder liveOrder);
 
     R payConfirmReward(LiveOrder liveOrder);
+
+    void initStock();
 }

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

@@ -204,4 +204,6 @@ public interface ILiveService
      * 获取直播间数量
      */
     Integer getLiveCountByMap(Map<String, Object> params);
+
+    R clearLiveCache(Long liveId);
 }

+ 1 - 1
fs-service-system/src/main/java/com/fs/live/service/impl/LiveCompletionPointsRecordServiceImpl.java

@@ -344,7 +344,7 @@ public class LiveCompletionPointsRecordServiceImpl implements ILiveCompletionPoi
             }
 
             // 8. 计算积分
-            int points = calculatePoints(continuousDays);
+            int points = Math.toIntExact(config.getScoreAmount());
 
             // 9. 创建完课记录
             LiveCompletionPointsRecord record = new LiveCompletionPointsRecord();

+ 20 - 4
fs-service-system/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.live.service.impl;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.core.redis.service.StockDeductService;
 import com.fs.common.utils.DateUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.live.domain.LiveGoods;
@@ -22,10 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -46,6 +44,10 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
     @Autowired
     private ILiveAutoTaskService liveAutoTaskService;
 
+    @Autowired
+    private StockDeductService stockDeductService;
+
+
     /**
      * 查询直播商品
      *
@@ -101,6 +103,7 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
             if(fsStoreProduct == null) return R.error("商品不存在");
             if(fsStoreProduct.getIsShow() == 0 || existGoods.getStatus() == 0) return R.error("商品已下架");
             if(fsStoreProduct.getStock() < liveGoods.getStock()) return R.error("商品库存不足");
+            stockDeductService.initStock(existGoods.getProductId(), existGoods.getLiveId(), liveGoods.getStock().intValue());
         }
         baseMapper.updateLiveGoods(liveGoods);
         return R.ok();
@@ -232,6 +235,16 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         baseMapper.updateLiveGoodsStatus(goodsId, status);
     }
 
+    @Override
+    public List<LiveGoods> listAll() {
+        return baseMapper.listAll();
+    }
+
+    @Override
+    public void updateBatchById(List<LiveGoods> logsToInsert) {
+        baseMapper.updateBatchById(logsToInsert);
+    }
+
     /**
      * 批量新增直播商品
      *
@@ -314,6 +327,9 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
                 .collect(Collectors.toList());
 //
 //        // 批量插入
+        liveGoodsList.forEach(e -> {
+            stockDeductService.initStock(e.getProductId(), liveId, e.getStock().intValue());
+        });
         return baseMapper.insertLiveGoodsList(liveGoodsList);
     }
 

Разница между файлами не показана из-за своего большого размера
+ 258 - 244
fs-service-system/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java


+ 10 - 2
fs-service-system/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -1283,11 +1283,19 @@ public class LiveServiceImpl implements ILiveService
      * 清除直播间数据缓存
      * @param liveId 直播间ID
      */
-    private void clearLiveCache(Long liveId) {
-        if (liveId != null) {
+    @Override
+    public R clearLiveCache(Long liveId) {
+        if (liveId == null) {
+            return R.error("直播间ID不能为空");
+        }
+        try {
             String cacheKey = String.format(LiveKeysConstant.LIVE_DATA_CACHE, liveId);
             redisCache.deleteObject(cacheKey);
             log.debug("清除直播间缓存: liveId={}", liveId);
+            return R.ok("缓存清理成功");
+        } catch (Exception e) {
+            log.error("清除直播间缓存失败: liveId={}", liveId, e);
+            return R.error("缓存清理失败: " + e.getMessage());
         }
     }
 

+ 7 - 2
fs-service-system/src/main/java/com/fs/live/service/impl/LiveTrafficLogServiceImpl.java

@@ -13,6 +13,7 @@ import com.fs.live.domain.LiveVideo;
 import com.fs.live.mapper.LiveVideoMapper;
 import com.fs.live.param.LiveFinishUParam;
 import com.fs.live.param.LiveTrafficLogParam;
+import com.fs.live.utils.redis.RedisBatchHandler;
 import com.fs.live.vo.LiveTrafficLogListVO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
@@ -37,6 +38,8 @@ public class LiveTrafficLogServiceImpl implements ILiveTrafficLogService
     private LiveTrafficLogMapper liveTrafficLogMapper;
     @Autowired
     private LiveVideoMapper liveVideoMapper;
+    @Autowired
+    public RedisBatchHandler redisBatchHandler;
 
     /**
      * 查询直播流量记录
@@ -140,7 +143,8 @@ public class LiveTrafficLogServiceImpl implements ILiveTrafficLogService
             if (StringUtils.isNotEmpty(trafficLog.getUuId())) {
                 // 直接插入或更新
 //                logger.error("zyp \n【插入或更新流量】:{}",trafficLog);
-                liveTrafficLogMapper.insertOrUpdateLiveTrafficLog(trafficLog);
+                redisBatchHandler.addDataToRedis(trafficLog);
+//                liveTrafficLogMapper.insertOrUpdateLiveTrafficLog(trafficLog);
             }
         } catch (Exception e) {
             e.printStackTrace();
@@ -165,7 +169,8 @@ public class LiveTrafficLogServiceImpl implements ILiveTrafficLogService
             if (StringUtils.isNotEmpty(trafficLog.getUuId())) {
                 // 直接插入或更新
 //                logger.error("zyp \n【插入或更新流量】:{}",trafficLog);
-                liveTrafficLogMapper.insertOrUpdateLiveTrafficLog(trafficLog);
+                redisBatchHandler.addDataToRedis(trafficLog);
+//                liveTrafficLogMapper.insertOrUpdateLiveTrafficLog(trafficLog);
             }
         } catch (Exception e) {
             e.printStackTrace();

+ 2 - 2
fs-service-system/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java

@@ -234,7 +234,7 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
         liveWatchUser.setAvatar(fsUser.getAvatar());
         liveWatchUser.setNickName(fsUser.getNickname());
         String hashKey = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);
-        redisCache.hashPut(hashKey, String.valueOf(userId), JSON.toJSONString(liveWatchUser));
+//        redisCache.hashPut(hashKey, String.valueOf(userId), JSON.toJSONString(liveWatchUser));
         return liveWatchUser;
     }
     @Override
@@ -354,7 +354,7 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
                                 watchUserVO -> String.valueOf(watchUserVO.getUserId()),
                                 JSON::toJSONString
                         ));
-                redisUtil.hashPut(hashKey,collect);
+//                redisUtil.hashPut(hashKey,collect);
             });
         }
         return liveWatchUserVOS;

+ 63 - 0
fs-service-system/src/main/java/com/fs/live/utils/redis/RedisBatchHandler.java

@@ -0,0 +1,63 @@
+package com.fs.live.utils.redis;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.live.domain.LiveTrafficLog;
+import com.fs.live.mapper.LiveTrafficLogMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component
+public class RedisBatchHandler {
+    @Autowired
+    public RedisTemplate redisTemplate;
+    @Autowired
+    public LiveTrafficLogMapper liveTrafficLogMapper;
+    // Redis队列key
+    private static final String BATCH_QUEUE_KEY = "live_traffic:batch:queue";
+    // 批量拉取条数
+    private static final int BATCH_PULL_SIZE = 500;
+    // 定时消费间隔
+    public static final long CONSUME_INTERVAL = 500;
+
+    // 1. 入队:多服务实例写入Redis List
+    public void addDataToRedis(LiveTrafficLog data) {
+        try {
+            // 序列化数据(用JSON)
+            String json = JSON.toJSONString(data);
+            redisTemplate.opsForList().rightPush(BATCH_QUEUE_KEY, json);
+        } catch (Exception e) {
+            // 降级为本地队列/单条插入
+            log.error("Redis入队失败,降级处理", e);
+        }
+    }
+
+    // 2. 定时消费:独立线程拉取批量数据写入MySQL
+    public void consumeBatchData() {
+        // 批量拉取Redis List中的数据(非阻塞)
+        List<String> jsonList = redisTemplate.opsForList().range(BATCH_QUEUE_KEY, 0, BATCH_PULL_SIZE - 1);
+        if (jsonList.isEmpty()) {
+            return;
+        }
+        // 转换为实体类
+        List<LiveTrafficLog> batchList = jsonList.stream()
+                .map(json -> JSON.parseObject(json, LiveTrafficLog.class))
+                .collect(Collectors.toList());
+        try {
+            // 批量写入数据库
+            liveTrafficLogMapper.batchInsert(batchList);
+            // 删除已消费的数据
+            redisTemplate.opsForList().trim(BATCH_QUEUE_KEY, batchList.size(), -1);
+        } catch (Exception e) {
+            // 消费失败:记录日志,不删除Redis数据(重试)
+            log.error("Redis批量消费失败,将重试", e);
+        }
+    }
+}

+ 1 - 1
fs-service-system/src/main/java/com/fs/store/config/StoreConfig.java

@@ -21,5 +21,5 @@ public class StoreConfig implements Serializable {
     private String videoUrl;
     private String refundPhoneNumber;
     private String refundAddress;
-
+    private Boolean checkStock;//是否检查库存,默认关闭
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsShippingTemplatesMapper.java

@@ -1,6 +1,9 @@
 package com.fs.store.mapper;
 
 import java.util.List;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.store.domain.FsShippingTemplates;
 import org.apache.ibatis.annotations.Select;
 
@@ -60,6 +63,7 @@ public interface FsShippingTemplatesMapper
      */
     public int deleteFsShippingTemplatesByIds(Long[] id);
     @Select("select * from fs_shipping_templates where find_in_set(id,#{ids})")
+    @DataSource(DataSourceType.SLAVE)
     List<FsShippingTemplates> selectFsShippingTemplatesByIds(String ids);
 
     /**

+ 5 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsShippingTemplatesRegionMapper.java

@@ -1,6 +1,9 @@
 package com.fs.store.mapper;
 
 import java.util.List;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.store.domain.FsShippingTemplatesRegion;
 import org.apache.ibatis.annotations.Delete;
 import org.apache.ibatis.annotations.Param;
@@ -65,9 +68,11 @@ public interface FsShippingTemplatesRegionMapper
     @Delete("delete from fs_shipping_templates_region where temp_id=#{tempId}")
     int deleteFsShippingTemplatesRegionByTempId(Long tempId);
     @Select("select * from fs_shipping_templates_region where find_in_set(temp_id,#{tempIds}) and find_in_set(city_id,#{cityIds})")
+    @DataSource(DataSourceType.SLAVE)
     List<FsShippingTemplatesRegion> selectFsShippingTemplatesRegionListByTempIdsAndCityIds(@Param("tempIds") String tempIds,@Param("cityIds") String cityIds);
 
     @Select("select * from fs_shipping_templates_region where temp_id=${templateId}")
+    @DataSource(DataSourceType.SLAVE)
     List<FsShippingTemplatesRegion> selectTempRegionByTempIdAndCityId(@Param("templateId") Long templateId);
 
     void batchSaveRegions(@Param("list") List<FsShippingTemplatesRegion> fsShippingTemplatesRegions);

+ 4 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsStoreCouponUserMapper.java

@@ -1,6 +1,9 @@
 package com.fs.store.mapper;
 
 import java.util.List;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.store.domain.FsStoreCouponUser;
 import com.fs.store.param.FsCouponUserEnableParam;
 import com.fs.store.vo.FsStoreCouponUserVO;
@@ -22,6 +25,7 @@ public interface FsStoreCouponUserMapper
      * @param id 优惠券发放记录ID
      * @return 优惠券发放记录
      */
+    @DataSource(DataSourceType.SLAVE)
     public FsStoreCouponUser selectFsStoreCouponUserById(Long id);
 
     /**

+ 5 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsStoreProductAttrValueMapper.java

@@ -1,6 +1,9 @@
 package com.fs.store.mapper;
 
 import java.util.List;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.store.domain.FsStoreProductAttrValue;
 import com.fs.store.param.FsProductAttrValueParam;
 import com.fs.store.param.FsStoreProductAttrValueQueryParam;
@@ -27,6 +30,7 @@ public interface FsStoreProductAttrValueMapper
      * @param id 商品属性值ID
      * @return 商品属性值
      */
+    @DataSource(DataSourceType.SLAVE)
         public FsStoreProductAttrValue selectFsStoreProductAttrValueById(Long id);
 
     /**
@@ -71,6 +75,7 @@ public interface FsStoreProductAttrValueMapper
     @Delete("delete from fs_store_product_attr_value where product_id=#{productId}")
     int deleteFsStoreProductAttrValueByProductId(Long productId);
     @Select("select * from fs_store_product_attr_value where  product_id=#{productId}")
+    @DataSource(DataSourceType.SLAVE)
     List<FsStoreProductAttrValue> selectFsStoreProductAttrValueByProductId(Long productId);
     @Select("select ifnull(stock,0) from fs_store_product_attr_value where  id=#{productAttrValueId}")
     int selectFsStoreProductStockById(Long productAttrValueId);

+ 3 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsStoreProductMapper.java

@@ -3,6 +3,8 @@ package com.fs.store.mapper;
 import java.util.List;
 import java.util.Map;
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.live.domain.LiveGoods;
 import com.fs.store.domain.FsStoreProduct;
 import com.fs.store.param.FsStoreProductQueryParam;
@@ -23,6 +25,7 @@ public interface FsStoreProductMapper
      * @param productId 商品ID
      * @return 商品
      */
+    @DataSource(DataSourceType.SLAVE)
     public FsStoreProduct selectFsStoreProductById(Long productId);
 
     /**

+ 4 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsUserAddressMapper.java

@@ -1,6 +1,9 @@
 package com.fs.store.mapper;
 
 import java.util.List;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.store.domain.FsUserAddress;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -61,6 +64,7 @@ public interface FsUserAddressMapper
      */
     public int deleteFsUserAddressByIds(Long[] ids);
     @Select("select * from fs_user_address where user_id=#{uid} and is_default=1 and is_del=0 limit 1")
+    @DataSource(DataSourceType.SLAVE)
     FsUserAddress selectFsUserAddressByDefaultAddress(long uid);
     @Update("update fs_user_address set is_default=0 where user_id=#{userId}")
     int clearIsDefalut(long userId);

+ 4 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java

@@ -2,6 +2,8 @@ package com.fs.store.mapper;
 
 import java.util.List;
 
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
 import com.fs.store.domain.FsUser;
 import com.fs.store.vo.FsUserListVO;
 import com.fs.store.vo.FsUserVO;
@@ -27,6 +29,7 @@ public interface FsUserMapper
      * @param userId 用户ID
      * @return 用户
      */
+    @DataSource(DataSourceType.SLAVE)
     public FsUser selectFsUserById(Long userId);
 
     /**
@@ -123,6 +126,7 @@ public interface FsUserMapper
             ") b ON u.user_id=b.user_id " +
             " LEFT JOIN fs_store_payment p ON u.user_id=p.user_id AND b.last_buy_time=p.pay_time AND b.payment_id=p.payment_id"+
             " WHERE u.user_id=#{userId}")
+    @DataSource(DataSourceType.SLAVE)
     FsUserVO selectFsUserByUserId(Long userId);
 
 

+ 1 - 0
fs-service-system/src/main/java/com/fs/store/param/LiveOrderComputedParam.java

@@ -17,6 +17,7 @@ public class LiveOrderComputedParam implements Serializable
     private Long productId;
     private String totalNum;
     private Long cityId;
+    private Long attrValueId;
 
 
     @ApiModelProperty(value = "使用积分 1使用")

+ 11 - 1
fs-service-system/src/main/java/com/fs/store/service/impl/FsCityServiceImpl.java

@@ -1,6 +1,8 @@
 package com.fs.store.service.impl;
 
 import java.util.List;
+
+import com.fs.common.core.redis.RedisCache;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
@@ -19,6 +21,8 @@ public class FsCityServiceImpl implements IFsCityService
 {
     @Autowired
     private FsCityMapper fsCityMapper;
+    @Autowired
+    private RedisCache redisCache;
 
     /**
      * 查询城市
@@ -94,6 +98,12 @@ public class FsCityServiceImpl implements IFsCityService
 
     @Override
     public List<FsCity> selectFsCitys() {
-        return fsCityMapper.selectFsCitys();
+        List<FsCity> redisList = redisCache.getCacheObject("city");
+        if(redisList != null){
+            return redisList;
+        }
+        List<FsCity> list = fsCityMapper.selectFsCitys();
+        redisCache.setCacheObject("city", list);
+        return list;
     }
 }

+ 11 - 0
fs-service-system/src/main/resources/application-config.yml

@@ -1,4 +1,15 @@
 #配置
+server:
+  servlet:
+    # 应用的访问路径
+    context-path: /
+  tomcat:
+    # tomcat的URI编码
+    uri-encoding: UTF-8
+    # tomcat最大线程数,默认为200
+    max-threads: 800
+    # Tomcat启动初始化的线程数,默认值25
+    min-spare-threads: 30
 fsConfig:
   omsCode: "CQRXSF.0235487868_241231"
   #快递鸟

+ 24 - 0
fs-service-system/src/main/resources/mapper/live/LiveGoodsMapper.xml

@@ -300,4 +300,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </delete>
 
+    <update id="updateBatchById" parameterType="java.util.List">
+        <foreach collection="list" item="item" open="" close="" separator=";">
+            update live_goods
+            <trim prefix="SET" suffixOverrides=",">
+                <if test="item.liveId != null">live_id = #{item.liveId},</if>
+                <if test="item.companyId != null">company_id = #{item.companyId},</if>
+                <if test="item.companyUserId != null">company_user_id = #{item.companyUserId},</if>
+                <if test="item.storeId != null">store_id = #{item.storeId},</if>
+                <if test="item.productId != null">product_id = #{item.productId},</if>
+                <if test="item.createTime != null">create_time = #{item.createTime},</if>
+                <if test="item.createBy != null">create_by = #{item.createBy},</if>
+                <if test="item.updateBy != null">update_by = #{item.updateBy},</if>
+                <if test="item.updateTime != null">update_time = #{item.updateTime},</if>
+                <if test="item.remark != null">remark = #{item.remark},</if>
+                <if test="item.status != null">status = #{item.status},</if>
+                <if test="item.stock != null">stock = #{item.stock},</if>
+                <if test="item.sort != null">sort = #{item.sort},</if>
+                <if test="item.isShow != null">is_show = #{item.isShow},</if>
+                <if test="item.sales != null">sales = #{item.sales},</if>
+            </trim>
+            where goods_id = #{item.goodsId}
+        </foreach>
+    </update>
+
 </mapper>

+ 21 - 10
fs-service-system/src/main/resources/mapper/live/LiveTrafficLogMapper.xml

@@ -120,16 +120,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                      #{companyId},
                      #{videoId},
                      #{uuId}
-                 ) ON DUPLICATE KEY UPDATE
-                     user_id = VALUES(user_id),
-            live_id = VALUES(live_id),
-            create_time = VALUES(create_time),
-            qw_external_contact_id = VALUES(qw_external_contact_id),
-            internet_traffic = VALUES(internet_traffic),
-            qw_user_id = VALUES(qw_user_id),
-            company_user_id = VALUES(company_user_id),
-            company_id = VALUES(company_id),
-            video_id = VALUES(video_id)
+                 )
+        on duplicate key update
+        <trim suffixOverrides=",">
+            <if test="internetTraffic != null">internet_traffic = #{internetTraffic},</if>
+        </trim>
+    </insert>
+
+    <insert id="batchInsert" parameterType="java.util.List">
+        INSERT INTO live_traffic_log (
+        user_id, live_id, create_time, qw_external_contact_id,
+        internet_traffic, qw_user_id, company_user_id, company_id, video_id, uu_id
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.userId}, #{item.liveId}, #{item.createTime}, #{item.qwExternalContactId},
+            #{item.internetTraffic}, #{item.qwUserId}, #{item.companyUserId},
+            #{item.companyId}, #{item.videoId}, #{item.uuId}
+            )
+        </foreach>
+        ON DUPLICATE KEY UPDATE
+        internet_traffic = VALUES(internet_traffic)
     </insert>
 
 </mapper>

+ 3 - 0
fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -556,6 +556,9 @@ public class CommonController extends AppBaseController {
 		FsProjectAddressConfig config = redisCache.getCacheObject(redisKey);
 		if (Objects.isNull(config)) {
 			config = projectAddressConfigService.selectDomainByCode(projectCode);
+			if(config == null || config.getAddressUrl() == null){
+				return R.ok();
+			}
 			redisCache.setCacheObject(redisKey, config, 5, TimeUnit.MINUTES);
 		}
 		String addressUrl = "";

+ 10 - 0
fs-user-app/src/main/java/com/fs/app/controller/LiveDataController.java

@@ -17,6 +17,7 @@ import springfox.documentation.annotations.ApiIgnore;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 @RestController
 @RequestMapping("/app/live/liveData")
@@ -110,7 +111,16 @@ public class LiveDataController extends AppBaseController{
     public R getRecentLiveViewers(@PathVariable  Long liveId) {
         Map<String, Object> params = new HashMap<>();
         params.put("liveId", liveId);
+        String key = "live:" + liveId + ":getRecentLiveViewers";
+        try {
+            List<LiveWatchUserVO> object = redisCache.getCacheObject(key);
+            if(object != null){
+                return R.ok().put("recentLiveViewers", object);
+            }
+        }catch (Exception ignored){
+        }
         List<LiveWatchUserVO> recentLiveViewers = liveWatchUserService.selectWatchUserList(params);
+        redisCache.setCacheObject(key, recentLiveViewers);
         return R.ok().put("recentLiveViewers", recentLiveViewers);
     }
 

+ 0 - 1
fs-user-app/src/main/java/com/fs/app/controller/LiveOrderController.java

@@ -352,7 +352,6 @@ public class LiveOrderController extends AppBaseController
     @GetMapping(value = "/liveOrderUser/{liveId}")
     public R liveOrderUser(@PathVariable  String liveId)
     {
-        log.info("正在购买的用户 参数: {}",liveId);
         return liveOrderService.liveOrderUser(liveId);
     }
 

+ 13 - 0
fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateTime;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.OrderUtils;
+import com.fs.live.service.ILiveOrderService;
 import com.fs.store.param.LoginMpWxParam;
 import com.fs.common.utils.IpUtil;
 import com.fs.wx.miniapp.config.WxMaConfiguration;
@@ -48,6 +49,18 @@ public class WxUserController extends AppBaseController{
     @Autowired
     private IFsUserService userService;
 
+
+    @Autowired
+    private ILiveOrderService liveOrderServiceImpl;
+
+
+    @ApiOperation("初始化库存")
+    @GetMapping("/initStock")
+    public R initStock(Long id) {
+        liveOrderServiceImpl.initStock();
+        return R.ok();
+    }
+
     /**
      * 登陆接口
      */

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/facade/impl/LiveFacadeServiceImpl.java

@@ -114,7 +114,7 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
 
         List<LiveWatchUserVO> liveWatchUserVOS;
         String setKey = String.format(LiveKeysConstant.LIVE_WATCH_USERS, param.getLiveId());
-        Map<Object, Object> hashEntries = redisUtil.hashEntries(setKey);
+        Map<Object, Object> hashEntries = redisUtil.hashEntriesByScan(setKey,50);
         if (CollUtil.isEmpty(hashEntries)) {
             liveWatchUserVOS = liveWatchUserService.asyncToCache(param.getLiveId());
         } else {

+ 15 - 392
fs-user-app/src/main/java/com/fs/app/task/Task.java

@@ -1,392 +1,15 @@
-//package com.fs.app.task;
-//
-//import cn.hutool.core.collection.CollUtil;
-//import cn.hutool.json.JSONUtil;
-//import com.alibaba.fastjson.JSON;
-//import com.alibaba.fastjson.JSONObject;
-//import com.fs.app.facade.LiveFacadeService;
-//import com.fs.app.vo.LotteryVo;
-//import com.fs.app.websocket.bean.SendMsgVo;
-//import com.fs.app.websocket.service.WebSocketServer;
-//import com.fs.common.annotation.QuartzRunnable;
-//import com.fs.common.constant.LiveKeysConstant;
-//import com.fs.common.core.domain.R;
-//import com.fs.common.core.redis.RedisCache;
-//import com.fs.common.core.redis.RedisUtil;
-//import com.fs.common.utils.StringUtils;
-//import com.fs.core.aspectj.lock.DistributeLock;
-//import com.fs.erp.domain.ErpDeliverys;
-//import com.fs.erp.domain.ErpOrderQuery;
-//import com.fs.erp.dto.ErpOrderQueryRequert;
-//import com.fs.erp.dto.ErpOrderQueryResponse;
-//import com.fs.erp.service.FsJstAftersalePushService;
-//import com.fs.erp.service.IErpOrderService;
-//import com.fs.erp.utils.ErpContextHolder;
-//import com.fs.live.domain.*;
-//import com.fs.live.mapper.LiveLotteryRegistrationMapper;
-//import com.fs.live.param.LiveReplayParam;
-//import com.fs.live.service.*;
-//import com.fs.live.vo.LiveConfigVo;
-//import com.fs.live.vo.LiveLotteryConfVo;
-//import com.fs.live.vo.LiveLotteryProductListVo;
-//import com.fs.store.domain.FsExpress;
-//import com.fs.store.enums.OrderLogEnum;
-//import com.fs.store.mapper.FsWarehousesMapper;
-//import com.fs.store.param.LiveAfterSalesAudit1Param;
-//import com.fs.store.service.IFsExpressService;
-//import com.fs.store.service.IFsUserService;
-//import lombok.AllArgsConstructor;
-//import org.apache.commons.collections4.CollectionUtils;
-//import org.apache.commons.lang.ObjectUtils;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.scheduling.annotation.Scheduled;
-//import org.springframework.stereotype.Component;
-//import org.springframework.transaction.annotation.Transactional;
-//
-//import java.math.BigDecimal;
-//import java.text.ParseException;
-//import java.time.Instant;
-//import java.time.LocalDateTime;
-//import java.util.*;
-//import java.util.stream.Collectors;
-//
-//@Component
-//@AllArgsConstructor
-//public class Task {
-//
-//    private static final Logger log = LoggerFactory.getLogger(Task.class);
-//    private final ILiveService liveService;
-//
-//    private final ILiveDataService liveDataService;
-//
-//    private final RedisCache redisCache;
-//
-//    @Autowired
-//    private ILiveWatchUserService liveWatchUserService;
-//    @Autowired
-//    private IFsUserService fsUserService;
-//    @Autowired
-//    private ILiveRewardRecordService liveRewardRecordService;
-//    @Autowired
-//    private WebSocketServer webSocketServer;
-//    @Autowired
-//    private ILiveAutoTaskService liveAutoTaskService;
-//    @Autowired
-//    private ILiveLotteryConfService liveLotteryConfService;
-//    @Autowired
-//    private ILiveUserLotteryRecordService liveUserLotteryRecordService;
-//    @Autowired
-//    private LiveLotteryRegistrationMapper liveLotteryRegistrationMapper;
-//    @Autowired
-//    private ILiveRedConfService liveRedConfService;
-////1111
-//    @Autowired
-//    private ILiveOrderService liveOrderService;
-//
-//    @Autowired
-//    private ILiveAfterSalesService afterSalesService;
-//
-//
-//    @Autowired
-//    private IErpOrderService erpOrderService;
-//
-//
-//    @Autowired
-//    private IFsExpressService expressService;
-//
-//
-//    @Autowired
-//    private ILiveOrderLogsService orderLogsService;
-//
-//
-//    @Autowired
-//    private FsWarehousesMapper fsWarehousesMapper;
-//
-//    @Autowired
-//    public FsJstAftersalePushService fsJstAftersalePushService;
-//    @Autowired
-//    public RedisUtil redisUtil;
-//    @Scheduled(cron = "0 0/1 * * * ?")
-//    @DistributeLock(key = "updateLiveStatusByTime", scene = "task")
-//    //public void selectSopUserLogsListByTime() {
-//    public void updateLiveStatusByTime() {
-//        List<Live> list = liveService.selectNoEndLiveList();
-//        if (list.isEmpty())
-//            return;
-//        List<Long> liveIdLists = list.stream().map(Live::getLiveId).collect(Collectors.toList());
-//        List<LiveAutoTask> liveAutoTasks = liveAutoTaskService.selectLiveAutoTaskByLiveIds(liveIdLists);
-//        List<Live> activeLiveList = new ArrayList<>();
-//        LocalDateTime now = LocalDateTime.now();
-//        list.forEach(live -> {
-//            if (live.getLiveType() != 3) {
-//                if (live.getFinishTime() == null) {
-//                    if (now.isAfter(live.getStartTime().minusSeconds(2L))){
-//                        live.setStatus(2);
-//                        activeLiveList.add( live);
-//                    } else if (now.isBefore(live.getStartTime())) {
-//                        live.setStatus(1);
-//                    }
-//                } else {
-//                    if (now.isAfter(live.getStartTime().minusSeconds(2L)) && now.isBefore(live.getFinishTime())) {
-//                        live.setStatus(2);
-//                        activeLiveList.add( live);
-//                    } else if (now.isBefore(live.getStartTime().minusSeconds(2L))) {
-//                        live.setStatus(1);
-//                    } else if (now.isAfter(live.getFinishTime().minusSeconds(2L))) {
-//                        live.setStatus(3);
-//                    }
-//                }
-//            } else {
-//                // 直播回放只需要检测结束时间就好了
-//                LiveReplayParam liveReplayParam = JSON.parseObject(live.getLiveConfig(), LiveReplayParam.class);
-//                if (liveReplayParam.getIsPlaybackOpen()) {
-//                    if (liveReplayParam.getFinishTime() != null) {
-//                        if (now.isAfter(live.getFinishTime().minusSeconds(2L))) {
-//                            live.setStatus(3);
-//                        }
-//                    }
-//                }
-//            }
-//
-//        });
-//        String key = "live:auto_task:";
-//        if(!activeLiveList.isEmpty()){
-//            activeLiveList.forEach(live -> {
-//                List<LiveAutoTask> collect = liveAutoTasks.stream().filter(liveAutoTask -> liveAutoTask.getLiveId().equals(live.getLiveId())).collect(Collectors.toList());
-//                if (!collect.isEmpty()) {
-//                    collect.forEach(liveAutoTask -> {
-//                        liveAutoTask.setCreateTime(null);
-//                        liveAutoTask.setUpdateTime(null);
-//                        redisCache.redisTemplate.opsForZSet().add(key + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
-//                        redisCache.redisTemplate.expire(key+live.getLiveId(), 30, java.util.concurrent.TimeUnit.MINUTES);
-//                    });
-//                }
-//            });
-//        }
-//
-//        if(!list.isEmpty()){
-//            for (Live live : list) {
-//                liveService.updateLive(live);
-//            }
-//        }
-//    }
-//    @Scheduled(cron = "0/1 * * * * ?")
-//    @DistributeLock(key = "liveLotteryTask", scene = "task")
-//    public void liveLotteryTask() {
-//        long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
-//        String lotteryKey = "live:lottery_task:*";
-//        Set<String> allLiveKeys = redisCache.redisTemplate.keys(lotteryKey);
-//        if (allLiveKeys != null && !allLiveKeys.isEmpty()) {
-//            for (String liveKey : allLiveKeys) {
-//                Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
-//                if (range == null || range.isEmpty()) {
-//                    continue;
-//                }
-//                processLotteryTask(range);
-//                redisCache.redisTemplate.opsForZSet()
-//                        .removeRangeByScore(liveKey, 0, currentTime);
-//            }
-//        }
-//
-//        String redKey = "live:red_task:*";
-//        allLiveKeys = redisCache.redisTemplate.keys(redKey);
-//        if (allLiveKeys == null || allLiveKeys.isEmpty()) {
-//            return;
-//        }
-//        for (String liveKey : allLiveKeys) {
-//            Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
-//            if (range == null || range.isEmpty()) {
-//                continue;
-//            }
-//
-//            updateRedStatus(range);
-//            redisCache.redisTemplate.opsForZSet()
-//                    .removeRangeByScore(liveKey, 0, currentTime);
-//            try {
-//                // 广播红包关闭消息
-//                SendMsgVo sendMsgVo = new SendMsgVo();
-//                sendMsgVo.setLiveId(Long.valueOf(liveKey));
-//                sendMsgVo.setCmd("red");
-//                sendMsgVo.setStatus(-1);
-//                webSocketServer.broadcastMessage(Long.valueOf(liveKey), JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
-//            } catch (Exception e) {
-//                log.error("更新红包状态异常", e);
-//            }
-//        }
-//    }
-//
-//    private void updateRedStatus(Set<String> range) {
-//
-//        liveRedConfService.finishRedStatusBySetIds(range);
-//    }
-//
-//    private void processLotteryTask(Set<String> range) {
-//        List<LiveLotteryConfVo> liveLotteries = liveLotteryConfService.selectVoListByLotteryIds(range);
-//        if(liveLotteries.isEmpty()) return;
-//        Date now = new Date();
-//        for (LiveLotteryConfVo liveLottery : liveLotteries) {
-//            // 查询抽奖数量
-//            List<LiveLotteryProductListVo> products = liveLottery.getProducts();
-//            Integer totalLots = products.stream().mapToInt(liveLotteryProductListVo -> Math.toIntExact(liveLotteryProductListVo.getTotalLots())).sum();
-//            if(totalLots <= 0) continue;
-//            // 先将参与记录插入数据库
-//            String hashKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_DRAW, liveLottery.getLiveId(), liveLottery.getLotteryId());
-//            Map<Object, Object> hashEntries = redisUtil.hashEntries(hashKey);
-//            List<LiveLotteryRegistration> registrationList = new ArrayList<>();
-//            if (CollUtil.isNotEmpty(hashEntries)) {
-//                registrationList = hashEntries.values().stream()
-//                        .map(value -> JSONUtil.toBean(JSONUtil.parseObj(value), LiveLotteryRegistration.class))
-//                        .collect(Collectors.toList());
-//                liveLotteryRegistrationMapper.insertLiveLotteryRegistrationBatch(registrationList);
-//            }
-//
-//            // 查询在线用户 并且参与了抽奖的用户
-//            List<LiveWatchUser> liveWatchUsers = liveWatchUserService.selectLiveWatchAndRegisterUser(liveLottery.getLiveId(),liveLottery.getLotteryId(), totalLots);
-//            if(liveWatchUsers.isEmpty()) continue;
-//            LiveLotteryRegistration liveLotteryRegistration;
-//            // 收集中奖信息
-//            List<LotteryVo> lotteryVos = new ArrayList<>();
-//            for (LiveLotteryProductListVo liveLotteryProductListVo : products) {
-//                // 随机抽奖一个用户获取奖品
-//                Long totalLotsPerProduct = liveLotteryProductListVo.getTotalLots();
-//                for (int i = 0; i < totalLotsPerProduct && !liveWatchUsers.isEmpty(); i++) {
-//                    // 随机选择一个用户
-//                    int randomIndex = new Random().nextInt(liveWatchUsers.size());
-//                    LiveWatchUser winningUser = liveWatchUsers.get(randomIndex);
-//
-//                    // 创建中奖记录
-//                    LiveUserLotteryRecord record = new LiveUserLotteryRecord();
-//                    record.setLotteryId(liveLottery.getLotteryId());
-//                    record.setLiveId(liveLottery.getLiveId());
-//                    record.setUserId(winningUser.getUserId());
-//                    record.setProductId(liveLotteryProductListVo.getProductId());
-//                    record.setCreateTime(new Date());
-//
-//                    // 保存中奖记录
-//                    liveUserLotteryRecordService.insertLiveUserLotteryRecord(record);
-//                    liveLotteryRegistration = new LiveLotteryRegistration();
-//                    liveLotteryRegistration.setLotteryId(liveLottery.getLotteryId());
-//                    liveLotteryRegistration.setLiveId(liveLottery.getLotteryId());
-//                    liveLotteryRegistration.setUserId(winningUser.getUserId());
-//                    liveLotteryRegistration.setIsWin(1L);
-//                    liveLotteryRegistration.setUpdateTime(now);
-//                    liveLotteryRegistration.setRizeLevel(liveLotteryProductListVo.getPrizeLevel());
-//                    liveLotteryRegistrationMapper.updateLiveLotteryRegistrationNoId(liveLotteryRegistration);
-//                    // 从候选列表中移除该用户,确保每人只能中奖一次
-//                    liveWatchUsers.remove(randomIndex);
-//                    LotteryVo lotteryVo = new LotteryVo();
-//                    lotteryVo.setUserId(winningUser.getUserId());
-//                    lotteryVo.setUserName(winningUser.getNickName());
-//                    lotteryVo.setPrizeLevel(liveLotteryProductListVo.getPrizeLevel());
-//                    lotteryVo.setProductName(liveLotteryProductListVo.getProductName());
-//                    lotteryVos.add(lotteryVo);
-//                }
-//            }
-//            SendMsgVo sendMsgVo = new SendMsgVo();
-//            sendMsgVo.setLiveId(liveLottery.getLiveId());
-//            sendMsgVo.setCmd("LotteryDetail");
-//            sendMsgVo.setData(JSON.toJSONString(lotteryVos));
-//            webSocketServer.broadcastMessage(liveLottery.getLiveId(), JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
-//
-//            // 删除缓存 同步抽奖记录
-//            redisUtil.delete(hashKey);
-//        }
-//
-//        List<Long> collect = liveLotteries.stream().map(LiveLotteryConfVo::getLotteryId).collect(Collectors.toList());
-//        liveLotteryConfService.finishStatusByLotteryIds(collect);
-//    }
-//
-//    @Scheduled(cron = "0/1 * * * * ?")
-//    @DistributeLock(key = "liveAutoTask", scene = "task")
-//    public void liveAutoTask() {
-//        long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
-//
-//        Set<String> allLiveKeys = redisCache.redisTemplate.keys("live:auto_task:*");
-//        if (allLiveKeys == null || allLiveKeys.isEmpty()) {
-//            return; // 没有数据,直接返回
-//        }
-//        // 2. 遍历每个直播间的ZSet键
-//        for (String liveKey : allLiveKeys) {
-//            // 3. 获取当前直播间ZSet中所有元素(按score排序)
-//            // range方法:0表示第一个元素,-1表示最后一个元素,即获取全部
-//            Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
-//            if (range == null || range.isEmpty()) {
-//                continue; // 没有数据,直接返回
-//            }
-//            redisCache.redisTemplate.opsForZSet()
-//                    .removeRangeByScore(liveKey, 0, currentTime);
-//            processAutoTask(range);
-//        }
-//    }
-//
-//    private void processAutoTask(Set<String> range) {
-//        for (String liveAutoTask : range) {
-//            LiveAutoTask task = JSON.parseObject(liveAutoTask, LiveAutoTask.class);
-//            webSocketServer.handleAutoTask(task);
-//            task.setFinishStatus(1L);
-//            liveAutoTaskService.finishLiveAutoTask(task);
-//        }
-//    }
-//
-//    @Scheduled(cron = "0 0/1 * * * ?")
-//    @DistributeLock(key = "autoUpdateWatchReward", scene = "task")
-//    @Transactional
-//    public void autoUpdateWatchReward() {
-//
-//        // 1.查询所有直播中的直播间
-//        List<Live> lives = liveService.liveList();
-//
-//
-//        // 2.检查是否开启观看奖励
-//        List<Live> openRewardLives = lives.stream().filter(live -> StringUtils.isNotEmpty(live.getConfigJson())).collect(Collectors.toList());
-//        Date now = new Date();
-//
-//        for (Live openRewardLive : openRewardLives) {
-//            String configJson = openRewardLive.getConfigJson();
-//            LiveWatchConfig config = JSON.parseObject(configJson, LiveWatchConfig.class);
-//            if (config.getEnabled()) {
-//                // 3.检查当前直播间的在线用户(可以传入一个时间,然后查出来当天没领取奖励的用户)
-//                List<LiveWatchUser> onlineUser = liveWatchUserService.checkOnlineNoRewardUser(openRewardLive.getLiveId(), now)
-//                        .stream().filter(user -> now.getTime() - user.getUpdateTime().getTime() > config.getWatchDuration() * 60 * 1000)
-//                        .collect(Collectors.toList());
-//                if(onlineUser.isEmpty()) continue;
-//
-//                List<Long> userIds = onlineUser.stream().map(LiveWatchUser::getUserId).collect(Collectors.toList());
-//                // 4.保存用户领取记录
-//                saveUserRewardRecord(openRewardLive, userIds,config.getScoreAmount());
-//                // 5.更新用户积分(芳华币
-//                fsUserService.incrIntegral(userIds,config.getScoreAmount());
-//                // 6.发送websocket事件消息 通知用户自动领取成功
-//                userIds.forEach(userId -> webSocketServer.sendIntegralMessage(openRewardLive.getLiveId(),userId,config.getScoreAmount()));
-//
-//            }
-//        }
-//    }
-//    private void saveUserRewardRecord(Live live, List<Long> userIds,Long scoreAmount) {
-//        for (Long userId : userIds) {
-//            LiveRewardRecord record = new LiveRewardRecord();
-//            record.setLiveId(live.getLiveId());
-//            record.setUserId(userId);
-//            record.setIncomeType(1L);
-//            record.setSourceType(1L);
-//            record.setSourceId(live.getCompanyId() == null ? 0L : live.getCompanyId());
-//            record.setRewardType(2L);
-//            record.setNum(BigDecimal.valueOf(scoreAmount));
-//            record.setRewardType(2L);
-//            record.setCreateTime(new Date());
-//            record.setCreateBy(String.valueOf(userId));
-//            liveRewardRecordService.insertLiveRewardRecord(record);
-//        }
-//    }
-//
-//    /**
-//     * 更新红包领取数量
-//     */
-//    @Scheduled(cron = "0/5 * * * * ?")
-//    @DistributeLock(key = "updateRedQuantityNum", scene = "task")
-//    public void updateRedQuantityNum() {
-//        liveRedConfService.updateRedQuantityNum();
-//    }
-//}
+package com.fs.app.task;
+
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+@AllArgsConstructor
+@Slf4j
+public class Task {
+
+
+}

+ 1 - 1
fs-user-app/src/main/java/com/fs/core/config/properties/DruidProperties.java

@@ -6,7 +6,7 @@ import org.springframework.context.annotation.Configuration;
 
 /**
  * druid 配置属性
- * 
+ *
 
  */
 @Configuration

+ 3 - 3
fs-user-app/src/main/resources/application-dev.yml

@@ -39,11 +39,11 @@ spring:
                 username: root
                 password: Ylrz_1q2w3e4r5t6y
             # 初始连接数
-            initialSize: 5
+            initialSize: 50
             # 最小连接池数量
-            minIdle: 10
+            minIdle: 50
             # 最大连接池数量
-            maxActive: 20
+            maxActive: 500
             # 配置获取连接等待超时的时间
             maxWait: 60000
             # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

+ 1 - 1
fs-user-app/src/main/resources/application.yml

@@ -58,7 +58,7 @@ server:
 # 日志配置
 logging:
   level:
-    com.fs: debug
+#    com.fs: debug
     org.springframework: warn
     org.springframework.web: info
     cn.binarywang.wx.miniapp: debug

Некоторые файлы не были показаны из-за большого количества измененных файлов