# 后端执行票 —— 预警效率端点缺预警自身处置流程字段(两族回同一套工单口径) Status: ready-for-agent Type: task Blocked by: None Labels: wayfinder:task Category: backend-contract ## 来源 [票 65](65-untitled.md)(前端执行票 —— 预警效率面板按预警/报警分流)收口时记录的**契约缺口**。 票 65 的验收第 5 条明写:若需要后端新增预警流程字段,**在票面记录契约缺口并另开后端票,本票不假装已有数据**。 本票就是那张后端票。 ## 缺口(2026-10-21 实测,`192.168.110.96:8301` 只读) `GET /api/cockpit/v1/events/handling-efficiency?eventFamily=WARNING` 与 `=ALARM` 的响应结构**完全相同**, 两族都只给工单口径: | 字段 | 语义 | 预警族实测 | | --- | --- | --- | | `summary.totalEvents` | 窗口内事件条数 | 5 | | `summary.linkedWorkOrderCount` | 回联到工单的事件条数 | **0** | | `summary.missingWorkOrderCount` | 未回联工单的事件条数 | **5**(=全部) | | `summary.handledCount` / `inProgressCount` / `cancelledCount` | **工单**状态派生(不是预警生命周期) | 0 / 1 / 0 | | `summary.handlingEfficiency` | 工单口径处置率(`CLOSED_WORK_ORDERS_OVER_ELIGIBLE_WORK_ORDERS`) | num=0 den=1(分母来自 1 张**与预警无关**的待派单工单) | | `summary.averageDuration` | 关联工单结案时长 | EMPTY / `NO_CLOSED_WORK_ORDERS` | | `workOrderStateCounts` | 窗口内**全部工单**八态(`scopeWorkOrders`,不要求关联事件) | 只有 1 张待派单 | **代码证据**(`CockpitHandlingEfficiencyService.build()`,t30 validation-checkout 同版): - `stateCounts` 由 `scopeWorkOrders(...)` 累加 —— 范围内**全部工单**,注释原文「不要求关联报警/事件」; - 于是 `handledCount` / `inProgressCount` / `cancelledCount` / `handlingEfficiency` / `workOrderStateCounts` 全都是**工单**读数,两族共用同一份(`stateCounts` 只算一次,family 只影响 `facts` 也就是事件集合); - 预警自身的生命周期 `WarningWorkflow`(草稿/待接收/已接收/处置中/已处置/已解除)**不在此端点**, 只存在于另一个端点 `/overview/warning-settings`,且那里的分布是**全量快照、不随 `range` 收缩**, 该端点还不接受 `LAST_90_DAYS`(400)。 ## 为什么必须补 预警**不挂工单**(实测 `linkedWorkOrderCount=0`、`missingWorkOrderCount=总数`)。前端若照字面消费 `summary` 三字段,就会画出「预警处置中 = 1」—— 那个 1 来自一张与预警毫无关系的工单, 正是票 65 票面禁止的「用一个不存在的对象统计预警」。票 65 因此只在预警模式下画 可证实的读数(条数/等级/发生趋势/工单关联实况),前端现在是**如实的空缺口**,不是已解决的问题。 ## 建议契约(形状供后端评审,不以本票为准) `GET /events/handling-efficiency?eventFamily=WARNING` 的 `summary` 增预警自身流程段, 与 `overview/warning-settings` 的预警族语义**同源**(同 `WarningWorkflow` 六态字典,不另造一套): - `warningStateCounts: [{ stateCode, stateName, count }]` —— 窗口内预警的六态全铺(含 0 桶,脏值进 UNKNOWN); - `warningHandlingEfficiency: { numerator, denominator, value, state, reason, basis }` —— 已解除 ÷ 窗口内预警总数(分母 0 → `state=EMPTY` + `reason=NO_WARNINGS`,与票 42 同一纪律); - `warningDuration: { value, unit, sampleCount, state, reason }` —— 预警从发布到已解除的平均时长 (无样本 → `state=EMPTY` + 明确 `reason`,不得回 0 时冒充真实读数); - `warningStatusTrend: [{ timeBucket, total, closed }]` —— 窗口内按桶的发布量/已解除量。 **两族语义分流要求**:`eventFamily=WARNING` 时 `handlingEfficiency`/`workOrderStateCounts`/ `averageDuration` 三个工单口径字段应**显式为 `null`**(而不是照旧回工单读数)—— 与票 41 在 `overview/warning-settings` 已做的裁定完全同一条纪律(预警族工单口径 null 保真, 不用 0 或别的族的数冒充)。报警族保持现状(工单是报警的处置手段,票 41/42/52 既有裁定)。 ## 验收 1. 契约测试覆盖两族:预警族工单口径三字段为 `null`、预警流程段齐备且 `sum(六态)==窗口内预警数`; 报警族工单口径原样保留。 2. 真实环境实测 `eventFamily=WARNING`:`linkedWorkOrderCount=0` 时预警流程段仍给出真实六态分布 (不是全 0 假读数)。 3. 分母 0 / 无样本的回 `state=EMPTY` + 原因码,不回 `"0.0000"` 当真实值。 4. 全量 `Cockpit*Test` 通过;改动推 `origin/master` 并在票面记录提交哈希。 5. 前端消费侧另立前端票(票 65 的预警分支已预留口径:只要新字段到位,替换 `warningEfficiencyKpis` 的 三个计数即可,块结构不用重排)。 ## 关联 - 上游:[票 65 —— 预警效率面板按预警/报警分流](65-untitled.md)(前端已按「诚实分流 + 记缺口」收口)。 - 同族既有裁定:[票 41](41-warning-settings-endpoint-rework.md)(预警族改预警自身生命周期、工单口径 null 保真)、 [票 42](42-42.md)(前端消费两族分叉)、[票 52](52-untitled.md)(报警的单设备工单优先级)、 [票 11](11-backend-work-order-rate.md)(处置率=状态 5 ÷ 状态 0–6 工单数)。 - 参考实现:`CockpitOverviewWarningSettingsService.familyMetrics()` 已有预警族六态/已解除率的 **现成聚合逻辑**,本票是把它搬进带窗口的 efficiency 端点并分族,不是从零写。 - 后端仓库/负责人/权限按本图 Notes:需在仓库、负责人和权限明确后由维护者启动。 ## 进度 - 2026-10-21 由票 65 收口时另立(前端侧已完成诚实分流并记录缺口;本票未动工)。 ## Comments ## Answer