Files
aihot/backend/app/api/news.py
2026-05-24 01:16:07 +08:00

97 lines
3.4 KiB
Python

from datetime import date, datetime
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select, func, distinct
from sqlalchemy.ext.asyncio import AsyncSession
from ..database import get_db
from ..models.news import ProcessedNews, RawNews
router = APIRouter()
def _serialize(n: ProcessedNews) -> dict:
raw = n.raw_news
return {
"id": n.id,
"title_zh": n.title_zh,
"summary": n.summary,
"opinion": n.opinion,
"keywords": n.keywords or [],
"importance_score": n.importance_score,
"importance_reason": n.importance_reason,
"category": n.category,
"is_featured": n.is_featured,
"featured_rank": n.featured_rank,
"source_name": n.source_name or (raw.source.name if raw and raw.source else ""),
"source_url": n.source_url or (raw.url if raw else ""),
"published_at": n.published_at.isoformat() if n.published_at else None,
"processed_at": n.processed_at.isoformat() if n.processed_at else None,
}
@router.get("/featured")
async def get_featured(
news_date: Optional[str] = Query(default=None, alias="date"),
db: AsyncSession = Depends(get_db),
):
target = date.fromisoformat(news_date) if news_date else date.today()
stmt = (
select(ProcessedNews)
.join(ProcessedNews.raw_news)
.where(ProcessedNews.is_featured == True)
.where(func.date(ProcessedNews.processed_at) == target)
.order_by(ProcessedNews.featured_rank)
)
result = await db.execute(stmt)
items = result.scalars().all()
return {"date": str(target), "items": [_serialize(n) for n in items]}
@router.get("")
async def get_news(
news_date: Optional[str] = Query(default=None, alias="date"),
category: Optional[str] = Query(default=None),
page: int = Query(default=1, ge=1),
page_size: int = Query(default=20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
):
target = date.fromisoformat(news_date) if news_date else date.today()
stmt = (
select(ProcessedNews)
.join(ProcessedNews.raw_news)
.where(func.date(ProcessedNews.processed_at) == target)
)
if category:
stmt = stmt.where(ProcessedNews.category == category)
count_stmt = select(func.count()).select_from(stmt.subquery())
total = (await db.execute(count_stmt)).scalar_one()
stmt = stmt.order_by(ProcessedNews.importance_score.desc()).offset((page - 1) * page_size).limit(page_size)
result = await db.execute(stmt)
items = result.scalars().all()
return {"date": str(target), "total": total, "page": page, "items": [_serialize(n) for n in items]}
@router.get("/dates")
async def get_dates(db: AsyncSession = Depends(get_db)):
stmt = select(
func.date(ProcessedNews.processed_at).label("d"),
func.count(ProcessedNews.id).label("cnt"),
).group_by("d").order_by(func.date(ProcessedNews.processed_at).desc()).limit(30)
result = await db.execute(stmt)
return [{"date": str(row.d), "count": row.cnt} for row in result]
@router.get("/{news_id}")
async def get_news_detail(news_id: int, db: AsyncSession = Depends(get_db)):
stmt = select(ProcessedNews).join(ProcessedNews.raw_news).where(ProcessedNews.id == news_id)
result = await db.execute(stmt)
news = result.scalar_one_or_none()
if not news:
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Not found")
return _serialize(news)