项目中业务编号的实现(业务码+日期+自增序列)


做项目的时候,经常会有自动生成业务编码的需求,比如插入数据的时候需要生成如下产品编号:P-(年份日期和三位序列号),比如P-20180727001

1. 实现思路

  1. 使用业务编号+当前日期获得redis的key值;
  2. 使用redis的incr来原子性地获得其对应的自增数;
  3. 避免redis的数据冗余,需要在第一次incr的时候使用expireAt设置其数据当天24点过期。

这样即可在并发情况下获得不重复的相应编码。

2. 代码实现

public interface CodeGenerateService {

	/**
	 * 根据编号类型生成相应的编号
	 *
	 * @param bizCode         编号类型
	 * @param inrNumberLength 自增编号的长度
	 *
	 * @return 编号
	 */
	String generateCode(String bizCode, int inrNumberLength);
}
@Service
public class CodeGenerateServiceImpl implements CodeGenerateService {

	private static final String SERIAL_GENERATE_PREX = "com.demo:serial_number";

	@Resource
	private CacheClient cacheClient;

	private static String getRedisKey(String bizType) {
	    //获得当前日期
		String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
		return SERIAL_GENERATE_PREX + ":" + bizType + "-" + date;
	}

	/**
	 * 获得自动补0的相应位数的值
	 *
	 * @param seq    数值
	 * @param length 长度
	 *
	 * @return 一定位数的序列号
	 */
	private static String getSequence(long seq, int length) {
		String str = String.valueOf(seq);
		int len = str.length();
		if (len > length) {
		   return str;
		}
		int rest = length - len;
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < rest; i++) {
			sb.append('0');
		}
		sb.append(str);
		return sb.toString();
	}

	/**
	 * 获取某一天的0时0分0秒0毫秒
	 *
	 * @param amount 日增减量
	 *
	 * @return 时间
	 */
	private Date getDay(int amount) {
		// 获取当前日期
		Calendar calendar = Calendar.getInstance();
		// 加一天
		calendar.add(Calendar.DAY_OF_MONTH, amount);
		// 清空时/分/秒/毫秒
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTime();
	}

	@Override
	public String generateCode(String bizCode, int inrNumberLength) {
		String key = getRedisKey(bizCode);
		long number = cacheClient.incr(key);
		if (String.valueOf(number).length() > inrNumberLength) {
		    //抛出自定义异常
			throw new SerialRangException("编码超出范围!");
		}
		if (number == 1) {
			cacheClient.expireAt(key, getDay(1).getTime() / 1000);
		}
		return key + getSequence(number, inrNumberLength);
	}

	@Override
	public String generateCode(String bizCode) {
		return this.generateCode(bizCode, 3);
	}


}

调用

codeGenerateService.generateCode("P")

后打印出来的编号为:P-20180917001

查看redis的存储值如图:

redis编号1

redis编号2

图中已有该key存储的值,且已设置了超时时间为当天的24点,可以满足项目的需求

3. 总结

利用redis的incr原子操作来获得唯一编号,此方法可保证在并发情况下编号的唯一性,但不能保证连续性,比如需要一次就生成连续的多个编号,需要使用来实现。

参考

  1. 流水号的生成(日期+业务码+自增序列)
posted @ 2018-09-17 20:06  David丁  阅读(1215)  评论(0编辑  收藏  举报