按决策票 05 真实联调基线与收口证据门槛 的固定骨架产出,对应执行票 13 报警/工单实时同步。状态判定:③ 真实数据联调完成(2026-10-09 部署后复验 6 项全通过,含真人触发状态变化的端到端帧捕获;④ 模式整体收口仍待真人验收与前端接线)。
| 项 | 值 |
|---|---|
| 记录时间 | 2026-10-09(本地时钟) |
| 记录角色 | 后端执行代理(实施 + 编译 + 契约测试 + 部署后复验) |
| 联调环境 baseUrl | http://192.168.110.96:8301(= 本机,IDE 内运行的 Spring Boot 进程) |
| 后端仓库 / 分支 / 提交 | http://192.168.110.30:3000/shaoy/pipe-ner-server / master / d8a6b03(已推送 4ef2159..d8a6b03) |
| 运行实例 | PID 20240,启动于 2026-10-09 13:40:58(已加载新构建) |
| 访问控制 | 驾驶舱只读 REST/WS 免鉴权(ADR 0005);本票不改鉴权范围 |
| 只读边界 | 未触碰工单创建/派单/接单/处置/验收/延期/取消逻辑(决策 01 非目标) |
| # | 条件 | 状态 | 证据 | 未完成项 |
|---|---|---|---|---|
| ① | 后端提交 / 编译 / 契约测试 | 已完成 | 编译通过;针对性契约测试 21/21;全量 8 模块 422/422;d8a6b03 已推送 |
无 |
| ② | REST 快照复用 | 已完成(无新端点) | 票 11/12 交付的读模型即快照源:/events/current、/equipments/{code}/work-orders、/events/{source}/{code}/work-orders、/work-orders/{code}/timeline |
无 |
| ③ | WS 增量帧 | 已完成(部署后实测) | 真人操作 284 工单(3→4)→ 40 秒内 cockpitWorkOrderStateDelta 帧到达,字段全对;无变化/不改状态的操作均正确不推送 |
设备帧 eventId 线上携带值待外部发布事件(契约已锁定) |
| ④ | 诚实降级 | 已完成(契约层 + 实测) | 帧带 scanIntervalMs 标注延迟上限(实测延迟 40s < 60s 上限);无订阅不查库;断线重订、失效会话清理有测试 |
前端消费侧呈现归票 16 |
| ⑤ | 真实设备报警闭环 | 不归本票 | 按决策 01 由真人团队执行 | 阶段总收口(票 18 上位阶段) |
cockpitWorkOrderStateDelta 工单状态增量帧| 项 | 决策 | 依据 |
|---|---|---|
| 产生机制 | 定时扫描 work_order.order_status 逐单比对(默认 60s,websocket.cockpit-work-order-delta.scan-interval-ms 可配) |
状态流转不写 update_time(transition() 只 set orderStatus、无 FieldFill),靠时间过滤会漏掉全部流转——诊断实测核实 |
| 推送对象 | 驾驶舱全量订阅会话(免鉴权 scope=all),不做按人可见性过滤 | 与管理端 workOrderReminderPush(需鉴权+按人过滤)明确区分:内容是状态增量,不是提醒清单 |
| 首扫语义 | 只建基线不推送 | REST 才是初始快照(与 cockpitSubscribe 既有语义一致);WS 只收后续变化 |
| 变化判定 | orderStatus 与上次扫描不同,或工单新出现 | 新工单也是状态变化(0-待派单入场) |
| 帧形状 | type/scanIntervalMs/serverUpdatedAt/data{items[{workOrderCode,deviceCode,stateCode,stateName}],total} |
对外键 workOrderCode(order_no),不泄漏数据库主键 orderId(与 REST 读模型同口径) |
| 诚实边界 | 帧不带工单 updateTime(流转不更新该列,携带会误导);变化时刻=帧级 serverUpdatedAt(扫描时间);脏状态码原样上报、stateName 显式 null |
不给消费方看起来可信实则不更新的时间;不编造名称 |
| 状态字典 | stateCode/stateName 出自 CockpitWorkOrderStateDictionary(0–7 单源,票 11) |
与 REST 读模型完全一致 |
| 降级与重连 | 无全量订阅会话时不查库;发送失败的失效会话被清出订阅集合;错过的增量由消费方 REST 重取兜底 | 先看会话再扫表(同 WorkOrderWebSocketPushTask 纪律);断线后重订即可 |
cockpitDeviceStatusDelta 补 eventId 透传Redis 消息(device:status:change)携带 eventId 时透传进驾驶舱帧,缺省显式 null(WriteMapNullValue 序列化,不静默丢)——前端收到 status=ALARM 增量后可精确重取该报警详情,而非盲刷列表。是否携带由发布方(zk-api-service)决定,后端不臆造。
cockpitDeviceStatusDelta(设备在线/报警状态)与 cockpitWorkOrderStateDelta(工单状态)帧类型不同、语义不同,文档明言不得互为冒充;REST 快照与 WS 增量的分工(快照=REST、增量=WS)在两帧上一致。
cd E:\pipe-ner-server
mvn -o -pl pipe-network-service/zksy-admin -am test "-Dtest=CockpitWorkOrderDeltaPushTaskTest,DeviceStatusCockpitWebSocketHandlerTest" "-DfailIfNoTests=false"
mvn -o -pl pipe-network-service/zksy-admin -am test # 全量
结果:针对性 21/21(新增 8 例);全量 8 模块 422/422(基线 414 + 8)。
新增契约覆盖(CockpitWorkOrderDeltaPushTaskTest 7 例 + 监听器 1 例):
firstScanEstablishesBaselineWithoutPush —— REST 才是快照,首扫零推送stateChangePushesDeltaWithDictionaryStateNames —— 变化推送 + 字典名称 + scanIntervalMs + 不泄漏 orderIdnewlyAppearingOrderIsPushedAsDelta —— 新工单入场即增量;无设备关联时 deviceCode 显式 nullunchangedOrdersProduceNoSecondPush —— 无变化不推unknownStateCodeKeepsRawCodeWithExplicitNullName —— 脏码原样、名称 nullnoCockpitSessionMeansNoDatabaseScan —— 无订阅不查库(降级纪律)staleAllScopeSessionIsPrunedWhenSendFails —— 失效会话清理(重连语义)cockpitDeltaCarriesEventIdWhenPresentAndExplicitNullWhenAbsent —— eventId 透传与显式 nulld8a6b03)# WS 直订(System.Net.WebSockets.ClientWebSocket,PowerShell)
$ws.ConnectAsync([Uri]'ws://192.168.110.96:8301/ws/deviceStatus', …)
→ {"type":"cockpitSubscribe","scope":"all"}
← {"type":"cockpitSubscribed",…}
# 保持订阅监听帧;同时用只读 JDBC 探针快照 work_order.order_status 对照
结果(6 项全通过):
| 复验项 | 实测 |
|---|---|
| 新构建加载 | PID 20240 启动 13:41:14 > 提交;日志无调度任务异常 |
| WS 直订回执 | cockpitSubscribed(13:49:38 与 13:52:23 两次) |
| 无变化不推送 | 订阅 75 秒零增量帧;复查库确认 7 张工单零变化(13:48:56 vs 13:50:53) |
| 不改状态的操作不触发 | 13:55:47 真人「处置」284(仅 update_time 变、状态码仍 3)→ 无帧(检测由 orderStatus 驱动的正面证明) |
| 真人状态变化 → 端到端帧 | 13:57:54 提交验收(3→4)→ 13:58:34 帧到达(延迟 40s < scanIntervalMs=60000):{"type":"cockpitWorkOrderStateDelta","scanIntervalMs":60000,"serverUpdatedAt":"2026-10-09T13:58:34+08:00","data":{"total":1,"items":[{"workOrderCode":"WO202610091014453765","stateCode":4,"stateName":"待验收","deviceCode":"TEST-CBYL5432168"}]}}帧内无 orderId/updateTime;items 只含变化那一张 |
设备帧 eventId |
契约与显式 null 已由测试锁定;线上携带值待外部发布事件(窗口内无设备状态发布) |
闭环演示未执行(归决策 01 的真人团队阶段总收口)。但本票复验包含一次真人真实操作:维护者于 13:57:54 在管理端对工单 284 提交验收(状态 3→4),驾驶舱侧 40 秒内收到增量帧——这是真实业务路径的端到端证据,非模拟。
cockpitWorkOrderStateDelta(按 workOrderCode 合并、scanIntervalMs 标注延迟);cockpitDeviceStatusDelta.eventId 用于精确重取报警。前端对未知帧静默忽略,不升级前端也不会崩。eventId 是否真的由 zk-api-service 发布未证实——透传是防御性的;复验窗口内无设备状态发布事件,线上携带值待外部发布时观察(契约与显式 null 已由测试锁定)。当前状态:③ 真实数据联调完成(2026-10-09 达成)。(④ 模式整体收口仍待真人验收与前端接线。)
依据:编译 + 422/422 全量测试 + 契约测试通过,且部署后真实环境复验 6 项全通过——含端到端正面证据(真人操作 3→4 后 40 秒帧到达、字段全对)与两项负向证据(无变化不推送、不改状态码的操作不触发,证明增量由 orderStatus 驱动)。
不进入 ④ 的原因:④ 要求模式整体收口(真人验收 + 前端接线 + 阶段联调)——本卡片只覆盖后端切片 3;前端 real 生命周期接线(票 14–16)与真人设备报警闭环(决策 01)尚未发生。