""" Pydantic Schemas - 请求/响应数据验证 """ from datetime import datetime from typing import Optional, List from pydantic import BaseModel, Field, EmailStr # ==================== 通用响应 ==================== class ResponseBase(BaseModel): """基础响应""" code: int = 0 message: str = "success" class ResponseData(ResponseBase): """带数据的响应""" data: Optional[dict] = None class ResponseList(ResponseBase): """带列表的响应""" data: List[dict] = [] total: int = 0 page: int = 1 page_size: int = 10 # ==================== 认证相关 ==================== class LoginRequest(BaseModel): """登录请求""" username: str = Field(..., min_length=3, max_length=50) password: str = Field(..., min_length=6) class TokenResponse(BaseModel): """令牌响应""" access_token: str refresh_token: str token_type: str = "Bearer" expires_in: int = 3600 class RefreshTokenRequest(BaseModel): """刷新令牌请求""" refresh_token: str # ==================== 用户相关 ==================== class UserBase(BaseModel): """用户基础信息""" username: str = Field(..., min_length=3, max_length=50) email: Optional[EmailStr] = None class UserCreate(UserBase): """创建用户""" password: str = Field(..., min_length=6) class UserResponse(UserBase): """用户响应""" id: int role: str is_active: bool created_at: datetime class Config: from_attributes = True class UserUpdate(BaseModel): """更新用户""" email: Optional[EmailStr] = None password: Optional[str] = Field(None, min_length=6) # ==================== K 线数据相关 ==================== class KlineRequest(BaseModel): """K 线数据查询请求""" symbol: str = Field(..., description="品种代码,如 IF2406") period: str = Field(..., description="周期,如 1m, 5m, 1h, 1d") start: datetime end: datetime class KlineDataItem(BaseModel): """单条 K 线数据""" time: datetime open: float high: float low: float close: float volume: int amount: Optional[float] = None open_interest: Optional[int] = None class KlineResponse(ResponseBase): """K 线数据响应""" data: List[KlineDataItem] = [] symbol: str period: str # ==================== 实时行情相关 ==================== class RealtimeQuoteItem(BaseModel): """实时行情数据""" time: datetime symbol: str last_price: float open_price: Optional[float] = None high_price: Optional[float] = None low_price: Optional[float] = None prev_close: Optional[float] = None volume: Optional[int] = None amount: Optional[float] = None bid_price_1: Optional[float] = None bid_volume_1: Optional[int] = None ask_price_1: Optional[float] = None ask_volume_1: Optional[int] = None position: Optional[int] = None change_percent: Optional[float] = None # 涨跌幅 class SubscribeRequest(BaseModel): """订阅请求""" symbols: List[str] = Field(..., description="品种代码列表") class UnsubscribeRequest(BaseModel): """取消订阅请求""" symbols: List[str] = Field(..., description="品种代码列表") # ==================== 告警相关 ==================== class AlertBase(BaseModel): """告警基础信息""" symbol: str = Field(..., description="品种代码") condition_type: str = Field(..., description="条件类型:greater_than, less_than, equals") condition_value: float = Field(..., description="条件值") alert_type: str = Field(default="price", description="告警类型:price, percent_change") class AlertCreate(AlertBase): """创建告警""" pass class AlertResponse(AlertBase): """告警响应""" id: int user_id: int status: str triggered_at: Optional[datetime] = None created_at: datetime updated_at: datetime class Config: from_attributes = True class AlertUpdate(BaseModel): """更新告警""" condition_value: Optional[float] = None status: Optional[str] = None # ==================== 订阅相关 ==================== class SubscriptionBase(BaseModel): """订阅基础信息""" symbol: str = Field(..., description="品种代码") period: Optional[str] = Field(None, description="周期") subscription_type: str = Field(default="kline", description="订阅类型:kline, realtime") class SubscriptionCreate(SubscriptionBase): """创建订阅""" pass class SubscriptionResponse(SubscriptionBase): """订阅响应""" id: int user_id: int is_active: bool created_at: datetime class Config: from_attributes = True # ==================== API Key 相关 ==================== class APIKeyCreate(BaseModel): """创建 API Key""" name: Optional[str] = None permissions: Optional[List[str]] = None expires_days: Optional[int] = None # 过期天数 class APIKeyResponse(BaseModel): """API Key 响应""" id: int name: Optional[str] key: str # 仅创建时返回 permissions: Optional[List[str]] expires_at: Optional[datetime] is_active: bool created_at: datetime class Config: from_attributes = True # ==================== 分页参数 ==================== class PageParams(BaseModel): """分页参数""" page: int = Field(1, ge=1) page_size: int = Field(10, ge=1, le=100)