package cn.iocoder.yudao.module.mes.service.qc.template; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcTemplateProcessRespVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.MesQcTemplateProcessSaveByTemplateReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.MesQcTemplateProcessSaveListReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.MesQcTemplateProcessSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateProcessDO; import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateProcessMapper; import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 质检方案-工序绑定 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesQcTemplateProcessServiceImpl implements MesQcTemplateProcessService { @Resource private MesQcTemplateProcessMapper templateProcessMapper; @Resource private MesProProcessService processService; @Resource private MesQcTemplateService templateService; @Resource private MesQcTemplateIndicatorService templateIndicatorService; @Resource private MesQcIndicatorService indicatorService; @Override public List getListByProcessId(Long processId) { if (processId == null) { return Collections.emptyList(); } return templateProcessMapper.selectListByProcessId(processId); } @Override @Transactional(rollbackFor = Exception.class) public void saveList(MesQcTemplateProcessSaveListReqVO reqVO) { processService.validateProcessExists(reqVO.getProcessId()); List list = reqVO.getList() == null ? Collections.emptyList() : reqVO.getList(); validateList(reqVO.getProcessId(), list); List existingList = templateProcessMapper.selectListByProcessId(reqVO.getProcessId()); Set existingIds = convertSet(existingList, MesQcTemplateProcessDO::getId); Set retainedIds = new HashSet<>(); for (MesQcTemplateProcessSaveReqVO item : list) { MesQcTemplateProcessDO binding = BeanUtils.toBean(item, MesQcTemplateProcessDO.class) .setProcessId(reqVO.getProcessId()) .setDefaultFlag(Boolean.TRUE.equals(item.getDefaultFlag())) .setStatus(item.getStatus() != null ? item.getStatus() : CommonStatusEnum.ENABLE.getStatus()) .setSort(item.getSort() != null ? item.getSort() : 0); if (binding.getId() == null) { templateProcessMapper.insert(binding); } else { if (!existingIds.contains(binding.getId())) { throw exception(QC_TEMPLATE_PROCESS_NOT_EXISTS); } templateProcessMapper.updateById(binding); } retainedIds.add(binding.getId()); // 同一工序、同一质检种类只允许一个默认方案 if (Boolean.TRUE.equals(binding.getDefaultFlag())) { clearOtherDefaults(reqVO.getProcessId(), binding.getQcType(), binding.getId()); } } for (Long existingId : existingIds) { if (!retainedIds.contains(existingId)) { templateProcessMapper.deleteById(existingId); } } } @Override public List getListByTemplateId(Long templateId) { if (templateId == null) { return Collections.emptyList(); } return templateProcessMapper.selectListByTemplateId(templateId); } @Override @Transactional(rollbackFor = Exception.class) public void saveByTemplate(MesQcTemplateProcessSaveByTemplateReqVO reqVO) { // 1. 校验方案与质检种类 MesQcTemplateDO template = templateService.getTemplate(reqVO.getTemplateId()); if (template == null) { throw new ServiceException(QC_TEMPLATE_PROCESS_NOT_EXISTS.getCode(), StrUtil.format("质检方案 ID={} 不存在;请刷新质检方案列表后重新选择", reqVO.getTemplateId())); } if (CollUtil.isNotEmpty(template.getTypes()) && !template.getTypes().contains(reqVO.getQcType())) { throw new ServiceException(QC_TEMPLATE_PROCESS_NOT_EXISTS.getCode(), StrUtil.format("质检方案[{}]的检测种类为{},不包含[{}];请先在质检方案中维护该检测种类", template.getName(), template.getTypes(), qcTypeName(reqVO.getQcType()))); } List list = reqVO.getList() == null ? Collections.emptyList() : reqVO.getList(); Map requestedMap = new LinkedHashMap<>(); for (MesQcTemplateProcessSaveByTemplateReqVO.ProcessItem item : list) { if (requestedMap.putIfAbsent(item.getProcessId(), item) != null) { throw new ServiceException(QC_TEMPLATE_PROCESS_DUPLICATE.getCode(), StrUtil.format("质检方案[{}]重复选择了工序 ID={};请删除重复的工序后重试", template.getName(), item.getProcessId())); } } // 2. 以「已有绑定工序 ∪ 本次选择工序」为范围,逐工序重算绑定列表 List existingList = templateProcessMapper.selectListByTemplateId(reqVO.getTemplateId()); Set processIds = new LinkedHashSet<>(convertSet(existingList, MesQcTemplateProcessDO::getProcessId)); processIds.addAll(requestedMap.keySet()); for (Long processId : processIds) { List processBindings = templateProcessMapper.selectListByProcessId(processId); List targetList = new ArrayList<>(); for (MesQcTemplateProcessDO binding : processBindings) { // 本方案 + 本质检种类的旧绑定由本次请求整体替换 if (ObjUtil.equal(binding.getTemplateId(), reqVO.getTemplateId()) && ObjUtil.equal(binding.getQcType(), reqVO.getQcType())) { continue; } targetList.add(BeanUtils.toBean(binding, MesQcTemplateProcessSaveReqVO.class)); } MesQcTemplateProcessSaveByTemplateReqVO.ProcessItem requested = requestedMap.get(processId); if (requested == null && targetList.isEmpty()) { continue; } if (requested != null) { targetList.add(new MesQcTemplateProcessSaveReqVO() .setProcessId(processId) .setTemplateId(reqVO.getTemplateId()) .setQcType(reqVO.getQcType()) .setDefaultFlag(Boolean.TRUE.equals(requested.getDefaultFlag())) .setSort(requested.getSort() != null ? requested.getSort() : 0)); } MesQcTemplateProcessSaveListReqVO saveReqVO = new MesQcTemplateProcessSaveListReqVO(); saveReqVO.setProcessId(processId); saveReqVO.setList(targetList); saveList(saveReqVO); } } @Override public Long resolveTemplateId(Long processId, Integer qcType) { if (processId == null || qcType == null) { return null; } return templateProcessMapper.selectListByProcessId(processId).stream() .filter(binding -> ObjUtil.equal(binding.getQcType(), qcType)) .filter(binding -> ObjUtil.equal(binding.getStatus(), CommonStatusEnum.ENABLE.getStatus())) .sorted(Comparator .comparing((MesQcTemplateProcessDO binding) -> Boolean.TRUE.equals(binding.getDefaultFlag())) .reversed() .thenComparing(binding -> binding.getSort() != null ? binding.getSort() : 0) .thenComparing(MesQcTemplateProcessDO::getId)) .map(MesQcTemplateProcessDO::getTemplateId) .findFirst().orElse(null); } @Override public MesQcIpqcTemplateProcessRespVO resolveTemplate(Long processId, Integer qcType) { MesQcIpqcTemplateProcessRespVO resp = new MesQcIpqcTemplateProcessRespVO() .setProcessId(processId).setQcType(qcType); Long templateId = resolveTemplateId(processId, qcType); if (templateId == null) { return resp; } MesQcTemplateDO template = templateService.getTemplate(templateId); if (template == null) { return resp; } resp.setTemplateId(template.getId()).setTemplateCode(template.getCode()).setTemplateName(template.getName()); List templateIndicators = templateIndicatorService.getTemplateIndicatorListByTemplateId(templateId); if (CollUtil.isEmpty(templateIndicators)) { resp.setIndicators(Collections.emptyList()); return resp; } Map indicatorMap = indicatorService.getIndicatorMap( convertSet(templateIndicators, MesQcTemplateIndicatorDO::getIndicatorId)); List items = new ArrayList<>(templateIndicators.size()); for (MesQcTemplateIndicatorDO templateIndicator : templateIndicators) { MesQcIndicatorDO indicator = indicatorMap.get(templateIndicator.getIndicatorId()); items.add(new MesQcIpqcTemplateProcessRespVO.IndicatorItem() .setTemplateIndicatorId(templateIndicator.getId()) .setIndicatorId(templateIndicator.getIndicatorId()) .setIndicatorCode(indicator != null ? indicator.getCode() : null) .setIndicatorName(indicator != null ? indicator.getName() : null) .setItemType(indicator != null ? indicator.getItemType() : null) .setResultType(indicator != null ? indicator.getResultType() : null) .setResultSpecification(indicator != null ? indicator.getResultSpecification() : null) .setUnitText(indicator != null ? indicator.getUnitText() : null) .setTool(indicator != null ? indicator.getTool() : null) .setCheckMethod(templateIndicator.getCheckMethod()) .setStandardValue(templateIndicator.getStandardValue()) .setUnitMeasureId(templateIndicator.getUnitMeasureId()) .setThresholdMax(templateIndicator.getThresholdMax()) .setThresholdMin(templateIndicator.getThresholdMin())); } resp.setIndicators(items); return resp; } @Override public void deleteListByTemplateId(Long templateId) { List list = templateProcessMapper.selectListByTemplateId(templateId); if (CollUtil.isEmpty(list)) { return; } templateProcessMapper.deleteBatch(MesQcTemplateProcessDO::getId, convertSet(list, MesQcTemplateProcessDO::getId)); } private void clearOtherDefaults(Long processId, Integer qcType, Long keepId) { templateProcessMapper.selectListByProcessId(processId).stream() .filter(binding -> ObjUtil.equal(binding.getQcType(), qcType)) .filter(binding -> Boolean.TRUE.equals(binding.getDefaultFlag())) .filter(binding -> !ObjUtil.equal(binding.getId(), keepId)) .forEach(binding -> templateProcessMapper.updateById( new MesQcTemplateProcessDO().setId(binding.getId()).setDefaultFlag(false))); } private static String qcTypeName(Integer qcType) { return Arrays.stream(MesQcTypeEnum.values()) .filter(item -> ObjUtil.equal(item.getType(), qcType)) .map(MesQcTypeEnum::getName).findFirst().orElse("未知(" + qcType + ")"); } private void validateList(Long processId, List list) { if (CollUtil.isEmpty(list)) { return; } Set keys = new HashSet<>(); Set defaultKeys = new HashSet<>(); for (MesQcTemplateProcessSaveReqVO item : list) { if (!Arrays.asList(MesQcTypeEnum.ARRAYS).contains(item.getQcType())) { throw new ServiceException(QC_TEMPLATE_PROCESS_NOT_EXISTS.getCode(), StrUtil.format("工序绑定的质检种类[{}]不合法;期望 1-来料检验 / 2-过程检验 / 3-出货检验 / 4-退货检验,请重新选择", item.getQcType())); } if (templateService.getTemplate(item.getTemplateId()) == null) { throw new ServiceException(QC_TEMPLATE_PROCESS_NOT_EXISTS.getCode(), StrUtil.format("工序 ID={} 绑定的质检方案 ID={} 不存在;请刷新质检方案后重新选择", processId, item.getTemplateId())); } String key = item.getQcType() + ":" + item.getTemplateId(); if (!keys.add(key)) { throw new ServiceException(QC_TEMPLATE_PROCESS_DUPLICATE.getCode(), StrUtil.format("工序 ID={} 在质检种类[{}]下重复绑定了质检方案 ID={};请删除重复行后重试", processId, qcTypeName(item.getQcType()), item.getTemplateId())); } if (Boolean.TRUE.equals(item.getDefaultFlag())) { String defaultKey = String.valueOf(item.getQcType()); if (!defaultKeys.add(defaultKey)) { throw new ServiceException(QC_TEMPLATE_PROCESS_DUPLICATE.getCode(), StrUtil.format("工序 ID={} 在质检种类[{}]下只能设置一个默认方案;请取消多余的默认标记后重试", processId, qcTypeName(item.getQcType()))); } } } } }