# HYApp 管理后台权限管理功能文档

本文档作为后台管理系统权限管理的设计基准。以后后台相关文档统一放在 `hyapp-admin-server/docs/`。

## 目标

后台权限采用 RBAC：

- 用户通过角色获得权限，不直接给用户绑定权限。
- 菜单权限控制页面入口和前端路由访问。
- 按钮权限控制页面内操作按钮、批量操作、导出、状态变更等动作。
- 后端接口必须做权限校验，前端按钮隐藏或禁用只作为体验层控制。
- 权限码是前后端契约，不能只改文案或按钮，不同步后端权限码。

## 当前实现事实

当前后端已经具备基础 RBAC 表：

- `admin_users`：后台用户。
- `admin_roles`：后台角色。
- `admin_permissions`：权限定义，字段包含 `code`、`name`、`kind`。
- `admin_user_roles`：用户角色关系。
- `admin_role_permissions`：角色权限关系。
- `admin_menus`：菜单定义，通过 `permission_code` 绑定菜单查看权限。

当前认证链路：

- 登录成功后，后端把用户权限码写入 access token。
- 前端 `AuthProvider` 保存 `permissions`，通过 `can(code)` 判断。
- 前端路由使用 `RequirePermission` 拦截页面。
- 后端 API 使用 `middleware.RequirePermission(code)` 校验。

当前注意点：

- 角色权限变更后，旧 access token 内的权限不会立即变化；用户重新登录或 refresh 后才会获得新权限。若后续需要立即生效，需要增加 session 失效或 token version 机制。
- `platform-admin` 当前会绑定全部权限；其他预置角色需要补默认权限绑定策略。
- `菜单权限` 页面当前只读，没有菜单、权限定义的增删改能力。

## 用户 App 可见范围

角色权限决定“能做什么”，用户 App 范围决定“能在哪个 App 中做”。两者必须同时满足：

- `admin_users.app_scope_mode=all`：主后台可以切换所有当前启用 App。
- `selected`：只允许切换 `admin_user_app_scopes` 中显式绑定的 App。
- `none`：主后台不发送 `X-App-Code`，只能进入通用工作台；财务工作台和运营工作台入口仍按 `finance:view`、`overview:view` 控制。
- 财务工作台的 App/区域数据范围继续由 `admin_user_money_scopes` 管理，不能与主后台 App 范围合并。
- App 级 API 除角色权限外还必须通过后端 App scope 中间件校验，不能只依赖前端隐藏下拉项。

## 权限类型

权限 `kind` 使用以下值：

| kind | 用途 | 示例 |
| --- | --- | --- |
| `menu` | 页面入口、路由访问、菜单展示 | `user:view` |
| `button` | 页面按钮、批量操作、导出、状态变更 | `user:create` |
| `api` | 独立接口权限。只有当接口不能自然归属到菜单或按钮时才使用 | `search:global` |

约定：

- 普通页面查看权限统一使用 `模块:view`。
- 创建使用 `模块:create`。
- 编辑使用 `模块:update`。
- 删除使用 `模块:delete`。
- 导出使用 `模块:export`。
- 状态切换使用 `模块:status`。
- 批量操作如果风险高于单条操作，单独定义 `模块:batch-*`。
- 不再新增过宽的 `*:manage` 权限；当前已有 `role:manage` 可兼容保留，后续应拆到按钮级权限。

## 后台基础权限矩阵

### 总览

| 权限码 | kind | 控制范围 | 后端接口 |
| --- | --- | --- | --- |
| `overview:view` | `menu` | 总览页面、总览菜单 | `GET /api/v1/dashboard/overview` |

按钮：

| 按钮 | 权限码 | 当前状态 | 目标 |
| --- | --- | --- | --- |
| 刷新 | `overview:view` | 已实现 | 查看总览即可刷新 |
| 部署服务 | `service:create` | 按钮已展示但未接动作 | 跳转或打开服务创建抽屉 |

### 服务管理

| 权限码 | kind | 控制范围 | 后端接口 |
| --- | --- | --- | --- |
| `service:view` | `menu` | 服务管理菜单、列表、详情 | `GET /api/v1/services`、`GET /api/v1/services/:id` |
| `service:create` | `button` | 创建服务、总览部署服务 | `POST /api/v1/services` |
| `service:update` | `button` | 编辑服务基础信息、配置 | `PATCH /api/v1/services/:id` |
| `service:operate` | `button` | 启动、停止、重启 | `PATCH /api/v1/services/:id/status`、`POST /api/v1/services/:id/restart` |

