Type: task Status: resolved Label: ready-for-agent Blocked by: 12 Category: backend-execution
已 resolved(2026-10-09):诊断 + 2 项维护者裁定 + 实施 + 契约测试 + 推送 + 真实环境部署复验 6 项全通过。提交 d8a6b03 已推 origin/master(4ef2159..d8a6b03)。验证:针对性契约测试 21/21(新增 8 例)、全量 8 模块 422/422。基线卡片:报警/工单实时同步(状态 ③ 真实数据联调完成)。复验明细记录在 票 17;上位回写(票 25 / Gogs #63)已按决策 01 执行(Gogs 侧需负责人同步)。
d8a6b03)| 复验项 | 实测 | 判定 |
|---|---|---|
| 新构建加载 | PID 20240 启动 13:41:14 > 提交;日志无调度任务异常 | 通过 |
| WS 直订回执 | cockpitSubscribe → cockpitSubscribed(两次实测) |
通过 |
| 无变化不推送 | 订阅 75 秒零增量帧;复查库确认 7 张工单零变化 | 通过 |
| 不改状态的操作不触发 | 13:55:47 真人「处置」284(仅 update_time 变、状态码不变)→ 无帧:证明增量由 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 只含变化那一张 |
通过 |
设备帧 eventId |
契约与显式 null 序列化已由测试锁定;线上携带值取决于外部发布方(zk-api-service),复验窗口无设备状态发布,如实记录为「契约通过、携带值待外部事件」 | 契约通过 |
决策 01 的「真实设备报警闭环真人演示」(报警产生→关联生成工单→推进→驾驶舱呈现全链路)是阶段总收口事项,不在本票验收内——本票已交付其驾驶舱侧全部依赖(REST 快照 + 设备增量 + 工单状态增量)。
按维护者裁定实施,共 6 个文件(2 新增 + 4 修改):
| 交付 | 内容 |
|---|---|
新增 cockpitWorkOrderStateDelta 帧 |
CockpitWorkOrderDeltaPushTask:定时扫描 work_order.order_status 逐单比对(60s 可配)——工单状态流转不写 update_time(诊断核实),靠时间过滤会漏掉全部流转,故必须比对状态。只有真实变化才推给驾驶舱全量订阅会话(免鉴权 scope=all,不做按人过滤,与管理端 workOrderReminderPush 语义隔离);首扫只建基线不推送(REST 才是初始快照);帧带 scanIntervalMs 诚实标注延迟上限、不带会误导的 updateTime、不泄漏主键 orderId;stateCode/stateName 出自票 11 字典单源,脏码原样上报名称显式 null |
设备帧补 eventId 透传 |
cockpitDeviceStatusDelta 在 Redis 消息携带 eventId 时透传(前端可精确重取该报警),缺省显式 null 不静默丢;是否携带由发布方(zk-api-service)决定,不臆造 |
| 范围 1 REST 快照 | 复用票 11/12 读模型,无新端点 |
| 范围 3 诚实降级/重连 | 无全量订阅会话时不查库;发送失败的失效会话被清出订阅集合(测试锁定);断线后重订即可,错过的增量由 REST 重取兜底 |
| 范围 4 契约测试 | 新增 8 例(基线不推送/变化推送/新单推送/无变化不推/脏码/无会话不查库/失效会话清理/eventId 透传) |
| 文档 | DeviceStatusWebSocketHandler 协议 javadoc + docs/cockpit/README.md(新帧示例 + 「工单状态增量口径」章节) |
对抗式自查修正一处:初版帧携带数据库主键 orderId,但驾驶舱 REST 读模型从不暴露它(对外键是 workOrderCode)——已移除并加测试断言「不泄漏 orderId」。
cockpitDeviceStatusDelta(设备在线/报警状态)与 cockpitWorkOrderStateDelta(工单状态)帧类型不同、语义不同,不得互为冒充;REST=快照、WS=增量的分工在两帧上一致。
| 项 | 值 |
|---|---|
| 仓库 / 分支 / 提交 | origin/master / master / 4ef2159(工作树干净) |
| 联调环境 | http://192.168.110.96:8301 实测可达 |
| 能力 | 状态 | 证据 |
|---|---|---|
| 范围 1:REST 快照 | 已具备(票 11/12 交付) | /events/current、/equipments/{code}/work-orders、/events/{source}/{code}/work-orders、/work-orders/{code}/timeline;设备/点位快照走 /map-snapshot。无需新端点 |
| WS 连接/直订/重连生命周期 | 已具备 | /ws/deviceStatus + cockpitSubscribe(scope=all 免鉴权,ADR 0005)+ cockpitSubscribed 回执;前端 runtimeSocket.js 退避重连;后端会话清理(afterConnectionClosed/handleTransportError) |
| 设备状态增量帧 | 已具备 | cockpitDeviceStatusDelta:Redis device:status:change 事件驱动(外部 zk-api-service 发布),帧含 deviceCode/monitoringPointCode/data{status,statusTime,serverUpdatedAt,reason},DeviceStatusDeltaTracker 按设备去重只推真变化 |
| 工单状态增量帧 | 不存在 | 唯一工单推送 workOrderReminderPush(WorkOrderWebSocketPushTask)是管理端提醒:需 authenticate 鉴权、按部门/责任人过滤可见性、内容是待处理/超时清单而非状态增量——驾驶舱(免鉴权、scope=all)收不到,也不该收 |
| 报警数据增量帧 | 无独立通道 | 报警记录由外部 zk-api-service 写入,唯一信号是设备级 device:status:change;旧版 deviceStatusPush 在 Redis 消息携带 eventId 时透传,驾驶舱 cockpitDeviceStatusDelta 不带 eventId |
| 前端兼容性 | 新增帧零破坏 | 前端 handleFrame 只认 cockpitSubscribed/cockpitDeviceStatusDelta/pong,未知帧静默忽略(注释明言「后端将来加帧类型不该让前端崩」) |
work_order.update_time 在状态流转时不更新:transition()(派单/接单/处置/验收/取消共用)只 .set(orderStatus),仅创建路径写 updateTime,实体无 FieldFill 自动填充。→ 扫描式增量不能靠 update_time 过滤,必须逐单比对 orderStatus(既有 WorkOrderWebSocketPushTask 的指纹法正是这么做的)。WorkOrderWebSocketPushTask(60s 可配扫描 + 指纹比对 + 变化才推 + 会话清理)是被测验证过的基础设施,可复用其模式给驾驶舱全量订阅会话。zksy-system(WorkOrderServiceImpl),WS 处理器在 zksy-admin,模块依赖方向不允许直连;改写路径还触碰本票非目标(「不修改工单操作逻辑」)。cockpitWorkOrderStateDelta 帧(扫描-比对-推送到全量订阅会话;帧带 scanIntervalMs 诚实标注延迟;复用票 11 字典单源)。理由:仓库已有可证明的扫描-指纹-推送基础设施;纯降级会让本票后端侧几乎无交付;前端对未知帧静默忽略,零破坏。eventId 透传status=ALARM 增量后可精确重取该报警;zk-api-service 是否真发 eventId 未证实,防御性透传不臆造。| 项 | 状态 | 证据 |
|---|---|---|
| 编译 + 针对性契约测试 | 通过 | 21/21(新增 8 例:帧形状/变化检测/基线/降级纪律/会话清理/eventId) |
| 全量测试 | 通过 | 8 模块 422/422(基线 414 + 8) |
| 联调基线登记 | 已产出 | 基线卡片 04(状态 ③) |
| 代码提交 + 推送 | 已完成 | d8a6b03 → origin/master(4ef2159..d8a6b03) |
| 部署后实测帧到达 | 已通过 | 真人操作 284(3→4)后 40 秒帧到达,字段全对(见上复验表) |
| 上位回写 | 已完成 | 票 25 已回写三切片证据;Gogs #63 同文需负责人远端执行 |
| 真实设备报警闭环 | 不归本票 | 按决策 01 由真人团队执行(阶段总收口) |
cockpitWorkOrderStateDelta(按 workOrderCode 合并、scanIntervalMs 标注延迟)与 cockpitDeviceStatusDelta.eventId;前端对未知帧静默忽略,不升级不会崩。eventId 线上携带值:待外部发布方(zk-api-service)事件发生时观察,契约已锁定。