☰
RuoYi-Vue与Vue-Element-Admin融合的通用后台权限管理系统重构实践
2026/9/30 12:19:37 网站建设 项目流程

做了这么多年后台管理系统,我越来越觉得“权限管理”这四个字看起来简单,真正落地的时候全是细节。RuoYi-Vue确实是一套很成熟的快速开发脚手架,前后端分离、RBAC权限模型、代码生成器都给你准备好了,但原版的界面风格和交互体验磨了很久之后还是会觉得有点跟不上现在的项目审美。后来我就基于 RuoYi-Vue 做了一次重构,前端换成了 Vue-Element-Admin 的布局和组件体系,后端权限核心逻辑保留并做了优化,最后沉淀出一套可以直接拿来二开的通用后台权限管理系统源码。这篇文章就聊聊这次重构的思路、权限模型的设计、核心模块的实现方式,以及我在实际过程中踩过的坑,希望能给正准备基于 RuoYi-Vue 做二次开发的同学一些参考。

如果你打算做企业级后台、SaaS 管理端或者内部管理系统,又不想从头反复写用户、角色、菜单、按钮权限这一套东西,那这套源码就是给你准备的。无论你是 Java 后端、前端开发,还是正在找毕业设计项目的学生,把它当作一套带权限控制的后台脚手架来用,能在很短的时间内把业务模块跑起来。

1. 项目背景:为什么我要把 RuoYi-Vue 改成 Vue-Element-Admin 风格

1.1 原版 RuoYi-Vue 的优势和槽点

先说结论:RuoYi-Vue 不是不好用,而是它在“快速交付”和“用户体验”之间侧重点很明确,优先保证“功能全、结构清晰、可以快速生成代码”。像部门管理、岗位管理、菜单管理、角色管理、用户管理、字典管理、参数管理、通知公告、操作日志、登录日志这些系统模块,原版基本都齐了。对于刚接触前后端分离开发的人,把数据库脚本导入、改一下配置、启动项目,马上就能看到一个完整的管理后台,这是一件很省心的事。

但只要你认真用上几个月,就会感受到几个痛点。第一个痛点是 UI 太偏“传统后台风”,虽然也是 Element UI,但整体布局、间距、表格密度、表单样式都比较老气,客户或领导看到之后往往会提一堆视觉上的修改需求。第二个痛点是前端代码的组织方式不够顺手,菜单数据虽然也能动态加载,但页面刷新之后路由恢复逻辑、标签页、面包屑这些交互细节都比较简单,做多级菜单嵌套时侧边栏的展示效果也一般。第三个痛点是权限标识比较粗糙,虽然做到了按钮级,但数据权限的配置需要手动改代码,对于要交付给非技术运维去配置的场景就不够友好。

我并不是说 RuoYi-Vue 不行,而是“项目成熟”不代表“满足所有场景”。在我接私活和做公司内部系统的过程中,客户经常提出“能不能做得更像现代后台一点,比如那种左边栏可以折叠、有顶部标签页、有暗色主题”的需求。为了在保持 RuoYi-Vue 后端稳定性的同时,尽量贴近现代中后台的交互标准,我开始考虑把它的前端界面整体迁移到 Vue-Element-Admin 这个成熟模板上。

1.2 Vue-Element-Admin 为什么适合做重构基底

Vue-Element-Admin 是很多开发者熟悉的 Vue2 后台管理模板,它的侧边栏、导航栏、面包屑、TagsView、权限指令、动态路由这些能力都已经封装得比较完整。选它而不从零手写一套前端,是考虑到三个点。

第一,它是基于 Vue2 + Element UI 的,和 RuoYi-Vue 原版前端技术栈一致,迁移成本比换成 Vue3 + Element Plus 要小得多。后端接口和前端组件之间的对接逻辑不用大改,只需要把路由、布局、状态管理、请求封装、页面视图换成 Vue-Element-Admin 的写法就行。第二,它对权限这套东西有天然的支持,比如v-permission指令、动态路由匹配规则、登录后根据角色生成路由的思路,都能和 RuoYi-Vue 的权限数据对得上。第三,它的视觉风格和组件拆解方式更符合我平时做项目的习惯,文件目录清晰,页面组件按views模块划分,后续加新功能或者给客户改主题都比较容易。

