Browse Source

回收站功能优化

lvy 1 week ago
parent
commit
21a5959bf1

+ 35 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/RecycleBinInfoDTO.java

@@ -0,0 +1,35 @@
+/*
+ *      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.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+@Data
+public class RecycleBinInfoDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("ids")
+    private String ids;
+
+    @ApiModelProperty("是否恢复同批次全部删除数据, 0否,1是")
+    private Integer recoverOperationData;
+
+}

+ 126 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/RecycleBinInfo.java

@@ -0,0 +1,126 @@
+/*
+ *      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.business.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.DateUtil;
+
+import java.util.Date;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ * @since 2022-07-19
+ */
+@Data
+@TableName("u_recycle_bin_info")
+@EqualsAndHashCode(callSuper = true)
+public class RecycleBinInfo extends BaseEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 项目ID
+     */
+    @ApiModelProperty("项目ID")
+    private Long projectId;
+    /**
+     * 合同段ID
+     */
+    @ApiModelProperty("合同段ID")
+    private Long contractId;
+
+    @ApiModelProperty("删除的id")
+    private Long delId;
+
+    @ApiModelProperty("删除位置id")
+    private Long delRootId;
+
+    @ApiModelProperty("删除位置名称")
+    private String delRootName;
+
+    @ApiModelProperty("操作记录id")
+    private String operationId;
+
+    /**
+     * 删除的类型,文件资料1,工程划分2,影像资料3
+     */
+    @ApiModelProperty("删除的类型,文件资料1,工程划分2,影像资料3")
+    private Integer delType;
+
+    /**
+     * 是否是资料节点,0否,1是
+     */
+    @ApiModelProperty("是否是资料节点,0否,1是")
+    private Integer isData;
+
+    /**
+     * 文件名称
+     */
+    @ApiModelProperty("文件名称(施工)")
+    private String fileName;
+
+    @ApiModelProperty("文件名称(监理)")
+    private String jlFileName;
+
+    /**
+     * 所在位置,即节点路径
+     */
+    @ApiModelProperty("所在位置,即节点路径")
+    private String position;
+    /**
+     * 业务ID
+     */
+    @ApiModelProperty("业务ID")
+    private String businessId;
+    /**
+     * 操作时间,与创建时间相同
+     */
+    @ApiModelProperty("操作时间,与创建时间相同")
+    private String operationTime;
+
+    @ApiModelProperty("操作人姓名")
+    private String createUserName;
+
+    @ApiModelProperty("更新者姓名")
+    private String updateUserName;
+
+    public RecycleBinInfo(String businessIds, String title, Integer deletedType, String position, String projectId, String contractId) {
+        this.businessId = businessIds;
+        this.fileName = title;
+        this.delType = deletedType;
+        this.position = position;
+        this.projectId = Long.parseLong(projectId);
+        this.contractId = Long.parseLong(contractId);
+
+        this.setCreateUser(AuthUtil.getUserId());
+        this.createUserName = AuthUtil.getNickName();
+        Date now = new Date();
+        this.setCreateTime(now);
+        this.operationTime = DateUtil.format(now, "yyyy-MM-dd HH:mm:ss");
+    }
+
+    public RecycleBinInfo() {
+    }
+
+}

+ 74 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/RecycleBinInfoVO.java

