/** * 设备工作状态推送服务 * 负责向客户端推送设备当前工作状态(任务、指令等) */ const db = require('../dbProxy'); class DeviceWorkStatusNotifier { constructor() { this.mqttClient = null; } /** * 设置 MQTT 客户端 * @param {object} mqttClient - MQTT 客户端实例 */ setMqttClient(mqttClient) { this.mqttClient = mqttClient; } /** * 获取 MQTT 客户端 * @returns {Promise} 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' } }; // 如果有当前执行的指令,优先显示指令状态 // 优先从 options 中获取,如果没有则从 taskStatusSummary 中获取 const currentCommand = options.currentCommand || taskStatusSummary.currentCommand; if (currentCommand) { const cmd = 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, startTime: cmd.startTime || cmd.start_time || new Date().toISOString(), commandType: cmd.command_type || cmd.type || '', commandParams: cmd.command_params || cmd.params || {} }; } // 如果有当前执行的任务,显示任务状态 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() }; } // 如果有等待消息(投递间隔等待等),添加到工作状态 if (options.waitingMessage) { workStatus.waitingMessage = options.waitingMessage; } // 格式化显示文案(服务端统一处理,客户端直接显示) 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 messagePayload = { action: 'device_work_status', data: workStatus, timestamp: new Date().toISOString() }; const message = JSON.stringify(messagePayload); await mqttClient.publish(topic, message); if (workStatus.currentActivity) { const activity = workStatus.currentActivity; const activityInfo = activity.type === 'command' ? `指令[${activity.name}]` : `任务[${activity.name}]`; console.log(` - 当前活动: ${activityInfo}`); } else { console.log(` - 当前活动: 无`); } if (workStatus.waitingMessage) { console.log(` - 等待消息: ${workStatus.waitingMessage.message}`); } console.log(` - 待执行: ${workStatus.pendingQueue.totalCount}个`); console.log(` - 下次执行: ${workStatus.pendingQueue.nextExecuteTimeText}`); console.log(` - 消息大小: ${message.length} 字节`); } 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' ? '失败' : '未知'; // 使用 description 或 name,避免重复显示 // 如果 description 存在且不等于 name,优先使用 description // 否则,使用 name 并可能添加 commandType let activityDesc = ''; if (activity.description && activity.description !== activity.name) { // 有独立的 description,直接使用,不再添加 commandType activityDesc = activity.description; } else { // 使用 name activityDesc = activity.name; // 对于指令,如果 commandType 存在且与 name 不同,添加详细信息 if (activity.type === 'command') { const cmdType = activity.commandType || ''; if (cmdType && cmdType !== activity.name && !activity.name.includes(cmdType)) { activityDesc = `${activityDesc} [${cmdType}]`; } } } parts.push(`${typeText}: ${activityDesc} (${statusText}${activity.progress > 0 ? `, 进度: ${activity.progress}%` : ''})`); } else { parts.push('当前活动: 无'); } // 如果有等待消息,添加到显示文本 if (workStatus.waitingMessage) { parts.push(workStatus.waitingMessage.message); } // 待执行数量 parts.push(`待执行: ${workStatus.pendingQueue.totalCount}个`); // 下次执行时间 parts.push(`下次执行: ${workStatus.pendingQueue.nextExecuteTimeText || '暂无'}`); return parts.join(' | '); } } // 导出单例 const deviceWorkStatusNotifier = new DeviceWorkStatusNotifier(); module.exports = deviceWorkStatusNotifier;