feat(backend): complete modular monolith with ledger, fastapi and websocket
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import enum
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
String,
|
||||
BigInteger,
|
||||
Integer,
|
||||
Numeric,
|
||||
DateTime,
|
||||
Enum,
|
||||
Index,
|
||||
JSON,
|
||||
ForeignKey,
|
||||
Boolean,
|
||||
Text,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class LedgerEntryType(str, enum.Enum):
|
||||
"""复式记账操作类型"""
|
||||
DEPOSIT = "DEPOSIT" # 链上充值
|
||||
WITHDRAW = "WITHDRAW" # 提现
|
||||
TRADE_BUY = "TRADE_BUY" # 现货/曲线买入扣保证金
|
||||
TRADE_SELL = "TRADE_SELL" # 现货/曲线卖出增保证金
|
||||
FEE_PROTOCOL = "FEE_PROTOCOL" # 协议手续费扣除
|
||||
FEE_CREATOR_REBATE = "FEE_CREATOR_REBATE" # 建盘者返佣
|
||||
ORDER_FREEZE = "ORDER_FREEZE" # 挂单冻结
|
||||
ORDER_UNFREEZE = "ORDER_UNFREEZE" # 撤单解冻
|
||||
SETTLEMENT_PAYOUT = "SETTLEMENT_PAYOUT" # 最终获胜兑付
|
||||
|
||||
class MarketStatus(str, enum.Enum):
|
||||
ACTIVE = "ACTIVE"
|
||||
RESOLVED = "RESOLVED"
|
||||
FINALISED = "FINALISED"
|
||||
PAUSED = "PAUSED"
|
||||
|
||||
class UserModel(Base):
|
||||
"""用户基础表"""
|
||||
__tablename__ = "users"
|
||||
|
||||
address = Column(String(42), primary_key=True, index=True) # Checksum EVM Address
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
last_login_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
|
||||
class AccountBalanceModel(Base):
|
||||
"""账户资金总览表 (由 Ledger 聚合维护)"""
|
||||
__tablename__ = "account_balances"
|
||||
|
||||
address = Column(String(42), primary_key=True, index=True)
|
||||
collateral_symbol = Column(String(16), primary_key=True, default="WUSD")
|
||||
available_balance = Column(Numeric(precision=36, scale=18), default=Decimal("0"), nullable=False)
|
||||
frozen_balance = Column(Numeric(precision=36, scale=18), default=Decimal("0"), nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
class LedgerEntryModel(Base):
|
||||
"""
|
||||
不可变复式记账明细表 (Immutable Ledger)
|
||||
资金流转与状态变更的唯一法定事实源
|
||||
"""
|
||||
__tablename__ = "ledger_entries"
|
||||
|
||||
id = Column(BigInteger().with_variant(Integer, "sqlite"), primary_key=True, autoincrement=True)
|
||||
tx_hash = Column(String(66), nullable=True, index=True)
|
||||
chain_id = Column(BigInteger, default=46630, nullable=False)
|
||||
user_address = Column(String(42), nullable=False, index=True)
|
||||
entry_type = Column(Enum(LedgerEntryType), nullable=False, index=True)
|
||||
amount = Column(Numeric(precision=36, scale=18), nullable=False) # 变动金额 (+ / -)
|
||||
balance_after = Column(Numeric(precision=36, scale=18), nullable=False) # 变动后可用余额
|
||||
frozen_after = Column(Numeric(precision=36, scale=18), nullable=False) # 变动后冻结金额
|
||||
related_market = Column(String(42), nullable=True, index=True)
|
||||
related_order_id = Column(String(64), nullable=True, index=True)
|
||||
extra_metadata = Column(JSON, default=dict)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_ledger_user_created", "user_address", "created_at"),
|
||||
)
|
||||
|
||||
class MarketModel(Base):
|
||||
"""预测市场主表"""
|
||||
__tablename__ = "markets"
|
||||
|
||||
market_address = Column(String(42), primary_key=True)
|
||||
question_id = Column(String(66), unique=True, nullable=False, index=True)
|
||||
title = Column(String(512), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
category = Column(String(64), default="Crypto", nullable=False, index=True)
|
||||
tier = Column(BigInteger, default=1, nullable=False)
|
||||
creator = Column(String(42), nullable=False, index=True)
|
||||
curve_address = Column(String(42), nullable=False)
|
||||
collateral_address = Column(String(42), nullable=False)
|
||||
fee_rate = Column(Numeric(precision=36, scale=18), default=Decimal("0.006"), nullable=False)
|
||||
resolution_time = Column(DateTime, nullable=False)
|
||||
outcomes = Column(JSON, nullable=False) # e.g. ["YES", "NO"]
|
||||
num_outcomes = Column(BigInteger, default=2, nullable=False)
|
||||
status = Column(Enum(MarketStatus), default=MarketStatus.ACTIVE, nullable=False, index=True)
|
||||
winning_outcome = Column(BigInteger, nullable=True)
|
||||
created_at_block = Column(BigInteger, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
||||
class OrderModel(Base):
|
||||
"""链下/链上委托订单明细"""
|
||||
__tablename__ = "orders"
|
||||
|
||||
order_id = Column(String(64), primary_key=True)
|
||||
market_address = Column(String(42), ForeignKey("markets.market_address"), nullable=False, index=True)
|
||||
user_address = Column(String(42), nullable=False, index=True)
|
||||
side = Column(String(8), nullable=False) # "BUY" | "SELL"
|
||||
outcome_index = Column(BigInteger, nullable=False)
|
||||
amount = Column(Numeric(precision=36, scale=18), nullable=False) # WUSD 或 Token 数量
|
||||
price = Column(Numeric(precision=36, scale=18), nullable=True) # 限价 (若为市价则为空)
|
||||
status = Column(String(16), default="FILLED", nullable=False) # PENDING, FILLED, CANCELLED
|
||||
filled_amount = Column(Numeric(precision=36, scale=18), default=Decimal("0"), nullable=False)
|
||||
fee = Column(Numeric(precision=36, scale=18), default=Decimal("0"), nullable=False)
|
||||
tx_hash = Column(String(66), nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
Reference in New Issue
Block a user