package com.zsElectric.boot.common.util; import lombok.Getter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.atomic.AtomicInteger; /** * 基于时间戳的4位自增序列和时间戳生成器 * 规则:同一秒内序列号自增(0001-9999),下一秒序列重置为0001 * * @author: wzq * @date: 2025/11/12 */ public class SequenceGenUtil { private static final AtomicInteger currentSequence = new AtomicInteger(0); private static volatile long lastSecond = -1L; private static final int MAX_SEQUENCE = 9999; // 时间格式化器(线程安全) private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); /** * 序列号生成结果对象 */ @Getter public static class SequenceResult { private final String sequence; // 4位序列号,如 "0001" private final String timestamp; // 时间戳,如 "20251112154530" public SequenceResult(String sequence, String timestamp) { this.sequence = sequence; this.timestamp = timestamp; } /** * 获取完整编号:时间戳 + 序列号 */ public String getFullNumber() { return timestamp + sequence; } @Override public String toString() { return "SequenceResult{sequence='" + sequence + "', timestamp='" + timestamp + "'}"; } } /** * 生成序列号和时间戳 * 使用synchronized确保线程安全 */ public static synchronized SequenceResult generate() { long currentSecond = getCurrentSecond(); String currentTimestamp = formatTimestamp(currentSecond); // 检查是否是新的一秒 if (currentSecond != lastSecond) { currentSequence.set(0); lastSecond = currentSecond; } // 获取下一个序列值 int seq = currentSequence.incrementAndGet(); // 检查序列号是否溢出 if (seq > MAX_SEQUENCE) { waitForNextSecond(currentSecond); // 递归调用,进入新秒后重新生成 return generate(); } // 格式化为4位数字并返回结果对象 String sequenceStr = String.format("%04d", seq); return new SequenceResult(sequenceStr, currentTimestamp); } /** * 仅生成4位序列号(保持原有功能) */ public static synchronized String generateSequenceOnly() { return generate().getSequence(); } /** * 生成完整编号(时间戳+序列号) */ public static synchronized String generateFullNumber() { return generate().getFullNumber(); } /** * 获取当前时间的秒级时间戳 */ private static long getCurrentSecond() { return System.currentTimeMillis() / 1000; } /** * 将秒级时间戳格式化为 yyyyMMddHHmmss */ private static String formatTimestamp(long timestampInSeconds) { LocalDateTime dateTime = LocalDateTime.ofEpochSecond(timestampInSeconds, 0, java.time.ZoneOffset.UTC); // 调整时区为系统默认时区 return dateTime.atZone(java.time.ZoneId.systemDefault()).format(TIMESTAMP_FORMATTER); } /** * 等待下一秒(当序列号溢出时调用) */ private static void waitForNextSecond(long currentSecond) { while (getCurrentSecond() <= currentSecond) { try { Thread.sleep(1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("序列生成器等待被中断", e); } } } /** * 重置生成器状态(主要用于测试) */ public static synchronized void reset() { lastSecond = -1L; currentSequence.set(0); } public static void main(String[] args) { System.out.println("=== 生成序列号和时间戳 ==="); // 生成完整的序列结果(包含序列号和时间戳) SequenceGenUtil.SequenceResult result1 = SequenceGenUtil.generate(); System.out.println("序列号: " + result1.getSequence()); // 输出: 0001 System.out.println("时间戳: " + result1.getTimestamp()); // 输出: 20251112154530 System.out.println("完整编号: " + result1.getFullNumber()); // 输出: 202511121545300001 System.out.println("完整对象: " + result1.toString()); // 同一秒内连续生成 System.out.println("\n=== 同一秒内连续生成 ==="); for (int i = 0; i < 3; i++) { SequenceGenUtil.SequenceResult result = SequenceGenUtil.generate(); System.out.println("时间戳: " + result.getTimestamp() + ", 序列号: " + result.getSequence()); } // 仅生成序列号(保持原有功能) System.out.println("\n=== 仅生成序列号 ==="); String sequenceOnly = SequenceGenUtil.generateSequenceOnly(); System.out.println("仅序列号: " + sequenceOnly); // 生成完整编号 System.out.println("\n=== 生成完整编号 ==="); String fullNumber = SequenceGenUtil.generateFullNumber(); System.out.println("完整编号: " + fullNumber); // 模拟跨秒生成 System.out.println("\n=== 模拟跨秒生成 ==="); SequenceGenUtil.SequenceResult beforeSleep = SequenceGenUtil.generate(); System.out.println("休眠前: " + beforeSleep.getTimestamp() + " - " + beforeSleep.getSequence()); try { Thread.sleep(1000); // 等待1秒 } catch (InterruptedException e) { e.printStackTrace(); } SequenceGenUtil.SequenceResult afterSleep = SequenceGenUtil.generate(); System.out.println("休眠后: " + afterSleep.getTimestamp() + " - " + afterSleep.getSequence()); } }