""" 游戏相关路由 """ 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, ChestTemplateCreate, ChestTemplateUpdate, ChestTemplateResponse ) 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) ): """ 创建宝箱(仅主播) """ print(f"[DEBUG] Creating chest: user={current_user.id}, role={current_user.role}, data={chest_data}") 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: 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}") 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) # ==================== 宝箱模板相关路由 ==================== @router.get("/templates", response_model=List[ChestTemplateResponse]) def get_templates( db: Session = Depends(get_db), current_user: get_current_streamer = Depends(get_current_streamer) ): """ 获取当前主播的宝箱模板列表 """ templates = GameService.get_templates_by_streamer(db, current_user.id) return templates @router.post("/templates", response_model=ChestTemplateResponse) def create_template( template_data: ChestTemplateCreate, db: Session = Depends(get_db), current_user: get_current_streamer = Depends(get_current_streamer) ): """ 创建宝箱模板 """ try: template = GameService.create_template(db, current_user.id, template_data) return template except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.put("/templates/{template_id}", response_model=ChestTemplateResponse) def update_template( template_id: int, template_data: ChestTemplateUpdate, db: Session = Depends(get_db), current_user: get_current_streamer = Depends(get_current_streamer) ): """ 更新宝箱模板 """ try: template = GameService.update_template(db, template_id, current_user.id, template_data) return template except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.delete("/templates/{template_id}") def delete_template( template_id: int, db: Session = Depends(get_db), current_user: get_current_streamer = Depends(get_current_streamer) ): """ 删除宝箱模板 """ try: GameService.delete_template(db, template_id, current_user.id) return {"message": "删除成功"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e))