# 后台 RBAC 与导航分工

## 单一真相源

| 能力 | 来源 | 说明 |
|------|------|------|
| 侧栏 / `auth/me` 的 `navigation` | `App\Support\AdminAuthorizationRegistry::navigationDefinitions()` | **代码注册表**，改菜单需发版 |
| 角色可勾选的产品权限 `prd.*` | 同上 `permissionDefinitions()` | UI 展示名与 `permission_code` 映射 |
| API 鉴权 `permission_code` | 库表 `admin_menu_actions` | 运行时校验 |
| 路由资源 | 库表 `admin_api_resources` + `admin_api_resource_bindings` | 由 `php artisan lottery:admin-auth-sync` 从 Registry 同步 |

## `admin_menus` 不是侧栏配置

- 表 `admin_menus`：仅用于 **`admin_menu_actions` 的业务分组**（权限模块树）。
- **不要**通过改 `admin_menus` 期望侧栏变化；侧栏只看 Registry + 用户拥有的 `prd.*`。

## 角色权限如何存储

- **权威数据**：`admin_role_menu_actions`（角色 ↔ 动作权限）。
- **`prd.*` 展示**：由 `AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes()` 从已授权动作**反推**，不单独落库。
- 用户直接授权：`admin_user_menu_actions`（可选，与角色权限合并生效）。

## 维护命令

```bash
php artisan lottery:admin-auth-sync --audit   # 同步 API 资源与 bindings，并体检
php artisan lottery:admin-auth-audit          # 仅体检：受保护路由是否登记、permission_required 是否已绑定
```

`migrate:fresh --seed` 会跑完全部迁移（含将 Registry 写回库的 resync 类迁移）与 `DatabaseSeeder`；**仅修改** `AdminAuthorizationRegistry.php` 时，在已有库上执行 `lottery:admin-auth-sync --audit`，不要依赖单独补丁 migration。

## 仪表盘 API 与子块权限

- `GET /api/v1/admin/dashboard` 与 `…/analytics`：中间件要求 `dashboard.view`（对应产品权限 `prd.dashboard.view`）。
- 子块权限判定统一按 `permission_code`（如 `draw.results.view`、`risk.monitor.view`、`service.reconcile.view`），`prd.*` 仅作为展示映射。

## 站点优先作用域约束（2026-06）

- 后台查询范围统一为：`site_scope ∩ agent_subtree_scope`。
- 新增统一入口 `App\Support\AdminScopePolicy`，查询应优先通过该策略应用数据范围。
- `auth/me` 字段分工：
  - `permissions`：legacy `prd.*` 产品权限（侧栏、角色 UI、旧页面门控）
  - `operational_permissions`：`admin_menu_actions.permission_code`（与 API 中间件鉴权一致）
  - `account_kind`：`super_admin` | `site_admin` | `site_finance` | `site_cs` | `agent_operator` | `platform_account`

前端新代码优先使用 `useAdminPermission().hasAnyCode()` 与 `operational_permissions`；`permissions` / `prd.*` 逐步废弃。

## 已废弃的 `prd.*`（请求体仍可传入，会自动归一）

| 旧 slug | 归一为 |
|---------|--------|
| `prd.audit.all` / `prd.audit.self` / `prd.audit.finance` | `prd.audit.view` |
| `prd.report.all` / `prd.report.risk` / `prd.report.finance` / `prd.report.player` | `prd.report.view` |

## 按站点开通后台（数据隔离）

所有人登录**同一套** lotteryadmin（`POST /api/v1/admin/auth/login`）。登录后 API 按账号绑定自动过滤站点数据，**无需**为每个站点单独部署后台域名。

| 给谁 | 账号形态 | 数据范围来源 | 推荐入口 |
|------|----------|--------------|----------|
| 整条代理线负责人 | **代理经营账号**（`admin_user_agents` 主绑定） | `agent_nodes.admin_site_id` + 代理子树 `path` | 超管：**代理线路 → 开通线路** |
| 平台侧站点运营 | **平台账号**（无代理绑定） | `admin_user_site_roles.site_id` | 超管：**系统 → 平台账号**，创建时选择 `admin_site_id` 与角色 |

**菜单权限**（能进哪些页）由角色的 `admin_role_menu_actions` / 反推 `prd.*` 决定；**数据范围**（能看哪些站点的行）由 `admin_user_site_roles.site_id`（平台）或代理绑定（线路）决定，二者独立。

### 路径 A：开通线路（代理主账号）

1. 侧栏 **代理线路 → 开通线路**（`prd.agent-line.provision`）。
2. 填写站点 `code`、名称、线路主 **用户名 / 密码**。
3. 后端一次性创建：`admin_sites`、根 `agent_nodes`、`admin_users`、`admin_user_agents`、平台代理经营角色。
4. 将账号密码交给对方；对方用同一 lotteryadmin 地址登录。
5. 登录后 `auth/me.agent` 含 `admin_site_id`、`site_code`；侧栏隐藏「开通线路」等平台-only 菜单；列表经 `AdminScopePolicy` 收敛到该站点 + 代理子树。

**子账号**：在代理树下 **代理节点 → 管理员**（`POST /api/v1/admin/agent-nodes/{id}/admin-users`），站点不变、子树更窄。勿在「系统 → 平台账号」为线路主建号。

### 路径 B：平台运营账号（单站）

1. 平台 **角色管理** 内置角色：
   - **超级管理员**（`is_super_admin`，全库唯一，不绑站点）
   - **站点管理员**（`slug=site_admin`）：接入站点默认主运营；代理/玩家/信用结算/钱包对账/经营报表（含导出）/期号只读
   - **站点财务**（`slug=site_finance`）：财务工作台（对账异常、待确认/待收付账单）；对账、报表导出、补单冲正、结算收付；不含代理树经营
   - **站点客服**（`slug=site_cs`）：客服工作台（玩家/今日注单）；玩家/注单/单用户钱包查询；不含全局报表与代理
   - **代理**（`slug=agent`）：经营主账号默认模板
2. **系统 → 平台账号 → 新建**：填写账号信息，**选择目标站点**（`admin_site_id`），勾选角色（默认接入站自动绑 `site_admin`）。
3. 对方登录后仅见绑定站点数据；`auth/me.accessible_sites` 列出可访问站点（单站时一项）。

改角色绑定时须带上同一 `admin_site_id`（`PUT /api/v1/admin/admin-users/{id}/roles`），仅替换该站点上的角色 pivot，不影响其他站点绑定。

### 运行时过滤（已实现）

- 可访问站点：`AdminUser::accessibleAdminSiteIds()`（超管 `null` = 不限）。
- 查询：`AdminSiteScope` + `AdminScopePolicy`（`site_scope ∩ agent_subtree_scope`）。
- 操作者授权站点：`AdminIntegrationSiteAccess::canAccess()`（创建/改绑平台用户时校验）。

### 临时手工绑站（迁移前）

若 UI 未就绪，超管建用户后可在库表 `admin_user_site_roles` 插入 `(admin_user_id, site_id, role_id)`，参考 `tests/Feature/AdminIntegrationSiteApiTest.php`。
