diff --git a/docs/project_experience_summary_v3_2026-04-17.md b/docs/project_experience_summary_v3_2026-04-17.md new file mode 100644 index 00000000..fde345f0 --- /dev/null +++ b/docs/project_experience_summary_v3_2026-04-17.md @@ -0,0 +1,253 @@ +# 立交桥项目经验总结 + +> 文档日期:2026-04-17 +> 项目阶段:纠偏整改收口 +> 文档类型:经验总结与规范固化 +> 版本:v3 + +--- + +## 一、这次收口解决了什么 + +2026-04-17 这轮工作,不是普通的“补几个 bug”,而是一次完整的纠偏与收口: + +1. 对 2026-04-16 旧审查报告做了事实核验,确认哪些问题仍然成立,哪些已经过时,哪些被旧报告漏掉。 +2. 按纠偏结果执行了 13 个整改任务,逐项修复代码、补齐测试、恢复验证链。 +3. 对三块核心服务重新建立了统一的、可信的验证基线。 +4. 把“已修复 / 上线条件 / 长期治理项”正式拆开,避免继续把不同性质的问题混成同一个清单。 +5. 基于当前真实代码,重新整理了已完成功能清单、页面流程树和按钮矩阵,替代“从旧 PRD 反推现状”的做法。 + +最终结果不是“文档更漂亮了”,而是仓库恢复到了**可验证、可回归、可继续交付**的状态。 + +--- + +## 二、本轮最重要的经验 + +### 2.1 旧审查报告不能直接等于当前真实状态 + +这次最明显的教训是:**报告会过时,代码不会说谎**。 + +2026-04-16 的几份审查报告里,既有真实问题,也有已经被代码修复但文档没更新的旧结论。例如: + +1. `platform-token-runtime` 里“Refresh TTL 不持久化”在当前代码中已经修复。 +2. `platform-token-runtime` 里“audit-events 无鉴权”在当前代码中已经不成立。 +3. `gateway` 里“请求 ID 信任用户输入”“内部错误细节泄露”这类问题,也已经被代码和测试纠正。 + +如果继续把旧报告当成当前事实,会直接导致: + +1. 优先级失真 +2. 修复顺序失真 +3. 完成度判断失真 + +因此,本项目后续所有审查结论都必须先经过“当前代码复核”,再谈整改。 + +### 2.2 无缓存测试才是可信基线 + +这次另一个核心经验是:**缓存绿,不等于真实绿**。 + +在整改启动时,三个核心服务的真实问题不是“看起来测试很多”,而是: + +1. `gateway` 测试已和实现失配 +2. `platform-token-runtime` 测试已和实现失配 +3. `supply-api` 在当前工具链下甚至有编译阻塞 + +这些问题之所以能被快速暴露,是因为使用了无缓存验证: + +```bash +cd gateway && go test -count=1 ./... +cd platform-token-runtime && go test -count=1 ./... +cd supply-api && go test -count=1 ./... +cd supply-api && bash scripts/run_integration_tests.sh ./internal/repository +bash scripts/ci/repo_integrity_check.sh +``` + +后面又进一步发现,连集成测试脚本内部也必须强制 `-count=1`,否则仓储集成照样可能被缓存污染。 + +结论很明确: + +1. 模块级验证必须默认无缓存。 +2. 仓库级统一校验脚本必须和人工校验口径一致。 +3. 任何“测试全绿”的结论,都要说明是不是无缓存跑出来的。 + +### 2.3 主启动链路才代表“功能已交付” + +这轮整改中,有不少代码看起来“已经写了”,但实际上不能算交付完成。 + +典型例子: + +1. `supply-api` 的 IAM 代码早就存在,但在整改前并没有挂载到主服务。 +2. `gateway` 的 `cost_based`、`cost_aware`、`fallback` 模块也存在,但没有接入 `BuildServer` 主启动链路。 +3. 旧竞品目录里有很多前端页面,但它们根本不是本项目的一方交付物。 + +这次由此沉淀出一个非常重要的判断原则: + +**能力是否完成,不看“仓库里有没有文件”,要看“主启动链路有没有接入”。** + +对后续工作来说,这意味着: + +1. `handler/service` 已实现但未挂载,不算完成。 +2. 实验模块未进入主装配,不算完成。 +3. 竞品样例、归档代码、历史方案,不算完成。 + +### 2.4 条件性能力必须显式 fail-closed + +这次整改前,有几类能力最危险的点不是“没实现”,而是“看起来像实现了,但实际上不能安全启用”。 + +典型例子: + +1. 提现能力依赖 SMS verifier,如果短信链路没接好,就不能放开。 +2. IAM 需要 DB-backed runtime,如果运行时还不是数据库实现,就不能对外暴露路由。 +3. 生产环境如果没有显式密钥和 CORS 白名单,就不能靠默认值启动。 +4. 补偿执行器如果没有真实回滚实现,就不能“记日志然后假成功”。 + +这轮整改的经验是:**功能不满足上线条件时,必须显式关闭并返回明确原因,而不是隐式降级成不可靠行为。** + +因此,后续项目里凡是依赖外部系统、配置门禁或持久化前提的能力,都应该: + +1. 默认关闭 +2. 条件满足后再显式打开 +3. 关闭时返回清晰错误 +4. 在文档里写明启用条件 + +### 2.5 “规范问题”和“阻塞问题”必须分开管理 + +这次纠偏明确暴露出一个管理问题:旧报告把很多“规范一致性问题”和“生产 readiness 阻塞问题”混写了。 + +比如: + +1. `ClientIP` / `SourceIP` 的残留命名差异,确实需要治理,但不该和编译失败、测试失配、持久化缺失放在同一个优先级层面。 +2. Python、framework、architecture 这些报告里有些观点有治理价值,但不能直接替代当前上线判断。 + +这次之后,项目里应至少使用三个分类: + +1. `阻塞缺陷` +2. `上线条件` +3. `长期治理项` + +只有这样,清单才不会不断膨胀成“什么都重要,结果什么都不清楚”。 + +### 2.6 功能盘点必须只看一方真实交付物 + +这次在做功能清单和页面流程树时,最重要的经验是:**不要把竞品目录、旧 PRD 或设计稿误算成当前交付状态**。 + +当前仓库里: + +1. 一方前端控制台源码并不存在。 +2. `llm-gateway-competitors/` 下面虽然有大量 Vue 页面,但它们只是竞品归档。 +3. 供应侧按钮级 PRD 可以参考版式,但不能直接当完成清单。 + +因此,后续只要做“功能完成度盘点”,都必须先问三个问题: + +1. 这是当前主链路能力,还是历史设计文档? +2. 这是本项目一方交付物,还是竞品/归档代码? +3. 这是默认可用能力,还是条件启用能力? + +--- + +## 三、这次形成的新工作方法 + +### 3.1 审查纠偏工作流 + +本轮证明有效的工作流是: + +1. 先把旧报告按 `已证实 / 已过时 / 漏报` 三类重写 +2. 再按 `P0 / P1 / P2` 重排真实优先级 +3. 然后输出整改执行清单 +4. 每个任务独立修复、独立验证、独立提交 +5. 最后再产出“整改完成确认单”,把 `已修复 / 上线条件 / 长期治理项` 拆开 + +这个工作流比“对着旧报告逐条打勾”更可靠,因为它强制先纠偏,再执行。 + +### 3.2 文档收口工作流 + +这次最终沉淀出的收口顺序也很清晰: + +1. `REPORT_CORRECTION_2026-04-17.md` +2. `2026-04-17-remediation-execution-checklist.md` +3. `REMEDIATION_COMPLETION_CONFIRMATION_2026-04-17.md` +4. `completed_feature_inventory_v1_2026-04-17.md` +5. `page_flow_tree_and_button_matrix_v1_2026-04-17.md` + +也就是说,后续项目文档收口不应该只停留在“问题报告”,而要至少闭环到: + +1. 纠偏 +2. 执行 +3. 验证 +4. 功能现状 +5. 页面与流程决策材料 + +### 3.3 提交策略 + +本轮整改采用了“任务粒度提交”,事实证明这是对的。 + +优点: + +1. 每个任务都有明确责任边界 +2. 每个提交都对应独立验证结果 +3. 回滚和复盘都更容易 +4. 文档和代码不会被混成一坨不可追踪的变更 + +因此,后续类似整改类工作应继续保持: + +1. 一项任务一类提交 +2. 验证先于结论 +3. 文档收口单独提交 + +--- + +## 四、需要固化为规范的新规则 + +### 4.1 验证规范 + +1. Go 模块级回归默认使用 `go test -count=1 ./...` +2. 仓储集成默认通过脚本执行,且脚本内部也必须禁用缓存 +3. 仓库级统一校验以 `scripts/ci/repo_integrity_check.sh` 为准 +4. 任何“全部通过”结论,必须说明使用的验证命令 + +### 4.2 完成度判断规范 + +1. 主启动链路是否挂载,才是“功能已完成”的判断依据 +2. 仅有实现文件但未接入运行时,不算完成 +3. 实验模块不计入默认交付范围 +4. 条件启用能力必须和默认能力分开盘点 + +### 4.3 能力门禁规范 + +1. 外部依赖未就绪时,能力必须 fail-closed +2. 默认值只能用于开发态,不能偷偷撑起生产态 +3. 条件能力必须在代码、配置和文档三处同时写清启用条件 +4. 禁止“只打日志后返回成功”的假闭环实现 + +### 4.4 审查结论规范 + +1. 报告必须区分 `已证实 / 已过时 / 漏报` +2. 结论必须附实际代码位置或验证命令 +3. 规范治理项不能替代生产 readiness 判断 +4. 竞品目录、归档目录、历史设计稿不得直接纳入当前完成度结论 + +--- + +## 五、对后续阶段的直接建议 + +1. 如果继续推进前端控制台,应直接以 `completed_feature_inventory` 和 `page_flow_tree_and_button_matrix` 为基线,而不是回到旧 PRD 重新猜范围。 +2. 如果继续做质量治理,下一轮要优先补“长期治理项”的独立 backlog,不要再和阻塞性问题混管。 +3. 如果继续做 staging / prod 准备,要按“上线条件清单”推进真实环境打通,不要把它重新包装成“代码 bug”。 +4. 如果继续做审查,必须先纠偏旧结论,再写新的系统报告。 + +--- + +## 六、关联文档 + +| 文档 | 用途 | +|------|------| +| `review/REPORT_CORRECTION_2026-04-17.md` | 审查纠偏 | +| `docs/plans/2026-04-17-remediation-execution-checklist.md` | 整改执行清单 | +| `review/REMEDIATION_COMPLETION_CONFIRMATION_2026-04-17.md` | 整改完成确认 | +| `docs/product/completed_feature_inventory_v1_2026-04-17.md` | 已完成功能清单 | +| `docs/product/page_flow_tree_and_button_matrix_v1_2026-04-17.md` | 页面流程树与按钮矩阵 | + +--- + +**文档状态**:v3 - 基于 2026-04-17 纠偏整改与验证收口更新 +**下次更新建议**:staging / prod 上线条件验证完成后 +**维护责任人**:项目架构与交付收口负责人 diff --git a/supply-api/CLAUDE.md b/supply-api/CLAUDE.md index fd1cebf0..c6ad930c 100644 --- a/supply-api/CLAUDE.md +++ b/supply-api/CLAUDE.md @@ -6,7 +6,7 @@ Supply API 是一个基于 Go 的微服务,提供供应链管理功能,包 ## 技术栈 -- **语言**: Go 1.21+ +- **语言**: Go 1.22+ - **数据库**: PostgreSQL 15+ - **缓存**: Redis - **框架**: 标准库 + 自定义中间件 @@ -23,19 +23,24 @@ Supply API 是一个基于 Go 的微服务,提供供应链管理功能,包 | 规范 | 示例 | 说明 | |------|------|------| -| IP来源字段 | `SourceIP` | 统一使用 `SourceIP`,禁止使用 `ClientIP` | +| IP来源字段 | `SourceIP` | 审计、持久化、对外 JSON 字段统一使用 `SourceIP`;HTTP 入口局部上下文允许保留 `ClientIP` | | 追踪ID字段 | `TraceID` | W3C Trace Context 标准 | | 请求ID字段 | `RequestID` | HTTP 请求追踪 | | 幂等键字段 | `IdempotencyKey` | 统一命名 | #### 1.2 结构体命名 ``` -// ✅ 正确 +// ✅ 正确 - 审计/持久化模型统一使用 SourceIP type AuditEvent struct { SourceIP string `json:"source_ip"` } -// ❌ 错误 - 与其他模块不一致 +// ⚠️ 仅限 HTTP 入口局部上下文,可使用 ClientIP +type RequestContext struct { + ClientIP string +} + +// ❌ 错误 - 对外/持久化结构继续使用 ClientIP type AuditEvent struct { ClientIP string `json:"client_ip"` } @@ -166,6 +171,30 @@ return errors.New("SUP_SET_4001: withdraw amount exceeds available balance") return errors.New("database connection failed: connection refused") ``` +#### 6.3 条件能力必须 fail-closed +**关键经验**: 依赖外部集成或运行时前提的能力,未满足条件时必须显式关闭,不能“假成功”或静默降级。 + +```go +// ✅ 正确 - 条件不满足时显式关闭 +if !cfg.SMS.IsReadyForWithdraw() { + writeError(w, http.StatusServiceUnavailable, CodeFeatureDisabled, "withdraw is disabled because SMS is not ready") + return +} + +// ❌ 错误 - 只打日志,不阻断功能 +logger.Warn("sms verifier is not wired", nil) +return nil +``` + +#### 6.4 运行时主链路才算已交付 +**关键经验**: 只有接入 `BuildServer` / `BuildRuntime` 主启动链路的能力,才能算“当前已完成”。 + +判定规则: + +1. 只有 handler / service 实现、但未挂载到运行时,不算已交付。 +2. 实验模块未接入主链路,不算默认交付范围。 +3. 条件启用能力必须和默认能力分开描述,不能混写为“已完成”。 + ### 7. 数据库设计 #### 7.1 乐观锁实现 @@ -333,6 +362,41 @@ database: password: ${DB_PASSWORD} ``` +### 3. 条件能力默认关闭 + +必须遵循以下原则: + +1. `server.iam_enabled` 默认 `false` +2. `settlement.withdraw_enabled` 默认 `false` +3. 生产配置下,如果 `settlement.withdraw_enabled=true` 但 SMS 未 ready,配置加载必须失败 +4. 任何依赖 DB-backed runtime 的能力,在数据库依赖缺失时必须直接拒绝启动或拒绝挂载 + +--- + +## 运行时与完成度规范(2026-04-17) + +### 1. 完成度判断 + +1. 路由是否在 `BuildServer` 中注册,是 HTTP 能力是否已交付的唯一准绳。 +2. 运行时是否在 `BuildRuntime` 中实际装配,是依赖能力是否可用的唯一准绳。 +3. 设计文档、竞品代码、历史归档、实验模块都不能直接算入当前完成度。 + +### 2. 报告与审查结论分类 + +审查结论至少分三类: + +1. `已证实`:当前代码和命令能直接证明 +2. `已过时`:曾经成立,但当前代码已不成立 +3. `漏报`:旧报告没有识别,但当前真实存在 + +同时必须明确区分: + +1. `阻塞缺陷` +2. `上线条件` +3. `长期治理项` + +禁止把规范一致性问题直接等同于生产 readiness 阻塞项。 + --- ## 常见问题与解决方案 @@ -370,25 +434,35 @@ export SUPPLY_API_DB_PORT="5432" ### 2. 测试运行命令 ```bash -# 单元测试(跳过集成测试) -go test -short ./... +# 模块级回归(默认无缓存) +go test -count=1 ./... -# 集成测试(需真实数据库) -go test -tags=integration ./... +# 快速单元测试(显式 short) +go test -count=1 -short ./... + +# 仓储集成测试(需真实数据库,默认使用脚本) +bash scripts/run_integration_tests.sh ./internal/repository # 性能基准测试 go test -tags=slow -bench=. -benchmem ./internal/benchmark/... -# 完整测试(含集成) -go test -tags=integration,benchmark ./... - # E2E 测试 -go test -tags=e2e ./e2e/... +go test -count=1 -tags=e2e ./e2e/... + +# 仓库级统一校验 +bash ../scripts/ci/repo_integrity_check.sh # 覆盖率报告 -go test -cover ./... +go test -count=1 -cover ./... ``` +### 2.1 验证口径要求 + +1. 任何“测试通过”默认指 `-count=1` 的无缓存结果。 +2. `scripts/run_integration_tests.sh` 内部必须保持无缓存执行。 +3. 修改 HTTP、runtime、配置门禁、持久化装配后,必须补跑仓库级统一校验。 +4. 没有实际运行命令时,不得声称“已验证”。 + ### 3. 服务启动 ```bash # 使用配置文件