"""Document 文档模型。""" from sqlalchemy import ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin class Document(UUIDPrimaryKeyMixin, TimestampMixin, Base): """文档模型。 技术审查 §2.2:documents 表。 """ __tablename__ = "documents" knowledge_base_id: Mapped[str] = mapped_column( String(32), ForeignKey("knowledge_bases.id"), nullable=False, index=True, comment="所属知识库 ID", ) user_id: Mapped[str] = mapped_column( String(32), ForeignKey("users.id"), nullable=False, index=True, comment="所有者用户 ID (冗余,便于隔离校验)", ) category_id: Mapped[str | None] = mapped_column( String(32), ForeignKey("document_categories.id"), nullable=True, comment="分类 ID", ) original_filename: Mapped[str] = mapped_column( String(512), nullable=False, comment="原始文件名或文本标题 (仅展示)", ) storage_path: Mapped[str | None] = mapped_column( String(1024), nullable=True, comment="相对于 data/ 的物理存储路径(文本内容文档为 NULL)", ) content: Mapped[str | None] = mapped_column( Text, nullable=True, comment="直接输入的文本内容(非文件上传时使用)", ) content_format: Mapped[str] = mapped_column( String(16), default="markdown", nullable=False, comment="内容格式:markdown / text", ) markdown_path: Mapped[str | None] = mapped_column( String(1024), nullable=True, comment="相对于 data/ 的 Markdown 文件路径", ) file_size: Mapped[int] = mapped_column( Integer, default=0, nullable=False, comment="文件大小 (字节),文本内容文档为内容长度", ) mime_type: Mapped[str] = mapped_column( String(127), default="text/markdown", nullable=False, comment="MIME 类型", ) file_ext: Mapped[str] = mapped_column( String(16), default=".md", nullable=False, comment="文件扩展名 (.docx/.pdf/.md)", ) sha256: Mapped[str | None] = mapped_column( String(64), nullable=True, comment="文件 SHA-256 哈希(文本内容文档为 NULL)", ) doc_token_hash: Mapped[str] = mapped_column( String(64), unique=True, nullable=True, index=True, comment="SHA-256(document_token) 十六进制", ) doc_token_encrypted: Mapped[str | None] = mapped_column( Text, nullable=True, comment="Fernet 加密的 document_token 原文", ) doc_token_hint: Mapped[str | None] = mapped_column( String(16), nullable=True, comment="document_token 末 8 位明文", ) title: Mapped[str | None] = mapped_column( String(512), nullable=True, comment="文档标题 (用户可改)", ) description: Mapped[str | None] = mapped_column( Text, nullable=True, comment="文档描述 (用户可改)", ) keywords: Mapped[str | None] = mapped_column( Text, nullable=True, comment="关键词 (逗号分隔)", ) content_summary: Mapped[str | None] = mapped_column( Text, nullable=True, comment="抽取式摘要 (~200字)", ) status: Mapped[str] = mapped_column( String(16), default="PENDING", nullable=False, comment="状态 (PENDING/PROCESSING/READY/FAILED/DELETED)", ) error_code: Mapped[str | None] = mapped_column( String(64), nullable=True, comment="错误码 (如 SCANNED_PDF_NO_TEXT_LAYER)", ) # 关系 knowledge_base = relationship("KnowledgeBase", back_populates="documents", lazy="selectin") user = relationship("User", back_populates="documents", lazy="selectin") category = relationship("DocumentCategory", back_populates="documents", lazy="selectin") access_logs = relationship("AccessLog", back_populates="document", lazy="selectin") def __repr__(self) -> str: return f""