重构:🔥 重构查询树列表相关 API,并抽取到后端 CRUD 公共组件中

1.基于 Hutool TreeUtil 重构查询树列表相关 API
2.抽取查询树列表 API 到后端 CRUD 公共组件中,大大简化部门管理和菜单管理部分代码
This commit is contained in:
2023-02-27 22:03:27 +08:00
parent 6723903c62
commit d4fd76dcc1
17 changed files with 210 additions and 248 deletions

View File

@@ -51,6 +51,10 @@ public @interface CrudRequestMapping {
* 分页
*/
PAGE,
/**
* 树列表
*/
TREE,
/**
* 列表
*/

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.charles7c.cnadmin.common.annotation;
import java.lang.annotation.*;
/**
* 树结构字段
*
* @see cn.hutool.core.lang.tree.TreeNodeConfig
* @author Charles7c
* @since 2023/2/26 23:50
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TreeField {
/**
* ID 字段名
*
* @return ID 字段名
*/
String value() default "key";
/**
* 父 ID 字段名
*
* @return 父 ID 字段名
*/
String parentIdKey() default "parentId";
/**
* 名称字段名
*
* @return 名称字段名
*/
String nameKey() default "title";
/**
* 排序字段名
*
* @return 排序字段名
*/
String weightKey() default "sort";
/**
* 子列表字段名
*
* @return 子列表字段名
*/
String childrenKey() default "children";
/**
* 递归深度(< 0 不限制)
*
* @return 递归深度
*/
int deep() default -1;
}

View File

