From 1936ede088c930ff621d3440a800db7f2be461db Mon Sep 17 00:00:00 2001 From: zhangxuanming <2260476558@qq.com> Date: Wed, 15 Jan 2025 15:37:28 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BA=A7=E5=93=81=E7=89=A9?= =?UTF-8?q?=E6=96=99BOM=E7=BB=84=E6=88=90=E6=96=B0=E5=A2=9E=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E6=A0=A1=E9=AA=8C=E6=95=B4=E4=BD=93=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E9=97=AD=E7=8E=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ktg/common/utils/MapCircleUtils.java | 108 +++++++++++++++--- .../md/controller/MdProductBomController.java | 2 +- .../ktg/mes/md/mapper/MdProductBomMapper.java | 6 + .../mes/md/service/IMdProductBomService.java | 6 +- .../service/impl/MdProductBomServiceImpl.java | 33 +++++- .../mapper/md/MdProductBomMapper.xml | 5 +- 6 files changed, 136 insertions(+), 24 deletions(-) diff --git a/ktg-common/src/main/java/com/ktg/common/utils/MapCircleUtils.java b/ktg-common/src/main/java/com/ktg/common/utils/MapCircleUtils.java index b0ac05f..4b07910 100644 --- a/ktg-common/src/main/java/com/ktg/common/utils/MapCircleUtils.java +++ b/ktg-common/src/main/java/com/ktg/common/utils/MapCircleUtils.java @@ -12,61 +12,135 @@ import java.util.*; @Service public class MapCircleUtils { - private Map> adj; // 邻接表 + private Map> adj; // 邻接表 private MapCircleUtils() { this.adj = new HashMap<>(); } + // 静态工厂方法,用于创建实例 + public static MapCircleUtils createInstance() { + return new MapCircleUtils(); + } public void clear() { adj.clear(); } - public void addEdge(int v, int w) { - adj.computeIfAbsent(v, k -> new ArrayList<>()).add(w); - adj.computeIfAbsent(w, k -> new ArrayList<>()); + /** + * 向图中添加一条从顶点v到顶点w的边(无向图则双向添加) + * @param v 起点 + * @param w 终点 + */ + public void addEdge(Long v, Long w) { + adj.putIfAbsent(v, new ArrayList<>()); + adj.putIfAbsent(w, new ArrayList<>()); + adj.get(v).add(w); +// adj.get(w).add(v); // 对于无向图,添加反向边 + } + + /** + * 向图中添加一个节点(如果节点已存在,则不执行任何操作) + * @param vertex 要添加的节点 + */ + public void addVertex(Long vertex) { + adj.putIfAbsent(vertex, new ArrayList<>()); } + /** + * 返回图中的顶点数量 + * @return + */ public int getVertices() { return adj.size(); } - private boolean isCircleUtil(Integer v, boolean[] visited, boolean[] onStack) { - if (visited[v]) { + /** + * 检测从顶点 v 出发是否存在环 + * @param v + * @param visited + * @param onStack + * @return + */ + private boolean isCircleUtil(Long v, boolean[] visited, boolean[] onStack) { + if (visited[v.intValue()]) { return true; // 已经访问过,说明有环 } - if (onStack[v]) { + if (onStack[v.intValue()]) { return false; // 已访问过且不在递归栈中 } - visited[v] = true; - onStack[v] = true; + visited[v.intValue()] = true; + onStack[v.intValue()] = true; - for (Integer w : adj.getOrDefault(v, Collections.emptyList())) { + for (Long w : adj.getOrDefault(v, Collections.emptyList())) { if (isCircleUtil(w, visited, onStack)) { return true; } } - onStack[v] = false; + onStack[v.intValue()] = false; return false; } - private int getMaxVertextIndex(){ - return adj.isEmpty() ? -1 : Collections.max(adj.keySet()); + /** + * 图中顶点索引的最大值 + * @return + */ + private Long getMaxVertextIndex(){ + return adj.isEmpty() ? -1L : Collections.max(adj.keySet()); } + /** + * 检测图中是否存在环 + * @return + */ public boolean hasCircle() { - boolean[] visited = new boolean[getMaxVertextIndex() + 1]; - boolean[] onStack = new boolean[getMaxVertextIndex() + 1]; - for ( int vertext: adj.keySet()) { - if (!visited[vertext] && isCircleUtil(vertext, visited, onStack)) { + Long max = getMaxVertextIndex(); + boolean[] visited = new boolean[(int) (max + 1)]; + boolean[] onStack = new boolean[(int) (max + 1)]; + for ( Long vertext: adj.keySet()) { + if (!visited[vertext.intValue()] && isCircleUtil(vertext, visited, onStack)) { return true; } } return false; } + + public boolean hasCycle() { + Set visited = new HashSet<>(); + Set inPath = new HashSet<>(); + + for (Long node : adj.keySet()) { + if (!visited.contains(node)) { + if (dfs(adj, node, visited, inPath)) { + return true; + } + } + } + return false; + } + + private boolean dfs(Map> graph, Long node, Set visited, Set inPath) { + visited.add(node); + inPath.add(node); + + List neighbors = graph.get(node); + if (neighbors != null) { + for (Long neighbor : neighbors) { + if (!visited.contains(neighbor)) { + if (dfs(graph, neighbor, visited, inPath)) { + return true; + } + } else if (inPath.contains(neighbor)) { + return true; + } + } + } + + inPath.remove(node); // 回溯时移除当前节点 + return false; + } } diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdProductBomController.java b/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdProductBomController.java index 32e7e4b..b778c74 100644 --- a/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdProductBomController.java +++ b/ktg-mes/src/main/java/com/ktg/mes/md/controller/MdProductBomController.java @@ -82,7 +82,7 @@ public class MdProductBomController extends BaseController return AjaxResult.error("产品不能作为自身的BOM物料!"); } - return toAjax(mdProductBomService.insertMdProductBom(mdProductBom)); + return mdProductBomService.insertMdProductBom(mdProductBom); } /** diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdProductBomMapper.java b/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdProductBomMapper.java index bace5fb..173ba89 100644 --- a/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdProductBomMapper.java +++ b/ktg-mes/src/main/java/com/ktg/mes/md/mapper/MdProductBomMapper.java @@ -58,4 +58,10 @@ public interface MdProductBomMapper * @return 结果 */ public int deleteMdProductBomByBomIds(Long[] bomIds); + + /** + * 查询所有产品BOM数据 + * @return + */ + List selectAll(); } diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdProductBomService.java b/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdProductBomService.java index e0eb80d..243eac0 100644 --- a/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdProductBomService.java +++ b/ktg-mes/src/main/java/com/ktg/mes/md/service/IMdProductBomService.java @@ -1,6 +1,8 @@ package com.ktg.mes.md.service; import java.util.List; + +import com.ktg.common.core.domain.AjaxResult; import com.ktg.mes.md.domain.MdProductBom; /** @@ -29,11 +31,11 @@ public interface IMdProductBomService /** * 新增产品BOM关系 - * + * * @param mdProductBom 产品BOM关系 * @return 结果 */ - public int insertMdProductBom(MdProductBom mdProductBom); + public AjaxResult insertMdProductBom(MdProductBom mdProductBom); /** * 修改产品BOM关系 diff --git a/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdProductBomServiceImpl.java b/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdProductBomServiceImpl.java index a8d8013..6c3e74e 100644 --- a/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdProductBomServiceImpl.java +++ b/ktg-mes/src/main/java/com/ktg/mes/md/service/impl/MdProductBomServiceImpl.java @@ -1,7 +1,12 @@ package com.ktg.mes.md.service.impl; import java.util.List; + +import com.ktg.common.core.domain.AjaxResult; import com.ktg.common.utils.DateUtils; +import com.ktg.common.utils.MapCircleUtils; +import com.ktg.mes.md.domain.MdItem; +import com.ktg.mes.md.service.IMdItemService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ktg.mes.md.mapper.MdProductBomMapper; @@ -20,6 +25,9 @@ public class MdProductBomServiceImpl implements IMdProductBomService @Autowired private MdProductBomMapper mdProductBomMapper; + @Autowired + private IMdItemService itemService; + /** * 查询产品BOM关系 * @@ -46,15 +54,30 @@ public class MdProductBomServiceImpl implements IMdProductBomService /** * 新增产品BOM关系 - * + * * @param mdProductBom 产品BOM关系 * @return 结果 */ @Override - public int insertMdProductBom(MdProductBom mdProductBom) + public AjaxResult insertMdProductBom(MdProductBom mdProductBom) { + // 首先构建图数据 + MapCircleUtils mapCircleUtils = MapCircleUtils.createInstance(); + List mdProductBoms = selectBomAll(); + // 将相关数据构建成图结构 + mdProductBoms.forEach(item -> { + mapCircleUtils.addEdge(item.getItemId(), item.getBomItemId()); + }); + mapCircleUtils.addEdge(mdProductBom.getItemId(), mdProductBom.getBomItemId()); + // 查询图结构是否存在闭环 + if (mapCircleUtils.hasCycle()) { + // 存在闭环,无法新增 + return AjaxResult.error("BOM物料存在闭环,无法新增"); + } + + // 不存在闭环,可以新增 mdProductBom.setCreateTime(DateUtils.getNowDate()); - return mdProductBomMapper.insertMdProductBom(mdProductBom); + return AjaxResult.success(mdProductBomMapper.insertMdProductBom(mdProductBom)); } /** @@ -93,4 +116,8 @@ public class MdProductBomServiceImpl implements IMdProductBomService { return mdProductBomMapper.deleteMdProductBomByBomId(bomId); } + + public List selectBomAll() { + return mdProductBomMapper.selectAll(); + } } diff --git a/ktg-mes/src/main/resources/mapper/md/MdProductBomMapper.xml b/ktg-mes/src/main/resources/mapper/md/MdProductBomMapper.xml index d656b71..470af2d 100644 --- a/ktg-mes/src/main/resources/mapper/md/MdProductBomMapper.xml +++ b/ktg-mes/src/main/resources/mapper/md/MdProductBomMapper.xml @@ -49,7 +49,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where bom_id = #{bomId} - + + insert into md_product_bom