# 后端执行票 —— 监测三端点 createdAt 真实契约缺失 ## 问题 阶段工单[前端真实接口联调与阶段收口](18-frontend-real-integration-gate.md)本轮已授权只读预检:2026-10-13约16:34,真实后端 `http://192.168.110.96:8301/api/cockpit/v1` 的 `/monitoring/overview`、`/monitoring/distribution`、`/monitoring/situation` 均业务 `code=200` / `readOnly=true`,但 `data` 缺少自有 `createdAt` 字段;不是显式 `createdAt:null`。监测概览真实监测点6、设备9。最新历史审计亦仅此三项失败。 ## 实施依据与边界 沿用[监测数据时间字段规格](08-monitoring-created-at-spec.md)及[监测数据时间字段决策](03-monitoring-created-at-contract.md),无需重新裁定业务口径。在后端仓库补三端点分区/信封与逐对象 `createdAt`,来源为真实 `create_time`,无数据显式null;不得用 updatedAt/serverUpdatedAt/sampledAt 冒充。本阶段验收票不修改功能代码。前端消费已由[数据时间与片区/行业接线](15-frontend-time-dimension-panel.md)交付;后端补齐后验证现有消费。 ## 验收 1. 三端点按规格字段落点显式返回 `createdAt`,distribution台账与situation异常设备逐对象字段齐备;空态显式null,非空取真实来源。 2. 异构来源跨类型max(create_time)与逐对象来源可核对,不伪造时间。 3. 保持sampledAt等旧字段各自语义,不在map-snapshot/archive新增无消费字段。 4. 有意义的契约测试、同运行版本真实只读复验、前后端数据时间/采样时间标签核对;提交推送并登记运行绑定。 ## 下一步 ready-for-agent,等待后端执行会话认领;本轮仅发现并建票,未实施、未测试。它是整体阶段收口的技术阻断,不由真人验收豁免。 ## 进度 - 2026-10-14 诊断(只读,未改业务代码):**未动工**阶段,票面 `Status: ready-for-agent` 与 `needs-triage` 双状态冲突已消除。 - 现象已复现:真实后端三端点 200/`code=200`,`data` 顶层与逐对象均无 `createdAt`(不是显式 null)。 - 根因:**字段从未实现**(高置信)——后端 `com.zksy.cockpit` 全包 `createdAt` 零命中、`git log -S createdAt` 零提交;已排除「部署滞后」「Jackson NON_NULL 省略」「字段名/落点不同」三个候选。 - 冗余检查:后端与前端消费层均无已实现版本,非「已实现 → wontfix」;本仓库无 `.out-of-scope/`,无既往拒绝。 - 分流:`bug` + **`ready-for-agent`**(终态),已摘 `needs-triage`;Agent Brief 已作为评论附上。 - 待确认(不阻塞标签):后端改动落在 `E:\pipe-ner-server`,本会话沙箱只覆盖前端工作区,需由后端工作区执行会话认领;票 18 记有维护者并行修改期间的暂停指示,实际认领时机待放行。 ### 实施与验收(2026-10-14,维护者裁定 Q1 已放权在本会话执行) **实施完成**:新增单一口径来源 `cockpit/foundation/CockpitCreatedAt`(只做 `create_time` → `createdAt` 的格式化与跨来源 max,**不读时钟**,故结构上无法用响应时间冒充数据时间);三端点响应类、三个适配器、三个快照类与三个响应内嵌对象(`MonitoringPointMarker` / `UnassociatedEquipmentMarker` / `AbnormalEquipment`)全部按规格 08 落点补齐。 - `overview`:信封级 `createdAt`,取值域=**筛选后**参与本响应的设备 + 点位,跨类型 max。 - `distribution`:信封级 + 点位 marker(`monitor_point.create_time`)+ 未关联设备 marker(`equipment_base.create_time`);信封取**分页前全量台账** max(与三份分布同口径,故不随翻页跳动)。 - `situation`:信封级取异常设备来源表 `create_time` max(维护者 Q3 裁定,与逐对象同源可复算)+ 异常设备逐对象 `createdAt`;事件时间仍由 `warningTrend`/`alarmTrend` 的 `bucketStart` 承载。 - `sampledAt` / `updatedAt` / `freshness.serverUpdatedAt` 语义与取值不变;`map-snapshot` 与 archive **未新增**该字段(真实探测 `createdAt` 出现次数均为 0)。 **自动化验证**:`Cockpit*Test` **507/507**(0 失败、0 错误、0 跳过),新增契约测试 18 项:`CockpitCreatedAtTest`(格式化/空值/max 取真实时刻而非字符串序)5 项 + 三端点控制器契约(信封=max(逐对象)、与 `updatedAt` 取值不同、空态显式 null、取值域跟随筛选、翻页不变)13 项;三个适配器测试各加一项 `create_time` 列来源与 `sampledAt`/`statusTime` 分离的断言(显式令两列取不同值,证明确实未互相冒充)。 **真实只读复验**(`http://192.168.110.96:8301`,运行实例 PID 18576): - 三端点 HTTP 200 / 业务 `code=200` / `readOnly=true`,`data.createdAt` 均存在且**不等于** `updatedAt`(未拿快照/响应时间冒充)。 - `data.createdAt` = `2026-10-12T15:15:47+08:00`;`distribution` 6/6 点位与 9/9 未关联设备逐对象字段齐备;`situation` 8/8 异常设备齐备。 - **自洽复算**:`distribution` 与 `situation` 的信封值 == 各自逐对象 `createdAt` 的 max(逐条比对通过)。 - **反例核对**:`t18-real-audit.mjs` 由 140/143 → **143/143,0 失败**,原三项 `monitoring-createdAt-*` 失败清零。 - **前端消费侧真实数据核对**:三面板「数据时间」读数由 `—` 变为 `2026-10-12T15:15:47+08:00`(此前 real 下恒为占位符)。 **独立数据源核对(不是只看响应自证)**:用 Kingbase JDBC 直连 `192.168.110.96:54321/pipi_ner`(`app_user` schema)逐表核对 —— `monitor_point` 六个点位 `create_time` **全部为 NULL**、`equipment_base` 中 `21119740` 为 NULL 而 `41010500001320000001/…002` 分别为 `15:15:12`/`15:15:47`。故逐对象 `createdAt: null`(前端显示「—」)是**数据真实缺失**,不是映射缺口;空态显式 null 行为符合规格 08,未被伪造成时间。 **交付**:提交 `05c23774` 已推送 `origin/master`(`5a821872..05c23774`),20 个文件(13 主源码 + 7 测试);变基到远端最新后复跑 `Cockpit*Test` **507/507** 通过。运行绑定:运行实例 `target/classes` 已含 `getCreatedAt` 与 `CockpitCreatedAt.class`,与本提交一致。 **并行在制品处置(纪律)**:入 `E:\pipe-ner-server` 时工作树另有并行会话的 #32 直径几何在制品(4 源文件 + `.idea`)与本地图无关改动。提交只裁剪本票 20 个文件;为推送做变基时**临时 stash 该在制品并在推送后按字节还原**(5 文件逐一 `git hash-object` 比对 IDENTICAL),分支 tip 复位到会话开始时的 `a96091ce`,其后 `git stash drop` 仅删本会话临时条目;**未代表他人提交或推送其改动**(该本地提交 `a96091ce` 至今不在任何远端分支,与原状一致)。 **未做**:未修改前端功能代码;未触碰 `map-snapshot`/archive;无写操作与 DDL(`create_time` 列已存在)。 Status: resolved Type: bug Blocked by: Assignee: ASUS Labels: ready-for-agent, wayfinder:task, backend-execution, bug ## Comments ### local — 2026-10-14T02:21:21.131Z > *This was generated by AI during triage.* ### local — 2026-10-14T02:33:50.050Z > *This was generated by AI during triage.* ## 维护者裁定已回(2026-10-14) - **Q1 = 已放权,在本会话执行** → 本会话(前端工作区沙箱已放开为 danger-full-access)直接执行后端仓库 `E:\pipe-ner-server` 的实现。标签保持 `ready-for-agent`。 - **Q2 = 现在放行** → 不等票 18 的并行窗口,立即开工。 - **Q3 = 按推荐** → `situation` 信封级 `createdAt` 取**异常设备来源表的 `create_time` max**(与逐对象字段同源可复算);事件时间继续由 `warningTrend`/`alarmTrend` 的 `bucketStart` 承载,不并入信封读数。 Agent Brief 的 Acceptance criteria 据此已明确:信封读数与逐对象字段必须同源可复算。 开始实施,进度回写本票 `## 进度`。 ## Answer **已解决并关闭(2026-10-14)**。三端点 `createdAt` 真实契约已补齐、测试、真实复验并推送。 **根因**:字段**从未实现** —— 后端 `com.zksy.cockpit` 全包 `createdAt` 零命中、`git log -S createdAt` 对 cockpit 路径零提交,三个响应类与适配器均未读取 `create_time`。已排除「部署滞后」「Jackson NON_NULL 省略字段」「字段名/落点不同」三个候选(反证:同一响应中 `district`/`reason`/`longitude` 等 null 值均显式序列化,且运行实例已带同批新契约)。 **修复**(提交 `05c23774`,已推 `origin/master` `5a821872..05c23774`): - 新增单一口径来源 `CockpitCreatedAt`:只做 `create_time` → `createdAt` 格式化与跨来源 max,**不读时钟**,故结构上无法用 `updatedAt`/`serverUpdatedAt`/`sampledAt`/`status_update_time` 冒充数据时间。 - 按规格 08 落点补齐:`overview` 信封级(筛选后设备+点位跨类型 max);`distribution` 信封级 + 点位 marker + 未关联设备 marker;`situation` 信封级(异常设备来源表 max,维护者 Q3 裁定)+ 异常设备逐对象。 - 空态显式 `null`;取值域跟随筛选;distribution/situation 信封取分页前全量(不随翻页跳动);`map-snapshot` 与 archive 不加该字段。 **验收证据**: - 自动化:`Cockpit*Test` **507/507**(0 失败/0 错误/0 跳过),新增契约测试 18 项;变基到远端最新后复跑仍 507/507。 - 真实只读 GET:三端点 `data.createdAt` 均存在、均**不等于** `updatedAt`;`distribution` 6/6 点位 + 9/9 未关联设备、`situation` 8/8 异常设备逐对象字段齐备;信封值 == 各自逐对象 max(自洽复算通过)。 - 权威审计脚本 `t18-real-audit.mjs`:**140/143 → 143/143,0 失败**。 - 前端消费侧:三面板「数据时间」由 `—` 变为真实 `2026-10-12T15:15:47+08:00`。 - 独立数据源核对(Kingbase JDBC 直连 `app_user` schema 逐表比对,非只看响应自证):`monitor_point` 六点位 `create_time` 全 NULL、`21119740` 为 NULL 而另两台为 `15:15:12`/`15:15:47` —— 逐对象 `null`(界面「—」)属**数据真实缺失**,非映射缺口。 **边界**:不改前端功能代码;`sampledAt` 语义不变;无 DDL(`create_time` 列已存在);无写操作。 **并行在制品纪律**:提交只裁剪本票 20 个文件;推送所需变基时临时 stash 并行会话的 #32 几何在制品,推送后按字节还原(5 文件 `git hash-object` 逐一 IDENTICAL)并复位分支 tip,未代表他人提交或推送其改动。 **方法论沉淀**:`createdAt` 类「真实契约缺失」应当**单一口径来源 + 不读时钟**实现,使「拿响应时间冒充」在结构上不可达;跨端点信封读数的正确性判据是**与自身逐对象字段自洽复算**(本次两处均 pass),而不是与 `updatedAt` 比较大小;逐对象 `null` 必须回到**源表**核对才能区分「数据缺失」与「映射缺口」——只看响应无法自证。 ## Triage Notes(2026-10-14) **阶段判定:未动工。** 票面 `Status: ready-for-agent`,同时又挂着 `needs-triage`(两个状态角色冲突)。读全正文/评论/标签后确认:本票自建立起只有发现记录,没有任何实施痕迹、没有认领人、`## 进度` 为空。按阶段闸门「未动工 → 正常诊断」处理。 ### 一、现象(本轮已复现,2026-10-14 10:16 +08:00) 真实后端 `http://192.168.110.96:8301/api/cockpit/v1` 三端点实测仍缺 `createdAt`: | 端点 | HTTP / 业务 code | 信封级 `createdAt` | 逐对象 `createdAt` | | --- | --- | --- | --- | | `/monitoring/overview` | 200 / 200 | **无** | —(本端点无逐对象) | | `/monitoring/distribution` | 200 / 200 | **无** | `monitoringPoints[6]`、`unassociatedEquipment[9]` 均**无** | | `/monitoring/situation` | 200 / 200 | **无** | `abnormalEquipment[7]` **无** | `data` 顶层 key 实测枚举(overview):`requestId, apiVersion, readOnly, displayName, timezone, range, updatedAt, freshness, counts, status, onlineRate, typeDistribution, mapFilter, excluded` —— 无任何等价时间字段。**是字段整体不存在,不是显式 `createdAt: null`**,与票面记载一致。 最小复现(纯只读,无需改代码): ```powershell $base='http://192.168.110.96:8301/api/cockpit/v1' foreach ($p in '/monitoring/overview','/monitoring/distribution','/monitoring/situation') { $d = ((Invoke-WebRequest "$base$p" -UseBasicParsing).Content | ConvertFrom-Json) [pscustomobject]@{ route=$p; code=$d.code; envelopeCreatedAt=($d.data.PSObject.Properties.Name -contains 'createdAt') } } # 实测:三行 code=200 / envelopeCreatedAt=False ``` ### 二、影响范围 - **前端消费侧已交付,real 下恒显示占位符**:`src/modules/cockpit/domain/monitoringPanels.js` 三处 `createdAt: reader.optionalStr('createdAt')`(overview / distribution / situation),缺失经 `valueText(null)` → `'—'`。后果是运行监测三面板「数据时间」在 real 模式恒为「—」,台账卡与异常设备清单的 `dataTime` 同样为「—」。mock 契约里有值(`mocks/contractMocks.js`),所以 mock 走查看不出这个缺口。 - **票 18 阶段收口的直接技术阻断**:`verify/t18-real-audit.mjs` 的 `monitoring-createdAt-*` 三项是当前审计唯一的失败项(21 项 18 过 / 3 失败);票 18 的 `Blocked by` 已含本票。 - 不影响其余 15 个端点,不涉及写操作,后端只读边界不变。 ### 三、根因候选 | # | 候选 | 置信度 | 判定依据 | | --- | --- | --- | --- | | 1 | **字段从未实现**(响应类/适配器/快照均无 `createdAt`,也从未读 `create_time`) | **高(≈95%)** | 后端 `com.zksy.cockpit` 全包 `createdAt` **零命中**;`git log -S createdAt` 对 cockpit 路径**零提交**;`CockpitMonitoring{Overview,Distribution,Situation}Response` 三类字段清单无该字段;overview 适配器只用 `status_update_time`(即 `sampledAt`),distribution 适配器完全不传时间,situation 的 `SourceMetricDevice` 只有 `sampledAt` | | 2 | 部署滞后(实现已在 master、运行实例是旧构建) | 极低(<5%) | 反证:仓库 HEAD 与远端都不存在任何含该字段的提交;且运行实例已带八桶 `pointStatus`、`district`、`infrastructure/geometry`、设施总数 5283 等新契约,产物并不旧 | | 3 | Jackson 把 null 字段省略(NON_NULL) | **已排除** | 反证:同一响应里 `mapFilter.district:null`、`districtName:null`、`reason:null`、`longitude:null` 均显式出现;后端无 `setSerializationInclusion` / `default-property-inclusion` 配置 | | 4 | 字段名或落点不同(后端另有名字/位置) | **已排除** | 反证:顶层与逐对象 key 全量枚举后无等价时间字段;`updatedAt`(快照加载时刻)与 `freshness.updatedAt/serverUpdatedAt`(响应生成时刻)语义已由决策 03 明确区分;`distribution` 的 `statusTime` 是现状**被丢弃在响应外**的字段 | **结论:单一根因、置信度高 —— 后端从未实现该契约。** 规格 08 与决策 03 已把字段语义、三端点落点、空态与「不冒充」规则全部定死,**无需重新裁定业务口径**。 ### 四、冗余与既往拒绝检查 - **冗余**:按领域概念(数据时间 / `createdAt` / `create_time`)检索后端 cockpit 包与前端消费层,**未发现任何已实现版本**,不是「已实现 → wontfix」。 - **既往拒绝**:本仓库不存在 `.out-of-scope/` 目录,无同类请求被拒记录。 ### 五、实施落点(供执行会话参考,本轮不动作) 三端点响应类各加可空 `createdAt` + 适配器/快照携带真实 `create_time`;来源实体 `EquipmentBase.createTime`、`MonitorPoint.createTime` 均已存在(**无需 DDL**);空态显式 `null`(当前序列化已确认会输出 null,无需额外配置);`map-snapshot` 与 archive **不加**。 --- ## Agent Brief **Category:** bug **Summary:** 后端为 `/monitoring/overview`、`/monitoring/distribution`、`/monitoring/situation` 补上自有 `createdAt`(记录创建时间 = `create_time`)契约:三端点信封级 + distribution/situation 逐对象级,空态显式 null,不用响应时间或采样时间冒充。 **Current behavior:** 三端点 HTTP 200 / 业务 `code=200` / `readOnly=true`,但 `data` 中没有 `createdAt`(也不是显式 `null`)。前端消费侧(`domain/monitoringPanels.js` 的 `createdAt` 读取 + `valueText(null) → '—'`)已交付并在 mock 下验证通过,real 下三面板「数据时间」与台账/异常清单时间恒为「—」。 **Desired behavior:** 按[规格 08](08-monitoring-created-at-spec.md) 与[决策 03](03-monitoring-created-at-contract.md) 的字段落点显式返回 `createdAt`: - `overview`:信封级一个读数,取聚合源真实 `create_time` 的跨类型 max; - `distribution`:信封级 + 点位 marker(`monitor_point.create_time`)+ 设备 marker(`equipment_base.create_time`); - `situation`:信封级 + 异常设备逐对象(来源表 `create_time`); - 无 `create_time` 或集合为空 → 显式 `null`;**不得**用 `updatedAt` / `serverUpdatedAt` / `sampledAt` / `status_update_time` 冒充;`sampledAt` 等旧字段语义不变;`map-snapshot` 与 archive 不新增该字段。 **Key interfaces:** 三端点响应类(`CockpitMonitoring{Overview,Distribution,Situation}Response`)各增一个可空 `createdAt`;三个适配器(`BusinessCockpitMonitoring{Overview,Distribution,Situation}Adapter`)在构建快照时携带来源 `create_time`;快照类(`CockpitMonitoring*Snapshot`)透传;`MonitoringPointMarker` / `UnassociatedEquipmentMarker` / `AbnormalEquipment` 三个逐对象结构各加 `createdAt`。来源实体 `EquipmentBase.createTime`、`MonitorPoint.createTime` 均已存在。 **Acceptance criteria:** - [ ] 三端点响应顶层含 `createdAt` 键(空态为 `null`,不是缺键) - [ ] `distribution.monitoringPoints[]`、`distribution.unassociatedEquipment[]`、`situation.abnormalEquipment[]` 逐对象含 `createdAt` - [ ] 非空取值等于对应来源表真实 `create_time`(可逐表核对),跨类型 max 语义可自洽复算 - [ ] `sampledAt` / `updatedAt` / `freshness.serverUpdatedAt` 语义与取值不变 - [ ] `map-snapshot` 与 archive 响应不含 `createdAt` - [ ] 有意义的契约测试;同运行版本真实只读复验三端点字段齐备;提交推送并登记运行绑定 **Out of scope:** 前端消费侧改动(已由票 15 交付,本轮只验证现有消费);`sampledAt` 语义变更;刷新/取数时机(归票 04/09);在 `map-snapshot`/archive 新增时间字段;任何写操作。 --- **分流建议:** 分类 `bug`;状态落 **`ready-for-agent`**(保持),**摘除 `needs-triage`**。理由:现象已复现、根因单一且置信度高、规格 08 已把口径与落点定死、验收标准可独立验证、无设计裁定待办;后端执行有大量同族先例(票 20/28/29/30/31 均由执行会话落地并推送 origin/master)。附两条待确认事项(见下),按「有小疑问可附待确认事项一起转,不阻塞」处理。 **待确认事项(不阻塞转 ready-for-agent):** 1. 本会话文件沙箱只覆盖前端工作区 `E:\City-Lifeline-Cockpit-TvT`,而本票改动落在后端仓库 `E:\pipe-ner-server` —— 需确认由后端工作区的执行会话认领(先例如此)。 2. 票 18 记有维护者「其他会话正在修改相关内容,等待正常后继续」的暂停指示 —— 本票标签照转,实际认领时机需维护者放行。 **本轮未做:** 未修改任何业务代码、未关闭票、未开始修复实施、未提交或推送;只做了只读检索与只读接口复现。 ## Answer