import logging from fastapi import APIRouter, HTTPException, Depends, Body from sqlalchemy.orm import Session from sqlalchemy import func from datetime import datetime, date from typing import Dict, Any, List, Optional from pathlib import Path import os import json from ..database import get_db from ..models import Topic, Article from ..core.generator import run_creator from ..core.optimizer import run_optimizer from ..core.sync import sync_all_topics from ..core.scheduler import scheduler from .auth import get_current_user PROJECT_ROOT = Path(__file__).resolve().parents[4] if os.getenv('PROJECT_ROOT'): PROJECT_ROOT = Path(os.getenv('PROJECT_ROOT')) LOGS_DIR = PROJECT_ROOT / "automation" / "logs" logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/system", tags=["system"]) def _aggregate_status_counts(db: Session): """聚合状态计数,兼容中英文状态值""" raw = db.query(Topic.status, func.count()).group_by(Topic.status).all() mapping = { 'pending': ['pending', '待处理'], 'review': ['review', '待审查'], 'ready': ['ready', '待发布'], 'published': ['published', '已发布'] } counts = {'pending': 0, 'review': 0, 'ready': 0, 'published': 0} for status_val, cnt in raw: for key, aliases in mapping.items(): if status_val in aliases: counts[key] += cnt break return counts @router.get("/status") def get_status(db: Session = Depends(get_db)): total = db.query(Topic).count() counts = _aggregate_status_counts(db) today = date.today() today_count = db.query(Topic).filter(func.date(Topic.created_at) == today).count() return { "stats": { "total": total, "pending": counts['pending'], "review": counts['review'], "ready": counts['ready'], "published": counts['published'], "today": today_count } } @router.post("/generate/run", dependencies=[Depends(get_current_user)]) def trigger_generation(topic_id: str = Body(None, embed=True), db: Session = Depends(get_db)): logger.info(f"Received topic_id={topic_id}") try: result = run_creator(topic_id) if not result["ok"]: raise HTTPException(status_code=500, detail=result["error"]) sync_all_topics() return {"message": "Generation triggered", "result": result} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/optimize/run", dependencies=[Depends(get_current_user)]) def trigger_optimization(topic_ids: List[str] = Body(None, embed=True), db: Session = Depends(get_db)): try: result = run_optimizer(topic_ids) if not result["ok"]: raise HTTPException(status_code=500, detail=result["error"]) report = result.get("report") if report: sync_all_topics() return {"message": "Optimization completed", "summary": report["summary"]} else: return {"message": "Optimization completed", "stdout": result.get("stdout", "")} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/logs/{log_date}", dependencies=[Depends(get_current_user)]) def get_logs(log_date: str, log_type: str = "creator"): log_file = LOGS_DIR / f"{log_type}_{log_date}.log" if not log_file.exists(): raise HTTPException(status_code=404, detail=f"Log file not found: {log_file}") content = log_file.read_text(encoding='utf-8') lines = content.splitlines()[-100:] if log_type != "collector" else content.splitlines()[-200:] return {"log_date": log_date, "log_type": log_type, "content": lines} @router.get("/pipeline/status", dependencies=[Depends(get_current_user)]) def get_pipeline_status(db: Session = Depends(get_db)): total = db.query(Topic).count() counts = _aggregate_status_counts(db) log_files = { "collector": LOGS_DIR / f"collector_{date.today().isoformat()}.log", "creator": LOGS_DIR / f"creator_{date.today().isoformat()}.log", "optimizer": LOGS_DIR / f"optimizer_{date.today().isoformat()}.log", } pipeline_status = {} for name, log_file in log_files.items(): if log_file.exists(): mtime = datetime.fromtimestamp(log_file.stat().st_mtime) pipeline_status[name] = {"last_run": mtime.isoformat(), "exists": True} else: pipeline_status[name] = {"exists": False, "last_run": None} return {"topics_count": total, "status_distribution": counts, "pipeline_modules": pipeline_status} @router.post("/sync/run") def run_sync(): try: sync_all_topics() return {"message": "Sync completed (DB → JSON backup)"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/automation/topics") def list_automation_topics(db: Session = Depends(get_db)): try: topics = db.query(Topic).order_by(Topic.created_at.desc()).limit(100).all() result = [] for t in topics: result.append({ "id": t.id, "title": t.title, "field": t.field, "status": t.status, "priority": t.priority, "priority_score": t.priority_score, "total_score": t.total_score, "created_at": t.created_at.isoformat() if t.created_at else None, "updated_at": t.updated_at.isoformat() if t.updated_at else None, "ready_at": t.ready_at.isoformat() if t.ready_at else None, "compliance_score": t.compliance_score }) return {"count": len(result), "topics": result[:50]} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/refresh") def refresh_all(): try: sync_all_topics() return {"message": "Refresh completed"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/scheduler/status", dependencies=[Depends(get_current_user)]) def get_scheduler_status(): return {"jobs": scheduler.get_jobs()}