| package com.dy.pmsProduct.process; | 
|   | 
| import com.dy.common.webFilter.UserTokenContext; | 
| import com.dy.common.webUtil.QueryResultVo; | 
| import com.dy.pmsGlobal.daoOth.OthFileMapper; | 
| import com.dy.pmsGlobal.daoPr.PrAssemblyPlanMapper; | 
| import com.dy.pmsGlobal.daoPr.PrProductionNodeMapper; | 
| import com.dy.pmsGlobal.daoPr.PrProductionProcessMapper; | 
| import com.dy.pmsGlobal.daoPr.PrWorkingInstructionMapper; | 
| import com.dy.pmsGlobal.dyFile.FileOperate; | 
| import com.dy.pmsGlobal.dyFile.FileRestVo; | 
| import com.dy.pmsGlobal.pojoBa.BaUser; | 
| import com.dy.pmsGlobal.pojoOth.OthFile; | 
| import com.dy.pmsGlobal.pojoPr.PrAssemblyPlan; | 
| import com.dy.pmsGlobal.pojoPr.PrProductionNode; | 
| import com.dy.pmsGlobal.pojoPr.PrProductionProcess; | 
| import com.dy.pmsGlobal.pojoPr.PrWorkingInstruction; | 
| import com.dy.pmsGlobal.util.UserUtil; | 
| import com.dy.pmsProduct.taskPlan.PlanStatusEnum; | 
| import lombok.extern.slf4j.Slf4j; | 
| import org.apache.commons.collections4.CollectionUtils; | 
| import org.apache.dubbo.common.utils.PojoUtils; | 
| import org.springframework.beans.factory.annotation.Autowired; | 
| import org.springframework.beans.factory.annotation.Value; | 
| import org.springframework.dao.DuplicateKeyException; | 
| import org.springframework.stereotype.Service; | 
| import org.springframework.transaction.annotation.Transactional; | 
|   | 
| import java.util.ArrayList; | 
| import java.util.List; | 
| import java.util.Map; | 
| import java.util.concurrent.atomic.AtomicInteger; | 
| import java.util.stream.Collectors; | 
|   | 
| @Slf4j | 
| @Service | 
| public class ProcessSv { | 
|     private PrAssemblyPlanMapper assemblyDao; | 
|     private PrProductionProcessMapper processDao; | 
|     private PrProductionNodeMapper nodeDao; | 
|     private PrWorkingInstructionMapper workDao; | 
|     private UserUtil userUtil; | 
|     private FileOperate fileOperate; | 
|     private OthFileMapper othFileMapper; | 
|     @Value("${dy.webFile.fmUrl}") | 
|     private String fmUrl; | 
|   | 
|     @Autowired | 
|     public void setAssemblyDao(PrAssemblyPlanMapper assemblyDao) { | 
|         this.assemblyDao = assemblyDao; | 
|     } | 
|   | 
|     @Autowired | 
|     public void setProcessDao(PrProductionProcessMapper dao) { | 
|         processDao = dao; | 
|     } | 
|   | 
|     @Autowired | 
|     public void setNodeDao(PrProductionNodeMapper dao) { | 
|         nodeDao = dao; | 
|     } | 
|   | 
|     @Autowired | 
|     public void setWorkDao(PrWorkingInstructionMapper dao) { | 
|         workDao = dao; | 
|     } | 
|   | 
|     @Autowired | 
|     public void setUserUtil(UserUtil userUtil) { | 
|         this.userUtil = userUtil; | 
|     } | 
|   | 
|     @Autowired | 
|     public void setFileOperate(FileOperate fileOperate) { | 
|         this.fileOperate = fileOperate; | 
|     } | 
|   | 
|     @Autowired | 
|     public void setOthFileMapper(OthFileMapper othFileMapper) { | 
|         this.othFileMapper = othFileMapper; | 
|     } | 
|   | 
|     @Transactional | 
|     public int save(PrProductionProcess process) { | 
|         //流程名称不能重复 | 
|         if (processDao.exists(process.name, process.id)) { | 
|             throw new RuntimeException("流程名称不能重复"); | 
|         } | 
|         prepareProcess(process); | 
|         int count = processDao.insertSelective(process); | 
|         saveNodesAndInstructions(process); | 
|         return count; | 
|     } | 
|   | 
|     @Transactional | 
|     public int update(PrProductionProcess process) { | 
|         //流程名称不能重复 | 
|         if (processDao.exists(process.name, process.id)) { | 
|             throw new RuntimeException("流程名称不能重复"); | 
|         } | 
|         PrProductionProcess originProductionProcess = processDao.selectByPrimaryKey(process.id); | 
|         //如果已经绑定组装任务计划(目前不包括暂停\结束状态 投入数为0的任务 以外的 所有任务),产品\节点id不能删除不能修改 | 
|         PrAssemblyPlan params = new PrAssemblyPlan(); | 
|         params.setProcessId(process.id); | 
|         List<PrAssemblyPlan> planList = assemblyDao.selectAssyPlanSimplify(params); | 
|         List<PrAssemblyPlan> onlinePlanList = planList.stream().filter(plan -> plan.getStatus() == PlanStatusEnum.NORMAL.getCode()).collect(Collectors.toList()); | 
|         if (CollectionUtils.isNotEmpty(planList)) { | 
|             if (planList.stream().anyMatch(plan -> plan.getInputNumber() > 0)) { //存在任务已经投入生产 | 
|                 if (!originProductionProcess.getProId().equals(process.getProId())) { | 
|                     throw new RuntimeException("存在已经投入生产的绑定任务,产品不能修改"); | 
|                 } | 
|                 //节点id不能删除 | 
|                 if (!originProductionProcess.getNodes().stream().allMatch(node -> process.getNodes().stream().anyMatch(newNode ->  node.getId().equals(newNode.getId())))) { | 
|                     throw new RuntimeException("已有绑定的任务投入生产,节点不能删除"); | 
|                 } | 
|             } else if (CollectionUtils.isNotEmpty(onlinePlanList)) { | 
|                 //先将组装任务置为暂停状态 | 
|                 onlinePlanList.forEach(plan -> { | 
|                     plan.status = PlanStatusEnum.PAUSE.getCode(); | 
|                     assemblyDao.updateByPrimaryKeySelective(plan); | 
|                 }); | 
|             } | 
|         } | 
|         prepareProcess(process); | 
|         int count = processDao.updateByPrimaryKeySelective(process); | 
|         // 优化:只有当节点有变更时    根据ID更新内容 没有匹配到的ID 删除  多余的ID 新增 | 
|   | 
|         List<Long> nodeIdsToDelete = originProductionProcess.getNodes().stream() | 
|                 .filter(node -> process.nodes.stream().filter(newNode ->  node.getId().equals(newNode.getId())).count() == 0) | 
|                 .map(PrProductionNode::getId) // 映射节点到其ID | 
|                 .collect(Collectors.toList()); | 
|         if (CollectionUtils.isNotEmpty(nodeIdsToDelete)) { | 
|             nodeDao.deleteByNodeId(nodeIdsToDelete); | 
|         } | 
|         //原来节点的SOP全部删除 | 
|         List<Long> originNodeIds = originProductionProcess.getNodes().stream() | 
|                 .map(PrProductionNode::getId) // 映射节点到其ID | 
|                 .collect(Collectors.toList()); | 
|         if (CollectionUtils.isNotEmpty(originNodeIds)) { | 
|             workDao.deleteByNodeId(originNodeIds); | 
|         } | 
|         saveNodesAndInstructions(process); | 
|         return count; | 
|     } | 
|   | 
|     // 提取共通逻辑到单独方法以减少代码重复 | 
|     private void prepareProcess(PrProductionProcess process) { | 
|         process.disabled = false; | 
|         process.deleted = false; | 
|         BaUser loginUser = userUtil.getUser(UserTokenContext.get()); | 
|         if (loginUser != null) { | 
|             process.creator = loginUser.id; | 
|         } | 
|         AtomicInteger startCount = new AtomicInteger(); | 
|         AtomicInteger endCount = new AtomicInteger(); | 
|         process.nodes.forEach(node -> { | 
|             node.processId = process.id; | 
|             node.deleted = false; | 
|             if(node.isStart){ | 
|                 startCount.getAndIncrement(); | 
|             } | 
|             if(node.isEnd){ | 
|                 endCount.getAndIncrement(); | 
|             } | 
|         }); | 
|         if (startCount.get() != 1 || endCount.get() != 1) { | 
|             throw new RuntimeException("节点开始和结束节点有且只能有一个"); | 
|         } | 
|     } | 
|   | 
|     // 将节点和工作指示的保存逻辑封装到一个方法中 | 
|     private void saveNodesAndInstructions(PrProductionProcess process) { | 
|         List<PrProductionNode> haveIdList = new ArrayList<>(); | 
|         List<PrProductionNode> noIdList = new ArrayList<>(); | 
|   | 
|         process.nodes.forEach(node -> { | 
|             node.processId = process.id; | 
|             if (node.getId() != null && node.getId() != 0) { | 
|                 haveIdList.add(node); | 
|             } else { | 
|                 noIdList.add(node); | 
|             } | 
|         }); | 
|         try { | 
|             if (haveIdList.size() > 0) { | 
|                 for (int i = 0; i < haveIdList.size(); i++) { | 
|                     nodeDao.updateByPrimaryKeySelective(haveIdList.get(i)); | 
|                 } | 
|             } | 
|             if (noIdList.size() > 0) {//CollectionUtils.isNotEmpty(noIdList) | 
|                 nodeDao.insertMany(noIdList); | 
|             } | 
|         } catch (DuplicateKeyException e) { | 
|             throw new RuntimeException("节点顺序重复"); | 
|         } | 
|         List<PrWorkingInstruction> workList = process.nodes.stream().map(node -> { | 
|             if (node.instruction != null) { | 
|                 node.instruction.nodeId = node.id; | 
|                 return node.instruction; | 
|             } | 
|             return null; | 
|         }).filter(work -> work != null).toList(); | 
|         if (CollectionUtils.isNotEmpty(workList)) { | 
|             workDao.insertMany(workList); | 
|         } | 
|     } | 
|   | 
|     public int delete(Long id) { | 
|         return processDao.deleteLogicById(id); | 
|     } | 
|   | 
|     public PrProductionProcess selectById(Long id) { | 
|         PrProductionProcess process = processDao.selectByPrimaryKey(id); | 
|         if (process != null) { | 
|             process.nodes.forEach(node -> { | 
|                 if (node.instruction != null) { | 
|                     addUrl(node.instruction); | 
|                 } | 
|             }); | 
|         } | 
|         return process; | 
|     } | 
|   | 
|     public QueryResultVo<List<PrProductionProcess>> selectSome(QueryVo queryVo) { | 
|         Map<String, Object> params = (Map<String, Object>) PojoUtils.generalize(queryVo); | 
|   | 
|         //查询符合条件的记录总数 | 
|         Long itemTotal = processDao.selectSomeCount(params); | 
|   | 
|         QueryResultVo<List<PrProductionProcess>> rsVo = new QueryResultVo<>(queryVo.pageSize, queryVo.pageCurr); | 
|         //计算分页等信息 | 
|         rsVo.calculateAndSet(itemTotal, params); | 
|   | 
|         //查询符合条件的记录 | 
|         rsVo.obj = processDao.selectSome(params); | 
|         rsVo.obj.stream().forEach(process -> { | 
|             process.nodes.forEach(node -> { | 
|                 if (node.instruction != null) { | 
|                     addUrl(node.instruction); | 
|                 } | 
|             }); | 
|         }); | 
|         return rsVo; | 
|     } | 
|   | 
|     public List<Map<String, String>> queryAll(Long proId) { | 
|         return processDao.queryAll(proId); | 
|     } | 
|   | 
|     private void addUrl(PrWorkingInstruction ins) { | 
|         if (ins == null || ins.fileId == null) { | 
|             return; | 
|         } | 
|         OthFile file = othFileMapper.selectByPrimaryKey(ins.fileId); | 
|         if (file == null) { | 
|             return; | 
|         } | 
|         FileRestVo fileRestVo = fileOperate.parseHashcode(fmUrl, file.hash); | 
|         ins.webUrl = fileRestVo.fileSysRestUrl + fileRestVo.fileWebDownloadPath + ins.fileId; | 
|         ins.orgName = file.orgName; | 
|         ins.extName = file.extName; | 
|     } | 
| } |