Browse Source

docs(wayfinder): record ticket 71 post-deploy live verification (10/10)

后端重新编译重启后复验:41010500001320000002、TEST-CBYL5432168 由 WARNING 翻 WORK_ORDER;
TEST-TCLLJ002215 仍 WARNING、21119740 点位仍 ALARM;未关联设备业务轴 预警 3→1 / 工单 3→5;
monitoring/situation 不出现 WORK_ORDER(Q3 裁定成立)。

- 新增 verify-after-deploy.mjs(对照部署前预演表逐项 PASS/FAIL,可重跑回归)
- after-deploy/ 落盘复验时刻载荷(map-snapshot / situation)
- 票面 Answer + 部署后复验评论、地图 71 任务行与 Decisions 条目同步实测读数
- 注:map.md 是共享账本,本次同步只改 71 两处;同文件里票 67 的地图行更新属并行会话,随本文件一并入库
lead 1 day ago
parent
commit
57ebd8ca26

+ 27 - 2
.scratch/cockpit-real-data-wayfinding/issues/71-vs-52.md

@@ -73,6 +73,31 @@ Labels: wayfinder:task, backend-contract, bug
 ### local — 2026-10-23T01:32:45.711Z
 > *This was generated by AI during triage.*
 
+### local — 2026-10-23T03:22:04.161Z
+> *This was generated by AI during triage.*
+
+## 部署后真实复验(2026-10-23 11:19,后端已重新编译并重启)
+
+`verify/artifacts/t71/verify-after-deploy.mjs` **10/10 通过**(快照 `2026-10-23T11:19:44+08:00`,载荷 `verify/artifacts/t71/after-deploy/`):
+
+| 项 | 实测 | 结论 |
+| --- | --- | --- |
+| `41010500001320000002`(2 预警 + 1 未结束工单) | **`WORK_ORDER`**(改前 `WARNING`) | 改序生效 |
+| `TEST-CBYL5432168`(1 预警 + 1 未结束工单) | **`WORK_ORDER`**(改前 `WARNING`) | 改序生效 |
+| `TEST-TCLLJ002215`(1 预警、无工单) | `WARNING` | 未被误伤 |
+| `860347065467627` / `TEST_001` / `TEST-CBLL222222222` | `WORK_ORDER` | 不变 |
+| `41010500001320000001` / `8652052334` / `TEST-CNXHS0000001` | `NORMAL` / `OFFLINE` | 不变 |
+| `21119740` 所在点位 | **`ALARM`** | 票 52 窄三路分支不受影响 |
+| 未关联设备业务轴计数 | NORMAL 1 / WARNING 1 / **WORK_ORDER 5** / OFFLINE 2 | 预警 3→1、工单 3→5,与部署前预演表逐项一致 |
+| Q3 对照:`monitoring/situation` | `410105...002` 为 `WARNING`、`TEST-CBYL5432168` 为 `OFFLINE` | 均 ≠ `WORK_ORDER` → Q3「不跟随」成立 |
+
+**两条如实说明**
+1. `TEST-CBYL5432168` 的 situation 状态是 `OFFLINE` 而非 `WARNING`:该设备在线态已漂移为离线,situation 先判离线。「不跟随」的判据是**不出现 `WORK_ORDER`**,成立。
+2. 同一台设备地图上仍是 `WORK_ORDER`(离线 + 未结束工单)—— 这是**既有序**:工单判断本来就排在 online 之前,本票只调了 `warning` 与工单的先后,**没有**引入「离线设备显示处置中」的新行为。
+
+复验判据已固化在脚本里(对照部署前预演表逐项 PASS/FAIL),重跑即可回归。至此本票验收项全部闭环:裁定(Q1/Q2/Q3=A)、实施(`a416f4ab`)、测试 566/566、变异闸 4/4、真实读数复验 10/10。
+
+
 ## 实施记录(2026-10-23,后端已推送 `a416f4ab`)
 
 **裁定依据**:维护者 2026-10-22 Q1=A / Q2=A / Q3=A。
