FDE·案例复盘
基础目录
德莉玛 monorepo 订购单流水线 会员制 CRM 订购单系统(单体) 玻璃图纸与拆单 定价引擎 P0~P3

复盘 25 · 订单到报价流水线,与一次「停下来」

这一篇的灵魂不是技术,是决定。一条生产流水线建到一半,数据不够时我停了手——不是做不下去,是「算不了」是一个合法且专业的输出。宁可报「数据不足,算不了」,也不给一个看起来精确的错误价格。

项目锚点

项目:德莉玛淋浴房订购单生产流水线(订单 → BOM 拆单 → 玻璃图纸 → 报价)。
全量历史数据:33,176 单 / 387 个经销商 / 2,799 个型号。抽样体检 400 单,其中 97% 的 spec 为 null 或「否」(等于没有尺寸),只有 3 单有真实尺寸——而且这 3 单属于 K / Y 系列,不是 H / S / M 主流系列。

校验结果(实测)值说明
型号匹配96.6%引擎能认出型号
厚度匹配95.2%物理参数基本齐
尺寸覆盖29%🔴 真正卡点
客户 / 经销商匹配0%🔴 不知道给谁报

相关基础篇:模块 13 · 报价引擎、模块 11 · 数据迁移实战(这批历史数据怎么落地成真库)、模块 05 · 需求诊断(这条结论的最初来源)。架构部分见 复盘 24 · 德莉玛 monorepo。

01

四段流水线:每段吃进什么、吐出什么

口径更正(2026-10-03):本篇说「spec = 尺寸」,这是错的——把全库 spec 取值分布打出来看,装的是规格与颜色(否 / 无 / 黑色 / 黑金沙 / 预埋),没有一条是尺寸。真正装尺寸的是 room 列,但它是混字段(形状 + 尺寸 + 单值 + 163 条「以图片为准」),必须用正则认:含「数字×数字」的 8,956 单 = 27.0%。详见 28 篇第 09 节的口径更正。
本篇「97% 的 spec 是 null 或「否」」这个观察本身仍然成立(那是 400 单抽样口径),错的是把 spec 的语义当成尺寸。

先把流水线画成四段。复盘一条流水线,第一件事是写清每一段的输入、输出、以及「这一段能不能单独跑」。

第 1 段
订单解析
输入:一张门店录进来的订单(经销商、型号、数量、备注)。输出:归一化的「系列 + 型号 + 数量」三条结论。
这一段最依赖主数据:型号不在词表里,后面三段全部空转。
第 2 段
BOM 拆单
输入:归一化后的型号。输出:标准件清单(框料、玻璃、五金、配件)。
这一段可以用 BOM 模板兜底——即使没有精确价,也能算出「要哪些料、多少量」。
第 3 段
玻璃图纸
输入:BOM + 尺寸 + 开孔参数。输出:切割图、框线、开孔位置。
这一段缺尺寸就是白做——没有长宽,图出不来。
第 4 段
报价
输入:BOM + 面积 + 单价(出厂价红线)。输出:金额区间。
这一段是唯一需要「钱」的一段,也是三段前置里最容易缺的一种。
段输入输出缺数据时能不能兜底缺什么就废
1 解析订单原始记录系列 / 型号 / 数量能(型号命中率 96.6% 兜住了大部分)型号词表
2 BOM归一化型号标准件清单能(BOM 模板兜底)模板缺失
3 图纸BOM + 尺寸切割图 / 框线 / 开孔不能尺寸(spec)
4 报价BOM + 价金额区间不能出厂价红线 + 尺寸
这张表最关键的一列是最后两行:第 3、4 段没有任何兜底。一个字段空了,这段就出不了东西。而 400 单抽样里,空的就是这两个段要用的字段。
02

数据体检先看三个口径:有单 / 有字段 / 有价格

