xiaoyi
3 天以前 fae95228037f0c1304c1917ae5fff452f2160c5a
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package cn.iocoder.yudao.module.mes.service.pd.knowledge;
 
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeStandardDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeResourceDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.knowledge.KnowledgeReferenceDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeStandardMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeResourceMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.knowledge.KnowledgeReferenceMapper;
import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdProductBomService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
import cn.iocoder.yudao.module.mes.service.pd.processparamtemplate.MesPdProcessParamTemplateService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import lombok.Data;
import org.springframework.stereotype.Service;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
 
@Service
public class KnowledgeWriteService {
    @Resource private KnowledgeMapper knowledgeMapper;
    @Resource private KnowledgeStandardMapper standardMapper;
    @Resource private KnowledgeResourceMapper resourceMapper;
    @Resource private KnowledgeReferenceMapper referenceMapper;
    @Resource private MesDvMachineryService machineryService;
    @Resource private MesMdWorkstationService workstationService;
    @Resource private MesProProcessService processService;
    @Resource private MesMdItemService itemService;
    @Resource private MesMdProductBomService bomService;
    @Resource private MesProRouteService routeService;
    @Resource private MesPdProcessParamTemplateService templateService;
 
    @Data
    public static class SaveRequest {
        private Long id;
        private String code;
        private String name;
        private Long productItemId;
        private Long bomId;
        private BigDecimal voltageMin;
        private BigDecimal voltageMax;
        private String market;
        private Long routeId;
        private Long templateId;
        private String description;
        private List<String> standards;
        private List<ResourceInput> resources;
    }
 
    @Data
    public static class ResourceInput {
        private Long processId;
        private String resourceType;
        private Long resourceId;
        private String capabilityDescription;
    }
 
    @Transactional(rollbackFor = Exception.class)
    public Long create(SaveRequest input) {
        validate(input);
        String code = input.getCode().trim();
        if (!knowledgeMapper.selectList(new LambdaQueryWrapperX<KnowledgeDO>()
                .eq(KnowledgeDO::getCode, code).eq(KnowledgeDO::getVersion, 1)).isEmpty()) {
            throw invalidParamException("知识编码已存在");
        }
        var record = new KnowledgeDO();
        record.setCode(code); record.setVersion(1); record.setStatus(0);
        copyFields(record, input);
        try {
            knowledgeMapper.insert(record);
        } catch (DuplicateKeyException e) {
            throw invalidParamException("知识编码已存在");
        }
        saveStandards(record.getId(), input.getStandards());
        saveResources(record.getId(), input.getResources());
        return record.getId();
    }
 
    @Transactional(rollbackFor = Exception.class)
    public void update(SaveRequest input) {
        KnowledgeDO existing = input == null || input.getId() == null ? null : knowledgeMapper.selectById(input.getId());
        if (existing == null || !Integer.valueOf(0).equals(existing.getStatus())) {
            throw invalidParamException("知识不存在或不是可编辑的草稿");
        }
        validate(input);
        if (!existing.getCode().equals(input.getCode().trim())) throw invalidParamException("知识编码不可修改");
        var update = new LambdaUpdateWrapper<KnowledgeDO>()
                .eq(KnowledgeDO::getId, existing.getId()).eq(KnowledgeDO::getStatus, 0)
                .set(KnowledgeDO::getName, input.getName().trim())
                .set(KnowledgeDO::getProductItemId, input.getProductItemId()).set(KnowledgeDO::getBomId, input.getBomId())
                .set(KnowledgeDO::getVoltageMin, input.getVoltageMin()).set(KnowledgeDO::getVoltageMax, input.getVoltageMax())
                .set(KnowledgeDO::getMarket, input.getMarket()).set(KnowledgeDO::getRouteId, input.getRouteId())
                .set(KnowledgeDO::getTemplateId, input.getTemplateId()).set(KnowledgeDO::getDescription, input.getDescription());
        if (knowledgeMapper.update(null, update) != 1) throw invalidParamException("知识状态已变化,请刷新后重试");
        if (input.getStandards() != null) {
            standardMapper.delete(new LambdaQueryWrapperX<KnowledgeStandardDO>().eq(KnowledgeStandardDO::getKnowledgeId, existing.getId()));
            saveStandards(existing.getId(), input.getStandards());
        }
        if (input.getResources() != null) {
            resourceMapper.delete(new LambdaQueryWrapperX<KnowledgeResourceDO>().eq(KnowledgeResourceDO::getKnowledgeId, existing.getId()));
            saveResources(existing.getId(), input.getResources());
        }
    }
 
