Просмотр исходного кода

质检合同段wbs节点收藏、历史记录、隐藏节点

lvy 1 день назад
Родитель
Сommit
5fb91877fd
15 измененных файлов с 1168 добавлено и 11 удалено
  1. 103 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ContractCollectFolder.java
  2. 12 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ContractInfo.java
  3. 22 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/ContractCollectFolderVO.java
  4. 2 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeContractLazyVO.java
  5. 9 0
      blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeContractTreeAllVO.java
  6. 53 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ContractCollectFolderController.java
  7. 98 3
      blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ContractInfoController.java
  8. 8 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractCollectFolderMapper.java
  9. 20 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractCollectFolderMapper.xml
  10. 20 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IContractCollectFolderService.java
  11. 5 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/IContractInfoService.java
  12. 220 0
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractCollectFolderServiceImpl.java
  13. 574 5
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractInfoServiceImpl.java
  14. 15 0
      blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java
  15. 7 3
      blade-service/blade-user/src/main/java/org/springblade/system/user/util/ComplexStringComparator.java

+ 103 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ContractCollectFolder.java

@@ -0,0 +1,103 @@
+package org.springblade.manager.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("m_contract_collect_folder")
+@NoArgsConstructor
+@AllArgsConstructor
+public class ContractCollectFolder implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("主键id")
+    @TableId(
+            value = "id",
+            type = IdType.ASSIGN_ID
+    )
+    private Long id;
+
+    /**
+     * 父主键
+     */
+    @ApiModelProperty(value = "父主键id")
+    private Long parentId;
+
+    /**
+     * 收藏夹名称
+     */
+    @ApiModelProperty("收藏夹名称")
+    private String folderName;
+    /**
+     * 节点Id
+     */
+    @ApiModelProperty("节点Id")
+    private Long nodeId;
+
+    /**
+     * 节点父Id
+     */
+    @ApiModelProperty("节点父Id")
+    private Long nodeParentId;
+
+    /**
+     * 节点路径pKeyIds
+     */
+    @ApiModelProperty("节点路径pKeyIds")
+    @TableField(exist = false)
+    private String nodeAncestors;
+
+    /**
+     * 合同Id
+     */
+    @ApiModelProperty("合同段Id")
+    private Long contractId;
+
+    /**
+     * 类型, 0 收藏夹, 1 节点
+     */
+    @ApiModelProperty(value = "类型, 0 收藏夹, 1 节点")
+    private Integer type;
+
+    /**
+     * 排序
+     */
+    @ApiModelProperty(value = "排序")
+    private Integer sort;
+
+    @ApiModelProperty("创建人")
+    private Long createUser;
+
+    @DateTimeFormat(
+            pattern = "yyyy-MM-dd HH:mm:ss"
+    )
+    @JsonFormat(
+            pattern = "yyyy-MM-dd HH:mm:ss"
+    )
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+
+
+    @DateTimeFormat(
+            pattern = "yyyy-MM-dd HH:mm:ss"
+    )
+    @JsonFormat(
+            pattern = "yyyy-MM-dd HH:mm:ss"
+    )
+    @ApiModelProperty("更新时间")
+    private Date updateTime;
+
+    @ApiModelProperty("业务状态")
+    private Integer status;
+
+    @ApiModelProperty("是否已删除")
+    private Integer isDeleted;
+}

+ 12 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ContractInfo.java

