# student-portal 问题记录

> 负责人：ai14
> 关联：[coord.md](../coord.md)、[contracts/student-portal_contract.md](../contracts/student-portal_contract.md)、[matrix.md](../matrix.md)
> 规则：AI 遇到问题时在此追加条目，coord 仲裁后更新状态

---

## §1 已有仲裁核查（ARB-001 / ARB-002 对 student-portal 的影响）

### 1.1 ARB-001（teacher-bff GraphQL schema 第一版）对 student-portal 的影响核查

| 裁决点                  | 对 student-portal 的适用性                                                                                                                           | ai14 落实方案                                                                                                                 | 状态       |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------- |
| Schema 存放位置         | ✅ 适用（原则一致）。student-bff GraphQL schema 应存放于 `packages/shared-ts/contracts/graphql/student-bff.graphql`（集中管理，与 teacher-bff 同源） | ai14 在 contract.md §1.3 已标注预期路径；实际由 ai04 创建，ai14 仅消费                                                        | ⏳ 待 ai04 |
| P2 Query 范围           | ⚠️ 部分参考。ARB-001 是 teacher-bff 的 P2 范围；student-portal 起步于 P3（不在 P2），因此 student-bff 直接以 P3 全量 Query 起步                      | ai14 在 P3 直接消费 student-bff 全量 Query（currentUser/myClasses/myExams/myHomework/myGrades/myAttendance/studentDashboard） | ✅ 已落实  |
| P2 Mutation 范围        | ⚠️ 部分参考。student-portal P3 起步即需要 submitHomework mutation（作业提交是 P3 核心场景）                                                          | ai14 P3 即消费 submitHomework mutation；ai04 P3 必须提供                                                                      | ⏳ 待 ai04 |
| DataLoader 防 N+1       | ✅ 适用。student-bff 聚合多 gRPC 时（如 studentDashboard 聚合 iam+core-edu+data-ana）必须使用 DataLoader                                             | ai14 不直接实现，但依赖 student-bff 返回结构稳定（无 N+1 慢查询）                                                             | ⏳ 待 ai04 |
| 复杂度限制（depth ≤ 7） | ✅ 适用。student-portal 发起的 GraphQL query 深度必须 ≤ 7                                                                                            | ai14 在 [02-architecture-design.md §4.2](../../../apps/student-portal/docs/02-architecture-design.md) 已设计扁平 query        | ✅ 已落实  |
| ActionState 信封        | ✅ 适用。student-bff 必须返回 ActionState 信封（success/errors/data）                                                                                | ai14 在 GraphQL 请求层（02 §3.2）已处理信封解包 + 降级字段识别                                                                | ✅ 已落实  |
| 降级模式（方案 B）      | ✅ 适用。部分聚合失败时 success=true + data 内 `extensions.degraded: true`                                                                           | ai14 在 [02 §18.2 降级策略矩阵](../../../apps/student-portal/docs/02-architecture-design.md) 已设计 12 个降级场景             | ✅ 已落实  |
| admin 命名空间          | ❌ 不适用。student-portal 不涉及 admin 命名空间                                                                                                      | -                                                                                                                             | -          |

**核查结论**：ARB-001 是 teacher-bff 的 P2 仲裁，但其设计原则（Schema 集中管理、ActionState 信封、降级模式方案 B、复杂度限制）适用于所有 BFF，student-portal 已在 02-architecture-design.md v2 中全面落实。

---

### 1.2 ARB-002（MF Shell 暴露清单）对 student-portal 的影响核查

