baoxiang/backend/app/routers/game.py
2025-12-16 18:06:50 +08:00

250 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
游戏相关路由
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from datetime import datetime
from ..core.database import get_db
from ..schemas.game import (
ChestCreate, ChestResponse, BetCreate, BetResponse,
ChestSettle, ChestLock
)
from ..services.game_service import GameService
from ..models.game import ChestStatus
from ..utils.deps import get_current_user, get_current_streamer
router = APIRouter(prefix="/api", tags=["game"])
@router.post("/chests", response_model=ChestResponse)
def create_chest(
chest_data: ChestCreate,
db: Session = Depends(get_db),
current_user: get_current_streamer = Depends(get_current_streamer)
):
"""
创建宝箱(仅主播)
"""
try:
chest = GameService.create_chest(db, current_user, chest_data)
# 计算剩余时间
time_remaining = 0
if chest.status == ChestStatus.BETTING:
# 使用本地时间进行计算与数据库func.now()保持一致)
if isinstance(chest.created_at, str):
if 'T' in chest.created_at:
created_time = datetime.fromisoformat(chest.created_at.replace('Z', ''))
else:
created_time = datetime.strptime(chest.created_at, '%Y-%m-%d %H:%M:%S')
else:
created_time = chest.created_at.replace(tzinfo=None) if chest.created_at and chest.created_at.tzinfo else chest.created_at
# 使用本地时间进行计算func.now()返回的是服务器本地时间)
now = datetime.now()
elapsed = (now - created_time).total_seconds()
time_remaining = max(0, int(chest.countdown_seconds - elapsed))
print(f'Created chest {chest.id}: created_at={created_time}, now_local={now}, elapsed={elapsed}, time_remaining={time_remaining}')
# 返回包含time_remaining的响应
return ChestResponse(
id=chest.id,
streamer_id=chest.streamer_id,
title=chest.title,
option_a=chest.option_a,
option_b=chest.option_b,
status=chest.status.value if hasattr(chest.status, 'value') else chest.status,
pool_a=chest.pool_a,
pool_b=chest.pool_b,
total_bets=chest.total_bets,
countdown_seconds=chest.countdown_seconds,
time_remaining=time_remaining,
locked_at=chest.locked_at,
settled_at=chest.settled_at,
result=chest.result,
created_at=chest.created_at
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.get("/chests", response_model=List[ChestResponse])
def get_chests(
streamer_id: int = None,
include_history: bool = False,
db: Session = Depends(get_db)
):
"""
获取宝箱列表
"""
if include_history and streamer_id:
# 获取指定主播的所有宝箱(包括历史)
chests = GameService.get_chests_by_streamer(db, streamer_id)
else:
# 默认行为:获取活跃宝箱
chests = GameService.get_active_chests(db, streamer_id)
# 为每个宝箱计算剩余时间并转换为响应模型
response_chests = []
for chest in chests:
# 计算time_remaining
time_remaining = 0
if chest.status == ChestStatus.BETTING:
# 使用本地时间进行计算与数据库func.now()保持一致)
if isinstance(chest.created_at, str):
if 'T' in chest.created_at:
created_time = datetime.fromisoformat(chest.created_at.replace('Z', ''))
else:
created_time = datetime.strptime(chest.created_at, '%Y-%m-%d %H:%M:%S')
else:
created_time = chest.created_at.replace(tzinfo=None) if chest.created_at and chest.created_at.tzinfo else chest.created_at
# 使用本地时间进行计算func.now()返回的是服务器本地时间)
now = datetime.now()
elapsed = (now - created_time).total_seconds()
time_remaining = max(0, int(chest.countdown_seconds - elapsed))
print(f'Chest {chest.id}: created_at={created_time}, now_local={now}, elapsed={elapsed}, time_remaining={time_remaining}')
# 转换为响应模型
chest_response = ChestResponse(
id=chest.id,
streamer_id=chest.streamer_id,
title=chest.title,
option_a=chest.option_a,
option_b=chest.option_b,
status=chest.status.value if hasattr(chest.status, 'value') else chest.status,
pool_a=chest.pool_a,
pool_b=chest.pool_b,
total_bets=chest.total_bets,
countdown_seconds=chest.countdown_seconds,
time_remaining=time_remaining,
locked_at=chest.locked_at,
settled_at=chest.settled_at,
result=chest.result,
created_at=chest.created_at
)
response_chests.append(chest_response)
return response_chests
@router.get("/chests/{chest_id}", response_model=ChestResponse)
def get_chest(
chest_id: int,
db: Session = Depends(get_db)
):
"""
获取宝箱详情
"""
chest = GameService.get_chest_by_id(db, chest_id)
if not chest:
raise HTTPException(status_code=404, detail="Chest not found")
# 计算剩余时间
time_remaining = 0
if chest.status == ChestStatus.BETTING:
# 使用本地时间进行计算与数据库func.now()保持一致)
if isinstance(chest.created_at, str):
if 'T' in chest.created_at:
created_time = datetime.fromisoformat(chest.created_at.replace('Z', ''))
else:
created_time = datetime.strptime(chest.created_at, '%Y-%m-%d %H:%M:%S')
else:
created_time = chest.created_at.replace(tzinfo=None) if chest.created_at and chest.created_at.tzinfo else chest.created_at
# 使用本地时间进行计算func.now()返回的是服务器本地时间)
now = datetime.now()
elapsed = (now - created_time).total_seconds()
time_remaining = max(0, int(chest.countdown_seconds - elapsed))
print(f"Single chest {chest.id}: created_at={created_time}, now_local={now}, elapsed={elapsed}, time_remaining={time_remaining}")
else:
time_remaining = 0
# 返回包含time_remaining的响应
return ChestResponse(
id=chest.id,
streamer_id=chest.streamer_id,
title=chest.title,
option_a=chest.option_a,
option_b=chest.option_b,
status=chest.status.value if hasattr(chest.status, 'value') else chest.status,
pool_a=chest.pool_a,
pool_b=chest.pool_b,
total_bets=chest.total_bets,
countdown_seconds=chest.countdown_seconds,
time_remaining=time_remaining,
locked_at=chest.locked_at,
settled_at=chest.settled_at,
result=chest.result,
created_at=chest.created_at
)
@router.post("/bet", response_model=BetResponse)
def place_bet(
bet_data: BetCreate,
db: Session = Depends(get_db),
current_user: get_current_user = Depends(get_current_user)
):
"""
下注
"""
try:
return GameService.place_bet(db, current_user, bet_data)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post("/chests/{chest_id}/lock")
def lock_chest(
chest_id: int,
lock_data: ChestLock,
db: Session = Depends(get_db),
current_user: get_current_streamer = Depends(get_current_streamer)
):
"""
封盘(仅主播)
"""
chest = GameService.get_chest_by_id(db, chest_id)
if not chest:
raise HTTPException(status_code=404, detail="Chest not found")
try:
GameService.lock_chest(db, chest, current_user)
return {"message": "封盘成功"}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post("/chests/{chest_id}/settle")
def settle_chest(
chest_id: int,
settle_data: ChestSettle,
db: Session = Depends(get_db),
current_user: get_current_streamer = Depends(get_current_streamer)
):
"""
结算宝箱(仅主播)
"""
chest = GameService.get_chest_by_id(db, chest_id)
if not chest:
raise HTTPException(status_code=404, detail="Chest not found")
try:
GameService.settle_chest(db, chest, settle_data.result, current_user)
return {"message": "结算成功"}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.get("/chests/{chest_id}/bets", response_model=List[BetResponse])
def get_chest_bets(
chest_id: int,
db: Session = Depends(get_db)
):
"""
获取宝箱下注记录
"""
return GameService.get_chest_bets(db, chest_id)