refactor(db)!: adopt project-routed pooled databases

Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
This commit is contained in:
2026-08-25 18:35:05 +08:00
parent fdbcc5c033
commit fa188af0b1
181 changed files with 8446 additions and 33546 deletions
+21 -25
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
from collections import Counter
from datetime import datetime, timedelta
from typing import Any
from uuid import UUID
import numpy as np
import pandas as pd
@@ -49,8 +50,7 @@ def run_burst_detection(
sensor_nodes: list[str] | None = None,
scheme_name: str | None = None,
data_source: str = "monitoring",
simulation_scheme_name: str | None = None,
simulation_scheme_type: str | None = None,
simulation_run_id: UUID | None = None,
) -> dict[str, Any]:
"""
运行爆管侦测服务入口。
@@ -153,18 +153,17 @@ def run_burst_detection(
else _get_pressure_sensor_nodes(network)
)
if data_source == "simulation":
if not simulation_scheme_name:
raise ValueError("模拟方案模式必须提供 simulation_scheme_name")
if not simulation_run_id:
raise ValueError("模拟数据模式必须提供 simulation_run_id")
observed_df = _build_observed_pressure_from_simulation(
network=network,
sensor_nodes=scada_sensor_nodes,
scada_start=scada_start,
scada_end=scada_end,
simulation_scheme_name=simulation_scheme_name,
simulation_scheme_type=simulation_scheme_type,
simulation_run_id=simulation_run_id,
)
observed_input = observed_df
observed_source = "simulation_scheme_timerange"
observed_source = "analysis_run_timerange"
else:
observed_df = _build_observed_pressure_from_scada(
network=network,
@@ -224,9 +223,8 @@ def run_burst_detection(
}
if data_source == "simulation":
payload["data_source"] = "simulation"
payload["simulation_scheme"] = {
"name": simulation_scheme_name,
"type": simulation_scheme_type,
payload["simulation_run"] = {
"run_id": str(simulation_run_id),
}
else:
payload["data_source"] = "monitoring"
@@ -299,11 +297,10 @@ def _build_observed_pressure_from_simulation(
sensor_nodes: list[str],
scada_start: datetime | str | None,
scada_end: datetime | str | None,
simulation_scheme_name: str | None,
simulation_scheme_type: str | None = None,
simulation_run_id: UUID,
) -> pd.DataFrame:
if scada_start is None or scada_end is None:
raise ValueError("使用模拟方案查询时必须同时提供 scada_start 与 scada_end。")
raise ValueError("使用分析模拟数据时必须同时提供 scada_start 与 scada_end。")
start_dt = _to_datetime(scada_start)
end_dt = _to_datetime(scada_end)
@@ -316,12 +313,9 @@ def _build_observed_pressure_from_simulation(
# Check for missing nodes in simulation result if needed, but InternalQueries handles some of it.
# We assume sensor_nodes are valid pressure nodes.
scheme_type = simulation_scheme_type or "burst_analysis"
simulation_data = InternalQueries.query_scheme_simulation_by_ids_timerange(
simulation_data = InternalQueries.query_analysis_simulation_by_ids_timerange(
db_name=network,
scheme_type=scheme_type,
scheme_name=simulation_scheme_name,
run_id=simulation_run_id,
element_ids=sensor_nodes,
start_time=start_dt.isoformat(),
end_time=end_dt.isoformat(),
@@ -646,8 +640,8 @@ def _resolve_sampling_interval_minutes(
inferred_intervals = [
parsed
for item in get_all_scada_info(network)
if str(item.get("type", "")).lower() == "pressure"
and str(item.get("associated_element_id", "")) in selected_nodes
if str(item.get("device_type", "")).lower() == "pressure"
and str(item.get("node_id", "")) in selected_nodes
and (
parsed := _parse_sampling_interval_minutes(
item.get("transmission_frequency")
@@ -685,9 +679,9 @@ def _parse_sampling_interval_minutes(value: Any) -> int | None:
def _get_pressure_sensor_mapping(network: str) -> dict[str, str]:
node_query_id: dict[str, str] = {}
for item in get_all_scada_info(network):
if str(item.get("type", "")).lower() != "pressure":
if str(item.get("device_type", "")).lower() != "pressure":
continue
node_id = item.get("associated_element_id")
node_id = item.get("node_id")
query_id = item.get("api_query_id")
if node_id and query_id is not None:
node_query_id[str(node_id)] = str(query_id)
@@ -697,14 +691,16 @@ def _get_pressure_sensor_mapping(network: str) -> dict[str, str]:
def _get_pressure_sensor_nodes(network: str) -> list[str]:
sensor_nodes: list[str] = []
for item in get_all_scada_info(network):
if str(item.get("type", "")).lower() != "pressure":
if str(item.get("device_type", "")).lower() != "pressure":
continue
node_id = item.get("associated_element_id")
node_id = item.get("node_id")
if isinstance(node_id, str) and node_id:
sensor_nodes.append(node_id)
sensor_nodes = list(dict.fromkeys(sensor_nodes))
if not sensor_nodes:
raise ValueError("未找到压力传感器对应节点(scada_info.type=pressure)。")
raise ValueError(
"未找到压力传感器对应节点(asset.scada_devices.device_type=pressure)。"
)
return sensor_nodes