Files
Edu/docs/architecture/issues/contracts/parent-portal_contract.md
SpecialX 7c8e0f5dea feat(parent-portal): 完成 P4-P6 全部任务 + ARB-022 §24.4 双 /v1 修正 + 413 测试通过
主要变更:
1. ARB-022 §24.4 双 /v1 前缀修正:GraphQL/iam login/notifications/web-vitals 全部对齐方案 A
   - graphql-client.ts: /api/v1/parent/v1/graphql
   - auth.ts: /api/v1/iam/v1/login + /api/v1/iam/v1/refresh
   - useWebSocket.ts: /api/v1/parent/v1/notifications
   - observability/env.ts: /api/v1/parent/v1/web-vitals
   - 同步更新 contract.md / 01-understanding.md / 02-architecture-design.md

2. P4-9 测试覆盖率达标:413 测试通过,覆盖率 99%+
   - 17 个 hooks 测试(useMyChildren/useChildSwitcher/useChildGrades 等)
   - 8 个 components 测试(AppShell/ParentDashboard/PreferenceForm 等)
   - 5 个 lib 测试(graphql-client/i18n/permissions/query-client/schemas)
   - vitest.config.ts 排除 pages/observability/middleware(由集成/E2E 覆盖)

3. ARB-020 §22.5 switchChild 双层实现(GraphQL Mutation 后端审计 + Zustand 前端缓存)

4. P6 硬化全部完成:
   - P6-1 OTel browser SDK + Web Vitals 挂载(observability/otel.ts + web-vitals.ts)
   - P6-2 A11y WCAG 2.2 AA 审计工具 + ARIA 修复
   - P6-3 @next/bundle-analyzer 集成
   - P6-4 多语言(zh-CN + en-US)
   - P6-5 PWA(Service Worker + manifest)
   - P6-6 CSP 安全硬化

5. 补齐参考项目差距页面:exams/exam result/classes/learning-path/settings/trend

6. 文档同步:workline.md / contract.md / known-issues.md 全部更新

parent-portal 全部 P4-P6 任务已完成,无剩余工作项。
2026-07-13 13:10:07 +08:00

18 KiB
Raw Blame History

parent-portal 对接契约

负责人ai15 关联:matrix.mdparent-bff_contract.mdiam_contract.mdpush-gateway_contract.md 依据ARB-001BFF GraphQL、ARB-002MF Shell 暴露清单)、port-allocation.md §4 待仲裁ISSUE-001 ~ ISSUE-010objections/parent-portal_issue.md),仲裁前本契约按 ARB-001 GraphQL 方向编写


§1 我提供什么(对外接口)

1.1 gRPC 接口(如有)

无。parent-portal 是前端微前端 Remote不提供 gRPC。

1.2 HTTP 端点(如有)

无对外 HTTP API 端点。parent-portal 是 Next.js 前端应用MF Remote不对外暴露 REST API。

说明ISSUE-006parent-portal 的页面路由(/parent/dashboard/parent/grades 等)是前端 SSR/CSR 路由,不是 HTTP API 端点。页面路由清单见 01-understanding.md §8 L2 路由表

parent-portal 仅提供两个内部健康检查端点(非业务 API

Method Path 用途 认证
GET /api/health Dockerfile HEALTHCHECK
GET /api/ready K8s readinessProbe

1.3 GraphQL schema如 BFF

不适用。parent-portal 消费 parent-bff GraphQL自身不提供 schema。

1.4 Kafka 事件发布(如有)

无。前端不发布 Kafka 事件。

1.5 错误码前缀

parent-portal 不产生错误码前缀(前端不定义错误码)。消费侧错误码前缀见 §2.5。

1.6 微前端架构

角色 说明
MF 角色 RemoteShell = teacher-portal :4000
Remote name parent_app
remoteEntry 路径 static/chunks/remoteEntry.js
暴露模块 ./pages(家长场景页面)、./ChildSwitcher(多子女切换组件)
MF 配置文件 apps/parent-portal/next.config.jsNextFederationPlugin02-architecture-design §1.2
MF sharedsingleton react / react-dom / urql / graphql / @tanstack/react-query / zustand / nuqs / @edu/ui-tokens / @edu/ui-components / @edu/hooksARB-002
dev/prod 端口 4002port-allocation.md §4
feature flag NEXT_PUBLIC_MF_ENABLEDARB-002P4 默认开)