接手一个「数据很多」的历史系统,最容易被骗的地方是:有历史 ≠ 能用。我固定按三个口径查一遍,任何一栏是空的,后面所有算法都别写。

口径要回答的问题怎么查实测结果结论
① 有单历史上到底有多少真实订单、多少经销商、多少型号?全量扫 order 主表 + 关联表33,176 单 / 387 经销商 / 2,799 型号✅ 数量级够,够做校验
② 有字段这些订单里,我要用的关键字段是不是真的填了?抽样 400 单查 spec(规格/尺寸)非空率97% 为 null 或「否」;真实有尺寸的 3 单🔴 致命
③ 有价格型号 / 系列有没有对应的出厂价红线(PV/KW/K1)?按五个系列(H / H4 / K / M / S)逐系列查价格表只有 H / S / M 有价,K 系列靠 BOM 模板兜底🔴 部分缺
红线:三个口径必须全过,任何一栏空,流水线就停在那一栏前面。
• 只有① → 你能做报表,做不了引擎。
• 有①②没③ → 你能出图,报不了价。
• 有①③没② → 你能对账,出不了图。
而当时是②和③同时缺,所以能交付的东西比客户以为的少一大截。
03

97% 空字段第一次把我打醒

这句话我原文记在这:「别急着写算法,先做采集。」——这句话不是方法论,是被数字打出来的。

那 400 单抽样到底说明了什么

观察我原来以为真实含义
97% 的 spec 是 null 或「否」「门店录单习惯差,通知一下就好了」这不是习惯问题,是链路问题:从订单录入到生产,从来没有人要求过必填尺寸。没被要求过的字段,填不填全靠心情,100 单里能有 3 单是运气
真实有尺寸的 只有 3 单「整改一个月能到 80%」基数太小,不具统计意义。用它去验证「我的引擎准不准」,等于拿 3 个点验证一套算法
这 3 单属于 K / Y 系列——这条最扎心:有尺寸习惯的是冷门系列的人,主流 H / S / M 反而没样本。也就是说,我最需要验证引擎的那批数据,恰恰一份都摸不到

我犯的第一个错,是「补默认值」

// 我当时第一版的处理(错的)
const w = spec?.width ?? 800;      // 默认 800
const h = spec?.height ?? 2000;    // 默认 2000
const price = base * w * h;        // → 输出一个「看起来很精确」的金额
这段代码能跑、不报错、页面上还显示得出金额——但它输出的是错的,而且错得很体面。一线看到「系统算出 3,200 元」会直接当真,拿去跟客户报。一次假价,后面所有报价都要打问号,客户对系统的信任一次性归零。
这正是 AI 和新人最容易踩的坑:面对空字段,本能反应是「填个默认值让流程跑通」。而在这类场景里,默认值不是兜底,是伪造。

于是我把手停了,改成三件事

停手之后做什么为什么是这个
① 录单表单改造:把尺寸做成必填项 + 校验(宽高必须为正数、厚度假数直接拒)解决源头。不改造表单,引擎再好也只是更准地算出空
② 把「待补尺寸」做成一档状态,而不是「算个大概」让缺失可见,而不是被默认值盖住
③ 报价引擎只接「尺寸完整」的输入,其余直接走「数据不足」分支把能不能算的判断权还给数据,不交给猜
这次停手省下来的钱比省下的工期多得多。如果继续写引擎:17 天工期里至少一半花在「调出一个看起来合理的数字」上;而且交付后每一条报价都要人工复核,复核成本比人工报价还高。
04

四个匹配率怎么读:别被平均数骗了

体检结果报上去,第一反应往往是「型号 96.6%、厚度 95.2%,这不挺好?」——看错方向了。

