Files
Edu/apps/teacher-portal/README.md
SpecialX b53a486c6e docs(teacher-portal): ai07 阶段1+2 模块理解确认书与架构设计文档
4端合并交付:理解确认书+架构设计+MF Shell/Remote架构+4端差异化对照表

AI Agent: ai07 (4 portals)

Branch: docs/teacher-portal-stage1-stage2-design-ai07
2026-07-09 17:24:58 +08:00

946 lines
66 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# teacher-portal / 4 端微前端架构设计
> 版本1.0
> 日期2026-07-09
> AI Agentai07前端 4 端teacher-portal / student-portal / parent-portal / admin-portal
> 阶段:阶段 1理解确认书+ 阶段 2模块架构设计文档
> 关联文档:
>
> - [004 架构影响地图](../../docs/architecture/004_architecture_impact_map.md) §1.1a/1.1b/§5.4
> - [AI 分配方案](../../docs/architecture/ai-allocation.md) §5 ai07
> - [项目规则](../../.trae/rules/project_rules.md) §3.8/§3.9/§3.10
> - [编码规范](../../docs/standards/coding-standards.md) §2.8-2.10/§7
> - [迁移指南](../../MIGRATION_GUIDE.md) §7.1-7.7
> - [known-issues](../../docs/troubleshooting/known-issues.md) §2.12
> 本文档合并 4 端的设计,因 ai07 单一负责全部 4 端Module Federation shell + remote 架构需统一设计4 端共享组件库和权限体系。当前仓库仅 `teacher-portal` 已实现P1 测试页 + P2 骨架student/parent/admin-portal 待建。
---
## 目录
1. [阶段 1模块理解确认书4 端)](#阶段-1模块理解确认书4-端)
2. [teacher-portal 现状审计(对齐黄金模板)](#teacher-portal-现状审计对齐黄金模板)
3. [阶段 2模块架构设计文档](#阶段-2模块架构设计文档)
4. [4 端差异化对比表](#4-端差异化对比表)
5. [与其他模块的交互点(契约清单)](#与其他模块的交互点契约清单)
6. [风险与假设](#风险与假设)
7. [coord 交叉审查所需信息](#coord-交叉审查所需信息)
---
# 阶段 1模块理解确认书4 端)
## 1.1 我在架构中的位置
| 维度 | teacher-portal | student-portal | parent-portal | admin-portal |
| ------------ | --------------------------------------------------------- | ---------------- | ---------------- | --------------------------- |
| 层级 | L2 微前端层 | L2 微前端层 | L2 微前端层 | L2 微前端层 |
| MF 角色 | **Shell 宿主**(主应用) | Remote子应用 | Remote子应用 | Remote子应用 |
| 上游 | 浏览器(教师 / 教导主任 / 教研组长) | 浏览器(学生) | 浏览器(家长) | 浏览器(系统/校管理员) |
| 下游(同步) | api-gatewayREST经 Next.js rewrites 代理) | api-gateway | api-gateway | api-gateway |
| 下游(推送) | push-gatewayWebSocket/SSEP5 | push-gateway | push-gateway | — |
| BFF 对接 | teacher-bffGraphQL Yoga + DataLoader | student-bff | parent-bff | teacher-bff 复用 + iam 直连 |
| 通信方式 | HTTP/REST前端→Gateway+ WebSocket前端→push-gateway | 同左 | 同左 | HTTP/REST |
**说明**
- 4 端均通过 `next.config.js``rewrites``/api/v1/*` 代理到 `api-gateway`,前端不直连任何业务服务或 BFF 后端实例
- MF 架构下4 端共享同一 Shellteacher-portal 作为 Shell 宿主),其余 3 端作为 Remote 子应用挂载Shell 提供 AppShell + 共享组件库 + 权限 Hook + API 请求层
- 场景域 BFF 复用策略004 §5.4):教导主任/教研组长复用 teacher-portal + 额外管理视口,不单独建 portal
## 1.2 我的限界上下文
| 项 | teacher-portal | student-portal | parent-portal | admin-portal |
| -------- | --------------------------------------------------------------------------------------------------- | -------------------------------- | ---------------------------------------- | ------------------------------------------------- |
| 业务领域 | 教学场景域 | 学习场景域 | 家长场景域 | 管理场景域 |
| 主要聚合 | 班级、考试、作业、成绩、备课、AI 出题 | 作答、作业提交、学情诊断、错题本 | 多子女切换、通知偏好、学情查看、成绩通知 | 用户/角色/权限/视口 CRUD、组织/班级管理、平台监控 |
| 不负责 | 学生作答界面、家长多子女切换 | 教师批改界面、AI 出题 | 教师沟通、学生作答 | 教学业务编排(归 teacher-portal |
| 数据范围 | DataScope L1-L5教师 L1 班级 / 教导主任 L2 年级 / 校管理员 L3 学校 / 区教研员 L4 / 系统管理员 L5 | DataScope L0仅本人 | DataScope L0仅子女 | DataScope L3-L5校管理员 L3 / 系统管理员 L5 |
## 1.3 我与外部的契约
### 1.3.1 消费的后端 API经 api-gateway 代理)
| 端 | 路径前缀 | 下游 BFF/服务 | 关键端点 |
| -------------- | ------------------------------------------------------------------------ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| teacher-portal | `/api/v1/iam/*` | iam | `POST /iam/login``POST /iam/register``POST /iam/refresh``GET /iam/me``GET /iam/rbac/...``GET /iam/effective-permissions` |
| teacher-portal | `/api/v1/teacher/*` | teacher-bff | `GET /teacher/viewports``GET /teacher/dashboard``GET /teacher/classes/:id/exams``GET /teacher/classes/:id/homework``GET /teacher/exams/:id/grades` |
| teacher-portal | `/api/v1/classes/*` | core-educlasses 模块) | CRUD黄金模板 |
| teacher-portal | `/api/v1/exams/*` `/api/v1/homework/*` `/api/v1/grades/*` | core-edu | P3 教学核心 |
| teacher-portal | `/api/v1/textbooks/*` `/api/v1/knowledge-points/*` `/api/v1/questions/*` | content | P4 内容 |
| teacher-portal | `/api/v1/ai/*` | aiSSE 流式) | P5 AI 辅助出题 |
| student-portal | `/api/v1/student/*` | student-bff | `GET /student/viewports``GET /student/dashboard``GET /student/homework``POST /student/homework/:id/submit``GET /student/diagnostic` |
| parent-portal | `/api/v1/parent/*` | parent-bff | `GET /parent/viewports``GET /parent/children``POST /parent/switch-child``GET /parent/notifications``PUT /parent/notification-preferences` |
| admin-portal | `/api/v1/iam/*`(管理用) | iam | 用户/角色/权限/视口 CRUD |
| admin-portal | `/api/v1/admin/*` | teacher-bff 复用 + iam 直连 | 平台监控、统计数据聚合 |
| 全部 | `/api/v1/notifications/*` | msg | 通知中心P5 |
### 1.3.2 统一响应契约
所有后端响应遵循 `ActionState` 结构(迁移指南 §7.5
```typescript
type ActionState<T> =
| { success: true; data: T }
| {
success: false;
error: { code: string; message: string; details?: unknown };
};
```
错误码前缀按服务名大写(如 `IAM_``CORE_EDU_``CONTENT_``MSG_``AI_``BFF_``GW_`)。前端 API 请求层根据 `error.code` 前缀路由到对应的 i18n key。
### 1.3.3 推送契约P5
| 端 | 协议 | 场景 |
| -------------- | ------------------------- | -------------------------------------------- |
| teacher-portal | WebSocketpush-gateway | 学生提交作业通知、考试成绩录入提醒、全校广播 |
| student-portal | WebSocket | 考试发布通知、成绩发布、作业截止提醒 |
| parent-portal | WebSocket | 子女成绩发布、教师沟通、学校通知 |
| admin-portal | — | 不消费推送(管理端用轮询) |
## 1.4 我的技术栈
| 维度 | 选型 | 说明 |
| --------------------------- | -------------------------------------------------------- | -------------------------------------------------------- |
| 框架 | Next.js 14+App Router | 4 端统一server components 默认client components 按需 |
| 语言 | TypeScript 5.5+strict | 沿用 tsconfig.base.json |
| 微前端 | Module Federation 2.0@module-federation/nextjs-mf | teacher-portal = Shell其余 = Remote |
| 样式 | Tailwind CSS 3.4+ | 配合设计令牌三层模型 |
| UI 组件库 | shadcn/ui迁移指南 §7.2 | 平移至 `packages/ui-components/`MF 共享 |
| 状态管理 L1 URL | nuqs | 可分享、可刷新状态 |
| 状态管理 L2 Server | TanStack Query v5 | 服务端数据缓存、重试、乐观更新 |
| 状态管理 L3 Client Business | Zustand slice | 客户端业务状态 |
| 状态管理 L4 Global UI | Zustand ui-store + ModalRoot | 全局 UI 状态 |
| 状态管理 L5 Form | react-hook-form + zodResolver | 表单状态 |
| 富文本 | Tiptap备课、出题、反馈 | SSR 安全 |
| 图表 | recharts | 学情、Dashboard |
| i18n | next-intl | BFF/服务返回 i18n key + 参数,前端翻译 |
| A11y | eslint-plugin-jsx-a11yerror 级) | WCAG 2.2 AA |
| 字体 | Intersans/ Frauncesserif/ JetBrains Monomono | next/font/google 加载CSS 变量暴露 |
## 1.5 我的阶段归属
| 端 | 阶段 | 当前状态 | 依赖上游阶段 |
| -------------- | ---- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| teacher-portal | P2 | ✅ 已实现 P1 测试页 + P2 骨架(登录/AppShell/Dashboard/classes CRUD 待审计对齐黄金模板 + 引入 MF + 共享组件库 | P1api-gateway + classes + iam |
| student-portal | P3 | 📐 需设计(待 core-edu + student-bff 就绪) | P3core-edu + student-bff |
| parent-portal | P4 | 📐 需设计(待 parent-bff + data-ana 就绪) | P4parent-bff + data-ana |
| admin-portal | P6 | 📐 需设计(待全部业务服务稳定) | P6硬化阶段 |
## 1.6 黄金模板对齐清单(对照 classes 服务)
> 前端无 `@RequirePermission` 装饰器(后端概念),对齐项改造为前端等价物。
| 对齐项 | classes后端黄金模板 | teacher-portal 前端等价 | 当前状态 |
| --------------------- | ------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------- |
| 权限校验 | `@RequirePermission(Permissions.XXX)` | `usePermission().hasPermission("XXX")` Hook + `<RequirePermission>` 组件 | ❌ 缺失,直接硬编码 `user.roles.join(", ")` |
| 错误码前缀统一 | `CLASSES_*``IAM_*` | API 请求层根据 `error.code` 前缀路由 i18n | ❌ 缺失统一请求层 |
| logger | pino | 前端 console + SentryP6 | ⚠️ 仅 console.error |
| metrics | prom-client `/metrics` | 前端 Web Vitals → Gateway 上报 | ❌ 缺失 |
| tracer | OTel SDK | 前端 OTel browser SDKP6 | ❌ 缺失 |
| /healthz + /readyz | `GET /healthz` `GET /readyz` | Next.js `/api/health` route + Dockerfile HEALTHCHECK | ⚠️ Dockerfile 有 HEALTHCHECK无 /api/health |
| 优雅关闭 | SIGTERM handler | Next.js 无长连接,无需 | ✅ N/A |
| 测试覆盖率 ≥ 80% | Vitest | Vitest + @testing-library/react + Playwright E2E | ❌ 0% |
| Dockerfile 多阶段构建 | builder + runtime | 已有多阶段 | ✅ 已对齐 |
| Zod 输入验证 | class-validator + Zod schema | react-hook-form + zodResolver | ❌ 缺失 |
| GlobalErrorFilter | NestJS 全局异常过滤器 | React ErrorBoundary + API 请求层统一错误处理 | ❌ 缺失 |
| 设计令牌三层 | — | primitive.css / semantic-light/dark.css / tailwind-theme.css | ❌ 硬编码在 globals.css + tailwind.config.js |
| A11y 工具集 | — | useA11yId / mergeA11yProps / describeInput / focus-trap | ❌ 缺失 |
---
# teacher-portal 现状审计(对齐黄金模板)
## 2.1 审计表
| 维度 | 状态 | 说明 |
| ------------------------------------ | ------ | ---------------------------------------------------------------------- |
| 权限装饰器(前端等价 usePermission | ❌ | AppShell.tsx 直接 `user.roles.join(", ")`,违反 project_rules §3.8 |
| 错误码前缀 | ❌ | 无统一 API 请求层,错误处理散落在每个 page.tsx |
| logger | ⚠️ | 仅 `console.error`,无结构化、无 trace_id |
| metrics | ❌ | 无 Web Vitals 采集 |
| tracer | ❌ | 无 OTel browser SDK |
| /healthz | ⚠️ | Dockerfile 有 `HEALTHCHECK wget /`,但无 `/api/health` route |
| /readyz | ❌ | 无 |
| 优雅关闭 | ✅ N/A | Next.js 无长连接 |
| 测试覆盖率 | ❌ | 0%,无测试文件 |
| Dockerfile 多阶段 | ✅ | builder + runtime非 root 用户HEALTHCHECK |
| Zod 输入验证 | ❌ | 表单直接 useState无 zodResolver |
| GlobalErrorFilterErrorBoundary | ❌ | 无 React ErrorBoundary |
| 设计令牌三层 | ❌ | 硬编码在 globals.css`:root` 变量)+ tailwind.config.jshex 字面量) |
| A11y 工具集 | ❌ | 无 useA11yId、focus-trap 等 |
| Module Federation 配置 | ❌ | next.config.js 仅有 rewrites无 MF |
| 5 层状态管理 | ❌ | 仅 useState + localStorage无 nuqs/TanStack Query/Zustand |
| 共享组件库 | ❌ | 仅 AppShell无 ErrorBoundary/Loading/Empty/RequirePermission |
| i18n | ❌ | 中文硬编码在 JSX |
| API 请求层 | ❌ | 每页重复 fetch + authHeaders + try/catch |
| ESLint flat config 自定义规则 | ❌ | 未配置 no-hardcoded-fonts / design-tokens 规则 |
## 2.2 现有文件清单
```
apps/teacher-portal/
├─ src/
│ ├─ app/
│ │ ├─ (app)/ # 受保护路由组(套 AppShell
│ │ │ ├─ classes/page.tsx # 班级 CRUDP1 测试页)
│ │ │ ├─ dashboard/page.tsx # 教师仪表盘
│ │ │ ├─ exams/page.tsx # 考试列表
│ │ │ ├─ grades/page.tsx # 成绩查询
│ │ │ ├─ homework/page.tsx # 作业列表
│ │ │ └─ layout.tsx # 套 AppShell
│ │ ├─ login/page.tsx # 登录页(不套壳)
│ │ ├─ globals.css # 全局样式 + 设计令牌(硬编码)
│ │ ├─ layout.tsx # 根布局(字体加载)
│ │ └─ page.tsx # 根路径重定向
│ ├─ components/
│ │ └─ AppShell.tsx # 左侧栏 + 主内容区
│ └─ lib/
│ └─ auth.ts # token + userInfo localStorage 管理
├─ Dockerfile # 多阶段构建 ✅
├─ next.config.js # 仅 rewrites无 MF ❌
├─ package.json # 仅 next/react/react-dom无 MF/Query/Zustand ❌
├─ tailwind.config.js # 硬编码 hex ❌
├─ postcss.config.js
└─ tsconfig.json
```
## 2.3 主要违规点(必须在 P2 收尾或 P3 起步时修复)
1. **权限硬编码**[AppShell.tsx:143](src/components/AppShell.tsx) `user.roles.join(", ")` 违反 project_rules §3.8,必须改为 `usePermission().hasPermission()`
2. **设计令牌硬编码**[globals.css:6-13](src/app/globals.css) 与 [tailwind.config.js:7-19](tailwind.config.js) 出现 `hsl(...)` 字面量与 `'Fraunces'`/`'Inter'` 字面量,违反 project_rules §3.10
3. **无统一 API 请求层**4 个 page.tsx 重复 `authHeaders()` + `fetch` + `try/catch` + `setError`,必须抽取到 `lib/api.ts`
4. **无权限 Hook**:缺少 `usePermission().hasPermission()`,无法做 L3 组件级视口控制
5. **无 ErrorBoundary**React 渲染异常会白屏
6. **无 5 层状态管理**:登录态用 localStorageL3但无 TanStack QueryL2导致每页重复 fetch
7. **字体名硬编码**[layout.tsx:3-7](src/app/layout.tsx) 直接 import `Inter/Fraunces/JetBrains_Mono`,应改为 `var(--font-family-sans/serif/mono)`
---
# 阶段 2模块架构设计文档
## 3.1 模块内部分层图4 端统一 MF 架构)
```mermaid
graph TB
subgraph Browser["浏览器"]
URL[URL 路由]
end
subgraph Shell["teacher-portalShell 宿主)"]
AppShell[AppShell<br/>左栏导航 + 主内容区]
RootLayout[RootLayout<br/>字体/令牌/i18n Provider]
Router[Next.js App Router]
SharedDeps["共享依赖暴露<br/>react/react-dom/@tanstack/react-query/zustand/nuqs"]
end
subgraph RemoteTeacher["teacher-portal Remote 模块"]
TeacherPages[教学场景页面<br/>dashboard/classes/exams/homework/grades/ai-assist]
end
subgraph RemoteStudent["student-portalRemote"]
StudentPages[学习场景页面<br/>dashboard/homework/submit/diagnostic/exam-taking]
end
subgraph RemoteParent["parent-portalRemote"]
ParentPages[家长场景页面<br/>dashboard/children-switch/grades/notifications]
end
subgraph RemoteAdmin["admin-portalRemote"]
AdminPages[管理场景页面<br/>users/roles/permissions/viewports/monitoring]
end
subgraph Shared["共享层packages/"]
UITokens[ui-tokens<br/>三层设计令牌]
UIComponents[ui-components<br/>shadcn + A11y + ErrorBoundary]
Contracts[contracts<br/>Permissions 常量 + 类型]
Hooks[hooks<br/>usePermission/useAuth/useA11y]
LibTS[shared-ts<br/>通用工具]
end
subgraph Gateway["api-gateway"]
GW[Gin 路由/鉴权/限流]
end
Browser --> URL
URL --> RootLayout
RootLayout --> AppShell
AppShell --> Router
Router -->|动态加载| RemoteTeacher
Router -->|动态加载| RemoteStudent
Router -->|动态加载| RemoteParent
Router -->|动态加载| RemoteAdmin
RemoteTeacher --> SharedDeps
RemoteStudent --> SharedDeps
RemoteParent --> SharedDeps
RemoteAdmin --> SharedDeps
Shell --> UITokens
Shell --> UIComponents
Shell --> Contracts
Shell --> Hooks
RemoteTeacher --> UITokens
RemoteStudent --> UITokens
RemoteParent --> UITokens
RemoteAdmin --> UITokens
AppShell -->|fetch /api/v1/iam/effective-permissions| Hooks
Hooks -->|透传 token| GW
RemoteTeacher -->|fetch /api/v1/teacher/*| GW
RemoteStudent -->|fetch /api/v1/student/*| GW
RemoteParent -->|fetch /api/v1/parent/*| GW
RemoteAdmin -->|fetch /api/v1/iam/* + /api/v1/admin/*| GW
```
### 3.1.1 MF 拓扑选型
| 方案 | 选否 | 理由 |
| ------------------------------------ | ---- | --------------------------------------------------------------------------------------- |
| 4 端独立部署 + 独立域名 + 各自 Shell | ❌ | 4 套 Shell 重复,登录态/权限/组件库要重复实现 |
| 单 Shell + 4 Remote**采用** | ✅ | teacher-portal 作为 Shell 宿主,提供 AppShell + 共享依赖;其余 3 端作为 Remote 动态加载 |
| 单一 Next.js 应用 + 4 路由组 | ❌ | 违反"微前端独立部署"目标ADR-012 |
**Shell 职责**
- RootLayout字体、设计令牌、i18n Provider、TanStack QueryClientProvider、Zustand StoreProvider
- AppShell左侧导航 + 主内容区 + 用户信息 + 登出)
- 共享依赖暴露react、react-dom、@tanstack/react-query、zustand、nuqs、ui-components、ui-tokens、contracts、hooks
- 路由表4 端路由前缀:`/teacher/*``/student/*``/parent/*``/admin/*`
- 登录页(统一登录入口,按角色重定向到对应 portal
**Remote 职责**
- 各场景域页面page.tsx
- 各场景域专属组件
- 各场景域专属 Zustand slice
- 通过 MF 共享 Shell 暴露的依赖,避免重复加载
### 3.1.2 MF 配置next.config.js
```javascript
// teacher-portal/next.config.jsShell
const NextFederationPlugin = require("@module-federation/nextjs-mf");
const remotes = (isServer) => ({
student: `student_app@http://localhost:3001/_next/static/${isServer ? "ssr" : "chunks"}/remoteEntry.js`,
parent: `parent_app@http://localhost:3002/_next/static/${isServer ? "ssr" : "chunks"}/remoteEntry.js`,
admin: `admin_app@http://localhost:3003/_next/static/${isServer ? "ssr" : "chunks"}/remoteEntry.js`,
});
module.exports = {
reactStrictMode: true,
webpack(config, { isServer }) {
config.plugins.push(
new NextFederationPlugin({
name: "teacher_app",
filename: "static/chunks/remoteEntry.js",
remotes: remotes(isServer),
exposes: {
"./AppShell": "./src/components/AppShell",
"./shared-deps": "./src/shared/deps",
},
shared: {
react: { singleton: true, requiredVersion: "^18.3.0" },
"react-dom": { singleton: true, requiredVersion: "^18.3.0" },
"@tanstack/react-query": { singleton: true },
zustand: { singleton: true },
nuqs: { singleton: true },
},
extraOptions: { exposePages: false },
}),
);
return config;
},
async rewrites() {
return [
{
source: "/api/v1/:path*",
destination: `${process.env.API_GATEWAY_URL || "http://localhost:8080"}/api/v1/:path*`,
},
];
},
};
```
> Remote 端配置对称:`name: 'student_app'``exposes: { './pages': './src/pages' }``remotes: { teacher: 'teacher_app@...' }`。
## 3.2 领域模型(前端视角)
前端不持有业务聚合根,仅持有"视图模型"ViewModel和"会话状态"。
### 3.2.1 会话状态Session
```typescript
interface Session {
user: UserInfo; // { id, email, name, roles, permissions, dataScope }
tokens: { accessToken: string; refreshToken: string };
viewports: ViewportItem[]; // L1 导航视口
expiresAt: number; // access token 过期时间戳
}
```
存储Zustand sessionSliceL3+ localStorage 持久化(刷新恢复)+ TanStack Query 缓存 `['session']`L2
### 3.2.2 视口模型Viewport
```typescript
interface ViewportItem {
key: string; // 'dashboard' | 'classes' | ...
label: string; // i18n key 或显式文案
route: string; // '/teacher/dashboard'
icon: string | null; // 图标 key按需
sortOrder: number; // 排序
requiredPermission: string | null; // 'CLASSES_READ' 等
scope: "teacher" | "student" | "parent" | "admin"; // 标记归属哪个 portal
}
```
来源:`GET /api/v1/{scope}/viewports`BFF 聚合 iam 视口配置。AppShell 按 `scope` 过滤渲染对应 portal 的导航。
### 3.2.3 权限模型Permission
```typescript
interface PermissionState {
permissions: string[]; // ['CLASSES_READ', 'EXAMS_CREATE', ...]
dataScope: DataScope; // L0-L5
hasPermission: (perm: string) => boolean;
hasAnyPermission: (perms: string[]) => boolean;
hasAllPermissions: (perms: string[]) => boolean;
}
```
来源:`GET /api/v1/iam/effective-permissions``{ permissions, viewports, dataScope }`。Redis 缓存 5miniam 侧),前端 TanStack Query 缓存 5min角色变更主动 invalidate。
## 3.3 数据模型(前端)
前端无数据库,仅有缓存层:
| 数据类型 | 存储 | TTL | 失效策略 |
| ----------------------- | ---------------------- | --------------------------- | -------------------------------------- |
| Sessiontoken + user | localStorage + Zustand | access 15min / refresh 7day | 401 自动 refreshrefresh 失败跳登录 |
| 权限列表 | TanStack Query cache | 5min | 角色变更事件 invalidate |
| 视口列表 | TanStack Query cache | 5min | 同上 |
| 班级/年级列表 | TanStack Query cache | 5min | staleTime 5minmutation 后 invalidate |
| 教学资源详情 | TanStack Query cache | 30s | staleTime 30s |
| 学情宽表 | TanStack Query cache | 30s | staleTime 30s实时性由 BFF 决定) |
| URL 状态(分页/筛选) | nuqs | — | 永久(可分享) |
| 表单临时态 | react-hook-form | — | 卸载即销毁 |
## 3.4 API 设计(前端 → 后端)
前端不设计后端 API仅声明消费的端点。详见 §1.3.1。
### 3.4.1 统一 API 请求层lib/api.ts
```typescript
// packages/shared-ts/src/api-client.ts共享
interface ApiClientOptions {
baseUrl?: string; // 默认 ''(走 Next.js rewrites
getToken?: () => string | null;
onUnauthorized?: () => void; // 401 → refresh → 重试 / 跳登录
onError?: (error: ApiError) => void; // 全局 toast
}
class ApiClient {
async get<T>(path: string, query?: Record<string, string>): Promise<T>;
async post<T>(path: string, body: unknown): Promise<T>;
async put<T>(path: string, body: unknown): Promise<T>;
async delete<T>(path: string): Promise<T>;
async sse<T>(path: string, body: unknown): AsyncIterable<T>; // AI 流式
}
// 错误结构
interface ApiError {
code: string; // 'IAM_INVALID_CREDENTIALS'
message: string; // 已 i18n 翻译或后端原文
details?: unknown;
httpStatus: number;
}
```
**职责**
- 自动注入 `Authorization: Bearer ${token}`
- 401 自动 refresh token 一次,失败调 `onUnauthorized`
- 解析 `ActionState`success=false 抛 `ApiError`
-`error.code` 前缀路由 i18n key
- 全局错误 toast除 401
- 请求/响应 trace_id 透传(从响应头 `X-Request-Id` 提取)
### 3.4.2 TanStack Query 约定
```typescript
// Query Key 命名:[scope, resource, ...args]
queryKey: ["teacher", "classes", { gradeId }];
queryKey: ["teacher", "exams", classId];
queryKey: ["session", "effective-permissions"];
queryKey: ["session", "viewports", "teacher"];
// Mutation 约定
const mutation = useMutation({
mutationFn: (input) => api.post("/api/v1/classes", input),
onSuccess: () =>
queryClient.invalidateQueries({ queryKey: ["teacher", "classes"] }),
onError: (e: ApiError) => toast.error(e.message),
});
```
## 3.5 事件设计
前端不发布 Kafka 事件,仅消费 WebSocket 推送P5和 Server-Sent EventsAI 流式)。
### 3.5.1 WebSocket 推送P5
| 事件 | 触发 | 前端动作 |
| ----------------------- | ------------ | --------------------------------------- |
| `NotificationRequested` | msg 服务投递 | toast 提示 + 通知中心未读数 +1 |
| `ExamPublished` | 教师发布考试 | 学生端 toast + dashboard invalidate |
| `GradeRecorded` | 教师录入成绩 | 学生/家长端 toast + 成绩列表 invalidate |
| `HomeworkSubmitted` | 学生提交作业 | 教师端 toast + 作业批改列表 invalidate |
### 3.5.2 SSE 流式P5 AI 辅助出题)
```
GET /api/v1/ai/generate-questions (SSE)
data: {"delta": "题目"}\n\n
data: {"delta": "A. option1"}\n\n
data: {"done": true}\n\n
```
前端用 `AsyncIterable<T>` 消费Tiptap 逐字插入。
## 3.6 横切关注点对齐清单
### 3.6.1 权限(前端等价)
| 端 | 路由 | requiredPermission |
| -------------- | ------------------------------ | ------------------------ |
| teacher-portal | `/teacher/dashboard` | `TEACHER_DASHBOARD_VIEW` |
| teacher-portal | `/teacher/classes` | `CLASSES_READ` |
| teacher-portal | `/teacher/classes/new` | `CLASSES_CREATE` |
| teacher-portal | `/teacher/exams` | `EXAMS_READ` |
| teacher-portal | `/teacher/exams/new` | `EXAMS_CREATE` |
| teacher-portal | `/teacher/homework` | `HOMEWORK_READ` |
| teacher-portal | `/teacher/homework/:id/grade` | `HOMEWORK_GRADE` |
| teacher-portal | `/teacher/grades` | `GRADES_READ` |
| teacher-portal | `/teacher/ai-assist` | `AI_GENERATE` |
| student-portal | `/student/dashboard` | `STUDENT_DASHBOARD_VIEW` |
| student-portal | `/student/homework` | `HOMEWORK_READ_OWN` |
| student-portal | `/student/homework/:id/submit` | `HOMEWORK_SUBMIT` |
| student-portal | `/student/diagnostic` | `DIAGNOSTIC_READ_OWN` |
| parent-portal | `/parent/dashboard` | `PARENT_DASHBOARD_VIEW` |
| parent-portal | `/parent/children` | `PARENT_CHILDREN_VIEW` |
| parent-portal | `/parent/grades` | `GRADES_READ_CHILD` |
| admin-portal | `/admin/users` | `IAM_USER_READ` |
| admin-portal | `/admin/users/new` | `IAM_USER_CREATE` |
| admin-portal | `/admin/roles` | `IAM_ROLE_READ` |
| admin-portal | `/admin/permissions` | `IAM_PERMISSION_READ` |
| admin-portal | `/admin/viewports` | `IAM_VIEWPORT_READ` |
| admin-portal | `/admin/monitoring` | `ADMIN_MONITORING_VIEW` |
> 完整权限点常量集中在 `packages/contracts/src/permissions.ts`待建立coord 负责 shared-tsai07 负责调用。L3 组件级视口用 `<RequirePermission perm="EXAMS_CREATE"><Button>新建考试</Button></RequirePermission>`。
### 3.6.2 错误码清单(前端 i18n 路由)
| 前缀 | 来源服务 | i18n key 模式 |
| ------------ | -------------------------- | ------------------------ |
| `IAM_*` | iam | `iam.error.{{code}}` |
| `CORE_EDU_*` | core-edu | `coreEdu.error.{{code}}` |
| `CLASSES_*` | core-edu/classes | `classes.error.{{code}}` |
| `CONTENT_*` | content | `content.error.{{code}}` |
| `MSG_*` | msg | `msg.error.{{code}}` |
| `AI_*` | ai | `ai.error.{{code}}` |
| `BFF_*` | teacher/student/parent-bff | `bff.error.{{code}}` |
| `GW_*` | api-gateway | `gateway.error.{{code}}` |
| `NETWORK_*` | 前端网络层 | `network.error.{{code}}` |
### 3.6.3 Logger
```typescript
// packages/shared-ts/src/logger.ts
interface Logger {
info(msg: string, meta?: Record<string, unknown>): void;
warn(msg: string, meta?: Record<string, unknown>): void;
error(msg: string, meta?: Record<string, unknown>): void;
}
// 实现:开发环境 console + 结构化;生产环境 → SentryP6
// 必含字段trace_id从响应头提取、user_id、scope、path
```
### 3.6.4 MetricsWeb Vitals
| 指标 | 类型 | 上报 |
| ----------------------------- | ---- | --------------------------------------------------- |
| `teacher_portal_lcp_seconds` | LCP | `next/web-vitals``POST /api/v1/admin/web-vitals` |
| `teacher_portal_cls` | CLS | 同上 |
| `teacher_portal_fid_seconds` | FID | 同上 |
| `teacher_portal_ttfb_seconds` | TTFB | 同上 |
P6 接入P2-P5 暂缓。
### 3.6.5 TracerOTel browser SDKP6
```typescript
// packages/shared-ts/src/tracer.ts
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
// BatchSpanProcessor → OTLP exporter → collector → Tempo
// 自动埋点fetch、XMLHttpRequest、document load、user interaction
```
### 3.6.6 健康检查
| 端点 | 用途 | 实现 |
| ----------------- | ---------------------- | -------------------------------------------------------------- |
| `GET /api/health` | Dockerfile HEALTHCHECK | Next.js Route Handler返回 `{ status: 'ok', ts: Date.now() }` |
| `GET /api/ready` | K8s readinessProbe | 检查 `process.env.API_GATEWAY_URL` 可达 + 内存 < 阈值 |
### 3.6.7 优雅关闭
Next.js 无长连接(除 SSE/WS无需特殊处理。SSE/WS 在 P5 由 push-gateway 管理,前端断线自动重连。
## 3.7 共享组件库packages/ui-components/,待建立)
| 组件 | 用途 | 来源 |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------- | ------------------------------ |
| `AppShell` | 左侧栏 + 主内容区布局 | teacher-portal 现有 → 抽取共享 |
| `RequirePermission` | L3 组件级视口控制(无权限不渲染 children | 新建 |
| `ErrorBoundary` | React 渲染异常兜底fallback UI | 新建 |
| `Loading` | 骨架屏Skeleton | 新建 |
| `Empty` | 空态(插画 + 文案 + CTA | 新建 |
| `Modal` / `Dialog` | 全局 ModalModalRoot + Zustand ui-store | shadcn/ui |
| `Toast` | 全局 toast错误/成功/警告) | shadcn/ui sonner |
| `Button` / `Input` / `Select` / `Textarea` | 基础表单 | shadcn/ui |
| `DataTable` | 表格(排序/分页/筛选) | shadcn/ui + TanStack Table |
| `Chart` | 图表封装recharts | 新建 |
| `A11y` 工具集 | useA11yId / mergeA11yProps / describeInput / focus-trap / skip-link / visually-hidden / aria-status | 迁移指南 §7.7 |
| `Form` | react-hook-form + zodResolver 封装 | 新建 |
## 3.8 共享 Hookspackages/hooks/,待建立)
| Hook | 职责 |
| --------------------- | --------------------------------------------------- |
| `useAuth()` | 会话状态user/token/refresh/login/logout |
| `usePermission()` | 权限查询hasPermission/hasAny/hasAll + dataScope |
| `useViewports(scope)` | 视口列表(按 scope 过滤) |
| `useApi()` | ApiClient 实例(注入 token + 401 处理) |
| `useA11yId()` | 唯一 ARIA ID 生成 |
| `useAriaLive()` | aria-live 区域管理 |
| `useToast()` | 全局 toastZustand ui-store |
## 3.9 设计令牌三层packages/ui-tokens/,待建立)
```
packages/ui-tokens/
├─ primitive.css # Layer 1 原始色板/字号/间距/阴影
├─ semantic-light.css # Layer 2 语义令牌(亮色)
├─ semantic-dark.css # Layer 2 语义令牌(暗色)
├─ tailwind-theme.css # Layer 3 @theme inline 暴露 bg-*/text-*/font-*
└─ package.json
```
**强制规则**project_rules §3.10
- 禁止 `#hex` 字面量ESLint `no-restricted-syntax`
- 禁止 `'Inter'`/`'Fraunces'`/`'JetBrains Mono'` 字面量ESLint `design-tokens/no-hardcoded-fonts`
- 禁止 `font-size: Npx`(用 `var(--font-size-1~9)`
- 禁止 Tailwind 任意值 `w-[Npx]`(用 `--space-*` 或默认阶梯)
**令牌命名**(迁移指南 §7.1
| Layer 1 Primitive | Layer 2 Semantic | Layer 3 Tailwind |
| ------------------ | ------------------ | ---------------- |
| `--color-blue-500` | `--color-accent` | `bg-accent` |
| `--font-size-3` | `--font-size-body` | `text-body` |
| `--space-4` | `--space-md` | `p-md` |
---
# 4 端差异化对比表
## 4.1 整体差异
| 维度 | teacher-portal | student-portal | parent-portal | admin-portal |
| -------------- | -------------------------------------------------------- | -------------- | ------------- | --------------------------- |
| MF 角色 | Shell + Remote | Remote | Remote | Remote |
| 路由前缀 | `/teacher/*` | `/student/*` | `/parent/*` | `/admin/*` |
| 端口dev | 3000 | 3001 | 3002 | 3003 |
| 对接 BFF | teacher-bff | student-bff | parent-bff | teacher-bff 复用 + iam 直连 |
| 默认角色 | teacher / head_teacher / grade_director / subject_leader | student | parent | school_admin / system_admin |
| DataScope 默认 | L1-L5按角色 | L0 | L0 | L3-L5 |
| 推送消费 | ✅ WebSocket | ✅ WebSocket | ✅ WebSocket | ❌ 轮询 |
| AI 辅助 | ✅ 出题/备课/分析 | ❌ | ❌ | ❌ |
| 富文本编辑 | ✅ Tiptap备课/出题/反馈) | ❌ | ❌ | ❌ |
| 多子女切换 | ❌ | ❌ | ✅ | ❌ |
| 用户管理 | ❌ | ❌ | ❌ | ✅ |
| 角色权限配置 | ❌ | ❌ | ❌ | ✅ |
| 平台监控 | ❌ | ❌ | ❌ | ✅ |
## 4.2 L1 导航菜单差异
| 端 | 菜单项(视口) |
| -------------- | -------------------------------------------------------------------------------------------- |
| teacher-portal | Dashboard、班级管理、考试管理、作业管理、成绩查询、备课P5、AI 辅助P5、知识图谱P4 |
| student-portal | Dashboard、我的作业、我的考试、学情诊断P4、错题本P4、通知中心P5 |
| parent-portal | Dashboard、子女切换、成绩查看、作业查看、通知中心P5、通知偏好设置 |
| admin-portal | Dashboard、用户管理、角色管理、权限管理、视口配置、组织管理、平台监控 |
## 4.3 L2 路由表差异
### teacher-portal
| 路由 | 页面 | 权限 |
| ----------------------------- | -------------- | ------------------------ |
| `/teacher/dashboard` | 教师仪表盘 | `TEACHER_DASHBOARD_VIEW` |
| `/teacher/classes` | 班级列表 | `CLASSES_READ` |
| `/teacher/classes/:id` | 班级详情 | `CLASSES_READ` |
| `/teacher/classes/new` | 新建班级 | `CLASSES_CREATE` |
| `/teacher/exams` | 考试列表 | `EXAMS_READ` |
| `/teacher/exams/:id` | 考试详情 | `EXAMS_READ` |
| `/teacher/exams/new` | 新建考试 | `EXAMS_CREATE` |
| `/teacher/homework` | 作业列表 | `HOMEWORK_READ` |
| `/teacher/homework/:id/grade` | 批改作业 | `HOMEWORK_GRADE` |
| `/teacher/grades` | 成绩查询 | `GRADES_READ` |
| `/teacher/lesson-prep` | 备课P5 | `LESSON_PREP_VIEW` |
| `/teacher/ai-assist` | AI 辅助P5 | `AI_GENERATE` |
| `/teacher/knowledge-graph` | 知识图谱P4 | `CONTENT_READ` |
### student-portal
| 路由 | 页面 | 权限 |
| ------------------------------ | -------------- | ------------------------ |
| `/student/dashboard` | 学生仪表盘 | `STUDENT_DASHBOARD_VIEW` |
| `/student/homework` | 我的作业 | `HOMEWORK_READ_OWN` |
| `/student/homework/:id/submit` | 提交作业 | `HOMEWORK_SUBMIT` |
| `/student/exams` | 我的考试 | `EXAMS_READ_OWN` |
| `/student/exams/:id/take` | 作答考试 | `EXAMS_TAKE` |
| `/student/diagnostic` | 学情诊断P4 | `DIAGNOSTIC_READ_OWN` |
| `/student/weakness` | 错题本P4 | `WEAKNESS_READ_OWN` |
| `/student/notifications` | 通知中心P5 | `NOTIFICATION_READ_OWN` |
### parent-portal
| 路由 | 页面 | 权限 |
| ----------------------- | -------------- | --------------------------- |
| `/parent/dashboard` | 家长仪表盘 | `PARENT_DASHBOARD_VIEW` |
| `/parent/children` | 子女列表 | `PARENT_CHILDREN_VIEW` |
| `/parent/grades` | 子女成绩 | `GRADES_READ_CHILD` |
| `/parent/homework` | 子女作业 | `HOMEWORK_READ_CHILD` |
| `/parent/notifications` | 通知中心P5 | `NOTIFICATION_READ_OWN` |
| `/parent/preferences` | 通知偏好 | `PARENT_PREFERENCES_UPDATE` |
### admin-portal
| 路由 | 页面 | 权限 |
| --------------------- | ---------- | ----------------------- |
| `/admin/dashboard` | 管理仪表盘 | `ADMIN_DASHBOARD_VIEW` |
| `/admin/users` | 用户管理 | `IAM_USER_READ` |
| `/admin/users/new` | 新建用户 | `IAM_USER_CREATE` |
| `/admin/users/:id` | 用户编辑 | `IAM_USER_UPDATE` |
| `/admin/roles` | 角色管理 | `IAM_ROLE_READ` |
| `/admin/permissions` | 权限管理 | `IAM_PERMISSION_READ` |
| `/admin/viewports` | 视口配置 | `IAM_VIEWPORT_READ` |
| `/admin/organization` | 组织管理 | `ORG_MANAGE` |
| `/admin/monitoring` | 平台监控 | `ADMIN_MONITORING_VIEW` |
## 4.4 L3 组件级差异
| 组件 | teacher | student | parent | admin |
| ---------------------------------- | -------------------- | ------------------ | ------------------ | -------------------- |
| `AppShell`(左栏+主区) | ✅ | ✅(复用 Shell | ✅(复用 Shell | ✅(复用 Shell |
| `RequirePermission` | ✅ | ✅ | ✅ | ✅ |
| `ErrorBoundary` | ✅ | ✅ | ✅ | ✅ |
| `Loading` / `Empty` | ✅ | ✅ | ✅ | ✅ |
| `DataTable` | ✅(班级/考试列表) | ✅(作业列表) | ✅(成绩列表) | ✅(用户列表) |
| `Form` | ✅(创建班级/考试) | ✅(提交作业) | ✅(通知偏好) | ✅(用户/角色 CRUD |
| `Chart` | ✅(班级成绩分布) | ✅(个人学情趋势) | ✅(子女成绩趋势) | ✅(平台监控) |
| `RichTextEditor`Tiptap | ✅(备课/出题/反馈) | ❌ | ❌ | ❌ |
| `ChildSwitcher` | ❌ | ❌ | ✅ | ❌ |
| `ExamTaking`(倒计时+自动保存) | ❌ | ✅ | ❌ | ❌ |
| `SSEViewer`AI 流式) | ✅ | ❌ | ❌ | ❌ |
| `UserManagementTable` | ❌ | ❌ | ❌ | ✅ |
| `RolePermissionMatrix` | ❌ | ❌ | ❌ | ✅ |
| `ViewportConfigEditor` | ❌ | ❌ | ❌ | ✅ |
| `PlatformMonitor`Grafana embed | ❌ | ❌ | ❌ | ✅ |
## 4.5 L4 数据层差异
| 端 | 主要数据来源 | 缓存策略 |
| -------------- | -------------------------------------------------------- | --------------------------------- |
| teacher-portal | teacher-bff聚合 iam + core-edu + content + data-ana | 5-30s 短缓存 |
| student-portal | student-bff聚合 iam + core-edu + data-ana | 5-30s 短缓存,作业列表 30s |
| parent-portal | parent-bff聚合 iam + core-edu + data-ana含子女关联 | 5-30s 短缓存,子女切换 invalidate |
| admin-portal | iam 直连 + teacher-bff 复用 | 5min 长缓存(管理数据低频变) |
---
# 与其他模块的交互点(契约清单)
| 方向 | 对方服务 | 协议 | 接口/事件 | 用途 | 阶段 |
| ------ | ------------ | ------------------ | --------------------------------------------------- | ---------------------------- | ---- |
| 调用 | api-gateway | HTTP/REST | `/api/v1/*` 代理 | 全部业务请求 | P1+ |
| 调用 | push-gateway | WebSocket | `ws://push-gateway/ws` | 实时推送 | P5 |
| 调用 | ai | SSE | `GET /api/v1/ai/generate-questions` | AI 流式出题 | P5 |
| 被调用 | — | — | — | 前端不暴露接口给其他服务 | — |
| 消费 | teacher-bff | HTTP经 Gateway | `GET /teacher/viewports` 等 | 教师场景聚合 | P2+ |
| 消费 | student-bff | HTTP经 Gateway | `GET /student/viewports` 等 | 学生场景聚合 | P3+ |
| 消费 | parent-bff | HTTP经 Gateway | `GET /parent/viewports` 等 | 家长场景聚合 | P4+ |
| 消费 | iam | HTTP经 Gateway | `/iam/*` | 登录/权限/视口/用户管理 | P2+ |
| 消费 | core-edu | HTTP经 Gateway | `/classes/*` `/exams/*` `/homework/*` `/grades/*` | 教学核心 | P2+ |
| 消费 | content | HTTP经 Gateway | `/textbooks/*` `/knowledge-points/*` `/questions/*` | 内容资源 | P4+ |
| 消费 | data-ana | HTTP经 Gateway | `/analytics/*` | 学情分析 | P4+ |
| 消费 | msg | HTTP经 Gateway | `/notifications/*` | 通知中心 | P5+ |
| 依赖 | coord 维护 | — | `packages/shared-proto` | TS 类型(仅 contracts 部分) | P1+ |
| 依赖 | coord 维护 | — | `packages/shared-ts`(待建) | ApiClient/Logger/通用工具 | P2+ |
| 依赖 | ai07 维护 | — | `packages/ui-tokens`(待建) | 三层设计令牌 | P2+ |
| 依赖 | ai07 维护 | — | `packages/ui-components`(待建) | shadcn + 共享组件 | P2+ |
| 依赖 | ai07 维护 | — | `packages/hooks`(待建) | usePermission/useAuth 等 | P2+ |
| 依赖 | coord 维护 | — | `packages/contracts`(待建) | Permissions 常量 + 类型 | P2+ |
> **proto 不直接消费**:前端不调用 gRPCBFF 把 gRPC 聚合为 REST/GraphQL 暴露给前端。前端仅消费 `packages/contracts/src/permissions.ts` 中的权限点常量TS 文件,非 proto 生成)。
---
# 风险与假设
## 8.1 假设
1. **假设 coord 建立 `packages/shared-ts`、`packages/contracts`**:包含 ApiClient、Logger、Permissions 常量、通用类型。若 coord 未建立ai07 自行在 `apps/teacher-portal/src/shared/` 内实现,后续提取到 packages。
2. **假设 ai02 iam 提供 `GET /iam/effective-permissions`**:返回 `{ permissions, viewports, dataScope }`。当前已实现known-issues §2.3 iam
3. **假设 ai03 teacher-bff 提供 `GET /teacher/viewports`**:返回 L1 导航视口。当前已实现。
4. **假设 ai03 core-edu classes 模块维持 `ActionState` 响应结构**:前端 API 请求层依赖此契约。
5. **假设 Next.js 14+ Module Federation 2.0 稳定**`@module-federation/nextjs-mf` 在 Next.js App Router 下可用。若不稳定,降级为 4 端独立部署 + 各自 Shell重复实现 AppShell
## 8.2 技术风险
| 风险 | 影响 | 缓解 |
| ------------------------ | ----------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| MF SSR 对齐复杂 | Remote 在 SSR 时需 Shell 提供上下文 | 优先 CSRSSR 仅用于首屏 dashboardMF 2.0 支持 SSR |
| 共享依赖版本漂移 | Remote 与 Shell 的 react/react-dom 版本不一致导致运行时错误 | MF `shared.singleton: true` + CI 检查版本对齐 |
| Token 刷新竞态 | 多请求同时 401 触发多次 refresh | ApiClient 全局单例 + refresh promise 复用 |
| 权限缓存陈旧 | 角色变更后前端 5min 内仍用旧权限 | iam 角色变更发 Kafka 事件 → msg 推送 WebSocket → 前端 invalidate |
| 设计令牌迁移破坏现有样式 | teacher-portal 现有硬编码令牌迁移到三层模型后样式漂移 | 灰度迁移:先建 ui-tokens 包teacher-portal 引入但不删除旧 globals.css验证后切换 |
| 4 端独立部署运维成本 | 4 个 Next.js 实例 = 4 倍内存 | Shell + 3 Remote 共享 node_modulesMF 运行时共享),实际内存增量 < 2x |
| TanStack Query 缓存膨胀 | 长时间使用后缓存项过多 | `gcTime` 5min + `staleTime` 按数据类型分级 |
## 8.3 未决设计决策(需 coord 仲裁)
1. **packages 归属**`ui-tokens` / `ui-components` / `hooks` 是 ai07 维护还是 coord 维护建议ai07 维护前端专属coord 仅维护 `shared-ts` / `contracts`(跨语言/跨服务)。
2. **GraphQL vs REST**004 §11.3 提到 BFF GraphQL Yoga + DataLoader但当前 teacher-bff 实现为 REST。前端 API 请求层是否需要 GraphQL clienturql/apollo建议P2-P3 用 RESTP4 起若 BFF 切 GraphQL 再引入 urql。
3. **i18n key 命名**`iam.error.IAM_INVALID_CREDENTIALS` 还是 `error.iam.invalid_credentials`?建议:`error.{{service}}.{{code_snake_case}}`,与错误码前缀对齐。
4. **MF 暴露粒度**Shell 暴露整个 AppShell 还是暴露更细粒度的组件Sidebar、Header、Content建议暴露 AppShell 整体 + 各 Remote 自行决定内部布局。
---
# coord 交叉审查所需信息
## 9.1 端口矩阵4 端)
| 端 | dev 端口 | 生产端口 | 备注 |
| -------------- | -------- | -------- | ---------- |
| teacher-portal | 3000 | 3000 | Shell 宿主 |
| student-portal | 3001 | 3001 | Remote |
| parent-portal | 3002 | 3002 | Remote |
| admin-portal | 3003 | 3003 | Remote |
> 与 [full-stack-runbook](../standards/full-stack-runbook.md) 端口矩阵对齐3000-3003 前端3001-3003 已被 Grafana3030/其他服务避让。
## 9.2 依赖的共享包(需 coord 建立)
| 包 | 路径 | 维护方 | 内容 |
| --------------- | ------------------------- | ------------ | ------------------------------------------------- |
| `shared-ts` | `packages/shared-ts/` | coord | ApiClient、Logger、通用工具 |
| `contracts` | `packages/contracts/` | coord | Permissions 常量、ActionState 类型、UserInfo 类型 |
| `ui-tokens` | `packages/ui-tokens/` | ai07建议 | 三层设计令牌 |
| `ui-components` | `packages/ui-components/` | ai07建议 | shadcn + ErrorBoundary + RequirePermission |
| `hooks` | `packages/hooks/` | ai07建议 | usePermission、useAuth、useViewports |
## 9.3 依赖的后端契约(需对应 AI 确认)
| 契约 | 提供方 | 当前状态 |
| ------------------------------------------------------------------ | ---------------------------- | ------------------- |
| `POST /iam/login``GET /iam/effective-permissions``GET /iam/me` | ai02 iam | ✅ 已实现 |
| `GET /teacher/viewports``GET /teacher/dashboard` | ai03 teacher-bff | ✅ 已实现 |
| `/classes/*` CRUD | ai03 core-edu | ✅ 已实现 |
| `/exams/*` `/homework/*` `/grades/*` | ai03 core-edu | ✅ 已实现P3 |
| `/textbooks/*` `/knowledge-points/*` `/questions/*` | ai05 content | ✅ 已实现P4 |
| `/analytics/*` | ai06 data-ana | ✅ 已实现P4 CDC |
| `GET /student/viewports` 等 | ai04 student-bff | 📐 待 ai04 设计 |
| `GET /parent/viewports` 等 | ai04 parent-bff | 📐 待 ai04 设计 |
| `/notifications/*` + WebSocket 推送 | ai05 msg + ai01 push-gateway | 📐 待 P5 |
| `GET /ai/generate-questions`SSE | ai06 ai | 📐 待 P5 |
## 9.4 错误码前缀(前端 i18n 路由依赖)
前端不产生错误码,仅消费。需各服务确认错误码前缀不重叠:
| 前缀 | 服务 | 状态 |
| ----------------------------------------------- | ---------------- | -------------------- |
| `IAM_` | iam | ✅ ai02 已用 |
| `CLASSES_` | core-edu/classes | ✅ 已用 |
| `EXAMS_` / `HOMEWORK_` / `GRADES_` | core-edu | ⚠️ 待 ai03 确认 |
| `CONTENT_` | content | ⚠️ 待 ai05 确认 |
| `MSG_` | msg | ⚠️ 待 ai05 确认 |
| `AI_` | ai | ⚠️ 待 ai06 确认 |
| `BFF_TEACHER_` / `BFF_STUDENT_` / `BFF_PARENT_` | 3 BFF | ⚠️ 待 ai03/ai04 确认 |
| `GW_` | api-gateway | ✅ ai01 已用 |
| `NETWORK_` | 前端 | ai07 自有 |
## 9.5 不产生 Kafka 事件
前端不发布/消费 Kafka 事件。WebSocket 推送由 push-gateway 消费 Kafka 转发。
---
# 实施路线ai07 自用)
## P2 收尾teacher-portal 审计对齐)
1.`packages/ui-tokens/`(三层设计令牌)+ `packages/ui-components/`ErrorBoundary/RequirePermission/Loading/Empty+ `packages/hooks/`usePermission/useAuth
2. teacher-portal 引入 TanStack Query + Zustand + nuqs + react-hook-form
3. 抽取 `lib/api.ts` 统一 API 请求层
4. AppShell 改用 `usePermission()`,删除 `user.roles.join(", ")` 硬编码
5. globals.css / tailwind.config.js 迁移到 ui-tokens 三层令牌
6. 引入 next-intl + i18n key 路由
7. 引入 ESLint flat config 自定义规则no-hardcoded-fonts / design-tokens
8. 补 ErrorBoundary + /api/health route
9. 配置 next.config.js Module FederationShell 角色)
10. 补 Vitest 单测 + Playwright E2E覆盖率 ≥ 80%
## P3student-portal
1.`apps/student-portal/`Remote 角色)
2. 配置 MFexposes pagesremotes teacher
3. 实现 Dashboard + 我的作业 + 提交作业 + 我的考试 + 作答考试
4. 复用 Shell 的 AppShell + 共享组件
5. SSE 接入(考试作答自动保存)
## P4parent-portal
1.`apps/parent-portal/`Remote 角色)
2. 实现 Dashboard + 子女切换 + 成绩查看 + 通知偏好
3. 多子女状态管理Zustand slice
## P5推送 + AI 接入)
1. teacher-portal 接入 WebSocketpush-gateway
2. teacher-portal AI 辅助出题SSE + Tiptap
3. student/parent-portal 接入通知推送
## P6admin-portal + 硬化)
1.`apps/admin-portal/`Remote 角色)
2. 实现用户/角色/权限/视口/组织/监控管理
3. Web Vitals + OTel browser SDK 接入
4. A11y WCAG 2.2 AA 审计
5. 性能优化MF shared 单例验证、bundle 分析)
---
**AI Agent**: ai07 (teacher-portal / student-portal / parent-portal / admin-portal)
**Branch**: docs/teacher-portal-stage1-stage2-design-ai07
**Coordinator**: coord-ai