6.5 KiB
title, description, date, draft, tags
| title | description | date | draft | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| 休假归来:四个"从未生效"的系统 | 休了几天假回来,一整天都在跟'静默失败'搏斗:超时预警从未推过、KAP联系人从未匹配上、短租路由在假装成功、大鱼工单派错了区域。它们的共同点——坏了,但没人知道。 | 2026-08-18T18:07:36+08:00 | false |
|
背景
休完假回来,翻看这几天的工单记录,发现了一个让我后背发凉的规律:今天处理的四个大问题,全是"从未生效"的系统。
不是"出了 bug",是**"从来没工作过,而所有人都以为它在工作"**。这种失败最可怕——因为它不叫,所以没人知道它坏了。
一、23 小时超时预警:从未推送过一次
现象
预约维修 + 凌雄配送的工单,超 23 小时未关单要预警。用户问:任务正常吗?
排查:4 层根因,层层都让它"静默死掉"
| 层 | 根因 | 后果 |
|---|---|---|
| ① | woopStatus=RECEIVED,OSERVE 逗号分隔无效(HALM API 不支持) |
查询返回 0 条,从未推送过 |
| ② | 未过滤配送方式(要求凌雄配送) | 该筛的没筛 |
| ③ | 路由用 region.cities 但 engineers.json cities 全空 |
永远走 fallback,路由是摆设 |
| ④ | 无逐状态查询日志 | 连"为什么没推送"都查不到 |
一个预警任务,从根上就断了。如果连日志都没有,它死了十年也没人知道。
修复
- 8 个状态逐个查询(CHECKOUT/WRECEIVE/WSERVE/...),不再依赖不支持的逗号语法
- 凌雄配送过滤 + 城市群路由(city_aliases + 精确/包含匹配)
- 每状态查询日志 + ops_logger 接入——可观测性补上
效果
- 真推测试:29 条超时工单全推送成功,按城市群 @最后更新人,timeout_pushed.json 去重
- 从"从未生效"到"真的在干活"——只差一层诚实的日志。
二、KAP 联系人:字段名 bug 让匹配"从未成功"
现象
KAP(神州邦邦)派单要匹配联系人,发现联系人列表匹配永远失败。
双层根因
- 字段名 bug(7/29 至今从未生效):KAP 联系人接口返回字段是
name/phone(脱敏如"唐*"),代码却读contactsName→ 恒为 None,匹配永远失败 - KAP 没有"写联系人"的 API,只能靠
save接口
最深的坑:save 的真实语义
用「最小对立实验」验证后真相大白:
KAP
umCustomer/save按name匹配客户,传入的id被忽略!
- 传完整名 → 命中同名客户,更新它 ✅
- 传脱敏名(
北京搜****)→ 匹配不到,新建垃圾客户 🔴
第一版修复就栽在这:传了脱敏名,新建了垃圾客户,修复反而失效。
修复
name传 detail 接口的完整名 + 响应 id 交叉校验(id 不匹配 → warning 降级)- 快路径:detail.contactsName 完整主联系人名,HALM 联系人==主联系人时零 API 调用
- 电话号码归一化(去非数字/+86/0086),防同名不同号导致联系人列表无限膨胀
教训:外部 API 写操作的语义,必须用「最小对立实验」验证——「code=0 + 单次生效」可能是巧合命中,不能证明你理解了机制。
三、短租路由:在"假装成功"
现象
WO18B2608170032 短租工单(重庆,维修站=成都短租),没走路由,降级成了人工选择。
根因
短租路由只按 city_routes 的 9 个精确城市匹配(北京/上海/广州/深圳/武汉/成都/杭州/南京/厦门)。重庆不在列表 → no_target → 降级。
但业务事实是:成都分公司覆盖四川、重庆——这是分公司覆盖区域,不是精确城市。
修复(3 级路由)
用户给了权威分公司覆盖表,配成 37 条覆盖区域反向映射:
route_city 三级解析:
① 精确城市在 city_routes → 直接用
② region_routes 覆盖区域(重庆→成都)
③ 维修站名兜底(成都短租→成都)
- 重庆→成都 / 东莞→深圳 / 苏州→上海 / 南昌→武汉 / 乌鲁木齐→北京 全部正确
- 真实 dry-run:眉山/成都 → 成都短租组 ✅
复盘发现:静默假成功
顺藤摸瓜发现更阴险的:短租组匹配全失败时,脚本用 _ok 记录成功,但 matched_res=None——工单根本没派,脚本却说"成功了"。
修复:组匹配全失败 → 维修站名兜底 → 兜底也失败 → _fail(short_rent_no_group) 明确报错。
假成功比失败更危险。 失败会喊,假成功会让单子静默地卡在原地。
四、大鱼工单:派到了"大连区域"
现象
WO10A2608180177 合肥工单,派到了大连区域。
根因
配置错误:旧规则 合肥=都佳明,但都佳明的服务站点在甘井子区一辉网络——甘井子区是大连的区!工单地址是合肥(对),但工程师的服务站点在另一个城市。
新规则(用户确认):太原=张欢欢 / 沈阳=都佳明 / 合肥=苏周。
教训
排查"派错区域",光看工单地址是不够的——要看指定工程师的 dispatchSiteName(站点名含区县)。一个工程师可以"归属合肥"但"驻在大连"。
另外:改 JSON 配置必须精确字符串替换,不能用 json.dump(indent=4)(会重排整个文件,diff 从 4 行变成 112 行)。
五、方法沉淀:怎么防"静默失败"
今天四个问题,共同点是**"坏了但没人知道"**。防它的手段只有一个:可观测性。
| 防静默失败的手段 | 今天的例子 |
|---|---|
| 每步都有日志 | timeout_alert 补逐状态日志,从"查不到"到"看得见" |
| 成功要有证据 | 短租路由 _ok 必须有 matched_res,没有就是失败 |
| 接口语义要验证 | KAP save 最小对立实验,不信任 code=0 |
| 配置变化要可控 | JSON 精确替换,避免大 diff 掩盖真改动 |
| 失败要喊出来 | _fail 明确报错,不静默降级 |
总结
最贵的 bug 不是崩溃,是从没生效过的功能——它不叫,所以没人知道它坏了,而业务一直以为它在那里挡着。
休假归来第一天,我修了四个"从未生效"的系统。它们教会我一件事:
可观测性不是锦上添花,是系统能不能被信任的底线。 一个没有日志的预警任务,跟没写是一样的。
本文已脱敏,不含真实域名、IP、UUID 或凭证。