# 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-ai01：matrix.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-ai01：shared-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-ai01：API 版本化路由方案（/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 仲裁最终方案：
  - 方案 A：Gateway 路由 `/api/v1/iam/v1/*path`，proxy 透传 `/iam/v1/*path`（前端调用 `/api/v1/iam/v1/auth/login`）
  - 方案 B：Gateway 路由 `/api/v1/iam/*path`，proxy 透传 `/iam/*path`，下游 Controller 内加 `/v1`（前端调用 `/api/v1/iam/v1/auth/login`，Gateway 不含 `/v1`）
  - 方案 C：其他
- **建议方案**：采纳方案 B（Gateway 路由不含服务级 `/v1`，由下游 Controller 添加），符合裁决第 4 条"避免双 v1"和第 3 条"Gateway 不改路径直接透传"
- **状态**：待 coord 仲裁

### ISSUE-004-ai01：zap vs slog 选型

- **提请方**：ai01
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：W3 裁决"首次实现即用 slog"，但 [shared-go/logger](../../../packages/shared-go/logger/logger.go) 的实现选型（zap 还是 slog）影响 ai01 接入方式。若 shared-go 用 zap，ai01 需通过 shared-go/logger 间接使用 zap；若 shared-go 用 slog，ai01 可直接用标准库 `log/slog` 或 shared-go 封装。需 coord 仲裁 shared-go/logger 最终选型。
- **建议方案**：shared-go/logger 基于 `log/slog` 封装（标准库零依赖，W3 裁决原文即 slog），ai01 直接 import shared-go/logger
- **状态**：待 coord 仲裁

### ISSUE-005-ai01：go.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-ai01：01-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-ai01：01-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-ai01：W1/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 编号未使用（预留）。
