| | |
| | | package cn.iocoder.yudao.framework.common.util.number; |
| | | |
| | | import java.util.concurrent.ThreadLocalRandom; |
| | | |
| | | /** |
| | | * 业务编码(单据号 / 主数据编码)生成的公共约定。 |
| | | * |
| | | * <p>需求:系统中所有自动生成的业务编码统一为「16 位纯数字」,且全系统唯一(任意两条编码都不重复)。 |
| | | * <p>需求:系统中所有自动生成的业务编码统一为「16 位纯数字」,且**随机生成、不使用自增/顺序**, |
| | | * 同时保证全系统唯一(任意两条编码都不重复)。 |
| | | * |
| | | * <p>实现约定:全系统共用同一个 Redis 自增 KEY({@link #GLOBAL_SEQ_KEY}), |
| | | * 每次生成时对其执行 INCR 得到单调递增的序列号,再左补 '0' 到 {@link #CODE_LENGTH} 位。 |
| | | * 由于所有编码都来自同一条递增序列,故天然保证「全系统唯一」。 |
| | | * |
| | | * <p>注意: |
| | | * <p>实现约定: |
| | | * <ul> |
| | | * <li>该 KEY 不设置过期时间,避免过期归零导致重号;</li> |
| | | * <li>Redis 若发生数据丢失 / 清空,需要从数据库现有编码的最大值恢复计数器,否则可能重号。</li> |
| | | * <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。 |
| | | * |
| | | * <p>所有模块(erp / crm / trade / aftersales / hrm / bpm / mes)共用此 KEY, |
| | | * 以保证「全系统唯一」。 |
| | | * 已使用业务编码的 Redis 集合 KEY,用于随机编码查重,保证全系统唯一。 |
| | | */ |
| | | public static final String GLOBAL_SEQ_KEY = "biz:seq_no:global"; |
| | | public static final String USED_SET_KEY = "biz:code:used"; |
| | | |
| | | /** |
| | | * 业务编码的总长度:16 位 |
| | |
| | | public static final int CODE_LENGTH = 16; |
| | | |
| | | /** |
| | | * 将自增序列号格式化为 16 位纯数字编码(左侧补 '0') |
| | | * |
| | | * @param seq 自增序列号 |
| | | * @return 16 位纯数字编码 |
| | | * 随机编码的空间上限(不含):10^16 |
| | | */ |
| | | public static String format(Long seq) { |
| | | return String.format("%0" + CODE_LENGTH + "d", seq); |
| | | 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)); |
| | | } |
| | | |
| | | } |