# 权限模块设计说明 ## 1. 权限架构总览 本系统采用**双层次权限控制架构**: | 层级 | 控制点 | 实现方式 | 范围 | |------|--------|----------|------| | 第一层 - 前端 | 路由访问、按钮显示 | 路由守卫(`meta.roles`)+ `Auth` 组件 / `v-auth` 指令 | 菜单可见性、按钮/操作可见性 | | 第二层 - 后端 | API 接口 | `@RequirePermission` 注解 + AOP 切面 | 接口访问权限校验 | 两个层级独立校验,前端控制用户体验(隐藏无权限的元素),后端控制数据安全(阻止越权请求)。 --- ## 2. 后端权限实现 ### 2.1 核心注解 #### `@RequirePermission` ```java @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequirePermission { String value(); // 权限编码,格式: module:operation String message() default "无操作权限"; // 无权限时的提示信息 } ``` **使用位置**:Controller 方法上,标注该接口所需的权限编码。 #### `@SuperAdmin` ```java public @interface SuperAdmin { String message() default "仅超级管理员可访问"; } ``` **使用位置**:Controller 方法上,标注仅超级管理员可访问的接口。 ### 2.2 权限校验原理(PermissionAspect) 使用 Spring AOP `@Around` 切面拦截所有带有 `@RequirePermission` 的方法,执行流程: 1. 通过 Sa-Token `StpUtil.getLoginId()` 获取当前登录用户 2. 从 Session 中获取用户的角色 ID 列表 3. **超级管理员豁免**:如果用户角色包含 roleId=1(超级管理员),直接放行 4. 通过 `DataPermissionService.getPermissionCodesByRoleIds()` 查询用户拥有的所有权限编码 5. 检查用户权限列表中是否包含接口所需的权限编码 6. 包含则放行,不包含则返回 `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 业务异常抛出 ```java // 使用 ResponseEnum 枚举 throw new BusinessException(ResponseEnum.ORDER_NOT_FOUND); // 自定义消息 throw new BusinessException(404, "订单不存在"); // 仅消息(默认500) throw new BusinessException("操作失败"); ``` ### 2.7 操作日志注解 `@Log` 写操作(增/删/改)必须添加操作日志注解: ```java @Log(module = "模块名", operation = OperationType.XXX, summary = "操作描述") ``` | OperationType | 说明 | |---------------|------| | INSERT | 新增操作 | | UPDATE | 更新操作 | | DELETE | 删除操作 | | SYNC | 同步操作 | | EXPORT | 导出操作 | | IMPORT | 导入操作 | | LOGIN | 登录操作 | | LOGOUT | 登出操作 | | OTHER | 其他操作 | --- ## 3. 前端权限实现 ### 3.1 路由级权限控制 **路由配置**:在 `src/router/modules/` 下的路由配置文件中,通过 `meta.roles` 指定允许访问的角色: ```ts meta: { title: "管理员管理", roles: ["admin"] // 仅 admin 角色可访问 } ``` **路由守卫**:`src/router/utils.ts` 中的 `filterTree` 和 `initRouter` 函数根据用户角色动态过滤路由。 ### 3.2 按钮级权限控制 提供两种方式: #### `v-auth` 指令(基于路由 meta 中的 auth 配置) ```vue 新增 ``` 在路由配置中定义按钮级权限码: ```ts meta: { auths: ["btn.add", "btn.edit", "btn.delete"] } ``` #### `v-perms` 指令(基于用户 permissions 列表) ```vue 新增商品 ``` 用户的 `permissions` 在登录时从后端获取并存储到 `userStore` 中。如果用户拥有 `*:*:*`(超级管理员权限),则所有操作均可执行。 ### 3.3 登录与权限加载流程 1. 调用 `/login` API 登录 2. 后端返回 `accessToken`、`refreshToken`、用户信息(包含角色和权限列表) 3. 前端存储 Token 到 Cookie(`authorized-token`)和 localStorage(`user-info`) 4. `useUserStoreHook().SET_ROLES/SET_PERMS` 存储角色和权限到 Pinia store 5. 路由守卫 `initRouter()` 根据用户角色动态过滤可访问的路由 6. 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 新增功能权限纳入规范 开发新功能时,必须按以下流程确保权限控制完整: 1. Controller 中所有接口必须添加 `@RequirePermission` 注解 2. 写操作接口(增/删/改)必须添加 `@Log` 注解 3. 在 `t_data_permission` 表中添加对应的权限记录 4. 前端菜单配置中添加 `meta.roles` 和按钮级权限码 5. 本文档中补充新增的权限编码 ### 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 |