新增 3 RPC:ExtendExam/ForceSubmitExam/ReorderExamQuestions
新增 3 Kafka 事件:exam.extended/exam.force_submitted/exam.question_reordered
exams.service.ts 新增 3 方法 + Outbox 事务内写入 + TOPIC_MAP 映射
grpc.server.ts 注册 3 handler + grpc-smoke 测试
logger.ts pino 导入修复(import pino → import { pino })
27/27 smoke test 通过
19 KiB
core-edu 下一步工作与上下游依赖(v2)
模块:core-edu(教学核心服务,HTTP 3004 + gRPC 50053) 更新日期:2026-07-14(v2:P3.14 考试实时事件完成 + 上下游依赖确认) 状态:9 Service / 43 RPC 全部就绪,本地 Docker 27/27 smoke test 通过
1. 本轮完成工作
1.1 P3.14 考试实时事件(3 个新 RPC + 3 个新 Kafka 事件)
下游 push-gateway / student-bff / student-portal 明确要求 core-edu 发布 3 类考试实时事件,用于 WebSocket 推送。本轮已完成实现:
| RPC | 触发场景 | Kafka 事件 | Topic |
|---|---|---|---|
ExamService.ExtendExam |
教师延长考试时间 | exam.extended |
edu.teaching.exam.extended |
ExamService.ForceSubmitExam |
教师强制收卷 | exam.force_submitted |
edu.teaching.exam.force_submitted |
ExamService.ReorderExamQuestions |
教师调整题目顺序 | exam.question_reordered |
edu.teaching.exam.question_reordered |
实现方式:
- proto 契约:在
core_edu.protoExamService 中新增 3 个 RPC + 对应 message - 服务层:exams.service.ts 新增
extendExam/forceSubmitExam/reorderExamQuestions方法 - 事件发布:通过 Outbox 模式事务内写入
core_edu_outbox表,OutboxPublisher 轮询投递到 Kafka - gRPC handler:grpc.server.ts 注册 3 个新 handler
- TOPIC_MAP:outbox.publisher.ts 新增 3 个事件类型映射
1.2 修复 pino 导入错误
logger.ts 从 import pino from 'pino' 改为 import { pino } from 'pino',与其他服务一致。
1.3 验证结果
镜像:edu/core-edu:test
容器:edu-core-edu-test(DEV_MODE=true, HTTP 13004→3004 + gRPC 50053)
smoke test:27/27 PASS(含 3 个 P3.14 新 RPC)
2. 上下游依赖确认
2.1 下游模块对 core-edu 的依赖状态
| 下游模块 | 依赖类型 | 需求 | core-edu 状态 |
|---|---|---|---|
| teacher-bff | gRPC :50053 | 11 个 RPC(ListClasses/ListExamsByClass/ListHomeworkByClass/ListGradesByExam/ListStudentsByClass/GetExam/GetHomework/CreateExam/AssignHomework/RecordGrade/GetClassPerformance) | ✅ 全部就绪(teacher-bff 文档过时,实际全部已实现) |
| student-bff | gRPC :50053 | 15 个 RPC(含 P3.13 新增 SaveExamDraft/RecordExamViolation/GetScheduleByStudent/LeaveRequest×3/GetReportCard) | ✅ 全部就绪 |
| parent-bff | gRPC :50053 | 3 个 RPC 就绪 + 7 个待补全(详见 §2.2) | ⚠️ 部分就绪(详见 §2.2) |
| api-gateway | HTTP :3004 | REST 代理 /api/v1/{classes,exams,homework,grades}/* | ✅ 就绪 |
| push-gateway | Kafka | 3 个考试实时事件(ExamExtended/ExamForceSubmitted/ExamQuestionReordered) | ✅ 已实现(P3.14) |
| msg | Kafka | 5 个 teaching 事件(exam.published/homework.assigned/grade.recorded/attendance.recorded 等) | ✅ 已实现 |
| data-ana | CDC binlog | MySQL binlog(grades/exams/homework/attendance/classes 表) | ✅ MySQL 就绪(Debezium 由 infra 部署) |
| admin-portal | 间接(经 teacher-bff) | AdminService 4 RPC | ✅ 就绪(stub) |
| student-portal | 间接(经 student-bff) | 10 个 RPC + 3 个考试事件 | ✅ 全部就绪 |
| parent-portal | 间接(经 parent-bff) | 7 个 Query + 2 个 Mutation | ⚠️ 部分就绪(详见 §2.2) |
2.2 parent-bff 待澄清项(不需要 core-edu 新增 RPC)
parent-bff nextstep-v2.md §6.3 列出 7 个"待补全"RPC,经核对全部可由现有 RPC 满足:
| parent-bff 提议 | core-edu 现有实现 | 澄清 |
|---|---|---|
ClassService.GetClass(ISSUE-008) |
✅ ClassService.GetClass |
proto 已定义,已实现,ISSUE-008 已解决 |
AttendanceService.ListAttendance |
✅ AttendanceService.ListAttendanceByClass 或 ListAttendanceByStudent |
parent-bff 按班级或按学生查询,两个 RPC 已实现 |
ExamResultService.GetExamResult(新 Service) |
✅ ExamService.GetExam + GradeService.ListGradesByExam 组合 |
不需要新 Service,考试结果 = 考试详情 + 成绩列表 |
ReportCardService.GetReportCard(新 Service) |
✅ GradeService.GetReportCard |
不需要新 Service,已在 GradeService 中实现 |
LeaveRequestService.List/Create |
✅ ListLeaveRequestsByStudent + CreateLeaveRequest |
家长视角通过 studentId=childId 参数实现 |
AcademicYearService.List(新 Service) |
✅ AdminService.ListAcademicYears |
不需要新 Service,已在 AdminService 中实现 |
ExportService.ExportGrades(新 Service) |
✅ GradeService.ListGradesByStudent |
导出属 BFF/Portal 层格式化能力,core-edu 提供数据,BFF 负责格式化 |
结论:core-edu 不需要新增任何 RPC 或 Service。 parent-bff 文档中提议的新 Service 均可由现有 9 Service / 43 RPC 组合满足。
2.3 Kafka topic 名协调结论
push-gateway 期望 topic edu.notify.notification.sent,student-bff/student-portal 期望 topic edu.exam.events。
协调结论:core-edu 遵循现有 edu.teaching.exam.* 命名规范,不改动。
正确的事件流:
core-edu → edu.teaching.exam.extended → msg 消费 → edu.notify.notification.sent → push-gateway → WebSocket
- core-edu 发布到
edu.teaching.exam.*(遵循 outbox.publisher.ts TOPIC_MAP 规范) - msg 服务负责消费
edu.teaching.exam.*并转发到edu.notify.notification.* - push-gateway 消费
edu.notify.notification.* - student-bff 应消费
edu.teaching.exam.*(而不是期望 core-edu 直接发布到edu.exam.events)
2.4 事件 payload 字段对齐(msg 服务需求)
msg nextstep.md §6 要求 core-edu 发布的事件 payload 字段名与 msg consumer 对齐。core-edu 当前事件 payload 遵循 event-builder.ts 的标准格式:
{
"event_id": "UUID",
"aggregate_id": "examId",
"event_type": "exam.extended",
"occurred_at": 1234567890,
"payload": {
"examId": "...",
"classId": "...",
"subjectId": "...",
"extensionSeconds": 300,
"newDuration": 7200
},
"metadata": {
"schema_version": "v1",
"trace_id": "...",
"user_id": "..."
}
}
msg 服务需按此结构消费。字段名使用驼峰命名(与 events.proto ExamEvent message 的 snake_case 不同,因为 Outbox payload 是 JSON 序列化,不经过 proto 编码)。
3. core-edu 需要上下游实现的工作
3.1 需要上游(同层级)实现
| 上游模块 | 需求 | 状态 |
|---|---|---|
| iam | gRPC :50052 — 用户信息查询(DashboardService 学生数统计需要 IAM 集成) | ⏳ 待 iam gRPC 就绪 |
| content | gRPC :50054 — 知识点关联(ClassPerformance 学科名需要 content 服务集成) | ⏳ 待 content gRPC 就绪 |
| data-ana | Kafka edu.insight.mastery.updated — 掌握度事件消费(P4 演进) |
⏳ 待 data-ana 就绪 |
| msg | gRPC :50056 — 消息服务联调(P5 演进) | ⏳ 待 msg gRPC 就绪 |
| infra | Debezium Connect 部署 — CDC 管道(data-ana 依赖) | ⏳ 待 infra 部署 |
| infra | Temporal server 部署 — 工作流试点(P3.10) | ⏳ 待 infra 部署 |
3.2 需要下游实现的工作
| 下游模块 | 需求 | 说明 |
|---|---|---|
| msg | 消费 edu.teaching.exam.extended / edu.teaching.exam.force_submitted / edu.teaching.exam.question_reordered 三个新 topic |
msg 需在 consumer 中订阅这 3 个 topic,生成通知后发布到 edu.notify.notification.* |
| push-gateway | 消费 edu.notify.notification.sent(由 msg 转发) |
push-gateway 不直接消费 core-edu 的 topic,应消费 msg 转发后的通知 topic |
| student-bff | 消费 edu.teaching.exam.* 三个新 topic(或经 msg 转发) |
student-bff EventSubscriber 需订阅正确的 topic 名 |
| teacher-bff | 更新文档:11 个 RPC 全部已实现,不需要 mock 降级 | teacher-bff nextstep-v2.md §2.2 标注的"待实现"全部过时 |
| parent-bff | 更新文档:7 个"待补全"RPC 全部可由现有 RPC 满足 | parent-bff 不需要 core-edu 新增 Service,按 §2.2 澄清映射调用现有 RPC |
4. 就绪信号
| 信号 | 状态 | 说明 |
|---|---|---|
| HTTP :3004 /healthz + /readyz | ✅ | 200,db=ok, redis=ok, kafka=ok |
| gRPC :50053 9 Service / 43 RPC | ✅ | 27/27 smoke test 通过 |
| Kafka 事件发布(14 个事件类型) | ✅ | 含 3 个 P3.14 新增考试实时事件 |
| Outbox 模式 | ✅ | 事务内写入 + 独立 publisher 投递 |
| Docker 镜像 | ✅ | edu/core-edu:test,单阶段构建 node:22-alpine |
4.1 完整 RPC 清单(43 RPC)
| Service | RPC 数 | RPC 列表 |
|---|---|---|
| ExamService | 13 | CreateExam, GetExam, ListExamsByClass, UpdateExam, DeleteExam, PublishExam, SubmitExam, GradeExam, SaveExamDraft, RecordExamViolation, ExtendExam, ForceSubmitExam, ReorderExamQuestions |
| HomeworkService | 5 | AssignHomework, GetHomework, ListHomeworkByClass, SubmitHomework, GradeHomework |
| GradeService | 7 | RecordGrade, GetGrade, ListGradesByStudent, ListGradesByExam, ListGradesByHomework, UpdateGrade, GetReportCard |
| ClassService | 4 | GetClass, GetClassesByTeacher, BatchGetClasses, ListStudentsByClass |
| AttendanceService | 4 | RecordAttendance, GetAttendance, ListAttendanceByStudent, ListAttendanceByClass |
| ScheduleService | 1 | GetScheduleByStudent |
| LeaveRequestService | 3 | ListLeaveRequestsByStudent, CreateLeaveRequest, CancelLeaveRequest |
| DashboardService | 2 | GetDashboard, GetClassPerformance |
| AdminService | 4 | ListSchools, ListGradeLevels, ListDepartments, ListAcademicYears |
4.2 完整 Kafka 事件清单(14 个事件类型)
| 事件类型 | Topic | 消费方 |
|---|---|---|
| exam.created | edu.teaching.exam.created | msg, data-ana(CDC) |
| exam.updated | edu.teaching.exam.updated | msg, data-ana(CDC) |
| exam.published | edu.teaching.exam.published | msg |
| exam.submitted | edu.teaching.exam.submitted | msg |
| exam.graded | edu.teaching.exam.graded | msg |
| exam.deleted | edu.teaching.exam.deleted | msg |
| exam.extended | edu.teaching.exam.extended | msg → push-gateway → student-portal |
| exam.force_submitted | edu.teaching.exam.force_submitted | msg → push-gateway → student-portal |
| exam.question_reordered | edu.teaching.exam.question_reordered | msg → push-gateway → student-portal |
| homework.assigned | edu.teaching.homework.assigned | msg |
| homework.submitted | edu.teaching.homework.submitted | msg |
| homework.graded | edu.teaching.homework.graded | msg |
| grade.recorded | edu.teaching.grade.recorded | msg |
| grade.updated | edu.teaching.grade.updated | msg |
| attendance.recorded | edu.teaching.attendance.recorded | msg |
5. 后续演进任务(不阻塞下游)
| 任务 | 优先级 | 阻塞条件 |
|---|---|---|
| P3.1 database.ts 改为 getDb() 函数式 | P3 | 无(技术债,14 个文件需重构) |
| P3.10 Temporal 工作流试点 | P3 | Temporal server 部署(infra) |
| P4.1 消费 data-ana mastery 事件 | P4 | data-ana gRPC 50055 就绪 |
| P4.2 content gRPC 调用(知识点关联) | P4 | content gRPC 50054 就绪 |
| P4.3 iam gRPC 调用(DashboardService 学生数) | P4 | iam gRPC 50052 就绪 |
| P5.1 msg 事件消费联调 | P5 | msg gRPC 50056 就绪 |
| P6.1 /readyz 硬化(+Temporal 探针) | P6 | Temporal 部署 |
core-edu P3.14 完成。9 Service / 43 RPC + 14 Kafka 事件全部就绪,本地 Docker 27/27 smoke test 通过(无 mock 数据)。下游 9 模块可基于此进行端到端联调。后续 P4+ 任务等待 iam/content/data-ana/msg 服务就绪。