114 lines
4.4 KiB
Python
114 lines
4.4 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, func, desc
|
|
from pydantic import BaseModel
|
|
from decimal import Decimal
|
|
from typing import Optional
|
|
from datetime import datetime
|
|
from app.database import get_db
|
|
from app.models.user import User
|
|
from app.models.bet import Bet
|
|
from app.models.bankroll import Bankroll
|
|
from app.utils.auth import get_current_user
|
|
from app.services.gamification import check_achievements
|
|
|
|
router = APIRouter(prefix="/api/bets", tags=["bets"])
|
|
|
|
class BetCreate(BaseModel):
|
|
sport: str
|
|
event_name: str
|
|
platform: str = "Bet365"
|
|
amount: float
|
|
odds: float
|
|
bet_type: str = ""
|
|
emotion: str = ""
|
|
is_impulsive: bool = False
|
|
|
|
class BetResult(BaseModel):
|
|
result: str # win, loss, void
|
|
|
|
@router.post("")
|
|
async def create_bet(req: BetCreate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
|
bet = Bet(
|
|
user_id=user.id, sport=req.sport, event_name=req.event_name,
|
|
platform=req.platform, amount=req.amount, odds=req.odds,
|
|
bet_type=req.bet_type, emotion=req.emotion, is_impulsive=req.is_impulsive,
|
|
result="pending"
|
|
)
|
|
db.add(bet)
|
|
# Update bankroll spending
|
|
br = (await db.execute(select(Bankroll).where(Bankroll.user_id == user.id))).scalar_one_or_none()
|
|
if br:
|
|
br.month_spent = float(br.month_spent or 0) + req.amount
|
|
br.week_spent = float(br.week_spent or 0) + req.amount
|
|
br.day_spent = float(br.day_spent or 0) + req.amount
|
|
await db.commit()
|
|
await db.refresh(bet)
|
|
await check_achievements(user.id, db)
|
|
return {"id": str(bet.id), "status": "created"}
|
|
|
|
@router.get("")
|
|
async def list_bets(
|
|
sport: Optional[str] = None,
|
|
result: Optional[str] = None,
|
|
limit: int = Query(50, le=200),
|
|
offset: int = 0,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
q = select(Bet).where(Bet.user_id == user.id)
|
|
if sport:
|
|
q = q.where(Bet.sport == sport)
|
|
if result:
|
|
q = q.where(Bet.result == result)
|
|
q = q.order_by(desc(Bet.created_at)).offset(offset).limit(limit)
|
|
rows = (await db.execute(q)).scalars().all()
|
|
return [
|
|
{"id": str(b.id), "sport": b.sport, "event_name": b.event_name, "platform": b.platform,
|
|
"amount": float(b.amount), "odds": float(b.odds) if b.odds else None, "result": b.result,
|
|
"profit": float(b.profit) if b.profit else 0, "emotion": b.emotion,
|
|
"is_impulsive": b.is_impulsive, "created_at": b.created_at.isoformat()}
|
|
for b in rows
|
|
]
|
|
|
|
@router.patch("/{bet_id}/result")
|
|
async def update_result(bet_id: str, req: BetResult, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
|
bet = (await db.execute(select(Bet).where(Bet.id == bet_id, Bet.user_id == user.id))).scalar_one_or_none()
|
|
if not bet:
|
|
raise HTTPException(404, "Bet not found")
|
|
bet.result = req.result
|
|
if req.result == "win":
|
|
bet.profit = float(bet.amount) * (float(bet.odds) - 1)
|
|
elif req.result == "loss":
|
|
bet.profit = -float(bet.amount)
|
|
else:
|
|
bet.profit = 0
|
|
await db.commit()
|
|
return {"id": str(bet.id), "result": bet.result, "profit": float(bet.profit)}
|
|
|
|
@router.get("/stats")
|
|
async def bet_stats(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
|
bets = (await db.execute(select(Bet).where(Bet.user_id == user.id))).scalars().all()
|
|
total = len(bets)
|
|
wins = sum(1 for b in bets if b.result == "win")
|
|
losses = sum(1 for b in bets if b.result == "loss")
|
|
pending = sum(1 for b in bets if b.result == "pending")
|
|
total_profit = sum(float(b.profit or 0) for b in bets)
|
|
total_staked = sum(float(b.amount) for b in bets)
|
|
win_rate = (wins / (wins + losses) * 100) if (wins + losses) > 0 else 0
|
|
roi = (total_profit / total_staked * 100) if total_staked > 0 else 0
|
|
|
|
by_sport = {}
|
|
for b in bets:
|
|
s = b.sport or "other"
|
|
if s not in by_sport:
|
|
by_sport[s] = {"count": 0, "profit": 0}
|
|
by_sport[s]["count"] += 1
|
|
by_sport[s]["profit"] += float(b.profit or 0)
|
|
|
|
return {
|
|
"total": total, "wins": wins, "losses": losses, "pending": pending,
|
|
"total_profit": round(total_profit, 2), "total_staked": round(total_staked, 2),
|
|
"win_rate": round(win_rate, 1), "roi": round(roi, 1), "by_sport": by_sport
|
|
}
|