o2o-castad-backend/app/social/models.py

257 lines
8.6 KiB
Python

"""
Social Media Models
소셜 미디어 업로드 관련 SQLAlchemy 모델을 정의합니다.
"""
from datetime import datetime
from typing import TYPE_CHECKING, Optional
from sqlalchemy import BigInteger, DateTime, ForeignKey, Index, Integer, String, Text, func
from sqlalchemy.dialects.mysql import JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database.session import Base
if TYPE_CHECKING:
from app.user.models import SocialAccount
from app.video.models import Video
class SocialUpload(Base):
"""
소셜 미디어 업로드 기록 테이블
영상의 소셜 미디어 플랫폼별 업로드 상태를 추적합니다.
Attributes:
id: 고유 식별자 (자동 증가)
user_uuid: 사용자 UUID (User.user_uuid 참조)
video_id: Video 외래키
social_account_id: SocialAccount 외래키
upload_seq: 업로드 순번 (동일 영상+채널 조합 내 순번, 관리자 추적용)
platform: 플랫폼 구분 (youtube, instagram, facebook, tiktok)
status: 업로드 상태 (pending, uploading, processing, completed, failed)
upload_progress: 업로드 진행률 (0-100)
platform_video_id: 플랫폼에서 부여한 영상 ID
platform_url: 플랫폼에서의 영상 URL
title: 영상 제목
description: 영상 설명
tags: 태그 목록 (JSON)
privacy_status: 공개 상태 (public, unlisted, private)
platform_options: 플랫폼별 추가 옵션 (JSON)
error_message: 에러 메시지 (실패 시)
retry_count: 재시도 횟수
uploaded_at: 업로드 완료 시간
created_at: 생성 일시
updated_at: 수정 일시
Relationships:
video: 연결된 Video
social_account: 연결된 SocialAccount
"""
__tablename__ = "social_upload"
__table_args__ = (
Index("idx_social_upload_user_uuid", "user_uuid"),
Index("idx_social_upload_video_id", "video_id"),
Index("idx_social_upload_social_account_id", "social_account_id"),
Index("idx_social_upload_platform", "platform"),
Index("idx_social_upload_status", "status"),
Index("idx_social_upload_created_at", "created_at"),
# 동일 영상+채널 조합 조회용 인덱스 (유니크 아님 - 여러 번 업로드 가능)
Index("idx_social_upload_video_account", "video_id", "social_account_id"),
# 순번 조회용 인덱스
Index("idx_social_upload_seq", "video_id", "social_account_id", "upload_seq"),
{
"mysql_engine": "InnoDB",
"mysql_charset": "utf8mb4",
"mysql_collate": "utf8mb4_unicode_ci",
},
)
# ==========================================================================
# 기본 식별자
# ==========================================================================
id: Mapped[int] = mapped_column(
BigInteger,
primary_key=True,
nullable=False,
autoincrement=True,
comment="고유 식별자",
)
# ==========================================================================
# 관계 필드
# ==========================================================================
user_uuid: Mapped[str] = mapped_column(
String(36),
ForeignKey("user.user_uuid", ondelete="CASCADE"),
nullable=False,
comment="사용자 UUID (User.user_uuid 참조)",
)
video_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("video.id", ondelete="CASCADE"),
nullable=False,
comment="Video 외래키",
)
social_account_id: Mapped[int] = mapped_column(
Integer,
ForeignKey("social_account.id", ondelete="CASCADE"),
nullable=False,
comment="SocialAccount 외래키",
)
# ==========================================================================
# 업로드 순번 (관리자 추적용)
# ==========================================================================
upload_seq: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=1,
comment="업로드 순번 (동일 영상+채널 조합 내 순번, 1부터 시작)",
)
# ==========================================================================
# 플랫폼 정보
# ==========================================================================
platform: Mapped[str] = mapped_column(
String(20),
nullable=False,
comment="플랫폼 구분 (youtube, instagram, facebook, tiktok)",
)
# ==========================================================================
# 업로드 상태
# ==========================================================================
status: Mapped[str] = mapped_column(
String(20),
nullable=False,
default="pending",
comment="업로드 상태 (pending, uploading, processing, completed, failed)",
)
upload_progress: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=0,
comment="업로드 진행률 (0-100)",
)
# ==========================================================================
# 플랫폼 결과
# ==========================================================================
platform_video_id: Mapped[Optional[str]] = mapped_column(
String(100),
nullable=True,
comment="플랫폼에서 부여한 영상 ID",
)
platform_url: Mapped[Optional[str]] = mapped_column(
String(500),
nullable=True,
comment="플랫폼에서의 영상 URL",
)
# ==========================================================================
# 메타데이터
# ==========================================================================
title: Mapped[str] = mapped_column(
String(200),
nullable=False,
comment="영상 제목",
)
description: Mapped[Optional[str]] = mapped_column(
Text,
nullable=True,
comment="영상 설명",
)
tags: Mapped[Optional[dict]] = mapped_column(
JSON,
nullable=True,
comment="태그 목록 (JSON 배열)",
)
privacy_status: Mapped[str] = mapped_column(
String(20),
nullable=False,
default="private",
comment="공개 상태 (public, unlisted, private)",
)
platform_options: Mapped[Optional[dict]] = mapped_column(
JSON,
nullable=True,
comment="플랫폼별 추가 옵션 (JSON)",
)
# ==========================================================================
# 에러 정보
# ==========================================================================
error_message: Mapped[Optional[str]] = mapped_column(
Text,
nullable=True,
comment="에러 메시지 (실패 시)",
)
retry_count: Mapped[int] = mapped_column(
Integer,
nullable=False,
default=0,
comment="재시도 횟수",
)
# ==========================================================================
# 시간 정보
# ==========================================================================
uploaded_at: Mapped[Optional[datetime]] = mapped_column(
DateTime,
nullable=True,
comment="업로드 완료 시간",
)
created_at: Mapped[datetime] = mapped_column(
DateTime,
nullable=False,
server_default=func.now(),
comment="생성 일시",
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="수정 일시",
)
# ==========================================================================
# Relationships
# ==========================================================================
video: Mapped["Video"] = relationship(
"Video",
lazy="selectin",
)
social_account: Mapped["SocialAccount"] = relationship(
"SocialAccount",
lazy="selectin",
)
def __repr__(self) -> str:
return (
f"<SocialUpload("
f"id={self.id}, "
f"video_id={self.video_id}, "
f"account_id={self.social_account_id}, "
f"seq={self.upload_seq}, "
f"platform='{self.platform}', "
f"status='{self.status}'"
f")>"
)