按钮：

| 页面 | 按钮 | 权限码 | 当前状态 | 目标 |
| --- | --- | --- | --- | --- |
| 总览 | 部署服务 | `service:create` | 缺少动作 | 接入服务创建 |
| 总览/服务管理 | 启动 | `service:operate` | 已按权限控制 | 保持 |
| 总览/服务管理 | 停止 | `service:operate` | 已按权限控制 | 保持 |
| 总览/服务管理 | 重启 | `service:operate` | 已按权限控制 | 保持 |
| 服务管理 | 创建服务 | `service:create` | 已按权限控制 | 保持 |
| 服务管理 | 编辑服务 | `service:update` | API 已有，页面未接 | 新增编辑入口 |
| 服务管理 | 更多 | 按具体动作拆分 | 当前无实际动作 | 有动作后再展示 |

### 用户管理

| 权限码 | kind | 控制范围 | 后端接口 |
| --- | --- | --- | --- |
| `user:view` | `menu` | 用户管理菜单、列表、详情 | `GET /api/v1/users`、`GET /api/v1/users/:id` |
| `user:create` | `button` | 新增后台用户 | `POST /api/v1/users` |
| `user:update` | `button` | 编辑后台用户姓名、团队、MFA、角色 | `PATCH /api/v1/users/:id` |
| `user:status` | `button` | 启用、停用、锁定、解锁、批量状态 | `PATCH /api/v1/users/:id/status`、`POST /api/v1/users/batch/status` |
| `user:reset-password` | `button` | 重置后台用户密码 | `POST /api/v1/users/:id/reset-password` |
| `user:export` | `button` | 导出后台用户 CSV | `GET /api/v1/users/export` |

按钮：

| 按钮 | 权限码 | 当前状态 | 目标 |
| --- | --- | --- | --- |
| 新增用户 | `user:create` | 已按权限控制 | 保持 |
| 编辑 | `user:update` | 已按权限控制 | 保持 |
| 锁定/解锁 | `user:status` | 已按权限控制 | 保持 |
| 启用/停用 | `user:status` | 已按权限控制 | 保持 |
| 批量启用/停用 | `user:status` | 已按权限控制 | 后续可拆 `user:batch-status` |
| 重置密码 | `user:reset-password` | 已按权限控制 | 保持 |
| 导出 | `user:export` | 当前使用 `user:view`，前端按钮未单独控制 | 拆独立导出权限 |
| 角色配置快捷入口 | `role:view` | 已按权限控制 | 保持 |

### 角色管理

当前后端使用 `role:view` 和 `role:manage`，目标需要拆细。

| 权限码 | kind | 控制范围 | 后端接口 |
| --- | --- | --- | --- |
| `role:view` | `menu` | 角色配置菜单、角色列表 | `GET /api/v1/roles` |
| `permission:view` | `menu` | 权限定义列表 | `GET /api/v1/permissions` |
| `role:create` | `button` | 新增角色 | `POST /api/v1/roles` |
| `role:update` | `button` | 编辑角色名称、编码、说明 | `PATCH /api/v1/roles/:id` |
| `role:delete` | `button` | 删除角色 | `DELETE /api/v1/roles/:id` |
| `role:permission` | `button` | 配置角色权限 | `PUT /api/v1/roles/:id/permissions` |
| `role:manage` | `button` | 兼容旧权限 | 当前已实现，后续拆分后保留迁移期 |

按钮：

| 按钮 | 权限码 | 当前状态 | 目标 |
| --- | --- | --- | --- |
| 新增角色 | `role:create` | 当前使用 `role:manage` | 拆为 `role:create` |
| 编辑角色 | `role:update` | 当前使用 `role:manage` | 拆为 `role:update` |
| 删除角色 | `role:delete` | 当前使用 `role:manage` | 拆为 `role:delete` |
| 勾选权限 | `role:permission` | 当前跟随编辑角色保存 | 拆为角色授权权限 |

### 菜单权限

当前页面只读，目标需要支持菜单树维护和权限定义查看。

