""" 用户输入数据模型定义 使用Pydantic进行数据验证 """ from pydantic import BaseModel, Field from typing import Optional, List from enum import Enum class RiskLevel(str, Enum): """风险等级枚举""" LOW = "low" MEDIUM = "medium" HIGH = "high" class ModelType(str, Enum): """模型类型枚举""" SARCO_I = "sarcoI" SARCO_II = "sarcoII" class UserInput(BaseModel): """用户输入数据模型""" # 基本信息 (注意:本系统适用于18-55岁成年人群) age_years: float = Field(..., ge=16, le=120, description="年龄(岁) - 推荐18-55岁") race_ethnicity: int = Field(..., ge=0, le=4, description="种族/民族编码(0-4)") # 身体指标 body_mass_index: float = Field(..., ge=10, le=80, description="体质量指数BMI") WWI: float = Field(..., ge=3, le=50, description="腰围重量指数") # NHANES PAQ 原始问卷数据 # 工作相关活动 PAQ605: int = Field(..., ge=1, le=2, description="工作高强度活动(1=是,2=否)") PAQ610: Optional[int] = Field(0, ge=0, le=7, description="工作高强度活动天数/周") PAD615: Optional[float] = Field(0, ge=0, le=1440, description="工作高强度活动分钟数/天") PAQ620: int = Field(..., ge=1, le=2, description="工作中等强度活动(1=是,2=否)") PAQ625: Optional[int] = Field(0, ge=0, le=7, description="工作中等强度活动天数/周") PAD630: Optional[float] = Field(0, ge=0, le=1440, description="工作中等强度活动分钟数/天") # 交通相关活动 PAQ635: int = Field(..., ge=1, le=2, description="步行/骑车通勤(1=是,2=否)") PAQ640: Optional[int] = Field(0, ge=0, le=7, description="步行/骑车通勤天数/周") PAD645: Optional[float] = Field(0, ge=0, le=1440, description="步行/骑车通勤分钟数/天") # 休闲运动活动 PAQ650: int = Field(..., ge=1, le=2, description="休闲高强度活动(1=是,2=否)") PAQ655: Optional[int] = Field(0, ge=0, le=7, description="休闲高强度活动天数/周") PAD660: Optional[float] = Field(0, ge=0, le=1440, description="休闲高强度活动分钟数/天") PAQ665: int = Field(..., ge=1, le=2, description="休闲中等强度活动(1=是,2=否)") PAQ670: Optional[int] = Field(0, ge=0, le=7, description="休闲中等强度活动天数/周") PAD675: Optional[float] = Field(0, ge=0, le=1440, description="休闲中等强度活动分钟数/天") # 久坐行为 PAD680: float = Field(..., ge=0, le=1440, description="每日久坐时间(分钟)") # 医疗史 (筛查模型必需) arthritis: Optional[int] = Field(0, ge=0, le=1, description="关节炎史(0=无,1=有)") diabetes: Optional[int] = Field(0, ge=0, le=1, description="糖尿病史(0=无,1=有)") # 计算得出的衍生特征 (前端计算后传入) Activity_Sedentary_Ratio: Optional[float] = Field(None, ge=0, le=100, description="久坐活动比例") Total_Moderate_Minutes_week: Optional[float] = Field(None, ge=0, le=10080, description="每周中等强度运动时间(分钟)") Total_Moderate_Equivalent_Minutes: Optional[float] = Field(None, ge=0, le=10080, description="每周中等强度等效运动时间(分钟)") Avg_Vigorous_Duration_Per_Bout: Optional[float] = Field(None, ge=0, le=1440, description="平均高强度运动时长每次(分钟)") Vigorous_MET_Ratio: Optional[float] = Field(None, ge=0, le=10, description="高强度运动比例") Activity_Diversity_Index: Optional[float] = Field(None, ge=0, le=5, description="活动多样性指数(0-5)") sedentary_minutes: Optional[float] = Field(None, ge=0, le=1440, description="每日久坐时间(分钟,同PAD680)") class Config: json_schema_extra = { "example": { "age_years": 45, "race_ethnicity": 2, "body_mass_index": 28.5, "WWI": 11.2, "PAQ605": 2, "PAQ620": 1, "PAQ625": 3, "PAD630": 60, "PAQ635": 1, "PAQ640": 5, "PAD645": 30, "PAQ650": 1, "PAQ655": 2, "PAD660": 45, "PAQ665": 1, "PAQ670": 3, "PAD675": 60, "PAD680": 480, "arthritis": 0, "diabetes": 0, "Activity_Sedentary_Ratio": 0.6, "Total_Moderate_Minutes_week": 150, "Total_Moderate_Equivalent_Minutes": 150, "Avg_Vigorous_Duration_Per_Bout": 30, "Vigorous_MET_Ratio": 0.3, "Activity_Diversity_Index": 3, "sedentary_minutes": 480 } } class ScreeningRequest(BaseModel): """筛查请求模型""" user_data: UserInput models: List[ModelType] = [ModelType.SARCO_I, ModelType.SARCO_II] class AdvisoryRequest(BaseModel): """建议请求模型""" user_data: UserInput risk_types: List[ModelType] = [] num_recommendations: int = Field(default=3, ge=1, le=5, description="建议数量") language: str = Field(default="zh", description="语言设置 (zh/en)") class ScreeningResponse(BaseModel): """筛查响应模型""" # 筛查模型结果 (高召回率) sarcoI_risk: RiskLevel sarcoI_probability: float sarcoI_threshold: float sarcoII_risk: RiskLevel sarcoII_probability: float sarcoII_threshold: float # 建议模型结果 (高精确率) sarcoI_advisory_risk: Optional[RiskLevel] = None sarcoI_advisory_probability: Optional[float] = None sarcoI_advisory_threshold: Optional[float] = None sarcoII_advisory_risk: Optional[RiskLevel] = None sarcoII_advisory_probability: Optional[float] = None sarcoII_advisory_threshold: Optional[float] = None # 综合结果 overall_risk: RiskLevel confidence: float processing_time: float class RecommendationItem(BaseModel): """单个建议项""" title: str description: str priority: str target_change: Optional[str] = None expected_impact: Optional[str] = None class AdvisoryResponse(BaseModel): """建议响应模型""" sarcoI_recommendations: List[RecommendationItem] sarcoII_recommendations: List[RecommendationItem] recommendations: Optional[List[RecommendationItem]] = None # 合并的建议列表,用于前端显示 priority_actions: List[str] target_metrics: dict processing_time: float success: bool fallback_used: bool = False class ErrorResponse(BaseModel): """错误响应模型""" error: str detail: str timestamp: str