phase 3 18 api tournament

This commit is contained in:
2026-03-06 20:03:16 +00:00
parent 94fa7c2df1
commit 90d3af253d
2 changed files with 61 additions and 3 deletions

View File

@@ -1,15 +1,64 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import func, select
from sqlalchemy import and_, case, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from api.deps import get_current_superuser
from database.models import Seat, Tournament, User
from database.models import Seat, Ticket, TicketStatus, Tournament, User
from database.session import get_db
from schemas.tournament import SeatGenerateRequest, TournamentCreate, TournamentResponse
from schemas.tournament import SeatGenerateRequest, SeatResponse, TournamentCreate, TournamentResponse
router = APIRouter(prefix="/api/tournaments", tags=["tournaments"])
@router.get("/{tournament_id}/seats", response_model=list[SeatResponse])
async def get_tournament_seats(
tournament_id: int,
db: AsyncSession = Depends(get_db),
) -> list[SeatResponse]:
"""
Публичный эндпоинт. Возвращает все места турнира с флагом is_available.
Место недоступно (is_available=False), если связанный Ticket
находится в статусе LOCKED или PAID.
Используется LEFT OUTER JOIN + CASE для вычисления флага одним запросом.
"""
# 1. Вычисляем is_available через CASE на стороне БД
is_available_expr = case(
(
and_(
Ticket.id.is_not(None),
Ticket.status.in_([TicketStatus.LOCKED, TicketStatus.PAID]),
),
False,
),
else_=True,
).label("is_available")
# 2. LEFT JOIN: берём все места турнира, присоединяем Ticket (если есть)
stmt = (
select(Seat, is_available_expr)
.outerjoin(Ticket, Ticket.seat_id == Seat.id)
.where(Seat.tournament_id == tournament_id)
.order_by(Seat.sector, Seat.row, Seat.number)
)
# 3. Выполняем запрос и формируем ответ
result = await db.execute(stmt)
rows = result.all()
return [
SeatResponse(
id=seat.id,
sector=seat.sector,
row=seat.row,
number=seat.number,
price=seat.price,
is_available=bool(is_available),
)
for seat, is_available in rows
]
@router.post("", response_model=TournamentResponse, status_code=status.HTTP_201_CREATED)
async def create_tournament(
body: TournamentCreate,