package cn.iocoder.yudao.module.mes.service.pro.workorder; import cn.hutool.core.collection.CollUtil; 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.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper; import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderProcessMapper; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService; 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.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 生产工单工序 Service 实现类 * * @author 芋道源码 */ @Service @Validated public class MesProWorkOrderProcessServiceImpl implements MesProWorkOrderProcessService { @Resource private MesProWorkOrderProcessMapper workOrderProcessMapper; @Resource @Lazy private MesProWorkOrderService workOrderService; @Resource private MesProProcessService processService; @Resource private MesProRouteProcessService routeProcessService; @Resource @Lazy private MesProWorkOrderBomService workOrderBomService; @Resource private MesProWorkOrderBomMapper workOrderBomMapper; @Override public Long createWorkOrderProcess(MesProWorkOrderProcessSaveReqVO createReqVO) { // 1. 校验数据 validateWorkOrderProcessSaveData(createReqVO); // 2. 插入数据 MesProWorkOrderProcessDO workOrderProcess = BeanUtils.toBean(createReqVO, MesProWorkOrderProcessDO.class); workOrderProcessMapper.insert(workOrderProcess); // 3. 重建工序链 rebuildProcessChain(createReqVO.getWorkOrderId()); return workOrderProcess.getId(); } @Override public void updateWorkOrderProcess(MesProWorkOrderProcessSaveReqVO updateReqVO) { // 1. 校验存在 + 数据 validateWorkOrderProcessExists(updateReqVO.getId()); validateWorkOrderProcessSaveData(updateReqVO); // 2. 更新数据 MesProWorkOrderProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderProcessDO.class); workOrderProcessMapper.updateById(updateObj); // 3. 重建工序链 rebuildProcessChain(updateReqVO.getWorkOrderId()); } @Override public void deleteWorkOrderProcess(Long id) { // 1. 校验存在 MesProWorkOrderProcessDO workOrderProcess = validateWorkOrderProcessExists(id); // 2. 删除 workOrderProcessMapper.deleteById(id); // 3. 重建工序链 rebuildProcessChain(workOrderProcess.getWorkOrderId()); } @Override public MesProWorkOrderProcessDO getWorkOrderProcess(Long id) { return workOrderProcessMapper.selectById(id); } @Override public PageResult getWorkOrderProcessPage(MesProWorkOrderProcessPageReqVO pageReqVO) { return workOrderProcessMapper.selectPage(pageReqVO); } @Override public List getWorkOrderProcessListByWorkOrderId(Long workOrderId) { return workOrderProcessMapper.selectListByWorkOrderId(workOrderId); } @Override public void deleteWorkOrderProcessByWorkOrderId(Long workOrderId) { workOrderProcessMapper.deleteByWorkOrderId(workOrderId); } @Override public List getProcessListByProductId(Long productId) { // 根据产品获取工艺路线工序列表(不保存,用于前端预填) List routeProcessList = routeProcessService.getRouteProcessListByProductId(productId); if (CollUtil.isEmpty(routeProcessList)) { return Collections.emptyList(); } // 转换为工单工序 DO(不设置 workOrderId,前端保存时再设置) List processList = new ArrayList<>(routeProcessList.size()); for (MesProRouteProcessDO routeProcess : routeProcessList) { processList.add(MesProWorkOrderProcessDO.builder() .processId(routeProcess.getProcessId()) .sort(routeProcess.getSort()) .nextProcessId(routeProcess.getNextProcessId()) .linkType(routeProcess.getLinkType()) .prepareTime(routeProcess.getPrepareTime()) .waitTime(routeProcess.getWaitTime()) .colorCode(routeProcess.getColorCode()) .keyFlag(routeProcess.getKeyFlag()) .checkFlag(routeProcess.getCheckFlag()) .backflushFlag(routeProcess.getBackflushFlag()) .outputItemId(routeProcess.getOutputItemId()) .remark(routeProcess.getRemark()) .build()); } return processList; } @Override @Transactional(rollbackFor = Exception.class) public void batchSaveWorkOrderProcess(List list) { if (CollUtil.isEmpty(list)) { return; } // 获取工单 ID Long workOrderId = list.get(0).getWorkOrderId(); if (workOrderId == null) { throw exception(PRO_WORK_ORDER_NOT_EXISTS); } // 校验工单存在 workOrderService.validateWorkOrderExists(workOrderId); // 1. 获取数据库中现有的工序 List existingList = workOrderProcessMapper.selectListByWorkOrderId(workOrderId); Set existingIds = existingList.stream().map(MesProWorkOrderProcessDO::getId).collect(java.util.stream.Collectors.toSet()); // 2. 找出需要删除的 ID(数据库存在但请求中没有) Set requestIds = list.stream() .filter(item -> item.getId() != null) .map(MesProWorkOrderProcessSaveReqVO::getId) .collect(java.util.stream.Collectors.toSet()); for (Long existingId : existingIds) { if (!requestIds.contains(existingId)) { workOrderProcessMapper.deleteById(existingId); } } // 3. 处理新增和更新 for (MesProWorkOrderProcessSaveReqVO item : list) { if (item.getId() == null) { // 新增 MesProWorkOrderProcessDO process = BeanUtils.toBean(item, MesProWorkOrderProcessDO.class); workOrderProcessMapper.insert(process); } else { // 更新 MesProWorkOrderProcessDO updateObj = BeanUtils.toBean(item, MesProWorkOrderProcessDO.class); workOrderProcessMapper.updateById(updateObj); } } // 4. 保存工单 BOM 投料(合并所有工序的 bomItems,按 itemId 去重) saveWorkOrderBomFromProcesses(workOrderId, list); // 5. 重建工序链 rebuildProcessChain(workOrderId); } /** * 从工序列表中提取 BOM 投料,合并去重后保存到工单 BOM 表 */ private void saveWorkOrderBomFromProcesses(Long workOrderId, List list) { // 收集所有工序的 bomItems,按 itemId 去重(保留第一个出现的 quantity) java.util.Map merged = new java.util.LinkedHashMap<>(); for (MesProWorkOrderProcessSaveReqVO item : list) { if (CollUtil.isNotEmpty(item.getBomItems())) { for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : item.getBomItems()) { if (bomItem.getItemId() != null) { merged.putIfAbsent(bomItem.getItemId(), bomItem); } } } } // 删除旧 BOM,批量插入新 BOM workOrderBomService.deleteWorkOrderBomByWorkOrderId(workOrderId); if (CollUtil.isNotEmpty(merged)) { List bomList = new ArrayList<>(merged.size()); for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : merged.values()) { bomList.add(MesProWorkOrderBomDO.builder() .workOrderId(workOrderId) .itemId(bomItem.getItemId()) .quantity(bomItem.getQuantity()) .remark(bomItem.getRemark()) .build()); } workOrderBomMapper.insertBatch(bomList); } } // ==================== 私有方法 ==================== private MesProWorkOrderProcessDO validateWorkOrderProcessExists(Long id) { MesProWorkOrderProcessDO workOrderProcess = workOrderProcessMapper.selectById(id); if (workOrderProcess == null) { throw exception(PRO_WORK_ORDER_PROCESS_NOT_EXISTS); } return workOrderProcess; } private void validateWorkOrderProcessSaveData(MesProWorkOrderProcessSaveReqVO reqVO) { // 1. 校验工单存在 workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId()); // 2. 校验工序存在 processService.validateProcessExists(reqVO.getProcessId()); // 3. 校验同一工单内序号唯一 List list = workOrderProcessMapper.selectListByWorkOrderId(reqVO.getWorkOrderId()); for (MesProWorkOrderProcessDO process : list) { // 排除自身 if (reqVO.getId() != null && reqVO.getId().equals(process.getId())) { continue; } if (process.getSort().equals(reqVO.getSort())) { throw exception(PRO_WORK_ORDER_PROCESS_SORT_DUPLICATE); } if (process.getProcessId().equals(reqVO.getProcessId())) { throw exception(PRO_WORK_ORDER_PROCESS_DUPLICATE); } } // 4. 校验关键工序唯一 if (Boolean.TRUE.equals(reqVO.getKeyFlag())) { for (MesProWorkOrderProcessDO process : list) { if (reqVO.getId() != null && reqVO.getId().equals(process.getId())) { continue; } if (Boolean.TRUE.equals(process.getKeyFlag())) { throw exception(PRO_WORK_ORDER_PROCESS_KEY_DUPLICATE); } } } } /** * 重建工序链(更新所有工序的 nextProcessId) */ private void rebuildProcessChain(Long workOrderId) { List list = workOrderProcessMapper.selectListByWorkOrderId(workOrderId); if (CollUtil.isEmpty(list)) { return; } // 按 sort 排序 list.sort(Comparator.comparing(MesProWorkOrderProcessDO::getSort)); for (int i = 0; i < list.size(); i++) { MesProWorkOrderProcessDO item = list.get(i); if (i + 1 < list.size()) { item.setNextProcessId(list.get(i + 1).getProcessId()); } else { item.setNextProcessId(null); } workOrderProcessMapper.updateById(item); } } }