feat: auto committed
This commit is contained in:
@@ -1,48 +1,98 @@
|
||||
# parent-bff 对接契约
|
||||
|
||||
> 负责人:ai05
|
||||
> 关联:[matrix.md](./matrix.md)、[iam.proto](../../../packages/shared-proto/proto/iam.proto)、[core_edu.proto](../../../packages/shared-proto/proto/core_edu.proto)、[analytics.proto](../../../packages/shared-proto/proto/analytics.proto)、[msg.proto](../../../packages/shared-proto/proto/msg.proto)
|
||||
> 关联:[matrix.md](../matrix.md)、[iam.proto](../../../packages/shared-proto/proto/iam.proto)、[core_edu.proto](../../../packages/shared-proto/proto/core_edu.proto)、[analytics.proto](../../../packages/shared-proto/proto/analytics.proto)、[msg.proto](../../../packages/shared-proto/proto/msg.proto)、[02-architecture-design.md](../../../services/parent-bff/docs/02-architecture-design.md)
|
||||
> 修订:2026-07-10 ai05 复审(修正仲裁引用、proto 包名、契约缺口标注)
|
||||
|
||||
---
|
||||
|
||||
## §0 契约基线说明
|
||||
|
||||
| 维度 | 内容 |
|
||||
| --- | --- |
|
||||
| proto 包名前缀 | 所有 proto 包名统一为 `next_edu_cloud.<domain>.v1`(如 `next_edu_cloud.iam.v1`),引用 proto message 时须带完整包名 |
|
||||
| GraphQL schema 文件 | `packages/shared-ts/contracts/graphql/parent-bff.graphql`(SDL-first 集中管理,对齐 coord ARB-001 模式) |
|
||||
| 错误码前缀 | `BFF_PARENT_`(C1 仲裁,BFF 在前) |
|
||||
| 端口 | HTTP 3010,不暴露 gRPC(C2 仲裁) |
|
||||
| API 风格 | GraphQL Yoga(U3 仲裁,P4 直接 GraphQL,不走 REST 过渡) |
|
||||
| 权限校验 | BFF 豁免 @RequirePermission(U4 仲裁),仅校验 x-user-id + ChildGuard 越权防御 |
|
||||
|
||||
---
|
||||
|
||||
## §1 我提供什么(对外接口)
|
||||
|
||||
### 1.1 gRPC 接口(如有)
|
||||
### 1.1 gRPC 接口
|
||||
|
||||
无对外 gRPC。parent-bff 是 GraphQL 聚合层。
|
||||
无对外 gRPC。parent-bff 是 GraphQL 聚合层,对上游仅暴露 HTTP/GraphQL(C2 仲裁)。
|
||||
|
||||
### 1.2 HTTP 端点(如有)
|
||||
### 1.2 HTTP 端点
|
||||
|
||||
| Method | Path | 用途 | 认证 |
|
||||
| ------ | -------- | ----------------------------------------- | ---------------------- |
|
||||
| POST | /graphql | 家长 BFF GraphQL 端点 | JWT 必需 + parent 角色 |
|
||||
| GET | /graphql | GraphQL Playground(开发环境) | 开发环境公开 |
|
||||
| GET | /healthz | 健康检查(liveness) | 公开 |
|
||||
| GET | /readyz | 就绪检查(readiness,含下游 gRPC 连通性) | 公开 |
|
||||
| Method | Path | 用途 | 认证 | 阶段 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| POST | /graphql | 家长 BFF GraphQL 端点(U3 仲裁) | JWT 必需 + parent 角色 | P4 |
|
||||
| GET | /graphql | GraphQL Playground(仅开发环境) | 开发环境公开 | P4 |
|
||||
| GET | /healthz | 健康检查(liveness,直接返回 ok) | 公开 | P4 |
|
||||
| GET | /readyz | 就绪检查(readiness,含下游 gRPC 连通性,02 §9 #7) | 公开 | P4 |
|
||||
| GET | /metrics | Prometheus 指标(parent_bff_*) | 公开 | P4 |
|
||||
|
||||
### 1.3 GraphQL schema(如 BFF)
|
||||
> 网关路径:`/api/v1/parent/*` → api-gateway 剥离 `/api/v1` 后代理到 parent-bff:3010
|
||||
> **不实现 REST 业务端点**(02 §4.1,仅保留 /healthz /readyz /metrics 基础端点)
|
||||
|
||||
GraphQL schema 文件路径:`apps/parent-bff/src/schema/*.graphql`(端口 :3010)
|
||||
### 1.3 GraphQL schema
|
||||
|
||||
核心 Query / Mutation 域:
|
||||
**schema 文件**:`packages/shared-ts/contracts/graphql/parent-bff.graphql`(SDL-first)
|
||||
**完整 schema 定义**:见 [02-architecture-design.md §4.2](../../../services/parent-bff/docs/02-architecture-design.md) GraphQL Schema 完整定义
|
||||
|
||||
- **auth**:currentUser(聚合 iam.GetUserInfo + GetEffectivePermissions + GetViewports)
|
||||
- **children**:myChildren(聚合 iam.GetChildrenByParent,核心依赖 I3 裁决)
|
||||
- **childSummary**:childSummary(聚合 data-ana.AnalyticsService.GetParentDashboard)
|
||||
- **childGrades**:childGrades(聚合 core-edu.GradeService.ListGradesByStudent)
|
||||
- **childAttendance**:childAttendance(聚合 core-edu.AttendanceService.ListAttendanceByStudent)
|
||||
- **childHomework**:childHomework(聚合 core-edu.HomeworkService.ListHomeworkByClass)
|
||||
- **childWeakness**:childWeakness(聚合 data-ana.AnalyticsService.GetStudentWeakness)
|
||||
- **childTrend**:childTrend(聚合 data-ana.AnalyticsService.GetLearningTrend)
|
||||
- **notifications**:myNotifications / markAsRead(聚合 msg.NotificationService)
|
||||
核心 Query / Mutation 域(按阶段分级):
|
||||
|
||||
### 1.4 Kafka 事件发布(如有)
|
||||
| 类型 | 字段 | 聚合下游 | ChildGuard | 阶段 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Query | dashboard | iam + core-edu | 否(聚合所有孩子) | P4 |
|
||||
| Query | viewports | iam | 否 | P4 |
|
||||
| Query | me | iam | 否 | P4 |
|
||||
| Query | children | iam | 否 | P4 |
|
||||
| Query | child | iam + core-edu | 是 | P4 |
|
||||
| Query | childGrades | core-edu | 是 | P4 |
|
||||
| Query | childHomework | core-edu | 是 | P4 |
|
||||
| Query | childExams | core-edu | 是 | P4 |
|
||||
| Query | childAnalytics | data-ana | 是 | P4 |
|
||||
| Query | notifications | msg | 否 | P5 |
|
||||
| Query | notificationPreferences | msg | 否 | P5 |
|
||||
| Mutation | selectChild | (BFF 内部审计) | 是 | P4 |
|
||||
| Mutation | markNotificationRead | msg | 否 | P5 |
|
||||
| Mutation | updateNotificationPreferences | msg | 否 | P5 |
|
||||
|
||||
无。parent-bff 不发布事件,仅做 gRPC 聚合。
|
||||
**统一响应信封**:GraphQL 规范(data/errors),错误扩展字段携带 `extensions.code = "BFF_PARENT_*"`(02 §4.5)
|
||||
|
||||
### 1.4 Kafka 事件发布
|
||||
|
||||
无。parent-bff 不发布领域事件(BFF 聚合层无业务状态变更,02 §5.6)。
|
||||
|
||||
### 1.5 错误码前缀
|
||||
|
||||
`BFF_PARENT_`(如 BFF_PARENT_UPSTREAM_UNAVAILABLE、BFF_PARENT_AGGREGATION_FAILED、BFF_PARENT_NO_CHILDREN、BFF_PARENT_FORBIDDEN)
|
||||
`BFF_PARENT_`(C1 仲裁,BFF 在前;非 PARENT_BFF_)
|
||||
|
||||
错误码清单(完整见 02 §6.2):
|
||||
|
||||
| 错误码 | HTTP | 触发条件 |
|
||||
| --- | --- | --- |
|
||||
| BFF_PARENT_VALIDATION_ERROR | 400 | Zod / GraphQL input 校验失败 |
|
||||
| BFF_PARENT_UNAUTHORIZED | 401 | 缺失 x-user-id 头 |
|
||||
| BFF_PARENT_CHILD_NOT_BOUND | 403 | ChildGuard 拦截:childId 不在家长绑定列表 |
|
||||
| BFF_PARENT_NOT_FOUND | 404 | 资源不存在 |
|
||||
| BFF_PARENT_BAD_GATEWAY | 502 | 下游服务返回非 ok 或 gRPC rejected |
|
||||
| BFF_PARENT_GATEWAY_TIMEOUT | 504 | 下游调用超时 |
|
||||
| BFF_PARENT_SERVICE_UNAVAILABLE | 503 | 熔断器开启(P6) |
|
||||
| BFF_PARENT_INTERNAL_ERROR | 500 | 未捕获异常 |
|
||||
|
||||
**下游错误透传**(C3 仲裁:core-edu 统一 CORE_EDU_*):
|
||||
|
||||
| 下游服务 | 错误码前缀 | 示例 |
|
||||
| --- | --- | --- |
|
||||
| iam | IAM_ | IAM_USER_NOT_FOUND |
|
||||
| core-edu | CORE_EDU_ | CORE_EDU_GRADE_NOT_FOUND |
|
||||
| data-ana | DATA_ANA_ | DATA_ANA_ANALYTICS_NOT_READY |
|
||||
| msg | MSG_ | MSG_NOTIFICATION_NOT_FOUND |
|
||||
|
||||
---
|
||||
|
||||
@@ -50,28 +100,59 @@ GraphQL schema 文件路径:`apps/parent-bff/src/schema/*.graphql`(端口 :3
|
||||
|
||||
### 2.1 gRPC 调用(同步)
|
||||
|
||||
| 被调用方 | Service.RPC | 用途 | mock 策略 |
|
||||
| --------------- | ----------------------------------------- | ------------------------ | ------------------------------------------------------ |
|
||||
| iam (ai06) | IamService.GetUserInfo | 获取当前家长信息 | iam 就绪前返回固定 UserInfo(parent 角色) |
|
||||
| iam (ai06) | IamService.GetEffectivePermissions | 权限校验 | iam 就绪前返回家长权限集 |
|
||||
| iam (ai06) | IamService.GetViewports | 家长导航菜单 | iam 就绪前返回固定视口列表 |
|
||||
| iam (ai06) | IamService.GetChildrenByParent | 查询关联孩子列表(核心) | iam 就绪前返回固定 2 个 ChildInfo(I3/ISSUE-047 裁决) |
|
||||
| core-edu (ai08) | GradeService.ListGradesByStudent | 孩子成绩 | core-edu 就绪前返回固定 5 个 Grade |
|
||||
| core-edu (ai08) | AttendanceService.ListAttendanceByStudent | 孩子考勤 | core-edu 就绪前返回固定 10 条 Attendance |
|
||||
| core-edu (ai08) | HomeworkService.ListHomeworkByClass | 孩子作业 | core-edu 就绪前返回固定 3 个 Homework |
|
||||
| data-ana (ai11) | AnalyticsService.GetParentDashboard | 家长仪表盘 | data-ana 就绪前返回固定仪表盘(child_avg_score=85.0) |
|
||||
| data-ana (ai11) | AnalyticsService.GetStudentWeakness | 孩子薄弱点 | data-ana 就绪前返回固定 3 个 weak_points |
|
||||
| data-ana (ai11) | AnalyticsService.GetLearningTrend | 孩子学习趋势 | data-ana 就绪前返回固定趋势数据 |
|
||||
| msg (ai10) | NotificationService.ListNotifications | 家长通知 | msg 就绪前返回固定 10 条通知 |
|
||||
| msg (ai10) | NotificationService.MarkAsRead | 标记已读 | msg 就绪前返回 success=true |
|
||||
> proto 包名均为 `next_edu_cloud.<domain>.v1`
|
||||
> **状态标注**:✅ 已有 = proto 已定义;❌ 待补 = proto 缺失;⚠️ 待仲裁 = ai05 提请 coord 仲裁中
|
||||
|
||||
### 2.2 Kafka 事件订阅(异步)
|
||||
| 被调用方 | Service.RPC | proto message 包名 | 用途 | mock 策略 | 状态 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| iam (ai06) | IamService.GetUserInfo | next_edu_cloud.iam.v1.UserInfo | 获取当前家长信息 | 返回固定 UserInfo(parent 角色) | ✅ 已有 |
|
||||
| iam (ai06) | IamService.GetViewports | next_edu_cloud.iam.v1.Viewport[] | 家长导航菜单 | 返回固定视口列表 | ❌ 待 ai06 补(coord-cross-review #1) |
|
||||
| iam (ai06) | IamService.GetEffectivePermissions | next_edu_cloud.iam.v1.EffectivePermissions | 权限校验 | 返回家长权限集 | ❌ 待 ai06 补 |
|
||||
| iam (ai06) | **IamService.GetChildrenByParent** | next_edu_cloud.iam.v1.Child[] | 查询关联孩子列表(**P0 核心依赖**) | 返回固定 2 个 ChildInfo | ❌ 待 ai06 补(**I6 裁决**,P0 阻塞) |
|
||||
| core-edu (ai08) | GradeService.ListGradesByStudent | next_edu_cloud.core_edu.v1.Grade[] | 孩子成绩 | 返回固定 5 个 Grade | ✅ 已有 |
|
||||
| core-edu (ai08) | HomeworkService.ListHomeworkByClass | next_edu_cloud.core_edu.v1.Homework[] | 孩子作业 | 返回固定 3 个 Homework | ✅ 已有 |
|
||||
| core-edu (ai08) | ExamService.ListExamsByClass | next_edu_cloud.core_edu.v1.Exam[] | 孩子考试 | 返回固定 3 个 Exam | ✅ 已有 |
|
||||
| core-edu (ai08) | **ClassService.GetClass** | next_edu_cloud.core_edu.v1.Class | 孩子班级信息 | 返回固定 ClassInfo | ❌ 待补(**ISSUE-008**:proto 缺 ClassService,待 coord 仲裁归属) |
|
||||
| core-edu (ai08) | AttendanceService.ListAttendanceByStudent | next_edu_cloud.core_edu.v1.Attendance[] | 孩子考勤(P5+) | 返回固定 10 条 | ❌ 待 ai08 补(coord-cross-review #5,P3 补全) |
|
||||
| data-ana (ai11) | AnalyticsService.GetStudentWeakness | next_edu_cloud.analytics.v1.StudentWeakness | 孩子薄弱点 | 返回固定 3 个 weak_points | ✅ 已有 |
|
||||
| data-ana (ai11) | AnalyticsService.GetLearningTrend | next_edu_cloud.analytics.v1.LearningTrend | 孩子学习趋势 | 返回固定趋势数据 | ✅ 已有 |
|
||||
| data-ana (ai11) | AnalyticsService.GetClassPerformance | next_edu_cloud.analytics.v1.ClassPerformance | 班级学情对比 | 返回固定班级数据 | ✅ 已有 |
|
||||
| data-ana (ai11) | **AnalyticsService.GetParentDashboard** | — | 家长仪表盘聚合(多子女防 N+1) | 返回固定仪表盘 | ⚠️ 待仲裁(02 §14 #3,ai05 提请,proto 未定义) |
|
||||
| msg (ai10) | NotificationService.ListNotifications | next_edu_cloud.msg.v1.Notification[] | 家长通知 | 返回固定 10 条通知 | ✅ 已有 |
|
||||
| msg (ai10) | NotificationService.MarkAsRead | next_edu_cloud.msg.v1.Empty | 标记已读 | 返回 success | ✅ 已有 |
|
||||
| msg (ai10) | **NotificationPreferenceService.*** | — | 通知偏好配置 | 返回固定偏好 | ❌ 待 ai10 补(proto + 实现均缺失,P5) |
|
||||
|
||||
无。parent-bff 不订阅 Kafka 事件,仅做同步 gRPC 聚合。
|
||||
**类型映射注意**(ISSUE-008):
|
||||
|
||||
### 2.3 HTTP 调用(如有)
|
||||
- `core_edu.v1.Grade.score` 是 `string` 类型,GraphQL `Grade.score` 是 `Float!`,BFF response-mapper 需做 `Number.parseFloat(score)` 转换,转换失败抛 BFF_PARENT_BAD_GATEWAY
|
||||
- `msg.v1.Notification.is_read` 映射为 GraphQL `Notification.read`(字段名重命名)
|
||||
- `msg.v1.Notification` 无 `child_id` 字段(ISSUE-007),GraphQL `Notification.childId` 暂从 notification.type+content 解析或置 null,待 ai10 补 proto 字段
|
||||
|
||||
无。
|
||||
### 2.2 Kafka 事件订阅(异步,P5 可选)
|
||||
|
||||
> P4 阶段不订阅事件(02 §5.1)。P5 阶段可选订阅以下 topic 用于实时推送 + 缓存失效(02 §5.2)。
|
||||
|
||||
| Topic | 事件 | 发布方 | 消费动作 | 幂等性 | 阶段 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| edu.notification.sent | 通知发送 | msg | 推送给家长(push-gateway HTTP) | event_id SETNX | P5 |
|
||||
| edu.notification.read | 通知已读 | msg | 失效 bff:parent:notifications:* | event_id SETNX | P5 |
|
||||
| edu.notification.recalled | 通知撤回 | msg | 失效通知缓存 + 推送撤回 | event_id SETNX | P5 |
|
||||
| edu.notification.failed | 通知失败 | msg | 记录日志 + 告警 | event_id SETNX | P5 |
|
||||
| edu.teaching.grade.recorded | 成绩录入 | core-edu | 失效 bff:parent:grades:{childId} + 推送 | event_id SETNX | P5 |
|
||||
| edu.teaching.homework.graded | 作业批改 | core-edu | 失效 bff:parent:homework:{childId} + 推送 | event_id SETNX | P5 |
|
||||
| edu.teaching.exam.published | 考试发布 | core-edu | 失效 bff:parent:exams:{childId} + 推送 | event_id SETNX | P5 |
|
||||
|
||||
**消费者组**:`parent-bff-event-subscriber`(02 §5.5)
|
||||
**DLQ**:`edu.parent-bff.dlq`
|
||||
**提交策略**:manual commit
|
||||
|
||||
### 2.3 HTTP 调用(非 gRPC)
|
||||
|
||||
| 被调用方 | Method | Path | 用途 | 认证 | 阶段 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| push-gateway (ai02) | POST | /internal/push | 推送给在线家长(**U2 仲裁**:push-gateway 豁免 gRPC) | X-Internal-Key | P5 |
|
||||
|
||||
> push-gateway HTTP 调用在 P5 阶段启用,P4 不涉及。
|
||||
|
||||
---
|
||||
|
||||
@@ -79,43 +160,73 @@ GraphQL schema 文件路径:`apps/parent-bff/src/schema/*.graphql`(端口 :3
|
||||
|
||||
### 3.1 我依赖的上游就绪标志
|
||||
|
||||
- [ ] iam gRPC 50052 启用(ai06)—— **核心依赖 GetChildrenByParent(I3/ISSUE-047 裁决)**
|
||||
- [ ] iam gRPC 50052 启用(ai06)—— **核心依赖 GetChildrenByParent(I6 裁决,P0 阻塞)**
|
||||
- [ ] iam 补 GetViewports / GetEffectivePermissions RPC(coord-cross-review #1)
|
||||
- [ ] iam_student_guardians 表已建立(I6 裁决)
|
||||
- [ ] core-edu gRPC 50053 启用(ai08)
|
||||
- [ ] core-edu ClassService.GetClass proto 补全(ISSUE-008 待仲裁)
|
||||
- [ ] data-ana gRPC 50055 启用(ai11)
|
||||
- [ ] msg gRPC 50056 启用(ai10)
|
||||
- [ ] msg gRPC 50056 启用(ai10)—— P5
|
||||
- [ ] msg.proto Notification 补 child_id 字段(ISSUE-007 待仲裁)—— P5
|
||||
- [ ] msg NotificationPreferenceService 补全 —— P5
|
||||
- [ ] push-gateway /internal/push 启用(ai02)—— P5
|
||||
- [ ] api-gateway /parent 路由注册(ai01)
|
||||
- [ ] Kafka topic 已创建(C5 仲裁)—— P5
|
||||
- [ ] Redis 已部署且网络可达
|
||||
- [ ] buf.gen.yaml 补 gRPC TS 插件(coord)
|
||||
|
||||
### 3.2 我的就绪标志(供下游消费)
|
||||
|
||||
- [ ] parent-bff GraphQL :3010 启用(/healthz 返回 200)
|
||||
- [ ] /readyz 返回 200(含 4 个下游 gRPC 连通性检查)
|
||||
- [ ] /readyz 返回 200(含 4 个下游 gRPC 连通性检查:iam + core-edu + data-ana + Redis,02 §9 #7)
|
||||
- [ ] GraphQL schema 可内省(POST /graphql 返回 schema)
|
||||
- [ ] 核心 Query 可执行:currentUser / myChildren / childSummary / childGrades
|
||||
- [ ] 核心 Mutation 可执行:markAsRead
|
||||
- [ ] 数据范围校验生效(家长只能查自己孩子的数据,基于 iam.GetChildrenByParent 返回的 user_id 校验)
|
||||
- [ ] 核心 Query 可执行:dashboard / children / childGrades / childAnalytics
|
||||
- [ ] 核心 Mutation 可执行:selectChild(P4)/ markNotificationRead(P5)
|
||||
- [ ] DataScope=CHILDREN 校验生效(家长只能查自己孩子的数据,ChildGuard 基于 iam.GetChildrenByParent 返回的列表校验)
|
||||
- [ ] /metrics 暴露 parent_bff_* 指标
|
||||
|
||||
---
|
||||
|
||||
## §4 Mock 策略
|
||||
|
||||
### 4.1 我提供的 mock
|
||||
### 4.1 我提供的 mock(供下游 parent-portal ai15)
|
||||
|
||||
在 parent-bff 真实就绪前,为下游(parent-portal)提供以下 mock:
|
||||
在 parent-bff 真实就绪前,为 parent-portal 提供以下 mock:
|
||||
|
||||
- **GraphQL mock**:使用 Apollo Server mockProviders 或 MSW 拦截 POST /graphql
|
||||
- currentUser 返回固定家长(id="parent-001", name="王家长", roles=["parent"])
|
||||
- myChildren 返回固定 2 个孩子(id="student-001" 李同学 + id="student-002" 李妹妹)
|
||||
- childSummary 返回固定仪表盘(child_avg_score=85.0, child_class_rank=5)
|
||||
- childGrades 返回固定 5 个成绩
|
||||
- childAttendance 返回固定 10 条考勤
|
||||
- myNotifications 返回固定 10 条通知
|
||||
- **GraphQL mock**:使用 MSW 拦截 POST /graphql 或 Apollo Server mockProviders
|
||||
- `dashboard` 返回固定家长(id="parent-001", name="王家长", roles=["parent"])+ 2 个孩子 + unreadNotifications=3
|
||||
- `children` 返回固定 2 个孩子(id="student-001" 李同学 + id="student-002" 李妹妹)
|
||||
- `childGrades` 返回固定 5 个成绩(含 score 字段,Float 类型)
|
||||
- `childAnalytics` 返回固定学情(child_avg_score=85.0, class_rank=5)
|
||||
- `childHomework` 返回固定 3 个作业
|
||||
- `childExams` 返回固定 3 个考试
|
||||
- `notifications`(P5)返回固定 10 条通知(含 childId 字段)
|
||||
|
||||
### 4.2 我消费的 mock
|
||||
### 4.2 我消费的 mock(上游未就绪时)
|
||||
|
||||
在真实上游就绪前,parent-bff 使用以下 mock(详见 §2.1 mock 策略列):
|
||||
|
||||
- **iam mock**:固定 UserInfo + 家长权限 + 固定视口 + 固定 2 个 ChildInfo(家长-学生关联核心数据)
|
||||
- **core-edu mock**:固定孩子成绩/考勤/作业
|
||||
- **data-ana mock**:固定家长仪表盘/孩子薄弱点/趋势
|
||||
- **msg mock**:固定通知列表 + MarkAsRead success
|
||||
- **core-edu mock**:固定孩子成绩/作业/考试/班级信息
|
||||
- **data-ana mock**:固定孩子薄弱点/趋势/班级对比
|
||||
- **msg mock**(P5):固定通知列表(含 childId)+ MarkAsRead success + 固定偏好配置
|
||||
- **push-gateway mock**(P5):fetch mock 返回 success
|
||||
- **Redis**:Testcontainers 真实 Redis 实例(不用 mock)
|
||||
- **Kafka**(P5):kafkajs mock + jest.mock
|
||||
|
||||
> 关键:iam.GetChildrenByParent 的 mock 必须返回与 core-edu mock 数据一致的 student_id,否则数据范围校验会失败。parent-bff 启动时校验 myChildren 返回的 user_id 与下游查询的 student_id 一致性。
|
||||
> **关键约束**:iam.GetChildrenByParent 的 mock 必须返回与 core-edu mock 数据一致的 student_id(student-001 + student-002),否则 ChildGuard 越权校验会失败。parent-bff 启动时校验 myChildren 返回的 user_id 与下游查询的 student_id 一致性。
|
||||
|
||||
---
|
||||
|
||||
## §5 跨模块契约冲突跟踪
|
||||
|
||||
> 以下为 ai05 复审发现的跨模块契约问题,详见 [objections/parent-bff_issue.md](../objections/parent-bff_issue.md)
|
||||
|
||||
| ISSUE | 问题 | 影响 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| ISSUE-003 | contract.md 仲裁引用编号错误(I3 → I6) | 引用勘误,已修正本文档 | 待 coord 确认 |
|
||||
| ISSUE-004 | 004 §4 + matrix.md §1 未同步 C6 仲裁(缺 DataAna + Msg) | 新 AI 误判依赖 | 待 coord 仲裁 |
|
||||
| ISSUE-005 | parent-portal 01 文档仍按 REST 消费 parent-bff | 跨模块契约冲突 | 待 coord 仲裁 |
|
||||
| ISSUE-006 | proto 包名引用缺 next_edu_cloud 前缀 | gRPC 代码生成错误,已修正本文档 | 待 coord 确认 |
|
||||
| ISSUE-007 | msg.proto Notification 缺 child_id 字段 | 按孩子过滤通知失效 | 待 coord 仲裁 |
|
||||
| ISSUE-008 | core_edu.proto 缺 ClassService + Grade.score 类型不一致 | 班级信息查询 + 类型转换 | 待 coord 仲裁 |
|
||||
|
||||
@@ -6,8 +6,165 @@
|
||||
|
||||
---
|
||||
|
||||
## §0 已有仲裁核查结论(ai05 复审,2026-07-10)
|
||||
|
||||
> 对 parent-bff 相关的已仲裁决策(用户 U1-U4 + coord C1-C6 + coord-final-decisions I6)逐项核查执行情况。
|
||||
|
||||
### 0.1 核查通过项(已正确执行)
|
||||
|
||||
| 仲裁 | 主题 | 核查结论 |
|
||||
| --- | --- | --- |
|
||||
| U2 | push-gateway 豁免 gRPC,HTTP /internal/push | ✅ 02 §5.3/§7.1 已执行 HTTP 调用 push-gateway |
|
||||
| C2 | 端口 3010,不暴露 gRPC | ✅ 02 §7.2 + matrix.md §3 已执行 |
|
||||
| C3 | core-edu 错误码 CORE_EDU_* | ✅ 02 §6.2 已执行 |
|
||||
| C5 | Kafka topic edu.notification.sent/read/recalled/failed | ✅ 02 §5.2 已执行(004 §7.2 同步属 coord 待办 #7,不阻塞 parent-bff) |
|
||||
|
||||
### 0.2 核查发现的问题(仲裁已裁决但文档未同步/执行有偏差)
|
||||
|
||||
以下问题均为"仲裁结论正确,但相关文档未同步"或"文档内部不一致",提请 coord 确认处理方式。
|
||||
|
||||
---
|
||||
|
||||
## 问题列表
|
||||
|
||||
### ISSUE-001-ai05:01-understanding.md 未同步 ai05 接手与多项仲裁
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:文档同步缺失
|
||||
- **描述**:01-understanding.md 头部仍标 "AI 标识:ai04"、"状态:待 coord 审核",未反映 ai05 已正式接手(ai-allocation.md §3.2)。同时以下仲裁已裁决但 01 未同步:
|
||||
- U3(GraphQL P2 引入):01 §4.1 仍建议"P4 先对齐 teacher-bff REST 现状",与 U3 仲裁冲突
|
||||
- U4(BFF 豁免 @RequirePermission):01 §6 表格"权限装饰器"行标"⚠️ 不对齐",未引用 U4 仲裁
|
||||
- C1(错误码前缀 BFF_PARENT_):01 §3.3 仍用 `PARENT_BFF_` 旧前缀
|
||||
- **建议方案**:01-understanding.md 头部更新为 ai05 复审版,同步 U3/U4/C1 仲裁结论;或由 coord 确认 01 作为"阶段 1 历史快照"保留原样,以 02 为准
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-002-ai05:02 文档内部 ChildGuard 缓存 TTL 不一致
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:文档内部不一致
|
||||
- **描述**:02-architecture-design.md 内部 ChildGuard 绑定列表缓存 TTL 三处不一致:
|
||||
- §3.1.1 Redis 缓存 Schema 表:写 "60s"(coord 推断原值)
|
||||
- §9 #2 ai05 review 结论:调整为 "30s + 主动失效"
|
||||
- §13 #7 黄金模板对齐表:写 "30s"
|
||||
§3.1.1 表格未同步 §9 的调整,导致同一文档内 60s 与 30s 并存。
|
||||
- **建议方案**:§3.1.1 表格 ChildGuard 行 TTL 改为 "30s",与 §9 #2 + §13 #7 一致
|
||||
- **状态**:待 coord 仲裁(ai05 建议直接修正,属于文档勘误)
|
||||
|
||||
### ISSUE-003-ai05:contract.md 仲裁引用编号错误(I3 → I6)
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约引用错误
|
||||
- **描述**:contracts/parent-bff_contract.md §2.1 表格 + §3.1 引用 "I3/ISSUE-047 裁决" 作为 GetChildrenByParent 的仲裁依据。但核查 coord-final-decisions.md 发现:
|
||||
- I3 是 "PermissionGuard 本地 map → DB 驱动" 裁决,与家长-学生关联无关
|
||||
- I6 才是 "家长-学生关联:P2 即补全 iam_student_guardians 表 + GetChildrenByParent RPC" 裁决
|
||||
- 全仓库未检索到 "ISSUE-047" 编号(grep 无结果),疑为虚构编号
|
||||
- **建议方案**:contract.md 将 "I3/ISSUE-047 裁决" 修正为 "I6 裁决(coord-final-decisions.md §1)"
|
||||
- **状态**:待 coord 仲裁(ai05 建议直接修正,属于引用勘误)
|
||||
|
||||
### ISSUE-004-ai05:004 §4 服务依赖图与 matrix.md §1 未同步 C6 仲裁
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:架构图未同步
|
||||
- **描述**:C6 仲裁将 parent-bff 依赖扩展为 iam + core-edu + data-ana + msg,但:
|
||||
- 004_architecture_impact_map.md §4 服务依赖图(line 376-377)仍只画 `PBFF --> IAM` + `PBFF --> CoreEdu`,未加 DataAna + Msg
|
||||
- matrix.md §1 服务依赖矩阵(line 64-65)同样只画 `PBFF --> IAM` + `PBFF --> CORE`
|
||||
- 02 §0.1 C6 行已标注 "004 §4 待 coord 同步更新",但至今未同步
|
||||
此差异导致新接手的 AI 看 004/matrix 会误以为 parent-bff 不依赖 data-ana/msg,与 02 设计冲突。
|
||||
- **建议方案**:coord 在 004 §4 服务依赖图补 `PBFF --> DataAna` + `PBFF --> Msg`,matrix.md §1 同步;更新 004 时按 project_rules §1 "改码必同步图" 执行
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-005-ai05:parent-portal 01 文档与 parent-bff GraphQL 决策跨模块冲突
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:跨模块契约冲突
|
||||
- **描述**:U3 仲裁决定 parent-bff P4 直接用 GraphQL(02 §4 已执行),但 parent-portal 01-understanding.md §3.1 仍按 REST 设计消费 parent-bff:
|
||||
- parent-portal §3.1 列 `GET /parent/viewports`、`GET /parent/dashboard`、`GET /parent/children` 等 REST 端点
|
||||
- parent-bff 02 §4.1 明确 "不实现 REST 业务端点(仅保留 /healthz /readyz /metrics)"
|
||||
- parent-portal §3.1 还引用 `GET /iam/effective-permissions`(C4 仲裁已改为 `/iam/permissions/effective`)
|
||||
- parent-portal §3.1 标 "BFF 对接:parent-bff(ai04 设计)",ai04 已过时(现 ai05)
|
||||
此冲突若不解决,parent-portal(ai15)会按 REST 实现 frontend client,与 parent-bff GraphQL 端点不兼容。
|
||||
- **建议方案**:coord 协调 ai15 将 parent-portal 01/02 文档的 parent-bff 消费契约从 REST 改为 GraphQL(`POST /api/v1/parent/graphql`),同步 C4 iam 路径仲裁;此属跨模块契约,按 project_rules §14.4 跨模块变更顺序处理
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-006-ai05:proto 包名引用不一致(缺失 next_edu_cloud 前缀)
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约引用错误
|
||||
- **描述**:02-architecture-design.md §3.3 DTO 映射表引用 proto message 为 `iam.v1.UserInfo`、`core_edu.v1.Grade[]`、`analytics.v1.StudentWeakness`、`msg.v1.Notification[]`。但实际 proto 文件包名均带 `next_edu_cloud.` 前缀:
|
||||
- iam.proto: `package next_edu_cloud.iam.v1;`
|
||||
- core_edu.proto: `package next_edu_cloud.core_edu.v1;`
|
||||
- analytics.proto: `package next_edu_cloud.analytics.v1;`
|
||||
- msg.proto: `package next_edu_cloud.msg.v1;`
|
||||
引用不一致会导致 gRPC client 代码生成时 package 路径错误。
|
||||
- **建议方案**:02 §3.3 DTO 映射表 proto message 列全部补 `next_edu_cloud.` 前缀;或确认是否统一去掉前缀(需 buf.yaml 配置一致)
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-007-ai05:GraphQL Notification.childId 字段在 msg.proto 缺失
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约缺口
|
||||
- **描述**:02 §4.2 GraphQL schema 定义 `Notification` type 含 `childId: ID` 字段(家长场景需知道通知关联哪个孩子)。但 msg.proto 的 Notification message 无 childId 字段:
|
||||
```proto
|
||||
message Notification {
|
||||
string id = 1;
|
||||
string user_id = 2;
|
||||
string type = 3;
|
||||
string title = 4;
|
||||
string content = 5;
|
||||
string channel = 6;
|
||||
bool is_read = 7;
|
||||
int64 created_at = 8;
|
||||
}
|
||||
```
|
||||
parent-bff 无法从 msg 服务获取通知关联的孩子 ID,影响"按孩子过滤通知"场景。
|
||||
- **建议方案**:coord 协调 ai10 在 msg.proto Notification message 补 `string child_id = 9;` 字段(可选,非家长通知为空);或 parent-bff 从 notification.content 解析(脆弱,不推荐)
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-008-ai05:core_edu.proto 缺 ClassService,02 §7.1 列为已有
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约缺口
|
||||
- **描述**:02 §7.1 交互矩阵列 `core-edu ClassService.GetClass`(查孩子班级信息)状态为 "✅ 已有"。但核查 core_edu.proto 实际只有 ExamService / HomeworkService / GradeService 三个 service,无 ClassService。matrix.md §2 却声称 core-edu 有 "ClassService + ExamService + HomeworkService + GradeService + AttendanceService" 共 22 RPC。proto 与 matrix.md 不一致,且 02 错误标注为"已有"。
|
||||
另:core_edu.proto 的 Grade.score 是 string 类型,02 GraphQL Grade.score 是 Float!,string→Float 转换规则未在 §3.3 说明。
|
||||
- **建议方案**:
|
||||
1. coord 确认 ClassService 归属(core-edu 还是 classes 服务),补 proto
|
||||
2. 02 §7.1 ClassService.GetClass 状态从 "✅ 已有" 改为 "❌ 待补"
|
||||
3. 02 §3.3 补 Grade.score string→Float 转换规则说明
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-009-ai05:ai-allocation iam 责任方与 01/02 文档不一致
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:责任方引用过时
|
||||
- **描述**:01-understanding.md §7.1/§7.3 多处提"推动 ai02 在 iam 补接口",02 §8.1 P0 阻塞项 + §14.1 P0-1 也标 iam 责任方为 "ai06(iam 现归属)" 但 §7.3 #1 仍标 ai02。实际 ai-allocation.md §3.2 确认 iam 归属 ai06。01 文档未同步。
|
||||
- **建议方案**:01 §7.1/§7.3 将 "ai02" 改为 "ai06";02 §7.3 #1 同步
|
||||
- **状态**:待 coord 仲裁(ai05 建议直接修正,属于引用勘误)
|
||||
|
||||
### ISSUE-010-ai05:02 缺少 ADR / NFR / 容量规划 / 威胁建模(业界规范差距)
|
||||
|
||||
- **提请方**:ai05
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:架构文档规范缺失
|
||||
- **描述**:对照业界通用架构文档规范(如 C4 model + ADR + NFR),02-architecture-design.md 存在以下规范差距:
|
||||
1. **ADR 缺失**:虽有"已仲裁决策"表,但未按 ADR 格式(Context/Decision/Consequences)记录关键决策(如 GraphQL vs REST、ChildGuard 位置、多子女切换方案)。建议补 ADR 索引章节。
|
||||
2. **NFR 未量化**:§10.4 P6 提"SLO 监控",但文档前部未明确非功能性需求(P95 延迟、可用性、吞吐量目标)。业界规范要求架构文档开头列 NFR。
|
||||
3. **容量规划缺失**:未估算家长端 QPS、并发数、数据量(家长数 × 孩子数 × 成绩数),无法指导 HPA 副本数和 Redis 容量规划。
|
||||
4. **安全威胁建模缺失**:§6.2 列错误码但未做威胁建模(STRIDE)。家长场景涉及未成年人数据(COPPA/FERPA/PIPL),应补威胁模型。
|
||||
5. **数据流图(DFD)缺失**:§1.2 只有 Dashboard 时序图,缺少 DFD 展示数据跨信任边界流动。
|
||||
- **建议方案**:coord 确认是否在 02 补全上述章节,或作为 P6 硬化阶段补全;当前不阻塞 P4 实施
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
追加条目格式:
|
||||
|
||||
@@ -20,5 +177,3 @@
|
||||
- **建议方案**:[AI 的建议]
|
||||
- **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X)
|
||||
-->
|
||||
|
||||
(暂无问题)
|
||||
|
||||
@@ -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 集中管理 |
|
||||
|
||||
Reference in New Issue
Block a user