| 权限码 | kind | 控制范围 | 后端接口 |
| --- | --- | --- | --- |
| `menu:view` | `menu` | 菜单权限页面、菜单树查看 | 当前用 `GET /api/v1/navigation/menus` |
| `menu:create` | `button` | 新增菜单 | 待新增 |
| `menu:update` | `button` | 编辑菜单标题、路由、图标、权限码 | 待新增 |
| `menu:delete` | `button` | 删除菜单 | 待新增 |
| `menu:sort` | `button` | 菜单排序 | 待新增 |
| `menu:visible` | `button` | 菜单显示/隐藏 | 待新增 |
| `permission:create` | `button` | 新增权限定义 | 待新增 |
| `permission:update` | `button` | 编辑权限名称、类型、说明 | 待新增 |
| `permission:delete` | `button` | 删除未绑定权限 | 待新增 |
| `permission:sync` | `button` | 从代码或配置同步权限种子 | 待新增 |

按钮：

| 按钮 | 权限码 | 当前状态 | 目标 |
| --- | --- | --- | --- |
| 新增菜单 | `menu:create` | 未实现 | 新增 |
| 编辑菜单 | `menu:update` | 未实现 | 新增 |
| 删除菜单 | `menu:delete` | 未实现 | 新增 |
| 上移/下移 | `menu:sort` | 未实现 | 新增 |
| 显示/隐藏 | `menu:visible` | 未实现 | 新增 |
| 新增权限 | `permission:create` | 未实现 | 新增 |
| 编辑权限 | `permission:update` | 未实现 | 新增 |
| 删除权限 | `permission:delete` | 未实现 | 新增 |
| 同步权限 | `permission:sync` | 未实现 | 新增 |

### 日志审计

| 权限码 | kind | 控制范围 | 后端接口 |
| --- | --- | --- | --- |
| `log:view` | `menu` | 登录日志、操作日志页面 | `GET /api/v1/logs/login`、`GET /api/v1/logs/operations` |
| `log:export` | `button` | 导出日志 | 待新增 |

按钮：

| 按钮 | 权限码 | 当前状态 | 目标 |
| --- | --- | --- | --- |
| 搜索 | `log:view` | 已实现 | 保持 |
| 导出 | `log:export` | 未实现 | 新增 |
| 查看详情 | `log:view` | 未实现 | 新增详情抽屉 |

### 个人账户

个人账户操作不走角色授权，登录用户默认可操作自己的会话和密码。

| 功能 | 权限 | 后端接口 | 当前状态 |
| --- | --- | --- | --- |
| 查看自己 | 登录态 | `GET /api/v1/auth/me` | 已实现 |
| 修改自己的密码 | 登录态 | `POST /api/v1/auth/change-password` | API 已有，前端入口未实现 |
| 退出登录 | 登录态 | `POST /api/v1/auth/logout` | 已实现 |

### 全局搜索

当前搜索接口为登录态可用：`GET /api/v1/search`。

目标：

- 搜索结果必须按用户权限过滤。
- 没有 `service:view` 时不返回服务结果。
- 没有 `user:view` 时不返回用户结果。
- 没有 `role:view` 时不返回角色结果。
- 搜索结果跳转的目标页面必须有对应菜单权限。

## App 业务后台权限预留

以下权限用于 App 业务管理模块。已接入默认菜单的模块由后台种子同步菜单、权限和平台管理员授权。

### App 用户

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `app-user:view` | `menu` | App 用户列表、详情 |
| `app-user:level` | `button` | 临时调整 App 用户富豪/魅力等级 |
| `app-user:export` | `button` | 按当前筛选条件异步导出 App 用户 |
| `app-user:update` | `button` | 修改用户资料 |
| `app-user:status` | `button` | 后台封禁、解封 |
| `app-user:password` | `button` | 后台设置 App 用户密码 |
| `app-user:identity` | `button` | 短号、靓号管理 |
| `app-user:country` | `button` | 国家/区域调整 |

### 国家和区域

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `country:view` | `menu` | 国家列表 |
| `country:create` | `button` | 新增国家 |
| `country:update` | `button` | 编辑国家 |
| `country:status` | `button` | 启用、停用国家 |
| `region:view` | `menu` | 区域列表 |
| `region:create` | `button` | 新增区域 |
| `region:update` | `button` | 编辑区域 |
| `region:status` | `button` | 停用区域 |
| `region:countries` | `button` | 维护区域国家 |

### 房间管理

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `room:view` | `menu` | 房间列表、详情、房间状态 |
| `room:update` | `button` | 后台修改房间展示资料、模式、状态、区域 |
| `room:delete` | `button` | 后台删除房间 |
| `room-pin:view` | `menu` | 房间置顶菜单、置顶列表 |
| `room-pin:create` | `button` | 新增或恢复房间置顶 |
| `room-pin:cancel` | `button` | 取消房间置顶 |
| `room-config:view` | `menu` | 房间配置页面、读取座位数配置 |
| `room-config:update` | `button` | 保存启用座位数和默认座位数 |
| `room:create` | `button` | 后台创建房间 |
| `room:mic` | `button` | 上下麦、换麦、锁麦 |
| `room:chat` | `button` | 公屏开关 |
| `room:admin` | `button` | 设置房间管理员 |
| `room:mute` | `button` | 禁言、解除禁言 |
| `room:kick` | `button` | 踢出房间 |
| `room:unban` | `button` | 房间解封用户 |

