From d4fd76dcc1c0bac6b8098899acf6475ce754f2b7 Mon Sep 17 00:00:00 2001
From: Charles7c <charles7c@126.com>
Date: Mon, 27 Feb 2023 22:03:27 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A:fire:=20=E9=87=8D?=
 =?UTF-8?q?=E6=9E=84=E6=9F=A5=E8=AF=A2=E6=A0=91=E5=88=97=E8=A1=A8=E7=9B=B8?=
 =?UTF-8?q?=E5=85=B3=20API=EF=BC=8C=E5=B9=B6=E6=8A=BD=E5=8F=96=E5=88=B0?=
 =?UTF-8?q?=E5=90=8E=E7=AB=AF=20CRUD=20=E5=85=AC=E5=85=B1=E7=BB=84?=
 =?UTF-8?q?=E4=BB=B6=E4=B8=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

1.基于 Hutool TreeUtil 重构查询树列表相关 API
2.抽取查询树列表 API 到后端 CRUD 公共组件中,大大简化部门管理和菜单管理部分代码
---
 .../common/annotation/CrudRequestMapping.java |  4 +
 .../cnadmin/common/annotation/TreeField.java  | 74 +++++++++++++++++++
 .../cnadmin/common/base/BaseController.java   | 19 +++++
 .../cnadmin/common/base/BaseService.java      | 15 ++++
 .../cnadmin/common/base/BaseServiceImpl.java  | 43 +++++++++++
 .../cnadmin/common/util/TreeUtils.java        | 44 ++++++++++-
 .../cnadmin/system/model/vo/DeptVO.java       | 10 +--
 .../cnadmin/system/model/vo/MenuVO.java       | 10 +--
 .../cnadmin/system/service/DeptService.java   | 25 +------
 .../cnadmin/system/service/MenuService.java   | 25 +------
 .../system/service/impl/DeptServiceImpl.java  | 70 ------------------
 .../system/service/impl/MenuServiceImpl.java  | 71 +-----------------
 continew-admin-ui/src/api/system/dept.ts      |  2 +-
 continew-admin-ui/src/api/system/menu.ts      |  2 +-
 .../controller/common/CommonController.java   |  8 +-
 .../controller/system/DeptController.java     | 18 +----
 .../controller/system/MenuController.java     | 18 +----
 17 files changed, 210 insertions(+), 248 deletions(-)
 create mode 100644 continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/TreeField.java

diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/CrudRequestMapping.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/CrudRequestMapping.java
index d2fcaf25..a6514529 100644
--- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/CrudRequestMapping.java
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/CrudRequestMapping.java
@@ -51,6 +51,10 @@ public @interface CrudRequestMapping {
          * 分页
          */
         PAGE,
+        /**
+         * 树列表
+         */
+        TREE,
         /**
          * 列表
          */
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/TreeField.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/TreeField.java
new file mode 100644
index 00000000..4c260062
--- /dev/null
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/TreeField.java
@@ -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;
+}
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseController.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseController.java
index 27776845..c9b31c37 100644
--- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseController.java
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseController.java
@@ -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);
+    }
+
     /**
      * 查询列表
      *
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseService.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseService.java
index 069a3ba5..ab3bee9f 100644
--- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseService.java
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseService.java
@@ -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);
+
     /**
      * 查询列表
      *
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseServiceImpl.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseServiceImpl.java
index 58735881..4df6c64c 100644
--- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseServiceImpl.java
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/base/BaseServiceImpl.java
@@ -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);
diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TreeUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TreeUtils.java
index ff59cd28..d5d372a1 100644
--- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TreeUtils.java
+++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/TreeUtils.java
@@ -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());
     }
 }
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptVO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptVO.java
index 35e43842..f7855d83 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptVO.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/DeptVO.java
@@ -16,13 +16,12 @@
 
 package top.charles7c.cnadmin.system.model.vo;
 
-import java.util.List;
-
 import lombok.Data;
 import lombok.experimental.Accessors;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 
+import top.charles7c.cnadmin.common.annotation.TreeField;
 import top.charles7c.cnadmin.common.base.BaseVO;
 import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
 
@@ -34,6 +33,7 @@ import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
  */
 @Data
 @Accessors(chain = true)