指标数值正确读法容易被误读成
型号匹配96.6%引擎认得型号。但这只是第 1 段过关❌「数据没问题,就快做引擎了」
厚度匹配95.2%物理参数基本齐,属于白捡的❌「参数齐了就能算价」
尺寸覆盖29%真正卡点。第 3、4 段同时依赖它❌「平均一下还有 55%,凑合」
客户匹配0%订单 ↔ 经销商 ID 对不上,不知道给谁报❌「0% 才要紧,别的还能救」
记住这条算法(不是计算机术语,是交付常识):
一条链能跑通的比例,不是四个数的平均数,是四个数里的最小值。
96.6% 和 0% 平均是 48.3%——而实际上能算出价的单量是 0%。报「平均覆盖 48%」等于当场承诺了一个做不到的东西。
所以我在交付文档里一律按最小值报:尺寸 29%、客户 0%,这两个是硬数字,一个都不能往好看里凑。
「客户匹配 0%」的成因和尺寸还不一样,值得单说:尺寸是「填了没填」,属于采集层;客户匹配是「订单表和经销商表对不上号」,属于主数据层——订单里有经销商代码,经销商主表里查无此 ID。这类问题只能靠对照表补录,不能靠算法猜。
05

五个系列,只有三个有价

价格是所有数字里最不能编的一种。没有价,就只能说没有价。

系列价格情况没价时靠什么报价可信度
H✅ 配了价格直接用高
H4⚠️ 未配独立价只能引用 H 或按模板折算中低(必须标注「参考价」)
K❌ 无价格BOM 模板兜底(算出料,算不出钱)不能对外报
M✅ 配了价格直接用高
S✅ 配了价格直接用高
「BOM 模板兜底」这句话要拆开看,别被它骗:
BOM 模板回答的是「这个型号由哪些料组成、各要多少」——它是物料清单,不是价格清单。
能算出用料,绝不等于能算出钱。要出价还需要每一项料对应的单价。所以 K 系列的真实状态是「能拆单、能出图、报不了价」,这三件事必须如实分开讲。
这一个事实(而不是这一行数字)给我的方法论:一个产品线里部分系列有价、部分没有,是很常见的。正确的产品形态不是「全系列统一报价」,而是「按系列分档显示」——有价的显示金额区间,没价的显示「待补价」。比给一个凑合的价安全一百倍。
06

把「算不了」设计成产品功能(这篇最值钱的一节)

“算不了”听起来像一个 bug 状态。但在报价这类产品里,它必须是和「算得出」平起平坐的一等状态。不然一线就只能靠猜,或者干脆绕开系统。

状态触发条件(实测口径)页面上怎么显示下一步动作(要能点)
① 算得出
(绿)
型号命中 + 尺寸完整 + 该系列有价直接显示金额区间(下沿 / 上沿),并标注所用的价导出 / 复核 / 送审
② 算不出,要补数据
(黄)
型号命中,但 spec 为空 / 该系列无价 / 经销商 ID 对不上逐条列出缺哪几项:缺尺寸(第几单缺)、K 系列待补价、订单 38747 对应经销商缺失一键跳到采集表单,把缺的字段补齐后重算
③ 数据本身错
(红)
字段填了但矛盾:厚度与型号不匹配、尺寸为 0 或负数、面积与长宽不符指出矛盾点在哪(例:H4-T52 标准厚度 8mm,此处填 12mm)回到录单源头改,不改就永远算不准
状态 ① 的价值它能让「算得出」的单量是个可承诺的数字:按实测,就是尺寸覆盖 29% 且客户匹配成功的那部分——这个数字是多少我如实报多少,不放大。
状态 ② 的价值(最大)它把「数据治理」从一句口号变成一个每天能消掉的待办。以前要补尺寸靠发通知、开会、催;现在系统每天把缺项列表推出来,谁的单谁补。
状态 ③ 的价值它把「脏数据」挡在系统里,不让脏数据流进报价。红比黄更严重:黄只是缺,红是错错在先。
三条硬规矩(我自己贴在引擎旁边的):
① 空字段不许填默认值续命——?? 800 这类写法在报价链路里一律禁止。
② 「数据不足」和「算出来是 0 元」是两种完全不同的输出,不许合并。
③ 一个算不出来的原因,只允许报一种主因——报三个等于没报,一线不知道先补哪个。
为什么一定要把「算不了」做成功能?因为只要它不存在,一线就有三种选择:① 自己估一个(最坏)、② 不用系统(等于白做)、③ 回来找你(你的成本最高)。
把它做成一等公民,等于让系统自己说「我不知道」,而不是让人替它瞎说。
07

