瀏覽代碼

修复订单号因缓存刷新导致重复的问题

Huanyi 2 天之前
父節點
當前提交
a7609e48c2

+ 45 - 32
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/service/impl/SysOrderServiceImpl.java

@@ -2,7 +2,10 @@ package org.dromara.order.service.impl;
 
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.IdUtil;
+import com.baomidou.lock.LockInfo;
 import com.baomidou.lock.LockTemplate;
+import com.baomidou.lock.executor.RedissonLockExecutor;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.CacheConstants;
@@ -42,6 +45,11 @@ public class SysOrderServiceImpl implements ISysOrderService {
 
     private final LockTemplate lockTemplate;
 
+    /**
+     * 订单号生成分布式锁Key前缀
+     */
+    private static final String ORDER_CODE_LOCK_KEY = "order:code:lock:";
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public boolean create(SysOrderCreateBo bo) {
@@ -168,47 +176,52 @@ public class SysOrderServiceImpl implements ISysOrderService {
 
     /**
      * 按照一定规则生成订单号
+     * <p>
+     * 规则:yyyyMMdd + 6位当日递增序号。
+     * 常规路径直接 Redis INCR 原子递增;
+     * 仅在计数器丢失时(Redis重启/Key过期),加分布式锁后查数据库恢复。
      */
     private String generateOrderCode() {
+        String date = DateUtil.format(new Date(), "yyyyMMdd");
+        String redisKey = CacheConstants.SYS_ORDER_COUNT + date;
+        String lockKey = ORDER_CODE_LOCK_KEY + date;
 
         try {
-            // 方案一:使用分布式锁来串行化
-//        String date = DateUtil.format(new Date(), "yyyyMMddHHmmss");
-
-//        LockInfo locker = lockTemplate.lock(LockContants.ORDER, 500L, 1000L);
-//
-//        try {
-//
-//            Long count = RedisUtils.getCacheObject(CacheConstants.SYS_ORDER_COUNT);
-//            if (count == null) {
-//                count = 1L;
-//            }
-//            RedisUtils.setCacheObject(CacheConstants.SYS_ORDER_COUNT, count + 1L);
-//
-//        } finally {
-//            // 无论是否抛出异常,都解锁
-//            if (locker != null) {
-//                lockTemplate.releaseLock(locker);
-//            }
-//        }
-//        return date;
-
-            // 方案二:Redis原子递增 以每天的订单为准
-            // 先不按照品牌或门店隔离
-            String date = DateUtil.format(new Date(), "yyyyMMdd");
-            String redisKey = CacheConstants.SYS_ORDER_COUNT + date;
-            long count = RedisUtils.incrAtomicValue(redisKey);
-            if (count == 1L) {
-                // 应对服务器时钟微调或网络延迟导致的临界点问题
-                RedisUtils.expire(redisKey, 60L * 60L * 25L);
+            Long exists = RedisUtils.getCacheObject(redisKey);
+            if (exists == null) {
+                LockInfo lockInfo = lockTemplate.lock(lockKey, 5000L, 3000L, RedissonLockExecutor.class);
+                if (lockInfo == null) {
+                    log.warn("订单号恢复锁获取超时,降级使用雪花ID");
+                    return "ORD" + IdUtil.getSnowflakeNextIdStr();
+                }
+                try {
+                    exists = RedisUtils.getCacheObject(redisKey);
+                    if (exists == null) {
+                        SysOrder lastOrder = baseMapper.selectOne(
+                            Wrappers.lambdaQuery(SysOrder.class)
+                                .likeRight(SysOrder::getCode, date)
+                                .orderByDesc(SysOrder::getId)
+                                .last("LIMIT 1")
+                        );
+                        if (lastOrder != null) {
+                            String lastCode = lastOrder.getCode();
+                            exists = Long.parseLong(lastCode.substring(lastCode.length() - 6));
+                        } else {
+                            exists = 0L;
+                        }
+                        RedisUtils.setCacheObject(redisKey, exists);
+                        RedisUtils.expire(redisKey, 60L * 60L * 25L);
+                    }
+                } finally {
+                    lockTemplate.releaseLock(lockInfo);
+                }
             }
+            long count = RedisUtils.incrAtomicValue(redisKey);
             return date + String.format("%06d", count);
         } catch (Exception e) {
-            // 容灾 如果Redis挂掉,降级采用雪花ID
-            log.error("Redis 订单号生成失败,降级使用本地雪花算法", e);
+            log.error("订单号生成失败,降级使用本地雪花算法", e);
             return "ORD" + IdUtil.getSnowflakeNextIdStr();
         }
-
     }
 
 }