package cn.iocoder.yudao.module.mes.service.qc.outsourcetest; import java.util.List; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; 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.outsourcetest.vo.MesQcOutsourceTestPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.outsourcetest.MesQcOutsourceTestDO; import cn.iocoder.yudao.module.mes.dal.mysql.qc.outsourcetest.MesQcOutsourceTestMapper; import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum; import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.Collection; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestImportExcelVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestImportRespVO; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.hutool.core.util.StrUtil; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * MES 委外检测/试验单 Service 实现类 * * @author xiaoyi */ @Service @Validated @Slf4j public class MesQcOutsourceTestServiceImpl implements MesQcOutsourceTestService { /** * 附件关联业务类型 */ private static final String ATTACHMENT_BIZ_TYPE = "file"; /** * 附件关联业务记录类型 */ private static final String ATTACHMENT_RECORD_TYPE = "mes_qc_outsource_test"; /** * 状态:草稿 */ private static final Integer STATUS_DRAFT = 0; /** * 状态:已提交 */ private static final Integer STATUS_SUBMITTED = 10; /** * 状态:已完成 */ private static final Integer STATUS_FINISHED = 20; @Resource private MesQcOutsourceTestMapper testMapper; @Resource private MesMdItemService itemService; @Resource private StorageAttachmentApi storageAttachmentApi; @Resource private MesMdAutoCodeRecordService autoCodeRecordService; @Override @Transactional(rollbackFor = Exception.class) public Long createTest(MesQcOutsourceTestSaveReqVO createReqVO) { // 0. 校验检测对象(物料)存在 validateItemIfPresent(createReqVO.getItemId()); // 1. 生成单号 String code = generateCode(); if (testMapper.selectByCode(code) != null) { throw exception(PRO_WORK_ORDER_CODE_DUPLICATE); } // 2. 插入 MesQcOutsourceTestDO test = BeanUtils.toBean(createReqVO, MesQcOutsourceTestDO.class, req -> req .setCode(code) .setStatus(STATUS_DRAFT)); testMapper.insert(test); // 3. 绑定附件 storageAttachmentApi.bindAttachments(ATTACHMENT_BIZ_TYPE, ATTACHMENT_RECORD_TYPE, test.getId(), createReqVO.getBlobIds()); return test.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateTest(MesQcOutsourceTestSaveReqVO updateReqVO) { // 1. 校验存在 + 草稿 + 检测对象(物料)存在 MesQcOutsourceTestDO oldTest = validateTestCanModify(updateReqVO.getId()); validateItemIfPresent(updateReqVO.getItemId()); // 2. 更新 MesQcOutsourceTestDO updateObj = BeanUtils.toBean(updateReqVO, MesQcOutsourceTestDO.class, req -> req .setCode(oldTest.getCode()) .setStatus(oldTest.getStatus())); testMapper.updateById(updateObj); // 3. 重新绑定附件 storageAttachmentApi.bindAttachments(ATTACHMENT_BIZ_TYPE, ATTACHMENT_RECORD_TYPE, updateReqVO.getId(), updateReqVO.getBlobIds()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteTest(Collection ids) { if (CollUtil.isEmpty(ids)) { return; } for (Long id : ids) { validateTestCanModify(id); testMapper.deleteById(id); } } @Override public MesQcOutsourceTestDO getTest(Long id) { return testMapper.selectById(id); } @Override public PageResult getTestPage(MesQcOutsourceTestPageReqVO pageReqVO) { return testMapper.selectPage(pageReqVO); } @Override @Transactional(rollbackFor = Exception.class) public void submitTest(Long id) { MesQcOutsourceTestDO test = validateTestExists(id); if (!Objects.equals(test.getStatus(), STATUS_DRAFT)) { throw exception(PRO_WORK_ORDER_NOT_PREPARE); } testMapper.updateById(new MesQcOutsourceTestDO().setId(id).setStatus(STATUS_SUBMITTED)); } @Override @Transactional(rollbackFor = Exception.class) public void finishTest(Long id) { MesQcOutsourceTestDO test = validateTestExists(id); if (!Objects.equals(test.getStatus(), STATUS_SUBMITTED)) { throw exception(PRO_WORK_ORDER_NOT_CONFIRMED); } testMapper.updateById(new MesQcOutsourceTestDO().setId(id).setStatus(STATUS_FINISHED)); } private void validateItemIfPresent(Long itemId) { if (itemId != null) { itemService.validateItemExists(itemId); } } private String generateCode() { try { return autoCodeRecordService.generateAutoCode( MesMdAutoCodeRuleCodeEnum.QC_OUTSOURCE_TEST_CODE.getCode()); } catch (Exception e) { log.warn("委外检测编码规则 {} 不可用,降级使用时间戳编码", MesMdAutoCodeRuleCodeEnum.QC_OUTSOURCE_TEST_CODE.getCode(), e); return "WT" + DateUtil.format(java.time.LocalDateTime.now(), "yyyyMMddHHmmssSSS"); } } private MesQcOutsourceTestDO validateTestExists(Long id) { MesQcOutsourceTestDO test = testMapper.selectById(id); if (test == null) { throw exception(PRO_WORK_ORDER_NOT_EXISTS); } return test; } private MesQcOutsourceTestDO validateTestCanModify(Long id) { MesQcOutsourceTestDO test = validateTestExists(id); if (!Objects.equals(test.getStatus(), STATUS_DRAFT)) { throw exception(PRO_WORK_ORDER_NOT_PREPARE); } return test; } @Override public MesQcOutsourceTestImportRespVO importTestList(List importList, boolean updateSupport) { if (CollUtil.isEmpty(importList)) { throw exception(QC_OUTSOURCE_TEST_IMPORT_LIST_IS_EMPTY); } MesQcOutsourceTestImportRespVO respVO = MesQcOutsourceTestImportRespVO.builder() .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>()) .failureCodes(new LinkedHashMap<>()).build(); AtomicInteger index = new AtomicInteger(1); importList.forEach(importItem -> { int currentIndex = index.getAndIncrement(); String key = "第 " + currentIndex + " 行"; if (StrUtil.isBlank(importItem.getName())) { respVO.getFailureCodes().put(key, "单据名称不能为空"); return; } if (importItem.getTestType() == null) { respVO.getFailureCodes().put(key, "检测类型不能为空"); return; } if (importItem.getTestDate() == null) { respVO.getFailureCodes().put(key, "检测日期不能为空"); return; } Long itemId = null; if (StrUtil.isNotBlank(importItem.getItemCode())) { cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.getItemByCode(importItem.getItemCode()); if (item == null) { respVO.getFailureCodes().put(key, "物料不存在:" + importItem.getItemCode()); return; } itemId = item.getId(); } try { MesQcOutsourceTestSaveReqVO createReqVO = BeanUtils.toBean(importItem, MesQcOutsourceTestSaveReqVO.class); createReqVO.setItemId(itemId); Long id = createTest(createReqVO); MesQcOutsourceTestDO created = testMapper.selectById(id); respVO.getCreateCodes().add(created != null && StrUtil.isNotBlank(created.getCode()) ? created.getCode() : key); } catch (ServiceException e) { respVO.getFailureCodes().put(key, e.getMessage()); } }); return respVO; } }