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

This commit is contained in:
疯狂的狮子Li
2026-04-01 09:51:09 +08:00
parent 9475a9118d
commit d5457ed99c
2 changed files with 61 additions and 37 deletions

View File

@@ -1,7 +1,8 @@
package org.dromara.system.listener;
import cn.hutool.core.collection.CollUtil;
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 org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
@@ -18,69 +19,92 @@ import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Excel 部门转换处理
*
* @author AprilWind
*/
@RequiredArgsConstructor
@Component
public class DeptExcelConverter implements Converter<Long> {
private static final ThreadLocal<Map<Long, String>> TL_ID_TO_NAME = new ThreadLocal<>();
private static final String CACHE_KEY = "dept:excel";
private static final ThreadLocal<Map<String, Long>> TL_NAME_TO_ID = new ThreadLocal<>();
/**
* Caffeine 缓存key=CACHE_KEYvalue=[idToName, nameToId]5分钟过期
*/
private static final Cache<String, DeptMaps> DEPT_CACHE = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.build();
private void initThreadCache() {
Map<Long, String> idMap = TL_ID_TO_NAME.get();
if (CollUtil.isNotEmpty(idMap)) {
return;
}
private DeptMaps getDeptMaps() {
ISysDeptService deptService = SpringUtils.getBean(ISysDeptService.class);
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);
});
}
Map<String, Tree<Long>> deptPathToTreeMap = TreeBuildUtils.buildTreeNodeMap(
SpringUtils.getBean(ISysDeptService.class).selectDeptTreeList(new SysDeptBo()),
/**
* 构建部门路径 → 树节点映射,供 Converter 和 Options 共用
*/
static Map<String, Tree<Long>> buildDeptPathMap(ISysDeptService deptService) {
return TreeBuildUtils.buildTreeNodeMap(
deptService.selectDeptTreeList(new SysDeptBo()),
"/",
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);
}
/**
* 指定 Java 类型Long部门ID
*/
@Override
public Class<?> supportJavaTypeKey() {
return Long.class;
}
/**
* 指定 Excel 类型:字符串
*/
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 【导入】Excel 填写的部门完整路径 → 转为 ID
*/
@Override
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
String deptName = cellData.getStringValue();
if (StringUtils.isBlank(deptName)) {
return null;
}
initThreadCache();
return TL_NAME_TO_ID.get().get(deptName);
return getDeptMaps().nameToId().get(deptName);
}
/**
* 【导出】部门 ID → 转为 Excel 显示的完整路径名称
*/
@Override
public WriteCellData<?> convertToExcelData(Long value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (value == null) {
return new WriteCellData<>("");
}
initThreadCache();
String deptName = TL_ID_TO_NAME.get().getOrDefault(value, "");
String deptName = getDeptMaps().idToName().getOrDefault(value, "");
return new WriteCellData<>(deptName);
}
private record DeptMaps(Map<Long, String> idToName, Map<String, Long> nameToId) {
}
}

View File

@@ -1,30 +1,30 @@
package org.dromara.system.listener;
import cn.hutool.core.lang.tree.Tree;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.TreeBuildUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.excel.core.ExcelOptionsProvider;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.service.ISysDeptService;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Excel 部门下拉选项数据源
*
* @author AprilWind
*/
@RequiredArgsConstructor
@Component
public class DeptExcelOptions implements ExcelOptionsProvider {
private final ISysDeptService deptService;
/**
* 获取下拉选项数据
*
* @return 下拉选项列表
*/
@Override
public Set<String> getOptions() {
List<Tree<Long>> trees = deptService.selectDeptTreeList(new SysDeptBo());
Map<String, Tree<Long>> treeMap = TreeBuildUtils.buildTreeNodeMap(trees, "/", Tree::getName);
return treeMap.keySet();
ISysDeptService deptService = SpringUtils.getBean(ISysDeptService.class);
return DeptExcelConverter.buildDeptPathMap(deptService).keySet();
}
}