### 文件上传

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `upload:create` | `button` | 后台上传文件、图片到 COS；object key 使用后台独立前缀 |

### 钱包和交易

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `wallet:view` | `menu` | 钱包概览、余额查询 |
| `wallet:transaction-view` | `menu` | 交易流水 |
| `wallet:adjust` | `button` | 后台调账 |
| `operations-withdrawal:view` | `menu` | 用户提现运营审核列表；不包含 `operations_status=skipped` 的历史单 |
| `operations-withdrawal:audit` | `button` | 运营初审；通过后转交财务，拒绝时释放冻结余额并终止流程 |
| `finance-withdrawal:view` | `menu` | 用户提现财务审核列表；只包含运营已通过和 `skipped` 历史兼容单 |
| `finance-withdrawal:audit` | `button` | 财务终审；通过时扣减 frozen，拒绝时释放 frozen |

两个提现工作台都以 `admin_user_money_scopes` 作为 App 数据范围：显式选择 App 时必须命中授权，未选 App 的列表只查授权 App 集合，仅 `platform-admin` 的全量资金范围可跨全部 App。审核请求必须显式携带目标行的 `X-App-Code`，后端会在钱包动作前再校验 MoneyAccess。`GET /admin/finance/scope` 仅返回这份授权目录，其读权限为 `finance:view`、`finance-withdrawal:view|audit` 或 `operations-withdrawal:view|audit`，不因此赋予审核写权。

运营状态 `rejecting` 表示拒绝 claim 已独立提交、钱包释放或通知/终态收敛仍需重试。该状态只能继续调用原拒绝接口，后端沿用第一次审核人、原因和固定 command；不能改点通过，也不会进入财务列表。运营工作台必须显示“重试拒绝”，不能把它当成普通 `pending`。

#### 用户提现两级审核发布顺序

`102_user_withdrawal_operations_review.sql` 不能和旧 admin 实例混跑。旧 admin 只识别总体 `status=pending`，会绕过 `operations_status` 直接调钱包终审。生产必须按以下顺序发布：

1. 先执行 `scripts/mysql/068_wallet_withdrawal_terminal_locks.sql`，完成新 wallet-service 的全量滚动，确保所有钱包实例都能按申请 ID 收敛旧/新 command。
2. 摘流所有旧 admin 实例，确认无旧版财务审核请求在飞。
3. 执行 admin 102 迁移。迁移持久化当时的最大申请 ID，仅把该范围内已经终审的历史行标记 `skipped`；尚未终审的 `status=pending` 申请和迁移后新申请都保持运营 `pending`。
4. 部署新 admin 并恢复流量，验证运营/财务列表和 MoneyAccess 隔离。
5. 最后滚动 gateway。即使窗口内旧 gateway 不写 `operations_status`，数据库默认也会让新单进入运营待审。

### 礼物和活动

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `resource:view` | `menu` | 资源列表、资源详情 |
| `resource:create` | `button` | 新增资源 |
| `resource:update` | `button` | 编辑资源、启用、禁用 |
| `resource-shop:view` | `menu` | 道具商店售卖列表 |
| `resource-shop:update` | `button` | 添加售卖资源、配置售卖天数和启用、禁用 |
| `resource-group:view` | `menu` | 资源组列表、资源组详情 |
| `resource-group:create` | `button` | 新增资源组 |
| `resource-group:update` | `button` | 编辑资源组、替换组成员、启用、禁用 |
| `resource-grant:view` | `menu` | 资源赠送记录 |
| `resource-grant:create` | `button` | 给用户赠送资源或资源组 |
| `resource-grant:revoke` | `button` | 撤销成功的资源组赠送 |
| `gift:view` | `menu` | 礼物列表 |
| `gift:create` | `button` | 从资源库选择资源新增礼物 |
| `gift:update` | `button` | 编辑礼物、资源绑定和价格 |
| `gift:status` | `button` | 上架、下架 |
| `gift:delete` | `button` | 删除礼物列表配置 |
| `activity:view` | `menu` | 活动列表 |
| `activity:create` | `button` | 新增活动 |
| `activity:update` | `button` | 编辑活动 |
| `activity:status` | `button` | 发布、暂停、结束 |

