# 用户权限管理接口 - 前端对接文档 ## 📋 文档信息 - **版本**: 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 ``` ### 三、权限检查逻辑修改 #### 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) } } }) // 使用示例 新建分组 ``` #### 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