"""Database models""" from datetime import datetime from sqlalchemy import ( Column, Integer, String, DateTime, Boolean, Float, ForeignKey, UniqueConstraint, Index, Enum as SQLEnum ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship import uuid import enum from app.db.base import Base from app.models.card import CardRarity class User(Base): """ユーザーモデル""" __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) did = Column(String, unique=True, nullable=False, index=True) handle = Column(String, nullable=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships cards = relationship("UserCard", back_populates="owner") draws = relationship("DrawHistory", back_populates="user") class CardMaster(Base): """カードマスタデータ""" __tablename__ = "card_master" id = Column(Integer, primary_key=True) # 0-15 name = Column(String, nullable=False) base_cp_min = Column(Integer, nullable=False) base_cp_max = Column(Integer, nullable=False) color = Column(String, nullable=False) description = Column(String) # Relationships user_cards = relationship("UserCard", back_populates="card_info") class UserCard(Base): """ユーザー所有カード""" __tablename__ = "user_cards" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) card_id = Column(Integer, ForeignKey("card_master.id"), nullable=False) cp = Column(Integer, nullable=False) status = Column(SQLEnum(CardRarity), nullable=False) skill = Column(String, nullable=True) obtained_at = Column(DateTime, default=datetime.utcnow) is_unique = Column(Boolean, default=False) unique_id = Column(UUID(as_uuid=True), nullable=True, unique=True) # Relationships owner = relationship("User", back_populates="cards") card_info = relationship("CardMaster", back_populates="user_cards") # Indexes __table_args__ = ( Index('idx_user_cards', 'user_id', 'card_id'), ) class UniqueCardRegistry(Base): """uniqueカードのグローバルレジストリ""" __tablename__ = "unique_card_registry" id = Column(Integer, primary_key=True) unique_id = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False) card_id = Column(Integer, ForeignKey("card_master.id"), nullable=False) owner_did = Column(String, ForeignKey("users.did"), nullable=False) obtained_at = Column(DateTime, default=datetime.utcnow) verse_skill_id = Column(String, nullable=True) # ai.verse連携用 # Unique constraint: 各card_idは1人のみ所有可能 __table_args__ = ( UniqueConstraint('card_id', name='unique_card_per_type'), ) class DrawHistory(Base): """ガチャ履歴""" __tablename__ = "draw_history" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) card_id = Column(Integer, nullable=False) status = Column(SQLEnum(CardRarity), nullable=False) cp = Column(Integer, nullable=False) is_paid = Column(Boolean, default=False) drawn_at = Column(DateTime, default=datetime.utcnow) # Relationships user = relationship("User", back_populates="draws") # Indexes __table_args__ = ( Index('idx_draw_history_user', 'user_id', 'drawn_at'), ) class GachaPool(Base): """ガチャプール(ピックアップ管理)""" __tablename__ = "gacha_pools" id = Column(Integer, primary_key=True) name = Column(String, nullable=False) description = Column(String) is_active = Column(Boolean, default=True) start_at = Column(DateTime, nullable=False) end_at = Column(DateTime, nullable=True) pickup_card_ids = Column(String) # JSON array of card IDs rate_up_multiplier = Column(Float, default=1.0) created_at = Column(DateTime, default=datetime.utcnow)