Files
Edu/docs/architecture/issues/objections/api-gateway_issue.md
SpecialX c6362f4b04
Some checks failed
CI / quality-ts (push) Failing after 53s
CI / quality-go (push) Failing after 7s
CI / quality-proto (push) Failing after 3s
CI / deploy (push) Has been skipped
docs(docs): 更新 issue 仲裁状态 + 生成集成测试阶段规划
- 14 个 issue.md 状态字段更新为"已裁决"
  - 对应 coord.md ARB-001~022 章节
  - 共 104 处状态字段更新
- 新增 integration-test-phase.md
  - 集成测试与部署验证阶段(P7)规划
  - 6 个批次任务分解
  - 8 条核心 E2E 链路定义
  - 验收标准和退出标准

Coord-AI
2026-07-10 22:05:37 +08:00

170 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.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.md §24.3
### 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.md §24.5
### 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.md §24.4
### 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.md §24.6
### 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.md §24.7
### 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.md §24.8
### 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.md §24.9
### 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.md §24.10
---
## §2 待 coord 仲裁事项汇总
| ISSUE | 主题 | 阻塞任务 | 状态 |
| --------- | ----------------------------------- | -------- | ------------------------------------------ |
| ISSUE-001 | matrix.md gRPC 矩阵修正 | — | ✅ 已裁决coord.md §24.3 |
| ISSUE-002 | shared-go 接入方式 | P2.1 | ✅ 已裁决coord.md §24.5 |
| ISSUE-003 | API 版本化路由方案(/v1 前缀) | P2.7 | ✅ 已裁决coord.md §24.4,方案 A 双 /v1 |
| ISSUE-004 | zap vs slog 选型 | P2.1 | ✅ 已裁决coord.md §24.6zap |
| ISSUE-005 | go.mod 与 Dockerfile 版本不一致 | P2.0 | ✅ 已裁决coord.md §24.7go 1.22 |
| ISSUE-007 | 01-understanding.md §6 审计表过时 | P2.0 | ✅ 已裁决coord.md §24.8 |
| ISSUE-008 | 01-understanding.md §7.1 死代码过时 | P2.0 | ✅ 已裁决coord.md §24.9 |
| ISSUE-009 | W1/W2 错误码 GW_ + ActionState | P2.3 | ✅ 已裁决coord.md §24.10 |
> ISSUE-006 编号未使用(预留)。
> 仲裁详情见 [coord.md §24 ARB-022](../coord.md)