发布于 更新于 约 8 分钟读完 · 3,046 字

本文目录 10 节
  1. 事实记录(不可修改区)
  2. 错在哪(设计事实)
  3. 结果分析
  4. 方法论沉淀
  5. ⚠️ 单因子分级会在最危险时段给出最低档(首次发现)
  6. ⚠️ 数据源的生命周期短于业务风险的生命周期(首次发现)
  7. ⚠️ 混合行尾会让多行锚点替换静默失败,而常用命令查不出来(首次发现)
  8. ⚠️ 自曝局限比被人发现更有利(首次发现)
  9. 顺带修掉的既有隐患
  10. 关联文档

风眼洪涝维度改造复盘:单因子分级会在最危险时段给出最低档

一句话:当分级依据只覆盖一条致灾路径,遇到走另一条路径的极端个例时,它不是”不够准”,而是给出反向信号——风眼按风力推档,美莎克减弱成热带低压后档位回落到四档最低的蓝色,而那正是伤亡最集中的 26 小时。

事实记录(不可修改区)

  • 项目:风眼 · Typhoon Eye(E:\typhoon-eye),台风实况与分级应急预案单页站。双发布面:GitHub Pages + B 站 Toy(slug YliUzbE5TOqySu4G / id 8980525008896)
  • 起因:2026-08-21 QQ 群里群友称”7 月初的美莎克台风在广西正面登陆”,并把”风眼上查不到实况数据”当作佐证之一转发。作者要求核查
  • 核查结论(外部事实):
    • 美莎克 = 2026 年第 10 号台风,国际编号 2610,强热带风暴级,全程未升格为台风级
    • 两次登陆均不在广西:7-03 18:20 前后登陆海南陵水黎族自治县椰林镇(23 m/s);7-04 20:30 前后二次登陆越南广宁省芒街市(10 级 / 28 m/s)。芒街紧贴中越边境,台风在越南一侧登陆后随即进入广西
    • 登陆后结构不散反而在陆上重建眼区,眼区经广西东兴入境,在广西内陆滞留 26 小时;南宁 24 小时最大雨量 713.3 mm
    • 伤亡通报:7-07 全区 6 人遇难 11 失联 / 37.5 万人受灾 → 7-09 全区累计 39 死 9 失联 → 8-21 新华社:南宁贵港两地 16 县 165 万人受灾,遇难 159 人、失联 10 人,其中六蓝、云表等水库溃坝洪灾遇难 107 人、失联 7 人
    • 官方定性:弱级强灾
  • “风眼查不到美莎克”的真实原因:本仓库首次提交 2026-07-10 08:05,首份 data/typhoon.json 落盘 08:38,而美莎克 7-07 停编——差三天,它从未进入过这个系统。且风眼从设计上只渲染在编台风,海神/红霞/鲸鱼/浪卡同样查不到轨迹。与数据可得性无关(官方路径系统公开可查)
  • 代码缺陷(改版前 assets/app.js suggestLevel,共 7 行):if (!t || !t.nearCoast) return "blue" → w>=14 orange → w>=10 yellow → blue。唯一输入是 windLevel。代入美莎克:登陆时 10 级→黄;减弱为热带低压后 <10 级→蓝(四档最低);7-07 停编后 typhoons 为空→页面显示”风平浪静”
  • 改造(v0.8,commit dd831ce):
    • scripts/fetch-typhoon.mjs 接入第四个源中央气象台预警发布平台(www.nmc.cn/rest/findAlarm),抓暴雨/台风/山洪灾害/山洪风险/地质灾害五类在效预警;alertid 前 6 位即行政区划代码,免抓详情页即可聚合到省
    • 关联规则(台风↔省份,不涉用户位置):该省已发台风预警(多台风并存按最近者归属,上限 1500 km)∪ 省中心距实况点或预报路径 ≤450 km
    • 档位改为 max(风力档, 洪涝档);洪涝档更高时页面显式标注”台风等级只反映风速,不代表致灾程度”
    • 停编后保留最长 7 天影响持续期,其间影响省份仍有洪涝预警在效则维持风险姿态,不回落”风平浪静”
    • 预警抓取设 60 s 总预算且可降级为 ok:false;两个数据工作流的校验步骤同步覆盖新字段
  • 实测验证(2026-08-21 当日真实数据):北部湾 0021 号热带低压(未命名,7 级)——旧逻辑=蓝色;海南(101 km)/广西(301 km)/广东(365 km)三省均挂暴雨橙色预警 → 新逻辑=橙色,抬升两档
  • 上线:Pages 已部署并核验;手动触发数据工作流跑通,CI 新校验输出 validated 2 active typhoon(s), 164 alert(s), 0 in aftermath;B 站 Toy 提交审核 status: auditing,后由作者确认发布完成;复盘专栏发到 Toy 开发者小站「晒晒 Toy」分区,勾选了 AI 辅助创作声明
  • 数据来源:git log、新华社 8-21 通报、中国天气网气象分析师叶梦龙分析、维基百科登陆记录、横州公安通报、toy mylist --json、gh run 日志、浏览器 DOM 实测

