"""PDF 转换任务实体。 一个任务记录一次「上传文件 -> 转为 PDF」的转换。原始上传文件与转换产物 PDF 均复用 UploadedFile 存储(落盘 + 元数据入库),本表只记录两者关系与转换状态, 不重复实现存储逻辑。 删除语义(两级): 用户软删(user_deleted=true)-- 用户页不再展示,但磁盘与 DB 行保留; 管理页仍可见且标注「已删除」。 管理员硬删 -- 删原始/产物磁盘文件 + UploadedFile 行 + 本表行,真正删除。 """ from __future__ import annotations from datetime import datetime from sqlalchemy import BigInteger, Boolean, DateTime, Integer, String, func from sqlalchemy.orm import Mapped, mapped_column from ..database import Base class PdfJob(Base): __tablename__ = "pdf_job" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) # 用户标识:httpOnly cookie 值(uuid4.hex),用户凭此查看自己的任务 owner_cookie: Mapped[str] = mapped_column(String(64), nullable=False, index=True) # 原始上传文件(复用 UploadedFile 存储) source_file_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True) source_filename: Mapped[str] = mapped_column(String(512), nullable=False) source_size: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0) # 转换产物 PDF(复用 UploadedFile 存储);转换完成前为 NULL output_file_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, default=None) # pending(排队) / converting(转换中) / done(完成) / failed(失败) status: Mapped[str] = mapped_column(String(16), nullable=False, default="pending", index=True) # 转换进度 0-100,供前端轮询显示 progress: Mapped[int] = mapped_column(Integer, nullable=False, default=0) # 失败原因(status=failed 时填写) error_message: Mapped[str] = mapped_column(String(512), nullable=False, default="") # 用户软删标记:true=用户已从其页面删除,不再对用户展示 user_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True) # 用户软删时间(管理页展示「是否已删除」时用) deleted_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None) created_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False ) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now(), nullable=False ) def __repr__(self) -> str: # pragma: no cover return ( f"" )