Add value-scan and value-dig skills for reverse-engineering value points

- value-scan: read-only breadth inventory of mechanisms in delivered code,

  stopping at the human selection gate

- value-dig: depth write-up of chosen points (feature list, design review,

  refactor plan) with templates and mechanical checkers

- Add skill-workbench design doc for the pair
This commit is contained in:
zhuyongxin
2026-09-18 18:23:23 +08:00
parent 24a71e78f1
commit fddefab0c9
10 changed files with 1748 additions and 0 deletions
@@ -0,0 +1,233 @@
# ⟨主题⟩ · 改造方案
> **背景**:现状问题登记在本文第 0 节(**缺陷唯一归属地**);复盘视角的"承接不住"见《⟨主题⟩-设计思路与取舍.md》第 5 节
> **目标**:⟨把「X」从**一条路的设计**,变成**覆盖全部写接口的事实**⟩
> **边界**:⟨不改省侧协议、不引入分布式事务(改造点全在本服务内);DDL 只列关键字段,完整建表脚本另出⟩
> **与其它方案的关系**:⟨一致性异常台账**复用**《⟨其它⟩-改造方案.md》的 `⟨表名⟩`,**不重复建表**,只新增 `⟨列/取值⟩`⟩
> **依据**:全部改造点均**对应代码中已核实的缺陷,非推测**
> **取材**:深挖链路时发现的结构性缺口,按 `value-dig` SKILL 的「机制模式库」(结构性限制 → 可引入机制)比对出改造方向;每个缺陷追问"能否从补漏上升到加机制/重构"
> **代码讲解要求(必读)**:本文交付标准是"**照着能讲代码**"——① §1 先给**改造前链路骨架**(整体调用关系,用伪代码写清"谁调谁、每一步做了什么",可标类名/方法名,**不写文件路径与行号**);② **每个改造点**必带「**关键片段**」(伪代码 / SQL,只示形状、不贴源码)。密度基准:`docs/order-改造方案.md`
> **产出门槛**:缺口中至少存在一个**②加机制 / ③重构**档的改造点(更好设计、重构、引入中间件级)才立本文档;全是①档补漏(加缓存/加判断/加隔离/改配置)的**不产出**——在设计复盘"重做改什么"小节记录即可
---
## 0. 现状问题登记(缺陷唯一归属地)
> 功能点清单与设计复盘**不承载缺陷表**;深挖中发现的问题全部登记在此,后文逐条消化。
**严重度**:🔴 资金/数据错误 · 🟠 静默失败或重复调用 · 🟡 卫生与可维护性
| # | 缺陷 | 证据 | 后果 | 严重度 |
|:-:|---|---|---|:-:|
| 1 | ⟨缺陷⟩ | ⟨可核对的事实:行为 / 字段 / 日志⟩ | ⟨后果⟩ | 🔴 |
| 2 | ⟨…⟩ | | | 🟠 |
| 3 | ⟨…⟩ | | | 🟡 |
**归纳**:这些不是孤立的 bug,而是 **⟨N⟩ 处结构性缺口**:
1. **⟨缺口一⟩** —— ⟨…⟩;
2. **⟨缺口二⟩** —— ⟨…⟩;
3. **⟨缺口三⟩** —— ⟨…⟩。
---
## 1. 总体思路
**改造前链路骨架**(先定位"改的是链路的哪一段"——用伪代码写清谁调谁、每一步做了什么):
```java
⟨入口类#方法⟩()
→ ⟨A类#方法⟩() // 这一步做了什么(对应缺陷 #1)
→ ⟨B类#方法⟩()
→ ⟨C类#方法⟩() // 对应缺陷 #3
```
所以改造是做 N 件事,**而不是调参数**:
1. **⟨把一个点的分级 → 铺成一张网⟩**
2. **⟨把「不确定」从日志提升为一等状态⟩**
3. **⟨把状态推进的时机对齐到权威事实⟩**
```mermaid
flowchart TB
subgraph BEFORE["改造前"]
B1["⟨…⟩"] --> B2["⟨…⟩"]
B3["⟨…⟩"] --> B4["⟨…⟩"]
end
subgraph AFTER["改造后"]
A1["⟨…⟩"] --> A2["⟨…⟩"]
A2 --> A3["⟨…⟩"]
A4["⟨…⟩"] --> A5["⟨…⟩"]
end
```
---
## 2. 阶段一 · ⟨阶段主题:先说止血/可见⟩
> 成本最低、收益最大,**优先做**。⟨N⟩ 处改动都只动本服务内部。
### 2.1 ⟨改造点名⟩(P0,最重要)
- **现状**:⟨…⟩(对应第 0 节缺陷 #⟨N⟩)。
- **后果(真金白银)**:⟨…⟩ 这**违反了 ⟨不变量一⟩**。
- **改造**:⟨…⟩
**关键片段 · ⟨片段名⟩**(伪代码 / SQL,只示形状)
```sql
⟨改造后的语句或结构;与改造前的旧写法形成对照⟩
```
| ⟨对象⟩ | ⟨写/读⟩ | 可逆性 | 处置 |
|---|---|---|---|
| ⟨接口/动作⟩ | 写 | ⟨拒绝可逆、超时不可逆⟩ | ⟨…⟩ |
- **注意**:⟨切换后**必须同步补"超时不可逆"的判断**,否则只是把"归档超时 → 退款"换成"归档超时 → 抛异常 → 同样退款",问题原地不动⟩。
```mermaid
flowchart TD
A["⟨入口⟩"] --> B{"⟨判断⟩"}
B -->|"改造后"| C{"分类"}
B -->|"现状"| Z["⟨旧行为⟩"]
C -->|"⟨可逆⟩"| D["⟨处置⟩"]
C -->|"⟨不确定⟩"| E["⟨处置⟩"]
```
### 2.⟨N⟩ ⟨改造点名⟩
- **现状**:⟨…⟩(对应第 0 节缺陷 #⟨N⟩)
- **改造**:⟨…⟩ **原则:⟨异常类型就是后果分类的载体,不能在中间被抹平⟩。**
- **验收**:⟨人为造一次 ⟨场景⟩,应出现 ⟨可观测结果⟩⟩。
**关键片段 · ⟨片段名⟩**
```java
⟨改造后的分支形状,只示形状⟩
```
### 2.⟨N⟩ 阶段一验收标准
| 指标 | 目标 |
|---|---|
| ⟨…⟩ | ⟨100% 不产生 ⟨错误动作⟩,转 ⟨正确处置⟩⟩ |
| ⟨人为造 X⟩ | ⟨出现可观测的 ⟨证据⟩⟩ |
---
## 3. 阶段二 · ⟨阶段主题:再说可靠/对称/一等状态⟩
> 阶段一保证"不再做错",阶段二保证"⟨挂起能被收走⟩"。
### 3.1 ⟨幂等键改造⟩
- **现状**:⟨没有幂等键 → 可被重复执行;MQ `maxRetry=3` 在异常路径上会重复调用外部⟩(对应缺陷 #⟨N⟩)
- **改造**:⟨唯一索引 · 冲突当幂等命中 · 语义:"同一 X + 同一动作 = 只允许一次对外调用"⟩
- **迁移**:⟨先查存量重复,治理后再加索引⟩
**关键片段 · 索引 + 冲突即命中**
```sql
ALTER TABLE ⟨表名⟩ ADD UNIQUE KEY ⟨uk名⟩ (⟨列⟩, ⟨列⟩);
```
```java
try { ⟨对外调用⟩; }
catch (DuplicateKeyException e) { return; } // 已执行过 → 直接返回,不再调外部
```
### 3.2 ⟨挂起态 + 巡检⟩
- **现状**:⟨…⟩(对应缺陷 #⟨N⟩)
- **改造**:
1. **⟨新增挂起态⟩**,与"失败""成功"并列,**不占用失败的语义**;
2. **巡检任务**(⟨XXL-Job⟩)按"距离挂起时长"分级:⟨<1h 自动重放一次 / 1–24h 告警 / >24h 人工队列⟩;
3. 重放走**同一套分级链路**(不新开代码路径)。
**关键片段 · ⟨片段名⟩**
```java
⟨挂起态写入 / 巡检取单的形状⟩
```
```mermaid
flowchart TD
A["⟨不确定⟩"] --> B["⟨挂起 + 台账⟩"]
B --> C["告警"]
B --> D["巡检任务"]
D --> E{"挂起时长"}
E -->|"⟨短⟩"| F["自动重放"]
E -->|"⟨中⟩"| G["升级告警"]
E -->|"⟨长⟩"| H["人工队列"]
```
**关键设计:重放不是"再调一次接口",而是"重新走一遍分级"。** 因为对方的状态可能已经变了,重放必须能得出"成功"这个结论。
### 3.⟨N⟩ ⟨改造点名⟩
| `⟨anomaly_type⟩` 取值 | 触发场景 | 处置方向 |
|---|---|---|
| `⟨…⟩` | | |
- **收益**:⟨两侧的失败**进同一张台账、走同一套告警和处理台**——运维只需要看一个地方。**这是"复用"而不是"新建"的价值。**⟩
### 3.⟨N⟩ 阶段二验收标准
| 指标 | 目标 |
|---|---|
| ⟨重复调用外部⟩ | 0 次 |
| ⟨挂起单⟩ | 100% 台账可见 + 100% 被巡检捞到 |
---
## 4. 阶段三 · ⟨阶段主题:最后收口卫生问题⟩
| # | 改什么 | 现状证据(缺陷 #⟨N⟩) | 成本 |
|:-:|---|---|---|
| 1 | ⟨…⟩ | ⟨可核对的事实⟩ | 低 |
| 2 | ⟨…⟩ | | 低 |
**⟨某条不是文档工作,是防错工作⟩**:⟨文档漂移会让后来人**按错误的图改代码**,成本远高于改文档。⟩
---
## 5. 实施顺序与成本收益
| 阶段 | 内容 | 成本 | 解决的根问题 | 关键收益 |
|:-:|---|---|---|---|
| 一 | ⟨…⟩ | 低 | ⟨…⟩ | **不再出错** |
| 二 | ⟨…⟩ | 中 | ⟨…⟩ | **不确定可运营** |
| 三 | ⟨…⟩ | 低 | 卫生与可维护性 | 可观测与可维护 |
**顺序逻辑**:先修**会花错钱**的(阶段一),再修**会重复调用外部系统 / 卡住看不见**的(阶段二),最后才是卫生(阶段三)。
---
## 6. 迁移与灰度
1. **⟨逐项切⟩**:先切**风险最高、问题最实**的 ⟨X⟩,观察一周无异常再切 ⟨Y⟩;每次切换**只改一个调用点**,便于回滚。
2. **⟨新增状态先"只记录不流转"⟩**:先写台账 + 告警**观察一周**,确认没有误报,再打开自动重放。
3. **⟨索引先查存量⟩**:先排查存量重复;加索引后**观测冲突命中次数**——这个数就是"原设计漏掉的重复调用次数"。
4. **回滚**:全部改造以"**新增状态 + 新增列 + 开关**"方式落地,回滚只需关开关/停调度,**不动存量数据**。
---
## 7. 改造后:⟨N⟩ 条不变量由谁保证
| 不变量 | 改造前 | 改造后 |
|---|---|---|
| **⟨不变量一⟩** | ⟨只有一条路成立;某路径会被错退⟩ | ⟨全部写类接口统一分类⟩ |
| **⟨不变量二⟩** | ⟨只有一行日志,库里无痕⟩ | ⟨挂起态 + 台账 + 巡检 + 重放⟩ |
| **⟨不变量三⟩** | ⟨实际只有两态(超时被降级抹平)⟩ | ⟨异常类型不被中间层改写⟩ |
---
## 附:与《⟨其它⟩-改造方案.md》的关系
| 项 | ⟨其它侧⟩ | ⟨本文⟩ |
|---|---|---|
| ⟨一致性异常台账⟩ | **建** `⟨表名⟩` | **复用**,只加取值 |
| ⟨Outbox⟩ | **建** `⟨表名⟩` | **复用同表**,`⟨biz_type⟩` 区分 |
| ⟨幂等⟩ | ⟨…⟩ | ⟨…⟩ |
**两边的根因是同一个**:⟨设计了机制,但**没有为"需要人介入"这个信号建自动出口**⟩。所以两套改造共用一套台账与告警,是**结构上的必然,而不是为了省事**。