32-untitled.md 16 KB

坐标自动准入(取消人工校核前置)与主场景区边界冲突

Type: implementation Status: claimed Assignee: codex Label: ready-for-agent Blocked by: None Category: backend-coordinate-admission

进度

✅ 2026-10-12 维护者重启后端,真实接口闭环复验通过。 后端 8013a20:CockpitCoordinateValidator 改为「结构合法 + 主场景区内」自动准入,新增 OUTSIDE_PRIMARY_AREA,coordinateVerified 不再影响结论;12 个测试文件重基线 + 新增 21 例判据单测。

重启后实测(真实在线接口,非离线复算):

端点 修复前(旧构建) 重启后实测 离线探针预测 一致
/map-snapshot 可上图 0 / 15 12 / 15 12 / 15 ✅
/map-snapshot unverifiedCount 15 0 0 ✅
/map-snapshot invalidCount 0 3(均 MISSING_LONGITUDE) 3 ✅
/infrastructure/geometry 可上图 0 / 5272 5269 / 5272 5269 / 5272 ✅
逐对象 state 全 UNVERIFIED 12×VALID/renderable:true、3×INVALID/MISSING_LONGITUDE — ✅

真实接口结果与重启前用已编译生产判据对同一批数据做的离线预测逐项吻合,说明预测方法与线上行为一致。零 UNVERIFIED 残留。

验证(均由我本人在落盘后独立复跑,非仅采信执行 agent 汇报):

  • 针对性 -Dtest=*Cockpit*Test,*BusinessCockpit*Test → 486 / 0 / 0,BUILD SUCCESS(基线 465 / 15F / 1E)。
  • 全量 -pl zksy-admin -am test → 全 reactor BUILD SUCCESS,zksy-admin 505 / 0 / 0。

前端 bf8d21b:原因词表同步 + t07 39/39。真实数据离线复算可上图 12/15 与 5269/5272。

第二轮补强(非自证式验证 + 文档/注释同步)

  • 真实接口数据 × 已编译生产判据联测(verify/artifacts/t32/LiveCoordinateAdmissionProbe.java,只读):把真实后端导出的 15 个地图对象 + 5272 条几何坐标喂给线上要跑的那一份 CockpitCoordinateValidator(而非另写的复算脚本),四项断言全 PASS:map 12/15、geo 5269/5272、原因码分布 MISSING_LONGITUDE=3 / LONGITUDE_OUT_OF_RANGE=2 / OUTSIDE_PRIMARY_AREA=1、零 UNVERIFIED。
  • 过程中探针自身出过一次解析错误(把经度当编码、只把纬度当载荷,误报 map 3/15);修正后重跑通过。该错误只影响探针读数,不触及生产判据——如实记录以免后来者误读中间结果。
  • 更正一处旧结论:那 3 个被挡的地图对象(41010500001320000001、41010500001320000002、TEST-CBLL222222222)经查响应里 longitude/latitude 是真正的 null(缺坐标),本票早前写的「(0,0) 零坐标」是我的读数误判。结论不变(都该被挡下),新判据下原因是 MISSING_LONGITUDE。
  • 管理端 E:\city-life-line 无需改动,已核实:它对 coordinateValidation / coordinateVerified 零引用——这反过来印证了根因(既无校核字段、也无校核入口)。
  • 同步 6 处会误导后人的注释(后端 1427a67):各适配器 javadoc 原先写着「无校核字段 → 按未校核上报、由契约层判定为不可渲染」——这正是本次 bug 的认知来源,判据改了而注释不改,下一个人会照原路再走一遍。纯注释改动,改后复跑仍 486 / 0 / 0。
  • 同步 4 份文档(f9c89ba):ADR 0002 原句「未通过校验的对象不上图」、ADR 0005、cockpit-deployment.md 的自检步骤与可观察字段表——按项目「代码优先」规则更新为 2026-10-12 自动准入口径。部署文档原先教运维「若真实数据均未校验,记为无可上图样本」,不改会让人把正常状态当缺口上报。
  • 前端 5096454 修正 coordinate.js 头部注释「六取一」→「七取一」。

