NestJS (6 services): implement @RequirePermission decorator with SetMetadata+Reflector, register APP_GUARD globally, fix as assertions to type guards, add explicit return types, fix import type for express, fix /metrics implicit any, replace native Error with ApplicationError, remove typeorm remnants, register LifecycleService. teacher-bff: add logger, ApplicationError, GlobalErrorFilter, forward real userId to downstream, log downstream failures, migrate health controller to shared/health. Go (2 services): interface to any, doc comments, CORS dev whitelist, JWT secret fail-fast, push-gateway internal API auth, metrics and readyz endpoints, remove dead code. Python (2 services): lifespan return type, dev_mode to bool, data-ana APIRouter, ai POST body model, ClickHouse async wrapping.
32 KiB
模块理解确认书 — student-bff
AI 标识:ai04 阶段:阶段 1(全局理解) 日期:2026-07-09 状态:待 coord 审核 关联文档:ai-allocation §6 模板、004 架构影响地图、pending-features P3
1. 我在架构中的位置
| 维度 | 内容 |
|---|---|
| 层级 | L4 BFF 聚合层(004 §3.1 六层架构) |
| 上游调用方 | api-gateway(Go Gin,反向代理 /api/v1/student/* → student-bff:3009) |
| 下游被调用方 | iam、core-edu、content、data-ana(按 004 §4 服务依赖图) |
| 通信方式(入) | HTTP REST(api-gateway → student-bff,当前阶段);设计意图为 gRPC(004 §4.1) |
| 通信方式(出) | HTTP fetch(当前阶段,对齐 teacher-bff 模式);设计意图为 gRPC(004 §4.1) |
| 微前端对接 | 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 模式)。职责:
- 聚合:并行调用多个下游业务服务,组装学生视角的复合数据
- 裁剪:将下游返回的领域数据裁剪为学生端所需的最小字段集
- 协议转换:对外暴露场景化 HTTP/GraphQL 端点,对内调用下游 REST/gRPC
- 缓存:聚合结果 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 / 下游接口
⚠️ 重要差距:当前阶段 BFF→Service 走 HTTP fetch(对齐 teacher-bff 现状),proto 仅作"契约文档"。gRPC 落地需 coord 在 buf.gen.yaml 补 gRPC 插件。
| 下游服务 | proto service(设计意图) | 当前 REST 端点(实际可用) | 用途 |
|---|---|---|---|
| iam | IamService.GetUserInfo |
GET /iam/me |
获取学生个人信息 + roles + dataScope |
| iam | IamService.GetViewports(proto 缺失) |
GET /iam/viewports |
获取学生端导航视口 |
| iam | IamService.GetEffectivePermissions(proto 缺失) |
GET /iam/permissions/effective |
获取有效权限列表 |
| classes(core-edu) | ClassService.GetClass / ListClasses |
GET /classes / GET /classes/:id |
查自己所在班级 |
| core-edu | ExamService.GetExam / ListExamsByClass |
GET /exams/class/:classId |
查班级考试 |
| core-edu | HomeworkService.GetHomework / ListHomeworkByClass / SubmitHomework |
GET /homework/class/:classId / POST /homework/:id/submit |
查作业 + 提交 |
| core-edu | GradeService.GetGrade / ListGradesByStudent |
GET /grades/student/:studentId |
查自己成绩 |
| content | TextbookService.GetTextbook / ListTextbooks |
GET /textbooks |
查教材 |
| content | ChapterService(proto 缺失) |
GET /chapters / GET /chapters/:id |
查章节 |
| content | QuestionService(proto 缺失) |
GET /questions |
查题库 |
| content | KnowledgeGraphService.GetLearningPath |
GET /knowledge-points/:id/learning-path |
学习路径 |
| msg | NotificationService.ListNotifications / MarkAsRead / SearchNotifications |
GET /notifications / POST /notifications/:id/read |
消息中心 |
| data-ana | AnalyticsService.GetStudentWeakness / GetLearningTrend |
REST 未实现 | 学情分析 |
| ai | AiService.Chat / StreamChat / GenerateQuestion |
REST 未实现 | AI 答疑 |
3.2 我暴露的 API 端点(student-bff 对外)
路由前缀:
/student(对齐 teacher-bff 用/teacher的命名规律,BFF 用角色单数无-bff后缀) 网关路径:/api/v1/student/*→ api-gateway 剥离/api/v1后代理到 student-bff:3009
| method | path | 聚合下游 | 权限(透传给下游校验) | 说明 |
|---|---|---|---|---|
| GET | /student/dashboard |
iam + core-edu + msg | STUDENT_DASHBOARD_READ | 学生首页聚合 |
| GET | /student/exams |
core-edu | STUDENT_EXAM_READ | 即将到来的考试 |
| GET | /student/homework |
core-edu | STUDENT_HOMEWORK_READ | 我的作业列表 |
| POST | /student/homework/:id/submit |
core-edu | STUDENT_HOMEWORK_SUBMIT | 提交作业 |
| GET | /student/grades |
core-edu | STUDENT_GRADE_READ | 我的成绩 |
| GET | /student/notifications |
msg | STUDENT_NOTIFICATION_READ | 消息列表 |
| POST | /student/notifications/:id/read |
msg | STUDENT_NOTIFICATION_READ | 标记已读 |
| GET | /student/textbooks |
content | STUDENT_CONTENT_READ | 教材列表 |
| GET | /student/chapters/:textbookId |
content | STUDENT_CONTENT_READ | 章节树 |
| GET | /student/questions |
content | STUDENT_CONTENT_READ | 题库(按知识点过滤) |
| GET | /student/analytics/weakness |
data-ana | STUDENT_ANALYTICS_READ | 学情诊断 |
| GET | /student/analytics/trend |
data-ana | STUDENT_ANALYTICS_READ | 学习趋势 |
| POST | /student/ai/chat |
ai | STUDENT_AI_CHAT | AI 答疑(同步) |
| POST | /student/ai/stream-chat |
ai | STUDENT_AI_CHAT | AI 答疑(SSE 流式) |
3.3 错误码前缀
| 前缀 | 用途 | 示例 |
|---|---|---|
STUDENT_BFF_ |
student-bff 自身错误 | STUDENT_BFF_UNAUTHORIZED、STUDENT_BFF_BAD_GATEWAY |
| 下游错误透传 | 下游服务错误码原样返回 | CLASSES_NOT_FOUND、IAM_USER_NOT_FOUND |
错误类清单(对齐 teacher-bff application-error.ts):
UnauthorizedError(401)— 缺失x-user-id头BadGatewayError(502)— 下游服务返回非 ok 或 fetch rejectedValidationError(400)— 入参校验失败(BFF 层 Zod 校验)InternalError(500)— 未捕获异常
3.4 我订阅的 Kafka 事件(可选,用于实时推送)
| Topic | 事件 | 消费动作 |
|---|---|---|
edu.homework.events |
homework.assigned / homework.graded |
推送给学生(push-gateway) |
edu.exam.events |
exam.published / exam.updated |
考试提醒推送 |
edu.grade.events |
grade.recorded |
成绩发布推送 |
⚠️ Kafka 订阅在 P5 阶段 push-gateway 落地后才有意义,P3 阶段 student-bff 可不消费事件,仅做同步聚合。
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 风格 | HTTP REST(当前阶段) | 对齐 teacher-bff 现状;pending-features P2 设计意图为 GraphQL,但 teacher-bff 实际未落地 GraphQL,需 coord 仲裁是否在 student-bff 引入 |
| 输入校验 | Zod | 对齐 classes/teacher-bff |
| 错误处理 | GlobalErrorFilter + ApplicationError | 对齐 classes/teacher-bff |
| ESM 模式 | NodeNext + .js 后缀 import |
对齐 teacher-bff tsconfig |
| 测试框架 | Jest(待定,对齐 classes) | 黄金模板要求测试覆盖率 ≥ 80% |
4.1 关于 GraphQL 的设计决策(待 coord 仲裁)
现状矛盾:
- 004 §11.3 BFF 聚合模式图示为 GraphQL Resolver + DataLoader + Redis 缓存
- pending-features P2 明确"Teacher BFF(TS/GraphQL)"用 GraphQL Yoga + DataLoader
- 实际:teacher-bff 当前是纯 REST + fetch,无 GraphQL、无 DataLoader
- ai-allocation.md §5 ai04 设计重点提到"DataLoader 复用 teacher-bff 模式"
ai04 倾向方案:P3 阶段 student-bff 先对齐 teacher-bff 现状(REST + fetch + Promise.allSettled),避免技术栈分裂;若 coord 决策统一升级到 GraphQL,则在 P3 后期或 P4 阶段同步升级 teacher-bff + student-bff + parent-bff 三端。此决策需 coord 仲裁。
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 阶段不一定要实现全部 14 个端点,优先级:
| 优先级 | 端点 | P3 必需 | 说明 |
|---|---|---|---|
| P0 | /student/homework GET |
✅ | 学生作答作业页面核心 |
| P0 | /student/homework/:id/submit POST |
✅ | 学生作答提交 |
| P0 | /student/grades GET |
✅ | 成绩查看 |
| P0 | /student/dashboard GET |
✅ | 学生首页 |
| P1 | /student/exams GET |
✅ | 考试日程 |
| P1 | /student/notifications GET |
⚠️ 可选 | P5 msg 服务落地后才有意义 |
| P2 | /student/textbooks / /student/chapters / /student/questions |
❌ P4 | content 服务 P4 才落地 |
| P2 | /student/analytics/* |
❌ P4 | data-ana 学情诊断 P4 |
| P2 | /student/ai/* |
❌ P5 | ai 服务 P5 |
6. 我需要对齐的黄金模板项(对照 classes 服务)
对照 ai-allocation.md §6 模板第 6 节 + §10 审计模板
| 对齐项 | classes 黄金模板 | student-bff 计划 | 备注 |
|---|---|---|---|
权限装饰器 @RequirePermission |
✅ 全部 Controller 方法 | ⚠️ 不对齐 | BFF 不做权限校验(对齐 teacher-bff),透传 x-user-id 给下游校验 |
| 错误码前缀统一 | ✅ CLASSES_ |
✅ STUDENT_BFF_ |
对齐 teacher-bff 的 TEACHER_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 |
✅ 复制 teacher-bff | BFF 不查 DB,直接返回 ok(可选:检查下游服务可达性) |
| 优雅关闭(SIGTERM) | ✅ LifecycleService 关闭 DB 连接池 | ✅ main.ts 注册 SIGTERM → app.close() + shutdownTracer() |
BFF 无 DB 连接,仅需关闭 HTTP server + tracer |
| 测试覆盖率 ≥ 80% | ✅ Jest | ⚠️ 待补 | BFF 测试重点是 Service 层聚合逻辑 mock 下游 fetch |
| 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_ |
STUDENT_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 设计决策待仲裁
| 决策点 | 选项 | ai04 建议 |
|---|---|---|
| BFF API 风格 | A. REST(对齐 teacher-bff 现状) B. GraphQL(对齐 004 §11.3 设计意图 + pending-features P2) |
A(P3 阶段先 REST,避免技术栈分裂;后续统一升级) |
| BFF 是否做权限校验 | A. 不校验(对齐 teacher-bff,透传 x-user-id) B. 加 @RequirePermission 装饰器 |
A(BFF 是聚合层,权限由下游服务校验) |
/readyz 检查逻辑 |
A. 直接返回 ok(对齐 teacher-bff) B. 检查下游服务可达性 |
A(P3 阶段,下游可达性由 Prometheus 监控) |
| Kafka 事件订阅 | A. P3 不订阅(仅同步聚合) B. P3 订阅事件推送 |
A(push-gateway P5 才落地,P3 无推送通道) |
| 端口分配 | 3009 | 对齐 full-stack-runbook 端口矩阵(3001-3008 已用) |
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 | 决定是否在 P3 升级到 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 可用,路由未预留) |
| 已识别设计决策待仲裁项 | ✅(见 §7.2) |
| 已识别跨模块协作需求 | ✅(见 §7.3) |
ai04 阶段 1 交付完成,请 coord 审核。审核通过后进入阶段 2(模块架构设计文档)。