feat: auto committed
This commit is contained in:
@@ -1,53 +1,223 @@
|
||||
# content 对接契约
|
||||
|
||||
> 负责人:ai09
|
||||
> 关联:[matrix.md](./matrix.md)、[content.proto](../../../packages/shared-proto/proto/content.proto)、[events.proto](../../../packages/shared-proto/proto/events.proto)
|
||||
> 关联:[matrix.md](../matrix.md)、[content.proto](../../../packages/shared-proto/proto/content.proto)、[events.proto](../../../packages/shared-proto/proto/events.proto)、[../../services/content/docs/02-architecture-design.md](../../../services/content/docs/02-architecture-design.md)、[../objections/content_issue.md](../objections/content_issue.md)、[../worklines/content_workline.md](../worklines/content_workline.md)
|
||||
> 当前分支:`feat-review-content-module-docs-WAIyMA`
|
||||
> 契约策略:[coord-final-decisions.md §2 B2](../../coord-final-decisions.md) "首次实现即 gRPC 调用下游"——content 对外契约统一为 gRPC(REST 端点仅自身管理面用,不作为下游消费契约)
|
||||
|
||||
---
|
||||
|
||||
## §0 契约状态总览
|
||||
|
||||
| 维度 | 当前状态 | 目标状态(P4 完成) |
|
||||
| ---------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
||||
| gRPC | ❌ 未实现([content.proto](../../../packages/shared-proto/proto/content.proto) 仅 5 RPC 定义) | ✅ 21 RPC(4 Service) |
|
||||
| HTTP REST | ✅ 已实现 22 端点(4 Controller) | 🔁 保留作为管理面(不作为下游契约,下游统一 gRPC) |
|
||||
| Kafka 发布 | ❌ 未实现 | ✅ 4 聚合 topic(待 ISSUE-002 仲裁确认策略) |
|
||||
| Kafka 消费 | ❌ 未实现 | 🟢 可选(content 是上游,不主动消费 core-edu 事件,见 ISSUE-005) |
|
||||
|
||||
---
|
||||
|
||||
## §1 我提供什么(对外接口)
|
||||
|
||||
### 1.1 gRPC 接口(如有)
|
||||
### 1.1 gRPC 接口(目标态 · P4 完成)
|
||||
|
||||
| Service | RPC | 请求 | 响应 | 端口 |
|
||||
| --------------------- | ------------------ | ------------------------- | -------------------------- | ----- |
|
||||
| TextbookService | CreateTextbook | CreateTextbookRequest | Textbook | 50054 |
|
||||
| TextbookService | GetTextbook | GetTextbookRequest | Textbook | 50054 |
|
||||
| TextbookService | ListTextbooks | ListTextbooksRequest | ListTextbooksResponse | 50054 |
|
||||
| ChapterService | GetChapter | GetChapterRequest | Chapter | 50054 |
|
||||
| ChapterService | ListChapters | ListChaptersRequest | ListChaptersResponse | 50054 |
|
||||
| ChapterService | CreateChapter | CreateChapterRequest | Chapter | 50054 |
|
||||
| ChapterService | UpdateChapter | UpdateChapterRequest | Chapter | 50054 |
|
||||
| KnowledgeGraphService | GetPrerequisites | GetPrerequisitesRequest | KnowledgePointsResponse | 50054 |
|
||||
| KnowledgeGraphService | GetLearningPath | GetLearningPathRequest | LearningPath | 50054 |
|
||||
| KnowledgeGraphService | AddPrerequisite | AddPrerequisiteRequest | AddPrerequisiteResponse | 50054 |
|
||||
| KnowledgeGraphService | RemovePrerequisite | RemovePrerequisiteRequest | RemovePrerequisiteResponse | 50054 |
|
||||
| QuestionService | CreateQuestion | CreateQuestionRequest | Question | 50054 |
|
||||
| QuestionService | GetQuestion | GetQuestionRequest | Question | 50054 |
|
||||
| QuestionService | ListQuestions | ListQuestionsRequest | ListQuestionsResponse | 50054 |
|
||||
| QuestionService | UpdateQuestion | UpdateQuestionRequest | Question | 50054 |
|
||||
| QuestionService | DeleteQuestion | DeleteQuestionRequest | DeleteQuestionResponse | 50054 |
|
||||
| QuestionService | PublishQuestion | PublishQuestionRequest | PublishQuestionResponse | 50054 |
|
||||
| QuestionService | SearchQuestions | SearchQuestionsRequest | SearchQuestionsResponse | 50054 |
|
||||
> 依据 [coord-final-decisions.md §3.3](../../coord-final-decisions.md) N1/N3/N5 + [02-architecture-design.md §4.2](../../../services/content/docs/02-architecture-design.md)
|
||||
> 待 ISSUE-004 仲裁后定稿,当前按建议方案 21 RPC 列出
|
||||
|
||||
### 1.2 HTTP 端点(如有)
|
||||
| Service | RPC | 请求 | 响应 | 端口 | 阶段 |
|
||||
| --------------------- | -------------------- | ------------------------------------------------------------ | ----------------------------------------------------------- | ----- | ---- |
|
||||
| TextbookService | CreateTextbook | CreateTextbookRequest{title, subject_id, grade_id, version?} | Textbook | 50054 | P4 |
|
||||
| TextbookService | GetTextbook | GetTextbookRequest{id} | Textbook | 50054 | P4 |
|
||||
| TextbookService | ListTextbooks | ListTextbooksRequest{subject_id?, grade_id?, page_token, page_size} | ListTextbooksResponse{textbooks[], next_page_token} | 50054 | P4 |
|
||||
| TextbookService | UpdateTextbook | UpdateTextbookRequest{id, title?, status?, metadata?} | Textbook | 50054 | P4 |
|
||||
| TextbookService | DeleteTextbook | DeleteTextbookRequest{id} | Empty | 50054 | P4 |
|
||||
| ChapterService | CreateChapter | CreateChapterRequest{textbook_id, title, order, parent_id?} | Chapter | 50054 | P4 |
|
||||
| ChapterService | GetChapter | GetChapterRequest{id} | Chapter | 50054 | P4 |
|
||||
| ChapterService | ListChapters | ListChaptersRequest{textbook_id, parent_id?} | ListChaptersResponse{chapters[]} | 50054 | P4 |
|
||||
| ChapterService | UpdateChapter | UpdateChapterRequest{id, title?, order?, status?} | Chapter | 50054 | P4 |
|
||||
| ChapterService | DeleteChapter | DeleteChapterRequest{id} | Empty | 50054 | P4 |
|
||||
| KnowledgeGraphService | GetPrerequisites | GetPrerequisitesRequest{knowledge_point_id, depth?} | KnowledgePointsResponse{points[]} | 50054 | P4 |
|
||||
| KnowledgeGraphService | GetLearningPath | GetLearningPathRequest{student_id, subject_id} | LearningPath{points[], recommended_order[]} | 50054 | P4 |
|
||||
| KnowledgeGraphService | AddPrerequisite | AddPrerequisiteRequest{kp_id, prerequisite_id} | Empty | 50054 | P4 |
|
||||
| KnowledgeGraphService | RemovePrerequisite | RemovePrerequisiteRequest{kp_id, prerequisite_id} | Empty | 50054 | P4 |
|
||||
| QuestionService | CreateQuestion | CreateQuestionRequest{knowledge_point_id, type, content, options?, answer, explanation?, difficulty?, source?, created_by?} | Question | 50054 | P4 |
|
||||
| QuestionService | BatchCreateQuestions | BatchCreateQuestionsRequest{questions[]} | BatchCreateQuestionsResponse{ids[], failed[]} | 50054 | P4 |
|
||||
| QuestionService | GetQuestion | GetQuestionRequest{id} | Question | 50054 | P4 |
|
||||
| QuestionService | ListQuestions | ListQuestionsRequest{knowledge_point_id?, type?, difficulty?, status?, page_token, page_size} | ListQuestionsResponse{questions[], next_page_token} | 50054 | P4 |
|
||||
| QuestionService | UpdateQuestion | UpdateQuestionRequest{id, content?, answer?, status?} | Question | 50054 | P4 |
|
||||
| QuestionService | DeleteQuestion | DeleteQuestionRequest{id} | Empty | 50054 | P4 |
|
||||
| QuestionService | PublishQuestion | PublishQuestionRequest{id} | Empty | 50054 | P4 |
|
||||
| QuestionService | SearchQuestions | SearchQuestionsRequest{q?, type?, difficulty?, knowledge_point_id?, page_token, page_size} | SearchQuestionsResponse{questions[], total, next_page_token} | 50054 | P5 |
|
||||
|
||||
无对外 HTTP 端点,仅 gRPC。
|
||||
**RPC 总数**:21(TextbookService 5 + ChapterService 5 + KnowledgeGraphService 4 + QuestionService 7)
|
||||
|
||||
### 1.3 GraphQL schema(如 BFF)
|
||||
> ⚠️ **matrix.md §2 同步项**:当前 matrix.md §2 登记 content 为 18 RPC,待 ISSUE-004 仲裁后需更新为 21 RPC(差额:ChapterService 补 Update + Delete = +2;TextbookService 补 Update + Delete = +2;QuestionService 原 contract 已含 Publish/Search,design doc 缺,对齐后 +0;原合计 18 + 4 - 1 = 21)
|
||||
|
||||
不适用。
|
||||
#### proto message 字段说明(关键字段)
|
||||
|
||||
### 1.4 Kafka 事件发布(如有)
|
||||
```protobuf
|
||||
message Textbook {
|
||||
string id = 1;
|
||||
string title = 2;
|
||||
string subject_id = 3;
|
||||
string grade_id = 4;
|
||||
string version = 5;
|
||||
string status = 6; // draft/pending_review/published/archived
|
||||
string tenant_id = 7; // 多租户预留
|
||||
google.protobuf.Struct metadata = 8; // 扩展字段
|
||||
int64 created_at = 9;
|
||||
int64 updated_at = 10;
|
||||
}
|
||||
|
||||
| Topic | Event | 消费方 |
|
||||
| ---------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
||||
| edu.content.knowledge_point.events | KnowledgePointEvent(action: created/updated/prerequisite_added/prerequisite_removed) | data-ana / ai / Neo4j Sync Worker / ES Sync Worker |
|
||||
| edu.content.question.events | QuestionEvent(action: created/updated/published/deleted) | data-ana |
|
||||
message Chapter {
|
||||
string id = 1;
|
||||
string textbook_id = 2;
|
||||
string title = 3;
|
||||
int32 order = 4; // DB 列名 order_num,proto 字段名 order
|
||||
string parent_id = 5; // 树形结构
|
||||
string status = 6;
|
||||
int64 created_at = 7;
|
||||
int64 updated_at = 8;
|
||||
}
|
||||
|
||||
message KnowledgePoint {
|
||||
string id = 1;
|
||||
string chapter_id = 2;
|
||||
string title = 3;
|
||||
string description = 4;
|
||||
int32 difficulty = 5; // 1-5
|
||||
google.protobuf.Struct metadata = 6;
|
||||
int64 created_at = 7;
|
||||
int64 updated_at = 8;
|
||||
}
|
||||
|
||||
message Question {
|
||||
string id = 1;
|
||||
string knowledge_point_id = 2;
|
||||
string type = 3; // single_choice/multiple_choice/short_answer/essay
|
||||
string content = 4; // 题干(HTML/markdown)
|
||||
google.protobuf.Struct options = 5; // 选项(选择题)
|
||||
string answer = 6;
|
||||
string explanation = 7;
|
||||
int32 difficulty = 8; // 1-5
|
||||
string status = 9; // draft/pending_review/published/rejected/archived
|
||||
string source = 10; // manual/ai_generated/imported
|
||||
string created_by = 11;
|
||||
google.protobuf.Struct metadata = 12;
|
||||
int64 created_at = 13;
|
||||
int64 updated_at = 14;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 HTTP 端点(管理面 · 非下游契约)
|
||||
|
||||
> ⚠️ 以下 REST 端点仅供 content 自身管理面/直接 Gateway 访问用,**下游服务(teacher-bff/student-bff/ai)统一走 gRPC**,不消费以下 REST 端点。
|
||||
|
||||
当前已实现 22 端点(4 Controller),详见 [02-architecture-design.md §4.1](../../../services/content/docs/02-architecture-design.md)。P4 重构后将统一加 `/v1/` 版本前缀(见 ISSUE-008)。
|
||||
|
||||
### 1.3 GraphQL schema
|
||||
|
||||
不适用。content 是 gRPC 服务,不暴露 GraphQL。
|
||||
|
||||
### 1.4 Kafka 事件发布(目标态 · P4 完成)
|
||||
|
||||
> 待 ISSUE-002 仲裁确认 topic 命名策略,当前按**聚合 topic + action 字段**策略列出(与 matrix.md §4 / events.proto ClassEvent 模式一致)
|
||||
|
||||
| Topic | Event message | action 字段值 | 消费方 | 阶段 |
|
||||
| ------------------------------------ | -------------------- | ---------------------------------------------------------- | -------------------------------------------------- | ---- |
|
||||
| edu.content.textbook.events | TextbookEvent | created / updated / published / archived | data-ana / msg(通知教师教材发布) | P4 |
|
||||
| edu.content.chapter.events | ChapterEvent | created / updated / deleted | data-ana | P4 |
|
||||
| edu.content.knowledge_point.events | KnowledgePointEvent | created / updated / prerequisite_added / prerequisite_removed | data-ana / ai / Neo4j Sync Worker / ES Sync Worker | P4 |
|
||||
| edu.content.question.events | QuestionEvent | created / updated / published / deleted | data-ana / ai / ES Sync Worker | P4 |
|
||||
|
||||
> ⚠️ **ISSUE-003 待仲裁**:当前 matrix.md §4 仅登记 kp + question 两类 topic,Textbook/Chapter 事件未登记。本契约按 design doc §5.1 补全 4 类,待 coord 仲裁后同步 matrix.md。
|
||||
|
||||
#### 事件 payload schema(待补 events.proto)
|
||||
|
||||
需在 [events.proto](../../../packages/shared-proto/proto/events.proto) 追加 4 个 message:
|
||||
|
||||
```protobuf
|
||||
message TextbookEvent {
|
||||
string event_id = 1;
|
||||
string aggregate_id = 2;
|
||||
string event_type = 3; // edu.content.textbook.created 等
|
||||
int64 occurred_at = 4;
|
||||
string textbook_id = 5;
|
||||
string title = 6;
|
||||
string subject_id = 7;
|
||||
string grade_id = 8;
|
||||
string version = 9;
|
||||
string action = 10; // created/updated/published/archived
|
||||
map<string, string> metadata = 11;
|
||||
}
|
||||
|
||||
message ChapterEvent {
|
||||
string event_id = 1;
|
||||
string aggregate_id = 2;
|
||||
string event_type = 3;
|
||||
int64 occurred_at = 4;
|
||||
string chapter_id = 5;
|
||||
string textbook_id = 6;
|
||||
string title = 7;
|
||||
int32 order = 8;
|
||||
string action = 9; // created/updated/deleted
|
||||
map<string, string> metadata = 10;
|
||||
}
|
||||
|
||||
message KnowledgePointEvent {
|
||||
string event_id = 1;
|
||||
string aggregate_id = 2;
|
||||
string event_type = 3;
|
||||
int64 occurred_at = 4;
|
||||
string kp_id = 5;
|
||||
string chapter_id = 6;
|
||||
string title = 7;
|
||||
int32 difficulty = 8;
|
||||
string action = 9; // created/updated/prerequisite_added/prerequisite_removed
|
||||
string prerequisite_id = 10; // 仅 prerequisite_* 有值
|
||||
map<string, string> metadata = 11;
|
||||
}
|
||||
|
||||
message QuestionEvent {
|
||||
string event_id = 1;
|
||||
string aggregate_id = 2;
|
||||
string event_type = 3;
|
||||
int64 occurred_at = 4;
|
||||
string question_id = 5;
|
||||
string kp_id = 6;
|
||||
string type = 7; // single_choice 等
|
||||
int32 difficulty = 8;
|
||||
string status = 9;
|
||||
string source = 10; // manual/ai_generated/imported
|
||||
string created_by = 11;
|
||||
string action = 12; // created/updated/published/deleted
|
||||
map<string, string> metadata = 13;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 错误码前缀
|
||||
|
||||
`CONTENT_`(如 CONTENT_TEXTBOOK_NOT_FOUND、CONTENT_QUESTION_DUPLICATE)
|
||||
`CONTENT_`(详见 [02-architecture-design.md §6.2](../../../services/content/docs/02-architecture-design.md))
|
||||
|
||||
| 错误码 | HTTP | gRPC status | 触发条件 |
|
||||
| ------------------------- | ---- | ------------------ | ---------------------------------- |
|
||||
| CONTENT_VALIDATION_ERROR | 400 | INVALID_ARGUMENT | Zod 校验失败 / 题型非法 / 难度越界 |
|
||||
| CONTENT_NOT_FOUND | 404 | NOT_FOUND | 资源不存在 |
|
||||
| CONTENT_PERMISSION_DENIED | 403 | PERMISSION_DENIED | 权限不足 |
|
||||
| CONTENT_CONFLICT | 409 | ALREADY_EXISTS | 唯一约束冲突 / 状态机非法转换 |
|
||||
| CONTENT_BUSINESS_ERROR | 422 | FAILED_PRECONDITION | 业务规则违反(如循环依赖检测) |
|
||||
| CONTENT_DATABASE_ERROR | 500 | INTERNAL | Drizzle 操作异常 |
|
||||
| CONTENT_INTERNAL_ERROR | 500 | INTERNAL | 未知异常 |
|
||||
| CONTENT_NEO4J_UNAVAILABLE | 503 | UNAVAILABLE | Neo4j 不可用且无降级路径 |
|
||||
|
||||
### 1.6 健康检查端点
|
||||
|
||||
| 端点 | 用途 | 鉴权 | 响应 |
|
||||
| ----------- | ------------------------------------------------------- | ---- | ------------------------------------------------------------------------------- |
|
||||
| GET /healthz | 存活探针(liveness),仅返回进程状态 | 无 | `{ "status": "ok", "service": "content", "timestamp": "..." }` |
|
||||
| GET /readyz | 就绪探针(readiness),检查 DB/Neo4j/Kafka 三依赖 | 无 | `{ "status": "ok|degraded|down", "checks": { database, neo4j, kafka_producer, kafka_consumer } }` |
|
||||
| GET /metrics | Prometheus 指标 | 无 | Prometheus exposition format |
|
||||
|
||||
---
|
||||
|
||||
@@ -55,18 +225,30 @@
|
||||
|
||||
### 2.1 gRPC 调用(同步)
|
||||
|
||||
无直接 gRPC 调用上游。content 通过 Kafka 事件接收 core-edu 班级/学生变更用于数据一致性。
|
||||
**无**。content 是内容资源上游提供方,不主动调用其他业务服务 gRPC。
|
||||
|
||||
> content 与 core-edu 的关系澄清(见 ISSUE-005):content 是 core-edu 的上游(core-edu 调 content 查知识点),不是下游。content 不消费 core-edu 事件。
|
||||
|
||||
### 2.2 Kafka 事件订阅(异步)
|
||||
|
||||
| Topic | Event | 发布方 | mock 策略 |
|
||||
| ---------------- | --------------------------------- | --------------- | ----------------------------------------------------- |
|
||||
| edu.class.events | ClassEvent(action: transferred) | core-edu (ai08) | core-edu 就绪前不订阅,content 内部不依赖班级实时数据 |
|
||||
| edu.exam.events | ExamEvent | core-edu (ai08) | core-edu 就绪前忽略,题目关联知识点不依赖考试事件 |
|
||||
**P4 不订阅任何事件**(按 ISSUE-005 建议删除原 `edu.teaching.content.invalidated` 条目)。
|
||||
|
||||
### 2.3 HTTP 调用(如有)
|
||||
若 P6+ 有教材/章节联动需求,由 core-edu 主动调用 content gRPC UpdateQuestion/UpdateTextbook 状态变更,而非事件驱动。
|
||||
|
||||
无。
|
||||
### 2.3 HTTP 调用
|
||||
|
||||
**无**。
|
||||
|
||||
### 2.4 基础设施依赖
|
||||
|
||||
| 依赖 | 用途 | 配置项(env.ts) | 必需性 |
|
||||
| --------------- | ----------------------------- | ----------------------------- | ---------------------- |
|
||||
| MySQL 8 | 写模型主库(4 张业务表 + outbox) | DATABASE_URL | 🔴 必需 |
|
||||
| Neo4j 5 | 知识图谱(PREREQUISITE_OF) | NEO4J_URL / NEO4J_PASSWORD | 🔴 必需(图谱查询核心) |
|
||||
| Kafka | Outbox 事件发布 | KAFKA_BROKERS(待补 env.ts) | 🔴 必需(Outbox 强制) |
|
||||
| Redis | 缓存(教材树/章节树,P5+) | REDIS_URL(已预留) | 🟢 P5+ 可选 |
|
||||
| Elasticsearch 8 | 题库全文检索(P5+) | ES_URL(已预留) | 🟢 P5+ 必需 |
|
||||
| OTLP Collector | 链路追踪 | OTEL_EXPORTER_OTLP_ENDPOINT | 🟢 可选(未配置时降级) |
|
||||
|
||||
---
|
||||
|
||||
@@ -74,37 +256,113 @@
|
||||
|
||||
### 3.1 我依赖的上游就绪标志
|
||||
|
||||
- [ ] core-edu gRPC 50053 启用(ai08)—— 用于知识点与班级关联(可选,content 可先独立运行)
|
||||
- [ ] edu.class.events / edu.exam.events topic 有事件发布(ai08)
|
||||
| 上游 | 就绪标志 | 必需性 | mock 策略 |
|
||||
| -------------- | ----------------------------------------- | ---------------------- | ----------------------------------------------- |
|
||||
| infra | MySQL 8 可用 | 🔴 必需 | 本地 docker-compose |
|
||||
| infra | Neo4j 5 可用 | 🔴 必需 | 本地 docker-compose;未配置时图谱查询返回 503 |
|
||||
| infra | Kafka 集群可用 | 🔴 必需 | 本地 docker-compose |
|
||||
| shared-proto | content.proto / events.proto 补全 | 🔴 必需(P4.6 自身完成) | ai09 自行修改 proto |
|
||||
| core-edu (ai08) | gRPC 50053 启用 | 🟢 可选(content 独立) | 不依赖 core-edu 实时数据 |
|
||||
| ai (ai12) | gRPC 50058 启用 | 🟢 P5 联调时必需 | grpc-mock 拦截 |
|
||||
|
||||
### 3.2 我的就绪标志(供下游消费)
|
||||
|
||||
#### P4 就绪(核心交付)
|
||||
|
||||
- [ ] content gRPC 50054 启用(HealthService.Check 返回 SERVING)
|
||||
- [ ] TextbookService 3 RPC 可调用
|
||||
- [ ] ChapterService 4 RPC 可调用
|
||||
- [ ] TextbookService 5 RPC 可调用(Create/Get/List/Update/Delete)
|
||||
- [ ] ChapterService 5 RPC 可调用(Create/Get/List/Update/Delete)
|
||||
- [ ] KnowledgeGraphService 4 RPC 可调用(GetPrerequisites/GetLearningPath/AddPrerequisite/RemovePrerequisite)
|
||||
- [ ] QuestionService 7 RPC 可调用(含 SearchQuestions 全文检索)
|
||||
- [ ] edu.content.knowledge_point.events / edu.content.question.events topic 可发布
|
||||
- [ ] QuestionService 7 RPC 可调用(Create/BatchCreate/Get/List/Update/Delete/Publish/Search)
|
||||
- [ ] edu.content.textbook.events / chapter.events / knowledge_point.events / question.events 4 topic 可发布
|
||||
- [ ] /readyz 返回 DB/Neo4j/Kafka 三依赖状态
|
||||
- [ ] Outbox Publisher worker 运行中(content_outbox_events 表 PENDING 事件 < 100)
|
||||
- [ ] Neo4j Sync Worker 运行中(知识点节点最终一致延迟 < 2s)
|
||||
|
||||
#### P5 就绪(检索 + AI 集成)
|
||||
|
||||
- [ ] GET /questions/search 检索 API 可用(延迟 < 200ms)
|
||||
- [ ] QuestionService.SearchQuestions gRPC 可调用
|
||||
- [ ] QuestionService.BatchCreateQuestions 与 ai12 联调通过
|
||||
- [ ] ES Sync Worker 运行中(题目索引最终一致延迟 < 2s)
|
||||
- [ ] 测试覆盖率 ≥ 80%
|
||||
|
||||
#### P6+ 就绪(演进)
|
||||
|
||||
- [ ] Question 审核工作流状态机完整
|
||||
- [ ] 知识图谱可视化 API 可用
|
||||
- [ ] 教材版本管理启用
|
||||
|
||||
---
|
||||
|
||||
## §4 Mock 策略
|
||||
|
||||
### 4.1 我提供的 mock
|
||||
### 4.1 我提供的 mock(供下游消费)
|
||||
|
||||
在 content 真实服务就绪前,为下游(teacher-bff / student-bff / ai / data-ana)提供以下 mock:
|
||||
|
||||
- **gRPC mock**:使用 grpc-mock 拦截 50054 端口
|
||||
- TextbookService.ListTextbooks 返回固定 5 个 Textbook(语数英理化)
|
||||
- ChapterService.ListChapters 返回固定章节树(每教材 10 章)
|
||||
- KnowledgeGraphService.GetLearningPath 返回固定 8 个 KnowledgePoint 推荐顺序
|
||||
- KnowledgeGraphService.GetPrerequisites 返回固定 3 个前置知识点
|
||||
- QuestionService.SearchQuestions 返回固定 20 个 Question(含 options)
|
||||
- **Kafka mock**:content 就绪前不发布真实事件,下游使用本地 stub
|
||||
#### gRPC mock(grpc-mock 拦截 50054 端口)
|
||||
|
||||
| Service | RPC | Mock 返回 |
|
||||
| --------------------- | --------------------- | ---------------------------------------------------------- |
|
||||
| TextbookService | ListTextbooks | 固定 5 个 Textbook(语数英理化,subject_id=math/chn/eng/phy/chem) |
|
||||
| TextbookService | GetTextbook | 返回第一个 Textbook |
|
||||
| ChapterService | ListChapters | 固定章节树(每教材 10 章,含 parent_id 树形结构) |
|
||||
| KnowledgeGraphService | GetLearningPath | 固定 8 个 KnowledgePoint 推荐顺序(按 difficulty 升序) |
|
||||
| KnowledgeGraphService | GetPrerequisites | 固定 3 个前置知识点 |
|
||||
| QuestionService | ListQuestions | 固定 20 个 Question(含 4 种题型各 5 个) |
|
||||
| QuestionService | SearchQuestions | 固定 20 个 Question(含 options) |
|
||||
| QuestionService | BatchCreateQuestions | 返回成功 + 生成 20 个 cuid2 ID |
|
||||
| QuestionService | CreateQuestion | 返回成功 + 生成 1 个 cuid2 ID |
|
||||
|
||||
#### Kafka mock
|
||||
|
||||
content 就绪前不发布真实事件,下游使用本地 stub(data-ana / ai 各自维护测试数据集)。
|
||||
|
||||
### 4.2 我消费的 mock
|
||||
|
||||
在真实 core-edu 就绪前,content 使用以下 mock:
|
||||
content P4 不消费任何上游事件,无需 mock 上游。
|
||||
|
||||
- 班级/学生数据:不依赖 core-edu 实时数据,知识点关联使用固定 subject_id/grade
|
||||
- 事件订阅:不订阅 edu.class.events / edu.exam.events,内部数据自洽
|
||||
P5 联调阶段消费 ai (ai12) 的 gRPC,使用 grpc-mock 拦截 50058 端口:
|
||||
- AiService.GenerateQuestion 返回固定 1 个 Question(source=ai_generated)
|
||||
- AiService.Chat 返回固定文本响应
|
||||
|
||||
---
|
||||
|
||||
## §5 契约一致性核查(2026-07-10)
|
||||
|
||||
> 本节记录 contract.md 与 design doc / matrix.md / proto 的对齐情况
|
||||
|
||||
### 5.1 与 02-architecture-design.md 对齐
|
||||
|
||||
| 维度 | design doc | contract.md(本文件) | 一致性 | 备注 |
|
||||
| ------------- | -------------------------------- | ------------------------------ | ------ | ------------------------------------------ |
|
||||
| gRPC RPC 数 | §4.2 列 18 RPC | §1.1 列 21 RPC | ⚠️ | 待 ISSUE-004 仲裁;建议以 21 RPC 为准 |
|
||||
| 事件 topic | §5.1 列 12 独立 topic | §1.4 列 4 聚合 topic | ⚠️ | 待 ISSUE-002 仲裁;建议以 4 聚合 topic 为准 |
|
||||
| 错误码 | §6.2 列 8 个 | §1.5 列 8 个 | ✅ | 一致 |
|
||||
| 健康检查 | §6.6 /readyz 多依赖 | §1.6 三依赖 | ✅ | 一致 |
|
||||
|
||||
### 5.2 与 matrix.md 对齐
|
||||
|
||||
| 维度 | matrix.md | contract.md(本文件) | 一致性 | 备注 |
|
||||
| ------------- | -------------------------------- | ------------------------------ | ------ | ------------------------------------------ |
|
||||
| gRPC RPC 总数 | §2 列 18 RPC | §1.1 列 21 RPC | ⚠️ | 待 ISSUE-004 仲裁后同步 matrix.md |
|
||||
| Kafka topic | §4 列 2 topic(kp + question) | §1.4 列 4 topic | ⚠️ | 待 ISSUE-003 仲裁后同步 matrix.md |
|
||||
| 错误码前缀 | §6 列 CONTENT_ | §1.5 列 CONTENT_ | ✅ | 一致 |
|
||||
| 端口 | §2 列 50054 | §1.1 列 50054 | ✅ | 一致 |
|
||||
|
||||
### 5.3 与 proto 文件对齐
|
||||
|
||||
| 文件 | 当前状态 | 目标状态(P4 完成) |
|
||||
| ------------- | ---------------------------------------------------------------- | ------------------------------------------------ |
|
||||
| content.proto | 5 RPC(TextbookService 3 + KnowledgeGraphService 2),无 Chapter/Question | 21 RPC(4 Service 完整) |
|
||||
| events.proto | 4 message(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent) | 8 message(+ TextbookEvent/ChapterEvent/KnowledgePointEvent/QuestionEvent) |
|
||||
|
||||
---
|
||||
|
||||
## §6 变更记录
|
||||
|
||||
| 日期 | 版本 | 变更内容 | 变更人 |
|
||||
| ---------- | ---- | ---------------------------------------------------------------------------------------------- | ------ |
|
||||
| 2026-07-09 | v1.0 | 初版(18 RPC,2 topic) | coord |
|
||||
| 2026-07-10 | v1.1 | ai09 复审:补全至 21 RPC(+ TextbookService Update/Delete + ChapterService Update/Delete + QuestionService Publish/Search);补全至 4 topic(+ Textbook/Chapter 事件);补 events.proto 4 message 草案;补 §0 状态总览 / §5 一致性核查 / §6 变更记录;明确 REST 端点为管理面(非下游契约) | ai09 |
|
||||
|
||||
@@ -1,24 +1,187 @@
|
||||
# content 问题记录
|
||||
|
||||
> 负责人:ai09
|
||||
> 关联:[coord.md](../coord.md)、[contracts/content_contract.md](../contracts/content_contract.md)
|
||||
> 关联:[coord.md](../coord.md)、[contracts/content_contract.md](../contracts/content_contract.md)、[../../services/content/docs/01-understanding.md](../../../services/content/docs/01-understanding.md)、[../../services/content/docs/02-architecture-design.md](../../../services/content/docs/02-architecture-design.md)
|
||||
> 规则:AI 遇到问题时在此追加条目,coord 仲裁后更新状态
|
||||
|
||||
---
|
||||
|
||||
## 问题列表
|
||||
## §0 已有仲裁核查(2026-07-10 复核)
|
||||
|
||||
<!--
|
||||
追加条目格式:
|
||||
> 复核依据:[coord-final-decisions.md §3.3](../../coord-final-decisions.md) N1-N5、[01-understanding.md §A](../../../services/content/docs/01-understanding.md) ai09 复核记录
|
||||
|
||||
### ISSUE-[编号]-[AI标识]:[标题]
|
||||
### 0.1 coord-final-decisions.md N1-N5 核查
|
||||
|
||||
- **提请方**:aiXX
|
||||
- **日期**:YYYY-MM-DD
|
||||
- **类型**:契约不明确 / 工作量超批 / 前置依赖缺失 / 编号冲突 / 其他
|
||||
- **描述**:[详细描述问题]
|
||||
- **建议方案**:[AI 的建议]
|
||||
- **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X)
|
||||
-->
|
||||
| 编号 | 仲裁结论 | 02-architecture-design.md 落实位置 | 核查结果 |
|
||||
| ---- | ----------------------------------------------------------------------- | ------------------------------------------------ | -------- |
|
||||
| N1 | P4 首次实现即启用 gRPC server 50054 | §1.2 入口 HTTP 3005 / gRPC 50054;§4.2 gRPC API | ✅ 已落实 |
|
||||
| N2 | 首次实现即检查 DB/Neo4j/Kafka | §6.6 /readyz 多依赖检查 | ✅ 已落实 |
|
||||
| N3 | P4 即补全 QuestionService proto(不等到 P5) | §4.2.4 QuestionService 6 RPC | ✅ 已落实 |
|
||||
| N4 | 首次实现即对齐 ActionState | §4.3 错误响应结构 success/error 信封 | ✅ 已落实 |
|
||||
| N5 | P4 首次实现即补全 ChapterService | §4.2.2 ChapterService 3 RPC | ✅ 已落实 |
|
||||
|
||||
(暂无问题)
|
||||
### 0.2 01-understanding.md §A 已裁决项核查
|
||||
|
||||
| 原编号 | 仲裁结论 | 02-architecture-design.md 落实位置 | 核查结果 |
|
||||
| ------ | --------------------------------------------------------------------- | ----------------------------------------- | -------- |
|
||||
| C2 | P4 必须引入 Outbox(004 §12.2 强制条款) | §3.1.5 content_outbox_events 表 + §5.4 Outbox Publisher | ✅ 已落实 |
|
||||
| C4 | P4 必须实现 gRPC controller | §4.2 gRPC API(4 个 Service) | ✅ 已落实 |
|
||||
| C10 | proto 包名保持 `next_edu_cloud.content.v1` | —(保持现状) | ✅ 已落实 |
|
||||
|
||||
### 0.3 核查结论
|
||||
|
||||
N1-N5 与 C2/C4/C10 共 8 项已有仲裁**全部在 02-architecture-design.md 中正确落实**,无遗漏、无偏离。
|
||||
|
||||
---
|
||||
|
||||
## §1 新提请异议(2026-07-10 ai09 复审)
|
||||
|
||||
### ISSUE-001-ai09:REST 端点设计文档与现有实现不一致
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约不明确
|
||||
- **描述**:02-architecture-design.md §4.1 列出的 REST API 与现有源码实现存在三处偏差:
|
||||
1. **knowledge-points 列表**:设计文档为 `GET /knowledge-points?chapterId=`(query 参数),源码 [knowledge-points.controller.ts](../../../services/content/src/knowledge-points/knowledge-points.controller.ts) 实现为 `GET /knowledge-points/chapter/:chapterId`(path 参数)
|
||||
2. **knowledge-points 删除前置**:设计文档列 `DELETE /knowledge-points/:id/prerequisites/:prereqId`,源码未实现该端点
|
||||
3. **chapters 列表**:设计文档为 `GET /chapters?textbookId=`,源码实现为 `GET /chapters/textbook/:textbookId`
|
||||
- **建议方案**:以设计文档为目标态(query 参数 + 补 DELETE prerequisite 端点),在 P4 重构时统一对齐。但需 coord 确认是否允许 API 路径变更(影响 teacher-bff 消费方)。
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-002-ai09:content 发布事件 topic 命名策略与契约文档不一致
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约不明确
|
||||
- **描述**:事件 topic 命名存在两种策略冲突:
|
||||
- **02-architecture-design.md §5.1 + §5.3 TOPIC_MAP**:每个事件类型独立 topic(`edu.content.textbook.created` / `edu.content.question.published` 等共 12 个 topic)
|
||||
- **contracts/content_contract.md §1.4 + matrix.md §4**:按聚合根聚合 topic(`edu.content.knowledge_point.events` / `edu.content.question.events` 共 2 个 topic,事件类型用 `action` 字段区分)
|
||||
- **events.proto**:未定义 KnowledgePointEvent / QuestionEvent message(仅 ClassEvent/ExamEvent/HomeworkEvent/GradeEvent)
|
||||
- **建议方案**:采用**聚合 topic + action 字段**策略(与契约文档、matrix.md、events.proto ClassEvent 模式一致),原因:
|
||||
1. 与 core-edu 既有模式(edu.exam.events / edu.homework.events 等)一致
|
||||
2. 减少 topic 数量(12 → 4),降低 Kafka 集群元数据压力
|
||||
3. 消费方按 action 字段过滤,订阅灵活性更高
|
||||
4. 需补 `KnowledgePointEvent` / `QuestionEvent` / `TextbookEvent` / `ChapterEvent` proto message
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-003-ai09:Textbook/Chapter 事件在契约文档遗漏
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约不明确
|
||||
- **描述**:02-architecture-design.md §5.1 列出 4 类 textbook 事件 + 1 类 chapter 事件,但 contracts/content_contract.md §1.4 仅列出 knowledge_point 与 question 两类事件,Textbook/Chapter 事件未登记。matrix.md §4 也仅列 kp + question。导致下游(data-ana)无法感知教材/章节变更。
|
||||
- **建议方案**:在 contract.md 与 matrix.md 补登记 `edu.content.textbook.events`(action: created/updated/published/archived)与 `edu.content.chapter.events`(action: created/updated/deleted)。若 coord 认为教材/章节无需对外发事件,则在 design doc §5.1 删除相关事件。
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-004-ai09:gRPC RPC 数量三方文档不一致
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约不明确
|
||||
- **描述**:content gRPC RPC 数量在三处文档不一致:
|
||||
|
||||
| Service | 02-architecture-design.md §4.2 | contracts/content_contract.md §1.1 | matrix.md §2 |
|
||||
| --------------------- | ------------------------------ | ---------------------------------- | ------------ |
|
||||
| TextbookService | 5(含 Update/Delete 新增) | 3(无 Update/Delete) | 18(总数) |
|
||||
| ChapterService | 3(Create/List/Get) | 4(含 Update,无 Delete) | — |
|
||||
| KnowledgeGraphService | 4 | 4 | — |
|
||||
| QuestionService | 6(无 Publish/Search) | 7(含 Publish/Search) | — |
|
||||
| **合计** | **18** | **18** | **18** |
|
||||
|
||||
- design doc 缺 QuestionService.PublishQuestion / SearchQuestions(contract 有)
|
||||
- contract 缺 TextbookService.Update/Delete(design doc 有)
|
||||
- design doc ChapterService 缺 Update(contract 有);contract ChapterService 缺 Delete(design doc 也缺)
|
||||
- **建议方案**:以 contract.md 为契约唯一源(已对齐 matrix.md 18 RPC 总数),反向修正 design doc:
|
||||
1. TextbookService 补 Update/Delete(与 contract 对齐)
|
||||
2. ChapterService 补 Update + Delete(design doc + contract 都缺 Delete,需补)
|
||||
3. QuestionService 补 PublishQuestion + SearchQuestions(与 contract 对齐)
|
||||
4. 最终 RPC 总数:TextbookService 5 + ChapterService 5 + KnowledgeGraphService 4 + QuestionService 7 = **21 RPC**(需同步更新 matrix.md §2 的 18 → 21)
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-005-ai09:core-edu → content 失效事件 topic 无定义
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:前置依赖缺失
|
||||
- **描述**:02-architecture-design.md §5.2 列出 content 消费 `edu.teaching.content.invalidated`(待 ai03 确认 topic),但:
|
||||
1. events.proto 无 ContentInvalidatedEvent message
|
||||
2. matrix.md §4 未登记该 topic
|
||||
3. core-edu 设计文档(ai08)未明确发布该事件
|
||||
4. 01-understanding.md §5 也标注"具体 topic 待 core-edu ai03 设计确认"——此处 ai03 疑为笔误,core-edu 实际由 ai08 负责
|
||||
- **建议方案**:content 不主动消费 core-edu 失效事件(content 是上游内容提供方,core-edu 是消费方),删除 §5.2 中该条目;若确有联动需求,由 core-edu 主动调用 content gRPC UpdateQuestion 状态变更,而非事件驱动。
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-006-ai09:文档结尾"直接 push main"与项目规则冲突
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:其他
|
||||
- **描述**:01-understanding.md 末尾与 02-architecture-design.md 末尾均标注 `Branch: 单仓库并行模式(直接 push main)`,但 [project_rules §8 Git 工作流](../../../../.trae/rules/project_rules.md) 明确规定:
|
||||
- §8:分支开发,AI 不得自行切换/创建/合并分支
|
||||
- §14.3:AI 禁止 `git merge`、`git push origin main`
|
||||
- 当前 worktree 分支为 `feat-review-content-module-docs-WAIyMA`
|
||||
- **建议方案**:删除两份文档末尾"单仓库并行模式(直接 push main)"字样,改为 `Branch: feat-review-content-module-docs-WAIyMA(分支开发,提交后通知人类合并)`。
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-007-ai09:questions 表 created_by 字段迁移风险
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:其他
|
||||
- **描述**:02-architecture-design.md §3.1.4 questions 表新增 `created_by varchar(32) NOT NULL`,但现有 [questions.schema.ts](../../../services/content/src/questions/questions.schema.ts) 无该字段,且现有数据无 created_by 值。schema 迁移时 NOT NULL 约束会导致历史数据迁移失败。
|
||||
- **建议方案**:迁移期间先用 `created_by varchar(32) NULL`,数据回填后再加 NOT NULL 约束;或为新数据强制要求 created_by(应用层校验),历史数据用 `'system'` 默认值回填。
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-008-ai09:设计文档缺缓存策略与 API 版本化策略
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:其他
|
||||
- **描述**:02-architecture-design.md 未涉及两个长远架构必备项:
|
||||
1. **缓存策略**:[env.ts](../../../services/content/src/config/env.ts) 已预留 `REDIS_URL`,但 design doc 未设计缓存层(教材树/知识点树是典型读多写少场景,应缓存)
|
||||
2. **API 版本化**:REST 端点无 `/v1/` 前缀(matrix.md §5 显示 api-gateway 路由为 `/api/v1/teacher/*`,但 content 自身端点 `/textbooks` 无版本号),未来破坏性变更无版本隔离机制
|
||||
- **建议方案**:
|
||||
1. P4 在 design doc §6 补"缓存策略"小节:教材树/章节树 Redis 缓存 + 失效策略(Outbox 事件触发缓存失效)
|
||||
2. P4 在 design doc §4 补"API 版本化"说明:REST 端点统一加 `/v1/` 前缀(gRPC 用 proto package version)
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-009-ai09:knowledge-points schema 实际缺 difficulty/metadata 字段
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:其他
|
||||
- **描述**:02-architecture-design.md §3.1.3 knowledge_points 表列出 `difficulty tinyint NOT NULL DEFAULT 3` 与 `metadata json NULL`,但实际 [textbooks.schema.ts](../../../services/content/src/textbooks/textbooks.schema.ts) 中 `knowledgePoints` 表仅含 id/chapterId/title/description 四个字段,无 difficulty 与 metadata。01-understanding.md C7 提到"时间戳缺失"但未提到 difficulty/metadata 缺失。
|
||||
- **建议方案**:P4 schema 迁移时一并补齐 difficulty + metadata + created_at + updated_at(与 design doc §3.1.3 对齐)。
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-010-ai09:Neo4j Sync Worker 与 ES Sync Worker 在 P4 阶段不必要
|
||||
|
||||
- **提请方**:ai09
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:工作量超批
|
||||
- **描述**:02-architecture-design.md §1.1 分层图将 Neo4j Sync Worker 与 ES Sync Worker 并列展示,但:
|
||||
1. ES 在 P5 才引入,ES Sync Worker 在 P4 不必要
|
||||
2. 当前 [knowledge-points.service.ts](../../../services/content/src/knowledge-points/knowledge-points.service.ts) 的 `safeCreateNode` 是同步双写(业务事务内写 Neo4j),与 design doc §0.1 第 3 条"禁止业务事务内同步双写"原则冲突
|
||||
3. P4 应改为 Outbox 事件驱动异步同步 Neo4j,但 design doc §1.1 图中 Neo4j Sync Worker 的输入源同时画了"Kafka Consumer"与"CONSUMER",链路不清晰
|
||||
- **建议方案**:
|
||||
1. §1.1 图中明确标注 ES Sync Worker 为 P5 组件(虚线或灰显)
|
||||
2. §1.1 图中 Neo4j Sync Worker 的输入仅来自 content 自身 Outbox 事件(不消费 core-edu 事件)
|
||||
3. P4 任务 T6 明确"重构 knowledge-points.service.ts:移除 safeCreateNode 同步写,改为发 Outbox 事件"
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
---
|
||||
|
||||
## §2 待 coord 仲裁项汇总
|
||||
|
||||
| # | 标题 | 阻塞性 | 状态 |
|
||||
| ---- | -------------------------------------------- | ----------------------- | ------------ |
|
||||
| 001 | REST 端点设计与实现不一致 | 🟡 P4 重构时对齐 | 待 coord 仲裁 |
|
||||
| 002 | 事件 topic 命名策略冲突(独立 vs 聚合) | 🔴 阻塞 Outbox 实现 | 待 coord 仲裁 |
|
||||
| 003 | Textbook/Chapter 事件在契约文档遗漏 | 🟡 契约完整性 | 待 coord 仲裁 |
|
||||
| 004 | gRPC RPC 数量三方文档不一致 | 🔴 阻塞 proto 修改 | 待 coord 仲裁 |
|
||||
| 005 | core-edu → content 失效事件 topic 无定义 | 🟢 建议删除 | 待 coord 仲裁 |
|
||||
| 006 | "直接 push main"与项目规则冲突 | 🟡 文档修正 | 待 coord 仲裁 |
|
||||
| 007 | questions.created_by 迁移风险 | 🟡 schema 迁移 | 待 coord 仲裁 |
|
||||
| 008 | 缓存策略与 API 版本化策略缺失 | 🟢 长远架构 | 待 coord 仲裁 |
|
||||
| 009 | knowledge-points schema 实际缺字段 | 🟡 P4 schema 迁移 | 待 coord 仲裁 |
|
||||
| 010 | Sync Worker 链路与 P4 阶段不必要 | 🟡 设计澄清 | 待 coord 仲裁 |
|
||||
|
||||
@@ -1,45 +1,359 @@
|
||||
# content 工作排期
|
||||
|
||||
> 负责人:ai09
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/content_contract.md](../contracts/content_contract.md)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/content_contract.md](../contracts/content_contract.md)、[objections/content_issue.md](../objections/content_issue.md)、[../../services/content/docs/02-architecture-design.md](../../../services/content/docs/02-architecture-design.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 当前分支:`feat-review-content-module-docs-WAIyMA`
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
content 是内容服务,提供 TextbookService、ChapterService、KnowledgeGraphService、QuestionService,结合 Neo4j 知识图谱与 Elasticsearch 全文检索。全阶段目标:P2 服务骨架+Neo4j+ES → P3 四大 Service 实现 → P4-P6 持续优化。
|
||||
content 是内容资源中台服务(P4 阶段),承载 D4 内容资源限界上下文,提供 Textbook / Chapter / KnowledgePoint / Question 四个聚合的 CRUD 与知识图谱查询。
|
||||
|
||||
**关键交付**:
|
||||
- gRPC 50054 + 4 Service(Textbook/Chapter/KnowledgeGraph/Question),按 [coord-final-decisions.md §3.3](../../coord-final-decisions.md) N1/N3/N5 仲裁,P4 首次实现即启用 gRPC + 补全 QuestionService/ChapterService proto
|
||||
- MySQL 写模型 + Neo4j 知识图谱 + Outbox 事件驱动异步同步(禁止业务事务内同步双写 Neo4j)
|
||||
- Kafka 发布 `edu.content.knowledge_point.events` / `edu.content.question.events`(聚合 topic 策略,待 ISSUE-002 仲裁)
|
||||
- P5 引入 Elasticsearch 全文检索 + AI 出题入库(QuestionService.BatchCreateQuestions)
|
||||
- P6+ 长远演进:教材版本管理 / 跨租户内容共享 / 个性化学习路径推荐
|
||||
|
||||
**关键路径位置**:批次 3(P4),依赖批次 2 core-edu 完成(实际可并行:content 不强依赖 core-edu)
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P4-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai09 content 全阶段排期
|
||||
title ai09 content 全阶段排期(P4-P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a9a, 2026-07-10, Xd
|
||||
section P4 基础设施
|
||||
P4.1 schema 迁移补字段 :crit, c4a, 2026-07-19, 2d
|
||||
P4.2 Outbox 表+Publisher worker :crit, c4b, after c4a, 3d
|
||||
P4.3 Kafka producer(idempotent+txn) :crit, c4c, after c4b, 2d
|
||||
P4.4 Neo4j Sync Worker(异步) :crit, c4d, after c4c, 2d
|
||||
P4.5 重构 kp.service 移除同步双写 :crit, c4e, after c4d, 1d
|
||||
|
||||
section P4 gRPC 契约
|
||||
P4.6 content.proto 补 ChapterService/QuestionService :crit, c4f, 2026-07-19, 1d
|
||||
P4.7 gRPC controller 实现(4 Service) :crit, c4g, after c4f, 4d
|
||||
P4.8 buf generate + 类型校验 :c4h, after c4g, 1d
|
||||
|
||||
section P4 横切与质量
|
||||
P4.9 /readyz 多依赖(DB/Neo4j/Kafka) :c4i, after c4e, 1d
|
||||
P4.10 ZodError GlobalErrorFilter 分支 :c4j, after c4i, 1d
|
||||
P4.11 DB 改 getDb()+ID 改 cuid2 :c4k, after c4j, 1d
|
||||
P4.12 Repository 抽象补齐 :c4l, after c4k, 2d
|
||||
P4.13 单元测试(Service/Repository)≥60% :c4m, after c4l, 3d
|
||||
P4.14 修正 README 与实现对齐 :c4n, after c4m, 1d
|
||||
|
||||
section P5 ES+AI 集成
|
||||
P5.1 引入 @elastic/elasticsearch :crit, c5a, after c4m, 1d
|
||||
P5.2 ES mapping+ensureIndex :crit, c5b, after c5a, 1d
|
||||
P5.3 ES Sync Worker(消费事件同步索引) :crit, c5c, after c5b, 2d
|
||||
P5.4 GET /questions/search 检索 API :crit, c5d, after c5c, 2d
|
||||
P5.5 QuestionService gRPC 完善Publish/Search :c5e, after c5d, 1d
|
||||
P5.6 AI 出题 BatchCreateQuestions 联调 :c5f, after c5e, 2d
|
||||
P5.7 检索性能优化(<200ms) :c5g, after c5f, 2d
|
||||
P5.8 测试覆盖率≥80% :c5h, after c5g, 2d
|
||||
|
||||
section P6+ 演进
|
||||
P6.1 Question 审核工作流状态机 :c6a, after c5h, 3d
|
||||
P6.2 知识图谱可视化 API :c6b, after c6a, 3d
|
||||
P6.3 教材版本管理 :c6c, after c6b, 2d
|
||||
P6.4 /readyz 硬化+监控告警完善 :c6d, after c6c, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai09 接管后必须自行细化为完整 P2-P6 排期。
|
||||
**预估总工期**:P4 约 21 天 + P5 约 13 天 + P6+ 约 10 天 = **44 天**(与 workline.md §1 批次 3+4 时间窗口一致)
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### 3.1 P4 阶段任务
|
||||
|
||||
#### P4.1 schema 迁移补字段
|
||||
|
||||
- **负责人**:ai09
|
||||
- **交付物**:⚠️ 由 ai09 自行补充
|
||||
- **依赖**:见 [contracts/content_contract.md](../contracts/content_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai09 自行补充
|
||||
- **依赖**:无(自身 schema 现状)
|
||||
- **交付物**:
|
||||
- [textbooks.schema.ts](../../../services/content/src/textbooks/textbooks.schema.ts) textbooks 表补 `status` / `tenant_id` / `metadata` 字段
|
||||
- chapters 表补 `created_at` / `updated_at` / `status`(解决 [01-understanding.md](../../../services/content/docs/01-understanding.md) C7)
|
||||
- knowledge_points 表补 `difficulty` / `metadata` / `created_at` / `updated_at`(解决 ISSUE-009)
|
||||
- questions 表补 `status` / `source` / `created_by`(NULL 起步,解决 ISSUE-007)/ `metadata`
|
||||
- **验收标准**:`pnpm typecheck` 通过;Drizzle 类型重新生成;迁移脚本可幂等执行
|
||||
|
||||
#### P4.2 Outbox 表 + Publisher worker
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.1
|
||||
- **交付物**:
|
||||
- 新建 `src/shared/outbox/outbox.schema.ts`(content_outbox_events 表,见 design doc §3.1.5)
|
||||
- 新建 `src/shared/outbox/outbox.publisher.ts`(轮询 PENDING 事件投递 Kafka,指数退避重试)
|
||||
- 新建 `src/shared/outbox/outbox.module.ts`
|
||||
- **验收标准**:业务事务内写 questions + outbox 同事务提交;Publisher worker 独立轮询;retry_count 累加正确
|
||||
|
||||
#### P4.3 Kafka producer(idempotent + transactionalId)
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.2
|
||||
- **交付物**:
|
||||
- 新建 `src/shared/kafka/producer.ts`(kafkajs 客户端,idempotent=true,transactionalId=content-producer)
|
||||
- 新建 `src/shared/kafka/kafka.module.ts`
|
||||
- package.json 添加 kafkajs 依赖
|
||||
- **验收标准**:producer 启动成功;transactionalId 唯一;幂等投递无重复
|
||||
|
||||
#### P4.4 Neo4j Sync Worker(异步同步)
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.3
|
||||
- **交付物**:
|
||||
- 新建 `src/shared/sync/neo4j-sync.worker.ts`(消费 content 自身 Outbox 事件,异步创建/更新 Neo4j 节点与关系)
|
||||
- 消费 `KnowledgePointCreated` / `KnowledgePointPrerequisiteAdded` 等事件
|
||||
- **验收标准**:MySQL 写知识点后,Neo4j 节点最终一致出现(延迟 < 2s);Neo4j 故障时事件不丢失,恢复后补齐
|
||||
|
||||
#### P4.5 重构 knowledge-points.service.ts 移除同步双写
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.4
|
||||
- **交付物**:
|
||||
- [knowledge-points.service.ts](../../../services/content/src/knowledge-points/knowledge-points.service.ts) 删除 `safeCreateNode` 同步写 Neo4j 逻辑
|
||||
- 改为发 Outbox 事件 `KnowledgePointCreated`
|
||||
- `addPrerequisite` 改为发 Outbox 事件 `KnowledgePointPrerequisiteAdded`
|
||||
- **验收标准**:业务事务内不再直接写 Neo4j;Neo4j 写入全部走异步 Sync Worker(解决 ISSUE-010 + 01-understanding C9)
|
||||
|
||||
#### P4.6 content.proto 补 ChapterService / QuestionService
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:无(自身 proto 现状)
|
||||
- **交付物**:
|
||||
- [content.proto](../../../packages/shared-proto/proto/content.proto) 补 ChapterService(CreateChapter/ListChapters/GetChapter/UpdateChapter/DeleteChapter)
|
||||
- 补 QuestionService(CreateQuestion/BatchCreateQuestions/GetQuestion/ListQuestions/UpdateQuestion/DeleteQuestion/PublishQuestion/SearchQuestions)
|
||||
- 补 TextbookService.UpdateTextbook / DeleteTextbook
|
||||
- 补全 message 定义(Chapter / Question / QuestionRequest 等)
|
||||
- 同步补 events.proto 的 KnowledgePointEvent / QuestionEvent / TextbookEvent / ChapterEvent(待 ISSUE-002 仲裁后定)
|
||||
- **验收标准**:`buf lint` 通过;`buf breaking` 无破坏性变更(新增字段 OK);contract.md 与 design doc §4.2 RPC 数对齐
|
||||
|
||||
#### P4.7 gRPC controller 实现(4 Service)
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.6
|
||||
- **交付物**:
|
||||
- 新建 `src/textbooks/textbooks.grpc.controller.ts`
|
||||
- 新建 `src/chapters/chapters.grpc.controller.ts`
|
||||
- 新建 `src/knowledge-points/knowledge-points.grpc.controller.ts`
|
||||
- 新建 `src/questions/questions.grpc.controller.ts`
|
||||
- main.ts 启用 gRPC server 50054
|
||||
- **验收标准**:`grpcurl` 调用 4 Service 全部 RPC 返回正确;HealthService.Check 返回 SERVING(解决 N1)
|
||||
|
||||
#### P4.8 buf generate + 类型校验
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.7
|
||||
- **交付物**:`pnpm buf:generate` 生成 TS 类型;content 服务引用生成类型
|
||||
- **验收标准**:`pnpm typecheck` 通过
|
||||
|
||||
#### P4.9 /readyz 多依赖检查
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.5
|
||||
- **交付物**:[health.controller.ts](../../../services/content/src/shared/health/health.controller.ts) 改造 /readyz,检查 DB / Neo4j / Kafka producer / Kafka consumer lag
|
||||
- **验收标准**:返回 design doc §6.6 格式;Neo4j 不可用 → status=degraded;DB 不可用 → status=down(解决 N2 + 01-understanding C-section readyz 问题)
|
||||
|
||||
#### P4.10 ZodError GlobalErrorFilter 分支
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:无
|
||||
- **交付物**:[global-error.filter.ts](../../../services/content/src/shared/errors/global-error.filter.ts) 增加 ZodError 识别分支,返回 400 + 字段级错误详情
|
||||
- **验收标准**:Zod 校验失败返回 design doc §4.3 错误结构
|
||||
|
||||
#### P4.11 DB 改 getDb() + ID 改 cuid2
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- [database.ts](../../../services/content/src/config/database.ts) 改为 `getDb()` 函数式懒加载(对齐 classes 黄金模板)
|
||||
- service 层 `randomUUID()` 改为 `cuid2()`(package.json 添加 @paralleldrive/cuid2)
|
||||
- **验收标准**:所有 service 使用 getDb();所有 ID 生成用 cuid2
|
||||
|
||||
#### P4.12 Repository 抽象补齐
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.11
|
||||
- **交付物**:补齐 textbooks/questions 的 Repository 抽象(与 chapters/knowledge-points 一致)
|
||||
- **验收标准**:Service 层不直接调用 Drizzle API,全部走 Repository
|
||||
|
||||
#### P4.13 单元测试 ≥ 60%
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.12
|
||||
- **交付物**:
|
||||
- 新建 `*.spec.ts` 覆盖 Service 层 + Repository 层
|
||||
- 重点覆盖 QuestionsService 题型校验 / KnowledgePointsService 前置依赖 / Outbox Publisher 重试逻辑
|
||||
- **验收标准**:`pnpm test` 通过;覆盖率 ≥ 60%
|
||||
|
||||
#### P4.14 修正 README 与实现对齐
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.13
|
||||
- **交付物**:[README.md](../../../services/content/README.md) 修正 `TextbooksService.createKnowledgeGraph` 错误描述(实际在 KnowledgePointsService);补齐 4 个领域模块说明
|
||||
- **验收标准**:README 与源码完全一致(解决 01-understanding C3)
|
||||
|
||||
### 3.2 P5 阶段任务
|
||||
|
||||
#### P5.1 引入 @elastic/elasticsearch
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4 全部完成
|
||||
- **交付物**:package.json 添加 @elastic/elasticsearch;新建 `src/config/elasticsearch.ts`
|
||||
- **验收标准**:esClient 单例;ES_URL 未配置时 esClient=null 降级
|
||||
|
||||
#### P5.2 ES mapping + ensureIndex
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.1
|
||||
- **交付物**:按 design doc §3.3.1 实现 questions 索引 mapping;启动时 `ensureIndex` 幂等
|
||||
- **验收标准**:索引创建成功;ik_max_word / ik_smart 分词器配置正确
|
||||
|
||||
#### P5.3 ES Sync Worker
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.2
|
||||
- **交付物**:新建 `src/shared/sync/es-sync.worker.ts`,消费 `QuestionCreated` / `QuestionUpdated` / `QuestionPublished` / `QuestionDeleted` 事件增量更新索引
|
||||
- **验收标准**:MySQL 写题目后,ES 索引最终一致(延迟 < 2s)
|
||||
|
||||
#### P5.4 GET /questions/search 检索 API
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.3
|
||||
- **交付物**:[questions.controller.ts](../../../services/content/src/questions/questions.controller.ts) 增加 `@Get("search")` 端点;ES 查询支持 q / type / difficulty / knowledgePointId 过滤
|
||||
- **验收标准**:检索延迟 < 200ms(P5 退出标准);返回分页结构
|
||||
|
||||
#### P5.5 QuestionService gRPC 完善 Publish/Search
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.4
|
||||
- **交付物**:gRPC controller 补 PublishQuestion / SearchQuestions RPC 实现
|
||||
- **验收标准**:与 contract.md §1.1 完全对齐(解决 ISSUE-004 部分)
|
||||
|
||||
#### P5.6 AI 出题 BatchCreateQuestions 联调
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.5 + ai12 ai 服务就绪
|
||||
- **交付物**:与 ai12 联调 BatchCreateQuestions RPC;服务账号权限校验
|
||||
- **验收标准**:AI 服务调用成功入库;batch_size ≤ 100 限制生效
|
||||
|
||||
#### P5.7 检索性能优化
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.6
|
||||
- **交付物**:ES 查询 DSL 优化;缓存热点查询结果(Redis)
|
||||
- **验收标准**:P95 延迟 < 200ms
|
||||
|
||||
#### P5.8 测试覆盖率 ≥ 80%
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.7
|
||||
- **交付物**:补集成测试(gRPC + ES + Neo4j 端到端)
|
||||
- **验收标准**:覆盖率 ≥ 80%
|
||||
|
||||
### 3.3 P6+ 演进任务
|
||||
|
||||
#### P6.1 Question 审核工作流状态机
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5 完成
|
||||
- **交付物**:Question.status 状态机完整实现(draft → pending_review → published/rejected → archived);审核日志表
|
||||
- **验收标准**:状态转换校验正确;非法转换返回 409
|
||||
|
||||
#### P6.2 知识图谱可视化 API
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P6.1
|
||||
- **交付物**:`GET /knowledge-graph/visualization` 返回 nodes/edges 结构(解决 01-understanding L3)
|
||||
- **验收标准**:返回 D3.js / vis.js 可消费的图结构
|
||||
|
||||
#### P6.3 教材版本管理
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P6.2
|
||||
- **交付物**:Textbook.version 字段启用;版本切换不破坏题库引用
|
||||
- **验收标准**:新旧版本教材并存;题库引用按版本隔离
|
||||
|
||||
#### P6.4 /readyz 硬化 + 监控告警完善
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P6.3
|
||||
- **交付物**:/readyz 探针列表完善;Prometheus 告警规则补齐(consumer lag / outbox pending / ES latency)
|
||||
- **验收标准**:告警阈值合理;故障演练通过
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai09 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai09 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游 | 就绪标志 | 必需性 | mock 策略 |
|
||||
| -------------- | ----------------------------------------------------- | ---------------------- | ------------------------------------------ |
|
||||
| infra | MySQL 8 / Neo4j 5 / Kafka 集群可用 | 🔴 必需 | 本地 docker-compose |
|
||||
| infra | Redis 可用(P5+ 缓存用,env.ts 已预留 REDIS_URL) | 🟢 P5+ 可选 | 未配置时跳过缓存 |
|
||||
| infra | Elasticsearch 8 可用(P5+) | 🟢 P5+ 必需 | 未配置时检索降级到 MySQL LIKE |
|
||||
| shared-proto | content.proto / events.proto 补全 | 🔴 必需(P4.6 自身完成) | 自行修改 |
|
||||
| core-edu (ai08) | gRPC 50053 启用 | 🟢 可选(content 独立) | 不依赖 core-edu 实时数据 |
|
||||
| ai (ai12) | gRPC 50058 启用 | 🟢 P5 联调时必需 | grpc-mock 拦截 |
|
||||
|
||||
### 4.2 我的就绪标志(供下游消费)
|
||||
|
||||
- [ ] **P4 就绪**:
|
||||
- [ ] content gRPC 50054 启用(HealthService.Check 返回 SERVING)
|
||||
- [ ] TextbookService 5 RPC 可调用(Create/Get/List/Update/Delete)
|
||||
- [ ] ChapterService 5 RPC 可调用(Create/Get/List/Update/Delete)
|
||||
- [ ] KnowledgeGraphService 4 RPC 可调用(GetPrerequisites/GetLearningPath/AddPrerequisite/RemovePrerequisite)
|
||||
- [ ] QuestionService 7 RPC 可调用(Create/BatchCreate/Get/List/Update/Delete/Publish/Search)
|
||||
- [ ] edu.content.knowledge_point.events / edu.content.question.events topic 可发布
|
||||
- [ ] /readyz 返回 DB/Neo4j/Kafka 三依赖状态
|
||||
- [ ] **P5 就绪**:
|
||||
- [ ] GET /questions/search 检索 API 可用(延迟 < 200ms)
|
||||
- [ ] QuestionService.SearchQuestions gRPC 可调用
|
||||
- [ ] BatchCreateQuestions 与 ai12 联调通过
|
||||
- [ ] **P6+ 就绪**:
|
||||
- [ ] 审核工作流状态机完整
|
||||
- [ ] 知识图谱可视化 API 可用
|
||||
|
||||
### 4.3 我提供的 mock(供下游消费)
|
||||
|
||||
在 content 真实服务就绪前,为下游(teacher-bff / student-bff / ai / data-ana)提供以下 mock:
|
||||
|
||||
- **gRPC mock**(grpc-mock 拦截 50054 端口):
|
||||
- TextbookService.ListTextbooks 返回固定 5 个 Textbook(语数英理化)
|
||||
- ChapterService.ListChapters 返回固定章节树(每教材 10 章)
|
||||
- KnowledgeGraphService.GetLearningPath 返回固定 8 个 KnowledgePoint 推荐顺序
|
||||
- KnowledgeGraphService.GetPrerequisites 返回固定 3 个前置知识点
|
||||
- QuestionService.SearchQuestions 返回固定 20 个 Question(含 options)
|
||||
- QuestionService.BatchCreateQuestions 返回成功 + 生成 20 个 ID
|
||||
- **Kafka mock**:content 就绪前不发布真实事件,下游使用本地 stub
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与缓解
|
||||
|
||||
| 风险 | 阶段 | 缓解措施 |
|
||||
| ------------------------------------------ | ---- | ------------------------------------------------------------ |
|
||||
| ISSUE-002 topic 策略未仲裁导致 Outbox 阻塞 | P4 | 优先推动 coord 仲裁;开发期间用 stub topic,仲裁后切换 |
|
||||
| ISSUE-004 RPC 数量未仲裁导致 proto 阻塞 | P4 | 优先推动 coord 仲裁;按建议方案 21 RPC 实现,仲裁后调整 |
|
||||
| Neo4j 与 MySQL 双向一致性 | P4 | Outbox 事件驱动 + 幂等去重 + consumer lag 监控 |
|
||||
| ES 索引重建期间检索不可用 | P5 | alias 切换模式(双索引蓝绿) |
|
||||
| AI 批量出题 CreateQuestions 高并发 | P5 | batch_size ≤ 100 + 异步队列 + 限流 |
|
||||
| schema 迁移期间历史数据 created_by 缺失 | P4 | 按 ISSUE-007 方案:NULL 起步或 'system' 默认值回填 |
|
||||
|
||||
---
|
||||
|
||||
## §6 与 workline.md §1 对齐
|
||||
|
||||
- 批次 3(P4):ai09 content 11 天(workline.md §1 排期 `after b2b, 11d`)
|
||||
- 本排期 P4 实际 21 天(含测试 + README 修正),与 workline.md 11 天存在差距
|
||||
- **差距原因**:workline.md §1 为 coord 初始规划(标注"ai09 接管后必须自行细化"),本文件为 ai09 细化后的实际排期
|
||||
- **同步动作**:提请 coord 在 workline.md §1 更新 content 工期为 21 天(或协调压缩测试任务)
|
||||
|
||||
Reference in New Issue
Block a user