# admin-portal 对接契约

> 负责人：ai16
> 关联：[matrix.md](./matrix.md)、[coord.md](./coord.md)、[ai-allocation.md §5 ai16](../../ai-allocation.md)、[iam.proto](../../../packages/shared-proto/proto/iam.proto)、[events.proto](../../../packages/shared-proto/proto/events.proto)
> 说明：本契约基于 GraphQL（ARB-001 admin 命名空间）+ 端口 4003 + ai16 归属，作为 01/02 文档修订基准（见 [objections/admin-portal_issue.md](../objections/admin-portal_issue.md) ISSUE-001~006）。

---

## §1 我提供什么（对外接口）

### 1.1 gRPC 接口

无。admin-portal 是前端微前端 Remote，不提供 gRPC。

### 1.2 前端路由（MF Remote 暴露给 Shell 动态加载）

> admin-portal 是前端，**不对外提供 HTTP 端点**。此处列出的是 admin-portal 在 Shell 路由树 `/admin/*` 下注册的前端页面路由，由 Shell 动态 import admin Remote 加载。

| 路由                | 页面             | 权限点                 | 数据范围        |
| ------------------- | ---------------- | ---------------------- | --------------- |
| `/admin/dashboard`  | 管理员仪表盘     | `ADMIN_DASHBOARD_VIEW` | L3-L5           |
| `/admin/users`      | 用户管理         | `IAM_USER_READ`        | L3-L5           |
| `/admin/roles`      | 角色权限管理     | `IAM_ROLE_READ`        | L3-L5           |
| `/admin/permissions`| 权限点管理       | `IAM_PERMISSION_READ`  | L5              |
| `/admin/viewports`  | 视口配置         | `IAM_VIEWPORT_READ`    | L5              |
| `/admin/organization` | 组织管理       | `ORG_MANAGE`           | L3-L5           |
| `/admin/system`     | 学校设置         | `ADMIN_SYSTEM_MANAGE`  | L5              |
| `/admin/classes`    | 班级管理（全局） | `ADMIN_CLASS_READ`     | L3-L5           |
| `/admin/teachers`   | 教师管理         | `ADMIN_TEACHER_READ`   | L3-L5           |
| `/admin/students`   | 学生管理         | `ADMIN_STUDENT_READ`   | L3-L5           |
| `/admin/audit-logs` | 审计日志         | `ADMIN_AUDIT_READ`     | L5              |

> 权限点使用 `ADMIN_` / `IAM_` / `ORG_` 前缀（ai-allocation §5：admin 权限点 `ADMIN_` 前缀）。`ADMIN_*` 常量由 coord 维护于 `packages/contracts/src/permissions.ts`，前端不硬编码。

### 1.3 GraphQL schema

不适用。admin-portal **消费** teacher-bff GraphQL admin 命名空间，自身不提供 schema。

### 1.4 Kafka 事件发布

无。前端不发布 Kafka 事件。

### 1.5 错误码前缀

无（前端不定义错误码前缀，透传 BFF/网关错误码）。消费的错误码前缀见 §2.5。

### 1.6 微前端架构

| 角色 | 说明 |
| ---- | ---- |
| MF Remote | admin-portal 是微前端远程模块，挂载到 teacher-portal Shell |
| Remote 名称 | `admin_app`（Shell `remotes` 中引用为 `admin: 'admin_app@http://localhost:4003/_next/static/chunks/remoteEntry.js'`） |
| 暴露模块 | `./AdminApp`（管理后台应用入口，供 Shell 动态 import） |
| MF 配置位置 | `apps/admin-portal/next.config.js`（NextFederationPlugin，对齐 ARB-002 §2.2 配置风格） |
| shared（singleton） | react / react-dom / urql / graphql / @edu/ui-tokens / @edu/ui-components / @edu/hooks / @edu/contracts |
| 复用 Shell 暴露 | `GraphQLProvider` / `AppShell` / `useAuth` / `usePermission` / `useGraphQLClient` / `ErrorBoundary` / `RequirePermission` 等（ARB-002 §2.2） |

> admin-portal **不暴露共享组件给 Shell**（共享组件由 Shell 统一暴露，对齐 ARB-002）。admin 特有组件（UserManagementTable / RolePermissionMatrix / ViewportConfigEditor）仅 admin-portal 内部使用。

### 1.7 i18n 命名空间

