mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-04-02 08:43:23 +08:00
update 增加部门Excel转换处理和下拉选项数据源
This commit is contained in:
@@ -103,6 +103,7 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 授权认证 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
<artifactId>ruoyi-common-social</artifactId>
|
<artifactId>ruoyi-common-social</artifactId>
|
||||||
@@ -151,6 +152,7 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- mqtt模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
<artifactId>ruoyi-common-mqtt</artifactId>
|
<artifactId>ruoyi-common-mqtt</artifactId>
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import cn.hutool.core.lang.tree.Tree;
|
|||||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
|
import cn.hutool.core.lang.tree.TreeNodeConfig;
|
||||||
import cn.hutool.core.lang.tree.TreeUtil;
|
import cn.hutool.core.lang.tree.TreeUtil;
|
||||||
import cn.hutool.core.lang.tree.parser.NodeParser;
|
import cn.hutool.core.lang.tree.parser.NodeParser;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -41,7 +43,7 @@ public class TreeBuildUtils extends TreeUtil {
|
|||||||
* @param <T> 输入节点的类型
|
* @param <T> 输入节点的类型
|
||||||
* @return 构建好的树形结构列表
|
* @return 构建好的树形结构列表
|
||||||
*/
|
*/
|
||||||
public static <K, T> List<T> build(List<T> items, K parentId, Function<T, K> classifier, BiConsumer<T,Map<K, List<T>>> action) {
|
public static <K, T> List<T> build(List<T> items, K parentId, Function<T, K> classifier, BiConsumer<T, Map<K, List<T>>> action) {
|
||||||
// 构建动态规划表 (依据父ID分组)
|
// 构建动态规划表 (依据父ID分组)
|
||||||
Map<K, List<T>> nodeTreeMaps = items.stream().collect(Collectors.groupingBy(classifier));
|
Map<K, List<T>> nodeTreeMaps = items.stream().collect(Collectors.groupingBy(classifier));
|
||||||
// 回溯构建各级节点关系
|
// 回溯构建各级节点关系
|
||||||
@@ -124,6 +126,20 @@ public class TreeBuildUtils extends TreeUtil {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建树节点路径 Map:路径为 key,节点为 value
|
||||||
|
*
|
||||||
|
* @param trees 树结构
|
||||||
|
* @param joiner 拼接符 / - _
|
||||||
|
* @param fieldGetter 路径拼接字段(Tree::getName / Tree::getId)
|
||||||
|
* @return Map<拼接路径, 原始Tree节点>
|
||||||
|
*/
|
||||||
|
public static <K> Map<String, Tree<K>> buildTreeNodeMap(List<Tree<K>> trees, String joiner, Function<Tree<K>, CharSequence> fieldGetter) {
|
||||||
|
Map<String, Tree<K>> nodeMap = new LinkedHashMap<>();
|
||||||
|
doBuildTreeNodeMap(trees, "", joiner, fieldGetter, nodeMap);
|
||||||
|
return nodeMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定节点下的所有叶子节点
|
* 获取指定节点下的所有叶子节点
|
||||||
*
|
*
|
||||||
@@ -141,4 +157,33 @@ public class TreeBuildUtils extends TreeUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归构建【路径为key,节点为value】的Map
|
||||||
|
* <p>深度优先遍历树结构,将拼接好的路径作为key,原始Tree节点作为value</p>
|
||||||
|
*
|
||||||
|
* @param trees 当前层级的节点列表
|
||||||
|
* @param parentPath 父节点已拼接好的路径
|
||||||
|
* @param joiner 路径拼接符,如 "/"、"-"、"_"
|
||||||
|
* @param fieldGetter 用于拼接路径的节点字段获取器
|
||||||
|
* @param nodeMap 存放最终结果的有序Map(路径->Tree节点)
|
||||||
|
* @param <K> 树节点ID的类型
|
||||||
|
*/
|
||||||
|
private static <K> void doBuildTreeNodeMap(List<Tree<K>> trees, String parentPath, String joiner, Function<Tree<K>, CharSequence> fieldGetter, Map<String, Tree<K>> nodeMap) {
|
||||||
|
if (CollUtil.isEmpty(trees)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Tree<K> tree : trees) {
|
||||||
|
CharSequence field = fieldGetter.apply(tree);
|
||||||
|
if (StrUtil.isEmpty(field)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 拼接路径作为 KEY
|
||||||
|
String currentPath = StrUtil.isEmpty(parentPath) ? field.toString() : parentPath + joiner + field;
|
||||||
|
// 路径 = key,节点 = value
|
||||||
|
nodeMap.put(currentPath, tree);
|
||||||
|
// 递归子节点
|
||||||
|
doBuildTreeNodeMap(tree.getChildren(), currentPath, joiner, fieldGetter, nodeMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
|
|||||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||||
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||||
import org.dromara.common.excel.convert.ExcelDictConvert;
|
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||||
|
import org.dromara.system.listener.DeptExcelConverter;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@@ -15,7 +16,6 @@ import java.util.Date;
|
|||||||
*
|
*
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class SysUserExportVo implements Serializable {
|
public class SysUserExportVo implements Serializable {
|
||||||
@@ -35,6 +35,12 @@ public class SysUserExportVo implements Serializable {
|
|||||||
@ExcelProperty(value = "用户账号")
|
@ExcelProperty(value = "用户账号")
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "部门名称", converter = DeptExcelConverter.class)
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户昵称
|
* 用户昵称
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import lombok.Data;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||||
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||||
|
import org.dromara.common.excel.annotation.ExcelDynamicOptions;
|
||||||
import org.dromara.common.excel.convert.ExcelDictConvert;
|
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||||
|
import org.dromara.system.listener.DeptExcelConverter;
|
||||||
|
import org.dromara.system.listener.DeptExcelOptions;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@@ -32,7 +35,8 @@ public class SysUserImportVo implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 部门ID
|
* 部门ID
|
||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "部门编号")
|
@ExcelProperty(value = "部门名称", converter = DeptExcelConverter.class)
|
||||||
|
@ExcelDynamicOptions(providerClass = DeptExcelOptions.class)
|
||||||
private Long deptId;
|
private Long deptId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package org.dromara.system.listener;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fesod.sheet.converters.Converter;
|
||||||
|
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
|
||||||
|
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
|
||||||
|
import org.apache.fesod.sheet.metadata.data.ReadCellData;
|
||||||
|
import org.apache.fesod.sheet.metadata.data.WriteCellData;
|
||||||
|
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.TreeBuildUtils;
|
||||||
|
import org.dromara.system.domain.bo.SysDeptBo;
|
||||||
|
import org.dromara.system.service.ISysDeptService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excel 部门转换处理
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component
|
||||||
|
public class DeptExcelConverter implements Converter<Long> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程内缓存:ID → 名称
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<Map<Long, String>> TL_ID_TO_NAME = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程内缓存:名称 → ID
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<Map<String, Long>> TL_NAME_TO_ID = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化当前线程的部门缓存(一次导出/导入只执行1次)
|
||||||
|
*/
|
||||||
|
private void initThreadCache() {
|
||||||
|
Map<Long, String> idMap = TL_ID_TO_NAME.get();
|
||||||
|
if (CollUtil.isNotEmpty(idMap)) {
|
||||||
|
// 当前线程已加载,直接返回
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Tree<Long>> deptPathToTreeMap = TreeBuildUtils.buildTreeNodeMap(
|
||||||
|
SpringUtils.getBean(ISysDeptService.class).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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【导出】部门 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, "");
|
||||||
|
return new WriteCellData<>(deptName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user