package com.ylx.massage.utils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; 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_FORMATTER2 = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); private static final int SEQUENCE_MAX_VALUE = 99999; private static final int SEQUENCE_WARN_THRESHOLD = 90000; /** * 机器ID(用于多实例部署时区分不同节点) * 可通过配置中心或启动参数动态获取,这里使用固定值 */ private static final String MACHINE_ID = "01"; /** * 每个前缀的序列号缓存(key 格式:prefix + yyyyMMddHHmmss) */ private final ConcurrentHashMap sequenceCache = new ConcurrentHashMap<>(); /** * 生成并返回当天的下一个自增单号。 * 不依赖 Redis,使用本地原子序列生成器。 * * @param prefix 订单号前缀 * @return String 订单号(格式:prefix + yyyyMMddHHmmss + machineId + 5位序列号) */ 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); } // 序列号溢出检查 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) { 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(); } } } } }