一处数字纠正如实记录:我最初推算 CockpitMapSnapshotControllerTest 汇总应为 renderableCount=7 是错的——7 个对象里 EQ-FREE-001(181)与 EQ-FREE-004(0,0)仍非法,可上图应为 3 个点位 + 2 个设备 = 5。执行 agent 按实测写 5 / 2 / 0 并纠正了我,票面以实测为准。

与本次无关的既有失败(已独立复核归因):zksy-system 的 AlarmLifecycleServiceTest.insertWritesLogBeforeCommittingWithoutTrigger —— 断言期望的日志串与实现不一致(alarmContent 已含「实际值/阈值」格式化文本,被再次拼接),单跑复现 same failure;该 alarm 域对 cockpit 零引用,物理上不受本次改动影响。未擅自修它。

待办:① 维护者重启后端加载新构建;② 重启后复验 renderableCount=12 / unverifiedCount=0;③ 对区外口径 A(严格小区块,当前实现)vs B(放宽到影像覆盖范围以保住框外可定位)拍板——本实现与 ADR 0009 的框外定位例外存在冲突;④ 2 条经度越界窨井属真实数据错误,需业务侧订正。

来源

维护者 2026-10-12 实报:真实后端有大量监测点、设备点位与管线数据,但一个都没有上屏,全部显示「坐标未经校验」,且找不到任何校验入口。随后裁定:取消该人工校核步骤(「所有进入数据库中的点位坐标都是已经过校验后的了」),或改为自动规则——只有落在主场景区内的坐标才正常显示。

现象与实测(修复前)

对真实后端 192.168.110.96:8301 只读 GET:

端点 对象数 可上图 未校验 非法
/map-snapshot 15 0 15 0
/infrastructure/geometry 5272 0 5270 2(经度越界)

样本 GS001 坐标 110.399876, 28.458287 数值完全正常,仍被判 UNVERIFIED_COORDINATE / renderable=false。

根因(已确认)

判定链三段,每段都指向同一个缺失:

  1. CockpitCoordinateValidator.validate() 第一道判断是 if (!coordinateVerified) return UNVERIFIED/renderable=false——未校核优先于所有数值检查。
  2. 真实适配器一律传 false:BusinessCockpitMapSnapshotAdapter(监测点、设备)、BusinessCockpitDeviceFactResolver、CockpitFacilityGeometryService(先用 true 做结构检查,通过后再故意用 false 降级成未校核)。
  3. 全后端搜索没有任何一行代码把 coordinateVerified 置为 true;monitor_point、equipment_base、pipe_network_base 都没有校核列;管理端也没有任何校核接口或页面。

前端没有误判:唯一准入判据只认后端 renderable === true,后端给 false 就不画——按原契约是正确行为。后端 docs/cockpit/README.md 自己也写过「可信坐标校核来源需另行排期」,即这是已知契约缺口,不是前端 bug。

维护者裁定

  • 不做人工校核入口(该步骤没有必要,入库坐标即视为可信来源)。
  • 改为自动规则:数值合法 + 落在主场景区内才可上图。
  • 范围选择:只用主场景小区块(map.primaryArea.bounds),不用更大的影像覆盖范围。

实施(已完成)

