tuqin
22 小时以前 0ddbb4ec727ffce844ef672107e3410d84b1f11a
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
package cn.iocoder.yudao.framework.common.util.number;
 
import java.util.concurrent.ThreadLocalRandom;
 
/**
 * 业务编码(单据号 / 主数据编码)生成的公共约定。
 *
 * <p>需求:系统中所有自动生成的业务编码统一为「16 位纯数字」,且**随机生成、不使用自增/顺序**,
 * 同时保证全系统唯一(任意两条编码都不重复)。
 *
 * <p>实现约定:
 * <ul>
 *     <li>编码 = 随机 16 位数字(范围 0 ~ 10^16-1,左侧补 '0');</li>
 *     <li>唯一性由 Redis 集合 {@link #USED_SET_KEY} 查重保证:随机生成后写入集合,
 *         写成功(返回 1)说明没用过才采用,否则重试;</li>
 *     <li>各生成点:见 {@link #randomCode()} 与 {@link #MAX_RETRY}。</li>
 * </ul>
 *
 * <p>注意:Redis 集合丢失后,历史编码可能被重新随机命中;应用启动时会将库内已有编码
 * 重新载入集合(见 yudao-server 的启动兜底)。
 */
public class BizNoUtils {
 
    /**
     * 已使用业务编码的 Redis 集合 KEY,用于随机编码查重,保证全系统唯一。
     */
    public static final String USED_SET_KEY = "biz:code:used";
 
    /**
     * 业务编码的总长度:16 位
     */
    public static final int CODE_LENGTH = 16;
 
    /**
     * 随机编码的空间上限(不含):10^16
     */
    private static final long CODE_SPACE = 10_000_000_000_000_000L;
 
    /**
     * 生成随机编码时的最大重试次数(命中已用编码时重试)
     */
    public static final int MAX_RETRY = 10;
 
    /**
     * 生成一个随机 16 位纯数字编码(左侧补 '0'),**不做查重**。
     *
     * <p>调用方需自行用 {@link #USED_SET_KEY} 查重,命中则重试。
     *
     * @return 随机 16 位纯数字编码
     */
    public static String randomCode() {
        return String.format("%0" + CODE_LENGTH + "d", ThreadLocalRandom.current().nextLong(CODE_SPACE));
    }
 
}