Files
Edu/services/api-gateway/docs/01-understanding.md
SpecialX 4307f6b73c feat(api-gateway): 实现 W1-W8 网关硬化与 P2-P5 路由扩展
依据 coord-final-decisions §3.8 W1-W8 裁决与
president-final-rulings §2.15/§2.16/§2.19 完整实现网关硬化:

- W1/W2: 错误码 GW_ 前缀 + ActionState 信封响应体
- W3: 全量替换为 log/slog 结构化日志
- W4: /readyz 并行 ping 9 下游 + 软失败规则
- W5: 7 个业务 Prometheus 指标 + /metrics 端点
- W6: tracer 资源属性补全(name/version/env/host)
- W7: DevMode=true && ENV=production panic 防护
- W8: 保持共享 downstream 熔断

P2 RS256 升级:接入 shared-go/jwks.Fetcher(TTL 5min)。
P2.7+P3-P5 路由扩展:student/parent/messages/dashboard。
文档同步:README/01/02/known-issues,arch.db 已更新。
质量校验:go vet + build + test 均通过。
2026-07-10 18:15:48 +08:00

15 KiB
Raw Blame History

模块理解确认书 — api-gateway

AIai01Go 网关层) 阶段:阶段 1 交付物 日期2026-07-09 关联:004 架构影响地图AI 分配方案


