NestJS (6 services): implement @RequirePermission decorator with SetMetadata+Reflector, register APP_GUARD globally, fix as assertions to type guards, add explicit return types, fix import type for express, fix /metrics implicit any, replace native Error with ApplicationError, remove typeorm remnants, register LifecycleService. teacher-bff: add logger, ApplicationError, GlobalErrorFilter, forward real userId to downstream, log downstream failures, migrate health controller to shared/health. Go (2 services): interface to any, doc comments, CORS dev whitelist, JWT secret fail-fast, push-gateway internal API auth, metrics and readyz endpoints, remove dead code. Python (2 services): lifespan return type, dev_mode to bool, data-ana APIRouter, ai POST body model, ClickHouse async wrapping.
15 KiB
15 KiB
ai03 阶段 1 交付物:模块理解确认书
AI 标识:ai03 负责模块:teacher-bff(P2)、core-edu(P3) 阶段:架构设计外包 · 阶段 1(全局理解) 日期:2026-07-09 关联文档:ai-allocation.md、004 架构影响地图、pending-features.md
模块理解确认书 — teacher-bff
1. 我在架构中的位置
- 层级:BFF 聚合层(L4)
- 上游:api-gateway(Go/Gin)通过 HTTP 转发请求,注入
x-user-id/x-user-roles头 - 下游:iam(3002)、classes(3001)、core-edu(3004);P3 后扩展 content、data-ana、ai
- 通信方式:
- 当前:REST
fetch(同步) - 目标态(004 §4.1 / pending-features P2):gRPC 调下游业务服务 + GraphQL Yoga 对前端暴露 + DataLoader 防 N+1
- 当前:REST
- 端口:3003(见 teacher-bff env.ts)
2. 我的限界上下文
- 聚合职责:教学场景域(教师 / 教导主任 / 教研组长 共用)的数据聚合、裁剪、协议转换
- 业务领域:跨 D2 教学组织 + D3 教学核心 + D1 身份认证(只读拉取视口/权限)
- 我不负责:
- 不持有业务状态(无 DB 写入,无 Outbox)
- 不做权限决策(依赖 Gateway JWT 校验 + 下游服务
@RequirePermission) - 不直接访问任何业务服务数据库
- 复用策略(004 §5.4):教导主任 / 教研组长复用 Teacher BFF,通过视口差异化(L1 导航扩展管理菜单,L4 DataScope 扩大到年级)
3. 我与外部的契约
- 消费的 proto message:
iam.v1.IamService:GetUserInfo / getEffectivePermissions(视口 + DataScope)classes.v1.ClassService:ListClasses / GetClasscore_edu.v1.ExamService / HomeworkService / GradeService:全部 RPC
- 暴露的 API(当前 REST,目标 GraphQL):
GET /teacher/dashboard— 聚合 IAM 用户信息 + classes 列表GET /teacher/viewports— 拉取 IAM 视口配置(L1 导航)GET /teacher/classes/:classId/exams— 聚合 core-edu 考试列表GET /teacher/classes/:classId/homework— 聚合 core-edu 作业列表GET /teacher/exams/:examId/grades— 聚合 core-edu 成绩列表
- 错误码前缀:BFF 自身错误用
BAD_GATEWAY(下游不可达);业务错误透传下游CORE_EDU_*/IAM_*/CLASSES_* - 缓存:聚合结果 Redis 短缓存 5-30s(004 §6.3,当前未实现)
4. 我的技术栈
- 语言:TypeScript 5.5+(ESM 模式,相对 import 带
.js后缀) - 框架:NestJS 10
- 下游通信:当前
fetch(REST)→ 目标@grpc/grpc-js+@bufbuild/protobuf - 对前端:目标 GraphQL Yoga + DataLoader
- 缓存:Redis(待引入)
- 可观测:pino logger + prom-client metrics + OpenTelemetry tracer(已具备 tracer.ts)
5. 我的阶段归属
- P2 身份:教师登录 → 获取 JWT → 访问 teacher-portal → 侧边栏按 viewports.L1 渲染 → 空白 Dashboard
- P3 扩展:考试/作业/成绩的 GraphQL 查询与 mutation
- 依赖上游:P1 黄金模板 classes、P2 iam(getEffectivePermissions + 视口)
6. 我需要对齐的黄金模板项(对照 classes 服务)
- 权限装饰器
@RequirePermission(BFF 不做权限决策,当前无;目标态:BFF 不加 Guard,仅校验x-user-id存在) - 错误处理:GlobalErrorFilter + ApplicationError 层次
- logger / metrics / tracer 三支柱(已具备)
/healthz健康检查(HealthModule 已注册)/readyz(当前 HealthController 仅 liveness,无下游就绪探针)- 优雅关闭 SIGTERM(main.ts 已处理)
- 测试覆盖率 ≥ 80%(当前 0%,无测试文件)
- Dockerfile 多阶段构建(需核对)
- Zod 输入验证(当前 Controller 直接透传 unknown,未做 Zod 校验)
- GlobalErrorFilter 统一兜底
模块理解确认书 — core-edu
1. 我在架构中的位置
- 层级:业务微服务层(L5)
- 上游:teacher-bff(聚合层)、api-gateway(直接路由
/api/v1/exams等) - 下游:MySQL(独占库)、Kafka(事件发布)、Redis(待引入,高并发提交锁)
- 通信方式:
- 入口:当前 REST(
/exams、/homework、/grades) - 目标态(proto 注释):P3 起转 gRPC(
core_edu.v1.ExamService/HomeworkService/GradeService已定义) - 出口:Kafka 事件(Outbox 模式发布)
- 入口:当前 REST(
- 端口:3004(见 core-edu env.ts)
2. 我的限界上下文
- 聚合职责:跨 D2 教学组织(classes 模块,待合并)+ D3 教学核心(exams / homework / grades)
- 聚合根:Exam、Homework、Grade、Class(待合并)
- 我不负责:
- 不负责题库内容(→ content 服务)
- 不负责学情分析(→ data-ana 服务,消费 core-edu 事件)
- 不负责通知投递(→ msg 服务,消费 core-edu 事件)
- 数据自治:独占
core_edu数据库,表前缀core_edu_*
3. 我与外部的契约
- 暴露的 gRPC 契约(core_edu.proto,已定义待实现):
ExamService:CreateExam / GetExam / ListExamsByClass / UpdateExam / DeleteExamHomeworkService:AssignHomework / GetHomework / ListHomeworkByClass / SubmitHomeworkGradeService:RecordGrade / GetGrade / ListGradesByStudent/Exam/Homework
- 发布的领域事件(events.proto + outbox.publisher.ts TOPIC_MAP):
| 事件 | Topic | 触发时机 | 消费者 |
|---|---|---|---|
| exam.created | edu.exam.events | CreateExam 事务内 | msg、data-ana |
| exam.updated | edu.exam.events | UpdateExam | msg |
| exam.deleted | edu.exam.events | DeleteExam | data-ana |
| homework.assigned | edu.homework.events | AssignHomework | msg、data-ana |
| homework.submitted | edu.homework.events | SubmitHomework | data-ana、msg |
| homework.graded | edu.homework.events | 未实现(P3 待补) | msg、data-ana |
| grade.recorded | edu.grade.events | RecordGrade | data-ana、msg |
| grade.updated | edu.grade.events | 未实现(P3 待补) | data-ana |
| class.transferred | edu.class.events | classes 合并后 | data-ana |
- 消费的事件:
edu.identity.user.created(IAM,初始化教师默认班级关联,当前未消费,P3 待补) - 错误码前缀:
CORE_EDU_*(见 application-error.ts CoreEduErrorCode)
4. 我的技术栈
- 语言:TypeScript 5.5+(ESM 模式)
- 框架:NestJS 10
- ORM:Drizzle ORM(mysql2 driver,直接
db导出,与 classes 的getDb()不一致) - 存储:MySQL 8(独占库)、Redis(待引入)、Kafka(kafkajs,idempotent + transactionalId)
- 可观测:pino + prom-client + OTel(已具备)
5. 我的阶段归属
- P3 核心教学:考试全生命周期 + Outbox + Kafka 事件落地
- 退出标准:教师创建考试 → 发布 → 学生作答 → 教师批改 → 事件到 Kafka → 成绩统计更新 → 全链路可观测
- 依赖上游:P1 classes 黄金模板、P2 iam(用户身份 + 权限)
6. 我需要对齐的黄金模板项(对照 classes 服务)
- 权限装饰器
@RequirePermission(exams.controller 全覆盖;需核对 homework/grades controller) - 错误码前缀
CORE_EDU_*(已用 CoreEduErrorCode 枚举) - logger / metrics / tracer 三支柱
/healthz健康检查/readyz(需补 DB SELECT 1 / Kafka 连接探针)- 优雅关闭 SIGTERM(main.ts 已处理 outboxPublisher.stop + disconnectKafka)
- 测试覆盖率 ≥ 80%(当前 0%,无测试文件)
- Dockerfile 多阶段构建(需核对)
- Zod 输入验证(当前 Controller 直接接收 body,未 Zod 校验;classes 用 zod schema)
- GlobalErrorFilter 统一兜底
- Outbox 模式(事务内写业务表 + outbox 表,独立 publisher 投递)
§10 服务审计表 — ai03
对照 黄金模板 classes 服务,审计已实现的两服务。状态:✅ 达标 / ⚠️ 部分 / ❌ 缺失
| 服务 | 权限装饰器 | 错误码前缀 | logger | metrics | tracer | /healthz | /readyz | 优雅关闭 | 测试覆盖率 | Dockerfile |
|---|---|---|---|---|---|---|---|---|---|---|
| teacher-bff | ⚠️ 无(BFF 不做权限决策,依赖 Gateway) | ⚠️ 用 BAD_GATEWAY(无自有前缀) |
✅ pino | ✅ prom-client | ✅ OTel | ✅ | ❌ | ✅ | 0% ❌ | 待核对 |
| core-edu | ✅ @RequirePermission(EXAM_*) 全覆盖 |
✅ CORE_EDU_* |
✅ pino | ✅ prom-client | ✅ OTel | ✅ | ❌ | ✅ | 0% ❌ | 待核对 |
审计发现的关键差距(P3 阶段 2 设计需解决)
teacher-bff:
- ❌ 通信方式:当前 REST
fetch,目标 gRPC + GraphQL(P2 退出标准要求 GraphQL Yoga + DataLoader) - ❌ 无 Redis 聚合缓存(004 §6.3 要求 5-30s 短缓存)
- ❌ 无 DataLoader(防 N+1,pending-features P2 明确要求)
- ❌ 无
/readyz下游就绪探针 - ⚠️ env 配置用
IamServiceUrl/ClassesServiceUrl(REST URL),转 gRPC 后需改为 gRPC target - ⚠️ 无 Zod 输入验证
- ⚠️ 无测试
core-edu:
- ❌ 考试生命周期状态机缺失(当前仅
draft初值,无published → in_progress → grading → graded → archived转换与校验) - ❌ 作业状态机不完整(仅
assigned → submitted,缺graded;pending-features 要求HomeworkGraded事件) - ❌ 成绩录入无业务校验(不校验 exam/homework 是否存在、score 是否在 totalScore 范围内、是否重复录入)
- ❌ 作业提交高并发优化缺失(004 §9.2 要求 Redis 分布式锁 + 排队)
- ❌ 无
grade.updated/homework.graded事件触发点(proto 已定义,service 未实现) - ❌ 未消费 IAM
user.created事件(初始化教师默认关联) - ⚠️ Drizzle
db直接导出 vs classes 的getDb()函数式 — 不一致,建议统一为getDb() - ⚠️ kafka.ts 用
console.log/console.warn,应改用结构化 logger - ⚠️ classes 模块在 core-edu 仅有
classes.module.ts占位,P3 待合并(classes 服务代码迁入 + 删除独立 services/classes) - ⚠️ 入口仍为 REST,proto gRPC 契约已定义但未接入
@grpc/grpc-js+ buf generate 代码 - ❌ 无 Zod 输入验证(Controller 直接接收
body: CreateExamInput,未走 zod schema) - ❌ 无测试
跨模块契约对齐待确认项(提请 coord 交叉审查)
| 待确认项 | 我方期望 | 对方模块 | 状态 |
|---|---|---|---|
iam getEffectivePermissions(userId) 返回结构 |
{permissions, viewports, dataScope} |
iam(ai02) | ⚠️ 当前 teacher-bff 调 /iam/viewports 与 /iam/me,未定义此聚合 API 的 proto |
iam user.created 事件 topic |
edu.identity.user.created |
iam(ai02) | ⚠️ 004 §7.2 定义,但 core-edu 未消费,需确认 iam 是否发布 |
| core-edu 端口 3004 | 不冲突 | 全局端口矩阵 | 待 coord 核对 |
| Kafka topic 命名 | edu.exam.events / edu.homework.events / edu.grade.events / edu.class.events |
004 §7.2 用 edu.teaching.* 前缀 |
⚠️ 不一致:004 文档用 edu.teaching.exam.published,代码用 edu.exam.events,需 coord 仲裁统一 |
| data-ana 消费 core-edu 事件 | 消费 exam.created / homework.submitted / grade.recorded |
data-ana(ai06) | 待 ai06 确认消费契约 |
| msg 消费 core-edu 事件 | 消费 exam.created / homework.assigned / grade.recorded 触发通知 |
msg(ai05) | 待 ai05 确认消费契约 |
下一步(阶段 2 入口)
待 coord 审核本确认书通过后,ai03 进入阶段 2,按 ai-allocation.md §5 ai03 设计重点 产出两份模块架构设计文档:
- teacher-bff 模块架构设计:GraphQL schema(Query/Mutation 按场景域组织)、DataLoader 批量去重、并行 gRPC 编排、聚合缓存 TTL、教师角色差异化视口推导
- core-edu 模块架构设计:classes 黄金模板对齐、考试生命周期状态机、Outbox 事件定义、成绩计算配置化、作业提交高并发(Redis 锁 + 排队)、排课/考勤数据模型
阶段 2 设计需先解决上述 12 项差距与 6 项跨模块契约对齐。
AI Agent: ai03 (teacher-bff + core-edu) Coordinator: coord-ai Branch: 单仓库并行模式(直接 push main)