mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-04-04 01:33:23 +08:00
fix 修复 DeptExcelConverter 存在的内存泄漏问题 与优化部分代码
This commit is contained in:
@@ -85,12 +85,6 @@ public class SysUserExportVo implements Serializable {
|
|||||||
@ExcelProperty(value = "最后登录时间")
|
@ExcelProperty(value = "最后登录时间")
|
||||||
private Date loginDate;
|
private Date loginDate;
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门名称
|
|
||||||
*/
|
|
||||||
@ExcelProperty(value = "部门名称")
|
|
||||||
private String deptName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责人
|
* 负责人
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.dromara.system.listener;
|
package org.dromara.system.listener;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.lang.tree.Tree;
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fesod.sheet.converters.Converter;
|
import org.apache.fesod.sheet.converters.Converter;
|
||||||
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
|
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
|
||||||
@@ -9,7 +10,6 @@ import org.apache.fesod.sheet.metadata.GlobalConfiguration;
|
|||||||
import org.apache.fesod.sheet.metadata.data.ReadCellData;
|
import org.apache.fesod.sheet.metadata.data.ReadCellData;
|
||||||
import org.apache.fesod.sheet.metadata.data.WriteCellData;
|
import org.apache.fesod.sheet.metadata.data.WriteCellData;
|
||||||
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
|
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.core.utils.TreeBuildUtils;
|
import org.dromara.common.core.utils.TreeBuildUtils;
|
||||||
import org.dromara.system.domain.bo.SysDeptBo;
|
import org.dromara.system.domain.bo.SysDeptBo;
|
||||||
@@ -18,6 +18,7 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excel 部门转换处理
|
* Excel 部门转换处理
|
||||||
@@ -28,44 +29,40 @@ import java.util.Map;
|
|||||||
@Component
|
@Component
|
||||||
public class DeptExcelConverter implements Converter<Long> {
|
public class DeptExcelConverter implements Converter<Long> {
|
||||||
|
|
||||||
/**
|
private static final String CACHE_KEY = "dept:excel";
|
||||||
* 线程内缓存:ID → 名称
|
|
||||||
*/
|
|
||||||
private static final ThreadLocal<Map<Long, String>> TL_ID_TO_NAME = new ThreadLocal<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 线程内缓存:名称 → ID
|
* Caffeine 缓存:key=CACHE_KEY,value=[idToName, nameToId],5分钟过期
|
||||||
*/
|
*/
|
||||||
private static final ThreadLocal<Map<String, Long>> TL_NAME_TO_ID = new ThreadLocal<>();
|
private static final Cache<String, DeptMaps> DEPT_CACHE = Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(30, TimeUnit.SECONDS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private final ISysDeptService deptService;
|
||||||
|
|
||||||
|
private DeptMaps getDeptMaps() {
|
||||||
|
return DEPT_CACHE.get(CACHE_KEY, k -> {
|
||||||
|
Map<String, Tree<Long>> deptPathToTreeMap = buildDeptPathMap(deptService);
|
||||||
|
Map<Long, String> idToName = new HashMap<>();
|
||||||
|
Map<String, Long> nameToId = new HashMap<>();
|
||||||
|
deptPathToTreeMap.forEach((name, treeNode) -> {
|
||||||
|
Long deptId = treeNode.getId();
|
||||||
|
idToName.put(deptId, name);
|
||||||
|
nameToId.put(name, deptId);
|
||||||
|
});
|
||||||
|
return new DeptMaps(idToName, nameToId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化当前线程的部门缓存(一次导出/导入只执行1次)
|
* 构建部门路径 → 树节点映射,供 Converter 和 Options 共用
|
||||||
*/
|
*/
|
||||||
private void initThreadCache() {
|
static Map<String, Tree<Long>> buildDeptPathMap(ISysDeptService deptService) {
|
||||||
Map<Long, String> idMap = TL_ID_TO_NAME.get();
|
return TreeBuildUtils.buildTreeNodeMap(
|
||||||
if (CollUtil.isNotEmpty(idMap)) {
|
deptService.selectDeptTreeList(new SysDeptBo()),
|
||||||
// 当前线程已加载,直接返回
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Tree<Long>> deptPathToTreeMap = TreeBuildUtils.buildTreeNodeMap(
|
|
||||||
SpringUtils.getBean(ISysDeptService.class).selectDeptTreeList(new SysDeptBo()),
|
|
||||||
"/",
|
"/",
|
||||||
Tree::getName
|
Tree::getName
|
||||||
);
|
);
|
||||||
|
|
||||||
// 构建互转映射
|
|
||||||
Map<Long, String> idToName = new HashMap<>();
|
|
||||||
Map<String, Long> nameToId = new HashMap<>();
|
|
||||||
deptPathToTreeMap.forEach((name, treeNode) -> {
|
|
||||||
Long deptId = treeNode.getId();
|
|
||||||
idToName.put(deptId, name);
|
|
||||||
nameToId.put(name, deptId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 设置到当前线程
|
|
||||||
TL_ID_TO_NAME.set(idToName);
|
|
||||||
TL_NAME_TO_ID.set(nameToId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,12 +90,7 @@ public class DeptExcelConverter implements Converter<Long> {
|
|||||||
if (StringUtils.isBlank(deptName)) {
|
if (StringUtils.isBlank(deptName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return getDeptMaps().nameToId().get(deptName);
|
||||||
// 初始化线程缓存(只执行一次)
|
|
||||||
initThreadCache();
|
|
||||||
|
|
||||||
// 从线程缓存中直接获取,不查库
|
|
||||||
return TL_NAME_TO_ID.get().get(deptName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,13 +101,11 @@ public class DeptExcelConverter implements Converter<Long> {
|
|||||||
if (value == null) {
|
if (value == null) {
|
||||||
return new WriteCellData<>("");
|
return new WriteCellData<>("");
|
||||||
}
|
}
|
||||||
|
String deptName = getDeptMaps().idToName().getOrDefault(value, "");
|
||||||
// 初始化线程缓存(只执行一次)
|
|
||||||
initThreadCache();
|
|
||||||
|
|
||||||
// 从线程缓存中直接获取,不查库
|
|
||||||
String deptName = TL_ID_TO_NAME.get().getOrDefault(value, "");
|
|
||||||
return new WriteCellData<>(deptName);
|
return new WriteCellData<>(deptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record DeptMaps(Map<Long, String> idToName, Map<String, Long> nameToId) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
package org.dromara.system.listener;
|
package org.dromara.system.listener;
|
||||||
|
|
||||||
import cn.hutool.core.lang.tree.Tree;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.common.core.utils.TreeBuildUtils;
|
|
||||||
import org.dromara.common.excel.core.ExcelOptionsProvider;
|
import org.dromara.common.excel.core.ExcelOptionsProvider;
|
||||||
import org.dromara.system.domain.bo.SysDeptBo;
|
|
||||||
import org.dromara.system.service.ISysDeptService;
|
import org.dromara.system.service.ISysDeptService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,9 +25,7 @@ public class DeptExcelOptions implements ExcelOptionsProvider {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getOptions() {
|
public Set<String> getOptions() {
|
||||||
List<Tree<Long>> trees = deptService.selectDeptTreeList(new SysDeptBo());
|
return DeptExcelConverter.buildDeptPathMap(deptService).keySet();
|
||||||
Map<String, Tree<Long>> treeMap = TreeBuildUtils.buildTreeNodeMap(trees, "/", Tree::getName);
|
|
||||||
return treeMap.keySet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user