RuoYi-Vue 权限学习

前言

本文聚焦 RuoYi-Vue 框架的权限体系展开深度解析,通过系统化的梳理与实战演示,你将收获:

  1. 清晰掌握 RuoYi-Vue 基于 Spring Security + JWT 的登录认证授权全流程,理解身份校验、Token 生成与校验的核心逻辑;
  2. 精准理解功能权限(菜单、按钮、接口)与数据权限的核心概念,区分两类权限在系统中的定位与价值;
  3. 深入剖析 RuoYi-Vue 功能权限(菜单权限、按钮权限、接口权限)和数据权限的底层实现原理,包括动态路由、自定义指令、AOP 切面、动态 SQL 拼接等关键技术的应用;
  4. 熟练掌握 RuoYi-Vue 中功能权限(菜单、按钮、接口)和数据权限的配置方法,能够结合实际业务场景完成权限的自定义配置与落地,解决企业级系统中的权限管控需求。

RuoYi-Vue 权限总共分为三大类:

  • 登录权限:控制能不能进入系统
  • 功能权限:控制前端组件的可见/用性(其中可细分为:菜单权限按钮权限接口权限
  • 数据权限:控制后端数据的可见性

PS:严格来讲登录权限并不是真正的权限,它是 “身份认证”(Authentication)功能,不属于权限范畴,这里只是我个人为了统一方便学习,我把它当作权限体系的入口统一梳理,同时也是因为功能权限和数据权限高度依赖于登录(它们都需要到 SpringSecurity 上下文中拿权限数据)

核心实现方式 权限类型 控制内容 控制层面 核心依赖表 关键判断逻辑 数据流向示例
Spring Security 认证 + Token 登录权限 能不能进系统 登录入口 sys_usersys_role 用户名密码校验、账号状态 status=0、验证码校验 登录页 → 校验账号密码 → 生成 JWT Token → 写入 Redis → 登录成功
动态路由 (Vue Router) + sys_menu 菜单权限 用户能看到左侧导航栏的哪些页面 前端菜单 sys_menu sys_role_menu sys_user_role sys_menu.menu_type in (‘M’, ‘C’) 用户ID → 查角色 → 查菜单ID → 过滤出类型为菜单的记录 → 生成左侧导航树
自定义指令 v-hasPermi 按钮权限 用户在页面内能看到哪些操作按钮 前端按钮 sys_menu sys_role_menu sys_user_role sys_menu.menu_type = ‘F’ 提取 perms 字段 用户ID → 查角色 → 查菜单ID → 过滤出类型为按钮的记录 → 提取 perms (如 user:add) → 前端 v-hasPermi
Spring Security + @PreAuthorize / @RequiresPermissions 接口权限 用户能否调用具体的API 接口 后端接口 sys_menu sys_role_menu sys_user_role 比对注解中的字符串与 sys_menu.perms 请求到达 Controller → Spring Security 拦截 → 获取用户 perms 列表 → 比对 @PreAuthorize('xxx') 中的字符串是否存在
AOP + @DataScope + 动态 SQL 数据权限 用户能看到查询结果中的哪些行数据 后端数据 sys_role sys_role_dept sys_user sys_dept sys_role.data_scope 的值 拦截 Service 方法 → 读 data_scope → 若是"自定义",查 sys_role_dept 获取部门ID列表 → 拼接 SQL WHERE dept_id IN (...)

登录权限

相关接口login

后端相关代码com.ruoyi.web.controller.system.SysLoginController#login

前端相关代码ruoyi-ui\src\views\login.vue

登录权限体系主要基于 Spring Security + JWT + Redis 构建,整个登录流程可以分为获取验证码和用户登录两个主要阶段。

PS:本文主要是讲解 RuoYi-Vue 的登录流程,并没有过于详细是介绍 SpringSecurity,RuoYi-Vue 底层核心是依赖于 SpringSecurity 实现登录认证授权的,关于 SpringSecurity 登录相关的详细学习请参考博主的这篇文章 初识SpringSecurity

image-20260403092653571

第一阶段:获取验证码

  • Step1:前端请求。用户打开登录页面时,前端通过 mounted 方法触发getCode方法自动向后端发起一个 captchaImage请求获取验证码
  • Step2:后端响应。
    1. 请求首先会经过 JwtAuthenticationTokenFilter 过滤器,由于是登录请求且没有 Token,过滤器会放行
    2. 请求会到达 CaptchaControllerCaptchaController 调用验证码生成器 DefaultKaptcha 来创建验证码图片和对应的答案
    3. 然后生成一个唯一的 uuid,并将 uuid 和验证码答案作为键值对存入 Redis 中,并设置一个较短的过期时间(如2分钟)
    4. 最后将生成的验证码图片(转为 Base64 格式)和 uuid 返回给前端。前端展示图片,并将 uuid 暂存,用于后续登录时校验

第二阶段:用户登录

  • Step1:前端请求。用户输入用户名、密码、验证码后,点击登录按钮,前端携带登录信息向后端服务器发送 login请求。

  • Step2:后端响应。

    1. 请求同样先被 JwtAuthenticationTokenFilter 拦截,因为此时并没有登录所以会放行。

    2. 请求到达 SysLoginControllerSysLoginController 调用 SysLoginServicelogin 方法,将用户名、密码、验证码和 uuid 传递过去。

    3. 验证码校验:核心方法是validateCaptcha

      a. 在 SysLoginService 中,首先会根据前端传来的 uuidRedisCache 中取出对应的验证码答案。校验取出的答案和用户输入的是否一致

      b. 校验完成后,会立即从 Redis 中删除该验证码,确保其一次性有效。如果验证码为空或错误,则抛出异常,登录失败

    4. 登录参数校验:非空判断、非法参数判断、IP 黑名单判断

    5. 用户身份认证:核心方法是 authenticate

      img

      a. 验证码通过后,SysLoginService 会调用 Spring Security 的核心认证组件 AuthenticationManager

      b. AuthenticationManager 会委托 UserDetailsServiceImpl 去根据用户名从数据库(通过 ISysUserService)查询用户信息

      c. UserDetailsServiceImpl 将查询到的用户信息封装成一个 LoginUser 对象(实现了 UserDetails 接口),其中包含了用户的基本信息、密码和权限列表

      d. AuthenticationManager 使用 DaoAuthenticationProvider 来比对 LoginUser 中的密码和用户提交的密码是否匹配。

    6. 生成令牌 (Token):核心方法是createToken

      a. 认证成功后,SysLoginService 会调用 TokenService 来创建 JWT 令牌。

      b. TokenService 会生成一个唯一的 token,并将登录用户 LoginUser 的详细信息缓存到 Redis 中,key 为这个 uuid ,并设置一个较长的过期时间(如30分钟)。

      c. 然后,TokenService 使用 JWT 工具,将用户的部分信息(如 userId)和这个 token 一起生成为一个最终的 JWT 字符串。

    7. 登陆成功,则后端将这个 JWT 令牌返回给前端。前端会将其存储在 localStorageCookie 中。

阶段三:后续请求与退出登录

  • Step1:前端在发起后续的业务请求时,会在请求头(Header)中携带这个 JWT 令牌(格式为 Bearer <token>

  • Step2:后端进行 Token 校验。

    1. 首先登陆后的请求,会被JwtAuthenticationTokenFilter 过滤器拦截,从请求头中获取 token,然后使用 JWT 工具解析,并从 Redis 中获取缓存的用户信息
    2. 如果 token 有效且未过期,则将用户信息存入 SecurityContextHolder,代表用户已登录,请求得以继续。
  • 退出登录:用户点击退出时,前端调用退出接口。后端的 LogoutFilter 会拦截该请求,并从 Redis 中删除对应的 token 缓存,完成登出。

功能权限

功能权限表结构分析

功能权限(菜单权限、按钮权限、接口权限)核心都是依赖sys_menu表中的perms字段进行鉴权

权限数据获取

user-id

role-id

menu-id

前端传

sys-user-role

sys-role-menu

获取 sys-menu 的 perms

功能权限表关系

user-id

role-id

role-id

menu-id

sys-user

sys-user-role

sys-role

sys-role-menu

sys-menu

  1. sys_user(用户表) 与 sys_role(角色表)是多对多关系,两者的关系存储在 sys_user_role(用户角色关联关系表) 中
  2. sys_role(角色表)与 sys_menu(菜单表)是多对多关系,两者的关系存储在 sys_role_menu(角色菜单关联关系表)中

PS:权限数据获取的流程图,并不是说从数据库中查询,而是表示数据源来自哪里,实际上有的数据是直接从SpringSecurity 上下文中拿的,并没有去查数据库表

菜单权限

相关接口getRouters

前端相关代码ruoyi-ui/src/permission.js

后端相关代码com.ruoyi.web.controller.system.SysLoginController#getRouters

在学习菜单权限之前,我们需要搞清楚menu_type字段,通过sys_menu 表我们可以发现菜单还有按钮的权限都是通过 perms 字段来存储的,其中 menu_type 字段用于区分类型,“M” 和 “C” 代表菜单,“F” 代表按钮

  • 目录(M):只用来分组,没有实际页面,点击不能跳转

  • 菜单(C):对应具体页面,点击能跳转,有对应的 Vue 组件

  • 按钮(F):对应具体的按钮

菜单权限流程分析

菜单权限流程解析:

  • Step1:前端发送 getRouters 请求

    首次登录或者页面刷新就会触发ruoyi-ui/src/permission.js中前置路由守卫调用,向后端发送 getRouters 请求

    image-20260330191312013

  • Step2:后端响应数据

    后端会根据当前登录用户的 id 到数据库中查出当前用户所拥有角色所有有的菜单,具体查询链路就是

    1. 从 Security 上下文中拿到当前用户的 userId
    2. 通过 userId 从 sys_user_role 表中找出当前用户所拥有的角色 roleIds
    3. 通过 roleIds 从 sys_role_menu 表中找出当前用户所有的可访问菜单 menuIds
    4. 通过 menuIds 从 sys_menu 表中找出所有的菜单信息

    具体的 SQL 如下:

    		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
    		from sys_menu m
    			 left join sys_role_menu rm on m.menu_id = rm.menu_id
    			 left join sys_user_role ur on rm.role_id = ur.role_id
    			 left join sys_role ro on ur.role_id = ro.role_id
    			 left join sys_user u on ur.user_id = u.user_id
    		where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0  AND ro.status = 0
    		order by m.parent_id, m.order_num
    

    后端查出当前用户的菜单数据后当前是扁平化的,因为前端需要使用动态路由,所以还需要构造为子父级格式,方便前端处理,具体的处理逻辑参考

    com.ruoyi.system.service.impl.SysMenuServiceImpl#getChildPerms

  • Step3:前端渲染数据

    前端在接收到后端返回的菜单数据后,直接将其添加为路由

    image-20260330191608475

菜单权限实战示例

这里演示新增一个菜单,并配置一个菜单权限

  • Step1:生成一个页面。

    这里我们借助 Ruoyi-Vue 提供的代码生成功能,为 tb_book 表生成一个页面

    1. 建表

      -- 图书表
      CREATE TABLE `tb_book` (
        `book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID',
        `book_name` varchar(100) DEFAULT NULL COMMENT '书名',
        `author` varchar(50) DEFAULT NULL COMMENT '作者',
        `price` decimal(10,2) DEFAULT NULL COMMENT '价格',
        `publish_date` date DEFAULT NULL COMMENT '出版日期',
        `description` varchar(500) DEFAULT NULL COMMENT '简介',
        `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
        `create_time` datetime DEFAULT NULL COMMENT '创建时间',
        `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
        `update_time` datetime DEFAULT NULL COMMENT '更新时间',
        `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
        PRIMARY KEY (`book_id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='图书信息表';
      

      image-20260331142557946

    2. 生成代码:点击生成代码按钮后,会下载一个下压缩包,这个压缩包就是生成的代码

      image-20260331142639144

    3. 执行SQL,放置代码:解压压缩包,发现里面不光有代码,还有SQL,这是因为我们前端是采用了动态路由,展示的菜单需要从后端获取,这个 SQL 的作用就是往 sys_menu 添加数据,这样前端就能够把我们前端的菜单展示出来了

      image-20260331142916310

      PS:其实这里也可以不执行SQL,选择手动到菜单管理哪里添加

  • Step2:配置菜单权限,其实这一步已经通过执行 bookMenu.sql 完成了,这里我们就讲解一下如何手动配置菜单权限

    image-20260331152109641

    这里介绍以下几个比较核心的配置项

    1. 路由地址:用于路由缓存、跳转定位,建议与组件名保持一致,避免冲突
    2. 组件路径:对应 Vue 路由的path属性,同时这个组件路径 system/user/index也对应具体页面的路径位置,比如src/views/system/user/index.vue,如果 index.vue 的位置发生了变化,组件路径需要同步修改,负责页面会无法正常展示
    3. 权限字符:菜单的权限标识符,目录菜单无需配置,页面菜单需要配置,通常配置为system:user:list,用于控制页面可见性,list 接口的权限标识符通常也是system:user:list,不同它是用于控制数据的可见性,两者共用(都存储在 sys_menuperms 字段中),但是作用不同

按钮权限

相关接口:getInfo

前端相关代码:ruoyi-ui/src/directive/permission/hasPermi.js

后端相关代码:com.ruoyi.web.controller.system.SysLoginController#getInfo

按钮权限流程分析

按钮权限在 RuoYi-Vue 中主要通过自定义操作符 v-hasRolev-hasPermi 来实现,这里以 v-hasPermi 为例,介绍按钮权限流程解析(v-hasRole鉴权逻辑是一样的):

  • Step1:前端发送getInfo请求

    首次登录或者页面刷新就会触发ruoyi-ui/src/permission.js中前置路由守卫调用,向后端发送 getInfo请求

    image-20260330194446604

  • Step2:后端响应数据

    后端会根据当前登录用户的 id 到数据库中查出当前用户所拥有角色所有有的权限操作符,具体查询链路就是

    1. 从 Security 上下文中拿到当前用户的 userId
    2. 通过 userId 从 sys_user_role 表中找出当前用户所拥有的角色 roleIds
    3. 通过 roleIds 从 sys_role_menu 表中找出当前用户所有的可访问菜单 menuIds
    4. 通过 menuIds 从 sys_menu 表中找出所有菜单的权限信息(也就是 perms 字段信息)

    具体的代码逻辑请参考com.ruoyi.web.controller.system.SysLoginController#getInfo

  • Step3:前端渲染数据

    前端拿到后端的数据后,会暂存在 store 中,每次页面加载时,会触发自定义鉴权指令的函数,进而判断当前用户是否拥有该按钮的权限操作符,如果没有就不展示(物理上移除DOM),有就展示。

    具体的判断代码请参考 ruoyi-ui/src/directive/permission/hasPermi.js

    image-20260330195007233

    image-20260330195044928

按钮权限实战示例

这里演示新增一个按钮,然后演示如何设置按钮权限项

  • Step1:新增按钮

    这里我们直接沿用菜单权限实战示例中生成的代码,那个 index.vue 里面已经有按钮了

  • Step2:配置权限

    1. 前端代码配置好 v-hasPermi 指令

      image-20260331154331438

    2. 后端数据库添加按钮权限数据

      同样的在 bookMenu.sql 中已经包含了,这里我们介绍一下手动配置,我们来到菜单管理页,在我们新增的页面下新增一个按钮菜单,配置好权限字符即可,例如这里配置好了一个用户管理页下的用户查询按钮的权限

      image-20260331154945668

接口权限

前面已经讲了按钮权限,按钮权限通过前端控制使得按钮不可见,从而杜绝用户越权操作,这是对于一般用户而言,对于懂技术的用户,可能会直接越过前端,直接向后端发起接口请求,此时我们就需要配置接口权限,从这里我们也可以看出来接口权限是对按钮权限的补充

后端相关代码com.ruoyi.framework.web.service.PermissionService#hasPermi

接口权限流程分析

接口权限主要借助 SpringSecurity 的 @PreAuthorize 注解实现,核心原理其实是 AOP

以下是对接口权限流程的解析:

  • Step1:前端发送请求

  • Step2:后端接收请求,此时如果前端请求的接口添加了 @PreAuthorize 注解,比如

    @PreAuthorize("@ss.hasPermi('system:user:list')")

    此时请求会被拦截鉴权,ss 是注入 IOC 的 Bean 的名字,在 RuoYi-Vue 中对应

    com.ruoyi.framework.web.service.PermissionService 这个类,hasPermi 是这个类的一个具体的鉴权方法,他会从 SpringSecurity 上下文中获取当前登录用的所有权限操作符,然后比较一下是否包含了“system:user:list”这个指定的权限操作符,如果包含了就鉴权成功,接口可以访问,然后就是走接口对应的 service 一系列方法并响应数据给前端,如果不包含说明鉴权失败,直接拒绝访问返回“500 + 提示 “权限不足”

    image-20260330202040734

    image-20260330202044496

接口权限实战示例

接口权限实际上就是按钮权限的一个补充,接口权限标识符与按钮权限标识符是共用的

  1. Step1:配置接口权限数据

    因为权限标识符和按钮权限是共用的,所以这里可以参考按钮权限数据的配置,两者都是在 sys_menu 中添加一条数据,并补充 perms 字段

  2. Step2:后端添加接口权限注解

    image-20260331161122155

数据权限

Ruo-Vue 中数据权限主要依赖于 AOP+SQL动态拼接 实现,核心注解是 @DataScope 注解,它是基于 “用户-角色-部门” 模型实现,是 RBAC 模型的一种具体实现,是一种行级数据权限,控制用户只能看到权限范围内的数据行。

数据权限按照数据范围进行分类:

  • 行级数据权限:用于控制能看到那些行的数据,依赖于在 where 条件后动态拼 SQL 实现
  • 列级数据权限:也叫字段级数据权限,用于控制能看到那些 列/字段 的数据,依赖于动态修改 select 后的字段(或序列化时过滤)实现

数据权限表结构分析

数据权限表关系

user-id

role-id

dept-id

role-id

dept-id

sys-user

sys-user-role

sys-role

sys-dept

sys-role-dept

  1. sys_user(用户表) 与 sys_dept (部门表)是一对多的关系,一个用户只能拥有一个部门,一个部门下可以有多个用户,关系依赖于 sys_user 表中的 dept_id 字段维护
  2. sys_user(用户表) 与 sys_role (角色表)是多对多关系,两者的关系存储在 sys_user_role (用户角色关联关系表)中
  3. sys_role(角色表)与 sys_dept(部门表)是多对多关系,两者的关系存储在sys_role_dept(角色部门关联关系表)中

数据权限注解参数介绍

在 RuoYi-Vue 中,数据权限核心注解是 @DataScope,他有三个参数

参数名 默认值 作用说明 生效场景 示例
deptAlias 空字符串 SQL 中部门表的别名,用于拼接 dept_id 过滤条件 本部门、本部门及以下、自定义部门 deptAlias = "d"
userAlias 空字符串 SQL 中用户表的别名,用于拼接 user_id 过滤条件 仅本人数据权限 userAlias = "u"
permission 空字符串 权限字符,多角色场景下指定生效的权限标识,多个用逗号分隔 用户拥有多个角色时,精准匹配该接口应使用的角色数据权限 permission = "system:user:list"

数据权限类型介绍

此外数据权限的类型,通过 sys_role 表的 data_scope 字段区分,总共5中类型

范围 标识 说明 生成的 SQL 片段示例
全部数据 1 无限制 空字符串
自定义部门 2 自选可访问部门 AND d.dept_id IN (101,102)
本部门 3 仅当前用户部门 AND d.dept_id = 100
本部门及以下 4 当前部门 + 所有子部门 AND d.dept_id IN (100,101,102)
仅本人 5 仅自己创建的数据 AND u.user_id = 1001

数据权限流程解析

数据权限流程解析:

  • Step1:前端发送请求

  • Step2:后端接收请求,此时如果前端请求的接口添加了 @DataScope注解,则会被切面类的前置通知 进行逻辑处理,接下来我们以 data_scope 为 3 的用户进行具体的流程分析

    1. 拼接权限 SQL 前先清空 params.dataScope 参数防止注入
    2. 从 Security 的上下文中获取当前登录用户
    3. 获取当前用户所拥有的权限操作符(如果数据权限注解的permission参数未指定)
    4. 从当前登录用户中获取对应所有的角色
    5. 然后遍历所有的角色,根据 data_scope 字段拼接SQL(以 OR d.dept_id = user.deptId 拼接)
    6. 最后将第一个 OR 替换为 AND,比如 OR d.dept_id = 2 替换为 AND d.dept_id = 2
    7. 最终执行的 SQL,我们只需要在 where 条件之后拼接一个 ${params.dataScope}

    详情代码请参考com.ruoyi.framework.aspectj.DataScopeAspect#doBefore

    下面是具体的流程图:

    开始

    是否有 DataScope 注解?

    获取当前用户

    执行SQL

    是否是超级管理员?

    遍历所有角色

    角色有效且匹配权限?

    所有角色都没有数据权限
    OR dept_id = 0

    全部数据权限
    不拼接SQL

    自定义数据
    OR dept_id IN ...

    部门级数据权限
    OR dept_id = ?

    部门及以下数据权限
    OR dept_id IN ...

    仅本人数据权限
    OR user_id = ?

    构建最终 SQL 条件

    最终SQL存入BaseEntity 的 params.dataScope

    MyBatis 查询时动态拼接

    返回结果

可能疑惑的点:

  • 如果一个 SQL 既要给有数据权限的 Service 使用,又要给不需要数据权限的 Service 使用,拼接 ${params.dataScope} 的 SQL 能复用吗?

    SQL 能复用。从前面的流程分析过程来看,params.dataScope 只有当我们走 AOP 切面时,才会被设置,如果我们不走,该参数不会被设置,此时是空字符串,我们在执行的 SQL 后面拼接一个空字符串,这个 SQL 照样能够被正常执行

数据权限实战示例

现在我们基于 RuoYi-Vue 开发一个管理系统,有如下的需求:

  1. 同一部门的员工只能看自己和本部门的数据;
  2. 部门领导可以看本部门所有人的数据;
  3. 管理员可以看全部数据。

实现思路

  • 通过 @DataScope 注解注入数据权限 SQL
  • 在 Mapper 层自动拼接 dept_id/user_id 过滤条件

具体实现

  • Step1:实体类

    class User {
        Long userId;
        Long deptId;
        // ...
    }
    
  • Step2:添加数据权限注解

    一般都是加在 Service 接口上,也可以加载 Controller 或者 Mapper 接口上

    	@DataScope(deptAlias = "d", userAlias = "u")
        List<User> getUserList();
    
  • Step3:为用户配置数据权限

    这个需要在后台管理页面中使用管理员账号进行配置,需要新增三个角色,部门领导、普通员工、管理员(普通员工和管理员角色已经有了,只需要新增部门领导角色即可)

    1. 新增角色

      image-20260403163234401

      配置完后,数据库 sys_role 表里面会多一条记录

    2. 配置数据权限

      image-20260403162842201

      image-20260403163328328

      配置完后,数据库 sys_role 表部门领导角色这条记录的 data_scope 字段会变为 4

    3. 分配用户,将配好了数据权限的角色分配给具体的用户即可

      image-20260403163756807

      image-20260403163737194

  • Step4:编写 SQL

    只需要在 SQL where 条件上拼接上 ${params.dataScope} 即可

        <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
    		select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
    		left join sys_dept d on u.dept_id = d.dept_id
    		where u.del_flag = '0'
    		<if test="userId != null and userId != 0">
    			AND u.user_id = #{userId}
    		</if>
    		<if test="userName != null and userName != ''">
    			AND u.user_name like concat('%', #{userName}, '%')
    		</if>
    		<if test="status != null and status != ''">
    			AND u.status = #{status}
    		</if>
    		<if test="phonenumber != null and phonenumber != ''">
    			AND u.phonenumber like concat('%', #{phonenumber}, '%')
    		</if>
    		<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
    			AND date_format(u.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
    		</if>
    		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
    			AND date_format(u.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
    		</if>
    		<if test="deptId != null and deptId != 0">
    			AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
    		</if>
    		<!-- 数据范围过滤 -->
    		${params.dataScope}
    	</select>
    
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