基于 Spring Boot 實現動態路由加載:從數據庫到前端菜單的完整方案

在后臺管理系統中,不同用戶角色往往擁有不同的操作權限,對應的菜單展示也需動態調整。動態路由加載正是解決這一問題的核心方案 —— 根據登錄用戶的權限,從數據庫查詢其可訪問的菜單,封裝成前端所需的路由結構并返回。本文將詳細講解如何基于 Spring Boot + MyBatis-Plus 實現這一功能,包含完整代碼與實現思路。

一、需求與實現思路

動態路由加載的核心目標是:根據登錄用戶的權限,動態生成其可訪問的菜單路由,最終返回給前端用于渲染側邊欄。整體實現思路分為四步:

  1. 獲取當前登錄用戶信息:通過 Session 獲取已登錄用戶的 ID(userId);
  2. 查詢用戶角色名稱:基于 userId,通過user_role表(用戶 - 角色關聯)和role表(角色表)聯查,獲取用戶的角色名稱(如 “超級管理員”);
  3. 查詢用戶權限菜單:基于 userId,通過user_rolerole_menu(角色 - 菜單關聯)、menu(菜單表)三表聯查,獲取用戶可訪問的所有菜單;
  4. 封裝路由結構:將數據庫查詢的菜單列表,轉換為前端所需的路由格式(包含一級菜單、二級菜單、路由元信息等)。

二、核心表結構設計

實現動態路由的前提是合理的表結構設計,需包含 3 張核心表(用戶 - 角色 - 菜單的關聯關系):

  • user:用戶表(存儲用戶 ID、用戶名等);
  • role:角色表(存儲角色 ID、角色名稱,如 “超級管理員”);
  • menu:菜單表(存儲菜單 ID、父級 ID、路徑、組件路徑等路由信息);
  • user_role:用戶 - 角色關聯表(多對多關系);
  • role_menu:角色 - 菜單關聯表(多對多關系)。

其中,menu表的核心字段如下(與代碼對應):

字段名含義說明示例值
menu_id菜單 ID(主鍵)1
parent_id父級菜單 ID(0 表示一級菜單)0
name菜單名稱(用于前端顯示)"系統管理"
path路由路徑"/sys"
component前端組件路徑"Layout"
icon菜單圖標(前端顯示)"system"
hidden是否隱藏("true"/"false")"false"
sort排序號(控制菜單展示順序)1

三、VO 類設計(適配前端路由格式)

前端路由通常需要包含菜單名稱、路徑、組件、圖標等信息,且需區分一級菜單和子菜單。因此,我們設計以下 VO(View Object)類封裝路由數據:

1. MenuRouterVO(一級菜單路由)

@Data
public class MenuRouterVO {private String name;       // 菜單名稱private String path;       // 路由路徑private String component;  // 前端組件路徑private String hidden;     // 是否隱藏("true"/"false")private String redirect = "noRedirect";  // 重定向路徑(默認無)private Boolean alwaysShow = true;       // 是否總是顯示(一級菜單通常為true)private MetaVO meta;       // 路由元信息(包含標題、圖標)private List<ChildMenuRouterVO> children;  // 子菜單列表
}

2. ChildMenuRouterVO(二級菜單路由)

@Data
public class ChildMenuRouterVO {private String name;       // 子菜單名稱private String path;       // 子菜單路徑private String component;  // 子菜單組件路徑private String hidden;     // 是否隱藏private MetaVO meta;       // 子菜單元信息
}

3. MetaVO(路由元信息)

用于存儲前端渲染所需的標題和圖標:

@Data
public class MetaVO {private String title;  // 菜單標題(顯示在側邊欄)private String icon;   // 菜單圖標(如"system")
}

四、核心代碼實現

1. 控制器:處理動態路由請求(Controller)

控制器的作用是接收前端請求,協調獲取用戶信息、角色、菜單,并封裝返回結果。

