chore(content): merge content full implementation into main

Merge feat/content-ai09 with complete content service
This commit is contained in:
SpecialX
2026-07-10 19:13:02 +08:00
74 changed files with 5256 additions and 355 deletions

View File

@@ -1,26 +1,30 @@
# content 内容资源服务
> 版本:0.1P4 骨架
> 端口:3005
> 版本:1.0P4 完整实现
> 端口:HTTP 3005 / gRPC 50054
## 职责
内容资源限界上下文,管理 Textbook、Chapter、KnowledgePoint 聚合。
支持多存储MySQL教材/章节/知识点写模型)+ Neo4j知识图谱前置依赖+ Elasticsearch题库全文检索P4 后续补充)。
内容资源限界上下文,管理 Textbook、Chapter、KnowledgePoint、Question 四个聚合。
支持多存储MySQL写模型权威源+ Neo4j知识图谱派生读模型+ ElasticsearchP5 引入,题库全文检索)。
事件驱动Outbox 模式发布 4 个聚合 topicNeo4j Sync Worker 异步消费事件同步图谱。
## 技术栈
- NestJS 10.x + TypeScript 5.6ESM
- Drizzle ORMMySQL
- neo4j-driver知识图谱
- Zod运行时校验
- NestJS 10.x + TypeScript 5.6ESM,相对 import 带 `.js` 后缀
- Drizzle ORM 0.31MySQL`getDb()` 函数式懒加载
- neo4j-driver 5.x(知识图谱,仅读模型查询
- KafkaJS 2.2idempotent producer + transactionalId
- @grpc/grpc-js + @nestjs/microservicesgRPC server 50054
- @paralleldrive/cuid2(统一 ID 策略)
- Zod 3.xController 层全量 schema.parse
- pino日志+ prom-client指标+ OpenTelemetry追踪
## 开发
```bash
pnpm install
pnpm dev # 端口 3005
pnpm dev # HTTP 3005 + gRPC 50054
pnpm build
pnpm typecheck
pnpm lint
@@ -29,46 +33,150 @@ pnpm test
## 环境变量
| 变量 | 说明 |
| ----------------------------- | ------------------------------- |
| `PORT` | 服务端口(默认 3005 |
| `DATABASE_URL` | MySQL 连接串 |
| `NEO4J_URL` | Neo4j Bolt 连接 URL |
| `NEO4J_PASSWORD` | Neo4j 密码 |
| `ES_URL` | Elasticsearch 地址 |
| `JWT_SECRET` | JWT 密钥 |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry OTLP 端点(可选) |
| 变量 | 说明 |
| ----------------------------- | -------------------------------------------------- |
| `PORT` | HTTP 服务端口(默认 3005 |
| `GRPC_PORT` | gRPC 服务端口(默认 50054 |
| `DATABASE_URL` | MySQL 连接串(必填) |
| `KAFKA_BROKERS` | Kafka broker 列表(逗号分隔,默认 localhost:9092 |
| `NEO4J_URL` | Neo4j Bolt 连接 URL可选 |
| `NEO4J_PASSWORD` | Neo4j 密码(可选) |
| `REDIS_URL` | Redis 连接预留P5+ 缓存) |
| `ES_URL` | Elasticsearch 地址P5 引入) |
| `JWT_SECRET` | JWT 密钥Gateway 校验,服务可选) |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry OTLP 端点(可选) |
## 模块结构
```
src/
├─ config/ # envdatabase(MySQL)、neo4j
├─ config/ # env / database(getDb) / kafka / neo4j
├─ grpc/ # gRPC controllers4 Service, 22 RPC
│ ├─ grpc.module.ts
│ ├─ grpc-types.ts # 手动定义 proto message TS 类型
│ ├─ textbook.grpc.controller.ts
│ ├─ chapter.grpc.controller.ts
│ ├─ knowledge-graph.grpc.controller.ts
│ └─ question.grpc.controller.ts
├─ textbooks/ # 教材领域schema/repository/service/controller/dto
├─ chapters/ # 章节领域
├─ knowledge-points/ # 知识点领域(含前置依赖管理)
├─ questions/ # 题库领域
├─ shared/
│ ├─ errors/ # ApplicationError + GlobalErrorFilterCONTENT_* 前缀)
observability/# logger、metrics、tracer
├─ textbooks/ # 教材/章节/知识点 + 知识图谱
│ ├─ errors/ # ApplicationError + GlobalErrorFilterCONTENT_* 前缀 + ZodError 分支
health/ # /healthz + /readyzDB/Neo4j/Kafka 多依赖检查)
│ ├─ lifecycle/ # 优雅关闭
│ ├─ observability/ # logger / metrics / tracer
│ ├─ outbox/ # Outbox 模式schema/repository/publisher/service/module/events
│ └─ sync/ # Neo4j Sync Worker消费 Kafka 事件异步同步图谱)
├─ middleware/ # auth.middleware + permission.guard
├─ app.module.ts
└─ main.ts
└─ main.ts # HTTP + gRPC hybrid app
```
## 关键端点
## REST API 端点
- `POST /textbooks` 创建教材
- `GET /textbooks` 教材列表
- `GET /textbooks/:id` 教材详情
- `TextbooksService.createKnowledgeGraph` 在 Neo4j 构建知识点前置依赖
- `TextbooksService.getPrerequisites` 查询知识点前置链路
### Textbooks
| 方法 | 路径 | 说明 |
| -------- | ---------------- | ------------------------- |
| `POST` | `/textbooks` | 创建教材 |
| `GET` | `/textbooks` | 教材列表(支持分页/过滤) |
| `GET` | `/textbooks/:id` | 教材详情 |
| `PUT` | `/textbooks/:id` | 更新教材 |
| `DELETE` | `/textbooks/:id` | 删除教材 |
### Chapters
| 方法 | 路径 | 说明 |
| -------- | -------------------------------- | -------------- |
| `POST` | `/chapters` | 创建章节 |
| `GET` | `/chapters/textbook/:textbookId` | 按教材列出章节 |
| `GET` | `/chapters/:id` | 章节详情 |
| `PUT` | `/chapters/:id` | 更新章节 |
| `DELETE` | `/chapters/:id` | 删除章节 |
### Knowledge Points
| 方法 | 路径 | 说明 |
| -------- | ----------------------------------------------------- | ------------------------------ |
| `POST` | `/knowledge-points` | 创建知识点 |
| `GET` | `/knowledge-points/chapter/:chapterId` | 按章节列出知识点 |
| `GET` | `/knowledge-points/:id` | 知识点详情 |
| `GET` | `/knowledge-points/:id/prerequisites` | 查询前置依赖(读 Neo4j |
| `POST` | `/knowledge-points/:id/prerequisites` | 添加前置依赖(发 Outbox 事件) |
| `DELETE` | `/knowledge-points/:id/prerequisites/:prerequisiteId` | 移除前置依赖(发 Outbox 事件) |
| `PUT` | `/knowledge-points/:id` | 更新知识点 |
| `DELETE` | `/knowledge-points/:id` | 删除知识点 |
### Questions
| 方法 | 路径 | 说明 |
| -------- | ---------------------------------------------- | ------------------------- |
| `POST` | `/questions` | 创建题目 |
| `GET` | `/questions` | 题目列表(支持分页/过滤) |
| `GET` | `/questions/knowledge-point/:knowledgePointId` | 按知识点列出题目 |
| `GET` | `/questions/:id` | 题目详情 |
| `PUT` | `/questions/:id` | 更新题目 |
| `DELETE` | `/questions/:id` | 删除题目 |
## gRPC API端口 50054
Proto 定义:`packages/shared-proto/proto/content.proto`(包名 `next_edu_cloud.content.v1`
| Service | RPC 数 | 关键方法 |
| ----------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `TextbookService` | 5 | CreateTextbook / GetTextbook / ListTextbooks / UpdateTextbook / DeleteTextbook |
| `ChapterService` | 5 | CreateChapter / GetChapter / ListChapters / UpdateChapter / DeleteChapter |
| `KnowledgeGraphService` | 4 | GetPrerequisites / GetLearningPath / AddPrerequisite / RemovePrerequisite |
| `QuestionService` | 8 | CreateQuestion / BatchCreateQuestions / GetQuestion / ListQuestions / UpdateQuestion / DeleteQuestion / PublishQuestion / SearchQuestions |
**合计 22 RPC。**
## 事件驱动
### Outbox 模式
业务操作create/update/delete在同一事务内写业务表 + `content_outbox_events` 表。
独立 `OutboxPublisher` worker 轮询 pending 记录投递 Kafkaat-least-once最大重试 5 次,指数退避)。
### Kafka Topics4 个聚合 topicaction 字段区分事件类型)
| Topic | 事件类型action |
| ------------------------------------ | ------------------------------------------------------------- |
| `edu.content.textbook.events` | created / updated / published / archived |
| `edu.content.chapter.events` | created / updated / deleted |
| `edu.content.knowledge_point.events` | created / updated / prerequisite_added / prerequisite_removed |
| `edu.content.question.events` | created / updated / published / deleted |
### Neo4j Sync Worker
消费 `edu.content.knowledge_point.events` topic异步同步知识图谱
- `kp.created` / `kp.updated` → MERGE 节点
- `prerequisite_added` → MERGE 关系
- `prerequisite_removed` → DELETE 关系
Neo4j 不可用时非阻塞跳过(不影响 MySQL 写入和主流程)。
## 健康检查
| 端点 | 用途 | 鉴权 |
| -------------- | ------------------------------------------------- | ---- |
| `GET /healthz` | 存活探针liveness仅返回进程状态,不检查依赖 | 无 |
| `GET /readyz` | 就绪探针readiness检查 DB 连接,失败返回 503 | 无 |
| 端点 | 用途 | 鉴权 |
| -------------- | -------------------------------------------------------- | ---- |
| `GET /healthz` | 存活探针liveness仅返回进程状态 | 无 |
| `GET /readyz` | 就绪探针readiness检查 DB/Neo4j/Kafka,失败返回 503 | 无 |
实现见 `src/shared/health/health.controller.ts`5 个 NestJS 服务iam/core-edu/content/msg/classes一致。
## 质量校验
| 检查项 | 命令 | 状态 |
| -------- | ----------------------- | ----------------- |
| 类型检查 | `pnpm typecheck` | ✅ 0 错误 |
| Lint | `pnpm lint` | ✅ 0 错误 |
| 单元测试 | `pnpm test` | ✅ 182 tests 通过 |
| 覆盖率 | `vitest run --coverage` | ✅ 66.94%≥60% |
## 对外契约
gRPC 服务 `TextbookService``KnowledgeGraphService` 定义见 `packages/shared-proto/proto/content.proto`
- gRPC proto`packages/shared-proto/proto/content.proto`
- 事件 proto`packages/shared-proto/proto/events.proto`TextbookEvent / ChapterEvent / KnowledgePointEvent / QuestionEvent
- 契约文档:`docs/architecture/issues/contracts/content_contract.md`