# student-bff 下游工作清单 V2(Next Steps v2) > 负责人:ai04 > 更新日期:2026-07-14 > 基于版本:v1(nextstep.md)+ student-portal v2 审计(§9 二次核查)后的更新 > 关联: > > - [student-portal/docs/nextstep-v2.md](../../../apps/student-portal/docs/nextstep-v2.md) > - [student-bff/docs/nextstep.md](./nextstep.md) > - [student-bff_contract.md](../../../docs/architecture/issues/contracts/student-bff_contract.md) --- ## 0. v2 核查总结 | 工作项 | v1 状态 | v2 核查结果 | 阻断级别 | | ------------------------------------------------- | --------- | --------------- | -------- | | index.ts 装配 extended resolvers | ❌ 未装配 | ✅ **已修复** | P0 | | shared-ts schema 补全 43 个操作 SDL | ❌ 未补全 | ✅ **已修复** | P0 | | myAttendance resolver 实现 | ❌ 缺失 | ✅ **已实现** | P1 | | startPracticeSession 类型修正(Query → Mutation) | ❌ 未修正 | ✅ **已修正** | P1 | | RecordExamViolationInput schema 类型对齐 | ❌ 未发现 | ✅ **已修复** | P0 | | Dockerfile 多阶段构建(对齐 teacher-bff 模式) | ❌ 失败 | ✅ **已修复** | P0 | | shared-ts pino NodeNext 类型修复 | ❌ 未发现 | ✅ **已修复** | P0 | | Docker 构建 + 容器启动 + GraphQL 60 操作验证 | ❌ 未验证 | ✅ **全部通过** | P0 | **结论:** student-bff v2 全部 P0/P1 工作已完成并验证通过。60 个 GraphQL 操作(36 Query + 24 Mutation)全部注册并可响应。 --- ## 1. student-bff v2 完成项详情 ### 1.1 P0:index.ts 装配 extended resolvers **修复前:** `index.ts` 仅装配 11 个基础 resolver,22 个 extended Query + 21 个 extended Mutation 已实现但未导入。 **修复后:** ```typescript import { extendedQueriesResolvers } from "./extended-queries.resolver.js"; import { extendedMutationsResolvers } from "./extended-mutations.resolver.js"; export const studentBffResolvers = { Query: { ...(authResolvers.Query ?? {}), // ... 其他基础 resolver ...(extendedQueriesResolvers.Query ?? {}), // ✅ 新增 }, Mutation: { ...(homeworkResolvers.Mutation ?? {}), ...(notificationsResolvers.Mutation ?? {}), ...(extendedMutationsResolvers.Mutation ?? {}), // ✅ 新增 }, Subscription: { ...(aiStreamResolvers.Subscription ?? {}), }, }; ``` **验证:** GraphQL introspection 查询返回 36 Query + 24 Mutation = 60 操作。 ### 1.2 P0:shared-ts schema 补全 **修复前:** `student-bff.schema.graphql` 仅 822 行,定义 15 Query + 2 Mutation + 1 Subscription。 **修复后:** 1648 行,定义 36 Query + 24 Mutation + 1 Subscription,包含所有 input type / enum / payload type。 ### 1.3 P1:myAttendance resolver 实现 调用 `core-edu.AttendanceService.ListAttendanceByStudent`,B4 强制 `userId = studentId`(DataScope SELF)。 ### 1.4 P1:startPracticeSession 类型修正 从 Query 移到 Mutation(语义为创建会话)。 ### 1.5 P0:RecordExamViolationInput schema 类型对齐 schema 引用 `RecordExamViolationInput` 但实际定义为 `RecordViolationInput`,已修正为 `recordExamViolation(input: RecordViolationInput!)`。 ### 1.6 P0:Dockerfile 修复 **问题:** 原 Dockerfile 的 COPY 路径未包含 `services/student-bff/` 前缀,且 runtime stage 使用 `--ignore-workspace` 导致 `@edu/shared-ts` 无法解析。 **修复:** 对齐 teacher-bff 模式: - Builder stage:复制 workspace 配置 + shared-proto + shared-ts + student-bff,使用 `--filter @edu/student-bff...` 过滤安装(避免 scripts/arch-scan 的 better-sqlite3 原生编译),先构建 shared-ts 再构建 student-bff - Runtime stage:保留 workspace 结构,`pnpm install --prod --filter @edu/student-bff... --ignore-scripts`,复制 shared-ts dist 产物 ### 1.7 P0:shared-ts pino NodeNext 类型修复 **问题:** pino 在 NodeNext + ESM 模式下 default import 丢失 callable 签名(declaration merging 失效)。 **修复:** 通过 namespace import + 显式类型断言恢复可调用性: ```typescript import * as pinoNs from "pino"; import type { Logger as PinoLogger, LoggerOptions } from "pino"; type PinoFn = (options?: LoggerOptions | unknown) => PinoLogger; const pino = ((pinoNs as unknown as { default: PinoFn }).default ?? (pinoNs as unknown as PinoFn)) as PinoFn; ``` 同时修复 `src/bff/downstream-client.ts:351` 的 IteratorResult 类型窄化问题。 --- ## 2. v2 本地 Docker 验证结果(2026-07-14) | 验证项 | 结果 | 说明 | | ------------------------------ | ---- | ----------------------------------------- | | `pnpm typecheck` | ✅ | 0 errors | | `pnpm lint` | ✅ | 0 errors, 0 warnings | | `pnpm test` | ✅ | 70/70 passed(5 test files) | | `packages/shared-ts typecheck` | ✅ | 0 errors(pino 修复后) | | `packages/shared-ts build` | ✅ | dist 产物生成成功 | | Docker 镜像构建 | ✅ | `edu-student-bff:test` 构建成功 | | 容器启动 | ✅ | NestFactory + GraphQL Yoga 初始化完成 | | `/healthz` 健康检查 | ✅ | `{"status":"ok","service":"student-bff"}` | | GraphQL `__typename` 查询 | ✅ | `{"data":{"__typename":"Query"}}` | | GraphQL introspection | ✅ | 36 Query + 24 Mutation = 60 操作全部注册 | | `MOCK_UPSTREAM=true` 降级模式 | ✅ | 所有下游调用返回 mock 数据 | **GraphQL 操作清单(60 个,全部已注册):** - **Query(36):** currentUser, myClasses, myExams, myHomework, myGrades, myAttendance, textbooks, chapters, learningPath, studentDashboard, myWeakness, myTrend, myNotifications, myNotificationUnreadCount, aiChat, examDetail, homeworkDetail, serverTime, mySchedule, studentGrowth, assignmentAnalysis, myProfile, myMasterySummary, myDiagnosticReports, myErrorBook, announcements, announcementDetail, myLeaveRequests, myElectiveSelections, availableElectiveCourses, myLessonPlans, lessonPlanDetail, myCoursePlans, coursePlanDetail, myReportCard, myPracticeSessions - **Mutation(24):** submitHomework, markNotificationAsRead, markAsRead, markAllAsRead, updateNotificationPreference, submitExam, saveExamDraft, recordExamViolation, recordPasteEvent, updateProfile, changePassword, requestExtension, joinClass, leaveClass, addErrorBookItem, updateErrorBookItem, deleteErrorBookItem, markAnnouncementRead, createLeaveRequest, cancelLeaveRequest, selectElectiveCourse, dropElectiveCourse, startPracticeSession, submitPracticeAnswer --- ## 3. 上游依赖(api-gateway 路由) ### 3.1 api-gateway(ai01 负责) **v2 核查结果:✅ 路由已注册 + 路径重写已修复** - `services/api-gateway/main.go` — `registerBffProxy(api, "student", cfg.StudentBffURL)` ✅ - `services/api-gateway/internal/config/config.go` — `StudentBffURL: getEnv("STUDENT_BFF_URL", "http://localhost:3009")` ✅ - 路径前缀改写:`/api/v1/student/*` → `/v1/student/*`(proxy.go `registerBffProxy`)✅ - JWT 鉴权 + `x-user-id` / `x-user-roles` / `x-request-id` header 注入 ✅ ### 3.2 待 SRE AI 修复(P0 部署阻断) | # | 工作项 | 详情 | | --- | ----------------------------------------------------- | ---------------------------------------------------------------------- | | 1 | `infra/docker-compose.deploy.yml` 新增 student-bff | 服务定义缺失,需新增 build context + environment + ports + networks | | 2 | `infra/docker-compose.deploy.yml` 新增 student-portal | 服务定义缺失 | | 3 | api-gateway environment 补充 `STUDENT_BFF_URL` | 当前 deploy.yml 缺失,会回退到 `http://localhost:3009`,容器内无法访问 | **student-bff 建议的 deploy.yml 配置:** ```yaml student-bff: build: context: ./repo dockerfile: services/student-bff/Dockerfile container_name: edu-student-bff restart: unless-stopped environment: NODE_ENV: production PORT: 3009 DEV_MODE: "false" MOCK_UPSTREAM: "false" REDIS_URL: redis://edu-redis:6379 KAFKA_BROKERS: edu-kafka:29092 # 下游 gRPC targets(Docker 服务名 + 端口) IAM_GRPC_TARGET: iam:50052 CORE_EDU_GRPC_TARGET: core-edu:50053 CONTENT_GRPC_TARGET: content:50054 DATA_ANA_GRPC_TARGET: data-ana:50055 MSG_GRPC_TARGET: msg:50056 AI_GRPC_TARGET: ai:50058 ports: - "${STUDENT_BFF_PORT:-3009}:3009" depends_on: iam: condition: service_healthy core-edu: condition: service_healthy networks: - edu-net - edu-shared ``` --- ## 4. 下游依赖(gRPC 微服务) ### 4.1 iam 服务(ai06 负责) **v2 核查结果:✅ 全部就绪** iam 已实现 15 个 gRPC RPC,包含 student-bff 需要的全部方法: | gRPC 方法 | 用途 | 状态 | | ------------------------- | ------------------------- | ---- | | `GetUserInfo` | `currentUser` Query | ✅ | | `GetEffectivePermissions` | `currentUser` permissions | ✅ | | `GetViewports` | `currentUser` viewport | ✅ | | `GetUserProfile` | `myProfile` Query | ✅ | | `UpdateProfile` | `updateProfile` Mutation | ✅ | | `ChangePassword` | `changePassword` Mutation | ✅ | ### 4.2 core-edu 服务(ai08 负责) **v2 核查结果:✅ 全部就绪** core-edu 已完成 P3.13,9 Service / 40 RPC 全部就绪(24/24 smoke test 通过): | gRPC 方法 | 用途 | 状态 | | ------------------------------------------------ | ------------------------------ | ---- | | `ExamService.GetExam` | `examDetail` Query | ✅ | | `ExamService.SubmitExam` | `submitExam` Mutation | ✅ | | `ExamService.SaveExamDraft` | `saveExamDraft` Mutation | ✅ | | `ExamService.RecordExamViolation` | `recordExamViolation` Mutation | ✅ | | `HomeworkService.GetHomework` | `homeworkDetail` Query | ✅ | | `HomeworkService.SubmitHomework` | `submitHomework` Mutation | ✅ | | `GradeService.ListGradesByStudent` | `myGrades` Query | ✅ | | `GradeService.GetReportCard` | `myReportCard` Query | ✅ | | `ScheduleService.GetScheduleByStudent` | `mySchedule` Query | ✅ | | `AttendanceService.ListAttendanceByStudent` | `myAttendance` Query | ✅ | | `LeaveRequestService.ListLeaveRequestsByStudent` | `myLeaveRequests` Query | ✅ | | `LeaveRequestService.CreateLeaveRequest` | `createLeaveRequest` Mutation | ✅ | | `LeaveRequestService.CancelLeaveRequest` | `cancelLeaveRequest` Mutation | ✅ | | `ClassService.GetClass` | `myClasses` Query | ✅ | | `ClassService.ListStudentsByClass` | `myClasses` Query | ✅ | ### 4.3 content 服务(ai09 负责) **v2 核查结果:⚠️ 需确认** student-bff 需要 8 个 content RPC,需 ai09 确认以下 RPC 是否已实现: | gRPC 方法 | 用途 | 状态 | | ------------------------------------------------- | -------------------------------- | ------ | | `TextbookService.ListTextbooks` | `textbooks` Query | 需确认 | | `ChapterService.ListChapters` | `chapters` Query | 需确认 | | `KnowledgeGraphService.GetLearningPath` | `learningPath` Query | 需确认 | | `ElectiveService.ListElectiveSelectionsByStudent` | `myElectiveSelections` Query | 需确认 | | `ElectiveService.ListAvailableElectiveCourses` | `availableElectiveCourses` Query | 需确认 | | `ElectiveService.SelectCourse` | `selectElectiveCourse` Mutation | 需确认 | | `ElectiveService.DropCourse` | `dropElectiveCourse` Mutation | 需确认 | | `LessonPlanService.ListLessonPlansByStudent` | `myLessonPlans` Query | 需确认 | | `LessonPlanService.GetLessonPlan` | `lessonPlanDetail` Query | 需确认 | | `CoursePlanService.ListCoursePlansByStudent` | `myCoursePlans` Query | 需确认 | | `CoursePlanService.GetCoursePlan` | `coursePlanDetail` Query | 需确认 | ### 4.4 data-ana 服务(ai12 负责) **v2 核查结果:⚠️ 需确认** student-bff 需要 8 个 data-ana RPC,需 ai12 确认以下 RPC 是否已实现: | gRPC 方法 | 用途 | 状态 | | ----------------------------------------------- | ------------------------------- | ------ | | `AnalyticsService.GetStudentDashboard` | `studentDashboard` Query | 需确认 | | `AnalyticsService.GetStudentWeakness` | `myWeakness` Query | 需确认 | | `AnalyticsService.GetLearningTrend` | `myTrend` Query | 需确认 | | `AnalyticsService.GetStudentGrowth` | `studentGrowth` Query | 需确认 | | `AnalyticsService.GetAssignmentAnalysis` | `assignmentAnalysis` Query | 需确认 | | `AnalyticsService.GetMasterySummary` | `myMasterySummary` Query | 需确认 | | `AnalyticsService.ListDiagnosticReports` | `myDiagnosticReports` Query | 需确认 | | `AnalyticsService.ListErrorBookItems` | `myErrorBook` Query | 需确认 | | `PracticeService.ListPracticeSessionsByStudent` | `myPracticeSessions` Query | 需确认 | | `PracticeService.StartPracticeSession` | `startPracticeSession` Mutation | 需确认 | | `PracticeService.SubmitPracticeAnswer` | `submitPracticeAnswer` Mutation | 需确认 | ### 4.5 msg 服务(ai10 负责) **v2 核查结果:✅ 全部就绪(gRPC + REST 混合)** msg 已实现 13 个 gRPC RPC + 完整 REST API: | gRPC / REST 方法 | 用途 | 状态 | | ------------------------------------------------- | --------------------------------------- | ---- | | `NotificationService.ListNotifications` | `myNotifications` Query | ✅ | | `NotificationService.MarkAsRead` | `markAsRead` Mutation | ✅ | | `NotificationService.MarkAllAsRead` | `markAllAsRead` Mutation | ✅ | | `NotificationPreferenceService.GetPreferences` | 通知偏好查询 | ✅ | | `NotificationPreferenceService.UpdatePreferences` | `updateNotificationPreference` Mutation | ✅ | | REST `GET /announcements` | `announcements` Query | ✅ | | REST `GET /announcements/:id` | `announcementDetail` Query | ✅ | | REST `POST /announcements/:id/read` | `markAnnouncementRead` Mutation | ✅ | ### 4.6 ai 服务(ai11 负责) **v2 核查结果:⚠️ 需确认** | gRPC 方法 | 用途 | 状态 | | ------------------------ | ----------------------- | ------ | | `ChatService.Chat` | `aiChat` Query | 需确认 | | `ChatService.StreamChat` | `aiStream` Subscription | 需确认 | ### 4.7 push-gateway(ai13 负责) **v2 核查结果:✅ 已验证** push-gateway 采用通用事件透传机制,三个事件均已 Docker 验证通过: - `ExamExtended` ✅ - `ExamForceSubmitted` ✅ - `ExamQuestionReordered` ✅ ### 4.8 core-edu 事件发布(ai08 待修复) **v2 核查结果:❌ 未实现** `ExamExtended` / `ExamForceSubmitted` / `ExamQuestionReordered` 事件字面量在 core-edu 源码中仍未找到。这三个事件由教师端触发(延长考试/强制收卷/调整题目),需 core-edu 在相应业务操作中通过 Outbox 发布到 Kafka topic `edu.exam.events`。 **影响:** 考试实时推送功能不可用(非阻断,student-bff 可降级)。 --- ## 5. student-bff 需要其他模块配合的工作 ### 5.1 SRE AI(P0 部署阻断) 1. **新增 `infra/docker-compose.deploy.yml` 中 student-bff 服务定义**(见 §3.2 模板) 2. **新增 `infra/docker-compose.deploy.yml` 中 student-portal 服务定义** 3. **api-gateway environment 补充 `STUDENT_BFF_URL: http://student-bff:3009`** ### 5.2 ai09 content 服务(P0 功能阻断) 确认并实现以下 RPC(如尚未实现): - ElectiveService 4 RPC(ListElectiveSelectionsByStudent / ListAvailableElectiveCourses / SelectCourse / DropCourse) - LessonPlanService 2 RPC(ListLessonPlansByStudent / GetLessonPlan) - CoursePlanService 2 RPC(ListCoursePlansByStudent / GetCoursePlan) ### 5.3 ai12 data-ana 服务(P0 功能阻断) 确认并实现以下 RPC(如尚未实现): - AnalyticsService 8 RPC(GetStudentDashboard / GetStudentWeakness / GetLearningTrend / GetStudentGrowth / GetAssignmentAnalysis / GetMasterySummary / ListDiagnosticReports / ListErrorBookItems) - PracticeService 3 RPC(ListPracticeSessionsByStudent / StartPracticeSession / SubmitPracticeAnswer) ### 5.4 ai11 ai 服务(P1 可降级) 确认并实现以下 RPC(如尚未实现): - ChatService.Chat(同步 AI 答疑) - ChatService.StreamChat(SSE 流式 AI 答疑) ### 5.5 ai08 core-edu 服务(P1 事件发布) 实现以下 Kafka 事件发布: - `ExamExtended`(延长考试时间时发布) - `ExamForceSubmitted`(强制收卷时发布) - `ExamQuestionReordered`(调整题目顺序时发布) 发布到 topic `edu.exam.events`,student-bff EventSubscriber 消费后转发到 push-gateway。 --- ## 6. 联调就绪状态 ``` student-portal ──✅──> api-gateway ──✅──> student-bff ──✅──> [60 操作全部注册] │ ├──✅──> core-edu (40 RPC 就绪) ├──✅──> iam (15 RPC 就绪) ├──✅──> msg (13 RPC + REST 就绪) ├──⚠️──> content (需确认) ├──⚠️──> data-ana (需确认) ├──⚠️──> ai (需确认) └──❌──> docker-compose 未配置 ``` **结论:** student-bff 自身已完全就绪(v2 全部 P0/P1 已修复并验证)。待 SRE AI 配置 docker-compose + content/data-ana/ai 服务确认 RPC 后,可进行端到端联调。 --- ## 7. 已知限制 | # | 限制 | 说明 | | --- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------- | | 1 | Dockerfile 使用 `--filter @edu/student-bff...` | 过滤掉 scripts/arch-scan 的 better-sqlite3 原生编译依赖,避免 alpine 缺少 build-essential | | 2 | Dockerfile 需先构建 shared-ts | student-bff 运行时依赖 `@edu/shared-ts/bff` 的 dist 产物,Dockerfile 已包含 shared-ts 构建步骤 | | 3 | shared-ts pino 需 namespace import | pino 在 NodeNext + ESM 下 default import 丢失 callable 签名,已通过 namespace import 修复 | | 4 | Kafka 未启动时 EventSubscriber 软失败 | 服务正常启动,但实时事件推送(通知/AI 流式响应)不工作 | | 5 | 下游 gRPC 未就绪时进入降级模式 | 所有 Query 返回 `degraded=true` + 空数据;所有 Mutation 返回 `success=false` + BAD_GATEWAY | | 6 | DEV_MODE=true 时跳过 B4 越权校验 | 生产环境必须 `DEV_MODE=false`,由 api-gateway 注入 `x-user-id` header | --- **本文件由 ai04 维护。student-bff v2 全部工作已完成,待下游服务确认 + SRE 部署配置后可联调。**