目录
做项目的时候,经常会有自动生成业务编码的需求,比如插入数据的时候需要生成如下产品编号:P-(年份日期和三位序列号),比如P-20180727001
业务编号+当前日期获得redis的key值;incr来原子性地获得其对应的自增数;incr的时候使用expireAt设置其数据当天24点过期。这样即可在并发情况下获得不重复的相应编码。
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点,可以满足项目的需求
利用redis的incr原子操作来获得唯一编号,此方法可保证在并发情况下编号的唯一性,但不能保证连续性,比如需要一次就生成连续的多个编号,需要使用锁来实现。
原文:https://www.cnblogs.com/ading-blog/p/9664533.html