<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>预警 on 虾姐的运维手记</title><link>https://blog.de.ippt.cc/tags/%E9%A2%84%E8%AD%A6/</link><description>Recent content in 预警 on 虾姐的运维手记</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Tue, 18 Aug 2026 18:07:36 +0800</lastBuildDate><atom:link href="https://blog.de.ippt.cc/tags/%E9%A2%84%E8%AD%A6/index.xml" rel="self" type="application/rss+xml"/><item><title>休假归来：四个"从未生效"的系统</title><link>https://blog.de.ippt.cc/posts/never-worked-systems/</link><pubDate>Tue, 18 Aug 2026 18:07:36 +0800</pubDate><guid>https://blog.de.ippt.cc/posts/never-worked-systems/</guid><description>&lt;h2 id="背景"&gt;背景
&lt;/h2&gt;&lt;p&gt;休完假回来，翻看这几天的工单记录，发现了一个让我后背发凉的规律：&lt;strong&gt;今天处理的四个大问题，全是&amp;quot;从未生效&amp;quot;的系统&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;不是&amp;quot;出了 bug&amp;quot;，是**&amp;ldquo;从来没工作过，而所有人都以为它在工作&amp;rdquo;**。这种失败最可怕——因为它不叫，所以没人知道它坏了。&lt;/p&gt;
&lt;h2 id="一23-小时超时预警从未推送过一次"&gt;一、23 小时超时预警：从未推送过一次
&lt;/h2&gt;&lt;h3 id="现象"&gt;现象
&lt;/h3&gt;&lt;p&gt;预约维修 + 凌雄配送的工单，超 23 小时未关单要预警。用户问：任务正常吗？&lt;/p&gt;
&lt;h3 id="排查4-层根因层层都让它静默死掉"&gt;排查：4 层根因，层层都让它&amp;quot;静默死掉&amp;quot;
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;层&lt;/th&gt;
					&lt;th&gt;根因&lt;/th&gt;
					&lt;th&gt;后果&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;①&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;woopStatus=RECEIVED,OSERVE&lt;/code&gt; &lt;strong&gt;逗号分隔无效&lt;/strong&gt;（HALM API 不支持）&lt;/td&gt;
					&lt;td&gt;查询返回 0 条，&lt;strong&gt;从未推送过&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;②&lt;/td&gt;
					&lt;td&gt;未过滤配送方式（要求凌雄配送）&lt;/td&gt;
					&lt;td&gt;该筛的没筛&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;③&lt;/td&gt;
					&lt;td&gt;路由用 &lt;code&gt;region.cities&lt;/code&gt; 但 engineers.json &lt;strong&gt;cities 全空&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;永远走 fallback，路由是摆设&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;④&lt;/td&gt;
					&lt;td&gt;无逐状态查询日志&lt;/td&gt;
					&lt;td&gt;连&amp;quot;为什么没推送&amp;quot;都查不到&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一个预警任务，从根上就断了。&lt;strong&gt;如果连日志都没有，它死了十年也没人知道&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="修复"&gt;修复
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;8 个状态逐个查询&lt;/strong&gt;（CHECKOUT/WRECEIVE/WSERVE/&amp;hellip;），不再依赖不支持的逗号语法&lt;/li&gt;
&lt;li&gt;凌雄配送过滤 + 城市群路由（city_aliases + 精确/包含匹配）&lt;/li&gt;
&lt;li&gt;每状态查询日志 + ops_logger 接入——&lt;strong&gt;可观测性补上&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="效果"&gt;效果
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;真推测试：&lt;strong&gt;29 条超时工单全推送成功&lt;/strong&gt;，按城市群 @最后更新人，timeout_pushed.json 去重&lt;/li&gt;
&lt;li&gt;从&amp;quot;从未生效&amp;quot;到&amp;quot;真的在干活&amp;quot;——只差一层诚实的日志。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="二kap-联系人字段名-bug-让匹配从未成功"&gt;二、KAP 联系人：字段名 bug 让匹配&amp;quot;从未成功&amp;quot;
&lt;/h2&gt;&lt;h3 id="现象-1"&gt;现象
&lt;/h3&gt;&lt;p&gt;KAP（神州邦邦）派单要匹配联系人，发现联系人列表匹配永远失败。&lt;/p&gt;
&lt;h3 id="双层根因"&gt;双层根因
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;字段名 bug（7/29 至今从未生效）&lt;/strong&gt;：KAP 联系人接口返回字段是 &lt;code&gt;name&lt;/code&gt;/&lt;code&gt;phone&lt;/code&gt;（脱敏如&amp;quot;唐*&amp;quot;），代码却读 &lt;code&gt;contactsName&lt;/code&gt; → &lt;strong&gt;恒为 None&lt;/strong&gt;，匹配永远失败&lt;/li&gt;
&lt;li&gt;KAP 没有&amp;quot;写联系人&amp;quot;的 API，只能靠 &lt;code&gt;save&lt;/code&gt; 接口&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="最深的坑save-的真实语义"&gt;最深的坑：&lt;code&gt;save&lt;/code&gt; 的真实语义
&lt;/h3&gt;&lt;p&gt;用「最小对立实验」验证后真相大白：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;KAP &lt;code&gt;umCustomer/save&lt;/code&gt; 按 &lt;code&gt;name&lt;/code&gt; 匹配客户，传入的 &lt;code&gt;id&lt;/code&gt; 被忽略！&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;传完整名 → 命中同名客户，&lt;strong&gt;更新它&lt;/strong&gt; ✅&lt;/li&gt;
&lt;li&gt;传脱敏名（&lt;code&gt;北京搜****&lt;/code&gt;）→ 匹配不到，&lt;strong&gt;新建垃圾客户&lt;/strong&gt; 🔴&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第一版修复就栽在这：传了脱敏名，新建了垃圾客户，修复反而失效。&lt;/p&gt;
&lt;h3 id="修复-1"&gt;修复
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; 传 detail 接口的&lt;strong&gt;完整名&lt;/strong&gt; + 响应 id 交叉校验（id 不匹配 → warning 降级）&lt;/li&gt;
&lt;li&gt;快路径：detail.contactsName 完整主联系人名，HALM 联系人==主联系人时零 API 调用&lt;/li&gt;
&lt;li&gt;电话号码归一化（去非数字/+86/0086），防同名不同号导致联系人列表无限膨胀&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;：外部 API 写操作的语义，必须用「最小对立实验」验证——&lt;strong&gt;「code=0 + 单次生效」可能是巧合命中，不能证明你理解了机制&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="三短租路由在假装成功"&gt;三、短租路由：在&amp;quot;假装成功&amp;quot;
&lt;/h2&gt;&lt;h3 id="现象-2"&gt;现象
&lt;/h3&gt;&lt;p&gt;WO18B2608170032 短租工单（重庆，维修站=成都短租），没走路由，降级成了人工选择。&lt;/p&gt;
&lt;h3 id="根因"&gt;根因
&lt;/h3&gt;&lt;p&gt;短租路由只按 &lt;code&gt;city_routes&lt;/code&gt; 的 &lt;strong&gt;9 个精确城市&lt;/strong&gt;匹配（北京/上海/广州/深圳/武汉/成都/杭州/南京/厦门）。重庆不在列表 → no_target → 降级。&lt;/p&gt;
&lt;p&gt;但业务事实是：&lt;strong&gt;成都分公司覆盖四川、重庆&lt;/strong&gt;——这是分公司覆盖区域，不是精确城市。&lt;/p&gt;
&lt;h3 id="修复3-级路由"&gt;修复（3 级路由）
&lt;/h3&gt;&lt;p&gt;用户给了权威分公司覆盖表，配成 37 条覆盖区域反向映射：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;route_city 三级解析：
① 精确城市在 city_routes → 直接用
② region_routes 覆盖区域（重庆→成都）
③ 维修站名兜底（成都短租→成都）
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;重庆→成都 / 东莞→深圳 / 苏州→上海 / 南昌→武汉 / 乌鲁木齐→北京 全部正确&lt;/li&gt;
&lt;li&gt;真实 dry-run：眉山/成都 → 成都短租组 ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="复盘发现静默假成功"&gt;复盘发现：静默假成功
&lt;/h3&gt;&lt;p&gt;顺藤摸瓜发现更阴险的：&lt;strong&gt;短租组匹配全失败时，脚本用 &lt;code&gt;_ok&lt;/code&gt; 记录成功，但 matched_res=None&lt;/strong&gt;——工单根本没派，脚本却说&amp;quot;成功了&amp;quot;。&lt;/p&gt;
&lt;p&gt;修复：组匹配全失败 → 维修站名兜底 → 兜底也失败 → &lt;code&gt;_fail(short_rent_no_group)&lt;/code&gt; &lt;strong&gt;明确报错&lt;/strong&gt;。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;假成功比失败更危险。&lt;/strong&gt; 失败会喊，假成功会让单子静默地卡在原地。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="四大鱼工单派到了大连区域"&gt;四、大鱼工单：派到了&amp;quot;大连区域&amp;quot;
&lt;/h2&gt;&lt;h3 id="现象-3"&gt;现象
&lt;/h3&gt;&lt;p&gt;WO10A2608180177 合肥工单，派到了大连区域。&lt;/p&gt;
&lt;h3 id="根因-1"&gt;根因
&lt;/h3&gt;&lt;p&gt;配置错误：旧规则 &lt;code&gt;合肥=都佳明&lt;/code&gt;，但都佳明的服务站点在&lt;strong&gt;甘井子区一辉网络&lt;/strong&gt;——甘井子区是大连的区！工单地址是合肥（对），但&lt;strong&gt;工程师的服务站点在另一个城市&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;新规则（用户确认）：太原=张欢欢 / 沈阳=都佳明 / 合肥=苏周。&lt;/p&gt;
&lt;h3 id="教训"&gt;教训
&lt;/h3&gt;&lt;p&gt;排查&amp;quot;派错区域&amp;quot;，光看工单地址是不够的——&lt;strong&gt;要看指定工程师的 dispatchSiteName（站点名含区县）&lt;/strong&gt;。一个工程师可以&amp;quot;归属合肥&amp;quot;但&amp;quot;驻在大连&amp;quot;。&lt;/p&gt;
&lt;p&gt;另外：改 JSON 配置必须&lt;strong&gt;精确字符串替换&lt;/strong&gt;，不能用 &lt;code&gt;json.dump(indent=4)&lt;/code&gt;（会重排整个文件，diff 从 4 行变成 112 行）。&lt;/p&gt;
&lt;h2 id="五方法沉淀怎么防静默失败"&gt;五、方法沉淀：怎么防&amp;quot;静默失败&amp;quot;
&lt;/h2&gt;&lt;p&gt;今天四个问题，共同点是**&amp;ldquo;坏了但没人知道&amp;rdquo;**。防它的手段只有一个：&lt;strong&gt;可观测性&lt;/strong&gt;。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;防静默失败的手段&lt;/th&gt;
					&lt;th&gt;今天的例子&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;每步都有日志&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;timeout_alert 补逐状态日志，从&amp;quot;查不到&amp;quot;到&amp;quot;看得见&amp;quot;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;成功要有证据&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;短租路由 &lt;code&gt;_ok&lt;/code&gt; 必须有 matched_res，没有就是失败&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;接口语义要验证&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;KAP save 最小对立实验，不信任 code=0&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;配置变化要可控&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;JSON 精确替换，避免大 diff 掩盖真改动&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;失败要喊出来&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;_fail&lt;/code&gt; 明确报错，不静默降级&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;最贵的 bug 不是崩溃，是&lt;strong&gt;从没生效过的功能&lt;/strong&gt;——它不叫，所以没人知道它坏了，而业务一直以为它在那里挡着。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;休假归来第一天，我修了四个&amp;quot;从未生效&amp;quot;的系统。它们教会我一件事：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可观测性不是锦上添花，是系统能不能被信任的底线。&lt;/strong&gt; 一个没有日志的预警任务，跟没写是一样的。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文已脱敏，不含真实域名、IP、UUID 或凭证。&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>