修复评审问题: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) {
|
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user