# admin_role — 运营后台角色(RBAC 可见页配置)

> 模型 `app/models/admin_role.py` · 仓库 `app/admin/repositories/admin_role.py` · 权限目录 `app/admin/permissions.py` · 接口 admin `GET/POST /admin/api/roles`、`PATCH/DELETE /admin/api/roles/{id}`、`GET /admin/api/roles/catalog`(`app/admin/routers/roles.py`) · [← 索引](./README.md) · [总览](./OVERVIEW.md)

运营后台的**角色 → 可见页面**配置表。#117 把 RBAC 从「代码写死三角色」升级为数据驱动:内建角色(`super_admin`/`finance`/`operator`)种子进表(`is_builtin=true`,不可删),`super_admin` 可另建**自定义角色**(#126)勾选任意页面组合。`admin_user.role` 存角色 `name` 按名引用本表;单个管理员还可用 `admin_user.pages_override` 在角色之上覆盖个人可见页。

## 用在哪 / 增删改查
- **C(插入)**:内建三角色随迁移种子;`POST /admin/api/roles`(super_admin)新建自定义角色。
- **U(更新)**:`PATCH /admin/api/roles/{id}` 改展示名/可见页(内建角色的 `pages` 也可调)。
- **D(删除)**:`DELETE /admin/api/roles/{id}`——**内建角色与在用角色(有 admin_user.role 引用)不可删**。
- **R**:登录/每次 admin 鉴权时按 `admin_user.role` 查本表算有效可见页(`pages_override` 非空则以覆盖为准);`GET /admin/api/roles/catalog` 返回全部可配页面目录(分组,来自 `permissions.py` 常量,不落库)。

## 字段
| 列 | 类型 | 约束 / 默认 | 说明 |
|---|---|---|---|
| `id` | Integer | PK, autoincrement | |
| `name` | String(32) | UNIQUE, index, NOT NULL | 角色标识(被 `admin_user.role` 按名引用):内建 `super_admin`/`finance`/`operator`;**自定义角色 name(key)= label = 创建时的输入名称**(不能叫 super_admin,重名 409) |
| `label` | String(32) | NOT NULL, default `""` | 展示名(后台下拉里显示) |
| `pages` | JSON | NOT NULL, default `[]` | 可见页面 key 列表(全集见 `permissions.py` 目录;前端按它渲染菜单,后端接口守卫同源校验)。**`super_admin` 行 pages 存空数组**,有效可见页特判为全部 |
| `is_builtin` | Boolean | NOT NULL, default false | 内建角色标记(不可删;`super_admin` 恒过所有守卫,不依赖 pages) |
| `created_at` | DateTime(tz) | server_default now() | |

## 关系 / Join Key
- ← `admin_user.role` **按 `name` 语义引用**(无硬 FK;删除保护在应用层:在用角色不可删)。
- 与 `admin_user.pages_override` 的关系:有效可见页 = `pages_override`(个人覆盖,非空优先)∪ 否则取角色 `pages`;`super_admin` 无视两者恒全通。

## 索引与约束
- PK `id`;UNIQUE+index `name`。

## 注意
- 页面 key 目录维护在代码 `app/admin/permissions.py`(加新后台页面要同步登记),表里只存勾选结果——目录变更不需要迁移。
- 角色守卫兼容旧语义:`require_role("finance")` 等旧代码路径仍工作,内建角色名不可改。
