20-overview-panels.md 18 KB

20: 总体概览模式面板

What to build: 在总体概览一级模式下同时展示态势总览、基础设施、运行监测、预警设置四个二级面板,形成城市生命线整体态势摘要。

Blocked by: 17, 18, 19

Status: resolved Labels: ready-for-agent

  • 态势总览展示跨行业 KPI:设施底数、监测规模、在线率、当前预警/报警、处置率、近 7 日趋势。
  • 基础设施面板汇总设施底数、行业构成和区域分布。
  • 运行监测面板汇总在线率、离线/故障统计和监测覆盖。
  • 预警设置面板保持合同名称,只读展示预警/报警统计。
  • 面板指标可联动地图或事件列表。
  • 面板可独立展开/收起。
  • 有汇总指标单测。

进度:100%(已关闭,2026-09-29)

逻辑闸门 63/63 · 变异测试 14/14 · 真机走查逐组通过(DSH 浏览器驱动)。

四块面板接三个概览端点(/overview/infrastructure、/overview/monitoring、 /overview/warning-settings)+ 借票 17 的 /infrastructure/base-archives 取区域分布; 三块各自成败(一块端点坏了其余照常画出真实内容),态势总览不自己取数、 由另外三块合成,所以「预警总数」在态势总览与预警设置里按构造是同一个数。

