Files
autoAiWorkSys/api/middleware/schedule/handlers/baseHandler.js
张成 65833dd32d 11
2025-12-30 15:46:18 +08:00

251 lines
8.0 KiB
JavaScript

const deviceManager = require('../core/deviceManager');
const accountValidator = require('../services/accountValidator');
const db = require('../../dbProxy');
/**
* 任务处理器基类
* 提供通用的授权检查、计时、错误处理、设备记录等功能
*/
class BaseHandler {
constructor(mqttClient) {
this.mqttClient = mqttClient;
}
/**
* 执行任务(带授权检查和错误处理)
* @param {object} task - 任务对象
* @param {Function} businessLogic - 业务逻辑函数
* @param {object} options - 选项
* @returns {Promise<object>} 执行结果
*/
async execute(task, businessLogic, options = {}) {
const {
checkAuth = true, // 是否检查授权
checkOnline = true, // 是否检查在线状态
recordDeviceMetrics = true // 是否记录设备指标
} = options;
const { sn_code, taskName } = task;
const startTime = Date.now();
try {
// 1. 验证账户(启用 + 授权 + 在线)
if (checkAuth || checkOnline) {
const validation = await accountValidator.validate(sn_code, {
checkEnabled: true,
checkAuth,
checkOnline,
offlineThreshold: 3 * 60 * 1000 // 3分钟
});
if (!validation.valid) {
throw new Error(`设备 ${sn_code} 验证失败: ${validation.reason}`);
}
}
// 2. 记录任务开始
if (recordDeviceMetrics) {
deviceManager.recordTaskStart(sn_code, task);
}
// 3. 执行业务逻辑
const result = await businessLogic();
// 4. 记录任务成功
const duration = Date.now() - startTime;
if (recordDeviceMetrics) {
deviceManager.recordTaskComplete(sn_code, task, true, duration);
}
return {
success: true,
duration,
...result
};
} catch (error) {
// 5. 记录任务失败
const duration = Date.now() - startTime;
if (recordDeviceMetrics) {
deviceManager.recordTaskComplete(sn_code, task, false, duration);
}
console.error(`[${taskName}] 执行失败 (设备: ${sn_code}):`, error.message);
return {
success: false,
error: error.message,
duration
};
}
}
/**
* 检查每日操作限制
* @param {string} sn_code - 设备序列号
* @param {string} operation - 操作类型 (search, deliver, chat)
* @param {string} platform - 平台类型
* @returns {Promise<{allowed: boolean, count?: number, limit?: number, reason?: string}>}
*/
async checkDailyLimit(sn_code, operation, platform = 'boss') {
try {
const today = new Date().toISOString().split('T')[0];
const task_status = db.getModel('task_status');
// 查询今日该操作的完成次数
const count = await task_status.count({
where: {
sn_code,
taskType: `auto_${operation}`,
status: 'completed',
endTime: {
[db.models.op.gte]: new Date(today)
}
}
});
// 获取每日限制(从 deviceManager 或配置)
const limit = deviceManager.canExecuteOperation(sn_code, operation);
if (!limit.allowed) {
return {
allowed: false,
count,
reason: limit.reason
};
}
return {
allowed: true,
count,
limit: limit.max || 999
};
} catch (error) {
console.error(`[每日限制检查] 失败 (${sn_code}, ${operation}):`, error);
return { allowed: true }; // 检查失败时默认允许
}
}
/**
* 检查执行间隔时间
* @param {string} sn_code - 设备序列号
* @param {string} taskType - 任务类型
* @param {number} intervalMinutes - 间隔时间(分钟)
* @returns {Promise<{allowed: boolean, elapsed?: number, remaining?: number, reason?: string}>}
*/
async checkInterval(sn_code, taskType, intervalMinutes) {
try {
const task_status = db.getModel('task_status');
// 查询最近一次成功完成的任务
const lastTask = await task_status.findOne({
where: {
sn_code,
taskType,
status: 'completed'
},
order: [['endTime', 'DESC']],
attributes: ['endTime']
});
if (!lastTask || !lastTask.endTime) {
return { allowed: true, elapsed: null };
}
const now = Date.now();
const lastTime = new Date(lastTask.endTime).getTime();
const elapsed = now - lastTime;
const intervalMs = intervalMinutes * 60 * 1000;
if (elapsed < intervalMs) {
const remainingMinutes = Math.ceil((intervalMs - elapsed) / (60 * 1000));
const elapsedMinutes = Math.floor(elapsed / (60 * 1000));
return {
allowed: false,
elapsed: elapsedMinutes,
remaining: remainingMinutes,
reason: `距离上次执行仅 ${elapsedMinutes} 分钟,还需等待 ${remainingMinutes} 分钟`
};
}
return {
allowed: true,
elapsed: Math.floor(elapsed / (60 * 1000))
};
} catch (error) {
console.error(`[间隔检查] 失败 (${sn_code}, ${taskType}):`, error);
return { allowed: true }; // 检查失败时默认允许
}
}
/**
* 获取账户配置
* @param {string} sn_code - 设备序列号
* @param {string[]} fields - 需要的字段
* @returns {Promise<object|null>}
*/
async getAccountConfig(sn_code, fields = ['*']) {
try {
const pla_account = db.getModel('pla_account');
const account = await pla_account.findOne({
where: { sn_code, is_delete: 0 },
attributes: fields
});
return account ? account.toJSON() : null;
} catch (error) {
console.error(`[获取账户配置] 失败 (${sn_code}):`, error);
return null;
}
}
/**
* 推送设备工作状态(可选的通知)
* @param {string} sn_code - 设备序列号
* @param {object} status - 状态信息
*/
async notifyDeviceStatus(sn_code, status) {
try {
const deviceWorkStatusNotifier = require('../notifiers/deviceWorkStatusNotifier');
await deviceWorkStatusNotifier.sendDeviceWorkStatus(sn_code, status);
} catch (error) {
console.warn(`[状态推送] 失败 (${sn_code}):`, error.message);
}
}
/**
* 标准化错误响应
* @param {Error} error - 错误对象
* @param {string} sn_code - 设备序列号
* @returns {object} 标准化的错误响应
*/
formatError(error, sn_code) {
return {
success: false,
error: error.message || '未知错误',
sn_code,
timestamp: new Date().toISOString()
};
}
/**
* 标准化成功响应
* @param {object} data - 响应数据
* @param {string} sn_code - 设备序列号
* @returns {object} 标准化的成功响应
*/
formatSuccess(data, sn_code) {
return {
success: true,
sn_code,
timestamp: new Date().toISOString(),
...data
};
}
}
module.exports = BaseHandler;