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);
|
}
|
}
|
}
|