Files
HyperNode-AI/app/schemas.py
T

232 lines
5.4 KiB
Python

"""接口请求与响应。金额在 JSON 里用字符串,避免浮点误差。"""
from __future__ import annotations
from datetime import date, datetime
from decimal import Decimal
from typing import Annotated
from pydantic import BaseModel, ConfigDict, Field, PlainSerializer
from app.models import NodeStatus, TaskStatus, UserRole, WithdrawalStatus, YieldStatus
SN = Annotated[str, Field(min_length=4, max_length=64, pattern=r"^[A-Za-z0-9_-]+$")]
WALLET = Annotated[str, Field(min_length=4, max_length=128, pattern=r"^[A-Za-z0-9:_-]+$")]
USERNAME = Annotated[str, Field(min_length=3, max_length=32, pattern=r"^[A-Za-z0-9_]+$")]
MoneyText = Annotated[Decimal, PlainSerializer(lambda value: format(value, "f"), return_type=str)]
class APIModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
class UserCreate(APIModel):
wallet_address: WALLET
class UserOut(APIModel):
id: int
wallet_address: str
username: str | None = None
role: UserRole = UserRole.USER
created_at: datetime
class RegisterIn(APIModel):
wallet_address: WALLET
username: USERNAME | None = None
password: str | None = Field(default=None, max_length=72)
class LoginIn(APIModel):
wallet_address: str | None = Field(default=None, max_length=128)
username: str | None = Field(default=None, max_length=32)
password: str | None = Field(default=None, max_length=72)
class TokenOut(APIModel):
access_token: str
token_type: str = "bearer"
role: UserRole
wallet_address: str
username: str | None = None
class AdminUserOut(APIModel):
id: int
wallet_address: str
username: str | None
role: UserRole
created_at: datetime
node_count: int
available_points: MoneyText
class NodeCreate(APIModel):
wallet_address: WALLET
sn: SN
purchase_amount: Decimal = Field(gt=0, max_digits=24, decimal_places=8)
callback_url: str | None = None
class NodeOut(APIModel):
id: int
user_id: int
sn: str
purchase_amount: MoneyText
license_months: int
licensed_at: datetime
license_expires_at: datetime
status: NodeStatus
last_heartbeat_at: datetime | None
callback_url: str | None
class NodeView(NodeOut):
wallet_address: str
online_rate: MoneyText = Decimal("0")
today_points: MoneyText = Decimal("0")
class HeartbeatIn(APIModel):
sn: SN
class HeartbeatOut(APIModel):
sn: str
status: NodeStatus
last_heartbeat_at: datetime
server_time: datetime
class TaskOut(APIModel):
task_id: str
task_type: str
payload: dict
status: TaskStatus
dispatched_at: datetime
deadline_at: datetime
class TaskReportIn(APIModel):
sn: SN
task_id: str = Field(min_length=36, max_length=36)
success: bool
nonce: str = Field(min_length=8, max_length=128)
class ContributionOut(APIModel):
sn: str
yield_date: date
eligible: bool
online_seconds: int
eligible_seconds: int
online_rate: MoneyText
tasks_dispatched: int
tasks_completed: int
task_response_rate: MoneyText
online_weight: MoneyText
task_weight: MoneyText
performance: MoneyText
compute_output: MoneyText
points_delta: MoneyText
cycle_days: int
class YieldOut(APIModel):
id: int
node_id: int
user_id: int
yield_date: date
compute_output: MoneyText
points_delta: MoneyText
online_seconds: int
eligible_seconds: int
online_rate: MoneyText
tasks_dispatched: int
tasks_completed: int
task_response_rate: MoneyText
online_weight: MoneyText
task_weight: MoneyText
performance: MoneyText
cycle_days: int
status: YieldStatus
is_partial: bool
created_at: datetime
class SettleOut(APIModel):
yield_date: date
created: int
skipped: int
total_points: MoneyText
is_partial: bool
class WithdrawalCreate(APIModel):
wallet_address: WALLET
amount: Decimal = Field(gt=0, max_digits=24, decimal_places=8)
asset_symbol: str = Field(default="POINT", min_length=1, max_length=32)
class MeWithdrawalCreate(APIModel):
amount: Decimal = Field(gt=0, max_digits=24, decimal_places=8)
asset_symbol: str = Field(default="POINT", min_length=1, max_length=32)
class MeNodeCreate(APIModel):
sn: SN
purchase_amount: Decimal = Field(gt=0, max_digits=24, decimal_places=8)
callback_url: str | None = None
class WithdrawalConfirm(APIModel):
tx_hash: str = Field(min_length=6, max_length=128, pattern=r"^[A-Za-z0-9]+$")
class WithdrawalReject(APIModel):
note: str | None = Field(default=None, max_length=500)
class WithdrawalOut(APIModel):
id: int
user_id: int
amount: MoneyText
asset_symbol: str
tx_hash: str | None
status: WithdrawalStatus
note: str | None
available_after: MoneyText | None = None
created_at: datetime
updated_at: datetime
class WithdrawalView(WithdrawalOut):
wallet_address: str
class BalanceOut(APIModel):
wallet_address: str
available_points: MoneyText
class SummaryOut(APIModel):
users: int
nodes: int
online: int
offline: int
authorized_amount: MoneyText
settled_points: MoneyText
pending_withdrawals: MoneyText
class DashboardOut(APIModel):
wallet_address: str
username: str | None
role: UserRole
available_points: MoneyText
settled_points: MoneyText
today_points: MoneyText
nodes: list[NodeView]
withdrawals: list[WithdrawalOut]