"""人物资料导入模型(api-spec §8;契约字典 §10)。 导入流水线把用户授权的聊天记录、人物设定、背景故事、角色卡、语气样例、 语音样本等资料解析为可追溯的 ``import_items``,再由后续阶段抽取为 档案 / 记忆 / 人格候选。``import_items.id`` 作为候选的 evidence 来源 (source_type = ``import_item``)。 """ from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.core.ids import new_import_item_id, new_import_job_id from app.core.time import utc_now from app.db.base import Base class CharacterImportJob(Base): """导入任务(契约字典 §10.3 状态机;api-spec §8)。""" __tablename__ = "character_import_jobs" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_import_job_id) character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True) material_type: Mapped[str] = mapped_column(String(40), nullable=False, index=True) source_type: Mapped[str] = mapped_column(String(40), nullable=False) title: Mapped[str | None] = mapped_column(String(200)) status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True) format: Mapped[str] = mapped_column(String(40), nullable=False, default="text") media_type: Mapped[str | None] = mapped_column(String(40)) mime_type: Mapped[str | None] = mapped_column(String(80)) source_description: Mapped[str | None] = mapped_column(Text) speaker_label: Mapped[str | None] = mapped_column(String(120)) consent_status: Mapped[str] = mapped_column(String(40), nullable=False, default="unknown") usage_scope: Mapped[str] = mapped_column(String(40), nullable=False, default="reference_only") checksum: Mapped[str | None] = mapped_column(String(120), index=True) item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) storage_path: Mapped[str | None] = mapped_column(String(500)) raw_content: Mapped[str | None] = mapped_column(Text) error_code: Mapped[str | None] = mapped_column(String(80)) error_message: Mapped[str | None] = mapped_column(Text) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=utc_now, onupdate=utc_now ) class ImportItem(Base): """导入解析条目(契约字典 §10.4 item_type / §10.5 role)。""" __tablename__ = "import_items" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_import_item_id) import_job_id: Mapped[str] = mapped_column( String(80), ForeignKey("character_import_jobs.id"), index=True ) character_id: Mapped[str] = mapped_column(String(80), ForeignKey("characters.id"), index=True) item_type: Mapped[str] = mapped_column(String(40), nullable=False) role: Mapped[str] = mapped_column(String(40), nullable=False, default="other") sequence: Mapped[int] = mapped_column(Integer, nullable=False, default=0) content: Mapped[str] = mapped_column(Text, nullable=False) metadata_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)