fix: SN回执幂等防止重复补发,后台命令下发后轮询回执直到出结果
This commit is contained in:
@@ -4258,11 +4258,46 @@ async function snAdmCmd(cmd) {
|
||||
if (!r.success) { toast(r.message || '下发失败', 'err'); return; }
|
||||
toast(r.message || '命令已下发', 'ok');
|
||||
setTimeout(() => renderSnAdmin(document.getElementById('C')), 1200);
|
||||
// ★ 游戏服回执是异步的(原先要等下一轮 Timer C,10~2 分钟不等)。
|
||||
// 原实现只在 1.2 秒后刷新一次,那时 status 还是 pending、result 为空,
|
||||
// 看起来就像"没动作 / 结果没返回"。这里持续轮询该命令回执直到出结果。
|
||||
const cmdId = (r.data && r.data.id) || 0;
|
||||
if (cmdId) snAdmPollCmd(cmdId, sn, 0);
|
||||
} catch (e) {
|
||||
toast('下发失败: ' + e.message, 'err');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮询 SN 命令回执(3 秒一次,最长 150 秒)。
|
||||
* 游戏服执行完会把 web_sn_commands.status 置为 done 并写入 result;
|
||||
* 3 分钟未回执则被后端标记为 timeout。
|
||||
*/
|
||||
function snAdmPollCmd(cmdId, sn, tries) {
|
||||
const MAX_TRIES = 50; // 50 * 3s = 150 秒
|
||||
setTimeout(async () => {
|
||||
if (snAdm.tab !== 'detail' || snAdm.sn !== sn) return; // 用户已离开该详情页
|
||||
let c = null;
|
||||
try {
|
||||
const r = await snAdmApi('admin_detail', { sn: sn });
|
||||
const list = (r.success && r.data && r.data.commands) || [];
|
||||
c = list.find(x => Number(x.id) === Number(cmdId)) || null;
|
||||
} catch (e) { /* 网络抖动,下一轮再试 */ }
|
||||
if (c && (c.status === 'done' || c.status === 'timeout')) {
|
||||
if (c.status === 'timeout') toast('游戏服 3 分钟内未回执,命令已超时', 'err');
|
||||
else toast('游戏服已执行:' + (c.result || '(无结果)'), 'ok');
|
||||
renderSnAdmin(document.getElementById('C'));
|
||||
return;
|
||||
}
|
||||
if (tries < MAX_TRIES) {
|
||||
snAdmPollCmd(cmdId, sn, tries + 1);
|
||||
} else {
|
||||
toast('游戏服仍未回执,可稍后刷新查看', 'err');
|
||||
renderSnAdmin(document.getElementById('C'));
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
async function renderSnLost(el) {
|
||||
const r = await snAdmApi('admin_lost_list', { page: 1, size: 100 });
|
||||
if (!r.success) { el.innerHTML = '<div class="card"><p style="color:var(--red)">' + escAdmHtml(r.message || '加载失败') + '</p></div>'; return; }
|
||||
|
||||
+7
-1
@@ -493,7 +493,8 @@ function snAckCommands() {
|
||||
$itemType = '';
|
||||
$linkType = '';
|
||||
$linkId = 0;
|
||||
$res = $db->prepare("SELECT cmd, sn, player, item_type, link_type, link_id
|
||||
$alreadyDone = false;
|
||||
$res = $db->prepare("SELECT cmd, sn, player, item_type, link_type, link_id, status
|
||||
FROM web_sn_commands WHERE id = :id");
|
||||
$res->bindValue(':id', $id, SQLITE3_INTEGER);
|
||||
$rr = $res->execute();
|
||||
@@ -504,8 +505,13 @@ function snAckCommands() {
|
||||
$itemType = (string)$row['item_type'];
|
||||
$linkType = (string)$row['link_type'];
|
||||
$linkId = (int)$row['link_id'];
|
||||
$alreadyDone = ((string)$row['status'] === 'done');
|
||||
}
|
||||
|
||||
// ★ 幂等:Java 侧只有确认收到才从本地回执缓冲移除,网络抖动会原样重发。
|
||||
// 已处理过的直接跳过,否则报失核查通过会再入队一次补发、注销会重置冷静期。
|
||||
if ($alreadyDone) continue;
|
||||
|
||||
$u = $db->prepare("UPDATE web_sn_commands SET status='done', result=:r, done_at=:t
|
||||
WHERE id=:id");
|
||||
$u->bindValue(':r', $result, SQLITE3_TEXT);
|
||||
|
||||
Reference in New Issue
Block a user