fix: 修复角色权限管理模块的API认证和数据加载问题

主要修复:
1. 修复所有RBAC API函数使用axios-client(自动添加JWT token)
   - getRoles, createRole, updateRole, deleteRole 从rbacFetch切换到axios-client
   - 解决401未授权导致的数据加载失败问题

2. 修复用户ID字段不匹配问题
   - getAllUsers函数使用user_id字段(兼容user.user_id || user.id)
   - 确保角色分配时使用正确的用户ID

3. 修复路由ID不匹配问题
   - getRoutes函数改用真实后端API(GET /rbac/user/routes)
   - 解决前端Mock路由ID与数据库不一致导致的400错误

4. 增强axios-client成功响应识别
   - 支持code=200作为成功状态(原本只支持code=0)
   - 兼容不同后端API的响应格式

5. 实现用户单角色限制功能
   - 添加getUserRoles API函数
   - 分配角色前检查用户现有角色
   - 在用户列表中显示当前角色标签

6. 改进创建角色的表单验证
   - role_key必须以字母开头(正则:^[a-z][a-z0-9_]*$)
   - 添加实时验证提示
   - 更新提示文案说明规则

7. 添加删除操作的安全确认机制
   - 删除角色/移除用户角色前显示确认模态框
   - 3秒倒计时后才能确认删除
   - 成功删除后自动刷新数据

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-24 18:03:57 +08:00
parent 1b546e6818
commit 689ef6bc3d
5 changed files with 2802 additions and 174 deletions
+709
View File
@@ -0,0 +1,709 @@
# 用户权限管理接口 - 前端对接文档
## 📋 文档信息
- **版本**: v2.0
- **更新日期**: 2025-01-24
- **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`
**请求体**:
```json
{
"role_key": "department_leader",
"role_name": "部门负责人",
"description": "负责部门日常管理",
"data_scope": "DEPT",
"metadata": {}
}
```
**响应示例**:
```json
{
"code": 200,
"message": "角色创建成功",
"data": {
"id": 6,
"role_key": "department_leader",
"role_name": "部门负责人",
"data_scope": "DEPT",
"is_system": false
}
}
```
---
### 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 获取角色可访问路由
**接口**: `GET /rbac/roles/{role_id}/routes`
**响应示例**:
```json
{
"code": 200,
"msg": "success",
"data": {
"role_id": 2,
"routes": [
{"id": 1, "route_path": "/", "route_name": "Home", "route_title": "首页"},
{"id": 11, "route_path": "/dashboard", "route_name": "Dashboard"}
]
}
}
```
---
### 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_id52, 1, 2
- [ ] 实现Token过期自动刷新机制
- [ ] 处理401/403错误(跳转登录/权限提示)
- [ ] 使用 `PUT /rbac/roles/{role_id}/routes` 实现路由权限分配
- [ ] 处理 `total` 字段为0的情况(用数组长度代替)
---
## 📞 技术支持
如有问题,请联系后端开发团队。
**文档版本**: v2.0
**最后更新**: 2025-01-24
**维护者**: Backend Team