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

Posted ading-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目中业务编号的实现(业务码+日期+自增序列)相关的知识,希望对你有一定的参考价值。

目录

做项目的时候,经常会有自动生成业务编码的需求,比如插入数据的时候需要生成如下产品编号: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的存储值如图:

技术分享图片

技术分享图片

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

3. 总结

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

参考

  1. 流水号的生成(日期+业务码+自增序列)

以上是关于项目中业务编号的实现(业务码+日期+自增序列)的主要内容,如果未能解决你的问题,请参考以下文章

0051利用Redis实现业务主键的自增

如何使用redis生成唯一编号及原理

实体框架和业务对象

业务编号生成器

业务ID 生成策略

关于后台系统自动生成的一点思考