新增web界面管理策略

This commit is contained in:
2025-11-21 16:08:03 +08:00
parent 4b7ec4e564
commit 218ca5f533
11 changed files with 747 additions and 124 deletions

View File

@@ -0,0 +1,144 @@
from fastapi import FastAPI, HTTPException, Query
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pathlib import Path
import logging
from collections import deque
# 引入调度器
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
# 复用现有manager
from core.manager import StrategyManager
# ================== 初始化 ==================
app = FastAPI(title="策略控制台")
manager = StrategyManager()
# 初始化调度器
scheduler = AsyncIOScheduler()
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# ================== 定时任务逻辑 ==================
def scheduled_restart_task():
"""
定时任务:重启所有正在运行的策略
"""
logger.info("⏰ [定时任务] 触发自动重启流程...")
# 获取当前所有策略状态
status = manager.get_status()
running_strategies = [
name for name, info in status['strategies'].items()
if info['status'] == 'running'
]
if not running_strategies:
logger.info("⏰ [定时任务] 当前无运行中的策略,无需重启")
return
logger.info(f"⏰ [定时任务] 即将重启以下策略: {running_strategies}")
for name in running_strategies:
try:
# 调用 manager 的重启逻辑 (包含 stop -> sleep -> start)
manager.restart_strategy(name)
logger.info(f"✅ [定时任务] {name} 重启成功")
except Exception as e:
logger.error(f"❌ [定时任务] {name} 重启失败: {e}")
logger.info("⏰ [定时任务] 自动重启流程结束")
# ================== FastAPI 事件钩子 ==================
@app.on_event("startup")
async def start_scheduler():
"""服务启动时,加载定时任务"""
# 任务 1: 每天 08:55
scheduler.add_job(
scheduled_restart_task,
CronTrigger(hour=8, minute=55),
id="restart_morning",
replace_existing=True
)
# 任务 2: 每天 20:55
scheduler.add_job(
scheduled_restart_task,
CronTrigger(hour=20, minute=55),
id="restart_evening",
replace_existing=True
)
scheduler.start()
logger.info("📅 定时任务调度器已启动 (计划时间: 08:55, 20:55)")
@app.on_event("shutdown")
async def stop_scheduler():
"""服务关闭时停止调度器"""
scheduler.shutdown()
# ================== 原有 REST API (保持不变) ==================
@app.get("/api/status")
def get_status():
return manager.get_status()
@app.post("/api/strategy/{name}/start")
def start_strategy(name: str):
if manager.start_strategy(name):
return {"success": True}
raise HTTPException(400, "启动失败")
@app.post("/api/strategy/{name}/stop")
def stop_strategy(name: str):
if manager.stop_strategy(name):
return {"success": True}
raise HTTPException(400, "停止失败")
@app.post("/api/strategy/{name}/restart")
def restart_strategy(name: str):
# 修复了之前提到的返回值问题
if manager.restart_strategy(name):
return {"success": True}
raise HTTPException(400, "重启失败,请检查日志")
@app.get("/api/logs/{name}")
def get_logs(name: str, lines: int = Query(50, le=500)):
try:
if '_' not in name:
return {"lines": []}
strategy_name, symbol = name.split('_', 1)
log_file = Path(f"logs/{strategy_name}/{symbol}.log")
if not log_file.exists():
return {"lines": ["日志文件尚未生成"]}
# 修复编码和读取
with open(log_file, 'r', encoding='utf-8', errors='replace') as f:
last_lines = deque(f, maxlen=lines)
return {"lines": [line.rstrip() for line in last_lines]}
except Exception as e:
raise HTTPException(500, f"读取日志失败: {e}")
# ================== 静态文件挂载 ==================
app.mount("/static", StaticFiles(directory="frontend/dist"), name="static")
@app.get("/")
def serve_frontend():
return FileResponse("frontend/dist/index.html")