模块理解确认书 — parent-portal
AI:ai15(TS/React · 家长场景域前端 remote)
阶段:阶段 1 交付物(v2 — ai15 接管审计与补全版)
初版日期:2026-07-09(ai07 起草)
审计日期:2026-07-09(ai15 修订:端口、所有权、长远架构遗漏补全)
关联:004 架构影响地图 §1.1a/1.1b/§5.4、AI 分配方案 §3.2 ai15、pending-features P4、known-issues §2.12、teacher-portal 阶段1、teacher-portal 阶段2、parent-bff 阶段1
审计修订摘要(ai15 → ai07 初稿):
- 端口修订:3002 → 4002(004 §1.2 强制 4 端 4000-4003,project memory 硬约束)
- MF URL 修订:teacher/student/parent/admin 端 URL 全部从 3000-3003 修订为 4000-4003
- 所有权修订:ai07 → ai15(ai-allocation.md §3.2)
- 遗漏补全:i18n 策略、移动端/PWA、多子女边界场景、通知偏好数据模型、隐私合规(COPPA/FERPA)、测试策略分层、韧性模式、性能预算、CSP/前端安全、跨标签同步、API 版本演进、未来扩展铺垫、长远愿景
- 新增章节:§11 多子女边界场景、§12 隐私合规、§13 测试策略、§14 性能与预算、§15 前端安全、§16 跨标签与跨设备同步、§17 i18n 深化、§18 移动端与 PWA、§19 长远愿景与演进路径
1. 我在架构中的位置
- 层级:L2 微前端层(004 §3.1 六层架构中的前端层)
- MF 角色:Remote 子应用,挂载到 teacher-portal Shell
- 上游(谁调用我):浏览器(家长)— 含桌面 Chrome/Edge/Safari、移动端 iOS Safari/Android Chrome
- 下游(同步):api-gateway(REST,经 Next.js
rewrites 代理 /api/v1/*)
- 下游(推送,P5):push-gateway(WebSocket,含 SSE 降级)
- BFF 对接:parent-bff(ai04 设计,端口 3010)
- 通信方式:HTTP/REST(前端→Gateway)+ WebSocket(前端→push-gateway,P5)+ SSE 降级(P5+)
- 不直连:前端不直连任何业务服务或 BFF 后端实例,全部经 api-gateway 代理
说明:
- 通过
next.config.js 的 rewrites 将 /api/v1/* 代理到 api-gateway
- MF 架构下,parent-portal 作为 Remote 子应用挂载到 teacher-portal Shell,复用 Shell 的 AppShell、共享依赖、权限 Hook、API 请求层
- 不独立提供 RootLayout / 登录页 / 字体加载 / 令牌初始化,全部由 Shell 提供
- 与 teacher-portal 共享会话状态(Session)、视口(Viewport)、权限(Permission)三个核心模型
2. 我的限界上下文
2.1 我负责的聚合 / 实体(前端视图模型)
- 多子女切换、通知偏好、学情查看、成绩通知(家长场景域前端视图)
- 会话状态(Session)、视口(Viewport)、权限(Permission)—— 与 teacher-portal 共享
- 多子女状态(ChildSwitcher)—— 家长端特有
2.2 业务领域
2.3 不负责
- 教师沟通(归 teacher-portal)
- 学生作答(归 student-portal)
- 用户/角色/权限 CRUD(归 admin-portal)
- 成绩录入(归 teacher-portal,家长端仅查看)
2.4 数据范围
- DataScope L0(仅子女)—— 家长只能查看自己子女的数据,不能跨家庭
3. 我与外部的契约
3.1 消费的后端 API(经 api-gateway 代理)
| 路径前缀 |
下游 BFF/服务 |
关键端点 |
/api/v1/parent/* |
parent-bff |
GET /parent/viewports、GET /parent/dashboard、GET /parent/children、POST /parent/children/:childId/select、GET /parent/children/:childId/exams、/homework、/grades、/analytics/trend、/analytics/weakness、GET /parent/notifications、PUT /parent/notification-preferences |
/api/v1/iam/* |
iam |
POST /iam/login、GET /iam/me、GET /iam/effective-permissions、GET /iam/children(P0 阻塞,待 ai02 补全) |
/api/v1/notifications/* |
msg |
通知中心(P5) |
parent-bff 聚合 iam + core-edu + data-ana + msg,对外暴露家长场景的统一接口(子女关联、视口、学情聚合)。
P0 阻塞项(来自 parent-bff §7.1):iam 缺失"家长-学生关联查询"接口(GetChildrenByParent proto + GET /iam/children REST + iam_student_guardians 表三缺失)。在 ai02 补全前,parent-portal 的多子女场景无法落地,仅能假设单子女硬编码 childId 进行开发调试。
3.1.1 API 契约版本演进策略
| 版本信号 |
携带位置 |
演进规则 |
| 主版本 |
URL 路径 /api/v1/* → /api/v2/* |
破坏性变更升主版本,parent-portal 同时支持 v1 + v2 至少 1 个迭代周期(4 周),通过 Feature Flag 切换 |
| 子版本 |
响应头 X-API-Version: 2026-07-09 |
向后兼容字段新增,前端忽略未知字段(Zod 默认行为) |
| Deprecation |
响应头 Deprecation: true + Sunset: <date> |
前端收到 Deprecation 头后上报埋点,跟踪使用率,确认 < 1% 后移除前端调用 |
| 字段裁剪 |
请求头 X-Fields: grades[].id,grades[].score |
父 portal 在低带宽移动场景按需裁剪(GraphQL 风格,BFF REST 透传支持) |
parent-portal 不主动驱动 API 版本升级;契约变更由 coord 协调各业务 AI 落地。parent-portal 仅负责消费侧的兼容与迁移。
3.2 统一响应契约
所有后端响应遵循 ActionState 结构(迁移指南 §7.5):
错误码前缀按服务名大写(如 IAM_、CORE_EDU_、GRADES_、HOMEWORK_、BFF_PARENT_、GW_、NETWORK_)。前端 API 请求层根据 error.code 前缀路由到对应的 i18n key。
3.3 推送契约(P5)
| 协议 |
场景 |
| WebSocket(push-gateway) |
子女成绩发布、教师沟通、学校通知 |
3.4 proto 不直接消费
前端不调用 gRPC,BFF 把 gRPC 聚合为 REST 暴露给前端。前端仅消费 packages/contracts/src/permissions.ts 中的权限点常量(TS 文件,非 proto 生成)。
4. 我的技术栈
| 维度 |
选型 |
说明 |
| 框架 |
Next.js 14+(App Router) |
与 teacher-portal 一致,MF Remote 角色 |
| 语言 |
TypeScript 5.5+(strict) |
沿用 tsconfig.base.json |
| 微前端 |
Module Federation 2.0(@module-federation/nextjs-mf) |
parent-portal = Remote |
| 样式 |
Tailwind CSS 3.4+ |
配合设计令牌三层模型(与 teacher-portal 共享) |
| UI 组件库 |
shadcn/ui(迁移指南 §7.2) |
复用 Shell 暴露的 packages/ui-components/ |
| 状态管理 L1 URL |
nuqs |
可分享、可刷新状态 |
| 状态管理 L2 Server |
TanStack Query v5 |
服务端数据缓存、重试、乐观更新 |
| 状态管理 L3 Client Business |
Zustand slice |
客户端业务状态(含 ChildSwitcher 多子女状态) |
| 状态管理 L4 Global UI |
Zustand ui-store + ModalRoot |
全局 UI 状态(复用 Shell 暴露) |
| 状态管理 L5 Form |
react-hook-form + zodResolver |
表单状态(通知偏好设置) |
| 富文本 |
N/A |
家长端不使用 Tiptap |
| 图表 |
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) |
由 Shell RootLayout 加载,parent-portal 仅消费 CSS 变量 |
5. 我的阶段归属
- 阶段:P4
- 当前状态:📐 需设计(待 parent-bff + data-ana 就绪),apps/parent-portal/ 目录为空(待建)
- 依赖上游阶段:P4(parent-bff + data-ana)
6. 我需要对齐的黄金模板项(对照 classes 服务)
前端无 @RequirePermission 装饰器(后端概念),对齐项改造为前端等价物。parent-portal 与 teacher-portal 共享前端等价物实现。
| 对齐项 |
classes(后端黄金模板) |
parent-portal 前端等价 |
当前状态 |
| 权限校验 |
@RequirePermission(Permissions.XXX) |
usePermission().hasPermission("XXX") Hook + <RequirePermission> 组件 |
❌ 待建 |
| 错误码前缀统一 |
CLASSES_*、IAM_* |
API 请求层根据 error.code 前缀路由 i18n |
❌ 待建 |
| logger |
pino |
前端 console + Sentry(P6) |
❌ 待建 |
| 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 |
❌ 待建 |
| 优雅关闭 |
SIGTERM handler |
Next.js 无长连接,无需 |
✅ N/A |
| 测试覆盖率 ≥ 80% |
Vitest |
Vitest + @testing-library/react + Playwright E2E |
❌ 待建 |
| Dockerfile 多阶段构建 |
builder + runtime |
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 |
❌ 待建 |
| A11y 工具集 |
— |
useA11yId / mergeA11yProps / describeInput / focus-trap |
❌ 待建 |
附:parent-portal 现状审计(对齐黄金模板)
审计表
| 维度 |
状态 |
说明 |
| 权限装饰器(前端等价 usePermission) |
❌ |
待建,复用 Shell 暴露的 usePermission Hook |
| 错误码前缀 |
❌ |
待建,复用 Shell 暴露的 ApiClient |
| logger |
❌ |
待建,复用 packages/shared-ts/src/logger.ts |
| metrics |
❌ |
待建,Web Vitals 上报 |
| tracer |
❌ |
待建,OTel browser SDK(P6) |
| /healthz |
❌ |
待建,Next.js Route Handler |
| /readyz |
❌ |
待建 |
| 优雅关闭 |
✅ N/A |
Next.js 无长连接 |
| 测试覆盖率 |
❌ |
0%,无测试文件(待建) |
| Dockerfile 多阶段 |
❌ |
待建(builder + runtime) |
| Zod 输入验证 |
❌ |
待建(react-hook-form + zodResolver) |
| GlobalErrorFilter(ErrorBoundary) |
❌ |
待建,复用 Shell 暴露的 ErrorBoundary |
| 设计令牌三层 |
❌ |
待建,复用 packages/ui-tokens |
| A11y 工具集 |
❌ |
待建,复用 Shell 暴露的 A11y 工具集 |
| Module Federation 配置 |
❌ |
待建(Remote 角色) |
| 5 层状态管理 |
❌ |
待建(含 ChildSwitcher 多子女 Zustand slice) |
| 共享组件库 |
❌ |
待建(复用 Shell 暴露 + 新增 ChildSwitcher) |
| i18n |
❌ |
待建(next-intl) |
| API 请求层 |
❌ |
待建,复用 Shell 暴露的 ApiClient |
| ESLint flat config 自定义规则 |
❌ |
待建,复用 teacher-portal 配置 |
现有文件清单
parent-portal 当前为空目录,所有维度均为 ❌ 待建状态。基础设施(Shell 暴露的 AppShell、共享依赖、权限 Hook、API 请求层、设计令牌、UI 组件、A11y 工具集)由 teacher-portal Shell 在 P2 收尾时建立,parent-portal 在 P4 启动时直接复用。
7. L1 导航菜单(视口)
家长端 L1 导航菜单由 parent-bff 通过 GET /parent/viewports 返回,AppShell 按 scope: 'parent' 过滤渲染:
- Dashboard(家长仪表盘)
- 子女切换
- 成绩查看
- 作业查看
- 通知中心(P5)
- 通知偏好设置
8. L2 路由表
| 路由 |
页面 |
权限 |
/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 |
L3 组件级视口用 <RequirePermission perm="PARENT_PREFERENCES_UPDATE"><Button>保存</Button></RequirePermission>。
9. L3 组件级差异(parent-portal 特有)
9.1 复用 Shell 暴露的组件
- AppShell(左栏导航 + 主内容区)
- RequirePermission(L3 组件级视口控制)
- ErrorBoundary(React 渲染异常兜底)
- Loading(骨架屏)
- Empty(空态)
- DataTable(表格)
- Form(react-hook-form + zodResolver 封装)
- Chart(recharts 封装)
9.2 parent-portal 特有组件(完整清单)
| 组件 |
用途 |
来源 |
是否 MF 暴露 |
ChildSwitcher |
多子女切换组件(顶部 Tab / 移动端下拉),切换后 invalidate 子女相关查询 |
新建 |
✅ ./ChildSwitcher |
ChildSummaryCard |
单个子女的概览卡片(头像/姓名/年级/今日作业数/近期成绩趋势缩略图) |
新建 |
❌ 内部使用 |
ParentDashboard |
家长仪表盘容器(多子女并列卡片 + 全家聚合统计 + 待办提醒) |
新建 |
❌ 内部使用 |
ChildGradeChart |
子女成绩趋势图(折线 + 班级均分对比 + 区间填充)+ 多子女对比模式 |
新建 |
❌ 内部使用 |
AttendanceCalendar |
出勤日历热力图(按月网格展示出勤/缺勤/迟到/请假,全年概览) |
新建 |
❌ 内部使用 |
NotificationFeed |
通知流(按子女×类型×已读筛选,支持批量已读、跳转、置顶) |
新建 |
❌ 内部使用 |
PreferenceForm |
通知偏好设置表单(子女×事件×渠道三维矩阵,react-hook-form + Zod 校验) |
新建 |
❌ 内部使用 |
ChildComparisonView |
多子女横向对比视图(成绩/出勤/作业完成率并排表格,P5+) |
新建 |
❌ 内部使用 |
EmptyChildState |
无子女绑定引导(CTA 跳转绑定流程,含客服联系方式) |
新建 |
❌ 内部使用 |
MultiChildTabBar |
多子女 Tab 栏(≤3 子女用 Tab,>3 子女用下拉,移动端友好) |
新建 |
❌ 内部使用 |
9.3 不使用的组件
- RichTextEditor(Tiptap)—— 家长端不编辑富文本
- ExamTaking —— 学生考试专用
- SSEViewer —— AI 流式响应查看器(教师端专用)
- UserManagementTable —— 管理员端专用
- LessonPlanEditor —— 教师备课专用
- KnowledgeGraphViewer —— 教师查看知识点图谱专用(家长端仅看诊断结论)
10. L4 数据层差异
| 维度 |
teacher-portal |
parent-portal |
| 主要数据源 |
teacher-bff |
parent-bff(聚合 iam + core-edu + data-ana + msg) |
| 缓存策略 |
5-30s 短缓存 |
5-30s 短缓存,子女切换 invalidate 子女相关查询 |
| 多子女状态 |
N/A |
ChildSwitcher Zustand slice,当前子女 ID 持久化到 localStorage |
| 跨标签同步 |
N/A |
BroadcastChannel API + Storage 事件同步当前子女 ID(见 §16) |
| 离线缓存 |
N/A |
P5+ PWA Service Worker 缓存最近查看的子女数据快照(见 §18) |
11. 多子女边界场景(家长端特有,必须覆盖)
家长端的核心复杂度来自多子女,必须在架构中预留所有边界场景的处理。
11.1 子女数量边界
| 场景 |
触发条件 |
前端处理 |
后端契约依赖 |
| 0 子女(未绑定) |
新注册家长或子女关系被解除 |
显示 EmptyChildState 引导页,CTA 跳转绑定流程;隐藏 dashboard/grades/homework 等业务路由 |
GET /parent/children 返回 [] |
| 1 子女 |
单子女家庭 |
不显示 MultiChildTabBar,直接进入业务页面;URL 不携带 ?childId= |
GET /parent/children 返回长度 1 数组 |
| 2-3 子女 |
多子女家庭(典型) |
MultiChildTabBar 显示 Tab 形式,默认选中最近查看的子女 |
同上 |
| 4-10 子女 |
大家庭或重组家庭 |
MultiChildTabBar 改为下拉选择器 + 头像缩略;Tab 栏超过 3 个时自动切换 |
同上 |
| >10 子女 |
校管理员或多监护人代管场景 |
强制下拉选择器 + 搜索框(按姓名/学号筛选);列表分页加载 |
GET /parent/children 支持分页 + 搜索 |
11.2 子女档案变更边界
| 场景 |
触发条件 |
前端处理 |
事件来源 |
| 子女被解绑 |
家长或管理员主动解绑 |
收到 WebSocket 事件 → invalidate children 列表 → 若当前选中子女被解绑,自动切换到第一个子女;若无子女则跳引导页 |
edu.identity.user.updated |
| 子女档案被归档/转学 |
学校主动操作 |
收到事件后该子女标识为"已离校",灰色显示但保留历史数据查看权限;不可选为当前子女 |
edu.identity.user.updated |
| 子女姓名/头像变更 |
学校维护档案 |
收到事件 → invalidate children 列表 → UI 自动刷新;不中断当前操作 |
edu.identity.user.updated |
| 当前子女被切到其他监护人 |
监护权变更 |
同"解绑"处理 |
edu.identity.user.updated |
| 新增子女绑定 |
家长新增绑定子女 |
收到事件 → invalidate children 列表 → 显示 toast 提示"已添加子女:XXX" → 不自动切换当前选中子女 |
edu.identity.user.created |
11.3 跨标签与跨设备同步边界
| 场景 |
触发条件 |
前端处理 |
| 同浏览器多标签切换子女 |
用户在 Tab A 切换子女 |
Tab B 通过 BroadcastChannel('parent-child-switch') 收到消息 → 同步更新 Zustand slice → invalidate 子女维度查询 |
| 隐身模式 / 不同浏览器 |
用户在另一浏览器登录 |
各自独立状态;服务端最终一致(依赖 iam currentChildId 是否持久化,按 ai04 建议方案 A 不持久化,仅前端 localStorage) |
| 移动端 + 桌面端同时登录 |
用户多设备登录 |
各自独立 currentChildId;通知偏好等共享数据通过 WebSocket 实时同步 |
| 网络中断时切换子女 |
离线场景 |
切换操作入队列(IDB),网络恢复后批量同步;UI 显示"离线模式"标识 |
11.4 子女数据访问越权
| 场景 |
触发条件 |
前端处理 |
| URL 直接访问非绑定子女 |
用户篡改 ?childId=xxx |
BFF 在 GET /parent/children/:childId/* 端点做 DataScope=CHILDREN 校验,返回 403 → 前端 toast 错误并跳转默认子女 |
| 切换到刚解绑的子女 |
网络延迟,事件未到达 |
BFF 校验失败 403 → 前端 invalidate children 列表 → 自动切换到第一个绑定子女 |
| 子女档案查看权限被回收 |
学校临时限制 |
同上,BFF 校验 |
12. 数据隐私与合规
子女数据是高敏感数据,parent-portal 必须在设计阶段预留合规框架。本节是 ai15 新增,覆盖 COPPA、FERPA、PIPL 等法规对前端架构的要求。
12.1 适用法规矩阵
| 法规 |
适用范围 |
对前端的要求 |
阶段 |
| COPPA |
美国 <13 岁儿童 |
收集前需家长可验证同意;展示同意记录入口;可删除子女数据请求入口 |
P6 海外扩展 |
| FERPA |
美国教育记录 |
家长有权查看子女教育记录;学校有权限制家长访问(离婚/监护权争议场景) |
P6 海外扩展 |
| PIPL |
中国个人信息保护法 |
隐私政策弹窗 + 同意按钮;敏感信息(成绩)展示前需二次确认;数据导出请求入口 |
P4 起强制 |
| GDPR |
欧盟用户 |
Cookie 同意管理;被遗忘权请求入口;数据可携带权导出 |
P6 海外扩展 |
| 未成年人保护法 |
中国 <18 岁 |
14 岁以下需家长同意;展示适合年龄段的内容过滤 |
P4 起强制 |
12.2 前端合规设计
| 合规点 |
实现位置 |
阶段 |
| 隐私政策同意弹窗 |
Shell RootLayout 首次登录后弹窗 |
P4 |
| Cookie 同意管理(按类别) |
Shell + parent-portal 复用 |
P4 |
| 子女成绩展示二次确认 |
ChildGradeChart 默认遮罩,点击"查看"展示 |
P4 |
| 数据导出请求入口 |
/parent/preferences#data-export 页面 |
P5 |
| 数据删除请求入口 |
/parent/preferences#data-deletion 页面 |
P5 |
| 同意记录查看 |
/parent/preferences#consent-history 页面 |
P5 |
| 监护权变更影响展示 |
收到 BFF 403 时显示"请联系学校"提示,不暴露细节 |
P4 |
| 敏感数据脱敏 |
截图/分享时自动遮罩成绩数字 |
P5+ |
12.3 数据保留策略(前端配合)
| 数据类型 |
前端保留 |
后端保留 |
前端处理 |
| 子女成绩列表缓存 |
30s(TanStack Query) |
永久 |
staleTime 30s 后自动失效 |
| 通知列表 |
30s |
90 天 |
同上 |
| 子女列表 |
5min |
关系存续期 |
同上 |
| 当前选中子女 ID |
localStorage 永久 |
不持久化 |
用户主动清除或解绑时清除 |
| 行为埋点 |
内存队列 100 条 |
90 天 |
队列满后批量上报,上报后清空 |
13. 测试策略分层
ai07 初稿仅提到"覆盖率 ≥ 80%",未细化测试类型与覆盖率分目标。ai15 补全。
13.1 测试金字塔
| 层级 |
工具 |
覆盖率目标 |
测试范围 |
| 单元测试 |
Vitest + @testing-library/react |
≥ 85% |
所有组件渲染/交互、Hook 业务逻辑、Zod schema 校验、纯函数 utils |
| 集成测试 |
Vitest + MSW(Mock Service Worker) |
≥ 75% |
API 请求层 + TanStack Query hook 组合、ChildSwitcher + Zustand slice + invalidate 流程 |
| 视觉回归 |
Playwright + Percy/Applitools(P6 引入) |
关键页面 |
dashboard/children/grades/notifications/preferences 五个核心页面在 light/dark + mobile/desktop 4 组合 |
| E2E 测试 |
Playwright |
关键路径 |
登录→看 dashboard→切换子女→查成绩→改通知偏好→登出 |
| A11y 测试 |
axe-core + jest-axe + @axe-core/playwright |
0 严重违规 |
所有页面 WCAG 2.2 AA 自动扫描 + 手动键盘导航测试 |
| 性能测试 |
Lighthouse CI |
≥ 90 分 |
LCP < 2.5s / CLS < 0.1 / TBT < 200ms(移动端 4G 模拟) |
| 契约测试 |
Pact(BFF ↔ parent-portal 双向,P6 引入) |
关键端点 |
防止 BFF 契约变更打破前端消费 |
13.2 关键 E2E 场景(必须覆盖)
13.3 Mock 数据策略
| 数据来源 |
Mock 方式 |
维护方 |
| BFF API 响应 |
MSW handlers,按 apps/parent-portal/src/mocks/handlers.ts 集中管理 |
ai15 |
| WebSocket 事件 |
Mock WebSocket Server(Playwright fixture) |
ai15 |
| i18n 文案 |
真实 next-intl messages(不 Mock) |
coord |
| 设计令牌 |
真实 ui-tokens(不 Mock) |
ai07 |
| 权限列表 |
按 fixture 角色预设(parent_with_X_children 等) |
ai15 |
14. 性能预算与代码分割
ai07 初稿未提及性能预算。ai15 补全。
14.1 性能预算(Bundle Size)
| 资源类型 |
预算(gzipped) |
备注 |
| parent-portal 首屏 JS |
≤ 80 KB |
含 ChildSwitcher + ParentDashboard + 共享依赖分摊 |
| parent-portal 首屏 CSS |
≤ 20 KB |
含 Tailwind purged + 设计令牌 |
| 路由级懒加载 chunk |
≤ 30 KB/chunk |
每个二级路由单独 chunk |
| 图片 |
≤ 100 KB/页 |
子女头像、空态插画 |
| 总下载量(首屏) |
≤ 200 KB |
4G 网络下 LCP < 2.5s |
14.2 代码分割策略
14.3 预加载策略
| 触发时机 |
预加载内容 |
| Dashboard 加载完成 |
预加载 grades chunk + homework chunk(最常访问) |
| 鼠标 hover Tab 栏子女头像 |
预加载该子女的 grades 数据(TanStack Query prefetch) |
| 通知未读数 > 0 |
预加载 notifications chunk |
| 用户进入 grades 页面 |
预加载 analytics chunk(趋势图 next-step) |
14.4 渲染策略
| 页面 |
渲染模式 |
理由 |
/parent/dashboard |
SSR(首屏)+ CSR(交互) |
SEO + 首屏速度 |
/parent/children |
CSR |
认证后数据,无需 SEO |
/parent/grades |
CSR + Suspense |
数据频变,SSR 反而拖慢 |
/parent/notifications |
CSR + 流式渲染 |
实时性要求 |
/parent/preferences |
CSR |
表单交互 |
15. 前端安全
ai07 初稿仅在横切关注点提到 401 处理。ai15 补全完整前端安全策略。
由 teacher-portal Shell 在 next.config.js 配置,parent-portal 复用:
| Header |
值 |
用途 |
Content-Security-Policy |
default-src 'self'; script-src 'self' 'unsafe-inline'; ... |
XSS 防护,MF 远程加载需放行 Shell 域 |
X-Frame-Options |
SAMEORIGIN |
防止 click-jacking |
X-Content-Type-Options |
nosniff |
防止 MIME 嗅探 |
Referrer-Policy |
strict-origin-when-cross-origin |
限制 referrer 泄漏 |
Permissions-Policy |
camera=(), microphone=(), geolocation=() |
禁用不需要的浏览器能力 |
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
强制 HTTPS |
15.2 XSS 防护
| 场景 |
防护措施 |
| 子女姓名/学校名称展示 |
React 默认转义,禁止 dangerouslySetInnerHTML |
| 通知内容(含富文本) |
DOMPurify 清洗后渲染(project_rules §4) |
URL 参数 ?childId= |
Zod 校验为 UUID 格式,禁止任意字符 |
| localStorage 存储子女 ID |
仅存 UUID,不存敏感信息;用户登出时清除 |
15.3 CSRF 防护
- parent-portal 仅消费 GET/POST/PUT/DELETE,所有 mutation 经 ApiClient
- ApiClient 自动注入
X-Requested-With: XMLHttpRequest 头
- 后端 BFF 校验该头 + 同源 Cookie SameSite=Strict(project_rules §4)
15.4 敏感数据处理
| 数据 |
敏感级别 |
前端处理 |
| 子女姓名 |
中 |
默认展示,截图时脱敏(P5+) |
| 子女成绩 |
高 |
默认展示,但页面离开 5s 后自动遮罩(防偷窥) |
| 子女出勤 |
中 |
同姓名 |
| 监护人联系方式 |
高 |
仅在 preferences 页面展示,掩码显示(138****1234) |
| 子女 ID |
低 |
URL 可携带,但 BFF 校验绑定关系 |
| 通知内容 |
中 |
不缓存到 localStorage,仅 TanStack Query 内存缓存 |
16. 跨标签与跨设备同步
16.1 同步机制矩阵
| 场景 |
同步机制 |
同步内容 |
冲突解决 |
| 同浏览器多标签状态同步 |
BroadcastChannel API |
currentChildId、notification unread count |
最后写入胜出(LWW) |
| localStorage 跨标签变更 |
storage 事件 |
currentChildId 持久化值 |
最后写入胜出 |
| 跨设备状态同步 |
WebSocket 事件(P5) |
通知偏好变更、子女关系变更 |
服务端为准 |
| 网络恢复后状态对齐 |
重连后批量 invalidate + refetch |
全部子女维度数据 |
服务端为准 |
16.2 BroadcastChannel 实现
16.3 跨标签选中子女冲突处理
若 Tab A 在 10:00:00 切换到 child-1,Tab B 在 10:00:01 切换到 child-2:
- 两个 Tab 都收到对方的 BroadcastChannel 消息
- 采用 LWW:以
ts 字段为依据,ts 大的胜出
- 胜出方写入 Zustand slice + localStorage
- 败方 UI 自动同步到胜出方的选中子女
- 用户感知:可能看到一瞬间的切换抖动,可接受
17. i18n 深化
ai07 初稿仅提到 next-intl。ai15 补全多语言策略。
17.1 支持语言矩阵
| 语言 |
locale |
阶段 |
完成度要求 |
| 简体中文 |
zh-CN |
P4 强制 |
100% |
| 英文 |
en-US |
P6 海外扩展 |
100% |
| 繁体中文 |
zh-TW |
P6 海外扩展 |
100% |
| 日文 |
ja-JP |
P6+ 未来 |
≥ 80% |
| 阿拉伯文(RTL) |
ar-SA |
P6+ 未来 |
≥ 80% |
17.2 locale 路由策略
采用 URL 前缀策略(与 Shell 共享,由 Shell 配置):
- Next.js 中间件根据
Accept-Language 头自动重定向
- 用户主动切换语言时写入 Cookie
NEXT_LOCALE,下次访问直接命中
- parent-portal 不维护 locale 路由,复用 Shell 中间件
17.3 翻译文件组织
按路由切分 message bundle,避免首屏加载全部翻译。
17.4 国际化格式
| 数据类型 |
库 |
示例(zh-CN) |
示例(en-US) |
| 日期 |
Intl.DateTimeFormat |
2026年7月9日 |
July 9, 2026 |
| 时间 |
同上 |
下午3:30 |
3:30 PM |
| 数字 |
Intl.NumberFormat |
1,234.56 |
1,234.56 |
| 百分比 |
同上 |
85.5% |
85.5% |
| 货币 |
同上 |
¥1,234.50 |
$1,234.50 |
| 成绩等级 |
自定义映射表 |
优秀/良好/及格/不及格 |
A/B/C/D/F |
| 时区 |
Intl.DateTimeFormat with timeZone |
Asia/Shanghai |
America/Los_Angeles |
17.5 RTL 支持(P6+ 预留)
- Tailwind CSS logical properties:
ms-*/me-*/ps-*/pe-* 替代 ml-*/mr-*/pl-*/pr-*
- 设计令牌预留 RTL 语义令牌:
--space-inline-start / --space-inline-end
- 图标方向敏感(如返回箭头)需根据
dir 属性翻转
18. 移动端与 PWA
家长端是 4 端中移动端使用比例最高的(家长多在通勤/碎片时间查看),移动端策略必须前置。
18.1 响应式断点
| 断点 |
宽度 |
典型设备 |
parent-portal 布局变化 |
sm (default) |
< 640px |
iPhone/Android 手机 |
单列布局;MultiChildTabBar 改为下拉;侧栏导航抽屉化 |
md |
640-1024px |
iPad Mini/Android 平板 |
双列布局(侧栏 + 内容);MultiChildTabBar 顶部 Tab |
lg |
1024-1280px |
iPad Pro/小笔记本 |
三列布局(侧栏 + 内容 + 详情);多子女并列卡片 |
xl |
> 1280px |
桌面 |
三列布局;多子女对比视图横向滚动 |
18.2 移动端交互优化
| 场景 |
移动端优化 |
| 切换子女 |
下拉选择器 + 头像 + 姓名,单手操作可达 |
| 查看成绩 |
卡片式纵向滚动,避免横向表格;图表触摸缩放 |
| 通知列表 |
左滑标记已读、右滑删除(iOS 风格) |
| 通知偏好设置 |
大号 Toggle Switch,手指点击友好 |
| 表单提交 |
底部固定按钮;软键盘弹出时自动避让 |
| 长列表 |
无限滚动 + 骨架屏;不上拉加载更多按钮 |
18.3 PWA 配置(P5+ 引入)
18.4 Service Worker 策略(P5+)
| 资源类型 |
缓存策略 |
TTL |
| 静态资源(JS/CSS/图片) |
Cache First + 网络更新 |
24 小时 |
| 子女列表 |
Stale While Revalidate |
5 分钟 |
| 子女成绩 |
Network First,失败回退缓存 |
30 秒 |
| 通知列表 |
Network Only |
— |
| 通知偏好 |
Network Only |
— |
| API 401 响应 |
不缓存 |
— |
19. 长远愿景与演进路径
19.1 阶段演进路线
19.2 未来功能铺垫(架构预留)
| 未来功能 |
架构预留点 |
启用阶段 |
| 家校沟通(IM 聊天) |
NotificationFeed 组件抽象为通用消息流;WebSocket 事件协议预留 chat.* 类型 |
P7 |
| 缴费(学费/餐费) |
ParentDashboard 卡片插槽;API 路由前缀 /parent/fees/* 预留 |
P7 |
| 活动 RSVP(家长会/运动会) |
NotificationFeed 支持 rsvp 类型消息;状态机:待回复/已确认/已拒绝 |
P7 |
| 家长端 AI 助手 |
API 路由 /parent/ai/* 预留;SSE 复用 teacher-portal 模式 |
P7+ |
| 多子女横向对比报告 |
ChildComparisonView 组件预留(P5+ 启用) |
P5+ |
| 学区/教育局版多租户 |
URL 路由前缀 /{tenantId}/parent/* 预留; TanStack Query key 加 tenantId 维度 |
P8+ |
| 移动端原生壳 |
PWA → Capacitor 打包;MF 不变 |
P8+ |
| 离线模式 |
Service Worker + IDB 队列(P5+ 引入) |
P5+ |
| 推送通知(Web Push) |
Service Worker PushManager + VAPID(P5+ 引入) |
P5+ |
19.3 模块解耦与演化
| 演化方向 |
触发条件 |
迁移策略 |
| parent-portal 拆分为多个 Remote |
bundle > 200KB 或团队规模 > 5 人 |
按场景域拆分:parent-core-remote(dashboard/grades)+ parent-comm-remote(notifications/preferences) |
| MF 2.0 → 3.0 升级 |
MF 3.0 稳定且解决 SSR 问题 |
Shell 端 @module-federation/nextjs-mf 升级;parent-portal 仅改 name/filename 字段 |
| 切换为原生 SSR(脱离 MF) |
SEO 需求强烈或 MF 维护成本过高 |
保留 API 请求层和组件库;移除 MF 配置;独立部署为完整 Next.js 应用 |
| 状态管理迁移(Zustand → Jotai) |
Zustand 性能瓶颈或团队偏好 |
逐 slice 迁移;Hook 接口保持不变 |
19.4 监控与降级
| 监控项 |
阈值 |
触发动作 |
| parent-portal 5xx 错误率 |
> 1% |
告警 SRE;自动切换到只读模式(隐藏 mutation 按钮) |
| MF Remote 加载失败 |
加载超时 10s |
Fallback 到 Shell 内置的最小化 dashboard(静态引导页) |
| WebSocket 连接失败 |
重试 5 次仍失败 |
降级为 HTTP 轮询(每 60s 拉取通知列表) |
| BFF 响应延迟 |
P95 > 3s |
前端展示"加载缓慢"提示;自动缩短缓存 TTL |
| 子女列表加载失败 |
3 次重试失败 |
显示"网络异常,请稍后重试"页面 + 重试按钮 |
AI Agent: ai15 (parent-portal remote)
Branch: docs/parent-portal-stage1-stage2-design-ai15
Coordinator: coord-ai
Predecessor: ai07(初版起草,ai15 接管审计与补全)