273 lines
9.9 KiB
JavaScript
273 lines
9.9 KiB
JavaScript
const node_schedule = require("node-schedule");
|
||
const config = require('./config.js');
|
||
const deviceManager = require('./deviceManager.js');
|
||
const command = require('./command.js');
|
||
const db = require('../dbProxy');
|
||
/**
|
||
* 定时任务管理器(简化版)
|
||
* 管理所有定时任务的创建和销毁
|
||
*/
|
||
class ScheduledJobs {
|
||
constructor(components, taskHandlers) {
|
||
this.taskQueue = components.taskQueue;
|
||
this.taskHandlers = taskHandlers;
|
||
this.jobs = [];
|
||
}
|
||
|
||
/**
|
||
* 启动所有定时任务
|
||
*/
|
||
start() {
|
||
// 每天凌晨重置统计数据
|
||
const resetJob = node_schedule.scheduleJob(config.schedules.dailyReset, () => {
|
||
this.resetDailyStats();
|
||
});
|
||
this.jobs.push(resetJob);
|
||
|
||
// 启动心跳检查定时任务(每分钟检查一次)
|
||
const monitoringJob = node_schedule.scheduleJob(config.schedules.monitoringInterval, async () => {
|
||
await deviceManager.checkHeartbeatStatus().catch(error => {
|
||
console.error('[定时任务] 检查心跳状态失败:', error);
|
||
});
|
||
});
|
||
|
||
this.jobs.push(monitoringJob);
|
||
|
||
// 启动离线设备任务清理定时任务(每分钟检查一次)
|
||
const cleanupOfflineTasksJob = node_schedule.scheduleJob(config.schedules.monitoringInterval, async () => {
|
||
await this.cleanupOfflineDeviceTasks().catch(error => {
|
||
console.error('[定时任务] 清理离线设备任务失败:', error);
|
||
});
|
||
});
|
||
|
||
this.jobs.push(cleanupOfflineTasksJob);
|
||
console.log('[定时任务] 已启动离线设备任务清理任务');
|
||
|
||
|
||
// 执行自动投递任务
|
||
const autoDeliverJob = node_schedule.scheduleJob(config.schedules.autoDeliver, () => {
|
||
this.autoDeliverTask();
|
||
});
|
||
|
||
// 立即执行一次自动投递任务
|
||
this.autoDeliverTask();
|
||
|
||
this.jobs.push(autoDeliverJob);
|
||
console.log('[定时任务] 已启动自动投递任务');
|
||
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 重置每日统计
|
||
*/
|
||
resetDailyStats() {
|
||
console.log('[定时任务] 重置每日统计数据');
|
||
|
||
try {
|
||
deviceManager.resetAllDailyCounters();
|
||
console.log('[定时任务] 每日统计重置完成');
|
||
} catch (error) {
|
||
console.error('[定时任务] 重置统计失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理过期数据
|
||
*/
|
||
cleanupCaches() {
|
||
console.log('[定时任务] 开始清理过期数据');
|
||
|
||
try {
|
||
deviceManager.cleanupOfflineDevices(config.monitoring.offlineThreshold);
|
||
command.cleanupExpiredCommands(30);
|
||
console.log('[定时任务] 数据清理完成');
|
||
} catch (error) {
|
||
console.error('[定时任务] 数据清理失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理离线设备任务
|
||
* 检查离线超过10分钟的设备,取消其所有pending/running状态的任务
|
||
*/
|
||
async cleanupOfflineDeviceTasks() {
|
||
try {
|
||
const Sequelize = require('sequelize');
|
||
const { device_status, task_status, op } = db.models;
|
||
|
||
// 离线阈值:10分钟
|
||
const offlineThreshold = 10 * 60 * 1000; // 10分钟
|
||
const now = new Date();
|
||
const thresholdTime = new Date(now.getTime() - offlineThreshold);
|
||
|
||
// 查询离线超过10分钟的设备
|
||
const offlineDevices = await device_status.findAll({
|
||
where: {
|
||
isOnline: false,
|
||
lastHeartbeatTime: {
|
||
[op.lt]: thresholdTime
|
||
}
|
||
},
|
||
attributes: ['sn_code', 'lastHeartbeatTime']
|
||
});
|
||
|
||
if (!offlineDevices || offlineDevices.length === 0) {
|
||
return;
|
||
}
|
||
|
||
const offlineSnCodes = offlineDevices.map(dev => dev.sn_code);
|
||
console.log(`[清理离线任务] 发现 ${offlineSnCodes.length} 个离线超过10分钟的设备: ${offlineSnCodes.join(', ')}`);
|
||
|
||
let totalCancelled = 0;
|
||
|
||
// 为每个离线设备取消任务
|
||
for (const sn_code of offlineSnCodes) {
|
||
try {
|
||
// 查询该设备的所有pending/running任务
|
||
const pendingTasks = await task_status.findAll({
|
||
where: {
|
||
sn_code: sn_code,
|
||
status: ['pending', 'running']
|
||
},
|
||
attributes: ['id']
|
||
});
|
||
|
||
if (pendingTasks.length === 0) {
|
||
continue;
|
||
}
|
||
|
||
// 更新任务状态为cancelled
|
||
const updateResult = await task_status.update(
|
||
{
|
||
status: 'cancelled',
|
||
endTime: new Date(),
|
||
result: JSON.stringify({
|
||
reason: '设备离线超过10分钟,任务已自动取消',
|
||
offlineTime: offlineDevices.find(d => d.sn_code === sn_code)?.lastHeartbeatTime
|
||
})
|
||
},
|
||
{
|
||
where: {
|
||
sn_code: sn_code,
|
||
status: ['pending', 'running']
|
||
}
|
||
}
|
||
);
|
||
|
||
const cancelledCount = Array.isArray(updateResult) ? updateResult[0] : updateResult;
|
||
totalCancelled += cancelledCount;
|
||
|
||
// 从内存队列中移除任务
|
||
if (this.taskQueue && typeof this.taskQueue.cancelDeviceTasks === 'function') {
|
||
await this.taskQueue.cancelDeviceTasks(sn_code);
|
||
}
|
||
|
||
console.log(`[清理离线任务] 设备 ${sn_code} 已取消 ${cancelledCount} 个任务`);
|
||
} catch (error) {
|
||
console.error(`[清理离线任务] 取消设备 ${sn_code} 的任务失败:`, error);
|
||
}
|
||
}
|
||
|
||
if (totalCancelled > 0) {
|
||
console.log(`[清理离线任务] 共取消 ${totalCancelled} 个离线设备的任务`);
|
||
}
|
||
} catch (error) {
|
||
console.error('[清理离线任务] 执行失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 自动投递任务
|
||
*/
|
||
async autoDeliverTask() {
|
||
const now = new Date();
|
||
console.log(`[自动投递] ${now.toLocaleString()} 开始执行自动投递任务`);
|
||
|
||
// 检查是否在工作时间
|
||
if (!config.isWorkingHours()) {
|
||
console.log(`[自动投递] 非工作时间,跳过执行`);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 从 device_status 查询所有在线且已登录的设备
|
||
const models = db.models;
|
||
const { device_status, pla_account, op } = models;
|
||
const onlineDevices = await device_status.findAll({
|
||
where: {
|
||
isOnline: true,
|
||
isLoggedIn: true
|
||
},
|
||
attributes: ['sn_code', 'accountName', 'platform']
|
||
});
|
||
|
||
if (!onlineDevices || onlineDevices.length === 0) {
|
||
console.log('[自动投递] 没有在线且已登录的设备');
|
||
return;
|
||
}
|
||
|
||
// 获取这些在线设备对应的账号配置(只获取启用且开启自动投递的账号)
|
||
const snCodes = onlineDevices.map(device => device.sn_code);
|
||
const pla_users = await pla_account.findAll({
|
||
where: {
|
||
sn_code: { [op.in]: snCodes },
|
||
is_delete: 0,
|
||
is_enabled: 1, // 只获取启用的账号
|
||
auto_deliver: 1
|
||
}
|
||
});
|
||
|
||
if (!pla_users || pla_users.length === 0) {
|
||
console.log('[自动投递] 没有启用且开启自动投递的账号');
|
||
return;
|
||
}
|
||
|
||
console.log(`[自动投递] 找到 ${pla_users.length} 个可用账号`);
|
||
|
||
// 为每个设备添加自动投递任务到队列
|
||
for (const pl_user of pla_users) {
|
||
const userData = pl_user.toJSON();
|
||
|
||
|
||
// 检查设备调度策略
|
||
const canExecute = deviceManager.canExecuteOperation(userData.sn_code, 'deliver');
|
||
if (!canExecute.allowed) {
|
||
console.log(`[自动投递] 设备 ${userData.sn_code} 不满足执行条件: ${canExecute.reason}`);
|
||
continue;
|
||
}
|
||
|
||
// 添加自动投递任务到队列
|
||
await this.taskQueue.addTask(userData.sn_code, {
|
||
taskType: 'auto_deliver',
|
||
taskName: `自动投递 - ${userData.keyword || '默认关键词'}`,
|
||
taskParams: {
|
||
keyword: userData.keyword || '',
|
||
platform: userData.platform_type || 'boss',
|
||
pageCount: 3, // 默认值
|
||
maxCount: 10, // 默认值
|
||
filterRules: {
|
||
minSalary: userData.min_salary || 0,
|
||
maxSalary: userData.max_salary || 0,
|
||
keywords: [],
|
||
excludeKeywords: []
|
||
}
|
||
},
|
||
priority: config.getTaskPriority('auto_deliver') || 6
|
||
});
|
||
|
||
console.log(`[自动投递] 已为设备 ${userData.sn_code} 添加自动投递任务,关键词: ${userData.keyword || '默认'}`);
|
||
}
|
||
|
||
console.log('[自动投递] 任务添加完成');
|
||
|
||
} catch (error) {
|
||
console.error('[自动投递] 执行失败:', error);
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = ScheduledJobs;
|
||
|