docs(student-bff): 更新架构问题记录、工作排期和对接契约,对齐已裁决规则
完成已有仲裁核查,新增待仲裁问题归档,细化全阶段排期与依赖,对齐coord B1-B8和总裁裁决,更新GraphQL规范、越权防御、DownstreamClient契约
This commit is contained in:
@@ -1,50 +1,310 @@
|
||||
# student-bff 对接契约
|
||||
|
||||
> 负责人:ai04
|
||||
> 关联:[matrix.md](./matrix.md)、[iam.proto](../../../packages/shared-proto/proto/iam.proto)、[core_edu.proto](../../../packages/shared-proto/proto/core_edu.proto)、[content.proto](../../../packages/shared-proto/proto/content.proto)、[analytics.proto](../../../packages/shared-proto/proto/analytics.proto)、[msg.proto](../../../packages/shared-proto/proto/msg.proto)
|
||||
> 阶段:P3(批次 2 启动)
|
||||
> 版本:v2(对齐 coord-final-decisions B1-B8 + president-final-rulings §2.2/2.3/2.4/2.6/2.7/2.8/2.9)
|
||||
> 日期:2026-07-10
|
||||
> 关联文档:
|
||||
>
|
||||
> - [matrix.md](../matrix.md)
|
||||
> - [coord-final-decisions.md](../../coord-final-decisions.md) §2 BFF 专项裁决 B1-B8
|
||||
> - [president-final-rulings.md](../../president-final-rulings.md) §2.2 GraphQL schema 仲裁机制
|
||||
> - [iam.proto](../../../../packages/shared-proto/proto/iam.proto)
|
||||
> - [core_edu.proto](../../../../packages/shared-proto/proto/core_edu.proto)
|
||||
> - [content.proto](../../../../packages/shared-proto/proto/content.proto)
|
||||
> - [analytics.proto](../../../../packages/shared-proto/proto/analytics.proto)
|
||||
> - [msg.proto](../../../../packages/shared-proto/proto/msg.proto)
|
||||
|
||||
---
|
||||
|
||||
## §0 裁决对齐声明
|
||||
|
||||
本文件已对齐以下裁决(无遗漏):
|
||||
|
||||
| 裁决编号 | 内容 | 本文件章节 |
|
||||
| -------- | ------------------------------------------ | -------------- |
|
||||
| B1 | P2 起直接 GraphQL(GraphQL Yoga + DataLoader) | §1.2 / §1.3 |
|
||||
| B2 | 首次实现即 gRPC 调用下游 | §2.1 / §2.4 |
|
||||
| B3 | BFF 豁免 @RequirePermission | §1.6 / §1.7 |
|
||||
| B4 | 全部 BFF 强制自我越权防御 | §1.7 |
|
||||
| B5 | 错误码前缀 `BFF_STUDENT_` | §1.6 |
|
||||
| B6 | Redis 5-30s 短缓存 | §7 |
|
||||
| B7 | P2-P4 不订阅 Kafka,P5 后再订阅 | §1.5 / §2.2 |
|
||||
| B8 | DownstreamClient 抽象回写 teacher-bff | §2.4 |
|
||||
| G14 | 错误码前缀服务名大写 | §1.6 |
|
||||
| F4 | i18n key `error.bffStudent.<code_snake>` | §1.6 |
|
||||
| F7 | 权限点命名 `<RESOURCE>_<ACTION>[_<SCOPE>]` | §1.3.4 |
|
||||
| F9 | 前端用 urql/apollo 消费 GraphQL | §1.2 |
|
||||
| §2.2 | GraphQL schema 仲裁机制 | §1.3.2 / §1.3.3 |
|
||||
| §2.3 | BFF 跨阶段扩展例外 | §3 |
|
||||
| §2.4 | /readyz 探针按阶段扩展 | §4 |
|
||||
| §2.6 | 降级模式方案 B(data 内 degraded 字段) | §1.4.2 |
|
||||
| §2.7 | BFF 错误码语义区分(3 类) | §1.6 / §1.7.3 |
|
||||
| §2.8 | Dashboard Query Resolver P2 实现方式 | §1.3.5 |
|
||||
| §2.9 | 越权防御 P2 实现方式(DEV_MODE 放行) | §1.7.4 |
|
||||
| §5.1 | admin-portal 复用 teacher-bff,不占 student-bff 命名空间 | §1.3.6 |
|
||||
|
||||
---
|
||||
|
||||
## §1 我提供什么(对外接口)
|
||||
|
||||
### 1.1 gRPC 接口(如有)
|
||||
### 1.1 服务基础信息
|
||||
|
||||
无对外 gRPC。student-bff 是 GraphQL 聚合层。
|
||||
| 项目 | 值 |
|
||||
| ------------- | --------------------------------------------- |
|
||||
| 服务名 | student-bff |
|
||||
| 服务类型 | BFF 聚合层(无 DB,无 Outbox) |
|
||||
| HTTP 端口 | 3009 |
|
||||
| gRPC 端口 | 不暴露(BFF 仅对下游走 gRPC,不对外提供 gRPC) |
|
||||
| 路由前缀 | `/student/*`(api-gateway 透传) |
|
||||
| 部署目录 | `services/student-bff/` |
|
||||
| 角色要求 | `student`(JWT 必需 + student 角色) |
|
||||
|
||||
### 1.2 HTTP 端点(如有)
|
||||
### 1.2 HTTP 端点
|
||||
|
||||
| Method | Path | 用途 | 认证 |
|
||||
| ------ | -------- | ----------------------------------------- | ----------------------- |
|
||||
| POST | /graphql | 学生 BFF GraphQL 端点 | JWT 必需 + student 角色 |
|
||||
| GET | /graphql | GraphQL Playground(开发环境) | 开发环境公开 |
|
||||
| GET | /healthz | 健康检查(liveness) | 公开 |
|
||||
| GET | /readyz | 就绪检查(readiness,含下游 gRPC 连通性) | 公开 |
|
||||
| Method | Path | 用途 | 认证 | 实现 |
|
||||
| ------ | ---------- | ------------------------------------------ | ---------------------------- | ------- |
|
||||
| POST | /graphql | 学生 BFF GraphQL 端点(B1 裁决,GraphQL Yoga) | JWT 必需 + student 角色 | P3 |
|
||||
| GET | /graphql | GraphQL Playground(开发环境) | 开发环境公开 | P3 |
|
||||
| GET | /healthz | 健康检查(liveness) | 公开 | P3 |
|
||||
| GET | /readyz | 就绪检查(readiness,按阶段扩展探针) | 公开 | P3 |
|
||||
| GET | /metrics | Prometheus 指标端点 | 公开(生产限内网) | P3 |
|
||||
|
||||
### 1.3 GraphQL schema(如 BFF)
|
||||
> **B1 裁决**:P2 起直接 GraphQL(GraphQL Yoga + DataLoader),禁止 REST → GraphQL 渐进式过渡。
|
||||
> **B2 裁决**:对下游通信首次实现即 gRPC,禁止 HTTP fetch → gRPC 渐进式过渡。
|
||||
> **F9 裁决**:前端 student-portal 用 urql/apollo 消费 GraphQL。
|
||||
|
||||
GraphQL schema 文件路径:`apps/student-bff/src/schema/*.graphql`(端口 :3009)
|
||||
### 1.3 GraphQL schema(核心契约)
|
||||
|
||||
核心 Query / Mutation 域:
|
||||
#### 1.3.1 schema 存放路径
|
||||
|
||||
- **auth**:currentUser(聚合 iam.GetUserInfo + GetEffectivePermissions + GetViewports)
|
||||
- **myClasses**:我的班级(聚合 core-edu.ClassService.GetClass + ListStudentsByClass)
|
||||
- **myExams**:我的考试列表(聚合 core-edu.ExamService.ListExamsByClass)
|
||||
- **myHomework**:我的作业(聚合 core-edu.HomeworkService.ListHomeworkByClass + SubmitHomework)
|
||||
- **myGrades**:我的成绩(聚合 core-edu.GradeService.ListGradesByStudent)
|
||||
- **myAttendance**:我的考勤(聚合 core-edu.AttendanceService.ListAttendanceByStudent)
|
||||
- **content**:textbooks / chapters / learningPath(聚合 content.KnowledgeGraphService.GetLearningPath)
|
||||
- **dashboard**:studentDashboard(聚合 data-ana.AnalyticsService.GetStudentDashboard)
|
||||
- **weakness**:myWeakness(聚合 data-ana.AnalyticsService.GetStudentWeakness)
|
||||
- **trend**:myTrend(聚合 data-ana.AnalyticsService.GetLearningTrend)
|
||||
- **notifications**:myNotifications / markAsRead(聚合 msg.NotificationService)
|
||||
**强制路径**:`packages/shared-ts/contracts/graphql/student-bff.schema.graphql`
|
||||
|
||||
### 1.4 Kafka 事件发布(如有)
|
||||
> 依据 president-final-rulings §2.2.1:3 个 BFF schema 统一存放于 `packages/shared-ts/contracts/graphql/`,由各 BFF 负责 AI 起草、coord 仲裁。student-bff schema 不放在 `services/student-bff/src/` 下,避免前端 AI 难以发现。
|
||||
|
||||
无。student-bff 不发布事件,仅做 gRPC 聚合。
|
||||
#### 1.3.2 起草与仲裁流程
|
||||
|
||||
### 1.5 错误码前缀
|
||||
依据 president-final-rulings §2.2.6:
|
||||
|
||||
`BFF_STUDENT_`(如 BFF_STUDENT_UPSTREAM_UNAVAILABLE、BFF_STUDENT_AGGREGATION_FAILED、BFF_STUDENT_FORBIDDEN)
|
||||
1. **起草方**:ai04 在批次 1 等待期起草 student-bff GraphQL schema 草案
|
||||
2. **仲裁方**:coord 在**批次 2 启动前**仲裁第一版 schema
|
||||
3. **消费方**:ai14(student-portal)基于仲裁版 schema 消费
|
||||
4. **变更流程**:
|
||||
- schema 变更需 PR + ai04(BFF)+ ai14(前端)双方 review
|
||||
- 重大变更(删除字段 / 修改类型)需 coord 仲裁
|
||||
- 新增字段允许,无需 coord 仲裁,但需通知 ai14
|
||||
5. **版本管理**:SDL-first(`schema.graphql` 文件),配合 `graphql-codegen` 生成 TS 类型
|
||||
|
||||
#### 1.3.3 schema 设计规范
|
||||
|
||||
依据 president-final-rulings §2.2.5:
|
||||
|
||||
| 规范项 | 规则 |
|
||||
| -------------- | ----------------------------------------------------------------- |
|
||||
| 命名风格 | Query/Mutation 用 camelCase(如 `studentDashboard` / `myClasses`) |
|
||||
| 分页规范 | **Relay Cursor Connections**(`{ edges, pageInfo, totalCount }`) |
|
||||
| 错误响应 | GraphQL errors 数组 + `extensions.code` + `extensions.traceId` |
|
||||
| 权限点标注 | 注释形式 `# @permission: DASHBOARD_VIEW`(F7 规范) |
|
||||
| DataScope 标注 | 注释形式 `# @dataScope: OWN`(学生数据隔离 SELF) |
|
||||
| 字段命名 | Type 用 PascalCase,字段用 camelCase,枚举用 UPPER_SNAKE_CASE |
|
||||
| 非空与可选 | 必填字段用 `!`,可空字段不标 `!`(避免破坏性变更) |
|
||||
|
||||
#### 1.3.4 核心 Query / Mutation 域
|
||||
|
||||
> 完整 SDL 定义见 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`(ai04 起草,coord 仲裁)。
|
||||
|
||||
**Query 域**:
|
||||
|
||||
| Query | 用途 | 聚合下游 RPC | 权限点标注 | DataScope |
|
||||
| ---------------------- | ------------------------ | ----------------------------------------------------------- | --------------------------- | --------- |
|
||||
| `currentUser` | 当前学生信息 + 权限 + 视口 | iam.GetUserInfo + GetEffectivePermissions + GetViewports | `# @permission: AUTH_READ` | OWN |
|
||||
| `myClasses` | 我的班级列表 | core-edu.ClassService.GetClass + ListStudentsByClass | `# @permission: CLASS_READ` | OWN |
|
||||
| `myExams` | 我的考试列表 | core-edu.ExamService.ListExamsByClass | `# @permission: EXAM_READ` | OWN |
|
||||
| `myHomework` | 我的作业列表 | core-edu.HomeworkService.ListHomeworkByClass | `# @permission: HOMEWORK_READ` | OWN |
|
||||
| `myGrades` | 我的成绩列表 | core-edu.GradeService.ListGradesByStudent | `# @permission: GRADE_READ` | OWN |
|
||||
| `myAttendance` | 我的考勤记录 | core-edu.AttendanceService.ListAttendanceByStudent | `# @permission: ATTENDANCE_READ` | OWN |
|
||||
| `textbooks` | 教材列表 | content.TextbookService.ListTextbooks | `# @permission: TEXTBOOK_READ` | OWN |
|
||||
| `chapters` | 章节列表 | content.ChapterService.ListChapters | `# @permission: CHAPTER_READ` | OWN |
|
||||
| `learningPath` | 学习路径推荐 | content.KnowledgeGraphService.GetLearningPath | `# @permission: LEARNING_PATH_READ` | OWN |
|
||||
| `studentDashboard` | 学生仪表盘 | data-ana.AnalyticsService.GetStudentDashboard | `# @permission: DASHBOARD_VIEW` | OWN |
|
||||
| `myWeakness` | 我的薄弱点 | data-ana.AnalyticsService.GetStudentWeakness | `# @permission: WEAKNESS_READ` | OWN |
|
||||
| `myTrend` | 学习趋势 | data-ana.AnalyticsService.GetLearningTrend | `# @permission: TREND_READ` | OWN |
|
||||
| `myNotifications` | 我的通知列表 | msg.NotificationService.ListNotifications | `# @permission: NOTIFICATION_READ` | OWN |
|
||||
| `myNotificationUnreadCount` | 通知未读数 | msg.NotificationService.GetUnreadCount | `# @permission: NOTIFICATION_READ` | OWN |
|
||||
|
||||
**Mutation 域**:
|
||||
|
||||
| Mutation | 用途 | 聚合下游 RPC | 权限点标注 | DataScope |
|
||||
| ---------------------- | ---------------- | ----------------------------------------- | --------------------------- | --------- |
|
||||
| `submitHomework` | 提交作业 | core-edu.HomeworkService.SubmitHomework | `# @permission: HOMEWORK_SUBMIT` | OWN |
|
||||
| `markNotificationAsRead` | 标记通知已读 | msg.NotificationService.MarkAsRead | `# @permission: NOTIFICATION_UPDATE` | OWN |
|
||||
|
||||
> **分页**:所有列表 Query(myClasses / myExams / myHomework / myGrades / myAttendance / textbooks / chapters / myNotifications)使用 Relay Cursor Connections 规范,返回 `{ edges, pageInfo, totalCount }`。
|
||||
> **DataScope=OWN**:学生数据隔离为 SELF,所有 Query 透传 `x-user-id` 给下游,下游 Repository 按 DataScope=SELF 过滤。
|
||||
|
||||
#### 1.3.5 Dashboard Query Resolver 实现方式(§2.8 裁决)
|
||||
|
||||
依据 president-final-rulings §2.8:
|
||||
|
||||
1. **GraphQL schema 设计完整**(含全部 Query/Mutation 字段定义),P3 即定型,后续不重构
|
||||
2. **Resolver 实现**:
|
||||
- **P3 阶段**:`studentDashboard` Query Resolver 内部调 iam + core-edu gRPC,返回学生基础信息 + 班级列表 + 考试列表 + 作业列表 + 成绩列表
|
||||
- **P4 扩展**:增加 content(教材/章节)+ data-ana(仪表盘/薄弱点/趋势)数据源,将 null 字段替换为真实数据
|
||||
- **P5 扩展**:增加 msg(通知未读数)+ ai(AI 助教入口)数据源
|
||||
- 未启用的下游字段返回 `null` + `extensions.warning = "field_unavailable_in_p3"`
|
||||
3. **前端配合**:ai14 student-portal 对 null 字段做 UI 降级展示(如"数据加载中"或隐藏模块)
|
||||
4. **此方案不违反"不分阶段"**:schema 即最终方案,Resolver 内部数据源扩展属"跨阶段扩展例外"(见 §3)
|
||||
|
||||
#### 1.3.6 admin-portal 命名空间
|
||||
|
||||
依据 president-final-rulings §5.1:
|
||||
|
||||
- **admin-portal 复用 teacher-bff GraphQL endpoint**,不新建 admin-bff 服务
|
||||
- **student-bff 不预留 admin schema 命名空间**(admin 操作走 teacher-bff 的 `admin.*` 命名空间)
|
||||
- ai04 无需为 admin-portal 做任何 schema 预留
|
||||
|
||||
### 1.4 GraphQL 错误响应格式
|
||||
|
||||
#### 1.4.1 GraphQL errors 数组 + ActionState 扩展
|
||||
|
||||
依据 president-final-rulings §2.2.3 + G8 裁决:
|
||||
|
||||
GraphQL 错误响应遵循标准 errors 数组格式,扩展 ActionState 字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"message": "学生身份验证失败",
|
||||
"extensions": {
|
||||
"code": "BFF_STUDENT_UNAUTHORIZED",
|
||||
"traceId": "abc-123-def-456",
|
||||
"i18nKey": "error.bffStudent.unauthorized",
|
||||
"severity": "error"
|
||||
}
|
||||
}
|
||||
],
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ------------------- | ------ | ----------------------------------------------- |
|
||||
| `extensions.code` | string | 错误码(BFF_STUDENT_* 前缀,见 §1.6) |
|
||||
| `extensions.traceId`| string | 全链路追踪 ID(由 Gateway 注入 X-Request-Id) |
|
||||
| `extensions.i18nKey`| string | i18n key(F4 规范:`error.bffStudent.<code_snake>`) |
|
||||
| `extensions.severity` | string | `error` / `warning` / `info` |
|
||||
|
||||
#### 1.4.2 降级模式(方案 B,§2.6 裁决)
|
||||
|
||||
依据 president-final-rulings §2.6:当下游服务不可用但需返回部分数据时,采用**方案 B**(success=true + error=null + data 内 degraded 字段):
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"studentDashboard": {
|
||||
"user": { "id": "stu-001", "name": "李同学" },
|
||||
"classes": [{ "id": "cls-001", "name": "高三1班" }],
|
||||
"weakness": null,
|
||||
"degraded": true,
|
||||
"degradedReason": "data_ana_unavailable",
|
||||
"degradedFields": ["weakness"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**规则**:
|
||||
|
||||
1. 降级时 HTTP 200,GraphQL `data` 非 null
|
||||
2. 降级字段返回 `null`,并在父对象内加 `degraded: true` + `degradedReason: string` + `degradedFields: string[]`
|
||||
3. 前端检查 `data.degraded` 判断降级,对 `degradedFields` 内字段做 UI 降级展示
|
||||
4. 降级场景示例:
|
||||
- data-ana gRPC 不可用 → `studentDashboard.weakness` / `myTrend` / `myWeakness` 降级
|
||||
- content gRPC 不可用 → `textbooks` / `chapters` / `learningPath` 降级
|
||||
- msg gRPC 不可用 → `myNotifications` / `myNotificationUnreadCount` 降级
|
||||
|
||||
### 1.5 Kafka 事件发布
|
||||
|
||||
**无**。student-bff 是纯聚合层,不发布 Kafka 事件,不写 Outbox。
|
||||
|
||||
### 1.6 错误码前缀与列表
|
||||
|
||||
依据 B5 + G14 + F4 + §2.7 裁决:
|
||||
|
||||
**错误码前缀**:`BFF_STUDENT_`(统一 BFF_ 前缀,服务名大写)
|
||||
|
||||
**i18n key 规范**:`error.bffStudent.<code_snake>`(F4 裁决)
|
||||
|
||||
**错误码清单**:
|
||||
|
||||
| 错误码 | HTTP | 场景 | i18n key |
|
||||
| ------------------------------------- | ---- | ---------------------------------------------- | --------------------------------------------- |
|
||||
| `BFF_STUDENT_UNAUTHORIZED` | 401 | x-user-id 缺失或无效 | `error.bffStudent.unauthorized` |
|
||||
| `BFF_STUDENT_FORBIDDEN_RESOURCE` | 403 | 学生越权访问他人数据(场景 A) | `error.bffStudent.forbidden_resource` |
|
||||
| `BFF_STUDENT_IDENTITY_MISMATCH` | 403 | JWT userId 与请求 body userId 不一致(场景 B) | `error.bffStudent.identity_mismatch` |
|
||||
| `BFF_STUDENT_BAD_GATEWAY` | 502 | 下游 gRPC 调用失败(非业务错误) | `error.bffStudent.bad_gateway` |
|
||||
| `BFF_STUDENT_UPSTREAM_UNAVAILABLE` | 503 | 下游服务不可用(降级模式触发) | `error.bffStudent.upstream_unavailable` |
|
||||
| `BFF_STUDENT_AGGREGATION_FAILED` | 500 | 聚合逻辑异常(未知错误) | `error.bffStudent.aggregation_failed` |
|
||||
| `BFF_STUDENT_VALIDATION_ERROR` | 400 | 输入参数校验失败(Zod 校验) | `error.bffStudent.validation_error` |
|
||||
| `BFF_STUDENT_GRAPHQL_PARSE_ERROR` | 400 | GraphQL 语法解析错误 | `error.bffStudent.graphql_parse_error` |
|
||||
| `BFF_STUDENT_GRAPHQL_VALIDATION_ERROR`| 400 | GraphQL 字段类型校验错误 | `error.bffStudent.graphql_validation_error` |
|
||||
| `BFF_STUDENT_RATE_LIMITED` | 429 | 限流触发(Gateway 层处理,BFF 兜底) | `error.bffStudent.rate_limited` |
|
||||
|
||||
> **B3 裁决澄清**:BFF 豁免 `@RequirePermission` 指不做"功能权限决策"(如"能否查看仪表盘"),但必须做"数据权限防御"(如"只能看自己的数据"),见 §1.7。
|
||||
|
||||
### 1.7 BFF 越权防御契约(B4 裁决)
|
||||
|
||||
#### 1.7.1 越权防御场景
|
||||
|
||||
依据 B4 + §2.7 裁决,student-bff 强制自我越权防御:
|
||||
|
||||
| 场景 | 描述 | 防御方式 |
|
||||
| ---- | ---------------------------------------------- | --------------------------------------------------- |
|
||||
| A | 学生查询他人数据(如 query 传入非自己 userId) | AuthorizationGuard 比对 `x-user-id` 与查询参数 |
|
||||
| B | JWT userId 与请求 body userId 不一致 | Mutation 入参校验,拒绝不一致请求 |
|
||||
|
||||
#### 1.7.2 AuthorizationGuard 接口
|
||||
|
||||
依据 §2.9 裁决:
|
||||
|
||||
```typescript
|
||||
// services/student-bff/src/middleware/authorization.guard.ts
|
||||
export interface AuthorizationGuard {
|
||||
/**
|
||||
* 校验学生是否有权访问指定资源
|
||||
* @param currentUserId 从 x-user-id header 获取
|
||||
* @param resourceUserId 查询参数中的 userId
|
||||
* @returns true 允许访问,false 拒绝
|
||||
*/
|
||||
canAccessSelfData(currentUserId: string, resourceUserId: string): Promise<boolean>;
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.7.3 3 类错误码语义(§2.7 裁决)
|
||||
|
||||
| 错误码 | HTTP | 场景 | 触发条件 |
|
||||
| -------------------------------- | ---- | ---------------------------------------------------- | ----------------------------------------- |
|
||||
| `BFF_STUDENT_UNAUTHORIZED` | 401 | x-user-id 缺失或无效 | header 无 x-user-id 或为空 |
|
||||
| `BFF_STUDENT_FORBIDDEN_RESOURCE` | 403 | 学生越权访问他人数据(场景 A) | canAccessSelfData 返回 false |
|
||||
| `BFF_STUDENT_IDENTITY_MISMATCH` | 403 | JWT userId 与请求 body userId 不一致(场景 B) | Mutation submitHomework 等 body userId 不匹配 |
|
||||
|
||||
#### 1.7.4 P3 实现方式(§2.9 裁决)
|
||||
|
||||
依据 §2.9 裁决:
|
||||
|
||||
1. **P3 抽象 AuthorizationGuard 接口**(`canAccessSelfData`,见 §1.7.2)
|
||||
2. **P3 内部实现为"DEV_MODE 放行 + 生产拒绝"**(保守策略):
|
||||
- `DEV_MODE=true`:放行所有请求,仅记录 warn 日志
|
||||
- `DEV_MODE=false`:严格校验,越权返回 `BFF_STUDENT_FORBIDDEN_RESOURCE`
|
||||
3. **Redis 缓存**(P3 后期接入):
|
||||
- key: `authz:student:{userId}`
|
||||
- value: 用户基础信息(userId / roles / classIds)
|
||||
- TTL: 5min
|
||||
- AuthorizationGuard 优先查缓存,缓存未命中调 iam gRPC
|
||||
4. **此方案不违反"不分阶段"**:Guard 接口即最终方案,P3→P3 后期仅替换内部实现(属"跨阶段扩展例外",见 §3)
|
||||
|
||||
**P3 阶段生产环境**:Guard 全部拒绝时,student-bff P3 端到端验证仅限 DEV_MODE。生产环境 P3 不接入流量(仅 dev 测试),P3 后期接入真实校验后正式上线。
|
||||
|
||||
---
|
||||
|
||||
@@ -52,79 +312,288 @@ GraphQL schema 文件路径:`apps/student-bff/src/schema/*.graphql`(端口 :
|
||||
|
||||
### 2.1 gRPC 调用(同步)
|
||||
|
||||
| 被调用方 | Service.RPC | 用途 | mock 策略 |
|
||||
| --------------- | ----------------------------------------- | ---------------- | ------------------------------------------- |
|
||||
| iam (ai06) | IamService.GetUserInfo | 获取当前学生信息 | iam 就绪前返回固定 UserInfo(student 角色) |
|
||||
| iam (ai06) | IamService.GetEffectivePermissions | 权限校验 | iam 就绪前返回学生权限集 |
|
||||
| iam (ai06) | IamService.GetViewports | 学生导航菜单 | iam 就绪前返回固定视口列表 |
|
||||
| core-edu (ai08) | ClassService.GetClass | 我的班级详情 | core-edu 就绪前返回固定 ClassInfo |
|
||||
| core-edu (ai08) | ClassService.ListStudentsByClass | 班级同学名单 | core-edu 就绪前返回固定 30 个 StudentInfo |
|
||||
| core-edu (ai08) | ExamService.ListExamsByClass | 我的考试 | core-edu 就绪前返回固定 2 个 Exam |
|
||||
| core-edu (ai08) | HomeworkService.ListHomeworkByClass | 我的作业 | core-edu 就绪前返回固定 3 个 Homework |
|
||||
| core-edu (ai08) | HomeworkService.SubmitHomework | 提交作业 | core-edu 就绪前返回 success=true |
|
||||
| core-edu (ai08) | GradeService.ListGradesByStudent | 我的成绩 | core-edu 就绪前返回固定 5 个 Grade |
|
||||
| core-edu (ai08) | AttendanceService.ListAttendanceByStudent | 我的考勤 | core-edu 就绪前返回固定 10 条 Attendance |
|
||||
| content (ai09) | TextbookService.ListTextbooks | 教材列表 | content 就绪前返回固定 5 个教材 |
|
||||
| content (ai09) | ChapterService.ListChapters | 章节列表 | content 就绪前返回固定章节树 |
|
||||
| content (ai09) | KnowledgeGraphService.GetLearningPath | 学习路径 | content 就绪前返回固定 8 个知识点推荐顺序 |
|
||||
| data-ana (ai11) | AnalyticsService.GetStudentDashboard | 学生仪表盘 | data-ana 就绪前返回固定仪表盘 |
|
||||
| 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 |
|
||||
依据 B2 裁决,student-bff 对下游全部走 gRPC(禁止 HTTP fetch)。
|
||||
|
||||
| 被调用方 | Service.RPC | 用途 | 启用阶段 | mock 策略 |
|
||||
| --------------- | ----------------------------------------- | ---------------- | -------- | ------------------------------------------- |
|
||||
| iam (ai06) | IamService.GetUserInfo | 获取当前学生信息 | P3 | iam 就绪前返回固定 UserInfo(student 角色) |
|
||||
| iam (ai06) | IamService.GetEffectivePermissions | 权限校验 | P3 | iam 就绪前返回学生权限集 |
|
||||
| iam (ai06) | IamService.GetViewports | 学生导航菜单 | P3 | iam 就绪前返回固定视口列表 |
|
||||
| iam (ai06) | IamService.GetEffectiveDataScope | DataScope 透传 | P3 | iam 就绪前返回 `SELF` |
|
||||
| core-edu (ai08) | ClassService.GetClass | 我的班级详情 | P3 | core-edu 就绪前返回固定 ClassInfo |
|
||||
| core-edu (ai08) | ClassService.ListStudentsByClass | 班级同学名单 | P3 | core-edu 就绪前返回固定 30 个 StudentInfo |
|
||||
| core-edu (ai08) | ExamService.ListExamsByClass | 我的考试 | P3 | core-edu 就绪前返回固定 2 个 Exam |
|
||||
| core-edu (ai08) | HomeworkService.ListHomeworkByClass | 我的作业 | P3 | core-edu 就绪前返回固定 3 个 Homework |
|
||||
| core-edu (ai08) | HomeworkService.SubmitHomework | 提交作业 | P3 | core-edu 就绪前返回 success=true |
|
||||
| core-edu (ai08) | GradeService.ListGradesByStudent | 我的成绩 | P3 | core-edu 就绪前返回固定 5 个 Grade |
|
||||
| core-edu (ai08) | AttendanceService.ListAttendanceByStudent | 我的考勤 | P3 | core-edu 就绪前返回固定 10 条 Attendance |
|
||||
| content (ai09) | TextbookService.ListTextbooks | 教材列表 | P4 | content 就绪前返回固定 5 个教材 |
|
||||
| content (ai09) | ChapterService.ListChapters | 章节列表 | P4 | content 就绪前返回固定章节树 |
|
||||
| content (ai09) | KnowledgeGraphService.GetLearningPath | 学习路径 | P4 | content 就绪前返回固定 8 个知识点推荐顺序 |
|
||||
| data-ana (ai11) | AnalyticsService.GetStudentDashboard | 学生仪表盘 | P4 | data-ana 就绪前返回固定仪表盘 |
|
||||
| data-ana (ai11) | AnalyticsService.GetStudentWeakness | 我的薄弱点 | P4 | data-ana 就绪前返回固定 3 个 weak_points |
|
||||
| data-ana (ai11) | AnalyticsService.GetLearningTrend | 学习趋势 | P4 | data-ana 就绪前返回固定趋势数据 |
|
||||
| msg (ai10) | NotificationService.ListNotifications | 学生通知 | P5 | msg 就绪前返回固定 10 条通知 |
|
||||
| msg (ai10) | NotificationService.GetUnreadCount | 通知未读数 | P5 | msg 就绪前返回固定 count=3 |
|
||||
| msg (ai10) | NotificationService.MarkAsRead | 标记已读 | P5 | msg 就绪前返回 success=true |
|
||||
|
||||
> **命名对齐**(coord §5.2):
|
||||
> - `AnalyticsService.GetStudentDashboard`(无 Stats 后缀,统一命名)
|
||||
> - `KnowledgeGraphService.GetLearningPath`(禁用 ContentService 命名)
|
||||
|
||||
### 2.2 Kafka 事件订阅(异步)
|
||||
|
||||
无。student-bff 不订阅 Kafka 事件,仅做同步 gRPC 聚合。
|
||||
依据 B7 裁决:
|
||||
|
||||
### 2.3 HTTP 调用(如有)
|
||||
- **P3-P4 阶段**:**不订阅 Kafka**(仅同步 gRPC 聚合)
|
||||
- **P5 阶段**:push-gateway 落地后,评估是否订阅 Kafka(如 `edu.identity.user.role_changed` 用于权限缓存失效)
|
||||
|
||||
无。
|
||||
**P5 可选订阅的 topic**(待 P5 评估):
|
||||
|
||||
| Topic | 用途 | 触发动作 |
|
||||
| ------------------------------- | -------------------------- | ------------------------------------- |
|
||||
| `edu.identity.user.role_changed` | 学生角色变更,失效权限缓存 | iam 发布,student-bff 清除 Redis 缓存 |
|
||||
|
||||
> **P3-P4 降级方案**:权限缓存用短 TTL(5min)兜底,不订阅 Kafka 事件。
|
||||
|
||||
### 2.3 HTTP 调用
|
||||
|
||||
**无**。依据 B2 裁决,student-bff 对下游全部走 gRPC,禁止 HTTP fetch。
|
||||
|
||||
### 2.4 DownstreamClient 抽象层(B8 裁决)
|
||||
|
||||
依据 B8 裁决:
|
||||
|
||||
1. **回写 teacher-bff**:DownstreamClient 作为 BFF 模式 v2 标准抽象,回写到 teacher-bff,3 个 BFF(teacher-bff / student-bff / parent-bff)统一使用
|
||||
2. **抽象位置**:`packages/shared-ts/src/bff/downstream-client.ts`(coord 维护)
|
||||
3. **核心能力**:
|
||||
|
||||
```typescript
|
||||
export class DownstreamClient {
|
||||
/**
|
||||
* gRPC 调用封装
|
||||
* @param service 下游服务名(如 'iam' / 'core-edu')
|
||||
* @param method RPC 方法名(如 'GetUserInfo')
|
||||
* @param request 请求 message
|
||||
* @param options 超时 / 重试 / traceId 透传
|
||||
*/
|
||||
call<TRequest, TResponse>(
|
||||
service: string,
|
||||
method: string,
|
||||
request: TRequest,
|
||||
options?: CallOptions,
|
||||
): Promise<TResponse>;
|
||||
}
|
||||
|
||||
interface CallOptions {
|
||||
timeoutMs?: number; // 默认 5000ms
|
||||
retryCount?: number; // 默认 2
|
||||
retryBackoffMs?: number; // 默认 100ms,指数退避
|
||||
traceId?: string; // 从 x-request-id header 获取
|
||||
metadata?: Record<string, string>; // gRPC metadata(含 x-user-id / x-user-roles / x-dataScope)
|
||||
}
|
||||
```
|
||||
|
||||
4. **student-bff 使用方式**:
|
||||
|
||||
```typescript
|
||||
// services/student-bff/src/auth/auth.service.ts
|
||||
import { DownstreamClient } from '@edu/shared-ts/bff/downstream-client';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private readonly downstream: DownstreamClient) {}
|
||||
|
||||
async getCurrentUser(userId: string): Promise<UserInfo> {
|
||||
return this.downstream.call('iam', 'GetUserInfo', { userId }, {
|
||||
metadata: { 'x-user-id': userId },
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **回写义务**:ai04 在 P3 实现时,将 DownstreamClient 抽象回写到 teacher-bff(替换 teacher-bff 现有的散落 fetch 调用),保证 3 个 BFF 统一。
|
||||
|
||||
---
|
||||
|
||||
## §3 就绪信号
|
||||
## §3 跨阶段扩展例外规则(§2.3 裁决)
|
||||
|
||||
### 3.1 我依赖的上游就绪标志
|
||||
依据 president-final-rulings §2.3:
|
||||
|
||||
- [ ] iam gRPC 50052 启用(ai06)
|
||||
- [ ] core-edu gRPC 50053 启用(ai08)
|
||||
- [ ] content gRPC 50054 启用(ai09)
|
||||
- [ ] data-ana gRPC 50055 启用(ai11)
|
||||
- [ ] msg gRPC 50056 启用(ai10)
|
||||
### 3.1 允许扩展(无需 coord 仲裁)
|
||||
|
||||
### 3.2 我的就绪标志(供下游消费)
|
||||
1. 新增下游 gRPC 调用(新增 RPC 方法到 DownstreamClient)
|
||||
2. 新增下游配置(gRPC endpoint 配置)
|
||||
3. 新增 /readyz 探针(按阶段启用,见 §4)
|
||||
4. Dashboard Query Resolver 内部数据源扩展(null 字段 → 真实数据)
|
||||
5. AuthorizationGuard 内部实现替换(DEV_MODE 放行 → 真实 gRPC 校验 + Redis 缓存)
|
||||
|
||||
- [ ] student-bff GraphQL :3009 启用(/healthz 返回 200)
|
||||
- [ ] /readyz 返回 200(含 5 个下游 gRPC 连通性检查)
|
||||
- [ ] GraphQL schema 可内省(POST /graphql 返回 schema)
|
||||
- [ ] 核心 Query 可执行:currentUser / myClasses / studentDashboard / myGrades
|
||||
- [ ] 核心 Mutation 可执行:submitHomework / markAsRead
|
||||
### 3.2 禁止变更(需 coord 仲裁)
|
||||
|
||||
1. 修改已有 RPC 调用的签名或返回类型
|
||||
2. 删除已实现的 RPC 调用(除非下游服务下线)
|
||||
3. 修改 GraphQL schema 已有字段的类型(新增字段允许,无需仲裁)
|
||||
4. 修改 /readyz 已有探针的检查项(只能新增,不能修改)
|
||||
|
||||
### 3.3 验收标准
|
||||
|
||||
扩展时必须:
|
||||
|
||||
1. 更新 student-bff 02-architecture-design.md 下游调用矩阵(§7)
|
||||
2. 更新 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`
|
||||
3. 运行 `pnpm run arch:scan` 更新 arch.db
|
||||
4. coord 在批次验收时检查上述 3 项
|
||||
|
||||
---
|
||||
|
||||
## §4 Mock 策略
|
||||
## §4 /readyz 探针按阶段扩展规则(§2.4 裁决)
|
||||
|
||||
### 4.1 我提供的 mock
|
||||
依据 president-final-rulings §2.4 + G2 裁决:
|
||||
|
||||
在 student-bff 真实就绪前,为下游(student-portal)提供以下 mock:
|
||||
### 4.1 探针列表(按阶段)
|
||||
|
||||
- **GraphQL mock**:使用 Apollo Server mockProviders 或 MSW 拦截 POST /graphql
|
||||
- currentUser 返回固定学生(id="student-001", name="李同学", roles=["student"])
|
||||
- myClasses 返回固定 1 个班级
|
||||
- studentDashboard 返回固定仪表盘(avg_score=85.0, class_rank=5)
|
||||
- myGrades 返回固定 5 个成绩
|
||||
- myHomework 返回固定 3 个作业(1 个待提交)
|
||||
- myNotifications 返回固定 10 条通知
|
||||
student-bff 无 DB,探针仅检查 Redis + 下游 gRPC 可达性:
|
||||
|
||||
### 4.2 我消费的 mock
|
||||
| 阶段 | 探针列表 | 数量 |
|
||||
| ---- | ----------------------------------------------------- | ---- |
|
||||
| P3 | Redis PING + iam gRPC 50052 + core-edu gRPC 50053 | 3 |
|
||||
| P4 | + content gRPC 50054 + data-ana gRPC 50055 | 5 |
|
||||
| P5 | + ai gRPC 50058 + msg gRPC 50056 | 7 |
|
||||
|
||||
**实现方式**:`DownstreamHealthCheck` 注册表模式,每个下游注册独立探针,按阶段启用(通过 ENV 过滤):
|
||||
|
||||
```typescript
|
||||
const checks: HealthCheck[] = [
|
||||
checkRedis(),
|
||||
checkGrpc('iam', 50052),
|
||||
checkGrpc('core-edu', 50053),
|
||||
];
|
||||
if (env.CONTENT_GRPC_ENABLED) checks.push(checkGrpc('content', 50054));
|
||||
if (env.DATA_ANA_GRPC_ENABLED) checks.push(checkGrpc('data-ana', 50055));
|
||||
if (env.AI_GRPC_ENABLED) checks.push(checkGrpc('ai', 50058));
|
||||
if (env.MSG_GRPC_ENABLED) checks.push(checkGrpc('msg', 50056));
|
||||
```
|
||||
|
||||
### 4.2 软失败规则
|
||||
|
||||
依据 §2.4.3:
|
||||
|
||||
- **必需依赖**(Redis + 已启用 gRPC 下游):失败返回 503,触发 Pod 重启
|
||||
- **可选依赖**(未启用 gRPC 下游 / Kafka 订阅):失败仅告警,返回 200 + body `degraded: true`
|
||||
|
||||
**student-bff 软失败场景**:
|
||||
|
||||
| 依赖 | 类型 | 失败行为 |
|
||||
| ------------------- | ------ | ------------------------------------------- |
|
||||
| Redis | 必需 | 503(缓存失效会影响性能,但 BFF 仍可降级运行) |
|
||||
| iam gRPC 50052 | 必需 | 503(无 iam 无法做身份校验) |
|
||||
| core-edu gRPC 50053 | 必需 | 503(核心数据源) |
|
||||
| content gRPC 50054 | 可选(P4 启用前) | 200 + degraded=true |
|
||||
| data-ana gRPC 50055 | 可选(P4 启用前) | 200 + degraded=true |
|
||||
| ai gRPC 50058 | 可选(P5 启用前) | 200 + degraded=true |
|
||||
| msg gRPC 50056 | 可选(P5 启用前) | 200 + degraded=true |
|
||||
|
||||
> **P3 阶段**:content / data-ana / ai / msg 均为可选,P3 /readyz 仅检查 Redis + iam + core-edu(3 项)。
|
||||
|
||||
---
|
||||
|
||||
## §5 就绪信号
|
||||
|
||||
### 5.1 我依赖的上游就绪标志
|
||||
|
||||
| 上游依赖 | 就绪标志 | 责任方 | 完成期限 |
|
||||
| ---------------- | ------------------------------------------- | ------ | ------------- |
|
||||
| iam gRPC 50052 | iam /readyz 返回 200 + GetUserInfo RPC 可调 | ai06 | 批次 1 完成 |
|
||||
| core-edu gRPC 50053 | core-edu /readyz 返回 200 + ListExamsByClass RPC 可调 | ai08 | 批次 2 完成 |
|
||||
| content gRPC 50054 | content /readyz 返回 200 + ListTextbooks RPC 可调 | ai09 | 批次 3 完成 |
|
||||
| data-ana gRPC 50055 | data-ana /readyz 返回 200 + GetStudentDashboard RPC 可调 | ai11 | 批次 3 完成 |
|
||||
| msg gRPC 50056 | msg /readyz 返回 200 + ListNotifications RPC 可调 | ai10 | 批次 4 完成 |
|
||||
| GraphQL schema 仲裁 | coord 仲裁 student-bff schema 第一版 | coord | 批次 2 启动前 |
|
||||
| DownstreamClient 抽象 | packages/shared-ts/src/bff/downstream-client.ts 就绪 | coord | 批次 1 启动前 |
|
||||
| shared-go 包 | packages/shared-go/ 骨架就绪 | coord | 批次 0.14 |
|
||||
|
||||
### 5.2 我的就绪标志(供下游消费)
|
||||
|
||||
| 就绪标志 | 完成标准 | 完成阶段 |
|
||||
| ----------------------------------------- | ----------------------------------------------------------- | -------- |
|
||||
| student-bff GraphQL :3009 启用 | /healthz 返回 200 | P3 |
|
||||
| /readyz 返回 200(P3 探针 3 项) | Redis + iam gRPC + core-edu gRPC 全部通过 | P3 |
|
||||
| GraphQL schema 可内省 | POST /graphql `{ query: "{ __schema { types { name } } }" }` 返回 schema | P3 |
|
||||
| 核心 Query 可执行 | currentUser / myClasses / myExams / myHomework / myGrades 返回真实数据 | P3 |
|
||||
| 核心 Mutation 可执行 | submitHomework 可执行 | P3 |
|
||||
| AuthorizationGuard 接口就绪 | canAccessSelfData 接口已实现(DEV_MODE 放行) | P3 |
|
||||
| DownstreamClient 回写 teacher-bff 完成 | teacher-bff 已切换为 DownstreamClient 抽象 | P3 |
|
||||
| /readyz 扩展 P4 探针(5 项) | + content gRPC + data-ana gRPC | P4 |
|
||||
| /readyz 扩展 P5 探针(7 项) | + ai gRPC + msg gRPC | P5 |
|
||||
|
||||
---
|
||||
|
||||
## §6 Mock 策略
|
||||
|
||||
### 6.1 我提供的 mock(供 student-portal 消费)
|
||||
|
||||
在 student-bff 真实就绪前,为 ai14(student-portal)提供以下 mock:
|
||||
|
||||
**GraphQL mock 实现方式**:GraphQL Yoga 内置 mock 模式(`graphql-yoga` 的 `mocking` 配置)或 MSW 拦截 POST /graphql
|
||||
|
||||
| Query/Mutation | mock 返回 |
|
||||
| --------------------------- | ------------------------------------------------------------ |
|
||||
| `currentUser` | 固定学生(id="student-001", name="李同学", roles=["student"]) |
|
||||
| `myClasses` | 固定 1 个班级(id="cls-001", name="高三1班") |
|
||||
| `myExams` | 固定 2 个考试 |
|
||||
| `myHomework` | 固定 3 个作业(1 个待提交) |
|
||||
| `myGrades` | 固定 5 个成绩(avg_score=85.0) |
|
||||
| `myAttendance` | 固定 10 条考勤 |
|
||||
| `studentDashboard` | 固定仪表盘(avg_score=85.0, class_rank=5) |
|
||||
| `myNotifications` | 固定 10 条通知(3 条未读) |
|
||||
| `myNotificationUnreadCount` | 固定 count=3 |
|
||||
| `submitHomework` | 返回 success=true |
|
||||
| `markNotificationAsRead` | 返回 success=true |
|
||||
|
||||
> **P4 字段降级**:P3 阶段 `studentDashboard.weakness` / `myTrend` / `textbooks` / `chapters` / `learningPath` 返回 null + `extensions.warning = "field_unavailable_in_p3"`
|
||||
|
||||
### 6.2 我消费的 mock(上游未就绪时)
|
||||
|
||||
在真实上游就绪前,student-bff 使用以下 mock(详见 §2.1 mock 策略列):
|
||||
|
||||
- **iam mock**:固定 UserInfo + 学生权限 + 固定视口
|
||||
- **core-edu mock**:固定班级/同学/考试/作业/成绩/考勤
|
||||
- **content mock**:固定教材/章节/学习路径
|
||||
- **data-ana mock**:固定仪表盘/薄弱点/趋势
|
||||
- **msg mock**:固定通知列表 + MarkAsRead success
|
||||
| 上游 | mock 实现 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| iam | 固定 UserInfo + 学生权限集 + 固定视口 + DataScope=SELF |
|
||||
| core-edu | 固定班级/同学/考试/作业/成绩/考勤 |
|
||||
| content | 固定教材/章节/学习路径(P4 启用前) |
|
||||
| data-ana | 固定仪表盘/薄弱点/趋势(P4 启用前) |
|
||||
| msg | 固定通知列表 + GetUnreadCount + MarkAsRead success(P5 启用前) |
|
||||
|
||||
> 所有上游 mock 通过 gRPC client 拦截器实现,上游就绪后移除拦截器切换真实调用。
|
||||
> **mock 实现方式**:通过 DownstreamClient 的 gRPC client 拦截器实现,上游就绪后移除拦截器切换真实调用。具体:在 `DownstreamClient.call()` 内部检查 `env.MOCK_UPSTREAM=true`,若为 true 则返回 mock 数据,否则走真实 gRPC。
|
||||
|
||||
---
|
||||
|
||||
## §7 缓存策略(B6 裁决)
|
||||
|
||||
依据 B6 裁决 + 004 §6.2 BFF 混合读策略:
|
||||
|
||||
### 7.1 Redis 短缓存
|
||||
|
||||
| 缓存对象 | key 模式 | TTL | 失效策略 |
|
||||
| ----------------------- | ----------------------------------- | ----- | ----------------------- |
|
||||
| currentUser 聚合结果 | `bff:student:user:{userId}` | 30s | TTL 过期 |
|
||||
| myClasses 聚合结果 | `bff:student:classes:{userId}` | 30s | TTL 过期 |
|
||||
| studentDashboard 聚合结果 | `bff:student:dashboard:{userId}` | 5s | TTL 过期(实时性要求高)|
|
||||
| 权限列表 | `authz:student:{userId}` | 5min | P5 订阅 Kafka 事件失效 |
|
||||
| 视口配置 | `bff:student:viewports:{userId}` | 5min | TTL 过期 |
|
||||
|
||||
### 7.2 缓存规则
|
||||
|
||||
1. **仅缓存 Query**:Mutation 不缓存
|
||||
2. **缓存粒度**:按 Query + userId 维度缓存(学生数据隔离 SELF)
|
||||
3. **降级策略**:Redis 不可用时,直接走 gRPC 调用(不缓存),返回数据 + `degraded: true`(标记缓存降级)
|
||||
4. **缓存击穿防护**:使用 `ioredis` 的 `GETSET` 或单飞模式(同一 key 并发请求只发一个 gRPC 调用)
|
||||
|
||||
---
|
||||
|
||||
## §8 变更记录
|
||||
|
||||
| 日期 | 版本 | 变更 | 负责人 |
|
||||
| ---------- | ---- | -------------------------------------------------------------------- | ------ |
|
||||
| 2026-07-09 | v1 | 初始创建 | ai04 |
|
||||
| 2026-07-10 | v2 | 对齐 coord B1-B8 + president §2.2-2.9 裁决(schema 路径/错误响应/越权防御/DownstreamClient/跨阶段扩展/readyz 探针/降级模式/缓存策略) | ai04 |
|
||||
|
||||
@@ -1,24 +1,221 @@
|
||||
# student-bff 问题记录
|
||||
|
||||
> 负责人:ai04
|
||||
> 关联:[coord.md](../coord.md)、[contracts/student-bff_contract.md](../contracts/student-bff_contract.md)
|
||||
> 关联:[coord.md](../coord.md)、[contracts/student-bff_contract.md](../contracts/student-bff_contract.md)、[matrix.md](../matrix.md)
|
||||
> 规则:AI 遇到问题时在此追加条目,coord 仲裁后更新状态
|
||||
> 仲裁依据:[coord-final-decisions.md](../../coord-final-decisions.md) §2 BFF 专项裁决(B1-B8)、[president-final-rulings.md](../../president-final-rulings.md)
|
||||
|
||||
---
|
||||
|
||||
## 问题列表
|
||||
## §0 已有仲裁核查总结(2026-07-10 审查)
|
||||
|
||||
<!--
|
||||
追加条目格式:
|
||||
> 本次审查对历史 issues.md 中 ai04 提请的 4 项问题(ISSUE-028/029/030/031-ai04)逐一核查总裁裁决落地情况。
|
||||
|
||||
### ISSUE-[编号]-[AI标识]:[标题]
|
||||
| 编号 | 主题 | 总裁裁决章节 | 裁决要点 | 核查结果 |
|
||||
| ---- | ---- | ------------ | -------- | -------- |
|
||||
| ISSUE-028-ai04 | 02 文档与 B1+B2 裁决冲突需回写 | president §3.4 | ai04 须在批次 2 启动前回写 student-bff 02:B1 GraphQL + B2 gRPC + B8 DownstreamClient | ⚠️ **未执行**:02-architecture-design.md 仍为 REST 设计(§4 21 个 REST 端点 / §9.2 REST→GraphQL 演进 / §9.3 HTTP→gRPC 演进),违反 B1、B2 |
|
||||
| ISSUE-029-ai04 | P3 启动前置依赖确认(4 项强阻塞) | president §4.1 / §6.1 | 批次 0 完成信号机制 + 批次 2 启动条件:批次 1 P2.1 完成 + ai03 DownstreamClient 抽象就绪 | ✅ **已裁决**:批次时间线 §6.1 明确批次 2 启动条件;前置依赖检查清单机制已建立 |
|
||||
| ISSUE-030-ai04 | student-bff GraphQL schema 第一版仲裁时机 | president §2.2 | ai04 起草 schema(批次 1 等待期),coord 在批次 2 启动前仲裁第一版;存放 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` | ✅ **已裁决**:schema 仲裁机制已建立(§2.2);但 schema 第一版尚未起草,需 ai04 在批次 1 等待期产出 |
|
||||
| ISSUE-031-ai04 | issues.md 编号冲突 | president §0.4 | 保留原始内容不删除,用 `ISSUE-XXX-<提请AI>` 格式唯一定位,不重新编号 | ✅ **已裁决**:编号规则已生效,本文件即按新流程在 objections/ 下维护 |
|
||||
|
||||
- **提请方**:aiXX
|
||||
- **日期**:YYYY-MM-DD
|
||||
- **类型**:契约不明确 / 工作量超批 / 前置依赖缺失 / 编号冲突 / 其他
|
||||
- **描述**:[详细描述问题]
|
||||
- **建议方案**:[AI 的建议]
|
||||
- **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X)
|
||||
-->
|
||||
### 0.1 核查结论
|
||||
|
||||
(暂无问题)
|
||||
- **唯一未落地项**:ISSUE-028-ai04(02 文档回写)。02-architecture-design.md 当前内容与 B1/B2/B8 裁决严重冲突,须在批次 2 启动前完成回写。
|
||||
- **schema 第一版未起草**:ISSUE-030-ai04 虽已建立仲裁机制,但 ai04 尚未产出 student-bff GraphQL schema 草案,需在批次 1 等待期完成。
|
||||
- **01-understanding.md 同步问题**:阶段 1 文档同样存在 REST 假设与错误码前缀错误(详见 §2),需与 02 文档一并修正。
|
||||
|
||||
---
|
||||
|
||||
## §1 待 coord 仲裁的新问题(本次审查发现)
|
||||
|
||||
### ISSUE-STU-001-ai04:01-understanding.md 与 B1/B2/B5 裁决冲突
|
||||
|
||||
- **提请方**:ai04
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:裁决冲突(文档未回写)
|
||||
- **描述**:阶段 1 文档 `services/student-bff/docs/01-understanding.md` 存在 3 项与已裁决规则的冲突:
|
||||
1. **B1 冲突(API 风格)**:§3.2 暴露 14 个 REST 端点(`/student/dashboard` 等);§4 / §4.1 明确"先对齐 teacher-bff 现状(REST + fetch)",建议"P3 阶段先 REST,后续统一升级 GraphQL"。与 B1"P2 起直接 GraphQL"冲突,属禁止的"中间过渡方案"。
|
||||
2. **B2 冲突(下游通信)**:§3.1 表述"BFF→Service 走 HTTP fetch(当前阶段)";§4 技术栈"HTTP fetch(当前阶段,对齐 teacher-bff 模式)"。与 B2"首次实现即 gRPC 调用下游"冲突。
|
||||
3. **B5 冲突(错误码前缀)**:§3.3 / §6 表格用 `STUDENT_BFF_` 前缀。与 B5"统一 BFF_ 前缀(BFF_TEACHER_ / BFF_STUDENT_ / BFF_PARENT_)"冲突。
|
||||
- **建议方案**:与 ISSUE-028-ai04 合并处理,01 文档随 02 文档一并回写:
|
||||
- 删除 REST 端点清单,改为 GraphQL Query/Mutation 清单
|
||||
- 删除"HTTP fetch 对齐 teacher-bff 现状"表述,改为"gRPC 调用下游(@grpc/grpc-js + @bufbuild/protobuf)"
|
||||
- 错误码前缀统一为 `BFF_STUDENT_`
|
||||
- §7.2"待 coord 仲裁"项中,B1/B2/B3/B5/B6/B7 已裁决,删除重复提请
|
||||
- **状态**:待 coord 确认回写范围(是否 01 文档也纳入回写义务)
|
||||
|
||||
### ISSUE-STU-002-ai04:02-architecture-design.md 引用不存在的 004 章节
|
||||
|
||||
- **提请方**:ai04
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:契约不明确(文档引用错误)
|
||||
- **描述**:02-architecture-design.md 多处引用"004 §11.4 错误码前缀矩阵"和"004 §11.5 统一响应信封 ActionState",但实际 004_architecture_impact_map.md §11 仅包含:
|
||||
- §11.1 Protobuf 契约体系
|
||||
- §11.2 契约规则
|
||||
- §11.3 BFF 聚合模式
|
||||
- **不存在 §11.4 和 §11.5**
|
||||
- **影响**:错误码前缀 BFF_STUDENT_ 的权威来源应改为 [coord-final-decisions.md](../../coord-final-decisions.md) G14 + B5;ActionState 信封规范应改为 [coord-final-decisions.md](../../coord-final-decisions.md) G8 + F9
|
||||
- **建议方案**:02 文档回写时修正引用源;coord 确认是否需要在 004 补充 §11.4/§11.5 章节,或统一指向 coord-final-decisions
|
||||
- **状态**:待 coord 仲裁
|
||||
|
||||
### ISSUE-STU-003-ai04:02 文档 §8.3 列 12 项未决决策,其中 8 项已裁决
|
||||
|
||||
- **提请方**:ai04
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:裁决冲突(文档未回写)
|
||||
- **描述**:02-architecture-design.md §8.3"未决设计决策(待 coord 仲裁)"列出 12 项,但其中 8 项已被 coord-final-decisions §2 裁决:
|
||||
|
||||
| # | 决策点 | 02 文档建议 | 已裁决结论 | 裁决章节 |
|
||||
| --- | ------ | ----------- | ---------- | -------- |
|
||||
| 1 | BFF API 风格 | A(P3 REST) | **B1:P2 起直接 GraphQL** | coord-final-decisions §2 B1 |
|
||||
| 2 | BFF 是否做权限校验 | A(不校验) | **B3:BFF 豁免 @RequirePermission** | coord-final-decisions §2 B3 |
|
||||
| 3 | 自我越权防御 | B(做) | **B4:全部 BFF 强制自我越权防御** | coord-final-decisions §2 B4 |
|
||||
| 4 | /readyz 检查逻辑 | A(P3 直接 ok) | **G2 + §2.4:按阶段扩展探针,必需依赖失败 503,可选依赖软失败** | president §2.4 |
|
||||
| 5 | Kafka 事件订阅时机 | A(P3 不订阅) | **B7:P2-P4 不订阅 Kafka,P5 后订阅** | coord-final-decisions §2 B7 |
|
||||
| 6 | 缓存策略 | B(Redis 5-30s) | **B6:Redis 5-30s 短缓存** | coord-final-decisions §2 B6 |
|
||||
| 8 | 错误码前缀 | BFF_STUDENT_ | **B5:BFF_STUDENT_** | coord-final-decisions §2 B5 |
|
||||
| 9 | DownstreamClient 回写 | B(回写) | **B8:回写 teacher-bff,3 个 BFF 统一** | coord-final-decisions §2 B8 |
|
||||
|
||||
仅剩 #7(端口 3009)、#10(CQRS 不引入)、#11(SSE 实现)、#12(熔断器引入)属合理的设计决策,但 #12 熔断器 president §2.4 已暗示按阶段评估。
|
||||
- **建议方案**:02 文档回写时删除已裁决项的"待仲裁"标注,改为"已裁决(见 coord-final-decisions §2 BX)"
|
||||
- **状态**:待 coord 确认(与 ISSUE-028-ai04 合并处理)
|
||||
|
||||
### ISSUE-STU-004-ai04:student-bff GraphQL schema 第一版尚未起草
|
||||
|
||||
- **提请方**:ai04
|
||||
- **日期**:2026-07-10
|
||||
- **类型**:前置依赖缺失
|
||||
- **描述**:president §2.2 裁决"ai04 起草 student-bff schema(批次 1 等待期),coord 在批次 2 启动前仲裁第一版"。当前批次 1 已启动(2026-07-10),ai04 处于批次 1 等待期,但 schema 草案尚未产出。02-architecture-design.md 仍是 REST 设计,未定义 GraphQL Query/Mutation/Type。
|
||||
- **影响**:阻塞 ai14(student-portal)P3 启动(依赖 schema 契约);阻塞 coord 批次 2 启动前仲裁
|
||||
- **建议方案**:ai04 在批次 1 等待期优先产出 student-bff GraphQL schema 草案,存放 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`,提交 coord 仲裁
|
||||
- **状态**:ai04 自行执行(非 coord 仲裁项),记录待办
|
||||
|
||||
---
|
||||
|
||||
## §2 01-understanding.md 审查详情
|
||||
|
||||
### 2.1 准确性问题
|
||||
|
||||
| 位置 | 问题 | 严重度 |
|
||||
| ---- | ---- | ------ |
|
||||
| §1 通信方式(入/出) | 表述"HTTP REST(当前阶段)/ HTTP fetch(当前阶段)",实际 B1/B2 裁决为 GraphQL + gRPC | 高 |
|
||||
| §3.1 表格 | 列"当前 REST 端点(实际可用)"列,暗示走 REST,但 BFF 是新服务不存在"当前 REST 现状" | 高 |
|
||||
| §3.2 端点表 | 14 个 REST 端点,违反 B1 GraphQL | 高 |
|
||||
| §3.3 错误码前缀 | `STUDENT_BFF_` 违反 B5 `BFF_STUDENT_` | 中 |
|
||||
| §4 技术栈 API 风格 | "HTTP REST(当前阶段)",违反 B1 | 高 |
|
||||
| §6 权限装饰器 | "⚠️ 不对齐"结论正确(B3 豁免),但理由应补充"已裁决 B3" | 低 |
|
||||
| §7.2 决策点 1-5 | 列为"待 coord 仲裁",但 B1/B2/B3/B5/B6/B7 均已裁决 | 高 |
|
||||
|
||||
### 2.2 遗漏项
|
||||
|
||||
| 遗漏内容 | 应补充位置 | 依据 |
|
||||
| -------- | ---------- | ---- |
|
||||
| GraphQL schema 设计意图(Query/Mutation/Type) | §3.2 | B1 + president §2.2 |
|
||||
| gRPC 下游调用设计(@grpc/grpc-js + @bufbuild/protobuf) | §3.1 / §4 | B2 |
|
||||
| DataLoader 防 N+1 策略 | §4 | 004 §11.3 + B1 |
|
||||
| B4 自我越权防御(userId 强制比对) | §2.3 / §3 | B4 |
|
||||
| B8 DownstreamClient 抽象(复用 teacher-bff) | §4 / §6 | B8 |
|
||||
| GraphQL schema 存放路径 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` | §3.2 | president §2.2 |
|
||||
| GraphQL errors 数组 + extensions.code + extensions.traceId 错误格式 | §3.3 | president §2.2 #5 |
|
||||
| Relay Cursor Connections 分页规范 | §3.2 | president §2.2 #5 |
|
||||
|
||||
---
|
||||
|
||||
## §3 02-architecture-design.md 审查详情
|
||||
|
||||
### 3.1 架构合理性评估
|
||||
|
||||
| 维度 | 评估 | 说明 |
|
||||
| ---- | ---- | ---- |
|
||||
| 分层设计(§1) | ✅ 合理 | Controller → Service → Aggregator → Cache → DownstreamClient 五层清晰,DownstreamClient/Aggregator/Transformer 抽象优于 teacher-bff 现状,符合 B8 |
|
||||
| 领域模型(§2) | ✅ 合理 | "场景聚合视图"概念恰当,BFF 无领域模型;DataScope=SELF 强制实现(§2.3)符合 B4 |
|
||||
| 缓存设计(§3) | ✅ 合理 | Redis Key 规范、TTL 分档、失效策略完整,符合 B6 |
|
||||
| API 设计(§4) | ❌ 严重冲突 | 21 个 REST 端点违反 B1 GraphQL;须改为 GraphQL Query/Mutation |
|
||||
| 事件设计(§5) | ⚠️ 部分冲突 | 订阅清单合理,但 B7 裁决 P2-P4 不订阅 Kafka,§5 应明确"P5 才落地"且标注 B7 |
|
||||
| 横切关注点(§6) | ✅ 合理 | logger/metrics/tracer/health/优雅关闭对齐黄金模板;错误码 BFF_STUDENT_ 正确(§0.2 已修正) |
|
||||
| 契约矩阵(§7) | ⚠️ 需更新 | 下游通信"HTTP→gRPC P3+"表述违反 B2,应改为"gRPC 首次实现即用" |
|
||||
| 风险与假设(§8) | ⚠️ 需更新 | §8.3 12 项未决决策中 8 项已裁决(见 ISSUE-STU-003) |
|
||||
| 演进路线(§9) | ❌ 严重冲突 | §9.2 REST→GraphQL、§9.3 HTTP→gRPC 违反"不分阶段"原则(president §0.3) |
|
||||
| 扩展点(§10) | ✅ 优秀 | 多端适配/国际化/多角色复用/离线模式/AI 增强/学习路径预留设计前瞻 |
|
||||
| 性能容量(§11) | ✅ 合理 | SLO 分级、容量规划、限流策略完整 |
|
||||
| 安全合规(§12) | ✅ 合理 | 身份认证/授权隔离/输入安全/数据合规/审计完整 |
|
||||
| 可观测性(§13) | ✅ 合理 | 日志规范/span/告警/Grafana 面板完整 |
|
||||
|
||||
### 3.2 长远性评估
|
||||
|
||||
| 长远性维度 | 评估 | 说明 |
|
||||
| ---------- | ---- | ---- |
|
||||
| 多角色复用(§9.5) | ✅ | 学习委员/课代表/走读生差异化通过视口扩展,无需改代码 |
|
||||
| 多端适配(§10.1) | ✅ | Transformer 层按 x-client-type 裁剪,H5/小程序可扩展 |
|
||||
| GraphQL 演进(§9.2) | ❌ | 规划为"P6+ 可选",但 B1 已裁决 GraphQL 是起点非终点,须移除"可选" |
|
||||
| 通信协议演进(§9.3) | ❌ | 规划"P3 HTTP → P4 gRPC 混合 → P6 Service Mesh",违反 B2"首次实现即 gRPC" |
|
||||
| 推送通道演进(§9.4) | ✅ | P3 无推送 → P5 SSE → P5+ WebSocket → P6+ 移动端推送,渐进合理 |
|
||||
| AI 答疑增强(§10.5) | ✅ | 预留 context 参数,支持多步编排 |
|
||||
| 国际化(§10.2) | ✅ | 预留 I18nContext 接入点 |
|
||||
|
||||
### 3.3 业界架构文档规范符合度
|
||||
|
||||
| 规范项 | 符合度 | 说明 |
|
||||
| ------ | ------ | ---- |
|
||||
| 文档导航/导读 | ✅ | §0.3 文档结构清晰,16 章覆盖完整 |
|
||||
| 设计原则 | ✅ | §0.1 列 P1-P9 九项原则 |
|
||||
| 架构图(C4 模型) | ✅ | §1 物理分层图 + 调用链时序图,符合 C4 Level 2/3 |
|
||||
| ADR 决策记录 | ⚠️ | §8.3 列决策但未用 ADR 格式,且已裁决项未更新 |
|
||||
| 非功能性需求 | ✅ | §11 性能 SLO + §12 安全 + §13 可观测性 |
|
||||
| 演进路线 | ⚠️ | 有 §9 但违反"不分阶段"原则 |
|
||||
| 实施清单 | ✅ | §14 P3-P6 分阶段清单完整 |
|
||||
| 风险登记 | ✅ | §8.1 技术风险 + §8.2 外部依赖假设 |
|
||||
|
||||
### 3.4 关键遗漏
|
||||
|
||||
| 遗漏内容 | 应补充位置 | 依据 |
|
||||
| -------- | ---------- | ---- |
|
||||
| GraphQL Schema 完整定义(Query/Mutation/Type/Enum) | 新增 §4.2 或独立 §5 | B1 + president §2.2 |
|
||||
| DataLoader 批量策略(哪些 Query 需要 DataLoader) | §1.2 或 §6 | 004 §11.3 + B1 |
|
||||
| gRPC client 设计(channel 复用、interceptor、metadata 透传) | §1.2 或 §7 | B2 |
|
||||
| GraphQL errors 数组扩展 ActionState 字段规范 | §6.2 错误码清单 | president §2.2 #3 |
|
||||
| Relay Cursor Connections 分页规范 | §4 API 设计 | president §2.2 #5 |
|
||||
| GraphQL schema 存放路径与 codegen 配置 | §14 实施清单 | president §2.2 #4 |
|
||||
| AuthorizationGuard 接口设计(B4 越权防御 P3 实现方式) | §2.3 或 §6 | president §2.9(参照 teacher-bff ISSUE-033-ai03) |
|
||||
|
||||
---
|
||||
|
||||
## §4 历史问题归档
|
||||
|
||||
> 以下问题原记录在 `docs/issues.md`(旧流程),现按新流程归档至此。总裁裁决详见 [president-final-rulings.md](../../president-final-rulings.md) §10 问题索引表。
|
||||
|
||||
### ISSUE-028-ai04:ai04 02-architecture-design.md 与 coord B1+B2 裁决冲突需回写
|
||||
|
||||
- **提请方**:ai04(student-bff + parent-bff)
|
||||
- **日期**:2026-07-09
|
||||
- **类型**:裁决冲突(文档回写义务)
|
||||
- **描述**:02-architecture-design.md 与 coord-final-decisions B1(P2 起直接 GraphQL)+ B2(首次实现即 gRPC)存在 2 项重大冲突(§4 API 设计决策为 REST;§9.2 演进路线 REST→GraphQL)
|
||||
- **裁决**:president §3.4 — ai04 须在批次 2 启动前回写 student-bff 02:B1 GraphQL + B2 gRPC + B8 DownstreamClient
|
||||
- **状态**:⚠️ 未执行(详见 §0 核查)
|
||||
|
||||
### ISSUE-029-ai04:ai04 P3 启动前置依赖确认(4 项强阻塞)
|
||||
|
||||
- **提请方**:ai04(student-bff + parent-bff)
|
||||
- **日期**:2026-07-09
|
||||
- **类型**:前置依赖未就绪
|
||||
- **描述**:P3 启动依赖 4 项强阻塞前置(core_edu.proto 补全 / buf.gen.yaml gRPC 插件 / ai03 DownstreamClient 抽象 / ai08 core-edu gRPC server)
|
||||
- **裁决**:president §4.1 / §6.1 — 批次 0 完成信号机制 + 批次 2 启动条件明确
|
||||
- **状态**:✅ 已裁决
|
||||
|
||||
### ISSUE-030-ai04:ai04⟷ai14 student-bff GraphQL schema 第一版仲裁时机
|
||||
|
||||
- **提请方**:ai04(student-bff + parent-bff)
|
||||
- **日期**:2026-07-09
|
||||
- **类型**:契约不明确
|
||||
- **描述**:student-bff GraphQL schema 第一版仲裁时机未明确
|
||||
- **裁决**:president §2.2 — ai04 起草 schema(批次 1 等待期),coord 在批次 2 启动前仲裁第一版
|
||||
- **状态**:✅ 已裁决(schema 第一版待 ai04 起草,见 ISSUE-STU-004)
|
||||
|
||||
### ISSUE-031-ai04:issues.md 编号冲突
|
||||
|
||||
- **提请方**:ai04(student-bff + parent-bff)
|
||||
- **日期**:2026-07-09
|
||||
- **类型**:工作归属不明(文档规范)
|
||||
- **描述**:issues.md ISSUE-024/025 编号冲突(ai11 与 ai09 重复)
|
||||
- **裁决**:president §0.4 — 保留原始内容,用 `ISSUE-XXX-<提请AI>` 格式定位,不重新编号
|
||||
- **状态**:✅ 已裁决
|
||||
|
||||
@@ -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) |
|
||||
|
||||
Reference in New Issue
Block a user