package com.doumee.core.utils; import java.security.SecureRandom; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.List; import java.util.ArrayList; /** * 不重复核销码生成器 * 用于生成唯一且易于识别的核销码 */ public class UniqueVerificationCodeGenerator { private final Set usedCodes; private final int codeLength; private final String prefix; private final String suffix; private final Random random; private final boolean useSecureRandom; private final CodeFormat format; // 核销码格式枚举 public enum CodeFormat { NUMERIC_ONLY("0123456789"), ALPHANUMERIC("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"), LETTERS_ONLY("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); private final String characters; CodeFormat(String characters) { this.characters = characters; } public String getCharacters() { return characters; } } /** * 构造函数 - 默认数字格式 * @param codeLength 核销码长度 */ public UniqueVerificationCodeGenerator(int codeLength) { this(codeLength, "", "", CodeFormat.NUMERIC_ONLY, false); } /** * 构造函数 - 完整配置 * @param codeLength 核销码长度 * @param prefix 前缀 * @param suffix 后缀 * @param format 核销码格式 * @param useSecureRandom 是否使用安全随机数生成器 */ public UniqueVerificationCodeGenerator(int codeLength, String prefix, String suffix, CodeFormat format, boolean useSecureRandom) { if (codeLength < 1) { throw new IllegalArgumentException("核销码长度必须大于0"); } this.codeLength = codeLength; this.prefix = prefix != null ? prefix : ""; this.suffix = suffix != null ? suffix : ""; this.format = format; this.usedCodes = new HashSet<>(); this.useSecureRandom = useSecureRandom; this.random = useSecureRandom ? new SecureRandom() : ThreadLocalRandom.current(); } /** * 生成下一个不重复的核销码 * @return 核销码,如果没有可用组合则返回null */ public String nextUniqueCode() { int maxAttempts = 1000; int attempts = 0; while (attempts < maxAttempts) { String code = generateCode(); if (!usedCodes.contains(code)) { usedCodes.add(code); return code; } attempts++; } // 如果随机生成失败,尝试顺序生成 return generateSequentialCode(); } /** * 生成指定数量的不重复核销码 * @param count 数量 * @return 核销码列表 */ public List nextUniqueCodes(int count) { List result = new ArrayList<>(); for (int i = 0; i < count; i++) { String code = nextUniqueCode(); if (code == null) { break; // 没有更多可用组合 } result.add(code); } return result; } /** * 生成单个核销码(不检查重复) * @return 核销码 */ private String generateCode() { StringBuilder sb = new StringBuilder(); sb.append(prefix); String characters = format.getCharacters(); for (int i = 0; i < codeLength; i++) { int index = random.nextInt(characters.length()); sb.append(characters.charAt(index)); } sb.append(suffix); return sb.toString(); } /** * 顺序生成核销码(作为备选方案) */ private String generateSequentialCode() { String characters = format.getCharacters(); long totalCombinations = (long) Math.pow(characters.length(), codeLength); if (usedCodes.size() >= totalCombinations) { return null; // 所有可能组合都已使用 } // 寻找下一个未使用的组合 for (long i = 0; i < totalCombinations; i++) { String code = convertToCode(i, characters); String fullCode = prefix + code + suffix; if (!usedCodes.contains(fullCode)) { usedCodes.add(fullCode); return fullCode; } } return null; } /** * 将数字转换为指定字符集的编码 */ private String convertToCode(long number, String characters) { StringBuilder sb = new StringBuilder(); long base = characters.length(); for (int i = 0; i < codeLength; i++) { sb.append(characters.charAt((int) (number % base))); number /= base; } return sb.reverse().toString(); } /** * 重置生成器 */ public void reset() { usedCodes.clear(); } /** * 获取已使用的核销码数量 */ public int getUsedCount() { return usedCodes.size(); } /** * 检查是否还有可用的核销码组合 */ public boolean hasMoreCodes() { String characters = format.getCharacters(); long totalCombinations = (long) Math.pow(characters.length(), codeLength); return usedCodes.size() < totalCombinations; } // ==================== 静态便捷方法 ==================== /** * 生成数字型核销码(默认6位) */ public static String generateNumericCode() { return generateNumericCode(6); } /** * 生成指定长度的数字型核销码 */ public static String generateNumericCode(int length) { UniqueVerificationCodeGenerator generator = new UniqueVerificationCodeGenerator(length, "", "", CodeFormat.NUMERIC_ONLY, true); return generator.nextUniqueCode(); } /** * 批量生成数字型核销码 */ public static List generateNumericCodes(int length, int count) { UniqueVerificationCodeGenerator generator = new UniqueVerificationCodeGenerator(length, "", "", CodeFormat.NUMERIC_ONLY, true); return generator.nextUniqueCodes(count); } /** * 生成带前缀的核销码 */ public static String generatePrefixedCode(String prefix, int length) { UniqueVerificationCodeGenerator generator = new UniqueVerificationCodeGenerator(length, prefix, "", CodeFormat.NUMERIC_ONLY, true); return generator.nextUniqueCode(); } /** * 生成字母数字混合核销码 */ public static String generateAlphanumericCode(int length) { UniqueVerificationCodeGenerator generator = new UniqueVerificationCodeGenerator(length, "", "", CodeFormat.ALPHANUMERIC, true); return generator.nextUniqueCode(); } /** * 生成订单号格式的核销码(如:ORD20241201XXXXXX) */ public static String generateOrderCode(String prefix) { String datePart = java.time.LocalDateTime.now().format( java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd")); UniqueVerificationCodeGenerator generator = new UniqueVerificationCodeGenerator(6, prefix + datePart, "", CodeFormat.NUMERIC_ONLY, true); return generator.nextUniqueCode(); } /** * 生成优惠券码格式(如:COUPON-XXXXXX) */ public static String generateCouponCode() { UniqueVerificationCodeGenerator generator = new UniqueVerificationCodeGenerator(8, "COUPON-", "", CodeFormat.ALPHANUMERIC, true); return generator.nextUniqueCode(); } }