错在哪(设计事实)

不是阈值调错了,是分级依据只覆盖了一条致灾路径。

台风等级(热带低压→热带风暴→…→超强台风)是按近中心最大风速定义的。风眼把它直接当成”危险程度”的代理,于是:

致灾路径风眼是否感知
大风(吹倒/吹飞)✅ windLevel
风暴潮⚠️ 间接(nearCoast)
暴雨内涝❌ 数据模型里没有雨
山洪/滑坡❌
水库溃坝❌

美莎克走的是后三条。而后三条恰恰在台风减弱时才充分发展(残涡滞留、水汽持续输送),于是出现反向关系:致灾能力上升的同时,风力档位在下降。

这比”少了个功能”严重:一个只报风的系统,在纯降雨型台风面前不是沉默,是主动给出安全信号。


结果分析

  • 符合预期:五个场景全部通过(正常台风 / 影响持续期 / 真·风平浪静 / 旧 schema 缓存 / 预警源抓取失败);当日真实个例抬档两级;暗色主题与 CSS 令牌正常;旧 schema 下雨情区块自动隐藏,向后兼容
  • 未验证部分(诚实边界):
    1. 影响持续期从未经历真实停编——只用构造数据测过,recentlyEnded 的识别逻辑(比对上一版 JSON 中消失的台风)要等下一次真实停编才算跑通
    2. 450 km / 1500 km 两个阈值是拍的,没有依据校准;省中心点关联对新疆、内蒙这类大省很粗糙(本场景多为华南沿海省份,影响有限)
    3. 省级 floodLevel 取全省最高档——省内可能只有个别区县发布,页面口径已注明,但仍是偏保守的放大
    4. Toy 侧最终发布状态是作者人工确认的,不是我抓到的页面内容(Chrome 扩展在点击发布的瞬间断连,我停在了 auditing)。与2026-07-30_风眼九段线问题地图事故复盘_v1同一处复验缺口第二次出现:这类平台的线上态目前没有可自动化的核验手段

方法论沉淀

⚠️ 单因子分级会在最危险时段给出最低档(首次发现)

核心:分级/评分系统若只取一个代理指标,遇到走其他致灾路径的个例时会反向失效——不是不够灵敏,是自信地报安全。风眼按风力推档,而美莎克的伤亡全部来自水;风力档位在残涡滞留阶段回落到蓝色,恰是死亡最集中的 26 小时。

来源:美莎克(2610)“弱级强灾”,官方定性;代入改版前 suggestLevel 得到的档位序列 黄→蓝→风平浪静。

操作规则:

  1. 给任何分级系统列一张致灾路径 × 是否被感知的表(见上文),空格就是盲区;
  2. 多路径系统取 max(各路径档位),不要用单一指标推导;
  3. 当次要路径档位高于主指标档位时,必须在界面上说破这件事,而不是静默取高——否则用户会拿主指标(台风等级)自行推翻你的建议;
  4. 迁移场景:健康风险评分、告警分级、风控评分、内容审核置信度——凡”用一个易测量代理去表达一个多因致的风险”都适用。

