319 lines
11 KiB
Python
319 lines
11 KiB
Python
"""
|
||
游戏相关路由
|
||
"""
|
||
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))
|