Files
yu-zhi-ran/platform/backend/app/api/search_rankings.py
T
2026-06-09 17:18:09 +08:00

78 lines
2.5 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import desc, func
from typing import Optional
from datetime import datetime, timedelta, timezone
from ..database import get_db
from ..models import SearchRanking, Article, Topic
from .auth import get_current_user, org_filter
router = APIRouter(prefix="/api/seo", tags=["seo"])
ArticleModel = Article
def _apply_org_filter(query, current_user, db):
of = org_filter(current_user, ArticleModel)
if of is not True:
query = query.join(ArticleModel, SearchRanking.article_id == ArticleModel.id, isouter=True)
query = query.filter(of)
return query
@router.get("/rankings")
def get_rankings(
article_id: Optional[str] = None,
keyword: Optional[str] = None,
days: int = Query(30, ge=1, le=365),
limit: int = Query(50, ge=1, le=200),
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""获取搜索排名数据"""
query = db.query(SearchRanking)
query = _apply_org_filter(query, current_user, db)
if article_id:
query = query.filter(SearchRanking.article_id == article_id)
if keyword:
query = query.filter(SearchRanking.keyword.ilike(f"%{keyword}%"))
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
query = query.filter(SearchRanking.checked_at >= cutoff)
rankings = query.order_by(desc(SearchRanking.checked_at)).limit(limit).all()
return {"ok": True, "data": [r.to_dict() for r in rankings]}
@router.get("/rankings/overview")
def get_rankings_overview(
days: int = Query(30, ge=1, le=365),
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""搜索排名概览统计"""
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
base = db.query(SearchRanking)
base = _apply_org_filter(base, current_user, db)
base = base.filter(SearchRanking.checked_at >= cutoff)
total_checks = base.count()
on_page = base.filter(SearchRanking.position.isnot(None)).count()
ai_cited = base.filter(SearchRanking.ai_cited == True).count()
best = base.filter(SearchRanking.position.isnot(None)).order_by(SearchRanking.position).first()
return {
"ok": True,
"data": {
"total_checks": total_checks,
"on_page": on_page,
"ai_cited": ai_cited,
"best_position": best.position if best else None,
"best_keyword": best.keyword if best else None,
}
}