|
|
@@ -1,12 +1,15 @@
|
|
|
package com.zksy.cockpit.infrastructure;
|
|
|
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.zksy.WaterSupply.WaterPlantInfo.domain.WaterPlantInfo;
|
|
|
import com.zksy.WaterSupply.WaterPlantInfo.service.IWaterPlantInfoService;
|
|
|
import com.zksy.WaterSupply.WaterPumpStation.domain.PumpStationType;
|
|
|
import com.zksy.WaterSupply.WaterPumpStation.domain.WaterPumpStation;
|
|
|
import com.zksy.WaterSupply.WaterPumpStation.service.IWaterPumpStationService;
|
|
|
+import com.zksy.WaterSupply.WaterSourceInfo.domain.WaterSourceInfo;
|
|
|
+import com.zksy.WaterSupply.WaterSourceInfo.service.IWaterSourceInfoService;
|
|
|
+import com.zksy.base.domain.ManholeBase;
|
|
|
import com.zksy.base.domain.PipeNetworkBase;
|
|
|
+import com.zksy.base.service.ManholeBaseService;
|
|
|
import com.zksy.base.service.PipeNetworkBaseService;
|
|
|
import com.zksy.cockpit.foundation.CockpitPipeNetworkType;
|
|
|
import com.zksy.drainage.domain.EngineeringFacility;
|
|
|
@@ -15,8 +18,8 @@ import org.springframework.context.annotation.Primary;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
-import java.time.Clock;
|
|
|
import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.time.ZoneId;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.ArrayList;
|
|
|
@@ -27,180 +30,139 @@ import java.util.Objects;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
- * 基础档案真实数据适配器(BT-5 第 6 片)。
|
|
|
- *
|
|
|
- * <p>组合现有设施表,不新增投影:水厂、共用泵站、统一管网基础表和排水工程设施。
|
|
|
- * 只有实体明确提供的字段才映射;缺少统一来源的规模、片区或长度保持 null,避免把
|
|
|
- * 容量、坐标、自由文本位置或展示文本误当作基础档案事实。
|
|
|
- *
|
|
|
- * <p>**片区(district)一律传 null**:水厂/泵站/管网/排水设施都没有
|
|
|
- * {@code equipment_base.district} 那种权威片区列,也不关联设备,故如实归入「未分区」,
|
|
|
- * 不新增数据库列、不用行业或管理单位冒充(决策票 06 / 执行票 12)。
|
|
|
+ * 三个驾驶舱设施统计端点的权威设施目录(执行票 31)。
|
|
|
+ * 包含管网、水厂、供排水泵站、排水工程设施、水源地与窨井的合法记录并集。
|
|
|
+ * 设施无权威片区来源,一律保持 null;未知类型或缺编码行以哨兵保留到 excluded。
|
|
|
*/
|
|
|
@Component
|
|
|
@Primary
|
|
|
public class BusinessCockpitInfrastructureBaseArchivesAdapter
|
|
|
- implements CockpitInfrastructureBaseArchivesAdapter {
|
|
|
-
|
|
|
+ implements CockpitInfrastructureBaseArchivesAdapter, CockpitFacilityCatalog {
|
|
|
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
|
|
|
- private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
|
|
+ private static final String UNCLASSIFIED = "FACILITY_UNCLASSIFIED";
|
|
|
private static final List<CockpitInfrastructureBaseArchivesSnapshot.SourceType> TYPES = Arrays.asList(
|
|
|
type("WATER_PLANT", "水厂", "WATER_SUPPLY"),
|
|
|
type("WATER_PUMP_STATION", "供水泵站", "WATER_SUPPLY"),
|
|
|
- type(CockpitPipeNetworkType.WATER_PIPE,
|
|
|
- CockpitPipeNetworkType.nameOf(CockpitPipeNetworkType.WATER_PIPE),
|
|
|
- CockpitPipeNetworkType.industryOfTypeCode(CockpitPipeNetworkType.WATER_PIPE)),
|
|
|
- type(CockpitPipeNetworkType.GAS_PIPE,
|
|
|
- CockpitPipeNetworkType.nameOf(CockpitPipeNetworkType.GAS_PIPE),
|
|
|
- CockpitPipeNetworkType.industryOfTypeCode(CockpitPipeNetworkType.GAS_PIPE)),
|
|
|
- type(CockpitPipeNetworkType.DRAINAGE_PIPE,
|
|
|
- CockpitPipeNetworkType.nameOf(CockpitPipeNetworkType.DRAINAGE_PIPE),
|
|
|
- CockpitPipeNetworkType.industryOfTypeCode(CockpitPipeNetworkType.DRAINAGE_PIPE)),
|
|
|
+ type(CockpitPipeNetworkType.WATER_PIPE, CockpitPipeNetworkType.nameOf(CockpitPipeNetworkType.WATER_PIPE), "WATER_SUPPLY"),
|
|
|
+ type(CockpitPipeNetworkType.GAS_PIPE, CockpitPipeNetworkType.nameOf(CockpitPipeNetworkType.GAS_PIPE), "GAS"),
|
|
|
+ type(CockpitPipeNetworkType.DRAINAGE_PIPE, CockpitPipeNetworkType.nameOf(CockpitPipeNetworkType.DRAINAGE_PIPE), "DRAINAGE"),
|
|
|
type("DRAINAGE_GATE", "排水闸门", "DRAINAGE"),
|
|
|
type("DRAINAGE_STORAGE", "排水调蓄设施", "DRAINAGE"),
|
|
|
- type("DRAINAGE_PUMP_STATION", "排水泵站", "DRAINAGE"));
|
|
|
+ type("DRAINAGE_PUMP_STATION", "排水泵站", "DRAINAGE"),
|
|
|
+ type("WATER_SOURCE", "水源地", "WATER_SUPPLY"),
|
|
|
+ type("MANHOLE", "窨井", "MANHOLE"));
|
|
|
|
|
|
- private final IWaterPlantInfoService plantService;
|
|
|
- private final IWaterPumpStationService pumpStationService;
|
|
|
- private final PipeNetworkBaseService pipeNetworkBaseService;
|
|
|
- private final EngineeringFacilityService engineeringFacilityService;
|
|
|
- private final Clock clock;
|
|
|
+ private final IWaterPlantInfoService plants;
|
|
|
+ private final IWaterPumpStationService stations;
|
|
|
+ private final PipeNetworkBaseService pipes;
|
|
|
+ private final EngineeringFacilityService engineering;
|
|
|
+ private final IWaterSourceInfoService sources;
|
|
|
+ private final ManholeBaseService manholes;
|
|
|
|
|
|
public BusinessCockpitInfrastructureBaseArchivesAdapter(
|
|
|
- IWaterPlantInfoService plantService,
|
|
|
- IWaterPumpStationService pumpStationService,
|
|
|
- PipeNetworkBaseService pipeNetworkBaseService,
|
|
|
- EngineeringFacilityService engineeringFacilityService,
|
|
|
- Clock clock) {
|
|
|
- this.plantService = plantService;
|
|
|
- this.pumpStationService = pumpStationService;
|
|
|
- this.pipeNetworkBaseService = pipeNetworkBaseService;
|
|
|
- this.engineeringFacilityService = engineeringFacilityService;
|
|
|
- this.clock = clock;
|
|
|
+ IWaterPlantInfoService plants, IWaterPumpStationService stations,
|
|
|
+ PipeNetworkBaseService pipes, EngineeringFacilityService engineering,
|
|
|
+ IWaterSourceInfoService sources, ManholeBaseService manholes) {
|
|
|
+ this.plants = plants;
|
|
|
+ this.stations = stations;
|
|
|
+ this.pipes = pipes;
|
|
|
+ this.engineering = engineering;
|
|
|
+ this.sources = sources;
|
|
|
+ this.manholes = manholes;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public CockpitInfrastructureBaseArchivesSnapshot load() {
|
|
|
List<CockpitInfrastructureBaseArchivesSnapshot.SourceFacility> facilities = new ArrayList<>();
|
|
|
- addPlants(facilities);
|
|
|
- addPumpStations(facilities);
|
|
|
- addPipes(facilities);
|
|
|
- addDrainageFacilities(facilities);
|
|
|
- return new CockpitInfrastructureBaseArchivesSnapshot(
|
|
|
- TIME_FORMATTER.format(clock.instant().atZone(BUSINESS_ZONE)), TYPES, facilities);
|
|
|
- }
|
|
|
-
|
|
|
- private void addPlants(List<CockpitInfrastructureBaseArchivesSnapshot.SourceFacility> facilities) {
|
|
|
- for (WaterPlantInfo plant : safeList(plantService.list())) {
|
|
|
- String code = trimToNull(plant.getPlantCode());
|
|
|
- if (code == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- facilities.add(facility(code, "WATER_PLANT", null, null,
|
|
|
- plant.getCommissionDate(), null));
|
|
|
+ LocalDateTime updatedAt = null;
|
|
|
+ for (WaterPlantInfo plant : safeList(plants.list())) {
|
|
|
+ updatedAt = latest(updatedAt, plant.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, plant.getCreateTime());
|
|
|
+ add(facilities, key("water_plant_info", plant.getId(), plant.getPlantCode()), plant.getPlantCode(), "WATER_PLANT", null, plant.getCommissionDate(), null);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- private void addPumpStations(List<CockpitInfrastructureBaseArchivesSnapshot.SourceFacility> facilities) {
|
|
|
- for (WaterPumpStation station : safeList(pumpStationService.list(
|
|
|
- new LambdaQueryWrapper<WaterPumpStation>()
|
|
|
- .in(WaterPumpStation::getStationType,
|
|
|
- PumpStationType.WATER_SUPPLY, PumpStationType.DRAINAGE)))) {
|
|
|
- String code = trimToNull(station.getStationCode());
|
|
|
- if (code == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ // 驾驶舱跨行业统计读取共用表全集,按 station_type 分类;未知归属显式 excluded。
|
|
|
+ // 不用于供水/排水管理入口(其强制归属过滤规则仍保持 ADR-0007)。
|
|
|
+ for (WaterPumpStation station : safeList(stations.list())) {
|
|
|
+ updatedAt = latest(updatedAt, station.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, station.getCreateTime());
|
|
|
String stationType = trimToNull(station.getStationType());
|
|
|
- String typeCode = PumpStationType.DRAINAGE.equalsIgnoreCase(stationType)
|
|
|
- ? "DRAINAGE_PUMP_STATION"
|
|
|
- : PumpStationType.WATER_SUPPLY.equalsIgnoreCase(stationType)
|
|
|
- ? "WATER_PUMP_STATION" : null;
|
|
|
- if (typeCode == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- facilities.add(facility(code, typeCode, null, null,
|
|
|
- station.getCommissionDate(), null));
|
|
|
+ String type = PumpStationType.DRAINAGE.equalsIgnoreCase(stationType) ? "DRAINAGE_PUMP_STATION"
|
|
|
+ : PumpStationType.WATER_SUPPLY.equalsIgnoreCase(stationType) ? "WATER_PUMP_STATION" : UNCLASSIFIED;
|
|
|
+ add(facilities, key("pump_station", station.getId(), station.getStationCode()), station.getStationCode(), type, null, station.getCommissionDate(), null);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- private void addPipes(List<CockpitInfrastructureBaseArchivesSnapshot.SourceFacility> facilities) {
|
|
|
- for (PipeNetworkBase pipe : safeList(pipeNetworkBaseService.list())) {
|
|
|
- String code = trimToNull(pipe.getNetworkCode());
|
|
|
- if (code == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 权威映射在 CockpitPipeNetworkType(0-燃气/1-排水/2-供水)。无法映射的取值
|
|
|
- // 归入未分类哨兵码:设施仍留在响应里,并由服务层计入 excluded.unknownTypeCount,
|
|
|
- // 不再被静默 continue 掉(执行票 20:4272 条管网曾因漏认 0/1 而整体丢失)。
|
|
|
- String typeCode = CockpitPipeNetworkType.typeCodeOf(pipe.getNetworkType());
|
|
|
- BigDecimal lengthKm = pipe.getLength() == null
|
|
|
- ? null : pipe.getLength().divide(BigDecimal.valueOf(1000));
|
|
|
- LocalDate builtAt = pipe.getBuildDate() == null
|
|
|
- ? null : new java.sql.Date(pipe.getBuildDate().getTime()).toLocalDate();
|
|
|
- facilities.add(facility(code, typeCode, null, lengthKm, null, builtAt));
|
|
|
+ for (PipeNetworkBase pipe : safeList(pipes.list())) {
|
|
|
+ updatedAt = latest(updatedAt, pipe.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, pipe.getCreateTime());
|
|
|
+ BigDecimal lengthKm = pipe.getLength() == null ? null : pipe.getLength().divide(BigDecimal.valueOf(1000));
|
|
|
+ LocalDate builtAt = pipe.getBuildDate() == null ? null
|
|
|
+ : new java.sql.Date(pipe.getBuildDate().getTime()).toLocalDate();
|
|
|
+ add(facilities, key("pipe_network_base", pipe.getNetworkId(), pipe.getNetworkCode()), pipe.getNetworkCode(), CockpitPipeNetworkType.typeCodeOf(pipe.getNetworkType()),
|
|
|
+ lengthKm, null, builtAt);
|
|
|
+ }
|
|
|
+ for (EngineeringFacility facility : safeList(engineering.list())) {
|
|
|
+ updatedAt = latest(updatedAt, facility.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, facility.getCreateTime());
|
|
|
+ // 保留现有工程设施年限映射;本票只统一设施范围,不裁定建设日期来源。
|
|
|
+ add(facilities, key("psgw_engineering_facility", facility.getId(), facility.getId()),
|
|
|
+ facility.getId(), drainageType(facility.getFacilityType()), null, null,
|
|
|
+ facility.getCreateTime() == null ? null : facility.getCreateTime().toLocalDate());
|
|
|
}
|
|
|
+ for (WaterSourceInfo source : safeList(sources.list())) {
|
|
|
+ updatedAt = latest(updatedAt, source.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, source.getCreateTime());
|
|
|
+ add(facilities, key("water_source_info", source.getId(), source.getSourceId()), source.getSourceId(), "WATER_SOURCE", null, null, null);
|
|
|
+ }
|
|
|
+ for (ManholeBase manhole : safeList(manholes.list())) {
|
|
|
+ updatedAt = latest(updatedAt, manhole.getUpdateTime());
|
|
|
+ updatedAt = latest(updatedAt, manhole.getCreateTime());
|
|
|
+ add(facilities, key("manhole_base", manhole.getManholeId(), manhole.getManholeCode()), manhole.getManholeCode(), "MANHOLE", null, null, null);
|
|
|
+ }
|
|
|
+ return new CockpitInfrastructureBaseArchivesSnapshot(
|
|
|
+ updatedAt == null ? null : DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(updatedAt.atZone(BUSINESS_ZONE)),
|
|
|
+ TYPES, facilities);
|
|
|
}
|
|
|
|
|
|
- private void addDrainageFacilities(
|
|
|
- List<CockpitInfrastructureBaseArchivesSnapshot.SourceFacility> facilities) {
|
|
|
- for (EngineeringFacility source : safeList(engineeringFacilityService.list())) {
|
|
|
- String code = trimToNull(source.getId());
|
|
|
- String typeCode = drainageFacilityType(source.getFacilityType());
|
|
|
- if (code == null || typeCode == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 片区一律 null:排水工程设施只有自由文本 area,不是 equipment_base.district,
|
|
|
- // 用它冒充片区会凭空造出片区归属(决策票 06),故如实交未分区。
|
|
|
- facilities.add(facility(code, typeCode, null, null, null,
|
|
|
- source.getCreateTime() == null ? null : source.getCreateTime().toLocalDate()));
|
|
|
+ private static void add(List<CockpitInfrastructureBaseArchivesSnapshot.SourceFacility> facilities,
|
|
|
+ String sourceKey, String code, String type, BigDecimal lengthKm, LocalDate commissionedAt, LocalDate builtAt) {
|
|
|
+ String resolvedCode = trimToNull(code);
|
|
|
+ if (resolvedCode == null) {
|
|
|
+ // 缺少标识不能进入正式设施集合,但必须让三个服务都能复算排除数。
|
|
|
+ resolvedCode = "MISSING_CODE:" + facilities.size();
|
|
|
+ type = UNCLASSIFIED;
|
|
|
}
|
|
|
+ facilities.add(new CockpitInfrastructureBaseArchivesSnapshot.SourceFacility(
|
|
|
+ sourceKey == null ? resolvedCode : sourceKey, resolvedCode, type, null, lengthKm, null, commissionedAt, builtAt));
|
|
|
}
|
|
|
|
|
|
- private static String drainageFacilityType(String facilityType) {
|
|
|
- String normalized = trimToNull(facilityType);
|
|
|
- if (normalized == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- if (normalized.contains("闸")) {
|
|
|
- return "DRAINAGE_GATE";
|
|
|
- }
|
|
|
- if (normalized.contains("调蓄") || normalized.contains("蓄水")) {
|
|
|
- return "DRAINAGE_STORAGE";
|
|
|
- }
|
|
|
- if (normalized.contains("泵站")) {
|
|
|
- return "DRAINAGE_PUMP_STATION";
|
|
|
- }
|
|
|
- return null;
|
|
|
+ private static String key(String table, Object id, String code) {
|
|
|
+ String identity = id == null ? trimToNull(code) : id.toString();
|
|
|
+ return identity == null ? null : table + "|" + identity;
|
|
|
}
|
|
|
|
|
|
- private static CockpitInfrastructureBaseArchivesSnapshot.SourceFacility facility(
|
|
|
- String code,
|
|
|
- String typeCode,
|
|
|
- Integer district,
|
|
|
- BigDecimal lengthKm,
|
|
|
- LocalDate commissionedAt,
|
|
|
- LocalDate builtAt) {
|
|
|
- return new CockpitInfrastructureBaseArchivesSnapshot.SourceFacility(
|
|
|
- code, typeCode, district, lengthKm, null, commissionedAt, builtAt);
|
|
|
+ private static String drainageType(String value) {
|
|
|
+ String type = trimToNull(value);
|
|
|
+ if (type == null) return UNCLASSIFIED;
|
|
|
+ if (type.contains("闸")) return "DRAINAGE_GATE";
|
|
|
+ if (type.contains("调蓄") || type.contains("蓄水")) return "DRAINAGE_STORAGE";
|
|
|
+ if (type.contains("泵站")) return "DRAINAGE_PUMP_STATION";
|
|
|
+ return UNCLASSIFIED;
|
|
|
}
|
|
|
|
|
|
- private static CockpitInfrastructureBaseArchivesSnapshot.SourceType type(
|
|
|
- String code, String name, String industry) {
|
|
|
- return new CockpitInfrastructureBaseArchivesSnapshot.SourceType(
|
|
|
- code, name, industry, "FACILITY");
|
|
|
+ private static CockpitInfrastructureBaseArchivesSnapshot.SourceType type(String code, String name, String industry) {
|
|
|
+ return new CockpitInfrastructureBaseArchivesSnapshot.SourceType(code, name, industry, "FACILITY");
|
|
|
}
|
|
|
|
|
|
private static <T> List<T> safeList(List<T> value) {
|
|
|
- return value == null
|
|
|
- ? Collections.emptyList()
|
|
|
+ return value == null ? Collections.emptyList()
|
|
|
: value.stream().filter(Objects::nonNull).collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
- private static String trimToNull(String value) {
|
|
|
- return isBlank(value) ? null : value.trim();
|
|
|
+ private static LocalDateTime latest(LocalDateTime current, LocalDateTime candidate) {
|
|
|
+ if (candidate == null) return current;
|
|
|
+ return current == null || candidate.isAfter(current) ? candidate : current;
|
|
|
}
|
|
|
|
|
|
- private static boolean isBlank(String value) {
|
|
|
- return value == null || value.trim().isEmpty();
|
|
|
+ private static String trimToNull(String value) {
|
|
|
+ return value == null || value.trim().isEmpty() ? null : value.trim();
|
|
|
}
|
|
|
}
|