161 lines
4.2 KiB
JavaScript
161 lines
4.2 KiB
JavaScript
/**
|
||
* 原子执行器 · L3 执行原子性保障
|
||
*
|
||
* 包装所有写入操作,确保:
|
||
* 1. 执行前保存检查点
|
||
* 2. 执行中记录每一步
|
||
* 3. 执行失败自动回滚
|
||
* 4. 执行完成释放锁
|
||
*
|
||
* 使用方式:
|
||
* var executor = new AtomicExecutor(devId);
|
||
* var result = await executor.execute('创建工单', async function(ctx) {
|
||
* await ctx.step('写入Notion', async function() { ... });
|
||
* await ctx.step('触发Workflow', async function() { ... });
|
||
* });
|
||
*
|
||
* 版权:国作登字-2026-A-00037559
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
var executionLock = require('../middleware/execution-lock');
|
||
var checkpoint = require('./checkpoint');
|
||
var CheckpointManager = checkpoint.CheckpointManager;
|
||
|
||
// 锁释放延迟(给前端时间拉取最终状态)
|
||
var LOCK_RELEASE_DELAY_MS = 10000;
|
||
|
||
/**
|
||
* 原子执行器
|
||
* @param {string} devId - 开发者编号
|
||
*/
|
||
function AtomicExecutor(devId) {
|
||
this.devId = devId;
|
||
}
|
||
|
||
/**
|
||
* 执行操作(原子性保障)
|
||
* @param {string} operationName - 操作描述
|
||
* @param {Function} fn - 异步执行函数,接收 ctx 参数
|
||
* @returns {Object} 执行结果
|
||
*/
|
||
AtomicExecutor.prototype.execute = async function(operationName, fn) {
|
||
// 1. 获取执行锁
|
||
var lockResult = executionLock.acquireLock(this.devId, operationName);
|
||
if (!lockResult.success) {
|
||
return lockResult;
|
||
}
|
||
|
||
var executionId = lockResult.executionId;
|
||
var lock = lockResult.lock;
|
||
var devId = this.devId;
|
||
var cpManager = new CheckpointManager(devId, executionId);
|
||
|
||
// 2. 保存初始检查点
|
||
await cpManager.save('执行前状态', {
|
||
type: 'initial',
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
// 3. 构建执行上下文
|
||
var ctx = {
|
||
executionId: executionId,
|
||
|
||
/**
|
||
* 执行单步操作
|
||
* @param {string} stepName - 步骤名称
|
||
* @param {Function} stepFn - 步骤执行函数
|
||
* @returns {*} 步骤执行结果
|
||
*/
|
||
step: async function(stepName, stepFn) {
|
||
executionLock.logStep(devId, {
|
||
message: '⏳ ' + stepName + '...',
|
||
success: false
|
||
});
|
||
|
||
try {
|
||
var result = await stepFn();
|
||
|
||
executionLock.logStep(devId, {
|
||
message: '✅ ' + stepName,
|
||
success: true
|
||
});
|
||
|
||
return result;
|
||
} catch (e) {
|
||
executionLock.logStep(devId, {
|
||
message: '❌ ' + stepName + ': ' + e.message,
|
||
success: false
|
||
});
|
||
throw e;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 保存检查点
|
||
* @param {string} label - 检查点标签
|
||
* @param {Object} data - 快照数据
|
||
* @returns {string} 检查点ID
|
||
*/
|
||
checkpoint: async function(label, data) {
|
||
return await cpManager.save(label, data);
|
||
}
|
||
};
|
||
|
||
// 4. 执行主逻辑
|
||
try {
|
||
var result = await fn(ctx);
|
||
|
||
// 5. 成功:更新锁状态
|
||
lock.state = 'completed';
|
||
lock.result = result;
|
||
|
||
// 延迟释放锁(给前端时间拉取最终状态)
|
||
setTimeout(function() { executionLock.releaseLock(devId); }, LOCK_RELEASE_DELAY_MS);
|
||
|
||
return {
|
||
success: true,
|
||
executionId: executionId,
|
||
result: result,
|
||
reply: result && result.reply ? result.reply : '✅ 操作「' + operationName + '」执行完成。'
|
||
};
|
||
|
||
} catch (e) {
|
||
// 6. 失败:自动回滚
|
||
lock.state = 'rolling_back';
|
||
executionLock.logStep(devId, {
|
||
message: '🔄 执行失败,正在自动回滚...',
|
||
success: false
|
||
});
|
||
|
||
try {
|
||
await cpManager.rollbackTo('cp-1');
|
||
executionLock.logStep(devId, {
|
||
message: '✅ 已回滚到执行前状态',
|
||
success: true
|
||
});
|
||
} catch (rollbackError) {
|
||
executionLock.logStep(devId, {
|
||
message: '⚠️ 回滚异常:' + rollbackError.message + ',已通知管理员',
|
||
success: false
|
||
});
|
||
}
|
||
|
||
lock.state = 'failed';
|
||
lock.error = { message: e.message };
|
||
|
||
setTimeout(function() { executionLock.releaseLock(devId); }, LOCK_RELEASE_DELAY_MS);
|
||
|
||
return {
|
||
success: false,
|
||
executionId: executionId,
|
||
error: e.message,
|
||
reply: '❌ 操作「' + operationName + '」执行失败:' + e.message +
|
||
'\n\n系统已自动回滚到执行前状态,没有数据被修改。'
|
||
};
|
||
}
|
||
};
|
||
|
||
module.exports = { AtomicExecutor: AtomicExecutor };
|