MrShi
9 小时以前 41115bb4b6d0161ff6d2c15ef6b3d5386a14ac9c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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<String> 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<String> nextUniqueCodes(int count) {
        List<String> 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<String> 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();
    }
}