@@ -232,10 +257,10 @@ category 维持 `bug`(观测行为与业务口径不符);state 由 `needs-
 - **判定序落地**:非报警分支「未结束工单」压过「未解决预警」;报警分支(票 52 窄三路)原样保留。Q3 只在 `BusinessCockpitMonitoringSituationAdapter.metricStatus()` 注释留档,行为未动。
 - **契约测试**:新增 `CockpitMonitoringPointStatusOrderTest` 9 条;`Cockpit*Test,BusinessCockpit*Test` **566/566**。
 - **变异闸** `verify/t71-mutations.mjs` **4/4** 打红并字节还原(含「偷改枚举优先级」这条 Q2 pin)。
-- **真实复验:待部署**。2026-10-23 09:31 实测两台仍 `WARNING`、`21119740` 仍 `ALARM`;部署后按 `verify/artifacts/t71/expected-after-deploy.md` 对照,归票 17/18 窗口。
+- **真实复验:已通过(2026-10-23 11:19,后端重新编译重启后)**。`verify-after-deploy.mjs` **10/10**:两台由 `WARNING` 翻 `WORK_ORDER`;`TEST-TCLLJ002215` 仍 `WARNING`、`21119740` 点位仍 `ALARM`;未关联设备业务轴 预警 3→1、工单 3→5,与部署前预演表逐项一致;`monitoring/situation` 不出现 `WORK_ORDER`(Q3 成立)。载荷见 `verify/artifacts/t71/after-deploy/`。
 - **既有阻碍已清理**:rebase 拉入的 `3c734fe9` 使 `zksy-system` 测试源码编译不过(`WaterSupplyDeviceServiceImplTest` 两处多一个 `)`)。按维护者 2026-10-23 指令**直接删除该测试类**(提交 `a13dcaf5`,395 行 / 13 个测试随之移除,内容可从 `c654d3f9` 取回),`-pl zksy-admin -am` 闸门恢复,清理后 `Cockpit*Test,BusinessCockpit*Test` **566/566**、变异闸 **4/4** 复跑通过。
 
 ### 状态
 
-**resolved**(已实施、测试与变异闭环、已推送)。真实环境读数复验待后端部署,归票 17/18。
+**resolved**(已实施、测试与变异闭环、已推送、**真实读数复验已通过**)。验收项全部闭环;`verify/artifacts/t71/verify-after-deploy.mjs` 可随时重跑回归。
 

File diff suppressed because it is too large
+ 1 - 2
.scratch/cockpit-real-data-wayfinding/map.md


File diff suppressed because it is too large
+ 0 - 0
.scratch/cockpit-real-data-wayfinding/verify/artifacts/t71/after-deploy/map-snapshot.json


File diff suppressed because it is too large
+ 0 - 0
.scratch/cockpit-real-data-wayfinding/verify/artifacts/t71/after-deploy/situation.json


+ 21 - 0
.scratch/cockpit-real-data-wayfinding/verify/artifacts/t71/expected-after-deploy.md

@@ -31,3 +31,24 @@
 - 设备级 `equipment_status.alarm_status` **不在只读 API 暴露**,因此「报警标志」由发布状态反推:发布 `WARNING`/`WORK_ORDER` 的设备必然 `alarm_status≠1`(否则会先落 ALARM 分支)。这条反推只用于本预演,不写进代码。
 - 「未处理报警是否被逐条承接」通过 `/events/ALARM/{alarmId}/work-orders` 逐条核实,实测两条均无工单。
 - 预演**不是**验收证据:部署后必须重跑 `probe.mjs` 对照本表,才能算真实复验(归票 17/18 窗口)。
+
+## 实测复验(2026-10-23 11:19,后端已重新编译并重启)
+
+`verify-after-deploy.mjs` **10/10** 通过(快照 `2026-10-23T11:19:44+08:00`,载荷 `after-deploy/`):
+
+| 项 | 实测 | 结论 |
+| --- | --- | --- |
+| `41010500001320000002` | **WORK_ORDER** | 与预演一致(改序生效) |
+| `TEST-CBYL5432168` | **WORK_ORDER** | 与预演一致(改序生效) |
+| `TEST-TCLLJ002215` | WARNING | 与预演一致(只有预警、无工单) |
+| `860347065467627` / `TEST_001` / `TEST-CBLL222222222` | WORK_ORDER | 不变 |
+| `41010500001320000001` | NORMAL | 不变 |
+| `8652052334` / `TEST-CNXHS0000001` | OFFLINE | 不变 |
+| `21119740` 所在点位 | **ALARM** | 票 52 分支不受改序影响 |
+| 未关联设备业务轴 | NORMAL 1 / WARNING 1 / **WORK_ORDER 5** / OFFLINE 2 | 预警 3→1、工单 3→5,与预演一致 |
+| Q3 对照 | `410105...002` situation=`WARNING`;`TEST-CBYL5432168` situation=`OFFLINE` | 均 ≠ `WORK_ORDER`,situation 不跟随(Q3 成立) |
+
+两条如实说明:
+
+1. `TEST-CBYL5432168` 的 situation 状态是 `OFFLINE` 而非 `WARNING`:该设备在线态已漂移为离线,situation 先判离线。「不跟随」的判据是**不出现 `WORK_ORDER`**,成立。
+2. 同一台设备在地图上仍是 `WORK_ORDER`(离线 + 未结束工单)—— 这是**既有序**:工单判断本来就排在 online 之前,票 71 只调了 `warning` 与工单的先后,没有引入「离线设备显示处置中」的新行为。

+ 73 - 0
.scratch/cockpit-real-data-wayfinding/verify/artifacts/t71/verify-after-deploy.mjs

@@ -0,0 +1,73 @@
+/**
+ * 票 71 部署后真实复验(2026-10-23)。
+ *
+ * 对照 verify/artifacts/t71/expected-after-deploy.md 的部署前预演表,逐项判 PASS/FAIL:
+ *  1. map-snapshot 里「预警未解决 + 未结束工单、无报警」的两台设备应已翻为 WORK_ORDER;
+ *  2. 只有预警、没有工单的设备仍 WARNING;
+ *  3. 报警设备 21119740 的点位仍 ALARM(票 52 分支不受改序影响);
+ *  4. 未关联设备业务轴计数:预警 3→1、工单 3→5;
+ *  5. Q3 留档:monitoring/situation 的 metricStatus 不跟随(两台仍是 WARNING)。
+ *
+ * 用法:node verify-after-deploy.mjs
+ */
+import fs from 'node:fs'
+
+const BASE = 'http://192.168.110.96:8301/api/cockpit/v1'
+const OUT = 'after-deploy'
+fs.mkdirSync(OUT, { recursive: true })
+
+const get = async (path, file) => {
+  const res = await fetch(BASE + path)
+  const text = await res.text()
+  fs.writeFileSync(`${OUT}/${file}`, text)
+  return JSON.parse(text)
+}
+
+const snap = (await get('/map-snapshot?page=1&pageSize=1000', 'map-snapshot.json')).data
+const situation = (await get('/monitoring/situation', 'situation.json')).data
+
+const devices = new Map(snap.unassociatedEquipment.map((d) => [d.deviceCode, d.deviceStatus]))
+const sortKeys = (o) => Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b)))
+const counts = {}
+for (const status of devices.values()) counts[status] = (counts[status] || 0) + 1
+
+const checks = []
+const check = (name, actual, expected) => {
+  const ok = JSON.stringify(actual) === JSON.stringify(expected)
+  checks.push({ name, ok, actual, expected })
+  console.log(`${ok ? 'PASS' : 'FAIL'} | ${name} | 实测 ${JSON.stringify(actual)} | 预期 ${JSON.stringify(expected)}`)
+}
+
+console.log('snapshotAt:', snap.snapshotAt)
+check('41010500001320000002 应翻 WORK_ORDER', devices.get('41010500001320000002'), 'WORK_ORDER')
+check('TEST-CBYL5432168 应翻 WORK_ORDER', devices.get('TEST-CBYL5432168'), 'WORK_ORDER')
+check('TEST-TCLLJ002215 无工单应仍 WARNING', devices.get('TEST-TCLLJ002215'), 'WARNING')
+check('860347065467627 有工单无预警应仍 WORK_ORDER', devices.get('860347065467627'), 'WORK_ORDER')
+check('41010500001320000001 应仍 NORMAL', devices.get('41010500001320000001'), 'NORMAL')
+check('8652052334 应仍 OFFLINE', devices.get('8652052334'), 'OFFLINE')
+
+const alarmPoint = snap.monitoringPoints.find((p) => (p.deviceCodes || []).includes('21119740'))
+check('21119740 所在点位应仍 ALARM', alarmPoint && alarmPoint.pointStatus, 'ALARM')
+
+check('未关联设备业务轴计数', sortKeys(counts), sortKeys({ NORMAL: 1, WARNING: 1, WORK_ORDER: 5, OFFLINE: 2 }))
+
+// Q3:situation 按事实判,不跟随业务序 —— 该端点的设备状态只在异常设备清单里(status 字段)
+const abnormal = situation.abnormalEquipment || []
+const statusInSituation = (code) => {
+  const row = abnormal.find((d) => d.deviceCode === code)
+  return row ? row.status : 'NOT_LISTED'
+}
+// 「不跟随」的判据是**不出现 WORK_ORDER**(situation 没有这个桶,按事实落 WARNING/ALARM/OFFLINE),
+// 不钉死具体是 WARNING:设备在线态是独立事实,会随现网漂移(CBYL 今日即被判 OFFLINE)。
+const checkNotFollowing = (name, code) => {
+  const actual = statusInSituation(code)
+  const ok = actual !== 'WORK_ORDER'
+  checks.push({ name, ok, actual, expected: '≠ WORK_ORDER' })
+  console.log(`${ok ? 'PASS' : 'FAIL'} | ${name} | 实测 ${actual} | 预期 ≠ WORK_ORDER`)
+}
+checkNotFollowing('Q3: 41010500001320000002 的 situation 状态不跟随业务序', '41010500001320000002')
+checkNotFollowing('Q3: TEST-CBYL5432168 的 situation 状态不跟随业务序', 'TEST-CBYL5432168')
+
+const failed = checks.filter((c) => !c.ok)
+console.log(`\n复验 ${checks.length - failed.length}/${checks.length} 通过`)
+process.exit(failed.length === 0 ? 0 : 1)

Some files were not shown because too many files changed in this diff