"""截图主表与处理状态。""" from __future__ import annotations from datetime import datetime from enum import Enum from sqlalchemy import ( BigInteger, DateTime, ForeignKey, Index, Integer, String, UniqueConstraint, func, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.db import Base class ProcessStatus(str, Enum): """处理流水线的状态枚举。""" PENDING = "pending" RUNNING = "running" DONE = "done" FAILED = "failed" SKIPPED = "skipped" class Screenshot(Base): """截图文件主记录。""" __tablename__ = "screenshots" __table_args__ = ( UniqueConstraint("file_hash", name="uq_screenshots_file_hash"), Index("ix_screenshots_captured_at", "captured_at"), Index("ix_screenshots_ai_status", "ai_status"), Index("ix_screenshots_category_id", "category_id"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) path: Mapped[str] = mapped_column(String(1024), nullable=False) file_hash: Mapped[str] = mapped_column(String(64), nullable=False) width: Mapped[int] = mapped_column(Integer, default=0) height: Mapped[int] = mapped_column(Integer, default=0) size: Mapped[int] = mapped_column(BigInteger, default=0) captured_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) imported_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False ) thumb_path: Mapped[str | None] = mapped_column(String(1024), nullable=True) ocr_status: Mapped[str] = mapped_column(String(16), default=ProcessStatus.PENDING.value) ai_status: Mapped[str] = mapped_column(String(16), default=ProcessStatus.PENDING.value) # AI 写回的分类:外键 + SET NULL,删除分类时自动把引用置空 category_id: Mapped[int | None] = mapped_column( Integer, ForeignKey("categories.id", ondelete="SET NULL"), nullable=True, ) is_favorite: Mapped[int] = mapped_column(Integer, default=0) # 0/1,便于 SQLite 索引 is_hidden: Mapped[int] = mapped_column(Integer, default=0) meta = relationship( "ScreenshotMeta", back_populates="screenshot", uselist=False, cascade="all, delete-orphan", ) tags = relationship( "Tag", secondary="screenshot_tags", back_populates="screenshots", lazy="selectin", ) todos = relationship( "Todo", back_populates="screenshot", cascade="all, delete-orphan", )