# app/db/repositories/home_featured.py from typing import List, Sequence from asyncpg import Connection from app.db.repositories.base import BaseRepository class HomeFeaturedRepository(BaseRepository): """ 维护首页推送文章的排序列表(最多 10 条)。 仅存 slug + sort_order,真正返回文章数据时再通过 ArticlesRepository 拉取。 """ def __init__(self, conn: Connection) -> None: super().__init__(conn) async def _ensure_table(self) -> None: await self.connection.execute( """ CREATE TABLE IF NOT EXISTS home_featured_articles ( slug TEXT PRIMARY KEY, sort_order INT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); """, ) async def list_slugs(self, *, limit: int = 10) -> List[str]: await self._ensure_table() rows = await self.connection.fetch( """ SELECT slug FROM home_featured_articles ORDER BY sort_order ASC, updated_at DESC, created_at DESC LIMIT $1; """, limit, ) return [row["slug"] for row in rows] async def save_slugs(self, *, slugs: Sequence[str], limit: int = 10) -> List[str]: """ 保存首页推送顺序,自动去重并截断到 limit。 返回最终生效的 slug 顺序。 """ await self._ensure_table() clean_slugs: List[str] = [] for slug in slugs: normalized = str(slug).strip() if not normalized: continue if normalized not in clean_slugs: clean_slugs.append(normalized) clean_slugs = clean_slugs[:limit] async with self.connection.transaction(): if clean_slugs: await self.connection.execute( """ DELETE FROM home_featured_articles WHERE slug <> ALL($1::text[]); """, clean_slugs, ) for idx, slug in enumerate(clean_slugs): await self.connection.execute( """ INSERT INTO home_featured_articles (slug, sort_order) VALUES ($1, $2) ON CONFLICT (slug) DO UPDATE SET sort_order = EXCLUDED.sort_order, updated_at = NOW(); """, slug, idx, ) else: await self.connection.execute("DELETE FROM home_featured_articles;") return clean_slugs