new web ract
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy import select, func, or_
|
||||
from sqlalchemy.orm import selectinload
|
||||
import redis.asyncio as aioredis
|
||||
|
||||
from bd.database import get_db, get_redis
|
||||
from bd.models import Article, Category, Tag, ArticleStatus
|
||||
from bd.models import Article, Category, Tag, ArticleStatus, article_tag
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -22,6 +23,7 @@ def _article_card(a: Article) -> dict:
|
||||
"slug": a.slug,
|
||||
"excerpt": a.excerpt,
|
||||
"cover_url": a.cover_url,
|
||||
"source_url": a.source_url,
|
||||
"published_at": a.published_at.isoformat() if a.published_at else None,
|
||||
"view_count": a.view_count,
|
||||
"category": _cat_dict(a.category) if a.category else None,
|
||||
@@ -42,10 +44,13 @@ async def list_news(
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
category: str | None = Query(None),
|
||||
tag: str | None = Query(None),
|
||||
date_from: str | None = Query(None),
|
||||
date_to: str | None = Query(None),
|
||||
q: str | None = Query(None, max_length=200),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
redis: aioredis.Redis = Depends(get_redis),
|
||||
):
|
||||
cache_key = f"news:list:{offset}:{limit}:{category or ''}:{tag or ''}"
|
||||
cache_key = f"news:list:{offset}:{limit}:{category or ''}:{tag or ''}:{date_from or ''}:{date_to or ''}:{q or ''}"
|
||||
cached = await redis.get(cache_key)
|
||||
if cached:
|
||||
return json.loads(cached)
|
||||
@@ -68,6 +73,29 @@ async def list_news(
|
||||
if t:
|
||||
base = base.where(Article.tags.any(Tag.id == t.id))
|
||||
|
||||
if date_from:
|
||||
try:
|
||||
base = base.where(Article.published_at >= datetime.fromisoformat(date_from))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if date_to:
|
||||
try:
|
||||
base = base.where(Article.published_at <= datetime.fromisoformat(date_to))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if q:
|
||||
pattern = f"%{q}%"
|
||||
base = base.where(
|
||||
or_(
|
||||
Article.title.ilike(pattern),
|
||||
Article.excerpt.ilike(pattern),
|
||||
Article.content.ilike(pattern),
|
||||
Article.tags.any(Tag.name.ilike(pattern)),
|
||||
)
|
||||
)
|
||||
|
||||
count_q = select(func.count()).select_from(base.subquery())
|
||||
total: int = (await db.execute(count_q)).scalar_one()
|
||||
|
||||
@@ -82,7 +110,7 @@ async def list_news(
|
||||
"has_more": (offset + limit) < total,
|
||||
"items": [_article_card(a) for a in articles],
|
||||
}
|
||||
await redis.setex(cache_key, 30, json.dumps(result, default=str))
|
||||
await redis.setex(cache_key, 30 if q else 120, json.dumps(result, default=str))
|
||||
return result
|
||||
|
||||
|
||||
@@ -96,7 +124,36 @@ async def list_categories(
|
||||
return json.loads(cached)
|
||||
cats = (await db.execute(select(Category).order_by(Category.name))).scalars().all()
|
||||
result = [_cat_dict(c) for c in cats]
|
||||
await redis.setex("news:categories", 60, json.dumps(result))
|
||||
await redis.setex("news:categories", 300, json.dumps(result))
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/tags")
|
||||
async def list_tags(
|
||||
limit: int = Query(30, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
redis: aioredis.Redis = Depends(get_redis),
|
||||
):
|
||||
cache_key = f"news:tags:{limit}"
|
||||
cached = await redis.get(cache_key)
|
||||
if cached:
|
||||
return json.loads(cached)
|
||||
|
||||
q = (
|
||||
select(Tag, func.count(article_tag.c.article_id).label("cnt"))
|
||||
.join(article_tag, Tag.id == article_tag.c.tag_id)
|
||||
.join(Article, article_tag.c.article_id == Article.id)
|
||||
.where(Article.status == ArticleStatus.published)
|
||||
.group_by(Tag.id)
|
||||
.order_by(func.count(article_tag.c.article_id).desc())
|
||||
.limit(limit)
|
||||
)
|
||||
rows = (await db.execute(q)).all()
|
||||
result = [
|
||||
{"id": str(r.Tag.id), "name": r.Tag.name, "slug": r.Tag.slug, "count": r.cnt}
|
||||
for r in rows
|
||||
]
|
||||
await redis.setex(cache_key, 300, json.dumps(result))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user