1e9e0044ba
1. 角色权限管理升级: - 添加路由下展开式API权限管理功能 - 新增 getRoleRoutesWithPermissions 和 saveRoleApiPermissions API - 支持按路由展开/收起查看和勾选权限 - 过滤"所有权限"选项,只显示具体权限 2. 错误处理优化: - 403 无权限错误显示为"无权限访问该资源" - 修复评查点分组批量删除显示"成功删除 undefined 个分组"的问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1094 lines
29 KiB
Markdown
1094 lines
29 KiB
Markdown
# 用户权限管理接口 - 前端对接文档
|
||
|
||
## 📋 文档信息
|
||
|
||
- **版本**: v3.0
|
||
- **更新日期**: 2025-11-26
|
||
- **API基础URL**: `http://YOUR_HOST:8000`
|
||
- **认证方式**: JWT Bearer Token
|
||
|
||
---
|
||
|
||
## ⚠️ 重要提示 - 前端对接必读
|
||
|
||
### 1. Token过期处理
|
||
|
||
**问题现象**: API返回 `code: 4001, msg: "token参数无效"`
|
||
|
||
**原因**: JWT Token已过期(默认有效期1小时)
|
||
|
||
**解决方案**:
|
||
```javascript
|
||
// 检查Token是否过期
|
||
const isTokenExpired = (token) => {
|
||
try {
|
||
const payload = JSON.parse(atob(token.split('.')[1]));
|
||
return Date.now() >= payload.exp * 1000;
|
||
} catch {
|
||
return true;
|
||
}
|
||
};
|
||
|
||
// 在发送请求前检查
|
||
if (isTokenExpired(token)) {
|
||
await login(); // 重新登录获取新Token
|
||
}
|
||
```
|
||
|
||
### 2. 实际角色ID对应关系
|
||
|
||
⚠️ **数据库中的实际角色ID** (请以实际数据为准):
|
||
|
||
| 角色名称 | role_key | 实际role_id | 说明 |
|
||
|---------|----------|------------|------|
|
||
| 省级管理员 | provincial_admin | **52** | 拥有完整RBAC管理权限 |
|
||
| 市级管理员 | admin | **1** | 负责本地区业务管理 |
|
||
| 普通员工 | common | **2** | 只能查看和管理自己的数据 |
|
||
|
||
**调用示例**:
|
||
```javascript
|
||
// ✅ 正确:使用实际的role_id
|
||
const provincialAdminUsers = await getRoleUsers(52);
|
||
const adminUsers = await getRoleUsers(1);
|
||
const commonUsers = await getRoleUsers(2);
|
||
|
||
// ❌ 错误:使用Mock数据的ID
|
||
const wrongUsers = await getRoleUsers(3); // 数据库中不存在
|
||
```
|
||
|
||
### 3. 响应格式说明
|
||
|
||
所有接口统一返回格式:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": { ... }
|
||
}
|
||
```
|
||
|
||
部分接口(如用户列表)直接返回数据对象:
|
||
```json
|
||
{
|
||
"users": [...],
|
||
"total": 10
|
||
}
|
||
```
|
||
|
||
### 4. 权限要求
|
||
|
||
| 接口模块 | 权限要求 | 说明 |
|
||
|---------|----------|------|
|
||
| RBAC管理接口 | `system:rbac:manage` | 仅provincial_admin角色 |
|
||
| 用户管理接口 | JWT认证即可 | 所有登录用户 |
|
||
| 路由权限接口 | JWT认证即可 | 所有登录用户 |
|
||
|
||
---
|
||
|
||
## 📚 接口总览
|
||
|
||
### 一、RBAC管理接口(18个)
|
||
|
||
**基础路径**: `/api/v3/rbac`
|
||
|
||
#### 1.1 角色管理(5个接口)
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/roles` | 获取角色列表(支持分页、搜索) |
|
||
| GET | `/roles/{role_id}` | 获取角色详情 |
|
||
| POST | `/roles` | 创建角色 |
|
||
| PUT | `/roles/{role_id}` | 更新角色 |
|
||
| DELETE | `/roles/{role_id}` | 删除角色(支持force参数) |
|
||
|
||
#### 1.2 权限管理(5个接口)
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/permissions` | 获取权限列表(支持树形/平铺) |
|
||
| GET | `/permissions/{permission_id}` | 获取权限详情 |
|
||
| POST | `/permissions` | 创建权限 |
|
||
| PUT | `/permissions/{permission_id}` | 更新权限 |
|
||
| DELETE | `/permissions/{permission_id}` | 删除权限 |
|
||
|
||
#### 1.3 角色权限关联(4个接口)
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/roles/{role_id}/permissions` | 获取角色的所有权限 |
|
||
| POST | `/roles/{role_id}/permissions` | 批量分配权限给角色(支持替换/追加) |
|
||
| PUT | `/roles/{role_id}/permissions/{permission_id}` | 更新单个权限配置 |
|
||
| DELETE | `/roles/{role_id}/permissions/{permission_id}` | 移除角色权限 |
|
||
|
||
#### 1.4 用户角色管理(4个接口)
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/roles/{role_id}/users` | 获取拥有某角色的用户列表 |
|
||
| GET | `/users/{user_id}/roles` | 获取用户的所有角色 |
|
||
| POST | `/users/{user_id}/roles` | 为用户分配角色 |
|
||
| DELETE | `/users/{user_id}/roles/{role_id}` | 移除用户角色 |
|
||
|
||
### 二、用户管理接口(3个)
|
||
|
||
**基础路径**: `/admin/users`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/users` | 获取用户列表(支持分页、搜索) |
|
||
| GET | `/organizations` | 获取组织架构(树形结构) |
|
||
| GET | `/organizations/flat` | 获取组织列表(扁平结构) |
|
||
|
||
### 三、路由权限接口(5个)
|
||
|
||
**基础路径**: `/rbac`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/user/routes` | 获取当前用户可访问路由(树形) |
|
||
| GET | `/user/routes/flat` | 获取当前用户可访问路由(扁平) |
|
||
| GET | `/roles/{role_id}/routes` | 获取角色可访问路由 |
|
||
| PUT | `/roles/{role_id}/routes` | **批量更新角色路由权限** ⭐新增 |
|
||
| GET | `/check-route` | 检查路由访问权限 |
|
||
|
||
---
|
||
|
||
## 📖 详细接口说明
|
||
|
||
## 一、RBAC管理接口
|
||
|
||
### 1.1 获取角色列表
|
||
|
||
**接口**: `GET /api/v3/rbac/roles`
|
||
|
||
**Query参数**:
|
||
```javascript
|
||
{
|
||
page: 1, // 页码
|
||
page_size: 20, // 每页数量
|
||
role_key: "", // 角色标识过滤(可选)
|
||
role_name: "", // 角色名称模糊搜索(可选)
|
||
include_system: true // 是否包含系统角色(可选)
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"total": 3,
|
||
"page": 1,
|
||
"page_size": 20,
|
||
"items": [
|
||
{
|
||
"id": 52,
|
||
"role_key": "provincial_admin",
|
||
"role_name": "省级管理员",
|
||
"description": "省级权限,可管理所有地区",
|
||
"data_scope": "ALL",
|
||
"is_system": true,
|
||
"user_count": 1,
|
||
"permission_count": 15
|
||
},
|
||
{
|
||
"id": 1,
|
||
"role_key": "admin",
|
||
"role_name": "市级管理员",
|
||
"data_scope": "DEPT",
|
||
"is_system": true,
|
||
"user_count": 6
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 1.2 创建角色
|
||
|
||
**接口**: `POST /api/v3/rbac/roles`
|
||
|
||
**请求体参数**:
|
||
|
||
| 字段 | 类型 | 必填 | 验证规则 | 示例 |
|
||
|------|------|------|---------|------|
|
||
| role_key | string | ✅ | 必须以小写字母开头,只能包含小写字母、数字、下划线 | `department_leader` |
|
||
| role_name | string | ✅ | 任意字符,建议不超过50字 | `部门负责人` |
|
||
| description | string | ❌ | 角色描述 | `负责部门日常管理` |
|
||
| data_scope | string | ❌ | 可选值:ALL/DEPT/SELF,默认SELF | `DEPT` |
|
||
| metadata | object | ❌ | 自定义元数据 | `{}` |
|
||
|
||
**role_key 验证规则**:
|
||
- ✅ 必须以**小写字母**开头:`a-z`
|
||
- ✅ 只能包含:小写字母、数字、下划线
|
||
- ✅ 正确示例:`admin_role`, `department_leader`, `role123`
|
||
- ❌ 错误示例:`1231`(数字开头), `Test_Role`(大写), `admin-role`(连字符)
|
||
|
||
**请求示例**:
|
||
```json
|
||
{
|
||
"role_key": "department_leader",
|
||
"role_name": "部门负责人",
|
||
"description": "负责部门日常管理",
|
||
"data_scope": "DEPT"
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "角色创建成功",
|
||
"data": {
|
||
"id": 53,
|
||
"role_key": "department_leader",
|
||
"role_name": "部门负责人",
|
||
"data_scope": "DEPT",
|
||
"is_system": false
|
||
}
|
||
}
|
||
```
|
||
|
||
**错误响应示例**:
|
||
```json
|
||
{
|
||
"code": 4002,
|
||
"msg": "参数验证错误",
|
||
"data": [
|
||
{
|
||
"type": "value_error",
|
||
"loc": ["body", "role_key"],
|
||
"msg": "Value error, role_key只能包含小写字母、数字、下划线,且必须以字母开头",
|
||
"input": "1231"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 1.3 获取角色的所有用户
|
||
|
||
**接口**: `GET /api/v3/rbac/roles/{role_id}/users`
|
||
|
||
**Query参数**:
|
||
```javascript
|
||
{
|
||
page: 1,
|
||
page_size: 20,
|
||
area: "梅州", // 按地区过滤(可选)
|
||
username: "admin" // 用户名模糊搜索(可选)
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"total": 6,
|
||
"page": 1,
|
||
"page_size": 20,
|
||
"items": [
|
||
{
|
||
"user_id": 8,
|
||
"username": "梅州烟草",
|
||
"nick_name": "梅州烟草",
|
||
"area": "梅州管理员账号",
|
||
"ou_name": "梅州管理员账号",
|
||
"phone_number": null,
|
||
"email": null,
|
||
"assigned_at": "2025-11-18T01:40:25.030949+00:00"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 1.4 批量分配权限给角色
|
||
|
||
**接口**: `POST /api/v3/rbac/roles/{role_id}/permissions`
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"permissions": [
|
||
{
|
||
"permission_id": 10,
|
||
"grant_type": "GRANT",
|
||
"data_scope": "ALL"
|
||
},
|
||
{
|
||
"permission_id": 11,
|
||
"grant_type": "GRANT",
|
||
"data_scope": "DEPT"
|
||
}
|
||
],
|
||
"replace": true
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
- `replace`: true=替换全部权限,false=追加权限
|
||
- `grant_type`: GRANT(授予)或 DENY(拒绝)
|
||
- `data_scope`: ALL(全部数据)/ DEPT(本部门)/ SELF(仅自己)
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "权限分配成功",
|
||
"data": {
|
||
"role_id": 2,
|
||
"assigned_count": 2
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 1.5 为用户分配角色
|
||
|
||
**接口**: `POST /api/v3/rbac/users/{user_id}/roles`
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"role_ids": [1, 2]
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "角色分配成功",
|
||
"data": {
|
||
"user_id": 20,
|
||
"username": "test_user",
|
||
"assigned_roles": [
|
||
{"role_id": 1, "role_name": "市级管理员"},
|
||
{"role_id": 2, "role_name": "普通员工"}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**注意事项**:
|
||
- ⚠️ 不能给自己分配角色(防止提权)
|
||
- ⚠️ provincial_admin角色只能由省级管理员分配
|
||
- ⚠️ admin角色只能给本地区用户分配角色
|
||
|
||
---
|
||
|
||
## 二、用户管理接口
|
||
|
||
### 2.1 获取用户列表
|
||
|
||
**接口**: `GET /admin/users/users`
|
||
|
||
**Query参数**:
|
||
```javascript
|
||
{
|
||
page: 1,
|
||
page_size: 20,
|
||
ou_id: "000", // 组织ID过滤(可选)
|
||
is_leader: true, // 是否领导过滤(可选)
|
||
search: "admin" // 搜索关键词(可选)
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"users": [
|
||
{
|
||
"id": 5,
|
||
"username": "admin",
|
||
"nick_name": "admin",
|
||
"ou_id": "000",
|
||
"ou_name": "test",
|
||
"is_leader": true,
|
||
"status": 0
|
||
}
|
||
],
|
||
"total": 10
|
||
}
|
||
```
|
||
|
||
⚠️ **已知问题**: 当前版本 `total` 字段返回0,请以 `users` 数组长度为准。
|
||
|
||
---
|
||
|
||
### 2.2 获取组织架构(树形)
|
||
|
||
**接口**: `GET /admin/users/organizations`
|
||
|
||
**Query参数**:
|
||
```javascript
|
||
{
|
||
include_users: true // 是否包含用户信息
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"organizations": [
|
||
{
|
||
"ou_id": "000",
|
||
"ou_name": "总部",
|
||
"parent_ou_id": null,
|
||
"level": 0,
|
||
"children": [
|
||
{
|
||
"ou_id": "0000000A1ML",
|
||
"ou_name": "梅州市局",
|
||
"parent_ou_id": "000",
|
||
"level": 1,
|
||
"users": [
|
||
{
|
||
"id": 8,
|
||
"username": "梅州烟草",
|
||
"nick_name": "梅州烟草",
|
||
"ou_id": "0000000A1ML",
|
||
"ou_name": "梅州市局"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"users": []
|
||
}
|
||
],
|
||
"total_organizations": 5,
|
||
"total_users": 10
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、路由权限接口
|
||
|
||
### 3.1 获取当前用户可访问路由
|
||
|
||
**接口**: `GET /rbac/user/routes`
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"user_id": 5,
|
||
"username": "admin",
|
||
"routes": [
|
||
{
|
||
"id": 1,
|
||
"route_path": "/",
|
||
"route_name": "Home",
|
||
"route_title": "首页",
|
||
"component": "Layout",
|
||
"parent_id": null,
|
||
"icon": "home",
|
||
"sort_order": 0,
|
||
"is_hidden": false,
|
||
"is_cache": true,
|
||
"meta": {},
|
||
"children": [
|
||
{
|
||
"id": 11,
|
||
"route_path": "/dashboard",
|
||
"route_name": "Dashboard",
|
||
"route_title": "工作台",
|
||
"parent_id": 1
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"routes_flat": [
|
||
{"id": 1, "route_path": "/", "route_name": "Home"},
|
||
{"id": 11, "route_path": "/dashboard", "route_name": "Dashboard"}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**说明**:
|
||
- `routes`: 树形结构(用于构建菜单)
|
||
- `routes_flat`: 扁平结构(用于路由守卫权限检查)
|
||
|
||
---
|
||
|
||
### 3.2 获取角色可访问路由(含API权限)⭐v3.0更新
|
||
|
||
**接口**: `GET /rbac/roles/{role_id}/routes`
|
||
|
||
**v3.0 重大变更**:
|
||
- 返回格式从**扁平列表**改为**树形结构**
|
||
- 每个路由节点新增 `permissions` 字段,包含该页面关联的所有API操作权限
|
||
- 用于权限管理界面展示「页面 → API权限」的层级关系
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"role_id": 1,
|
||
"routes": [
|
||
{
|
||
"id": 31,
|
||
"route_path": "/home",
|
||
"route_name": "Home",
|
||
"route_title": "系统概览",
|
||
"parent_id": null,
|
||
"icon": "ri-dashboard-line",
|
||
"sort_order": 1,
|
||
"is_hidden": false,
|
||
"component": "views/Home.vue",
|
||
"permissions": []
|
||
},
|
||
{
|
||
"id": 41,
|
||
"route_path": "/rules",
|
||
"route_name": "Rules",
|
||
"route_title": "评查规则库",
|
||
"parent_id": null,
|
||
"icon": "ri-book-3-line",
|
||
"sort_order": 3,
|
||
"is_hidden": false,
|
||
"component": "views/rules/Index.vue",
|
||
"permissions": [
|
||
{
|
||
"id": 28,
|
||
"permission_key": "evaluation_group:list:read",
|
||
"display_name": "查看评查点分组列表",
|
||
"api_method": "GET",
|
||
"api_path": "/api/v3/evaluation-point-groups"
|
||
},
|
||
{
|
||
"id": 30,
|
||
"permission_key": "evaluation_group:create:write",
|
||
"display_name": "创建评查点分组",
|
||
"api_method": "POST",
|
||
"api_path": "/api/v3/evaluation-point-groups"
|
||
},
|
||
{
|
||
"id": 35,
|
||
"permission_key": "evaluation_point:list:read",
|
||
"display_name": "查看评查点规则列表",
|
||
"api_method": "GET",
|
||
"api_path": "/api/v3/evaluation-points"
|
||
}
|
||
],
|
||
"children": [
|
||
{
|
||
"id": 43,
|
||
"route_path": "/rule-groups",
|
||
"route_name": "RuleGroups",
|
||
"route_title": "评查点分组",
|
||
"parent_id": 41,
|
||
"permissions": []
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**字段说明**:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | int | 路由ID |
|
||
| route_path | string | 前端路由路径 |
|
||
| route_name | string | 路由名称(用于Vue router name) |
|
||
| route_title | string | 页面标题(用于菜单显示) |
|
||
| parent_id | int/null | 父路由ID,null表示顶级路由 |
|
||
| icon | string | 菜单图标 |
|
||
| sort_order | int | 排序顺序 |
|
||
| is_hidden | bool | 是否隐藏(不显示在侧边栏) |
|
||
| component | string | Vue组件路径 |
|
||
| permissions | array | **关联的API权限列表** |
|
||
| children | array | 子路由列表(可选) |
|
||
|
||
**permissions 子字段说明**:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | int | 权限ID |
|
||
| permission_key | string | 权限标识(用于权限检查) |
|
||
| display_name | string | 权限显示名称 |
|
||
| api_method | string | HTTP方法(GET/POST/PUT/DELETE等) |
|
||
| api_path | string | API路径 |
|
||
|
||
---
|
||
|
||
### 3.3 批量更新角色路由权限
|
||
|
||
**接口**: `PUT /rbac/roles/{role_id}/routes`
|
||
|
||
**功能说明**:
|
||
- 批量更新指定角色的路由权限
|
||
- 采用**替换模式**:先删除现有所有关联,再插入新关联
|
||
- 自动清除相关缓存(角色缓存 + 用户缓存)
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"route_ids": [1, 11, 12, 3, 31, 32, 2, 21],
|
||
"permission": "RW"
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
- `route_ids`: 路由ID列表(必填)
|
||
- `permission`: 权限类型,可选值:
|
||
- `R`: 只读权限
|
||
- `W`: 只写权限
|
||
- `RW`: 读写权限(默认)
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"role_id": 2,
|
||
"assigned_count": 8,
|
||
"removed_count": 5,
|
||
"route_ids": [1, 11, 12, 3, 31, 32, 2, 21]
|
||
}
|
||
}
|
||
```
|
||
|
||
**前端调用示例**:
|
||
```javascript
|
||
const updateRoleRoutes = async (roleId, routeIds) => {
|
||
const response = await fetch(`/rbac/roles/${roleId}/routes`, {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
route_ids: routeIds,
|
||
permission: 'RW'
|
||
})
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.code === 200) {
|
||
console.log(`成功分配 ${result.data.assigned_count} 个路由`);
|
||
console.log(`移除了 ${result.data.removed_count} 个旧路由`);
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
// 使用示例
|
||
await updateRoleRoutes(2, [1, 11, 12, 3, 31, 32, 2, 21]);
|
||
```
|
||
|
||
**注意事项**:
|
||
- ⚠️ 所有 `route_ids` 必须存在且未删除
|
||
- ⚠️ 使用事务确保数据一致性
|
||
- ⚠️ 会自动清除所有受影响用户的路由缓存
|
||
|
||
---
|
||
|
||
### 3.4 检查路由访问权限
|
||
|
||
**接口**: `GET /rbac/check-route`
|
||
|
||
**Query参数**:
|
||
```javascript
|
||
{
|
||
route_path: "/system/users"
|
||
}
|
||
```
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"route_path": "/system/users",
|
||
"has_access": true
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🚨 错误码说明
|
||
|
||
### HTTP状态码
|
||
|
||
| 状态码 | 说明 | 处理建议 |
|
||
|--------|------|---------|
|
||
| 200 | 成功 | - |
|
||
| 400 | 请求参数错误 | 检查请求体格式和必填字段 |
|
||
| 401 | 未认证 | Token过期或无效,需要重新登录 |
|
||
| 403 | 无权限 | 显示权限不足提示 |
|
||
| 404 | 资源不存在 | 检查ID是否正确 |
|
||
| 409 | 冲突 | 如role_key重复 |
|
||
| 500 | 服务器错误 | 联系后端排查 |
|
||
|
||
### 常见错误处理
|
||
|
||
```javascript
|
||
// 统一错误处理
|
||
const handleError = (error) => {
|
||
if (error.response?.status === 401) {
|
||
// Token过期,跳转登录
|
||
localStorage.removeItem('token');
|
||
window.location.href = '/login';
|
||
} else if (error.response?.status === 403) {
|
||
alert('权限不足');
|
||
} else if (error.response?.data?.detail) {
|
||
alert(error.response.data.detail);
|
||
} else {
|
||
alert('操作失败,请稍后重试');
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 💡 常见问题FAQ
|
||
|
||
### Q1: 为什么所有接口返回 code: 4001?
|
||
**A**: Token已过期,请重新登录获取新Token
|
||
|
||
### Q2: role_id应该用哪个?文档里写的1,2,3还是52,1,2?
|
||
**A**: 使用实际数据库中的ID:
|
||
- provincial_admin = **52**
|
||
- admin = **1**
|
||
- common = **2**
|
||
|
||
### Q3: 需要用Mock数据吗?
|
||
**A**: **不需要**,所有接口已完整实现,直接调用真实API
|
||
|
||
### Q4: 如何判断当前用户是否有RBAC管理权限?
|
||
```javascript
|
||
// 方式1:检查JWT中的user_role
|
||
const payload = JSON.parse(atob(token.split('.')[1]));
|
||
const isProvincialAdmin = payload.user_role === 'provincial_admin';
|
||
|
||
// 方式2:调用权限检查接口(推荐)
|
||
const routes = await fetch('/rbac/user/routes');
|
||
const hasRBACAccess = routes.data.routes.some(r => r.route_path === '/rbac');
|
||
```
|
||
|
||
### Q5: 如何实现角色权限页面的路由分配功能?
|
||
**A**: 使用新的批量更新接口 `PUT /rbac/roles/{role_id}/routes`:
|
||
1. 通过 `GET /rbac/roles/{role_id}/routes` 获取当前角色已有路由
|
||
2. 用户选择要分配的路由ID列表
|
||
3. 调用 `PUT /rbac/roles/{role_id}/routes` 提交更新
|
||
4. 接口会自动替换所有路由权限并清除缓存
|
||
|
||
### Q6: total字段为什么返回0?
|
||
**A**: `/admin/users/users` 接口的 `total` 字段当前版本存在bug,请以 `users` 数组长度为准。该问题已记录,将在后续版本修复。
|
||
|
||
### Q7: 批量更新路由权限后,用户需要重新登录吗?
|
||
**A**: 不需要。接口会自动清除相关用户的路由缓存,用户刷新页面即可看到新权限。
|
||
|
||
---
|
||
|
||
## 📦 前端开发清单
|
||
|
||
- [ ] 移除所有RBAC相关的Mock数据
|
||
- [ ] 使用实际的role_id(52, 1, 2)
|
||
- [ ] 实现Token过期自动刷新机制
|
||
- [ ] 处理401/403错误(跳转登录/权限提示)
|
||
- [ ] 使用 `PUT /rbac/roles/{role_id}/routes` 实现路由权限分配
|
||
- [ ] 处理 `total` 字段为0的情况(用数组长度代替)
|
||
- [ ] **v3.0新增**: 适配新的路由权限接口(含permissions字段)
|
||
|
||
---
|
||
|
||
## 🎯 v3.0 前端修改思路说明
|
||
|
||
### 一、架构变更说明
|
||
|
||
**变更前(v2.0)**:
|
||
```
|
||
sys_routes 表:同时存储页面路由和API接口路由
|
||
└── 侧边栏展示所有路由(包括API接口)❌ 不符合预期
|
||
```
|
||
|
||
**变更后(v3.0)**:
|
||
```
|
||
sys_routes 表:仅存储页面路由(侧边栏菜单)
|
||
permissions 表:存储API操作权限(通过 route_id 关联到页面)
|
||
|
||
页面路由
|
||
├── 评查规则库 (/rules)
|
||
│ ├── [权限] 查看评查点分组列表 (GET /api/v3/evaluation-point-groups)
|
||
│ ├── [权限] 创建评查点分组 (POST /api/v3/evaluation-point-groups)
|
||
│ ├── [权限] 查看评查点规则列表 (GET /api/v3/evaluation-points)
|
||
│ └── ...
|
||
└── 系统设置 (/settings)
|
||
└── [子页面] 角色权限管理 (/role-permissions)
|
||
```
|
||
|
||
### 二、权限管理界面修改思路
|
||
|
||
#### 2.1 数据结构适配
|
||
|
||
```typescript
|
||
// 新的路由类型定义
|
||
interface RouteWithPermissions {
|
||
id: number;
|
||
route_path: string;
|
||
route_name: string;
|
||
route_title: string;
|
||
parent_id: number | null;
|
||
icon: string;
|
||
sort_order: number;
|
||
is_hidden: boolean;
|
||
component: string;
|
||
permissions: Permission[]; // 新增:关联的API权限
|
||
children?: RouteWithPermissions[];
|
||
}
|
||
|
||
interface Permission {
|
||
id: number;
|
||
permission_key: string;
|
||
display_name: string;
|
||
api_method: string;
|
||
api_path: string;
|
||
}
|
||
```
|
||
|
||
#### 2.2 权限分配界面布局建议
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 角色权限管理 - 市级管理员 │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ☑ 系统概览 (/home) │
|
||
│ │
|
||
│ ☑ 评查规则库 (/rules) │
|
||
│ ├─ ☑ 查看评查点分组列表 [GET] │
|
||
│ ├─ ☑ 创建评查点分组 [POST] │
|
||
│ ├─ ☑ 更新评查点分组 [PUT] │
|
||
│ ├─ ☐ 删除评查点分组 [DELETE] ← 未勾选 │
|
||
│ ├─ ☑ 查看评查点规则列表 [GET] │
|
||
│ └─ ... │
|
||
│ │
|
||
│ ☐ 系统设置 (/settings) │
|
||
│ └─ ☑ 角色权限管理 (/role-permissions) │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 2.3 前端组件实现示例
|
||
|
||
```vue
|
||
<template>
|
||
<div class="role-permission-manager">
|
||
<h3>角色权限管理 - {{ role.role_name }}</h3>
|
||
|
||
<!-- 路由权限树 -->
|
||
<el-tree
|
||
:data="routeTree"
|
||
show-checkbox
|
||
node-key="id"
|
||
:default-checked-keys="checkedRouteIds"
|
||
:props="treeProps"
|
||
@check="handleRouteCheck"
|
||
>
|
||
<template #default="{ node, data }">
|
||
<span class="route-node">
|
||
<i :class="data.icon"></i>
|
||
{{ data.route_title }}
|
||
|
||
<!-- 展示该路由关联的API权限 -->
|
||
<div v-if="data.permissions?.length" class="permission-list">
|
||
<el-checkbox-group v-model="checkedPermissions[data.id]">
|
||
<el-checkbox
|
||
v-for="perm in data.permissions"
|
||
:key="perm.id"
|
||
:label="perm.id"
|
||
>
|
||
<el-tag :type="getMethodTagType(perm.api_method)" size="small">
|
||
{{ perm.api_method }}
|
||
</el-tag>
|
||
{{ perm.display_name }}
|
||
</el-checkbox>
|
||
</el-checkbox-group>
|
||
</div>
|
||
</span>
|
||
</template>
|
||
</el-tree>
|
||
|
||
<el-button type="primary" @click="savePermissions">保存权限</el-button>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from 'vue'
|
||
|
||
const routeTree = ref([])
|
||
const checkedRouteIds = ref([])
|
||
const checkedPermissions = ref({}) // { routeId: [permissionId, ...] }
|
||
|
||
// 获取角色路由权限
|
||
const fetchRoleRoutes = async (roleId) => {
|
||
const res = await fetch(`/rbac/roles/${roleId}/routes`, {
|
||
headers: { 'Authorization': `Bearer ${token}` }
|
||
})
|
||
const { data } = await res.json()
|
||
routeTree.value = data.routes
|
||
|
||
// 初始化已选中的路由和权限
|
||
initCheckedState(data.routes)
|
||
}
|
||
|
||
// 根据HTTP方法返回标签类型
|
||
const getMethodTagType = (method) => {
|
||
const types = {
|
||
'GET': 'success',
|
||
'POST': 'primary',
|
||
'PUT': 'warning',
|
||
'DELETE': 'danger',
|
||
'PATCH': 'info'
|
||
}
|
||
return types[method] || 'info'
|
||
}
|
||
|
||
// 保存权限
|
||
const savePermissions = async () => {
|
||
// 1. 保存路由权限
|
||
await fetch(`/rbac/roles/${roleId}/routes`, {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
route_ids: checkedRouteIds.value,
|
||
permission: 'RW'
|
||
})
|
||
})
|
||
|
||
// 2. 保存功能权限(如需细粒度控制)
|
||
// await saveRolePermissions(roleId, flattenPermissions())
|
||
|
||
ElMessage.success('权限保存成功')
|
||
}
|
||
</script>
|
||
```
|
||
|
||
### 三、权限检查逻辑修改
|
||
|
||
#### 3.1 按钮级权限控制
|
||
|
||
```javascript
|
||
// 权限指令 v-permission
|
||
app.directive('permission', {
|
||
mounted(el, binding) {
|
||
const { value } = binding // permission_key
|
||
const userPermissions = store.state.user.permissions
|
||
|
||
if (!userPermissions.includes(value)) {
|
||
el.parentNode?.removeChild(el)
|
||
}
|
||
}
|
||
})
|
||
|
||
// 使用示例
|
||
<el-button
|
||
v-permission="'evaluation_group:create:write'"
|
||
@click="handleCreate"
|
||
>
|
||
新建分组
|
||
</el-button>
|
||
```
|
||
|
||
#### 3.2 API请求权限校验
|
||
|
||
```javascript
|
||
// 请求拦截器中添加权限检查
|
||
axios.interceptors.request.use((config) => {
|
||
const { method, url } = config
|
||
const requiredPermission = findPermissionByApi(method, url)
|
||
|
||
if (requiredPermission && !hasPermission(requiredPermission)) {
|
||
ElMessage.error('无权执行此操作')
|
||
return Promise.reject(new Error('Permission denied'))
|
||
}
|
||
|
||
return config
|
||
})
|
||
```
|
||
|
||
### 四、数据流程图
|
||
|
||
```
|
||
┌──────────────┐ GET /rbac/roles/{id}/routes ┌──────────────┐
|
||
│ 前端页面 │ ─────────────────────────────────→ │ 后端API │
|
||
│ 权限管理 │ │ │
|
||
└──────────────┘ └──────────────┘
|
||
│ │
|
||
│ ▼
|
||
│ ┌──────────────┐
|
||
│ │ 数据库查询 │
|
||
│ │ sys_routes │
|
||
│ │ permissions │
|
||
│ │ role_route │
|
||
│ └──────────────┘
|
||
│ │
|
||
│ 返回树形结构(含permissions) │
|
||
│ ←──────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────────────────────────────────┐
|
||
│ 渲染权限树 │
|
||
│ ├── 页面路由节点(可展开) │
|
||
│ │ └── API权限列表(checkbox) │
|
||
│ └── 用户勾选/取消勾选 │
|
||
└──────────────────────────────────────────────────────────────────┘
|
||
│
|
||
│ PUT /rbac/roles/{id}/routes (保存路由权限)
|
||
│ POST /api/v3/rbac/roles/{id}/permissions (保存功能权限)
|
||
▼
|
||
┌──────────────┐
|
||
│ 权限生效 │
|
||
│ 用户刷新后 │
|
||
│ 看到新权限 │
|
||
└──────────────┘
|
||
```
|
||
|
||
### 五、迁移检查清单
|
||
|
||
| 序号 | 检查项 | 状态 |
|
||
|------|--------|------|
|
||
| 1 | 更新 `GET /rbac/roles/{role_id}/routes` 接口调用,适配新的树形返回格式 | ⬜ |
|
||
| 2 | 权限管理界面支持展示「页面路由 → API权限」层级结构 | ⬜ |
|
||
| 3 | 添加 permissions 字段的 TypeScript 类型定义 | ⬜ |
|
||
| 4 | 实现按钮级权限控制(使用 permission_key) | ⬜ |
|
||
| 5 | 测试权限保存和刷新后生效 | ⬜ |
|
||
|
||
---
|
||
|
||
## 📞 技术支持
|
||
|
||
如有问题,请联系后端开发团队。
|
||
|
||
**文档版本**: v3.0
|
||
**最后更新**: 2025-11-26
|
||
**维护者**: Backend Team
|