feat(teacher-bff): 完整实现 teacher-bff GraphQL 聚合层

包含 clients/graphql/middleware、health probes、shared-ts contracts 等
This commit is contained in:
SpecialX
2026-07-10 19:10:07 +08:00
parent b82593aac2
commit 99155a5ea1
37 changed files with 2862 additions and 324 deletions

View File

@@ -501,3 +501,27 @@
| P3 MF 降级与版本协商 | Remote `remoteEntry.js` 加载失败 → fallback 独立页面 + 错误提示;共享依赖版本不匹配 → 运行时协商降级 |
| P3 AI 答疑 SSE 流式P5 | `POST /student/ai/stream-chat` SSE 流式响应 + `streamingText` 逐字渲染 + 打字机效果 + AbortController 取消 |
| P3 权限点命名 | 与 student-bff §3.2 对齐 `STUDENT_*_READ` 风格(`STUDENT_DASHBOARD_READ`/`STUDENT_EXAM_READ`/`STUDENT_HOMEWORK_SUBMIT`/`STUDENT_GRADE_READ` 等) |
### 2.16 teacher-bffTS/NestJSP2
> teacher-bff 是 GraphQL 聚合层B1 裁决P2 起直接 GraphQL无 DB无 @RequirePermissionB3 豁免做数据权限防御B4。本节记录 teacher-bff 特有的"场景→技术"映射。
| 场景 | 技术/规则 |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P2 GraphQL 直接实现 | B1 裁决P2 起直接 GraphQL Yoga禁止 REST 过渡POST /graphql endpoint + SDL-first schemapackages/shared-ts/contracts/graphql/ |
| P2 gRPC 下游通信 | B2 裁决:首次实现即 gRPC禁止 REST fetch@grpc/grpc-js + @grpc/proto-loader 动态加载 proto无生成代码依赖 |
| DownstreamClient 抽象 | B8 裁决3 个 BFF 共用标准抽象层src/clients/base.client.ts + grpc.factory.ts + per-service clientiam/client.interface + grpc + mock |
| DEV_MODE mock 开关 | TEACHER_BFF_DEV_MODE=true → IamClient 走 mock固定数据false → gRPC未就绪 RPC 降级 mock + warningpresident §2.6 降级模式 B |
| 越权防御 3 类错误码 | president §2.7UNAUTHORIZED(401) / FORBIDDEN_RESOURCE(403 场景 A 资源无归属) / IDENTITY_MISMATCH(403 场景 B JWT 与 body 不一致) |
| AuthorizationGuard P2 策略 | president §2.9P2 DEV_MODE 放行 + 日志告警生产保守拒绝P3+ 接入 core-edu.GetClassesByTeacher 真实校验 |
| /readyz 探针注册表 | president §2.4DownstreamHealthCheck 注册表模式P2 = Redis + iam gRPC2 项按阶段扩展P3=3/P4=5/P5=7必需依赖失败返 503可选失败返 200+degraded |
| Dashboard Resolver P2 实现 | president §2.8P2 仅调 iam gRPCuser+viewports 有数据),未启用字段返 null + extensions.warning="field_unavailable_in_p2" |
| admin 命名空间预留 | president §5.1P2 预留 schemaAdminQuery/AdminMutation 类型声明Resolver 返 null/空P6 实现 Resolver 真实数据admin-portal 复用 teacher-bff GraphQL endpoint |
| 错误码 BFF_TEACHER_ 前缀 | B5 + G14 裁决:统一 BFF_ 前缀(非 TEACHER_BFF_含 10 个错误码validation/unauthorized/forbidden_resource/identity_mismatch/not_found/conflict/business/upstream_unavailable/aggregation_failed/internal |
| ESM .js 后缀 | G12 裁决NestJS ESM 模式下相对 import 必须带 .js 后缀(构建产物),所有源码 import 路径已对齐 |
| import type 强制 | G13 裁决:仅类型导入用 `import type`,运行时导入用 `import` |
| gRPC 健康检查 | grpc.health.v1.Health/Check 标准协议makeUnaryRequest + 2s 超时,解析 HealthCheckResponse.status varint |
| ioredis ESM 导入类型 | `import IORedis from "ioredis"` ESM 下报 TS2709/TS2351改用命名导入 `import { Redis } from "ioredis"`,类型注解用 `Redis` 而非 `IORedis` |
| NestJS HttpAdapter 路由注册 | `app.getHttpAdapter().get()` 的 RequestHandler 与 Express 类型不兼容,改用 `getInstance() as Application` 获取 Express 实例后注册路由 |
| graphql-yoga v5 context | YogaInitialContext 不含 responsecontext 回调只解构 `{ request }`buildGraphQLContext 只接收 requestP5+ SSE 再扩展 response |
| readonly 属性延迟初始化 | `readonly` 属性只能在构造函数赋值,需在 initLogger() 方法中重新赋值时移除 readonly 修饰符 |