388 lines
21 KiB
Markdown
388 lines
21 KiB
Markdown
# 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 部署配置后可联调。**
|