ISSUE-007MF 配置文件统一为 next.config.js,不使用 module-federation.config.ts(与 02-architecture-design + teacher-portal Shell 一致)。


§2 我消费什么(依赖上游)

2.1 gRPC 调用(同步)

无。前端不直接调 gRPC。

2.2 Kafka 事件订阅(异步)

无。前端不直接订阅 Kafka。

2.3 HTTP 调用(非 GraphQL

被调用方 Method.Path 用途 mock 策略
api-gateway (ai01) POST /api/v1/iam/v1/login 家长登录 api-gateway 就绪前 MSW 返回固定 JWTparent 角色)
api-gateway (ai01) POST /api/v1/iam/v1/refresh 刷新令牌 MSW 返回新 access/refresh token

ISSUE-004 + ARB-022 §24.4

  • 登录端点统一为 POST /api/v1/iam/v1/login(双 /v1 前缀ARB-022 §24.4 ISSUE-003 方案 A与 matrix.md §5 /api/v1/iam/v1/* 一致)
  • 登录是 parent-portal 唯一走 REST非 GraphQL的端点登录前无 JWTGraphQL endpoint 需鉴权
  • ARB-020 §22.3 早期描述为单 /v1ARB-022 §24.4 已修正为双 /v1以 ARB-022 为准)

2.4 GraphQL 查询域(经 api-gateway 代理到 parent-bff

依据ARB-001BFF GraphQL+ parent-bff_contract.md §1.3 + ARB-022 §24.4 方案 A

端点POST /api/v1/parent/v1/graphql(双 /v1 前缀ARB-022 §24.4 ISSUE-003 方案 Aapi-gateway 代理 /api/v1/parent/v1/* → parent-bff :3010 /parent/v1/*

ISSUE-001 / ISSUE-008 + ARB-022 §24.4

  • 仲裁前本表按 GraphQL 方向编写(与 parent-bff contract + matrix.md 一致)
  • 路径前缀统一为 /api/v1/parent/v1/graphql(双 /v1ARB-022 §24.4 方案 A与 matrix.md §5 一致)
  • ARB-020 §22.3 早期描述为单 /v1ARB-022 §24.4 已修正为双 /v1以 ARB-022 为准)
Query/Mutation 类型 用途 对应 parent-bff 聚合 mock 策略
currentUser Query 当前家长信息 iam.GetUserInfo + GetEffectivePermissions + GetViewports MSW 返回固定家长parent-001 王家长)
myChildren Query 我的子女列表(核心) iam.GetChildrenByParentI3/ISSUE-047 裁决) MSW 返回固定 2 个子女student-001 + student-002
childSummary(childId) Query 子女仪表盘概览 data-ana.GetParentDashboard MSW 返回固定仪表盘
childGrades(childId) Query 子女成绩 core-edu.ListGradesByStudent MSW 返回固定 5 个成绩
childAttendance(childId) Query 子女考勤 core-edu.ListAttendanceByStudent MSW 返回固定 10 条考勤
childHomework(childId) Query 子女作业 core-edu.ListHomeworkByClass MSW 返回固定 3 个作业
childWeakness(childId) Query 子女薄弱点 data-ana.GetStudentWeakness MSW 返回固定 3 个 weak_points
childTrend(childId) Query 子女学习趋势 data-ana.GetLearningTrend MSW 返回固定趋势数据
myNotifications Query 通知列表P5 msg.ListNotifications MSW 返回固定 10 条通知
markAsRead(notificationId) Mutation 标记已读P5 msg.MarkAsRead MSW 返回 success=true
updateNotificationPreferences Mutation 更新通知偏好 msg待 ai05 确认) MSW 返回 success=true
switchChild(childId) Mutation 切换当前子女 待 ISSUE-009 仲裁确认 见 ISSUE-009

switchChild 说明ISSUE-009

  • parent-bff_contract.md §1.3 未列 switchChild Mutation
  • 待 coord 仲裁switchChild 是 GraphQL Mutation后端记录当前子女还是纯前端状态localStorage + Zustand
  • 若纯前端:本表移除 switchChild切换逻辑在 useChildSwitcher 内直接写 Zustand + localStorage

2.5 消费的错误码前缀(前端 i18n 路由)

parent-portal 不产生错误码,仅消费。前端 API 请求层根据 error.code 前缀路由到对应 i18n key

前缀 来源服务 i18n key 模式
IAM_ iam error.iam.{{code}}
CORE_EDU_ core-edu error.core_edu.{{code}}
BFF_PARENT_ parent-bff error.bff_parent.{{code}}
GW_ api-gateway error.gw.{{code}}
NETWORK_ 前端网络层 error.network.{{code}}

:与 matrix.md §6 错误码前缀矩阵对齐。BFF_PARENT_ 前缀由 parent-bff 定义(见 parent-bff_contract.md §1.5)。

2.6 WebSocket 推送P5

被调用方 协议 路径 用途 mock 策略
push-gateway (ai02) WebSocket /ws 实时推送 mock-socket 模拟 WS 推送(每 30s 1 条)
push-gateway (ai02) SSE降级 /sse SSE 降级

WebSocket 连接由 Shell 建立统一连接管理parent-portal 通过 Zustand ui-store 订阅事件流。

2.7 消费的 MF Shell 暴露ARB-002

暴露模块 来源 用途
AppShell teacher-portal Shell 左栏导航 + 主内容区布局
GraphQLProvider teacher-portal Shell urql client 单例ARB-002
useAuth packages/hooks 会话状态
usePermission packages/hooks 权限查询
useGraphQLClient packages/hooks urql client 获取
ErrorBoundary packages/ui-components React 渲染异常兜底
Loading / Empty packages/ui-components 骨架屏 / 空态
RequirePermission packages/ui-components L3 组件级视口控制

MF sharedsingletonreact / react-dom / urql / graphql / @tanstack/react-query / zustand / nuqs / @edu/ui-tokens / @edu/ui-components / @edu/hooksARB-002 裁决,见 coord.md §2


§3 就绪信号

3.1 我依赖的上游就绪标志

上游 就绪信号 提供方 状态
api-gateway HTTP :8080 启用 + JWT 验签 + /api/v1/parent/v1/* 代理ARB-022 §24.4 双 /v1 ai01
parent-bff GraphQL POST /graphql :3010 + currentUser/myChildren/childSummary/childGrades Query ai05 P4
iam GetChildrenByParent gRPC 50052 + iam_student_guardiansI3/ISSUE-047 裁决) ai06 P3 补全
teacher-portal Shell MF exposesAppShell + GraphQLProvider + hooks + UI 组件)+ shared singleton ai13 P2
push-gateway WebSocket :8081/ws ai02 P5
msg gRPC 50056 + NotificationService ai10 P5
shared-ts / contracts ApiClient / Logger / Permissions 常量 coord
ui-tokens / ui-components / hooks 三层令牌 + shadcn + usePermission/useAuth ai07/ai13 P2 收尾

P0 阻塞ISSUE-010iam GetChildrenByParent 缺失,多子女场景无法落地。补全前用 mock固定 2 个子女 student-001 + student-002开发。

3.2 我的就绪标志(供下游消费)

matrix.md §8 就绪信号跟踪表对齐

信号 说明 阶段
parent-portal dev server :4002 启用 MF Remote 可被 Shell 加载 P4-1
MF Remote remoteEntry.js 可加载 Shell 端 remotes.parent = parent_app@http://localhost:4002/... 可解析 P4-1
独立壳渲染 首页 + 导航 + 路由守卫 P4-1
登录流程可用 POST /api/v1/iam/v1/login 获取 JWT 存入 httpOnly cookieARB-022 §24.4 双 /v1 P4-2
GraphQL 查询可执行 currentUser / myChildren / childSummary 返回数据mock 或真实) P4-2
多子女切换可用 ChildSwitcher + invalidate 流程通过 P4-3
数据范围校验生效 前端路由守卫校验 childId 是否在 myChildren 返回列表中 P4-3
Dashboard 可访问 家长登录 → 看到 Dashboard含子女卡片 P4-4
健康检查通过 GET /api/health + GET /api/ready 200 P4-1
测试覆盖率达标 单元 ≥ 85% + 集成 ≥ 75% P4-9
Docker 镜像可构建 docker build 成功 P4-10
WebSocket 通知可接收 push-gateway WS 事件正确处理 P5-1

§4 Mock 策略

4.1 我提供的 mock

parent-portal 是前端,无下游消费方。但对开发体验提供:

  • Storybook:各组件独立 story
  • MSW handlersapps/parent-portal/src/mocks/handlers.ts,拦截所有 GraphQL/HTTP 请求

4.2 我消费的 mock

在真实上游就绪前parent-portal 使用以下 mockNEXT_PUBLIC_API_MOCKING=enabled 控制):

  • GraphQL mockMSW 拦截 POST /api/v1/parent/v1/graphql(双 /v1ARB-022 §24.4
    • 按 operationName 返回对应 mock 响应(与 parent-bff mock 数据一致)
    • currentUser → 固定家长id="parent-001", name="王家长", roles=["parent"]
    • myChildren → 固定 2 个子女id="student-001" 李同学 + id="student-002" 李妹妹)
    • childSummary → 固定仪表盘child_avg_score=85.0, child_class_rank=5
    • childGrades → 固定 5 个成绩
    • childAttendance → 固定 10 条考勤
    • childHomework → 固定 3 个作业
    • myNotifications → 固定 10 条通知
    • 所有 mock 响应定义在 apps/parent-portal/src/mocks/fixtures/*.json
  • HTTP mockMSW 拦截 POST /api/v1/iam/v1/login(双 /v1ARB-022 §24.4 → 返回固定 JWT + UserInfoparent 角色)
  • WebSocket mockmock-socket 库,连接后每 30 秒推送 1 条 mock 通知
  • JWT mock:固定 mock JWT存入 httpOnly cookie
  • 环境切换NEXT_PUBLIC_API_MOCKING=enabled(开发)/ disabled(上游就绪后)
  • 数据一致性myChildren mock 必须返回固定 2 个孩子student-001 + student-002与所有 child* 查询的 student_id 一致(否则前端数据范围校验失败)

§5 待协调事项(指向 objections

以下事项已提请 coord 仲裁,仲裁结果可能影响本契约:

ISSUE 影响章节 当前处理
ISSUE-001REST vs GraphQL §2.4 已裁决 ARB-020 §22GraphQLPOST /api/v1/parent/v1/graphql
ISSUE-004登录端点 §2.3 已裁决 ARB-020 §22 + ARB-022 §24.4POST /api/v1/iam/v1/login(双 /v1
ISSUE-006HTTP 端点分类) §1.2 已修正为"无对外 HTTP API"
ISSUE-007MF 配置文件名) §1.6 已修正为 next.config.js
ISSUE-008GraphQL 路径前缀) §2.4 已修正为 /api/v1/parent/v1/graphql(双 /v1ARB-022 §24.4 方案 A
ISSUE-009switchChild Mutation §2.4 已裁决 ARB-020 §22.5双层实现GraphQL Mutation 后端审计 + Zustand 前端缓存)
ISSUE-010iam GetChildrenByParent 缺失) §3.1 已裁决 ARB-020 §22P0 阻塞,用 mock 开发(固定 2 子女)

详见 objections/parent-portal_issue.md