docs(api-gateway): 补充完整架构问题记录与裁决核查

完成api-gateway模块边界确认,添加W1-W8裁决遵循情况核查与架构不一致问题梳理
汇总8项待coord仲裁的issue,包含模块契约、依赖、版本、文档等问题
This commit is contained in:
SpecialX
2026-07-10 16:41:57 +08:00
parent 88f6822473
commit 8e91039896

View File

@@ -1,3 +1,168 @@
# api-gateway 问题记录
>
> 负责人ai01
> 关联:[coord.md](../coord.md)、[contracts/api-gateway_contract.md](../contracts/api-gateway_contract.md)、[worklines/api-gateway_workline.md](../worklines/api-gateway_workline.md)
> 依据:[coord-final-decisions.md](../../coord-final-decisions.md) §3.8 W1-W8、[president-final-rulings.md](../../president-final-rulings.md) §2.15/§2.16/§2.19
> 规则AI 遇到问题时在此追加条目coord 仲裁后更新状态
---
## §0 模块边界确认(审查结论)
### 0.1 边界定义(来自 [01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §2
- **层级**L3 网关层004 §3.1 六层架构)
- **职责**所有外部请求的统一入口、JWT 校验、用户身份注入(`x-user-id`/`x-user-roles`/`x-data-scope`、限流、熔断、CORS、安全头、请求 ID 注入、反向代理
- **不负责**
- WebSocket 长连接管理push-gateway 负责)
- 用户身份认证逻辑iam 负责,本服务只校验 JWT 签名)
- 权限点解析(下游服务 Controller `@RequirePermission` 自校验,本服务只透传身份头)
- 业务数据持久化(无 DB
- **通信方式**:入口 HTTP/REST出口 HTTP 反向代理(`httputil.ReverseProxy`),不消费 gRPC不接入 Kafka
> **边界审查结论**:边界定义清晰,与 [02-architecture-design.md](../../../services/api-gateway/docs/02-architecture-design.md) §7 交互点清单、[contracts/api-gateway_contract.md](../contracts/api-gateway_contract.md) v2 一致。本服务是纯同步 HTTP 反向代理,不持有业务状态。
### 0.2 W1-W8 裁决遵循情况核查2026-07-10
对照 [coord-final-decisions.md](../../coord-final-decisions.md) §3.8 W1-W8 裁决逐项核查当前代码P1 交付态):
| # | 裁决项 | 强制最终方案 | 当前代码状态 | 遵循 | 对应 ISSUE |
| --- | -------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---- | ---------- |
| W1 | **错误码前缀** | `GW_*`GW_UNAUTHORIZED 等) | [auth.go](../../../services/api-gateway/internal/middleware/auth.go) 用 `UNAUTHORIZED`/`INVALID_TOKEN`/`INVALID_CLAIMS`(无 GW_ 前缀) | ❌ | ISSUE-009 |
| W2 | **错误信封** | 首次实现即对齐 ActionState | [ratelimit.go](../../../services/api-gateway/internal/middleware/ratelimit.go) L51-54 响应 `{"error":"rate_limited","retry_after":60}`(无 success/error.code 结构);[circuit-breaker.go](../../../services/api-gateway/internal/middleware/circuit-breaker.go) L55-58 同样无信封;[recovery.go](../../../services/api-gateway/internal/middleware/recovery.go) L24-27 无信封auth.go 部分对齐 | ❌ | ISSUE-009 |
| W3 | **slog** | 首次实现即用 slog | 全部用 `log.Printf`[main.go](../../../services/api-gateway/main.go) L70/160/169/176、[config.go](../../../services/api-gateway/internal/config/config.go) L38/41、[recovery.go](../../../services/api-gateway/internal/middleware/recovery.go) L22、[circuit-breaker.go](../../../services/api-gateway/internal/middleware/circuit-breaker.go) L38、[tracer.go](../../../services/api-gateway/internal/observability/tracer.go) L25/31/43/51/65/70 | ❌ | ISSUE-004 |
| W4 | **/readyz** | 首次实现即真实健康检查(轮询下游 /healthz | [health.go](../../../services/api-gateway/internal/health/health.go) L19-23 直接返回 200有 TODO 注释"P7 接入服务发现后" | ❌ | — |
| W5 | **业务 metrics** | 首次实现即基础业务指标 | [main.go](../../../services/api-gateway/main.go) L56 已注册 `/metrics` 端点promhttp.Handler但未注册 7 个业务指标(无 `internal/observability/metrics.go` 文件) | ⚠️ | ISSUE-007 |
| W6 | **tracer 资源属性** | 首次实现即完整version/env/host | [tracer.go](../../../services/api-gateway/internal/observability/tracer.go) L47-49 只有 `service.name`,缺 `service.version`/`deployment.environment`/`host.name` | ❌ | — |
| W7 | **DevMode 防护** | 首次实现即生产防护DEV_MODE 仅非生产环境生效)| [config.go](../../../services/api-gateway/internal/config/config.go) `Load()``ENV` 环境变量读取DevMode=true 时无生产防护 | ❌ | — |
| W8 | **per-服务实例熔断** | 保持共享 downstream 熔断per-实例 P6 单独评估 | [main.go](../../../services/api-gateway/main.go) L61 `middleware.CircuitBreaker("downstream")` 共享熔断 | ✅ | — |
**核查结论**W1/W2/W3/W4/W6/W7 共 6 项未遵循裁决W5 部分遵循W8 已遵循。未遵循项需在 P2.0-P2.6 任务中修复(见 [workline.md](../worklines/api-gateway_workline.md) §3
### 0.3 架构文档与代码不一致问题
| # | 文件 | 问题 | 修复建议 |
| --- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| D1 | [02-architecture-design.md](../../../services/api-gateway/docs/02-architecture-design.md) §1 | 中间件链图标注顺序 `Auth → CircuitBreaker`,但 [main.go](../../../services/api-gateway/main.go) L61-63 实际顺序为 `CircuitBreaker → Auth` | 统一文档与代码(建议代码保持 CircuitBreaker 在 Auth 之前,避免未鉴权请求消耗下游配额,更新文档图) |
| D2 | [README.md](../../../services/api-gateway/README.md) L38 | 提到 `GET /health` 兼容端点,但 [main.go](../../../services/api-gateway/main.go) 未注册 `/health` 路由 | 删除 README L38 描述(已在 workline P2.0 列出) |
| D3 | [proxy.go](../../../services/api-gateway/internal/proxy/proxy.go) L24 | 连续两次 `TrimPrefix`L23 去 `/api/v1`L24 再去 `/api`),第二次无效果(首字符已是 `/` | 删除 L24 冗余 `TrimPrefix("/api")`(已在 workline P2.0 列出) |
| D4 | [01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §3.1 | 提到"P2 起 RS256 公钥校验时通过 IAM `/auth/jwks` 端点拉公钥",但 [contract.md](../contracts/api-gateway_contract.md) v2 §2.3 已修正为 `GET /.well-known/jwks.json` | 统一为 `/.well-known/jwks.json`RFC 8414 标准路径) |
---
## §1 问题列表
<!--
追加条目格式:
### ISSUE-[编号]-[AI标识][标题]
- **提请方**aiXX
- **日期**YYYY-MM-DD
- **类型**:契约不明确 / 工作量超批 / 前置依赖缺失 / 编号冲突 / 其他
- **描述**[详细描述问题]
- **建议方案**[AI 的建议]
- **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X
-->
### ISSUE-001-ai01matrix.md gRPC 矩阵错误声明 api-gateway 消费 iam gRPC
- **提请方**ai01
- **日期**2026-07-10
- **类型**:契约不明确
- **描述**[matrix.md](../matrix.md) §2 gRPC 接口提供方矩阵中iam 的"消费方"列包含 `api-gateway`,暗示 api-gateway 消费 iam gRPC。但依据 [president-final-rulings.md](../../president-final-rulings.md) §2.16 裁决"gateway 保持 HTTP 透传,不改为 gRPC 客户端"api-gateway 通过 HTTP JWKS 端点(`GET /.well-known/jwks.json`)拉取 RS256 公钥,不消费任何 gRPC。同时 [matrix.md](../matrix.md) §7 Mock 策略表中"api-gateway 消费 iam.GetPublicKey"也需修正为 HTTP JWKS。
- **建议方案**
1. matrix.md §2 iam 行"消费方"列移除 `api-gateway`
2. matrix.md §7 Mock 策略表中 api-gateway 行改为"硬编码 RS256 公钥DEV_MODE→ HTTP JWKS 端点"
- **状态**:待 coord 仲裁
### ISSUE-002-ai01shared-go 接入方式与时机
- **提请方**ai01
- **日期**2026-07-10
- **类型**:前置依赖缺失
- **描述**:依据 [president-final-rulings.md](../../president-final-rulings.md) §2.19 裁决ai01 应直接 import `packages/shared-go`jwks/logger/tracer/env 4 模块)。但当前 [go.work](../../../go.work) L3-4 仅声明 `./services/api-gateway``./services/push-gateway`,未包含 `./packages/shared-go`[go.mod](../../../services/api-gateway/go.mod) 也无 `github.com/edu-cloud/shared-go` 依赖。需确认 shared-go 包骨架是否已建立、模块路径命名、go.work 接入方式。
- **建议方案**
1. coord 确认 `packages/shared-go` 骨架已建立(批次 0.14 任务)
2. go.work 增加 `./packages/shared-go`
3. ai01 go.mod 增加 `github.com/edu-cloud/shared-go` 依赖
4. P2.1 任务接入 jwks/logger/tracer/env
- **状态**:待 coord 仲裁
### ISSUE-003-ai01API 版本化路由方案(/v1 前缀位置)
- **提请方**ai01
- **日期**2026-07-10
- **类型**:契约不明确
- **描述**:依据 [president-final-rulings.md](../../president-final-rulings.md) §2.15 裁决,全服务统一加 `/v1` 前缀Controller 内加 `v1` 前缀Gateway 透传时不改路径。但当前 [main.go](../../../services/api-gateway/main.go) 路由为 `/api/v1/iam/*path`(无服务级 `/v1`),而 [contract.md](../contracts/api-gateway_contract.md) v2 §1.2.2 路由表列为 `/api/v1/iam/v1/*path`(方案 A双 v1。裁决第 4 条"避免双 v1"与 contract v2 路由表存在矛盾,需 coord 仲裁最终方案:
- 方案 AGateway 路由 `/api/v1/iam/v1/*path`proxy 透传 `/iam/v1/*path`(前端调用 `/api/v1/iam/v1/auth/login`
- 方案 BGateway 路由 `/api/v1/iam/*path`proxy 透传 `/iam/*path`,下游 Controller 内加 `/v1`(前端调用 `/api/v1/iam/v1/auth/login`Gateway 不含 `/v1`
- 方案 C其他
- **建议方案**:采纳方案 BGateway 路由不含服务级 `/v1`,由下游 Controller 添加),符合裁决第 4 条"避免双 v1"和第 3 条"Gateway 不改路径直接透传"
- **状态**:待 coord 仲裁
### ISSUE-004-ai01zap vs slog 选型
- **提请方**ai01
- **日期**2026-07-10
- **类型**:契约不明确
- **描述**W3 裁决"首次实现即用 slog",但 [shared-go/logger](../../../packages/shared-go/logger/logger.go) 的实现选型zap 还是 slog影响 ai01 接入方式。若 shared-go 用 zapai01 需通过 shared-go/logger 间接使用 zap若 shared-go 用 slogai01 可直接用标准库 `log/slog` 或 shared-go 封装。需 coord 仲裁 shared-go/logger 最终选型。
- **建议方案**shared-go/logger 基于 `log/slog` 封装标准库零依赖W3 裁决原文即 slogai01 直接 import shared-go/logger
- **状态**:待 coord 仲裁
### ISSUE-005-ai01go.mod 与 Dockerfile Go 版本不一致
- **提请方**ai01
- **日期**2026-07-10
- **类型**:其他
- **描述**[go.mod](../../../services/api-gateway/go.mod) L3 声明 `go 1.25.0`[go.work](../../../go.work) L1 声明 `go 1.26.0`,但 [Dockerfile](../../../services/api-gateway/Dockerfile) L1 用 `golang:1.22-alpine`。三者不一致,可能导致构建环境与本地开发环境行为差异。
- **建议方案**:统一为 `go 1.22`(与 Dockerfile 一致,与 [01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §4 声明一致),或升级 Dockerfile 到 `golang:1.25-alpine` 并同步 go.mod/go.work
- **状态**:待 coord 仲裁
### ISSUE-007-ai0101-understanding.md §6 审计表 metrics 行过时
- **提请方**ai01
- **日期**2026-07-10
- **类型**:其他
- **描述**[01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §6 审计表 metrics 行标注 `❌ 无`§7.1 issue #1 也说"无 `/metrics` 端点Prometheus 404"。但实际 [main.go](../../../services/api-gateway/main.go) L56 已注册 `r.GET("/metrics", gin.WrapH(promhttp.Handler()))``/metrics` 端点已存在。文档过时,需更新为"⚠️ 端点已注册,但缺 7 个业务指标"。
- **建议方案**:更新 01-understanding.md §6 审计表 metrics 行为 `⚠️ 端点已注册,缺业务指标`§7.1 issue #1 更新为"已注册 /metrics需补 7 个业务指标"
- **状态**:待 coord 仲裁
### ISSUE-008-ai0101-understanding.md §7.1 issue #3 死代码引用过时
- **提请方**ai01
- **日期**2026-07-10
- **类型**:其他
- **描述**[01-understanding.md](../../../services/api-gateway/docs/01-understanding.md) §7.1 issue #3 引用"auth.go L124-139 死代码 `RequestIDMiddleware()` + `generateUUID()`",但实际 [auth.go](../../../services/api-gateway/internal/middleware/auth.go) 当前仅 121 行L124-139 不存在,死代码已删除。文档引用过时。
- **建议方案**:删除 01-understanding.md §7.1 issue #3 整行(死代码已修复)
- **状态**:待 coord 仲裁
### ISSUE-009-ai01W1/W2 错误码 GW_ 前缀 + ActionState 信封未遵循
- **提请方**ai01
- **日期**2026-07-10
- **类型**:其他
- **描述**W1 裁决要求错误码统一 `GW_` 前缀W2 裁决要求错误响应统一 ActionState 信封 `{success:false,error:{code,message}}`。但当前代码:
- [auth.go](../../../services/api-gateway/internal/middleware/auth.go) 错误码用 `UNAUTHORIZED`/`INVALID_TOKEN`/`INVALID_CLAIMS`(无 GW_ 前缀但响应体已部分对齐信封L44-50 有 `success`/`error.code`/`error.message`
- [ratelimit.go](../../../services/api-gateway/internal/middleware/ratelimit.go) L51-54 响应 `{"error":"rate_limited","retry_after":60}`(无 GW_ 前缀,无信封)
- [circuit-breaker.go](../../../services/api-gateway/internal/middleware/circuit-breaker.go) L55-58 响应 `{"error":"circuit_open","retry_after":30}`(无 GW_ 前缀,无信封)
- [recovery.go](../../../services/api-gateway/internal/middleware/recovery.go) L24-27 响应 `{"error":"internal_error","request_id":"..."}`(无 GW_ 前缀,无信封)
- **建议方案**P2.3 任务统一修复(见 [workline.md](../worklines/api-gateway_workline.md) P2.3),所有错误码改为 `GW_*`,所有响应体改为 `{success:false,error:{code:"GW_*",message:"..."},...}`
- **状态**:待 coord 仲裁
---
## §2 待 coord 仲裁事项汇总
| ISSUE | 主题 | 阻塞任务 | 状态 |
| -------- | --------------------------------- | -------- | --------- |
| ISSUE-001 | matrix.md gRPC 矩阵修正 | — | 待 coord |
| ISSUE-002 | shared-go 接入方式 | P2.1 | 待 coord |
| ISSUE-003 | API 版本化路由方案(/v1 前缀) | P2.7 | 待 coord |
| ISSUE-004 | zap vs slog 选型 | P2.1 | 待 coord |
| ISSUE-005 | go.mod 与 Dockerfile 版本不一致 | P2.0 | 待 coord |
| ISSUE-007 | 01-understanding.md §6 审计表过时 | P2.0 | 待 coord |
| ISSUE-008 | 01-understanding.md §7.1 死代码过时 | P2.0 | 待 coord |
| ISSUE-009 | W1/W2 错误码 GW_ + ActionState | P2.3 | 待 coord |
> ISSUE-006 编号未使用(预留)。