From 2f71b4be3c8b41af6a2675761c70b83465949588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 14:58:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(backend-java):=20=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=8E=A5=E5=8F=A3=E6=94=B9=E4=B8=BA=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E8=8F=9C=E5=8D=95=E6=A0=91=E5=B9=B6=E9=80=82=E9=85=8D?= =?UTF-8?q?=20admin-vue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 菜单接口 /api/admin/current-user/menus 由扁平 route 列表改为含父子 层级的菜单树;权限标识用 column_key,页面地址用 route 单独下发; /admin 与 / 入口重定向到 admin-vue。 --- .../controller/AdminConsoleController.java | 85 ++++++++++++++++--- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java index b8e775f1..d672f08f 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/admin/controller/AdminConsoleController.java @@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -35,13 +37,13 @@ public class AdminConsoleController { @GetMapping("/") @Operation(summary = "根路径跳管理后台", hidden = true) public String root() { - return "redirect:/admin.html"; + return "redirect:/admin-vue/"; } @GetMapping("/admin") @Operation(summary = "管理后台入口", hidden = true) public String adminPage() { - return "redirect:/admin.html"; + return "redirect:/admin-vue/"; } @GetMapping("/login") @@ -68,21 +70,84 @@ public class AdminConsoleController { } @GetMapping("/api/admin/current-user/menus") - @Operation(summary = "当前登录管理员的可见菜单(扁平 route_path/name 列表)") + @Operation(summary = "当前登录管理员的可见后台菜单树") public ApiResponse> currentUserMenus(HttpServletRequest request) { AdminUserEntity operator = adminAuthSupport.requireAdminOrInternal(request); List menus = permissionMenuService.getUserColumnPermissions( operator, operator.getId(), PermissionMenuService.MENU_TYPE_ADMIN); - List> items = new ArrayList<>(menus.size()); + return ApiResponse.success(Map.of("items", buildAdminMenuTree(menus))); + } + + /** + * 将 columns 的有效权限项转换为新版后台菜单树。权限 key 使用 column_key, + * 页面地址单独使用 route;这样后续调整 Vue URL 时不会改变授权标识。 + */ + private List> buildAdminMenuTree(List menus) { + Map> nodes = new HashMap<>(); for (PermissionMenuItemVo menu : menus) { - if (menu.getRoutePath() == null || menu.getRoutePath().isBlank()) { + if (menu.getId() == null) { continue; } - Map item = new LinkedHashMap<>(); - item.put("route_path", menu.getRoutePath()); - item.put("name", menu.getName() == null ? menu.getRoutePath() : menu.getName()); - items.add(item); + Map node = new LinkedHashMap<>(); + node.put("key", menu.getColumnKey() == null ? String.valueOf(menu.getId()) : menu.getColumnKey()); + node.put("name", menu.getName() == null ? "未命名菜单" : menu.getName()); + node.put("sort", menu.getSortOrder() == null ? 0 : menu.getSortOrder()); + String route = adminRoute(menu.getColumnKey(), menu.getRoutePath()); + if (route != null && !route.isBlank() && !route.startsWith("group-")) { + node.put("route", "/" + route.replaceFirst("^/", "")); + } + node.put("children", new ArrayList>()); + nodes.put(menu.getId(), node); } - return ApiResponse.success(Map.of("items", items)); + List> roots = new ArrayList<>(); + for (PermissionMenuItemVo menu : menus) { + Map node = nodes.get(menu.getId()); + if (node == null) continue; + @SuppressWarnings("unchecked") + List> children = (List>) node.get("children"); + if (menu.getParentId() != null && nodes.containsKey(menu.getParentId())) { + @SuppressWarnings("unchecked") + List> parentChildren = (List>) nodes.get(menu.getParentId()).get("children"); + parentChildren.add(node); + } else { + roots.add(node); + } + } + Comparator> order = Comparator + .comparingInt((Map node) -> ((Number) node.get("sort")).intValue()) + .thenComparing(node -> String.valueOf(node.get("key"))); + sortMenuTree(roots, order); + stripSort(roots); + return roots; + } + + private void sortMenuTree(List> nodes, + Comparator> order) { + nodes.sort(order); + for (Map node : nodes) { + @SuppressWarnings("unchecked") + List> children = (List>) node.get("children"); + sortMenuTree(children, order); + } + } + + private void stripSort(List> nodes) { + for (Map node : nodes) { + node.remove("sort"); + @SuppressWarnings("unchecked") + List> children = (List>) node.get("children"); + if (children.isEmpty()) node.remove("children"); + else stripSort(children); + } + } + + private String adminRoute(String columnKey, String storedRoute) { + if (columnKey == null) return storedRoute; + return switch (columnKey) { + case "admin_users" -> "account/users"; + case "admin_columns" -> "account/menus"; + case "admin_group_manage" -> "account/groups"; + default -> storedRoute; + }; } }