Parcourir la source

后管系统-合同WBS调整
1、给私有库的数据添加公有库的对应ID-对应字段为tree_p_id
2、私有库根据主键添加父子级关系 **p_id**
3、添加ancestors_p_id 字段记录私有库的父节点路径

LHB il y a 4 mois
Parent
commit
2e976228c2

+ 11 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/WbsTreeContract.java

@@ -119,6 +119,12 @@ public class WbsTreeContract extends BaseEntity {
     @ApiModelProperty(value = "祖级id列表")
     private String ancestors;
 
+    /**
+     * 父级Id路径
+     */
+    @ApiModelProperty(value = "祖级id列表")
+    private String ancestorsPId;
+
     /**
      * 备注
      */
@@ -308,5 +314,10 @@ public class WbsTreeContract extends BaseEntity {
     @ApiModelProperty(value = "自定义数字化节点的上传时间")
     private String digitizeTime;
 
+    //m_wbs_tree 的主键Id
+    @JsonProperty(value = "treePId")
+    private Long treePId;
+
+
 
 }

+ 8 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.java

@@ -119,4 +119,12 @@ public interface WbsTreeContractMapper extends EasyBaseMapper<WbsTreeContract> {
     void batchUpdateTable(@Param("list") List<String> list);
 
     List<WbsTreeContractTreeVOS> selectContractJLForm(List<Long> removeList);
+
+    void updateContractPid(@Param("projectId") String projectId, @Param("contractId") String contractId);
+
+    /**
+     * 批量更新Ancestors字段---临时更新remark字段做比较
+     * @param allNodes
+     */
+    void updateBatchAncestorsByPKeyId(@Param("allNodes") List<WbsTreeContract> allNodes);
 }

+ 22 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreeContractMapper.xml

@@ -768,6 +768,28 @@
             ${item}
         </foreach>
     </update>
+    <update id="updateContractPid">
+        UPDATE m_wbs_tree_contract a
+            LEFT JOIN (
+            SELECT
+            tree_p_id,
+            p_key_id
+            FROM
+            m_wbs_tree_contract
+            WHERE
+            project_id = #{projectId} and contract_id = #{contractId} ) b ON a.parent_id = b.tree_p_id
+            SET a.p_id = IFNULL( b.p_key_id, 0 )
+        WHERE
+            a.project_id = #{projectId} and contract_id = #{contractId}
+
+    </update>
+    <update id="updateBatchAncestorsByPKeyId">
+        <foreach collection="allNodes" item="item" separator=";">
+            UPDATE m_wbs_tree_contract
+            <set>`ancestors_p_id` = #{item.ancestorsPId}</set>
+            where p_key_id = #{item.pKeyId}
+        </foreach>
+    </update>
 
     <select id="selectQueryValueLikeNodeName" resultMap="ResultMap">
         select *

+ 137 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java

@@ -1,5 +1,6 @@
 package org.springblade.manager.service.impl;
 
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
@@ -12,6 +13,9 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.google.common.collect.Lists;
 import lombok.AllArgsConstructor;
 import org.apache.commons.lang.StringUtils;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -107,6 +111,9 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
 
     @Autowired
     StringRedisTemplate redisTemplate;
+    @Autowired
+    private SqlSessionFactory sqlSessionFactory;
+
 
     //存储当前合同段contractId对应的合同段树
     private final Map<String, List<WbsTreeContractLazyVO>> localCacheNodes = new ConcurrentHashMap<>();
@@ -148,7 +155,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
             throw new ServiceException("请勿重复提交,请60秒后再尝试");
         }
         bladeRedis.set("submit-wbs-contract:" + pawDTO.getContractId(), "1");
-        bladeRedis.expire("submit-wbs-contract:" + pawDTO.getContractId(), 60);
+        bladeRedis.expire("submit-wbs-contract:" + pawDTO.getContractId(), 5);
 
         String wbsTreeIds = pawDTO.getWbsTreeIds();
         String[] ids = wbsTreeIds.split(",");
@@ -373,6 +380,9 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
                                     constructionLedger.setContractId(Long.parseLong(pawDTO.getContractId()));
                                     constructionLedgerList.add(constructionLedger);
                                 }
+                                //TODO  这里是添加公共节点id还是添加私有节点id还有待商榷   暂时先使用公共节点id完成逻辑
+                                wbsTreeContract.setTreePId(wbsTreePrivate.getId());
+
                                 return wbsTreeContract;
                             }
                             return null;
@@ -383,6 +393,31 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
                 //入库
                 this.insertBatch(wbsTreeContractList, 1000);
                 constructionLedgerFeign.initConstructionLedger(constructionLedgerList);
