Files
junhong_cmp_fiber/openspec/specs/role-permission/spec.md
huang 5556b1028c feat(role): 新增平台角色管理功能增强
- 权限表增加 available_for_role_types 字段,支持标记权限可用角色类型
- 权限列表和权限树接口支持按 available_for_role_type 过滤
- 新增角色状态切换接口 PUT /api/admin/roles/:id/status
- 角色分配权限时验证权限的可用角色类型
- 完善数据库迁移脚本和单元测试
- 补充数据库迁移相关开发规范文档
2026-01-14 12:15:57 +08:00

224 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# role-permission Specification
## Purpose
TBD - created by archiving change add-role-permission-system. Update Purpose after archive.
## Requirements
### Requirement: 角色类型定义
系统 SHALL 定义两种角色类型平台角色role_type=1用于平台用户的职责区分客户角色role_type=2用于代理和企业账号的能力边界控制。
#### Scenario: 创建平台角色
- **WHEN** 创建角色时指定 role_type = 1
- **THEN** 系统创建平台角色,该角色只能分配给平台用户
#### Scenario: 创建客户角色
- **WHEN** 创建角色时指定 role_type = 2
- **THEN** 系统创建客户角色,该角色可分配给代理账号或企业账号
#### Scenario: 角色类型常量使用
- **WHEN** 代码中需要判断角色类型
- **THEN** 必须使用 constants.RoleTypePlatform、constants.RoleTypeCustomer 常量
---
### Requirement: 权限端口属性
系统 SHALL 在权限表添加 `platform` 字段用于标识权限的适用端口all全部、web仅Web后台、h5仅H5端。默认值为 all。同时权限表应包含 `available_for_role_types` 字段VARCHAR(20)),用于标记权限可分配给哪些角色类型,默认值为 `'1,2'`
#### Scenario: 创建通用权限
- **WHEN** 创建权限时 platform = 'all' 或未指定
- **THEN** 该权限在 Web 后台和 H5 端均可用
#### Scenario: 创建Web专用权限
- **WHEN** 创建权限时 platform = 'web'
- **THEN** 该权限仅在 Web 后台可用H5 端无法使用
#### Scenario: 创建H5专用权限
- **WHEN** 创建权限时 platform = 'h5'
- **THEN** 该权限仅在 H5 端可用Web 后台无法使用
#### Scenario: 按端口过滤权限列表
- **WHEN** 前端请求用户权限列表时指定 platform 参数
- **THEN** 系统返回 platform 为指定值或 'all' 的权限
#### Scenario: 按可用角色类型过滤权限列表
- **WHEN** 调用权限列表接口时传递 `available_for_role_type` 参数
- **THEN** 系统返回 `available_for_role_types` 包含指定角色类型的权限
---
### Requirement: 角色类型与用户类型匹配
系统 SHALL 在分配角色时校验角色类型与用户类型的匹配关系:平台用户只能分配平台角色,代理/企业账号只能分配客户角色,超级管理员和个人客户不分配角色。
#### Scenario: 平台用户分配平台角色
- **WHEN** 为平台用户user_type=2分配平台角色role_type=1
- **THEN** 系统允许分配
#### Scenario: 平台用户分配客户角色
- **WHEN** 为平台用户user_type=2分配客户角色role_type=2
- **THEN** 系统拒绝分配并返回错误"角色类型与账号类型不匹配"
#### Scenario: 代理账号分配客户角色
- **WHEN** 为代理账号user_type=3分配客户角色role_type=2
- **THEN** 系统允许分配
#### Scenario: 代理账号分配平台角色
- **WHEN** 为代理账号user_type=3分配平台角色role_type=1
- **THEN** 系统拒绝分配并返回错误"角色类型与账号类型不匹配"
#### Scenario: 企业账号分配客户角色
- **WHEN** 为企业账号user_type=4分配客户角色role_type=2
- **THEN** 系统允许分配
#### Scenario: 超级管理员分配角色
- **WHEN** 尝试为超级管理员user_type=1分配任何角色
- **THEN** 系统拒绝分配并返回错误"超级管理员不需要分配角色"
---
### Requirement: 账号角色数量限制
系统 SHALL 对不同用户类型实施角色数量限制:平台用户可分配多个角色,代理账号和企业账号只能分配一个角色。
#### Scenario: 平台用户分配多个角色
- **WHEN** 平台用户已有 N 个角色,再分配第 N+1 个角色
- **THEN** 系统允许分配,该用户拥有 N+1 个角色
#### Scenario: 代理账号分配第一个角色
- **WHEN** 代理账号没有角色,分配第一个角色
- **THEN** 系统允许分配
#### Scenario: 代理账号分配第二个角色
- **WHEN** 代理账号已有一个角色,尝试分配第二个角色
- **THEN** 系统拒绝分配并返回错误"该账号类型只能分配一个角色"
#### Scenario: 企业账号角色数量限制
- **WHEN** 企业账号已有一个角色,尝试分配第二个角色
- **THEN** 系统拒绝分配并返回错误"该账号类型只能分配一个角色"
#### Scenario: 替换代理账号的角色
- **WHEN** 代理账号已有一个角色,需要更换为另一个角色
- **THEN** 系统需要先取消当前角色,再分配新角色
---
### Requirement: 权限端口校验
系统 SHALL 在权限校验时考虑请求来源Web/H5和权限的 platform 属性,只有当权限的 platform 为 'all' 或与请求来源匹配时才允许访问。
#### Scenario: Web请求访问通用权限
- **WHEN** 来自 Web 后台的请求访问 platform='all' 的权限保护接口
- **THEN** 权限校验通过(前提是用户拥有该权限)
#### Scenario: Web请求访问Web权限
- **WHEN** 来自 Web 后台的请求访问 platform='web' 的权限保护接口
- **THEN** 权限校验通过(前提是用户拥有该权限)
#### Scenario: Web请求访问H5权限
- **WHEN** 来自 Web 后台的请求访问 platform='h5' 的权限保护接口
- **THEN** 权限校验失败,返回错误"该权限不适用于当前端口"
#### Scenario: H5请求访问Web权限
- **WHEN** 来自 H5 端的请求访问 platform='web' 的权限保护接口
- **THEN** 权限校验失败,返回错误"该权限不适用于当前端口"
---
### Requirement: 用户权限列表查询
系统 SHALL 提供 API 供前端查询当前登录用户的权限列表,支持按端口和可用角色类型过滤,并返回权限编码列表和菜单树结构。
#### Scenario: 查询全部权限
- **WHEN** 用户调用 GET /api/v1/account/permissions
- **THEN** 系统返回用户拥有的所有权限(权限编码列表 + 菜单树)
#### Scenario: 查询Web端权限
- **WHEN** 用户调用 GET /api/v1/account/permissions?platform=web
- **THEN** 系统返回 platform 为 'all' 或 'web' 的权限
#### Scenario: 查询H5端权限
- **WHEN** 用户调用 GET /api/v1/account/permissions?platform=h5
- **THEN** 系统返回 platform 为 'all' 或 'h5' 的权限
#### Scenario: 按可用角色类型过滤权限列表
- **WHEN** 管理员调用 GET /api/admin/permissions?available_for_role_type=1
- **THEN** 系统返回 `available_for_role_types` 包含 `'1'` 的权限(如 `'1'``'1,2'`
#### Scenario: 构建菜单树
- **WHEN** 返回权限列表时
- **THEN** 系统根据权限的 parent_id 关系构建层级菜单树结构
---
### Requirement: 权限可用角色类型标记
系统 SHALL 在权限表添加 `available_for_role_types` 字段VARCHAR(20)),用于标记该权限可以分配给哪些角色类型。字段值为逗号分隔的角色类型列表(如 `'1'``'2'``'1,2'`),默认值为 `'1,2'`(同时支持平台角色和客户角色)。
#### Scenario: 创建仅限平台角色的权限
- **WHEN** 创建权限时设置 `available_for_role_types = '1'`
- **THEN** 该权限只能分配给平台角色role_type=1不能分配给客户角色
#### Scenario: 创建仅限客户角色的权限
- **WHEN** 创建权限时设置 `available_for_role_types = '2'`
- **THEN** 该权限只能分配给客户角色role_type=2不能分配给平台角色
#### Scenario: 创建通用权限
- **WHEN** 创建权限时设置 `available_for_role_types = '1,2'` 或使用默认值
- **THEN** 该权限可以分配给平台角色和客户角色
#### Scenario: 按可用角色类型过滤权限列表
- **WHEN** 调用权限列表接口时传递 `available_for_role_type=1`
- **THEN** 系统返回 `available_for_role_types` 包含 `'1'` 的权限(如 `'1'``'1,2'`
#### Scenario: 按可用角色类型过滤权限树
- **WHEN** 调用权限树接口时传递 `available_for_role_type=2`
- **THEN** 系统返回 `available_for_role_types` 包含 `'2'` 的权限树结构(如 `'2'``'1,2'`
---
### Requirement: 角色权限分配验证
系统 SHALL 在为角色分配权限时,验证每个权限的 `available_for_role_types` 字段是否包含该角色的 `role_type`。如果权限不可用于该角色类型,系统应拒绝分配并返回错误。
#### Scenario: 为平台角色分配平台权限
- **WHEN** 为 `role_type=1` 的角色分配 `available_for_role_types='1'` 的权限
- **THEN** 系统允许分配
#### Scenario: 为平台角色分配客户专用权限
- **WHEN** 为 `role_type=1` 的角色分配 `available_for_role_types='2'` 的权限
- **THEN** 系统拒绝分配并返回错误"该权限不适用于此角色类型"
#### Scenario: 为客户角色分配通用权限
- **WHEN** 为 `role_type=2` 的角色分配 `available_for_role_types='1,2'` 的权限
- **THEN** 系统允许分配
#### Scenario: 批量分配权限时部分权限不可用
- **WHEN** 为角色批量分配权限,其中部分权限的 `available_for_role_types` 不包含该角色类型
- **THEN** 系统拒绝整个分配操作并返回详细错误信息(列出不可用的权限 ID
---
### Requirement: 角色状态切换接口
系统 SHALL 提供独立的角色状态切换接口 `PUT /api/admin/roles/:id/status`,用于快速启用或禁用角色。接口接受 `status` 参数0=禁用1=启用),并更新角色的状态字段。
#### Scenario: 启用角色
- **WHEN** 调用 `PUT /api/admin/roles/123/status` 并传递 `{ "status": 1 }`
- **THEN** 系统将角色 ID 123 的状态更新为启用status=1
#### Scenario: 禁用角色
- **WHEN** 调用 `PUT /api/admin/roles/456/status` 并传递 `{ "status": 0 }`
- **THEN** 系统将角色 ID 456 的状态更新为禁用status=0
#### Scenario: 角色不存在
- **WHEN** 调用状态切换接口时角色 ID 不存在
- **THEN** 系统返回错误"角色不存在"(错误码 1021
#### Scenario: 无效的状态值
- **WHEN** 调用状态切换接口时传递 `status` 值不为 0 或 1
- **THEN** 系统返回错误"无效的参数"(错误码 1000
---