From 548ea34238e16e06d7801af95e1f359a161dc76a Mon Sep 17 00:00:00 2001 From: liaozhaorun <1300336796@qq.com> Date: Tue, 16 Dec 2025 00:04:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=89=8D=E7=AB=AFui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- strategy_manager/frontend/dist/index.html | 30 ++++++---- strategy_manager/web_backend.py | 72 +++++++++++++++++------ 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/strategy_manager/frontend/dist/index.html b/strategy_manager/frontend/dist/index.html index dae6a84..d61095e 100644 --- a/strategy_manager/frontend/dist/index.html +++ b/strategy_manager/frontend/dist/index.html @@ -42,7 +42,12 @@

📈 量化策略控制台

- + + + 📦 Version: {{ gitInfo }} + + + - + 刷新状态 @@ -120,18 +125,18 @@ const loading = ref(false); const lastUpdated = ref('-'); - // --- 修改点:默认值为 0 (仅手动) --- - const refreshInterval = ref(0); + // [核心修改] 2. 为 Git 信息创建一个 ref + const gitInfo = ref('Loading...'); + const refreshInterval = ref(0); const intervalOptions = [ - { label: '✋ 仅手动', value: 0 }, // 建议将手动选项放在第一个 + { label: '✋ 仅手动', value: 0 }, { label: '⚡ 3秒自动', value: 3000 }, { label: '⏱ 5秒自动', value: 5000 }, { label: '🐢 10秒自动', value: 10000 } ]; let timer = null; - // --- 核心数据获取 --- const fetchStatus = async () => { if (loading.value) return; loading.value = true; @@ -140,6 +145,10 @@ if (!res.ok) throw new Error("Error"); const data = await res.json(); strategies.value = data.strategies; + + // [核心修改] 3. 更新 Git 信息 + gitInfo.value = data.git_info || 'N/A'; + lastUpdated.value = new Date().toLocaleTimeString(); } catch (e) { message.error("连接服务器失败"); @@ -148,16 +157,13 @@ } }; - // --- 定时器管理 --- const resetTimer = () => { if (timer) clearInterval(timer); - // 只有大于 0 才启动定时器 if (refreshInterval.value > 0) { timer = setInterval(fetchStatus, refreshInterval.value); } }; - // 监听下拉框变化 watch(refreshInterval, () => { resetTimer(); if (refreshInterval.value > 0) { @@ -168,7 +174,6 @@ } }); - // --- 其他逻辑 --- const handleAction = (name, action) => { const map = { start: '启动', stop: '停止', restart: '重启' }; dialog.warning({ @@ -216,8 +221,8 @@ }; onMounted(() => { - fetchStatus(); // 页面加载时请求一次 - resetTimer(); // 初始化定时器(当前为0,所以不启动) + fetchStatus(); + resetTimer(); }); onUnmounted(() => { @@ -226,6 +231,7 @@ return { strategies, loading, lastUpdated, + gitInfo, // [核心修改] 4. 将 gitInfo 暴露给模板 refreshInterval, intervalOptions, showLogModal, currentLogKey, logLines, logLoading, fetchStatus, handleAction, viewLogs, fetchLogs diff --git a/strategy_manager/web_backend.py b/strategy_manager/web_backend.py index d32e5f9..2fc9beb 100644 --- a/strategy_manager/web_backend.py +++ b/strategy_manager/web_backend.py @@ -1,9 +1,14 @@ +# filename: main.py + +import subprocess +import os +import logging +from collections import deque +from pathlib import Path + 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 @@ -24,7 +29,41 @@ logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) -# ================== 定时任务逻辑 ================== +# ================== 新增:获取 Git 版本信息 ================== +def get_git_commit_info(): + """ + [智能版] 获取 Git 仓库的最新提交信息。 + 此函数会自动从当前文件位置向上查找项目根目录(.git 文件夹所在位置)。 + """ + try: + # 1. 获取 main.py 所在的目录 + script_dir = Path(__file__).resolve().parent + + # 2. 推断出项目根目录 (即 main.py 所在目录的上一级) + project_root = script_dir.parent + + # 3. 检查项目根目录下是否存在 .git 文件夹 + git_dir = project_root / ".git" + if not git_dir.is_dir(): + return "Git repo not found in parent directory" + + # 4. 在推断出的项目根目录下执行 git 命令 + # 使用 cwd (current working directory) 参数是关键 + result = subprocess.run( + ['git', 'log', '-1', '--pretty=format:%h - %s (%cr)'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=True, + encoding='utf-8', + cwd=project_root # <--- 在这里指定命令的执行目录 + ) + return result.stdout.strip() + except (FileNotFoundError, subprocess.CalledProcessError) as e: + logger.warning(f"无法获取 Git 提交信息: {e}") + return "获取 Git 信息失败" + +# ================== 定时任务逻辑 (保持不变) ================== def scheduled_restart_task(): """ @@ -47,7 +86,6 @@ def scheduled_restart_task(): for name in running_strategies: try: - # 调用 manager 的重启逻辑 (包含 stop -> sleep -> start) manager.restart_strategy(name) logger.info(f"✅ [定时任务] {name} 重启成功") except Exception as e: @@ -56,42 +94,39 @@ def scheduled_restart_task(): logger.info("⏰ [定时任务] 自动重启流程结束") -# ================== FastAPI 事件钩子 ================== +# ================== FastAPI 事件钩子 (保持不变) ================== @app.on_event("startup") async def start_scheduler(): - """服务启动时,加载定时任务""" - # 任务 1: 每天 08:55 scheduler.add_job( scheduled_restart_task, CronTrigger(hour=8, minute=58), id="restart_morning", replace_existing=True ) - - # 任务 2: 每天 20:55 scheduler.add_job( scheduled_restart_task, CronTrigger(hour=20, minute=58), id="restart_evening", replace_existing=True ) - scheduler.start() - logger.info("📅 定时任务调度器已启动 (计划时间: 08:55, 20:55)") + logger.info("📅 定时任务调度器已启动 (计划时间: 08:58, 20:58)") @app.on_event("shutdown") async def stop_scheduler(): - """服务关闭时停止调度器""" scheduler.shutdown() -# ================== 原有 REST API (保持不变) ================== +# ================== API 路由 ================== @app.get("/api/status") def get_status(): - return manager.get_status() + # [核心修改] 在返回状态的同时,注入 Git 版本信息 + status_data = manager.get_status() + status_data['git_info'] = get_git_commit_info() + return status_data @app.post("/api/strategy/{name}/start") @@ -110,7 +145,6 @@ def stop_strategy(name: str): @app.post("/api/strategy/{name}/restart") def restart_strategy(name: str): - # 修复了之前提到的返回值问题 if manager.restart_strategy(name): return {"success": True} raise HTTPException(400, "重启失败,请检查日志") @@ -127,7 +161,6 @@ def get_logs(name: str, lines: int = Query(50, le=500)): 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]} @@ -135,10 +168,11 @@ def get_logs(name: str, lines: int = Query(50, le=500)): raise HTTPException(500, f"读取日志失败: {e}") -# ================== 静态文件挂载 ================== +# ================== 静态文件挂载 (保持不变) ================== +# 注意: 这里的路径是示例,请确保它与你的项目结构匹配 +# 假设你的HTML文件在 "frontend/" 目录下 app.mount("/static", StaticFiles(directory="frontend/dist"), name="static") - @app.get("/") def serve_frontend(): return FileResponse("frontend/dist/index.html") \ No newline at end of file