from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from sqlalchemy import func as sa_func from typing import List, Optional from ..database import get_db from ..models import TaskConfig, TaskLog from ..schemas import TaskConfigBase, TaskConfigUpdate, TaskConfigResponse, TaskLogResponse from .auth import get_current_admin router = APIRouter(prefix="/api/admin/task-configs", tags=["admin"]) DEFAULT_CONFIGS = { "scheduled_fetch_trends": {"name": "🔥 热点趋势", "cron": "01:10", "params": {"llm_provider": "nvidia"}}, "scheduled_collect": {"name": "📡 内容采集", "cron": "01:30", "params": {"max_topics": 20, "llm_provider": "nvidia"}}, "scheduled_generate": {"name": "🤖 内容创作", "cron": "02:00", "params": {"auto_review": True, "llm_provider": "nvidia"}}, "scheduled_optimize": {"name": "🔍 合规审查", "cron": "03:00", "params": {"auto_pass_threshold": 80, "llm_provider": "sensenova"}}, "scheduled_optimize_sources": {"name": "📡 信息源优化", "cron": "05:00", "params": {"llm_provider": "nvidia"}}, "scheduled_metrics_sync": {"name": "📊 指标同步", "cron": "06:00", "params": {"llm_provider": "nvidia"}}, "scheduled_task_monitor": {"name": "⏰ 任务监控", "cron": "*", "params": {"llm_provider": "nvidia"}}, } def _attach_last_log(resp: TaskConfigResponse, db: Session, module_id: str) -> TaskConfigResponse: """从最近一次 TaskLog 中附加 last_run_at 和 result_data""" last_log = db.query(TaskLog).filter( TaskLog.module_id == module_id, TaskLog.status.in_(["success", "failed"]) ).order_by(TaskLog.started_at.desc()).first() if last_log: resp.last_run_at = last_log.started_at resp.result_data = last_log.result_data or {} return resp @router.get("", response_model=List[TaskConfigResponse]) def list_configs(db: Session = Depends(get_db), admin_user=Depends(get_current_admin)): configs = db.query(TaskConfig).order_by(TaskConfig.id).all() if not configs: _ensure_defaults(db) configs = db.query(TaskConfig).order_by(TaskConfig.id).all() # 批量查询各模块最近一次运行记录 latest_ids = db.query( sa_func.max(TaskLog.id).label('max_id') ).filter( TaskLog.status.in_(["success", "failed"]) ).group_by(TaskLog.module_id).subquery() latest_logs = db.query(TaskLog).filter( TaskLog.id.in_(db.query(latest_ids.c.max_id)) ).all() log_by_module = {log.module_id: log for log in latest_logs} result = [] for cfg in configs: resp = TaskConfigResponse.model_validate(cfg) log = log_by_module.get(cfg.module_id) if log: resp.last_run_at = log.started_at resp.result_data = log.result_data or {} result.append(resp) return result @router.get("/{module_id}", response_model=TaskConfigResponse) def get_config(module_id: str, db: Session = Depends(get_db), admin_user=Depends(get_current_admin)): cfg = db.query(TaskConfig).filter(TaskConfig.module_id == module_id).first() if not cfg: _ensure_defaults(db) cfg = db.query(TaskConfig).filter(TaskConfig.module_id == module_id).first() if not cfg: default = DEFAULT_CONFIGS.get(module_id, {}) cfg = TaskConfig(module_id=module_id, enabled=True, params=default.get("params", {}), schedule=default.get("cron", "")) db.add(cfg) db.commit() db.refresh(cfg) resp = TaskConfigResponse.model_validate(cfg) return _attach_last_log(resp, db, module_id) @router.put("/{module_id}", response_model=TaskConfigResponse) def update_config(module_id: str, data: TaskConfigUpdate, db: Session = Depends(get_db), admin_user=Depends(get_current_admin)): cfg = db.query(TaskConfig).filter(TaskConfig.module_id == module_id).first() if not cfg: _ensure_defaults(db) cfg = db.query(TaskConfig).filter(TaskConfig.module_id == module_id).first() if data.enabled is not None: cfg.enabled = data.enabled if data.params is not None: cfg.params = data.params if data.schedule is not None: cfg.schedule = data.schedule if data.last_modified_by: cfg.last_modified_by = data.last_modified_by db.commit() db.refresh(cfg) return cfg @router.get("/history/{module_id}", response_model=List[TaskLogResponse]) def get_module_history(module_id: str, db: Session = Depends(get_db), admin_user=Depends(get_current_admin), limit: int = 20): logs = db.query(TaskLog).filter(TaskLog.module_id == module_id).order_by(TaskLog.started_at.desc()).limit(limit).all() return [TaskLogResponse.model_validate(l) for l in logs] def _ensure_defaults(db: Session): existing = {c.module_id for c in db.query(TaskConfig).all()} for mid, info in DEFAULT_CONFIGS.items(): if mid not in existing: db.add(TaskConfig(module_id=mid, enabled=True, params=info.get("params", {}), schedule=info.get("cron", ""))) db.commit()