Files
yu-zhi-ran/platform/backend/app/api/system.py
T
lt 5fe426fc90 feat: 全面优化前端架构
- 本地化所有资源 (Vue3, Element Plus) 无需外部 CDN
- 重新设计主页:登录页 + 系统概览 + 侧边栏导航
- 完善管理后台功能:选题管理、系统日志、用户管理
- 全面支持 PC 和 H5 移动端(响应式布局 + 底部导航)
- 优化后端 API 返回格式,兼容前端需求
- 默认首页为登录/系统概览页面
- 统计卡片可点击筛选选题列表
- 模块状态实时展示
2026-04-27 15:14:26 +08:00

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 = {
'待处理': by_status.get('pending', 0),
'待审查': by_status.get('review', 0),
'待发布': by_status.get('ready', 0),
'已发布': by_status.get('published', 0)
}
# 计算今日新增
today = date.today()
today_count = db.query(Topic).filter(
func.date(Topic.created_at) == today
).count()
return {
"stats": {
"total": total,
"pending": status_map['待处理'],
"review": status_map['待审查'],
"ready": status_map['待发布'],
"published": status_map['已发布'],
"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))