@@ -0,0 +1,74 @@
+package org.springblade.business.vo;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.support.Query;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Date;
+
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RecycleBinInfoVO extends Query implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 开始时间
+     */
+    @ApiModelProperty("开始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+    /**
+     * 结束时间
+     */
+    @ApiModelProperty("结束时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+
+    /**
+     * 用户ID
+     */
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    /**
+     * 合同段ID
+     */
+    @ApiModelProperty("合同段ID")
+    @NotNull
+    private Long contractId;
+
+    /**
+     * 是否是资料节点,0否,1是
+     */
+    @ApiModelProperty("是否是资料节点,0否,1是")
+    private Integer isData;
+
+    /**
+     * 0:删除台账, 1:恢复台账
+     */
+    @ApiModelProperty("0:删除台账, 1:恢复台账")
+    private Integer recycleType = 0;
+
+    /**
+     * 删除的类型,文件资料1,工程划分2,影像资料3
+     */
+    @ApiModelProperty("删除的类型,文件资料1,工程划分2,影像资料3")
+    private Integer delType = 2;
+
+    /**
+     * 内容
+     */
+    @ApiModelProperty("内容")
+    private String content;
+
+    @ApiModelProperty("操作记录id")
+    private String operationId;
+
+
+}

+ 17 - 17
blade-service/blade-business/src/main/java/org/springblade/business/controller/InformationWriteQueryController.java

@@ -157,6 +157,7 @@ public class InformationWriteQueryController extends BladeController {
 
     private final IRecycleBinService recycleBinService;
     private final WbsTreeContractStatisticsClientImpl wbsTreeContractStatisticsClient;
+    private final IRecycleBinInfoService recycleBinInfoService;
 
 
     @Autowired
@@ -3650,23 +3651,21 @@ public R removeContractTreeNodeJudge(@RequestParam String ids) {
                 String wbsIds = removeList.stream().map(Object::toString).collect(Collectors.joining(","));
                 /** 判断是否子节点有上报或审批过的资料,因为父id和祖级节点错误,直接使用上面的值去查询上报情况*/
                 List<String> unRemoveIds = jdbcTemplate.queryForList("SELECT wbs_id from u_information_query WHERE wbs_id in (" + wbsIds + ") and is_deleted = 0 and status in (1,2) GROUP BY wbs_id HAVING count(1) > 0", String.class);
-                if (!unRemoveIds.isEmpty()) {
-                    // 剔除此节点
-                    Map<String, String> map = unRemoveIds.stream().collect(Collectors.toMap(id -> id, Function.identity()));
-                    removeNodeList.removeIf(node -> {
-                        //删除掉表格 TODO(不清楚为什么要剔除表格,按理说删除节点后,节点下的表也应该一起删除的,猜测或许是为了方便恢复节点的时候表数据还存在)2023年9月19日
-                        boolean removeIf = node.getType() != null && new Integer("2").equals(node.getType());
-                        boolean equals = map.containsKey(node.getPKeyId().toString());
-                        if (equals) {
-                            unremoveNodeMap.put(node.getPKeyId(), node);
-                        }
-                        return removeIf || equals;
-                    });
-                    Map<Long, WbsTreeContract> contractMap = removeNodeList.stream().collect(Collectors.toMap(WbsTreeContract::getId, Function.identity()));
-                    Map<Long, WbsTreeContract> tempMap = new HashMap<>();
-                    unremoveNodeMap.forEach((key, value) -> collectNodeAndAncestors(value, contractMap, tempMap));
-                    unremoveNodeMap.putAll(tempMap);
-                }
+                // 剔除此节点
+                Map<String, String> map = unRemoveIds.stream().collect(Collectors.toMap(id -> id, Function.identity()));
+                removeNodeList.removeIf(node -> {
+                    //删除掉表格 TODO(不清楚为什么要剔除表格,按理说删除节点后,节点下的表也应该一起删除的,猜测或许是为了方便恢复节点的时候表数据还存在)2023年9月19日
+                    boolean removeIf = node.getType() != null && new Integer("2").equals(node.getType());
+                    boolean equals = map.containsKey(node.getPKeyId().toString());
+                    if (equals) {
+                        unremoveNodeMap.put(node.getPKeyId(), node);
+                    }
+                    return removeIf || equals;
+                });
+                Map<Long, WbsTreeContract> contractMap = removeNodeList.stream().collect(Collectors.toMap(WbsTreeContract::getId, Function.identity()));
+                Map<Long, WbsTreeContract> tempMap = new HashMap<>();
+                unremoveNodeMap.forEach((key, value) -> collectNodeAndAncestors(value, contractMap, tempMap));
+                unremoveNodeMap.putAll(tempMap);
             }
             if(removeNodeList.isEmpty()) {
                 return R.fail("删除失败,该节点下有已填报的资料");
@@ -3691,6 +3690,7 @@ public R removeContractTreeNodeJudge(@RequestParam String ids) {
 
             //保存进回收站
             this.recycleBinService.save(new RecycleBin(String.join(",", idArray), "工程划分批量删除", 2, positionStr, projectId, contractId));
+            this.recycleBinInfoService.saveRecycleBinInfoByWbsTreeContract(String.join(",", idArray),ids);
             //改为物理删除 (8.28改为逻辑删除,方便恢复)
             this.wbsTreeContractClient.removeContractTreeNode(idArray);
             //更新redis

+ 83 - 0
blade-service/blade-business/src/main/java/org/springblade/business/controller/RecycleBinController.java

@@ -10,25 +10,34 @@ import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import lombok.AllArgsConstructor;
 
 import org.apache.commons.lang.StringUtils;
+import org.springblade.business.dto.RecycleBinInfoDTO;
 import org.springblade.business.entity.ArchiveFile;
 import org.springblade.business.entity.ImageClassificationFile;
+import org.springblade.business.entity.RecycleBinInfo;
 import org.springblade.business.feign.InformationQueryClient;
 import org.springblade.business.feignClient.WbsTreeContractStatisticsClientImpl;
 import org.springblade.business.service.IArchiveFileService;
 import org.springblade.business.service.IImageClassificationFileService;
+import org.springblade.business.service.IRecycleBinInfoService;
+import org.springblade.business.vo.RecycleBinInfoVO;
 import org.springblade.business.vo.RecycleBinVO;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
+import org.springblade.core.redis.cache.BladeRedis;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.manager.feign.WbsTreeContractClient;
+import org.springblade.system.user.vo.UserVO2;
 import org.springframework.web.bind.annotation.*;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springblade.business.entity.RecycleBin;
 import org.springblade.business.service.IRecycleBinService;
 import org.springblade.core.boot.ctrl.BladeController;
 
+import javax.validation.Valid;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -55,6 +64,8 @@ public class RecycleBinController extends BladeController {
     private final InformationQueryClient informationQueryClient;
 
     private final WbsTreeContractStatisticsClientImpl wbsTreeContractStatisticsClient;
+    private final IRecycleBinInfoService recycleBinInfoService;
+    private final BladeRedis bladeRedis;
 
     /**
      * 恢复
@@ -168,4 +179,76 @@ public class RecycleBinController extends BladeController {
         return R.data(this.recycleBinService.page(Condition.getPage(query), wrapper));
     }
 
+    @GetMapping("/page")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "分页")
+    public R<IPage<RecycleBinInfo>> page(@Valid RecycleBinInfoVO vo) {
+        return R.data(this.recycleBinInfoService.selectPage(vo));
+    }
+
+    /**
+     * 恢复
+     */
+    @PostMapping("/recover")
+    @ApiOperationSupport(order = 2)
+    @ApiOperation(value = "恢复(工程划分)")
+    public R<Boolean> recover(@RequestBody RecycleBinInfoDTO dto) {
+        if (StringUtil.hasText(dto.getIds())) {
+            //获取数据
+            String[] ids = dto.getIds().split(",");
+            List<Long> collect = Arrays.stream(ids).filter(StringUtils::isNumeric).map(Long::parseLong).collect(Collectors.toList());
+            if (collect.isEmpty()) {
+                return R.data(true);
+            }
+            List<RecycleBinInfo> recycleBinInfoList = this.recycleBinInfoService.list(Wrappers.<RecycleBinInfo>lambdaQuery().in(RecycleBinInfo::getId, collect).eq(RecycleBinInfo::getStatus, 0).eq(RecycleBinInfo::getDelType, 2));
+            if (collect.size() == 1 && dto.getRecoverOperationData() == 1) {
+                List<RecycleBinInfo> list = this.recycleBinInfoService.list(Wrappers.<RecycleBinInfo>lambdaQuery().eq(RecycleBinInfo::getOperationId, recycleBinInfoList.get(0).getOperationId())
+                                .eq(RecycleBinInfo::getDelType, 2).eq(RecycleBinInfo::getStatus, 0));
+                recycleBinInfoList.addAll(list);
+            }
+            boolean regainNode = false;
+            if (!recycleBinInfoList.isEmpty()) {
+                List<String> processNodeList = new ArrayList<>();
+                try {
+                    //恢复数据
+                    processNodeList = recycleBinInfoList.stream().map(item -> item.getDelId() + "").collect(Collectors.toList());
+                    regainNode = this.wbsTreeContractClient.regainRemoveTreeByPrimaryKeyIds(processNodeList);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                informationQueryClient.delAsyncWbsTree(recycleBinInfoList.get(0).getContractId() + "");
+                wbsTreeContractStatisticsClient.recycleWbsTreeContractNodes(String.join(",", processNodeList));
+            }
+            //删除回收站记录
+            if (!recycleBinInfoList.isEmpty() && regainNode) {
+                List<Long> recycleBinIds = recycleBinInfoList.stream().map(RecycleBinInfo::getId).collect(Collectors.toList());
+                this.recycleBinInfoService.update(Wrappers.<RecycleBinInfo>lambdaUpdate().set(RecycleBinInfo::getStatus, 1).set(RecycleBinInfo::getUpdateTime, new Date())
+                        .set(RecycleBinInfo::getUpdateUser, AuthUtil.getUserId()).set(RecycleBinInfo::getUpdateUserName, AuthUtil.getUserName()).in(RecycleBinInfo::getId, recycleBinIds));
+                Set<Long> set = recycleBinInfoList.stream().map(RecycleBinInfo::getContractId).collect(Collectors.toSet());
+                for (Long l : set) {
+                    bladeRedis.del("blade:recycle:user:cache:" + l, "blade:recycle:user:cache:" + l + "_1");
+                }
+                return R.data(true);
+            } else {
+                return R.data(300, false, "数据恢复失败,请联系维护人员处理");
+            }
+        }
+        return R.data(300, false, "未找到需要需要恢复的数据");
+    }
+
+    @GetMapping("/queryUser")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "查询用户")
+    public R<Collection<UserVO2>> queryUser(@RequestParam Long contractId, @RequestParam(required = false, defaultValue = "0") Integer isRecycleBin, @RequestParam(required = false) String name) {
+        return R.data(this.recycleBinInfoService.queryUser(contractId,isRecycleBin, name));
+    }
+
+
+    @GetMapping("/queryOperation")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "查询同一批删除数据")
+    public R<Collection<RecycleBinInfo>> queryOperation(@RequestParam Long id) {
+        return R.data(this.recycleBinInfoService.queryOperation(id));
+    }
+
 }

+ 13 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/RecycleBinInfoMapper.java

@@ -0,0 +1,13 @@
+package org.springblade.business.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
+import org.springblade.business.entity.RecycleBinInfo;
+import org.springblade.business.vo.RecycleBinInfoVO;
+
+public interface RecycleBinInfoMapper extends BaseMapper<RecycleBinInfo> {
+
+
+    IPage<RecycleBinInfo> page(IPage<RecycleBinInfo> iPage, @Param("vo") RecycleBinInfoVO recycleBinInfoVO);
+}

+ 87 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/RecycleBinInfoMapper.xml

@@ -0,0 +1,87 @@
+<?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.business.mapper.RecycleBinInfoMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="recycleBinResultMap" type="org.springblade.business.entity.RecycleBinInfo">
+        <result column="id" property="id"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_dept" property="createDept"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+        <result column="project_id" property="projectId"/>
+        <result column="contract_id" property="contractId"/>
+        <result column="del_type" property="delType"/>
+        <result column="file_name" property="fileName"/>
+        <result column="position" property="position"/>
+        <result column="business_id" property="businessId"/>
+        <result column="operation_time" property="operationTime"/>
+        <result column="create_user_name" property="createUserName"/>
+        <result column="update_user_name" property="updateUserName"/>
+        <result column="del_id" property="delId"/>
+        <result column="del_root_id" property="delRootId"/>
+        <result column="del_root_name" property="delRootName"/>
+        <result column="operation_id" property="operationId"/>
+        <result column="jl_file_name" property="jlFileName"/>
+    </resultMap>
+
+    <sql id="includeSql" >
+        id, create_user, create_dept, create_time, update_user, update_time, status, is_deleted, project_id, contract_id, del_type, file_name, position, business_id, operation_time, create_user_name, update_user_name, del_id, del_root_id, del_root_name, operation_id, jl_file_name
+    </sql>
+    <select id="page" resultMap="recycleBinResultMap">
+        select
+        <include refid="includeSql"/>
+        from u_recycle_bin_info
+        where is_deleted = 0
+        <if test="vo.contractId != null">
+            and contract_id = #{vo.contractId}
+        </if>
+        <if test="vo.delType != null">
+            and del_type = #{vo.delType}
+        </if>
+        <if test="vo.isData != null">
+            and is_data = #{vo.isData}
+        </if>
+        <if test="vo.recycleType != null">
+            and status = #{vo.recycleType}
+            <if test="vo.recycleType == 1">
+                <if test="vo.startTime != null">
+                    and create_time >= #{vo.startTime}
+                </if>
+                <if test="vo.endTime != null">
+                    and create_time &lt;= #{vo.endTime}
+                </if>
+                <if test="vo.userId != null">
+                    and create_user = #{vo.userId}
+                </if>
+            </if>
+            <if test="vo.recycleType == 0">
+                <if test="vo.startTime != null">
+                    and update_time >= #{vo.startTime}
+                </if>
+                <if test="vo.endTime != null">
+                    and update_time &lt;= #{vo.endTime}
+                </if>
+                <if test="vo.userId != null">
+                    and update_user = #{vo.userId}
+                </if>
+            </if>
+        </if>
+        <if test="vo.content != null">
+            and (file_name like concat('%',#{vo.content},'%') or jl_file_name like concat('%',#{vo.content},'%') or delRootName like concat('%',#{vo.content},'%') or position like concat('%',#{vo.content},'%'))
+        </if>
+        <if test="vo.operationId != null">
+            and operation_id = #{vo.operationId}
+        </if>
+        order by
+        <if test="vo.recycleType == 1">
+            create_time desc
+        </if>
+        <if test="vo.recycleType == 0">
+            update_time desc
+        </if>
+    </select>
+</mapper>

+ 25 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/IRecycleBinInfoService.java

@@ -0,0 +1,25 @@
+package org.springblade.business.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springblade.business.entity.RecycleBinInfo;
+import org.springblade.business.vo.RecycleBinInfoVO;
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.system.user.vo.UserVO2;
+
+import java.util.Collection;
+import java.util.List;
+
+
+public interface IRecycleBinInfoService extends BaseService<RecycleBinInfo> {
+
+    /**
+     * 分页
+     */
+    IPage<RecycleBinInfo> selectPage(RecycleBinInfoVO recycleBinInfoVO);
+
+    Collection<UserVO2> queryUser(Long contractId, Integer isRecycleBin, String name);
+
+    Collection<RecycleBinInfo> queryOperation(Long id);
+
+    Boolean saveRecycleBinInfoByWbsTreeContract(String ids, String rootIds);
+}

+ 365 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/RecycleBinInfoServiceImpl.java

@@ -0,0 +1,365 @@
+package org.springblade.business.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.Nullable;
+import org.springblade.business.entity.InformationQuery;
+import org.springblade.business.entity.RecycleBin;
+import org.springblade.business.entity.RecycleBinInfo;
+import org.springblade.business.mapper.RecycleBinInfoMapper;
+import org.springblade.business.mapper.RecycleBinMapper;
+import org.springblade.business.service.IInformationQueryService;
+import org.springblade.business.service.IRecycleBinInfoService;
+import org.springblade.business.vo.RecycleBinInfoVO;
+import org.springblade.common.utils.SnowFlakeUtil;
+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.redis.cache.BladeRedis;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.manager.entity.ContractInfo;
+import org.springblade.manager.entity.WbsTreeContract;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.vo.UserVO2;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class RecycleBinInfoServiceImpl extends BaseServiceImpl<RecycleBinInfoMapper, RecycleBinInfo> implements IRecycleBinInfoService {
+
+    private final RecycleBinInfoMapper recycleBinInfoMapper;
+
+    private final JdbcTemplate jdbcTemplate;
+
+    private final BladeRedis bladeRedis;
+
+    private final IInformationQueryService informationQueryService;
+
+    private final static String BLADE_RECYCLE_USER_CACHE_KEY = "blade:recycle:user:cache:";
+
+    @Override
+    public IPage<RecycleBinInfo> selectPage(RecycleBinInfoVO recycleBinInfoVO) {
+        IPage<RecycleBinInfo> iPage = Condition.getPage(recycleBinInfoVO);
+        return this.recycleBinInfoMapper.page(iPage, recycleBinInfoVO);
+    }
+
+    @Override
+    public Collection<UserVO2> queryUser(Long contractId, Integer isRecycleBin, String name) {
+        String str = bladeRedis.get(BLADE_RECYCLE_USER_CACHE_KEY + contractId);
+        List<UserVO2> userVOs = getUserVOs(str);
+        List<UserVO2> userVOs1;
+        if (isRecycleBin == 1) {
+            userVOs1 = getUpdateUser(contractId);
+        } else {
+            userVOs1 = new ArrayList<>();
+        }
+        if (userVOs.isEmpty()) {
+            List<RecycleBinInfo> list = this.list(Wrappers.<RecycleBinInfo>lambdaQuery()
+                    .select(RecycleBinInfo::getCreateUser, RecycleBinInfo::getCreateUserName)
+                    .eq(RecycleBinInfo::getContractId, contractId).eq(RecycleBinInfo::getDelType, 2)
+                    .groupBy(Arrays.asList(RecycleBinInfo::getCreateUser,RecycleBinInfo::getCreateUserName)));
+            if (list != null && !list.isEmpty()) {
+                list.forEach(item -> {
+                    UserVO2 vo2 = new UserVO2();
+                    vo2.setId(item.getCreateUser());
+                    vo2.setName(item.getCreateUserName());
+                    userVOs.add(vo2);
+                });
+                bladeRedis.set(BLADE_RECYCLE_USER_CACHE_KEY + contractId, JSON.toJSONString(userVOs));
+                bladeRedis.expire(BLADE_RECYCLE_USER_CACHE_KEY + contractId, 60 * 60);
+            }
+        }
+        if (!userVOs.isEmpty()) {
+            if (name != null && !name.isEmpty()) {
+                List<UserVO2> collect = userVOs.stream().filter(item -> item.getName() != null && item.getName().contains(name)).collect(Collectors.toList());
+                Set<UserVO2> set = new HashSet<>(collect);
+                set.addAll(userVOs1.stream().filter(item -> item.getName() != null && item.getName().contains(name)).collect(Collectors.toList()));
+                return set;
+            }
+            Set<UserVO2> set = new HashSet<>(userVOs);
+            set.addAll(userVOs1);
+            return set;
+        }
+        return userVOs;
+    }
+
+    @Override
+    public Collection<RecycleBinInfo> queryOperation(Long id) {
+        RecycleBinInfo info = this.getById(id);
+        if (info == null || !StringUtil.hasText(info.getOperationId())) {
+            return new ArrayList<>();
+        }
+        return this.list(Wrappers.<RecycleBinInfo>lambdaQuery().eq(RecycleBinInfo::getOperationId, info.getOperationId()).eq(RecycleBinInfo::getDelType, info.getDelType()).eq(RecycleBinInfo::getStatus, info.getStatus()));
+    }
+
+    @Override
+    public Boolean saveRecycleBinInfoByWbsTreeContract(String ids, String rootIds) {
+        if (ids == null || ids.isEmpty()) {
+            return true;
+        }
+        List<WbsTreeContract> query = jdbcTemplate.query("select * from m_wbs_tree_contract where p_key_id in ( " + ids + ") and is_deleted = 0 and type = 1", new BeanPropertyRowMapper<>(WbsTreeContract.class));
+        if (query.isEmpty()) {
+            return true;
+        }
+        List<WbsTreeContract> rootContracts = new ArrayList<>();
+        List<String> parentIds = new ArrayList<>();
+        for (WbsTreeContract contract : query) {
+            if (rootIds.contains(contract.getPKeyId() + "")) {
+                rootContracts.add(contract);
+            }
+            String ancestorsPId = contract.getAncestorsPId();
+            if (StringUtil.hasText(ancestorsPId)) {
+                String[] split = ancestorsPId.split(",");
+                for (String s : split) {
+                    if (StringUtil.isNumeric(s)) {
+                        parentIds.add(s);
+                    }
+                }
+            }
+        }
+        List<WbsTreeContract> parentContracts;
+        if (!parentIds.isEmpty()) {
+            parentContracts = jdbcTemplate.query("select * from m_wbs_tree_contract where p_key_id in ( " + String.join(",", parentIds) + ") and is_deleted = 0 and type = 1", new BeanPropertyRowMapper<>(WbsTreeContract.class));
+        } else {
+            parentContracts = new ArrayList<>();
+        }
+        Map<Long, WbsTreeContract> rootMap = rootContracts.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, item -> item));
+        Map<Long, WbsTreeContract> parentMap = parentContracts.stream().collect(Collectors.toMap(WbsTreeContract::getPKeyId, item -> item));
+        String contractId = query.get(0).getContractId();
+        List<InformationQuery> queries = informationQueryService.list(Wrappers.<InformationQuery>lambdaQuery().select(InformationQuery::getWbsId, InformationQuery::getClassify, InformationQuery::getName)
+                .eq(InformationQuery::getContractId, contractId).in(InformationQuery::getWbsId, ids).in(InformationQuery::getClassify, 1, 2));
+        Map<String, List<InformationQuery>> map = queries.stream().collect(Collectors.groupingBy(item -> item.getWbsId() + "_" + item.getClassify()));
+        List<ContractInfo> contractInfos = jdbcTemplate.query("select * from m_contract_info where id = ( " + contractId + ") and is_deleted = 0", new BeanPropertyRowMapper<>(ContractInfo.class));
+        ContractInfo contractInfo;
+        if (contractInfos.isEmpty()) {
+            contractInfo = new ContractInfo();
+        } else {
+            contractInfo = contractInfos.get(0);
+        }
+        long operationId = SnowFlakeUtil.getId();
+        List<RecycleBinInfo> recycleBinInfos = new ArrayList<>(query.size());
+        for (WbsTreeContract contract : query) {
+            RecycleBinInfo info = new RecycleBinInfo();
+            info.setId(SnowFlakeUtil.getId());
+            info.setContractId(Long.parseLong(contract.getContractId()));
+            info.setProjectId(Long.parseLong(contract.getProjectId()));
+            info.setOperationId(operationId + "");
+            info.setDelType(2);
+            info.setCreateUserName(AuthUtil.getUserName());
+            info.setCreateUser(AuthUtil.getUserId());
+            info.setOperationTime(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
+            info.setDelId(contract.getPKeyId());
+
+            WbsTreeContract delRoot = rootMap.get(contract.getPKeyId());
+            if (delRoot != null) {
+                info.setDelRootId(delRoot.getPKeyId());
+                info.setDelRootName(StringUtil.hasText(delRoot.getFullName()) ? delRoot.getFullName() : delRoot.getNodeName());
+            }
+            String ancestorsPId = contract.getAncestorsPId();
+            StringBuilder sb = new StringBuilder();
+            if (StringUtil.hasText(ancestorsPId)) {
+                String[] split = ancestorsPId.split(",");
+                for (String s : split) {
+                    if (StringUtil.isNumeric(s)) {
+                        WbsTreeContract parent = parentMap.get(Long.parseLong(s));
+                        if (parent != null) {
+                            if (parent.getPId() == null || parent.getPId() == 0) {
+                                sb.append(contractInfo.getContractName()).append("/");
+                            } else {
+                                sb.append(StringUtil.hasText(parent.getFullName()) ? parent.getFullName() : parent.getNodeName()).append("/");
+                            }
+                            if (info.getDelRootId() == null) {
+                                WbsTreeContract temp = rootMap.get(parent.getPKeyId());
+                                if (temp != null) {
+                                    info.setDelRootId(temp.getPKeyId());
+                                    info.setDelRootName(StringUtil.hasText(temp.getFullName()) ? temp.getFullName() : temp.getNodeName());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (info.getDelRootId() == null) {
+                info.setDelRootId(info.getDelId());
+                info.setDelRootName(StringUtil.hasText(contract.getFullName()) ? contract.getFullName() : contract.getNodeName());
+            }
+            info.setPosition(sb + (StringUtil.hasText(contract.getFullName()) ? contract.getFullName() : contract.getNodeName()));
+            List<InformationQuery> queryList = map.get(contract.getPKeyId() + "_1");
+            if (queryList != null && !queryList.isEmpty()) {
+                info.setFileName(queryList.get(0).getName());
+                info.setIsData(1);
+            }
+            queryList = map.get(contract.getPKeyId() + "_2");
+            if (queryList != null && !queryList.isEmpty()) {
+                info.setJlFileName(queryList.get(0).getName());
+                info.setIsData(1);
+            }
+            recycleBinInfos.add( info);
+        }
+        this.saveBatch(recycleBinInfos);
+        bladeRedis.del(BLADE_RECYCLE_USER_CACHE_KEY + contractId + "_1", BLADE_RECYCLE_USER_CACHE_KEY + contractId);
+        return true;
+    }
+
+    private List<UserVO2> getUpdateUser(Long contractId) {
+        String key = BLADE_RECYCLE_USER_CACHE_KEY + contractId + "_1";
+        String str1 = bladeRedis.get(key);
+        List<UserVO2> userVOs1 = getUserVOs(str1);
+        if (!userVOs1.isEmpty()) {
+            return userVOs1;
+        }
+        List<RecycleBinInfo> list = this.list(Wrappers.<RecycleBinInfo>lambdaQuery()
+                .select(RecycleBinInfo::getUpdateUser, RecycleBinInfo::getUpdateUserName)
+                .eq(RecycleBinInfo::getContractId, contractId).eq(RecycleBinInfo::getDelType, 2).eq(RecycleBinInfo::getStatus, 1)
+                .groupBy(Arrays.asList(RecycleBinInfo::getUpdateUser,RecycleBinInfo::getUpdateUserName)));
+        if (list != null && !list.isEmpty()) {
+            list.forEach(item -> {
+                UserVO2 vo2 = new UserVO2();
+                vo2.setId(item.getUpdateUser());
+                vo2.setName(item.getUpdateUserName());
+                userVOs1.add(vo2);
+            });
+            bladeRedis.set(key, JSON.toJSONString(userVOs1));
+            bladeRedis.expire(key, 60 * 60);
+        }
+        return userVOs1;
+    }
+    private List<UserVO2> getUserVOs(String str) {
+        if (str != null) {
+            try {
+                List<UserVO2> userVO2s = JSON.parseArray(str, UserVO2.class);
+                if (userVO2s != null && !userVO2s.isEmpty()) {
+                    return userVO2s;
+                }
+            } catch (Exception e) {
+                log.warn("从缓存中获取用户信息失败,error msg:" + e.getMessage());
+            }
+        }
+        return new ArrayList<>();
+    }
+
+    public void sync() {
+        List<ContractInfo> contractInfos = jdbcTemplate.query("select id,contract_name from m_contract_info where is_deleted = 0 and p_id in (select id from m_project_info where is_deleted = 0)", new BeanPropertyRowMapper<>(ContractInfo.class));
+        List<User> users = jdbcTemplate.query("select * from blade_user", new BeanPropertyRowMapper<>(User.class));
+        Map<Long, User> userMap = users.stream().collect(Collectors.toMap(User::getId, item -> item));
+        for (ContractInfo contractInfo : contractInfos) {
+            List<RecycleBin> recycleBins = jdbcTemplate.query("select * from u_recycle_bin where del_type = 2 and contract_id = " + contractInfo.getId(), new BeanPropertyRowMapper<>(RecycleBin.class));
+            if (recycleBins.isEmpty()) {
+                continue;
+            }
+            Map<Long, WbsTreeContract> treeContractMap = new TreeMap<>();
+            for (RecycleBin recycleBin : recycleBins) {
+                String businessId = recycleBin.getBusinessId();
+                String[] split1 = businessId.split(",");
+                String ids = Arrays.stream(split1).filter(StringUtil::isNumeric).collect(Collectors.joining(","));
+                if (StringUtil.isEmpty(ids)) {
+                    continue;
+                }
+                List<WbsTreeContract> query = jdbcTemplate.query("select * from m_wbs_tree_contract where p_key_id in ( " + ids + ") and type = 1", new BeanPropertyRowMapper<>(WbsTreeContract.class));
+                if (query.isEmpty()) {
+                    continue;
+                }
+                List<String> parentIds = new ArrayList<>();
+                for (WbsTreeContract contract : query) {
+                    String ancestorsPId = contract.getAncestorsPId();
+                    if (StringUtil.hasText(ancestorsPId)) {
+                        String[] split = ancestorsPId.split(",");
+                        for (String s : split) {
+                            if (StringUtil.isNumeric(s)) {
+                                long l = Long.parseLong(s);
+                                if (treeContractMap.containsKey(l)) {
+                                    continue;
+                                }
+                                parentIds.add(s);
+                            }
+                        }
+                    }
+                    treeContractMap.put(contract.getPKeyId(), contract);
+                }
+                List<WbsTreeContract> parentContracts;
+                if (!parentIds.isEmpty()) {
+                    parentContracts = jdbcTemplate.query("select * from m_wbs_tree_contract where p_key_id in ( " + String.join(",", parentIds) + ") and is_deleted = 0 and type = 1", new BeanPropertyRowMapper<>(WbsTreeContract.class));
+                } else {
+                    parentContracts = new ArrayList<>();
+                }
+                if (!parentContracts.isEmpty()) {
+                    parentContracts.forEach(item -> treeContractMap.put(item.getPKeyId(), item));
+                }
+                List<InformationQuery> queries = informationQueryService.list(Wrappers.<InformationQuery>lambdaQuery().select(InformationQuery::getWbsId, InformationQuery::getClassify, InformationQuery::getName)
+                        .eq(InformationQuery::getContractId, contractInfo.getId()).in(InformationQuery::getWbsId, ids).in(InformationQuery::getClassify, 1, 2));
+                Map<String, List<InformationQuery>> map = queries.stream().collect(Collectors.groupingBy(item -> item.getWbsId() + "_" + item.getClassify()));
+                long operationId = SnowFlakeUtil.getId();
+                List<RecycleBinInfo> recycleBinInfos = new ArrayList<>(query.size());
+                for (WbsTreeContract contract : query) {
+                    RecycleBinInfo info = new RecycleBinInfo();
+                    info.setId(SnowFlakeUtil.getId());
+                    info.setContractId(Long.parseLong(contract.getContractId()));
+                    info.setProjectId(Long.parseLong(contract.getProjectId()));
+                    info.setOperationId(operationId + "");
+                    info.setDelType(2);
+                    info.setCreateUserName(recycleBin.getCreateUserName());
+                    info.setCreateUser(recycleBin.getCreateUser());
+                    info.setCreateTime(recycleBin.getCreateTime());
+                    info.setOperationTime(recycleBin.getOperationTime());
+                    info.setDelId(contract.getPKeyId());
+                    if (recycleBin.getIsDeleted() == 1) {
+                        info.setStatus(1);
+                        info.setUpdateUser(recycleBin.getUpdateUser());
+                        info.setUpdateTime(recycleBin.getUpdateTime());
+                        User user = userMap.get(recycleBin.getUpdateUser());
+                        info.setUpdateUserName(user.getName());
+                        info.setUpdateTime(recycleBin.getUpdateTime());
+                    }
+
+                    String ancestorsPId = contract.getAncestorsPId();
+                    StringBuilder sb = new StringBuilder();
+                    if (StringUtil.hasText(ancestorsPId)) {
+                        String[] split = ancestorsPId.split(",");
+                        for (String s : split) {
+                            if (StringUtil.isNumeric(s)) {
+                                WbsTreeContract parent = treeContractMap.get(Long.parseLong(s));
+                                if (parent != null) {
+                                    if (parent.getPId() == null || parent.getPId() == 0) {
+                                        sb.append(contractInfo.getContractName()).append("/");
+                                    } else {
+                                        sb.append(StringUtil.hasText(parent.getFullName()) ? parent.getFullName() : parent.getNodeName()).append("/");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (info.getDelRootId() == null) {
+                        info.setDelRootId(info.getDelId());
+                        info.setDelRootName(StringUtil.hasText(contract.getFullName()) ? contract.getFullName() : contract.getNodeName());
+                    }
+                    info.setPosition(sb + (StringUtil.hasText(contract.getFullName()) ? contract.getFullName() : contract.getNodeName()));
+                    List<InformationQuery> queryList = map.get(contract.getPKeyId() + "_1");
+                    if (queryList != null && !queryList.isEmpty()) {
+                        info.setFileName(queryList.get(0).getName());
+                        info.setIsData(1);
+                    }
+                    queryList = map.get(contract.getPKeyId() + "_2");
+                    if (queryList != null && !queryList.isEmpty()) {
+                        info.setJlFileName(queryList.get(0).getName());
+                        info.setIsData(1);
+                    }
+                    recycleBinInfos.add( info);
+                }
+                this.saveBatch(recycleBinInfos);
+            }
+        }
+    }
+}

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

@@ -150,7 +150,18 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
 
     @Override
     public Boolean regainRemoveTreeByPrimaryKeyIds(List<String> primaryKeyIds) {
-        return this.baseMapper.regainRemoveTreeByPrimaryKeyIds(primaryKeyIds);
+        // 恢复时如果底层节点名称相同就加上 `(恢)`
+        Boolean bool = this.baseMapper.regainRemoveTreeByPrimaryKeyIds(primaryKeyIds);
+        List<WbsTreeContract> list = this.list(Wrappers.<WbsTreeContract>lambdaQuery().in(WbsTreeContract::getPKeyId, primaryKeyIds));
+        if (list != null && !list.isEmpty()) {
+            list.forEach(item -> {
+                String nodeName = item.getNodeName();
+                if (nodeName.contains("(恢复)")) {
+                    item.setNodeName(nodeName.replace("(恢复)", ""));
+                }
+            });
+        }
+        return bool;
     }
 
     @Override