docs: summarize remediation lessons and refresh project standards
This commit is contained in:
253
docs/project_experience_summary_v3_2026-04-17.md
Normal file
253
docs/project_experience_summary_v3_2026-04-17.md
Normal file
@@ -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 上线条件验证完成后
|
||||
**维护责任人**:项目架构与交付收口负责人
|
||||
@@ -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
|
||||
# 使用配置文件
|
||||
|
||||
Reference in New Issue
Block a user