Просмотр исходного кода

feat(cockpit): 补齐点位关联设备坐标校验

Kazerin 3 недель назад
Родитель
Сommit
8006fb43d7

+ 14 - 0
docs/cockpit/README.md

@@ -85,6 +85,20 @@
 - 适配器未给出 `coordinateVerified` 时按**未验证**处理(`state=UNVERIFIED`、`reason=UNVERIFIED_COORDINATE`、`renderable=false`)——字典要求「未验证禁止渲染」,服务端不臆造验证状态。报警族在数据可得时另带 `thresholdMinValue`/`thresholdMaxValue`(预警族为 `null`)。
 - 适配器未给出 `coordinateVerified` 时按**未验证**处理(`state=UNVERIFIED`、`reason=UNVERIFIED_COORDINATE`、`renderable=false`)——字典要求「未验证禁止渲染」,服务端不臆造验证状态。报警族在数据可得时另带 `thresholdMinValue`/`thresholdMaxValue`(预警族为 `null`)。
 - `events/history-analysis` 的 `rows[]` 是**小时桶聚合行**,不存在逐事件行,因此不携带 `coordinateValidation` 与阈值字段(逐事件坐标只在 `events/current` 提供)。
 - `events/history-analysis` 的 `rows[]` 是**小时桶聚合行**,不存在逐事件行,因此不携带 `coordinateValidation` 与阈值字段(逐事件坐标只在 `events/current` 提供)。
 
 
+### 监测点详情设备坐标
+
+`GET /api/cockpit/v1/monitoring-points/{monitoringPointCode}` 的 `data.devices[]` 保留点位下的全部关联设备(包括离线设备),以 `deviceCode` 作为唯一关联键,并返回设备自身坐标与状态:
+
+| 字段 | 语义 |
+| --- | --- |
+| `longitude` / `latitude` | 来自 `equipment_base` 的设备坐标;不会复制所属监测点坐标,也不会人工偏移。缺失时字段为 `null`。 |
+| `coordinateValidation.state` | `VALID`、`INVALID` 或 `UNVERIFIED`。 |
+| `coordinateValidation.reason` | 不可上图时的非空原因,例如 `MISSING_LONGITUDE`、`LONGITUDE_OUT_OF_RANGE`、`ZERO_COORDINATE` 或 `UNVERIFIED_COORDINATE`。 |
+| `coordinateValidation.renderable` | 只有可信校核来源存在且坐标有效时为 `true`;前端仅在此为 `true` 时绘制设备点位。 |
+| `coordinateValidation.crs` | 当前统一为 `WGS84`。 |
+
+当前 `equipment_base` 没有可信坐标校核字段,因此真实适配器透传设备坐标但按 `UNVERIFIED_COORDINATE` 降级,`renderable=false`;设备仍保留在详情清单中。点位详情与 `map-snapshot` 都从同一设备关联关系和设备编码集合构造,均不因离线状态或坐标不可渲染而静默删除设备。
+
 ## 5. WebSocket 实时增量
 ## 5. WebSocket 实时增量
 
 
 端点:`/ws/deviceStatus`。
 端点:`/ws/deviceStatus`。

+ 8 - 0
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/BusinessCockpitDeviceFactResolverTest.java

@@ -17,6 +17,7 @@ import com.zksy.ops.service.WorkOrderService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
@@ -66,6 +67,11 @@ class BusinessCockpitDeviceFactResolverTest {
         assertTrue(orderQuery.getValue().getSqlSegment().contains("device_id"), "工单必须按设备主键关联");
         assertTrue(orderQuery.getValue().getSqlSegment().contains("device_id"), "工单必须按设备主键关联");
         assertTrue(orderQuery.getValue().getParamNameValuePairs().containsValue("ID-DEV-1"));
         assertTrue(orderQuery.getValue().getParamNameValuePairs().containsValue("ID-DEV-1"));
         assertEquals("压力计", fact.getDeviceTypeName());
         assertEquals("压力计", fact.getDeviceTypeName());
+        assertEquals(new BigDecimal("110.4025"), fact.getLongitude());
+        assertEquals(new BigDecimal("28.4500"), fact.getLatitude());
+        assertEquals("UNVERIFIED", fact.getCoordinateValidation().getState());
+        assertEquals("UNVERIFIED_COORDINATE", fact.getCoordinateValidation().getReason());
+        assertFalse(fact.getCoordinateValidation().isRenderable());
         assertEquals(CockpitMonitoringPointStatus.ALARM,
         assertEquals(CockpitMonitoringPointStatus.ALARM,
                 CockpitMonitoringPointStatusResolver.deviceStatus(fact));
                 CockpitMonitoringPointStatusResolver.deviceStatus(fact));
     }
     }