| 命名空间 | 用途 |
| -------- | ---- |
| `admin.*` | 管理端 UI 文案（导航/按钮/表单标签等） |
| `iam.error.*` | iam 服务错误码 i18n（透传） |
| `bff.error.*` | teacher-bff 错误码 i18n（透传） |
| `gateway.error.*` | api-gateway 错误码 i18n（透传） |
| `network.error.*` | 前端网络层错误 i18n |

---

## §2 我消费什么（依赖上游）

### 2.1 gRPC 调用（同步）

无。前端不直接调 gRPC。

### 2.2 Kafka 事件订阅（异步）

无。前端不直接订阅 Kafka。审计日志经 teacher-bff 聚合后通过 GraphQL `auditLogs` Query 消费（链路：iam → Kafka `edu.iam.audit.created` → **teacher-bff 消费** → GraphQL → admin-portal）。

> 注：[matrix.md §4](../matrix.md) 将 admin-portal 列为 `edu.iam.audit.created` 消费方，表述不精确，已提请 coord 修正为 teacher-bff（见 [objections ISSUE-007](../objections/admin-portal_issue.md)）。

### 2.3 HTTP 调用

| 被调用方 | Method.Path | 用途 | mock 策略 |
| -------- | ----------- | ---- | --------- |
| api-gateway (ai01) | POST /api/admin/graphql | 管理 GraphQL 查询（经网关代理到 teacher-bff admin 命名空间） | api-gateway/teacher-bff 就绪前使用 MSW 拦截返回 mock GraphQL 响应 |
| push-gateway (ai02) | GET /ws | WebSocket 实时通知（审计告警/异常登录/系统异常） | push-gateway 就绪前使用 mock-socket 模拟 WS 推送（**ISSUE-006 待 coord 仲裁**） |

> **登录不自行实现**：admin-portal 复用 Shell 统一登录入口 `/login`（ARB-002 §2.3：登录页 P2 不走 MF，Shell 独占）。管理员登录后按 admin 角色重定向到 `/admin/dashboard`。开发期 mock 登录由 Shell 的 MSW handler 提供（admin 角色 JWT + permissions=["*"]）。

### 2.4 GraphQL 查询域（经 api-gateway 代理到 teacher-bff admin 命名空间）

> 依赖 teacher-bff admin 命名空间 schema（ARB-001，**ISSUE-005 待 ai03 补齐**）。以下为 admin-portal 消费的 Query/Mutation 清单，作为 ai03 补齐 schema 的输入。

| Query/Mutation | 类型 | 用途 | mock 策略 |
| -------------- | ---- | ---- | --------- |
| `currentUser` | Query | 当前管理员信息 | MSW 返回固定管理员（admin 角色） |
| `adminUsers` | Query | 用户列表（含筛选/分页） | MSW 返回固定 50 个用户 |
| `adminUser(id)` | Query | 用户详情 | MSW 返回对应用户 |
| `createUser` / `updateUser` / `deleteUser` / `toggleUserStatus` | Mutation | 用户 CRUD | MSW 返回 CRUD success |
| `adminRoles` | Query | 角色列表（含权限） | MSW 返回固定 5 个角色 + 权限矩阵 |
| `createRole` / `updateRolePermissions` | Mutation | 角色 CRUD + 权限矩阵 | MSW 返回 success |
| `adminPermissions` | Query | 全量权限点（按 resource 分组） | MSW 返回固定权限矩阵 |
| `adminViewports(scope)` | Query | 视口配置列表 | MSW 返回固定 7 个视口 |
| `updateViewport` | Mutation | 视口配置更新 | MSW 返回 success |
| `adminOrganization(parentId)` | Query | 组织树 | MSW 返回固定 school/grade/class 树 |
| `adminClasses` | Query | 班级管理（全局） | MSW 返回固定 20 个班级 |
| `adminTeachers` | Query | 教师管理 | MSW 返回固定 50 个教师 |
| `adminStudents` | Query | 学生管理 | MSW 返回固定 1200 个学生 |
| `auditLogs(filter)` | Query | 审计日志（聚合 iam AuditEvent） | MSW 返回固定 100 条审计日志 |
| `adminDashboard` | Query | 管理员仪表盘聚合 | MSW 返回固定仪表盘（total_teachers=50, total_students=1200, school_avg_score=80.0） |

### 2.5 消费的错误码前缀（前端 i18n 路由）

| 前缀 | 来源服务 | i18n key 模式 |
| ---- | -------- | ------------- |
| `IAM_` | iam | `iam.error.{{code}}` |
| `BFF_TEACHER_` | teacher-bff | `bff.error.{{code}}` |
| `GW_` | api-gateway | `gateway.error.{{code}}` |
| `NETWORK_` | 前端网络层 | `network.error.{{code}}` |

