权限模块设计说明
1. 权限架构总览
本系统采用双层次权限控制架构:
| 层级 |
控制点 |
实现方式 |
范围 |
| 第一层 - 前端 |
路由访问、按钮显示 |
路由守卫(meta.roles)+ Auth 组件 / v-auth 指令 |
菜单可见性、按钮/操作可见性 |
| 第二层 - 后端 |
API 接口 |
@RequirePermission 注解 + AOP 切面 |
接口访问权限校验 |
两个层级独立校验,前端控制用户体验(隐藏无权限的元素),后端控制数据安全(阻止越权请求)。
2. 后端权限实现
2.1 核心注解
@RequirePermission
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
String value(); // 权限编码,格式: module:operation
String message() default "无操作权限"; // 无权限时的提示信息
}
使用位置:Controller 方法上,标注该接口所需的权限编码。
@SuperAdmin
public @interface SuperAdmin {
String message() default "仅超级管理员可访问";
}
使用位置:Controller 方法上,标注仅超级管理员可访问的接口。
2.2 权限校验原理(PermissionAspect)
使用 Spring AOP @Around 切面拦截所有带有 @RequirePermission 的方法,执行流程:
- 通过 Sa-Token
StpUtil.getLoginId() 获取当前登录用户
- 从 Session 中获取用户的角色 ID 列表
- 超级管理员豁免:如果用户角色包含 roleId=1(超级管理员),直接放行
- 通过
DataPermissionService.getPermissionCodesByRoleIds() 查询用户拥有的所有权限编码
- 检查用户权限列表中是否包含接口所需的权限编码
- 包含则放行,不包含则返回
Result.error(403, "无操作权限")
2.3 数据模型
t_data_permission(权限定义表)
| 字段 |
类型 |
说明 |
| id |
BIGINT |
主键(雪花算法) |
| permission_code |
VARCHAR |
权限编码,如 product:create |
| permission_name |
VARCHAR |
权限名称,如 商品创建 |
| module_code |
VARCHAR |
模块编码,如 product |
| module_name |
VARCHAR |
模块名称,如 商品管理 |
| operation_type |
VARCHAR |
操作类型: create/read/update/delete/execute |
| description |
VARCHAR |
权限描述 |
| sort_order |
INT |
排序 |
| status |
INT |
状态: 1-启用, 0-禁用 |
| create_time |
DATETIME |
创建时间 |
| update_time |
DATETIME |
更新时间 |
t_role(角色表)
角色通过 role_id 与 t_role_permission 关联,实现角色-权限多对多关系。
t_role_permission(角色权限关联表)
关联角色与权限的多对多关系。
2.4 权限编码规则
格式:{module_code}:{operation_type}
| 操作类型 |
适用场景 |
说明 |
read |
查询类操作 |
列表、详情、统计、导出 |
create |
创建类操作 |
新增记录 |
update |
更新类操作 |
编辑、修改状态、发布/下线、审核 |
delete |
删除类操作 |
删除、批量删除 |
export |
导出操作 |
导出数据 |
import |
导入操作 |
导入数据 |
execute |
执行操作 |
同步、手动执行 |
distribute |
发放操作 |
发放优惠券 |
review |
审核操作 |
审核提现 |
generate |
生成操作 |
生成报表 |
2.5 权限编码清单
系统管理
| 权限编码 |
权限名称 |
所属模块 |
| admin:read |
管理员查询 |
管理员管理 |
| admin:create |
管理员新增 |
管理员管理 |
| admin:update |
管理员编辑 |
管理员管理 |
| admin:delete |
管理员删除 |
管理员管理 |
| role:read |
角色查询 |
角色管理 |
| role:create |
角色新增 |
角色管理 |
| role:update |
角色编辑 |
角色管理 |
| role:delete |
角色删除 |
角色管理 |
| dict:read |
字典查询 |
字典管理 |
| dict:create |
字典新增 |
字典管理 |
| dict:update |
字典编辑 |
字典管理 |
| dict:delete |
字典删除 |
字典管理 |
| dict:export |
字典导出 |
字典管理 |
| dict:import |
字典导入 |
字典管理 |
运营监控
| 权限编码 |
权限名称 |
所属模块 |
| operation-log:read |
操作日志查询 |
操作日志 |
| operation-log:delete |
操作日志删除 |
操作日志 |
| sync:read |
同步记录查询 |
数据同步 |
| sync:execute |
执行数据同步 |
数据同步 |
公告管理
| 权限编码 |
权限名称 |
所属模块 |
| announcement:read |
公告查询 |
公告管理 |
| announcement:create |
公告新增 |
公告管理 |
| announcement:update |
公告编辑 |
公告管理 |
| announcement:delete |
公告删除 |
公告管理 |
订单管理
| 权限编码 |
权限名称 |
所属模块 |
| order:read |
订单查询 |
订单管理 |
| order:update |
订单编辑/退款审核 |
订单管理 |
商品管理
| 权限编码 |
权限名称 |
所属模块 |
| product:read |
商品查询 |
商品管理 |
| product:create |
商品新增 |
商品管理 |
| product:update |
商品编辑/同步 |
商品管理 |
| product:delete |
商品删除 |
商品管理 |
| product-apply:read |
新品申请查询 |
新品申请 |
| product-apply:create |
新品申请提交 |
新品申请 |
| product-apply:update |
新品申请更新 |
新品申请 |
| product-apply:delete |
新品申请删除 |
新品申请 |
门店管理
| 权限编码 |
权限名称 |
所属模块 |
| shop:read |
门店查询 |
门店管理 |
| shop:create |
门店新增 |
门店管理 |
| shop:update |
门店编辑/设备关联/补货员管理 |
门店管理 |
| shop:delete |
门店删除 |
门店管理 |
设备管理
| 权限编码 |
权限名称 |
所属模块 |
| device:read |
设备查询/详情/统计 |
设备管理 |
| device:update |
设备远程控制/配置 |
设备管理 |
设备告警
| 权限编码 |
权限名称 |
所属模块 |
| device-alert:read |
告警记录查询/统计 |
设备告警 |
库存管理
| 权限编码 |
权限名称 |
所属模块 |
| inventory:read |
库存查询/日志/统计 |
库存管理 |
| inventory:create |
创建上货记录 |
库存管理 |
| inventory:update |
库存调整/上货完成 |
库存管理 |
客户管理
| 权限编码 |
权限名称 |
所属模块 |
| user:read |
客户查询/详情/统计 |
客户管理 |
| user:update |
客户状态/信用分更新 |
客户管理 |
营销中心
| 权限编码 |
权限名称 |
所属模块 |
| dashboard:read |
数据概览 |
数据概览 |
| marketing:activity:read |
活动查询 |
营销活动 |
| marketing:activity:create |
活动创建 |
营销活动 |
| marketing:activity:edit |
活动编辑/暂停/恢复 |
营销活动 |
| marketing:activity:publish |
活动发布 |
营销活动 |
| marketing:activity:delete |
活动删除 |
营销活动 |
| marketing:activity:list |
统一活动列表 |
统一活动 |
| marketing:coupon:read |
优惠券查询 |
优惠券管理 |
| marketing:coupon:create |
优惠券创建 |
优惠券管理 |
| marketing:coupon:edit |
优惠券编辑 |
优惠券管理 |
| marketing:coupon:delete |
优惠券删除 |
优惠券管理 |
| marketing:coupon:distribute |
优惠券发放 |
优惠券管理 |
| marketing:invite:read |
邀请活动查询 |
邀请活动 |
| marketing:invite:create |
邀请活动创建 |
邀请活动 |
| marketing:invite:edit |
邀请活动编辑 |
邀请活动 |
| timed-discount:read |
定时折扣查询 |
定时折扣 |
| timed-discount:create |
定时折扣创建 |
定时折扣 |
| timed-discount:edit |
定时折扣编辑 |
定时折扣 |
| timed-discount:delete |
定时折扣删除 |
定时折扣 |
| timed-discount:execute |
定时折扣执行 |
定时折扣 |
分销管理
| 权限编码 |
权限名称 |
所属模块 |
| distribution:distributor:create |
分销员创建 |
分销管理 |
| distribution:distributor:read |
分销员查询 |
分销管理 |
| distribution:distributor:update |
分销员编辑/激活/停用 |
分销管理 |
| distribution:distributor:delete |
分销员删除 |
分销管理 |
| distribution:referral:read |
推荐记录查询 |
分销管理 |
| distribution:commission:read |
佣金记录查询 |
分销管理 |
| distribution:withdrawal:read |
提现记录查询 |
分销管理 |
| distribution:withdrawal:review |
提现审核/确认到账 |
分销管理 |
| distribution:report:read |
月度报表查询 |
分销管理 |
| distribution:report:generate |
生成月度报表 |
分销管理 |
| distribution:report:export |
导出月度报表 |
分销管理 |
| distribution:dashboard:read |
分销概览 |
分销管理 |
| distribution:config:read |
分销配置查询 |
分销管理 |
| distribution:config:update |
分销配置更新 |
分销管理 |
统计报表
| 权限编码 |
权限名称 |
所属模块 |
| statistics:read |
统计概览 |
统计报表 |
| statistics:category |
品类统计 |
统计报表 |
| statistics:product |
商品统计 |
统计报表 |
| statistics:device |
设备统计 |
统计报表 |
| statistics:shop |
门店统计 |
统计报表 |
| statistics:profit |
利润统计 |
统计报表 |
| statistics:repurchase |
复购统计 |
统计报表 |
| statistics:export |
统计导出 |
统计报表 |
设备层模版
| 权限编码 |
权限名称 |
所属模块 |
| layer-template:read |
层模版查询 |
层模版管理 |
| layer-template:create |
层模版创建 |
层模版管理 |
| layer-template:update |
层模版编辑/同步 |
层模版管理 |
| layer-template:delete |
层模版删除 |
层模版管理 |
签到管理
| 权限编码 |
权限名称 |
所属模块 |
| checkin:read |
签到记录查询 |
签到管理 |
| checkin:create |
签到提交/同步 |
签到管理 |
2.6 业务异常抛出
// 使用 ResponseEnum 枚举
throw new BusinessException(ResponseEnum.ORDER_NOT_FOUND);
// 自定义消息
throw new BusinessException(404, "订单不存在");
// 仅消息(默认500)
throw new BusinessException("操作失败");
2.7 操作日志注解 @Log
写操作(增/删/改)必须添加操作日志注解:
@Log(module = "模块名", operation = OperationType.XXX, summary = "操作描述")
| OperationType |
说明 |
| INSERT |
新增操作 |
| UPDATE |
更新操作 |
| DELETE |
删除操作 |
| SYNC |
同步操作 |
| EXPORT |
导出操作 |
| IMPORT |
导入操作 |
| LOGIN |
登录操作 |
| LOGOUT |
登出操作 |
| OTHER |
其他操作 |
3. 前端权限实现
3.1 路由级权限控制
路由配置:在 src/router/modules/ 下的路由配置文件中,通过 meta.roles 指定允许访问的角色:
meta: {
title: "管理员管理",
roles: ["admin"] // 仅 admin 角色可访问
}
路由守卫:src/router/utils.ts 中的 filterTree 和 initRouter 函数根据用户角色动态过滤路由。
3.2 按钮级权限控制
提供两种方式:
v-auth 指令(基于路由 meta 中的 auth 配置)
<el-button v-auth="['btn.add']">新增</el-button>
在路由配置中定义按钮级权限码:
meta: {
auths: ["btn.add", "btn.edit", "btn.delete"]
}
v-perms 指令(基于用户 permissions 列表)
<el-button v-perms="['product:create']">新增商品</el-button>
用户的 permissions 在登录时从后端获取并存储到 userStore 中。如果用户拥有 *:*:*(超级管理员权限),则所有操作均可执行。
3.3 登录与权限加载流程
- 调用
/login API 登录
- 后端返回
accessToken、refreshToken、用户信息(包含角色和权限列表)
- 前端存储 Token 到 Cookie(
authorized-token)和 localStorage(user-info)
useUserStoreHook().SET_ROLES/SET_PERMS 存储角色和权限到 Pinia store
- 路由守卫
initRouter() 根据用户角色动态过滤可访问的路由
- 401 响应自动触发登出流程
3.4 Token 管理
- Token 存储在 Cookie 和 localStorage 双保险
- 请求头格式:
Authorization: Bearer {token}
- 无感刷新机制:Token 过期时自动调用刷新接口
- 401 响应自动清除 Token 并跳转登录页
4. 完整权限校验流程图
用户请求
|
v
[前端路由守卫] ── 检查 meta.roles
| 无权限 → 跳转 403 页面
| 有权限 → 加载页面
v
[前端按钮级 v-perms/v-auth]
| 无权限 → DOM 元素被移除
| 有权限 → 显示按钮
v
[前端发起 API 请求] ── 携带 Bearer Token
|
v
[Sa-Token 拦截器] ── 验证 Token 有效性
| 无效 → 返回 401
| 有效 → 继续
v
[AOP @RequirePermission 切面]
| 无注解 → 放行
| 有注解:
| 获取用户角色列表
| 是超级管理员(roleId=1) → 放行
| 检查用户权限列表是否包含所需权限
| 不包含 → 返回 403
| 包含 → 放行
v
[Controller 执行业务逻辑]
5. 注意事项与最佳实践
5.1 权限编码命名规范
- 格式:
{module_code}:{operation_type}
module_code 使用小写字母,多词用中划线连接(如 timed-discount、operation-log)
operation_type 使用小写字母
5.2 新增功能权限纳入规范
开发新功能时,必须按以下流程确保权限控制完整:
- Controller 中所有接口必须添加
@RequirePermission 注解
- 写操作接口(增/删/改)必须添加
@Log 注解
- 在
t_data_permission 表中添加对应的权限记录
- 前端菜单配置中添加
meta.roles 和按钮级权限码
- 本文档中补充新增的权限编码
5.3 常见错误
- UserController 缺少权限注解(已修复)—— 客户管理模块完全缺失
@RequirePermission
- DeviceAlertController 缺少权限注解(已修复)—— 设备告警模块完全缺失
@RequirePermission
- LayerTemplateController 临时移除权限(已修复)—— 三个接口的
@RequirePermission 被注释并标注 TODO
- 辅助查询接口缺少权限(已修复)—— 部分下拉选择接口未加权限注解
5.4 接口分类与权限策略
| 接口类型 |
权限策略 |
示例 |
| 查询列表 |
module:read |
@RequirePermission("order:read") |
| 查询详情 |
module:read |
@RequirePermission("order:read") |
| 新增 |
module:create |
@RequirePermission("product:create") |
| 编辑 |
module:update |
@RequirePermission("product:update") |
| 删除 |
module:delete |
@RequirePermission("product:delete") |
| 状态变更 |
module:update |
@RequirePermission("shop:update") |
| 审核 |
module:review |
@RequirePermission("distribution:withdrawal:review") |
| 同步 |
module:update |
@RequirePermission("layer-template:update") |
| 导出 |
module:export |
@RequirePermission("statistics:export") |
| 登录 |
无需权限(@SaIgnore) |
AdminLoginController.login() |
6. 前端菜单与后端权限映射
| 前端菜单 |
后端 Controller |
权限前缀 |
| 数据概览 |
DashboardController |
dashboard:read |
| 订单管理 |
OrderController |
order:read/update |
| 退款管理 |
RefundApplicationController |
order:read/update |
| 门店管理 |
ShopController |
shop:read/create/update/delete |
| 设备管理 |
DeviceController |
device:read/update |
| 门店分布地图 |
(ShopController 复用) |
shop:read |
| 商品列表 |
ProductController |
product:read/create/update/delete |
| 新品申请 |
NewProductApplyController |
product-apply:read/create/update/delete |
| 库存管理 |
InventoryController |
inventory:read/create/update |
| 客户管理 |
UserController |
user:read/update |
| 营销活动 |
MarketingActivityController / UnifiedActivityController |
marketing:activity:* |
| 优惠券管理 |
CouponController |
marketing:coupon:* |
| 定时折扣 |
TimedDiscountController |
timed-discount:* |
| 邀请活动 |
InviteActivityAdminController |
marketing:invite:* |
| 分销管理 |
DistributorAdminController |
distribution:* |
| 统计报表 |
StatisticsController |
statistics:* |
| 管理员管理 |
AdminController |
admin:read/create/update/delete |
| 角色管理 |
RoleController |
role:read/create/update/delete |
| 权限管理 |
DataPermissionController |
role:read/update |
| 公告管理 |
AnnouncementController |
announcement:* |
| 操作日志 |
OperationLogController |
operation-log:read/delete |
| 数据同步 |
DataSyncController |
sync:read/execute |
| 字典管理 |
DictController |
dict:read/create/update/delete/export/import |
| 签到管理 |
CheckinController |
checkin:read/create |
| 设备告警 |
DeviceAlertController |
device-alert:read |
| 层模版管理 |
LayerTemplateController |
layer-template:* |
7. 权限修复记录
2026-04-29 修复清单
| 文件 |
问题 |
修复内容 |
| UserController.java |
5个接口完全缺失@RequirePermission |
新增 user:read(列表/详情/统计), user:update(状态/信用分) |
| DeviceAlertController.java |
2个接口完全缺失@RequirePermission |
新增 device-alert:read(列表/统计) |
| LayerTemplateController.java |
3个接口TEMP注释移除权限 |
恢复 layer-template:read(list), layer-template:update(sync/batchSync) |
| DictController.java |
getDictDataList缺少@RequirePermission |
新增 dict:read |
| DictController.java |
refreshCache缺少@RequirePermission和@Log |
新增 dict:update + 操作日志 |
| RolePermissionMapper.xml |
自定义batchInsert未包含id列,雪花ID不生效 |
改为实体insert方式,利用@TableId自动生成ID |
| haha-admin, haha-miniapp application.yml |
全局缺少db-config.id-type兜底配置 |
新增 db-config.id-type: ASSIGN_ID |
| MarketingActivityController.java |
getOngoingActivities缺少@RequirePermission |
新增 marketing:activity:read |
| TimedDiscountController.java |
getEnabledActivities缺少@RequirePermission |
新增 timed-discount:read |
| CouponController.java |
getAvailableTemplates缺少@RequirePermission |
新增 marketing:coupon:read |
| ShopController.java |
getEnabledShops缺少@RequirePermission |
新增 shop:read |