# 模块理解确认书 — classes

> AI：ai07（TS / 教学组织 · 黄金模板）
> 阶段：阶段 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/Gin）：HTTP 反向代理 `/api/v1/classes/*` → `http://classes:3001/classes/*`（Gateway 已注册 classes 路由块，含精确路径 + 通配路径）
  - `teacher-bff`（NestJS）：P2 起通过 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）
  - 出口：MySQL（Drizzle ORM）
  - 演进：proto 已定义 `ClassService`（5 个 RPC），P3 起 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.1b）：classes 独占此领域（当前），P3 起与 subjects、enrollment 共同组成 D2
- 上游依赖：无业务领域依赖（教学组织是基础数据，不依赖其他业务领域）
- 下游被依赖：
  - core-edu D3 教学核心域（考试/作业按班级归属）
  - data-ana D6 智能洞察域（班级维度统计聚合）
  - msg D5 沟通通知域（班级群组广播）

### 2.3 我不负责什么（边界外）

- ❌ 学科（subjects）→ core-edu（P3 新增，D2 教学组织）
- ❌ 选课 / 学生分班（enrollment）→ core-edu（P3 新增）
- ❌ 考试 / 作业 / 成绩 → core-edu（D3 教学核心）
- ❌ 教材 / 知识点 / 题库 → content（D4 内容资源）
- ❌ 站内信 / 通知投递 → msg（D5 沟通通知）
- ❌ 学情分析 / 掌握度计算 → data-ana（D6 智能洞察）
- ❌ 用户 / 角色 / 权限管理 → iam（D1 身份认证）
- ❌ JWT 校验 → api-gateway（classes 仅读 `x-user-id` / `x-user-roles` 头）
- ❌ 年级（grade）数据 → 当前 classes 仅持有 `gradeId` 外键引用，年级实体归属待 core-edu P3 定义（当前由前端/管理端维护）

---

## 3. 我与外部的契约

### 3.1 我消费的 proto message（从 shared-proto）

- 当前**不消费**任何外部 proto（classes 是基础数据源，不反向依赖业务服务）
- 未来 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}`                  | 删除（先校验存在）                 |

> **响应信封**：遵循 ActionState（004 §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 50053（004 §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 8（mysql2 驱动）            | 连接池 `connectionLimit: 10`                                    |
| 校验     | Zod 3.23                          | 输入 DTO 校验，controller 层 `schema.parse(body)`               |
| 日志     | pino 9 + pino-pretty（dev）       | 结构化 JSON，`base:{service,version}`                           |
| 指标     | prom-client 15                    | 自定义 Counter/Histogram + 默认进程指标，`/metrics` 端点        |
| 链路     | OpenTelemetry SDK + OTLP exporter | auto-instrumentations，serviceName=`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 P1）：classes 域 CRUD 端到端跑通 + 全横切关注点落地 + 30 分钟可复制新服务
- **依赖上游阶段产出**：无（P1 是地基，classes 是最早交付的服务）
- **未来演进路径**：
  - P3：合并入 core-edu（ai08 负责），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 类错误码均带前缀                         | —          |
| logger（pino）                  | ✅   | [logger.ts](../src/shared/observability/logger.ts)                                                           | —          |
| metrics（prom-client）          | ✅   | [metrics.ts](../src/shared/observability/metrics.ts) + `/metrics` 端点                                       | —          |
| tracer（OTel）                  | ⚠️   | [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 字段类型 |
