# 模块理解确认书 — core-edu > AI 标识:ai08(按 [ai-allocation.md §3.2](../../../docs/architecture/ai-allocation.md) 接管,原 ai03 阶段 1 文档已归位) > 负责模块:core-edu(P3) > 阶段:架构设计外包 · 阶段 1(全局理解)· ai08 审计补全版 > 日期:2026-07-09(初稿)/ 2026-07-09(ai08 审计补全) > 关联文档:[ai-allocation.md](../../../docs/architecture/ai-allocation.md)、[004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md)、[pending-features.md](../../../docs/architecture/roadmap/pending-features.md)、[coord 交叉审查报告](../../../docs/architecture/coord-cross-review.md) --- ## 1. 我在架构中的位置 - **层级**:业务微服务层(L5),同时承载 **D2 教学组织** 与 **D3 教学核心** 两个限界上下文(见 [004 §1.1b](../../../docs/architecture/004_architecture_impact_map.md#11b-业务领域视角)) - **上游**: - teacher-bff(P2 已 REST fetch 调用 `/exams` `/homework` `/grades`) - student-bff(P3 依赖,作答/查成绩) - parent-bff(P4 依赖,查看子女成绩/考勤) - api-gateway(直接路由 `/api/v1/exams` 等,绕过 BFF 的内部直连场景) - **下游**:MySQL(独占库 `core_edu_*` 表前缀)、Kafka(Outbox 事件发布)、Redis(P3 引入:作业高并发提交锁 + DataScope 缓存 + 短期聚合缓存) - **通信方式**: - 入口 HTTP:当前 REST(`/exams`、`/homework`、`/grades`),端口 **3004** - 入口 gRPC:**P3 启用**([004 §4.2](../../../docs/architecture/004_architecture_impact_map.md#42-grpc-启用阶段矩阵) 裁决),端口 **50053**,proto 已定义 `ExamService/HomeworkService/GradeService` - 出口:Kafka 事件(Outbox 模式,[004 §12.2](../../../docs/architecture/004_architecture_impact_map.md#12-架构约束) 强制,core-edu 不享受派生数据豁免) - **端口声明**([coord 全局端口矩阵](../../../docs/architecture/coord-cross-review.md#43-全局端口矩阵coord-裁决基线已同步至-004-12)): - HTTP:3004(P3) - gRPC:50053(P3 启用) - /metrics:随 HTTP 3004(Prometheus 抓取) ## 2. 我的限界上下文 - **聚合职责**:跨 **D2 教学组织**(classes 模块,待合并)+ **D3 教学核心**(exams / homework / grades + 排课/考勤 course/lesson/schedule/attendance 四表,[pending-features P3](../../../docs/architecture/roadmap/pending-features.md#p3-核心教学阶段m7-m10) 要求) - **聚合根**:Exam、Homework、Grade、Class(待合并)、Course、Lesson、Schedule、Attendance(P3 新增) - **我不负责**: - 不负责题库内容(→ content 服务,core-edu 通过事件通知 content 教学内容变更,[004 §4 服务依赖图](../../../docs/architecture/004_architecture_impact_map.md#4-服务依赖图) `CoreEdu -.事件.-> Content`) - 不负责学情分析/掌握度计算(→ data-ana 服务,消费 core-edu 事件) - 不负责通知投递(→ msg 服务,消费 core-edu 事件) - 不负责 AI 出题(→ ai 服务,ai 通过 gRPC 调 content 查题库) - 不负责权限/角色管理(→ iam,core-edu 仅消费 iam 事件同步教师关联) - **数据自治**:独占 `core_edu` 数据库(init-sql 中为 `next_edu_cloud.core_edu_*`),表前缀 `core_edu_*`,**禁止跨库联表**([004 §12.1](../../../docs/architecture/004_architecture_impact_map.md#12-架构约束)) - **CDC 联动**:MySQL binlog 已通过 Debezium 投递到 Kafka 的 `edu-cdc.next_edu_cloud.core_edu_grades` / `core_edu_exams` / `core_edu_homework` / `core_edu_attendance` topic,由 data-ana 消费写 ClickHouse 宽表([known-issues §2.6](../../../docs/troubleshooting/known-issues.md) 已实施) ## 3. 我与外部的契约 - **暴露的 gRPC 契约**([core_edu.proto](../../../packages/shared-proto/proto/core_edu.proto),包名 `next_edu_cloud.core_edu.v1`,已定义待 P3 启用 server): - `ExamService`:CreateExam / GetExam / ListExamsByClass / UpdateExam / DeleteExam - `HomeworkService`:AssignHomework / GetHomework / ListHomeworkByClass / SubmitHomework - `GradeService`:RecordGrade / GetGrade / ListGradesByStudent/Exam/Homework - **缺口**:缺 `AttendanceService`(coord 整改清单 #14,P3 补全,[coord §6](../../../docs/architecture/coord-cross-review.md#6-裁决整改清单汇总)) - **发布的领域事件**([events.proto](../../../packages/shared-proto/proto/events.proto) + [outbox.publisher.ts TOPIC_MAP](../src/shared/outbox/outbox.publisher.ts)): > **coord 已仲裁**([coord §3.1](../../../docs/architecture/coord-cross-review.md#31-core-edu-教学事件-topic-命名双轨p0-最严重)):topic 命名**统一为 `edu.teaching..`**,原代码中的 `edu.exam.events` / `edu.homework.events` / `edu.grade.events` / `edu.class.events` 风格**违规**,P3 必须改 TOPIC_MAP。下表已按裁决后规范列出。 | 事件(eventType) | Topic(裁决后) | 触发时机 | 消费者 | 当前状态 | | ------------------- | ------------------------------------------ | ------------------ | ------------- | ---------- | | exam.created | `edu.teaching.exam.created` | CreateExam 事务内 | msg、data-ana | ✅ 已发布 | | exam.updated | `edu.teaching.exam.updated` | UpdateExam | msg | ✅ 已发布 | | exam.deleted | `edu.teaching.exam.deleted` | DeleteExam | data-ana | ✅ 已发布 | | exam.published | `edu.teaching.exam.published` | 考试发布(状态机) | msg、data-ana | ❌ P3 新增 | | homework.assigned | `edu.teaching.homework.assigned` | AssignHomework | msg、data-ana | ✅ 已发布 | | homework.submitted | `edu.teaching.homework.submitted` | SubmitHomework | data-ana、msg | ✅ 已发布 | | homework.graded | `edu.teaching.homework.graded` | 教师批改完成 | msg、data-ana | ❌ P3 新增 | | grade.recorded | `edu.teaching.grade.recorded` | RecordGrade | data-ana、msg | ✅ 已发布 | | grade.updated | `edu.teaching.grade.updated` | 批改后修正 | data-ana | ❌ P3 新增 | | class.transferred | `edu.org.class.created`(合并后归 org 域) | classes 合并后 | data-ana | ⚠️ 待合并 | | attendance.recorded | `edu.teaching.attendance.recorded` | 考勤录入 | data-ana、msg | ❌ P3 新增 | - **事件 schema 版本化**:所有事件 payload 必须含 `schema_version` 字段(默认 `"v1"`),消费端按版本处理([known-issues §1.3](../../../docs/troubleshooting/known-issues.md))。当前 outbox payload 仅含业务字段,**P3 必须补 `schema_version`**。 - **消费的事件**: - `edu.identity.user.created` / `edu.identity.user.updated` / `edu.identity.user.deleted`(IAM,初始化/同步教师默认班级关联,[004 §7.2](../../../docs/architecture/004_architecture_impact_map.md#72-事件-topic-分类) 已定义,**当前未消费**,P3 待补) - `edu.insight.mastery.updated`(data-ana,掌握度更新后 core-edu 可消费用于推荐个性化练习,[004 §8.3](../../../docs/architecture/004_architecture_impact_map.md#83-异步事件闭环))— P3 可选,P4 强制 - **错误码前缀**:`CORE_EDU_*`(见 [application-error.ts CoreEduErrorCode](../src/shared/errors/application-error.ts)),子域(exams/homework/grades/attendance)**统一用 `CORE_EDU_*`**,不再细分 `EXAMS_` / `HOMEWORK_` / `GRADES_`([coord §5.5](../../../docs/architecture/coord-cross-review.md#55-p1-问题core-edu-子模块前缀) 仲裁) - **响应信封**:必须遵循 [004 §11.5](../../../docs/architecture/004_architecture_impact_map.md#115-统一响应信封actionstate) ActionState 信封 `{success, data? | error: {code, message, details?, traceId?}}`,GlobalErrorFilter 已注册 ## 4. 我的技术栈 - 语言:TypeScript 5.5+(ESM 模式,NestJS ESM 模式下相对 import 必须 `.js` 后缀) - 框架:NestJS 10 - ORM:Drizzle ORM(mysql2 driver,直接 `db` 导出,**与 classes 的 `getDb()` 不一致**,需 P3 统一) - 存储:MySQL 8(独占库 `core_edu_*` 表前缀)、Redis(P3 引入)、Kafka(kafkajs,idempotent + transactionalId `core-edu-tx`) - **gRPC server**:P3 启用(`@grpc/grpc-js` + `@bufbuild/protobuf`,coord 已在 [buf.gen.yaml](../../../packages/shared-proto/buf.gen.yaml) 补 gRPC 插件,[coord 整改 #16](../../../docs/architecture/coord-cross-review.md#6-裁决整改清单汇总)) - **Temporal**:P3 引入,仅试点 1 个工作流(考试发布编排:创建作业→通知,[pending-features P3](../../../docs/architecture/roadmap/pending-features.md#p3-核心教学阶段m7-m10)) - 可观测:pino + prom-client(已 4 指标:http_requests_total / request_duration_seconds / outbox_pending / outbox_published_total)+ OTel auto-instrumentations(已具备) ## 5. 我的阶段归属 - **P3 核心教学**:考试全生命周期 + Outbox + Kafka 事件落地 - **退出标准**([pending-features P3](../../../docs/architecture/roadmap/pending-features.md#p3-核心教学阶段m7-m10)):教师创建考试 → 发布 → 学生作答 → 教师批改 → 事件到 Kafka → 成绩统计更新 → 全链路可观测 - **P3 关键功能**: 1. CoreEdu 服务考试/作业/成绩域 CRUD + 批改业务编排 + Outbox 事件发布 2. MySQL schema:exams / exam_questions / homework_assignments / homework_submissions / homework_answers / grade_records / outbox_events(**当前 schema 仅 4 表**,缺 exam_questions / homework_submissions / homework_answers,P3 必须补全) 3. Outbox 模式 + Outbox relay worker(**当前 relay 在 NestJS 进程内 setInterval 5s,pending-features 提及"独立 Go 服务 `services/outbox-relay/`"是远期目标,P3 保留进程内模式**) 4. Kafka topics:`edu.teaching.exam.published` / `edu.teaching.homework.graded` / `edu.teaching.grade.recorded`(按 coord 仲裁后的命名) 5. Teacher BFF 扩展(考试/作业/成绩的查询与 mutation) 6. teacher-portal 扩展(考试创建/作业批改/成绩查看页面) 7. student-portal 微前端(学生作答作业页面) 8. Temporal 试点 1 个工作流(考试发布编排) 9. **gRPC server 启用(端口 50053)** 10. **排课/考勤数据模型(course/lesson/schedule/attendance 四表)+ AttendanceService proto 补全** - **依赖上游**:P1 classes 黄金模板、P2 iam(用户身份 + 权限 + DataScope) - **下游依赖方**:student-bff(P3)、parent-bff(P4,查看子女成绩/考勤)、data-ana(消费 CDC + 领域事件)、msg(消费事件触发通知)、content(接收教学内容变更事件) ## 6. 我需要对齐的黄金模板项(对照 classes 服务) - [x] 权限装饰器 `@RequirePermission`(exams.controller 全覆盖;需核对 homework/grades controller) - [x] 错误码前缀 `CORE_EDU_*`(已用 [CoreEduErrorCode 枚举](../src/shared/errors/application-error.ts)) - [x] logger / metrics / tracer 三支柱 - [x] `/healthz` 健康检查 - [x] `/readyz`(已实现 DB SELECT 1 探针,[health.controller.ts](../src/shared/health/health.controller.ts) Drizzle `db.execute(sql\`SELECT 1\`)`,**需补 Kafka 连接探针 + Redis ping 探针**) - [x] 优雅关闭 SIGTERM(main.ts 已处理 outboxPublisher.stop + disconnectKafka) - [ ] 测试覆盖率 ≥ 80%(**当前 0%**,无测试文件,仅 vitest.config.ts 配置就绪) - [ ] Dockerfile 多阶段构建(需核对) - [ ] Zod 输入验证(**当前 Controller 直接接收 body,未 Zod 校验**;classes 用 zod schema) - [x] GlobalErrorFilter 统一兜底(响应信封 ActionState 对齐,[coord §5.7](../../../docs/architecture/coord-cross-review.md#57-p2-问题错误信封结构不一致)) - [x] Outbox 模式(事务内写业务表 + outbox 表,独立 publisher 投递,**TOPIC_MAP 命名违规待修正**) - [ ] **gRPC server 实现**(P3 启用,端口 50053,proto 已定义) - [ ] **ActionState 信封 traceId**(GlobalErrorFilter 已输出 traceId,需验证) - [ ] **DataScope 下推**(004 §5.3 要求 Repository 层根据 dataScope 注入 WHERE,当前未实现) - [ ] **Drizzle 访问方式统一**(core-edu 用 `export const db`,classes 用 `getDb()` 函数式,需 P3 统一为 `getDb()`) - [ ] **kafka.ts 用 logger 替代 console.log/warn**([config/kafka.ts](../src/config/kafka.ts) L20/L22 违反 [known-issues §1.4](../../../docs/troubleshooting/known-issues.md) "禁止 console.*" 规则) --- ## 服务审计表 — ai08(core-edu) > 对照 [黄金模板 classes 服务](../../classes/src/),审计已实现的 core-edu 服务。状态:✅ 达标 / ⚠️ 部分 / ❌ 缺失 | 服务 | 权限装饰器 | 错误码前缀 | logger | metrics | tracer | /healthz | /readyz | 优雅关闭 | 测试覆盖率 | Dockerfile | gRPC server | ActionState | | -------- | -------------------------------------- | --------------- | -------------------------------- | --------------------- | ------------ | -------- | ------------------------------------- | -------- | ---------- | ---------- | ---------------- | ----------- | | core-edu | ✅ `@RequirePermission(EXAM_*)` 全覆盖 | ✅ `CORE_EDU_*` | ⚠️ pino(kafka.ts 仍用 console) | ✅ prom-client 4 指标 | ✅ OTel auto | ✅ | ⚠️ DB SELECT 1(缺 Kafka/Redis 探针) | ✅ | 0% ❌ | 待核对 | ❌ P3 启用 50053 | ✅ 已对齐 | ### 审计发现的关键差距(P3 阶段 2 设计需解决) > ai03 原列 12 项 + ai08 审计新增 8 项 = 20 项 1. ❌ 考试生命周期状态机缺失(当前仅 `draft` 初值,无 `published → in_progress → grading → graded → archived` 转换与校验) 2. ❌ 作业状态机不完整(仅 `assigned → submitted`,缺 `graded`;pending-features 要求 `HomeworkGraded` 事件) 3. ❌ 成绩录入无业务校验(不校验 exam/homework 是否存在、score 是否在 totalScore 范围内、是否重复录入) 4. ❌ 作业提交高并发优化缺失([004 §9.2](../../../docs/architecture/004_architecture_impact_map.md#92-高并发提交) 要求 Redis 分布式锁 + 排队) 5. ❌ 无 `grade.updated` / `homework.graded` 事件触发点(proto 已定义,service 未实现) 6. ❌ 未消费 IAM `user.created` / `user.updated` / `user.deleted` 事件(初始化教师默认关联) 7. ⚠️ Drizzle `db` 直接导出 vs classes 的 `getDb()` 函数式 — **不一致**,建议统一为 `getDb()` 8. ⚠️ [kafka.ts](../src/config/kafka.ts) 用 `console.log`/`console.warn`,应改用结构化 logger 9. ⚠️ classes 模块在 core-edu 仅有 `classes.module.ts` 占位,**P3 待合并**(classes 服务代码迁入 + 删除独立 services/classes) 10. ⚠️ 入口仍为 REST,proto gRPC 契约已定义但未接入 `@grpc/grpc-js` + buf generate 代码 11. ❌ 无 Zod 输入验证(Controller 直接接收 `body: CreateExamInput`,未走 zod schema) 12. ❌ 无测试 13. ❌ **TOPIC_MAP 命名违规**(coord 已仲裁统一为 `edu.teaching..`,当前代码用 `edu.exam.events` 等表名分组风格,[coord §3.1](../../../docs/architecture/coord-cross-review.md#31-core-edu-教学事件-topic-命名双轨p0-最严重)) 14. ❌ **排课/考勤数据模型缺失**(pending-features P3 要求 course/lesson/schedule/attendance 四表,当前 core-edu 仅 exams/homework/grades/outbox 四表) 15. ❌ **core_edu.proto 缺 AttendanceService**(coord 整改 #14,P3 必须补全) 16. ❌ **DataScope 下推未实现**([004 §5.3](../../../docs/architecture/004_architecture_impact_map.md#53-datascop-6-级数据范围) 要求 Repository 层根据 dataScope 注入 WHERE,当前 PermissionGuard 仅做粗粒度角色判断,未做行级数据过滤) 17. ❌ **事件 schema_version 字段缺失**([known-issues §1.3](../../../docs/troubleshooting/known-issues.md) 要求 Kafka 事件带 `schema_version`,当前 outbox payload 仅含业务字段) 18. ❌ **Temporal 工作流未引入**(pending-features P3 要求试点 1 个工作流:考试发布编排,当前未集成) 19. ⚠️ **outbox schema 缺少 event_id 字段**(消费端幂等去重要求 event_id,当前 outbox 仅用 id 作主键,但 event_id 应独立于 outbox id 以支持重投递幂等) 20. ❌ **成绩计算公式未配置化**(ai-allocation §5 ai08 设计重点要求支持加权/平均/自定义公式,当前仅原始 score 存储) ### 跨模块契约对齐(ai08 接管后核对 coord 仲裁结果) | 待确认项 | coord 仲裁结论 | 状态 | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | iam `user.created` 等事件 topic | `edu.identity.user.created` / `.updated` / `.deleted`([004 §7.2](../../../docs/architecture/004_architecture_impact_map.md#72-事件-topic-分类)) | ✅ 已仲裁,core-edu P3 实现消费端 | | core-edu 端口 3004 + gRPC 50053 | 不冲突,已纳入 [coord 全局端口矩阵](../../../docs/architecture/coord-cross-review.md#43-全局端口矩阵coord-裁决基线已同步至-004-12) | ✅ 已仲裁 | | Kafka topic 命名 | **统一为 `edu.teaching..`**([coord §3.1](../../../docs/architecture/coord-cross-review.md#31-core-edu-教学事件-topic-命名双轨p0-最严重)) | ✅ 已仲裁,core-edu P3 修 TOPIC_MAP | | data-ana 消费 core-edu 事件 | 消费 `edu.teaching.exam.created` / `homework.submitted` / `grade.recorded` + CDC topics | ✅ ai06 已确认,data-ana 已实施 CDC 消费 | | msg 消费 core-edu 事件 | 消费 `edu.teaching.exam.created` / `homework.assigned` / `grade.recorded` 触发通知 | ⚠️ 待 ai05 在 msg 02 文档确认消费契约 | | proto 包名规范 | 保持 `next_edu_cloud.core_edu.v1`([coord §2.1](../../../docs/architecture/coord-cross-review.md#21-proto-包名规范冲突p0-全局)) | ✅ 已仲裁 | | 错误码前缀 | `CORE_EDU_*` 统一(不再细分 `EXAMS_`/`HOMEWORK_`/`GRADES_`,[coord §5.5](../../../docs/architecture/coord-cross-review.md#55-p1-问题core-edu-子模块前缀)) | ✅ 已仲裁 | | core_edu.proto AttendanceService 缺失 | P3 补全([coord 整改 #14](../../../docs/architecture/coord-cross-review.md#6-裁决整改清单汇总)) | ❌ 待 ai08 P3 补全 proto + service 实现 | --- ## 下一步(阶段 2 入口) ai08 进入阶段 2,按 [ai-allocation.md §5 ai08 设计重点](../../../docs/architecture/ai-allocation.md) 产出模块架构设计文档 `02-architecture-design.md`: - **core-edu 模块架构设计**: - classes 黄金模板对齐(Drizzle `getDb()` 统一 + Zod + 测试 + Dockerfile 多阶段) - 考试生命周期状态机(草稿 → 已发布 → 作答中 → 批改中 → 已出分 → 已归档) - Outbox 事件定义与发布(补全 `exam.published` / `homework.graded` / `grade.updated` / `attendance.recorded`,TOPIC_MAP 重命名为 `edu.teaching.*`,payload 补 `schema_version` + `event_id`) - 成绩计算公式与配置化(加权/平均/自定义公式) - 作业提交高并发优化(Redis 分布式锁 + 排队机制) - 排课/考勤数据模型(course/lesson/schedule/attendance 四表)+ AttendanceService proto 补全 - 与 Kafka 的 Relay Worker 轮询逻辑(保留进程内模式,远期迁出独立 Go 服务) - gRPC server 启用(端口 50053) - DataScope 下推(Repository 层 WHERE 注入) - Temporal 工作流试点(考试发布编排) - 消费 IAM 事件(user.created/updated/deleted) 阶段 2 设计需先解决上述 20 项差距与跨模块契约对齐。 --- **AI Agent**: ai08 (core-edu) **Coordinator**: coord-ai **Branch**: 单仓库并行模式(直接 push main)