⚠️ 数据源的生命周期短于业务风险的生命周期(首次发现)

核心:上游数据源的对象生命周期,往往按它自己的业务定义,而不是按你的。台风”编号”只存在 6 天(停编即从官方 JSON 消失),但这场灾害的伤亡数字是停编 45 天后才核定的(39 → 159)。系统跟着数据源的生命周期归零,于是在灾后重建期显示”风平浪静,风来之前都是准备的好时候”。

来源:7-07 停编 → 8-21 新华社定案,中间六周页面持续显示无台风态。

操作规则:

  1. 接入外部源时明确问一句:“这个对象消失,是它的风险结束了,还是只是它在上游的记账周期结束了?”
  2. 两者不一致时自建一段”余波窗口”,由真实风险信号(此处=影响省份仍有洪涝预警在效)而非源对象存在与否来决定退出;
  3. 余波期的文案要与常态区分,不要复用”一切正常”那套话术。

⚠️ 混合行尾会让多行锚点替换静默失败,而常用命令查不出来(首次发现)

核心:E:\typhoon-eye 全部源文件为 CRLF / LF 混合(仓库既有状态,无 .gitattributes)。用 \n 拼接的多行锚点做字符串替换必然 miss,单行锚点却正常命中——症状极像”锚点写错了”,实际是行尾不匹配。更坑的是 Git Bash 的 grep -c $'\r'、awk '/\r$/'、cat -A、wc -l 都会静默剥掉 CR,查不出问题;只有 node -e 'JSON.stringify(...)' 或 file 看得见(实测 app.js 有 943 个 CR)。

来源:本轮连续 3 次多行锚点替换失败,先后怀疑过锚点抄错、heredoc 转义、patcher 逻辑,最后靠 node 打印原始字节定位。

操作规则:

  1. 改这类仓库,锚点替换要么只用单行锚点,要么让匹配对行尾不敏感(先按原样找,找不到再按全 CRLF 变体找);
  2. 不要为此做全库行尾规范化——那会把真实改动淹没在噪声里;git 本身配了 autocrlf,提交时会归一;
  3. 排查行尾问题一律用 node/python 看原始字节,别信 Git Bash 的文本工具;
  4. 附带一坑:本机 bash heredoc 即使用 <<'EOF' 引号形式也观察到会把 \\ 折成 \,所以 patcher 脚本里避免写正则转义(用双变体字符串匹配代替)。

⚠️ 自曝局限比被人发现更有利(首次发现)

核心:“风眼查不到美莎克”被当成了传闻的佐证。真实原因(仓库比台风晚建三天 + 只渲染在编台风)平淡且对作者不利,但由作者自己讲清楚,比等别人发现要好得多——它同时解决了传闻和信任两件事。本轮把这条写进了 README、PLAN.md 和对外专栏的最后一节,并顺势承诺了”做一个能查历史台风的版本”。

操作规则:工具的已知局限写在自己的文档里,并在对外稿里主动点名;别等它变成别人手里的证据。与打磨不等于校验律_v1互补:那条讲”看起来精致会虚假抬高信任”,这条讲”主动示弱能换回真实信任”。


顺带修掉的既有隐患

#tyKicker 内含 #tyCode / #tyEnName 两个 span,而”风平浪静”分支直接 tyKicker.textContent = "西北太平洋" 会把这两个子节点连根删除,此后任何 $("tyCode") 返回 null。原代码没暴露是因为无台风分支之后不再引用它们;本轮新增”影响持续期”分支需要读 code/enName,一写就崩。已改为重建结构(setKickerStorm),顺带修掉”无台风态 → 有台风态”切换时的潜在崩溃。

教训:textContent 赋值是破坏性的,凡容器内嵌有 id 的子元素,一律重建而非直接赋值。


关联文档

类型/平台工程主题/风眼主题/预警设计主题/踩坑