Files
leaudit-platform-frontend/docs/role-permissions/FRONTEND_API_GUIDE.md
T
TanWenyan 689ef6bc3d 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>
2025-11-24 18:03:57 +08:00

710 lines
16 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.
# 用户权限管理接口 - 前端对接文档
## 📋 文档信息
- **版本**: 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