目录


一、项目概述与业务需求

1. 项目背景

在企业级后台管理系统中,数据权限控制是一个核心需求。RuoYi-Vue(若依)框架内置了一套完善的数据权限体系,通过@DataScope注解+AOP切面+动态SQL拼接的方式,实现了基于部门维度的数据隔离。本项目以"车间设备管理"为业务场景,完整演示从建表、代码生成、Mapper修改、Service注解配置到后台菜单与角色权限分配的全流程。

2. 功能需求

本实验需要实现以下核心功能:

  • 管理员(admin):可以查看全部部门的设备数据(3条设备记录)
  • 普通用户(ry/lewis):仅能查看自己所属部门的设备数据
    • 用户 ry 属于"测试部门"(dept_id=105),角色为"普通角色"
    • 用户 lewis 属于"财务部门"(dept_id=106),角色为"普通角色"
  • 超级管理员角色:拥有"全部数据权限"
  • 普通角色:拥有"本部门数据权限"

3. 技术栈介绍

技术层 技术选型
后端框架 Spring Boot + MyBatis(RuoYi-Vue v3.8.2)
前端框架 Vue 2 + Element UI
数据库 MySQL(数据库名:ry_vue_32)
开发工具 IntelliJ IDEA、Navicat Premium Lite
数据权限 @DataScope注解 + DataScopeAspect切面 + MyBatis动态SQL

二、完整实操流程

步骤1:数据库建表——创建车间设备表

操作页面:Navicat Premium Lite 数据库管理工具

首先在数据库中创建车间设备信息表sys_equipment。该表需要包含设备基本信息字段,以及与部门关联的dept_id字段(这是实现数据权限的关键)。

完整SQL建表语句

