baoxiang/backend/app/routers/game.py

255 lines
9.1 KiB
Python
Raw Normal View History

2025-12-16 18:06:50 +08:00
"""
游戏相关路由
"""
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)
):
"""
创建宝箱仅主播
"""
2025-12-18 13:28:29 +08:00
print(f"[DEBUG] Creating chest: user={current_user.id}, role={current_user.role}, data={chest_data}")
2025-12-16 18:06:50 +08:00
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:
2025-12-18 13:28:29 +08:00
print(f"[DEBUG] Create chest ValueError: {e}")
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
print(f"[DEBUG] Create chest Exception: {type(e).__name__}: {e}")
2025-12-16 18:06:50 +08:00
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)