如题,在研究 ruoyi 框架时发现 ruoyi 生成树状结构的方法需要硬编码具体的类型
,如下所示
遂用反射改造了该方法(使用构造者模式,使用者仅需一行代码即可将多条数据记录构造为树状结构)
代码如下,main方法为测试方法
package com.ruoyi.common.utils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 该类负责将通用的包含父子关系的对象列表构造为多主根的树状结构输出
*/
public class BuildTreeUtil<T> {
/**
* @param args 测试
*/
public static void main(String[] args) {
class DataObject {
@Override
public String toString() {
return "{" +
"\"id\":" + id +
", \"parentId\":" + parentId +
", \"label\":" + "\""+label +"\""+
", \"children\":" + children +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public List<DataObject> getChildren() {
return children;
}
public void setChildren(List<DataObject> children) {
this.children = children;
}
int id;
int parentId;
final String label;
List<DataObject> children;
public DataObject(int id, int parentId,String label, List<DataObject> children) {
this.id = id;
this.parentId = parentId;
this.label = label;
this.children = children;
}
}
List<DataObject> test_data = new ArrayList<>();
test_data.add(new DataObject(1, 0, "类目"+1,new ArrayList<>()));
test_data.add(new DataObject(2, 0, "类目"+2,new ArrayList<>()));
test_data.add(new DataObject(3, 1, "类目"+3,new ArrayList<>()));
test_data.add(new DataObject(4, 1, "类目"+4,new ArrayList<>()));
test_data.add(new DataObject(5, 2, "类目"+5,new ArrayList<>()));
test_data.add(new DataObject(6, 2, "类目"+6,new ArrayList<>()));
test_data.add(new DataObject(7, 3, "类目"+7,new ArrayList<>()));
test_data.add(new DataObject(8, 3, "类目"+8,new ArrayList<>()));
test_data.add(new DataObject(9, 4, "类目"+9,new ArrayList<>()));
test_data.add(new DataObject(10, 4,"类目"+10, new ArrayList<>()));
test_data.add(new DataObject(11, 5,"类目"+11, new ArrayList<>()));
test_data.add(new DataObject(12, 5,"类目"+12, new ArrayList<>()));
test_data.add(new DataObject(13, 6,"类目"+13, new ArrayList<>()));
test_data.add(new DataObject(14, 6,"类目"+14, new ArrayList<>()));
test_data.add(new DataObject(15, 3,"类目"+15, new ArrayList<>()));
test_data.add(new DataObject(16, 3,"类目"+16, new ArrayList<>()));
test_data.add(new DataObject(17, 4,"类目"+17, new ArrayList<>()));
test_data.add(new DataObject(18, 4,"类目"+18, new ArrayList<>()));
test_data.add(new DataObject(19, 5,"类目"+19, new ArrayList<>()));
test_data.add(new DataObject(20, 5,"类目"+20, new ArrayList<>()));
test_data.add(new DataObject(21, 6,"类目"+21, new ArrayList<>()));
test_data.add(new DataObject(22, 6,"类目"+22, new ArrayList<>()));
test_data.add(new DataObject(23, 7,"类目"+23, new ArrayList<>()));
test_data.add(new DataObject(24, 8,"类目"+24, new ArrayList<>()));
test_data.add(new DataObject(25, 9,"类目"+25, new ArrayList<>()));
test_data.add(new DataObject(26, 10, "类目"+26,new ArrayList<>()));
test_data.add(new DataObject(27, 11, "类目"+27,new ArrayList<>()));
test_data.add(new DataObject(28, 12, "类目"+28,new ArrayList<>()));
test_data.add(new DataObject(29, 0, "类目"+29,new ArrayList<>()));
test_data.add(new DataObject(30, 0, "类目"+30,new ArrayList<>()));
try {
List<DataObject> build = new BuildTreeUtil<DataObject>()
.setId("id")
.setParentId("parentId")
.setChildrenName("children")
.setNodeList(test_data)
.build();
System.out.println("build:"+build);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* 包含父子关系的节点数据(id,parentId)
*/
List<T> nodeList;
/**
* 对象主键(对象 id)
*/
private String id = "id";
/**
* 对象的父id
*/
private String parentId = "parentId";
/**
* 子类字段名 (List<this> children)
*/
private String childrenName = "children";
public BuildTreeUtil<T> setNodeList(List<T> nodeList) {
this.nodeList = nodeList;
return this;
}
public BuildTreeUtil<T> setId(String id) {
this.id = id;
return this;
}
public BuildTreeUtil<T> setParentId(String parentId) {
this.parentId = parentId;
return this;
}
public BuildTreeUtil<T> setChildrenName(String childrenName) {
this.childrenName = childrenName;
return this;
}
/**
* 构建前端所需要树结构
*
* @return 树结构列表
*/
public List<T> build() throws NoSuchFieldException, IllegalAccessException {
List<T> returnList = new ArrayList<T>();
//所有对象的 id 列表
List<String> idList = nodeList.stream().map(data -> {
try {
Field declaredField = data.getClass().getDeclaredField(id);
declaredField.setAccessible(true);
return String.valueOf(declaredField.get(data));
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
for (T node : nodeList) {
String parentId;
Field declaredField = node.getClass().getDeclaredField(this.parentId);
declaredField.setAccessible(true);
parentId = String.valueOf(declaredField.get(node));
// 遍历所有部门,如果该部门的父类 id 不在部门列表中,说明这个部门在部门列表中是父部门
if (parentId != null && !idList.contains(parentId)) {
//自顶向下递归设置子部门,会直接改变入参node对象
recursionFn(nodeList, node);
//将设置好子部门的部门加入待返回列表
returnList.add(node);
}
}
//如果都是父部门,没有子部门则直接返回原始列表(平行树)
if (returnList.isEmpty()) {
returnList = nodeList;
}
//最后返回树状列表
return returnList;
}
/**
* 递归列表
*/
private void recursionFn(List<T> nodeList, T node) throws NoSuchFieldException, IllegalAccessException {
// 得到子节点列表
List<T> childList;
childList = getChildList(nodeList, node);
Field declaredField = node.getClass().getDeclaredField(childrenName);
declaredField.setAccessible(true);
declaredField.set(node, childList);
for (T tChild : childList) {
if (hasChild(nodeList, tChild)) {
recursionFn(nodeList, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<T> getChildList(List<T> nodeList, T node) throws NoSuchFieldException, IllegalAccessException {
List<T> childList = new ArrayList<T>();
for (T n : nodeList) {
Field nParentIdField = n.getClass().getDeclaredField(this.parentId);
Field nodeIdField = node.getClass().getDeclaredField(this.id);
nParentIdField.setAccessible(true);
nodeIdField.setAccessible(true);
String nParentId = String.valueOf(nParentIdField.get(n));
String nodeId = String.valueOf(nodeIdField.get(node));
if (StringUtils.isNotNull(nParentId) && nParentId.equals(nodeId)) //如果列表中有属于顶级节点的子节点则将该节点加入待返回列表中
{
childList.add(n);
}
}
return childList;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<T> nodeList, T node) throws NoSuchFieldException, IllegalAccessException {
return getChildList(nodeList, node).size() > 0;
}
}