feat(teacher-bff): 完整实现 teacher-bff GraphQL 聚合层
包含 clients/graphql/middleware、health probes、shared-ts contracts 等
This commit is contained in:
@@ -189,16 +189,16 @@ graph LR
|
||||
|
||||
**目标**:在不改变对外 REST 契约的前提下,补齐黄金模板差距,为后续 gRPC/GraphQL 切换铺路。
|
||||
|
||||
| 工作项 | 详情 | 优先级 |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------ |
|
||||
| 引入 Client 抽象层 | 新增 `src/clients/` 目录,定义 `IamClient`/`ClassesClient`/`CoreEduClient` 接口,当前实现为 REST fetch adapter,P3 替换为 gRPC adapter | P0 |
|
||||
| 错误码迁移 | `TEACHER_BFF_*` → `BFF_TEACHER_*`(全量替换 [application-error.ts](../src/shared/errors/application-error.ts)) | P0 |
|
||||
| Zod 输入验证 | Controller 层引入 Zod schema 解析 path/body(classId/examId 等) | P0 |
|
||||
| `/readyz` 下游探针 | 并行 ping iam + classes + core-edu 的 `/healthz`,全部 ok 才返回 ok | P0 |
|
||||
| Vitest 测试框架 | 引入 Vitest,覆盖 Service 聚合逻辑 + Controller + 错误处理,目标 ≥ 80% | P1 |
|
||||
| env 重构 | `IamServiceUrl` → `IamServiceTarget`(兼容 REST URL 和 gRPC target),为 P3 切换铺路 | P1 |
|
||||
| DataLoader 骨架 | 新增 `src/dataloader/` 目录,定义 DataLoader 注册器骨架(P2 不启用,P4 接入 GraphQL) | P2 |
|
||||
| Redis 客户端引入 | 新增 `src/shared/cache/redis.client.ts`,P2 仅用于会话/权限缓存,P3 接入聚合缓存 | P2 |
|
||||
| 工作项 | 详情 | 优先级 |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
|
||||
| 引入 Client 抽象层 | 新增 `src/clients/` 目录,定义 `IamClient`/`ClassesClient`/`CoreEduClient` 接口,当前实现为 REST fetch adapter,P3 替换为 gRPC adapter | P0 |
|
||||
| 错误码迁移 | `TEACHER_BFF_*` → `BFF_TEACHER_*`(已完成,见 [application-error.ts](../src/shared/errors/application-error.ts))+ 3 类越权错误码(president §2.7) | P0 |
|
||||
| Zod 输入验证 | Controller 层引入 Zod schema 解析 path/body(classId/examId 等) | P0 |
|
||||
| `/readyz` 下游探针 | 并行 ping iam + classes + core-edu 的 `/healthz`,全部 ok 才返回 ok | P0 |
|
||||
| Vitest 测试框架 | 引入 Vitest,覆盖 Service 聚合逻辑 + Controller + 错误处理,目标 ≥ 80% | P1 |
|
||||
| env 重构 | `IamServiceUrl` → `IamServiceTarget`(兼容 REST URL 和 gRPC target),为 P3 切换铺路 | P1 |
|
||||
| DataLoader 骨架 | 新增 `src/dataloader/` 目录,定义 DataLoader 注册器骨架(P2 不启用,P4 接入 GraphQL) | P2 |
|
||||
| Redis 客户端引入 | 新增 `src/shared/cache/redis.client.ts`,P2 仅用于会话/权限缓存,P3 接入聚合缓存 | P2 |
|
||||
|
||||
**P2 退出标准**:lint + typecheck 零错误;测试覆盖率 ≥ 80%;`/readyz` 真实探针;错误码全部 `BFF_TEACHER_*`;Zod 校验全 Controller 覆盖。
|
||||
|
||||
@@ -228,7 +228,7 @@ graph LR
|
||||
| Schema 设计 | 按教学场景域组织 Query/Mutation(见 §5) |
|
||||
| DataLoader 接入 | Resolver 内调用 DataLoader,覆盖全部 N+1 风险点(见 §6) |
|
||||
| ContentClient gRPC adapter | 调用 content:50054(KnowledgeGraphService + ChapterService/QuestionService 待 ai09 补) |
|
||||
| DataAnaClient gRPC adapter | 调用 data-ana:50055(AnalyticsService + GetTeacherDashboardStats 待 ai11 补) |
|
||||
| DataAnaClient gRPC adapter | 调用 data-ana:50055(AnalyticsService + GetTeacherDashboard 待 ai11 补) |
|
||||
| 双轨读支持 | data-ana 学情查询支持实时查主库 + 聚合查 ClickHouse 宽表 |
|
||||
| REST 兼容期 | REST 端点保留(deprecation header),前端逐步迁移到 GraphQL |
|
||||
| 查询复杂度限制 | GraphQL depth limit + query cost analysis(防恶意查询) |
|
||||
@@ -469,13 +469,13 @@ services/teacher-bff/src/
|
||||
|
||||
### 4.3 P3 REST 扩展
|
||||
|
||||
| method | path | 说明 |
|
||||
| ------ | ------------------------------------ | ----------------------------------------------------------------- |
|
||||
| POST | /teacher/exams | 创建考试(透传 core-edu CreateExam) |
|
||||
| POST | /teacher/homework | 布置作业(透传 core-edu AssignHomework) |
|
||||
| POST | /teacher/grades | 录入成绩(透传 core-edu RecordGrade) |
|
||||
| GET | /teacher/classes/:classId/attendance | 出勤列表(core-edu AttendanceService,待 ai08 补 proto) |
|
||||
| GET | /teacher/dashboard/stats | 仪表盘统计(data-ana GetTeacherDashboardStats,待 ai11 补 proto) |
|
||||
| method | path | 说明 |
|
||||
| ------ | ------------------------------------ | ------------------------------------------------------------ |
|
||||
| POST | /teacher/exams | 创建考试(透传 core-edu CreateExam) |
|
||||
| POST | /teacher/homework | 布置作业(透传 core-edu AssignHomework) |
|
||||
| POST | /teacher/grades | 录入成绩(透传 core-edu RecordGrade) |
|
||||
| GET | /teacher/classes/:classId/attendance | 出勤列表(core-edu AttendanceService,待 ai08 补 proto) |
|
||||
| GET | /teacher/dashboard/stats | 仪表盘统计(data-ana GetTeacherDashboard,待 ai11 补 proto) |
|
||||
|
||||
### 4.4 P4 GraphQL API(目标态)
|
||||
|
||||
@@ -1168,18 +1168,22 @@ BFF 是聚合层,**不发布领域事件**(无业务状态变更)。BFF
|
||||
| /graphql(P4) | 校验 x-user-id + 注入 context | 同上 |
|
||||
| /ai/chat/stream(P5) | 校验 x-user-id | 同上 |
|
||||
|
||||
### 15.2 错误码清单(迁移为 `BFF_TEACHER_*`)
|
||||
### 15.2 错误码清单(`BFF_TEACHER_*` 前缀,B5 + G14 裁决)
|
||||
|
||||
| 错误码 | HTTP | 触发条件 | 当前状态 |
|
||||
| ------------------------------- | ---- | ------------------------ | --------------------------------------------- |
|
||||
| `BFF_TEACHER_VALIDATION_ERROR` | 400 | Zod 校验失败 | 需迁移(当前 `TEACHER_BFF_VALIDATION_ERROR`) |
|
||||
| `BFF_TEACHER_UNAUTHORIZED` | 401 | 缺 x-user-id | 需迁移 |
|
||||
| `BFF_TEACHER_PERMISSION_DENIED` | 403 | (保留,BFF 一般不触发) | 需迁移 |
|
||||
| `BFF_TEACHER_NOT_FOUND` | 404 | 资源不存在 | 需迁移 |
|
||||
| `BFF_TEACHER_CONFLICT` | 409 | 并发冲突 | 需迁移 |
|
||||
| `BFF_TEACHER_BUSINESS_ERROR` | 422 | 业务规则违反 | 需迁移 |
|
||||
| `BFF_TEACHER_BAD_GATEWAY` | 502 | 下游不可达 | 需迁移 |
|
||||
| `BFF_TEACHER_INTERNAL_ERROR` | 500 | 未知异常 | 需迁移 |
|
||||
> 越权防御 3 类错误码(president §2.7):UNAUTHORIZED / FORBIDDEN_RESOURCE / IDENTITY_MISMATCH
|
||||
|
||||
| 错误码 | HTTP | 触发条件 | 状态 |
|
||||
| ---------------------------------- | ---- | ------------------------------------------------------ | --------- |
|
||||
| `BFF_TEACHER_VALIDATION_FAILED` | 400 | Zod 校验失败 | ✅ 已实现 |
|
||||
| `BFF_TEACHER_UNAUTHORIZED` | 401 | x-user-id 缺失或无效(president §2.7) | ✅ 已实现 |
|
||||
| `BFF_TEACHER_FORBIDDEN_RESOURCE` | 403 | teacherId 与资源无归属关系(场景 A,§2.7) | ✅ 已实现 |
|
||||
| `BFF_TEACHER_IDENTITY_MISMATCH` | 403 | JWT teacherId 与 body teacherId 不一致(场景 B,§2.7) | ✅ 已实现 |
|
||||
| `BFF_TEACHER_NOT_FOUND` | 404 | 资源不存在 | ✅ 已实现 |
|
||||
| `BFF_TEACHER_CONFLICT` | 409 | 并发冲突 | ✅ 已实现 |
|
||||
| `BFF_TEACHER_BUSINESS_ERROR` | 422 | 业务规则违反(含 P2 未就绪字段) | ✅ 已实现 |
|
||||
| `BFF_TEACHER_UPSTREAM_UNAVAILABLE` | 502 | 下游 gRPC 不可达 | ✅ 已实现 |
|
||||
| `BFF_TEACHER_AGGREGATION_FAILED` | 500 | 聚合多下游部分失败且无降级数据(president §2.6) | ✅ 已实现 |
|
||||
| `BFF_TEACHER_INTERNAL_ERROR` | 500 | 未知异常 | ✅ 已实现 |
|
||||
|
||||
**下游业务错误透传**:iam 抛 `IAM_*`、core-edu 抛 `CORE_EDU_*`、classes 抛 `CLASSES_*`,BFF 不改写错误码,直接透传给前端。
|
||||
|
||||
@@ -1269,7 +1273,7 @@ async readiness() {
|
||||
| 调用 | content | gRPC 50054(P4+) | KnowledgeGraphService.GetPrerequisites / GetLearningPath | 知识图谱 | P4 | ✅ 已有 |
|
||||
| 调用 | content | gRPC 50054(P4+) | _*ChapterService.* / QuestionService._** | 章节/题库 | P4 | ❌ 待 ai09 补 |
|
||||
| 调用 | data-ana | gRPC 50055(P4+) | AnalyticsService.GetClassPerformance / GetStudentWeakness / GetLearningTrend | 学情诊断 | P4 | ✅ 已有 |
|
||||
| 调用 | data-ana | gRPC 50055(P4+) | **GetTeacherDashboardStats**(建议新增) | 仪表盘统计 | P4 | ❌ 待 coord 仲裁 |
|
||||
| 调用 | data-ana | gRPC 50055(P4+) | **GetTeacherDashboard**(建议新增) | 仪表盘统计 | P4 | ❌ 待 coord 仲裁 |
|
||||
| 调用 | ai | gRPC 50057(P5+) | AiService.GenerateQuestion / Chat / OptimizeExpression | AI 辅助 | P5 | ✅ 已有 |
|
||||
| 调用 | ai | gRPC 50057 streaming(P5+) | **AiService.StreamChat** | SSE 流式对话 | P5 | ✅ 已有 |
|
||||
| 调用 | msg | gRPC 50056(P5+) | NotificationService.ListNotifications / SearchNotifications / MarkAsRead | 通知聚合 | P5 | ✅ 已有 |
|
||||
@@ -1463,7 +1467,7 @@ Redis 单实例 256MB 足够,无需集群。
|
||||
| 3 | **iam 补 4 个 RPC** | GetViewports / GetEffectivePermissions / Logout / GetPublicKey | 强烈建议 P2 补全(teacher-bff 直接依赖) | P2 |
|
||||
| 4 | **core-edu 补 AttendanceService** | service 级新增 | 建议 P3 补全 | P3 |
|
||||
| 5 | **core-edu 补 GetClassesByTeacher RPC** | 按教师拉班级 | 建议新增(当前 ListClasses 仅按 gradeId) | P3 |
|
||||
| 6 | **data-ana 补 GetTeacherDashboardStats RPC** | 仪表盘统计聚合 | 建议新增 | P4 |
|
||||
| 6 | **data-ana 补 GetTeacherDashboard RPC** | 仪表盘统计聚合 | 建议新增 | P4 |
|
||||
| 7 | **iam 补 BatchGetUsers RPC** | DataLoader 批量化 | 建议新增(否则 DataLoader 退化为一对一) | P4 |
|
||||
| 8 | **core-edu 补 BatchGetClasses RPC** | DataLoader 批量化 | 建议新增 | P4 |
|
||||
| 9 | **Kafka consumer 引入时机** | A. P5 引入精确失效 / B. 全程短 TTL 兜底 | **P5 评估**(取决于权限变更频率) | P5 |
|
||||
|
||||
Reference in New Issue
Block a user