Merge worktree branch merge-15-modules-to-main-5ug5xJ
This commit is contained in:
@@ -1,45 +1,246 @@
|
||||
# admin-portal 工作排期
|
||||
|
||||
> 负责人:ai16
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/admin-portal_contract.md](../contracts/admin-portal_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/admin-portal_contract.md](../contracts/admin-portal_contract.md)、[ai-allocation.md §5 ai16](../../ai-allocation.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
> 说明:本排期基于 GraphQL(ARB-001 admin 命名空间)+ 端口 4003 + ai16 归属,已对齐 [matrix.md](../matrix.md) 与 ai-allocation.md。01/02 文档中 REST/3003/ai07 表述待 coord 仲裁后修订(见 [objections/admin-portal_issue.md](../objections/admin-portal_issue.md))。
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
admin-portal 是管理端微前端,通过 MF Remote 接入主应用,覆盖用户管理、角色权限、审计日志等场景。全阶段目标:P2 MF Remote 骨架 → P3 用户管理+角色权限+审计日志 → P4-P6 持续优化。
|
||||
admin-portal 是管理端微前端(MF Remote),挂载到 teacher-portal Shell,覆盖**用户管理 / 角色权限管理 / 学校设置 / 组织管理 / 审计日志消费**等管理场景(ai-allocation §5 ai16)。复用 teacher-bff GraphQL endpoint 的 **admin 命名空间**(ARB-001),admin 权限点使用 `ADMIN_` 前缀(ai-allocation §5)。
|
||||
|
||||
全阶段目标:
|
||||
|
||||
- **P2**:MF Remote 骨架(next.config.js + MF 配置 + 独立壳渲染 + MSW mock)
|
||||
- **P3**:用户管理 + 角色权限管理(admin 命名空间 Query/Mutation)
|
||||
- **P4**:组织管理 + 学校设置 + 班级/教师/学生全局管理
|
||||
- **P5**:审计日志消费(auditLogs Query 聚合 iam AuditEvent)+ WebSocket 实时通知
|
||||
- **P6**:硬化(A11y WCAG 2.2 AA / Web Vitals / OTel / 测试覆盖率 ≥ 80% / Dockerfile 多阶段)
|
||||
|
||||
> 跨阶段:开发期间全部经 MSW mock(见 [contract §4](../contracts/admin-portal_contract.md)),上游就绪后逐项切换真实。
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai16 admin-portal 全阶段排期
|
||||
title ai16 admin-portal 全阶段排期(P2-P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a16a, 2026-07-10, Xd
|
||||
section P2 骨架
|
||||
16.1 MF Remote 骨架(next.config+独立壳) :crit, a16a, 2026-07-10, 2d
|
||||
16.2 MSW handlers+fixtures+mock JWT :a16b, after a16a, 2d
|
||||
16.3 接入Shell共享(GraphQLProvider/AppShell/useAuth) :crit, a16c, after a16a, 2d
|
||||
|
||||
section P3 用户/角色权限
|
||||
16.4 用户管理页(users CRUD+UserManagementTable) :crit, a16d, after a16c, 3d
|
||||
16.5 角色权限矩阵(RolePermissionMatrix+updateRolePermissions) :a16e, after a16d, 3d
|
||||
16.6 权限点管理+视口配置(ADMIN_前缀) :a16f, after a16e, 2d
|
||||
|
||||
section P4 组织/学校/全局实体
|
||||
16.7 组织树管理(organization) :a16g, after a16f, 2d
|
||||
16.8 学校设置(system) :a16h, after a16g, 2d
|
||||
16.9 班级/教师/学生全局管理(adminClasses/Teachers/Students) :a16i, after a16h, 3d
|
||||
|
||||
section P5 审计日志/实时通知
|
||||
16.10 审计日志页(auditLogs Query+筛选/导出) :crit, a16j, after a16i, 3d
|
||||
16.11 WebSocket实时通知(审计告警/异常登录) :a16k, after a16j, 2d
|
||||
16.12 管理仪表盘(adminDashboard聚合) :a16l, after a16k, 2d
|
||||
|
||||
section P6 硬化
|
||||
16.13 A11y WCAG 2.2 AA审计+修复 :crit, a16m, after a16l, 3d
|
||||
16.14 Web Vitals+OTel browser SDK接入 :a16n, after a16m, 2d
|
||||
16.15 Vitest单测+Playwright E2E(≥80%) :crit, a16o, after a16n, 3d
|
||||
16.16 Dockerfile多阶段+/api/health+/api/ready :a16p, after a16o, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai16 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> 关键路径(crit):16.1 → 16.3 → 16.4 → 16.10 → 16.13 → 16.15。总工期约 34 个工作日。
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### P2 阶段
|
||||
|
||||
#### 16.1 MF Remote 骨架(next.config + 独立壳)
|
||||
|
||||
- **负责人**:ai16
|
||||
- **交付物**:⚠️ 由 ai16 自行补充
|
||||
- **依赖**:见 [contracts/admin-portal_contract.md](../contracts/admin-portal_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai16 自行补充
|
||||
- **依赖**:teacher-portal Shell MF exposes/shared 配置就绪(ARB-002,ai13 P2 交付)
|
||||
- **交付物**:
|
||||
- `apps/admin-portal/next.config.js`(NextFederationPlugin,Remote 角色,`name: 'admin_app'`,`filename: 'static/chunks/remoteEntry.js'`,`exposes: { './AdminApp': './src/app/admin-app.tsx' }`,shared 全部 singleton)
|
||||
- `apps/admin-portal/src/app/admin-app.tsx`(独立壳入口,供 Shell 动态加载 + 独立 dev 渲染)
|
||||
- `apps/admin-portal/src/app/standalone.tsx`(独立 dev 壳:自实现 AppShell 占位 + mock providers,供 `pnpm --filter admin-portal dev` 在 :4003 独立预览)
|
||||
- `tsconfig.json`(沿用 tsconfig.base.json)、`tailwind.config.js`(引入 `@edu/ui-tokens`)、`package.json`
|
||||
- **验收标准**:
|
||||
- `pnpm --filter admin-portal dev` 在 :4003 启动,独立壳渲染首页 + 导航占位
|
||||
- MF `remoteEntry.js` 可被 Shell `dynamic import` 加载(feature flag `NEXT_PUBLIC_MF_ENABLED` 控制)
|
||||
- shared 单例配置通过(react/react-dom/urql/graphql/@edu/* 全 singleton)
|
||||
|
||||
#### 16.2 MSW handlers + fixtures + mock JWT
|
||||
|
||||
- **负责人**:ai16
|
||||
- **依赖**:无(mock 先行)
|
||||
- **交付物**:
|
||||
- `apps/admin-portal/src/mocks/handlers.ts`(拦截 `POST /api/admin/graphql` + `POST /api/auth/login` + `GET /ws`)
|
||||
- `apps/admin-portal/src/mocks/fixtures/*.json`(50 用户 / 5 角色 / 20 班级 / 50 教师 / 1200 学生 / 100 审计日志 / 仪表盘统计)
|
||||
- mock JWT(admin 角色,permissions=["*"],httpOnly cookie)
|
||||
- **验收标准**:
|
||||
- `NEXT_PUBLIC_API_MOCKING=enabled` 时所有请求被 MSW 拦截返回 mock
|
||||
- GraphQL mock 按 operationName 返回对应 fixture(与 teacher-bff admin namespace mock 数据一致)
|
||||
|
||||
#### 16.3 接入 Shell 共享(GraphQLProvider/AppShell/useAuth)
|
||||
|
||||
- **负责人**:ai16
|
||||
- **依赖**:ARB-002 Shell 暴露清单就绪(ai13)
|
||||
- **交付物**:
|
||||
- `AdminApp` 通过 MF `import from 'teacher/GraphQLProvider'`、`'teacher/AppShell'`、`'teacher/useAuth'`、`'teacher/usePermission'`、`'teacher/useGraphQLClient'`
|
||||
- `<AppShell scope="admin">` 渲染管理端视口导航
|
||||
- GraphQL client 经 `useGraphQLClient()` 获取(**不使用 useApi/ApiClient**,对齐 ARB-002)
|
||||
- **验收标准**:
|
||||
- urql client 单例跨 Remote 共享(与 Shell 同一实例)
|
||||
- `currentUser` Query 返回 mock 管理员信息
|
||||
- admin 角色校验:非 admin 角色重定向到登录页
|
||||
|
||||
### P3 阶段
|
||||
|
||||
#### 16.4 用户管理页(users CRUD + UserManagementTable)
|
||||
|
||||
- **负责人**:ai16
|
||||
- **依赖**:teacher-bff admin 命名空间 `adminUsers`/`createUser`/`updateUser`/`deleteUser` schema(ISSUE-005 待 coord 仲裁 ai03 补齐)
|
||||
- **交付物**:
|
||||
- `/admin/users` 列表页(UserManagementTable:邮箱/姓名/角色/状态/数据范围/最后登录 + 筛选/分页/批量操作)
|
||||
- `/admin/users/new` + `/admin/users/:id` 表单页(react-hook-form + zodResolver)
|
||||
- admin 业务 Hooks:`useUsers` / `useUser` / `useCreateUser` / `useUpdateUser` / `useToggleUserStatus`(urql query/mutation)
|
||||
- **验收标准**:
|
||||
- 列表筛选/分页正常,mutation 后 invalidate 刷新
|
||||
- DataScope L3-L5 越权由后端强制,前端 `AdminDataScopeFilter` 仅作 UI 提示
|
||||
- 权限:`IAM_USER_READ` / `IAM_USER_CREATE` / `IAM_USER_UPDATE`
|
||||
|
||||
#### 16.5 角色权限矩阵(RolePermissionMatrix + updateRolePermissions)
|
||||
|
||||
- **负责人**:ai16
|
||||
- **依赖**:teacher-bff `adminRoles` / `updateRolePermissions` schema
|
||||
- **交付物**:
|
||||
- `/admin/roles` 页(RolePermissionMatrix:行=角色,列=权限按 resource 分组;checkbox 网格)
|
||||
- 系统预置角色只读(isSystem=true 不可编辑/删除)
|
||||
- Hooks:`useRoles` / `useRole` / `useCreateRole` / `useUpdateRolePermissions`
|
||||
- **验收标准**:
|
||||
- 勾选触发 `updateRolePermissions` Mutation,乐观更新 + 失败回滚
|
||||
- 删除前校验 userCount > 0 时禁用删除并提示
|
||||
|
||||
#### 16.6 权限点管理 + 视口配置(ADMIN_ 前缀)
|
||||
|
||||
- **负责人**:ai16
|
||||
- **依赖**:`packages/contracts/src/permissions.ts`(coord 维护,admin 权限点 `ADMIN_*` 前缀)
|
||||
- **交付物**:
|
||||
- `/admin/permissions` 只读列表(DataTable,按 resource 分组)
|
||||
- `/admin/viewports` 视口配置编辑器(ViewportConfigEditor:@dnd-kit 拖拽排序 + 权限绑定 + scope/isVisible)
|
||||
- Hooks:`usePermissions`(30min 缓存)/ `useViewportsConfig` / `useUpdateViewport`
|
||||
- **验收标准**:
|
||||
- 权限点常量来自 `@edu/contracts`(不硬编码)
|
||||
- 视口配置保存后 AppShell 导航即时刷新(invalidate viewports queryKey)
|
||||
|
||||
### P4 阶段
|
||||
|
||||
#### 16.7 组织树管理(organization)
|
||||
|
||||
- **交付物**:`/admin/organization` 页(树形 + DataTable,school/grade/class 层级 CRUD)
|
||||
- **验收标准**:树形展开/折叠 + 拖拽调整层级(后端校验)+ DataScope 过滤
|
||||
|
||||
#### 16.8 学校设置(system)
|
||||
|
||||
- **交付物**:`/admin/system` 页(学校基础信息 / 学年学期 / 系统参数表单)
|
||||
- **验收标准**:表单 zod 校验 + 保存后 toast 反馈;仅 L5 系统管理员可编辑
|
||||
|
||||
#### 16.9 班级/教师/学生全局管理
|
||||
|
||||
- **交付物**:`/admin/classes` / `/admin/teachers` / `/admin/students` 三个全局管理页(adminClasses/adminTeachers/adminStudents Query)
|
||||
- **验收标准**:跨班级/跨年级全局视角(区别于 teacher-portal 的教师自身视角);DataScope 控制可见范围
|
||||
|
||||
### P5 阶段
|
||||
|
||||
#### 16.10 审计日志页(auditLogs Query + 筛选/导出)
|
||||
|
||||
- **负责人**:ai16
|
||||
- **依赖**:teacher-bff 消费 `edu.iam.audit.created` Kafka 并暴露 `auditLogs` Query(ISSUE-007 待 coord 修正 matrix.md §4 消费方为 teacher-bff)
|
||||
- **交付物**:
|
||||
- `/admin/audit-logs` 页(DataTable:时间/操作人/action/resource/ip + 筛选:action/user/dateRange + 导出 CSV)
|
||||
- Hooks:`useAuditLogs`(含游标分页)
|
||||
- **验收标准**:
|
||||
- 审计日志经 GraphQL 查询(**非直接订阅 Kafka**,对齐 contract §2.2)
|
||||
- 100 条 mock 审计日志覆盖 create/update/delete/login/logout/permission_change
|
||||
|
||||
#### 16.11 WebSocket 实时通知(审计告警/异常登录)
|
||||
|
||||
- **交付物**:
|
||||
- 接入 push-gateway `GET /ws`(与 parent-portal 同类契约一致,对齐 ISSUE-006)
|
||||
- 审计告警 / 异常登录 / 系统异常 toast + 通知中心入口
|
||||
- **验收标准**:
|
||||
- mock-socket 每 30s 推送 1 条 mock 系统通知
|
||||
- WebSocket 断线自动重连
|
||||
|
||||
#### 16.12 管理仪表盘(adminDashboard 聚合)
|
||||
|
||||
- **交付物**:`/admin/dashboard` 页(recharts:total_teachers / total_students / school_avg_score / 趋势图)
|
||||
- **验收标准**:adminDashboard Query 返回聚合数据;60s 轮询监控指标 + 5min 轮询统计
|
||||
|
||||
### P6 阶段(硬化)
|
||||
|
||||
#### 16.13 A11y WCAG 2.2 AA 审计 + 修复
|
||||
|
||||
- **交付物**:eslint-plugin-jsx-a11y(error 级)+ 手动审计修复
|
||||
- **验收标准**:0 个 error 级违规
|
||||
|
||||
#### 16.14 Web Vitals + OTel browser SDK 接入
|
||||
|
||||
- **交付物**:`next/web-vitals` → `POST /api/admin/web-vitals`;OTel browser SDK(复用 Shell TracerProvider,scope='admin')
|
||||
- **验收标准**:LCP/CLS/FID/TTFB 上报 + trace 上报 collector
|
||||
|
||||
#### 16.15 Vitest 单测 + Playwright E2E(覆盖率 ≥ 80%)
|
||||
|
||||
- **交付物**:`apps/admin-portal/src/**/*.test.tsx` + `e2e/*.spec.ts`
|
||||
- **验收标准**:覆盖率 ≥ 80%,E2E 覆盖登录 → dashboard → 用户 CRUD → 审计日志主链路
|
||||
|
||||
#### 16.16 Dockerfile 多阶段 + /api/health + /api/ready
|
||||
|
||||
- **交付物**:`apps/admin-portal/Dockerfile`(builder + runtime,非 root)+ `src/app/api/health/route.ts` + `src/app/api/ready/route.ts`
|
||||
- **验收标准**:`/api/health` 返回 200;`/api/ready` 检查 Shell URL 可达;Dockerfile HEALTHCHECK 配置
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai16 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai16 自行补充
|
||||
### 4.1 我依赖(上游就绪标志)
|
||||
|
||||
- [ ] teacher-portal Shell MF exposes/shared 就绪(ai13,ARB-002)—— Remote 挂载前提
|
||||
- [ ] teacher-bff GraphQL :3003 启用(ai03)—— admin 命名空间可用(**ISSUE-005 待 ai03 补齐 schema**)
|
||||
- [ ] api-gateway HTTP :8080 启用 + JWT 验签 + admin 角色校验(ai01)
|
||||
- [ ] iam gRPC 50052 启用(ai06)—— 用户/角色/审计日志数据来源
|
||||
- [ ] `edu.iam.audit.created` topic 有事件发布(ai06)—— 审计日志来源(经 teacher-bff 消费)
|
||||
- [ ] push-gateway WebSocket :8081/ws 启用(ai02)—— 实时通知(**ISSUE-006 待 coord 仲裁**)
|
||||
- [ ] `packages/contracts` admin 权限点 `ADMIN_*` 常量就绪(coord)
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
- [ ] admin-portal dev server :4003 启用
|
||||
- [ ] MF Remote 可被 AppShell 加载(暴露 `./AdminApp` 模块)
|
||||
- [ ] 独立壳渲染(首页 + 导航 + 路由守卫 + admin 角色校验)
|
||||
- [ ] 登录流程可用(复用 Shell `/login`,admin 角色校验后重定向 `/admin/dashboard`)—— **不自行实现登录页**(对齐 ARB-002 §2.3)
|
||||
- [ ] GraphQL 查询可执行(currentUser / adminDashboard / auditLogs 返回数据)
|
||||
- [ ] 用户/角色 CRUD 可执行(createUser / updateRolePermissions)
|
||||
- [ ] WebSocket 通知可接收
|
||||
|
||||
---
|
||||
|
||||
## §5 风险跟踪
|
||||
|
||||
| 风险 | 影响 | 缓解 | 状态 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| teacher-bff admin namespace schema 缺失(ISSUE-005) | P3+ 全部业务页阻塞 | 提请 coord 仲裁 ai03 在 P3 启动前补齐;P2 用 MSW mock 推进 | ⏳ 待仲裁 |
|
||||
| 01/02 文档 REST/3003/ai07 与仲裁不一致 | 误导实现 | 已提 7 项异议(ISSUE-001~007),以 contract.md 为修订基准 | ⏳ 待仲裁 |
|
||||
| Shell 延迟暴露 GraphQLProvider | P2 骨架阻塞 | P2 用独立壳 + mock providers,Shell 就绪后切换 | ⏳ |
|
||||
| MF SSR 对齐复杂 | Remote SSR 上下文依赖 Shell | 优先 CSR,SSR 仅首屏 dashboard | ⏳ |
|
||||
|
||||
@@ -1,45 +1,264 @@
|
||||
# ai 工作排期
|
||||
|
||||
> 负责人:ai12
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/ai_contract.md](../contracts/ai_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/ai_contract.md](../contracts/ai_contract.md)、[objections/ai_issue.md](../objections/ai_issue.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
> 阶段归属:批次 4(P5),见 [workline.md §1](../workline.md) 甘特图 `b4c: ai12 ai服务 gRPC 50058, after b3a, 13d`
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
ai 是智能服务,提供 AiService(6 个 RPC),结合 Elasticsearch 检索与 LLM 网关实现智能问答与推荐。全阶段目标:P2 ES 接入+LLM 网关 → P3 AiService 6 RPC → P4-P6 持续优化。
|
||||
ai 是 D6 智能洞察领域的"生成子域"服务(Python/FastAPI,无状态),统一封装 LLM 调用(多 Provider 适配 + 故障切换 + 限流 + 成本控制),提供聊天 / 出题 / 表达优化 / 备课工作流四类 AI 能力。通过 gRPC 查询 content 知识点与 data-ana 学情,通过 Kafka 外发用量计费事件供 data-ana 落 ClickHouse。
|
||||
|
||||
**端口**:HTTP 3008 + gRPC 50058([port-allocation.md](../../../../infra/port-allocation.md) §3/§5 权威源)
|
||||
|
||||
**P5 全阶段目标**(退出标准,对应 [pending-features P5](../../../architecture/roadmap/pending-features.md) + ai-allocation §5):
|
||||
|
||||
1. LLM Provider 适配器模式(OpenAI/百川/Anthropic/本地 Ollama,统一接口 + 故障切换)
|
||||
2. SSE / gRPC 流式响应(题目逐字生成 + 前端打字机效果)
|
||||
3. 出题 Prompt 模板管理(YAML + Jinja2,模板 CRUD + 参数注入:年级/学科/难度/知识点)
|
||||
4. 备课工作流 4 步编排(分析学情 → 推荐知识点 → 生成题目 → 教师审核 → 入库)
|
||||
5. 用量计费 / 频率限制(按用户 / 按 IP / 按 token / 按学校配额)
|
||||
6. 生成质量门禁(RuleValidator + LLMJudge,评估通过率 > 80%)
|
||||
7. 安全层(PII 脱敏 + Prompt 注入防御 + 输出内容审核)
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P5,13 天)
|
||||
|
||||
> 对齐 [workline.md §1](../workline.md) 批次 4:`ai12 ai服务 gRPC 50058 :b4c, after b3a, 13d`(b3a = content P4 就绪后启动)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai12 ai 全阶段排期
|
||||
title ai12 ai 服务 P5 排期(13 天)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a12a, 2026-07-10, Xd
|
||||
section M14 基础架构(第1-4天)
|
||||
12.1 LLMProvider抽象+4适配器 :crit, a12a, 2026-07-10, 2d
|
||||
12.2 ProviderFailoverChain+CircuitBreaker :a12b, after a12a, 1d
|
||||
12.3 gRPC server(Chat+StreamChat)+ActionState整改 :crit, a12c, after a12a, 2d
|
||||
12.4 Redis多维度限流+Dockerfile多阶段 :a12d, after a12b, 1d
|
||||
|
||||
section M15 出题核心(第5-9天)
|
||||
12.5 PromptTemplateService+Jinja2渲染 :crit, a12e, after a12c, 2d
|
||||
12.6 GenerateQuestion+StreamGenerateQuestion逐字流式 :crit, a12f, after a12e, 2d
|
||||
12.7 RuleValidator+LLMJudge+QualityGate评估三道防线 :a12g, after a12f, 1d
|
||||
12.8 UsageRecorder+KafkaProducer+QuotaEnforcer :a12h, after a12g, 1d
|
||||
12.9 PIIRedactor+InputSanitizer+OutputModerator安全层 :a12i, after a12g, 1d
|
||||
|
||||
section M16 备课工作流(第10-13天)
|
||||
12.10 gRPC client(content/data-ana/iam)+interceptor :crit, a12j, after a12f, 1d
|
||||
12.11 LessonPreparationWorkflow 4步编排+状态机 :crit, a12k, after a12j, 2d
|
||||
12.12 WorkflowStateStore(Redis)+教师审核+content入库 :a12l, after a12k, 1d
|
||||
12.13 集成测试+契约测试+文档同步+arch:scan :a12m, after a12l, 1d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai12 接管后必须自行细化为完整 P2-P6 排期。
|
||||
**关键路径**(红色 crit):LLMProvider 抽象 → gRPC server → PromptTemplateService → GenerateQuestion → gRPC client → 备课工作流编排
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### M14 基础架构(第1-4天)
|
||||
|
||||
#### P5-12.1:LLMProvider 抽象 + 4 适配器
|
||||
|
||||
- **负责人**:ai12
|
||||
- **交付物**:⚠️ 由 ai12 自行补充
|
||||
- **依赖**:见 [contracts/ai_contract.md](../contracts/ai_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai12 自行补充
|
||||
- **依赖**:无(P5 起点)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/providers/base.py`(`LLMProvider` 抽象接口:chat / stream_chat / embed)
|
||||
- `services/ai/src/ai/providers/openai_provider.py`
|
||||
- `services/ai/src/ai/providers/anthropic_provider.py`
|
||||
- `services/ai/src/ai/providers/baichuan_provider.py`
|
||||
- `services/ai/src/ai/providers/local_ollama_provider.py`
|
||||
- 重构 `llm_client.py` 为基于抽象接口的调用
|
||||
- **验收标准**:
|
||||
- 4 Provider 切换可用(通过 `llm_model_routing` 配置路由)
|
||||
- httpx 异步调用,不依赖 openai SDK
|
||||
- 单元测试覆盖 ≥ 80%(用 MockLLMProvider)
|
||||
- `ruff check src/` 零错误
|
||||
|
||||
#### P5-12.2:ProviderFailoverChain + CircuitBreaker
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.1
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/providers/failover.py`(按优先级尝试 Provider,失败自动切换)
|
||||
- `services/ai/src/ai/providers/circuit_breaker.py`(连续 3 次失败触发熔断 60s)
|
||||
- **验收标准**:单 Provider 故障自动切下一个;熔断器状态正确(closed/open/half_open)
|
||||
|
||||
#### P5-12.3:gRPC server + ActionState 整改(P0 阻塞)
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.1;**前置**:ISSUE-03(coord 升级 ai.proto 到 v1 完整版)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/grpc_server.py`(`grpc.aio` server,端口 50058)
|
||||
- 实现 `Chat` + `StreamChat` 两个 RPC(含流式)
|
||||
- `grpc.aio.ServerInterceptor` 透传 W3C traceparent
|
||||
- **ActionState 整改**(ISSUE-09):所有 HTTP 端点 + gRPC RPC 返回值改为 `{success, data, error:{code,message,details,traceId}}`,删除顶层 `degraded` 字段
|
||||
- **验收标准**:
|
||||
- teacher-bff 可调通 ai gRPC 50058 `Chat` / `StreamChat`(含流式)
|
||||
- HealthService.Check 返回 SERVING
|
||||
- 响应信封 004 §11.5 合规
|
||||
- `pnpm run arch:scan` 更新 arch.db
|
||||
|
||||
#### P5-12.4:Redis 多维度限流 + Dockerfile 多阶段
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.2
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/middleware/rate_limit.py`(Redis 令牌桶:user/IP/school 三维度)
|
||||
- `services/ai/Dockerfile`(多阶段构建,目标镜像 < 200MB)
|
||||
- **验收标准**:限流命中准确(user 10/min、IP 30/min、school 100/min);镜像 < 200MB
|
||||
|
||||
---
|
||||
|
||||
### M15 出题核心(第5-9天)
|
||||
|
||||
#### P5-12.5:PromptTemplateService + Jinja2 渲染
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.3
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/prompts/*.yaml`(5+ 模板:generate_question / optimize_expression / chat / lesson_plan 等)
|
||||
- `services/ai/src/ai/services/prompt_template_service.py`(模板注册 + Jinja2 渲染 + CRUD)
|
||||
- HTTP 端点:`GET/POST/PUT /ai/v1/prompts`
|
||||
- **验收标准**:5+ 模板可渲染;变量缺失返回 `AI_PROMPT_RENDER_FAILED`;模板缓存 1h TTL
|
||||
|
||||
#### P5-12.6:GenerateQuestion + StreamGenerateQuestion 逐字流式
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.5;**前置**:ISSUE-03(ai.proto 补 `StreamGenerateQuestion` + `GenerateQuestionRequest` 字段扩展)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/services/question_generation_service.py`
|
||||
- gRPC RPC:`GenerateQuestion` + `StreamGenerateQuestion`(题目逐字流式生成)
|
||||
- HTTP 端点:`POST /ai/v1/generate/question` + `POST /ai/v1/generate/question/stream`
|
||||
- **验收标准**:题目逐字流式返回;Pydantic 请求模型完整(grade/knowledge_point_ids/question_type/count)
|
||||
|
||||
#### P5-12.7:评估三道防线(RuleValidator + LLMJudge + QualityGate)
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.6
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/evaluation/rule_validator.py`(题型匹配/答案非空/解析合理/知识点覆盖)
|
||||
- `services/ai/src/ai/evaluation/llm_judge.py`(LLM-as-judge,5 维度加权评分)
|
||||
- `services/ai/src/ai/evaluation/quality_gate.py`(阈值 0.7,不达标重试 < 3 次)
|
||||
- **验收标准**:评估通过率 > 80%;不达标自动重试;重试耗尽返回 `AI_EVALUATION_FAILED`
|
||||
|
||||
#### P5-12.8:UsageRecorder + KafkaProducer + QuotaEnforcer
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.6;**前置**:ISSUE-02(topic 裁决)+ ISSUE-04(events.proto 补 AIUsageEvent)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/usage/usage_recorder.py`(token 消耗统计)
|
||||
- `services/ai/src/ai/usage/kafka_producer.py`(`aiokafka` + acks=all + idempotent + transactional_id)
|
||||
- `services/ai/src/ai/usage/quota_enforcer.py`(学校/教师月度配额,Redis 计数)
|
||||
- HTTP 端点:`GET /ai/v1/usage/me` + `GET /ai/v1/usage/school/{id}`
|
||||
- **验收标准**:用量事件落 data-ana ClickHouse;配额超限返回 `AI_QUOTA_EXCEEDED`;event_id SETNX 去重
|
||||
|
||||
#### P5-12.9:安全层(PIIRedactor + InputSanitizer + OutputModerator)
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.6
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/security/pii_redactor.py`(学生姓名/手机号/身份证/邮箱脱敏)
|
||||
- `services/ai/src/ai/security/input_sanitizer.py`(Prompt 注入防御)
|
||||
- `services/ai/src/ai/security/output_moderator.py`(敏感词过滤 + 安全校验)
|
||||
- **验收标准**:安全测试通过;PII 检出返回 `AI_PII_DETECTED`;注入检出返回 `AI_PROMPT_INJECTION_DETECTED`
|
||||
|
||||
---
|
||||
|
||||
### M16 备课工作流(第10-13天)
|
||||
|
||||
#### P5-12.10:gRPC client(content / data-ana / iam)+ interceptor
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.3;**前置**:content gRPC 50054 就绪(ai09 P4)、ISSUE-07(iam GetEffectiveDataScope P4 补全)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/clients/content_client.py`(KnowledgeGraphService.GetPrerequisites / GetLearningPath)
|
||||
- `services/ai/src/ai/clients/data_ana_client.py`(AnalyticsService.GetStudentWeakness / GetLearningTrend / GetClassPerformance)
|
||||
- `services/ai/src/ai/clients/iam_client.py`(GetEffectiveDataScope,Redis 缓存 5min)
|
||||
- `grpc.aio.ClientInterceptor`(trace 注入 + 重试 + 熔断)
|
||||
- **验收标准**:下游 gRPC 不可达时降级(跳过学情查询 + `degraded:true`);DataScope 缓存命中
|
||||
|
||||
#### P5-12.11:LessonPreparationWorkflow 4 步编排 + 状态机
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.10;**前置**:ISSUE-03(ai.proto 补 `GenerateLessonPlan` RPC)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/services/lesson_preparation_workflow.py`(4 步:分析学情 → 推荐知识点 → 生成题目 → 教师审核)
|
||||
- 状态机实现(02-architecture-design.md §2.4:Pending→Analyzing→Recommended→Generating→PendingReview→Persisted)
|
||||
- gRPC RPC:`GenerateLessonPlan`
|
||||
- HTTP 端点:`POST /ai/v1/lesson/preparation` + `GET /ai/v1/lesson/preparation/{id}`
|
||||
- **验收标准**:端到端跑通 4 步;评估未通过自动重试 < 3 次;工作流状态可查询
|
||||
|
||||
#### P5-12.12:WorkflowStateStore + 教师审核 + content 入库
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.11;**前置**:content `QuestionService.CreateQuestions`(待 coord 补 proto,见 02 doc §7)
|
||||
- **交付物**:
|
||||
- `services/ai/src/ai/workflow/workflow_state_store.py`(Redis 持久化,key `ai:workflow:{id}`,TTL 24h)
|
||||
- HTTP 端点:`POST /ai/v1/lesson/preparation/{id}/confirm`(教师确认/修改/拒绝)
|
||||
- 调 content.CreateQuestions 入库
|
||||
- **验收标准**:24h 内工作流可恢复;教师可审核/修改/拒绝;入库成功;24h 未审核过期
|
||||
|
||||
#### P5-12.13:集成测试 + 契约测试 + 文档同步
|
||||
|
||||
- **负责人**:ai12
|
||||
- **依赖**:P5-12.12
|
||||
- **交付物**:
|
||||
- `services/ai/tests/`(pytest + pytest-asyncio + testcontainers,覆盖率 ≥ 80%)
|
||||
- 契约测试(pact-python,ai.proto 与 teacher-bff 一致性)
|
||||
- 更新 `services/ai/README.md` + `docs/troubleshooting/known-issues.md` ai 分区
|
||||
- `pnpm run arch:scan` 确认 arch.db 已更新
|
||||
- **验收标准**:`ruff check src/` + `pytest` 零错误;覆盖率 ≥ 80%;README 含完整架构图
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai12 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai12 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 依赖项 | 提供方 | 就绪标志 | 状态 | 阻塞任务 |
|
||||
| --------------------------------------------------- | ------------- | ----------------------------------------- | ---- | ------------- |
|
||||
| ai.proto 升级 v1 完整版(6 RPC + 字段扩展) | coord (shared-proto) | proto 文件含 GenerateLessonPlan / StreamGenerateQuestion | ⏳ ISSUE-03 | P5-12.3/12.6/12.11 |
|
||||
| events.proto 补 AIUsageEvent message | coord (shared-proto) | proto 含 AIUsageEvent | ⏳ ISSUE-04 | P5-12.8 |
|
||||
| ai 用量事件 topic 命名裁决 | coord | 004 §7.2 补登 | ⏳ ISSUE-02 | P5-12.8 |
|
||||
| content gRPC 50054 启用 | ai09 (content) | HealthService.Check = SERVING | ⏳ P4 | P5-12.10 |
|
||||
| data-ana gRPC 50055 启用(可选) | ai11 (data-ana) | HealthService.Check = SERVING | ⏳ P4 | P5-12.10(可降级) |
|
||||
| iam `GetEffectiveDataScope` RPC P4 补全 | ai06 (iam) + coord | iam.proto 含此 RPC | ⏳ ISSUE-07 | P5-12.10(可降级) |
|
||||
| LLM Provider API key(OpenAI / 百川 / Ollama) | 人类决策者 | 环境变量配置 | — | P5-12.1 |
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
| 就绪标志 | 消费方 | 状态 |
|
||||
| ------------------------------------------------- | ---------------------- | ---- |
|
||||
| ai gRPC 50058 启用(HealthService.Check = SERVING) | teacher-bff (ai03) | ⏳ |
|
||||
| AiService.Chat / StreamChat 可调用(含流式) | teacher-bff | ⏳ |
|
||||
| AiService.GenerateQuestion / StreamGenerateQuestion 可调用 | teacher-bff | ⏳ |
|
||||
| AiService.GenerateLessonPlan 可调用(P5 补全) | teacher-bff | ⏳ |
|
||||
| AiService.OptimizeExpression 可调用 | teacher-bff | ⏳ |
|
||||
| ai 用量事件 topic 可发布(供 data-ana 统计) | data-ana (ai11) | ⏳ |
|
||||
|
||||
### 4.3 完成信号(批次 4 P5 完成)
|
||||
|
||||
ai P5 完成的 5 个标志:
|
||||
|
||||
1. ai12:ai gRPC 50058 启用 + 6 RPC 全部实现 + HealthService SERVING
|
||||
2. LLM Provider 4 适配器 + FailoverChain + CircuitBreaker 可用
|
||||
3. 备课工作流 4 步端到端跑通(含教师审核 + content 入库)
|
||||
4. 用量事件可发布到 Kafka + data-ana ClickHouse 可落库
|
||||
5. 端到端:teacher-portal 教师 AI 出题 → 流式返回 → 审核入库
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与缓解
|
||||
|
||||
| 风险 | 缓解措施 |
|
||||
| ----------------------------- | -------------------------------------------------------------------------- |
|
||||
| coord 未在 P5 启动前补全 proto(ISSUE-02/03/04) | ai12 先按 02-architecture-design.md §3.3/§4.2 建议 schema 实现,proto 落地后对齐 |
|
||||
| content / iam gRPC 未就绪 | 降级:跳过学情查询 + `degraded:true`;配额降级为仅 user_id 维度 |
|
||||
| LLM API key 未配置 | 降级骨架响应(已具备,main.py 当前行为) |
|
||||
| 工作流状态丢失(Redis 故障) | Redis 哨兵(P6 硬化)+ 事件日志;P6 评估迁移 Temporal(ISSUE-06) |
|
||||
|
||||
@@ -1,57 +1,393 @@
|
||||
# api-gateway 工作排期
|
||||
|
||||
> 负责人:ai01
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/api-gateway_contract.md](../contracts/api-gateway_contract.md)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/api-gateway_contract.md](../contracts/api-gateway_contract.md)、[objections/api-gateway_issue.md](../objections/api-gateway_issue.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 依据:[coord-final-decisions.md](../../coord-final-decisions.md) §3.8 W1-W8、[president-final-rulings.md](../../president-final-rulings.md) §2.15/§2.16/§2.19、[02-architecture-design.md](../../../services/api-gateway/docs/02-architecture-design.md) §9
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
api-gateway 是 Edu 系统统一入口,负责路由、JWT 验签、限流、熔断、CORS。全阶段目标:P2 路由+JWT → P3 限流加固 → P4-P6 持续优化。
|
||||
api-gateway 是 Edu 系统统一入口(L3 网关层),负责路由转发、JWT RS256 验签、限流、熔断、CORS、可观测性。无业务状态,纯 HTTP 反向代理。
|
||||
|
||||
**全阶段目标**:
|
||||
- **P2**:路由表 + JWT RS256(HTTP JWKS) + shared-go 接入 + 错误码 GW_ 前缀 + ActionState 信封 + slog + /readyz 真实检查 + 业务 metrics + tracer 资源属性 + DevMode 防护(遵循 W1-W8 / G1-G17 裁决)
|
||||
- **P3**:路由扩展(student-bff :3009)+ core-edu 路由
|
||||
- **P4**:路由扩展(parent-bff :3010)+ content / data-ana 路由
|
||||
- **P5**:路由扩展(msg / ai 路由)+ 接入 push-gateway 协作(WebSocket 升级透传评估)
|
||||
- **P6**:限流迁 Redis + per-服务实例熔断评估 + 测试覆盖率 ≥ 80% + 安全加固
|
||||
|
||||
**当前状态(2026-07-10)**:P1 已交付(classes 域 CRUD 端到端跑通),P2 升级未启动。已有仲裁核查发现 6 项未遵循裁决(见 [objections/api-gateway_issue.md](../objections/api-gateway_issue.md) §0.4)。
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai01 api-gateway 全阶段排期
|
||||
title ai01 api-gateway 全阶段排期(P2-P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2 基础
|
||||
路由表+双入口+shared-go :a1a, 2026-07-10, 3d
|
||||
JWT校验+JWKS fetcher :a1b, after a1a, 3d
|
||||
限流+熔断+CORS :a1c, after a1b, 2d
|
||||
section P2 基础升级(批次1)
|
||||
P2.0 修复P0遗留 :crit, p2a, 2026-07-10, 1d
|
||||
P2.1 shared-go接入 :crit, p2b, after p2a, 2d
|
||||
P2.2 JWT RS256+JWKS :crit, p2c, after p2b, 3d
|
||||
P2.3 错误码GW_+ActionState :crit, p2d, after p2c, 1d
|
||||
P2.4 slog+metrics+tracer :crit, p2e, after p2d, 2d
|
||||
P2.5 /readyz真实检查 :crit, p2f, after p2e, 1d
|
||||
P2.6 DevMode防护 :p2g, after p2f, 1d
|
||||
P2.7 路由表扩展iam/teacher :p2h, after p2g, 1d
|
||||
|
||||
section P3-P6 持续优化
|
||||
路由扩展(student/parent/admin) :a1d, after a1c, 2d
|
||||
指标+链路加固 :a1e, after a1d, 2d
|
||||
section P3 路由扩展(批次2)
|
||||
P3.1 student-bff路由 :p3a, after p2h, 1d
|
||||
P3.2 core-edu路由 :p3b, after p3a, 1d
|
||||
P3.3 API版本化/v1迁移 :p3c, after p3b, 2d
|
||||
|
||||
section P4 路由扩展(批次3)
|
||||
P4.1 parent-bff路由 :p4a, after p3c, 1d
|
||||
P4.2 content路由 :p4b, after p4a, 1d
|
||||
P4.3 data-ana路由 :p4c, after p4b, 1d
|
||||
|
||||
section P5 路由扩展(批次4)
|
||||
P5.1 msg路由 :p5a, after p4c, 1d
|
||||
P5.2 ai路由 :p5b, after p5a, 1d
|
||||
P5.3 push-gateway协作评估 :p5c, after p5b, 2d
|
||||
|
||||
section P6 硬化(批次5)
|
||||
P6.1 限流迁Redis :p6a, after p5c, 3d
|
||||
P6.2 per-服务熔断评估 :p6b, after p6a, 2d
|
||||
P6.3 测试覆盖率80% :p6c, after p6b, 3d
|
||||
P6.4 安全加固 :p6d, after p6c, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai01 接管后必须自行细化为完整 P2-P6 排期。
|
||||
**关键路径**(红色 crit):P2.0 → P2.1 → P2.2 → P2.3 → P2.4 → P2.5 → P2.6 → P2.7
|
||||
**总时间线**:P2 约 11 天 + P3-P5 约 9 天 + P6 约 10 天 = 约 30 天
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### P2:路由表 + JWT + 限流
|
||||
### P2.0:修复 P0 遗留问题
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- `services/api-gateway/internal/routing/router.go` — 路由表
|
||||
- `/api/v1/teacher/*` → teacher-bff:3003 代理
|
||||
- `/api/v1/iam/*` → iam:3002 代理
|
||||
- JWT RS256 验签(shared-go/jwks)
|
||||
- 限流 + 熔断 + CORS
|
||||
- **依赖**:shared-go 骨架(批次 0 已完成)+ iam GetPublicKey(ai06)
|
||||
- **验收标准**:路由双入口 + JWT 验签 + 限流 + CORS 白名单
|
||||
- **完整 P3-P6 任务**:⚠️ 由 ai01 自行补充
|
||||
- `services/api-gateway/go.mod` L3 改为 `go 1.22`(修复 ISSUE-005)
|
||||
- `go.work` L1 改为 `go 1.22`
|
||||
- 删除 [01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §7.1 issue #3 死代码引用(ISSUE-008)
|
||||
- 修正 [01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §6 审计表 metrics 行(ISSUE-007)
|
||||
- 修正 [README.md](../../../services/api-gateway/README.md) L38 删除不存在的 `/health` 兼容端点描述
|
||||
- 修正 [proxy.go](../../../services/api-gateway/internal/proxy/proxy.go) L24 删除冗余 `TrimPrefix("/api")`
|
||||
- **验收标准**:`go build ./...` + `go vet ./...` 通过;文档与代码一致
|
||||
- **对应 ISSUE**:ISSUE-005 / ISSUE-007 / ISSUE-008
|
||||
|
||||
### P2.1:shared-go 包接入
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:[packages/shared-go](../../../packages/shared-go/) 已建立(批次 0 已完成);coord 仲裁 ISSUE-002 / ISSUE-004
|
||||
- **交付物**:
|
||||
- `go.work` 增加 `./packages/shared-go`
|
||||
- `services/api-gateway/go.mod` 增加 `github.com/edu-cloud/shared-go` 依赖
|
||||
- 按 ISSUE-004 仲裁结果接入 shared-go/logger(zap 或 slog,取决于 coord 裁决)
|
||||
- [tracer.go](../../../services/api-gateway/internal/observability/tracer.go) 评估接入 shared-go/tracer(若接口兼容)
|
||||
- [config.go](../../../services/api-gateway/internal/config/config.go) 评估接入 shared-go/env
|
||||
- **验收标准**:`go build ./...` 通过;import shared-go 成功;logger 输出结构化 JSON
|
||||
- **对应 ISSUE**:ISSUE-002 / ISSUE-004
|
||||
|
||||
### P2.2:JWT RS256 升级 + JWKS 缓存
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:iam (ai06) 暴露 `GET /.well-known/jwks.json` HTTP 端点;coord 仲裁 ISSUE-001(确认 HTTP JWKS)
|
||||
- **交付物**:
|
||||
- [auth.go](../../../services/api-gateway/internal/middleware/auth.go) 改用 shared-go/jwks.Fetcher(`jwks.NewFetcher(cfg.JWKSURL)`)
|
||||
- HS256 逻辑废弃,`cfg.JWTSecret` 仅 DevMode 下用作 mock 密钥
|
||||
- JWKS 缓存策略:TTL 5min(shared-go/jwks 默认),kid 未命中时强制刷新,刷新失败保留旧公钥
|
||||
- 启动时同步拉取一次 JWKS,失败则 panic 拒绝启动
|
||||
- claims 增加 `data_scope` 字段提取,注入 `x-data-scope` 头
|
||||
- `internal/config/config.go` 增加 `JWKSURL` 字段(环境变量 `IAM_JWKS_URL`)
|
||||
- **验收标准**:JWT RS256 验签通过;JWKS 缓存命中率达 99%+;kid 未命中自动刷新
|
||||
- **对应裁决**:W1(错误码加 GW_ 前缀)、§2.16(HTTP JWKS,非 gRPC)
|
||||
|
||||
### P2.3:错误码 GW_ 前缀 + ActionState 信封
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- [auth.go](../../../services/api-gateway/internal/middleware/auth.go) 错误码改为 `GW_UNAUTHORIZED` / `GW_INVALID_TOKEN` / `GW_INVALID_CLAIMS`
|
||||
- [ratelimit.go](../../../services/api-gateway/internal/middleware/ratelimit.go) 响应体改为 `{success:false,error:{code:"GW_RATE_LIMITED",message:"...",retry_after:60}}`
|
||||
- [circuit-breaker.go](../../../services/api-gateway/internal/middleware/circuit-breaker.go) 响应体改为 `{success:false,error:{code:"GW_CIRCUIT_OPEN",message:"...",retry_after:30}}`
|
||||
- [recovery.go](../../../services/api-gateway/internal/middleware/recovery.go) 响应体改为 `{success:false,error:{code:"GW_INTERNAL_ERROR",message:"...",request_id:"..."}}`
|
||||
- `RequestBodyLimit` 超限响应改为 `{success:false,error:{code:"GW_REQUEST_TOO_LARGE",message:"..."}}`
|
||||
- **验收标准**:所有错误响应符合 ActionState 信封;错误码统一 `GW_` 前缀
|
||||
- **对应裁决**:W1 / W2 / G14
|
||||
- **对应 ISSUE**:ISSUE-009
|
||||
|
||||
### P2.4:slog + 业务 metrics + tracer 资源属性
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:P2.1 shared-go 接入完成
|
||||
- **交付物**:
|
||||
- 按 ISSUE-004 仲裁结果统一 logger(zap 或 slog)
|
||||
- 所有 `log.Printf` / `log.Println` / `log.Fatal` 改为结构化日志(带 `request_id` / `trace_id` / `user_id` / `method` / `path` / `status` / `latency_ms` 字段)
|
||||
- 新增 `internal/observability/metrics.go`,注册 7 个业务指标:
|
||||
- `api_gateway_http_requests_total`(Counter,method/endpoint/status)
|
||||
- `api_gateway_http_request_duration_seconds`(Histogram,method/endpoint)
|
||||
- `api_gateway_circuit_breaker_state`(Gauge,service/state)
|
||||
- `api_gateway_rate_limited_total`(Counter,ip)
|
||||
- `api_gateway_proxy_upstream_duration_seconds`(Histogram,upstream)
|
||||
- `api_gateway_jwks_refresh_total`(Counter,result)
|
||||
- `api_gateway_auth_failures_total`(Counter,reason)
|
||||
- [tracer.go](../../../services/api-gateway/internal/observability/tracer.go) 资源属性补全:`service.name` + `service.version`(编译时注入)+ `deployment.environment`(ENV 变量)+ `host.name`
|
||||
- Metrics 中间件:在 Auth 之后、CircuitBreaker 之前注册(统计通过鉴权的请求)
|
||||
- **验收标准**:`/metrics` 端点返回 7 个业务指标;日志为 JSON 结构化;tracer 资源属性完整
|
||||
- **对应裁决**:W3 / W5 / W6 / G4 / G5 / G6
|
||||
|
||||
### P2.5:/readyz 真实健康检查
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:所有下游服务实现 `/healthz`(P2 阶段 iam / teacher-bff 已就绪)
|
||||
- **交付物**:
|
||||
- [health.go](../../../services/api-gateway/internal/health/health.go) `Readyz` 重构为并行 ping 下游 `/healthz`
|
||||
- 下游清单从 `cfg.ServicesURL` 动态读取(iam / classes / teacher-bff / core-edu / content / msg / ai / data-ana)
|
||||
- 超时 2s,任一不可达返回 503 + `{"status":"error","unhealthy":["iam","core-edu"]}`
|
||||
- 全部可达返回 200 + `{"status":"ok"}`
|
||||
- 可选依赖软失败规则:未启用 gRPC 的下游(P3-P5 阶段未就绪的服务)失败仅告警,返回 200 + `degraded: true`(依据 president-final-rulings.md §3.3)
|
||||
- **验收标准**:/readyz 真实检查下游;某服务下线时返回 503
|
||||
- **对应裁决**:W4 / G2
|
||||
|
||||
### P2.6:DevMode 生产防护
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- [config.go](../../../services/api-gateway/internal/config/config.go) `Load()` 增加 `ENV` 环境变量读取
|
||||
- 若 `DevMode=true && ENV=production` 则 `panic` 拒绝启动
|
||||
- 启动日志打印 `ENV` / `DevMode` 状态
|
||||
- **验收标准**:`DEV_MODE=true ENV=production` 启动失败;`DEV_MODE=true ENV=development` 启动成功
|
||||
- **对应裁决**:W7
|
||||
|
||||
### P2.7:路由表扩展(iam / teacher)
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:coord 仲裁 ISSUE-003(API 版本化路由规则);iam (ai06) / teacher-bff (ai03) P2 就绪
|
||||
- **交付物**:
|
||||
- 按 ISSUE-003 仲裁结果更新路由(方案 A/B/C 之一)
|
||||
- 若方案 A:iam 路由改为 `/api/v1/iam/v1/*path`,proxy 透传 `/iam/v1/*path`
|
||||
- teacher-bff 路由保持 `/api/v1/teacher/*path`
|
||||
- [02-architecture-design.md](../../../services/api-gateway/docs/02-architecture-design.md) §4.1 路由表同步更新
|
||||
- **验收标准**:路由表与代码一致;前端调用 `/api/v1/iam/v1/auth/login` 透传到 iam 服务
|
||||
- **对应裁决**:§2.15
|
||||
|
||||
---
|
||||
|
||||
### P3.1:student-bff 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:student-bff (ai04) P3 就绪
|
||||
- **交付物**:
|
||||
- `main.go` 增加 student-bff 路由:`/api/v1/student` + `/api/v1/student/*path` → `cfg.StudentBffURL`(:3009)
|
||||
- `config.go` 增加 `StudentBffURL` 字段(环境变量 `STUDENT_BFF_URL`)
|
||||
- **验收标准**:`/api/v1/student/*` 代理到 student-bff:3009
|
||||
|
||||
### P3.2:core-edu 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:core-edu (ai08) P3 就绪;classes 服务已合并入 core-edu(C1 裁决)
|
||||
- **交付物**:
|
||||
- `main.go` classes 路由目标改为 core-edu(`cfg.ClassesServiceURL` → `cfg.CoreEduServiceURL`)
|
||||
- 或保留 classes 路由别名,proxy 到 core-edu
|
||||
- exams / homework / grades 路由已在 P1 实现,无需修改
|
||||
- **验收标准**:`/api/v1/classes/*` 代理到 core-edu:3004
|
||||
|
||||
### P3.3:API 版本化 /v1 迁移
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:P2.7 路由规则仲裁结果;core-edu (ai08) P3 就绪
|
||||
- **交付物**:
|
||||
- 按 ISSUE-003 仲裁结果,全服务路由统一加 `/v1` 前缀
|
||||
- core-edu 路由:`/api/v1/exams/v1/*path` 等(若方案 A)
|
||||
- 更新 [02-architecture-design.md](../../../services/api-gateway/docs/02-architecture-design.md) §4.1 路由表
|
||||
- **验收标准**:所有业务路由含 `/v1` 版本前缀
|
||||
|
||||
---
|
||||
|
||||
### P4.1:parent-bff 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:parent-bff (ai05) P4 就绪
|
||||
- **交付物**:
|
||||
- `main.go` 增加 parent-bff 路由:`/api/v1/parent` + `/api/v1/parent/*path` → `cfg.ParentBffURL`(:3010)
|
||||
- `config.go` 增加 `ParentBffURL` 字段
|
||||
- **验收标准**:`/api/v1/parent/*` 代理到 parent-bff:3010
|
||||
|
||||
### P4.2:content 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:content (ai09) P4 就绪
|
||||
- **交付物**:
|
||||
- `main.go` 已有 content 路由(P1 实现:textbooks / chapters / knowledge-points / questions)
|
||||
- 验证路由目标 `cfg.ContentServiceURL`(:3005)正确
|
||||
- 按 P3.3 版本化规则加 `/v1` 前缀
|
||||
- **验收标准**:content 路由可用 + 版本化
|
||||
|
||||
### P4.3:data-ana 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:data-ana (ai11) P4 就绪
|
||||
- **交付物**:
|
||||
- `main.go` 已有 data-ana 路由(P1 实现:analytics)
|
||||
- 增加 `dashboard` 路由别名:`/api/v1/dashboard` + `/*path` → data-ana
|
||||
- 按版本化规则加 `/v1` 前缀
|
||||
- **验收标准**:data-ana 路由可用 + dashboard 别名可用
|
||||
|
||||
---
|
||||
|
||||
### P5.1:msg 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:msg (ai10) P5 就绪
|
||||
- **交付物**:
|
||||
- `main.go` 已有 msg 路由(P1 实现:notifications)
|
||||
- 增加 `messages` 路由别名:`/api/v1/messages` + `/*path` → msg
|
||||
- 按版本化规则加 `/v1` 前缀
|
||||
- **验收标准**:msg 路由可用 + messages 别名可用
|
||||
|
||||
### P5.2:ai 路由
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:ai (ai12) P5 就绪
|
||||
- **交付物**:
|
||||
- `main.go` 已有 ai 路由(P1 实现)
|
||||
- 按版本化规则加 `/v1` 前缀
|
||||
- **验收标准**:ai 路由可用
|
||||
|
||||
### P5.3:push-gateway 协作评估
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:push-gateway (ai02) P5 就绪
|
||||
- **交付物**:
|
||||
- 评估 WebSocket 升级请求是否需要 Gateway 透传到 push-gateway
|
||||
- 若需要:增加 `/ws` + `/sse` 路由透传到 push-gateway(:8081)
|
||||
- 若不需要:文档说明 WebSocket 直连 push-gateway,不经过 Gateway
|
||||
- 更新 [02-architecture-design.md](../../../services/api-gateway/docs/02-architecture-design.md) §7 交互点清单
|
||||
- **验收标准**:WebSocket 推送链路可用(透传或直连)
|
||||
|
||||
---
|
||||
|
||||
### P6.1:限流迁 Redis
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:Redis 基础设施就绪
|
||||
- **交付物**:
|
||||
- [ratelimit.go](../../../services/api-gateway/internal/middleware/ratelimit.go) 改用 Redis 令牌桶(`github.com/go-redis/redis_rate/v10`)
|
||||
- 支持多副本一致限流
|
||||
- 增加用户级限流(基于 `x-user-id` 头)
|
||||
- 登录接口额外加用户级限流(防爆破)
|
||||
- 保留 DevMode 下内存令牌桶回退
|
||||
- **验收标准**:多副本部署时限流一致;用户级限流生效
|
||||
|
||||
### P6.2:per-服务实例熔断评估
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:P6.1 完成
|
||||
- **交付物**:
|
||||
- 评估是否将共享 `downstream` 熔断器拆分为 per-服务实例(iam / core-edu / teacher-bff 等)
|
||||
- 若拆分:每个下游服务独立熔断状态,互不影响
|
||||
- 若不拆分:保持 W8 裁决现状,文档说明理由
|
||||
- 按 W8 裁决,此项 P6 单独评估,不强制拆分
|
||||
- **验收标准**:评估报告 + 决策记录
|
||||
|
||||
### P6.3:测试覆盖率 80%
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:P2-P5 全部完成
|
||||
- **交付物**:
|
||||
- 补全 [auth_test.go](../../../services/api-gateway/internal/middleware/auth_test.go):JWKS 验签 / claims 解析 / DevMode 旁路 / 公开路径白名单
|
||||
- 补全 [cors_test.go](../../../services/api-gateway/internal/middleware/cors_test.go):白名单匹配 / 预检请求 / Vary 头
|
||||
- 补全 [security_test.go](../../../services/api-gateway/internal/middleware/security_test.go):安全头设置 / Server 头移除
|
||||
- 补全 [recovery_test.go](../../../services/api-gateway/internal/middleware/recovery_test.go):panic 捕获 / request_id 生成 / ActionState 信封
|
||||
- 补全 [requestid_test.go](../../../services/api-gateway/internal/middleware/requestid_test.go):透传 / 生成 / 响应头
|
||||
- 补全 [proxy_test.go](../../../services/api-gateway/internal/proxy/proxy_test.go):路径前缀去除 / Host 改写
|
||||
- 补全 [health_test.go](../../../services/api-gateway/internal/health/health_test.go):/readyz 下游检查 / 软失败规则
|
||||
- **验收标准**:`go test ./... -cover` 覆盖率 ≥ 80%
|
||||
|
||||
### P6.4:安全加固
|
||||
|
||||
- **负责人**:ai01
|
||||
- **依赖**:P6.1-P6.3 完成
|
||||
- **交付物**:
|
||||
- 评估 IP 黑名单 / WAF 规则是否在 Gateway 层实现(建议在 Istio 层做,本服务不介入)
|
||||
- 限流策略表 per-路由细化(02 §4.2)
|
||||
- 熔断阈值 per-服务配置(02 §4.3)
|
||||
- CORS 白名单生产环境强制配置(禁止 `*`)
|
||||
- 审计日志:记录所有 401 / 403 / 429 / 503 响应
|
||||
- **验收标准**:安全扫描通过;审计日志可追溯
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:iam GetPublicKey RPC(ai06)
|
||||
- **我的就绪信号**:api-gateway :8080 可访问 + JWT 验签可用
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游 | 就绪标志 | 阶段 | 状态 |
|
||||
| ---- | -------- | ---- | ---- |
|
||||
| coord | shared-go 包骨架(tracer/logger/jwks/env)| 批次 0 | ✅ 已完成 |
|
||||
| coord | ISSUE-001 仲裁(JWKS vs gRPC)| P2 启动前 | ⏳ 待仲裁 |
|
||||
| coord | ISSUE-002 仲裁(shared-go 接入)| P2 启动前 | ⏳ 待仲裁 |
|
||||
| coord | ISSUE-003 仲裁(API 版本化路由)| P2.7 前 | ⏳ 待仲裁 |
|
||||
| coord | ISSUE-004 仲裁(zap vs slog)| P2.1 前 | ⏳ 待仲裁 |
|
||||
| iam (ai06) | `GET /.well-known/jwks.json` HTTP 端点 + RS256 签发 | P2 | ⏳ |
|
||||
| iam (ai06) | gRPC 50052 启用(不影响 Gateway,Gateway 走 HTTP)| P2 | ⏳ |
|
||||
| teacher-bff (ai03) | `POST /graphql` :3003 启用 | P2 | ⏳ |
|
||||
| student-bff (ai04) | `POST /graphql` :3009 启用 | P3 | ⏳ |
|
||||
| core-edu (ai08) | gRPC 50053 启用 + classes 合并 | P3 | ⏳ |
|
||||
| parent-bff (ai05) | `POST /graphql` :3010 启用 | P4 | ⏳ |
|
||||
| content (ai09) | gRPC 50054 启用 | P4 | ⏳ |
|
||||
| data-ana (ai11) | gRPC 50055 启用 | P4 | ⏳ |
|
||||
| msg (ai10) | gRPC 50056 启用 | P5 | ⏳ |
|
||||
| ai (ai12) | gRPC 50058 启用 | P5 | ⏳ |
|
||||
| push-gateway (ai02) | :8081 启用 + /internal/push | P5 | ⏳ |
|
||||
|
||||
### 4.2 我的就绪标志(供下游消费)
|
||||
|
||||
| 阶段 | 就绪标志 | 状态 |
|
||||
| ---- | -------- | ---- |
|
||||
| P2 | api-gateway :8080 可访问 + JWT RS256 验签可用 + 7 个业务指标 + /readyz 真实检查 | ⏳ |
|
||||
| P2 | /api/v1/iam/* + /api/v1/teacher/* 路由可用 | ⏳ |
|
||||
| P3 | /api/v1/student/* + /api/v1/exams/* 等路由可用 | ⏳ |
|
||||
| P4 | /api/v1/parent/* + /api/v1/textbooks/* + /api/v1/analytics/* 路由可用 | ⏳ |
|
||||
| P5 | /api/v1/notifications/* + /api/v1/ai/* 路由可用 | ⏳ |
|
||||
| P6 | 限流迁 Redis + 测试覆盖率 ≥ 80% | ⏳ |
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与应对
|
||||
|
||||
| 风险 | 概率 | 影响 | 应对 |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| coord 仲裁延期(ISSUE-001/002/003/004)| 中 | P2 阻塞 | ai01 先按 HTTP JWKS + shared-go + 方案 A 推进,仲裁后调整 |
|
||||
| iam JWKS 端点延期 | 中 | P2.2 阻塞 | DevMode 下用本地 mock RS256 公钥(与 mock 私钥配对) |
|
||||
| shared-go 接口不兼容 | 低 | P2.1 阻塞 | ai01 自行适配,或反馈 coord 修改 shared-go |
|
||||
| 下游服务未实现 /healthz | 中 | /readyz 误报 | 软失败规则:未就绪服务失败仅告警,返回 200 + degraded |
|
||||
| JWKS 缓存过期时 iam 不可达 | 低 | 全量 401 | fail-open 1 次后 fail-close;监控 `jwks_refresh_total` 指标 |
|
||||
| DevMode 旁路误开到生产 | 低 | 鉴权绕过 | P2.6 生产防护(W7 裁决) |
|
||||
|
||||
---
|
||||
|
||||
## §6 与其他模块的协作
|
||||
|
||||
| 模块 | 协作内容 | 时机 |
|
||||
| ---- | -------- | ---- |
|
||||
| iam (ai06) | JWKS HTTP 端点 + JWT RS256 签发 | P2 |
|
||||
| teacher-bff (ai03) | 反向代理 :3003 GraphQL | P2 |
|
||||
| student-bff (ai04) | 反向代理 :3009 GraphQL | P3 |
|
||||
| parent-bff (ai05) | 反向代理 :3010 GraphQL | P4 |
|
||||
| core-edu (ai08) | 反向代理 :3004 + classes 合并 | P3 |
|
||||
| content (ai09) | 反向代理 :3005 | P4 |
|
||||
| data-ana (ai11) | 反向代理 :3006 | P4 |
|
||||
| msg (ai10) | 反向代理 :3007 | P5 |
|
||||
| ai (ai12) | 反向代理 :3008 | P5 |
|
||||
| push-gateway (ai02) | WebSocket 升级透传评估 | P5 |
|
||||
| coord | shared-go 包维护 + ISSUE 仲裁 | 持续 |
|
||||
|
||||
@@ -1,45 +1,359 @@
|
||||
# content 工作排期
|
||||
|
||||
> 负责人:ai09
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/content_contract.md](../contracts/content_contract.md)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/content_contract.md](../contracts/content_contract.md)、[objections/content_issue.md](../objections/content_issue.md)、[../../services/content/docs/02-architecture-design.md](../../../services/content/docs/02-architecture-design.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 当前分支:`feat-review-content-module-docs-WAIyMA`
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
content 是内容服务,提供 TextbookService、ChapterService、KnowledgeGraphService、QuestionService,结合 Neo4j 知识图谱与 Elasticsearch 全文检索。全阶段目标:P2 服务骨架+Neo4j+ES → P3 四大 Service 实现 → P4-P6 持续优化。
|
||||
content 是内容资源中台服务(P4 阶段),承载 D4 内容资源限界上下文,提供 Textbook / Chapter / KnowledgePoint / Question 四个聚合的 CRUD 与知识图谱查询。
|
||||
|
||||
**关键交付**:
|
||||
- gRPC 50054 + 4 Service(Textbook/Chapter/KnowledgeGraph/Question),按 [coord-final-decisions.md §3.3](../../coord-final-decisions.md) N1/N3/N5 仲裁,P4 首次实现即启用 gRPC + 补全 QuestionService/ChapterService proto
|
||||
- MySQL 写模型 + Neo4j 知识图谱 + Outbox 事件驱动异步同步(禁止业务事务内同步双写 Neo4j)
|
||||
- Kafka 发布 `edu.content.knowledge_point.events` / `edu.content.question.events`(聚合 topic 策略,待 ISSUE-002 仲裁)
|
||||
- P5 引入 Elasticsearch 全文检索 + AI 出题入库(QuestionService.BatchCreateQuestions)
|
||||
- P6+ 长远演进:教材版本管理 / 跨租户内容共享 / 个性化学习路径推荐
|
||||
|
||||
**关键路径位置**:批次 3(P4),依赖批次 2 core-edu 完成(实际可并行:content 不强依赖 core-edu)
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P4-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai09 content 全阶段排期
|
||||
title ai09 content 全阶段排期(P4-P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a9a, 2026-07-10, Xd
|
||||
section P4 基础设施
|
||||
P4.1 schema 迁移补字段 :crit, c4a, 2026-07-19, 2d
|
||||
P4.2 Outbox 表+Publisher worker :crit, c4b, after c4a, 3d
|
||||
P4.3 Kafka producer(idempotent+txn) :crit, c4c, after c4b, 2d
|
||||
P4.4 Neo4j Sync Worker(异步) :crit, c4d, after c4c, 2d
|
||||
P4.5 重构 kp.service 移除同步双写 :crit, c4e, after c4d, 1d
|
||||
|
||||
section P4 gRPC 契约
|
||||
P4.6 content.proto 补 ChapterService/QuestionService :crit, c4f, 2026-07-19, 1d
|
||||
P4.7 gRPC controller 实现(4 Service) :crit, c4g, after c4f, 4d
|
||||
P4.8 buf generate + 类型校验 :c4h, after c4g, 1d
|
||||
|
||||
section P4 横切与质量
|
||||
P4.9 /readyz 多依赖(DB/Neo4j/Kafka) :c4i, after c4e, 1d
|
||||
P4.10 ZodError GlobalErrorFilter 分支 :c4j, after c4i, 1d
|
||||
P4.11 DB 改 getDb()+ID 改 cuid2 :c4k, after c4j, 1d
|
||||
P4.12 Repository 抽象补齐 :c4l, after c4k, 2d
|
||||
P4.13 单元测试(Service/Repository)≥60% :c4m, after c4l, 3d
|
||||
P4.14 修正 README 与实现对齐 :c4n, after c4m, 1d
|
||||
|
||||
section P5 ES+AI 集成
|
||||
P5.1 引入 @elastic/elasticsearch :crit, c5a, after c4m, 1d
|
||||
P5.2 ES mapping+ensureIndex :crit, c5b, after c5a, 1d
|
||||
P5.3 ES Sync Worker(消费事件同步索引) :crit, c5c, after c5b, 2d
|
||||
P5.4 GET /questions/search 检索 API :crit, c5d, after c5c, 2d
|
||||
P5.5 QuestionService gRPC 完善Publish/Search :c5e, after c5d, 1d
|
||||
P5.6 AI 出题 BatchCreateQuestions 联调 :c5f, after c5e, 2d
|
||||
P5.7 检索性能优化(<200ms) :c5g, after c5f, 2d
|
||||
P5.8 测试覆盖率≥80% :c5h, after c5g, 2d
|
||||
|
||||
section P6+ 演进
|
||||
P6.1 Question 审核工作流状态机 :c6a, after c5h, 3d
|
||||
P6.2 知识图谱可视化 API :c6b, after c6a, 3d
|
||||
P6.3 教材版本管理 :c6c, after c6b, 2d
|
||||
P6.4 /readyz 硬化+监控告警完善 :c6d, after c6c, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai09 接管后必须自行细化为完整 P2-P6 排期。
|
||||
**预估总工期**:P4 约 21 天 + P5 约 13 天 + P6+ 约 10 天 = **44 天**(与 workline.md §1 批次 3+4 时间窗口一致)
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### 3.1 P4 阶段任务
|
||||
|
||||
#### P4.1 schema 迁移补字段
|
||||
|
||||
- **负责人**:ai09
|
||||
- **交付物**:⚠️ 由 ai09 自行补充
|
||||
- **依赖**:见 [contracts/content_contract.md](../contracts/content_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai09 自行补充
|
||||
- **依赖**:无(自身 schema 现状)
|
||||
- **交付物**:
|
||||
- [textbooks.schema.ts](../../../services/content/src/textbooks/textbooks.schema.ts) textbooks 表补 `status` / `tenant_id` / `metadata` 字段
|
||||
- chapters 表补 `created_at` / `updated_at` / `status`(解决 [01-understanding.md](../../../services/content/docs/01-understanding.md) C7)
|
||||
- knowledge_points 表补 `difficulty` / `metadata` / `created_at` / `updated_at`(解决 ISSUE-009)
|
||||
- questions 表补 `status` / `source` / `created_by`(NULL 起步,解决 ISSUE-007)/ `metadata`
|
||||
- **验收标准**:`pnpm typecheck` 通过;Drizzle 类型重新生成;迁移脚本可幂等执行
|
||||
|
||||
#### P4.2 Outbox 表 + Publisher worker
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.1
|
||||
- **交付物**:
|
||||
- 新建 `src/shared/outbox/outbox.schema.ts`(content_outbox_events 表,见 design doc §3.1.5)
|
||||
- 新建 `src/shared/outbox/outbox.publisher.ts`(轮询 PENDING 事件投递 Kafka,指数退避重试)
|
||||
- 新建 `src/shared/outbox/outbox.module.ts`
|
||||
- **验收标准**:业务事务内写 questions + outbox 同事务提交;Publisher worker 独立轮询;retry_count 累加正确
|
||||
|
||||
#### P4.3 Kafka producer(idempotent + transactionalId)
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.2
|
||||
- **交付物**:
|
||||
- 新建 `src/shared/kafka/producer.ts`(kafkajs 客户端,idempotent=true,transactionalId=content-producer)
|
||||
- 新建 `src/shared/kafka/kafka.module.ts`
|
||||
- package.json 添加 kafkajs 依赖
|
||||
- **验收标准**:producer 启动成功;transactionalId 唯一;幂等投递无重复
|
||||
|
||||
#### P4.4 Neo4j Sync Worker(异步同步)
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.3
|
||||
- **交付物**:
|
||||
- 新建 `src/shared/sync/neo4j-sync.worker.ts`(消费 content 自身 Outbox 事件,异步创建/更新 Neo4j 节点与关系)
|
||||
- 消费 `KnowledgePointCreated` / `KnowledgePointPrerequisiteAdded` 等事件
|
||||
- **验收标准**:MySQL 写知识点后,Neo4j 节点最终一致出现(延迟 < 2s);Neo4j 故障时事件不丢失,恢复后补齐
|
||||
|
||||
#### P4.5 重构 knowledge-points.service.ts 移除同步双写
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.4
|
||||
- **交付物**:
|
||||
- [knowledge-points.service.ts](../../../services/content/src/knowledge-points/knowledge-points.service.ts) 删除 `safeCreateNode` 同步写 Neo4j 逻辑
|
||||
- 改为发 Outbox 事件 `KnowledgePointCreated`
|
||||
- `addPrerequisite` 改为发 Outbox 事件 `KnowledgePointPrerequisiteAdded`
|
||||
- **验收标准**:业务事务内不再直接写 Neo4j;Neo4j 写入全部走异步 Sync Worker(解决 ISSUE-010 + 01-understanding C9)
|
||||
|
||||
#### P4.6 content.proto 补 ChapterService / QuestionService
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:无(自身 proto 现状)
|
||||
- **交付物**:
|
||||
- [content.proto](../../../packages/shared-proto/proto/content.proto) 补 ChapterService(CreateChapter/ListChapters/GetChapter/UpdateChapter/DeleteChapter)
|
||||
- 补 QuestionService(CreateQuestion/BatchCreateQuestions/GetQuestion/ListQuestions/UpdateQuestion/DeleteQuestion/PublishQuestion/SearchQuestions)
|
||||
- 补 TextbookService.UpdateTextbook / DeleteTextbook
|
||||
- 补全 message 定义(Chapter / Question / QuestionRequest 等)
|
||||
- 同步补 events.proto 的 KnowledgePointEvent / QuestionEvent / TextbookEvent / ChapterEvent(待 ISSUE-002 仲裁后定)
|
||||
- **验收标准**:`buf lint` 通过;`buf breaking` 无破坏性变更(新增字段 OK);contract.md 与 design doc §4.2 RPC 数对齐
|
||||
|
||||
#### P4.7 gRPC controller 实现(4 Service)
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.6
|
||||
- **交付物**:
|
||||
- 新建 `src/textbooks/textbooks.grpc.controller.ts`
|
||||
- 新建 `src/chapters/chapters.grpc.controller.ts`
|
||||
- 新建 `src/knowledge-points/knowledge-points.grpc.controller.ts`
|
||||
- 新建 `src/questions/questions.grpc.controller.ts`
|
||||
- main.ts 启用 gRPC server 50054
|
||||
- **验收标准**:`grpcurl` 调用 4 Service 全部 RPC 返回正确;HealthService.Check 返回 SERVING(解决 N1)
|
||||
|
||||
#### P4.8 buf generate + 类型校验
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.7
|
||||
- **交付物**:`pnpm buf:generate` 生成 TS 类型;content 服务引用生成类型
|
||||
- **验收标准**:`pnpm typecheck` 通过
|
||||
|
||||
#### P4.9 /readyz 多依赖检查
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.5
|
||||
- **交付物**:[health.controller.ts](../../../services/content/src/shared/health/health.controller.ts) 改造 /readyz,检查 DB / Neo4j / Kafka producer / Kafka consumer lag
|
||||
- **验收标准**:返回 design doc §6.6 格式;Neo4j 不可用 → status=degraded;DB 不可用 → status=down(解决 N2 + 01-understanding C-section readyz 问题)
|
||||
|
||||
#### P4.10 ZodError GlobalErrorFilter 分支
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:无
|
||||
- **交付物**:[global-error.filter.ts](../../../services/content/src/shared/errors/global-error.filter.ts) 增加 ZodError 识别分支,返回 400 + 字段级错误详情
|
||||
- **验收标准**:Zod 校验失败返回 design doc §4.3 错误结构
|
||||
|
||||
#### P4.11 DB 改 getDb() + ID 改 cuid2
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- [database.ts](../../../services/content/src/config/database.ts) 改为 `getDb()` 函数式懒加载(对齐 classes 黄金模板)
|
||||
- service 层 `randomUUID()` 改为 `cuid2()`(package.json 添加 @paralleldrive/cuid2)
|
||||
- **验收标准**:所有 service 使用 getDb();所有 ID 生成用 cuid2
|
||||
|
||||
#### P4.12 Repository 抽象补齐
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.11
|
||||
- **交付物**:补齐 textbooks/questions 的 Repository 抽象(与 chapters/knowledge-points 一致)
|
||||
- **验收标准**:Service 层不直接调用 Drizzle API,全部走 Repository
|
||||
|
||||
#### P4.13 单元测试 ≥ 60%
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.12
|
||||
- **交付物**:
|
||||
- 新建 `*.spec.ts` 覆盖 Service 层 + Repository 层
|
||||
- 重点覆盖 QuestionsService 题型校验 / KnowledgePointsService 前置依赖 / Outbox Publisher 重试逻辑
|
||||
- **验收标准**:`pnpm test` 通过;覆盖率 ≥ 60%
|
||||
|
||||
#### P4.14 修正 README 与实现对齐
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4.13
|
||||
- **交付物**:[README.md](../../../services/content/README.md) 修正 `TextbooksService.createKnowledgeGraph` 错误描述(实际在 KnowledgePointsService);补齐 4 个领域模块说明
|
||||
- **验收标准**:README 与源码完全一致(解决 01-understanding C3)
|
||||
|
||||
### 3.2 P5 阶段任务
|
||||
|
||||
#### P5.1 引入 @elastic/elasticsearch
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P4 全部完成
|
||||
- **交付物**:package.json 添加 @elastic/elasticsearch;新建 `src/config/elasticsearch.ts`
|
||||
- **验收标准**:esClient 单例;ES_URL 未配置时 esClient=null 降级
|
||||
|
||||
#### P5.2 ES mapping + ensureIndex
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.1
|
||||
- **交付物**:按 design doc §3.3.1 实现 questions 索引 mapping;启动时 `ensureIndex` 幂等
|
||||
- **验收标准**:索引创建成功;ik_max_word / ik_smart 分词器配置正确
|
||||
|
||||
#### P5.3 ES Sync Worker
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.2
|
||||
- **交付物**:新建 `src/shared/sync/es-sync.worker.ts`,消费 `QuestionCreated` / `QuestionUpdated` / `QuestionPublished` / `QuestionDeleted` 事件增量更新索引
|
||||
- **验收标准**:MySQL 写题目后,ES 索引最终一致(延迟 < 2s)
|
||||
|
||||
#### P5.4 GET /questions/search 检索 API
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.3
|
||||
- **交付物**:[questions.controller.ts](../../../services/content/src/questions/questions.controller.ts) 增加 `@Get("search")` 端点;ES 查询支持 q / type / difficulty / knowledgePointId 过滤
|
||||
- **验收标准**:检索延迟 < 200ms(P5 退出标准);返回分页结构
|
||||
|
||||
#### P5.5 QuestionService gRPC 完善 Publish/Search
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.4
|
||||
- **交付物**:gRPC controller 补 PublishQuestion / SearchQuestions RPC 实现
|
||||
- **验收标准**:与 contract.md §1.1 完全对齐(解决 ISSUE-004 部分)
|
||||
|
||||
#### P5.6 AI 出题 BatchCreateQuestions 联调
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.5 + ai12 ai 服务就绪
|
||||
- **交付物**:与 ai12 联调 BatchCreateQuestions RPC;服务账号权限校验
|
||||
- **验收标准**:AI 服务调用成功入库;batch_size ≤ 100 限制生效
|
||||
|
||||
#### P5.7 检索性能优化
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.6
|
||||
- **交付物**:ES 查询 DSL 优化;缓存热点查询结果(Redis)
|
||||
- **验收标准**:P95 延迟 < 200ms
|
||||
|
||||
#### P5.8 测试覆盖率 ≥ 80%
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5.7
|
||||
- **交付物**:补集成测试(gRPC + ES + Neo4j 端到端)
|
||||
- **验收标准**:覆盖率 ≥ 80%
|
||||
|
||||
### 3.3 P6+ 演进任务
|
||||
|
||||
#### P6.1 Question 审核工作流状态机
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P5 完成
|
||||
- **交付物**:Question.status 状态机完整实现(draft → pending_review → published/rejected → archived);审核日志表
|
||||
- **验收标准**:状态转换校验正确;非法转换返回 409
|
||||
|
||||
#### P6.2 知识图谱可视化 API
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P6.1
|
||||
- **交付物**:`GET /knowledge-graph/visualization` 返回 nodes/edges 结构(解决 01-understanding L3)
|
||||
- **验收标准**:返回 D3.js / vis.js 可消费的图结构
|
||||
|
||||
#### P6.3 教材版本管理
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P6.2
|
||||
- **交付物**:Textbook.version 字段启用;版本切换不破坏题库引用
|
||||
- **验收标准**:新旧版本教材并存;题库引用按版本隔离
|
||||
|
||||
#### P6.4 /readyz 硬化 + 监控告警完善
|
||||
|
||||
- **负责人**:ai09
|
||||
- **依赖**:P6.3
|
||||
- **交付物**:/readyz 探针列表完善;Prometheus 告警规则补齐(consumer lag / outbox pending / ES latency)
|
||||
- **验收标准**:告警阈值合理;故障演练通过
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai09 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai09 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游 | 就绪标志 | 必需性 | mock 策略 |
|
||||
| -------------- | ----------------------------------------------------- | ---------------------- | ------------------------------------------ |
|
||||
| infra | MySQL 8 / Neo4j 5 / Kafka 集群可用 | 🔴 必需 | 本地 docker-compose |
|
||||
| infra | Redis 可用(P5+ 缓存用,env.ts 已预留 REDIS_URL) | 🟢 P5+ 可选 | 未配置时跳过缓存 |
|
||||
| infra | Elasticsearch 8 可用(P5+) | 🟢 P5+ 必需 | 未配置时检索降级到 MySQL LIKE |
|
||||
| shared-proto | content.proto / events.proto 补全 | 🔴 必需(P4.6 自身完成) | 自行修改 |
|
||||
| core-edu (ai08) | gRPC 50053 启用 | 🟢 可选(content 独立) | 不依赖 core-edu 实时数据 |
|
||||
| ai (ai12) | gRPC 50058 启用 | 🟢 P5 联调时必需 | grpc-mock 拦截 |
|
||||
|
||||
### 4.2 我的就绪标志(供下游消费)
|
||||
|
||||
- [ ] **P4 就绪**:
|
||||
- [ ] content gRPC 50054 启用(HealthService.Check 返回 SERVING)
|
||||
- [ ] TextbookService 5 RPC 可调用(Create/Get/List/Update/Delete)
|
||||
- [ ] ChapterService 5 RPC 可调用(Create/Get/List/Update/Delete)
|
||||
- [ ] KnowledgeGraphService 4 RPC 可调用(GetPrerequisites/GetLearningPath/AddPrerequisite/RemovePrerequisite)
|
||||
- [ ] QuestionService 7 RPC 可调用(Create/BatchCreate/Get/List/Update/Delete/Publish/Search)
|
||||
- [ ] edu.content.knowledge_point.events / edu.content.question.events topic 可发布
|
||||
- [ ] /readyz 返回 DB/Neo4j/Kafka 三依赖状态
|
||||
- [ ] **P5 就绪**:
|
||||
- [ ] GET /questions/search 检索 API 可用(延迟 < 200ms)
|
||||
- [ ] QuestionService.SearchQuestions gRPC 可调用
|
||||
- [ ] BatchCreateQuestions 与 ai12 联调通过
|
||||
- [ ] **P6+ 就绪**:
|
||||
- [ ] 审核工作流状态机完整
|
||||
- [ ] 知识图谱可视化 API 可用
|
||||
|
||||
### 4.3 我提供的 mock(供下游消费)
|
||||
|
||||
在 content 真实服务就绪前,为下游(teacher-bff / student-bff / ai / data-ana)提供以下 mock:
|
||||
|
||||
- **gRPC mock**(grpc-mock 拦截 50054 端口):
|
||||
- TextbookService.ListTextbooks 返回固定 5 个 Textbook(语数英理化)
|
||||
- ChapterService.ListChapters 返回固定章节树(每教材 10 章)
|
||||
- KnowledgeGraphService.GetLearningPath 返回固定 8 个 KnowledgePoint 推荐顺序
|
||||
- KnowledgeGraphService.GetPrerequisites 返回固定 3 个前置知识点
|
||||
- QuestionService.SearchQuestions 返回固定 20 个 Question(含 options)
|
||||
- QuestionService.BatchCreateQuestions 返回成功 + 生成 20 个 ID
|
||||
- **Kafka mock**:content 就绪前不发布真实事件,下游使用本地 stub
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与缓解
|
||||
|
||||
| 风险 | 阶段 | 缓解措施 |
|
||||
| ------------------------------------------ | ---- | ------------------------------------------------------------ |
|
||||
| ISSUE-002 topic 策略未仲裁导致 Outbox 阻塞 | P4 | 优先推动 coord 仲裁;开发期间用 stub topic,仲裁后切换 |
|
||||
| ISSUE-004 RPC 数量未仲裁导致 proto 阻塞 | P4 | 优先推动 coord 仲裁;按建议方案 21 RPC 实现,仲裁后调整 |
|
||||
| Neo4j 与 MySQL 双向一致性 | P4 | Outbox 事件驱动 + 幂等去重 + consumer lag 监控 |
|
||||
| ES 索引重建期间检索不可用 | P5 | alias 切换模式(双索引蓝绿) |
|
||||
| AI 批量出题 CreateQuestions 高并发 | P5 | batch_size ≤ 100 + 异步队列 + 限流 |
|
||||
| schema 迁移期间历史数据 created_by 缺失 | P4 | 按 ISSUE-007 方案:NULL 起步或 'system' 默认值回填 |
|
||||
|
||||
---
|
||||
|
||||
## §6 与 workline.md §1 对齐
|
||||
|
||||
- 批次 3(P4):ai09 content 11 天(workline.md §1 排期 `after b2b, 11d`)
|
||||
- 本排期 P4 实际 21 天(含测试 + README 修正),与 workline.md 11 天存在差距
|
||||
- **差距原因**:workline.md §1 为 coord 初始规划(标注"ai09 接管后必须自行细化"),本文件为 ai09 细化后的实际排期
|
||||
- **同步动作**:提请 coord 在 workline.md §1 更新 content 工期为 21 天(或协调压缩测试任务)
|
||||
|
||||
@@ -1,45 +1,370 @@
|
||||
# core-edu 工作排期
|
||||
|
||||
> 负责人:ai08
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/core-edu_contract.md](../contracts/core-edu_contract.md)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/core-edu_contract.md](../contracts/core-edu_contract.md)、[services/core-edu/docs/02-architecture-design.md](../../../services/core-edu/docs/02-architecture-design.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 批次归属:批次 2(P3 核心教学),关键路径
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
core-edu 是教学核心服务,提供 ClassService、ExamService、HomeworkService、GradeService、AttendanceService,并基于 Outbox 模式发布领域事件。全阶段目标:P2 服务骨架+Outbox → P3 五大 Service 实现 → P4-P6 持续优化。
|
||||
core-edu 是教学核心服务,承载 D2 教学组织(classes)+ D3 教学核心(exams / homework / grades / attendance / schedule)两个限界上下文。
|
||||
|
||||
**全阶段目标**:
|
||||
|
||||
| 阶段 | 目标 | 就绪信号 |
|
||||
| ---- | ---- | -------- |
|
||||
| P2(已部分完成) | 服务骨架 + Outbox 模式 + REST CRUD + 三支柱可观测 | HTTP 3004 可访问 + /healthz + /readyz(DB 探针) |
|
||||
| P3(核心) | gRPC 50053 启用 + 5 Service 全量 RPC + 状态机 + Outbox 事件全量 + 排课考勤 + 成绩计算配置化 + Temporal 试点 | gRPC 50053 + 27 RPC + HealthService SERVING |
|
||||
| P4 | 持续优化 + 消费 data-ana mastery 事件 + content gRPC 调用(知识点关联) | — |
|
||||
| P5 | 配合 msg 服务事件消费联调 + AI 辅助批改预留 | — |
|
||||
| P6 | /readyz 硬化 + Outbox relay 迁独立 Go 服务评估 + 多租户行级隔离 | — |
|
||||
|
||||
**当前状态(2026-07-10)**:
|
||||
- ✅ P2 骨架已就绪(exams/homework/grades 三域 REST CRUD + Outbox + 三支柱)
|
||||
- ⏳ P3 待启动(依赖 ISSUE-001 ~ ISSUE-006 仲裁结果)
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai08 core-edu 全阶段排期
|
||||
title ai08 core-edu 全阶段排期(P2-P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a8a, 2026-07-10, Xd
|
||||
section P2 骨架(已就绪)
|
||||
P2 服务骨架+Outbox+REST CRUD :done, p2, 2026-07-01, 7d
|
||||
|
||||
section P3 核心教学(批次2 关键路径)
|
||||
P3.0 等待 coord 仲裁 ISSUE-001~006 :crit, p3a, 2026-07-10, 2d
|
||||
P3.1 黄金模板对齐(Drizzle getDb+Zod+kafka logger+/readyz探针) :crit, p3b, after p3a, 2d
|
||||
P3.2 TOPIC_MAP 重命名 edu.teaching.* + payload schema_version/event_id :crit, p3c, after p3b, 1d
|
||||
P3.3 考试状态机+作业状态机+成绩幂等 :crit, p3d, after p3c, 3d
|
||||
P3.4 gRPC server 50053 启用+5 Service 27 RPC :crit, p3e, after p3d, 3d
|
||||
P3.5 排课考勤数据模型+AttendanceService :p3f, after p3e, 2d
|
||||
P3.6 成绩计算配置化(grade_formulas+GradeCalculator) :p3g, after p3e, 2d
|
||||
P3.7 作业提交 Redis 分布式锁 :p3h, after p3e, 1d
|
||||
P3.8 DataScope 下推(Repository WHERE 注入) :p3i, after p3e, 1d
|
||||
P3.9 消费 IAM 事件(user.created/updated/deleted) :p3j, after p3e, 1d
|
||||
P3.10 Temporal 工作流试点(考试发布编排) :p3k, after p3e, 2d
|
||||
P3.11 classes 服务合并到 core-edu :p3l, after p3e, 2d
|
||||
P3.12 测试覆盖率≥80%+Dockerfile多阶段核对 :p3m, after p3l, 2d
|
||||
|
||||
section P4 持续优化
|
||||
P4.1 消费 data-ana mastery.updated 事件 :p4a, after p3m, 2d
|
||||
P4.2 content gRPC 调用(知识点关联) :p4b, after p4a, 2d
|
||||
P4.3 读模型双轨读策略验证(MySQL+ClickHouse) :p4c, after p4b, 1d
|
||||
|
||||
section P5 联调
|
||||
P5.1 msg 事件消费联调(通知触发) :p5a, after p4c, 2d
|
||||
P5.2 AI 辅助批改接口预留(GetExam/ListGradesByExam) :p5b, after p5a, 1d
|
||||
|
||||
section P6 硬化
|
||||
P6.1 /readyz 硬化(Kafka+Redis+Temporal 探针) :p6a, after p5b, 1d
|
||||
P6.2 Outbox relay 迁独立 Go 服务评估 :p6b, after p6a, 2d
|
||||
P6.3 多租户行级隔离验证 :p6c, after p6b, 1d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai08 接管后必须自行细化为完整 P2-P6 排期。
|
||||
**关键路径**:P3.0(仲裁)→ P3.1(模板对齐)→ P3.2(TOPIC_MAP)→ P3.3(状态机)→ P3.4(gRPC)→ P3.5/P3.6/P3.7/P3.8/P3.9/P3.10/P3.11(并行)→ P3.12(测试)
|
||||
|
||||
**预计工期**:
|
||||
- P3:13 天(含 2 天等待仲裁)
|
||||
- P4:5 天
|
||||
- P5:3 天
|
||||
- P6:4 天
|
||||
- 合计:25 天(P3 后)
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### P3.0 等待 coord 仲裁 ISSUE-001 ~ ISSUE-006
|
||||
|
||||
- **负责人**:ai08(等待 coord)
|
||||
- **依赖**:无
|
||||
- **交付物**:coord 出具仲裁结论(见 [coord.md](../coord.md) 后续章节)
|
||||
- **验收标准**:6 项 ISSUE 全部裁决,状态更新为"已裁决"
|
||||
- **阻塞说明**:
|
||||
- ISSUE-001(proto 实际状态):阻塞 P3.4 gRPC 实现
|
||||
- ISSUE-002(events.proto 未同步):阻塞 P3.2 TOPIC_MAP 重命名
|
||||
- ISSUE-003(状态命名不一致):阻塞 P3.3 状态机实现
|
||||
- ISSUE-004(class.transferred topic):阻塞 P3.11 classes 合并
|
||||
- ISSUE-005(RPC 数量口径):阻塞就绪信号声明
|
||||
- ISSUE-006(7 项设计决策):阻塞 P3 全部实施
|
||||
|
||||
### P3.1 黄金模板对齐(P0)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **交付物**:⚠️ 由 ai08 自行补充
|
||||
- **依赖**:见 [contracts/core-edu_contract.md](../contracts/core-edu_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai08 自行补充
|
||||
- **依赖**:无(可与其他 P3 任务并行)
|
||||
- **交付物**:
|
||||
1. `src/config/database.ts` 改为 `getDb()` 函数式(对齐 classes 黄金模板)
|
||||
2. `src/config/kafka.ts` L20/L22 `console.log`/`console.warn` → `logger.info`/`logger.warn`
|
||||
3. 全部 Controller 接入 Zod ValidationPipe(exams/homework/grades schema 已存在,需接入到 Controller)
|
||||
4. `src/shared/health/health.controller.ts` /readyz 补 Redis ping + Kafka producer 连接探针
|
||||
- **验收标准**:
|
||||
- `pnpm run lint` + `pnpm run typecheck` 零错误
|
||||
- /readyz 返回 3 项依赖状态(MySQL + Redis + Kafka)
|
||||
- kafka.ts 无 console.* 调用
|
||||
|
||||
### P3.2 TOPIC_MAP 重命名 + payload 补字段(P0)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:ISSUE-002 仲裁(events.proto 同步)
|
||||
- **交付物**:
|
||||
1. `src/shared/outbox/outbox.publisher.ts` TOPIC_MAP 改为 `edu.teaching.<aggregate>.<action>` 风格:
|
||||
- `edu.exam.events` → `edu.teaching.exam.created` / `.updated` / `.deleted` / `.published` / `.submitted`
|
||||
- `edu.homework.events` → `edu.teaching.homework.assigned` / `.submitted` / `.graded`
|
||||
- `edu.grade.events` → `edu.teaching.grade.recorded` / `.updated`
|
||||
- `edu.class.events` → 按 ISSUE-004 仲裁结果
|
||||
- 新增 `edu.teaching.attendance.recorded`
|
||||
2. outbox payload 补 `schema_version`(默认 `"v1"`)+ `event_id`(UUID)+ `occurred_at`(业务时间戳)+ `metadata: { traceId, userId }`
|
||||
3. `src/shared/outbox/outbox.schema.ts` 补 `event_id`、`occurred_at`、`next_retry_at` 字段 + `uniq_event_id` 唯一索引
|
||||
- **验收标准**:
|
||||
- TOPIC_MAP 命名符合 coord §3.1 仲裁
|
||||
- outbox 表新增字段已迁移
|
||||
- payload JSON 含 schema_version + event_id + occurred_at + metadata
|
||||
|
||||
### P3.3 考试/作业状态机 + 成绩幂等(P1)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:ISSUE-003 仲裁(状态命名统一)
|
||||
- **交付物**:
|
||||
1. `src/exams/domain/exam-state-machine.ts`(纯函数:canTransition / transition)
|
||||
2. `src/homework/domain/homework-state-machine.ts`(纯函数)
|
||||
3. `src/exams/exams.service.ts` 接入状态机(PublishExam / StartExam / SubmitExam / GradeExam / ArchiveExam)
|
||||
4. `src/homework/homework.service.ts` 接入状态机(SubmitHomework / GradeHomework)
|
||||
5. `src/grades/grades.schema.ts` 补 `idempotency_key` 字段 + `uniq_student_exam` / `uniq_student_hw` / `uniq_idempotency` 唯一索引
|
||||
6. `src/grades/grades.service.ts` 实现幂等录入(先 SELECT 检查,再 INSERT)
|
||||
- **验收标准**:
|
||||
- 状态机非法转换抛 `CORE_EDU_EXAM_INVALID_STATUS_TRANSITION`(409)
|
||||
- 同一学生同一考试/作业重复录入返回已存在成绩(幂等)
|
||||
- 状态命名按 ISSUE-003 仲裁结果统一
|
||||
|
||||
### P3.4 gRPC server 启用 + 5 Service 27 RPC(P1)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:ISSUE-001 仲裁(proto 补全)、ISSUE-005 仲裁(RPC 统计口径)
|
||||
- **交付物**:
|
||||
1. `src/main.ts` 启用 gRPC server(端口 50053,`@grpc/grpc-js` + `@bufbuild/protobuf`)
|
||||
2. `src/exams/exams.grpc-controller.ts`(ExamService 8 RPC:CreateExam / GetExam / ListExamsByClass / UpdateExam / DeleteExam / PublishExam / SubmitExam / GradeExam)
|
||||
3. `src/homework/homework.grpc-controller.ts`(HomeworkService 5 RPC:AssignHomework / GetHomework / ListHomeworkByClass / SubmitHomework / GradeHomework)
|
||||
4. `src/grades/grades.grpc-controller.ts`(GradeService 6 RPC:RecordGrade / GetGrade / ListGradesByStudent / ListGradesByExam / ListGradesByHomework / UpdateGrade)
|
||||
5. `src/attendance/attendance.grpc-controller.ts`(AttendanceService 4 RPC:RecordAttendance / GetAttendance / ListAttendanceByStudent / ListAttendanceByClass)
|
||||
6. `src/classes/classes.grpc-controller.ts`(ClassService 4 RPC:GetClass / GetClassesByTeacher / BatchGetClasses / ListStudentsByClass)
|
||||
7. `src/shared/health/health.grpc-controller.ts`(HealthService.Check 返回 SERVING)
|
||||
- **验收标准**:
|
||||
- gRPC 50053 可连接,HealthService.Check 返回 SERVING
|
||||
- 27 RPC 全部可调用(含 P3 新增 5 RPC)
|
||||
- gRPC 调用走 AuthMiddleware + PermissionGuard(gRPC 上下文适配)
|
||||
|
||||
### P3.5 排课考勤数据模型 + AttendanceService(P2)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:P3.4 gRPC 启用
|
||||
- **交付物**:
|
||||
1. `src/schedule/schedule.schema.ts`(core_edu_courses / core_edu_lessons / core_edu_schedules 三表)
|
||||
2. `src/attendance/attendance.schema.ts`(core_edu_attendance 表)
|
||||
3. `src/schedule/schedule.service.ts`(含 ScheduleConflictChecker:教师/班级时间冲突检测)
|
||||
4. `src/attendance/attendance.service.ts`(含唯一索引幂等)
|
||||
5. `src/schedule/schedule.controller.ts` + `src/attendance/attendance.controller.ts`(REST + gRPC 双入口)
|
||||
- **验收标准**:
|
||||
- 排课冲突检测正确(教师时间重叠抛 CORE_EDU_SCHEDULE_CONFLICT 409)
|
||||
- 考勤录入幂等(同一学生同一课时仅 1 条)
|
||||
|
||||
### P3.6 成绩计算配置化(P2)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:P3.4 gRPC 启用、ISSUE-006 决策 #3(scope 优先级)
|
||||
- **交付物**:
|
||||
1. `src/grades/grade-formulas.schema.ts`(core_edu_grade_formulas 表:scope / scope_id / formula_type / weights / custom_expression / effective_from / effective_to)
|
||||
2. `src/grades/domain/grade-calculator.ts`(纯函数:按 scope 优先级查询公式 → 加权/平均/自定义求值)
|
||||
3. `src/grades/grades.service.ts` 接入 GradeCalculator
|
||||
4. custom 公式安全求值(白名单正则 + Function 沙箱)
|
||||
- **验收标准**:
|
||||
- 三种公式类型(weighted / average / custom)均正确计算
|
||||
- scope 优先级按 ISSUE-006 决策 #3 仲裁结果
|
||||
- custom 公式禁止函数调用 / 对象访问 / eval(白名单校验)
|
||||
|
||||
### P3.7 作业提交 Redis 分布式锁(P2)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:P3.4 gRPC 启用、Redis 部署
|
||||
- **交付物**:
|
||||
1. `src/shared/redis/redis.client.ts`(Redis 单例)
|
||||
2. `src/homework/homework.service.ts` submitHomework 接入 Redis 分布式锁
|
||||
- 锁 key:`lock:hw:submit:{homeworkId}:{studentId}`
|
||||
- 锁过期:30s
|
||||
- 重试:5 次 × 100ms 间隔
|
||||
- 超时:429 CORE_EDU_HOMEWORK_SUBMIT_LOCK_TIMEOUT
|
||||
3. 同步对 exam submit 也接入锁(锁 key:`lock:exam:submit:{examId}:{studentId}`)
|
||||
- **验收标准**:
|
||||
- 50 并发提交同一作业,仅 1 条提交入库
|
||||
- 锁超时返回 429
|
||||
- DB 唯一索引兜底(锁失效时仍幂等)
|
||||
|
||||
### P3.8 DataScope 下推(P1)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
1. `src/shared/datascope/datascope-injector.ts`(根据 x-user-data-scope 头注入 WHERE 条件)
|
||||
2. `src/exams/exams.repository.ts` 接入 DataScopeInjector(按 class_id / school_id 过滤)
|
||||
3. `src/grades/grades.repository.ts` 接入 DataScopeInjector(按 student_id / class_id / school_id 过滤)
|
||||
4. `src/middleware/permission.guard.ts` 增强:解析 dataScope 注入到 request
|
||||
- **验收标准**:
|
||||
- 教师仅能查自己班级的考试/成绩
|
||||
- 学生仅能查自己的成绩
|
||||
- 校管理员可查全校
|
||||
|
||||
### P3.9 消费 IAM 事件(P1)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:IAM gRPC 50052 就绪(ai06)
|
||||
- **交付物**:
|
||||
1. `src/shared/kafka/kafka.consumer.ts`(Kafka consumer 单例)
|
||||
2. `src/iam-events/iam-user.consumer.ts`(订阅 edu.identity.user.created / .updated / .deleted)
|
||||
3. `src/iam-events/teacher-associations.schema.ts`(core_edu_teacher_associations 表 + uniq_teacher_class_subject 唯一索引)
|
||||
4. 消费幂等(基于 user_id + class_id + subject_id 唯一索引)
|
||||
- **验收标准**:
|
||||
- IAM 发布 user.created 事件后,core-edu 写入 teacher_associations
|
||||
- 重复消费同一事件不重复写入(幂等)
|
||||
- user.deleted 事件软删除教师关联(保留历史成绩归属)
|
||||
|
||||
### P3.10 Temporal 工作流试点(P3)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:Temporal server 部署(infra)、P3.4 gRPC 启用
|
||||
- **交付物**:
|
||||
1. `src/workflows/exam-publish.workflow.ts`(考试发布编排工作流)
|
||||
2. `src/workflows/exam-publish.activities.ts`(5 个 Activity:创建提交骨架 / 通知 msg / 等待作答窗口 / 自动提交未答 / 通知教师)
|
||||
3. `src/exams/exams.service.ts` publishExam 调用 `workflowClient.start(examPublishWorkflow, ...)`
|
||||
4. `src/shared/temporal/temporal.client.ts`(Temporal client 单例)
|
||||
- **验收标准**:
|
||||
- 考试发布后 Temporal UI 可见工作流实例
|
||||
- 工作流完成 5 个 Activity
|
||||
- 失败可重试
|
||||
|
||||
### P3.11 classes 服务合并到 core-edu(P3)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:ISSUE-006 决策 #1(合并时机)仲裁、P3.4 gRPC 启用
|
||||
- **交付物**:
|
||||
1. `services/classes/src/` 代码迁入 `services/core-edu/src/classes/`
|
||||
2. 删除独立 `services/classes/` 目录
|
||||
3. `src/classes/classes.module.ts` 接入 core-edu AppModule
|
||||
4. `src/classes/classes.grpc-controller.ts`(ClassService 4 RPC)
|
||||
5. classes 错误码 `CLASSES_*` 保留(coord §5.5 仲裁,黄金模板历史遗留)
|
||||
- **验收标准**:
|
||||
- classes 服务代码完全迁入 core-edu
|
||||
- ClassService 4 RPC 可调用
|
||||
- 原 classes 服务端口 3001 不再存在
|
||||
- `pnpm run arch:scan` 确认 arch.db 已更新
|
||||
|
||||
### P3.12 测试覆盖率 + Dockerfile 核对(P3)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:P3.1 ~ P3.11 全部完成
|
||||
- **交付物**:
|
||||
1. 单元测试:状态机 / GradeCalculator / ScheduleConflictChecker / DataScopeInjector(纯函数优先)
|
||||
2. 集成测试:ExamsService / HomeworkService / GradesService / AttendanceService / ScheduleService
|
||||
3. Outbox relay 测试(mock Kafka)
|
||||
4. `Dockerfile` 多阶段构建核对(builder → runner,最终镜像无 devDependencies)
|
||||
- **验收标准**:
|
||||
- `pnpm run test` 通过
|
||||
- 覆盖率 ≥ 80%
|
||||
- Dockerfile 多阶段构建,最终镜像 ≤ 200MB
|
||||
|
||||
### P4.1 消费 data-ana mastery.updated 事件
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:data-ana gRPC 50055 就绪(ai11)
|
||||
- **交付物**:`src/data-ana-events/mastery.consumer.ts`(订阅 edu.insight.mastery.updated,基于 mastery_score_id 幂等)
|
||||
- **验收标准**:重复消费不重复写入
|
||||
|
||||
### P4.2 content gRPC 调用(知识点关联)
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:content gRPC 50054 就绪(ai09)
|
||||
- **交付物**:`src/content/content.client.ts`(调用 ContentService.GetKnowledgePoints,排课关联知识点)
|
||||
- **验收标准**:lessons.knowledge_point_ids 引用的知识点在 content 服务可查
|
||||
|
||||
### P4.3 读模型双轨读策略验证
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:data-ana CDC 链路就绪
|
||||
- **交付物**:验证刚提交成绩查 MySQL(强一致),聚合统计查 ClickHouse(最终一致 < 5s)
|
||||
- **验收标准**:双轨读策略符合 02 文档 §3.3
|
||||
|
||||
### P5.1 msg 事件消费联调
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:msg gRPC 50056 就绪(ai10)
|
||||
- **交付物**:验证 msg 消费 core-edu 事件触发通知(edu.teaching.exam.created / homework.assigned / grade.recorded)
|
||||
- **验收标准**:端到端事件链路通
|
||||
|
||||
### P5.2 AI 辅助批改接口预留
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:无
|
||||
- **交付物**:确认 ExamService.GetExam / GradeService.ListGradesByExam 接口对 ai 服务可用
|
||||
- **验收标准**:ai 服务可调用上述 RPC
|
||||
|
||||
### P6.1 /readyz 硬化
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:无
|
||||
- **交付物**:/readyz 补 Temporal client 连接探针
|
||||
- **验收标准**:/readyz 返回 4 项依赖状态(MySQL + Redis + Kafka + Temporal)
|
||||
|
||||
### P6.2 Outbox relay 迁独立 Go 服务评估
|
||||
|
||||
- **负责人**:ai08(评估,不实施)
|
||||
- **依赖**:无
|
||||
- **交付物**:评估报告(是否迁出进程内 relay)
|
||||
- **验收标准**:产出评估结论
|
||||
|
||||
### P6.3 多租户行级隔离验证
|
||||
|
||||
- **负责人**:ai08
|
||||
- **依赖**:无
|
||||
- **交付物**:验证 school_id 行级隔离(不同学校数据互不可见)
|
||||
- **验收标准**:跨学校查询返回空
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai08 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai08 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 依赖项 | 提供方 | 就绪信号 | 状态 | 影响 |
|
||||
| ------ | ------ | -------- | ---- | ---- |
|
||||
| coord 仲裁 ISSUE-001 ~ ISSUE-006 | coord | coord.md 仲裁章节 | ⏳ 待仲裁 | 阻塞 P3 全部 |
|
||||
| iam gRPC 50052 | ai06 | HealthService.Check = SERVING | ⏳ | 阻塞 P3.9 消费 IAM 事件 |
|
||||
| events.proto 同步 | coord | events.proto 含 AttendanceEvent + schema_version | ⏳ | 阻塞 P3.2 TOPIC_MAP |
|
||||
| core_edu.proto 补全 | coord 或 ai08 | 5 service 27 RPC 定义 | ⏳ | 阻塞 P3.4 gRPC 实现 |
|
||||
| Redis 部署 | infra | redis:6379 可连接 | ⏳ | 阻塞 P3.7 分布式锁 + P3.1 /readyz |
|
||||
| Temporal server 部署 | infra | temporal:7233 可连接 | ⏳ | 阻塞 P3.10 工作流试点 |
|
||||
| buf.gen.yaml gRPC 插件 | coord | buf generate 产出 TS gRPC 代码 | ⏳ | 阻塞 P3.4 gRPC 实现 |
|
||||
| data-ana gRPC 50055(P4) | ai11 | HealthService.Check = SERVING | ⏳ | 阻塞 P4.1 mastery 消费 |
|
||||
| content gRPC 50054(P4) | ai09 | HealthService.Check = SERVING | ⏳ | 阻塞 P4.2 知识点关联 |
|
||||
| msg gRPC 50056(P5) | ai10 | HealthService.Check = SERVING | ⏳ | 阻塞 P5.1 事件联调 |
|
||||
|
||||
### 4.2 我的就绪标志(供下游消费)
|
||||
|
||||
| 阶段 | 就绪信号 | 消费方 | 状态 |
|
||||
| ---- | -------- | ------ | ---- |
|
||||
| P2(已就绪) | HTTP 3004 可访问 + /healthz + /readyz(DB 探针) | teacher-bff(REST 调用) | ✅ |
|
||||
| P3(核心) | gRPC 50053 + 27 RPC + HealthService SERVING | teacher-bff / student-bff / parent-bff / ai | ⏳ |
|
||||
| P3 子信号 1 | ClassService 4 RPC 可调用 | teacher-bff(班级列表) | ⏳ |
|
||||
| P3 子信号 2 | ExamService 8 RPC 可调用 | teacher-bff / student-bff / ai | ⏳ |
|
||||
| P3 子信号 3 | HomeworkService 5 RPC 可调用 | teacher-bff / student-bff | ⏳ |
|
||||
| P3 子信号 4 | GradeService 6 RPC 可调用 | teacher-bff / student-bff / parent-bff | ⏳ |
|
||||
| P3 子信号 5 | AttendanceService 4 RPC 可调用 | parent-bff | ⏳ |
|
||||
| P3 子信号 6 | edu.teaching.* topic 可发布(含 attendance.recorded) | msg / data-ana / push-gateway | ⏳ |
|
||||
|
||||
### 4.3 Mock 策略(全并行开发期间)
|
||||
|
||||
详见 [contracts/core-edu_contract.md §4](../contracts/core-edu_contract.md)。
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
# data-ana 工作排期
|
||||
|
||||
> 负责人:ai11
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/data-ana_contract.md](../contracts/data-ana_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/data-ana_contract.md](../contracts/data-ana_contract.md)、[objections/data-ana_issue.md](../objections/data-ana_issue.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
> 基线日期:批次 0 已完成(2026-07-09),批次 1(P2)2026-07-10 启动
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
data-ana 是数据分析服务,提供 AnalyticsService(12 个 RPC),基于 ClickHouse 列存查询与 CDC 消费实现实时分析。全阶段目标:P2 ClickHouse 接入+CDC 消费 → P3 AnalyticsService 12 RPC → P4-P6 持续优化。
|
||||
data-ana 是 D6 智能洞察领域的纯读模型服务(Python/FastAPI),基于 ClickHouse ReplacingMergeTree 宽表 + Debezium CDC 消费实现实时学情分析。
|
||||
|
||||
**核心交付物**:
|
||||
- gRPC server :50055 + AnalyticsService 12 RPC(含 1 个 Server Streaming)
|
||||
- HTTP :3006 14 端点(3 基础 + 11 业务,保留作 Gateway 直连降级)
|
||||
- ClickHouse 5 宽表(student_dashboard_view / student_errors / mastery_snapshot / attendance_logs / ai_usage_log)
|
||||
- CDC 消费者(core-edu MySQL binlog → Kafka → ClickHouse 宽表投影)
|
||||
- 掌握度计算(加权滑动平均 + 遗忘曲线)+ 预警评估
|
||||
- 派生数据事件发布(edu.insight.mastery.updated / edu.insight.warning.triggered,豁免 Outbox)
|
||||
|
||||
**阶段里程碑**:
|
||||
- P2 预备:ClickHouse 接入 + CDC 骨架 + ActionState 信封重构 + mock 数据集
|
||||
- P3 预备:CDC 通道接入 + 掌握度算法 v1 + Repository 封装
|
||||
- P4 主战场:gRPC 50055 启用 + 12 RPC + 4 端 Dashboard + Warning + DataScope + 事件发布
|
||||
- P5 扩展:SubscribeMasteryUpdate stream + AI 用量消费 + 手动 commit
|
||||
- P6 硬化:CDC 水平扩展 + 容量规划 + 数据治理 + 监控告警
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
@@ -20,26 +36,317 @@ gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a11a, 2026-07-10, Xd
|
||||
section P2 预备(与批次1并行)
|
||||
2.1 ClickHouse DDL 5宽表建表 :a11a, 2026-07-10, 2d
|
||||
2.2 CDC消费骨架(aiokafka) :a11b, after a11a, 2d
|
||||
2.3 mock数据集(30学生×5考试×10作业) :a11c, after a11a, 1d
|
||||
2.4 ActionState信封重构(P0整改) :crit, a11d, 2026-07-10, 2d
|
||||
2.5 config.py修正(env/Redis/gRPC) :a11e, after a11d, 1d
|
||||
|
||||
section P3 预备(与批次2并行)
|
||||
3.1 gRPC server骨架(3 RPC,不启用) :a11f, after a11b, 2d
|
||||
3.2 core-edu CDC通道接入(grades/exams/homework) :a11g, after a11b, 3d
|
||||
3.3 ExamCache内存LRU :a11h, after a11g, 1d
|
||||
3.4 掌握度算法v1(weighted_moving_avg) :a11i, after a11g, 2d
|
||||
3.5 ClickHouseRepository(FINAL/argMax) :a11j, after a11i, 2d
|
||||
|
||||
section P4 主战场(批次3, 11d)
|
||||
4.1 gRPC 50055正式启用 :crit, a11k, after a11j, 1d
|
||||
4.2 analytics.proto扩展12 RPC :crit, a11l, after a11k, 2d
|
||||
4.3 4端Dashboard RPC实现 :a11m, after a11l, 3d
|
||||
4.4 WarningService+TriggerWarning :a11n, after a11l, 2d
|
||||
4.5 GetMasteryDistribution+GetStudentMastery :a11o, after a11m, 1d
|
||||
4.6 iam.GetEffectiveDataScope集成(降级兜底) :crit, a11p, after a11k, 2d
|
||||
4.7 DataScope 6级WHERE注入 :a11q, after a11p, 1d
|
||||
4.8 attendance+content CDC消费 :a11r, after a11g, 2d
|
||||
4.9 MasteryEvent+WarningTriggered发布 :a11s, after a11n, 1d
|
||||
4.10 HTTP 14端点+readyz硬化 :a11t, after a11m, 2d
|
||||
|
||||
section P5 扩展(批次4并行)
|
||||
5.1 SubscribeMasteryUpdate stream RPC :a11u, after a11t, 3d
|
||||
5.2 AIUsageEvent消费→ai_usage_log :a11v, after a11u, 2d
|
||||
5.3 手动commit替换auto_commit :a11w, after a11v, 1d
|
||||
5.4 Admin Dashboard AI用量区块 :a11x, after a11v, 2d
|
||||
|
||||
section P6 硬化(批次5并行)
|
||||
6.1 CDC多实例水平扩展 :a11y, after a11x, 3d
|
||||
6.2 ExamCache Redis化 :a11z, after a11y, 2d
|
||||
6.3 容量规划+TTL归档策略 :a11aa, after a11z, 2d
|
||||
6.4 监控告警(consumer lag HPA) :a11ab, after a11aa, 2d
|
||||
6.5 readyz深度硬化 :a11ac, after a11ab, 1d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai11 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> **关键路径**(crit):ActionState 信封重构 → gRPC 50055 启用 → analytics.proto 扩展 → iam GetEffectiveDataScope 集成
|
||||
> **总工期**:约 51 天(2026-07-10 ~ 2026-08-30),其中 P4 主战场 11 天为关键交付期
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### 3.1 P2 预备期(2026-07-10 ~ 2026-07-18,8d)
|
||||
|
||||
#### 任务 2.1:ClickHouse DDL 5 宽表建表
|
||||
|
||||
- **负责人**:ai11
|
||||
- **交付物**:⚠️ 由 ai11 自行补充
|
||||
- **依赖**:见 [contracts/data-ana_contract.md](../contracts/data-ana_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai11 自行补充
|
||||
- **依赖**:无(ClickHouse 实例就绪,由 infra 提供)
|
||||
- **交付物**:`infra/clickhouse/ddl/data_ana.sql`(5 宽表 DDL:student_dashboard_view / student_errors / mastery_snapshot / attendance_logs / ai_usage_log)
|
||||
- **验收标准**:5 表在 ClickHouse 中创建成功,ReplacingMergeTree 引擎 + ORDER BY + PARTITION BY 符合 02 §3 DDL 设计
|
||||
|
||||
#### 任务 2.2:CDC 消费骨架
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:Kafka 就绪
|
||||
- **交付物**:`src/data_ana/cdc_consumer.py` 重构(aiokafka AIOKafkaConsumer + EventHandler 路由框架)
|
||||
- **验收标准**:能消费 mock CDC 事件并打印路由日志,consumer group = `data-ana-cdc`
|
||||
|
||||
#### 任务 2.3:mock 数据集
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 2.1
|
||||
- **交付物**:`scripts/seed_clickhouse.py`(批量导入 30 学生 × 5 考试 × 10 作业 × 30 天出勤模拟数据)
|
||||
- **验收标准**:ClickHouse 5 表有数据,可查询返回非空结果
|
||||
|
||||
#### 任务 2.4:ActionState 信封重构(P0 整改,coord-cross-review §5.3)
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:无
|
||||
- **交付物**:`src/data_ana/shared/action_state.py`(ActionState[T] 泛型 + ActionStateError + ok()/fail() 类方法)
|
||||
- **验收标准**:main.py 所有端点返回 `ActionState[T]`,degraded 标记在顶层 `details.degraded`(非 error.details),ruff 零错误
|
||||
|
||||
#### 任务 2.5:config.py 修正
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:无
|
||||
- **交付物**:`src/data_ana/config.py` 修正(env_prefix 补 Redis / gRPC / ClickHouse 配置项,pydantic-settings 校验)
|
||||
- **验收标准**:配置项覆盖 02 §13 配置清单,环境变量缺失时 pydantic-settings 报错
|
||||
|
||||
---
|
||||
|
||||
### 3.2 P3 预备期(2026-07-18 ~ 2026-07-26,8d)
|
||||
|
||||
#### 任务 3.1:gRPC server 骨架(3 RPC,不正式启用)
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:analytics.proto 当前 3 RPC(无需 coord 补全)
|
||||
- **交付物**:`src/data_ana/grpc_server.py`(grpc.aio Server 骨架 + 3 RPC 实现,绑定 :50055 但不启动对外)
|
||||
- **验收标准**:本地可启动 gRPC server,3 RPC 可调用返回 mock 数据
|
||||
|
||||
#### 任务 3.2:core-edu CDC 通道接入
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:core-edu MySQL 就绪 + Debezium CDC 配置(core-edu 就绪前用 mock binlog 事件)
|
||||
- **交付物**:`src/data_ana/cdc_consumer.py` 完善(EventHandler 处理 grades/exams/homework/classes 表 CDC 事件)
|
||||
- **验收标准**:消费 CDC 事件 → 解析 Debezium JSON → 查 ExamCache 填 class_id → upsert ClickHouse 宽表
|
||||
|
||||
#### 任务 3.3:ExamCache 内存 LRU
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.2
|
||||
- **交付物**:`src/data_ana/exam_cache.py`(内存 LRU dict,max 10000 条,exam_id → {class_id, subject_id})
|
||||
- **验收标准**:CDC exams 事件触发 ExamCache 更新,grades 事件查 ExamCache 获取 class_id
|
||||
|
||||
#### 任务 3.4:掌握度算法 v1
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.2
|
||||
- **交付物**:`src/data_ana/mastery_service.py`(加权滑动平均算法,权重 w_i = 0.6^i,归一化)
|
||||
- **验收标准**:输入学生近期 N 次成绩 → 输出 mastery_level (0.0-1.0) → 写 mastery_snapshot 表
|
||||
|
||||
#### 任务 3.5:ClickHouseRepository 封装
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 2.1
|
||||
- **交付物**:`src/data_ana/clickhouse_client.py` 重构(查询封装 + FINAL/argMax 去重 + DataScope WHERE 注入接口)
|
||||
- **验收标准**:查询 student_dashboard_view 返回去重后最新版本数据
|
||||
|
||||
---
|
||||
|
||||
### 3.3 P4 主战场期(2026-07-26 ~ 2026-08-06,11d,批次 3)
|
||||
|
||||
#### 任务 4.1:gRPC 50055 正式启用
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.1
|
||||
- **交付物**:main.py lifespan 启动 gRPC server :50055,HealthService.Check 返回 SERVING
|
||||
- **验收标准**:gRPC server 对外可访问,HealthService.Check = SERVING
|
||||
|
||||
#### 任务 4.2:analytics.proto 扩展 12 RPC
|
||||
|
||||
- **负责人**:ai11(本分支内补全 proto,提请 coord 合并)
|
||||
- **依赖**:ISSUE-003 解决(coord 确认或 ai11 自行补全)
|
||||
- **交付物**:`packages/shared-proto/proto/analytics.proto` 扩展至 12 RPC(3 现有 + 9 新增 message 定义)
|
||||
- **验收标准**:`buf lint` 零错误,`buf generate` 生成 Python stub 成功,12 RPC 全部可调用
|
||||
|
||||
#### 任务 4.3:4 端 Dashboard RPC 实现
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 4.2
|
||||
- **交付物**:GetTeacherDashboard / GetStudentDashboard / GetParentDashboard / GetAdminDashboard 4 RPC 实现
|
||||
- **验收标准**:4 RPC 返回 ActionState[DashboardData],DataScope 过滤生效,降级时返回骨架数据 + degraded: true
|
||||
|
||||
#### 任务 4.4:WarningService + TriggerWarning
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.4(掌握度算法)
|
||||
- **交付物**:`src/data_ana/warning_service.py`(预警阈值评估 + TriggerWarning RPC + GetWarnings RPC)
|
||||
- **验收标准**:掌握度 < 0.4 触发 LOW_MASTERY 预警,成绩环比下降 20% 触发 SCORE_DROP,缺勤 ≥ 3 次/周触发 ABSENT_FREQUENT
|
||||
|
||||
#### 任务 4.5:GetMasteryDistribution + GetStudentMastery
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.4 + 任务 4.2
|
||||
- **交付物**:2 RPC 实现(班级掌握度分布 + 学生知识点掌握度明细)
|
||||
- **验收标准**:返回 mastered/progressing/weak 三档分布数据
|
||||
|
||||
#### 任务 4.6:iam.GetEffectiveDataScope 集成(降级兜底)
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:ISSUE-001 解决(iam.proto 补全 GetEffectiveDataScope)。若 P4 时 iam 未就绪,使用降级兜底
|
||||
- **交付物**:`src/data_ana/iam_client.py`(gRPC 调 iam.GetEffectiveDataScope + Redis 缓存 5min + 降级兜底)
|
||||
- **验收标准**:iam 可用时调 gRPC 获取 DataScope;iam 不可用时按 role 映射默认 DataScope + degraded: true
|
||||
|
||||
#### 任务 4.7:DataScope 6 级 WHERE 注入
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 4.6 + 任务 3.5
|
||||
- **交付物**:ClickHouseRepository 查询方法注入 DataScope WHERE 子句(SELF/CLASS/GRADE/SCHOOL/DISTRICT/ALL)
|
||||
- **验收标准**:教师只能查自己班级数据,学生只能查自己数据,管理员可查全校数据
|
||||
|
||||
#### 任务 4.8:attendance + content CDC 消费
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.2(CDC 框架)
|
||||
- **交付物**:EventHandler 扩展 attendance_logs 表 CDC + content_knowledge_points 表 CDC
|
||||
- **验收标准**:考勤事件落 attendance_logs 表,知识点事件更新 mastery_snapshot 元数据
|
||||
|
||||
#### 任务 4.9:MasteryEvent + WarningTriggered 事件发布
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.4 + 任务 4.4
|
||||
- **交付物**:`src/data_ana/kafka_producer.py`(aiokafka AIOKafkaProducer + idempotent + transactional_id)
|
||||
- **验收标准**:掌握度计算完成发布 `edu.insight.mastery.updated`,预警触发发布 `edu.insight.warning.triggered`,豁免 Outbox
|
||||
|
||||
#### 任务 4.10:HTTP 14 端点 + readyz 硬化
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 4.3 + 任务 4.4 + 任务 4.5
|
||||
- **交付物**:main.py 14 个 HTTP 端点全部实现(3 基础 + 11 业务)+ /readyz 检查 4 依赖(clickhouse/cdc_consumer/redis/iam_grpc)
|
||||
- **验收标准**:14 端点返回 ActionState[T],/readyz 依赖检查正确反映服务状态
|
||||
|
||||
---
|
||||
|
||||
### 3.4 P5 扩展期(2026-08-06 ~ 2026-08-19,13d,批次 4 并行)
|
||||
|
||||
#### 任务 5.1:SubscribeMasteryUpdate Server Streaming RPC
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 4.2 + 任务 4.9
|
||||
- **交付物**:SubscribeMasteryUpdate RPC 实现(server-streaming,客户端订阅 student_id/class_id,掌握度更新时推送)
|
||||
- **验收标准**:客户端订阅后,掌握度计算完成时收到 MasteryUpdateEvent 流
|
||||
|
||||
#### 任务 5.2:AIUsageEvent 消费 → ai_usage_log
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:ISSUE-002 解决(events.proto 补 AIUsageEvent)+ ai 服务发布 `edu.insight.ai.usage` topic
|
||||
- **交付物**:EventHandler 扩展 AIUsageEvent 消费 → 落 ai_usage_log 表
|
||||
- **验收标准**:ai 服务发布用量事件后,ai_usage_log 表有数据,Admin Dashboard AI 用量区块可展示
|
||||
|
||||
#### 任务 5.3:手动 commit 替换 auto_commit
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 3.2
|
||||
- **交付物**:cdc_consumer.py 改为 `enable_auto_commit=False` + 手动 commit(at-least-once)
|
||||
- **验收标准**:ClickHouse 写入成功后才 commit offset,重启后无重复消费(依赖 ReplacingMergeTree 去重)
|
||||
|
||||
#### 任务 5.4:Admin Dashboard AI 用量区块
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 5.2
|
||||
- **交付物**:GetAdminDashboard RPC 补全 AI 用量统计区块(按 provider/model/时间窗聚合)
|
||||
- **验收标准**:Admin Dashboard 返回 AI 用量数据,无数据时显示"暂无数据"
|
||||
|
||||
---
|
||||
|
||||
### 3.5 P6 硬化期(2026-08-19 ~ 2026-08-30,11d,批次 5 并行)
|
||||
|
||||
#### 任务 6.1:CDC 多实例水平扩展
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 5.3(手动 commit)
|
||||
- **交付物**:CdcConsumer 支持多实例分摊 partition(consumer group 不变)
|
||||
- **验收标准**:2+ 实例消费同一 topic 无重复无遗漏
|
||||
|
||||
#### 任务 6.2:ExamCache Redis 化
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 6.1
|
||||
- **交付物**:exam_cache.py 改为 Redis 实现(key: `data_ana:exam:{exam_id}` TTL 30 天)
|
||||
- **验收标准**:多实例共享 ExamCache,重启后缓存不丢失
|
||||
|
||||
#### 任务 6.3:容量规划 + TTL 归档策略
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:无
|
||||
- **交付物**:ClickHouse TTL 策略(student_dashboard_view 保留 2 年,ai_usage_log 保留 1 年)+ 冷热数据分离方案
|
||||
- **验收标准**:TTL 配置生效,过期数据自动清理
|
||||
|
||||
#### 任务 6.4:监控告警完善
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 6.1
|
||||
- **交付物**:Prometheus 指标补全(consumer lag histogram + 慢查询 counter + ClickHouse 连接池 gauge)+ Grafana dashboard
|
||||
- **验收标准**:consumer lag 超阈值触发 HPA,慢查询超阈值告警
|
||||
|
||||
#### 任务 6.5:readyz 深度硬化
|
||||
|
||||
- **负责人**:ai11
|
||||
- **依赖**:任务 4.10
|
||||
- **交付物**:/readyz 检查项完善(ClickHouse 查询超时 1s + Redis ping + iam gRPC 超时 2s + CDC consumer lag < 1000)
|
||||
- **验收标准**:任一依赖不健康时 /readyz 返回 503,K8s 摘流量
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai11 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai11 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
- [ ] **core-edu gRPC 50053 启用**(ai08,批次 2)—— CDC 数据源(grades/exams/homework/attendance 表 binlog)
|
||||
- [ ] **core-edu MySQL Debezium CDC 配置**(ai08 + SRE)—— CDC 通道前提
|
||||
- [ ] **content gRPC 50054 启用**(ai09,批次 3)—— 知识点维度 CDC
|
||||
- [ ] **iam.proto 补全 GetEffectiveDataScope RPC**(ai06/coord,ISSUE-001)—— DataScope 解析
|
||||
- [ ] **analytics.proto 扩展至 12 RPC**(coord/ai11,ISSUE-003)—— gRPC stub 生成前提
|
||||
- [ ] **events.proto 补全 AIUsageEvent message**(coord,ISSUE-002)—— AI 用量消费(P5)
|
||||
- [ ] **ai 服务发布 `edu.insight.ai.usage` topic**(ai12,批次 4)—— AI 用量统计(P5)
|
||||
|
||||
> **降级兜底**:core-edu / content / iam 未就绪时,使用 ClickHouse 内置 mock 数据集 + 硬编码 DataScope 降级,标注 `details.degraded: true`
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
- [ ] **P4 就绪**:data-ana gRPC 50055 启用(HealthService.Check = SERVING)+ AnalyticsService 12 RPC 可调用 + 4 端 Dashboard 返回结构化数据
|
||||
- [ ] **P4 就绪**:`edu.insight.mastery.updated` topic 可发布(mastery.updated / warning.triggered)
|
||||
- [ ] **P5 就绪**:SubscribeMasteryUpdate server-streaming RPC 可订阅
|
||||
- [ ] **P6 就绪**:CDC 多实例水平扩展 + ExamCache Redis 化完成
|
||||
|
||||
### 4.3 下游消费方
|
||||
|
||||
| 下游 | 消费接口 | 就绪依赖阶段 |
|
||||
| -------------------------- | ---------------------------------------------------------------- | ------------ |
|
||||
| teacher-bff(ai03) | gRPC 50055 GetTeacherDashboard / GetClassPerformance 等 | P4 |
|
||||
| student-bff(ai04) | gRPC 50055 GetStudentDashboard / GetStudentWeakness 等 | P4 |
|
||||
| parent-bff(ai05) | gRPC 50055 GetParentDashboard | P4 |
|
||||
| ai 服务(ai12) | gRPC 50055 反向调用查学情(GetStudentMastery / GetLearningTrend) | P5 |
|
||||
| core-edu(ai08) | Kafka `edu.insight.mastery.updated`(推荐个性化练习) | P4 |
|
||||
| msg(ai10) | Kafka `edu.insight.warning.triggered`(推送通知) | P4 |
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与缓解
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
| -------------------------------------------- | ---- | ------------------------------------------------------------------------------------------ |
|
||||
| iam.proto 未补全 GetEffectiveDataScope | P4 | 降级兜底:按 role 映射默认 DataScope + degraded: true(ISSUE-001) |
|
||||
| analytics.proto 未扩展 12 RPC | P4 | ai11 本分支自行补全 proto,提请 coord 合并(ISSUE-003) |
|
||||
| core-edu CDC 通道未就绪 | P3-P4 | mock 数据集降级 + 本地 stub CDC 事件 |
|
||||
| ClickHouse ReplacingMergeTree 去重延迟 | P4 | 查询加 FINAL / argMax 强制去重(02 §3.6 已设计) |
|
||||
| 单实例 CDC 消费者单点故障 | P4 | P6 演进为多实例 + Redis ExamCache;P4 阶段监控 consumer lag 告警 |
|
||||
| 掌握度算法精度不足 | P4 | v1 用加权滑动平均,P5+ 评估引入遗忘曲线 max 叠加(02 §9 已设计 MasteryMethod 枚举预留) |
|
||||
|
||||
@@ -3,54 +3,154 @@
|
||||
> 负责人:ai06
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/iam_contract.md](../contracts/iam_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 裁决依据:[coord-final-decisions](../../coord-final-decisions.md) I1-I8、[president-final-rulings](../../president-final-rulings.md) §3.2/§2.15/§2.16/§5.5
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
iam 是身份认证服务,全阶段目标:gRPC 50052 + 12 RPC + /readyz 深度 + iam_student_guardians + DataScope + 审计日志 + Outbox。
|
||||
iam 是身份认证服务,全阶段目标:gRPC 50052 + 12 RPC + REST 双入口 + /readyz 深度 + iam_student_guardians + DataScope 6 级 + 审计日志 + Outbox + RBAC CRUD 完整化。
|
||||
|
||||
**核心裁决约束**(coord-final-decisions I1-I8):
|
||||
|
||||
- I1:P2 即启用 gRPC server 50052(REST + gRPC 双入口并存,非"P2 仅 REST → P3 gRPC")
|
||||
- I2:直接用 shared-ts Outbox 工具包(非 iam 自建)
|
||||
- I3:首次实现即 DB 驱动 + Redis 缓存 PermissionGuard(废弃硬编码 ROLE_PERMISSIONS map)
|
||||
- I4:首次实现即注册 AuthMiddleware(Controller 通过 @Req() 注入用户上下文)
|
||||
- I5:P2 本地文件 RS256 密钥(IAM_PRIVATE_KEY_PATH / IAM_PUBLIC_KEY_PATH),P6 迁 Vault
|
||||
- I6:P2 即补全 iam_student_guardians 表 + GetChildrenByParent RPC + GET /iam/children
|
||||
- I7:/iam/v1/* 前缀(Controller 加 v1 前缀,Gateway 透传)
|
||||
- I8:统一 GET /iam/permissions/effective
|
||||
|
||||
**工作量分级**(president §3.2):iam 实际工作量 26-27 天,拆分 P2.1(核心,阻塞批次 2)+ P2.2(扩展,P3 期间持续补,不阻塞)。
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai06 iam 全阶段排期
|
||||
title ai06 iam 全阶段排期(P2.1/P2.2 拆分版)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2.1 核心
|
||||
gRPC server 50052启用 :crit, a6a, 2026-07-10, 2d
|
||||
12 RPC实现 :crit, a6b, after a6a, 4d
|
||||
/readyz深度(5依赖) :a6c, after a6b, 1d
|
||||
iam_student_guardians+DataScope :a6d, after a6b, 1d
|
||||
section P2.1 核心(阻塞批次2)
|
||||
1.1 proto 契约确认(依赖coord补全iam.proto) :crit, a1, 2026-07-10, 1d
|
||||
1.2 gRPC server 50052 + AuthMiddleware注册 :crit, a2, after a1, 2d
|
||||
1.3 8 RPC实现(GetViewports/GetEffectivePermissions/GetEffectiveAccess/Logout/GetPublicKey/BatchGetUsers/GetEffectiveDataScope/GetChildrenByParent) :crit, a3, after a2, 4d
|
||||
1.4 JWT RS256本地文件+refresh轮换 :crit, a4, after a2, 2d
|
||||
1.5 /iam/v1/*前缀迁移+端点统一(I7/I8) :crit, a5, after a3, 1d
|
||||
1.6 iam_student_guardians表+GetChildrenByParent(I6) :crit, a6, after a3, 1d
|
||||
1.7 shared-ts Outbox接入+事件发布(I2) :crit, a7, after a3, 2d
|
||||
1.8 DB驱动PermissionGuard基础(I3) :crit, a8, after a3, 2d
|
||||
1.9 /readyz深度(5依赖:DB/Redis/Kafka/gRPC/JWKS) :a9, after a8, 1d
|
||||
1.10 02文档回写(I1-I8对齐) :crit, a10, 2026-07-10, 1d
|
||||
|
||||
section P2.2-P6 扩展
|
||||
审计日志+Outbox :a6e, after a6d, 3d
|
||||
持续补全 :a6f, after a6e, 5d
|
||||
section P2.2 扩展(P3期间持续补)
|
||||
2.1 三层角色模型(system/organization/temporary) :b1, after a8, 3d
|
||||
2.2 DataScope 6级实现(待ISSUE-004裁决) :b2, after a8, 2d
|
||||
2.3 视口4层+getEffectivePermissions完整 :b3, after b1, 3d
|
||||
2.4 审计日志(user_audit_log表+AuditEvent发布) :b4, after b1, 3d
|
||||
2.5 Redis缓存完整实现(I3完整) :b5, after b1, 2d
|
||||
2.6 密码策略(强度/过期/重用限制) :b6, after b4, 2d
|
||||
2.7 单元测试+集成测试(覆盖率≥80%) :b7, after b6, 3d
|
||||
|
||||
section P3-P6 持续优化
|
||||
3.1 RBAC CRUD完整化(角色/权限/视口增删改) :c1, after b3, 3d
|
||||
3.2 2FA实现(TOTP) :c2, after b6, 3d
|
||||
3.3 JWT密钥迁移Vault(P6) :c3, after c2, 2d
|
||||
3.4 /readyz硬化+性能优化 :c4, after c1, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai06 接管后必须自行细化为完整 P2-P6 排期。
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### P2.1:gRPC + 12 RPC + /readyz + DataScope
|
||||
### P2.1:gRPC + 8 RPC + RS256 + AuthMiddleware + Outbox(阻塞批次 2)
|
||||
|
||||
- **负责人**:ai06
|
||||
- **批次**:批次 1(P2)
|
||||
- **预估**:5-7 天
|
||||
- **前置依赖**:
|
||||
- coord 补全 iam.proto 至 12 RPC(ISSUE-005,阻塞项)
|
||||
- coord 补全 events.proto UserEvent/RoleEvent(ISSUE-002,阻塞 Outbox 事件发布)
|
||||
- shared-ts Outbox 工具包已就绪(✅ 已存在)
|
||||
- **交付物**:
|
||||
- gRPC server 50052 + 12 RPC(见 iam.proto)
|
||||
- /readyz 5 项依赖检查
|
||||
- iam_student_guardians 表 + DataScope=CHILDREN
|
||||
- **依赖**:iam.proto(批次 0 已完成)
|
||||
- **验收标准**:12 RPC 全部可用 + /readyz 返回 5 项状态
|
||||
- **完整 P2.2-P6 任务**:⚠️ 由 ai06 自行补充
|
||||
1. gRPC server 50052 启用(NestJS gRPC transport)
|
||||
2. 8 RPC 实现:GetViewports / GetEffectivePermissions / GetEffectiveAccess / Logout / GetPublicKey / BatchGetUsers / GetEffectiveDataScope / GetChildrenByParent
|
||||
3. AuthMiddleware 注册(app.module.ts configure 消费),Controller 改用 @Req() 注入用户上下文(I4)
|
||||
4. JWT RS256 本地文件加载(IAM_PRIVATE_KEY_PATH / IAM_PUBLIC_KEY_PATH)+ refresh token 轮换 + 旧 token 黑名单(I5)
|
||||
5. /iam/v1/* 前缀迁移(Controller `@Controller('v1/iam')`)+ 端点路径统一 I8(I7)
|
||||
6. iam_student_guardians 表 + GET /iam/v1/children REST + GetChildrenByParent gRPC(I6)
|
||||
7. shared-ts Outbox 接入,发布 UserEvent/RoleEvent(I2)
|
||||
8. DB 驱动 PermissionGuard 基础(废弃 permission.guard.ts 硬编码 ROLE_PERMISSIONS map,改调 IamService.getEffectivePermissions)(I3)
|
||||
9. /readyz 深度检查 5 项依赖(DB SELECT 1 / Redis PING / Kafka 连接 / gRPC 自身可达 / JWKS 可读)
|
||||
10. 01/02 文档回写(删除全部中间过渡方案,对齐 I1-I8 + §2.15/§2.16/§5.5)
|
||||
- **验收标准**:
|
||||
- gRPC 50052 HealthService.Check 返回 SERVING
|
||||
- 8 RPC 全部可调用并返回正确响应
|
||||
- GetPublicKey 返回 RS256 PEM 公钥(供 api-gateway 验签)
|
||||
- GetChildrenByParent 返回学生列表(供 parent-bff P4 消费)
|
||||
- /iam/v1/* 前缀生效,旧路径不保留
|
||||
- AuthMiddleware 注入 req.user(userId/roles/dataScope)
|
||||
- /readyz 返回 5 项依赖状态
|
||||
- **就绪信号**:gRPC 50052 启用 + GetPublicKey RPC 可用 + HealthService.Check 返回 SERVING
|
||||
|
||||
### P2.2:三层角色 + DataScope + 视口 + 审计 + 缓存(P3 期间持续补,不阻塞)
|
||||
|
||||
- **负责人**:ai06
|
||||
- **批次**:批次 2-3 期间(P3 进行中持续补)
|
||||
- **预估**:12-15 天
|
||||
- **前置依赖**:P2.1 完成 + ISSUE-004(DataScope 枚举裁决)
|
||||
- **交付物**:
|
||||
1. 三层角色模型(system / organization / temporary,iam_roles 表扩展 role_type + level 字段)
|
||||
2. DataScope 6 级实现(ALL / SCHOOL / GRADE / CLASS / SUBJECT|DISTRICT / SELF,待 ISSUE-004 裁决)
|
||||
3. 视口 4 层(admin / teacher / student / parent)+ getEffectivePermissions 完整聚合
|
||||
4. 审计日志(user_audit_log 表 + AuditEvent 发布到 edu.iam.audit.created topic,president §5.5)
|
||||
5. Redis 缓存完整实现(getEffectivePermissions TTL 5min + 角色变更主动 DEL + getUserViewports 缓存)
|
||||
6. 密码策略(强度校验 / 过期提醒 / 重用限制)
|
||||
7. 单元测试 + 集成测试(覆盖率 ≥ 80%,对齐 classes 黄金模板)
|
||||
- **验收标准**:
|
||||
- 三层角色可创建/分配/查询
|
||||
- DataScope 在 Repository 层动态注入 WHERE 条件
|
||||
- 审计事件可发布到 Kafka
|
||||
- Redis 缓存命中率可观测(iam_permission_cache_hits_total 指标)
|
||||
- 测试覆盖率 ≥ 80%
|
||||
|
||||
### P3-P6:RBAC CRUD + 2FA + Vault 迁移 + 硬化
|
||||
|
||||
- **负责人**:ai06
|
||||
- **批次**:批次 4-5(P5-P6 期间)
|
||||
- **预估**:8-10 天
|
||||
- **交付物**:
|
||||
1. RBAC CRUD 完整化(角色/权限/视口增删改,系统角色禁止删除)
|
||||
2. 2FA 实现(TOTP,pending-features §P2 提及)
|
||||
3. JWT 密钥迁移 Vault(P6,president X8)
|
||||
4. /readyz 硬化 + 性能优化(连接池调优、缓存策略优化)
|
||||
- **验收标准**:
|
||||
- RBAC CRUD 全部端点可用 + 权限装饰器覆盖
|
||||
- 2FA 可启用/验证/禁用
|
||||
- Vault 密钥轮换不中断服务
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:无(iam 是基础服务)
|
||||
- **我的就绪信号**:gRPC 50052 启用 + GetPublicKey RPC 可用 + HealthService.Check 返回 SERVING
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 依赖项 | 提供方 | 就绪标志 | 状态 |
|
||||
| ------ | ------ | -------- | ---- |
|
||||
| iam.proto 补全至 12 RPC | coord | proto 文件含 12 RPC + 全部 message | ❌ 仅 4 RPC(ISSUE-005) |
|
||||
| events.proto 补全 UserEvent/RoleEvent/AuditEvent | coord | proto 文件含 3 个 message | ❌ 缺失(ISSUE-002) |
|
||||
| shared-ts Outbox 工具包 | coord | outbox.service.ts + outbox.module.ts 可导入 | ✅ 已就绪 |
|
||||
| shared-ts Redis 工具包 | coord | redis client 单例可导入 | ⏳ 待确认 |
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
- [ ] iam gRPC 50052 启用(HealthService.Check 返回 SERVING)
|
||||
- [ ] IamService 12 RPC 全部可调用(Register/Login/RefreshToken/Logout/GetUserInfo/BatchGetUsers/GetEffectivePermissions/GetEffectiveAccess/GetEffectiveDataScope/GetViewports/GetPublicKey/GetChildrenByParent)
|
||||
- [ ] IamService.GetPublicKey 可用(返回 RS256 PEM 公钥,供 api-gateway 验签)
|
||||
- [ ] IamService.GetChildrenByParent 可用(供 parent-bff 查孩子列表)
|
||||
- [ ] edu.iam.user.events / edu.iam.role.events / edu.iam.audit.created topic 可发布
|
||||
- [ ] JWT RS256 签发链路打通(access_token 15min + refresh_token 7day 轮换)
|
||||
- [ ] /iam/v1/* REST 端点可用(供 gateway 透传 + admin-portal 直连)
|
||||
|
||||
@@ -1,45 +1,316 @@
|
||||
# msg 工作排期
|
||||
|
||||
> 负责人:ai10
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/msg_contract.md](../contracts/msg_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/msg_contract.md](../contracts/msg_contract.md)、[02-architecture-design.md §10](../../../services/msg/docs/02-architecture-design.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
msg 是消息服务,提供 NotificationService、PreferenceService、TemplateService,并基于 Outbox 模式发布消息事件。全阶段目标:P2 服务骨架+Outbox → P3 三大 Service 实现 → P4-P6 持续优化。
|
||||
msg 是消息通知中台(P5),提供 NotificationService + NotificationPreferenceService + NotificationTemplateService 三服务,基于 Outbox 模式发布通知事件,消费 iam/core-edu/data-ana 共 12 类事件触发多渠道通知。
|
||||
|
||||
**全阶段目标**:
|
||||
- P2-P3:服务骨架补全(schema 迁移 + Outbox + Kafka 基础设施 + mock 消费)
|
||||
- P4:三大 Service 主体实现(Notification + Preference + Template)+ ChannelDispatcher 多渠道
|
||||
- P5:gRPC 50056 启用 + PushGatewayClient gRPC + 12 类事件 consumer + ES mapping
|
||||
- P6:测试覆盖 ≥ 80% + /readyz 硬化 + 黄金模板对齐 + README 修正
|
||||
|
||||
**批次归属**:批次 4(P5),依赖批次 3 content(ai09)就绪后启动,预估 13 天。
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai10 msg 全阶段排期
|
||||
title ai10 msg 全阶段排期(13 天)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a10a, 2026-07-10, Xd
|
||||
```
|
||||
section P2-P3 骨架补全
|
||||
T1-T2 schema迁移(notifications+preferences字段) :crit, a1, 2026-07-10, 1d
|
||||
T3 新建msg_notification_templates表 :a2, after a1, 1d
|
||||
T4 新建msg_outbox_events+Publisher worker :crit, a3, after a1, 2d
|
||||
T5 新建shared/kafka(producer+consumer骨架) :crit, a4, after a3, 1d
|
||||
T6 引入ioredis+IdempotencyGuard(SETNX) :a5, after a3, 1d
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai10 接管后必须自行细化为完整 P2-P6 排期。
|
||||
section P4 主体实现
|
||||
T7 ChannelDispatcher多渠道抽象 :crit, a6, after a4, 2d
|
||||
T10 重构notifications.service(移除同步fetch) :a7, after a6, 1d
|
||||
T11 batchMarkAsRead/markAllAsRead/recall/getUnreadCount :a8, after a7, 1d
|
||||
T12 NotificationPreference CRUD :a9, after a7, 1d
|
||||
T13 NotificationTemplate CRUD+render :a10, after a7, 1d
|
||||
T15 createBatch改批量INSERT :a11, after a7, 1d
|
||||
|
||||
section P5 gRPC+事件+ES
|
||||
T8 PushGatewayClient gRPC(替代fetch降级) :crit, a12, after a8, 1d
|
||||
T9 12类Kafka事件consumer(iam/core-edu/data-ana) :crit, a13, after a12, 2d
|
||||
T14 ES索引mapping+ensureIndex+同步 :a14, after a12, 1d
|
||||
gRPC 50056启用+3 Service 13 RPC :crit, a15, after a13, 1d
|
||||
|
||||
section P6 硬化与对齐
|
||||
T16 NotificationsModule补exports :a16, after a15, 1d
|
||||
T17 /readyz多依赖(DB/ES/Redis/Kafka/PushGW) :a17, after a15, 1d
|
||||
T19 统一关闭到LifecycleService :a18, after a15, 1d
|
||||
T20-T21 DB改getDb()+ID改cuid2 :a19, after a15, 1d
|
||||
T22 单元测试覆盖≥80% :crit, a20, after a19, 2d
|
||||
T23 修正README与实现对齐 :a21, after a20, 1d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### P2-P3 骨架补全
|
||||
|
||||
#### T1-T2:schema 迁移(notifications + preferences 字段扩展)
|
||||
|
||||
- **负责人**:ai10
|
||||
- **交付物**:⚠️ 由 ai10 自行补充
|
||||
- **依赖**:见 [contracts/msg_contract.md](../contracts/msg_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai10 自行补充
|
||||
- **依赖**:无(msg 独占 DB)
|
||||
- **交付物**:
|
||||
- `msg_notifications` 表新增 status / metadata / related_entity_type / related_entity_id / group_id / sender_id / template_id / event_id / updated_at 字段 + 6 个索引
|
||||
- `msg_notification_preferences` 表补齐 created_at / updated_at + 新增 frequency_limit / quiet_hours_start / quiet_hours_end / quiet_hours_timezone 字段
|
||||
- **验收标准**:Drizzle schema 定义更新,迁移脚本可执行,索引符合 02-architecture-design.md §3.1.1 / §3.1.2
|
||||
|
||||
#### T3:新建 msg_notification_templates 表
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T1-T2
|
||||
- **交付物**:`msg_notification_templates` 表 schema(code + type + title_template + content_template + default_channels + variables + locale + status),UNIQUE INDEX `(code, locale)`
|
||||
- **验收标准**:schema 定义 + 迁移脚本,符合 02-architecture-design.md §3.1.3
|
||||
|
||||
#### T4:新建 msg_outbox_events 表 + Outbox Publisher worker
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T1-T2
|
||||
- **交付物**:
|
||||
- `msg_outbox_events` 表 schema(event_id PK + aggregate_type + aggregate_id + event_type + topic + payload + status + retry_count + created_at + published_at + next_retry_at)
|
||||
- `shared/outbox/outbox.publisher.ts`(独立 worker,每 1s 轮询 PENDING 事件投递 Kafka)
|
||||
- `shared/outbox/outbox.schema.ts`(Drizzle schema)
|
||||
- **验收标准**:Outbox Publisher 可轮询 + 投递 + 更新 status=SENT,符合 02-architecture-design.md §3.1.4 / §5.4
|
||||
- **关联 ISSUE**:ISSUE-003(Outbox 强制)
|
||||
|
||||
#### T5:新建 shared/kafka/(producer + consumer 骨架)
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T4
|
||||
- **交付物**:
|
||||
- `shared/kafka/kafka.producer.ts`(idempotent=true + transactionalId=msg-producer)
|
||||
- `shared/kafka/kafka.consumer.ts`(consumer group = msg-service)
|
||||
- `shared/kafka/topic-map.ts`(PRODUCER_TOPIC_MAP + CONSUMER_TOPICS)
|
||||
- **验收标准**:producer 可投递消息,consumer 可订阅 topic,符合 02-architecture-design.md §5.3
|
||||
|
||||
#### T6:引入 ioredis + IdempotencyGuard(SETNX)
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- `shared/redis/redis.client.ts`(ioredis 客户端,连接 REDIS_URL)
|
||||
- `shared/redis/idempotency.guard.ts`(SETNX `msg:processed:{event_id}` TTL 7 天)
|
||||
- `shared/redis/read-bitmap.ts`(已读位图 BITCOUNT / GETBIT)
|
||||
- env.ts 补 REDIS_URL 必填校验
|
||||
- **验收标准**:IdempotencyGuard SETNX 原子去重,Redis 不可用时降级到 DB 唯一索引
|
||||
- **关联 ISSUE**:ISSUE-012(三层幂等防线,补 msg_idempotency 表中间层)
|
||||
|
||||
### P4 主体实现
|
||||
|
||||
#### T7:ChannelDispatcher 多渠道抽象
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T5、T6
|
||||
- **交付物**:
|
||||
- `channels/notification-channel.interface.ts`(NotificationChannel 接口)
|
||||
- `channels/in-app.channel.ts`(站内信,写 MySQL)
|
||||
- `channels/email.channel.ts`(邮件,SMTP,异步队列)
|
||||
- `channels/sms.channel.ts`(短信,HTTP API)
|
||||
- `channels/wechat.channel.ts`(微信,HTTP API)
|
||||
- `channels/push.channel.ts`(推送,调 PushGatewayClient)
|
||||
- `channels/channel-dispatcher.ts`(Promise.allSettled 并行投递 + in_app 总是发送)
|
||||
- **验收标准**:新增渠道只需实现接口 + 注册,符合 02-architecture-design.md §12
|
||||
|
||||
#### T10:重构 notifications.service.ts
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T4、T5、T7
|
||||
- **交付物**:重构 notifications.service.ts,移除同步 fetch push-gateway,改为 ChannelDispatcher + Outbox 事务
|
||||
- **验收标准**:send 方法走 BEGIN TX → INSERT notifications + INSERT outbox → COMMIT → ChannelDispatcher.dispatch
|
||||
|
||||
#### T11:新增端点(batchMarkAsRead / markAllAsRead / recall / getUnreadCount)
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T10
|
||||
- **交付物**:controller + service 新增 4 个端点
|
||||
- **验收标准**:符合 02-architecture-design.md §4.1 REST API 表
|
||||
- **关联 ISSUE**:ISSUE-010(markAsRead 权限改 READ)
|
||||
|
||||
#### T12:NotificationPreference CRUD
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T1-T2
|
||||
- **交付物**:`preferences/` 目录(controller + service + repository + schema + dto)
|
||||
- **验收标准**:GET / PUT preferences 端点可用
|
||||
|
||||
#### T13:NotificationTemplate CRUD + render
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T3
|
||||
- **交付物**:`templates/` 目录(controller + service + repository + schema + dto),含 `{{variable}}` 占位符替换渲染
|
||||
- **验收标准**:CreateTemplate / GetTemplate / ListTemplates / RenderTemplate 可用
|
||||
|
||||
#### T15:createBatch 改批量 INSERT
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T10
|
||||
- **交付物**:createBatch 改为 `db.insert(notifications).values([...])` 批量 INSERT
|
||||
- **验收标准**:1 万条广播通知 < 5s(性能验收)
|
||||
|
||||
### P5 gRPC + 事件 + ES
|
||||
|
||||
#### T8:PushGatewayClient gRPC
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:D5(push-gateway 提供 gRPC PushService.Push)
|
||||
- **交付物**:`shared/push/push-gateway.client.ts`(gRPC 调用,替代 fetch POST /internal/push 降级)
|
||||
- **验收标准**:gRPC 调用 push-gateway PushService.Push,降级模式保留(push-gateway 不可用时走 in_app)
|
||||
- **关联 ISSUE**:ISSUE-005(调用方向澄清)
|
||||
|
||||
#### T9:12 类 Kafka 事件 consumer
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T5、T6、D1-D2(events.proto 补齐 message + 字段)
|
||||
- **交付物**:
|
||||
- `shared/kafka/consumers/iam.consumer.ts`(6 类 user/role 事件)
|
||||
- `shared/kafka/consumers/core-edu.consumer.ts`(5 类 exam/homework/grade/attendance 事件)
|
||||
- `shared/kafka/consumers/data-ana.consumer.ts`(1 类 mastery 事件)
|
||||
- 每个 consumer 走 IdempotencyGuard → NotificationService.createNotificationFromEvent
|
||||
- **验收标准**:12 类事件均可消费 + 幂等去重 + fan-out 通知
|
||||
- **关联 ISSUE**:ISSUE-013(events.proto 缺 4 类 message,阻塞)
|
||||
|
||||
#### T14:ES 索引 mapping + ensureIndex + 同步
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- `config/elasticsearch.ts` 补 `notifications` 索引 mapping(ik_max_word 分词)
|
||||
- ensureIndex 幂等创建
|
||||
- 数据同步:Outbox 事件触发 ES 索引更新(替代当前同步 safeIndex)
|
||||
- **验收标准**:ES 检索可用,mapping 符合 02-architecture-design.md §3.2.1
|
||||
- **关联 ISSUE**:ISSUE-011(降级方向待仲裁)
|
||||
|
||||
#### gRPC 50056 启用 + 3 Service 13 RPC
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:D3-D4(msg.proto 补 RPC + Service)
|
||||
- **交付物**:
|
||||
- `notifications.grpc.controller.ts`(NotificationService gRPC)
|
||||
- `preferences.grpc.controller.ts`(NotificationPreferenceService gRPC)
|
||||
- `templates.grpc.controller.ts`(NotificationTemplateService gRPC)
|
||||
- app.module.ts 注册 gRPC server :50056
|
||||
- **验收标准**:HealthService.Check 返回 SERVING,13 RPC 可调用
|
||||
- **关联 ISSUE**:ISSUE-009(RPC 数量待仲裁 13 vs 17)
|
||||
|
||||
### P6 硬化与对齐
|
||||
|
||||
#### T16:NotificationsModule 补 exports
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:无
|
||||
- **交付物**:notifications.module.ts 补 `exports: [NotificationsService]`
|
||||
- **验收标准**:BFF 可注入 NotificationsService
|
||||
|
||||
#### T17:/readyz 多依赖检查
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:T4、T5、T6、T8
|
||||
- **交付物**:health.controller.ts /readyz 检查 DB / ES / Redis / Kafka producer / Kafka consumer / PushGateway 6 项依赖
|
||||
- **验收标准**:符合 02-architecture-design.md §6.6 判定规则(DB down → down;Redis/ES/Kafka down → degraded)
|
||||
|
||||
#### T19:统一关闭到 LifecycleService
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:无
|
||||
- **交付物**:移除 main.ts 重复 closeDb/closeEs,统一到 LifecycleService,8 步关闭序列
|
||||
- **验收标准**:符合 02-architecture-design.md §6.7 优雅关闭顺序
|
||||
|
||||
#### T20-T21:DB 改 getDb() + ID 改 cuid2
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:无
|
||||
- **交付物**:database.ts 改 getDb() 函数式;service 层 randomUUID → cuid2
|
||||
- **验收标准**:与 classes 黄金模板对齐
|
||||
|
||||
#### T22:单元测试覆盖 ≥ 80%
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:全部 P4-P5 任务
|
||||
- **交付物**:`*.spec.ts`(Service / Repository / ChannelDispatcher / IdempotencyGuard / OutboxPublisher)
|
||||
- **验收标准**:覆盖率 ≥ 80%
|
||||
|
||||
#### T23:修正 README
|
||||
|
||||
- **负责人**:ai10
|
||||
- **依赖**:全部任务
|
||||
- **交付物**:README.md API 表与实现对齐(PUT /:id/read、补 batch/user/:userId/page 端点、补 env 变量表)
|
||||
- **验收标准**:无文档脱节
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai10 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai10 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
- [ ] **D1**:events.proto 补 UserEvent / RoleEvent / NotificationEvent / MasteryEvent message(coord 维护)— 🔴 阻塞 T9
|
||||
- [ ] **D2**:events.proto GradeEvent 补 class_id;全部事件补 student_ids[](coord 维护)— 🔴 阻塞 T9 fan-out
|
||||
- [ ] **D3**:msg.proto 补 BatchSendNotification / GetUnreadCount / BatchMarkAsRead / MarkAllAsRead / RecallNotification RPC(coord 维护)— 🔴 阻塞 gRPC(依赖 ISSUE-009 仲裁)
|
||||
- [ ] **D4**:msg.proto 补 NotificationPreferenceService + NotificationTemplateService(coord 维护)— 🔴 阻塞 gRPC
|
||||
- [ ] **D5**:push-gateway 提供 gRPC PushService.Push 方法(ai02)— 🔴 阻塞 T8
|
||||
- [ ] **D6**:iam 发布 6 类 user/role 事件(ai06)— 🟡 不阻塞开发(用 mock),阻塞集成验证
|
||||
- [ ] **D7**:core-edu 发布 5 类教学事件(ai08)— 🟡 同上
|
||||
- [ ] **D8**:data-ana 发布 mastery 事件(ai11)— 🟡 同上
|
||||
- [ ] **D9**:Redis 集群可用(infra 部署)— 🟡 降级到 DB 唯一索引
|
||||
|
||||
### 4.2 我的就绪标志(供下游消费)
|
||||
|
||||
- [ ] msg gRPC 50056 启用(HealthService.Check 返回 SERVING)
|
||||
- [ ] NotificationService RPC 可调用(数量待 ISSUE-009 仲裁)
|
||||
- [ ] NotificationPreferenceService RPC 可调用
|
||||
- [ ] NotificationTemplateService RPC 可调用(含 RenderTemplate)
|
||||
- [ ] `edu.notification.*` topic 可发布(供 push-gateway / data-ana 消费,命名待 ISSUE-008 仲裁)
|
||||
- [ ] /readyz 返回 6 项依赖状态
|
||||
- [ ] 测试覆盖率 ≥ 80%
|
||||
|
||||
---
|
||||
|
||||
## §5 Mock 策略
|
||||
|
||||
### 5.1 我提供的 mock(供下游)
|
||||
|
||||
在 msg 真实服务就绪前,为下游(teacher-bff / student-bff / parent-bff / push-gateway)提供 mock:
|
||||
|
||||
- **gRPC mock**:grpc-mock 拦截 50056 端口
|
||||
- ListNotifications 返回固定 10 条未读通知
|
||||
- MarkAsRead 返回 success=true
|
||||
- GetPreference 返回默认偏好(in_app + email 开启,sms + push 关闭)
|
||||
- RenderTemplate 返回固定 title + content
|
||||
- **Kafka mock**:msg 就绪前不发布真实通知事件,push-gateway 使用本地 stub 推送
|
||||
|
||||
### 5.2 我消费的 mock(开发期间)
|
||||
|
||||
在真实上游就绪前,msg 使用以下 mock:
|
||||
|
||||
- **业务事件**:core-edu / data-ana 就绪前,msg 内置定时器发布本地 stub 事件(ExamEvent / HomeworkEvent),触发 mock 通知流程验证 consumer 链路
|
||||
- **用户偏好**:iam 就绪前使用默认偏好(所有用户 in_app 开启)
|
||||
- **模板渲染**:内置 5 个常用模板(exam.published / homework.graded / grade.recorded / mastery.warning / system.notice)
|
||||
- **Push Gateway**:ai02 就绪前用 fetch POST /internal/push 降级(当前实现保留)
|
||||
|
||||
---
|
||||
|
||||
## §6 风险与缓解
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| ---- | ---- | ---- |
|
||||
| events.proto 补齐延迟(D1-D2) | T9 consumer 无法验证 | 开发期用 stub 事件,proto 补齐后切换 |
|
||||
| RPC 数量仲裁延迟(ISSUE-009) | gRPC controller 实现范围不确定 | 先实现 13 RPC 基线,仲裁后增减 |
|
||||
| topic 命名仲裁延迟(ISSUE-008) | Kafka producer/consumer topic 不确定 | 开发期用 02-architecture-design.md §5.3 的 TOPIC_MAP,仲裁后统一 |
|
||||
| push-gateway gRPC 延迟(D5) | T8 无法验证 | 保留 fetch POST 降级,gRPC 就绪后切换 |
|
||||
|
||||
@@ -1,45 +1,333 @@
|
||||
# parent-bff 工作排期
|
||||
|
||||
> 负责人:ai05
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/parent-bff_contract.md](../contracts/parent-bff_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/parent-bff_contract.md](../contracts/parent-bff_contract.md)、[02-architecture-design.md](../../../services/parent-bff/docs/02-architecture-design.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
parent-bff 为家长端提供 GraphQL 聚合 API,覆盖 Dashboard、多子女切换、成绩趋势等场景。全阶段目标:P2 GraphQL schema 骨架 → P3 Dashboard+多子女+成绩趋势 → P4-P6 持续优化。
|
||||
parent-bff 为家长端提供 GraphQL 聚合 API(端口 3010),覆盖 Dashboard、多子女切换、成绩趋势、学情诊断、通知偏好等场景。
|
||||
|
||||
**全阶段目标**:
|
||||
|
||||
| 阶段 | 交付核心 | 依赖 |
|
||||
| --- | --- | --- |
|
||||
| P4 MVP | GraphQL Yoga + DataLoader + 多子女切换 + ChildGuard + 并行 gRPC 聚合 + Redis 缓存 + /readyz 下游探针 | iam.GetChildrenByParent(I6 裁决)+ core-edu gRPC + data-ana gRPC |
|
||||
| P5 通知接入 | msg gRPC + push-gateway HTTP + Kafka consumer 缓存失效 + 通知偏好过滤 | msg gRPC 50056 + push-gateway /internal/push |
|
||||
| P6 硬化 | 熔断器 opossum + HPA + SLO 监控 + 灰度发布 | — |
|
||||
|
||||
**关键路径**:批次 0(coord 补 proto)→ 批次 1(iam gRPC + GetChildrenByParent)→ 批次 2(core-edu gRPC)→ **批次 3(parent-bff P4 MVP)** → 批次 4(msg P5)→ parent-bff P5 接入
|
||||
|
||||
**P0 阻塞项**(详见 [objections/parent-bff_issue.md](../objections/parent-bff_issue.md) ISSUE-008/007):
|
||||
|
||||
- iam.GetChildrenByParent RPC + iam_student_guardians 表(I6 裁决,ai06 负责)
|
||||
- core-edu ClassService.GetClass proto 缺失(ISSUE-008,待 coord 仲裁归属)
|
||||
- msg.proto Notification 缺 child_id 字段(ISSUE-007,P5 阶段阻塞)
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai05 parent-bff 全阶段排期
|
||||
title ai05 parent-bff 全阶段排期(全并行 + mock)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a5a, 2026-07-10, Xd
|
||||
section P4 MVP 核心
|
||||
P4.1 骨架搭建(env+main+health) :crit, a5a, 2026-07-31, 2d
|
||||
P4.2 GraphQL Yoga+schema第一版 :crit, a5b, after a5a, 3d
|
||||
P4.3 DownstreamClient抽象+gRPC mock :crit, a5c, after a5a, 2d
|
||||
P4.4 ChildGuard+DataLoader实现 :crit, a5d, after a5b, 3d
|
||||
P4.5 Dashboard/children/grades Resolver :a5e, after a5d, 2d
|
||||
P4.6 Redis缓存层+ Orchestrator降级 :a5f, after a5e, 2d
|
||||
P4.7 /readyz下游探针+可观测三支柱 :a5g, after a5f, 1d
|
||||
P4.8 单元+集成测试(≥80%覆盖) :a5h, after a5g, 2d
|
||||
|
||||
section P5 通知接入
|
||||
P5.1 msg gRPC client接入 :b5a, after a5h, 2d
|
||||
P5.2 Notification Resolver+偏好配置 :b5b, after b5a, 2d
|
||||
P5.3 push-gateway HTTP /internal/push :b5c, after b5a, 1d
|
||||
P5.4 Kafka consumer订阅+缓存失效 :b5d, after b5c, 3d
|
||||
P5.5 通知偏好过滤逻辑 :b5e, after b5d, 1d
|
||||
|
||||
section P6 硬化
|
||||
P6.1 opossum熔断器per-service :c5a, after b5e, 2d
|
||||
P6.2 HPA+podAntiAffinity :c5b, after c5a, 1d
|
||||
P6.3 SLO告警规则+灰度发布 :c5c, after c5b, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai05 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> **说明**:以上日期为 coord 总排期推算(批次 3 P4 在批次 2 P3 完成后启动)。全并行模式下,P4/P5/P6 代码一口气完成,上游未就绪时用 mock,最后统一集成测试。
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### P4.1:骨架搭建(env + main + health)
|
||||
|
||||
- **负责人**:ai05
|
||||
- **交付物**:⚠️ 由 ai05 自行补充
|
||||
- **依赖**:见 [contracts/parent-bff_contract.md](../contracts/parent-bff_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai05 自行补充
|
||||
- **依赖**:无(克隆 teacher-bff 骨架)
|
||||
- **交付物**:
|
||||
- `services/parent-bff/package.json`(name=@edu/parent-bff)
|
||||
- `src/config/env.ts`(Zod 校验,见 02 §12.1 完整配置项)
|
||||
- `src/main.ts`(启动 + /metrics + SIGTERM 优雅关闭)
|
||||
- `src/app.module.ts`
|
||||
- `src/shared/health/health.controller.ts`(/healthz 直接 ok)
|
||||
- `src/shared/observability/{logger,metrics,tracer}.ts`(service=parent-bff)
|
||||
- `src/shared/errors/{application-error,global-error.filter}.ts`(BFF_PARENT_ 前缀)
|
||||
- `Dockerfile`(多阶段,EXPOSE 3010)
|
||||
- `tsconfig.json`(NodeNext + ESM .js 后缀)
|
||||
- **验收标准**:`pnpm run typecheck` + `pnpm run lint` 零错误;`docker build` 通过;本地启动 /healthz 返回 200
|
||||
|
||||
### P4.2:GraphQL Yoga + schema 第一版
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.1
|
||||
- **交付物**:
|
||||
- `src/entry/graphql.controller.ts`(POST /graphql + GET /graphql playground 仅 dev)
|
||||
- `src/entry/context.middleware.ts`(解析 x-user-id/x-user-roles/x-request-id 注入 GraphQL context)
|
||||
- `src/graphql/schema.ts`(typeDefs + resolvers,见 02 §4.2 GraphQL schema)
|
||||
- `src/graphql/types/`(parent/child/grade/homework/exam/analytics/notification.type.ts)
|
||||
- GraphQL 复杂度限制(depth ≤ 7,cost ≤ 1000,02 §9 #5)
|
||||
- `packages/shared-ts/contracts/graphql/parent-bff.graphql`(SDL-first 集中管理,对齐 coord ARB-001 模式)
|
||||
- **验收标准**:POST /graphql 可内省 schema;深度超 7 的查询被拒;cost 超 1000 被拒
|
||||
|
||||
### P4.3:DownstreamClient 抽象 + gRPC mock
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.1 + ai03 DownstreamClient 抽象模式(teacher-bff 参考)
|
||||
- **交付物**:
|
||||
- `src/clients/grpc/grpc.factory.ts`(gRPC client 创建 + interceptor:trace/metrics/retry)
|
||||
- `src/clients/iam.client.ts`(IamClient interface + gRPC impl + mock impl)
|
||||
- `src/clients/core-edu.client.ts`(CoreEduClient interface + gRPC impl + mock impl)
|
||||
- `src/clients/data-ana.client.ts`(DataAnaClient interface + gRPC impl + mock impl)
|
||||
- mock 数据:固定 2 个孩子(student-001 李同学 + student-002 李妹妹)+ 固定成绩/作业/考试/学情
|
||||
- **验收标准**:DEV_MODE=true 时走 mock impl,返回固定数据;mock 数据 student_id 与 core-edu mock 一致(见 contract.md §4.2)
|
||||
|
||||
### P4.4:ChildGuard + DataLoader 实现
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.2 + P4.3
|
||||
- **交付物**:
|
||||
- `src/aggregation/child-guard.ts`(DataScope=CHILDREN 越权校验,见 02 §2.3)
|
||||
- 30s TTL Redis 缓存绑定列表(ISSUE-002 修正:§3.1.1 同步为 30s)
|
||||
- singleflight 模式防缓存击穿(02 §14 #5)
|
||||
- 越权时抛 BFF_PARENT_CHILD_NOT_BOUND(403)
|
||||
- `src/dataloader/dataloader.module.ts`(per-request 实例注册器)
|
||||
- `src/dataloader/{children,grade,homework,exam}.dataloader.ts`(批量去重 N+1 防御)
|
||||
- **验收标准**:
|
||||
- childId ∉ 绑定列表时抛 403
|
||||
- 30s 内第二次查询不调 iam.GetChildrenByParent
|
||||
- 并发 100 请求只调 iam 1 次(singleflight)
|
||||
- DataLoader 同 parentId 多次调用合并为 1 次 gRPC
|
||||
|
||||
### P4.5:Dashboard / children / grades Resolver
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.4
|
||||
- **交付物**:
|
||||
- `src/graphql/resolvers/dashboard.resolver.ts`(聚合 iam.GetUserInfo + iam.GetChildrenByParent + core-edu.ListGradesByStudent 并行)
|
||||
- `src/graphql/resolvers/child.resolver.ts`(childQuery + ChildGuard 校验 + 延迟加载 grades/homework/exams/analytics)
|
||||
- `src/graphql/resolvers/select-child.resolver.ts`(mutation,仅审计日志,不持久化)
|
||||
- `src/graphql/resolvers/grade.resolver.ts`(childGrades query,含分页)
|
||||
- **验收标准**:
|
||||
- dashboard Query 返回 parent + children + unreadNotifications
|
||||
- child(childId) 对未绑定 childId 返回 403
|
||||
- selectChild mutation 记录审计日志(traceId + parentId + childId + timestamp)
|
||||
|
||||
### P4.6:Redis 缓存层 + Orchestrator 降级
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.5
|
||||
- **交付物**:
|
||||
- `src/shared/cache/redis.client.ts`(ioredis 连接)
|
||||
- `src/shared/cache/cache-key.builder.ts`(bff:parent:* 前缀,见 02 §3.1.1)
|
||||
- `src/aggregation/orchestrator.ts`(Promise.allSettled 并行 + 降级标记 partial)
|
||||
- `src/aggregation/response-mapper.ts`(proto → GraphQL type,含 Grade.score string→Float 转换,ISSUE-008)
|
||||
- `src/aggregation/fallback-strategy.ts`(下游失败时返回缓存陈旧数据或 null 字段)
|
||||
- **验收标准**:
|
||||
- dashboard 聚合结果缓存 15s,第二次命中不调下游
|
||||
- data-ana 失败时返回 dashboard.degraded=true,其他字段正常
|
||||
- Redis 不可用时降级为内存 LRU
|
||||
|
||||
### P4.7:/readyz 下游探针 + 可观测三支柱
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.6
|
||||
- **交付物**:
|
||||
- `src/shared/health/health.controller.ts` 补 /readyz 下游探针(02 §9 #7 ai05 调整)
|
||||
- 探针:iam gRPC + core-edu gRPC + data-ana gRPC + Redis,超时 1s/服务
|
||||
- 任一失败返回 503 + degraded=true
|
||||
- metrics 指标全量落地(02 §6.4 表格 11 项指标)
|
||||
- tracer auto-instrumentations(http/nestjs/express/ioredis/grpc-js)
|
||||
- logger 字段对齐(parentId/childId/operation/traceId)
|
||||
- **验收标准**:
|
||||
- /readyz 返回 4 项依赖状态
|
||||
- /metrics 暴露 parent_bff_* 指标
|
||||
- Jaeger 可看到 dashboard 请求完整 span 链
|
||||
|
||||
### P4.8:单元 + 集成测试(≥80% 覆盖)
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P4.7
|
||||
- **交付物**:
|
||||
- `test/unit/child-guard.test.ts`(越权拦截 + 缓存命中 + singleflight)
|
||||
- `test/unit/orchestrator.test.ts`(并行编排 + 部分失败降级)
|
||||
- `test/unit/dataloader.test.ts`(批量去重)
|
||||
- `test/unit/graphql-complexity.test.ts`(depth/cost 限制)
|
||||
- `test/integration/dashboard.test.ts`(3 子女 × 3 下游并行,Redis Testcontainers)
|
||||
- `test/integration/readyz.test.ts`(iam 故障时 503)
|
||||
- `vitest.config.ts`(覆盖率阈值 80%)
|
||||
- **验收标准**:覆盖率 ≥ 80%;10 项关键用例(02 §11.2)全部通过
|
||||
|
||||
### P5.1:msg gRPC client 接入
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:msg gRPC 50056 就绪(ai10)或 mock
|
||||
- **交付物**:
|
||||
- `src/clients/msg.client.ts`(MsgClient interface + gRPC impl + mock impl)
|
||||
- env.ts 启用 MsgServiceUrl / MsgGrpcTarget
|
||||
- **验收标准**:mock 模式下 NotificationService.ListNotifications / MarkAsRead 可调
|
||||
|
||||
### P5.2:Notification Resolver + 偏好配置
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P5.1 + msg.proto 补 child_id 字段(ISSUE-007 仲裁结果)
|
||||
- **交付物**:
|
||||
- `src/graphql/resolvers/notification.resolver.ts`(notifications query + markNotificationRead mutation)
|
||||
- `src/graphql/resolvers/notification-preference.resolver.ts`(notificationPreferences query + updateNotificationPreferences mutation)
|
||||
- `src/parent/dto/parent-inputs.dto.ts`(UpdateNotificationPreferencesSchema Zod 校验)
|
||||
- **验收标准**:notifications Query 返回家长通知列表(含 childId);偏好更新后缓存失效
|
||||
|
||||
### P5.3:push-gateway HTTP /internal/push 接入
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:push-gateway /internal/push 就绪(ai02)或 mock
|
||||
- **交付物**:
|
||||
- `src/clients/http/push-http.client.ts`(HTTP POST /internal/push,U2 仲裁)
|
||||
- env.ts 启用 PushGatewayUrl
|
||||
- **验收标准**:mock 模式下 pushViaHttp 返回 success
|
||||
|
||||
### P5.4:Kafka consumer 订阅 + 缓存失效
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:Kafka topic 已创建(C5 仲裁:edu.notification.sent/read/recalled/failed + edu.teaching.grade.recorded/homework.graded/exam.published)
|
||||
- **交付物**:
|
||||
- `src/shared/kafka/kafka.consumer.ts`(consumer group: parent-bff-event-subscriber)
|
||||
- `src/shared/kafka/handlers/notification-push.handler.ts`(偏好过滤 + push-gateway 推送)
|
||||
- `src/shared/kafka/handlers/cache-invalidation.handler.ts`(成绩/作业/考试事件失效对应缓存)
|
||||
- 幂等性:Redis SETNX event_id 去重
|
||||
- DLQ:edu.parent-bff.dlq
|
||||
- **验收标准**:
|
||||
- 收到 edu.teaching.grade.recorded 后 bff:parent:grades:{childId} 缓存失效
|
||||
- 家长关闭"成绩推送"偏好时,该家长不收到推送
|
||||
- 重复 event_id 不重复处理
|
||||
|
||||
### P5.5:通知偏好过滤逻辑
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P5.4
|
||||
- **交付物**:通知偏好过滤逻辑集成到 notification-push.handler(02 §5.4)
|
||||
- 拉取家长 NotificationPreferences(Redis 缓存 300s)
|
||||
- 按 eventTypeMap 映射事件类型 → 偏好开关
|
||||
- 取 prefs.channels 与 event.channels 交集
|
||||
- **验收标准**:偏好开关为 false 时不推送;channels 无交集时不推送
|
||||
|
||||
### P6.1:opossum 熔断器 per-service
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P5 完成
|
||||
- **交付物**:
|
||||
- `src/clients/grpc/circuit-breaker.ts`(opossum,per-downstream-service 独立 circuit:iam/core-edu/data-ana/msg)
|
||||
- 熔断开启时抛 BFF_PARENT_SERVICE_UNAVAILABLE(503)
|
||||
- metrics: parent_bff_circuit_state Gauge
|
||||
- **验收标准**:下游连续失败 5 次熔断开启;30s 后半开探测
|
||||
|
||||
### P6.2:HPA + podAntiAffinity
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P6.1
|
||||
- **交付物**:`infra/k8s/helm/parent-bff/` Chart(对齐 004 §1.2 端口)
|
||||
- HPA 2-10 副本(CPU 70% / 内存 80% 触发)
|
||||
- podAntiAffinity 跨节点分布
|
||||
- values-dev.yaml / values-staging.yaml / values-prod.yaml
|
||||
- **验收标准**:helm template 通过;HPA 可根据负载扩缩
|
||||
|
||||
### P6.3:SLO 告警规则 + 灰度发布
|
||||
|
||||
- **负责人**:ai05
|
||||
- **依赖**:P6.2
|
||||
- **交付物**:
|
||||
- `infra/prometheus/rules.yml` 追加 parent-bff 告警规则(P95 > 200ms / 错误率 > 0.1% / 可用性 < 99.9%)
|
||||
- `infra/grafana/dashboards/parent-bff.json` 面板
|
||||
- 灰度发布:按 parentId hash 路由流量百分比(K8s Service + Istio/Envoy weight)
|
||||
- **验收标准**:Prometheus 告警规则 lint 通过;Grafana 面板可展示 parent-bff 指标
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai05 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai05 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游 | 就绪信号 | 责任方 | 阻塞阶段 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| iam gRPC 50052 + GetChildrenByParent RPC | HealthService.Check = SERVING + GetChildrenByParent 可调 | ai06 | P4(P0 阻塞) | ⏳ |
|
||||
| iam_student_guardians 表 | 表已建 + Repository 查询方法可用 | ai06 | P4(P0 阻塞) | ⏳ |
|
||||
| core-edu gRPC 50053 | HealthService.Check = SERVING + Exam/Homework/Grade Service 可调 | ai08 | P4 | ⏳ |
|
||||
| core-edu ClassService.GetClass | proto 补全 + RPC 实现(ISSUE-008 待仲裁) | ai08 或 classes | P4 | ⏳ |
|
||||
| data-ana gRPC 50055 | HealthService.Check = SERVING + AnalyticsService 可调 | ai11 | P4 | ⏳ |
|
||||
| msg gRPC 50056 | HealthService.Check = SERVING + NotificationService 可调 | ai10 | P5 | ⏳ |
|
||||
| msg.proto Notification.child_id | proto 字段补全(ISSUE-007 待仲裁) | ai10 | P5 | ⏳ |
|
||||
| push-gateway /internal/push | HTTP 端点可用 | ai02 | P5 | ⏳ |
|
||||
| api-gateway /parent 路由 | `/api/v1/parent/*` → parent-bff:3010 代理生效 | ai01 | P4 | ⏳ |
|
||||
| Kafka topic 已创建 | edu.notification.sent/read/recalled/failed + edu.teaching.* | coord/infra | P5 | ⏳ |
|
||||
| Redis 已部署 | redis://edu-redis:6379 可达 | coord/infra | P4 | ⏳ |
|
||||
| buf.gen.yaml gRPC 插件 | TS gRPC client 代码生成可用 | coord | P4 | ⏳ |
|
||||
| ai03 DownstreamClient 抽象 | teacher-bff clients/ 抽象层可参考 | ai03 | P4 | ⏳ |
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
| 信号 | 检查方式 | 消费方 |
|
||||
| --- | --- | --- |
|
||||
| parent-bff GraphQL :3010 启用 | GET /healthz 返回 200 | api-gateway / K8s |
|
||||
| /readyz 返回 200(含 4 下游 gRPC 连通性) | GET /readyz 返回 200 | K8s readinessProbe |
|
||||
| GraphQL schema 可内省 | POST /graphql 返回 schema | parent-portal(ai15) |
|
||||
| 核心 Query 可执行 | dashboard / myChildren / childGrades / childAnalytics | parent-portal |
|
||||
| 核心 Mutation 可执行 | selectChild / markNotificationRead(P5) | parent-portal |
|
||||
| DataScope=CHILDREN 校验生效 | 家长查询未绑定 childId 返回 403 | 集成测试 |
|
||||
| metrics 暴露 | GET /metrics 返回 parent_bff_* 指标 | Prometheus |
|
||||
|
||||
### 4.3 全并行 Mock 策略
|
||||
|
||||
> 开发期间上游未就绪时,parent-bff 使用 mock 完成全部 P4-P6 代码,最后统一集成测试。
|
||||
|
||||
| 下游 | Mock 方式 | 切换真实时机 |
|
||||
| --- | --- | --- |
|
||||
| iam gRPC | grpc-mock 拦截 + 固定 UserInfo(parent 角色)+ 固定 2 个 ChildInfo | iam 就绪信号 ✅ |
|
||||
| core-edu gRPC | grpc-mock 拦截 + 固定成绩/作业/考试 | core-edu 就绪信号 ✅ |
|
||||
| data-ana gRPC | grpc-mock 拦截 + 固定学情/趋势 | data-ana 就绪信号 ✅ |
|
||||
| msg gRPC | grpc-mock 拦截 + 固定 10 条通知(含 childId) | msg 就绪信号 ✅ |
|
||||
| push-gateway HTTP | fetch mock + 返回 success | push-gateway 就绪信号 ✅ |
|
||||
| Redis | Testcontainers 真实 Redis 实例 | — |
|
||||
| Kafka | kafkajs mock + jest.mock | Kafka topic 创建 ✅ |
|
||||
|
||||
**关键**:iam.GetChildrenByParent 的 mock 必须返回与 core-edu mock 数据一致的 student_id,否则 ChildGuard 越权校验会失败。
|
||||
|
||||
---
|
||||
|
||||
## §5 跨模块协作需求(需 coord 协调)
|
||||
|
||||
| # | 需求 | 涉及 AI | 阻塞阶段 | 协调内容 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | iam 补 GetChildrenByParent RPC + iam_student_guardians 表 | ai06 | P4(P0) | I6 裁决已定,ai06 P2.1 即补 |
|
||||
| 2 | core-edu ClassService 归属仲裁 + proto 补全 | ai08 / classes | P4 | ISSUE-008 待 coord 仲裁 |
|
||||
| 3 | msg.proto Notification 补 child_id 字段 | ai10 | P5 | ISSUE-007 待 coord 仲裁 |
|
||||
| 4 | api-gateway 新增 /parent 路由 | ai01 | P4 | main.go + config.go 新增 ParentBffURL |
|
||||
| 5 | 004 §4 依赖图同步 C6 仲裁(补 DataAna + Msg) | coord | P4 | ISSUE-004 |
|
||||
| 6 | parent-portal 文档同步 GraphQL 决策 | ai15 | P4 | ISSUE-005 跨模块契约冲突 |
|
||||
| 7 | buf.gen.yaml 补 gRPC TS 插件 | coord | P4 | 02 §7.3 #7 |
|
||||
| 8 | docker-compose.deploy.yml 新增 parent-bff 服务 | coord | P4 | 端口 3010 + edu-net |
|
||||
| 9 | full-stack-runbook 端口矩阵追加 3010 | coord | P4 | 02 §7.3 #5 |
|
||||
| 10 | shared-ts/contracts/graphql/parent-bff.graphql 建库 | ai05 | P4 | SDL-first 集中管理 |
|
||||
|
||||
@@ -2,44 +2,331 @@
|
||||
|
||||
> 负责人:ai15
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/parent-portal_contract.md](../contracts/parent-portal_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
parent-portal 是家长端微前端,通过 MF Remote 接入主应用,覆盖 Dashboard、多子女切换等场景。全阶段目标:P2 MF Remote 骨架 → P3 Dashboard+多子女切换 → P4-P6 持续优化。
|
||||
parent-portal 是家长端微前端(MF Remote),挂载到 teacher-portal Shell,覆盖家长仪表盘、多子女切换、子女学情查看、通知偏好等场景。
|
||||
|
||||
- **MF 角色**:Remote(Shell = teacher-portal :4000)
|
||||
- **端口**:4002(dev/prod 一致,[port-allocation.md](../../../../infra/port-allocation.md) §4)
|
||||
- **阶段归属**:P4 启动(依赖 P4 的 parent-bff + data-ana 就绪)
|
||||
- **DataScope**:CHILDREN(仅查看自己绑定子女的数据)
|
||||
- **全阶段目标**:P4 MF Remote 骨架 + 核心页面 → P5 推送接入 + 通知中心 → P6 硬化(A11y/性能/安全/PWA/多语言)
|
||||
|
||||
> **前置阻塞**(见 [objections/parent-portal_issue.md](../objections/parent-portal_issue.md) ISSUE-010):
|
||||
> - iam `GetChildrenByParent` 接口缺失(P0 阻塞,ai06 补全),补全前用 mock(固定 2 个子女 student-001 + student-002)
|
||||
> - ISSUE-001 待 coord 仲裁(REST vs GraphQL),仲裁前按 GraphQL 预排期,mock 用 MSW 拦截 GraphQL
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P4-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai15 parent-portal 全阶段排期
|
||||
title ai15 parent-portal 全阶段排期(P4-P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a15a, 2026-07-10, Xd
|
||||
section P4 骨架与核心页面(11d)
|
||||
4.1 MF Remote 骨架+next.config.js+健康检查 :crit, a15a, 2026-07-29, 2d
|
||||
4.2 GraphQL client接入+MSW mock层 :crit, a15b, after a15a, 1d
|
||||
4.3 ChildSwitcher+useChildSwitcher+Zustand slice :crit, a15c, after a15b, 2d
|
||||
4.4 Dashboard页面+ParentDashboard组件 :a15d, after a15c, 2d
|
||||
4.5 子女成绩页面+ChildGradeChart :a15e, after a15c, 1d
|
||||
4.6 子女作业页面 :a15f, after a15e, 1d
|
||||
4.7 通知偏好页面+PreferenceForm+Zod :a15g, after a15d, 1d
|
||||
4.8 跨标签同步(BroadcastChannel) :a15h, after a15c, 1d
|
||||
|
||||
section P4 质量保障(并行)
|
||||
4.9 Vitest单测+MSW集成测试 :a15i, after a15g, 2d
|
||||
4.10 Dockerfile多阶段构建 :a15j, after a15a, 1d
|
||||
|
||||
section P5 推送与通知中心(5d)
|
||||
5.1 WebSocket接入+事件处理 :crit, a15k, after a15i, 2d
|
||||
5.2 通知中心页面+NotificationFeed :a15l, after a15k, 2d
|
||||
5.3 推送降级(HTTP轮询) :a15m, after a15l, 1d
|
||||
|
||||
section P6 硬化(8d)
|
||||
6.1 Web Vitals+OTel browser SDK :a15n, after a15m, 2d
|
||||
6.2 A11y WCAG 2.2 AA审计+修复 :a15o, after a15n, 2d
|
||||
6.3 性能优化+bundle分析 :a15p, after a15o, 1d
|
||||
6.4 多语言扩展(en-US) :a15q, after a15p, 1d
|
||||
6.5 PWA(Service Worker+manifest) :a15r, after a15q, 1d
|
||||
6.6 安全硬化(CSP+敏感数据脱敏) :a15s, after a15r, 1d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai15 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> **总工期**:P4 11d + P5 5d + P6 8d = 24d(约 5 周)
|
||||
> **关键路径**(红色 crit):MF 骨架 → GraphQL client → ChildSwitcher → 质量保障 → WebSocket → 通知中心
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### P4 阶段任务
|
||||
|
||||
#### P4-1:MF Remote 骨架 + next.config.js + 健康检查
|
||||
|
||||
- **负责人**:ai15
|
||||
- **交付物**:⚠️ 由 ai15 自行补充
|
||||
- **依赖**:见 [contracts/parent-portal_contract.md](../contracts/parent-portal_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai15 自行补充
|
||||
- **依赖**:teacher-portal Shell MF 配置就绪(ARB-002,ai13 P2 交付);ISSUE-002 仲裁(MF shared 清单)
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/next.config.js`(NextFederationPlugin,Remote 角色,`name: parent_app`,`exposes: ./pages + ./ChildSwitcher`,`shared` 含 ARB-002 全部 7 项)
|
||||
- `apps/parent-portal/src/app/layout.tsx`(RootLayout,复用 Shell 暴露的字体/令牌/i18n Provider)
|
||||
- `apps/parent-portal/src/app/api/health/route.ts`(`GET /api/health` → `{ status: 'ok', ts }`)
|
||||
- `apps/parent-portal/src/app/api/ready/route.ts`(`GET /api/ready` → 检查 API_GATEWAY_URL 可达)
|
||||
- `apps/parent-portal/tsconfig.json`(继承 tsconfig.base.json,strict)
|
||||
- `apps/parent-portal/package.json`(依赖对齐 Shell:react 18.3 + next 14 + urql + @tanstack/react-query v5 + zustand + nuqs)
|
||||
- **验收标准**:
|
||||
1. `pnpm --filter parent-portal dev` 启动 :4002
|
||||
2. `GET /api/health` 返回 200
|
||||
3. teacher-portal Shell 能加载 parent-portal remoteEntry.js(MF 拓扑验证)
|
||||
4. feature flag `NEXT_PUBLIC_MF_ENABLED` 可控制 MF 开关
|
||||
|
||||
#### P4-2:GraphQL client 接入 + MSW mock 层
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-1;ISSUE-001 仲裁(确认 GraphQL);teacher-portal Shell 暴露 GraphQLProvider(ARB-002)
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/lib/graphql-client.ts`(从 Shell 暴露的 `useGraphQLClient()` 获取 urql client 单例)
|
||||
- `apps/parent-portal/src/graphql/queries/`(currentUser / myChildren / childSummary / childGrades / childHomework / childTrend / childWeakness 查询文档)
|
||||
- `apps/parent-portal/src/graphql/mutations/`(markAsRead / updateNotificationPreferences mutation 文档)
|
||||
- `apps/parent-portal/src/mocks/handlers.ts`(MSW 拦截 `POST /api/v1/parent/graphql`,按 operationName 返回 mock)
|
||||
- `apps/parent-portal/src/mocks/fixtures/*.json`(固定 2 个子女 student-001 李同学 + student-002 李妹妹,与 parent-bff mock 一致)
|
||||
- `apps/parent-portal/src/mocks/browser.ts`(MSW worker 初始化,`NEXT_PUBLIC_API_MOCKING=enabled` 控制)
|
||||
- **验收标准**:
|
||||
1. MSW enabled 时,所有 GraphQL 查询返回 mock 数据
|
||||
2. myChildren mock 返回 2 个子女,id 与 childGrades/childHomework mock 的 student_id 一致
|
||||
3. urql client 单例跨组件共享(MF shared singleton 验证)
|
||||
|
||||
#### P4-3:ChildSwitcher + useChildSwitcher + Zustand slice
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-2;ISSUE-009 仲裁(switchChild 是 GraphQL Mutation 还是纯前端状态)
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/stores/childSwitcherSlice.ts`(Zustand slice:children / currentChildId / isLoading / error / switchChild / refreshChildren)
|
||||
- `apps/parent-portal/src/hooks/useChildSwitcher.ts`(封装 myChildren GraphQL query + 切换逻辑 + invalidate 子女维度查询)
|
||||
- `apps/parent-portal/src/components/ChildSwitcher.tsx`(variant: tab | dropdown,状态机:idle/switching/switched/error)
|
||||
- `apps/parent-portal/src/components/MultiChildTabBar.tsx`(≤3 子女用 Tab,>3 用下拉,移动端友好)
|
||||
- localStorage 持久化 `parent:currentChildId`(刷新恢复)
|
||||
- **验收标准**:
|
||||
1. 切换子女后,`['parent','grades',currentChildId]` 等子女维度查询 invalidate 重拉
|
||||
2. 刷新页面后 currentChildId 从 localStorage 恢复
|
||||
3. 0 子女显示 EmptyChildState;1 子女不显示 TabBar;2-3 子女显示 Tab
|
||||
4. 切换子女竞态:快速连续切换,旧请求 abort,新数据正确显示
|
||||
|
||||
#### P4-4:Dashboard 页面 + ParentDashboard 组件
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-3
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/app/(app)/parent/dashboard/page.tsx`
|
||||
- `apps/parent-portal/src/components/ParentDashboard.tsx`(组合 useChildren + useChildSummary,插槽:summary-cards / todo-reminders / recent-grades / attendance / custom)
|
||||
- `apps/parent-portal/src/components/ChildSummaryCard.tsx`(单子女概览:头像/姓名/年级/今日作业数/成绩趋势缩略图)
|
||||
- `apps/parent-portal/src/components/AttendanceCalendar.tsx`(出勤日历热力图)
|
||||
- **验收标准**:
|
||||
1. 多子女并列卡片展示
|
||||
2. 权限校验:`PARENT_DASHBOARD_VIEW`,用 `<RequirePermission>`
|
||||
3. SSR 首屏 + CSR 交互(依 02 §14.4 渲染策略)
|
||||
|
||||
#### P4-5:子女成绩页面 + ChildGradeChart
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-3
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/app/(app)/parent/grades/page.tsx`
|
||||
- `apps/parent-portal/src/components/ChildGradeChart.tsx`(recharts 折线 + 班级均分对比 + 多子女对比模式)
|
||||
- **验收标准**:
|
||||
1. 权限校验:`GRADES_READ_CHILD`
|
||||
2. 切换子女后图表刷新
|
||||
3. 多子女对比模式可选
|
||||
|
||||
#### P4-6:子女作业页面
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-3
|
||||
- **交付物**:`apps/parent-portal/src/app/(app)/parent/homework/page.tsx`
|
||||
- **验收标准**:
|
||||
1. 权限校验:`HOMEWORK_READ_CHILD`
|
||||
2. 复用 Shell 暴露的 DataTable 展示作业列表
|
||||
|
||||
#### P4-7:通知偏好页面 + PreferenceForm + Zod
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-4
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/app/(app)/parent/preferences/page.tsx`
|
||||
- `apps/parent-portal/src/components/PreferenceForm.tsx`(矩阵式 UI:子女×事件×渠道,react-hook-form + zodResolver)
|
||||
- `apps/parent-portal/src/schemas/notificationPreferences.ts`(Zod schema,见 02 §14.4)
|
||||
- **验收标准**:
|
||||
1. 权限校验:`PARENT_PREFERENCES_UPDATE`
|
||||
2. 不可用渠道 Toggle disabled + tooltip
|
||||
3. 保存成功后 invalidate `['parent','preferences']`
|
||||
4. 表单 dirty 状态追踪 + 离开页提示
|
||||
|
||||
#### P4-8:跨标签同步(BroadcastChannel)
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-3
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/lib/crossTabSync.ts`(见 02 §16.2 完整实现)
|
||||
- 集成到 RootLayout(`useCrossTabSync()`)
|
||||
- **验收标准**:
|
||||
1. Tab A 切换子女 → Tab B 同步更新
|
||||
2. LWW 冲突解决(ts 大的胜出)
|
||||
3. Safari 降级为 storage 事件
|
||||
|
||||
#### P4-9:Vitest 单测 + MSW 集成测试
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-4 ~ P4-7
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/vitest.config.ts`
|
||||
- 单测:组件渲染/交互、Hook 逻辑、Zod schema 校验、纯函数 utils(覆盖率 ≥ 85%)
|
||||
- 集成测试:useChildSwitcher + Zustand slice + invalidate 流程(MSW mock,覆盖率 ≥ 75%)
|
||||
- **验收标准**:
|
||||
1. `pnpm --filter parent-portal test` 全绿
|
||||
2. 覆盖率达标(单元 ≥ 85%,集成 ≥ 75%)
|
||||
|
||||
#### P4-10:Dockerfile 多阶段构建
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P4-1
|
||||
- **交付物**:`apps/parent-portal/Dockerfile`(builder + runtime,node:22-alpine)
|
||||
- **验收标准**:
|
||||
1. `docker build` 成功
|
||||
2. HEALTHCHECK 指向 `/api/health`
|
||||
3. 镜像体积 < 300MB
|
||||
|
||||
### P5 阶段任务
|
||||
|
||||
#### P5-1:WebSocket 接入 + 事件处理
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:push-gateway :8081/ws 就绪(ai02);parent-portal P4 完成
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/hooks/useWebSocket.ts`(连接 push-gateway ws,JWT 鉴权,自动重连)
|
||||
- 事件处理:`NotificationRequested` → toast + 未读数+1;`GradeRecorded` → toast + 成绩 invalidate;`SchoolAnnouncement` → toast + dashboard invalidate
|
||||
- **验收标准**:
|
||||
1. WS 连接建立后收事件正常
|
||||
2. 断线自动重连(指数退避)
|
||||
|
||||
#### P5-2:通知中心页面 + NotificationFeed
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P5-1
|
||||
- **交付物**:
|
||||
- `apps/parent-portal/src/app/(app)/parent/notifications/page.tsx`
|
||||
- `apps/parent-portal/src/components/NotificationFeed.tsx`(按子女×类型×已读筛选,批量已读,跳转,置顶)
|
||||
- **验收标准**:
|
||||
1. 权限校验:`NOTIFICATION_READ_OWN`
|
||||
2. WebSocket 推送 invalidate 通知列表
|
||||
|
||||
#### P5-3:推送降级(HTTP 轮询)
|
||||
|
||||
- **负责人**:ai15
|
||||
- **依赖**:P5-1
|
||||
- **交付物**:WS 重试 5 次失败后降级为 HTTP 轮询(60s 拉取通知列表)
|
||||
- **验收标准**:降级后通知延迟 ≤ 60s,用户感知降级提示
|
||||
|
||||
### P6 阶段任务
|
||||
|
||||
#### P6-1:Web Vitals + OTel browser SDK
|
||||
|
||||
- **交付物**:`next/web-vitals` 上报 + OTel browser SDK(复用 Shell 暴露的 TracerProvider)
|
||||
- **验收标准**:LCP/CLS/TTFB 指标上报到 Gateway
|
||||
|
||||
#### P6-2:A11y WCAG 2.2 AA 审计 + 修复
|
||||
|
||||
- **交付物**:axe-core 自动扫描 + 手动键盘导航测试,0 严重违规
|
||||
- **验收标准**:所有页面 0 严重 A11y 违规
|
||||
|
||||
#### P6-3:性能优化 + bundle 分析
|
||||
|
||||
- **交付物**:bundle 分析报告 + 代码分割优化(首屏 JS ≤ 80KB gzipped)
|
||||
- **验收标准**:Lighthouse 移动端 4G ≥ 90 分
|
||||
|
||||
#### P6-4:多语言扩展(en-US)
|
||||
|
||||
- **交付物**:`apps/parent-portal/src/i18n/messages/en-US/*.json`(镜像 zh-CN 结构)
|
||||
- **验收标准**:en-US 完成度 100%
|
||||
|
||||
#### P6-5:PWA(Service Worker + manifest)
|
||||
|
||||
- **交付物**:`public/manifest.json` + Service Worker 缓存策略(见 02 §18.4)
|
||||
- **验收标准**:可安装到主屏,离线可查看缓存的子女数据
|
||||
|
||||
#### P6-6:安全硬化(CSP + 敏感数据脱敏)
|
||||
|
||||
- **交付物**:CSP 头配置(复用 Shell)+ 截图脱敏 + 页面离开遮罩
|
||||
- **验收标准**:CSP 无违规报告;敏感数据不泄漏
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai15 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai15 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游 | 就绪信号 | 提供方 | 状态 | 阻塞影响 |
|
||||
| ---- | -------- | ------ | ---- | -------- |
|
||||
| teacher-portal Shell | MF exposes(AppShell + GraphQLProvider + hooks + UI 组件)+ shared singleton | ai13 | ⏳ P2 | P4 无法启动 |
|
||||
| parent-bff GraphQL | `POST /graphql` :3010 + currentUser/myChildren/childSummary/childGrades Query | ai05 | ⏳ P4 | 核心数据源 |
|
||||
| iam GetChildrenByParent | gRPC 50052 + `iam_student_guardians` 表 | ai06 | ⏳ P3 补全 | P0 多子女阻塞(见 ISSUE-010) |
|
||||
| core-edu | gRPC 50053 + GradeService/HomeworkService/AttendanceService | ai08 | ⏳ P3 | 成绩/作业数据 |
|
||||
| data-ana | gRPC 50055 + AnalyticsService | ai11 | ⏳ P4 | 学情分析数据 |
|
||||
| msg | gRPC 50056 + NotificationService | ai10 | ⏳ P5 | 通知中心 |
|
||||
| push-gateway | :8081/ws WebSocket | ai02 | ⏳ P5 | 实时推送 |
|
||||
| shared-ts | ApiClient/Logger(coord 维护) | coord | ⏳ | 基础工具 |
|
||||
| contracts | Permissions 常量(coord 维护) | coord | ⏳ | 权限校验 |
|
||||
| ui-tokens / ui-components / hooks | 三层令牌 + shadcn + usePermission/useAuth(ai07/ai13 维护) | ai07/ai13 | ⏳ P2 收尾 | UI 基础 |
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
| 信号 | 说明 | 阶段 |
|
||||
| ---- | ---- | ---- |
|
||||
| parent-portal :4002 dev server 启用 | MF Remote 可被 Shell 加载 | P4-1 完成 |
|
||||
| MF Remote remoteEntry.js 可加载 | Shell 端 `remotes.parent` 可解析 | P4-1 完成 |
|
||||
| 核心 GraphQL 查询可执行 | currentUser / myChildren / childSummary 返回数据(mock 或真实) | P4-2 完成 |
|
||||
| 多子女切换可用 | ChildSwitcher + invalidate 流程通过 | P4-3 完成 |
|
||||
| Dashboard 可访问 | 家长登录 → 看到 Dashboard(含子女卡片) | P4-4 完成 |
|
||||
| 健康检查通过 | `GET /api/health` + `GET /api/ready` 200 | P4-1 完成 |
|
||||
| 测试覆盖率达标 | 单元 ≥ 85% + 集成 ≥ 75% | P4-9 完成 |
|
||||
| Docker 镜像可构建 | `docker build` 成功 | P4-10 完成 |
|
||||
|
||||
### 4.3 全并行 Mock 策略
|
||||
|
||||
| 消费接口 | Mock 方式 | 切换真实时机 |
|
||||
| -------- | --------- | ------------ |
|
||||
| parent-bff GraphQL | MSW 拦截 `POST /api/v1/parent/graphql`,按 operationName 返回 fixtures | parent-bff GraphQL :3010 就绪 ✅ |
|
||||
| iam login | MSW 返回固定 JWT(parent 角色) | api-gateway + iam 就绪 ✅ |
|
||||
| push-gateway WebSocket | mock-socket 模拟 WS 推送(每 30s 1 条通知) | push-gateway :8081 就绪 ✅ |
|
||||
| 子女数据一致性 | myChildren mock 返回 student-001 + student-002,与所有 child* 查询 student_id 一致 | iam GetChildrenByParent 就绪 |
|
||||
|
||||
> Mock 由 `NEXT_PUBLIC_API_MOCKING=enabled` 环境变量控制,上游就绪后设为 `disabled`。
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与缓解
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| ---- | ---- | ---- |
|
||||
| ISSUE-001/002 未仲裁(REST vs GraphQL) | P4-2 GraphQL client 接入方向不确定 | 先按 GraphQL 预排期;仲裁若改 REST,P4-2 重写(预计 1d) |
|
||||
| iam GetChildrenByParent 缺失(ISSUE-010) | 多子女场景无法落地 | mock 固定 2 子女开发;coord 跟踪 ai06 P3 补全 |
|
||||
| MF SSR 对齐复杂 | Remote SSR 需 Shell 上下文 | 优先 CSR,仅 Dashboard 首屏 SSR;P4-1 PoC 验证 |
|
||||
| parent-bff 契约未最终确认 | GraphQL schema 可能变动 | P4 启动前与 ai05 对齐 schema;MSW mock 解耦 |
|
||||
| TanStack Query 缓存膨胀 | 多子女历史查询堆积 | gcTime 5min + 切换子女清理非当前子女缓存 |
|
||||
|
||||
---
|
||||
|
||||
## §6 质量门禁
|
||||
|
||||
每个任务完成前必须通过:
|
||||
|
||||
- `pnpm --filter parent-portal lint` 零错误
|
||||
- `pnpm --filter parent-portal typecheck` 零错误
|
||||
- `pnpm --filter parent-portal test` 全绿(P4-9 起强制)
|
||||
- 设计令牌三层规则(无 `#hex` / 无硬编码字体 / 无任意值,ESLint 强制)
|
||||
- A11y:jsx-a11y error 级零违规
|
||||
|
||||
> 提交前校验见 [project_rules §8](../../../../.trae/rules/project_rules.md),commit 遵循 Conventional Commits:`feat(parent-portal): ...`
|
||||
|
||||
@@ -1,45 +1,406 @@
|
||||
# push-gateway 工作排期
|
||||
|
||||
> 负责人:ai02
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/push-gateway_contract.md](../contracts/push-gateway_contract.md)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/push-gateway_contract.md](../contracts/push-gateway_contract.md)、[objections/push-gateway_issue.md](../objections/push-gateway_issue.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 依据:[ai-allocation.md](../../ai-allocation.md) §7.2、[02-architecture-design.md](../../../services/push-gateway/docs/02-architecture-design.md) §12 实施优先级、[president-final-rulings.md](../../president-final-rulings.md) §3.4 回写义务
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
push-gateway 负责长连接接入(HTTP /internal/* + WebSocket/SSE)与多设备会话管理,配合审计表实现消息推送可观测。全阶段目标:P2 HTTP+WS 接入 → P3 多设备会话 → P4-P6 审计与加固。
|
||||
push-gateway 是 Go 实现的实时推送基础设施服务(L3 网关层),管理 WebSocket 长连接,接收 msg 服务的推送请求投递到在线客户端。无业务状态,仅持有连接池 + Redis 跨实例广播。
|
||||
|
||||
**全阶段目标**:
|
||||
- **批次 0(等待期)**:复审 02 文档 + 回写 ISSUE-053/055/056/058 裁决 + 提请 ISSUE-001~007 仲裁
|
||||
- **批次 4(P5,13 天)**:完整实现 /internal/push + WebSocket 生命周期 + Redis Pub/Sub + Kafka 消费 + /readyz + /metrics + OTel + 多阶段 Dockerfile + slog + shared-go 接入 + 集成测试
|
||||
- **批次 5(P6 硬化,5 天)**:Reconnect 协议 + Redis Stream 持久化 + 测试覆盖率 ≥ 80% + ADR/非功能性需求/失败模式章节补全
|
||||
|
||||
**关键路径依赖**:
|
||||
- 批次 0.14:shared-go 包骨架(coord)—— tracer/logger/jwks/env 4 模块
|
||||
- 批次 1:iam P2.1(JWT RS256 + JWKS 端点)—— WebSocket 鉴权前置
|
||||
- 批次 4:msg gRPC 50056 + edu.notification.requested topic —— 推送事件来源
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(批次 0 + 批次 4 + 批次 5)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai02 push-gateway 全阶段排期
|
||||
title ai02 push-gateway 全阶段排期(批次 0 + 批次 4 P5 + 批次 5 P6)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a2a, 2026-07-10, Xd
|
||||
section 批次0 等待期 2天
|
||||
0.1 复审01/02文档+核查已有仲裁 :crit, a0a, 2026-07-10, 1d
|
||||
0.2 回写ISSUE-053/055/056/058到02文档 :crit, a0b, after a0a, 1d
|
||||
0.3 提请ISSUE-001~007待coord仲裁 :a0c, after a0a, 1d
|
||||
|
||||
section 批次4 P5-P0 安全加固 3天
|
||||
4.1 Origin校验+CheckOrigin白名单 :crit, a4a, after b3a, 1d
|
||||
4.2 /internal/*鉴权对齐X-Internal-Token :crit, a4b, after a4a, 1d
|
||||
4.3 心跳改WebSocket控制帧+SetReadDeadline :crit, a4c, after a4a, 1d
|
||||
4.4 单用户连接数限制MaxConn=5 :crit, a4d, after a4c, 1d
|
||||
4.5 Send通道满时指标+日志 :a4e, after a4d, 1d
|
||||
|
||||
section 批次4 P5-P0 基础设施 2天
|
||||
4.6 Dockerfile重构多阶段+非root+healthcheck+ldflags :crit, a4f, after b3a, 1d
|
||||
4.7 引入log/slog替换标准log :a4g, after a4f, 1d
|
||||
4.8 接入shared-go tracer/logger/jwks/env :a4h, after a4g, 1d
|
||||
|
||||
section 批次4 P5-P1 横向扩展 3天
|
||||
4.9 Redis Pub/Sub跨实例广播 :crit, a4i, after a4h, 2d
|
||||
4.10 Redis SET在线状态+启动重建(ISSUE-058) :crit, a4j, after a4i, 1d
|
||||
4.11 /metrics自定义指标+/readyz软失败(ISSUE-055) :a4k, after a4j, 1d
|
||||
4.12 优雅关闭所有WebSocket连接 :a4l, after a4k, 1d
|
||||
|
||||
section 批次4 P5-P2 高级功能 3天
|
||||
4.13 Kafka消费edu.notification.requested(ISSUE-053) :a4m, after a4j, 2d
|
||||
4.14 JWT RS256升级+JWKS fetcher :a4n, after a4m, 1d
|
||||
4.15 设计决策记录章节回写(ISSUE-056) :a4o, after a4n, 1d
|
||||
|
||||
section 批次4 P5-P2 集成 2天
|
||||
4.16 与msg联调/internal/push双通道 :crit, a4p, after a4o, 1d
|
||||
4.17 集成测试+端到端验证 :crit, a4q, after a4p, 1d
|
||||
|
||||
section 批次5 P6 硬化 5天
|
||||
5.1 Reconnect协议session_id+last_seq :a5a, after a4q, 2d
|
||||
5.2 Redis Stream替代Pub/Sub持久化 :a5b, after a5a, 2d
|
||||
5.3 测试覆盖率≥80% :crit, a5c, after a4q, 3d
|
||||
5.4 ADR+非功能性需求+失败模式章节(ISSUE-007) :a5d, after a4q, 2d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai02 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> **依赖锚点**:`b3a` = 批次 3 完成信号(content + data-ana 就绪,见 [workline.md](../workline.md) §1 批次 3)
|
||||
> **总工期**:批次 0(2 天)+ 批次 4(13 天)+ 批次 5(5 天)= **20 天**
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### 3.1 批次 0:等待期(2 天)
|
||||
|
||||
#### 任务 0.1:复审 01/02 文档 + 核查已有仲裁
|
||||
|
||||
- **负责人**:ai02
|
||||
- **交付物**:⚠️ 由 ai02 自行补充
|
||||
- **依赖**:见 [contracts/push-gateway_contract.md](../contracts/push-gateway_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai02 自行补充
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- [objections/push-gateway_issue.md](../objections/push-gateway_issue.md) §0 核查矩阵
|
||||
- 01/02 文档审查结论(已汇报给用户)
|
||||
- **验收标准**:
|
||||
- 5 项已有仲裁(ISSUE-053/055/056/058 + ARB /internal/push)核查完成
|
||||
- 01 文档 7 项偏差登记
|
||||
- 02 文档 4 项未回写 + 3 项规范缺失登记
|
||||
|
||||
#### 任务 0.2:回写 ISSUE-053/055/056/058 到 02 文档
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 0.1
|
||||
- **交付物**:02-architecture-design.md 修订
|
||||
- §5.1 topic 改为 `edu.notification.requested`(ISSUE-053)
|
||||
- §6.7 增 Kafka 软失败逻辑 + Redis 软失败(ISSUE-055/058,待 ISSUE-006 仲裁最终策略)
|
||||
- 新增 §5.4"设计决策记录:gRPC vs HTTP 协议选型(coord 已采纳 P1)"(ISSUE-056)
|
||||
- §3.1/§8.4 补 Hub 启动 Redis SET 重建 + 60s 不一致窗口文档化 + `push_gateway_redis_set_rebuild_total` 指标(ISSUE-058)
|
||||
- **验收标准**:4 项裁决全部回写,[objections/push-gateway_issue.md](../objections/push-gateway_issue.md) §0 核查矩阵状态更新为 ✅
|
||||
|
||||
#### 任务 0.3:提请 ISSUE-001~007 待 coord 仲裁
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 0.1
|
||||
- **交付物**:[objections/push-gateway_issue.md](../objections/push-gateway_issue.md) §1 七项 issue
|
||||
- **验收标准**:coord 在 [coord.md](../coord.md) 追加 ARB-003+ 仲裁章节
|
||||
|
||||
### 3.2 批次 4(P5):完整实现(13 天)
|
||||
|
||||
#### 任务 4.1:Origin 校验 + CheckOrigin 白名单(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:批次 3 完成信号 `b3a`
|
||||
- **交付物**:[internal/ws/handler.go](../../../services/push-gateway/internal/ws/handler.go) 修订
|
||||
- `upgrader.CheckOrigin` 从 `return true` 改为读 `WS_ALLOWED_ORIGINS` 环境变量白名单
|
||||
- 无 Origin 头拒绝
|
||||
- **验收标准**:
|
||||
- 非白名单 Origin 返 403
|
||||
- 白名单来源(teacher/student/parent portal 域名)通过
|
||||
|
||||
#### 任务 4.2:/internal/* 鉴权对齐 X-Internal-Token(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.1 + ISSUE-002 仲裁结果
|
||||
- **交付物**:
|
||||
- [internal/ws/handler.go](../../../services/push-gateway/internal/ws/handler.go) `internalAPIKeyHeader` 改为 `X-Internal-Token`(待仲裁确认)
|
||||
- [internal/config/config.go](../../../services/push-gateway/internal/config/config.go) `InternalAPIKey` → `InternalAPIToken`,环境变量 `INTERNAL_API_TOKEN`
|
||||
- 错误码对齐 `PUSH_UNAUTHORIZED`
|
||||
- **验收标准**:无 token/错 token 返 401 + `PUSH_UNAUTHORIZED`;DevMode 跳过
|
||||
|
||||
#### 任务 4.3:心跳改用 WebSocket 控制帧 + SetReadDeadline(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.1
|
||||
- **交付物**:[internal/ws/handler.go](../../../services/push-gateway/internal/ws/handler.go) 重构
|
||||
- 移除文本消息 `ping/pong` 逻辑([handler.go#L82-L84](../../../services/push-gateway/internal/ws/handler.go#L82-L84))
|
||||
- 改用 `conn.SetReadDeadline(60s)` + `conn.SetPongHandler`
|
||||
- 客户端 Ping 控制帧 → gorilla 自动回 Pong
|
||||
- 60s 无任何消息则关闭连接
|
||||
- **验收标准**:
|
||||
- 僵尸连接 60s 后自动清理
|
||||
- 心跳走 RFC 6455 控制帧,不再走文本消息
|
||||
|
||||
#### 任务 4.4:单用户连接数限制 MaxConn=5(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.3
|
||||
- **交付物**:[internal/hub/hub.go](../../../services/push-gateway/internal/hub/hub.go) 修订
|
||||
- Hub 增加 `counters map[string]int`(02 文档 §2)
|
||||
- `Register` 检查 `counters[userID] >= 5` 返 `ErrTooManyConnections`
|
||||
- 超限返 close 帧(code=1008 policy violation)
|
||||
- **验收标准**:第 6 个连接被拒,错误码 `PUSH_TOO_MANY_CONNECTIONS` 429
|
||||
|
||||
#### 任务 4.5:Send 通道满时指标 + 日志(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.4
|
||||
- **交付物**:[internal/hub/hub.go](../../../services/push-gateway/internal/hub/hub.go) `Send` 方法修订
|
||||
- 通道满时 `slog.Warn` + `messages_dropped_total` Counter
|
||||
- **验收标准**:`/metrics` 暴露 `push_gateway_messages_dropped_total{reason="channel_full"}`
|
||||
|
||||
#### 任务 4.6:Dockerfile 重构(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:批次 3 完成信号 `b3a`
|
||||
- **交付物**:[Dockerfile](../../../services/push-gateway/Dockerfile) 重构
|
||||
- 多阶段(已有,保留)
|
||||
- 非 root 用户(`adduser -D appuser` + `USER appuser`)
|
||||
- healthcheck(`wget --spider http://localhost:8081/healthz`)
|
||||
- ldflags 优化(`-ldflags="-s -w -X main.Version=$(git rev-parse --short HEAD)"`)
|
||||
- go.mod 与 Dockerfile 版本对齐(1.25.0 vs golang:1.22-alpine)
|
||||
- **验收标准**:`docker build` 通过,容器以非 root 运行,healthcheck 工作
|
||||
|
||||
#### 任务 4.7:引入 log/slog 替换标准 log(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.6
|
||||
- **交付物**:新增 `internal/observability/logger.go`
|
||||
- `slog.NewJSONHandler` + `slog.SetDefault`
|
||||
- 日志字段:`timestamp` `level` `service=push-gateway` `request_id` `trace_id` `user_id` `conn_id` `event`
|
||||
- main.go / handler.go / hub.go 替换所有 `log.Printf` 为 `slog`
|
||||
- **验收标准**:日志输出 JSON 格式,包含 trace_id 字段
|
||||
|
||||
#### 任务 4.8:接入 shared-go(P0,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.7 + 批次 0.14(shared-go 骨架)
|
||||
- **交付物**:
|
||||
- go.mod 引入 `github.com/edu-cloud/shared-go`
|
||||
- 替换本地 [observability/tracer.go](../../../services/push-gateway/internal/observability/tracer.go) 为 `shared-go/observability/tracer`
|
||||
- 引入 `shared-go/observability/logger`(替换任务 4.7 本地实现)
|
||||
- 引入 `shared-go/config/env`(替换 [config.go](../../../services/push-gateway/internal/config/config.go) `getEnv`)
|
||||
- 引入 `shared-go/auth/jwks`(任务 4.14 使用)
|
||||
- **验收标准**:本地 tracer.go/logger.go 删除,统一从 shared-go import
|
||||
|
||||
#### 任务 4.9:Redis Pub/Sub 跨实例广播(P1,2 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.8
|
||||
- **交付物**:新增 `internal/redis/pubsub.go` + Hub 改造
|
||||
- 订阅 `edu.push.channel.user.*` + `edu.push.channel.broadcast`
|
||||
- 本实例无目标用户时 PUBLISH 到对应 channel
|
||||
- 持有该用户的实例订阅后投递到本地连接
|
||||
- 引入 `github.com/redis/go-redis/v9` 依赖
|
||||
- **验收标准**:
|
||||
- 双实例部署,msg 调实例 A `/internal/push user=B`,实例 B 持有 B → 收到推送
|
||||
- 广播 PUBLISH 一次,所有实例投递本地连接
|
||||
|
||||
#### 任务 4.10:Redis SET 在线状态 + 启动重建(P1,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.9
|
||||
- **交付物**:Hub 改造(对齐 ISSUE-058)
|
||||
- 连接建立:`SADD edu:push:online:<userID> <instanceID>` + `EXPIRE 60s`
|
||||
- 心跳续期:`EXPIRE 60s`
|
||||
- 连接断开:`SREM` + 空 SET 则 `DEL`
|
||||
- **Hub 启动重建**:遍历内存连接 SADD + EXPIRE;先清空 Redis 中本 instanceID 旧成员(避免幽灵)
|
||||
- 实例崩溃 SET 自然过期(60s)
|
||||
- **验收标准**:
|
||||
- 实例重启后 60s 内 Redis SET 重建完成
|
||||
- `push_gateway_redis_set_rebuild_total` 指标暴露
|
||||
|
||||
#### 任务 4.11:/metrics 自定义指标 + /readyz 软失败(P1,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.10
|
||||
- **交付物**:新增 `internal/observability/metrics.go` + /readyz 重构
|
||||
- 指标清单(02 文档 §6.4):`active_connections` `messages_pushed_total` `messages_dropped_total` `heartbeat_total` `disconnect_total` `redis_pubsub_latency_seconds` `kafka_consumed_total` `redis_set_rebuild_total`
|
||||
- /readyz 检查 Redis PING + Kafka consumer lag
|
||||
- Redis/Kafka 软失败(ISSUE-055/058,待 ISSUE-006 仲裁):返 200 + `degraded: true`
|
||||
- **验收标准**:
|
||||
- `/metrics` 暴露 8+ 自定义指标
|
||||
- Redis 故障时 /readyz 返 200 + `degraded: true`(不返 503)
|
||||
|
||||
#### 任务 4.12:优雅关闭所有 WebSocket 连接(P1,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.11
|
||||
- **交付物**:[main.go](../../../services/push-gateway/main.go) + Hub 改造
|
||||
- Hub 新增 `CloseAll()` 方法,向所有连接发 close 帧(code=1001 going away)
|
||||
- SIGTERM → 标记 Hub closing(拒新连接)→ CloseAll → 等 10s → srv.Shutdown → 关 Kafka consumer → 关 Redis subscriber → tracerShutdown
|
||||
- **验收标准**:SIGTERM 后所有连接收到 close 帧,无连接泄漏
|
||||
|
||||
#### 任务 4.13:Kafka 消费 edu.notification.requested(P2,2 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.10 + msg 就绪信号
|
||||
- **交付物**:新增 `internal/kafka/consumer.go`
|
||||
- Consumer Group `push-gateway`
|
||||
- 订阅 `edu.notification.requested`(ISSUE-053 裁决的 topic 名)
|
||||
- 至少一次 + 重试 3 次入 DLQ
|
||||
- 幂等:`event_id` Redis SETNX TTL 24h
|
||||
- 消费 → 调 Hub.SendToUser / Broadcast
|
||||
- **验收标准**:
|
||||
- msg 发布 `NotificationRequested` → push-gateway 消费 → 推送到在线客户端
|
||||
- 重复 event_id 不重投
|
||||
|
||||
#### 任务 4.14:JWT RS256 升级 + JWKS fetcher(P2,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.8(shared-go/jwks)+ iam 就绪信号
|
||||
- **交付物**:[internal/ws/handler.go](../../../services/push-gateway/internal/ws/handler.go) `authenticate` 重构
|
||||
- 移除 HS256 共享密钥校验
|
||||
- 改用 RS256:通过 `shared-go/auth/jwks` 拉取 iam `/.well-known/jwks.json` 公钥
|
||||
- 缓存公钥 + 定期刷新(5 分钟)
|
||||
- **验收标准**:
|
||||
- iam 签发的 RS256 JWT 通过校验
|
||||
- 公钥轮换后 5 分钟内生效
|
||||
|
||||
#### 任务 4.15:设计决策记录章节回写(P2,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.13
|
||||
- **交付物**:02-architecture-design.md 新增 §5.4(ISSUE-056)
|
||||
- 标题:"设计决策记录:gRPC vs HTTP 协议选型(coord 已采纳 P1)"
|
||||
- 正文标注"coord 已采纳,见 coord-final-decisions P1/P5/P6"
|
||||
- 记录决策背景、方案对比、采纳理由
|
||||
- **验收标准**:章节存在且标注正确
|
||||
|
||||
#### 任务 4.16:与 msg 联调 /internal/push 双通道(P2,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.13 + 任务 4.14 + msg 就绪
|
||||
- **交付物**:联调测试报告
|
||||
- 定向推送:msg → HTTP /internal/push → push-gateway → WebSocket
|
||||
- 广播:msg → Kafka NotificationRequested → push-gateway → 全在线客户端
|
||||
- 离线场景:`delivered: false, online: false` → msg 走 SMS/邮件
|
||||
- **验收标准**:[matrix.md](../matrix.md) §9.5 推送链路检查清单全通过
|
||||
|
||||
#### 任务 4.17:集成测试 + 端到端验证(P2,1 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 4.16
|
||||
- **交付物**:集成测试用例 + 端到端验证报告
|
||||
- 单实例 1000 连接压测
|
||||
- 双实例跨实例推送验证
|
||||
- Redis 故障降级验证
|
||||
- Kafka 消费积压验证
|
||||
- **验收标准**:
|
||||
- [workline.md](../workline.md) §8 push-gateway 行更新为 ✅
|
||||
- [matrix.md](../matrix.md) §8 push-gateway 就绪信号 ✅
|
||||
|
||||
### 3.3 批次 5(P6):硬化(5 天)
|
||||
|
||||
#### 任务 5.1:Reconnect 协议(2 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:批次 4 完成
|
||||
- **交付物**:02 文档 §7.3 落地
|
||||
- 首次连接返 `{type:"hello", session_id, seq:0}`
|
||||
- 每条推送带递增 `seq`
|
||||
- 重连 `/ws?token=&session_id=&last_seq=` → 从 msg 拉取 `last_seq+1` 到当前补推
|
||||
- **验收标准**:客户端断线 30s 内重连,未送达消息补推成功
|
||||
|
||||
#### 任务 5.2:Redis Stream 替代 Pub/Sub(2 天)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:任务 5.1
|
||||
- **交付物**:`internal/redis/stream.go`
|
||||
- Pub/Sub → Redis Stream(持久化)
|
||||
- Consumer Group `push-gateway`
|
||||
- ACK 机制:投递成功后 XACK
|
||||
- 崩溃恢复:未 ACK 消息重新投递
|
||||
- **验收标准**:实例崩溃时未投递消息不丢失(msg 落库兜底 + Stream 持久化双保险)
|
||||
|
||||
#### 任务 5.3:测试覆盖率 ≥ 80%(3 天,可与 5.1/5.2 并行)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:批次 4 完成
|
||||
- **交付物**:
|
||||
- `hub_test.go`:注册/注销/推送/广播/连接数限制
|
||||
- `ws_test.go`:鉴权/心跳/Origin 校验
|
||||
- `config_test.go`:环境变量加载
|
||||
- `redis_test.go`:Pub/Sub + SET 重建
|
||||
- `kafka_test.go`:消费 + 幂等
|
||||
- **验收标准**:`go test -cover ./...` ≥ 80%
|
||||
|
||||
#### 任务 5.4:ADR + 非功能性需求 + 失败模式章节补全(2 天,可与 5.1/5.2 并行)
|
||||
|
||||
- **负责人**:ai02
|
||||
- **依赖**:批次 4 完成 + ISSUE-007 仲裁
|
||||
- **交付物**:02-architecture-design.md 新增章节
|
||||
- §14 ADR(Architecture Decision Records)
|
||||
- ADR-001:gorilla/websocket 选型(vs nhooyr/websocket)
|
||||
- ADR-002:Redis Pub/Sub vs Stream(P5 Pub/Sub → P6 Stream 演进)
|
||||
- ADR-003:心跳间隔 30s/60s 选型依据
|
||||
- ADR-004:10w 容量依据(goroutine-per-connection 内存估算)
|
||||
- ADR-005:HTTP + Kafka 双通道(vs 单通道)
|
||||
- §15 非功能性需求(可用性 SLO 99.9% / 安全合规 / 容量 SLA)
|
||||
- §16 失败模式(实例崩溃 / Redis 故障 / 网络分区 / Kafka 积压降级)
|
||||
- §17 容量估算(10w 连接内存/CPU/带宽)
|
||||
- **验收标准**:4 章节齐全,符合 arc42 规范
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai02 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai02 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 依赖项 | 提供方 | 就绪信号 | 当前状态 | 影响任务 |
|
||||
| ------ | ------ | -------- | -------- | -------- |
|
||||
| shared-go 包骨架 | coord(批次 0.14) | `packages/shared-go` 含 tracer/logger/jwks/env 4 模块 | ⏳ | 任务 4.8 |
|
||||
| iam JWT RS256 + JWKS 端点 | ai06(批次 1) | iam gRPC 50052 + `/.well-known/jwks.json` 可访问 | ⏳ | 任务 4.14 |
|
||||
| msg gRPC + Kafka topic | ai10(批次 4) | msg gRPC 50056 + `edu.notification.requested` topic 有事件 | ⏳ | 任务 4.13/4.16 |
|
||||
| Redis 基础设施 | coord(P1) | Redis 7.x 可访问 | ✅ P1 已就绪 | 任务 4.9/4.10 |
|
||||
| Kafka 基础设施 | coord(P1) | Kafka 可访问 | ✅ P1 已就绪 | 任务 4.13 |
|
||||
| ISSUE-001~007 仲裁 | coord | [coord.md](../coord.md) 追加 ARB-003+ | ⏳ | 任务 4.2/4.11/4.15/5.4 |
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
| 就绪标志 | 验证方式 | 供消费方 |
|
||||
| -------- | -------- | -------- |
|
||||
| push-gateway HTTP :8081 启用 | `GET /healthz` 返 200 | k8s 探针 / 监控 |
|
||||
| /readyz 返 200(含 Redis/Kafka 软失败检查) | `GET /readyz` 返 200 + `degraded` 字段 | k8s 探针 |
|
||||
| WebSocket /ws 端点可升级(JWT RS256 鉴权) | 客户端 `ws://host:8081/ws?token=JWT` 建立连接 | teacher-portal / student-portal / parent-portal |
|
||||
| /internal/push + /internal/broadcast 接收 msg 推送 | msg 调用返 `{success:true, delivered:true}` | msg (ai10) |
|
||||
| /internal/online/<userID> 查在线状态 | 返 `{online:bool, instances:[]}` | msg (ai10) |
|
||||
| Kafka consumer `edu.notification.requested` 订阅成功 | Consumer Group `push-gateway` lag=0 | msg (ai10) |
|
||||
| /metrics 暴露 8+ 自定义指标 | `GET /metrics` 含 `push_gateway_*` 指标 | Prometheus |
|
||||
|
||||
### 4.3 Mock 策略(开发期间)
|
||||
|
||||
**我提供的 mock**(push-gateway 未就绪前,供前端 portal):
|
||||
- WebSocket mock:前端用 mock-socket 库模拟 WS 连接,每 30s 推 1 条 mock 通知
|
||||
- HTTP mock:/internal/* 返 200 success
|
||||
|
||||
**我消费的 mock**(上游未就绪前):
|
||||
- NotificationEvent mock:msg 未就绪前,push-gateway 内置定时器每 30s 生成 mock 事件推所有在线客户端
|
||||
- JWT 验签 mock:iam 未就绪前使用本地固定 RS256 公钥(或 DevMode dev-token)
|
||||
- Kafka 订阅 mock:msg 未就绪前不启动 Kafka consumer,用本地定时器替代
|
||||
|
||||
---
|
||||
|
||||
## §5 风险与缓解
|
||||
|
||||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||||
| ---- | ---- | ---- | -------- |
|
||||
| ISSUE-001~007 仲裁延迟 | 中 | 阻塞批次 4 启动 | ai02 先按建议方案推进,仲裁结果出来后调整 |
|
||||
| msg 就绪延迟 | 中 | 阻塞任务 4.13/4.16 | 用 mock NotificationEvent 先完成 Kafka 消费逻辑 |
|
||||
| 10w 连接压测不达标 | 低 | 容量目标降级 | P6 阶段压测,若不达标则 horizontal scaling 兜底 |
|
||||
| Redis Pub/Sub 消息丢失 | 中 | 跨实例推送丢失 | msg 落库兜底 + P6 升级 Redis Stream |
|
||||
| shared-go 接口变更 | 低 | 任务 4.8 返工 | 紧跟 coord 0.14 任务,接口冻结后立即对接 |
|
||||
|
||||
@@ -1,45 +1,362 @@
|
||||
# student-bff 工作排期
|
||||
|
||||
> 负责人:ai04
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/student-bff_contract.md](../contracts/student-bff_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/student-bff_contract.md](../contracts/student-bff_contract.md)、[objections/student-bff_issue.md](../objections/student-bff_issue.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
> 裁决依据:[coord-final-decisions.md](../../coord-final-decisions.md) §2 B1-B8、[president-final-rulings.md](../../president-final-rulings.md) §2.2/§3.4/§6.1
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
student-bff 为学生端提供 GraphQL 聚合 API,覆盖 Dashboard、考试作答、作业提交等场景。全阶段目标:P2 GraphQL schema 骨架 → P3 Dashboard+考试+作业聚合 → P4-P6 持续优化。
|
||||
student-bff 为学生端提供 **GraphQL 聚合 API**(B1 裁决:P2 起直接 GraphQL + DataLoader),下游通过 **gRPC** 调用业务服务(B2 裁决:首次实现即 gRPC),复用 teacher-bff 产出的 **DownstreamClient 抽象**(B8 裁决)。
|
||||
|
||||
- **阶段归属**:P3 核心教学阶段(批次 2)
|
||||
- **端口**:3009(HTTP GraphQL endpoint)
|
||||
- **路由前缀**:`/student`(api-gateway 代理 `/api/v1/student/*` → student-bff:3009)
|
||||
- **schema 存放**:`packages/shared-ts/contracts/graphql/student-bff.schema.graphql`(president §2.2)
|
||||
- **核心场景**:学生 Dashboard 聚合 + 考试作答 + 作业提交 + 成绩查看
|
||||
|
||||
### 1.1 关键裁决对齐
|
||||
|
||||
| 裁决 | 结论 | 对齐方式 |
|
||||
| ---- | ---- | -------- |
|
||||
| B1 API 风格 | P2 起直接 GraphQL(Yoga + DataLoader) | P3 首次实现即 GraphQL,禁止 REST |
|
||||
| B2 下游通信 | 首次实现即 gRPC | @grpc/grpc-js + @bufbuild/protobuf,禁止 HTTP fetch |
|
||||
| B3 权限装饰器 | BFF 豁免 @RequirePermission | 仅校验 x-user-id 存在,权限交下游 |
|
||||
| B4 越权防御 | 全部 BFF 强制 | AuthorizationGuard 强制 userId 比对 |
|
||||
| B5 错误码前缀 | BFF_STUDENT_ | 统一 BFF_ 前缀 |
|
||||
| B6 缓存策略 | Redis 5-30s 短缓存 | CacheInterceptor + Redis |
|
||||
| B7 Kafka 订阅 | P2-P4 不订阅,P5 后订阅 | P3/P4 纯同步聚合,P5 引入 EventSubscriber |
|
||||
| B8 DownstreamClient | 回写 teacher-bff,3 BFF 统一 | 复用 ai03 P2 产出的抽象 |
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(批次 1 等待期 + 批次 2-5)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai04 student-bff 全阶段排期
|
||||
title ai04 student-bff 全阶段排期(对齐总裁 §6.1 批次时间线)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a4a, 2026-07-10, Xd
|
||||
section 批次1等待期
|
||||
W0.1 GraphQL schema 第一版起草 :crit, w1, 2026-07-10, 3d
|
||||
W0.2 01/02 文档回写(B1/B2/B5/B8) :crit, w2, after w1, 3d
|
||||
W0.3 schema 提交 coord 仲裁 :milestone, w3, after w2, 0d
|
||||
|
||||
section 批次2 P3 核心
|
||||
P3.1 NestJS+GraphQL Yoga 骨架 :crit, p1, after w3, 2d
|
||||
P3.2 DownstreamClient+gRPC client(iam+core-edu) :crit, p2, after p1, 3d
|
||||
P3.3 核心 Query Resolver(dashboard/homework/grades/exams) :crit, p3, after p2, 3d
|
||||
P3.4 Mutation(submitHomework)+AuthorizationGuard(B4) :crit, p4, after p3, 2d
|
||||
P3.5 DataLoader+N+1防御 :p5, after p4, 1d
|
||||
P3.6 Redis缓存(B6)+ActionState信封 :p6, after p4, 1d
|
||||
P3.7 /healthz+/readyz探针(iam+core-edu) :p7, after p6, 1d
|
||||
P3.8 横切关注点(logger/metrics/tracer/error filter) :p8, after p6, 2d
|
||||
P3.9 单元测试(覆盖率≥80%) :p9, after p8, 2d
|
||||
|
||||
section 批次3 P4 扩展
|
||||
P4.1 content gRPC client(textbooks/chapters/questions) :p10, after p9, 3d
|
||||
P4.2 data-ana gRPC client(weakness/trend) :p11, after p10, 2d
|
||||
P4.3 Query 扩展(myTextbooks/myWeakness/myTrend) :p12, after p11, 2d
|
||||
P4.4 /readyz 扩展探针(content+data-ana) :p13, after p12, 1d
|
||||
P4.5 Dashboard Resolver 字段扩展 :p14, after p12, 1d
|
||||
|
||||
section 批次4 P5 扩展
|
||||
P5.1 msg gRPC client(notifications) :p15, after p14, 2d
|
||||
P5.2 ai gRPC client(chat/streamChat SSE) :p16, after p15, 3d
|
||||
P5.3 Query/Mutation 扩展(myNotifications/markAsRead/aiChat) :p17, after p16, 2d
|
||||
P5.4 Kafka EventSubscriber(B7 P5订阅) :p18, after p17, 2d
|
||||
P5.5 push-gateway 推送通道 :p19, after p18, 2d
|
||||
P5.6 /readyz 扩展探针(msg+ai) :p20, after p19, 1d
|
||||
|
||||
section 批次5 P6 硬化
|
||||
P6.1 熔断器(opossum)完善 :p21, after p20, 2d
|
||||
P6.2 HPA+全链路可观测 :p22, after p21, 2d
|
||||
P6.3 灾备演练+99.9%可用性 :p23, after p22, 3d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai04 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> **关键路径**(crit):schema 起草 → 文档回写 → P3 骨架 → gRPC client → Query Resolver → Mutation+Guard
|
||||
> **总时间线**:批次 1 等待期 6 天 + 批次 2 P3 约 17 天 + 批次 3 P4 约 9 天 + 批次 4 P5 约 12 天 + 批次 5 P6 约 7 天
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### 3.1 批次 1 等待期(2026-07-10 起,约 6 天)
|
||||
|
||||
#### W0.1 GraphQL schema 第一版起草
|
||||
|
||||
- **负责人**:ai04
|
||||
- **交付物**:⚠️ 由 ai04 自行补充
|
||||
- **依赖**:见 [contracts/student-bff_contract.md](../contracts/student-bff_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai04 自行补充
|
||||
- **依赖**:无(president §2.2 裁决 ai04 在批次 1 等待期起草)
|
||||
- **交付物**:
|
||||
- `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` 第一版
|
||||
- 包含 Query/Mutation 清单 + 类型定义 + 权限点标注(`# @permission:`)+ DataScope 标注(`# @dataScope: SELF`)
|
||||
- 分页采用 Relay Cursor Connections 规范(president §2.2 #5)
|
||||
- 错误格式:GraphQL errors 数组 + `extensions.code` + `extensions.traceId`(president §2.2 #3)
|
||||
- **验收标准**:
|
||||
- P3 核心 Query:studentDashboard / myHomework / myGrades / myExams / myClasses / currentUser
|
||||
- P3 核心 Mutation:submitHomework
|
||||
- 提交 coord 仲裁(president §2.2:coord 在批次 2 启动前仲裁第一版)
|
||||
- **状态**:⏳ 待办(见 objections ISSUE-STU-004)
|
||||
|
||||
#### W0.2 01/02 文档回写
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:无
|
||||
- **交付物**:
|
||||
- `services/student-bff/docs/01-understanding.md` 回写(ISSUE-STU-001):
|
||||
- §3.2 REST 端点 → GraphQL Query/Mutation 清单
|
||||
- §3.1/§4 HTTP fetch → gRPC 下游调用
|
||||
- §3.3/§6 错误码 `STUDENT_BFF_` → `BFF_STUDENT_`
|
||||
- §7.2 删除已裁决的"待仲裁"项
|
||||
- `services/student-bff/docs/02-architecture-design.md` 回写(ISSUE-028-ai04):
|
||||
- §4 21 个 REST 端点 → GraphQL Schema 设计
|
||||
- §9.2 删除 REST→GraphQL 演进,改为 GraphQL 即起点
|
||||
- §9.3 删除 HTTP→gRPC 演进,改为 gRPC 首次实现即用
|
||||
- §8.3 删除 8 项已裁决的"待仲裁"标注(ISSUE-STU-003)
|
||||
- 修正 §11.4/§11.5 错误引用(ISSUE-STU-002)
|
||||
- 补充 GraphQL Schema / DataLoader / gRPC client / AuthorizationGuard 设计
|
||||
- **验收标准**:与 coord-final-decisions §2 B1-B8 + president §2.2 完全一致
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
### 3.2 批次 2 P3 核心教学(约 17 天)
|
||||
|
||||
#### P3.1 NestJS + GraphQL Yoga 骨架
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:批次 1 完成(iam gRPC 50052 + ai03 DownstreamClient 抽象就绪,president §6.1)
|
||||
- **交付物**:
|
||||
- `services/student-bff/` 服务骨架(克隆 teacher-bff 结构,B8 复用 shared/)
|
||||
- `src/app.module.ts` + `src/main.ts`(端口 3009)
|
||||
- GraphQL Yoga endpoint(`POST /graphql`)+ Playground(开发环境)
|
||||
- `package.json`(@edu/student-bff)+ `tsconfig.json`(NodeNext ESM)+ `nest-cli.json`
|
||||
- `Dockerfile`(多阶段构建,EXPOSE 3009)
|
||||
- **验收标准**:`POST /graphql` 返回 200 + schema 内省可用
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.2 DownstreamClient + gRPC client(iam + core-edu)
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.1 + ai03 teacher-bff P2 产出的 DownstreamClient 抽象(B8)
|
||||
- **交付物**:
|
||||
- `src/shared/downstream/downstream-client.ts`(复用 teacher-bff 抽象,B8)
|
||||
- gRPC client 配置:iam:50052 + core-edu:50053
|
||||
- `src/config/env.ts`:IamGrpcUrl + CoreEduGrpcUrl + 超时/重试参数
|
||||
- gRPC interceptor:traceId 透传 + 错误归一化
|
||||
- **验收标准**:可调用 iam.GetUserInfo + core-edu.HomeworkService.ListHomeworkByClass
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.3 核心 Query Resolver
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.2 + coord 仲裁的 schema 第一版
|
||||
- **交付物**:
|
||||
- `src/student/resolvers/dashboard.resolver.ts`:studentDashboard(聚合 iam + core-edu)
|
||||
- `src/student/resolvers/homework.resolver.ts`:myHomework(core-edu)
|
||||
- `src/student/resolvers/grades.resolver.ts`:myGrades(core-edu,B4 强制 userId 比对)
|
||||
- `src/student/resolvers/exams.resolver.ts`:myExams(core-edu)
|
||||
- `src/student/resolvers/classes.resolver.ts`:myClasses(core-edu)
|
||||
- `src/student/resolvers/auth.resolver.ts`:currentUser(聚合 iam.GetUserInfo + GetEffectivePermissions + GetViewports)
|
||||
- 并行编排:Promise.allSettled + 部分降级(president §2.6 方案 B:data 内 degraded 字段)
|
||||
- **验收标准**:5 个核心 Query 可执行,返回 ActionState 信封
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.4 Mutation(submitHomework)+ AuthorizationGuard(B4)
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.3
|
||||
- **交付物**:
|
||||
- `src/student/resolvers/homework.mutation.resolver.ts`:submitHomework Mutation
|
||||
- `src/student/guards/authorization.guard.ts`:B4 自我越权防御
|
||||
- 接口:`canAccessOwnData(userId, requestedStudentId): Promise<boolean>`
|
||||
- P3 实现:强制 `studentId === userId`(学生只能操作自己数据)
|
||||
- 参照 teacher-bff ISSUE-033-ai03 的 AuthorizationGuard 模式(president §2.9)
|
||||
- Zod 输入校验:SubmitHomeworkInput schema
|
||||
- **验收标准**:submitHomework 可提交;越权请求(studentId ≠ userId)返回 BFF_STUDENT_FORBIDDEN
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.5 DataLoader + N+1 防御
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.3
|
||||
- **交付物**:
|
||||
- `src/student/dataloaders/homework.loader.ts`:批量加载作业
|
||||
- `src/student/dataloaders/grades.loader.ts`:批量加载成绩
|
||||
- Dashboard 内多学生场景用 DataLoader 批量去重(004 §11.3)
|
||||
- **验收标准**:N+1 查询场景下下游 gRPC 调用数 ≤ 2
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.6 Redis 缓存(B6)+ ActionState 信封
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.3
|
||||
- **交付物**:
|
||||
- `src/shared/cache/cache.module.ts`:Redis CacheInterceptor
|
||||
- 缓存 Key 规范:`student:dashboard:{userId}` 等(TTL 5-30s,B6)
|
||||
- ActionState 信封:`{success, data, meta?}` / `{success: false, error: {code, message, details?, traceId?}}`
|
||||
- 降级模式:`data.degraded = true` + `data.degradedReason`(president §2.6 方案 B)
|
||||
- **验收标准**:缓存命中时 P50 < 100ms;降级响应符合方案 B
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.7 /healthz + /readyz 探针
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.2
|
||||
- **交付物**:
|
||||
- `src/shared/health/health.controller.ts`:/healthz(liveness)+ /readyz(readiness)
|
||||
- P3 /readyz 探针:iam gRPC 50052 + core-edu gRPC 50053(2 项,president §2.4)
|
||||
- 必需依赖失败返回 503;可选依赖软失败返回 200 + degraded
|
||||
- **验收标准**:/readyz 返回 2 项探针状态
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.8 横切关注点
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.1
|
||||
- **交付物**:
|
||||
- `src/shared/observability/logger.ts`(pino,service: 'student-bff')
|
||||
- `src/shared/observability/metrics.ts`(prom-client,11 个 student_bff_* 指标)
|
||||
- `src/shared/observability/tracer.ts`(OTel,serviceName: 'student-bff')
|
||||
- `src/shared/errors/global-error.filter.ts`(@Catch(),BFF_STUDENT_* 错误码)
|
||||
- `src/shared/errors/application-error.ts`(错误类层次)
|
||||
- 优雅关闭:SIGTERM → app.close() → shutdownTracer()
|
||||
- **验收标准**:/metrics 可访问;GlobalErrorFilter 捕获所有异常
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P3.9 单元测试
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:P3.3-P3.8
|
||||
- **交付物**:
|
||||
- `test/unit/resolvers/*.test.ts`:Resolver 聚合逻辑(mock gRPC 下游)
|
||||
- `test/unit/guards/*.test.ts`:AuthorizationGuard 越权防御
|
||||
- `test/unit/dataloaders/*.test.ts`:DataLoader 批量逻辑
|
||||
- `vitest.config.ts`(对齐 classes 测试框架)
|
||||
- **验收标准**:覆盖率 ≥ 80%
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
### 3.3 批次 3 P4 内容分析扩展(约 9 天)
|
||||
|
||||
#### P4.1-P4.2 content + data-ana gRPC client
|
||||
|
||||
- **负责人**:ai04
|
||||
- **依赖**:批次 3 启动(content gRPC 50054 + data-ana gRPC 50055 就绪)
|
||||
- **交付物**:
|
||||
- content gRPC client:TextbookService + ChapterService + QuestionService + KnowledgeGraphService
|
||||
- data-ana gRPC client:AnalyticsService.GetStudentWeakness + GetLearningTrend
|
||||
- **验收标准**:可调用 content + data-ana gRPC RPC
|
||||
- **状态**:⏳ 待办(属"跨阶段扩展例外",president §2.3 允许新增下游 gRPC 调用)
|
||||
|
||||
#### P4.3-P4.5 Query 扩展 + 探针扩展 + Dashboard 字段扩展
|
||||
|
||||
- **交付物**:
|
||||
- Query 扩展:myTextbooks / myChapters / myQuestions / myLearningPath / myWeakness / myTrend
|
||||
- /readyz 扩展探针:+ content 50054 + data-ana 50055(共 4 项)
|
||||
- Dashboard Resolver 字段扩展:null 字段 → 真实 data-ana 数据(president §2.3 #4 允许)
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
### 3.4 批次 4 P5 沟通 AI 扩展(约 12 天)
|
||||
|
||||
#### P5.1-P5.3 msg + ai gRPC client + Query/Mutation 扩展
|
||||
|
||||
- **交付物**:
|
||||
- msg gRPC client:NotificationService.ListNotifications + MarkAsRead
|
||||
- ai gRPC client:AiService.Chat + StreamChat(SSE 流式透传)
|
||||
- Query/Mutation 扩展:myNotifications / markAsRead Mutation / aiChat / aiStreamChat
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
#### P5.4-P5.6 Kafka EventSubscriber + push-gateway + 探针扩展
|
||||
|
||||
- **交付物**:
|
||||
- `src/student/events/event-subscriber.ts`:Kafka 消费者组(B7 P5 才订阅)
|
||||
- 订阅 topic:edu.homework.events / edu.exam.events / edu.grade.events / edu.identity.user.role_changed
|
||||
- 幂等性:Redis SETNX event_id 去重
|
||||
- push-gateway 推送通道:POST /push/user/:userId
|
||||
- /readyz 扩展探针:+ msg 50056 + ai 50058(共 6 项)
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
### 3.5 批次 5 P6 硬化(约 7 天)
|
||||
|
||||
#### P6.1-P6.3 熔断器 + HPA + 灾备
|
||||
|
||||
- **交付物**:
|
||||
- 熔断器(opossum)完善:每个下游 gRPC client 独立熔断器
|
||||
- HPA 自动扩缩容配置
|
||||
- 全链路 trace + Grafana 仪表盘
|
||||
- 灾备演练 + 99.9% 可用性压测
|
||||
- **状态**:⏳ 待办
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai04 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai04 自行补充
|
||||
### 4.1 我依赖的上游就绪标志
|
||||
|
||||
| 依赖项 | 提供 AI | 就绪信号 | 阻塞阶段 | 状态 |
|
||||
| ------ | ------- | -------- | -------- | ---- |
|
||||
| core_edu.proto 补全(AttendanceService / GetClassesByTeacher) | coord(president §2.5) | proto message + RPC 签名定义 | 批次 2 P3 | ⏳ |
|
||||
| buf.gen.yaml gRPC 插件 | coord(批次 0.9) | grpc/node 插件配置 | 批次 2 P3 | ⏳ |
|
||||
| ai03 DownstreamClient 抽象 | ai03(B8 裁决) | teacher-bff P2 产出可复用抽象 | 批次 2 P3 | ⏳ |
|
||||
| iam gRPC 50052 + 12 RPC | ai06(I1 裁决) | HealthService.Check = SERVING | 批次 2 P3 | ⏳ |
|
||||
| core-edu gRPC 50053 + 22 RPC | ai08(C2 裁决) | HealthService.Check = SERVING | 批次 2 P3 | ⏳ |
|
||||
| content gRPC 50054 + 18 RPC | ai09 | HealthService.Check = SERVING | 批次 3 P4 | ⏳ |
|
||||
| data-ana gRPC 50055 + 12 RPC | ai11 | HealthService.Check = SERVING | 批次 3 P4 | ⏳ |
|
||||
| msg gRPC 50056 + 13 RPC | ai10 | HealthService.Check = SERVING | 批次 4 P5 | ⏳ |
|
||||
| ai gRPC 50058 + 6 RPC | ai12 | HealthService.Check = SERVING | 批次 4 P5 | ⏳ |
|
||||
| coord 仲裁 student-bff schema 第一版 | coord(president §2.2) | schema 第一版裁定 | 批次 2 P3 | ⏳ |
|
||||
| api-gateway `/student` 路由 | ai01 | /api/v1/student/* 可代理 | 批次 2 P3 | ⏳ |
|
||||
|
||||
### 4.2 我的就绪标志(供下游消费)
|
||||
|
||||
| 就绪标志 | 验证方式 | 消费方 |
|
||||
| -------- | -------- | ------ |
|
||||
| student-bff GraphQL :3009 启用 | GET /healthz 返回 200 | k8s / 监控 |
|
||||
| /readyz 返回 200(含下游 gRPC 连通性) | GET /readyz 返回 200 + checks | k8s / 监控 |
|
||||
| GraphQL schema 可内省 | POST /graphql 返回 schema | ai14(student-portal) |
|
||||
| 核心 Query 可执行 | studentDashboard / myHomework / myGrades / myClasses / currentUser | ai14 |
|
||||
| 核心 Mutation 可执行 | submitHomework | ai14 |
|
||||
| /metrics 可访问 | GET /metrics 返回 prometheus 格式 | Prometheus |
|
||||
|
||||
---
|
||||
|
||||
## §5 Mock 策略(全并行开发期间)
|
||||
|
||||
### 5.1 我提供的 mock(供 ai14 student-portal)
|
||||
|
||||
在 student-bff 真实就绪前,为 ai14 提供 GraphQL mock:
|
||||
|
||||
- **方式**:MSW 拦截 POST /graphql + 固定 response
|
||||
- **mock 数据**:
|
||||
- currentUser 返回固定学生(id="student-001", name="李同学", roles=["student"])
|
||||
- studentDashboard 返回固定仪表盘(pendingHomework=3, upcomingExams=2, unreadNotifications=5)
|
||||
- myHomework 返回固定 3 个作业(1 个待提交)
|
||||
- myGrades 返回固定 5 个成绩
|
||||
- myExams 返回固定 2 个考试
|
||||
- myClasses 返回固定 1 个班级
|
||||
|
||||
### 5.2 我消费的 mock(上游未就绪前)
|
||||
|
||||
| 上游 | mock 方式 | 切换真实时机 |
|
||||
| ---- | --------- | ------------ |
|
||||
| iam gRPC | grpc-mock 拦截 + 固定 UserInfo/Permissions/Viewports | iam 就绪信号 ✅ |
|
||||
| core-edu gRPC | grpc-mock 拦截 + 固定 Homework/Exam/Grade/Class | core-edu 就绪信号 ✅ |
|
||||
| content gRPC | grpc-mock 拦截 + 固定 Textbook/Chapter/Question | content 就绪信号 ✅ |
|
||||
| data-ana gRPC | grpc-mock 拦截 + 固定 Weakness/Trend | data-ana 就绪信号 ✅ |
|
||||
| msg gRPC | grpc-mock 拦截 + 固定 Notification | msg 就绪信号 ✅ |
|
||||
| ai gRPC | grpc-mock 拦截 + 固定 Chat response | ai 就绪信号 ✅ |
|
||||
|
||||
> 所有上游 mock 通过 gRPC client 拦截器实现,上游就绪后移除拦截器切换真实调用(对齐 matrix.md §7 全并行 Mock 策略)。
|
||||
|
||||
---
|
||||
|
||||
## §6 风险与缓解
|
||||
|
||||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||||
| ---- | ---- | ---- | -------- |
|
||||
| schema 仲裁延迟阻塞 P3 启动 | 中 | 高 | ai04 批次 1 等待期优先产出 schema 草案(W0.1) |
|
||||
| ai03 DownstreamClient 抽象未就绪 | 中 | 高 | ISSUE-007 已识别,coord 验收批次 1 时检查 |
|
||||
| core_edu.proto 补全延迟 | 低 | 高 | president §2.5 已明确 coord 负责 proto 定义 |
|
||||
| GraphQL + gRPC 首次实现复杂度高 | 中 | 中 | 复用 teacher-bff P2 模式(B8 DownstreamClient + Yoga endpoint) |
|
||||
| 下游 gRPC mock 与真实行为不一致 | 中 | 低 | 集成测试阶段统一验证(matrix.md §9) |
|
||||
|
||||
@@ -1,45 +1,271 @@
|
||||
# student-portal 工作排期
|
||||
|
||||
> 负责人:ai14
|
||||
> 关联:[workline.md](../workline.md)、[coord.md](../coord.md)、[contracts/student-portal_contract.md](../contracts/student-portal_contract.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md §1 ARB-001](../coord.md)、[coord.md §2 ARB-002](../coord.md)、[contracts/student-portal_contract.md](../contracts/student-portal_contract.md)、[matrix.md](../matrix.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,开发期间用 mock,最后统一集成测试)
|
||||
> 依据:president-final-rulings.md §3.6(ai14 P3 功能范围)+ §7.14(ai14 工作内容最终清单)+ ARB-002 §2.3(P3 首个 Remote)+ 02-architecture-design.md v2 阶段能力累积矩阵
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
student-portal 是学生端微前端,通过 MF Remote 接入主应用,覆盖考试作答、作业提交等场景。全阶段目标:P2 MF Remote 骨架 → P3 考试作答+作业提交 → P4-P6 持续优化。
|
||||
student-portal 是学生端微前端(MF Remote),通过 Module Federation 接入 teacher-portal Shell,覆盖考试作答、作业提交、学情查看等场景。全阶段目标:P2 MF Remote 骨架预埋 → P3 考试作答 + 作业提交 + 基础页面 → P4 知识图谱 + 学情诊断 → P5 实时通知 + AI 辅助 → P6 可观测性硬化 + A11y + 性能。
|
||||
|
||||
**全并行模式**:ai14 一口气完成 P2-P6 全部代码,开发期间用 MSW mock 上游(student-bff GraphQL / api-gateway HTTP / push-gateway WebSocket),上游就绪后在 [matrix.md](../matrix.md) §8 更新就绪信号,最后统一集成测试。
|
||||
|
||||
**批次归属**(见 [workline.md §1](../workline.md)):
|
||||
|
||||
- 批次 2(P3):ai14 与 ai07 + ai08 + ai04 + ai03扩展 并行启动(`b2d, after b1d, 8d`)
|
||||
- 批次 3(P4):与 ai09 + ai11 + ai05 + ai15 并行
|
||||
- 批次 4(P5):与 ai10 + ai02 + ai12 + ai03扩展 并行
|
||||
- 批次 5(P6):与 ai16 + 持续优化 并行
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title ai14 student-portal 全阶段排期
|
||||
title ai14 student-portal 全阶段排期(全并行)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2-P6
|
||||
[阶段任务] :a14a, 2026-07-10, Xd
|
||||
```
|
||||
section P2 预埋
|
||||
项目骨架+设计令牌三层+独立壳路由 :crit, a14p0, 2026-07-10, 2d
|
||||
MF Remote配置(NextFederationPlugin remotes) :crit, a14a, after a14p0, 2d
|
||||
MSW基础设施+GraphQL请求层骨架 :a14b, after a14a, 2d
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai14 接管后必须自行细化为完整 P2-P6 排期。
|
||||
section P3 学生核心
|
||||
AppShell复用+学生端导航+路由守卫 :crit, a14c, after a14b, 2d
|
||||
Dashboard页(studentDashboard query) :a14d, after a14c, 2d
|
||||
我的班级页+我的考试列表+我的作业列表 :a14e, after a14d, 3d
|
||||
考试作答页(状态机+服务器时间同步) :crit, a14f, after a14e, 4d
|
||||
IDB断网恢复队列+自动保存+DraftRecovery :crit, a14g, after a14f, 3d
|
||||
防作弊采集+BroadcastChannel多标签检测 :a14h, after a14g, 2d
|
||||
作业提交页(submitHomework mutation+乐观更新) :a14i, after a14h, 2d
|
||||
我的成绩页+我的考勤页 :a14j, after a14i, 2d
|
||||
|
||||
section P4 知识与学情
|
||||
学习路径页(learningPath query+知识点卡片) :a14k, after a14j, 3d
|
||||
学情诊断页(myWeakness+myTrend query+图表) :a14l, after a14k, 3d
|
||||
教材章节浏览页(textbooks+chapters) :a14m, after a14l, 2d
|
||||
|
||||
section P5 推送与AI
|
||||
WebSocket通知中心(myNotifications+markAsRead) :a14n, after a14m, 2d
|
||||
跨Tab通知同步(BroadcastChannel) :a14o, after a14n, 1d
|
||||
AI辅助答疑(SSE流式,可选) :a14p, after a14o, 3d
|
||||
|
||||
section P6 硬化
|
||||
Sentry+RUM+OTel browser :a14q, after a14p, 2d
|
||||
A11y审计(WCAG 2.2 AA) :a14r, after a14q, 2d
|
||||
性能优化+bundle门禁(Remote <80KB) :a14s, after a14r, 2d
|
||||
P3未尽事项补全+降级策略验证 :a14t, after a14s, 2d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### 全阶段任务
|
||||
### P2:MF Remote 骨架预埋
|
||||
|
||||
- **负责人**:ai14
|
||||
- **交付物**:⚠️ 由 ai14 自行补充
|
||||
- **依赖**:见 [contracts/student-portal_contract.md](../contracts/student-portal_contract.md)
|
||||
- **验收标准**:⚠️ 由 ai14 自行补充
|
||||
- **裁决依据**:ARB-002 §2.3(P3 首个 Remote,但 P2 可预埋骨架)+ 总裁裁决 §3.6(ai14 P3 起步)+ §2.17(MF GraphQL client 单例方案 A)
|
||||
- **交付物**:
|
||||
- 项目骨架(`apps/student-portal/` 目录结构:`src/app/`、`src/components/`、`src/lib/`、`src/hooks/`、`src/mocks/`)
|
||||
- 设计令牌三层(primitive.css / semantic.css / tailwind-theme.ts,与 teacher-portal Shell 一致)
|
||||
- 独立壳路由(next.config.js + app/layout.tsx + app/page.tsx,MF 关闭时可独立渲染)
|
||||
- NextFederationPlugin 配置(`remotes: { teacher: 'teacher@http://localhost:4000/_next/static/chunks/remoteEntry.js' }`,`exposes: { './StudentApp': './src/app/student-app.tsx' }`)
|
||||
- MF shared singleton 配置(react/react-dom/urql/graphql/@tanstack/react-query/zustand/nuqs/@edu/* 全部 singleton)
|
||||
- MSW 基础设施(`src/mocks/handlers.ts` + `src/mocks/fixtures/*.json` + `NEXT_PUBLIC_API_MOCKING=enabled`)
|
||||
- GraphQL 请求层骨架(`src/lib/graphql.ts` 复用 Shell `useGraphQLClient()`,不重复创建 client)
|
||||
- **依赖**:
|
||||
- packages 骨架(ui-tokens/ui-components/hooks,ai13 批次 0.15 已完成)
|
||||
- teacher-portal MF Shell 配置(ai13 P2,exposes 就绪)
|
||||
- **Mock 策略**:MSW 拦截 `POST /api/v1/student/graphql` + `POST /api/auth/login`
|
||||
- **验收标准**:
|
||||
- `pnpm dev` 启动 :4001 可访问
|
||||
- MF 配置不破坏独立壳渲染(`NEXT_PUBLIC_MF_ENABLED=false` 时独立渲染首页)
|
||||
- MSW 拦截 GraphQL 请求返回 mock 数据
|
||||
- lint + typecheck 零错误
|
||||
|
||||
### P3:考试作答 + 作业提交 + 基础页面(核心)
|
||||
|
||||
- **负责人**:ai14
|
||||
- **裁决依据**:总裁裁决 §3.6(ai14 P3 功能范围)+ ARB-001 §1.3(ActionState 信封 + 降级模式方案 B)+ ARB-002 §2.2(复用 Shell 暴露清单)+ 02-architecture-design.md v2 §14(考试作答架构设计)
|
||||
- **交付物**:
|
||||
- **AppShell 复用 + 学生端导航**:从 Shell 导入 `AppShell`,覆写学生端视口(myClasses/myExams/myHomework/myGrades/myAttendance/learningPath/dashboard/notifications)
|
||||
- **路由守卫**:未登录跳转 `http://localhost:4000/login?redirect=student`,登录后回跳
|
||||
- **Dashboard 页**(`/dashboard`):消费 `studentDashboard` query(upcomingHomework + upcomingExams + recentGrades + attendanceRate + learningStreakDays)
|
||||
- **我的班级页**(`/my-classes`):消费 `myClasses` query
|
||||
- **我的考试列表页**(`/my-exams`):消费 `myExams` query,按状态分组(未开始/进行中/已提交/已批改)
|
||||
- **我的作业列表页**(`/my-homework`):消费 `myHomework` query,按状态分组
|
||||
- **考试作答页**(`/my-exams/[id]/take`):
|
||||
- 状态机(NotStarted → InProgress → AutoSaving → Submitting → Submitted,见 02 §14 状态机图)
|
||||
- 服务器时间同步(`useServerTimeSync` hook,5 分钟重新同步,倒计时基于服务器时间)
|
||||
- IDB 断网恢复队列(`idb-keyval` 存草稿 + 队列,网络恢复后重试)
|
||||
- 自动保存(每 30s + blur 事件触发,乐观更新本地状态)
|
||||
- DraftRecovery 草稿恢复(进入作答页时检查 IDB 草稿,提示恢复)
|
||||
- 防作弊采集(visibilitychange/copy/paste/fullscreen/contextmenu 事件监听 + 记录)
|
||||
- BroadcastChannel 多标签检测(`edu-exam-session` channel,检测到多标签警告)
|
||||
- 提交防重复(idempotency key + 提交按钮 disabled + 提交中状态)
|
||||
- **作业提交页**(`/my-homework/[id]/submit`):
|
||||
- 消费 `submitHomework` mutation
|
||||
- 乐观更新(useMutation onMutate 回滚 + invalidateQueries)
|
||||
- 附件上传(待 ISSUE-014-05 仲裁后实现,暂走 mock)
|
||||
- **我的成绩页**(`/my-grades`):消费 `myGrades` query,成绩列表 + 趋势图
|
||||
- **我的考勤页**(`/my-attendance`):消费 `myAttendance` query,考勤日历
|
||||
- **依赖**:
|
||||
- student-bff GraphQL schema(ai04 P3,`packages/shared-ts/contracts/graphql/student-bff.graphql`,待 ISSUE-014-02 仲裁)
|
||||
- api-gateway 路由(ai01 P3,`/api/v1/student/*` 反向代理 student-bff,待 ISSUE-014-01 仲裁)
|
||||
- core-edu gRPC(ai08 P3,提供 ExamService/HomeworkService/GradeService/AttendanceService/ClassService)
|
||||
- iam gRPC(ai06 P2,GetUserInfo + GetEffectivePermissions + GetViewports)
|
||||
- data-ana gRPC(ai11 P4,但 studentDashboard 聚合需要,P3 用 mock)
|
||||
- **Mock 策略**:
|
||||
- MSW 拦截 `POST /api/v1/student/graphql`,按 operationName 返回 mock 响应
|
||||
- mock-socket 模拟 WebSocket 推送(考试延长/强制提交事件)
|
||||
- IDB 草稿恢复用真实 idb-keyval(前端可独立测试)
|
||||
- **验收标准**:
|
||||
- Dashboard 页渲染(mock 数据):upcomingHomework + upcomingExams + recentGrades 三栏
|
||||
- 考试作答页状态机完整:进入 → 作答 → 自动保存 → 提交 → 跳转结果页
|
||||
- 断网恢复:手动 offline → 作答 → 恢复网络 → 草稿自动提交
|
||||
- 防作弊采集:visibilitychange hidden 触发记录(mock 上报)
|
||||
- 多标签检测:开第二个 Tab 作答,第一个 Tab 收到警告
|
||||
- 作业提交乐观更新:提交后立即 UI 反馈,失败回滚
|
||||
- lint + typecheck 零错误
|
||||
|
||||
### P4:知识图谱 + 学情诊断
|
||||
|
||||
- **负责人**:ai14
|
||||
- **交付物**:
|
||||
- **学习路径页**(`/learning-path`):消费 `learningPath` query,知识点卡片列表 + 掌握度进度条
|
||||
- **学情诊断页**(`/dashboard/weakness`):消费 `myWeakness` query,薄弱点雷达图(recharts)
|
||||
- **学习趋势页**(`/dashboard/trend`):消费 `myTrend` query,趋势折线图(recharts)
|
||||
- **教材章节浏览页**(`/textbooks`、`/textbooks/[id]/chapters`):消费 `textbooks` + `chapters` query
|
||||
- **依赖**:
|
||||
- student-bff 扩展 content + data-ana gRPC 调用(ai04 P4)
|
||||
- content gRPC(ai09 P4,TextbookService + ChapterService + KnowledgeGraphService)
|
||||
- data-ana gRPC(ai11 P4,AnalyticsService.GetStudentWeakness + GetLearningTrend)
|
||||
- **Mock 策略**:MSW 返回固定知识点 + 薄弱点 + 趋势数据
|
||||
- **验收标准**:
|
||||
- 学习路径页渲染知识点卡片 + 掌握度(mock)
|
||||
- 学情诊断页雷达图 + 趋势折线图渲染(mock)
|
||||
- 教材章节树形导航可用
|
||||
|
||||
### P5:实时通知 + AI 辅助
|
||||
|
||||
- **负责人**:ai14
|
||||
- **交付物**:
|
||||
- **WebSocket 通知中心**(`/notifications`):
|
||||
- 消费 `myNotifications` query + `markAsRead` mutation
|
||||
- WebSocket 连接 `ws://push-gateway:8081/ws`,实时接收通知
|
||||
- 通知分类(作业/考试/成绩/系统),未读计数
|
||||
- **跨 Tab 通知同步**(BroadcastChannel `edu-notification` channel,新通知在所有 Tab 同步)
|
||||
- **AI 辅助答疑**(可选,`/ai-tutor`):
|
||||
- SSE 流式接收 AI 回答
|
||||
- 消费 ai 服务(待 ai12 P5 就绪)
|
||||
- **依赖**:
|
||||
- push-gateway WebSocket(ai02 P5,`/ws` 端点)
|
||||
- msg gRPC(ai10 P5,NotificationService)
|
||||
- ai 服务 gRPC(ai12 P5,AiService.Chat,可选)
|
||||
- **Mock 策略**:mock-socket 模拟 WS 推送(每 30 秒 1 条通知)+ MSW 返回固定 AI 响应(SSE 用 ReadableStream mock)
|
||||
- **验收标准**:
|
||||
- 通知中心实时接收 WS 推送(mock)
|
||||
- 多 Tab 同步:Tab A 收到通知,Tab B 未读计数同步更新
|
||||
- AI 辅助答疑流式输出(mock)
|
||||
|
||||
### P6:可观测性硬化 + A11y + 性能
|
||||
|
||||
- **负责人**:ai14
|
||||
- **交付物**:
|
||||
- **Sentry 错误追踪**(`NEXT_PUBLIC_SENTRY_DSN` + beforeSend PII 过滤,学生隐私合规)
|
||||
- **Web Vitals RUM**(LCP/INP/CLS/TTFB → `/api/v1/admin/web-vitals`)
|
||||
- **OTel browser SDK**(自动埋点 fetch/XHR/document load → OTLP collector)
|
||||
- **A11y 审计**(WCAG 2.2 AA:eslint-plugin-jsx-a11y + @axe-core/playwright + 对比度审计)
|
||||
- **性能优化**(bundle analyzer + size-limit CI 门禁:Remote < 80KB / CSS < 50KB)
|
||||
- **P3 未尽事项补全**(根据 ISSUE-014-03/04/05/06/07 仲裁结果补全防作弊策略、附件上传、实时事件响应、DataScope 强制执行)
|
||||
- **降级策略验证**(02 §18.2 降级策略矩阵的 12 个场景端到端验证)
|
||||
- **依赖**:
|
||||
- push-gateway WebSocket 真实就绪(ai02 P5)
|
||||
- Sentry DSN + OTel collector(基础设施)
|
||||
- 全部上游就绪(统一集成测试)
|
||||
- **验收标准**:
|
||||
- 99.9% 可用 + WCAG 2.2 AA + LCP < 2.5s / INP < 200ms / CLS < 0.1(P75)
|
||||
- Remote bundle < 80KB / CSS < 50KB
|
||||
- 12 个降级场景全部验证通过
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:⚠️ 由 ai14 自行补充(见 contract.md)
|
||||
- **我的就绪信号**:⚠️ 由 ai14 自行补充
|
||||
### §4.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游 | 就绪标志 | 阻塞阶段 | 状态 |
|
||||
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -------- | ----------- |
|
||||
| packages 骨架(ai13 批次 0.15) | ui-tokens/ui-components/hooks 可 import | P2 启动 | ✅ 已就绪 |
|
||||
| teacher-portal MF Shell(ai13 P2) | exposes AppShell/GraphQLProvider/useGraphQLClient/useAuth/usePermission + shared singleton | P2 启动 | ⏳ 待 ai13 P2 |
|
||||
| api-gateway HTTP :8080(ai01 P3) | `/api/v1/student/*` 反向代理 student-bff 可用 | P3 启动 | ⏳ 待 ai01 P3 |
|
||||
| student-bff GraphQL(ai04 P3) | `POST /graphql` :3009 + 核心 Query/Mutation 可执行 | P3 启动 | ⏳ 待 ai04 P3 |
|
||||
| student-bff GraphQL schema(ai04 + ISSUE-014-02 仲裁) | `packages/shared-ts/contracts/graphql/student-bff.graphql` 创建 | P3 启动 | ⏳ 待仲裁 |
|
||||
| core-edu gRPC 50053(ai08 P3) | ExamService/HomeworkService/GradeService/AttendanceService/ClassService 全部 RPC | P3 启动 | ⏳ 待 ai08 P3 |
|
||||
| iam gRPC 50052(ai06 P2) | GetUserInfo + GetEffectivePermissions + GetViewports | P3 启动 | ⏳ 待 ai06 P2 |
|
||||
| student-bff content/data-ana 扩展(ai04 P4) | learningPath/myWeakness/myTrend/textbooks/chapters query 可用 | P4 启动 | ⏳ 待 ai04 P4 |
|
||||
| content gRPC 50054(ai09 P4) | TextbookService + ChapterService + KnowledgeGraphService | P4 启动 | ⏳ 待 ai09 P4 |
|
||||
| data-ana gRPC 50055(ai11 P4) | AnalyticsService.GetStudentWeakness + GetLearningTrend | P4 启动 | ⏳ 待 ai11 P4 |
|
||||
| push-gateway WebSocket :8081/ws(ai02 P5) | WS 连接可建立 + 推送可接收 | P5 启动 | ⏳ 待 ai02 P5 |
|
||||
| msg gRPC 50056(ai10 P5) | NotificationService.ListNotifications + MarkAsRead | P5 启动 | ⏳ 待 ai10 P5 |
|
||||
| ai 服务 gRPC 50057(ai12 P5,可选) | AiService.Chat(SSE 流式) | P5 启动 | ⏳ 待 ai12 P5 |
|
||||
| Sentry DSN + OTel collector(基础设施) | Sentry 项目创建 + OTel collector 可接收 OTLP | P6 启动 | ⏳ 待基础设施 |
|
||||
|
||||
### §4.2 我的就绪信号(供下游消费)
|
||||
|
||||
| 信号 | 就绪标志 | 消费方 |
|
||||
| ------------------------------- | --------------------------------------------------------- | ------ |
|
||||
| student-portal dev server :4001 | `pnpm dev` 启动 + 首页可访问 | 无(最前端,但 teacher-portal Shell 需加载 Remote) |
|
||||
| MF Remote 可加载 | teacher-portal Shell 可加载 `student-portal/StudentApp` | teacher-portal(ai13 P3 集成测试) |
|
||||
| 登录流程可用 | 未登录跳转 Shell `/login`,登录后回跳 student | 无 |
|
||||
| GraphQL 查询可执行 | currentUser/studentDashboard/myClasses 返回数据 | 无 |
|
||||
| 考试作答链路通 | 进入作答 → 自动保存 → 提交 → 跳转结果页 | 无 |
|
||||
| WebSocket 通知可接收 | 通知中心实时更新(mock) | 无 |
|
||||
|
||||
---
|
||||
|
||||
## §5 全并行开发说明
|
||||
|
||||
按 [matrix.md](../matrix.md) §全并行模式:
|
||||
|
||||
1. ai14 一口气完成 P2-P6 全部代码,开发期间用 MSW mock 上游
|
||||
2. 上游就绪后在 matrix.md §8 更新就绪信号(`student-portal | ai14 | :4001 可访问 + MF Remote | ⏳ → ✅`)
|
||||
3. 所有模块就绪后统一集成测试(matrix.md §9 检查清单)
|
||||
4. Mock 切换:`NEXT_PUBLIC_API_MOCKING=enabled` → `disabled`
|
||||
5. MF 切换:`NEXT_PUBLIC_MF_ENABLED=false`(P2 独立壳)→ `true`(P3 接入 Shell)
|
||||
|
||||
---
|
||||
|
||||
## §6 阶段能力累积矩阵(与 02-architecture-design.md v2 §20 对齐)
|
||||
|
||||
| 阶段 | 能力 | 关键页面/功能 |
|
||||
| ---- | --------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| P2 | 项目骨架 + MF Remote 配置 + MSW 基础设施 | 独立壳首页(占位) |
|
||||
| P3 | + 考试作答 + 作业提交 + 基础页面 | Dashboard / myClasses / myExams / myHomework / myGrades / myAttendance |
|
||||
| P4 | + 知识图谱 + 学情诊断 + 教材章节 | learningPath / myWeakness / myTrend / textbooks / chapters |
|
||||
| P5 | + 实时通知 + 跨 Tab 同步 + AI 辅助(可选) | notifications / ai-tutor |
|
||||
| P6 | + 可观测性 + A11y + 性能 + 降级验证 + 尽事项补全 | Sentry / RUM / OTel / WCAG 2.2 AA / bundle 门禁 |
|
||||
|
||||
---
|
||||
|
||||
## §7 关键风险与缓解
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
| ---------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- |
|
||||
| student-bff GraphQL schema 未就绪(ai04 P3 延迟) | 高 | MSW mock 全量 query/mutation,schema 就绪后切换;ai14 自行维护 mock schema 用于 codegen |
|
||||
| 考试作答断网恢复逻辑复杂(IDB 队列 + 服务器时间对齐)| 高 | 02 §14 已设计完整状态机 + 时间同步算法;P3 优先实现核心链路,P6 验证降级场景 |
|
||||
| MF Remote 加载失败(Shell 未就绪或版本不兼容) | 中 | 02 §3.2 已设计独立壳回退(`NEXT_PUBLIC_MF_ENABLED=false` 时独立渲染) |
|
||||
| 防作弊策略未仲裁(ISSUE-014-03/04) | 中 | P3 先实现采集 + 本地记录,P6 根据仲裁结果补全上报逻辑 |
|
||||
| 附件上传协议未仲裁(ISSUE-014-05) | 中 | P3 先实现文本作业提交,附件上传 P6 根据仲裁结果补全 |
|
||||
| 实时事件命名未确认(ISSUE-014-06) | 低 | P3 不依赖实时事件(考试作答页基于本地倒计时),P5 根据仲裁结果接入 WebSocket 实时事件 |
|
||||
|
||||
---
|
||||
|
||||
**AI Agent**: ai14(student-portal)
|
||||
**Branch**: feat-review-student-portal-docs-9yN6Av
|
||||
**Coordinator**: coord-ai
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
# teacher-bff 工作排期
|
||||
|
||||
> 负责人:ai03
|
||||
> 关联:[workline.md](../workline.md)、[coord.md §1 ARB-001](../coord.md)、[contracts/teacher-bff_contract.md](../contracts/teacher-bff_contract.md)
|
||||
> 关联:[workline.md](../workline.md)、[coord.md §1 ARB-001](../coord.md)、[contracts/teacher-bff_contract.md](../contracts/teacher-bff_contract.md)、[president-final-rulings.md §3.1/§7.3](../../president-final-rulings.md)、[coord-final-decisions.md §2 B1-B8](../../coord-final-decisions.md)
|
||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||
> 裁决依据:B1 P2 即 GraphQL / B2 首次实现即 gRPC / B3 豁免 @RequirePermission / B4 越权防御 / B5 BFF_TEACHER_ 前缀 / B6 Redis 短缓存 / B7 P2-P4 不订阅 Kafka / B8 DownstreamClient 抽象
|
||||
|
||||
---
|
||||
|
||||
## §1 总览
|
||||
|
||||
teacher-bff 是教学场景域聚合层,全阶段目标:P2 GraphQL schema 第一版 → P3 扩展 exams/homework/grades → P4 学情分析 → P5 通知+SSE → P6 admin 命名空间。
|
||||
teacher-bff 是教学场景域聚合层(BFF),全阶段目标:
|
||||
|
||||
| 阶段 | 核心交付 | 裁决依据 |
|
||||
| ---- | -------- | -------- |
|
||||
| P2 | GraphQL schema 第一版(5 Query + admin 预留)+ DownstreamClient 抽象 + iam gRPC + AuthorizationGuard + ActionState 信封 | B1/B2/B3/B4/B5/B8 + ARB-001 + §3.1 |
|
||||
| P3 | core-edu gRPC 扩展(exams/homework/grades Query + Mutation)+ AuthorizationGuard 接入 core-edu + Redis 聚合缓存 | §2.3 跨阶段扩展例外 |
|
||||
| P4 | content + data-ana gRPC 扩展(学情分析 Query)+ DataLoader 全量接入 | §2.3 + §2.8 |
|
||||
| P5 | ai + msg gRPC 扩展(SSE 流式 + notifications Query)+ Kafka consumer(push-gateway 落地后) | B7 |
|
||||
| P6 | admin 命名空间实现 + 硬化(熔断/重试/超时/HPA/mTLS) | §5.1 + P6 硬化 |
|
||||
|
||||
---
|
||||
|
||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
||||
## §2 全阶段甘特图(P2-P6)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
@@ -20,39 +29,307 @@ gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
|
||||
section P2 GraphQL 基础
|
||||
schema第一版(ARB-001) :crit, a3a, 2026-07-10, 2d
|
||||
section P2 GraphQL 基础(8d)
|
||||
schema第一版+admin预留(ARB-001) :crit, a3a, 2026-07-10, 2d
|
||||
Yoga endpoint+5 Query Resolver :crit, a3b, after a3a, 3d
|
||||
DataLoader+N+1防御 :a3c, after a3b, 1d
|
||||
ActionState信封+降级模式B :a3d, after a3b, 1d
|
||||
DownstreamClient+iam gRPC :crit, a3c, after a3a, 2d
|
||||
AuthorizationGuard越权防御(B4) :crit, a3d, after a3b, 1d
|
||||
ActionState信封+降级模式B :a3e, after a3b, 1d
|
||||
/readyz探针注册表+iam探针 :a3f, after a3d, 1d
|
||||
错误码迁移BFF_TEACHER_+回写02 :a3g, after a3e, 1d
|
||||
|
||||
section P3-P6 扩展
|
||||
exams/homework/grades Query :a3e, after a3d, 3d
|
||||
学情分析+通知+SSE :a3f, after a3e, 4d
|
||||
admin命名空间 :a3g, after a3f, 2d
|
||||
section P3 core-edu 扩展(5d)
|
||||
CoreEduClient gRPC(exams/homework/grades) :crit, a3h, after a3g, 2d
|
||||
Mutation(createExam/assignHomework/recordGrade) :a3i, after a3h, 1d
|
||||
AuthorizationGuard接入core-edu+Redis缓存 :a3j, after a3h, 1d
|
||||
/readyz+core-edu探针 :a3k, after a3j, 1d
|
||||
|
||||
section P4 content+data-ana 扩展(4d)
|
||||
ContentClient+DataAnaClient gRPC :a3l, after a3k, 2d
|
||||
学情分析Query+DataLoader全量 :a3m, after a3l, 1d
|
||||
/readyz+content+data-ana探针 :a3n, after a3m, 1d
|
||||
|
||||
section P5 ai+msg 扩展(8d)
|
||||
AiClient+MsgClient gRPC :a3o, after a3n, 2d
|
||||
SSE流式透传+notifications Query :a3p, after a3o, 2d
|
||||
Kafka consumer(push-gateway落地后) :a3q, after a3p, 2d
|
||||
/readyz+ai+msg探针 :a3r, after a3q, 1d
|
||||
Should Have补全(OTel/metrics/DataLoader/Redis) :a3s, after a3r, 1d
|
||||
|
||||
section P6 admin+硬化(3d)
|
||||
admin命名空间实现 :a3t, after a3s, 1d
|
||||
熔断/重试/超时 :a3u, after a3t, 1d
|
||||
Nice to Have补全(Zod全量/优雅关闭) :a3v, after a3u, 1d
|
||||
```
|
||||
|
||||
> **注意**:以上为 coord 初始规划,ai03 接管后必须自行细化为完整 P2-P6 排期。
|
||||
> **总工期**:28d(P2 8d + P3 5d + P4 4d + P5 8d + P6 3d),与 workline.md §1 总时间线对齐(批次1 8d + 批次2 5d + 批次4 8d + P4/P6 合并 7d)。
|
||||
|
||||
---
|
||||
|
||||
## §3 详细任务
|
||||
|
||||
### P2:GraphQL schema + 5 Query + DataLoader + ActionState
|
||||
### P2:GraphQL 基础 + DownstreamClient + 越权防御(批次 1,8d)
|
||||
|
||||
> 裁决依据:president §3.1 Must Have 13 项(teacher-bff 无 DB,G10/G11 不适用,实际 11 项 + DownstreamClient + admin 预留)
|
||||
|
||||
#### 3.1 GraphQL schema 第一版 + admin 预留(2d,P0 阻塞 ai13)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:coord 仲裁 ARB-001(已裁决)
|
||||
- **交付物**:
|
||||
- `packages/shared-ts/contracts/graphql/teacher-bff.graphql` — P2 schema
|
||||
- Yoga endpoint `POST /graphql`
|
||||
- 5 Query:dashboard / viewports / me / classes / class
|
||||
- DataLoader + ActionState 信封 + 降级模式 B
|
||||
- **依赖**:iam gRPC(ai06)+ coord 仲裁 ARB-001
|
||||
- **验收标准**:5 Query 可用 + ActionState 信封 + depth ≤ 7
|
||||
- **完整 P3-P6 任务**:⚠️ 由 ai03 自行补充
|
||||
- `packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql` — P2 schema(5 Query: dashboard/viewports/me/classes/class + admin 命名空间占位)
|
||||
- admin 命名空间预留:schema 中声明 `admin` Query/Mutation 类型骨架(无实际 Resolver),P6 实现
|
||||
- **验收标准**:5 Query SDL 定义完整 + admin 占位类型声明 + depth ≤ 7 + cost ≤ 1000
|
||||
- **裁决引用**:ARB-001 §1.2/§1.3 + president §5.1/§2.17
|
||||
|
||||
#### 3.2 Yoga endpoint + 5 Query Resolver(3d,P0 阻塞 ai13)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:3.1 schema + iam gRPC 50052(ai06 P2.1)
|
||||
- **交付物**:
|
||||
- `POST /graphql` Yoga GraphQL endpoint
|
||||
- 5 Query Resolver:dashboard / viewports / me / classes / class
|
||||
- Dashboard Resolver P2 实现方式(ISSUE-032):P2 仅调 iam gRPC,未启用字段返回 null + `extensions.warning = "field_unavailable_in_p2"`
|
||||
- **验收标准**:5 Query 可执行 + ActionState 信封 + 降级模式 B(success=true + error=null + data 内 degraded 字段)
|
||||
- **裁决引用**:B1 + ARB-001 §1.4 + president §2.6/§2.8
|
||||
|
||||
#### 3.3 DownstreamClient 抽象 + iam gRPC(2d,P0 阻塞 ai04/ai05)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:iam gRPC 50052(ai06 P2.1)
|
||||
- **交付物**:
|
||||
- `src/clients/` DownstreamClient 抽象层(B8:BFF 模式 v2 标准抽象,3 个 BFF 统一使用,回写 teacher-bff)
|
||||
- IamClient gRPC 实现:`@grpc/grpc-js` + `@bufbuild/protobuf`,调 iam:50052
|
||||
- gRPC interceptor:注入 trace context(traceparent)+ x-user-id metadata + metrics
|
||||
- **验收标准**:IamClient gRPC 调 iam GetUserInfo/GetViewports/GetEffectiveAccess 成功
|
||||
- **裁决引用**:B2(首次实现即 gRPC)+ B8(DownstreamClient 抽象)
|
||||
|
||||
#### 3.4 AuthorizationGuard 越权防御(1d,P0)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:3.2 Resolver
|
||||
- **交付物**:
|
||||
- `src/middleware/authorization.guard.ts` — AuthorizationGuard 接口(`canAccessClass(userId, classId): Promise<boolean>`)
|
||||
- P2 内部实现:DEV_MODE 放行 + 生产拒绝(保守策略)
|
||||
- 错误码:`BFF_TEACHER_FORBIDDEN_RESOURCE`(teacherId 与资源无归属)+ `BFF_TEACHER_IDENTITY_MISMATCH`(JWT teacherId 与 body 不一致)
|
||||
- **验收标准**:Guard 接口定义 + DEV_MODE 放行 + 生产拒绝 + 2 个越权错误码
|
||||
- **裁决引用**:B4 + president §2.7(错误码语义)+ §2.9(越权防御 P2 实现)
|
||||
|
||||
#### 3.5 ActionState 信封 + 降级模式 B(1d)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:3.2 Resolver
|
||||
- **交付物**:GlobalErrorFilter + ActionState 信封(success/errors/data)+ 降级模式 B
|
||||
- **验收标准**:GraphQL errors 数组扩展 ActionState 字段,`extensions.code = BFF_TEACHER_*`
|
||||
- **裁决引用**:G8 + president §2.6
|
||||
|
||||
#### 3.6 /readyz 探针注册表 + iam 探针(1d)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:3.3 IamClient
|
||||
- **交付物**:
|
||||
- `src/shared/health/readiness.probe.ts` — DownstreamHealthCheck 注册表模式
|
||||
- P2 探针:Redis + iam gRPC 50052(teacher-bff 无 DB,2 项)
|
||||
- **验收标准**:/readyz 返回 2 项检查结果 + 必需依赖失败返回 503
|
||||
- **裁决引用**:G2 + president §2.4(探针按阶段扩展)
|
||||
|
||||
#### 3.7 错误码迁移 + 回写 02 文档(1d)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:3.2-3.6
|
||||
- **交付物**:
|
||||
- `application-error.ts` 全量迁移 `TEACHER_BFF_*` → `BFF_TEACHER_*`
|
||||
- 回写 02 文档:4 处 `GetTeacherDashboardStats` → `GetTeacherDashboard`(ISSUE-035)+ B1/B2/B4/B8 裁决对齐
|
||||
- **验收标准**:源码零 `TEACHER_BFF_*` + 02 文档与裁决一致
|
||||
- **裁决引用**:B5 + G14 + president §3.4 回写义务
|
||||
|
||||
#### P2 横切项(贯穿 3.1-3.7)
|
||||
|
||||
| 项 | 状态 | 说明 |
|
||||
| -- | ---- | ---- |
|
||||
| pino 结构化日志(G4) | ✅ 已具备 | logger.ts |
|
||||
| /healthz liveness(G3) | ✅ 已具备 | health.controller.ts |
|
||||
| GlobalErrorFilter(G8) | ✅ 已具备 | global-error.filter.ts |
|
||||
| ESM import .js 后缀(G12) | ✅ 已具备 | 源码已用 .js |
|
||||
| import type(G13) | ✅ 已具备 | 源码已用 import type |
|
||||
| OTel tracer(G6) | ⚠️ Should Have | P2 可降级为 logger-only,P5 补全 |
|
||||
| /metrics 业务指标(G5) | ⚠️ Should Have | P2 仅暴露 process metrics |
|
||||
| DataLoader(B1) | ⚠️ Should Have | P2 可先用普通 resolver,P4 全量接入 |
|
||||
| Redis 5-30s 短缓存(B6) | ⚠️ Should Have | P3 接入聚合缓存 |
|
||||
| Zod 全量验证(G7) | ⚠️ Nice to Have | P2 先校验核心 Query,P6 全量 |
|
||||
| 优雅关闭 SIGTERM(G9) | ⚠️ Nice to Have | P2 已有基础,P6 补全关闭顺序 |
|
||||
|
||||
**P2 退出标准**:POST /graphql 可用 + 5 Query Resolver + DownstreamClient + AuthorizationGuard + ActionState 信封 + /readyz 2 项探针 + 错误码 BFF_TEACHER_* + admin 命名空间预留。
|
||||
|
||||
---
|
||||
|
||||
### P3:core-edu gRPC 扩展(批次 2,5d)
|
||||
|
||||
> 裁决依据:§2.3 跨阶段扩展例外(新增下游 gRPC 调用 + AuthorizationGuard 内部实现替换 + /readyz 探针扩展)
|
||||
|
||||
#### 3.8 CoreEduClient gRPC(2d,P0)
|
||||
|
||||
- **负责人**:ai03
|
||||
- **依赖**:core-edu gRPC 50053(ai08 P3)
|
||||
- **交付物**:
|
||||
- CoreEduClient gRPC 实现:ExamService / HomeworkService / GradeService
|
||||
- GraphQL Query 扩展:exams(classId) / homework(classId) / grades(examId)
|
||||
- Dashboard Resolver 扩展:null 字段替换为 core-edu 真实数据
|
||||
- **验收标准**:3 个 Query 返回 core-edu 数据 + Dashboard null 字段消除
|
||||
- **裁决引用**:§2.3 跨阶段扩展例外 + §2.8 Dashboard Resolver 扩展
|
||||
|
||||
#### 3.9 Mutation 透传(1d)
|
||||
|
||||
- **交付物**:createExam / assignHomework / recordGrade Mutation(透传 core-edu gRPC)
|
||||
- **验收标准**:3 个 Mutation 可执行 + 返回 ActionState 信封
|
||||
|
||||
#### 3.10 AuthorizationGuard 接入 core-edu + Redis 缓存(1d)
|
||||
|
||||
- **交付物**:
|
||||
- AuthorizationGuard 内部实现替换:DEV_MODE 放行 → 真实 gRPC 校验
|
||||
- Redis 缓存:`GetClassesByTeacher` 结果缓存(key: `authz:teacher:{teacherId}:classes`,TTL 5min)
|
||||
- **验收标准**:生产环境越权防御生效 + Redis 缓存命中
|
||||
- **裁决引用**:president §2.9(P3 接入 core-edu 后替换 Guard 实现)
|
||||
|
||||
#### 3.11 /readyz + core-edu 探针(1d)
|
||||
|
||||
- **交付物**:/readyz 探针注册表扩展 core-edu gRPC 50053 探针(3 项:Redis + iam + core-edu)
|
||||
- **验收标准**:/readyz 返回 3 项检查结果
|
||||
|
||||
**P3 退出标准**:core-edu gRPC 3 Query + 3 Mutation + AuthorizationGuard 生产生效 + Redis 缓存 + /readyz 3 项探针。
|
||||
|
||||
---
|
||||
|
||||
### P4:content + data-ana gRPC 扩展(4d)
|
||||
|
||||
> 裁决依据:§2.3 跨阶段扩展例外
|
||||
|
||||
#### 3.12 ContentClient + DataAnaClient gRPC(2d)
|
||||
|
||||
- **依赖**:content gRPC 50054(ai09 P4)+ data-ana gRPC 50055(ai11 P4)
|
||||
- **交付物**:
|
||||
- ContentClient gRPC:KnowledgeGraphService(GetPrerequisites / GetLearningPath)
|
||||
- DataAnaClient gRPC:AnalyticsService(GetClassPerformance / GetStudentWeakness / GetLearningTrend / GetTeacherDashboard)
|
||||
- GraphQL Query 扩展:knowledgePath / classPerformance / studentWeakness / learningTrend / teacherDashboard
|
||||
- **验收标准**:5 个 Query 返回真实数据
|
||||
|
||||
#### 3.13 DataLoader 全量接入(1d)
|
||||
|
||||
- **交付物**:DataLoader 覆盖全部 N+1 风险点(UserLoader / ClassLoader / ExamLoader / HomeworkLoader / GradeLoader)
|
||||
- **验收标准**:DataLoader per-request 实例 + 批量化窗口 16ms
|
||||
- **裁决引用**:B1 Should Have → P4 全量接入
|
||||
|
||||
#### 3.14 /readyz + content + data-ana 探针(1d)
|
||||
|
||||
- **交付物**:/readyz 探针扩展 content + data-ana(5 项:Redis + iam + core-edu + content + data-ana)
|
||||
|
||||
**P4 退出标准**:content + data-ana gRPC + 5 Query + DataLoader 全量 + /readyz 5 项探针。
|
||||
|
||||
---
|
||||
|
||||
### P5:ai + msg gRPC 扩展 + SSE + Kafka(批次 4,8d)
|
||||
|
||||
> 裁决依据:B7(P5 push-gateway 落地后再订阅 Kafka)
|
||||
|
||||
#### 3.15 AiClient + MsgClient gRPC(2d)
|
||||
|
||||
- **依赖**:ai gRPC 50057(ai12 P5)+ msg gRPC 50056(ai10 P5)
|
||||
- **交付物**:
|
||||
- AiClient gRPC:AiService(Chat / StreamChat streaming / GenerateQuestion / OptimizeExpression)
|
||||
- MsgClient gRPC:NotificationService(ListNotifications / SearchNotifications / MarkAsRead)
|
||||
- GraphQL Query 扩展:notifications + Mutation:generateQuestion / markNotificationAsRead
|
||||
|
||||
#### 3.16 SSE 流式透传(2d)
|
||||
|
||||
- **交付物**:`GET /ai/chat/stream` SSE 端点(ai.StreamChat gRPC stream → BFF → 前端 EventSource)
|
||||
- **验收标准**:SSE 三层透传端到端通 + 背压处理 + 超时取消
|
||||
- **裁决引用**:02 文档 §10
|
||||
|
||||
#### 3.17 Kafka consumer(2d,push-gateway 落地后)
|
||||
|
||||
- **交付物**:
|
||||
- Kafka consumer 订阅 `edu.identity.user.role_changed` / `edu.identity.role.updated`,精确失效 Redis 权限缓存
|
||||
- 幂等性:基于 event_id 去重(Redis SETNX)
|
||||
- **验收标准**:权限变更秒级缓存失效 + 幂等消费
|
||||
- **裁决引用**:B7(P5 push-gateway 落地后再订阅)
|
||||
|
||||
#### 3.18 /readyz + ai + msg 探针 + Should Have 补全(2d)
|
||||
|
||||
- **交付物**:
|
||||
- /readyz 探针扩展 ai + msg(7 项:Redis + iam + core-edu + content + data-ana + ai + msg)
|
||||
- Should Have 补全:OTel tracer 全链路 + /metrics 业务指标 + Redis 聚合缓存 5-30s
|
||||
- **验收标准**:/readyz 7 项 + OTel 全链路 trace + 缓存命中率 ≥ 60%
|
||||
|
||||
**P5 退出标准**:ai + msg gRPC + SSE 流式 + notifications Query + Kafka consumer + /readyz 7 项探针 + Should Have 补全。
|
||||
|
||||
---
|
||||
|
||||
### P6:admin 命名空间 + 硬化(3d)
|
||||
|
||||
> 裁决依据:president §5.1(admin-portal 复用 teacher-bff)+ P6 硬化
|
||||
|
||||
#### 3.19 admin 命名空间实现(1d)
|
||||
|
||||
- **依赖**:admin-portal(ai16 P6)
|
||||
- **交付物**:
|
||||
- admin schema 命名空间 Resolver 实现(P2 预留的占位类型填充实际 Resolver)
|
||||
- admin Query/Mutation:用户管理 / 角色权限管理 / 学校设置 / 组织管理 / 审计日志查询
|
||||
- **验收标准**:admin namespace 可内省 + admin-portal 可消费
|
||||
- **裁决引用**:president §5.1(admin-portal 复用 teacher-bff GraphQL endpoint)
|
||||
|
||||
#### 3.20 熔断 / 重试 / 超时(1d)
|
||||
|
||||
- **交付物**:
|
||||
- Circuit Breaker(opossum,per-downstream-service)
|
||||
- Retry(gRPC interceptor,仅幂等 RPC,指数退避)
|
||||
- Timeout(per-RPC 3s,聚合总超时 5s)
|
||||
- **验收标准**:熔断/重试/超时生效 + 降级策略覆盖
|
||||
|
||||
#### 3.21 Nice to Have 补全(1d)
|
||||
|
||||
- **交付物**:Zod 全量验证 + 优雅关闭顺序(HTTP→Redis→gRPC→Kafka→Tracer)+ 测试覆盖率 ≥ 80%
|
||||
- **验收标准**:Zod 全 Controller 覆盖 + SIGTERM 顺序关闭 + Vitest 覆盖率 ≥ 80%
|
||||
|
||||
**P6 退出标准**:admin namespace 实现 + 熔断/重试/超时 + Zod 全量 + 测试 ≥ 80% + SLO 99.9%。
|
||||
|
||||
---
|
||||
|
||||
## §4 依赖与就绪信号
|
||||
|
||||
- **我依赖**:iam gRPC 50052(ai06)+ core-edu gRPC 50053(ai08,P3+)
|
||||
- **我的就绪信号**:POST /graphql 可用 + dashboard Query 返回正确数据
|
||||
### 4.1 我依赖的上游就绪信号
|
||||
|
||||
| 上游 | 就绪信号 | 阶段 | 状态 |
|
||||
| ---- | -------- | ---- | ---- |
|
||||
| iam(ai06) | gRPC 50052 + 8 RPC(GetUserInfo/GetViewports/GetEffectivePermissions/GetEffectiveAccess/Logout/GetPublicKey/BatchGetUsers/GetChildrenByParent) | P2 | ⏳ |
|
||||
| core-edu(ai08) | gRPC 50053 + ExamService/HomeworkService/GradeService | P3 | ⏳ |
|
||||
| content(ai09) | gRPC 50054 + KnowledgeGraphService | P4 | ⏳ |
|
||||
| data-ana(ai11) | gRPC 50055 + AnalyticsService(含 GetTeacherDashboard,ISSUE-027 补全) | P4 | ⏳ |
|
||||
| ai(ai12) | gRPC 50057 + AiService(含 StreamChat) | P5 | ⏳ |
|
||||
| msg(ai10) | gRPC 50056 + NotificationService | P5 | ⏳ |
|
||||
| push-gateway(ai02) | /internal/push 落地(B7 Kafka 订阅前提) | P5 | ⏳ |
|
||||
| admin-portal(ai16) | admin schema 需求确认 | P6 | ⏳ |
|
||||
|
||||
### 4.2 我的就绪信号(供下游消费)
|
||||
|
||||
| 阶段 | 就绪信号 | 消费方 |
|
||||
| ---- | -------- | ------ |
|
||||
| P2 | POST /graphql 可用 + 5 Query + admin 预留 | teacher-portal(ai13) |
|
||||
| P2 | DownstreamClient 抽象(B8 回写) | student-bff(ai04)/ parent-bff(ai05) |
|
||||
| P3 | exams/homework/grades Query + Mutation | teacher-portal(ai13) |
|
||||
| P4 | 学情分析 Query + DataLoader | teacher-portal(ai13) |
|
||||
| P5 | SSE 流式 + notifications Query | teacher-portal(ai13) |
|
||||
| P6 | admin namespace 可用 | admin-portal(ai16) |
|
||||
|
||||
---
|
||||
|
||||
## §5 跨阶段扩展例外验收清单
|
||||
|
||||
> 裁决依据:president §2.3(ISSUE-020)。每次跨阶段扩展时对照检查。
|
||||
|
||||
- [ ] 扩展时更新 02-architecture-design.md 下游调用矩阵
|
||||
- [ ] 扩展时更新 packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql
|
||||
- [ ] 扩展时运行 `pnpm run arch:scan` 更新 arch.db
|
||||
- [ ] 未修改已有 RPC 调用签名或返回类型
|
||||
- [ ] 未删除已实现的 RPC 调用
|
||||
- [ ] 未修改 GraphQL schema 已有字段类型(仅新增字段)
|
||||
- [ ] 未修改 /readyz 已有探针检查项(仅新增)
|
||||
|
||||
@@ -74,7 +74,7 @@ gantt
|
||||
- 学生列表页(classStudents GraphQL query,iam 数据)
|
||||
- 个人设置页(currentUser + updateUser GraphQL query/mutation)
|
||||
- **依赖**:teacher-bff GraphQL schema 第一版(ai03 + coord 仲裁 ISSUE-037)+ api-gateway 路由(ai01)
|
||||
- **Mock 策略**:MSW 拦截 POST /api/teacher/graphql + POST /api/auth/login(见 contract.md §4.2)
|
||||
- **Mock 策略**:MSW 拦截 POST /api/v1/teacher/graphql + POST /api/auth/login(见 contract.md §4.2,对齐 matrix.md §5 路径前缀)
|
||||
- **验收标准**:MF 配置不破坏单体 + GraphQL 拉取数据 + 登录→Dashboard→班级列表→学生列表链路通
|
||||
|
||||
### P3:考试/作业/成绩 + 乐观更新 + 多 Tab 同步
|
||||
@@ -135,7 +135,7 @@ gantt
|
||||
| --------------------------------------------------------- | ------------------------------------------------- | -------- | ---------------------- |
|
||||
| packages 骨架(ai13 自建) | ui-tokens/ui-components/hooks 可 import | P2 启动 | ✅ 已就绪(批次 0.15) |
|
||||
| teacher-bff GraphQL schema(ai03 + coord 仲裁 ISSUE-037) | packages/shared-ts/contracts/graphql/ 第一版 | P2 启动 | ⏳ 待 coord 仲裁 |
|
||||
| api-gateway HTTP :8080(ai01) | /api/teacher/graphql + /api/auth/login 路由可用 | P2 启动 | ⏳ 待 ai01 |
|
||||
| api-gateway HTTP :8080(ai01) | /api/v1/teacher/graphql + /api/auth/login 路由可用 | P2 启动 | ⏳ 待 ai01 |
|
||||
| teacher-bff core-edu 扩展(ai03 P3) | classExams/classHomework/studentGrades query 可用 | P3 启动 | ⏳ 待 ai03 P3 |
|
||||
| teacher-bff content/data-ana 扩展(ai03 P4) | knowledgeGraph/studentAnalytics query 可用 | P4 启动 | ⏳ 待 ai03 P4 |
|
||||
| push-gateway WebSocket :8081/ws(ai02 P5) | WS 连接可建立 + 推送可接收 | P5 启动 | ⏳ 待 ai02 P5 |
|
||||
|
||||
Reference in New Issue
Block a user