feat: auto committed
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
# teacher-bff 对接契约
|
# teacher-bff 对接契约
|
||||||
|
|
||||||
> 负责人:ai03
|
> 负责人:ai03
|
||||||
> 关联:[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)、[ai.proto](../../../packages/shared-proto/proto/ai.proto)
|
> 版本:v2(2026-07-10,对齐 president §2.7/§2.17/§5.1 + coord B5/B8 + port-allocation §5)
|
||||||
|
> 关联:[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)、[ai.proto](../../../../packages/shared-proto/proto/ai.proto)、[coord-final-decisions.md §2 B1-B8](../../coord-final-decisions.md)、[president-final-rulings.md §2.7/§2.17/§5.1](../../president-final-rulings.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -9,42 +10,77 @@
|
|||||||
|
|
||||||
### 1.1 gRPC 接口(如有)
|
### 1.1 gRPC 接口(如有)
|
||||||
|
|
||||||
无对外 gRPC。teacher-bff 是 GraphQL 聚合层。
|
无对外 gRPC。teacher-bff 是 GraphQL 聚合层(B1 裁决:P2 起直接 GraphQL)。
|
||||||
|
|
||||||
### 1.2 HTTP 端点(如有)
|
### 1.2 HTTP 端点(如有)
|
||||||
|
|
||||||
| Method | Path | 用途 | 认证 |
|
| Method | Path | 用途 | 认证 | 裁决依据 |
|
||||||
| ------ | -------- | ----------------------------------------- | ----------------------- |
|
| ------ | -------- | ----------------------------------------- | ----------------------- | -------- |
|
||||||
| POST | /graphql | 教师 BFF GraphQL 端点 | JWT 必需 + teacher 角色 |
|
| POST | /graphql | 教师 BFF GraphQL 端点 | JWT 必需 + teacher 角色 | B1 |
|
||||||
| GET | /graphql | GraphQL Playground(开发环境) | 开发环境公开 |
|
| GET | /graphql | GraphQL Playground(开发环境,生产关闭) | 开发环境公开 | ARB-001 |
|
||||||
| GET | /healthz | 健康检查(liveness) | 公开 |
|
| GET | /healthz | 健康检查(liveness) | 公开 | G3 |
|
||||||
| GET | /readyz | 就绪检查(readiness,含下游 gRPC 连通性) | 公开 |
|
| GET | /readyz | 就绪检查(readiness,按阶段扩展下游探针) | 公开 | G2 + §2.4 |
|
||||||
|
|
||||||
### 1.3 GraphQL schema(如 BFF)
|
### 1.3 GraphQL schema(如 BFF)
|
||||||
|
|
||||||
GraphQL schema 文件路径:`apps/teacher-bff/src/schema/*.graphql`(端口 :3003)
|
**SDL-first**(president §2.17 裁决),schema 文件路径:
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql
|
||||||
|
```
|
||||||
|
|
||||||
|
> 文件命名遵循 president §2.17 统一规范 `<bff-name>.schema.graphql`,由 ai03 起草、coord 在批次 1 启动前仲裁第一版。
|
||||||
|
> 前端 AI(ai13/ai16)通过 graphql-codegen 生成 TS 类型消费。
|
||||||
|
|
||||||
核心 Query / Mutation 域:
|
核心 Query / Mutation 域:
|
||||||
|
|
||||||
- **auth**:currentUser(聚合 iam.GetUserInfo + GetEffectivePermissions + GetViewports)
|
| 域 | Query / Mutation | 聚合下游 | 阶段 |
|
||||||
- **classes**:myClasses(聚合 core-edu.ClassService.GetClassesByTeacher)
|
| -------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ |
|
||||||
- **students**:classStudents(聚合 core-edu.ClassService.ListStudentsByClass + iam.BatchGetUsers 补用户名)
|
| **auth** | currentUser | iam.GetUserInfo + GetEffectivePermissions + GetViewports | P2 |
|
||||||
- **exams**:classExams / createExam / updateExam(聚合 core-edu.ExamService)
|
| **dashboard** | teacherDashboard | P2: iam gRPC(用户基础信息,下游字段返 null + extensions.warning)<br>P3+: data-ana.GetTeacherDashboard | P2 null → P4 真实 |
|
||||||
- **homework**:classHomework / assignHomework(聚合 core-edu.HomeworkService)
|
| **classes** | myClasses | P2: iam gRPC(按 president §3.5,P2 班级列表来自 iam 数据)<br>P3+: core-edu.ClassService.GetClassesByTeacher | P2 iam → P3 core-edu |
|
||||||
- **grades**:studentGrades / recordGrade(聚合 core-edu.GradeService)
|
| **students** | classStudents | core-edu.ClassService.ListStudentsByClass + iam.BatchGetUsers | P3+ |
|
||||||
- **attendance**:classAttendance / recordAttendance(聚合 core-edu.AttendanceService)
|
| **exams** | classExams / createExam / updateExam / deleteExam | core-edu.ExamService | P3+ |
|
||||||
- **content**:textbooks / chapters / knowledgePoints / questions(聚合 content 4 个 Service)
|
| **homework** | classHomework / assignHomework | core-edu.HomeworkService | P3+ |
|
||||||
- **dashboard**:teacherDashboard(聚合 data-ana.AnalyticsService.GetTeacherDashboard)
|
| **grades** | studentGrades / recordGrade | core-edu.GradeService | P3+ |
|
||||||
- **notifications**:myNotifications / markAsRead(聚合 msg.NotificationService)
|
| **attendance** | classAttendance / recordAttendance | core-edu.AttendanceService(C4 裁决 P3 补全) | P3+ |
|
||||||
- **ai**:aiChat / generateQuestion / generateLessonPlan(聚合 ai.AiService)
|
| **content** | textbooks / chapters / knowledgePoints / questions | content 4 个 Service | P4+ |
|
||||||
|
| **notifications** | myNotifications / markAsRead | msg.NotificationService | P5+ |
|
||||||
|
| **ai** | aiChat / generateQuestion / generateLessonPlan | ai.AiService(A4 裁决 P5 补全 GenerateLessonPlan) | P5+ |
|
||||||
|
| **admin.\*** | admin.schoolStats / admin.listClasses / admin.listTeachers 等 | admin 命名空间,复用 teacher-bff endpoint(president §5.1) | P2 预留 schema / P6 实现 |
|
||||||
|
|
||||||
|
**admin 命名空间预留**(president §5.1 + §7.3 强制):
|
||||||
|
|
||||||
|
- **P2 预留**:schema 文件中预留 `admin.*` Query/Mutation 命名空间占位(含类型定义但 Resolver 返 null)
|
||||||
|
- **P6 实现**:ai16 admin-portal 复用 teacher-bff GraphQL endpoint,ai03 在 P6 实现 admin Resolver 真实数据
|
||||||
|
- **理由**:admin 操作低 QPS,无需独立 admin-bff 服务;减少 ai16 工作量
|
||||||
|
|
||||||
### 1.4 Kafka 事件发布(如有)
|
### 1.4 Kafka 事件发布(如有)
|
||||||
|
|
||||||
无。teacher-bff 不发布事件,仅做 gRPC 聚合。
|
无。teacher-bff 不发布事件,仅做 gRPC 聚合(B7 裁决:P2-P4 不订阅 Kafka,P5 push-gateway 落地后再订阅)。
|
||||||
|
|
||||||
### 1.5 错误码前缀
|
### 1.5 错误码前缀
|
||||||
|
|
||||||
`BFF_TEACHER_`(如 BFF_TEACHER_UPSTREAM_UNAVAILABLE、BFF_TEACHER_AGGREGATION_FAILED、BFF_TEACHER_FORBIDDEN)
|
`BFF_TEACHER_`(B5 + G14 裁决,统一 BFF_ 前缀)。
|
||||||
|
|
||||||
|
**BFF 越权防御 3 类错误码**(president §2.7 裁决):
|
||||||
|
|
||||||
|
| 错误码 | HTTP | 场景 | i18n key |
|
||||||
|
| -------------------------------- | ---- | ---------------------------------------------------- | ------------------------------------- |
|
||||||
|
| `BFF_TEACHER_UNAUTHORIZED` | 401 | x-user-id 缺失或无效 | `error.bffTeacher.unauthorized` |
|
||||||
|
| `BFF_TEACHER_FORBIDDEN_RESOURCE` | 403 | teacherId 与资源无归属关系(场景 A) | `error.bffTeacher.forbidden_resource` |
|
||||||
|
| `BFF_TEACHER_IDENTITY_MISMATCH` | 403 | JWT teacherId 与请求 body teacherId 不一致(场景 B) | `error.bffTeacher.identity_mismatch` |
|
||||||
|
|
||||||
|
**其他 BFF_TEACHER_ 错误码**(聚合层):
|
||||||
|
|
||||||
|
| 错误码 | HTTP | 场景 |
|
||||||
|
| ------------------------------------- | ---- | -------------------------------------- |
|
||||||
|
| `BFF_TEACHER_UPSTREAM_UNAVAILABLE` | 502 | 下游 gRPC 不可达 |
|
||||||
|
| `BFF_TEACHER_AGGREGATION_FAILED` | 500 | 聚合多下游时部分失败且无降级数据 |
|
||||||
|
| `BFF_TEACHER_VALIDATION_FAILED` | 400 | 输入参数校验失败 |
|
||||||
|
| `BFF_TEACHER_INTERNAL_ERROR` | 500 | 兜底内部错误 |
|
||||||
|
|
||||||
|
**B3 裁决澄清**:BFF 豁免 `@RequirePermission` 指不做"功能权限决策",但必须做"数据权限防御"(B4 越权防御,见 §3.3)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -52,38 +88,115 @@ GraphQL schema 文件路径:`apps/teacher-bff/src/schema/*.graphql`(端口 :
|
|||||||
|
|
||||||
### 2.1 gRPC 调用(同步)
|
### 2.1 gRPC 调用(同步)
|
||||||
|
|
||||||
| 被调用方 | Service.RPC | 用途 | mock 策略 |
|
**B2 裁决**:首次实现即 gRPC 调用下游,禁止 REST fetch 过渡。
|
||||||
| --------------- | ------------------------------------- | ---------------- | ----------------------------------------------- |
|
**B8 裁决**:通过 `DownstreamClient` 抽象统一封装(BFF 模式 v2 标准抽象,3 个 BFF 共用)。
|
||||||
| iam (ai06) | IamService.GetUserInfo | 获取当前教师信息 | iam 就绪前返回固定 UserInfo(teacher 角色) |
|
|
||||||
| iam (ai06) | IamService.BatchGetUsers | 批量补全学生姓名 | iam 就绪前返回固定用户名("学生001"~"学生030") |
|
> **RPC 现状标注说明**:
|
||||||
| iam (ai06) | IamService.GetEffectivePermissions | 权限校验 | iam 就绪前返回全权限(放行) |
|
> - ✅ = proto 中已定义(按 packages/shared-proto/proto/ 现状核对)
|
||||||
| iam (ai06) | IamService.GetViewports | 教师导航菜单 | iam 就绪前返回固定视口列表 |
|
> - ❌ = proto 中未定义,待 coord 补全(已在裁决中明确补全时机)
|
||||||
| core-edu (ai08) | ClassService.GetClassesByTeacher | 教师班级列表 | core-edu 就绪前返回固定 3 个 ClassInfo |
|
|
||||||
| core-edu (ai08) | ClassService.ListStudentsByClass | 班级学生名单 | core-edu 就绪前返回固定 30 个 StudentInfo |
|
#### 2.1.1 iam (ai06) — gRPC 50052
|
||||||
| core-edu (ai08) | ExamService.* | 考试管理 | core-edu 就绪前返回固定考试数据 |
|
|
||||||
| core-edu (ai08) | HomeworkService.* | 作业管理 | core-edu 就绪前返回固定作业数据 |
|
| Service.RPC | 用途 | 现状 | 阶段 | mock 策略 |
|
||||||
| core-edu (ai08) | GradeService.* | 成绩管理 | core-edu 就绪前返回固定成绩数据 |
|
| --------------------------------- | ---------------- | ---- | ---- | ------------------------------------------- |
|
||||||
| core-edu (ai08) | AttendanceService.* | 考勤管理 | core-edu 就绪前返回固定考勤数据 |
|
| IamService.GetUserInfo | 获取当前教师信息 | ✅ | P2 | 返回固定 UserInfo(teacher 角色) |
|
||||||
| content (ai09) | TextbookService.ListTextbooks | 教材列表 | content 就绪前返回固定 5 个教材 |
|
| IamService.GetViewports | 教师导航菜单 | ❌ | P2 | 返回固定视口列表(待 coord 补 proto) |
|
||||||
| content (ai09) | ChapterService.ListChapters | 章节列表 | content 就绪前返回固定章节树 |
|
| IamService.GetEffectivePermissions | 权限校验 | ❌ | P2 | 返回全权限(放行) |
|
||||||
| content (ai09) | KnowledgeGraphService.* | 知识图谱 | content 就绪前返回固定知识点 |
|
| IamService.BatchGetUsers | 批量补全学生姓名 | ❌ | P3+ | 返回固定用户名("学生001"~"学生030") |
|
||||||
| content (ai09) | QuestionService.SearchQuestions | 题库检索 | content 就绪前返回固定 20 题 |
|
| IamService.GetEffectiveDataScope | 数据范围校验 | ❌ | P3+ | 返回 OWN 范围 |
|
||||||
| data-ana (ai11) | AnalyticsService.GetTeacherDashboard | 教师仪表盘 | data-ana 就绪前返回固定仪表盘数据 |
|
| IamService.GetChildrenByParent | (parent-bff 用)| ❌ | P4 | teacher-bff 不调用 |
|
||||||
| data-ana (ai11) | AnalyticsService.GetClassPerformance | 班级成绩分析 | data-ana 就绪前返回固定分析数据 |
|
|
||||||
| data-ana (ai11) | AnalyticsService.GetWarningList | 预警列表 | data-ana 就绪前返回固定 5 条预警 |
|
> iam.proto 现状仅 4 RPC(Register/Login/RefreshToken/GetUserInfo),matrix.md §2 标称 12 RPC,待 ai06 + coord 按 I1-I8 裁决补全。
|
||||||
| msg (ai10) | NotificationService.ListNotifications | 教师通知列表 | msg 就绪前返回固定 10 条通知 |
|
|
||||||
| msg (ai10) | NotificationService.MarkAsRead | 标记已读 | msg 就绪前返回 success=true |
|
#### 2.1.2 core-edu (ai08) — gRPC 50053
|
||||||
| ai (ai12) | AiService.Chat | AI 对话 | ai 就绪前返回固定回复 |
|
|
||||||
| ai (ai12) | AiService.GenerateQuestion | AI 出题 | ai 就绪前返回固定题目 |
|
| Service.RPC | 用途 | 现状 | 阶段 | mock 策略 |
|
||||||
| ai (ai12) | AiService.GenerateLessonPlan | AI 备课 | ai 就绪前返回固定教案 |
|
| ------------------------------------ | -------------- | ---- | ---- | ---------------------------------- |
|
||||||
|
| ExamService.CreateExam | 创建考试 | ✅ | P3 | 返回固定 examId |
|
||||||
|
| ExamService.GetExam | 获取考试详情 | ✅ | P3 | 返回固定考试数据 |
|
||||||
|
| ExamService.ListExamsByClass | 按班级列考试 | ✅ | P3 | 返回固定 5 场考试 |
|
||||||
|
| ExamService.UpdateExam | 更新考试 | ✅ | P3 | 返回 success=true |
|
||||||
|
| ExamService.DeleteExam | 删除考试 | ✅ | P3 | 返回 success=true |
|
||||||
|
| HomeworkService.AssignHomework | 布置作业 | ✅ | P3 | 返回固定 homeworkId |
|
||||||
|
| HomeworkService.GetHomework | 获取作业详情 | ✅ | P3 | 返回固定作业数据 |
|
||||||
|
| HomeworkService.ListHomeworkByClass | 按班级列作业 | ✅ | P3 | 返回固定 5 份作业 |
|
||||||
|
| HomeworkService.SubmitHomework | 提交作业 | ✅ | P3 | 返回 success=true(student-bff 用)|
|
||||||
|
| GradeService.RecordGrade | 录入成绩 | ✅ | P3 | 返回 success=true |
|
||||||
|
| GradeService.GetGrade | 获取成绩 | ✅ | P3 | 返回固定成绩数据 |
|
||||||
|
| GradeService.ListGradesByStudent | 按学生列成绩 | ✅ | P3 | 返回固定 10 条成绩 |
|
||||||
|
| GradeService.ListGradesByExam | 按考试列成绩 | ✅ | P3 | 返回固定 30 条成绩 |
|
||||||
|
| GradeService.ListGradesByHomework | 按作业列成绩 | ✅ | P3 | 返回固定 30 条成绩 |
|
||||||
|
| ClassService.GetClassesByTeacher | 教师班级列表 | ❌ | P3 | 返回固定 3 个 ClassInfo |
|
||||||
|
| ClassService.ListStudentsByClass | 班级学生名单 | ❌ | P3 | 返回固定 30 个 StudentInfo |
|
||||||
|
| ClassService.BatchGetClasses | 批量获取班级 | ❌ | P3 | 返回固定班级数据 |
|
||||||
|
| AttendanceService.RecordAttendance | 记录考勤 | ❌ | P3 | 返回 success=true(C4 裁决补全) |
|
||||||
|
| AttendanceService.GetClassAttendance | 查询考勤 | ❌ | P3 | 返回固定考勤数据(C4 裁决补全) |
|
||||||
|
|
||||||
|
> core_edu.proto 现状 14 RPC(ExamService 5 + HomeworkService 4 + GradeService 5),matrix.md §2 标称 22 RPC 5 Service,待 coord 按 C4/C5 裁决补 AttendanceService + ClassService(GetClassesByTeacher / ListStudentsByClass / BatchGetClasses)。
|
||||||
|
|
||||||
|
#### 2.1.3 content (ai09) — gRPC 50054
|
||||||
|
|
||||||
|
| Service.RPC | 用途 | 现状 | 阶段 | mock 策略 |
|
||||||
|
| ------------------------------------ | ------------ | ---- | ---- | ------------------------------- |
|
||||||
|
| TextbookService.ListTextbooks | 教材列表 | ✅ | P4 | 返回固定 5 个教材 |
|
||||||
|
| TextbookService.GetTextbook | 获取教材详情 | ✅ | P4 | 返回固定教材数据 |
|
||||||
|
| TextbookService.CreateTextbook | 创建教材 | ✅ | P4 | 返回固定 textbookId |
|
||||||
|
| KnowledgeGraphService.GetPrerequisites | 知识点前置 | ✅ | P4 | 返回固定知识点依赖 |
|
||||||
|
| KnowledgeGraphService.GetLearningPath | 学习路径 | ✅ | P4 | 返回固定学习路径 |
|
||||||
|
| ChapterService.ListChapters | 章节列表 | ❌ | P4 | 返回固定章节树(N5 裁决补全) |
|
||||||
|
| ChapterService.GetChapter | 章节详情 | ❌ | P4 | 返回固定章节(N5 裁决补全) |
|
||||||
|
| QuestionService.SearchQuestions | 题库检索 | ❌ | P4 | 返回固定 20 题(N3 裁决补全) |
|
||||||
|
|
||||||
|
> content.proto 现状 5 RPC(TextbookService 3 + KnowledgeGraphService 2),matrix.md §2 标称 18 RPC 4 Service,待 coord 按 N3/N5 裁决补 ChapterService + QuestionService。
|
||||||
|
|
||||||
|
#### 2.1.4 data-ana (ai11) — gRPC 50055
|
||||||
|
|
||||||
|
| Service.RPC | 用途 | 现状 | 阶段 | mock 策略 |
|
||||||
|
| ------------------------------------ | -------------- | ---- | ---- | ---------------------------------- |
|
||||||
|
| AnalyticsService.GetClassPerformance | 班级成绩分析 | ✅ | P4 | 返回固定分析数据 |
|
||||||
|
| AnalyticsService.GetStudentWeakness | 学生薄弱点 | ✅ | P4 | 返回固定 3 个薄弱知识点 |
|
||||||
|
| AnalyticsService.GetLearningTrend | 学习趋势 | ✅ | P4 | 返回固定 12 个月趋势 |
|
||||||
|
| AnalyticsService.GetTeacherDashboard | 教师仪表盘 | ❌ | P4 | 返回固定仪表盘数据(D4 裁决补全) |
|
||||||
|
| AnalyticsService.GetWarningList | 预警列表 | ❌ | P4 | 返回固定 5 条预警(D4 裁决补全) |
|
||||||
|
| AnalyticsService.GetMasteryDistribution | 知识掌握分布 | ❌ | P4 | 返回固定分布数据(D4 裁决补全) |
|
||||||
|
| AnalyticsService.SubscribeMasteryUpdate (stream) | 掌握度订阅 | ❌ | P5+ | 流式 mock(D4 裁决补全) |
|
||||||
|
|
||||||
|
> analytics.proto 现状 3 RPC,matrix.md §2 标称 12 RPC,待 coord 按 D4 裁决补全 4 端 Dashboard + Warning + MasteryDistribution + SubscribeMasteryUpdate Stream RPC。
|
||||||
|
|
||||||
|
#### 2.1.5 msg (ai10) — gRPC 50056
|
||||||
|
|
||||||
|
| Service.RPC | 用途 | 现状 | 阶段 | mock 策略 |
|
||||||
|
| ---------------------------------------- | ------------ | ---- | ---- | ---------------------------------- |
|
||||||
|
| NotificationService.SendNotification | 发送通知 | ✅ | P5 | 返回固定 notificationId |
|
||||||
|
| NotificationService.ListNotifications | 教师通知列表 | ✅ | P5 | 返回固定 10 条通知 |
|
||||||
|
| NotificationService.MarkAsRead | 标记已读 | ✅ | P5 | 返回 success=true |
|
||||||
|
| NotificationService.SearchNotifications | 搜索通知 | ✅ | P5 | 返回固定搜索结果 |
|
||||||
|
| NotificationPreferenceService.* | 通知偏好 | ❌ | P5 | 返回默认偏好(待 coord 补 proto) |
|
||||||
|
| NotificationTemplateService.* | 通知模板 | ❌ | P5 | 返回固定模板(待 coord 补 proto) |
|
||||||
|
|
||||||
|
> msg.proto 现状 4 RPC(NotificationService),matrix.md §2 标称 13 RPC 3 Service,待 coord 补 NotificationPreferenceService + NotificationTemplateService。
|
||||||
|
|
||||||
|
#### 2.1.6 ai (ai12) — gRPC 50058
|
||||||
|
|
||||||
|
| Service.RPC | 用途 | 现状 | 阶段 | mock 策略 |
|
||||||
|
| --------------------------------- | -------- | ---- | ---- | ------------------------------- |
|
||||||
|
| AiService.Chat | AI 对话 | ✅ | P5 | 返回固定回复 |
|
||||||
|
| AiService.StreamChat (stream) | 流式对话 | ✅ | P5 | 流式 mock(SSE) |
|
||||||
|
| AiService.GenerateQuestion | AI 出题 | ✅ | P5 | 返回固定题目 |
|
||||||
|
| AiService.OptimizeExpression | 表达优化 | ✅ | P5 | 返回固定优化结果 |
|
||||||
|
| AiService.GenerateLessonPlan | AI 备课 | ❌ | P5 | 返回固定教案(A4 裁决补全) |
|
||||||
|
| AiService.StreamGenerateQuestion (stream) | 流式出题 | ❌ | P5 | 流式 mock(A4 裁决补全) |
|
||||||
|
|
||||||
|
> ai.proto 现状 4 RPC,matrix.md §2 标称 6 RPC,待 coord 按 A4 裁决补 GenerateLessonPlan + StreamGenerateQuestion。
|
||||||
|
> **端口说明**:ai 服务端口为 **50058**(port-allocation.md §5 最终值,push-gateway 50057 豁免释放后 50058 让给 ai)。
|
||||||
|
|
||||||
### 2.2 Kafka 事件订阅(异步)
|
### 2.2 Kafka 事件订阅(异步)
|
||||||
|
|
||||||
无。teacher-bff 不订阅 Kafka 事件,仅做同步 gRPC 聚合。
|
无。teacher-bff 不订阅 Kafka 事件(B7 裁决:P2-P4 不订阅,仅同步聚合;P5 push-gateway 落地后再订阅,届时评估订阅 edu.notification.* 用于实时通知推送)。
|
||||||
|
|
||||||
### 2.3 HTTP 调用(如有)
|
### 2.3 HTTP 调用(如有)
|
||||||
|
|
||||||
无。
|
无。B2 裁决:首次实现即 gRPC,禁止 REST fetch。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -91,21 +204,53 @@ GraphQL schema 文件路径:`apps/teacher-bff/src/schema/*.graphql`(端口 :
|
|||||||
|
|
||||||
### 3.1 我依赖的上游就绪标志
|
### 3.1 我依赖的上游就绪标志
|
||||||
|
|
||||||
- [ ] iam gRPC 50052 启用(ai06)
|
| 上游 | AI | 就绪信号 | 阶段 |
|
||||||
- [ ] core-edu gRPC 50053 启用(ai08)
|
| ---------- | ---- | ------------------------------------------------ | ---- |
|
||||||
- [ ] content gRPC 50054 启用(ai09)
|
| iam | ai06 | gRPC 50052 + 12 RPC + HealthService SERVING | P2 |
|
||||||
- [ ] data-ana gRPC 50055 启用(ai11)
|
| core-edu | ai08 | gRPC 50053 + 22 RPC + HealthService SERVING | P3 |
|
||||||
- [ ] msg gRPC 50056 启用(ai10)
|
| content | ai09 | gRPC 50054 + 18 RPC + HealthService SERVING | P4 |
|
||||||
- [ ] ai gRPC 50057 启用(ai12)
|
| data-ana | ai11 | gRPC 50055 + 12 RPC + HealthService SERVING | P4 |
|
||||||
|
| msg | ai10 | gRPC 50056 + 13 RPC + HealthService SERVING | P5 |
|
||||||
|
| ai | ai12 | gRPC 50058 + 6 RPC + HealthService SERVING | P5 |
|
||||||
|
| coord | coord | shared-ts DownstreamClient 抽象包就绪(B8 裁决)| P2 启动前 |
|
||||||
|
| coord | coord | teacher-bff.schema.graphql 第一版仲裁完成(含 admin namespace)| P2 启动前 |
|
||||||
|
|
||||||
### 3.2 我的就绪标志(供下游消费)
|
### 3.2 我的就绪标志(供下游消费)
|
||||||
|
|
||||||
- [ ] teacher-bff GraphQL :3003 启用(/healthz 返回 200)
|
| 信号 | 阶段 | 消费方 |
|
||||||
- [ ] /readyz 返回 200(含 6 个下游 gRPC 连通性检查)
|
| --------------------------------------------- | ---- | ------------------- |
|
||||||
- [ ] GraphQL schema 可内省(POST /graphql 返回 schema)
|
| teacher-bff GraphQL :3003 启用(/healthz 200)| P2 | teacher-portal |
|
||||||
- [ ] 核心 Query 可执行:currentUser / myClasses / teacherDashboard
|
| /readyz 返回 200(按阶段扩展探针,见 §3.3) | P2+ | api-gateway |
|
||||||
- [ ] 核心 Mutation 可执行:createExam / assignHomework / recordGrade
|
| GraphQL schema 可内省(POST /graphql) | P2 | teacher-portal / admin-portal |
|
||||||
- [ ] admin namespace 可用(供 admin-portal 消费)
|
| 核心 Query 可执行:currentUser / myClasses / teacherDashboard | P2 | teacher-portal |
|
||||||
|
| 核心 Mutation 可执行:createExam / assignHomework / recordGrade | P3 | teacher-portal |
|
||||||
|
| admin namespace 预留(schema 含 admin.* 类型,P6 实现 Resolver)| P2 预留 / P6 实现 | admin-portal |
|
||||||
|
| DownstreamClient 抽象落地(3 BFF 共用,B8) | P2 | student-bff / parent-bff |
|
||||||
|
|
||||||
|
### 3.3 /readyz 探针按阶段扩展(president §2.4 + G2)
|
||||||
|
|
||||||
|
**DownstreamHealthCheck 注册表模式**,每个下游注册独立探针,按阶段启用:
|
||||||
|
|
||||||
|
| 阶段 | 探针列表 | 项数 |
|
||||||
|
| ---- | --------------------------------------------------------------------- | ---- |
|
||||||
|
| P2 | Redis PING + iam gRPC 50052 | 2 |
|
||||||
|
| P3 | + core-edu gRPC 50053 | 3 |
|
||||||
|
| P4 | + content gRPC 50054 + data-ana gRPC 50055 | 5 |
|
||||||
|
| P5 | + ai gRPC 50058 + msg gRPC 50056 | 7 |
|
||||||
|
|
||||||
|
> 总裁裁决 §2.4:探针列表扩展属"跨阶段扩展例外"(president §2.3),允许新增探针但禁止修改已有探针检查项。
|
||||||
|
> **软失败规则**:必需依赖(Redis / 已启用 gRPC 下游)失败返 503;可选依赖(Kafka 消费 / 未启用 gRPC 下游)失败仅告警返 200 + `degraded: true`。
|
||||||
|
|
||||||
|
### 3.4 越权防御(B4 + president §2.9)
|
||||||
|
|
||||||
|
**AuthorizationGuard** 实现 teacherId 与资源归属校验:
|
||||||
|
|
||||||
|
| 阶段 | 实现方式 | 裁决依据 |
|
||||||
|
| ---- | --------------------------------------------------------------------- | -------- |
|
||||||
|
| P2 | DEV_MODE 放行(环境变量 TEACHER_BFF_DEV_MODE=true)+ 日志告警 | §2.9 |
|
||||||
|
| P3+ | 接入 core-edu gRPC 真实校验(GetClassesByTeacher 比对 teacherId) | §2.9 |
|
||||||
|
|
||||||
|
> B3 裁决澄清:BFF 豁免 `@RequirePermission` 不做"功能权限决策",但必须做"数据权限防御"(B4)。错误码见 §1.5。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -124,7 +269,7 @@ GraphQL schema 文件路径:`apps/teacher-bff/src/schema/*.graphql`(端口 :
|
|||||||
|
|
||||||
### 4.2 我消费的 mock
|
### 4.2 我消费的 mock
|
||||||
|
|
||||||
在真实上游就绪前,teacher-bff 使用以下 mock(详见 §2.1 mock 策略列):
|
在真实上游就绪前,teacher-bff 使用以下 mock(详见 §2.1 各表的 mock 策略列):
|
||||||
|
|
||||||
- **iam mock**:固定 UserInfo + 全权限 + 固定视口
|
- **iam mock**:固定 UserInfo + 全权限 + 固定视口
|
||||||
- **core-edu mock**:固定班级/学生/考试/作业/成绩/考勤数据
|
- **core-edu mock**:固定班级/学生/考试/作业/成绩/考勤数据
|
||||||
@@ -133,4 +278,4 @@ GraphQL schema 文件路径:`apps/teacher-bff/src/schema/*.graphql`(端口 :
|
|||||||
- **msg mock**:固定通知列表 + MarkAsRead success
|
- **msg mock**:固定通知列表 + MarkAsRead success
|
||||||
- **ai mock**:固定 AI 回复/题目/教案
|
- **ai mock**:固定 AI 回复/题目/教案
|
||||||
|
|
||||||
> 所有上游 mock 通过 gRPC client 拦截器实现,上游就绪后移除拦截器切换真实调用。
|
> 所有上游 mock 通过 gRPC client 拦截器实现(DownstreamClient 抽象内置 mock 开关,B8 裁决),上游就绪后移除拦截器切换真实调用。
|
||||||
|
|||||||
@@ -21,4 +21,38 @@
|
|||||||
- **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X)
|
- **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
(暂无问题)
|
### ISSUE-001-ai03:admin 命名空间 P2 预留与 ARB-001 "P2 不包含" 冲突
|
||||||
|
|
||||||
|
- **提请方**:ai03
|
||||||
|
- **日期**:2026-07-10
|
||||||
|
- **类型**:契约不明确
|
||||||
|
- **描述**:coord.md ARB-001 §1.3 关键裁决表写 "admin 命名空间 | **P2 不包含**,P6 admin-portal 阶段新增 `admin` 命名空间";但 president-final-rulings.md §5.1(ISSUE-044)裁决 "ai03 P2 预留 admin schema 命名空间(如 `admin.*` Query/Mutation)",且 §7.3 ai03 工作清单明确 "批次 1(P2):Must Have 13 项 + DownstreamClient 抽象 + admin schema 命名空间预留"。两处对 P2 admin 命名空间的要求不一致——总裁裁决要求 P2 预留(schema 中声明占位),coord 仲裁说 P2 不包含。
|
||||||
|
- **建议方案**:以总裁裁决为准(裁决优先级 president > coord),修正 ARB-001 §1.3 为 "P2 预留 admin 命名空间占位(schema 中声明 `admin` Query/Mutation 类型骨架,无实际 Resolver),P6 admin-portal 阶段实现具体 Resolver"。ai03 在 P2 schema 第一版中预留 `admin` 命名空间类型声明。
|
||||||
|
- **状态**:待 coord 仲裁
|
||||||
|
|
||||||
|
### ISSUE-002-ai03:P2 dashboard classes 数据来源与 ARB-001 §1.4 调用链冲突
|
||||||
|
|
||||||
|
- **提请方**:ai03
|
||||||
|
- **日期**:2026-07-10
|
||||||
|
- **类型**:前置依赖缺失
|
||||||
|
- **描述**:coord.md ARB-001 §1.4 ai03 执行项第 4 条写 "dashboard Resolver 并行调用 iam(3 RPC)+ classes(1 RPC),用 DataLoader 防御 N+1",暗示 P2 dashboard 需 gRPC 调 classes 服务。但存在三重冲突:
|
||||||
|
1. **classes P2 无 gRPC server**:classes.proto 注释 "P1: REST 实现,P3 起转 gRPC";matrix.md §2 gRPC 接口提供方矩阵中 core-edu(含 classes 合并)gRPC 50053 状态为 P3 就绪。classes 服务 P2 阶段未启用 gRPC。
|
||||||
|
2. **B2 裁决约束**:coord-final-decisions.md B2 裁决 "首次实现即 gRPC 调用下游",若 classes P2 无 gRPC,teacher-bff 不能用 REST 调 classes(B2 禁止 REST 过渡)。
|
||||||
|
3. **president §3.5 功能范围**:president-final-rulings.md §3.5 裁决 P2 实现 "班级列表(iam 数据)",即 P2 班级列表数据来自 iam,不是 classes 服务。
|
||||||
|
- **建议方案**:采纳 president §3.5,P2 dashboard 的 classes 数据从 iam gRPC 获取(iam `GetEffectiveAccess` 或 `GetViewports` 返回的关联班级),不调 classes 服务。ARB-001 §1.4 第 4 条修正为 "dashboard Resolver 并行调用 iam gRPC(GetUserInfo + GetViewports + GetEffectiveAccess),classes 列表从 iam 返回数据推导,P3 core-edu gRPC 就绪后切换为 `GetClassesByTeacher` RPC"。这样 P2 不依赖 classes gRPC,与 B2 + president §3.5 一致。
|
||||||
|
- **状态**:待 coord 仲裁
|
||||||
|
|
||||||
|
### ISSUE-003-ai03:GraphQL schema 文件命名不一致(ARB-001 vs president §2.17)
|
||||||
|
|
||||||
|
- **提请方**:ai03
|
||||||
|
- **日期**:2026-07-10
|
||||||
|
- **类型**:编号冲突
|
||||||
|
- **描述**:GraphQL schema 文件存放目录两处一致(`packages/shared-ts/contracts/graphql/`),但文件名不一致:
|
||||||
|
- coord.md ARB-001 §1.3 + §1.4:`teacher-bff.graphql`
|
||||||
|
- president-final-rulings.md §2.17 第 1 条:`teacher-bff.schema.graphql`
|
||||||
|
|
||||||
|
ai03 创建文件时无法确定用哪个文件名。
|
||||||
|
- **建议方案**:以 president §2.17 为准(裁决优先级 president > coord),统一为 `teacher-bff.schema.graphql`。coord 修正 ARB-001 §1.3/§1.4 文件名。
|
||||||
|
- **状态**:待 coord 仲裁
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
# teacher-bff 工作排期
|
# teacher-bff 工作排期
|
||||||
|
|
||||||
> 负责人:ai03
|
> 负责人:ai03
|
||||||
> 关联:[workline.md](../workline.md)、[coord.md §1 ARB-001](../coord.md)、[contracts/teacher-bff_contract.md](../contracts/teacher-bff_contract.md)
|
> 关联:[workline.md](../workline.md)、[coord.md §1 ARB-001](../coord.md)、[contracts/teacher-bff_contract.md](../contracts/teacher-bff_contract.md)、[president-final-rulings.md §3.1/§7.3](../../president-final-rulings.md)、[coord-final-decisions.md §2 B1-B8](../../coord-final-decisions.md)
|
||||||
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
> 模式:全并行(各 AI 一口气完成 P2-P6 全部代码,最后统一集成测试)
|
||||||
|
> 裁决依据:B1 P2 即 GraphQL / B2 首次实现即 gRPC / B3 豁免 @RequirePermission / B4 越权防御 / B5 BFF_TEACHER_ 前缀 / B6 Redis 短缓存 / B7 P2-P4 不订阅 Kafka / B8 DownstreamClient 抽象
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## §1 总览
|
## §1 总览
|
||||||
|
|
||||||
teacher-bff 是教学场景域聚合层,全阶段目标:P2 GraphQL schema 第一版 → P3 扩展 exams/homework/grades → P4 学情分析 → P5 通知+SSE → P6 admin 命名空间。
|
teacher-bff 是教学场景域聚合层(BFF),全阶段目标:
|
||||||
|
|
||||||
|
| 阶段 | 核心交付 | 裁决依据 |
|
||||||
|
| ---- | -------- | -------- |
|
||||||
|
| P2 | GraphQL schema 第一版(5 Query + admin 预留)+ DownstreamClient 抽象 + iam gRPC + AuthorizationGuard + ActionState 信封 | B1/B2/B3/B4/B5/B8 + ARB-001 + §3.1 |
|
||||||
|
| P3 | core-edu gRPC 扩展(exams/homework/grades Query + Mutation)+ AuthorizationGuard 接入 core-edu + Redis 聚合缓存 | §2.3 跨阶段扩展例外 |
|
||||||
|
| P4 | content + data-ana gRPC 扩展(学情分析 Query)+ DataLoader 全量接入 | §2.3 + §2.8 |
|
||||||
|
| P5 | ai + msg gRPC 扩展(SSE 流式 + notifications Query)+ Kafka consumer(push-gateway 落地后) | B7 |
|
||||||
|
| P6 | admin 命名空间实现 + 硬化(熔断/重试/超时/HPA/mTLS) | §5.1 + P6 硬化 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## §2 全阶段甘特图(P2-P6,各 AI 自行细化)
|
## §2 全阶段甘特图(P2-P6)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
gantt
|
gantt
|
||||||
@@ -20,39 +29,307 @@ gantt
|
|||||||
dateFormat YYYY-MM-DD
|
dateFormat YYYY-MM-DD
|
||||||
axisFormat %m-%d
|
axisFormat %m-%d
|
||||||
|
|
||||||
section P2 GraphQL 基础
|
section P2 GraphQL 基础(8d)
|
||||||
schema第一版(ARB-001) :crit, a3a, 2026-07-10, 2d
|
schema第一版+admin预留(ARB-001) :crit, a3a, 2026-07-10, 2d
|
||||||
Yoga endpoint+5 Query Resolver :crit, a3b, after a3a, 3d
|
Yoga endpoint+5 Query Resolver :crit, a3b, after a3a, 3d
|
||||||
DataLoader+N+1防御 :a3c, after a3b, 1d
|
DownstreamClient+iam gRPC :crit, a3c, after a3a, 2d
|
||||||
ActionState信封+降级模式B :a3d, after a3b, 1d
|
AuthorizationGuard越权防御(B4) :crit, a3d, after a3b, 1d
|
||||||
|
ActionState信封+降级模式B :a3e, after a3b, 1d
|
||||||
|
/readyz探针注册表+iam探针 :a3f, after a3d, 1d
|
||||||
|
错误码迁移BFF_TEACHER_+回写02 :a3g, after a3e, 1d
|
||||||
|
|
||||||
section P3-P6 扩展
|
section P3 core-edu 扩展(5d)
|
||||||
exams/homework/grades Query :a3e, after a3d, 3d
|
CoreEduClient gRPC(exams/homework/grades) :crit, a3h, after a3g, 2d
|
||||||
学情分析+通知+SSE :a3f, after a3e, 4d
|
Mutation(createExam/assignHomework/recordGrade) :a3i, after a3h, 1d
|
||||||
admin命名空间 :a3g, after a3f, 2d
|
AuthorizationGuard接入core-edu+Redis缓存 :a3j, after a3h, 1d
|
||||||
|
/readyz+core-edu探针 :a3k, after a3j, 1d
|
||||||
|
|
||||||
|
section P4 content+data-ana 扩展(4d)
|
||||||
|
ContentClient+DataAnaClient gRPC :a3l, after a3k, 2d
|
||||||
|
学情分析Query+DataLoader全量 :a3m, after a3l, 1d
|
||||||
|
/readyz+content+data-ana探针 :a3n, after a3m, 1d
|
||||||
|
|
||||||
|
section P5 ai+msg 扩展(8d)
|
||||||
|
AiClient+MsgClient gRPC :a3o, after a3n, 2d
|
||||||
|
SSE流式透传+notifications Query :a3p, after a3o, 2d
|
||||||
|
Kafka consumer(push-gateway落地后) :a3q, after a3p, 2d
|
||||||
|
/readyz+ai+msg探针 :a3r, after a3q, 1d
|
||||||
|
Should Have补全(OTel/metrics/DataLoader/Redis) :a3s, after a3r, 1d
|
||||||
|
|
||||||
|
section P6 admin+硬化(3d)
|
||||||
|
admin命名空间实现 :a3t, after a3s, 1d
|
||||||
|
熔断/重试/超时 :a3u, after a3t, 1d
|
||||||
|
Nice to Have补全(Zod全量/优雅关闭) :a3v, after a3u, 1d
|
||||||
```
|
```
|
||||||
|
|
||||||
> **注意**:以上为 coord 初始规划,ai03 接管后必须自行细化为完整 P2-P6 排期。
|
> **总工期**:28d(P2 8d + P3 5d + P4 4d + P5 8d + P6 3d),与 workline.md §1 总时间线对齐(批次1 8d + 批次2 5d + 批次4 8d + P4/P6 合并 7d)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## §3 详细任务
|
## §3 详细任务
|
||||||
|
|
||||||
### P2:GraphQL schema + 5 Query + DataLoader + ActionState
|
### P2:GraphQL 基础 + DownstreamClient + 越权防御(批次 1,8d)
|
||||||
|
|
||||||
|
> 裁决依据:president §3.1 Must Have 13 项(teacher-bff 无 DB,G10/G11 不适用,实际 11 项 + DownstreamClient + admin 预留)
|
||||||
|
|
||||||
|
#### 3.1 GraphQL schema 第一版 + admin 预留(2d,P0 阻塞 ai13)
|
||||||
|
|
||||||
- **负责人**:ai03
|
- **负责人**:ai03
|
||||||
|
- **依赖**:coord 仲裁 ARB-001(已裁决)
|
||||||
- **交付物**:
|
- **交付物**:
|
||||||
- `packages/shared-ts/contracts/graphql/teacher-bff.graphql` — P2 schema
|
- `packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql` — P2 schema(5 Query: dashboard/viewports/me/classes/class + admin 命名空间占位)
|
||||||
- Yoga endpoint `POST /graphql`
|
- admin 命名空间预留:schema 中声明 `admin` Query/Mutation 类型骨架(无实际 Resolver),P6 实现
|
||||||
- 5 Query:dashboard / viewports / me / classes / class
|
- **验收标准**:5 Query SDL 定义完整 + admin 占位类型声明 + depth ≤ 7 + cost ≤ 1000
|
||||||
- DataLoader + ActionState 信封 + 降级模式 B
|
- **裁决引用**:ARB-001 §1.2/§1.3 + president §5.1/§2.17
|
||||||
- **依赖**:iam gRPC(ai06)+ coord 仲裁 ARB-001
|
|
||||||
- **验收标准**:5 Query 可用 + ActionState 信封 + depth ≤ 7
|
#### 3.2 Yoga endpoint + 5 Query Resolver(3d,P0 阻塞 ai13)
|
||||||
- **完整 P3-P6 任务**:⚠️ 由 ai03 自行补充
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:3.1 schema + iam gRPC 50052(ai06 P2.1)
|
||||||
|
- **交付物**:
|
||||||
|
- `POST /graphql` Yoga GraphQL endpoint
|
||||||
|
- 5 Query Resolver:dashboard / viewports / me / classes / class
|
||||||
|
- Dashboard Resolver P2 实现方式(ISSUE-032):P2 仅调 iam gRPC,未启用字段返回 null + `extensions.warning = "field_unavailable_in_p2"`
|
||||||
|
- **验收标准**:5 Query 可执行 + ActionState 信封 + 降级模式 B(success=true + error=null + data 内 degraded 字段)
|
||||||
|
- **裁决引用**:B1 + ARB-001 §1.4 + president §2.6/§2.8
|
||||||
|
|
||||||
|
#### 3.3 DownstreamClient 抽象 + iam gRPC(2d,P0 阻塞 ai04/ai05)
|
||||||
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:iam gRPC 50052(ai06 P2.1)
|
||||||
|
- **交付物**:
|
||||||
|
- `src/clients/` DownstreamClient 抽象层(B8:BFF 模式 v2 标准抽象,3 个 BFF 统一使用,回写 teacher-bff)
|
||||||
|
- IamClient gRPC 实现:`@grpc/grpc-js` + `@bufbuild/protobuf`,调 iam:50052
|
||||||
|
- gRPC interceptor:注入 trace context(traceparent)+ x-user-id metadata + metrics
|
||||||
|
- **验收标准**:IamClient gRPC 调 iam GetUserInfo/GetViewports/GetEffectiveAccess 成功
|
||||||
|
- **裁决引用**:B2(首次实现即 gRPC)+ B8(DownstreamClient 抽象)
|
||||||
|
|
||||||
|
#### 3.4 AuthorizationGuard 越权防御(1d,P0)
|
||||||
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:3.2 Resolver
|
||||||
|
- **交付物**:
|
||||||
|
- `src/middleware/authorization.guard.ts` — AuthorizationGuard 接口(`canAccessClass(userId, classId): Promise<boolean>`)
|
||||||
|
- P2 内部实现:DEV_MODE 放行 + 生产拒绝(保守策略)
|
||||||
|
- 错误码:`BFF_TEACHER_FORBIDDEN_RESOURCE`(teacherId 与资源无归属)+ `BFF_TEACHER_IDENTITY_MISMATCH`(JWT teacherId 与 body 不一致)
|
||||||
|
- **验收标准**:Guard 接口定义 + DEV_MODE 放行 + 生产拒绝 + 2 个越权错误码
|
||||||
|
- **裁决引用**:B4 + president §2.7(错误码语义)+ §2.9(越权防御 P2 实现)
|
||||||
|
|
||||||
|
#### 3.5 ActionState 信封 + 降级模式 B(1d)
|
||||||
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:3.2 Resolver
|
||||||
|
- **交付物**:GlobalErrorFilter + ActionState 信封(success/errors/data)+ 降级模式 B
|
||||||
|
- **验收标准**:GraphQL errors 数组扩展 ActionState 字段,`extensions.code = BFF_TEACHER_*`
|
||||||
|
- **裁决引用**:G8 + president §2.6
|
||||||
|
|
||||||
|
#### 3.6 /readyz 探针注册表 + iam 探针(1d)
|
||||||
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:3.3 IamClient
|
||||||
|
- **交付物**:
|
||||||
|
- `src/shared/health/readiness.probe.ts` — DownstreamHealthCheck 注册表模式
|
||||||
|
- P2 探针:Redis + iam gRPC 50052(teacher-bff 无 DB,2 项)
|
||||||
|
- **验收标准**:/readyz 返回 2 项检查结果 + 必需依赖失败返回 503
|
||||||
|
- **裁决引用**:G2 + president §2.4(探针按阶段扩展)
|
||||||
|
|
||||||
|
#### 3.7 错误码迁移 + 回写 02 文档(1d)
|
||||||
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:3.2-3.6
|
||||||
|
- **交付物**:
|
||||||
|
- `application-error.ts` 全量迁移 `TEACHER_BFF_*` → `BFF_TEACHER_*`
|
||||||
|
- 回写 02 文档:4 处 `GetTeacherDashboardStats` → `GetTeacherDashboard`(ISSUE-035)+ B1/B2/B4/B8 裁决对齐
|
||||||
|
- **验收标准**:源码零 `TEACHER_BFF_*` + 02 文档与裁决一致
|
||||||
|
- **裁决引用**:B5 + G14 + president §3.4 回写义务
|
||||||
|
|
||||||
|
#### P2 横切项(贯穿 3.1-3.7)
|
||||||
|
|
||||||
|
| 项 | 状态 | 说明 |
|
||||||
|
| -- | ---- | ---- |
|
||||||
|
| pino 结构化日志(G4) | ✅ 已具备 | logger.ts |
|
||||||
|
| /healthz liveness(G3) | ✅ 已具备 | health.controller.ts |
|
||||||
|
| GlobalErrorFilter(G8) | ✅ 已具备 | global-error.filter.ts |
|
||||||
|
| ESM import .js 后缀(G12) | ✅ 已具备 | 源码已用 .js |
|
||||||
|
| import type(G13) | ✅ 已具备 | 源码已用 import type |
|
||||||
|
| OTel tracer(G6) | ⚠️ Should Have | P2 可降级为 logger-only,P5 补全 |
|
||||||
|
| /metrics 业务指标(G5) | ⚠️ Should Have | P2 仅暴露 process metrics |
|
||||||
|
| DataLoader(B1) | ⚠️ Should Have | P2 可先用普通 resolver,P4 全量接入 |
|
||||||
|
| Redis 5-30s 短缓存(B6) | ⚠️ Should Have | P3 接入聚合缓存 |
|
||||||
|
| Zod 全量验证(G7) | ⚠️ Nice to Have | P2 先校验核心 Query,P6 全量 |
|
||||||
|
| 优雅关闭 SIGTERM(G9) | ⚠️ Nice to Have | P2 已有基础,P6 补全关闭顺序 |
|
||||||
|
|
||||||
|
**P2 退出标准**:POST /graphql 可用 + 5 Query Resolver + DownstreamClient + AuthorizationGuard + ActionState 信封 + /readyz 2 项探针 + 错误码 BFF_TEACHER_* + admin 命名空间预留。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P3:core-edu gRPC 扩展(批次 2,5d)
|
||||||
|
|
||||||
|
> 裁决依据:§2.3 跨阶段扩展例外(新增下游 gRPC 调用 + AuthorizationGuard 内部实现替换 + /readyz 探针扩展)
|
||||||
|
|
||||||
|
#### 3.8 CoreEduClient gRPC(2d,P0)
|
||||||
|
|
||||||
|
- **负责人**:ai03
|
||||||
|
- **依赖**:core-edu gRPC 50053(ai08 P3)
|
||||||
|
- **交付物**:
|
||||||
|
- CoreEduClient gRPC 实现:ExamService / HomeworkService / GradeService
|
||||||
|
- GraphQL Query 扩展:exams(classId) / homework(classId) / grades(examId)
|
||||||
|
- Dashboard Resolver 扩展:null 字段替换为 core-edu 真实数据
|
||||||
|
- **验收标准**:3 个 Query 返回 core-edu 数据 + Dashboard null 字段消除
|
||||||
|
- **裁决引用**:§2.3 跨阶段扩展例外 + §2.8 Dashboard Resolver 扩展
|
||||||
|
|
||||||
|
#### 3.9 Mutation 透传(1d)
|
||||||
|
|
||||||
|
- **交付物**:createExam / assignHomework / recordGrade Mutation(透传 core-edu gRPC)
|
||||||
|
- **验收标准**:3 个 Mutation 可执行 + 返回 ActionState 信封
|
||||||
|
|
||||||
|
#### 3.10 AuthorizationGuard 接入 core-edu + Redis 缓存(1d)
|
||||||
|
|
||||||
|
- **交付物**:
|
||||||
|
- AuthorizationGuard 内部实现替换:DEV_MODE 放行 → 真实 gRPC 校验
|
||||||
|
- Redis 缓存:`GetClassesByTeacher` 结果缓存(key: `authz:teacher:{teacherId}:classes`,TTL 5min)
|
||||||
|
- **验收标准**:生产环境越权防御生效 + Redis 缓存命中
|
||||||
|
- **裁决引用**:president §2.9(P3 接入 core-edu 后替换 Guard 实现)
|
||||||
|
|
||||||
|
#### 3.11 /readyz + core-edu 探针(1d)
|
||||||
|
|
||||||
|
- **交付物**:/readyz 探针注册表扩展 core-edu gRPC 50053 探针(3 项:Redis + iam + core-edu)
|
||||||
|
- **验收标准**:/readyz 返回 3 项检查结果
|
||||||
|
|
||||||
|
**P3 退出标准**:core-edu gRPC 3 Query + 3 Mutation + AuthorizationGuard 生产生效 + Redis 缓存 + /readyz 3 项探针。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P4:content + data-ana gRPC 扩展(4d)
|
||||||
|
|
||||||
|
> 裁决依据:§2.3 跨阶段扩展例外
|
||||||
|
|
||||||
|
#### 3.12 ContentClient + DataAnaClient gRPC(2d)
|
||||||
|
|
||||||
|
- **依赖**:content gRPC 50054(ai09 P4)+ data-ana gRPC 50055(ai11 P4)
|
||||||
|
- **交付物**:
|
||||||
|
- ContentClient gRPC:KnowledgeGraphService(GetPrerequisites / GetLearningPath)
|
||||||
|
- DataAnaClient gRPC:AnalyticsService(GetClassPerformance / GetStudentWeakness / GetLearningTrend / GetTeacherDashboard)
|
||||||
|
- GraphQL Query 扩展:knowledgePath / classPerformance / studentWeakness / learningTrend / teacherDashboard
|
||||||
|
- **验收标准**:5 个 Query 返回真实数据
|
||||||
|
|
||||||
|
#### 3.13 DataLoader 全量接入(1d)
|
||||||
|
|
||||||
|
- **交付物**:DataLoader 覆盖全部 N+1 风险点(UserLoader / ClassLoader / ExamLoader / HomeworkLoader / GradeLoader)
|
||||||
|
- **验收标准**:DataLoader per-request 实例 + 批量化窗口 16ms
|
||||||
|
- **裁决引用**:B1 Should Have → P4 全量接入
|
||||||
|
|
||||||
|
#### 3.14 /readyz + content + data-ana 探针(1d)
|
||||||
|
|
||||||
|
- **交付物**:/readyz 探针扩展 content + data-ana(5 项:Redis + iam + core-edu + content + data-ana)
|
||||||
|
|
||||||
|
**P4 退出标准**:content + data-ana gRPC + 5 Query + DataLoader 全量 + /readyz 5 项探针。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P5:ai + msg gRPC 扩展 + SSE + Kafka(批次 4,8d)
|
||||||
|
|
||||||
|
> 裁决依据:B7(P5 push-gateway 落地后再订阅 Kafka)
|
||||||
|
|
||||||
|
#### 3.15 AiClient + MsgClient gRPC(2d)
|
||||||
|
|
||||||
|
- **依赖**:ai gRPC 50057(ai12 P5)+ msg gRPC 50056(ai10 P5)
|
||||||
|
- **交付物**:
|
||||||
|
- AiClient gRPC:AiService(Chat / StreamChat streaming / GenerateQuestion / OptimizeExpression)
|
||||||
|
- MsgClient gRPC:NotificationService(ListNotifications / SearchNotifications / MarkAsRead)
|
||||||
|
- GraphQL Query 扩展:notifications + Mutation:generateQuestion / markNotificationAsRead
|
||||||
|
|
||||||
|
#### 3.16 SSE 流式透传(2d)
|
||||||
|
|
||||||
|
- **交付物**:`GET /ai/chat/stream` SSE 端点(ai.StreamChat gRPC stream → BFF → 前端 EventSource)
|
||||||
|
- **验收标准**:SSE 三层透传端到端通 + 背压处理 + 超时取消
|
||||||
|
- **裁决引用**:02 文档 §10
|
||||||
|
|
||||||
|
#### 3.17 Kafka consumer(2d,push-gateway 落地后)
|
||||||
|
|
||||||
|
- **交付物**:
|
||||||
|
- Kafka consumer 订阅 `edu.identity.user.role_changed` / `edu.identity.role.updated`,精确失效 Redis 权限缓存
|
||||||
|
- 幂等性:基于 event_id 去重(Redis SETNX)
|
||||||
|
- **验收标准**:权限变更秒级缓存失效 + 幂等消费
|
||||||
|
- **裁决引用**:B7(P5 push-gateway 落地后再订阅)
|
||||||
|
|
||||||
|
#### 3.18 /readyz + ai + msg 探针 + Should Have 补全(2d)
|
||||||
|
|
||||||
|
- **交付物**:
|
||||||
|
- /readyz 探针扩展 ai + msg(7 项:Redis + iam + core-edu + content + data-ana + ai + msg)
|
||||||
|
- Should Have 补全:OTel tracer 全链路 + /metrics 业务指标 + Redis 聚合缓存 5-30s
|
||||||
|
- **验收标准**:/readyz 7 项 + OTel 全链路 trace + 缓存命中率 ≥ 60%
|
||||||
|
|
||||||
|
**P5 退出标准**:ai + msg gRPC + SSE 流式 + notifications Query + Kafka consumer + /readyz 7 项探针 + Should Have 补全。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### P6:admin 命名空间 + 硬化(3d)
|
||||||
|
|
||||||
|
> 裁决依据:president §5.1(admin-portal 复用 teacher-bff)+ P6 硬化
|
||||||
|
|
||||||
|
#### 3.19 admin 命名空间实现(1d)
|
||||||
|
|
||||||
|
- **依赖**:admin-portal(ai16 P6)
|
||||||
|
- **交付物**:
|
||||||
|
- admin schema 命名空间 Resolver 实现(P2 预留的占位类型填充实际 Resolver)
|
||||||
|
- admin Query/Mutation:用户管理 / 角色权限管理 / 学校设置 / 组织管理 / 审计日志查询
|
||||||
|
- **验收标准**:admin namespace 可内省 + admin-portal 可消费
|
||||||
|
- **裁决引用**:president §5.1(admin-portal 复用 teacher-bff GraphQL endpoint)
|
||||||
|
|
||||||
|
#### 3.20 熔断 / 重试 / 超时(1d)
|
||||||
|
|
||||||
|
- **交付物**:
|
||||||
|
- Circuit Breaker(opossum,per-downstream-service)
|
||||||
|
- Retry(gRPC interceptor,仅幂等 RPC,指数退避)
|
||||||
|
- Timeout(per-RPC 3s,聚合总超时 5s)
|
||||||
|
- **验收标准**:熔断/重试/超时生效 + 降级策略覆盖
|
||||||
|
|
||||||
|
#### 3.21 Nice to Have 补全(1d)
|
||||||
|
|
||||||
|
- **交付物**:Zod 全量验证 + 优雅关闭顺序(HTTP→Redis→gRPC→Kafka→Tracer)+ 测试覆盖率 ≥ 80%
|
||||||
|
- **验收标准**:Zod 全 Controller 覆盖 + SIGTERM 顺序关闭 + Vitest 覆盖率 ≥ 80%
|
||||||
|
|
||||||
|
**P6 退出标准**:admin namespace 实现 + 熔断/重试/超时 + Zod 全量 + 测试 ≥ 80% + SLO 99.9%。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## §4 依赖与就绪信号
|
## §4 依赖与就绪信号
|
||||||
|
|
||||||
- **我依赖**:iam gRPC 50052(ai06)+ core-edu gRPC 50053(ai08,P3+)
|
### 4.1 我依赖的上游就绪信号
|
||||||
- **我的就绪信号**:POST /graphql 可用 + dashboard Query 返回正确数据
|
|
||||||
|
| 上游 | 就绪信号 | 阶段 | 状态 |
|
||||||
|
| ---- | -------- | ---- | ---- |
|
||||||
|
| iam(ai06) | gRPC 50052 + 8 RPC(GetUserInfo/GetViewports/GetEffectivePermissions/GetEffectiveAccess/Logout/GetPublicKey/BatchGetUsers/GetChildrenByParent) | P2 | ⏳ |
|
||||||
|
| core-edu(ai08) | gRPC 50053 + ExamService/HomeworkService/GradeService | P3 | ⏳ |
|
||||||
|
| content(ai09) | gRPC 50054 + KnowledgeGraphService | P4 | ⏳ |
|
||||||
|
| data-ana(ai11) | gRPC 50055 + AnalyticsService(含 GetTeacherDashboard,ISSUE-027 补全) | P4 | ⏳ |
|
||||||
|
| ai(ai12) | gRPC 50057 + AiService(含 StreamChat) | P5 | ⏳ |
|
||||||
|
| msg(ai10) | gRPC 50056 + NotificationService | P5 | ⏳ |
|
||||||
|
| push-gateway(ai02) | /internal/push 落地(B7 Kafka 订阅前提) | P5 | ⏳ |
|
||||||
|
| admin-portal(ai16) | admin schema 需求确认 | P6 | ⏳ |
|
||||||
|
|
||||||
|
### 4.2 我的就绪信号(供下游消费)
|
||||||
|
|
||||||
|
| 阶段 | 就绪信号 | 消费方 |
|
||||||
|
| ---- | -------- | ------ |
|
||||||
|
| P2 | POST /graphql 可用 + 5 Query + admin 预留 | teacher-portal(ai13) |
|
||||||
|
| P2 | DownstreamClient 抽象(B8 回写) | student-bff(ai04)/ parent-bff(ai05) |
|
||||||
|
| P3 | exams/homework/grades Query + Mutation | teacher-portal(ai13) |
|
||||||
|
| P4 | 学情分析 Query + DataLoader | teacher-portal(ai13) |
|
||||||
|
| P5 | SSE 流式 + notifications Query | teacher-portal(ai13) |
|
||||||
|
| P6 | admin namespace 可用 | admin-portal(ai16) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §5 跨阶段扩展例外验收清单
|
||||||
|
|
||||||
|
> 裁决依据:president §2.3(ISSUE-020)。每次跨阶段扩展时对照检查。
|
||||||
|
|
||||||
|
- [ ] 扩展时更新 02-architecture-design.md 下游调用矩阵
|
||||||
|
- [ ] 扩展时更新 packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql
|
||||||
|
- [ ] 扩展时运行 `pnpm run arch:scan` 更新 arch.db
|
||||||
|
- [ ] 未修改已有 RPC 调用签名或返回类型
|
||||||
|
- [ ] 未删除已实现的 RPC 调用
|
||||||
|
- [ ] 未修改 GraphQL schema 已有字段类型(仅新增字段)
|
||||||
|
- [ ] 未修改 /readyz 已有探针检查项(仅新增)
|
||||||
|
|||||||
Reference in New Issue
Block a user