From 99eb557c0157b87884643975e61a5dea11e2c3ea Mon Sep 17 00:00:00 2001 From: wangrui233 Date: Sun, 25 Oct 2026 22:52:47 +0800 Subject: [PATCH] rbac --- .codebuddy/docs/admin-rbac.md | 450 ++++++++++++++++++++++++++++++++++ 1 file changed, 450 insertions(+) create mode 100644 .codebuddy/docs/admin-rbac.md diff --git a/.codebuddy/docs/admin-rbac.md b/.codebuddy/docs/admin-rbac.md new file mode 100644 index 0000000..2a4c7e8 --- /dev/null +++ b/.codebuddy/docs/admin-rbac.md @@ -0,0 +1,450 @@ +# 后台管理 · RBAC 模块(管理员 / 角色 / 权限) + +> 本文件描述 `/api/admin/rbac` 下的**后台 RBAC 管理**(管理员、角色、权限)相关接口。公共约定(请求头、统一响应、鉴权中间件、错误码、配置)见 [admin-api.md](./admin-api.md)。 + +## 1. 接口列表 + +所有接口均需登录后携带 `Authorization: Bearer `(由 `AdminAuth` 校验),并经由 `PermissionLoader` 加载权限集合后,由路由守卫(`RequirePerm`)按权限 code 拦截: + +| 资源 | 路由前缀 | 所需权限 code | +| --- | --- | --- | +| 管理员 | `/api/admin/rbac/admins` | `admin:manage` | +| 角色 | `/api/admin/rbac/roles` | `role:manage` | +| 权限 | `/api/admin/rbac/permissions` | `perm:manage` | + +> 超级管理员角色(`super_admin`)对所有接口短路放行,无需逐条匹配权限。 + +状态枚举(`admin_users` / `admin_roles` 的 `status`):`0` 禁用,`1` 启用(默认 `1`)。 + +--- + +### 1.1 管理员接口(需 `admin:manage`) + +#### 1.1.1 `POST /api/admin/rbac/admins` + +新建管理员。用户名全局唯一,密码以 bcrypt 哈希存储。 + +**请求体** + +```json +{ + "username": "alice", + "nickname": "Alice", + "password": "Admin@123456", + "status": 1 +} +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `username` | string | 是 | 管理员账号,全局唯一。已存在返回 `40900`。 | +| `nickname` | string | 否 | 昵称。 | +| `password` | string | 是 | 明文密码,后端以 bcrypt 哈希存储。 | +| `status` | int8 | 否 | `0` 禁用,`1` 启用;缺省或 `0` 时置为 `1`;非法值返回 `40000`。 | + +**响应**:返回创建后的 `User` 对象(见 §2.1),`password_hash` 不返回。 + +**curl 示例** + +```bash +curl -X POST http://localhost:8080/api/admin/rbac/admins \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"username":"alice","password":"Admin@123456","status":1}' +``` + +#### 1.1.2 `GET /api/admin/rbac/admins` + +分页查询管理员列表,支持按关键字与状态过滤。 + +**查询参数** + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `keyword` | string | 空 | 模糊匹配 `username` 或 `nickname`。 | +| `status` | int8 | `-1`(全部) | `0` 禁用,`1` 启用,`-1` 全部。 | +| `page` | int | `1` | 页码,从 1 开始。 | +| `size` | int | `20` | 每页条数。 | + +**响应** + +```json +{ + "code": 0, + "msg": "ok", + "data": { + "list": [ + { + "id": 1, + "username": "alice", + "nickname": "Alice", + "status": 1, + "created_at": "2026-01-01T00:00:00Z", + "updated_at": "2026-01-01T00:00:00Z" + } + ], + "total": 1, + "page": 1, + "size": 20 + } +} +``` + +#### 1.1.3 `GET /api/admin/rbac/admins/:id` + +查询单个管理员。 + +**路径参数**:`id`(uint64)。 + +**响应**:返回 `User` 对象;不存在返回 `40400`。 + +#### 1.1.4 `PUT /api/admin/rbac/admins/:id` + +编辑管理员昵称 / 状态。字段以指针区分「未提供」与「置空」,全部为空时直接返回原记录。 + +**路径参数**:`id`(uint64)。 + +**请求体** + +```json +{ + "nickname": "新昵称", + "status": 0 +} +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `nickname` | string | 否 | 昵称,提供即更新。 | +| `status` | int8 | 否 | `0` 禁用,`1` 启用;非法值返回 `40000`。 | + +**响应**:返回更新后的 `User` 对象。 + +#### 1.1.5 `DELETE /api/admin/rbac/admins/:id` + +删除管理员(同时清理其角色关联并失效权限缓存)。 + +**路径参数**:`id`(uint64)。 + +**响应**:`{ "code": 0, "msg": "ok" }`。 + +#### 1.1.6 `POST /api/admin/rbac/admins/:id/roles` + +为管理员分配角色(**全量替换**)。 + +**路径参数**:`id`(uint64)。 + +**请求体** + +```json +{ "role_ids": [1, 2] } +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `role_ids` | []uint64 | 是(binding:`required`) | 角色 ID 列表,覆盖该管理员原有关联。 | + +**响应**:`{ "code": 0, "msg": "ok" }`。 + +#### 1.1.7 `POST /api/admin/rbac/admins/:id/reset-pwd` + +重置管理员密码。 + +**路径参数**:`id`(uint64)。 + +**请求体** + +```json +{ "password": "New@123456" } +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `password` | string | 是(binding:`required`) | 新明文密码,后端以 bcrypt 哈希存储。 | + +**响应**:`{ "code": 0, "msg": "ok" }`。 + +--- + +### 1.2 角色接口(需 `role:manage`) + +#### 1.2.1 `POST /api/admin/rbac/roles` + +新建角色。`code` 全局唯一。 + +**请求体** + +```json +{ + "code": "editor", + "name": "编辑", + "description": "内容编辑角色", + "status": 1 +} +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `code` | string | 是 | 角色标识,全局唯一;已存在返回 `40900`。 | +| `name` | string | 是 | 角色名称。 | +| `description` | string | 否 | 描述。 | +| `status` | int8 | 否 | `0` 禁用,`1` 启用;缺省或 `0` 时置为 `1`;非法值返回 `40000`。 | + +**响应**:返回创建后的 `Role` 对象(见 §2.2)。 + +**curl 示例** + +```bash +curl -X POST http://localhost:8080/api/admin/rbac/roles \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"code":"editor","name":"编辑","status":1}' +``` + +#### 1.2.2 `GET /api/admin/rbac/roles` + +分页查询角色列表,支持按关键字与状态过滤。 + +**查询参数** + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `keyword` | string | 空 | 模糊匹配 `code` 或 `name`。 | +| `status` | int8 | `-1`(全部) | `0` 禁用,`1` 启用,`-1` 全部。 | +| `page` | int | `1` | 页码。 | +| `size` | int | `20` | 每页条数。 | + +**响应**:`data` 为 `{ "list": [...Role], "total": N, "page": 1, "size": 20 }`。 + +#### 1.2.3 `GET /api/admin/rbac/roles/:id` + +查询单个角色。 + +**路径参数**:`id`(uint64)。 + +**响应**:返回 `Role` 对象;不存在返回 `40400`。 + +#### 1.2.4 `PUT /api/admin/rbac/roles/:id` + +编辑角色。字段以指针区分「未提供」与「置空」,全部为空时直接返回原记录。 + +**路径参数**:`id`(uint64)。 + +**请求体** + +```json +{ + "name": "新名称", + "description": "新描述", + "status": 0 +} +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `name` | string | 否 | 角色名称,提供即更新。 | +| `description` | string | 否 | 描述,提供即更新。 | +| `status` | int8 | 否 | `0` 禁用,`1` 启用;非法值返回 `40000`。 | + +**响应**:返回更新后的 `Role` 对象。 + +#### 1.2.5 `DELETE /api/admin/rbac/roles/:id` + +删除角色(清理关联并失效权限缓存)。 + +**路径参数**:`id`(uint64)。 + +**响应**:`{ "code": 0, "msg": "ok" }`。 + +#### 1.2.6 `POST /api/admin/rbac/roles/:id/permissions` + +为角色分配权限(**全量替换**)。 + +**路径参数**:`id`(uint64)。 + +**请求体** + +```json +{ "permission_ids": [1, 2, 3] } +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `permission_ids` | []uint64 | 是(binding:`required`) | 权限 ID 列表,覆盖该角色原有关联。 | + +**响应**:`{ "code": 0, "msg": "ok" }`。 + +--- + +### 1.3 权限接口(需 `perm:manage`) + +权限分两类:`api`(接口权限,对应后端守卫 code)与 `menu`(菜单权限,用于前端动态渲染菜单树)。 + +#### 1.3.1 `POST /api/admin/rbac/permissions` + +新建权限。`code` 全局唯一。 + +**请求体** + +```json +{ + "code": "user:view", + "name": "用户查看", + "type": "api", + "method": "GET", + "path": "/api/admin/users", + "parent_id": 0, + "sort": 0 +} +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `code` | string | 是 | 权限标识,全局唯一;已存在返回 `40900`。 | +| `name` | string | 是 | 权限名称。 | +| `type` | string | 是 | 权限类型:`api` 或 `menu`;非法值返回 `40000`。 | +| `method` | string | 否 | 接口方法(如 `GET`/`POST`),`type=api` 时建议填写。 | +| `path` | string | 否 | 接口路径或菜单路由;`type=api` 时建议填写。 | +| `parent_id` | uint64 | 否 | 父级权限 ID,菜单树用;`0` 表示顶级。 | +| `sort` | int | 否 | 排序号,菜单树用。 | + +**响应**:返回创建后的 `Permission` 对象(见 §2.3)。 + +**curl 示例** + +```bash +curl -X POST http://localhost:8080/api/admin/rbac/permissions \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"code":"user:view","name":"用户查看","type":"api","method":"GET","path":"/api/admin/users"}' +``` + +#### 1.3.2 `GET /api/admin/rbac/permissions` + +分页查询权限列表,支持按类型与关键字过滤。 + +**查询参数** + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `type` | string | 空(全部) | `api` 或 `menu`;为空表示全部类型。 | +| `keyword` | string | 空 | 模糊匹配 `code` 或 `name`。 | +| `page` | int | `1` | 页码。 | +| `size` | int | `20` | 每页条数。 | + +**响应**:`data` 为 `{ "list": [...Permission], "total": N, "page": 1, "size": 20 }`。 + +#### 1.3.3 `GET /api/admin/rbac/permissions/:id` + +查询单个权限。 + +**路径参数**:`id`(uint64)。 + +**响应**:返回 `Permission` 对象;不存在返回 `40400`。 + +#### 1.3.4 `PUT /api/admin/rbac/permissions/:id` + +编辑权限。字段以指针区分「未提供」与「置空」,全部为空时直接返回原记录。 + +**路径参数**:`id`(uint64)。 + +**请求体** + +```json +{ + "name": "新名称", + "type": "menu", + "method": "GET", + "path": "/dashboard", + "parent_id": 0, + "sort": 1 +} +``` + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `name` | string | 否 | 名称,提供即更新。 | +| `type` | string | 否 | `api` 或 `menu`;非法值返回 `40000`。 | +| `method` | string | 否 | 接口方法,提供即更新。 | +| `path` | string | 否 | 路径/路由,提供即更新。 | +| `parent_id` | uint64 | 否 | 父级权限 ID,提供即更新。 | +| `sort` | int | 否 | 排序号,提供即更新。 | + +**响应**:返回更新后的 `Permission` 对象。 + +#### 1.3.5 `DELETE /api/admin/rbac/permissions/:id` + +删除权限(清理关联并失效权限缓存)。 + +**路径参数**:`id`(uint64)。 + +**响应**:`{ "code": 0, "msg": "ok" }`。 + +--- + +## 2. 数据模型 + +### 2.1 User(后台管理员) + +| 字段 | 类型 | JSON | 说明 | +| --- | --- | --- | --- | +| `ID` | uint64 | `id` | 主键,自增。 | +| `Username` | string | `username` | 账号,全局唯一。 | +| `PasswordHash` | string | `password_hash` | **不返回**(json `-`)。 | +| `Nickname` | string | `nickname` | 昵称。 | +| `Status` | int8 | `status` | `0` 禁用,`1` 启用(默认 `1`)。 | +| `CreatedAt` | time | `created_at` | 创建时间。 | +| `UpdatedAt` | time | `updated_at` | 更新时间。 | + +### 2.2 Role(角色) + +| 字段 | 类型 | JSON | 说明 | +| --- | --- | --- | --- | +| `ID` | uint64 | `id` | 主键,自增。 | +| `Code` | string | `code` | 角色标识,全局唯一。 | +| `Name` | string | `name` | 角色名称。 | +| `Description` | string | `description` | 描述。 | +| `Status` | int8 | `status` | `0` 禁用,`1` 启用(默认 `1`)。 | +| `CreatedAt` | time | `created_at` | 创建时间。 | +| `UpdatedAt` | time | `updated_at` | 更新时间。 | + +### 2.3 Permission(权限) + +| 字段 | 类型 | JSON | 说明 | +| --- | --- | --- | --- | +| `ID` | uint64 | `id` | 主键,自增。 | +| `Code` | string | `code` | 权限标识,全局唯一。 | +| `Name` | string | `name` | 权限名称。 | +| `Type` | string | `type` | `api`(接口)或 `menu`(菜单)。 | +| `Method` | string | `method` | 接口方法(如 `GET`)。 | +| `Path` | string | `path` | 接口路径或菜单路由。 | +| `ParentID` | uint64 | `parent_id` | 父级权限 ID(`0` 为顶级)。 | +| `Sort` | int | `sort` | 排序号(菜单树用)。 | +| `CreatedAt` | time | `created_at` | 创建时间。 | + +### 2.4 关联中间表 + +| 表名 | 字段 | 说明 | +| --- | --- | --- | +| `admin_user_roles` | `admin_id`, `role_id` | 管理员-角色多对多。 | +| `admin_role_permissions` | `role_id`, `permission_id` | 角色-权限多对多。 | + +--- + +## 3. 补充错误码 + +除 [admin-api.md](./admin-api.md) §2.4 所列错误码外,RBAC 模块额外使用: + +| 业务码 | 含义 | +| --- | --- | +| `40900` | 资源冲突(用户名已存在 / 角色 `code` 已存在 / 权限 `code` 已存在)。 | + +> 资源不存在统一返回 `40400`;状态/类型等非法枚举统一返回 `40000`。 + +## 4. 备注 + +- **权限守卫**:RBAC 管理接口自身受 `RequirePerm` 守卫(见 §1 表),需调用方具备对应权限 `code`;`super_admin` 角色短路放行。权限 `code` 常量定义见 `internal/middleware/permission.go`(`PermAdminManage` / `PermRoleManage` / `PermPermManage`)。 +- **缓存失效**:新建/编辑/删除管理员、角色、权限,或调整角色-权限、管理员-角色关联后,会失效对应权限缓存(`PermissionProvider` 的 `Load`/`MenuTree` 优先走缓存),下次请求重新加载。 +- **种子初始化**:服务启动时由 `provider.Seed` 用环境变量 `ADMIN_SEED_USERNAME` / `ADMIN_SEED_PASSWORD`(缺省 `admin` / `Admin@123456`)初始化首个管理员,详见 `internal/router/router.go`。 +- **建表脚本**:RBAC 相关表结构见 `scripts/001_rbac.sql`(项目未开启 AutoMigrate,需手动执行)。 +- 新增/修改上述接口时须同步更新本文档。