@@ -129,6 +135,8 @@ class BusinessCockpitDeviceFactResolverTest {
         device.setEquipmentId("ID-" + code);
         device.setEquipmentId("ID-" + code);
         device.setEquipmentCode(code);
         device.setEquipmentCode(code);
         device.setEquipmentName(code);
         device.setEquipmentName(code);
+        device.setLongitude(new BigDecimal("110.4025"));
+        device.setLatitude(new BigDecimal("28.4500"));
         return device;
         return device;
     }
     }
 
 

+ 16 - 2
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/BusinessCockpitMapSnapshotAdapterTest.java

@@ -68,7 +68,8 @@ class BusinessCockpitMapSnapshotAdapterTest {
         EquipmentBase bound = device("D-1", "Z-TEST-RQXL01", "Z-测试燃气泄漏01", "22",
         EquipmentBase bound = device("D-1", "Z-TEST-RQXL01", "Z-测试燃气泄漏01", "22",
                 new BigDecimal("110.4024"), new BigDecimal("28.4499"));
                 new BigDecimal("110.4024"), new BigDecimal("28.4499"));
         stubDevices(Collections.singletonList(bound), Collections.singletonList(bound));
         stubDevices(Collections.singletonList(bound), Collections.singletonList(bound));
-        when(deviceFactResolver.resolve(any(), anyString())).thenReturn(fact("Z-TEST-RQXL01"));
+        when(deviceFactResolver.resolve(any(), anyString())).thenReturn(
+                fact("Z-TEST-RQXL01", new BigDecimal("110.4025"), new BigDecimal("28.4500"), false));
 
 
         CockpitMapSnapshot snapshot = adapter.load(null);
         CockpitMapSnapshot snapshot = adapter.load(null);
 
 
@@ -78,6 +79,9 @@ class BusinessCockpitMapSnapshotAdapterTest {
         assertEquals("22", point.getTypeCode(), "关联设备类型唯一时点位类型可推导");
         assertEquals("22", point.getTypeCode(), "关联设备类型唯一时点位类型可推导");
         assertEquals(1, point.getDevices().size());
         assertEquals(1, point.getDevices().size());
         assertEquals("Z-TEST-RQXL01", point.getDevices().get(0).getDeviceCode());
         assertEquals("Z-TEST-RQXL01", point.getDevices().get(0).getDeviceCode());
+        assertEquals(new BigDecimal("110.4025"), point.getDevices().get(0).getLongitude());
+        assertEquals(new BigDecimal("28.4500"), point.getDevices().get(0).getLatitude());
+        assertFalse(point.getDevices().get(0).getCoordinateValidation().isRenderable());
 
 
         assertTrue(snapshot.getTypes().stream().anyMatch(
         assertTrue(snapshot.getTypes().stream().anyMatch(
                 type -> "22".equals(type.getCode()) && "GAS".equals(type.getIndustry())),
                 type -> "22".equals(type.getCode()) && "GAS".equals(type.getIndustry())),
@@ -102,6 +106,10 @@ class BusinessCockpitMapSnapshotAdapterTest {
 
 
         CockpitMapSnapshot snapshot = adapter.load(null);
         CockpitMapSnapshot snapshot = adapter.load(null);
         assertEquals(2, snapshot.getSmartDevices().size(), "适配器上报全量设备");
         assertEquals(2, snapshot.getSmartDevices().size(), "适配器上报全量设备");
+        assertEquals(1, snapshot.getMonitoringPoints().get(0).getDevices().size());
+        assertEquals("BOUND-1", snapshot.getMonitoringPoints().get(0).getDevices().get(0).getDeviceCode());
+        assertFalse(snapshot.getMonitoringPoints().get(0).getDevices().get(0).isOnline(),
+                "离线设备仍属于点位关联集合");
 
 
         CockpitMapSnapshotService service = new CockpitMapSnapshotService(
         CockpitMapSnapshotService service = new CockpitMapSnapshotService(
                 adapter, Clock.fixed(Instant.parse("2026-09-02T02:00:00Z"), ZoneOffset.UTC));
                 adapter, Clock.fixed(Instant.parse("2026-09-02T02:00:00Z"), ZoneOffset.UTC));
@@ -157,8 +165,14 @@ class BusinessCockpitMapSnapshotAdapterTest {
     // ---- fixtures ----
     // ---- fixtures ----
 
 
     private static CockpitMonitoringPointDeviceSnapshot fact(String code) {
     private static CockpitMonitoringPointDeviceSnapshot fact(String code) {
+        return fact(code, null, null, false);
+    }
+
+    private static CockpitMonitoringPointDeviceSnapshot fact(
+            String code, BigDecimal longitude, BigDecimal latitude, boolean online) {
         return new CockpitMonitoringPointDeviceSnapshot(
         return new CockpitMonitoringPointDeviceSnapshot(
-                code, code, "类型名", true, false, false, false, Collections.emptyList(), null);
+                code, code, "类型名", longitude, latitude, false,
+                online, false, false, false, Collections.emptyList(), null);
     }
     }
 
 
     private static MonitorPoint point(String id, String code, String name, BigDecimal lon, BigDecimal lat) {
     private static MonitorPoint point(String id, String code, String name, BigDecimal lon, BigDecimal lat) {

+ 14 - 1
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/BusinessCockpitMonitoringPointDetailAdapterTest.java

@@ -47,13 +47,15 @@ class BusinessCockpitMonitoringPointDetailAdapterTest {
     void loadsPointAndResolvesBoundDeviceTypeAndRuntimeFacts() {
     void loadsPointAndResolvesBoundDeviceTypeAndRuntimeFacts() {
         MonitorPoint point = point("P-1", "MP-001", "供水干管监测点", "110.4024", "28.4499");
         MonitorPoint point = point("P-1", "MP-001", "供水干管监测点", "110.4024", "28.4499");
         EquipmentBase device = device("D-1", "DEV-001", "压力计-001", 11);
         EquipmentBase device = device("D-1", "DEV-001", "压力计-001", 11);
+        device.setLongitude(new BigDecimal("110.4025"));
+        device.setLatitude(new BigDecimal("28.4500"));
         EquipmentType type = type(11, "超声波流量计");
         EquipmentType type = type(11, "超声波流量计");
         when(monitorPointService.list()).thenReturn(Collections.singletonList(point));
         when(monitorPointService.list()).thenReturn(Collections.singletonList(point));
         when(equipmentTypeService.list()).thenReturn(Collections.singletonList(type));
         when(equipmentTypeService.list()).thenReturn(Collections.singletonList(type));
         when(equipmentBaseService.getEquipmentByPoint("P-1"))
         when(equipmentBaseService.getEquipmentByPoint("P-1"))
                 .thenReturn(Collections.singletonList(device));
                 .thenReturn(Collections.singletonList(device));
         when(deviceFactResolver.resolve(device, "超声波流量计"))
         when(deviceFactResolver.resolve(device, "超声波流量计"))
-                .thenReturn(fact("DEV-001"));
+                .thenReturn(fact("DEV-001", new BigDecimal("110.4025"), new BigDecimal("28.4500")));
 
 
         CockpitMonitoringPointDetail detail = adapter.load(" MP-001 ");
         CockpitMonitoringPointDetail detail = adapter.load(" MP-001 ");
 
 
@@ -62,6 +64,11 @@ class BusinessCockpitMonitoringPointDetailAdapterTest {
         assertEquals(new BigDecimal("110.4024"), detail.getLongitude());
         assertEquals(new BigDecimal("110.4024"), detail.getLongitude());
         assertEquals(new BigDecimal("28.4499"), detail.getLatitude());
         assertEquals(new BigDecimal("28.4499"), detail.getLatitude());
         assertFalse(detail.isCoordinateVerified(), "现网没有可信坐标校核字段");
         assertFalse(detail.isCoordinateVerified(), "现网没有可信坐标校核字段");
+        assertEquals(new BigDecimal("110.4025"), detail.getDevices().get(0).getLongitude());
+        assertEquals(new BigDecimal("28.4500"), detail.getDevices().get(0).getLatitude());
+        assertEquals("UNVERIFIED", detail.getDevices().get(0).getCoordinateValidation().getState());
+        assertEquals("UNVERIFIED_COORDINATE", detail.getDevices().get(0).getCoordinateValidation().getReason());
+        assertFalse(detail.getDevices().get(0).getCoordinateValidation().isRenderable());
         assertEquals(1, detail.getDevices().size());
         assertEquals(1, detail.getDevices().size());
         assertEquals("DEV-001", detail.getDevices().get(0).getDeviceCode());
         assertEquals("DEV-001", detail.getDevices().get(0).getDeviceCode());
         assertEquals("压力计", detail.getDevices().get(0).getDeviceTypeName());
         assertEquals("压力计", detail.getDevices().get(0).getDeviceTypeName());
@@ -103,6 +110,12 @@ class BusinessCockpitMonitoringPointDetailAdapterTest {
                 Collections.emptyList(), null);
                 Collections.emptyList(), null);
     }
     }
 
 
+    private static CockpitMonitoringPointDeviceSnapshot fact(String code, BigDecimal longitude, BigDecimal latitude) {
+        return new CockpitMonitoringPointDeviceSnapshot(
+                code, code, "压力计", longitude, latitude, false,
+                true, false, false, false, Collections.emptyList(), null);
+    }
+
     private static MonitorPoint point(String id, String code, String name, String longitude, String latitude) {
     private static MonitorPoint point(String id, String code, String name, String longitude, String latitude) {
         MonitorPoint point = new MonitorPoint();
         MonitorPoint point = new MonitorPoint();
         point.setPointId(id);
         point.setPointId(id);

+ 9 - 1
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringPointDetailControllerTest.java

@@ -79,7 +79,12 @@ class CockpitMonitoringPointDetailControllerTest {
                 .andExpect(jsonPath("$.data.deviceStatistics.offlineDeviceCount").value(1))
                 .andExpect(jsonPath("$.data.deviceStatistics.offlineDeviceCount").value(1))
                 .andExpect(jsonPath("$.data.devices", hasSize(5)))
                 .andExpect(jsonPath("$.data.devices", hasSize(5)))
                 .andExpect(jsonPath("$.data.devices[0].deviceCode").value("EQ-001"))
                 .andExpect(jsonPath("$.data.devices[0].deviceCode").value("EQ-001"))
-                .andExpect(jsonPath("$.data.devices[0].deviceStatus").value("ALARM"))
+                .andExpect(jsonPath("$.data.devices[0].longitude").value(110.142))
+                 .andExpect(jsonPath("$.data.devices[0].latitude").value(28.453))
+                 .andExpect(jsonPath("$.data.devices[0].coordinateValidation.state").value("UNVERIFIED"))
+                 .andExpect(jsonPath("$.data.devices[0].coordinateValidation.reason").value("UNVERIFIED_COORDINATE"))
+                 .andExpect(jsonPath("$.data.devices[0].coordinateValidation.renderable").value(false))
+                 .andExpect(jsonPath("$.data.devices[0].deviceStatus").value("ALARM"))
                 .andExpect(jsonPath("$.data.devices[0].statusLabel").value("报警"))
                 .andExpect(jsonPath("$.data.devices[0].statusLabel").value("报警"))
                 .andExpect(jsonPath("$.data.devices[1].deviceCode").value("EQ-003"))
                 .andExpect(jsonPath("$.data.devices[1].deviceCode").value("EQ-003"))
                 .andExpect(jsonPath("$.data.devices[2].deviceCode").value("EQ-005"))
                 .andExpect(jsonPath("$.data.devices[2].deviceCode").value("EQ-005"))
@@ -251,6 +256,9 @@ class CockpitMonitoringPointDetailControllerTest {
                 deviceCode,
                 deviceCode,
                 deviceCode + "设备",
                 deviceCode + "设备",
                 "压力计",
                 "压力计",
+                new BigDecimal("110.142"),
+                new BigDecimal("28.453"),
+                false,
                 online,
                 online,
                 alarm,
                 alarm,
                 warning,
                 warning,

+ 7 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/monitoring/CockpitMonitoringDistributionService.java

@@ -344,7 +344,10 @@ public class CockpitMonitoringDistributionService {
                         device.getDeviceCode().trim(),
                         device.getDeviceCode().trim(),
                         device.getDeviceName(),
                         device.getDeviceName(),
                         type.getName(),
                         type.getName(),
-                        device.isOnline(),
+                        device.getLongitude(),
+                         device.getLatitude(),
+                         validation,
+                         device.isOnline(),
                         device.isAlarm(),
                         device.isAlarm(),
                         device.isWarning(),
                         device.isWarning(),
                         device.isWorkOrderInProgress(),
                         device.isWorkOrderInProgress(),
@@ -474,6 +477,9 @@ public class CockpitMonitoringDistributionService {
                 source.getDeviceCode(),
                 source.getDeviceCode(),
                 source.getDeviceName(),
                 source.getDeviceName(),
                 source.getDeviceTypeName(),
                 source.getDeviceTypeName(),
+                 source.getLongitude(),
+                 source.getLatitude(),
+                 source.getCoordinateValidation(),
                 online,
                 online,
                 source.isAlarm(),
                 source.isAlarm(),
                 source.isWarning(),
                 source.isWarning(),

+ 3 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/point/BusinessCockpitDeviceFactResolver.java

@@ -75,6 +75,9 @@ public class BusinessCockpitDeviceFactResolver {
                 deviceCode,
                 deviceCode,
                 device.getEquipmentName(),
                 device.getEquipmentName(),
                 deviceTypeName,
                 deviceTypeName,
+                 device.getLongitude(),
+                 device.getLatitude(),
+                 false,
                 online,
                 online,
                 alarm,
                 alarm,
                 hasOpenWarning(deviceCode),
                 hasOpenWarning(deviceCode),

+ 22 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/point/CockpitMonitoringPointDeviceCard.java

@@ -1,11 +1,18 @@
 package com.zksy.cockpit.point;
 package com.zksy.cockpit.point;
 
 
+import com.zksy.cockpit.foundation.CockpitCoordinateValidation;
+import com.zksy.cockpit.foundation.CockpitCoordinateValidator;
+
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.List;
 
 
 public class CockpitMonitoringPointDeviceCard {
 public class CockpitMonitoringPointDeviceCard {
     private final String deviceCode;
     private final String deviceCode;
     private final String deviceName;
     private final String deviceName;
     private final String deviceTypeName;
     private final String deviceTypeName;
+    private final BigDecimal longitude;
+    private final BigDecimal latitude;
+    private final CockpitCoordinateValidation coordinateValidation;
     private final boolean online;
     private final boolean online;
     private final boolean alarm;
     private final boolean alarm;
     private final boolean warning;
     private final boolean warning;
@@ -18,9 +25,21 @@ public class CockpitMonitoringPointDeviceCard {
     public CockpitMonitoringPointDeviceCard(String deviceCode, String deviceName, String deviceTypeName,
     public CockpitMonitoringPointDeviceCard(String deviceCode, String deviceName, String deviceTypeName,
             boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
             boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
             List<String> statusReasons, String statusTime, CockpitMonitoringPointStatus deviceStatus) {
             List<String> statusReasons, String statusTime, CockpitMonitoringPointStatus deviceStatus) {
+        this(deviceCode, deviceName, deviceTypeName, null, null,
+                CockpitCoordinateValidator.validate(null, null, false),
+                online, alarm, warning, workOrderInProgress, statusReasons, statusTime, deviceStatus);
+    }
+
+    public CockpitMonitoringPointDeviceCard(String deviceCode, String deviceName, String deviceTypeName,
+            BigDecimal longitude, BigDecimal latitude, CockpitCoordinateValidation coordinateValidation,
+            boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
+            List<String> statusReasons, String statusTime, CockpitMonitoringPointStatus deviceStatus) {
         this.deviceCode = deviceCode;
         this.deviceCode = deviceCode;
         this.deviceName = deviceName;
         this.deviceName = deviceName;
         this.deviceTypeName = deviceTypeName;
         this.deviceTypeName = deviceTypeName;
+        this.longitude = longitude;
+        this.latitude = latitude;
+        this.coordinateValidation = coordinateValidation;
         this.online = online;
         this.online = online;
         this.alarm = alarm;
         this.alarm = alarm;
         this.warning = warning;
         this.warning = warning;
@@ -34,6 +53,9 @@ public class CockpitMonitoringPointDeviceCard {
     public String getDeviceCode() { return deviceCode; }
     public String getDeviceCode() { return deviceCode; }
     public String getDeviceName() { return deviceName; }
     public String getDeviceName() { return deviceName; }
     public String getDeviceTypeName() { return deviceTypeName; }
     public String getDeviceTypeName() { return deviceTypeName; }
+    public BigDecimal getLongitude() { return longitude; }
+    public BigDecimal getLatitude() { return latitude; }
+    public CockpitCoordinateValidation getCoordinateValidation() { return coordinateValidation; }
     public boolean isOnline() { return online; }
     public boolean isOnline() { return online; }
     public boolean isAlarm() { return alarm; }
     public boolean isAlarm() { return alarm; }
     public boolean isWarning() { return warning; }
     public boolean isWarning() { return warning; }

+ 30 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/point/CockpitMonitoringPointDeviceSnapshot.java

@@ -1,11 +1,18 @@
 package com.zksy.cockpit.point;
 package com.zksy.cockpit.point;
 
 
+import com.zksy.cockpit.foundation.CockpitCoordinateValidation;
+import com.zksy.cockpit.foundation.CockpitCoordinateValidator;
+
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.List;
 
 
 public class CockpitMonitoringPointDeviceSnapshot {
 public class CockpitMonitoringPointDeviceSnapshot {
     private final String deviceCode;
     private final String deviceCode;
     private final String deviceName;
     private final String deviceName;
     private final String deviceTypeName;
     private final String deviceTypeName;
+    private final BigDecimal longitude;
+    private final BigDecimal latitude;
+    private final CockpitCoordinateValidation coordinateValidation;
     private final boolean online;
     private final boolean online;
     private final boolean alarm;
     private final boolean alarm;
     private final boolean warning;
     private final boolean warning;
@@ -16,9 +23,29 @@ public class CockpitMonitoringPointDeviceSnapshot {
     public CockpitMonitoringPointDeviceSnapshot(String deviceCode, String deviceName, String deviceTypeName,
     public CockpitMonitoringPointDeviceSnapshot(String deviceCode, String deviceName, String deviceTypeName,
             boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
             boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
             List<String> statusReasons, String statusTime) {
             List<String> statusReasons, String statusTime) {
+        this(deviceCode, deviceName, deviceTypeName, null, null, false,
+                online, alarm, warning, workOrderInProgress, statusReasons, statusTime);
+    }
+
+    public CockpitMonitoringPointDeviceSnapshot(String deviceCode, String deviceName, String deviceTypeName,
+            BigDecimal longitude, BigDecimal latitude, boolean coordinateVerified,
+            boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
+            List<String> statusReasons, String statusTime) {
+        this(deviceCode, deviceName, deviceTypeName, longitude, latitude,
+                CockpitCoordinateValidator.validate(longitude, latitude, coordinateVerified),
+                online, alarm, warning, workOrderInProgress, statusReasons, statusTime);
+    }
+
+    public CockpitMonitoringPointDeviceSnapshot(String deviceCode, String deviceName, String deviceTypeName,
+            BigDecimal longitude, BigDecimal latitude, CockpitCoordinateValidation coordinateValidation,
+            boolean online, boolean alarm, boolean warning, boolean workOrderInProgress,
+            List<String> statusReasons, String statusTime) {
         this.deviceCode = deviceCode;
         this.deviceCode = deviceCode;
         this.deviceName = deviceName;
         this.deviceName = deviceName;
         this.deviceTypeName = deviceTypeName;
         this.deviceTypeName = deviceTypeName;
+        this.longitude = longitude;
+        this.latitude = latitude;
+        this.coordinateValidation = coordinateValidation;
         this.online = online;
         this.online = online;
         this.alarm = alarm;
         this.alarm = alarm;
         this.warning = warning;
         this.warning = warning;
@@ -30,6 +57,9 @@ public class CockpitMonitoringPointDeviceSnapshot {
     public String getDeviceCode() { return deviceCode; }
     public String getDeviceCode() { return deviceCode; }
     public String getDeviceName() { return deviceName; }
     public String getDeviceName() { return deviceName; }
     public String getDeviceTypeName() { return deviceTypeName; }
     public String getDeviceTypeName() { return deviceTypeName; }
+    public BigDecimal getLongitude() { return longitude; }
+    public BigDecimal getLatitude() { return latitude; }
+    public CockpitCoordinateValidation getCoordinateValidation() { return coordinateValidation; }
     public boolean isOnline() { return online; }
     public boolean isOnline() { return online; }
     public boolean isAlarm() { return alarm; }
     public boolean isAlarm() { return alarm; }
     public boolean isWarning() { return warning; }
     public boolean isWarning() { return warning; }

+ 4 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/point/CockpitMonitoringPointStatusResolver.java

@@ -37,7 +37,10 @@ public final class CockpitMonitoringPointStatusResolver {
                         device.getDeviceCode(),
                         device.getDeviceCode(),
                         device.getDeviceName(),
                         device.getDeviceName(),
                         device.getDeviceTypeName(),
                         device.getDeviceTypeName(),
-                        device.isOnline(),
+                        device.getLongitude(),
+                         device.getLatitude(),
+                         device.getCoordinateValidation(),
+                         device.isOnline(),
                         device.isAlarm(),
                         device.isAlarm(),
                         device.isWarning(),
                         device.isWarning(),
                         device.isWorkOrderInProgress(),
                         device.isWorkOrderInProgress(),