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