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:
@@ -0,0 +1,115 @@
|
||||
---
|
||||
name: value-dig
|
||||
description: Use when the user has already chosen which mechanisms or value points matter and now wants them written up in depth — "把这个点写成文档", "把这条链路的实现整理成功能点", "写设计思路与取舍", "出改造方案", or when an existing module's design must be reconstructed layer by layer for an interview walkthrough. Read-only — never modifies the scanned project.
|
||||
---
|
||||
|
||||
# value-dig · 深度产出(功能点 / 设计复盘 / 改造方案)
|
||||
|
||||
## 一句话
|
||||
|
||||
把**已经选定的**机制点写成**能讲、能追问、能被审**的文档:按需产出三件套。
|
||||
|
||||
**两条原则**
|
||||
|
||||
1. **价值优先 → 深入 → 更优方案。** 缺陷与改造只在「改造方案」里出现;功能点清单与设计复盘以"这个设计为什么好、怎么取舍"为主体。
|
||||
2. **只读**:不修改被扫描项目的任何文件(新增产物不算)。
|
||||
|
||||
## 两种入口
|
||||
|
||||
| 入口 | 情形 | 怎么做 |
|
||||
|---|---|---|
|
||||
| **A · 有候选清单** | 上游 `value-scan` 已产出且人已勾选 | 直接开工;勾选结果回写候选清单「闸门状态」节(`✅ 已勾选:★N…⟨谁/日期⟩`) |
|
||||
| **A′ · 候选被否决改向** | 人看了清单说"都不够硬,直接分析整条链路"——否决候选但给出新目标 | 三步:①原清单「闸门状态」记**否决原因原文摘要**(这是筛选口径的校准数据);②按 B 的最小枚举对准新目标重走;③产物头部声明"入口 A′(否决改向)" |
|
||||
| **B · 直接点名** | 用户直接说"把 XX 链路写成文档",无候选清单 | 只针对该链路按 `value-scan` 的判据做一次**最小枚举**(按触发者拆链路 + 三条硬标准),在产物头部声明"入口 B" |
|
||||
|
||||
> **入口 B 不能省粗筛**:跳过判据会把"框架常识/纯业务实现"写成深度文档。
|
||||
|
||||
## 三份文档(各自独立,可只做其一)
|
||||
|
||||
> 若用户只要"每个点先给个轻理由看看"再决定深挖谁,可用 `assets/价值点报告模板.md` 快速产出一份轻量报告,不必直接进三件套。
|
||||
|
||||
| 文档 | 模板 | 内容 | 关键 |
|
||||
|---|---|---|---|
|
||||
| ① **功能点清单** | `assets/深度模板/功能点清单模板.md` | 链路总览 · **主干调用骨架** · 各功能点 · 推荐组合 · 附录速查 | **只记功能点,不列缺陷** |
|
||||
| ② **设计思路与取舍** | `assets/深度模板/设计思路与取舍模板.md` | 第一人称复盘:问题定义 → 不变量 → 怎么拆 → 设计主线 → 逐环节决策表 → 做对的/承接不住的 → 重做改什么 → 面试口述版 | 讲**取舍与代价**,不只讲做法 |
|
||||
| ③ **改造方案** | `assets/深度模板/改造方案模板.md` | 缺陷(含严重度)→ **改造前链路骨架**(整体调用关系,伪代码)→ 总体思路 → 分阶段(每点含**关键片段**(伪代码/SQL)+ 验收)→ 迁移灰度 → 不变量由谁保证 | **缺陷的唯一归属地**;复用既有资产,不重复建表。**代码讲解要求:交付标准是"照着能讲代码"**——§1 必有改造前链路骨架(写清谁调谁、每步做了什么,可标类名/方法名,**不写路径与行号**)、每个改造点必有「关键片段」(伪代码/SQL,只示形状);只有观点没有片段即不达标。**产出门槛:深挖浮现的缺口中至少存在一个②加机制/③重构档的改造点才立方案;全是①档补漏的(加缓存/加判断/加隔离/改配置类),在设计复盘"重做改什么"里带一句即可,不单独成文——硬写就是灌水** |
|
||||
|
||||
### ① 功能点清单的必备件:主干调用骨架
|
||||
|
||||
把整条链路的 N 段主干压成**一段连续伪代码**,每行右侧标注方式(`[锁]` `[幂等]` `[策略]` `[MQ]` `[外调]` `[落库]` `[兜底]` `[履历]` `[通知]` `[隔离]` `[验签]`);附方式图例(反向索引)。**每个功能点必须能在骨架上找到**(互为索引)。
|
||||
|
||||
### ③ 改造方案的取材工具(深入链路时用)
|
||||
|
||||
缺陷**不是枚举出来的,是贴着链路走一遍时浮现的**。深挖时用这三把刀看结构,发现的缺口按「机制模式库」比对出改造方向:
|
||||
|
||||
| 刀 | 怎么做 |
|
||||
|---|---|
|
||||
| **按触发者拆链路** | 找「同一行数据被几个触发者改」→ 判断收敛/防重是否单点 |
|
||||
| **竞态矩阵** | 「并发场景 × 后果」穷举表:重复回调 / 回调与兜底并发 / 并发退款… |
|
||||
| **幂等键清单 + 状态流转图** | 列出链路上所有幂等键并标层级;画状态机——"重复入账"与"状态与事实不符"的发现入口 |
|
||||
|
||||
**机制模式库**("结构性限制 → 可引入机制"对照表,改造方案的取材清单;**只有这一份,不复制**):
|
||||
|
||||
| 结构性限制(发现) | 可引入的机制 / 重构 |
|
||||
|---|---|
|
||||
| 业务写 + 发消息不原子 | 本地事务表 / Outbox + 投递器(业务与消息同事务,投递可重放) |
|
||||
| 兜底任务不可恢复(poll 即删) | 任务表为权威,队列只做加速(宕机可恢复、积压可观测) |
|
||||
| 不一致只有日志、无人知 | 一致性异常台账 + 告警 + 处理台(待处理 → 处理中 → 已处理) |
|
||||
| 状态靠应用层"猜测" | CAS 条件更新 + 影响行数判定(把判断交给数据库) |
|
||||
| 上游码散落多处、口径不一致 | 码字典化:每码标注「是否受理 / 是否终态 / 可重试」,多处共查一份 |
|
||||
| "不确定"没有归宿 | 挂起态 + 巡检 + 重放(重放须走同一套分级,因为对方状态可能已变) |
|
||||
| 对外调用有副作用却被重复调用 | 幂等键(业务唯一键)+ 冲突当命中 |
|
||||
| 资金出去的方向弱于进来 | 对称化:退款补齐同构的锁 / 短路 / 唯一索引 |
|
||||
| 重试责任多点(乘法放大) | 重试责任单点 + 显式关闭其他层(应用重试了,MQ 就不重试) |
|
||||
| 状态写入早于事实 | 中间态 + 以权威回调驱动终态(不以"发起成功"为准) |
|
||||
| 能力只覆盖一条路径 | 铺开:把一个点的分级铺成一张网(全部写类接口统一) |
|
||||
| 跨系统没有事务 | 「幂等 + 可重放 + 对账」三件套替代事务 |
|
||||
| 关键参数散落、取值无解释 | 参数登记与解释:汇总阈值 / 退避 / 分片并说明取值依据 |
|
||||
|
||||
### 改进的三档尺度
|
||||
|
||||
| 档 | 做法 | 简历硬度 |
|
||||
|:-:|---|:-:|
|
||||
| ① 补漏 | 修一个具体的错:加校验、加索引、补 try-catch | ⭐ |
|
||||
| ② 加机制 | 引入一个新机制,替代"靠人 / 靠自觉"的现状 | ⭐⭐⭐ |
|
||||
| ③ 重构 / 更合理设计 | 改结构本身:隐式判断变显式判定、口径收敛、方向对称化 | ⭐⭐⭐ |
|
||||
|
||||
**硬要求:每个缺陷都追问"能不能上升一档"**;改造方案里 ②③ 档应占多数。改进的固定 5 要素:① 结构性限制 ② 机制/方案 ③ 替代了什么 ④ 代价 ⑤ 为什么不用通用方案。
|
||||
|
||||
**缺陷严重度**:🔴 资金/数据错误 · 🟠 静默失败或重复调用 · 🟡 卫生与可维护性。
|
||||
**边界**:只做系统级结构问题(一致性·幂等·可运营性·可恢复性·职责漂移·口径分散),不做代码风格/坏味道/命名/注释。
|
||||
|
||||
## 硬规则(合并版)
|
||||
|
||||
| # | 规则 | 为什么 |
|
||||
|:-:|---|---|
|
||||
| 1 | **深度闸门**:允许简短的伪代码/关键片段(CAS 语句·锁与短路顺序·状态分支·DDL·Lua 脚本)把机制画实——**行数是参考不是硬限,忠实于机制形状优先**;真正禁止的是大段源码摘录、代码块内行号、逐层讲解。**豁免** = 主干调用骨架(技术地图,压成一行一段) | 禁代码会"太空",贴源码则成"阅读报告" |
|
||||
| 2 | **证据真实性**:锚点 `路径:行号`,**必须读过再写**;拿不准标【待确认】 | 错锚点毁掉可信度 |
|
||||
| 3 | **只读**:不改被扫项目任何文件(新增产物不算) | — |
|
||||
| 4 | **缺陷只进改造方案**:功能点清单与设计复盘不列缺陷表、不标严重度;设计复盘里"承接不住的地方"自然带出即可,不展开成清单 | 价值优先;缺陷集中一处才成体系 |
|
||||
| 5 | **改进不停在补漏档;不写通用套话**("加监控""上分布式事务");必须贴合业务的具体语义/表/状态,优先复用既有资产 | ②③ 档才是简历上能写硬的 |
|
||||
| 6 | **诚实性**:"设计对了但落地不完整"要明说;判断与代码事实不一致处逐条列出;文档写的方案必须回代码验是否落地 | 深度文档可信度的唯一来源 |
|
||||
| 7 | **只做被点单的 1–3 个点**;每点先想清楚归哪份文档,不为凑齐三件套而写 | 实测一个功能点 ≈ 1–2 份 20KB 文档 |
|
||||
|
||||
## 格式保证
|
||||
|
||||
1. **模板外置**:读 `assets/` 对应模板填空。
|
||||
2. **机械校验**(与 `value-scan` 共用 `../value-scan/assets/check.ps1`):
|
||||
|
||||
```powershell
|
||||
$ck = .agents/skills/value-scan/assets/check.py
|
||||
# ① 功能点清单
|
||||
python $ck dig <产物> --template .agents/skills/value-dig/assets/深度模板/功能点清单模板.md
|
||||
# ② 设计思路与取舍 / ③ 改造方案
|
||||
python $ck doc <产物> --template .agents/skills/value-dig/assets/深度模板/<对应模板>.md
|
||||
```
|
||||
|
||||
**FAIL 必须为 0**;WARN 允许保留但写明原因。
|
||||
3. **偏差回写**:被纠正过就回写模板。
|
||||
|
||||
## 触发词与落盘
|
||||
|
||||
**触发词**:「把这个点写成文档」「把这条链路整理成功能点」「写设计思路与取舍」「出改造方案」。
|
||||
**落盘**:`<项目根>/docs/`,与候选清单同目录(可被用户覆盖)。
|
||||
|
||||
**上游**:候选清单由 `value-scan` 产出。
|
||||
@@ -0,0 +1,76 @@
|
||||
# ⟨域⟩ · 价值点报告(S3 产物)
|
||||
|
||||
> **用途**:为 S2 勾选出的每个点写**可讲述的轻理由**(默认交付深度)。
|
||||
> **上游**:`⟨域⟩-候选价值点.md`(S1)· **勾选来源**:⟨候选清单「闸门状态」节 / 用户消息直接点名⟩
|
||||
> **证据快照**:基于 ⟨仓库名⟩ `⟨commit 短号 / 分支⟩`,⟨读取日期⟩。**换版本要重核**。
|
||||
> **判据**:**简历行测试**——填不出机制的退回「附录·待定」
|
||||
> **路径简写**:`⟨简写⟩/` = `⟨真实路径⟩`(锚点一律用简写前缀)
|
||||
|
||||
---
|
||||
|
||||
## 每个点 = 7 类内容
|
||||
|
||||
> **7 类不是"行数上限"**。关键片段与偏差附录**另有归属,不占这 7 类**。
|
||||
> **点编号沿用 S1 的 ★ 编号**(便于与候选清单对照)。
|
||||
|
||||
### ★⟨N⟩ · ⟨机制名⟩
|
||||
|
||||
| # | 行 | 内容 |
|
||||
|:-:|---|---|
|
||||
| 1 | **简历行** | 用【⟨机制⟩】解决了【⟨具体问题/场景⟩】,代价是【⟨取舍⟩】 |
|
||||
| 2 | **Problem** | ⟨不做会怎样 / 原来的做法会出什么问题⟩ |
|
||||
| 3 | **Pattern** | ⟨机制是什么、怎么起作用⟩ |
|
||||
| 4 | **Alternatives** | ⟨当时还能怎么做 / 为什么没选⟩ |
|
||||
| 5 | **Tradeoffs** | ⟨代价:新增复杂度 · 依赖 · 运维负担⟩ |
|
||||
| 6 | **Evidence** | `⟨简写⟩/⟨路径⟩.java:⟨行号⟩` · ⟨可核对的图/表/配置⟩ |
|
||||
| 7 | **追问预判** | **问**:⟨高概率追问 1⟩ **答**:⟨一句话⟩<br/>**问**:⟨高概率追问 2(可选,最多 2 条)⟩ **答**:⟨一句话⟩ |
|
||||
|
||||
**关键片段 · ⟨片段名⟩**(≤10 行伪代码,把"只有名词"的地方画实)
|
||||
|
||||
```
|
||||
⟨CAS 语句 / 锁与短路顺序 / 状态分支 / DDL⟩
|
||||
```
|
||||
|
||||
**事实强度**(可选,用于区分"已核实"与"推断")
|
||||
|
||||
| 结论 | 强度 |
|
||||
|---|---|
|
||||
| ⟨…⟩ | **已核实**(读过代码) |
|
||||
| ⟨…⟩ | **推断**(未核实,需人工确认) |
|
||||
|
||||
⟨重复上面 3 块,每点一节⟩
|
||||
|
||||
---
|
||||
|
||||
# 附录 · 与候选清单的偏差
|
||||
|
||||
> **实读代码后必须回头核对 S1 的表述**。S1 是"候选"不是"事实"——**偏差本身就是高价值材料**(往往是简历上最硬的一条)。
|
||||
|
||||
| # | S1 怎么写的 | 代码事实 | 处理 |
|
||||
|:-:|---|---|---|
|
||||
| 1 | ⟨S1 的原文⟩ | ⟨实读结果 + `路径:行号`⟩ | ⟨纠正 / 标【待确认】/ 升级为独立价值点⟩ |
|
||||
|
||||
---
|
||||
|
||||
# 报告尾 · 推荐组合
|
||||
|
||||
> **先讲哪 3 个**——给"追问密度最高 / 最能体现设计能力"的组合。**内容列必须是上面已写的 ★ 编号。**
|
||||
|
||||
| 组合 | 内容(★ 编号) | 适合 |
|
||||
|---|---|---|
|
||||
| **主线(推荐)** | ⟨★N + ★M⟩ | ⟨最能体现什么能力,追问密度最高⟩ |
|
||||
| **完整版** | ⟨★1 + ★2 + ★3 + ★4 + ★5⟩ | ⟨讲清完整链路⟩ |
|
||||
| **差异化** | ⟨★K⟩ | ⟨少数人会讲的点⟩ |
|
||||
|
||||
**建议讲述顺序**:⟨★N → ★M → ★K,并说明为什么这个顺序⟩
|
||||
|
||||
---
|
||||
|
||||
# 附录 · 待定
|
||||
|
||||
> 简历行**填不出机制**的点落在这里(**不删除**——防灌水,也不误杀)。
|
||||
> ⚠️ **闸门二只在本阶段生效**:进入 S4 的点若机制仍未定,改为在其小节头部标 **【待确认:机制未定】**,**不在 S4 新建「待定」节**。
|
||||
|
||||
| # | 点 | 为什么填不出 | 需要补什么才能定 |
|
||||
|:-:|---|---|---|
|
||||
| 1 | | | |
|
||||
@@ -0,0 +1,174 @@
|
||||
# ⟨域⟩ 域 · 功能点清单
|
||||
|
||||
> **用途**:把「⟨链路主题⟩」这条链路按其设计文档与代码实读整理成功能点
|
||||
> **流程**(与上游一致):**先记录功能点 → 定稿文档 → 再讨论最佳设计与取舍**
|
||||
> **上游**:`⟨域⟩-候选价值点.md`(S1)+ 勾选结果(S2)
|
||||
> **素材来源**:`⟨wiki/xxx-spec.md⟩`(⟨N⟩ 行)+ `⟨仓库名⟩` 代码实读
|
||||
> **路径简写**:`⟨简写1⟩/` = `⟨真实路径⟩`;`⟨简写2⟩/` = `⟨真实路径⟩`
|
||||
|
||||
---
|
||||
|
||||
# 一、链路总览
|
||||
|
||||
**一句话**:⟨统领句——给出这条链路的**主线判断**,不是复述流程⟩
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["⟨起点⟩"] --> B["⟨★ 机制节点⟩"]
|
||||
B --> C{"⟨分支⟩"}
|
||||
C -->|"⟨路径⟩"| D["⟨汇合点 / 分级发生地⟩"]
|
||||
C -->|"⟨路径⟩"| D
|
||||
D --> E{"⟨结果类型⟩"}
|
||||
E -->|"⟨可逆⟩"| F["⟨处置⟩"]
|
||||
E -->|"⟨不确定⟩"| G["⟨处置⟩"]
|
||||
```
|
||||
|
||||
⟨可选⟩**关键差异表(设计的核心)**
|
||||
|
||||
| ⟨维度⟩ | ⟨取值1⟩ | ⟨取值2⟩ | ⟨取值3⟩ |
|
||||
|---|---|---|---|
|
||||
| ⟨例:对方结果⟩ | | | |
|
||||
| ⟨例:可逆性⟩ | | | |
|
||||
| ⟨例:处置⟩ | | | |
|
||||
|
||||
---
|
||||
|
||||
## 主干调用骨架(一眼看清哪一步用了什么方式)
|
||||
|
||||
> **这是"整条链路的技术地图"**——把 N 段主干压成**一段连续伪代码**,每行标注**用了什么方式**(`[锁]` `[幂等]` `[策略]` `[MQ]` `[兜底]` …)。
|
||||
> 局部片段回答"一个机制长什么样",这一段回答"**哪些地方用了什么**",以及**哪些地方本该有兜底却没有**。
|
||||
|
||||
```java
|
||||
// ═══ ① ⟨阶段名⟩ ═══
|
||||
⟨方法名⟩(): ⟨动作⟩ // [锁]
|
||||
⟨动作⟩ // [幂等]
|
||||
⟨动作⟩ // [策略]
|
||||
⟨动作⟩ // [外调]
|
||||
|
||||
// ═══ ② ⟨阶段名⟩ ═══
|
||||
⟨方法名⟩(): ⟨动作⟩ // [验签]
|
||||
⟨动作⟩ // [MQ]
|
||||
⟨动作⟩ // [落库]
|
||||
|
||||
// ═══ ③ ⟨兜底 / 失败处置⟩ ═══
|
||||
⟨方法名⟩(): ⟨动作⟩ // [兜底]
|
||||
⟨动作⟩ // [重试] 只重超时
|
||||
⟨动作⟩ // [履历]
|
||||
```
|
||||
|
||||
**方式图例(反向索引:一种方式出现在哪些地方)**
|
||||
|
||||
| 标记 | 方式 | 出现在 |
|
||||
|---|---|---|
|
||||
| `[锁]` | ⟨分布式锁(注解式 / Redisson)⟩ | ⟨…⟩ · ⟨…⟩ |
|
||||
| `[幂等]` | ⟨状态短路 + CAS + 一次性消费⟩ | ⟨…⟩ · ⟨…⟩ |
|
||||
| `[策略]` | ⟨策略模式 + 工厂⟩ | ⟨…⟩ |
|
||||
| `[MQ]` | ⟨Kafka⟩ | ⟨…⟩ |
|
||||
| `[兜底]` | ⟨延时队列 + XXL-Job⟩ | ⟨…⟩ |
|
||||
| `[重试]` | ⟨`@Retryable`(**只重超时**)⟩ | ⟨…⟩ |
|
||||
| `[履历]` | ⟨DB 台账⟩ | ⟨…⟩ |
|
||||
| `[验签]` | ⟨网关签名⟩ | ⟨…⟩ |
|
||||
| `[隔离]` | ⟨try-catch / 线程池⟩ | ⟨…⟩ |
|
||||
| `[外调]` / `[落库]` / `[通知]` | ⟨外部系统调用 / DB 写 / 站内信⟩ | 全链路 |
|
||||
|
||||
> **这张骨架的两个用处**:① **评审时一眼看清技术分布**(⟨锁 3 处、幂等 5 处、兜底 4 处⟩);② **横向对比**"同样的机制在别处有没有用"。
|
||||
|
||||
---
|
||||
|
||||
# 二、功能点(⟨N⟩ 个)
|
||||
|
||||
> 每个点:**核心内容** / **这个点在讲什么** / **一句话价值** / **图** / **关键片段(≤10 行伪代码)** / **关键表与字段** / **关键做法与证据**。
|
||||
> **段落式**,不要压成表格("做了什么"常是 5–7 条,塞进单元格必然被简化)。
|
||||
|
||||
## ① ⟨功能点名⟩ ⭐⭐⭐⭐⭐
|
||||
|
||||
**核心内容**:⟨1 句,把"形状"说清(一把锁 + 三级短路 + 一条条件更新)⟩
|
||||
|
||||
**这个点在讲什么**
|
||||
|
||||
- **业务场景**:⟨谁在什么时刻触发了什么,为什么这事难;点出"同一时刻还有谁在改同一行数据"⟩
|
||||
- **做了什么**(⟨用一句话概括做法⟩):
|
||||
1. ⟨…⟩;
|
||||
2. ⟨…⟩;
|
||||
3. ⟨…⟩。
|
||||
- **只把 fail 留给"我真的还没处理"**:⟨边界在哪⟩(若适用)
|
||||
- **临界场景**:⟨并发 / 乱序 / 迟到时的行为⟩(若适用)
|
||||
- **解决了什么问题**:⟨不做会怎样⟩
|
||||
|
||||
**一句话价值**:⟨说清"判断权 / 控制权"落在谁手里⟩
|
||||
|
||||
**图 · ⟨图名⟩**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["⟨入口⟩"] --> B{"⟨判断⟩"}
|
||||
B -->|"⟨…⟩"| B1["⟨抢不到锁:直接回 success,让给持锁方⟩"]
|
||||
B -->|"⟨…⟩"| C["⟨动作⟩"]
|
||||
C --> D["⟨落库⟩"]
|
||||
```
|
||||
|
||||
**关键片段 · ⟨片段名⟩**(伪代码,只示形状)
|
||||
|
||||
```
|
||||
⟨CAS 语句 / 锁与短路顺序 / 状态分支 / DDL,≤10 行⟩
|
||||
```
|
||||
|
||||
**关键表与字段**
|
||||
|
||||
| 表 | 关键字段 | 说明 |
|
||||
|---|---|---|
|
||||
| `⟨表名⟩` | `⟨字段⟩` · **`⟨关键字段⟩`** | ⟨说明⟩ |
|
||||
|
||||
**关键做法与证据**
|
||||
|
||||
| 做法 | 证据 |
|
||||
|---|---|
|
||||
| ⟨做法⟩ | `⟨简写⟩/⟨路径⟩.java:⟨行号⟩` |
|
||||
| ⟨做法⟩ | 同上 `:⟨行号⟩` |
|
||||
|
||||
⟨重复 ① 的结构,每个功能点一节⟩
|
||||
|
||||
---
|
||||
|
||||
# 三、推荐组合
|
||||
|
||||
| 组合 | 内容 | 适合 |
|
||||
|---|---|---|
|
||||
| **主线(推荐)** | ⟨① + ⑤⟩ | ⟨最能体现什么能力,追问密度最高⟩ |
|
||||
| **完整版** | ⟨① + ② + ③ + ④ + ⑤⟩ | ⟨讲清完整链路⟩ |
|
||||
| **差异化** | ⟨④⟩ | ⟨少数候选人会讲的点⟩ |
|
||||
|
||||
---
|
||||
|
||||
# 四、设计亮点
|
||||
|
||||
## 4.1 亮点(面试可直接讲)
|
||||
|
||||
| # | 亮点 | 价值 |
|
||||
|:-:|---|---|
|
||||
| 1 | ⟨亮点⟩ | ⟨价值——**逐条对着旧实现的病灶**,不是堆框架⟩ |
|
||||
| 2 | | |
|
||||
|
||||
> **本模板不设缺陷表**:缺陷、严重度与改进方案统一在《⟨主题⟩-改造方案.md》中呈现。深挖过程中发现的结构缺口先记到工作笔记,写改造方案时再展开。
|
||||
|
||||
---
|
||||
|
||||
# 附录 A · ⟨状态 / 结果类型⟩速查
|
||||
|
||||
| ⟨类型⟩ | 含义 | 终态? | ⟨处置1⟩ | ⟨处置2⟩ | 重试 |
|
||||
|---|---|:-:|:-:|:-:|---|
|
||||
| `⟨枚举值⟩` | | ✅ / ❌ | | | |
|
||||
|
||||
# 附录 B · 参数速查
|
||||
|
||||
| 参数 | 值 | 出处 |
|
||||
|---|---|---|
|
||||
| ⟨锁 leaseTime⟩ | | `⟨路径⟩.java:⟨行号⟩` |
|
||||
| ⟨重试次数⟩ | | |
|
||||
| ⟨延时 / 超时⟩ | | |
|
||||
|
||||
# 附录 C · 关键类索引
|
||||
|
||||
| 类 | 角色 |
|
||||
|---|---|
|
||||
| `⟨类名⟩` | ⟨在链路里的角色⟩ |
|
||||
@@ -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⟩` 区分 |
|
||||
| ⟨幂等⟩ | ⟨…⟩ | ⟨…⟩ |
|
||||
|
||||
**两边的根因是同一个**:⟨设计了机制,但**没有为"需要人介入"这个信号建自动出口**⟩。所以两套改造共用一套台账与告警,是**结构上的必然,而不是为了省事**。
|
||||
@@ -0,0 +1,206 @@
|
||||
# ⟨主题⟩ · 设计思路与取舍(复盘)(S4 产物 ②)
|
||||
|
||||
> **视角**:第一人称复盘——**我拿到「⟨需求名⟩」这个需求时,是怎么想、怎么设计、怎么取舍、预判会遇到什么问题的**。贴合现有实现,末尾给改进建议与可直接口述的版本。
|
||||
> **配套文档**:功能点见同目录《⟨主题⟩-功能点清单.md》;⟨可选的关联文档⟩
|
||||
> **说明**:文中「我会这样想」是设计时的推理;「实际做法」是代码里的真实实现,**两者不一致处必须标出**。
|
||||
> **路径简写**:`⟨简写⟩/` = `⟨真实路径⟩`
|
||||
|
||||
---
|
||||
|
||||
## 0. 我拿到需求,先不写代码:把问题定义清楚
|
||||
|
||||
**需求的一句话**:⟨…⟩
|
||||
|
||||
**我第一件事是承认 N 个事实**,它们决定了后面所有设计:
|
||||
|
||||
1. **⟨例:对方的回答不是二元的⟩。** ⟨…⟩
|
||||
2. **⟨例:几种失败的"可逆性"不同⟩。** ⟨…⟩
|
||||
3. **⟨例:我没法从"失败"这个现象本身推出可逆性⟩。** ⟨…⟩
|
||||
|
||||
**由此我定下 N 条不变量(当成验收标准)**:
|
||||
|
||||
- **不变量一**:⟨例:只有确定对方没接单才退款⟩——⟨为什么⟩;
|
||||
- **不变量二**:⟨例:不确定的失败不能当成失败处理⟩——⟨为什么⟩;
|
||||
- **不变量三**:⟨例:每一种失败都必须有归属⟩——⟨为什么⟩。
|
||||
|
||||
**再看 N 个我不能改变的前提**:⟨省侧是权威 · 对外调用有副作用 · 没有跨系统事务 · …⟩
|
||||
|
||||
**结论**:⟨这不是"加个 try-catch 再套个重试框架"的需求,而是「把异常翻译成后果」的需求。⟩——**这句是我做取舍时反复回到的锚点。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 我怎么拆这个需求
|
||||
|
||||
**第一刀,我按「⟨拆解维度⟩」拆,不按「⟨被否决的维度⟩」拆。**
|
||||
|
||||
| 为什么否决另一个维度 | ⟨例:按商品类型拆会得到三块,但三种商品的前置动作不同、汇合点却是同一个;各写一套会得到三份可能走偏的代码⟩ |
|
||||
|---|---|
|
||||
|
||||
拆完是 ⟨N⟩ 条路 + ⟨N⟩ 个汇合点:
|
||||
|
||||
| 路径 | ⟨前置动作⟩ | ⟨性质⟩ | 风险 |
|
||||
|---|---|---|---|
|
||||
| ⟨…⟩ | | 异步 / 同步 | |
|
||||
|
||||
**第二刀,我找「⟨关键定位决策⟩」,这是本需求最关键的一个决策。**
|
||||
|
||||
**我的判断是**:⟨…⟩。理由:⟨只有那一层同时拿得到 X 和 Y;再往上一层只能看到一个已被包装过的异常,原始信息已经丢了⟩。
|
||||
|
||||
**实际做法与我的判断是否一致**:⟨是 / 否⟩,证据 `⟨路径:行号⟩`。
|
||||
|
||||
**这个决策的代价**:⟨例:调用层要维护两套调用方式;推广不全会造成覆盖面缺口(实际就漏了)⟩。
|
||||
|
||||
---
|
||||
|
||||
## 2. 我的设计主线:⟨一句话概括主线⟩
|
||||
|
||||
⟨这是我做的最核心的一个决定:不给 A/B/C 各写一套处理,而是让它们沿一条固定的链往下走,每层只做一件事。⟩
|
||||
|
||||
```
|
||||
① ⟨层名(职责)⟩ → ② ⟨层名(职责)⟩ → ③ ⟨层名(职责)⟩ → ④ ⟨层名(职责)⟩
|
||||
```
|
||||
|
||||
**⟨N⟩ 层不是我拍脑袋凑的,是从"上一版为什么会漏"反推出来的**——每层都对着一个旧实现的具体病灶:
|
||||
|
||||
| 层 | 旧实现的病灶 | 我的做法 | 为什么必须在这一层做 |
|
||||
|:-:|---|---|---|
|
||||
| ① | | `⟨路径:行号⟩` | ⟨信息只在最底层存在,越往上越不可恢复⟩ |
|
||||
| ② | | | |
|
||||
| ③ | | | |
|
||||
| ④ | | | |
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["⟨入口⟩"] --> B["① ⟨层⟩"]
|
||||
B --> C{"② ⟨层⟩"}
|
||||
C -->|"⟨…⟩"| D["③ ⟨层⟩"]
|
||||
C -->|"⟨…⟩"| E["③ ⟨层⟩"]
|
||||
D --> F["④ ⟨归集⟩"]
|
||||
E --> F
|
||||
F -->|"⟨可逆⟩"| G["⟨处置⟩"]
|
||||
F -->|"⟨不确定⟩"| H["⟨处置⟩"]
|
||||
```
|
||||
|
||||
### 2.1 ⟨N⟩ 重判断,才是这个设计的真正内容(标题按实际重数写,如「三重判断…」)
|
||||
|
||||
> 比"N 层"更重要的是**层里做的判断**——它们才是经验,层只是载体。
|
||||
|
||||
**判断一:⟨…⟩**
|
||||
|
||||
⟨用收益 × 概率算的:…⟩ **关键点是"不做什么"比"做什么"更难做对**——⟨因为框架默认什么都做⟩。
|
||||
|
||||
**判断二:⟨…⟩**
|
||||
|
||||
⟨…⟩ **代价必须自己扛**:⟨你关掉了 X,就必须自己承担 Y 的责任。⟩
|
||||
|
||||
**判断三:⟨…⟩**
|
||||
|
||||
⟨…⟩ **为什么"⟨反面做法⟩"是错的**:⟨…⟩
|
||||
|
||||
**还有一条我特意保留的例外**:⟨…⟩——因为 ⟨…⟩。
|
||||
|
||||
### 2.2 ⟨独立工具/组件名⟩ 为什么要写成一个独立⟨工具/组件⟩(若无可删本节)
|
||||
|
||||
⟨它看着简单,但它是整个 ⟨机制⟩ 的地基——判错一次,方向就错,后面全走偏。⟩
|
||||
|
||||
1. **精确匹配** ⟨…⟩;
|
||||
2. **兜底模糊匹配** ⟨…⟩;
|
||||
3. **逐层剥 `cause`**——⟨因为异常常被框架层层包装,真实原因往往在第三、四层⟩。
|
||||
|
||||
**这里的取舍我很清楚**:⟨选了"宁可多试一次"——因为重试的成本(多一次调用)远低于漏重试的成本(一单卡死)⟩。
|
||||
|
||||
---
|
||||
|
||||
## 3. 逐环节决策复盘
|
||||
|
||||
| # | 当时的问题 | 我的选择 | 为什么 | 代价 / 风险 |
|
||||
|:-:|---|---|---|---|
|
||||
| 1 | ⟨…⟩ | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
| … | | | | |
|
||||
|
||||
> **代价/风险列不可空**——只讲选择不讲代价,就等于没做取舍。
|
||||
|
||||
---
|
||||
|
||||
## 4. 我预判会遇到的问题(拿到需求时就能想到的)
|
||||
|
||||
1. **⟨…⟩。** ⟨…⟩
|
||||
2. **⟨…⟩。** ⟨…⟩
|
||||
3. **⟨…⟩。** ⟨这是我设计时预判到了却仍然漏掉的一条⟩
|
||||
|
||||
---
|
||||
|
||||
## 5. 我做对的 ⟨N⟩ 件事 / 承接不住的 ⟨N⟩ 件事
|
||||
|
||||
**做对的**
|
||||
|
||||
1. **⟨…⟩**——⟨为什么它最有价值;它是从业务语义推出来的,不是从技术模式套出来的⟩。
|
||||
2. **⟨…⟩**——⟨…⟩
|
||||
3. **⟨…⟩**——⟨方向是对的,只是推广范围不够⟩
|
||||
|
||||
**承接不住的**
|
||||
|
||||
1. **⟨…(最严重)⟩**——⟨后果;这是"设计对了但落地不完整"的典型案例⟩。
|
||||
2. **⟨…⟩**
|
||||
3. **⟨…⟩**
|
||||
|
||||
---
|
||||
|
||||
## 6. 如果让我重做,我会改什么(按性价比排序)
|
||||
|
||||
> 复盘视角只给**优先级判断**;分阶段展开、验收标准与灰度见《⟨主题⟩-改造方案.md》,此处不重复。
|
||||
|
||||
| 优先级 | 改什么 | 为什么 | 成本 | 收益 |
|
||||
|:-:|---|---|---|---|
|
||||
| **P0** | | ⟨**真金白银 / 会花错钱**⟩ | 低 | |
|
||||
| **P1** | | | 中 | |
|
||||
| **P2** | | | | |
|
||||
| **P3** | | ⟨卫生问题⟩ | | |
|
||||
|
||||
**一句话排序逻辑**:先修**会花错钱和会重复调用外部系统的**(P0/P1),再做**让"不确定"可运营**(P1/P2),最后才是卫生问题(P3)。**因为前两类是"静默地把事情做错",后者只是"做得不够漂亮"。**
|
||||
|
||||
---
|
||||
|
||||
## 7. 这套设计里可以复用的方法论
|
||||
|
||||
1. **⟨先判"可逆性",再决定"要不要补偿"。⟩** ⟨任何跨系统写操作都适用…⟩
|
||||
2. **⟨异常分层翻译:真相层 → 重试层 → 履历层 → 归集层。⟩** ⟨每层只做一件事,且必须在能拿到信息的最高层把信息捞住⟩
|
||||
3. **⟨重试责任单点。⟩** ⟨否则是乘法关系,不是加法⟩
|
||||
4. **⟨"不确定"必须是一等状态。⟩** ⟨否则会退化成一行日志,等于不存在⟩
|
||||
5. **⟨分级的上限取决于覆盖面,不是取决于精细度。⟩** ⟨一条路上做四层分级,不如四条路上各做一层⟩
|
||||
|
||||
---
|
||||
|
||||
## 8. 附:面试口述版
|
||||
|
||||
### 8.1 一分钟版(先讲骨架)
|
||||
|
||||
> 「⟨需求一句话⟩。我的判断是:⟨核心判断⟩。所以我不能只写 try-catch,得**把 ⟨X⟩ 翻译成 ⟨Y⟩**。
|
||||
>
|
||||
> 设计上是 N 层:⟨逐层一句话⟩。调用方只做一件事:⟨…⟩。」
|
||||
|
||||
### 8.2 三分钟版(加取舍与代价)
|
||||
|
||||
在上一段基础上补三段:⟨取舍一(最看重的一点)+ 它的代价⟩ · ⟨取舍二 + 难在哪⟩ · ⟨要坦白的一点(覆盖面 / 落地不完整)⟩。
|
||||
|
||||
### 8.3 追问预判(问题 → 一句话答)
|
||||
|
||||
| 追问 | 答 |
|
||||
|---|---|
|
||||
| ⟨为什么…?⟩ | ⟨一句话⟩ |
|
||||
| ⟨既然…,那…?⟩ | ⟨一句话,含"坦白说没有"这类诚实回答⟩ |
|
||||
| ⟨这套设计最大的风险?⟩ | ⟨一句话⟩ |
|
||||
|
||||
---
|
||||
|
||||
## 附:与代码的对照说明
|
||||
|
||||
| 本文的判断 | 代码事实 | 是否一致 |
|
||||
|---|---|---|
|
||||
| ⟨分级放最底层⟩ | `⟨路径:行号⟩` | ✅ 一致,但**未推广** |
|
||||
| ⟨只重超时⟩ | `⟨路径:行号⟩` | ✅ 一致 |
|
||||
| ⟨…⟩ | | ⚠️ 部分一致 |
|
||||
|
||||
> **诚实性要求**:本文的判断与代码事实**不一致处必须逐条列出**——这是这份文档可信度的来源。
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
name: value-scan
|
||||
description: Use when the user wants to inventory the mechanisms worth talking about in code that is already delivered — "盘点这个域/模块的价值点", "扫一下 order 域有什么值得讲的", "提取功能点", "这个项目有什么值得讲的设计", or when interview/resume material must be reconstructed backwards from an existing module. Read-only — never modifies the scanned project. Stops at the human selection gate; deep write-up is a separate skill.
|
||||
---
|
||||
|
||||
# value-scan · 价值点盘点(广度枚举)
|
||||
|
||||
## 一句话
|
||||
|
||||
从**已交付的代码**里,逆向枚举出**值得讲的机制点**,交人勾选后**停下**。
|
||||
|
||||
**三条核心原则**
|
||||
|
||||
1. **agent 判不了"价值"**——判据不是分类体系,而是**简历行填空测试**。
|
||||
2. **价值优先,本阶段不找缺陷。** 缺陷是深入链路时自然浮现的副产物,归 `value-dig`。
|
||||
3. **只读。** 不修改被扫描项目的任何文件。
|
||||
|
||||
## 流程
|
||||
|
||||
```
|
||||
S0 定范围 → S1 枚举 → S2 勾选【终点:产出清单后必须停】
|
||||
↓(人勾选后)
|
||||
value-dig(轻理由 → 设计复盘 / 功能点 / 改造方案)
|
||||
```
|
||||
|
||||
| 需求 | 该用 |
|
||||
|---|---|
|
||||
| 盘点 / 扫一遍 / 提取候选价值点 | **本 skill** |
|
||||
| 把选中的点写成深度文档 | `value-dig` |
|
||||
| 找 bug、修缺陷 | `diagnose` |
|
||||
|
||||
## S0 · 定范围
|
||||
|
||||
- **快路**:仓库已有域清单(`.aspirecode/sdd/rules.md` §9、`pom.xml`、`wiki/` 目录)→ 直接读。
|
||||
- **慢路**:陌生仓库 → 按接口路径前缀、`-api` Feign 接口名、controller 清单切出能力面,一次一个 package。
|
||||
- 范围由**人给定**(一个域/模块),不一次扫全库。
|
||||
|
||||
**域是入口锚点,不是物理边界。** 完整链路往往跨模块,用两头夹的办法拼:
|
||||
|
||||
- **聚合层(web)定链路形状**:controller / Kafka consumer / XxlJob / callback 四类入口全在聚合层,且分包与路径自带业务语义(`/consumer/fulfillment/`、topic 名、请求路径)——从这里正向切出"有哪些触发者、哪些链路"。MQ 消费、定时任务这类**非 Feign 入口**靠反推找不到。
|
||||
- **原子域挖机制内容**:锁/幂等/状态机等机制本体大多在原子域的 service 实现,聚合层只有编排——读实现要去原子域。
|
||||
- **中间用调用图接**(`gitnexus_route_map` / `gitnexus_cypher`);被驱动型域也可用"谁 import 了我的 Feign 接口"反查调用方作兜底。
|
||||
- 只读链路经过的路径,不通读途径模块。
|
||||
|
||||
## S1 · 枚举
|
||||
|
||||
### 取材顺序(信噪比递减)
|
||||
|
||||
| 顺序 | 来源 | 备注 |
|
||||
|:-:|---|---|
|
||||
| 1 | `wiki/*.md`、`.claude/docs/**`、`devflow/projects/**` | 先读「设计说明类」,后读「问题分析类」——先读问题分析会把清单带成缺陷导向 |
|
||||
| 2 | `git log`:`feat(...)` / `refactor(...)`、带单号、单次大改动 | 有意识的改动藏着设计意图 |
|
||||
| 3 | `gitnexus_query` / `gitnexus_route_map` | 用图查结构,不读全文 |
|
||||
| 4 | 代码结构统计 + 关键字 grep(锁/重试/MQ/Job/条件更新/状态机/延时/对账) | 只做清单式定位,不通读代码 |
|
||||
|
||||
> ⚠️ 读完文档必须回代码验一遍"文档写的方案是否落地"。**"文档 vs 代码分叉"本身就是高价值点**。
|
||||
|
||||
### 怎么看结构:按「触发者」拆链路
|
||||
|
||||
不按业务功能拆,按**谁触发**拆:找出「**同一行数据被几个触发者改**」(如回调/查单/取消都改同一行支付流水)——收敛点多半就是机制所在。通常 1–4 条链路,被驱动型模块可到 5–6 条(超过要写一句为什么)。
|
||||
|
||||
每条链路挂 1–4 个机制节点。**候选颗粒度 = 机制/能力**,不是注解、类、配置项——"用了 `@LockAction`、有 4 个 handler"是实现清单;跨 ≥2 个写类入口或 ≥2 张表的才够"机制"。
|
||||
|
||||
### 判据 · 简历行测试
|
||||
|
||||
**一级 · 准入(三条硬标准,全过才进清单)**
|
||||
|
||||
| 标准 | 反例(据此剔除) |
|
||||
|---|---|
|
||||
| ① **核心链路**(资金/履约/交付这类业务闭环) | 购物车 key 命名、查询接口 |
|
||||
| ② **高级工程师的设计**(设计决策,非框架常识) | Redis `GETDEL`、Spring 自注入修事务自调用 |
|
||||
| ③ **简历够硬**(能写成"我设计了 X 机制解决 Y") | 定长文件 + 签名 + SFTP 的对接实现 |
|
||||
|
||||
**二级 · 表达**:`用【机制】解决了【具体问题/场景】,代价是【取舍】`——本阶段只试填"机制+问题"两格;填不出机制 → 并入「已剔除」表(标 `机制格填不出`,待复核);填不出取舍**不误杀**(取舍归 `value-dig`)。
|
||||
|
||||
> ⚠️ **`机制格填不出` 的剔除门槛(防误杀,实测翻案教训)**:机制本体常藏在实现大类(2000+ 行)的私有方法群里,入口类名/Job 类名只是壳——**没读过入口方法向下 ~100 行,不得以此理由定稿剔除**。剔除非此理由的照常。被剔除后复评翻案的,在剔除表原行标注撤销原因与日期(如"2026-09-18 复评撤销:快照轮询机制实存,升级为 ★N"),不删行——翻案记录本身就是筛选口径的进化证据。
|
||||
|
||||
被剔除的点进「已剔除」表(注明未过哪条标准),不丢弃。
|
||||
|
||||
### 数量
|
||||
|
||||
亮点 **5–8 条**为宜。超过 12 条 = 颗粒度掉到实现层,退回重并。
|
||||
|
||||
## S2 · 门控(终点,不可跳过)
|
||||
|
||||
1. 候选清单**已落盘**(每条含「一句话价值」与锚点),头部「闸门状态」节写明"S1 已完成 · 待勾选";
|
||||
2. 回复里**显式请求勾选**("请从这 N 条里挑 3–6 条")。用户禁用提问时也一样停——把请求写成文字,**不是**替他挑完继续写文档。
|
||||
3. **否决即校准**:人否决候选("不够硬"/"一般")时,把否决原因记入清单(闸门状态或剔除表)——下次扫同域/同仓先读上次的否决记录,同类点不再重复上报;整批否决且未给新目标 = 流程终点,不强续。
|
||||
|
||||
## 硬规则(合并版,替代旧的硬规则/反模式/Gotchas 三张表)
|
||||
|
||||
| # | 规则 | 为什么 |
|
||||
|:-:|---|---|
|
||||
| 1 | **深度闸门**:无围栏代码块(Mermaid 除外,行内反引号可用);不贴源码、不写行号、不逐层拆解、不展开取舍 | 本阶段是广度层 |
|
||||
| 2 | **不找缺陷、不读"坏味道报告"**;把克制当设计("manager 只有 3 个"可能是有意的) | 缺陷导向会摧毁清单 |
|
||||
| 3 | **证据真实性**:锚点 `⟨简写⟩/路径#方法名`(机制级可锚到类;不写行号),**读过再写**,拿不准标【待确认】,每条 ≤2 个 | 错锚点毁掉可信度 |
|
||||
| 4 | **每条链路(含未入选的)都要写「是什么 + 为什么入选/未入选」**;0 机制先问"真没有,还是采样不到位" | 链路是骨架,不因无亮点而省略 |
|
||||
| 5 | **三层结构**:域 → 链路 → 机制节点,机制必须挂在链路图上 | 并列清单会显得"都是散的" |
|
||||
| 6 | **只读 + 门控**:不改被扫项目文件;产出后必停 | — |
|
||||
|
||||
## 格式保证
|
||||
|
||||
1. **模板外置**:读 `assets/候选清单模板.md` 填空,不照印象写。
|
||||
2. **机械校验**:产出后跑 `python <skills>/value-scan/assets/check.py scan -Product <产物> -Template <skills>/value-scan/assets/候选清单模板.md`(跨平台 Python 版;Windows 下若中文乱码先设 `PYTHONIOENCODING=utf-8`。旧 `check.ps1` 保留但不再维护)。FAIL 必须为 0;WARN 允许保留但写明原因。
|
||||
3. **偏差回写**:被纠正过就回写模板。模板是活资产。
|
||||
|
||||
## 触发词与落盘
|
||||
|
||||
**触发词**:「盘点这个模块的价值点」「扫一下 X 域有什么值得讲的」「提取功能点」。
|
||||
**落盘**:`<项目根>/docs/{域}-候选价值点.md`(可被用户覆盖)。
|
||||
|
||||
**下一步**:用户勾选后,用 `value-dig` 接手。
|
||||
@@ -0,0 +1,268 @@
|
||||
<#
|
||||
value-scan / value-dig 产物机械校验(替代人工核对)
|
||||
------------------------------------------------------------------
|
||||
用法:
|
||||
# S1 候选清单
|
||||
powershell -ExecutionPolicy Bypass -File check.ps1 -Mode scan `
|
||||
-Product <产物路径> -Template <skills>/value-scan/assets/候选清单模板.md
|
||||
|
||||
# S4 功能点清单
|
||||
powershell -ExecutionPolicy Bypass -File check.ps1 -Mode dig `
|
||||
-Product <产物路径> -Template <skills>/value-dig/assets/深度模板/功能点清单模板.md
|
||||
|
||||
# S4 设计思路与取舍 / 改造方案(整篇级文档,只跑通用校验)
|
||||
powershell -ExecutionPolicy Bypass -File check.ps1 -Mode doc -Product <path> -Template <path>
|
||||
|
||||
退出码:0 = 无 FAIL;1 = 有 FAIL
|
||||
说明:FAIL = 必错;WARN = 需人判断
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('scan', 'dig', 'doc')]
|
||||
[string]$Mode,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Product,
|
||||
|
||||
[string]$Template
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$script:fail = New-Object System.Collections.Generic.List[string]
|
||||
$script:warn = New-Object System.Collections.Generic.List[string]
|
||||
$script:pass = New-Object System.Collections.Generic.List[string]
|
||||
function Fail($m) { $script:fail.Add($m) }
|
||||
function Warn($m) { $script:warn.Add($m) }
|
||||
function Pass($m) { $script:pass.Add($m) }
|
||||
|
||||
$prodPath = (Resolve-Path -LiteralPath $Product).Path
|
||||
$text = [System.IO.File]::ReadAllText($prodPath, [System.Text.Encoding]::UTF8)
|
||||
if ($text.Length -gt 0 -and [int][char]$text[0] -eq 0xFEFF) { $text = $text.Substring(1) }
|
||||
$lines = $text -split "`r?`n"
|
||||
|
||||
# ---------------------------------------------------------------- 围栏代码块
|
||||
$fences = @()
|
||||
$inFence = $false; $fStart = 0; $fLang = ''
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^\s*```') {
|
||||
if (-not $inFence) {
|
||||
$inFence = $true; $fStart = $i; $fLang = ($lines[$i] -replace '^\s*```', '').Trim()
|
||||
}
|
||||
else {
|
||||
$inFence = $false
|
||||
$fences += [pscustomobject]@{ Start = $fStart; End = $i; Lang = $fLang; Body = ($i - $fStart - 1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($inFence) { Fail "存在未闭合的代码围栏(起始行 $($fStart + 1))" }
|
||||
|
||||
# ---------------------------------------------------------------- 通用 1:引号逐行配对
|
||||
$oddQuoteLines = @()
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ((([regex]::Matches($lines[$i], '"')).Count % 2) -ne 0) { $oddQuoteLines += ($i + 1) }
|
||||
}
|
||||
if ($oddQuoteLines.Count -eq 0) { Pass '引号逐行配对' }
|
||||
else { Fail ("引号未配对的行: " + ($oddQuoteLines -join ', ')) }
|
||||
|
||||
# ---------------------------------------------------------------- 通用 2:mermaid 合法性
|
||||
$mermaidFences = @($fences | Where-Object { $_.Lang -eq 'mermaid' })
|
||||
$mmBad = 0
|
||||
foreach ($f in $mermaidFences) {
|
||||
$body = ($lines[($f.Start + 1)..($f.End - 1)] -join "`n")
|
||||
$firstLine = (($body -split "`r?`n") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1)
|
||||
$endCount = ([regex]::Matches($body, '(?m)^\s*end\s*$')).Count
|
||||
if ($firstLine -match 'sequenceDiagram') {
|
||||
# 时序图:end 收的是 alt/opt/loop/par/critical/break/rect
|
||||
$blk = ([regex]::Matches($body, '(?m)^\s*(alt|opt|loop|par|critical|break|rect)\b')).Count
|
||||
if ($blk -ne $endCount) { Fail "mermaid 时序图(第 $($f.Start + 1) 行起)alt/opt/loop 等 $blk 个但 end=$endCount"; $mmBad++ }
|
||||
}
|
||||
else {
|
||||
$sg = ([regex]::Matches($body, '(?m)^\s*subgraph\s')).Count
|
||||
if ($sg -ne $endCount) { Fail "mermaid 流程图(第 $($f.Start + 1) 行起)subgraph=$sg 但 end=$endCount"; $mmBad++ }
|
||||
if ($body -match '(?m)^\s*subgraph\s+\S+\s*\[' -and $body -notmatch '(?m)^\s*subgraph\s+\S+\s*\["') {
|
||||
Fail "mermaid(第 $($f.Start + 1) 行起)subgraph 缺引号标题,必须写 subgraph id[`"标题`"]"; $mmBad++
|
||||
}
|
||||
if ($body -match '(?m)^\s*subgraph\s+\S+\s*\[\(') {
|
||||
Fail "mermaid(第 $($f.Start + 1) 行起)用了 subgraph xxx[(...)],会解析失败"; $mmBad++
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($mermaidFences.Count -gt 0 -and $mmBad -eq 0) { Pass "mermaid 块 $($mermaidFences.Count) 个通过" }
|
||||
|
||||
# ---------------------------------------------------------------- 通用 3:表格列数一致
|
||||
$ti = 0
|
||||
while ($ti -lt $lines.Count) {
|
||||
if ($lines[$ti] -match '^\s*\|') {
|
||||
$blk = @(); $bs = $ti
|
||||
while ($ti -lt $lines.Count -and $lines[$ti] -match '^\s*\|') { $blk += $lines[$ti]; $ti++ }
|
||||
$counts = @($blk | ForEach-Object { ([regex]::Matches($_, '\|')).Count } | Sort-Object -Unique)
|
||||
if ($counts.Count -gt 1) { Fail "表格列数不一致(第 $($bs + 1) 行起):pipe 数 = $($counts -join '/')" }
|
||||
}
|
||||
else { $ti++ }
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 通用 3b:表格不得缩进(嵌套在列表内的表多数渲染器不显示)
|
||||
$indentedTable = @()
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^[ \t]+\|') { $indentedTable += ($i + 1) }
|
||||
}
|
||||
if ($indentedTable.Count -eq 0) { Pass '表格均顶格(无嵌套缩进表)' }
|
||||
else { Fail ('表格存在缩进(第 ' + ($indentedTable -join ', ') + ' 行起)——嵌套在列表里的表多数渲染器不显示,必须顶格') }
|
||||
|
||||
# ---------------------------------------------------------------- 通用 4:章节完整性(模板必备节 ⊆ 产出节)
|
||||
if ($Template) {
|
||||
$tPath = (Resolve-Path -LiteralPath $Template).Path
|
||||
$tText = [System.IO.File]::ReadAllText($tPath, [System.Text.Encoding]::UTF8)
|
||||
if ($tText.Length -gt 0 -and [int][char]$tText[0] -eq 0xFEFF) { $tText = $tText.Substring(1) }
|
||||
$tLines = $tText -split "`r?`n"
|
||||
$tHeads = $tLines | Where-Object { $_ -match '^\s*#+\s+\S' }
|
||||
$missingLiteral = @(); $missingPlaceholder = @()
|
||||
foreach ($h in $tHeads) {
|
||||
$t = ($h -replace '^\s*#+\s+', '') -replace '\s+$', ''
|
||||
$hasPh = $t.Contains([string][char]0x27E8)
|
||||
$sentinel = '@@PH@@'
|
||||
$phRx = [string][char]0x27E8 + '[^' + [string][char]0x27E9 + ']*' + [string][char]0x27E9
|
||||
$rx = [regex]::Escape([regex]::Replace($t, $phRx, $sentinel)).Replace($sentinel, '.*')
|
||||
$hit = $false
|
||||
foreach ($pl in $lines) { if ($pl -match ('^\s*#+\s+' + $rx + '\s*$')) { $hit = $true; break } }
|
||||
if (-not $hit) {
|
||||
if ($hasPh) { $missingPlaceholder += $t } else { $missingLiteral += $t }
|
||||
}
|
||||
}
|
||||
if ($missingLiteral.Count -eq 0) { Pass '章节完整性:模板必备节全部存在' }
|
||||
else { Fail ('缺章节(字面量,必错): ' + ($missingLiteral -join ' | ')) }
|
||||
if ($missingPlaceholder.Count -gt 0) { Warn ('示例性章节未匹配(可能条数不同,需人判): ' + ($missingPlaceholder -join ' | ')) }
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 定位辅助
|
||||
function Get-HeadIndex($pattern) {
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match $pattern) { return $i } }
|
||||
return -1
|
||||
}
|
||||
function Get-NextHeadIndex($from) {
|
||||
for ($i = $from + 1; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*#+\s+\S') { return $i } }
|
||||
return $lines.Count
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- scan 专属
|
||||
if ($Mode -eq 'scan') {
|
||||
# 1) 禁止围栏代码块(mermaid 除外)
|
||||
$nonMermaid = @($fences | Where-Object { $_.Lang -ne 'mermaid' })
|
||||
if ($nonMermaid.Count -eq 0) { Pass '深度闸门:S1 无围栏代码块(仅 mermaid)' }
|
||||
else { Fail ("S1 不允许围栏代码块,发现 $($nonMermaid.Count) 个(第 " + (($nonMermaid | ForEach-Object { $_.Start + 1 }) -join ', ') + ' 行起)') }
|
||||
|
||||
# 2) 锚点格式:路径#方法名(不带行号,每条最多 2 个)
|
||||
$badAnchor = @()
|
||||
$anchorCount = 0
|
||||
foreach ($l in $lines) {
|
||||
if ($l -match '^\s*\*\*锚点\*\*\s*[::]\s*(.+?)\s*$') {
|
||||
$anchorCount++
|
||||
$v = $Matches[1]
|
||||
$items = @($v -split '[、,,]' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
||||
if ($items.Count -gt 2) { $badAnchor += ("锚点超过 2 个($($items.Count) 个): " + $l.Trim()) }
|
||||
foreach ($it in $items) {
|
||||
$a = $it.Trim().Trim('`')
|
||||
if ($a -match ':\d') { $badAnchor += ("S1 锚点不写行号 -> $a") }
|
||||
elseif ($a -notmatch '\w[/\\]\w') { $badAnchor += ("S1 锚点须为 路径#方法名(机制级可到类名) -> $a") }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($anchorCount -eq 0) { Warn '未找到 **锚点** 行(若产物为空则忽略)' }
|
||||
elseif ($badAnchor.Count -eq 0) { Pass "锚点格式合规($anchorCount 处,路径#方法名)" }
|
||||
else { Fail ('锚点格式不合规: ' + ($badAnchor -join ' ; ')) }
|
||||
|
||||
# 3) 闸门状态节
|
||||
if ((Get-HeadIndex '^\s*#+\s*闸门状态') -ge 0) { Pass '有「闸门状态」节(S2 门控有证据)' }
|
||||
else { Fail '缺「闸门状态」节 —— S2 门控没有证据' }
|
||||
|
||||
# 4) 候选条数(提示性:超 12 才 FAIL,无下限硬卡)
|
||||
$starCount = ($lines | Where-Object { $_ -match '^\s*#+\s*★' }).Count
|
||||
if ($starCount -le 12) { Pass "候选条数 $starCount(≤12)" }
|
||||
else { Fail "候选条数 $starCount 超过 12(颗粒度掉到实现层,需重并)" }
|
||||
if ($starCount -lt 5 -and $starCount -gt 0) { Warn "候选条数 $starCount 少于 5——先确认是否采样不到位,而非真没有" }
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- dig 专属
|
||||
if ($Mode -eq 'dig') {
|
||||
$skelIdx = Get-HeadIndex '^\s*#+\s*主干调用骨架'
|
||||
$skelEnd = if ($skelIdx -ge 0) { Get-NextHeadIndex $skelIdx } else { -1 }
|
||||
if ($skelIdx -ge 0) { Pass '有「主干调用骨架」节' }
|
||||
else { Warn '未找到「主干调用骨架」节(仅 S4 ① 功能点清单必需)' }
|
||||
|
||||
# 1) 局部代码块 ≤10 行(骨架块豁免)
|
||||
$exempt = 0
|
||||
foreach ($f in $fences) {
|
||||
if ($f.Lang -eq 'mermaid') { continue }
|
||||
$isSkel = ($skelIdx -ge 0 -and $f.Start -gt $skelIdx -and $f.Start -lt $skelEnd)
|
||||
if ($isSkel) { $exempt++; continue }
|
||||
if ($f.Body -gt 10) { Warn "局部代码块超过 10 行(第 $($f.Start + 1) 行起,$($f.Body) 行)——行数非硬限,请人判断是关键片段还是源码摘录" }
|
||||
}
|
||||
Pass ("局部代码块行数检查完成(豁免骨架块 $exempt 个)")
|
||||
|
||||
# 2) 骨架每行必须带方式标记
|
||||
if ($skelIdx -ge 0) {
|
||||
$skelFence = @($fences | Where-Object { $_.Start -gt $skelIdx -and $_.Start -lt $skelEnd }) | Select-Object -First 1
|
||||
if (-not $skelFence) { Warn '骨架节里没有代码块' }
|
||||
else {
|
||||
$missTag = @()
|
||||
foreach ($bl in ($lines[($skelFence.Start + 1)..($skelFence.End - 1)])) {
|
||||
if ([string]::IsNullOrWhiteSpace($bl)) { continue }
|
||||
if ($bl -match '^\s*//') { continue } # 分段注释行
|
||||
if ($bl -match ':\s*$') { continue } # 块头行(xxx(): ),标"谁"不标"怎么做"
|
||||
if ($bl -notmatch '[(\[]') { continue } # 连接词行(↓ 三路汇合)
|
||||
if ($bl -notmatch '//') { $missTag += $bl.Trim() } # 其余动作行必须有方式标记
|
||||
}
|
||||
if ($missTag.Count -eq 0) { Pass '骨架每行都带方式标记' }
|
||||
else { Fail "骨架有 $($missTag.Count) 行缺方式标记: " + (($missTag | Select-Object -First 3) -join ' ; ') }
|
||||
}
|
||||
}
|
||||
|
||||
# 3) 证据须含 行号
|
||||
if ($text -notmatch '\.(java|xml|yaml|yml|sql):\d+') { Fail '未找到任何 `文件:行号` 证据(本阶段必须有可核对的锚点)' }
|
||||
else { Pass '存在 `文件:行号` 形式的证据' }
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- doc 专属
|
||||
if ($Mode -eq 'doc') {
|
||||
# 改造方案识别:含「现状问题登记」节(设计思路与取舍模板不含此节)
|
||||
$isPlan = (Get-HeadIndex '^\s*#+\s*0\.\s*现状问题登记') -ge 0
|
||||
if ($isPlan) {
|
||||
# 1) 必有非 mermaid 代码片段(伪代码 / SQL)
|
||||
$codeFences = @($fences | Where-Object { $_.Lang -ne 'mermaid' })
|
||||
if ($codeFences.Count -ge 1) { Pass "改造方案含关键片段 $($codeFences.Count) 个" }
|
||||
else { Fail '改造方案缺「关键片段」代码块——必须"照着能讲代码"(伪代码/SQL 均可,只禁大段源码摘录)' }
|
||||
|
||||
# 2) 片段应覆盖"骨架 + 改造点",只给一块多半只在开头充数
|
||||
if ($codeFences.Count -ge 2) { Pass "关键片段 $($codeFences.Count) 个(骨架 + 改造点)" }
|
||||
elseif ($codeFences.Count -eq 1) { Warn '只有 1 个关键片段——可能只有链路骨架,各改造点未给伪代码,需人判' }
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 输出
|
||||
Write-Output ""
|
||||
Write-Output ("=" * 68)
|
||||
Write-Output "机械校验 Mode=$Mode"
|
||||
Write-Output (" product : " + $prodPath)
|
||||
if ($Template) { Write-Output (" template : " + (Resolve-Path -LiteralPath $Template).Path) }
|
||||
Write-Output ("=" * 68)
|
||||
|
||||
if ($script:pass.Count -gt 0) {
|
||||
Write-Output ""
|
||||
Write-Output "[PASS]"
|
||||
foreach ($m in $script:pass) { Write-Output (" + " + $m) }
|
||||
}
|
||||
if ($script:warn.Count -gt 0) {
|
||||
Write-Output ""
|
||||
Write-Output "[WARN] 需人判断"
|
||||
foreach ($m in $script:warn) { Write-Output (" ! " + $m) }
|
||||
}
|
||||
if ($script:fail.Count -gt 0) {
|
||||
Write-Output ""
|
||||
Write-Output "[FAIL] 必错"
|
||||
foreach ($m in $script:fail) { Write-Output (" x " + $m) }
|
||||
}
|
||||
|
||||
Write-Output ""
|
||||
Write-Output ("结果:PASS {0} / WARN {1} / FAIL {2}" -f $script:pass.Count, $script:warn.Count, $script:fail.Count)
|
||||
if ($script:fail.Count -gt 0) { exit 1 } else { exit 0 }
|
||||
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
value-scan / value-dig 产物机械校验(Python 版,等价移植自 check.ps1,跨平台)
|
||||
------------------------------------------------------------------
|
||||
用法:
|
||||
# S1 候选清单
|
||||
python check.py scan <产物路径> --template <skills>/value-scan/assets/候选清单模板.md
|
||||
|
||||
# S4 功能点清单
|
||||
python check.py dig <产物路径> --template <skills>/value-dig/assets/深度模板/功能点清单模板.md
|
||||
|
||||
# S4 设计思路与取舍 / 改造方案(整篇级文档,只跑通用校验)
|
||||
python check.py doc <产物路径> --template <路径>
|
||||
|
||||
退出码:0 = 无 FAIL;1 = 有 FAIL;2 = 用法/IO 错误
|
||||
说明:FAIL = 必错;WARN = 需人判断
|
||||
"""
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
FAIL, WARN, PASS = [], [], []
|
||||
|
||||
# ⟨ ⟩ 占位符(U+27E8 / U+27E9)
|
||||
PH_L, PH_R = '\u27e8', '\u27e9'
|
||||
|
||||
|
||||
def fail(m):
|
||||
FAIL.append(m)
|
||||
|
||||
|
||||
def warn(m):
|
||||
WARN.append(m)
|
||||
|
||||
|
||||
def pass_(m):
|
||||
PASS.append(m)
|
||||
|
||||
|
||||
def read_text(path_str):
|
||||
p = Path(path_str)
|
||||
if not p.is_file():
|
||||
print(f"[ERROR] 文件不存在: {path_str}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
text = p.read_text(encoding='utf-8-sig') # 自动剥 BOM
|
||||
return text, text.splitlines()
|
||||
|
||||
|
||||
def head_index(lines, pattern):
|
||||
for i, line in enumerate(lines):
|
||||
if re.search(pattern, line):
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
def next_head_index(lines, start):
|
||||
for i in range(start + 1, len(lines)):
|
||||
if re.match(r'^\s*#+\s+\S', lines[i]):
|
||||
return i
|
||||
return len(lines)
|
||||
|
||||
|
||||
def line_snip(line, width=40):
|
||||
"""报错附带的行内容摘要(改进:报错不指内容曾导致误诊)"""
|
||||
s = line.strip().replace('|', '\\|')
|
||||
return s[:width] + ('…' if len(s) > width else '')
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description='value-scan/value-dig 产物机械校验')
|
||||
ap.add_argument('mode', choices=['scan', 'dig', 'doc'])
|
||||
ap.add_argument('product', help='产物路径')
|
||||
ap.add_argument('--template', '-t', help='模板路径')
|
||||
ap.add_argument('-Mode', '-Product', '-Template', dest='legacy', help=argparse.SUPPRESS)
|
||||
args = ap.parse_args()
|
||||
|
||||
prod_path = Path(args.product).resolve()
|
||||
text, lines = read_text(args.product)
|
||||
|
||||
# ------------------------------------------------ 围栏代码块
|
||||
fences = []
|
||||
in_fence = False
|
||||
f_start, f_lang = 0, ''
|
||||
for i, line in enumerate(lines):
|
||||
if re.match(r'^\s*```', line):
|
||||
if not in_fence:
|
||||
in_fence, f_start = True, i
|
||||
f_lang = re.sub(r'^\s*```', '', line).strip()
|
||||
else:
|
||||
in_fence = False
|
||||
fences.append({'start': f_start, 'end': i, 'lang': f_lang,
|
||||
'body': i - f_start - 1})
|
||||
if in_fence:
|
||||
fail(f'存在未闭合的代码围栏(起始行 {f_start + 1})')
|
||||
|
||||
# ------------------------------------------------ 通用 1:引号逐行配对
|
||||
odd_quote = [i + 1 for i, line in enumerate(lines)
|
||||
if line.count('"') % 2 != 0]
|
||||
if not odd_quote:
|
||||
pass_('引号逐行配对')
|
||||
else:
|
||||
fail('引号未配对的行: ' + ', '.join(map(str, odd_quote)))
|
||||
|
||||
# ------------------------------------------------ 通用 2:mermaid 合法性
|
||||
mermaid_fences = [f for f in fences if f['lang'] == 'mermaid']
|
||||
mm_bad = 0
|
||||
for f in mermaid_fences:
|
||||
body_lines = lines[f['start'] + 1:f['end']]
|
||||
body = '\n'.join(body_lines)
|
||||
first = next((l for l in body_lines if l.strip()), '')
|
||||
end_count = len(re.findall(r'(?m)^\s*end\s*$', body))
|
||||
if 'sequenceDiagram' in first:
|
||||
blk = len(re.findall(r'(?m)^\s*(alt|opt|loop|par|critical|break|rect)\b', body))
|
||||
if blk != end_count:
|
||||
fail(f"mermaid 时序图(第 {f['start'] + 1} 行起)alt/opt/loop 等 {blk} 个但 end={end_count}")
|
||||
mm_bad += 1
|
||||
else:
|
||||
sg = len(re.findall(r'(?m)^\s*subgraph\s', body))
|
||||
if sg != end_count:
|
||||
fail(f"mermaid 流程图(第 {f['start'] + 1} 行起)subgraph={sg} 但 end={end_count}")
|
||||
mm_bad += 1
|
||||
if (re.search(r'(?m)^\s*subgraph\s+\S+\s*\[', body)
|
||||
and not re.search(r'(?m)^\s*subgraph\s+\S+\s*\["', body)):
|
||||
fail(f'mermaid(第 {f["start"] + 1} 行起)subgraph 缺引号标题,必须写 subgraph id["标题"]')
|
||||
mm_bad += 1
|
||||
if re.search(r'(?m)^\s*subgraph\s+\S+\s*\[\(', body):
|
||||
fail(f'mermaid(第 {f["start"] + 1} 行起)用了 subgraph xxx[(...)],会解析失败')
|
||||
mm_bad += 1
|
||||
if mermaid_fences and mm_bad == 0:
|
||||
pass_(f'mermaid 块 {len(mermaid_fences)} 个通过')
|
||||
|
||||
# ------------------------------------------------ 通用 3:表格列数一致(含行内容摘要)
|
||||
ti = 0
|
||||
while ti < len(lines):
|
||||
if re.match(r'^\s*\|', lines[ti]):
|
||||
bs = ti
|
||||
blk = []
|
||||
while ti < len(lines) and re.match(r'^\s*\|', lines[ti]):
|
||||
blk.append(lines[ti])
|
||||
ti += 1
|
||||
counts = sorted({b.count('|') for b in blk})
|
||||
if len(counts) > 1:
|
||||
fail(f"表格列数不一致(第 {bs + 1} 行起):pipe 数 = {'/'.join(map(str, counts))}"
|
||||
f"|首行内容: {line_snip(blk[0])}")
|
||||
else:
|
||||
ti += 1
|
||||
|
||||
# ------------------------------------------------ 通用 3b:表格不得缩进
|
||||
indented = [i + 1 for i, line in enumerate(lines) if re.match(r'^[ \t]+\|', line)]
|
||||
if not indented:
|
||||
pass_('表格均顶格(无嵌套缩进表)')
|
||||
else:
|
||||
fail('表格存在缩进(第 ' + ', '.join(map(str, indented)) + ' 行起)——嵌套在列表里的表多数渲染器不显示,必须顶格')
|
||||
|
||||
# ------------------------------------------------ 通用 4:章节完整性
|
||||
if args.template:
|
||||
_, t_lines = read_text(args.template)
|
||||
t_heads = [l for l in t_lines if re.match(r'^\s*#+\s+\S', l)]
|
||||
missing_literal, missing_ph = [], []
|
||||
for h in t_heads:
|
||||
t = re.sub(r'\s+$', '', re.sub(r'^\s*#+\s+', '', h))
|
||||
has_ph = PH_L in t
|
||||
ph_rx = re.escape(PH_L) + '[^' + re.escape(PH_R) + ']*' + re.escape(PH_R)
|
||||
rx = re.escape(re.sub(ph_rx, '@@PH@@', t)).replace('@@PH@@', '.*')
|
||||
hit = any(re.match(r'^\s*#+\s+' + rx + r'\s*$', pl) for pl in lines)
|
||||
if not hit:
|
||||
(missing_ph if has_ph else missing_literal).append(t)
|
||||
if not missing_literal:
|
||||
pass_('章节完整性:模板必备节全部存在')
|
||||
else:
|
||||
fail('缺章节(字面量,必错): ' + ' | '.join(missing_literal))
|
||||
if missing_ph:
|
||||
warn('示例性章节未匹配(可能条数不同,需人判): ' + ' | '.join(missing_ph))
|
||||
|
||||
# ------------------------------------------------ scan 专属
|
||||
if args.mode == 'scan':
|
||||
non_mermaid = [f for f in fences if f['lang'] != 'mermaid']
|
||||
if not non_mermaid:
|
||||
pass_('深度闸门:S1 无围栏代码块(仅 mermaid)')
|
||||
else:
|
||||
fail('S1 不允许围栏代码块,发现 {} 个(第 {} 行起)'.format(
|
||||
len(non_mermaid),
|
||||
', '.join(str(f['start'] + 1) for f in non_mermaid)))
|
||||
|
||||
bad_anchor, anchor_count = [], 0
|
||||
for line in lines:
|
||||
m = re.match(r'^\s*\*\*锚点\*\*\s*[::]\s*(.+?)\s*$', line)
|
||||
if not m:
|
||||
continue
|
||||
anchor_count += 1
|
||||
items = [s for s in re.split(r'[、,,]', m.group(1)) if s.strip()]
|
||||
if len(items) > 2:
|
||||
bad_anchor.append(f'锚点超过 2 个({len(items)} 个): {line.strip()}')
|
||||
for it in items:
|
||||
a = it.strip().strip('`')
|
||||
if re.search(r':\d', a):
|
||||
bad_anchor.append(f'S1 锚点不写行号 -> {a}')
|
||||
elif not re.search(r'\w[/\\]\w', a):
|
||||
bad_anchor.append(f'S1 锚点须为 路径#方法名(机制级可到类名) -> {a}')
|
||||
if anchor_count == 0:
|
||||
warn('未找到 **锚点** 行(若产物为空则忽略)')
|
||||
elif not bad_anchor:
|
||||
pass_(f'锚点格式合规({anchor_count} 处,路径#方法名)')
|
||||
else:
|
||||
fail('锚点格式不合规: ' + ' ; '.join(bad_anchor))
|
||||
|
||||
if head_index(lines, r'^\s*#+\s*闸门状态') >= 0:
|
||||
pass_('有「闸门状态」节(S2 门控有证据)')
|
||||
else:
|
||||
fail('缺「闸门状态」节 —— S2 门控没有证据')
|
||||
|
||||
star_count = sum(1 for l in lines if re.match(r'^\s*#+\s*★', l))
|
||||
if star_count <= 12:
|
||||
pass_(f'候选条数 {star_count}(≤12)')
|
||||
else:
|
||||
fail(f'候选条数 {star_count} 超过 12(颗粒度掉到实现层,需重并)')
|
||||
if 0 < star_count < 5:
|
||||
warn(f'候选条数 {star_count} 少于 5——先确认是否采样不到位,而非真没有')
|
||||
|
||||
# ------------------------------------------------ dig 专属
|
||||
if args.mode == 'dig':
|
||||
skel_idx = head_index(lines, r'^\s*#+\s*主干调用骨架')
|
||||
skel_end = next_head_index(lines, skel_idx) if skel_idx >= 0 else -1
|
||||
if skel_idx >= 0:
|
||||
pass_('有「主干调用骨架」节')
|
||||
else:
|
||||
warn('未找到「主干调用骨架」节(仅 S4 ① 功能点清单必需)')
|
||||
|
||||
exempt = 0
|
||||
for f in fences:
|
||||
if f['lang'] == 'mermaid':
|
||||
continue
|
||||
if skel_idx >= 0 and skel_idx < f['start'] < skel_end:
|
||||
exempt += 1
|
||||
continue
|
||||
if f['body'] > 10:
|
||||
warn(f"局部代码块超过 10 行(第 {f['start'] + 1} 行起,{f['body']} 行)"
|
||||
f"——行数非硬限,请人判断是关键片段还是源码摘录")
|
||||
pass_(f'局部代码块行数检查完成(豁免骨架块 {exempt} 个)')
|
||||
|
||||
if skel_idx >= 0:
|
||||
skel_fences = [f for f in fences if skel_idx < f['start'] < skel_end]
|
||||
if not skel_fences:
|
||||
warn('骨架节里没有代码块')
|
||||
else:
|
||||
sf = skel_fences[0]
|
||||
miss_tag = []
|
||||
for bl in lines[sf['start'] + 1:sf['end']]:
|
||||
if not bl.strip():
|
||||
continue
|
||||
if re.match(r'^\s*//', bl):
|
||||
continue
|
||||
if re.search(r':\s*$', bl):
|
||||
continue
|
||||
if not re.search(r'[(\[]', bl):
|
||||
continue
|
||||
if '//' not in bl:
|
||||
miss_tag.append(bl.strip())
|
||||
if not miss_tag:
|
||||
pass_('骨架每行都带方式标记')
|
||||
else:
|
||||
fail(f"骨架有 {len(miss_tag)} 行缺方式标记: "
|
||||
+ ' ; '.join(miss_tag[:3]))
|
||||
if not re.search(r'\.(java|xml|yaml|yml|sql):\d+', text):
|
||||
fail('未找到任何 `文件:行号` 证据(本阶段必须有可核对的锚点)')
|
||||
else:
|
||||
pass_('存在 `文件:行号` 形式的证据')
|
||||
|
||||
# ------------------------------------------------ doc 专属
|
||||
if args.mode == 'doc':
|
||||
is_plan = head_index(lines, r'^\s*#+\s*0\.\s*现状问题登记') >= 0
|
||||
if is_plan:
|
||||
code_fences = [f for f in fences if f['lang'] != 'mermaid']
|
||||
if len(code_fences) >= 1:
|
||||
pass_(f'改造方案含关键片段 {len(code_fences)} 个')
|
||||
else:
|
||||
fail('改造方案缺「关键片段」代码块——必须"照着能讲代码"(伪代码/SQL 均可,只禁大段源码摘录)')
|
||||
if len(code_fences) >= 2:
|
||||
pass_(f"关键片段 {len(code_fences)} 个(骨架 + 改造点)")
|
||||
elif len(code_fences) == 1:
|
||||
warn('只有 1 个关键片段——可能只有链路骨架,各改造点未给伪代码,需人判')
|
||||
|
||||
# ------------------------------------------------ 输出
|
||||
print()
|
||||
print('=' * 68)
|
||||
print(f'机械校验 Mode={args.mode}')
|
||||
print(f' product : {prod_path}')
|
||||
if args.template:
|
||||
print(f" template : {Path(args.template).resolve()}")
|
||||
print('=' * 68)
|
||||
|
||||
for tag, bucket, mark in (('[PASS]', PASS, '+ '), ('[WARN] 需人判断', WARN, '! '), ('[FAIL] 必错', FAIL, 'x ')):
|
||||
if bucket:
|
||||
print()
|
||||
print(tag)
|
||||
for m in bucket:
|
||||
print(f' {mark}{m}')
|
||||
|
||||
print()
|
||||
print(f'结果:PASS {len(PASS)} / WARN {len(WARN)} / FAIL {len(FAIL)}')
|
||||
sys.exit(1 if FAIL else 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,146 @@
|
||||
# ⟨域⟩ 域 · 候选价值点(S1 产物)
|
||||
|
||||
> **阶段**:S1 枚举(**只产出亮点**;缺陷不在此阶段——它是深入链路时自然浮现的副产物)
|
||||
> **域**:`⟨模块名⟩`(走 S0 ⟨快路 / 慢路⟩:⟨域清单来源⟩)
|
||||
> **结构**:**域 → 链路 → 链路上的机制节点** 三层
|
||||
> **筛选标准(三条,全过才进)**:**① 是否核心链路 ② 是否符合高级工程师的设计 ③ 写到简历上够不够硬**
|
||||
> **深度闸门**:允许「**这个点在讲什么**」的业务语言说明;**禁止围栏代码块(行内反引号可用)、禁止逐层拆解、禁止取舍复盘**(那些属深度阶段)
|
||||
> **取材**:① 设计说明类文档 ② `git log` ③ 图查询 / 结构统计 ④ 关键字 grep
|
||||
|
||||
---
|
||||
|
||||
# 闸门状态
|
||||
|
||||
> **本节是 S2 门控的证据**——没有它,"停在第几段"无法核查。
|
||||
|
||||
| 段 | 状态 | 说明 |
|
||||
|:-:|---|---|
|
||||
| S0 定范围 | ✅ | ⟨范围声明一句话⟩ |
|
||||
| **S1 枚举** | ✅ 已完成 | 候选 ⟨N⟩ 条(**亮点**)+ 剔除 ⟨M⟩ 条 |
|
||||
| **S2 勾选** | ⏸ **待人工勾选** | **请从下面挑 3–6 条**;本文件到此为止,**未做深度产出** |
|
||||
|
||||
> **勾选/否决后回写本表(保持 3 列不压列)**,两种回写形状:
|
||||
>
|
||||
> ```
|
||||
> | **S2 勾选** | ✅ 已勾选 | ⟨勾了哪些(★N…)/ 谁日期⟩ → 产出《⟨产物名⟩》 |
|
||||
> | **S2 勾选** | ↩️ 被否决改向 | 否决原因:⟨用户原话摘要⟩ → 改挖 ⟨新目标⟩(入口 A′) |
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
## 一、链路总览
|
||||
|
||||
**一句话**:⟨统领句——不是复述流程,而是给出这条链路的**主线判断**。例:"这条链路的每一段都在把外部系统给的不确定,收敛成我方的确定状态"⟩
|
||||
|
||||
**读法**:⟨一条端到端链路 = …→…→…;下面每个机制都挂在这张图的某个位置上⟩
|
||||
**图例**:`★` = 入选的机制节点;**未标 ★ 的链路仍属本链路的一环**(见第二、三节),只是未列为独立机制。
|
||||
|
||||
### 端到端链路图(★ = 入选的机制节点)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph PA["链路 A · ⟨链路名⟩"]
|
||||
A1["⟨起点⟩"] --> A2["⟨机制节点⟩ ★1"]
|
||||
A3["⟨兜底 / 分支⟩ ★2"]
|
||||
end
|
||||
subgraph PB["链路 B · ⟨链路名⟩"]
|
||||
B1["⟨起点⟩"] --> B2["⟨机制节点⟩ ★3"]
|
||||
B2 --> B3["⟨机制节点⟩ ★4"]
|
||||
end
|
||||
subgraph PC["链路 C · ⟨链路名⟩"]
|
||||
C1["⟨起点⟩"] --> C2["⟨终点⟩"]
|
||||
end
|
||||
A2 --> B1
|
||||
A3 -.-> A2
|
||||
B3 --> C1
|
||||
C2 -.-> A2
|
||||
```
|
||||
|
||||
⟨可选⟩**关键差异表**(若这条链路的核心是"几种结果后果完全不同",用一张表压住)
|
||||
|
||||
| ⟨维度⟩ | ⟨取值1⟩ | ⟨取值2⟩ | ⟨取值3⟩ |
|
||||
|---|---|---|---|
|
||||
| ⟨例:对方结果⟩ | | | |
|
||||
| ⟨例:可逆性⟩ | | | |
|
||||
| ⟨例:处置⟩ | | | |
|
||||
|
||||
---
|
||||
|
||||
## 二、链路上的机制节点(⟨N⟩ 个 ★)
|
||||
|
||||
> **按链路分组**。每点 4 段,**段落式**(不要压成表格——"做了什么"常是 5–7 条,塞进单元格必然被简化)。
|
||||
|
||||
### 链路 ⟨A⟩ · ⟨链路名⟩
|
||||
|
||||
## ★1 · ⟨机制名⟩
|
||||
|
||||
**核心内容**:⟨1 句:这个机制是什么,把它的"形状"说清(一把锁 + 三级短路 + 一条条件更新)⟩
|
||||
|
||||
**这个点在讲什么**
|
||||
|
||||
- **业务场景**:⟨谁在什么时刻触发了什么,为什么这事难;点出"同一时刻还有谁在改同一行数据"⟩
|
||||
- **做了什么**(⟨用一句话概括做法⟩):
|
||||
1. ⟨…⟩;
|
||||
2. ⟨…⟩;
|
||||
3. ⟨…⟩。
|
||||
- **解决了什么问题**:⟨不做会怎样⟩
|
||||
|
||||
**一句话价值**:⟨1 句,可讲述;说清"判断权 / 控制权"落在谁手里⟩
|
||||
|
||||
**锚点**:`⟨简写⟩/⟨路径⟩#⟨方法名⟩`
|
||||
|
||||
---
|
||||
|
||||
## ★2 · ⟨机制名⟩
|
||||
|
||||
⟨同 ★1 的 4 段结构⟩
|
||||
|
||||
---
|
||||
|
||||
### 链路 ⟨B⟩ · ⟨链路名⟩
|
||||
|
||||
## ★3 · ⟨机制名⟩
|
||||
|
||||
⟨同 ★1 的 4 段结构⟩
|
||||
|
||||
---
|
||||
|
||||
### 链路 ⟨C⟩ · ⟨链路名⟩(⟨依附型,未列为独立机制 / 未入选⟩)
|
||||
|
||||
> **本链路 0 个独立机制,但说明不可省**——否则图上有个框、没人知道里面在干嘛。
|
||||
|
||||
**核心内容**:⟨它是什么⟩
|
||||
|
||||
**这个点在讲什么**
|
||||
|
||||
- **业务场景**:⟨…⟩
|
||||
- **做了什么**:⟨…⟩
|
||||
- **依附关系**:⟨它的哪几个设计决策其实是 ★N 在另一个方向 / 另一个域上的复用;判不清就按独立节点处理,不要为了凑数强行依附⟩
|
||||
- **为什么未入选 / 为什么依附**:⟨★ 必须写清⟩
|
||||
|
||||
**锚点**:`⟨…⟩`
|
||||
|
||||
---
|
||||
|
||||
## 三、已剔除(附理由,供复核筛选口径)
|
||||
|
||||
> **被剔除的点不丢弃**——注明**未过哪条标准**。
|
||||
|
||||
**「未过的标准」建议取值**:`①核心链路` · `②非设计决策(框架常识 / 通用工程质量)` · `③简历不硬` · `机制格填不出` · `示例数据 / 字典表 / 配置装配`
|
||||
|
||||
| # | 被剔除的点 | 未过的标准 | 理由 |
|
||||
|:-:|---|---|---|
|
||||
| 1 | ⟨例:key 命名约定⟩ | `①核心链路` | 不落在业务闭环上 |
|
||||
| 2 | ⟨例:Redis `GETDEL` 用法⟩ | `②非设计决策` | 框架常识用法,非设计决策 |
|
||||
| 3 | ⟨例:契约模式 / CI / Checkstyle⟩ | `②非设计决策` | 通用工程质量,无设计含量 |
|
||||
| 4 | ⟨例:表 CRUD / 单表封装⟩ | `机制格填不出` | 弱候选,并入本表待复核 |
|
||||
|
||||
---
|
||||
|
||||
## 四、待确认 / 覆盖缺口
|
||||
|
||||
| # | 项 | 说明 |
|
||||
|:-:|---|---|
|
||||
| 1 | **⟨链路名⟩ 0 个节点** | ⟨先问"真没亮点,还是采样不到位"(回看该链路的触发者与写类接口);判不清就写明理由⟩ |
|
||||
| 2 | `⟨路径⟩#⟨方法名⟩` | 【待确认】⟨拿不准的锚点必须标出,禁止猜⟩ |
|
||||
| 3 | **文档 vs 代码分叉** | ⟨设计文档写的方案在代码里是否落地?不落地本身就是高价值点⟩ |
|
||||
Reference in New Issue
Block a user