Browse Source

剥离客户端懒加载树

liuyc 1 year ago
parent
commit
db8dfe5e04

+ 4 - 0
blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/feign/IUserClient.java

@@ -177,4 +177,8 @@ public interface IUserClient {
     @PostMapping(SAVE_USER_DTO)
     R<Boolean> saveUserDTO(@RequestBody UserDTO user);
 
+    /*删除合同段本地缓存*/
+    @GetMapping(API_PREFIX + "/deleteContractLocalCache")
+    void deleteContractLocalCache(@RequestParam String contractId);
+
 }

+ 6 - 2
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/InformationQueryServiceImpl.java

@@ -954,7 +954,9 @@ public class InformationQueryServiceImpl extends BaseServiceImpl<InformationQuer
                     }
 
                     //删除合同段本地缓存
-                    wbsTreeContractClient.deleteContractLocalCache(String.valueOf(sgId));
+                    //wbsTreeContractClient.deleteContractLocalCache(String.valueOf(sgId));
+                    /*剥离到user服务中*/
+                    userClient.deleteContractLocalCache(String.valueOf(sgId));
                 }
             }
 
@@ -979,7 +981,9 @@ public class InformationQueryServiceImpl extends BaseServiceImpl<InformationQuer
             }
 
             //删除合同段本地缓存
-            wbsTreeContractClient.deleteContractLocalCache(contractId);
+            //wbsTreeContractClient.deleteContractLocalCache(contractId);
+            /*剥离到user服务中*/
+            userClient.deleteContractLocalCache(contractId);
         }
     }
 

+ 5 - 5
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TaskServiceImpl.java

@@ -724,7 +724,7 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
                     //  }
 
                     //return前先删除合同段树redis缓存
-                    informationQueryService.delAsyncWbsTree(masterTask.getContractId());
+                    /*informationQueryService.delAsyncWbsTree(masterTask.getContractId());*/
 
                     //返回电签成功的pdf路径,给试验用
                     return finalPdfUrl;
@@ -732,8 +732,8 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
                     //只更新PDF路径
                     this.updateBusinessDataByFormDataId(masterTask, 1, eVisaStatus.contains("@@@@") ? eVisaStatus.split("@@@@")[1] : null, taskApprovalVO.getUserId());
 
-                    //return前先删除合同段树redis缓存
-                    informationQueryService.delAsyncWbsTree(masterTask.getContractId());
+                    /*//return前先删除合同段树redis缓存
+                    informationQueryService.delAsyncWbsTree(masterTask.getContractId());*/
 
                     return eVisaStatus.contains("@@@@") ? eVisaStatus.split("@@@@")[1] : null;
                 }
@@ -819,8 +819,8 @@ public class TaskServiceImpl extends BaseServiceImpl<TaskMapper, Task> implement
             this.abolishMessage(masterTask, currentLink, comment, taskApprovalVO.getNickName());
         }
 
-        //return前先删除合同段树redis缓存
-        informationQueryService.delAsyncWbsTree(masterTask.getContractId());
+        /*//return前先删除合同段树redis缓存
+        informationQueryService.delAsyncWbsTree(masterTask.getContractId());*/
 
         return "";
     }

+ 3 - 2
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java

