# 41: 后端执行票 —— 预警设置端点改为预警自身生命周期统计 Type: task Status: resolved Label: ready-for-agent Blocked by: None Category: backend-execution Assignee: codex ## 来源 维护者 2026-10-13 反馈第 1 条,并明确裁定:**预警不走工单流程,走自己单独的流程**;`/overview/warning-settings` 需要针对这条信息修改,**只展示预警自身生命周期的统计**(Q1=A:让后端改这个端点)。 ## 背景(实测) - 当前 `/api/cockpit/v1/overview/warning-settings` 返回的是**工单口径**字段:`workOrderStates`、`handled`、`pending`、`cancelledCount`、`handlingEfficiency`(处置率)等。 - 该端点在本地联调环境实测 **HTTP 200 + body `code=500`**:`字段 "dispatch_user" 不存在`(`WorkOrderMapper` 仍映射 `app_user.work_order` 已不存在的列)——即该端点的工单依赖本身就是当前故障源。 - 前端「总体概览 - 预警设置」面板因此显示「数据加载失败」(前端行为正确,如实反映后端故障)。 ## 范围 1. **端点语义改为预警自身生命周期**:去掉工单口径字段(工单状态分布、处置率、已处理/待处理等),改为预警自身统计: - 预警总数; - 按**预警自身状态**分布; - 按等级分布; - 近 7 日趋势(保留既有 `range` 窗口契约); - 按行业 / 片区分布。 2. **与报警族的关系**:需明确该端点是否仍同时给预警与报警两族(现状是一次给两族、态势总览共用)。若不改这一结构,报警族也应改为**报警自身生命周期**口径,而非工单口径。 3. **移除对工单表的依赖**:本端点不得再依赖 `work_order`(既消除 `dispatch_user` 故障,也符合「预警不走工单」)。 4. **契约测试**:新字段形状、状态/等级分布计数自洽、趋势窗口跟随 `range`、空态。 ## 非目标 - 不修改工单自身逻辑(工单仍由外部业务系统与既有流程负责)。 - 不改驾驶舱只读边界。 ## 验收 - 后端:编译 + 契约测试;本地联调环境复验该端点返回新契约且不再 500(登记到基线卡片)。 - 前端消费侧改动(面板改为读新字段)在契约确定后另立前端票。 ## 诊断(2026-10-14,本会话实测) ### 结论一:票面背景里的根因已经过期 票面写「实测 HTTP 200 + body `code=500`:字段 `dispatch_user` 不存在」。本会话复测,**HTTP 200 + body `code=500` 仍然成立,但报错列已变成 `dept_id`**: ``` ### Error querying database. Cause: com.kingbase8.util.KSQLException: ERROR: 字段 "dept_id" 不存在 ### SQL: SELECT order_id,order_no,device_id,fault_time,order_level,order_desc, dept_id,receive_user,order_status,... FROM app_user.work_order ``` `dispatch_user` 在本地 HEAD(`a96091ce`)里**已经不存在**——上游 `8fb5cb52`(工单新增创建人)已把 `dispatchUser`/`dispatchTime` 从 `WorkOrder` 删除。票面记的是**更早一次**的故障形态。 ### 结论二:真实根因是「运行构建落后于库结构」,不是本端点的设计缺陷 - 本地 HEAD `a96091ce` 的 `WorkOrder.java` 与 `WorkOrderMapper.xml` **仍声明并选择 `dept_id`**(`13d5bd1e` 引入)。 - 上游 `origin/master`(`5a821872`)的 `94bb7e4d「移动端-工单管理」` **已删掉 `dept_id`**:实体去字段,`WorkOrderMapper.xml` 的 `Base_Column_List` 与 resultMap 同步去掉。 - 联调实例(PID 17972,2026-10-14 08:48:40 启动)实测该列不存在 → **库已不再有 `dept_id`,而实例加载的构建仍去 SELECT 它**。 这与 [票 19](19-19-early-warning-warning-name.md) 形态完全一致:**构建/部署基线错位,不是缺列**。同一错列还打穿了 `events/handling-efficiency` 与 `/work-orders/{code}/timeline`(本会话实测两者同样 `code=500` + `dept_id`),故障面比票面记的「单点」更大。 ### 结论三:这决定了本票的实施基点 维护者本轮裁定(见下)要求**报警族保留工单口径**(报警靠工单闭环),而工单读取正是当前故障点,所以: > 本票实施必须建立在**已含 `94bb7e4d` 的基点**上,否则报警族仍会 `dept_id` 500,验收项「不再 500」无法达成。 本地 `HEAD=a96091ce` 不含该提交(本地领先 merge-base `989a707e` 1 个未推送提交=票 32 几何改动;`origin/master=5a821872` 领先本地 4 个提交)。合并 `origin/master` 与共享工作树里并行会话的未提交几何改动**无文本冲突**:上游 `989a707e..origin/master` 未触碰 `cockpit/geometry` 任何文件(`git diff --name-only` 核实)。 ### 维护者裁定(本会话 grilling,2026-10-14) - **Q1 契约形状**:**两族都留**。预警族改为**预警自身生命周期**(预警不走工单);**报警族保留工单口径**——「报警的生命周期与工单关联性很强,报警需要通过工单去进行处理解决」,同时报警自身生命周期也要保留。 - **Q2 验证授权**:全授权 —— Maven 编译 + `Cockpit*Test` + 重启实例做真实复验。 ### 前端影响(另立票,本票不改) 现前端 `toOverviewWarningSettings()` 读 `warning.workOrderStates` / `handled` / `pending` / `handlingEfficiency` 等工单口径字段。预警族字段改造后这些字段回 `null`,前端 `numberish(null)` 会**数成 0**(不是 `—`)——「预警设置」面板的预警半边会显示全 0 而非新分布块,直到前端消费票落地;报警半边保持可用(旧字段保留)。 ## 实施(2026-10-14) **基点**:从 `origin/master`(`5a821872`)拉**独立 worktree** `E:\pipe-ner-server-t41`(`git worktree add --detach`), 不动共享工作树(其上有并行会话的票 32 几何未提交改动)。选这个基点的原因见「诊断 结论三」: 本地 `HEAD` 仍声明 `dept_id`,而上游 `94bb7e4d` 已删除它;报警族按裁定保留工单口径, 必须建立在已删除该列的基点上,否则「不再 500」无法达成。 **改动(7 文件,+935/−279)**: | 文件 | 改动 | | --- | --- | | `CockpitOverviewWarningSettingsResponse` | `FamilyMetrics` 工单口径字段改**包装类型**(`Integer`/`Map`)以便显式 `null`;新增 `Bucket` 与四个分布字段 | | `CockpitOverviewWarningSettingsSnapshot` | `SourceEvent` 增自身生命周期 `status`/`level` 与维度 `districtCode`/`industryCode`(旧构造器保留为委托重载) | | `CockpitOverviewWarningSettingsService` | 拆 `warningFamily`(纯自身生命周期,工单字段全 `null`)与 `alarmFamily`(保留工单口径 + 并存自身生命周期);新增状态/等级/行业/片区分布构造 | | `BusinessCockpitOverviewWarningSettingsAdapter` | 预警**不再桥接工单**(`warningBuckets` 的 handled 改判自身 `CLOSED`);两族沿 `equipment_base` + `equipment_type` 树解析片区/行业带入快照 | | 三个测试文件 | 契约测试重写为两族口径分离 | **口径落点**: - 预警族:`total`、`statusDistribution`(`WarningWorkflow` 六态全铺)、`levelDistribution`(1–4 全铺)、 `industryDistribution`(四正式行业 + 未知)、`districtDistribution`(0/1/2 + **恒有**未分区)、 `handlingEfficiency`=已解除÷总数、`trend`(按发布时间分桶,handled=该桶已解除)。 工单口径六字段一律 `null`。 - 报警族:工单口径**原样保留**(含 `workOrderStates` 0–7、工单口径处置率、未回联/脏码/已取消分列), **并存** `statusDistribution`(`alarm_status` 0/1)、等级/行业/片区分布。 - 分布取整个快照(不随 `range` 收缩)→ `sum(分布桶) == total` 恒成立;只有趋势桶跟随窗口。 - 「未知」类(脏状态/脏等级/推不出行业)与「未分区」都**显式入桶**,故计数不静默丢。 **验证**: - 编译:`mvn -o -pl pipe-network-service/zksy-admin -am compile` → 通过。 - 针对性契约测试:`CockpitOverviewWarningSettings*Test` + `BusinessCockpitOverviewWarningSettings*Test` → **24/24**。 - 全量:`-Dtest=Cockpit*Test,BusinessCockpit*Test` → **479/479**,`BUILD SUCCESS`。 - 提交:`ae9c5731`(独立 worktree,7 文件)。 ## 真实复验(2026-10-14,验收达成) **方法**:不动共享实例(PID 17972,其他会话在用),从本票 jar 在 **8399 端口**起一个**第二实例** (`t41` 本地 profile + 关闭 nacos 远端配置导入与注册、推送任务 initial-delay 全推后), 连**同一个**联调库与 Redis。实测 `GET /overview/warning-settings?mode=WARNING&range=LAST_7_DAYS`: - **HTTP 200 + `code=200`**(验收项「不再 500」达成)。 - **预警族新契约实测**(真实库数据,全部自洽): - `total=31`;工单口径六字段**全部 `null`**(`handled/pending/cancelledCount/unlinkedEventCount/invalidWorkOrderStateCount/workOrderStates`)。 - `statusDistribution`:`DRAFT 2 + PENDING 4 + RECEIVED 0 + PROCESSING 4 + HANDLED 2 + CLOSED 19 = 31` ✓(和=总数)。 - `levelDistribution`:`13+5+1+12 = 31` ✓;`industryDistribution`:`0+2+24+1+UNKNOWN 4 = 31` ✓; `districtDistribution`:`城南 24 + 城北 1 + 太常 2 + 未分区 4 = 31` ✓。 - `handlingEfficiency = 19/31 = 0.6129`(已解除÷总数,预警自身口径)。 - `trend` 按 `LAST_7_DAYS` 裁桶(2026-10-09/10/14 三桶,窗外桶被裁掉)。 - **报警族实测**(工单口径保留 + 自身生命周期并存): - `handled=64`(`alarm_status=1`);`workOrderStates` 0–7 齐全;处置率 `1/5 = 0.2000`(工单口径); - `statusDistribution`:`UNHANDLED 2 + HANDLED 64 = 66` ✓;等级/行业/片区分布和均=66 ✓。 - **对照证据**(同一时刻):共享实例 8301(彼时已被他人重启到上游 `05c23774`,`dept_id` 故障已随上游修复消失) 仍回**旧契约**(`warning.handled=0`、`workOrderStates` 在、无 `statusDistribution`、处置率 0.2000)—— 证明 8301 的新 200 来自上游修复而非本票改动;本票的新契约在 8399 实例实测生效。 **推送**:变基到 `05c23774` 后复跑全量 **498/498**(变基带来 19 个新测试),提交最终为 **`b047a60d`**, 已推 `origin/master`(`05c23774..b047a60d`),远端 `master=b047a60d` 与本地一致。 第二实例与临时 worktree `E:\pipe-ner-server-t41` 已停机并清理,共享工作树未触碰 (票 32 几何改动仍原样留在工作树)。 **边界说明**:实施期间上游基点从 `5a821872` 前移到 `05c23774`(并行会话推送的票 35 createdAt),已通过变基合入,无冲突。 ## 前端后续(另立票,不阻塞本票) 预警族 `handled/pending/cancelledCount/workOrderStates` 等工单口径字段在真实环境回 `null` 后, 前端 `toOverviewWarningSettings()` 会把 `null` 数成 0、`workOrderStates` 空铺 —— 「预警设置」面板的预警半边会显示全 0 而非新分布块。**前端消费票**(改读 `statusDistribution`/`levelDistribution`/`industryDistribution`/`districtDistribution` 与新处置率语义) 需按票面验收第 2 条另立,与 mock 契约(`contractMocks.js` 的 `overviewWarningSettingsPayload`)同步改。 ## 关联 - 相关:[11 工单状态与处置率读模型对齐](11-backend-work-order-rate.md)(工单口径本身的修订)、决策 06(异常统计与业务语义)。 - 上位:本地票 25、Gogs #63。