# 模块理解确认书 — student-bff

> AI 标识：ai04
> 阶段：阶段 1（全局理解）
> 日期：2026-07-09
> 状态：已对齐仲裁裁决（coord-final-decisions §2 B1-B8 + president-final-rulings §2.2-2.9）
> 关联文档：[ai-allocation §6 模板](../../docs/architecture/ai-allocation.md)、[004 架构影响地图](../../docs/architecture/004_architecture_impact_map.md)、[pending-features P3](../../docs/architecture/roadmap/pending-features.md)、[coord-final-decisions](../../docs/architecture/coord-final-decisions.md)、[president-final-rulings](../../docs/architecture/president-final-rulings.md)
>
> **仲裁对齐说明（ISSUE-STU-001 修复）**：
> - B1: API 风格 = GraphQL Yoga + DataLoader（P2 起直接 GraphQL，禁止 REST 渐进）
> - B2: 下游通信 = gRPC 首次实现即用（@grpc/grpc-js + @grpc/proto-loader，禁止 HTTP fetch）
> - B5: 错误码前缀 = `BFF_STUDENT_`（BFF 在前，非 `STUDENT_BFF_`）
> - B8: DownstreamClient 抽象复用 shared-ts（回写 teacher-bff，3 BFF 统一）
> - 本文档中早期将通信方式写为 HTTP REST/fetch、错误码前缀写为 `STUDENT_BFF_` 的部分已修正，以本对齐说明为准。

---

## 1. 我在架构中的位置

| 维度           | 内容                                                                          |
| -------------- | ----------------------------------------------------------------------------- |
| 层级           | **L4 BFF 聚合层**（004 §3.1 六层架构）                                        |
| 上游调用方     | api-gateway（Go Gin，反向代理 `/api/v1/student/*` → student-bff:3009）        |
| 下游被调用方   | iam、core-edu、content、data-ana（按 004 §4 服务依赖图）                      |
| 通信方式（入） | **GraphQL Yoga over HTTP**（api-gateway → student-bff:3009，B1 裁决：P2 起直接 GraphQL，禁止 REST 渐进） |
| 通信方式（出） | **gRPC**（@grpc/grpc-js + @grpc/proto-loader，B2 裁决：首次实现即用 gRPC，禁止 HTTP fetch）            |
| 微前端对接     | student-portal（ai07 负责，P3 阶段）通过 api-gateway 调用 student-bff         |
| 推送通道       | push-gateway（P5 阶段，WebSocket/SSE 推送考试通知、成绩发布等）               |

**架构定位**（004 §1.1a + §5.4）：

- 按"使用场景域"分 BFF，student-bff 服务于**学习场景域**，复用角色：学生
- 不按角色分 BFF，新角色复用现有 BFF 通过视口差异化
- DataScope = **SELF（L0）**：学生只能看自己的数据（004 §5.3）

---

## 2. 我的限界上下文

### 2.1 我负责什么

student-bff 是**纯聚合层**，不持有业务状态、不直接访问 DB（对齐 teacher-bff 模式）。职责：

1. **聚合**：并行调用多个下游业务服务，组装学生视角的复合数据
2. **裁剪**：将下游返回的领域数据裁剪为学生端所需的最小字段集
3. **协议转换**：对外暴露场景化 HTTP/GraphQL 端点，对内调用下游 REST/gRPC
4. **缓存**：聚合结果 Redis 短缓存 5-30s（004 §6.2 BFF 混合读策略）

### 2.2 我的聚合场景（学生视角）

