package cn.iocoder.yudao.module.mes.service.md.autocode; import cn.hutool.core.collection.CollUtil; 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.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.dal.mysql.md.autocode.MesMdAutoCodeRecordMapper; import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodePaddedMethodEnum; import cn.iocoder.yudao.module.mes.service.md.autocode.strategy.MesMdAutoCodeContext; import cn.iocoder.yudao.module.mes.service.md.autocode.strategy.MesMdAutoCodePartStrategy; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 编码生成记录 Service 实现类 * * @author 芋道源码 */ @Service @Validated public class MesMdAutoCodeRecordServiceImpl implements MesMdAutoCodeRecordService { private static final int GENERATE_MAX_RETRY_COUNT = 10; @Resource private MesMdAutoCodeRecordMapper recordMapper; @Resource private MesMdAutoCodeRuleService ruleService; @Resource private MesMdAutoCodePartService partService; @Resource private List strategyList; private Map strategyMap; @PostConstruct public void init() { strategyMap = CollectionUtils.convertMap(strategyList, MesMdAutoCodePartStrategy::getType); } @Override @Transactional(rollbackFor = Exception.class) public String generateAutoCode(String ruleCode, String inputChar) { // 1.1 查询规则 MesMdAutoCodeRuleDO rule = ruleService.getAutoCodeRuleByCode(ruleCode); if (rule == null) { throw exception(AUTO_CODE_RULE_NOT_EXISTS); } // 1.2 查询规则组成 List parts = partService.getAutoCodePartListByRuleId(rule.getId()); if (CollUtil.isEmpty(parts)) { throw exception(AUTO_CODE_GENERATE_FAILED); } // 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. 重试多次后仍然失败,抛出异常 throw exception(AUTO_CODE_GENERATE_FAILED); } /** * 生成编码 */ private String generateCode(MesMdAutoCodeRuleDO rule, List parts, MesMdAutoCodeContext context) { 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); } // 补齐处理 String result = codeBuilder.toString(); if (Boolean.TRUE.equals(rule.getPadded()) && rule.getMaxLength() != null) { result = padCode(result, rule); } return result; } /** * 补齐编码 */ private String padCode(String code, MesMdAutoCodeRuleDO rule) { if (code.length() >= rule.getMaxLength()) { return StrUtil.sub(code, 0, rule.getMaxLength()); } String paddedChar = StrUtil.emptyToDefault(rule.getPaddedChar(), "0"); Integer paddedMethod = rule.getPaddedMethod() != null ? rule.getPaddedMethod() : MesMdAutoCodePaddedMethodEnum.LEFT.getMethod(); if (MesMdAutoCodePaddedMethodEnum.LEFT.getMethod().equals(paddedMethod)) { // 左补齐 return StrUtil.padPre(code, rule.getMaxLength(), paddedChar.charAt(0)); } else { // 右补齐 return StrUtil.padAfter(code, rule.getMaxLength(), paddedChar.charAt(0)); } } }