背景
产品需求要生成有序的订单 key+ 年月日时分秒 +6位序号 由00001-99999组成 且每天都是从00001开始
公司系统有部署多台服务,这需要一个有序的序列不能重复而且得保证获取时的原子性这里 我们考虑使用了redis Incr 这个命令
Redis Incr 命令能将 key 中储存的数字值增一,这样就不会取到重复的编号。
需要提前引入Redis
使用
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;public class DateUtils {public static final long ONE_DAY_MILLS = 3600000 * 24;private DateUtils() {}/*** 日期格式 enum(枚举类型):一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符 -->为申明的变量提供更大的取值范围*/public enum DateFormat {/*** 格式:"yyyyMMddHHmmss"*/ALLTIME {@Overridepublic String getValue() {return "yyyyMMddHHmmss";}},/*** 格式:"yyyy-MM-dd HH:mm:ss"*/ALL_TIME {public String getValue() {return "yyyy-MM-dd HH:mm:ss";}},/*** 格式:"yyyy-MM-dd HH:mm"*/ONLY_MINUTE {public String getValue() {return "yyyy-MM-dd HH:mm";}},/*** 格式:"yyyy-MM-dd HH"*/ONLY_HOUR {public String getValue() {return "yyyy-MM-dd HH";}},/*** 格式:"yyyy-MM-dd"*/ONLY_DAY {public String getValue() {return "yyyy-MM-dd";}},/*** 格式:"yyyy-MM"*/ONLY_MONTH {public String getValue() {return "yyyy-MM";}},/*** 格式:"MM-dd"*/ONLY_MONTH_DAY {public String getValue() {return "MM-dd";}},/*** 格式:"MM-dd HH:mm"*/ONLY_MONTH_SEC {public String getValue() {return "MM-dd HH:mm";}},/*** 格式:"HH:mm:ss"*/ONLY_TIME {public String getValue() {return "HH:mm:ss";}},/*** 格式:"HH:mm"*/ONLY_HOUR_MINUTE {public String getValue() {return "HH:mm";}};public abstract String getValue();}/*** 获取当前时间 格式---->2019-08-04 13:30:29*/public static String getNowTime(DateFormat format) {String nowtime = null;Calendar calendar = Calendar.getInstance();Date dateNow = calendar.getTime();SimpleDateFormat sdf = new SimpleDateFormat(format.getValue(), Locale.CHINA);nowtime = sdf.format(dateNow);return nowtime;}/*** 将一个日期字符串转换成Data对象 string-->date** @param dateString 日期字符串* @param format 转换格式* @return*/public static Date stringToDate(String dateString, DateFormat format) {Date date = null;SimpleDateFormat sdf = new SimpleDateFormat(format.getValue(), Locale.CHINA);try {date = sdf.parse(dateString);} catch (ParseException e) {e.printStackTrace();}return date;}/*** 将date转换成字符串 date--->string** @param date 日期* @param format 日期目标格式* @return*/public static String dateToString(Date date, DateFormat format) {String string = "";SimpleDateFormat sdf = new SimpleDateFormat(format.getValue(), Locale.CHINA);string = sdf.format(date);return string;}/*** 获取指定日期** @param date 指定日期* @return 返回值为: "周日", "周一", "周二", "周三", "周四", "周五", "周六"*/public static String getWeekOfDate(Date date) {String[] weekDays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};Calendar calendar = Calendar.getInstance();calendar.setTime(date);int week = calendar.get(Calendar.DAY_OF_WEEK) - 1;if (week < 0)week = 0;return weekDays[week];}/*** 获取指定日期对应周几的序列** @param date 指定日期* @return 周一:1 周二:2 周三:3 周四:4 周五:5 周六:6 周日:7*/public static int getIndexWeekOfDate(Date date) {Calendar calendar = Calendar.getInstance();calendar.setTime(date);int index = calendar.get(Calendar.DAY_OF_WEEK);if (index == 1) {return 7;} else {return --index;}}/*** 获取当前月份*/public static int getNowMonth() {Calendar calendar = Calendar.getInstance();return calendar.get(Calendar.MONTH) + 1;}/*** 获取当前月号*/public static int getNowDay() {Calendar calendar = Calendar.getInstance();return calendar.get(Calendar.DATE);}/*** 获取当前年份*/public static int getNowYear() {Calendar calendar = Calendar.getInstance();return calendar.get(Calendar.YEAR);}/*** 获取本月份的天数*/public static int getNowDaysOfMonth() {Calendar calendar = Calendar.getInstance();return daysOfMonth(calendar.get(Calendar.YEAR), calendar.get(Calendar.DATE) + 1);}/*** 计算两个日期之间的年份差距** @param firstDate* @param secondDate* @return*/public static int getYearGapOfDates(Date firstDate, Date secondDate) {if (firstDate == null || secondDate == null) {return 0;}Calendar helpCalendar = Calendar.getInstance();helpCalendar.setTime(firstDate);int firstYear = helpCalendar.get(Calendar.YEAR);helpCalendar.setTime(secondDate);int secondYear = helpCalendar.get(Calendar.YEAR);return secondYear - firstYear;}/*** 计算两个日期之间的天数差** @param startDate* @param endDate* @return*/public static int getDaysGapOfDates(Date startDate, Date endDate) {int date = 0;if (startDate != null && endDate != null) {date = getDaysBetween(startDate, endDate);}return date;}/*** 计算两个日期之间的月份差距** @param firstDate* @param secondDate* @return*/public static int getMonthGapOfDates(Date firstDate, Date secondDate) {if (firstDate == null || secondDate == null) {return 0;}return (int) ((secondDate.getTime() - firstDate.getTime())/ ONE_DAY_MILLS / 30);}/*** 获取指定月份的天数** @param year 年份* @param month 月份* @return 对应天数*/public static int daysOfMonth(int year, int month) {switch (month) {case 1:case 3:case 5:case 7:case 8:case 10:case 12:return 31;case 4:case 6:case 9:case 11:return 30;case 2:if ((year % 4 == 0 && year % 100 == 0) || year % 400 != 0) {return 29;} else {return 28;}default:return -1;}}private static int getDaysBetween(Date startDate, Date endDate) {return (int) ((endDate.getTime() - startDate.getTime()) / ONE_DAY_MILLS);}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
@SuppressWarnings(value = {"rawtypes"})
@Slf4j
public class OrderNumberUtils {@Autowired@Qualifier("redisTemplate")private RedisTemplate redisTemplate;/*** 对取值补 0** @param keyName* @param lenght 补足多少位* @return*/public String generateOrderNum(String keyName, int lenght) {Integer serialNumber = getSerialNumber(keyName);return String.format("%0" + lenght + "d", serialNumber);}/*** 数量自增 已设置过期时间** @param keyName* @return*/public Integer getSerialNumber(String keyName) {keyName = "SerialNumber:" + keyName;try {RedisAtomicLong entityIdCounter = new RedisAtomicLong(keyName, redisTemplate.getConnectionFactory());Long incr = entityIdCounter.getAndIncrement();if (null == incr || incr.longValue() == 0) {// 初始设置过期时间// 设置过期时间 24小时entityIdCounter.expire(24, TimeUnit.HOURS);// 这里取第二次 incr 就是从1开始了,默认从0开始incr = entityIdCounter.getAndIncrement();}log.debug(keyName + "==========" + incr);return incr.intValue();} catch (Exception e) {log.error("======redisIncr======", e);}return null;}
}
测试
// 生成number指定长度 0000001、int leng = 6;String 年月日 = DateUtils.dateToString(new Date(), DateUtils.DateFormat.ALLTIME);String number = orderNumber.generateOrderNum(年月日, leng );System.out.println(年月日.concat( number));
订单结果
20230722114301000001
20230722114301000002
20230722114301000003
20230722114301000004
20230722114301000005
20230722114302000001
20230722114302000002
20230722114302000004