Files
Edu/services/student-bff/docs/01-understanding.md
SpecialX f585080e70 feat(student-bff): 完整实现 student-bff 聚合层
包含 src 全部实现、Dockerfile、shared-ts/bff 包等
2026-07-10 19:10:51 +08:00

364 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 模块理解确认书 — 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 + DataLoaderP2 起直接 GraphQL禁止 REST 渐进)
> - B2: 下游通信 = gRPC 首次实现即用(@grpc/grpc-js + @grpc/proto-loader禁止 HTTP fetch
> - B5: 错误码前缀 = `BFF_STUDENT_`BFF 在前,非 `STUDENT_BFF_`
> - B8: DownstreamClient 抽象复用 shared-ts回写 teacher-bff3 BFF 统一)
> - 本文档中早期将通信方式写为 HTTP REST/fetch、错误码前缀写为 `STUDENT_BFF_` 的部分已修正,以本对齐说明为准。
---
## 1. 我在架构中的位置
| 维度 | 内容 |
| -------------- | ----------------------------------------------------------------------------- |
| 层级 | **L4 BFF 聚合层**004 §3.1 六层架构) |
| 上游调用方 | api-gatewayGo Gin反向代理 `/api/v1/student/*` → student-bff:3009 |
| 下游被调用方 | iam、core-edu、content、data-ana按 004 §4 服务依赖图) |
| 通信方式(入) | **GraphQL Yoga over HTTP**api-gateway → student-bff:3009B1 裁决P2 起直接 GraphQL禁止 REST 渐进) |
| 通信方式(出) | **gRPC**@grpc/grpc-js + @grpc/proto-loaderB2 裁决:首次实现即用 gRPC禁止 HTTP fetch |
| 微前端对接 | student-portalai07 负责P3 阶段)通过 api-gateway 调用 student-bff |
| 推送通道 | push-gatewayP5 阶段WebSocket/SSE 推送考试通知、成绩发布等) |
**架构定位**004 §1.1a + §5.4
- 按"使用场景域"分 BFFstudent-bff 服务于**学习场景域**,复用角色:学生
- 不按角色分 BFF新角色复用现有 BFF 通过视口差异化
- DataScope = **SELFL0**学生只能看自己的数据004 §5.3
---
## 2. 我的限界上下文
### 2.1 我负责什么
student-bff 是**纯聚合层**,不持有业务状态、不直接访问 DB对齐 teacher-bff 模式)。职责:
1. **聚合**:并行调用多个下游业务服务,组装学生视角的复合数据
2. **裁剪**:将下游返回的领域数据裁剪为学生端所需的最小字段集
3. **协议转换**:对外暴露场景化 HTTP/GraphQL 端点,对内调用下游 REST/gRPC
4. **缓存**:聚合结果 Redis 短缓存 5-30s004 §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 校验在 GatewayBFF 只读 `x-user-id` 头 |
| 领域事件发布 | core-edu / content | BFF 不发布事件,仅可选订阅事件用于实时推送 |
| 数据范围过滤 | 下游业务服务 Repository 层 | BFF 透传 userId下游按 DataScope=SELF 过滤 |
| 班级管理 | core-educlasses 模块) | 学生只读自己所在班级 |
| 考试批改 | core-edu | 学生不能批改,只能查看成绩 |
---
## 3. 我与外部的契约
### 3.1 我消费的 proto message / 下游接口
> ✅ **B2 裁决落地**BFF→Service 首次实现即用 gRPC@grpc/grpc-js + @grpc/proto-loader通过 `DownstreamClient` 抽象B8 裁决,复用 shared-ts3 个 BFF 统一。proto 即契约,不再是"文档"。
| 下游服务 | proto service | gRPC methodB2 裁决) | 用途 |
| ------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------ |
| iam | `IamService.GetUserInfo` | `iam.GetUserInfo` | 获取学生个人信息 + roles + dataScope |
| iam | `IamService.GetViewports`proto 缺失) | `iam.GetViewports` | 获取学生端导航视口 |
| iam | `IamService.GetEffectivePermissions`proto 缺失) | `iam.GetEffectivePermissions` | 获取有效权限列表 |
| classescore-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-streamingB2 裁决) | AI 答疑 |
### 3.2 我暴露的 GraphQL APIstudent-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
#### Query14 个字段)
| 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 | 通知未读数 |
#### Mutation2 个字段)
| Mutation 字段 | 下游 | 权限点 | 说明 |
| ---------------------------- | --------------------- | ------------------- | ---------------------------- |
| `submitHomework` | core-edu.SubmitHomework | HOMEWORK_SUBMIT | 提交作业B4 强制 userId |
| `markNotificationAsRead` | msg.MarkAsRead | NOTIFICATION_UPDATE | 标记通知已读B4 强制 userId|
#### Subscription1 个字段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` | 场景 BJWT/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 阶段不订阅 KafkaP5 起 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-loaderDownstreamClient 抽象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/functionsbranches ≥ 70% |
### 4.1 GraphQL 设计决策(已裁决 B1
> ✅ **B1 裁决**student-bff 从 P2 起直接采用 GraphQL Yoga + DataLoader禁止 REST 渐进。
**裁决结论**
- **API 风格**GraphQL Yoga over HTTPSSE 传输 Subscription
- **DataLoader**:解决 N+1 查询问题,按下游服务分批聚合
- **schema 存放**`packages/shared-ts/contracts/graphql/student-bff.schema.graphql`president §2.2.1
- **分页规范**Relay Cursor Connections`{ edges, pageInfo, totalCount }`
- **降级模式**:方案 Bpresident §2.6`success=true + data 内 degraded=true + degradedFields`
- **越权防御**B4 强制自我越权防御AuthorizationGuard 拦截president §2.9 方案 DDEV_MODE 放行)
**已落地的 GraphQL 核心文件**
| 文件 | 职责 |
| --------------------------------------------- | ------------------------------------------------- |
| `src/shared/graphql/yoga.ts` | GraphQL Yoga 实例 + context 构建 |
| `src/shared/graphql/dataloader.ts` | DataLoader 工厂(按下游服务分批) |
| `src/student/resolvers/*.resolver.ts` | Query/Mutation/Subscription Resolver6 个文件) |
| `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` 成绩查看 |
| 依赖上游阶段产出 | P1api-gateway 路由骨架 + classes 黄金模板 + shared-proto、P2iam 认证 + 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 裁决但强制自我越权防御B4AuthorizationGuard |
| 错误码前缀统一 | ✅ `CLASSES_` | ✅ `BFF_STUDENT_` | B5 裁决BFF 在前,非 `STUDENT_BFF_` |
| loggerpino | ✅ `shared/observability/logger.ts` | ✅ 复制 teacher-bff 实现 | service 名改 `student-bff` |
| metricsprom-client | ✅ `/metrics` 端点 | ✅ 复制 teacher-bff main.ts 注册方式 | 指标名前缀 `student_bff_` |
| tracerOpenTelemetry | ✅ 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 服务未实现查询 APIanalytics.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 + DataLoaderP2 起直接 GraphQL禁止 REST 渐进) |
| 下游通信 | **B2** | gRPC 首次实现即用(@grpc/grpc-js + @grpc/proto-loader禁止 HTTP fetch |
| BFF 权限校验 | **B3** | BFF 豁免 `@RequirePermission`,但透传 `x-user-id` 给下游校验 |
| 自我越权防御 | **B4** | 强制 B4 越权防御AuthorizationGuard场景 A + 场景 Bpresident §2.9 方案 D DEV_MODE 放行) |
| 错误码前缀 | **B5** | `BFF_STUDENT_`BFF 在前,非 `STUDENT_BFF_` |
| Redis 缓存 | **B6** | 5-30s 短缓存TTL ±20% 随机抖动防雪崩 |
| Kafka 订阅 | **B7** | P2-P4 不订阅P5 起订阅 7 个 topicRedis SETNX `event_id` 幂等去重 |
| DownstreamClient | **B8** | 抽象复用 shared-ts回写 teacher-bff3 个 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` |
| 端口分配 | - | 3009HTTP无 gRPC 端口对外 |
### 7.3 跨模块协作需求(需提交 coord 协调)
| 需求 | 涉及 AI | 协调内容 |
| ------------------------------------------------------------ | -------------- | --------------------------------------------------------- |
| api-gateway 新增 `/student` 路由 | ai01 | 在 main.go + config.go 新增 `StudentBffURL` 字段 + 路由块 |
| docker-compose.deploy.yml 新增 student-bff 服务定义 | coordinfra | 端口 3009加入 edu-net + edu-shared 网络 |
| full-stack-runbook 端口矩阵更新 | coorddocs | 追加 3009 行 |
| 004 架构图状态更新 | coorddocs | student-bff 状态从"📐 需设计"改为"✅ 已实现" |
| shared-proto 补全 content.protoChapter/Question | coord | P4 阶段 content 服务落地前补全 |
| shared-proto 补全 iam.protoViewport/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 全部代码已实现。**