+@TreeField(value = "deptId", nameKey = "deptName", weightKey = "deptSort")
 @Schema(description = "部门信息")
 public class DeptVO extends BaseVO {
 
@@ -74,10 +74,4 @@ public class DeptVO extends BaseVO {
      */
     @Schema(description = "状态(1启用 2禁用)")
     private DisEnableStatusEnum status;
-
-    /**
-     * 子部门列表
-     */
-    @Schema(description = "子部门列表")
-    private List<DeptVO> children;
 }
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/MenuVO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/MenuVO.java
index a8c61341..c40a87b0 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/MenuVO.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/vo/MenuVO.java
@@ -16,13 +16,12 @@
 
 package top.charles7c.cnadmin.system.model.vo;
 
-import java.util.List;
-
 import lombok.Data;
 import lombok.experimental.Accessors;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 
+import top.charles7c.cnadmin.common.annotation.TreeField;
 import top.charles7c.cnadmin.common.base.BaseVO;
 import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
 import top.charles7c.cnadmin.common.enums.MenuTypeEnum;
@@ -35,6 +34,7 @@ import top.charles7c.cnadmin.common.enums.MenuTypeEnum;
  */
 @Data
 @Accessors(chain = true)
+@TreeField(value = "menuId", nameKey = "menuName", weightKey = "menuSort")
 @Schema(description = "菜单信息")
 public class MenuVO extends BaseVO {
 
@@ -123,10 +123,4 @@ public class MenuVO extends BaseVO {
      */
     @Schema(description = "状态(1启用 2禁用)")
     private DisEnableStatusEnum status;
-
-    /**
-     * 子菜单列表
-     */
-    @Schema(description = "子菜单列表")
-    private List<MenuVO> children;
 }
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java
index 907f134b..b091abc9 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/DeptService.java
@@ -16,10 +16,6 @@
 
 package top.charles7c.cnadmin.system.service;
 
-import java.util.List;
-
-import cn.hutool.core.lang.tree.Tree;
-
 import top.charles7c.cnadmin.common.base.BaseService;
 import top.charles7c.cnadmin.system.model.query.DeptQuery;
 import top.charles7c.cnadmin.system.model.request.DeptRequest;
@@ -32,23 +28,4 @@ import top.charles7c.cnadmin.system.model.vo.DeptVO;
  * @author Charles7c
  * @since 2023/1/22 17:54
  */
-public interface DeptService extends BaseService<DeptVO, DeptDetailVO, DeptQuery, DeptRequest> {
-
-    /**
-     * 构建树
-     *
-     * @param list
-     *            原始列表数据
-     * @return 树列表
-     */
-    List<DeptVO> buildListTree(List<DeptVO> list);
-
-    /**
-     * 构建树
-     *
-     * @param list
-     *            原始列表数据
-     * @return 树列表
-     */
-    List<Tree<Long>> buildTree(List<DeptVO> list);
-}
+public interface DeptService extends BaseService<DeptVO, DeptDetailVO, DeptQuery, DeptRequest> {}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/MenuService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/MenuService.java
index 033949de..b6cd8307 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/MenuService.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/MenuService.java
@@ -16,10 +16,6 @@
 
 package top.charles7c.cnadmin.system.service;
 
-import java.util.List;
-
-import cn.hutool.core.lang.tree.Tree;
-
 import top.charles7c.cnadmin.common.base.BaseService;
 import top.charles7c.cnadmin.system.model.query.MenuQuery;
 import top.charles7c.cnadmin.system.model.request.MenuRequest;
@@ -31,23 +27,4 @@ import top.charles7c.cnadmin.system.model.vo.MenuVO;
  * @author Charles7c
  * @since 2023/2/15 20:30
  */
-public interface MenuService extends BaseService<MenuVO, MenuVO, MenuQuery, MenuRequest> {
-
-    /**
-     * 构建树
-     *
-     * @param list
-     *            原始列表数据
-     * @return 树列表
-     */
-    List<MenuVO> buildListTree(List<MenuVO> list);
-
-    /**
-     * 构建树
-     *
-     * @param list
-     *            原始列表数据
-     * @return 树列表
-     */
-    List<Tree<Long>> buildTree(List<MenuVO> list);
-}
+public interface MenuService extends BaseService<MenuVO, MenuVO, MenuQuery, MenuRequest> {}
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/DeptServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/DeptServiceImpl.java
index d659b4bd..d098dff6 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/DeptServiceImpl.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/DeptServiceImpl.java
@@ -16,11 +16,7 @@
 
 package top.charles7c.cnadmin.system.service.impl;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
 
 import javax.annotation.Resource;
 
@@ -29,13 +25,9 @@ import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.tree.Tree;
-
 import top.charles7c.cnadmin.common.base.BaseServiceImpl;
 import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
 import top.charles7c.cnadmin.common.util.ExceptionUtils;
-import top.charles7c.cnadmin.common.util.TreeUtils;
 import top.charles7c.cnadmin.common.util.validate.CheckUtils;
 import top.charles7c.cnadmin.system.mapper.DeptMapper;
 import top.charles7c.cnadmin.system.model.entity.DeptDO;
@@ -90,68 +82,6 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptVO,
         super.lambdaUpdate().in(DeptDO::getParentId, ids).remove();
     }
 
-    @Override
-    public List<DeptVO> buildListTree(List<DeptVO> list) {
-        if (CollUtil.isEmpty(list)) {
-            return Collections.emptyList();
-        }
-
-        // 去除重复子部门列表
-        List<DeptVO> deDuplicationList = deDuplication(list);
-        return deDuplicationList.stream().map(d -> d.setChildren(this.getChildren(d, list)))
-            .collect(Collectors.toList());
-    }
-
-    /**
-     * 数据去重(去除重复子部门列表)
-     *
-     * @param list
-     *            部门列表
-     * @return 去重后部门列表
-     */
-    private List<DeptVO> deDuplication(List<DeptVO> list) {
-        List<DeptVO> deDuplicationList = new ArrayList<>();
-        for (DeptVO outer : list) {
-            boolean flag = true;
-            for (DeptVO inner : list) {
-                // 忽略重复子列表
-                if (Objects.equals(inner.getDeptId(), outer.getParentId())) {
-                    flag = false;
-                    break;
-                }
-            }
-
-            if (flag) {
-                deDuplicationList.add(outer);
-            }
-        }
-        return deDuplicationList;
-    }
-
-    /**
-     * 获取指定部门的子部门列表
-     *
-     * @param deptVO
-     *            指定部门
-     * @param list
-     *            部门列表
-     * @return 子部门列表
-     */
-    private List<DeptVO> getChildren(DeptVO deptVO, List<DeptVO> list) {
-        return list.stream().filter(d -> Objects.equals(d.getParentId(), deptVO.getDeptId()))
-            .map(d -> d.setChildren(this.getChildren(d, list))).collect(Collectors.toList());
-    }
-
-    @Override
-    public List<Tree<Long>> buildTree(List<DeptVO> list) {
-        return TreeUtils.build(list, (d, tree) -> {
-            tree.setId(d.getDeptId());
-            tree.setName(d.getDeptName());
-            tree.setParentId(d.getParentId());
-            tree.setWeight(d.getDeptSort());
-        });
-    }
-
     /**
      * 检查名称是否存在
      *
diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/MenuServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/MenuServiceImpl.java
index a32608fe..0f4c2b45 100644
--- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/MenuServiceImpl.java
+++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/MenuServiceImpl.java
@@ -16,23 +16,15 @@
 
 package top.charles7c.cnadmin.system.service.impl;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
 
 import lombok.RequiredArgsConstructor;
 
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.tree.Tree;
-
 import top.charles7c.cnadmin.common.base.BaseServiceImpl;
 import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
-import top.charles7c.cnadmin.common.util.TreeUtils;
 import top.charles7c.cnadmin.common.util.validate.CheckUtils;
 import top.charles7c.cnadmin.system.mapper.MenuMapper;
 import top.charles7c.cnadmin.system.model.entity.MenuDO;
@@ -71,6 +63,7 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuVO,
         boolean isExists = this.checkNameExists(menuName, request.getParentId(), request.getMenuId());
         CheckUtils.throwIf(() -> isExists, String.format("修改失败,'%s'已存在", menuName));
 
+        // 更新信息
         super.update(request);
     }
 
@@ -81,68 +74,6 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuVO,
         super.lambdaUpdate().in(MenuDO::getParentId, ids).remove();
     }
 
-    @Override
-    public List<MenuVO> buildListTree(List<MenuVO> list) {
-        if (CollUtil.isEmpty(list)) {
-            return Collections.emptyList();
-        }
-
-        // 去除重复子菜单列表
-        List<MenuVO> deDuplicationList = deDuplication(list);
-        return deDuplicationList.stream().map(m -> m.setChildren(this.getChildren(m, list)))
-            .collect(Collectors.toList());
-    }
-
-    /**
-     * 数据去重(去除重复子菜单列表)
-     *
-     * @param list
-     *            菜单列表
-     * @return 去重后菜单列表
-     */
-    private List<MenuVO> deDuplication(List<MenuVO> list) {
-        List<MenuVO> deDuplicationList = new ArrayList<>();
-        for (MenuVO outer : list) {
-            boolean flag = true;
-            for (MenuVO inner : list) {
-                // 忽略重复子列表
-                if (Objects.equals(inner.getMenuId(), outer.getParentId())) {
-                    flag = false;
-                    break;
-                }
-            }
-
-            if (flag) {
-                deDuplicationList.add(outer);
-            }
-        }
-        return deDuplicationList;
-    }
-
-    /**
-     * 获取指定菜单的子菜单列表
-     *
-     * @param menuVO
-     *            指定菜单
-     * @param list
-     *            菜单列表
-     * @return 子菜单列表
-     */
-    private List<MenuVO> getChildren(MenuVO menuVO, List<MenuVO> list) {
-        return list.stream().filter(m -> Objects.equals(m.getParentId(), menuVO.getMenuId()))
-            .map(m -> m.setChildren(this.getChildren(m, list))).collect(Collectors.toList());
-    }
-
-    @Override
-    public List<Tree<Long>> buildTree(List<MenuVO> list) {
-        return TreeUtils.build(list, (m, tree) -> {
-            tree.setId(m.getMenuId());
-            tree.setName(m.getMenuName());
-            tree.setParentId(m.getParentId());
-            tree.setWeight(m.getMenuSort());
-        });
-    }
-
     /**
      * 检查名称是否存在
      *
diff --git a/continew-admin-ui/src/api/system/dept.ts b/continew-admin-ui/src/api/system/dept.ts
index d420db24..0a36d3b9 100644
--- a/continew-admin-ui/src/api/system/dept.ts
+++ b/continew-admin-ui/src/api/system/dept.ts
@@ -24,7 +24,7 @@ export interface DeptParam {
 }
 
 export function listDept(params: DeptParam) {
-  return axios.get<DeptRecord[]>(`${BASE_URL}/list`, {
+  return axios.get<DeptRecord[]>(`${BASE_URL}/tree`, {
     params,
     paramsSerializer: (obj) => {
       return qs.stringify(obj);
diff --git a/continew-admin-ui/src/api/system/menu.ts b/continew-admin-ui/src/api/system/menu.ts
index 38c16a94..c88c046e 100644
--- a/continew-admin-ui/src/api/system/menu.ts
+++ b/continew-admin-ui/src/api/system/menu.ts
@@ -32,7 +32,7 @@ export interface MenuParam {
 }
 
 export function listMenu(params: MenuParam) {
-  return axios.get<MenuRecord[]>(`${BASE_URL}/list`, {
+  return axios.get<MenuRecord[]>(`${BASE_URL}/tree`, {
     params,
     paramsSerializer: (obj) => {
       return qs.stringify(obj);
diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/common/CommonController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/common/CommonController.java
index a2fecada..e099a820 100644
--- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/common/CommonController.java
+++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/common/CommonController.java
@@ -40,8 +40,6 @@ import top.charles7c.cnadmin.system.model.query.DeptQuery;
 import top.charles7c.cnadmin.system.model.query.MenuQuery;
 import top.charles7c.cnadmin.system.model.query.PostQuery;
 import top.charles7c.cnadmin.system.model.query.RoleQuery;
-import top.charles7c.cnadmin.system.model.vo.DeptVO;
-import top.charles7c.cnadmin.system.model.vo.MenuVO;
 import top.charles7c.cnadmin.system.model.vo.PostVO;
 import top.charles7c.cnadmin.system.model.vo.RoleVO;
 import top.charles7c.cnadmin.system.service.DeptService;
@@ -72,16 +70,14 @@ public class CommonController {
     @Operation(summary = "查询部门树", description = "查询树结构的部门列表")
     @GetMapping("/tree/dept")
     public R<List<Tree<Long>>> listDeptTree(@Validated DeptQuery query, @Validated SortQuery sortQuery) {
-        List<DeptVO> list = deptService.list(query, sortQuery);
-        List<Tree<Long>> treeList = deptService.buildTree(list);
+        List<Tree<Long>> treeList = deptService.tree(query, sortQuery, true);
         return R.ok(treeList);
     }
 
     @Operation(summary = "查询菜单树", description = "查询树结构的菜单列表")
     @GetMapping("/tree/menu")
     public R<List<Tree<Long>>> listMenuTree(@Validated MenuQuery query, @Validated SortQuery sortQuery) {
-        List<MenuVO> list = menuService.list(query, sortQuery);
-        List<Tree<Long>> treeList = menuService.buildTree(list);
+        List<Tree<Long>> treeList = menuService.tree(query, sortQuery, true);
         return R.ok(treeList);
     }
 
diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java
index 3c0628b7..3016a085 100644
--- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java
+++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/DeptController.java
@@ -18,18 +18,12 @@ package top.charles7c.cnadmin.webapi.controller.system;
 
 import static top.charles7c.cnadmin.common.annotation.CrudRequestMapping.Api;
 
-import java.util.List;
-
-import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 
-import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import top.charles7c.cnadmin.common.annotation.CrudRequestMapping;
 import top.charles7c.cnadmin.common.base.BaseController;
-import top.charles7c.cnadmin.common.model.query.SortQuery;
-import top.charles7c.cnadmin.common.model.vo.R;
 import top.charles7c.cnadmin.system.model.query.DeptQuery;
 import top.charles7c.cnadmin.system.model.request.DeptRequest;
 import top.charles7c.cnadmin.system.model.vo.DeptDetailVO;
@@ -44,13 +38,5 @@ import top.charles7c.cnadmin.system.service.DeptService;
  */
 @Tag(name = "部门管理 API")
 @RestController
-@CrudRequestMapping(value = "/system/dept", api = {Api.LIST, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
-public class DeptController extends BaseController<DeptService, DeptVO, DeptDetailVO, DeptQuery, DeptRequest> {
-
-    @Override
-    @Operation(summary = "查询列表树")
-    public R<List<DeptVO>> list(@Validated DeptQuery query, @Validated SortQuery sortQuery) {
-        List<DeptVO> list = baseService.list(query, sortQuery);
-        return R.ok(baseService.buildListTree(list));
-    }
-}
+@CrudRequestMapping(value = "/system/dept", api = {Api.TREE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
+public class DeptController extends BaseController<DeptService, DeptVO, DeptDetailVO, DeptQuery, DeptRequest> {}
diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/MenuController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/MenuController.java
index 42d4acb8..eff0e89d 100644
--- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/MenuController.java
+++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/system/MenuController.java
@@ -18,18 +18,12 @@ package top.charles7c.cnadmin.webapi.controller.system;
 
 import static top.charles7c.cnadmin.common.annotation.CrudRequestMapping.Api;
 
-import java.util.List;
-
-import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 
-import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RestController;
 
 import top.charles7c.cnadmin.common.annotation.CrudRequestMapping;
 import top.charles7c.cnadmin.common.base.BaseController;
-import top.charles7c.cnadmin.common.model.query.SortQuery;
-import top.charles7c.cnadmin.common.model.vo.R;
 import top.charles7c.cnadmin.system.model.query.MenuQuery;
 import top.charles7c.cnadmin.system.model.request.MenuRequest;
 import top.charles7c.cnadmin.system.model.vo.MenuVO;
@@ -43,13 +37,5 @@ import top.charles7c.cnadmin.system.service.MenuService;
  */
 @Tag(name = "菜单管理 API")
 @RestController
-@CrudRequestMapping(value = "/system/menu", api = {Api.LIST, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
-public class MenuController extends BaseController<MenuService, MenuVO, MenuVO, MenuQuery, MenuRequest> {
-
-    @Override
-    @Operation(summary = "查询列表树")
-    public R<List<MenuVO>> list(@Validated MenuQuery query, @Validated SortQuery sortQuery) {
-        List<MenuVO> list = baseService.list(query, sortQuery);
-        return R.ok(baseService.buildListTree(list));
-    }
-}
+@CrudRequestMapping(value = "/system/menu", api = {Api.TREE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
+public class MenuController extends BaseController<MenuService, MenuVO, MenuVO, MenuQuery, MenuRequest> {}