当然,直接拿 Vue-Element-Admin 原封不动接 RuoYi-Vue 也不行。RuoYi-Vue 的菜单模型有自己的字段设计,比如menuType、perms、component、visible、status,Vue-Element-Admin 的动态路由则更偏向于前端定义routes然后按角色过滤。所以这次重构的核心思路是:前端变成 Vue-Element-Admin 的壳和交互,权限数据来源仍然使用 RuoYi-Vue 后端的sys_menu表,这样既保留了 RuoYi-Vue 设计好的 RBAC 体系,又让界面和操作体验上升了一个台阶。

2. 整体架构设计与技术栈选型

2.1 通用后台权限管理系统的前后端分工

说完背景,我们把这套系统的结构拉通看一下。后端仍然是 Spring Boot 为主体,配合 Spring Security 做认证和授权,JWT 做无状态登录,Redis 保存验证码和权限缓存,MyBatis 操作 MySQL。前端以 Vue-Element-Admin 为基底,Vue2 生态,Vuex 管理用户状态和权限状态,Vue Router 负责路由注册,Axios 封装请求,Element UI 提供基础组件。

前后端的具体分工是这样的:后端负责真实的权限校验,每一个受保护的接口都会被 Spring Security 拦截,再通过@PreAuthorize注解或自定义权限服务判断当前登录用户是否拥有某个权限标识。前端负责呈现和交互,登录成功后拿到 token,之后通过/getInfo接口拿到用户信息和权限标识集合,再通过/getRouters接口拿到菜单路由树。前端根据菜单树动态生成路由,根据权限标识集合控制按钮和链接的显示。

这套分层的最大好处是:即使有人绕过前端直接调接口,后端也会拒绝没有权限的请求。前端所有隐藏菜单、隐藏按钮、禁用操作的行为都只是“体验优化”,真正的安全边界在后端。这也是我在重构时一直提醒自己的一件事:不要为了界面效果把权限控制的信任点放在前端,否则那就是一个花架子。

技术栈整理成表格大概是这样:

层次技术选型用途
后端框架Spring Boot 2.x核心业务接口与配置管理
安全框架Spring Security + JWT登录认证与接口授权
持久层MyBatis / MyBatis-Plus数据库操作
缓存Redis验证码、权限缓存、在线用户
数据库MySQL 5.7 / 8.0业务数据存储
前端框架Vue 2.6 + Element UI页面与组件
前端状态管理Vuex用户信息、权限集合、路由状态
前端路由Vue Router静态路由 + 动态路由
HTTP 请求Axios接口调用与拦截器

2.2 重构后项目目录怎么划分

这次重构后,前端目录结构基本保留了 Vue-Element-Admin 的骨架,同时把 RuoYi-Vue 中的业务页面按模块重新归置。src/api目录下按后端模块拆分接口文件,src/views目录下放页面组件,src/store/modules里新增了permission.js和user.js,分别管理动态路由和用户权限。后端的包结构则沿用 RuoYi-Vue 的com.ruoyi基础包,再根据自身需要做了少量调整。

很多人会觉得目录结构没什么好讲的,但我在实际重构中体会到,目录就是团队默契的地图。如果把接口请求、业务页面、路由配置混在一起,后续每加一个模块都要找半天,项目稍微一扩大就乱了。所以重构时我专门抽出了一部分时间把目录理清楚:系统管理相关的页面集中在system/下面,比如system/user/index.vue、system/role/index.vue;业务模块页面放在business/下;公共组件放到components/下。这样一来,后面接手的同事或自己半年后再看代码,基本不需要找人问就能定位到文件。

后端这里没有做过度拆分,因为 RuoYi-Vue 本身的分层已经够用了。controller管接口,service管业务,mapper管 SQL,domain放实体。我重点优化的是权限相关的几个服务类,比如SysPermissionService和SysLoginService,把权限获取、菜单构建、用户状态校验的流程做了梳理。代码清晰度比原版更好维护,也方便下一步扩展多租户或者数据权限。

3. 权限模型重构:从表结构到前后端联动

3.1 RBAC 权限模型的基础表结构是怎么设计的

这套系统依然采用最经典的 RBAC 模型,就是“用户-角色-权限”三道关系。具体拆成表来看,包含sys_user用户表、sys_role角色表、sys_menu菜单权限表,以及两张关联表sys_user_role用户角色关联表、sys_role_menu角色菜单关联表。另外还有sys_dept部门表、sys_post岗位表、sys_dict_type字典类型表和sys_dict_data字典数据表,这些是辅助组织架构和下拉数据的。

