fix: 修改自动运行失败跳过设置,确保状态一致性

This commit is contained in:
QLHazyCoder
2026-04-14 12:48:52 +08:00
parent 6ff6960d97
commit 2f0495fb6e
3 changed files with 134 additions and 28 deletions
+99 -12
View File
@@ -65,7 +65,7 @@ const PERSISTED_SETTING_DEFAULTS = {
sub2apiPassword: '',
sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME,
customPassword: '',
autoRunSkipFailures: true,
autoRunSkipFailures: false,
autoRunFallbackThreadIntervalMinutes: 0,
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
@@ -204,7 +204,7 @@ function normalizeScheduledAutoRunPlan(plan) {
return {
totalRuns: normalizeRunCount(plan.totalRuns),
autoRunSkipFailures: true,
autoRunSkipFailures: Boolean(plan.autoRunSkipFailures),
mode: plan.mode === 'continue' ? 'continue' : 'restart',
};
}
@@ -2520,7 +2520,7 @@ async function launchScheduledAutoRun(trigger = 'alarm') {
'info'
);
startAutoRunLoop(plan.totalRuns, {
autoRunSkipFailures: true,
autoRunSkipFailures: Boolean(plan.autoRunSkipFailures),
mode: plan.mode,
});
return true;
@@ -2836,7 +2836,7 @@ async function handleMessage(message, sender) {
throw new Error('已有自动运行倒计时计划,请先取消或立即开始。');
}
const totalRuns = normalizeRunCount(message.payload?.totalRuns || 1);
const autoRunSkipFailures = true;
const autoRunSkipFailures = Boolean(message.payload?.autoRunSkipFailures);
const mode = message.payload?.mode === 'continue' ? 'continue' : 'restart';
await setState({ autoRunSkipFailures });
startAutoRunLoop(totalRuns, { autoRunSkipFailures, mode });
@@ -2848,7 +2848,7 @@ async function handleMessage(message, sender) {
const totalRuns = normalizeRunCount(message.payload?.totalRuns || 1);
return await scheduleAutoRun(totalRuns, {
delayMinutes: message.payload?.delayMinutes,
autoRunSkipFailures: true,
autoRunSkipFailures: Boolean(message.payload?.autoRunSkipFailures),
mode: message.payload?.mode,
});
}
@@ -4025,6 +4025,41 @@ async function logAutoRunFinalSummary(totalRuns, roundSummaries = []) {
}
}
async function waitBetweenAutoRunRounds(targetRun, totalRuns, roundSummary) {
if (totalRuns <= 1 || targetRun >= totalRuns) {
return;
}
const fallbackThreadIntervalMinutes = normalizeAutoRunFallbackThreadIntervalMinutes(
(await getState()).autoRunFallbackThreadIntervalMinutes
);
if (fallbackThreadIntervalMinutes <= 0) {
return;
}
const statusLabel = roundSummary?.status === 'failed' ? '失败' : '完成';
await addLog(
`线程间隔:第 ${targetRun}/${totalRuns} 轮已${statusLabel},等待 ${fallbackThreadIntervalMinutes} 分钟后开始下一轮。`,
'info'
);
await sleepWithStop(fallbackThreadIntervalMinutes * 60 * 1000);
}
async function waitBeforeAutoRunRetry(targetRun, totalRuns, nextAttemptRun) {
const fallbackThreadIntervalMinutes = normalizeAutoRunFallbackThreadIntervalMinutes(
(await getState()).autoRunFallbackThreadIntervalMinutes
);
if (fallbackThreadIntervalMinutes <= 0) {
return;
}
await addLog(
`线程间隔:等待 ${fallbackThreadIntervalMinutes} 分钟后开始第 ${targetRun}/${totalRuns} 轮第 ${nextAttemptRun} 次尝试。`,
'info'
);
await sleepWithStop(fallbackThreadIntervalMinutes * 60 * 1000);
}
async function handleAutoRunLoopUnhandledError(error) {
console.error(LOG_PREFIX, 'Auto run loop crashed:', error);
if (!isStopError(error)) {
@@ -4059,7 +4094,7 @@ async function autoRunLoop(totalRuns, options = {}) {
autoRunTotalRuns = totalRuns;
autoRunCurrentRun = 0;
autoRunAttemptRun = 0;
const autoRunSkipFailures = true;
const autoRunSkipFailures = Boolean(options.autoRunSkipFailures);
const initialMode = options.mode === 'continue' ? 'continue' : 'restart';
const resumeCurrentRun = Number.isInteger(options.resumeCurrentRun) && options.resumeCurrentRun > 0
? Math.min(totalRuns, options.resumeCurrentRun)
@@ -4102,10 +4137,14 @@ async function autoRunLoop(totalRuns, options = {}) {
for (let targetRun = resumeCurrentRun; targetRun <= totalRuns; targetRun += 1) {
const roundSummary = roundSummaries[targetRun - 1];
let attemptRun = continueCurrentOnFirstAttempt && targetRun === resumeCurrentRun ? resumeAttemptRun : 1;
let reuseExistingProgress = continueCurrentOnFirstAttempt && targetRun === resumeCurrentRun;
const resumingCurrentRound = continueCurrentOnFirstAttempt && targetRun === resumeCurrentRun;
let attemptRun = resumingCurrentRound ? resumeAttemptRun : 1;
let reuseExistingProgress = resumingCurrentRound;
const maxAttemptsForRound = autoRunSkipFailures
? AUTO_RUN_MAX_RETRIES_PER_ROUND + 1
: Math.max(1, attemptRun);
while (attemptRun <= AUTO_RUN_MAX_RETRIES_PER_ROUND + 1) {
while (attemptRun <= maxAttemptsForRound) {
autoRunCurrentRun = targetRun;
autoRunAttemptRun = attemptRun;
roundSummary.attempts = attemptRun;
@@ -4207,7 +4246,7 @@ async function autoRunLoop(totalRuns, options = {}) {
const reason = getErrorMessage(err);
roundSummary.failureReasons.push(reason);
const canRetry = attemptRun <= AUTO_RUN_MAX_RETRIES_PER_ROUND;
const canRetry = autoRunSkipFailures && attemptRun < maxAttemptsForRound;
await setState({
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
@@ -4247,6 +4286,21 @@ async function autoRunLoop(totalRuns, options = {}) {
}
throw sleepError;
}
try {
await waitBeforeAutoRunRetry(targetRun, totalRuns, attemptRun + 1);
} catch (sleepError) {
if (isStopError(sleepError)) {
stoppedEarly = true;
await addLog(`${targetRun}/${totalRuns} 轮已被用户停止`, 'warn');
await broadcastAutoRunStatus('stopped', {
currentRun: targetRun,
totalRuns,
attemptRun,
});
break;
}
throw sleepError;
}
attemptRun += 1;
reuseExistingProgress = false;
continue;
@@ -4257,8 +4311,25 @@ async function autoRunLoop(totalRuns, options = {}) {
await setState({
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
});
if (!autoRunSkipFailures) {
cancelPendingCommands('当前轮执行失败。');
await broadcastStopToContentScripts();
await addLog('自动重试未开启,自动运行将在当前失败后停止。', 'warn');
stoppedEarly = true;
await broadcastAutoRunStatus('stopped', {
currentRun: targetRun,
totalRuns,
attemptRun,
});
break;
}
await addLog(`${targetRun}/${totalRuns} 轮最终失败:${reason}`, 'error');
await addLog(`${targetRun}/${totalRuns} 轮已达到 ${AUTO_RUN_MAX_RETRIES_PER_ROUND} 次重试上限,继续下一轮。`, 'warn');
await addLog(
targetRun < totalRuns
? `${targetRun}/${totalRuns} 轮已达到 ${AUTO_RUN_MAX_RETRIES_PER_ROUND} 次重试上限,继续下一轮。`
: `${targetRun}/${totalRuns} 轮已达到 ${AUTO_RUN_MAX_RETRIES_PER_ROUND} 次重试上限,本次自动运行结束。`,
'warn'
);
cancelPendingCommands('当前轮已达到重试上限。');
await broadcastStopToContentScripts();
forceFreshTabsNextRun = true;
@@ -4272,6 +4343,22 @@ async function autoRunLoop(totalRuns, options = {}) {
if (stoppedEarly) {
break;
}
try {
await waitBetweenAutoRunRounds(targetRun, totalRuns, roundSummary);
} catch (sleepError) {
if (isStopError(sleepError)) {
stoppedEarly = true;
await addLog(`${targetRun}/${totalRuns} 轮已被用户停止`, 'warn');
await broadcastAutoRunStatus('stopped', {
currentRun: targetRun,
totalRuns,
attemptRun: autoRunAttemptRun,
});
break;
}
throw sleepError;
}
}
await setState({
@@ -4333,7 +4420,7 @@ async function resumeAutoRun() {
await addLog('检测到自动流程暂停上下文已丢失,正在从当前进度恢复自动运行...', 'warn');
startAutoRunLoop(totalRuns, {
autoRunSkipFailures: true,
autoRunSkipFailures: Boolean(state.autoRunSkipFailures),
mode: 'continue',
resumeCurrentRun: currentRun,
resumeAttemptRun: attemptRun,
+5 -3
View File
@@ -939,9 +939,11 @@ header {
.toggle-switch input {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
pointer-events: none;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
cursor: pointer;
}
.toggle-switch-track {
+30 -13
View File
@@ -134,6 +134,7 @@ const AUTO_DELAY_DEFAULT_MINUTES = 30;
const AUTO_FALLBACK_THREAD_INTERVAL_MIN_MINUTES = 0;
const AUTO_FALLBACK_THREAD_INTERVAL_MAX_MINUTES = 1440;
const AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES = 0;
const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3;
const AUTO_STEP_DELAY_MIN_SECONDS = 0;
const AUTO_STEP_DELAY_MAX_SECONDS = 600;
const DEFAULT_LOCAL_CPA_STEP9_MODE = 'submit';
@@ -427,10 +428,14 @@ function setAutoRunFallbackRiskPromptDismissed(dismissed) {
setPromptDismissed(AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY, dismissed);
}
function shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures) {
return totalRuns >= AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS;
}
async function openAutoSkipFailuresConfirmModal() {
const result = await openConfirmModalWithOption({
title: '兜底说明',
message: '开启后,当某一轮失败且无法继续时,会直接放弃当前线程,重新开新一轮,直到补足目标运行次数。线程间隔只在开启兜底且运行次数大于 1 时生效。',
title: '自动重试说明',
message: `开启后,自动模式在某一轮失败时,会先在当前轮自动重试;单轮最多重试 ${AUTO_RUN_MAX_RETRIES_PER_ROUND} 次,仍失败则放弃当前轮并继续下一轮。线程间隔只在开启自动重试且总轮数大于 1 时生效。`,
confirmLabel: '确认开启',
});
@@ -440,10 +445,6 @@ async function openAutoSkipFailuresConfirmModal() {
};
}
function shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures) {
return Boolean(autoRunSkipFailures) && totalRuns >= AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS;
}
async function openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadIntervalMinutes) {
const intervalLabel = Number.isFinite(fallbackThreadIntervalMinutes)
? `${fallbackThreadIntervalMinutes} 分钟`
@@ -451,7 +452,7 @@ async function openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadInte
const result = await openConfirmModalWithOption({
title: '自动运行风险提醒',
message: `当前设置为 ${totalRuns} 轮自动化,已开启兜底,线程间隔为 ${intervalLabel}次数太多,可能会因为 IP 短时间注册过多原因而都失败。建议控制在 ${AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS} 轮以下,并将线程间隔设置在 ${AUTO_RUN_FALLBACK_RISK_RECOMMENDED_THREAD_INTERVAL_MINUTES} 分钟以上。是否继续?`,
message: `当前设置为 ${totalRuns} 轮自动化,已开启自动重试,线程间隔为 ${intervalLabel}轮数过多时,可能会因为 IP 短时间注册过多而集中失败。建议控制在 ${AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS} 轮以下,并将线程间隔设置在 ${AUTO_RUN_FALLBACK_RISK_RECOMMENDED_THREAD_INTERVAL_MINUTES} 分钟以上。是否继续?`,
confirmLabel: '继续',
});
@@ -671,7 +672,7 @@ function updateFallbackThreadIntervalInputState() {
return;
}
inputAutoSkipFailuresThreadIntervalMinutes.disabled = !(inputAutoSkipFailures.checked && getRunCountValue() > 1);
inputAutoSkipFailuresThreadIntervalMinutes.disabled = Boolean(inputAutoSkipFailures.disabled);
}
function updateAutoDelayInputState() {
@@ -845,7 +846,7 @@ function collectSettingsPayload() {
hotmailLocalBaseUrl: inputHotmailLocalBaseUrl.value.trim(),
cloudflareDomain: selectedCloudflareDomain,
cloudflareDomains: domains,
autoRunSkipFailures: true,
autoRunSkipFailures: inputAutoSkipFailures.checked,
autoRunFallbackThreadIntervalMinutes: normalizeAutoRunThreadIntervalMinutes(inputAutoSkipFailuresThreadIntervalMinutes.value),
autoRunDelayEnabled: inputAutoDelayEnabled.checked,
autoRunDelayMinutes: normalizeAutoDelayMinutes(inputAutoDelayMinutes.value),
@@ -1017,7 +1018,7 @@ function applyAutoRunStatus(payload = currentAutoRun) {
|| usesGeneratedAliasMailProvider(selectMailProvider.value)
|| isCustomEmailGeneratorSelected();
inputEmail.disabled = locked;
inputAutoSkipFailures.disabled = true;
inputAutoSkipFailures.disabled = scheduled;
if (currentAutoRun.totalRuns > 0) {
inputRunCount.value = String(currentAutoRun.totalRuns);
@@ -1121,7 +1122,7 @@ function applySettingsState(state) {
inputHotmailLocalBaseUrl.value = state?.hotmailLocalBaseUrl || '';
renderCloudflareDomainOptions(state?.cloudflareDomain || '');
setCloudflareDomainEditMode(false, { clearInput: true });
inputAutoSkipFailures.checked = true;
inputAutoSkipFailures.checked = Boolean(state?.autoRunSkipFailures);
inputAutoSkipFailuresThreadIntervalMinutes.value = String(normalizeAutoRunThreadIntervalMinutes(state?.autoRunFallbackThreadIntervalMinutes));
inputAutoDelayEnabled.checked = Boolean(state?.autoRunDelayEnabled);
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(state?.autoRunDelayMinutes));
@@ -2662,6 +2663,11 @@ btnAutoRun.addEventListener('click', async () => {
try {
const totalRuns = getRunCountValue();
let mode = 'restart';
const autoRunSkipFailures = inputAutoSkipFailures.checked;
const fallbackThreadIntervalMinutes = normalizeAutoRunThreadIntervalMinutes(
inputAutoSkipFailuresThreadIntervalMinutes.value
);
inputAutoSkipFailuresThreadIntervalMinutes.value = String(fallbackThreadIntervalMinutes);
if (shouldOfferAutoModeChoice()) {
const startStep = getFirstUnfinishedStep();
@@ -2673,6 +2679,17 @@ btnAutoRun.addEventListener('click', async () => {
mode = choice;
}
if (shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures)
&& !isAutoRunFallbackRiskPromptDismissed()) {
const result = await openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadIntervalMinutes);
if (!result.confirmed) {
return;
}
if (result.dismissPrompt) {
setAutoRunFallbackRiskPromptDismissed(true);
}
}
btnAutoRun.disabled = true;
inputRunCount.disabled = true;
const delayEnabled = inputAutoDelayEnabled.checked;
@@ -2687,7 +2704,7 @@ btnAutoRun.addEventListener('click', async () => {
payload: {
totalRuns,
delayMinutes,
autoRunSkipFailures: true,
autoRunSkipFailures,
mode,
},
});
@@ -3116,7 +3133,7 @@ chrome.runtime.onMessage.addListener((message) => {
}
}
if (message.payload.autoRunSkipFailures !== undefined) {
inputAutoSkipFailures.checked = true;
inputAutoSkipFailures.checked = Boolean(message.payload.autoRunSkipFailures);
updateFallbackThreadIntervalInputState();
}
if (message.payload.autoRunDelayEnabled !== undefined) {