-- 车间设备表
CREATE TABLE sys_equipment (
  equipment_id   BIGINT       NOT NULL AUTO_INCREMENT COMMENT '设备ID',
  dept_id        BIGINT       NOT NULL                COMMENT '所属部门ID',
  equipment_no   VARCHAR(50)  NOT NULL                COMMENT '设备编号(Zhengyixuan_739)',
  equipment_name VARCHAR(100) NOT NULL                COMMENT '设备名称',
  status         CHAR(1)      DEFAULT '0'             COMMENT '状态(0正常 1停用)',
  del_flag       CHAR(1)      DEFAULT '0'             COMMENT '删除标志(0正常 2删除)',
  create_by      VARCHAR(64)  DEFAULT ''              COMMENT '创建者',
  create_time    DATETIME     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  update_by      VARCHAR(64)  DEFAULT ''              COMMENT '更新者',
  update_time    DATETIME     DEFAULT NULL            COMMENT '更新时间',
  remark         VARCHAR(255) DEFAULT NULL            COMMENT '备注',
  PRIMARY KEY (equipment_id),
  KEY idx_dept_id (dept_id),
  KEY idx_del_flag (del_flag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车间设备信息表';

字段说明

字段名 类型 说明
equipment_id BIGINT 主键ID,自增
dept_id BIGINT 所属部门ID,数据权限过滤的关键字段
equipment_no VARCHAR(50) 设备编号,格式为"姓名拼音_序号"
equipment_name VARCHAR(100) 设备名称(中文)
status CHAR(1) 状态:0-正常,1-停用
del_flag CHAR(1) 删除标志:0-正常,2-删除
create_by/create_time VARCHAR/DATETIME 创建者/创建时间
update_by/update_time VARCHAR/DATETIME 更新者/更新时间
remark VARCHAR(255) 备注信息

设计要点

  • dept_id 字段是数据权限的核心,标记每台设备属于哪个部门
  • del_flag 字段用于逻辑删除,与RuoYi框架规范保持一致
  • 表名前缀使用sys_,与RuoYi系统表命名规范统一
  • 索引设计:idx_dept_id索引用于加速部门维度查询

执行结果:OK,运行时间 0.157s,表创建成功。

【此处插入对应截图】


步骤2:插入测试数据

操作页面:Navicat Premium Lite 数据库管理工具

sys_equipment表中插入3条测试数据,分别属于两个不同的部门:

  • 测试部门(dept_id = 105):2台机床设备
  • 财务部门(dept_id = 106):1台打印机设备

完整INSERT语句

-- 部门:测试部门 dept_id=105; 财务部门 dept_id=106
INSERT INTO sys_equipment
(dept_id, equipment_no, equipment_name, status, del_flag, create_by)
VALUES
(105, 'Zhengyixuan_739_001', '一号机床', '0', '0', 'admin'),
(105, 'Zhengyixuan_739_002', '二号机床', '0', '0', 'admin'),
(106, 'Zhengyixuan_739_003', '财务专用打印机', '0', '0', 'admin');

数据分布说明

设备编号 设备名称 所属部门 dept_id 创建者
Zhengyixuan_739_001 一号机床 测试部门 105 admin
Zhengyixuan_739_002 二号机床 测试部门 105 admin
Zhengyixuan_739_003 财务专用打印机 财务部门 106 admin

执行结果:Affected rows: 3,运行时间 0.108s,数据插入成功。

数据权限测试预期

  • 登录admin用户:应看到全部3条设备记录
  • 登录ry用户(测试部门):应仅看到2条测试部门的设备
  • 登录lewis用户(财务部门):应仅看到1条财务部门的设备

【此处插入对应截图】


步骤3:代码生成——导入数据库表

操作页面:若依管理系统 → 系统工具 → 代码生成 → 导入表

在RuoYi后台管理系统中,进入"系统工具 → 代码生成"页面,点击"导入"按钮,弹出"导入表"对话框。

操作步骤

  1. 点击左侧菜单"系统工具" → “代码生成”
  2. 在代码生成页面中,点击"导入"按钮
  3. 在弹出的"导入表"对话框中,输入搜索条件
  4. 在数据列表中找到sys_equipment
  5. 勾选该表的复选框
  6. 点击"确定"按钮完成导入

导入成功后,代码生成列表中会出现一条新的记录,表名称为"车间设备信息表",对应实体类名为SysEquipment

【此处插入对应截图】


步骤4:代码生成——进入编辑配置

操作页面:若依管理系统 → 系统工具 → 代码生成 → 编辑

导入成功后,在代码生成列表中找到"车间设备信息表"这条记录,点击操作栏中的"编辑"按钮,进入生成配置页面。

此时可以看到:

  • 表名称:车间设备信息表
  • 表描述:车间设备信息表
  • 实体类:SysEquipment
  • 创建时间:2026-06-01 09:11:36

【此处插入对应截图】


步骤5:代码生成——填写基本信息

操作页面:若依管理系统 → 代码生成 → 修改生成配置 → 基本信息

在"修改[车间设备信息表]生成配置"页面中,切换到"基本信息"选项卡,填写以下内容:

配置项 填写内容 说明
表名称 车间设备信息表 自动读取,无需修改
表描述 车间设备信息表 自动读取
实体类名称 SysEquipment Java实体类名(驼峰命名)
作者 Zhengyixuan_739 代码中@author标注的作者名
备注 (留空) 可选填写

填写完成后,不要急于提交,还需要配置"字段信息"和"生成信息"。

【此处插入对应截图】


步骤6:代码生成——配置字段信息

操作页面:若依管理系统 → 代码生成 → 修改生成配置 → 字段信息

切换到"字段信息"选项卡,确认每个数据库字段对应的Java属性和类型是否正确:

序号 字段列名 字段描述 物理类型 Java类型 Java属性
1 equipment_id 设备ID bigint Long equipmentId
2 dept_id 所属部门 bigint Long deptId
3 equipment_no 设备编号 varchar(50) String equipmentNo
4 equipment_name 设备名称 varchar(100) String equipmentName
5 status 状态 char(1) String status
6 del_flag 删除标志 char(1) String delFlag
7 create_by 创建者 varchar(64) String createBy
8 create_time 创建时间 datetime Date createTime
9 update_by 更新者 varchar(64) String updateBy
10 update_time 更新时间 datetime Date updateTime
11 remark 备注 varchar(500) String remark

关键字段确认

  • dept_id(所属部门)字段是后续数据权限过滤的核心,必须确保正确映射
  • del_flag(删除标志)字段用于条件逻辑删除过滤

【此处插入对应截图】


步骤7:代码生成——设置生成信息

操作页面:若依管理系统 → 代码生成 → 修改生成配置 → 生成信息

切换到"生成信息"选项卡,进行以下配置:

配置项 填写/选择内容 说明
生成模板 单表(增删改查) 选择单表CRUD模板
前端类型 Vue2 Element UI 模版 前端Vue页面类型
生成包路径 com.ruoyi.system Java代码的根包路径
生成模块名 system 模块名称
生成业务名 equipment 业务名称(URL路径会用到)
生成功能名 车间设备信息 页面上显示的功能名称
生成代码方式 zip压缩包 下载ZIP包手动解压
上级菜单 系统管理 菜单归属到"系统管理"下

配置完成后点击"提交"按钮保存配置。

配置效果说明

  • 后端Java代码会生成到com.ruoyi.system包下
  • 控制器路由前缀为:/system/equipment
  • 前端页面文件位于Vue项目的equipment目录下

【此处插入对应截图】


步骤8:代码生成——下载生成代码

操作页面:若依管理系统 → 系统工具 → 代码生成

回到代码生成主页面,可以看到"车间设备信息表"这一行的更新时间已刷新(09:25:04)。点击该行操作栏中的"生成代码"按钮(红色方框高亮),系统会生成一份包含前后端全套代码的ZIP压缩包。

ZIP包中包含的文件

  • Java后端代码:Entity实体类、Mapper接口、Mapper XML、Service接口、Service实现类、Controller控制器
  • Vue前端代码:列表页面(index.vue)、API接口调用(api.js)
  • SQL脚本:菜单数据插入脚本(equipmentMenu.sql)

【此处插入对应截图】


步骤9:解压并整合代码文件

操作页面:Windows文件资源管理器

将下载的ZIP压缩包解压,可以看到以下文件结构:

├── main/              (后端Java代码和资源文件)
│   └── ...
├── vue/               (前端Vue代码)
│   └── ...
└── equipmentMenu.sql  (后台菜单SQL脚本)

整合步骤

  1. main目录下的Java文件按包结构放入项目对应位置
  2. 将Mapper XML文件放入resources/mapper/system/目录
  3. vue目录下的前端文件放入Vue项目对应位置
  4. 执行equipmentMenu.sql脚本(或在后台管理界面中手动添加菜单)

【此处插入对应截图】


步骤10:查看生成的Service实现类

操作页面:IntelliJ IDEA开发环境(RuoYi-Vue-v3.8.2项目)

打开生成的SysEquipmentServiceImpl.java文件,查看Service实现类的基本结构。

关键代码

package com.ruoyi.system.service.impl;

import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.SysEquipmentMapper;
import com.ruoyi.system.domain.SysEquipment;
import com.ruoyi.system.service.ISysEquipmentService;
import com.ruoyi.common.annotation.DataScope;  // ← 需要手动导入此注解

/**
 * 车间设备信息Service业务层处理
 *
 * @author Zhengyixuan_739
 * @date 2026-06-01
 */
@Service
public class SysEquipmentServiceImpl implements ISysEquipmentService
{
    @Autowired
    private SysEquipmentMapper sysEquipmentMapper;

    // 查询方法将在此处添加 @DataScope 注解
}

注意事项

  • 代码生成器自动生成了基本的CRUD方法框架
  • import com.ruoyi.common.annotation.DataScope; 需要手动添加(代码生成器不会自动导入)
  • @DataScope 是RuoYi框架自定义的数据权限注解

【此处插入对应截图】


步骤11:添加数据权限注解@DataScope

操作页面:IntelliJ IDEA开发环境(RuoYi-Vue-v3.8.2项目)

SysEquipmentServiceImpl.javaselectSysEquipmentList方法上添加@DataScope注解,这是实现数据权限的核心步骤。

完整代码

@Service
public class SysEquipmentServiceImpl implements ISysEquipmentService
{
    @Autowired
    private SysEquipmentMapper sysEquipmentMapper;

    /**
     * 查询车间设备信息列表
     *
     * @param sysEquipment 车间设备信息
     * @return 车间设备信息
     */
    @Override
    @DataScope(deptAlias = "d", userAlias = "e")  // ← 数据权限注解
    public List<SysEquipment> selectSysEquipmentList(SysEquipment sysEquipment)
    {
        return sysEquipmentMapper.selectSysEquipmentList(sysEquipment);
    }

    /**
     * 新增车间设备信息
     *
     * @param sysEquipment 车间设备信息
     * @return 结果
     */
    @Override
    public int insertSysEquipment(SysEquipment sysEquipment)
    {
        sysEquipment.setCreateTime(DateUtils.getNowDate());
        return sysEquipmentMapper.insertSysEquipment(sysEquipment);
    }

    /**
     * 修改车间设备信息
     */
    // ... 其他CRUD方法
}

@DataScope注解参数详解

参数 含义
deptAlias “d” SQL中部门表的别名(对应sys_dept dd
userAlias “e” SQL中主表(设备表)的别名(对应sys_equipment ee

工作原理简述

  1. 当调用selectSysEquipmentList方法时,AOP切面DataScopeAspect会拦截该方法
  2. 切面读取当前登录用户的角色数据权限范围
  3. 切面根据权限范围生成对应的SQL过滤条件(如AND (d.dept_id = 105)
  4. 将过滤条件拼接到SQL中的${params.dataScope}占位符位置

【此处插入对应截图】


步骤12:查看Mapper XML初始版本

操作页面:IntelliJ IDEA开发环境(RuoYi-Vue-v3.8.2项目)

打开代码生成器生成的SysEquipmentMapper.xml初始版本。这个版本还不能支持数据权限,需要进行关键修改。

初始Mapper XML代码

<mapper namespace="com.ruoyi.system.mapper.SysEquipmentMapper">

    <resultMap type="SysEquipment" id="SysEquipmentResult">
        <result property="deptId"         column="dept_id"         />
        <result property="equipmentNo"    column="equipment_no"    />
        <result property="equipmentName"  column="equipment_name"  />
        <result property="status"         column="status"          />
        <result property="delFlag"        column="del_flag"        />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
    </resultMap>

    <sql id="selectSysEquipmentVo">
        select e.equipment_id, e.dept_id, e.equipment_no, e.equipment_name, 
               e.status, e.del_flag, e.create_by, e.create_time, 
               e.update_by, e.update_time
        from sys_equipment e
    </sql>

    <select id="selectSysEquipmentList" parameterType="SysEquipment" 
            resultMap="SysEquipmentResult">
        <include refid="selectSysEquipmentVo"/>
        <where>
            <if test="deptId != null "> and dept_id = #{deptId}</if>
            <if test="equipmentNo != null  and equipmentNo != ''">
                and equipment_no like concat('%', #{equipmentNo}, '%')
            </if>
            <if test="equipmentName != null  and equipmentName != ''">
                and equipment_name like concat('%', #{equipmentName}, '%')
            </if>
            <if test="status != null  and status != ''">
                and status = #{status}
            </if>
        </where>
    </select>

    <!-- 其他查询、插入、更新方法... -->
</mapper>

初始版本存在的问题

  1. 缺少部门表关联(LEFT JOIN):无法获取部门信息,数据权限切面无法根据部门ID过滤
  2. 缺少${params.dataScope}占位符:数据权限切面生成的SQL片段没有注入点
  3. 缺少逻辑删除过滤(del_flag = '0':可能查出已删除的记录

【此处插入对应截图】在这里插入图片描述


步骤13:排查中文表名相关问题

操作页面:数据库管理工具搜索功能

在进行Mapper XML修改之前,需要先在代码中排查可能存在的"中文表名"问题。使用数据库管理工具(Navicat/DBeaver)的全局搜索功能,搜索关键字"车间设备信息表",确保所有引用处使用的是英文表名sys_equipment而非中文表名。

搜索操作

  • 搜索关键字:车间设备信息表(中文表名)
  • 关联搜索:sys_equipment(英文表名)
  • 找到4处匹配项,逐项检查确认

排查要点

  • 如果Mapper XML中写的是中文表名,MySQL会因字符集问题报错
  • 确保所有SQL语句使用英文表名sys_equipment
  • RuoYi代码生成器会自动使用英文表名,但手动修改时要特别注意

【此处插入对应截图】


步骤14:修改Mapper XML——添加部门关联与数据权限参数

操作页面:IntelliJ IDEA开发环境(RuoYi-Vue-v3.8.2项目)

这是整个数据权限实现中最关键的一步。需要在selectSysEquipmentList查询方法中做三处关键修改(红色方框标注处)。

修改后的完整Mapper XML代码

<mapper namespace="com.ruoyi.system.mapper.SysEquipmentMapper">

    <resultMap type="SysEquipment" id="SysEquipmentResult">
        <result property="updateTime"    column="update_time"    />
        <result property="remark"        column="remark"         />
        <!-- 其他字段映射... -->
    </resultMap>

    <sql id="selectSysEquipmentVo">
        select e.equipment_id, e.dept_id, e.equipment_no, e.equipment_name, 
               e.status, e.del_flag, e.create_by, e.create_time, 
               e.update_by, e.update_time
        from sys_equipment e
    </sql>

    <select id="selectSysEquipmentList" parameterType="SysEquipment" 
            resultMap="SysEquipmentResult">
        <include refid="selectSysEquipmentVo"/>
        left join sys_dept d on e.dept_id = d.dept_id      【修改1:关联部门表】
        <where>
            e.del_flag = '0'                                 【修改2:逻辑删除过滤】
            ${params.dataScope}                              【修改3:数据权限占位符】
            <if test="deptId != null "> and e.dept_id = #{deptId}</if>
            <if test="equipmentNo != null  and equipmentNo != ''">
                and e.equipment_no like concat('%', #{equipmentNo}, '%')
            </if>
            <if test="equipmentName != null and equipmentName != ''">
                and e.equipment_name like concat('%', #{equipmentName}, '%')
            </if>
            <if test="status != null and status != ''">
                and e.status = #{status}
            </if>
        </where>
    </select>

    <select id="selectSysEquipmentByEquipmentId" parameterType="Long" 
            resultMap="SysEquipmentResult">
        <include refid="selectSysEquipmentVo"/>
        where equipment_id = #{equipmentId}
    </select>

    <insert id="insertSysEquipment" parameterType="SysEquipment" 
            useGeneratedKeys="true" keyProperty="equipmentId">
        insert into sys_equipment
        <!-- 插入字段列表... -->
    </insert>
</mapper>

三处核心修改详解

修改项 代码 作用
修改1:关联部门表 left join sys_dept d on e.dept_id = d.dept_id 关联部门表,使数据权限切面可以根据部门ID过滤。注意别名字母d@DataScope(deptAlias = "d")中的参数保持一致
修改2:逻辑删除过滤 e.del_flag = '0' 只查询未被删除(del_flag=‘0’)的记录。注意添加表别名e.前缀,避免字段歧义
修改3:数据权限占位符 ${params.dataScope} 数据权限切面(DataScopeAspect)会动态替换此占位符。例如,普通角色登录时,此占位符会被替换为AND (d.dept_id = 105)

注意添加表别名的重要性

  • 修改前:del_flag = '0'(没有表别名,如果有多表可能会引起字段歧义)
  • 修改后:e.del_flag = '0'(明确指定从设备表别名e取字段)
  • 修改前:dept_id = #{deptId}(没有表别名)
  • 修改后:e.dept_id = #{deptId}(明确指定从设备表别名e取字段)

【此处插入对应截图】


步骤15:后台菜单配置——新增"车间设备信息"菜单

操作页面:若依管理系统 → 系统管理 → 菜单管理

代码整合完成后,需要在RuoYi后台管理系统中添加"车间设备信息"菜单,使其显示在左侧导航栏中。

重要提示:也可以直接执行代码生成时提供的equipmentMenu.sql脚本,或者在后台手动添加。

手动添加菜单的操作步骤如下:

  1. 进入"系统管理 → 菜单管理"页面
  2. 点击"新增"按钮
  3. 在弹出的菜单新增表单中,填写以下信息:
配置项 填写内容 说明
菜单名称 车间设备信息 左侧导航栏显示的名称
上级菜单 系统管理 菜单归属到"系统管理"菜单组下
显示顺序 (按实际设置) 菜单在导航栏中的排列顺序
路由地址 equipment 前端Vue页面的路由路径
权限标识 system:equipment:list 权限控制的标识符
菜单类型 菜单(M) 选择菜单类型
菜单图标 (选择合适的图标) 菜单前面显示的图标
是否外链 不是外部链接
  1. 点击"确定"按钮完成菜单添加

【此处插入对应截图】


步骤16:后端SQL调试验证

操作页面:IntelliJ IDEA控制台(DEBUG日志)

启动项目,访问车间设备信息页面,观察后端控制台输出的SQL调试日志,验证数据权限SQL是否正确拼接。

控制台DEBUG日志输出

10:04:22.162 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectSysEquipmentList_COUNT - [debug,137] - ==>  
Preparing: select count(0) from (
    select e.equipment_id, e.dept_id, e.equipment_no, e.equipment_name, 
           e.status, e.del_flag, e.create_by, e.create_time, 
           e.update_by, e.update_time
    from sys_equipment e
    left join sys_dept d on e.dept_id = d.dept_id
    where e.del_flag = '0' 
    AND (d.dept_id = 105)
) tmp_count

10:04:22.163 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectSysEquipmentList - [debug,137] - ==>  
Preparing: select e.equipment_id, e.dept_id, e.equipment_no, e.equipment_name, 
           e.status, e.del_flag, e.create_by, e.create_time, 
           e.update_by, e.update_time
from sys_equipment e
left join sys_dept d on e.dept_id = d.dept_id
where e.del_flag = '0' 
AND (d.dept_id = 105)
LIMIT ?

10:04:22.163 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectSysEquipmentList - [debug,137] - ==> 
Parameters: 10(Integer)
10:04:22.168 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectSysEquipmentList - [debug,137] - <==  
Total: 3

SQL分析——当管理员(admin)登录时

-- 超级管理员有"全部数据权限",因此不会拼接部门过滤条件
select ... from sys_equipment e
left join sys_dept d on e.dept_id = d.dept_id
where e.del_flag = '0'
LIMIT ?
-- 查询结果: Total: 3(显示全部设备)

SQL分析——当普通用户(ry,属于测试部门dept_id=105)登录时

-- @DataScope生成的条件: AND (d.dept_id = 105)
select ... from sys_equipment e
left join sys_dept d on e.dept_id = d.dept_id
where e.del_flag = '0'
AND (d.dept_id = 105)
LIMIT ?
-- 查询结果: Total: 2(仅显示测试部门的2台设备)

关键验证点

  • ${params.dataScope}被正确替换为AND (d.dept_id = 105)
  • LEFT JOIN关联的部门表别名d被用来过滤部门ID
  • 逻辑删除过滤e.del_flag = '0'正常工作
  • 动态条件(equipmentNo、equipmentName等)支持模糊查询

【此处插入对应截图】


步骤17:前端页面验证——管理员视图

操作页面:若依管理系统 → 系统管理 → 车间设备信息

使用管理员账号(admin)登录后,进入"车间设备信息"页面,验证页面功能。

页面功能说明

(1)筛选查询区:支持按所属部门ID、设备编号、设备名称、状态等条件进行搜索

(2)功能按钮区

  • 新增:添加新的设备记录
  • 修改:编辑选中的设备信息
  • 删除:逻辑删除选中的设备
  • 导出:导出设备列表数据

(3)数据表格(管理员视图——显示全部设备):

设备ID 设备编号 设备名称 状态 创建时间 操作
1 Zhengyixuan_739_001 一号机床 正常 2026-06-01 修改/删除
2 Zhengyixuan_739_002 二号机床 正常 2026-06-01 修改/删除
3 Zhengyixuan_739_003 财务专用打印机 正常 2026-06-01 修改/删除

验证结论:管理员admin可以看到所有3条设备记录(包括测试部门和财务部门的设备),数据权限对超级管理员不产生限制效果。

(4)分页区:共3条记录,支持翻页和跳转

【此处插入对应截图】


步骤18:角色权限分配——为普通角色赋权

操作页面:若依管理系统 → 系统管理 → 角色管理 → 修改角色

最后一步是配置角色权限,确保普通角色的用户只能看到自己部门的设备数据。

操作步骤

  1. 进入"系统管理 → 角色管理"页面
  2. 在角色列表中找到"普通角色"(权限字符:common)
  3. 点击该角色的"修改"按钮,弹出"修改角色"对话框

角色基本信息

配置项
角色名称 普通角色
权限字符 common
角色顺序 2
状态 正常

菜单权限分配
在"菜单权限"树形结构中,展开"系统管理"节点,勾选"车间设备信息"菜单项(红色方框标注),确保普通角色有权访问车间设备信息页面。

数据权限配置
进入"系统管理 → 角色管理 → 数据权限",为"普通角色"设置数据权限范围:

配置项 选择
权限范围 本部门数据权限

完整配置总结

角色 权限字符 菜单权限 数据权限 效果
超级管理员 admin 全部菜单 全部数据权限 查看所有部门的设备
普通角色 common 系统管理(含车间设备信息) 本部门数据权限 仅查看本部门的设备

多账号验证

登录用户 所属部门 角色 预期可见设备
admin 总公司 超级管理员 3条(全部)
ry 测试部门(105) 普通角色 2条(Zhengyixuan_739_001、002)

三、实操问题汇总(踩坑记录)

问题1:中文表名导致MySQL报错

项目 说明
现象 在Mapper XML中误用了中文表名"车间设备信息表"作为SQL查询的目标表
原因 MySQL在SQL语句中无法解析中文表名,需要严格使用英文表名sys_equipment
解决方案 使用数据库管理工具的全局搜索功能,查找所有"车间设备信息表"的中文引用,全部替换为sys_equipment。RuoYi代码生成器会自动使用英文表名,但手动修改代码时需格外注意

问题2:字段歧义(Column ambiguous)报错

项目 说明
现象 启动应用后访问页面,后端报错:Column 'del_flag' in where clause is ambiguousColumn 'dept_id' in where clause is ambiguous
原因 LEFT JOIN关联部门表后,主表sys_equipment和关联表sys_dept都有del_flag等公共字段。SQL中的where del_flag = '0'没有指定表别名,MySQL无法确定使用哪个表的字段
解决方案 WHERE子句中的所有字段前面加上表别名前缀:e.del_flag = '0'e.dept_id = #{deptId}e.status = #{status}等。确保每个字段都有明确的表别名

对比代码

修改前(会报错):
<where>
    del_flag = '0'              ← 字段歧义!
    ${params.dataScope}
    <if test="deptId != null "> and dept_id = #{deptId}</if>    ← 字段歧义!

修改后(正确):
<where>
    e.del_flag = '0'            ← 明确表别名e
    ${params.dataScope}
    <if test="deptId != null "> and e.dept_id = #{deptId}</if>  ← 明确表别名e

问题3:菜单添加后不显示(缓存问题)

项目 说明
现象 在后台菜单管理中添加了"车间设备信息"菜单,但左侧导航栏没有显示
原因 RuoYi框架对菜单进行了缓存,新增菜单后需要刷新缓存才能生效
解决方案 方法一:退出登录后重新登录;方法二:在系统监控中清除缓存;方法三:重新编译部署前端项目。推荐使用重新登录的方式

问题4:普通角色无法访问车间设备信息菜单

项目 说明
现象 使用普通角色用户(ry/lewis)登录后,左侧菜单没有"车间设备信息"选项
原因 新增菜单后,需要在角色管理中为"普通角色"分配该菜单的访问权限。否则即使用户属于该角色,也无法看到该菜单
解决方案 进入"系统管理 → 角色管理 → 修改普通角色 → 菜单权限",勾选"车间设备信息"菜单项并保存

问题5:数据权限不生效

项目 说明
现象 @DataScope注解已添加,Mapper XML也加了LEFT JOIN和${params.dataScope},但所有用户仍然可以看到全部数据
原因 可能原因有三:① deptAlias/userAlias参数值与XML中的表别名不一致;② 角色的数据权限范围没有设置为"本部门数据权限";③ 实体类没有继承BaseEntity(params属性丢失)
解决方案 逐个排查:检查注解参数与XML别名一致性 → 检查角色数据权限配置 → 检查实体类是否继承BaseEntity

四、RuoYi数据权限核心原理

4.1 整体架构

RuoYi框架的数据权限实现采用了 “注解声明 + AOP拦截 + SQL动态拼接” 的设计模式,整体架构如下:

Controller层
    ↓ 调用
Service层 (@DataScope注解声明权限规则)
    ↓ AOP拦截
DataScopeAspect切面 (根据用户角色拼接权限SQL)
    ↓ 注入params.dataScope
Mapper XML (通过${params.dataScope}使用权限SQL)
    ↓ 最终SQL
数据库 (执行带权限过滤的SQL)

4.2 核心组件说明

(1)@DataScope注解

定义在com.ruoyi.common.annotation.DataScope,两个核心参数:

参数 说明
deptAlias SQL中部门表的别名,用于拼接部门ID过滤条件
userAlias SQL中主表(用户/业务表)的别名,用于拼接用户过滤条件

(2)DataScopeAspect切面类

这是一个AOP切面类(标注@Aspect),通过@Before("@annotation(controllerDataScope)")拦截所有标注了@DataScope的方法。其核心逻辑在dataScopeFilter方法中:

处理流程

  1. 获取当前登录用户的所有角色
  2. 遍历角色,找到数据权限范围(全部/自定义/本部门/本部门及以下/仅本人)
  3. 根据权限范围生成对应的SQL片段:
    • 全部数据权限:不添加任何过滤条件
    • 本部门数据权限:生成 AND (d.dept_id = 当前用户部门ID)
    • 本部门及以下:生成 AND (d.dept_id IN (当前部门及子部门ID集合))
    • 自定义数据权限:生成 AND (d.dept_id IN (自定义部门ID集合))
    • 仅本人:生成 AND (e.create_by = 当前用户名)
  4. 将生成的SQL片段拼接后赋值给params.dataScope

(3)params.dataScope 占位符

params是RuoYi框架中BaseEntity基类的一个Map<String, Object>属性。AOP切面将生成的权限SQL片段放入params.dataScope键中,MyBatis通过${params.dataScope}直接替换为SQL片段。

注意:这里使用的是$符号(字符串替换),而不是#符号(预编译参数)。因为这里替换的是整个SQL条件片段,而非单个参数值。

4.3 以本实验为例的完整数据流

用户ry登录 → 所属部门dept_id=105 → 角色为"普通角色" → 数据权限为"本部门数据权限"
    ↓
访问车间设备信息页面 → Controller调用selectSysEquipmentList()
    ↓
DataScopeAspect拦截 → 读取用户角色权限 → "本部门数据权限"
    ↓
生成SQL片段: AND (d.dept_id = 105)
    ↓
params.dataScope = "AND (d.dept_id = 105)"
    ↓
最终SQL:
SELECT ... FROM sys_equipment e 
LEFT JOIN sys_dept d ON e.dept_id = d.dept_id
WHERE e.del_flag = '0' 
AND (d.dept_id = 105)
    ↓
返回结果: 2条设备记录(仅测试部门的设备)

4.4 五种数据权限范围对应的SQL

权限范围 对应编号 生成的SQL片段示例
全部数据权限 1 不添加过滤条件
自定义数据权限 2 AND d.dept_id IN (105, 106, ...)
本部门数据权限 3 AND d.dept_id = 105
本部门及以下数据权限 4 AND d.dept_id IN (105, 107, 108, ...)
仅本人数据权限 5 AND e.create_by = 'ry'

五、项目总结

5.1 核心要点回顾

  1. 数据权限的本质是对SQL的动态修改。无论是数据分页(PageHelper)还是数据权限(DataScope),本质上都是在原始SQL基础上动态拼接额外的条件语句。

  2. 三个关键要素缺一不可

    • Service层的@DataScope注解(声明需要数据权限过滤)
    • Mapper XML中的LEFT JOIN部门表 + ${params.dataScope}占位符(提供执行机制)
    • 后台角色管理中的数据权限配置(定义过滤规则)
  3. 别名一致性至关重要@DataScope(deptAlias = "d")中的d必须与Mapper XML中sys_dept d的别名d完全一致。

  4. 表别名解决字段歧义:在LEFT JOIN后,WHERE中所有字段都必须用e.字段名d.字段名的形式明确指定来源表。

5.2 技术收获

通过本次实操,掌握了以下核心技术点:

  • MyBatis动态SQL的编写(<if>条件判断 + <include>复用 + ${}字符串替换)
  • AOP面向切面编程的应用场景(在方法执行前拦截并修改参数)
  • RuoYi框架数据权限的完整实现链路
  • 数据库表设计中的权限字段预留(dept_idcreate_by
  • 前后端代码生成器的使用,大幅提高开发效率
  • 后台多角色权限配置与测试验证方法

5.3 拓展思考

  • 数据分页与数据权限的共同点:两者本质上都是对SQL语句进行修改——分页添加LIMIT子句,数据权限添加WHERE过滤条件
  • 列权限控制:如果要实现不同用户看到不同列(如用户A看不到手机号码列),可以考虑从SELECT列集合入手,通过动态控制列的选择来实现
  • 代码复用:掌握了本次设备管理的数据权限实现后,可以为系统中的其他业务表(如学生管理、订单管理等)添加类似的数据权限控制

更多推荐