可能有人会问,为什么sys_menu既管菜单又管权限?这是 RuoYi 的设计特点。菜单表里的记录不只是侧边栏显示用的目录和菜单,它还包含按钮类型的数据。比如“用户管理”是菜单类型,它下面会有“新增用户”“修改用户”“删除用户”“查询用户”这些按钮类型记录,每个按钮记录都有一个perms字段,存的是权限标识,例如system:user:add、system:user:edit。一个角色的权限集合,最终就是通过关联这些菜单/按钮记录得到的。

我把核心字段整理出来,方便理解:

字段作用
menu_id菜单或按钮的唯一ID
menu_name菜单显示名称
parent_id父级ID,用于构造树形结构
order_num排序号
path路由地址,如/system/user
component前端组件路径,如system/user/index
menu_type类型:M目录、C菜单、F按钮
perms权限标识,如system:user:list
visible是否显示,0显示 1隐藏
status状态,0正常 1停用

菜单、按钮共用一张表的好处是权限模型简单清晰,前端拿到菜单树后能根据menuType区分哪条记录是目录、哪条是页面、哪条是按钮。后端校验权限时也只关注perms字段,不需要额外的权限点表。缺点是一旦菜单层级很深、按钮很多,表数据会膨胀,但大多数中后台项目都远没到那种量级,完全够用。

3.2 权限标识格式与接口校验逻辑

RuoYi-Vue 体系里的权限标识格式约定是三段式:模块名加功能名加操作名。例如system:user:list、system:user:add、system:role:edit。这个格式看起来简单,但它是前后端识别权限的“通用语言”。后端在接口方法上标记@PreAuthorize("@ss.hasPermi('system:user:add')"),前端在按钮上写v-hasPermi="['system:user:add']",两边必须完全一致才能协同工作。

这也是我在重构中特别注意的一个地方。原版 RuoYi-Vue 中存在少量权限标识不统一的历史问题,比如某个接口用的是query,前端按钮却写成list,结果用户明明有权限点击后还是提示无权限。重构时我做了一轮全面梳理,把菜单表里的perms、后端接口注解、前端按钮指令三者对照起来,统一成一套命名规范。偏好的用法是:查询列表用list,详情用query,新增用add,修改用edit,删除用remove,导出用export,导入用import。这样做之后,代码生成器生成的页面和接口能直接对上,几乎不需要再手动改权限码。

后端校验的逻辑其实不复杂。用户登录成功后,系统会加载该用户的角色,再根据角色关联的菜单表把所有perms字段收集起来,存到 Redis 或每次请求时动态查询。接口被调用时,Spring Security 上下文里有当前用户信息,@ss.hasPermi()方法会检查当前用户拥有的权限标识集合里是否包含指定的值。校验通过就放行,不通过就抛出异常,最终由全局异常处理器统一转成403响应。

3.3 动态路由数据是怎么生成的

动态路由是这次重构里最核心的一部分。原版 RuoYi-Vue 也能动态生成菜单,但组件路径的处理方式在迁移到 Vue-Element-Admin 之后需要重新梳理。后端/getRouters返回的菜单数据里,每条路由记录会包含name、path、component、meta这些 Vue Router 需要的关键信息。其中component是一个字符串,比如system/user/index,前端拿到这个字符串后要把它转换为真实组件对象。

Vue-Element-Admin 的动态路由处理思路是这样的:项目里提前把需要动态加载的组件注册到一个views目录映射表里,然后遍历后端返回的菜单树,遇到component字段就通过import方法去加载对应路径的组件。这里有一个特别容易踩坑的点:如果直接写import(component),构建工具会因为变量是动态的而无法正确把文件打进 Webpack 的 context 里,导致路由加载后白屏。稳妥的做法是维护一个modules对象,把所有views下的页面路径映射好,再用modules[component]拿组件。这个方法虽然古老,但在 Vue2 项目里非常稳定。

我把菜单树返回的一段简单示例放在这里,帮你理解前端到底拿到了什么:

{ "name": "System", "path": "/system", "hidden": false, "component": "Layout", "meta": { "title": "系统管理", "icon": "system", "noCache": false }, "children": [ { "name": "User", "path": "user", "component": "system/user/index", "meta": { "title": "用户管理", "icon": "user", "noCache": false } } ] }

