refactor(backend)!: separate algorithm and data layers
Reorganize algorithm packages by business responsibility, move orchestration into services, and keep database access behind pooled repositories. Harden analysis API validation, remove unsafe legacy simulation endpoints, and add regression and architecture boundary coverage. BREAKING CHANGE: legacy algorithm module paths and obsolete simulation endpoints are removed.
This commit is contained in:
@@ -3,34 +3,49 @@ from typing import Any
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Body
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
|
||||
from app.auth.keycloak_dependencies import get_current_keycloak_username
|
||||
from app.services.leakage_identifier import (
|
||||
from app.services.dma_leakage_estimation import (
|
||||
run_leakage_identification,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
DEFAULT_N_WORKERS = max(1, min((os.cpu_count() or 1) - 1, 4))
|
||||
MAX_POPULATION_SIZE = 1_000
|
||||
MAX_GENERATIONS = 1_000
|
||||
MAX_DURATION_HOURS = 168
|
||||
|
||||
|
||||
class LeakageIdentifyRequest(BaseModel):
|
||||
"""漏损识别请求模型"""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
network: str = Field(..., description="管网名称(或数据库名称)")
|
||||
observed_pressure_data: str | dict[str, list[Any]] | list[dict[str, Any]] | None = Field(
|
||||
None, description="观测的压力数据"
|
||||
observed_pressure_data: dict[str, list[Any]] | list[dict[str, Any]] | None = (
|
||||
Field(None, description="观测的压力数据;文件路径不属于公共 API 输入")
|
||||
)
|
||||
start_time: float = Field(0, description="起始时间(小时)")
|
||||
duration: float = Field(24, description="持续时间(小时)")
|
||||
timestep: float = Field(5, description="时间步长(分钟)")
|
||||
q_sum: float = Field(0.2, description="总流量(m3/s)")
|
||||
start_time: float = Field(0, ge=0, description="起始时间(小时)")
|
||||
duration: float = Field(
|
||||
24, gt=0, le=MAX_DURATION_HOURS, description="持续时间(小时)"
|
||||
)
|
||||
timestep: float = Field(5, gt=0, le=1440, description="时间步长(分钟)")
|
||||
q_sum: float = Field(0.2, ge=0, description="总流量(m3/s)")
|
||||
q_sum_unit: str = Field("m3/s", description="流量单位")
|
||||
output_dir: str = Field("db_inp", description="输出目录")
|
||||
pop_size: int = Field(50, description="种群大小")
|
||||
max_gen: int = Field(100, description="最大代数")
|
||||
n_workers: int = Field(DEFAULT_N_WORKERS, description="工作线程数")
|
||||
pop_size: int = Field(
|
||||
50, ge=2, le=MAX_POPULATION_SIZE, description="种群大小"
|
||||
)
|
||||
max_gen: int = Field(100, ge=1, le=MAX_GENERATIONS, description="最大代数")
|
||||
n_workers: int = Field(
|
||||
DEFAULT_N_WORKERS,
|
||||
ge=1,
|
||||
le=DEFAULT_N_WORKERS,
|
||||
description="工作进程数",
|
||||
)
|
||||
output_flow_unit: str = Field("m3/s", description="输出流量单位")
|
||||
dma_count: int | None = Field(None, description="DMA区域数量")
|
||||
dma_count: int | None = Field(None, ge=1, description="DMA区域数量")
|
||||
scada_start: datetime | None = Field(None, description="SCADA数据起始时间")
|
||||
scada_end: datetime | None = Field(None, description="SCADA数据结束时间")
|
||||
sensor_nodes: list[str] | None = Field(None, description="传感器节点列表")
|
||||
@@ -63,6 +78,10 @@ async def identify_leakage(
|
||||
HTTPException: 当处理过程中发生错误时
|
||||
"""
|
||||
try:
|
||||
return run_leakage_identification(**data.model_dump(), username=username)
|
||||
return await run_in_threadpool(
|
||||
run_leakage_identification,
|
||||
**data.model_dump(),
|
||||
username=username,
|
||||
)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
Reference in New Issue
Block a user