From eee03bbfea8b15d1a56ab63bd09a35c6c861c4cb Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 09 十一月 2026 13:01:08 +0800
Subject: [PATCH] feat(mes): 不合格品处置联动入库与生产订单
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java | 133 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 133 insertions(+), 0 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java
index 104fb0a..9db67d1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java
@@ -1,10 +1,15 @@
package cn.iocoder.yudao.module.mes.service.md.item;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportExcelVO;
+import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomSaveReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdProductBomMapper;
import jakarta.annotation.Resource;
@@ -141,4 +146,132 @@
productBomMapper.deleteByItemId(itemId);
}
+ @Override
+ public MesMdProductBomImportRespVO importProductBomList(List<MesMdProductBomImportExcelVO> importList,
+ Boolean updateSupport) {
+ if (CollUtil.isEmpty(importList)) {
+ throw exception(MD_PRODUCT_BOM_IMPORT_LIST_IS_EMPTY);
+ }
+ MesMdProductBomImportRespVO respVO = MesMdProductBomImportRespVO.builder()
+ .createList(new ArrayList<>()).updateList(new ArrayList<>())
+ .failureList(new LinkedHashMap<>()).build();
+
+ // 1. 鐜版湁 BOM 鐨勭埗瀛愮粍鍚堢储寮� + 閭绘帴琛紙鐢ㄤ簬鍒ら噸鍜岄棴鐜娴嬶級
+ Map<String, MesMdProductBomDO> existingPairMap = new HashMap<>();
+ Map<Long, Set<Long>> graph = new HashMap<>();
+ for (MesMdProductBomDO bom : productBomMapper.selectAll()) {
+ existingPairMap.put(pairKey(bom.getItemId(), bom.getBomItemId()), bom);
+ graph.computeIfAbsent(bom.getItemId(), key -> new HashSet<>()).add(bom.getBomItemId());
+ }
+ Map<String, Optional<MesMdItemDO>> itemCache = new HashMap<>();
+ Set<String> filePairs = new HashSet<>();
+
+ // 2. 閫愯鏍¢獙 + 瀵煎叆
+ int rowNo = 0;
+ for (MesMdProductBomImportExcelVO importVO : importList) {
+ rowNo++;
+ String parentCode = StrUtil.trim(importVO.getParentItemCode());
+ String componentCode = StrUtil.trim(importVO.getBomItemCode());
+ String rowKey = "绗�" + rowNo + "琛� " + StrUtil.blankToDefault(parentCode, "(绌虹埗椤�)")
+ + "鈫�" + StrUtil.blankToDefault(componentCode, "(绌哄瓙椤�)");
+ // 2.1 鍩虹鏍¢獙
+ if (StrUtil.isBlank(parentCode) || StrUtil.isBlank(componentCode)) {
+ respVO.getFailureList().put("绗�" + rowNo + "琛�",
+ StrUtil.isBlank(parentCode) && StrUtil.isBlank(componentCode) ? "鐖堕」鐗╂枡缂栫爜鍜屽瓙椤圭墿鏂欑紪鐮侀兘涓嶈兘涓虹┖"
+ : StrUtil.isBlank(parentCode) ? "鐖堕」鐗╂枡缂栫爜涓嶈兘涓虹┖" : "瀛愰」鐗╂枡缂栫爜涓嶈兘涓虹┖");
+ continue;
+ }
+ if (importVO.getQuantity() == null || importVO.getQuantity().signum() <= 0) {
+ respVO.getFailureList().put(rowKey, "鍗曚綅鐢ㄩ噺蹇呴』澶т簬 0锛屽綋鍓嶄负锛�"
+ + (importVO.getQuantity() == null ? "绌�" : importVO.getQuantity().toPlainString()));
+ continue;
+ }
+ // 2.2 鐖堕」銆佸瓙椤瑰繀椤诲凡鍦� MES 鐗╂枡涓淮鎶�
+ MesMdItemDO parent = lookupItem(itemCache, parentCode);
+ MesMdItemDO component = lookupItem(itemCache, componentCode);
+ if (parent == null) {
+ respVO.getFailureList().put(rowKey, "鐖堕」鐗╂枡缂栫爜[" + parentCode
+ + "]鍦ㄧ敓浜х墿鏂欑鐞嗕腑涓嶅瓨鍦紝璇峰厛缁存姢璇ョ墿鏂欏悗鍐嶅鍏�");
+ continue;
+ }
+ if (component == null) {
+ respVO.getFailureList().put(rowKey, "瀛愰」鐗╂枡缂栫爜[" + componentCode
+ + "]鍦ㄧ敓浜х墿鏂欑鐞嗕腑涓嶅瓨鍦紝璇峰厛缁存姢璇ョ墿鏂欏悗鍐嶅鍏�");
+ continue;
+ }
+ if (parent.getId().equals(component.getId())) {
+ respVO.getFailureList().put(rowKey, "鐖堕」涓庡瓙椤逛笉鑳芥槸鍚屼竴鐗╂枡[" + parentCode + "]");
+ continue;
+ }
+ String pairKey = pairKey(parent.getId(), component.getId());
+ // 2.3 宸插瓨鍦ㄧ殑鐖跺瓙缁勫悎锛氶粯璁よ烦杩囷紝鍕鹃�夆�滄敮鎸佹洿鏂扳�濇椂鍙洿鏂扮敤閲�/澶囨敞锛堢姸鎬佷繚鎸佸師鍊硷級
+ MesMdProductBomDO existing = existingPairMap.get(pairKey);
+ if (existing != null) {
+ if (!Boolean.TRUE.equals(updateSupport)) {
+ respVO.getFailureList().put(rowKey, "璇ョ埗瀛愮粍鍚堝凡瀛樺湪 BOM锛屾湰娆℃湭鏇存柊锛�"
+ + "濡傞渶瑕嗙洊鐢ㄩ噺璇峰嬀閫夆�滄敮鎸佹洿鏂扳�濆悗閲嶆柊瀵煎叆");
+ continue;
+ }
+ MesMdProductBomDO updateObj = new MesMdProductBomDO().setId(existing.getId())
+ .setQuantity(importVO.getQuantity())
+ .setRemark(StrUtil.blankToDefault(importVO.getRemark(), existing.getRemark()));
+ productBomMapper.updateById(updateObj);
+ respVO.getUpdateList().add(parentCode + "鈫�" + componentCode);
+ continue;
+ }
+ // 2.4 鏂囦欢鍐呴噸澶�
+ if (filePairs.contains(pairKey)) {
+ respVO.getFailureList().put(rowKey, "鍚屼竴鐖跺瓙缁勫悎鍦ㄦ枃浠朵腑閲嶅鍑虹幇锛岃鍚堝苟涓轰竴琛屽悗閲嶆柊瀵煎叆");
+ continue;
+ }
+ // 2.5 闂幆妫�娴嬶紙鍩轰簬鐜版湁 BOM + 鏈枃浠跺凡鎺ュ彈鐨勮锛�
+ if (reaches(graph, component.getId(), parent.getId())) {
+ respVO.getFailureList().put(rowKey, "鏂板鍚� BOM 灏嗗舰鎴愬惊鐜紩鐢紙" + parentCode
+ + "鈫�" + componentCode + "锛夛紝璇疯皟鏁寸埗瀛愬叧绯诲悗閲嶆柊瀵煎叆");
+ continue;
+ }
+ // 2.6 鏂板
+ MesMdProductBomSaveReqVO saveReqVO = new MesMdProductBomSaveReqVO();
+ saveReqVO.setItemId(parent.getId());
+ saveReqVO.setBomItemId(component.getId());
+ saveReqVO.setQuantity(importVO.getQuantity());
+ saveReqVO.setStatus(0); // 瀵煎叆鐨� BOM 榛樿鍚敤
+ saveReqVO.setRemark(StrUtil.blankToDefault(importVO.getRemark(), null));
+ createProductBom(saveReqVO);
+ filePairs.add(pairKey);
+ graph.computeIfAbsent(parent.getId(), key -> new HashSet<>()).add(component.getId());
+ respVO.getCreateList().add(parentCode + "鈫�" + componentCode);
+ }
+ return respVO;
+ }
+
+ private MesMdItemDO lookupItem(Map<String, Optional<MesMdItemDO>> cache, String code) {
+ return cache.computeIfAbsent(code,
+ key -> Optional.ofNullable(itemService.getItemByCode(key))).orElse(null);
+ }
+
+ private static String pairKey(Long itemId, Long bomItemId) {
+ return itemId + ":" + bomItemId;
+ }
+
+ /** 娌跨幇鏈夛紙鍚湰娆″凡鎺ュ彈鐨勶級鐖跺瓙杈逛粠 start 鍑哄彂锛屽垽鏂兘鍚﹀埌杈� target */
+ private static boolean reaches(Map<Long, Set<Long>> graph, Long start, Long target) {
+ Deque<Long> stack = new ArrayDeque<>();
+ Set<Long> visited = new HashSet<>();
+ stack.push(start);
+ while (!stack.isEmpty()) {
+ Long node = stack.pop();
+ if (!visited.add(node)) {
+ continue;
+ }
+ if (node.equals(target)) {
+ return true;
+ }
+ for (Long next : graph.getOrDefault(node, Set.of())) {
+ stack.push(next);
+ }
+ }
+ return false;
+ }
+
}
--
Gitblit v1.9.3