251 lines
8.0 KiB
JavaScript
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;
|