"""Модель сообщения чата — текстовый чат в конференции (WS). Автор — зарегистрированный пользователь ИЛИ гость (ровно один из `user_id`/ `guest_access_id` заполнен, аналогично `ConferenceParticipant` из ADR-001, п.6). `author_name` — снапшот отображаемого имени из LiveKit access-токена на момент отправки сообщения: не связан жёстко с текущим именем пользователя/гостя, переживает их последующее переименование или удаление гостевой записи (`ON DELETE CASCADE` у `guest_access_id` относится к самому сообщению, а не к отображаемому имени в истории чата). """ import uuid from datetime import datetime from sqlalchemy import ( BigInteger, CheckConstraint, DateTime, ForeignKey, Identity, Index, String, Text, func, ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column from models.base import Base class ChatMessage(Base): """Одно сообщение чата, опубликованное в конференции.""" __tablename__ = "chat_messages" __table_args__ = ( Index("ix_chat_messages_session_id_created_at", "session_id", "created_at"), CheckConstraint( "user_id IS NOT NULL OR guest_access_id IS NOT NULL", name="ck_chat_messages_author", ), ) id: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True) session_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("conference_sessions.id", ondelete="CASCADE"), nullable=False, ) user_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("users.id"), nullable=True ) guest_access_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("guest_access.id", ondelete="CASCADE"), nullable=True ) author_name: Mapped[str] = mapped_column(String(255), nullable=False) text: Mapped[str] = mapped_column(Text, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() )