"""Article schemas.""" from __future__ import annotations from datetime import datetime from pydantic import BaseModel, ConfigDict, Field class SourceBrief(BaseModel): model_config = ConfigDict(from_attributes=True) id: int name: str slug: str region: str | None = None class ArticleListItem(BaseModel): """列表项:首页展示标题/译标/正文摘要/分类/插图,详细阅读进详情页。""" model_config = ConfigDict(from_attributes=True) id: int source: SourceBrief title: str title_zh: str | None = None # 翻译后的正文(纯文本);列表里截断显示,详情页展示完整 body_zh_text: str | None = None summary_zh: str | None = None lang_src: str | None = None translation_status: str category: str | None = None published_at: datetime | None = None fetched_at: datetime image_url: str | None = None # === 列表预览钩子:点击进详情前的"诱导点" === commentary: str | None = None # LLM 点评(列表里截断显示) commentary_status: str | None = None # ok/failed/pending/n/a image_ai_url: str | None = None # AI 插图(列表里缩略图) is_starred: bool = False class ArticleDetail(BaseModel): model_config = ConfigDict(from_attributes=True) id: int source: SourceBrief url: str title: str body_html: str | None = None body_text: str title_zh: str | None = None body_zh_html: str | None = None body_zh_text: str | None = None body_zh_formatted: str | None = None # LLM 排版后 summary_zh: str | None = None lang_src: str | None = None author: str | None = None image_url: str | None = None image_ai_url: str | None = None # LLM 生成的插图 translation_status: str translation_engine: str | None = None translated_at: datetime | None = None # === LLM 增强状态 + 内容 === category: str | None = None format_status: str | None = None # pending/ok/failed/n/a classify_status: str | None = None image_ai_status: str | None = None commentary_status: str | None = None commentary: str | None = None entities: dict | None = None sentiment: float | None = None duplicate_of: int | None = None published_at: datetime | None = None fetched_at: datetime is_starred: bool = False class ArticleListResponse(BaseModel): items: list[ArticleListItem] # 页码分页 page: int = 1 page_size: int = 50 total: int total_pages: int class ArticleQuery(BaseModel): """用作 ?query= 解析参考(实际 FastAPI 直接用 Query)。""" since: datetime | None = None until: datetime | None = None source: str | None = None # 逗号分隔 slug category: str | None = None q: str | None = None lang: str = Field(default="both", pattern=r"^(src|zh|both)$") page: int = Field(default=1, ge=1) page_size: int = Field(default=50, ge=1, le=200) starred_only: bool = False