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
-185
View File
@@ -22,11 +22,6 @@ from app.algorithms.simulation.scenarios import (
# scheduling_analysis,
pressure_regulation,
)
from app.algorithms.sensor import (
pressure_sensor_placement_sensitivity,
pressure_sensor_placement_kmeans,
)
from app.services.simulation_ops import (
project_management,
scheduling_simulation,
@@ -108,14 +103,6 @@ class PumpFailureState(BaseModel):
pump_status: dict = Field(..., description="泵状态字典")
class PressureSensorPlacement(BaseModel):
name: str = Field(..., description="管网名称(或数据库名称)")
scheme_name: str = Field(..., description="方案名称")
sensor_number: int = Field(..., description="传感器数量")
min_diameter: int = Field(0, description="最小管径限制")
username: str = Field(..., description="用户名")
def run_simulation_manually_by_date(
network_name: str, start_time: datetime, duration: int
) -> None:
@@ -663,154 +650,6 @@ async def fastapi_pump_failure(data: PumpFailureState = Body(..., description="
return json.dumps("SUCCESS")
@router.post("/pressure-sensor-placement-sensitivity-calculations", summary="压力传感器放置-灵敏度分析(基础)", description="基于灵敏度分析方法,为指定管网项目确定最优的压力传感器放置位置。此为基础版本。")
async def pressure_sensor_placement_sensitivity_endpoint(
name: str = Query(..., description="管网名称(或数据库名称)"),
scheme_name: str = Query(..., description="放置方案名称"),
sensor_number: int = Query(..., description="传感器数量"),
min_diameter: int = Query(..., description="最小管径限制(毫米)"),
username: str = Query(..., description="用户名"),
):
"""
压力传感器放置-灵敏度分析(基础版本)
- **name**: 管网名称(或数据库名称)
- **scheme_name**: 放置方案名称
- **sensor_number**: 传感器数量
- **min_diameter**: 最小管径限制(毫米)
- **username**: 用户名
基于灵敏度分析方法确定传感器放置位置。
"""
return pressure_sensor_placement_sensitivity(
name, scheme_name, sensor_number, min_diameter, username
)
@router.post("/pressure-sensor-placement-sensitivities", summary="压力传感器放置-灵敏度分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于灵敏度分析方法确定最优放置位置。")
async def fastapi_pressure_sensor_placement_sensitivity(
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
) -> None:
"""
压力传感器放置-灵敏度分析(高级版本)
请求体参数:
- **name**: 管网名称(或数据库名称)
- **scheme_name**: 放置方案名称
- **sensor_number**: 传感器数量
- **min_diameter**: 最小管径限制(毫米)
- **username**: 用户名
基于灵敏度分析方法确定压力传感器的最优放置位置。
"""
item = data.dict()
pressure_sensor_placement_sensitivity(
name=item["name"],
scheme_name=item["scheme_name"],
sensor_number=item["sensor_number"],
min_diameter=item["min_diameter"],
username=item["username"],
)
@router.post("/pressure-sensor-placement-kmeans-calculations", summary="压力传感器放置-KMeans聚类分析(基础)", description="基于KMeans聚类算法,为指定管网项目确定压力传感器的最优放置位置。此为基础版本。")
async def pressure_sensor_placement_kmeans_endpoint(
name: str = Query(..., description="管网名称(或数据库名称)"),
scheme_name: str = Query(..., description="放置方案名称"),
sensor_number: int = Query(..., description="传感器数量"),
min_diameter: int = Query(..., description="最小管径限制(毫米)"),
username: str = Query(..., description="用户名"),
):
"""
压力传感器放置-KMeans聚类分析(基础版本)
- **name**: 管网名称(或数据库名称)
- **scheme_name**: 放置方案名称
- **sensor_number**: 传感器数量
- **min_diameter**: 最小管径限制(毫米)
- **username**: 用户名
基于KMeans聚类算法确定传感器放置位置。
"""
return pressure_sensor_placement_kmeans(
name, scheme_name, sensor_number, min_diameter, username
)
@router.post("/pressure-sensor-placement-kmeans", summary="压力传感器放置-KMeans聚类分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于KMeans聚类算法确定最优放置位置。")
async def fastapi_pressure_sensor_placement_kmeans(
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
) -> None:
"""
压力传感器放置-KMeans聚类分析(高级版本)
请求体参数:
- **name**: 管网名称(或数据库名称)
- **scheme_name**: 放置方案名称
- **sensor_number**: 传感器数量
- **min_diameter**: 最小管径限制(毫米)
- **username**: 用户名
基于KMeans聚类算法确定压力传感器的最优放置位置。
"""
item = data.dict()
pressure_sensor_placement_kmeans(
name=item["name"],
scheme_name=item["scheme_name"],
sensor_number=item["sensor_number"],
min_diameter=item["min_diameter"],
username=item["username"],
)
@router.post("/sensor-placement-schemes", summary="传感器放置方案创建", description="创建新的传感器放置方案,支持灵敏度分析和KMeans聚类两种方法。根据指定的方法自动计算最优的传感器放置位置。")
async def fastapi_pressure_sensor_placement(
network: str = Query(..., description="管网名称(或数据库名称)"),
scheme_name: str = Query(..., description="放置方案名称"),
sensor_type: str = Query(..., description="传感器类型"),
method: str = Query(..., description="放置方法('sensitivity''kmeans'"),
sensor_count: int = Query(..., description="传感器数量"),
min_diameter: int = Query(0, description="最小管径限制(毫米),默认0"),
user_name: str = Query(..., description="用户名"),
) -> str:
"""
传感器放置方案创建
- **network**: 管网名称(或数据库名称)
- **scheme_name**: 放置方案名称
- **sensor_type**: 传感器类型
- **method**: 放置方法('sensitivity''kmeans'
- **sensor_count**: 传感器数量
- **min_diameter**: 最小管径限制(毫米,默认0)
- **user_name**: 用户名
支持两种放置方法:
- sensitivity: 基于灵敏度分析
- kmeans: 基于KMeans聚类
"""
if method not in ["sensitivity", "kmeans"]:
raise HTTPException(
status_code=400, detail="Invalid method. Must be 'sensitivity' or 'kmeans'"
)
if method == "sensitivity":
pressure_sensor_placement_sensitivity(
name=network,
scheme_name=scheme_name,
sensor_number=sensor_count,
min_diameter=min_diameter,
username=user_name,
)
elif method == "kmeans":
pressure_sensor_placement_kmeans(
name=network,
scheme_name=scheme_name,
sensor_number=sensor_count,
min_diameter=min_diameter,
username=user_name,
)
return "success"
@router.post("/simulation-runs", summary="手动运行日期指定模拟", description="根据指定的开始时间和持续时间,手动运行水力模拟。开始时间必须是显式带时区的 ISO 8601 / RFC3339 时间。")
async def fastapi_run_simulation_manually_by_date(
data: RunSimulationManuallyByDate = Body(..., description="模拟运行参数"),
@@ -829,30 +668,6 @@ async def fastapi_run_simulation_manually_by_date(
item = data.model_dump()
try:
simulation.query_corresponding_element_id_and_query_id(item["name"])
simulation.query_corresponding_pattern_id_and_query_id(item["name"])
region_result = simulation.query_non_realtime_region(item["name"])
globals.source_outflow_region_id = simulation.get_source_outflow_region_id(
item["name"], region_result
)
globals.realtime_region_pipe_flow_and_demand_id = (
simulation.query_realtime_region_pipe_flow_and_demand_id(
item["name"], region_result
)
)
globals.pipe_flow_region_patterns = simulation.query_pipe_flow_region_patterns(
item["name"]
)
globals.non_realtime_region_patterns = (
simulation.query_non_realtime_region_patterns(item["name"], region_result)
)
(
globals.source_outflow_region_patterns,
globals.realtime_region_pipe_flow_and_demand_patterns,
) = simulation.get_realtime_region_patterns(
item["name"],
globals.source_outflow_region_id,
globals.realtime_region_pipe_flow_and_demand_id,
)
start_time = parse_utc_time(item["start_time"], field_name="start_time")
run_simulation_manually_by_date(
item["name"], start_time, item["duration"]