24-24.md 15 KB

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,并接为本票 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):橙=燃气、蓝=供水、紫=排水三条管段按真实几何贴地绘制;构件符号各带中文标签——阀门(红)、弯头(黄)、三通(绿)、窨井(灰),以及未建模类型调压(黄,保留后端原文,未被猜成已知类别)。
  • 「无地图位置」清单与徽标自洽:全域读数 已加载 19 · 可上图 13 · 无地图位置 6,与离线断言完全吻合(地图对象 5 + 管线 6 + 构件 5 = 可上图 13;地图 3 + 设施 3 = 不可图 6)。证明设施/管线确实并进了清单与徽标。
  • 切行业按预期裁剪:切「燃气」后 已加载 12 · 可上图 9 · 无地图位置 3 —— 可上图 = 地图 2 + 燃气管线 3 + 燃气构件 4;不可图 = 燃气那三条(未校验 / 零坐标 / 缺经度)。徽标随筛选同步变化,未出现「徽标与清单不一致」。

验证结果

  • 离线回归 verify/t24-pipe-layer.mjs 16/16 通过:四类构件分类、多段路径保留、未建模类型保留后端原文、三类坐标纪律(未校验/零坐标/缺经度)一律拦下且带中文原因、点数不足不补点、行业筛选、管径分档。
  • 链路回归 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 并接为本票 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);断言面 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。
  3. 片区筛选不作用到管线(几何无片区字段),已如实标记 districtApplied:false。

下一步

  1. 并发会话收工后提交 mapService.js / MapCanvas.vue 两处桥接。
  2. 票 32 补 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