修复评审问题:A1-A5/B1-B7/C1-C6/D1-D4 全部22项修改收尾

- 捡漏模式移入循环内部(非死代码)
- rAF/interval 双重触发改 else if 互斥
- 删除冗余 '555' 判断和重复 EXPIRE
- 网络错误计数基准从 batchSize 改为 failedResults.length
- Fetch 拦截加 needsPatch 快速短路
- 跨天顺延逻辑(超过30秒移至明天)
- rAF 可通过 _glmRafCancelled 中断
- setState 改用 Object.assign
This commit is contained in:
ephron
2026-04-18 22:22:40 +08:00
parent d1791e709d
commit 86b64c4248

View File

@@ -91,7 +91,7 @@
}; };
function setState(patch) { function setState(patch) {
state = { ...state, ...patch }; Object.assign(state, patch);
refreshUI(); refreshUI();
} }
@@ -338,7 +338,8 @@
setState({ stats: { ...state.stats, errors: state.stats.errors + failedResults.length } }); setState({ stats: { ...state.stats, errors: state.stats.errors + failedResults.length } });
const networkErrors = reasons.filter(r => r.startsWith('网络')).length; const networkErrors = reasons.filter(r => r.startsWith('网络')).length;
consecutiveErrors = networkErrors === batchSize ? consecutiveErrors + 1 : 0; consecutiveErrors = (networkErrors > 0 && networkErrors === failedResults.length)
? consecutiveErrors + 1 : 0;
// 连续网络错误 → 暂停 // 连续网络错误 → 暂停
if (consecutiveErrors >= 3) { if (consecutiveErrors >= 3) {
@@ -354,8 +355,8 @@
return { ok: false }; return { ok: false };
} }
// 只有 429限流才退避555 和 EXPIRE 无延迟立即重试 // 只有 429限流才退避EXPIRE 和系统繁忙无延迟立即重试
if (reasons.every(r => r === 'EXPIRE' || r === '系统繁忙' || r === '555')) continue; if (reasons.every(r => r === 'EXPIRE' || r === '系统繁忙')) continue;
// 限流检测 (独立计数) // 限流检测 (独立计数)
if (reasons.some(r => r.includes('429') || r.includes('限流'))) { if (reasons.some(r => r.includes('429') || r.includes('限流'))) {
@@ -367,9 +368,6 @@
throttleCount = 0; throttleCount = 0;
} }
// EXPIRE → 立即重试不等待
if (reasons.every(r => r === 'EXPIRE')) continue;
// 前20秒全速冲之后才考虑降速 // 前20秒全速冲之后才考虑降速
const elapsedSec = (performance.now() - state.stats.startTime) / 1000; const elapsedSec = (performance.now() - state.stats.startTime) / 1000;
@@ -398,25 +396,29 @@
// 自适应延迟 // 自适应延迟
const d = getDelay(roundNum); const d = getDelay(roundNum);
if (d > 0) await sleep(d); if (d > 0) await sleep(d);
}
if (!stopRequested) { // 超过 maxRetry 且在5分钟内 → 切捡漏模式
const elapsed = (performance.now() - state.stats.startTime) / 1000; if (totalAttempt >= CFG.maxRetry) {
if (elapsed < 300) { const elapsedSec2 = (performance.now() - state.stats.startTime) / 1000;
log('进入捡漏模式(10:00-10:05),降速等待退票...'); if (elapsedSec2 < 300) {
log('进入捡漏模式,降速等待退票...');
CFG._savedConcurrency = CFG.concurrency; CFG._savedConcurrency = CFG.concurrency;
CFG._savedMaxRetry = CFG.maxRetry;
CFG.concurrency = 2; CFG.concurrency = 2;
CFG.slowDelay = 3000; CFG.slowDelay = 3000;
// 继续循环不走 else CFG.maxRetry = totalAttempt + 200;
continue;
} else { } else {
setState({ status: 'failed' }); setState({ status: 'failed' });
CFG.concurrency = CFG._savedConcurrency ?? CFG.concurrency; CFG.concurrency = CFG._savedConcurrency ?? CFG.concurrency;
} CFG.maxRetry = CFG._savedMaxRetry ?? CFG.maxRetry;
} else { log(`达到上限 ${CFG._savedMaxRetry ?? CFG.maxRetry}`);
CFG.concurrency = CFG._savedConcurrency ?? CFG.concurrency; return { ok: false };
} }
}
}
return { ok: false }; return { ok: false };
})();
try { return await _retryLock; } try { return await _retryLock; }
finally { _retryLock = null; } finally { _retryLock = null; }
@@ -486,7 +488,14 @@
const ct = resp.headers.get('content-type') || ''; const ct = resp.headers.get('content-type') || '';
if (ct.includes('application/json')) { if (ct.includes('application/json')) {
const text = await resp.text(); const text = await resp.text();
if (/"isSoldOut":true|"soldOut":true|"isServerBusy":true/.test(text)) { const needsPatch = /"isSoldOut":true|"soldOut":true|"isServerBusy":true|"stock":0/.test(text);
if (!needsPatch) {
return new Response(text, {
status: resp.status,
statusText: resp.statusText,
headers: resp.headers,
});
}
const patched = text const patched = text
.replace(/"isSoldOut":true/g, '"isSoldOut":false') .replace(/"isSoldOut":true/g, '"isSoldOut":false')
.replace(/"soldOut":true/g, '"soldOut":false') .replace(/"soldOut":true/g, '"soldOut":false')
@@ -498,12 +507,6 @@
headers: resp.headers, headers: resp.headers,
}); });
} }
return new Response(text, {
status: resp.status,
statusText: resp.statusText,
headers: resp.headers,
});
}
return resp; return resp;
}; };
// 伪装 // 伪装
@@ -786,6 +789,7 @@
function stopAll() { function stopAll() {
stopRequested = true; stopRequested = true;
if (window._glmRafCancelled) window._glmRafCancelled();
_activeControllers.forEach(ac => { try { ac.abort(); } catch {} }); _activeControllers.forEach(ac => { try { ac.abort(); } catch {} });
_activeControllers = []; _activeControllers = [];
setState({ proactive: false, status: 'idle', count: 0 }); setState({ proactive: false, status: 'idle', count: 0 });
@@ -836,9 +840,12 @@
log(`已过${CFG.rushTime} ${passedSec.toFixed(0)}秒, 立即开抢!`); log(`已过${CFG.rushTime} ${passedSec.toFixed(0)}秒, 立即开抢!`);
startProactive(); startProactive();
} else { } else {
// 超过30秒顺延至明天
log(`今天${CFG.rushTime}已过, 明天自动抢购`); log(`今天${CFG.rushTime}已过, 明天自动抢购`);
target.setDate(target.getDate() + 1);
log(`顺延至明天 ${CFG.rushTime}`);
} }
return; // 如果是顺延情况,继续往下设定定时
} }
// 未到时间 → 自动设定定时 // 未到时间 → 自动设定定时
@@ -869,16 +876,17 @@
// 精确等待: 用 setInterval 10ms 检查, 到时间立即启动 // 精确等待: 用 setInterval 10ms 检查, 到时间立即启动
const preAdvanceMs = (CFG.preAdvanceSec || 0) * 1000; const preAdvanceMs = (CFG.preAdvanceSec || 0) * 1000;
let rafCancelled = false;
window._glmRafCancelled = () => { rafCancelled = true; };
const checkInterval = setInterval(() => { const checkInterval = setInterval(() => {
const remaining = target.getTime() - getServerNow(); const remaining = target.getTime() - getServerNow();
if (remaining <= preAdvanceMs) { if (remaining <= preAdvanceMs) {
clearInterval(checkInterval); clearInterval(checkInterval);
startProactive(); startProactive();
} } else if (remaining > 0 && remaining <= 1000) {
// 额外在剩余1秒内用 requestAnimationFrame 做最后精确对齐
if (remaining > 0 && remaining <= 1000) {
clearInterval(checkInterval); clearInterval(checkInterval);
function rafWait() { function rafWait() {
if (rafCancelled || stopRequested) return;
if (target.getTime() - getServerNow() <= preAdvanceMs) { if (target.getTime() - getServerNow() <= preAdvanceMs) {
startProactive(); startProactive();
} else { } else {