tuqin
16 小时以前 0ddbb4ec727ffce844ef672107e3410d84b1f11a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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.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.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.data.redis.core.StringRedisTemplate;
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 StringRedisTemplate stringRedisTemplate;
 
    @Resource
    private List<MesMdAutoCodePartStrategy> strategyList;
    private Map<Integer, MesMdAutoCodePartStrategy> 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<MesMdAutoCodePartDO> 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<MesMdAutoCodePartDO> parts,
                                MesMdAutoCodeContext context) {
        // 需求:所有业务编码统一为「16 位纯数字」,且全系统唯一。
        // 因此不再按规则分段拼装(固定字符/日期段会带入字母),改为取全系统统一自增序列。
        // 随机生成 16 位纯数字编码,并用 Redis 集合查重,保证全系统唯一(不使用自增/顺序)
        for (int i = 0; i < BizNoUtils.MAX_RETRY; i++) {
            String code = BizNoUtils.randomCode();
            Long added = stringRedisTemplate.opsForSet().add(BizNoUtils.USED_SET_KEY, code);
            if (added != null && added > 0) {
                return code;
            }
        }
        throw new IllegalStateException("生成唯一业务编码失败,请重试");
    }
 
    /**
     * 补齐编码
     */
    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));
        }
    }
 
}