@@ -193,7 +193,8 @@ public class WbsTreeContractController extends BladeController {
      * @author liuyc
      * @date 2023年7月17日10:28:49
      */
-    @GetMapping("/lazyQueryContractWbsTree")
+    @Deprecated /*(已剥离到user服务)*/
+    /*@GetMapping("/lazyQueryContractWbsTree")
     @ApiOperationSupport(order = 9)
     @ApiOperation(value = "客户端懒加载获取合同段树(统计颜色、填报数量)")
     @ApiImplicitParams(value = {
@@ -203,7 +204,7 @@ public class WbsTreeContractController extends BladeController {
             @ApiImplicitParam(name = "contractIdRelation", value = "合同段关联id(监理、业主合同关联施工合同id)"),
             @ApiImplicitParam(name = "classifyType", value = "合同段区分,施工合同段=1,监理合同段=2"),
             @ApiImplicitParam(name = "tableOwner", value = "所属方节点权限,施工=1,监理=2,区分节点的数量、颜色")
-    })
+    })*/
     public R<List<WbsTreeContractLazyVO>> lazyQueryContractWbsTree(@RequestParam String primaryKeyId, @RequestParam String parentId, @RequestParam String contractId, @RequestParam String contractIdRelation, @RequestParam String classifyType, @RequestParam String tableOwner) {
         //这里是对应的监理合同段下,加载树时primaryKeyId=parentId;与前端对接时没沟通好入参,就不单独处理了,直接重新映射赋值一下
         if (StringUtils.isNotEmpty(primaryKeyId)) {

+ 3 - 7
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java

@@ -68,10 +68,10 @@ import java.util.stream.Stream;
 @AllArgsConstructor
 public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractMapper, WbsTreeContract> implements IWbsTreeContractService {
 
-    static {
-        /*parallelStream并行流粒度*/
+    /*static {
+        *//*parallelStream并行流粒度*//*
         System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");
-    }
+    }*/
 
     private static final Logger logger = LoggerFactory.getLogger(WbsTreeContractServiceImpl.class);
     private final ConstructionLedgerFeignClient constructionLedgerFeign;
@@ -2792,10 +2792,6 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
                 iterator_2.remove();
             }
         }
-
-        /*更新缓存,主动清理后,重新加载进本地缓存中*/
-        this.getNodeAll(contractId);
-
     }
 
 }

+ 212 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/bean/NodeVO.java

@@ -0,0 +1,212 @@
+package org.springblade.system.user.bean;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.Data;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.stream.Collectors;
+
+/**
+ * @author liuyc
+ * @date 2023年9月27日16:46:43
+ * @description 计算节点颜色状态
+ */
+@Data
+public class NodeVO {
+
+    private Long pKeyId;
+    private Long id;
+    private Long parentId;
+    private List<NodeVO> children;
+    private Integer status;
+
+    /**
+     * DFS
+     *
+     * @param nodeList
+     * @param nodeParentGroupToIdMap
+     */
+    public static void calculateStatusToDFS(List<NodeVO> nodeList, Map<Long, NodeVO> nodeParentGroupToIdMap) {
+        NodeVO rootNode = findRootNode(nodeList);
+        if (rootNode != null) {
+            calculateNodeStatusToDFS(rootNode, nodeParentGroupToIdMap);
+        }
+    }
+
+    /**
+     * BFS
+     *
+     * @param nodeList
+     * @param nodeParentGroupToIdMap
+     */
+    public static void calculateStatusToBFS(List<NodeVO> nodeList, Map<Long, NodeVO> nodeParentGroupToIdMap) {
+        NodeVO rootNode = findRootNode(nodeList);
+        if (rootNode != null) {
+            calculateNodeStatusToBFS(rootNode);
+            Queue<NodeVO> queue = new LinkedList<>(rootNode.getChildren());
+            while (!queue.isEmpty()) {
+                NodeVO node = queue.poll();
+                if (node != null) {
+                    calculateNodeStatusToBFS(node);
+
+                    //将子节点逐个加入队列
+                    if (node.getChildren() != null) {
+                        queue.addAll(node.getChildren());
+                    }
+
+                    if (node.getParentId() != null) {
+                        NodeVO nodeVO = nodeParentGroupToIdMap.get(node.getParentId());
+                        if (nodeVO != null) {
+                            List<NodeVO> children = nodeVO.getChildren();
+                            if (children != null) {
+                                queue.addAll(children);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 获取根节点
+     *
+     * @param nodeList
+     * @return
+     */
+    private static NodeVO findRootNode(List<NodeVO> nodeList) {
+        for (NodeVO node : nodeList) {
+            if (node.getParentId().equals(0L)) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * DFS 判断节点状态
+     *
+     * @param node
+     * @param nodeParentGroupToIdMap
+     */
+    private static void calculateNodeStatusToDFS(NodeVO node, Map<Long, NodeVO> nodeParentGroupToIdMap) {
+        //最底层节点直接返回
+        if (ObjectUtil.isEmpty(node.getChildren())) {
+            return;
+        }
+
+        //递归处理子节点
+        for (NodeVO child : node.getChildren()) {
+            calculateNodeStatusToDFS(child, nodeParentGroupToIdMap);
+        }
+
+        //判断子级
+        List<Integer> childStatusList = node.getChildren().stream().map(NodeVO::getStatus).collect(Collectors.toList());
+
+        //如果子节点都是相同的状态,则父节点状态也为该状态
+        if (childStatusList.stream().distinct().count() == 1) {
+            node.setStatus(childStatusList.get(0));
+            //更新父节点的状态
+            if (node.getParentId() != null) {
+                NodeVO parentNode = nodeParentGroupToIdMap.get(node.getParentId());
+                if (parentNode != null) {
+                    calculateNodeStatusToDFS(parentNode, nodeParentGroupToIdMap);
+                }
+            }
+            return;
+        }
+
+        //判断是否存在同时只存在1、3的情况
+        if (childStatusList.contains(1) && childStatusList.contains(3) && !childStatusList.contains(2)) {
+            node.setStatus(2);
+            return;
+        }
+
+        //判断是否存在同时只存在3、4的情况
+        if (childStatusList.contains(3) && childStatusList.contains(4) && !childStatusList.contains(1) && !childStatusList.contains(2)) {
+            node.setStatus(3);
+            return;
+        }
+
+        //判断是否存在只有1但不全是1的情况
+        if (childStatusList.contains(1) && !childStatusList.contains(2) && !childStatusList.contains(3) && !childStatusList.contains(4)) {
+            node.setStatus(2);
+            return;
+        }
+
+        //判断是否存在只有2但不全是2的情况
+        if (childStatusList.contains(2) && !childStatusList.contains(1) && !childStatusList.contains(3) && !childStatusList.contains(4)) {
+            node.setStatus(2);
+            return;
+        }
+
+        //判断是否存在只有3但不全是3的情况
+        if (childStatusList.contains(3) && !childStatusList.contains(1) && !childStatusList.contains(2) && !childStatusList.contains(4)) {
+            node.setStatus(3);
+            return;
+        }
+
+        //其他情况,父节点状态默认为2
+        node.setStatus(2);
+    }
+
+    /**
+     * BFS
+     *
+     * @param node
+     */
+    private static void calculateNodeStatusToBFS(NodeVO node) {
+        //最底层节点直接返回
+        if (ObjectUtil.isEmpty(node.getChildren())) {
+            return;
+        }
+
+        //判断子级
+        List<Integer> childStatusList = node.getChildren().stream().map(NodeVO::getStatus).collect(Collectors.toList());
+
+        //如果子节点都是相同的状态,则父节点状态也为该状态
+        if (childStatusList.stream().distinct().count() == 1) {
+            node.setStatus(childStatusList.get(0));
+            return;
+        }
+
+        //判断是否存在同时只存在1、3的情况
+        if (childStatusList.contains(1) && childStatusList.contains(3) && !childStatusList.contains(2)) {
+            node.setStatus(2);
+            return;
+        }
+
+        //判断是否存在同时只存在3、4的情况
+        if (childStatusList.contains(3) && childStatusList.contains(4) && !childStatusList.contains(1) && !childStatusList.contains(2)) {
+            node.setStatus(3);
+            return;
+        }
+
+        //判断是否存在只有1但不全是1的情况
+        if (childStatusList.contains(1) && !childStatusList.contains(2) && !childStatusList.contains(3) && !childStatusList.contains(4)) {
+            node.setStatus(2);
+            return;
+        }
+
+        //判断是否存在只有2但不全是2的情况
+        if (childStatusList.contains(2) && !childStatusList.contains(1) && !childStatusList.contains(3) && !childStatusList.contains(4)) {
+            node.setStatus(2);
+            return;
+        }
+
+        //判断是否存在只有3但不全是3的情况
+        if (childStatusList.contains(3) && !childStatusList.contains(1) && !childStatusList.contains(2) && !childStatusList.contains(4)) {
+            node.setStatus(3);
+            return;
+        }
+
+        //其他情况,父节点状态默认为2
+        node.setStatus(2);
+    }
+
+
+}

+ 107 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/controller/WbsTreeController.java

@@ -0,0 +1,107 @@
+package org.springblade.system.user.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang.StringUtils;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.manager.vo.WbsTreeContractLazyVO;
+import org.springblade.system.user.service.IUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping(value = "/wbs")
+@AllArgsConstructor
+@Api(value = "WBS树接口", tags = "WBS树接口")
+public class WbsTreeController extends BladeController {
+
+    @Autowired
+    StringRedisTemplate redisTemplate;
+    private final IUserService iUserService;
+
+    /**
+     * 客户端懒加载获取合同段树(统计颜色、填报数量)
+     *
+     * @author liuyc
+     * @date 2023年12月6日09:52:54(接口剥离,从manager剥离到user,解决manager服务与mysql不在同一服务器中,网络延迟问题)
+     */
+    @GetMapping("/lazyQueryContractWbsTree")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "客户端懒加载获取合同段树(统计颜色、填报数量)")
+    @ApiImplicitParams(value = {
+            @ApiImplicitParam(name = "primaryKeyId"),
+            @ApiImplicitParam(name = "parentId", value = "父节点id,为空则查询第一级节点"),
+            @ApiImplicitParam(name = "contractId", value = "合同段id"),
+            @ApiImplicitParam(name = "contractIdRelation", value = "合同段关联id(监理、业主合同关联施工合同id)"),
+            @ApiImplicitParam(name = "classifyType", value = "合同段区分,施工合同段=1,监理合同段=2"),
+            @ApiImplicitParam(name = "tableOwner", value = "所属方节点权限,施工=1,监理=2,区分节点的数量、颜色")
+    })
+    public R<List<WbsTreeContractLazyVO>> lazyQueryContractWbsTree(@RequestParam String primaryKeyId, @RequestParam String parentId, @RequestParam String contractId, @RequestParam String contractIdRelation, @RequestParam String classifyType, @RequestParam String tableOwner) {
+        //这里是对应的监理合同段下,加载树时primaryKeyId=parentId;与前端对接时没沟通好入参,就不单独处理了,直接重新映射赋值一下
+        if (StringUtils.isNotEmpty(primaryKeyId)) {
+            parentId = primaryKeyId;
+        }
+
+        //结果集
+        List<WbsTreeContractLazyVO> vos;
+
+        //构造Redis缓存Key
+        String dataInfoId = "";
+        if (("1").equals(classifyType)) {
+            dataInfoId = contractId + "_" + parentId + "_" + classifyType + "_" + tableOwner;
+        } else if (("2").equals(classifyType)) {
+            //监理合同段下,classifyType=1,直接查询对应的施工树缓存
+            dataInfoId = contractIdRelation + "_" + parentId + "_" + "1" + "_" + tableOwner;
+        }
+
+        //获取Redis缓存信息
+        Object data = null;
+        if (ObjectUtil.isNotEmpty(dataInfoId)) {
+            if (("2").equals(classifyType) && ObjectUtil.isNotEmpty(contractIdRelation)) {
+                //监理根据contractIdRelation关联合同段id来判断获取缓存
+                data = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:" + dataInfoId);
+            } else if (("1").equals(classifyType)) {
+                //施工直接获取缓存
+                data = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:" + dataInfoId);
+            }
+        }
+
+        if (data != null) {
+            //返回缓存
+            vos = JSON.parseArray(data.toString(), WbsTreeContractLazyVO.class);
+
+        } else {
+            //响应结果集
+            vos = iUserService.lazyQueryContractWbsTree(parentId, contractId, contractIdRelation, tableOwner);
+
+            //存储缓存
+            if (vos != null && ObjectUtil.isNotEmpty(dataInfoId)) {
+                //监理根据contractIdRelation关联合同段id来判断存储缓存
+                if (("2").equals(classifyType) && ObjectUtil.isNotEmpty(contractIdRelation)) {
+                    JSONArray array = JSONArray.parseArray(JSON.toJSONString(vos));
+                    redisTemplate.opsForValue().set("blade-manager::contract:wbstree:" + dataInfoId, JSON.toJSON(array).toString());
+                } else if (("1").equals(classifyType)) {
+                    //施工直接存储缓存
+                    JSONArray array = JSONArray.parseArray(JSON.toJSONString(vos));
+                    redisTemplate.opsForValue().set("blade-manager::contract:wbstree:" + dataInfoId, JSON.toJSON(array).toString());
+                }
+            }
+        }
+        return R.data(vos);
+    }
+
+}

+ 5 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/feign/UserClient.java

@@ -140,5 +140,10 @@ public class UserClient implements IUserClient {
         return R.data(true);
     }
 
+    @Override
+    public void deleteContractLocalCache(String contractId) {
+        service.deleteContractLocalCache(contractId);
+    }
+
 
 }

+ 5 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/service/IUserService.java

@@ -23,6 +23,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springblade.core.mp.base.BaseService;
 import org.springblade.core.mp.support.Query;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
+import org.springblade.manager.vo.WbsTreeContractLazyVO;
 import org.springblade.system.user.dto.UserDTO;
 import org.springblade.system.user.entity.User;
 import org.springblade.system.user.entity.UserInfo;
@@ -241,4 +242,8 @@ public interface IUserService extends BaseService<User> {
 
     List<User> selectUserAll();
 
+    List<WbsTreeContractLazyVO> lazyQueryContractWbsTree(String parentId, String contractId, String contractIdRelation, String tableOwner);
+
+    void deleteContractLocalCache(String contractId);
+
 }

+ 649 - 48
blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -1,25 +1,7 @@
-/*
- *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *
- *  Redistributions of source code must retain the above copyright notice,
- *  this list of conditions and the following disclaimer.
- *  Redistributions in binary form must reproduce the above copyright
- *  notice, this list of conditions and the following disclaimer in the
- *  documentation and/or other materials provided with the distribution.
- *  Neither the name of the dreamlu.net developer nor the names of its
- *  contributors may be used to endorse or promote products derived from
- *  this software without specific prior written permission.
- *  Author: Chill 庄骞 (smallchill@163.com)
- */
 package org.springblade.system.user.service.impl;
 
-
-import cn.hutool.crypto.SecureUtil;
-import cn.hutool.crypto.digest.MD5;
-import com.alibaba.nacos.common.utils.MD5Utils;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.nacos.common.utils.StringUtils;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -32,7 +14,6 @@ import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.mp.base.BaseServiceImpl;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
-import org.springblade.core.secure.BladeUser;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.tenant.BladeTenantProperties;
 import org.springblade.core.tool.api.R;
@@ -41,15 +22,17 @@ import org.springblade.core.tool.jackson.JsonUtil;
 import org.springblade.core.tool.support.Kv;
 import org.springblade.core.tool.utils.*;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
+import org.springblade.manager.entity.ContractInfo;
 import org.springblade.manager.feign.ContractClient;
-import org.springblade.manager.feign.SaveUserInfoByProjectClient;
+import org.springblade.manager.vo.WbsTreeContractLazyQueryInfoVO;
+import org.springblade.manager.vo.WbsTreeContractLazyVO;
 import org.springblade.system.cache.DictCache;
-import org.springblade.system.cache.ParamCache;
 import org.springblade.system.cache.SysCache;
 import org.springblade.system.entity.Dept;
 import org.springblade.system.entity.Tenant;
 import org.springblade.system.enums.DictEnum;
 import org.springblade.system.feign.ISysClient;
+import org.springblade.system.user.bean.NodeVO;
 import org.springblade.system.user.cache.UserCache;
 import org.springblade.system.user.dto.UserDTO;
 import org.springblade.system.user.entity.*;
@@ -59,37 +42,47 @@ import org.springblade.system.user.mapper.UserMapper;
 import org.springblade.system.user.service.IUserDeptService;
 import org.springblade.system.user.service.IUserOauthService;
 import org.springblade.system.user.service.IUserService;
-import org.springblade.system.user.vo.UserContractInfoVO;
 import org.springblade.system.user.vo.UserVO;
 import org.springblade.system.user.wrapper.UserWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import sun.security.rsa.RSASignature;
 
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_PASSWORD;
-
-/**
- * 服务实现类
- *
- * @author Chill
- */
 @Service
 @AllArgsConstructor
 public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {
-    private static final String GUEST_NAME = "guest";
 
+    static {
+        /*parallelStream并行流粒度*/
+        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "6");
+    }
+
+    /*存储当前合同段contractId对应的合同段树*/
+    private final Map<String, List<WbsTreeContractLazyVO>> localCacheNodes = new ConcurrentHashMap<>();
+    /*存储当前合同段contractId_tableOwner对应的资料查询信息*/
+    private final Map<String, List<WbsTreeContractLazyQueryInfoVO>> localCacheQueryInfos = new ConcurrentHashMap<>();
+    /*存储当前合同段contractId_tableOwner对应的节点数量统计缓存信息*/
+    private final Map<String, List<WbsTreeContractLazyVO>> localCacheParentCountNodes = new ConcurrentHashMap<>();
+
+    private static final String GUEST_NAME = "guest";
     private final IUserDeptService userDeptService;
     private final IUserOauthService userOauthService;
     private final ISysClient sysClient;
     private final BladeTenantProperties tenantProperties;
     private final ContractClient contractClient;
-    private final SaveUserInfoByProjectClient saveUserInfoByProjectClient;
     private final JdbcTemplate jdbcTemplate;
+    @Autowired
+    StringRedisTemplate redisTemplate;
 
 
     @Override
@@ -121,13 +114,13 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
         }
 
         // 内控修改  当为部门负责人不为空时 执行
-        if (StringUtil.isNotBlank(user.getIsLeader())){
+        if (StringUtil.isNotBlank(user.getIsLeader())) {
             // 由于一个部门只有一个负责人  需要修改 之前的部门负责人
-            String [] leader = Func.toStrArray(user.getIsLeader());
-            for (String deptId : leader){
-                String reData = deptId+",";
-                String eqSql = "update blade_user set is_leader = null where  is_leader='"+deptId+"' ";
-                String fiSql = "update blade_user set is_leader = REPLACE(is_leader,"+reData+",'') where  FIND_IN_SET('"+deptId+"',is_leader) ";
+            String[] leader = Func.toStrArray(user.getIsLeader());
+            for (String deptId : leader) {
+                String reData = deptId + ",";
+                String eqSql = "update blade_user set is_leader = null where  is_leader='" + deptId + "' ";
+                String fiSql = "update blade_user set is_leader = REPLACE(is_leader," + reData + ",'') where  FIND_IN_SET('" + deptId + "',is_leader) ";
                 jdbcTemplate.execute(eqSql);
                 jdbcTemplate.execute(fiSql);
             }
@@ -233,13 +226,13 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
             throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", user.getAccount()));
         }
         // 内控修改  当为部门负责人不为空时 执行
-        if (StringUtil.isNotBlank(user.getIsLeader())){
+        if (StringUtil.isNotBlank(user.getIsLeader())) {
             // 由于一个部门只有一个负责人  需要修改 之前的部门负责人
-            String [] leader = Func.toStrArray(user.getIsLeader());
-            for (String deptId : leader){
-                String reData = deptId+",";
-                String eqSql = "update blade_user set is_leader = REPLACE(is_leader,'"+deptId+"','') where  FIND_IN_SET('"+deptId+"',is_leader) ";
-                String fiSql = "update blade_user set is_leader = REPLACE(is_leader,'"+reData+"','') where  FIND_IN_SET('"+deptId+"',is_leader) ";
+            String[] leader = Func.toStrArray(user.getIsLeader());
+            for (String deptId : leader) {
+                String reData = deptId + ",";
+                String eqSql = "update blade_user set is_leader = REPLACE(is_leader,'" + deptId + "','') where  FIND_IN_SET('" + deptId + "',is_leader) ";
+                String fiSql = "update blade_user set is_leader = REPLACE(is_leader,'" + reData + "','') where  FIND_IN_SET('" + deptId + "',is_leader) ";
                 jdbcTemplate.execute(fiSql);
                 jdbcTemplate.execute(eqSql);
             }
@@ -269,9 +262,9 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
     public boolean updateUserInfo(User user) {
 
         // 当为内控是 可以修改密码
-        if( StringUtils.isNotEmpty(user.getUserType()) && user.getUserType().equals("5") && StringUtil.isNotBlank(user.getPassword())){
+        if (StringUtils.isNotEmpty(user.getUserType()) && user.getUserType().equals("5") && StringUtil.isNotBlank(user.getPassword())) {
             user.setPassword(DigestUtil.encrypt(user.getPassword()));
-        }else{
+        } else {
             user.setPassword(null);
         }
         return updateById(user);
@@ -690,4 +683,612 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
     public List<User> selectUserAll() {
         return baseMapper.selectList(Wrappers.<User>query().lambda());
     }
+
+    @Override
+    public List<WbsTreeContractLazyVO> lazyQueryContractWbsTree(String id, String contractId, String contractIdRelation, String tableOwner) {
+        if (org.apache.commons.lang.StringUtils.isEmpty(tableOwner)) {
+            throw new ServiceException("tableOwner is not null");
+        }
+        if (cn.hutool.core.util.ObjectUtil.isNotEmpty(contractId)) {
+            ContractInfo contractInfo = jdbcTemplate.query("SELECT contract_name,contract_type FROM m_contract_info WHERE id = " + contractId, new BeanPropertyRowMapper<>(ContractInfo.class)).stream().findAny().orElse(null);
+            if (contractInfo != null) {
+                /*施工合同段*/
+                if (new Integer(1).equals(contractInfo.getContractType())) {
+                    //获取当前层懒加载节点
+                    String sql = "select p_key_id,contract_id,(SELECT id FROM u_contract_tree_drawings where process_id = p_key_id and is_deleted = 0 limit 1) AS drawingsId,id,parent_id,node_type,type,wbs_type,is_concrete,major_data_type,partition_code,old_id,contract_id_relation,is_concealed_works_node,CASE (SELECT count(1) FROM u_tree_contract_first AS tcf WHERE tcf.is_deleted = 0 AND tcf.wbs_node_id = a.p_key_id) WHEN 0 THEN 'false' ELSE 'true' END AS isFirst,IFNULL(if(length(trim(full_name))>0,full_name,node_name),node_name) AS title,(SELECT CASE WHEN count(1) > 0 THEN 1 ELSE 0 END FROM m_wbs_tree_contract b WHERE b.parent_id = a.id AND b.type = 1 and b.status = 1 AND b.contract_id = " + contractId + " AND b.is_deleted = 0 ) AS hasChildren from m_wbs_tree_contract a where a.node_type != 111 and a.type = 1 and a.status = 1 and a.is_deleted = 0 and parent_id = " + (org.apache.commons.lang.StringUtils.isNotEmpty(id) ? id : 0) + " and contract_id = " + contractId + " ORDER BY a.sort,title,a.create_time";
+                    List<WbsTreeContractLazyVO> lazyNodes = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContractLazyVO.class));
+                    if (lazyNodes.size() > 0) {
+
+                        /*根节点直接返回*/
+                        if (lazyNodes.stream().anyMatch(f -> f.getParentId().equals(0L))) {
+                            for (WbsTreeContractLazyVO lazyNode : lazyNodes) {
+                                lazyNode.setSubmitCounts(0);
+                                lazyNode.setColorStatus(2);
+                                lazyNode.setType(lazyNode.getNodeType());
+                                lazyNode.setNotExsitChild(!lazyNode.getHasChildren().equals(1));
+                                lazyNode.setPrimaryKeyId(lazyNode.getPKeyId());
+                                lazyNode.setTitle(contractInfo.getContractName());
+                            }
+                            return lazyNodes;
+                        }
+
+                        //获取本地缓存节点信息
+                        List<WbsTreeContractLazyVO> nodesAll = this.getNodeAll(contractId);
+                        List<WbsTreeContractLazyVO> distinctNodesAll = nodesAll.stream()
+                                .collect(Collectors.collectingAndThen(
+                                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(WbsTreeContractLazyVO::getPKeyId))),
+                                        ArrayList::new
+                                ));
+
+                        //所有最底层节点
+                        List<WbsTreeContractLazyVO> distinctLowestNodesAll = distinctNodesAll.stream().filter(f -> f.getHasChildren().equals(0)).collect(Collectors.collectingAndThen(
+                                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(WbsTreeContractLazyVO::getPKeyId))),
+                                ArrayList::new
+                        ));
+
+                        //获取本地缓存资料信息
+                        List<WbsTreeContractLazyQueryInfoVO> queryInfoList = this.getQueryInfoList(contractId, tableOwner);
+
+                        Map<Long, Integer> queryInfoMaps = queryInfoList.stream().filter(f -> cn.hutool.core.util.ObjectUtil.isNotEmpty(f.getWbsId()))
+                                .collect(Collectors.toMap(WbsTreeContractLazyQueryInfoVO::getWbsId, WbsTreeContractLazyQueryInfoVO::getStatus, (existingValue, newValue) -> existingValue));
+                        List<Long> pKeyIdList = new ArrayList<>(queryInfoMaps.keySet());
+
+                        /*处理数量*/
+                        //填报过的所有最底层节点
+                        List<WbsTreeContractLazyVO> lowestNodesTB = distinctLowestNodesAll.parallelStream().filter(f -> pKeyIdList.contains(f.getPKeyId())).collect(Collectors.toList());
+                        List<Long> lowestNodeParentIdsTB = lowestNodesTB.parallelStream().map(WbsTreeContractLazyVO::getParentId).collect(Collectors.toList());
+                        //获取本地缓存节点数量统计
+                        List<WbsTreeContractLazyVO> resultParentNodesTB = this.getCachedParentCountNodes(contractId, lowestNodeParentIdsTB, nodesAll, tableOwner);
+
+                        /*List<WbsTreeContractLazyVO> resultParentNodesTB = new ArrayList<>();
+                        long startTime1 = System.currentTimeMillis();
+                        this.recursiveGetParentNodes(resultParentNodesTB, lowestNodeParentIdsTB, nodesAll);
+                        long endTime1 = System.currentTimeMillis();
+                        long executionTime1 = endTime1 - startTime1;
+                        System.out.println("合同段 " + contractId + " wbs节点树 数量计算 执行时间:" + executionTime1 + " ms");*/
+
+                        //最底层节点颜色构造后Map
+                        Map<Long, WbsTreeContractLazyVO> lowestNodesMap = lowestNodesTB.stream()
+                                .peek(vo -> {
+                                    Integer colorStatus = queryInfoMaps.get(vo.getPKeyId());
+                                    if (colorStatus != null) {
+                                        //任务状态0=未上报=颜色2蓝色、任务状态1=待审批=颜色3橙色、任务状态2=已审批=颜色4绿色、
+                                        ///*任务状态3=已废除=颜色1黑色*/(2023年10月16日13:59:00 已废除改为=颜色2蓝色)
+                                        vo.setColorStatus(colorStatus == 0 ? 2 : (colorStatus == 1 ? 3 : (colorStatus == 2 ? 4 : 2)));
+                                    } else {
+                                        //未填报的=颜色1黑色
+                                        vo.setColorStatus(1);
+                                    }
+                                }).collect(Collectors.toMap(WbsTreeContractLazyVO::getPKeyId, Function.identity()));
+
+                        /*处理颜色*/
+                        long startTime = System.currentTimeMillis();
+                        //先将WbsTreeContractLazyVO转为NodeVO
+                        List<NodeVO> nodeVOList = distinctNodesAll.stream().map(this::convertToNodeVO).collect(Collectors.toList());
+                        //转为Map<Long, NodeVO>
+                        Map<Long, NodeVO> nodeVOMap = nodeVOList.stream().collect(Collectors.toMap(NodeVO::getId, vo -> vo, (existing, replacement) -> existing));
+                        //把distinctNodesAll把所有节点转为树形结构,再转为List<NodeVO>对象
+                        List<NodeVO> treeNodeVOList = this.buildNodeTreeByStream(distinctNodesAll, lowestNodesMap);
+                        //处理节点颜色
+                        NodeVO.calculateStatusToDFS(treeNodeVOList, nodeVOMap);
+                        //把树形结构转为普通List集合
+                        List<NodeVO> nodeVOS = this.flattenTree(treeNodeVOList);
+                        //获取所有节点颜色Map
+                        Map<Long, Integer> nodeColorStatusMap = nodeVOS.stream().collect(Collectors.toMap(NodeVO::getPKeyId, NodeVO::getStatus, (existing, replacement) -> existing));
+                        long endTime = System.currentTimeMillis();
+                        long executionTime = endTime - startTime;
+                        System.out.println("合同段 " + contractId + " 处理颜色 执行时间:" + executionTime + " ms");
+
+                        /*处理最终结果集*/
+                        if (lazyNodes.size() > 0) {
+                            //处理填报数量
+                            Map<Long, Integer> countMap = new HashMap<>();
+                            for (WbsTreeContractLazyVO node : resultParentNodesTB) {
+                                Long key = node.getPKeyId();
+                                if (countMap.containsKey(key)) {
+                                    countMap.put(key, countMap.get(key) + 1);
+                                } else {
+                                    countMap.put(key, 1);
+                                }
+                            }
+
+                            //返回最终结果集
+                            for (WbsTreeContractLazyVO lazyNodeVO : lazyNodes) {
+                                lazyNodeVO.setType(lazyNodeVO.getNodeType()); //前端显示需要一样的,所以修改成一样的
+                                lazyNodeVO.setNotExsitChild(!lazyNodeVO.getHasChildren().equals(1));
+                                lazyNodeVO.setPrimaryKeyId(lazyNodeVO.getPKeyId());
+                                if (lazyNodeVO.getParentId() == 0L) {
+                                    if (cn.hutool.core.util.ObjectUtil.isNotEmpty(contractInfo.getContractName())) {
+                                        lazyNodeVO.setTitle(contractInfo.getContractName());
+                                    }
+                                }
+
+                                //设置数量
+                                lazyNodeVO.setSubmitCounts(cn.hutool.core.util.ObjectUtil.isNotEmpty(countMap.get(lazyNodeVO.getPKeyId())) ? countMap.get(lazyNodeVO.getPKeyId()) : (cn.hutool.core.util.ObjectUtil.isNotEmpty(queryInfoMaps.get(lazyNodeVO.getPKeyId())) ? 1 : 0));
+
+                                //设置颜色
+                                if (lazyNodeVO.getSubmitCounts().equals(0)) {
+                                    lazyNodeVO.setColorStatus(1);
+                                    continue;
+                                }
+
+                                Integer parentColorStatus = nodeColorStatusMap.get(lazyNodeVO.getPKeyId());
+                                if (parentColorStatus != null) {
+                                    lazyNodeVO.setColorStatus(parentColorStatus);
+                                } else {
+                                    WbsTreeContractLazyVO lowestNode = lowestNodesMap.get(lazyNodeVO.getPKeyId());
+                                    if (lowestNode != null) {
+                                        lazyNodeVO.setColorStatus(lowestNode.getColorStatus());
+                                    }
+                                }
+                            }
+                        }
+                        return lazyNodes;
+                    }
+
+                    /*监理、业主合同段*/
+                } else if (new Integer("2").equals(contractInfo.getContractType()) || new Integer("3").equals(contractInfo.getContractType())) {
+                    List<WbsTreeContractLazyVO> lazyNodesAll = new ArrayList<>();
+                    List<String> contractIds = new ArrayList<>();
+                    if (cn.hutool.core.util.ObjectUtil.isNotEmpty(contractIdRelation) && cn.hutool.core.util.ObjectUtil.isNotEmpty(id)) {
+                        //非根节点时选择加载施工合同段的树
+                        contractIds.add(contractIdRelation);
+                    } else {
+                        //根节点时默认加载所有施工合同段的树
+                        contractIds = this.contractClient.getProcessContractByJLContractId(contractId);
+                    }
+                    if (cn.hutool.core.util.ObjectUtil.isEmpty(contractIds) || contractIds.size() <= 0) {
+                        return null;
+                    }
+                    for (String sgContractId : contractIds) {
+                        ContractInfo sgContractInfo = jdbcTemplate.query("select contract_name from m_contract_info where id = " + sgContractId, new BeanPropertyRowMapper<>(ContractInfo.class)).stream().findAny().orElse(null);
+                        if (sgContractInfo != null) {
+                            List<WbsTreeContractLazyVO> lazyNodes = jdbcTemplate.query("select p_key_id,contract_id,(SELECT id FROM u_contract_tree_drawings where process_id = p_key_id and is_deleted = 0 limit 1) AS drawingsId,id,parent_id,node_type,type,wbs_type,major_data_type,partition_code,old_id,contract_id_relation,is_concealed_works_node,CASE (SELECT count(1) FROM u_tree_contract_first AS tcf WHERE tcf.is_deleted = 0 AND tcf.wbs_node_id = a.p_key_id) WHEN 0 THEN 'false' ELSE 'true' END AS isFirst,IFNULL(if(length(trim(full_name))>0,full_name,node_name),node_name) AS title,(SELECT CASE WHEN count(1) > 0 THEN 1 ELSE 0 END FROM m_wbs_tree_contract b WHERE b.parent_id = a.id AND  b.type = 1 and b.status = 1 AND b.contract_id = " + sgContractId + " AND b.is_deleted = 0 ) AS hasChildren from m_wbs_tree_contract a where a.node_type != 111 and a.type = 1 and a.status = 1 and a.is_deleted = 0 and parent_id = " + (org.apache.commons.lang.StringUtils.isNotEmpty(id) ? id : 0) + " and contract_id = " + sgContractId + " ORDER BY a.sort,title,a.create_time", new BeanPropertyRowMapper<>(WbsTreeContractLazyVO.class));
+                            if (lazyNodes.size() > 0) {
+
+                                if (lazyNodes.stream().anyMatch(f -> f.getParentId().equals(0L))) {
+                                    for (WbsTreeContractLazyVO lazyNode : lazyNodes) {
+                                        lazyNode.setSubmitCounts(0);
+                                        lazyNode.setColorStatus(2);
+                                        lazyNode.setType(lazyNode.getNodeType());
+                                        lazyNode.setNotExsitChild(!lazyNode.getHasChildren().equals(1));
+                                        lazyNode.setPrimaryKeyId(lazyNode.getPKeyId());
+                                        lazyNode.setContractIdRelation(sgContractId);
+                                        lazyNode.setTitle(sgContractInfo.getContractName());
+                                    }
+                                    return lazyNodes;
+                                }
+
+                                List<WbsTreeContractLazyVO> nodesAll = this.getNodeAll(sgContractId);
+                                List<WbsTreeContractLazyVO> distinctNodesAll = nodesAll.stream()
+                                        .collect(Collectors.collectingAndThen(
+                                                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(WbsTreeContractLazyVO::getPKeyId))),
+                                                ArrayList::new
+                                        ));
+                                List<WbsTreeContractLazyVO> distinctLowestNodesAll = distinctNodesAll.stream().filter(f -> f.getHasChildren().equals(0)).collect(Collectors.collectingAndThen(
+                                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(WbsTreeContractLazyVO::getPKeyId))),
+                                        ArrayList::new
+                                ));
+
+                                List<WbsTreeContractLazyQueryInfoVO> queryInfoList = this.getQueryInfoList(sgContractId, tableOwner);
+
+                                Map<Long, Integer> queryInfoMaps = queryInfoList.stream().filter(f -> cn.hutool.core.util.ObjectUtil.isNotEmpty(f.getWbsId()))
+                                        .collect(Collectors.toMap(WbsTreeContractLazyQueryInfoVO::getWbsId, WbsTreeContractLazyQueryInfoVO::getStatus, (existingValue, newValue) -> existingValue));
+                                List<Long> pKeyIdList = new ArrayList<>(queryInfoMaps.keySet());
+
+                                List<WbsTreeContractLazyVO> lowestNodesTB = distinctLowestNodesAll.parallelStream().filter(f -> pKeyIdList.contains(f.getPKeyId())).collect(Collectors.toList());
+                                List<Long> lowestNodeParentIdsTB = lowestNodesTB.parallelStream().map(WbsTreeContractLazyVO::getParentId).collect(Collectors.toList());
+
+                                List<WbsTreeContractLazyVO> resultParentNodesTB = this.getCachedParentCountNodes(sgContractId, lowestNodeParentIdsTB, nodesAll, tableOwner);
+
+                                Map<Long, WbsTreeContractLazyVO> lowestNodesMap = lowestNodesTB.stream()
+                                        .peek(vo -> {
+                                            Integer colorStatus = queryInfoMaps.get(vo.getPKeyId());
+                                            if (colorStatus != null) {
+                                                vo.setColorStatus(colorStatus == 0 ? 2 : (colorStatus == 1 ? 3 : (colorStatus == 2 ? 4 : 2)));
+                                            } else {
+                                                vo.setColorStatus(1);
+                                            }
+                                        }).collect(Collectors.toMap(WbsTreeContractLazyVO::getPKeyId, Function.identity()));
+
+                                List<NodeVO> nodeVOList = distinctNodesAll.stream().map(this::convertToNodeVO).collect(Collectors.toList());
+                                Map<Long, NodeVO> nodeVOMap = nodeVOList.stream().collect(Collectors.toMap(NodeVO::getId, vo -> vo, (existing, replacement) -> existing));
+                                List<NodeVO> treeNodeVOList = this.buildNodeTreeByStream(distinctNodesAll, lowestNodesMap);
+                                NodeVO.calculateStatusToDFS(treeNodeVOList, nodeVOMap);
+                                List<NodeVO> nodeVOS = this.flattenTree(treeNodeVOList);
+                                Map<Long, Integer> nodeColorStatusMap = nodeVOS.stream().collect(Collectors.toMap(NodeVO::getPKeyId, NodeVO::getStatus, (existing, replacement) -> existing));
+
+                                if (lazyNodes.size() > 0) {
+                                    Map<Long, Integer> countMap = new HashMap<>();
+                                    for (WbsTreeContractLazyVO node : resultParentNodesTB) {
+                                        Long key = node.getPKeyId();
+                                        if (countMap.containsKey(key)) {
+                                            countMap.put(key, countMap.get(key) + 1);
+                                        } else {
+                                            countMap.put(key, 1);
+                                        }
+                                    }
+
+                                    for (WbsTreeContractLazyVO lazyNodeVO : lazyNodes) {
+                                        lazyNodeVO.setType(lazyNodeVO.getNodeType());
+                                        lazyNodeVO.setNotExsitChild(!lazyNodeVO.getHasChildren().equals(1));
+                                        lazyNodeVO.setPrimaryKeyId(lazyNodeVO.getPKeyId());
+                                        lazyNodeVO.setContractIdRelation(sgContractId);
+                                        if (lazyNodeVO.getParentId() == 0L) {
+                                            if (cn.hutool.core.util.ObjectUtil.isNotEmpty(sgContractInfo.getContractName())) {
+                                                lazyNodeVO.setTitle(sgContractInfo.getContractName());
+                                            }
+                                        }
+
+                                        lazyNodeVO.setSubmitCounts(cn.hutool.core.util.ObjectUtil.isNotEmpty(countMap.get(lazyNodeVO.getPKeyId())) ? countMap.get(lazyNodeVO.getPKeyId()) : (cn.hutool.core.util.ObjectUtil.isNotEmpty(queryInfoMaps.get(lazyNodeVO.getPKeyId())) ? 1 : 0));
+                                        if (lazyNodeVO.getSubmitCounts().equals(0)) {
+                                            lazyNodeVO.setColorStatus(1);
+                                            continue;
+                                        }
+
+                                        Integer parentColorStatus = nodeColorStatusMap.get(lazyNodeVO.getPKeyId());
+                                        if (parentColorStatus != null) {
+                                            lazyNodeVO.setColorStatus(parentColorStatus);
+                                        } else {
+                                            WbsTreeContractLazyVO lowestNode = lowestNodesMap.get(lazyNodeVO.getPKeyId());
+                                            if (lowestNode != null) {
+                                                lazyNodeVO.setColorStatus(lowestNode.getColorStatus());
+                                            }
+                                        }
+                                    }
+                                }
+                                lazyNodesAll.addAll(lazyNodes);
+                            }
+                        }
+                    }
+                    return lazyNodesAll;
+                }
+            }
+        }
+        return null;
+    }
+
+    /*非批量电签时,清理缓存,如资料填报保存、任务上报等*/
+    @Override
+    public void deleteContractLocalCache(String contractId) {
+        /*删除节点缓存*/
+        localCacheNodes.remove(contractId);
+
+        /*删除资料缓存*/
+        Iterator<Map.Entry<String, List<WbsTreeContractLazyQueryInfoVO>>> iterator_1 = localCacheQueryInfos.entrySet().iterator();
+        while (iterator_1.hasNext()) {
+            Map.Entry<String, List<WbsTreeContractLazyQueryInfoVO>> entry = iterator_1.next();
+            String cacheKey = entry.getKey();
+            if (cacheKey.startsWith(contractId + "_")) {
+                iterator_1.remove();
+            }
+        }
+
+        /*删除节点计算统计缓存*/
+        Iterator<Map.Entry<String, List<WbsTreeContractLazyVO>>> iterator_2 = localCacheParentCountNodes.entrySet().iterator();
+        while (iterator_2.hasNext()) {
+            Map.Entry<String, List<WbsTreeContractLazyVO>> entry = iterator_2.next();
+            String cacheKey = entry.getKey();
+            if (cacheKey.startsWith(contractId + "_")) {
+                iterator_2.remove();
+            }
+        }
+    }
+
+    /*定时清理缓存(本地+Redis)信息,不在批量电签时删除,批量电签是多线程,会一直删除,导致缓存基本不生效*/
+    @Scheduled(fixedRate = 120000) //每2分钟执行一次
+    public void clearContractLocalCacheAndRedisCache() {
+        //获取锁
+        ReentrantLock lock = new ReentrantLock();
+        lock.lock();
+        try {
+            //清除本地缓存
+            localCacheNodes.clear();
+            localCacheQueryInfos.clear();
+            localCacheParentCountNodes.clear();
+
+            //清除RedisWbs树节点缓存
+            Set<String> keysByNodes = redisTemplate.keys("blade-manager::contract:wbstree:*");
+            if (keysByNodes != null) {
+                redisTemplate.delete(keysByNodes);
+            }
+        } finally {
+            //释放锁
+            lock.unlock();
+            System.out.println("定时执行任务:{clearContractLocalCacheAndRedisCache} 完成 ... ");
+        }
+    }
+
+    /**
+     * 获取当前合同段所有节点缓存
+     *
+     * @param contractId
+     * @return
+     */
+    public List<WbsTreeContractLazyVO> getNodeAll(String contractId) {
+        //获取本地缓存
+        List<WbsTreeContractLazyVO> nodesAll = localCacheNodes.get(contractId);
+
+        if (nodesAll == null || nodesAll.isEmpty()) {
+            //从Redis获取数据
+            Object data = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:" + contractId);
+            if (data != null) {
+                nodesAll = JSON.parseArray(data.toString(), WbsTreeContractLazyVO.class);
+                //更新本地缓存
+                localCacheNodes.put(contractId, nodesAll);
+            } else {
+                //返回数据库数据
+                long startTime = System.currentTimeMillis();
+
+                /*分页查询,每次5000条*/
+                int pageSize = 5000;
+                int pageNumber = 1;
+                int offset;
+
+                nodesAll = new ArrayList<>();
+                List<WbsTreeContractLazyVO> nodesAllPage;
+                do {
+                    offset = (pageNumber - 1) * pageSize;
+                    nodesAllPage = jdbcTemplate.query(
+                            "SELECT p_key_id, id, parent_id FROM m_wbs_tree_contract " +
+                                    "WHERE type = 1 " +
+                                    "AND status = 1 " +
+                                    "AND is_deleted = 0 " +
+                                    "AND contract_id = ? " +
+                                    "LIMIT ? OFFSET ?",
+                            new Object[]{contractId, pageSize, offset},
+                            new BeanPropertyRowMapper<>(WbsTreeContractLazyVO.class)
+                    );
+                    nodesAll.addAll(nodesAllPage);
+                    pageNumber++;
+                } while (nodesAllPage.size() == pageSize);
+
+                long endTime = System.currentTimeMillis();
+                long executionTime = endTime - startTime;
+                System.out.println("合同段 " + contractId + " 查询所有wbs节点树 执行时间:" + executionTime + " ms");
+
+                if (nodesAll.size() > 0) {
+                    //判断是否有子级,赋值
+                    Map<Long, List<WbsTreeContractLazyVO>> groupedByParentId = nodesAll.stream().collect(Collectors.groupingBy(WbsTreeContractLazyVO::getParentId));
+                    for (WbsTreeContractLazyVO vo : nodesAll) {
+                        if (vo.getParentId() == 0) {
+                            vo.setHasChildren(1);
+                        }
+                        List<WbsTreeContractLazyVO> childNodes = groupedByParentId.getOrDefault(vo.getId(), null);
+                        if (childNodes != null && childNodes.size() > 0) {
+                            vo.setHasChildren(1);
+                        } else {
+                            vo.setHasChildren(0);
+                        }
+                    }
+
+                    //存储到Redis中
+                    JSONArray array = JSONArray.parseArray(JSON.toJSONString(nodesAll));
+                    redisTemplate.opsForValue().set("blade-manager::contract:wbstree:" + contractId, JSON.toJSON(array).toString());
+
+                    //更新本地缓存
+                    localCacheNodes.put(contractId, nodesAll);
+                }
+            }
+        }
+        return nodesAll;
+    }
+
+    /**
+     * 获取节点数量统计缓存
+     *
+     * @param contractId
+     * @param lowestNodeParentIdsTB
+     * @param nodesAll
+     * @return
+     */
+    public List<WbsTreeContractLazyVO> getCachedParentCountNodes(String contractId, List<Long> lowestNodeParentIdsTB, List<WbsTreeContractLazyVO> nodesAll, String tableOwner) {
+        //从本地缓存获取数据
+        String cacheKey = contractId + "_" + tableOwner;
+        List<WbsTreeContractLazyVO> resultParentNodesTB = localCacheParentCountNodes.get(cacheKey);
+
+        if (resultParentNodesTB == null || resultParentNodesTB.isEmpty()) {
+
+            //从Redis获取数据
+            Object data = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:byParentCountNodes:" + cacheKey);
+            if (data != null) {
+                resultParentNodesTB = JSON.parseArray(data.toString(), WbsTreeContractLazyVO.class);
+                //更新本地缓存
+                localCacheParentCountNodes.put(cacheKey, resultParentNodesTB);
+
+            } else {
+                /*重新计算,进行递归获取父节点计数统计*/
+                resultParentNodesTB = new ArrayList<>();
+                long startTime = System.currentTimeMillis();
+
+                this.recursiveGetParentNodes(resultParentNodesTB, lowestNodeParentIdsTB, nodesAll);
+
+                long endTime = System.currentTimeMillis();
+                long executionTime = endTime - startTime;
+                System.out.println("合同段 " + contractId + " wbs节点树 数量计算 执行时间:" + executionTime + " ms");
+
+                if (resultParentNodesTB.size() > 0) {
+                    //存储到Redis中
+                    JSONArray array = JSONArray.parseArray(JSON.toJSONString(resultParentNodesTB));
+                    redisTemplate.opsForValue().set("blade-manager::contract:wbstree:byParentCountNodes:" + cacheKey, JSON.toJSON(array).toString());
+
+                    //更新本地缓存
+                    localCacheParentCountNodes.put(cacheKey, resultParentNodesTB);
+                }
+            }
+        }
+
+        return resultParentNodesTB;
+    }
+
+    /**
+     * 构造从最底层节点获取他的所有父级,并统计出现次数(父级Id=子级parentId)
+     *
+     * @param result              结果集
+     * @param lowestNodeParentIds 最底层节点ParentIds
+     * @param nodesAll            所有节点
+     */
+    public void recursiveGetParentNodes(List<WbsTreeContractLazyVO> result, List<Long> lowestNodeParentIds, List<WbsTreeContractLazyVO> nodesAll) {
+        if (lowestNodeParentIds.isEmpty()) {
+            return;
+        }
+
+        Map<Long, Long> parentIdGroup = lowestNodeParentIds.stream()
+                .collect(Collectors.groupingByConcurrent(Function.identity(), Collectors.counting()));
+
+        List<WbsTreeContractLazyVO> collectedNodes = parentIdGroup.entrySet().parallelStream()
+                .flatMap(entry -> {
+                    List<WbsTreeContractLazyVO> nodes = nodesAll.stream()
+                            .filter(f -> entry.getKey().equals(f.getId()))
+                            .collect(Collectors.toList());
+
+                    if (entry.getValue() > 1L) {
+                        nodes = nodes.stream().limit(1)
+                                .flatMap(node -> Collections.nCopies(entry.getValue().intValue(), node).stream())
+                                .collect(Collectors.toList());
+                    }
+
+                    return nodes.stream();
+                })
+                .collect(Collectors.toList());
+
+        List<Long> collect = collectedNodes.stream()
+                .map(WbsTreeContractLazyVO::getParentId)
+                .collect(Collectors.toList());
+
+        if (!collect.isEmpty()) {
+            result.addAll(collectedNodes);
+            this.recursiveGetParentNodes(result, collect, nodesAll);
+        }
+    }
+
+    /**
+     * 获取当前合同段所有填报资料缓存信息
+     *
+     * @param contractId
+     * @param tableOwner
+     * @return
+     */
+    public List<WbsTreeContractLazyQueryInfoVO> getQueryInfoList(String contractId, String tableOwner) {
+        //从本地缓存获取
+        String cacheKey = contractId + "_" + tableOwner;
+        List<WbsTreeContractLazyQueryInfoVO> cachedQueryInfoList = localCacheQueryInfos.get(cacheKey);
+
+        if (cachedQueryInfoList != null && !cachedQueryInfoList.isEmpty()) {
+            return cachedQueryInfoList;
+        }
+
+        //从Redis获取数据
+        Object dataInformationQuery = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:byInformationQuery:" + cacheKey);
+
+        List<WbsTreeContractLazyQueryInfoVO> queryInfoList = new ArrayList<>();
+        if (dataInformationQuery != null) {
+            queryInfoList = JSON.parseArray(dataInformationQuery.toString(), WbsTreeContractLazyQueryInfoVO.class);
+            //更新本地缓存
+            localCacheQueryInfos.put(cacheKey, queryInfoList);
+
+        } else {
+            //返回数据库数据
+            long startTime = System.currentTimeMillis();
+
+            /*分页查询,每次5000条*/
+            int pageSize = 5000;
+            int pageNumber = 1;
+            int offset;
+
+            List<WbsTreeContractLazyQueryInfoVO> queryInfoListPage;
+
+            do {
+                offset = (pageNumber - 1) * pageSize;
+                queryInfoListPage = jdbcTemplate.query(
+                        "SELECT wbs_id, status FROM u_information_query " +
+                                "WHERE type = 1 " +
+                                "AND contract_id = ? " +
+                                "AND classify = ? " +
+                                "LIMIT ? OFFSET ?",
+                        new Object[]{contractId, tableOwner, pageSize, offset},
+                        new BeanPropertyRowMapper<>(WbsTreeContractLazyQueryInfoVO.class));
+                queryInfoList.addAll(queryInfoListPage);
+                pageNumber++;
+            } while (queryInfoListPage.size() == pageSize);
+
+            long endTime = System.currentTimeMillis();
+            long executionTime = endTime - startTime;
+            System.out.println("合同段 " + contractId + " 查询所有资料信息 执行时间:" + executionTime + " ms");
+
+            if (queryInfoList.size() > 0) {
+                JSONArray array = JSONArray.parseArray(JSON.toJSONString(queryInfoList));
+
+                //存储到Redis中
+                redisTemplate.opsForValue().set("blade-manager::contract:wbstree:byInformationQuery:" + cacheKey, JSON.toJSON(array).toString());
+
+                //更新本地缓存
+                localCacheQueryInfos.put(cacheKey, queryInfoList);
+            }
+        }
+
+        return queryInfoList;
+    }
+
+    //转换VO
+    public NodeVO convertToNodeVO(WbsTreeContractLazyVO wbsTreeContractLazyVO) {
+        NodeVO nodeVO = new NodeVO();
+        nodeVO.setId(wbsTreeContractLazyVO.getId());
+        nodeVO.setParentId(wbsTreeContractLazyVO.getParentId());
+        nodeVO.setPKeyId(wbsTreeContractLazyVO.getPKeyId());
+        nodeVO.setStatus(cn.hutool.core.util.ObjectUtil.isNotEmpty(wbsTreeContractLazyVO.getColorStatus()) ? wbsTreeContractLazyVO.getColorStatus() : 1);
+        return nodeVO;
+    }
+
+    /**
+     * 构造树形结构数据 (解决节点颜色问题)
+     *
+     * @param distinctNodesAll 去重后所有节点数据
+     * @return
+     */
+    public List<NodeVO> buildNodeTreeByStream(List<WbsTreeContractLazyVO> distinctNodesAll,
+                                              Map<Long, WbsTreeContractLazyVO> lowestNodesMap) {
+        List<WbsTreeContractLazyVO> list = distinctNodesAll.stream().filter(f -> f.getParentId().equals(0L)).collect(Collectors.toList());
+        Map<Long, List<WbsTreeContractLazyVO>> map = distinctNodesAll.stream().collect(Collectors.groupingBy(WbsTreeContractLazyVO::getParentId));
+        return recursionFnNodeTree(list, map, lowestNodesMap);
+    }
+
+    public List<NodeVO> recursionFnNodeTree(List<WbsTreeContractLazyVO> list, Map<Long, List<WbsTreeContractLazyVO>> map,
+                                            Map<Long, WbsTreeContractLazyVO> lowestNodesMap) {
+        List<NodeVO> result = new ArrayList<>();
+        for (WbsTreeContractLazyVO vo : list) {
+            if (vo.getHasChildren().equals(0)) {
+                WbsTreeContractLazyVO lowestNodeVO = lowestNodesMap.getOrDefault(vo.getPKeyId(), null);
+                if (lowestNodeVO != null && cn.hutool.core.util.ObjectUtil.isNotEmpty(lowestNodeVO.getColorStatus())) {
+                    //最底层颜色初始化
+                    vo.setColorStatus(lowestNodeVO.getColorStatus());
+                }
+            } else {
+                //非最底层节点,颜色默认=1黑色
+                vo.setColorStatus(1);
+            }
+            //转换为NodeVO
+            NodeVO nodeVO = convertToNodeVO(vo);
+            List<WbsTreeContractLazyVO> childrenList = map.get(vo.getId());
+            if (childrenList != null && !childrenList.isEmpty()) {
+                nodeVO.setChildren(recursionFnNodeTree(childrenList, map, lowestNodesMap));
+            }
+            result.add(nodeVO);
+        }
+        return result;
+    }
+
+    //把树形结构转为普通List
+    public List<NodeVO> flattenTree(List<NodeVO> tree) {
+        List<NodeVO> result = new ArrayList<>();
+        for (NodeVO node : tree) {
+            result.add(node);
+            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
+                result.addAll(flattenTree(node.getChildren()));
+                node.setChildren(null);
+            }
+        }
+        return result;
+    }
+
 }