171 lines
4.2 KiB
JavaScript
171 lines
4.2 KiB
JavaScript
|
|
/**
|
|||
|
|
* 执行锁中间件 · 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
|
|||
|
|
};
|