| | |
| | | import cn.hutool.core.lang.Assert; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.framework.common.util.number.BizNoUtils; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodePartDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodeRecordDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodeRuleDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.service.md.autocode.strategy.MesMdAutoCodePartStrategy; |
| | | import jakarta.annotation.PostConstruct; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.data.redis.core.StringRedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | |
| | | /** |
| | | * MES 编码生成记录 Service 实现类 |
| | | * |
| | | * @author 芋道源码 |
| | | * @author 超级管理员 |
| | | */ |
| | | @Service |
| | | @Validated |
| | | public class MesMdAutoCodeRecordServiceImpl implements MesMdAutoCodeRecordService { |
| | | |
| | | private static final int GENERATE_MAX_RETRY_COUNT = 10; |
| | | |
| | | @Resource |
| | | private MesMdAutoCodeRecordMapper recordMapper; |
| | |
| | | private MesMdAutoCodeRuleService ruleService; |
| | | @Resource |
| | | private MesMdAutoCodePartService partService; |
| | | |
| | | @Resource |
| | | private StringRedisTemplate stringRedisTemplate; |
| | | |
| | | @Resource |
| | | private List<MesMdAutoCodePartStrategy> strategyList; |
| | |
| | | throw exception(AUTO_CODE_GENERATE_FAILED); |
| | | } |
| | | |
| | | // 2.1 构建上下文 |
| | | MesMdAutoCodeContext context = new MesMdAutoCodeContext() |
| | | .setRule(rule).setParts(parts).setInputChar(inputChar); |
| | | // 2.2 遍历分段,生成编码 |
| | | StringBuilder codeBuilder = new StringBuilder(); |
| | | for (MesMdAutoCodePartDO part : parts) { |
| | | MesMdAutoCodePartStrategy strategy = strategyMap.get(part.getType()); |
| | | Assert.notNull(strategy, () -> exception(AUTO_CODE_GENERATE_FAILED)); |
| | | // 生成分段编码 |
| | | String partCode = strategy.generate(part, context); |
| | | // 统一截取指定长度 |
| | | partCode = StrUtil.sub(partCode, 0, part.getLength()); |
| | | // 拼接分段编码 |
| | | codeBuilder.append(partCode); |
| | | } |
| | | // 2.3 补齐处理 |
| | | String result = codeBuilder.toString(); |
| | | if (Boolean.TRUE.equals(rule.getPadded()) && rule.getMaxLength() != null) { |
| | | result = padCode(result, rule); |
| | | // 2. 循环生成编码,跳过因 Redis 流水号落后导致的历史重复编码 |
| | | for (int i = 0; i < GENERATE_MAX_RETRY_COUNT; i++) { |
| | | // 2.1 生成编码 |
| | | MesMdAutoCodeContext context = new MesMdAutoCodeContext() |
| | | .setRule(rule).setParts(parts).setInputChar(inputChar); |
| | | String result = generateCode(rule, parts, context); |
| | | // 2.2 二次校验(防止重复) |
| | | MesMdAutoCodeRecordDO existRecord = recordMapper.selectByResult(result); |
| | | if (existRecord != null) { |
| | | continue; |
| | | } |
| | | // 2.3 保存生成记录 |
| | | MesMdAutoCodeRecordDO record = new MesMdAutoCodeRecordDO() |
| | | .setRuleId(rule.getId()).setResult(result) |
| | | .setSerialNo(context.getSerialNo()).setInputChar(inputChar); |
| | | recordMapper.insert(record); |
| | | return result; |
| | | } |
| | | |
| | | // 3.1 二次校验(防止重复) |
| | | MesMdAutoCodeRecordDO existRecord = recordMapper.selectByResult(result); |
| | | if (existRecord != null) { |
| | | throw exception(AUTO_CODE_GENERATE_FAILED); |
| | | } |
| | | // 3.2 保存生成记录 |
| | | MesMdAutoCodeRecordDO record = new MesMdAutoCodeRecordDO() |
| | | .setRuleId(rule.getId()).setResult(result) |
| | | .setSerialNo(context.getSerialNo()).setInputChar(inputChar); |
| | | recordMapper.insert(record); |
| | | return result; |
| | | // 3. 重试多次后仍然失败,抛出异常 |
| | | throw exception(AUTO_CODE_GENERATE_FAILED); |
| | | } |
| | | |
| | | /** |
| | | * 生成编码 |
| | | */ |
| | | private String generateCode(MesMdAutoCodeRuleDO rule, List<MesMdAutoCodePartDO> parts, |
| | | MesMdAutoCodeContext context) { |
| | | // 需求:所有业务编码统一为「16 位纯数字」,且全系统唯一。 |
| | | // 因此不再按规则分段拼装(固定字符/日期段会带入字母),改为取全系统统一自增序列。 |
| | | Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY); |
| | | return BizNoUtils.format(no); |
| | | } |
| | | |
| | | /** |