from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Text, JSON, UniqueConstraint from sqlalchemy.orm import relationship from datetime import datetime, timezone from .database import Base import secrets class User(Base): """Модель пользователя с регистрацией""" __tablename__ = "users" id = Column(Integer, primary_key=True) telegram_id = Column(Integer, unique=True, nullable=False, index=True) username = Column(String(255)) first_name = Column(String(255)) last_name = Column(String(255)) phone = Column(String(20), nullable=True) # Телефон для верификации club_card_number = Column(String(50), unique=True, nullable=True, index=True) # Номер клубной карты is_registered = Column(Boolean, default=False) # Прошел ли полную регистрацию is_admin = Column(Boolean, default=False) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) # Секретный код для верификации выигрыша (генерируется при регистрации) verification_code = Column(String(10), unique=True, nullable=True) # Связи accounts = relationship("Account", back_populates="owner", cascade="all, delete-orphan") participations = relationship("Participation", back_populates="user") winners = relationship("Winner", back_populates="user") def __repr__(self): return f"" def generate_verification_code(self): """Генерирует уникальный код верификации""" self.verification_code = secrets.token_hex(4).upper() # 8-символьный код class Account(Base): """Модель счета клиента (может быть несколько у одного пользователя)""" __tablename__ = "accounts" id = Column(Integer, primary_key=True) account_number = Column(String(20), unique=True, nullable=False, index=True) # XX-XX-XX-XX-XX-XX-XX owner_id = Column(Integer, ForeignKey("users.id"), nullable=False) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) is_active = Column(Boolean, default=True) # Активен ли счет # Связи owner = relationship("User", back_populates="accounts") participations = relationship("Participation", back_populates="account") def __repr__(self): return f"" class WinnerVerification(Base): """Модель верификации победителя""" __tablename__ = "winner_verifications" id = Column(Integer, primary_key=True) winner_id = Column(Integer, ForeignKey("winners.id"), nullable=False, unique=True) verification_token = Column(String(32), unique=True, nullable=False) # Токен для подтверждения is_verified = Column(Boolean, default=False) verified_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) expires_at = Column(DateTime(timezone=True), nullable=False) # Срок действия токена # Связи winner = relationship("Winner", back_populates="verification") def __repr__(self): return f"" @staticmethod def generate_token(): """Генерирует уникальный токен верификации""" return secrets.token_urlsafe(24) class Lottery(Base): """Модель розыгрыша""" __tablename__ = "lotteries" id = Column(Integer, primary_key=True) title = Column(String(500), nullable=False) description = Column(Text) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) start_date = Column(DateTime(timezone=True)) end_date = Column(DateTime(timezone=True)) is_active = Column(Boolean, default=True) is_completed = Column(Boolean, default=False) prizes = Column(JSON) # Список призов в формате JSON creator_id = Column(Integer, ForeignKey("users.id"), nullable=False) # Настройки для ручного управления победителями manual_winners = Column(JSON, default=lambda: {}) # {место: telegram_id} draw_results = Column(JSON) # Результаты розыгрыша # Тип отображения победителей: "username", "chat_id", "account_number" winner_display_type = Column(String(20), default="username") # Связи creator = relationship("User") participations = relationship("Participation", back_populates="lottery") def __repr__(self): return f"" class Participation(Base): """Модель участия в розыгрыше""" __tablename__ = "participations" id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=True) lottery_id = Column(Integer, ForeignKey("lotteries.id"), nullable=False) account_id = Column(Integer, ForeignKey("accounts.id"), nullable=True) # Привязка к счету account_number = Column(String(20), nullable=True, index=True) # Дублируем для быстрого доступа created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) # Связи user = relationship("User", back_populates="participations") lottery = relationship("Lottery", back_populates="participations") account = relationship("Account", back_populates="participations") def __repr__(self): if self.account_number: return f"" return f"" class Winner(Base): """Модель победителя розыгрыша""" __tablename__ = "winners" id = Column(Integer, primary_key=True) lottery_id = Column(Integer, ForeignKey("lotteries.id"), nullable=False) user_id = Column(Integer, ForeignKey("users.id"), nullable=True) account_number = Column(String(20), nullable=True, index=True) place = Column(Integer, nullable=False) prize = Column(String(500)) is_manual = Column(Boolean, default=False) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) # Статус подтверждения выигрыша is_notified = Column(Boolean, default=False) # Отправлено ли уведомление is_claimed = Column(Boolean, default=False) # Подтвердил ли победитель claimed_at = Column(DateTime(timezone=True), nullable=True) # Время подтверждения # Связи user = relationship("User", back_populates="winners") lottery = relationship("Lottery") verification = relationship("WinnerVerification", back_populates="winner", uselist=False) def __repr__(self): if self.account_number: return f"" return f""