feat(backend): complete modular monolith with ledger, fastapi and websocket

This commit is contained in:
Bot
2026-08-31 01:02:16 +08:00
parent 9f6ecf33f9
commit 30dc22a989
16 changed files with 706 additions and 17 deletions
Binary file not shown.
Binary file not shown.
+120
View File
@@ -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)