交付物

  • domain/overviewPanels.js(新):四块的契约适配 + 视图计划(纯函数)。
  • services/overviewApi.js(新):四个端点(含借用的 base-archives)的唯一出口。
  • stores/overviewStore.js(新):四分区 + 态势总览的合成 + 两条联动出口。
  • mocks/contractMocks.js:三个 /overview/* 端点(按请求真的回显/过滤)。
  • components/PanelBlocks.vue:新增通用 panel-action 转发与可点 KPI/分布行 (渲染层仍不认识 panelId,票 18 解除的耦合没长回来)。
  • domain/panelViews.js + CockpitShell.vue:注册并接进共享接缝(16 块面板至此全部接上真实视图)。
  • verify/t20-overview-panels.mjs(新,63 项)+ verify/t20-mutations.mjs(新,14 条变异)。

九条值得记下的判断

1. 预警与报警两族都画、不做切换,且态势总览与预警设置共用一次取数。** /overview/warning-settings 一次响应就同时给 warning 与 alarm(逐类读过响应类)。 做切换等于把已经拿到的数据藏起来,还会把概览与预警处置的 eventStore.mode 耦上。 两处读的是同一个对象引用(D2b 断言),所以数字不可能对不上。

2. 「近 7 日趋势」必须真的声明窗口,否则只是一句空话。 后端趋势桶跟随声明窗口过滤(CockpitOverviewWarningSettingsService.resolveWindow); 不声明时维持 CURRENT_BASELINE(无界)。本票固定声明 LAST_7_DAYS,界面并回显窗口类型。

3. 「区域分布」不在概览端点上 —— 借兄弟端点,但只借纯适配层。 /overview/infrastructure 只有 Totals + Industries + Excluded(无区域字段)。 区域数据真实存在于票 17 的 base-archives,故复用其纯适配函数 toBaseArchives(); 只引适配层、不引块构造器(那才是票 18 抽出 panelBlocks.js 要避免的耦合)。 它同时带来里程与规模两个读数,一并呈现。

4. 「故障统计」后端没有这个桶 —— 不发明,如实标缺口(维护者 2026-09-29 裁定)。 Counts.Status 的真实四桶是 online/offline/stale/unknownOrMissing(服务第 175-182 行 只认 ONLINE/OFFLINE/STALE)。维护者指出「数据过期未必是故障,可能只是网络波动」, 故不合成一个"故障"数,四桶按本义呈现(滞后 ≠ 故障),缺口写成 MONITORING_FAULT_GAP 印在面板上。

5. 「联动地图」在概览数据上做不到 —— 改走两条真实存在的路。 三个概览端点都不含点位编码或坐标(逐类读过),票 17 的 mapLocate 接缝没有可绑对象。 故联动走:① 指标读数 → 切到预警处置模式落到真实事件列表(那里的事件带 deviceCode, 再由票 13/14 既有链路跳点位);② 行业构成行 → 写全局 filterStore.setIndustry (真的同时驱动一张图与各面板)。不伪造一次地图定位。

6. 分母为 0 不是 0% —— 且这条判据只有一份实现。 票 18 在在线率上用 M3/M14 两条变异测试守过这一族;本票的在线率与处置率都要它。 故把 handlingRateView()/handlingRateHint() 从票 19 的模块导出供两处共用, 并给 toHandlingEfficiency() 补带 handlingState("分母为 0"与"真的是 0%"分得开)。

7. ⚠️ 真机走查逮到真缺陷:两块面板的读数全是 —,而逻辑闸门 59/59 全绿。 monitoringSummaryPanel 与 alertSettingsPanel 以为 data 是 {monitoring}/{warningSettings} 包装对象,而外壳传的是裸 section.data。测试喂的正是那个错误的包装形状 —— 测试与实现犯了同一个错,所以闸门看不见。 已修,并补三条护栏:C3/C4(按真实形状断言读数是数字)、D8(经 panelView() 端到端逐块读数字)、 F6c(钉住外壳逐块的数据源分派)。 教训:手喂 data 的单元断言证明不了"外壳喂进来的形状是对的"——必须有一条走外壳真实入口的断言。

8. 变异测试又逮到两条"守卫比它要守的东西宽/窄"(本仓第 N 次)。 ① F6b 第一版只查 linkToMode/linkToIndustry 在文件里出现过 —— 而它们在 handlePanelAction 的定义里当然出现,于是删掉 @panel-action 绑定(联动出口实际断掉) 根本测不出来(M13 全绿)→ 改为断言两处模板绑定都在。 ② F3 的裸字段词表只列了本票用到的几个,比 t05 的 D1 窄 → 改为与 D1 同宽。

9. 工单状态字典按后端权威枚举对齐到 0–7(维护者裁定),并删除冗余副本。 后端 CockpitWorkOrderStateDictionary 的类注释原文:「与运行时 WorkOrderStatusEnum(0–7)对齐… 终态为 5、7,延期中是处理旁路」。前端旧表是 1–7 且把「已结案」标在码 6 上 → 每态错位一格。 已对齐(含 WORK_ORDER_CLOSED_CODE 6→5、主线 1→6 改为 0→5、新增 BYPASS 动作), 并删掉 mock 里那份重复字典(改为从唯一源派生)。跨票影响见下。

验证

逻辑闸门 63/63(verify/t20-overview-panels.mjs,六段):

node .scratch/cockpit-shell-panel-layout/verify/t20-overview-panels.mjs
  • A 面板映射与注册(6):四块与 ADR 0003 一致、左右各二、构造器与目录逐项一致、 注册表覆盖四个模式、未注册 panelId 仍回 null、不越界接别模式的面板。
  • B 契约适配(9):BigDecimal 字符串、行业 label 回退字典、本端点无区域字段(如实)、 四态桶、不发明「故障」桶、逐设备采集时间(票 18 的 I2/BT-8 在这里有值)、 两族同源、处置率带 state、0–7 共 8 个码全铺、窗口回显。
  • C 视图层(12):验收点名的画面元素逐项在场;两条趋势;三个读数带联动出口; 分母为 0 显示占位符而不是 0.0%;区域缺口与故障缺口如实说明; C3/C4 按外壳真实形状断言读数是数字(真机缺陷的护栏);失败态仍返回结构完整的视图。
  • D 分区独立(9):三块各自成败、一块坏不遮其它 + 对抗式反证; 态势总览由三块合成且与预警设置同一引用;区域并进基础设施; D8 经 panelView() 端到端逐块读数字;行业筛选真的重算且不重取不可筛的块; 窗口真的声明;空 ≠ 失败。
  • E 联动(4):指标 → 切模式;行业行 → 写全局筛选;空动作被拒; 概览 store 不提供任何地图定位入口(定位只能伪造,故结构上不该有)。
  • F 静态守卫(10):domain 纯函数、service 不拼绝对地址不带凭据、 store 零后端裸字段(与 t05 的 D1 同宽)、只读、渲染层不认识 panelId 也不拼路径、 外壳保持两处 hasPanelView 与三态措辞、F6b 断言两处模板绑定、F6c 钉住逐块数据源。

对抗式自查(变异测试,14 条全部以断言失败打红闸门):

node .scratch/cockpit-shell-panel-layout/verify/t20-mutations.mjs

M1 凭空造「故障」桶 → B2b;M2 不声明故障缺口 → C3c;M3 分母为 0 直画 0% → C1d; M4 状态分布退回 1–7 → B3c;M5 态势总览改读拷贝 → D2b;M6 区域失败遮整块 → D3; M7 指标联动变空操作 → E1;M8 行业联动不写筛选 → E2;M9 行业筛选不重取 → D6; M10 渲染层长出 panelId → F5;M11 domain 反向 import service → F1; M12 store 写后端裸字段 → F3;M13 外壳不接联动动作 → F6b;M14 不声明窗口 → D7。 14/14 被抓住,还原后复跑全绿。

变异脚本自身的两处坑(都已修,记下来免得下次再踩): ① 子进程输出用管道会被本机沙箱以 EPERM 拒(Windows confined mode 不允许开命名管道)→ 改成把 stdout/stderr 写进临时文件描述符; ② 本仓 .vue 是 CRLF,而变异源文本按 \n 写 → 出现"变异源文本未找到"的假失败 → 按目标文件换行风格归一。

真机走查(已执行:2026-09-29,逐组通过)

本机 @puppeteer/browsers 是空目录(票 01 记录的既有环境问题,非本票引入), 故沿用票 36 的办法:用 DSH 自带浏览器真实驱动同一份页面,逐项跑同一批断言。

组 验的是什么 实测
A 四块真面板、四块 success、零占位 4 块面板 / 0 占位
A 态势总览 KPI 从 DOM 读 设施底数 5处 · 监测规模 9台 · 智能设备 9台 · 监测点 7个 · 在线率 75.0% · 当前预警 8条 · 当前报警 9条 · 处置率 62.5%
A 两条近 7 日趋势 近 7 日预警趋势 / 近 7 日报警趋势(14 个趋势点)
A 基础设施含行业构成与区域分布 设施 5处 · 里程 12.4km · 规模 中型 · 行业 3 个 · 区域 CN3/CB1/未知1
A 运行监测四态桶 + 故障缺口印在界面上 在线 6台 / 离线 1台 / 滞后 1台 / 状态未知 1台 + 缺口说明
A 预警设置两族 + 0–7 共 8 个状态码 预警 8条/报警 9条;状态分布含延期中 1(新状态真的画出来了)
A 零后端代号泄漏 两栏可见文本零 GAS/CURRENT_BASELINE/LAST_7_DAYS/medium 等
B 行业行 → 全局筛选 + 真的重算 筛选 all → gas;在线 6→3台、智能设备 9→4台、监测点 7→3个、正式行业范围=燃气
B 不可筛的块不假装生效 基础设施设施总数 5处 → 5处(该端点不收行业入参)
C 指标读数 → 切预警处置模式 点「当前预警」→ 一级模式 = 预警处置,落到真实事件列表
D 只坏一块不遮其它 监测 failed("数据加载失败"),基础设施 9 行分布 / 预警设置 16 行分布照常画出真实内容
E 空场景 ≠ 失败、不伪造 0 三块 empty("无数据"/该面板当前没有数据);0.0% 未出现

证据:verify/walkthrough/t20-overview-panels.png。

跨票改动(已重开对应票记录,请维护者复核)

按维护者裁定「以后端 WorkOrderStatusEnum(0–7)为唯一真理,删除其他不必要的字典」, 本票改动了三张已关闭票的交付面(详见各票的重开记录):

  • 票 12(工单时间线):workOrder.js/workOrderTimeline.js 对齐 0–7; 主线 1→6 改为 0→5;新增 BYPASS(延期中是处理旁路,与已取消同为路径外)。 票 12 原验收「不展示延期」与后端权威枚举直接冲突,已作废并同步改 ADR 0003。 闸门 49 → 52 项(回退语义一字未改)。
  • 票 19(预警处置):查出两处真缺陷 —— ① 「工单状态分布」用错字典, 标签整体错位一格(已随字典对齐修正,标题改为中性的「工单状态分布」); ② 处置率"分母为 0"会显示虚假 0.0%(已修,并导出共用判据)。 另外本票发现票 19 从未把新文件补进 t05 的静态守卫面(票 11/17/18 都补过), 实测那两个文件干净后已补入(t05 255 → 270 项)。
  • 票 11(设备详情):仅断言/样例的码值跟随字典更新(行为未变),闸门 61/61 保持。
  • 票 17(基础设施走查):其 A8b 断言"总体概览恰好 4 块占位",被本票接真面板后必然假红 —— 这是钉在别人票进度上的断言(票 18 在完全相同处境下已栽过一次并已自修)。 按 Q9=(a) 去魔数化:只断言本模式,其余模式只汇报。

边界与下一步

  • 本票交付面是前端页面;后端真实数据接线(概览三端点的真实适配器)归 驾驶舱真实后端数据接线(决策地图), 按票 18 的收口口径不阻塞本票。
  • 「故障统计」的口径是业务问题(票 25 的三条待答之一),本票按维护者裁定 不发明故障数、四态如实呈现并标缺口。
  • 本部署的 map.ui.debug 决定顶栏是否显示诊断读数(票 32),与本次改动无关。

Answer

总体概览四块面板落地:接三个概览端点 + 借票 17 的档案端点取区域分布, 三块各自成败(一块坏了其余照常画出真实内容),态势总览由另外三块合成 (故与预警设置的数字按构造同源)。

四条边界如实划清,不发明业务: ① 「故障统计」后端没有该桶 —— 四态如实呈现并标缺口,不把「滞后/状态未知」折算成故障 (维护者 2026-09-29 裁定:故障语义复杂,不合成); ② 「区域分布」不在概览端点上 —— 借票 17 的 base-archives 纯适配层,只引适配不引构造器; ③ 「联动地图」在概览数据上做不到(三端点都不含点位编码/坐标)—— 改走两条真实存在的路 (指标→切预警处置模式落到真实事件列表;行业行→写全局筛选真的驱动地图),不伪造定位; ④ 分母为 0 不是 0% —— 在线率与处置率共用同一条判据,不画虚假零值。

票面七条验收全部达成(逐条见正文勾选)。

本票真机走查逮到一个逻辑闸门看不见的真缺陷(两块面板读数全是 —, 而闸门 59/59 全绿 —— 因为测试与实现犯了同一个错,都以为 data 是包装对象), 已修并补了三条护栏(含一条经外壳真实入口的端到端断言)。 跨票改动三张已关闭票(12/19/11)与一张走查脚本(17),均已重开记录、请维护者复核。

Comments

2026-09-29 认领与阶段闸门

已认领(Status: claimed)。本票无 needs-triage 标签,Blocked by: 17, 18, 19 三张前置票均已 resolved,故按闸门规则不经过诊断阶段,直接进入实施; 但票面含多处"不该猜"的地方,故按本仓成例(票 17/18)先做澄清。

读现状时核出的关键事实:本票票面写「0% 未动工」是准确的 —— 前端仓库对 /overview/* 零实现(无 mock、无 service、无 api); 三个端点在后端已存在;且票 24 调研里 situationSummary 那处「趋势窗口不可声明」的缺口 已由后端 BT-3 关闭(warning-settings 现收 range),故「近 7 日趋势」可做。

2026-09-29 实施前的三轮澄清(维护者裁定)

第一轮:预警/报警并列与共用取数(A)、区域分布读兄弟端点(A)、 故障统计按真实四态不发明(A)、联动走事件列表+全局筛选(B)、真机走查授权(B)、 处置率不加工口径说明(A)、全局筛选只作用于能收它的那块(A)、 补 t05 守卫面(C)、修票 17 假红(A)。

第二轮:维护者补充了故障语义的完整口径(点位四态与设备五态都算异常; 但"数据过期"未必是故障,建议只保留离线统计,语义过复杂则删掉故障统计); 并明确 Q4 的联动语义是"面板中的设备/报警 → 跳到场景中对应点位"。 据此核出:三个概览端点都不含点位编码或坐标,故该语义在本模式数据上无法直接实现 —— 改走"切到预警处置模式落到真实事件列表"(那里有 deviceCode 可再跳点位)。

第三轮:Q10 维护者先答 (c) 后更正为 (b)(一次收口、同步票 12/19 断言); Q13 裁定以后端 WorkOrderStatusEnum(0–7)为唯一真理并删除冗余字典; Q14=(a) 延期中画成路径外旁路、口径改中性词;Q16 按此落地。 期间向维护者指出两处硬冲突:「七态」在 0–7 下不成立(是 8 个码)、 ADR 0003「不展示延期」被后端枚举推翻(6 延期中是正式状态)。

2026-09-29 实施、自查与收口

实施中自查(变异测试)逮到两条自己的守卫缺陷(F6b 只查词出现过、F3 词表比 t05 窄), 均已修并用变异确认有牙。真机走查又逮到一条真缺陷(两块面板读数全 —, 测试与实现犯同一个错),已修并补三条护栏。逻辑闸门 63/63、变异 14/14、 全量回归 29 个闸门绿(t04-config 与 t28-fill-container 的失败是既有环境问题: 前者因另一张票在 cockpitApi.js 注释里写了 /api/cockpit 而误报, 后者需要 puppeteer 的 @puppeteer/browsers,两者都与本票无关、本票未触碰)。