| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- package com.sqx.modules.utils;
- /**
- * 推荐码生成工具类
- */
- public class PromotionCodeUtil {
- /** 自定义进制字符集(避免使用易混淆的字符) */
- private static final char[] r = new char[]{ 'M', 'J', 'U', 'D', 'Z', 'X', '9', 'C', '7', 'P',
- 'E', '8', '6', 'B', 'G', 'H', 'S', '2', '5', 'F',
- 'R', '4', 'Q', 'W', 'K', '3', 'V', 'Y', 'T', 'N'};
- /** 进制长度 */
- private static final int binLen = r.length;
- /** 推荐码长度 */
- private static final int codeLength = 6; // 8位长度
- /** 用于生成唯一ID的锁对象 */
- private static final Object lock = new Object();
- /** 最后生成的时间戳,用于防止短时间内生成重复码 */
- private static long lastTimestamp = -1L;
- /** 序列号,用于同一毫秒内生成多个码时保证唯一性 */
- private static long sequence = 0L;
- /** 序列号最大值 */
- private static final long MAX_SEQUENCE = 99999L;
- /** 节点ID(在集群环境中非常重要) */
- private static final int NODE_ID;
- // 静态初始化块,初始化节点ID
- static {
- int nodeId1;
- // 优先从环境变量获取节点ID
- String nodeIdStr = System.getenv("NODE_ID");
- if (nodeIdStr != null && !nodeIdStr.isEmpty()) {
- try {
- nodeId1 = Integer.parseInt(nodeIdStr) & 0x3F; // 限制在0-63之间
- } catch (NumberFormatException e) {
- nodeId1 = generateRandomNodeId();
- }
- } else {
- // 如果没有配置,生成随机节点ID
- nodeId1 = generateRandomNodeId();
- }
- NODE_ID = nodeId1;
- System.out.println("当前服务器节点ID: " + NODE_ID);
- }
- /**
- * 生成随机节点ID(当环境变量未配置时使用)
- */
- private static int generateRandomNodeId() {
- // 使用服务器信息和当前时间生成相对唯一的节点ID
- try {
- String hostName = java.net.InetAddress.getLocalHost().getHostName();
- int hashCode = hostName.hashCode();
- // 结合当前时间戳的低8位,增加随机性
- long timestamp = System.currentTimeMillis() & 0xFF;
- return (Math.abs(hashCode) ^ (int)timestamp) & 0x3F; // 限制在0-63之间
- } catch (Exception e) {
- // 如果获取主机名失败,使用纯随机数
- return (int)(Math.random() * 64);
- }
- }
- /**
- * 生成唯一推荐码(支持集群环境)
- * @return 唯一的推荐码字符串
- */
- public static String generatePromotionCode() {
- // 生成基础唯一数值
- long uniqueValue = generateUniqueValue();
- // 使用进制转换算法
- StringBuilder sb = new StringBuilder();
- while (uniqueValue > 0) {
- int ind = (int) (uniqueValue % binLen);
- sb.append(r[ind]);
- uniqueValue = uniqueValue / binLen;
- }
- // 确保推荐码长度
- String code = sb.toString();
- if (code.length() < codeLength) {
- // 使用随机字符填充前面
- StringBuilder padding = new StringBuilder();
- int paddingLength = codeLength - code.length();
- for (int i = 0; i < paddingLength; i++) {
- long randomSeed = System.nanoTime() + sequence;
- int randomIndex = (int) (Math.abs(randomSeed) % binLen);
- padding.append(r[randomIndex]);
- }
- code = padding.append(sb).toString();
- } else if (code.length() > codeLength) {
- // 如果超过长度,截取前codeLength位
- code = code.substring(0, codeLength);
- }
- return code;
- }
- /**
- * 生成唯一数值,结合时间戳、节点ID和序列号
- * @return 唯一数值
- */
- private static long generateUniqueValue() {
- synchronized (lock) {
- long timestamp = System.currentTimeMillis();
- // 如果是同一毫秒,序列号加1
- if (timestamp == lastTimestamp) {
- sequence++;
- // 防止序列号溢出
- if (sequence > MAX_SEQUENCE) {
- // 等待下一毫秒
- while (System.currentTimeMillis() <= timestamp) {
- Thread.yield();
- }
- timestamp = System.currentTimeMillis();
- sequence = 0L;
- }
- } else {
- // 不同毫秒,序列号重置
- sequence = 0L;
- }
- lastTimestamp = timestamp;
- // 将节点ID纳入唯一性计算
- // 格式:时间戳(41位) + 节点ID(6位) + 序列号(17位)
- long result = (timestamp << 23);
- result |= ((long)NODE_ID << 17);
- result |= (sequence & 0x1FFFF); // 17位序列号
- return Math.abs(result);
- }
- }
- }
|