1. 我在架构中的位置

  • 层级L3 网关层004 §3.1 六层架构)
  • 上游4 个微前端teacher-portal / student-portal / parent-portal / admin-portal通过浏览器/移动端 HTTP 请求
  • 下游3 个 BFF + 6 个业务服务,共 9 个反向代理目标
    • BFFteacher-bff3003、student-bff待建、parent-bff待建
    • 业务iam3002、core-edu3004、content3005、data-ana3006、msg3007、ai3008
  • 通信方式
    • 入口HTTP/REST含 WebSocket 升级透传到 push-gateway不在本服务
    • 出口HTTP 反向代理(httputil.ReverseProxyP3 起部分链路改 gRPC
  • 不持有业务状态:仅做路由/鉴权/限流/熔断/可观测,无 DB

2. 我的限界上下文

  • 我负责所有外部请求的统一入口、JWT 校验、用户身份注入、限流、熔断、CORS、安全头、请求 ID 注入、反向代理
  • 聚合/实体:无(网关无领域模型)
  • 业务领域:不属于 D1-D6 任一业务领域,属于基础设施层
  • 我不负责
    • WebSocket 长连接管理push-gateway 负责)
    • 用户身份认证逻辑iam 负责,本服务只校验 JWT 签名)
    • 权限点解析(业务服务 Controller 通过 @RequirePermission 自行校验,本服务只透传 x-user-id/x-user-roles 头)
    • 业务数据持久化(无 DB

3. 我与外部的契约

3.1 我消费的 proto message

proto message 用途
iam.proto UserInfo / TokenPair P2 起 RS256 公钥校验时通过 IAM /auth/jwks 端点拉公钥HTTP非 gRPC

P1 阶段用 HS256 共享密钥,不消费任何 proto。P2 起 RS256 通过 IAM 暴露的 JWKS 端点(/.well-known/jwks.json)拉取公钥,仍是 HTTP无需 gRPC 客户端。

3.2 我暴露的 API 端点

方法 路径 鉴权 说明
GET /healthz liveness 探针
GET /readyz readiness 探针(当前 stub需补真实检查
ANY /api/v1/classes + /api/v1/classes/*path JWT 代理到 classes/core-edu 服务
ANY /api/v1/iam + /api/v1/iam/*path JWT除 register/login/refresh 白名单) 代理到 iam 服务
ANY /api/v1/teacher + /api/v1/teacher/*path JWT 代理到 teacher-bff
ANY /api/v1/exams /homework /grades + /*path JWT 代理到 core-edu
ANY /api/v1/textbooks /chapters /knowledge-points /questions + /*path JWT 代理到 content
ANY /api/v1/notifications + /*path JWT 代理到 msg
ANY /api/v1/ai + /*path JWT 代理到 ai
ANY /api/v1/analytics + /*path JWT 代理到 data-ana
GET /metrics 无(待实现) Prometheus 指标端点

3.3 我发布/消费的 Kafka 事件

。api-gateway 不接入 Kafka是纯同步 HTTP 反向代理。

3.4 错误码前缀

错误码 HTTP 触发条件
UNAUTHORIZED 401 缺失 Authorization 头
INVALID_TOKEN 401 JWT 签名/格式错误
INVALID_CLAIMS 401 JWT claims 解析失败
RATE_LIMITED 429 超出令牌桶限流(响应体当前写 error: rate_limited,需统一加 code
CIRCUIT_OPEN 503 下游熔断打开(响应体当前写 error: circuit_open
INTERNAL_ERROR 500 panic 兜底Recovery 中间件)
REQUEST_TOO_LARGE 413 请求体超 10MB由 MaxBytesReader 自动触发,但响应非 JSON 信封)

错误码统一约定:本服务无业务错误码前缀(无业务),仅上述 7 个 HTTP 语义错误。响应体需统一为 { success: false, error: { code, message } } 信封,与 classes 黄金模板 ApplicationError 对齐。

4. 我的技术栈

  • 语言Go 1.22go.mod 与 Dockerfile 一致)
  • 框架Gin v1.12.0
  • 核心依赖
    • github.com/golang-jwt/jwt/v5 v5.2.1JWT 校验)
    • github.com/sony/gobreaker/v2 v2.1.0(熔断)
    • github.com/google/uuid v1.6.0(请求 ID
    • go.opentelemetry.io/otel v1.44.0 + otelgin v0.69.0(链路追踪)
  • 存储:无 DB无 Redis限流用内存令牌桶 sync.Map
  • 构建:多阶段 Dockerfilegolang:1.22-alpine → alpine:3.20,非 root 用户)

5. 我的阶段归属

  • 阶段P1已实现+ P2 升级RS256+ P6 硬化(限流策略表、熔断阈值细化)
  • 当前阶段目标
    • P1 退出标准已达成classes 域 CRUD 端到端跑通(见 known-issues 工作日志 2026-07-09 00:24
    • P2 待升级JWT HS256 → RS256IAM 签发Gateway 公钥校验)
  • 依赖上游阶段产出
    • P2 iam 服务须暴露 JWKS 端点(/.well-known/jwks.json)供本服务拉公钥

6. 我需要对齐的黄金模板项(对照 classes 服务)

classes黄金模板 api-gateway 现状 差距
权限装饰器 @RequirePermission() N/AGo 无装饰器;用中间件 AuthMiddleware 替代) 等价实现
错误码前缀 CLASSES_* 无前缀(基础设施层) 设计合理
loggerpino shared/observability/logger.ts log/slog 结构化 JSON 对齐
metrics shared/observability/metrics.ts 暴露 /metrics 7 个业务指标promauto 对齐
tracer shared/observability/tracer.ts OTel SDK internal/observability/tracer.goW6 资源属性完整) 对齐
/healthz 对齐
/readyz 检查 DB SELECT 1 并行 ping 下游 /healthz软失败规则 对齐
优雅关闭 SIGTERM → app.close() srv.Shutdown(ctx) 5s 超时 对齐
测试覆盖率 ≥ 80% ~25%(仅 circuit-breaker + ratelimit ⚠️ 待补 auth/cors/security/recovery/requestid/proxy 测试
Dockerfile 多阶段 + 非 root + healthcheck 对齐
Zod 输入验证 schema.safeParse(body) N/AGo 无 ZodShouldBindJSON 等价实现
GlobalErrorFilter GlobalErrorFilter Recovery 中间件兜底 等价实现

7. 服务审计表(按 ai-allocation §10 模板)

服务 权限装饰器 错误码前缀 logger metrics tracer /healthz /readyz 优雅关闭 测试覆盖率 Dockerfile
api-gateway ⚠️ 中间件替代 GW_ 前缀 slog 7 指标 OTel 并行 ping 5s 超时 ~25% 多阶段

7.1 详细问题清单(按严重度排序)

更新日期2026-07-10P2-P5 实施后复核)

# 严重度 文件 问题 状态 修复说明
1 internal/observability/ /metrics 端点Prometheus 404 已修复 新增 metrics.go,注册 7 个业务指标main.go 暴露 /metrics
2 internal/health/health.go /readyz 直接返回 200未检查下游 已修复 改为并行 ping 9 个下游 /healthz软失败规则iam/teacher-bff required其余 optional
3 internal/middleware/auth.go 死代码 RequestIDMiddleware() + generateUUID() 重复 requestid.go 且未使用 已修复 死代码已删除P2.0
4 全文件 log.Printf,不符合 coding-standards §3.8 log/slog 结构化日志要求 已修复 全部 log.Printf/log.Fatal 替换为 slog.Info/slog.ErrorW3
5 go.mod L3 vs Dockerfile L1 go.mod 声明 go 1.25.0Dockerfile 用 golang:1.22-alpine 已修复 go.mod 统一为 go 1.22go.work 因 push-gateway 要求升级为 go 1.25.0workspace 兼容更低版本模块)
6 internal/middleware/auth.go P2 待升级 HS256 → RS256 已修复 接入 shared-go/jwks.FetcherRS256 公钥校验 + kid 路由W6 资源属性完整)
7 internal/middleware/cors.go L21 CORS_ORIGINS 直接 os.Getenv,未纳入 Config 结构 已修复 CORS() 改为 CORS(cfg *config.Config),从 Config 读取白名单
8 internal/middleware/ratelimit.go 单实例内存令牌桶,水平扩展后限流失效 P6 P6 引入 Redis 令牌桶(redis_rate),支持多副本一致
9 internal/middleware/auth.go L68 DevMode 注入固定 teacher,admin 角色,生产风险 已修复 启动时 DevMode=true && ENV=production panic 拒绝启动W7 防护config.go
10 README.md L38 提到 GET /health 兼容端点,但代码未注册 已修复 README 重写,删除 /health 描述
11 internal/proxy/proxy.go L24 连续两次 TrimPrefix/api/v1 后再 /api)逻辑冗余 已修复 冗余 TrimPrefix 已删除P2.0
12 Dockerfile L18 构建命令 ./main.go 而非 ./ ⚠️ 保留 ./main.go 单文件构建可正常工作,-ldflags="-s -w" 已添加;改为 . 需评估是否有其他 main 包文件(当前无)
13 测试 auth/cors/security/recovery/requestid/proxy 无测试 P6 P6 补 *_test.go,目标覆盖率 ≥ 80%

8. 风险与假设

  • 假设iam 服务在 P2 会暴露 JWKS 端点,否则 RS256 升级阻塞
  • 假设:所有下游服务在 P1 都已实现 /healthz(用于 /readyz 真实检查)
  • 风险单实例限流在多副本部署后失效P6 需迁 Redis
  • 风险DevMode 旁路若误开到生产,可绕过鉴权注入 admin 角色
  • 未决:是否在 Gateway 层做权限校验(当前仅透传角色,由下游服务自校验)?建议保持现状,避免 Gateway 持有权限点常量造成耦合