from datetime import datetime from typing import TYPE_CHECKING, List, Optional from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, String, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database.session import Base if TYPE_CHECKING: from app.user.models import User from app.video.models import Video class Comment(Base): """ 영상 댓글 테이블 2-depth 구조 (최상위 댓글 + 대댓글 1단계). parent_id가 NULL이면 최상위 댓글, 값이 있으면 대댓글. 작성자(user_uuid)는 DB에 저장하지만 API 응답에는 미노출 (익명 정책). """ __tablename__ = "comment" __table_args__ = ( Index("idx_comment_video_id", "video_id"), Index("idx_comment_user_uuid", "user_uuid"), Index("idx_comment_parent_id", "parent_id"), Index("idx_comment_is_deleted", "is_deleted"), { "mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci", }, ) id: Mapped[int] = mapped_column( Integer, primary_key=True, autoincrement=True, comment="고유 식별자" ) video_id: Mapped[int] = mapped_column( Integer, ForeignKey("video.id", ondelete="CASCADE"), nullable=False, comment="연결된 Video의 id", ) user_uuid: Mapped[str] = mapped_column( ForeignKey("user.user_uuid", ondelete="CASCADE"), nullable=False, comment="작성자 UUID (응답 미노출, 권한 검증용)", ) parent_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("comment.id", ondelete="CASCADE"), nullable=True, comment="NULL=최상위 댓글, 값=대댓글의 부모 id", ) nickname: Mapped[Optional[str]] = mapped_column( String(50), nullable=True, comment="댓글 작성자 닉네임 (null이면 익명)" ) content: Mapped[str] = mapped_column( String(100), nullable=False, comment="댓글 본문 (한글 기준 100자 이내)" ) is_deleted: Mapped[bool] = mapped_column( Boolean, nullable=False, default=False, comment="소프트 삭제 여부" ) created_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, server_default=func.now(), comment="작성 일시", ) video: Mapped["Video"] = relationship("Video", back_populates="comments") user: Mapped["User"] = relationship("User", back_populates="comments") parent: Mapped[Optional["Comment"]] = relationship( "Comment", remote_side=[id], back_populates="replies" ) replies: Mapped[List["Comment"]] = relationship( "Comment", back_populates="parent", cascade="all, delete-orphan", )