---

## §3 就绪信号

### 3.1 我依赖的上游就绪标志

- [ ] api-gateway HTTP :8080 启用（ai01）—— 前端请求入口 + admin 角色校验
- [ ] teacher-portal Shell MF exposes/shared 就绪（ai13，ARB-002）—— Remote 挂载前提
- [ ] teacher-bff GraphQL :3003 启用 + **admin 命名空间 schema 就绪**（ai03）—— **ISSUE-005 待 ai03 补齐**
- [ ] iam gRPC 50052 启用（ai06）—— 用户/角色/审计日志数据来源
- [ ] `edu.iam.audit.created` topic 有事件发布（ai06）—— 审计日志来源（经 teacher-bff 消费）
- [ ] push-gateway WebSocket :8081/ws 启用（ai02）—— 实时通知（**ISSUE-006 待 coord 仲裁**）
- [ ] `packages/contracts` admin 权限点 `ADMIN_*` 常量就绪（coord）

> 注：adminDashboard 的数据聚合由 teacher-bff 完成（teacher-bff 内部聚合 iam + core-edu + data-ana）。admin-portal **不直连 data-ana / core-edu gRPC**，统一经 teacher-bff GraphQL。原 contract 误列 data-ana gRPC 50055 为直接依赖，已修正。

### 3.2 我的就绪标志（供下游消费）

- [ ] admin-portal dev server :4003 启用
- [ ] MF Remote 可被 AppShell 加载（暴露 `./AdminApp` 模块）
- [ ] 独立壳渲染（首页 + 导航 + 路由守卫 + admin 角色校验）
- [ ] 登录流程可用（复用 Shell `/login`，admin 角色校验后重定向 `/admin/dashboard`）
- [ ] GraphQL 查询可执行（currentUser / adminDashboard / auditLogs 返回数据）
- [ ] 用户/角色 CRUD 可执行（createUser / updateRolePermissions）
- [ ] WebSocket 通知可接收

---

## §4 Mock 策略

### 4.1 我提供的 mock

admin-portal 是前端，无下游消费方。但对开发体验提供：

- **Storybook**：各组件独立 story（含权限矩阵编辑器、审计日志表格等复杂组件）
- **MSW handlers**：`apps/admin-portal/src/mocks/handlers.ts`，拦截所有 GraphQL/HTTP 请求

### 4.2 我消费的 mock

在真实上游就绪前，admin-portal 使用以下 mock：

- **HTTP/GraphQL mock**：使用 MSW 拦截所有请求
  - POST /api/admin/graphql → 按 operationName 返回对应 mock 响应（与 teacher-bff admin namespace mock 数据一致）
  - auditLogs mock 返回固定 100 条审计日志（含 action: create/update/delete/login/logout/permission_change）
  - adminDashboard mock 返回固定全校统计仪表盘
  - 所有 mock 响应定义在 `apps/admin-portal/src/mocks/fixtures/*.json`
- **WebSocket mock**：使用 mock-socket 库
  - 连接后每 30 秒推送 1 条 mock 系统通知（审计告警/异常登录）
- **JWT mock**：使用固定 mock JWT（admin 角色，permissions=["*"]），由 Shell MSW handler 写入 httpOnly cookie（复用 Shell 登录 mock）
- **权限矩阵 mock**：内置固定 5 个角色 + 完整权限矩阵（teacher/student/parent/admin/super_admin）
- **环境切换**：通过 `NEXT_PUBLIC_API_MOCKING=enabled` 环境变量控制，上游就绪后设为 `disabled`

---

## §5 跨模块契约确认清单（需对应 AI 确认）

| 契约 | 提供方 | 当前状态 |
| ---- | ------ | -------- |
| teacher-bff admin 命名空间 schema（§2.4 全部 Query/Mutation） | ai03 | ⚠️ 待补齐（ISSUE-005） |
| Shell 暴露 GraphQLProvider / useGraphQLClient / AppShell / useAuth / usePermission | ai13 | ⏳ P2 交付（ARB-002） |
| iam 用户/角色/权限/视口 CRUD gRPC + AuditEvent Kafka | ai06 | ⏳ P2.1 |
| api-gateway /api/admin/graphql 代理路由 + admin 角色校验 | ai01 | ⏳ |
| push-gateway GET /ws（admin-portal 实时通知） | ai02 | ⚠️ 待 coord 仲裁（ISSUE-006） |
| `packages/contracts` ADMIN_* 权限点常量 | coord | ⏳ |