| 场景               | 聚合的下游服务                                                              | 用途                           |
| ------------------ | --------------------------------------------------------------------------- | ------------------------------ |
| 学生首页 Dashboard | iam `/iam/me` + core-edu `/homework/class/:classId` + msg `/notifications`  | 个人信息 + 待办作业 + 未读消息 |
| 即将到来的考试     | core-edu `/exams/class/:classId`                                            | 考试日程提醒                   |
| 我的作业列表       | core-edu `/homework/class/:classId`                                         | 查看待完成作业                 |
| 提交作业           | core-edu `/homework/:id/submit`                                             | 学生提交作业答案               |
| 我的成绩           | core-edu `/grades/student/:studentId`                                       | 查询历史成绩                   |
| 消息中心           | msg `/notifications` + `/notifications/:id/read`                            | 通知列表 + 已读                |
| 教材浏览           | content `/textbooks` + `/chapters`                                          | 按章节学习                     |
| 题库练习           | content `/questions`                                                        | 按知识点刷题                   |
| 学情诊断           | data-ana `/analytics/student/:id/weakness` + `/analytics/student/:id/trend` | 自我掌握度分析                 |
| AI 答疑            | ai `/ai/chat` + `/ai/stream-chat`（SSE 流式）                               | 智能答疑辅助                   |
| 个性化学习路径     | content `/knowledge-points/:id/learning-path`                               | 基于学情推荐学习路径           |

### 2.3 我不负责什么（明确边界外）

| 不负责项       | 归属服务                       | 说明                                                              |
| -------------- | ------------------------------ | ----------------------------------------------------------------- |
| 业务数据持久化 | core-edu / content / msg / iam | BFF 不写 DB                                                       |
| 权限校验       | 下游业务服务 + iam             | BFF 不做权限校验（对齐 teacher-bff），透传 `x-user-id` 让下游校验 |
| 用户认证       | iam + api-gateway              | JWT 校验在 Gateway，BFF 只读 `x-user-id` 头                       |
| 领域事件发布   | core-edu / content             | BFF 不发布事件，仅可选订阅事件用于实时推送                        |
| 数据范围过滤   | 下游业务服务 Repository 层     | BFF 透传 userId，下游按 DataScope=SELF 过滤                       |
| 班级管理       | core-edu（classes 模块）       | 学生只读自己所在班级                                              |
| 考试批改       | core-edu                       | 学生不能批改，只能查看成绩                                        |

---

## 3. 我与外部的契约

### 3.1 我消费的 proto message / 下游接口

> ✅ **B2 裁决落地**：BFF→Service 首次实现即用 gRPC（@grpc/grpc-js + @grpc/proto-loader），通过 `DownstreamClient` 抽象（B8 裁决，复用 shared-ts，3 个 BFF 统一）。proto 即契约，不再是"文档"。

| 下游服务            | proto service                                                              | gRPC method（B2 裁决）                                                        | 用途                                 |
| ------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------ |
| iam                 | `IamService.GetUserInfo`                                                   | `iam.GetUserInfo`                                                             | 获取学生个人信息 + roles + dataScope |
| iam                 | `IamService.GetViewports`（proto 缺失）                                    | `iam.GetViewports`                                                            | 获取学生端导航视口                   |
| iam                 | `IamService.GetEffectivePermissions`（proto 缺失）                         | `iam.GetEffectivePermissions`                                                 | 获取有效权限列表                     |
| classes（core-edu） | `ClassService.GetClass` / `ListClasses`                                    | `classes.GetClass` / `classes.ListClasses`                                    | 查自己所在班级                       |
| core-edu            | `ExamService.GetExam` / `ListExamsByClass`                                 | `core-edu.GetExam` / `core-edu.ListExamsByClass`                              | 查班级考试                           |
| core-edu            | `HomeworkService.GetHomework` / `ListHomeworkByClass` / `SubmitHomework`   | `core-edu.ListHomeworkByStudent` / `core-edu.SubmitHomework`                  | 查作业 + 提交                        |
| core-edu            | `GradeService.GetGrade` / `ListGradesByStudent`                            | `core-edu.ListGradesByStudent`                                                | 查自己成绩                           |
| content             | `TextbookService.GetTextbook` / `ListTextbooks`                            | `content.ListTextbooks`                                                       | 查教材                               |
| content             | `ChapterService`（proto 缺失）                                             | `content.ListChapters`                                                        | 查章节                               |
| content             | `QuestionService`（proto 缺失）                                            | `content.ListQuestions`                                                       | 查题库                               |
| content             | `KnowledgeGraphService.GetLearningPath`                                    | `content.GetLearningPath`                                                     | 学习路径                             |
| msg                 | `NotificationService.ListNotifications` / `MarkAsRead` / `SearchNotifications` | `msg.ListNotifications` / `msg.MarkAsRead`                                    | 消息中心                             |
| data-ana            | `AnalyticsService.GetStudentWeakness` / `GetLearningTrend`                 | `data-ana.GetStudentWeakness` / `data-ana.GetLearningTrend`                   | 学情分析                             |
| ai                  | `AiService.Chat` / `StreamChat` / `GenerateQuestion`                       | `ai.Chat`（同步） / `ai.StreamChat`（server-streaming，B2 裁决）              | AI 答疑                              |

