
- Set up project structure with FastAPI and SQLite - Implement user authentication with JWT - Create database models for users, events, bets, and transactions - Add API endpoints for user management - Add API endpoints for events and betting functionality - Add wallet management for deposits and withdrawals - Configure Alembic for database migrations - Add linting with Ruff - Add documentation in README
128 lines
3.7 KiB
Python
128 lines
3.7 KiB
Python
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.crud.event import get_outcome
|
|
from app.crud.user import update_user_balance
|
|
from app.models.bet import Bet, BetStatus
|
|
from app.models.transaction import Transaction, TransactionStatus, TransactionType
|
|
from app.schemas.bet import BetCreate
|
|
|
|
|
|
def get_bet(db: Session, bet_id: int) -> Optional[Bet]:
|
|
return db.query(Bet).filter(Bet.id == bet_id).first()
|
|
|
|
|
|
def get_user_bets(
|
|
db: Session, user_id: int, skip: int = 0, limit: int = 100, status: Optional[BetStatus] = None,
|
|
) -> List[Bet]:
|
|
query = db.query(Bet).filter(Bet.user_id == user_id)
|
|
if status:
|
|
query = query.filter(Bet.status == status)
|
|
return query.order_by(Bet.created_at.desc()).offset(skip).limit(limit).all()
|
|
|
|
|
|
def create_bet(db: Session, bet_in: BetCreate, user_id: int) -> Optional[Bet]:
|
|
# Get the outcome to check if it exists and get odds
|
|
outcome = get_outcome(db, bet_in.outcome_id)
|
|
if not outcome or not outcome.is_active:
|
|
return None
|
|
|
|
# Calculate potential win
|
|
potential_win = bet_in.amount * outcome.odds
|
|
|
|
# Create bet
|
|
db_bet = Bet(
|
|
user_id=user_id,
|
|
outcome_id=bet_in.outcome_id,
|
|
amount=bet_in.amount,
|
|
odds=outcome.odds,
|
|
potential_win=potential_win,
|
|
status=BetStatus.PENDING,
|
|
)
|
|
|
|
# Update user balance
|
|
update_user_balance(db, user_id, -bet_in.amount)
|
|
|
|
# Create transaction record
|
|
transaction = Transaction(
|
|
user_id=user_id,
|
|
amount=-bet_in.amount,
|
|
transaction_type=TransactionType.BET_PLACED,
|
|
status=TransactionStatus.COMPLETED,
|
|
bet_id=db_bet.id,
|
|
)
|
|
|
|
db.add(db_bet)
|
|
db.commit()
|
|
db.refresh(db_bet)
|
|
|
|
# Update transaction with bet_id
|
|
transaction.bet_id = db_bet.id
|
|
db.add(transaction)
|
|
db.commit()
|
|
|
|
return db_bet
|
|
|
|
|
|
def update_bet_status(
|
|
db: Session, bet_id: int, status: BetStatus,
|
|
) -> Optional[Bet]:
|
|
bet = get_bet(db, bet_id)
|
|
if bet and bet.status == BetStatus.PENDING:
|
|
bet.status = status
|
|
bet.settled_at = datetime.utcnow()
|
|
|
|
# If bet is won, create a transaction and update user balance
|
|
if status == BetStatus.WON:
|
|
transaction = Transaction(
|
|
user_id=bet.user_id,
|
|
amount=bet.potential_win,
|
|
transaction_type=TransactionType.BET_WON,
|
|
status=TransactionStatus.COMPLETED,
|
|
bet_id=bet.id,
|
|
)
|
|
db.add(transaction)
|
|
update_user_balance(db, bet.user_id, bet.potential_win)
|
|
|
|
# If bet is cancelled or voided, refund the amount
|
|
if status in [BetStatus.CANCELLED, BetStatus.VOIDED]:
|
|
transaction = Transaction(
|
|
user_id=bet.user_id,
|
|
amount=bet.amount,
|
|
transaction_type=TransactionType.BET_REFUND,
|
|
status=TransactionStatus.COMPLETED,
|
|
bet_id=bet.id,
|
|
)
|
|
db.add(transaction)
|
|
update_user_balance(db, bet.user_id, bet.amount)
|
|
|
|
db.add(bet)
|
|
db.commit()
|
|
db.refresh(bet)
|
|
|
|
return bet
|
|
|
|
|
|
def settle_bets_for_outcome(
|
|
db: Session, outcome_id: int, is_winner: bool,
|
|
) -> int:
|
|
"""
|
|
Settle all pending bets for a specific outcome.
|
|
Returns the number of bets settled.
|
|
"""
|
|
bets = db.query(Bet).filter(
|
|
Bet.outcome_id == outcome_id,
|
|
Bet.status == BetStatus.PENDING,
|
|
).all()
|
|
|
|
count = 0
|
|
for bet in bets:
|
|
if is_winner:
|
|
update_bet_status(db, bet.id, BetStatus.WON)
|
|
else:
|
|
update_bet_status(db, bet.id, BetStatus.LOST)
|
|
count += 1
|
|
|
|
return count |