17-postdeploy-live-verification.md 16 KB

17: 部署后真实环境复验(后端切片 11–13)

Type: task Status: resolved Label: ready-for-human Blocked by: 11 Category: backend-execution

本票为滚动复验票,贯穿票 11–13:每张后端切片落地并部署后,其新契约复验追加到本票(同一部署窗口、同一环境),不另开复验票。当前进度:票 11 复验已通过(2026-10-08);票 12 复验已通过(2026-10-09,8 项全通过);票 13 复验已通过(2026-10-09,6 项全通过,含真人触发状态变化的端到端帧捕获,见下)。三切片全部复验通过 → 滚动使命完成,本票 resolved;决策 01 的真人闭环演示归阶段总收口(票 18 上位阶段),不在本票范围。

复验进度

切片 复验项 状态
票 11(工单状态与处置率) handling-efficiency 新 basis + 工单分母;history-analysis 汇总;overview/warning-settings 两 family 已通过(2026-10-08,见下)
票 12(报警/工单读模型 + district) 六族 district 字段出现、region 入参被忽略、脏值 district=9→400、管理信息无 organizations、工单显式关联标记 已通过(2026-10-09,8 项全通过,见下)
票 13(报警/工单实时同步) WS 直订回执;扫描任务健康;无变化不推送;真人触发状态变化后收到 cockpitWorkOrderStateDelta 帧;设备帧 eventId 字段 已通过(2026-10-09,6 项全通过,见下)

票 13 复验结论(2026-10-09,已通过)

六项复验全部符合票 13 的新契约。 部署依据:origin/master = d8a6b03;运行进程 PID 20240,启动于 13:40:58(13:41:14 Started),晚于提交。

复验项 实测 判定
新构建加载 PID 20240 启动 13:41:14 > 提交 d8a6b03;日志无调度任务异常 通过
WS 直订回执 连接 /ws/deviceStatus → cockpitSubscribe → cockpitSubscribed(13:49:38 与 13:52:23 两次) 通过
无变化不推送 订阅保持 75 秒(一个完整扫描周期):仅收回执 1 帧、零增量帧;复查库确认 7 张工单状态零变化(13:48:56 vs 13:50:53) 通过
不改状态的操作不触发(意外强化) 13:55:47 真人「处置」284(只改 update_time、orderStatus 仍 3)→ 无帧——证明增量检测由 orderStatus 驱动、不被 update_time 误导,正是设计意图 通过
真人状态变化 → 端到端帧到达 13:57:54 真人对 284 提交验收(3→4);13:58:34 收到帧(延迟 40s,在 scanIntervalMs=60000 标注上限内):workOrderCode=WO202610091014453765、stateCode=4、stateName=待验收(字典单源)、deviceCode=TEST-CBYL5432168;帧内无 orderId/updateTime(按设计);items 只含变化那一张(283/285 未入帧) 通过
设备帧 eventId 字段 契约与序列化(显式 null)已由单元测试锁定;线上是否携带由发布方(zk-api-service)决定——复验窗口内无设备状态发布,无法实测携带值,如实记录 契约通过;携带值待外部发布事件

基线卡片 04 的阶梯已由 ② 升 ③(真实数据联调完成) —— 该判定针对票 13 切片。决策 01 的「真实设备报警闭环真人演示」是阶段总收口事项(报警产生→关联生成工单→推进→驾驶舱呈现全链路),不在本票复验范围。

本次复验方法论(供后续切片借鉴)

  • 负向观察必须闭环:说「没收到帧」必须同时复查库证明「确实没变化」,否则「没帧」与「链路坏」不可区分。
  • 临时探针一律放 %TEMP%(不进 git 跟踪目录),用完即删——本图早前曾把含明文口令的探针留在 .scratch,已纠正为纪律。

票 12 复验结论(2026-10-09,已通过)

八项复验全部符合票 12 的新契约。 部署依据:origin/master = 818a6d7(含票 12 的 ee2e1b6+18db910);运行进程 PID 15244(重启后加载新字节码)。