判定顺序(唯一一处 CockpitCoordinateValidator):

  1. 缺经度 → INVALID/MISSING_LONGITUDE
  2. 缺纬度 → INVALID/MISSING_LATITUDE
  3. 经度越界 → INVALID/LONGITUDE_OUT_OF_RANGE
  4. 纬度越界 → INVALID/LATITUDE_OUT_OF_RANGE
  5. 零坐标 → INVALID/ZERO_COORDINATE
  6. 数值合法但区外 → INVALID/**OUTSIDE_PRIMARY_AREA**(新增原因码)
  7. 其余 → VALID/reason=null/renderable=true

结构错误优先于区外判定,因此既有的非法坐标拦截一条都没有被削弱。coordinateVerified 入参保留(不改 16 处调用点)但不再参与判定;新流程不再产生 UNVERIFIED。

  • 无数据库改动,无管理端改动,不伪造坐标。
  • 前端 COORDINATE_REASON_TEXT 同步新增 OUTSIDE_PRIMARY_AREA → 「不在主场景区范围内」。
  • 后端文档 docs/cockpit/README.md 的坐标章节已同步改写(原「未验证禁止渲染」表述作废)。

实测效果(真实接口数据 × 已编译生产判据)

用编译好的生产类 CockpitCoordinateValidator 逐条判定从真实接口导出的坐标(探针 verify/artifacts/t32/LiveCoordinateAdmissionProbe.java,只读):

端点 修复前可上图 修复后可上图 被挡下(按原因码)
/map-snapshot 0 / 15 12 / 15 MISSING_LONGITUDE ×3
/infrastructure/geometry 0 / 5272 5269 / 5272 LONGITUDE_OUT_OF_RANGE ×2、OUTSIDE_PRIMARY_AREA ×1

被挡下的 3 个地图对象是 41010500001320000001、41010500001320000002、TEST-CBLL222222222——经核实它们的 longitude/latitude 在响应里是真正的 null(缺坐标),不是零坐标。探针断言 map 12/15、geo 5269/5272、零 UNVERIFIED 三项全部 PASS。

(早前本票一度把这 3 个写成「(0,0) 零坐标」,那是我读数的误判,已在此更正;结论不变——它们本就该被挡下。)

额外核对:没有一条管线跨越主场景区边界(跨界 0 条),所以小区块不会把管线切坏或让整段变 geometry=null。

被挡下的 3 条几何 —— 均为业务侧脏数据(非判据问题)

已逐条读出原值,可直接交业务侧修正;不需要也不应该由代码"顺手"改:

来源 编码 名称 原始坐标 原因码 判读
manhole_base YL-YJ-001 沅陵县窨井001 1212, 1212 LONGITUDE_OUT_OF_RANGE 疑似把 121.2,121.2 的小数点漏了,或占位值未替换
manhole_base YL-YJ-002 沅陵县窨井002 1212, 1212 LONGITUDE_OUT_OF_RANGE 同上
gas_pipe_point GD-001 管点001 12, 12 OUTSIDE_PRIMARY_AREA 数值合法但不在沅陵,明显是占位/测试数据

三者都本该被挡下:判据在如实工作,不是判据误伤。修正归业务侧(equipment_base / 管点表直接改值),或确认为测试数据后清理。

验证

  • 后端针对性测试:mvn -o -pl pipe-network-service/zksy-admin -am test "-Dtest=*Cockpit*Test,*BusinessCockpit*Test" "-DfailIfNoTests=false" → 486 / 0 / 0,BUILD SUCCESS(我自己复跑确认,非仅采信执行 agent 汇报)。
  • 后端全量测试:mvn -o -pl pipe-network-service/zksy-admin -am test → 全 reactor BUILD SUCCESS,zksy-admin 505 / 0 / 0(同样自己复跑确认)。
  • 新增 CockpitCoordinateValidatorTest 21 用例:区内 VALID、东南西北四向区外 OUTSIDE_PRIMARY_AREA、闭区间四角边界命中与边界外一丁点不命中、结构错误优先于区外、数值合法但区外、coordinateVerified 传 true/false 结论完全一致、insidePrimaryArea(null,*) 两个方向 → false。
  • 12 个受影响的既有测试文件已按新契约重基线;非法坐标参数化用例 CsvSource 一行未改(181、-181、91、-91、(0,0)、null 全部保留作结构错误样本),原始坐标保留断言与「被拦对象仍计入统计」断言均保留;3 处语义自相矛盾的方法名已如实改名(不是把断言反过来)。
  • 前端 t07-coordinate.mjs 39/39 通过(原因词表 6 → 7 已同步)。
  • 真实数据 × 已编译生产判据联测(本轮新增,非自证式):LiveCoordinateAdmissionProbe 读取真实接口导出的 15 个地图对象 + 5272 条几何坐标,逐条调用线上要跑的那一份 CockpitCoordinateValidator(不是另写的复算脚本),结果 map 12/15、geo 5269/5272、零 UNVERIFIED 三项断言全部 PASS。过程中探针自身出过一次解析错误(把经度当编码、只喂纬度,误报 map 3/15),已修正并重跑;这类错误只影响探针读数,不影响生产判据。
  • 已确认与本变更无关的既有失败:前端 t17(MANAGEMENT_REGION_GAP 导出缺失)、t05 D1(overviewStore.js 出现 deviceStatistics,并行会话在改);后端 AlarmLifecycleServiceTest 既有失败(还原 validator 到 HEAD 后原样复现)。

✅ 区外口径已裁定:A(严格主场景区)

ADR 0009 与票 37 在 2026-09-28 曾裁定「点选/定位到框外对象是例外」;本次准入规则让区外对象 renderable: false,与此例外存在张力。

2026-10-12 维护者裁定:选 A(严格区块)。 已同步修订 ADR 0009:

  • 准入保持严格主场景区,接受「框外点位不上图、不可定位」。
  • 该例外经产品路径已不可达(mapLocate 对不可上图对象返回 BLOCKED;selectMonitoringPoint 先过 isRenderable),不会再出现"点选框外对象、镜头飞出去"。
  • 相机层的例外实现(cameraControl.js 定位豁免 + outsideArea 读数)保留不删:它是相机层的正确保证,票 37 用例继续有效。
  • 同源警告已写入 ADR:准入矩形在后端是常量、在限位侧是配置 map.primaryArea.bounds,当前两者逐位相同;改配置必须同时改后端常量。

影响面:当前真实数据全部落在区内,所以今天无可见行为差异;差别只在未来接入框外合法点位时——那时按 A 它们不上图、留在清单并显示 OUTSIDE_PRIMARY_AREA 原因。

待办

  • 维护者重启后端实例加载新构建(2026-10-12 已完成)。
  • 重启后复验真实接口:map-snapshot 12/15、unverifiedCount=0;infrastructure/geometry 5269/5272(已完成,见上表)。
  • 区外口径拍板:选 A(严格主场景区),已同步 ADR 0009(2026-10-12)。
  • 3 条脏几何数据(YL-YJ-001/002 = 1212,1212;GD-001 = 12,12)需业务侧订正或确认为测试数据后清理——见下表,属数据问题、非代码问题。

Out of scope

  • 前端管线图层接线(/map-facilities → /infrastructure/geometry 路径不匹配)——维护者明确指示「管线先放着,本来也还没开发完全」,归票 24。
  • 人工坐标校核字段、校核人/时间/来源与回填——已被本次裁定取消,不再需要。
  • 后端其它业务路径与写接口。

Comments

local — 2026-10-12T11:07:12.071Z

实施已完成并提交:后端 8013a20(判据重写 + 13 个测试重基线 + 新增 16 例 CockpitCoordinateValidatorTest + docs/cockpit/README.md 同步),前端 bf8d21b(原因词表新增 OUTSIDE_PRIMARY_AREA + t07 回归同步,39/39 通过)。自验:mvn -o -pl pipe-network-service/zksy-admin -am test "-Dtest=*Cockpit*Test,*BusinessCockpit*Test" "-DfailIfNoTests=false" → 486/0/0 BUILD SUCCESS(我本人复跑,非仅采信队友汇报)。红线复核:非法坐标参数化 CsvSource 一行未改,原始坐标保留断言与「被拦对象仍计入统计」断言均保留;3 处语义自相矛盾的方法名已如实改名。真实数据离线复算:map-snapshot 可上图 0/15 → 12/15(挡下 3 个 (0,0)),geometry 0/5272 → 5269/5272;跨界管线 0 条。未重启/未构建(遵守 AGENTS.md),真实接口仍是旧构建 renderableCount=0,需维护者重启后复验。临时镜像 268MB 与日志已清理,IDE 文件已还原。