Files
leaudit-platform-frontend/auth_doc/通用权限前端对接文档.md
T
LiangShiyong d4000cd292 fix: 1. 继续对齐交叉评查的接口,完善创建交叉评查的逻辑 和 相关组件的渲染布局。
2. 文档的基本信息修改改用接口。      3. 重新完善角色权限管理的页面逻辑。     4.将评查点列表中的返回逻辑改用浏览器的记忆返回。
2025-12-12 12:00:36 +08:00

1425 lines
35 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.
# RBAC 权限管理前端对接文档
## 概述
本文档详细说明 RBAC(基于角色的访问控制)模块的所有 API 接口和业务逻辑,包括:
- 角色管理(5个接口)
- 权限管理(5个接口)
- 角色权限关联(4个接口)
- 用户角色管理(4个接口)
- 路由管理(3个接口)
**总计:21个 API 接口**
---
## 目录
1. [核心概念](#核心概念)
2. [角色管理 API](#角色管理-api)
3. [权限管理 API](#权限管理-api)
4. [角色权限关联 API](#角色权限关联-api)
5. [用户角色管理 API](#用户角色管理-api)
6. [路由管理 API](#路由管理-api)
7. [业务逻辑详解](#业务逻辑详解)
8. [前端实现指南](#前端实现指南)
---
## 核心概念
### 角色层级
| 角色 | role_key | 说明 | 数据范围 |
|------|----------|------|----------|
| 省级管理员 | `provincial_admin` | 最高权限,管理所有地区 | ALL |
| 市级管理员 | `admin` | 管理本地区用户 | DEPT |
| 普通员工 | `common` | 普通操作权限 | SELF |
### 数据范围 (data_scope)
| 值 | 说明 |
|----|------|
| `ALL` | 全部数据(所有地区) |
| `DEPT` | 部门数据(本地区) |
| `SELF` | 个人数据(仅自己) |
### 权限类型 (permission_type)
| 值 | 说明 |
|----|------|
| `API` | API 接口权限 |
| `MENU` | 菜单访问权限 |
| `BUTTON` | 按钮操作权限 |
### 授权类型 (grant_type)
| 值 | 说明 |
|----|------|
| `GRANT` | 授予权限 |
| `DENY` | 拒绝权限 |
### 权限类型(独立/通用)
| 类型 | route_id | related_routes | 说明 |
|------|----------|----------------|------|
| **独立权限** | 有值(如58) | NULL | 只属于单个页面 |
| **通用权限** | NULL | 有值(如[58,37] | 被多个页面共享 |
### 关键数据表
| 表名 | 说明 |
|------|------|
| `roles` | 角色定义表 |
| `permissions` | 权限定义表 |
| `role_permissions` | 角色-权限关联表 |
| `user_role` | 用户-角色关联表 |
| `sys_routes` | 前端路由配置表 |
| `sso_users` | 用户信息表 |
| `rbac_audit_logs` | RBAC审计日志表 |
---
## 角色管理 API
### 1. 获取角色列表
**请求**
```http
GET /api/v3/rbac/roles?page=1&page_size=20&role_name=&include_system=true
```
**权限要求**: 仅需登录(所有用户可访问,返回数据根据角色过滤)
**Query 参数**
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| page | int | 否 | 1 | 页码 |
| page_size | int | 否 | 20 | 每页数量(1-100 |
| role_key | string | 否 | - | 角色标识精确过滤 |
| role_name | string | 否 | - | 角色名称模糊搜索 |
| include_system | bool | 否 | true | 是否包含系统角色 |
**响应**
```json
{
"total": 10,
"page": 1,
"page_size": 20,
"items": [
{
"id": 1,
"role_key": "provincial_admin",
"role_name": "省级管理员",
"description": "系统最高权限管理员",
"data_scope": "ALL",
"is_system": true,
"user_count": 5,
"permission_count": 120,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00",
"metadata": null
},
{
"id": 2,
"role_key": "admin",
"role_name": "市级管理员",
"description": "地市级管理员",
"data_scope": "DEPT",
"is_system": true,
"user_count": 20,
"permission_count": 80,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00",
"metadata": null
}
]
}
```
**业务逻辑**
- 所有登录用户都可以查看角色列表
- 角色是全局统一的,不区分地区
- 返回数据包含用户数量和权限数量统计
---
### 2. 获取角色详情
**请求**
```http
GET /api/v3/rbac/roles/{role_id}
```
**权限要求**: `system:rbac:manage`
**Path 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_id | int | 是 | 角色ID |
**响应**
```json
{
"id": 2,
"role_key": "admin",
"role_name": "市级管理员",
"description": "地市级管理员",
"data_scope": "DEPT",
"is_system": true,
"permissions": [
{
"id": 1,
"permission_id": 10,
"permission_key": "document:list:read",
"display_name": "查看文档列表",
"grant_type": "GRANT",
"data_scope": "DEPT"
},
{
"id": 2,
"permission_id": 11,
"permission_key": "document:detail:read",
"display_name": "查看文档详情",
"grant_type": "GRANT",
"data_scope": "DEPT"
}
],
"metadata": null,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00"
}
```
---
### 3. 创建角色
**请求**
```http
POST /api/v3/rbac/roles
Content-Type: application/json
{
"role_key": "reviewer",
"role_name": "",
"description": "",
"data_scope": "DEPT",
"metadata": {
"department": ""
}
}
```
**权限要求**: `system:rbac:manage`
**Request Body**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_key | string | 是 | 角色标识(唯一,只能包含小写字母、数字、下划线) |
| role_name | string | 是 | 角色名称 |
| description | string | 否 | 角色描述 |
| data_scope | string | 否 | 数据范围(ALL/DEPT/SELF,默认SELF |
| metadata | object | 否 | 扩展元数据 |
**响应**
```json
{
"id": 10,
"role_key": "reviewer",
"role_name": "评查员",
"description": "负责文档评查工作",
"data_scope": "DEPT",
"is_system": false,
"permissions": [],
"metadata": {"department": "评查部"},
"created_at": "2025-12-11T10:00:00",
"updated_at": "2025-12-11T10:00:00"
}
```
**业务逻辑**
- `role_key` 只能包含小写字母、数字、下划线,且必须以字母开头
- `role_key` 必须唯一
- 新创建的角色 `is_system` 默认为 `false`
- 操作会记录到审计日志
---
### 4. 更新角色
**请求**
```http
PUT /api/v3/rbac/roles/{role_id}
Content-Type: application/json
{
"role_name": "",
"description": "",
"data_scope": "ALL"
}
```
**权限要求**: `system:rbac:manage`
**Request Body**(所有字段可选)
| 参数 | 类型 | 说明 |
|------|------|------|
| role_name | string | 角色名称 |
| description | string | 角色描述 |
| data_scope | string | 数据范围 |
| metadata | object | 扩展元数据 |
**响应**
```json
{
"id": 10,
"role_key": "reviewer",
"role_name": "高级评查员",
"description": "负责高级文档评查工作",
"data_scope": "ALL",
"is_system": false,
"permissions": [],
"metadata": {"department": "评查部"},
"created_at": "2025-12-11T10:00:00",
"updated_at": "2025-12-11T11:00:00"
}
```
**业务逻辑**
- `role_key` 不可修改
- 系统角色(`is_system=true`)有额外限制
- `provincial_admin` 角色禁止修改 `data_scope`
- 操作会记录到审计日志
---
### 5. 删除角色
**请求**
```http
DELETE /api/v3/rbac/roles/{role_id}?force=false
```
**权限要求**: `system:rbac:manage`
**Query 参数**
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| force | bool | 否 | false | 强制删除(自动解除用户关联) |
**响应**
```json
{
"code": 200,
"message": "角色删除成功: 评查员",
"data": {
"role_id": 10,
"deleted_users": 5
}
}
```
**业务逻辑**
- 系统角色(`is_system=true`)禁止删除
- `provincial_admin` 角色绝对禁止删除
- 删除时自动解除用户关联、角色权限关联
- 操作会记录到审计日志
---
## 权限管理 API
### 1. 获取权限列表
**请求**
```http
GET /api/v3/rbac/permissions?format=tree&module=&permission_type=&include_system=true
```
**权限要求**: `system:rbac:manage`
**Query 参数**
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| format | string | 否 | tree | 返回格式(tree/flat |
| module | string | 否 | - | 按模块过滤 |
| permission_type | string | 否 | - | 权限类型过滤(API/MENU/BUTTON |
| include_system | bool | 否 | true | 是否包含系统权限 |
**响应(树形格式)**
```json
[
{
"id": 1,
"permission_key": "document:*:*",
"display_name": "文档管理",
"description": "文档管理模块全部权限",
"module": "document",
"resource": "*",
"action": "*",
"permission_type": "API",
"is_system": true,
"parent_id": null,
"sort_order": 0,
"children": [
{
"id": 2,
"permission_key": "document:list:read",
"display_name": "查看文档列表",
"module": "document",
"resource": "list",
"action": "read",
"permission_type": "API",
"is_system": false,
"parent_id": 1,
"sort_order": 0,
"children": []
}
]
}
]
```
**业务逻辑**
- 树形格式:按 `parent_id` 构建层级结构
- 平铺格式:直接返回列表
---
### 2. 获取权限详情
**请求**
```http
GET /api/v3/rbac/permissions/{permission_id}
```
**权限要求**: `system:rbac:manage`
**响应**
```json
{
"id": 10,
"permission_key": "document:list:read",
"display_name": "查看文档列表",
"description": "允许查看文档列表",
"module": "document",
"resource": "list",
"action": "read",
"permission_type": "API",
"is_system": false,
"parent_id": 1,
"sort_order": 0,
"metadata": null,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00"
}
```
---
### 3. 创建权限
**请求**
```http
POST /api/v3/rbac/permissions
Content-Type: application/json
{
"permission_key": "evaluation:result:export",
"display_name": "",
"description": "Excel",
"module": "evaluation",
"resource": "result",
"action": "export",
"permission_type": "API",
"parent_id": null,
"sort_order": 10
}
```
**权限要求**: `system:rbac:manage`
**Request Body**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| permission_key | string | 是 | 权限键(格式:module:resource:action |
| display_name | string | 是 | 显示名称 |
| description | string | 否 | 权限描述 |
| module | string | 否 | 模块名(可从permission_key自动解析) |
| resource | string | 否 | 资源名(可从permission_key自动解析) |
| action | string | 否 | 操作名(可从permission_key自动解析) |
| permission_type | string | 否 | 权限类型(API/MENU/BUTTON,默认API |
| parent_id | int | 否 | 父权限ID |
| sort_order | int | 否 | 排序顺序(默认0) |
| metadata | object | 否 | 扩展元数据 |
**响应**
```json
{
"id": 100,
"permission_key": "evaluation:result:export",
"display_name": "导出评查结果",
"description": "允许导出评查结果为Excel",
"module": "evaluation",
"resource": "result",
"action": "export",
"permission_type": "API",
"is_system": false,
"parent_id": null,
"sort_order": 10,
"metadata": null,
"created_at": "2025-12-11T10:00:00",
"updated_at": "2025-12-11T10:00:00"
}
```
**业务逻辑**
- `permission_key` 格式必须是 `module:resource:action`
- `permission_key` 必须唯一
- 如果包含通配符 `*`,自动标记为系统权限
- 如果指定 `parent_id`,必须指向存在的权限
- 操作会记录到审计日志
---
### 4. 更新权限
**请求**
```http
PUT /api/v3/rbac/permissions/{permission_id}
Content-Type: application/json
{
"display_name": "",
"description": "ExcelPDF",
"sort_order": 5
}
```
**权限要求**: `system:rbac:manage`
**Request Body**(所有字段可选)
| 参数 | 类型 | 说明 |
|------|------|------|
| display_name | string | 显示名称 |
| description | string | 权限描述 |
| sort_order | int | 排序顺序 |
| metadata | object | 扩展元数据 |
**业务逻辑**
- `permission_key` 不可修改
- 系统权限有额外限制
- 操作会记录到审计日志
---
### 5. 删除权限
**请求**
```http
DELETE /api/v3/rbac/permissions/{permission_id}?force=false
```
**权限要求**: `system:rbac:manage`
**Query 参数**
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| force | bool | false | 强制删除(自动解除角色关联) |
**响应**
```json
{
"code": 200,
"message": "权限删除成功: 导出评查结果",
"data": {
"permission_id": 100,
"deleted_role_relations": 3
}
}
```
**业务逻辑**
- 系统权限禁止删除
- 有子权限的权限禁止删除(必须先删除子权限)
- `force=false` 时,有角色关联则拒绝删除
- `force=true` 时,自动解除角色关联后删除
- 操作会记录到审计日志
---
## 角色权限关联 API
### 1. 获取角色的所有权限
**请求**
```http
GET /api/v3/rbac/role-permissions?role_id=2
```
**权限要求**: `system:rbac:manage`
**Query 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_id | int | 是 | 角色ID |
**响应**
```json
{
"code": 200,
"message": "success",
"data": {
"role_id": 2,
"role_name": "市级管理员",
"role_key": "admin",
"permissions": [
{
"id": 1,
"permission_id": 10,
"permission_key": "document:list:read",
"display_name": "查看文档列表",
"module": "document",
"resource": "list",
"action": "read",
"permission_type": "API",
"grant_type": "GRANT",
"data_scope": "DEPT"
},
{
"id": 2,
"permission_id": 11,
"permission_key": "document:detail:read",
"display_name": "查看文档详情",
"module": "document",
"resource": "detail",
"action": "read",
"permission_type": "API",
"grant_type": "GRANT",
"data_scope": "DEPT"
}
]
}
}
```
---
### 2. 批量分配角色权限
**请求**
```http
POST /api/v3/rbac/role-permissions
Content-Type: application/json
{
"role_id": 2,
"permissions": [
{"permission_id": 10, "grant_type": "GRANT", "data_scope": "DEPT"},
{"permission_id": 11, "grant_type": "GRANT", "data_scope": "DEPT"},
{"permission_id": 12, "grant_type": "DENY", "data_scope": null}
],
"replace": true
}
```
**权限要求**: `system:rbac:manage`
**Request Body**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_id | int | 是 | 角色ID |
| permissions | array | 是 | 权限列表 |
| permissions[].permission_id | int | 是 | 权限ID |
| permissions[].grant_type | string | 是 | 授权类型(GRANT/DENY |
| permissions[].data_scope | string | 否 | 数据范围(ALL/DEPT/SELF |
| replace | bool | 否 | 替换模式(默认false |
**模式说明**
- `replace=true`: 替换全部权限(未在列表中的权限设为DENY)
- `replace=false`: 追加模式(保留现有权限,追加新权限)
**响应**
```json
{
"code": 200,
"message": "成功替换权限",
"data": {
"role_id": 2,
"role_name": "市级管理员",
"assigned_count": 3,
"replace": true
}
}
```
**业务逻辑**
- 验证所有 `permission_id` 必须存在
- `provincial_admin` 角色的核心权限不可移除(即使在replace模式下)
- 核心权限:`system:rbac:manage``system:*:*`
- 操作会记录到审计日志
---
### 3. 更新单个角色权限
**请求**
```http
PUT /api/v3/rbac/role-permissions?role_id=2&permission_id=10
Content-Type: application/json
{
"grant_type": "DENY",
"data_scope": "SELF"
}
```
**权限要求**: `system:rbac:manage`
**Query 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_id | int | 是 | 角色ID |
| permission_id | int | 是 | 权限ID |
**Request Body**(至少提供一个)
| 参数 | 类型 | 说明 |
|------|------|------|
| grant_type | string | 授权类型(GRANT/DENY |
| data_scope | string | 数据范围(ALL/DEPT/SELF |
**响应**
```json
{
"code": 200,
"message": "权限更新成功",
"data": {
"id": 1,
"role_id": 2,
"permission_id": 10,
"grant_type": "DENY",
"data_scope": "SELF"
}
}
```
---
### 4. 移除角色权限
**请求**
```http
DELETE /api/v3/rbac/role-permissions?role_id=2&permission_id=10
```
**权限要求**: `system:rbac:manage`
**Query 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_id | int | 是 | 角色ID |
| permission_id | int | 是 | 权限ID |
**响应**
```json
{
"code": 200,
"message": "成功移除角色权限: 市级管理员 - 查看文档列表",
"data": {
"role_id": 2,
"permission_id": 10
}
}
```
**业务逻辑**
- `provincial_admin` 角色的核心权限不可移除
- 核心权限列表:`system:rbac:manage``system:*:*`
- 操作会记录到审计日志
---
## 用户角色管理 API
### 1. 获取角色的所有用户
**请求**
```http
GET /api/v3/rbac/roles/{role_id}/users?page=1&page_size=20&area=&username=
```
**权限要求**: 仅需登录(数据根据用户角色过滤)
**Path 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_id | int | 是 | 角色ID |
**Query 参数**
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| page | int | 否 | 1 | 页码 |
| page_size | int | 否 | 20 | 每页数量(1-100 |
| area | string | 否 | - | 按地区过滤 |
| username | string | 否 | - | 用户名模糊搜索 |
**响应**
```json
{
"total": 20,
"page": 1,
"page_size": 20,
"items": [
{
"user_id": 100,
"username": "zhangsan",
"nick_name": "张三",
"area": "梅州",
"ou_name": "梅州市检察院",
"phone_number": "13800138000",
"email": "zhangsan@example.com",
"assigned_at": "2025-01-01T00:00:00"
}
]
}
```
**业务逻辑**
- `provincial_admin`: 可查看所有地区用户
- `admin`: 只能查看同地区(area字段)的用户
---
### 2. 获取用户的所有角色
**请求**
```http
GET /api/v3/rbac/users/{user_id}/roles
```
**权限要求**: `system:rbac:manage`
**Path 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | int | 是 | 用户ID |
**响应**
```json
{
"user_id": 100,
"username": "zhangsan",
"nick_name": "张三",
"roles": [
{
"role_id": 2,
"role_key": "admin",
"role_name": "市级管理员",
"data_scope": "DEPT",
"assigned_at": "2025-01-01T00:00:00"
}
]
}
```
---
### 3. 为用户分配角色
**请求**
```http
POST /api/v3/rbac/users/{user_id}/roles
Content-Type: application/json
{
"role_ids": [2]
}
```
**权限要求**: `system:rbac:manage`
**Path 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | int | 是 | 用户ID |
**Request Body**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| role_ids | array | 是 | 角色ID列表 |
**响应**
```json
{
"code": 200,
"message": "角色分配成功",
"data": {
"user_id": 100,
"username": "zhangsan",
"assigned_roles": [
{"role_id": 2, "role_name": "市级管理员"}
]
}
}
```
**业务逻辑**
- 一个用户只能拥有一个角色
- 如果用户已有角色,需要先移除再分配新角色
- 禁止给自己分配角色(防止自己提权)
- `admin` 只能给本地区用户分配角色
- `admin` 不能分配 `provincial_admin``admin` 角色(只有省级管理员可以)
- 操作会记录到审计日志
---
### 4. 移除用户角色
**请求**
```http
DELETE /api/v3/rbac/users/{user_id}/roles/{role_id}
```
**权限要求**: `system:rbac:manage`
**Path 参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | int | 是 | 用户ID |
| role_id | int | 是 | 角色ID |
**响应**
```json
{
"code": 200,
"message": "成功移除用户角色: zhangsan - 市级管理员",
"data": {
"user_id": 100,
"role_id": 2
}
}
```
**业务逻辑**
- 禁止移除自己的角色(防止自锁)
- `admin` 只能移除本地区用户的角色
- `admin` 不能移除用户的 `provincial_admin``admin` 角色
- 操作会记录到审计日志
---
## 路由管理 API
### 1. 获取所有路由列表
**请求**
```http
GET /api/v3/routes?format=tree&include_hidden=false
```
**权限要求**: 仅需登录
**Query 参数**
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| format | string | 否 | tree | 返回格式(tree/flat |
| include_hidden | bool | 否 | false | 是否包含隐藏路由 |
**响应(树形格式)**
```json
[
{
"id": 2,
"route_path": "/documents",
"route_name": "Documents",
"route_title": "文件管理",
"component": "views/documents/index",
"parent_id": null,
"icon": "ri-file-list-line",
"sort_order": 1,
"is_hidden": false,
"is_cache": true,
"meta": null,
"status": 0,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00",
"children": [
{
"id": 58,
"route_path": "/reviews",
"route_name": "Reviews",
"route_title": "文档评查结果详情",
"parent_id": 2,
"children": null
}
]
}
]
```
---
### 2. 获取路由详情
**请求**
```http
GET /api/v3/routes/{route_id}
```
**权限要求**: 仅需登录
**响应**
```json
{
"id": 58,
"route_path": "/reviews",
"route_name": "Reviews",
"route_title": "文档评查结果详情",
"component": "views/reviews/index",
"parent_id": 2,
"icon": "ri-file-list-line",
"sort_order": 3,
"is_hidden": false,
"is_cache": true,
"meta": null,
"status": 0,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00"
}
```
---
### 3. 获取路由的所有权限(含通用权限)⭐核心接口
**请求**
```http
GET /api/v3/routes/{route_id}/permissions
```
**权限要求**: 仅需登录
**响应**
```json
{
"route_id": 58,
"route_path": "/reviews",
"route_title": "文档评查结果详情",
"permissions": [
{
"id": 82,
"permission_key": "evaluation_point:result:view",
"display_name": "查看评查结果",
"description": "查看评查结果详情",
"module": "evaluation_point",
"resource": "result",
"action": "view",
"permission_type": "API",
"route_id": 58,
"related_routes": null,
"api_path": "/api/postgrest/proxy/evaluation_results",
"api_method": "GET",
"is_system": false,
"sort_order": 0,
"is_shared": false
},
{
"id": 88,
"permission_key": "evaluation:audit_status:view",
"display_name": "查看审计状态",
"description": "查看审计状态(通用权限)",
"module": "evaluation",
"resource": "audit_status",
"action": "view",
"permission_type": "API",
"route_id": null,
"related_routes": [58, 37],
"api_path": "/api/postgrest/proxy/audit_status",
"api_method": "GET",
"is_system": false,
"sort_order": 0,
"is_shared": true
}
]
}
```
**响应字段说明**
| 字段 | 说明 |
|------|------|
| `route_id` | 当前查询的路由ID |
| `route_path` | 路由路径 |
| `route_title` | 路由标题 |
| `permissions` | 权限列表 |
| `permissions[].is_shared` | **关键字段**`true`=通用权限,`false`=独立权限 |
| `permissions[].related_routes` | 通用权限关联的所有路由ID |
**业务逻辑**
查询SQL逻辑:
```sql
SELECT * FROM permissions
WHERE route_id = {route_id} -- 独立权限
OR {route_id} = ANY(related_routes) -- 通用权限
ORDER BY
CASE WHEN related_routes IS NOT NULL THEN 1 ELSE 0 END,
sort_order, id
```
---
## 业务逻辑详解
### 1. 权限检查逻辑
后端权限检查**只看 `permission_key`**,与 `route_id` 无关:
```sql
SELECT p.permission_key
FROM role_permissions rp
JOIN permissions p ON rp.permission_id = p.id
WHERE rp.role_id = {user_role_id} AND rp.grant_type = 'GRANT'
```
### 2. 通用权限保存逻辑
- 通用权限在数据库中只有**一条记录**
- 保存时只需提交**一次 permission_id**
- 两个页面自动都会有该权限
```javascript
// 正确:通用权限只提交一次
{
"permissions": [
{"permission_id": 88, "grant_type": "GRANT"} // 通用权限
]
}
// 错误:不需要为每个页面分别提交
{
"permissions": [
{"permission_id": 88, "grant_type": "GRANT", "route_id": 58}, // ❌ 不需要
{"permission_id": 88, "grant_type": "GRANT", "route_id": 37} // ❌ 不需要
]
}
```
### 3. 角色层级权限控制
| 操作 | provincial_admin | admin | common |
|------|------------------|-------|--------|
| 查看角色列表 | ✅ 全部 | ✅ 全部 | ✅ 全部 |
| 查看角色详情 | ✅ | ✅ | ❌ |
| 创建/修改/删除角色 | ✅ | ❌ | ❌ |
| 分配 provincial_admin 角色 | ✅ | ❌ | ❌ |
| 分配 admin 角色 | ✅ | ❌ | ❌ |
| 分配 common 角色 | ✅ | ✅(本地区) | ❌ |
| 查看用户列表 | ✅ 全部 | ✅ 本地区 | ❌ |
### 4. 核心权限保护
`provincial_admin` 角色的以下核心权限不可移除:
| 权限 | 说明 |
|------|------|
| `system:rbac:manage` | RBAC管理权限 |
| `system:*:*` | 系统全部权限 |
### 5. 安全检查规则
| 规则 | 说明 |
|------|------|
| 禁止自操作 | 不能给自己分配/移除角色 |
| 地区隔离 | admin 只能操作本地区用户 |
| 权限升级限制 | 只有省级管理员可以分配管理员角色 |
| 系统保护 | 系统角色/权限禁止删除 |
| 关联检查 | 有子权限的权限禁止删除 |
### 6. 审计日志
所有写操作都会记录到 `rbac_audit_logs` 表:
| 操作类型 | 说明 |
|----------|------|
| CREATE_ROLE | 创建角色 |
| UPDATE_ROLE | 更新角色 |
| DELETE_ROLE | 删除角色 |
| CREATE_PERMISSION | 创建权限 |
| UPDATE_PERMISSION | 更新权限 |
| DELETE_PERMISSION | 删除权限 |
| BATCH_ASSIGN_PERMISSION | 批量分配权限 |
| UPDATE_ROLE_PERMISSION | 更新角色权限 |
| REVOKE_ROLE_PERMISSION | 移除角色权限 |
| ASSIGN_USER_ROLE | 分配用户角色 |
| REVOKE_USER_ROLE | 移除用户角色 |
---
## 前端实现指南
### 1. 判断通用权限
```javascript
// 使用后端返回的 is_shared 字段
function isSharedPermission(permission) {
return permission.is_shared === true;
}
```
### 2. 加载权限配置页面
```javascript
async function loadPermissionConfigPage(roleId) {
// 1. 获取所有路由树
const routesRes = await fetch('/api/v3/routes?format=tree');
const routes = await routesRes.json();
// 2. 获取角色已有的权限
const rolePermRes = await fetch(`/api/v3/rbac/role-permissions?role_id=${roleId}`);
const rolePermissions = await rolePermRes.json();
const checkedIds = new Set(
rolePermissions.data.permissions
.filter(p => p.grant_type === 'GRANT')
.map(p => p.permission_id)
);
// 3. 为每个路由加载权限
for (const route of flattenRoutes(routes)) {
const permRes = await fetch(`/api/v3/routes/${route.id}/permissions`);
const permData = await permRes.json();
route.permissions = permData.permissions.map(p => ({
...p,
checked: checkedIds.has(p.id)
}));
}
return routes;
}
// 扁平化路由树
function flattenRoutes(routes, result = []) {
for (const route of routes) {
result.push(route);
if (route.children) {
flattenRoutes(route.children, result);
}
}
return result;
}
```
### 3. 处理通用权限勾选同步
```javascript
// 权限勾选变化处理
function onPermissionCheck(permission, checked, allRoutes) {
// 更新当前权限的勾选状态
permission.checked = checked;
// 如果是通用权限,同步更新其他关联路由的显示
if (permission.is_shared && permission.related_routes) {
permission.related_routes.forEach(routeId => {
const route = findRouteById(allRoutes, routeId);
if (route && route.permissions) {
const samePerm = route.permissions.find(p => p.id === permission.id);
if (samePerm) {
samePerm.checked = checked;
}
}
});
}
}
```
### 4. 保存权限配置
```javascript
async function saveRolePermissions(roleId, allRoutes) {
// 收集所有勾选的权限(去重,通用权限只收集一次)
const checkedPermissionIds = new Set();
flattenRoutes(allRoutes).forEach(route => {
if (route.permissions) {
route.permissions
.filter(p => p.checked)
.forEach(p => checkedPermissionIds.add(p.id));
}
});
// 构建请求数据
const requestData = {
role_id: roleId,
permissions: Array.from(checkedPermissionIds).map(id => ({
permission_id: id,
grant_type: 'GRANT',
data_scope: 'DEPT' // 根据业务需求设置
})),
replace: true
};
// 提交
const response = await fetch('/api/v3/rbac/role-permissions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestData)
});
return response.json();
}
```
### 5. UI 展示建议
```jsx
function PermissionItem({ permission, onChange }) {
return (
<div className={`permission-item ${permission.is_shared ? 'shared' : ''}`}>
<Checkbox
checked={permission.checked}
onChange={(e) => onChange(permission, e.target.checked)}
/>
<span className="permission-name">
{permission.is_shared && <Tag color="blue">通用</Tag>}
{permission.display_name}
</span>
<span className="permission-api">
{permission.api_method} {permission.api_path}
</span>
{permission.is_shared && (
<Tooltip title={`此权限同时适用于路由: ${permission.related_routes.join(', ')}`}>
<InfoCircleOutlined />
</Tooltip>
)}
</div>
);
}
```
### 6. 样式参考
```css
.permission-item {
display: flex;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
}
.permission-item.shared {
background-color: #f0f7ff;
border-left: 3px solid #1890ff;
}
.permission-name {
flex: 1;
margin-left: 8px;
}
.permission-api {
color: #999;
font-size: 12px;
font-family: monospace;
}
```
---
## API 接口汇总
### 角色管理(5个)
| 接口 | 方法 | 权限要求 | 说明 |
|------|------|----------|------|
| `/api/v3/rbac/roles` | GET | 登录 | 获取角色列表 |
| `/api/v3/rbac/roles/{role_id}` | GET | system:rbac:manage | 获取角色详情 |
| `/api/v3/rbac/roles` | POST | system:rbac:manage | 创建角色 |
| `/api/v3/rbac/roles/{role_id}` | PUT | system:rbac:manage | 更新角色 |
| `/api/v3/rbac/roles/{role_id}` | DELETE | system:rbac:manage | 删除角色 |
### 权限管理(5个)
| 接口 | 方法 | 权限要求 | 说明 |
|------|------|----------|------|
| `/api/v3/rbac/permissions` | GET | system:rbac:manage | 获取权限列表 |
| `/api/v3/rbac/permissions/{permission_id}` | GET | system:rbac:manage | 获取权限详情 |
| `/api/v3/rbac/permissions` | POST | system:rbac:manage | 创建权限 |
| `/api/v3/rbac/permissions/{permission_id}` | PUT | system:rbac:manage | 更新权限 |
| `/api/v3/rbac/permissions/{permission_id}` | DELETE | system:rbac:manage | 删除权限 |
### 角色权限关联(4个)
| 接口 | 方法 | 权限要求 | 说明 |
|------|------|----------|------|
| `/api/v3/rbac/role-permissions` | GET | system:rbac:manage | 获取角色的所有权限 |
| `/api/v3/rbac/role-permissions` | POST | system:rbac:manage | 批量分配角色权限 |
| `/api/v3/rbac/role-permissions` | PUT | system:rbac:manage | 更新单个角色权限 |
| `/api/v3/rbac/role-permissions` | DELETE | system:rbac:manage | 移除角色权限 |
### 用户角色管理(4个)
| 接口 | 方法 | 权限要求 | 说明 |
|------|------|----------|------|
| `/api/v3/rbac/roles/{role_id}/users` | GET | 登录 | 获取角色的所有用户 |
| `/api/v3/rbac/users/{user_id}/roles` | GET | system:rbac:manage | 获取用户的所有角色 |
| `/api/v3/rbac/users/{user_id}/roles` | POST | system:rbac:manage | 为用户分配角色 |
| `/api/v3/rbac/users/{user_id}/roles/{role_id}` | DELETE | system:rbac:manage | 移除用户角色 |
### 路由管理(3个)
| 接口 | 方法 | 权限要求 | 说明 |
|------|------|----------|------|
| `/api/v3/routes` | GET | 登录 | 获取所有路由列表 |
| `/api/v3/routes/{route_id}` | GET | 登录 | 获取路由详情 |
| `/api/v3/routes/{route_id}/permissions` | GET | 登录 | **获取路由的所有权限(含通用权限)** |
---
## 当前通用权限数据
| id | permission_key | display_name | related_routes |
|----|----------------|--------------|----------------|
| 88 | evaluation:audit_status:view | 查看审计状态 | [58, 37] |
| 89 | evaluation:audit_status:update | 更新审计状态 | [58, 37] |
| 136 | evaluation:result:update | 更新评查结果 | [58, 37] |
| 137 | evaluation:audit_status:create | 创建审核状态 | [58, 37] |
| 138 | evaluation:document:confirm | 确认文档审核完成 | [58, 37] |
**关联的路由**
| route_id | route_title | route_path |
|----------|-------------|------------|
| 58 | 文档评查结果详情 | /reviews |
| 37 | 评查结果 | /cross-checking/result |
---
## 更新记录
| 日期 | 版本 | 说明 |
|------|------|------|
| 2025-12-11 | 3.0 | 完整 RBAC 文档,包含 21 个 API 接口和详细业务逻辑 |
| 2025-12-11 | 2.0 | 使用 FastAPI 专用接口,删除 PostgREST 方式 |