fix: content quality, image format, task monitor, calendar data source, search UI & sort

This commit is contained in:
Yuzhiran Dev
2026-05-26 11:26:10 +08:00
parent b2d043b231
commit ac8644d752
36 changed files with 2294 additions and 873 deletions
+103
View File
@@ -27,6 +27,8 @@ MODULES = {
"scheduled_optimize": {"name": "🔍 合规审查", "cron": "03:00"},
"scheduled_optimize_sources": {"name": "📡 信息源优化", "cron": "05:00"},
"scheduled_metrics_sync": {"name": "📊 指标同步", "cron": "06:00"},
"scheduled_reset_search_usage": {"name": "🔁 搜索用量重置", "cron": "00:05"},
"scheduled_task_monitor": {"name": "⏰ 任务监控", "cron": "*"},
}
def _log_task(module_id: str, status: str, message: str = None,
@@ -107,6 +109,7 @@ class TaskScheduler:
("scheduled_optimize", self._run_optimize, "合规审查"),
("scheduled_optimize_sources", self._run_optimize_sources, "信息源优化"),
("scheduled_metrics_sync", self._run_metrics_sync, "指标同步"),
("scheduled_reset_search_usage", self._run_reset_search_usage, "搜索用量重置"),
]
for module_id, fn, name in MODULE_JOBS:
@@ -129,6 +132,17 @@ class TaskScheduler:
)
logger.info(f"调度任务: {module_id} -> {schedule}")
# 每小时运行的任务监控:检测卡死/中断任务
self.scheduler.add_job(
self._run_task_monitor,
CronTrigger(hour='*/1'),
id='scheduled_task_monitor',
replace_existing=True,
max_instances=1,
coalesce=True
)
logger.info("调度任务: scheduled_task_monitor -> 每小时")
self.scheduler.start()
self._started = True
logger.info("Scheduler started with dynamic schedule from TaskConfig")
@@ -441,6 +455,95 @@ class TaskScheduler:
started_at=started, finished_at=datetime.now(timezone.utc))
logger.exception("[Scheduled] Metrics sync failed: %s", e)
def _run_reset_search_usage(self):
"""每日凌晨重置搜索 API 提供商用量计数"""
started = datetime.now(timezone.utc)
_log_task("scheduled_reset_search_usage", "running", started_at=started)
try:
from ..database import SessionLocal
from ..models import SearchProvider
db = SessionLocal()
try:
total = db.query(SearchProvider).update({SearchProvider.usage_today: 0, SearchProvider.last_used_at: None})
db.commit()
_log_task("scheduled_reset_search_usage", "success",
message=f"已重置 {total} 个提供商用量",
result_data={"reset_count": total},
started_at=started, finished_at=datetime.now(timezone.utc))
logger.info("[Scheduled] Reset %d search providers usage", total)
finally:
db.close()
except Exception as e:
_log_task("scheduled_reset_search_usage", "failed",
message=str(e),
error_trace=traceback.format_exc(),
started_at=started, finished_at=datetime.now(timezone.utc))
logger.exception("[Scheduled] Reset search usage failed: %s", e)
def _run_task_monitor(self):
"""每小时检查卡死/中断的任务,标记为失败"""
started = datetime.now(timezone.utc)
_log_task("scheduled_task_monitor", "running", started_at=started)
stuck_tasklog_timeout = 7200 # 超过2小时视为卡死
stuck_contenttask_timeout = 10800 # 超过3小时视为卡死
try:
from ..database import SessionLocal
from ..models import TaskLog, ContentTask
db = SessionLocal()
try:
now = datetime.now(timezone.utc)
cutoff_tasklog = now.timestamp() - stuck_tasklog_timeout
cutoff_content = now.timestamp() - stuck_contenttask_timeout
marked = 0
# 检查 TaskLog 中卡死的 running 记录
stuck_logs = db.query(TaskLog).filter(
TaskLog.status == "running",
TaskLog.started_at.isnot(None)
).all()
for log in stuck_logs:
if log.started_at.timestamp() < cutoff_tasklog:
log.status = "failed"
log.finished_at = now
log.error_trace = "系统监控:任务运行超时(超过2小时)或进程中断,已自动标记为失败"
if log.started_at:
log.duration = int((now - log.started_at).total_seconds())
marked += 1
logger.warning("[TaskMonitor] 标记 TaskLog %d (%s) 为失败(超时)", log.id, log.module_id)
# 检查 ContentTask 中卡死的 running 记录
stuck_tasks = db.query(ContentTask).filter(
ContentTask.status == "running",
ContentTask.started_at.isnot(None)
).all()
for task in stuck_tasks:
if task.started_at.timestamp() < cutoff_content:
task.status = "failed"
task.finished_at = now
task.error_msg = "系统监控:任务运行超时(超过3小时)或进程中断,已自动标记为失败"
if task.started_at:
task.duration = int((now - task.started_at).total_seconds())
marked += 1
logger.warning("[TaskMonitor] 标记 ContentTask %s (%s) 为失败(超时)", task.task_id, task.stage)
if marked:
db.commit()
logger.info("[TaskMonitor] 已标记 %d 个卡死任务为失败", marked)
_log_task("scheduled_task_monitor", "success",
message=f"检查完成,标记 {marked} 个卡死任务",
result_data={"marked_failed": marked},
started_at=started, finished_at=datetime.now(timezone.utc))
finally:
db.close()
except Exception as e:
import traceback
_log_task("scheduled_task_monitor", "failed",
message=str(e),
error_trace=traceback.format_exc(),
started_at=started, finished_at=datetime.now(timezone.utc))
logger.exception("[TaskMonitor] 监控检查失败: %s", e)
def get_jobs(self):
"""返回当前所有定时任务的状态"""
jobs = []