    @Transactional(rollbackFor = Exception.class)
    public void publish(Long id) {
        KnowledgeDO record = id == null ? null : knowledgeMapper.selectById(id);
        if (record == null || !Integer.valueOf(0).equals(record.getStatus()))
            throw invalidParamException("知识不存在或不是草稿");
        if (record.getRouteId() == null) throw invalidParamException("发布前请选择工艺路线");
        routeService.validateRouteExists(record.getRouteId());
        if (record.getTemplateId() != null) templateService.validateProcessParamTemplateExists(record.getTemplateId());
        var update = new LambdaUpdateWrapper<KnowledgeDO>().eq(KnowledgeDO::getId, id)
                .eq(KnowledgeDO::getStatus, 0).eq(KnowledgeDO::getRouteId, record.getRouteId());
        if (record.getTemplateId() == null) update.isNull(KnowledgeDO::getTemplateId);
        else update.eq(KnowledgeDO::getTemplateId, record.getTemplateId());
        if (knowledgeMapper.update(null, update.set(KnowledgeDO::getStatus, 1)) != 1)
            throw invalidParamException("知识状态已变化,请刷新后重试");
        // 同一编码只保留当前发布版本,旧版仍可作为引用快照追溯。
        knowledgeMapper.update(null, new LambdaUpdateWrapper<KnowledgeDO>()
                .eq(KnowledgeDO::getCode, record.getCode()).eq(KnowledgeDO::getStatus, 1)
                .lt(KnowledgeDO::getVersion, record.getVersion()).set(KnowledgeDO::getStatus, 2));
    }
 
    @Transactional(rollbackFor = Exception.class)
    public Long revise(Long id) {
        KnowledgeDO source = id == null ? null : knowledgeMapper.selectById(id);
        if (source == null || !Integer.valueOf(1).equals(source.getStatus()))
            throw invalidParamException("只能从已发布的知识创建新版本");
        List<KnowledgeDO> versions = knowledgeMapper.selectList(new LambdaQueryWrapperX<KnowledgeDO>()
                .eq(KnowledgeDO::getCode, source.getCode()).orderByDesc(KnowledgeDO::getVersion));
        if (versions.stream().anyMatch(version -> Integer.valueOf(0).equals(version.getStatus())))
            throw invalidParamException("该知识已有未发布的草稿版本");
        int nextVersion = versions.stream().map(KnowledgeDO::getVersion).max(Integer::compareTo).orElse(0) + 1;
        var draft = new KnowledgeDO();
        draft.setCode(source.getCode()); draft.setVersion(nextVersion); draft.setStatus(0); draft.setName(source.getName());
        draft.setProductItemId(source.getProductItemId()); draft.setBomId(source.getBomId());
        draft.setVoltageMin(source.getVoltageMin()); draft.setVoltageMax(source.getVoltageMax()); draft.setMarket(source.getMarket());
        draft.setRouteId(source.getRouteId()); draft.setTemplateId(source.getTemplateId()); draft.setDescription(source.getDescription());
        try { knowledgeMapper.insert(draft); }
        catch (DuplicateKeyException e) { throw invalidParamException("版本已变化,请刷新后重试"); }
        List<String> standards = standardMapper.selectList(new LambdaQueryWrapperX<KnowledgeStandardDO>()
                .eq(KnowledgeStandardDO::getKnowledgeId, source.getId())).stream().map(KnowledgeStandardDO::getStandardCode).toList();
        saveStandards(draft.getId(), standards);
        List<KnowledgeResourceDO> resources = resourceMapper.selectList(new LambdaQueryWrapperX<KnowledgeResourceDO>()
                .eq(KnowledgeResourceDO::getKnowledgeId, source.getId()));
        for (KnowledgeResourceDO resource : resources) {
            var copy = new KnowledgeResourceDO(); copy.setKnowledgeId(draft.getId());
            copy.setProcessId(resource.getProcessId()); copy.setResourceType(resource.getResourceType());
            copy.setResourceId(resource.getResourceId()); copy.setCapabilityDescription(resource.getCapabilityDescription());
            resourceMapper.insert(copy);
        }
        return draft.getId();
    }
 
    /** 记录参考关系,不修改目标路线或复制路线内容。 */
    @Transactional(rollbackFor = Exception.class)
    public void referenceRoute(Long knowledgeId, Long targetRouteId) {
        if (knowledgeId == null || targetRouteId == null) throw invalidParamException("请选择知识及目标工艺路线");
        KnowledgeDO source = knowledgeMapper.selectOne(new LambdaQueryWrapperX<KnowledgeDO>()
                .eq(KnowledgeDO::getId, knowledgeId).last("FOR UPDATE"));
        if (source == null || !Integer.valueOf(1).equals(source.getStatus()))
            throw invalidParamException("只能参考当前已发布的知识");
        routeService.validateRouteExists(targetRouteId);
        var reference = new KnowledgeReferenceDO(); reference.setKnowledgeId(source.getId());
        reference.setKnowledgeVersion(source.getVersion()); reference.setTargetType("ROUTE"); reference.setTargetId(targetRouteId);
        try { referenceMapper.insert(reference); }
        catch (DuplicateKeyException e) { throw invalidParamException("该路线已参考此知识版本"); }
    }
 
