This commit is contained in:
dengqichen 2025-11-19 15:28:57 +08:00
parent fafec7278d
commit 8ebb7779e0
2 changed files with 46 additions and 5 deletions

View File

@ -1,4 +1,5 @@
const BaseAction = require('../core/base-action'); const BaseAction = require('../core/base-action');
const { ConfigurationError, TimeoutError } = require('../core/errors');
/** /**
* 自定义动作 - 调用适配器中的自定义函数 * 自定义动作 - 调用适配器中的自定义函数
@ -11,20 +12,30 @@ class CustomAction extends BaseAction {
const timeout = this.config.timeout || 300000; // 默认5分钟超时 const timeout = this.config.timeout || 300000; // 默认5分钟超时
if (!handler) { if (!handler) {
throw new Error('缺少处理函数名称'); throw new ConfigurationError('缺少处理函数名称', 'handler', {
action: 'custom',
config: this.config
});
} }
this.log('info', `执行自定义函数: ${handler}`); this.log('info', `执行自定义函数: ${handler}`);
// 检查适配器中是否存在该函数 // 检查适配器中是否存在该函数
if (typeof this.context.adapter[handler] !== 'function') { if (typeof this.context.adapter[handler] !== 'function') {
throw new Error(`自定义处理函数不存在: ${handler}`); throw new ConfigurationError(
`自定义处理函数不存在: ${handler}`,
`adapter.${handler}`,
{ availableHandlers: Object.keys(this.context.adapter) }
);
} }
// 使用 Promise.race 实现超时保护 // 使用 Promise.race 实现超时保护
const timeoutPromise = new Promise((_, reject) => { const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => { setTimeout(() => {
reject(new Error(`自定义函数执行超时 (${timeout}ms): ${handler}`)); reject(new TimeoutError(`自定义函数: ${handler}`, timeout, {
handler,
params
}));
}, timeout); }, timeout);
}); });

View File

@ -1,13 +1,16 @@
const BaseAction = require('../core/base-action'); const BaseAction = require('../core/base-action');
const { ConfigurationError, TimeoutError, RetryExhaustedError } = require('../core/errors');
/** /**
* 重试块动作 - 将一组步骤作为整体进行重试 * 重试块动作 - 将一组步骤作为整体进行重试
* 支持整体超时保护防止无限重试
* *
* 配置示例 * 配置示例
* - action: retryBlock * - action: retryBlock
* name: "支付流程" * name: "支付流程"
* maxRetries: 5 * maxRetries: 5
* retryDelay: 2000 * retryDelay: 2000
* totalTimeout: 300000 # 整体超时5分钟
* onRetryBefore: * onRetryBefore:
* - action: custom * - action: custom
* handler: "regenerateCard" * handler: "regenerateCard"
@ -23,6 +26,7 @@ class RetryBlockAction extends BaseAction {
steps = [], steps = [],
maxRetries = 3, maxRetries = 3,
retryDelay = 1000, retryDelay = 1000,
totalTimeout = 600000, // 默认10分钟整体超时
onRetryBefore = [], onRetryBefore = [],
onRetryAfter = [] onRetryAfter = []
} = this.config; } = this.config;
@ -30,12 +34,30 @@ class RetryBlockAction extends BaseAction {
const blockName = this.config.name || 'RetryBlock'; const blockName = this.config.name || 'RetryBlock';
if (!steps || steps.length === 0) { if (!steps || steps.length === 0) {
throw new Error('RetryBlock 必须包含至少一个步骤'); throw new ConfigurationError(
'RetryBlock 必须包含至少一个步骤',
'steps',
{ blockName, config: this.config }
);
} }
let lastError = null; let lastError = null;
const startTime = Date.now();
for (let attempt = 0; attempt <= maxRetries; attempt++) { for (let attempt = 0; attempt <= maxRetries; attempt++) {
// 检查整体超时
const elapsed = Date.now() - startTime;
if (elapsed > totalTimeout) {
throw new TimeoutError(
`${blockName} (整体)`,
totalTimeout,
{
attempts: attempt,
elapsed,
lastError: lastError?.message
}
);
}
try { try {
if (attempt > 0) { if (attempt > 0) {
this.log('info', `${blockName} - 第 ${attempt + 1} 次重试...`); this.log('info', `${blockName} - 第 ${attempt + 1} 次重试...`);
@ -82,7 +104,15 @@ class RetryBlockAction extends BaseAction {
} }
// 所有重试都失败 // 所有重试都失败
throw new Error(`${blockName} 失败: ${lastError.message}`); throw new RetryExhaustedError(
blockName,
maxRetries + 1,
{
lastError: lastError?.message,
stack: lastError?.stack,
totalTime: Date.now() - startTime
}
);
} }
/** /**