This commit is contained in:
薇薇安 2026-02-01 22:15:35 +08:00
parent c01f681dec
commit 4da3e0bd48
3 changed files with 61 additions and 51 deletions

View File

@ -1151,3 +1151,37 @@ async def backend_restart(_admin: Dict[str, Any] = Depends(require_system_admin)
"note": "重启期间接口可能短暂不可用,页面可等待 3-5 秒后刷新状态。",
}
@router.post("/backend/stop")
async def backend_stop(_admin: Dict[str, Any] = Depends(require_system_admin)) -> Dict[str, Any]:
"""
停止后端服务uvicorn
警告停止后 API 将不可用必须手动登录服务器启动
"""
backend_dir = Path(__file__).parent.parent.parent # backend/
stop_script = backend_dir / "stop.sh"
if not stop_script.exists():
raise HTTPException(status_code=500, detail=f"找不到停止脚本: {stop_script}")
cur_pid = os.getpid()
# 后台执行sleep 1 后再停止,保证当前请求可以返回
cmd = ["bash", "-lc", f"sleep 1; '{stop_script}'"]
try:
subprocess.Popen(
cmd,
cwd=str(backend_dir),
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True,
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"启动停止脚本失败: {e}")
return {
"message": "已发起后端停止1s 后执行)",
"pid_before": cur_pid,
"script": str(stop_script),
"warning": "后端服务停止后Web 界面将无法访问,请手动在服务器启动!",
}

View File

@ -610,43 +610,16 @@ const GlobalConfig = () => {
}
}
const handleStartTrading = async () => {
const handleStopBackend = async () => {
if (!window.confirm('警告:确定要停止后端服务吗?\n\n停止后 Web 界面将立即无法访问!\n必须手动登录服务器执行 ./start.sh 才能恢复。')) return
setSystemBusy(true)
setMessage('')
try {
const res = await api.startTradingSystem()
setMessage(res.message || '交易系统已启动')
await loadSystemStatus()
const res = await api.stopBackend()
setMessage(res.message || '后端服务已停止')
alert('后端服务已停止,页面将不再响应。请手动去服务器启动。')
} catch (error) {
setMessage('启动失败: ' + (error.message || '未知错误'))
} finally {
setSystemBusy(false)
}
}
const handleStopTrading = async () => {
setSystemBusy(true)
setMessage('')
try {
const res = await api.stopTradingSystem()
setMessage(res.message || '交易系统已停止')
await loadSystemStatus()
} catch (error) {
setMessage('停止失败: ' + (error.message || '未知错误'))
} finally {
setSystemBusy(false)
}
}
const handleRestartTrading = async () => {
setSystemBusy(true)
setMessage('')
try {
const res = await api.restartTradingSystem()
setMessage(res.message || '交易系统已重启')
await loadSystemStatus()
} catch (error) {
setMessage('重启失败: ' + (error.message || '未知错误'))
setMessage('停止后端失败: ' + (error.message || '未知错误'))
} finally {
setSystemBusy(false)
}
@ -1203,6 +1176,15 @@ const GlobalConfig = () => {
>
重启后端服务
</button>
<button
type="button"
className="system-btn danger"
onClick={handleStopBackend}
disabled={systemBusy}
title="停止后端服务(需要手动去服务器启动)"
>
停止后端服务
</button>
<button
type="button"
className="system-btn"
@ -1218,24 +1200,6 @@ const GlobalConfig = () => {
<div className="system-control-group" style={{ marginTop: '15px' }}>
<h4 className="control-group-title" style={{ margin: '10px 0 8px', fontSize: '14px', color: '#666' }}>交易服务管理</h4>
<div className="system-actions">
<button
type="button"
className="system-btn"
onClick={handleStartTrading}
disabled={systemBusy || systemStatus?.running === true}
title="通过 supervisorctl 启动交易系统"
>
启动
</button>
<button
type="button"
className="system-btn"
onClick={handleStopTrading}
disabled={systemBusy || systemStatus?.running === false}
title="通过 supervisorctl 停止交易系统"
>
停止
</button>
<button
type="button"
className="system-btn primary"

View File

@ -671,6 +671,18 @@ export const api = {
return response.json();
},
stopBackend: async () => {
const response = await fetch(buildUrl('/api/system/backend/stop'), {
method: 'POST',
headers: withAccountHeaders({ 'Content-Type': 'application/json' }),
});
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: '停止后端失败' }));
throw new Error(error.detail || '停止后端失败');
}
return response.json();
},
// 日志监控Redis List
getSystemLogs: async (params = {}) => {
const query = new URLSearchParams(params).toString();