diff --git a/platform/backend/app/core/scheduler.py b/platform/backend/app/core/scheduler.py index 1b9189e..ee7b6af 100644 --- a/platform/backend/app/core/scheduler.py +++ b/platform/backend/app/core/scheduler.py @@ -57,6 +57,7 @@ MODULES = { "scheduled_reset_search_usage": {"name": "🔁 搜索用量重置", "cron": "00:05"}, "scheduled_task_monitor": {"name": "⏰ 任务监控", "cron": "*"}, "scheduled_rank_tracker": {"name": "🔍 搜索排名追踪", "cron": "07:00"}, + "scheduled_geo_tracker": {"name": "🌐 AI 搜索引用追踪", "cron": "07:30"}, } LOG_FILE_MAP = { @@ -69,6 +70,7 @@ LOG_FILE_MAP = { "scheduled_reset_search_usage": "reset_search_usage", "scheduled_task_monitor": "task_monitor", "scheduled_rank_tracker": "rank_tracker", + "scheduled_geo_tracker": "geo_tracker", } def _log_to_file(module_id: str, status: str, message: str = None, error_trace: str = None): @@ -185,6 +187,7 @@ class TaskScheduler: ("scheduled_metrics_sync", self._run_metrics_sync, "指标同步"), ("scheduled_reset_search_usage", self._run_reset_search_usage, "搜索用量重置"), ("scheduled_rank_tracker", self._run_rank_tracker, "搜索排名追踪"), + ("scheduled_geo_tracker", self._run_geo_tracker, "AI 搜索引用追踪"), ] for module_id, fn, name in MODULE_JOBS: @@ -381,6 +384,7 @@ class TaskScheduler: started_at=started, finished_at=datetime.now(timezone.utc), triggered_by=triggered_by) db.close() except Exception as e: + db.close() _log_task("scheduled_optimize_sources", "failed", log_id=log_id, message=str(e), error_trace=traceback.format_exc(), @@ -493,6 +497,7 @@ class TaskScheduler: logger.info("[Scheduled] Metrics sync: no zhihu articles to sync") db.close() except Exception as e: + db.close() _log_task("scheduled_metrics_sync", "failed", log_id=log_id, message=str(e), error_trace=traceback.format_exc(), @@ -622,6 +627,40 @@ class TaskScheduler: started_at=started, finished_at=datetime.now(timezone.utc)) logger.exception("[RankTracker] 排名追踪失败: %s", e) + def _run_geo_tracker(self): + """每日 AI 搜索引用追踪""" + started = datetime.now(timezone.utc) + log_id = _log_task("scheduled_geo_tracker", "running", started_at=started) + try: + import subprocess + result = subprocess.run( + [sys.executable, str(PROJECT_ROOT / "scripts" / "geo_tracker.py")], + capture_output=True, text=True, timeout=600 + ) + if result.returncode == 0: + try: + data = json.loads(result.stdout.strip()) + except json.JSONDecodeError: + data = {"raw": result.stdout.strip()[:200]} + _log_task("scheduled_geo_tracker", "success", log_id=log_id, + message=f"GEO 追踪完成: {data.get('articles_checked', 0)} 篇, " + f"AI 引用 {data.get('ai_citations', 0)} 条", + result_data=data, + started_at=started, finished_at=datetime.now(timezone.utc)) + logger.info("[GeoTracker] 完成: %s", result.stdout.strip()[:200]) + else: + _log_task("scheduled_geo_tracker", "failed", log_id=log_id, + message=f"返回码 {result.returncode}", + error_trace=result.stderr[-500:], + started_at=started, finished_at=datetime.now(timezone.utc)) + except Exception as e: + import traceback + _log_task("scheduled_geo_tracker", "failed", log_id=log_id, + message=str(e), + error_trace=traceback.format_exc(), + started_at=started, finished_at=datetime.now(timezone.utc)) + logger.exception("[GeoTracker] AI 搜索引用追踪失败: %s", e) + def get_jobs(self): """返回当前所有定时任务的状态""" jobs = []