|
@@ -1784,6 +1784,7 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
|
|
|
/*获取元素表数据*/
|
|
|
Map<Long, Map<String, Object>> colMaps = new HashMap<>();
|
|
|
+ Map<Long, String> colMap = new HashMap<>();
|
|
|
//是否需要复制数据
|
|
|
if (vo.getIsCopyData() == 1) {
|
|
|
//表名集合
|
|
@@ -1794,7 +1795,7 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
.collect(Collectors.toList());
|
|
|
//表名集合转逗号拼接的字符串
|
|
|
String inClausePlaceholders = String.join(",", Collections.nCopies(tabNames.size(), "?"));
|
|
|
- String sql = "SELECT table_name AS queryType, GROUP_CONCAT(COLUMN_name) AS ancestors " +
|
|
|
+ String sql = "SELECT table_name AS queryType, GROUP_CONCAT(distinct COLUMN_name) AS ancestors " +
|
|
|
"FROM information_schema.COLUMNS WHERE table_name IN (" + inClausePlaceholders + ") " +
|
|
|
"GROUP BY table_name";
|
|
|
Object[] params = tabNames.toArray();
|
|
@@ -1823,21 +1824,23 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
List<WbsTreeContract> tabs = tabsGroup.get(tabName);
|
|
|
for (WbsTreeContract tab : tabs) {
|
|
|
//根据字段
|
|
|
- String dataSql = "SELECT " + keys + " FROM " + tabName + " WHERE p_key_id = " + tab.getPKeyId() + " LIMIT 1;";
|
|
|
- try {
|
|
|
- //查询指定表指定表节点的数据
|
|
|
- Map<String, Object> resultMap = jdbcTemplate.queryForMap(dataSql);
|
|
|
- //删除空值
|
|
|
- resultMap.values().removeIf(value -> value == null || (value instanceof String && ObjectUtil.isEmpty(value)));
|
|
|
- colMaps.put(tab.getPKeyId(), resultMap);
|
|
|
- } catch (EmptyResultDataAccessException e) {
|
|
|
- continuePkeyIds.add(tab.getPKeyId());
|
|
|
- }
|
|
|
+// String dataSql = "SELECT " + keys + " FROM " + tabName + " WHERE p_key_id = " + tab.getPKeyId() + " LIMIT 1;";
|
|
|
+// try {
|
|
|
+// //查询指定表指定表节点的数据
|
|
|
+// Map<String, Object> resultMap = jdbcTemplate.queryForMap(dataSql);
|
|
|
+// //删除空值
|
|
|
+// resultMap.values().removeIf(value -> value == null || (value instanceof String && ObjectUtil.isEmpty(value)));
|
|
|
+// colMaps.put(tab.getPKeyId(), resultMap);
|
|
|
+// } catch (EmptyResultDataAccessException e) {
|
|
|
+// continuePkeyIds.add(tab.getPKeyId());
|
|
|
+// }
|
|
|
+ colMap.put(tab.getPKeyId(), keys);
|
|
|
}
|
|
|
}
|
|
|
logger.info("以下元素表没有获取到对应实体表数据,已跳过 ===> 表pKeyId:[{}]", StringUtils.join(continuePkeyIds, ","));
|
|
|
}
|
|
|
// 节点+表节点
|
|
|
+ List<WbsTreeContract> wbsParamAndLedgerList = new ArrayList<>();
|
|
|
for (WbsTreeContract nodeOld : nodeChildAll) {
|
|
|
//新节点
|
|
|
WbsTreeContract newData = new WbsTreeContract();
|
|
@@ -1848,15 +1851,18 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
if (nodeOld.getNodeType() != null && nodeOld.getNodeType() == 6) {
|
|
|
//新旧节点关联关系
|
|
|
peerMap.put(newData.getPKeyId(), nodeOld.getPKeyId());
|
|
|
- //复制节点命名配置
|
|
|
- //查询节点绑定的公共配置 文件提名信息
|
|
|
- WbsParam wbsParam = wbsParamClient.getWbsParam(Long.parseLong(vo.getNeedCopyPrimaryKeyId()));
|
|
|
- if(wbsParam!=null){
|
|
|
- wbsParam.setId(SnowFlakeUtil.getId());
|
|
|
- wbsParam.setNodeId(newData.getPKeyId());
|
|
|
- //给复制的节点绑定的公共配置
|
|
|
- wbsParamClient.saveWbsParam(wbsParam);
|
|
|
- }
|
|
|
+// //复制节点命名配置
|
|
|
+// //查询节点绑定的公共配置 文件提名信息
|
|
|
+// WbsParam wbsParam = wbsParamClient.getWbsParam(Long.parseLong(vo.getNeedCopyPrimaryKeyId()));
|
|
|
+// if(wbsParam!=null){
|
|
|
+// wbsParam.setId(SnowFlakeUtil.getId());
|
|
|
+// wbsParam.setNodeId(newData.getPKeyId());
|
|
|
+// //给复制的节点绑定的公共配置
|
|
|
+// wbsParamClient.saveWbsParam(wbsParam);
|
|
|
+// }
|
|
|
+// /*生成工序节点施工日志*/
|
|
|
+// this.createLedger(newData, saveLedger, nodeMap, null);
|
|
|
+ wbsParamAndLedgerList.add(newData);
|
|
|
}
|
|
|
//源节点是否为复制节点 如果是则设置源节点的源节点 如何不是设置源节点id
|
|
|
if (StringUtils.isNotEmpty(nodeOld.getOldId())) {
|
|
@@ -1901,9 +1907,9 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
saveList.add(newData);
|
|
|
|
|
|
/*生成工序节点施工日志*/
|
|
|
- if (new Integer("6").equals(nodeOld.getNodeType())) {
|
|
|
- this.createLedger(newData, saveLedger, nodeMap, null);
|
|
|
- }
|
|
|
+// if (new Integer("6").equals(nodeOld.getNodeType())) {
|
|
|
+// this.createLedger(newData, saveLedger, nodeMap, null);
|
|
|
+// }
|
|
|
|
|
|
/*处理复制表数据*/
|
|
|
if (nodeOld.getType() == 2 && vo.getIsCopyData() == 1
|
|
@@ -1911,64 +1917,101 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
&& tabOwner.contains(nodeOld.getTableOwner())) {
|
|
|
|
|
|
/*获取表对应的实体数据*/
|
|
|
- Map<String, Object> resultMap = colMaps.getOrDefault(nodeOld.getPKeyId(), null);
|
|
|
- if (resultMap == null) {
|
|
|
+// Map<String, Object> resultMap = colMaps.getOrDefault(nodeOld.getPKeyId(), null);
|
|
|
+// if (resultMap == null) {
|
|
|
+// continue;
|
|
|
+// }
|
|
|
+ String keys = colMap.getOrDefault(nodeOld.getPKeyId(), null);
|
|
|
+ if (keys == null || keys.isEmpty()) {
|
|
|
continue;
|
|
|
}
|
|
|
-
|
|
|
/*重置*/
|
|
|
- Map<String, String> eMap = reviseValue(nodeOld, null, ekvMap);
|
|
|
+// Map<String, String> eMap = reviseValue(nodeOld, null, ekvMap);
|
|
|
|
|
|
/*构造复制表数据*/
|
|
|
- StringBuilder newString = new StringBuilder();
|
|
|
- List<String> keySet = new LinkedList<>();
|
|
|
- for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
|
|
|
- keySet.add(entry.getKey());
|
|
|
- Object value = reviseValue(eMap, entry.getKey(), entry.getValue());
|
|
|
- if (value != null) {
|
|
|
- if (value.toString().contains("\n")) {
|
|
|
- value = value.toString().replace("\n", "\\n");
|
|
|
- }
|
|
|
- newString.append("'").append(value).append("',");
|
|
|
- }
|
|
|
- }
|
|
|
- if (newString.length() > 0) {
|
|
|
- newString.insert(0, ',');
|
|
|
- if (newString.charAt(newString.length() - 1) == ',') {
|
|
|
- newString.deleteCharAt(newString.length() - 1);
|
|
|
- }
|
|
|
- }
|
|
|
- String keysHaveValue = StringUtils.join(keySet, ",");
|
|
|
- if (keysHaveValue.length() > 0) {
|
|
|
- keysHaveValue = "," + keysHaveValue;
|
|
|
- }
|
|
|
+// StringBuilder newString = new StringBuilder();
|
|
|
+// List<String> keySet = new LinkedList<>();
|
|
|
+// for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
|
|
|
+// keySet.add(entry.getKey());
|
|
|
+// Object value = reviseValue(eMap, entry.getKey(), entry.getValue());
|
|
|
+// if (value != null) {
|
|
|
+// if (value.toString().contains("\n")) {
|
|
|
+// value = value.toString().replace("\n", "\\n");
|
|
|
+// }
|
|
|
+// newString.append("'").append(value).append("',");
|
|
|
+// }
|
|
|
+// }
|
|
|
+// if (newString.length() > 0) {
|
|
|
+// newString.insert(0, ',');
|
|
|
+// if (newString.charAt(newString.length() - 1) == ',') {
|
|
|
+// newString.deleteCharAt(newString.length() - 1);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// String keysHaveValue = StringUtils.join(keySet, ",");
|
|
|
+// if (keysHaveValue.length() > 0) {
|
|
|
+// keysHaveValue = "," + keysHaveValue;
|
|
|
+// }
|
|
|
//delete SQL (先删除旧数据,再新增)
|
|
|
String delSql = "DELETE FROM " + newData.getInitTableName() + " WHERE p_key_id = " + newData.getPKeyId() + " ; ";
|
|
|
//insert SQL
|
|
|
+// copySql.append(delSql)
|
|
|
+// .append("INSERT INTO ")
|
|
|
+// .append(newData.getInitTableName())
|
|
|
+// .append(" (id,p_key_id,group_id")
|
|
|
+// .append(keysHaveValue)
|
|
|
+// .append(") VALUES (")
|
|
|
+// .append(SnowFlakeUtil.getId()).append(",")
|
|
|
+// .append(newData.getPKeyId()).append(",null")
|
|
|
+// .append(newString)
|
|
|
+// .append(");");
|
|
|
copySql.append(delSql)
|
|
|
.append("INSERT INTO ")
|
|
|
.append(newData.getInitTableName())
|
|
|
- .append(" (id,p_key_id,group_id")
|
|
|
- .append(keysHaveValue)
|
|
|
- .append(") VALUES (")
|
|
|
+ .append(" (id,p_key_id,group_id,")
|
|
|
+ .append(keys).append(") select ")
|
|
|
.append(SnowFlakeUtil.getId()).append(",")
|
|
|
- .append(newData.getPKeyId()).append(",null")
|
|
|
- .append(newString)
|
|
|
- .append(");");
|
|
|
+ .append(newData.getPKeyId()).append(",null, ")
|
|
|
+ .append(keys).append(" from ").append(newData.getInitTableName()).append(" where p_key_id =").append(nodeOld.getPKeyId()).append(" LIMIT 1;");
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+ }
|
|
|
+ if (!wbsParamAndLedgerList.isEmpty()) {
|
|
|
+ //复制节点命名配置
|
|
|
+ //查询节点绑定的公共配置 文件提名信息
|
|
|
+ WbsParam wbsParam = wbsParamClient.getWbsParam(Long.parseLong(vo.getNeedCopyPrimaryKeyId()));
|
|
|
+ List<WbsParam> wbsParamList = new ArrayList<>();
|
|
|
+ wbsParamAndLedgerList.forEach(newData -> {
|
|
|
+ if(wbsParam!=null){
|
|
|
+ WbsParam wbsParam1 = new WbsParam();
|
|
|
+ BeanUtil.copy(wbsParam, wbsParam1);
|
|
|
+ wbsParam1.setId(SnowFlakeUtil.getId());
|
|
|
+ wbsParam1.setNodeId(newData.getPKeyId());
|
|
|
+ //给复制的节点绑定的公共配置
|
|
|
+ wbsParamList.add(wbsParam1);
|
|
|
+ }
|
|
|
+ /*生成工序节点施工日志*/
|
|
|
+ this.createLedger(newData, saveLedger, nodeMap, null);
|
|
|
+ });
|
|
|
+ wbsParamClient.saveWbsParams(wbsParamList);
|
|
|
}
|
|
|
|
|
|
//TODO 20250414-lhb-新增 添加祖级字段 ancestorsPId
|
|
|
- List<WbsTreeContract> contractWbsTreeByContractId = wbsTreeContractClient.getContractWbsTreeByContractId(Long.valueOf(needCopyNode.getContractId()));
|
|
|
- contractWbsTreeByContractId.addAll(saveList);
|
|
|
- Map<Long, WbsTreeContract> collect = contractWbsTreeByContractId.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, Function.identity()));
|
|
|
- saveList.forEach(node -> {
|
|
|
- String correctAncestors = createAncestorsPId(node,collect);;
|
|
|
- node.setAncestorsPId(correctAncestors);
|
|
|
- });
|
|
|
+ //因为复制选中节点,所以要查询出选中节点的父节点信息 来组装祖级节点
|
|
|
+ if(needCopyNode != null){
|
|
|
+ Long parentPKeyId = null;
|
|
|
+ String ancestorsPId = null;
|
|
|
+ if(needCopyNode.getPId() == 0L){
|
|
|
+ ancestorsPId = "0";
|
|
|
+ parentPKeyId = 0L;
|
|
|
+ }else{
|
|
|
+ WbsTreeContract parentNode = this.wbsTreeContractClient.getContractNodeByPrimaryKeyId(String.valueOf(needCopyNode.getPId()));
|
|
|
+ ancestorsPId = parentNode.getAncestorsPId();
|
|
|
+ parentPKeyId = parentNode.getPKeyId();
|
|
|
+ }
|
|
|
+ attachNodesToTarget(saveList,parentPKeyId,ancestorsPId);
|
|
|
+ }
|
|
|
}
|
|
|
needCopyNode.setNodeName(vo.getNeedCopyNodeName());
|
|
|
|
|
@@ -2136,6 +2179,26 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
this.addCopyTabData(needCopyNode, toCopyNode, tabOwner, resultTablesData, addTabList, vo.getIsCopyData(), addNewFileTabs);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ /*重构祖级id*/
|
|
|
+ List<WbsTreeContract> resultAll = new ArrayList<>();
|
|
|
+ resultAll.addAll(addNodeList);
|
|
|
+ resultAll.addAll(addTabList);
|
|
|
+ //因为复制选中节点,所以要查询出选中节点的父节点信息 来组装祖级节点
|
|
|
+ if(needCopyNode != null && CollectionUtil.isNotEmpty(resultAll)){
|
|
|
+ Long parentPKeyId = null;
|
|
|
+ String ancestorsPId = null;
|
|
|
+ if(needCopyNode.getPId() == 0L){
|
|
|
+ ancestorsPId = "0";
|
|
|
+ parentPKeyId = 0L;
|
|
|
+ }else{
|
|
|
+ WbsTreeContract parentNode = this.wbsTreeContractClient.getContractNodeByPrimaryKeyId(String.valueOf(toCopyVO.getPrimaryKeyId()));
|
|
|
+ ancestorsPId = parentNode.getAncestorsPId();
|
|
|
+ parentPKeyId = parentNode.getPKeyId();
|
|
|
+ }
|
|
|
+ attachNodesToTarget(resultAll,parentPKeyId,ancestorsPId);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -2145,14 +2208,6 @@ public R<Boolean> copyContractTreeNode(@RequestBody CopyContractTreeNodeVO vo) {
|
|
|
resultAll.addAll(addNodeList);
|
|
|
resultAll.addAll(addTabList);
|
|
|
|
|
|
- //TODO 20250414-lhb-新增
|
|
|
- List<WbsTreeContract> contractWbsTreeByContractId = wbsTreeContractClient.getContractWbsTreeByContractId(Long.valueOf(contractId));
|
|
|
- contractWbsTreeByContractId.addAll(resultAll);
|
|
|
- Map<Long, WbsTreeContract> collect = contractWbsTreeByContractId.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, Function.identity()));
|
|
|
- resultAll.forEach(node -> {
|
|
|
- String correctAncestors = createAncestorsPId(node,collect);;
|
|
|
- node.setAncestorsPId(correctAncestors);
|
|
|
- });
|
|
|
|
|
|
List<WbsTreeContract> allData = this.reBuildAncestors(resultAll);
|
|
|
List<WbsTreeContract> nodes = allData.stream().filter(f -> f.getType().equals(1)).collect(Collectors.toList());
|
|
@@ -2287,7 +2342,7 @@ private Map<String, String> reviseValue(WbsTreeContract wtc, WbsTreeContract par
|
|
|
if (!ekvMap.containsKey(tableName)) {
|
|
|
Map<String, String> map = ekvMap.computeIfAbsent(wtc.getInitTableName(), K -> new HashMap<>());
|
|
|
if (parent == null) {
|
|
|
- parent = this.wbsTreeContractClient.getContractWbsTreeByContractIdAndId(wtc.getParentId(), Long.parseLong(wtc.getContractId()));
|
|
|
+ parent = this.wbsTreeContractClient.getContractWbsTreeByPrimaryKeyId(wtc.getPId());
|
|
|
}
|
|
|
/*凡是关联了节点参数公式的元素都不复制数据*/
|
|
|
List<Map<String, Object>> paramKey = this.jdbcTemplate.queryForList("select DISTINCT b.e_key ek from m_table_info a join m_wbs_form_element b on a.id=b.f_id join m_element_formula_mapping c on b.id = c.element_id where a.tab_en_name='" + tableName + "' and b.is_deleted=0 and c.scope=35 and c.is_deleted=0");
|
|
@@ -2445,8 +2500,8 @@ private List<WbsTreeContract> reBuildAncestors(List<WbsTreeContract> list) {
|
|
|
public void addCopyTabFile(Set<WbsTreeContract> addChildNodesTables,
|
|
|
Set<WbsTreeContract> addChildNodesTablesOld) {
|
|
|
//获取所有数据源附件文件
|
|
|
- List<Long> tabFileIds = addChildNodesTablesOld.stream().distinct().map(WbsTreeContract::getPKeyId).collect(Collectors.toList());
|
|
|
- Map<String, List<TableFile>> tableFileOldMap = tableFileClient.getTabFilesByTabIds(StringUtils.join(tabFileIds, ",")).stream().collect(Collectors.groupingBy(TableFile::getTabId));
|
|
|
+ List<String> tabFileIds = addChildNodesTablesOld.stream().distinct().map(item -> item.getPKeyId() + "").collect(Collectors.toList());
|
|
|
+ Map<String, List<TableFile>> tableFileOldMap = tableFileClient.getTabFilesByTabIds(tabFileIds).stream().collect(Collectors.groupingBy(TableFile::getTabId));
|
|
|
if (tableFileOldMap != null && tableFileOldMap.size() > 0) {
|
|
|
List<TableFile> resultFileData = new ArrayList<>();
|
|
|
Set<Long> updatePKeyIds = new HashSet<>();
|
|
@@ -2738,7 +2793,7 @@ private void addCopyNodesAndTabsBuildData(List<WbsTreeContract> addNodeList, Lis
|
|
|
List<String> initTabNames = needTabs.stream().map(WbsTreeContract::getInitTableName).distinct().filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());
|
|
|
String joined = StringUtils.join(initTabNames, ",");
|
|
|
joined = "'" + joined.replaceAll(",", "','") + "'";
|
|
|
- Map<String, QueryProcessDataVO> tabColsAllByTabNameMaps = jdbcTemplate.query("SELECT table_name as queryType, GROUP_CONCAT(COLUMN_name) as ancestors from information_schema.COLUMNS where table_name in (" + joined + ") GROUP BY table_name", new BeanPropertyRowMapper<>(QueryProcessDataVO.class)).stream().collect(Collectors.toMap(QueryProcessDataVO::getQueryType, Function.identity()));
|
|
|
+ Map<String, QueryProcessDataVO> tabColsAllByTabNameMaps = jdbcTemplate.query("SELECT table_name as queryType, GROUP_CONCAT(DISTINCT COLUMN_name) as ancestors from information_schema.COLUMNS where table_name in (" + joined + ") GROUP BY table_name", new BeanPropertyRowMapper<>(QueryProcessDataVO.class)).stream().collect(Collectors.toMap(QueryProcessDataVO::getQueryType, Function.identity()));
|
|
|
for (WbsTreeContract node : needNodes) {
|
|
|
Long oldId = node.getId();
|
|
|
//新节点
|
|
@@ -3887,13 +3942,7 @@ public R<Boolean> saveContractTreeNode(@RequestBody AddContractTreeNodeVO vo) {
|
|
|
}
|
|
|
}
|
|
|
//TODO 20250414-lhb-新增 添加ancestorsPId字段
|
|
|
- List<WbsTreeContract> contractWbsTreeByContractId = wbsTreeContractClient.getContractWbsTreeByContractId(Long.valueOf(treeContract.getContractId()));
|
|
|
- contractWbsTreeByContractId.addAll(saveList);
|
|
|
- Map<Long, WbsTreeContract> collect = contractWbsTreeByContractId.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, Function.identity()));
|
|
|
- saveList.forEach(node -> {
|
|
|
- String correctAncestors = createAncestorsPId(node,collect);;
|
|
|
- node.setAncestorsPId(correctAncestors);
|
|
|
- });
|
|
|
+ attachNodesToTarget(saveList,treeContract.getPKeyId(),treeContract.getAncestorsPId());
|
|
|
|
|
|
R<Boolean> booleanR = this.saveOrCopyNodeTree(saveList, saveLedger, 2, treeContract);
|
|
|
|
|
@@ -4803,4 +4852,98 @@ public R<Object> customAddContractNode(@RequestBody CustomAddContractNodeDTO dto
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public void attachNodesToTarget(List<WbsTreeContract> newNodes, Long targetId, String targetAncestors) {
|
|
|
+ // 1. 找到新数据中的顶层节点(新树的根节点)
|
|
|
+ WbsTreeContract newRoot = findRootNode(newNodes);
|
|
|
+
|
|
|
+ // 2. 将新树的根节点绑定到目标节点
|
|
|
+ newRoot.setPId(targetId);
|
|
|
+ newRoot.setAncestorsPId(calculateAncestors(targetAncestors, targetId));
|
|
|
+
|
|
|
+ // 3. 构建映射关系
|
|
|
+ Map<Long, WbsTreeContract> nodeMap = new HashMap<>();
|
|
|
+ Map<Long, List<WbsTreeContract>> childrenMap = new HashMap<>();
|
|
|
+
|
|
|
+ for (WbsTreeContract node : newNodes) {
|
|
|
+ nodeMap.put(node.getPKeyId(), node);
|
|
|
+ childrenMap.computeIfAbsent(node.getPId(), k -> new ArrayList<>()).add(node);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 安全层序遍历(带循环检测)
|
|
|
+ safeBfsTraversal(newRoot, childrenMap, newNodes.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void safeBfsTraversal(WbsTreeContract root,
|
|
|
+ Map<Long, List<WbsTreeContract>> childrenMap,
|
|
|
+ int totalNodes) {
|
|
|
+ Queue<WbsTreeContract> queue = new LinkedList<>();
|
|
|
+ Set<Long> visited = new HashSet<>(); // 已访问节点集合
|
|
|
+ queue.add(root);
|
|
|
+ visited.add(root.getPKeyId());
|
|
|
+
|
|
|
+ int processedCount = 0; // 已处理节点计数器
|
|
|
+ final int MAX_DEPTH = 100; // 最大深度保护
|
|
|
+
|
|
|
+ while (!queue.isEmpty()) {
|
|
|
+ WbsTreeContract parent = queue.poll();
|
|
|
+ processedCount++;
|
|
|
+
|
|
|
+ // 安全检测1:防止循环引用导致无限循环
|
|
|
+ if (processedCount > totalNodes * 2) {
|
|
|
+ throw new IllegalStateException("处理节点数超过预期,可能存在循环引用。已处理: "
|
|
|
+ + processedCount + ",总节点: " + totalNodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安全检测2:防止路径过长
|
|
|
+ if (parent.getAncestorsPId().split(",").length > MAX_DEPTH) {
|
|
|
+ throw new IllegalStateException("祖级路径超过最大深度限制: " + MAX_DEPTH);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<WbsTreeContract> children = childrenMap.get(parent.getPKeyId());
|
|
|
+ if (children == null) continue;
|
|
|
+
|
|
|
+ for (WbsTreeContract child : children) {
|
|
|
+ // 安全检测3:检查循环引用
|
|
|
+ if (visited.contains(child.getPKeyId())) {
|
|
|
+ throw new IllegalStateException("检测到循环引用: 节点" + child.getPKeyId()
|
|
|
+ + " -> 节点" + parent.getPKeyId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新祖级路径 = 父路径 + 父ID
|
|
|
+ child.setAncestorsPId(calculateAncestors(parent.getAncestorsPId(), parent.getPKeyId()));
|
|
|
+
|
|
|
+ queue.add(child);
|
|
|
+ visited.add(child.getPKeyId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证是否处理了所有节点
|
|
|
+ if (processedCount < totalNodes) {
|
|
|
+ throw new IllegalStateException("存在未处理的节点,可能是不连通的子树。已处理: "
|
|
|
+ + processedCount + ",总节点: " + totalNodes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String calculateAncestors(String parentAncestors, Long parentId) {
|
|
|
+ if (parentAncestors == null || parentAncestors.isEmpty()) {
|
|
|
+ return parentId.toString();
|
|
|
+ }
|
|
|
+ return parentAncestors + "," + parentId;
|
|
|
+ }
|
|
|
+
|
|
|
+ private WbsTreeContract findRootNode(List<WbsTreeContract> nodes) {
|
|
|
+ Set<Long> nodeIds = new HashSet<>();
|
|
|
+ for (WbsTreeContract node : nodes) {
|
|
|
+ nodeIds.add(node.getPKeyId());
|
|
|
+ }
|
|
|
+
|
|
|
+ for (WbsTreeContract node : nodes) {
|
|
|
+ Long parentId = node.getPId();
|
|
|
+ // 父节点为空 或 父节点不在当前新数据列表中 → 视为根节点
|
|
|
+ if (parentId == null || !nodeIds.contains(parentId)) {
|
|
|
+ return node;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new IllegalArgumentException("新数据中未找到根节点");
|
|
|
+ }
|
|
|
}
|