content 内容资源服务
版本:1.0(P4 完整实现)
端口:HTTP 3005 / gRPC 50054
职责
内容资源限界上下文,管理 Textbook、Chapter、KnowledgePoint、Question 四个聚合。
支持多存储:MySQL(写模型权威源)+ Neo4j(知识图谱派生读模型)+ Elasticsearch(P5 引入,题库全文检索)。
事件驱动:Outbox 模式发布 4 个聚合 topic,Neo4j Sync Worker 异步消费事件同步图谱。
技术栈
- NestJS 10.x + TypeScript 5.6(ESM,相对 import 带
.js 后缀)
- Drizzle ORM 0.31(MySQL,
getDb() 函数式懒加载)
- neo4j-driver 5.x(知识图谱,仅读模型查询)
- KafkaJS 2.2(idempotent producer + transactionalId)
- @grpc/grpc-js + @nestjs/microservices(gRPC server 50054)
- @paralleldrive/cuid2(统一 ID 策略)
- Zod 3.x(Controller 层全量 schema.parse)
- pino(日志)+ prom-client(指标)+ OpenTelemetry(追踪)
开发
环境变量
| 变量 |
说明 |
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 端点(可选) |
模块结构
REST API 端点
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 记录投递 Kafka(at-least-once,最大重试 5 次,指数退避)。
Kafka Topics(4 个聚合 topic,action 字段区分事件类型)
| 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/Neo4j/Kafka,失败返回 503 |
无 |
质量校验
| 检查项 |
命令 |
状态 |
| 类型检查 |
pnpm typecheck |
✅ 0 错误 |
| Lint |
pnpm lint |
✅ 0 错误 |
| 单元测试 |
pnpm test |
✅ 182 tests 通过 |
| 覆盖率 |
vitest run --coverage |
✅ 66.94%(≥60%) |
对外契约
- 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