自定义自增编码规则生成
- 需求场景
- 开发需求
- 需求实现
- 其它
在项目中遇到的需求,这里记录下实现。以下仅供参考,代码有所删减,但核心代码在
需求场景
1.需要多个编号规则,不同的场景使用的编码规则不同。
2.编码需要可自定义灵活选择配置,序号按自增处理。
3.编码规则包含时间,前缀,后缀,自增序列位数,分隔符等。
开发需求
1.创建一个表,用来维护编号规则,每个规则都有一个type,用来区分规则的使用场景,type唯一。数据如下所示
2.使用redis,用来存储自增序数(本文示例中,核心代码是设置redis的自增和时间那一部分代码
)
需求实现
1.数据库设计
表名 | code_rules |
字段 | 说明 |
rule_name | 规则名称 |
prefix | 前缀 |
serial_type | 序号规则 |
serial_digitis | 自增序列位数 |
suffix | 后缀 |
rule_separator | 分隔符 |
rule_type | 编号类型(用于区分使用场景,不同的场景通过type获取对应的规则) |
public interface SequenceCodeService {
/**
* 根据codeRulesId获取编码规则
* @param codeRulesId
* @return
*/
public String getCodeByCodeRulesId(String codeRulesId);
/**
* @Description: 根据规则类型获取编码
* @param ruleType
* @return: {@link java.lang.String}
* @Author: lvyq
* @Date: 2022/9/21 17:04
*/
public String getCodeByRuleType(String ruleType);
}
3.SequenceCodeServiceImpl.java(核心代码
)
@Service
public class SequenceCodeServiceImpl implements SequenceCodeService {
public static Logger logger = LoggerFactory.getLogger(SequenceCodeService.class);
@Resource
public RedisTemplate redisTemplate;
@Resource
public CodeRulesMapper codeRulesMapper;
private static final String CODE_PUB="code:pub:";
/**
* @Description: 根据规则类型获取编码
* @param ruleType
* @return: {@link java.lang.String}
* @Author: lvyq
* @Date: 2022/9/21 17:04
*/
@Override
public String getCodeByRuleType(String ruleType) {
String codeRulesId =codeRulesMapper.getIdByRuleType(ruleType);
if (StringUtils.isEmpty(codeRulesId)){
throw new RuntimeException(ruleType+"编码规则不存在");
}
return getCodeByCodeRulesId(codeRulesId);
}
/**
* 根据codeRulesId获取编号
* @param codeRulesId
* @return
*/
@Override
public String getCodeByCodeRulesId(String codeRulesId) {
CodeRules codeRules = codeRulesMapper.selectById(codeRulesId);
//以codeRulesId为key进行存储
if (codeRules!=null){
return getCode(codeRules);
}else {
throw new RuntimeException("当前编码规则不存在");
}
}
/**
* 获取编码
* @param codeRules
* @return
*/
public String getCode(CodeRules codeRules) {
//前缀
String prefix = codeRules.getPrefix();
//规则类型-时间规则
String serialType=codeRules.getSerialType();
//后缀
String suffix = codeRules.getSuffix();
//自增序列位数
String serialDigitis = codeRules.getSerialDigitis();
//分隔符
String ruleSeparator = codeRules.getRuleSeparator();
if (ruleSeparator==null){ruleSeparator="";}
if (prefix==null){prefix="";}
if (suffix==null){suffix="";}
if (StringUtils.isNotEmpty(serialType)){
return getCodeNo(CODE_PUB+codeRules.getRuleType()+":"+codeRules.getId(),serialType,prefix,suffix,serialDigitis,ruleSeparator);
}else {
//无规则,无过期
return geOtherNo(CODE_PUB+codeRules.getRuleType()+":"+codeRules.getId(),prefix,suffix,serialDigitis,ruleSeparator);
}
}
/**
* 含规则
* @param key
* @param serialType
* @param prefix
* @param suffix
* @param serialDigitis
* @param ruleSeparator
* @return
*/
private String getCodeNo(String key, String serialType, String prefix, String suffix, String serialDigitis, String ruleSeparator) {
Calendar calendar = Calendar.getInstance();
//按年
if (!serialType.contains("MM") ){
//设置过期时间,这里设置为当年12月31日的23:59:59
SimpleDateFormat format = new SimpleDateFormat("yyyy");
calendar.set(Integer.valueOf(format.format(new Date())),11,31,23,59,59);
}else if (serialType.contains("dd")){
//按日,设置时间,到当日23:59:59过期
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
}else if (serialType.contains("MM") && !serialType.contains("dd")){
//按月。设置时间,当月过期
SimpleDateFormat format = new SimpleDateFormat("MM");
calendar.set(Calendar.MONTH,Integer.valueOf(format.format(new Date())));
calendar.set(Calendar.DATE,0);//表示取当前月份的前一天
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
}
Date expireDate = calendar.getTime();
Long seq = generate(redisTemplate, key, expireDate);
String sequence="";
//生成x位序列号,如果seq不够x位,seq前面补0,
if (StringUtils.isNotEmpty(serialDigitis)){
sequence = StringUtils.leftPad(seq.toString(), Integer.valueOf(serialDigitis), "0");
}else {
//无补位自增
sequence=seq.toString();
}
//规则日期生成
SimpleDateFormat format = new SimpleDateFormat(serialType);
//拼接业务编号
String seqNo = prefix + ruleSeparator+format.format(new Date()) +ruleSeparator+ sequence+ruleSeparator+suffix;
if (seqNo.endsWith(ruleSeparator) && StringUtils.isNotEmpty(ruleSeparator)){
seqNo=seqNo.substring(0,seqNo.length()-ruleSeparator.length());
}
if (seqNo.startsWith(ruleSeparator) && StringUtils.isNotEmpty(ruleSeparator)){
seqNo=seqNo.substring(ruleSeparator.length());
}
logger.info("KEY:{}, 序列号生成:{}, 过期时间:{}", key, seqNo, String.format("%tF %tT ", expireDate, expireDate));
return seqNo;
}
/**
* 无规则
* @param prefix
* @param suffix
* @param serialDigitis
* @param ruleSeparator
* @return
*/
public String geOtherNo(String key,String prefix,String suffix,String serialDigitis,String ruleSeparator) {
RedisAtomicLong counter = new RedisAtomicLong(key,redisTemplate.getConnectionFactory());
Long seq=counter.incrementAndGet();
String sequence="";
//生成x位序列号,如果seq不够x位,seq前面补0,
if (StringUtils.isNotEmpty(serialDigitis)){
sequence = StringUtils.leftPad(seq.toString(), Integer.valueOf(serialDigitis), "0");
}else {
//无补位自增
sequence=seq.toString();
}
//拼接业务编号 前缀+分割符+序号+分割+后缀
String seqNo=prefix+ruleSeparator+sequence+ruleSeparator+suffix;
if (seqNo.endsWith(ruleSeparator) && StringUtils.isNotEmpty(ruleSeparator)){
seqNo=seqNo.substring(0,seqNo.length()-ruleSeparator.length());
}
if (seqNo.startsWith(ruleSeparator) && StringUtils.isNotEmpty(ruleSeparator)){
seqNo=seqNo.substring(ruleSeparator.length());
}
logger.info("KEY:{}, 序列号生成:{}", key, seqNo);
return seqNo;
}
/**
* @param key
* @param expireTime <i>过期时间</i>
* @return
*/
public static long generate(RedisTemplate<?,?> redisTemplate,String key,Date expireTime) {
RedisAtomicLong counter = new RedisAtomicLong(key,redisTemplate.getConnectionFactory());
//设置过期时间
counter.expireAt(expireTime);
return counter.incrementAndGet();
}
}
说明 | codeRulesMapper 的两个查询比较简单,就不贴了,也可以根据type直接查询获取对应的规则。 |
4.使用
在使用的地方注入
@Resource
private SequenceCodeService sequenceCodeService;
并使用
String code =sequenceCodeService.getCodeByRuleType(RuleCodeConstant.SHDW);
5 RuleCodeConstant.java
常量类,放的表中的type。
public class RuleCodeConstant {
/**
* 商混单位
*/
public static final String SHDW ="sh_type";
}
其它
1.修改规则
2.redis配置修改
修改redis.conf
appendonly no
改为
appendonly yes
防止redis服务重启后,含有到期时间的数据失效。