前端拿到这样一颗路由树后,会先注册父级 Layout 布局组件,再把所有子路由追加到同一个父路由的children里。这样一来,刷新页面时只需要重新请求一次/getRouters,就能还原整个侧边栏和页面路由,不会出现“刷新后 404”的尴尬。

4. 核心模块重构与实现细节

4.1 登录认证流程:验证码、JWT 和请求拦截

登录认证流程是每一个后台系统的门面,我重构时把 RuoYi-Vue 原本的逻辑移植到了 Vue-Element-Admin 的登录页和状态管理里。整体流程是这样的:用户输入用户名、密码和验证码之后,前端先调用验证码接口,得到 base64 格式的图片和验证码 key;然后将用户名、密码、验证码 key 和值一起提交到/login接口;后端校验验证码通过后,验证用户名密码,如果没问题就生成一个 JWT token 返回给前端,同时把用户信息、权限标识、路由数据等放在后续接口中返回。

前端拿到 token 后并没有立刻跳转,而是先把它保存到cookie和 Vuex 状态中,然后调用/getInfo获取用户基本信息、角色集合、权限标识集合,再调用/getRouters获取动态路由树。所有准备工作都完成之后,才通过router.addRoutes注册动态路由、生成侧边栏菜单,最后跳转到首页。

这里我特别想提醒一下请求拦截器的配置。Vue-Element-Admin 原版默认用token作为请求头的字段名,而 RuoYi-Vue 后端默认从Authorization请求头里拿 token。如果不做统一,你的登录永远都是成功的,但后续每个请求都会返回 401。重构时我在 Axios 拦截器里把 token 放到Authorization头,并且在请求发送前统一加上Bearer前缀,后端通过 JWT 过滤器解析这个头来获取用户信息。调整好这一个小点,前后端认证就完全对上了。

4.2 按钮级权限指令的实现方式

按钮权限是后台管理系统里最容易被忽略但又最烦人的细节。最常见的需求是:普通用户能看到用户管理页面,但看不到“新增用户”按钮;管理员能看到所有按钮。原版 RuoYi-Vue 在前端会通过v-hasPermi指令来判断按钮是否显示。重构时我把这个指令重新封装了一遍,并结合 Vue-Element-Admin 的权限处理逻辑做了增强。

指令的核心逻辑很简单:从 Vuex 的user模块里取出当前用户拥有的permissions数组,判断数组里是否包含指定权限标识。比如页面中有这样一个按钮:

<el-button v-hasPermi="['system:user:add']" type="primary">新增用户</el-button>

指令被触发后,会检查当前用户权限集合,如果包含system:user:add,就什么都不做,按钮正常渲染;如果不包含,就直接把按钮的 DOM 节点remove掉。我还额外支持了多权限传参的情况,默认使用some逻辑,也就是只要拥有其中一个权限就显示按钮;对于需要同时拥有多个权限才显示的场景,可以通过修饰符改成every逻辑。这样一套指令基本能覆盖日常所有按钮控制需求。

这里要注意,指令控制的只是前端“能不能看见”和“能不能点”,真正的安全校验依旧在后端。所以我所有涉及按钮操作的接口都加了@PreAuthorize注解,两边权限码保持一致。如果有人绕过前端手动调接口,后端也会拦截住,这算是我一直坚持的安全底线。

4.3 布局与主题改造:侧边栏、TagsView、面包屑

Vue-Element-Admin 给人最直观的感觉就是它的布局比普通后台模板丰富很多。这次重构把 RuoYi-Vue 原有的首页布局整体替换成了 Vue-Element-Admin 的布局结构:左侧是递归渲染的侧边栏,顶部是导航栏,中间是 TagsView 标签页,再往下是内容区域,页面切换时会自动保留多个打开的标签页。对用户来说,最明显的提升就是可以在多个页面之间来回切换,不用每次重新点菜单。

改造过程中,最容易出问题的是侧边栏的递归渲染和菜单图标的映射。RuoYi-Vue 的菜单表里用icon字段存储图标名,但原版前端和 Vue-Element-Admin 的图标体系并不完全一样。Vue-Element-Admin 用的是 SVG 图标,放在src/icons/svg目录下,通过svg-sprite-loader加载。为了保证后端配置的图标名能正确显示,我在图标组件里维护了一张映射关系表,把 RuoYi-Vue 常用的图标名称和 Vue-Element-Admin 的 SVG 文件名统一起来。现在后端配置图标时,只要填的图标名存在于 SVG 目录里,侧边栏就能显示;如果找不到对应文件,就显示一个默认的占位图标。

