fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service) - 移除登录页面上的明文密码提示 - 改进前端错误处理,避免数据加载失败导致登录显示错误
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
"""
|
||||
Pydantic 数据模型定义
|
||||
用于 API 请求和响应的数据验证
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Any
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
# ============================================
|
||||
# 认证模型
|
||||
# ============================================
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
"""登录请求"""
|
||||
username: str = Field(..., min_length=1, max_length=50)
|
||||
password: str = Field(..., min_length=1, max_length=100)
|
||||
|
||||
@field_validator('username', 'password')
|
||||
def no_whitespace(cls, v):
|
||||
if v.strip() != v:
|
||||
raise ValueError('不能包含前后空格')
|
||||
return v
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
"""Token 响应"""
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
expires_in: int # 秒数
|
||||
refresh_token: Optional[str] = None
|
||||
|
||||
|
||||
class TokenVerifyRequest(BaseModel):
|
||||
"""Token 验证请求"""
|
||||
token: str
|
||||
|
||||
|
||||
class TokenVerifyResponse(BaseModel):
|
||||
"""Token 验证响应"""
|
||||
valid: bool
|
||||
user_info: Optional[dict] = None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 用户模型
|
||||
# ============================================
|
||||
|
||||
class User(BaseModel):
|
||||
"""用户信息"""
|
||||
username: str
|
||||
email: Optional[str] = None
|
||||
full_name: Optional[str] = None
|
||||
is_active: bool = True
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class UserCreate(BaseModel):
|
||||
"""创建用户"""
|
||||
username: str = Field(..., min_length=3, max_length=50)
|
||||
password: str = Field(..., min_length=6, max_length=100)
|
||||
email: Optional[str] = None
|
||||
full_name: Optional[str] = None
|
||||
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
"""更新用户"""
|
||||
email: Optional[str] = None
|
||||
full_name: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 交易模型
|
||||
# ============================================
|
||||
|
||||
class OrderDirection(str, Enum):
|
||||
"""订单方向"""
|
||||
BUY = "buy"
|
||||
SELL = "sell"
|
||||
|
||||
|
||||
class OrderType(str, Enum):
|
||||
"""订单类型"""
|
||||
LIMIT = "limit"
|
||||
MARKET = "market"
|
||||
STOP = "stop"
|
||||
|
||||
|
||||
class SendOrderRequest(BaseModel):
|
||||
"""发送订单请求"""
|
||||
symbol: str = Field(..., description="交易品种代码")
|
||||
exchange: str = Field(..., description="交易所代码")
|
||||
direction: OrderDirection
|
||||
order_type: OrderType
|
||||
volume: float = Field(..., gt=0, description="数量")
|
||||
price: Optional[float] = Field(None, gt=0, description="价格(限价单必填)")
|
||||
stop_price: Optional[float] = Field(None, gt=0, description="止损价")
|
||||
reference: Optional[str] = Field(None, description="客户引用")
|
||||
|
||||
|
||||
class CancelOrderRequest(BaseModel):
|
||||
"""撤单请求"""
|
||||
order_id: str = Field(..., description="订单号")
|
||||
|
||||
|
||||
class OrderResponse(BaseModel):
|
||||
"""订单响应"""
|
||||
order_id: str
|
||||
symbol: str
|
||||
exchange: str
|
||||
direction: str
|
||||
order_type: str
|
||||
volume: float
|
||||
price: Optional[float]
|
||||
traded: float
|
||||
status: str
|
||||
time: datetime
|
||||
reference: Optional[str] = None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 行情数据模型
|
||||
# ============================================
|
||||
|
||||
class TickData(BaseModel):
|
||||
"""Tick 数据"""
|
||||
symbol: str
|
||||
exchange: str
|
||||
datetime: datetime
|
||||
name: Optional[str] = None
|
||||
last_price: float
|
||||
bid_price_1: Optional[float] = None
|
||||
ask_price_1: Optional[float] = None
|
||||
bid_volume_1: Optional[float] = None
|
||||
ask_volume_1: Optional[float] = None
|
||||
volume: Optional[float] = None
|
||||
open_interest: Optional[float] = None
|
||||
|
||||
|
||||
class KlineRequest(BaseModel):
|
||||
"""K线数据请求"""
|
||||
symbol: str = Field(..., description="交易品种代码")
|
||||
exchange: str = Field(..., description="交易所代码")
|
||||
interval: str = Field(..., description="周期: 1m, 5m, 15m, 1h, 4h, 1d")
|
||||
start: Optional[datetime] = Field(None, description="开始时间")
|
||||
end: Optional[datetime] = Field(None, description="结束时间")
|
||||
limit: int = Field(1000, ge=1, le=10000, description="数据条数限制")
|
||||
|
||||
|
||||
class KlineData(BaseModel):
|
||||
"""K线数据"""
|
||||
symbol: str
|
||||
exchange: str
|
||||
datetime: datetime
|
||||
interval: str
|
||||
open_price: float
|
||||
high_price: float
|
||||
low_price: float
|
||||
close_price: float
|
||||
volume: float
|
||||
open_interest: Optional[float] = None
|
||||
|
||||
|
||||
class SubscribeRequest(BaseModel):
|
||||
"""订阅行情请求"""
|
||||
symbol: str = Field(..., description="交易品种代码")
|
||||
exchange: str = Field(..., description="交易所代码")
|
||||
gateway_name: Optional[str] = Field(None, description="网关名称")
|
||||
|
||||
|
||||
class UnsubscribeRequest(BaseModel):
|
||||
"""取消订阅请求"""
|
||||
symbol: str = Field(..., description="交易品种代码")
|
||||
exchange: str = Field(..., description="交易所代码")
|
||||
gateway_name: Optional[str] = Field(None, description="网关名称")
|
||||
|
||||
|
||||
# ============================================
|
||||
# 持仓数据模型
|
||||
# ============================================
|
||||
|
||||
class PositionData(BaseModel):
|
||||
"""持仓数据"""
|
||||
symbol: str
|
||||
exchange: str
|
||||
direction: str
|
||||
volume: float
|
||||
price: float
|
||||
pnl: float
|
||||
pnl_ratio: float
|
||||
frozen: float = 0.0
|
||||
yd_volume: float = 0.0
|
||||
|
||||
|
||||
# ============================================
|
||||
# 账户数据模型
|
||||
# ============================================
|
||||
|
||||
class AccountData(BaseModel):
|
||||
"""账户数据"""
|
||||
account_id: str
|
||||
balance: float
|
||||
available: float
|
||||
frozen: float = 0.0
|
||||
margin: float = 0.0
|
||||
|
||||
|
||||
class TradeData(BaseModel):
|
||||
"""成交数据"""
|
||||
trade_id: str
|
||||
order_id: str
|
||||
symbol: str
|
||||
exchange: str
|
||||
direction: str
|
||||
volume: float
|
||||
price: float
|
||||
time: datetime
|
||||
|
||||
|
||||
class AccountResponse(BaseModel):
|
||||
"""账户响应"""
|
||||
account: AccountData
|
||||
positions: List[PositionData]
|
||||
orders: List[OrderResponse]
|
||||
|
||||
|
||||
# ============================================
|
||||
# 网关模型
|
||||
# ============================================
|
||||
|
||||
class GatewayType(str, Enum):
|
||||
"""网关类型"""
|
||||
CTP = "ctp"
|
||||
IB = "ib"
|
||||
OKX = "okx"
|
||||
BINAANCE = "binance"
|
||||
TEST = "test"
|
||||
|
||||
|
||||
class GatewayStatus(BaseModel):
|
||||
"""网关状态"""
|
||||
gateway_name: str
|
||||
gateway_type: GatewayType
|
||||
status: str # connected, disconnected, connecting
|
||||
connected_time: Optional[datetime] = None
|
||||
message: Optional[str] = None
|
||||
|
||||
|
||||
class GatewayConnectRequest(BaseModel):
|
||||
"""连接网关请求"""
|
||||
gateway_name: str = Field(..., description="网关名称")
|
||||
gateway_type: GatewayType
|
||||
setting: dict = Field(..., description="网关配置参数")
|
||||
|
||||
|
||||
class GatewayDisconnectRequest(BaseModel):
|
||||
"""断开网关请求"""
|
||||
gateway_name: str
|
||||
|
||||
|
||||
# ============================================
|
||||
# 策略模型
|
||||
# ============================================
|
||||
|
||||
class StrategyStatus(str, Enum):
|
||||
"""策略状态"""
|
||||
CREATED = "created"
|
||||
RUNNING = "running"
|
||||
STOPPED = "stopped"
|
||||
PAUSED = "paused"
|
||||
|
||||
|
||||
class StrategyInfo(BaseModel):
|
||||
"""策略信息"""
|
||||
strategy_name: str
|
||||
class_name: str
|
||||
status: StrategyStatus
|
||||
created_at: datetime
|
||||
variables: Optional[dict] = None
|
||||
|
||||
|
||||
class StrategyCreateRequest(BaseModel):
|
||||
"""创建策略请求"""
|
||||
strategy_name: str = Field(..., min_length=1, max_length=50)
|
||||
class_name: str = Field(..., description="策略类名")
|
||||
setting: dict = Field(default_factory=dict, description="策略参数")
|
||||
|
||||
|
||||
class StrategyInitRequest(BaseModel):
|
||||
"""初始化策略请求"""
|
||||
strategy_name: str
|
||||
|
||||
|
||||
class StrategyStartRequest(BaseModel):
|
||||
"""启动策略请求"""
|
||||
strategy_name: str
|
||||
|
||||
|
||||
class StrategyStopRequest(BaseModel):
|
||||
"""停止策略请求"""
|
||||
strategy_name: str
|
||||
|
||||
|
||||
class StrategyEditRequest(BaseModel):
|
||||
"""编辑策略请求"""
|
||||
strategy_name: str
|
||||
setting: dict
|
||||
|
||||
|
||||
# ============================================
|
||||
# 响应模型
|
||||
# ============================================
|
||||
|
||||
class ApiResponse(BaseModel):
|
||||
"""通用 API 响应"""
|
||||
success: bool
|
||||
message: Optional[str] = None
|
||||
data: Optional[Any] = None
|
||||
|
||||
|
||||
class PaginatedResponse(BaseModel):
|
||||
"""分页响应"""
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
data: List[Any]
|
||||
|
||||
|
||||
# ============================================
|
||||
# 系统模型
|
||||
# ============================================
|
||||
|
||||
class HealthResponse(BaseModel):
|
||||
"""健康检查响应"""
|
||||
status: str
|
||||
service: str
|
||||
version: str
|
||||
timestamp: datetime
|
||||
|
||||
|
||||
class SystemInfo(BaseModel):
|
||||
"""系统信息"""
|
||||
version: str
|
||||
vnpy_version: str
|
||||
python_version: str
|
||||
uptime: float
|
||||
connected_gateways: int
|
||||
active_strategies: int
|
||||
|
||||
|
||||
# ============================================
|
||||
# WebSocket 模型
|
||||
# ============================================
|
||||
|
||||
class WSSubscribeRequest(BaseModel):
|
||||
"""WebSocket 订阅请求"""
|
||||
type: str # tick, order, position, trade
|
||||
symbols: Optional[List[str]] = None
|
||||
|
||||
|
||||
class WSMessage(BaseModel):
|
||||
"""WebSocket 消息"""
|
||||
type: str
|
||||
data: Any
|
||||
timestamp: datetime
|
||||
Reference in New Issue
Block a user