给干系人的演示页,为什么比系统 demo 更有用

我们已经有了三个接口,按理说直接演示系统就行。但我最后给干系人的是一张单文件 HTML:shower-cpq-demo.html。原因有三条,都是硬约束。

为什么不用系统 demo真实阻碍
干系人打不开系统系统跑在内网 / 需要登录态,演示那天临时开权限,风险高、体验差
系统里有真价,不能给外部看真价是核心商业数据。让干系人看真实价格表的截图,等于把底牌掀了
系统 demo 会带有噪音一进系统就是列表、筛选、分页,干系人看三分钟还在找按钮,根本来不及看「引擎是什么意思」
系统 demo单文件演示页
打开方式内网 + 登录 + 网络双击就开,断网也能开
数据真价(敏感)假数(只用来演示逻辑)
展示重点功能完整度引擎的三态逻辑
P0 / P1 分档混在一起单文件里按优先级分档,先看 P0 动起来
能带走吗不能能,一个文件发给谁都能看
单文件、无服务器、P0/P1 分档——这三个特征是有意为之,不是图省事。
• 单文件:干系人不会装环境,先验素质是「打开就能看懂」。
• 无服务器:没有后端就没有价格泄漏,也没有部署事故。
• P0/P1 分档:给不写代码的人看的东西,第一原则是让他先看到「结论能出」,不是功能全。

给不写代码的人看的产物,第一原则是「打开就能看懂」,不是「功能全」。这条我后来在别的项目里也一直用。
08

三个接口的反向验证,与已经产出的三类解法

接口建完之后,我做的不是「按预期跑一遍」,是拿真实脏数据往里砸,看它会不会说一套做一套。

接口反向验证做了什么结论
/api/bom拿 33,176 单全量跑一遍拆单,统计命中 / 走模板兜底 / 失败三档能出清单,但 K 系列只能出「料」不出「钱」——与第 05 节口径一致
/api/glass_dwg用3 份真实尺寸和一堆空尺寸分别打,看会不会出个「空图」假装成功空尺寸直接返回「缺尺寸」,不返回图——三态 ② 生效,这是关键一条
/api/quote拿真单去打,看返回的是金额还是「缺什么」缺尺寸 / 无价系列 / 经销商对不上时,返回结构里带缺项清单,而不是返回一个数字
反向验证和正向测试的区别:正向测试是「我给它一个正确输入,看它返回正确输出」;反向验证是「我给它一个脏输入,看它敢不敢拒绝」。
一个报价引擎的正向测试可以 100% 通过,同时整体是个废的——因为现实里 97% 的输入是脏的。所以我现在的习惯是:结论类接口必须有「拒绝态」的用例,而且是先写拒绝态的用例。

这轮已经落地的三类解法

解法解决什么为什么不用 AI 重画
shower-cpq-demo.html让干系人不开服务器就能看懂引擎逻辑它根本不是图,是逻辑演示,用不上 AI 出图
product-grid-redrawAI 生成的画面里门扇数画错了(四扇变六扇),只重画竖直框线AI 重画会把玻璃内容和背景一起改掉;程序化只重画框线,玻璃与背景一个像素都不动
product-frame-recolor效果图要出多色版本(金框改白框)换色必须结构 100% 不变,AI 改色等于重画一张,客户拿到的就不是同一个门了
后两条是同一条方法论的两个实例:当 AI 生成的东西在「结构」上出了错(扇数、框色、比例),修的方向不是让 AI 再生成一次,而是把错的那一层剥出来,用程序化方法只改那一层。因为重画一次,你就丢掉了原图的确定性;只改一层,你保住了它。
09

