fix:产品物料BOM组成新增时,校验整体是否存在闭环。
This commit is contained in:
parent
12e74e3520
commit
1936ede088
@ -12,61 +12,135 @@ import java.util.*;
|
||||
@Service
|
||||
public class MapCircleUtils {
|
||||
|
||||
private Map<Integer,List<Integer>> adj; // 邻接表
|
||||
private Map<Long,List<Long>> 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<Long> visited = new HashSet<>();
|
||||
Set<Long> 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<Long, List<Long>> graph, Long node, Set<Long> visited, Set<Long> inPath) {
|
||||
visited.add(node);
|
||||
inPath.add(node);
|
||||
|
||||
List<Long> 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;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public class MdProductBomController extends BaseController
|
||||
return AjaxResult.error("产品不能作为自身的BOM物料!");
|
||||
}
|
||||
|
||||
return toAjax(mdProductBomService.insertMdProductBom(mdProductBom));
|
||||
return mdProductBomService.insertMdProductBom(mdProductBom);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,4 +58,10 @@ public interface MdProductBomMapper
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteMdProductBomByBomIds(Long[] bomIds);
|
||||
|
||||
/**
|
||||
* 查询所有产品BOM数据
|
||||
* @return
|
||||
*/
|
||||
List<MdProductBom> selectAll();
|
||||
}
|
||||
|
@ -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关系
|
||||
|
@ -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<MdProductBom> 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<MdProductBom> selectBomAll() {
|
||||
return mdProductBomMapper.selectAll();
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="selectMdProductBomVo"/>
|
||||
where bom_id = #{bomId}
|
||||
</select>
|
||||
|
||||
<select id="selectAll" resultType="com.ktg.mes.md.domain.MdProductBom" resultMap="MdProductBomResult">
|
||||
<include refid="selectMdProductBomVo"/>
|
||||
</select>
|
||||
|
||||
<insert id="insertMdProductBom" parameterType="MdProductBom" useGeneratedKeys="true" keyProperty="bomId">
|
||||
insert into md_product_bom
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
|
Loading…
Reference in New Issue
Block a user