package com.tourguide.common.huifu; import java.security.SecureRandom; import java.util.concurrent.atomic.AtomicLong; /** * 请求流水号生成工具类 * *

用于生成32位唯一请求流水号,格式:[前缀2位][日期8位][时间6位][毫秒3位][序列号13位]

*

示例:rQ202511141540078404299875651

* *

特性:

* * * @author harley * @since 2025-11-14 */ public class RequestSeqUtil { /** * 默认前缀 */ private static final String DEFAULT_PREFIX = "RQ"; /** * 序列号生成器,保证线程安全 */ private static final AtomicLong SEQUENCE_GENERATOR = new AtomicLong(0); /** * 安全随机数生成器 */ private static final SecureRandom SECURE_RANDOM = new SecureRandom(); /** * 流水号总长度 */ private static final int TOTAL_LENGTH = 32; /** * 前缀长度 */ private static final int PREFIX_LENGTH = 2; /** * 日期长度 (yyyyMMdd) */ private static final int DATE_LENGTH = 8; /** * 时间长度 (HHmmss) */ private static final int TIME_LENGTH = 6; /** * 毫秒长度 */ private static final int MILLIS_LENGTH = 3; /** * 序列号长度 */ private static final int SEQUENCE_LENGTH = 13; /** * 私有构造函数,防止实例化 */ private RequestSeqUtil() { throw new UnsupportedOperationException("工具类不允许实例化"); } /** * 生成默认前缀的32位请求流水号 * * @return 32位请求流水号,格式:RQ + 时间信息 + 序列号 */ public static String generate() { return generate(DEFAULT_PREFIX); } /** * 生成指定前缀的32位请求流水号 * *

流水号格式:[前缀2位][日期8位][时间6位][毫秒3位][序列号13位]

*

示例:rQ202511141540078404299875651

* * @param prefix 自定义前缀,建议2位字符 * @return 32位请求流水号 * @throws IllegalArgumentException 当前缀为空或长度不符合要求时抛出 */ public static String generate(String prefix) { // 参数校验 if (prefix == null || prefix.isEmpty()) { throw new IllegalArgumentException("前缀不能为空"); } if (prefix.length() != PREFIX_LENGTH) { throw new IllegalArgumentException("前缀长度必须为" + PREFIX_LENGTH + "位"); } long currentTime = System.currentTimeMillis(); // 构建流水号各个部分 String datePart = DateUtil.format(new java.util.Date(currentTime), "yyyyMMdd"); String timePart = DateUtil.format(new java.util.Date(currentTime), "HHmmss"); String millisPart = String.format("%03d", currentTime % 1000); // 生成序列号(时间戳后7位 + 随机数6位) long sequence = SEQUENCE_GENERATOR.incrementAndGet(); String sequencePart = String.format("%013d", Math.abs(sequence % 1000000000000L)); // 组装完整的流水号 StringBuilder seqBuilder = new StringBuilder(TOTAL_LENGTH); seqBuilder.append(prefix) .append(datePart) .append(timePart) .append(millisPart) .append(sequencePart); return seqBuilder.toString(); } /** * 生成带业务标识的流水号 * *

常用的业务前缀:

* * * @param businessCode 业务代码前缀 * @return 32位业务流水号 */ public static String generateForBusiness(String businessCode) { return generate(businessCode); } /** * 生成汇付相关的请求流水号 * * @return 以"rQ"为前缀的32位流水号 */ public static String generateForHuiFu() { return generate("rQ"); } /** * 验证流水号格式是否正确 * * @param sequence 要验证的流水号 * @return 验证结果 */ public static boolean isValid(String sequence) { if (sequence == null || sequence.length() != TOTAL_LENGTH) { return false; } // 验证时间部分格式(第3-16位应该是数字) String timePart = sequence.substring(2, 16); if (!timePart.matches("\\d+")) { return false; } // 验证最后13位序列号是否为数字 String sequencePart = sequence.substring(19); if (!sequencePart.matches("\\d+")) { return false; } return true; } /** * 从流水号中提取前缀 * * @param sequence 流水号 * @return 流水号前缀 */ public static String extractPrefix(String sequence) { if (sequence == null || sequence.length() < PREFIX_LENGTH) { return null; } return sequence.substring(0, PREFIX_LENGTH); } /** * 从流水号中提取时间信息 * * @param sequence 流水号 * @return 格式化的时间字符串,格式:yyyy-MM-dd HH:mm:ss.SSS */ public static String extractDateTime(String sequence) { if (!isValid(sequence)) { return null; } String dateStr = sequence.substring(2, 10); // yyyyMMdd String timeStr = sequence.substring(10, 16); // HHmmss String millisStr = sequence.substring(16, 19); // 毫秒 return dateStr.substring(0, 4) + "-" + dateStr.substring(4, 6) + "-" + dateStr.substring(6, 8) + " " + timeStr.substring(0, 2) + ":" + timeStr.substring(2, 4) + ":" + timeStr.substring(4, 6) + "." + millisStr; } }