package com.tourguide.common.huifu;
import java.security.SecureRandom;
import java.util.concurrent.atomic.AtomicLong;
/**
* 请求流水号生成工具类
*
*
用于生成32位唯一请求流水号,格式:[前缀2位][日期8位][时间6位][毫秒3位][序列号13位]
* 示例:rQ202511141540078404299875651
*
* 特性:
*
* - 线程安全:使用AtomicLong保证高并发环境下的唯一性
* - 高性能:避免重复创建对象,优化内存使用
* - 可配置:支持自定义前缀适应不同业务场景
* - 高可靠:时间戳+随机数+序列号三重保障唯一性
*
*
* @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();
}
/**
* 生成带业务标识的流水号
*
* 常用的业务前缀:
*
* - rQ - 汇付请求
* - OR - 订单业务
* - PY - 支付业务
* - RF - 退款业务
*
*
* @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;
}
}