fix: 修复登录500错误和移除明文密码提示

- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service)
- 移除登录页面上的明文密码提示
- 改进前端错误处理,避免数据加载失败导致登录显示错误
This commit is contained in:
2026-07-02 12:23:55 +08:00
parent 3fbe6c9be5
commit 918bbed0fc
63 changed files with 16699 additions and 10 deletions
+367
View File
@@ -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