# student-portal 工作排期 > 负责人:ai14 > 关联:[workline.md](../workline.md)、[coord.md §1 ARB-001](../coord.md)、[coord.md §2 ARB-002](../coord.md)、[contracts/student-portal_contract.md](../contracts/student-portal_contract.md)、[matrix.md](../matrix.md) > 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试) > 依据:president-final-rulings.md §3.6(ai14 P3 功能范围)+ §7.14(ai14 工作内容最终清单)+ ARB-002 §2.3(P3 首个 Remote)+ 02-architecture-design.md v2 阶段能力累积矩阵 --- ## §1 总览 student-portal 是学生端微前端(MF Remote),通过 Module Federation 接入 teacher-portal Shell,覆盖考试作答、作业提交、学情查看等场景。全阶段目标:P2 MF Remote 骨架预埋 → P3 考试作答 + 作业提交 + 基础页面 → P4 知识图谱 + 学情诊断 → P5 实时通知 + AI 辅助 → P6 可观测性硬化 + A11y + 性能。 **全并行模式**:ai14 一口气完成 P2-P6 全部代码,开发期间用 MSW mock 上游(student-bff GraphQL / api-gateway HTTP / push-gateway WebSocket),上游就绪后在 [matrix.md](../matrix.md) §8 更新就绪信号,最后统一集成测试。 **批次归属**(见 [workline.md §1](../workline.md)): - 批次 2(P3):ai14 与 ai07 + ai08 + ai04 + ai03扩展 并行启动(`b2d, after b1d, 8d`) - 批次 3(P4):与 ai09 + ai11 + ai05 + ai15 并行 - 批次 4(P5):与 ai10 + ai02 + ai12 + ai03扩展 并行 - 批次 5(P6):与 ai16 + 持续优化 并行 --- ## §2 全阶段甘特图(P2-P6) ```mermaid gantt title ai14 student-portal 全阶段排期(全并行) dateFormat YYYY-MM-DD axisFormat %m-%d section P2 预埋 项目骨架+设计令牌三层+独立壳路由 :crit, a14p0, 2026-07-10, 2d MF Remote配置(NextFederationPlugin remotes) :crit, a14a, after a14p0, 2d MSW基础设施+GraphQL请求层骨架 :a14b, after a14a, 2d section P3 学生核心 AppShell复用+学生端导航+路由守卫 :crit, a14c, after a14b, 2d Dashboard页(studentDashboard query) :a14d, after a14c, 2d 我的班级页+我的考试列表+我的作业列表 :a14e, after a14d, 3d 考试作答页(状态机+服务器时间同步) :crit, a14f, after a14e, 4d IDB断网恢复队列+自动保存+DraftRecovery :crit, a14g, after a14f, 3d 防作弊采集+BroadcastChannel多标签检测 :a14h, after a14g, 2d 作业提交页(submitHomework mutation+乐观更新) :a14i, after a14h, 2d 我的成绩页+我的考勤页 :a14j, after a14i, 2d section P4 知识与学情 学习路径页(learningPath query+知识点卡片) :a14k, after a14j, 3d 学情诊断页(myWeakness+myTrend query+图表) :a14l, after a14k, 3d 教材章节浏览页(textbooks+chapters) :a14m, after a14l, 2d section P5 推送与AI WebSocket通知中心(myNotifications+markAsRead) :a14n, after a14m, 2d 跨Tab通知同步(BroadcastChannel) :a14o, after a14n, 1d AI辅助答疑(SSE流式,可选) :a14p, after a14o, 3d section P6 硬化 Sentry+RUM+OTel browser :a14q, after a14p, 2d A11y审计(WCAG 2.2 AA) :a14r, after a14q, 2d 性能优化+bundle门禁(Remote <80KB) :a14s, after a14r, 2d P3未尽事项补全+降级策略验证 :a14t, after a14s, 2d ``` --- ## §3 详细任务 ### P2:MF Remote 骨架预埋 - **负责人**:ai14 - **裁决依据**:ARB-002 §2.3(P3 首个 Remote,但 P2 可预埋骨架)+ 总裁裁决 §3.6(ai14 P3 起步)+ §2.17(MF GraphQL client 单例方案 A) - **交付物**: - 项目骨架(`apps/student-portal/` 目录结构:`src/app/`、`src/components/`、`src/lib/`、`src/hooks/`、`src/mocks/`) - 设计令牌三层(primitive.css / semantic.css / tailwind-theme.ts,与 teacher-portal Shell 一致) - 独立壳路由(next.config.js + app/layout.tsx + app/page.tsx,MF 关闭时可独立渲染) - NextFederationPlugin 配置(`remotes: { teacher: 'teacher@http://localhost:4000/_next/static/chunks/remoteEntry.js' }`,`exposes: { './StudentApp': './src/app/student-app.tsx' }`) - MF shared singleton 配置(react/react-dom/urql/graphql/@tanstack/react-query/zustand/nuqs/@edu/* 全部 singleton) - MSW 基础设施(`src/mocks/handlers.ts` + `src/mocks/fixtures/*.json` + `NEXT_PUBLIC_API_MOCKING=enabled`) - GraphQL 请求层骨架(`src/lib/graphql.ts` 复用 Shell `useGraphQLClient()`,不重复创建 client) - **依赖**: - packages 骨架(ui-tokens/ui-components/hooks,ai13 批次 0.15 已完成) - teacher-portal MF Shell 配置(ai13 P2,exposes 就绪) - **Mock 策略**:MSW 拦截 `POST /api/v1/student/graphql` + `POST /api/auth/login` - **验收标准**: - `pnpm dev` 启动 :4001 可访问 - MF 配置不破坏独立壳渲染(`NEXT_PUBLIC_MF_ENABLED=false` 时独立渲染首页) - MSW 拦截 GraphQL 请求返回 mock 数据 - lint + typecheck 零错误 ### P3:考试作答 + 作业提交 + 基础页面(核心) - **负责人**:ai14 - **裁决依据**:总裁裁决 §3.6(ai14 P3 功能范围)+ ARB-001 §1.3(ActionState 信封 + 降级模式方案 B)+ ARB-002 §2.2(复用 Shell 暴露清单)+ 02-architecture-design.md v2 §14(考试作答架构设计) - **交付物**: - **AppShell 复用 + 学生端导航**:从 Shell 导入 `AppShell`,覆写学生端视口(myClasses/myExams/myHomework/myGrades/myAttendance/learningPath/dashboard/notifications) - **路由守卫**:未登录跳转 `http://localhost:4000/login?redirect=student`,登录后回跳 - **Dashboard 页**(`/dashboard`):消费 `studentDashboard` query(upcomingHomework + upcomingExams + recentGrades + attendanceRate + learningStreakDays) - **我的班级页**(`/my-classes`):消费 `myClasses` query - **我的考试列表页**(`/my-exams`):消费 `myExams` query,按状态分组(未开始/进行中/已提交/已批改) - **我的作业列表页**(`/my-homework`):消费 `myHomework` query,按状态分组 - **考试作答页**(`/my-exams/[id]/take`): - 状态机(NotStarted → InProgress → AutoSaving → Submitting → Submitted,见 02 §14 状态机图) - 服务器时间同步(`useServerTimeSync` hook,5 分钟重新同步,倒计时基于服务器时间) - IDB 断网恢复队列(`idb-keyval` 存草稿 + 队列,网络恢复后重试) - 自动保存(每 30s + blur 事件触发,乐观更新本地状态) - DraftRecovery 草稿恢复(进入作答页时检查 IDB 草稿,提示恢复) - 防作弊采集(visibilitychange/copy/paste/fullscreen/contextmenu 事件监听 + 记录) - BroadcastChannel 多标签检测(`edu-exam-session` channel,检测到多标签警告) - 提交防重复(idempotency key + 提交按钮 disabled + 提交中状态) - **作业提交页**(`/my-homework/[id]/submit`): - 消费 `submitHomework` mutation - 乐观更新(useMutation onMutate 回滚 + invalidateQueries) - 附件上传(ARB-019 §21.5 方案 A 已裁决,暂走 mock) - **我的成绩页**(`/my-grades`):消费 `myGrades` query,成绩列表 + 趋势图 - **我的考勤页**(`/my-attendance`):消费 `myAttendance` query,考勤日历 - **依赖**: - student-bff GraphQL schema(ai04 P3,`packages/shared-ts/contracts/graphql/student-bff.graphql`,ARB-019 §21 已裁决) - api-gateway 路由(ai01 P3,`/api/v1/student/*` 反向代理 student-bff,ARB-019 §21 已裁决) - core-edu gRPC(ai08 P3,提供 ExamService/HomeworkService/GradeService/AttendanceService/ClassService) - iam gRPC(ai06 P2,GetUserInfo + GetEffectivePermissions + GetViewports) - data-ana gRPC(ai11 P4,但 studentDashboard 聚合需要,P3 用 mock) - **Mock 策略**: - MSW 拦截 `POST /api/v1/student/graphql`,按 operationName 返回 mock 响应 - mock-socket 模拟 WebSocket 推送(考试延长/强制提交事件) - IDB 草稿恢复用真实 idb-keyval(前端可独立测试) - **验收标准**: - Dashboard 页渲染(mock 数据):upcomingHomework + upcomingExams + recentGrades 三栏 - 考试作答页状态机完整:进入 → 作答 → 自动保存 → 提交 → 跳转结果页 - 断网恢复:手动 offline → 作答 → 恢复网络 → 草稿自动提交 - 防作弊采集:visibilitychange hidden 触发记录(mock 上报) - 多标签检测:开第二个 Tab 作答,第一个 Tab 收到警告 - 作业提交乐观更新:提交后立即 UI 反馈,失败回滚 - lint + typecheck 零错误 ### P4:知识图谱 + 学情诊断 - **负责人**:ai14 - **交付物**: - **学习路径页**(`/learning-path`):消费 `learningPath` query,知识点卡片列表 + 掌握度进度条 - **学情诊断页**(`/dashboard/weakness`):消费 `myWeakness` query,薄弱点雷达图(recharts) - **学习趋势页**(`/dashboard/trend`):消费 `myTrend` query,趋势折线图(recharts) - **教材章节浏览页**(`/textbooks`、`/textbooks/[id]/chapters`):消费 `textbooks` + `chapters` query - **依赖**: - student-bff 扩展 content + data-ana gRPC 调用(ai04 P4) - content gRPC(ai09 P4,TextbookService + ChapterService + KnowledgeGraphService) - data-ana gRPC(ai11 P4,AnalyticsService.GetStudentWeakness + GetLearningTrend) - **Mock 策略**:MSW 返回固定知识点 + 薄弱点 + 趋势数据 - **验收标准**: - 学习路径页渲染知识点卡片 + 掌握度(mock) - 学情诊断页雷达图 + 趋势折线图渲染(mock) - 教材章节树形导航可用 ### P5:实时通知 + AI 辅助 - **负责人**:ai14 - **交付物**: - **WebSocket 通知中心**(`/notifications`): - 消费 `myNotifications` query + `markAsRead` mutation - WebSocket 连接 `ws://push-gateway:8081/ws`,实时接收通知 - 通知分类(作业/考试/成绩/系统),未读计数 - **跨 Tab 通知同步**(BroadcastChannel `edu-notification` channel,新通知在所有 Tab 同步) - **AI 辅助答疑**(可选,`/ai-tutor`): - SSE 流式接收 AI 回答 - 消费 ai 服务(待 ai12 P5 就绪) - **依赖**: - push-gateway WebSocket(ai02 P5,`/ws` 端点) - msg gRPC(ai10 P5,NotificationService) - ai 服务 gRPC(ai12 P5,AiService.Chat,可选) - **Mock 策略**:mock-socket 模拟 WS 推送(每 30 秒 1 条通知)+ MSW 返回固定 AI 响应(SSE 用 ReadableStream mock) - **验收标准**: - 通知中心实时接收 WS 推送(mock) - 多 Tab 同步:Tab A 收到通知,Tab B 未读计数同步更新 - AI 辅助答疑流式输出(mock) ### P6:可观测性硬化 + A11y + 性能 - **负责人**:ai14 - **交付物**: - **Sentry 错误追踪**(`NEXT_PUBLIC_SENTRY_DSN` + beforeSend PII 过滤,学生隐私合规) - **Web Vitals RUM**(LCP/INP/CLS/TTFB → `/api/v1/admin/web-vitals`) - **OTel browser SDK**(自动埋点 fetch/XHR/document load → OTLP collector) - **A11y 审计**(WCAG 2.2 AA:eslint-plugin-jsx-a11y + @axe-core/playwright + 对比度审计) - **性能优化**(bundle analyzer + size-limit CI 门禁:Remote < 80KB / CSS < 50KB) - **P3 未尽事项补全**(根据 ISSUE-014-03/04/05/06/07 仲裁结果补全防作弊策略、附件上传、实时事件响应、DataScope 强制执行) - **降级策略验证**(02 §18.2 降级策略矩阵的 12 个场景端到端验证) - **依赖**: - push-gateway WebSocket 真实就绪(ai02 P5) - Sentry DSN + OTel collector(基础设施) - 全部上游就绪(统一集成测试) - **验收标准**: - 99.9% 可用 + WCAG 2.2 AA + LCP < 2.5s / INP < 200ms / CLS < 0.1(P75) - Remote bundle < 80KB / CSS < 50KB - 12 个降级场景全部验证通过 --- ## §4 依赖与就绪信号 ### §4.1 我依赖的上游就绪标志 | 上游 | 就绪标志 | 阻塞阶段 | 状态 | | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -------- | ----------- | | packages 骨架(ai13 批次 0.15) | ui-tokens/ui-components/hooks 可 import | P2 启动 | ✅ 已就绪 | | teacher-portal MF Shell(ai13 P2) | exposes AppShell/GraphQLProvider/useGraphQLClient/useAuth/usePermission + shared singleton | P2 启动 | ⏳ 待 ai13 P2 | | api-gateway HTTP :8080(ai01 P3) | `/api/v1/student/*` 反向代理 student-bff 可用 | P3 启动 | ⏳ 待 ai01 P3 | | student-bff GraphQL(ai04 P3) | `POST /graphql` :3009 + 核心 Query/Mutation 可执行 | P3 启动 | ⏳ 待 ai04 P3 | | student-bff GraphQL schema(ai04 + ISSUE-014-02 仲裁) | `packages/shared-ts/contracts/graphql/student-bff.graphql` 创建 | P3 启动 | ✅ 已裁决(ARB-019) | | core-edu gRPC 50053(ai08 P3) | ExamService/HomeworkService/GradeService/AttendanceService/ClassService 全部 RPC | P3 启动 | ⏳ 待 ai08 P3 | | iam gRPC 50052(ai06 P2) | GetUserInfo + GetEffectivePermissions + GetViewports | P3 启动 | ⏳ 待 ai06 P2 | | student-bff content/data-ana 扩展(ai04 P4) | learningPath/myWeakness/myTrend/textbooks/chapters query 可用 | P4 启动 | ⏳ 待 ai04 P4 | | content gRPC 50054(ai09 P4) | TextbookService + ChapterService + KnowledgeGraphService | P4 启动 | ⏳ 待 ai09 P4 | | data-ana gRPC 50055(ai11 P4) | AnalyticsService.GetStudentWeakness + GetLearningTrend | P4 启动 | ⏳ 待 ai11 P4 | | push-gateway WebSocket :8081/ws(ai02 P5) | WS 连接可建立 + 推送可接收 | P5 启动 | ⏳ 待 ai02 P5 | | msg gRPC 50056(ai10 P5) | NotificationService.ListNotifications + MarkAsRead | P5 启动 | ⏳ 待 ai10 P5 | | ai 服务 gRPC 50058(ai12 P5,可选) | AiService.Chat(SSE 流式) | P5 启动 | ⏳ 待 ai12 P5 | | Sentry DSN + OTel collector(基础设施) | Sentry 项目创建 + OTel collector 可接收 OTLP | P6 启动 | ⏳ 待基础设施 | ### §4.2 我的就绪信号(供下游消费) | 信号 | 就绪标志 | 消费方 | | ------------------------------- | --------------------------------------------------------- | ------ | | student-portal dev server :4001 | `pnpm dev` 启动 + 首页可访问 | 无(最前端,但 teacher-portal Shell 需加载 Remote) | | MF Remote 可加载 | teacher-portal Shell 可加载 `student-portal/StudentApp` | teacher-portal(ai13 P3 集成测试) | | 登录流程可用 | 未登录跳转 Shell `/login`,登录后回跳 student | 无 | | GraphQL 查询可执行 | currentUser/studentDashboard/myClasses 返回数据 | 无 | | 考试作答链路通 | 进入作答 → 自动保存 → 提交 → 跳转结果页 | 无 | | WebSocket 通知可接收 | 通知中心实时更新(mock) | 无 | --- ## §5 全并行开发说明 按 [matrix.md](../matrix.md) §全并行模式: 1. ai14 一口气完成 P2-P6 全部代码,开发期间用 MSW mock 上游 2. 上游就绪后在 matrix.md §8 更新就绪信号(`student-portal | ai14 | :4001 可访问 + MF Remote | ⏳ → ✅`) 3. 所有模块就绪后统一集成测试(matrix.md §9 检查清单) 4. Mock 切换:`NEXT_PUBLIC_API_MOCKING=enabled` → `disabled` 5. MF 切换:`NEXT_PUBLIC_MF_ENABLED=false`(P2 独立壳)→ `true`(P3 接入 Shell) --- ## §6 阶段能力累积矩阵(与 02-architecture-design.md v2 §20 对齐) | 阶段 | 能力 | 关键页面/功能 | | ---- | --------------------------------------------------- | -------------------------------------------------------------------------- | | P2 | 项目骨架 + MF Remote 配置 + MSW 基础设施 | 独立壳首页(占位) | | P3 | + 考试作答 + 作业提交 + 基础页面 | Dashboard / myClasses / myExams / myHomework / myGrades / myAttendance | | P4 | + 知识图谱 + 学情诊断 + 教材章节 | learningPath / myWeakness / myTrend / textbooks / chapters | | P5 | + 实时通知 + 跨 Tab 同步 + AI 辅助(可选) | notifications / ai-tutor | | P6 | + 可观测性 + A11y + 性能 + 降级验证 + 尽事项补全 | Sentry / RUM / OTel / WCAG 2.2 AA / bundle 门禁 | --- ## §7 关键风险与缓解 | 风险 | 影响 | 缓解措施 | | ---------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- | | student-bff GraphQL schema 未就绪(ai04 P3 延迟) | 高 | MSW mock 全量 query/mutation,schema 就绪后切换;ai14 自行维护 mock schema 用于 codegen | | 考试作答断网恢复逻辑复杂(IDB 队列 + 服务器时间对齐)| 高 | 02 §14 已设计完整状态机 + 时间同步算法;P3 优先实现核心链路,P6 验证降级场景 | | MF Remote 加载失败(Shell 未就绪或版本不兼容) | 中 | 02 §3.2 已设计独立壳回退(`NEXT_PUBLIC_MF_ENABLED=false` 时独立渲染) | | 防作弊策略未仲裁(ISSUE-014-03/04) | 中 | ✅ 已裁决(ARB-019 §21.4);02 §14.5 已补全全屏策略 + visibilitychange + 多标签 + 粘贴题型分层 | | 附件上传协议未仲裁(ISSUE-014-05) | 中 | ✅ 已裁决(ARB-019 §21.5 方案 A);02 §4.2 已更新为 REST upload + GraphQL 提交 URL | | 实时事件命名未确认(ISSUE-014-06) | 低 | ✅ 已裁决(ARB-019 §21.6);02 §5.1/§5.2 已补全 ExamExtended + ExamForceSubmitted | --- ## §8 工作进度跟踪 ### §8.1 ARB-019 §21.8 ai14 执行项落实情况(2026-07-11 审计) | # | 执行项 | ISSUE | 状态 | 说明 | |---|--------|-------|------|------| | 1 | contract.md §2.3 路径统一为 `POST /api/v1/student/graphql` | 014-01 | ✅ 已落实 | contract.md v2 已包含 | | 2 | 02 §4.2 GraphQL query 移除所有 `studentId` 参数 | 014-07 | ✅ 已落实 | 全部 query 无 studentId 参数 | | 3 | 02 §14 防作弊策略补全(全屏 + visibilitychange + 多标签 + 事件上报) | 014-03 | ✅ 已落实 | §14.5 已补全;mutation 名对齐为 `recordExamViolation` | | 4 | 02 §14 粘贴策略题型分层(客观题禁止/主观题允许记录/作文题允许不记录) | 014-04 | ✅ 已落实 | §14.5 已补全分层策略 + `recordPasteEvent` mutation | | 5 | 02 §5.1 实时事件补 ExamExtended + ExamForceSubmitted 命名 | 014-06 | ✅ 已落实 | §5.1 事件表 + §5.2 handlePushMessage 已补全 | | 6 | 02 §4.2 mutation 域确认 recordExamViolation + recordPasteEvent 参数 | 014-03/04 | ✅ 已落实 | §4.2 已对齐 contract.md,补全 markAllAsRead + updateNotificationPreference | ### §8.2 额外修复项(2026-07-11) | # | 修复项 | 说明 | |---|--------|------| | 1 | `SAVE_EXAM_ANSWER` → `SAVE_EXAM_DRAFT` | 对齐 contract.md §2.4.2 mutation 命名 | | 2 | `RECORD_SUSPICIOUS_BEHAVIOR` → `RECORD_EXAM_VIOLATION` | 对齐 contract.md §2.4.2 mutation 命名 | | 3 | `uploadAttachment` mutation 移除 | 改为 REST 端点(ARB-019 §21.5 方案 A) | | 4 | `SuspiciousBehaviorRecord.type` 更新 | `copy-paste` 拆分为 `copy`/`paste`/`paste-essay` | | 5 | §11.3 未决设计决策表更新 | 4 项 ⚠️ → ✅ 已裁决 | | 6 | §12.3 契约表更新 | WebSocket 事件名已裁决 | | 7 | §11.1 假设 6 更新 | 事件名已由 ARB-019 §21.6 裁决确认 | | 8 | §20.5 技术债务表更新 | WebSocket 事件名已裁决 | | 9 | contract.md 过时"待仲裁"标注更新 | 10 处"待 ISSUE-014-XX 仲裁"更新为"已裁决 ARB-019 §XX" | | 10 | workline.md 过时"待仲裁"标注更新 | 4 处"待仲裁"更新为"已裁决 ARB-019" | ### §8.3 当前阶段状态 - **文档阶段**:✅ 完成(01-understanding + 02-architecture-design + contract + workline + issue 全部对齐 ARB-019 裁决) - **代码实现阶段**:✅ 已完成(通过 `606acb6` 合并到 main,包含 P2-P6 全部代码) - **集成测试阶段**:✅ 已完成(通过 `61d8249` + `a6b0c54` 修复全部集成测试 bug) - **待办**:无未完成的架构工作项(contract.md + workline.md + 02-architecture-design.md 全部对齐 ARB-019 裁决) --- **AI Agent**: ai14(student-portal) **Branch**: feat-review-student-portal-docs-9yN6Av **Coordinator**: coord-ai