Files
Edu/services/content
SpecialX 99580fa13a feat(content): docker 本地测试通过 + P5 ES 集成 + P6+ 审核工作流/可视化/可观测性
P5 ES 集成:
- config/elasticsearch.ts: 惰性初始化 + ik_max_word→standard 回退
- shared/sync/es-sync.worker.ts: Kafka 消费 question 事件并索引 ES
- questions search API: ES 优先, MySQL LIKE 降级
- ensureQuestionIndex() 幂等创建, IK 不可用回退 standard
- main.ts: 启动 ensureQuestionIndex + esSyncWorker 生命周期管理

P6+ 审核工作流/可视化/可观测性:
- Question 状态机: draft→pending_review→published→archived
- 非法转换拦截
- 知识图谱可视化 API: Neo4j 优先 + MySQL 降级
- Cypher 返回标量避免 Node 包装对象问题
- 教材版本管理: GET /textbooks/versions + archive
- 5 个 Prometheus 指标 + /readyz Outbox 积压检查

Docker 本地测试 (8 类全通过):
- healthz/readyz (5 依赖 ok)
- REST CRUD (textbook/chapter/kp/question)
- ES 全文检索命中
- 审核工作流状态机 (合法/非法转换)
- Outbox 事件驱动 (8 事件全 published)
- Neo4j 同步 (KnowledgePoint 节点创建)
- 可视化 (nodes/edges 正确)
- Prometheus 指标

docs/nextstep.md: 上游 (MySQL/Neo4j/Kafka/Redis/ES/ai)
+ 下游 (teacher-bff/student-bff/parent-bff/data-ana/api-gateway/ai)
2026-07-14 00:58:50 +08:00
..
2026-07-10 18:57:57 +08:00
2026-07-10 18:57:57 +08:00

content 内容资源服务

版本1.0P4 完整实现) 端口HTTP 3005 / gRPC 50054

职责

内容资源限界上下文,管理 Textbook、Chapter、KnowledgePoint、Question 四个聚合。 支持多存储MySQL写模型权威源+ Neo4j知识图谱派生读模型+ ElasticsearchP5 引入,题库全文检索)。 事件驱动Outbox 模式发布 4 个聚合 topicNeo4j Sync Worker 异步消费事件同步图谱。

技术栈

  • NestJS 10.x + TypeScript 5.6ESM相对 import 带 .js 后缀)
  • Drizzle ORM 0.31MySQLgetDb() 函数式懒加载)
  • 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追踪

开发

pnpm install
pnpm dev        # HTTP 3005 + gRPC 50054
pnpm build
pnpm typecheck
pnpm lint
pnpm test

环境变量

变量 说明
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/              # 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_* 前缀 + 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              # HTTP + gRPC hybrid app

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 记录投递 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/Neo4j/Kafka失败返回 503

质量校验

检查项 命令 状态
类型检查 pnpm typecheck 0 错误
Lint pnpm lint 0 错误
单元测试 pnpm test 182 tests 通过
覆盖率 vitest run --coverage 66.94%≥60%

对外契约

  • gRPC protopackages/shared-proto/proto/content.proto
  • 事件 protopackages/shared-proto/proto/events.protoTextbookEvent / ChapterEvent / KnowledgePointEvent / QuestionEvent
  • 契约文档:docs/architecture/issues/contracts/content_contract.md