|
@@ -0,0 +1,158 @@
|
|
|
|
|
+package com.zksy.web.controller.cockpit;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
+import com.zksy.WaterSupply.WaterPlantInfo.service.IWaterPlantInfoService;
|
|
|
|
|
+import com.zksy.WaterSupply.WaterPumpStation.domain.WaterPumpStation;
|
|
|
|
|
+import com.zksy.WaterSupply.WaterPumpStation.service.IWaterPumpStationService;
|
|
|
|
|
+import com.zksy.WaterSupply.WaterSourceInfo.service.IWaterSourceInfoService;
|
|
|
|
|
+import com.zksy.base.domain.PipeNetworkBase;
|
|
|
|
|
+import com.zksy.base.service.EquipmentBaseService;
|
|
|
|
|
+import com.zksy.base.service.EquipmentTypeService;
|
|
|
|
|
+import com.zksy.base.service.ManholeBaseService;
|
|
|
|
|
+import com.zksy.base.service.PipeNetworkBaseService;
|
|
|
|
|
+import com.zksy.cockpit.device.BusinessCockpitDeviceBoundaryAdapter;
|
|
|
|
|
+import com.zksy.cockpit.device.CockpitDeviceBoundaryService;
|
|
|
|
|
+import com.zksy.cockpit.infrastructure.BusinessCockpitInfrastructureBaseArchivesAdapter;
|
|
|
|
|
+import com.zksy.cockpit.infrastructure.BusinessCockpitInfrastructureManagementInformationAdapter;
|
|
|
|
|
+import com.zksy.cockpit.infrastructure.BusinessCockpitInfrastructureOperatingInformationAdapter;
|
|
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureBaseArchivesService;
|
|
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureManagementInformationService;
|
|
|
|
|
+import com.zksy.cockpit.infrastructure.CockpitInfrastructureOperatingInformationService;
|
|
|
|
|
+import com.zksy.cockpit.overview.BusinessCockpitOverviewInfrastructureAdapter;
|
|
|
|
|
+import com.zksy.cockpit.overview.CockpitOverviewInfrastructureService;
|
|
|
|
|
+import com.zksy.drainage.service.EngineeringFacilityService;
|
|
|
|
|
+import org.apache.ibatis.builder.MapperBuilderAssistant;
|
|
|
|
|
+import org.junit.jupiter.api.BeforeAll;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.time.Clock;
|
|
|
|
|
+import java.time.Instant;
|
|
|
|
|
+import java.time.ZoneOffset;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
|
|
+
|
|
|
|
|
+/** 同一份管网数据贯通五套真实适配器与服务,锁定行业计数及未知项排除口径。 */
|
|
|
|
|
+class CockpitPipeNetworkCrossEndpointContractTest {
|
|
|
|
|
+ private static final Clock CLOCK = Clock.fixed(Instant.parse("2026-10-12T00:00:00Z"), ZoneOffset.UTC);
|
|
|
|
|
+ private static final ObjectMapper JSON = new ObjectMapper();
|
|
|
|
|
+
|
|
|
|
|
+ @BeforeAll
|
|
|
|
|
+ static void initializeLambdaCache() {
|
|
|
|
|
+ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
|
|
|
|
|
+ WaterPumpStation.class);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void numericAndHistoricalTypesHaveEqualIndustryCountsAcrossFiveEndpoints() {
|
|
|
|
|
+ assertCrossEndpointCounts(Arrays.asList("0", "1", "2", "gas", "drainage", "water"), 0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void unknownTypesIncludingRemovedCodeThreeAreCountedRatherThanLost() {
|
|
|
|
|
+ assertCrossEndpointCounts(Arrays.asList("0", "1", "2", "gas", "drainage", "water", "3", "99", null), 3);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void assertCrossEndpointCounts(List<String> types, int unknown) {
|
|
|
|
|
+ PipeNetworkBaseService pipes = mock(PipeNetworkBaseService.class);
|
|
|
|
|
+ List<PipeNetworkBase> source = new ArrayList<>();
|
|
|
|
|
+ for (int i = 0; i < types.size(); i++) {
|
|
|
|
|
+ PipeNetworkBase pipe = new PipeNetworkBase();
|
|
|
|
|
+ pipe.setNetworkCode("PIPE-" + i);
|
|
|
|
|
+ pipe.setNetworkName("管网" + i);
|
|
|
|
|
+ pipe.setNetworkType(types.get(i));
|
|
|
|
|
+ pipe.setLength(BigDecimal.valueOf(1000));
|
|
|
|
|
+ source.add(pipe);
|
|
|
|
|
+ }
|
|
|
|
|
+ when(pipes.list()).thenReturn(source);
|
|
|
|
|
+ IWaterPlantInfoService plants = mock(IWaterPlantInfoService.class);
|
|
|
|
|
+ IWaterPumpStationService stations = mock(IWaterPumpStationService.class);
|
|
|
|
|
+ EngineeringFacilityService engineering = mock(EngineeringFacilityService.class);
|
|
|
|
|
+ EquipmentTypeService equipmentTypes = mock(EquipmentTypeService.class);
|
|
|
|
|
+ EquipmentBaseService equipment = mock(EquipmentBaseService.class);
|
|
|
|
|
+
|
|
|
|
|
+ // 只替换表读取;类型解析、未知计数、行业归属与响应构造全部走真实代码。
|
|
|
|
|
+ JsonNode archives = JSON.valueToTree(new CockpitInfrastructureBaseArchivesService(
|
|
|
|
|
+ new BusinessCockpitInfrastructureBaseArchivesAdapter(plants, stations, pipes, engineering, CLOCK),
|
|
|
|
|
+ CLOCK).load("archives", null, null, null, null));
|
|
|
|
|
+ JsonNode management = JSON.valueToTree(new CockpitInfrastructureManagementInformationService(
|
|
|
|
|
+ new BusinessCockpitInfrastructureManagementInformationAdapter(equipment, equipmentTypes,
|
|
|
|
|
+ mock(ManholeBaseService.class), pipes, mock(IWaterSourceInfoService.class)),
|
|
|
|
|
+ CLOCK).load("management", null, null));
|
|
|
|
|
+ JsonNode operating = JSON.valueToTree(new CockpitInfrastructureOperatingInformationService(
|
|
|
|
|
+ new BusinessCockpitInfrastructureOperatingInformationAdapter(plants, stations, pipes, engineering),
|
|
|
|
|
+ CLOCK, 60).load("operating", null, null, null, null));
|
|
|
|
|
+ JsonNode overview = JSON.valueToTree(new CockpitOverviewInfrastructureService(
|
|
|
|
|
+ new BusinessCockpitOverviewInfrastructureAdapter(equipmentTypes, equipment, pipes, plants, stations),
|
|
|
|
|
+ CLOCK).load("overview"));
|
|
|
|
|
+ JsonNode boundary = JSON.valueToTree(new CockpitDeviceBoundaryService(
|
|
|
|
|
+ new BusinessCockpitDeviceBoundaryAdapter(equipmentTypes, equipment, pipes), CLOCK).load("boundary"));
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Integer> expected = new HashMap<>();
|
|
|
|
|
+ expected.put("GAS", 2);
|
|
|
|
|
+ expected.put("WATER_SUPPLY", 2);
|
|
|
|
|
+ expected.put("DRAINAGE", 2);
|
|
|
|
|
+ expected.put("MANHOLE", 0);
|
|
|
|
|
+ Map<String, Integer> archiveCounts = zeroCounts();
|
|
|
|
|
+ for (JsonNode row : archives.path("industryDistribution")) {
|
|
|
|
|
+ archiveCounts.put(row.path("code").asText(), row.path("count").asInt());
|
|
|
|
|
+ }
|
|
|
|
|
+ assertEquals(expected, archiveCounts, "基础档案不能漏计 0/1 管网");
|
|
|
|
|
+ Map<String, Integer> managementCounts = zeroCounts();
|
|
|
|
|
+ for (JsonNode row : management.path("districtIndustry")) {
|
|
|
|
|
+ managementCounts.merge(row.path("industry").asText(), row.path("facilityCount").asInt(), Integer::sum);
|
|
|
|
|
+ }
|
|
|
|
|
+ assertEquals(expected, managementCounts, "管理信息与基础档案的管网行业计数必须一致");
|
|
|
|
|
+ Map<String, Integer> operatingCounts = zeroCounts();
|
|
|
|
|
+ for (JsonNode row : operating.path("industries")) {
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ for (JsonNode metric : row.path("metrics")) {
|
|
|
|
|
+ if (metric.path("metricCode").asText().endsWith("_pipe_network_length")) {
|
|
|
|
|
+ count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ operatingCounts.put(row.path("industry").path("code").asText(), count);
|
|
|
|
|
+ }
|
|
|
|
|
+ assertEquals(expected, operatingCounts, "运行信息的管网指标条数必须与其它端点一致");
|
|
|
|
|
+ Map<String, Integer> overviewCounts = zeroCounts();
|
|
|
|
|
+ for (JsonNode row : overview.path("industries")) {
|
|
|
|
|
+ overviewCounts.put(row.path("code").asText(), row.path("facilityCount").asInt());
|
|
|
|
|
+ }
|
|
|
|
|
+ assertEquals(expected, overviewCounts, "总体概览不得把管网行业置空");
|
|
|
|
|
+ Map<String, Integer> boundaryCounts = zeroCounts();
|
|
|
|
|
+ for (JsonNode row : boundary.path("facilities")) {
|
|
|
|
|
+ if (row.path("formalScope").asBoolean()) {
|
|
|
|
|
+ boundaryCounts.merge(row.path("industry").asText(), 1, Integer::sum);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertEquals(expected, boundaryCounts, "设备边界设施不能借用设备类型命名空间");
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals(6, archives.path("totals").path("facilityCount").asInt());
|
|
|
|
|
+ assertEquals(6, management.path("totals").path("facilityCount").asInt());
|
|
|
|
|
+ assertEquals(6, overview.path("totals").path("facilityCount").asInt());
|
|
|
|
|
+ assertEquals(types.size(), boundary.path("statistics").path("facilityCount").asInt());
|
|
|
|
|
+ assertEquals(unknown, archives.path("excluded").path("unknownTypeCount").asInt());
|
|
|
|
|
+ assertEquals(unknown, management.path("excluded").path("unknownIndustryCount").asInt());
|
|
|
|
|
+ assertEquals(unknown, operating.path("excluded").path("unknownIndustryCount").asInt());
|
|
|
|
|
+ assertEquals(unknown, overview.path("excluded").path("unknownTypeCount").asInt());
|
|
|
|
|
+ assertEquals(unknown, boundary.path("statistics").path("unknownTypeCount").asInt());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Map<String, Integer> zeroCounts() {
|
|
|
|
|
+ Map<String, Integer> result = new HashMap<>();
|
|
|
|
|
+ for (String industry : Arrays.asList("GAS", "WATER_SUPPLY", "DRAINAGE", "MANHOLE")) {
|
|
|
|
|
+ result.put(industry, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|