### 主播、公会和 BD

| 权限码 | kind | 控制范围 |
| --- | --- | --- |
| `host:view` | `menu` | 主播列表、主播资料 |
| `host:update` | `button` | 编辑主播资料 |
| `host:status` | `button` | 主播状态调整 |
| `agency:view` | `menu` | 公会列表、详情 |
| `agency:create` | `button` | 新增公会 |
| `agency:update` | `button` | 编辑公会 |
| `agency:status` | `button` | 关闭公会、开关入会 |
| `bd:view` | `menu` | BD 列表 |
| `bd:create` | `button` | 新增 BD |
| `bd:update` | `button` | 编辑 BD |
| `coin-seller:view` | `menu` | 币商列表、币商余额 |
| `coin-seller:create` | `button` | 新增币商 |
| `coin-seller:update` | `button` | 启用或禁用币商 |
| `coin-seller:stock-credit` | `button` | 币商进货、金币补偿 |
| `salary:view` | `menu` | 薪资周期、薪资明细 |
| `salary:calculate` | `button` | 计算薪资 |
| `salary:approve` | `button` | 审批薪资 |
| `salary:post` | `button` | 入账薪资 |
| `salary:void` | `button` | 作废薪资 |
| `salary:adjust` | `button` | 调整薪资项 |

## 角色预设

### 超级管理员

`platform-admin`

- 拥有全部权限。
- 能维护用户、角色、权限、菜单和所有业务模块。

### 运营负责人

`ops-admin`

- 负责运营、团队、房间、资源、活动和游戏的日常管理。
- 负责用户提现运营初审，不拥有用户提现财务终审权限。
- 不包含后台设置、版本管理、财务负责人看板和三方汇率编辑/全局同步。

### 运营专员

`operations-specialist`

- 可执行用户、团队结构、房间、资源和常规运营动作。
- 可执行用户提现运营初审，不拥有用户提现财务终审权限。
- 活动配置和游戏列表/自研游戏只读；可管理全站机器人和房内猜拳配置。

### 产品负责人

`product-lead`

- 可管理 APP 配置、版本、房间、资源、活动和游戏。
- 团队、支付、地区和 App 用户主资料按表格保持只读或限定动作。

### 产品专员

`product-specialist`

- 负责资源配置；房间仅可管理机器人配置。
- APP 配置、活动、游戏、支付和地区模块保持只读。

### 财务负责人

`finance-lead`

- 只进入财务看板、充值对账、用户提现财务终审和房内猜拳订单；不参与运营初审。

### 财务专员

`finance-specialist`

- 当前与财务负责人使用同一权限矩阵，负责用户提现财务终审且不参与运营初审；保留独立角色用于人员分工和后续数据范围配置。

固定岗位执行“同步权限”或显式 bootstrap 时按上述矩阵精确重建，清除历史跨模块绑定。历史 `auditor`、`readonly` 和用户自建角色不会被删除或重置。

## 前端落地规则

页面路由：

- 路由必须配置菜单查看权限。
- 没有菜单权限时跳转到用户有权限的第一个页面；不能固定跳到无权限页面。

菜单：

- 左侧菜单只展示当前用户拥有 `permission_code` 的菜单。
- 父菜单没有权限码时，只要存在可见子菜单即可展示。
- 父菜单没有可见子菜单时不展示。

按钮：

- 危险操作按钮没有权限时不展示。
- 表格行内操作没有权限时不展示，必要时可以禁用但必须有明确状态。
- 批量操作必须同时检查权限和选中项。
- 导出按钮必须有独立导出权限。

搜索：

- 搜索入口登录后可见。
- 搜索结果按权限过滤。
- 点击结果前仍由路由权限兜底。

## 后端落地规则

接口：

- 除登录、刷新、健康检查外，所有接口必须先通过认证。
- 每个写接口必须绑定按钮权限。
- 每个读接口必须绑定菜单查看权限或更细读权限。
- 批量、导出、重置密码、状态变更不能复用普通查看权限。

日志：

- 创建、更新、删除、授权、状态变更、密码重置、导出、批量操作必须写操作日志。
- 操作日志需要记录操作者、资源、动作、结果、详情、IP、路径。

权限变更：

- 角色权限保存后必须记录操作日志。
- 后续若要求立即生效，需要使相关用户 session 失效或提高 token version。

数据约束：

