Files
ticket-system/backend/database/models.py
openit 5cc5efd2e0
All checks were successful
Deploy / deploy (push) Successful in 19s
add logging
2026-03-12 14:20:45 +00:00

105 lines
4.3 KiB
Python

import enum
import uuid
from datetime import datetime, timezone
from sqlalchemy import String, Integer, ForeignKey, DateTime, Enum, Boolean
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Base(DeclarativeBase):
pass
class TicketStatus(str, enum.Enum):
AVAILABLE = "AVAILABLE"
LOCKED = "LOCKED"
PAID = "PAID"
SCANNED = "SCANNED"
REFUNDED = "REFUNDED"
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(String, unique=True, index=True)
hashed_password: Mapped[str] = mapped_column(String)
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False)
tickets: Mapped[list["Ticket"]] = relationship(back_populates="user")
class Tournament(Base):
__tablename__ = "tournaments"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String)
description: Mapped[str | None] = mapped_column(String, nullable=True)
event_date: Mapped[datetime] = mapped_column(DateTime(timezone=True))
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
seats: Mapped[list["Seat"]] = relationship(back_populates="tournament")
class Seat(Base):
__tablename__ = "seats"
id: Mapped[int] = mapped_column(primary_key=True)
tournament_id: Mapped[int] = mapped_column(ForeignKey("tournaments.id"), index=True)
sector: Mapped[str] = mapped_column(String)
row: Mapped[int] = mapped_column(Integer)
number: Mapped[int] = mapped_column(Integer)
price: Mapped[int] = mapped_column(Integer)
tournament: Mapped["Tournament"] = relationship(back_populates="seats")
ticket: Mapped["Ticket"] = relationship(back_populates="seat", uselist=False)
class Ticket(Base):
__tablename__ = "tickets"
id: Mapped[int] = mapped_column(primary_key=True)
seat_id: Mapped[int] = mapped_column(ForeignKey("seats.id"), unique=True, index=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
status: Mapped[TicketStatus] = mapped_column(
Enum(TicketStatus, name="ticket_status_enum", create_type=False),
default=TicketStatus.AVAILABLE,
index=True
)
idempotency_key: Mapped[str] = mapped_column(String, unique=True, nullable=True)
pdf_url: Mapped[str | None] = mapped_column(String, nullable=True)
secret_token: Mapped[str | None] = mapped_column(
String, unique=True, index=True, nullable=True,
default=lambda: str(uuid.uuid4()),
)
# --- Поля для эквайринга ---
payment_id: Mapped[str | None] = mapped_column(String, index=True, nullable=True)
payment_url: Mapped[str | None] = mapped_column(String, nullable=True)
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
# ---------------------------
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc)
)
seat: Mapped["Seat"] = relationship(back_populates="ticket")
user: Mapped["User"] = relationship(back_populates="tickets")
class ActionLog(Base):
"""Audit trail: every mutating request is recorded here by AuditLogMiddleware."""
__tablename__ = "action_logs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
# nullable — anonymous / unauthenticated requests (e.g. /api/auth/register)
user_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
# "POST /api/tickets/book", "DELETE /api/seats/42", etc.
action: Mapped[str] = mapped_column(String, nullable=False, index=True)
# request.client.host or X-Forwarded-For (behind Traefik)
ip_address: Mapped[str | None] = mapped_column(String, nullable=True)
# Optional structured payload (response body excerpt, error detail, …)
details: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
index=True,
)