docs: ai 协作文档体系重构与多 ai 仲裁结果落地

1.AI 协作文档体系重构(objections/worklines/contracts+matrix.md)

2.coord 仲裁文档(final-decisions/cross-review/final-rulings/orchestration)

3.各服务 01/02 文档补全

4.共享包初始化(shared-ts/shared-go/hooks/ui-components/ui-tokens)

5.Proto 契约补全

6.004 架构影响地图更新

7.端口分配表

8.设计规格文档
This commit is contained in:
SpecialX
2026-07-10 12:58:22 +08:00
parent 2a2a56f541
commit faaaf29f67
120 changed files with 23201 additions and 2 deletions

View File

@@ -0,0 +1,199 @@
# ai13 审核 report — teacher-portal 现有文档遗漏分析
> AIai13TS/React · teacher-portal 正式负责人,依据 ai-allocation.md §3.2
> 阶段:阶段 1/2 文档审核 + 长远架构补全
> 日期2026-07-09
> 审核对象:
>
> - [01-understanding.md](./01-understanding.md)ai07 于 2026-07-09 产出)
> - [02-architecture-design.md](./02-architecture-design.md)ai07 于 2026-07-09 产出)
>
> 关联裁决:[coord 交叉审查报告](../../../docs/architecture/coord-cross-review.md) §4.1/§5.2/§5.5/§6整改项 #2/#5/#17/#18、[004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md) §1.2/§4.2/§7.2/§11.4
---
## 1. 归属与版本背景
| 维度 | ai07 版本(被审核) | ai13 版本(本次修正) |
| -------- | --------------------------------------------- | ----------------------------------------- |
| AI 标识 | ai07 | ai13ai-allocation.md §3.2 正式负责人) |
| 文档日期 | 2026-07-09 | 2026-07-09同日审核+补全) |
| 文档分支 | docs/teacher-portal-stage1-stage2-design-ai07 | docs/teacher-portal-ai13-audit-supplement |
| 触发原因 | ai07 临时承接 4 端合并设计 | ai13 接管 teacher-portal 正式所有权 |
> **背景说明**ai-allocation.md §3.2 矩阵中 ai07 正式职责是 classes黄金模板ai13 才是 teacher-portal 的负责人。前期因并行加速ai07 临时承担 4 端合并设计并产出 `apps/teacher-portal/README.md`945 行)与 docs/01-02 两份文档。coord 已在 [coord-cross-review.md](../../../docs/architecture/coord-cross-review.md) §6 整改清单 #2/#17/#18 中列出多项需修正项,但整改责任原标注为 ai07。本次 ai13 正式接管并一次性完成审核 + 修正 + 长远架构补全。
---
## 2. 需修正项(来自 coord 裁决)
### 2.1 P0 端口冲突修正coord §4.1,整改 #2
| 项 | ai07 原文档 | 正确值 | 依据 |
| -------------- | -------------------------- | ------------------------------------------------------------- | ---------------------------------- |
| teacher-portal | 3000 | **4000** | 004 §1.2 + project_memory 端口约束 |
| student-portal | 3001 | **4001** | 004 §1.2 |
| parent-portal | 3002 | **4002** | 004 §1.2 |
| admin-portal | 3003 | **4003** | 004 §1.2 |
| MF remoteEntry | `localhost:3001/3002/3003` | `localhost:4001/4002/4003` | 与端口矩阵一致 |
| 后端冲突说明 | "3001-3003 已被避让" | 删除错误描述3001-3003 仍被 classes/iam/teacher-bff 占用) | coord §4.1 表 |
### 2.2 P1 端点契约对齐coord §6 整改 #174 处端点不一致)
| ai07 原文档 | 正确值(以后端服务为准) | 依据 |
| ------------------------------------------------------------ | ------------------------------------------------------------------------- | -------------- |
| `GET /api/v1/ai/generate-questions`SSE | `POST /api/v1/ai/chat/stream`SSE+ `POST /api/v1/ai/generate/question` | coord §3 表 #1 |
| `GET /iam/effective-permissions` | `GET /iam/permissions/effective` | coord §3 表 #2 |
| `GET /iam/rbac/...` | `GET /iam/viewports``GET /iam/roles``GET /iam/permissions/*` | coord §3 表 #3 |
| `POST /parent/switch-child`(家长端,不影响 teacher-portal | `POST /parent/children/:childId/select` | coord §3 表 #4 |
### 2.3 P1 错误码前缀期望修正coord §5.5,整改 #18
| ai07 原文档 | 正确值 | 依据 |
| ---------------------------------------------- | ---------------------------------------------------------------------- | ---------- |
| 期望 `EXAMS_` / `HOMEWORK_` / `GRADES_` 子前缀 | 删除,统一为 `CORE_EDU_*`classes 模块保留 `CLASSES_*` 黄金模板遗留) | coord §5.5 |
| BFF 错误码前缀 `BFF_TEACHER_` | 保留coord §5.2 已裁决 3 BFF 统一为 `BFF_XXX_` 风格) | coord §5.2 |
| 期望 `GW_` 前缀 | 保留coord §5.4 已裁决 api-gateway 加 `GW_` 前缀) | coord §5.4 |
---
## 3. 长远架构遗漏分析ai13 新增)
ai07 原文档已覆盖阶段 1/2 模板要求(位置/限界/契约/技术栈/分层图/领域模型/数据模型/API/事件/横切/交互/风险),但**未充分覆盖以下"长远、全面、面向未来"的架构维度**。这些维度是通用前端架构标准Next.js + Module Federation + 企业级 SaaS必备项本次审核需补全。
### 3.1 演进路线缺失
| 维度 | ai07 原文档 | 缺失内容 |
| ------------------- | --------------------- | ------------------------------------------------- |
| 当前 → MF 迁移路径 | 仅描述目标态 | 当前单端部署如何演进到 Shell+Remote 的分步路径 |
| 阶段间能力累积 | 仅 P2/P5/P6 三段 | P2/P3/P4/P5/P6 每阶段 teacher-portal 必备能力清单 |
| 后端契约演进对齐 | 无 | 后端 P3 启用 gRPC 后前端是否需要切换、何时切换 |
| REST → GraphQL 切换 | 提一句"待 coord 仲裁" | 切换的触发条件、迁移策略、灰度方案 |
### 3.2 安全架构缺失
| 维度 | ai07 原文档 | 缺失内容 |
| ------------------- | ----------------- | --------------------------------------------------------------- |
| Token 存储安全 | 仅说 localStorage | localStorage 的 XSS 风险 + httpOnly Cookie 替代方案 + CSRF 防护 |
| CSP内容安全策略 | 无 | Next.js `csp()` / `nonce()` 配置,限制 script/style/img 来源 |
| XSS 防护 | 仅提 DOMPurify | Tiptap 富文本的 XSS 防护、`dangerouslySetInnerHTML` 白名单 |
| CSRF 防护 | 无 | 若改用 Cookie 鉴权需 CSRF Token / SameSite |
| 点击劫持防护 | 无 | X-Frame-Options: DENY + CSP frame-ancestors |
| 子资源完整性 SRI | 无 | MF remoteEntry.js 的 SRI 校验、CDN 静态资源 integrity |
| 敏感数据脱敏 | 无 | 日志/错误上报中 token/PII 脱敏 |
### 3.3 性能架构缺失
| 维度 | ai07 原文档 | 缺失内容 |
| ------------------- | -------------- | ------------------------------------------------------------------ |
| 性能预算Budget | 无 | JS bundle ≤ 200KB / CSS ≤ 50KB / LCP ≤ 2.5s / CLS ≤ 0.1 等具体目标 |
| 路由级代码分割 | 无 | Next.js App Router 默认按路由分割 + 动态 import 重型组件 |
| SSR/CSR 决策矩阵 | 仅提"优先 CSR" | 每类页面dashboard/exam-taking/lesson-prep的 SSR/CSR 选择标准 |
| 字体加载策略 | 仅说 next/font | FOUT vs FOIT 选择、preload、font-display: swap |
| 图片优化 | 无 | next/image + AVIF/WebP + 响应式 sizes + lazy load |
| MF 共享依赖版本漂移 | 仅说 singleton | 共享依赖版本协商机制、peer deps 警告 |
| Prefetch / Preload | 无 | 路由 prefetch 策略、关键资源 preload |
| 长任务优化 | 无 | React 18 concurrent features、useTransition、useDeferredValue |
### 3.4 可观测性深度缺失
| 维度 | ai07 原文档 | 缺失内容 |
| -------------- | -------------------- | ---------------------------------------------------------------- |
| 错误上报 | 提"P6 接入 Sentry" | Sentry DSN 配置、release tracking、source maps、采样率、PII 过滤 |
| 性能监控 | 仅列 4 项 Web Vitals | RUMReal User Monitoring方案、长任务监听、卡顿追踪 |
| 用户行为分析 | 无 | 哪些事件需要埋点、埋点规范、隐私合规 |
| Session Replay | 无 | Sentry Replay / LogRocket 选型、隐私区域遮罩 |
| 前端日志聚合 | 仅 console | 浏览器日志如何汇聚到 Lokipino-browser → Gateway → Loki |
| trace_id 透传 | 仅提"从响应头提取" | 浏览器入口生成 trace_id、写入后续请求 header、跨 MF 透传 |
### 3.5 a11y 实施细节缺失
| 维度 | ai07 原文档 | 缺失内容 |
| ---------------- | --------------- | ------------------------------------------ |
| WCAG 2.2 AA 清单 | 仅列工具集 | 逐条对照 WCAG 2.2 AA 13 项准则 + 实施方式 |
| 键盘导航 | 仅提 focus-trap | 跳过链接、Tab 顺序、可见焦点、快捷键 |
| 屏幕阅读器测试 | 无 | NVDA/JAWS/VoiceOver 测试矩阵 + ARIA 模式 |
| 颜色对比度 | 无 | 自动化检测axe-core+ 设计令牌对比度审计 |
| 移动端 a11y | 无 | 触摸目标 ≥ 44px、缩放支持、手势替代 |
### 3.6 i18n 实施细节缺失
| 维度 | ai07 原文档 | 缺失内容 |
| -------------- | --------------- | ------------------------------------------------------ |
| next-intl 配置 | 仅提"next-intl" | locale 路由策略(前缀 vs 域名、SSR i18n、locale 检测 |
| fallback 策略 | 无 | 缺失翻译时回退到默认语言、缺失 key 在 dev 环境警告 |
| 复数/性别 | 无 | ICU MessageFormat 复数规则、性别变化 |
| 日期/数字格式 | 无 | Intl.DateTimeFormat / Intl.NumberFormat 按 locale |
| RTL 支持 | 无 | 是否需要支持阿拉伯语/希伯来语(影响布局) |
| 翻译协作流程 | 无 | 翻译文件位置、协作工具Crowdin/Transifex、CI 校验 |
### 3.7 部署拓扑缺失
| 维度 | ai07 原文档 | 缺失内容 |
| ------------- | ---------------- | ---------------------------------------------------- |
| 部署目标 | 仅说 Docker | Docker ComposeP1-P5→ K8sP6演进路径 |
| CDN/Edge 缓存 | 无 | Next.js 静态资源 CDN、edge middleware、ISR 缓存 |
| 多环境配置 | 无 | dev/staging/prod 环境变量管理、Next.js runtimeConfig |
| 健康检查端点 | 仅说 /api/health | /api/health vs /api/ready 区别、K8s probe 配置 |
| 灰度发布 | 无 | MF remoteEntry 版本协商、灰度发布策略 |
| 回滚策略 | 仅说 git revert | 前端回滚特殊性CDN 缓存失效、用户会话保持 |
### 3.8 测试策略细节缺失
| 维度 | ai07 原文档 | 缺失内容 |
| -------------- | --------------- | ------------------------------------------------------- |
| 单元测试 | 仅说 Vitest | 测试什么hooks/utils/pure components、mocking 策略 |
| 集成测试 | 无 | 组件树集成、API mockMSW、TanStack Query 集成测试 |
| E2E 测试 | 仅说 Playwright | 关键路径(登录/dashboard/班级 CRUD、CI 并发、稳定策略 |
| 视觉回归测试 | 无 | Storybook + Chromatic / Percy 选型 |
| 覆盖率目标分层 | 仅说 ≥80% | hooks ≥90% / utils ≥90% / components ≥80% / pages ≥60% |
| 性能测试 | 无 | Lighthouse CI / WebPageTest 集成 |
### 3.9 文件/目录结构目标态缺失
ai07 原文档 §6/§7 列出共享组件库/Hooks/令牌的"待建包",但**未给出 teacher-portal 自身的目录结构目标态**(当前 vs 目标对比)。
### 3.10 移动端策略缺失
ai07 原文档未提及:
- 是否需要响应式适配(教师在家备课用手机查看)?
- 是否需要 PWA / Service Worker离线批改作业
- 是否需要后续原生 AppReact Native / Electron 桌面端)?
- 移动端手势、触摸目标规范
### 3.11 MF 演进策略缺失
ai07 原文档描述了 MF 2.0 目标态,但**未回答关键演进问题**
- 当前 teacher-portal 是单端部署(无 MF何时引入 MF
- 引入 MF 后如何保持现有路由不被破坏?
- MF 失败时的降级方案remoteEntry.js 加载失败怎么办)?
- MF 版本协商Shell 与 Remote 的合约版本)?
### 3.12 数据流与乐观更新缺失
ai07 原文档 §4.2 提了 TanStack Query mutation但**未覆盖**
- 乐观更新策略(提交作业、批改成绩时如何乐观更新 UI
- 冲突解决(多教师同时批改同一作业)
- 失败回滚机制
- 缓存失效链路mutate A 后哪些 B/C/D 需要失效)
---
## 4. 本次审核产出
ai13 本次审核产出 **3 份文档**
1. **本报告**`apps/teacher-portal/docs/00-ai13-audit-report.md`(审核报告,永久保留作为审核凭证)
2. **更新** `apps/teacher-portal/docs/01-understanding.md`:修正归属/端口/端点/前缀,补充长远维度
3. **更新** `apps/teacher-portal/docs/02-architecture-design.md`:修正归属/端口/端点/前缀,补充横切与演进章节
4. **新建** `apps/teacher-portal/docs/03-long-term-architecture.md`:长远架构补全(演进/安全/性能/可观测/a11y/i18n/部署/测试/移动端/MF 演进)
---
**AI Agent**: ai13 (teacher-portal)
**Branch**: docs/teacher-portal-ai13-audit-supplement
**Coordinator**: coord-ai
**Audited**: ai07 stage1+2 outputs (2026-07-09)

