diff --git a/backend/api/routes/system.py b/backend/api/routes/system.py index d487e65..0388d12 100644 --- a/backend/api/routes/system.py +++ b/backend/api/routes/system.py @@ -592,6 +592,20 @@ def _parse_supervisor_status(raw: str) -> Tuple[bool, Optional[int], str]: return False, None, state return False, None, "UNKNOWN" +def _list_supervisor_process_names(status_all_raw: str) -> list[str]: + names: list[str] = [] + if not status_all_raw: + return names + for ln in status_all_raw.splitlines(): + s = (ln or "").strip() + if not s: + continue + # 每行格式: ... + name = s.split(None, 1)[0].strip() + if name: + names.append(name) + return names + def _get_program_name() -> str: # 你给的宝塔配置是 [program:auto_sys] @@ -880,6 +894,99 @@ async def trading_restart(_admin: Dict[str, Any] = Depends(require_system_admin) raise HTTPException(status_code=500, detail=f"supervisorctl restart 失败: {e}") +@router.post("/trading/restart-all") +async def trading_restart_all( + _admin: Dict[str, Any] = Depends(require_system_admin), + prefix: str = "auto_sys_acc", + include_default: bool = False, + do_update: bool = True, +) -> Dict[str, Any]: + """ + 一键重启所有账号交易进程(supervisor)。 + + - 默认重启所有以 auto_sys_acc 开头的 program(例如 auto_sys_acc1/2/3...) + - 可选 include_default=true:同时包含 SUPERVISOR_TRADING_PROGRAM(默认 auto_sys) + - 可选 do_update=true:先执行 supervisorctl reread/update 再重启(确保新 ini 生效) + """ + try: + prefix = (prefix or "auto_sys_acc").strip() + if not prefix: + prefix = "auto_sys_acc" + + # 先读取全量 status,拿到有哪些进程 + status_all = _run_supervisorctl(["status"]) + names = _list_supervisor_process_names(status_all) + + targets: list[str] = [] + for n in names: + if n.startswith(prefix): + targets.append(n) + + if include_default: + default_prog = _get_program_name() + if default_prog and default_prog not in targets and default_prog in names: + targets.append(default_prog) + + if not targets: + return { + "message": "未找到可重启的交易进程", + "prefix": prefix, + "include_default": include_default, + "count": 0, + "targets": [], + "status_all": status_all, + } + + reread_out = "" + update_out = "" + if do_update: + try: + reread_out = _run_supervisorctl(["reread"]) + except Exception as e: + reread_out = f"failed: {e}" + try: + update_out = _run_supervisorctl(["update"]) + except Exception as e: + update_out = f"failed: {e}" + + results: list[Dict[str, Any]] = [] + ok = 0 + failed = 0 + for prog in targets: + try: + out = _run_supervisorctl(["restart", prog]) + raw = _run_supervisorctl(["status", prog]) + running, pid, state = _parse_supervisor_status(raw) + results.append( + { + "program": prog, + "ok": True, + "output": out, + "status": {"running": running, "pid": pid, "state": state, "raw": raw}, + } + ) + ok += 1 + except Exception as e: + failed += 1 + results.append({"program": prog, "ok": False, "error": str(e)}) + + return { + "message": "已发起批量重启", + "prefix": prefix, + "include_default": include_default, + "do_update": do_update, + "count": len(targets), + "ok": ok, + "failed": failed, + "reread": reread_out, + "update": update_out, + "targets": targets, + "results": results, + } + except Exception as e: + raise HTTPException(status_code=500, detail=f"批量重启失败: {e}") + + @router.get("/backend/status") async def backend_status(_admin: Dict[str, Any] = Depends(require_system_admin)) -> Dict[str, Any]: """ diff --git a/frontend/src/components/ConfigPanel.jsx b/frontend/src/components/ConfigPanel.jsx index 1a2b2cd..6dd2d3f 100644 --- a/frontend/src/components/ConfigPanel.jsx +++ b/frontend/src/components/ConfigPanel.jsx @@ -379,6 +379,20 @@ const ConfigPanel = ({ currentUser }) => { } } + const handleRestartAllTrading = async () => { + if (!window.confirm('确定要重启【所有账号】的交易进程吗?这会让所有用户的交易服务短暂中断(约 3-10 秒),用于升级代码后统一生效。')) return + setSystemBusy(true) + setMessage('') + try { + const res = await api.restartAllTradingSystems({ prefix: 'auto_sys_acc', do_update: true }) + setMessage(`已发起批量重启:共 ${res.count} 个,成功 ${res.ok},失败 ${res.failed}`) + } catch (e) { + setMessage('批量重启失败: ' + (e?.message || '未知错误')) + } finally { + setSystemBusy(false) + } + } + useEffect(() => { loadConfigs() checkFeasibility() @@ -972,6 +986,15 @@ const ConfigPanel = ({ currentUser }) => { > 重启交易系统 +