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); } } }