@@ -30,6 +30,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import cn.hutool.core.lang.tree.Tree;
import top.charles7c.cnadmin.common.model.query.PageQuery;
import top.charles7c.cnadmin.common.model.query.SortQuery;
import top.charles7c.cnadmin.common.model.vo.PageDataVO;
@@ -74,6 +76,23 @@ public abstract class BaseController<S extends BaseService<V, D, Q, C>, V, D, Q,
return R.ok(pageDataVO);
}
/**
* 查询树列表
*
* @param query
* 查询条件
* @param sortQuery
* 排序查询条件
* @return 树列表信息
*/
@Operation(summary = "查询树列表")
@ResponseBody
@GetMapping("/tree")
protected R<List<Tree<Long>>> tree(@Validated Q query, @Validated SortQuery sortQuery) {
List<Tree<Long>> list = baseService.tree(query, sortQuery, false);
return R.ok(list);
}
/**
* 查询列表
*

View File

@@ -20,6 +20,8 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse;
import cn.hutool.core.lang.tree.Tree;
import top.charles7c.cnadmin.common.model.query.PageQuery;
import top.charles7c.cnadmin.common.model.query.SortQuery;
import top.charles7c.cnadmin.common.model.vo.PageDataVO;
@@ -51,6 +53,19 @@ public interface BaseService<V, D, Q, C extends BaseRequest> {
*/
PageDataVO<V> page(Q query, PageQuery pageQuery);
/**
* 查询树列表
*
* @param query
* 查询条件
* @param sortQuery
* 排序查询条件
* @param isSimple
* 是否为简单树结构(不包含基本树结构之外的扩展字段)
* @return 树列表信息
*/
List<Tree<Long>> tree(Q query, SortQuery sortQuery, boolean isSimple);
/**
* 查询列表
*

View File

@@ -16,8 +16,11 @@
package top.charles7c.cnadmin.common.base;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
@@ -41,15 +44,21 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import top.charles7c.cnadmin.common.annotation.TreeField;
import top.charles7c.cnadmin.common.model.query.PageQuery;
import top.charles7c.cnadmin.common.model.query.SortQuery;
import top.charles7c.cnadmin.common.model.vo.PageDataVO;
import top.charles7c.cnadmin.common.service.CommonUserService;
import top.charles7c.cnadmin.common.util.ExcelUtils;
import top.charles7c.cnadmin.common.util.ExceptionUtils;
import top.charles7c.cnadmin.common.util.ReflectUtils;
import top.charles7c.cnadmin.common.util.TreeUtils;
import top.charles7c.cnadmin.common.util.helper.QueryHelper;
import top.charles7c.cnadmin.common.util.validate.CheckUtils;
@@ -90,6 +99,40 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T, V, D, Q, C ext
return pageDataVO;
}
@Override
public List<Tree<Long>> tree(Q query, SortQuery sortQuery, boolean isSimple) {
List<V> list = this.list(query, sortQuery);
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
}
// 如果构建简单树结构,则不包含基本树结构之外的扩展字段
TreeNodeConfig treeNodeConfig = TreeUtils.DEFAULT_CONFIG;
TreeField treeField = voClass.getDeclaredAnnotation(TreeField.class);
if (!isSimple) {
// 根据 @TreeField 配置生成树结构配置
treeNodeConfig = TreeUtils.genTreeNodeConfig(treeField);
}
// 构建树
return TreeUtils.build(list, treeNodeConfig, (node, tree) -> {
// 转换器
tree.setId(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.value())));
tree.setParentId(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.parentIdKey())));
tree.setName(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.nameKey())));
tree.setWeight(ReflectUtil.invoke(node, StrUtil.genGetter(treeField.weightKey())));
if (!isSimple) {
Field[] fieldArr = ReflectUtils.getNonStaticFields(voClass);
List<Field> fieldList = Arrays.stream(fieldArr)
.filter(f -> !StrUtil.containsAnyIgnoreCase(f.getName(), treeField.value(), treeField.parentIdKey(),
treeField.nameKey(), treeField.weightKey(), treeField.childrenKey()))
.collect(Collectors.toList());
fieldList
.forEach(f -> tree.putExtra(f.getName(), ReflectUtil.invoke(node, StrUtil.genGetter(f.getName()))));
}
});
}
@Override
public List<V> list(Q query, SortQuery sortQuery) {
List<V> list = this.list(query, sortQuery, voClass);

View File

@@ -29,6 +29,9 @@ import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.lang.tree.parser.NodeParser;
import cn.hutool.core.util.ReflectUtil;
import top.charles7c.cnadmin.common.annotation.TreeField;
import top.charles7c.cnadmin.common.util.validate.CheckUtils;
/**
* 树工具类
*
@@ -39,7 +42,7 @@ import cn.hutool.core.util.ReflectUtil;
public class TreeUtils {
/** 默认属性配置对象(根据前端树结构灵活调整名称) */
private static final TreeNodeConfig DEFAULT_CONFIG =
public static final TreeNodeConfig DEFAULT_CONFIG =
TreeNodeConfig.DEFAULT_CONFIG.setNameKey("title").setIdKey("key").setWeightKey("sort");
/**
@@ -53,13 +56,46 @@ public class TreeUtils {
* 源数据集合
* @param nodeParser
* 转换器
* @return List
* @return List 树列表
*/
public static <T, E> List<Tree<E>> build(List<T> list, NodeParser<T, E> nodeParser) {
return build(list, DEFAULT_CONFIG, nodeParser);
}
/**
* 树构建
*
* @param <T>
* 转换的实体 为数据源里的对象类型
* @param <E>
* ID类型
* @param list
* 源数据集合
* @param treeNodeConfig
* 配置
* @param nodeParser
* 转换器
* @return List 树列表
*/
public static <T, E> List<Tree<E>> build(List<T> list, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
}
E parentId = (E)ReflectUtil.getFieldValue(list.get(0), DEFAULT_CONFIG.getParentIdKey());
return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
E parentId = (E)ReflectUtil.getFieldValue(list.get(0), treeNodeConfig.getParentIdKey());
return TreeUtil.build(list, parentId, treeNodeConfig, nodeParser);
}
/**
* 根据 @TreeField 配置生成树结构配置
*
* @param treeField
* 树结构字段注解
* @return 树结构配置
*/
public static TreeNodeConfig genTreeNodeConfig(TreeField treeField) {
CheckUtils.throwIfNull(treeField, "请添加并配置 @TreeField 树结构信息");
return new TreeNodeConfig().setIdKey(treeField.value()).setParentIdKey(treeField.parentIdKey())
.setNameKey(treeField.nameKey()).setWeightKey(treeField.weightKey()).setChildrenKey(treeField.childrenKey())
.setDeep(treeField.deep() < 0 ? null : treeField.deep());
}
}