### 3.2 我暴露的 GraphQL API（student-bff 对外）

> ✅ **B1 裁决落地**：P2 起直接 GraphQL Yoga（禁止 REST 渐进），schema 存放于 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`（president §2.2.1）。
> 网关路径：`/api/v1/student/*` → api-gateway 剥离 `/api/v1` 后代理到 student-bff:3009 GraphQL endpoint。
> 分页采用 Relay Cursor Connections 规范（`{ edges, pageInfo, totalCount }`）。
> 权限点标注于 schema 注释 `# @permission:`，DataScope 固定 `OWN`（学生数据隔离 SELF）。

#### Query（14 个字段）

| Query 字段                | 聚合下游             | 权限点（注释标注）      | 说明                 |
| ------------------------- | -------------------- | ----------------------- | -------------------- |
| `currentUser`             | iam                  | AUTH_READ               | 学生信息 + 权限 + 视口 |
| `myClasses`               | core-edu             | CLASS_READ              | 我的班级列表         |
| `myExams`                 | core-edu             | EXAM_READ               | 即将到来的考试       |
| `myHomework`              | core-edu             | HOMEWORK_READ           | 我的作业列表         |
| `myGrades`                | core-edu             | GRADE_READ              | 我的成绩（B4 比对）  |
| `myAttendance`            | core-edu             | ATTENDANCE_READ         | 我的考勤记录         |
| `textbooks`               | content              | TEXTBOOK_READ           | 教材列表             |
| `chapters`                | content              | CHAPTER_READ            | 章节树               |
| `learningPath`            | content              | LEARNING_PATH_READ      | 学习路径推荐         |
| `studentDashboard`        | data-ana             | DASHBOARD_VIEW          | 学生仪表盘聚合       |
| `myWeakness`              | data-ana             | WEAKNESS_READ           | 学情诊断（薄弱点）   |
| `myTrend`                 | data-ana             | TREND_READ              | 学习趋势             |
| `myNotifications`         | msg                  | NOTIFICATION_READ       | 通知列表             |
| `myNotificationUnreadCount` | msg                | NOTIFICATION_READ       | 通知未读数           |

#### Mutation（2 个字段）

| Mutation 字段                | 下游                  | 权限点              | 说明                         |
| ---------------------------- | --------------------- | ------------------- | ---------------------------- |
| `submitHomework`             | core-edu.SubmitHomework | HOMEWORK_SUBMIT   | 提交作业（B4 强制 userId）   |
| `markNotificationAsRead`     | msg.MarkAsRead        | NOTIFICATION_UPDATE | 标记通知已读（B4 强制 userId）|

#### Subscription（1 个字段，SSE 传输）

| Subscription 字段 | 下游                | 权限点          | 说明                          |
| ----------------- | ------------------- | --------------- | ----------------------------- |
| `aiStreamChat`    | ai.StreamChat       | STUDENT_AI_CHAT | AI 答疑流式响应（gRPC server-streaming 透传） |

### 3.3 错误码前缀

> ✅ **B5 裁决**：BFF 在前，统一 `BFF_STUDENT_` 前缀（非 `STUDENT_BFF_`）。
> **G14 裁决**：服务名大写前缀。**F4 裁决**：i18n key 格式 `error.bffStudent.<code_snake>`。

| 前缀           | 用途                   | 示例                                                          |
| -------------- | ---------------------- | ------------------------------------------------------------- |
| `BFF_STUDENT_` | student-bff 自身错误   | `BFF_STUDENT_UNAUTHORIZED`、`BFF_STUDENT_BAD_GATEWAY`         |
| 下游错误透传   | 下游服务错误码原样返回 | `CLASSES_NOT_FOUND`、`IAM_USER_NOT_FOUND`                     |

错误类清单（`shared/errors/application-error.ts`，11 个类，G8 ActionState 信封）：

| 错误类                         | statusCode | code                              | 说明                          |
| ------------------------------ | ---------- | --------------------------------- | ----------------------------- |
| `ValidationError`              | 400        | `BFF_STUDENT_VALIDATION_ERROR`    | Zod 校验失败                  |
| `UnauthorizedError`            | 401        | `BFF_STUDENT_UNAUTHORIZED`        | 缺失 `x-user-id` 头           |
| `ForbiddenResourceError`       | 403        | `BFF_STUDENT_FORBIDDEN_RESOURCE`  | 场景 A：资源无归属（president §2.7） |
| `IdentityMismatchError`        | 403        | `BFF_STUDENT_IDENTITY_MISMATCH`   | 场景 B：JWT/body userId 不一致（president §2.7） |
| `NotFoundError`                | 404        | `BFF_STUDENT_NOT_FOUND`           | 资源不存在                    |
| `ConflictError`                | 409        | `BFF_STUDENT_CONFLICT`            | 重复提交 / 状态冲突           |
| `BusinessError`                | 422        | `BFF_STUDENT_BUSINESS_ERROR`      | 业务规则违反                  |
| `BadGatewayError`              | 502        | `BFF_STUDENT_BAD_GATEWAY`         | 下游 gRPC 失败                |
| `ServiceUnavailableError`      | 503        | `BFF_STUDENT_SERVICE_UNAVAILABLE` | 熔断器开启（P6）              |
| `GatewayTimeoutError`          | 504        | `BFF_STUDENT_GATEWAY_TIMEOUT`     | 下游超时                      |
| `InternalError`                | 500        | `BFF_STUDENT_INTERNAL_ERROR`      | 未捕获异常                    |

### 3.4 我订阅的 Kafka 事件（P5 起订阅，B7 裁决）

> ✅ **B7 裁决**：P2-P4 阶段不订阅 Kafka，P5 起 student-bff 订阅事件用于实时推送。
> 消费者组：`student-bff-event-subscriber`。幂等去重：Redis SETNX `event_id`。
> 推送通道：push-gateway HTTP POST `/push/user/:userId`（失败软处理，仅 warn 日志）。

| Topic                      | 事件                                    | 消费动作                       |
| -------------------------- | --------------------------------------- | ------------------------------ |
| `edu.homework.events`      | `homework.assigned` / `homework.graded` | 推送给学生（push-gateway）     |
| `edu.exam.events`          | `exam.published` / `exam.updated`       | 考试提醒推送                   |
| `edu.grade.events`         | `grade.recorded`                        | 成绩发布推送                   |
| `edu.notification.events`  | `notification.created`                  | 通知推送                       |
| `edu.attendance.events`    | `attendance.recorded`                   | 考勤提醒推送                   |
| `edu.class.events`         | `class.updated`                         | 班级信息变更推送               |
| `edu.content.events`       | `content.published`                     | 教材/章节更新推送             |

> ⚠️ P3-P4 阶段 student-bff 不消费事件，仅做同步聚合。事件订阅逻辑在 `src/student/events/event-subscriber.ts`。

---

## 4. 我的技术栈

| 维度         | 选型                                 | 依据                                                                                                                                   |
| ------------ | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| 语言         | TypeScript 5.5+                      | 004 §2.1                                                                                                                               |
| 框架         | NestJS 10                            | 004 §2.1，对齐 teacher-bff 模板                                                                                                        |
| ORM          | **无**（BFF 不访问 DB）              | 对齐 teacher-bff，无 repository/schema/dto                                                                                             |
| 缓存         | Redis 7（短缓存 5-30s）              | 004 §6.2 BFF 混合读策略                                                                                                                |
| 可观测性日志 | pino                                 | 对齐 classes/teacher-bff                                                                                                               |
| 可观测性指标 | prom-client（`/metrics` 端点）       | 对齐 teacher-bff main.ts                                                                                                               |
| 可观测性链路 | OpenTelemetry SDK + OTLP exporter    | 对齐 teacher-bff tracer.ts                                                                                                             |
| API 风格     | **GraphQL Yoga**（B1 裁决）          | P2 起直接 GraphQL + DataLoader，禁止 REST 渐进；schema 存放 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` |
| 下游通信     | **gRPC**（B2 裁决）                  | @grpc/grpc-js + @grpc/proto-loader，DownstreamClient 抽象（B8 复用 shared-ts）                                           |
| 输入校验     | Zod                                  | Resolver 层 `schema.safeParse(args.input)`（G7 裁决）                                                                    |
| 错误处理     | GlobalErrorFilter + ApplicationError | 11 个错误类，ActionState 信封（G8 裁决）                                                                                 |
| ESM 模式     | NodeNext + `.js` 后缀 import         | 对齐 teacher-bff tsconfig                                                                                               |
| 测试框架     | **Vitest**                           | 覆盖率 ≥ 80%（lines/functions），branches ≥ 70%                                                                          |

### 4.1 GraphQL 设计决策（已裁决 B1）

> ✅ **B1 裁决**：student-bff 从 P2 起直接采用 GraphQL Yoga + DataLoader，禁止 REST 渐进。

**裁决结论**：

- **API 风格**：GraphQL Yoga over HTTP（SSE 传输 Subscription）
- **DataLoader**：解决 N+1 查询问题，按下游服务分批聚合
- **schema 存放**：`packages/shared-ts/contracts/graphql/student-bff.schema.graphql`（president §2.2.1）
- **分页规范**：Relay Cursor Connections（`{ edges, pageInfo, totalCount }`）
- **降级模式**：方案 B（president §2.6），`success=true + data 内 degraded=true + degradedFields`
- **越权防御**：B4 强制自我越权防御，AuthorizationGuard 拦截（president §2.9 方案 D，DEV_MODE 放行）

**已落地的 GraphQL 核心文件**：

| 文件                                          | 职责                                              |
| --------------------------------------------- | ------------------------------------------------- |
| `src/shared/graphql/yoga.ts`                  | GraphQL Yoga 实例 + context 构建                  |
| `src/shared/graphql/dataloader.ts`            | DataLoader 工厂（按下游服务分批）                 |
| `src/student/resolvers/*.resolver.ts`         | Query/Mutation/Subscription Resolver（6 个文件）  |
| `src/student/guards/authorization.guard.ts`   | B4 越权防御（assertOwnData + assertIdentityMatch）|
| `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` | GraphQL schema 定义 |

---

## 5. 我的阶段归属

| 维度                            | 内容                                                                                                                            |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| 阶段                            | **P3 核心教学阶段**（M7-M10）                                                                                                   |
| 退出标准（pending-features P3） | 教师创建考试 → 发布 → 学生作答提交 → 教师批改 → 事件发到 Kafka → 成绩统计更新 → 全链路可观测                                    |
| student-bff 在 P3 的最小交付    | 学生作答作业页面所需 API：`/student/homework` 列表 + `/student/homework/:id/submit` 提交 + `/student/grades` 成绩查看           |
| 依赖上游阶段产出                | P1（api-gateway 路由骨架 + classes 黄金模板 + shared-proto）、P2（iam 认证 + teacher-bff BFF 模板 + teacher-portal 微前端骨架） |
| P3 同阶段依赖                   | core-edu（考试/作业/成绩域 CRUD + Outbox 事件）                                                                                 |
| P4 阶段扩展                     | 学情诊断查询（双轨读：实时查 core-edu 主库 + 聚合查 data-ana ClickHouse 宽表）                                                  |
| P5 阶段扩展                     | AI 答疑流式响应 + Kafka 事件订阅推送                                                                                            |

### 5.1 P3 阶段最小可行集合（MVP）

student-bff 在 P3 阶段不一定要实现全部 17 个 GraphQL 字段，优先级：

| 优先级 | GraphQL 字段                                      | P3 必需 | 说明                      |
| ------ | ------------------------------------------------- | ------- | ------------------------- |
| P0     | `Query.myHomework` + `Mutation.submitHomework`    | ✅      | 学生作答作业页面核心      |
| P0     | `Query.myGrades`                                  | ✅      | 成绩查看                  |
| P0     | `Query.currentUser`                               | ✅      | 学生信息 + 权限 + 视口    |
| P0     | `Query.studentDashboard`                          | ✅      | 学生首页聚合              |
| P1     | `Query.myExams`                                   | ✅      | 考试日程                  |
| P1     | `Query.myNotifications`                           | ⚠️ 可选 | P5 msg 服务落地后才有意义 |
| P2     | `Query.textbooks` / `chapters` / `learningPath`   | ❌ P4   | content 服务 P4 才落地    |
| P2     | `Query.myWeakness` / `myTrend`                    | ❌ P4   | data-ana 学情诊断 P4      |
| P2     | `Subscription.aiStreamChat`                       | ❌ P5   | ai 服务 P5                |

---

## 6. 我需要对齐的黄金模板项（对照 classes 服务）

> 对照 ai-allocation.md §6 模板第 6 节 + §10 审计模板

| 对齐项                          | classes 黄金模板                         | student-bff 计划                                             | 备注                                                              |
| ------------------------------- | ---------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------- |
| 权限装饰器 `@RequirePermission` | ✅ 全部 Controller 方法                  | ⚠️ **B3 豁免**                                               | BFF 豁免 `@RequirePermission`（B3 裁决），但强制自我越权防御（B4，AuthorizationGuard） |
| 错误码前缀统一                  | ✅ `CLASSES_`                            | ✅ `BFF_STUDENT_`                                            | B5 裁决：BFF 在前，非 `STUDENT_BFF_`                              |
| logger（pino）                  | ✅ `shared/observability/logger.ts`      | ✅ 复制 teacher-bff 实现                                     | service 名改 `student-bff`                                        |
| metrics（prom-client）          | ✅ `/metrics` 端点                       | ✅ 复制 teacher-bff main.ts 注册方式                         | 指标名前缀 `student_bff_`                                         |
| tracer（OpenTelemetry）         | ✅ OTLP exporter + auto-instrumentations | ✅ 复制 teacher-bff tracer.ts                                | serviceName 改 `student-bff`                                      |
| `/healthz` 健康检查             | ✅ liveness                              | ✅ 复制 teacher-bff                                          | BFF 不查 DB，直接返回 ok                                          |
| `/readyz` 健康检查              | ✅ Drizzle `SELECT 1`                    | ✅ 检查下游 6 个服务可达性                                   | 必需失败返回 503，可选软失败返回 200 + degraded=true              |
| 优雅关闭（SIGTERM）             | ✅ LifecycleService 关闭 DB 连接池       | ✅ main.ts 注册 SIGTERM → `app.close()` + `shutdownTracer()` + `circuitBreaker.shutdown()` | BFF 无 DB 连接，关闭 HTTP server + tracer + 熔断器 |
| 测试覆盖率 ≥ 80%                | ✅ Jest                                  | ✅ **Vitest**（已落地）                                      | 5 个测试文件：action-state / application-error / authorization.guard / homework.resolver / push-gateway.service |
| Dockerfile 多阶段构建           | ✅ builder + runtime                     | ✅ 复制 teacher-bff Dockerfile                               | EXPOSE 改 3009                                                    |
| Zod 输入验证                    | ✅ Controller 层 `schema.parse(body)`    | ✅ Controller 层校验                                         | 提交作业 body 需 Zod 校验                                         |
| GlobalErrorFilter               | ✅ `@Catch()` 全局过滤器                 | ✅ 复制 teacher-bff                                          | 注册到 main.ts                                                    |
| ESM `.js` 后缀 import           | ✅ tsconfig NodeNext                     | ✅ 复制 teacher-bff tsconfig                                 | 所有相对 import 带 `.js`                                          |
| `import type` 纯类型导入        | ✅                                       | ✅                                                           | 对齐 classes 规范                                                 |
| 环境变量 Zod 校验               | ✅ `config/env.ts`                       | ✅ 复制 teacher-bff env.ts                                   | 下游 URL 配置项扩展                                               |

### 6.1 与 teacher-bff 模板的差异点（克隆时必须改）

| 文件                                  | teacher-bff 现值                                      | student-bff 应改为                                                                   |
| ------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `package.json` name                   | `@edu/teacher-bff`                                    | `@edu/student-bff`                                                                   |
| `src/config/env.ts` `PORT` default    | `"3003"`                                              | `"3009"`                                                                             |
| `src/config/env.ts` 下游 URL          | IamServiceUrl / ClassesServiceUrl / CoreEduServiceUrl | + ContentServiceUrl / DataAnaServiceUrl / MsgServiceUrl / AiServiceUrl（按聚合需求） |
| `src/teacher/` 目录名                 | `teacher/`                                            | `student/`                                                                           |
| `@Controller("teacher")`              | `"teacher"`                                           | `"student"`                                                                          |
| `health.controller.ts` `SERVICE_NAME` | `"teacher-bff"`                                       | `"student-bff"`                                                                      |
| `application-error.ts` 错误码前缀     | `TEACHER_BFF_`                                        | `BFF_STUDENT_`（B5 裁决：BFF 在前）                                                  |
| `metrics.ts` 指标名前缀               | `teacher_bff_`                                        | `student_bff_`                                                                       |
| `tracer.ts` serviceName               | `"teacher-bff"`                                       | `"student-bff"`                                                                      |
| `logger.ts` service                   | `"teacher-bff"`                                       | `"student-bff"`                                                                      |
| `main.ts` 启动日志                    | `"Teacher BFF started"`                               | `"Student BFF started"`                                                              |
| `Dockerfile` `EXPOSE`                 | `3003`                                                | `3009`                                                                               |

---

## 7. 风险与依赖（待 coord 仲裁）

### 7.1 上游依赖缺口

| 风险                                                                       | 影响                                | 缓解措施                                                                            |
| -------------------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------- |
| data-ana 服务未实现查询 API（analytics.proto 3 个 method 无 REST 端点）    | P4 学情诊断端点无法实现             | P3 阶段先不实现 `/student/analytics/*`，等 ai06 在 P4 实现 data-ana 查询 API 后再补 |
| ai 服务未实现 REST/gRPC 端点                                               | P5 AI 答疑端点无法实现              | P3/P4 阶段先不实现 `/student/ai/*`，等 ai06 在 P5 实现 ai 服务后再补                |
| content.proto 缺 Chapter/Question 域                                       | P4 教材/题库端点 proto 契约不全     | 推动 coord 在 shared-proto 补全 content.proto                                       |
| iam.proto 缺 Viewport/EffectivePermissions                                 | 学生端导航视口 proto 契约不全       | 当前走 REST `/iam/viewports`，proto 补全后切换                                      |
| 出勤（attendance）全局缺失                                                 | 学生端无法查出勤                    | 推动 coord 在 core_edu.proto 补 Attendance 域（P3 后期或 P4）                       |
| 学生-家长关联表缺失（pending-features P2 提到 `parent_student_relations`） | 影响 parent-bff，不影响 student-bff | 报告给 coord，由 ai02 在 iam 或 ai03 在 core-edu 补表                               |

### 7.2 设计决策（已裁决 B1-B8）

> ✅ 全部 8 项决策已由 coord-final-decisions §2 B1-B8 + president-final-rulings §2.2-2.9 裁决。

| 决策点             | 裁决编号 | 裁决结论                                                                                       |
| ------------------ | -------- | ---------------------------------------------------------------------------------------------- |
| BFF API 风格       | **B1**   | GraphQL Yoga + DataLoader（P2 起直接 GraphQL，禁止 REST 渐进）                                 |
| 下游通信           | **B2**   | gRPC 首次实现即用（@grpc/grpc-js + @grpc/proto-loader，禁止 HTTP fetch）                       |
| BFF 权限校验       | **B3**   | BFF 豁免 `@RequirePermission`，但透传 `x-user-id` 给下游校验                                   |
| 自我越权防御       | **B4**   | 强制 B4 越权防御（AuthorizationGuard，场景 A + 场景 B，president §2.9 方案 D DEV_MODE 放行）   |
| 错误码前缀         | **B5**   | `BFF_STUDENT_`（BFF 在前，非 `STUDENT_BFF_`）                                                  |
| Redis 缓存         | **B6**   | 5-30s 短缓存，TTL ±20% 随机抖动防雪崩                                                          |
| Kafka 订阅         | **B7**   | P2-P4 不订阅，P5 起订阅 7 个 topic，Redis SETNX `event_id` 幂等去重                            |
| DownstreamClient   | **B8**   | 抽象复用 shared-ts，回写 teacher-bff，3 个 BFF 统一使用                                        |
| 降级模式           | president §2.6 | 方案 B：`success=true + data 内 degraded=true + degradedFields`                          |
| 越权防御错误码     | president §2.7 | 3 类：ForbiddenResourceError / IdentityMismatchError / DEV_MODE 放行                     |
| GraphQL schema 存放 | president §2.2.1 | `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`                       |
| 端口分配           | -        | 3009（HTTP），无 gRPC 端口对外                                                                 |

### 7.3 跨模块协作需求（需提交 coord 协调）

| 需求                                                         | 涉及 AI        | 协调内容                                                  |
| ------------------------------------------------------------ | -------------- | --------------------------------------------------------- |
| api-gateway 新增 `/student` 路由                             | ai01           | 在 main.go + config.go 新增 `StudentBffURL` 字段 + 路由块 |
| docker-compose.deploy.yml 新增 student-bff 服务定义          | coord（infra） | 端口 3009，加入 edu-net + edu-shared 网络                 |
| full-stack-runbook 端口矩阵更新                              | coord（docs）  | 追加 3009 行                                              |
| 004 架构图状态更新                                           | coord（docs）  | student-bff 状态从"📐 需设计"改为"✅ 已实现"              |
| shared-proto 补全 content.proto（Chapter/Question）          | coord          | P4 阶段 content 服务落地前补全                            |
| shared-proto 补全 iam.proto（Viewport/EffectivePermissions） | coord          | 推动 ai02 补 proto                                        |
| buf.gen.yaml 补 gRPC 插件                                    | coord          | ✅ B2 裁决已落地：TS 走 @grpc/proto-loader 动态加载，无需 buf generate gRPC 插件 |

---

## 8. 阶段 1 自检结论

| 检查项                               | 状态                        |
| ------------------------------------ | --------------------------- |
| 已读必读文档清单（ai-allocation §4） | ✅                          |
| 已运行 arch:scan 更新 arch.db        | ✅                          |
| 已查 arch:query modules / stats      | ✅                          |
| 已读 classes 黄金模板源码            | ✅                          |
| 已读 teacher-bff BFF 模板源码        | ✅                          |
| 已读 iam 认证服务源码                | ✅                          |
| 已读 shared-proto 全部 .proto        | ✅                          |
| 已识别 proto 契约缺口                | ✅（见 §7.1）               |
| 已识别端口/路由预留情况              | ✅（3009 可用，路由未预留） |
| 已识别设计决策待仲裁项               | ✅ → **已裁决**（B1-B8 + president §2.2-2.9，见 §7.2） |
| 已识别跨模块协作需求                 | ✅（见 §7.3）               |

**ai04 阶段 1 交付完成，已对齐仲裁裁决（coord-final-decisions B1-B8 + president-final-rulings §2.2-2.9）。P3-P6 全部代码已实现。**