TagsView 的改造相对独立,我直接沿用了 Vue-Element-Admin 的组件,再增加了一个基于路由meta.title的中文标题映射。每次路由切换时,把当前路由信息存入一个visitedViews数组,渲染在顶部的标签栏中。关闭标签页时,要处理好路由跳转逻辑,否则会出现关闭当前页后地址栏还是旧地址,但内容已经是新页面这种奇怪的视觉错位。

4.4 通用组件与工具层整理

除了页面布局,我还把 RuoYi-Vue 里比较实用的几个能力迁移并整理成了更通用的组件。分页表格就是一个典型例子。RuoYi-Vue 原版的users/index.vue里,表格和分页的代码是直接写在业务页面里的,每个页面都要重复写一遍handleQuery、resetQuery、handleSizeChange、handleCurrentChange。重构时我封装了一个SearchTable组件,把搜索表单、表格、分页串起来,前端页面只需要传入列配置、请求方法和搜索字段,就能快速输出一个标准列表页。

字典翻译也是后台系统的高频功能。原版 RuoYi-Vue 在页面里经常用dictDatas循环下拉选项,重构后我更倾向于在全局挂载一个dictTranslate方法,所有需要字典翻译的字段直接调用方法处理。比如“用户状态”字段实际存的是0或1,页面显示时需要翻译成“正常”或“停用”。我把字典类型从后端一次加载到 Vuex 里,前端在表格格式化、表单下拉、详情展示时直接从内存中取,速度快且代码干净。

同时,Excel 导入导出、定时任务管理、通知公告、操作日志这些常用模块也做了梳理。重点优化了操作日志的展示,把请求方式、请求参数、返回结果、异常信息、消耗时间都结构化展示出来。这套基础能力放到“通用权限管理系统源码”这个定位下,已经足够支撑大多数企业级项目的启动阶段。

5. 从零开始跑起这套源码的完整实操

5.1 环境准备与项目初始化

很多人拿到源码的第一步就被环境卡住了,我建议先确认这四样东西:JDK 8 以上、Maven 3.6 以上、MySQL 5.7 或 8.0、Redis 5 以上。前端需要 Node.js,建议使用 14 或 16 版本,不要盲目用最新的 Node 18,因为 Vue2 项目依赖的一些包在更高版本下会报 node-sass 编译错误。

初始化流程按顺序来会比较顺。先把源码解压,在数据库里创建一个空库,比如ruoyi_rebuild,然后执行项目sql目录下的初始化脚本。脚本会建表并插入基础数据,里面默认内置了一个admin管理员账号。接着修改后端配置文件application-druid.yml和application.yml,把数据源地址、账号密码、Redis 地址配好。这里提醒一下,如果你用的是 MySQL 8,驱动依赖和连接 URL 要注意时区参数,连接串里建议加上serverTimezone=Asia/Shanghai。

后端依赖安装直接用 Maven 就好,在项目根目录执行mvn clean package -DskipTests,等打包完成运行生成的 jar 包,或者直接用 IDE 启动RuoYiApplication。启动日志里如果看到启动成功并且 Tomcat 端口默认是 8080,说明后端已经就绪。一个快速验证的方法是访问http://localhost:8080/captchaImage,如果能返回 JSON 或验证码图片信息,说明项目和 Redis 之间是通的。

5.2 前端启动与登录验证

前端目录独立,进入项目目录后先执行npm install安装依赖。如果你用的是低版本 Node,这一步通常很顺利;如果遇到 node-sass 相关报错,多半是 Node 版本不匹配,建议换用 Node 14 再试。之后修改前端.env.development文件里的VUE_APP_BASE_API配置,指向后端地址,比如http://localhost:8080。这里的配置决定了 Axios 请求的 baseURL,很多人启动前端后所有接口都 404,基本就是这里没改对。

依赖装好后执行npm run dev,浏览器会自动打开登录页。输入默认账号admin,密码admin123,再输入验证码,点击登录后如果能看到动态菜单和首页,说明整套前后端认证链路已经跑通。我第一次重构完测试时也卡过一下,登录页一直显示“登录失败”,后来发现是验证码接口返回的 key 没有正确在提交参数中使用。如果你也遇到这类问题,优先检查 Redis 里验证码是否保存成功、提交的验证码 key 和值对不对。

