/**
* @author yqlin
* @description 树结构实体通用工具类
* @tip pid 和 id 的类型必须一致 排序字段最好是数值类型
*/
@Slf4j
public final class TreeUtil {
/**
* Map<FieldType String> ---> Map<[注解属性类型,[字段名]>
*/
private static Map<FieldType, String> fieldsMap = new HashMap<>(8);
/**
* @param list 所有列表
* @param rootNode 根节点
* @param treeClazz 树状元素类型
* @param <E> 传入元素
*
* @return 树状结构实体
*/
public static <E> E toTree(List<E> list, E rootNode, Class<E> treeClazz) {
if (fieldsMap.size() < 6 >> 1) {
fieldsMap = getFieldsMap(treeClazz);
}
Map<Object, E> map = new HashMap<>(2);
Object id = ReflectUtil.getFieldValue(rootNode, fieldsMap.get(FieldType.ID));
Object pid = ReflectUtil.getFieldValue(rootNode, fieldsMap.get(FieldType.PID));
map.put(id, rootNode);
for (E childNode : list) {
buildChildNodes(pid, map, childNode, false, false);
}
return map.get(id);
}
/**
* 引用写法 绕过递归
*
* @param list 所有列表
* @param pid 父级id
* @param treeClazz 树状元素类型
* @param <E> 传入元素
*
* @return 树状结构集合
*/
public static <E> List<E> toTreeList(List<E> list, Object pid, Class<E> treeClazz) {
if (fieldsMap.size() < 6 >> 1) {
fieldsMap = getFieldsMap(treeClazz);
}
TreeEntity treeEntity = treeClazz.getAnnotation(TreeEntity.class);
SortType sortType = treeEntity.sortType();
String sortField = fieldsMap.get(FieldType.SORT);
boolean sort = false;
boolean asc = false;
if (StringUtils.isNotBlank(sortField)) {
sort = true;
if (sortType == SortType.ASC) {
asc = true;
}
}
Map<Object, E> map = new HashMap<>(2);
List<E> rootNodes = new ArrayList<>();
for (E o : list) {
Object tPid = ReflectUtil.getFieldValue(o, fieldsMap.get(FieldType.PID));
if (tPid.equals(pid)) {
rootNodes.add(o);
map.put(pid, o);
}
}
if (sort) {
treeNodesSort(rootNodes, asc);
}
for (E childNode : list) {
if (childNode != null) {
buildChildNodes(pid, map, childNode, sort, asc);
}
}
return rootNodes;
}
/**
* 集合排序
*
* @param nodes 节点集合
* @param asc 是否升序
* @param <E> 类型
*/
private static <E> void treeNodesSort(List<E> nodes, boolean asc) {
if (asc) {
nodes.sort(Comparator.comparing(e -> Integer.valueOf(ReflectUtil.getFieldValue(e, fieldsMap.get(FieldType.SORT)).toString())));
} else {
nodes.sort(Comparator.comparing(e -> Integer.valueOf(ReflectUtil.getFieldValue(e, fieldsMap.get(FieldType.SORT)).toString())).reversed());
}
}
/**
* 构建子节点集合
*/
private static <E> void buildChildNodes(Object pid, Map<Object, E> map, E childNode, boolean sort, boolean asc) {
Object tId = ReflectUtil.getFieldValue(childNode, fieldsMap.get(FieldType.ID));
map.put(tId, childNode);
Object tPid = ReflectUtil.getFieldValue(childNode, fieldsMap.get(FieldType.PID));
if (!tPid.equals(pid)) {
E parentNode = map.get(tPid);
List<E> tChildNodes = (List<E>) ReflectUtil.getFieldValue(parentNode, fieldsMap.get(FieldType.CHILD));
if (tChildNodes == null) {
tChildNodes = new ArrayList<>();
}
tChildNodes.add(childNode);
if (sort) {
treeNodesSort(tChildNodes, asc);
}
if (parentNode != null) {
ReflectUtil.setFieldValue(parentNode, fieldsMap.get(FieldType.CHILD), tChildNodes);
}
}
}
/**
* 获取 Field Map
*
* @param clazz 类名
*
* @return Map<FieldType String> ---> Map<[注解属性类型,[字段名]>
*/
private static <E> Map<FieldType, String> getFieldsMap(Class<E> clazz) {
if (!clazz.isAnnotationPresent(TreeEntity.class)) {
throw new RuntimeException("缺少注解: @TreeEntity");
}
TreeEntity treeEntity = clazz.getAnnotation(TreeEntity.class);
IdType idType = treeEntity.idType();
Stream.of(clazz.getDeclaredFields()).forEach(field -> {
if (field.isAnnotationPresent(TreeField.class)) {
TreeField treeField = field.getAnnotation(TreeField.class);
switch (treeField.fieldType()) {
case ID:
if (!idType.getClazz().equals(field.getType())) {
throw new RuntimeException("ID类型与idType类型不一致");
}
fieldsMap.put(FieldType.ID, field.getName());
break;
case PID:
if (!idType.getClazz().equals(field.getType())) {
throw new RuntimeException("PID类型与idType类型不一致");
}
fieldsMap.put(FieldType.PID, field.getName());
break;
case CHILD:
fieldsMap.put(FieldType.CHILD, field.getName());
break;
case SORT:
fieldsMap.put(FieldType.SORT, field.getName());
break;
default:
break;
}
}
});
if (fieldsMap.size() < 6 >> 1) {
throw new RuntimeException("缺少某一个属性: FieldType.ID | FieldType.PID | FieldType.CHILD)");
}
return fieldsMap;
}
}
/**
* @author yqlin
*/
public enum FieldType {
/**
* 主Id
*/
ID,
/**
* 父ID
*/
PID,
/**
* 子数据
*/
CHILD,
/**
* 排序字段
*/
SORT
}
@AllArgsConstructor
@Getter
public enum IdType {
/**
* 支持三种数值类型
*/
STRING(String.class),
LONG(Long.class),
INTEGER(Integer.class),
;
private Class<?> clazz;
}
public enum SortType {
/**
* 支持升序和降序
*/
ASC,
DESC,
;
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TreeEntity {
IdType idType();
SortType sortType() default SortType.ASC;
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TreeField {
/**
* 所属类型
* 三种: ID,PID,CHILD
*/
FieldType fieldType();
}
用法: 支持所在层级节点排序 以及构建树结构集合Json字符串
@ToString
@Data
@Accessors(chain = true)
@TreeEntity(idType = IdType.INTEGER)
public class ProdCateOaTreeVO implements Serializable {
@TreeField(fieldType = FieldType.ID)
private Integer value;
@TreeField(fieldType = FieldType.PID)
private Integer parentValue;
private String title;
private boolean checked;
@TreeField(fieldType = FieldType.SORT)
private Integer sort;
@TreeField(fieldType = FieldType.CHILD)
private List<ProdCateOaTreeVO> data;
}
voList = TreeUtil.toTreeList(treeVoList, Constants.PARENT_ID, ProdCateOaTreeVO.class);