# 42: 前端执行票 —— 预警设置面板消费预警自身生命周期契约 Type: task Status: resolved Label: ready-for-agent Blocked by: 41 Category: frontend-execution Assignee: codex ## 来源 执行票 [41 预警设置端点改为预警自身生命周期统计](41-warning-settings-endpoint-rework.md) 已收口(提交 `b047a60d` 已推 origin/master),新契约已确定并实测;证据见 [基线卡片 06 预警设置生命周期](../handoff/baselines/warning-settings-lifecycle.md)。本票承接票 41 验收第 2 条:"前端消费侧改动(面板改为读新字段)在契约确定后另立前端票"。 ## 后端新契约(b047a60d 已实测) `GET /api/cockpit/v1/overview/warning-settings` 一次仍给 `warning` + `alarm` 两族,但语义已分叉: **预警族(预警自身生命周期,不走工单)**: - `total`:预警总数(语义不变)。 - `statusDistribution`:`[{code,label,count}]` — `WarningWorkflow` 六态全铺(DRAFT 草稿 / PENDING 待办 / RECEIVED 已接收 / PROCESSING 处置中 / HANDLED 已处置 / CLOSED 已解除;含 0 桶,脏值进 UNKNOWN 桶)。 - `levelDistribution`:`[{code:"1".."4",label,count}]` 1–4 全铺(`warning_level` 字符串解析,脏值 UNKNOWN)。 - `industryDistribution`:GAS 燃气 / WATER_SUPPLY 供水 / DRAINAGE 排水 / MANHOLE 窨井 四正式桶 + UNKNOWN。 - `districtDistribution`:0 城南 / 1 城北 / 2 太常 + **恒有** UNASSIGNED 未分区。 - `handlingEfficiency`:**语义已变** = 已解除 ÷ 预警总数(numerator/denominator/value/state/reason;分母 0 → `state=EMPTY`、`reason=NO_WARNINGS`)。 - `trend`:按发布时间分桶 `{bucket,total,handled,handlingEfficiency}`,`handled`=桶内已解除。 - **工单口径六字段一律 `null`**:`handled` / `pending` / `cancelledCount` / `unlinkedEventCount` / `invalidWorkOrderStateCount` / `workOrderStates`。 **报警族(保留工单口径 + 并存自身生命周期)**: - 工单口径字段原样保留:`handled/pending/cancelledCount/unlinkedEventCount/invalidWorkOrderStateCount/workOrderStates(0–7)/handlingEfficiency`(处置率仍是工单口径)。 - 新增 `statusDistribution`:UNHANDLED 未处理 / HANDLED 已处理(`alarm_status`)。 - 新增 `levelDistribution` / `industryDistribution` / `districtDistribution`(形状同预警族)。 - `trend` 不变(工单口径 handled)。 注意:`sum(分布桶) == total` 恒成立(分布取整个快照、不随 `range` 收缩),只有 `trend` 跟随窗口。 ## 现状消费点(会坏的地方) - `src/modules/cockpit/domain/overviewPanels.js`:`toOverviewWarningSettings()` 的 `family()` 读 `workOrderStates/handled/pending/cancelledCount/unlinkedEventCount/invalidWorkOrderStateCount/handlingEfficiency` —— 预警族这些字段现在是 `null`;`familyBlocks()` 渲染「已处理/待处理/已取消/未关联工单/处置率/工单状态分布」六项 + stacked 块,预警半边会全 0 或空。 - `situationSummaryPanel()`:`当前预警/当前报警` 读 `total`(不受影响);`处置率` 读 `warning.efficiency` —— 语义已变为**已解除率**,必须连 `state/reason` 一起读,分母 0 显示「不适用」。 - `src/modules/cockpit/mocks/contractMocks.js`:`overviewWarningSettingsPayload` 需与新契约同构。 **关键风险**:`numberish(null)` 会把「不适用」数成 0 —— 空态/不适用必须显式判 `state`/`null` 后显示占位符,不得显示 0(沿用票 22「故障不伪装成正常空态」的同一纪律)。 ## 范围 1. `toOverviewWarningSettings` 适配新契约:预警族读四个分布 + 已解除率;报警族读工单口径 + 自身生命周期分布;`null` 字段在领域层记「不适用」,不数 0。 2. `familyBlocks`:预警半边改为状态六桶 + 等级/行业/片区分布 + 已解除率;报警半边保留工单口径块 + 并存 `alarm_status` 分布。图表形态沿用票 39/40 既有块构造(`panelBlocks.js`),不另起一套。 3. `situationSummaryPanel` 处置率语义更新(已解除率),空态「不适用」。 4. mock 同步:`contractMocks.js` 的 warning-settings 载荷改为新契约形状(真实数值参照基线卡片 §4.2)。 5. 契约/verify 脚本与领域层回归更新。 ## 非目标 - 后端改动(票 41 已完成并推送)。 - 不新增端点、不改只读边界、不改其它面板。 ## 验收 - 契约测试/verify 脚本通过;mock 与 real 同构(同一领域层函数可消费两者)。 - 预警半边:状态六桶/等级/行业/片区分布 + 已解除率正常渲染;工单口径字段不再出现(或显式「不适用」)。 - 报警半边:工单口径块保留 + `alarm_status` 分布并存。 - 空态与「不适用」显示占位符而非 0。 - 真实环境走查(`http://127.0.0.1:8000/#/cockpit` 总体概览-预警设置):按项目规则,浏览器验收需维护者明确授权后执行;且后端需先滚动部署 `b047a60d`(当前共享实例 8301 仍回旧契约,见基线卡片 §4.3)。 ## 关联 - 上游:[41 预警设置端点改为预警自身生命周期统计](41-warning-settings-endpoint-rework.md)(已收口)。 - 基线:[基线卡片 06 预警设置生命周期](../handoff/baselines/warning-settings-lifecycle.md)。 - 相关决策:决策 06(异常统计与业务语义)、[22 真实响应信封消费](22-22.md)(失败/空态纪律)。 ## 进度 - 2026-10-14 建票(由票 41 收口时另立,承接其验收第 2 条);契约细节已写入票面,未动工。 - 2026-10-14 本会话实施并收口(见下方「实施记录」)。 ## 实施记录(2026-10-14,已收口) **改动(提交 `0cd16ac`,6 文件 +745/−55)**: | 文件 | 改动 | | --- | --- | | `domain/overviewPanels.js` | `toOverviewWarningSettings()`:工单口径字段 null 保真(`workOrderStates` 缺失不再补 8 根假零条)、四组分布直读透传(不补 0/不重排/不翻码)、`stateReason`/`handlingReason` 补带;`familyBlocks` 拆为 `warningFamilyBlocks`(纯自身生命周期:KPI=总数+已解除率,四分布块)与 `alarmFamilyBlocks`(工单口径 KPI + 工单状态分布 + `alarm_status` 生命周期分布并存);`situationSummaryPanel` 处置率 KPI 改「预警已解除率」(hint 写明「已解除 X / 预警总数 Y」,判据复用票 20 单源 `handlingRateView`) | | `mocks/contractMocks.js` | 载荷与新契约同构(形状对齐 `b047a60d`:预警族工单字段 null、四组分布、已解除率;报警族工单口径 + `alarm_status` 分布;空场景分布全铺计数 0、速率 EMPTY + `NO_WARNINGS`/`NO_ELIGIBLE_WORK_ORDERS`),总数保持 8/9 不动 t20 的 D8 端到端断言 | | `verify/t42-warning-settings-contract.mjs`(新增) | 本票守卫:用基线卡片 §4.2 **真实复验值**(31/66)过领域+视图两层,断言 null 保真、四组和恒等于 total、报警工单保留、空态「不适用」 | | 票 41 文件 / 基线卡片 06 | 上一会话的收口记录随本提交入库 | **t20 守卫随契约更新**(`cockpit-shell-panel-layout/verify/`,两文件):`B3c` 移到报警族(工单口径只在报警族)、新增 `B3e`(预警族 null 保真)/`B3f`(UNKNOWN/UNASSIGNED 桶透传)/`C4d`(预警族零工单词汇);`M4` 变异锚点重定位到新行、新增 `M14`(null 守卫摘除 → B3e 红,已用领域层直证:变异后 `workOrderStates` 变 8 根假零条 → `B3e` 必红)。 **验证**: - `t42-warning-settings-contract.mjs` → **通过**(提交前后各跑一次一致)。 - `t20-overview-panels.mjs` → **60/66**:新增 3 条守卫全绿;**6 条红(B4/C2/D2b/D3/E2/F3)经 HEAD 基线对照证实为既有失败**——在 HEAD 临时 worktree 复跑同闸门为 57/63、**同样的 6 条**,与本票改动无关(落在区域分布/行业联动/store 裸字段路径,本票 diff 与之零交集)。D8 端到端(mock→领域→store→视图)在本票路径上 PASS。 - 变异闸 `t20-mutations.mjs`:本轮无法完整跑——**运行环境在会话中途被并行会话破坏**(16:03 起 `node_modules/@babel/*` 被清空,vue 链 `Cannot find module '@babel/parser'`,t20 的 D 段 import 与 t50 均崩);M14 已用领域层直证补验(见上)。环境恢复后建议复跑。 - **跳过 build/dev/浏览器验收**(项目规则未获授权);真实走查的前置=共享实例滚动部署 `b047a60d` + 维护者授权,归票 17 部署窗口。 **入库方式(重要)**:工作树混有**票 37–40 的孤儿在飞批次**(donut/stacked/折线等 UI hunks,未认领未提交,且与本票同文件)。本提交经 **git plumbing**(`hash-object -w` + `update-index --cacheinfo`)只含**本票 hunks**(HEAD+42),孤儿批次**字节未动**地留在工作树(提交后 `git status` 两个 src 文件仍 M,diff 即孤儿 hunks)。提交版已先在 HEAD 临时 worktree 里通过语法检查与 t42 全量断言。 **推送状态**:`0cd16ac` 在**本地**,**未推送** —— 本地领先 `origin/master` 19 个提交,其中 18 个是并行会话的未推工作(票 32/33/35/36/48 等),代推会把它们一起带上(票 35/36 的既有纪律)。推送需维护者裁剪协调。 **遗留(不阻塞本票)**: - 预警族 `handled`(alarm_status)与 `pending/cancelledCount/unlinkedEventCount`(工单口径)在同一 KPI 行混用两种口径 —— 后端契约语义问题(票 41 范围),前端如实渲染;建议维护者裁定是否拆行。 - 真实环境走查(验收第 5 条):待后端部署 `b047a60d` + 维护者授权浏览器验收,滚动复验归票 17。 ## Comments ## Answer