Files
yu-zhi-ran/platform/backend/app/api/system.py
T
lt 8920a337e0 fix: 修复前端空白页与API调用错误,统一创作流程
- 前端
  - topics.html: 恢复结构并修复Vue初始化问题
  - 调整创作按钮逻辑:仅已发布选题禁用
  - 修正API端点与payload格式(generate/optimizer/publishing使用topic_ids数组)
  - 移除ElementPlus图标模块依赖,使用全局构建
  - admin.html: 回退至Options API版本,解决this上下文错误
- 后端
  - 注册/api/generate/run路由
  - 简化generate逻辑:允许非已发布选题重创作,更新状态为“待审查”
  - 统一logs查询接口支持query参数
  - 修复admin用户管理字段引用
  - 系统概览返回{ stats }结构
- 静态资源整理
  - 删除冗余element-plus-icons、重复CSS/JS、图标文件
  - 正确放置Vue和ElementPlus全局文件
- 数据库与数据
  - 补充30个案例
  - 更新选题状态与初始数据

验证:所有页面可访问,API认证与端点正常工作。
2026-05-06 21:44:56 +08:00

160 lines
6.2 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_all_topics
from ..core.scheduler import scheduler
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))
@router.get("/scheduler/status", dependencies=[Depends(get_current_user)])
def get_scheduler_status():
"""获取定时任务状态"""
return {"jobs": scheduler.get_jobs()}