假精确,是这行最贵的交付

红线具体口径
不许用默认值把空字段填平报价链路里任何 ?? 默认值 都视为伪造。缺就是缺
覆盖率按最小值报四个匹配率里报最小的那个(29% / 0%),不报平均数
「算不了」是一个可交付状态交付文档里必须写明「在现有数据下,能自动报价的单量约等于 X」,X 必须是用实测跑出来的,不能是估的
演示页用假数给外部看的演示一律假数据;真价只存在于系统内部
我怎么跟客户讲这回事(这段话我反复用过):
「自动报价需要三个东西:出厂价红线、真实尺寸、订单和经销商的对应关系。目前尺寸覆盖率 29%,客户对应是 0%。所以我建议这一期先交付「数据补齐工具 + 报价引擎的三态提示」,不做全量自动报价。
硬做全量自动报价,我会给你一个系统,它每天输出一些看起来像金额的数字,然后你的业务员得重新复核一遍——那这个系统不如你们现在的 Excel。」
这段的说服力全在数字上。——「97% 是空的」「只有 3 单有尺寸,还是 K 系列」「客户匹配 0%」这三句一讲,任何人都明白为什么要停。一个有真数字的「我算不了」,比十个漂亮的功能更专业。
基础篇里:这条诊断的最初来源在 模块 05 · 需求诊断,这批数据落地成真库的过程在 模块 11 · 数据迁移实战,引擎细节在 模块 13 · 报价引擎。
✓

自查清单

  • ✓我能背出全量数字:33,176 单 / 387 个经销商 / 2,799 个型号,以及 400 单抽样里 97% 空字段、3 单有真实尺寸且属 K/Y 系列
  • ✓我讲得清三个口径(有单 / 有字段 / 有价格)分别查什么、实测结果分别是什么
  • ✓我知道第 1、2 段能兜底、第 3、4 段没有任何兜底,缺废一整段
  • ✓四个匹配率我按最小值(29% / 0%)报,不报平均数(48.3%)
  • ✓我承认自己写过 ?? 800 这种默认值兜底,并知道在报价链路里它等于伪造
  • ✓我讲得清三态(算得出 / 算不出要补数据 / 数据本身错)各自的触发条件、页面表现和下一步动作
  • ✓我知道「算不了」不是 bug 状态,是一等公民状态,不做它一线就只有「自己估 / 不用系统 / 回来找你」三条路
  • ✓我理解 shower-cpq-demo.html 为什么是单文件 + 无服务器 + P0/P1 分档,并知道给不写代码的人看的产物第一原则是「打开就能看懂」
  • ✓我分得清反向验证(给脏输入看它敢不敢拒绝)和正向测试(给正确输入看返回对不对)
  • ✓我明白 product-grid-redraw / product-frame-recolor 的共同点:结构出错时只改错的那层,不重画
  • ✓我能现场说出「BOM 模板兜底」不等于「能报出价」——模板出的是料,不是钱
案例复盘 26 会员制 CRM 与零售工具箱:83 个工具是怎么长出来的 →
面试怎么答

题目:你做过的最难的一个「不做什么」的决定是什么?

是我把一条生产流水线停下来的那个决定。当时客户要的是「自动报价」,我已经把订单→BOM→玻璃图纸→报价四段都设计好了,但体检数据不支持开工,所以我把手停了。

数据是这样的:全量历史 33,176 单、387 个经销商、2,799 个型号,看起来很丰足。但抽样 400 单查关键规格字段 spec,97% 是 null 或者「否」,真正有尺寸的一共 3 单——而且这 3 单属于 K / Y 系列,不是 H / S / M 主流系列。也就是说:我最需要拿去验证引擎的那批主流数据,一份都摸不到。

