teacher-portal / 4 端微前端架构设计
版本:1.0
日期:2026-07-09
AI Agent:ai07(前端 4 端:teacher-portal / student-portal / parent-portal / admin-portal)
阶段:阶段 1(理解确认书)+ 阶段 2(模块架构设计文档)
关联文档:
本文档合并 4 端的设计,因 ai07 单一负责全部 4 端,Module Federation shell + remote 架构需统一设计,4 端共享组件库和权限体系。当前仓库仅 teacher-portal 已实现(P1 测试页 + P2 骨架),student/parent/admin-portal 待建。
目录
- 阶段 1:模块理解确认书(4 端)
- teacher-portal 现状审计(对齐黄金模板)
- 阶段 2:模块架构设计文档
- 4 端差异化对比表
- 与其他模块的交互点(契约清单)
- 风险与假设
- coord 交叉审查所需信息
阶段 1:模块理解确认书(4 端)
1.1 我在架构中的位置
| 维度 |
teacher-portal |
student-portal |
parent-portal |
admin-portal |
| 层级 |
L2 微前端层 |
L2 微前端层 |
L2 微前端层 |
L2 微前端层 |
| MF 角色 |
Shell 宿主(主应用) |
Remote(子应用) |
Remote(子应用) |
Remote(子应用) |
| 上游 |
浏览器(教师 / 教导主任 / 教研组长) |
浏览器(学生) |
浏览器(家长) |
浏览器(系统/校管理员) |
| 下游(同步) |
api-gateway(REST,经 Next.js rewrites 代理) |
api-gateway |
api-gateway |
api-gateway |
| 下游(推送) |
push-gateway(WebSocket/SSE,P5) |
push-gateway |
push-gateway |
— |
| BFF 对接 |
teacher-bff(GraphQL 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 端共享同一 Shell(teacher-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-edu(classes 模块) |
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/* |
ai(SSE 流式) |
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):
错误码前缀按服务名大写(如 IAM_、CORE_EDU_、CONTENT_、MSG_、AI_、BFF_、GW_)。前端 API 请求层根据 error.code 前缀路由到对应的 i18n key。
1.3.3 推送契约(P5)
| 端 |
协议 |
场景 |
| teacher-portal |
WebSocket(push-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-a11y(error 级) |
WCAG 2.2 AA |
| 字体 |
Inter(sans)/ Fraunces(serif)/ JetBrains Mono(mono) |
next/font/google 加载,CSS 变量暴露 |
1.5 我的阶段归属
| 端 |
阶段 |
当前状态 |
依赖上游阶段 |
| teacher-portal |
P2 |
✅ 已实现 P1 测试页 + P2 骨架(登录/AppShell/Dashboard/classes CRUD);⚠️ 待审计对齐黄金模板 + 引入 MF + 共享组件库 |
P1(api-gateway + classes + iam) |
| student-portal |
P3 |
📐 需设计(待 core-edu + student-bff 就绪) |
P3(core-edu + student-bff) |
| parent-portal |
P4 |
📐 需设计(待 parent-bff + data-ana 就绪) |
P4(parent-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 + Sentry(P6) |
⚠️ 仅 console.error |
| metrics |
prom-client /metrics |
前端 Web Vitals → Gateway 上报 |
❌ 缺失 |
| tracer |
OTel SDK |
前端 OTel browser SDK(P6) |
❌ 缺失 |
| /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 |
| GlobalErrorFilter(ErrorBoundary) |
❌ |
无 React ErrorBoundary |
| 设计令牌三层 |
❌ |
硬编码在 globals.css(:root 变量)+ tailwind.config.js(hex 字面量) |
| 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 现有文件清单
2.3 主要违规点(必须在 P2 收尾或 P3 起步时修复)
- 权限硬编码:AppShell.tsx:143
user.roles.join(", ") 违反 project_rules §3.8,必须改为 usePermission().hasPermission()
- 设计令牌硬编码:globals.css:6-13 与 tailwind.config.js:7-19 出现
hsl(...) 字面量与 'Fraunces'/'Inter' 字面量,违反 project_rules §3.10
- 无统一 API 请求层:4 个 page.tsx 重复
authHeaders() + fetch + try/catch + setError,必须抽取到 lib/api.ts
- 无权限 Hook:缺少
usePermission().hasPermission(),无法做 L3 组件级视口控制
- 无 ErrorBoundary:React 渲染异常会白屏
- 无 5 层状态管理:登录态用 localStorage(L3),但无 TanStack Query(L2)导致每页重复 fetch
- 字体名硬编码:layout.tsx:3-7 直接 import
Inter/Fraunces/JetBrains_Mono,应改为 var(--font-family-sans/serif/mono)
阶段 2:模块架构设计文档
3.1 模块内部分层图(4 端统一 MF 架构)
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)
Remote 端配置对称:name: 'student_app',exposes: { './pages': './src/pages' },remotes: { teacher: 'teacher_app@...' }。
3.2 领域模型(前端视角)
前端不持有业务聚合根,仅持有"视图模型"(ViewModel)和"会话状态"。
3.2.1 会话状态(Session)
存储:Zustand sessionSlice(L3)+ localStorage 持久化(刷新恢复)+ TanStack Query 缓存 ['session'](L2)。
3.2.2 视口模型(Viewport)
来源:GET /api/v1/{scope}/viewports(BFF 聚合 iam 视口配置)。AppShell 按 scope 过滤渲染对应 portal 的导航。
3.2.3 权限模型(Permission)
来源:GET /api/v1/iam/effective-permissions → { permissions, viewports, dataScope }。Redis 缓存 5min(iam 侧),前端 TanStack Query 缓存 5min,角色变更主动 invalidate。
3.3 数据模型(前端)
前端无数据库,仅有缓存层:
| 数据类型 |
存储 |
TTL |
失效策略 |
| Session(token + user) |
localStorage + Zustand |
access 15min / refresh 7day |
401 自动 refresh,refresh 失败跳登录 |
| 权限列表 |
TanStack Query cache |
5min |
角色变更事件 invalidate |
| 视口列表 |
TanStack Query cache |
5min |
同上 |
| 班级/年级列表 |
TanStack Query cache |
5min |
staleTime 5min,mutation 后 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)
职责:
- 自动注入
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 约定
3.5 事件设计
前端不发布 Kafka 事件,仅消费 WebSocket 推送(P5)和 Server-Sent Events(AI 流式)。
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 辅助出题)
前端用 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-ts,ai07 负责调用)。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
3.6.4 Metrics(Web 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 Tracer(OTel browser SDK,P6)
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 |
全局 Modal(ModalRoot + 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 共享 Hooks(packages/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() |
全局 toast(Zustand ui-store) |
3.9 设计令牌三层(packages/ui-tokens/,待建立)
强制规则(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 不直接消费:前端不调用 gRPC,BFF 把 gRPC 聚合为 REST/GraphQL 暴露给前端。前端仅消费 packages/contracts/src/permissions.ts 中的权限点常量(TS 文件,非 proto 生成)。
风险与假设
8.1 假设
- 假设 coord 建立
packages/shared-ts、packages/contracts:包含 ApiClient、Logger、Permissions 常量、通用类型。若 coord 未建立,ai07 自行在 apps/teacher-portal/src/shared/ 内实现,后续提取到 packages。
- 假设 ai02 iam 提供
GET /iam/effective-permissions:返回 { permissions, viewports, dataScope }。当前已实现(known-issues §2.3 iam)。
- 假设 ai03 teacher-bff 提供
GET /teacher/viewports:返回 L1 导航视口。当前已实现。
- 假设 ai03 core-edu classes 模块维持
ActionState 响应结构:前端 API 请求层依赖此契约。
- 假设 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 提供上下文 |
优先 CSR,SSR 仅用于首屏 dashboard;MF 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_modules(MF 运行时共享),实际内存增量 < 2x |
| TanStack Query 缓存膨胀 |
长时间使用后缓存项过多 |
gcTime 5min + staleTime 按数据类型分级 |
8.3 未决设计决策(需 coord 仲裁)
- packages 归属:
ui-tokens / ui-components / hooks 是 ai07 维护还是 coord 维护?建议:ai07 维护(前端专属),coord 仅维护 shared-ts / contracts(跨语言/跨服务)。
- GraphQL vs REST:004 §11.3 提到 BFF GraphQL Yoga + DataLoader,但当前 teacher-bff 实现为 REST。前端 API 请求层是否需要 GraphQL client(urql/apollo)?建议:P2-P3 用 REST,P4 起若 BFF 切 GraphQL 再引入 urql。
- i18n key 命名:
iam.error.IAM_INVALID_CREDENTIALS 还是 error.iam.invalid_credentials?建议:error.{{service}}.{{code_snake_case}},与错误码前缀对齐。
- 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 端口矩阵对齐:3000-3003 前端,3001-3003 已被 Grafana(3030)/其他服务避让。
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 审计对齐)
- 建
packages/ui-tokens/(三层设计令牌)+ packages/ui-components/(ErrorBoundary/RequirePermission/Loading/Empty)+ packages/hooks/(usePermission/useAuth)
- teacher-portal 引入 TanStack Query + Zustand + nuqs + react-hook-form
- 抽取
lib/api.ts 统一 API 请求层
- AppShell 改用
usePermission(),删除 user.roles.join(", ") 硬编码
- globals.css / tailwind.config.js 迁移到 ui-tokens 三层令牌
- 引入 next-intl + i18n key 路由
- 引入 ESLint flat config 自定义规则(no-hardcoded-fonts / design-tokens)
- 补 ErrorBoundary + /api/health route
- 配置 next.config.js Module Federation(Shell 角色)
- 补 Vitest 单测 + Playwright E2E(覆盖率 ≥ 80%)
P3(student-portal)
- 建
apps/student-portal/(Remote 角色)
- 配置 MF(exposes pages,remotes teacher)
- 实现 Dashboard + 我的作业 + 提交作业 + 我的考试 + 作答考试
- 复用 Shell 的 AppShell + 共享组件
- SSE 接入(考试作答自动保存)
P4(parent-portal)
- 建
apps/parent-portal/(Remote 角色)
- 实现 Dashboard + 子女切换 + 成绩查看 + 通知偏好
- 多子女状态管理(Zustand slice)
P5(推送 + AI 接入)
- teacher-portal 接入 WebSocket(push-gateway)
- teacher-portal AI 辅助出题(SSE + Tiptap)
- student/parent-portal 接入通知推送
P6(admin-portal + 硬化)
- 建
apps/admin-portal/(Remote 角色)
- 实现用户/角色/权限/视口/组织/监控管理
- Web Vitals + OTel browser SDK 接入
- A11y WCAG 2.2 AA 审计
- 性能优化(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