""" ORM-модели для PostgreSQL (SQLAlchemy 2.0). Таблицы: - bot_settings: настройки бота (ключ-значение) - bot_features: фичи бота (вкл/выкл) - bot_categories: категории базы знаний - bot_knowledge_base: карточки базы знаний - bot_users: пользователи бота (контакты, согласие) - bot_conversations: диалоги (state machine) - bot_messages: сообщения (incoming/outgoing) - bot_tickets: заявки - bot_ticket_messages: сообщения заявок - bot_processing_logs: логи обработки - bot_ticket_statuses: история смены статусов заявок - bot_unknown_questions: неизвестные вопросы (автогенерация KB) """ from __future__ import annotations import datetime from sqlalchemy import Column, Integer, BigInteger, String, Text, Boolean, DateTime, ForeignKey, ARRAY from sqlalchemy.orm import DeclarativeBase, relationship class Base(DeclarativeBase): pass class BotSetting(Base): __tablename__ = "bot_settings" id = Column(Integer, primary_key=True, autoincrement=True) key = Column(String(255), unique=True, nullable=False) value = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) class BotFeature(Base): __tablename__ = "bot_features" id = Column(Integer, primary_key=True, autoincrement=True) key = Column(String(255), unique=True, nullable=False) name = Column(String(255), nullable=False) description = Column(Text, nullable=True) is_enabled = Column(Boolean, default=False) created_at = Column(DateTime, default=datetime.datetime.utcnow) class BotCategory(Base): __tablename__ = "bot_categories" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(255), nullable=False) description = Column(Text, nullable=True) sort_order = Column(Integer, default=0) created_at = Column(DateTime, default=datetime.datetime.utcnow) class BotKnowledgeBase(Base): __tablename__ = "bot_knowledge_base" id = Column(Integer, primary_key=True, autoincrement=True) category_id = Column(Integer, ForeignKey("bot_categories.id"), nullable=True) question = Column(Text, nullable=False) answer = Column(Text, nullable=True) keywords = Column(ARRAY(String), nullable=True) is_active = Column(Boolean, default=True) source_url = Column(String(500), nullable=True) sort_order = Column(Integer, default=0) created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) category = relationship("BotCategory", lazy="joined") class BotUser(Base): """Пользователь бота: профиль, контакты, согласие на обработку ПД.""" __tablename__ = "bot_users" id = Column(BigInteger, primary_key=True) first_name = Column(String(255), nullable=True) last_name = Column(String(255), nullable=True) patronymic = Column(String(255), nullable=True) username = Column(String(255), nullable=True) phone = Column(String(50), nullable=True) email = Column(String(255), nullable=True) organization = Column(String(255), nullable=True) address = Column(Text, nullable=True) vcf_raw = Column(Text, nullable=True) contact_hash = Column(String(255), nullable=True) phone_verified = Column(Boolean, default=False) email_verified = Column(Boolean, default=False) consent_given = Column(Boolean, default=False) consent_date = Column(DateTime, nullable=True) consent_revoked_date = Column(DateTime, nullable=True) last_interaction = Column(DateTime, nullable=True) total_conversations = Column(Integer, default=0) total_tickets = Column(Integer, default=0) created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) class BotConversation(Base): __tablename__ = "bot_conversations" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(BigInteger, ForeignKey("bot_users.id"), nullable=False) state = Column(String(50), default="greeting") intent = Column(String(50), nullable=True) inquiry_text = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) user = relationship("BotUser", lazy="joined") class BotMessage(Base): __tablename__ = "bot_messages" id = Column(BigInteger, primary_key=True, autoincrement=True) conversation_id = Column(BigInteger, ForeignKey("bot_conversations.id", ondelete="CASCADE"), nullable=False) direction = Column(String(10), nullable=False) text = Column(Text, nullable=True) attachment_json = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) conversation = relationship("BotConversation", lazy="joined") class BotTicket(Base): __tablename__ = "bot_tickets" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(BigInteger, ForeignKey("bot_users.id"), nullable=False) title = Column(String(500), nullable=True) description = Column(Text, nullable=True) priority = Column(String(20), default="normal") status = Column(String(20), default="Новая") created_by = Column(String(100), default="bot") assigned_to = Column(BigInteger, nullable=True) contact_name = Column(String(500), nullable=True) contact_phone = Column(String(50), nullable=True) contact_email = Column(String(255), nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) user = relationship("BotUser", lazy="joined") class BotTicketMessage(Base): __tablename__ = "bot_ticket_messages" id = Column(BigInteger, primary_key=True, autoincrement=True) ticket_id = Column(BigInteger, ForeignKey("bot_tickets.id"), nullable=False) direction = Column(String(10), nullable=False) text = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) ticket = relationship("BotTicket", lazy="joined") class BotProcessingLog(Base): __tablename__ = "bot_processing_logs" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(BigInteger, ForeignKey("bot_users.id"), nullable=False) conversation_id = Column(BigInteger, ForeignKey("bot_conversations.id", ondelete="CASCADE"), nullable=True) step = Column(String(50), nullable=False) status = Column(String(20), default="ok") message = Column(Text, nullable=True) detail = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) user = relationship("BotUser", lazy="joined") class BotTicketStatus(Base): __tablename__ = "bot_ticket_statuses" id = Column(BigInteger, primary_key=True, autoincrement=True) ticket_id = Column(BigInteger, ForeignKey("bot_tickets.id"), nullable=False) old_status = Column(String(20), nullable=True) new_status = Column(String(20), nullable=False) changed_by = Column(String(100), nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) ticket = relationship("BotTicket", lazy="joined") class BotUnknownQuestion(Base): __tablename__ = "bot_unknown_questions" id = Column(Integer, primary_key=True, autoincrement=True) question_text = Column(Text, nullable=False) normalized_text = Column(Text, nullable=True) ask_count = Column(Integer, default=1) user_ids = Column(ARRAY(BigInteger), default=[]) status = Column(String(50), default="new") generated_answer = Column(Text, nullable=True) generated_keywords = Column(ARRAY(String), nullable=True) generated_category_id = Column(Integer, nullable=True) created_at = Column(DateTime, default=datetime.datetime.utcnow) reviewed_at = Column(DateTime, nullable=True)