- 权限 `code` 唯一。
- 菜单 `code` 唯一。
- 删除权限前必须确认没有角色绑定、没有菜单引用。
- 删除菜单前必须确认没有子菜单，或明确采用级联删除策略。
- 删除角色前必须确认没有用户绑定，或要求先解绑。

## 需要补齐的接口

### 权限定义

| 方法 | 路径 | 权限 |
| --- | --- | --- |
| `GET` | `/api/v1/permissions` | `permission:view` |
| `POST` | `/api/v1/permissions` | `permission:create` |
| `PATCH` | `/api/v1/permissions/:id` | `permission:update` |
| `DELETE` | `/api/v1/permissions/:id` | `permission:delete` |
| `POST` | `/api/v1/permissions/sync` | `permission:sync` |

### 菜单管理

| 方法 | 路径 | 权限 |
| --- | --- | --- |
| `GET` | `/api/v1/system/menus` | `menu:view` |
| `POST` | `/api/v1/system/menus` | `menu:create` |
| `PATCH` | `/api/v1/system/menus/:id` | `menu:update` |
| `DELETE` | `/api/v1/system/menus/:id` | `menu:delete` |
| `PATCH` | `/api/v1/system/menus/:id/visible` | `menu:visible` |
| `PUT` | `/api/v1/system/menus/sort` | `menu:sort` |

说明：

- `GET /api/v1/navigation/menus` 保留为当前用户菜单接口，只返回当前用户可见菜单。
- `GET /api/v1/system/menus` 是后台菜单维护接口，返回全量菜单树。

### 角色权限

| 方法 | 路径 | 权限 |
| --- | --- | --- |
| `GET` | `/api/v1/roles` | `role:view` |
| `POST` | `/api/v1/roles` | `role:create` |
| `PATCH` | `/api/v1/roles/:id` | `role:update` |
| `DELETE` | `/api/v1/roles/:id` | `role:delete` |
| `PUT` | `/api/v1/roles/:id/permissions` | `role:permission` |

## 前端页面规划

### 角色配置页

功能：

- 角色列表。
- 新增角色。
- 编辑角色。
- 删除角色。
- 打开授权抽屉。
- 按模块、菜单、按钮分组勾选权限。

按钮权限：

- 新增角色：`role:create`
- 编辑角色：`role:update`
- 删除角色：`role:delete`
- 保存授权：`role:permission`

### 菜单权限页

功能：

- 菜单树维护。
- 菜单绑定权限码。
- 菜单排序。
- 菜单显隐。
- 权限定义列表。
- 权限同步。

按钮权限：

- 新增菜单：`menu:create`
- 编辑菜单：`menu:update`
- 删除菜单：`menu:delete`
- 排序：`menu:sort`
- 显隐：`menu:visible`
- 新增权限：`permission:create`
- 编辑权限：`permission:update`
- 删除权限：`permission:delete`
- 同步权限：`permission:sync`

### 用户管理页

功能：

- 用户列表。
- 新增后台用户。
- 编辑后台用户。
- 绑定角色。
- 启用、停用、锁定、解锁。
- 重置密码。
- 导出。

按钮权限：

- 新增用户：`user:create`
- 编辑用户：`user:update`
- 状态操作：`user:status`
- 重置密码：`user:reset-password`
- 导出：`user:export`

## 验收标准

- 无权限用户看不到对应菜单。
- 直接访问无权限路由会被拦截。
- 无按钮权限用户看不到对应操作按钮。
- 直接调用无权限接口返回 403。
- 角色授权保存后，用户重新登录或 refresh 后权限生效。
- 创建、更新、删除、授权、导出、批量和状态类操作都有操作日志。
- 全局搜索结果按当前用户权限过滤。
- `platform-admin` 拥有所有权限。
- 六个岗位角色的权限与 `093_role_permission_matrix.sql` 及后端默认矩阵完全一致。
- 游戏列表、自研游戏、全站机器人、房内猜拳配置和房内猜拳订单互不借用查看或更新权限。
- 支付账单导出/刷新、支付方式更新/同步、汇率编辑/同步互不借用列表查看权限。

## 维护规则

1. 新增页面时先定义独立的 `*:view`，再按真实副作用拆按钮权限。
2. 不允许为了复用一个 Handler，把相邻菜单重新绑定到同一个宽权限。
3. 固定岗位矩阵变更必须同时更新默认种子、版本化迁移和矩阵一致性测试。
4. 旧权限码只能作为自定义历史角色的兼容入口，不能重新分配给七类固定岗位。