@@ -184,6 +184,18 @@ public class ContractInfo extends BaseEntity {
     @ApiModelProperty(value = "1正在扫描中 ,2没有扫描")
     private Integer isScan;
 
+    @ApiModelProperty(value = "电签项目id")
+    private String sealProjectId;
+
+    @ApiModelProperty(value = "账号id")
+    private String sealAccountId;
+
+    @ApiModelProperty(value = "客户编号")
+    private String sealCustomerId;
+
+    @ApiModelProperty(value = "通信key")
+    private String sealCommKey;
+
     public BigDecimal getProjectMileage() {
         if (projectMileage == null){
             return null;

+ 22 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/ContractCollectFolderVO.java

@@ -0,0 +1,22 @@
+package org.springblade.manager.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class ContractCollectFolderVO {
+    @ApiModelProperty(value = "id, 收藏夹id")
+    private Long id;
+
+    @ApiModelProperty(value = "收藏夹名称")
+    private String name;
+
+    @ApiModelProperty(value = "节点pKeyId, 收藏和取消收藏时使用")
+    private Long nodeId;
+
+    @ApiModelProperty(value = "合同段id")
+    private Long contractId;
+
+    @ApiModelProperty(value = "1收藏,0取消收藏")
+    private Integer type;
+}

+ 2 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeContractLazyVO.java

@@ -142,4 +142,6 @@ public class WbsTreeContractLazyVO implements Serializable {
     @ApiModelProperty(value = "是否最近同步节点")
     private Integer isSync;
 
+    @ApiModelProperty(value = "是否收藏")
+    private Integer isCollect;
 }

+ 9 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/vo/WbsTreeContractTreeAllVO.java

@@ -53,6 +53,15 @@ public class WbsTreeContractTreeAllVO implements INode<WbsTreeContractTreeAllVO>
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private Boolean hasChildren;
 
+    @ApiModelProperty(value = "是否收藏")
+    private Integer isCollect;
+
+    @ApiModelProperty(value = "是否隐藏,2为隐藏")
+    private Integer isBussShow;
+
+    @ApiModelProperty("是否存在下级")
+    private Boolean notExsitChild;
+
     @Override
     public List<WbsTreeContractTreeAllVO> getChildren() {
         if (this.children == null) {

+ 53 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ContractCollectFolderController.java

@@ -0,0 +1,53 @@
+package org.springblade.manager.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.manager.service.IContractCollectFolderService;
+import org.springblade.manager.vo.*;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+@RestController
+@AllArgsConstructor
+@RequestMapping("/contractCollectFolder")
+@Api(value = "用户合同段wbs收藏", tags = "用户合同段wbs收藏接口")
+public class ContractCollectFolderController extends BladeController {
+
+    private final IContractCollectFolderService contractCollectFolderService;
+
+    @GetMapping("/folderList")
+    @ApiOperation(value = "收藏文件夹列表")
+    public R<List<ContractCollectFolderVO>> folderList(Long contractId){
+        return R.data(contractCollectFolderService.folderList(contractId));
+    }
+
+    @PostMapping("/deleteFolder")
+    @ApiOperation(value = "删除文件夹")
+    public R<Boolean> deleteFolder(Long id){
+        return R.data(contractCollectFolderService.deleteFolder(id));
+    }
+
+    @PostMapping("/saveOrUpdateFolder")
+    @ApiOperation(value = "编辑文件夹")
+    public R<Boolean> saveOrUpdateFolder(@RequestBody ContractCollectFolderVO vo){
+        return R.data(contractCollectFolderService.saveOrUpdateFolder(vo));
+    }
+
+    @PostMapping("/sortFolder")
+    @ApiOperation(value = "排序")
+    public R<Boolean> sortFolder(String ids){
+        return R.data(contractCollectFolderService.sortFolder(ids));
+    }
+
+    @PostMapping("/collect")
+    @ApiOperation(value = "收藏")
+    public R<Boolean> collect(@RequestBody ContractCollectFolderVO vo){
+        return R.data(contractCollectFolderService.collect(vo));
+    }
+
+}

+ 98 - 3
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ContractInfoController.java

@@ -1,6 +1,9 @@
 package org.springblade.manager.controller;
 
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import io.swagger.annotations.*;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
@@ -9,19 +12,21 @@ import lombok.AllArgsConstructor;
 import javax.validation.Valid;
 
 import org.apache.commons.lang.StringUtils;
+import org.jetbrains.annotations.Nullable;
+import org.springblade.business.entity.WbsTreeContractStatistics;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
+import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.secure.utils.SecureUtil;
 import org.springblade.core.tool.api.R;
-import org.springblade.core.tool.utils.BeanUtil;
-import org.springblade.core.tool.utils.Func;
-import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.core.tool.utils.*;
 import org.springblade.manager.dto.FindAllUserByConditionDTO;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
 import org.springblade.manager.dto.WbsTreeContractDTO;
 import org.springblade.manager.entity.ContractRelationJlyz;
+import org.springblade.manager.entity.WbsTreeContract;
 import org.springblade.manager.mapper.SaveUserInfoByProjectMapper;
 import org.springblade.manager.service.IWbsTreeContractService;
 import org.springblade.manager.service.SaveUserInfoByProjectService;
@@ -31,6 +36,7 @@ import org.springblade.system.user.entity.User;
 import org.springblade.system.user.feign.IUserClient;
 import org.springblade.system.user.vo.UserContractInfoVO;
 import org.springblade.system.user.vo.UserVO2;
+import org.springframework.dao.DataAccessException;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.transaction.annotation.Transactional;
@@ -830,4 +836,93 @@ public class ContractInfoController extends BladeController {
         List<ContractInfo> list = contractInfoService.getContractInfoByContractId(contractId);
         return R.data(list);
     }
+
+    @GetMapping("/saveHistory")
+    @ApiOperationSupport(order = 23)
+    @ApiOperation(value = "最近n天的保存记录", notes = "最近n天的保存记录, 传递contractId")
+    public R<Object> saveHistory(@RequestParam String queryValue, @RequestParam String contractId, @RequestParam String tableOwner, @RequestParam(defaultValue = "5", required = false) Integer  days) {
+        R<Object> result = contractInfoService.queryWbsTreeNodeBySaveOrHidden(contractId, tableOwner, days, queryValue, 1);
+        R<Object> data = doResult(contractId, result);
+        if (data != null) return data;
+        return R.fail(200, "未查询到信息");
+    }
+    @GetMapping("/getHiddenTreeNode")
+    @ApiOperationSupport(order = 26)
+    @ApiOperation(value = "获取隐藏节点树", notes = "获取隐藏节点树")
+    public R<Object> getHiddenTreeNode(@RequestParam String queryValue, @RequestParam String contractId, @RequestParam String tableOwner) {
+        R<Object> result = contractInfoService.queryWbsTreeNodeBySaveOrHidden(contractId, tableOwner,null, queryValue, 2);
+        R<Object> data = doResult(contractId, result);
+        if (data != null) return data;
+        return R.fail(200, "未查询到信息");
+    }
+
+    @GetMapping("/getCollectTreeNode")
+    @ApiOperationSupport(order = 26)
+    @ApiOperation(value = "获取收藏节点树", notes = "获取收藏节点树")
+    public R<Object> getCollectTreeNode(@RequestParam String queryValue, @RequestParam String contractId, @RequestParam String tableOwner, @RequestParam Long folderId, @RequestParam Long pKeyId) {
+        R<Object> result = contractInfoService.getCollectTreeNode(contractId, tableOwner,folderId, queryValue,pKeyId);
+        R<Object> data = doResult(contractId, result);
+        if (data != null) return data;
+        return R.fail(200, "未查询到信息");
+    }
+    @GetMapping("/getCollectTreeNodeByQuery")
+    @ApiOperationSupport(order = 27)
+    @ApiOperation(value = "搜索收藏节点树", notes = "搜索收藏节点树")
+    public R<Object> getCollectTreeNodeByQuery(@RequestParam String queryValue, @RequestParam String contractId, @RequestParam String tableOwner, @RequestParam Long folderId) {
+        R<Object> result = contractInfoService.getCollectTreeNodeByQuery(contractId, tableOwner,folderId, queryValue);
+        R<Object> data = doResult(contractId, result);
+        if (data != null) return data;
+        return R.fail(200, "未查询到信息");
+    }
+
+
+    @Nullable
+    private R<Object> doResult(String contractId, R<Object> result) {
+        if (ObjectUtil.isNotEmpty(result) && ObjectUtil.isNotEmpty(result.getData())) {
+            Object data = result.getData();
+            ContractInfo contractInfo = contractInfoService.getBaseMapper().selectById(contractId);
+            if (data instanceof List) {
+                List<?> dataList = (List<?>) data;
+                if (contractInfo.getContractType().equals(1)) {
+                    for (Object item : dataList) {
+                        if (item instanceof WbsTreeContractTreeAllVO) {
+                            WbsTreeContractTreeAllVO wbsTreeContractVO = (WbsTreeContractTreeAllVO) item;
+                            if (ObjectUtil.isNotEmpty(wbsTreeContractVO.getParentId()) && 0L == wbsTreeContractVO.getParentId()) {
+                                wbsTreeContractVO.setTitle(contractInfo.getContractName());
+                                break;
+                            }
+                        }
+                    }
+                    return R.data(data);
+                }
+            } else if (data instanceof Map) {
+                Map<?, ?> dataMap = (Map<?, ?>) data;
+                if (contractInfo.getContractType().equals(2) || contractInfo.getContractType().equals(3)) {
+                    List<WbsTreeContractTreeAllVO> jlYzList = new LinkedList<>();
+                    for (Map.Entry<?, ?> entry : dataMap.entrySet()) {
+                        Object key = entry.getKey();
+                        Object value = entry.getValue();
+                        if (key instanceof Long && value instanceof List) {
+                            Long mapKey = (Long) key;
+                            List<?> mapValue = (List<?>) value;
+                            ContractInfo contractInfoJlYz = contractInfoService.getBaseMapper().selectById(mapKey);
+                            List<? extends WbsTreeContractTreeAllVO> typedList = mapValue.stream()
+                                    .filter(item -> item instanceof WbsTreeContractTreeAllVO)
+                                    .map(item -> (WbsTreeContractTreeAllVO) item)
+                                    .collect(Collectors.toList());
+                            for (WbsTreeContractTreeAllVO wbsTreeContractVO : typedList) {
+                                if (ObjectUtil.isNotEmpty(wbsTreeContractVO.getParentId()) && 0L == wbsTreeContractVO.getParentId()) {
+                                    wbsTreeContractVO.setTitle(contractInfoJlYz == null ? wbsTreeContractVO.getTitle() : contractInfoJlYz.getContractName());
+                                    break;
+                                }
+                            }
+                            jlYzList.addAll(typedList);
+                        }
+                    }
+                    return R.data(jlYzList);
+                }
+            }
+        }
+        return null;
+    }
 }

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

@@ -0,0 +1,8 @@
+package org.springblade.manager.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.manager.entity.ContractCollectFolder;
+
+public interface ContractCollectFolderMapper extends BaseMapper<ContractCollectFolder> {
+
+}

+ 20 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/ContractCollectFolderMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.springblade.manager.mapper.ContractCollectFolderMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="wbsFormElementResultMap" type="org.springblade.manager.entity.ContractCollectFolder">
+        <result column="id" property="id"/>
+        <result column="parent_id" property="parentId"/>
+        <result column="folder_name" property="folderName"/>
+        <result column="contract_id" property="contractId"/>
+        <result column="node_id" property="nodeId"/>
+        <result column="type" property="type"/>
+        <result column="sort" property="sort"/>
+        <result column="create_time" property="createTime"/>
+        <result column="create_user" property="createUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+    </resultMap>
+</mapper>

+ 20 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IContractCollectFolderService.java

@@ -0,0 +1,20 @@
+package org.springblade.manager.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springblade.manager.entity.ContractCollectFolder;
+import org.springblade.manager.vo.ContractCollectFolderVO;
+
+import java.util.List;
+
+public interface IContractCollectFolderService extends IService<ContractCollectFolder> {
+
+    List<ContractCollectFolderVO> folderList(Long contractId);
+
+    Boolean deleteFolder(Long id);
+
+    Boolean saveOrUpdateFolder(ContractCollectFolderVO vo);
+
+    Boolean collect(ContractCollectFolderVO vo);
+
+    Boolean sortFolder(String ids);
+}

+ 5 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/IContractInfoService.java

@@ -86,4 +86,9 @@ public interface IContractInfoService extends BaseService<ContractInfo> {
     void updateIsArchivesAutoById(Long id, Integer isArchivesAuto);
 
     List<ContractInfo> getContractInfoByContractId(String contractId);
+
+    R<Object> queryWbsTreeNodeBySaveOrHidden(String contractId, String tableOwner, Integer days, String queryValue, Integer type);
+
+    R<Object> getCollectTreeNode(String contractId, String tableOwner, Long folderId, String queryValue, Long pKeyId);
+    R<Object> getCollectTreeNodeByQuery(String contractId, String tableOwner, Long folderId, String queryValue);
 }

+ 220 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractCollectFolderServiceImpl.java

@@ -0,0 +1,220 @@
+package org.springblade.manager.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.manager.entity.ContractCollectFolder;
+import org.springblade.manager.entity.WbsTree;
+import org.springblade.manager.entity.WbsTreeContract;
+import org.springblade.manager.mapper.ContractCollectFolderMapper;
+import org.springblade.manager.service.IContractCollectFolderService;
+import org.springblade.manager.service.IWbsTreeContractService;
+import org.springblade.manager.vo.ContractCollectFolderVO;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+public class ContractCollectFolderServiceImpl extends ServiceImpl<ContractCollectFolderMapper, ContractCollectFolder> implements IContractCollectFolderService {
+    private final IWbsTreeContractService wbsTreeContractService;
+    private final StringRedisTemplate redisTemplate;
+
+    private static final Logger log = LoggerFactory.getLogger(ContractCollectFolderServiceImpl.class);
+
+    @Override
+    public List<ContractCollectFolderVO> folderList(Long contractId) {
+        List<ContractCollectFolder> list = this.list(Wrappers.<ContractCollectFolder>lambdaQuery().select(ContractCollectFolder::getId, ContractCollectFolder::getFolderName)
+                .eq(ContractCollectFolder::getType, 0).eq(ContractCollectFolder::getContractId, contractId).orderByAsc(ContractCollectFolder::getSort).orderByDesc(ContractCollectFolder::getCreateTime));
+        if (list == null || list.isEmpty()) {
+            return new ArrayList<>();
+        }
+        return list.stream().map(item -> {
+            ContractCollectFolderVO vo = new ContractCollectFolderVO();
+            vo.setId(item.getId());
+            vo.setName(item.getFolderName());
+            return vo;
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional
+    public Boolean deleteFolder(Long id) {
+        ContractCollectFolder entity = this.getById(id);
+        if (entity != null) {
+            deletedCache(entity.getContractId());
+            boolean b = this.removeById(id);
+            if (b) {
+                this.remove(Wrappers.<ContractCollectFolder>lambdaQuery().eq(ContractCollectFolder::getParentId, id).eq(ContractCollectFolder::getType, 1).eq(ContractCollectFolder::getContractId, entity.getContractId()));
+            }
+            return b;
+        }
+        return true;
+    }
+
+    @Override
+    public Boolean saveOrUpdateFolder(ContractCollectFolderVO vo) {
+        if (vo == null || StringUtil.isBlank(vo.getName())) {
+            return false;
+        }
+        String lockKey = "blade-manager:lock:contract:collectFolder:" + vo.getContractId();
+        String lockValue = UUID.randomUUID().toString();
+        try {
+            // 尝试获取分布式锁,设置过期时间防止死锁
+            Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, Duration.ofSeconds(30));
+            if (lockAcquired == null || !lockAcquired) {
+                throw new ServiceException("系统繁忙,请稍后再试");
+            }
+            long count = this.count(Wrappers.<ContractCollectFolder>lambdaQuery().eq(ContractCollectFolder::getType, 0).eq(ContractCollectFolder::getContractId, vo.getContractId())
+                    .eq(ContractCollectFolder::getFolderName, vo.getName()).ne(vo.getId() != null, ContractCollectFolder::getId, vo.getId()).last(" limit 1"));
+            if (count > 0) {
+                throw new ServiceException("收藏夹名称重复");
+            }
+            deletedCache(vo.getContractId());
+            if (vo.getId() == null) {
+                ContractCollectFolder entity = new ContractCollectFolder();
+                entity.setContractId(vo.getContractId());
+                entity.setCreateUser(AuthUtil.getUserId());
+                entity.setType(0);
+                entity.setId(SnowFlakeUtil.getId());
+                entity.setFolderName(vo.getName());
+                return this.save(entity);
+            }
+            return this.update(Wrappers.<ContractCollectFolder>lambdaUpdate()
+                    .set(ContractCollectFolder::getFolderName, vo.getName())
+                    .eq(ContractCollectFolder::getId, vo.getId()));
+        } finally {
+            // 释放分布式锁
+            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
+            redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Collections.singletonList(lockKey), lockValue);
+        }
+    }
+
+    @Override
+    public Boolean collect(ContractCollectFolderVO vo) {
+        if (vo == null || vo.getNodeId() == null || (vo.getId() == null && vo.getType() != 0)) {
+            throw new ServiceException("参数错误");
+        }
+        WbsTreeContract wbsTreeContract = wbsTreeContractService.getOne(Wrappers.<WbsTreeContract>lambdaQuery().eq(WbsTreeContract::getPKeyId, vo.getNodeId()));
+        if (wbsTreeContract == null) {
+            throw new ServiceException("节点不存在或者已被删除");
+        }
+        ContractCollectFolder node = this.getOne(Wrappers.<ContractCollectFolder>lambdaQuery().eq(ContractCollectFolder::getNodeId, vo.getNodeId()).eq(ContractCollectFolder::getType, 1).eq(ContractCollectFolder::getContractId, vo.getContractId()));
+        deletedCache(vo.getContractId());
+        if (vo.getType() == 0) {
+            // 取消
+            if (node == null) {
+                return true;
+            }
+            List<WbsTreeContract> childrenList = new ArrayList<>();
+            getAllChildren(Collections.singleton(vo.getNodeId()), childrenList);
+            childrenList.add(wbsTreeContract);
+            Set<Long> nodeIds = childrenList.stream().map(WbsTreeContract::getPKeyId).collect(Collectors.toSet());
+            this.remove(Wrappers.<ContractCollectFolder>lambdaQuery().eq(ContractCollectFolder::getParentId, node.getParentId())
+                    .eq(ContractCollectFolder::getType, 1).eq(ContractCollectFolder::getContractId, vo.getContractId()).in(ContractCollectFolder::getNodeId, nodeIds));
+        } else if (vo.getType() == 1) {
+            ContractCollectFolder collectFolder = this.getById(vo.getId());
+            if (collectFolder == null) {
+                throw new ServiceException("收藏夹不存在");
+            }
+            if (node == null) {
+                List<WbsTreeContract> childrenList = new ArrayList<>();
+                getAllChildren(Collections.singleton(vo.getNodeId()), childrenList);
+                childrenList.add(wbsTreeContract);
+                Map<Long, WbsTreeContract> nodeMap = childrenList.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, v -> v, (v1, v2) -> v1));
+                List<ContractCollectFolder> list = this.list(Wrappers.<ContractCollectFolder>lambdaQuery().in(ContractCollectFolder::getNodeId, nodeMap.keySet())
+                        .eq(ContractCollectFolder::getType, 1).eq(ContractCollectFolder::getContractId, vo.getContractId()));
+                for (ContractCollectFolder contractCollectFolder : list) {
+                    nodeMap.remove(contractCollectFolder.getNodeId());
+                }
+                if (nodeMap.isEmpty()) {
+                    return true;
+                }
+                List<ContractCollectFolder> nodes = new ArrayList<>();
+                for (WbsTreeContract contract : nodeMap.values()) {
+                    ContractCollectFolder node1 = new ContractCollectFolder();
+                    node1.setId(SnowFlakeUtil.getId());
+                    node1.setNodeId(contract.getPKeyId());
+                    node1.setContractId(vo.getContractId());
+                    node1.setType(1);
+                    node1.setCreateUser(AuthUtil.getUserId());
+                    node1.setParentId(vo.getId());
+                    nodes.add(node1);
+                }
+                return this.saveBatch(nodes);
+            }
+            ContractCollectFolder folder = this.getById(node.getParentId());
+            if (folder != null) {
+                throw new ServiceException("该节点已被【 "+ folder.getFolderName() +" 】收藏");
+            }
+            return this.update(Wrappers.<ContractCollectFolder>lambdaUpdate().set(ContractCollectFolder::getParentId, vo.getId())
+                    .eq(ContractCollectFolder::getId, node.getId()));
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional
+    public Boolean sortFolder(String ids) {
+        if (StringUtil.isBlank(ids)) {
+            return true;
+        }
+        int sort = 1;
+        String[] split = ids.split(",");
+        for (String s : split) {
+            if (StringUtil.isNumeric(s)) {
+                long id = Long.parseLong(s);
+                if (id <= 0) {
+                    continue;
+                }
+                this.update(Wrappers.<ContractCollectFolder>lambdaUpdate().set(ContractCollectFolder::getSort, sort++)
+                        .eq(ContractCollectFolder::getId, id).eq(ContractCollectFolder::getCreateUser, AuthUtil.getUserId()));
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 获取所有子节点
+     */
+    public void getAllChildren(Collection<Long> pKeyIds, List<WbsTreeContract> childrenList) {
+        if (pKeyIds == null || pKeyIds.isEmpty()) {
+            return;
+        }
+        List<WbsTreeContract> list = wbsTreeContractService.list(Wrappers.<WbsTreeContract>lambdaQuery().select(WbsTreeContract::getPKeyId, WbsTreeContract::getAncestorsPId, WbsTreeContract::getPId)
+                .in(WbsTreeContract::getPId, pKeyIds).eq(WbsTreeContract::getStatus, 1).eq(WbsTreeContract::getType, 1));
+        if (list == null || list.isEmpty()) {
+            return;
+        }
+        childrenList.addAll(list);
+        Set<Long> ids = list.stream().map(WbsTreeContract::getPKeyId).collect(Collectors.toSet());
+        getAllChildren(ids, childrenList);
+    }
+    public void deletedCache(Long contractId) {
+        CompletableFuture.runAsync(() -> {
+            redisTemplate.delete("blade-manager::contract:collectFolder:" + contractId);
+            try {
+                Thread.sleep(5000);
+                redisTemplate.delete("blade-manager::contract:collectFolder:" + contractId);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                redisTemplate.delete("blade-manager::contract:collectFolder:" + contractId);
+            }
+        });
+    }
+}

+ 574 - 5
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/ContractInfoServiceImpl.java

@@ -1,5 +1,6 @@
 package org.springblade.manager.service.impl;
 
+import cn.hutool.core.date.DateUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
@@ -10,24 +11,25 @@ import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
+import org.jetbrains.annotations.NotNull;
 import org.springblade.business.entity.WbsTreeContractStatistics;
 import org.springblade.business.feign.InformationQueryClient;
 import org.springblade.common.utils.BaiduApiUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.secure.utils.SecureUtil;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.node.ForestNodeMerger;
-import org.springblade.core.tool.utils.BeanUtil;
-import org.springblade.core.tool.utils.CollectionUtil;
-import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.core.tool.utils.*;
 import org.springblade.manager.bean.NodeVO;
 import org.springblade.manager.dto.FindAllUserByConditionDTO;
 import org.springblade.manager.dto.SaveUserInfoByProjectDTO;
 import org.springblade.manager.entity.*;
 import org.springblade.manager.mapper.*;
 import org.springblade.manager.service.IContractInfoService;
+import org.springblade.manager.utils.ComplexStringComparator;
 import org.springblade.manager.vo.*;
 import org.springblade.meter.entity.MeterContractInfo;
 import org.springblade.meter.feign.MeterContractInfoClient;
@@ -40,6 +42,7 @@ import org.springframework.dao.DataAccessException;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.SingleColumnRowMapper;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -47,6 +50,7 @@ import org.springframework.transaction.annotation.Transactional;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -392,6 +396,7 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
 
             if (contractInfo != null) {
                 /*质检施工合同段*/
+                Map<Long, String> collectNodesMap = getCollectNodesByContractId(contractId);
                 if (contractInfo.getContractType().equals(1)) {
                     LambdaQueryWrapper<WbsTreeContract> queryWrapper = new LambdaQueryWrapper<>();
                     queryWrapper.select(WbsTreeContract::getParentId, WbsTreeContract::getId, WbsTreeContract::getPKeyId);
@@ -402,6 +407,7 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
                     }
                     queryWrapper.eq(WbsTreeContract::getContractId, contractId);
                     queryWrapper.eq(WbsTreeContract::getType, 1);
+                    queryWrapper.and(wrapper -> wrapper.ne(WbsTreeContract::getIsBussShow, 2).or().isNull(WbsTreeContract::getIsBussShow));
                     List<WbsTreeContract> nodes = wbsTreeContractMapper.selectList(queryWrapper);
                     if (nodes.size() > 0) {
                         List<WbsTreeContractLazyVO> nodesAllTemp;
@@ -545,6 +551,11 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
                                                 //判断是否展示同步标识
                                                 vo.setIsSync(syncPKeyIds.contains(vo.getPrimaryKeyId().toString()) ? 1 : 0);
                                             }
+                                            if (collectNodesMap.containsKey(vo.getPrimaryKeyId())) {
+                                                vo.setIsCollect(1);
+                                            } else {
+                                                vo.setIsCollect(0);
+                                            }
                                         }
                                         return vo;
                                     }).collect(Collectors.toList());
@@ -570,6 +581,7 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
                         }
                         queryWrapper.eq(WbsTreeContract::getContractId, contractRelationJlyz.getContractIdSg());
                         queryWrapper.eq(WbsTreeContract::getType, 1);
+                        queryWrapper.and(wrapper -> wrapper.ne(WbsTreeContract::getIsBussShow, 2).or().isNull(WbsTreeContract::getIsBussShow));
                         List<WbsTreeContract> nodes = wbsTreeContractMapper.selectList(queryWrapper);
                         if (nodes.size() > 0) {
                             List<WbsTreeContractLazyVO> nodesAllTemp;
@@ -714,6 +726,11 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
                                                     //判断是否展示同步标识
                                                     vo.setIsSync(syncPKeyIds.contains(vo.getPrimaryKeyId().toString()) ? 1 : 0);
                                                 }
+                                                if (collectNodesMap.containsKey(vo.getPrimaryKeyId())) {
+                                                    vo.setIsCollect(1);
+                                                } else {
+                                                    vo.setIsCollect(0);
+                                                }
                                             }
                                             return vo;
                                         }).collect(Collectors.toList());
@@ -820,7 +837,17 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
 
     public List<WbsTreeContractTreeAllVO> buildWbsTreeByStreamByTreeAll(List<WbsTreeContractTreeAllVO> wbsTreeVO2s) {
         List<WbsTreeContractTreeAllVO> list = wbsTreeVO2s.stream().filter(f -> f.getParentId() == 0L).collect(Collectors.toList());
-        Map<Long, List<WbsTreeContractTreeAllVO>> map = wbsTreeVO2s.stream().collect(Collectors.groupingBy(WbsTreeContractTreeAllVO::getParentId));
+        // 修改比较器链,处理空值情况
+        Comparator<WbsTreeContractTreeAllVO> safeComparator = Comparator
+                .comparing(WbsTreeContractTreeAllVO::getSort, Comparator.nullsFirst(Comparator.naturalOrder()))
+                .thenComparing(new ComplexStringComparator<>(obj ->
+                        obj.getTitle() != null ? obj.getTitle() : ""))
+                .thenComparing(
+                        WbsTreeContractTreeAllVO::getId,
+                        Comparator.nullsLast(Comparator.reverseOrder()));
+        list.sort(safeComparator);
+        Map<Long, List<WbsTreeContractTreeAllVO>> map = wbsTreeVO2s.stream().collect(Collectors.groupingBy(WbsTreeContractTreeAllVO::getParentId,
+                Collectors.collectingAndThen(Collectors.toList(), items -> items.stream().sorted(safeComparator).collect(Collectors.toList()))));
         this.recursionFnTreeAll(list, map);
         return list;
     }
@@ -828,7 +855,16 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
         String[] split = position.split("\\\\");
         if (split.length > 1 && split[1] != null && !split[1].isEmpty()) {
             List<WbsTreeContractTreeAllVO> list = wbsTreeVO2s.stream().filter(f -> f.getParentId() == 0L).collect(Collectors.toList());
-            Map<Long, List<WbsTreeContractTreeAllVO>> map = wbsTreeVO2s.stream().collect(Collectors.groupingBy(WbsTreeContractTreeAllVO::getParentId));
+            // 修改比较器链,处理空值情况
+            Comparator<WbsTreeContractTreeAllVO> safeComparator = Comparator
+                    .comparing(WbsTreeContractTreeAllVO::getSort, Comparator.nullsFirst(Comparator.naturalOrder()))
+                    .thenComparing(new ComplexStringComparator<>(obj ->
+                            obj.getTitle() != null ? obj.getTitle() : ""))
+                    .thenComparing(
+                            WbsTreeContractTreeAllVO::getPrimaryKeyId,
+                            Comparator.nullsLast(Comparator.reverseOrder()));
+            Map<Long, List<WbsTreeContractTreeAllVO>> map = wbsTreeVO2s.stream().collect(Collectors.groupingBy(WbsTreeContractTreeAllVO::getParentId,
+                    Collectors.collectingAndThen(Collectors.toList(), items -> items.stream().sorted(safeComparator).collect(Collectors.toList()))));
             this.recursionFnTreeAll(list, map, split, 0);
             return list;
         }
@@ -1268,4 +1304,537 @@ public class ContractInfoServiceImpl extends BaseServiceImpl<ContractInfoMapper,
         return contractInfo;
     }
 
+    @Override
+    public R<Object> queryWbsTreeNodeBySaveOrHidden(String contractId, String tableOwner, Integer  days, String queryValue, Integer type) {
+        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isEmpty(contractId)) {
+            return R.fail(200, "合同段id错误");
+        }
+        ContractInfo contractInfo = this.selectById(contractId);
+        if (contractInfo == null) {
+            return R.fail(200, "合同段不存在");
+        }
+        String idsStr = "";
+        if (type != null && type == 1) {
+            List<Long> pKeyIds = jdbcTemplate.query("SELECT distinct p_key_id from blade_tab_sql WHERE user_id = ? and create_time > ?", new Object[]{AuthUtil.getUserId(), DateUtil.offsetDay(new Date(), -days)}, new SingleColumnRowMapper<>(Long.class));
+            if (pKeyIds.isEmpty()) {
+                return R.fail(200, "未查询到信息");
+            }
+            idsStr = pKeyIds.stream().map(id -> id + "").collect(Collectors.joining(","));
+        }
+        Set<String> syncPKeyIds = getSyncFlagIds(contractId, tableOwner);
+        Map<Long, String> collectNodesMap = getCollectNodesByContractId(contractId);
+        /*质检施工合同段*/
+        if (contractInfo.getContractType().equals(1)) {
+            LambdaQueryWrapper<WbsTreeContract> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(WbsTreeContract::getContractId, contractId).eq(WbsTreeContract::getType, 1);
+            if (type != null && type == 1) {
+                queryWrapper.last(" and p_key_id in ( select p_id from m_wbs_tree_contract where p_key_id in (" + idsStr + ") ) ");
+            } else {
+                queryWrapper.eq(WbsTreeContract::getIsBussShow, 2);
+            }
+            List<WbsTreeContract> nodes = wbsTreeContractMapper.selectList(queryWrapper);
+            if (!nodes.isEmpty()) {
+                List<WbsTreeContractLazyVO> nodesAllTemp;
+                Object data = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:" + contractId);
+                if (data != null) {
+                    nodesAllTemp = JSON.parseArray(data.toString(), WbsTreeContractLazyVO.class);
+                } else {
+                    nodesAllTemp = jdbcTemplate.query("select a.p_key_id,a.id,a.parent_id from m_wbs_tree_contract a where a.type = 1 and a.status = 1 and a.is_deleted = 0 and a.contract_id = " + contractId, new BeanPropertyRowMapper<>(WbsTreeContractLazyVO.class));
+                    if (!nodesAllTemp.isEmpty()) {
+                        Map<Long, List<WbsTreeContractLazyVO>> groupedByParentId = nodesAllTemp.stream().collect(Collectors.groupingBy(WbsTreeContractLazyVO::getParentId));
+                        for (WbsTreeContractLazyVO vo : nodesAllTemp) {
+                            if (vo.getParentId() == 0) {
+                                vo.setHasChildren(1);
+                            }
+                            List<WbsTreeContractLazyVO> childNodes = groupedByParentId.getOrDefault(vo.getId(), null);
+                            if (childNodes != null && !childNodes.isEmpty()) {
+                                vo.setHasChildren(1);
+                            } else {
+                                vo.setHasChildren(0);
+                            }
+                        }
+                        JSONArray array = JSONArray.parseArray(JSON.toJSONString(nodesAllTemp));
+                        redisTemplate.opsForValue().set("blade-manager::contract:wbstree:" + contractId, JSON.toJSON(array).toString());
+                    }
+                }
+                List<Long> parentIds = nodes.stream().map(WbsTreeContract::getParentId).collect(Collectors.toList());
+                List<Long> ids = nodes.stream().map(WbsTreeContract::getId).collect(Collectors.toList());
+                Set<Long> resultNodesPKeyIds = new LinkedHashSet<>();
+                this.recursiveGetParentNodes(resultNodesPKeyIds, parentIds, nodesAllTemp);
+                this.recursiveGetChildNodes(resultNodesPKeyIds, ids, nodesAllTemp);
+
+                resultNodesPKeyIds.addAll(nodes.stream().map(WbsTreeContract::getPKeyId).collect(Collectors.toList()));
+                List<WbsTreeContract> wbsTreeContractList = wbsTreeContractMapper.selectList(Wrappers.<WbsTreeContract>lambdaQuery().in(WbsTreeContract::getPKeyId, resultNodesPKeyIds));
+
+                if (!wbsTreeContractList.isEmpty()) {
+                    String pKeyIdStr = wbsTreeContractList.stream().map(item -> item.getPKeyId() + "").collect(Collectors.joining(","));
+                    List<WbsTreeContractStatistics> wbsTreeContractStatisticsList = jdbcTemplate.query("select id, leaf_num, fill_num, approve_num, complete_num,jl_fill_num, jl_approve_num, jl_complete_num from m_wbs_tree_contract_statistics where id in (" + pKeyIdStr + ")",
+                            new BeanPropertyRowMapper<>(WbsTreeContractStatistics.class));
+                    Map<Long, WbsTreeContractStatistics> wbsTreeContractStatisticsMap = wbsTreeContractStatisticsList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+                    List<WbsTreeContractTreeAllVO> wbsTreeContractTreeAllVOS = wbsTreeContractList.stream()
+                            .map(node -> {
+                                WbsTreeContractTreeAllVO vo = BeanUtil.copyProperties(node, WbsTreeContractTreeAllVO.class);
+                                if (vo != null) {
+                                    vo.setContractIdRelation(node.getContractId());
+                                    vo.setType(ObjectUtils.isNotEmpty(node.getNodeType()) ? node.getNodeType() : 0);
+                                    vo.setTitle(ObjectUtil.isNotEmpty(node.getFullName()) ? node.getFullName() : node.getNodeName());
+                                    vo.setPrimaryKeyId(node.getPKeyId());
+                                    WbsTreeContractStatistics statistics = wbsTreeContractStatisticsMap.get(node.getPKeyId());
+                                    if (statistics != null) {
+                                        Integer nums = statistics.calculateSubmitNums(tableOwner);
+                                        vo.setSubmitCounts(nums == null ? 0 : nums.longValue());
+                                        vo.setColorStatus(statistics.calculateColorStatus(tableOwner));
+                                    } else {
+                                        vo.setSubmitCounts(0L);
+                                        vo.setColorStatus(1);
+                                    }
+                                    if(CollectionUtil.isNotEmpty(syncPKeyIds)){
+                                        //判断是否展示同步标识
+                                        vo.setIsSync(syncPKeyIds.contains(vo.getPrimaryKeyId().toString()) ? 1 : 0);
+                                    }
+                                    if (collectNodesMap.containsKey(vo.getPrimaryKeyId())) {
+                                        vo.setIsCollect(1);
+                                    } else {
+                                        vo.setIsCollect(0);
+                                    }
+                                }
+                                return vo;
+                            }).collect(Collectors.toList());
+                    List<WbsTreeContractTreeAllVO> resultList = this.buildWbsTreeByStreamByTreeAll(wbsTreeContractTreeAllVOS);
+                    if (StringUtil.hasText(queryValue)) {
+                        return R.data(queryTreeResult(resultList, queryValue));
+                    }
+                    return R.data(resultList);
+                }
+            }
+            /*监理、业主合同段*/
+        } else if (contractInfo.getContractType().equals(2) || contractInfo.getContractType().equals(3)) {
+            Map<Long, List<WbsTreeContractTreeAllVO>> resultMaps = new LinkedHashMap<>();
+            List<ContractRelationJlyz> relationJLYZList = jdbcTemplate.query("select * from m_contract_relation_jlyz where contract_id_jlyz = " + contractId, new BeanPropertyRowMapper<>(ContractRelationJlyz.class));
+            if (ObjectUtil.isEmpty(relationJLYZList) || relationJLYZList.isEmpty()) {
+                return null;
+            }
+            for (ContractRelationJlyz contractRelationJlyz : relationJLYZList) {
+                LambdaQueryWrapper<WbsTreeContract> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(WbsTreeContract::getContractId, contractRelationJlyz.getContractIdSg()).eq(WbsTreeContract::getType, 1);
+                if (type != null && type == 1) {
+                    queryWrapper.last(" and p_key_id in ( select p_id from m_wbs_tree_contract where p_key_id in (" + idsStr + ") ) ");
+                } else {
+                    queryWrapper.eq(WbsTreeContract::getIsBussShow, 2);
+                }
+                List<WbsTreeContract> nodes = wbsTreeContractMapper.selectList(queryWrapper);
+                if (!nodes.isEmpty()) {
+                    List<WbsTreeContractLazyVO> nodesAllTemp;
+                    Object data = redisTemplate.opsForValue().get("blade-manager::contract:wbstree:" + contractId);
+                    if (data != null) {
+                        nodesAllTemp = JSON.parseArray(data.toString(), WbsTreeContractLazyVO.class);
+                    } else {
+                        nodesAllTemp = jdbcTemplate.query("select a.p_key_id,a.id,a.parent_id from m_wbs_tree_contract a where a.type = 1 and a.status = 1 and a.is_deleted = 0 and a.contract_id = " + contractId, new BeanPropertyRowMapper<>(WbsTreeContractLazyVO.class));
+                        if (!nodesAllTemp.isEmpty()) {
+                            Map<Long, List<WbsTreeContractLazyVO>> groupedByParentId = nodesAllTemp.stream().collect(Collectors.groupingBy(WbsTreeContractLazyVO::getParentId));
+                            for (WbsTreeContractLazyVO vo : nodesAllTemp) {
+                                if (vo.getParentId() == 0) {
+                                    vo.setHasChildren(1);
+                                }
+                                List<WbsTreeContractLazyVO> childNodes = groupedByParentId.getOrDefault(vo.getId(), null);
+                                if (childNodes != null && !childNodes.isEmpty()) {
+                                    vo.setHasChildren(1);
+                                } else {
+                                    vo.setHasChildren(0);
+                                }
+                            }
+                            JSONArray array = JSONArray.parseArray(JSON.toJSONString(nodesAllTemp));
+                            redisTemplate.opsForValue().set("blade-manager::contract:wbstree:" + contractId, JSON.toJSON(array).toString());
+                        }
+                    }
+                    List<Long> parentIds = nodes.stream().map(WbsTreeContract::getParentId).collect(Collectors.toList());
+                    List<Long> ids = nodes.stream().map(WbsTreeContract::getId).collect(Collectors.toList());
+                    Set<Long> resultNodesPKeyIds = new LinkedHashSet<>();
+                    this.recursiveGetParentNodes(resultNodesPKeyIds, parentIds, nodesAllTemp);
+                    this.recursiveGetChildNodes(resultNodesPKeyIds, ids, nodesAllTemp);
+
+                    resultNodesPKeyIds.addAll(nodes.stream().map(WbsTreeContract::getPKeyId).collect(Collectors.toList()));
+                    List<WbsTreeContract> wbsTreeContractList = wbsTreeContractMapper.selectList(Wrappers.<WbsTreeContract>lambdaQuery().in(WbsTreeContract::getPKeyId, resultNodesPKeyIds));
+                    if (!wbsTreeContractList.isEmpty()) {
+                        String pKeyIdStr = wbsTreeContractList.stream().map(item -> item.getPKeyId() + "").collect(Collectors.joining(","));
+                        List<WbsTreeContractStatistics> wbsTreeContractStatisticsList = jdbcTemplate.query("select id, leaf_num, fill_num, approve_num, complete_num,jl_fill_num, jl_approve_num, jl_complete_num from m_wbs_tree_contract_statistics where id in (" + pKeyIdStr + ")",
+                                new BeanPropertyRowMapper<>(WbsTreeContractStatistics.class));
+                        Map<Long, WbsTreeContractStatistics> wbsTreeContractStatisticsMap = wbsTreeContractStatisticsList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+                        List<WbsTreeContractTreeAllVO> wbsTreeContractTreeAllVOS = wbsTreeContractList.stream()
+                                .map(node -> {
+                                    WbsTreeContractTreeAllVO vo = BeanUtil.copyProperties(node, WbsTreeContractTreeAllVO.class);
+                                    if (vo != null) {
+                                        vo.setContractIdRelation(node.getContractId());
+                                        vo.setType(ObjectUtils.isNotEmpty(node.getNodeType()) ? node.getNodeType() : 0);
+                                        vo.setTitle(ObjectUtil.isNotEmpty(node.getFullName()) ? node.getFullName() : node.getNodeName());
+                                        vo.setPrimaryKeyId(node.getPKeyId());
+                                        WbsTreeContractStatistics statistics = wbsTreeContractStatisticsMap.get(node.getPKeyId());
+                                        if (statistics != null) {
+                                            Integer nums = statistics.calculateSubmitNums(tableOwner);
+                                            vo.setSubmitCounts(nums == null ? 0 : nums.longValue());
+                                            vo.setColorStatus(statistics.calculateColorStatus(tableOwner));
+                                        } else {
+                                            vo.setSubmitCounts(0L);
+                                            vo.setColorStatus(1);
+                                        }
+                                        if(CollectionUtil.isNotEmpty(syncPKeyIds)){
+                                            //判断是否展示同步标识
+                                            vo.setIsSync(syncPKeyIds.contains(vo.getPrimaryKeyId().toString()) ? 1 : 0);
+                                        }
+                                        if (collectNodesMap.containsKey(vo.getPrimaryKeyId())) {
+                                            vo.setIsCollect(1);
+                                        } else {
+                                            vo.setIsCollect(0);
+                                        }
+                                    }
+                                    return vo;
+                                }).collect(Collectors.toList());
+                        List<WbsTreeContractTreeAllVO> list = this.buildWbsTreeByStreamByTreeAll(wbsTreeContractTreeAllVOS);
+                        if (StringUtil.hasText(queryValue)) {
+                            resultMaps.put(contractRelationJlyz.getContractIdSg(), this.queryTreeResult(list, queryValue));
+                        } else {
+                            resultMaps.put(contractRelationJlyz.getContractIdSg(), list);
+                        }
+                    }
+                }
+            }
+            return R.data(resultMaps);
+        }
+        return null;
+    }
+
+    private Map<Long, String> getCollectNodesByContractId(String contractId){
+        if (contractId ==  null || contractId.trim().isEmpty()) {
+            return new HashMap<>();
+        }
+        Map<Long, String> collectNodes = new HashMap<>();
+        try {
+            String string = redisTemplate.opsForValue().get("blade-manager::contract:collectFolder:" + contractId);
+            if (string != null) {
+                String[] split = string.split(",");
+                for (String s : split) {
+                    if (StringUtil.isNumeric(s)) {
+                        collectNodes.put(Long.parseLong(s), "");
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("",e);
+        }
+        try {
+            if (!collectNodes.isEmpty()) {
+                return collectNodes;
+            }
+            List<ContractCollectFolder> query = jdbcTemplate.query(String.format("select node_id from m_contract_collect_folder where contract_id = %s and type = 1", contractId), new BeanPropertyRowMapper<>(ContractCollectFolder.class));
+            for (ContractCollectFolder contractCollectFolder : query) {
+                Long nodeId = contractCollectFolder.getNodeId();
+                collectNodes.put(nodeId, "");
+//                String nodeAncestors = contractCollectFolder.getNodeAncestors();
+//                if (nodeAncestors != null) {
+//                    String[] split = nodeAncestors.split(",");
+//                    for (String s : split) {
+//                        if (StringUtil.isNumeric(s)) {
+//                            collectNodes.put(Long.parseLong(s), "");
+//                        }
+//                    }
+//                }
+                String ids = collectNodes.keySet().stream().map(item -> item + "").collect(Collectors.joining(","));
+                redisTemplate.opsForValue().set("blade-manager::contract:collectFolder:" + contractId, ids, 60 * 60 * 40L, TimeUnit.SECONDS);
+            }
+        } catch (Exception e) {
+            log.error("合同段id:" + contractId + ", 获取收藏节点失败:" + e.getMessage(), e);
+        }
+        return collectNodes;
+    }
+
+    private List<WbsTreeContractTreeAllVO> queryTreeResult(List<WbsTreeContractTreeAllVO> resultList, String query) {
+        // 查找树结构中name包含query的节点,返回其父节点及所有子节点
+        List<WbsTreeContractTreeAllVO> result = new ArrayList<>();
+        for (WbsTreeContractTreeAllVO node : resultList) {
+            if (node.getTitle().contains(query)) {
+                result.add(node);
+                continue;
+            }
+            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
+                List<WbsTreeContractTreeAllVO> list = queryTreeResult(node.getChildren(), query);
+                if (list.isEmpty()) {
+                    node.setChildren(new ArrayList<>());
+                } else {
+                    node.setChildren( list);
+                }
+            }
+            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
+                result.add(node);
+            }
+        }
+        return result;
+    }
+    @NotNull
+    private Set<String> getSyncFlagIds(String contractId, String tableOwner) {
+        //查询wbs同步信息
+        String contractExtendSql = "select a.ancestors from m_wbs_tree_contract_extend a " +
+                "inner join m_wbs_tree_contract b on a.p_key_id = b.p_key_id and b.is_deleted = 0 " +
+                "where a.type = " + tableOwner + " and a.is_sync = 1 and a.is_deleted = 0 and a.contract_id = " + contractId;
+        Set<String> syncPKeyIds = new HashSet<>();
+        if(SecureUtil.isAdministrator()){
+            List<String> strings = null;
+            try {
+                strings = jdbcTemplate.queryForList(contractExtendSql, String.class);
+            } catch (DataAccessException e) {
+                //TODO 暂时忽略错误
+                e.printStackTrace();
+            }
+            if(strings != null && !strings.isEmpty()){
+                strings.stream().filter(StringUtils::isNotEmpty).forEach(f -> syncPKeyIds.addAll(Arrays.asList(f.split(","))));
+            }
+        }
+        return syncPKeyIds;
+    }
+    @Override
+    public R<Object> getCollectTreeNode(String contractId, String tableOwner, Long folderId, String queryValue, Long pKeyId) {
+        if (StringUtils.isEmpty(contractId)) {
+            return R.fail(200, "合同段id错误");
+        }
+        if (folderId == null || folderId <= 0) {
+            return R.fail(200, "收藏夹不存在");
+        }
+        ContractInfo contractInfo = this.selectById(contractId);
+        if (contractInfo == null) {
+            return R.fail(200, "合同段不存在");
+        }
+        Set<String> syncPKeyIds = getSyncFlagIds(contractId, tableOwner);
+        List<ContractCollectFolder> contractCollectNodes = jdbcTemplate.query("select node_id, ifnull((select ancestors_p_id from m_wbs_tree_contract where p_key_id = a.node_id), (select ancestors from m_wbs_tree_contract_statistics where id = a.node_id)) as nodeAncestors " +
+                "from m_contract_collect_folder a where type = 1 and parent_id = " + folderId + " and (select 1 from m_wbs_tree_contract where p_key_id = a.node_id and is_buss_show is not null and is_buss_show = 2) is null", new BeanPropertyRowMapper<>(ContractCollectFolder.class));
+        if (!contractCollectNodes.isEmpty()) {
+            Set<Long> pKeyIds = new LinkedHashSet<>();
+            Map<Long, String> collectMap = new HashMap<>();
+            for (ContractCollectFolder node : contractCollectNodes) {
+                String ancestors = node.getNodeAncestors();
+                if (ancestors != null) {
+                    String[] split = ancestors.split(",");
+                    for (int i = 0; i < split.length; i++) {
+                        String s = split[i];
+                        if (StringUtil.isNumeric(s)) {
+                            pKeyIds.add(Long.parseLong(s));
+                        }
+                    }
+                }
+                collectMap.put(node.getNodeId(), "");
+                pKeyIds.add(node.getNodeId());
+            }
+            List<WbsTreeContract> wbsTreeContractList = new ArrayList<>();
+            if (!pKeyIds.isEmpty()) {
+                LambdaQueryWrapper<WbsTreeContract> wrapper = Wrappers.<WbsTreeContract>lambdaQuery().in(WbsTreeContract::getPKeyId, pKeyIds);
+                wrapper.and(wrapper1 -> wrapper1.isNull(WbsTreeContract::getIsBussShow).or().ne(WbsTreeContract::getIsBussShow, 2));
+                wbsTreeContractList = wbsTreeContractMapper.selectList(wrapper);
+            }
+            if (!wbsTreeContractList.isEmpty()) {
+                String pKeyIdStr = wbsTreeContractList.stream().map(item -> item.getPKeyId() + "").collect(Collectors.joining(","));
+                List<WbsTreeContractStatistics> wbsTreeContractStatisticsList = jdbcTemplate.query("select id, parent_id, leaf_num, fill_num, approve_num, complete_num,jl_fill_num, jl_approve_num, jl_complete_num from m_wbs_tree_contract_statistics where id in (" + pKeyIdStr + ")",
+                        new BeanPropertyRowMapper<>(WbsTreeContractStatistics.class));
+                Map<Long, WbsTreeContractStatistics> wbsTreeContractStatisticsMap = wbsTreeContractStatisticsList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+                Map<String, List<WbsTreeContract>> contractGroupMap = wbsTreeContractList.stream().collect(Collectors.groupingBy(WbsTreeContract::getContractId));
+                Map<Long, List<WbsTreeContractTreeAllVO>> resultMaps = new LinkedHashMap<>();
+                contractGroupMap.forEach((cId, list) -> {
+                    List<WbsTreeContractTreeAllVO> wbsTreeContractTreeAllVOS = list.stream()
+                            .map(node -> {
+                                WbsTreeContractTreeAllVO vo = BeanUtil.copyProperties(node, WbsTreeContractTreeAllVO.class);
+                                if (vo != null) {
+                                    vo.setContractIdRelation(node.getContractId());
+                                    vo.setType(ObjectUtils.isNotEmpty(node.getNodeType()) ? node.getNodeType() : 0);
+                                    vo.setTitle(ObjectUtil.isNotEmpty(node.getFullName()) ? node.getFullName() : node.getNodeName());
+                                    vo.setPrimaryKeyId(node.getPKeyId());
+                                    vo.setParentId(node.getPId());
+                                    WbsTreeContractStatistics statistics = wbsTreeContractStatisticsMap.get(node.getPKeyId());
+                                    if (statistics != null) {
+                                        Integer nums = statistics.calculateSubmitNums(tableOwner);
+                                        vo.setSubmitCounts(nums == null ? 0 : nums.longValue());
+                                        vo.setColorStatus(statistics.calculateColorStatus(tableOwner));
+                                        if (statistics.getLeafNum() > 0) {
+                                            vo.setHasChildren( true);
+                                        } else {
+                                            vo.setHasChildren(false);
+                                        }
+                                        if (vo.getParentId() == null) {
+                                            vo.setParentId(statistics.getParentId());
+                                        }
+                                    } else {
+                                        vo.setSubmitCounts(0L);
+                                        vo.setColorStatus(1);
+                                        vo.setHasChildren(false);
+                                    }
+                                    vo.setNotExsitChild(!vo.getHasChildren());
+                                    if(CollectionUtil.isNotEmpty(syncPKeyIds)){
+                                        //判断是否展示同步标识
+                                        vo.setIsSync(syncPKeyIds.contains(vo.getPrimaryKeyId().toString()) ? 1 : 0);
+                                    }
+                                    if (collectMap.containsKey(node.getPKeyId())) {
+                                        vo.setIsCollect(1);
+                                    } else {
+                                        vo.setIsCollect(0);
+                                    }
+                                }
+                                return vo;
+                            }).collect(Collectors.toList());
+                    List<WbsTreeContractTreeAllVO> resultList = this.buildWbsTreeByStreamByTreeAll1(wbsTreeContractTreeAllVOS, pKeyId);
+                    resultMaps.put(Long.parseLong(cId), resultList);
+                });
+                if (contractInfo.getContractType() != null && contractInfo.getContractType() == 1) {
+                    return R.data(resultMaps.get(contractInfo.getId()));
+                }
+                return R.data(resultMaps);
+            }
+        }
+        return null;
+    }
+    public List<WbsTreeContractTreeAllVO> buildWbsTreeByStreamByTreeAll1(List<WbsTreeContractTreeAllVO> wbsTreeVO2s, Long pKeyId) {
+        List<WbsTreeContractTreeAllVO> list = wbsTreeVO2s.stream().filter(f -> f.getParentId() == 0L).collect(Collectors.toList());
+        // 修改比较器链,处理空值情况
+        Comparator<WbsTreeContractTreeAllVO> safeComparator = Comparator
+                .comparing(WbsTreeContractTreeAllVO::getSort, Comparator.nullsFirst(Comparator.naturalOrder()))
+                .thenComparing(new ComplexStringComparator<>(obj ->
+                        obj.getTitle() != null ? obj.getTitle() : ""))
+                .thenComparing(
+                        WbsTreeContractTreeAllVO::getId,
+                        Comparator.nullsLast(Comparator.reverseOrder()));
+        list.sort(safeComparator);
+        Map<Long, List<WbsTreeContractTreeAllVO>> map = wbsTreeVO2s.stream().collect(Collectors.groupingBy(WbsTreeContractTreeAllVO::getParentId,
+                Collectors.collectingAndThen(Collectors.toList(), items -> items.stream().sorted(safeComparator).collect(Collectors.toList()))));
+        List<WbsTreeContractTreeAllVO> list1 = map.get(pKeyId);
+        if (list1 == null || list1.isEmpty()) {
+            return list1;
+        }
+        List<WbsTreeContractTreeAllVO> resultList = new ArrayList<>();
+        for (WbsTreeContractTreeAllVO vo : list1) {
+            List<WbsTreeContractTreeAllVO> list2 = map.get(vo.getPrimaryKeyId());
+            if ((list2 == null || list2.isEmpty())) {
+                if (vo.getIsCollect() != null && vo.getIsCollect() == 1) {
+                    resultList.add(vo);
+                }
+                continue;
+            }
+            long count = list2.stream().filter(f -> f.getIsCollect() != null && f.getIsCollect() == 1).count();
+            if (count > 0) {
+                resultList.add(vo);
+            } else {
+                if (isShow(list2, map)) {
+                    resultList.add(vo);
+                }
+            }
+        }
+        return resultList;
+    }
+
+    public boolean isShow(List<WbsTreeContractTreeAllVO> list, Map<Long, List<WbsTreeContractTreeAllVO>> map){
+        for (WbsTreeContractTreeAllVO vo : list) {
+            List<WbsTreeContractTreeAllVO> list2 = map.get(vo.getPrimaryKeyId());
+            if ((list2 == null || list2.isEmpty())) {
+                if (vo.getIsCollect() != null && vo.getIsCollect() == 1) {
+                    return true;
+                }
+                continue;
+            }
+            long count = list2.stream().filter(f -> f.getIsCollect() != null && f.getIsCollect() == 1).count();
+            if (count > 0) {
+                return true;
+            } else {
+                if (isShow(list2, map)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public R<Object> getCollectTreeNodeByQuery(String contractId, String tableOwner, Long folderId, String queryValue) {
+        if (StringUtils.isEmpty(contractId)) {
+            return R.fail(200, "合同段id错误");
+        }
+        if (folderId == null || folderId <= 0) {
+            return R.fail(200, "收藏夹不存在");
+        }
+        ContractInfo contractInfo = this.selectById(contractId);
+        if (contractInfo == null) {
+            return R.fail(200, "合同段不存在");
+        }
+        Set<String> syncPKeyIds = getSyncFlagIds(contractId, tableOwner);
+        List<ContractCollectFolder> contractCollectNodes = jdbcTemplate.query("select node_id, ifnull((select ancestors_p_id from m_wbs_tree_contract where p_key_id = a.node_id), (select ancestors from m_wbs_tree_contract_statistics where id = a.node_id)) as nodeAncestors from m_contract_collect_folder a where type = 1 and parent_id = " + folderId,
+                new BeanPropertyRowMapper<>(ContractCollectFolder.class));
+        if (!contractCollectNodes.isEmpty()) {
+            Set<Long> pKeyIds = new LinkedHashSet<>();
+            Map<Long, String> collectMap = new HashMap<>();
+            for (ContractCollectFolder node : contractCollectNodes) {
+                String ancestors = node.getNodeAncestors();
+                if (ancestors != null) {
+                    String[] split = ancestors.split(",");
+                    for (String s : split) {
+                        if (StringUtil.isNumeric(s)) {
+                            pKeyIds.add(Long.parseLong(s));
+                        }
+                    }
+                }
+                collectMap.put(node.getNodeId(), "");
+                pKeyIds.add(node.getNodeId());
+            }
+            List<WbsTreeContract> wbsTreeContractList = wbsTreeContractMapper.selectList(Wrappers.<WbsTreeContract>lambdaQuery().in(WbsTreeContract::getPKeyId, pKeyIds).
+                    and(wrapper1 -> wrapper1.isNull(WbsTreeContract::getIsBussShow).or().ne(WbsTreeContract::getIsBussShow, 2)));
+            if (!wbsTreeContractList.isEmpty()) {
+                String pKeyIdStr = wbsTreeContractList.stream().map(item -> item.getPKeyId() + "").collect(Collectors.joining(","));
+                List<WbsTreeContractStatistics> wbsTreeContractStatisticsList = jdbcTemplate.query("select id, leaf_num, fill_num, approve_num, complete_num,jl_fill_num, jl_approve_num, jl_complete_num from m_wbs_tree_contract_statistics where id in (" + pKeyIdStr + ")",
+                        new BeanPropertyRowMapper<>(WbsTreeContractStatistics.class));
+                Map<Long, WbsTreeContractStatistics> wbsTreeContractStatisticsMap = wbsTreeContractStatisticsList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+                Map<String, List<WbsTreeContract>> contractGroupMap = wbsTreeContractList.stream().collect(Collectors.groupingBy(WbsTreeContract::getContractId));
+                Map<Long, List<WbsTreeContractTreeAllVO>> resultMaps = new LinkedHashMap<>();
+                contractGroupMap.forEach((cId, list) -> {
+                    List<WbsTreeContractTreeAllVO> wbsTreeContractTreeAllVOS = list.stream()
+                            .map(node -> {
+                                WbsTreeContractTreeAllVO vo = BeanUtil.copyProperties(node, WbsTreeContractTreeAllVO.class);
+                                if (vo != null) {
+                                    vo.setContractIdRelation(node.getContractId());
+                                    vo.setType(ObjectUtils.isNotEmpty(node.getNodeType()) ? node.getNodeType() : 0);
+                                    vo.setTitle(ObjectUtil.isNotEmpty(node.getFullName()) ? node.getFullName() : node.getNodeName());
+                                    vo.setPrimaryKeyId(node.getPKeyId());
+                                    WbsTreeContractStatistics statistics = wbsTreeContractStatisticsMap.get(node.getPKeyId());
+                                    if (statistics != null) {
+                                        Integer nums = statistics.calculateSubmitNums(tableOwner);
+                                        vo.setSubmitCounts(nums == null ? 0 : nums.longValue());
+                                        vo.setColorStatus(statistics.calculateColorStatus(tableOwner));
+                                        if (statistics.getLeafNum() > 0) {
+                                            vo.setHasChildren( true);
+                                        } else {
+                                            vo.setHasChildren(false);
+                                        }
+                                    } else {
+                                        vo.setSubmitCounts(0L);
+                                        vo.setColorStatus(1);
+                                        vo.setHasChildren(false);
+                                    }
+                                    if(CollectionUtil.isNotEmpty(syncPKeyIds)){
+                                        //判断是否展示同步标识
+                                        vo.setIsSync(syncPKeyIds.contains(vo.getPrimaryKeyId().toString()) ? 1 : 0);
+                                    }
+                                    if (collectMap.containsKey(node.getPKeyId())) {
+                                        vo.setIsCollect(1);
+                                    } else {
+                                        vo.setIsCollect(0);
+                                    }
+                                }
+                                return vo;
+                            }).collect(Collectors.toList());
+                    List<WbsTreeContractTreeAllVO> resultList = this.buildWbsTreeByStreamByTreeAll(wbsTreeContractTreeAllVOS);
+                    if (StringUtil.hasText(queryValue)) {
+                        resultMaps.put(Long.parseLong(cId), this.queryTreeResult(resultList, queryValue));
+                    } else {
+                        resultMaps.put(Long.parseLong(cId), resultList);
+                    }
+                });
+                if (contractInfo.getContractType() != null && contractInfo.getContractType() == 1) {
+                    return R.data(resultMaps.get(contractInfo.getId()));
+                }
+                return R.data(resultMaps);
+            }
+        }
+        return null;
+    }
 }

+ 15 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -82,6 +82,7 @@ import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -890,6 +891,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                         List<WbsTreeContractStatistics> wbsTreeContractStatisticsList = jdbcTemplate.query("select id, leaf_num, fill_num, approve_num, complete_num,jl_fill_num, jl_approve_num, jl_complete_num from m_wbs_tree_contract_statistics where id in (" + pKeyIds + ")",
                                 new BeanPropertyRowMapper<>(WbsTreeContractStatistics.class));
                         Map<Long, WbsTreeContractStatistics> wbsTreeContractStatisticsMap = wbsTreeContractStatisticsList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+                        List<ContractCollectFolder> collectList = jdbcTemplate.query("select node_id from m_contract_collect_folder where type = 1 and contract_id = " + contractId + " and node_id in ( " + pKeyIds + ")", new BeanPropertyRowMapper<>(ContractCollectFolder.class));
+                        Map<Long, ContractCollectFolder> collectNodesMap = collectList.stream().collect(Collectors.toMap(ContractCollectFolder::getNodeId, item -> item, (existing, replacement) -> existing));
                         setPrivateTemplate(lazyNodes);
                         for (WbsTreeContractLazyVO lazyNodeVO : lazyNodes) {
                                 if(lazyNodeVO.getIsCustom()!=null&&lazyNodeVO.getIsCustom()==1){
@@ -910,6 +913,11 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                                     lazyNodeVO.setSubmitCounts(0);
                                     lazyNodeVO.setColorStatus(1);
                                 }
+                                if (collectNodesMap.containsKey(lazyNodeVO.getPrimaryKeyId())) {
+                                    lazyNodeVO.setIsCollect(1);
+                                } else {
+                                    lazyNodeVO.setIsCollect(0);
+                                }
 //                                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)) {
@@ -1027,6 +1035,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                                 List<WbsTreeContractStatistics> wbsTreeContractStatisticsList = jdbcTemplate.query("select id, leaf_num, fill_num, approve_num, complete_num,jl_fill_num, jl_approve_num, jl_complete_num  from m_wbs_tree_contract_statistics where id in (" + pKeyIds + ")",
                                         new BeanPropertyRowMapper<>(WbsTreeContractStatistics.class));
                                 Map<Long, WbsTreeContractStatistics> wbsTreeContractStatisticsMap = wbsTreeContractStatisticsList.stream().collect(Collectors.toMap(WbsTreeContractStatistics::getId, item -> item));
+                                List<ContractCollectFolder> collectList = jdbcTemplate.query("select node_id from m_contract_collect_folder where type = 1 and contract_id = " + contractId + " and node_id in ( " + pKeyIds + ")", new BeanPropertyRowMapper<>(ContractCollectFolder.class));
+                                Map<Long, ContractCollectFolder> collectNodesMap = collectList.stream().collect(Collectors.toMap(ContractCollectFolder::getNodeId, item -> item, (existing, replacement) -> existing));
                                 setPrivateTemplate(lazyNodes);
                                     for (WbsTreeContractLazyVO lazyNodeVO : lazyNodes) {
                                         if(lazyNodeVO.getIsCustom()!=null&&lazyNodeVO.getIsCustom()==1){
@@ -1049,6 +1059,11 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
                                             lazyNodeVO.setSubmitCounts(0);
                                             lazyNodeVO.setColorStatus(1);
                                         }
+                                        if (collectNodesMap.containsKey(lazyNodeVO.getPrimaryKeyId())) {
+                                            lazyNodeVO.setIsCollect(1);
+                                        } else {
+                                            lazyNodeVO.setIsCollect(0);
+                                        }
 //                                        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);

+ 7 - 3
blade-service/blade-user/src/main/java/org/springblade/system/user/util/ComplexStringComparator.java

@@ -31,8 +31,8 @@ public class ComplexStringComparator<T> implements Comparator<T> {
             Object p1 = parts1.get(i);
             Object p2 = parts2.get(i);
 
-            if (p1 instanceof Integer && p2 instanceof Integer) {
-                int cmp = ((Integer) p1).compareTo((Integer) p2);
+            if (p1 instanceof Long && p2 instanceof Long) {
+                int cmp = ((Long) p1).compareTo((Long) p2);
                 if (cmp != 0) return cmp;
             } else {
                 int cmp = p1.toString().compareTo(p2.toString());
@@ -68,7 +68,11 @@ public class ComplexStringComparator<T> implements Comparator<T> {
 
         if (buffer.length() > 0) {
             if (numFlag == 1) {
-                parts.add(Integer.parseInt(buffer.toString()));
+                try {
+                    parts.add(Long.parseLong(buffer.toString()));
+                } catch (NumberFormatException e) {
+                    parts.add(buffer.toString());
+                }
             } else {
                 parts.add(buffer.toString());
             }