Files
card-game/backend/app/models.py

115 lines
5.1 KiB
Python

from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime, Enum, UniqueConstraint
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.database import Base
import uuid
def generate_uuid():
return str(uuid.uuid4())
class User(Base):
__tablename__ = "users"
id = Column(String, primary_key=True, default=generate_uuid)
email = Column(String, unique=True, nullable=False)
password_hash = Column(String, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
profile = relationship("UserProfile", back_populates="user", uselist=False)
wallet = relationship("Wallet", back_populates="user", uselist=False)
cards = relationship("UserCard", back_populates="user")
transactions = relationship("WalletTransaction", back_populates="user")
chest_opens = relationship("ChestOpen", back_populates="user")
class UserProfile(Base):
__tablename__ = "user_profiles"
user_id = Column(String, ForeignKey("users.id"), primary_key=True)
nickname = Column(String, unique=True, nullable=False)
is_collection_public = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="profile")
class Wallet(Base):
__tablename__ = "wallets"
user_id = Column(String, ForeignKey("users.id"), primary_key=True)
balance = Column(Integer, nullable=False, default=0)
user = relationship("User", back_populates="wallet")
class WalletTransaction(Base):
__tablename__ = "wallet_transactions"
id = Column(String, primary_key=True, default=generate_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False)
type = Column(String, nullable=False) # CREDIT / DEBIT
amount = Column(Integer, nullable=False)
reason = Column(String, nullable=False) # REGISTER_BONUS, CHEST_OPEN, etc.
reference_id = Column(String, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="transactions")
class Card(Base):
__tablename__ = "cards"
id = Column(String, primary_key=True, default=generate_uuid)
name = Column(String, nullable=False)
rarity = Column(String, nullable=False) # COMMON, RARE, LEGENDARY
created_at = Column(DateTime(timezone=True), server_default=func.now())
owners = relationship("UserCard", back_populates="card")
class Chest(Base):
__tablename__ = "chests"
id = Column(String, primary_key=True, default=generate_uuid)
name = Column(String, nullable=False)
cost_gold = Column(Integer, nullable=False)
cards_per_open = Column(Integer, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class UserCard(Base):
__tablename__ = "user_cards"
user_id = Column(String, ForeignKey("users.id"), primary_key=True)
card_id = Column(String, ForeignKey("cards.id"), primary_key=True)
obtained_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="cards")
card = relationship("Card", back_populates="owners")
class ChestOpen(Base):
__tablename__ = "chest_opens"
id = Column(String, primary_key=True, default=generate_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False)
chest_id = Column(String, ForeignKey("chests.id"), nullable=False)
spent_gold = Column(Integer, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
user = relationship("User", back_populates="chest_opens")
items = relationship("ChestOpenItem", back_populates="chest_open")
class ChestOpenItem(Base):
__tablename__ = "chest_open_items"
id = Column(String, primary_key=True, default=generate_uuid)
chest_open_id = Column(String, ForeignKey("chest_opens.id"), nullable=False)
card_id = Column(String, ForeignKey("cards.id"), nullable=True) # Nullable if pure conversion without card entity reference logic? Actually SOP says card NON possessed -> NEW. If possessed -> CONVERTED. Usually still references the card.
rarity = Column(String, nullable=False)
outcome_type = Column(String, nullable=False) # NEW / CONVERTED
converted_gold = Column(Integer, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
chest_open = relationship("ChestOpen", back_populates="items")
class IdempotencyKey(Base):
__tablename__ = "idempotency_keys"
key = Column(String, nullable=False)
user_id = Column(String, nullable=False)
endpoint = Column(String, nullable=False)
response_body = Column(String, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
__table_args__ = (
UniqueConstraint('key', 'user_id', 'endpoint', name='uix_idempotency_key_user_endpoint'),
{"extend_existing": True} # Just in case
)
# Note: Using composite primary key might be better but SOP says "UNIQUE (key, user_id, endpoint)".
# Let's add an ID or make the composite PK. SQLAlchemy needs a PK.
id = Column(Integer, primary_key=True, autoincrement=True)