From 9ba368477dd4bb9351120e32c4c237a9999816ea Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:04:49 +0800 Subject: [PATCH] =?UTF-8?q?refactor(docs):=20=E7=A7=BB=E9=99=A4=20ai07=20c?= =?UTF-8?q?lasses=20=E5=90=88=E5=B9=B6=E5=88=B0=20core-edu=EF=BC=8C16=20AI?= =?UTF-8?q?=20=E9=99=8D=E4=B8=BA=2015=20AI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/architecture/ai-allocation.md | 541 ++++++----- .../architecture/ai03-phase1-understanding.md | 216 ----- .../architecture/ai05-phase1-understanding.md | 269 ------ .../architecture/ai06-phase1-understanding.md | 339 ------- docs/architecture/ai06-phase2-design.md | 864 ------------------ .../issues/contracts/classes_contract.md | 123 --- .../issues/objections/classes_issue.md | 58 -- .../issues/worklines/classes_workline.md | 107 --- docs/troubleshooting/known-issues.md | 229 ++++- 9 files changed, 457 insertions(+), 2289 deletions(-) delete mode 100644 docs/architecture/ai03-phase1-understanding.md delete mode 100644 docs/architecture/ai05-phase1-understanding.md delete mode 100644 docs/architecture/ai06-phase1-understanding.md delete mode 100644 docs/architecture/ai06-phase2-design.md delete mode 100644 docs/architecture/issues/contracts/classes_contract.md delete mode 100644 docs/architecture/issues/objections/classes_issue.md delete mode 100644 docs/architecture/issues/worklines/classes_workline.md diff --git a/docs/architecture/ai-allocation.md b/docs/architecture/ai-allocation.md index 7e3ca25..25fbdac 100644 --- a/docs/architecture/ai-allocation.md +++ b/docs/architecture/ai-allocation.md @@ -1,102 +1,126 @@ # AI 分配方案与架构设计外包流程 -> 版本:1.1 +> 版本:2.0 > 日期:2026-07-10 -> 适用范围:Edu 微服务项目(架构设计外包三阶段已实施,进入持续开发阶段) -> 关联文档:[多 AI 协作指南](../standards/multi-ai-collaboration.md)、[004 架构影响地图](./004_architecture_impact_map.md)、[待开发功能路线图](./roadmap/pending-features.md) +> 适用范围:Edu 微服务项目(15 AI + 1 coord 全并行开发模式) +> 关联文档:[多 AI 协作指南](../standards/multi-ai-collaboration.md)、[004 架构影响地图](./004_architecture_impact_map.md)、[待开发功能路线图](./roadmap/pending-features.md)、[总裁最终裁决](./president-final-rulings.md)、[AI 工作顺序协同规划](./ai-work-orchestration.md)、[coord 仲裁汇总](./issues/coord.md)、[总工作安排](./issues/workline.md)、[上下游对接总矩阵](./issues/matrix.md) > -> **状态说明**:架构设计外包三阶段(模块理解 → 模块架构设计 → 按图实施)已实施完成,各模块设计文档见 `services//docs/` 与 `apps//docs/`。当前进入持续开发阶段,所有 AI 工作默认按多 AI 协作模式进行。 +> **状态说明**:架构设计外包三阶段(模块理解 → 模块架构设计 → 按图实施)已实施完成。当前进入**全并行开发模式**:各 AI 一口气完成自己模块从 P2 到 P6 的所有代码,开发期间用 mock,最后统一集成测试。 --- -## 1. 外包总流程:三阶段(已实施) +## 1. 全并行开发模式 ``` -阶段 1:模块理解 阶段 2:模块架构设计 阶段 3:按图实施 - (每个 AI 独立) (每个 AI 独立) (并行开发) - │ │ │ - 按需阅读模块架构文档 产出模块内部架构图 按自己画的图写代码 - 理解边界与契约 定义内部模块/数据流 coord 定期巡检一致性 - 理解与其他模块的接口 标注与其他模块的交互点 遇到偏差更新架构图 - │ │ │ - ▼ ▼ ▼ - 交付:理解确认书 交付:模块架构设计文档 交付:代码 + 更新图 +各 AI 独立开发(全并行) coord 协调 + │ │ + 读取自己的 contract.md 维护 proto 契约 + 在 workline.md 规划全阶段任务 仲裁异议(objections/) + 开发期间用 mock(见 contract.md §4) 汇总就绪信号(matrix.md §8) + │ │ + ▼ ▼ + 交付:完整代码 + 就绪信号 交付:契约 + 仲裁 + 总矩阵 + │ │ + └──────────► 统一集成测试 ◄─────────────┘ ``` -> **三阶段已实施完成**。阶段 1 产出见各模块 `docs/01-understanding.md`,阶段 2 产出见各模块 `docs/02-architecture-design.md`,阶段 3 持续进行中。 +**核心原则**: -**阶段 1 目标**:每个 AI 读懂自己负责的模块在架构中的位置、边界、契约(按需阅读,见 §4)。 -**阶段 2 目标**:每个 AI 产出自己模块的内部架构设计,经过 coord 交叉审查后放行。 -**阶段 3 目标**:按设计文档写代码,coord 定期巡检一致性。 +- 各 AI 一口气完成自己模块从 P2 到 P6 的所有代码 +- 开发期间消费上游的 mock(gRPC mock / MSW / Kafka mock) +- 上游就绪后在 `issues/matrix.md` §8 更新就绪信号 +- 所有模块就绪后统一集成测试(见 `issues/matrix.md` §9 检查清单) --- ## 2. 完整服务清单 -| 类别 | 服务名 | 语言/框架 | 限界上下文 | 阶段 | 状态 | -| ---- | -------------- | ---------------- | ---------------------- | ------ | ----------------- | -| 网关 | api-gateway | Go (Gin) | API 网关 | P1 | ✅ 已实现 | -| 网关 | push-gateway | Go (Gin) | 推送网关 | P5 | 📐 需设计 | -| BFF | teacher-bff | TS (NestJS) | 教学场景域聚合 | P2 | ✅ 已实现 | -| BFF | student-bff | TS (NestJS) | 学习场景域聚合 | P3 | 📐 需设计 | -| BFF | parent-bff | TS (NestJS) | 家长场景域聚合 | P4 | 📐 需设计 | -| 业务 | iam | TS (NestJS) | 身份认证 | P2 | ✅ 已实现 | -| 业务 | core-edu | TS (NestJS) | 教学核心(含 classes) | P3 | 📐 待合并 classes | -| 业务 | content | TS (NestJS) | 内容资源 | P4 | 📐 需设计 | -| 业务 | msg | TS (NestJS) | 消息通知 | P5 | 📐 需设计 | -| 业务 | data-ana | Python (FastAPI) | 数据分析 | P4 | 📐 需设计 | -| 业务 | ai | Python (FastAPI) | AI 网关 | P5 | 📐 需设计 | -| 前端 | teacher-portal | TS (Next.js) | 教学场景域前端 | P2 | ✅ 已实现 | -| 前端 | student-portal | TS (Next.js) | 学习场景域前端 | P3 | 📐 需设计 | -| 前端 | parent-portal | TS (Next.js) | 家长场景域前端 | P4 | 📐 需设计 | -| 前端 | admin-portal | TS (Next.js) | 管理场景域前端 | P6 | 📐 需设计 | -| 共享 | shared-proto | protobuf | 契约 | 跨阶段 | ✅ 部分 | -| 共享 | shared-ts | TS | TS 共享工具 | 跨阶段 | — | -| 共享 | shared-go | Go | Go 共享工具 | 跨阶段 | — | -| 共享 | shared-py | Python | Python 共享工具 | 跨阶段 | — | -| 基础 | infra | — | K8s/Grafana/WAF | 跨阶段 | ✅ 部分 | +| 类别 | 服务名 | 语言/框架 | 限界上下文 | 阶段 | HTTP 端口 | gRPC 端口 | 负责 AI | +| ---- | -------------- | ---------------- | ---------------------- | ------ | --------- | --------- | ------- | +| 网关 | api-gateway | Go (Gin) | API 网关 | P2 | 8080 | — | ai01 | +| 网关 | push-gateway | Go (Gin) | 推送网关 | P5 | 8081 | — | ai02 | +| BFF | teacher-bff | TS (NestJS) | 教学场景域聚合 | P2 | 3003 | — | ai03 | +| BFF | student-bff | TS (NestJS) | 学习场景域聚合 | P3 | 3009 | — | ai04 | +| BFF | parent-bff | TS (NestJS) | 家长场景域聚合 | P4 | 3010 | — | ai05 | +| 业务 | iam | TS (NestJS) | 身份认证 | P2 | 3002 | 50052 | ai06 | +| 业务 | core-edu | TS (NestJS) | 教学核心(含 classes) | P3 | 3004 | 50053 | ai08 | +| 业务 | content | TS (NestJS) | 内容资源 | P4 | 3005 | 50054 | ai09 | +| 业务 | msg | TS (NestJS) | 消息通知 | P5 | 3007 | 50056 | ai10 | +| 业务 | data-ana | Python (FastAPI) | 数据分析 | P4 | 3006 | 50055 | ai11 | +| 业务 | ai | Python (FastAPI) | AI 网关 | P5 | 3008 | 50057 | ai12 | +| 前端 | teacher-portal | TS (Next.js) | 教学场景域前端 | P2 | 4000 | — | ai13 | +| 前端 | student-portal | TS (Next.js) | 学习场景域前端 | P3 | 4001 | — | ai14 | +| 前端 | parent-portal | TS (Next.js) | 家长场景域前端 | P4 | 4002 | — | ai15 | +| 前端 | admin-portal | TS (Next.js) | 管理场景域前端 | P6 | 4003 | — | ai16 | +| 共享 | shared-proto | protobuf | 契约 | 跨阶段 | — | — | coord | +| 共享 | shared-ts | TS | TS 共享工具 | 跨阶段 | — | — | coord | +| 共享 | shared-go | Go | Go 共享工具 | 跨阶段 | — | — | coord | +| 共享 | shared-py | Python | Python 共享工具 | 跨阶段 | — | — | coord | +| 共享 | ui-tokens | TS | 设计令牌 | 跨阶段 | — | — | coord | +| 共享 | ui-components | TS | UI 组件库 | 跨阶段 | — | — | ai13 | +| 共享 | hooks | TS | React Hooks 库 | 跨阶段 | — | — | ai13 | +| 基础 | infra | — | K8s/Grafana/WAF | 跨阶段 | — | — | coord | -> 状态标记:✅ 已实现需审计 | 📐 需架构设计(本次外包核心产出) +> **注**:classes 模块已合并到 core-edu(ai08 负责),不再独立分配 AI。 --- -## 3. AI 分配方案(7 AI + 1 coord) +## 3. AI 分配方案(15 AI + 1 coord) ### 3.1 分配原则 -- **同语言内聚**:一个 AI 负责多个同语言服务,学习成本只付一次 +- **单一负责制**:每个模块(限界上下文)只有一个 AI 负责,禁止并行修改同一模块 +- **同语言内聚**:一个 AI 负责同语言服务,学习成本只付一次 - **领域亲缘性**:同类业务放一起(BFF 归 BFF、Python 归 Python) -- **工作负载均衡**:Neo4j+ES 的内容服务、ClickHouse 的分析服务复杂度高,不绑太多其他服务 -- **前端统一**:Module Federation 微前端由一人设计,保证 shell + remote 架构一致 -- **黄金模板对齐**:已实现的 services 负责 AI 需审计并对齐 classes 标准 +- **工作负载均衡**:复杂服务(Neo4j+ES 的 content、ClickHouse 的 data-ana)独立分配 +- **前端统一**:Module Federation 微前端由各自 AI 负责,teacher-portal 作为 Shell 由 ai13 统一设计 +- **coord 不写业务代码**:专注契约管理 + 仲裁 + 共享包 ### 3.2 分配矩阵 -| AI 标识 | 语言 | 服务 | 数量 | 阶段归属 | -| --------- | ------ | ----------------------------------------------------------- | ---- | -------- | -| **ai01** | Go | api-gateway、push-gateway | 2 | P1 + P5 | -| **ai02** | TS | iam | 1 | P2 | -| **ai03** | TS | teacher-bff、core-edu | 2 | P2 + P3 | -| **ai04** | TS | student-bff、parent-bff | 2 | P3 + P4 | -| **ai05** | TS | content、msg | 2 | P4 + P5 | -| **ai06** | Python | data-ana、ai | 2 | P4 + P5 | -| **ai07** | TS | teacher-portal、student-portal、parent-portal、admin-portal | 4 | P2-P6 | -| **coord** | — | shared-proto、shared-*、infra/、docs/、CI/CD | — | 跨阶段 | +| AI 标识 | 语言 | 服务 | 阶段 | gRPC 端口 | +| --------- | ------ | ----------------------- | --------- | --------- | +| **ai01** | Go | api-gateway | P2 | — | +| **ai02** | Go | push-gateway | P5 | — | +| **ai03** | TS | teacher-bff | P2+扩展 | — | +| **ai04** | TS | student-bff | P3 | — | +| **ai05** | TS | parent-bff | P4 | — | +| **ai06** | TS | iam | P2.1+P2.2 | 50052 | +| **ai08** | TS | core-edu | P3 | 50053 | +| **ai09** | TS | content | P4 | 50054 | +| **ai10** | TS | msg | P5 | 50056 | +| **ai11** | Python | data-ana | P4 | 50055 | +| **ai12** | Python | ai | P5 | 50057 | +| **ai13** | TS | teacher-portal | P2+扩展 | — | +| **ai14** | TS | student-portal | P3 | — | +| **ai15** | TS | parent-portal | P4 | — | +| **ai16** | TS | admin-portal | P6 | — | +| **coord** | — | shared-* / infra / docs | 跨阶段 | — | ### 3.3 为什么这样拆 -| 决策 | 理由 | -| ----------------------------- | ------------------------------------------------------------------------------- | -| ai02 独立负责 iam | RBAC 三层角色 + DataScope 6 级 + 视口 4 层是整个系统的权限中枢,复杂度最高 | -| ai03 teacher-bff + core-edu | 教学域全栈:BFF 聚合 + 核心业务。考试/作业/成绩状态机在一个人手里,不跨 AI 协调 | -| ai04 student-bff + parent-bff | 两个 BFF 都是纯聚合层,技术同质(GraphQL + DataLoader),设计模式完全复用 | -| ai05 content + msg | 都依赖 ES,content 建索引、msg 查索引,一人设计避免 ES 索引冲突 | -| ai07 前端 4 端 | Module Federation shell + remote 架构需一人统一设计,4 端共享组件库和权限体系 | -| coord 不写业务代码 | 专注契约管理 + 交叉审查,保证 7 份设计文档的接口一致性 | +| 决策 | 理由 | +| ---------------------------- | ------------------------------------------------------------------------------- | +| ai01 独立负责 api-gateway | Go 网关层,路由 + JWT 验签 + 限流,接入 shared-go | +| ai02 独立负责 push-gateway | Go 推送网关,WebSocket/SSE + 多设备会话隔离 + 审计表 | +| ai03 独立负责 teacher-bff | 教学场景域 BFF,GraphQL schema + DataLoader + ActionState + admin 命名空间 | +| ai04 独立负责 student-bff | 学习场景域 BFF,GraphQL + 考试作答 + 作业提交 | +| ai05 独立负责 parent-bff | 家长场景域 BFF,GraphQL + 多子女切换 + 成绩趋势 | +| ai06 独立负责 iam | RBAC + DataScope + JWT RS256 + gRPC 12 RPC + 审计日志,权限中枢复杂度最高 | +| ai08 独立负责 core-edu | 教学核心(含原 classes 模块),5 Service 22 RPC + Outbox + 考试/作业/成绩状态机 | +| ai09 独立负责 content | 内容资源,4 Service 18 RPC + Neo4j 知识图谱 + ES 题库检索 | +| ai10 独立负责 msg | 消息通知,3 Service 13 RPC + Outbox + 多渠道通知 | +| ai11 独立负责 data-ana | Python 数据分析,12 RPC + ClickHouse 宽表 + CDC 消费 | +| ai12 独立负责 ai | Python AI 网关,6 RPC + ES 检索 + LLM 网关 + 流式响应 | +| ai13 独立负责 teacher-portal | MF Shell + GraphQL client 单例 + AppShell + 共享组件库 | +| ai14 独立负责 student-portal | MF Remote + 考试作答 + 作业提交 | +| ai15 独立负责 parent-portal | MF Remote + Dashboard + 多子女切换 | +| ai16 独立负责 admin-portal | MF Remote + 用户管理 + 角色权限 + 审计日志 | +| coord 不写业务代码 | 专注 proto 契约 + 仲裁 + shared-* 包 + infra + 文档体系 | --- -## 4. 各 AI 阶段 1 文档阅读清单(按需阅读) +## 4. 各 AI 文档阅读清单(按需阅读) > **架构文档按需阅读原则**(见 [project_rules §10](../../.trae/rules/project_rules.md) 与 [multi-ai-collaboration §2.2](../standards/multi-ai-collaboration.md)): > @@ -112,18 +136,27 @@ | 1 | [project_rules.md](../../.trae/rules/project_rules.md) | 项目强制约束(架构规则、编码规范、提交规范、多 AI 协作、分支开发) | | 2 | [coding-standards.md](../standards/coding-standards.md) | 多语言编码标准 | | 3 | [multi-ai-collaboration.md](../standards/multi-ai-collaboration.md) | 多 AI 协作指南(§2.1 严格模块边界、§2.2 按需阅读、§3 分支开发规则) | +| 4 | [president-final-rulings.md](./president-final-rulings.md) | 总裁最终裁决(70+ 问题逐个裁决 + 时间统筹 + 15 AI 工作清单) | ### 4.2 模块开发按需阅读(只读自己模块,禁止读其他模块内部实现) -| AI | 必读文档 | 说明 | -| ---- | ------------------------------------------------------------------------------------ | ------------------- | -| ai01 | `services/api-gateway/README.md`、`services/push-gateway/README.md`、004 §网关层章节 | Go 网关层架构 | -| ai02 | `services/iam/README.md`、004 §iam 章节 | 身份认证架构 | -| ai03 | `services/teacher-bff/README.md`、`services/core-edu/README.md`、004 §教学场景域章节 | 教学场景域架构 | -| ai04 | 004 §student-bff / parent-bff 章节(待建立服务,无 README) | 学习+家长场景域 BFF | -| ai05 | 004 §content / msg 章节(待建立服务,无 README) | 内容+通知架构 | -| ai06 | `services/data-ana/README.md`、`services/ai/README.md`、004 §数据分析/AI 章节 | Python 数据+AI 架构 | -| ai07 | `apps/teacher-portal/README.md`、004 §前端章节 | 前端 4 端架构 | +| AI | 必读文档 | 说明 | +| ---- | ------------------------------------------------------------------------------------------------------------------ | --------------- | +| ai01 | `services/api-gateway/README.md`、004 §网关层章节、[contract](./issues/contracts/api-gateway_contract.md) | Go 网关层架构 | +| ai02 | `services/push-gateway/README.md`、004 §push-gateway 章节、[contract](./issues/contracts/push-gateway_contract.md) | Go 推送网关架构 | +| ai03 | `services/teacher-bff/README.md`、004 §teacher-bff 章节、[contract](./issues/contracts/teacher-bff_contract.md) | 教学场景域 BFF | +| ai04 | 004 §student-bff 章节、[contract](./issues/contracts/student-bff_contract.md) | 学习场景域 BFF | +| ai05 | 004 §parent-bff 章节、[contract](./issues/contracts/parent-bff_contract.md) | 家长场景域 BFF | +| ai06 | `services/iam/README.md`、004 §iam 章节、[contract](./issues/contracts/iam_contract.md) | 身份认证架构 | +| ai08 | `services/core-edu/README.md`、004 §core-edu 章节、[contract](./issues/contracts/core-edu_contract.md) | 教学核心架构 | +| ai09 | 004 §content 章节、[contract](./issues/contracts/content_contract.md) | 内容资源架构 | +| ai10 | 004 §msg 章节、[contract](./issues/contracts/msg_contract.md) | 消息通知架构 | +| ai11 | `services/data-ana/README.md`、004 §data-ana 章节、[contract](./issues/contracts/data-ana_contract.md) | Python 数据分析 | +| ai12 | 004 §ai 章节、[contract](./issues/contracts/ai_contract.md) | Python AI 网关 | +| ai13 | `apps/teacher-portal/README.md`、004 §前端章节、[contract](./issues/contracts/teacher-portal_contract.md) | 教师端前端 | +| ai14 | 004 §前端章节、[contract](./issues/contracts/student-portal_contract.md) | 学生端前端 | +| ai15 | 004 §前端章节、[contract](./issues/contracts/parent-portal_contract.md) | 家长端前端 | +| ai16 | 004 §前端章节、[contract](./issues/contracts/admin-portal_contract.md) | 管理端前端 | > **禁止越界阅读**:上表只列自己模块。如需确认与某模块的契约,走 §4.4 跨模块契约阅读,不读对方内部源码与 README。 @@ -135,245 +168,203 @@ | 2 | [MIGRATION_GUIDE.md](../../MIGRATION_GUIDE.md) | 迁移指南 | | 3 | [004 架构影响地图](./004_architecture_impact_map.md) 全文 | 全局架构与依赖(coord 必读;模块开发只读 §4.2 章节) | | 4 | [pending-features.md](./roadmap/pending-features.md) | 六阶段路线图 | - -> 模块开发 AI **不需要**通读上述全局文档,仅在任务涉及跨模块协调、基础设施变更、文档体系维护时按需阅读。 +| 5 | [ai-work-orchestration.md](./ai-work-orchestration.md) | AI 工作顺序协同规划 | +| 6 | [issues/coord.md](./issues/coord.md) | coord 仲裁汇总 | +| 7 | [issues/matrix.md](./issues/matrix.md) | 上下游对接总矩阵 | ### 4.4 跨模块契约按需阅读(仅涉及 proto 变更或确认接口时) -| 场景 | 必读文档 | -| ----------------------- | ----------------------------------------------------------------------------------------------------- | -| 修改/新增 proto message | `packages/shared-proto/proto/` 中**相关** .proto 文件(不读全部,只读变更涉及的文件) | -| 确认与其他模块的接口 | 004 §跨模块交互章节 + 对方服务的**对外接口**(proto / API 端点 / Kafka 事件名),**不读对方内部实现** | +| 场景 | 必读文档 | +| ----------------------- | --------------------------------------------------------------------------------------- | +| 修改/新增 proto message | `packages/shared-proto/proto/` 中**相关** .proto 文件(不读全部,只读变更涉及的文件) | +| 确认与其他模块的接口 | [issues/contracts/](./issues/contracts/) 中对方模块的 contract.md + 004 §跨模块交互章节 | +| 确认上下游依赖 | [issues/matrix.md](./issues/matrix.md) §1 服务依赖图 + §2-§5 接口矩阵 | ### 4.5 黄金模板参考(仅 TS 服务首次实现新服务时) -| AI | 参考文档 | -| ------------------ | ---------------------------------------------------------------------------------- | -| ai02-05(TS 服务) | `services/classes/src/` 黄金模板源码(首次实现新服务时参考横切关注点,不强制全读) | -| ai01 / ai06 / ai07 | 按需参考 classes 的横切关注点实现模式(错误处理/可观测/健康检查等),不强制读源码 | +| AI | 参考文档 | +| ---------------------------- | ---------------------------------------------------------------------------------- | +| ai03-05, ai08-10(TS 服务) | `services/classes/src/` 黄金模板源码(首次实现新服务时参考横切关注点,不强制全读) | +| ai01 / ai02 / ai06 / ai11-16 | 按需参考 classes 的横切关注点实现模式(错误处理/可观测/健康检查等),不强制读源码 | --- -## 5. 各 AI 阶段 2 设计重点 +## 5. 各 AI 全阶段设计重点 -### ai01 — Go 网关层 +### ai01 — api-gateway(Go) -| 服务 | 设计重点 | -| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| api-gateway | 路由表矩阵(路径 → 下游服务 + 端口,需覆盖全部 6 个业务服务 + 3 个 BFF);限流策略表(每路由 QPS);熔断阈值配置(错误率/延迟阈值);JWT RS256 公钥校验流程;CORS 白名单;请求 ID 注入 | -| push-gateway | WebSocket 连接生命周期(认证 → 心跳 → 断线重连);与 msg 的 gRPC 推送通道协议;用户 session 映射(在线用户 → WebSocket 连接);水平扩展方案(Redis Pub/Sub 跨实例广播) | +| 设计重点 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 路由表矩阵(路径 → 下游服务 + 端口,需覆盖全部 6 个业务服务 + 3 个 BFF);限流策略表(每路由 QPS);熔断阈值配置(错误率/延迟阈值);JWT RS256 公钥校验流程(shared-go/jwks);CORS 白名单;请求 ID 注入;接入 shared-go(env/logger/tracer) | -### ai02 — 身份认证 +### ai02 — push-gateway(Go) -| 服务 | 设计重点 | -| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| iam | RBAC 权限点枚举(全部模块的 CRUD 权限常量);三层角色模型(系统/组织/临时)权限合并规则;DataScope 6 级 SQL WHERE 注入规则(每级对应的过滤条件);视口 4 层配置表设计(导航/路由/组件/数据);JWT RS256 私钥签发 + 公钥暴露端点;refresh_token 轮换策略;权限解析 API(getEffectivePermissions → permissions + viewports + dataScope) | +| 设计重点 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| HTTP /internal/* + Kafka 双通道;WebSocket/SSE 连接生命周期(认证 → 心跳 → 断线重连);多设备会话隔离策略;Redis Pub/Sub 跨实例广播;X-Internal-Key 认证;审计表(6 字段);ActionState 信封响应 | -### ai03 — 教学场景域 +### ai03 — teacher-bff(TS) -| 服务 | 设计重点 | -| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| teacher-bff | GraphQL schema(Query/Mutation,按场景域组织);DataLoader 批量去重策略;并行 gRPC 调用编排;聚合结果缓存 TTL 策略(5-30s 短缓存);教师角色差异化(教师 vs 教导主任 vs 教研组长 → 视口推导) | -| core-edu | classes 模块黄金模板对齐;考试生命周期状态机(草稿 → 已发布 → 作答中 → 批改中 → 已出分 → 已归档);Outbox 事件定义(ExamPublished、HomeworkSubmitted、GradeRecorded);成绩计算公式与配置化;作业提交高并发优化(Redis 分布式锁 + 排队);排课/考勤数据模型 | +| 设计重点 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| GraphQL schema(SDL-first,存放 packages/shared-ts/contracts/graphql/);DataLoader 批量去重策略;并行 gRPC 调用编排;ActionState 信封 + 降级模式 B;admin schema 命名空间预留(P6);DownstreamClient 抽象;depth ≤ 7 + cost ≤ 1000 | -### ai04 — 学习 + 家长场景域 BFF +### ai04 — student-bff(TS) -| 服务 | 设计重点 | -| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| student-bff | 学生端 GraphQL schema(与 teacher-bff 对比差异);DataLoader 复用 teacher-bff 模式;权限区分(学生只能看自己的数据,DataScope=SELF);考试/作业/成绩的学生视角 API | -| parent-bff | 家长端 GraphQL schema;与 iam 的学生-家长关联查询;多子女账户切换设计;家长通知偏好配置 | +| 设计重点 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 学生端 GraphQL schema;DataLoader 复用 teacher-bff 模式;权限区分(学生只能看自己的数据,DataScope=SELF);考试/作业/成绩的学生视角 API;DownstreamClient 复用 | -### ai05 — 内容 + 通知 +### ai05 — parent-bff(TS) -| 服务 | 设计重点 | -| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| content | Neo4j 图模型(知识点 → 知识点前置依赖 → 教材关联);ES 索引 mapping 设计(题库全文检索 + 标签过滤);题库 CRUD 完整 API(含批量导入);与 ai 服务的 gRPC 接口(查询知识点/题库用于 AI 出题);教材/章节结构树 | -| msg | 通知渠道抽象(站内信/邮件/短信,策略模式);ES 降级查询策略(DB 不可用时走 ES);Kafka 消费幂等设计(event_id 去重);与 push-gateway 的推送通道协议;通知模板管理;已读/未读状态管理 | +| 设计重点 | +| --------------------------------------------------------------------------------------------------------------------------------------- | +| 家长端 GraphQL schema;与 iam 的学生-家长关联查询(GetChildrenByParent);多子女账户切换设计;家长通知偏好配置;DataScope=CHILDREN 过滤 | -### ai06 — Python 数据 + AI +### ai06 — iam(TS) -| 服务 | 设计重点 | -| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| data-ana | ClickHouse 宽表设计(考试、作业、成绩、掌握度、出勤 5 张宽表);CDC 消费者架构(Debezium → Kafka → ClickHouse);学情分析 API(班级统计/个人趋势/预警阈值);掌握度计算算法(加权滑动平均);Dashboard 数据聚合 | -| ai | LLM Provider 适配器模式(OpenAI/百川/本地模型);SSE 流式响应(题目逐字生成);出题 Prompt 模板管理;备课工作流(分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库);用量计费/频率限制 | +| 设计重点 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RBAC 权限点枚举(全部模块的 CRUD 权限常量);三层角色模型(系统/组织/临时)权限合并规则;DataScope 6 级 SQL WHERE 注入规则;视口 4 层配置表设计;JWT RS256 私钥签发 + 公钥暴露端点(GetPublicKey RPC);gRPC 50052 + 12 RPC;iam_student_guardians 表;审计日志(AuditEvent + topic edu.iam.audit.created);shared-ts Outbox 接入;/readyz 5 项依赖检查 | -### ai07 — 前端 4 端 +### ai08 — core-edu(TS) -| 服务 | 设计重点 | -| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| teacher-portal | 现有代码审计对齐黄金标准;Module Federation shell 暴露的共享组件 | -| 全部 4 端 | Module Federation shell + remote 架构设计;路由骨架(4 端路由表对照);共享组件库(错误边界 ErrorBoundary、Loading 骨架屏、Empty 空态、权限控制组件);`usePermission().hasPermission()` 统一权限 Hook;API 请求层统一错误处理(toast 提示);4 端差异化对比表(导航菜单/路由/组件/数据 4 层差异) | +| 设计重点 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| classes 模块黄金模板对齐;gRPC 50053 + 5 Service 22 RPC(ClassService + ExamService + HomeworkService + GradeService + AttendanceService);考试生命周期状态机;Outbox 事件定义(ExamEvent/HomeworkEvent/GradeEvent/ClassEvent);TOPIC_MAP 修订(edu.teaching.* 命名);成绩计算公式与配置化 | + +### ai09 — content(TS) + +| 设计重点 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gRPC 50054 + 4 Service 18 RPC(TextbookService + ChapterService + KnowledgeGraphService + QuestionService);Neo4j 图模型(知识点 → 前置依赖 → 教材关联);ES 索引 mapping 设计(题库全文检索 + 标签过滤);Outbox(KnowledgePointEvent + QuestionEvent);Neo4j Sync Worker + ES Sync Worker;/readyz(MySQL + Neo4j + Kafka) | + +### ai10 — msg(TS) + +| 设计重点 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gRPC 50056 + 3 Service 13 RPC(NotificationService + NotificationPreferenceService + NotificationTemplateService);通知渠道抽象(站内信/邮件/短信,策略模式);Kafka 消费幂等设计(event_id 去重);Outbox(NotificationEvent);与 push-gateway 的 /internal/push 通道;通知模板管理;已读/未读状态管理 | + +### ai11 — data-ana(Python) + +| 设计重点 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gRPC 50055 + 12 RPC(含 Server Streaming SubscribeMasteryUpdate);ClickHouse 宽表设计(考试、作业、成绩、掌握度、出勤 5 张宽表);CDC 消费者架构(Debezium → Kafka → ClickHouse);学情分析 API(班级统计/个人趋势/预警阈值);掌握度计算算法(加权滑动平均);Dashboard 数据聚合;降级模式(ClickHouse 不可用时降级);MasteryEvent + AIUsageEvent(豁免 Outbox) | + +### ai12 — ai(Python) + +| 设计重点 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gRPC 50057 + 6 RPC(含 StreamGenerateQuestion + StreamChat);LLM Provider 适配器模式(OpenAI/百川/本地模型);SSE 流式响应(题目逐字生成);出题 Prompt 模板管理;备课工作流(分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库);用量计费/频率限制;AIUsageEvent(豁免 Outbox);接入 content + data-ana gRPC | + +### ai13 — teacher-portal(TS) + +| 设计重点 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Module Federation Shell 配置(exposes + shared,无 remotes);GraphQLProvider + urql client 单例;AppShell 改造(REST → GraphQL);登录页 + Dashboard + 班级列表;共享组件库(packages/ui-components + packages/hooks + packages/ui-tokens);feature flag NEXT_PUBLIC_MF_ENABLED;P3+ 扩展(考试/作业/成绩/学情/推送/AI 页面) | + +### ai14 — student-portal(TS) + +| 设计重点 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| MF Remote;7 页(dashboard/homework/exams/take/result/grades/my-schedule);复用 teacher-portal Shell 暴露的 GraphQLProvider + AppShell + 共享组件;学生端权限(DataScope=SELF) | + +### ai15 — parent-portal(TS) + +| 设计重点 | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | +| MF Remote;Dashboard + 孩子列表 + 考试/作业/成绩 + 通知偏好 UI 占位;复用 teacher-portal Shell;多子女切换 UI;家长端权限(DataScope=CHILDREN) | + +### ai16 — admin-portal(TS) + +| 设计重点 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| MF Remote;用户管理 + 角色权限管理 + 学校设置 + 组织管理 + 审计日志消费;复用 teacher-bff GraphQL endpoint(admin 命名空间);admin 权限点(ADMIN_ 前缀) | ### coord — 协调 AI -| 职责 | 具体内容 | -| ---------------- | ------------------------------------------------------------------------------------------------------- | -| proto 契约维护 | 统一管理 `packages/shared-proto/`,跨 AI 的 proto 变更唯一入口 | -| 设计文档交叉审查 | 审查 7 份模块架构设计文档的跨模块接口一致性(接口签名匹配、topic 不重复、端口不冲突、错误码前缀不重叠) | -| 黄金模板维护 | 维护 classes 黄金模板标准,审查其他服务对齐情况 | -| 架构文档同步 | 各 AI 产出设计文档后,同步更新 `004_architecture_impact_map.md` | -| 共享包管理 | shared-ts / shared-go / shared-py 建立与维护 | -| CI/CD | `.github/workflows/ci.yml` 覆盖全部 15 服务 | -| 基础设施 | `infra/` K8s/Grafana/WAF/灾备(可由 SRE AI 协助) | +| 职责 | 具体内容 | +| -------------- | ----------------------------------------------------------------------------- | +| proto 契约维护 | 统一管理 `packages/shared-proto/`,跨 AI 的 proto 变更唯一入口 | +| 仲裁汇总 | 审阅 `issues/objections/` 各 AI 提请的异议,在 `issues/coord.md` 写入仲裁结果 | +| 总矩阵维护 | 维护 `issues/matrix.md` 上下游对接总矩阵 + 就绪信号跟踪 | +| 共享包管理 | shared-proto / shared-ts / shared-go / shared-py / ui-tokens 建立与维护 | +| 架构文档同步 | 各 AI 产出后,同步更新 `004_architecture_impact_map.md` | +| CI/CD | `.github/workflows/ci.yml` 覆盖全部 15 服务 | +| 基础设施 | `infra/` K8s/Grafana/WAF/灾备(可由 SRE AI 协助) | --- -## 6. 阶段 1 交付物模板 +## 6. 协作文档体系 -每个 AI 按需阅读完 §4 对应文档后,必须产出以下确认书: +### 6.1 目录结构 -```markdown -## 模块理解确认书 — [模块名] - -### 1. 我在架构中的位置 - -- 层级:Gateway / BFF / Service / Data / Frontend -- 上游:谁调用我? -- 下游:我调用谁? -- 通信方式:HTTP / gRPC / Kafka / WebSocket / 直接 DB? - -### 2. 我的限界上下文 - -- 我负责哪些聚合/实体? -- 我的数据属于哪个业务领域(D1-D6 中的哪个)? -- 我不负责什么(明确边界外的东西)? - -### 3. 我与外部的契约 - -- 我消费哪些 proto message(从 shared-proto)? -- 我暴露哪些 API 端点或 gRPC 方法或 Kafka 事件? -- 错误码前缀是什么? - -### 4. 我的技术栈 - -- 语言 / 框架 / ORM / 存储 - -### 5. 我的阶段归属 - -- 属于 P1-P6 哪个阶段? -- 当前阶段目标是什么? -- 依赖哪些上游阶段的产出? - -### 6. 我需要对齐的黄金模板项(对照 classes 服务) - -- [ ] 权限装饰器 @RequirePermission(全部 Controller 方法) -- [ ] 错误码前缀统一 -- [ ] logger / metrics / tracer 三支柱 -- [ ] /healthz + /readyz 健康检查 -- [ ] 优雅关闭(SIGTERM) -- [ ] 测试覆盖率 ≥ 80% -- [ ] Dockerfile 多阶段构建 -- [ ] Zod 输入验证 -- [ ] GlobalErrorFilter 统一兜底 +``` +docs/architecture/issues/ +├── coord.md # coord 仲裁汇总 +├── workline.md # 总工作安排(15 AI 甘特图) +├── matrix.md # 上下游对接总矩阵 +├── objections/ # 各模块问题/异议(AI 提请,coord 仲裁) +│ ├── [模块名]_issue.md # 15 个模块各一个 +│ └── cross_issue.md # 跨模块问题 +├── worklines/ # 各模块工作排期(AI 自己写,覆盖 P2-P6 全阶段) +│ └── [模块名]_workline.md # 15 个模块各一个 +└── contracts/ # 各模块对接契约(我提供什么 + 我消费什么 + mock 策略) + └── [模块名]_contract.md # 15 个模块各一个 ``` ---- +### 6.2 三类文档职责 -## 7. 阶段 2 交付物模板 +| 文档类型 | 子文件夹 | 维护方 | 内容 | +| ---------------- | ------------- | ---------------------- | ---------------------------------------------- | +| issue(异议) | `objections/` | 各 AI 提请,coord 仲裁 | 遇到问题时追加条目,coord 仲裁后更新状态 | +| workline(排期) | `worklines/` | 各 AI 自己写 | 全阶段甘特图 + 详细任务 + 依赖与就绪信号 | +| contract(契约) | `contracts/` | 各 AI 维护 | 我提供什么 + 我消费什么 + mock 策略 + 就绪信号 | -每个 AI 在阶段 1 确认书通过 coord 审核后,产出以下架构设计文档: +### 6.3 全并行开发工作流 -```markdown -## 模块架构设计文档 — [模块名] +1. 各 AI 读取自己的 `contracts/[模块名]_contract.md` 了解上下游接口 +2. 各 AI 在 `worklines/[模块名]_workline.md` 详细规划全阶段任务 +3. 开发期间消费上游的 mock(见 contract.md §4 mock 策略) +4. 上游就绪后在 `matrix.md` §8 更新就绪信号 +5. 所有模块就绪后统一集成测试(见 `matrix.md` §9 检查清单) -### 1. 模块内部分层图 +### 6.4 遇到问题 -[画图:Controller → Guard → Service → Repository → DB 调用链] -[标注中间件、Guard、Filter 的拦截点] - -### 2. 领域模型 - -- 聚合根:有哪些? -- 实体/值对象:有哪些? -- 聚合间如何通信?(同服务内直接调用 / 跨服务走事件) - -### 3. 数据模型 - -- 有哪些表?(列出 schema,标注字段类型、约束) -- 每张表的索引策略(主键、唯一索引、查询索引) -- 读写分离策略(哪些走主库、哪些走读模型) - -### 4. API 设计 - -| method | path | 权限 | 请求/响应结构 | 说明 | -| ------ | ---- | ---------- | ------------- | ---- | -| POST | /xxx | XXX_CREATE | { ... } | ... | - -### 5. 事件设计(如适用) - -- 我发布哪些领域事件?触发时机是什么? -- 我消费哪些外部事件?消费后做什么? -- 事件 Topic 名称(遵循 `edu...` 格式) - -### 6. 横切关注点对齐清单 - -- [ ] 权限装饰器(列出所有端点及对应权限常量) -- [ ] 错误码清单(带前缀,每个错误码 → 触发条件 → HTTP 状态码) -- [ ] Logger 初始化位置与配置(pino/zap/structlog) -- [ ] Metrics 指标清单(指标名 / 类型 / 标签 / 描述) -- [ ] Tracer 初始化位置(OTLP endpoint) -- [ ] /healthz 检查逻辑 -- [ ] /readyz 检查逻辑(DB SELECT 1 / Redis PING / Kafka 连接) -- [ ] 优雅关闭顺序(HTTP server → DB → Redis → Kafka) - -### 7. 与其他模块的交互点(契约清单) - -| 方向 | 对方服务 | 协议 | 接口/事件 | 用途 | -| ------ | -------- | ----- | --------- | ---- | -| 调用 | xxx | gRPC | XxxMethod | ... | -| 被调用 | xxx | gRPC | YyyMethod | ... | -| 发布 | — | Kafka | topic 名 | ... | -| 消费 | — | Kafka | topic 名 | ... | - -### 8. 风险与假设 - -- 我假设 [某服务] 提供了 [某接口],如果没提供我的 fallback 是什么? -- 我的模块有哪些技术风险?(性能瓶颈、数据一致性、外部依赖) -- 有哪些未决的设计决策需要协调 AI 仲裁? -``` +各 AI 在 `objections/[模块名]_issue.md` 追加异议条目 → coord 审阅 → 在 `coord.md` 写入仲裁结果 → 各 AI 按仲裁结果执行。 --- -## 8. 交叉审查规则 +## 7. 交叉审查规则 -coord 收到全部 7 份设计文档后,执行以下审查: +### 7.1 coord 定期审查 -### 8.1 接口一致性检查 +coord 收到各 AI 的 contract.md 后,执行以下审查: -```markdown -| 服务 A 说 | 服务 B 说 | 是否匹配 | -| --------------------------------------------------------- | ---------------------------------------------- | --------- | -| ai02 iam: 暴露 getUserInfo(userId) | ai03 teacher-bff: 调用 iam.getUserInfo(userId) | ✅ | -| ai03 core-edu: 调用 content.getKnowledgePoints(subjectId) | ai05 content: ??? | ⚠️ 待确认 | -``` +| 检查项 | 检查方式 | +| -------------------- | --------------------------------------------------------------- | +| 端口不冲突 | 对照 [port-allocation](../../infra/port-allocation.md) 端口矩阵 | +| gRPC 端口不冲突 | 50052-50057 一一对应 | +| Topic 不重复 | 汇总全部 AI 的 Kafka 事件,去重检查(见 matrix.md §4) | +| 错误码前缀不重叠 | 汇总全部 AI 的错误码前缀,唯一性检查(见 matrix.md §6) | +| Proto message 不遗漏 | 检查全部"跨模块交互点"是否在 proto 中有对应定义 | +| 就绪信号可验证 | 检查每个 AI 的就绪信号是否可验证(见 matrix.md §8) | -### 8.2 全局冲突检查 +### 7.2 AI 间契约确认 -| 检查项 | 检查方式 | -| -------------------- | ---------------------------------------------------------------------- | -| 端口不冲突 | 对照 [full-stack-runbook](../standards/full-stack-runbook.md) 端口矩阵 | -| Topic 不重复 | 汇总全部 AI 的 §5 事件设计,去重检查 | -| 错误码前缀不重叠 | 汇总全部 AI 的 §6 错误码清单,前缀唯一性检查 | -| Proto message 不遗漏 | 检查全部"跨模块交互点"是否在 proto 中有对应定义 | +当 AI A 需要确认与 AI B 的接口时: -### 8.3 黄金模板对齐检查 - -| 检查项 | 全部 TS NestJS 服务 | -| ----------------------- | ---------------------- | -| @RequirePermission 覆盖 | 每个 Controller 方法 | -| 错误码前缀 | 用服务名大写前缀 | -| /healthz + /readyz | 存在且逻辑正确 | -| Zod 输入验证 | Controller 层解析 body | -| GlobalErrorFilter | 注册到 AppModule | -| Dockerfile 多阶段 | builder + runtime | +1. 读 AI B 的 `contracts/[B模块名]_contract.md` +2. 如有疑问,在 `objections/cross_issue.md` 追加条目 +3. coord 仲裁后在 `coord.md` 写入结论 --- -## 9. 协作规则 +## 8. 协作规则 > **默认多 AI 协作**:所有 AI 工作默认按多 AI 合作模式进行,详见 [multi-ai-collaboration.md](../standards/multi-ai-collaboration.md)。 -### 9.1 分支并行开发 +### 8.1 分支并行开发 采用**分支开发**模式,人类决策者维护分支: @@ -384,7 +375,7 @@ coord 收到全部 7 份设计文档后,执行以下审查: - `packages/shared-proto/` 仅 coord 修改,其他 AI 只读 - `docs/`、`.trae/`、`infra/`、`.github/` 仅 coord 修改 -### 9.2 唯一冲突文件处理 +### 8.2 唯一冲突文件处理 `pnpm-lock.yaml` 是唯一可能多 AI 同时修改的文件,冲突时由**人类决策者**在合并时解决: @@ -399,33 +390,29 @@ git add pnpm-lock.yaml git commit ``` -### 9.3 提交规范 +### 8.3 提交规范 ```bash # 每个 AI 在自己的服务目录内工作(在分配的分支上) git add services//... -git commit -m "docs(): 模块架构设计文档" - -# 或 -git commit -m "docs(): 阶段1理解确认书" -# 完成后通知人类决策者合并 +git commit -m "feat(): 模块架构设计文档" ``` > **不标注 AI 身份**:commit message 不追加 `AI-Agent:` 等身份字段,经验沉淀不出现 AI 标识。 -### 9.4 proto 变更流程 +### 8.4 proto 变更流程 任何 AI 需要新增/修改 proto: -1. 在共享协调渠道声明需求(格式:`# proto-change: <描述>`) +1. 在 `objections/cross_issue.md` 声明需求 2. coord 统一修改 `packages/shared-proto/` -3. coord 通知受影响 AI 更新设计文档 +3. coord 通知受影响 AI 更新 contract.md --- -## 10. 审计模板(阶段 1 自检用) +## 9. 审计模板(自检用) -每个 AI 审计自己负责的已实现服务时,填写下表: +每个 AI 审计自己负责的服务时,填写下表: ```markdown ## 服务审计表 — [AI标识] @@ -437,10 +424,18 @@ git commit -m "docs(): 阶段1理解确认书" --- -## 11. 相关文档 +## 10. 相关文档 - [多 AI 协作指南](../standards/multi-ai-collaboration.md) — 日常开发协作流程 - [004 架构影响地图](./004_architecture_impact_map.md) — 全局架构与依赖 +- [总裁最终裁决](./president-final-rulings.md) — 70+ 问题逐个裁决 + 时间统筹 + 15 AI 工作清单 +- [AI 工作顺序协同规划](./ai-work-orchestration.md) — 6 批次并行规划(历史参考) +- [coord 强制裁决](./coord-final-decisions.md) — 80+ 项强制裁决 +- [coord 交叉审查](./coord-cross-review.md) — 交叉审查结果 +- [coord 仲裁汇总](./issues/coord.md) — 新流程仲裁记录 +- [总工作安排](./issues/workline.md) — 15 AI 甘特图 +- [上下游对接总矩阵](./issues/matrix.md) — 服务依赖 + 接口矩阵 + mock 策略 - [项目规则](../../.trae/rules/project_rules.md) — 强制约束 - [编码规范](../standards/coding-standards.md) — 多语言编码标准 - [待开发功能路线图](./roadmap/pending-features.md) — 六阶段目标 +- [端口分配](../../infra/port-allocation.md) — 端口矩阵 diff --git a/docs/architecture/ai03-phase1-understanding.md b/docs/architecture/ai03-phase1-understanding.md deleted file mode 100644 index 0471a6a..0000000 --- a/docs/architecture/ai03-phase1-understanding.md +++ /dev/null @@ -1,216 +0,0 @@ -# ai03 阶段 1 交付物:模块理解确认书 - -> AI 标识:ai03 -> 负责模块:teacher-bff(P2)、core-edu(P3) -> 阶段:架构设计外包 · 阶段 1(全局理解) -> 日期:2026-07-09 -> 关联文档:[ai-allocation.md](./ai-allocation.md)、[004 架构影响地图](./004_architecture_impact_map.md)、[pending-features.md](./roadmap/pending-features.md) - ---- - -## 模块理解确认书 — teacher-bff - -### 1. 我在架构中的位置 - -- **层级**:BFF 聚合层(L4) -- **上游**:api-gateway(Go/Gin)通过 HTTP 转发请求,注入 `x-user-id` / `x-user-roles` 头 -- **下游**:iam(3002)、classes(3001)、core-edu(3004);P3 后扩展 content、data-ana、ai -- **通信方式**: - - 当前:REST `fetch`(同步) - - 目标态(004 §4.1 / pending-features P2):**gRPC** 调下游业务服务 + **GraphQL Yoga** 对前端暴露 + DataLoader 防 N+1 -- **端口**:3003(见 [teacher-bff env.ts](../../services/teacher-bff/src/config/env.ts)) - -### 2. 我的限界上下文 - -- **聚合职责**:教学场景域(教师 / 教导主任 / 教研组长 共用)的数据聚合、裁剪、协议转换 -- **业务领域**:跨 D2 教学组织 + D3 教学核心 + D1 身份认证(只读拉取视口/权限) -- **我不负责**: - - 不持有业务状态(无 DB 写入,无 Outbox) - - 不做权限决策(依赖 Gateway JWT 校验 + 下游服务 `@RequirePermission`) - - 不直接访问任何业务服务数据库 -- **复用策略**(004 §5.4):教导主任 / 教研组长复用 Teacher BFF,通过视口差异化(L1 导航扩展管理菜单,L4 DataScope 扩大到年级) - -### 3. 我与外部的契约 - -- **消费的 proto message**: - - `iam.v1.IamService`:GetUserInfo / getEffectivePermissions(视口 + DataScope) - - `classes.v1.ClassService`:ListClasses / GetClass - - `core_edu.v1.ExamService / HomeworkService / GradeService`:全部 RPC -- **暴露的 API**(当前 REST,目标 GraphQL): - - `GET /teacher/dashboard` — 聚合 IAM 用户信息 + classes 列表 - - `GET /teacher/viewports` — 拉取 IAM 视口配置(L1 导航) - - `GET /teacher/classes/:classId/exams` — 聚合 core-edu 考试列表 - - `GET /teacher/classes/:classId/homework` — 聚合 core-edu 作业列表 - - `GET /teacher/exams/:examId/grades` — 聚合 core-edu 成绩列表 -- **错误码前缀**:BFF 自身错误用 `BAD_GATEWAY`(下游不可达);业务错误透传下游 `CORE_EDU_*` / `IAM_*` / `CLASSES_*` -- **缓存**:聚合结果 Redis 短缓存 5-30s(004 §6.3,当前未实现) - -### 4. 我的技术栈 - -- 语言:TypeScript 5.5+(ESM 模式,相对 import 带 `.js` 后缀) -- 框架:NestJS 10 -- 下游通信:当前 `fetch`(REST)→ 目标 `@grpc/grpc-js` + `@bufbuild/protobuf` -- 对前端:目标 GraphQL Yoga + DataLoader -- 缓存:Redis(待引入) -- 可观测:pino logger + prom-client metrics + OpenTelemetry tracer(已具备 [tracer.ts](../../services/teacher-bff/src/shared/observability/tracer.ts)) - -### 5. 我的阶段归属 - -- **P2 身份**:教师登录 → 获取 JWT → 访问 teacher-portal → 侧边栏按 viewports.L1 渲染 → 空白 Dashboard -- **P3 扩展**:考试/作业/成绩的 GraphQL 查询与 mutation -- **依赖上游**:P1 黄金模板 classes、P2 iam(getEffectivePermissions + 视口) - -### 6. 我需要对齐的黄金模板项(对照 classes 服务) - -- [ ] 权限装饰器 `@RequirePermission`(**BFF 不做权限决策**,当前无;目标态:BFF 不加 Guard,仅校验 `x-user-id` 存在) -- [x] 错误处理:[GlobalErrorFilter](../../services/teacher-bff/src/shared/errors/global-error.filter.ts) + ApplicationError 层次 -- [x] logger / metrics / tracer 三支柱(已具备) -- [x] `/healthz` 健康检查(HealthModule 已注册) -- [ ] `/readyz`(当前 HealthController 仅 liveness,无下游就绪探针) -- [x] 优雅关闭 SIGTERM(main.ts 已处理) -- [ ] 测试覆盖率 ≥ 80%(**当前 0%**,无测试文件) -- [ ] Dockerfile 多阶段构建(需核对) -- [ ] Zod 输入验证(当前 Controller 直接透传 unknown,**未做 Zod 校验**) -- [x] GlobalErrorFilter 统一兜底 - ---- - -## 模块理解确认书 — core-edu - -### 1. 我在架构中的位置 - -- **层级**:业务微服务层(L5) -- **上游**:teacher-bff(聚合层)、api-gateway(直接路由 `/api/v1/exams` 等) -- **下游**:MySQL(独占库)、Kafka(事件发布)、Redis(待引入,高并发提交锁) -- **通信方式**: - - 入口:当前 REST(`/exams`、`/homework`、`/grades`) - - 目标态(proto 注释):P3 起转 gRPC(`core_edu.v1.ExamService/HomeworkService/GradeService` 已定义) - - 出口:Kafka 事件(Outbox 模式发布) -- **端口**:3004(见 [core-edu env.ts](../../services/core-edu/src/config/env.ts)) - -### 2. 我的限界上下文 - -- **聚合职责**:跨 **D2 教学组织**(classes 模块,待合并)+ **D3 教学核心**(exams / homework / grades) -- **聚合根**:Exam、Homework、Grade、Class(待合并) -- **我不负责**: - - 不负责题库内容(→ content 服务) - - 不负责学情分析(→ data-ana 服务,消费 core-edu 事件) - - 不负责通知投递(→ msg 服务,消费 core-edu 事件) -- **数据自治**:独占 `core_edu` 数据库,表前缀 `core_edu_*` - -### 3. 我与外部的契约 - -- **暴露的 gRPC 契约**([core_edu.proto](../../packages/shared-proto/proto/core_edu.proto),已定义待实现): - - `ExamService`:CreateExam / GetExam / ListExamsByClass / UpdateExam / DeleteExam - - `HomeworkService`:AssignHomework / GetHomework / ListHomeworkByClass / SubmitHomework - - `GradeService`:RecordGrade / GetGrade / ListGradesByStudent/Exam/Homework -- **发布的领域事件**([events.proto](../../packages/shared-proto/proto/events.proto) + [outbox.publisher.ts TOPIC_MAP](../../services/core-edu/src/shared/outbox/outbox.publisher.ts)): - -| 事件 | Topic | 触发时机 | 消费者 | -| ------------------ | ------------------- | --------------------- | ------------- | -| exam.created | edu.exam.events | CreateExam 事务内 | msg、data-ana | -| exam.updated | edu.exam.events | UpdateExam | msg | -| exam.deleted | edu.exam.events | DeleteExam | data-ana | -| homework.assigned | edu.homework.events | AssignHomework | msg、data-ana | -| homework.submitted | edu.homework.events | SubmitHomework | data-ana、msg | -| homework.graded | edu.homework.events | **未实现**(P3 待补) | msg、data-ana | -| grade.recorded | edu.grade.events | RecordGrade | data-ana、msg | -| grade.updated | edu.grade.events | **未实现**(P3 待补) | data-ana | -| class.transferred | edu.class.events | classes 合并后 | data-ana | - -- **消费的事件**:`edu.identity.user.created`(IAM,初始化教师默认班级关联,**当前未消费**,P3 待补) -- **错误码前缀**:`CORE_EDU_*`(见 [application-error.ts CoreEduErrorCode](../../services/core-edu/src/shared/errors/application-error.ts)) - -### 4. 我的技术栈 - -- 语言:TypeScript 5.5+(ESM 模式) -- 框架:NestJS 10 -- ORM:Drizzle ORM(mysql2 driver,直接 `db` 导出,**与 classes 的 `getDb()` 不一致**) -- 存储:MySQL 8(独占库)、Redis(待引入)、Kafka(kafkajs,idempotent + transactionalId) -- 可观测:pino + prom-client + OTel(已具备) - -### 5. 我的阶段归属 - -- **P3 核心教学**:考试全生命周期 + Outbox + Kafka 事件落地 -- **退出标准**:教师创建考试 → 发布 → 学生作答 → 教师批改 → 事件到 Kafka → 成绩统计更新 → 全链路可观测 -- **依赖上游**:P1 classes 黄金模板、P2 iam(用户身份 + 权限) - -### 6. 我需要对齐的黄金模板项(对照 classes 服务) - -- [x] 权限装饰器 `@RequirePermission`(exams.controller 全覆盖;需核对 homework/grades controller) -- [x] 错误码前缀 `CORE_EDU_*`(已用 [CoreEduErrorCode 枚举](../../services/core-edu/src/shared/errors/application-error.ts)) -- [x] logger / metrics / tracer 三支柱 -- [x] `/healthz` 健康检查 -- [ ] `/readyz`(需补 DB SELECT 1 / Kafka 连接探针) -- [x] 优雅关闭 SIGTERM(main.ts 已处理 outboxPublisher.stop + disconnectKafka) -- [ ] 测试覆盖率 ≥ 80%(**当前 0%**,无测试文件) -- [ ] Dockerfile 多阶段构建(需核对) -- [ ] Zod 输入验证(**当前 Controller 直接接收 body,未 Zod 校验**;classes 用 zod schema) -- [x] GlobalErrorFilter 统一兜底 -- [x] Outbox 模式(事务内写业务表 + outbox 表,独立 publisher 投递) - ---- - -## §10 服务审计表 — ai03 - -> 对照 [黄金模板 classes 服务](../../services/classes/src/),审计已实现的两服务。状态:✅ 达标 / ⚠️ 部分 / ❌ 缺失 - -| 服务 | 权限装饰器 | 错误码前缀 | logger | metrics | tracer | /healthz | /readyz | 优雅关闭 | 测试覆盖率 | Dockerfile | -| ----------- | --------------------------------------- | --------------------------------- | ------- | -------------- | ------- | -------- | ------- | -------- | ---------- | ---------- | -| teacher-bff | ⚠️ 无(BFF 不做权限决策,依赖 Gateway) | ⚠️ 用 `BAD_GATEWAY`(无自有前缀) | ✅ pino | ✅ prom-client | ✅ OTel | ✅ | ❌ | ✅ | 0% ❌ | 待核对 | -| core-edu | ✅ `@RequirePermission(EXAM_*)` 全覆盖 | ✅ `CORE_EDU_*` | ✅ pino | ✅ prom-client | ✅ OTel | ✅ | ❌ | ✅ | 0% ❌ | 待核对 | - -### 审计发现的关键差距(P3 阶段 2 设计需解决) - -**teacher-bff**: - -1. ❌ 通信方式:当前 REST `fetch`,目标 gRPC + GraphQL(P2 退出标准要求 GraphQL Yoga + DataLoader) -2. ❌ 无 Redis 聚合缓存(004 §6.3 要求 5-30s 短缓存) -3. ❌ 无 DataLoader(防 N+1,pending-features P2 明确要求) -4. ❌ 无 `/readyz` 下游就绪探针 -5. ⚠️ env 配置用 `IamServiceUrl`/`ClassesServiceUrl`(REST URL),转 gRPC 后需改为 gRPC target -6. ⚠️ 无 Zod 输入验证 -7. ⚠️ 无测试 - -**core-edu**: - -1. ❌ 考试生命周期状态机缺失(当前仅 `draft` 初值,无 `published → in_progress → grading → graded → archived` 转换与校验) -2. ❌ 作业状态机不完整(仅 `assigned → submitted`,缺 `graded`;pending-features 要求 `HomeworkGraded` 事件) -3. ❌ 成绩录入无业务校验(不校验 exam/homework 是否存在、score 是否在 totalScore 范围内、是否重复录入) -4. ❌ 作业提交高并发优化缺失(004 §9.2 要求 Redis 分布式锁 + 排队) -5. ❌ 无 `grade.updated` / `homework.graded` 事件触发点(proto 已定义,service 未实现) -6. ❌ 未消费 IAM `user.created` 事件(初始化教师默认关联) -7. ⚠️ Drizzle `db` 直接导出 vs classes 的 `getDb()` 函数式 — **不一致**,建议统一为 `getDb()` -8. ⚠️ [kafka.ts](../../services/core-edu/src/config/kafka.ts) 用 `console.log`/`console.warn`,应改用结构化 logger -9. ⚠️ classes 模块在 core-edu 仅有 `classes.module.ts` 占位,**P3 待合并**(classes 服务代码迁入 + 删除独立 services/classes) -10. ⚠️ 入口仍为 REST,proto gRPC 契约已定义但未接入 `@grpc/grpc-js` + buf generate 代码 -11. ❌ 无 Zod 输入验证(Controller 直接接收 `body: CreateExamInput`,未走 zod schema) -12. ❌ 无测试 - -### 跨模块契约对齐待确认项(提请 coord 交叉审查) - -| 待确认项 | 我方期望 | 对方模块 | 状态 | -| ---------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------- | -| iam `getEffectivePermissions(userId)` 返回结构 | `{permissions, viewports, dataScope}` | iam(ai02) | ⚠️ 当前 teacher-bff 调 `/iam/viewports` 与 `/iam/me`,未定义此聚合 API 的 proto | -| iam `user.created` 事件 topic | `edu.identity.user.created` | iam(ai02) | ⚠️ 004 §7.2 定义,但 core-edu 未消费,需确认 iam 是否发布 | -| core-edu 端口 3004 | 不冲突 | 全局端口矩阵 | 待 coord 核对 | -| Kafka topic 命名 | `edu.exam.events` / `edu.homework.events` / `edu.grade.events` / `edu.class.events` | 004 §7.2 用 `edu.teaching.*` 前缀 | ⚠️ **不一致**:004 文档用 `edu.teaching.exam.published`,代码用 `edu.exam.events`,需 coord 仲裁统一 | -| data-ana 消费 core-edu 事件 | 消费 `exam.created` / `homework.submitted` / `grade.recorded` | data-ana(ai06) | 待 ai06 确认消费契约 | -| msg 消费 core-edu 事件 | 消费 `exam.created` / `homework.assigned` / `grade.recorded` 触发通知 | msg(ai05) | 待 ai05 确认消费契约 | - ---- - -## 下一步(阶段 2 入口) - -待 coord 审核本确认书通过后,ai03 进入阶段 2,按 [ai-allocation.md §5 ai03 设计重点](./ai-allocation.md#ai03--教学场景域) 产出两份模块架构设计文档: - -1. **teacher-bff 模块架构设计**:GraphQL schema(Query/Mutation 按场景域组织)、DataLoader 批量去重、并行 gRPC 编排、聚合缓存 TTL、教师角色差异化视口推导 -2. **core-edu 模块架构设计**:classes 黄金模板对齐、考试生命周期状态机、Outbox 事件定义、成绩计算配置化、作业提交高并发(Redis 锁 + 排队)、排课/考勤数据模型 - -阶段 2 设计需先解决上述 12 项差距与 6 项跨模块契约对齐。 - ---- - -**AI Agent**: ai03 (teacher-bff + core-edu) -**Coordinator**: coord-ai -**Branch**: 单仓库并行模式(直接 push main) diff --git a/docs/architecture/ai05-phase1-understanding.md b/docs/architecture/ai05-phase1-understanding.md deleted file mode 100644 index 33d2287..0000000 --- a/docs/architecture/ai05-phase1-understanding.md +++ /dev/null @@ -1,269 +0,0 @@ -# ai05 阶段 1 交付物:模块理解确认书 - -> AI 标识:ai05 -> 负责模块:content(P4)、msg(P5) -> 阶段:架构设计外包 · 阶段 1(全局理解) -> 日期:2026-07-09 -> 关联文档:[ai-allocation.md](./ai-allocation.md)、[004 架构影响地图](./004_architecture_impact_map.md)、[pending-features.md](./roadmap/pending-features.md)、[known-issues.md](../troubleshooting/known-issues.md) - ---- - -## 模块理解确认书 — content - -### 1. 我在架构中的位置 - -- **层级**:业务微服务层(L5),DDD 限界上下文 -- **业务领域**:D4 内容资源领域(004 §1.1b) -- **上游**(谁调用我): - - teacher-bff(3003):教学场景聚合,教师查教材/知识点/题库 - - student-bff(学习场景,P3 起):学生查学习路径、知识点前置 - - ai(Python,P5):gRPC 查询知识点/题库用于 AI 出题(004 §4.1 `AI -.gRPC.-> Content`,§9.3 AI 辅助出题流程) -- **下游**(我调用谁): - - MySQL(写模型主库,独占) - - Neo4j(知识图谱,前置依赖图) - - Elasticsearch(题库全文检索,P4 后续补充,当前未实现) -- **通信方式**: - - 当前:HTTP REST(Controller,无 gRPC controller) - - 目标态(004 §4.1 / pending-features P4):gRPC 暴露 `TextbookService` + `KnowledgeGraphService` - - Kafka:消费 core-edu 教学内容变更通知(004 §4.1 `CoreEdu -.事件.-> Content`);发布 `edu.content.question.published`(004 §7.2) -- **端口**:3005(见 [content env.ts](../../services/content/src/config/env.ts)) - -### 2. 我的限界上下文 - -- **聚合职责**:管理 Textbook(教材)、Chapter(章节)、KnowledgePoint(知识点)、Question(题库)四个聚合 -- **我的数据属于**:D4 内容资源领域 -- **我不负责**: - - 不负责学情分析(D6,由 data-ana 承载) - - 不负责考试/作业/成绩(D3,由 core-edu 承载) - - 不负责通知分发(D5,由 msg 承载) - - 不直接访问 core-edu / iam / msg 的数据库 -- **现有骨架领域模块**(4 个,见 [content/src](../../services/content/src)): - - `textbooks/`:教材 CRUD(5 端点) - - `chapters/`:章节 CRUD(5 端点,按 textbook 查询) - - `knowledge-points/`:知识点 CRUD + Neo4j 知识图谱(7 端点,含前置链路查询/添加) - - `questions/`:题库 CRUD(5 端点,4 种题型校验) - -### 3. 我与外部的契约 - -- **消费的 proto message**(从 shared-proto): - - 无直接消费其他服务 proto;通过 Kafka 事件接收 core-edu 教学内容变更(事件契约见 `events.proto`) -- **暴露的契约**(见 [content.proto](../../packages/shared-proto/proto/content.proto),包名 `next_edu_cloud.content.v1`): - - `TextbookService`:CreateTextbook / GetTextbook / ListTextbooks - - `KnowledgeGraphService`:GetPrerequisites / GetLearningPath - - **当前实现均为 REST,gRPC controller 未实现**(proto 已定义待迁移) -- **事件契约**: - - 发布:`edu.content.question.published`(题库新增/发布,消费者 AI、ES,见 004 §7.2) - - 消费:core-edu 教学内容变更事件(004 §4.1,具体 topic 待 core-edu ai03 设计确认) -- **错误码前缀**:`CONTENT_*`(CONTENT_VALIDATION_ERROR / NOT_FOUND / PERMISSION_DENIED / CONFLICT / BUSINESS_ERROR / DATABASE_ERROR / INTERNAL_ERROR,见 [application-error.ts](../../services/content/src/shared/errors/application-error.ts)) -- **权限点**(16 个,见 [permission.guard.ts](../../services/content/src/middleware/permission.guard.ts)): - - `CONTENT_TEXTBOOK_{CREATE,READ,UPDATE,DELETE}` - - `CONTENT_CHAPTER_{CREATE,READ,UPDATE,DELETE}` - - `CONTENT_QUESTION_{CREATE,READ,UPDATE,DELETE}` - - `CONTENT_KNOWLEDGE_POINT_{CREATE,READ,UPDATE,DELETE}` - -### 4. 我的技术栈 - -- 语言:TypeScript 5.6(ESM 模式,相对 import 带 `.js` 后缀) -- 框架:NestJS 10 -- ORM:Drizzle ORM 0.31 + mysql2 3.11 -- 知识图谱:neo4j-driver 5.23(PREREQUISITE_OF 关系,Cypher 查询深度 1..5) -- 全文检索:Elasticsearch(**待引入**,env.ts 预留 ES_URL 但 package.json 未装 @elastic/elasticsearch) -- 可观测:pino logger + prom-client metrics + OpenTelemetry tracer(三支柱已具备) -- 消息总线:Kafka(**待引入**,pending-features P4 未明确要求 content 发事件,但 004 §7.2 列了 `edu.content.question.published`) - -### 5. 我的阶段归属 - -- **P4 内容分析阶段**(pending-features §P4): - - 教材/章节/知识点 CRUD(仅 CRUD,不实现检索)+ 知识图谱查询(Neo4j)+ 题库 CRUD(不实现检索) - - MySQL schema:textbooks / chapters / knowledge_points / questions - - Neo4j 数据:知识点前置依赖图(从 MySQL 同步) - - CDC 链路:Debezium 监听 MySQL binlog → Kafka → data-ana 消费写 ClickHouse -- **退出标准**:教师查看知识图谱前置依赖(Neo4j 秒级返回)→ 学生查看学情诊断(ClickHouse 宽表 5s 内返回)→ CDC 链路延迟 < 5s -- **依赖上游**:P1 黄金模板 classes(横切关注点对齐)、P3 core-edu(教学内容变更事件,待 ai03 设计确认 topic) - -### 6. 我需要对齐的黄金模板项(对照 classes 服务) - -- [x] 权限装饰器 `@RequirePermission`(16 个 CONTENT_* 权限点,全部 Controller 方法已覆盖) -- [x] 错误码前缀统一(`CONTENT_*`) -- [x] logger / metrics / tracer 三支柱(已具备) -- [x] `/healthz` 健康检查(HealthModule 已注册) -- [⚠️] `/readyz`(**仅检查 DB `SELECT 1`,未检查 Neo4j 连通性**,Neo4j 故障时仍返回 ok) -- [x] 优雅关闭 SIGTERM(main.ts 已处理:closeNeo4j → closeDb → shutdownTracer) -- [ ] 测试覆盖率 ≥ 80%(**当前 0%**,无测试文件) -- [x] Dockerfile 多阶段构建(已具备) -- [⚠️] Zod 输入验证(questions 用 service 层手动 if 校验抛 ValidationError,**非 Controller 层 schema.parse**) -- [x] GlobalErrorFilter 统一兜底 - -### 7. 现有骨架差距与待决策(提请 coord 仲裁) - -| # | 差距 | 影响 | 提请决策 | -| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| C1 | **ES 完全未实现**:env.ts 预留 ES_URL 但无 @elastic/elasticsearch 依赖、无 config/elasticsearch.ts、无 service 调用 | P4 退出标准要求题库检索,pending-features 注明"P4 仅 CRUD,P5 引入 ES" | 确认 content 的 ES 检索归属 P4 还是 P5(004 §2.2 列 ES 使用者为 Content、AI) | -| C2 | **无 Outbox / Kafka**:骨架无 shared/outbox/,无 Kafka producer/consumer | 004 §7.2 列 `edu.content.question.published` 事件需发布 | 确认 content 是否需 Outbox(iam 无 Outbox,core-edu 有) | -| C3 | **README 与实现脱节**:README 声称 `TextbooksService.createKnowledgeGraph` 和 `getPrerequisites`,源码中 createKnowledgeGraph 不存在,getPrerequisites 在 KnowledgePointsService | 文档误导 | 阶段 2 设计需同步修正 README | -| C4 | **gRPC 未实现**:proto 定义了 TextbookService/KnowledgeGraphService,但无 gRPC controller | pending-features P4 未强制 gRPC,004 §4.1 目标态 gRPC | 确认 P4 是否启用 gRPC(ai03 提请统一决策) | -| C5 | **schema 定义分散**:textbooks.schema.ts 定义 3 张表(textbooks/chapters/knowledgePoints),chapters/knowledge-points schema 仅 re-export;questions.schema.ts 独立 | 维护成本 | 阶段 2 设计统一 schema 归属 | -| C6 | **DB 连接模式与 iam 不一致**:content 用模块级 `const db`,iam 用 `getDb()` 函数式懒加载 | 测试 mock 困难 | coord 已在 known-issues 记录"db 常量导出对齐黄金模板",需确认统一方向 | -| C7 | **表时间戳不统一**:textbooks/questions 有 created_at+updated_at,chapters/knowledge_points 无时间戳 | 审计追踪缺失 | 阶段 2 设计补齐 | -| C8 | **无外键约束**:questions.knowledge_point_id → knowledge_points.id 等无 Drizzle 外键 | 引用完整性靠应用层 | 阶段 2 设计评估是否加外键 | -| C9 | **addPrerequisite 降级策略不一致**:safeCreateNode 非阻塞(Neo4j 失败仅 warn),addPrerequisite 在 Neo4j 不可用时抛 InternalError | 行为不一致 | 阶段 2 设计统一降级策略 | -| C10 | **proto 包名**:实际 `next_edu_cloud.content.v1`,project_rules §5 规定 `edu.content.v1` | 命名规范不一致 | **coord 仲裁**(ai03 已提请) | - ---- - -## 模块理解确认书 — msg - -### 1. 我在架构中的位置 - -- **层级**:业务微服务层(L5),DDD 限界上下文 -- **业务领域**:D5 沟通通知领域(004 §1.1b) -- **上游**(谁调用我): - - teacher-bff / student-bff / parent-bff:聚合层调 msg 查询用户通知列表、发送通知 - - api-gateway:REST 转发通知请求 - - Kafka:消费 core-edu / iam 事件触发通知(004 §4.1 `CoreEdu -.事件.-> Msg`、`IAM -.事件.-> Msg`) -- **下游**(我调用谁): - - MySQL(写模型主库,独占) - - Elasticsearch(全文检索,已实现 safeSearch) - - push-gateway(gRPC 推送通道,004 §4.1 `PushGW → Msg`,当前用 fetch POST /internal/push 降级) -- **通信方式**: - - 当前:HTTP REST(Controller,无 gRPC controller) - - 目标态(004 §4.1 / pending-features P5):gRPC 暴露 `NotificationService` - - Kafka:消费 core-edu(ExamPublished/HomeworkGraded/GradeRecorded)、iam(UserRegistered)事件(004 §7.3) -- **端口**:3007(见 [msg env.ts](../../services/msg/src/config/env.ts)) - -### 2. 我的限界上下文 - -- **聚合职责**:管理 Notification(通知)聚合 + NotificationPreference(用户通知偏好) -- **我的数据属于**:D5 沟通通知领域 -- **我不负责**: - - 不负责 WebSocket 长连接管理(由 push-gateway 承载) - - 不负责业务数据变更(仅消费事件触发通知) - - 不直接访问 core-edu / iam 的数据库 -- **现有骨架领域模块**(1 个,见 [msg/src](../../services/msg/src)): - - `notifications/`:通知 CRUD + ES 全文检索 + Push Gateway 推送 + 用户偏好(6 端点) - -### 3. 我与外部的契约 - -- **消费的 proto message**(从 shared-proto): - - 消费 `events.proto` 的事件 message(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent) - - **events.proto 当前无 NotificationEvent**,若 msg 发事件需补充 -- **暴露的契约**(见 [msg.proto](../../packages/shared-proto/proto/msg.proto),包名 `next_edu_cloud.msg.v1`): - - `NotificationService`:SendNotification / ListNotifications / MarkAsRead / SearchNotifications - - **当前实现均为 REST,gRPC controller 未实现** -- **事件契约**: - - 消费(004 §7.2 / §7.3): - - `edu.identity.user.created` / `edu.identity.user.updated`(IAM 发,msg 发欢迎通知) - - `edu.teaching.exam.published`(core-edu 发,msg 推送考试通知给学生) - - `edu.teaching.assignment.submitted`(core-edu 发,msg 通知教师) - - `edu.teaching.grade.recorded`(core-edu 发,msg 通知学生) - - `edu.insight.mastery.updated`(data-ana 发,msg 触发预警) - - 发布:无明确(pending-features 未要求 msg 发事件) -- **错误码前缀**:`MSG_*`(MSG_VALIDATION_ERROR / NOT_FOUND / PERMISSION_DENIED / CONFLICT / BUSINESS_ERROR / DATABASE_ERROR / INTERNAL_ERROR,见 [application-error.ts](../../services/msg/src/shared/errors/application-error.ts)) -- **权限点**(3 个,见 [permission.guard.ts](../../services/msg/src/middleware/permission.guard.ts)): - - `MSG_NOTIFICATION_SEND`、`MSG_NOTIFICATION_READ`、`MSG_NOTIFICATION_MANAGE` - -### 4. 我的技术栈 - -- 语言:TypeScript 5.6(ESM 模式) -- 框架:NestJS 10 -- ORM:Drizzle ORM 0.31 + mysql2 3.11 -- 全文检索:@elastic/elasticsearch 8.15(已实现 safeIndex/safeSearch,**无 mapping 定义**,依赖动态 mapping) -- 消息总线:kafkajs 2.2(**已装依赖但无 consumer/producer 代码**,env.ts 有 KAFKA_BROKERS 默认值) -- 推送:fetch POST 到 push-gateway `/internal/push`(降级模式,PUSH_GATEWAY_URL 未配置或失败时跳过) -- 幂等去重:Redis(**env.ts 预留 REDIS_URL 但无 redis 客户端依赖**,pending-features P5 要求 event_id 去重) -- 可观测:pino + prom-client + OpenTelemetry(三支柱已具备) - -### 5. 我的阶段归属 - -- **P5 沟通与 AI 阶段**(pending-features §P5): - - 会话/消息 CRUD + 调 Push Gateway 推送 + 通知偏好 - - 多渠道(站内/SMS/邮件/微信),沿用旧项目 dispatcher 模式 - - Elasticsearch 题库全文检索(从 MySQL 同步) -- **退出标准**:教师发广播通知 → 全在线学生实时收到(Push Gateway)→ AI 辅助出题流式返回 → 题库全文检索 < 200ms -- **依赖上游**:P1 黄金模板 classes、P3 core-edu(事件来源)、P5 push-gateway(推送通道,ai01 设计)、P5 ai(无直接依赖) - -### 6. 我需要对齐的黄金模板项(对照 classes 服务) - -- [x] 权限装饰器 `@RequirePermission`(3 个 MSG_* 权限点,全部 Controller 方法已覆盖) -- [x] 错误码前缀统一(`MSG_*`) -- [x] logger / metrics / tracer 三支柱(已具备) -- [x] `/healthz` 健康检查(HealthModule 已注册) -- [⚠️] `/readyz`(**仅检查 DB `SELECT 1`,未检查 ES 连通性**,ES 故障时仍返回 ok) -- [x] 优雅关闭 SIGTERM(main.ts 已处理:app.close → closeEs → closeDb → shutdownTracer) -- [ ] 测试覆盖率 ≥ 80%(**当前 0%**,无测试文件) -- [x] Dockerfile 多阶段构建(已具备) -- [⚠️] Zod 输入验证(Controller 用 `schema.parse(body)`,**但 ZodError 未在 GlobalErrorFilter 特殊处理,走默认 500 而非 400**) -- [x] GlobalErrorFilter 统一兜底 - -### 7. 现有骨架差距与待决策(提请 coord 仲裁) - -| # | 差距 | 影响 | 提请决策 | -| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -| M1 | **Kafka 消费未实现**:装了 kafkajs 但无 consumer 代码,无 shared/kafka/ 目录 | 004 §7.3 列 msg 消费 5 类事件(ExamPublished/HomeworkGraded/GradeRecorded/UserRegistered/MasteryUpdated) | 阶段 2 设计需补 Kafka consumer + 幂等去重 | -| M2 | **无 Outbox**:骨架无 shared/outbox/ | msg 作为通知中心,发送通知后可能需发事件(如 NotificationSent) | 确认 msg 是否需 Outbox(iam 无,core-edu 有) | -| M3 | **ES 无 mapping 定义**:依赖动态 mapping,索引名 "notifications" 硬编码在 service | 检索质量不稳定,索引管理缺失 | 阶段 2 设计补 mapping + ensureIndex | -| M4 | **无独立 repository**:notifications.service.ts 直接用 db,无 repository 抽象 | 与黄金模板分层不一致(iam 有 repository) | 阶段 2 设计补 repository 层 | -| M5 | **Redis 未引入**:env.ts 预留 REDIS_URL 但无 redis 客户端依赖 | pending-features P5 要求 event_id 去重(Redis SETNX 或 DB 唯一索引) | 阶段 2 设计决策:Redis SETNX vs DB 唯一索引 | -| M6 | **createBatch 无事务/无批量优化**:for 循环串行调 send,无批量 INSERT | 性能瓶颈(广播场景) | 阶段 2 设计改为批量 INSERT | -| M7 | **NotificationsModule 缺 exports**:notifications.module.ts 无 `exports: [NotificationsService]` | 未来 BFF 注入受阻 | 阶段 2 设计补 exports | -| M8 | **ZodError 未特殊处理**:GlobalErrorFilter 未识别 ZodError,走默认 500 | 输入校验错误返回码错误(500 而非 400) | 阶段 2 设计 GlobalErrorFilter 补 ZodError 分支(iam 已有可参考) | -| M9 | **gRPC 未实现**:proto 定义了 NotificationService,但无 gRPC controller | pending-features P5 未强制 gRPC | 确认 P5 是否启用 gRPC(ai03 提请统一决策) | -| M10 | **README 与实现脱节**:README API 表标 `POST /notifications/:id/read`,实际是 PUT;漏 batch/user/:userId/user/:userId/page 端点;声称"消费 Kafka 事件"但无代码 | 文档误导 | 阶段 2 设计同步修正 README | -| M11 | **main.ts 与 LifecycleService 重复关闭资源**:两者都调 closeDb/closeEs | 重复关闭可能报错(虽有 try-catch) | 阶段 2 设计统一关闭逻辑到 LifecycleService | -| M12 | **proto 包名**:实际 `next_edu_cloud.msg.v1`,project_rules §5 规定 `edu.msg.v1` | 命名规范不一致 | **coord 仲裁**(ai03 已提请) | - ---- - -## 三、服务审计表 — ai05 - -> 审计标准对照 [project_rules §3](../../.trae/rules/project_rules.md) 与 [known-issues §2.2 classes 黄金模板](../troubleshooting/known-issues.md) - -| 服务 | 权限装饰器 | 错误码前缀 | logger | metrics | tracer | /healthz | /readyz | 优雅关闭 | 测试覆盖率 | Dockerfile | -| ------- | ---------------- | -------------- | ------- | -------------- | ------------------------------- | -------- | ------------------- | --------------------- | ---------- | ---------- | -| content | ✅ 16 端点全覆盖 | ✅ `CONTENT_*` | ✅ pino | ✅ prom-client | ✅ OTel + auto-instrumentations | ✅ | ⚠️ 仅 DB 不查 Neo4j | ✅ closeNeo4j→closeDb | 0% | ✅ | -| msg | ✅ 6 端点全覆盖 | ✅ `MSG_*` | ✅ pino | ✅ prom-client | ✅ OTel + auto-instrumentations | ✅ | ⚠️ 仅 DB 不查 ES | ✅ closeEs→closeDb | 0% | ✅ | - ---- - -## 四、跨模块契约对齐提请(coord 交叉审查) - -### 4.1 接口一致性检查 - -| 本服务声明 | 对方服务声明 | 是否匹配 | 备注 | -| --------------------------------------------------------- | ------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------ | -| ai05 content: 暴露 KnowledgeGraphService.GetPrerequisites | ai06 ai: 调 content 查知识点(004 §9.3) | ⚠️ 待确认 | ai06 设计文档需确认调用签名 | -| ai05 content: 暴露 TextbookService.ListTextbooks | ai03 teacher-bff: 聚合 content 查教材(004 §4.1) | ⚠️ 待确认 | ai03 设计文档需确认调用 | -| ai05 content: 发布 `edu.content.question.published` | ai06 ai: 消费题库事件(004 §7.2 消费者 AI) | ⚠️ 待确认 | ai06 设计需确认是否消费 | -| ai05 msg: 消费 `edu.teaching.exam.published` | ai03 core-edu: 发布考试事件 | ⚠️ 待确认 | ai03 提请 topic 命名统一(004 `edu.teaching.exam.published` vs core-edu 代码 `edu.exam.events`) | -| ai05 msg: 消费 `edu.identity.user.created` | ai02 iam: 发布用户创建事件 | ⚠️ 待确认 | ai02 设计需确认 topic | -| ai05 msg: 调 push-gateway `/internal/push` | ai01 push-gateway: 暴露推送端点 | ✅ 已实现 | msg 当前用 fetch POST,push-gateway 已有 /internal/push 端点 | - -### 4.2 全局冲突检查 - -| 检查项 | 检查结果 | 备注 | -| -------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| 端口不冲突 | ✅ content 3005、msg 3007 | 与 iam(3002)/classes(3001)/teacher-bff(3003)/core-edu(3004)/data-ana(3006)/ai(3008) 不冲突 | -| Topic 不重复 | ⚠️ 待汇总 | content 发布 `edu.content.question.published`;msg 仅消费不发布 | -| 错误码前缀不重叠 | ✅ `CONTENT_*` / `MSG_*` 唯一 | 与 iam `IAM_*` / core-edu `CORE_EDU_*` 不重叠 | -| Proto message 不遗漏 | ⚠️ 待确认 | events.proto 无 NotificationEvent,若 msg 发事件需补充;content.proto 缺 Update/Delete/分页(对比 classes.proto 有 page_token) | -| proto 包名规范 | ⚠️ 不一致 | 实际 `next_edu_cloud..v1`,规则要求 `edu..v1`,**提请 coord 仲裁**(ai03 已提请) | - -### 4.3 待 coord 仲裁的决策项 - -1. **proto 包名统一**:`next_edu_cloud.*` vs `edu.*`(影响全部 proto,需 coord 决策) -2. **gRPC 启用时机**:P4/P5 是否启用 gRPC controller,还是继续 REST(影响 content/msg/iam/core-edu 全部服务) -3. **content ES 检索归属**:P4(pending-features 注明"P4 仅 CRUD,P5 引入 ES")vs 004 §2.2(列 ES 使用者 Content、AI)—— 需明确 content 何时引入 ES -4. **content Outbox**:是否需要(004 §7.2 列 content 发事件,但 pending-features P4 未要求) -5. **msg Outbox**:是否需要(msg 仅消费事件触发通知,是否需发 NotificationSent 事件) -6. **msg Redis 引入**:env.ts 预留 REDIS_URL 但无依赖,幂等去重用 Redis SETNX 还是 DB 唯一索引 -7. **events.proto 补充**:若 msg 发事件需追加 NotificationEvent message - ---- - -## 五、下一步 - -1. **等待 coord 审核本确认书**(§4 跨模块契约对齐 + §4.3 仲裁项) -2. coord 放行后进入**阶段 2:模块架构设计文档**,按 ai-allocation.md §7 模板产出: - - content 模块架构设计文档(含 Neo4j 图模型、ES 索引 mapping、题库 CRUD API、与 ai 的 gRPC 接口、教材/章节结构树) - - msg 模块架构设计文档(含通知渠道抽象策略模式、ES 降级查询、Kafka 消费幂等、push-gateway 推送协议、通知模板、已读/未读状态管理) -3. 阶段 2 设计完成后同步更新 README(修正 §C3/M10 文档脱节问题) diff --git a/docs/architecture/ai06-phase1-understanding.md b/docs/architecture/ai06-phase1-understanding.md deleted file mode 100644 index ad7e118..0000000 --- a/docs/architecture/ai06-phase1-understanding.md +++ /dev/null @@ -1,339 +0,0 @@ -# ai06 阶段 1 交付物:模块理解确认书 - -> AI 标识:ai06 -> 负责模块:data-ana(P4)、ai(P5) -> 语言:Python 3.12+ / FastAPI 0.115+ -> 阶段:架构设计外包 · 阶段 1(全局理解) -> 日期:2026-07-09 -> 关联文档:[ai-allocation.md](./ai-allocation.md)、[004 架构影响地图](./004_architecture_impact_map.md)、[pending-features.md](./roadmap/pending-features.md)、[known-issues.md](../troubleshooting/known-issues.md) - ---- - -## 阶段 1 必读清单完成确认 - -按 ai-allocation.md §4,ai06 必读 7 份全局文档 + 全部 `.proto` + `services/data-ana/`、`services/ai/` 骨架源码: - -| # | 文档 | 状态 | -| --- | ------------------------------------------------------------------- | ------------------------------ | -| 1 | [README.md](../../README.md) | ✅ 已读 | -| 2 | [MIGRATION_GUIDE.md](../../MIGRATION_GUIDE.md) | ✅ 已读 | -| 3 | [004 架构影响地图](./004_architecture_impact_map.md) | ✅ 已读 | -| 4 | [pending-features.md](./roadmap/pending-features.md) | ✅ 已读 | -| 5 | [project_rules.md](../../.trae/rules/project_rules.md) | ✅ 已读(workspace 规则) | -| 6 | [coding-standards.md](../standards/coding-standards.md) | ✅ 已读(重点 §4 Python 规范) | -| 7 | [multi-ai-collaboration.md](../standards/multi-ai-collaboration.md) | ✅ 已读 | -| 8 | `packages/shared-proto/proto/*.proto`(8 份) | ✅ 已读 | -| 9 | `services/data-ana/src/`、`services/ai/src/` 全部源码 | ✅ 已读 | - -> ai06 不需要读 classes 黄金模板(ai-allocation.md §4 矩阵 ai06 列对黄金模板行为 "—"),但审计表横切关注点对齐仍参考 classes 标准。 - ---- - -## 模块理解确认书 — data-ana - -### 1. 我在架构中的位置 - -- **层级**:业务微服务层(L5),属 **D6 智能洞察领域**(004 §1.1b) -- **上游**: - - api-gateway 直接 HTTP 代理 `/api/v1/analytics/*` → data-ana:3006(见 [api-gateway main.go:142-148](../../services/api-gateway/main.go)) - - teacher-bff / student-bff 聚合查询(004 §4.1:BFF → 业务服务 gRPC;当前为 REST) -- **下游**: - - ClickHouse(独占读模型,宽表 `student_dashboard_view` / `student_errors`) - - Kafka(消费 Debezium CDC 事件 + 待发布 `edu.insight.mastery.updated` 领域事件) -- **通信方式**: - - 入口:当前 REST(`/analytics/class/{id}/performance`、`/analytics/student/{id}/weakness`、`/analytics/student/{id}/errorbook`);目标态(004 §4.1 + analytics.proto)转 **gRPC** 暴露 `AnalyticsService` - - 出口:Kafka 消费(CDC + 领域事件订阅);Kafka 发布(`edu.insight.mastery.updated`,**未实现**) -- **端口**:3006(见 [data-ana config.py:7](../../services/data-ana/src/data_ana/config.py) + [api-gateway config.go:55](../../services/api-gateway/internal/config/config.go)) - -### 2. 我的限界上下文 - -- **聚合职责**:学情分析读模型构建 + 查询服务,承载 **D6 智能洞察领域**的"分析/诊断"子域 -- **聚合根 / 实体**: - - `StudentDashboard`(学生学情宽表视图,ClickHouse 物化) - - `StudentErrorBook`(学生错题本,ClickHouse 物化) - - `ClassPerformance`(班级成绩聚合,ClickHouse 即时聚合) - - `MasterySnapshot`(知识点掌握度快照,**待实现**) -- **业务领域**:D6 智能洞察(与 ai 服务共享领域,ai 偏"生成",data-ana 偏"分析") -- **我不负责**: - - 不负责写模型(成绩由 core-edu 写 MySQL,data-ana 只消费 CDC) - - 不负责题库内容(→ content 服务) - - 不负责通知投递(→ msg 服务消费 data-ana 发布的 `mastery.updated` 事件触发预警) - - 不负责 AI 推理(→ ai 服务,ai 通过 gRPC 反向查询 data-ana 学情数据) -- **数据自治**:独占 `edu_analytics` ClickHouse 数据库(不与 MySQL 写模型混用) - -### 3. 我与外部的契约 - -#### 消费的 proto message - -| 来源 | message | 用途 | -| --------------- | ------------------------------- | ---------------------------------------------- | -| events.proto | `GradeEvent` | 消费 core-edu 成绩写入事件 → 更新学情宽表 | -| events.proto | `ExamEvent` | 消费考试事件 → 缓存 exam_id→class_id 映射 | -| events.proto | `HomeworkEvent` | 消费作业提交/批改事件 → 更新学情(**待实现**) | -| events.proto | `ClassEvent` | 消费班级变更事件 → 同步班级维度(**待实现**) | -| analytics.proto | `GetClassPerformanceRequest` 等 | gRPC 暴露契约(**待实现 gRPC server**) | - -> **CDC 直连 vs 领域事件双通道**:当前实现走 Debezium CDC(直接监听 MySQL binlog),不依赖 core-edu 的 Outbox。这是 ADR-008 的设计决策(CDC 解耦 Outbox Relay,减少业务侵入)。Outbox 领域事件(events.proto)作为业务语义更清晰的补充通道,待 P4 后期评估是否双消费。 - -#### 暴露的 API / 事件 - -**HTTP 端点**(当前实现,见 [main.py](../../services/data-ana/src/data_ana/main.py)): - -| method | path | 说明 | -| ------ | ------------------------------------------- | -------------------------------------- | -| GET | `/healthz` | liveness | -| GET | `/readyz` | readiness(含 ClickHouse + CDC 状态) | -| GET | `/metrics` | Prometheus 指标 | -| GET | `/analytics/class/{class_id}/performance` | 班级成绩分析(平均分/及格率/参考人数) | -| GET | `/analytics/student/{student_id}/weakness` | 学生薄弱知识点(mastery < 0.6) | -| GET | `/analytics/student/{student_id}/errorbook` | 学生错题本 | - -**gRPC 契约**(analytics.proto,**待实现**): - -- `GetClassPerformance(GetClassPerformanceRequest) → ClassPerformance` -- `GetStudentWeakness(GetStudentWeaknessRequest) → StudentWeakness` -- `GetLearningTrend(GetLearningTrendRequest) → LearningTrend`(**当前 HTTP 未暴露**) - -**发布的领域事件**: - -| 事件 | Topic(004 §7.2) | 触发时机 | 消费者(004 §7.3) | -| ---------------- | ----------------------------- | -------------- | --------------------------------------- | -| `MasteryUpdated` | `edu.insight.mastery.updated` | 掌握度计算完成 | core-edu(推荐个性化练习)、msg(预警) | - -> **当前未实现发布**:data-ana 当前只消费不发布。掌握度计算完成后应通过 Kafka 发布 `MasteryUpdated`,下游 core-edu / msg 消费。阶段 2 设计需补全此发布链路(Python 无 Outbox 模式,需评估直接 producer 还是引入 Outbox 表)。 - -- **错误码前缀**:`DATA_ANA_*`(待定义清单,见阶段 2 §6) -- **缓存**:当前无 Redis 缓存;004 §6.3 学情宽表走 ClickHouse 实时,CDC 同步延迟 < 5s - -### 4. 我的技术栈 - -- 语言:Python 3.12+ -- 框架:FastAPI 0.115+ / uvicorn -- ORM / 客户端:`clickhouse-connect`(HTTP 协议,非原生协议) -- 消息:`aiokafka`(CDC 消费者,AIOKafkaConsumer) -- 配置:`pydantic-settings` BaseSettings(env_prefix="",全大写环境变量) -- 可观测: - - 日志:`structlog` 24.x(`make_filtering_bound_logger`,**注意**:旧版 `make_filtering_logger` 已废弃) - - 指标:`prometheus-client` + `make_asgi_app()` 挂载 `/metrics` - - 链路:`opentelemetry-sdk` + `OTLPSpanExporter` + `FastAPIInstrumentor.instrument_app(app)` -- 序列化:JSON(Debezium 事件 `schemas.enable=false`,直接 `json.loads`) -- 测试:pytest + pytest-asyncio(**当前 0% 覆盖率**) - -### 5. 我的阶段归属 - -- **P4 内容分析阶段(M11-M13)**:建 DataAna 服务 + CDC 链路落地 -- **退出标准**(pending-features P4):学生查看学情诊断 ClickHouse 宽表 5s 内返回 + CDC 链路延迟 < 5s -- **依赖上游**: - - P1 地基:api-gateway 路由 + arch.db 扫描器 Python 支持 - - P3 核心教学:core-edu 写成绩到 MySQL(Debezium 监听 binlog) - - P4 同期:content 服务(提供知识点 ID 供掌握度计算) -- **下游依赖我**: - - P5 ai 服务通过 gRPC 查询学情数据(004 §4.1:AI → DataAna gRPC) - - P5 msg 服务消费 `mastery.updated` 触发预警 - -### 6. 我需要对齐的黄金模板项(对照 classes 服务 + Python 规范) - -> Python 服务无 NestJS 装饰器体系,权限校验等通过等价方式实现。 - -- [ ] 权限装饰器等价物:**当前 HTTP 端点全部裸露,无权限校验**。Gateway 层做 JWT 校验,但 data-ana 本身未校验 `x-user-id` / DataScope。**阶段 2 需设计 FastAPI Depends 权限依赖 + DataScope 过滤注入** -- [ ] 错误码前缀统一:**当前无错误码体系**,降级时返回 `degraded: true` 标记但无业务错误码。**阶段 2 需定义 `DATA_ANA_*` 错误码清单** -- [x] logger / metrics / tracer 三支柱(已具备,见 main.py + clickhouse_client.py) -- [x] `/healthz` + `/readyz` 健康检查(已具备,readyz 含 ClickHouse ping + CDC 状态) -- [ ] 优雅关闭 SIGTERM:当前 lifespan 仅关闭 CDC task + ClickHouse client,**未注册 SIGTERM 信号处理器**显式 drain -- [ ] 测试覆盖率 ≥ 80%:**当前 0%**,无 tests/ 目录 -- [ ] Dockerfile 多阶段构建:**当前单阶段**(`FROM python:3.12-slim` → `uv sync` → `COPY src`),非多阶段 -- [ ] Pydantic 输入验证:**当前端点直接接收 path param,无 Pydantic 请求模型校验**(应补 `ClassPerformanceResponse` 等 response_model) -- [x] 配置通过 pydantic-settings 管理(已具备) -- [x] 异步优先(async def,aiokafka async consumer) -- [ ] 类型注解强制:**部分函数缺返回值标注**(如 `init_logger` 返回 `BoundLogger` 但 `_logger` 全局变量标注 `None`,需统一) -- [ ] ruff check 零错误:需阶段 2 验证 - ---- - -## 模块理解确认书 — ai - -### 1. 我在架构中的位置 - -- **层级**:业务微服务层(L5),属 **D6 智能洞察领域**(004 §1.1b,与 data-ana 共享领域) -- **上游**: - - api-gateway 直接 HTTP 代理 `/api/v1/ai/*` → ai:3008(见 [api-gateway main.go:138-139](../../services/api-gateway/main.go)) - - teacher-bff 聚合 AI 出题/优化能力(004 §4.1:BFF → ai,当前 REST) -- **下游**: - - content 服务(gRPC 查询知识点 / 题库,004 §4.1:AI → Content gRPC) - - data-ana 服务(gRPC 查询学情数据,004 §4.1:AI → DataAna gRPC) - - LLM Provider(外部 HTTP,OpenAI 兼容 REST API) -- **通信方式**: - - 入口:当前 REST(`/ai/chat`、`/ai/chat/stream`、`/ai/generate/question`、`/ai/optimize/expression`);目标态(ai.proto)转 **gRPC** 暴露 `AiService`(含 `StreamChat` 流式 RPC) - - 出口:gRPC 调 content / data-ana(**当前未实现,仅 LLM HTTP 调用**);SSE 流式对前端 -- **端口**:3008(见 [ai config.py:8](../../services/ai/src/ai/config.py) + [api-gateway config.go:57](../../services/api-gateway/internal/config/config.go)) - -### 2. 我的限界上下文 - -- **聚合职责**:LLM 调用网关 + 教学场景 AI 编排(备课 / 出题 / 表达优化),承载 **D6 智能洞察领域**的"生成"子域 -- **聚合根 / 实体**: - - `ChatConversation`(聊天会话,**待实现**,当前无状态) - - `GeneratedQuestion`(生成的题目,待审核入库) - - `PromptTemplate`(Prompt 模板,**待实现**) - - `UsageRecord`(用量计费记录,**待实现**) -- **业务领域**:D6 智能洞察(与 data-ana 共享,data-ana 偏"分析",ai 偏"生成") -- **我不负责**: - - 不负责题库存储(→ content 服务,ai 生成后调 content.CreateQuestions 入库) - - 不负责学情计算(→ data-ana 服务,ai 查询学情用于个性化出题) - - 不负责通知投递(→ msg 服务) - - 不持有业务状态(无 DB 写入,无 Outbox;用量计费记录可走 Kafka 事件给 data-ana 落 ClickHouse) -- **数据自治**:**无独占数据库**(设计上无状态;用量计费通过 Kafka 事件外发) - -### 3. 我与外部的契约 - -#### 消费的 proto message - -| 来源 | message / service | 用途 | -| --------------- | ---------------------------------------- | --------------------------------------- | -| content.proto | `KnowledgeGraphService.GetPrerequisites` | 查询知识点前置依赖用于出题上下文 | -| content.proto | `KnowledgeGraphService.GetLearningPath` | 查询学生学习路径用于个性化出题 | -| analytics.proto | `AnalyticsService.GetStudentWeakness` | 查询学生薄弱知识点用于靶向出题 | -| analytics.proto | `AnalyticsService.GetLearningTrend` | 查询学习趋势用于难度调节 | -| ai.proto | `ChatRequest` 等 | gRPC 暴露契约(**待实现 gRPC server**) | - -#### 暴露的 API / 事件 - -**HTTP 端点**(当前实现,见 [main.py](../../services/ai/src/ai/main.py)): - -| method | path | 说明 | -| ------ | ------------------------- | ---------------------------- | -| GET | `/healthz` | liveness | -| GET | `/readyz` | readiness(含 LLM 是否配置) | -| GET | `/metrics` | Prometheus 指标 | -| POST | `/ai/chat` | LLM 聊天(非流式) | -| POST | `/ai/chat/stream` | LLM 流式聊天(SSE) | -| POST | `/ai/generate/question` | 生成题目 | -| POST | `/ai/optimize/expression` | 优化表达 | - -**gRPC 契约**(ai.proto,**待实现**): - -- `Chat(ChatRequest) → ChatResponse` -- `StreamChat(ChatRequest) → stream ChatChunk`(流式 RPC) -- `GenerateQuestion(GenerateQuestionRequest) → GeneratedQuestion` -- `OptimizeExpression(OptimizeExpressionRequest) → OptimizedExpression` - -**发布的领域事件**:**当前无发布**。设计上可发布 `AIUsageRecorded` 事件(用量计费),由 data-ana 消费落 ClickHouse。004 §7.2 未列出此 topic,**阶段 2 需与 coord 确认是否新增 `edu.insight.ai.usage` topic**。 - -- **错误码前缀**:`AI_*`(待定义清单,见阶段 2 §6) -- **降级策略**:LLM API key 为空或调用失败时返回 `degraded: true` 骨架响应(见 [llm_client.py](../../services/ai/src/ai/llm_client.py)) - -### 4. 我的技术栈 - -- 语言:Python 3.12+ -- 框架:FastAPI 0.115+ / uvicorn -- LLM 客户端:`httpx` 异步直接调 OpenAI 兼容 REST API(**不依赖 openai SDK**,见 [llm_client.py:1-7](../../services/ai/src/ai/llm_client.py)) -- 配置:`pydantic-settings` BaseSettings(env_prefix="") -- 可观测: - - 日志:`structlog` - - 指标:`prometheus-client` + `make_asgi_app()` - - 链路:`opentelemetry-sdk` + `OTLPSpanExporter` + `FastAPIInstrumentor`(dev_mode=true 时跳过 exporter 初始化避免本地无 collector 报错) -- 流式响应:FastAPI `StreamingResponse` + `AsyncGenerator`,SSE 格式 `data: \n\n` -- 测试:pytest(**当前 0% 覆盖率**) - -### 5. 我的阶段归属 - -- **P5 沟通与 AI 阶段(M14-M16)**:建 AI 网关 + LLM Provider 适配 + 流式 SSE -- **退出标准**(pending-features P5):AI 辅助出题流式返回 + 题库全文检索 < 200ms(ES 部分由 ai05 负责) -- **依赖上游**: - - P1 地基:api-gateway 路由 - - P4 内容分析:content 服务(gRPC 查询知识点 / 题库)+ data-ana 服务(gRPC 查询学情) - - 外部:LLM Provider API key(OpenAI / Anthropic / 百川 / 本地模型) -- **下游依赖我**: - - P5 teacher-bff 聚合 AI 出题 mutation(004 §9.3:教师用 AI 出题并发布到班级) - - P5 teacher-portal SSE 流式 AI 对话(前端) - -### 6. 我需要对齐的黄金模板项(对照 classes 服务 + Python 规范) - -- [ ] 权限装饰器等价物:**当前 HTTP 端点全部裸露,无权限校验**。阶段 2 需设计 FastAPI Depends 权限依赖(AI 出题需 `AI_QUESTION_GENERATE` 权限,表达优化需 `AI_EXPRESSION_OPTIMIZE`) -- [ ] 错误码前缀统一:**当前无错误码体系**,降级时返回 `degraded: true` 但无业务错误码。**阶段 2 需定义 `AI_*` 错误码清单** -- [x] logger / metrics / tracer 三支柱(已具备) -- [x] `/healthz` + `/readyz` 健康检查(已具备,readyz 含 LLM 配置状态) -- [ ] 优雅关闭 SIGTERM:当前 lifespan 无显式 drain(LLM 流式请求需等待完成) -- [ ] 测试覆盖率 ≥ 80%:**当前 0%**,无 tests/ 目录 -- [ ] Dockerfile 多阶段构建:**当前单阶段**(`FROM python:3.12-slim`) -- [ ] Pydantic 输入验证:**当前仅 `ChatRequest` 是 BaseModel,`generate/question` 和 `optimize/expression` 直接接收 `prompt: str` / `text: str` query param,无请求体模型**。阶段 2 需补完整 Pydantic 请求/响应模型 -- [x] 配置通过 pydantic-settings 管理(已具备) -- [x] 异步优先(httpx async + AsyncGenerator stream) -- [x] 类型注解强制(已基本符合,main.py 函数返回值已标注) -- [ ] LLM Provider 适配器模式:**当前仅 OpenAI 兼容 REST**,未抽象 Provider 接口。阶段 2 需设计 `LLMProvider` 抽象 + OpenAI/Anthropic/百川/本地 多适配器 -- [ ] Prompt 模板管理:**当前 Prompt 硬编码在 main.py**,阶段 2 需设计模板管理(DB 或文件) -- [ ] 用量计费 / 频率限制:**当前无用量记录和限流**,阶段 2 需设计(Kafka 事件 + Redis 限流) -- [ ] 备课工作流:**当前未实现**,pending-features P5 要求"分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库"完整工作流 -- [ ] ruff check 零错误:需阶段 2 验证 - ---- - -## ai06 服务审计表 - -> 对照 ai-allocation.md §10 审计模板。data-ana 与 ai 均为 Python/FastAPI,黄金模板对齐按 Python 规范(coding-standards §4)评估。 -> 符号说明:✅ 已实现 | ❌ 缺失 | ⚠️ 部分实现 - -| 服务 | 权限校验 | 错误码前缀 | logger | metrics | tracer | /healthz | /readyz | 优雅关闭 | 测试覆盖率 | Dockerfile | -| -------- | -------- | ---------- | ------------ | ------------- | ------------------- | -------- | -------------------- | ------------------- | ---------- | ---------- | -| data-ana | ❌ | ❌ | ✅ structlog | ✅ prometheus | ✅ OTel | ✅ | ✅(含 CH+CDC 状态) | ⚠️ 仅 lifespan 关闭 | 0% | ❌ 单阶段 | -| ai | ❌ | ❌ | ✅ structlog | ✅ prometheus | ✅ OTel(dev 跳过) | ✅ | ✅(含 LLM 状态) | ⚠️ 仅 lifespan 关闭 | 0% | ❌ 单阶段 | - -### 审计发现的关键差距清单 - -#### data-ana 关键差距(按优先级) - -1. **P0 权限校验缺失**:所有 `/analytics/*` 端点裸露,无 DataScope 过滤。学生 A 可查询学生 B 的错题本(越权风险)。阶段 2 必须设计 `Depends(require_permission)` + `Depends(inject_data_scope)` 依赖注入 -2. **P0 gRPC server 未实现**:analytics.proto 定义了 `AnalyticsService` 但 data-ana 当前仅 HTTP。004 §4.1 明确 BFF → 业务服务走 gRPC,阶段 2 需引入 `grpc.aio` + `betterproto` 实现 gRPC server -3. **P1 事件发布缺失**:`edu.insight.mastery.updated` 事件未发布,下游 core-edu/msg 无法消费。需设计掌握度计算 + Kafka producer 发布链路 -4. **P1 ClickHouse schema 不规范**:当前 `student_dashboard_view` 实为 MergeTree 表(注释提到应为 ReplacingMergeTree(last_updated) 实现幂等去重),无 DDL 文件管理。阶段 2 需产出完整 ClickHouse DDL(5 张宽表:考试/作业/成绩/掌握度/出勤) -5. **P2 测试覆盖率 0%**:无 tests/ 目录,pytest 未配置 -6. **P2 Dockerfile 单阶段**:未做多阶段构建(builder + runtime),镜像体积大 -7. **P2 掌握度计算算法缺失**:当前 `_handle_grades_event` 用 `score / 100.0` 简化,未实现 pending-features 要求的"加权滑动平均"算法 -8. **P2 无 Pydantic 响应模型**:端点返回 `dict` 而非 `BaseModel`,无 `response_model` 校验 - -#### ai 关键差距(按优先级) - -1. **P0 权限校验缺失**:所有 `/ai/*` 端点裸露,AI 出题等敏感操作无权限校验 -2. **P0 gRPC server 未实现**:ai.proto 定义了 `AiService`(含 `StreamChat` 流式 RPC)但 ai 当前仅 HTTP。阶段 2 需引入 `grpc.aio` 实现 gRPC server + 流式 RPC -3. **P0 gRPC client 未实现**:004 §4.1 明确 AI → Content / AI → DataAna 走 gRPC,当前未实现。阶段 2 需设计 gRPC client 调用 content / data-ana -4. **P1 LLM Provider 适配器缺失**:当前 `llm_client.py` 仅 OpenAI 兼容 REST,未抽象 Provider 接口。pending-features P5 要求"LLM Provider 适配(OpenAI/Anthropic/百川/本地模型)" -5. **P1 Prompt 模板管理缺失**:system prompt 硬编码在 main.py,无模板管理。阶段 2 需设计模板存储(文件 or DB)+ 模板渲染 -6. **P1 备课工作流缺失**:pending-features P5 要求"分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库"完整工作流,当前仅"生成题目"单步 -7. **P1 用量计费 / 频率限制缺失**:无用量记录(token 消耗)、无频率限制(用户可无限调用 LLM)。阶段 2 需设计 Redis 限流 + Kafka 事件外发用量 -8. **P2 测试覆盖率 0%**:无 tests/ 目录 -9. **P2 Dockerfile 单阶段**:未做多阶段构建 -10. **P2 Pydantic 输入验证不完整**:`generate/question` 和 `optimize/expression` 直接接收 query param,无请求体模型 - ---- - -## 阶段 1 待 coord 交叉审查的跨模块契约对齐项 - -以下项需 coord 在交叉审查时仲裁(见 ai-allocation.md §8): - -| # | 议题 | 涉及方 | 当前状态 | ai06 建议 | -| --- | -------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | **data-ana 是否发布 `edu.insight.mastery.updated` 事件** | data-ana → core-edu / msg | 004 §7.2 列出此 topic,但当前 data-ana 未实现发布 | 阶段 2 设计发布链路;Python 无 Outbox,建议直接 Kafka producer(掌握度计算是派生数据,非事务写) | -| 2 | **ai 是否发布 `edu.insight.ai.usage` 用量事件** | ai → data-ana | 004 §7.2 未列出此 topic | 建议 coord 新增此 topic 用于用量计费落 ClickHouse;若 coord 不同意则 ai 自建用量记录表(破坏无状态原则) | -| 3 | **data-ana / ai 是否需要实现 gRPC server** | data-ana / ai ← teacher-bff / student-bff / ai | 004 §4.1 明确 BFF → 业务服务走 gRPC,analytics.proto / ai.proto 已定义 service,但当前两服务仅 HTTP | 阶段 2 引入 `grpc.aio` + `betterproto` 实现 gRPC server;HTTP 端点保留作为 Gateway 直连降级通道 | -| 4 | **CDC 直连 vs Outbox 领域事件双通道** | data-ana ← core-edu | 当前 data-ana 走 Debezium CDC(监听 binlog),不消费 core-edu Outbox 事件(events.proto) | 维持 CDC 为主通道(ADR-008 决策);events.proto 作为业务语义补充,待 P4 后期评估是否双消费 | -| 5 | **data-ana DataScope 过滤实现位置** | data-ana + iam | 004 §5.3 DataScope 6 级,业务服务在 Repository 层注入 WHERE;data-ana 是 ClickHouse 查询无 Repository 层 | 阶段 2 在 ClickHouse 查询 SQL 拼接时注入 DataScope WHERE(学生只能看自己,教师看本班,校管理员看本校);需 iam 提供 `getEffectiveDataScope(userId)` API | -| 6 | **ai 备课工作流是否引入 Temporal** | ai + coord | 004 §2.3 列出 Temporal 用于"工作流编排(考试生命周期、AI 编排)",P3 已引入 Temporal 试点 | 阶段 2 评估:简单工作流(4 步)可用 FastAPI BackgroundTasks;复杂工作流(含教师审核等待)建议 Temporal;待 coord 仲裁 | -| 7 | **LLM Provider 切换的配置化** | ai + coord | 当前 ai config.py 仅 OpenAI + Anthropic 字段 | 阶段 2 设计 `LLMProvider` 抽象 + 配置化路由(按 model 名路由到不同 provider);本地模型走 Ollama REST API | -| 8 | **data-ana ClickHouse DDL 管理位置** | data-ana + coord | 当前无 DDL 文件,宽表手动创建 | 建议 coord 在 `infra/clickhouse/` 下建立 DDL 目录(类似 MySQL init.sql),data-ana 提供 DDL 内容 | - ---- - -## 阶段 1 总结 - -ai06 已完成阶段 1 全局理解,产出本确认书。核心结论: - -1. **data-ana**(P4):CDC 链路已跑通(Debezium → Kafka → ClickHouse),3 个分析端点已实现降级模式。**关键差距**:权限校验、gRPC server、事件发布(mastery.updated)、ClickHouse DDL 规范化、掌握度算法、测试覆盖。 -2. **ai**(P5):LLM 客户端已实现降级模式(httpx 异步 + SSE 流式),4 个端点已实现。**关键差距**:权限校验、gRPC server + client、LLM Provider 适配器、Prompt 模板管理、备课工作流、用量计费、测试覆盖。 -3. **跨模块契约**:8 项待 coord 仲裁,最关键的是 gRPC server 实现决策(影响阶段 2 设计核心)和事件发布 topic 新增决策。 - -下一步进入阶段 2,按 ai-allocation.md §7 模板产出 data-ana 与 ai 的模块架构设计文档。 - ---- - -**AI Agent**: ai06 (data-ana + ai) -**Branch**: 单仓库并行模式(直接提交 main) -**Coordinator**: coord-ai diff --git a/docs/architecture/ai06-phase2-design.md b/docs/architecture/ai06-phase2-design.md deleted file mode 100644 index 5560090..0000000 --- a/docs/architecture/ai06-phase2-design.md +++ /dev/null @@ -1,864 +0,0 @@ -# ai06 阶段 2 交付物:模块架构设计文档 - -> AI 标识:ai06 -> 负责模块:data-ana(P4)、ai(P5) -> 阶段:架构设计外包 · 阶段 2(模块架构设计) -> 日期:2026-07-09 -> 关联文档:[ai06 阶段 1 确认书](./ai06-phase1-understanding.md)、[ai-allocation.md](./ai-allocation.md)、[004 架构影响地图](./004_architecture_impact_map.md)、[pending-features.md](./roadmap/pending-features.md) -> 审查请求:本设计文档待 coord 按 ai-allocation.md §8 交叉审查(接口一致性 / 端口冲突 / Topic 重复 / 错误码重叠 / 黄金模板对齐) - ---- - -## 设计原则与全局约束 - -本设计遵循以下强制约束(来自 project_rules.md + coding-standards.md + 004): - -1. **契约先行**:proto 已定义(analytics.proto / ai.proto),实现前不修改 proto,如需修改走 coord 流程 -2. **CQRS 读写分离**:data-ana 是纯读模型服务(无 MySQL 写),ClickHouse 宽表由 CDC 投影构建 -3. **事件驱动**:data-ana 消费 CDC + 领域事件;ai 不参与事件流(无状态) -4. **gRPC 优先**:004 §4.1 明确 BFF → 业务服务走 gRPC,两服务需实现 gRPC server(HTTP 保留作 Gateway 直连降级) -5. **DataScope 过滤**:004 §5.3 DataScope 6 级在查询层注入 WHERE -6. **三支柱可观测**:structlog + prometheus-client + OpenTelemetry(已具备,需补业务指标) -7. **降级模式**:外部依赖(ClickHouse / LLM / 下游 gRPC)不可用时返回骨架数据 + `degraded: true` -8. **Python 规范**:pydantic-settings 配置 / Pydantic 模型校验 / async 优先 / 类型注解强制 / ruff 零错误 - ---- - -# 模块架构设计文档 — data-ana - -## 1. 模块内部分层图 - -```mermaid -flowchart TB - subgraph Entry["入口层"] - HTTP[FastAPI HTTP Router
/analytics/* + /healthz + /readyz] - GRPC[grpc.aio Server
AnalyticsService] - end - - subgraph Middleware["中间件层"] - AUTH[AuthDepends
校验 x-user-id / x-user-roles] - SCOPE[DataScopeDepends
注入 data_scope 元数据] - TRACE[OTel FastAPIInstrumentor
+ grpc.aio server interceptor] - end - - subgraph Service["应用服务层 Application Service"] - S1[AnalyticsService
班级/学生/趋势查询编排] - S2[MasteryService
掌握度计算 + 事件发布] - S3[ErrorBookService
错题本查询] - end - - subgraph Repo["数据访问层 Repository"] - R1[ClickHouseRepository
宽表查询 + DataScope WHERE 注入] - R2[KafkaProducer
mastery.updated 事件发布] - R3[IamClient
gRPC 调 iam.getEffectiveDataScope] - end - - subgraph Consumer["CDC 消费者(后台任务)"] - C1[CdcConsumer
aiokafka AIOKafkaConsumer] - C2[ExamCache
exam_id→class_id 内存映射] - C3[EventHandler
grades/exams/homework/classes 路由] - end - - subgraph Storage["存储 / 总线"] - CH[(ClickHouse
edu_analytics 库)] - KAFKA[(Kafka
edu-cdc.* + edu.insight.mastery.updated)] - IAM[iam:3002 gRPC] - end - - HTTP --> AUTH --> SCOPE --> S1 - HTTP --> S3 - GRPC --> S1 - S1 --> R1 - S3 --> R1 - S2 --> R1 - S2 --> R2 - SCOPE --> R3 - R1 --> CH - R2 --> KAFKA - R3 --> IAM - - C1 --> C3 - C3 --> C2 - C3 --> R1 - C1 --> KAFKA -``` - -**分层规则**: - -- **入口层**:HTTP(保留作 Gateway 直连降级)+ gRPC(主入口,BFF 调用)。两入口共享同一 Application Service -- **中间件层**:FastAPI Depends 链(`AuthDepends` → `DataScopeDepends`);gRPC 用 server interceptor 注入身份元数据 -- **应用服务层**:编排查询 / 计算掌握度 / 发布事件,不直接访问存储 -- **数据访问层**:ClickHouse 查询封装 + Kafka producer + gRPC client(调 iam) -- **CDC 消费者**:独立后台任务(lifespan 启动),与 HTTP/gRPC 入口解耦 - -## 2. 领域模型 - -data-ana 是**纯读模型服务**,不持有写聚合根。领域模型为**视图聚合**(ClickHouse 物化): - -### 聚合根(视图型) - -| 聚合根 | 含义 | 物化载体 | 不变式 | -| ------------------ | ---------------- | ----------------------------------------- | ------------------------------------------------------------- | -| `StudentDashboard` | 学生学情宽表 | ClickHouse `student_dashboard_view` | 同一 (student_id, exam_id, knowledge_point_id) 仅保留最新版本 | -| `ClassPerformance` | 班级成绩聚合 | ClickHouse 即时聚合(不物化) | 聚合维度为 class_id + 时间窗 | -| `StudentErrorBook` | 学生错题本 | ClickHouse `student_errors` | 同一 (student_id, question_id) 累计 error_count | -| `MasterySnapshot` | 知识点掌握度快照 | ClickHouse `mastery_snapshot`(**新增**) | 同一 (student_id, knowledge_point_id) 保留历史版本 | - -### 值对象 - -- `WeakPoint`:knowledge_point_id + title + mastery_level -- `TrendPoint`:date + score -- `DataScope`:level (SELF/CLASS/GRADE/SCHOOL/DISTRICT/ALL) + scope_ids(具体可见的 class_id / grade_id 列表) - -### 聚合间通信 - -- 同服务内:直接函数调用(Application Service → Repository) -- 跨服务:仅通过 Kafka 事件(发布 `mastery.updated`)+ gRPC(调 iam 查 DataScope) - -## 3. 数据模型(ClickHouse DDL) - -> DDL 文件由 coord 统一管理在 `infra/clickhouse/ddl/`(待 coord 建立),data-ana 提供内容。 - -### 3.1 宽表 `student_dashboard_view` - -```sql --- 学生学情宽表:每次成绩写入产生一行,按 ORDER BY 去重保留最新版本 -CREATE TABLE IF NOT EXISTS student_dashboard_view -( - student_id String, - class_id String, - exam_id String, - subject_id String, - score Float64, - rank_in_class UInt32, - knowledge_point_id String, - mastery_level Float32, -- 0.0-1.0 - error_count UInt32, - last_updated DateTime64(3, 'UTC') -) -ENGINE = ReplacingMergeTree(last_updated) -PARTITION BY toYYYYMM(last_updated) -ORDER BY (student_id, exam_id, knowledge_point_id) -SETTINGS index_granularity = 8192; -``` - -**索引策略**: - -- ORDER BY `(student_id, exam_id, knowledge_point_id)`:主键索引,支持按学生查学情、按考试查成绩、按知识点查掌握度 -- PARTITION BY `toYYYYMM(last_updated)`:按月分区,支持历史数据归档 -- ReplacingMergeTree(last_updated):同 ORDER BY 自动去重,保留 last_updated 最大版本(幂等消费保证) - -### 3.2 错题本 `student_errors` - -```sql -CREATE TABLE IF NOT EXISTS student_errors -( - student_id String, - question_id String, - knowledge_point_id String, - error_count UInt32, - last_error_time DateTime64(3, 'UTC'), - content String -) -ENGINE = ReplacingMergeTree(last_error_time) -PARTITION BY toYYYYMM(last_error_time) -ORDER BY (student_id, question_id); -``` - -### 3.3 掌握度快照 `mastery_snapshot`(**新增**) - -```sql --- 知识点掌握度历史快照:每次掌握度计算产生新版本,支持趋势查询 -CREATE TABLE IF NOT EXISTS mastery_snapshot -( - student_id String, - knowledge_point_id String, - subject_id String, - mastery_level Float32, - calculated_at DateTime64(3, 'UTC'), - calculation_method LowCardinality(String) -- 'weighted_moving_avg' / 'simple_avg' -) -ENGINE = MergeTree -PARTITION BY toYYYYMM(calculated_at) -ORDER BY (student_id, knowledge_point_id, calculated_at); -``` - -### 3.4 用量计费 `ai_usage_log`(**新增,供 ai 服务写入**) - -```sql --- AI 用量记录:ai 服务通过 Kafka 事件投递,data-ana 消费落库 -CREATE TABLE IF NOT EXISTS ai_usage_log -( - request_id String, - user_id String, - provider LowCardinality(String), -- 'openai' / 'anthropic' / 'baichuan' / 'local' - model LowCardinality(String), - prompt_tokens UInt32, - completion_tokens UInt32, - total_tokens UInt32, - latency_ms UInt32, - success Boolean, - occurred_at DateTime64(3, 'UTC') -) -ENGINE = MergeTree -PARTITION BY toYYYYMM(occurred_at) -ORDER BY (user_id, occurred_at); -``` - -### 3.5 读写分离策略 - -| 操作 | 路径 | 说明 | -| -------------- | ----------------------------------- | ------------------------------------------ | -| 学情查询 | ClickHouse 宽表 | 实时聚合,亚秒级响应 | -| 错题本查询 | ClickHouse student_errors | 实时查询 | -| 掌握度趋势 | ClickHouse mastery_snapshot | 历史快照 | -| 掌握度计算 | CDC 触发 → 内存计算 → 写 ClickHouse | 派生数据,非事务写 | -| DataScope 解析 | gRPC 调 iam | 实时查询,结果 Redis 缓存 5min(004 §6.3) | - -## 4. API 设计 - -### 4.1 HTTP 端点(保留作 Gateway 直连降级) - -| method | path | 权限 | 请求 | 响应 | 说明 | -| ------ | ------------------------------------------- | ----------------------------- | ------------------------------------------------ | ----------------------------------------------------- | -------------------------------- | -| GET | `/healthz` | — | — | `{status, service}` | liveness | -| GET | `/readyz` | — | — | `{status, ready, degraded, clickhouse, cdc_consumer}` | readiness | -| GET | `/metrics` | — | — | Prometheus 格式 | 指标 | -| GET | `/analytics/class/{class_id}/performance` | `ANALYTICS_CLASS_READ` | query: `subject_id?`, `start_date?`, `end_date?` | `ClassPerformanceResponse` | 班级成绩分析 | -| GET | `/analytics/student/{student_id}/weakness` | `ANALYTICS_STUDENT_READ` | query: `subject_id?` | `StudentWeaknessResponse` | 学生薄弱知识点(DataScope 过滤) | -| GET | `/analytics/student/{student_id}/errorbook` | `ANALYTICS_STUDENT_READ` | query: `page?`, `size?` | `StudentErrorBookResponse` | 学生错题本(DataScope 过滤) | -| GET | `/analytics/student/{student_id}/trend` | `ANALYTICS_STUDENT_READ` | query: `start_date`, `end_date`, `subject_id?` | `LearningTrendResponse` | 学习趋势(**新增**) | -| GET | `/analytics/dashboard/teacher/{user_id}` | `ANALYTICS_TEACHER_DASHBOARD` | query: `class_id?` | `TeacherDashboardResponse` | 教师仪表盘聚合(**新增**) | - -### 4.2 gRPC 契约(analytics.proto,待实现 server) - -| RPC | 请求 | 响应 | 权限 | -| --------------------- | ------------------------------------------------------------------------ | ------------------ | ------------------------ | -| `GetClassPerformance` | `GetClassPerformanceRequest{class_id, subject_id, start_date, end_date}` | `ClassPerformance` | `ANALYTICS_CLASS_READ` | -| `GetStudentWeakness` | `GetStudentWeaknessRequest{student_id, subject_id}` | `StudentWeakness` | `ANALYTICS_STUDENT_READ` | -| `GetLearningTrend` | `GetLearningTrendRequest{student_id, start_date, end_date}` | `LearningTrend` | `ANALYTICS_STUDENT_READ` | - -**权限校验**:gRPC server interceptor 从 metadata 提取 `x-user-id` / `x-user-roles` / `x-data-scope`,调用 `AuthDepends` 等价逻辑。 - -### 4.3 Pydantic 请求/响应模型 - -```python -# 示例:班级成绩分析响应 -class ClassPerformanceResponse(BaseModel): - success: bool - data: ClassPerformanceData - degraded: bool = False - -class ClassPerformanceData(BaseModel): - class_id: str - average_score: float - pass_rate: float - total_students: int - scores: list[StudentScore] = [] # 详细成绩列表(受 DataScope 过滤) - -class StudentScore(BaseModel): - student_id: str - score: float - grade: str -``` - -## 5. 事件设计 - -### 5.1 消费的事件 - -| Topic | 来源 | 消息格式 | 消费动作 | -| ------------------------------------------------------ | ------------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------- | -| `edu-cdc.next_edu_cloud.core_edu_grades` | Debezium CDC(core-edu MySQL) | Debezium JSON(before/after/source/op/ts_ms) | 解析 → 查 ExamCache 填 class_id → 计算掌握度 → upsert student_dashboard_view | -| `edu-cdc.next_edu_cloud.core_edu_exams` | Debezium CDC | Debezium JSON | upsert ExamCache(exam_id → class_id, subject_id) | -| `edu-cdc.next_edu_cloud.core_edu_homework_submissions` | Debezium CDC(**新增订阅**) | Debezium JSON | 记录作业提交行为 → 更新 student_dashboard_view | -| `edu-cdc.next_edu_cloud.classes` | Debezium CDC | Debezium JSON | 同步班级维度(head_teacher_id)用于教师 DataScope | -| `edu-cdc.next_edu_cloud.iam_users` | Debezium CDC(**新增订阅**) | Debezium JSON | 同步用户 dataScope 用于查询过滤(避免每次查 iam) | -| `edu.insight.ai.usage` | ai 服务 Kafka producer(**待 coord 新增 topic**) | JSON(UsageRecord) | 落 ai_usage_log 表 | - -**幂等性**: - -- CDC 事件:依赖 ClickHouse `ReplacingMergeTree(last_updated)` 引擎按 ORDER BY 去重 -- 领域事件(若消费):基于 `event_id` 去重(Redis SETNX,TTL 7 天) - -### 5.2 发布的事件 - -| 事件 | Topic | 触发时机 | 消费者 | Payload | -| ---------------- | ----------------------------- | ----------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------- | -| `MasteryUpdated` | `edu.insight.mastery.updated` | 掌握度计算完成(CDC grades 事件触发后异步计算) | core-edu(推荐个性化练习)、msg(掌握度预警) | `{event_id, student_id, knowledge_point_id, mastery_level, calculated_at}` | - -**发布实现**(Python 无 Outbox 模式): - -- 掌握度计算是**派生数据**(非业务事务写),不需要 Outbox 保证事务一致 -- 直接用 `aiokafka.AIOKafkaProducer` 发布,`idempotent=true` + 事务性 producer -- 失败重试 3 次,仍失败记录日志 + 落 `mastery_publish_failed` 本地表(待 P6 评估是否引入 Outbox) - -## 6. 横切关注点对齐清单 - -### 6.1 权限装饰器等价物(FastAPI Depends) - -```python -# 权限点常量(与 iam 权限点对齐) -class Permissions: - ANALYTICS_CLASS_READ = "analytics:class:read" - ANALYTICS_STUDENT_READ = "analytics:student:read" - ANALYTICS_TEACHER_DASHBOARD = "analytics:teacher:dashboard" - -async def require_permission(permission: str) -> UserContext: - """FastAPI Depends 权限校验. - - 从 x-user-id / x-user-roles 头提取身份,校验角色是否含 permission。 - """ - ... - -async def inject_data_scope(ctx: UserContext = Depends(require_permission(...)))-> DataScope: - """注入 DataScope(从 iam.getEffectiveDataScope 查询,Redis 缓存 5min).""" - ... -``` - -### 6.2 错误码清单(前缀 `DATA_ANA_*`) - -| 错误码 | 触发条件 | HTTP | gRPC status | -| --------------------------------- | --------------------------------------- | ------------------- | ------------------ | -| `DATA_ANA_UNAUTHORIZED` | 缺失 x-user-id 头或 token 无效 | 401 | UNAUTHENTICATED | -| `DATA_ANA_FORBIDDEN` | 角色无对应权限 | 403 | PERMISSION_DENIED | -| `DATA_ANA_DATASCOPE_VIOLATION` | 查询目标超出 DataScope 范围 | 403 | PERMISSION_DENIED | -| `DATA_ANA_CLICKHOUSE_UNAVAILABLE` | ClickHouse 不可达(降级模式仍返回骨架) | 200 + degraded:true | OK + degraded flag | -| `DATA_ANA_INVALID_DATE_RANGE` | start_date > end_date | 400 | INVALID_ARGUMENT | -| `DATA_ANA_STUDENT_NOT_FOUND` | student_id 不存在 | 404 | NOT_FOUND | -| `DATA_ANA_CLASS_NOT_FOUND` | class_id 不存在 | 404 | NOT_FOUND | -| `DATA_ANA_INTERNAL_ERROR` | 未捕获异常 | 500 | INTERNAL | - -### 6.3 Logger 初始化 - -- 位置:`main.py` `init_logger()`(已具备) -- 配置:`structlog.make_filtering_bound_logger(level)` + `TimeStamper(fmt="iso")` + `ConsoleRenderer` -- **改进**:生产环境改用 `structlog.processors.JSONRenderer()`(当前 ConsoleRenderer 适合开发) - -### 6.4 Metrics 指标清单 - -| 指标名 | 类型 | 标签 | 描述 | -| --------------------------------------------- | --------- | -------------------- | -------------------------- | -| `data_ana_http_requests_total` | Counter | method, path, status | HTTP 请求总数 | -| `data_ana_http_request_duration_seconds` | Histogram | method, path | HTTP 请求延迟 | -| `data_ana_clickhouse_query_duration_seconds` | Histogram | query_type | ClickHouse 查询延迟 | -| `data_ana_clickhouse_query_total` | Counter | query_type, status | ClickHouse 查询总数 | -| `data_ana_cdc_events_consumed_total` | Counter | table, op | CDC 事件消费总数 | -| `data_ana_cdc_event_process_duration_seconds` | Histogram | table | CDC 事件处理延迟 | -| `data_ana_cdc_consumer_lag` | Gauge | topic, partition | CDC 消费者 lag | -| `data_ana_mastery_calculated_total` | Counter | — | 掌握度计算次数 | -| `data_ana_mastery_published_total` | Counter | status | mastery.updated 事件发布数 | -| `data_ana_datascope_cache_hits_total` | Counter | — | DataScope 缓存命中 | - -### 6.5 Tracer 初始化 - -- 位置:`main.py` `init_tracer()`(已具备) -- endpoint:`settings.otel_endpoint` + `/v1/traces` -- **改进**:gRPC server 注册 `grpc.aio.ServerInterceptor` 透传 W3C trace context - -### 6.6 /healthz 检查逻辑 - -- liveness:仅进程存活(已具备) - -### 6.7 /readyz 检查逻辑 - -```python -async def readyz() -> dict: - return { - "status": "ok" if all_ready else "degraded", - "ready": all_ready, - "degraded": not all_ready, - "clickhouse": "ok" | "unreachable" | "not_configured", - "cdc_consumer": "running" | "disabled" | "failed", - "kafka_brokers": settings.kafka_brokers or None, - "iam_grpc": "ok" | "unreachable", # 新增:iam gRPC 连通性 - "timestamp": datetime.now(UTC).isoformat(), - } -``` - -### 6.8 优雅关闭顺序 - -1. HTTP server stop accepting new requests(uvicorn graceful shutdown) -2. gRPC server graceful stop(等待在途 RPC 完成,30s 超时) -3. CDC consumer stop(等待在途消息处理完成,commit offset) -4. Kafka producer flush + close(确保 mastery.updated 事件已投递) -5. ClickHouse client close -6. iam gRPC channel close - -**信号处理**:注册 `signal.SIGTERM` handler,触发上述顺序。 - -## 7. 与其他模块的交互点(契约清单) - -| 方向 | 对方服务 | 协议 | 接口/事件 | 用途 | -| ------ | ------------------------- | ----- | ------------------------------------------------------------------- | ----------------------------- | -| 被调用 | api-gateway | HTTP | `/analytics/*` | Gateway 代理 | -| 被调用 | teacher-bff / student-bff | gRPC | `AnalyticsService.*` | BFF 聚合查询 | -| 被调用 | ai | gRPC | `AnalyticsService.GetStudentWeakness / GetLearningTrend` | AI 个性化出题上下文 | -| 调用 | iam | gRPC | `IamService.GetEffectiveDataScope`(**待 proto 新增**) | DataScope 解析 | -| 消费 | core-edu(CDC) | Kafka | `edu-cdc.next_edu_cloud.core_edu_grades/exams/homework_submissions` | 学情数据投递 | -| 消费 | core-edu(CDC) | Kafka | `edu-cdc.next_edu_cloud.classes` | 班级维度同步 | -| 消费 | iam(CDC) | Kafka | `edu-cdc.next_edu_cloud.iam_users` | 用户 dataScope 同步 | -| 消费 | ai | Kafka | `edu.insight.ai.usage`(**待 coord 新增**) | AI 用量落库 | -| 发布 | — | Kafka | `edu.insight.mastery.updated` | 掌握度更新通知 core-edu / msg | - -## 8. 风险与假设 - -### 8.1 假设 - -- **假设 1**:iam 提供 `GetEffectiveDataScope(userId) → DataScope` gRPC API。若 iam 未提供,fallback 为:从 `x-user-roles` 头推导(admin=ALL, teacher=CLASS_TAUGHT, student=SELF),但无法支持细粒度年级/学校范围 -- **假设 2**:core-edu 的 `core_edu_homework_submissions` 表存在 binlog。若不存在,作业相关学情无法通过 CDC 获取,需 core-edu 补表或走 Outbox 事件 -- **假设 3**:ClickHouse `ReplacingMergeTree` 在查询时需 `FINAL` 关键字确保去重生效。当前查询未加 `FINAL`,可能读到重复版本。**修复**:所有查询加 `FINAL` 或使用 `argMax` 聚合 -- **假设 4**:coord 同意新增 `edu.insight.ai.usage` topic。若不同意,ai 服务的用量计费需自建记录(破坏 ai 无状态原则) - -### 8.2 技术风险 - -| 风险 | 影响 | 缓解措施 | -| --------------------------- | ------------------------------------ | ------------------------------------------------ | -| ClickHouse 查询延迟超 5s | 违反 P4 退出标准 | 宽表索引优化 + 物化视图预聚合 + 查询超时 3s 降级 | -| CDC 消费者 lag 过大 | 学情数据延迟 > 5s | 监控 lag + 告警 + 水平扩展消费者(分区数提升) | -| ExamCache 内存泄漏 | 长期运行 OOM | LRU 淘汰策略(max 10000 条)+ 定期清理过期 exam | -| mastery.updated 事件丢失 | 下游 core-edu/msg 收不到通知 | Kafka producer `acks=all` + 本地失败表重试 | -| iam gRPC 不可达 | DataScope 无法解析 → 查询降级为 SELF | Redis 缓存 5min + fallback SELF 范围(最保守) | -| ClickHouse `FINAL` 查询性能 | 查询变慢 | 使用 `argMax` 替代 `FINAL`,或在写入时去重 | - -### 8.3 未决设计决策(需 coord 仲裁) - -1. **mastery.updated 发布是否需要 Outbox**:Python 无 Outbox 模式,建议直接 producer;但 004 §12.2 明确"事件发布:Outbox 模式 / 禁止直接 Kafka producer"。**冲突**:data-ana 是 Python 服务无 MySQL 写事务,Outbox 不适用。建议 coord 裁定:派生数据事件(非业务事务)允许直接 producer -2. **iam GetEffectiveDataScope proto 新增**:当前 iam.proto 仅有 `GetUserInfo`,无 DataScope 解析 API。需 coord 在 shared-proto 新增 `GetEffectiveDataScope` RPC -3. **edu.insight.ai.usage topic 新增**:004 §7.2 未列出,需 coord 确认是否新增 - ---- - -# 模块架构设计文档 — ai - -## 1. 模块内部分层图 - -```mermaid -flowchart TB - subgraph Entry["入口层"] - HTTP[FastAPI HTTP Router
/ai/* + /healthz + /readyz] - GRPC[grpc.aio Server
AiService 含 StreamChat] - end - - subgraph Middleware["中间件层"] - AUTH[AuthDepends
校验 x-user-id / x-user-roles] - RATE[RateLimitDepends
Redis 令牌桶限流] - TRACE[OTel + grpc interceptor] - end - - subgraph Service["应用服务层"] - S1[ChatService
聊天编排 + Prompt 模板渲染] - S2[QuestionGenerationService
出题工作流编排] - S3[ExpressionOptimizationService
表达优化] - S4[LessonPreparationWorkflow
备课工作流 4 步编排] - end - - subgraph Provider["LLM Provider 适配层"] - P0[LLMProvider 抽象接口
chat / stream_chat] - P1[OpenAIProvider
httpx 异步] - P2[AnthropicProvider
httpx 异步] - P3[BaichuanProvider
httpx 异步] - P4[LocalOllamaProvider
httpx 异步] - end - - subgraph Template["Prompt 模板管理"] - T1[PromptTemplateRegistry
模板注册 + 渲染] - T2[模板存储
YAML 文件 / DB] - end - - subgraph Client["下游 gRPC client"] - C1[ContentClient
查询知识点 / 题库] - C2[DataAnaClient
查询学情 / 薄弱点] - end - - subgraph Usage["用量计费"] - U1[UsageRecorder
token 消耗统计] - U2[KafkaProducer
发布 edu.insight.ai.usage] - end - - subgraph External["外部 / 存储"] - LLM[LLM Provider API
OpenAI/Anthropic/百川/Ollama] - CONTENT[content:3005 gRPC] - DATAANA[data-ana:3006 gRPC] - KAFKA[(Kafka)] - REDIS[(Redis
限流 + 缓存)] - end - - HTTP --> AUTH --> RATE --> S1 - HTTP --> S2 - HTTP --> S3 - GRPC --> S1 - GRPC --> S2 - S2 --> S4 - S4 --> C1 - S4 --> C2 - S1 --> T1 - S2 --> T1 - S1 --> P0 - S2 --> P0 - P0 --> P1 - P0 --> P2 - P0 --> P3 - P0 --> P4 - P1 --> LLM - P2 --> LLM - P3 --> LLM - P4 --> LLM - S1 --> U1 - S2 --> U1 - U1 --> U2 - U2 --> KAFKA - RATE --> REDIS - C1 --> CONTENT - C2 --> DATAANA -``` - -**分层规则**: - -- **入口层**:HTTP(保留作 Gateway 直连)+ gRPC(主入口,含 `StreamChat` 流式 RPC) -- **中间件层**:Auth + RateLimit(Redis 令牌桶,按 user_id 限流) -- **应用服务层**:4 个 Service,每个对应一类 AI 能力 -- **Provider 适配层**:抽象 `LLMProvider` 接口,多适配器实现(策略模式) -- **Prompt 模板**:模板注册 + 渲染,模板存储可配置(YAML 文件 or DB) -- **下游 client**:gRPC 调 content / data-ana -- **用量计费**:token 消耗统计 + Kafka 事件外发 - -## 2. 领域模型 - -ai 是**无状态服务**,不持有持久化聚合根。领域模型为**请求/响应模型 + 工作流编排**: - -### 聚合根(请求型,无持久化) - -| 聚合根 | 含义 | 生命周期 | -| ---------------------------- | ------------ | ------------------------------------------------------------ | -| `ChatConversation` | 单次聊天请求 | 单次请求 | -| `QuestionGenerationTask` | 出题任务 | 单次请求(备课工作流中多步) | -| `ExpressionOptimizationTask` | 表达优化任务 | 单次请求 | -| `LessonPreparationWorkflow` | 备课工作流 | 跨多步(分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库) | - -### 值对象 - -- `ChatMessage`:role + content -- `Usage`:prompt_tokens + completion_tokens + total_tokens -- `GeneratedQuestion`:question + answer + explanation -- `PromptTemplate`:name + system_prompt + user_template + variables - -### 工作流编排(备课) - -```mermaid -sequenceDiagram - participant T as 教师 - participant BFF as teacher-bff - participant AI as ai 服务 - participant Content as content - participant DA as data-ana - - T->>BFF: 请求备课(class_id, subject_id) - BFF->>AI: gRPC GenerateLessonPlan - AI->>DA: gRPC GetStudentWeakness(class_id) - DA-->>AI: 薄弱知识点列表 - AI->>Content: gRPC GetPrerequisites(knowledge_point_id) - Content-->>AI: 前置依赖知识点 - AI->>AI: LLM 生成题目(基于学情 + 知识点) - AI-->>BFF: 题目列表 + 推荐理由 - BFF-->>T: 题目供审核 - T->>BFF: 确认入库 - BFF->>Content: gRPC CreateQuestions - Content-->>BFF: 入库成功 -``` - -**工作流实现**: - -- **简单场景**(4 步内):FastAPI BackgroundTasks + asyncio.gather 并行查询 -- **复杂场景**(含教师审核等待):待 coord 仲裁是否引入 Temporal(004 §2.3 列出 Temporal 用于 AI 编排) - -## 3. 数据模型 - -ai 服务**无独占数据库**,无 MySQL schema。所有数据通过 Kafka 事件外发(用量计费)或 gRPC 查询下游。 - -### 用量计费(Kafka 事件 → data-ana 落 ClickHouse) - -```json -{ - "event_id": "uuid", - "user_id": "user-xxx", - "request_id": "req-xxx", - "provider": "openai", - "model": "gpt-4o-mini", - "prompt_tokens": 150, - "completion_tokens": 80, - "total_tokens": 230, - "latency_ms": 1200, - "success": true, - "occurred_at": "2026-07-09T12:00:00Z" -} -``` - -**Topic**:`edu.insight.ai.usage`(**待 coord 新增**) - -### 缓存策略 - -| 数据 | 存储 | TTL | 失效策略 | -| -------------------------------------- | ----------------------------- | -------- | ---------------------- | -| Prompt 模板 | Redis(模板变更事件驱动失效) | 1 小时 | 文件/DB 变更时主动失效 | -| LLM 响应(相同 prompt) | Redis(hash 缓存) | 30 分钟 | 短 TTL,避免陈旧 | -| DataScope(ai 不需要,仅 data-ana 用) | — | — | — | -| 限流计数 | Redis 令牌桶 | 滑动窗口 | 自动过期 | - -## 4. API 设计 - -### 4.1 HTTP 端点(保留作 Gateway 直连降级) - -| method | path | 权限 | 请求体 | 响应 | 说明 | -| ------ | ------------------------- | ------------------------ | --------------------------- | ------------------------------------ | ---------------------- | -| GET | `/healthz` | — | — | `{status, service}` | liveness | -| GET | `/readyz` | — | — | `{status, llm_configured, degraded}` | readiness | -| GET | `/metrics` | — | — | Prometheus | 指标 | -| POST | `/ai/chat` | `AI_CHAT` | `ChatRequest` | `ChatResponse` | LLM 聊天 | -| POST | `/ai/chat/stream` | `AI_CHAT` | `ChatRequest` | SSE stream | 流式聊天 | -| POST | `/ai/generate/question` | `AI_QUESTION_GENERATE` | `GenerateQuestionRequest` | `GeneratedQuestionResponse` | 生成题目 | -| POST | `/ai/optimize/expression` | `AI_EXPRESSION_OPTIMIZE` | `OptimizeExpressionRequest` | `OptimizedExpressionResponse` | 优化表达 | -| POST | `/ai/lesson/preparation` | `AI_LESSON_PREPARE` | `LessonPreparationRequest` | `LessonPreparationResponse` | 备课工作流(**新增**) | - -### 4.2 gRPC 契约(ai.proto,待实现 server) - -| RPC | 请求 | 响应 | 权限 | 说明 | -| -------------------- | ------------------------------------------------------ | ------------------------------------- | ------------------------ | ------------------------- | -| `Chat` | `ChatRequest{messages, model, temperature}` | `ChatResponse{content, model, usage}` | `AI_CHAT` | 非流式聊天 | -| `StreamChat` | `ChatRequest` | `stream ChatChunk` | `AI_CHAT` | 流式聊天(SSE over gRPC) | -| `GenerateQuestion` | `GenerateQuestionRequest{prompt, subject, difficulty}` | `GeneratedQuestion` | `AI_QUESTION_GENERATE` | 生成题目 | -| `OptimizeExpression` | `OptimizeExpressionRequest{text, context}` | `OptimizedExpression` | `AI_EXPRESSION_OPTIMIZE` | 优化表达 | - -### 4.3 Pydantic 请求/响应模型 - -```python -class ChatRequest(BaseModel): - messages: list[ChatMessage] - model: str = "gpt-4o-mini" - temperature: float = Field(0.7, ge=0.0, le=2.0) - stream: bool = False - -class ChatMessage(BaseModel): - role: Literal["system", "user", "assistant"] - content: str - -class ChatResponse(BaseModel): - success: bool - data: ChatData - degraded: bool = False - -class ChatData(BaseModel): - content: str - model: str - usage: Usage - -class Usage(BaseModel): - prompt_tokens: int - completion_tokens: int - total_tokens: int - -class GenerateQuestionRequest(BaseModel): - prompt: str = Field(..., min_length=1, max_length=2000) - subject: str - difficulty: Literal["easy", "medium", "hard"] - knowledge_point_ids: list[str] = [] # 可选:靶向知识点 - -class GeneratedQuestionResponse(BaseModel): - success: bool - data: GeneratedQuestionData - degraded: bool = False -``` - -## 5. 事件设计 - -### 5.1 消费的事件 - -ai 服务**不消费任何事件**(无状态,纯请求-响应)。 - -### 5.2 发布的事件 - -| 事件 | Topic | 触发时机 | 消费者 | Payload | -| ----------------- | ------------------------------------------- | ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `AIUsageRecorded` | `edu.insight.ai.usage`(**待 coord 新增**) | 每次 LLM 调用完成 | data-ana(落 ClickHouse ai_usage_log) | `{event_id, user_id, request_id, provider, model, prompt_tokens, completion_tokens, total_tokens, latency_ms, success, occurred_at}` | - -**发布实现**: - -- 每次 LLM 调用后异步发布(不阻塞响应) -- `aiokafka.AIOKafkaProducer` + `acks=all` -- 失败重试 3 次,仍失败记录日志(不影响主流程) - -## 6. 横切关注点对齐清单 - -### 6.1 权限装饰器等价物 - -```python -class Permissions: - AI_CHAT = "ai:chat" - AI_QUESTION_GENERATE = "ai:question:generate" - AI_EXPRESSION_OPTIMIZE = "ai:expression:optimize" - AI_LESSON_PREPARE = "ai:lesson:prepare" - -async def require_permission(permission: str) -> UserContext: - """从 x-user-id / x-user-roles 校验权限.""" - ... -``` - -### 6.2 错误码清单(前缀 `AI_*`) - -| 错误码 | 触发条件 | HTTP | gRPC status | -| --------------------------- | ----------------------------------------- | ------------------- | ------------------ | -| `AI_UNAUTHORIZED` | 缺失 x-user-id 或 token 无效 | 401 | UNAUTHENTICATED | -| `AI_FORBIDDEN` | 角色无对应权限 | 403 | PERMISSION_DENIED | -| `AI_RATE_LIMITED` | 触发限流 | 429 | RESOURCE_EXHAUSTED | -| `AI_LLM_UNAVAILABLE` | LLM Provider 不可达(降级模式仍返回骨架) | 200 + degraded:true | OK + degraded flag | -| `AI_LLM_TIMEOUT` | LLM 调用超时(30s) | 504 | DEADLINE_EXCEEDED | -| `AI_INVALID_MODEL` | model 名不支持 | 400 | INVALID_ARGUMENT | -| `AI_INVALID_DIFFICULTY` | difficulty 不在 easy/medium/hard | 400 | INVALID_ARGUMENT | -| `AI_DOWNSTREAM_UNAVAILABLE` | content / data-ana gRPC 不可达 | 502 | UNAVAILABLE | -| `AI_PROMPT_RENDER_FAILED` | Prompt 模板渲染失败(变量缺失) | 500 | INTERNAL | -| `AI_INTERNAL_ERROR` | 未捕获异常 | 500 | INTERNAL | - -### 6.3 Logger 初始化 - -- 位置:`main.py`(已具备) -- **改进**:生产环境改用 `JSONRenderer` - -### 6.4 Metrics 指标清单 - -| 指标名 | 类型 | 标签 | 描述 | -| ---------------------------------- | --------- | ----------------------------------------- | ------------------ | -| `ai_http_requests_total` | Counter | method, path, status | HTTP 请求总数 | -| `ai_http_request_duration_seconds` | Histogram | method, path | HTTP 请求延迟 | -| `ai_llm_calls_total` | Counter | provider, model, status | LLM 调用总数 | -| `ai_llm_call_duration_seconds` | Histogram | provider, model | LLM 调用延迟 | -| `ai_llm_tokens_total` | Counter | provider, model, type (prompt/completion) | token 消耗总数 | -| `ai_llm_stream_chunks_total` | Counter | provider, model | 流式 chunk 总数 | -| `ai_grpc_calls_total` | Counter | downstream, method, status | 下游 gRPC 调用总数 | -| `ai_rate_limit_hits_total` | Counter | user_id | 限流命中次数 | -| `ai_usage_events_published_total` | Counter | status | 用量事件发布数 | -| `ai_prompt_template_renders_total` | Counter | template_name, status | 模板渲染次数 | - -### 6.5 Tracer 初始化 - -- 位置:`main.py` `init_tracer()`(已具备,dev_mode 跳过) -- **改进**:gRPC server interceptor + 下游 gRPC client interceptor 透传 trace context - -### 6.6 /healthz 检查逻辑 - -- liveness:仅进程存活(已具备) - -### 6.7 /readyz 检查逻辑 - -```python -async def readyz() -> dict: - return { - "status": "ok", - "service": "ai", - "llm_configured": settings.llm_available, - "degraded": not settings.llm_available, - "providers": { - "openai": bool(settings.openai_api_key), - "anthropic": bool(settings.anthropic_api_key), - "baichuan": bool(settings.baichuan_api_key), - "local_ollama": bool(settings.ollama_base_url), - }, - "downstream_grpc": { - "content": "ok" | "unreachable", # 新增 - "data_ana": "ok" | "unreachable", # 新增 - }, - "redis": "ok" | "unreachable", # 新增(限流依赖) - } -``` - -### 6.8 优雅关闭顺序 - -1. HTTP server stop accepting new requests -2. gRPC server graceful stop(**关键**:等待在途 `StreamChat` 流式 RPC 完成,60s 超时,避免截断用户响应) -3. LLM 流式请求 drain(等待 httpx stream 完成) -4. Kafka producer flush + close(确保用量事件已投递) -5. 下游 gRPC channels close(content / data-ana) -6. Redis connection close - -## 7. 与其他模块的交互点(契约清单) - -| 方向 | 对方服务 | 协议 | 接口/事件 | 用途 | -| ------ | ------------ | ----- | ---------------------------------------------------------- | ------------------ | -| 被调用 | api-gateway | HTTP | `/ai/*` | Gateway 代理 | -| 被调用 | teacher-bff | gRPC | `AiService.*` | BFF 聚合 AI 能力 | -| 调用 | content | gRPC | `KnowledgeGraphService.GetPrerequisites / GetLearningPath` | 出题上下文查询 | -| 调用 | content | gRPC | `TextbookService.*`(若需教材上下文) | 出题教材关联 | -| 调用 | data-ana | gRPC | `AnalyticsService.GetStudentWeakness / GetLearningTrend` | 个性化出题学情查询 | -| 调用 | LLM Provider | HTTP | OpenAI 兼容 REST `/chat/completions` | LLM 推理 | -| 发布 | — | Kafka | `edu.insight.ai.usage`(**待 coord 新增**) | 用量计费外发 | - -## 8. 风险与假设 - -### 8.1 假设 - -- **假设 1**:content 服务实现了 `KnowledgeGraphService` gRPC server。当前 content.proto 已定义但未实现 gRPC server(ai05 阶段 2 设计中)。ai 调用前需确认 content gRPC 可用 -- **假设 2**:data-ana 实现了 `AnalyticsService` gRPC server(本设计文档已设计)。ai 调用前需确认 data-ana gRPC 可用 -- **假设 3**:coord 同意新增 `edu.insight.ai.usage` topic。若不同意,用量计费降级为 ai 本地日志(不落 ClickHouse,影响成本分析) -- **假设 4**:Redis 可用(限流依赖)。若 Redis 不可用,限流降级为"无限制"(风险:LLM 成本失控),或降级为内存令牌桶(单实例有效,多实例不一致) - -### 8.2 技术风险 - -| 风险 | 影响 | 缓解措施 | -| ---------------------- | ---------------- | --------------------------------------------------------------- | -| LLM 调用延迟高(>30s) | 用户体验差 | 超时 30s + 降级骨架响应 + 流式优先(用户感知首字延迟) | -| LLM 成本失控 | 财务风险 | Redis 令牌桶限流(每用户每分钟 10 次)+ 用量计费监控 + 告警阈值 | -| LLM Provider 单点故障 | 服务不可用 | 多 Provider 适配器 + 自动 failover(OpenAI 失败切 Anthropic) | -| 流式 RPC 中断 | 用户响应截断 | gRPC server graceful shutdown 60s drain + 客户端重连机制 | -| Prompt 注入攻击 | LLM 输出恶意内容 | 输入 sanitize + system prompt 加安全约束 + 输出过滤 | -| 下游 gRPC 不可达 | 备课工作流失败 | 降级:跳过学情查询,仅基于 prompt 生成题目 + `degraded: true` | - -### 8.3 未决设计决策(需 coord 仲裁) - -1. **备课工作流是否引入 Temporal**:004 §2.3 列出 Temporal 用于 AI 编排,但简单 4 步工作流可用 FastAPI BackgroundTasks。建议:M14 用 BackgroundTasks,M15 评估是否迁移 Temporal -2. **`edu.insight.ai.usage` topic 新增**:需 coord 在 shared-proto events.proto 新增 `AIUsageEvent` message + 004 §7.2 新增 topic -3. **LLM Provider 配置化路由**:是否在 shared-py 建立通用 `LLMProvider` 抽象(供未来其他 Python 服务复用)。建议 P5 阶段在 ai 服务内部实现,P6 评估是否提取到 shared-py -4. **Prompt 模板存储位置**:YAML 文件(简单,无 DB)vs DB(动态更新)。建议 P5 用 YAML 文件(`services/ai/src/ai/prompts/*.yaml`),P6 评估迁移 DB - ---- - -# 阶段 2 总结 - -ai06 已完成阶段 2 模块架构设计,产出 data-ana 与 ai 两份设计文档。核心设计决策: - -## data-ana 设计要点 - -1. **分层**:HTTP + gRPC 双入口共享 Application Service;CDC 消费者独立后台任务 -2. **数据模型**:4 张 ClickHouse 宽表(student_dashboard_view / student_errors / mastery_snapshot / ai_usage_log),ReplacingMergeTree 引擎保证幂等 -3. **权限**:FastAPI Depends 链(require_permission + inject_data_scope),DataScope WHERE 注入 ClickHouse 查询 -4. **事件**:消费 6 个 CDC topic + 发布 `edu.insight.mastery.updated`(直接 producer,非 Outbox,因派生数据非事务写) -5. **gRPC**:实现 `AnalyticsService` server(analytics.proto 已定义) -6. **降级**:ClickHouse 不可达返回骨架 + degraded:true;iam gRPC 不可达降级为 SELF DataScope - -## ai 设计要点 - -1. **分层**:HTTP + gRPC 双入口;LLM Provider 适配层(策略模式,4 适配器) -2. **无状态**:无 DB,用量计费通过 Kafka 事件外发 -3. **权限**:FastAPI Depends + Redis 令牌桶限流(每用户每分钟 10 次) -4. **工作流**:备课 4 步编排(学情查询 → 知识点推荐 → 题目生成 → 教师审核入库),简单场景用 BackgroundTasks -5. **gRPC**:实现 `AiService` server(含 `StreamChat` 流式 RPC);gRPC client 调 content / data-ana -6. **降级**:LLM 不可达返回骨架 + degraded:true;下游 gRPC 不可达降级跳过 - -## 待 coord 交叉审查项(汇总) - -| # | 议题 | 涉及文档 | -| --- | ------------------------------------------------------------------------------------ | ----------------------- | -| 1 | data-ana 发布 `edu.insight.mastery.updated` 用直接 producer(非 Outbox)是否合规 | 004 §12.2 | -| 2 | 新增 `edu.insight.ai.usage` topic + `AIUsageEvent` proto message | 004 §7.2 + events.proto | -| 3 | iam 新增 `GetEffectiveDataScope` gRPC RPC | iam.proto | -| 4 | data-ana / ai 实现 gRPC server 决策 | 004 §4.1 | -| 5 | ClickHouse DDL 管理位置(建议 `infra/clickhouse/ddl/`) | infra/ | -| 6 | ai 备课工作流是否引入 Temporal | 004 §2.3 | -| 7 | 端口冲突检查:data-ana 3006 / ai 3008(无冲突) | full-stack-runbook | -| 8 | 错误码前缀检查:`DATA_ANA_*` / `AI_*`(与其他服务不重叠) | — | -| 9 | 黄金模板对齐:Python 服务无 NestJS 装饰器,权限校验用 FastAPI Depends 等价物是否认可 | — | - -下一步:等待 coord 交叉审查通过后,进入阶段 3(按图实施)。 - ---- - -**AI Agent**: ai06 (data-ana + ai) -**Branch**: 单仓库并行模式(直接提交 main) -**Coordinator**: coord-ai diff --git a/docs/architecture/issues/contracts/classes_contract.md b/docs/architecture/issues/contracts/classes_contract.md deleted file mode 100644 index 59ede53..0000000 --- a/docs/architecture/issues/contracts/classes_contract.md +++ /dev/null @@ -1,123 +0,0 @@ -# classes 对接契约 - -> 负责人:ai07 -> 关联:[matrix.md](../matrix.md)、[classes.proto](../../../packages/shared-proto/proto/classes.proto)、[events.proto](../../../packages/shared-proto/proto/events.proto)、[02-architecture-design.md](../../../services/classes/docs/02-architecture-design.md) -> 状态:黄金模板(P1 已实现),P3 合并入 core-edu - ---- - -## §1 我提供什么(对外接口) - -### 1.1 gRPC 接口(如有) - -| Service | RPC | 请求 | 响应 | 端口 | -| ------------ | ----------- | ------------------ | ------------------- | ------------------------------- | -| ClassService | CreateClass | CreateClassRequest | Class | 50053(P3 启用,core-edu 承载) | -| ClassService | GetClass | GetClassRequest | Class | 50053 | -| ClassService | ListClasses | ListClassesRequest | ListClassesResponse | 50053 | -| ClassService | UpdateClass | UpdateClassRequest | Class | 50053 | -| ClassService | DeleteClass | DeleteClassRequest | Empty | 50053 | - -> **注意**:classes 当前仅 REST(端口 3001),gRPC server 50053 在 P3 合并入 core-edu 后启用。proto 契约已就绪(classes.proto),由 core-edu 实现承载。 - -### 1.2 HTTP 端点(如有) - -| Method | Path | 权限 | 说明 | -| ------ | -------------- | ---------------- | ----------------------------- | -| POST | `/classes` | `CLASSES_CREATE` | 创建班级 | -| GET | `/classes` | `CLASSES_READ` | 列表(可选 `?gradeId=` 过滤) | -| GET | `/classes/:id` | `CLASSES_READ` | 单条查询 | -| PUT | `/classes/:id` | `CLASSES_UPDATE` | 更新 | -| DELETE | `/classes/:id` | `CLASSES_DELETE` | 删除(先校验存在) | -| GET | `/healthz` | 无 | liveness | -| GET | `/readyz` | 无 | readiness(校验 DB) | -| GET | `/metrics` | 无 | Prometheus 指标 | - -> **响应信封**:ActionState(`{success:true, data:T}` / `{success:false, error:{code,message,details?,traceId?}}`) - -### 1.3 GraphQL schema(如 BFF) - -不适用。classes 是业务服务,非 BFF。 - -### 1.4 Kafka 事件发布(如有) - -| Topic | Event | 消费方 | 阶段 | -| --------------------------- | --------------------------------- | ------------------------------- | ------------------- | -| `edu.org.class.created` | ClassEvent(action: created) | data-ana(建宽表行) | P3(core-edu 承载) | -| `edu.org.class.updated` | ClassEvent(action: updated) | data-ana、msg(班主任变更通知) | P3 | -| `edu.org.class.deleted` | ClassEvent(action: deleted) | data-ana、core-edu(关联检查) | P3 | -| `edu.org.class.transferred` | ClassEvent(action: transferred) | msg(通知新/旧班主任) | P3 | - -> **事件 message**:`events.proto` 的 `ClassEvent`(event_id / aggregate_id / event_type / occurred_at / class_id / name / action / metadata) -> **发布方式**:Outbox 模式(P3 补齐 `shared/outbox/`),保证事务与事件最终一致 - -### 1.5 错误码前缀 - -`CLASSES_`(004 §11.4 确认保留,P3 合并入 core-edu 后保留历史遗留前缀) - -| 错误码 | HTTP | 触发条件 | -| --------------------------- | ---- | ----------------------------- | -| `CLASSES_VALIDATION_ERROR` | 400 | Zod 校验失败 / 空 update body | -| `CLASSES_NOT_FOUND` | 404 | 资源不存在 | -| `CLASSES_PERMISSION_DENIED` | 403 | PermissionGuard 校验失败 | -| `CLASSES_CONFLICT` | 409 | 并发冲突(预留) | -| `CLASSES_BUSINESS_ERROR` | 422 | 业务规则违反(预留) | -| `CLASSES_DATABASE_ERROR` | 500 | DB 操作失败 | -| `CLASSES_INTERNAL_ERROR` | 500 | 未预期异常 | - ---- - -## §2 我消费什么(依赖上游) - -### 2.1 gRPC 调用(同步) - -当前无。P3 合并入 core-edu 后,可能调用 iam 的 `BatchGetUsers`(班主任信息批量查询)。 - -### 2.2 Kafka 事件订阅(异步) - -| Topic | Event | 消费动作 | 阶段 | -| --------------------------- | ---------------------------- | -------------------------------------------- | ---- | -| `edu.identity.user.deleted` | UserEvent(action: deleted) | 若 deleted user 是班主任,置空 headTeacherId | P3 | - -### 2.3 HTTP 调用(如有) - -无。classes 是基础数据源,不反向调用其他服务。 - ---- - -## §3 就绪信号 - -### 3.1 我依赖的上游就绪标志 - -- [ ] MySQL classes_db 可用(已就绪,P1) -- [ ] api-gateway `/classes/*` 路由已注册(已就绪,P1) -- [ ] P3:iam `BatchGetUsers` gRPC 可用(班主任信息查询) -- [ ] P3:Kafka `edu.identity.user.deleted` topic 可消费 - -### 3.2 我的就绪标志(供下游消费) - -- [x] classes REST API 5 端点可用(P1 已实现) -- [x] `/healthz` + `/readyz` 可用(P1 已实现) -- [x] `/metrics` 可用(P1 已实现) -- [ ] P3:gRPC 50053 启用(由 core-edu 承载,`ClassService` 5 RPC 可调用) -- [ ] P3:`edu.org.class.created/updated/deleted/transferred` topic 可发布 -- [ ] P3:Outbox 模式落地(`shared/outbox/` 目录补齐) - ---- - -## §4 Mock 策略 - -### 4.1 我提供的 mock - -classes 是 P1 黄金模板,REST API 已实现,**下游无需 mock,可直接调用真实服务**。 - -但为 P3 gRPC 迁移期间兼容,提供以下 mock 供下游在 gRPC 未启用时使用: - -- **REST mock**(已可用):直接调用 `http://classes:3001/classes/*`,返回真实数据 -- **gRPC mock**(P3 过渡期):grpc-mock 拦截 50053,ClassService 5 RPC 返回固定 Class 数据 -- **Kafka mock**(P3 过渡期):classes 事件未发布前,下游订阅方使用本地 stub(固定 ClassEvent JSON) - -### 4.2 我消费的 mock - -- P3 期间 iam `BatchGetUsers` 未就绪时,使用 grpc-mock 返回固定用户信息(班主任姓名) -- P3 期间 `edu.identity.user.deleted` topic 未就绪时,使用本地 Kafka mock consumer stub diff --git a/docs/architecture/issues/objections/classes_issue.md b/docs/architecture/issues/objections/classes_issue.md deleted file mode 100644 index 79fc5bb..0000000 --- a/docs/architecture/issues/objections/classes_issue.md +++ /dev/null @@ -1,58 +0,0 @@ -# classes 问题记录 - -> 负责人:ai07 -> 关联:[coord.md](../coord.md)、[contracts/classes_contract.md](../contracts/classes_contract.md) -> 规则:AI 遇到问题时在此追加条目,coord 仲裁后更新状态 - ---- - -## 问题列表 - - - -### ISSUE-001-ai07:P3 合并后 classes 目录保留方式 - -- **提请方**:ai07 -- **日期**:2026-07-10 -- **类型**:契约不明确 -- **描述**:classes 是黄金模板,P3 合并入 core-edu 后,`services/classes/` 目录是否保留?若保留,是只读对照基准还是改为 shared-ts 模板?若删除,黄金模板对照基准丢失,其他 TS 服务无法对齐。 -- **建议方案**:保留 `services/classes/` 作为只读对照基准(不删除),core-edu 复制其结构。黄金模板 checklist(02-architecture-design.md §10)由 ai07 持续维护。 -- **状态**:待 coord 仲裁 - -### ISSUE-002-ai07:cuid2 迁移时机 - -- **提请方**:ai07 -- **日期**:2026-07-10 -- **类型**:前置依赖缺失 -- **描述**:classes 当前用 uuid v4 生成 ID,project_rules 要求 cuid2。迁移时机有两种选择:(A) P1 黄金模板立即迁移;(B) P3 合并入 core-edu 时统一迁移。若选 A,黄金模板先行避免 ai08 继承 uuid 遗留;若选 B,减少一次迁移成本。 -- **建议方案**:选 A(P1 立即迁移),黄金模板应先行,避免其他服务继承 uuid 遗留。迁移涉及 classes 表主键、proto message 字段类型。 -- **状态**:待 coord 仲裁 - -### ISSUE-003-ai07:proto ClassService 合并后归属 - -- **提请方**:ai07 -- **日期**:2026-07-10 -- **类型**:契约不明确 -- **描述**:P3 合并入 core-edu 后,`classes.proto` 的 `ClassService` 是否迁移到 `core_edu.proto`?若迁移,破坏 proto 契约稳定性(下游需更新 import);若保留,core_edu.proto 不会膨胀但需在 core-edu 实现中 import classes.proto。 -- **建议方案**:保留 `classes.proto` 由 core-edu 实现(选项 B),保持契约稳定性,避免下游 import 路径变更。 -- **状态**:待 coord 仲裁 - -### ISSUE-004-ai07:node:20-alpine → node:22-alpine 升级时机 - -- **提请方**:ai07 -- **日期**:2026-07-10 -- **类型**:其他 -- **描述**:classes Dockerfile 用 `node:20-alpine`,project_rules §15.8 镜像预拉清单是 `node:22-alpine`。是 P1 立即升级还是等统一升级? -- **建议方案**:P1 立即升级,影响所有 TS 服务 Dockerfile 基准,黄金模板应先行。 -- **状态**:待 coord 仲裁 diff --git a/docs/architecture/issues/worklines/classes_workline.md b/docs/architecture/issues/worklines/classes_workline.md deleted file mode 100644 index 4e56d1e..0000000 --- a/docs/architecture/issues/worklines/classes_workline.md +++ /dev/null @@ -1,107 +0,0 @@ -# classes 工作排期 - -> 负责人:ai07 -> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/classes_contract.md](../contracts/classes_contract.md) -> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试) -> 特殊说明:classes 是黄金模板(P1 已实现),P3 合并入 core-edu(ai08 接管) - ---- - -## §1 总览 - -classes 是 Edu 微服务的黄金模板,P1 阶段已实现班级 CRUD + 全横切关注点。P3 阶段合并入 core-edu 后,classes 模块代码由 ai08 接管维护,但黄金模板对照 checklist 由 ai07 持续维护。 - -**全阶段目标**: - -- P1(已完成):班级 CRUD + 黄金模板 + 6 项整改 -- P3:合并入 core-edu + Outbox + gRPC + DataScope + Redis 缓存 + cuid2 迁移 -- P6:黄金模板 checklist 持续维护 + 硬化 - ---- - -## §2 全阶段甘特图(P1-P6) - -```mermaid -gantt - title ai07 classes 黄金模板全阶段排期 - dateFormat YYYY-MM-DD - axisFormat %m-%d - - section P1 黄金模板整改 - 阶段1+2架构设计文档 :crit, a7a, 2026-07-10, 1d - typeorm移除+tracer修复+@Query替换 :crit, a7b, 2026-07-10, 1d - 测试覆盖率60→80%+Dockerfile升级 :a7c, 2026-07-11, 1d - schema索引补齐 :a7d, 2026-07-11, 1d - - section P3 合并入core-edu - shared/outbox目录补齐 :a7e, 2026-07-14, 2d - cuid2迁移+DataScope注入 :a7f, 2026-07-15, 2d - Redis缓存+软删除+分页 :a7g, 2026-07-17, 3d - gRPC 50053启用+核心edu合并 :a7h, 2026-07-20, 2d - - section P6 硬化 - 黄金模板checklist持续维护 :a7i, 2026-07-25, 5d - 可观测性增强+告警规则 :a7j, 2026-07-30, 3d -``` - ---- - -## §3 详细任务 - -### P1:黄金模板整改(立即) - -| # | 任务 | 交付物 | 验收标准 | 状态 | -| --- | ----------------------------------- | ------------------------------------------------------------------------- | --------------------------------- | --------- | -| 1 | 阶段 1+2 架构设计文档 | `services/classes/docs/01-understanding.md` + `02-architecture-design.md` | coord 交叉审查通过 | ✅ 已完成 | -| 2 | 移除 typeorm 冗余依赖 | `package.json` 删除 typeorm 行 | `pnpm install` 零错误 | 待执行 | -| 3 | tracer.ts console.log → logger.info | `src/shared/observability/tracer.ts` L20 | grep `console.log` 返回 0 | 待执行 | -| 4 | controller list @Req → @Query | `src/classes/classes.controller.ts` L49-53 | list 方法用 `@Query('gradeId')` | 待执行 | -| 5 | 测试覆盖率阈值 60% → 80% | `vitest.config.ts` + 补测试用例 | `pnpm test:coverage` 达 80% | 待执行 | -| 6 | Dockerfile node:20 → node:22 | `Dockerfile` | 基础镜像 `node:22-alpine` | 待执行 | -| 7 | schema 补齐索引 | `classes.schema.ts` + 迁移脚本 | grade_id / head_teacher_id 有索引 | 待执行 | - -### P3:合并入 core-edu(交接给 ai08) - -| # | 任务 | 交付物 | 验收标准 | 状态 | -| --- | ------------------------- | ---------------------------------------------------------- | ------------------------------- | ------ | -| 1 | `shared/outbox/` 目录补齐 | outbox.repository / outbox.publisher / outbox.relay-worker | 事件可发布到 Kafka | 待执行 | -| 2 | cuid2 迁移 | id 字段 uuid v4 → cuid2 + 数据迁移脚本 | 双写过渡期完成 | 待执行 | -| 3 | DataScope 6 级 WHERE 注入 | Repository list 方法接收 dataScope 参数 | 6 级过滤生效 | 待执行 | -| 4 | Redis 班级列表缓存 | cachedList 方法 + TTL 5 分钟 + 事件失效 | 缓存命中率 > 80% | 待执行 | -| 5 | 软删除 + 审计字段 | deleted_at / created_by / updated_by | DELETE 改软删除 | 待执行 | -| 6 | 分页 + 批量查询 | 游标分页 + POST /classes/batch | proto page_size/page_token 落地 | 待执行 | -| 7 | gRPC server 50053 启用 | ClassService 5 RPC 实现 | gRPC health check SERVING | 待执行 | -| 8 | PermissionGuard 改调 iam | 动态权限查询 getEffectivePermissions | 角色变更无需改代码重启 | 待执行 | - -### P6:硬化 - -| # | 任务 | 交付物 | 验收标准 | 状态 | -| --- | --------------------------- | ---------------------------------- | ---------------- | ------ | -| 1 | 黄金模板 checklist 持续维护 | 02-architecture-design.md §10 更新 | 覆盖全部新模式 | 待执行 | -| 2 | 可观测性增强 | 日志注入 traceId + Outbox 积压告警 | Grafana 面板可用 | 待执行 | - ---- - -## §4 依赖与就绪信号 - -- **我依赖**: - - MySQL classes_db(P1 已就绪) - - api-gateway 路由注册(P1 已就绪) - - P3:iam `BatchGetUsers` + `getEffectivePermissions` - - P3:Kafka 基础设施(ai08 启用) -- **我的就绪信号**: - - P1:REST API 5 端点 + /healthz + /readyz + /metrics 可用(已就绪) - - P3:gRPC 50053 + Outbox 事件发布 + Redis 缓存可用 -- **交接信号**:P3 合并时,ai07 向 ai08 交接 classes 模块,交接清单见 02-architecture-design.md §11 - ---- - -## §5 交接计划(ai07 → ai08) - -| 交接项 | 内容 | 时间 | -| ------------------ | ------------------------------------------------------- | --------- | -| 源码交接 | `services/classes/src/` 全部源码 | P3 启动时 | -| 文档交接 | `services/classes/docs/` 2 份设计文档 | P3 启动时 | -| proto 交接 | `classes.proto` 由 core-edu 实现承载 | P3 启动时 | -| 黄金模板保留 | `services/classes/` 保留作只读对照基准(待 coord 仲裁) | P3 合并后 | -| checklist 持续维护 | ai07 持续维护 02-architecture-design.md §10 | 跨阶段 | diff --git a/docs/troubleshooting/known-issues.md b/docs/troubleshooting/known-issues.md index 7ad1651..e3b072e 100644 --- a/docs/troubleshooting/known-issues.md +++ b/docs/troubleshooting/known-issues.md @@ -164,6 +164,31 @@ | 全量测试 | `pnpm test` | | 启动最小基础设施 | `docker compose -f infra/docker-compose.minimal.yml up -d` | +### 1.10 coord 交叉审查仲裁审查(跨模块) + +> AI 在执行 coord 仲裁整改清单前,必须以 ai-allocation.md §3.2 为唯一源交叉验证责任方,并检查裁决内部自洽性。 + +| 场景 | 技术/规则 | +| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 仲裁责任方错配(content→ai05) | content 模块由 ai09 负责(ai-allocation §3.2 L80),coord 整改清单若标 ai05 则为错配;AI 必须以 ai-allocation §3.2 为准,提请 coord 修正而非自行执行 | +| 仲裁责任方错配(msg→ai05) | msg 模块由 ai10 负责(ai-allocation §3.2 L81),coord 整改清单若标 ai05 则为错配;同上 | +| 仲裁责任方错配(iam→ai02) | iam 模块由 ai06 负责(ai-allocation §3.2 L77),ai02=push-gateway;coord 整改清单若标 ai02 则为错配;同上 | +| 仲裁责任方错配(push-gateway→ai01) | push-gateway 由 ai02 负责(ai-allocation §3.2 L73),ai01=api-gateway;coord §4.2/§5.6/整改第 21 项若标 ai01 则为错配;同上 | +| 仲裁责任方错配(teacher-portal→ai07) | teacher-portal 由 ai13 负责(ai-allocation §3.2 L84),ai07=classes(黄金模板);coord §2.4/§4.1/§5.5/整改第 2 项若标 ai07 修正 teacher-portal 则为错配;同上 | +| 仲裁责任方错配(parent-portal→ai07) | parent-portal 由 ai15 负责(ai-allocation §3.2 L86),ai07=classes;coord §2.4 第 4 项若标 ai07 修正 parent-portal 端点则为错配;同上 | +| 仲裁责任方错配(core-edu→ai03) | core-edu 由 ai08 负责(ai-allocation §3.2 L79),ai03=teacher-bff;coord §4.2/整改第 14 项/整改第 23 项若标 ai03 则为错配;同上 | +| 仲裁责任方错配(parent-bff→ai07) | parent-bff 由 ai05 负责(ai-allocation §3.2 L76),ai07=classes;coord §5.2 "ai03/ai04/ai07" 若指 BFF 前缀迁移则 ai07 应为 ai05;同上 | +| 仲裁责任方错配系统性(5 组认知错位) | coord 存在 5 组系统性 AI 编号映射错位(ai07→teacher-portal/ai05→content-msg/ai02→iam/ai01→push-gateway/ai03→core-edu),疑似沿用之前 session 错误认知;整改前必须以 ai-allocation §3.2 全量交叉验证 | +| 仲裁裁决内部冲突(topic 保留 vs 废弃) | coord-cross-review.md §3.5 保留 `edu.notification.events`,但 parent-bff 02 §0.1 C5 废弃改为 `edu.notification.sent/read/recalled/failed`;coord 自身前后不一致时,以正式审查报告(coord-cross-review.md)为准或提请 coord 统一 | +| 仲裁裁决内部冲突(GraphQL 引入时机) | coord-cross-review.md §2.5 裁决"P2-P3 用 REST",但 parent-bff 02 §0.1 U3 用户仲裁"GraphQL P2 立即引入";coord §2.5 与用户 U3 仲裁直接冲突,用户仲裁优先级高,应以 U3 为准,提请 coord 修正 §2.5 | +| 仲裁现状误判(project_rules §5) | coord §2.1 裁决"修订 project_rules §5 包名规范为 next_edu_cloud.*",但 project_rules §5 L214 已是目标态;整改前应先验证现状,避免多余整改 | +| 仲裁审查三维度 | ① 责任方是否匹配 ai-allocation §3.2 ② 裁决是否自洽(无内部冲突)③ 现状是否误判;三维度全过方可执行 | +| 仲裁问题处理流程 | 发现问题记录到 known-issues 工作日志 + 提请 coord 修正,禁止自行修改仲裁文档或按错误责任分配执行 | +| coord 代笔文档仲裁 vs 正式审查报告 | coord 代笔文档(如 parent-bff 02)中的仲裁决策(如 C5)可能与 coord 正式审查报告(coord-cross-review.md)不一致;以正式审查报告为准,代笔文档中的仲裁需 coord 在正式报告中确认 | +| 仲裁审查覆盖面要求 | 首次审查覆盖面不足(仅查 3 组错配遗漏 4 组),二次审查应严格逐条核对全部 24 项整改项,禁止抽样核对;系统性错配需按 ai-allocation §3.2 全量交叉验证 | +| 用户仲裁 vs coord 仲裁优先级 | 用户仲裁(U1-U4)优先级高于 coord 仲裁;coord 裁决与用户仲裁冲突时(如 §2.5 GraphQL vs U3),以用户仲裁为准,提请 coord 修正其裁决以保持一致 | +| 整改清单整体可执行性判断 | 若整改清单责任方错配占比超阈值(本次 71%,17/24 项),则整改清单整体不可直接执行,必须等 coord 重新发布修正版整改清单后方可推进 | + --- ## 二、模块经验 @@ -264,31 +289,78 @@ ### 2.6 data-ana(Python/FastAPI,P4) -| 场景 | 技术/规则 | -| -------------------- | ------------------------------------------------------------------------------------------------------------------- | -| 学情诊断 | ClickHouse 宽表查询,5s 内返回 | -| CDC 链路 | Debezium 监听 MySQL binlog → Kafka(`edu-cdc.next_edu_cloud.`)→ DataAna 消费写 ClickHouse | -| CDC 延迟监控 | Debezium 暴露 lag metrics,超阈值告警 | -| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) | -| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) | -| CDC 消费者实现 | `cdc_consumer.py` 用 aiokafka AIOKafkaConsumer,lifespan 启动 asyncio.create_task 后台运行 | -| ClickHouse 写入 | `clickhouse_client.upsert_student_dashboard()` 用 client.insert() 写宽表,client 为 None 时降级返回 False | -| Debezium 事件解析 | `before/after/source/op/ts_ms` 五字段,op=r(快照)/c(新增)/u(更新)/d(删除) | -| 多表关联缓存 | 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件),grades 事件触发时查缓存填充宽表 class_id | -| Consumer offset 重置 | `kafka-consumer-groups --reset-offsets --to-earliest --execute` 需先停消费者让 group 处于 Empty 状态 | -| structlog API | 24.x 用 `make_filtering_bound_logger(level)`,旧版 `make_filtering_logger` 已废弃 | +| 场景 | 技术/规则 | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 学情诊断 | ClickHouse 宽表查询,5s 内返回 | +| CDC 链路 | Debezium 监听 MySQL binlog → Kafka(`edu-cdc.next_edu_cloud.
`)→ DataAna 消费写 ClickHouse | +| CDC 延迟监控 | Debezium 暴露 lag metrics,超阈值告警 | +| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) | +| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) | +| CDC 消费者实现 | `cdc_consumer.py` 用 aiokafka AIOKafkaConsumer,lifespan 启动 asyncio.create_task 后台运行 | +| ClickHouse 写入 | `clickhouse_client.upsert_student_dashboard()` 用 client.insert() 写宽表,client 为 None 时降级返回 False | +| Debezium 事件解析 | `before/after/source/op/ts_ms` 五字段,op=r(快照)/c(新增)/u(更新)/d(删除) | +| 多表关联缓存 | 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件),grades 事件触发时查缓存填充宽表 class_id | +| Consumer offset 重置 | `kafka-consumer-groups --reset-offsets --to-earliest --execute` 需先停消费者让 group 处于 Empty 状态 | +| 5 张宽表对齐 | student_dashboard_view / student_errors / mastery_snapshot / attendance_logs / ai_usage_log(ai-allocation §5 强制) | +| ActionState 信封对齐 | Python 服务 `{success, data, degraded}` 必须改 `{success, data, error:{code, message, details?, traceId?}}`,degraded 作 details 子字段(004 §11.5 P0) | +| gRPC server 启用 | P4 启用 data-ana gRPC server 端口 50055(004 §4.2 矩阵),HTTP 3006 保留作 Gateway 直连降级 | +| DataScope 6 级过滤 | Repository 层注入 WHERE(SELF/CLASS/GRADE/SCHOOL/DISTRICT/ALL),iam `GetEffectiveDataScope` gRPC P4 补全(004 §15.3 #5) | +| Outbox 豁免 | 派生数据事件(MasteryUpdated / WarningTriggered / AIUsageRecorded)豁免 Outbox,允许直接 Kafka producer(004 §12.2 + §15.3 #6) | +| 掌握度算法 | WEIGHTED_MOVING_AVG(w_i=0.6^i)+ FORGETTING_CURVE(半衰期 30 天)取 max,近 5 次不足降级 SIMPLE_AVG | +| 预警阈值设计 | LOW_MASTERY(<0.4) / CRITICAL_LOW(<0.2) / SCORE_DROP(下降≥20%) / ABSENT_FREQUENT(≥3次/周) / TREND_DECLINE(连续3次降) | +| 预警去重 | 同一 (target_id, warning_type) 24h 内只触发一次,Redis 位图 `data_ana:warning:{target_id}:{type}:{date}` TTL 25h | +| CDC 水平扩展 | P4 单实例 + 内存 ExamCache LRU → P6 多实例 + Redis ExamCache + partition 扩容 + HPA | +| CDC 手动 commit | v2 改进:`enable_auto_commit=False` 手动 commit(at-least-once),commit 前确保 ClickHouse 写入成功 | +| ClickHouse 容量规划 | 5 宽表年增量 ~2.3GB(5000 学生×20 次/月),单节点可承载;P6 按 class_id hash 分片 + ReplicatedMergeTree | +| ClickHouse FINAL | ReplacingMergeTree 查询必须加 `FINAL` 或用 `argMax` 聚合,否则读到重复版本(当前实现未加,P0 整改) | +| 缓存键命名 | `data_ana:datascope:{user_id}` 5min / `data_ana:exam:{exam_id}` 30day / `data_ana:dedup:{event_id}` 7day | +| 4 端 Dashboard | teacher/student/parent/admin 各自 Dashboard API + 权限点 ANALYTICS_*_DASHBOARD(v2 新增 4 端权限) | +| SubscribeMasteryUpdate | gRPC server-streaming RPC 为 P5+ AI 个性化推荐预留实时推送通道(analytics.proto v2 提案) | +| GDPR 数据删除 | ClickHouse 无 UPDATE/DELETE,用 `ALTER TABLE ... DELETE WHERE student_id=?` 异步 mutation(P6 实现) | +| 字段脱敏 | student_id 在日志中 hash 化(structlog processor),PII 不入日志 | +| 事件版本化 | 事件 payload 加 `schema_version: "v1"` 字段,破坏性变更发 v2,consumer 双消费 6 个月过渡 | +| API 版本化 | HTTP `/analytics/v1/*` + gRPC `next_edu_cloud.analytics.v1`,破坏性变更新增 v2,旧版保留 6 个月 | +| Projection 演进 | P4 CDC 直连 → P4 后期双消费(CDC + 领域事件)→ P6 ClickHouse MaterializedView 自动聚合 | +| 测试策略 | pytest + Testcontainers(ClickHouse/Kafka 容器化)+ 单元 ≥80% + 集成 ≥60% + 契约 pact-python + E2E | +| Dockerfile 多阶段 | builder(uv sync --frozen)+ runtime(python:3.12-slim < 200MB)+ HEALTHCHECK 集成 | +| 配置项完整清单 | pydantic-settings 30+ 配置项,env_prefix="DATA_ANA_",含 ClickHouse/Kafka/iam gRPC/Redis/OTel/算法参数/预警阈值 | +| 降级模式 4 场景 | ClickHouse/Kafka/Redis/iam gRPC 不可用时返回骨架 + `error.details.degraded=true`,4 错误码 DATA_ANA_*_UNAVAILABLE | +| Python gRPC 实现 | grpc.aio + betterproto + ServerInterceptor 透传 W3C trace context,与 HTTP 共享同一 Application Service | +| structlog API | 24.x 用 `make_filtering_bound_logger(level)`,旧版 `make_filtering_logger` 已废弃 | ### 2.7 messaging(TS/NestJS,P5) -| 场景 | 技术/规则 | -| ------------- | ------------------------------------------------------------------ | -| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 | -| 通知批量化 | `createBatch(items)` 单次 INSERT,沿用旧项目 dispatcher 模式 | -| 多渠道 | 站内/SMS/邮件/微信,in_app 批量 + 其他渠道并行 | -| fan-out 分页 | `listByUserWithPagination(userId, page, pageSize)` 分页查询 | -| ES 降级 | ES_URL 未设置时 esClient=null,safeIndex/safeSearch 跳过返回空结果 | -| Push 推送降级 | PUSH_GATEWAY_URL 未设置或连接失败时 try/catch 跳过,不影响 DB 写入 | -| db 常量导出 | database.ts 导出 `db` 常量替代 `getDb()` 函数 | +| 场景 | 技术/规则 | +| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 | +| 通知批量化 | `createBatch(items)` 单次 INSERT,沿用旧项目 dispatcher 模式 | +| 多渠道策略 | ChannelStrategy 接口 + InApp/Email/Sms/Push 实现,新增渠道不改核心(ai10 设计) | +| fan-out 分页 | `listByUserWithPagination(userId, page, pageSize)` 分页查询 | +| ES 降级(单向) | ES_URL 未设置时 esClient=null,safeIndex/safeSearch 跳过返回空结果 | +| DB→ES 降级读 | DB 故障时 fallback 到 ES 读模型,响应头标 `X-Read-Model: es-degraded`(ai10 设计) | +| ES→DB 降级检索 | ES 故障时 search() fallback 到 DB LIKE,降级计数打 metrics(ai10 设计) | +| Push 推送降级 | PUSH_GATEWAY_URL 未设置或连接失败时 try/catch 跳过,不影响 DB 写入;PushChannel 失败降级 in_app | +| Kafka 消费幂等 | event_id → Redis SETNX `msg:idem:{event_id}` TTL 7d;Redis 不可用降级 msg_idempotency 表唯一键(ai10 设计) | +| 三层幂等防线 | L1 Redis SETNX / L2 msg_idempotency 表 / L3 notifications.source_event_id 唯一索引(ai10 设计) | +| 已读/未读位图 | Redis Bitmap `msg:unread:{userId}`,BITCOUNT 计数;Redis 不可用降级 DB COUNT(ai10 设计) | +| Outbox 强制(待 ai10 确认) | 004 §7.2 列 msg 生产 `edu.notification.events`,按 004 §12.2 强制 Outbox,P5 必须实现 msg_outbox_events 表 + Publisher worker | +| 12 类事件消费清单(待 ai10 确认) | 消费 iam(user.created/updated/deleted/role_changed + role.created/updated)+ core-edu(exam.published/assignment.submitted/assignment.graded/grade.recorded/attendance.recorded)+ data-ana(mastery.updated)共 12 类事件触发通知 | +| 4 类发布事件(待 ai10 确认) | 发布 NotificationSent / NotificationRead / NotificationRecalled / NotificationFailed 4 类事件,消费者 push-gateway + data-ana | +| 通知状态机(待 ai10 确认) | pending → sent → delivered → read / failed / recalled,含 6 状态转换 + 撤回场景预留 | +| NotificationTemplate 模板渲染(待 ai10 确认) | titleTemplate/contentTemplate 含 `{{variable}}` 占位符,RenderTemplate RPC 根据 variables 替换渲染,支持 locale 多语言 | +| NotificationPreference 用户偏好(待 ai10 确认) | 渠道开关 channels[] + 频率限制 frequency_limit + 静默时段 quiet_hours,5 维度配置矩阵 | +| Push Gateway 调用方向澄清(待 ai10 确认) | 004 §4.1 表述"PushGW→Msg 推送通道建立"歧义,实际方向是"msg → push-gateway (gRPC PushService.Push)",建议 coord 修正 004 §4.1 表述 | +| 频率限流(待 ai10 确认) | Redis 计数 `msg:ratelimit:{userId}:{type}:{yyyyMMddHH}` TTL 1h,按 NotificationPreference.frequency_limit 限制;Redis 不可用降级跳过限流 | +| 优雅关闭顺序(待 ai10 确认) | HTTP server → Kafka consumer → Outbox publisher → ChannelDispatcher → Redis → ES → DB pool → Tracer,8 步序列保证在途消息处理完成 | +| events.proto message 缺失阻塞(待 ai10 确认) | events.proto 缺 UserEvent/RoleEvent/NotificationEvent/MasteryEvent 4 类 message,msg 消费 iam/data-ana 事件阻塞,提请 coord 补齐 proto | +| 死信队列 | 消费失败超 3 次投递 `edu.notification.dlq`,告警人工介入(ai10 设计) | +| Outbox 事件发布 | 事务内写 msg_outbox + 业务表,独立 publisher 投递 `edu.notification.events`(参照 core-edu) | +| 模板渲染 | TemplateService + {{var}} 替换 + i18n locale;渲染失败降级默认文案记 MSG_TEMPLATE_RENDER_ERROR | +| 静默时段 | QuietHours 过滤非紧急通知,URGENT 优先级绕过静默(ai10 设计) | +| 短信限流配额 | Redis 滑动窗口限流 + 配额计数,超限返 MSG_RATE_LIMITED/MSG_QUOTA_EXCEEDED(ai10 设计) | +| 优雅关闭 | 统一到 LifecycleService,顺序 HTTP→Kafka→Outbox→ES→Redis→DB→Tracer(ai10 修订) | +| db 常量导出 | database.ts 导出 `db` 常量替代 `getDb()` 函数 | +| db 常量导出 | database.ts 导出 `db` 常量替代 `getDb()` 函数 | ### 2.8 push-gateway(Go,P5) @@ -334,21 +406,98 @@ ### 2.12 teacher-portal(微前端宿主,P1 测试页) -| 场景 | 技术/规则 | -| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| P1 测试页 | 单一 Next.js 应用,验证 classes CRUD 端到端链路 | -| API 调用 | `fetch(${API_BASE}/api/v1/classes)` + `Authorization: Bearer ${TEST_JWT}` | -| P1 测试 JWT | 开发工具生成 HS256 token,P2 起由 IAM 签发 RS256 | -| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal | -| P2 路由组 + AppShell | `app/(app)/layout.tsx` 用 AppShell 包裹受保护页;`/login` 与 `/` 不套壳 | -| P2 真实 JWT | 登录后 token 存 localStorage,`authHeaders()` 读 `Bearer ${getToken()}` | -| P2 视口驱动侧边栏 | AppShell fetch `/teacher/viewports` 渲染左侧导航,active 路由高亮 | -| P2 根路径重定向 | `app/page.tsx` 客户端组件 `router.replace(isAuthenticated() ? '/dashboard' : '/login')` | -| fetch headers 类型 | `authHeaders(): Record` 显式标注,避免 `{}` 与 `HeadersInit` 不兼容 | -| P2 前端权限校验等价物 | `usePermission().hasPermission()` Hook + `` 组件,镜像后端 `@RequirePermission()` 装饰器,禁止 `role === "xxx"` 硬编码(§3.1) | -| P2 MF Shell+Remote 架构 | teacher-portal:3000 为 Shell,student:3001/parent:3002/admin:3003 为 Remote,共享登录态/布局/组件库/权限体系 | -| P2 统一 API 请求层 | ApiClient 封装 401 自动刷新 token 轮转 + ActionState `{success,data,error}` 解析 + 错误码前缀路由 i18n,替代页面级 `authHeaders()+fetch` 重复 | -| P2 设计令牌三层模型 | primitive(原始色板)→ semantic-light/dark(语义)→ tailwind-theme(`@theme inline` 暴露 `bg-*`);ESLint `no-restricted-syntax` 禁 `#hex` + `design-tokens/no-hardcoded-fonts` 禁字面量字体(§3.10) | -| P2 5 层状态管理 | nuqs(URL 同步)/ TanStack Query(服务端缓存)/ Zustand(客户端业务状态)/ Zustand UI(UI 临时态)/ react-hook-form(表单) | -| P2 4 端错误码前缀对齐 | TP_=teacher-bff / SP_=student-bff / PP_=parent-bff / AP_=admin-bff,前端按前缀路由 i18n key | -| P2 ErrorBoundary | 路由级 `` 包裹避免白屏,搭配 `` 流式加载 | +| 场景 | 技术/规则 | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P1 测试页 | 单一 Next.js 应用,验证 classes CRUD 端到端链路 | +| API 调用 | `fetch(${API_BASE}/api/v1/classes)` + `Authorization: Bearer ${TEST_JWT}` | +| P1 测试 JWT | 开发工具生成 HS256 token,P2 起由 IAM 签发 RS256 | +| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal | +| P2 路由组 + AppShell | `app/(app)/layout.tsx` 用 AppShell 包裹受保护页;`/login` 与 `/` 不套壳 | +| P2 真实 JWT | 登录后 token 存 localStorage,`authHeaders()` 读 `Bearer ${getToken()}` | +| P2 视口驱动侧边栏 | AppShell fetch `/teacher/viewports` 渲染左侧导航,active 路由高亮 | +| P2 根路径重定向 | `app/page.tsx` 客户端组件 `router.replace(isAuthenticated() ? '/dashboard' : '/login')` | +| fetch headers 类型 | `authHeaders(): Record` 显式标注,避免 `{}` 与 `HeadersInit` 不兼容 | +| P2 MF Shell+Remote 架构 | teacher-portal:**4000** 为 Shell,student:**4001**/parent:**4002**/admin:**4003** 为 Remote(004 §1.2 + coord final §F1);MF 暴露 **AppShell 整体**(coord final §F10),各 Remote 自行决定内部布局 | +| P2 统一 API 请求层 | ApiClient 封装 401 自动刷新 token 轮转 + ActionState `{success,data,error:{code,message,details?,traceId}}` 解析 + 错误码前缀路由 i18n + trace_id 透传;**P2 起对接 GraphQL**(urql/apollo,coord final §F9/B1) | +| P2 统一 API 请求层 | ApiClient 封装 401 自动刷新 token 轮转 + ActionState `{success,data,error}` 解析 + 错误码前缀路由 i18n,替代页面级 `authHeaders()+fetch` 重复 | +| P2 设计令牌三层模型 | primitive(原始色板)→ semantic-light/dark(语义)→ tailwind-theme(`@theme inline` 暴露 `bg-*`);ESLint `no-restricted-syntax` 禁 `#hex` + `design-tokens/no-hardcoded-fonts` 禁字面量字体(§3.10) | +| P2 4 端错误码前缀对齐 | **BFF_TEACHER\_=teacher-bff / BFF_STUDENT\_=student-bff / BFF_PARENT\_=parent-bff**(coord final §B5,统一 `BFF_XXX_` 风格);admin-bff 待 P6 定;前端按前缀路由 i18n key | +| P2 4 端错误码前缀对齐 | TP_=teacher-bff / SP_=student-bff / PP_=parent-bff / AP_=admin-bff,前端按前缀路由 i18n key | +| P2 i18n key 命名 | **统一 `error..`**(如 error.iam.invalid_credentials、error.bffTeacher.validation_error,coord final §F4);翻译文件按域分 namespace | +| P2 权限点命名风格 | **统一 `_`**(如 DASHBOARD_VIEW、EXAM_READ、GRADE_READ),数据范围用后缀 `_OWN`/`_CHILD`(如 GRADE_READ_CHILD,coord final §F7) | +| P2 Token 存储 | **P2 用 localStorage**(accessToken + refreshToken,coord final §F12);httpOnly Cookie + CSRF Token **P6 单独评估**(非分阶段迁移,P2 直接采用最终方案的当前阶段形态) | +| P2 packages 归属 | **ai13 维护** packages/ui-tokens、ui-components、hooks(coord final §F8);coord 仅维护 shared-ts、contracts;ai13 需建立这 3 个共享包 | +| P2 GraphQL 客户端 | **P2 起前端用 urql 或 apollo**(coord final §F9),对接 teacher-bff GraphQL Yoga + DataLoader;废弃 REST fetch 模式;ApiClient 适配 GraphQL 操作类型(query/mutation/subscription) | +| P2 视口端点对齐 | **GET /iam/permissions/effective**(非 /iam/effective-permissions,coord final §F6/I8)+ GET /iam/viewports + GET /iam/roles;parent 子女切换 POST /parent/children/:childId/select | + +### 2.13 parent-portal(家长端微前端 Remote,P4) + +> parent-portal 复用 teacher-portal Shell 暴露的 AppShell / usePermission / ApiClient / ErrorBoundary / 设计令牌 / A11y 工具集,本节仅记录 parent-portal **特有**的"场景→技术"映射,通用前端映射见 §2.12。 + +| 场景 | 技术/规则 | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P4 端口分配 | parent-portal = **4002**(004 §1.2 强制 4 端 4000-4003;ai07 初稿误用 3002 与 iam 冲突,ai15 修订) | +| P4 MF Remote 角色 | next.config.js 配 `@module-federation/nextjs-mf` 暴露 `./ChildSwitcher` 等家长端特有组件,复用 Shell 提供的 `AppShell`/`usePermission`/`ApiClient`/`RequirePermission` | +| P4 DataScope CHILDREN | 家长仅能查看自己绑定子女的数据(004 §5.4 L0 SELF 变体),前端通过 ApiClient 透传 userId + childId,下游服务按 DataScope 过滤 | +| P4 多子女切换 ChildSwitcher | Zustand slice(`selectedChildId`)+ 切换后 `queryClient.invalidateQueries({ queryKey: ['child', prevId] })`;多子女 Tab(≤3)或下拉(≥4);空态/单子女/多子女三态分支 | +| P4 多子女数量边界 | 0 子女(EmptyChildState 引导绑定)→ 1 子女(隐藏切换器)→ 2-3(Tab Bar)→ 4-10(下拉)→ >10(搜索 + 列表);超出 10 触发性能预警 | +| P4 通知偏好三维矩阵 | `Record>>`;react-hook-form + Zod 校验;默认值矩阵(成绩发布=邮件+短信、作业批改=邮件、教师沟通=短信、学校通知=邮件);渠道可用性校验 | +| P4 跨标签同步 | BroadcastChannel API(`parent-portal-sync`)+ localStorage 持久化 + LWW(Last-Write-Wins)冲突解决;同步 selectedChildId / 通知偏好 / 已读状态 | +| P4 隐私合规(COPPA/FERPA/PIPL) | 未成年人数据最小化采集;通知偏好默认 opt-in;数据保留 7 年后自动归档;前端不缓存敏感字段到 localStorage(仅 childId + 已读位图) | +| P4 i18n 5 语言含 RTL | next-intl 支持 zh-CN / en-US / zh-TW / ja-JP / ar-SA;ar-SA 用 `dir="rtl"` + Tailwind `rtl:` 变体;翻译文件按域分 namespace(parent/grades/notifications/preferences) | +| P4 PWA + Service Worker | manifest.json(name/short_name/icons/theme_color)+ Service Worker 缓存策略(App Shell precache + API stale-while-revalidate + 离线兜底页);P5+ 支持离线查看子女成绩缓存 | +| P4 API 契约版本管理 | 主版本 URL `/api/v1/*` → `/api/v2/*` + 子版本响应头 `X-API-Version` + Deprecation/Sunset 头埋点 + 低带宽场景 `X-Fields` 字段裁剪(GraphQL 风格,BFF REST 透传) | +| P4 性能预算 | Bundle ≤ 80KB JS / 20KB CSS / 200KB 总;recharts 懒加载;MF shared 单例避免 React 重复打包;dynamic import 非首屏组件;预加载子女数据 | +| P4 监控与降级 | 10 项前端指标(MF 加载失败率/ApiClient 错误率/Web Vitals/切换延迟等)+ 9 项降级策略(MF 失败回退独立页/BFF 5xx 降级缓存/WS 重试指数退避等) | +| P4 模块演化与解耦 | 拆分触发条件(Bundle > 120KB / 团队 > 3 人 / 独立部署需求);拆分后 MF 配置从 Shell 暴露;状态管理迁移路径(Zustand → Server Component + Server Actions);MF 升级路径(MF 2.0 → 3.0) | +| P4 iam 家长-学生关联 P0 阻塞 | iam 缺 `GetChildrenByParent` proto RPC + `GET /iam/children` REST + `iam_student_guardians` 表;ai02 补全前 parent-portal 仅能假设单子女硬编码 childId 开发调试 | +| P4 BFF 错误码前缀 | **BFF_PARENT\_=parent-bff**(coord final §B5,统一 `BFF_XXX_` 风格);前端按 `BFF_PARENT_*` 前缀路由 i18n key,与 BFF_TEACHER_/BFF_STUDENT_ 对齐 | + +### 2.14 parent-bff(TS/NestJS,P4) + +> parent-bff 是 BFF 聚合层,无 DB,无聚合根,无 @RequirePermission(U4 仲裁豁免)。本节记录 parent-bff 特有的"场景→技术"映射。 + +| 场景 | 技术/规则 | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| BFF 聚合层定位 | 只聚合不持状态,无 DB,无聚合根,无 @RequirePermission(U4 仲裁豁免,权限由 Gateway JWT + 下游服务负责) | +| GraphQL Yoga 端点 | POST /graphql(GraphQL over HTTP),对前端 GraphQL,对下游 gRPC(U3 仲裁 GraphQL P2 立即引入,parent-bff P4 直接用 GraphQL) | +| DataScope=CHILDREN 越权校验 | ChildGuard 在 Resolver 执行前校验 requestedChildId ∈ iam.GetChildrenByParent(parentId),不绑定抛 BFF_PARENT_CHILD_NOT_BOUND(403) | +| 多子女切换方案 A | 前端管理 childId(Zustand slice),BFF 无状态,ParentSession 仅请求级值对象不持久化;selectChild mutation 仅记录审计日志 | +| 并行 gRPC 编排 | Promise.allSettled + 降级策略(iam 失败返回 user=null,core-edu 失败返回 grades=null),部分失败标记 partial=true | +| DataLoader 批量去重 | per-request 实例,按 parentId 批量取孩子列表、按 studentId 批量取成绩/作业/考试,防 N+1 | +| Redis 聚合缓存 | 30s TTL(coord 推断 60s,ai05 调整为 30s + 主动失效),key=bff:parent:{userId}:{queryHash},singleflight 防击穿 | +| 错误码前缀 | BFF_PARENT__(coord 仲裁 C1,对齐 004 §11.4 BFF 在前风格,不是 PARENT_BFF__) | +| 端口分配 | HTTP 3010,不暴露 gRPC(coord 仲裁 C2,BFF 对下游走 gRPC,对上游仅 HTTP/GraphQL) | +| 下游依赖 | iam + core-edu + data-ana + msg(coord 仲裁 C6 扩展,与 student-bff 对齐) | +| P0 阻塞 | iam 缺 GetChildrenByParent RPC + iam_student_guardians 表,ai02 补全前 parent-bff 无法启动 | +| push-gateway 调用 | 走 HTTP /internal/push(U2 仲裁豁免 gRPC,msg 调用方改用 HTTP) | +| Kafka topic 命名 | edu.notification.sent/read/recalled/failed(coord 仲裁 C5,废弃 edu.notification.events 抽象名) | +| /readyz 下游探针 | P4 添加并行 ping iam + core-edu + data-ana + msg 的 /healthz(coord 推断 BFF 直接 ok,ai05 调整为 P4 加下游探针) | +| GraphQL complexity 防御 | depth limit(≤7)+ query cost analysis(≤1000)+ persisted queries(P6 预留) | +| 优雅关闭 | HTTP server → Redis → gRPC clients → Tracer(BFF 无 DB/Kafka,关闭顺序简化) | +| ParentSession 值对象 | parentId/roles/dataScope:CHILDREN/requestedChildId/traceId,每次请求重建不跨请求(004 §12.1 无状态约束) | +| 通知偏好配置 | NotificationPreference(渠道开关 channels[] + 频率限制 frequency_limit + 静默时段 quiet_hours),P4 通过 msg 服务管理 | + +### 2.15 student-portal(学生端微前端 Remote,P3) + +> student-portal 复用 teacher-portal Shell 暴露的 AppShell / usePermission / ApiClient / ErrorBoundary / 设计令牌 / A11y 工具集,本节仅记录 student-portal **特有**的"场景→技术"映射,通用前端映射见 §2.12。 + +| 场景 | 技术/规则 | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| P3 端口分配 | student-portal = **4001**(004 §1.2 强制 4 端 4000-4003;ai07 初稿误用 3001 与 iam 冲突,ai14 修订) | +| P3 MF Remote 角色 | next.config.js 配 `@module-federation/nextjs-mf`(name: `student_app`,port 4001)暴露 `./pages` + `./ExamTaking`,复用 Shell 的 `AppShell`/`usePermission`/`ApiClient` | +| P3 DataScope SELF | 学生仅看自己数据(004 §5.4 L0),前端**无班级/年级/学校切换器**(与 teacher-portal L1-L5 核心差异),路由参数不含 classId/gradeId/schoolId | +| P3 错误码前缀统一 | core-edu 子域统一 `CORE_EDU_`(移除 `EXAMS_`/`HOMEWORK_`/`GRADES_` 独立前缀);BFF 前缀 `BFF_STUDENT_`(coord §5.2 裁决,从 `STUDENT_BFF_` 迁移) | +| P3 考试防作弊 useAntiCheat | `visibilitychange` 切屏检测 + `blur` 失焦 + `copy`/`paste`/`contextmenu` 禁止 + 违规计数(maxViolations=3)+ 超限自动交卷;前端层仅辅助,最终权威由后端考试时长 + 服务端时间校准保证 | +| P3 服务器时间同步 | `useServerTimeSync` Hook:RTT 计算 `offset = (t1 + rtt/2) - serverTime` + 5min 重同步周期;`CountdownTimer` 用 `role="timer"` + `aria-live="polite"` 朗读剩余时间 | +| P3 考试作答状态机 | NotStarted→InProgress→Paused(切屏警告)→AutoSubmitting(倒计时归零)→Submitting→Submitted;Zustand L3 + localStorage 持久化草稿,自动保存每 30s + blur 时保存 | +| P3 考试页面 CSP iframe 防嵌套 | 考试路由 `frame-ancestors 'none'` + `X-Frame-Options: DENY`;其他路由允许 Shell 同源嵌入 | +| P3 考试 chunk 性能预算 | 考试作答 chunk < 200KB(动态 import 隔离);总 Bundle < 300KB;LCP < 2.5s / CLS < 0.1 / INP < 200ms | +| P3 移动端一等公民 | 学生端高频移动场景:Tailwind 响应式断点 + viewport meta + 触摸优化(按钮 ≥ 44px);考试页面支持横屏锁定 | +| P3 A11y 考试专项 | WCAG 2.2 AA:键盘导航作答(Tab/Enter/方向键)+ `aria-live` 倒计时 + 焦点陷阱(考试中不可 Tab 到地址栏)+ 屏幕阅读器兼容 | +| P3 考试草稿多设备同步预留 | 当前 localStorage 单设备;P5+ 预留草稿同步(LWW 冲突解决 + WebSocket 推送 + version 字段) | +| P3 Feature Flag 阶段化 | P3-P5 用环境变量(`NEXT_PUBLIC_FEATURE_*`);P6+ 预留 Consul 配置中心动态开关 | +| P3 PWA 离线壳预留 | P5+ Service Worker 缓存考试壳,断网时仍可作答,恢复后自动提交 | +| P3 MF 降级与版本协商 | Remote `remoteEntry.js` 加载失败 → fallback 独立页面 + 错误提示;共享依赖版本不匹配 → 运行时协商降级 | +| P3 AI 答疑 SSE 流式(P5) | `POST /student/ai/stream-chat` SSE 流式响应 + `streamingText` 逐字渲染 + 打字机效果 + AbortController 取消 | +| P3 权限点命名 | 与 student-bff §3.2 对齐 `STUDENT_*_READ` 风格(`STUDENT_DASHBOARD_READ`/`STUDENT_EXAM_READ`/`STUDENT_HOMEWORK_SUBMIT`/`STUDENT_GRADE_READ` 等) |