| 裁决点                               | 对 student-portal 的适用性                                                              | ai14 落实方案                                                                                                                                                                            | 状态      |
| ------------------------------------ | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| Shell 身份                           | ✅ 适用。teacher-portal 是 MF Shell，student-portal 是 P3 首个 Remote                   | ai14 在 [02 §3.2 MF 配置](../../../apps/student-portal/docs/02-architecture-design.md) 已声明 `remotes: { teacher: 'teacher@http://localhost:4000/_next/static/chunks/remoteEntry.js' }` | ✅ 已落实 |
| P3 首个 Remote                       | ✅ 适用。ARB-002 §2.3 明确 P3 首个 Remote 是 student-portal                             | ai14 P3 任务启动即接入 MF Remote                                                                                                                                                         | ✅ 已落实 |
| GraphQL client 归属                  | ✅ 适用。Shell 暴露 GraphQLProvider，student-portal 复用，**不重复创建 client**         | ai14 在 [02 §3.2 GraphQL 请求层](../../../apps/student-portal/docs/02-architecture-design.md) 已使用 `useGraphQLClient()` 从 `@edu/hooks` 获取                                           | ✅ 已落实 |
| MF shared singleton 配置             | ✅ 适用。student-portal 必须将 react/react-dom/urql/graphql/@edu/* 声明为 singleton     | ai14 在 [02 §3.2 next.config.js](../../../apps/student-portal/docs/02-architecture-design.md) 已声明全部 singleton                                                                       | ✅ 已落实 |
| AppShell 复用                        | ✅ 适用。student-portal 不重复实现 AppShell，复用 Shell 暴露的 AppShell                 | ai14 在 02 §3.2 已设计 `<AppShell>` 包裹 + 学生端导航覆写                                                                                                                                | ✅ 已落实 |
| useAuth / usePermission 复用         | ✅ 适用。student-portal 复用 Shell 暴露的 useAuth/usePermission                         | ai14 在 [01-understanding.md §6](../../../apps/student-portal/docs/01-understanding.md) 已声明权限校验走 usePermission                                                                   | ✅ 已落实 |
| ErrorBoundary / Loading / Empty 复用 | ✅ 适用。student-portal 复用 Shell 暴露的共享 UI 组件                                   | ai14 在 02 §6 组件设计已使用 `@edu/ui-components`                                                                                                                                        | ✅ 已落实 |
| feature flag                         | ✅ 适用。`NEXT_PUBLIC_MF_ENABLED` 控制是否走 MF；P3 默认 true                           | ai14 在 02 §3.2 已设计独立壳回退（MF 关闭时独立渲染）                                                                                                                                    | ✅ 已落实 |
| 登录页                               | ✅ 适用。P2 登录页由 Shell 独占；student-portal 不实现登录页，未登录跳转 Shell `/login` | ai14 在 02 §3.2 已设计未认证 → 跳转 `window.location.href = 'http://localhost:4000/login?redirect=student'`                                                                              | ✅ 已落实 |

**核查结论**：ARB-002 是 student-portal 接入 MF 的直接依据，ai14 已在 02-architecture-design.md v2 中全面落实。无异议。

---

### 1.3 ARB-001 / ARB-002 核查总结

| 仲裁    | 对 student-portal 的影响                         | ai14 落实情况 | 异议 |
| ------- | ------------------------------------------------ | ------------- | ---- |
| ARB-001 | 设计原则适用（GraphQL + ActionState + 降级模式） | ✅ 已落实     | 无   |
| ARB-002 | 直接适用（P3 首个 Remote + Shell 暴露清单）      | ✅ 已落实     | 无   |

> **ai14 声明**：ARB-001 / ARB-002 已在 02-architecture-design.md v2 中全面落实，无需新增仲裁。

---

## §2 新提请异议（待 coord 仲裁）

### ISSUE-014-01-ai14：GraphQL endpoint 路径不一致（`/api/student/graphql` vs `/api/v1/student/graphql`）

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - `student-portal_contract.md` §2.3 当前写 `POST /api/student/graphql`（无 `/v1/` 前缀）
  - `matrix.md` §5 HTTP 接口矩阵明确写 `api-gateway` 反向代理 `student-portal` 的路径是 `/api/v1/student/*`
  - `01-understanding.md` v2 §3.1 和 `02-architecture-design.md` v2 §4.1 已统一为 `POST /api/v1/student/graphql`
  - 三处不一致，需要 coord 仲裁统一为 `/api/v1/student/graphql`（与 matrix.md §5 对齐，与 teacher-portal `/api/v1/teacher/graphql` 保持命名一致性）
- **建议方案**：
  - 统一为 `POST /api/v1/student/graphql`
  - 由 ai01（api-gateway）确认路由：`/api/v1/student/*` → `student-bff:3009/*`（即 `/api/v1/student/graphql` → `student-bff:3009/graphql`）
  - 由 ai04（student-bff）确认 GraphQL endpoint 路径为 `POST /graphql`（与 teacher-bff 一致）
- **状态**：待 coord 仲裁

---

### ISSUE-014-02-ai14：student-bff GraphQL schema 文件存放位置不一致（集中管理 vs 应用内管理）

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - ARB-001 §1.3 关键裁决明确：teacher-bff GraphQL schema 存放于 `packages/shared-ts/contracts/graphql/teacher-bff.graphql`（集中管理，总裁裁决 §2.17 SDL-first + 集中管理）
  - `student-bff_contract.md` §1.3 写：`apps/student-bff/src/schema/*.graphql`（应用内管理，与 ARB-001 原则不一致）
  - `matrix.md` §3 GraphQL 接口提供方矩阵写：`packages/shared-ts/contracts/graphql/student-bff.graphql`（与 ARB-001 一致）
  - 两处不一致，需要 coord 仲裁统一
- **建议方案**：
  - 统一为 `packages/shared-ts/contracts/graphql/student-bff.graphql`（与 ARB-001 原则对齐，集中管理便于前端 codegen）
  - ai14 在 student-portal 端使用 `graphql-codegen` 从该 schema 生成 TypeScript 类型
  - 由 ai04（student-bff）创建该 schema 文件并维护
- **状态**：待 coord 仲裁

---

### ISSUE-014-03-ai14：考试作答页全屏策略与防作弊检测边界

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - student-portal 02-architecture-design.md §14 设计了防作弊检测（visibilitychange/copy/paste/fullscreen/contextmenu），但未明确以下边界：
    1. **全屏 API 强制策略**：是否强制全屏（Fullscreen API）？退出全屏是否触发警告/记录？
    2. **离开页面策略**：visibilitychange hidden 触发时，是仅记录还是自动提交？
    3. **多标签检测**：BroadcastChannel 检测到多标签时，是警告还是阻止作答？
    4. **防作弊事件上报**：前端采集的防作弊事件如何上报？走 student-bff GraphQL mutation 还是 push-gateway WebSocket？
  - 这些决策影响 ai04（student-bff）是否需要提供 `recordExamViolation` mutation，以及 ai08（core-edu）是否需要存储违规记录
- **建议方案**：
  - **全屏策略**：P3 推荐但不强制（提示"建议全屏作答"），P4 评估是否升级为强制（基于教师反馈）
  - **离开页面策略**：visibilitychange hidden 触发时仅记录（不自动提交），累计 3 次警告后教师端可见
  - **多标签检测**：警告 + 记录，不阻止作答（避免误伤合法场景如查词典）
  - **防作弊事件上报**：走 student-bff GraphQL mutation `recordExamViolation(examId, type, payload)`，由 ai04 在 P3 提供
- **状态**：待 coord 仲裁

---

### ISSUE-014-04-ai14：主观题粘贴策略（防作弊 vs 学生体验）

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - 02-architecture-design.md §14 防作弊检测包含 `paste` 事件拦截，但学生作答主观题时可能需要粘贴（如从草稿本粘贴长文本）
  - 策略不明确：全部禁止粘贴？仅主观题允许？仅客观题禁止？
  - 影响学生体验和防作弊效果平衡
- **建议方案**：
  - **客观题**：禁止粘贴（防作弊优先）
  - **主观题（简答/论述）**：允许粘贴，但记录粘贴事件 + 粘贴内容长度，教师端批改时可见
  - **作文题**：允许粘贴（学生体验优先），不记录
  - 由 ai04（student-bff）在 P3 提供 `recordPasteEvent` mutation（或复用 ISSUE-014-03 的 `recordExamViolation`，type=`PASTE`）
- **状态**：待 coord 仲裁

---

### ISSUE-014-05-ai14：作业附件上传协议（GraphQL mutation vs REST multipart）

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - 学生提交作业时可能需要上传附件（图片/PDF/文档），GraphQL mutation 不适合处理大文件上传（multipart/form-data）
  - 当前 contract.md 未明确附件上传协议
  - 选项：
    - A. 走 api-gateway REST 端点（`POST /api/v1/student/upload` → 对象存储），返回 URL，再走 GraphQL mutation 提交 URL
    - B. 走 student-bff GraphQL multipart（graphql-upload，需要 ai04 支持）
    - C. 走独立上传服务（如 push-gateway 扩展或新建 upload-service）
- **建议方案**：
  - **推荐 A**：走 api-gateway REST `POST /api/v1/student/upload` → 对象存储（MinIO/OSS），返回 signed URL，再走 GraphQL `submitHomework(attachmentUrls: [String!])` mutation 提交
  - 理由：GraphQL 不适合大文件传输；REST + 对象存储是业界通用方案；api-gateway 已有 JWT 鉴权
  - 由 ai01（api-gateway）确认是否提供 `/api/v1/student/upload` 路由，由 ai04（student-bff）确认 `submitHomework` mutation 是否接受 `attachmentUrls` 字段
- **状态**：待 coord 仲裁

---

### ISSUE-014-06-ai14：考试延长/题目重排等实时事件命名未确认

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - student-portal 02-architecture-design.md §16 设计了 WebSocket 实时通知，但以下事件命名未在 matrix.md §4 Kafka 事件矩阵中确认：
    1. **考试延长**（教师延长考试时间）：事件名 `ExamExtended`？还是 `ExamUpdated`？由 ai08（core-edu）发布？
    2. **题目重排**（教师重排题目顺序）：事件名 `ExamQuestionReordered`？是否需要前端实时重排？
    3. **考试强制提交**（教师强制收卷）：事件名 `ExamForceSubmitted`？前端收到后立即提交？
  - 这些事件影响 student-portal 考试作答页的实时响应逻辑
- **建议方案**：
  - **考试延长**：ai08（core-edu）发布 `ExamExtended` 事件到 `edu.exam.events` topic，msg（ai10）消费后通过 push-gateway 推送，student-portal 收到后更新倒计时
  - **题目重排**：P3 不实现（题目顺序固定），P4 评估是否需要实时重排
  - **考试强制提交**：ai08 发布 `ExamForceSubmitted` 事件，student-portal 收到后立即触发提交流程
  - 由 ai08（core-edu）确认事件命名，由 ai10（msg）确认推送路径
- **状态**：待 coord 仲裁

---

### ISSUE-014-07-ai14：学生端 DataScope L0 边界（仅能查看自己数据）的强制执行层

- **提请方**：ai14
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - 01-understanding.md §8 和 02-architecture-design.md §6 声明学生 DataScope L0（仅能查看自己数据）
  - 但强制执行层不明确：
    - A. student-bff 在 Resolver 层基于 JWT 的 `x-user-id` 强制过滤（推荐，前端无法绕过）
    - B. student-portal 在 GraphQL query 中显式传 `studentId`（不安全，前端可篡改）
  - 当前 02-architecture-design.md §4.2 的 GraphQL query 设计中，部分 query 显式传 `studentId`（如 `myClasses(studentId: ID!)`），这与 L0 强制执行矛盾
- **建议方案**：
  - **统一为方案 A**：student-bff 在 Resolver 层从 JWT `x-user-id` 提取 studentId，强制过滤，前端 query 不传 `studentId` 参数
  - ai14 修改 02-architecture-design.md §4.2 的 GraphQL query 定义，移除 `studentId` 参数（如 `myClasses` 改为无参 query）
  - 由 ai04（student-bff）确认所有学生端 Query 均从 JWT 提取 studentId，不接受前端传入
- **状态**：待 coord 仲裁

---

## §3 异议状态汇总

| 编号         | 类型       | 标题                                                   | 状态                                 |
| ------------ | ---------- | ------------------------------------------------------ | ------------------------------------ |
| ISSUE-014-01 | 契约不明确 | GraphQL endpoint 路径不一致                            | ✅ 已裁决（见 coord.md §21 ARB-019） |
| ISSUE-014-02 | 契约不明确 | student-bff GraphQL schema 存放位置不一致              | ✅ 已裁决（见 coord.md §21 ARB-019） |
| ISSUE-014-03 | 契约不明确 | 考试作答页全屏策略与防作弊检测边界                     | ✅ 已裁决（见 coord.md §21 ARB-019） |
| ISSUE-014-04 | 契约不明确 | 主观题粘贴策略（防作弊 vs 学生体验）                   | ✅ 已裁决（见 coord.md §21 ARB-019） |
| ISSUE-014-05 | 契约不明确 | 作业附件上传协议（GraphQL mutation vs REST multipart） | ✅ 已裁决（见 coord.md §21 ARB-019） |
| ISSUE-014-06 | 契约不明确 | 考试延长/题目重排等实时事件命名未确认                  | ✅ 已裁决（见 coord.md §21 ARB-019） |
| ISSUE-014-07 | 契约不明确 | 学生端 DataScope L0 边界的强制执行层                   | ✅ 已裁决（见 coord.md §21 ARB-019） |

---

## §4 仲裁结论（coord 2026-07-10）

> 详见 [coord.md §21 ARB-019](../coord.md) 完整仲裁章节

| ISSUE  | 仲裁结论摘要                                                                                   | 执行方                           |
| ------ | ---------------------------------------------------------------------------------------------- | -------------------------------- |
| 014-01 | 统一为 `POST /api/v1/student/graphql`（双 /v1 前缀，对齐 ARB-014 方案 A）                      | ai14 修 contract + ai01 确认路由 |
| 014-02 | 统一为 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`                       | ai04 创建并维护                  |
| 014-03 | P3 不强制全屏 + visibilitychange 仅记录 + 多标签警告不阻止 + recordExamViolation mutation      | ai14 + ai04 + ai08 补 RPC        |
| 014-04 | 客观题禁止 / 主观题允许记录 / 作文题允许不记录；复用 recordExamViolation（type=PASTE）         | ai14 + ai04                      |
| 014-05 | 方案 A：api-gateway REST `POST /api/v1/student/upload` → 对象存储 → GraphQL mutation 提交 URL  | ai01 路由 + ai04 mutation        |
| 014-06 | ExamExtended + ExamForceSubmitted（ai08 发布到 `edu.exam.events`）；题目重排 P3 不实现 P4 评估 | ai08 发布 + ai10 推送            |
| 014-07 | 方案 A：student-bff Resolver 从 JWT x-user-id 提取 studentId，前端 query 不传 studentId        | ai14 修 query 设计 + ai04 实现   |

---

<!--
追加条目格式：

### ISSUE-[编号]-[AI标识]：[标题]

- **提请方**：aiXX
- **日期**：YYYY-MM-DD
- **类型**：契约不明确 / 工作量超批 / 前置依赖缺失 / 编号冲突 / 其他
- **描述**：[详细描述问题]
- **建议方案**：[AI 的建议]
- **状态**：待 coord 仲裁 / 已裁决（见 coord.md §X）
-->
