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:
286
services/content/docs/nextstep.md
Normal file
286
services/content/docs/nextstep.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# content 下一步工作与上下游依赖
|
||||
|
||||
> 模块:content(内容域服务,Textbook/Chapter/KnowledgeGraph/Question 四 Service,端口 HTTP 3005 / gRPC 50054)
|
||||
> 负责人:ai09
|
||||
> 更新日期:2026-07-13(P4–P6+ 全部完成,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 模块已完成 P4–P6+ 全部批次,提供 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 Worker(Kafka 消费 → 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)
|
||||
|
||||
测试环境:本地 Docker(edu-mysql + edu-neo4j + edu-es + edu-kafka + edu-redis + edu-content-test,均接入 `edu-full_default` 网络)
|
||||
|
||||
```
|
||||
镜像:edu/content:test
|
||||
容器:edu-content-test(DEV_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]} ✅
|
||||
|
||||
测试 2:REST 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)
|
||||
|
||||
测试 3:ES 全文检索
|
||||
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)
|
||||
|
||||
测试 5:Outbox 事件驱动
|
||||
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
|
||||
|
||||
测试 6:Neo4j 知识图谱同步
|
||||
Neo4j KnowledgePoint 节点已创建 ✅ (id=tuuv90gloo7kf2x1al0b1b6g, difficulty=2)
|
||||
GET /knowledge-graph/visualization → nodes=1, edges=0 ✅
|
||||
|
||||
测试 7:教材归档
|
||||
POST /textbooks/:id/archive → status=archived ✅
|
||||
|
||||
测试 8:Prometheus 指标
|
||||
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-bff(ai03 负责)— 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-bff(ai04 负责)— 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-bff(ai05 负责)— 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-ana(ai11 负责)— Kafka 消费
|
||||
|
||||
| # | 依赖项 | 用途 | 状态 |
|
||||
| --- | ----------------------------------------------- | ------------------ | ----------------------- |
|
||||
| 1 | 消费 topic `edu.content.knowledge_point.events` | 分析知识点难度分布 | ✅ content Kafka 已就绪 |
|
||||
| 2 | 消费 topic `edu.content.question.events` | 题目来源统计 | ✅ content Kafka 已就绪 |
|
||||
|
||||
### 3.5 api-gateway(ai01 负责)— 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 更新状态。**
|
||||
Reference in New Issue
Block a user