# 21: 后端执行票 —— 工单关联标记口径统一(设备维度 vs 时间线) Type: task Status: resolved Label: ready-for-agent Labels: ready-for-agent, backend-execution, wayfinder:task Category: backend-execution Assignee: Kazerin Blocked by: None ## 来源 票 18 真人闭环实测(2026-10-10,工单 `WO202610101511303516`)。 ## 现象 同一张工单在两处返回互相矛盾的「是否关联当前事件」: | 端点 | `associatedWithCurrentEvent` | `eventAssociation` | | --- | --- | --- | | `GET /equipments/21119740/work-orders` | `true` | `{ALARM, cd57be42bd18d02f7d3390fb98539b93, 15:11:30}` | | `GET /work-orders/WO202610101511303516/timeline` | `false` | `null` | 时间线侧缺少关联信息,设备侧完整。前端详情面板按「未关联当前报警明确提示」消费该字段,口径不一致会直接导致同一张工单在两个位置呈现相反结论。 ## 范围 1. 统一 `associatedWithCurrentEvent` 与 `eventAssociation` 的**单一权威来源**(关联键为 `alarm_data.work_order_id = work_order.order_id`,已由历史决策确证)。 2. 时间线与设备维度返回同一判定与同一关联信息;不得一处显式 `false`、另一处省略。 3. 事件维度端点一并核对同一口径。 4. 补契约测试:同一工单在设备维度、报警维度、时间线三处判定一致。 ## 验收 真实环境复验:设备、ALARM 事件、时间线对同一真实关联工单返回一致的 `associatedWithCurrentEvent=true` 与同一完整 `eventAssociation`;编译 + `Cockpit*Test` 全绿 + 提交推送。 首次指定的 `WO202610101511303516` 当前为待接单且无报警关联;维护者本轮明确提供新真实样本:工单 `WO202610121103507623`、报警单号 `BJ202610121103101791`、设备 `21119740`,用于最终正向验收,原工单保留为未关联负向对照。 ## 关联 发现于票 18;承接决策票 01 的关联键结论。 ## 进度 - 认领:Kazerin(后端 Git 身份已核验);本地后端 assignees 操作未实际写入,按 tracker 约定使用 `Status: claimed`,已调用 deck_issue_report 上报。 - 阶段闸门:完整正文、评论、标签和父子/阻塞边已读;初始 `ready-for-agent`,无 `needs-triage`,无已有实施记录,无阻塞;所属 map 允许后端执行票实施。 - 根因已核验:`BusinessCockpitWorkOrderReadAdapter.loadByDevice` 经 `alarm_data.work_order_id = work_order.order_id` 查询真实关联;旧 `loadTimeline` 未查关联,直接 `toSummary(order, null, null, false)`;报警维度另有独立组装逻辑。 - 实施已完成:设备、ALARM 事件、时间线共用 `alarmsByOrderId` 与报警摘要转换;唯一真实关联为 true + 关联对象,未关联显式 false/null。按维护者裁定,重复报警引用同一工单抛类型化读取异常,三个入口返回 HTTP 500 + `SERVER_ERROR`,不选主报警。批量关联查询的 Spring DAO / MyBatis 持久化异常转换为相同失败契约。WARNING 分支和既有时间字段语义保持原实现。 - 契约测试已编写并执行:新增 `CockpitWorkOrderAssociationContractTest` 共 7 个测试方法,经真实适配器贯穿 HTTP 三端点,覆盖正向一致性、报警单号别名与 SQL 查询条件、未关联、同设备串单隔离、未知工单、重复关联正反序及两个真实数据库异常类型;旧适配器测试补共享反查数据桩。用户明确要求验证后在精确提交 `894402a` 独立工作树运行 Maven,**BUILD SUCCESS;417/417 测试通过,Failures/Errors/Skipped 全为 0**(56 个报告,admin 模块 415 + 其它模块 2);本票新契约 **7/7**,适配器 **3/3**、控制器 **9/9**。完成时间 `2026-10-12T10:38:19+08:00`。 - 后端提交与推送:`E:\pipe-ner-server` / `master` / [`894402a`](http://192.168.110.30:3000/shaoy/pipe-ner-server/commit/894402a6d495be28265014b316742fac355b311a),已推 `origin/master`(`0a6b108..894402a`);提交限定适配器、两个测试文件和所属领域词汇,共 4 文件。未夹带并行会话的 monitoring 改动。 - 对抗复查:Standards 首轮无阻断;Spec 首轮发现多关联歧义和失败测试失真,均已据用户裁定修正;修订后源码/测试静态复核无剩余阻断,主代理限定 diff 与 `git diff --check` 通过。**静态复核不等于编译或测试通过。** - 首次验收(历史):10:38 的精确修复提交测试 417/417 通过;旧实例 PID 30160 对原工单返回 false/null、原报警为空,故当时留票待补正向样本,未把一致 false 算作通过。维护者要求复测后,11:06 确认原工单两处均 1 / 待接单,但仍无关联。 - **最终真实状态:本票全部验收通过,已关闭,无本票剩余项。** 用户已明确要求编译/测试/真实接口复验,随后提供新的正向样本。修复提交已推送,本票关联源码在最新已提交基线 `7fb7c2c` 中未变化;本轮独立工作树编译 **BUILD SUCCESS,428/428 测试通过、Failures/Errors/Skipped 全 0**(58 个报告,admin 426 + 其它模块 2),新增关联契约仍 **7/7**,完成于 `2026-10-12T11:10:00+08:00`。 - 11:11:17 最终正向复验:新工单 `WO202610121103507623` 在设备、ALARM 单号查询、时间线均唯一命中、HTTP 200 / code 200、`associatedWithCurrentEvent=true`;均为状态 **0 / 待派单**,关联对象完全相同:`{eventSource: ALARM, eventCode: e68a942d92c48ac709fca8cee3160f3b, associatedAt: 2026-10-12T11:03:51+08:00}`。11:13 额外核对真实报警 ID 查询与单号别名一致。 - 最终负向对照:原工单两处均 **1 / 待接单**、false/null;设备总工单 6,未关联 5,逐条重算与 `unassociatedWorkOrderCount=5` 一致。 - 当前实例 PID 36420(启动 11:09:02);主工作树与隔离测试的本票适配器产物 SHA-256 完全相同,产物写入早于启动,真实时间线正向行为已证明本票修复生效。仅锁定本票适配器和行为,不把并行会话的整体实例声称为某个纯净提交。 - 最终证据与复验命令见 [验收说明](../handoff/work-order-association-verification.md)、[最终验收记录](../verify/t21-work-order-association-final.json);[首次验收记录](../verify/t21-work-order-association-results.json) 保留为历史。未修改真实业务数据,未启动/重启服务。上位前端阶段的其它验收仍由所属阶段票承担。 ## 口径裁定 维护者本轮 grilling 选择:**坚持一对一,重复关联报错**。多个报警引用同一工单时,设备、ALARM 事件和时间线均须明确失败,不默认取最早、最新或首条报警;不新增数据库约束,不修改现有关联数据。 ## Comments - 对抗复查发现原设备逻辑允许多报警取首条,而事件逻辑取请求报警;即使修好时间线 false/null,多关联仍可能返回不同关联对象。维护者裁定如上,已据此加一对一校验。 - 失败场景测试已从人为抛领域异常改为真实 `DataAccessResourceFailureException` / `PersistenceException`,用于断言适配器转换后三个入口返回 HTTP 500 + `SERVER_ERROR`;报警单号回退补 SQL 字段与参数断言。 ### local — 2026-10-12T03:18:37.495Z 验收通过,关闭「后端执行票 —— 工单关联标记口径统一(设备维度 vs 时间线)」。 维护者提供的新真实样本:WO202610121103507623 / BJ202610121103101791 / 设备21119740。设备、ALARM(单号和真实ID)、时间线均HTTP200/code200,关联true;eventAssociation三字段全部相同:ALARM / e68a942d92c48ac709fca8cee3160f3b / 2026-10-12T11:03:51+08:00。新工单三端均0/待派单;旧工单WO202610101511303516两处均1/待接单且false/null,作为负向对照通过。 修复894402a已推送;本轮隔离测试基线7fb7c2c(关联源码未变)BUILD SUCCESS,428/428通过,新增关联契约7/7。当前实例PID36420,本票适配器产物与隔离测试产物SHA256一致,真实修复行为已核验。 正文已写入最终状态和Answer;证据:../handoff/work-order-association-verification.md、../verify/t21-work-order-association-final.json。本票无剩余验收项,上位前端整体收口仍归阶段票。 ## Answer 统一查询与摘要组装已消除时间线 false/null 硬编码;设备、ALARM、时间线按真实 `alarm_data.work_order_id = work_order.order_id` 同源消费。按维护者裁定坚持一对一、重复引用报错;未关联显式 false/null,WARNING 语义不变。 修复 `894402a` 已推送;首次精确提交 417/417、本轮当前已提交基线 428/428 全绿,新增关联契约 7/7。维护者提供的新真实工单 `WO202610121103507623` 与报警 `BJ202610121103101791` 三端点均 true 且完整关联对象一致,旧待接单工单作为未关联负向对照通过。本票验收完成,关闭;最终证据见上方链接。