fix: CTP重连前探测前置可达性,失败时关闭网关并明确报错

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-25 16:30:35 +08:00
parent 240fbe7994
commit 72361233a0
4 changed files with 157 additions and 42 deletions
+38 -8
View File
@@ -19,7 +19,9 @@
var lastQuotePrice = null;
var priceType = 'limit';
var lastCtpReconnectAt = 0;
var lastCtpUnreachableAt = 0;
var ctpReconnecting = false;
var ctpConnectInflight = false;
var isTradingSession = false;
var hasSlTpMonitoring = false;
var ctpConnected = false;
@@ -121,6 +123,15 @@
} catch (e) { /* quota */ }
}
function showCtpError(msg) {
var hint = document.querySelector('.ctp-install-hint');
if (hint) hint.textContent = msg || '';
}
function isCtpUnreachableError(msg) {
return !!(msg && (msg.indexOf('不可达') >= 0 || msg.indexOf('Connection refused') >= 0 || msg.indexOf('timed out') >= 0));
}
function applyPositionsData(data) {
if (!list || !data) return;
var cap = document.getElementById('cap-display');
@@ -130,6 +141,12 @@
ctpConnected = !!connected;
isTradingSession = !!data.trading_session;
updateCtpBadge(!!connected, !!connecting);
if (!connected && !connecting && data.ctp_status && data.ctp_status.last_error) {
showCtpError(data.ctp_status.last_error);
if (isCtpUnreachableError(data.ctp_status.last_error)) {
lastCtpUnreachableAt = Date.now();
}
}
var riskBadge = document.getElementById('risk-badge');
if (riskBadge && data.risk_status) {
riskBadge.textContent = data.risk_status.status_label || '';
@@ -300,7 +317,7 @@
}
function waitForCtpConnected(maxMs) {
var deadline = Date.now() + (maxMs || 35000);
var deadline = Date.now() + (maxMs || 70000);
function tick() {
return fetch('/api/ctp/status')
.then(function (r) { return r.json(); })
@@ -308,6 +325,7 @@
var st = d.status || {};
if (st.connected) {
updateCtpBadge(true, false);
showCtpError('');
if (d.account && d.account.available != null) {
var avail = document.getElementById('avail-display');
if (avail) avail.textContent = Number(d.account.available).toFixed(2);
@@ -323,8 +341,10 @@
}
updateCtpBadge(false, false);
if (st.last_error) {
var hint = document.querySelector('.ctp-install-hint');
if (hint) hint.textContent = st.last_error;
showCtpError(st.last_error);
if (isCtpUnreachableError(st.last_error)) {
lastCtpUnreachableAt = Date.now();
}
}
return false;
})
@@ -334,6 +354,10 @@
}
function requestCtpConnect(force) {
if (!force && ctpConnectInflight) {
return Promise.resolve({});
}
ctpConnectInflight = true;
updateCtpBadge(false, true);
return fetch('/api/ctp/connect', {
method: 'POST',
@@ -344,24 +368,29 @@
.then(function (d) {
if (d.status && d.status.connected) {
updateCtpBadge(true, false);
showCtpError('');
pollPositions();
return d;
}
if (d.connecting || (d.status && d.status.connecting)) {
return waitForCtpConnected(35000).then(function (ok) {
if (!ok && d.error) alert(d.error);
else if (!ok && d.status && d.status.last_error) alert(d.status.last_error);
return waitForCtpConnected(70000).then(function (ok) {
if (!ok && d.error) showCtpError(d.error);
else if (!ok && d.status && d.status.last_error) showCtpError(d.status.last_error);
return d;
});
}
if (!d.ok) {
updateCtpBadge(false, false);
alert(d.error || (d.status && d.status.last_error) || '连接失败');
var err = d.error || (d.status && d.status.last_error) || '连接失败';
showCtpError(err);
}
return d;
})
.catch(function () {
updateCtpBadge(false, false);
})
.finally(function () {
ctpConnectInflight = false;
});
}
@@ -465,9 +494,10 @@
}
function tryAutoCtpReconnect() {
if (ctpReconnecting) return;
if (ctpReconnecting || ctpConnectInflight) return;
var now = Date.now();
if (now - lastCtpReconnectAt < 60000) return;
if (lastCtpUnreachableAt && now - lastCtpUnreachableAt < 300000) return;
lastCtpReconnectAt = now;
ctpReconnecting = true;
requestCtpConnect(false).finally(function () {