from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, Text, JSON, UniqueConstraint, BigInteger 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(BigInteger, 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"" class ChatSettings(Base): """Настройки системы чата""" __tablename__ = "chat_settings" id = Column(Integer, primary_key=True) mode = Column(String(20), nullable=False, default='broadcast') # broadcast или forward forward_chat_id = Column(String(50), nullable=True) # ID группы/канала для пересылки global_ban = Column(Boolean, default=False) # Глобальный бан чата created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) updated_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) def __repr__(self): return f"" class BannedUser(Base): """Забаненные пользователи (не могут отправлять сообщения)""" __tablename__ = "banned_users" id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) telegram_id = Column(BigInteger, nullable=False, index=True) banned_by = Column(Integer, ForeignKey("users.id"), nullable=False) reason = Column(Text, nullable=True) banned_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)) is_active = Column(Boolean, default=True, index=True) # Активен ли бан # Связи user = relationship("User", foreign_keys=[user_id]) admin = relationship("User", foreign_keys=[banned_by]) def __repr__(self): return f"" class ChatMessage(Base): """История сообщений чата (для модерации)""" __tablename__ = "chat_messages" id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) telegram_message_id = Column(Integer, nullable=False) message_type = Column(String(20), nullable=False) # text, photo, video, document, animation, sticker, voice, etc. text = Column(Text, nullable=True) # Текст сообщения file_id = Column(String(255), nullable=True) # ID файла в Telegram forwarded_message_ids = Column(JSON, nullable=True) # Список telegram_message_id пересланных сообщений {"user_telegram_id": message_id} is_deleted = Column(Boolean, default=False, index=True) deleted_by = Column(Integer, ForeignKey("users.id"), nullable=True) deleted_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), index=True) # Связи sender = relationship("User", foreign_keys=[user_id]) moderator = relationship("User", foreign_keys=[deleted_by]) def __repr__(self): return f"" class P2PMessage(Base): """P2P сообщения между пользователями""" __tablename__ = "p2p_messages" id = Column(Integer, primary_key=True) sender_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) recipient_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) message_type = Column(String(20), nullable=False) # text, photo, video, etc. text = Column(Text, nullable=True) file_id = Column(String(255), nullable=True) sender_message_id = Column(Integer, nullable=False) # ID сообщения у отправителя recipient_message_id = Column(Integer, nullable=True) # ID сообщения у получателя is_read = Column(Boolean, default=False, index=True) read_at = Column(DateTime(timezone=True), nullable=True) reply_to_id = Column(Integer, ForeignKey("p2p_messages.id"), nullable=True) # Ответ на сообщение created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), index=True) # Связи sender = relationship("User", foreign_keys=[sender_id], backref="sent_p2p_messages") recipient = relationship("User", foreign_keys=[recipient_id], backref="received_p2p_messages") reply_to = relationship("P2PMessage", remote_side=[id], backref="replies") def __repr__(self): return f""