5.3 在系统里配置一个新的业务功能

跑通基础登录之后,我想用一个例子说明这套系统怎么实际用来做权限管理。假设我要增加一个“设备管理”模块,包含设备列表和设备新增两个操作。第一步,先在数据库建一张device_info表,同时配置好菜单:一级菜单叫“设备管理”,类型选“目录”,再在它下面建一个菜单“设备列表”,类型选“菜单”,组件路径填business/device/index,权限标识填business:device:list;然后建一个按钮“新增设备”,类型选“按钮”,权限标识填business:device:add。

第二步,在前端views/business/device/index.vue写列表页面,接口调用写在src/api/device.js里。后端写好DeviceController,在新增接口上加上:

@PreAuthorize("@ss.hasPermi('business:device:add')")

第三步,给角色分配权限。进入系统管理-角色管理,选中需要授权的角色,点击“修改”之后在菜单权限树里勾选“设备管理”下面对应的菜单和按钮,保存即可。分配完成后,我建议把当前用户重新登录一下,或者主动清一下权限缓存,否则旧权限数据可能还留在 Redis 里,导致新权限不能马上生效。

通过这个例子你可以发现,通用后台权限管理系统的价值就在这里:新加一个模块时,你不用从零写权限框架,只需要做好业务表、页面、接口,然后在菜单管理里配好权限标识,再给角色勾选权限,前后端就能自动控制访问范围和按钮显示。

6. 常见问题与排查技巧实录

6.1 登录成功但菜单没有生成怎么办

这是重构后新手最容易遇到的问题。登录接口返回成功,但页面没有侧边栏菜单,或者只有默认首页。问题一般出在/getRouters接口返回的数据没有被前端正确转换。排查时先打开浏览器控制台看 Network,确认/getRouters是否调用成功,返回的菜单树是否包含component字段。如果接口正常,再检查前端动态路由加载处是否把后端返回的component字符串成功映射成组件。如果某个菜单的组件路径写错了,比如component写成system/user/index但前端实际路径是system/user/index.vue且没被映射表收录,整个动态路由注册就会失败,菜单树自然就没了。

我自己的处理方式是:在动态路由生成的工具函数里加一个try-catch和console.log,先打印每一条菜单记录,再看哪条加载失败。定位到问题后,优先改菜单表的component字段,而不是去前端写死映射。因为数据修复一次后续就正常,写死映射只会让问题越积越多。

6.2 接口报 401 或 403

401 是未认证,一般是 token 没有正确传递到后端。先看登录成功之后,请求头的Authorization字段有没有值。如果没有,回到 Axios 拦截器检查 token 的读取方式,确认 Vuex 和 cookie 里的变量名与拦截器里一致。还有一个小坑是跨域情况下,如果后端没有放开请求头Authorization,浏览器会在正式发送请求之前发出 OPTIONS 预检请求,后端没处理好就会直接返回 401。解决方法是后端配置 CORS 过滤器时,允许的请求头里加上Authorization。

403 则代表已经认证,但权限不够。先确认当前用户确实拥有该权限,再对比接口注解里的权限码和菜单表里的perms是否一致。比如接口里写的是system:user:query,菜单和按钮里却是system:user:list,那前端显示正常但接口必定 403。这种情况没有太多技巧,就是统一权限码。

6.3 按钮权限指令不生效

指令不生效通常是两种原因。第一种是 Vuex 里的permissions数组为空,指令拿不到任何权限标识,所有按钮都被移除。检查/getInfo接口返回的用户权限集合是否正常,如果只有一个空数组,多半是角色没有配置按钮权限,回到角色管理中勾选。第二种是权限标识大小写或空格不一致。后端返回可能带空格,前端指令又没做 trim,例如system:user:add和system:user:add在字符串比对时是不相等的。我封装指令时统一做了 trim,避免这类隐蔽问题。

还有一种情况比较特殊,就是按钮出现在动态渲染的表格列里,比如每一行都会根据数据状态显示或隐藏操作按钮。这时指令已经执行过了,但数据更新后 DOM 没有重新触发指令,导致按钮状态没刷新。遇到这种场景,我不推荐继续用指令,而是用函数式判断,在获取表格数据时统一计算当前行的操作权限,然后通过v-if来控制按钮显隐,这样更可控。

