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