54e7db0ef0
完整功能包括: - FastAPI 后端 + SQLite + FTS5 全文搜索 - RSS 源管理、自动发现、OPML 导入导出 - 文章抓取、去重、分类、全文检索 - RSS 源健康度监控 - Vue 3 + Element Plus 暗色主题 Web UI - 对外 REST API 供 AI 分析调用 - Docker + docker-compose 部署
113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
"""RSS 源健康度检测"""
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Dict
|
|
from sqlalchemy.orm import Session
|
|
from models import Feed, FetchLog
|
|
|
|
|
|
def get_feed_health(db: Session, feed_id: int = None) -> List[Dict]:
|
|
"""获取 RSS 源健康度信息
|
|
返回每个源的健康状态详情
|
|
"""
|
|
query = db.query(Feed)
|
|
if feed_id:
|
|
query = query.filter(Feed.id == feed_id)
|
|
|
|
feeds = query.all()
|
|
results = []
|
|
|
|
for feed in feeds:
|
|
total = feed.success_count + feed.fail_count
|
|
success_rate = round(feed.success_count / total * 100, 1) if total > 0 else 0
|
|
|
|
days_since_fetch = None
|
|
if feed.last_fetch_at:
|
|
days_since_fetch = (datetime.utcnow() - feed.last_fetch_at).days
|
|
|
|
# 获取最近 7 天抓取记录
|
|
recent_logs = db.query(FetchLog).filter(
|
|
FetchLog.feed_id == feed.id,
|
|
FetchLog.created_at >= datetime.utcnow() - timedelta(days=7)
|
|
).order_by(FetchLog.created_at.desc()).limit(10).all()
|
|
|
|
health = feed.health_status()
|
|
|
|
results.append({
|
|
"id": feed.id,
|
|
"title": feed.title or feed.url,
|
|
"url": feed.url,
|
|
"is_active": feed.is_active,
|
|
"health_status": health,
|
|
"health_label": _health_label(health),
|
|
"success_rate": success_rate,
|
|
"success_count": feed.success_count,
|
|
"fail_count": feed.fail_count,
|
|
"total_fetches": total,
|
|
"last_fetch_at": feed.last_fetch_at.isoformat() if feed.last_fetch_at else None,
|
|
"days_since_fetch": days_since_fetch,
|
|
"article_count": feed.article_count,
|
|
"last_error": feed.last_error,
|
|
"recent_logs": [
|
|
{
|
|
"status": log.status,
|
|
"articles_fetched": log.articles_fetched,
|
|
"response_time_ms": log.response_time_ms,
|
|
"created_at": log.created_at.isoformat(),
|
|
"error_message": log.error_message if log.status == "fail" else None,
|
|
}
|
|
for log in recent_logs
|
|
],
|
|
})
|
|
|
|
return results
|
|
|
|
|
|
def _health_label(status: str) -> str:
|
|
labels = {
|
|
"healthy": "健康",
|
|
"warning": "警告",
|
|
"unhealthy": "异常",
|
|
"unknown": "未知",
|
|
}
|
|
return labels.get(status, "未知")
|
|
|
|
|
|
def get_overall_stats(db: Session) -> Dict:
|
|
"""获取整体统计信息"""
|
|
total_feeds = db.query(Feed).count()
|
|
active_feeds = db.query(Feed).filter(Feed.is_active == True).count()
|
|
total_articles = db.query(Feed).with_entities(Feed.article_count).all()
|
|
total_articles_count = sum(a[0] for a in total_articles) if total_articles else 0
|
|
|
|
# 健康源统计
|
|
feeds = db.query(Feed).all()
|
|
healthy = warning = unhealthy = 0
|
|
for feed in feeds:
|
|
status = feed.health_status()
|
|
if status == "healthy":
|
|
healthy += 1
|
|
elif status == "warning":
|
|
warning += 1
|
|
elif status == "unhealthy":
|
|
unhealthy += 1
|
|
|
|
# 今日抓取
|
|
today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
from models import FetchLog
|
|
today_fetches = db.query(FetchLog).filter(FetchLog.created_at >= today).count()
|
|
today_success = db.query(FetchLog).filter(
|
|
FetchLog.created_at >= today, FetchLog.status == "success"
|
|
).count()
|
|
|
|
return {
|
|
"total_feeds": total_feeds,
|
|
"active_feeds": active_feeds,
|
|
"total_articles": total_articles_count,
|
|
"healthy_feeds": healthy,
|
|
"warning_feeds": warning,
|
|
"unhealthy_feeds": unhealthy,
|
|
"today_fetches": today_fetches,
|
|
"today_success": today_success,
|
|
"today_success_rate": round(today_success / today_fetches * 100, 1) if today_fetches > 0 else 0,
|
|
}
|