Files
Edu/services/core-edu/docs/nextstep-v2.md
SpecialX d11441c9a8 feat(core-edu): v2 P3.14 考试实时事件 + pino 修复
新增 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 通过
2026-07-14 22:58:38 +08:00

208 lines
19 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.
# core-edu 下一步工作与上下游依赖v2
> 模块core-edu教学核心服务HTTP 3004 + gRPC 50053
> 更新日期2026-07-14v2P3.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.proto` ExamService 中新增 3 个 RPC + 对应 message
- 服务层:[exams.service.ts](file:///e:/Desktop/Edu/services/core-edu/src/exams/exams.service.ts) 新增 `extendExam` / `forceSubmitExam` / `reorderExamQuestions` 方法
- 事件发布:通过 Outbox 模式事务内写入 `core_edu_outbox`OutboxPublisher 轮询投递到 Kafka
- gRPC handler[grpc.server.ts](file:///e:/Desktop/Edu/services/core-edu/src/grpc/grpc.server.ts) 注册 3 个新 handler
- TOPIC_MAP[outbox.publisher.ts](file:///e:/Desktop/Edu/services/core-edu/src/shared/outbox/outbox.publisher.ts) 新增 3 个事件类型映射
### 1.2 修复 pino 导入错误
[logger.ts](file:///e:/Desktop/Edu/services/core-edu/src/shared/observability/logger.ts) 从 `import pino from 'pino'` 改为 `import { pino } from 'pino'`,与其他服务一致。
### 1.3 验证结果
```
镜像edu/core-edu:test
容器edu-core-edu-testDEV_MODE=true, HTTP 13004→3004 + gRPC 50053
smoke test27/27 PASS含 3 个 P3.14 新 RPC
```
---
## 2. 上下游依赖确认
### 2.1 下游模块对 core-edu 的依赖状态
| 下游模块 | 依赖类型 | 需求 | core-edu 状态 |
| -------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| teacher-bff | gRPC :50053 | 11 个 RPCListClasses/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 binloggrades/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` 的标准格式:
```json
{
"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 | ✅ | 200db=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 服务就绪。**