复验项 实测 判定
六族端点健康 7/7 全部 HTTP 200 通过
district 字段出现 mapFilter 含 district/districtName/districtFilter;region 已消失 通过
筛选生效 district=0→4、1→2、2→1、UNASSIGNED→6;合计 7+6=13 == 全量(自洽) 通过
脏值 400 district=9 / -1 / 城区B 全部 400 通过
旧 region 被忽略 region=城南 → 200 且 districtFilter=NONE(不生效、不报错) 通过
管理信息删字段 顶层无 organizations/ownershipDistribution;districtIndustry[] 7 条(含「未分区」桶) 通过
点位 district 归纳 equipment_point_rel 0 行 → 点位全部「未分区」(如实,非缺陷) 通过
工单显式关联标记 设备维度逐张 associatedWithCurrentEvent;报警维度按真实 UUID 回 READY+1 张;unassociatedWorkOrderCount 同源 通过

基线卡片 03 的阶梯已到 ③(真实数据联调完成) —— 该判定针对票 12 切片。④「模式整体收口」仍待真人验收(决策票 01)与前端接线(票 14–16)。

复验中的一处测量错误(如实登记,供后续复验借鉴)

复验时我一度依据「alarm_data.id 全部是同一个全零占位值」提交了 905f593,把报警定位从「按主键 id」改为「按 alarm_no」。该观测是错的:它是控制台把 32 位十六进制串统一渲染成 0 的显示假象,连 md5(id) 都被同样渲染(PowerShell 输出为 000...0)。

用不可被渲染扭曲的方式复核后确认 6 行 id 是互不相同的真实 UUID:count(DISTINCT id)=6、字符直方图显示非零字符数 30/29/32/29/32/30、base64 解出 fca8fd6b…/8b5c81fc…(与端点回显一致)。实测 events/ALARM/{真UUID}/work-orders 回 READY+1 张工单,证明原按 id 解析本就是对的。

已以 18db910 撤回。复验纪律:判「两个值是否相同/是否为零」时,禁止凭肉眼读控制台长十六进制串,必须用 count(DISTINCT)、直方图或 base64。

票 11 复验结论(2026-10-08,已通过)

三项复验全部符合票 11 的新契约。 部署依据:origin/master = 89ca989(含票 11 的 3de679c 与上游 7df9a87);运行进程 PID 26956,启动于 18:39:11(重启后加载新字节码)。实测命令与结果见下方「实测记录」。

基线卡片 02 的阶梯已由 ② 升到 ③(真实数据联调完成) —— 该判定针对票 11 切片。④「模式整体收口」仍待真人验收(决策票 01)与前端接线(票 14–16)。

复验项 判据 实测 判定
events/handling-efficiency basis 改名;sum(状态0–6)==denominator;分母≠事件数 ALARM:basis=CLOSED_WORK_ORDERS_OVER_ELIGIBLE_WORK_ORDERS、num/den=2/3、value=0.6667、sum(0..6)=3==分母、totalEvents=6≠分母 通过
events/history-analysis 汇总为新口径;小时桶仍事件桶(预期不变) 汇总 num/den=2/3、value=0.6667;行级 reason=NO_EVENTS(事件桶) 通过
overview/warning-settings 两 family 共用同一份全范围工单读数 warning 与 alarm 均 num/den=2/3;事件计数各算(10 / 6) 通过

同时确认:票 11 的口径单源 CockpitWorkOrderStateDictionary(HANDLING_RATE_BASIS / CLOSED_RATE_CODE=5 / isRateEligible)与三处服务改造(各命中 5 处 isRateEligible/scopeWorkOrders/scopeEfficiency)在部署版本中完整存活 —— 上游 7df9a87 未触及口径相关代码。

数据稀疏事实(如实记录,不当作缺陷):work_order 表仅 3 行(状态 1×1、5×2),故分母为 3。分母 ≠ 2(早先勘误已说明:那 2 是旧「事件回联」计数,与新口径的「全部工单」基数不同)。样本极小,本票只验证口径与自洽性,不宣称统计意义。

