auto-account-machine/src/tools/automation-framework/actions/retry-block-action.js
2025-11-18 23:02:49 +08:00

147 lines
3.7 KiB
JavaScript

const BaseAction = require('../core/base-action');
/**
* 重试块动作 - 将一组步骤作为整体进行重试
*
* 配置示例:
* - action: retryBlock
* name: "支付流程"
* maxRetries: 5
* retryDelay: 2000
* onRetryBefore:
* - action: custom
* handler: "regenerateCard"
* steps:
* - action: fillForm
* fields: {...}
* - action: click
* selector: {...}
*/
class RetryBlockAction extends BaseAction {
async execute() {
const {
steps = [],
maxRetries = 3,
retryDelay = 1000,
onRetryBefore = [],
onRetryAfter = []
} = this.config;
const blockName = this.config.name || 'RetryBlock';
if (!steps || steps.length === 0) {
throw new Error('RetryBlock 必须包含至少一个步骤');
}
let lastError = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
if (attempt > 0) {
this.log('info', `${blockName} - 第 ${attempt + 1} 次重试...`);
// 执行重试前的钩子
if (onRetryBefore.length > 0) {
this.log('debug', '执行重试前钩子...');
await this.executeHooks(onRetryBefore);
}
// 延迟
if (retryDelay > 0) {
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
// 执行步骤块
this.log('debug', `执行 ${steps.length} 个步骤...`);
await this.executeSteps(steps);
// 执行成功后的钩子(仅首次成功时)
if (attempt > 0 && onRetryAfter.length > 0) {
this.log('debug', '执行重试后钩子...');
await this.executeHooks(onRetryAfter);
}
// 成功,跳出循环
if (attempt > 0) {
this.log('success', `${blockName} 在第 ${attempt + 1} 次尝试后成功`);
}
return { success: true, attempts: attempt + 1 };
} catch (error) {
lastError = error;
if (attempt < maxRetries) {
this.log('warn', `${blockName} 执行失败: ${error.message}`);
this.log('info', `准备重试 (${attempt + 1}/${maxRetries})...`);
} else {
this.log('error', `${blockName}${maxRetries + 1} 次尝试后仍然失败`);
}
}
}
// 所有重试都失败
throw new Error(`${blockName} 失败: ${lastError.message}`);
}
/**
* 执行钩子函数
*/
async executeHooks(hooks) {
for (const hookConfig of hooks) {
await this.executeStep(hookConfig);
}
}
/**
* 执行步骤列表
*/
async executeSteps(steps) {
for (const stepConfig of steps) {
await this.executeStep(stepConfig);
}
}
/**
* 执行单个步骤
*/
async executeStep(stepConfig) {
const actionType = stepConfig.action;
// 动态加载对应的 Action
const ActionClass = this.getActionClass(actionType);
const action = new ActionClass(
this.page,
stepConfig,
this.context
);
return await action.execute();
}
/**
* 根据 action 类型获取 Action 类
*/
getActionClass(actionType) {
const actionMap = {
navigate: require('./navigate-action'),
fillForm: require('./fill-form-action'),
click: require('./click-action'),
wait: require('./wait-action'),
custom: require('./custom-action')
};
const ActionClass = actionMap[actionType];
if (!ActionClass) {
throw new Error(`未知的 action 类型: ${actionType}`);
}
return ActionClass;
}
}
module.exports = RetryBlockAction;