我犯的第一个错值得单独说:我第一版写了 const w = spec?.width ?? 800,用默认值把空字段填平,让流程跑通。这段代码能跑、不报错、页面上还显示得出金额——但它输出的是错的,而且错得很体面。一线看到「系统算出 3,200 元」会直接当真拿去报给客户。一次假价,客户对系统的信任就一次性归零了。默认值在这里不是兜底,是伪造。

所以我把手停了,改成先做采集:录单表单把尺寸改成必填、加校验;把「待补尺寸」做成一档状态而不是算个大概;报价引擎只接尺寸完整的输入。「别急着写算法,先做采集」——这句话不是方法论,是被 97% 这个数字打出来的。

体检还有一组数字我特别想讲,因为它是我 shepherd 客户预期用的:四个匹配率——型号匹配 96.6%、厚度 95.2%、尺寸覆盖 29%、客户匹配 0%。前两个很好看,但一条链能跑通的比例不是平均数,是四个数里的最小值。96.6 和 0 平均是 48.3%,而实际上能算出价的单量是 0%。报「平均覆盖 48%」等于当场承诺了一个做不到的东西。所以我在交付文档里一律按最小值报。

停手之后我做了三件事,其中两件已经变成产品能力:
① 把「算不了」做成一等公民。报价必须有三态:算得出(绿,显示金额区间并标注所用价)、算不出要补数据(黄,逐条列出缺哪几项,并能一键跳到采集表单补齐后重算)、数据本身错(红,指出矛盾点,比如 H4-T52 标准厚度 8mm 此处填了 12mm)。为什么必须这么做?因为只要「算不了」不是一个正式状态,一线就只有三条路:自己估一个(最坏)、不用系统(等于白做)、回来找你(成本最高)。把它做成一等公民,等于让系统自己说「我不知道」,而不是让人替它瞎说。
② 交付物换成了一张单文件 HTML:shower-cpq-demo.html,无服务器、假数据、按 P0/P1 分档。不用系统 demo 的原因很硬:干系人打不开内网、系统里有真价不能给外部看、系统 demo 一进去就是列表筛选分页,看他三分钟还在找按钮。给不写代码的人看的产物,第一原则是「打开就能看懂」,不是「功能全」。

技术侧我做了三接口的反向验证——注意是反向:不是「给正确输入看返回对不对」,而是拿真实脏数据往里砸,看它敢不敢拒绝。/api/glass_dwg 空尺寸必须直接返回「缺尺寸」而不返回一张空图;/api/quote 缺尺寸 / 无价系列 / 经销商对不上时,返回结构里带缺项清单,而不是返回一个数字。一个报价引擎的正向测试可以 100% 通过,同时整体是个废的——因为现实里 97% 的输入是脏的。所以我现在的习惯是结论类接口先写拒绝态的用例。

还有个方法论上的收获,后来一直在用:AI 生成的效果图在「结构」上出错时(比如门扇数四扇画成六扇、金框要改成白框),修的方向不是让 AI 再生成一次,而是把错的那层剥出来,用程序化方法只改那一层——product-grid-redraw 只重画竖直框线,玻璃和背景一个像素不动;product-frame-recolor 只改框色,结构 100% 不变。重画一次你就丢掉了原图的确定性,只改一层你保住了它。

最后我怎么跟客户讲:「自动报价要三个东西:出厂价红线、真实尺寸、订单和经销商的对应关系。目前尺寸覆盖 29%,客户对应 0%。所以我建议这一期先交数据补齐工具 + 报价引擎的三态提示。硬上全量自动报价,我会给你一个每天输出些像金额的数字、然后你的业务员还得重新复核一遍的系统——那还不如你们现在的 Excel。」
这段的说服力全在真数字上。「我算不了」配上三个实测数字,比十个漂亮的功能更专业。