6.4 Redis 缓存导致的权限更新不生效

权限修改后不生效,我相信很多人遇到过。RuoYi-Vue 会把用户权限放到 Redis 里,角色和菜单变更后,如果缓存没有及时清理,旧权限会存在一段时间。处理方式有两种。第一种是暴力但有效,在 Redis 客户端里删除对应的缓存 key,让用户重新登录后重新加载;第二种是代码层面完善,角色或菜单数据变更时,主动删除相关用户的缓存。重构后我建议在SysRoleServiceImpl和SysMenuServiceImpl的变更方法里增加缓存清理逻辑,这样系统管理人员在后台修改权限后,用户下次请求时就能重新从数据库加载最新权限。

这个问题最坑的一点是,它往往不是必现的。你可能在后台改完权限后立刻用管理员测试,发现没问题,但被影响的用户那边还是旧权限。原因就是管理员和普通用户各自有独立的权限缓存,改角色时没有把用户列表动态清掉。在排查时,优先定位缓存有效期和用户缓存 key 之间的关系,基本就能解决。

7. 重构过程中的一些经验沉淀和后续扩展方向

7.1 重构不是推翻重写,而是兼容中升级

把 RuoYi-Vue 和 Vue-Element-Admin 结合,我的一个重要体会是“能不动就不动”。RuoYi-Vue 的后端业务模块设计已经很成熟,如果因为换了前端就把后端也重写,不仅工作量大,还容易引入各种隐蔽的问题。重构时我把重点放在前端交互、页面组件、路由体系和权限指令这几块,后端核心的用户、角色、菜单服务保持原有逻辑,只是把接口返回接口按需调整成 Vue-Element-Admin 更习惯的数据结构。

这样可以保证两个重要的东西:一是历史数据不用迁移,数据库表结构和原版 RuoYi-Vue 几乎一致,已有系统可以直接把前端替换过来;二是团队里熟悉 RuoYi-Vue 的人上手不会太困难,因为后端代码风格和分层还是熟悉的配方。我在很多项目里发现,所谓重构最容易翻车的地方不是技术实现,而是把稳定运行的模块也一并“优化”了,导致回归测试范围失控。所以我的原则是:只有当前架构明确阻碍新需求时才动底层,否则只做局部替换。

7.2 权限系统想做得“通用”,要考虑扩展性

通用后台权限管理系统的“通用”二字不是说说而已。我在这次重构中,特意把权限模块的扩展点留出来。比如数据权限,目前系统支持按部门、按岗位、按自定义 SQL 去做范围限制,最基础的实现方式是用户在角色配置里选择“数据范围”,包括全部数据、本部门数据、本部门及以下数据、仅本人数据。在业务查询时,通过工具类自动拼接 SQL 条件,这样业务代码里就不用到处写死数据过滤逻辑。

未来如果想做成 SaaS 多租户版本,可以考虑在sys_user表中增加tenant_id字段,并把所有业务表都加上租户标识,在 MyBatis 层用拦截器自动拼接租户条件。这个动作在当前基础上是可以平滑扩展的,因为权限核心和菜单模型是独立的,不需要把整套代码推倒重来。

7.3 最后再分享一个使用技巧

说了这么多,最后再分享一个我从这套项目里提炼出来的小技巧。当你要给客户交付二开权限系统时,建议把菜单表里的component字段整理成一份“页面组件路径字典”。因为 Vue-Element-Admin 的动态路由是依赖这个字段去加载组件的,如果你不熟悉系统里有哪些可用的组件路径,每次新增页面都要去翻目录。这份字典可以直接放在项目文档里,按模块列出所有组件路径和对应的权限标识,团队成员在配置菜单时就不用来回试错,效率会高很多。我自己在重构后维护了这样一份表,后续新增模块时基本五分钟就能配好菜单和按钮权限。

我实际使用下来最大的体会是,一个权限管理系统的价值不在于它写了多少个页面,而在于权限模型是否清晰、扩展是否顺手、前后端权限码是否统一。这套基于 RuoYi-Vue 优化重构的通用后台权限管理系统源码,正是围绕着这几个核心点来做的,你可以直接拿来当作新项目的底座,也可以参考其中的改造思路,把手上的老系统慢慢迁移过来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询