package org.dromara.common.redis.utils; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DatePattern; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.redisson.api.RIdGenerator; import org.redisson.api.RedissonClient; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; /** * 发号器工具类 * * @author 秋辞未寒 * @date 2024-12-10 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SequenceUtils { /** * 默认初始值 */ public static final long DEFAULT_INIT_VALUE = 1L; /** * 默认步长 */ public static final long DEFAULT_STEP_VALUE = 1L; /** * 默认过期时间-天 */ public static final Duration DEFAULT_EXPIRE_TIME_DAY = Duration.ofDays(1); /** * 默认过期时间-分钟 */ public static final Duration DEFAULT_EXPIRE_TIME_MINUTE = Duration.ofMinutes(1); /** * 默认最小ID容量位数 - 6位数(即至少可以生成的ID为999999个) */ public static final int DEFAULT_MIN_ID_CAPACITY_BITS = 6; /** * 获取Redisson客户端实例 */ private static final RedissonClient REDISSON_CLIENT = SpringUtils.getBean(RedissonClient.class); /** * 获取ID生成器 * * @param key 业务key * @param expireTime 过期时间 * @param initValue ID初始值 * @param stepValue ID步长 * @return ID生成器 */ public static RIdGenerator getIdGenerator(String key, Duration expireTime, long initValue, long stepValue) { RIdGenerator idGenerator = REDISSON_CLIENT.getIdGenerator(key); // 初始值和步长不能小于等于0 initValue = initValue <= 0 ? DEFAULT_INIT_VALUE : initValue; stepValue = stepValue <= 0 ? DEFAULT_STEP_VALUE : stepValue; // 设置初始值和步长 idGenerator.tryInit(initValue, stepValue); // 设置过期时间 idGenerator.expire(expireTime); return idGenerator; } /** * 获取ID生成器 * * @param key 业务key * @param expireTime 过期时间 * @return ID生成器 */ public static RIdGenerator getIdGenerator(String key, Duration expireTime) { return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE); } /** * 获取指定业务key的唯一id * * @param key 业务key * @param expireTime 过期时间 * @param initValue ID初始值 * @param stepValue ID步长 * @return 唯一id */ public static long getNextId(String key, Duration expireTime, long initValue, long stepValue) { return getIdGenerator(key, expireTime, initValue, stepValue).nextId(); } /** * 获取指定业务key的唯一id (ID初始值=1,ID步长=1) * * @param key 业务key * @param expireTime 过期时间 * @return 唯一id */ public static long getNextId(String key, Duration expireTime) { return getIdGenerator(key, expireTime).nextId(); } /** * 获取指定业务key的唯一id字符串 * * @param key 业务key * @param expireTime 过期时间 * @param initValue ID初始值 * @param stepValue ID步长 * @return 唯一id */ public static String getNextIdString(String key, Duration expireTime, long initValue, long stepValue) { return Convert.toStr(getNextId(key, expireTime, initValue, stepValue)); } /** * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1) * * @param key 业务key * @param expireTime 过期时间 * @return 唯一id */ public static String getNextIdString(String key, Duration expireTime) { return Convert.toStr(getNextId(key, expireTime)); } /** * 获取指定业务key的唯一id字符串 (ID初始值=1,ID步长=1),不足位数自动补零 * * @param key 业务key * @param expireTime 过期时间 * @param width 位数,不足左补0 * @return 补零后的唯一id字符串 */ public static String getPaddedNextIdString(String key, Duration expireTime, Integer width) { return StringUtils.leftPad(getNextIdString(key, expireTime), width, '0'); } /** * 获取 yyyyMMdd 格式的唯一id * * @return 唯一id * @deprecated 请使用 {@link #getDateId(String)} 或 {@link #getDateId(String, boolean)}、{@link #getDateId(String, boolean, int)},确保不同业务的ID连续性 */ @Deprecated public static String getDateId() { return getDateId(""); } /** * 获取 prefix + yyyyMMdd 格式的唯一id * * @param prefix 业务前缀 * @return 唯一id */ public static String getDateId(String prefix) { return getDateId(prefix, true); } /** * 获取 prefix + yyyyMMdd 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @return 唯一id */ public static String getDateId(String prefix, boolean isWithPrefix) { return getDateId(prefix, isWithPrefix, -1); } /** * 获取 prefix + yyyyMMdd 格式的唯一id (启用ID补位,补位长度 = {@link #DEFAULT_MIN_ID_CAPACITY_BITS})}) * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @return 唯一id */ public static String getPaddedDateId(String prefix, boolean isWithPrefix) { return getDateId(prefix, isWithPrefix, DEFAULT_MIN_ID_CAPACITY_BITS); } /** * 获取 prefix + yyyyMMdd 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @return 唯一id */ public static String getDateId(String prefix, boolean isWithPrefix, int minIdCapacityBits) { return getDateId(prefix, isWithPrefix, minIdCapacityBits, LocalDate.now()); } /** * 获取 prefix + yyyyMMdd 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @param time 时间 * @return 唯一id */ public static String getDateId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDate time) { return getDateId(prefix, isWithPrefix, minIdCapacityBits, time, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE); } /** * 获取 prefix + yyyyMMdd 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @param time 时间 * @param initValue ID初始值 * @param stepValue ID步长 * @return 唯一id */ public static String getDateId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDate time, long initValue, long stepValue) { return getDatePatternId(prefix, isWithPrefix, minIdCapacityBits, time, DatePattern.PURE_DATE_FORMATTER, DEFAULT_EXPIRE_TIME_DAY, initValue, stepValue); } /** * 获取 yyyyMMddHHmmss 格式的唯一id * * @return 唯一id * @deprecated 请使用 {@link #getDateTimeId(String)} 或 {@link #getDateTimeId(String, boolean)}、{@link #getDateTimeId(String, boolean, int)},确保不同业务的ID连续性 */ @Deprecated public static String getDateTimeId() { return getDateTimeId("", false); } /** * 获取 prefix + yyyyMMddHHmmss 格式的唯一id * * @param prefix 业务前缀 * @return 唯一id */ public static String getDateTimeId(String prefix) { return getDateTimeId(prefix, true); } /** * 获取 prefix + yyyyMMddHHmmss 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @return 唯一id */ public static String getDateTimeId(String prefix, boolean isWithPrefix) { return getDateTimeId(prefix, isWithPrefix, -1); } /** * 获取 prefix + yyyyMMddHHmmss 格式的唯一id (启用ID补位,补位长度 = {@link #DEFAULT_MIN_ID_CAPACITY_BITS})}) * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @return 唯一id */ public static String getPaddedDateTimeId(String prefix, boolean isWithPrefix) { return getDateTimeId(prefix, isWithPrefix, DEFAULT_MIN_ID_CAPACITY_BITS); } /** * 获取 prefix + yyyyMMddHHmmss 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @return 唯一id */ public static String getDateTimeId(String prefix, boolean isWithPrefix, int minIdCapacityBits) { return getDateTimeId(prefix, isWithPrefix, minIdCapacityBits, LocalDateTime.now()); } /** * 获取 prefix + yyyyMMddHHmmss 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @param time 时间 * @return 唯一id */ public static String getDateTimeId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDateTime time) { return getDateTimeId(prefix, isWithPrefix, minIdCapacityBits, time, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE); } /** * 获取 prefix + yyyyMMddHHmmss 格式的唯一id * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @param initValue ID初始值 * @param stepValue ID步长 * @return 唯一id */ public static String getDateTimeId(String prefix, boolean isWithPrefix, int minIdCapacityBits, LocalDateTime time, long initValue, long stepValue) { return getDatePatternId(prefix, isWithPrefix, minIdCapacityBits, time, DatePattern.PURE_DATETIME_FORMATTER, DEFAULT_EXPIRE_TIME_MINUTE, initValue, stepValue); } /** * 获取指定业务key的指定时间格式的ID * * @param prefix 业务前缀 * @param isWithPrefix id是否携带业务前缀 * @param minIdCapacityBits 最小ID容量位数,小于该位数的ID,左补0(小于等于0表示不启用补位) * @param temporalAccessor 时间访问器 * @param timeFormatter 时间格式 * @param expireTime 过期时间 * @param initValue ID初始值 * @param stepValue ID步长 * @return 唯一id */ private static String getDatePatternId(String prefix, boolean isWithPrefix, int minIdCapacityBits, TemporalAccessor temporalAccessor, DateTimeFormatter timeFormatter, Duration expireTime, long initValue, long stepValue) { // 时间前缀 String timePrefix = timeFormatter.format(temporalAccessor); // 业务前缀 + 时间前缀 构建 prefixKey String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), timePrefix); // 获取id,例 -> 1 String nextId = getNextIdString(prefixKey, expireTime, initValue, stepValue); // minIdCapacityBits 大于0,且 nextId 的长度小于 minIdCapacityBits,则左补0 if (minIdCapacityBits > 0 && nextId.length() < minIdCapacityBits) { nextId = StringUtils.leftPad(nextId, minIdCapacityBits, '0'); } // 是否携带业务前缀 if (isWithPrefix) { // 例 -> P202507031 // 其中 P 为业务前缀,202507031 为 yyyyMMdd 格式时间, 1 为nextId return StringUtils.format("{}{}", prefixKey, nextId); } // 例 -> 202507031 // 其中 202507031 为 yyyyMMdd 格式时间, 1 为nextId return StringUtils.format("{}{}", timePrefix, nextId); } }