diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java index 657dbbc07..5258c8558 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java @@ -1,7 +1,6 @@ package org.dromara.common.redis.utils; import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateUtil; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.dromara.common.core.utils.SpringUtils; @@ -10,6 +9,10 @@ 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; /** * 发号器工具类 @@ -23,12 +26,12 @@ public class SequenceUtils { /** * 默认初始值 */ - public static final Long DEFAULT_INIT_VALUE = 1L; + public static final long DEFAULT_INIT_VALUE = 1L; /** * 默认步长 */ - public static final Long DEFAULT_STEP_VALUE = 1L; + public static final long DEFAULT_STEP_VALUE = 1L; /** * 默认过期时间-天 @@ -40,6 +43,11 @@ public class SequenceUtils { */ 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客户端实例 */ @@ -54,14 +62,11 @@ public class SequenceUtils { * @param stepValue ID步长 * @return ID生成器 */ - private static RIdGenerator getIdGenerator(String key, Duration expireTime, Long initValue, Long stepValue) { - if (initValue == null || initValue <= 0) { - initValue = DEFAULT_INIT_VALUE; - } - if (stepValue == null || stepValue <= 0) { - stepValue = DEFAULT_STEP_VALUE; - } + 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); // 设置过期时间 @@ -69,6 +74,17 @@ public class SequenceUtils { 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 * @@ -78,10 +94,21 @@ public class SequenceUtils { * @param stepValue ID步长 * @return 唯一id */ - public static long nextId(String key, Duration expireTime, Long initValue, Long stepValue) { + 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字符串 * @@ -91,19 +118,8 @@ public class SequenceUtils { * @param stepValue ID步长 * @return 唯一id */ - public static String nextIdStr(String key, Duration expireTime, Long initValue, Long stepValue) { - return String.valueOf(nextId(key, expireTime, initValue, stepValue)); - } - - /** - * 获取指定业务key的唯一id (ID初始值=1,ID步长=1) - * - * @param key 业务key - * @param expireTime 过期时间 - * @return 唯一id - */ - public static long nextId(String key, Duration expireTime) { - return getIdGenerator(key, expireTime, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); + public static String getNextIdString(String key, Duration expireTime, long initValue, long stepValue) { + return String.valueOf(getNextId(key, expireTime, initValue, stepValue)); } /** @@ -113,8 +129,8 @@ public class SequenceUtils { * @param expireTime 过期时间 * @return 唯一id */ - public static String nextIdStr(String key, Duration expireTime) { - return String.valueOf(nextId(key, expireTime)); + public static String getNextIdString(String key, Duration expireTime) { + return String.valueOf(getNextId(key, expireTime)); } /** @@ -125,56 +141,210 @@ public class SequenceUtils { * @param width 位数,不足左补0 * @return 补零后的唯一id字符串 */ - public static String nextPaddedIdStr(String key, Duration expireTime, Integer width) { - return StringUtils.leftPad(nextIdStr(key, expireTime), width, '0'); + public static String getPaddedNextIdString(String key, Duration expireTime, Integer width) { + return StringUtils.leftPad(getNextIdString(key, expireTime), width, '0'); } /** - * 获取 yyyyMMdd 开头的唯一id + * 获取 yyyyMMdd 格式的唯一id * * @return 唯一id + * @deprecated 请使用 {@link #getDateId(String)} 或 {@link #getDateId(String, boolean)}、{@link #getDateId(String, boolean, int)},确保不同业务的ID连续性 */ - public static String nextIdDate() { - return nextIdDate(""); + @Deprecated + public static String getDateId() { + return getDateId(""); } /** - * 获取 prefix + yyyyMMdd 开头的唯一id + * 获取 prefix + yyyyMMdd 格式的唯一id * * @param prefix 业务前缀 * @return 唯一id */ - public static String nextIdDate(String prefix) { - // 前缀+日期 构建 prefixKey - String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER)); - // 获取下一个id - long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_DAY, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); - // 返回完整id - return StringUtils.format("{}{}", prefixKey, nextId); + public static String getDateId(String prefix) { + return getDateId(prefix, true); } /** - * 获取 yyyyMMddHHmmss 开头的唯一id + * 获取 prefix + yyyyMMdd 格式的唯一id * + * @param prefix 业务前缀 + * @param isWithPrefix id是否携带业务前缀 * @return 唯一id */ - public static String nextIdDateTime() { - return nextIdDateTime(""); + public static String getDateId(String prefix, boolean isWithPrefix) { + return getDateId(prefix, isWithPrefix, -1); } /** - * 获取 prefix + yyyyMMddHHmmss 开头的唯一id + * 获取 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 nextIdDateTime(String prefix) { - // 前缀+日期时间 构建 prefixKey - String prefixKey = StringUtils.format("{}{}", StringUtils.blankToDefault(prefix, ""), DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER)); - // 获取下一个id - long nextId = getIdGenerator(prefixKey, DEFAULT_EXPIRE_TIME_MINUTE, DEFAULT_INIT_VALUE, DEFAULT_STEP_VALUE).nextId(); - // 返回完整id - return StringUtils.format("{}{}", prefixKey, nextId); + 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); + } }