+
+
+
+                //绑定合同WBS树自身父级id 和 父级路径
+                baseMapper.updateContractPid(pawDTO.getProjectId(),pawDTO.getContractId());
+
+                // 获取所有节点数据并更新 ancestors 字段
+                try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+                    WbsTreeContractMapper mapper = sqlSession.getMapper(WbsTreeContractMapper.class);
+
+                    // 获取所有节点数据
+                    List<WbsTreeContract> list = repairWithMemoryCalculation(pawDTO.getProjectId(),pawDTO.getContractId());
+
+                    int batchSize = 1000;
+                    for (int i = 0; i < list.size(); i += batchSize) {
+                        List<WbsTreeContract> batch = list.subList(i, Math.min(i + batchSize, list.size()));
+                        mapper.updateBatchAncestorsByPKeyId(batch);
+                    }
+
+                    // 统一提交事务
+                    sqlSession.commit();
+                } catch (Exception e) {
+                    throw new ServiceException("操作失败: " + e.getMessage());
+                }
+
             }
         }
 
@@ -3590,4 +3625,105 @@ public static boolean hasConflictingCodes(List<ImportTreeDto> list) {
             setAncestors(child, parentChildMap, nodeMap);
         }
     }
+
+
+
+    /**
+     * 使用内存计算优化修复合同数据
+     * 该方法加载指定项目和合同相关的所有节点到内存,并进行数据修复和优化
+     * 主要包括:构建节点映射、检测并修复循环引用、重新计算所有节点的祖先路径
+     *
+     * @param projectId 项目ID,用于筛选相关节点
+     * @param contractId 合同ID,用于进一步筛选相关节点
+     * @return 返回修复和优化后的节点列表
+     */
+    public List<WbsTreeContract> repairWithMemoryCalculation(String projectId,String contractId) {
+        // 1. 加载全量数据到内存
+        List<WbsTreeContract> allNodes = baseMapper.selectList(new QueryWrapper<WbsTreeContract>().lambda()
+                .eq(WbsTreeContract::getProjectId, projectId)
+                .eq(WbsTreeContract::getContractId,contractId));
+
+        // 构建节点映射,以便快速访问每个节点
+        Map<Long, WbsTreeContract> nodeMap = buildNodeMap(allNodes);
+
+        // 2. 检测并修复循环引用
+        // 这一步确保数据的一致性和完整性,避免后续处理中出现无限递归等问题
+        detectAndBreakCircles(allNodes, nodeMap);
+
+        // 3. 重新计算所有ancestors
+        // 由于节点关系可能发生变化(如修复循环引用),需要重新计算每个节点的祖先路径
+        allNodes.stream()
+                .forEach(node -> {
+                    String correctAncestors = calculateCorrectAncestors(node, nodeMap);
+                    node.setAncestorsPId(correctAncestors);
+                });
+
+        // 返回修复和优化后的节点列表
+        return allNodes;
+    }
+
+    private Map<Long, WbsTreeContract> buildNodeMap(List<WbsTreeContract> nodes) {
+        return nodes.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, Function.identity()));
+    }
+
+
+    private String calculateCorrectAncestors(WbsTreeContract node, Map<Long, WbsTreeContract> nodeMap) {
+        List<Long> path = new ArrayList<>();
+        WbsTreeContract current = node;
+        Set<Long> visited = new HashSet<>();
+
+        while (true) {
+            if (current == null || current.getPId() == null ||
+                    current.getPId() == 0 || current.getPId().equals(current.getPKeyId())) {
+                break;
+            }
+
+            // 检测循环引用
+            if (visited.contains(current.getPId())) {
+                log.warn("Circular reference detected at node {}" + current.getPKeyId());
+                break;
+            }
+            visited.add(current.getPKeyId());
+
+            current = nodeMap.get(current.getPId());
+            if (current != null) {
+                path.add(0, current.getPKeyId());
+            }
+
+            // 安全限制
+            if (path.size() > 50) {
+                log.warn("Path too long (>50) at node {}" + node.getPKeyId());
+                break;
+            }
+        }
+        path.add(0, 0L);
+        return String.join(",", path.stream().map(String::valueOf).toArray(String[]::new));
+    }
+
+    private void detectAndBreakCircles(List<WbsTreeContract> nodes, Map<Long, WbsTreeContract> nodeMap) {
+        nodes.forEach(node -> {
+            if (hasCircle(node, nodeMap)) {
+                log.warn("Breaking circle for node {}" + node.getPKeyId());
+                node.setParentId(null); // 断开循环引用
+            }
+        });
+    }
+
+    private boolean hasCircle(WbsTreeContract node, Map<Long, WbsTreeContract> nodeMap) {
+        Set<Long> visited = new HashSet<>();
+        WbsTreeContract current = node;
+
+        while (current != null && current.getPId() != null &&
+                !current.getPId().equals(current.getPKeyId())) {
+
+            if (visited.contains(current.getPId())) {
+                return true;
+            }
+            visited.add(current.getPKeyId());
+
+            current = nodeMap.get(current.getPId());
+            if (current == null) break;
+        }
+        return false;
+    }
 }