jinshihui 23 ساعت پیش
والد
کامیت
773e1eda5a
1فایلهای تغییر یافته به همراه56 افزوده شده و 60 حذف شده
  1. 56 60
      nightFragrance-massage/src/main/java/com/ylx/massage/utils/OrderNumberGenerator.java

+ 56 - 60
nightFragrance-massage/src/main/java/com/ylx/massage/utils/OrderNumberGenerator.java

@@ -1,95 +1,91 @@
 package com.ylx.massage.utils;
 
-import com.ylx.common.constant.MassageConstants;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.data.redis.core.StringRedisTemplate;
-import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.stereotype.Component;
 
-import java.time.*;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
 
 @Component
 @Slf4j
 public class OrderNumberGenerator {
 
-    //订单单号前缀
+    // 订单单号前缀
     public static final String KEY_PREFIX_ORDER = "YORDER";
-
     public static final String KEY_PREFIX_RECHAR = "RECHAR";
-
     public static final String KEY_PREFIX_CASH = "CASH";
-
     public static final String KEY_PREFIX_REFUND = "REFUND";
-
     public static final String KEY_PREFIX_PRODUCTORDER = "PRODUCT";
 
-    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
+    private static final DateTimeFormatter DATE_FORMATTER2 = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
 
-    private final StringRedisTemplate stringRedisTemplate;
+    private static final int SEQUENCE_MAX_VALUE = 99999;
 
-    public OrderNumberGenerator(StringRedisTemplate stringRedisTemplate) {
-        this.stringRedisTemplate = stringRedisTemplate;
-    }
+    private static final int SEQUENCE_WARN_THRESHOLD = 90000;
 
     /**
-     * 生成并返回当天的下一个自增单号。
-     *
-     * @param prefix 订单号前缀
-     * @return String 当天的下一个单号(格式:yyyyMMdd0001)
+     * 机器ID(用于多实例部署时区分不同节点)
+     * 可通过配置中心或启动参数动态获取,这里使用固定值
      */
-    public String generateNextOrderNumber(String prefix) {
-        LocalDate today = LocalDate.now();
-        String currentDateStr = today.format(DATE_FORMATTER);
-
-        String key = MassageConstants.NUMBER + prefix + currentDateStr;
-        Long incrementResult = incrementByScript(key);
-        int sequence = incrementResult.intValue();
-        return prefix + currentDateStr + String.format("%05d", sequence);
-    }
+    private static final String MACHINE_ID = "01";
 
+    /**
+     * 每个前缀的序列号缓存(key 格式:prefix + yyyyMMddHHmmss)
+     */
+    private final ConcurrentHashMap<String, AtomicLong> sequenceCache = new ConcurrentHashMap<>();
 
     /**
-     * 使用Lua脚本原子性递增Redis键值
+     * 生成并返回当天的下一个自增单号。
+     * 不依赖 Redis,使用本地原子序列生成器。
      *
-     * @param key Redis键名
-     * @return Long递增后的键值
+     * @param prefix 订单号前缀
+     * @return String 订单号(格式:prefix + yyyyMMddHHmmss + machineId + 5位序列号)
      */
-    public Long incrementByScript(String key) {
-        log.info("key的值:{}", key);
-        if (key == null || key.trim().isEmpty()) {
-            throw new IllegalArgumentException("Key cannot be null or empty");
+    public String generateNextOrderNumber(String prefix) {
+        LocalDateTime now = LocalDateTime.now();
+        String timeStr = now.format(DATE_FORMATTER2);
+        String cacheKey = prefix + timeStr;
+
+        // 获取或创建当前秒的序列号
+        AtomicLong sequence = sequenceCache.computeIfAbsent(cacheKey, k -> {
+            log.debug("创建新的序列号缓存,key:{}", cacheKey);
+            return new AtomicLong(0);
+        });
+
+        // 原子递增并获取序列号
+        long newSequence = sequence.incrementAndGet();
+
+        // 容量预警:序列号超过警戒阈值时记录警告日志
+        if (newSequence > SEQUENCE_WARN_THRESHOLD) {
+            log.warn("订单号序列号接近上限,当前值:{},前缀:{},时间:{}", newSequence, prefix, timeStr);
         }
 
-        // 假设 "script" 是一个从外部文件或安全的静态资源加载的Lua脚本
-        String script = "return redis.call('INCR', KEYS[1])";
-        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
-
-        try {
-            Long execute = stringRedisTemplate.execute(redisScript, Collections.singletonList(key));
-            log.info("递增后的键值:{}", execute);
-            stringRedisTemplate.expireAt(key, LocalDateTime.of(LocalDate.now(), LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
-            return execute;
-        } catch (Exception e) {
-            // 根据业务需求,这里可以选择抛出运行时异常或者返回特定的错误码
-            throw new RuntimeException("Failed to execute Redis script", e);
+        // 序列号溢出检查
+        if (newSequence > SEQUENCE_MAX_VALUE) {
+            log.error("订单号序列号超过上限,当前值:{},前缀:{},时间:{}", newSequence, prefix, timeStr);
+            throw new IllegalStateException("当前秒内订单号序列号超出系统限制,请稍后重试");
         }
-    }
 
+        // 格式:前缀 + 时间串 + 机器ID + 5位序列号
+        return prefix + timeStr + MACHINE_ID + String.format("%05d", newSequence);
+    }
 
     public static void main(String[] args) {
-        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-        LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
-        LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
-
-        Instant instant = todayStart.atZone(ZoneId.systemDefault()).toInstant();
-
-        System.out.println("当前时间,当天的开始时间(日期+时分秒):" + todayStart.format(dtf));
-        System.out.println("当前时间,当天的结束时间(日期+时分秒):" + todayEnd.format(dtf));
-        System.out.println("当前的时间(只有日期)" + LocalDate.now());
-        System.out.println(instant);
+        OrderNumberGenerator generator = new OrderNumberGenerator();
+
+        // 测试生成 10 个订单号
+        for (int i = 0; i < 10; i++) {
+            System.out.println(generator.generateNextOrderNumber(KEY_PREFIX_PRODUCTORDER));
+            if (i < 9) {
+                try {
+                    // 模拟不同时间的订单
+                    Thread.sleep(10);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
     }
-
-
 }