fix 修复 DeptExcelConverter 存在的内存泄漏问题 与优化部分代码

This commit is contained in:
疯狂的狮子Li
2026-03-31 17:54:41 +08:00
parent ec9e98096f
commit 37e1ed0bf3
3 changed files with 34 additions and 57 deletions

View File

@@ -85,12 +85,6 @@ public class SysUserExportVo implements Serializable {
@ExcelProperty(value = "最后登录时间") @ExcelProperty(value = "最后登录时间")
private Date loginDate; private Date loginDate;
/**
* 部门名称
*/
@ExcelProperty(value = "部门名称")
private String deptName;
/** /**
* 负责人 * 负责人
*/ */

View File

@@ -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_KEYvalue=[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) {
}
} }

View File

@@ -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();
} }
} }