部署过程中的一次真实环境故障(已解决,如实登记)

重启窗口内曾出现 5 个只读端点 500(events/* 与 overview/warning-settings),根因是数据库表结构重构与 Java 层不匹配:

  • app_user.equipment_warning_type 已重构为关联表(equipment_type_id + warning_type_id),指标元数据移到新的 app_user.warning_type 表;
  • app_user.alarm_data.warning_type 列已删除(warning_code 仍在)。

旧基点 9380c1b 的代码仍按重构前的单表形状映射,BaseMapper 生成的 SELECT id,...,warning_type,... FROM equipment_warning_type 与 alarm_data 查询因此报「字段不存在」。

该重构已由上游 7df9a87「优化报警管理」 完整修复(新增 EquipmentWarningType 关联表实体、AlarmData 删除 warning_type 并新增非持久化 warningName、WarningThresholdServiceImpl 与 4 个 XML mapper 一并改到新结构)。本会话曾基于旧基点尝试局部修复(分支 fix/cockpit-warning-type-split),发现上游已有完整解后已废弃该分支(未推送、无损失),跟随 master。

教训(对后续复验有用):复验前必须先 git fetch 确认远端基线,不要在落后于远端的基点上诊断「环境坏了」。当时把上游的新结构误读为「改了一半的迁移」,方向判断反了。

为什么有这张票

执行票 11 的实施已交付(代码提交 + 编译 + 契约测试 + 基线卡片),但「新契约在真实部署环境生效」需要重新部署才能观察。把这件事留在票 11 里,会让「已完成的实现」与「尚未发生的部署」共用一张票,进而阻塞下游 执行票 12 / 13 的启动。

本票把该复验单独承载:票 11 按其自身交付物收口,本票负责把真实环境证据补齐、把基线阶梯从 ② 推上 ③。本票不阻塞 12/13 —— 12 只依赖票 11。

前置(人工)

  1. 部署新构建到联调环境 http://192.168.110.96:8301。部署需维护者显式确认(本仓库规则:部署不自动执行)。
  2. 确认部署所依据的提交包含票 11 的口径修订 3de679c(及其后的 9380c1b),即远端 master ≥ 9380c1b。

复验内容(可复制命令)

按基线卡片 02 工单状态与处置率读模型 §4.1 复跑:

$base='http://192.168.110.96:8301/api/cockpit/v1'
$a = Invoke-WebRequest "$base/events/handling-efficiency?eventFamily=ALARM&startTime=2026-09-08T00:00:00%2B08:00&endTime=2026-10-08T00:00:00%2B08:00" -UseBasicParsing
($a.Content | ConvertFrom-Json).data.summary

期望(票 11 的新契约):

字段 期望值
handlingEfficiency.basis CLOSED_WORK_ORDERS_OVER_ELIGIBLE_WORK_ORDERS
handlingEfficiency.denominator 工单数(状态 0–6),不再等于 totalEvents
handlingEfficiency.state AVAILABLE(有分母)/EMPTY + reason=NO_ELIGIBLE_WORK_ORDERS(分母 0)
workOrderStateCounts sum(状态 0–6) == denominator,stateCounts[7] 不计入分母

部署前实测对照(旧口径,供比对):同请求回 basis=CLOSED_WORK_ORDERS_OVER_TOTAL_EVENTS、denominator=6(=totalEvents)、value=0.1667;当次 workOrderStateCounts 为 0=1, 5=1(事件回联计数)。

勘误(2026-10-08,来自票 11 的更正):本行原写「新口径应为 denominator=2、value=0.5000」,该预期错误。那个 2 是从旧响应的 stateCounts(0=1, 5=1)反推的,但旧 stateCounts 只统计已关联报警的工单(未关联者 continue 跳过)且按事件累加(同一工单多事件重复计);新口径分母取自 scopeWorkOrders,含范围内全部工单(包括未关联报警者),且按工单计数。两者基数不同,不可互推。

因此上表「denominator = 工单数(状态 0–6),不再等于 totalEvents」与「sum(状态 0–6) == denominator」是正确判据;不得把「分母等于某个具体数值」当作通过条件 —— 部署后分母随真实工单数据变化,通常大于 2。请以自洽性(sum(0–6) == denominator 且分母 ≠ 事件数)判定。

同时复核票 11 另两处同族改动未被部署遗漏:

  • GET /events/history-analysis?eventFamily=WARNING&startTime=…&endTime=… → summary.handlingEfficiency 为新 basis/工单分母;小时桶仍为事件桶(预期不变)。
  • GET /overview/warning-settings?mode=WARNING → handlingEfficiency 两 family 共用同一份全范围工单读数。

判定与回写

本票为滚动复验票,判定按切片记录,不因单一切片通过而关闭本票。

  • 票 11 切片:全部符合期望(2026-10-08)→ 已回写:基线卡片 02 §4.3 改写为实测结果、§2 收口条件⑤ 置为完成、§7 阶梯升至 ③ 真实数据联调完成;票 11 的「部署后复验」缺口标记为已补齐。本票保持开放,等待票 12/13 追加。
  • 后续切片(12/13)落地并部署后:在本票「复验进度」表追加行、在文末追加各自的实测记录;票 12 的复验项另起基线卡片(按决策票 05 模板)。
  • 不符合期望时 → 不改基线卡片结论、不升阶梯;把实测值与原期望的差异写进本票并回写对应执行票。不在本票内改后端代码:发现问题按正常流程另开修复或重开该执行票。

实测记录(票 11 切片,2026-10-08,可复制命令)

$base='http://192.168.110.96:8301/api/cockpit/v1'
$end=(Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz'); $start=(Get-Date).AddDays(-31).ToString('yyyy-MM-ddTHH:mm:sszzz')
$qs="startTime=$([uri]::EscapeDataString($start))&endTime=$([uri]::EscapeDataString($end))"

# ① 效率端点
Invoke-RestMethod "$base/events/handling-efficiency?eventFamily=ALARM&$qs" | % { $_.data.summary }
# ② 历史分析(汇总 vs 行级)
Invoke-RestMethod "$base/events/history-analysis?eventFamily=WARNING&$qs" | % { $_.data.summary }
# ③ 预警设置(两 family)
Invoke-RestMethod "$base/overview/warning-settings" | % { $_.data.warning.handlingEfficiency; $_.data.alarm.handlingEfficiency }

结果:

端点 关键实测值
events/handling-efficiency?eventFamily=ALARM basis=CLOSED_WORK_ORDERS_OVER_ELIGIBLE_WORK_ORDERS;num/den=2/3;value=0.6667;state=AVAILABLE;stateCounts: 1=1 5=2;sum(0..6)=3==分母;totalEvents=6
events/handling-efficiency?eventFamily=WARNING 同上(工单范围与 family 无关,符合决策 06)
events/history-analysis?eventFamily=WARNING 汇总 num/den=2/3、value=0.6667;行级 effState=EMPTY、reason=NO_EVENTS(事件桶未变)
overview/warning-settings warning 与 alarm 均 num/den=2/3;warning.total=10 / alarm.total=6(事件各算)
events/current、events/multidimensional-analysis 均 HTTP 200(500 故障已随上游 7df9a87 解决)

非目标

  • 不实施 执行票 12 / 13 的功能(read model、district、实时同步)。
  • 不把 mock 走查或本机证据当作真实环境结论。
  • 不做真人设备报警闭环验收(按决策票 01,那是阶段总收口)。

后续扩展

票 12 / 13 落地并部署后,各自的新契约复验追加到本票(同一部署窗口、同一环境),不另开复验票,避免票据碎片化。票 12 的复验项按 决策票 05 的基线卡片模板另起卡片。

关联