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 通过
This commit is contained in:
SpecialX
2026-07-14 22:58:38 +08:00
parent aac26c7c6f
commit d11441c9a8
7 changed files with 471 additions and 9 deletions

View File

@@ -87,6 +87,16 @@ async function main() {
r = await call(clientMap.ExamService, "RecordExamViolation", { exam_id: "1b7f27aa-f45d-495d-b74f-cecbd0230e2e", student_id: "stu-1", violation_type: "tab_switch", detail: "test", severity: 1 });
record("ExamService.RecordExamViolation", !r.error, r.error?.message || `violationId=${r.response?.violationId}`);
// P3.14 考试实时事件
r = await call(clientMap.ExamService, "ExtendExam", { exam_id: "1b7f27aa-f45d-495d-b74f-cecbd0230e2e", extension_seconds: 300, extended_by: "dev-teacher" });
record("ExamService.ExtendExam", !r.error || r.error?.code === grpc.status.NOT_FOUND || r.error?.code === grpc.status.FAILED_PRECONDITION, r.error?.message || `newDuration=${r.response?.newDuration}`);
r = await call(clientMap.ExamService, "ForceSubmitExam", { exam_id: "1b7f27aa-f45d-495d-b74f-cecbd0230e2e", student_ids: [], forced_by: "dev-teacher" });
record("ExamService.ForceSubmitExam", !r.error || r.error?.code === grpc.status.NOT_FOUND || r.error?.code === grpc.status.FAILED_PRECONDITION, r.error?.message || `affected=${r.response?.affectedCount}`);
r = await call(clientMap.ExamService, "ReorderExamQuestions", { exam_id: "1b7f27aa-f45d-495d-b74f-cecbd0230e2e", orders: [{ question_id: "q-1", order: 1 }], reordered_by: "dev-teacher" });
record("ExamService.ReorderExamQuestions", !r.error || r.error?.code === grpc.status.NOT_FOUND || r.error?.code === grpc.status.FAILED_PRECONDITION, r.error?.message || `success=${r.response?.success}`);
// ---- HomeworkService ----
r = await call(clientMap.HomeworkService, "ListHomeworkByClass", { class_id: "cls-nonexistent" });
record("HomeworkService.ListHomeworkByClass", !r.error, r.error?.message || `homework=${r.response?.homework?.length ?? 0}`);