11
This commit is contained in:
250
api/middleware/schedule/handlers/baseHandler.js
Normal file
250
api/middleware/schedule/handlers/baseHandler.js
Normal file
@@ -0,0 +1,250 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user