Files
autoAiWorkSys/api/middleware/schedule/deviceWorkStatusNotifier.js
张成 735fa0c621 1
2025-12-17 17:30:03 +08:00

254 lines
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 设备工作状态推送服务
* 负责向客户端推送设备当前工作状态(任务、指令等)
*/
const db = require('../dbProxy');
class DeviceWorkStatusNotifier {
constructor() {
this.mqttClient = null;
}
/**
* 设置 MQTT 客户端
* @param {object} mqttClient - MQTT 客户端实例
*/
setMqttClient(mqttClient) {
this.mqttClient = mqttClient;
}
/**
* 获取 MQTT 客户端
* @returns {Promise<object>} MQTT 客户端实例
*/
async getMqttClient() {
if (this.mqttClient) {
return this.mqttClient;
}
// 从调度系统获取 MQTT 客户端(与 taskQueue 保持一致)
try {
const scheduleManager = require('./index');
if (scheduleManager.mqttClient) {
return scheduleManager.mqttClient;
}
} catch (error) {
// 调度系统未初始化
}
return null;
}
/**
* 发送设备工作状态(统一方法,包含任务和指令状态)
* @param {string} sn_code - 设备SN码
* @param {object} taskStatusSummary - 任务状态摘要(从 taskQueue.getTaskStatusSummary 获取)
* @param {object} options - 可选参数
* @param {object} options.currentCommand - 当前执行的指令信息(可选)
*/
async sendDeviceWorkStatus(sn_code, taskStatusSummary, options = {}) {
try {
const mqttClient = await this.getMqttClient();
if (!mqttClient) {
return; // MQTT客户端不可用静默失败
}
// 构建设备工作状态数据
const workStatus = {
sn_code: taskStatusSummary.sn_code || sn_code,
timestamp: new Date().toISOString(),
// 当前活动(任务或指令)
currentActivity: null,
// 待执行队列
pendingQueue: {
count: taskStatusSummary.pendingCount || 0,
totalCount: taskStatusSummary.totalPendingCount || 0,
nextExecuteTime: taskStatusSummary.nextTaskTime ? new Date(taskStatusSummary.nextTaskTime).toISOString() : null,
tasks: taskStatusSummary.pendingTasks || []
},
// 设备状态(从 deviceManager 获取)
deviceStatus: {
isOnline: true, // 默认在线,实际应从 deviceManager 获取
workMode: 'auto'
}
};
// 如果有当前执行的指令,优先显示指令状态
if (options.currentCommand) {
const cmd = options.currentCommand;
workStatus.currentActivity = {
type: 'command',
id: cmd.command_id || cmd.id,
name: cmd.command_name || cmd.name || '执行指令',
description: this._formatCommandDescription(cmd),
status: 'running',
progress: cmd.progress || 0,
currentStep: cmd.currentStep || '',
startTime: cmd.startTime || new Date().toISOString()
};
}
// 如果有当前执行的任务,显示任务状态
else if (taskStatusSummary.currentTask) {
const task = taskStatusSummary.currentTask;
workStatus.currentActivity = {
type: 'task',
id: task.taskId,
name: task.taskName || task.taskType || '未知任务',
description: this._formatTaskDescription(task),
status: task.status || 'running',
progress: task.progress || 0,
currentStep: task.currentStep || '',
startTime: task.startTime ? new Date(task.startTime).toISOString() : new Date().toISOString()
};
}
// 格式化显示文案(服务端统一处理,客户端直接显示)
workStatus.displayText = this._formatDisplayText(workStatus);
// 格式化下次执行时间显示文案
if (workStatus.pendingQueue.nextExecuteTime) {
const nextTime = new Date(workStatus.pendingQueue.nextExecuteTime);
const now = new Date();
const diff = nextTime - now;
if (diff < 0) {
workStatus.pendingQueue.nextExecuteTimeText = '即将执行';
} else if (diff < 60000) {
workStatus.pendingQueue.nextExecuteTimeText = `${Math.floor(diff / 1000)}秒后`;
} else if (diff < 3600000) {
workStatus.pendingQueue.nextExecuteTimeText = `${Math.floor(diff / 60000)}分钟后`;
} else if (diff < 86400000) {
workStatus.pendingQueue.nextExecuteTimeText = `${Math.floor(diff / 3600000)}小时后`;
} else {
workStatus.pendingQueue.nextExecuteTimeText = nextTime.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
} else {
workStatus.pendingQueue.nextExecuteTimeText = '暂无';
}
// 通过MQTT发布设备工作状态
// 主题格式: device_work_status_{sn_code}
const topic = `device_work_status_${sn_code}`;
const message = JSON.stringify({
action: 'device_work_status',
data: workStatus,
timestamp: new Date().toISOString()
});
await mqttClient.publish(topic, message);
// 输出日志
console.log(`[设备工作状态] 已推送到 ${sn_code}: ${workStatus.displayText}`);
} catch (error) {
// 通知失败不影响任务执行,只记录日志
console.warn(`[设备工作状态] 推送失败:`, error.message);
}
}
/**
* 格式化任务描述
* @private
*/
_formatTaskDescription(task) {
if (task.jobTitle || task.companyName) {
const jobInfo = [];
if (task.jobTitle) {
jobInfo.push(task.jobTitle);
}
if (task.companyName) {
const companyName = task.companyName.length > 20
? task.companyName.substring(0, 20) + '...'
: task.companyName;
jobInfo.push(companyName);
}
if (jobInfo.length > 0) {
return `投递职位: ${jobInfo.join(' @ ')}`;
}
}
return task.taskName || task.taskType || '未知任务';
}
/**
* 格式化指令描述
* @private
*/
_formatCommandDescription(command) {
const params = command.command_params || command.params || {};
let parsedParams = {};
if (typeof params === 'string') {
try {
parsedParams = JSON.parse(params);
} catch (e) {
// 解析失败,忽略
}
} else {
parsedParams = params;
}
// 根据指令类型格式化描述
const commandType = command.command_type || command.type || '';
const commandName = command.command_name || command.name || '';
if (parsedParams.jobTitle && parsedParams.companyName) {
const companyName = parsedParams.companyName.length > 20
? parsedParams.companyName.substring(0, 20) + '...'
: parsedParams.companyName;
return `投递职位: ${parsedParams.jobTitle} @ ${companyName}`;
} else if (parsedParams.jobTitle) {
return `投递职位: ${parsedParams.jobTitle}`;
} else if (commandType === 'applyJob' || commandName.includes('投递')) {
return '投递简历';
} else if (commandType === 'searchJobs' || commandName.includes('搜索')) {
return `搜索职位: ${parsedParams.keyword || '默认关键词'}`;
} else if (commandType === 'sendChatMessage' || commandName.includes('沟通')) {
return '发送消息';
} else if (commandName) {
return commandName;
}
return '执行指令';
}
/**
* 格式化整体显示文案(服务端统一处理,客户端直接显示)
* @private
*/
_formatDisplayText(workStatus) {
const parts = [];
// 当前活动
if (workStatus.currentActivity) {
const activity = workStatus.currentActivity;
const typeText = activity.type === 'command' ? '指令' : '任务';
const statusText = activity.status === 'running' ? '执行中' :
activity.status === 'completed' ? '已完成' :
activity.status === 'failed' ? '失败' : '未知';
parts.push(`${typeText}: ${activity.description || activity.name} (${statusText}, 进度: ${activity.progress}%)`);
} else {
parts.push('当前活动: 无');
}
// 待执行数量
parts.push(`待执行: ${workStatus.pendingQueue.totalCount}`);
// 下次执行时间
parts.push(`下次执行: ${workStatus.pendingQueue.nextExecuteTimeText || '暂无'}`);
return parts.join(' | ');
}
}
// 导出单例
const deviceWorkStatusNotifier = new DeviceWorkStatusNotifier();
module.exports = deviceWorkStatusNotifier;