From b513d05611b39fb1ff97b10358c6d913e90e98a1 Mon Sep 17 00:00:00 2001 From: Jiang Date: Fri, 13 Mar 2026 15:17:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96API=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=8F=82=E6=95=B0=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E5=92=8C=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/v1/endpoints/audit.py | 20 +- app/api/v1/endpoints/burst_detection.py | 100 ++- app/api/v1/endpoints/burst_location.py | 114 +++- app/api/v1/endpoints/cache.py | 34 +- app/api/v1/endpoints/components/controls.py | 56 +- app/api/v1/endpoints/components/curves.py | 73 +- app/api/v1/endpoints/components/options.py | 112 +++- app/api/v1/endpoints/components/patterns.py | 73 +- app/api/v1/endpoints/components/quality.py | 252 +++++-- app/api/v1/endpoints/components/visuals.py | 148 ++++- app/api/v1/endpoints/extension.py | 91 ++- app/api/v1/endpoints/leakage.py | 116 +++- app/api/v1/endpoints/meta.py | 23 +- app/api/v1/endpoints/misc.py | 49 +- app/api/v1/endpoints/network/demands.py | 101 ++- app/api/v1/endpoints/network/general.py | 340 ++++++++-- app/api/v1/endpoints/network/geometry.py | 72 +- app/api/v1/endpoints/network/junctions.py | 315 +++++++-- app/api/v1/endpoints/network/pipes.py | 366 ++++++++-- app/api/v1/endpoints/network/pumps.py | 176 ++++- app/api/v1/endpoints/network/regions.py | 471 ++++++++++--- app/api/v1/endpoints/network/reservoirs.py | 378 ++++++++++- app/api/v1/endpoints/network/tags.py | 50 +- app/api/v1/endpoints/network/tanks.py | 501 ++++++++++++-- app/api/v1/endpoints/network/valves.py | 291 ++++++-- app/api/v1/endpoints/project.py | 400 +++++++++-- app/api/v1/endpoints/project_data.py | 30 +- app/api/v1/endpoints/risk.py | 105 ++- app/api/v1/endpoints/scada.py | 472 +++++++++++-- app/api/v1/endpoints/schemes.py | 29 +- app/api/v1/endpoints/simulation.py | 660 +++++++++++++------ app/api/v1/endpoints/snapshots.py | 171 +++-- app/api/v1/endpoints/timeseries/composite.py | 137 ++-- app/api/v1/endpoints/timeseries/realtime.py | 213 ++++-- app/api/v1/endpoints/timeseries/scada.py | 119 +++- app/api/v1/endpoints/timeseries/scheme.py | 337 ++++++++-- app/api/v1/endpoints/user_management.py | 46 +- app/api/v1/endpoints/users.py | 29 +- 38 files changed, 5846 insertions(+), 1224 deletions(-) diff --git a/app/api/v1/endpoints/audit.py b/app/api/v1/endpoints/audit.py index 974d5e4..01183f5 100644 --- a/app/api/v1/endpoints/audit.py +++ b/app/api/v1/endpoints/audit.py @@ -6,7 +6,7 @@ from typing import List, Optional from uuid import UUID from datetime import datetime -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Query, Path from app.domain.schemas.audit import AuditLogResponse from app.infra.repositories.audit_repository import AuditRepository from app.auth.metadata_dependencies import ( @@ -24,7 +24,7 @@ async def get_audit_repository( """获取审计日志仓储""" return AuditRepository(session) -@router.get("/logs", response_model=List[AuditLogResponse]) +@router.get("/logs", summary="查询审计日志", description="查询审计日志(仅管理员)", response_model=List[AuditLogResponse]) async def get_audit_logs( user_id: Optional[UUID] = Query(None, description="按用户ID过滤"), project_id: Optional[UUID] = Query(None, description="按项目ID过滤"), @@ -38,9 +38,9 @@ async def get_audit_logs( audit_repo: AuditRepository = Depends(get_audit_repository), ) -> List[AuditLogResponse]: """ - 查询审计日志(仅管理员) + 查询审计日志 - 支持按用户、时间、操作类型等条件过滤 + 支持按用户、时间、操作类型等条件过滤,仅管理员可访问 """ logs = await audit_repo.get_logs( user_id=user_id, @@ -54,7 +54,7 @@ async def get_audit_logs( ) return logs -@router.get("/logs/count") +@router.get("/logs/count", summary="获取审计日志总数", description="获取审计日志总数(仅管理员)") async def get_audit_logs_count( user_id: Optional[UUID] = Query(None, description="按用户ID过滤"), project_id: Optional[UUID] = Query(None, description="按项目ID过滤"), @@ -66,7 +66,9 @@ async def get_audit_logs_count( audit_repo: AuditRepository = Depends(get_audit_repository), ) -> dict: """ - 获取审计日志总数(仅管理员) + 获取审计日志总数 + + 获取符合条件的审计日志的总数,仅管理员可访问 """ count = await audit_repo.get_log_count( user_id=user_id, @@ -78,13 +80,13 @@ async def get_audit_logs_count( ) return {"count": count} -@router.get("/logs/my", response_model=List[AuditLogResponse]) +@router.get("/logs/my", summary="查询我的审计日志", description="查询当前用户的审计日志", response_model=List[AuditLogResponse]) async def get_my_audit_logs( action: Optional[str] = Query(None, description="按操作类型过滤"), start_time: Optional[datetime] = Query(None, description="开始时间"), end_time: Optional[datetime] = Query(None, description="结束时间"), - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), + skip: int = Query(0, ge=0, description="跳过记录数"), + limit: int = Query(100, ge=1, le=1000, description="限制记录数"), current_user=Depends(get_current_metadata_user), audit_repo: AuditRepository = Depends(get_audit_repository), ) -> List[AuditLogResponse]: diff --git a/app/api/v1/endpoints/burst_detection.py b/app/api/v1/endpoints/burst_detection.py index 149343f..37d7849 100644 --- a/app/api/v1/endpoints/burst_detection.py +++ b/app/api/v1/endpoints/burst_detection.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Any -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body from pydantic import BaseModel, Field from app.auth.keycloak_dependencies import get_current_keycloak_username @@ -15,7 +15,8 @@ router = APIRouter() class BurstDetectionRequest(BaseModel): - network: str + """爆管检测请求模型""" + network: str = Field(..., description="管网名称(或数据库名称)") observed_pressure_data: ( dict[str, list[Any]] | list[dict[str, Any]] | list[list[Any]] | None ) = Field( @@ -26,45 +27,104 @@ class BurstDetectionRequest(BaseModel): "或二维数组 [[t1_s1, t1_s2], [t2_s1, t2_s2], ...]。" ), ) - points_per_day: int = 1440 - mu: int = 100 - iforest_params: dict[str, Any] | None = None - scada_start: datetime | None = None - scada_end: datetime | None = None - 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 + points_per_day: int = Field(1440, description="每天的数据点数") + mu: int = Field(100, description="异常值检测的参数") + iforest_params: dict[str, Any] | None = Field(None, description="隔离森林算法参数") + scada_start: datetime | None = Field(None, description="SCADA数据起始时间") + scada_end: datetime | None = Field(None, description="SCADA数据结束时间") + sensor_nodes: list[str] | None = Field(None, description="传感器节点列表") + scheme_name: str | None = Field(None, description="方案名称") + data_source: str = Field("monitoring", description="数据来源:monitoring(监测)或simulation(模拟)") + simulation_scheme_name: str | None = Field(None, description="模拟方案名称") + simulation_scheme_type: str | None = Field(None, description="模拟方案类型") -@router.post("/detect/") +@router.post( + "/detect/", + summary="执行爆管检测", + description="基于压力观测数据和其他参数执行爆管检测分析" +) async def detect_burst( - data: BurstDetectionRequest, + data: BurstDetectionRequest = Body(..., description="爆管检测请求数据"), username: str = Depends(get_current_keycloak_username), ) -> dict[str, Any]: + """ + 执行爆管检测分析。 + + 使用异常检测算法(隔离森林)识别压力时间序列中的异常, + 将其作为潜在的爆管事件。 + + Args: + data: 包含管网名称(或数据库名称)、压力数据及相关参数的请求体 + username: 当前认证用户名 + + Returns: + 包含检测结果的字典 + + Raises: + HTTPException: 当处理过程中发生错误时 + """ try: return run_burst_detection(**data.model_dump(), username=username) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) -@router.get("/schemes/") +@router.get( + "/schemes/", + summary="查询爆管检测方案列表", + description="获取指定网络的所有爆管检测方案" +) async def query_burst_detection_schemes( - network: str, - query_date: datetime | None = None, + network: str = Query(..., description="管网名称(或数据库名称)"), + query_date: datetime | None = Query(None, description="查询日期(可选)"), ) -> list[dict[str, Any]]: + """ + 获取爆管检测方案列表。 + + 查询指定网络的所有已配置的爆管检测方案, + 可按日期进行筛选。 + + Args: + network: 管网名称(或数据库名称) + query_date: 查询日期(可选) + + Returns: + 爆管检测方案列表 + + Raises: + HTTPException: 当查询失败时 + """ try: return list_burst_detection_schemes(network=network, query_date=query_date) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) -@router.get("/schemes/{scheme_name}") +@router.get( + "/schemes/{scheme_name}", + summary="获取爆管检测方案详情", + description="获取指定爆管检测方案的详细信息" +) async def query_burst_detection_scheme_detail( - network: str, - scheme_name: str, + network: str = Query(..., description="管网名称(或数据库名称)"), + scheme_name: str = Path(..., description="爆管检测方案名称"), ) -> dict[str, Any]: + """ + 获取爆管检测方案详情。 + + 查询指定爆管检测方案的完整配置和参数信息。 + + Args: + network: 管网名称(或数据库名称) + scheme_name: 爆管检测方案名称 + + Returns: + 包含方案详情的字典 + + Raises: + HTTPException: 当查询失败时 + """ try: return get_burst_detection_scheme_detail(network=network, scheme_name=scheme_name) except Exception as exc: diff --git a/app/api/v1/endpoints/burst_location.py b/app/api/v1/endpoints/burst_location.py index e9ec3e4..bc4023c 100644 --- a/app/api/v1/endpoints/burst_location.py +++ b/app/api/v1/endpoints/burst_location.py @@ -3,8 +3,8 @@ from datetime import datetime from typing import Literal -from fastapi import APIRouter, Depends, HTTPException -from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body +from pydantic import BaseModel, Field from app.auth.keycloak_dependencies import get_current_keycloak_username from app.services.burst_location import ( @@ -17,48 +17,112 @@ router = APIRouter() class BurstLocationRequest(BaseModel): - network: str - data_source: Literal["monitoring", "simulation"] = "monitoring" - pressure_scada_ids: list[str] | None = None - burst_pressure: dict[str, float] | list[dict[str, Any]] | None = None - normal_pressure: dict[str, float] | list[dict[str, Any]] | None = None - burst_leakage: float - flow_scada_ids: list[str] | None = None - burst_flow: dict[str, float] | list[dict[str, Any]] | None = None - normal_flow: dict[str, float] | list[dict[str, Any]] | None = None - min_dpressure: float = 2.0 - basic_pressure: float = 10.0 - scada_burst_start: datetime | None = None - scada_burst_end: datetime | None = None - use_scada_flow: bool = False - scheme_name: str | None = None - simulation_scheme_name: str | None = None - simulation_scheme_type: str | None = None + """爆管定位请求模型""" + network: str = Field(..., description="管网名称(或数据库名称)") + data_source: Literal["monitoring", "simulation"] = Field("monitoring", description="数据来源:monitoring(监测)或simulation(模拟)") + pressure_scada_ids: list[str] | None = Field(None, description="压力SCADA传感器ID列表") + burst_pressure: dict[str, float] | list[dict[str, Any]] | None = Field(None, description="爆管时的压力数据") + normal_pressure: dict[str, float] | list[dict[str, Any]] | None = Field(None, description="正常时的压力数据") + burst_leakage: float = Field(..., description="爆管时的漏水量") + flow_scada_ids: list[str] | None = Field(None, description="流量SCADA传感器ID列表") + burst_flow: dict[str, float] | list[dict[str, Any]] | None = Field(None, description="爆管时的流量数据") + normal_flow: dict[str, float] | list[dict[str, Any]] | None = Field(None, description="正常时的流量数据") + min_dpressure: float = Field(2.0, description="最小压力差(bar)") + basic_pressure: float = Field(10.0, description="基准压力(bar)") + scada_burst_start: datetime | None = Field(None, description="SCADA爆管开始时间") + scada_burst_end: datetime | None = Field(None, description="SCADA爆管结束时间") + use_scada_flow: bool = Field(False, description="是否使用SCADA流量数据") + scheme_name: str | None = Field(None, description="方案名称") + simulation_scheme_name: str | None = Field(None, description="模拟方案名称") + simulation_scheme_type: str | None = Field(None, description="模拟方案类型") -@router.post("/locate/") +@router.post( + "/locate/", + summary="执行爆管定位", + description="基于压力和流量数据定位管网中的爆管位置" +) async def locate_burst( - data: BurstLocationRequest, + data: BurstLocationRequest = Body(..., description="爆管定位请求数据"), username: str = Depends(get_current_keycloak_username), ) -> dict[str, Any]: + """ + 执行爆管定位分析。 + + 使用压力和流量SCADA数据,通过对比爆管和正常状态下的数据差异, + 定位管网中的爆管位置。 + + Args: + data: 包含管网名称(或数据库名称)、压力、流量数据及相关参数的请求体 + username: 当前认证用户名 + + Returns: + 包含定位结果的字典 + + Raises: + HTTPException: 当数据类型或值不正确时 + """ try: return run_burst_location_by_network(**data.model_dump(), username=username) except (TypeError, ValueError) as exc: raise HTTPException(status_code=400, detail=str(exc)) -@router.get("/schemes/") +@router.get( + "/schemes/", + summary="查询爆管定位方案列表", + description="获取指定网络的所有爆管定位方案" +) async def query_burst_schemes( - network: str, query_date: datetime | None = None + network: str = Query(..., description="管网名称(或数据库名称)"), + query_date: datetime | None = Query(None, description="查询日期(可选)") ) -> list[dict[str, Any]]: + """ + 获取爆管定位方案列表。 + + 查询指定网络的所有已配置的爆管定位方案, + 可按日期进行筛选。 + + Args: + network: 管网名称(或数据库名称) + query_date: 查询日期(可选) + + Returns: + 爆管定位方案列表 + + Raises: + HTTPException: 当查询失败时 + """ try: return list_burst_location_schemes(network=network, query_date=query_date) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) -@router.get("/schemes/{scheme_name}") -async def query_burst_scheme_detail(network: str, scheme_name: str) -> dict[str, Any]: +@router.get( + "/schemes/{scheme_name}", + summary="获取爆管定位方案详情", + description="获取指定爆管定位方案的详细信息" +) +async def query_burst_scheme_detail( + network: str = Query(..., description="管网名称(或数据库名称)"), + scheme_name: str = Path(..., description="爆管定位方案名称") +) -> dict[str, Any]: + """ + 获取爆管定位方案详情。 + + 查询指定爆管定位方案的完整配置和参数信息。 + + Args: + network: 管网名称(或数据库名称) + scheme_name: 爆管定位方案名称 + + Returns: + 包含方案详情的字典 + + Raises: + HTTPException: 当查询失败时 + """ try: return get_burst_location_scheme_detail(network=network, scheme_name=scheme_name) except Exception as exc: diff --git a/app/api/v1/endpoints/cache.py b/app/api/v1/endpoints/cache.py index 06d6bb1..9e4dbdf 100644 --- a/app/api/v1/endpoints/cache.py +++ b/app/api/v1/endpoints/cache.py @@ -1,16 +1,26 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Query from app.infra.cache.redis_client import redis_client router = APIRouter() -@router.post("/clearrediskey/") -async def fastapi_clear_redis_key(key: str): +@router.post("/clearrediskey/", summary="清除单个缓存键", description="根据键名清除单个Redis缓存") +async def fastapi_clear_redis_key(key: str = Query(..., description="缓存键名")): + """ + 清除单个缓存键 + + 根据指定的键名删除Redis中对应的缓存 + """ redis_client.delete(key) return True -@router.post("/clearrediskeys/") -async def fastapi_clear_redis_keys(keys: str): +@router.post("/clearrediskeys/", summary="清除匹配的缓存键", description="根据模式清除匹配的Redis缓存键") +async def fastapi_clear_redis_keys(keys: str = Query(..., description="缓存键模式(支持通配符)")): + """ + 清除匹配的缓存键 + + 根据指定的模式删除Redis中所有匹配的缓存键 + """ # delete keys contains the key matched_keys = redis_client.keys(f"*{keys}*") if matched_keys: @@ -19,14 +29,24 @@ async def fastapi_clear_redis_keys(keys: str): return True -@router.post("/clearallredis/") +@router.post("/clearallredis/", summary="清除所有缓存", description="清空整个Redis数据库的所有缓存") async def fastapi_clear_all_redis(): + """ + 清除所有缓存 + + 清空Redis数据库中的所有缓存键值对 + """ redis_client.flushdb() return True -@router.get("/queryredis/") +@router.get("/queryredis/", summary="查询缓存键列表", description="获取Redis中所有的缓存键") async def fastapi_query_redis(): + """ + 查询缓存键列表 + + 获取Redis数据库中所有的缓存键列表 + """ # Helper to decode bytes to str for JSON response if needed, # but original just returned keys (which might be bytes in redis-py unless decode_responses=True) # create_redis_client usually sets decode_responses=False by default. diff --git a/app/api/v1/endpoints/components/controls.py b/app/api/v1/endpoints/components/controls.py index 3f89510..a0edd6f 100644 --- a/app/api/v1/endpoints/components/controls.py +++ b/app/api/v1/endpoints/components/controls.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -13,28 +13,58 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getcontrolschema/") -async def fastapi_get_control_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getcontrolschema/", summary="获取控制架构", description="获取网络中控制对象的架构定义") +async def fastapi_get_control_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取控制架构。 + + 返回指定网络中控制对象的属性架构定义。 + """ return get_control_schema(network) -@router.get("/getcontrolproperties/") -async def fastapi_get_control_properties(network: str) -> dict[str, Any]: +@router.get("/getcontrolproperties/", summary="获取控制属性", description="获取指定网络中的控制属性信息") +async def fastapi_get_control_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取控制属性。 + + 返回指定网络中的控制对象属性信息。 + """ return get_control(network) -@router.post("/setcontrolproperties/", response_model=None) -async def fastapi_set_control_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setcontrolproperties/", response_model=None, summary="设置控制属性", description="更新指定网络中的控制属性") +async def fastapi_set_control_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置控制属性。 + + 更新指定网络中的控制属性值。 + """ props = await req.json() return set_control(network, ChangeSet(props)) -@router.get("/getruleschema/") -async def fastapi_get_rule_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getruleschema/", summary="获取规则架构", description="获取网络中规则对象的架构定义") +async def fastapi_get_rule_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取规则架构。 + + 返回指定网络中规则对象的属性架构定义。 + """ return get_rule_schema(network) -@router.get("/getruleproperties/") -async def fastapi_get_rule_properties(network: str) -> dict[str, Any]: +@router.get("/getruleproperties/", summary="获取规则属性", description="获取指定网络中的规则属性信息") +async def fastapi_get_rule_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取规则属性。 + + 返回指定网络中的规则对象属性信息。 + """ return get_rule(network) -@router.post("/setruleproperties/", response_model=None) -async def fastapi_set_rule_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setruleproperties/", response_model=None, summary="设置规则属性", description="更新指定网络中的规则属性") +async def fastapi_set_rule_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置规则属性。 + + 更新指定网络中的规则属性值。 + """ props = await req.json() return set_rule(network, ChangeSet(props)) diff --git a/app/api/v1/endpoints/components/curves.py b/app/api/v1/endpoints/components/curves.py index 33aabe0..3e160ce 100644 --- a/app/api/v1/endpoints/components/curves.py +++ b/app/api/v1/endpoints/components/curves.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -14,39 +14,82 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getcurveschema") -async def fastapi_get_curve_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getcurveschema", summary="获取曲线架构", description="获取网络中曲线对象的架构定义") +async def fastapi_get_curve_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取曲线架构。 + + 返回指定网络中曲线对象的属性架构定义。 + """ return get_curve_schema(network) -@router.post("/addcurve/", response_model=None) -async def fastapi_add_curve(network: str, curve: str, req: Request) -> ChangeSet: +@router.post("/addcurve/", response_model=None, summary="添加曲线", description="在网络中添加一条新的曲线") +async def fastapi_add_curve( + network: str = Query(..., description="管网名称(或数据库名称)"), + curve: str = Query(..., description="曲线ID"), + req: Request = Body(...) +) -> ChangeSet: + """添加曲线。 + + 在指定网络中创建一条新的曲线,并设置其初始属性。 + """ props = await req.json() ps = { "id": curve, } | props return add_curve(network, ChangeSet(ps)) -@router.post("/deletecurve/", response_model=None) -async def fastapi_delete_curve(network: str, curve: str) -> ChangeSet: +@router.post("/deletecurve/", response_model=None, summary="删除曲线", description="从网络中删除指定的曲线") +async def fastapi_delete_curve( + network: str = Query(..., description="管网名称(或数据库名称)"), + curve: str = Query(..., description="曲线ID") +) -> ChangeSet: + """删除曲线。 + + 从指定网络中删除指定的曲线及其相关数据。 + """ ps = {"id": curve} return delete_curve(network, ChangeSet(ps)) -@router.get("/getcurveproperties/") -async def fastapi_get_curve_properties(network: str, curve: str) -> dict[str, Any]: +@router.get("/getcurveproperties/", summary="获取曲线属性", description="获取指定曲线的属性信息") +async def fastapi_get_curve_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + curve: str = Query(..., description="曲线ID") +) -> dict[str, Any]: + """获取曲线属性。 + + 返回指定曲线的所有属性信息。 + """ return get_curve(network, curve) -@router.post("/setcurveproperties/", response_model=None) +@router.post("/setcurveproperties/", response_model=None, summary="设置曲线属性", description="更新指定曲线的属性") async def fastapi_set_curve_properties( - network: str, curve: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + curve: str = Query(..., description="曲线ID"), + req: Request = Body(...) ) -> ChangeSet: + """设置曲线属性。 + + 更新指定曲线的属性值。 + """ props = await req.json() ps = {"id": curve} | props return set_curve(network, ChangeSet(ps)) -@router.get("/getcurves/") -async def fastapi_get_curves(network: str) -> list[str]: +@router.get("/getcurves/", summary="获取所有曲线", description="获取网络中的所有曲线列表") +async def fastapi_get_curves(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]: + """获取所有曲线。 + + 返回指定网络中的所有曲线ID列表。 + """ return get_curves(network) -@router.get("/iscurve/") -async def fastapi_is_curve(network: str, curve: str) -> bool: +@router.get("/iscurve/", summary="检查曲线存在性", description="检查指定的曲线是否存在") +async def fastapi_is_curve( + network: str = Query(..., description="管网名称(或数据库名称)"), + curve: str = Query(..., description="曲线ID") +) -> bool: + """检查曲线是否存在。 + + 判断指定的曲线是否在网络中存在。 + """ return is_curve(network, curve) diff --git a/app/api/v1/endpoints/components/options.py b/app/api/v1/endpoints/components/options.py index c05db24..f560ca5 100644 --- a/app/api/v1/endpoints/components/options.py +++ b/app/api/v1/endpoints/components/options.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -19,57 +19,119 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/gettimeschema") -async def fastapi_get_time_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/gettimeschema", summary="获取时间选项架构", description="获取网络中时间选项的架构定义") +async def fastapi_get_time_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取时间选项架构。 + + 返回指定网络中时间相关选项的属性架构定义。 + """ return get_time_schema(network) -@router.get("/gettimeproperties/") -async def fastapi_get_time_properties(network: str) -> dict[str, Any]: +@router.get("/gettimeproperties/", summary="获取时间选项属性", description="获取指定网络中的时间选项属性信息") +async def fastapi_get_time_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取时间选项属性。 + + 返回指定网络中的时间相关选项属性。 + """ return get_time(network) -@router.post("/settimeproperties/", response_model=None) -async def fastapi_set_time_properties(network: str, req: Request) -> ChangeSet: +@router.post("/settimeproperties/", response_model=None, summary="设置时间选项属性", description="更新指定网络中的时间选项属性") +async def fastapi_set_time_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置时间选项属性。 + + 更新指定网络中的时间相关选项属性值。 + """ props = await req.json() return set_time(network, ChangeSet(props)) -@router.get("/getenergyschema/") -async def fastapi_get_energy_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getenergyschema/", summary="获取能耗选项架构", description="获取网络中能耗选项的架构定义") +async def fastapi_get_energy_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取能耗选项架构。 + + 返回指定网络中能耗相关选项的属性架构定义。 + """ return get_energy_schema(network) -@router.get("/getenergyproperties/") -async def fastapi_get_energy_properties(network: str) -> dict[str, Any]: +@router.get("/getenergyproperties/", summary="获取能耗选项属性", description="获取指定网络中的能耗选项属性信息") +async def fastapi_get_energy_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取能耗选项属性。 + + 返回指定网络中的能耗相关选项属性。 + """ return get_energy(network) -@router.post("/setenergyproperties/", response_model=None) -async def fastapi_set_energy_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setenergyproperties/", response_model=None, summary="设置能耗选项属性", description="更新指定网络中的能耗选项属性") +async def fastapi_set_energy_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置能耗选项属性。 + + 更新指定网络中的能耗相关选项属性值。 + """ props = await req.json() return set_energy(network, ChangeSet(props)) -@router.get("/getpumpenergyschema/") -async def fastapi_get_pump_energy_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getpumpenergyschema/", summary="获取泵能耗选项架构", description="获取网络中泵能耗选项的架构定义") +async def fastapi_get_pump_energy_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取泵能耗选项架构。 + + 返回指定网络中泵能耗相关选项的属性架构定义。 + """ return get_pump_energy_schema(network) -@router.get("/getpumpenergyproperties//") -async def fastapi_get_pump_energy_proeprties(network: str, pump: str) -> dict[str, Any]: +@router.get("/getpumpenergyproperties//", summary="获取泵能耗属性", description="获取指定泵的能耗属性信息") +async def fastapi_get_pump_energy_proeprties( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="泵ID") +) -> dict[str, Any]: + """获取泵能耗属性。 + + 返回指定泵的能耗相关属性。 + """ return get_pump_energy(network, pump) -@router.get("/setpumpenergyproperties//", response_model=None) +@router.get("/setpumpenergyproperties//", response_model=None, summary="设置泵能耗属性", description="更新指定泵的能耗属性") async def fastapi_set_pump_energy_properties( - network: str, pump: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="泵ID"), + req: Request = Body(...) ) -> ChangeSet: + """设置泵能耗属性。 + + 更新指定泵的能耗相关属性值。 + """ props = await req.json() ps = {"id": pump} | props return set_pump_energy(network, ChangeSet(ps)) -@router.get("/getoptionschema/") -async def fastapi_get_option_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getoptionschema/", summary="获取选项架构", description="获取网络中选项对象的架构定义") +async def fastapi_get_option_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取选项架构。 + + 返回指定网络中选项对象的属性架构定义。 + """ return get_option_v3_schema(network) -@router.get("/getoptionproperties/") -async def fastapi_get_option_properties(network: str) -> dict[str, Any]: +@router.get("/getoptionproperties/", summary="获取选项属性", description="获取指定网络中的选项属性信息") +async def fastapi_get_option_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取选项属性。 + + 返回指定网络中的选项对象属性信息。 + """ return get_option_v3(network) -@router.post("/setoptionproperties/", response_model=None) -async def fastapi_set_option_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setoptionproperties/", response_model=None, summary="设置选项属性", description="更新指定网络中的选项属性") +async def fastapi_set_option_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置选项属性。 + + 更新指定网络中的选项属性值。 + """ props = await req.json() return set_option_v3(network, ChangeSet(props)) diff --git a/app/api/v1/endpoints/components/patterns.py b/app/api/v1/endpoints/components/patterns.py index b42e14a..d5ef05e 100644 --- a/app/api/v1/endpoints/components/patterns.py +++ b/app/api/v1/endpoints/components/patterns.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -14,39 +14,82 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getpatternschema") -async def fastapi_get_pattern_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getpatternschema", summary="获取模式架构", description="获取网络中模式对象的架构定义") +async def fastapi_get_pattern_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取模式架构。 + + 返回指定网络中模式对象的属性架构定义。 + """ return get_pattern_schema(network) -@router.post("/addpattern/", response_model=None) -async def fastapi_add_pattern(network: str, pattern: str, req: Request) -> ChangeSet: +@router.post("/addpattern/", response_model=None, summary="添加模式", description="在网络中添加一个新的模式") +async def fastapi_add_pattern( + network: str = Query(..., description="管网名称(或数据库名称)"), + pattern: str = Query(..., description="模式ID"), + req: Request = Body(...) +) -> ChangeSet: + """添加模式。 + + 在指定网络中创建一个新的模式,并设置其初始属性。 + """ props = await req.json() ps = { "id": pattern, } | props return add_pattern(network, ChangeSet(ps)) -@router.post("/deletepattern/", response_model=None) -async def fastapi_delete_pattern(network: str, pattern: str) -> ChangeSet: +@router.post("/deletepattern/", response_model=None, summary="删除模式", description="从网络中删除指定的模式") +async def fastapi_delete_pattern( + network: str = Query(..., description="管网名称(或数据库名称)"), + pattern: str = Query(..., description="模式ID") +) -> ChangeSet: + """删除模式。 + + 从指定网络中删除指定的模式及其相关数据。 + """ ps = {"id": pattern} return delete_pattern(network, ChangeSet(ps)) -@router.get("/getpatternproperties/") -async def fastapi_get_pattern_properties(network: str, pattern: str) -> dict[str, Any]: +@router.get("/getpatternproperties/", summary="获取模式属性", description="获取指定模式的属性信息") +async def fastapi_get_pattern_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + pattern: str = Query(..., description="模式ID") +) -> dict[str, Any]: + """获取模式属性。 + + 返回指定模式的所有属性信息。 + """ return get_pattern(network, pattern) -@router.post("/setpatternproperties/", response_model=None) +@router.post("/setpatternproperties/", response_model=None, summary="设置模式属性", description="更新指定模式的属性") async def fastapi_set_pattern_properties( - network: str, pattern: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + pattern: str = Query(..., description="模式ID"), + req: Request = Body(...) ) -> ChangeSet: + """设置模式属性。 + + 更新指定模式的属性值。 + """ props = await req.json() ps = {"id": pattern} | props return set_pattern(network, ChangeSet(ps)) -@router.get("/ispattern/") -async def fastapi_is_pattern(network: str, pattern: str) -> bool: +@router.get("/ispattern/", summary="检查模式存在性", description="检查指定的模式是否存在") +async def fastapi_is_pattern( + network: str = Query(..., description="管网名称(或数据库名称)"), + pattern: str = Query(..., description="模式ID") +) -> bool: + """检查模式是否存在。 + + 判断指定的模式是否在网络中存在。 + """ return is_pattern(network, pattern) -@router.get("/getpatterns/") -async def fastapi_get_patterns(network: str) -> list[str]: +@router.get("/getpatterns/", summary="获取所有模式", description="获取网络中的所有模式列表") +async def fastapi_get_patterns(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]: + """获取所有模式。 + + 返回指定网络中的所有模式ID列表。 + """ return get_patterns(network) diff --git a/app/api/v1/endpoints/components/quality.py b/app/api/v1/endpoints/components/quality.py index 01fc1d1..cb54158 100644 --- a/app/api/v1/endpoints/components/quality.py +++ b/app/api/v1/endpoints/components/quality.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -32,116 +32,266 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getqualityschema/") -async def fastapi_get_quality_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getqualityschema/", summary="获取水质架构", description="获取网络中水质对象的架构定义") +async def fastapi_get_quality_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取水质架构。 + + 返回指定网络中水质对象的属性架构定义。 + """ return get_quality_schema(network) -@router.get("/getqualityproperties/") -async def fastapi_get_quality_properties(network: str, node: str) -> dict[str, Any]: +@router.get("/getqualityproperties/", summary="获取水质属性", description="获取指定节点的水质属性信息") +async def fastapi_get_quality_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> dict[str, Any]: + """获取水质属性。 + + 返回指定节点的水质属性信息。 + """ return get_quality(network, node) -@router.post("/setqualityproperties/", response_model=None) -async def fastapi_set_quality_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setqualityproperties/", response_model=None, summary="设置水质属性", description="更新指定节点的水质属性") +async def fastapi_set_quality_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置水质属性。 + + 更新指定节点的水质属性值。 + """ props = await req.json() return set_quality(network, ChangeSet(props)) -@router.get("/getemitterschema") -async def fastapi_get_emitter_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getemitterschema", summary="获取发射器架构", description="获取网络中发射器对象的架构定义") +async def fastapi_get_emitter_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取发射器架构。 + + 返回指定网络中发射器对象的属性架构定义。 + """ return get_emitter_schema(network) -@router.get("/getemitterproperties/") -async def fastapi_get_emitter_properties(network: str, junction: str) -> dict[str, Any]: +@router.get("/getemitterproperties/", summary="获取发射器属性", description="获取指定连接点的发射器属性信息") +async def fastapi_get_emitter_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="连接点ID") +) -> dict[str, Any]: + """获取发射器属性。 + + 返回指定连接点的发射器属性信息。 + """ return get_emitter(network, junction) -@router.post("/setemitterproperties/", response_model=None) +@router.post("/setemitterproperties/", response_model=None, summary="设置发射器属性", description="更新指定连接点的发射器属性") async def fastapi_set_emitter_properties( - network: str, junction: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="连接点ID"), + req: Request = Body(...) ) -> ChangeSet: + """设置发射器属性。 + + 更新指定连接点的发射器属性值。 + """ props = await req.json() ps = {"junction": junction} | props return set_emitter(network, ChangeSet(ps)) -@router.get("/getsourcechema/") -async def fastapi_get_source_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getsourcechema/", summary="获取水源架构", description="获取网络中水源对象的架构定义") +async def fastapi_get_source_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取水源架构。 + + 返回指定网络中水源对象的属性架构定义。 + """ return get_source_schema(network) -@router.get("/getsource/") -async def fastapi_get_source(network: str, node: str) -> dict[str, Any]: +@router.get("/getsource/", summary="获取水源属性", description="获取指定节点的水源属性信息") +async def fastapi_get_source( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> dict[str, Any]: + """获取水源属性。 + + 返回指定节点的水源属性信息。 + """ return get_source(network, node) -@router.post("/setsource/", response_model=None) -async def fastapi_set_source(network: str, req: Request) -> ChangeSet: +@router.post("/setsource/", response_model=None, summary="设置水源属性", description="更新指定节点的水源属性") +async def fastapi_set_source( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置水源属性。 + + 更新指定节点的水源属性值。 + """ props = await req.json() return set_source(network, ChangeSet(props)) -@router.post("/addsource/", response_model=None) -async def fastapi_add_source(network: str, req: Request) -> ChangeSet: +@router.post("/addsource/", response_model=None, summary="添加水源", description="在网络中添加一个新的水源") +async def fastapi_add_source( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加水源。 + + 在指定网络中创建一个新的水源,并设置其初始属性。 + """ props = await req.json() return add_source(network, ChangeSet(props)) -@router.post("/deletesource/", response_model=None) -async def fastapi_delete_source(network: str, node: str) -> ChangeSet: +@router.post("/deletesource/", response_model=None, summary="删除水源", description="从网络中删除指定节点的水源") +async def fastapi_delete_source( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> ChangeSet: + """删除水源。 + + 从指定网络中删除指定节点的水源。 + """ props = {"node": node} return delete_source(network, ChangeSet(props)) -@router.get("/getreactionschema/") -async def fastapi_get_reaction_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getreactionschema/", summary="获取反应架构", description="获取网络中反应对象的架构定义") +async def fastapi_get_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取反应架构。 + + 返回指定网络中反应对象的属性架构定义。 + """ return get_reaction_schema(network) -@router.get("/getreaction/") -async def fastapi_get_reaction(network: str) -> dict[str, Any]: +@router.get("/getreaction/", summary="获取反应属性", description="获取指定网络中的反应属性信息") +async def fastapi_get_reaction(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取反应属性。 + + 返回指定网络中的反应属性信息。 + """ return get_reaction(network) -@router.post("/setreaction/", response_model=None) -async def fastapi_set_reaction(network: str, req: Request) -> ChangeSet: +@router.post("/setreaction/", response_model=None, summary="设置反应属性", description="更新指定网络中的反应属性") +async def fastapi_set_reaction( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置反应属性。 + + 更新指定网络中的反应属性值。 + """ props = await req.json() return set_reaction(network, ChangeSet(props)) -@router.get("/getpipereactionschema/") -async def fastapi_get_pipe_reaction_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getpipereactionschema/", summary="获取管道反应架构", description="获取网络中管道反应对象的架构定义") +async def fastapi_get_pipe_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取管道反应架构。 + + 返回指定网络中管道反应对象的属性架构定义。 + """ return get_pipe_reaction_schema(network) -@router.get("/getpipereaction/") -async def fastapi_get_pipe_reaction(network: str, pipe: str) -> dict[str, Any]: +@router.get("/getpipereaction/", summary="获取管道反应属性", description="获取指定管道的反应属性信息") +async def fastapi_get_pipe_reaction( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> dict[str, Any]: + """获取管道反应属性。 + + 返回指定管道的反应属性信息。 + """ return get_pipe_reaction(network, pipe) -@router.post("/setpipereaction/", response_model=None) -async def fastapi_set_pipe_reaction(network: str, req: Request) -> ChangeSet: +@router.post("/setpipereaction/", response_model=None, summary="设置管道反应属性", description="更新指定管道的反应属性") +async def fastapi_set_pipe_reaction( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置管道反应属性。 + + 更新指定管道的反应属性值。 + """ props = await req.json() return set_pipe_reaction(network, ChangeSet(props)) -@router.get("/gettankreactionschema/") -async def fastapi_get_tank_reaction_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/gettankreactionschema/", summary="获取水池反应架构", description="获取网络中水池反应对象的架构定义") +async def fastapi_get_tank_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取水池反应架构。 + + 返回指定网络中水池反应对象的属性架构定义。 + """ return get_tank_reaction_schema(network) -@router.get("/gettankreaction/") -async def fastapi_get_tank_reaction(network: str, tank: str) -> dict[str, Any]: +@router.get("/gettankreaction/", summary="获取水池反应属性", description="获取指定水池的反应属性信息") +async def fastapi_get_tank_reaction( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水池ID") +) -> dict[str, Any]: + """获取水池反应属性。 + + 返回指定水池的反应属性信息。 + """ return get_tank_reaction(network, tank) -@router.post("/settankreaction/", response_model=None) -async def fastapi_set_tank_reaction(network: str, req: Request) -> ChangeSet: +@router.post("/settankreaction/", response_model=None, summary="设置水池反应属性", description="更新指定水池的反应属性") +async def fastapi_set_tank_reaction( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置水池反应属性。 + + 更新指定水池的反应属性值。 + """ props = await req.json() return set_tank_reaction(network, ChangeSet(props)) -@router.get("/getmixingschema/") -async def fastapi_get_mixing_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getmixingschema/", summary="获取混合架构", description="获取网络中混合对象的架构定义") +async def fastapi_get_mixing_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取混合架构。 + + 返回指定网络中混合对象的属性架构定义。 + """ return get_mixing_schema(network) -@router.get("/getmixing/") -async def fastapi_get_mixing(network: str, tank: str) -> dict[str, Any]: +@router.get("/getmixing/", summary="获取混合属性", description="获取指定水池的混合属性信息") +async def fastapi_get_mixing( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水池ID") +) -> dict[str, Any]: + """获取混合属性。 + + 返回指定水池的混合属性信息。 + """ return get_mixing(network, tank) -@router.post("/setmixing/", response_model=None) -async def fastapi_set_mixing(network: str, req: Request) -> ChangeSet: +@router.post("/setmixing/", response_model=None, summary="设置混合属性", description="更新指定水池的混合属性") +async def fastapi_set_mixing( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置混合属性。 + + 更新指定水池的混合属性值。 + """ props = await req.json() return api.set_mixing(network, ChangeSet(props)) -@router.post("/addmixing/", response_model=None) -async def fastapi_add_mixing(network: str, req: Request) -> ChangeSet: +@router.post("/addmixing/", response_model=None, summary="添加混合", description="在网络中添加一个新的混合") +async def fastapi_add_mixing( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加混合。 + + 在指定网络中创建一个新的混合,并设置其初始属性。 + """ props = await req.json() return add_mixing(network, ChangeSet(props)) -@router.post("/deletemixing/", response_model=None) -async def fastapi_delete_mixing(network: str, req: Request) -> ChangeSet: +@router.post("/deletemixing/", response_model=None, summary="删除混合", description="从网络中删除指定的混合") +async def fastapi_delete_mixing( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """删除混合。 + + 从指定网络中删除指定的混合及其相关数据。 + """ props = await req.json() return delete_mixing(network, ChangeSet(props)) diff --git a/app/api/v1/endpoints/components/visuals.py b/app/api/v1/endpoints/components/visuals.py index 65f1cad..766165f 100644 --- a/app/api/v1/endpoints/components/visuals.py +++ b/app/api/v1/endpoints/components/visuals.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request, Response +from fastapi import APIRouter, Request, Query, Path, Body, Response from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -24,71 +24,157 @@ import json router = APIRouter() -@router.get("/getvertexschema/") -async def fastapi_get_vertex_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getvertexschema/", summary="获取图形元素架构", description="获取网络中图形元素对象的架构定义") +async def fastapi_get_vertex_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取图形元素架构。 + + 返回指定网络中图形元素对象的属性架构定义。 + """ return get_vertex_schema(network) -@router.get("/getvertexproperties/") -async def fastapi_get_vertex_properties(network: str, link: str) -> dict[str, Any]: +@router.get("/getvertexproperties/", summary="获取图形元素属性", description="获取指定图形元素的属性信息") +async def fastapi_get_vertex_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="图形元素链接") +) -> dict[str, Any]: + """获取图形元素属性。 + + 返回指定图形元素的所有属性信息。 + """ return get_vertex(network, link) -@router.post("/setvertexproperties/", response_model=None) -async def fastapi_set_vertex_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setvertexproperties/", response_model=None, summary="设置图形元素属性", description="更新指定图形元素的属性") +async def fastapi_set_vertex_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置图形元素属性。 + + 更新指定图形元素的属性值。 + """ props = await req.json() return set_vertex(network, ChangeSet(props)) -@router.post("/addvertex/", response_model=None) -async def fastapi_add_vertex(network: str, req: Request) -> ChangeSet: +@router.post("/addvertex/", response_model=None, summary="添加图形元素", description="在网络中添加一个新的图形元素") +async def fastapi_add_vertex( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加图形元素。 + + 在指定网络中创建一个新的图形元素,并设置其初始属性。 + """ props = await req.json() return add_vertex(network, ChangeSet(props)) -@router.post("/deletevertex/", response_model=None) -async def fastapi_delete_vertex(network: str, req: Request) -> ChangeSet: +@router.post("/deletevertex/", response_model=None, summary="删除图形元素", description="从网络中删除指定的图形元素") +async def fastapi_delete_vertex( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """删除图形元素。 + + 从指定网络中删除指定的图形元素及其相关数据。 + """ props = await req.json() return delete_vertex(network, ChangeSet(props)) -@router.get("/getallvertexlinks/", response_class=PlainTextResponse) -async def fastapi_get_all_vertex_links(network: str) -> list[str]: +@router.get("/getallvertexlinks/", response_class=PlainTextResponse, summary="获取所有图形元素链接", description="获取网络中的所有图形元素链接列表") +async def fastapi_get_all_vertex_links(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]: + """获取所有图形元素链接。 + + 返回指定网络中的所有图形元素链接列表。 + """ return json.dumps(get_all_vertex_links(network)) -@router.get("/getallvertices/", response_class=PlainTextResponse) -async def fastapi_get_all_vertices(network: str) -> list[dict[str, Any]]: +@router.get("/getallvertices/", response_class=PlainTextResponse, summary="获取所有图形元素", description="获取网络中的所有图形元素详细信息") +async def fastapi_get_all_vertices(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[str, Any]]: + """获取所有图形元素。 + + 返回指定网络中的所有图形元素详细信息。 + """ return json.dumps(get_all_vertices(network)) -@router.get("/getlabelschema/") -async def fastapi_get_label_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getlabelschema/", summary="获取标签架构", description="获取网络中标签对象的架构定义") +async def fastapi_get_label_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取标签架构。 + + 返回指定网络中标签对象的属性架构定义。 + """ return get_label_schema(network) -@router.get("/getlabelproperties/") +@router.get("/getlabelproperties/", summary="获取标签属性", description="获取指定坐标处的标签属性信息") async def fastapi_get_label_properties( - network: str, x: float, y: float + network: str = Query(..., description="管网名称(或数据库名称)"), + x: float = Query(..., description="X坐标"), + y: float = Query(..., description="Y坐标") ) -> dict[str, Any]: + """获取标签属性。 + + 返回指定坐标处的标签属性信息。 + """ return get_label(network, x, y) -@router.post("/setlabelproperties/", response_model=None) -async def fastapi_set_label_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setlabelproperties/", response_model=None, summary="设置标签属性", description="更新指定标签的属性") +async def fastapi_set_label_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置标签属性。 + + 更新指定标签的属性值。 + """ props = await req.json() return set_label(network, ChangeSet(props)) -@router.post("/addlabel/", response_model=None) -async def fastapi_add_label(network: str, req: Request) -> ChangeSet: +@router.post("/addlabel/", response_model=None, summary="添加标签", description="在网络中添加一个新的标签") +async def fastapi_add_label( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加标签。 + + 在指定网络中创建一个新的标签,并设置其初始属性。 + """ props = await req.json() return add_label(network, ChangeSet(props)) -@router.post("/deletelabel/", response_model=None) -async def fastapi_delete_label(network: str, req: Request) -> ChangeSet: +@router.post("/deletelabel/", response_model=None, summary="删除标签", description="从网络中删除指定的标签") +async def fastapi_delete_label( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """删除标签。 + + 从指定网络中删除指定的标签及其相关数据。 + """ props = await req.json() return delete_label(network, ChangeSet(props)) -@router.get("/getbackdropschema/") -async def fastapi_get_backdrop_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getbackdropschema/", summary="获取背景架构", description="获取网络中背景对象的架构定义") +async def fastapi_get_backdrop_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """获取背景架构。 + + 返回指定网络中背景对象的属性架构定义。 + """ return get_backdrop_schema(network) -@router.get("/getbackdropproperties/") -async def fastapi_get_backdrop_properties(network: str) -> dict[str, Any]: +@router.get("/getbackdropproperties/", summary="获取背景属性", description="获取指定网络的背景属性信息") +async def fastapi_get_backdrop_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取背景属性。 + + 返回指定网络的背景属性信息。 + """ return get_backdrop(network) -@router.post("/setbackdropproperties/", response_model=None) -async def fastapi_set_backdrop_properties(network: str, req: Request) -> ChangeSet: +@router.post("/setbackdropproperties/", response_model=None, summary="设置背景属性", description="更新指定网络的背景属性") +async def fastapi_set_backdrop_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置背景属性。 + + 更新指定网络的背景属性值。 + """ props = await req.json() return set_backdrop(network, ChangeSet(props)) diff --git a/app/api/v1/endpoints/extension.py b/app/api/v1/endpoints/extension.py index 5939f61..245da56 100644 --- a/app/api/v1/endpoints/extension.py +++ b/app/api/v1/endpoints/extension.py @@ -1,5 +1,5 @@ from typing import List, Any -from fastapi import APIRouter, Request, HTTPException +from fastapi import APIRouter, Request, HTTPException, Query, Body from app.services.tjnetwork import ( ChangeSet, get_all_extension_data_keys, @@ -10,20 +10,93 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getallextensiondatakeys/") -async def get_all_extension_data_keys_endpoint(network: str) -> list[str]: +@router.get( + "/getallextensiondatakeys/", + summary="获取所有扩展数据键", + description="获取指定网络的所有扩展数据的键列表" +) +async def get_all_extension_data_keys_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[str]: + """ + 获取所有扩展数据键。 + + 返回指定网络中所有可用的扩展数据键。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 扩展数据键列表 + """ return get_all_extension_data_keys(network) -@router.get("/getallextensiondata/") -async def get_all_extension_data_endpoint(network: str) -> dict[str, Any]: +@router.get( + "/getallextensiondata/", + summary="获取所有扩展数据", + description="获取指定网络的所有扩展数据" +) +async def get_all_extension_data_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, Any]: + """ + 获取所有扩展数据。 + + 返回指定网络的所有扩展数据及其值。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 扩展数据字典 + """ return get_all_extension_data(network) -@router.get("/getextensiondata/") -async def get_extension_data_endpoint(network: str, key: str) -> str | None: +@router.get( + "/getextensiondata/", + summary="获取指定扩展数据", + description="获取指定网络中指定键的扩展数据值" +) +async def get_extension_data_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + key: str = Query(..., description="扩展数据键") +) -> str | None: + """ + 获取指定扩展数据。 + + 返回指定网络中指定键对应的扩展数据值。 + + Args: + network: 管网名称(或数据库名称) + key: 扩展数据键 + + Returns: + 扩展数据值,如果不存在返回None + """ return get_extension_data(network, key) -@router.post("/setextensiondata/", response_model=None) -async def set_extension_data_endpoint(network: str, req: Request) -> ChangeSet: +@router.post( + "/setextensiondata/", + response_model=None, + summary="设置扩展数据", + description="设置指定网络中的扩展数据" +) +async def set_extension_data_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(..., description="扩展数据请求体") +) -> ChangeSet: + """ + 设置扩展数据。 + + 在指定网络中设置扩展数据,并返回变更集信息。 + + Args: + network: 管网名称(或数据库名称) + req: 包含扩展数据的请求体 + + Returns: + 变更集信息 + """ props = await req.json() print(props) cs = set_extension_data(network, ChangeSet(props)) diff --git a/app/api/v1/endpoints/leakage.py b/app/api/v1/endpoints/leakage.py index 8277745..0261e26 100644 --- a/app/api/v1/endpoints/leakage.py +++ b/app/api/v1/endpoints/leakage.py @@ -2,8 +2,8 @@ import os from typing import Any from datetime import datetime -from fastapi import APIRouter, Depends, HTTPException -from pydantic import BaseModel +from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body +from pydantic import BaseModel, Field from app.auth.keycloak_dependencies import get_current_keycloak_username from app.services.leakage_identifier import ( @@ -17,50 +17,114 @@ DEFAULT_N_WORKERS = max(1, min((os.cpu_count() or 1) - 1, 4)) class LeakageIdentifyRequest(BaseModel): - network: str - observed_pressure_data: str | dict[str, list[Any]] | list[dict[str, Any]] | None = ( - None + """漏损识别请求模型""" + network: str = Field(..., description="管网名称(或数据库名称)") + observed_pressure_data: str | dict[str, list[Any]] | list[dict[str, Any]] | None = Field( + None, description="观测的压力数据" ) - start_time: float = 0 - duration: float = 24 - timestep: float = 5 - q_sum: float = 0.2 - q_sum_unit: str = "m3/s" - output_dir: str = "db_inp" - pop_size: int = 50 - max_gen: int = 100 - n_workers: int = DEFAULT_N_WORKERS - output_flow_unit: str = "m3/s" - dma_count: int | None = None - scada_start: datetime | None = None - scada_end: datetime | None = None - sensor_nodes: list[str] | None = None - scheme_name: str | None = None + 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)") + 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="工作线程数") + output_flow_unit: str = Field("m3/s", description="输出流量单位") + dma_count: int | None = Field(None, 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="传感器节点列表") + scheme_name: str | None = Field(None, description="方案名称") -@router.post("/identify/") +@router.post( + "/identify/", + summary="执行漏损识别", + description="基于压力观测数据和遗传算法识别管网中的漏损位置和大小" +) async def identify_leakage( - data: LeakageIdentifyRequest, + data: LeakageIdentifyRequest = Body(..., description="漏损识别请求数据"), username: str = Depends(get_current_keycloak_username), ) -> dict[str, Any]: + """ + 执行漏损识别分析。 + + 使用遗传算法对比模型计算和实测压力数据, + 识别管网中的漏损节点和漏水量。 + + Args: + data: 包含管网名称(或数据库名称)、压力数据及优化参数的请求体 + username: 当前认证用户名 + + Returns: + 包含识别结果的字典 + + Raises: + HTTPException: 当处理过程中发生错误时 + """ try: return run_leakage_identification(**data.model_dump(), username=username) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) -@router.get("/schemes/") +@router.get( + "/schemes/", + summary="查询漏损识别方案列表", + description="获取指定网络的所有漏损识别方案" +) async def query_leakage_schemes( - network: str, query_date: datetime | None = None + network: str = Query(..., description="管网名称(或数据库名称)"), + query_date: datetime | None = Query(None, description="查询日期(可选)") ) -> list[dict[str, Any]]: + """ + 获取漏损识别方案列表。 + + 查询指定网络的所有已配置的漏损识别方案, + 可按日期进行筛选。 + + Args: + network: 管网名称(或数据库名称) + query_date: 查询日期(可选) + + Returns: + 漏损识别方案列表 + + Raises: + HTTPException: 当查询失败时 + """ try: return list_leakage_identify_schemes(network=network, query_date=query_date) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) -@router.get("/schemes/{scheme_name}") -async def query_leakage_scheme_detail(network: str, scheme_name: str) -> dict[str, Any]: +@router.get( + "/schemes/{scheme_name}", + summary="获取漏损识别方案详情", + description="获取指定漏损识别方案的详细信息" +) +async def query_leakage_scheme_detail( + network: str = Query(..., description="管网名称(或数据库名称)"), + scheme_name: str = Path(..., description="漏损识别方案名称") +) -> dict[str, Any]: + """ + 获取漏损识别方案详情。 + + 查询指定漏损识别方案的完整配置和参数信息。 + + Args: + network: 管网名称(或数据库名称) + scheme_name: 漏损识别方案名称 + + Returns: + 包含方案详情的字典 + + Raises: + HTTPException: 当查询失败时 + """ try: return get_leakage_identify_scheme_detail( network=network, scheme_name=scheme_name diff --git a/app/api/v1/endpoints/meta.py b/app/api/v1/endpoints/meta.py index 1e86a44..1e46710 100644 --- a/app/api/v1/endpoints/meta.py +++ b/app/api/v1/endpoints/meta.py @@ -1,5 +1,5 @@ import logging -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends, HTTPException, status, Query, Path from psycopg import AsyncConnection from sqlalchemy import text from sqlalchemy.exc import SQLAlchemyError @@ -25,11 +25,16 @@ router = APIRouter() logger = logging.getLogger(__name__) -@router.get("/meta/project", response_model=ProjectMetaResponse) +@router.get("/meta/project", summary="获取项目元数据", description="获取当前项目的元数据和配置信息", response_model=ProjectMetaResponse) async def get_project_metadata( ctx: ProjectContext = Depends(get_project_context), metadata_repo: MetadataRepository = Depends(get_metadata_repository), ): + """ + 获取项目元数据 + + 返回当前项目的完整元数据,包括项目基本信息和GeoServer配置 + """ project = await metadata_repo.get_project_by_id(ctx.project_id) if not project: raise HTTPException( @@ -59,11 +64,16 @@ async def get_project_metadata( ) -@router.get("/meta/projects", response_model=list[ProjectSummaryResponse]) +@router.get("/meta/projects", summary="列出用户项目", description="获取当前用户有权限的所有项目列表", response_model=list[ProjectSummaryResponse]) async def list_user_projects( current_user=Depends(get_current_metadata_user), metadata_repo: MetadataRepository = Depends(get_metadata_repository), ): + """ + 列出用户的所有项目 + + 返回当前用户有权限访问的项目摘要列表 + """ try: projects = await metadata_repo.list_projects_for_user(current_user.id) except SQLAlchemyError as exc: @@ -90,11 +100,16 @@ async def list_user_projects( ] -@router.get("/meta/db/health") +@router.get("/meta/db/health", summary="检查数据库健康状态", description="检查项目数据库连接的健康状况") async def project_db_health( pg_session: AsyncSession = Depends(get_project_pg_session), ts_conn: AsyncConnection = Depends(get_project_timescale_connection), ): + """ + 检查数据库健康状态 + + 检查PostgreSQL和TimescaleDB数据库的连接状态 + """ await pg_session.execute(text("SELECT 1")) async with ts_conn.cursor() as cur: await cur.execute("SELECT 1") diff --git a/app/api/v1/endpoints/misc.py b/app/api/v1/endpoints/misc.py index d040df9..1ebb083 100644 --- a/app/api/v1/endpoints/misc.py +++ b/app/api/v1/endpoints/misc.py @@ -1,6 +1,6 @@ from typing import Any import random -from fastapi import APIRouter +from fastapi import APIRouter, Query from fastapi.responses import JSONResponse from fastapi import status from pydantic import BaseModel @@ -12,8 +12,13 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getjson/") +@router.get("/getjson/", summary="获取JSON示例", description="获取JSON格式响应示例") async def fastapi_get_json(): + """ + 获取JSON示例 + + 返回示例JSON格式的响应 + """ return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content={ @@ -24,32 +29,58 @@ async def fastapi_get_json(): ) -@router.get("/getallsensorplacements/") -async def fastapi_get_all_sensor_placements(network: str) -> list[dict[Any, Any]]: +@router.get("/getallsensorplacements/", summary="获取所有传感器位置", description="获取网络中所有传感器的放置位置信息") +async def fastapi_get_all_sensor_placements(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]: + """ + 获取所有传感器位置 + + 返回网络中所有传感器的放置位置及其配置信息 + """ return get_all_sensor_placements(network) -@router.get("/getallburstlocateresults/") -async def fastapi_get_all_burst_locate_results(network: str) -> list[dict[Any, Any]]: +@router.get("/getallburstlocateresults/", summary="获取所有爆管定位结果", description="获取网络中所有爆管定位的分析结果") +async def fastapi_get_all_burst_locate_results(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]: + """ + 获取所有爆管定位结果 + + 返回网络中所有的爆管定位分析结果 + """ return get_all_burst_locate_results(network) class Item(BaseModel): + """测试数据模型""" str_info: str -@router.post("/test_dict/") +@router.post("/test_dict/", summary="测试字典处理", description="测试处理字典类型数据") async def fastapi_test_dict(data: Item) -> dict[str, str]: + """ + 测试字典处理 + + 接收Item模型,返回其字典格式 + """ item = data.dict() return item -@router.get("/getrealtimedata/") +@router.get("/getrealtimedata/", summary="获取实时数据", description="获取实时监测数据") async def fastapi_get_realtimedata(): + """ + 获取实时数据 + + 返回随机生成的实时监测数据示例 + """ data = [random.randint(0, 100) for _ in range(100)] return data -@router.get("/getsimulationresult/") +@router.get("/getsimulationresult/", summary="获取模拟结果", description="获取仿真计算结果") async def fastapi_get_simulationresult(): + """ + 获取仿真结果 + + 返回随机生成的仿真计算结果示例 + """ data = [random.randint(0, 100) for _ in range(100)] return data diff --git a/app/api/v1/endpoints/network/demands.py b/app/api/v1/endpoints/network/demands.py index 747788f..6094896 100644 --- a/app/api/v1/endpoints/network/demands.py +++ b/app/api/v1/endpoints/network/demands.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -17,21 +17,54 @@ router = APIRouter() # demand 9.[DEMANDS] ############################################################ -@router.get("/getdemandschema") -async def fastapi_get_demand_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/getdemandschema", + summary="获取需水量属性架构", + description="获取指定水网中需水量(Demand)的属性架构定义" +) +async def fastapi_get_demand_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """ + 获取需水量属性架构。 + + 返回指定水网的需水量属性架构,包括所有可配置的属性及其类型定义。 + """ return get_demand_schema(network) -@router.get("/getdemandproperties/") -async def fastapi_get_demand_properties(network: str, junction: str) -> dict[str, Any]: +@router.get( + "/getdemandproperties/", + summary="获取需水量属性", + description="获取指定水网中节点的需水量属性信息" +) +async def fastapi_get_demand_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点ID") +) -> dict[str, Any]: + """ + 获取节点的需水量属性。 + + 返回指定节点的所有需水量信息,包括需水量值、水压等级等。 + """ return get_demand(network, junction) # example: set_demand(p, ChangeSet({'junction': 'j1', 'demands': [{'demand': 10.0, 'pattern': None, 'category': 'x'}, {'demand': 20.0, 'pattern': None, 'category': None}]})) -@router.post("/setdemandproperties/", response_model=None) +@router.post( + "/setdemandproperties/", + response_model=None, + summary="设置需水量属性", + description="设置指定水网中节点的需水量属性信息" +) async def fastapi_set_demand_properties( - network: str, junction: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点ID"), + req: Request = Body(...) ) -> ChangeSet: + """ + 设置节点的需水量属性。 + + 修改指定节点的需水量信息。请求体应包含需水量值、水压等级等属性。 + """ props = await req.json() ps = {"junction": junction} | props return set_demand(network, ChangeSet(ps)) @@ -39,26 +72,68 @@ async def fastapi_set_demand_properties( ############################################################ # water distribution 36.[Water Distribution] ############################################################ -@router.get("/calculatedemandtonodes/") +@router.get( + "/calculatedemandtonodes/", + summary="计算需水量到节点分配", + description="将总需水量按指定方式分配到多个节点" +) async def fastapi_calculate_demand_to_nodes( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> dict[str, float]: + """ + 计算需水量到节点分配。 + + 将指定的总需水量均匀或按比例分配到指定的节点列表中。 + + 请求体格式: + { + "demand": 需水量值(float), + "nodes": 节点ID列表(list[str]) + } + """ props = await req.json() demand = props["demand"] nodes = props["nodes"] return calculate_demand_to_nodes(network, demand, nodes) -@router.get("/calculatedemandtoregion/") +@router.get( + "/calculatedemandtoregion/", + summary="计算需水量到区域分配", + description="将总需水量按区域特征分配到该区域内的节点" +) async def fastapi_calculate_demand_to_region( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> dict[str, float]: + """ + 计算需水量到区域分配。 + + 根据区域内节点的特征(如面积、人口等)将总需水量分配到该区域的各个节点。 + + 请求体格式: + { + "demand": 需水量值(float), + "region": 区域ID(str) + } + """ props = await req.json() demand = props["demand"] region = props["region"] return calculate_demand_to_region(network, demand, region) -@router.get("/calculatedemandtonetwork/") +@router.get( + "/calculatedemandtonetwork/", + summary="计算需水量到整网分配", + description="将需水量均匀分配到整个水网的所有需水节点" +) async def fastapi_calculate_demand_to_network( - network: str, demand: float + network: str = Query(..., description="管网名称(或数据库名称)"), + demand: float = Query(..., description="总需水量(m³/h)", gt=0) ) -> dict[str, float]: + """ + 计算需水量到整网分配。 + + 将指定的需水量均匀分配到整个水网的所有需水节点。 + """ return calculate_demand_to_network(network, demand) diff --git a/app/api/v1/endpoints/network/general.py b/app/api/v1/endpoints/network/general.py index bfa0bd2..f80c7ed 100644 --- a/app/api/v1/endpoints/network/general.py +++ b/app/api/v1/endpoints/network/general.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -44,110 +44,291 @@ router = APIRouter() # type ############################################################ -@router.get("/isnode/") -async def fastapi_is_node(network: str, node: str) -> bool: +@router.get( + "/isnode/", + summary="检查节点有效性", + description="检查指定ID是否为水网中的有效节点" +) +async def fastapi_is_node( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> bool: + """检查指定ID是否为节点。""" return is_node(network, node) -@router.get("/isjunction/") -async def fastapi_is_junction(network: str, node: str) -> bool: +@router.get( + "/isjunction/", + summary="检查是否为接点", + description="检查指定ID是否为水网中的接点(需求点)" +) +async def fastapi_is_junction( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> bool: + """检查指定ID是否为接点。""" return is_junction(network, node) -@router.get("/isreservoir/") -async def fastapi_is_reservoir(network: str, node: str) -> bool: +@router.get( + "/isreservoir/", + summary="检查是否为水源", + description="检查指定ID是否为水网中的水源(水库/河流)" +) +async def fastapi_is_reservoir( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> bool: + """检查指定ID是否为水源。""" return is_reservoir(network, node) -@router.get("/istank/") -async def fastapi_is_tank(network: str, node: str) -> bool: +@router.get( + "/istank/", + summary="检查是否为蓄水池", + description="检查指定ID是否为水网中的蓄水池" +) +async def fastapi_is_tank( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> bool: + """检查指定ID是否为蓄水池。""" return is_tank(network, node) -@router.get("/islink/") -async def fastapi_is_link(network: str, link: str) -> bool: +@router.get( + "/islink/", + summary="检查管线有效性", + description="检查指定ID是否为水网中的有效管线" +) +async def fastapi_is_link( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> bool: + """检查指定ID是否为管线。""" return is_link(network, link) -@router.get("/ispipe/") -async def fastapi_is_pipe(network: str, link: str) -> bool: +@router.get( + "/ispipe/", + summary="检查是否为管道", + description="检查指定ID是否为水网中的管道" +) +async def fastapi_is_pipe( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> bool: + """检查指定ID是否为管道。""" return is_pipe(network, link) -@router.get("/ispump/") -async def fastapi_is_pump(network: str, link: str) -> bool: +@router.get( + "/ispump/", + summary="检查是否为泵", + description="检查指定ID是否为水网中的泵" +) +async def fastapi_is_pump( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> bool: + """检查指定ID是否为泵。""" return is_pump(network, link) -@router.get("/isvalve/") -async def fastapi_is_valve(network: str, link: str) -> bool: +@router.get( + "/isvalve/", + summary="检查是否为阀门", + description="检查指定ID是否为水网中的阀门" +) +async def fastapi_is_valve( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> bool: + """检查指定ID是否为阀门。""" return is_valve(network, link) -@router.get("/getnodetype/") -async def fastapi_get_node_type(network: str, node: str) -> str: +@router.get( + "/getnodetype/", + summary="获取节点类型", + description="获取指定节点的类型(接点/水源/蓄水池)" +) +async def fastapi_get_node_type( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> str: + """获取节点的类型标识。""" return get_node_type(network, node) -@router.get("/getlinktype/") -async def fastapi_get_link_type(network: str, link: str) -> str: +@router.get( + "/getlinktype/", + summary="获取管线类型", + description="获取指定管线的类型(管道/泵/阀门)" +) +async def fastapi_get_link_type( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> str: + """获取管线的类型标识。""" return get_link_type(network, link) -@router.get("/getelementtype/") -async def fastapi_get_element_type(network: str, element: str) -> str: +@router.get( + "/getelementtype/", + summary="获取元素类型", + description="获取指定元素的类型(节点或管线)" +) +async def fastapi_get_element_type( + network: str = Query(..., description="管网名称(或数据库名称)"), + element: str = Query(..., description="元素ID") +) -> str: + """获取元素的类型标识。""" return get_element_type(network, element) -@router.get("/getelementtypevalue/") -async def fastapi_get_element_type_value(network: str, element: str) -> int: +@router.get( + "/getelementtypevalue/", + summary="获取元素类型值", + description="获取指定元素的类型数值标识" +) +async def fastapi_get_element_type_value( + network: str = Query(..., description="管网名称(或数据库名称)"), + element: str = Query(..., description="元素ID") +) -> int: + """获取元素的类型数值。""" return get_element_type_value(network, element) -@router.get("/getnodes/") -async def fastapi_get_nodes(network: str) -> list[str]: +@router.get( + "/getnodes/", + summary="获取所有节点", + description="获取指定水网中的所有节点ID列表" +) +async def fastapi_get_nodes(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]: + """获取水网中所有节点的ID列表。""" return get_nodes(network) -@router.get("/getlinks/") -async def fastapi_get_links(network: str) -> list[str]: +@router.get( + "/getlinks/", + summary="获取所有管线", + description="获取指定水网中的所有管线ID列表" +) +async def fastapi_get_links(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]: + """获取水网中所有管线的ID列表。""" return get_links(network) -@router.get("/getnodelinks/") -def get_node_links_endpoint(network: str, node: str) -> list[str]: +@router.get( + "/getnodelinks/", + summary="获取节点的关联管线", + description="获取指定节点连接的所有管线ID列表" +) +def get_node_links_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> list[str]: + """获取节点关联的所有管线。""" return get_node_links(network, node) ############################################################ # Node & Link properties ############################################################ -@router.get("/getnodeproperties/") -async def fast_get_node_properties(network: str, node: str) -> dict[str, Any]: +@router.get( + "/getnodeproperties/", + summary="获取节点属性", + description="获取指定节点的所有属性信息" +) +async def fast_get_node_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> dict[str, Any]: + """获取节点的完整属性信息。""" return get_node_properties(network, node) -@router.get("/getlinkproperties/") -async def fast_get_link_properties(network: str, link: str) -> dict[str, Any]: +@router.get( + "/getlinkproperties/", + summary="获取管线属性", + description="获取指定管线的所有属性信息" +) +async def fast_get_link_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> dict[str, Any]: + """获取管线的完整属性信息。""" return get_link_properties(network, link) -@router.get("/getscadaproperties/") -async def fast_get_scada_properties(network: str, scada: str) -> dict[str, Any]: +@router.get( + "/getscadaproperties/", + summary="获取SCADA点属性", + description="获取指定SCADA点的属性信息" +) +async def fast_get_scada_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + scada: str = Query(..., description="SCADA点ID") +) -> dict[str, Any]: + """获取SCADA点的属性信息。""" return get_scada_info(network, scada) -@router.get("/getallscadaproperties/") -async def fast_get_all_scada_properties(network: str) -> list[dict[str, Any]]: +@router.get( + "/getallscadaproperties/", + summary="获取所有SCADA点属性", + description="获取指定水网中所有SCADA点的属性信息" +) +async def fast_get_all_scada_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """获取水网中所有SCADA点的属性列表。""" return get_all_scada_info(network) -@router.get("/getelementpropertieswithtype/") +@router.get( + "/getelementpropertieswithtype/", + summary="获取指定类型元素属性", + description="获取指定类型的元素属性信息" +) async def fast_get_element_properties_with_type( - network: str, elementtype: str, element: str + network: str = Query(..., description="管网名称(或数据库名称)"), + elementtype: str = Query(..., description="元素类型"), + element: str = Query(..., description="元素ID") ) -> dict[str, Any]: + """获取指定类型元素的属性。""" return get_element_properties_with_type(network, elementtype, element) -@router.get("/getelementproperties/") -async def fast_get_element_properties(network: str, element: str) -> dict[str, Any]: +@router.get( + "/getelementproperties/", + summary="获取元素属性", + description="获取指定元素的属性信息" +) +async def fast_get_element_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + element: str = Query(..., description="元素ID") +) -> dict[str, Any]: + """获取元素的完整属性信息。""" return get_element_properties(network, element) ############################################################ # title 1.[TITLE] ############################################################ -@router.get("/gettitleschema/") -async def fast_get_title_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/gettitleschema/", + summary="获取标题属性架构", + description="获取指定水网的标题(标题)属性架构定义" +) +async def fast_get_title_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """获取水网标题的属性架构。""" return get_title_schema(network) -@router.get("/gettitle/") -async def fast_get_title(network: str) -> dict[str, Any]: +@router.get( + "/gettitle/", + summary="获取水网标题属性", + description="获取指定水网的标题(Title)信息" +) +async def fast_get_title(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """获取水网的标题属性。""" return get_title(network) -@router.get("/settitle/", response_model=None) -async def fastapi_set_title(network: str, req: Request) -> ChangeSet: +@router.get( + "/settitle/", + response_model=None, + summary="设置水网标题属性", + description="设置指定水网的标题(Title)信息" +) +async def fastapi_set_title( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置水网的标题属性。""" props = await req.json() return set_title(network, ChangeSet(props)) @@ -155,18 +336,41 @@ async def fastapi_set_title(network: str, req: Request) -> ChangeSet: # status 10.[STATUS] ############################################################ -@router.get("/getstatusschema") -async def fastapi_get_status_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/getstatusschema", + summary="获取状态属性架构", + description="获取指定水网的状态(Status)属性架构定义" +) +async def fastapi_get_status_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """获取水网状态的属性架构。""" return get_status_schema(network) -@router.get("/getstatus/") -async def fastapi_get_status(network: str, link: str) -> dict[str, Any]: +@router.get( + "/getstatus/", + summary="获取管线状态", + description="获取指定管线的状态信息" +) +async def fastapi_get_status( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> dict[str, Any]: + """获取管线的状态属性。""" return get_status(network, link) -@router.post("/setstatus/", response_model=None) +@router.post( + "/setstatus/", + response_model=None, + summary="设置管线状态", + description="设置指定管线的状态信息" +) async def fastapi_set_status_properties( - network: str, link: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID"), + req: Request = Body(...) ) -> ChangeSet: + """设置管线的状态属性。""" props = await req.json() ps = {"link": link} | props return set_status(network, ChangeSet(ps)) @@ -175,8 +379,17 @@ async def fastapi_set_status_properties( # General Deletion ############################################################ -@router.post("/deletenode/", response_model=None) -async def fastapi_delete_node(network: str, node: str) -> ChangeSet: +@router.post( + "/deletenode/", + response_model=None, + summary="删除节点", + description="删除指定的节点(接点/水源/蓄水池)" +) +async def fastapi_delete_node( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> ChangeSet: + """删除指定的节点。自动识别节点类型并调用相应的删除操作。""" ps = {"id": node} if is_junction(network, node): return delete_junction(network, ChangeSet(ps)) @@ -186,8 +399,17 @@ async def fastapi_delete_node(network: str, node: str) -> ChangeSet: return delete_tank(network, ChangeSet(ps)) return ChangeSet() # Should probably raise error or return empty -@router.post("/deletelink/", response_model=None) -async def fastapi_delete_link(network: str, link: str) -> ChangeSet: +@router.post( + "/deletelink/", + response_model=None, + summary="删除管线", + description="删除指定的管线(管道/泵/阀门)" +) +async def fastapi_delete_link( + network: str = Query(..., description="管网名称(或数据库名称)"), + link: str = Query(..., description="管线ID") +) -> ChangeSet: + """删除指定的管线。自动识别管线类型并调用相应的删除操作。""" ps = {"id": link} if is_pipe(network, link): return delete_pipe(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/network/geometry.py b/app/api/v1/endpoints/network/geometry.py index 9b4e555..8a99743 100644 --- a/app/api/v1/endpoints/network/geometry.py +++ b/app/api/v1/endpoints/network/geometry.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request, Depends +from fastapi import APIRouter, Request, Depends, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -34,19 +34,44 @@ router = APIRouter() # props = await req.json() # return set_coord(network, ChangeSet(props)) -@router.get("/getnodecoord/") -async def fastapi_get_node_coord(network: str, node: str) -> dict[str, float] | None: +@router.get( + "/getnodecoord/", + summary="获取节点坐标", + description="获取指定节点的地理坐标(X, Y)" +) +async def fastapi_get_node_coord( + network: str = Query(..., description="管网名称(或数据库名称)"), + node: str = Query(..., description="节点ID") +) -> dict[str, float] | None: + """获取节点的地理坐标信息。""" return get_node_coord(network, node) # Additional geometry queries found in main.py logic (implicit or explicit) -@router.get("/getnetworkinextent/") +@router.get( + "/getnetworkinextent/", + summary="获取范围内的网络元素", + description="获取指定地理范围内的网络节点和管线" +) async def fastapi_get_network_in_extent( - network: str, x1: float, y1: float, x2: float, y2: float + network: str = Query(..., description="管网名称(或数据库名称)"), + x1: float = Query(..., description="范围左下角X坐标", alias="x1"), + y1: float = Query(..., description="范围左下角Y坐标", alias="y1"), + x2: float = Query(..., description="范围右上角X坐标", alias="x2"), + y2: float = Query(..., description="范围右上角Y坐标", alias="y2") ) -> dict[str, Any]: + """获取地理范围内的网络几何信息。""" return get_network_in_extent(network, x1, y1, x2, y2) -@router.get("/getnetworkgeometries/", dependencies=[Depends(verify_token)]) -async def fastapi_get_network_geometries(network: str) -> dict[str, Any] | None: +@router.get( + "/getnetworkgeometries/", + dependencies=[Depends(verify_token)], + summary="获取完整网络几何信息", + description="获取整个水网的所有节点、管线和SCADA点的几何信息(需要身份验证)" +) +async def fastapi_get_network_geometries( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, Any] | None: + """获取完整的网络几何信息,包括所有节点、管线和SCADA点。结果从缓存返回。""" cache_key = f"getnetworkgeometries_{network}" data = redis_client.get(cache_key) if data: @@ -64,18 +89,39 @@ async def fastapi_get_network_geometries(network: str) -> dict[str, Any] | None: redis_client.set(cache_key, msgpack.packb(results, default=encode_datetime)) return results -@router.get("/getmajornodecoords/") +@router.get( + "/getmajornodecoords/", + summary="获取主要节点坐标", + description="获取直径大于等于指定值的节点坐标" +) async def fastapi_get_majornode_coords( - network: str, diameter: int + network: str = Query(..., description="管网名称(或数据库名称)"), + diameter: int = Query(..., description="最小直径(mm)", gt=0) ) -> dict[str, dict[str, float]]: + """获取主要节点的坐标。只返回直径大于等于指定值的节点。""" return get_major_node_coords(network, diameter) -@router.get("/getmajorpipenodes/") -async def fastapi_get_major_pipe_nodes(network: str, diameter: int) -> list[str] | None: +@router.get( + "/getmajorpipenodes/", + summary="获取主要管道节点", + description="获取直径大于等于指定值的管道的节点ID" +) +async def fastapi_get_major_pipe_nodes( + network: str = Query(..., description="管网名称(或数据库名称)"), + diameter: int = Query(..., description="最小直径(mm)", gt=0) +) -> list[str] | None: + """获取主要管道节点。只返回直径大于等于指定值的管道。""" return get_major_pipe_nodes(network, diameter) -@router.get("/getnetworklinknodes/") -async def fastapi_get_network_link_nodes(network: str) -> list[str] | None: +@router.get( + "/getnetworklinknodes/", + summary="获取网络管线节点", + description="获取指定水网所有管线的起点和终点节点" +) +async def fastapi_get_network_link_nodes( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[str] | None: + """获取网络中所有管线的连接节点。""" return get_network_link_nodes(network) # @router.get("/getallcoords/") diff --git a/app/api/v1/endpoints/network/junctions.py b/app/api/v1/endpoints/network/junctions.py index c306099..306d9b0 100644 --- a/app/api/v1/endpoints/network/junctions.py +++ b/app/api/v1/endpoints/network/junctions.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -13,108 +13,349 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getjunctionschema") -async def fast_get_junction_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getjunctionschema", summary="获取节点架构", description="获取指定项目的节点属性架构和数据类型定义。") +async def fast_get_junction_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取节点架构信息。 + + 返回指定项目的节点属性架构,包括所有属性的类型和约束信息。 + + Args: + network: 管网名称(或数据库名称) + """ return get_junction_schema(network) -@router.post("/addjunction/", response_model=None) +@router.post("/addjunction/", response_model=None, summary="添加节点", description="在供水网络中添加新的节点,指定节点ID和空间坐标。") async def fastapi_add_junction( - network: str, junction: str, x: float, y: float, z: float + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + x: float = Query(..., description="X 坐标"), + y: float = Query(..., description="Y 坐标"), + z: float = Query(..., description="标高(海拔高度)") ) -> ChangeSet: + """ + 添加新节点到供水网络。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + x: X 坐标值 + y: Y 坐标值 + z: 标高(海拔高度) + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "x": x, "y": y, "elevation": z} return add_junction(network, ChangeSet(ps)) -@router.post("/deletejunction/", response_model=None) -async def fastapi_delete_junction(network: str, junction: str) -> ChangeSet: +@router.post("/deletejunction/", response_model=None, summary="删除节点", description="从供水网络中删除指定的节点。") +async def fastapi_delete_junction( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> ChangeSet: + """ + 删除指定的节点。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction} return delete_junction(network, ChangeSet(ps)) -@router.get("/getjunctionelevation/") -async def fastapi_get_junction_elevation(network: str, junction: str) -> float: +@router.get("/getjunctionelevation/", summary="获取节点标高", description="获取指定节点的标高(海拔高度)。") +async def fastapi_get_junction_elevation( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> float: + """ + 获取节点的标高值。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + float: 节点标高值 + """ ps = get_junction(network, junction) return ps["elevation"] -@router.get("/getjunctionx/") -async def fastapi_get_junction_x(network: str, junction: str) -> float: +@router.get("/getjunctionx/", summary="获取节点 X 坐标", description="获取指定节点的 X 坐标值。") +async def fastapi_get_junction_x( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> float: + """ + 获取节点的 X 坐标。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + float: 节点 X 坐标值 + """ ps = get_junction(network, junction) return ps["x"] -@router.get("/getjunctiony/") -async def fastapi_get_junction_y(network: str, junction: str) -> float: +@router.get("/getjunctiony/", summary="获取节点 Y 坐标", description="获取指定节点的 Y 坐标值。") +async def fastapi_get_junction_y( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> float: + """ + 获取节点的 Y 坐标。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + float: 节点 Y 坐标值 + """ ps = get_junction(network, junction) return ps["y"] -@router.get("/getjunctioncoord/") -async def fastapi_get_junction_coord(network: str, junction: str) -> dict[str, float]: +@router.get("/getjunctioncoord/", summary="获取节点坐标", description="获取指定节点的 X 和 Y 坐标。") +async def fastapi_get_junction_coord( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> dict[str, float]: + """ + 获取节点的坐标信息。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + dict: 包含 x 和 y 坐标的字典 + """ ps = get_junction(network, junction) coord = {"x": ps["x"], "y": ps["y"]} return coord -@router.get("/getjunctiondemand/") -async def fastapi_get_junction_demand(network: str, junction: str) -> float: +@router.get("/getjunctiondemand/", summary="获取节点需水量", description="获取指定节点的需水量。") +async def fastapi_get_junction_demand( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> float: + """ + 获取节点的需水量。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + float: 节点的需水量值 + """ ps = get_junction(network, junction) return ps["demand"] -@router.get("/getjunctionpattern/") -async def fastapi_get_junction_pattern(network: str, junction: str) -> str: +@router.get("/getjunctionpattern/", summary="获取节点需水模式", description="获取指定节点的需水模式标识。") +async def fastapi_get_junction_pattern( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") +) -> str: + """ + 获取节点的需水模式。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + str: 节点的需水模式标识 + """ ps = get_junction(network, junction) return ps["pattern"] -@router.post("/setjunctionelevation/", response_model=None) +@router.post("/setjunctionelevation/", response_model=None, summary="设置节点标高", description="设置指定节点的标高值。") async def fastapi_set_junction_elevation( - network: str, junction: str, elevation: float + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + elevation: float = Query(..., description="标高(海拔高度)") ) -> ChangeSet: + """ + 设置节点的标高。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + elevation: 标高值 + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "elevation": elevation} return set_junction(network, ChangeSet(ps)) -@router.post("/setjunctionx/", response_model=None) -async def fastapi_set_junction_x(network: str, junction: str, x: float) -> ChangeSet: +@router.post("/setjunctionx/", response_model=None, summary="设置节点 X 坐标", description="设置指定节点的 X 坐标值。") +async def fastapi_set_junction_x( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + x: float = Query(..., description="X 坐标值") +) -> ChangeSet: + """ + 设置节点的 X 坐标。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + x: X 坐标值 + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "x": x} return set_junction(network, ChangeSet(ps)) -@router.post("/setjunctiony/", response_model=None) -async def fastapi_set_junction_y(network: str, junction: str, y: float) -> ChangeSet: +@router.post("/setjunctiony/", response_model=None, summary="设置节点 Y 坐标", description="设置指定节点的 Y 坐标值。") +async def fastapi_set_junction_y( + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + y: float = Query(..., description="Y 坐标值") +) -> ChangeSet: + """ + 设置节点的 Y 坐标。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + y: Y 坐标值 + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "y": y} return set_junction(network, ChangeSet(ps)) -@router.post("/setjunctioncoord/", response_model=None) +@router.post("/setjunctioncoord/", response_model=None, summary="设置节点坐标", description="设置指定节点的 X 和 Y 坐标。") async def fastapi_set_junction_coord( - network: str, junction: str, x: float, y: float + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + x: float = Query(..., description="X 坐标值"), + y: float = Query(..., description="Y 坐标值") ) -> ChangeSet: + """ + 设置节点的坐标。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + x: X 坐标值 + y: Y 坐标值 + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "x": x, "y": y} return set_junction(network, ChangeSet(ps)) -@router.post("/setjunctiondemand/", response_model=None) +@router.post("/setjunctiondemand/", response_model=None, summary="设置节点需水量", description="设置指定节点的需水量。") async def fastapi_set_junction_demand( - network: str, junction: str, demand: float + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + demand: float = Query(..., description="需水量值") ) -> ChangeSet: + """ + 设置节点的需水量。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + demand: 需水量值 + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "demand": demand} return set_junction(network, ChangeSet(ps)) -@router.post("/setjunctionpattern/", response_model=None) +@router.post("/setjunctionpattern/", response_model=None, summary="设置节点需水模式", description="设置指定节点的需水模式标识。") async def fastapi_set_junction_pattern( - network: str, junction: str, pattern: str + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + pattern: str = Query(..., description="需水模式标识") ) -> ChangeSet: + """ + 设置节点的需水模式。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + pattern: 需水模式标识 + + Returns: + ChangeSet: 包含变更信息的结果 + """ ps = {"id": junction, "pattern": pattern} return set_junction(network, ChangeSet(ps)) -@router.get("/getjunctionproperties/") +@router.get("/getjunctionproperties/", summary="获取节点属性", description="获取指定节点的所有属性信息。") async def fastapi_get_junction_properties( - network: str, junction: str + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID") ) -> dict[str, Any]: + """ + 获取节点的完整属性信息。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + + Returns: + dict: 包含节点所有属性的字典 + """ return get_junction(network, junction) -@router.get("/getalljunctionproperties/") -async def fastapi_get_all_junction_properties(network: str) -> list[dict[str, Any]]: +@router.get("/getalljunctionproperties/", summary="获取所有节点属性", description="获取指定项目中所有节点的属性信息。") +async def fastapi_get_all_junction_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取所有节点的属性信息列表。 + + 此端点返回指定项目中所有节点的详细属性。缓存查询结果以提高性能。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + list: 包含所有节点属性的列表 + """ # 缓存查询结果提高性能 # global redis_client # Redis logic removed for clean split, can be re-added if needed or imported results = get_all_junctions(network) return results -@router.post("/setjunctionproperties/", response_model=None) +@router.post("/setjunctionproperties/", response_model=None, summary="批量设置节点属性", description="批量设置指定节点的多个属性。") async def fastapi_set_junction_properties( - network: str, junction: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + junction: str = Query(..., description="节点 ID"), + req: Request = Body(..., description="包含待设置属性的请求体") ) -> ChangeSet: + """ + 批量设置节点属性。 + + 允许一次性设置节点的多个属性,如坐标、标高、需水量等。 + + Args: + network: 管网名称(或数据库名称) + junction: 节点 ID + req: 包含属性和值的 JSON 请求体 + + Returns: + ChangeSet: 包含变更信息的结果 + """ props = await req.json() ps = {"id": junction} | props return set_junction(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/network/pipes.py b/app/api/v1/endpoints/network/pipes.py index ff01786..b182d83 100644 --- a/app/api/v1/endpoints/network/pipes.py +++ b/app/api/v1/endpoints/network/pipes.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -14,22 +14,50 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getpipeschema") -async def fastapi_get_pipe_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getpipeschema", summary="获取管道模式", description="获取管道对象的模式定义,包含所有可用字段及其类型") +async def fastapi_get_pipe_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取管道数据模式定义。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含管道模式信息的字典 + """ return get_pipe_schema(network) -@router.post("/addpipe/", response_model=None) +@router.post("/addpipe/", response_model=None, summary="添加管道", description="向网络中添加新的管道,需要提供管道的基本参数如长度、管径、粗糙度等") async def fastapi_add_pipe( - network: str, - pipe: str, - node1: str, - node2: str, - length: float = 0, - diameter: float = 0, - roughness: float = 0, - minor_loss: float = 0, - status: str = PIPE_STATUS_OPEN, + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道标识符"), + node1: str = Query(..., description="管道起始节点ID"), + node2: str = Query(..., description="管道终止节点ID"), + length: float = Query(0, description="管道长度(单位:米)"), + diameter: float = Query(0, description="管道管径(单位:毫米)"), + roughness: float = Query(0, description="管道粗糙度"), + minor_loss: float = Query(0, description="管道局部阻力系数"), + status: str = Query(PIPE_STATUS_OPEN, description="管道状态(开启/关闭)"), ) -> ChangeSet: + """ + 添加新管道到网络。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + node1: 起始节点ID + node2: 终止节点ID + length: 管道长度 + diameter: 管道管径 + roughness: 管道粗糙度 + minor_loss: 局部阻力系数 + status: 管道状态 + + Returns: + ChangeSet对象,包含本次操作的变更信息 + """ ps = { "id": pipe, "node1": node1, @@ -42,102 +70,342 @@ async def fastapi_add_pipe( } return add_pipe(network, ChangeSet(ps)) -@router.post("/deletepipe/", response_model=None) -async def fastapi_delete_pipe(network: str, pipe: str) -> ChangeSet: +@router.post("/deletepipe/", response_model=None, summary="删除管道", description="从网络中删除指定的管道") +async def fastapi_delete_pipe( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="要删除的管道ID") +) -> ChangeSet: + """ + 删除管道。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + ChangeSet对象,包含本次删除操作的变更信息 + """ ps = {"id": pipe} return delete_pipe(network, ChangeSet(ps)) -@router.get("/getpipenode1/") -async def fastapi_get_pipe_node1(network: str, pipe: str) -> str | None: +@router.get("/getpipenode1/", summary="获取管道起始节点", description="获取指定管道的起始节点ID") +async def fastapi_get_pipe_node1( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> str | None: + """ + 获取管道的起始节点。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 起始节点ID,如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["node1"] -@router.get("/getpipenode2/") -async def fastapi_get_pipe_node2(network: str, pipe: str) -> str | None: +@router.get("/getpipenode2/", summary="获取管道终止节点", description="获取指定管道的终止节点ID") +async def fastapi_get_pipe_node2( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> str | None: + """ + 获取管道的终止节点。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 终止节点ID,如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["node2"] -@router.get("/getpipelength/") -async def fastapi_get_pipe_length(network: str, pipe: str) -> float | None: +@router.get("/getpipelength/", summary="获取管道长度", description="获取指定管道的长度") +async def fastapi_get_pipe_length( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> float | None: + """ + 获取管道长度。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 管道长度(单位:米),如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["length"] -@router.get("/getpipediameter/") -async def fastapi_get_pipe_diameter(network: str, pipe: str) -> float | None: +@router.get("/getpipediameter/", summary="获取管道管径", description="获取指定管道的管径") +async def fastapi_get_pipe_diameter( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> float | None: + """ + 获取管道管径。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 管道管径(单位:毫米),如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["diameter"] -@router.get("/getpiperoughness/") -async def fastapi_get_pipe_roughness(network: str, pipe: str) -> float | None: +@router.get("/getpiperoughness/", summary="获取管道粗糙度", description="获取指定管道的粗糙度") +async def fastapi_get_pipe_roughness( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> float | None: + """ + 获取管道粗糙度。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 管道粗糙度值,如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["roughness"] -@router.get("/getpipeminorloss/") -async def fastapi_get_pipe_minor_loss(network: str, pipe: str) -> float | None: +@router.get("/getpipeminorloss/", summary="获取管道局部阻力系数", description="获取指定管道的局部阻力系数") +async def fastapi_get_pipe_minor_loss( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> float | None: + """ + 获取管道局部阻力系数。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 局部阻力系数,如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["minor_loss"] -@router.get("/getpipestatus/") -async def fastapi_get_pipe_status(network: str, pipe: str) -> str | None: +@router.get("/getpipestatus/", summary="获取管道状态", description="获取指定管道的状态(开启或关闭)") +async def fastapi_get_pipe_status( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> str | None: + """ + 获取管道状态。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 管道状态(开启/关闭),如果不存在则返回None + """ ps = get_pipe(network, pipe) return ps["status"] -@router.post("/setpipenode1/", response_model=None) -async def fastapi_set_pipe_node1(network: str, pipe: str, node1: str) -> ChangeSet: +@router.post("/setpipenode1/", response_model=None, summary="设置管道起始节点", description="设置指定管道的起始节点") +async def fastapi_set_pipe_node1( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + node1: str = Query(..., description="新的起始节点ID") +) -> ChangeSet: + """ + 设置管道起始节点。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + node1: 新的起始节点ID + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "node1": node1} return set_pipe(network, ChangeSet(ps)) -@router.post("/setpipenode2/", response_model=None) -async def fastapi_set_pipe_node2(network: str, pipe: str, node2: str) -> ChangeSet: +@router.post("/setpipenode2/", response_model=None, summary="设置管道终止节点", description="设置指定管道的终止节点") +async def fastapi_set_pipe_node2( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + node2: str = Query(..., description="新的终止节点ID") +) -> ChangeSet: + """ + 设置管道终止节点。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + node2: 新的终止节点ID + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "node2": node2} return set_pipe(network, ChangeSet(ps)) -@router.post("/setpipelength/", response_model=None) -async def fastapi_set_pipe_length(network: str, pipe: str, length: float) -> ChangeSet: +@router.post("/setpipelength/", response_model=None, summary="设置管道长度", description="设置指定管道的长度") +async def fastapi_set_pipe_length( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + length: float = Query(..., description="新的管道长度(单位:米)") +) -> ChangeSet: + """ + 设置管道长度。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + length: 新的管道长度 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "length": length} return set_pipe(network, ChangeSet(ps)) -@router.post("/setpipediameter/", response_model=None) +@router.post("/setpipediameter/", response_model=None, summary="设置管道管径", description="设置指定管道的管径") async def fastapi_set_pipe_diameter( - network: str, pipe: str, diameter: float + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + diameter: float = Query(..., description="新的管道管径(单位:毫米)") ) -> ChangeSet: + """ + 设置管道管径。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + diameter: 新的管道管径 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "diameter": diameter} return set_pipe(network, ChangeSet(ps)) -@router.post("/setpiperoughness/", response_model=None) +@router.post("/setpiperoughness/", response_model=None, summary="设置管道粗糙度", description="设置指定管道的粗糙度") async def fastapi_set_pipe_roughness( - network: str, pipe: str, roughness: float + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + roughness: float = Query(..., description="新的管道粗糙度值") ) -> ChangeSet: + """ + 设置管道粗糙度。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + roughness: 新的管道粗糙度 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "roughness": roughness} return set_pipe(network, ChangeSet(ps)) -@router.post("/setpipeminorloss/", response_model=None) +@router.post("/setpipeminorloss/", response_model=None, summary="设置管道局部阻力系数", description="设置指定管道的局部阻力系数") async def fastapi_set_pipe_minor_loss( - network: str, pipe: str, minor_loss: float + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + minor_loss: float = Query(..., description="新的局部阻力系数值") ) -> ChangeSet: + """ + 设置管道局部阻力系数。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + minor_loss: 新的局部阻力系数 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "minor_loss": minor_loss} return set_pipe(network, ChangeSet(ps)) -@router.post("/setpipestatus/", response_model=None) -async def fastapi_set_pipe_status(network: str, pipe: str, status: str) -> ChangeSet: +@router.post("/setpipestatus/", response_model=None, summary="设置管道状态", description="设置指定管道的状态(开启或关闭)") +async def fastapi_set_pipe_status( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + status: str = Query(..., description="新的管道状态(开启/关闭)") +) -> ChangeSet: + """ + 设置管道状态。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + status: 新的管道状态 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pipe, "status": status} return set_pipe(network, ChangeSet(ps)) -@router.get("/getpipeproperties/") -async def fastapi_get_pipe_properties(network: str, pipe: str) -> dict[str, Any]: +@router.get("/getpipeproperties/", summary="获取管道属性", description="获取指定管道的所有属性信息") +async def fastapi_get_pipe_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID") +) -> dict[str, Any]: + """ + 获取管道的所有属性。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + + Returns: + 包含管道所有属性的字典 + """ return get_pipe(network, pipe) -@router.get("/getallpipeproperties/") -async def fastapi_get_all_pipe_properties(network: str) -> list[dict[str, Any]]: +@router.get("/getallpipeproperties/", summary="获取所有管道属性", description="获取网络中所有管道的属性信息列表") +async def fastapi_get_all_pipe_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取网络中所有管道的属性。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含所有管道属性的字典列表 + """ # 缓存查询结果提高性能 # global redis_client results = get_all_pipes(network) return results -@router.post("/setpipeproperties/", response_model=None) +@router.post("/setpipeproperties/", response_model=None, summary="设置管道属性", description="批量设置指定管道的多个属性") async def fastapi_set_pipe_properties( - network: str, pipe: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe: str = Query(..., description="管道ID"), + req: Request = Body(...) ) -> ChangeSet: + """ + 批量设置管道属性。 + + Args: + network: 管网名称(或数据库名称) + pipe: 管道ID + req: 请求体,包含要设置的属性及其值 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ props = await req.json() ps = {"id": pipe} | props return set_pipe(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/network/pumps.py b/app/api/v1/endpoints/network/pumps.py index 0b63c21..c274803 100644 --- a/app/api/v1/endpoints/network/pumps.py +++ b/app/api/v1/endpoints/network/pumps.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -13,57 +13,191 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getpumpschema") -async def fastapi_get_pump_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getpumpschema", summary="获取水泵模式", description="获取水泵对象的模式定义,包含所有可用字段及其类型") +async def fastapi_get_pump_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取水泵数据模式定义。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含水泵模式信息的字典 + """ return get_pump_schema(network) -@router.post("/addpump/", response_model=None) +@router.post("/addpump/", response_model=None, summary="添加水泵", description="向网络中添加新的水泵,需要提供水泵的基本参数如功率等") async def fastapi_add_pump( - network: str, pump: str, node1: str, node2: str, power: float = 0.0 + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵标识符"), + node1: str = Query(..., description="水泵起始节点ID"), + node2: str = Query(..., description="水泵终止节点ID"), + power: float = Query(0.0, description="水泵功率(单位:千瓦)") ) -> ChangeSet: + """ + 添加新水泵到网络。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + node1: 起始节点ID + node2: 终止节点ID + power: 水泵功率 + + Returns: + ChangeSet对象,包含本次操作的变更信息 + """ ps = {"id": pump, "node1": node1, "node2": node2, "power": power} return add_pump(network, ChangeSet(ps)) -@router.post("/deletepump/", response_model=None) -async def fastapi_delete_pump(network: str, pump: str) -> ChangeSet: +@router.post("/deletepump/", response_model=None, summary="删除水泵", description="从网络中删除指定的水泵") +async def fastapi_delete_pump( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="要删除的水泵ID") +) -> ChangeSet: + """ + 删除水泵。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + + Returns: + ChangeSet对象,包含本次删除操作的变更信息 + """ ps = {"id": pump} return delete_pump(network, ChangeSet(ps)) -@router.get("/getpumpnode1/") -async def fastapi_get_pump_node1(network: str, pump: str) -> str | None: +@router.get("/getpumpnode1/", summary="获取水泵起始节点", description="获取指定水泵的起始节点ID") +async def fastapi_get_pump_node1( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵ID") +) -> str | None: + """ + 获取水泵的起始节点。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + + Returns: + 起始节点ID,如果不存在则返回None + """ ps = get_pump(network, pump) return ps["node1"] -@router.get("/getpumpnode2/") -async def fastapi_get_pump_node2(network: str, pump: str) -> str | None: +@router.get("/getpumpnode2/", summary="获取水泵终止节点", description="获取指定水泵的终止节点ID") +async def fastapi_get_pump_node2( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵ID") +) -> str | None: + """ + 获取水泵的终止节点。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + + Returns: + 终止节点ID,如果不存在则返回None + """ ps = get_pump(network, pump) return ps["node2"] -@router.post("/setpumpnode1/", response_model=None) -async def fastapi_set_pump_node1(network: str, pump: str, node1: str) -> ChangeSet: +@router.post("/setpumpnode1/", response_model=None, summary="设置水泵起始节点", description="设置指定水泵的起始节点") +async def fastapi_set_pump_node1( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵ID"), + node1: str = Query(..., description="新的起始节点ID") +) -> ChangeSet: + """ + 设置水泵起始节点。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + node1: 新的起始节点ID + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pump, "node1": node1} return set_pump(network, ChangeSet(ps)) -@router.post("/setpumpnode2/", response_model=None) -async def fastapi_set_pump_node2(network: str, pump: str, node2: str) -> ChangeSet: +@router.post("/setpumpnode2/", response_model=None, summary="设置水泵终止节点", description="设置指定水泵的终止节点") +async def fastapi_set_pump_node2( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵ID"), + node2: str = Query(..., description="新的终止节点ID") +) -> ChangeSet: + """ + 设置水泵终止节点。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + node2: 新的终止节点ID + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ ps = {"id": pump, "node2": node2} return set_pump(network, ChangeSet(ps)) -@router.get("/getpumpproperties/") -async def fastapi_get_pump_properties(network: str, pump: str) -> dict[str, Any]: +@router.get("/getpumpproperties/", summary="获取水泵属性", description="获取指定水泵的所有属性信息") +async def fastapi_get_pump_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵ID") +) -> dict[str, Any]: + """ + 获取水泵的所有属性。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + + Returns: + 包含水泵所有属性的字典 + """ return get_pump(network, pump) -@router.get("/getallpumpproperties/") -async def fastapi_get_all_pump_properties(network: str) -> list[dict[str, Any]]: +@router.get("/getallpumpproperties/", summary="获取所有水泵属性", description="获取网络中所有水泵的属性信息列表") +async def fastapi_get_all_pump_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取网络中所有水泵的属性。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含所有水泵属性的字典列表 + """ # 缓存查询结果提高性能 # global redis_client results = get_all_pumps(network) return results -@router.post("/setpumpproperties/", response_model=None) +@router.post("/setpumpproperties/", response_model=None, summary="设置水泵属性", description="批量设置指定水泵的多个属性") async def fastapi_set_pump_properties( - network: str, pump: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + pump: str = Query(..., description="水泵ID"), + req: Request = Body(...) ) -> ChangeSet: + """ + 批量设置水泵属性。 + + Args: + network: 管网名称(或数据库名称) + pump: 水泵ID + req: 请求体,包含要设置的属性及其值 + + Returns: + ChangeSet对象,包含本次修改的变更信息 + """ props = await req.json() ps = {"id": pump} | props return set_pump(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/network/regions.py b/app/api/v1/endpoints/network/regions.py index 6c957b3..e5768a3 100644 --- a/app/api/v1/endpoints/network/regions.py +++ b/app/api/v1/endpoints/network/regions.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -7,9 +7,11 @@ from app.services.tjnetwork import ( add_region, add_service_area, add_virtual_district, + calculate_district_metering_area, calculate_district_metering_area_for_network, calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, + calculate_region, calculate_service_area, calculate_virtual_district, delete_district_metering_area, @@ -17,11 +19,13 @@ from app.services.tjnetwork import ( delete_service_area, delete_virtual_district, generate_district_metering_area, + generate_region, generate_service_area, generate_sub_district_metering_area, generate_virtual_district, get_all_district_metering_area_ids, get_all_district_metering_areas, + get_all_regions, get_all_service_areas, get_all_virtual_districts, get_district_metering_area, @@ -44,41 +48,105 @@ router = APIRouter() # region 32 ############################################################ -@router.get("/calculateregion/") -async def fastapi_calculate_region(network: str, time_index: int) -> dict[str, Any]: +@router.get( + "/calculateregion/", + summary="计算区域", + description="计算指定水网在指定时间步长的区域分区" +) +async def fastapi_calculate_region( + network: str = Query(..., description="管网名称(或数据库名称)"), + time_index: int = Query(..., description="时间步长索引", ge=0) +) -> dict[str, Any]: + """计算区域分区。""" return calculate_region(network, time_index) -@router.get("/getregionschema/") -async def fastapi_get_region_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/getregionschema/", + summary="获取区域属性架构", + description="获取指定水网的区域属性架构定义" +) +async def fastapi_get_region_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """获取区域的属性架构。""" return get_region_schema(network) -@router.get("/getregion/") -async def fastapi_get_region(network: str, id: str) -> dict[str, Any]: +@router.get( + "/getregion/", + summary="获取区域信息", + description="获取指定ID的区域详细信息" +) +async def fastapi_get_region( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="区域ID") +) -> dict[str, Any]: + """获取区域的详细信息。""" return get_region(network, id) -@router.post("/setregion/", response_model=None) -async def fastapi_set_region(network: str, req: Request) -> ChangeSet: +@router.post( + "/setregion/", + response_model=None, + summary="设置区域属性", + description="修改指定区域的属性信息" +) +async def fastapi_set_region( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置区域属性。""" props = await req.json() return set_region(network, ChangeSet(props)) -@router.post("/addregion/", response_model=None) -async def fastapi_add_region(network: str, req: Request) -> ChangeSet: +@router.post( + "/addregion/", + response_model=None, + summary="添加新区域", + description="向水网添加一个新的区域" +) +async def fastapi_add_region( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加新的区域。""" props = await req.json() return add_region(network, ChangeSet(props)) -@router.post("/deleteregion/", response_model=None) -async def fastapi_delete_region(network: str, req: Request) -> ChangeSet: +@router.post( + "/deleteregion/", + response_model=None, + summary="删除区域", + description="删除指定的区域" +) +async def fastapi_delete_region( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """删除区域。""" props = await req.json() return delete_region(network, ChangeSet(props)) -@router.get("/getallregions/") -async def fastapi_get_all_regions(network: str) -> list[dict[str, Any]]: +@router.get( + "/getallregions/", + summary="获取所有区域", + description="获取指定水网中的所有区域信息" +) +async def fastapi_get_all_regions( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """获取所有区域的信息列表。""" return get_all_regions(network) -@router.post("/generateregion/", response_model=None) +@router.post( + "/generateregion/", + response_model=None, + summary="生成区域分区", + description="根据参数自动生成水网的区域分区" +) async def fastapi_generate_region( - network: str, inflate_delta: float + network: str = Query(..., description="管网名称(或数据库名称)"), + inflate_delta: float = Query(..., description="膨胀参数") ) -> ChangeSet: + """生成区域分区。""" return generate_region(network, inflate_delta) @@ -86,10 +154,25 @@ async def fastapi_generate_region( # district_metering_area 33 ############################################################ -@router.get("/calculatedistrictmeteringarea/") +@router.get( + "/calculatedistrictmeteringarea/", + summary="计算DMA分区", + description="计算指定节点集的区域计量(DMA)分区方案" +) async def fastapi_calculate_district_metering_area( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> list[list[str]]: + """ + 计算DMA分区。 + + 请求体格式: + { + "nodes": 节点ID列表(list[str]), + "part_count": 分区数量(int), + "part_type": 分区类型(int) + } + """ props = await req.json() nodes = props["nodes"] part_count = props["part_count"] @@ -98,10 +181,25 @@ async def fastapi_calculate_district_metering_area( network, nodes, part_count, part_type ) -@router.get("/calculatedistrictmeteringareaforregion/") +@router.get( + "/calculatedistrictmeteringareaforregion/", + summary="计算区域内DMA分区", + description="为指定区域计算区域计量(DMA)分区方案" +) async def fastapi_calculate_district_metering_area_for_region( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> list[list[str]]: + """ + 计算区域内DMA分区。 + + 请求体格式: + { + "region": 区域ID(str), + "part_count": 分区数量(int), + "part_type": 分区类型(int) + } + """ props = await req.json() region = props["region"] part_count = props["part_count"] @@ -110,32 +208,77 @@ async def fastapi_calculate_district_metering_area_for_region( network, region, part_count, part_type ) -@router.get("/calculatedistrictmeteringareafornetwork/") +@router.get( + "/calculatedistrictmeteringareafornetwork/", + summary="计算整网DMA分区", + description="为整个水网计算区域计量(DMA)分区方案" +) async def fastapi_calculate_district_metering_area_for_network( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> list[list[str]]: + """ + 计算整网DMA分区。 + + 请求体格式: + { + "part_count": 分区数量(int), + "part_type": 分区类型(int) + } + """ props = await req.json() part_count = props["part_count"] part_type = props["part_type"] return calculate_district_metering_area_for_network(network, part_count, part_type) -@router.get("/getdistrictmeteringareaschema/") +@router.get( + "/getdistrictmeteringareaschema/", + summary="获取DMA属性架构", + description="获取指定水网的区域计量(DMA)属性架构定义" +) async def fastapi_get_district_metering_area_schema( - network: str, + network: str = Query(..., description="管网名称(或数据库名称)"), ) -> dict[str, dict[str, Any]]: + """获取DMA的属性架构。""" return get_district_metering_area_schema(network) -@router.get("/getdistrictmeteringarea/") -async def fastapi_get_district_metering_area(network: str, id: str) -> dict[str, Any]: +@router.get( + "/getdistrictmeteringarea/", + summary="获取DMA信息", + description="获取指定ID的区域计量(DMA)详细信息" +) +async def fastapi_get_district_metering_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="DMA ID") +) -> dict[str, Any]: + """获取DMA的详细信息。""" return get_district_metering_area(network, id) -@router.post("/setdistrictmeteringarea/", response_model=None) -async def fastapi_set_district_metering_area(network: str, req: Request) -> ChangeSet: +@router.post( + "/setdistrictmeteringarea/", + response_model=None, + summary="设置DMA属性", + description="修改指定DMA的属性信息" +) +async def fastapi_set_district_metering_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置DMA属性。""" props = await req.json() return set_district_metering_area(network, ChangeSet(props)) -@router.post("/adddistrictmeteringarea/", response_model=None) -async def fastapi_add_district_metering_area(network: str, req: Request) -> ChangeSet: +@router.post( + "/adddistrictmeteringarea/", + response_model=None, + summary="添加新DMA", + description="向水网添加一个新的区域计量(DMA)" +) +async def fastapi_add_district_metering_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加新的DMA。""" props = await req.json() # boundary should be [(x,y), (x,y)] boundary = props.get("boundary", []) @@ -146,33 +289,73 @@ async def fastapi_add_district_metering_area(network: str, req: Request) -> Chan props["boundary"] = newBoundary return add_district_metering_area(network, ChangeSet(props)) -@router.post("/deletedistrictmeteringarea/", response_model=None) +@router.post( + "/deletedistrictmeteringarea/", + response_model=None, + summary="删除DMA", + description="删除指定的区域计量(DMA)" +) async def fastapi_delete_district_metering_area( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> ChangeSet: + """删除DMA。""" props = await req.json() return delete_district_metering_area(network, ChangeSet(props)) -@router.get("/getalldistrictmeteringareaids/") -async def fastapi_get_all_district_metering_area_ids(network: str) -> list[str]: +@router.get( + "/getalldistrictmeteringareaids/", + summary="获取所有DMA ID", + description="获取指定水网中所有DMA的ID列表" +) +async def fastapi_get_all_district_metering_area_ids( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[str]: + """获取所有DMA的ID列表。""" return get_all_district_metering_area_ids(network) -@router.get("/getalldistrictmeteringareas/") -async def getalldistrictmeteringareas(network: str) -> list[dict[str, Any]]: +@router.get( + "/getalldistrictmeteringareas/", + summary="获取所有DMA", + description="获取指定水网中所有DMA的详细信息" +) +async def getalldistrictmeteringareas( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """获取所有DMA的详细信息列表。""" return get_all_district_metering_areas(network) -@router.post("/generatedistrictmeteringarea/", response_model=None) +@router.post( + "/generatedistrictmeteringarea/", + response_model=None, + summary="生成DMA分区", + description="根据参数自动生成水网的DMA分区方案" +) async def fastapi_generate_district_metering_area( - network: str, part_count: int, part_type: int, inflate_delta: float + network: str = Query(..., description="管网名称(或数据库名称)"), + part_count: int = Query(..., description="分区数量", gt=0), + part_type: int = Query(..., description="分区类型"), + inflate_delta: float = Query(..., description="膨胀参数") ) -> ChangeSet: + """生成DMA分区。""" return generate_district_metering_area( network, part_count, part_type, inflate_delta ) -@router.post("/generatesubdistrictmeteringarea/", response_model=None) +@router.post( + "/generatesubdistrictmeteringarea/", + response_model=None, + summary="生成DMA子分区", + description="为指定DMA生成子DMA分区" +) async def fastapi_generate_sub_district_metering_area( - network: str, dma: str, part_count: int, part_type: int, inflate_delta: float + network: str = Query(..., description="管网名称(或数据库名称)"), + dma: str = Query(..., description="DMA ID"), + part_count: int = Query(..., description="分区数量", gt=0), + part_type: int = Query(..., description="分区类型"), + inflate_delta: float = Query(..., description="膨胀参数") ) -> ChangeSet: + """生成DMA子分区。""" return generate_sub_district_metering_area( network, dma, part_count, part_type, inflate_delta ) @@ -182,43 +365,105 @@ async def fastapi_generate_sub_district_metering_area( # service_area 34 ############################################################ -@router.get("/calculateservicearea/") +@router.get( + "/calculateservicearea/", + summary="计算服务区", + description="计算指定水网在指定时间步长的服务区分区" +) async def fastapi_calculate_service_area( - network: str, time_index: int + network: str = Query(..., description="管网名称(或数据库名称)"), + time_index: int = Query(..., description="时间步长索引", ge=0) ) -> dict[str, Any]: + """计算服务区分区。""" return calculate_service_area(network, time_index) -@router.get("/getserviceareaschema/") -async def fastapi_get_service_area_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/getserviceareaschema/", + summary="获取服务区属性架构", + description="获取指定水网的服务区属性架构定义" +) +async def fastapi_get_service_area_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """获取服务区的属性架构。""" return get_service_area_schema(network) -@router.get("/getservicearea/") -async def fastapi_get_service_area(network: str, id: str) -> dict[str, Any]: +@router.get( + "/getservicearea/", + summary="获取服务区信息", + description="获取指定ID的服务区详细信息" +) +async def fastapi_get_service_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="服务区ID") +) -> dict[str, Any]: + """获取服务区的详细信息。""" return get_service_area(network, id) -@router.post("/setservicearea/", response_model=None) -async def fastapi_set_service_area(network: str, req: Request) -> ChangeSet: +@router.post( + "/setservicearea/", + response_model=None, + summary="设置服务区属性", + description="修改指定服务区的属性信息" +) +async def fastapi_set_service_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置服务区属性。""" props = await req.json() return set_service_area(network, ChangeSet(props)) -@router.post("/addservicearea/", response_model=None) -async def fastapi_add_service_area(network: str, req: Request) -> ChangeSet: +@router.post( + "/addservicearea/", + response_model=None, + summary="添加新服务区", + description="向水网添加一个新的服务区" +) +async def fastapi_add_service_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加新的服务区。""" props = await req.json() return add_service_area(network, ChangeSet(props)) -@router.post("/deleteservicearea/", response_model=None) -async def fastapi_delete_service_area(network: str, req: Request) -> ChangeSet: +@router.post( + "/deleteservicearea/", + response_model=None, + summary="删除服务区", + description="删除指定的服务区" +) +async def fastapi_delete_service_area( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """删除服务区。""" props = await req.json() return delete_service_area(network, ChangeSet(props)) -@router.get("/getallserviceareas/") -async def fastapi_get_all_service_areas(network: str) -> list[dict[str, Any]]: +@router.get( + "/getallserviceareas/", + summary="获取所有服务区", + description="获取指定水网中的所有服务区信息" +) +async def fastapi_get_all_service_areas( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """获取所有服务区的信息列表。""" return get_all_service_areas(network) -@router.post("/generateservicearea/", response_model=None) +@router.post( + "/generateservicearea/", + response_model=None, + summary="生成服务区分区", + description="根据参数自动生成水网的服务区分区" +) async def fastapi_generate_service_area( - network: str, inflate_delta: float + network: str = Query(..., description="管网名称(或数据库名称)"), + inflate_delta: float = Query(..., description="膨胀参数") ) -> ChangeSet: + """生成服务区分区。""" return generate_service_area(network, inflate_delta) @@ -226,52 +471,128 @@ async def fastapi_generate_service_area( # virtual_district 35 ############################################################ -@router.get("/calculatevirtualdistrict/") +@router.get( + "/calculatevirtualdistrict/", + summary="计算虚拟分区", + description="根据指定的中心节点计算虚拟分区方案" +) async def fastapi_calculate_virtual_district( - network: str, centers: list[str] + network: str = Query(..., description="管网名称(或数据库名称)"), + centers: list[str] = Query(..., description="中心节点ID列表") ) -> dict[str, list[Any]]: + """计算虚拟分区。""" return calculate_virtual_district(network, centers) -@router.get("/getvirtualdistrictschema/") +@router.get( + "/getvirtualdistrictschema/", + summary="获取虚拟分区属性架构", + description="获取指定水网的虚拟分区属性架构定义" +) async def fastapi_get_virtual_district_schema( - network: str, + network: str = Query(..., description="管网名称(或数据库名称)"), ) -> dict[str, dict[str, Any]]: + """获取虚拟分区的属性架构。""" return get_virtual_district_schema(network) -@router.get("/getvirtualdistrict/") -async def fastapi_get_virtual_district(network: str, id: str) -> dict[str, Any]: +@router.get( + "/getvirtualdistrict/", + summary="获取虚拟分区信息", + description="获取指定ID的虚拟分区详细信息" +) +async def fastapi_get_virtual_district( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="虚拟分区ID") +) -> dict[str, Any]: + """获取虚拟分区的详细信息。""" return get_virtual_district(network, id) -@router.post("/setvirtualdistrict/", response_model=None) -async def fastapi_set_virtual_district(network: str, req: Request) -> ChangeSet: +@router.post( + "/setvirtualdistrict/", + response_model=None, + summary="设置虚拟分区属性", + description="修改指定虚拟分区的属性信息" +) +async def fastapi_set_virtual_district( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置虚拟分区属性。""" props = await req.json() return set_virtual_district(network, ChangeSet(props)) -@router.post("/addvirtualdistrict/", response_model=None) -async def fastapi_add_virtual_district(network: str, req: Request) -> ChangeSet: +@router.post( + "/addvirtualdistrict/", + response_model=None, + summary="添加新虚拟分区", + description="向水网添加一个新的虚拟分区" +) +async def fastapi_add_virtual_district( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """添加新的虚拟分区。""" props = await req.json() return add_virtual_district(network, ChangeSet(props)) -@router.post("/deletevirtualdistrict/", response_model=None) -async def fastapi_delete_virtual_district(network: str, req: Request) -> ChangeSet: +@router.post( + "/deletevirtualdistrict/", + response_model=None, + summary="删除虚拟分区", + description="删除指定的虚拟分区" +) +async def fastapi_delete_virtual_district( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """删除虚拟分区。""" props = await req.json() return delete_virtual_district(network, ChangeSet(props)) -@router.get("/getallvirtualdistrict/") -async def fastapi_get_all_virtual_district(network: str) -> list[dict[str, Any]]: +@router.get( + "/getallvirtualdistrict/", + summary="获取所有虚拟分区", + description="获取指定水网中的所有虚拟分区信息" +) +async def fastapi_get_all_virtual_district( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """获取所有虚拟分区的信息列表。""" return get_all_virtual_districts(network) -@router.post("/generatevirtualdistrict/", response_model=None) +@router.post( + "/generatevirtualdistrict/", + response_model=None, + summary="生成虚拟分区", + description="根据参数自动生成虚拟分区方案" +) async def fastapi_generate_virtual_district( - network: str, inflate_delta: float, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + inflate_delta: float = Query(..., description="膨胀参数"), + req: Request = Body(...) ) -> ChangeSet: + """生成虚拟分区。""" props = await req.json() return generate_virtual_district(network, props["centers"], inflate_delta) -@router.get("/calculatedistrictmeteringareafornodes/") +@router.get( + "/calculatedistrictmeteringareafornodes/", + summary="计算节点DMA分区", + description="为指定节点集计算区域计量(DMA)分区方案" +) async def fastapi_calculate_district_metering_area_for_nodes( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) ) -> list[list[str]]: + """ + 计算节点DMA分区。 + + 请求体格式: + { + "nodes": 节点ID列表(list[str]), + "part_count": 分区数量(int), + "part_type": 分区类型(int) + } + """ props = await req.json() nodes = props["nodes"] part_count = props["part_count"] diff --git a/app/api/v1/endpoints/network/reservoirs.py b/app/api/v1/endpoints/network/reservoirs.py index 0973882..14fac6a 100644 --- a/app/api/v1/endpoints/network/reservoirs.py +++ b/app/api/v1/endpoints/network/reservoirs.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -13,102 +13,410 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getreservoirschema") -async def fast_get_reservoir_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/getreservoirschema", + summary="获取水库模式", + description="获取指定供水网络中所有水库的模式/属性字段定义" +) +async def fast_get_reservoir_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取水库模式定义。 + + 该端点返回指定网络中水库对象的模式定义,包括所有可用的属性字段。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 水库属性的模式定义字典 + """ return get_reservoir_schema(network) -@router.post("/addreservoir/", response_model=None) +@router.post( + "/addreservoir/", + response_model=None, + summary="添加水库", + description="在指定供水网络中添加新的水库/水源节点" +) async def fastapi_add_reservoir( - network: str, reservoir: str, x: float, y: float, head: float + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + x: float = Query(..., description="水库的X坐标"), + y: float = Query(..., description="水库的Y坐标"), + head: float = Query(..., description="水库的水头/总水头(米)") ) -> ChangeSet: + """ + 添加新的水库/水源节点。 + + 在指定的供水网络中创建一个新的水库,并设置其坐标和水头参数。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + x: 水库的X坐标位置 + y: 水库的Y坐标位置 + head: 水库的供水水头(以米为单位) + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir, "x": x, "y": y, "head": head} return add_reservoir(network, ChangeSet(ps)) -@router.post("/deletereservoir/", response_model=None) -async def fastapi_delete_reservoir(network: str, reservoir: str) -> ChangeSet: +@router.post( + "/deletereservoir/", + response_model=None, + summary="删除水库", + description="从指定供水网络中删除指定的水库/水源节点" +) +async def fastapi_delete_reservoir( + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="要删除的水库的唯一标识符") +) -> ChangeSet: + """ + 删除指定的水库节点。 + + 从指定的供水网络中删除一个水库及其相关的所有连接关系。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir} return delete_reservoir(network, ChangeSet(ps)) -@router.get("/getreservoirhead/") -async def fastapi_get_reservoir_head(network: str, reservoir: str) -> float | None: +@router.get( + "/getreservoirhead/", + summary="获取水库水头", + description="获取指定水库的供水水头/总水头值" +) +async def fastapi_get_reservoir_head( + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符") +) -> float | None: + """ + 获取水库的水头参数。 + + 返回指定水库的供水水头(总水头),单位为米。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 水库的水头值(米),如果水库不存在则返回None + """ ps = get_reservoir(network, reservoir) return ps["head"] -@router.get("/getreservoirpattern/") -async def fastapi_get_reservoir_pattern(network: str, reservoir: str) -> str | None: +@router.get( + "/getreservoirpattern/", + summary="获取水库模式", + description="获取指定水库的运行模式/供水模式" +) +async def fastapi_get_reservoir_pattern( + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符") +) -> str | None: + """ + 获取水库的运行模式。 + + 返回指定水库的供水模式,如固定水头模式、时间序列模式等。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 水库的运行模式字符串,如果水库不存在则返回None + """ ps = get_reservoir(network, reservoir) return ps["pattern"] -@router.get("/getreservoirx/") +@router.get( + "/getreservoirx/", + summary="获取水库X坐标", + description="获取指定水库的X坐标位置" +) async def fastapi_get_reservoir_x( - network: str, reservoir: str + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符") ) -> dict[str, float] | None: + """ + 获取水库的X坐标。 + + 返回指定水库的X轴坐标值。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 水库的X坐标值,如果水库不存在则返回None + """ ps = get_reservoir(network, reservoir) return ps["x"] -@router.get("/getreservoiry/") +@router.get( + "/getreservoiry/", + summary="获取水库Y坐标", + description="获取指定水库的Y坐标位置" +) async def fastapi_get_reservoir_y( - network: str, reservoir: str + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符") ) -> dict[str, float] | None: + """ + 获取水库的Y坐标。 + + 返回指定水库的Y轴坐标值。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 水库的Y坐标值,如果水库不存在则返回None + """ ps = get_reservoir(network, reservoir) return ps["y"] -@router.get("/getreservoircoord/") +@router.get( + "/getreservoircoord/", + summary="获取水库坐标", + description="获取指定水库的平面坐标(X和Y坐标)" +) async def fastapi_get_reservoir_coord( - network: str, reservoir: str + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符") ) -> dict[str, float] | None: + """ + 获取水库的坐标。 + + 返回指定水库的平面坐标,包含水库ID、X坐标和Y坐标。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 包含water库ID和X、Y坐标的字典,如果水库不存在则返回None + """ ps = get_reservoir(network, reservoir) coord = {"id": reservoir, "x": ps["x"], "y": ps["y"]} return coord -@router.post("/setreservoirhead/", response_model=None) +@router.post( + "/setreservoirhead/", + response_model=None, + summary="设置水库水头", + description="更新指定水库的供水水头/总水头值" +) async def fastapi_set_reservoir_head( - network: str, reservoir: str, head: float + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + head: float = Query(..., description="新的水头值(米)") ) -> ChangeSet: + """ + 设置水库的水头参数。 + + 更新指定水库的供水水头(总水头)值。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + head: 新的水头值(以米为单位) + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir, "head": head} return set_reservoir(network, ChangeSet(ps)) -@router.post("/setreservoirpattern/", response_model=None) +@router.post( + "/setreservoirpattern/", + response_model=None, + summary="设置水库模式", + description="更新指定水库的运行模式/供水模式" +) async def fastapi_set_reservoir_pattern( - network: str, reservoir: str, pattern: str + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + pattern: str = Query(..., description="新的运行模式") ) -> ChangeSet: + """ + 设置水库的运行模式。 + + 更新指定水库的供水模式,如固定水头模式、时间序列模式等。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + pattern: 新的运行模式字符串 + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir, "pattern": pattern} return set_reservoir(network, ChangeSet(ps)) -@router.post("/setreservoirx/", response_model=None) -async def fastapi_set_reservoir_x(network: str, reservoir: str, x: float) -> ChangeSet: +@router.post( + "/setreservoirx/", + response_model=None, + summary="设置水库X坐标", + description="更新指定水库的X坐标位置" +) +async def fastapi_set_reservoir_x( + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + x: float = Query(..., description="新的X坐标值") +) -> ChangeSet: + """ + 设置水库的X坐标。 + + 更新指定水库的X轴坐标值。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + x: 新的X坐标值 + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir, "x": x} return set_reservoir(network, ChangeSet(ps)) -@router.post("/setreservoiry/", response_model=None) -async def fastapi_set_reservoir_y(network: str, reservoir: str, y: float) -> ChangeSet: +@router.post( + "/setreservoiry/", + response_model=None, + summary="设置水库Y坐标", + description="更新指定水库的Y坐标位置" +) +async def fastapi_set_reservoir_y( + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + y: float = Query(..., description="新的Y坐标值") +) -> ChangeSet: + """ + 设置水库的Y坐标。 + + 更新指定水库的Y轴坐标值。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + y: 新的Y坐标值 + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir, "y": y} return set_reservoir(network, ChangeSet(ps)) -@router.post("/setreservoircoord/", response_model=None) +@router.post( + "/setreservoircoord/", + response_model=None, + summary="设置水库坐标", + description="更新指定水库的平面坐标(X和Y坐标)" +) async def fastapi_set_reservoir_coord( - network: str, reservoir: str, x: float, y: float + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + x: float = Query(..., description="新的X坐标值"), + y: float = Query(..., description="新的Y坐标值") ) -> ChangeSet: + """ + 设置水库的坐标。 + + 更新指定水库的平面坐标,包括X和Y坐标。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + x: 新的X坐标值 + y: 新的Y坐标值 + + Returns: + 包含操作变更集的ChangeSet对象 + """ ps = {"id": reservoir, "x": x, "y": y} return set_reservoir(network, ChangeSet(ps)) -@router.get("/getreservoirproperties/") +@router.get( + "/getreservoirproperties/", + summary="获取水库属性", + description="获取指定水库的所有属性" +) async def fastapi_get_reservoir_properties( - network: str, reservoir: str + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符") ) -> dict[str, Any]: + """ + 获取水库的所有属性。 + + 返回指定水库的完整属性信息,包括ID、坐标、水头、模式等所有属性。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + + Returns: + 包含水库所有属性的字典 + """ return get_reservoir(network, reservoir) -@router.get("/getallreservoirproperties/") -async def fastapi_get_all_reservoir_properties(network: str) -> list[dict[str, Any]]: - # 缓存查询结果提高性能 - # global redis_client +@router.get( + "/getallreservoirproperties/", + summary="获取所有水库属性", + description="获取指定供水网络中所有水库的属性" +) +async def fastapi_get_all_reservoir_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取所有水库的属性。 + + 返回指定供水网络中所有水库的完整属性信息列表。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含所有水库属性的字典列表 + """ results = get_all_reservoirs(network) return results -@router.post("/setreservoirproperties/", response_model=None) +@router.post( + "/setreservoirproperties/", + response_model=None, + summary="设置水库属性", + description="批量更新指定水库的多个属性" +) async def fastapi_set_reservoir_properties( - network: str, reservoir: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + reservoir: str = Query(..., description="水库的唯一标识符"), + req: Request = Body(..., description="包含要更新的属性的请求体") ) -> ChangeSet: + """ + 设置水库的多个属性。 + + 批量更新指定水库的属性。属性通过JSON请求体传递。 + + Args: + network: 管网名称(或数据库名称) + reservoir: 水库的唯一标识符 + req: HTTP请求对象,包含JSON格式的属性数据 + + Returns: + 包含操作变更集的ChangeSet对象 + """ props = await req.json() ps = {"id": reservoir} | props return set_reservoir(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/network/tags.py b/app/api/v1/endpoints/network/tags.py index 7a63828..33d675f 100644 --- a/app/api/v1/endpoints/network/tags.py +++ b/app/api/v1/endpoints/network/tags.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -15,20 +15,52 @@ router = APIRouter() # tag 8.[TAGS] ############################################################ -@router.get("/gettagschema/") -async def fastapi_get_tag_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/gettagschema/", + summary="获取标签属性架构", + description="获取指定水网的标签(Tag)属性架构定义" +) +async def fastapi_get_tag_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """获取标签的属性架构。""" return get_tag_schema(network) -@router.get("/gettag/") -async def fastapi_get_tag(network: str, t_type: str, id: str) -> dict[str, Any]: +@router.get( + "/gettag/", + summary="获取标签信息", + description="获取指定类型和ID的标签信息" +) +async def fastapi_get_tag( + network: str = Query(..., description="管网名称(或数据库名称)"), + t_type: str = Query(..., description="标签类型"), + id: str = Query(..., description="元素ID") +) -> dict[str, Any]: + """获取标签信息。""" return get_tag(network, t_type, id) -@router.get("/gettags/") -async def fastapi_get_tags(network: str) -> list[dict[str, Any]]: +@router.get( + "/gettags/", + summary="获取所有标签", + description="获取指定水网中的所有标签信息" +) +async def fastapi_get_tags( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """获取水网中所有标签的列表。""" tags = get_tags(network) return tags -@router.post("/settag/", response_model=None) -async def fastapi_set_tag(network: str, req: Request) -> ChangeSet: +@router.post( + "/settag/", + response_model=None, + summary="设置标签", + description="为指定元素设置或修改标签信息" +) +async def fastapi_set_tag( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(...) +) -> ChangeSet: + """设置标签信息。""" props = await req.json() return set_tag(network, ChangeSet(props)) diff --git a/app/api/v1/endpoints/network/tanks.py b/app/api/v1/endpoints/network/tanks.py index 1696b1b..40733cc 100644 --- a/app/api/v1/endpoints/network/tanks.py +++ b/app/api/v1/endpoints/network/tanks.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -13,23 +13,50 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/gettankschema") -async def fast_get_tank_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/gettankschema", summary="获取水箱模式", description="获取指定网络的水箱数据结构模式定义") +async def fast_get_tank_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]: + """ + 获取水箱的数据结构模式。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含水箱属性的模式定义字典 + """ return get_tank_schema(network) -@router.post("/addtank/", response_model=None) +@router.post("/addtank/", summary="新增水箱", description="向指定网络中新增一个水箱", response_model=None) async def fastapi_add_tank( - network: str, - tank: str, - x: float, - y: float, - elevation: float, - init_level: float = 0, - min_level: float = 0, - max_level: float = 0, - diameter: float = 0, - min_vol: float = 0, + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + x: float = Query(..., description="X坐标"), + y: float = Query(..., description="Y坐标"), + elevation: float = Query(..., description="标高"), + init_level: float = Query(0, description="初始水位"), + min_level: float = Query(0, description="最小水位"), + max_level: float = Query(0, description="最大水位"), + diameter: float = Query(0, description="直径"), + min_vol: float = Query(0, description="最小体积"), ) -> ChangeSet: + """ + 创建新水箱。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + x: X坐标 + y: Y坐标 + elevation: 水箱标高 + init_level: 初始水位,默认为0 + min_level: 最小水位,默认为0 + max_level: 最大水位,默认为0 + diameter: 水箱直径,默认为0 + min_vol: 最小体积,默认为0 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = { "id": tank, "x": x, @@ -43,155 +70,497 @@ async def fastapi_add_tank( } return add_tank(network, ChangeSet(ps)) -@router.post("/deletetank/", response_model=None) -async def fastapi_delete_tank(network: str, tank: str) -> ChangeSet: +@router.post("/deletetank/", summary="删除水箱", description="删除指定网络中的水箱", response_model=None) +async def fastapi_delete_tank( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> ChangeSet: + """ + 删除指定的水箱。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank} return delete_tank(network, ChangeSet(ps)) -@router.get("/gettankelevation/") -async def fastapi_get_tank_elevation(network: str, tank: str) -> float | None: +@router.get("/gettankelevation/", summary="获取水箱标高", description="获取指定水箱的标高值") +async def fastapi_get_tank_elevation( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float | None: + """ + 获取水箱的标高。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱标高值,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["elevation"] -@router.get("/gettankinitlevel/") -async def fastapi_get_tank_init_level(network: str, tank: str) -> float | None: +@router.get("/gettankinitlevel/", summary="获取水箱初始水位", description="获取指定水箱的初始水位值") +async def fastapi_get_tank_init_level( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float | None: + """ + 获取水箱的初始水位。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱初始水位值,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["init_level"] -@router.get("/gettankminlevel/") -async def fastapi_get_tank_min_level(network: str, tank: str) -> float | None: +@router.get("/gettankminlevel/", summary="获取水箱最小水位", description="获取指定水箱的最小水位值") +async def fastapi_get_tank_min_level( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float | None: + """ + 获取水箱的最小水位。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱最小水位值,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["min_level"] -@router.get("/gettankmaxlevel/") -async def fastapi_get_tank_max_level(network: str, tank: str) -> float | None: +@router.get("/gettankmaxlevel/", summary="获取水箱最大水位", description="获取指定水箱的最大水位值") +async def fastapi_get_tank_max_level( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float | None: + """ + 获取水箱的最大水位。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱最大水位值,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["max_level"] -@router.get("/gettankdiameter/") -async def fastapi_get_tank_diameter(network: str, tank: str) -> float | None: +@router.get("/gettankdiameter/", summary="获取水箱直径", description="获取指定水箱的直径值") +async def fastapi_get_tank_diameter( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float | None: + """ + 获取水箱的直径。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱直径值,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["diameter"] -@router.get("/gettankminvol/") -async def fastapi_get_tank_min_vol(network: str, tank: str) -> float | None: +@router.get("/gettankminvol/", summary="获取水箱最小体积", description="获取指定水箱的最小体积值") +async def fastapi_get_tank_min_vol( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float | None: + """ + 获取水箱的最小体积。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱最小体积值,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["min_vol"] -@router.get("/gettankvolcurve/") -async def fastapi_get_tank_vol_curve(network: str, tank: str) -> str | None: +@router.get("/gettankvolcurve/", summary="获取水箱容积曲线", description="获取指定水箱的容积曲线标识") +async def fastapi_get_tank_vol_curve( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> str | None: + """ + 获取水箱的容积曲线。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱容积曲线标识,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["vol_curve"] -@router.get("/gettankoverflow/") -async def fastapi_get_tank_overflow(network: str, tank: str) -> str | None: +@router.get("/gettankoverflow/", summary="获取水箱溢流口", description="获取指定水箱的溢流口配置") +async def fastapi_get_tank_overflow( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> str | None: + """ + 获取水箱的溢流口配置。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱溢流口配置,如果不存在则返回None + """ ps = get_tank(network, tank) return ps["overflow"] -@router.get("/gettankx/") -async def fastapi_get_tank_x(network: str, tank: str) -> float: +@router.get("/gettankx/", summary="获取水箱X坐标", description="获取指定水箱的X坐标值") +async def fastapi_get_tank_x( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float: + """ + 获取水箱的X坐标。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱X坐标值 + """ ps = get_tank(network, tank) return ps["x"] -@router.get("/gettanky/") -async def fastapi_get_tank_y(network: str, tank: str) -> float: +@router.get("/gettanky/", summary="获取水箱Y坐标", description="获取指定水箱的Y坐标值") +async def fastapi_get_tank_y( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> float: + """ + 获取水箱的Y坐标。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 水箱Y坐标值 + """ ps = get_tank(network, tank) return ps["y"] -@router.get("/gettankcoord/") -async def fastapi_get_tank_coord(network: str, tank: str) -> dict[str, float]: +@router.get("/gettankcoord/", summary="获取水箱坐标", description="获取指定水箱的X和Y坐标") +async def fastapi_get_tank_coord( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> dict[str, float]: + """ + 获取水箱的坐标。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 包含x和y坐标的字典 + """ ps = get_tank(network, tank) coord = {"x": ps["x"], "y": ps["y"]} return coord -@router.post("/settankelevation/", response_model=None) +@router.post("/settankelevation/", summary="设置水箱标高", description="设置指定水箱的标高值", response_model=None) async def fastapi_set_tank_elevation( - network: str, tank: str, elevation: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + elevation: float = Query(..., description="新的标高值") ) -> ChangeSet: + """ + 设置水箱的标高。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + elevation: 新的标高值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "elevation": elevation} return set_tank(network, ChangeSet(ps)) -@router.post("/settankinitlevel/", response_model=None) +@router.post("/settankinitlevel/", summary="设置水箱初始水位", description="设置指定水箱的初始水位值", response_model=None) async def fastapi_set_tank_init_level( - network: str, tank: str, init_level: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + init_level: float = Query(..., description="新的初始水位值") ) -> ChangeSet: + """ + 设置水箱的初始水位。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + init_level: 新的初始水位值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "init_level": init_level} return set_tank(network, ChangeSet(ps)) -@router.post("/settankminlevel/", response_model=None) +@router.post("/settankminlevel/", summary="设置水箱最小水位", description="设置指定水箱的最小水位值", response_model=None) async def fastapi_set_tank_min_level( - network: str, tank: str, min_level: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + min_level: float = Query(..., description="新的最小水位值") ) -> ChangeSet: + """ + 设置水箱的最小水位。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + min_level: 新的最小水位值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "min_level": min_level} return set_tank(network, ChangeSet(ps)) -@router.post("/settankmaxlevel/", response_model=None) +@router.post("/settankmaxlevel/", summary="设置水箱最大水位", description="设置指定水箱的最大水位值", response_model=None) async def fastapi_set_tank_max_level( - network: str, tank: str, max_level: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + max_level: float = Query(..., description="新的最大水位值") ) -> ChangeSet: + """ + 设置水箱的最大水位。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + max_level: 新的最大水位值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "max_level": max_level} return set_tank(network, ChangeSet(ps)) -@router.post("settankdiameter//", response_model=None) +@router.post("/settankdiameter/", summary="设置水箱直径", description="设置指定水箱的直径值", response_model=None) async def fastapi_set_tank_diameter( - network: str, tank: str, diameter: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + diameter: float = Query(..., description="新的直径值") ) -> ChangeSet: + """ + 设置水箱的直径。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + diameter: 新的直径值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "diameter": diameter} return set_tank(network, ChangeSet(ps)) -@router.post("/settankminvol/", response_model=None) +@router.post("/settankminvol/", summary="设置水箱最小体积", description="设置指定水箱的最小体积值", response_model=None) async def fastapi_set_tank_min_vol( - network: str, tank: str, min_vol: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + min_vol: float = Query(..., description="新的最小体积值") ) -> ChangeSet: + """ + 设置水箱的最小体积。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + min_vol: 新的最小体积值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "min_vol": min_vol} return set_tank(network, ChangeSet(ps)) -@router.post("/settankvolcurve/", response_model=None) +@router.post("/settankvolcurve/", summary="设置水箱容积曲线", description="设置指定水箱的容积曲线标识", response_model=None) async def fastapi_set_tank_vol_curve( - network: str, tank: str, vol_curve: str + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + vol_curve: str = Query(..., description="新的容积曲线标识") ) -> ChangeSet: + """ + 设置水箱的容积曲线。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + vol_curve: 新的容积曲线标识 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "vol_curve": vol_curve} return set_tank(network, ChangeSet(ps)) -@router.post("/settankoverflow/", response_model=None) +@router.post("/settankoverflow/", summary="设置水箱溢流口", description="设置指定水箱的溢流口配置", response_model=None) async def fastapi_set_tank_overflow( - network: str, tank: str, overflow: str + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + overflow: str = Query(..., description="新的溢流口配置") ) -> ChangeSet: + """ + 设置水箱的溢流口配置。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + overflow: 新的溢流口配置 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "overflow": overflow} return set_tank(network, ChangeSet(ps)) -@router.post("/settankx/", response_model=None) -async def fastapi_set_tank_x(network: str, tank: str, x: float) -> ChangeSet: +@router.post("/settankx/", summary="设置水箱X坐标", description="设置指定水箱的X坐标值", response_model=None) +async def fastapi_set_tank_x( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + x: float = Query(..., description="新的X坐标值") +) -> ChangeSet: + """ + 设置水箱的X坐标。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + x: 新的X坐标值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "x": x} return set_tank(network, ChangeSet(ps)) -@router.post("/settanky/", response_model=None) -async def fastapi_set_tank_y(network: str, tank: str, y: float) -> ChangeSet: +@router.post("/settanky/", summary="设置水箱Y坐标", description="设置指定水箱的Y坐标值", response_model=None) +async def fastapi_set_tank_y( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + y: float = Query(..., description="新的Y坐标值") +) -> ChangeSet: + """ + 设置水箱的Y坐标。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + y: 新的Y坐标值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "y": y} return set_tank(network, ChangeSet(ps)) -@router.post("/settankcoord/", response_model=None) +@router.post("/settankcoord/", summary="设置水箱坐标", description="设置指定水箱的X和Y坐标", response_model=None) async def fastapi_set_tank_coord( - network: str, tank: str, x: float, y: float + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + x: float = Query(..., description="新的X坐标值"), + y: float = Query(..., description="新的Y坐标值") ) -> ChangeSet: + """ + 设置水箱的坐标。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + x: 新的X坐标值 + y: 新的Y坐标值 + + Returns: + 包含变更信息的ChangeSet对象 + """ ps = {"id": tank, "x": x, "y": y} return set_tank(network, ChangeSet(ps)) -@router.get("/gettankproperties/") -async def fastapi_get_tank_properties(network: str, tank: str) -> dict[str, Any]: +@router.get("/gettankproperties/", summary="获取水箱属性", description="获取指定水箱的所有属性") +async def fastapi_get_tank_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID") +) -> dict[str, Any]: + """ + 获取水箱的所有属性。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + + Returns: + 包含水箱所有属性的字典 + """ return get_tank(network, tank) -@router.get("/getalltankproperties/") -async def fastapi_get_all_tank_properties(network: str) -> list[dict[str, Any]]: +@router.get("/getalltankproperties/", summary="获取所有水箱属性", description="获取指定网络中所有水箱的属性") +async def fastapi_get_all_tank_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取网络中所有水箱的属性。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含所有水箱属性的字典列表 + """ # 缓存查询结果提高性能 # global redis_client results = get_all_tanks(network) return results -@router.post("/settankproperties/", response_model=None) +@router.post("/settankproperties/", summary="设置水箱属性", description="批量设置指定水箱的多个属性", response_model=None) async def fastapi_set_tank_properties( - network: str, tank: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + tank: str = Query(..., description="水箱ID"), + req: Request = Body(..., description="包含要设置的属性的请求体") ) -> ChangeSet: + """ + 批量设置水箱的属性。 + + Args: + network: 管网名称(或数据库名称) + tank: 水箱ID + req: 包含水箱属性的请求体(JSON格式) + + Returns: + 包含变更信息的ChangeSet对象 + """ props = await req.json() ps = {"id": tank} | props return set_tank(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/network/valves.py b/app/api/v1/endpoints/network/valves.py index 5b7f258..3ee3b19 100644 --- a/app/api/v1/endpoints/network/valves.py +++ b/app/api/v1/endpoints/network/valves.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query, Path, Body from typing import Any, List, Dict, Union from app.services.tjnetwork import ( Any, @@ -14,21 +14,42 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getvalveschema") -async def fastapi_get_valve_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get( + "/getvalveschema", + summary="获取阀门架构", + description="获取指定水网中所有阀门的架构和字段定义", +) +async def fastapi_get_valve_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取阀门架构。 + + 返回指定水网中所有阀门类型的完整架构定义,包括字段名称、类型和默认值。 + """ return get_valve_schema(network) -@router.post("/addvalve/", response_model=None) +@router.post( + "/addvalve/", + response_model=None, + summary="添加阀门", + description="在指定的水网中添加新的阀门", +) async def fastapi_add_valve( - network: str, - valve: str, - node1: str, - node2: str, - diameter: float = 0, - v_type: str = VALVES_TYPE_PRV, - setting: float = 0, - minor_loss: float = 0, + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + node1: str = Query(..., description="起点节点ID"), + node2: str = Query(..., description="终点节点ID"), + diameter: float = Query(0, description="阀门直径(mm)"), + v_type: str = Query(VALVES_TYPE_PRV, description="阀门类型"), + setting: float = Query(0, description="阀门开度/设置值"), + minor_loss: float = Query(0, description="损失系数"), ) -> ChangeSet: + """ + 添加新的阀门。 + + 在指定的水网中创建一个新的阀门,设置其连接的两个节点、直径、类型、开度和损失系数。 + """ ps = { "id": valve, "node1": node1, @@ -41,85 +62,271 @@ async def fastapi_add_valve( return add_valve(network, ChangeSet(ps)) -@router.post("/deletevalve/", response_model=None) -async def fastapi_delete_valve(network: str, valve: str) -> ChangeSet: +@router.post( + "/deletevalve/", + response_model=None, + summary="删除阀门", + description="从指定的水网中删除指定的阀门", +) +async def fastapi_delete_valve( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> ChangeSet: + """ + 删除阀门。 + + 从指定的水网中删除指定ID的阀门。 + """ ps = {"id": valve} return delete_valve(network, ChangeSet(ps)) -@router.get("/getvalvenode1/") -async def fastapi_get_valve_node1(network: str, valve: str) -> str | None: +@router.get( + "/getvalvenode1/", + summary="获取阀门起点节点", + description="获取指定阀门连接的起点节点ID", +) +async def fastapi_get_valve_node1( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> str | None: + """ + 获取阀门的起点节点。 + + 返回指定阀门连接的起点(第一个)节点的ID。 + """ ps = get_valve(network, valve) return ps["node1"] -@router.get("/getvalvenode2/") -async def fastapi_get_valve_node2(network: str, valve: str) -> str | None: +@router.get( + "/getvalvenode2/", + summary="获取阀门终点节点", + description="获取指定阀门连接的终点节点ID", +) +async def fastapi_get_valve_node2( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> str | None: + """ + 获取阀门的终点节点。 + + 返回指定阀门连接的终点(第二个)节点的ID。 + """ ps = get_valve(network, valve) return ps["node2"] -@router.get("/getvalvediameter/") -async def fastapi_get_valve_diameter(network: str, valve: str) -> float | None: +@router.get( + "/getvalvediameter/", + summary="获取阀门直径", + description="获取指定阀门的直径", +) +async def fastapi_get_valve_diameter( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> float | None: + """ + 获取阀门的直径。 + + 返回指定阀门的直径值(单位:mm)。 + """ ps = get_valve(network, valve) return ps["diameter"] -@router.get("/getvalvetype/") -async def fastapi_get_valve_type(network: str, valve: str) -> str | None: +@router.get( + "/getvalvetype/", + summary="获取阀门类型", + description="获取指定阀门的类型", +) +async def fastapi_get_valve_type( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> str | None: + """ + 获取阀门的类型。 + + 返回指定阀门的类型(例如:减压阀、调节阀等)。 + """ ps = get_valve(network, valve) return ps["type"] -@router.get("/getvalvesetting/") -async def fastapi_get_valve_setting(network: str, valve: str) -> float | None: +@router.get( + "/getvalvesetting/", + summary="获取阀门开度", + description="获取指定阀门的开度/设置值", +) +async def fastapi_get_valve_setting( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> float | None: + """ + 获取阀门的开度。 + + 返回指定阀门的开度/设置值。 + """ ps = get_valve(network, valve) return ps["setting"] -@router.get("/getvalveminorloss/") -async def fastapi_get_valve_minor_loss(network: str, valve: str) -> float | None: +@router.get( + "/getvalveminorloss/", + summary="获取阀门损失系数", + description="获取指定阀门的损失系数", +) +async def fastapi_get_valve_minor_loss( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> float | None: + """ + 获取阀门的损失系数。 + + 返回指定阀门的损失系数值,用于计算流体通过阀门的压力损失。 + """ ps = get_valve(network, valve) return ps["minor_loss"] -@router.post("/setvalvenode1/", response_model=None) -async def fastapi_set_valve_node1(network: str, valve: str, node1: str) -> ChangeSet: +@router.post( + "/setvalvenode1/", + response_model=None, + summary="设置阀门起点节点", + description="设置指定阀门的起点节点", +) +async def fastapi_set_valve_node1( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + node1: str = Query(..., description="新的起点节点ID"), +) -> ChangeSet: + """ + 设置阀门的起点节点。 + + 更新指定阀门的起点节点连接。 + """ ps = {"id": valve, "node1": node1} return set_valve(network, ChangeSet(ps)) -@router.post("/setvalvenode2/", response_model=None) -async def fastapi_set_valve_node2(network: str, valve: str, node2: str) -> ChangeSet: +@router.post( + "/setvalvenode2/", + response_model=None, + summary="设置阀门终点节点", + description="设置指定阀门的终点节点", +) +async def fastapi_set_valve_node2( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + node2: str = Query(..., description="新的终点节点ID"), +) -> ChangeSet: + """ + 设置阀门的终点节点。 + + 更新指定阀门的终点节点连接。 + """ ps = {"id": valve, "node2": node2} return set_valve(network, ChangeSet(ps)) -@router.post("/setvalvenodediameter/", response_model=None) +@router.post( + "/setvalvenodediameter/", + response_model=None, + summary="设置阀门直径", + description="设置指定阀门的直径", +) async def fastapi_set_valve_diameter( - network: str, valve: str, diameter: float + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + diameter: float = Query(..., description="新的直径值(mm)"), ) -> ChangeSet: + """ + 设置阀门的直径。 + + 更新指定阀门的直径值。 + """ ps = {"id": valve, "diameter": diameter} return set_valve(network, ChangeSet(ps)) -@router.post("/setvalvetype/", response_model=None) -async def fastapi_set_valve_type(network: str, valve: str, type: str) -> ChangeSet: +@router.post( + "/setvalvetype/", + response_model=None, + summary="设置阀门类型", + description="设置指定阀门的类型", +) +async def fastapi_set_valve_type( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + type: str = Query(..., description="新的阀门类型"), +) -> ChangeSet: + """ + 设置阀门的类型。 + + 更新指定阀门的类型(例如:减压阀、调节阀等)。 + """ ps = {"id": valve, "type": type} return set_valve(network, ChangeSet(ps)) -@router.post("/setvalvesetting/", response_model=None) +@router.post( + "/setvalvesetting/", + response_model=None, + summary="设置阀门开度", + description="设置指定阀门的开度/设置值", +) async def fastapi_set_valve_setting( - network: str, valve: str, setting: float + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + setting: float = Query(..., description="新的开度值"), ) -> ChangeSet: + """ + 设置阀门的开度。 + + 更新指定阀门的开度/设置值。 + """ ps = {"id": valve, "setting": setting} return set_valve(network, ChangeSet(ps)) -@router.get("/getvalveproperties/") -async def fastapi_get_valve_properties(network: str, valve: str) -> dict[str, Any]: +@router.get( + "/getvalveproperties/", + summary="获取阀门所有属性", + description="获取指定阀门的所有属性", +) +async def fastapi_get_valve_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), +) -> dict[str, Any]: + """ + 获取阀门的所有属性。 + + 返回指定阀门的完整属性集合,包括ID、节点、直径、类型、开度和损失系数。 + """ return get_valve(network, valve) -@router.get("/getallvalveproperties/") -async def fastapi_get_all_valve_properties(network: str) -> list[dict[str, Any]]: +@router.get( + "/getallvalveproperties/", + summary="获取所有阀门属性", + description="获取指定水网中所有阀门的属性", +) +async def fastapi_get_all_valve_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取所有阀门的属性。 + + 返回指定水网中所有阀门的完整属性列表。 + """ # 缓存查询结果提高性能 # global redis_client results = get_all_valves(network) return results -@router.post("/setvalveproperties/", response_model=None) +@router.post( + "/setvalveproperties/", + response_model=None, + summary="批量设置阀门属性", + description="批量设置指定阀门的多个属性", +) async def fastapi_set_valve_properties( - network: str, valve: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + valve: str = Query(..., description="阀门ID"), + req: Request = Body(..., description="请求体,包含要更新的属性"), ) -> ChangeSet: + """ + 批量设置阀门的属性。 + + 更新指定阀门的一个或多个属性,通过JSON请求体传递要更新的属性。 + """ props = await req.json() ps = {"id": valve} | props return set_valve(network, ChangeSet(ps)) diff --git a/app/api/v1/endpoints/project.py b/app/api/v1/endpoints/project.py index 8faabe7..46ed209 100644 --- a/app/api/v1/endpoints/project.py +++ b/app/api/v1/endpoints/project.py @@ -1,7 +1,7 @@ import json -from fastapi import APIRouter, Request, HTTPException +from fastapi import APIRouter, Request, HTTPException, Query, Path, Body from fastapi.responses import PlainTextResponse -from typing import Any, Dict +from typing import Any, Dict, List import app.services.project_info as project_info from app.infra.db.postgresql.database import get_database_instance as get_pg_db from app.infra.db.timescaledb.database import get_database_instance as get_ts_db @@ -39,30 +39,70 @@ inpDir = "data/" # Assuming data directory exists or is defined somewhere. router = APIRouter() lockedPrjs: Dict[str, str] = {} -@router.get("/listprojects/") +@router.get("/listprojects/", summary="获取项目列表", description="获取服务器上所有可用的供水管网项目名称列表。") async def list_projects_endpoint() -> list[str]: + """ + 获取项目列表 + + 返回所有已创建项目的名称列表。 + """ return list_project() -@router.get("/haveproject/") -async def have_project_endpoint(network: str): +@router.get("/haveproject/", summary="检查项目是否存在", description="检查指定名称的项目是否存在。") +async def have_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 检查项目是否存在 + + - **network**: 管网名称(或数据库名称) + """ return have_project(network) -@router.post("/createproject/") -async def create_project_endpoint(network: str): +@router.post("/createproject/", summary="创建新项目", description="创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。") +async def create_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 创建新项目 + + - **network**: 管网名称(或数据库名称) + """ create_project(network) return network -@router.post("/deleteproject/") -async def delete_project_endpoint(network: str): +@router.post("/deleteproject/", summary="删除项目", description="永久删除指定的供水管网项目。此操作不可恢复。") +async def delete_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 删除项目 + + - **network**: 管网名称(或数据库名称) + """ delete_project(network) return True -@router.get("/isprojectopen/") -async def is_project_open_endpoint(network: str): +@router.get("/isprojectopen/", summary="检查项目是否已打开", description="检查指定项目是否已被加载到内存中。") +async def is_project_open_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 检查项目是否已打开 + + - **network**: 管网名称(或数据库名称) + """ return is_project_open(network) -@router.post("/openproject/") -async def open_project_endpoint(network: str): +@router.post("/openproject/", summary="打开项目", description="将指定项目加载到内存中,并初始化数据库连接池。") +async def open_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 打开项目 + + - **network**: 管网名称(或数据库名称) + """ open_project(network) # 尝试连接指定数据库 @@ -88,18 +128,43 @@ async def open_project_endpoint(network: str): return network -@router.post("/closeproject/") -async def close_project_endpoint(network: str): +@router.post("/closeproject/", summary="关闭项目", description="将指定项目从内存中卸载,释放资源。") +async def close_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 关闭项目 + + - **network**: 管网名称(或数据库名称) + """ close_project(network) return True -@router.post("/copyproject/") -async def copy_project_endpoint(source: str, target: str): +@router.post("/copyproject/", summary="复制项目", description="将现有项目复制为新项目。") +async def copy_project_endpoint( + source: str = Query(..., description="管网名称(或数据库名称)"), + target: str = Query(..., description="管网名称(或数据库名称)") +): + """ + 复制项目 + + - **source**: 管网名称(或数据库名称) + - **target**: 管网名称(或数据库名称) + """ copy_project(source, target) return True -@router.post("/importinp/") -async def import_inp_endpoint(network: str, req: Request): +@router.post("/importinp/", summary="导入 INP 文件内容", description="将 INP 格式的文本内容导入到指定项目中。") +async def import_inp_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = Body(..., description="包含 'inp' 字段的 JSON 对象") +): + """ + 导入 INP 文件内容 + + - **network**: 管网名称(或数据库名称) + - **req**: 请求体,需包含 `{"inp": "..."}` 结构 + """ jo_root = await req.json() inp_text = jo_root["inp"] ps = {"inp": inp_text} @@ -107,8 +172,17 @@ async def import_inp_endpoint(network: str, req: Request): print(ret) return ret -@router.get("/exportinp/", response_model=None) -async def export_inp_endpoint(network: str, version: str) -> ChangeSet: +@router.get("/exportinp/", response_model=None, summary="导出项目为 ChangeSet", description="导出项目的变更集 (ChangeSet),包含顶点、SCADA 元素、DMA、SA、VD 等信息。") +async def export_inp_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + version: str = Query(..., description="版本号 (通常用于增量更新)") +) -> ChangeSet: + """ + 导出项目为 ChangeSet + + - **network**: 管网名称(或数据库名称) + - **version**: 版本号 + """ cs = export_inp(network, version) op = cs.operations[0] open_project(network) @@ -131,30 +205,75 @@ async def export_inp_endpoint(network: str, version: str) -> ChangeSet: return cs -@router.post("/readinp/") -async def read_inp_endpoint(network: str, inp: str) -> bool: +@router.post("/readinp/", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。") +async def read_inp_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + inp: str = Query(..., description="INP 文件名 (不包含路径)") +) -> bool: + """ + 读取 INP 文件到项目 + + - **network**: 管网名称(或数据库名称) + - **inp**: INP 文件名 + """ read_inp(network, inp) return True -@router.get("/dumpinp/") -async def dump_inp_endpoint(network: str, inp: str) -> bool: +@router.get("/dumpinp/", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。") +async def dump_inp_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + inp: str = Query(..., description="目标文件名") +) -> bool: + """ + 导出项目到 INP 文件 + + - **network**: 管网名称(或数据库名称) + - **inp**: 目标文件名 + """ dump_inp(network, inp) return True -@router.get("/isprojectlocked/") -async def is_project_locked_endpoint(network: str, req: Request): +@router.get("/isprojectlocked/", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。") +async def is_project_locked_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 检查项目是否被锁定 + + - **network**: 管网名称(或数据库名称) + """ return network in lockedPrjs.keys() -@router.get("/isprojectlockedbyme/") -async def is_project_locked_by_me_endpoint(network: str, req: Request): +@router.get("/isprojectlockedbyme/", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。") +async def is_project_locked_by_me_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 检查项目是否被当前用户锁定 + + - **network**: 管网名称(或数据库名称) + """ client_host = req.client.host return lockedPrjs.get(network) == client_host # 0 successfully locked # 1 already locked by you # 2 locked by others -@router.post("/lockproject/") -async def lock_project_endpoint(network: str, req: Request): +@router.post("/lockproject/", summary="锁定项目", description="锁定指定项目以防止并发修改。") +async def lock_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 锁定项目 + + 返回值: + - **0**: 锁定成功 + - **1**: 已被当前用户锁定 + - **2**: 已被其他用户锁定 + """ client_host = req.client.host if not network in lockedPrjs.keys(): lockedPrjs[network] = client_host @@ -165,8 +284,16 @@ async def lock_project_endpoint(network: str, req: Request): else: return 2 -@router.post("/unlockproject/") -def unlock_project_endpoint(network: str, req: Request): +@router.post("/unlockproject/", summary="解锁项目", description="释放对项目的锁定。") +def unlock_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 解锁项目 + + 只有锁定者才能解锁。 + """ client_host = req.client.host if lockedPrjs.get(network) == client_host: print("delete key") @@ -176,8 +303,17 @@ def unlock_project_endpoint(network: str, req: Request): return False # inp file operations -@router.post("/uploadinp/", status_code=status.HTTP_200_OK) -async def fastapi_upload_inp(afile: bytes, name: str): +@router.post("/uploadinp/", status_code=status.HTTP_200_OK, summary="上传 INP 文件", description="上传 INP 文件到服务器数据目录。") +async def fastapi_upload_inp( + afile: bytes = Body(..., description="文件二进制内容"), + name: str = Query(..., description="保存的文件名") +): + """ + 上传 INP 文件 + + - **afile**: 文件内容 + - **name**: 文件名 + """ if not os.path.exists(inpDir): os.makedirs(inpDir, exist_ok=True) @@ -186,8 +322,16 @@ async def fastapi_upload_inp(afile: bytes, name: str): f.write(afile) return True -@router.get("/downloadinp/", status_code=status.HTTP_200_OK) -async def fastapi_download_inp(name: str, response: Response): +@router.get("/downloadinp/", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。") +async def fastapi_download_inp( + name: str = Query(..., description="文件名"), + response: Response = None +): + """ + 下载 INP 文件 + + - **name**: 文件名 + """ filePath = inpDir + name if os.path.exists(filePath): return FileResponse( @@ -198,8 +342,186 @@ async def fastapi_download_inp(name: str, response: Response): return True # DingZQ, 2024-12-28, convert v3 to v2 -@router.get("/convertv3tov2/", response_model=None) -async def fastapi_convert_v3_to_v2(req: Request) -> ChangeSet: +@router.get("/convertv3tov2/", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。") +async def fastapi_convert_v3_to_v2( + req: Request = Body(..., description="包含 'inp' 字段的 JSON 对象") +) -> ChangeSet: + """ + 转换 INP V3 为 V2 + + - **req**: 请求体,需包含 `{"inp": "..."}` 结构 + """ + network = "v3Tov2" + jo_root = await req.json() + inp = jo_root["inp"] + cs = convert_inp_v3_to_v2(inp) + op = cs.operations[0] + open_project(network) + op["vertex"] = json.dumps(get_all_vertices(network)) + op["scada"] = json.dumps(get_all_scada_elements(network)) + op["dma"] = json.dumps(get_all_district_metering_areas(network)) + op["sa"] = json.dumps(get_all_service_areas(network)) + op["vd"] = json.dumps(get_all_virtual_districts(network)) + op["legend"] = get_extension_data(network, "legend") + + db = get_extension_data(network, "scada_db") + print(db) + scada_db = "" + if db: + scada_db = db + print(scada_db) + op["scada_db"] = scada_db + + close_project(network) + + return cs + +@router.post("/readinp/", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。") +async def read_inp_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + inp: str = Query(..., description="INP 文件名 (不包含路径)") +) -> bool: + """ + 读取 INP 文件到项目 + + - **network**: 管网名称(或数据库名称) + - **inp**: INP 文件名 + """ + read_inp(network, inp) + return True + +@router.get("/dumpinp/", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。") +async def dump_inp_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + inp: str = Query(..., description="目标文件名") +) -> bool: + """ + 导出项目到 INP 文件 + + - **network**: 管网名称(或数据库名称) + - **inp**: 目标文件名 + """ + dump_inp(network, inp) + return True + +@router.get("/isprojectlocked/", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。") +async def is_project_locked_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 检查项目是否被锁定 + + - **network**: 管网名称(或数据库名称) + """ + return network in lockedPrjs.keys() + +@router.get("/isprojectlockedbyme/", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。") +async def is_project_locked_by_me_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 检查项目是否被当前用户锁定 + + - **network**: 管网名称(或数据库名称) + """ + client_host = req.client.host + return lockedPrjs.get(network) == client_host + +# 0 successfully locked +# 1 already locked by you +# 2 locked by others +@router.post("/lockproject/", summary="锁定项目", description="锁定指定项目以防止并发修改。") +async def lock_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 锁定项目 + + 返回值: + - **0**: 锁定成功 + - **1**: 已被当前用户锁定 + - **2**: 已被其他用户锁定 + """ + client_host = req.client.host + if not network in lockedPrjs.keys(): + lockedPrjs[network] = client_host + return 0 + else: + if lockedPrjs.get(network) == client_host: + return 1 + else: + return 2 + +@router.post("/unlockproject/", summary="解锁项目", description="释放对项目的锁定。") +def unlock_project_endpoint( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +): + """ + 解锁项目 + + 只有锁定者才能解锁。 + """ + client_host = req.client.host + if lockedPrjs.get(network) == client_host: + print("delete key") + del lockedPrjs[network] + return True + + return False + +# inp file operations +@router.post("/uploadinp/", status_code=status.HTTP_200_OK, summary="上传 INP 文件", description="上传 INP 文件到服务器数据目录。") +async def fastapi_upload_inp( + afile: bytes = Body(..., description="文件二进制内容"), + name: str = Query(..., description="保存的文件名") +): + """ + 上传 INP 文件 + + - **afile**: 文件内容 + - **name**: 文件名 + """ + if not os.path.exists(inpDir): + os.makedirs(inpDir, exist_ok=True) + + filePath = inpDir + str(name) + with open(filePath, "wb") as f: + f.write(afile) + return True + +@router.get("/downloadinp/", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。") +async def fastapi_download_inp( + name: str = Query(..., description="文件名"), + response: Response = None +): + """ + 下载 INP 文件 + + - **name**: 文件名 + """ + filePath = inpDir + name + if os.path.exists(filePath): + return FileResponse( + filePath, media_type="application/octet-stream", filename="inp.inp" + ) + else: + response.status_code = status.HTTP_400_BAD_REQUEST + return True + +# DingZQ, 2024-12-28, convert v3 to v2 +@router.get("/convertv3tov2/", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。") +async def fastapi_convert_v3_to_v2( + req: Request = Body(..., description="包含 'inp' 字段的 JSON 对象") +) -> ChangeSet: + """ + 转换 INP V3 为 V2 + + - **req**: 请求体,需包含 `{"inp": "..."}` 结构 + """ network = "v3Tov2" jo_root = await req.json() inp = jo_root["inp"] diff --git a/app/api/v1/endpoints/project_data.py b/app/api/v1/endpoints/project_data.py index 8e13f02..1d9a928 100644 --- a/app/api/v1/endpoints/project_data.py +++ b/app/api/v1/endpoints/project_data.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, Path, Query from psycopg import AsyncConnection import app.native.wndb as wndb @@ -12,15 +12,18 @@ router = APIRouter() async def get_database_connection( conn: AsyncConnection = Depends(get_project_pg_connection), ): + """获取数据库连接""" yield conn -@router.get("/scada-info") +@router.get("/scada-info", summary="获取SCADA信息", description="使用连接池查询所有SCADA信息") async def get_scada_info_with_connection( conn: AsyncConnection = Depends(get_database_connection), ): """ - 使用连接池查询所有SCADA信息 + 获取所有SCADA信息 + + 返回项目中所有的SCADA设备信息 """ try: _ = conn @@ -33,12 +36,14 @@ async def get_scada_info_with_connection( ) -@router.get("/scheme-list") +@router.get("/scheme-list", summary="获取方案列表", description="使用连接池查询所有方案信息") async def get_scheme_list_with_connection( conn: AsyncConnection = Depends(get_database_connection), ): """ - 使用连接池查询所有方案信息 + 获取所有方案信息 + + 返回项目中所有方案的详细信息 """ try: scheme_data = await SchemeRepository.get_schemes(conn) @@ -47,12 +52,14 @@ async def get_scheme_list_with_connection( raise HTTPException(status_code=500, detail=f"查询方案信息时发生错误: {str(e)}") -@router.get("/burst-locate-result") +@router.get("/burst-locate-result", summary="获取爆管定位结果", description="使用连接池查询所有爆管定位结果") async def get_burst_locate_result_with_connection( conn: AsyncConnection = Depends(get_database_connection), ): """ - 使用连接池查询所有爆管定位结果 + 获取所有爆管定位结果 + + 返回项目中所有的爆管定位分析结果 """ try: burst_data = await SchemeRepository.get_burst_locate_results(conn) @@ -63,13 +70,16 @@ async def get_burst_locate_result_with_connection( ) -@router.get("/burst-locate-result/{burst_incident}") +@router.get("/burst-locate-result/{burst_incident}", summary="按事件查询爆管定位结果", description="根据爆管事件ID查询对应的爆管定位结果") async def get_burst_locate_result_by_incident( - burst_incident: str, + burst_incident: str = Path(..., description="爆管事件ID"), conn: AsyncConnection = Depends(get_database_connection), ): """ - 根据 burst_incident 查询爆管定位结果 + 根据爆管事件ID查询爆管定位结果 + + 参数: + burst_incident: 爆管事件的唯一标识符 """ try: return await SchemeRepository.get_burst_locate_result_by_incident( diff --git a/app/api/v1/endpoints/risk.py b/app/api/v1/endpoints/risk.py index 43addb9..20a009c 100644 --- a/app/api/v1/endpoints/risk.py +++ b/app/api/v1/endpoints/risk.py @@ -1,5 +1,5 @@ from typing import Any, List, Dict -from fastapi import APIRouter +from fastapi import APIRouter, Query, Path from app.services.tjnetwork import ( get_pipe_risk_probability_now, get_pipe_risk_probability, @@ -10,35 +10,118 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getpiperiskprobabilitynow/") +@router.get( + "/getpiperiskprobabilitynow/", + summary="获取管道当前风险概率", + description="获取指定管道当前时刻的风险概率值" +) async def fastapi_get_pipe_risk_probability_now( - network: str, pipe_id: str + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe_id: str = Query(..., description="管道ID") ) -> dict[str, Any]: + """ + 获取管道当前风险概率。 + + 查询指定管道在当前时刻的风险概率值。 + + Args: + network: 管网名称(或数据库名称) + pipe_id: 管道ID + + Returns: + 包含风险概率信息的字典 + """ return get_pipe_risk_probability_now(network, pipe_id) -@router.get("/getpiperiskprobability/") +@router.get( + "/getpiperiskprobability/", + summary="获取管道风险概率历史", + description="获取指定管道的风险概率历史数据" +) async def fastapi_get_pipe_risk_probability( - network: str, pipe_id: str + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe_id: str = Query(..., description="管道ID") ) -> dict[str, Any]: + """ + 获取管道风险概率历史。 + + 查询指定管道的历史风险概率数据。 + + Args: + network: 管网名称(或数据库名称) + pipe_id: 管道ID + + Returns: + 包含风险概率历史的字典 + """ return get_pipe_risk_probability(network, pipe_id) -@router.get("/getpipesriskprobability/") +@router.get( + "/getpipesriskprobability/", + summary="批量获取多条管道风险概率", + description="批量获取多条管道的风险概率值" +) async def fastapi_get_pipes_risk_probability( - network: str, pipe_ids: str + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe_ids: str = Query(..., description="逗号分隔的管道ID列表") ) -> list[dict[str, Any]]: + """ + 批量获取多条管道风险概率。 + + 查询多条指定管道的风险概率值。 + + Args: + network: 管网名称(或数据库名称) + pipe_ids: 逗号分隔的管道ID列表(例如:pipe1,pipe2,pipe3) + + Returns: + 包含多条管道风险概率的列表 + """ pipeids = pipe_ids.split(",") return get_pipes_risk_probability(network, pipeids) -@router.get("/getnetworkpiperiskprobabilitynow/") +@router.get( + "/getnetworkpiperiskprobabilitynow/", + summary="获取整个网络的管道风险概率", + description="获取指定网络中所有管道的当前风险概率值" +) async def fastapi_get_network_pipe_risk_probability_now( - network: str, + network: str = Query(..., description="管网名称(或数据库名称)"), ) -> list[dict[str, Any]]: + """ + 获取整个网络的管道风险概率。 + + 查询指定网络中所有管道在当前时刻的风险概率值。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含网络内所有管道风险概率的列表 + """ return get_network_pipe_risk_probability_now(network) -@router.get("/getpiperiskprobabilitygeometries/") -async def fastapi_get_pipe_risk_probability_geometries(network: str) -> dict[str, Any]: +@router.get( + "/getpiperiskprobabilitygeometries/", + summary="获取管道风险几何信息", + description="获取指定网络中管道的风险相关几何数据" +) +async def fastapi_get_pipe_risk_probability_geometries( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, Any]: + """ + 获取管道风险几何信息。 + + 查询指定网络中管道的地理和风险相关的几何数据。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 包含几何信息和风险数据的字典 + """ return get_pipe_risk_probability_geometries(network) diff --git a/app/api/v1/endpoints/scada.py b/app/api/v1/endpoints/scada.py index dc871a6..29b8fee 100644 --- a/app/api/v1/endpoints/scada.py +++ b/app/api/v1/endpoints/scada.py @@ -1,5 +1,5 @@ from typing import Any -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query from app.services.tjnetwork import ( ChangeSet, get_scada_info, @@ -31,139 +31,497 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getscadaproperties/") -async def fast_get_scada_properties(network: str, scada: str) -> dict[str, Any]: +@router.get("/getscadaproperties/", summary="获取SCADA属性", tags=["SCADA基础"]) +async def fast_get_scada_properties( + network: str = Query(..., description="管网名称(或数据库名称)"), + scada: str = Query(..., description="SCADA设备ID") +) -> dict[str, Any]: + """ + 获取单个SCADA设备的属性信息 + + 根据管网名称和SCADA设备ID获取该设备的完整属性。 + + Args: + network: 管网名称(或数据库名称) + scada: SCADA设备ID + + Returns: + SCADA设备的属性字典 + """ return get_scada_info(network, scada) -@router.get("/getallscadaproperties/") -async def fast_get_all_scada_properties(network: str) -> list[dict[str, Any]]: +@router.get("/getallscadaproperties/", summary="获取所有SCADA属性", tags=["SCADA基础"]) +async def fast_get_all_scada_properties( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取指定管网所有SCADA设备的属性信息 + + 查询该管网下所有已配置的SCADA设备的属性列表。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA设备属性列表 + """ return get_all_scada_info(network) ############################################################ -# scada_device 29 +# scada_device 设备管理 ############################################################ -@router.get("/getscadadeviceschema/") -async def fastapi_get_scada_device_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getscadadeviceschema/", summary="获取SCADA设备架构", tags=["SCADA设备"]) +async def fastapi_get_scada_device_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取SCADA设备的数据架构 + + 返回SCADA设备表的字段定义和类型信息。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA设备的字段架构信息 + """ return get_scada_device_schema(network) -@router.get("/getscadadevice/") -async def fastapi_get_scada_device(network: str, id: str) -> dict[str, Any]: +@router.get("/getscadadevice/", summary="获取SCADA设备", tags=["SCADA设备"]) +async def fastapi_get_scada_device( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="SCADA设备ID") +) -> dict[str, Any]: + """ + 获取单个SCADA设备的信息 + + 根据设备ID查询该设备的详细信息。 + + Args: + network: 管网名称(或数据库名称) + id: SCADA设备ID + + Returns: + SCADA设备信息 + """ return get_scada_device(network, id) -@router.post("/setscadadevice/", response_model=None) -async def fastapi_set_scada_device(network: str, req: Request) -> ChangeSet: +@router.post("/setscadadevice/", response_model=None, summary="更新SCADA设备", tags=["SCADA设备"]) +async def fastapi_set_scada_device( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 更新SCADA设备信息 + + 修改指定SCADA设备的属性。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含要更新的设备属性 + + Returns: + 变更集合信息 + """ props = await req.json() return set_scada_device(network, ChangeSet(props)) -@router.post("/addscadadevice/", response_model=None) -async def fastapi_add_scada_device(network: str, req: Request) -> ChangeSet: +@router.post("/addscadadevice/", response_model=None, summary="添加SCADA设备", tags=["SCADA设备"]) +async def fastapi_add_scada_device( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 添加新的SCADA设备 + + 在指定管网中添加一个新的SCADA设备。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含新设备的属性 + + Returns: + 变更集合信息 + """ props = await req.json() return add_scada_device(network, ChangeSet(props)) -@router.post("/deletescadadevice/", response_model=None) -async def fastapi_delete_scada_device(network: str, req: Request) -> ChangeSet: +@router.post("/deletescadadevice/", response_model=None, summary="删除SCADA设备", tags=["SCADA设备"]) +async def fastapi_delete_scada_device( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 删除SCADA设备 + + 从指定管网中删除一个SCADA设备。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含要删除的设备ID + + Returns: + 变更集合信息 + """ props = await req.json() return delete_scada_device(network, ChangeSet(props)) -@router.post("/cleanscadadevice/", response_model=None) -async def fastapi_clean_scada_device(network: str) -> ChangeSet: +@router.post("/cleanscadadevice/", response_model=None, summary="清空SCADA设备表", tags=["SCADA设备"]) +async def fastapi_clean_scada_device( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> ChangeSet: + """ + 清空SCADA设备表 + + 删除指定管网中所有的SCADA设备。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 变更集合信息 + """ return clean_scada_device(network) -@router.get("/getallscadadeviceids/") -async def fastapi_get_all_scada_device_ids(network: str) -> list[str]: +@router.get("/getallscadadeviceids/", summary="获取所有SCADA设备ID", tags=["SCADA设备"]) +async def fastapi_get_all_scada_device_ids( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[str]: + """ + 获取指定管网所有SCADA设备的ID列表 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA设备ID列表 + """ return get_all_scada_device_ids(network) -@router.get("/getallscadadevices/") -async def fastapi_get_all_scada_devices(network: str) -> list[dict[str, Any]]: +@router.get("/getallscadadevices/", summary="获取所有SCADA设备", tags=["SCADA设备"]) +async def fastapi_get_all_scada_devices( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取指定管网所有SCADA设备的完整信息 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA设备信息列表 + """ return get_all_scada_devices(network) ############################################################ -# scada_device_data 30 +# scada_device_data 设备数据管理 ############################################################ -@router.get("/getscadadevicedataschema/") +@router.get("/getscadadevicedataschema/", summary="获取SCADA设备数据架构", tags=["SCADA设备数据"]) async def fastapi_get_scada_device_data_schema( - network: str, + network: str = Query(..., description="管网名称(或数据库名称)"), ) -> dict[str, dict[str, Any]]: + """ + 获取SCADA设备数据的表结构 + + 返回SCADA设备数据表的字段定义和类型信息。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA设备数据的字段架构信息 + """ return get_scada_device_data_schema(network) -@router.get("/getscadadevicedata/") -async def fastapi_get_scada_device_data(network: str, device_id: str) -> dict[str, Any]: +@router.get("/getscadadevicedata/", summary="获取SCADA设备数据", tags=["SCADA设备数据"]) +async def fastapi_get_scada_device_data( + network: str = Query(..., description="管网名称(或数据库名称)"), + device_id: str = Query(..., description="SCADA设备ID") +) -> dict[str, Any]: + """ + 获取单个SCADA设备的数据 + + 查询指定设备的监测数据或配置数据。 + + Args: + network: 管网名称(或数据库名称) + device_id: SCADA设备ID + + Returns: + SCADA设备数据 + """ return get_scada_device_data(network, device_id) -@router.post("/setscadadevicedata/", response_model=None) -async def fastapi_set_scada_device_data(network: str, req: Request) -> ChangeSet: +@router.post("/setscadadevicedata/", response_model=None, summary="更新SCADA设备数据", tags=["SCADA设备数据"]) +async def fastapi_set_scada_device_data( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 更新SCADA设备数据 + + 修改指定SCADA设备的数据。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含要更新的数据 + + Returns: + 变更集合信息 + """ props = await req.json() return set_scada_device_data(network, ChangeSet(props)) -@router.post("/addscadadevicedata/", response_model=None) -async def fastapi_add_scada_device_data(network: str, req: Request) -> ChangeSet: +@router.post("/addscadadevicedata/", response_model=None, summary="添加SCADA设备数据", tags=["SCADA设备数据"]) +async def fastapi_add_scada_device_data( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 添加新的SCADA设备数据 + + 为指定SCADA设备添加新的数据记录。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含新数据的内容 + + Returns: + 变更集合信息 + """ props = await req.json() return add_scada_device_data(network, ChangeSet(props)) -@router.post("/deletescadadevicedata/", response_model=None) -async def fastapi_delete_scada_device_data(network: str, req: Request) -> ChangeSet: +@router.post("/deletescadadevicedata/", response_model=None, summary="删除SCADA设备数据", tags=["SCADA设备数据"]) +async def fastapi_delete_scada_device_data( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 删除SCADA设备数据 + + 删除指定SCADA设备的数据记录。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含要删除的数据ID + + Returns: + 变更集合信息 + """ props = await req.json() return delete_scada_device_data(network, ChangeSet(props)) -@router.post("/cleanscadadevicedata/", response_model=None) -async def fastapi_clean_scada_device_data(network: str) -> ChangeSet: +@router.post("/cleanscadadevicedata/", response_model=None, summary="清空SCADA设备数据表", tags=["SCADA设备数据"]) +async def fastapi_clean_scada_device_data( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> ChangeSet: + """ + 清空SCADA设备数据表 + + 删除指定管网中所有SCADA设备的数据。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 变更集合信息 + """ return clean_scada_device_data(network) ############################################################ -# scada_element 31 +# scada_element SCADA元素映射 ############################################################ -@router.get("/getscadaelementschema/") +@router.get("/getscadaelementschema/", summary="获取SCADA元素架构", tags=["SCADA元素映射"]) async def fastapi_get_scada_element_schema( - network: str, + network: str = Query(..., description="管网名称(或数据库名称)"), ) -> dict[str, dict[str, Any]]: + """ + 获取SCADA元素映射的表结构 + + 返回SCADA元素映射表的字段定义和类型信息。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA元素映射的字段架构信息 + """ return get_scada_element_schema(network) -@router.get("/getscadaelements/") -async def fastapi_get_scada_elements(network: str) -> list[dict[str, Any]]: +@router.get("/getscadaelements/", summary="获取所有SCADA元素映射", tags=["SCADA元素映射"]) +async def fastapi_get_scada_elements( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取指定管网所有SCADA元素映射 + + 查询所有SCADA设备与管网元素(节点/管道)的映射关系。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA元素映射列表 + """ return get_all_scada_elements(network) -@router.get("/getscadaelement/") -async def fastapi_get_scada_element(network: str, id: str) -> dict[str, Any]: +@router.get("/getscadaelement/", summary="获取单个SCADA元素映射", tags=["SCADA元素映射"]) +async def fastapi_get_scada_element( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="SCADA元素映射ID") +) -> dict[str, Any]: + """ + 获取单个SCADA元素映射的信息 + + 根据ID查询特定的SCADA设备与管网元素的映射关系。 + + Args: + network: 管网名称(或数据库名称) + id: SCADA元素映射ID + + Returns: + SCADA元素映射信息 + """ return get_scada_element(network, id) -@router.post("/setscadaelement/", response_model=None) -async def fastapi_set_scada_element(network: str, req: Request) -> ChangeSet: +@router.post("/setscadaelement/", response_model=None, summary="更新SCADA元素映射", tags=["SCADA元素映射"]) +async def fastapi_set_scada_element( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 更新SCADA元素映射 + + 修改SCADA设备与管网元素的映射关系。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含要更新的映射信息 + + Returns: + 变更集合信息 + """ props = await req.json() return set_scada_element(network, ChangeSet(props)) -@router.post("/addscadaelement/", response_model=None) -async def fastapi_add_scada_element(network: str, req: Request) -> ChangeSet: +@router.post("/addscadaelement/", response_model=None, summary="添加SCADA元素映射", tags=["SCADA元素映射"]) +async def fastapi_add_scada_element( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 添加新的SCADA元素映射 + + 创建SCADA设备与管网元素的新映射关系。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含新映射的信息 + + Returns: + 变更集合信息 + """ props = await req.json() return add_scada_element(network, ChangeSet(props)) -@router.post("/deletescadaelement/", response_model=None) -async def fastapi_delete_scada_element(network: str, req: Request) -> ChangeSet: +@router.post("/deletescadaelement/", response_model=None, summary="删除SCADA元素映射", tags=["SCADA元素映射"]) +async def fastapi_delete_scada_element( + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None +) -> ChangeSet: + """ + 删除SCADA元素映射 + + 移除SCADA设备与管网元素的映射关系。 + + Args: + network: 管网名称(或数据库名称) + req: 请求体,包含要删除的映射ID + + Returns: + 变更集合信息 + """ props = await req.json() return delete_scada_element(network, ChangeSet(props)) -@router.post("/cleanscadaelement/", response_model=None) -async def fastapi_clean_scada_element(network: str) -> ChangeSet: +@router.post("/cleanscadaelement/", response_model=None, summary="清空SCADA元素映射表", tags=["SCADA元素映射"]) +async def fastapi_clean_scada_element( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> ChangeSet: + """ + 清空SCADA元素映射表 + + 删除指定管网中所有的SCADA元素映射。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + 变更集合信息 + """ return clean_scada_element(network) ############################################################ -# scada_info 38 +# scada_info SCADA信息 ############################################################ -@router.get("/getscadainfoschema/") -async def fastapi_get_scada_info_schema(network: str) -> dict[str, dict[str, Any]]: +@router.get("/getscadainfoschema/", summary="获取SCADA信息架构", tags=["SCADA信息"]) +async def fastapi_get_scada_info_schema( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> dict[str, dict[str, Any]]: + """ + 获取SCADA信息表的结构 + + 返回SCADA信息表的字段定义和类型信息。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA信息的字段架构信息 + """ return get_scada_info_schema(network) -@router.get("/getscadainfo/") -async def fastapi_get_scada_info(network: str, id: str) -> dict[str, Any]: +@router.get("/getscadainfo/", summary="获取SCADA信息", tags=["SCADA信息"]) +async def fastapi_get_scada_info( + network: str = Query(..., description="管网名称(或数据库名称)"), + id: str = Query(..., description="SCADA信息ID") +) -> dict[str, Any]: + """ + 获取单个SCADA信息 + + 根据ID查询SCADA的详细配置信息。 + + Args: + network: 管网名称(或数据库名称) + id: SCADA信息ID + + Returns: + SCADA信息详情 + """ return get_scada_info(network, id) -@router.get("/getallscadainfo/") -async def fastapi_get_all_scada_info(network: str) -> list[dict[str, Any]]: +@router.get("/getallscadainfo/", summary="获取所有SCADA信息", tags=["SCADA信息"]) +async def fastapi_get_all_scada_info( + network: str = Query(..., description="管网名称(或数据库名称)") +) -> list[dict[str, Any]]: + """ + 获取指定管网所有SCADA的信息 + + 查询该管网下所有已配置的SCADA的完整信息。 + + Args: + network: 管网名称(或数据库名称) + + Returns: + SCADA信息列表 + """ return get_all_scada_info(network) diff --git a/app/api/v1/endpoints/schemes.py b/app/api/v1/endpoints/schemes.py index 0e5cb02..3b650e4 100644 --- a/app/api/v1/endpoints/schemes.py +++ b/app/api/v1/endpoints/schemes.py @@ -1,17 +1,32 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Query from typing import Any, List, Dict from app.services.tjnetwork import get_scheme_schema, get_scheme, get_all_schemes router = APIRouter() -@router.get("/getschemeschema/") -async def fastapi_get_scheme_schema(network: str) -> dict[str, dict[Any, Any]]: +@router.get("/getschemeschema/", summary="获取方案模式", description="获取指定网络的方案模式定义") +async def fastapi_get_scheme_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]: + """ + 获取方案模式定义 + + 返回指定网络的方案模式结构定义 + """ return get_scheme_schema(network) -@router.get("/getscheme/") -async def fastapi_get_scheme(network: str, schema_name: str) -> dict[Any, Any]: +@router.get("/getscheme/", summary="获取单个方案", description="根据名称获取指定的方案信息") +async def fastapi_get_scheme(network: str = Query(..., description="管网名称(或数据库名称)"), schema_name: str = Query(..., description="方案名称")) -> dict[Any, Any]: + """ + 获取单个方案详情 + + 返回指定网络中指定名称的方案详细信息 + """ return get_scheme(network, schema_name) -@router.get("/getallschemes/") -async def fastapi_get_all_schemes(network: str) -> list[dict[Any, Any]]: +@router.get("/getallschemes/", summary="获取所有方案", description="获取指定网络的所有方案信息") +async def fastapi_get_all_schemes(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]: + """ + 获取所有方案列表 + + 返回指定网络中所有可用的方案 + """ return get_all_schemes(network) diff --git a/app/api/v1/endpoints/simulation.py b/app/api/v1/endpoints/simulation.py index 56dea9f..2f77051 100644 --- a/app/api/v1/endpoints/simulation.py +++ b/app/api/v1/endpoints/simulation.py @@ -4,8 +4,7 @@ import json import os import shutil import threading -import pandas as pd -from fastapi import APIRouter, HTTPException, File, UploadFile, Query +from fastapi import APIRouter, HTTPException, File, UploadFile, Query, Path, Body from fastapi.responses import PlainTextResponse import app.infra.db.influxdb.api as influxdb_api import app.services.simulation as simulation @@ -39,77 +38,77 @@ from app.services.simulation_ops import ( daily_scheduling_simulation, ) from app.services.valve_isolation import analyze_valve_isolation -from pydantic import BaseModel +from pydantic import BaseModel, Field router = APIRouter() class RunSimulationManuallyByDate(BaseModel): - name: str - simulation_date: str - start_time: str - duration: int + name: str = Field(..., description="管网名称(或数据库名称)") + simulation_date: str = Field(..., description="模拟基准日期 (YYYY-MM-DD)") + start_time: str = Field(..., description="开始时间 (HH:MM 或 HH:MM:SS)") + duration: int = Field(..., description="持续时间 (分钟)") class BurstAnalysis(BaseModel): - name: str - modify_pattern_start_time: str - burst_ID: List[str] | str | None = None - burst_size: List[float] | float | int | None = None - modify_total_duration: int = 900 - modify_fixed_pump_pattern: Optional[dict[str, list]] = None - modify_variable_pump_pattern: Optional[dict[str, list]] = None - modify_valve_opening: Optional[dict[str, float]] = None - scheme_name: Optional[str] = None + name: str = Field(..., description="管网名称(或数据库名称)") + modify_pattern_start_time: str = Field(..., description="模式修改开始时间 (ISO 8601)") + burst_ID: List[str] | str | None = Field(None, description="爆管节点/管段ID列表") + burst_size: List[float] | float | int | None = Field(None, description="爆管流量大小") + modify_total_duration: int = Field(900, description="模拟总时长 (秒)") + modify_fixed_pump_pattern: Optional[dict[str, list]] = Field(None, description="定速泵模式修改") + modify_variable_pump_pattern: Optional[dict[str, list]] = Field(None, description="变速泵模式修改") + modify_valve_opening: Optional[dict[str, float]] = Field(None, description="阀门开度修改") + scheme_name: Optional[str] = Field(None, description="方案名称") class SchedulingAnalysis(BaseModel): - network: str - start_time: str - pump_control: dict - tank_id: str - water_plant_output_id: str - time_delta: Optional[int] = 300 + network: str = Field(..., description="管网名称(或数据库名称)") + start_time: str = Field(..., description="开始时间") + pump_control: dict = Field(..., description="泵控制策略") + tank_id: str = Field(..., description="水箱ID") + water_plant_output_id: str = Field(..., description="水厂出水ID") + time_delta: Optional[int] = Field(300, description="时间步长 (秒)") class PressureRegulation(BaseModel): - network: str - start_time: str - pump_control: dict - tank_init_level: Optional[dict] = None - duration: Optional[int] = 900 - scheme_name: Optional[str] = None + network: str = Field(..., description="管网名称(或数据库名称)") + start_time: str = Field(..., description="开始时间") + pump_control: dict = Field(..., description="泵控制策略") + tank_init_level: Optional[dict] = Field(None, description="水箱初始水位") + duration: Optional[int] = Field(900, description="持续时间 (秒)") + scheme_name: Optional[str] = Field(None, description="方案名称") class ProjectManagement(BaseModel): - network: str - start_time: str - pump_control: dict - tank_init_level: Optional[dict] = None - region_demand: Optional[dict] = None + network: str = Field(..., description="管网名称(或数据库名称)") + start_time: str = Field(..., description="开始时间") + pump_control: dict = Field(..., description="泵控制策略") + tank_init_level: Optional[dict] = Field(None, description="水箱初始水位") + region_demand: Optional[dict] = Field(None, description="区域需水量控制") class DailySchedulingAnalysis(BaseModel): - network: str - start_time: str - pump_control: dict - reservoir_id: str - tank_id: str - water_plant_output_id: str - time_delta: Optional[int] = 300 + network: str = Field(..., description="管网名称(或数据库名称)") + start_time: str = Field(..., description="开始时间") + pump_control: dict = Field(..., description="泵控制策略") + reservoir_id: str = Field(..., description="水库ID") + tank_id: str = Field(..., description="水箱ID") + water_plant_output_id: str = Field(..., description="水厂出水ID") + time_delta: Optional[int] = Field(300, description="时间步长 (秒)") class PumpFailureState(BaseModel): - time: str - pump_status: dict + time: str = Field(..., description="故障发生时间") + pump_status: dict = Field(..., description="泵状态字典") class PressureSensorPlacement(BaseModel): - name: str - scheme_name: str - sensor_number: int - min_diameter: int = 0 - username: str + 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( @@ -140,8 +139,15 @@ def run_simulation_manually_by_date( # 必须用这个PlainTextResponse,不然每个key都有引号 -@router.get("/runproject/", response_class=PlainTextResponse) -async def run_project_endpoint(network: str) -> str: +@router.get("/runproject/", response_class=PlainTextResponse, summary="运行项目模拟", description="基于指定的管网项目运行标准水力模拟,返回纯文本格式的模拟报告。使用分布式锁机制确保同时只有一个模拟任务运行。") +async def run_project_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> str: + """ + 运行项目模拟 + + - **network**: 管网名称(或数据库名称) + + 使用分布式锁机制确保同一时间只有一个模拟任务在运行。如果已有任务在运行,返回409状态码。 + """ lock_key = "exclusive_api_lock" timeout = 120 # 锁自动过期时间(秒) @@ -162,8 +168,19 @@ async def run_project_endpoint(network: str) -> str: # output 和 report # output 是 json # report 是 text -@router.get("/runprojectreturndict/") -async def run_project_return_dict_endpoint(network: str) -> dict[str, Any]: +@router.get("/runprojectreturndict/", summary="运行项目模拟(返回字典)", description="基于指定的管网项目运行标准水力模拟,返回JSON格式的字典,包含输出数据和报告文本。使用分布式锁机制确保同时只有一个模拟任务运行。") +async def run_project_return_dict_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]: + """ + 运行项目模拟(返回字典) + + - **network**: 管网名称(或数据库名称) + + 返回字典包含: + - output: JSON格式的模拟输出数据 + - report: 文本格式的模拟报告 + + 使用分布式锁机制确保同一时间只有一个模拟任务在运行。 + """ lock_key = "exclusive_api_lock" timeout = 120 # 锁自动过期时间(秒) @@ -181,34 +198,73 @@ async def run_project_return_dict_endpoint(network: str) -> dict[str, Any]: # put in inp folder, name without extension -@router.get("/runinp/") -async def run_inp_endpoint(network: str) -> str: +@router.get("/runinp/", summary="运行INP文件", description="运行指定INP文件格式的管网模型进行水力模拟。INP文件应该放在inp文件夹中,参数为文件名不含扩展名。") +async def run_inp_endpoint(network: str = Query(..., description="inp文件名(不含扩展名)")) -> str: + """ + 运行INP文件 + + - **network**: inp文件名(不含扩展名) + + 从inp文件夹中读取指定的INP文件并运行模拟。 + """ return run_inp(network) # path is absolute path -@router.get("/dumpoutput/") -async def dump_output_endpoint(output: str) -> str: +@router.get("/dumpoutput/", summary="导出模拟输出", description="导出指定路径的模拟输出文件内容。参数应为绝对路径。") +async def dump_output_endpoint(output: str = Query(..., description="模拟输出文件的绝对路径")) -> str: + """ + 导出模拟输出 + + - **output**: 模拟输出文件的绝对路径 + + 读取并返回指定路径的模拟输出内容。 + """ return dump_output(output) # Analysis Endpoints -@router.get("/burstanalysis/") +@router.get("/burstanalysis/", summary="爆管分析(基础)", description="对管网中的爆管事件进行分析,包括爆管对管网压力和流量的影响。此为基础版本,接收简化的查询参数。") async def burst_analysis_endpoint( - network: str, pipe_id: str, start_time: str, end_time: str, burst_flow: float + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe_id: str = Query(..., description="管段ID"), + start_time: str = Query(..., description="分析开始时间(ISO 8601格式)"), + end_time: str = Query(..., description="分析结束时间(ISO 8601格式)"), + burst_flow: float = Query(..., description="爆管流量大小(L/s)"), ): + """ + 爆管分析(基础版本) + + - **network**: 管网名称(或数据库名称) + - **pipe_id**: 管段ID + - **start_time**: 分析开始时间 + - **end_time**: 分析结束时间 + - **burst_flow**: 爆管流量大小 + """ return burst_analysis(network, pipe_id, start_time, end_time, burst_flow) -@router.get("/burst_analysis/") +@router.get("/burst_analysis/", summary="爆管分析(高级)", description="高级版本的爆管分析,支持在指定时间点修改泵控制模式和阀门开度,以分析这些改变对爆管影响的作用。支持固定泵和变速泵的独立控制。") async def fastapi_burst_analysis( - network: str = Query(...), - modify_pattern_start_time: str = Query(...), - burst_ID: list[str] = Query(...), - burst_size: list[float] = Query(...), - modify_total_duration: int = Query(...), - scheme_name: str = Query(...), + network: str = Query(..., description="管网名称(或数据库名称)"), + modify_pattern_start_time: str = Query(..., description="模式修改开始时间(ISO 8601格式)"), + burst_ID: list[str] = Query(..., description="爆管节点/管段ID列表"), + burst_size: list[float] = Query(..., description="对应各爆管点的爆管流量大小列表(L/s)"), + modify_total_duration: int = Query(..., description="模拟总时长(秒)"), + scheme_name: str = Query(..., description="分析方案名称"), ) -> str: + """ + 爆管分析(高级版本) + + - **network**: 管网名称(或数据库名称) + - **modify_pattern_start_time**: 模式修改开始时间 + - **burst_ID**: 爆管节点/管段ID列表 + - **burst_size**: 爆管流量大小列表(与burst_ID对应) + - **modify_total_duration**: 模拟总时长(秒) + - **scheme_name**: 分析方案名称 + + 支持在指定时间修改泵控制模式和阀门开度。 + """ burst_analysis( name=network, modify_pattern_start_time=modify_pattern_start_time, @@ -220,20 +276,41 @@ async def fastapi_burst_analysis( return "success" -@router.get("/valvecloseanalysis/") +@router.get("/valvecloseanalysis/", summary="阀门关闭分析(基础)", description="对管网中的阀门关闭事件进行分析,评估关闭阀门对管网的影响。此为基础版本。") async def valve_close_analysis_endpoint( - network: str, valve_id: str, start_time: str, end_time: str + network: str = Query(..., description="管网名称(或数据库名称)"), + valve_id: str = Query(..., description="阀门ID"), + start_time: str = Query(..., description="分析开始时间(ISO 8601格式)"), + end_time: str = Query(..., description="分析结束时间(ISO 8601格式)"), ): + """ + 阀门关闭分析(基础版本) + + - **network**: 管网名称(或数据库名称) + - **valve_id**: 阀门ID + - **start_time**: 分析开始时间 + - **end_time**: 分析结束时间 + """ return valve_close_analysis(network, valve_id, start_time, end_time) -@router.get("/valve_close_analysis/", response_class=PlainTextResponse) +@router.get("/valve_close_analysis/", response_class=PlainTextResponse, summary="阀门关闭分析(高级)", description="高级版本的阀门关闭分析,支持同时关闭多个阀门,并在指定持续时间内进行模拟。返回纯文本格式的分析结果。") async def fastapi_valve_close_analysis( - network: str, - start_time: str, - valves: List[str] = Query(...), - duration: int | None = None, + network: str = Query(..., description="管网名称(或数据库名称)"), + start_time: str = Query(..., description="阀门关闭开始时间(ISO 8601格式)"), + valves: List[str] = Query(..., description="要关闭的阀门ID列表"), + duration: int | None = Query(None, description="模拟持续时间(秒),默认900秒"), ) -> str: + """ + 阀门关闭分析(高级版本) + + - **network**: 管网名称(或数据库名称) + - **start_time**: 阀门关闭开始时间 + - **valves**: 要关闭的阀门ID列表 + - **duration**: 模拟持续时间(秒,可选,默认900) + + 支持同时关闭多个阀门进行分析。 + """ result = valve_close_analysis( name=network, modify_pattern_start_time=start_time, @@ -243,12 +320,25 @@ async def fastapi_valve_close_analysis( return result or "success" -@router.get("/valve_isolation_analysis/") +@router.get("/valve_isolation_analysis/", summary="阀门隔离分析", description="分析当发生突发事件时,通过关闭指定阀门进行隔离,确定哪些阀门必须关闭、哪些可选关闭,以及隔离的可行性。") async def valve_isolation_endpoint( - network: str, - accident_element: List[str] = Query(...), - disabled_valves: List[str] = Query(None), + network: str = Query(..., description="管网名称(或数据库名称)"), + accident_element: List[str] = Query(..., description="发生事故的管段/节点ID列表"), + disabled_valves: List[str] = Query(None, description="已故障的阀门ID列表(可选)"), ): + """ + 阀门隔离分析 + + - **network**: 管网名称(或数据库名称) + - **accident_element**: 发生事故的管段/节点ID列表 + - **disabled_valves**: 已故障的阀门ID列表(可选) + + 返回隔离方案,包括: + - must_close_valves: 必须关闭的阀门列表 + - optional_valves: 可选关闭的阀门列表 + - affected_nodes: 受影响的节点列表 + - isolatable: 是否可以有效隔离 + """ result = { "accident_element": "P461309", "accident_elements": ["P461309"], @@ -270,24 +360,51 @@ async def valve_isolation_endpoint( return result -@router.get("/flushinganalysis/") +@router.get("/flushinganalysis/", summary="冲洗分析(基础)", description="对管网的冲洗操作进行分析,评估冲洗流量和持续时间对管网的影响。此为基础版本。") async def flushing_analysis_endpoint( - network: str, pipe_id: str, start_time: str, duration: float, flow: float + network: str = Query(..., description="管网名称(或数据库名称)"), + pipe_id: str = Query(..., description="要冲洗的管段ID"), + start_time: str = Query(..., description="冲洗开始时间(ISO 8601格式)"), + duration: float = Query(..., description="冲洗持续时间(分钟)"), + flow: float = Query(..., description="冲洗流量(L/s)"), ): + """ + 冲洗分析(基础版本) + + - **network**: 管网名称(或数据库名称) + - **pipe_id**: 要冲洗的管段ID + - **start_time**: 冲洗开始时间 + - **duration**: 冲洗持续时间(分钟) + - **flow**: 冲洗流量(L/s) + """ return flushing_analysis(network, pipe_id, start_time, duration, flow) -@router.get("/flushing_analysis/", response_class=PlainTextResponse) +@router.get("/flushing_analysis/", response_class=PlainTextResponse, summary="冲洗分析(高级)", description="高级版本的冲洗分析,支持同时开启多个阀门进行冲洗,指定排污节点,并设置固定的冲洗流量。返回纯文本格式的分析结果。") async def fastapi_flushing_analysis( - network: str, - start_time: str, - valves: List[str] = Query(...), - valves_k: List[float] = Query(...), - drainage_node_ID: str = Query(...), - flush_flow: float = 0, - duration: int | None = None, - scheme_name: str | None = None, + network: str = Query(..., description="管网名称(或数据库名称)"), + start_time: str = Query(..., description="冲洗开始时间(ISO 8601格式)"), + valves: List[str] = Query(..., description="要开启的阀门ID列表"), + valves_k: List[float] = Query(..., description="对应各阀门的开度列表(0-1)"), + drainage_node_ID: str = Query(..., description="排污节点ID"), + flush_flow: float = Query(0, description="冲洗流量(L/s),0表示自动计算"), + duration: int | None = Query(None, description="模拟持续时间(秒),默认900秒"), + scheme_name: str | None = Query(None, description="冲洗方案名称(可选)"), ) -> str: + """ + 冲洗分析(高级版本) + + - **network**: 管网名称(或数据库名称) + - **start_time**: 冲洗开始时间 + - **valves**: 要开启的阀门ID列表 + - **valves_k**: 各阀门的开度列表(0-1,与valves对应) + - **drainage_node_ID**: 排污节点ID + - **flush_flow**: 冲洗流量(L/s) + - **duration**: 模拟持续时间(秒,可选,默认900) + - **scheme_name**: 冲洗方案名称(可选) + + 支持多阀联合冲洗操作。 + """ valve_opening = { valve_id: float(valves_k[idx]) for idx, valve_id in enumerate(valves) } @@ -303,16 +420,29 @@ async def fastapi_flushing_analysis( return result or "success" -@router.get("/contaminant_simulation/", response_class=PlainTextResponse) +@router.get("/contaminant_simulation/", response_class=PlainTextResponse, summary="污染物模拟", description="对管网中的污染物扩散进行模拟,评估污染源对管网的影响范围和浓度分布。支持指定污染源位置、污染浓度和扩散模式。") async def fastapi_contaminant_simulation( - network: str, - start_time: str, - source: str, - concentration: float, - duration: int, - scheme_name: str | None = None, - pattern: str | None = None, + network: str = Query(..., description="管网名称(或数据库名称)"), + start_time: str = Query(..., description="污染开始时间(ISO 8601格式)"), + source: str = Query(..., description="污染源节点ID"), + concentration: float = Query(..., description="污染浓度(mg/L)"), + duration: int = Query(..., description="模拟持续时间(秒)"), + scheme_name: str | None = Query(None, description="模拟方案名称(可选)"), + pattern: str | None = Query(None, description="污染源模式ID(可选)"), ) -> str: + """ + 污染物模拟 + + - **network**: 管网名称(或数据库名称) + - **start_time**: 污染开始时间 + - **source**: 污染源节点ID + - **concentration**: 污染浓度(mg/L) + - **duration**: 模拟持续时间(秒) + - **scheme_name**: 模拟方案名称(可选) + - **pattern**: 污染源模式ID(可选) + + 用于评估管网中污染物的传播和影响范围。 + """ result = contaminant_simulation( name=network, modify_pattern_start_time=start_time, @@ -325,15 +455,35 @@ async def fastapi_contaminant_simulation( return result or "success" -@router.get("/ageanalysis/") -async def age_analysis_endpoint(network: str): +@router.get("/ageanalysis/", summary="水龄分析(基础)", description="对管网中的水体停留时间(水龄)进行分析。此为基础版本。") +async def age_analysis_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")): + """ + 水龄分析(基础版本) + + - **network**: 管网名称(或数据库名称) + + 分析管网中各节点的水体停留时间。 + """ return age_analysis(network) -@router.get("/age_analysis/", response_class=PlainTextResponse) +@router.get("/age_analysis/", response_class=PlainTextResponse, summary="水龄分析(高级)", description="高级版本的水龄分析,在指定时间点进行分析,支持自定义模拟持续时间。返回纯文本格式的分析结果。") async def fastapi_age_analysis( - network: str, start_time: str, end_time: str, duration: int + network: str = Query(..., description="管网名称(或数据库名称)"), + start_time: str = Query(..., description="分析开始时间(ISO 8601格式)"), + end_time: str = Query(..., description="分析结束时间(ISO 8601格式)"), + duration: int = Query(..., description="模拟持续时间(秒)"), ) -> str: + """ + 水龄分析(高级版本) + + - **network**: 管网名称(或数据库名称) + - **start_time**: 分析开始时间 + - **end_time**: 分析结束时间(可选) + - **duration**: 模拟持续时间(秒) + + 分析指定时间段内管网中各节点的水体停留时间。 + """ result = age_analysis(network, start_time, duration) return result or "success" @@ -343,15 +493,39 @@ async def fastapi_age_analysis( # return scheduling_analysis(network) -@router.get("/pressureregulation/") +@router.get("/pressureregulation/", summary="压力调节(基础)", description="对管网的压力进行调节分析,通过控制泵的运行来维持目标节点的目标压力。此为基础版本。") async def pressure_regulation_endpoint( - network: str, target_node: str, target_pressure: float + network: str = Query(..., description="管网名称(或数据库名称)"), + target_node: str = Query(..., description="目标节点ID"), + target_pressure: float = Query(..., description="目标压力值(kPa)"), ): + """ + 压力调节(基础版本) + + - **network**: 管网名称(或数据库名称) + - **target_node**: 目标节点ID + - **target_pressure**: 目标压力值(kPa) + + 通过泵控制维持目标节点的压力。 + """ return pressure_regulation(network, target_node, target_pressure) -@router.post("/pressure_regulation/") -async def fastapi_pressure_regulation(data: PressureRegulation) -> str: +@router.post("/pressure_regulation/", summary="压力调节(高级)", description="高级版本的压力调节分析,通过JSON请求体提供详细的控制参数,包括固定泵和变速泵的独立控制、水箱初始水位等。") +async def fastapi_pressure_regulation(data: PressureRegulation = Body(..., description="压力调节控制参数")) -> str: + """ + 压力调节(高级版本) + + 请求体参数: + - **network**: 管网名称(或数据库名称) + - **start_time**: 控制开始时间 + - **pump_control**: 泵控制策略字典 + - **tank_init_level**: 水箱初始水位字典(可选) + - **duration**: 模拟持续时间(秒,可选,默认900) + - **scheme_name**: 控制方案名称(可选) + + 支持固定泵和变速泵的独立控制。 + """ item = data.dict() simulation.query_corresponding_element_id_and_query_id(item["network"]) fixed_pumps = set(globals.fixed_pumps_id.keys()) @@ -375,13 +549,32 @@ async def fastapi_pressure_regulation(data: PressureRegulation) -> str: return "success" -@router.get("/projectmanagement/") -async def project_management_endpoint(network: str): +@router.get("/projectmanagement/", summary="项目管理(基础)", description="对管网项目进行基础的管理操作。此为基础版本。") +async def project_management_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")): + """ + 项目管理(基础版本) + + - **network**: 管网名称(或数据库名称) + + 进行基础的项目管理操作。 + """ return project_management(network) -@router.post("/project_management/") -async def fastapi_project_management(data: ProjectManagement) -> str: +@router.post("/project_management/", summary="项目管理(高级)", description="高级版本的项目管理,通过JSON请求体提供详细的控制参数,包括泵控制策略、水箱初始水位和区域需水量控制。") +async def fastapi_project_management(data: ProjectManagement = Body(..., description="项目管理控制参数")) -> str: + """ + 项目管理(高级版本) + + 请求体参数: + - **network**: 管网名称(或数据库名称) + - **start_time**: 管理开始时间 + - **pump_control**: 泵控制策略字典 + - **tank_init_level**: 水箱初始水位字典(可选) + - **region_demand**: 区域需水量控制字典(可选) + + 支持多维度的项目管理。 + """ item = data.dict() return project_management( prj_name=item["network"], @@ -397,8 +590,21 @@ async def fastapi_project_management(data: ProjectManagement) -> str: # return daily_scheduling_analysis(network) -@router.post("/scheduling_analysis/") -async def fastapi_scheduling_analysis(data: SchedulingAnalysis) -> str: +@router.post("/scheduling_analysis/", summary="排程分析", description="对管网的供水排程进行分析,优化泵的运行时间和出水流量,平衡水厂出水、水箱进出水,满足用户需求。") +async def fastapi_scheduling_analysis(data: SchedulingAnalysis = Body(..., description="排程分析参数")) -> str: + """ + 排程分析 + + 请求体参数: + - **network**: 管网名称(或数据库名称) + - **start_time**: 分析开始时间 + - **pump_control**: 泵控制策略字典 + - **tank_id**: 水箱ID + - **water_plant_output_id**: 水厂出水ID + - **time_delta**: 时间步长(秒,可选,默认300) + + 用于优化供水排程。 + """ item = data.dict() return scheduling_simulation( item["network"], @@ -410,8 +616,22 @@ async def fastapi_scheduling_analysis(data: SchedulingAnalysis) -> str: ) -@router.post("/daily_scheduling_analysis/") -async def fastapi_daily_scheduling_analysis(data: DailySchedulingAnalysis) -> str: +@router.post("/daily_scheduling_analysis/", summary="日排程分析", description="对管网的每日供水排程进行分析,优化水库、水厂、水箱和用户需求的协调,制定合理的每日排程方案。") +async def fastapi_daily_scheduling_analysis(data: DailySchedulingAnalysis = Body(..., description="日排程分析参数")) -> str: + """ + 日排程分析 + + 请求体参数: + - **network**: 管网名称(或数据库名称) + - **start_time**: 分析开始时间 + - **pump_control**: 泵控制策略字典 + - **reservoir_id**: 水库ID + - **tank_id**: 水箱ID + - **water_plant_output_id**: 水厂出水ID + - **time_delta**: 时间步长(秒,可选,默认300) + + 用于制定每日供水排程方案。 + """ item = data.dict() return daily_scheduling_simulation( item["network"], @@ -423,8 +643,15 @@ async def fastapi_daily_scheduling_analysis(data: DailySchedulingAnalysis) -> st ) -@router.post("/network_project/") -async def fastapi_network_project(file: UploadFile = File()) -> str: +@router.post("/network_project/", summary="导入网络项目", description="通过上传INP格式的管网文件导入新的网络项目。系统将自动处理文件并执行模拟。") +async def fastapi_network_project(file: UploadFile = File(..., description="INP格式的管网文件")) -> str: + """ + 导入网络项目 + + - **file**: 上传的INP格式管网文件 + + 系统将上传的文件保存到inp文件夹并执行模拟。 + """ temp_file_dir = "./inp/" if not os.path.exists(temp_file_dir): os.mkdir(temp_file_dir) @@ -435,13 +662,27 @@ async def fastapi_network_project(file: UploadFile = File()) -> str: return run_inp(temp_file_name) -@router.get("/networkupdate/") -async def network_update_endpoint(network: str): +@router.get("/networkupdate/", summary="管网更新(基础)", description="对指定管网项目进行基础的更新操作。此为基础版本。") +async def network_update_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")): + """ + 管网更新(基础版本) + + - **network**: 管网名称(或数据库名称) + + 进行管网的基础更新操作。 + """ return network_update(network) -@router.post("/network_update/") -async def fastapi_network_update(file: UploadFile = File()) -> str: +@router.post("/network_update/", summary="管网更新(高级)", description="通过上传更新文件对管网进行高级的更新操作。系统将处理更新文件并应用到数据库。") +async def fastapi_network_update(file: UploadFile = File(..., description="包含管网更新信息的文件")) -> str: + """ + 管网更新(高级版本) + + - **file**: 包含管网更新信息的文件 + + 系统将处理上传的文件并应用管网更新。 + """ default_folder = "./" temp_file_name = f'network_update_{datetime.now().strftime("%Y%m%d")}' temp_file_path = os.path.join(default_folder, temp_file_name) @@ -459,8 +700,17 @@ async def fastapi_network_update(file: UploadFile = File()) -> str: # return pump_failure(network, pump_id, time) -@router.post("/pump_failure/") -async def fastapi_pump_failure(data: PumpFailureState) -> str: +@router.post("/pump_failure/", summary="泵故障管理", description="记录和管理泵的故障状态,包括故障发生时间和受影响的泵列表。系统将记录故障日志并更新泵状态。") +async def fastapi_pump_failure(data: PumpFailureState = Body(..., description="泵故障状态信息")) -> str: + """ + 泵故障管理 + + 请求体参数: + - **time**: 故障发生时间 + - **pump_status**: 泵状态字典,包含第一阶段和第二阶段泵的故障状态 + + 系统将验证泵信息的有效性并更新故障状态文件。 + """ item = data.dict() with open("./pump_failure_message.txt", "a", encoding="utf-8-sig") as f1: f1.write("[{}] {}\n".format(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), item)) @@ -494,19 +744,46 @@ async def fastapi_pump_failure(data: PumpFailureState) -> str: return json.dumps("SUCCESS") -@router.get("/pressuresensorplacementsensitivity/") +@router.get("/pressuresensorplacementsensitivity/", summary="压力传感器放置-灵敏度分析(基础)", description="基于灵敏度分析方法,为指定管网项目确定最优的压力传感器放置位置。此为基础版本。") async def pressure_sensor_placement_sensitivity_endpoint( - name: str, scheme_name: str, sensor_number: int, min_diameter: int, username: str + 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_sensitivity/") +@router.post("/pressure_sensor_placement_sensitivity/", summary="压力传感器放置-灵敏度分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于灵敏度分析方法确定最优放置位置。") async def fastapi_pressure_sensor_placement_sensitivity( - data: PressureSensorPlacement, + data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"), ) -> None: + """ + 压力传感器放置-灵敏度分析(高级版本) + + 请求体参数: + - **name**: 管网名称(或数据库名称) + - **scheme_name**: 放置方案名称 + - **sensor_number**: 传感器数量 + - **min_diameter**: 最小管径限制(毫米) + - **username**: 用户名 + + 基于灵敏度分析方法确定压力传感器的最优放置位置。 + """ item = data.dict() pressure_sensor_placement_sensitivity( name=item["name"], @@ -517,19 +794,46 @@ async def fastapi_pressure_sensor_placement_sensitivity( ) -@router.get("/pressuresensorplacementkmeans/") +@router.get("/pressuresensorplacementkmeans/", summary="压力传感器放置-KMeans聚类分析(基础)", description="基于KMeans聚类算法,为指定管网项目确定压力传感器的最优放置位置。此为基础版本。") async def pressure_sensor_placement_kmeans_endpoint( - name: str, scheme_name: str, sensor_number: int, min_diameter: int, username: str + 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/") +@router.post("/pressure_sensor_placement_kmeans/", summary="压力传感器放置-KMeans聚类分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于KMeans聚类算法确定最优放置位置。") async def fastapi_pressure_sensor_placement_kmeans( - data: PressureSensorPlacement, + 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"], @@ -540,16 +844,31 @@ async def fastapi_pressure_sensor_placement_kmeans( ) -@router.post("/sensorplacementscheme/create") +@router.post("/sensorplacementscheme/create", summary="传感器放置方案创建", description="创建新的传感器放置方案,支持灵敏度分析和KMeans聚类两种方法。根据指定的方法自动计算最优的传感器放置位置。") async def fastapi_pressure_sensor_placement( - network: str = Query(...), - scheme_name: str = Query(...), - sensor_type: str = Query(...), - method: str = Query(...), - sensor_count: int = Query(...), - min_diameter: int = Query(0), - user_name: str = Query(...), + 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'" @@ -573,67 +892,22 @@ async def fastapi_pressure_sensor_placement( return "success" -@router.post("/scadadevicedatacleaning/") -async def fastapi_scada_device_data_cleaning( - network: str = Query(...), - ids_list: List[str] = Query(...), - start_time: str = Query(...), - end_time: str = Query(...), - user_name: str = Query(...), -) -> str: - item = { - "network": network, - "ids": ids_list, - "start_time": start_time, - "end_time": end_time, - "user_name": user_name, - } - query_ids_list = item["ids"][0].split(",") - scada_data = influxdb_api.query_SCADA_data_by_device_ID_and_timerange( - query_ids_list=query_ids_list, - start_time=item["start_time"], - end_time=item["end_time"], - ) - scada_device_info = influxdb_api.query_pg_scada_info(item["network"]) - scada_device_info_dict = {info["id"]: info for info in scada_device_info} - type_groups: dict[str, list[str]] = {} - for device_id in query_ids_list: - device_info = scada_device_info_dict.get(device_id, {}) - device_type = device_info.get("type", "unknown") - type_groups.setdefault(device_type, []).append(device_id) - for device_type, device_ids in type_groups.items(): - if device_type not in ["pressure", "pipe_flow"]: - continue - type_scada_data = { - device_id: scada_data[device_id] - for device_id in device_ids - if device_id in scada_data - } - if not type_scada_data: - continue - time_list = [record["time"] for record in next(iter(type_scada_data.values()))] - df = pd.DataFrame({"time": time_list}) - for device_id in device_ids: - if device_id in type_scada_data: - values = [record["value"] for record in type_scada_data[device_id]] - df[device_id] = values - if device_type == "pressure": - cleaned_value_df = pressure_data_clean.clean_pressure_data_df_km(df) - elif device_type == "pipe_flow": - cleaned_value_df = flow_data_clean.clean_flow_data_df_kf(df) - cleaned_value_df = pd.DataFrame(cleaned_value_df) - cleaned_df = pd.concat([df["time"], cleaned_value_df], axis=1) - influxdb_api.import_multicolumn_data_from_dict( - data_dict=cleaned_df.to_dict("list"), - raw=False, - ) - return "success" - - -@router.post("/runsimulationmanuallybydate/") +@router.post("/runsimulationmanuallybydate/", summary="手动运行日期指定模拟", description="根据指定的日期、开始时间和持续时间,手动运行水力模拟。系统将自动查询管网参数并执行模拟。") async def fastapi_run_simulation_manually_by_date( - data: RunSimulationManuallyByDate, + data: RunSimulationManuallyByDate = Body(..., description="模拟运行参数"), ) -> dict[str, str]: + """ + 手动运行日期指定模拟 + + 请求体参数: + - **name**: 管网名称(或数据库名称) + - **simulation_date**: 模拟基准日期(YYYY-MM-DD格式) + - **start_time**: 开始时间(HH:MM或HH:MM:SS格式) + - **duration**: 模拟持续时间(分钟) + + 系统将从指定日期和时间开始,按15分钟间隔多次运行模拟。 + 每次模拟间隔15分钟,直至达到指定的总持续时间。 + """ item = data.dict() try: simulation.query_corresponding_element_id_and_query_id(item["name"]) diff --git a/app/api/v1/endpoints/snapshots.py b/app/api/v1/endpoints/snapshots.py index d8e8dd9..210f58e 100644 --- a/app/api/v1/endpoints/snapshots.py +++ b/app/api/v1/endpoints/snapshots.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query from app.services.tjnetwork import ( ChangeSet, get_current_operation, @@ -22,90 +22,183 @@ from app.services.tjnetwork import ( router = APIRouter() -@router.get("/getcurrentoperationid/") -async def get_current_operation_id_endpoint(network: str) -> int: +@router.get("/getcurrentoperationid/", summary="获取当前操作ID", description="获取网络当前的操作ID") +async def get_current_operation_id_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int: + """ + 获取当前操作ID + + 返回网络当前正在执行的操作ID + """ return get_current_operation(network) -@router.post("/undo/") -async def undo_endpoint(network: str): +@router.post("/undo/", summary="撤销操作", description="撤销网络上最后的一个操作") +async def undo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")): + """ + 撤销操作 + + 撤销网络上最近执行的一个操作 + """ return execute_undo(network) -@router.post("/redo/") -async def redo_endpoint(network: str): +@router.post("/redo/", summary="重做操作", description="重做网络上被撤销的操作") +async def redo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")): + """ + 重做操作 + + 重做网络上被撤销的操作 + """ return execute_redo(network) -@router.get("/getsnapshots/") -async def list_snapshot_endpoint(network: str) -> list[tuple[int, str]]: +@router.get("/getsnapshots/", summary="获取快照列表", description="获取网络中的所有快照") +async def list_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[tuple[int, str]]: + """ + 获取快照列表 + + 返回网络中所有可用的快照及其信息 + """ return list_snapshot(network) -@router.get("/havesnapshot/") -async def have_snapshot_endpoint(network: str, tag: str) -> bool: +@router.get("/havesnapshot/", summary="检查快照是否存在", description="检查指定标签的快照是否存在") +async def have_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> bool: + """ + 检查快照是否存在 + + 返回指定标签的快照是否存在 + """ return have_snapshot(network, tag) -@router.get("/havesnapshotforoperation/") -async def have_snapshot_for_operation_endpoint(network: str, operation: int) -> bool: +@router.get("/havesnapshotforoperation/", summary="检查操作快照是否存在", description="检查指定操作ID的快照是否存在") +async def have_snapshot_for_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> bool: + """ + 检查操作快照是否存在 + + 返回指定操作ID的快照是否存在 + """ return have_snapshot_for_operation(network, operation) -@router.get("/havesnapshotforcurrentoperation/") -async def have_snapshot_for_current_operation_endpoint(network: str) -> bool: +@router.get("/havesnapshotforcurrentoperation/", summary="检查当前操作快照是否存在", description="检查当前操作的快照是否存在") +async def have_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> bool: + """ + 检查当前操作快照是否存在 + + 返回当前操作的快照是否存在 + """ return have_snapshot_for_current_operation(network) -@router.post("/takesnapshotforoperation/") +@router.post("/takesnapshotforoperation/", summary="为操作创建快照", description="为指定的操作创建快照") async def take_snapshot_for_operation_endpoint( - network: str, operation: int, tag: str + network: str = Query(..., description="管网名称(或数据库名称)"), + operation: int = Query(..., description="操作ID"), + tag: str = Query(..., description="快照标签") ) -> None: + """ + 为操作创建快照 + + 为指定操作创建一个带标签的快照 + """ return take_snapshot_for_operation(network, operation, tag) -@router.post("/takesnapshotforcurrentoperation") -async def take_snapshot_for_current_operation_endpoint(network: str, tag: str) -> None: +@router.post("/takesnapshotforcurrentoperation", summary="为当前操作创建快照", description="为当前操作创建快照") +async def take_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None: + """ + 为当前操作创建快照 + + 为网络当前操作创建一个快照 + """ return take_snapshot_for_current_operation(network, tag) # 兼容旧拼写: takenapshotforcurrentoperation -@router.post("/takenapshotforcurrentoperation") -async def take_snapshot_for_current_operation_legacy_endpoint( - network: str, tag: str -) -> None: +@router.post("/takenapshotforcurrentoperation", summary="为当前操作创建快照(兼容模式)", description="为当前操作创建快照(兼容旧的API路径)") +async def take_snapshot_for_current_operation_legacy_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None: + """ + 为当前操作创建快照(兼容模式) + + 兼容旧的API路径,为网络当前操作创建一个快照 + """ return take_snapshot_for_current_operation(network, tag) -@router.post("/takesnapshot/") -async def take_snapshot_endpoint(network: str, tag: str) -> None: +@router.post("/takesnapshot/", summary="创建快照", description="为网络创建一个快照") +async def take_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None: + """ + 创建快照 + + 为网络创建一个带标签的快照 + """ return take_snapshot(network, tag) -@router.post("/picksnapshot/", response_model=None) -async def pick_snapshot_endpoint(network: str, tag: str, discard: bool = False) -> ChangeSet: +@router.post("/picksnapshot/", summary="选择快照", description="选择并恢复到指定的快照", response_model=None) +async def pick_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签"), discard: bool = Query(False, description="是否丢弃当前更改")) -> ChangeSet: + """ + 选择快照 + + 选择并恢复到指定的快照 + """ return pick_snapshot(network, tag, discard) -@router.post("/pickoperation/", response_model=None) +@router.post("/pickoperation/", summary="选择操作", description="选择并恢复到指定的操作", response_model=None) async def pick_operation_endpoint( - network: str, operation: int, discard: bool = False + network: str = Query(..., description="管网名称(或数据库名称)"), + operation: int = Query(..., description="操作ID"), + discard: bool = Query(False, description="是否丢弃当前更改") ) -> ChangeSet: + """ + 选择操作 + + 选择并恢复到指定的操作 + """ return pick_operation(network, operation, discard) -@router.get("/syncwithserver/", response_model=None) -async def sync_with_server_endpoint(network: str, operation: int) -> ChangeSet: +@router.get("/syncwithserver/", summary="与服务器同步", description="将网络与服务器同步到指定操作", response_model=None) +async def sync_with_server_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="目标操作ID")) -> ChangeSet: + """ + 与服务器同步 + + 将网络与服务器同步到指定的操作 + """ return sync_with_server(network, operation) -@router.post("/batch/", response_model=None) -async def execute_batch_commands_endpoint(network: str, req: Request) -> ChangeSet: +@router.post("/batch/", summary="执行批量命令", description="执行多个网络操作命令", response_model=None) +async def execute_batch_commands_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), req: Request = None) -> ChangeSet: + """ + 执行批量命令 + + 在网络上执行多个操作命令 + """ jo_root = await req.json() cs: ChangeSet = ChangeSet() cs.operations = jo_root["operations"] rcs = execute_batch_commands(network, cs) return rcs -@router.post("/compressedbatch/", response_model=None) +@router.post("/compressedbatch/", summary="执行压缩批量命令", description="执行压缩的批量命令", response_model=None) async def execute_compressed_batch_commands_endpoint( - network: str, req: Request + network: str = Query(..., description="管网名称(或数据库名称)"), + req: Request = None ) -> ChangeSet: + """ + 执行压缩批量命令 + + 执行压缩格式的批量命令 + """ jo_root = await req.json() cs: ChangeSet = ChangeSet() cs.operations = jo_root["operations"] return execute_batch_command(network, cs) -@router.get("/getrestoreoperation/") -async def get_restore_operation_endpoint(network: str) -> int: +@router.get("/getrestoreoperation/", summary="获取恢复操作ID", description="获取网络的恢复操作ID") +async def get_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int: + """ + 获取恢复操作ID + + 返回网络的恢复操作ID + """ return get_restore_operation(network) -@router.post("/setrestoreoperation/") -async def set_restore_operation_endpoint(network: str, operation: int) -> None: +@router.post("/setrestoreoperation/", summary="设置恢复操作ID", description="设置网络的恢复操作ID") +async def set_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> None: + """ + 设置恢复操作ID + + 设置网络的恢复操作ID + """ return set_restore_operation(network, operation) diff --git a/app/api/v1/endpoints/timeseries/composite.py b/app/api/v1/endpoints/timeseries/composite.py index 116532b..c0cbf41 100644 --- a/app/api/v1/endpoints/timeseries/composite.py +++ b/app/api/v1/endpoints/timeseries/composite.py @@ -8,21 +8,37 @@ from .dependencies import get_timescale_connection, get_postgres_connection router = APIRouter() -@router.get("/composite/scada-simulation") +@router.get("/composite/scada-simulation", summary="获取SCADA关联的模拟数据", + tags=["复合查询"]) async def get_scada_associated_simulation_data( - start_time: datetime, - end_time: datetime, - device_ids: str, - scheme_type: str = Query(None, description="指定方案名称,若为空则查询实时数据"), - scheme_name: str = Query(None, description="指定方案名称,若为空则查询实时数据"), + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), + device_ids: str = Query(..., description="SCADA设备ID列表,逗号分隔"), + scheme_type: str = Query(None, description="方案类型,若为空则查询实时数据"), + scheme_name: str = Query(None, description="方案名称,若为空则查询实时数据"), timescale_conn: AsyncConnection = Depends(get_timescale_connection), postgres_conn: AsyncConnection = Depends(get_postgres_connection), ): """ - 获取 SCADA 关联的 link/node 模拟值 + 获取SCADA关联的link/node模拟值 + + 根据传入的SCADA device_ids,找到关联的link/node, + 并根据对应的type,查询对应的模拟数据。支持查询实时或方案数据。 - 根据传入的 SCADA device_ids,找到关联的 link/node, - 并根据对应的 type,查询对应的模拟数据 + Args: + start_time: 查询开始时间 + end_time: 查询结束时间 + device_ids: SCADA设备ID列表,用逗号分隔 + scheme_type: 方案类型,若为空则查询实时数据 + scheme_name: 方案名称,若为空则查询实时数据 + timescale_conn: TimescaleDB连接 + postgres_conn: PostgreSQL连接 + + Returns: + SCADA关联的模拟数据 + + Raises: + HTTPException: 当查询参数无效时返回400错误,未找到数据时返回404错误 """ try: device_ids_list = ( @@ -58,26 +74,38 @@ async def get_scada_associated_simulation_data( raise HTTPException(status_code=400, detail=str(e)) -@router.get("/composite/element-simulation") +@router.get("/composite/element-simulation", summary="获取管网元素的模拟数据", + tags=["复合查询"]) async def get_feature_simulation_data( - start_time: datetime, - end_time: datetime, + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), feature_infos: str = Query( - ..., description="特征信息,格式: id1:type1,id2:type2,type为pipe或junction" + ..., description="特征信息,格式: id1:type1,id2:type2,type为pipe(管道)或junction(节点)" ), - scheme_type: str = Query(None, description="指定方案类型,若为空则查询实时数据"), - scheme_name: str = Query(None, description="指定方案名称,若为空则查询实时数据"), + scheme_type: str = Query(None, description="方案类型,若为空则查询实时数据"), + scheme_name: str = Query(None, description="方案名称,若为空则查询实时数据"), timescale_conn: AsyncConnection = Depends(get_timescale_connection), ): """ - 获取 link/node 模拟值 - - 根据传入的 featureInfos,找到关联的 link/node, - 并根据对应的 type,查询对应的模拟数据 + 获取link/node模拟值 + + 根据传入的featureInfos,找到关联的link/node, + 并根据对应的type,查询对应的模拟数据。支持查询实时或方案数据。 Args: + start_time: 查询开始时间 + end_time: 查询结束时间 feature_infos: 格式为 "element_id1:type1,element_id2:type2" 例如: "P1:pipe,J1:junction" + scheme_type: 方案类型,若为空则查询实时数据 + scheme_name: 方案名称,若为空则查询实时数据 + timescale_conn: TimescaleDB连接 + + Returns: + 管网元素的模拟数据 + + Raises: + HTTPException: 当feature_infos为空返回400错误,未找到数据返回404错误,其他错误返回400错误 """ try: feature_infos_list = [] @@ -117,20 +145,35 @@ async def get_feature_simulation_data( raise HTTPException(status_code=400, detail=str(e)) -@router.get("/composite/element-scada") +@router.get("/composite/element-scada", summary="获取管网元素关联的SCADA监测数据", + tags=["复合查询"]) async def get_element_associated_scada_data( - element_id: str, - start_time: datetime, - end_time: datetime, + element_id: str = Query(..., description="管网元素ID(管道或节点)"), + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), use_cleaned: bool = Query(False, description="是否使用清洗后的数据"), timescale_conn: AsyncConnection = Depends(get_timescale_connection), postgres_conn: AsyncConnection = Depends(get_postgres_connection), ): """ - 获取 link/node 关联的 SCADA 监测值 + 获取link/node关联的SCADA监测值 + + 根据传入的link/node id,匹配SCADA信息, + 如果存在关联的SCADA device_id,获取实际的监测数据。 - 根据传入的 link/node id,匹配 SCADA 信息, - 如果存在关联的 SCADA device_id,获取实际的监测数据 + Args: + element_id: 管网元素ID + start_time: 查询开始时间 + end_time: 查询结束时间 + use_cleaned: 是否使用清洗后的数据,默认为False使用原始数据 + timescale_conn: TimescaleDB连接 + postgres_conn: PostgreSQL连接 + + Returns: + 管网元素关联的SCADA监测数据 + + Raises: + HTTPException: 当查询参数无效时返回400错误,未找到关联数据返回404错误 """ try: result = await CompositeQueries.get_element_associated_scada_data( @@ -145,18 +188,33 @@ async def get_element_associated_scada_data( raise HTTPException(status_code=400, detail=str(e)) -@router.post("/composite/clean-scada") +@router.post("/composite/clean-scada", summary="清洗SCADA监测数据", + tags=["复合查询"]) async def clean_scada_data( - device_ids: str, - start_time: datetime = Query(...), - end_time: datetime = Query(...), + device_ids: str = Query(..., description="设备ID列表或 'all' 表示清洗所有设备"), + start_time: datetime = Query(..., description="清洗数据的开始时间"), + end_time: datetime = Query(..., description="清洗数据的结束时间"), timescale_conn: AsyncConnection = Depends(get_timescale_connection), postgres_conn: AsyncConnection = Depends(get_postgres_connection), ): """ - 清洗 SCADA 数据 + 清洗SCADA监测数据 + + 根据device_ids查询monitored_value,清洗后更新cleaned_value。 + 支持清洗指定设备或所有设备的数据。 - 根据 device_ids 查询 monitored_value,清洗后更新 cleaned_value + Args: + device_ids: 设备ID列表,用逗号分隔,或 'all' 表示清洗所有设备 + start_time: 清洗数据的开始时间 + end_time: 清洗数据的结束时间 + timescale_conn: TimescaleDB连接 + postgres_conn: PostgreSQL连接 + + Returns: + 清洗结果信息 + + Raises: + HTTPException: 当清洗过程出现错误时返回400错误 """ try: if device_ids == "all": @@ -174,24 +232,29 @@ async def clean_scada_data( raise HTTPException(status_code=400, detail=str(e)) -@router.get("/composite/pipeline-health-prediction") +@router.get("/composite/pipeline-health-prediction", summary="预测管道健康状况", + tags=["复合查询"]) async def predict_pipeline_health( query_time: datetime = Query(..., description="查询时间"), - network_name: str = Query(..., description="管网数据库名称"), + network_name: str = Query(..., description="管网名称(或数据库名称)"), timescale_conn: AsyncConnection = Depends(get_timescale_connection), ): """ 预测管道健康状况 - + 根据管网名称和当前时间,查询管道信息和实时数据, - 使用随机生存森林模型预测管道的生存概率 + 使用随机生存森林模型预测管道的生存概率。 Args: query_time: 查询时间 - db_name: 管网数据库名称 + network_name: 管网名称(或数据库名称) + timescale_conn: TimescaleDB连接 Returns: 预测结果列表,每个元素包含 link_id 和对应的生存函数 + + Raises: + HTTPException: 当模型文件不存在返回404错误,其他错误返回400或500错误 """ try: return await CompositeQueries.predict_pipeline_health( diff --git a/app/api/v1/endpoints/timeseries/realtime.py b/app/api/v1/endpoints/timeseries/realtime.py index 6f12c44..d6fabf3 100644 --- a/app/api/v1/endpoints/timeseries/realtime.py +++ b/app/api/v1/endpoints/timeseries/realtime.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body from typing import List from datetime import datetime from psycopg import AsyncConnection @@ -9,41 +9,96 @@ from .dependencies import get_timescale_connection router = APIRouter() -@router.post("/realtime/links/batch", status_code=201) +@router.post("/realtime/links/batch", status_code=201, summary="批量插入实时管道数据", + tags=["时间序列-实时数据"]) async def insert_realtime_links( - data: List[dict], conn: AsyncConnection = Depends(get_timescale_connection) + data: List[dict] = Body(..., description="管道数据列表,每项包含管道ID、时间戳等信息"), + conn: AsyncConnection = Depends(get_timescale_connection) ): + """ + 批量插入实时管道数据 + + 将管道的实时监测数据批量插入时间序列数据库。 + + Args: + data: 管道数据列表 + + Returns: + 插入成功的记录数 + """ await RealtimeRepository.insert_links_batch(conn, data) return {"message": f"Inserted {len(data)} records"} -@router.get("/realtime/links") +@router.get("/realtime/links", summary="查询实时管道数据", tags=["时间序列-实时数据"]) async def get_realtime_links( - start_time: datetime, - end_time: datetime, + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 查询指定时间范围内的实时管道数据 + + 根据时间范围查询所有实时管道的监测值。 + + Args: + start_time: 查询开始时间 + end_time: 查询结束时间 + + Returns: + 实时管道数据列表 + """ return await RealtimeRepository.get_links_by_time_range(conn, start_time, end_time) -@router.delete("/realtime/links") +@router.delete("/realtime/links", summary="删除实时管道数据", tags=["时间序列-实时数据"]) async def delete_realtime_links( - start_time: datetime, - end_time: datetime, + start_time: datetime = Query(..., description="删除开始时间"), + end_time: datetime = Query(..., description="删除结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 删除指定时间范围内的实时管道数据 + + 删除在指定时间范围内的所有实时管道监测数据。 + + Args: + start_time: 删除开始时间 + end_time: 删除结束时间 + + Returns: + 删除结果信息 + """ await RealtimeRepository.delete_links_by_time_range(conn, start_time, end_time) return {"message": "Deleted successfully"} -@router.patch("/realtime/links/{link_id}/field") +@router.patch("/realtime/links/{link_id}/field", summary="更新实时管道字段", + tags=["时间序列-实时数据"]) async def update_realtime_link_field( - link_id: str, - time: datetime, - field: str, - value: float, + link_id: str = Path(..., description="管道ID"), + time: datetime = Query(..., description="更新数据的时间戳"), + field: str = Query(..., description="要更新的字段名称"), + value: float = Query(..., description="更新的字段值"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 更新指定管道的字段值 + + 更新实时管道在特定时间的某个字段数据。 + + Args: + link_id: 管道ID + time: 数据时间戳 + field: 字段名称 + value: 字段新值 + + Returns: + 更新结果信息 + + Raises: + HTTPException: 当字段不存在或更新失败时返回400错误 + """ try: await RealtimeRepository.update_link_field(conn, time, link_id, field, value) return {"message": "Updated successfully"} @@ -51,55 +106,123 @@ async def update_realtime_link_field( raise HTTPException(status_code=400, detail=str(e)) -@router.post("/realtime/nodes/batch", status_code=201) +@router.post("/realtime/nodes/batch", status_code=201, summary="批量插入实时节点数据", + tags=["时间序列-实时数据"]) async def insert_realtime_nodes( - data: List[dict], conn: AsyncConnection = Depends(get_timescale_connection) + data: List[dict] = Body(..., description="节点数据列表,每项包含节点ID、时间戳等信息"), + conn: AsyncConnection = Depends(get_timescale_connection) ): + """ + 批量插入实时节点数据 + + 将节点的实时监测数据批量插入时间序列数据库。 + + Args: + data: 节点数据列表 + + Returns: + 插入成功的记录数 + """ await RealtimeRepository.insert_nodes_batch(conn, data) return {"message": f"Inserted {len(data)} records"} -@router.get("/realtime/nodes") +@router.get("/realtime/nodes", summary="查询实时节点数据", tags=["时间序列-实时数据"]) async def get_realtime_nodes( - start_time: datetime, - end_time: datetime, + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 查询指定时间范围内的实时节点数据 + + 根据时间范围查询所有实时节点的监测值。 + + Args: + start_time: 查询开始时间 + end_time: 查询结束时间 + + Returns: + 实时节点数据列表 + """ return await RealtimeRepository.get_nodes_by_time_range(conn, start_time, end_time) -@router.delete("/realtime/nodes") +@router.delete("/realtime/nodes", summary="删除实时节点数据", tags=["时间序列-实时数据"]) async def delete_realtime_nodes( - start_time: datetime, - end_time: datetime, + start_time: datetime = Query(..., description="删除开始时间"), + end_time: datetime = Query(..., description="删除结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 删除指定时间范围内的实时节点数据 + + 删除在指定时间范围内的所有实时节点监测数据。 + + Args: + start_time: 删除开始时间 + end_time: 删除结束时间 + + Returns: + 删除结果信息 + """ await RealtimeRepository.delete_nodes_by_time_range(conn, start_time, end_time) return {"message": "Deleted successfully"} -@router.post("/realtime/simulation/store", status_code=201) + + +@router.post("/realtime/simulation/store", status_code=201, summary="存储实时模拟结果", + tags=["时间序列-实时数据"]) async def store_realtime_simulation_result( - node_result_list: List[dict], - link_result_list: List[dict], - result_start_time: str, + node_result_list: List[dict] = Body(..., description="节点模拟结果列表"), + link_result_list: List[dict] = Body(..., description="管道模拟结果列表"), + result_start_time: str = Query(..., description="模拟结果开始时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): - """Store realtime simulation results to TimescaleDB""" + """ + 存储实时模拟结果到时间序列数据库 + + 将节点和管道的实时模拟计算结果批量存储到TimescaleDB数据库。 + + Args: + node_result_list: 节点模拟结果列表 + link_result_list: 管道模拟结果列表 + result_start_time: 模拟结果对应的起始时间 + + Returns: + 存储结果信息 + """ await RealtimeRepository.store_realtime_simulation_result( conn, node_result_list, link_result_list, result_start_time ) return {"message": "Simulation results stored successfully"} -@router.get("/realtime/query/by-time-property") +@router.get("/realtime/query/by-time-property", summary="按时间和属性查询实时数据", + tags=["时间序列-实时数据"]) async def query_realtime_records_by_time_property( - query_time: str, - type: str, - property: str, + query_time: str = Query(..., description="查询时间"), + type: str = Query(..., description="数据类型,pipe(管道)或 junction(节点)"), + property: str = Query(..., description="要查询的属性名称"), conn: AsyncConnection = Depends(get_timescale_connection), ): - """Query all realtime records by time and property""" + """ + 按指定时间和属性查询所有实时监测数据 + + 查询在特定时间点,所有指定类型元素的特定属性值。 + + Args: + query_time: 查询时间 + type: 元素类型(pipe或junction) + property: 属性名称 + + Returns: + 查询结果列表 + + Raises: + HTTPException: 当查询参数无效时返回400错误 + """ try: results = await RealtimeRepository.query_all_record_by_time_property( conn, query_time, type, property @@ -109,14 +232,30 @@ async def query_realtime_records_by_time_property( raise HTTPException(status_code=400, detail=str(e)) -@router.get("/realtime/query/by-id-time") +@router.get("/realtime/query/by-id-time", summary="按ID和时间查询实时模拟数据", + tags=["时间序列-实时数据"]) async def query_realtime_simulation_by_id_time( - id: str, - type: str, - query_time: str, + id: str = Query(..., description="元素ID(管道ID或节点ID)"), + type: str = Query(..., description="元素类型,pipe(管道)或 junction(节点)"), + query_time: str = Query(..., description="查询时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): - """Query realtime simulation results by id and time""" + """ + 按指定ID和时间查询实时模拟结果 + + 查询特定元素在某一时间点的实时模拟数据。 + + Args: + id: 元素ID + type: 元素类型(pipe或junction) + query_time: 查询时间 + + Returns: + 模拟结果数据 + + Raises: + HTTPException: 当查询参数无效时返回400错误 + """ try: results = await RealtimeRepository.query_simulation_result_by_id_time( conn, id, type, query_time diff --git a/app/api/v1/endpoints/timeseries/scada.py b/app/api/v1/endpoints/timeseries/scada.py index e36759f..a42f87c 100644 --- a/app/api/v1/endpoints/timeseries/scada.py +++ b/app/api/v1/endpoints/timeseries/scada.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body from typing import List from datetime import datetime from psycopg import AsyncConnection @@ -9,21 +9,48 @@ from .dependencies import get_timescale_connection router = APIRouter() -@router.post("/scada/batch", status_code=201) +@router.post("/scada/batch", status_code=201, summary="批量插入SCADA监测数据", + tags=["时间序列-监测数据"]) async def insert_scada_data( - data: List[dict], conn: AsyncConnection = Depends(get_timescale_connection) + data: List[dict] = Body(..., description="SCADA设备监测数据列表"), + conn: AsyncConnection = Depends(get_timescale_connection) ): + """ + 批量插入SCADA监测数据 + + 将多个设备的实时监测数据批量插入时间序列数据库。 + + Args: + data: SCADA设备监测数据列表,每项包含device_id、时间戳和监测值等信息 + + Returns: + 插入成功的记录数 + """ await ScadaRepository.insert_scada_batch(conn, data) return {"message": f"Inserted {len(data)} records"} -@router.get("/scada/by-ids-time-range") +@router.get("/scada/by-ids-time-range", summary="按设备ID和时间范围查询SCADA数据", + tags=["时间序列-监测数据"]) async def get_scada_by_ids_time_range( - start_time: datetime, - end_time: datetime, - device_ids: str, + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), + device_ids: str = Query(..., description="设备ID列���,逗号分隔,如 'device1,device2,device3'"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 按设备ID和时间范围查询SCADA监测数据 + + 查询多个设备在指定时间范围内的所有监测数据。 + + Args: + start_time: 查询开始时间 + end_time: 查询结束时间 + device_ids: 设备ID列表,用逗号分隔 + + Returns: + SCADA监测数据列表 + """ device_ids_list = ( [id.strip() for id in device_ids.split(",") if id.strip()] if device_ids else [] ) @@ -32,14 +59,32 @@ async def get_scada_by_ids_time_range( ) -@router.get("/scada/by-ids-field-time-range") +@router.get("/scada/by-ids-field-time-range", summary="按设备ID、字段和时间范围查询SCADA数据", + tags=["时间序列-监测数据"]) async def get_scada_field_by_ids_time_range( - start_time: datetime, - end_time: datetime, - field: str, - device_ids: str, + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), + field: str = Query(..., description="要查询的字段名称"), + device_ids: str = Query(..., description="设备ID列表,逗号分隔,如 'device1,device2,device3'"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 按设备ID、字段和时间范围查询特定SCADA数据 + + 查询多个设备在指定时间范围内的特定字段监测数据。 + + Args: + start_time: 查询开始时间 + end_time: 查询结束时间 + field: 字段名称 + device_ids: 设备ID列表,用逗号分隔 + + Returns: + SCADA字段数据列表 + + Raises: + HTTPException: 当字段不存在或查询参数无效时返回400错误 + """ try: device_ids_list = ( [id.strip() for id in device_ids.split(",") if id.strip()] @@ -53,14 +98,32 @@ async def get_scada_field_by_ids_time_range( raise HTTPException(status_code=400, detail=str(e)) -@router.patch("/scada/{device_id}/field") +@router.patch("/scada/{device_id}/field", summary="更新SCADA设备字段", + tags=["时间序列-监测数据"]) async def update_scada_field( - device_id: str, - time: datetime, - field: str, - value: float, + device_id: str = Path(..., description="设备ID"), + time: datetime = Query(..., description="更新数据的时间戳"), + field: str = Query(..., description="要更新的字段名称"), + value: float = Query(..., description="更新的字段值"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 更新指定设备的字段值 + + 更新SCADA设备在特定时间的某个字段监测数据。 + + Args: + device_id: 设备ID + time: 数据时间戳 + field: 字段名称 + value: 字段新值 + + Returns: + 更新结果信息 + + Raises: + HTTPException: 当字段不存在或更新失败时返回400错误 + """ try: await ScadaRepository.update_scada_field(conn, time, device_id, field, value) return {"message": "Updated successfully"} @@ -68,13 +131,27 @@ async def update_scada_field( raise HTTPException(status_code=400, detail=str(e)) -@router.delete("/scada/by-id-time-range") +@router.delete("/scada/by-id-time-range", summary="按设备ID和时间范围删除SCADA数据", + tags=["时间序列-监测数据"]) async def delete_scada_data( - device_id: str, - start_time: datetime, - end_time: datetime, + device_id: str = Query(..., description="设备ID"), + start_time: datetime = Query(..., description="删除开始时间"), + end_time: datetime = Query(..., description="删除结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 删除指定设备和时间范围内的SCADA数据 + + 删除在指定时间范围内的特定设备监测数据。 + + Args: + device_id: 设备ID + start_time: 删除开始时间 + end_time: 删除结束时间 + + Returns: + 删除结果信息 + """ await ScadaRepository.delete_scada_by_id_time_range( conn, device_id, start_time, end_time ) diff --git a/app/api/v1/endpoints/timeseries/scheme.py b/app/api/v1/endpoints/timeseries/scheme.py index 307f7f1..7e342c0 100644 --- a/app/api/v1/endpoints/timeseries/scheme.py +++ b/app/api/v1/endpoints/timeseries/scheme.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException, Query, Path, Body from typing import List from datetime import datetime from psycopg import AsyncConnection @@ -9,37 +9,84 @@ from .dependencies import get_timescale_connection router = APIRouter() -@router.post("/scheme/links/batch", status_code=201) +@router.post("/scheme/links/batch", status_code=201, summary="批量插入方案管道数据", + tags=["时间序列-方案数据"]) async def insert_scheme_links( - data: List[dict], conn: AsyncConnection = Depends(get_timescale_connection) + data: List[dict] = Body(..., description="方案管道数据列表"), + conn: AsyncConnection = Depends(get_timescale_connection) ): + """ + 批量插入方案管道数据 + + 将特定方案的管道模拟数据批量插入时间序列数据库。 + + Args: + data: 方案管道数据列表 + + Returns: + 插入成功的记录数 + """ await SchemeRepository.insert_links_batch(conn, data) return {"message": f"Inserted {len(data)} records"} -@router.get("/scheme/links") +@router.get("/scheme/links", summary="查询方案管道数据", tags=["时间序列-方案数据"]) async def get_scheme_links( - scheme_type: str, - scheme_name: str, - start_time: datetime, - end_time: datetime, + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 查询指定方案和时间范围内的管道数据 + + 根据方案和时间范围查询管道的模拟值。 + + Args: + scheme_type: 方案类型 + scheme_name: 方案名称 + start_time: 查询开始时间 + end_time: 查询结束时间 + + Returns: + 方案管道数据列表 + """ return await SchemeRepository.get_links_by_scheme_and_time_range( conn, scheme_type, scheme_name, start_time, end_time ) -@router.get("/scheme/links/{link_id}/field") +@router.get("/scheme/links/{link_id}/field", summary="查询方案管道字段数据", + tags=["时间序列-方案数据"]) async def get_scheme_link_field( - scheme_type: str, - scheme_name: str, - link_id: str, - start_time: datetime, - end_time: datetime, - field: str, + link_id: str = Path(..., description="管道ID"), + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), + field: str = Query(..., description="要查询的字段名称"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 查询指定方案管道的特定字段数据 + + 查询特定方案中指定管道在时间范围内的特定字段值。 + + Args: + link_id: 管道ID + scheme_type: 方案类型 + scheme_name: 方案名称 + start_time: 查询开始时间 + end_time: 查询结束时间 + field: 字段名称 + + Returns: + 字段数据列表 + + Raises: + HTTPException: 当查询参数无效时返回400错误 + """ try: return await SchemeRepository.get_link_field_by_scheme_and_time_range( conn, scheme_type, scheme_name, start_time, end_time, link_id, field @@ -48,16 +95,36 @@ async def get_scheme_link_field( raise HTTPException(status_code=400, detail=str(e)) -@router.patch("/scheme/links/{link_id}/field") +@router.patch("/scheme/links/{link_id}/field", summary="更新方案管道字段", + tags=["时间序列-方案数据"]) async def update_scheme_link_field( - scheme_type: str, - scheme_name: str, - link_id: str, - time: datetime, - field: str, - value: float, + link_id: str = Path(..., description="管道ID"), + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + time: datetime = Query(..., description="更新数据的时间戳"), + field: str = Query(..., description="要更新的字段名称"), + value: float = Query(..., description="更新的字段值"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 更新指定方案管道的字段值 + + 更新特定方案中指定管道在某个时间的字段数据。 + + Args: + link_id: 管道ID + scheme_type: 方案类型 + scheme_name: 方案名称 + time: 数据时间戳 + field: 字段名称 + value: 字段新值 + + Returns: + 更新结果信息 + + Raises: + HTTPException: 当字段不存在或更新失败时返回400错误 + """ try: await SchemeRepository.update_link_field( conn, time, scheme_type, scheme_name, link_id, field, value @@ -67,38 +134,85 @@ async def update_scheme_link_field( raise HTTPException(status_code=400, detail=str(e)) -@router.delete("/scheme/links") +@router.delete("/scheme/links", summary="删除方案管道数据", tags=["时间序列-方案数据"]) async def delete_scheme_links( - scheme_type: str, - scheme_name: str, - start_time: datetime, - end_time: datetime, + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + start_time: datetime = Query(..., description="删除开始时间"), + end_time: datetime = Query(..., description="删除结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 删除指定方案和时间范围内的管道数据 + + 删除在指定方案和时间范围内的所有管道模拟数据。 + + Args: + scheme_type: 方案类型 + scheme_name: 方案名称 + start_time: 删除开始时间 + end_time: 删除结束时间 + + Returns: + 删除结果信息 + """ await SchemeRepository.delete_links_by_scheme_and_time_range( conn, scheme_type, scheme_name, start_time, end_time ) return {"message": "Deleted successfully"} -@router.post("/scheme/nodes/batch", status_code=201) +@router.post("/scheme/nodes/batch", status_code=201, summary="批量插入方案节点数据", + tags=["时间序列-方案数据"]) async def insert_scheme_nodes( - data: List[dict], conn: AsyncConnection = Depends(get_timescale_connection) + data: List[dict] = Body(..., description="方案节点数据列表"), + conn: AsyncConnection = Depends(get_timescale_connection) ): + """ + 批量插入方案节点数据 + + 将特定方案的节点模拟数据批量插入时间序列数据库。 + + Args: + data: 方案节点数据列表 + + Returns: + 插入成功的记录数 + """ await SchemeRepository.insert_nodes_batch(conn, data) return {"message": f"Inserted {len(data)} records"} -@router.get("/scheme/nodes/{node_id}/field") +@router.get("/scheme/nodes/{node_id}/field", summary="查询方案节点字段数据", + tags=["时间序列-方案数据"]) async def get_scheme_node_field( - scheme_type: str, - scheme_name: str, - node_id: str, - start_time: datetime, - end_time: datetime, - field: str, + node_id: str = Path(..., description="节点ID"), + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + start_time: datetime = Query(..., description="查询开始时间"), + end_time: datetime = Query(..., description="查询结束时间"), + field: str = Query(..., description="要查询的字段名称"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 查询指定方案节点的特定字段数据 + + 查询特定方案中指定节点在时间范围内的特定字段值。 + + Args: + node_id: 节点ID + scheme_type: 方案类型 + scheme_name: 方案名称 + start_time: 查询开始时间 + end_time: 查询结束时间 + field: 字段名称 + + Returns: + 字段数据列表 + + Raises: + HTTPException: 当查询参数无效时返回400错误 + """ try: return await SchemeRepository.get_node_field_by_scheme_and_time_range( conn, scheme_type, scheme_name, start_time, end_time, node_id, field @@ -107,16 +221,36 @@ async def get_scheme_node_field( raise HTTPException(status_code=400, detail=str(e)) -@router.patch("/scheme/nodes/{node_id}/field") +@router.patch("/scheme/nodes/{node_id}/field", summary="更新方案节点字段", + tags=["时间序列-方案数据"]) async def update_scheme_node_field( - scheme_type: str, - scheme_name: str, - node_id: str, - time: datetime, - field: str, - value: float, + node_id: str = Path(..., description="节点ID"), + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + time: datetime = Query(..., description="更新数据的时间戳"), + field: str = Query(..., description="要更新的字段名称"), + value: float = Query(..., description="更新的字段值"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 更新指定方案节点的字段值 + + 更新特定方案中指定节点在某个时间的字段数据。 + + Args: + node_id: 节点ID + scheme_type: 方案类型 + scheme_name: 方案名称 + time: 数据时间戳 + field: 字段名称 + value: 字段新值 + + Returns: + 更新结果信息 + + Raises: + HTTPException: 当字段不存在或更新失败时返回400错误 + """ try: await SchemeRepository.update_node_field( conn, time, scheme_type, scheme_name, node_id, field, value @@ -126,30 +260,59 @@ async def update_scheme_node_field( raise HTTPException(status_code=400, detail=str(e)) -@router.delete("/scheme/nodes") +@router.delete("/scheme/nodes", summary="删除方案节点数据", tags=["时间序列-方案数据"]) async def delete_scheme_nodes( - scheme_type: str, - scheme_name: str, - start_time: datetime, - end_time: datetime, + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + start_time: datetime = Query(..., description="删除开始时间"), + end_time: datetime = Query(..., description="删除结束时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): + """ + 删除指定方案和时间范围内的节点数据 + + 删除在指定方案和时间范围内的所有节点模拟数据。 + + Args: + scheme_type: 方案类型 + scheme_name: 方案名称 + start_time: 删除开始时间 + end_time: 删除结束时间 + + Returns: + 删除结果信息 + """ await SchemeRepository.delete_nodes_by_scheme_and_time_range( conn, scheme_type, scheme_name, start_time, end_time ) return {"message": "Deleted successfully"} -@router.post("/scheme/simulation/store", status_code=201) +@router.post("/scheme/simulation/store", status_code=201, summary="存储方案模拟结果", + tags=["时间序列-方案数据"]) async def store_scheme_simulation_result( - scheme_type: str, - scheme_name: str, - node_result_list: List[dict], - link_result_list: List[dict], - result_start_time: str, + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + node_result_list: List[dict] = Body(..., description="节点模拟结果列表"), + link_result_list: List[dict] = Body(..., description="管道模拟结果列表"), + result_start_time: str = Query(..., description="模拟结果开始时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): - """Store scheme simulation results to TimescaleDB""" + """ + 存储方案模拟结果到时间序列数据库 + + 将特定方案的节点和管道模拟计算结果批量存储到TimescaleDB数据库。 + + Args: + scheme_type: 方案类型 + scheme_name: 方案名称 + node_result_list: 节点模拟结果列表 + link_result_list: 管道模拟结果列表 + result_start_time: 模拟结果对应的起始时间 + + Returns: + 存储结果信息 + """ await SchemeRepository.store_scheme_simulation_result( conn, scheme_type, @@ -161,16 +324,34 @@ async def store_scheme_simulation_result( return {"message": "Scheme simulation results stored successfully"} -@router.get("/scheme/query/by-scheme-time-property") +@router.get("/scheme/query/by-scheme-time-property", summary="按方案、时间和属性查询数据", + tags=["时间序列-方案数据"]) async def query_scheme_records_by_scheme_time_property( - scheme_type: str, - scheme_name: str, - query_time: str, - type: str, - property: str, + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + query_time: str = Query(..., description="查询时间"), + type: str = Query(..., description="元素类型,pipe(管道)或 junction(节点)"), + property: str = Query(..., description="要查询的属性名称"), conn: AsyncConnection = Depends(get_timescale_connection), ): - """Query all scheme records by scheme, time and property""" + """ + 按指定方案、时间和属性查询所有方案数据 + + 查询在特定方案和时间点,所有指定类型元素的特定属性值。 + + Args: + scheme_type: 方案类型 + scheme_name: 方案名称 + query_time: 查询时间 + type: 元素类型(pipe或junction) + property: 属性名称 + + Returns: + 查询结果列表 + + Raises: + HTTPException: 当查询参数无效时返回400错误 + """ try: results = await SchemeRepository.query_all_record_by_scheme_time_property( conn, scheme_type, scheme_name, query_time, type, property @@ -180,16 +361,34 @@ async def query_scheme_records_by_scheme_time_property( raise HTTPException(status_code=400, detail=str(e)) -@router.get("/scheme/query/by-id-time") +@router.get("/scheme/query/by-id-time", summary="按ID和时间查询方案模拟数据", + tags=["时间序列-方案数据"]) async def query_scheme_simulation_by_id_time( - scheme_type: str, - scheme_name: str, - id: str, - type: str, - query_time: str, + scheme_type: str = Query(..., description="方案类型"), + scheme_name: str = Query(..., description="方案名称"), + id: str = Query(..., description="元素ID(管道ID或节点ID)"), + type: str = Query(..., description="元素类型,pipe(管道)或 junction(节点)"), + query_time: str = Query(..., description="查询时间"), conn: AsyncConnection = Depends(get_timescale_connection), ): - """Query scheme simulation results by id and time""" + """ + 按指定ID和时间查询方案模拟结果 + + 查询特定方案中的元素在某一时间点的模拟数据。 + + Args: + scheme_type: 方案类型 + scheme_name: 方案名称 + id: 元素ID + type: 元素类型(pipe或junction) + query_time: 查询时间 + + Returns: + 模拟结果数据 + + Raises: + HTTPException: 当查询参数无效时返回400错误 + """ try: result = await SchemeRepository.query_scheme_simulation_result_by_id_time( conn, scheme_type, scheme_name, id, type, query_time diff --git a/app/api/v1/endpoints/user_management.py b/app/api/v1/endpoints/user_management.py index 8109ec3..af8b1ce 100644 --- a/app/api/v1/endpoints/user_management.py +++ b/app/api/v1/endpoints/user_management.py @@ -4,7 +4,7 @@ 演示权限控制的使用 """ from typing import List -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends, HTTPException, status, Path, Query from app.domain.schemas.user import UserResponse, UserUpdate, UserCreate from app.domain.models.role import UserRole from app.domain.schemas.user import UserInDB @@ -14,22 +14,24 @@ from app.auth.permissions import get_current_admin, require_role, check_resource router = APIRouter() -@router.get("/", response_model=List[UserResponse]) +@router.get("/", summary="列出所有用户", description="获取用户列表(仅管理员)", response_model=List[UserResponse]) async def list_users( - skip: int = 0, - limit: int = 100, + skip: int = Query(0, ge=0, description="跳过的用户数"), + limit: int = Query(100, ge=1, le=1000, description="返回的最大用户数"), current_user: UserInDB = Depends(require_role(UserRole.ADMIN)), user_repo: UserRepository = Depends(get_user_repository) ) -> List[UserResponse]: """ - 获取用户列表(仅管理员) + 获取用户列表 + + 获取系统中所有的用户信息(需要管理员权限) """ users = await user_repo.get_all_users(skip=skip, limit=limit) return [UserResponse.model_validate(user) for user in users] -@router.get("/{user_id}", response_model=UserResponse) +@router.get("/{user_id}", summary="获取用户详情", description="获取指定用户的详细信息", response_model=UserResponse) async def get_user( - user_id: int, + user_id: int = Path(..., gt=0, description="用户ID"), current_user: UserInDB = Depends(get_current_active_user), user_repo: UserRepository = Depends(get_user_repository) ) -> UserResponse: @@ -54,10 +56,10 @@ async def get_user( return UserResponse.model_validate(user) -@router.put("/{user_id}", response_model=UserResponse) +@router.put("/{user_id}", summary="更新用户信息", description="更新指定用户的信息", response_model=UserResponse) async def update_user( - user_id: int, - user_update: UserUpdate, + user_id: int = Path(..., gt=0, description="用户ID"), + user_update: UserUpdate = None, current_user: UserInDB = Depends(get_current_active_user), user_repo: UserRepository = Depends(get_user_repository) ) -> UserResponse: @@ -107,14 +109,16 @@ async def update_user( return UserResponse.model_validate(updated_user) -@router.delete("/{user_id}") +@router.delete("/{user_id}", summary="删除用户", description="删除指定用户(仅管理员)") async def delete_user( - user_id: int, + user_id: int = Path(..., gt=0, description="用户ID"), current_user: UserInDB = Depends(get_current_admin), user_repo: UserRepository = Depends(get_user_repository) ) -> dict: """ - 删除用户(仅管理员) + 删除用户 + + 删除指定用户(需要管理员权限,不能删除自己) """ # 不能删除自己 if current_user.id == user_id: @@ -132,14 +136,16 @@ async def delete_user( return {"message": "User deleted successfully"} -@router.post("/{user_id}/activate") +@router.post("/{user_id}/activate", summary="激活用户", description="激活指定用户账户(仅管理员)", response_model=UserResponse) async def activate_user( - user_id: int, + user_id: int = Path(..., gt=0, description="用户ID"), current_user: UserInDB = Depends(get_current_admin), user_repo: UserRepository = Depends(get_user_repository) ) -> UserResponse: """ - 激活用户(仅管理员) + 激活用户 + + 激活指定用户的账户(需要管理员权限) """ user_update = UserUpdate(is_active=True) updated_user = await user_repo.update_user(user_id, user_update) @@ -152,14 +158,16 @@ async def activate_user( return UserResponse.model_validate(updated_user) -@router.post("/{user_id}/deactivate") +@router.post("/{user_id}/deactivate", summary="停用用户", description="停用指定用户账户(仅管理员)", response_model=UserResponse) async def deactivate_user( - user_id: int, + user_id: int = Path(..., gt=0, description="用户ID"), current_user: UserInDB = Depends(get_current_admin), user_repo: UserRepository = Depends(get_user_repository) ) -> UserResponse: """ - 停用用户(仅管理员) + 停用用户 + + 停用指定用户的账户(需要管理员权限,不能停用自己) """ # 不能停用自己 if current_user.id == user_id: diff --git a/app/api/v1/endpoints/users.py b/app/api/v1/endpoints/users.py index fd98cdc..9cd1507 100644 --- a/app/api/v1/endpoints/users.py +++ b/app/api/v1/endpoints/users.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, Query from typing import Any, List, Dict, Union from app.services.tjnetwork import Any, get_all_users, get_user, get_user_schema @@ -8,14 +8,29 @@ router = APIRouter() # user 39 ########################################################### -@router.get("/getuserschema/") -async def fastapi_get_user_schema(network: str) -> dict[str, dict[Any, Any]]: +@router.get("/getuserschema/", summary="获取用户模式", description="获取指定网络的用户模式定义") +async def fastapi_get_user_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]: + """ + 获取用户模式定义 + + 返回指定网络的用户模式结构定义 + """ return get_user_schema(network) -@router.get("/getuser/") -async def fastapi_get_user(network: str, user_name: str) -> dict[Any, Any]: +@router.get("/getuser/", summary="获取单个用户", description="获取指定网络中的单个用户信息") +async def fastapi_get_user(network: str = Query(..., description="管网名称(或数据库名称)"), user_name: str = Query(..., description="用户名")) -> dict[Any, Any]: + """ + 获取用户信息 + + 返回指定网络中指定用户名的详细信息 + """ return get_user(network, user_name) -@router.get("/getallusers/") -async def fastapi_get_all_users(network: str) -> list[dict[Any, Any]]: +@router.get("/getallusers/", summary="获取所有用户", description="获取指定网络的所有用户列表") +async def fastapi_get_all_users(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]: + """ + 获取所有用户列表 + + 返回指定网络中所有用户的信息 + """ return get_all_users(network) \ No newline at end of file