View File

@@ -0,0 +1,770 @@
# teacher-portal 长远架构补全
> AIai074 端合并设计,承接 ai13 审核 §3 长远遗漏补全)
> 阶段:阶段 2 补充交付物(长远架构)
> 日期2026-07-09
> 关联:
>
> - [02-architecture-design.md](./02-architecture-design.md)ai13 修正版,阶段 2 主文档)
> - [00-ai13-audit-report.md](./00-ai13-audit-report.md) §3 长远遗漏分析
> - [004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md) §1.1a/§3.1/§5.4
> - [project_rules](../../../.trae/rules/project_rules.md) §3.10/§4
>
> 本文档承接 ai13 在 [00-ai13-audit-report.md](./00-ai13-audit-report.md) §3 列出的 12 项长远遗漏,逐一补全。原则:**架构不堵死未来演进路径**,当前不实现的也要预留接入点。
---
## 1. 演进与迁移策略(补 ai13 §3.1 / §3.11
### 1.1 阶段能力累积矩阵
| 阶段 | teacher-portal 必备能力 | 依赖上游 | 退出标准 |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------- | ------------------------------------------------------------- |
| P2 | 登录 + AppShell + 视口导航 + classes CRUD + GraphQL clienturqlF9 裁决 P2 起) + 设计令牌三层 + ErrorBoundary + /api/health&ready | iam、teacher-bff、classes | 教师登录看到按角色的侧边栏 + 班级 CRUD |
| P3 | 考试/作业/成绩页面 + MF Shell 配置就绪(暂无 Remote+ 乐观更新 + 多 Tab 会话同步 | core-eduexams/homework/grades | 教师创建考试→学生student-portal Remote作答→教师批改全链路 |
| P4 | 知识图谱页 + 学情分析页 + parent-portal Remote 接入 | content、data-ana、parent-bff | 教师查看知识图谱 + 学情宽表 5s 返回 |
| P5 | WebSocket 推送 + AI 辅助出题 SSE + 通知中心 | msg、push-gateway、ai | 全校广播实时到达 + AI 流式出题 |
| P6 | Sentry + RUM + OTel browser + A11y 审计 + 性能优化 + localStorage→httpOnly Cookie 迁移 | 全链路可观测 | 99.9% 可用 + WCAG 2.2 AA |
### 1.2 后端 gRPC 启用对前端的影响(补 ai13 §3.1
004 §4.2 裁决各服务分阶段启用 gRPCP3 iam+core-edu、P4 content+data-ana、P5 ai。**前端不直接消费 gRPC**,影响只在 BFF 层。F9 裁决 BFF P2 起即用 GraphQL非"P4 起 REST→GraphQL 迁移"),前端 P2 起即引入 urql client。
| 后端切换 | BFF 动作 | 前端动作 |
| ---------------------------- | --------------------------------------------- | -------------------------------------------- |
| 服务 HTTP→gRPC | BFF 改为 gRPC client 调用服务 | 无感BFF 仍对前端暴露 GraphQL |
| BFF GraphQLP2 起,已裁决) | teacher-bff P2 起即 GraphQL Yoga + DataLoader | 前端 P2 起即引入 urql client统一走 GraphQL |
**前端契约**:始终消费 `ActionState` 信封004 §11.5与传输协议无关。BFF GraphQL schema 由 ai03 维护coord 在批次 1 启动前仲裁第一版ISSUE-037
### 1.3 单体 → MF 迁移路径(绞杀者模式,补 ai13 §3.11
```
当前态 过渡态 目标态
┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ teacher-portal │ │ teacher-portal │ │ teacher-portal │
│ 单体 Next.js │ ──> │ Shell + MF 配置 │ ──> │ Shell │
│ 5 个页面 │ │ (暂无 Remote │ │ + student Remote │
│ │ │ + student-portal │ │ + parent Remote │
│ │ │ 作为首个 Remote │ │ + admin Remote │
└─────────────┘ └──────────────────┘ └──────────────────┘
```
| 步骤 | 动作 | 回滚 | 验证 |
| ---- | ------------------------------------------------------------------------------ | -------------------------------------------------------- | ------------------------- |
| 1 | 升级 Next.js 14→15 + react 18→19 | git revert | classes CRUD 端到端 |
| 2 | 抽取 ui-tokens/ui-components/hooks 到 packages | 引用回退到本地 | 视觉无变化 |
| 3 | teacher-portal 加 NextFederationPluginShellexposes AppShell暂无 Remote | 删除 plugin 配置 | 现有页面不受影响 |
| 4 | student-portalP3接入为首个 Remote | feature flag `NEXT_PUBLIC_MF_ENABLED=false` 回退单体路由 | 学生作答页在 Shell 内渲染 |
| 5 | parent/admin 逐步接入 | 同上 | 各端独立可访问 |
**关键**:每步用 feature flag 控制,失败可一键回退到单体路由;保留旧路由文件直到 MF 稳定2 个版本周期)。
### 1.4 GraphQL 客户端架构P2 起最终方案,补 ai13 §3.1
> 本节原为"REST → GraphQL 切换策略"(过渡方案描述)。按总裁裁决 F9P2 起 BFF 用 GraphQL+ §4.4(删除过渡方案本身,保留设计决策记录)+ ISSUE-036-ai13 回写义务,已替换为最终方案描述。
**裁决依据**coord-final-decisions F9 + 总裁裁决 §2.17MF AppShell GraphQL client 单例,采纳方案 AShell 暴露 GraphQLProvider
#### 1.4.1 client 选型与单例
| 维度 | 方案 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| client 库 | urql轻量、可插拔 exchange、TS 友好;备选 apollo但 urql bundle 更小,符合 §3.1 性能预算) |
| 单例归属 | teacher-portal Shell 暴露 `GraphQLProvider`@edu/mf-contracts 登记student/parent/admin Remote 复用 Shell 单例(总裁 §2.17 方案 A |
| MF shared 配置 | `react` / `urql` / `graphql` 设为 MF singleton避免 Remote 各自初始化导致多实例 + 缓存不一致 |
| 缓存交换 | urql `cacheExchange`(文档缓存 +normalized 可选MF 下 Shell 单例 cache 跨 Remote 共享 |
#### 1.4.2 client 初始化P2 起)
```typescript
// teacher-portal Shellpackages/hooks 提供 useGraphQLClientShell 注入 GraphQLProvider
import { createClient, Client } from "urql";
import { cacheExchange, fetchExchange } from "urql";
const client: Client = createClient({
url: process.env.NEXT_PUBLIC_TEACHER_BFF_GRAPHQL_URL!, // teacher-bff /graphql endpoint
fetchOptions: () => {
const token = readAuthToken(); // useAuth 注入
const traceId = readTraceId();
return {
headers: {
...(token ? { Authorization: `Bearer ${token}` } : {}),
"X-Trace-Id": traceId,
},
};
},
exchanges: [cacheExchange, fetchExchange],
});
```
- Shell 在 `app/providers.tsx` 包裹 `<Provider value={client}>`,所有 Remote 通过 Shell 暴露的 `useGraphQLClient()` 取同一 client
- token 刷新/401 处理由 useAuth 统一拦截(不污染 urql exchange 链),见 §2.7
#### 1.4.3 错误处理与 ActionState 契约
| 场景 | 处理 |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| GraphQL 错误errors[] | 解析 `extensions.code`BFF_TEACHER_* 前缀G14 裁决),映射到 i18n key `error.teacher_bff.<code_snake>`F11 |
| 网络错误 | 重试 1 次 → 提示网络异常 → 记录 trace_id |
| 401 | useAuth 静默 refresh 一次 → 重试;失败跳登录(见 §2.7 |
| 部分失败data + errors 共存) | 渲染 data 可用部分 + errors 区域提示,不整体失败 |
- BFF 始终返回 `ActionState` 信封004 §11.5),前端统一走 `normalizeActionState()` 工具packages/hooks
#### 1.4.4 缓存策略
| 数据类型 | staleTime | 失效策略 |
| -------------- | ----------------- | --------------------------------------------- |
| 班级列表/详情 | 30s | mutation 后 invalidate见 §10.2 |
| 考试/作业/成绩 | 10s | mutation 后 invalidate + 乐观更新(见 §10.1 |
| 用户权限/视口 | 0always fetch | 角色变更推送后强制 invalidate |
| Dashboard 概览 | 60s | ISR + 客户端 stale-while-revalidate |
- urql 默认 document cache 适合简单场景复杂关联如班级↔考试↔成绩P3 起评估切 `@urql/exchange-graphcache` normalized cache
- 不引入 TanStack Query 做 GraphQL 缓存urql 自带缓存足够避免双缓存层TanStack Query 仅用于非 GraphQL 场景(文件上传/SSE见 useApi
#### 1.4.5 与 ISSUE-037/038/039 的关联
| ISSUE | 关联点 | 状态 |
| -------------- | ----------------------------------------------------------------------- | ------------- |
| ISSUE-037-ai13 | teacher-bff GraphQL schema 第一版由 ai03 维护coord 批次 1 启动前仲裁 | 待 coord 仲裁 |
| ISSUE-038-ai13 | MF AppShell 暴露 GraphQL client 单例(总裁 §2.17 方案 A 已采纳) | 已裁决 |
| ISSUE-039-ai13 | packages含 hooks 中的 GraphQL client 工具)建立时机:批次 0.15 已建立 | 已执行 |
#### 1.4.6 设计决策记录(原过渡方案追溯)
> 本节为决策追溯,非过渡方案描述。
- **决策**P2 起 BFF 即 GraphQL前端即 urql不经过 REST 中间态
- **裁决来源**coord F9 + 总裁 §0.3"不分阶段"原则 + §2.17 MF GraphQL client 单例
- **原过渡方案**(已废弃):曾考虑 P2-P4 REST + P5+ 评估迁移 GraphQL + feature flag 灰度。该方案违反"不分阶段"原则,已删除
- **影响范围**02-architecture-design.md已回写+ 03-long-term-architecture.md §1.4(本次回写)
---
## 2. 安全架构(补 ai13 §3.2
### 2.1 Token 存储演进
| 阶段 | access token | refresh token | 风险等级 |
| ------- | -------------------------------------- | ------------------------------------------ | -------------- |
| P2 过渡 | localStorageZustand 持久化) | localStorage | 高XSS 可读) |
| P6 终态 | 内存Zustand sessionSlice刷新丢失 | httpOnly + Secure + SameSite=Strict cookie | 极低 |
**切换条件**iam 支持 refresh cookie 端点 + CSRF 防护就绪后迁移。
### 2.2 CSP内容安全策略
MF 下 CSP 必须显式允许 remote origin。Next.js 通过 `next.config.js` headers 或 middleware.ts 注入:
```
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' http://localhost:4001 http://localhost:4002 http://localhost:4003;
connect-src 'self' http://localhost:8080 ws://localhost:8081;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
font-src 'self' data:;
frame-ancestors 'none';
base-uri 'self';
object-src 'none';
```
- 生产环境 remote origin 改对应域名,逐步移除 `'unsafe-inline'`(用 nonce/hash 替代)
- 配套头:`X-Content-Type-Options: nosniff``X-Frame-Options: DENY`(防点击劫持)、`Referrer-Policy: strict-origin-when-cross-origin``Permissions-Policy: geolocation=(), microphone=(), camera=()`
### 2.3 MF Remote 完整性校验
Remote `remoteEntry.js` 由 Shell 动态加载,存在"被篡改 remote 注入恶意代码到 Shell 上下文"风险MF 共享 singleton 使 remote 可访问 Shell 全局状态)。
| 缓解措施 | 说明 |
| ---------------------------- | ----------------------------------------------------------------------------------------------- |
| Subresource IntegritySRI | remoteEntry.js 计算 SHA-384Shell 加载时校验MF 2.0 支持 `integrity` 字段) |
| Remote origin 白名单 | CSP `script-src` 限定已知 remote 域名 |
| 最小暴露面 | Shell `exposes` 仅暴露必要组件,不暴露整个 store/ApiClient 内部 |
| Remote 隔离 | Remote 不直接持有 Shell 的 token统一通过 `useApi()` Hook 取 ApiClienttoken 注入在 Hook 层) |
| CDN 静态资源 SRI | next/image、next/font 产物带 integrity 属性 |
### 2.4 CSRF 防护(切换 cookie 后)
- SameSite=Strict cookie 阻断跨站携带
- 对 state-changing mutationPOST/PUT/DELETE额外校验 `X-CSRF-Token` header双提交 cookie 模式)
- Next.js middleware.ts 校验 origin header 与允许域名匹配
### 2.5 多 Tab 会话同步(关键遗漏补齐)
教师多 Tab 操作时,登出/角色变更须跨 Tab 同步,否则出现"Tab A 登出但 Tab B 仍可用"的越权窗口。
| 事件 | 同步机制 | 动作 |
| ---------- | ------------------------------------------------------- | ----------------------------------------- |
| 登出 | `BroadcastChannel('edu-session')` postMessage('logout') | 所有 Tab 清 session + 跳登录 |
| Token 刷新 | BroadcastChannel('token-refreshed') | 其他 Tab 更新内存 token |
| 角色变更 | WebSocket 推送P5+ BroadcastChannel | 所有 Tab invalidate 权限缓存 + 重渲染视口 |
| 会话过期 | BroadcastChannel('session-expired') | 跳登录 + 提示 |
降级:不支持 BroadcastChannel 的浏览器走 `localStorage` `storage` 事件。
### 2.6 XSS / 注入防护
- `dangerouslySetInnerHTML` 全局禁用project_rules §4AI 流式内容先用 DOMPurify 清洗再渲染
- Tiptap 内容序列化为 JSON 存储,渲染走 ProseMirror schema白名单节点禁止 raw HTML
- 用户输入展示统一走 React 转义
### 2.7 鉴权边界与权限失效
| 场景 | 前端动作 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| access token 过期 | 401 → 静默 refresh 一次 → 重试;失败跳登录 |
| refresh token 过期 | 跳登录 + 提示"会话已过期" |
| 角色被撤销 | WebSocket 推送 `RoleRevoked` → invalidate 权限 → 重新拉取 → 视口收敛;权限不足以访问当前路由 → 重定向到 dashboard |
| 强制下线 | iam 调 push-gateway 推 `ForceLogout` → 前端清 session |
### 2.8 敏感数据脱敏(补 ai13 §3.2
- 日志/错误上报前过滤token、password、refreshToken、PII身份证/手机号)字段置 `[REDACTED]`
- Sentry 配置 `beforeSend` 钩子剥离 PIISession Replay 对敏感输入框 `mask` 所有文本
- 前端不持久化密码原文,登录后立即从内存清除
---
## 3. 性能架构(补 ai13 §3.3
### 3.1 性能预算
| 指标 | 目标 | 度量 | CI 门禁 |
| ----------------------- | ----------------- | ----------------------------- | --------------- |
| LCP | < 2.5sP75 | Web Vitals RUM | lighthouse CI |
| INP | < 200msP75 | Web Vitals RUMCWV 2024 | lighthouse CI |
| CLS | < 0.1 | Web Vitals RUM | lighthouse CI |
| TTFB | < 800ms | Web Vitals RUM | — |
| 长任务(>50ms | < 5 次/页 | PerformanceObserver Long Task | — |
| Shell JS bundlegzip | < 150KB | @next/bundle-analyzer | size-limit fail |
| 每个 Remote JSgzip | < 80KB | bundle analyzer | size-limit fail |
| CSSgzip | < 50KB | bundle analyzer | — |
| shared deps 重复 | 0MF singleton | MF manifest 校验 | CI 检查 |
| 首屏请求数 | < 20 | lighthouse CI | — |
- CI 集成 `@next/bundle-analyzer` + size-limit超预算 fail build
- 每版本 lighthouse CI 报告归档,回归监控
### 3.2 渲染模式决策矩阵
| 页面类型 | 模式 | 理由 |
| -------------------------- | ------------------------------- | ---------------------------- |
| 登录页 | SSR | SEO + 首屏快 |
| Dashboard 概览 | SSR + Streaming | 首屏骨架 + Suspense 流式填充 |
| 列表/表格classes/exams | CSR + TanStack Query | 强交互、筛选、分页 |
| 详情页(成绩/学情) | ISRrevalidate 60s+ CSR 交互 | 数据准实时CDN 缓存降负载 |
| AI 出题SSE | CSR | 长连接流式 |
| 知识图谱可视化 | CSR | 交互重 |
| 管理端配置 | CSR | 纯交互 |
| 考试作答student | CSR | 防刷新丢答 + 离线缓存 |
### 3.3 Next.js 缓存层级App Router
| 缓存层 | 用途 | 失效策略 |
| ------------------------- | -------------------- | --------------------- |
| Request Memoization | 单次请求内去重 fetch | 请求结束即失效 |
| Data Cachefetch cache | 跨请求 fetch 结果 | revalidate / tag 失效 |
| Full Route Cache | 静态路由产物 | 按路由 revalidate |
| Router Cache客户端 | RSC payload + 预取 | staleTime 30s |
**约定**BFF 数据 `fetch(..., { next: { revalidate: 30, tags: ['classes'] } })`mutation 后 `revalidateTag('classes')`
### 3.4 Server / Client Component 边界
- **Server Component**数据获取、静态展示、设计令牌注入、i18n Provider
- **Client Component**交互onClick/state、TanStack Query、Zustand、Tiptap、图表
- 边界标记:`'use client'` 仅在交互根节点声明,子树尽量保持 Server Component
### 3.5 字体加载策略(补 ai13 §3.3
| 策略 | 实现 |
| -------- | ------------------------------------------------------------------------ |
| 加载方式 | `next/font/google`self-host无第三方请求 |
| 显示策略 | `font-display: swap`FOUT优先文本可见性避免 FOIT 白屏) |
| preload | 首屏字体 `preload`,次屏字体 lazy |
| 子集化 | Inter/Fraunces 按中文字符子集next/font 自动 latin中文需手动 subset |
| 变量暴露 | `var(--font-family-sans/serif/mono)`禁止字面量project_rules §3.10 |
### 3.6 图片优化(补 ai13 §3.3
- 统一 `next/image`:自动 AVIF/WebP 转换 + 响应式 `sizes` + lazy load + blur placeholder
- 知识图谱节点图、用户头像走 `next/image`;远程图配 `remotePatterns` 白名单
- 图表截图导出走 `canvas.toBlob` + CDN不走 base64 内联
### 3.7 路由级代码分割与长任务优化(补 ai13 §3.3
- App Router 默认按路由分割重型组件Tiptap/recharts/知识图谱)用 `@next/dynamic` 懒加载
- React 18 concurrent长列表用 `useDeferredValue`;路由切换用 `useTransition` 标记非紧急更新
- MF Remote 按 scope 动态加载,非当前 scope 不下载 remoteEntry.js
### 3.8 Prefetch / Preload 策略
| 资源 | 策略 |
| ------------------ | ------------------------------------------- |
| 当前路由数据 | TanStack Query 预取(`prefetchQuery` |
| 可能访问的 Remote | `requestIdleCallback` 预拉取 remoteEntry.js |
| 关键字体/Logo | `<link rel="preload">` |
| 下一页 RSC payload | Next.js Link `prefetch={true}`(默认) |
---
## 4. 可观测性深度(补 ai13 §3.4
### 4.1 三支柱前端落地
| 支柱 | 工具 | 实现 | 阶段 |
| ---- | ------------------------------------------ | ------------------------------------------- | ---- |
| 日志 | pino-browser → Gateway → Loki | 结构化 JSON含 trace_id/user_id/scope/path | P6 |
| 指标 | Web Vitals → `/api/v1/admin/web-vitals` | LCP/INP/CLS/TTFB + Long Task | P6 |
| 链路 | OTel browser SDK → OTLP collector → Jaeger | 自动埋点 fetch/XHR/document load | P6 |
### 4.2 trace_id 全链路透传(补 ai13 §3.4
```
浏览器入口生成 trace_idcrypto.randomUUID
→ 写入 fetch header 'X-Request-Id'
→ Gateway 透传
→ 服务间 gRPC metadata
→ 日志/metrics/trace 统一 trace_id
→ 响应头返回 'X-Request-Id'
→ 前端日志记录(便于用户报障定位)
```
- MF 透传Shell 生成 trace_id 后通过共享 `useApi()` 注入所有 Remote 的请求
- Session 模型补 `traceId` 字段ai13 已在 02 补全)
### 4.3 错误追踪与 Session Replay补 ai13 §3.4
| 维度 | 方案 |
| ---------------- | ------------------------------------------------------------------------- |
| 平台 | SentryP6 |
| DSN | 环境变量 `NEXT_PUBLIC_SENTRY_DSN` |
| release tracking | 每次构建注入 commit SHA |
| source maps | CI 上传到 Sentry生产不暴露 |
| 采样率 | errors 100%traces 10%replay 1% |
| PII 过滤 | `beforeSend` 剥离 token/password/身份证Session Replay 对敏感输入 `mask` |
| Session Replay | Sentry Replay敏感区域 `data-sentry-mask` |
### 4.4 用户行为埋点规范(补 ai13 §3.4
| 事件类别 | 示例 | 上报 |
| -------- | -------------------------------------------- | -------------------------- |
| 页面访问 | `page_view` {path, scope, role} | Gateway 聚合 |
| 关键操作 | `exam_create`/`homework_grade`/`ai_generate` | Gateway 聚合 |
| 错误 | `action_failed` {code, path} | Sentry + Gateway |
| 性能 | `web_vitals` {lcp, inp, cls} | `/api/v1/admin/web-vitals` |
- 埋点统一走 `packages/hooks/src/useAnalytics()`,禁止散落 `gtag`/`mixpanel` 直调
- 隐私合规:不采集身份证/成绩具体值,仅采集聚合指标
### 4.5 前端日志聚合(补 ai13 §3.4
```
pino-browser (batch 10 条/5s flush)
→ POST /api/v1/admin/logs
→ Gateway → Loki
→ Grafana 查询(按 trace_id/user_id/path 过滤)
```
- 级别debugdev only/info/warn/error
- 采样info 10%warn 100%error 100%
---
## 5. a11y 实施细节(补 ai13 §3.5
### 5.1 WCAG 2.2 AA 对照清单
| 准则 | 实施方式 |
| -------------- | ----------------------------------------------------- |
| 1.1 非文本内容 | 所有 `<img>``alt`;装饰图 `alt=""` |
| 1.3 适应性 | 语义化 HTML`<nav>/<main>/<aside>`ARIA role 补充 |
| 1.4 可辨别性 | 对比度 ≥ 4.5:1设计令牌审计禁用纯色信息编码 |
| 2.1 键盘可达 | 全键盘导航;可见焦点(`focus-visible`);跳过链接 |
| 2.4 导航 | 标题层级正确;`<title>` 动态;面包屑 |
| 2.5 输入模态 | 触摸目标 ≥ 44px取消指针防误触 |
| 3.1 可读 | `lang` 属性;缩写标注 |
| 3.2 可预测 | 一致导航;无意外跳转 |
| 3.3 输入辅助 | 错误标识 + `aria-describedby` + 修正建议 |
| 4.1 兼容 | ARIA 正确使用;不依赖仅颜色 |
### 5.2 键盘导航
- 跳过链接skip to main content首位
- Tab 顺序与视觉顺序一致DOM 顺序即 tab 顺序)
- Modal 打开时焦点陷阱(`focus-trap`),关闭后焦点返回触发元素
- 快捷键:`/` 聚焦搜索、`g` 后接字母跳转teacher/student/parent/admin
### 5.3 屏幕阅读器测试矩阵
| 读屏器 | 平台 | 阶段 |
| --------- | --------- | ------- |
| NVDA | Windows | P6 审计 |
| JAWS | Windows | P6 抽测 |
| VoiceOver | macOS/iOS | P6 审计 |
| TalkBack | Android | P6 抽测 |
### 5.4 自动化检测
- `eslint-plugin-jsx-a11y`error 级CI 强制)
- `@axe-core/playwright` E2E 集成,每版本扫描关键页面
- 设计令牌对比度审计脚本primitive 色板两两组合计算对比度)
---
## 6. i18n 实施细节(补 ai13 §3.6
| 维度 | 方案 |
| ----------- | --------------------------------------------------------------------------------------------- |
| 库 | next-intlApp Router 原生支持) |
| 路由 | `/[locale]/teacher/...`(前缀式,默认 `zh` |
| 检测 | middleware.tscookie `NEXT_LOCALE``Accept-Language` → 默认 `zh` |
| 加载 | 按路由分片动态加载 message`getMessages` + `useTranslations` |
| fallback | `zh` 为 fallback locale缺 key 回退dev 环境缺 key console.warn |
| 复数/性别 | ICU MessageFormatnext-intl 内置):`{count, plural, =0 {无} one {# 项} other {# 项}}` |
| 数字/日期 | `Intl.NumberFormat` / `Intl.DateTimeFormat`(按 locale不用 dayjs 本地化) |
| 错误码 i18n | `error.{{service}}.{{code_snake_case}}`(见 02 §6.2 |
| RTL 预留 | 布局用 logical properties`margin-inline`/`padding-inline`),未来阿拉伯语只需切换 direction |
| 文档术语 | 业务术语统一 glossary`packages/contracts/src/i18n-glossary.ts`)防翻译漂移 |
| 翻译协作 | `i18n/locales/*.json` 纳入 gitCI 校验 key 完整性zh/en key 集合一致);后续可接 Crowdin |
> BFF/服务返回 i18n key + 参数(不返回已翻译文案),前端统一翻译,避免多端文案不一致。
---
## 7. 部署拓扑(补 ai13 §3.7
### 7.1 部署演进
| 阶段 | 目标 | 方式 |
| ----- | ------------------- | ---------------------------------------------------------- |
| P1-P5 | 单机 Docker Compose | `docker compose up -d --build`(每服务 Dockerfile 多阶段) |
| P6+ | K8s | 每服务 Deployment + HPA + Service + Ingress |
### 7.2 MF 部署与 CDN
| 端 | 部署单元 | 策略 | 备注 |
| ------------------------------ | -------- | ----------- | ---------------------------------------- |
| teacher-portalShell | 独立 | 滚动 | Shell 变更频率最低 |
| student/parent/adminRemote | 各自独立 | 蓝绿/金丝雀 | remoteEntry.js 不可变文件名 + CDN 长缓存 |
- **部署顺序约束**:破坏性 shared dep 升级时Remote 先升、Shell 后升Shell singleton 向下兼容旧 Remote
- **CDN**remoteEntry.js 设 immutable 长缓存,版本化文件名 `remoteEntry.{version}.js` 解决缓存失效
- **预拉取**Shell 在 idle 时预拉取用户角色对应 Remote 的 remoteEntry.js`requestIdleCallback`
- **Edge middleware**locale 检测、A/B 测试、灰度路由放 edgeVercel/Cloudflare Workers
### 7.3 多环境配置
| 环境 | 配置来源 | 说明 |
| ------- | ---------------------- | -------- |
| dev | `.env.local` | 本地开发 |
| staging | K8s ConfigMap + Secret | 预发布 |
| prod | K8s Secret | 生产 |
- 前端变量必须 `NEXT_PUBLIC_` 前缀project_rules §4服务端变量不加
- 环境校验用 `@t3-oss/env-nextjs` + Zod启动时 fail-fast
### 7.4 健康检查端点
| 端点 | 用途 | 实现 |
| ----------------- | --------------------------------- | ----------------------------------------------------------------------- |
| `GET /api/health` | Dockerfile HEALTHCHECK / liveness | 返回 `{ status: 'ok', ts }` |
| `GET /api/ready` | K8s readinessProbe | 检查 `API_GATEWAY_URL` 可达 + MF remoteEntry 可达Shell+ 内存 < 阈值 |
### 7.5 灰度发布与回滚
| 场景 | 策略 |
| ----------- | ---------------------------------------------------------------------- |
| Remote 灰度 | remoteEntry.js 多版本并存manifest 按用户分桶切换 |
| 回滚 | remoteEntry.js 保留 N 版CDN 切上一版本;数据库无变更,无数据回滚风险 |
| 会话保持 | 回滚不强制登出,旧版本 Session 兼容(向前兼容设计) |
---
## 8. 测试策略细节(补 ai13 §3.8
### 8.1 测试金字塔
| 层级 | 工具 | 覆盖目标 | 范围 |
| -------- | -------------------------------- | -------------------------------------------------- | ----------------------------------- |
| 单元 | Vitest + @testing-library/react | hooks ≥90% / utils ≥90% / 纯组件 ≥80% / pages ≥60% | Hook、工具函数、纯组件 |
| 集成 | Vitest + MSW | 关键链路 | ApiClient + TanStack Query + 权限流 |
| 契约 | Vitest + `@edu/mf-contracts` zod | 100% Shell 暴露 | Shell↔Remote 契约 |
| 视觉回归 | Playwright + Percy/Chromatic | 关键页面 | 设计令牌、布局 |
| E2E | Playwright | 4 端关键流 | 登录→操作→结果 |
| 性能 | Lighthouse CI | 每版本 | LCP/INP/CLS 回归 |
### 8.2 MF 集成测试
- Shell 单独测试mock Remote 为 stub 组件
- Remote 单独测试mock Shell 暴露的 Hook
- 契约测试:`@edu/mf-contracts` 携带 zod schemaCI 校验 Shell 暴露与 Remote 消费一致
- 跨端 E2EPlaywright 启动 Shell + 真实 Remote覆盖"教师发作业→学生提交→家长查看"全链路
### 8.3 E2E 关键路径
| 路径 | 步骤 |
| ---------- | ------------------------------------------------- |
| 登录流 | 登录→侧边栏按角色渲染→dashboard |
| 班级 CRUD | 新建→列表→详情→编辑→删除 |
| 考试全链路 | 教师建考→发布→学生作答→教师批改→成绩发布→家长查看 |
| AI 出题 | 选知识点→AI 流式生成→教师审核→入库 |
| 权限降级 | 角色变更→视口收敛→路由重定向 |
### 8.4 CI 稳定策略
- E2E 并行分片Playwright shard
- 重试 2 次flaky 容忍)
- 截图归档失败用例
- 独立测试 DB每次重置
---
## 9. 移动端策略(补 ai13 §3.10
### 9.1 响应式适配
| 端 | 适配 | 说明 |
| -------------- | ------------------ | --------------------------------- |
| teacher-portal | 响应式(桌面优先) | 教师主要用桌面,手机用于查看/通知 |
| student-portal | 移动优先 | 学生作答多用手机 |
| parent-portal | 移动优先 | 家长查看看手机 |
- Tailwind 断点:`sm 640 / md 768 / lg 1024 / xl 1280`
- AppShell 移动端折叠侧边栏为抽屉(汉堡菜单)
- 触摸目标 ≥ 44pxWCAG 2.5.5
### 9.2 PWA / 离线(预留,补 ai13 §3.10
| 能力 | 预留点 | 触发时机 |
| -------------- | -------------------------------------------------- | ---------------- |
| Service Worker | `sw.ts` 预留next-pwa 接入 | 教师弱网批改需求 |
| 离线缓存 | TanStack Query `onlineManager` + mutation 离线队列 | 弱网 |
| 可安装 | manifest.json + installable | 学校要求桌面图标 |
| 后台同步 | Background Sync API | 离线提交作业 |
> 当前不实现,但 ApiClient 的 mutation 设计为可排队(失败入队列,上线重放)。
### 9.3 原生 App远期补 ai13 §3.10
- hooks/contracts 纯 TS 不耦合 DOM可被 React Native 复用
- 若未来出 RN App复用 usePermission/useAuth/ApiClientUI 层重写
- 不为此提前抽象,保持当前 Next.js 实现简洁YAGNI
---
## 10. 数据流与乐观更新(补 ai13 §3.12
### 10.1 乐观更新模式
```typescript
const gradeMutation = useMutation({
mutationFn: (input) => api.post("/api/v1/grades", input),
onMutate: async (input) => {
await queryClient.cancelQueries({
queryKey: ["teacher", "grades", input.classId],
});
const previous = queryClient.getQueryData([
"teacher",
"grades",
input.classId,
]);
queryClient.setQueryData(["teacher", "grades", input.classId], (old) =>
old?.map((g) => (g.id === input.id ? { ...g, score: input.score } : g)),
);
return { previous };
},
onError: (err, input, context) => {
queryClient.setQueryData(
["teacher", "grades", input.classId],
context.previous,
); // 回滚
toast.error("保存失败,已回滚");
},
onSettled: (data, err, input) => {
queryClient.invalidateQueries({
queryKey: ["teacher", "grades", input.classId],
}); // 与服务端同步
},
});
```
### 10.2 缓存失效链路
mutate 一个资源后,需联动失效所有依赖它的缓存:
| mutation | 失效 queryKey |
| -------- | ------------------------------------------------------------------------------------------------------------------- |
| 创建班级 | `['teacher','classes']``['teacher','dashboard']` |
| 发布考试 | `['teacher','exams']``['teacher','classes',{id},'exams']` |
| 批改作业 | `['teacher','grades']``['teacher','homework',{id}]``['student','grades']`(跨端,走 WebSocket 推送 invalidate |
| 角色变更 | `['session','effective-permissions']``['session','viewports']`、所有业务 query |
### 10.3 冲突解决
| 场景 | 策略 |
| ---------------------- | ------------------------------------------------------------------------ |
| 多教师同时批改同一作业 | 后端乐观锁version 字段);前端 409 时重新拉取 + 提示"数据已被他人修改" |
| 学生重复提交 | 后端幂等event_id 去重);前端禁用提交按钮 + loading |
| 缓存与服务端不一致 | staleTime 到期自动重取;关键操作后强制 invalidate |
---
## 11. MF 运维架构(补 ai13 §3.11
### 11.1 Remote 版本与兼容性
| 维度 | 策略 |
| -------- | --------------------------------------------------------------------------------------- |
| 版本号 | Shell 与 Remote 各自 SemverremoteEntry.js 路径带版本 `remoteEntry.{version}.js` |
| 契约版本 | Shell↔Remote 交互契约(暴露组件 props、共享 store shape独立版本化 `@edu/mf-contracts` |
| 兼容范围 | Shell 声明 `peerDeps` 兼容的 Remote 主版本范围;不兼容版本走 fallback UI |
| 升级顺序 | 先升 shared depsreact 等)→ 再升 Remote → 最后升 Shell每步灰度 |
| 回滚 | remoteEntry.js 保留 N 个历史版本CI 一键切回上一版本 URL |
### 11.2 Remote 故障与降级
```mermaid
graph LR
A[Shell 加载 Remote] --> B{remoteEntry.js 可达?}
B -- 否 --> C[重试 3 次指数退避]
C --> D{仍失败?}
D -- 是 --> E[渲染 Fallback UI]
E --> F[提示模块暂不可用 + 重试按钮 + 通知运维]
B -- 是 --> G[执行 Remote 模块]
G --> H{运行时抛错?}
H -- 是 --> I[Remote 级 ErrorBoundary 捕获]
I --> E
H -- 否 --> J[正常渲染]
```
- 每个 Remote 用独立 `<ErrorBoundary fallback={...}>` 包裹,单 Remote 崩溃不带垮整个 Shell
- 动态 import 用 `@next/dynamic` + `ssr: false` + `loading` 占位
- Remote 不可用不阻断其他 Remote 与 Shell 自身路由
### 11.3 共享依赖演化
| 场景 | 处理 |
| -------------------------- | ------------------------------------------------------------------------------------- |
| React 18 → 19 大版本升级 | 全 4 端统一窗口期升级;过渡期 MF `shared.singleton` 容忍 minor 差异major 不一致报错 |
| 新增共享依赖(如 zustand | 先在 `@edu/mf-contracts` 登记 → Shell exposes shared → Remote 声明 shared |
| 移除共享依赖 | 先 Remote 移除引用 → 确认无 Remote 引用 → Shell 移除 shared |
| 版本漂移检测 | CI 校验 4 端 `package.json` 的 shared deps 版本一致pnpm dedupe + 自定义检查) |
### 11.4 跨端类型安全
- Shell 通过 `@module-federation/typescript` 生成 `@mf-types/*`Remote 消费 Shell 暴露组件的类型
- 运行时边界:跨端调用一律走 Hook`useApi()`/`usePermission()`),不直接访问对方内部 store
- 契约测试:`@edu/mf-contracts` 携带类型 + 运行时 zod schemaRemote 接入 Shell 时校验
### 11.5 路由策略
| 关注点 | 方案 |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| 路由前缀 | `/teacher/*` `/student/*` `/parent/*` `/admin/*`004 §5.4 场景域划分) |
| 深链直入 | URL `/student/exams/123` → Shell middleware 识别前缀 → 动态加载 student remote → remote 内部路由匹配 |
| 404 | Shell 全局 `not-found.tsx` + 各 Remote 内部 `not-found.tsx` |
| 路由守卫 | `middleware.ts` 统一鉴权:无 token → `/login`;权限不足当前前缀 → 重定向到对应 scope dashboard |
| 路由切换 loading | next.js `loading.tsx`Suspense骨架屏 + 滚动恢复 |
---
## 12. 远期演进铺垫
### 12.1 远期能力预留(不立即实现,但架构不堵死)
| 能力 | 预留点 | 触发时机 |
| ------------------- | ------------------------------------------------------------------ | ---------------- |
| PWA / 离线 | Service Worker 预留 `sw.ts`TanStack Query onlineManager 离线队列 | 教师弱网批改需求 |
| 多租户 / SaaS | 前端按 `tenant_id` 注入i18n glossary 支持租户覆写 | 多校部署 |
| Feature Flags | `packages/contracts/src/flags.ts` 统一枚举middleware 注入 | 灰度发布 |
| 移动端复用 | hooks/contracts 纯 TS 可被 React Native 复用;不耦合 DOM | RN App |
| 插件化 | Shell 预留 Remote 注册表(动态 manifest第三方 Remote 可挂载 | 开放生态 |
| Web Components 抽取 | ui-components 用 lit 封装跨框架版本(远期) | 跨技术栈 |
| A/B 测试 | edge middleware 分桶 + feature flag | 增长需求 |
### 12.2 ADR架构决策记录登记
本设计产生的关键决策应沉淀为 ADR 供未来回溯:
| ADR | 决策 | 状态 |
| ------- | --------------------------------------------------------- | ---------------------- |
| ADR-013 | Token 存储localStorage 过渡 → 内存+httpOnly cookie 终态 | 拟采纳 |
| ADR-014 | MF 拓扑:单 Shell + 3 Remote | 已采纳(沿用 ADR-012 |
| ADR-015 | 状态管理 5 层模型 | 拟采纳 |
| ADR-016 | i18n 路由前缀式 + next-intl | 拟采纳 |
| ADR-017 | 性能预算 + bundle CI 门禁 | 拟采纳 |
| ADR-018 | MF Remote SRI + 最小暴露面 | 拟采纳 |
| ADR-019 | 多 Tab 会话同步 BroadcastChannel | 拟采纳 |
> ADR 正式登记由 coord 在 `docs/architecture/adr/` 维护ai07 提请。
### 12.3 待 coord 协调问题
| # | 问题 | 影响 | 建议裁决 |
| --- | ------------------------------------------------------------------------------ | --------------------- | ---------------------------------------------------------------- |
| 1 | `full-stack-runbook.md` §2.1 端口矩阵未更新到 4 端(仍记 teacher-portal=3000 | 端口权威源不一致 | coord 更新 runbook 为 4000-4003 |
| 2 | packages 归属ui-tokens/ui-components/hooks | 维护边界 | ai07 维护前端专属包coord 维护 shared-ts/contracts |
| 3 | ~~GraphQL vs RESTBFF 何时切 GraphQL~~ **已裁决F9 + 总裁 §0.3** | 前端是否引入 urql | **P2 起 BFF 即 GraphQL前端即 urql无 REST 中间态**(见 §1.4 |
| 4 | admin-bff 是否存在004 §4 未列出,但 admin-portal 消费 `/admin/*` | admin-portal 契约缺失 | coord 确认 admin-bff 归属或由 iam 承载管理 API |
| 5 | shared deps 版本对齐 CI 校验机制 | MF singleton 安全 | coord 在 CI 增加 4 端 package.json diff 检查 |
| 6 | `@edu/mf-contracts` 包归属 | Shell↔Remote 契约载体 | 建议 coord 维护(跨 AI 共享) |
| 7 | Next.js 14→15 升级窗口 | App Router 破坏性变更 | P2 收尾统一升级4 端同步 |
---
## 13. 覆盖度对照ai13 审核 §3 遗漏逐项闭合)
| ai13 §3 遗漏项 | 本文档章节 | 状态 |
| -------------------------- | ---------------------- | ------- |
| §3.1 演进路线缺失 | §1 演进与迁移策略 | ✅ 闭合 |
| §3.2 安全架构缺失 | §2 安全架构 | ✅ 闭合 |
| §3.3 性能架构缺失 | §3 性能架构 | ✅ 闭合 |
| §3.4 可观测性深度缺失 | §4 可观测性深度 | ✅ 闭合 |
| §3.5 a11y 实施细节缺失 | §5 a11y 实施细节 | ✅ 闭合 |
| §3.6 i18n 实施细节缺失 | §6 i18n 实施细节 | ✅ 闭合 |
| §3.7 部署拓扑缺失 | §7 部署拓扑 | ✅ 闭合 |
| §3.8 测试策略细节缺失 | §8 测试策略细节 | ✅ 闭合 |
| §3.9 文件/目录结构缺失 | 02 §14ai13 已补) | ✅ 闭合 |
| §3.10 移动端策略缺失 | §9 移动端策略 | ✅ 闭合 |
| §3.11 MF 演进策略缺失 | §1.3 + §11 MF 运维架构 | ✅ 闭合 |
| §3.12 数据流与乐观更新缺失 | §10 数据流与乐观更新 | ✅ 闭合 |
---
**AI Agent**: ai074 端合并设计 / 长远架构补全)
**Branch**: docs/teacher-portal-long-term-architecture-ai07
**Coordinator**: coord-ai
**Closes**: ai13 audit §3 all 12 items
---
## 14. 回写记录
### 14.1 ai13 回写2026-07-09批次 0.15 后,总裁裁决 §3.4 ISSUE-036-ai13
**回写依据**:总裁裁决 §3.4 + §4.4 第 5 条(删除过渡方案本身,保留设计决策记录)+ coord F9P2 起 BFF 用 GraphQL
**回写内容**
| 位置 | 原内容 | 新内容 |
| ---------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| §1.1 P2 行 | "统一 API 层" | "GraphQL clienturqlF9 裁决 P2 起)" |
| §1.2 表格 | "BFF 切 GraphQLP4 起) + feature flag 灰度" | "BFF GraphQLP2 起,已裁决) + 前端 P2 起即 urql" |
| §1.4 整节 | "REST → GraphQL 切换策略"过渡方案P2-P4 REST + P5+ 迁移 + feature flag 灰度) | "GraphQL 客户端架构P2 起最终方案)"client 选型/单例/初始化/错误处理/缓存策略/ISSUE 关联/设计决策记录) |
| §12.3 #3 | 待裁决"P2-P3 RESTP4 起 BFF 切 GraphQL" | 已裁决"P2 起 BFF 即 GraphQL前端即 urql无 REST 中间态" |
**未删除内容**(设计决策记录,按 §4.4 保留):
- §1.4.6 设计决策记录:明确记录原过渡方案已废弃 + 裁决来源 + 影响范围,供未来回溯
- §2.1 Token 存储演进P2 localStorage → P6 httpOnly cookie属独立决策F12 裁决 P2 用 localStorage非"REST→GraphQL 过渡方案",不在本次回写范围
**关联 ISSUE**ISSUE-036-ai13已通过本次回写闭合
**AI Agent**: ai13teacher-portal回写执行
**Branch**: feat/teacher-portal-docs-rewrite-issue036-ai13
**Coordinator**: coord-ai
**依据**: president-final-rulings.md §3.4 + §4.4