Files
Edu/services/classes/docs/01-understanding.md
SpecialX faaaf29f67 docs: ai 协作文档体系重构与多 ai 仲裁结果落地
1.AI 协作文档体系重构(objections/worklines/contracts+matrix.md)

2.coord 仲裁文档(final-decisions/cross-review/final-rulings/orchestration)

3.各服务 01/02 文档补全

4.共享包初始化(shared-ts/shared-go/hooks/ui-components/ui-tokens)

5.Proto 契约补全

6.004 架构影响地图更新

7.端口分配表

8.设计规格文档
2026-07-10 12:58:22 +08:00

199 lines
16 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.
# 模块理解确认书 — classes
> AIai07TS / 教学组织 · 黄金模板)
> 阶段:阶段 1 交付物
> 日期2026-07-10
> 关联:[004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md)、[AI 分配方案](../../../docs/architecture/ai-allocation.md)、[pending-features P1](../../../docs/architecture/roadmap/pending-features.md)、[项目规则](../../../.trae/rules/project_rules.md)
>
> **本确认书定位**classes 是 Edu 微服务的 **黄金模板**ai-allocation.md §3.4),是其余 8 个 TS 服务的对照基准。本确认书既是对 classes 现状的审计,也是为其他 AI 提供的"读这一份即可理解黄金模板全貌"的入口。
---
## 1. 我在架构中的位置
- **层级**L5 业务微服务层004 §3.1 六层架构)
- **业务领域****D2 教学组织领域**004 §1.1b承载班级Class聚合根
- **上游(谁调用我)**
- `api-gateway`Go/GinHTTP 反向代理 `/api/v1/classes/*``http://classes:3001/classes/*`Gateway 已注册 classes 路由块,含精确路径 + 通配路径)
- `teacher-bff`NestJSP2 起通过 GraphQL 聚合调用 classes 班级列表(教师仪表盘)
- 未来:`student-bff``parent-bff`(同样走 BFF 聚合)
- **下游(我调用谁)**
- `MySQL`(独占库 `classes_db`,连接串 `DATABASE_URL`mysql2 连接池 + Drizzle ORM
- `Redis`(依赖已声明 `ioredis`env 已留 `REDIS_URL` 占位当前未启用P3 合并入 core-edu 后启用班级列表缓存)
- `Kafka`(依赖已声明 `kafkajs`当前未启用P3 起 Outbox 事件发布 `edu.org.class.created` 等)
- **通信方式**
- 入口:**HTTP/REST**(当前实现,端口 3001
- 出口MySQLDrizzle ORM
- 演进proto 已定义 `ClassService`5 个 RPCP3 起 core-edu 合并 classes 后启用 gRPC server 50053
- **不持有跨服务状态**:无本地内存缓存,无 WebSocket 连接
### 1.1 黄金模板职责
classes 除了承载 D2 教学组织领域(班级 CRUD业务外**首要职责是作为黄金模板**
| 黄金模板职责 | 说明 |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| 横切关注点基准 | 错误处理 / 可观测性 / 安全 / 契约 / 测试 / 文档 / 配置 / Dockerfile 全部落地,其他 TS 服务对齐 |
| 目录结构基准 | `src/{main,app.module,config,middleware,<domain>,shared}` 标准结构 |
| 对照 checklist 源 | 产出黄金模板对照 checklist见 [02 架构设计 §10](./02-architecture-design.md#10-黄金模板对照-checklist供其他-ts-服务自检))供 ai01-ai10 自检 |
| 模式回写 | 各阶段新模式实现后回写本模板Outbox 模式 P3 / 长连接 P5 / CDC P4 |
---
## 2. 我的限界上下文
### 2.1 我负责的聚合 / 实体
| 聚合根 | 实体 / 值对象 | 当前表 | 职责 |
| ----------------- | ----------------------------------------- | --------- | --------------------------------- |
| **Class**(班级) | name、gradeId、headTeacherId、description | `classes` | 班级 CRUD、按年级过滤、班主任归属 |
> **演进**P3 合并入 core-edu 后classes 表与 `subjects`(学科)、`enrollment`(选课)同属 D2 教学组织域,由 core-edu 统一承载(见 004 §1.3 CICD→Edu 映射)。
### 2.2 业务领域归属
- **D2 教学组织领域**004 §1.1bclasses 独占此领域当前P3 起与 subjects、enrollment 共同组成 D2
- 上游依赖:无业务领域依赖(教学组织是基础数据,不依赖其他业务领域)
- 下游被依赖:
- core-edu D3 教学核心域(考试/作业按班级归属)
- data-ana D6 智能洞察域(班级维度统计聚合)
- msg D5 沟通通知域(班级群组广播)
### 2.3 我不负责什么(边界外)
- ❌ 学科subjects→ core-eduP3 新增D2 教学组织)
- ❌ 选课 / 学生分班enrollment→ core-eduP3 新增)
- ❌ 考试 / 作业 / 成绩 → core-eduD3 教学核心)
- ❌ 教材 / 知识点 / 题库 → contentD4 内容资源)
- ❌ 站内信 / 通知投递 → msgD5 沟通通知)
- ❌ 学情分析 / 掌握度计算 → data-anaD6 智能洞察)
- ❌ 用户 / 角色 / 权限管理 → iamD1 身份认证)
- ❌ JWT 校验 → api-gatewayclasses 仅读 `x-user-id` / `x-user-roles` 头)
- ❌ 年级grade数据 → 当前 classes 仅持有 `gradeId` 外键引用,年级实体归属待 core-edu P3 定义(当前由前端/管理端维护)
---
## 3. 我与外部的契约
### 3.1 我消费的 proto message从 shared-proto
- 当前**不消费**任何外部 protoclasses 是基础数据源,不反向依赖业务服务)
- 未来 P3 起core-edu 合并 classes 后,消费 `iam.proto``BatchGetUsers`(班主任信息批量查询)
### 3.2 我暴露的契约
#### 当前 REST API已实现见 [classes.controller.ts](../src/classes/classes.controller.ts)
| Method | Path | 权限 | 请求体 / 参数 | 响应 | 说明 |
| ------ | -------------- | ---------------- | -------------------------- | ---------------------------------- | ---------------------------------- |
| POST | `/classes` | `CLASSES_CREATE` | `{name, gradeId, ...}` | `{success, data: ClassResponse}` | 创建班级 |
| GET | `/classes` | `CLASSES_READ` | `?gradeId=<uuid>`(可选) | `{success, data: ClassResponse[]}` | 列表,支持按年级过滤 |
| GET | `/classes/:id` | `CLASSES_READ` | path: `id` | `{success, data: ClassResponse}` | 单条查询 |
| PUT | `/classes/:id` | `CLASSES_UPDATE` | path:`id` + `{name?, ...}` | `{success, data: ClassResponse}` | 更新(空 body 抛 ValidationError |
| DELETE | `/classes/:id` | `CLASSES_DELETE` | path: `id` | `{success: true}` | 删除(先校验存在) |
> **响应信封**:遵循 ActionState004 §11.5),成功 `{success:true, data:T}`,失败 `{success:false, error:{code,message,details?,traceId?}}`。
#### 健康检查(见 [health.controller.ts](../src/shared/health/health.controller.ts)
| Method | Path | 鉴权 | 用途 |
| ------ | ---------- | ---- | ------------------------------------------------------------------- |
| GET | `/healthz` | 无 | liveness进程存活 |
| GET | `/readyz` | 无 | readiness校验 DB `SELECT 1`,失败 503 |
| GET | `/metrics` | 无 | Prometheus 指标抓取(见 [main.ts](../src/main.ts) 第 23-26 行注册) |
#### Proto 契约(见 [classes.proto](../../../packages/shared-proto/proto/classes.proto)
```protobuf
package next_edu_cloud.classes.v1;
service ClassService {
rpc CreateClass(CreateClassRequest) returns (Class);
rpc GetClass(GetClassRequest) returns (Class);
rpc ListClasses(ListClassesRequest) returns (ListClassesResponse);
rpc UpdateClass(UpdateClassRequest) returns (Class);
rpc DeleteClass(DeleteClassRequest) returns (Empty);
}
```
> **proto 完备性**5 个 RPC 与 REST 端点一一对应,已包含分页字段(`page_size` / `page_token`REST 实现尚未落地分页。P3 合并后由 core-edu 启用 gRPC server 50053004 §16.7.3 #1
#### 我发布的领域事件当前未实现P3 由 core-edu 接管)
| 事件 | 触发时机 | Topic遵循 004 §7.2 | 消费者 |
| ------------------ | ------------ | --------------------------- | -------------------------------- |
| `ClassCreated` | 班级创建成功 | `edu.org.class.created` | data-ana建班级维度宽表行 |
| `ClassUpdated` | 班级信息变更 | `edu.org.class.updated` | data-ana、msg班主任变更通知 |
| `ClassDeleted` | 班级删除 | `edu.org.class.deleted` | data-ana、core-edu关联检查 |
| `ClassTransferred` | 班主任变更 | `edu.org.class.transferred` | msg通知新/旧班主任、data-ana |
> **事件 message 契约**`events.proto` 已定义 `ClassEvent`event_id / aggregate_id / event_type / occurred_at / class_id / name / action / metadata见 [events.proto](../../../packages/shared-proto/proto/events.proto) 第 15-24 行。Topic 命名遵循 004 §7.2 `edu.<domain>.<aggregate>.<action>` 强制格式。
---
## 4. 我的技术栈
| 维度 | 选型 | 说明 |
| -------- | --------------------------------- | --------------------------------------------------------------- |
| 语言 | TypeScript 5.6+ | ESM 模式(`"type": "module"`),相对 import 用 `.js` 后缀 |
| 框架 | NestJS 10 | 装饰器 + DI适合 DDD 分层 |
| ORM | Drizzle ORM 0.31 | 参数化查询,类型安全,替代 TypeORM |
| 数据库 | MySQL 8mysql2 驱动) | 连接池 `connectionLimit: 10` |
| 校验 | Zod 3.23 | 输入 DTO 校验controller 层 `schema.parse(body)` |
| 日志 | pino 9 + pino-prettydev | 结构化 JSON`base:{service,version}` |
| 指标 | prom-client 15 | 自定义 Counter/Histogram + 默认进程指标,`/metrics` 端点 |
| 链路 | OpenTelemetry SDK + OTLP exporter | auto-instrumentationsserviceName=`classes` |
| 测试 | Vitest 2.1 + @vitest/coverage-v8 | 单元测试mock repository阈值 60%(待提升至 80% |
| 容器 | node:20-alpine多阶段构建 | builder + runtime 两阶段(项目规则要求 node:22-alpine待对齐 |
| ID 生成 | uuid v4 | 当前实现project_rules 要求 cuid2黄金模板待迁移 |
| 预留依赖 | ioredis、kafkajs | 已声明但未使用,为 P3 Redis 缓存 + Kafka 事件预留 |
---
## 5. 我的阶段归属
- **阶段****P1 地基阶段**(已实现,黄金模板)
- **当前阶段目标**pending-features P1classes 域 CRUD 端到端跑通 + 全横切关注点落地 + 30 分钟可复制新服务
- **依赖上游阶段产出**P1 是地基classes 是最早交付的服务)
- **未来演进路径**
- P3合并入 core-eduai08 负责classes 表 + subjects + enrollment 共同组成 D2 教学组织域;启用 gRPC server 50053
- P3补齐 Outbox 事件发布能力(`shared/outbox/` 目录)
- P3启用 Redis 班级列表缓存TTL 5 分钟,事件驱动失效,见 004 §6.3
- P3落地 DataScope 6 级 WHERE 注入(见 004 §5.3,当前 PermissionGuard 仅做角色级校验)
---
## 6. 我需要对齐的黄金模板项(对照 classes 自身——作为基准的自我审计)
> classes 是黄金模板本身,本节为**自我审计**,确认各项已落地,供其他 TS 服务对照。
| 检查项 | 状态 | 证据 | 待整改 |
| ------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------ | ---------- |
| 权限装饰器 `@RequirePermission` | ✅ | [permission.guard.ts](../src/middleware/permission.guard.ts) + controller 全部方法装饰 | — |
| 错误码前缀 `CLASSES_` | ✅ | [application-error.ts](../src/shared/errors/application-error.ts) 7 类错误码均带前缀 | — |
| loggerpino | ✅ | [logger.ts](../src/shared/observability/logger.ts) | — |
| metricsprom-client | ✅ | [metrics.ts](../src/shared/observability/metrics.ts) + `/metrics` 端点 | — |
| tracerOTel | ⚠️ | [tracer.ts](../src/shared/observability/tracer.ts) 第 20 行仍用 `console.log`,需改 `logger.info` | P1 整改 |
| `/healthz` | ✅ | [health.controller.ts](../src/shared/health/health.controller.ts) liveness | — |
| `/readyz` | ✅ | readiness 校验 DB `SELECT 1`,失败 503 | — |
| 优雅关闭SIGTERM | ✅ | [main.ts](../src/main.ts) 第 31-34 行 + [lifecycle.service.ts](../src/shared/lifecycle/lifecycle.service.ts) | — |
| Zod 输入验证 | ✅ | [classes.dto.ts](../src/classes/classes.dto.ts) + controller `schema.parse(body)` | — |
| GlobalErrorFilter | ✅ | [global-error.filter.ts](../src/shared/errors/global-error.filter.ts) 捕获 ZodError + ApplicationError | — |
| Dockerfile 多阶段 | ✅ | [Dockerfile](../Dockerfile) builder + runtime 两阶段 | node:20→22 |
| 测试覆盖率 | ⚠️ | [vitest.config.ts](../vitest.config.ts) 阈值 60%,需提升至 80% | P1 整改 |
| `shared/outbox/` 目录 | ❌ | 缺失,需补齐事件发布能力 | P3 补齐 |
| `typeorm` 冗余依赖 | ❌ | [package.json](../package.json) 第 33 行仍声明 typeorm已用 Drizzle需移除 | P1 整改 |
| `@Req()``@Query()` | ❌ | [classes.controller.ts](../src/classes/classes.controller.ts) 第 49-53 行 list 方法仍用 `@Req()` | P1 整改 |
| ID 生成 cuid2 | ❌ | [classes.service.ts](../src/classes/classes.service.ts) 第 1 行用 uuid v4 | P3 迁移 |
> **整改项汇总**4 项 P1 立即整改tracer console.log / 测试覆盖率 / typeorm 移除 / @Req 替换)+ 2 项 P3 演进outbox / cuid2详见 [02 架构设计 §11 整改清单](./02-architecture-design.md#11-整改清单与里程碑)。
---
## 7. 待 coord 仲裁 / 提请事项
| # | 议题 | 我的倾向 | 影响 |
| --- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------- | ------------------------------------------- |
| 1 | classes 在 P3 合并入 core-edu 后,黄金模板源码是否保留独立目录 `services/classes/` | 保留作为只读对照基准core-edu 复制其结构) | 影响 ai08 合并策略与黄金模板 checklist 归属 |
| 2 | `node:20-alpine``node:22-alpine` 升级是否在 P1 立即做? | 立即做project_rules §15.8 镜像预拉清单已是 node:22-alpine | 影响所有 TS 服务 Dockerfile 基准 |
| 3 | cuid2 迁移时机P1 黄金模板立即迁移,还是 P3 合并时统一迁移? | P1 立即迁移(黄金模板应先行,避免 ai08 继承 uuid 遗留) | 影响 classes 表主键、proto message 字段类型 |