feat(backend): add market metadata storage and outcomes extension
This commit is contained in:
@@ -7,6 +7,8 @@ from app.schemas.dtos import (
|
|||||||
TokenResponse,
|
TokenResponse,
|
||||||
AccountBalanceDTO,
|
AccountBalanceDTO,
|
||||||
LedgerEntryDTO,
|
LedgerEntryDTO,
|
||||||
|
CreateMarketMetadataRequest,
|
||||||
|
ResolveMarketRequest,
|
||||||
MarketDTO,
|
MarketDTO,
|
||||||
CreateOrderRequest,
|
CreateOrderRequest,
|
||||||
OrderDTO
|
OrderDTO
|
||||||
@@ -24,6 +26,39 @@ async def login_with_wallet(req: UserAuthRequest, session: AsyncSession = Depend
|
|||||||
return TokenResponse(access_token=token, address=req.address.lower())
|
return TokenResponse(access_token=token, address=req.address.lower())
|
||||||
|
|
||||||
# ================= Market Routes =================
|
# ================= Market Routes =================
|
||||||
|
@api_router.post("/markets/metadata", response_model=MarketDTO)
|
||||||
|
async def create_market_metadata(
|
||||||
|
req: CreateMarketMetadataRequest,
|
||||||
|
current_user: str = Depends(get_current_user),
|
||||||
|
session: AsyncSession = Depends(get_db_session)
|
||||||
|
):
|
||||||
|
"""保存预测市场的链下元数据 (标题, 描述, 类别, 2~255个选项)"""
|
||||||
|
market_service = MarketService(session)
|
||||||
|
market = await market_service.save_market_metadata(
|
||||||
|
market_address=req.market_address,
|
||||||
|
question_id=req.question_id,
|
||||||
|
title=req.title,
|
||||||
|
description=req.description,
|
||||||
|
category=req.category,
|
||||||
|
outcomes=req.outcomes,
|
||||||
|
resolution_time=req.resolution_time,
|
||||||
|
creator=current_user
|
||||||
|
)
|
||||||
|
return MarketDTO(
|
||||||
|
market_address=market.market_address,
|
||||||
|
question_id=market.question_id,
|
||||||
|
title=market.title,
|
||||||
|
description=market.description,
|
||||||
|
category=market.category,
|
||||||
|
tier=market.tier,
|
||||||
|
creator=market.creator,
|
||||||
|
status=market.status.value if hasattr(market.status, 'value') else str(market.status),
|
||||||
|
outcomes=market.outcomes,
|
||||||
|
winning_outcome=market.winning_outcome,
|
||||||
|
resolution_time=market.resolution_time,
|
||||||
|
created_at=market.created_at
|
||||||
|
)
|
||||||
|
|
||||||
@api_router.get("/markets", response_model=List[MarketDTO])
|
@api_router.get("/markets", response_model=List[MarketDTO])
|
||||||
async def get_markets(
|
async def get_markets(
|
||||||
category: Optional[str] = None,
|
category: Optional[str] = None,
|
||||||
|
|||||||
@@ -12,6 +12,38 @@ class MarketService:
|
|||||||
def __init__(self, session: AsyncSession):
|
def __init__(self, session: AsyncSession):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
|
async def save_market_metadata(
|
||||||
|
self,
|
||||||
|
market_address: str,
|
||||||
|
question_id: str,
|
||||||
|
title: str,
|
||||||
|
description: Optional[str],
|
||||||
|
category: str,
|
||||||
|
outcomes: List[str],
|
||||||
|
resolution_time,
|
||||||
|
creator: str
|
||||||
|
) -> MarketModel:
|
||||||
|
market = MarketModel(
|
||||||
|
market_address=market_address.lower(),
|
||||||
|
question_id=question_id.lower(),
|
||||||
|
title=title,
|
||||||
|
description=description,
|
||||||
|
category=category or "Others",
|
||||||
|
tier=1,
|
||||||
|
creator=creator.lower(),
|
||||||
|
curve_address="0xF0E189974c413506098AFB6Bc6Bf4C6715fBf7B1",
|
||||||
|
collateral_address="0xe776e957953EA69b7Eaa9d7d4098aBC076bDD5E7",
|
||||||
|
fee_rate=Decimal("0.006"),
|
||||||
|
resolution_time=resolution_time,
|
||||||
|
outcomes=outcomes,
|
||||||
|
num_outcomes=len(outcomes),
|
||||||
|
status=MarketStatus.ACTIVE,
|
||||||
|
created_at_block=0
|
||||||
|
)
|
||||||
|
self.session.add(market)
|
||||||
|
await self.session.flush()
|
||||||
|
return market
|
||||||
|
|
||||||
async def list_markets(self, category: Optional[str] = None, status: Optional[str] = None) -> List[MarketModel]:
|
async def list_markets(self, category: Optional[str] = None, status: Optional[str] = None) -> List[MarketModel]:
|
||||||
stmt = select(MarketModel)
|
stmt = select(MarketModel)
|
||||||
if category:
|
if category:
|
||||||
|
|||||||
+12
-5
@@ -28,13 +28,15 @@ class LedgerEntryDTO(BaseModel):
|
|||||||
related_market: Optional[str] = None
|
related_market: Optional[str] = None
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|
||||||
class CreateMarketRequest(BaseModel):
|
class CreateMarketMetadataRequest(BaseModel):
|
||||||
|
market_address: str
|
||||||
|
question_id: str
|
||||||
title: str
|
title: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
category: str = "Crypto"
|
category: str = "Others" # 默认 Others,支持用户自定义
|
||||||
tier: int = 1
|
outcomes: List[str] = Field(..., min_length=2, max_length=255) # 支持最多 255 个选项
|
||||||
resolution_time: datetime
|
resolution_time: datetime
|
||||||
outcomes: List[str] = Field(..., min_length=2, max_length=8)
|
creator: str
|
||||||
|
|
||||||
class MarketDTO(BaseModel):
|
class MarketDTO(BaseModel):
|
||||||
market_address: str
|
market_address: str
|
||||||
@@ -50,10 +52,15 @@ class MarketDTO(BaseModel):
|
|||||||
resolution_time: datetime
|
resolution_time: datetime
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|
||||||
|
class ResolveMarketRequest(BaseModel):
|
||||||
|
market_address: str
|
||||||
|
question_id: str
|
||||||
|
winning_outcome_index: int = Field(..., ge=0, le=255)
|
||||||
|
|
||||||
class CreateOrderRequest(BaseModel):
|
class CreateOrderRequest(BaseModel):
|
||||||
market_address: str
|
market_address: str
|
||||||
vault_address: Optional[str] = None
|
vault_address: Optional[str] = None
|
||||||
outcome_index: int = Field(..., ge=0, le=7)
|
outcome_index: int = Field(..., ge=0, le=255)
|
||||||
side: str = Field(..., pattern="^(BUY|SELL)$")
|
side: str = Field(..., pattern="^(BUY|SELL)$")
|
||||||
amount: Decimal = Field(..., gt=0)
|
amount: Decimal = Field(..., gt=0)
|
||||||
price: Optional[Decimal] = None
|
price: Optional[Decimal] = None
|
||||||
|
|||||||
Reference in New Issue
Block a user