@RestController
@RequestMapping("/sys/user")
public class UserController {@Autowiredprivate RoleMapper roleMapper;@Autowiredprivate MenuService menuService;/*** 加載動態路由:返回用戶信息、角色、可訪問菜單路由*/@GetMapping("/getRouters")public Result getRouters(HttpSession session) {// 1. 從Session獲取當前登錄用戶(登錄時已存入Session)User user = (User) session.getAttribute("user");if (user == null) {return Result.error("用戶未登錄");}// 2. 根據userId查詢角色名稱(如"超級管理員")String roleName = roleMapper.getRoleNameByUserId(user.getUserId());// 3. 根據userId查詢并封裝用戶可訪問的菜單路由List<MenuRouterVO> routers = menuService.getMenuRouterByUserId(user.getUserId());// 4. 封裝結果返回(用戶信息、角色、路由)return Result.ok().put("data", user)       // 用戶基本信息.put("roles", roleName)  // 角色名稱.put("routers", routers); // 動態路由列表}
}

2. 角色查詢:獲取用戶角色名稱(RoleMapper)

通過user_role表關聯role表,根據 userId 查詢角色名稱:

@Repository
public interface RoleMapper extends BaseMapper<Role> {/*** 根據userId查詢角色名稱* 聯表邏輯:user_role(用戶-角色關聯) → role(角色表)*/@Select("SELECT role_name FROM role, user_role " +"WHERE user_role.role_id = role.role_id " +"AND user_role.user_id = #{userId}")String getRoleNameByUserId(Integer userId);
}

說明:若用戶擁有多個角色,可修改 SQL 為GROUP_CONCAT(role_name)并返回字符串(如 “管理員,編輯”)。

3. 菜單查詢:獲取用戶權限菜單(MenuMapper)

通過user_rolerole_menumenu三表聯查,獲取用戶可訪問的所有菜單:

@Repository
public interface MenuMapper extends BaseMapper<Menu> {/*** 根據userId查詢可訪問的菜單列表* 聯表邏輯:user_role → role_menu → menu*/@Select({"SELECT m.menu_id, m.parent_id, m.name, m.path, m.component, " +"m.icon, m.hidden, m.sort " +"FROM user_role ur, role_menu rm, menu m " +"WHERE ur.role_id = rm.role_id " +"AND rm.menu_id = m.menu_id " +"AND ur.user_id = #{userId} " +"ORDER BY m.sort"  // 按sort排序,保證菜單展示順序})List<Menu> getMenusByUserId(Integer userId);
}

說明:查詢結果包含菜單的 ID、父級 ID、路徑等核心信息,后續將轉換為路由 VO。

4. 菜單服務:封裝路由結構(MenuService)

Service 層的核心是將數據庫查詢的Menu列表轉換為前端所需的MenuRouterVO列表,實現步驟:

  1. 從數據庫查詢用戶可訪問的所有菜單(menuList);
  2. 篩選一級菜單(parent_id = 0);
  3. 為每個一級菜單封裝MenuRouterVO屬性(名稱、路徑、組件等);
  4. 為每個一級菜單匹配子菜單(parent_id = 一級菜單ID),封裝為ChildMenuRouterVO
  5. 組合一級菜單與子菜單,返回最終路由列表。
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {@Autowiredprivate MenuMapper menuMapper;@Overridepublic List<MenuRouterVO> getMenuRouterByUserId(Integer userId) {// 1. 查詢用戶可訪問的所有菜單List<Menu> menuList = menuMapper.getMenusByUserId(userId);// 2. 存儲最終的路由列表(一級菜單)List<MenuRouterVO> routerList = new ArrayList<>();// 3. 遍歷菜單列表,篩選一級菜單并封裝for (Menu menu : menuList) {// 一級菜單:parent_id = 0if (menu.getParentId() == 0) {MenuRouterVO parentRouter = new MenuRouterVO();// 封裝一級菜單基本屬性parentRouter.setName(menu.getName());parentRouter.setPath(menu.getPath());parentRouter.setComponent(menu.getComponent());parentRouter.setHidden(menu.getHidden());parentRouter.setRedirect("noRedirect"); // 固定值(前端要求)parentRouter.setAlwaysShow(true);       // 總是顯示一級菜單// 封裝元信息(標題、圖標,用于前端渲染)MetaVO parentMeta = new MetaVO();parentMeta.setTitle(menu.getName());parentMeta.setIcon(menu.getIcon());parentRouter.setMeta(parentMeta);// 4. 為當前一級菜單匹配子菜單List<ChildMenuRouterVO> children = new ArrayList<>();for (Menu childMenu : menuList) {// 子菜單:parent_id = 一級菜單IDif (childMenu.getParentId().equals(menu.getMenuId())) {ChildMenuRouterVO childRouter = new ChildMenuRouterVO();// 封裝子菜單屬性childRouter.setName(childMenu.getName());childRouter.setPath(childMenu.getPath());childRouter.setComponent(childMenu.getComponent());childRouter.setHidden(childMenu.getHidden());// 子菜單元信息MetaVO childMeta = new MetaVO();childMeta.setTitle(childMenu.getName());childMeta.setIcon(childMenu.getIcon());childRouter.setMeta(childMeta);children.add(childRouter);}}// 5. 綁定子菜單到一級菜單parentRouter.setChildren(children);routerList.add(parentRouter);}}return routerList;}
}

五、關鍵邏輯解析

1. 表關聯查詢的意義

動態路由的核心是 “權限控制”,而權限控制的基礎是用戶 - 角色 - 菜單的關聯關系:

  • 用戶(user)通過user_role關聯角色(role);
  • 角色(role)通過role_menu關聯菜單(menu);
  • 最終實現 “用戶→角色→菜單” 的權限傳遞,確保用戶只能訪問其角色允許的菜單。

2. 路由封裝的核心思路

數據庫查詢的menuList是扁平的菜單列表(包含一級和二級菜單),需要轉換為樹形結構(一級菜單包含子菜單列表):

  • 先篩選parent_id = 0的一級菜單;
  • 再遍歷所有菜單,為每個一級菜單匹配parent_id等于其menu_id的子菜單;
  • 通過MetaVO封裝前端渲染所需的標題和圖標,確保與前端路由組件屬性對應。

3. 擴展性考慮

若系統需要支持三級及以上菜單,只需修改 Service 層的封裝邏輯,將子菜單的篩選改為遞歸處理:

// 遞歸獲取子菜單(示例偽代碼)
private List<ChildMenuRouterVO> getChildRouters(Integer parentId, List<Menu> menuList) {List<ChildMenuRouterVO> children = new ArrayList<>();for (Menu menu : menuList) {if (menu.getParentId().equals(parentId)) {ChildMenuRouterVO child = new ChildMenuRouterVO();// 封裝子菜單屬性...// 遞歸查詢當前子菜單的子菜單(三級菜單)child.setChildren(getChildRouters(menu.getMenuId(), menuList)); children.add(child);}}return children;
}

六、最終返回結果示例

前端接收的 JSON 格式如下(與 VO 類結構對應),可直接用于渲染動態路由:

{"code": 200,"msg": "操作成功","data": {"userId": 1,"username": "admin","realName": "管理員"// ...其他用戶信息},"roles": "超級管理員","routers": [{"name": "系統管理","path": "/sys","component": "Layout","hidden": "false","redirect": "noRedirect","alwaysShow": true,"meta": {"title": "系統管理","icon": "system"},"children": [{"name": "管理員管理","path": "/user","component": "sys/user/index","hidden": "false","meta": {"title": "管理員管理","icon": "user"}}]}]
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91357.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91357.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91357.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VitePress學習-自定義主題

VitePress-自定義主題 代碼倉庫 基礎了解 初始化項目的時候選擇 custom theme 運行后會發現頁面挺丑的。 如果想要用默認主題怎么辦呢&#xff0c;修改Layout。 使用默認主題的Layout <script setup lang"ts"> import { useData } from vitepress; impo…

【GEO從入門到精通】生成式引擎與其他 AI 技術的關系

2.1.3 生成式引擎與其他 AI 技術的關系生成式引擎作為人工智能領域的創新力量&#xff0c;與其他 AI 技術緊密相連&#xff0c;相互促進&#xff0c;共同推動 生成式引擎優化&#xff08;GEO&#xff09; 的發展。這些技術使生成式引擎能夠為消費者提供更加個性化和精準的內容。…

JAVAEE--4.多線程案例

設計模式1.單例模式1.1餓漢模式1.2懶漢模式(單線程版)1.3懶漢模式(多線程版本)1.4懶漢模式(多線程版本進階版)2.阻塞隊列3.定時器4.線程池1.單例模式設計模式是"軟性約束",不是強制的,可以遵守也可以不遵守,按照設計模式寫代碼使代碼不會太差框架是"硬性約束&qu…

量化感知訓練(QAT)流程

WHAT&#xff1a;量化感知訓練&#xff08;Quantization-Aware Training, QAT&#xff09; 是一種在模型訓練階段引入量化誤差的技術。它的核心思想是&#xff1a;通過在前向傳播時插入“偽量化節點”引入量化誤差&#xff0c;將權重和激活模擬為低精度&#xff08;如 int8&…

docker 用于將鏡像打包為 tar 文件

docker save 是 Docker 中用于將鏡像打包為 tar 文件的命令&#xff0c;常用于鏡像的備份、遷移或離線傳輸。以下是其核心用法和注意事項&#xff1a;一、基本語法bashdocker save [選項] IMAGE [IMAGE...] > 文件名.tar # 或 docker save -o 文件名.tar IMAGE [IMAGE...]IM…

設計模式(六)創建型:單例模式詳解

設計模式&#xff08;六&#xff09;創建型&#xff1a;單例模式詳解單例模式&#xff08;Singleton Pattern&#xff09;是 GoF 23 種設計模式中最簡單卻最常被誤用的創建型模式。其核心價值在于確保一個類在整個應用程序生命周期中僅存在一個實例&#xff0c;并提供一個全局訪…

PostgreSQL AND OR 操作符詳解

PostgreSQL AND & OR 操作符詳解 在數據庫查詢中,AND 和 OR 是兩種常見的邏輯操作符,用于組合多個查詢條件。PostgreSQL 作為一款功能強大的開源關系型數據庫管理系統,同樣支持這些操作符。本文將詳細介紹 PostgreSQL 中的 AND 和 OR 操作符,并探討它們在查詢中的應用…

RabbiteMQ安裝-ubuntu

Ubuntu 1.安裝Erlang RabbitMQ需要Erlang語言的支持&#xff0c;在安裝RabbitMQ之前需要安裝Erlang #更新軟件包 sudo apt-get update#安裝erlang sudo apt-get install erlang查看erlang版本 roothcss-ecs-027f:/# erl Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [sm…

Linux驅動20 --- FFMPEG視頻API

目錄 一、FFMPEG 視頻 API 的使用 1.1 介紹 1.2 整體編程過程 獲取核心上下文指針 打開輸入流文件 獲取輸入流 獲取編碼器 初始化解碼器 申請輸出流指針 獲取顯示數據空間大小 申請輸出顯示空間 綁定輸出流和輸出顯示空間 申請格式轉換上下文 申請輸入流指針 讀取一幀數據 發…

OpenBayes 一周速覽丨Self Forcing 實現亞秒級延遲實時流視頻生成;邊緣AI新秀,LFM2-1.2B采用創新性架構超越傳統模型

公共資源速遞 This Weekly Snapshots &#xff01; 5 個公共數據集&#xff1a; * AF-Chat 音頻對話文本數據集 * ArtVIP 機器交互式圖像數據集 * Updesh 印度語合成文本數據集 * Medical Information 藥品信息數據集 * Nemotron-Math-HumanReasoning 數學推理數據集…

[NOIP2002 提高組] 均分紙牌

題目描述有N堆紙牌&#xff0c;編號分別為 1,2,…,N。每堆上有若干張&#xff0c;但紙牌總數必為N的倍數。可以在任一堆上取若干張紙牌&#xff0c;然后移動。移牌規則為&#xff1a;在編號為1堆上取的紙牌&#xff0c;只能移到編號為2的堆上&#xff1b;在編號為N的堆上取的紙…

【音視頻】WebRTC-Web 音視頻采集與播放

一、打開攝像頭 打開攝像頭首先需要有一個html的video標簽&#xff1a; id "local-video"&#xff0c;是為了后續的js腳本調用這個對象autoplay是設置打開后自動播放&#xff0c;playsinline則是為了兼容移動端 <video id "local-video" autoplay p…

數據治理平臺如何選?深度解析國產化全棧方案與行業落地實踐

“數據治理平臺廠商有哪些&#xff1f;”國內主流廠商包括阿里云、華為、百分點科技等&#xff0c;各有所長。其中&#xff0c;百分點科技憑借在應急管理、智慧公安及央國企數字化領域的深度實踐&#xff0c;打造了行業特色鮮明的數據治理解決方案。百分點科技的數據治理解決方…

限流算法詳解:固定窗口、滑動窗口、令牌桶與漏桶算法全面對比

限流&#xff08;Rate Limiting&#xff09;是保障系統穩定性和服務質量的關鍵機制&#xff0c;尤其在高并發、突發流量、攻擊防護等場景中至關重要。本文將詳細介紹四種主流限流算法&#xff1a;固定窗口&#xff08;Fixed Window&#xff09;滑動窗口&#xff08;Sliding Win…

Sentinel 搭建應用層面與網關層面的流控保護

源碼&#xff1a;妖精的尾巴/spring-cloud-alibaba Nacos 和 Sentinel Dashboard 我這里全是使用window 本地運行的&#xff0c;需要自行下載運行 服務層面&#xff1a; 當你在某個具體的服務上使用Sentinel時&#xff0c;更多的是關注該服務內部資源的保護。例如&#xff0c…

純血鴻蒙 AudioRenderer+AudioCapturer+RingBuffer 實現麥克風采集+發聲

總共兩個類&#xff0c;放到代碼里&#xff0c;就可以快速完成K歌的效果&#xff0c;但應用層這么做延遲是比較高的&#xff0c;只是做一個分享。 類代碼 import { audio } from kit.AudioKit; import { BusinessError } from kit.BasicServicesKit; import { AudioBufferFlow,…

洛谷 P1601 A+B Problem(高精)普及-

題目描述 高精度加法&#xff0c;相當于 ab problem&#xff0c;不用考慮負數。 輸入格式 分兩行輸入。a,b≤10500a,b \leq 10^{500}a,b≤10500。 輸出格式 輸出只有一行&#xff0c;代表 ababab 的值。 輸入輸出樣例 #1 輸入 #1 1 1輸出 #1 2輸入輸出樣例 #2 輸入 #2 1001 909…

Matrix Theory study notes[6]

文章目錄linear spacereferenceslinear space a basis of linear space VkV^kVk,which is x1,x2,...xkx_1,x_2,...x_kx1?,x2?,...xk?,can be called as a coordinate system.let vector v∈Vkv \in V^kv∈Vk and it can be linear expressed on this basis as va1x1a2x2...…

專線與專線之間的區別

下面我們從定義、技術特點、適用場景、優缺點等多個維度來詳細對比&#xff1a;? 一、四種方案簡要定義技術方案定義MPLS 專線運營商基于 MPLS 技術提供的私有虛擬網絡&#xff0c;邏輯隔離、安全可靠VPN over Internet利用公網加密通道&#xff08;如IPSec&#xff09;構建虛…

Git工作流:團隊協作的最佳實踐

目錄 一、什么是 Git 工作流&#xff1f;為什么需要它&#xff1f; 二、基礎&#xff1a;Git 分支核心概念 三、主流 Git 工作流實戰指南 1. 集中式工作流&#xff08;Centralized Workflow&#xff09;&#xff1a;適合小團隊 / 新手 操作步驟&#xff1a; 優缺點&#…