83 lines
2.8 KiB
Python
83 lines
2.8 KiB
Python
# 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
|