diff --git a/platform/backend/app/core/collector.py b/platform/backend/app/core/collector.py
new file mode 100644
index 0000000..97c3e1d
--- /dev/null
+++ b/platform/backend/app/core/collector.py
@@ -0,0 +1,31 @@
+"""
+选题收集模块
+调用 scripts/collector.py 脚本,从外部来源收集/生成新选题并写入 JSON
+"""
+import subprocess
+from pathlib import Path
+import logging
+import os
+
+logger = logging.getLogger(__name__)
+
+# 计算项目根目录
+PROJECT_ROOT = Path(__file__).resolve().parents[4]
+if os.getenv('PROJECT_ROOT'):
+ PROJECT_ROOT = Path(os.getenv('PROJECT_ROOT'))
+
+def run_collector():
+ """运行选题收集脚本"""
+ script_path = PROJECT_ROOT / "scripts" / "collector.py"
+ if not script_path.exists():
+ raise FileNotFoundError(f"Collector script not found: {script_path}")
+ result = subprocess.run(
+ ["python", str(script_path)],
+ capture_output=True,
+ text=True,
+ cwd=PROJECT_ROOT,
+ timeout=300
+ )
+ if result.returncode != 0:
+ raise RuntimeError(f"Collector failed: {result.stderr}")
+ return {"ok": True, "output": result.stdout}
\ No newline at end of file
diff --git a/platform/backend/app/core/scheduler.py b/platform/backend/app/core/scheduler.py
new file mode 100644
index 0000000..1eb2bd9
--- /dev/null
+++ b/platform/backend/app/core/scheduler.py
@@ -0,0 +1,94 @@
+"""
+定时任务调度器
+基于 APScheduler,支持在 FastAPI 生命周期内运行定时任务
+"""
+import os
+import logging
+from datetime import datetime
+from apscheduler.schedulers.background import BackgroundScheduler
+from apscheduler.triggers.cron import CronTrigger
+from .generator import run_creator
+from .optimizer import run_optimizer
+from .sync import sync_all_topics
+
+logger = logging.getLogger(__name__)
+
+class TaskScheduler:
+ def __init__(self):
+ self.scheduler = BackgroundScheduler()
+ self._started = False
+
+ def start(self):
+ if self._started:
+ logger.warning("Scheduler already started")
+ return
+ # 使用 CronTrigger 设置每日固定时间点
+ self.scheduler.add_job(
+ self._run_sync,
+ CronTrigger(hour=2, minute=30),
+ id='scheduled_sync',
+ replace_existing=True,
+ max_instances=1,
+ coalesce=True
+ )
+ self.scheduler.add_job(
+ self._run_generate,
+ CronTrigger(hour=3, minute=30),
+ id='scheduled_generate',
+ replace_existing=True,
+ max_instances=1,
+ coalesce=True
+ )
+ self.scheduler.add_job(
+ self._run_optimize,
+ CronTrigger(hour=4, minute=30),
+ id='scheduled_optimize',
+ replace_existing=True,
+ max_instances=1,
+ coalesce=True
+ )
+ self.scheduler.start()
+ self._started = True
+ logger.info("Scheduler started with daily cron triggers (02:30, 03:30, 04:30)")
+ def shutdown(self):
+ if self.scheduler.running:
+ self.scheduler.shutdown()
+ logger.info("Scheduler shut down")
+
+ def _run_generate(self):
+ try:
+ logger.info("[Scheduled] Starting content generation...")
+ result = run_creator()
+ logger.info("[Scheduled] Generation completed: %s", result)
+ except Exception as e:
+ logger.exception("[Scheduled] Generation failed: %s", e)
+
+ def _run_optimize(self):
+ try:
+ logger.info("[Scheduled] Starting compliance optimization...")
+ result = run_optimizer()
+ logger.info("[Scheduled] Optimization completed: %s", result)
+ except Exception as e:
+ logger.exception("[Scheduled] Optimization failed: %s", e)
+
+ def _run_sync(self):
+ try:
+ logger.info("[Scheduled] Starting data sync...")
+ sync_all_topics()
+ logger.info("[Scheduled] Sync completed")
+ except Exception as e:
+ logger.exception("[Scheduled] Sync failed: %s", e)
+
+ def get_jobs(self):
+ """返回当前所有定时任务的状态"""
+ jobs = []
+ for job in self.scheduler.get_jobs():
+ jobs.append({
+ "id": job.id,
+ "next_run_time": job.next_run_time.isoformat() if job.next_run_time else None,
+ "trigger": str(job.trigger),
+ })
+ return jobs
+
+# 全局单例
+scheduler = TaskScheduler()
\ No newline at end of file
diff --git a/platform/frontend/admin.html b/platform/frontend/admin.html
index 2947f51..601e5bf 100644
--- a/platform/frontend/admin.html
+++ b/platform/frontend/admin.html
@@ -255,6 +255,7 @@ const app = createApp({
const activeTab = ref('cases');
const currentUser = ref({ username: '' });
+ const redirectToPage = (page) => { window.location.href = '/' + page; }; const currentUser = ref({ username: '' });
const isAdmin = ref(false);
const isLoggedIn = ref(false);
@@ -262,8 +263,9 @@ const app = createApp({
fetch('/api/auth/me', { headers: { 'Authorization': 'Bearer ' + token } })
.then(response => response.ok ? response.json() : Promise.reject())
.then(data => {
- currentUser.value = data.user;
- isAdmin.value = data.user.role === 'admin';
+ const user = data.user || { username: '', role: 'user' };
+ currentUser.value = user;
+ isAdmin.value = user.role === 'admin';
isLoggedIn.value = true;
if (!isAdmin.value) {
ElMessage.warning('需要管理员权限');
@@ -275,6 +277,11 @@ const app = createApp({
window.location.href = 'login.html';
});
+ // 页面跳转
+ const redirectToPage = (page) => {
+ window.location.href = '/' + page;
+ };
+
// Cases
const cases = ref([]);
const caseDialogVisible = ref(false);
diff --git a/platform/frontend/logs.html b/platform/frontend/logs.html
index 1f53d18..0bc6aab 100644
--- a/platform/frontend/logs.html
+++ b/platform/frontend/logs.html
@@ -71,7 +71,7 @@
{{ logContent }}{{ logContent }}