Bläddra i källkod

聚水潭拆分订单处理,红包领取,销售公司分佣数据计算

yuhongqi 1 vecka sedan
förälder
incheckning
ec02ae99e1

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

@@ -339,9 +339,6 @@ public class LiveTask {
             request.setCode(order.getExtendOrderId());
             IErpOrderService erpOrderService = getErpOrderService();
             ErpOrderQueryResponse response = erpOrderService.getLiveOrder(request);
-            if(!response.getSuccess() && "429".equals(response.getCode())){
-                break;
-            }
             if (erpOrderService != dfOrderService) {
                 if (response.getOrders() != null && !response.getOrders().isEmpty()) {
                     for (ErpOrderQuery orderQuery : response.getOrders()) {

+ 0 - 3
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -267,9 +267,6 @@ public class MallStoreTask
             request.setCode(order.getExtendOrderId());
             IErpOrderService erpOrderService = getErpOrderService();
             ErpOrderQueryResponse response = erpOrderService.getScrmOrder(request);
-            if(!response.getSuccess() && "429".equals(response.getCode())){
-                break;
-            }
             if (erpOrderService != dfOrderService) {
                 if(response.getOrders()!=null && !response.getOrders().isEmpty()){
                     for(ErpOrderQuery orderQuery : response.getOrders()){

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

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

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

@@ -685,7 +685,7 @@ public class CompanyServiceImpl implements ICompanyService
                 BigDecimal tuiMoney = BigDecimal.ZERO;
                 if (config != null && config.getTuiMoneyRate() != null) {
                     Double rate = config.getTuiMoneyRate() / 100d;
-                    tuiMoney = order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                    tuiMoney = order.getTotalPrice().multiply(new BigDecimal(rate));
                 } else {
                     tuiMoney = order.getPayPrice();
                 }
@@ -719,8 +719,8 @@ public class CompanyServiceImpl implements ICompanyService
                 //支付金额-(订单金额*rate%)
                 BigDecimal tuiMoney = BigDecimal.ZERO;
                 if (config != null && config.getTuiMoneyRate() != null) {
-                    Double rate = config.getTuiMoneyRate() / 100d;
-                    tuiMoney = order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                    double rate = config.getTuiMoneyRate() / 100d;
+                    tuiMoney = order.getTotalPrice().multiply(new BigDecimal(rate));
                 } else {
                     tuiMoney = order.getPayPrice();
                 }

+ 48 - 11
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -592,19 +592,56 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         // 2. 调用ERP服务查询订单
         OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
 
-
-
         // 4. 设置基本响应信息
 
         // 5. 转换订单数据
         if (query.getOrders() != null && !query.getOrders().isEmpty()) {
-            List<ErpOrderQuery> erpOrders = query.getOrders().stream()
-                    .map(this::convertToErpOrderQueryLive)
-                    .collect(Collectors.toList());
-//            if ("Split".equals(query.getOrders().get(0).getStatus())) {
-//                this.splitLiveOrder(query.getOrders().get(0));
-//            }
-            response.setOrders(erpOrders);
+            OrderQueryResponseDTO.Order firstOrder = query.getOrders().get(0);
+            
+            // 如果订单状态是拆分,重新查询聚水潭获取所有拆分订单
+            if ("Split".equals(firstOrder.getStatus())) {
+                // 处理拆分订单逻辑
+//                this.splitLiveOrder(firstOrder);
+                
+                // 使用订单号(soId)重新查询聚水潭,获取所有相关订单
+                OrderQueryRequestDTO splitRequestDTO = new OrderQueryRequestDTO();
+                splitRequestDTO.setSoIds(Collections.singletonList(firstOrder.getSoId()));
+                splitRequestDTO.setOrderItemFlds(Arrays.asList("status"));
+                
+                // 重新查询聚水潭
+                OrderQueryResponseDTO splitQuery = jstErpHttpService.query(splitRequestDTO);
+                
+                if (splitQuery != null && splitQuery.getOrders() != null && !splitQuery.getOrders().isEmpty()) {
+                    // 查找状态不为拆分并且已发货的订单
+                    OrderQueryResponseDTO.Order sentOrder = splitQuery.getOrders().stream()
+                            .filter(order -> !"Split".equals(order.getStatus()) 
+                                    && ErpQueryOrderStatusEnum.SENT.getCode().equals(order.getStatus()))
+                            .findFirst()
+                            .orElse(null);
+                    
+                    if (sentOrder != null) {
+                        // 找到符合条件的订单,转换状态并设置到响应中
+                        ErpOrderQuery erpOrder = convertToErpOrderQueryLive(sentOrder);
+                        response.setOrders(Collections.singletonList(erpOrder));
+                        log.info("拆分订单查询:找到已发货订单,orderCode: {}, status: {}", 
+                                sentOrder.getSoId(), sentOrder.getStatus());
+                    } else {
+                        // 如果没有找到已发货的订单,返回空列表
+                        log.warn("拆分订单查询:未找到已发货的非拆分订单,orderCode: {}", firstOrder.getSoId());
+                        response.setOrders(Collections.emptyList());
+                    }
+                } else {
+                    // 重新查询失败,返回空列表
+                    log.error("拆分订单查询:重新查询聚水潭失败,orderCode: {}", firstOrder.getSoId());
+                    response.setOrders(Collections.emptyList());
+                }
+            } else {
+                // 非拆分订单,正常转换
+                List<ErpOrderQuery> erpOrders = query.getOrders().stream()
+                        .map(this::convertToErpOrderQueryLive)
+                        .collect(Collectors.toList());
+                response.setOrders(erpOrders);
+            }
         } else {
             response.setOrders(Collections.emptyList());
         }
@@ -635,8 +672,8 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             }
 
             // ③ 把原来的订单状态修改为 6(被拆分)
-            liveOrder.setStatus(6);
-            liveOrderMapper.updateLiveOrder(liveOrder);
+//            liveOrder.setStatus(6);
+//            liveOrderMapper.updateLiveOrder(liveOrder);
             log.info("直播拆分订单:原订单状态已更新为被拆分, orderCode={}, orderId={}", order.getSoId(), liveOrder.getOrderId());
 
             // ④ 查询出来的订单里面,除了原来的订单,将查询出来的订单新增到数据库里面

+ 138 - 116
fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -71,6 +71,7 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     private static final String REDPACKET_REMAININGLOTS_KEY = "live:red:remainingLots:";
     private static final String REDPACKET_REMAININGNUM_KEY = "live:red:remainingNum:";
     private static final String REDPACKET_CLAIM_KEY = "live:red:claim:";
+    private static final String REDPACKET_CONF_CACHE_KEY = "live:red:conf:"; // 红包配置缓存
 
     @Autowired
     private LiveRedConfMapper baseMapper;
@@ -130,10 +131,17 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
             double score = localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
             redisCache.redisTemplate.opsForZSet().add(cacheKey, String.valueOf(liveRedConf.getRedId()), score);
             redisCache.redisTemplate.expire(cacheKey, 30, TimeUnit.MINUTES);
+            
+            // 将红包配置缓存到 Redis(用于高并发查询)
+            String redConfCacheKey = REDPACKET_CONF_CACHE_KEY + liveRedConf.getRedId();
+            redisCache.setCacheObject(redConfCacheKey, JSONUtil.toJsonStr(liveRedConf), liveRedConf.getDuration().intValue() + 5, TimeUnit.MINUTES);
+            log.info("红包配置已缓存到 Redis,redId: {}, liveId: {}", liveRedConf.getRedId(), liveRedConf.getLiveId());
         } else {
             // 其他
             redisCache.deleteObject(REDPACKET_REMAININGLOTS_KEY + liveRedConf.getRedId());
             redisCache.deleteObject(cacheKey);
+            // 删除红包配置缓存
+            redisCache.deleteObject(REDPACKET_CONF_CACHE_KEY + liveRedConf.getRedId());
             redStatusUpdate(CollUtil.newHashSet(liveRedConf.getRedId()));
         }
         return baseMapper.updateLiveRedConf(liveRedConf);
@@ -226,137 +234,151 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         baseMapper.deleteById(redId);
     }
 
-    /**
-     * 用户领取红包
-     */
+
     @Override
-    @Transactional
     public R claimRedPacket(RedPO red) {
-        // String claimKey = REDPACKET_CLAIM_KEY + red.getRedId();
-        Object o = redisCache.hashGet(String.format(LiveKeysConstant. LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()));
-        if (ObjectUtil.isNotEmpty(o)) {
-            return R.error("您已经领取过红包了!");
-        }
-        /*try {*/
-/*            //获取红包锁w
-            if (!tryLock(claimKey, red.getUserId().toString(), 5)) {
-                return R.error("您已经领取过红包了!");
-            }*/
-
-        LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(red.getRedId());
-        if (conf == null || conf.getRedStatus() != 1) {
-            return R.error("手慢了,红包已结束~");
-        }
-        //redis剩余红包数
-        // 平均分 暂时不适用redis 记录红包数
-        Long integral = calculateIntegralAverage(conf);
-        if (0L == integral) {
-            return R.error("手慢了,红包被抢完了~");
-        }
-
-        // 更新数据库
-/*
-        Date now = new Date();
-        conf.setTotalSend(conf.getTotalSend() + 1);
-        conf.setRemaining(Math.toIntExact(conf.getTotalLots() - conf.getTotalSend()));
-        conf.setUpdateTime(now);
-        baseMapper.updateLiveRedConf(conf);
-*/
-
-        // 最后更新缓存
-        if (getRemaining(red.getRedId()) <= 0 || !decreaseRemainingLotsIfPossible(red.getRedId())) {
-            LiveRedConf liveRedConf = new LiveRedConf();
-            liveRedConf.setRedId(red.getRedId());
-            liveRedConf.setRedStatus(2L);
-            baseMapper.updateLiveRedConf(liveRedConf);
-            Set<String> range = CollUtil.newHashSet(String.valueOf(red.getRedId()));
-//            finishRedStatusBySetIds(range);
-            updateDbByRed(liveRedConf);
-            return R.error("手慢了,红包已被抢完~");
-        }
-        // 记录用户红包
+//             * 1. 使用 Redis HSETNX 原子操作保证幂等性(每个用户只能领取一次)
+//     * 2. 从 Redis 读取红包配置(提高响应速度)
+//     * 3. 使用 Redis 原子操作减少剩余数量
+//                * 4. 异步更新数据库
+        String redisKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId());
+        String userIdStr = String.valueOf(red.getUserId());
+        
+        // 1. 使用 Redis HSETNX 原子操作保证幂等性(每个用户只能领取一次)
+        // 先尝试在 Redis 中标记用户已领取(原子操作,保证高并发安全)
         LiveUserRedRecord record = new LiveUserRedRecord();
         record.setRedId(red.getRedId());
         record.setLiveId(red.getLiveId());
         record.setUserId(red.getUserId());
-        record.setIntegral(integral);
         record.setCreateTime(new Date());
         
-        // 双重检查:先检查 Redis(已有),再检查数据库(防止重复领取)
-        String redisKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId());
-        Object redisRecord = redisCache.hashGet(redisKey, String.valueOf(red.getUserId()));
-        if (ObjectUtil.isNotEmpty(redisRecord)) {
-            log.warn("用户 {} 在 Redis 中已存在红包记录 redId: {},跳过重复处理", red.getUserId(), red.getRedId());
-            return R.error("您已经领取过红包了!");
-        }
-        
-        LiveUserRedRecord queryRecord = new LiveUserRedRecord();
-        queryRecord.setUserId(red.getUserId());
-        queryRecord.setRedId(red.getRedId());
-        List<LiveUserRedRecord> existingRecords = userRedRecordMapper.selectLiveUserRedRecordList(queryRecord);
-        if (existingRecords != null && !existingRecords.isEmpty()) {
-            log.warn("用户 {} 在数据库中已存在红包记录 redId: {},跳过重复处理", red.getUserId(), red.getRedId());
-            // 如果数据库已有记录但 Redis 没有,同步到 Redis
-            redisCache.hashPut(redisKey, String.valueOf(red.getUserId()), JSONUtil.toJsonStr(existingRecords.get(0)));
+        // 使用 HSETNX 原子操作:如果字段不存在则设置,返回 true;如果已存在则返回 false
+        Boolean claimed = redisCache.hashPutIfAbsent(redisKey, userIdStr, "claimed");
+        if (Boolean.FALSE.equals(claimed)) {
+            // 用户已经领取过(Redis 中已存在记录)
+            log.debug("用户 {} 已领取过红包 redId: {}(Redis 检查)", red.getUserId(), red.getRedId());
             return R.error("您已经领取过红包了!");
+        } else {
+            redisCache.expire(redisKey, 24, TimeUnit.HOURS);
         }
         
-        // 先插入数据库记录(使用数据库约束防止重复)
-        int insertResult = userRedRecordMapper.insertLiveUserRedRecord(record);
-        if (insertResult <= 0) {
-            log.error("插入红包记录失败,userId: {}, redId: {}", red.getUserId(), red.getRedId());
-            return R.error("领取红包失败,请稍后重试");
-        }
-        
-        // 插入后再次验证,防止并发插入导致重复
-        List<LiveUserRedRecord> verifyRecords = userRedRecordMapper.selectLiveUserRedRecordList(queryRecord);
-        if (verifyRecords != null && verifyRecords.size() > 1) {
-            // 发现重复记录,删除刚插入的记录并回滚
-            log.error("检测到重复红包记录,userId: {}, redId: {},记录数: {}", red.getUserId(), red.getRedId(), verifyRecords.size());
-            // 删除最后插入的记录(通常是当前请求插入的)
-            userRedRecordMapper.deleteLiveUserRedRecordById(record.getId());
-            return R.error("您已经领取过红包了!");
+        try {
+            // 2. 从 Redis 读取红包配置(优先从缓存读取,提高响应速度)
+            String redConfCacheKey = REDPACKET_CONF_CACHE_KEY + red.getRedId();
+            Object confCache = redisCache.getCacheObject(redConfCacheKey);
+            LiveRedConf conf = null;
+            
+            if (confCache != null) {
+                try {
+                    conf = JSONUtil.toBean(confCache.toString(), LiveRedConf.class);
+                    log.debug("从 Redis 缓存读取红包配置,redId: {}", red.getRedId());
+                } catch (Exception e) {
+                    log.warn("从 Redis 缓存解析红包配置失败,从数据库读取,redId: {}", red.getRedId(), e);
+                }
+            }
+            
+            // 如果 Redis 中没有配置,从数据库读取并缓存
+            if (conf == null) {
+                conf = baseMapper.selectLiveRedConfByRedId(red.getRedId());
+                if (conf != null && conf.getRedStatus() == 1) {
+                    // 缓存到 Redis
+                    redisCache.setCacheObject(redConfCacheKey, JSONUtil.toJsonStr(conf), conf.getDuration().intValue() + 5, TimeUnit.MINUTES);
+                    log.debug("从数据库读取红包配置并缓存到 Redis,redId: {}", red.getRedId());
+                }
+            }
+            
+            // 验证红包状态
+            if (conf == null || conf.getRedStatus() != 1) {
+                // 回滚:删除 Redis 中的标记
+                redisCache.hashDelete(redisKey, userIdStr);
+                return R.error("手慢了,红包已结束~");
+            }
+            
+            // 3. 使用 Redis 原子操作减少剩余数量
+            if (getRemaining(red.getRedId()) <= 0 || !decreaseRemainingLotsIfPossible(red.getRedId())) {
+                // 回滚:删除 Redis 中的标记
+                redisCache.hashDelete(redisKey, userIdStr);
+                // 更新红包状态为已结束
+                LiveRedConf liveRedConf = new LiveRedConf();
+                liveRedConf.setRedId(red.getRedId());
+                liveRedConf.setRedStatus(2L);
+                baseMapper.updateLiveRedConf(liveRedConf);
+                // 删除配置缓存
+                redisCache.deleteObject(redConfCacheKey);
+                return R.error("手慢了,红包已被抢完~");
+            }
+            
+            // 计算积分(平均分)
+            Long integral = calculateIntegralAverage(conf);
+            if (0L == integral) {
+                // 回滚:删除 Redis 中的标记
+                redisCache.hashDelete(redisKey, userIdStr);
+                return R.error("手慢了,红包被抢完了~");
+            }
+            
+            record.setIntegral(integral);
+            
+            // 4. 更新用户积分(同步操作,保证数据一致性)
+            BigDecimal balanceAmount = BigDecimal.valueOf(integral);
+            int updateResult = fsUserScrmMapper.incrIntegral(red.getUserId(), balanceAmount);
+            if (updateResult <= 0) {
+                // 回滚:删除 Redis 中的标记和恢复剩余数量
+                redisCache.hashDelete(redisKey, userIdStr);
+                // 恢复剩余数量
+                String remainingKey = REDPACKET_REMAININGLOTS_KEY + red.getRedId();
+                redisCache.redisTemplate.opsForValue().increment(remainingKey);
+                log.error("更新用户余额失败,userId: {}, balance: {}", red.getUserId(), balanceAmount);
+                return R.error("更新用户余额失败");
+            }
+            
+            // 5. 更新 Redis 缓存中的记录(包含完整信息)
+            record.setCreateTime(new Date());
+            redisCache.hashPut(redisKey, userIdStr, JSONUtil.toJsonStr(record));
+            
+            // 6. 异步更新数据库(提高响应速度,不阻塞用户)
+            // 查询用户当前余额(用于积分日志)
+            com.fs.hisStore.domain.FsUserScrm user = fsUserScrmMapper.selectFsUserById(red.getUserId());
+            Long currentIntegral = user.getIntegral() != null ? user.getIntegral() : 0L;
+            
+
+            final LiveUserRedRecord finalRecord = record;
+            final LiveRedConf finalConf = conf;
+            final Long finalIntegral = integral;
+            final Long finalCurrentIntegral = currentIntegral;
+            
+        try {
+            // 插入红包记录
+            userRedRecordMapper.insertLiveUserRedRecord(finalRecord);
+
+            // 添加积分变动记录
+            FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+            integralLogs.setUserId(red.getUserId());
+            integralLogs.setIntegral(finalIntegral);
+            integralLogs.setBalance(finalCurrentIntegral);
+            integralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_26.getValue());
+            integralLogs.setBusinessId(String.valueOf(red.getRedId()));
+            integralLogs.setBusinessType(Math.toIntExact(finalConf.getRedId()));
+            integralLogs.setStatus(0);
+            integralLogs.setCreateTime(new Date());
+            fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+
+            log.debug("异步更新数据库成功,userId: {}, redId: {}, integral: {}", red.getUserId(), red.getRedId(), finalIntegral);
+        } catch (Exception e) {
+            // 数据库更新失败不影响用户领取(已更新积分),记录日志即可
+            log.error("异步更新数据库失败,userId: {}, redId: {}, integral: {}", red.getUserId(), red.getRedId(), finalIntegral, e);
         }
 
+            
 
-        // 查询用户当前余额
-        com.fs.hisStore.domain.FsUserScrm user = fsUserScrmMapper.selectFsUserById(red.getUserId());
-        Long currentIntegral = user.getIntegral() != null ? user.getIntegral() : 0L;
-        Long newIntegral = currentIntegral + integral;
-
-        // 添加余额变动记录
-        FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
-        integralLogs.setUserId(red.getUserId());
-        integralLogs.setIntegral(integral);
-        integralLogs.setBalance(newIntegral);
-        integralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_26.getValue()); // 3表示分享获得积分,可根据实际情况调整
-        integralLogs.setBusinessId(String.valueOf(red.getRedId()));
-        integralLogs.setBusinessType(Math.toIntExact(conf.getRedId())); // 1表示直播红包
-        integralLogs.setStatus(0);
-        integralLogs.setCreateTime(new Date());
-        fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
-        // 更新用户余额
-        BigDecimal balanceAmount = BigDecimal.valueOf(integral);
-        int updateResult = fsUserScrmMapper.incrIntegral(red.getUserId(), balanceAmount);
-        if (updateResult <= 0) {
-            log.error("更新用户余额失败,userId: {}, balance: {}", red.getUserId(), balanceAmount);
-            // 回滚:删除已插入的记录
-            userRedRecordMapper.deleteLiveUserRedRecordById(record.getId());
-            return R.error("更新用户余额失败");
+            
+            return R.ok("恭喜您成功抢到" + integral + "芳华币");
+            
+        } catch (Exception e) {
+            // 发生异常,回滚 Redis 标记
+            redisCache.hashDelete(redisKey, userIdStr);
+            log.error("领取红包异常,userId: {}, redId: {}", red.getUserId(), red.getRedId(), e);
+            return R.error("领取红包失败,请稍后重试");
         }
-
-        // 最后更新 Redis 缓存(确保原子性:先插入数据库,再更新 Redis)
-        redisCache.hashPut(redisKey, String.valueOf(red.getUserId()), JSONUtil.toJsonStr(record));
-
-        // WebSocket 通知
-        //String msg = String.format("用户 %d 抢到了红包 %d,获得 %d 芳华币", userId, redId, integral);
-        //WebSocketServer.notifyUsers(msg);
-        return R.ok("恭喜您成功抢到" + integral + "芳华币");
-/*        } catch (Exception e) {
-            e.printStackTrace();
-            log.error("抢红包异常:" + e.getMessage());
-        }*/
-        // return R.error("抢红包异常");
     }
 
     @Override