zhizhi/backend/api-server/middleware/execution-lock.js

171 lines
4.2 KiB
JavaScript
Raw Normal View History

/**
* 执行锁中间件 · L2 API 执行拦截层
*
* 当一个操作开始执行时
* 1. 为该操作创建一个执行上下文executionId
* 2. 锁定相关资源防止并发冲突
* 3. 拒绝一切 cancel/abort/rollback 请求
* 4. 执行完成后自动释放锁
*
* 同一个开发者不能同时执行多个写入操作
* 版权国作登字-2026-A-00037559
*/
'use strict';
var crypto = require('crypto');
// 内存中的执行锁表
var activeLocks = new Map();
function generateExecutionId() {
return 'exec-' + Date.now() + '-' + crypto.randomBytes(4).toString('hex');
}
/**
* 获取执行锁表引用供看门狗使用
*/
function getActiveLocks() {
return activeLocks;
}
/**
* 创建执行锁
* @param {string} devId - 开发者编号
* @param {string} operation - 操作描述
* @returns {Object} { success, executionId, lock } { success: false, reply }
*/
function acquireLock(devId, operation) {
if (activeLocks.has(devId)) {
var existing = activeLocks.get(devId);
if (existing.state === 'running') {
return {
success: false,
reply: '⏳ 你有一个操作正在执行中(' + existing.operation +
'),请等待完成。\n\n系统不允许同时执行多个操作——这是为了保护数据一致性。'
};
}
}
var executionId = generateExecutionId();
var lock = {
executionId: executionId,
devId: devId,
operation: operation,
startTime: Date.now(),
state: 'running',
steps: [],
checkpoints: [],
warned: false,
completedSteps: 0,
totalSteps: 0,
result: null,
error: null
};
activeLocks.set(devId, lock);
return {
success: true,
executionId: executionId,
lock: lock
};
}
/**
* 释放执行锁
*/
function releaseLock(devId) {
activeLocks.delete(devId);
}
/**
* 获取执行状态通过 executionId
*/
function getExecutionStatus(executionId) {
for (var entry of activeLocks) {
if (entry[1].executionId === executionId) {
return entry[1];
}
}
return null;
}
/**
* 记录执行步骤用于事后回放
*/
function logStep(devId, step) {
var lock = activeLocks.get(devId);
if (lock) {
lock.steps.push({
message: step.message,
success: step.success,
timestamp: new Date().toISOString()
});
if (step.success) {
lock.completedSteps++;
}
}
}
/**
* 保存检查点用于失败时回滚
*/
function saveCheckpoint(devId, checkpoint) {
var lock = activeLocks.get(devId);
if (lock) {
lock.checkpoints.push({
id: checkpoint.id,
label: checkpoint.label,
data: checkpoint.data,
timestamp: new Date().toISOString()
});
}
}
/**
* 取消拦截中间件
* 拦截所有试图取消执行中操作的请求
*/
function blockCancellation(req, res, next) {
var devId = req.user ? req.user.devId : null;
if (!devId) return next();
var isCancelAttempt =
req.path.includes('/cancel') ||
req.path.includes('/abort') ||
req.path.includes('/rollback');
var isDeleteOnLocked = req.method === 'DELETE' && activeLocks.has(devId);
if (isCancelAttempt || isDeleteOnLocked) {
var lock = activeLocks.get(devId);
if (lock && lock.state === 'running') {
return res.status(423).json({
error: true,
code: 'EXECUTION_LOCKED',
locked: true,
executionId: lock.executionId,
reply: '🔒 操作「' + lock.operation + '」正在执行中,不可取消。\n\n' +
'系统已接收你的指令并开始执行。这条执行链路涉及多个自动化流程的联动,' +
'中途中断会导致数据不一致和级联故障。\n\n' +
'⏳ 请等待执行完成。完成后你会看到完整的执行结果。\n\n' +
'如果你对结果不满意,可以说「前面的没想好,我们重新来」——' +
'系统会在已有结果的基础上重新迭代,而不是撤回已执行的操作。'
});
}
}
next();
}
module.exports = {
acquireLock: acquireLock,
releaseLock: releaseLock,
getExecutionStatus: getExecutionStatus,
getActiveLocks: getActiveLocks,
logStep: logStep,
saveCheckpoint: saveCheckpoint,
blockCancellation: blockCancellation
};