基於訂單生成規則,期望是以年與日+隨機串進行生成規則,因為分布式,所以對自增id場景需要,所以存在分布式自增id場景
直接上代碼
@Slf4j @Service public class CommonLocalCacheService { @Autowired private RedisTemplate redisTemplate; /** * 鎖對象 */ private static final Object LOCK = new Object(); /** * 本地緩存redis自增序列,例如:key:value=20211104:【1-10000】 */ private static final Cache<String, Optional<RedisSequence>> REDIS_SEQUENCE_CACHE = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.DAYS) .build(); /** * 獲取redis自增Key, * 分布式id生成規則: * 根據日期 : 年+月+日 + 3位隨機字符串 + 7位redis自增key * 取redis key 為 20211109,每次取10000,本地緩存下來,后續如果達到最大值,網上遞增10000 * 【1-10000】 20211109xxx0000001 */ @SneakyThrows public String generatorId() { String sequenceKey = DateUtils.parseTime(LocalDateTime.now(), DateUtils.TimeFormat.SHORT_DATE_PATTERN_NONE); StringBuilder orderId = new StringBuilder(sequenceKey); // 如果達到最大批次,換一批 synchronized (LOCK) { //緩存時長為當前時間+1天 Optional<RedisSequence> redisSequenceOptional = getLocalRedisSequence(sequenceKey, 10000, DateUtils.addDays(DateUtils.now(), 1)); // 如果緩存為空,則redis掛了,隨機字段 if (!redisSequenceOptional.isPresent()) { for (int i = 0; i < 10; i++) { orderId.append(RandomUtils.nextInt(0, 10)); } return orderId.toString(); } RedisSequence redisSequence = redisSequenceOptional.get(); if (redisSequence.getStartSequence().get() == redisSequence.getEndSequence().get()) { //如果值相等則失效,重新獲取 log.info("----------------------------invalidate---------start:{}, end:{}", redisSequence.getStartSequence(), redisSequence.getEndSequence()); REDIS_SEQUENCE_CACHE.invalidate(sequenceKey); return generatorId(); } redisSequence.getStartSequence().getAndAdd(1L); REDIS_SEQUENCE_CACHE.put(sequenceKey, Optional.of(redisSequence)); String sequence = String.format("%1$07d", redisSequence.getStartSequence().get()); //中間3位隨機數 for (int i = 0; i < 3; i++) { orderId.append(RandomUtils.nextInt(0, 10)); } orderId.append(sequence); } log.info("orderId is {}", orderId.toString()); return orderId.toString(); } /** * 從本地緩存中取redisSequence * * @param sequenceKey * @return */ private Optional<RedisSequence> getLocalRedisSequence(String sequenceKey, int increment, Date expireTime) { try { return REDIS_SEQUENCE_CACHE.get(sequenceKey, () -> { RedisAtomicLong counter = new RedisAtomicLong(RedisConstants.GOODS_GENERATOR_ORDER_ID.concat(sequenceKey), redisTemplate.getConnectionFactory()); counter.expireAt(expireTime); long end = counter.addAndGet(increment); RedisSequence redisSequence = RedisSequence.builder().startSequence(new AtomicLong(end-increment)).endSequence(new AtomicLong(end)).build(); log.info("----------------------------get---------start:{}, end:{}", redisSequence.getStartSequence(), redisSequence.getEndSequence()); return Optional.of(redisSequence); }); } catch (Exception e) { log.error("getLocalRedisSequence is errored, sequenceKey:{}, increment:{}", sequenceKey, increment, e); } return Optional.empty(); } }
//日期工具類
public class DateUtils extends org.apache.commons.lang3.time.DateUtils { private static final String YEAR_TO_SECOND_PATTERN = "yyyyMMddHHmmss"; /** * 獲取默認時間格式: yyyy-MM-dd HH:mm:ss */ private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = TimeFormat.LONG_DATE_PATTERN_LINE.formatter; private DateUtils() { //工具類不應該有可用的構造方法 } /** * 計算指定日期的生日 * * @param credentialNo 身份證號 * @param someDay 指定日期 * @return 歲數 */ public static int getAge(String credentialNo, LocalDate someDay) { LocalDate birthDate = getBirthDay(credentialNo); return Period.between(birthDate, someDay).getYears(); } /** * 計算當前年齡 * @param credentialNo 身份證號 * @return 歲數 */ public static int getAge(String credentialNo) { LocalDate birthDate = getBirthDay(credentialNo); return Period.between(birthDate, LocalDate.now()).getYears(); } /** * 計算今天的生日 * * @param credentialNo 身份證號 * @return 歲數 */ public static int getAgeToday(String credentialNo) { LocalDate birthDate = getBirthDay(credentialNo); return Period.between(birthDate, LocalDate.now()).getYears(); } /** * 計算明天的生日 * * @param credentialNo 身份證號 * @return 歲數 */ public static int getAgeTomorrow(String credentialNo) { LocalDate birthDate = getBirthDay(credentialNo); return Period.between(birthDate, LocalDate.now().plusDays(1)).getYears(); } private static LocalDate getBirthDay(String credentialNo) { String birthdayStr; if (credentialNo.length() == 15) { birthdayStr = "19" + credentialNo.substring(6, 12); } else if (credentialNo.length() == 18) { birthdayStr = credentialNo.substring(6, 14); } else { throw new IllegalArgumentException("credentialNo's length is not 15 or 18!"); } return LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd")); } public static Date now() { return new Date(); } public static Long stringToEpochSecond(String dateStr) { return parseDate(dateStr).toInstant().getEpochSecond(); } /** * 格式化日期: 精確到秒 */ public static String getCurrentTimeStr() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YEAR_TO_SECOND_PATTERN); return LocalDateTime.now().format(formatter); } /** * 格式化日期: 精確到日 */ public static String formatDateStr(Date date) { return DateFormatUtils.format(date, "yyyyMMdd"); } /** * 解析日期yyyyMMddHHmmss為LocalDateTime */ public static LocalDateTime parseLocalDateTime(String dateStr) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YEAR_TO_SECOND_PATTERN); return LocalDateTime.parse(dateStr, formatter); } /** * 解析日期yyyyMMddHHmmss為Date */ @SneakyThrows public static Date parseDate(String dateStr) { return org.apache.commons.lang3.time.DateUtils.parseDate(dateStr, YEAR_TO_SECOND_PATTERN); } /** * 格式化為db格式: 20170911000000 -> 2017-09-11 00:00:00 */ @SneakyThrows public static String formatDbDateStr(String dateTime) { Date date = org.apache.commons.lang3.time.DateUtils.parseDate(dateTime, YEAR_TO_SECOND_PATTERN); return DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss"); } public static String getYearAndMonth(Date date) { return DateFormatUtils.format(date, "yyyyMM"); } @SneakyThrows public static String formatChineseDateStr(String dateTime) { Date date = parseDate(dateTime); return DateFormatUtils.format(date, "yyyy年MM月dd日"); } public static Long utcFromDate(String dateTime, String... dateFormat) { String defaultDateFormat = YEAR_TO_SECOND_PATTERN; if (dateFormat != null && dateFormat.length > 0) { defaultDateFormat = dateFormat[0]; } DateTimeFormatter formatter = DateTimeFormatter.ofPattern(defaultDateFormat); return LocalDateTime.parse(dateTime, formatter).atZone(ZoneId.systemDefault()).toEpochSecond(); } /** * String 轉時間 * * @param timeStr * @return */ public static LocalDateTime parseTime(String timeStr) { return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER); } /** * String 轉時間 * * @param timeStr * @param format 時間格式 * @return */ public static LocalDateTime parseTime(String timeStr, TimeFormat format) { return LocalDateTime.parse(timeStr, format.formatter); } /** * 判斷日期是否今天 * @param date * @return */ public static boolean isToday(Date date){ return dateToLocalDateTime(date).isAfter(dateToLocalDateTime(localDateToDate(LocalDate.now()))); } /** * 時間轉 String * * @param time * @return */ public static String parseTime(LocalDateTime time) { return DEFAULT_DATETIME_FORMATTER.format(time); } /** * 時間轉 String * * @param time * @param format 時間格式 * @return */ public static String parseTime(LocalDateTime time, TimeFormat format) { return format.formatter.format(time); } /** * 獲取當前時間 * * @return */ public static String getCurrentDateTime() { return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now()); } /** * 獲取當前時間 * * @param format 時間格式 * @return */ public static String getCurrentDateTime(TimeFormat format) { return format.formatter.format(LocalDateTime.now()); } /** * 當天時間起始點. * * @param date the date * @return the start date */ public static LocalDateTime getStartDate(LocalDateTime date) { return string2LocalDateTime(date2String(date, TimeFormat.LONG_DATE_BEGIN_PATTERN_LINE)); } /** * 當天時間終點. * * @param date the date * @return the end date */ public static LocalDateTime getEndDate(LocalDateTime date) { return string2LocalDateTime(date2String(date, TimeFormat.LONG_DATE_END_PATTERN_LINE)); } /** * String 轉時間 * @param timeStr * @return */ public static LocalDateTime string2LocalDateTime(String timeStr) { return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER); } /** * String 轉時間 * @param timeStr * @return */ public static LocalDate string2LocalDate(String timeStr, TimeFormat timeFormat) { return LocalDate.parse(timeStr, timeFormat.formatter); } /** * String 轉時間 * * @param timeStr * @param format 時間格式 * @return */ public static LocalDateTime string2LocalDateTime(String timeStr, TimeFormat format) { return LocalDateTime.parse(timeStr, format.formatter); } // 01. java.util.Date --> java.time.LocalDateTime public static LocalDateTime dateToLocalDateTime(Date date) { Instant instant = date.toInstant(); ZoneId zone = ZoneId.systemDefault(); return LocalDateTime.ofInstant(instant, zone); } // 02. java.util.Date --> java.time.LocalDate public static LocalDate dateToLocalDate(Date date) { Instant instant = date.toInstant(); ZoneId zone = ZoneId.systemDefault(); LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone); return localDateTime.toLocalDate(); } // 03. java.util.Date --> java.time.LocalTime public static LocalTime dateToLocalTime(Date date) { Instant instant = date.toInstant(); ZoneId zone = ZoneId.systemDefault(); LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone); return localDateTime.toLocalTime(); } // 04. java.time.LocalDateTime --> java.util.Date public static Date localDateTimeToDate(LocalDateTime localDateTime) { ZoneId zone = ZoneId.systemDefault(); Instant instant = localDateTime.atZone(zone).toInstant(); return Date.from(instant); } // 05. java.time.LocalDate --> java.util.Date public static Date localDateToDate(LocalDate localDate) { ZoneId zone = ZoneId.systemDefault(); Instant instant = localDate.atStartOfDay().atZone(zone).toInstant(); return Date.from(instant); } // 06. java.time.LocalTime --> java.util.Date public static Date LocalTimeToDate(LocalDate localDate, LocalTime localTime) { LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); ZoneId zone = ZoneId.systemDefault(); Instant instant = localDateTime.atZone(zone).toInstant(); return Date.from(instant); } /** * 時間轉 String * * @param time * @return */ public static String date2String(LocalDateTime time) { return DEFAULT_DATETIME_FORMATTER.format(time); } /** * 時間轉 String * * @param time * @param format 時間格式 * @return */ public static String date2String(LocalDateTime time, TimeFormat format) { return format.formatter.format(time); } /** * 時間格式 */ public enum TimeFormat { /** * 短時間格式 */ SHORT_DATE_PATTERN_CHINA("yyyy年MM月dd日"), SHORT_DATE_PATTERN_LINE("yyyy-MM-dd"), SHORT_DATE_PATTERN_SLASH("yyyy/MM/dd"), SHORT_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd"), SHORT_DATE_PATTERN_NONE("yyyyMMdd"), /** * 長時間格式 */ LONG_DATE_BEGIN_PATTERN_LINE("yyyy-MM-dd 00:00:00"), LONG_DATE_END_PATTERN_LINE("yyyy-MM-dd 23:59:59"), LONG_DATE_PATTERN_LINE("yyyy-MM-dd HH:mm:ss"), LONG_DATE_PATTERN_SLASH("yyyy/MM/dd HH:mm:ss"), LONG_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss"), LONG_DATE_PATTERN_NONE("yyyyMMdd HH:mm:ss"), LONG_DATE_PATTERN_ALL_NONE("yyyyMMddHHmmss"), LONG_DATE_PATTERN_CHINA("yyyy年MM月dd日 HH:mm:ss"), LONG_DATE_BEGIN_PATTERN_CHINA("yyyy年MM月dd日 00:00:00"), /** * 長時間格式 帶毫秒 */ LONG_DATE_PATTERN_WITH_MILSEC_LINE("yyyy-MM-dd HH:mm:ss.SSS"), LONG_DATE_PATTERN_WITH_MILSEC_SLASH("yyyy/MM/dd HH:mm:ss.SSS"), LONG_DATE_PATTERN_WITH_MILSEC_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss.SSS"), LONG_DATE_PATTERN_WITH_MILSEC_NONE("yyyyMMdd HH:mm:ss.SSS"); public transient DateTimeFormatter formatter; TimeFormat(String pattern) { formatter = DateTimeFormatter.ofPattern(pattern); } } /** * 獲取指定date的后一天作為保單生效時間 * @param date 一般是投保日 * @return */ @SneakyThrows public static Date getNextDateAsEffectiveTime(Date date){ return org.apache.commons.lang3.time.DateUtils.truncate(org.apache.commons.lang3.time.DateUtils.addDays(date, 1), Calendar.DATE); } /** * 獲取下一年的日期作為保單失效時間 * @param date 一般是保單生效日 20190528000000 * @return 20200527235959 */ @SneakyThrows public static Date getNextYearAsExpiredTime(Date date){ date = addYears(date, 1); date = truncate(date, Calendar.DATE); return addSeconds(date, -1); } /** * 獲取指定date的作為保單生效時間 * @param date 格式化date的時分秒為00:00:00 * @return */ @SneakyThrows public static Date getEffectiveTimeByDate(Date date){ return org.apache.commons.lang3.time.DateUtils.truncate(date, Calendar.DATE); } /** * 判斷當前時間是否處於兩個時間中間,不判斷beginTime和endTime邏輯大小 * * @param beginTime 開始時間 * @param endTime 結束時間 * @return true or false */ public static boolean isBetween(Date beginTime, Date endTime) { Date now = new Date(); if (now.after(beginTime) && now.before(endTime)) { return true; } return false; } }
