import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * T32 真实数据 × 生产判据 联测探针。 * *

只读:读取从真实后端导出的坐标文件,逐条调用**已编译的生产类** * {@code com.zksy.cockpit.foundation.CockpitCoordinateValidator},统计准入分布。 * 不修改数据、不启动服务,只回答一个问题:维护者重启后端后,真实数据会有多少可上图。 * *

被测判据就是线上要跑的那一份,因此本探针验证的是真实实现而非另写的复算脚本。 * 用法:java -cp LiveCoordinateAdmissionProbe */ public final class LiveCoordinateAdmissionProbe { public static void main(String[] args) throws Exception { Path input = Path.of(args[0]); List lines = Files.readAllLines(input, StandardCharsets.UTF_8); Map byReason = new LinkedHashMap<>(); int mapRenderable = 0; int mapTotal = 0; int geometryRenderable = 0; int geometryTotal = 0; List blockedSamples = new ArrayList<>(); int boundaryHits = 0; for (String raw : lines) { String line = raw.trim(); if (line.isEmpty()) continue; String[] parts = line.split("\\|", -1); if (parts.length < 3) continue; // 行格式:地图对象 `M|code|lon|lat`;几何 `G|source|lon,lat[;lon,lat...]`。 boolean geometry = "G".equals(parts[0]); String label = geometry ? parts[1] : "map-object"; String code = geometry ? "" : parts[1]; String payload = geometry ? parts[2] : (parts[2] + "," + parts[3]); boolean renderable = true; String reason = ""; // 管段按逐端点校验:任一端不可渲染则整段不可渲染(与服务层同口径)。 for (String pair : payload.split(";")) { if (pair.isEmpty()) continue; String[] xy = pair.split(",", -1); BigDecimal lon = parse(xy.length > 0 ? xy[0] : null); BigDecimal lat = parse(xy.length > 1 ? xy[1] : null); var validation = com.zksy.cockpit.foundation.CockpitCoordinateValidator .validate(lon, lat, false); if (lon != null && lat != null && (lon.compareTo(com.zksy.cockpit.foundation.CockpitCoordinateValidator.WEST) == 0 || lon.compareTo(com.zksy.cockpit.foundation.CockpitCoordinateValidator.EAST) == 0 || lat.compareTo(com.zksy.cockpit.foundation.CockpitCoordinateValidator.SOUTH) == 0 || lat.compareTo(com.zksy.cockpit.foundation.CockpitCoordinateValidator.NORTH) == 0)) { boundaryHits++; } if (!validation.isRenderable()) { renderable = false; reason = validation.getReason(); break; } } if (!renderable) { byReason.merge(reason, 1, Integer::sum); if (blockedSamples.size() < 6) { blockedSamples.add(label + " " + code + " → " + reason); } } if (geometry) { geometryTotal++; if (renderable) geometryRenderable++; } else { mapTotal++; if (renderable) mapRenderable++; } } System.out.println("input=" + input.getFileName()); System.out.println("map-snapshot : renderable " + mapRenderable + "/" + mapTotal); System.out.println("infrastructure/geo: renderable " + geometryRenderable + "/" + geometryTotal); System.out.println("blocked by reason : " + byReason); System.out.println("boundary hits : " + boundaryHits); System.out.println("blocked samples : " + blockedSamples); boolean mapMatches = mapRenderable == 12 && mapTotal == 15; boolean geomMatches = geometryRenderable == 5269 && geometryTotal == 5272; boolean invalidOnly = byReason.keySet().stream() .allMatch(r -> "ZERO_COORDINATE".equals(r) || "LONGITUDE_OUT_OF_RANGE".equals(r) || "LATITUDE_OUT_OF_RANGE".equals(r) || "MISSING_LONGITUDE".equals(r) || "MISSING_LATITUDE".equals(r) || "OUTSIDE_PRIMARY_AREA".equals(r)); // 原因码分布也要钉住:只断言总数会掩盖「本来缺坐标的被误报成别的合法原因」。 Map expected = new LinkedHashMap<>(); expected.put("MISSING_LONGITUDE", 3); expected.put("LONGITUDE_OUT_OF_RANGE", 2); expected.put("OUTSIDE_PRIMARY_AREA", 1); boolean reasonsMatch = expected.equals(byReason); System.out.println("EXPECT map 12/15 : " + (mapMatches ? "PASS" : "FAIL")); System.out.println("EXPECT geo 5269/5272: " + (geomMatches ? "PASS" : "FAIL")); System.out.println("EXPECT reason split: " + (reasonsMatch ? "PASS" : "FAIL") + " (expected " + expected + ")"); System.out.println("no UNVERIFIED left : " + (invalidOnly ? "PASS" : "FAIL")); if (!mapMatches || !geomMatches || !invalidOnly || !reasonsMatch) { System.exit(1); } } private static BigDecimal parse(String value) { if (value == null) return null; String trimmed = value.trim(); if (trimmed.isEmpty() || "null".equalsIgnoreCase(trimmed)) return null; return new BigDecimal(trimmed); } }