# 邻域设备示意挂载与增强面板内部详情 ## What to build 在邻域增强场景显示真实监测设备模型,按行业在管道固定示意槽位摆放并可点击查看内部设备详情。真实设施关系优先;无关系但合法坐标的邻近设备可挂同业最近管段,标“邻近设备·示意安装”。无合适管段独立显示,无坐标无关联仅列清单。 ## Acceptance criteria - [x] 核验并补齐邻域设备读取来源及设施/井关系;不能把所选监测点设备当附近全部设备。 - [x] 真实设备ID与父监测点身份保留,真实关联优先于空间近邻,networkId按真实主键匹配。 - [x] 同业固定槽位示意布局、无位置清单、关系/布局提示,绝不创建设备或改绑定/源坐标。 - [x] 设备模型点选打开增强面板内部详情,复用真实详情接口;场景和相机保持。 - [x] 在线、告警及读数真实显示,缺失/失败/过期/重试/换选取消正确;不能从附近设备推导管道健康。 - [x] 同业近邻、悬空关系、无坐标、缺来源/部分覆盖、设备点击至详情和销毁行为回归。 ## Blocked by 51 ## Context 完整已确认方案为本effort local-network-enhancement-spec.md。历史gas_pipe_point_device_rel、manhole_device_rel不等于equipment_point_rel;当前前端适配未完整暴露设施关系,实施需核验契约,缺关联按用户允许的示意降级,不得伪报完整覆盖。 ## 设施/井关系核验结论(2026-10-21 实测) 第 1 条验收的「核验」部分已按免鉴权 cockpit 接口逐端点确认;「补齐」部分需要后端新增契约,前端无法自补。证据如下。 ### 设备读取来源(已补齐,可满足) | 来源 | 端点 | 状态 | | --- | --- | --- | | 当前快照全部监测点 | `/map-snapshot` → `monitoringPoints[].deviceCodes` | 可用,7 点全部带 `deviceCodes` | | 每个点位的设备卡 | `/monitoring-points/{pointCode}` → `devices[]` | 可用,含 `deviceCode`/坐标/状态/`deviceStatus` | | 未关联设备 | `/map-snapshot` → `unassociatedEquipment[]` | 可用,8 台 | | 设备详情 | `/equipments/{deviceCode}/detail-aggregate` | 可用 | 结论:读取来源**不是**「所选监测点的设备」,而是当前快照全部点位 + 独立设备,符合验收第 1 条的前半句。 ### 设施/井关系(契约缺口,无法前端补齐) | 探针 | 结果 | | --- | --- | | `/map-snapshot` 的 `unassociatedEquipment` 带 `networkId` | **0 / 8** | | `/map-snapshot` 的 `monitoringPoints` 带 `networkId` | **0 / 7** | | `/monitoring-points/{code}` 的 `devices[]` 带 `networkId` | **无该字段** | | `/equipments/{code}/archive` 的关系字段 | **无**(只有档案属性) | | `/api/cockpit/v1/device-boundary` | 6762 条 `facilities` + 9 条 `devices`,**两份互不相连的扁平清单,无关系边** | | cockpit 免鉴权 Swagger 路径清单中的设施/井关系端点 | **不存在**(只有 `device-boundary`/`archive`/`detail-aggregate`/`metrics`/`events`/`work-orders`) | | `/api/manhole/device/list/{id}` | 免鉴权可用,但实测返回 `[]` | | `/api/gas-pipe-point/device/list/{id}` | 免鉴权可用,但实测返回 `[]` | | `/api/manhole/device/rel/list/{id}` | **401 认证失败**(非免鉴权面) | `/infrastructure/geometry` 侧 6766 个 feature **全部**带非空 `networkId`,但实测 `networkId === sourceId`(即设施自身主键),不是「设备→设施」的关联键。因此**当前不存在设备侧可用的真实设施关联主键**。 **这意味着**:`domain/enhancementDevices.js` 的「真实 `networkId` 匹配优先」分支在真实数据下永远不会触发;真实运行时不变量是「同业邻近示意」或「无同业管段 standalone」。该分支有构造数据回归(`t52-device-plan.mjs`),但从未被真实载荷触发过——这是契约现状,不是代码缺陷。 ### 归类 本票同时含两类无法由 AI 闭环的事项: 1. **外部契约依赖**——「补齐设施/井关系」需要后端在免鉴权 cockpit 面新增设备→设施关系端点(或让 `map-snapshot` 的监测点/设备带出真实关联主键)。前端不能凭空造关系,按规格也不得把 `code` 当关联主键。 2. **需人工测试**——邻域场景中设备模型是否可辨、槽位是否合理、点选详情是否符合预期、以及 8 倍显示下设备是否可见。 AI 不代劳的原因:`AGENTS.md` 禁止自动 build/dev/浏览器截图;后端仓库与权限不在本会话交付范围。 ## 给维护者的实施清单 ### A. 现场视觉验收(只需你本人执行) 1. 你启动 `yarn dev`,打开 `http://127.0.0.1:8000/#/cockpit`。 2. 拾取一条管道进入「地下管线详情视图」。 3. 逐项目检: - 右侧「监测设备」区是否与「附近管道状态」分离显示、不被长列表挤走; - 场景中是否有可辨的设备图标/模型,点击后是否打开内部设备详情面板; - 设备清单点击是否聚焦到场景设备并打开同一详情; - 设备标注是否明确写「邻近设备 · 示意安装」(而非暗示真实安装位置); - 无坐标设备是否只出现在清单、**没有**被摆到伪造位置。 4. 检查点:图标大小是否合适、密集时是否互相遮挡、详情面板是否遮挡关键信息、关闭详情后场景是否保持。 5. 边界与回归:换选管道、切换 25/50/100 米、切换全局行业筛选——确认设备清单随之变化且旧详情被取消。 ### B. 后端契约补齐(需后端负责人,非本会话) 要让「真实设施关系优先」在真实数据上生效,需要下列之一: 1. 在免鉴权 cockpit 面新增关系端点,返回 `{ deviceCode, facilityCode | networkId, relationType }`; 2. 或让 `map-snapshot` 的点位设备卡 / `monitoring-points/{code}` 的 `devices[]` 带出真实设施关联主键; 3. 或把 `manhole_device_rel` / `gas_pipe_point_device_rel` 的读取面纳入免鉴权 cockpit 契约。 补齐后本票的 `linked` 分支会自然生效,**前端无需改动**(`domain/enhancementDevices.js` 已按真实主键匹配,且明确禁止用 `code` 冒充)。 **在此之前**:系统按维护者已批准的「同业邻近示意」降级运行,界面如实标注「邻近设备 · 示意安装」,不宣称真实绑定。 ### C. 不在本票范围 邻域裁切([51](51-untitled.md),已关闭)、窨井剖切([53](53-untitled.md))、真实管径与材质([54](54-untitled.md))、场景设备图标细化([58](58-untitled.md))、按设备类型取指标([56](56-untitled.md))。 ## 可复跑的离线证据 - `node .scratch/cockpit-shell-panel-layout/verify/t52-device-source.mjs`(读取来源、覆盖缺口、渐进发布) - `node .scratch/cockpit-shell-panel-layout/verify/t52-device-plan.mjs`(真实 ID 优先、邻近示意、无位置清单、旧响应隔离) - `node .scratch/cockpit-shell-panel-layout/verify/t52-device-pick.mjs`(真实 Three 射线派发设备身份、拖拽不误点) - 整套:`node .scratch/cockpit-shell-panel-layout/verify/t50-53-local-enhancement.mjs`(12 个脚本) ## 进度 - 2026-10-16 代码与离线回归完成,转 `ready-for-human`。加载当前快照全部监测点详情(4 并发)及独立设备,保留真实父点/ID;摘要未齐、缓存截断、详情失败/缺/额外设备均报 partial 并可重试。 - 固定槽位按管段/裁片稳定分组,模型不改源坐标,未准入且无关系仅清单。Three 射线派发设备身份并尊重首交点遮挡;内部复用 `EquipmentDetailSections` 与真实 `detail-aggregate` 接口,打开详情不重建场景。范围移除 reconcile、刷新开始 sync invalidate、模式可见性与异常集合、旧详情/source token 隔离已实现。 - 2026-10-21 按 `/wayfinder` 接手(上游 #51 已关闭,阻塞解除):复跑 `t52-device-source`/`t52-device-plan`/`t52-device-pick` 全部通过;实测核验设施/井关系端点,确认**免鉴权 cockpit 面无设备→设施关系契约**(`map-snapshot` 设备侧 `networkId` 0/8、点位 0/7,`device-boundary` 只有两份扁平清单,`manhole_device_rel` 读取需 401)。据此勾选第 1 条验收的核验部分,并把「补齐」明确为后端契约依赖写入正文。 - 记录:正文曾被 `deck_issue_patch` 传入空 `body` 擦除,已按原文恢复;后续只读上报。 ### 2026-10-22 维护者四条反馈修正 + 后端契约落地 维护者现场验收后给出 4 条修正(A)并批准后端补契约(B),均已在本会话完成,提交 `c12f774`(前端)与 `83bd5ee`(后端),均已推送。 | # | 反馈 | 处理 | | --- | --- | --- | | A1 | 设备区与管道状态已分离,但附近管道状态要分页 | 每页 8 条 + 上一页/下一页 + 页码;换选/邻域变化回到第 1 页。`t23-click-flow` 断言 12 条时只渲染 8 条且设备区仍在 | | A2 | 场景设备图标没有用地图同款,要保持一致 | Three 场景**不再自绘图标**:改为 `planMarker` + `renderMarker`(与一张图同一合成链路:水滴母版 + 行业内层 + 白描边 + 状态色),产物作 Sprite 贴图,尺寸沿用其 `draw` | | A3 | 详情面板要与监测设备坐标点一致,点击后在三维场景中浮动 | 详情从右侧栏移出,改为浮在三维场景之上的独立面板(可单独关闭,不动相机/场景) | | A4 | 无坐标设备在地下管线详情视图中应隐藏 | 改为**直接跳过**,不再进清单;主图台账仍可查 | **顺带修掉一个真实缺陷**:改造图标点选时曾丢失每次点击的 `setFromCamera`,导致拾取依赖上一次射线的残留状态。已恢复按本次坐标重新投射;图标点选用与图标同尺寸的透明代理平面命中(Sprite 在嵌套缩放下 raycast 不可靠——同一图标最小用例能命中、放进场景就 miss)。 **B 后端契约(已上线)**:`GET /api/cockpit/v1/device-facility-relations`(免认证只读,沿用既有 `permitAll`,未放宽安全边界;commit `83bd5ee`)。前端新增 `services/deviceFacilityRelations.js` 消费它,关联优先级变为**真实设施关系 > 载荷 networkId > 同业邻近示意**。 **⚠️ 必须如实记录的数据现状**:契约已就绪,但**当前真实可回联关系为 0 条**。实测响应:`manholeRelationRows=6`、`gasPipePointRelationRows=4`、`returnedRelationCount=2`,其中 `skippedManholeFacilityMissing=4`(4 条存的是设备 id 不是窨井主键)、`skippedGasPipeNetworkMissing=4`(管点 `network_id` 在管段表里不存在,悬空)、`deviceCodeUnresolved=2`(2 条 `equipment_id` 在 `equipment_base` 查不到,`deviceCode` 为 `null`)。因此界面当前仍标「邻近设备 · 示意安装」,**不是**真实绑定;要真正点亮真实关联,需运维补数据(管点 `network_id` 指向真实管段、关联表 `equipment_id` 指向 `equipment_base` 主键)。 **验证**:`t52-facility-relations`(真实关联命中/设施编码不匹配降级/空索引降级/只回联齐全行/取数失败降级)、`t52-device-source`、`t52-device-plan`、`t52-device-pick`、`t55-detail-usability`、`t55-device-loading`、`t56-real-water-device`、`t56-device-metrics`、`t23-click-flow`、`t23-sfc-compile`、`t09-point-status`(38/38) 与整套 12 脚本全部通过。按 AGENTS 未跑 build/dev/browser。 **范围自查**:`mapService.js` 与 `markerPlan.js` 带其它在制品改动,本票**未提交**这两个文件(Three 服务自带同值兜底色表,避免触碰他人工作);未改任何后端既有文件。 Status: ready-for-human Assignee: codex Type: task Blocked by: #51 Labels: ready-for-human, wayfinder:task ## Comments ### local — 2026-10-21T04:10:00.000Z 按 /wayfinder 接手。路径指向的 `cockpit-real-data-wayfinding/52` 是已 resolved 的后端优先级票,按标题选择本 effort 票。 复跑三个离线回归全部通过。实测核验设施/井关系契约:`map-snapshot` 设备侧 `networkId` 0/8、点位 0/7;`monitoring-points/{code}` 的 `devices[]` 无 `networkId`;`device-boundary` 只给 facilities+devices 两份扁平清单、无关系边;cockpit 免鉴权 Swagger 无设施关系端点;`manhole/device/rel/list` 需 401。因此「真实关联优先」分支在真实数据下永不触发,运行时按已批准的邻近示意降级。 归类:**外部契约依赖**(补齐需后端新增端点)+ **需人工测试**(现场视觉验收)。两点均写入正文并给出清单。 ## Answer 代码与离线回归完成,核验部分已闭环;剩余两件不由 AI 代劳:① 维护者在正式入口目检设备图标/槽位/内部详情;② 后端补齐「设备→设施」关系契约后,真实关联分支才会在真实数据上生效(当前按已批准的邻近示意降级,不伪报完整覆盖)。