""" API 请求/响应数据模型 """ from datetime import datetime from typing import List, Optional, Dict, Any from pydantic import BaseModel, Field, model_validator # ============================================================ # 暴雨预测 # ============================================================ class RainfallPredictRequest(BaseModel): """ 暴雨灾害链预测请求 参数规则(二选一): 1. 自动推演模式:rainfall、duration、region_code 全部不传 → 从气象表自动获取,不限区域 2. 指定条件模式:rainfall、duration、region_code 全部传入 → 按指定条件预测 """ disaster_name: str = Field(min_length=1, max_length=255) point_ids: Optional[List[int]] = Field(None, max_length=500, description="点位ID列表,不传则查询所有点") region_code: Optional[str] = Field(None, description="行政区划代码(如 '610104')") rainfall: Optional[float] = Field(None, ge=0, description="累计降雨量(mm)") duration: Optional[float] = Field(None, ge=0, description="降雨持续时间(h)") occurred_time: Optional[datetime] = Field(None, description="事件发生时间,不传则为当前时间") operation_type: str = Field("模拟", min_length=1, max_length=50, description="操作类型(如 '模拟', '实时监测', '应急评估')") @model_validator(mode='after') def validate_mode_exclusivity(self): """校验 rainfall / duration / region_code 必须要么全不传,要么全传""" trio = (self.rainfall, self.duration, self.region_code) none_count = sum(1 for v in trio if v is None) if none_count not in (0, 3): raise ValueError( "rainfall、duration、region_code 必须全部传入或全部不传," "不允许只传部分参数" ) return self # ============================================================ # 地震预测 # ============================================================ class EarthquakePredictRequest(BaseModel): """地震灾害链预测请求""" disaster_name: str = Field(min_length=1, max_length=255) point_ids: Optional[List[int]] = Field(None, max_length=500, description="点位ID列表,不传则查询所有点") region_code: Optional[str] = Field(None, description="行政区划代码(如 '610104'),不传则不限区域") magnitude: float = Field(..., ge=0, le=10, description="震级(Richter)") depth: float = Field(10.0, gt=0, le=700, description="震源深度(km),默认10km") epicenter_lon: float = Field(..., ge=-180, le=180, description="震中经度") epicenter_lat: float = Field(..., ge=-90, le=90, description="震中纬度") occurred_time: Optional[datetime] = Field(None, description="地震发生时间,不传则为当前时间") operation_type: str = Field("模拟", min_length=1, max_length=50, description="操作类型(如 '模拟', '实时监测', '应急评估')") # ============================================================ # 专题图产出 # ============================================================ class QgisMapExportRequest(BaseModel): """专题图导出请求""" inferenceId: int = Field(..., description="推理结果ID(xian_inference_result.id)") class QgisMapExportResponse(BaseModel): """专题图导出响应""" code: int = Field(200, description="状态码") message: str = Field("success", description="提示信息") data: Optional[str] = Field(None, description="导出图片的访问路径") # ============================================================ # 通用响应 # ============================================================ class PredictData(BaseModel): """预测数据""" record_id: Optional[int] = Field(None, description="推理结果记录ID") list: Dict[str, Dict[str, Any]] = Field(default_factory=dict, description="预测结果列表,包含概率和经纬度") class PredictResponse(BaseModel): """预测响应""" code: int = Field(200, description="状态码") message: str = Field("success", description="提示信息") data: Optional[PredictData] = Field(None, description="预测数据") class UpdateMonitoringTimeRequest(BaseModel): """更新监测时间请求""" query_time: str = Field(..., description="查询时间,格式: YYYY-MM-DD HH:mm:ss,如 '2025-09-16 20:00:00'") class HealthResponse(BaseModel): """健康检查响应""" status: str = "ok" rainfall_model_loaded: bool = False earthquake_model_loaded: bool = False # ============================================================ # 各区降雨概况 # ============================================================ class DistrictSummaryRequest(BaseModel): """各区降雨概况请求""" inference_id: int = Field(..., ge=1, description="推理结果ID(xian_inference_result.id)") class DistrictSummaryItem(BaseModel): """单个区的降雨概况""" district_name: str = Field(..., description="行政区名称(如 '碑林区')") district_code: str = Field(..., description="行政区划代码(如 '610103')") rainfall: float = Field(..., description="累计降雨量(mm)") duration_hours: float = Field(..., description="持续降雨时间(h)") class DistrictSummaryResponse(BaseModel): """各区降雨概况响应""" code: int = Field(200, description="状态码") message: str = Field("success", description="提示信息") data: Optional[List[DistrictSummaryItem]] = Field(None, description="各区降雨概况列表")