package cn.iocoder.yudao.module.mes.service.qc.ncr; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcNcrDO; import cn.iocoder.yudao.module.mes.dal.mysql.qc.ncr.MesQcNcrMapper; import cn.iocoder.yudao.module.mes.enums.qc.MesNcrStatusEnum; import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService; import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService; import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService; import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService; import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.Collection; import java.util.Map; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 不合格品处理单(NCR) Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesQcNcrServiceImpl implements MesQcNcrService { @Resource private MesQcNcrMapper ncrMapper; @Resource private MdmItemApi mdmItemApi; @Resource @Lazy private MesQcIqcService iqcService; @Resource @Lazy private MesQcIpqcService ipqcService; @Resource @Lazy private MesQcOqcService oqcService; @Resource @Lazy private MesQcRqcService rqcService; @Override @Transactional(rollbackFor = Exception.class) public Long createNcr(MesQcNcrSaveReqVO createReqVO) { // 1. 校验数据 validateNcrCodeUnique(null, createReqVO.getCode()); validateItemExists(createReqVO.getItemId()); // 2. 插入主表 MesQcNcrDO ncr = BeanUtils.toBean(createReqVO, MesQcNcrDO.class) .setStatus(MesNcrStatusEnum.PENDING_REVIEW.getStatus()); // 如果传入了 qcId 和 qcType,则查询来源检验单编号并冗余存储 if (createReqVO.getQcType() != null && createReqVO.getQcId() != null) { ncr.setQcCode(lookupQcCode(createReqVO.getQcType(), createReqVO.getQcId())); } ncrMapper.insert(ncr); return ncr.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateNcr(MesQcNcrSaveReqVO updateReqVO) { // 1. 校验存在 + 只允许待评审状态编辑 MesQcNcrDO ncr = validateNcrStatusPendingReview(updateReqVO.getId()); validateNcrCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); validateItemExists(updateReqVO.getItemId()); // 2. 更新主表(仅允许更新部分字段) MesQcNcrDO updateObj = BeanUtils.toBean(updateReqVO, MesQcNcrDO.class); updateObj.setQcType(null); // 不允许修改检验类型 updateObj.setQcId(null); // 不允许修改检验单 updateObj.setStatus(null); // 不允许修改状态 ncrMapper.updateById(updateObj); } @Override @Transactional(rollbackFor = Exception.class) public void deleteNcr(Long id) { // 校验存在 + 只允许删除待评审状态 validateNcrStatusPendingReview(id); ncrMapper.deleteById(id); } @Override @Transactional(rollbackFor = Exception.class) public void reviewNcr(Long id, Integer disposition, Long responsibleDeptId, Integer defectLevel, String reviewOpinion) { // 1. 校验存在 + 待评审状态 validateNcrStatusPendingReview(id); // 2. 更新评审信息 MesQcNcrDO updateObj = new MesQcNcrDO() .setId(id) .setDisposition(disposition) .setResponsibleDeptId(responsibleDeptId) .setReviewOpinion(reviewOpinion) .setReviewerUserId(getLoginUserId()) .setReviewDate(LocalDateTime.now()) .setStatus(MesNcrStatusEnum.REVIEWED.getStatus()); if (defectLevel != null) { updateObj.setDefectLevel(defectLevel); } ncrMapper.updateById(updateObj); } @Override @Transactional(rollbackFor = Exception.class) public void disposeNcr(Long id, Long handlerUserId, String handleResult) { // 1. 校验存在 + 已评审状态 MesQcNcrDO ncr = validateNcrExists(id); if (!MesNcrStatusEnum.REVIEWED.getStatus().equals(ncr.getStatus())) { throw exception(QC_NCR_NOT_REVIEWED); } // 2. 更新处置信息 // TODO: WM 模块集成 — 处置为退货时触发供应商退货,处置为报废时触发杂项出库 ncrMapper.updateById(new MesQcNcrDO() .setId(id) .setHandlerUserId(handlerUserId) .setHandleDate(LocalDateTime.now()) .setHandleResult(handleResult) .setStatus(MesNcrStatusEnum.DISPOSED.getStatus())); } @Override @Transactional(rollbackFor = Exception.class) public void closeNcr(Long id, String closeOpinion) { // 1. 校验存在 + 已处置状态 MesQcNcrDO ncr = validateNcrExists(id); if (!MesNcrStatusEnum.DISPOSED.getStatus().equals(ncr.getStatus())) { throw exception(QC_NCR_NOT_DISPOSED); } // 2. 更新关闭信息 ncrMapper.updateById(new MesQcNcrDO() .setId(id) .setCloseUserId(getLoginUserId()) .setCloseDate(LocalDateTime.now()) .setCloseOpinion(closeOpinion) .setStatus(MesNcrStatusEnum.CLOSED.getStatus())); } // ==================== 校验方法 ==================== @Override public MesQcNcrDO validateNcrExists(Long id) { MesQcNcrDO ncr = ncrMapper.selectById(id); if (ncr == null) { throw exception(QC_NCR_NOT_EXISTS); } return ncr; } /** * 校验 NCR 存在且为待评审状态 */ private MesQcNcrDO validateNcrStatusPendingReview(Long id) { MesQcNcrDO ncr = validateNcrExists(id); if (!MesNcrStatusEnum.PENDING_REVIEW.getStatus().equals(ncr.getStatus())) { throw exception(QC_NCR_NOT_PENDING_REVIEW); } return ncr; } /** * 校验 NCR 编号唯一 */ private void validateNcrCodeUnique(Long id, String code) { MesQcNcrDO existing = ncrMapper.selectByCode(code); if (existing != null && !existing.getId().equals(id)) { throw exception(QC_NCR_CODE_DUPLICATE); } } /** * 校验物料存在 */ private void validateItemExists(Long itemId) { if (itemId != null) { mdmItemApi.getItem(itemId).getCheckedData(); } } /** * 根据检验类型和检验单 ID 查询来源检验单编号 */ private String lookupQcCode(Integer qcType, Long qcId) { if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) { return iqcService.getIqc(qcId).getCode(); } else if (Objects.equals(qcType, MesQcTypeEnum.IPQC.getType())) { return ipqcService.getIpqc(qcId).getCode(); } else if (Objects.equals(qcType, MesQcTypeEnum.OQC.getType())) { return oqcService.getOqc(qcId).getCode(); } else if (Objects.equals(qcType, MesQcTypeEnum.RQC.getType())) { return rqcService.getRqc(qcId).getCode(); } return null; } // ==================== 查询方法 ==================== @Override public MesQcNcrDO getNcr(Long id) { return ncrMapper.selectById(id); } @Override public PageResult getNcrPage(MesQcNcrPageReqVO pageReqVO) { return ncrMapper.selectPage(pageReqVO); } @Override public Map getNcrMap(Collection ids) { return convertMap(ncrMapper.selectBatchIds(ids), MesQcNcrDO::getId); } }