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)
This commit is contained in:
SpecialX
2026-07-14 00:58:50 +08:00
parent 5b06bdbc52
commit 99580fa13a
29 changed files with 2486 additions and 26 deletions

View File

@@ -0,0 +1,286 @@
# content 下一步工作与上下游依赖
> 模块content内容域服务Textbook/Chapter/KnowledgeGraph/Question 四 Service端口 HTTP 3005 / gRPC 50054
> 负责人ai09
> 更新日期2026-07-13P4P6+ 全部完成Docker 本地测试通过)
> 关联文档:[02-architecture-design.md](./02-architecture-design.md)、[content_contract.md](../../../docs/architecture/issues/contracts/content_contract.md)、[content_workline.md](../../../docs/architecture/issues/worklines/content_workline.md)
---
## 1. 模块当前状态
content 模块已完成 P4P6+ 全部批次,提供 Textbook / Chapter / KnowledgeGraph / Question 四个 Service 共 22 个 RPC本地 Docker 镜像构建与运行验证通过(非 mock 数据ES 全文检索、Neo4j 知识图谱、审核工作流状态机、Outbox 事件驱动全部联调通过。
### 1.1 服务能力概览
| Service | RPC 数量 | 核心能力 |
| --------------------- | -------- | --------------------------------------------------------------- |
| TextbookService | — | 教材 CRUD、教材树管理、归档、版本查询 |
| ChapterService | — | 章节 CRUD、章节目录树 |
| KnowledgeGraphService | — | 知识点 CRUD、知识图谱Neo4j、学习路径、前置依赖、可视化 |
| QuestionService | — | 题目 CRUD、ES 全文检索、审核工作流状态机、AI 批量出题(待联调) |
| **合计** | **22** | 4 Service |
### 1.2 P5 完成项ES 集成)
| 验证项 | 状态 | 说明 |
| -------------------------------------- | ---- | -------------------------------------------------------------- |
| Elasticsearch 容器就绪 | ✅ | 本地 Docker edu-es 已就绪 |
| ES config 模块 | ✅ | `src/config/elasticsearch.ts`(惰性初始化 + ik→standard 回退) |
| ES Sync WorkerKafka 消费 → ES 索引) | ✅ | `src/shared/sync/es-sync.worker.ts`event_id 去重 + 软失败) |
| SearchQuestions API 接入 ES | ✅ | ES 优先MySQL LIKE 降级 |
| content_questions 索引映射 | ✅ | `ensureQuestionIndex()` 幂等创建ik 不可用时回退 standard |
| /readyz ES 索引存在性检查 | ✅ | degraded 而非 error |
### 1.3 P6+ 完成项
| 验证项 | 状态 | 说明 |
| ------------------------- | ---- | ------------------------------------------------------------------------------------------- |
| Question 审核工作流状态机 | ✅ | draft→pending_review→published/rejected→archived非法转换拦截 |
| 知识图谱可视化 API | ✅ | `GET /knowledge-graph/visualization`Neo4j 优先 + MySQL 降级 |
| 教材版本管理 | ✅ | `GET /textbooks/versions` + `POST /textbooks/:id/archive` |
| 可观测性硬化 | ✅ | 5 个 Prometheus 指标outbox_pending / neo4j_lag / es_lag / search_total / search_latency |
| /readyz Outbox 积压检查 | ✅ | pending > 100 → degraded |
### 1.4 本地 Docker 验证结果2026-07-13
测试环境:本地 Dockeredu-mysql + edu-neo4j + edu-es + edu-kafka + edu-redis + edu-content-test均接入 `edu-full_default` 网络)
```
镜像edu/content:test
容器edu-content-testDEV_MODE=true
测试 1健康检查
GET /healthz → {"status":"ok","service":"content"} ✅
GET /readyz → {"status":"ok","dependencies":[mysql:ok, neo4j:ok, kafka:ok, outbox:ok(0), elasticsearch:ok]} ✅
测试 2REST CRUD全部通过
POST /textbooks → 创建教材 ✅ (id=l9oo0iycnxlguqvhskjhrksa)
POST /chapters → 创建章节 ✅ (id=qdry24sm4ni2lrdf0fe7is2k)
POST /knowledge-points → 创建知识点 ✅ (id=tuuv90gloo7kf2x1al0b1b6g)
POST /questions → 创建题目 ✅ (id=ellr9gi1mnb9ir7ifxukhixs)
GET /questions/:id → 读取题目 ✅ (status=draft)
GET /questions?knowledgePointId=... → 列表查询 ✅ (count=1)
测试 3ES 全文检索
GET /questions/search?q=3 → total=1 ✅ (ES 索引命中)
GET /questions/search?q=苹果 → total=0 (standard 分析器中文按字切分IK 插件可用时改善)
测试 4审核工作流状态机
POST /questions/:id/submit-review → draft→pending_review ✅
POST /questions/:id/approve → pending_review→published ✅
POST /questions/:id/archive → published→archived ✅
POST /questions/:id/approve → 非法转换正确拒绝 ✅ (CONTENT_VALIDATION_ERROR)
测试 5Outbox 事件驱动
content_outbox_events 表 8 条事件,全部 status=published, retry_count=0 ✅
事件类型textbook.created, chapter.created, knowledge_point.created,
question.created, question.updated×2, question.published, textbook.archived
测试 6Neo4j 知识图谱同步
Neo4j KnowledgePoint 节点已创建 ✅ (id=tuuv90gloo7kf2x1al0b1b6g, difficulty=2)
GET /knowledge-graph/visualization → nodes=1, edges=0 ✅
测试 7教材归档
POST /textbooks/:id/archive → status=archived ✅
测试 8Prometheus 指标
GET /metrics → content_outbox_pending_total=0, content_neo4j_sync_lag_ms=0,
content_es_sync_lag_ms=0, content_question_search_total{source="es"} ✅
```
---
## 2. 上游依赖content 依赖谁)
### 2.1 MySQL基础设施— P0 必需
| 项 | 内容 |
| -------- | -------------------------------------------------------------------------------------------- |
| 端点 | `mysql://edu-mysql:3306/next_edu_cloud` |
| 用途 | 主数据存储textbooks / chapters / knowledge_points / questions / content_outbox_events 表) |
| 状态 | ✅ 本地 Docker edu-mysql 已就绪 |
| 环境变量 | `DATABASE_URL` |
### 2.2 Neo4j基础设施— P0 必需
| 项 | 内容 |
| -------- | ---------------------------------------------------------- |
| 端点 | `bolt://edu-neo4j:7687` |
| 用途 | 知识图谱存储KnowledgePoint 节点 + PREREQUISITE_OF 关系) |
| 状态 | ✅ 本地 Docker edu-neo4j 已就绪 |
| 环境变量 | `NEO4J_URL``NEO4J_PASSWORD` |
### 2.3 Kafka基础设施— P0 必需
| 项 | 内容 |
| -------- | ------------------------------------------------------------------------------- |
| 端点 | `kafka:29092` |
| 用途 | Outbox 事件发布4 个聚合 topic+ Neo4j Sync Worker 消费 + ES Sync Worker 消费 |
| 状态 | ✅ 本地 Docker edu-kafka 已就绪 |
| 环境变量 | `KAFKA_BROKERS` |
### 2.4 Redis基础设施— P1 可选
| 项 | 内容 |
| -------- | ---------------------------------------------------------------- |
| 端点 | `redis://edu-redis:6379` |
| 用途 | 缓存(教材树 / 知识点树读多写少场景env.ts 已预留 `REDIS_URL` |
| 状态 | ✅ 本地 Docker edu-redis 已就绪 |
| 环境变量 | `REDIS_URL` |
### 2.5 Elasticsearch基础设施— P5 必需
| 项 | 内容 |
| -------- | ------------------------------------------ |
| 端点 | `http://edu-es:9200` |
| 用途 | 题目全文检索content_questions 索引) |
| 状态 | ✅ 本地 Docker edu-es 已就绪profile p5 |
| 环境变量 | `ES_URL` |
### 2.6 ai 服务ai12 负责)— P5 联调
| # | 依赖项 | 用途 | 状态 |
| --- | ------------------------------ | -------------------------------------------------------- | ----------------- |
| 1 | gRPC `GenerateQuestion` :50058 | AI 批量出题QuestionService.BatchCreateQuestions 联调) | ⏳ 待 ai 服务就绪 |
**关键环境变量**
- `AI_GRPC_TARGET=ai:50058`(待联调阶段配置)
---
## 3. 下游依赖(谁依赖 content
### 3.1 teacher-bffai03 负责)— gRPC :50054
| # | 依赖项 | 用途 | 状态 |
| --- | --------------------------------------------------- | -------------------- | ---------------------- |
| 1 | gRPC `GetLearningPath(studentId, subjectId)` :50054 | `knowledgePath` 查询 | ✅ content gRPC 已就绪 |
| 2 | gRPC `ListTextbooks(subjectId, gradeId)` :50054 | 教材列表 | ✅ content gRPC 已就绪 |
| 3 | gRPC `ListChapters(textbookId)` :50054 | 章节目录 | ✅ content gRPC 已就绪 |
### 3.2 student-bffai04 负责)— gRPC :50054
| # | 依赖项 | 用途 | 状态 |
| --- | ---------------------------------- | ------------ | ----------------------------------------- |
| 1 | gRPC `ListTextbooks` :50054 | 学生浏览教材 | ✅ content gRPC 已就绪 |
| 2 | gRPC `ListChapters` :50054 | 学生浏览章节 | ✅ content gRPC 已就绪 |
| 3 | gRPC `GetLearningPath` :50054 | 学生学习路径 | ✅ content gRPC 已就绪 |
| 4 | gRPC `SearchQuestions` :50054 | 题目检索 | ✅ content ES 已就绪,待 student-bff 联调 |
| 5 | gRPC `BatchCreateQuestions` :50054 | AI 出题 | ⏳ 待 ai 服务就绪后联调 |
### 3.3 parent-bffai05 负责)— gRPC :50054
| # | 依赖项 | 用途 | 状态 |
| --- | ------------------------------ | -------------------- | ---------------------- |
| 1 | gRPC `ListTextbooks` :50054 | 家长查看孩子教材 | ✅ content gRPC 已就绪 |
| 2 | gRPC `ListChapters` :50054 | 家长查看孩子章节 | ✅ content gRPC 已就绪 |
| 3 | gRPC `GetPrerequisites` :50054 | 孩子学习路径前置依赖 | ✅ content gRPC 已就绪 |
| 4 | gRPC `GetLearningPath` :50054 | 孩子学习路径 | ✅ content gRPC 已就绪 |
### 3.4 data-anaai11 负责)— Kafka 消费
| # | 依赖项 | 用途 | 状态 |
| --- | ----------------------------------------------- | ------------------ | ----------------------- |
| 1 | 消费 topic `edu.content.knowledge_point.events` | 分析知识点难度分布 | ✅ content Kafka 已就绪 |
| 2 | 消费 topic `edu.content.question.events` | 题目来源统计 | ✅ content Kafka 已就绪 |
### 3.5 api-gatewayai01 负责)— HTTP REST :3005
| 能力 | 配置 | 状态 |
| ----------------------------------- | -------------------- | ------------------------- |
| `/api/v1/textbooks` 反向代理 | → content:3005 | ✅ api-gateway 已配置路由 |
| `/api/v1/chapters` 反向代理 | → content:3005 | ✅ api-gateway 已配置路由 |
| `/api/v1/knowledge-points` 反向代理 | → content:3005 | ✅ api-gateway 已配置路由 |
| `/api/v1/questions` 反向代理 | → content:3005 | ✅ api-gateway 已配置路由 |
| `GET /healthz` 端点 | /readyz 下游健康检查 | ✅ 已实现 |
### 3.6 ai 服务ai12 负责)— Kafka 消费
| # | 依赖项 | 用途 | 状态 |
| --- | ---------------------------------------- | ----------------------- | ----------------------- |
| 1 | 消费 topic `edu.content.question.events` | AI 出题时获取知识点信息 | ✅ content Kafka 已就绪 |
---
## 4. 环境变量清单
| 变量 | 必填 | 示例值 | 说明 |
| ----------------------------- | ---- | ---------------------------------------------------- | ------------------ |
| `PORT` | 是 | `3005` | HTTP 监听端口 |
| `GRPC_PORT` | 是 | `50054` | gRPC 监听端口 |
| `DATABASE_URL` | 是 | `mysql://edu:changeme@edu-mysql:3306/next_edu_cloud` | MySQL 连接 |
| `REDIS_URL` | 否 | `redis://edu-redis:6379` | Redis 缓存 |
| `NEO4J_URL` | 否 | `bolt://edu-neo4j:7687` | Neo4j 连接 |
| `NEO4J_PASSWORD` | 否 | `changeme` | Neo4j 密码 |
| `ES_URL` | 否 | `http://edu-es:9200` | Elasticsearch 连接 |
| `KAFKA_BROKERS` | 是 | `kafka:29092` | Kafka broker |
| `JWT_SECRET` | 否 | — | JWT 密钥 |
| `DEV_MODE` | 否 | `false` | 开发模式 |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | 否 | `http://otel-collector:4318` | OTLP 端点 |
| `LOG_LEVEL` | 否 | `info` | 日志级别 |
---
## 5. 剩余工作
| # | 工作项 | 阶段 | 状态 |
| --- | ---------------------------------------------- | ---- | ----------------- |
| 1 | ES 集成config + sync worker + search API | P5 | ✅ 完成 |
| 2 | Question 审核工作流状态机 | P6+ | ✅ 完成 |
| 3 | 知识图谱可视化 API | P6+ | ✅ 完成 |
| 4 | 教材版本管理 | P6+ | ✅ 完成 |
| 5 | 可观测性硬化Prometheus 指标 + readyz 检查) | P6+ | ✅ 完成 |
| 6 | AI 批量出题联调BatchCreateQuestions + ai12 | 联调 | ⏳ 待 ai 服务就绪 |
content 模块自身功能已全部完成,无剩余开发任务。唯一待办为 ai 服务ai12就绪后的 AI 出题联调。
---
## 6. Docker 本地测试
### 6.1 镜像构建
```bash
docker build -t edu/content:test -f services/content/Dockerfile .
```
### 6.2 容器启动
```bash
docker run -d \
--name edu-content-test \
--network edu-full_default \
-p 3005:3005 -p 50054:50054 \
-e DATABASE_URL=mysql://edu:changeme@edu-mysql:3306/next_edu_cloud \
-e NEO4J_URL=bolt://edu-neo4j:7687 \
-e NEO4J_PASSWORD=changeme \
-e ES_URL=http://edu-es:9200 \
-e KAFKA_BROKERS=kafka:29092 \
-e REDIS_URL=redis://edu-redis:6379 \
-e NODE_ENV=production \
-e DEV_MODE=true \
edu/content:test
```
---
## 7. 关键文件路径
| 文件 | 用途 |
| -------------------------------------------------------- | ----------------------------- |
| `packages/shared-proto/proto/content.proto` | gRPC 契约22 RPC |
| `packages/shared-proto/proto/events.proto` | 事件契约4 content message |
| `services/content/src/main.ts` | 服务入口 |
| `services/content/src/app.module.ts` | NestJS 模块注册 |
| `services/content/src/grpc/` | 4 个 gRPC controller |
| `services/content/src/shared/outbox/` | Outbox 模式实现 |
| `services/content/src/shared/sync/neo4j-sync.worker.ts` | Neo4j 异步同步 |
| `services/content/Dockerfile` | Docker 构建 |
| `docs/architecture/issues/contracts/content_contract.md` | 契约文档 |
| `docs/architecture/issues/worklines/content_workline.md` | 工作排期 |
---
**本文件由 ai09 维护,上游/下游工作项请各负责 AI 完成后通知 ai09 更新状态。**