154 lines
6.0 KiB
Python
154 lines
6.0 KiB
Python
from fastapi import APIRouter, HTTPException, Depends
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import func
|
|
from datetime import datetime, date, timedelta
|
|
from typing import Dict, Any, List, Optional
|
|
from pathlib import Path
|
|
from .auth import get_current_user
|
|
import os
|
|
import json
|
|
from ..database import get_db
|
|
from ..models import Topic, Article
|
|
from ..schemas import SystemStatus
|
|
from ..core.generator import run_creator
|
|
from ..core.optimizer import run_optimizer
|
|
from ..core.sync import sync_topic_to_db, sync_all_topics
|
|
|
|
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"
|
|
DATA_DIR = PROJECT_ROOT / "automation" / "data"
|
|
|
|
router = APIRouter(prefix="/api/system", tags=["system"])
|
|
|
|
@router.get("/status")
|
|
def get_status(db: Session = Depends(get_db)):
|
|
"""系统状态概览 - 返回前端兼容格式"""
|
|
total = db.query(Topic).count()
|
|
by_status_result = db.query(Topic.status, func.count()).group_by(Topic.status).all()
|
|
by_status = {status: count for status, count in by_status_result}
|
|
|
|
# 确保返回所有状态(数据库存中文,返回前端需要英文)
|
|
status_map = {
|
|
'pending': by_status.get('待处理', 0),
|
|
'review': by_status.get('待审查', 0),
|
|
'ready': by_status.get('待发布', 0),
|
|
'published': by_status.get('已发布', 0)
|
|
}
|
|
|
|
# 计算今日新增
|
|
today = date.today()
|
|
today_count = db.query(Topic).filter(
|
|
func.date(Topic.created_at) == today
|
|
).count()
|
|
|
|
return {
|
|
"stats": {
|
|
"total": total,
|
|
"pending": status_map['pending'],
|
|
"review": status_map['review'],
|
|
"ready": status_map['ready'],
|
|
"published": status_map['published'],
|
|
"today": today_count
|
|
}
|
|
}
|
|
|
|
@router.post("/generate/run", dependencies=[Depends(get_current_user)])
|
|
def trigger_generation(topic_id: str = None, db: Session = Depends(get_db)):
|
|
"""手动触发内容创作任务"""
|
|
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] = None, 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():
|
|
"""获取流水线各模块状态"""
|
|
try:
|
|
topics_file = DATA_DIR / "sustainability_topics.json"
|
|
topics = []
|
|
if topics_file.exists():
|
|
topics = json.loads(topics_file.read_text(encoding='utf-8'))
|
|
status_counts = {}
|
|
for t in topics:
|
|
s = t.get('status', 'unknown')
|
|
status_counts[s] = status_counts.get(s, 0) + 1
|
|
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": len(topics), "status_distribution": status_counts, "pipeline_modules": pipeline_status}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
@router.post("/sync/run")
|
|
def run_sync():
|
|
"""手动触发数据同步"""
|
|
try:
|
|
sync_all_topics()
|
|
return {"message": "Sync completed"}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
@router.get("/automation/topics")
|
|
def list_automation_topics():
|
|
"""直接读取自动化流水线的选题 JSON"""
|
|
try:
|
|
topics_file = DATA_DIR / "sustainability_topics.json"
|
|
if not topics_file.exists():
|
|
raise HTTPException(status_code=404, detail="Topics JSON not found")
|
|
topics = json.loads(topics_file.read_text(encoding='utf-8'))
|
|
return {"count": len(topics), "topics": topics[-50:]}
|
|
except json.JSONDecodeError as e:
|
|
raise HTTPException(status_code=500, detail=f"JSON parse error: {e}")
|
|
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))
|