# 模块理解确认书 — admin-portal > AI:ai16(TS/React · 管理场景域前端 remote) > 阶段:阶段 1 交付物(仲裁后修订版 v2) > 日期:2026-07-10 > 关联: > > - [004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md) §1.1a/1.1b/§5.4 > - [AI 分配方案](../../../docs/architecture/ai-allocation.md) §5 ai16 > - [总统最终裁决](../../../docs/architecture/president-final-rulings.md) §5.1-5.5(ISSUE-044~048) > - [admin-portal 对接契约](../../architecture/issues/contracts/admin-portal_contract.md) > - [admin-portal 工作线](../../architecture/issues/worklines/admin-portal_workline.md) > - [known-issues §2.16](../../../docs/troubleshooting/known-issues.md) --- ## 0. 仲裁结果摘要(ARB-001~006) | 编号 | 议题 | 裁决 | | -------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------- | | ARB-001 | BFF 归属(ISSUE-044) | 复用 teacher-bff GraphQL endpoint,不新建 admin-bff;ai03 预留 `admin.*` 命名空间 | | ARB-002 | 错误码前缀(ISSUE-045) | `ADMIN_` 前缀(合并 `SCHOOL_`/`ORG_` 入 `ADMIN_`);i18n key `error.admin.*` | | ARB-003 | 权限点命名(ISSUE-046) | admin 自身资源用 `ADMIN_*`;跨服务资源保留原前缀(`IAM_USER_READ` 等) | | ARB-004 | GraphQL 客户端(ISSUE-047) | MF 模式复用 Shell 暴露的 GraphQL client 单例;standalone 模式自建 urql client | | ARB-005 | 审计日志归属(ISSUE-048) | 审计事件归 iam 发布到 `edu.iam.audit.created`;teacher-bff 消费聚合;admin-portal 经 GraphQL `auditLogs` Query 消费 | | ARB-006 | WebSocket 实时通知 | 接入 push-gateway `GET /ws`(审计告警/异常登录/系统异常),不再用轮询 | | 端口分配 | admin-portal dev server | **4003**(非旧文档的 3003) | --- ## 1. 我在架构中的位置 - **层级**:L2 微前端层(004 §3.1 六层架构中的前端层) - **MF 角色**:**Remote 子应用**,挂载到 teacher-portal Shell(主应用,端口 4000) - **上游(谁调用我)**:浏览器(系统管理员 / 校管理员) - **下游(同步)**:api-gateway(GraphQL `POST /api/admin/graphql` + REST `/api/auth/login`) - **下游(推送)**:push-gateway `GET /ws`(WebSocket 实时通知,ARB-006) - **BFF 对接**:复用 teacher-bff GraphQL admin 命名空间(ARB-001),不新建 admin-bff - **通信方式**:GraphQL(业务数据)+ HTTP(登录)+ WebSocket(实时通知) - **不直连**:前端不直连任何业务服务或 BFF 后端实例,全部经 api-gateway 代理 **说明**: - admin-portal 作为 Remote 子应用,挂载到 teacher-portal Shell - Shell 暴露 `GraphQLProvider` / `useGraphQLClient` / `AppShell` / `useAuth` / `usePermission` 等,admin-portal MF 模式复用,standalone 模式自建 - 路由前缀 `/admin/*` 由 teacher-portal Shell 动态加载 admin-portal Remote 模块(暴露 `./AdminApp`) - 场景域 BFF 复用策略(004 §5.4 + ARB-001):复用 teacher-bff GraphQL endpoint,admin 命名空间(`admin.*` Query/Mutation) ## 2. 我的限界上下文 ### 2.1 我负责的聚合 / 实体(前端视图模型) - 用户、角色、权限、视口(管理场景域前端视图,调 iam 经 GraphQL) - 组织(学校/年级/班级层级) - 学校设置(system 路由) - 班级/教师/学生全局管理(admin 视角,只读 + 状态管理) - 审计日志(只读,经 teacher-bff GraphQL `auditLogs` Query 消费) - 管理仪表盘(聚合统计 + 服务健康) - 实时通知(WebSocket 推送:审计告警/异常登录/系统异常) ### 2.2 业务领域 - **管理场景域**(前端场景域:管理场景域,对应后端 iam 限界上下文 + teacher-bff admin 命名空间) ### 2.3 不负责 - 教学业务编排(班级/考试/作业/成绩 CRUD,归 teacher-portal) - 学生作答界面(归 student-portal) - 家长多子女切换(归 parent-portal) - 教师仪表盘(归 teacher-portal,admin-portal 仅有管理仪表盘) - 审计事件发布(归 iam,admin-portal 仅消费) ### 2.4 数据范围 - DataScope L3-L5(校管理员 L3 学校 / 区教研员 L4 / 系统管理员 L5 全平台) - 不出现 L1(班级)/ L2(年级)级别的教师数据视角 ## 3. 我与外部的契约 ### 3.1 消费的后端 API(经 api-gateway 代理) | 路径 | Method | 下游 BFF/服务 | 用途 | | ------------------------- | ------ | ----------------------------- | ------------------------------------------------- | | `/api/admin/graphql` | POST | teacher-bff(admin 命名空间) | 全部业务 GraphQL 查询(ARB-001) | | `/api/auth/login` | POST | api-gateway → iam | 登录(standalone 模式用,MF 模式复用 Shell 登录) | | `GET /ws`(push-gateway) | WS | push-gateway | 实时通知(审计告警/异常登录/系统异常,ARB-006) | ### 3.2 GraphQL Operations(contract §2.4) admin-portal 消费 teacher-bff admin 命名空间的 18 个 GraphQL operations: | Operation | 类型 | 用途 | | ------------------------------------------------ | -------------- | ------------------------------- | | `currentUser` | Query | 当前管理员信息 | | `adminUsers` / `adminUser(id)` | Query | 用户列表 / 详情 | | `createUser` / `updateUser` / `toggleUserStatus` | Mutation | 用户 CRUD | | `adminRoles` | Query | 角色列表(含权限) | | `createRole` / `updateRolePermissions` | Mutation | 角色 CRUD + 权限矩阵 | | `adminPermissions` | Query | 全量权限点(按 resource) | | `adminViewports(scope)` | Query | 视口配置列表 | | `updateViewport` | Mutation | 视口配置更新 | | `adminOrganization(parentId)` | Query | 组织树 | | `adminClasses` | Query | 班级管理(全局) | | `adminTeachers` | Query | 教师管理 | | `adminStudents` | Query | 学生管理 | | `auditLogs(filter)` | Query | 审计日志(聚合 iam AuditEvent) | | `adminDashboard` | Query | 管理员仪表盘聚合 | | `systemSettings` / `updateSystemSettings` | Query/Mutation | 学校设置 | ### 3.3 推送契约(ARB-006) admin-portal 接入 push-gateway `GET /ws`,消费 3 类实时通知: | 通知类型 | severity | 用途 | | ---------------- | -------- | ------------------------- | | `audit_alert` | warning | 审计告警(敏感操作触发) | | `abnormal_login` | error | 异常登录(异地/异常时段) | | `system_error` | error | 系统异常(服务降级/宕机) | > **不再用轮询**:旧文档的"60s 轮询监控 + 5min 轮询统计"策略已被 ARB-006 替换为 WebSocket 实时推送。开发期用 mock-socket + 30s 定时器模拟推送。 ### 3.4 proto 不直接消费 前端不调用 gRPC,teacher-bff 把 gRPC 聚合为 GraphQL 暴露给前端。前端仅消费 `packages/contracts/src/permissions.ts` 中的权限点常量(TS 文件,非 proto 生成)。 ## 4. 我的技术栈 | 维度 | 选型 | 说明 | | ---------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | 框架 | Next.js 14+(App Router) | server components 默认,client components 按需 | | 语言 | TypeScript 5.5+(strict) | 沿用 tsconfig.base.json | | 微前端 | Module Federation 2.0(@module-federation/nextjs-mf) | admin-portal = **Remote**,端口 4003 | | 数据层 | urql GraphQL client | standalone 自建 / MF 复用 Shell 单例(ARB-004) | | 样式 | Tailwind CSS 3.4+ | 配合设计令牌三层(复用 Shell) | | UI 组件库 | shadcn/ui 风格(复用 Shell `@edu/ui-components`) | admin-portal 内部 `ui.tsx` 封装 PaperCard/Button/Table 等纸面组件 | | 表单 | react-hook-form + zod | 用户表单/学校设置 | | 拖拽排序 | @dnd-kit/sortable | 视口配置拖拽排序 | | 图表 | recharts | 仪表盘趋势表(实际以纯表格为主,纸面风格) | | i18n | 轻量 i18n(`messages` 字典 + `t()` 函数) | 命名空间 `admin.*`,不依赖 next-intl | | Mock | MSW(Mock Service Worker)+ mock-socket | 开发期拦截全部 GraphQL/HTTP + WebSocket mock | | Web Vitals | web-vitals v4 | LCP/CLS/FCP/INP/TTFB 采集,`navigator.sendBeacon` 上报 | | A11y | eslint-plugin-jsx-a11y(error 级) | WCAG 2.2 AA | | 字体 | Inter(sans)/ Fraunces(serif)/ JetBrains Mono(mono) | RootLayout 加载(standalone);MF 模式复用 Shell | | 测试 | Vitest + @testing-library/react | jsdom 环境,覆盖率 ≥ 80% | | ESLint | ESLint 9 flat config | jsx-a11y + tseslint + prettier(注:`@next/eslint-plugin-next` 14.x 与 ESLint 9 不兼容,已移除) | > **技术栈差异**(vs teacher-portal):MF 角色(Remote)、GraphQL 为主(非 REST)、消费 WebSocket 推送(非 SSE)、纸面 UI 风格、数据范围 L3-L5。 ## 5. 我的阶段归属 - **阶段**:P6(硬化阶段),批次 5 - **当前状态**:✅ **已实现**(P2-P6 全阶段交付完成,含 11 路由 + MSW mock + A11y + Web Vitals + Dockerfile) - **依赖上游阶段**:P6(依赖 teacher-portal Shell 已就绪 + teacher-bff GraphQL admin 命名空间 + iam 已实现用户/角色/权限/视口 CRUD + push-gateway WebSocket) ## 6. 路由表(11 路由,contract §1.2) | 路由 | 页面 | 权限点 | 数据范围 | | --------------------- | ---------------- | ---------------------- | -------- | | `/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` | 组织管理 | `ADMIN_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 | > 权限点前缀规则(ARB-003):admin 自身资源用 `ADMIN_*`;跨服务资源保留原前缀(如 `IAM_USER_READ`)。`ADMIN_*` 常量由 coord 维护于 `packages/contracts/src/permissions.ts`,admin-portal 引用 `src/lib/permissions.ts`,不硬编码。 ## 7. 黄金模板对齐清单(前端等价) | 对齐项 | 后端等价 | admin-portal 实现 | 状态 | | --------------------- | ------------------------------------- | ------------------------------------------------------------- | ---- | | 权限校验 | `@RequirePermission(Permissions.XXX)` | `usePermission().hasPermission("XXX")` Hook + `AuthGuard` | ✅ | | 错误码前缀 | `IAM_*` / `ADMIN_*` | `CONSUMED_ERROR_PREFIXES` 常量 + i18n 路由 | ✅ | | logger | pino | console + 结构化(生产 → 上报) | ✅ | | metrics | prom-client `/metrics` | Web Vitals v4 → `navigator.sendBeacon` 上报 | ✅ | | /health + /ready | `GET /healthz` `GET /readyz` | Next.js Route Handler `/api/health` + `/api/ready` | ✅ | | 优雅关闭 | SIGTERM handler | N/A(Next.js 无长连接) | ✅ | | 测试覆盖率 ≥ 80% | Vitest | Vitest + @testing-library/react | ⚠️ | | Dockerfile 多阶段构建 | builder + runtime | `node:22-alpine` 多阶段,standalone 输出,EXPOSE 4003 | ✅ | | Zod 输入验证 | class-validator + Zod schema | react-hook-form + zod | ✅ | | GlobalErrorFilter | NestJS 全局异常过滤器 | React ErrorBoundary + Toast 错误处理 | ✅ | | 设计令牌三层 | — | `globals.css`(primitive/semantic)+ tailwind-theme | ✅ | | A11y 工具集 | — | eslint-plugin-jsx-a11y(error 级)+ skip-link + focus-visible | ✅ | | Module Federation | — | `next.config.js` Remote 角色,`NEXT_PUBLIC_MF_ENABLED` 控制 | ✅ | | MSW mock | — | 18 GraphQL operation + `/api/auth/login` mock | ✅ | --- ## 附:admin-portal 实现清单(v2,仲裁后修订) ### 文件结构 ``` apps/admin-portal/ ├─ package.json # 依赖(urql/msw/@dnd-kit/web-vitals/eslint-plugin-jsx-a11y) ├─ next.config.js # MF Remote 配置 + rewrites(GraphQL 代理) ├─ tsconfig.json # extends tsconfig.base.json ├─ tailwind.config.js # paper/ink/accent/rule 色板 ├─ eslint.config.js # ESLint 9 flat config(jsx-a11y + tseslint + prettier) ├─ Dockerfile # 多阶段构建,node:22-alpine,standalone ├─ vitest.config.ts # jsdom + coverage ≥80% ├─ .env.example # 环境变量模板 ├─ public/mockServiceWorker.js # MSW worker └─ src/ ├─ app/ │ ├─ globals.css # 设计令牌 + A11y 样式 │ ├─ layout.tsx # RootLayout(字体加载) │ ├─ page.tsx # 根页面(redirect /admin/dashboard) │ ├─ admin-app.tsx # MF Remote 入口(exposes ./AdminApp) │ ├─ login/page.tsx # 登录页(standalone mock 登录) │ ├─ admin/ │ │ ├─ layout.tsx # admin 路由组 layout(Provider 链 + AuthGuard + AdminShell) │ │ ├─ dashboard/page.tsx # 管理仪表盘 │ │ ├─ users/page.tsx # 用户管理 │ │ ├─ roles/page.tsx # 角色权限 │ │ ├─ permissions/page.tsx # 权限点管理 │ │ ├─ viewports/page.tsx # 视口配置(@dnd-kit 拖拽) │ │ ├─ organization/page.tsx # 组织管理 │ │ ├─ system/page.tsx # 学校设置 │ │ ├─ classes/page.tsx # 班级管理 │ │ ├─ teachers/page.tsx # 教师管理 │ │ ├─ students/page.tsx # 学生管理 │ │ └─ audit-logs/page.tsx # 审计日志(CSV 导出) │ └─ api/ │ ├─ health/route.ts # Liveness 检查 │ └─ ready/route.ts # Readiness 检查 ├─ components/ │ ├─ ui.tsx # 通用 UI(PaperCard/Button/Input/Select/Badge/Table/Pagination) │ ├─ admin-shell.tsx # AdminShell(左栏 11 项导航 + 主内容 + 跳过链接) │ ├─ user-management-table.tsx # 用户管理表格 │ ├─ user-form-modal.tsx # 用户表单弹窗 │ ├─ role-permission-matrix.tsx# 角色权限矩阵 │ ├─ organization-tree.tsx # 组织树(A11y treeitem) │ ├─ notification-panel.tsx # 通知面板(WebSocket) │ ├─ msw-initializer.tsx # MSW 初始化 │ └─ web-vitals-initializer.tsx# Web Vitals 初始化 ├─ hooks/ │ ├─ use-graphql.ts # useGraphQuery + useGraphMutation │ ├─ use-users.ts # 用户 CRUD │ ├─ use-roles.ts # 角色 CRUD │ ├─ use-permissions.ts # 权限点查询 │ ├─ use-viewports.ts # 视口配置 │ ├─ use-organization.ts # 组织树 │ ├─ use-classes.ts # 班级 │ ├─ use-teachers.ts # 教师 │ ├─ use-students.ts # 学生 │ ├─ use-audit-logs.ts # 审计日志 + CSV 导出 │ ├─ use-dashboard.ts # 仪表盘 │ ├─ use-system-settings.ts # 学校设置 │ └─ use-websocket.ts # WebSocket 实时通知 ├─ providers/ │ ├─ graphql-provider.tsx # GraphQLProvider(standalone 自建 urql client) │ ├─ auth-provider.tsx # AuthProvider + useAuth + usePermission │ └─ toast-provider.tsx # ToastProvider + useToast ├─ lib/ │ ├─ graphql-client.ts # urql client + 18 个 GraphQL operations │ ├─ auth.ts # 认证工具 │ ├─ permissions.ts # 权限常量(ADMIN_ + IAM_) │ ├─ i18n.ts # 轻量 i18n │ └─ web-vitals.ts # Web Vitals 采集 ├─ types/ │ └─ view-models.ts # 视图模型类型定义 └─ mocks/ ├─ browser.ts # MSW Browser Worker ├─ server.ts # MSW Node Server(vitest) ├─ fixtures.ts # mock 数据库 └─ handlers.ts # MSW handlers(18 GraphQL + login) ``` ### 质量校验 - `pnpm run typecheck` → ✅ 通过(0 errors) - `pnpm run lint` → ✅ 通过(0 errors) --- **AI Agent**: ai16 (admin-portal remote) **Branch**: admin-portal-arbitration-done-TaFkyb **Coordinator**: coord-ai