    private void validate(SaveRequest input) {
        if (input == null || input.getCode() == null || input.getCode().isBlank() || input.getCode().length() > 64
                || input.getName() == null || input.getName().isBlank() || input.getName().length() > 200)
            throw invalidParamException("请填写有效的知识编码和名称");
        if (input.getMarket() == null || !Set.of("ALL", "DOMESTIC", "EXPORT").contains(input.getMarket())) throw invalidParamException("内外销类型不正确");
        if ((input.getVoltageMin() != null && input.getVoltageMin().signum() < 0)
                || (input.getVoltageMax() != null && input.getVoltageMax().signum() < 0)
                || (input.getVoltageMin() != null && input.getVoltageMax() != null && input.getVoltageMin().compareTo(input.getVoltageMax()) > 0))
            throw invalidParamException("电压范围不正确");
        if (input.getProductItemId() != null) itemService.validateItemExists(input.getProductItemId());
        if (input.getBomId() != null) {
            if (input.getProductItemId() == null) throw invalidParamException("选择 BOM 明细前请先选择适用物料");
            boolean found = bomService.getProductBomListByItemId(input.getProductItemId()).stream()
                    .anyMatch(bom -> Objects.equals(bom.getItemId(), input.getProductItemId())
                            && Objects.equals(bom.getBomItemId(), input.getBomId()) && Objects.equals(bom.getStatus(), 0));
            if (!found) throw invalidParamException("所选 BOM 子物料不存在、未启用或不属于该物料");
        }
        if (input.getRouteId() != null) routeService.validateRouteExists(input.getRouteId());
        if (input.getTemplateId() != null) templateService.validateProcessParamTemplateExists(input.getTemplateId());
        if (input.getStandards() != null && input.getStandards().stream().anyMatch(s -> s == null || s.isBlank() || s.trim().length() > 128))
            throw invalidParamException("标准编码不能为空或超过 128 字符");
        if (input.getResources() != null) for (ResourceInput resource : input.getResources()) {
            if (resource == null || resource.getResourceType() == null || !Set.of("MACHINE", "WORKSTATION", "OTHER").contains(resource.getResourceType()))
                throw invalidParamException("资源类型不正确");
            if (resource.getCapabilityDescription() == null || resource.getCapabilityDescription().isBlank()
                    || resource.getCapabilityDescription().length() > 500) throw invalidParamException("请填写不超过 500 字的资源能力说明");
            if (resource.getProcessId() != null) processService.validateProcessExists(resource.getProcessId());
            if ("MACHINE".equals(resource.getResourceType())) {
                if (resource.getResourceId() == null) throw invalidParamException("请选择设备");
                machineryService.validateMachineryExists(resource.getResourceId());
            } else if ("WORKSTATION".equals(resource.getResourceType())) {
                if (resource.getResourceId() == null) throw invalidParamException("请选择工作站");
                workstationService.validateWorkstationExists(resource.getResourceId());
            } else if (resource.getResourceId() != null) throw invalidParamException("其他资源不能指定设备或工作站编号");
        }
    }
 
    private void copyFields(KnowledgeDO record, SaveRequest input) {
        record.setName(input.getName().trim()); record.setProductItemId(input.getProductItemId()); record.setBomId(input.getBomId());
        record.setVoltageMin(input.getVoltageMin()); record.setVoltageMax(input.getVoltageMax()); record.setMarket(input.getMarket());
        record.setRouteId(input.getRouteId()); record.setTemplateId(input.getTemplateId()); record.setDescription(input.getDescription());
    }
 
    private void saveStandards(Long id, List<String> standards) {
        if (standards == null) return;
        standards.stream().map(String::trim).distinct().forEach(code -> {
            var standard = new KnowledgeStandardDO(); standard.setKnowledgeId(id); standard.setStandardCode(code);
            standardMapper.insert(standard);
        });
    }
 
    private void saveResources(Long id, List<ResourceInput> resources) {
        if (resources == null) return;
        for (ResourceInput resource : resources) {
            var item = new KnowledgeResourceDO(); item.setKnowledgeId(id); item.setProcessId(resource.getProcessId());
            item.setResourceType(resource.getResourceType()); item.setResourceId(resource.getResourceId());
            item.setCapabilityDescription(resource.getCapabilityDescription().trim()); resourceMapper.insert(item);
        }
    }
}