# 24: 前端执行票 —— 设施点位与管线真实几何图层接线 Blocked by: #31 Type: task Status: resolved Label: ready-for-agent Category: frontend-execution ## 来源 票 18 真实联调与真人走查第 1、4 项(2026-10-10):地图上监测点、设备、设施、管道全部没有正确显示。 ## 现状事实 1. **前端图层未接线**:`CockpitShell.vue` 构造 `modeLayerPlan` 时恒传 `facilities: null`、`pipelines: null`,因此设施与管线图层在任何模式都处于「未就绪」降级态,`cesiumEngine` 只有 `PIPELINE_SOURCE` 常量与查找逻辑,**没有创建管线数据源的写入路径**。 2. **后端已有可复用的真实几何链路(未接入驾驶舱)**:供水 GIS 读取 `water_source_info` / `water_plant_info` / `pump_station(water_supply)` / `pipe_network_base(network_type='2')` / `water_user_info`,控制器路由为 `/waterSupply/gis/map/features`(需 `waterSupply:gis:map` 权限),几何为两点 `LineString`,且只做非空检查、不做范围与零坐标校核。 3. **坐标全部未校验**:真实监测点与设备的 `coordinateValidation` 均为 `UNVERIFIED` / `renderable=false`,按契约不得上图。这是「没有显示」的一个独立且正确的成因,与图层接线问题必须分开处理。 4. **设施统计口径**:界面设施总数 `1001`,真实管网表 5268 条(成因归后端执行票 20)。 ## 范围 1. 明确设施点位与管线几何的**权威读取路径**(复用供水 GIS 链路还是驾驶舱自建读模型),并给出跨行业(燃气/供水/排水/窨井)覆盖判定。 2. 前端接线:把真实设施与管线数据接入 `modeLayerPlan`,并补齐引擎侧管线数据源的写入与渲染路径(含按行业/片区/模式过滤)。 3. 设施点位与管线不得借用监测点口径;坐标缺失或不合法时如实呈现为「无地图位置」,不静默隐藏、不伪造位置。 4. 几何校核纪律与决策票 07 一致:范围、零坐标、未验证坐标都不得静默渲染。 5. 与后端执行票 20 的设施映射修复协同:映射未修好前不得宣称设施数据真实完整。 ## 验收 浏览器实测:主场景内设施点位与管线按真实几何绘制,切换行业/片区/模式时图层正确显隐;无合法坐标的对象出现在「无地图位置」清单并给出原因;设施总数与后端真实口径一致。 ## 关联 发现于票 18;依赖后端执行票 20(设施映射);坐标校核来源缺失需另行排期。 ## 诊断与裁定(Codex,2026-10-12) ### 根因(推翻票面「后端已有可复用链路」的乐观前提) 票面现状事实 2 只看了供水侧,**不够**。一手核实(后端源码 `E:\pipe-ner-server` + 真实只读探测): 1. **驾驶舱侧一个几何端点都没有**。24 个 `Cockpit*Controller` 无一产出几何;`/api/cockpit/v1/map-snapshot` 实测返回顶层键为 `requestId, apiVersion, readOnly, page, pageSize, snapshotAt, total, batchUpdatedAt, complete, monitoringPoints, unassociatedEquipment, coordinateValidation, mapFilter` —— 无设施、无管线。`/infrastructure/base-archives` 只有聚合统计。 2. **全后端仅两处几何产出**:`WaterSupplyGisMapController` 与 `gasbasic/GisController`,**都在 `/api/cockpit/v1/**` 之外**。 3. **两者都要鉴权**:实测 `GET /waterSupply/gis/map/features` → HTTP 200 但业务 `code=401 认证失败`。ADR 0005 明文「免鉴权范围不扩展到后端其他业务路径」,故前端复用它会直接违反鉴权决策。 4. **数据底座按行业不均**(决定建模口径): | 行业 | 管线几何 | 构件属性 | | --- | --- | --- | | 燃气 | `pipe_network_base` 每条**仅 2 端点**直线段 | ✅ `gas_pipe_point`(经纬度 + `pointType`=阀门/调压/计量/阴极保护/**弯头**/**三通**/其它 + `networkId`) | | 供水 | 同为 2 端点直线段(996 条) | ❌ | | 排水 | 同为 2 端点直线段(3735 条) | ⚠️ `drainage.domain.PipeNetworkInfo`(管线/管点/排口),未见构件类型 | | 窨井 | 无管线;`ManholeBase` 有经纬度 | ❌ | 规模:5268 条(燃气 537 / 供水 996 / 排水 3735),管线总长 67.6 km。**管段之间没有共享端点拓扑**——「角/支管构件」无法从几何推断,只能用燃气 `gas_pipe_point.pointType` 的**真实属性**落位。 ### 维护者裁定(2026-10-12) | 问题 | 裁定 | | --- | --- | | 几何权威读取路径 | **新开后端驾驶舱几何读模型**,不在前端复用业务 GIS 端点 → 已建 [票 30](30-30.md),并接为本票 `Blocked by: #30` | | 跨行业覆盖 | **燃气出全套构件**(管段 + 阀门/弯头/三通,取自真实 `pointType`);供水/排水只画真实 2 点直线段;窨井按真实经纬度落点;**哪个行业没数据就如实说没数据** | | 渲染层 | **Cesium**。实测 `src/modules/cockpit` 一行 three/Tres 都没有,地图栈 100% Cesium;规模仅数百段,两引擎均无性能压力,而管线贴地与地形/影像/3D Tiles 正确遮挡是 Cesium 原生能力。Three.js 的优势要到上万级管段 + PBR 才兑现,引入却要新建第二个场景与整套相机/地形/拾取对齐层 | | 建模方式 | 按管点数据**程序化生成管线模型**,构件分四类:样条管道 / 角管道(弯头)/ 阀门管道 / 支管管道(三通) | **附带发现**:ADR 0005 中「Three.js 只在选中的重点管段或构件上增强」在代码里**根本没有实现**(`src/modules/cockpit` 零 three/Tres 引用),属纸面设想。选 Cesium 则该 ADR 无需修订。 ### 并行风险与规避 另一会话正实时编辑 `services/cesiumEngine.js` 与 `domain/overlapDevices.js`(内容为重叠聚合标签/光环动效,属票 45/47 域)。故本轮管线图层**另起模块**(`domain/pipeNetwork.js` + `services/pipeLayerService.js` + 引擎侧独立文件),从 `MapCanvas.vue` 平行挂载,**不改 `cesiumEngine.js` 一个字**,也不提交那两个文件。 ### 本轮实施(Codex,2026-10-12,提交 `cb4a5cb`) | 文件 | 作用 | | --- | --- | | `domain/pipeNetwork.js` | **纯判定层**:契约转换、构件分类、管径→线宽分档、行业筛选、坐标准入 | | `services/cesiumPipeLayer.js` | Cesium 渲染层:写 `cockpit-pipelines` 数据源,管段贴地折线 + 构件符号;upsert 不全量重建 | | `services/pipeLayerService.js` | 取数:读 `/map-facilities`(票 30 新建),收成分区 | | `stores/pipeNetworkStore.js` | 状态:与 `mapStore` 分开持有,行业切换**本地裁剪不重发请求** | | `mocks/contractMocks.js` | 几何样例:燃气全套构件、供排水直线段、窨井点位,外加三类「不得上图」 | | `CockpitShell.vue` | 接线:不再传 `facilities/pipelines: null`;几何随共享分区启动 | **关键实现取舍**:数据源名沿用引擎已有的 `cockpit-pipelines`,于是 `cesiumEngine.js` 里现成的 `pipelineSource(viewer)` 直接可用——**引擎文件一个字未改**,常量值两边一致(等并发会话落地后合并为单一来源)。构件先以**可辨识符号 + 中文标签**表达,glTF 预制构件实例化需素材库,仓库暂无管线素材,**不拿临时符号冒充"已建成的管道模型"**。 ### mock 模式真机验收(2026-10-12 11:20,经用户授权) 在 `public/cockpit/config.json` 临时切到 `dataSource: mock`(该文件 gitignored,验完已还原为 `real`),起 `yarn dev` + 浏览器实测。**这只能验证渲染路径与显隐,真实几何仍待票 30**。 - **管线与构件确实画在图上**(证据 [`mock-pipe-layer-rendered.png`](../verify/artifacts/t24/mock-pipe-layer-rendered.png)):橙=燃气、蓝=供水、紫=排水三条管段按真实几何贴地绘制;构件符号各带中文标签——**阀门**(红)、**弯头**(黄)、**三通**(绿)、**窨井**(灰),以及未建模类型**调压**(黄,保留后端原文,未被猜成已知类别)。 - **「无地图位置」清单与徽标自洽**:全域读数 **已加载 19 · 可上图 13 · 无地图位置 6**,与离线断言完全吻合(地图对象 5 + 管线 6 + 构件 5 = 可上图 13;地图 3 + 设施 3 = 不可图 6)。证明设施/管线确实并进了清单与徽标。 - **切行业按预期裁剪**:切「燃气」后 **已加载 12 · 可上图 9 · 无地图位置 3** —— 可上图 = 地图 2 + 燃气管线 3 + 燃气构件 4;不可图 = 燃气那三条(未校验 / 零坐标 / 缺经度)。徽标随筛选同步变化,未出现「徽标与清单不一致」。 ### 验证结果 - **离线回归 [`verify/t24-pipe-layer.mjs`](../verify/t24-pipe-layer.mjs) 16/16 通过**:四类构件分类、多段路径保留、未建模类型保留后端原文、三类坐标纪律(未校验/零坐标/缺经度)一律拦下且带中文原因、点数不足不补点、行业筛选、管径分档。 - **链路回归 [`verify/t24-pipe-layer-flow.mjs`](../verify/t24-pipe-layer-flow.mjs) 17/17 通过**:mock → 契约 → `pipeLayerService` → `pipeNetworkStore` → `modeLayerPlan` 形状整条链路;空场景、空态、取数失败;设施/管线并入「无地图位置」总览后的徽标与清单自洽;**四个一级模式 + 引导态 + 未知模式回落下的管线/设施图层显隐**(验收第 2 条中可离线验证的部分已覆盖)。 - 回归再次逮到一处**我自己引入的算错**:`coordinateOverview` 扩展后 `loaded.total` 漏加设施的不可图数(11 ≠ 11+3)。由 G2 的 `total === renderable + blocked` 不变式当场转红,已修。 - 既有回归无回退:`t46-mode-layer-plan`、`t04-config`、`t05-adapter`(283)、`t07-coordinate`(39)、`t08-map-summary`(44)、`t48-real-envelope` 全绿。 ### 链路回归逮到的真缺陷(已修,提交 `3f12751`) 补链路回归时发现**取数失败会被显示成「这里没有管线」**: `pipeNetworkStore` 原先在失败时交出空数组 `[]`,而 `modeLayerPlan.planNonPointLayers` 用 `hasArray()` 判定就绪——于是几何请求失败(未配地址、端点不存在、后端 500)时,图层状态仍是 `ready`,界面不会出现「管线数据未就绪」说明。这正是本项目明令禁止的那类谎报(`infrastructureApi.js` 里已有同一陷阱的注释:「把一次读取失败说成这里本来就没有危险源——那是撒谎」)。 修复:`facilities`/`pipelines` 改为**取数成功(含真实为空)才交数组,失败或未取过一律交 `null`**,由计划如实降级为 `degraded` 并给出说明。`t24-pipe-layer-flow` 的 F9 锁定该行为。 ### 验收自查:第 4 条已查实不通过(新增后端票 31) 对抗式自查逐项核对验收,发现**设施总数口径在三个后端端点间不一致**(只读实测 2026-10-12): | 端点 | `totals.facilityCount` | 行业拆分 | | --- | --- | --- | | `/infrastructure/base-archives` | **5278** | 燃气 537 / 供水 1004 / **排水 3737** / 窨井 0 | | `/overview/infrastructure` | **5276** | 燃气 537 / 供水 1004 / **排水 3735** / 窨井 0 | | `/infrastructure/management-information` | **5273** | 片区×行业视图 | 三者**各自内部自洽**(5278 = 537+1004+3737;5276 = 537+1004+3735),差异收敛到**排水 2 条**,说明三套服务对同一张 `pipe_network_base` 各自遍历、各自定口径。 之所以真机上肉眼可见:`overviewStore` **设计上**就把两个端点并成一张卡片(总数与行业构成取 `/overview/infrastructure`,片区分布取 `/infrastructure/base-archives`),口径一不一致,同一张卡片上就并列出现「设施总数 5276」与「片区分布 未分区 5278」。 **前端没有绕过**:任选一个数字显示等于把后端缺陷藏起来,违反本项目「不静默」纪律。故如实立 [票 31](31-31.md) 并接为本票 `Blocked by: #31`。 ### 收口(2026-10-12 晚):四条验收全部达成 票 30、31 已收口,本轮对齐真实契约后完成真机验收。 **契约对齐(提交 `c4aa10e`)**:票 30 的真实端点是 `GET /api/cockpit/v1/infrastructure/geometry`(**不是**我原先约定的 `/map-facilities`,那个 404 是路径没对上)。实测真实形状并逐项对齐: | 差异 | 原约定 | 真实契约 | 处置 | | --- | --- | --- | --- | | 路径 | `/map-facilities` | `/infrastructure/geometry` | 改常量 | | feature 判别 | 自定 `kind` 字段 | 标准 GeoJSON,**无 `kind`** | 改按 `geometry.type` 判;`geometry:null` 时按 `typeCode` 判 | | `pointType` | 中文(阀门/弯头/三通) | 实测**英文** `valve` | 两侧词表都认 | | `coverage` | 自定 4 行 `geometryAvailable` | **9 行三态**(`AVAILABLE`/`EMPTY`/`UNAVAILABLE` + `reason`) | 按行归并、缺口如实记 `gaps` | | 非法坐标 | 保留几何但 `renderable:false` | **`geometry: null`**,原值在 `sourceCoordinates` | 由既有守卫拦下 | | 新增 reason | — | `OUTSIDE_PRIMARY_AREA`(后端 `8013a207` 改为主场景区自动准入) | 已有专属文案 | **真机验收(`real` 模式,2026-10-12 20:54,浏览器实测)**: - **验收第 1 条(真实几何绘制)通过**:真实 **5268 条管线**绘制在地图上(沿道路的紫蓝色线段,见 [`real-geometry-rendered.png`](../verify/artifacts/t24/real-geometry-rendered.png));断言面 `data-map-pipes="5268"`、`data-map-pipe-components="1"`。 - **验收第 2 条(图层显隐)通过**:管线随模式常驻、设施只在总体概览/基础设施可见(离线断言 H1–H4 覆盖四个一级模式 + 引导态 + 未知模式回落)。 - **验收第 3 条(无地图位置清单)通过**:真实读数 **已加载 5287 · 可上图 5281 · 无地图位置 6**,与后端口径逐项吻合(几何 5272 + 地图对象 15 = 5287;可上图 5269+12 = 5281;不可图 3+3 = 6),徽标与清单同源。 - **验收第 4 条(设施总数一致)通过**:三个端点实测**全部 5283**(票 31 已把口径收敛),界面「设施底数 5283」与之一致。 **遗留(不阻塞本票收口)**: 1. `services/mapService.js` / `components/MapCanvas.vue` 两处桥接改动**仍未提交**——同文件含并发会话的在制品(对方已提交票 25/47 等)。我的桥接经本轮真机验证**确实生效**(`data-map-pipes` 就是它写进去的),归属清楚,待对方收工后单独提交。 2. 构件素材未做 → 已另立[票 32](32-32.md)。 3. 片区筛选不作用到管线(几何无片区字段),已如实标记 `districtApplied:false`。 ### 下一步 1. 并发会话收工后提交 `mapService.js` / `MapCanvas.vue` 两处桥接。 2. [票 32](32-32.md) 补 glTF 构件素材与实例化。 3. 真实数据下「切燃气行业 → 地图摘要变无数据」的现象属监测点筛选域(监测点当前多为无设备态),与本票无关,未在本票处置。 ## Comments ### local — 2026-10-12T01:37:47.935Z 按用户指派认领。本轮先做诊断(范围第 1 项「权威读取路径」是决策点,需 grilling 澄清)。已确认:阻塞项票 20 已关闭 → 本票当前可做;无 needs-triage。并行风险:另一会话正在实时编辑 src/modules/cockpit/services/cesiumEngine.js 与 domain/overlapDevices.js(内容为重叠聚合标签/光环动效,属票 45/47 域),与本票设施/管线图层不重叠,但其未提交改动本轮不碰、不提交。 ## Answer