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
+43 -30
View File
@@ -1,6 +1,7 @@
from datetime import datetime
from io import BytesIO
from typing import Any
from uuid import UUID
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font, PatternFill
@@ -8,7 +9,7 @@ from openpyxl.worksheet.worksheet import Worksheet
from openpyxl.utils import get_column_letter
from pyproj import Transformer
from app.native import wndb
from app.infra.db.postgresql import sensor_placement as sensor_placement_repository
class SensorPlacementNotFoundError(LookupError):
@@ -61,7 +62,9 @@ def _sensor_points(
network: str,
sensor_location: list[str],
) -> list[dict[str, Any]]:
nodes = wndb.get_sensor_placement_nodes(network, sensor_location)
nodes = sensor_placement_repository.get_sensor_placement_nodes(
network, sensor_location
)
by_id = {str(node["node_id"]): node for node in nodes}
missing = [node_id for node_id in sensor_location if node_id not in by_id]
if missing:
@@ -113,49 +116,59 @@ def validate_sensor_placement_nodes(
_sensor_points(network, _normalize_locations(sensor_location))
def get_sensor_placement_scheme(network: str, scheme_id: int) -> dict[str, Any]:
scheme = wndb.get_sensor_placement(network, scheme_id)
if scheme is None:
raise SensorPlacementNotFoundError("监测点方案不存在")
def get_sensor_placement_run(network: str, run_id: UUID) -> dict[str, Any]:
run = sensor_placement_repository.get_sensor_placement(network, run_id)
if run is None:
raise SensorPlacementNotFoundError("监测点优化运行不存在")
locations = [str(item) for item in (scheme.get("sensor_location") or [])]
locations = [str(item) for item in (run.get("sensor_locations") or [])]
return {
**scheme,
"sensor_number": len(locations),
"sensor_location": locations,
**run,
"sensor_count": len(locations),
"sensor_locations": locations,
"sensor_points": _sensor_points(network, locations),
}
def update_sensor_placement_scheme(
def list_sensor_placement_runs(network: str) -> list[dict[str, Any]]:
return [
{
**run,
"sensor_points": _sensor_points(network, run["sensor_locations"]),
}
for run in sensor_placement_repository.get_all_sensor_placements(network)
]
def update_sensor_placement_run(
network: str,
scheme_id: int,
run_id: UUID,
*,
expected_sensor_location: list[str],
sensor_location: list[str],
expected_sensor_locations: list[str],
sensor_locations: list[str],
) -> dict[str, Any]:
expected = _normalize_locations(expected_sensor_location)
next_locations = _normalize_locations(sensor_location)
expected = _normalize_locations(expected_sensor_locations)
next_locations = _normalize_locations(sensor_locations)
_sensor_points(network, next_locations)
updated = wndb.update_sensor_placement(
updated = sensor_placement_repository.update_sensor_placement(
network,
scheme_id,
expected_sensor_location=expected,
sensor_location=next_locations,
run_id,
expected_sensor_locations=expected,
sensor_locations=next_locations,
)
if updated is None:
if wndb.get_sensor_placement(network, scheme_id) is None:
raise SensorPlacementNotFoundError("监测点方案不存在")
raise SensorPlacementConflictError("方案已被其他用户修改,请重新加载")
return get_sensor_placement_scheme(network, scheme_id)
if sensor_placement_repository.get_sensor_placement(network, run_id) is None:
raise SensorPlacementNotFoundError("监测点优化运行不存在")
raise SensorPlacementConflictError("运行结果已被其他用户修改,请重新加载")
return get_sensor_placement_run(network, run_id)
def can_edit_sensor_placement(user: Any, scheme: dict[str, Any]) -> bool:
def can_edit_sensor_placement(user: Any, run: dict[str, Any]) -> bool:
return bool(
getattr(user, "is_superuser", False)
or getattr(user, "role", None) == "admin"
or getattr(user, "username", None) == scheme.get("username")
or getattr(user, "username", None) == run.get("created_by")
)
@@ -174,16 +187,16 @@ def _populate_info_sheet(
location_count: int,
is_draft: bool,
) -> None:
created_at = scheme["create_time"]
created_at = scheme["created_at"]
if isinstance(created_at, datetime):
created_at = created_at.isoformat(timespec="minutes")
rows = [
("项目", network),
("方案名称", scheme["scheme_name"]),
("运行名称", scheme["name"]),
("监测点数量", location_count),
("最小管径", scheme["min_diameter"]),
("创建人", scheme["username"]),
("创建人", scheme["created_by"]),
("创建时间", created_at),
("导出时间", datetime.now().astimezone().isoformat(timespec="minutes")),
("文档状态", "未保存草稿" if is_draft else "当前方案"),
@@ -245,7 +258,7 @@ def build_sensor_placement_workbook(
) -> BytesIO:
locations = _normalize_locations(sensor_location)
points = _sensor_points(network, locations)
is_draft = locations != list(scheme["sensor_location"])
is_draft = locations != list(scheme["sensor_locations"])
workbook = Workbook()
info_sheet = workbook.active