diff --git a/api/controller_front/task.js b/api/controller_front/task.js new file mode 100644 index 0000000..24d64bf --- /dev/null +++ b/api/controller_front/task.js @@ -0,0 +1,211 @@ +const Framework = require("../../framework/node-core-framework.js"); + +/** + * 任务管理控制器(客户端接口) + * 提供客户端调用的任务相关接口 + */ +module.exports = { + /** + * @swagger + * /api/task/current: + * get: + * summary: 获取当前执行的任务 + * description: 根据设备SN码获取当前正在执行的任务信息 + * tags: [前端-任务管理] + * parameters: + * - in: query + * name: sn_code + * required: true + * schema: + * type: string + * description: 设备SN码 + * example: 'GHJU' + * responses: + * 200: + * description: 获取成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * code: + * type: integer + * description: 状态码,0表示成功 + * example: 0 + * message: + * type: string + * description: 响应消息 + * example: 'success' + * data: + * type: object + * nullable: true + * description: 当前任务信息,null表示没有正在执行的任务 + * properties: + * taskId: + * type: integer + * description: 任务ID + * taskName: + * type: string + * description: 任务名称 + * taskType: + * type: string + * description: 任务类型 + * status: + * type: string + * description: 任务状态(running-执行中) + * progress: + * type: integer + * description: 任务进度(0-100) + * currentStep: + * type: string + * description: 当前步骤 + * startTime: + * type: string + * description: 开始时间 + */ + 'GET /task/current': async (ctx) => { + try { { + const { sn_code } = ctx.query || {}; + + if (!sn_code) { + return ctx.fail('请提供设备SN码'); + } + + const { task_status, op } = await Framework.getModels(); + + // 查询当前正在执行的任务(status = 'running') + const currentTask = await task_status.findOne({ + where: { + sn_code: sn_code, + status: 'running' + }, + order: [['id', 'DESC']] // 获取最新的任务 + }); + + if (!currentTask) { + return ctx.success(null, '暂无执行中的任务'); + } + + const taskData = currentTask.toJSON(); + + return ctx.success({ + taskId: taskData.id, + taskName: taskData.taskName || taskData.task_name || taskData.taskType || taskData.task_type || '未知任务', + taskType: taskData.taskType || taskData.task_type, + status: 'running', + progress: taskData.progress || 0, + currentStep: taskData.currentStep || taskData.current_step || '', + startTime: taskData.startTime || taskData.start_time || taskData.created_time + }); + } catch (error) { + console.error('[任务管理] 获取当前任务失败:', error); + return ctx.fail('获取当前任务失败: ' + (error.message || '未知错误')); + } + }, + + /** + * @swagger + * /api/task/pending: + * get: + * summary: 获取待执行的任务列表 + * description: 根据设备SN码获取待执行的任务列表 + * tags: [前端-任务管理] + * parameters: + * - in: query + * name: sn_code + * required: true + * schema: + * type: string + * description: 设备SN码 + * example: 'GHJU' + * - in: query + * name: limit + * required: false + * schema: + * type: integer + * default: 10 + * description: 返回数量限制 + * responses: + * 200: + * description: 获取成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * code: + * type: integer + * description: 状态码,0表示成功 + * example: 0 + * message: + * type: string + * description: 响应消息 + * example: 'success' + * data: + * type: array + * description: 待执行任务列表 + * items: + * type: object + * properties: + * taskId: + * type: integer + * description: 任务ID + * taskName: + * type: string + * description: 任务名称 + * taskType: + * type: string + * description: 任务类型 + * status: + * type: string + * description: 任务状态(pending-待执行) + * scheduledTime: + * type: string + * description: 计划执行时间 + * priority: + * type: integer + * description: 优先级 + */ + 'GET /task/pending': async (ctx) => { + try { + const { sn_code, limit = 10 } = ctx.query || {}; + + if (!sn_code) { + return ctx.fail('请提供设备SN码'); + } + + const { task_status, op } = await Framework.getModels(); + + // 查询待执行的任务(status = 'pending') + const pendingTasks = await task_status.findAll({ + where: { + sn_code: sn_code, + status: 'pending' + }, + order: [ + ['priority', 'DESC'], // 按优先级降序 + ['id', 'ASC'] // 同优先级按ID升序 + ], + limit: parseInt(limit) || 10 + }); + + const taskList = pendingTasks.map(task => { + const taskData = task.toJSON(); + return { + taskId: taskData.id, + taskName: taskData.taskName || taskData.task_name || taskData.taskType || taskData.task_type || '未知任务', + taskType: taskData.taskType || taskData.task_type, + status: 'pending', + scheduledTime: taskData.scheduledTime || taskData.scheduled_time || taskData.created_time, + priority: taskData.priority || 0 + }; + }); + + return ctx.success(taskList); + } catch (error) { + console.error('[任务管理] 获取待执行任务失败:', error); + return ctx.fail('获取待执行任务失败: ' + (error.message || '未知错误')); + } + } +}; + diff --git a/api/middleware/schedule/taskQueue.js b/api/middleware/schedule/taskQueue.js index 8130de4..0d5ba78 100644 --- a/api/middleware/schedule/taskQueue.js +++ b/api/middleware/schedule/taskQueue.js @@ -582,6 +582,16 @@ class TaskQueue { { where: { id: task.id } } ); + // 通知客户端任务状态变更 + await this.notifyTaskStatusChange(task.sn_code, { + taskId: task.id, + taskName: task.taskName, + taskType: task.taskType, + status: 'running', + progress: 0, + startTime: task.startTime + }); + // 使用注册的任务处理器执行任务 const handler = this.taskHandlers.get(task.taskType); if (!handler) { @@ -617,6 +627,16 @@ class TaskQueue { { where: { id: task.id } } ); + // 通知客户端任务状态变更 + await this.notifyTaskStatusChange(task.sn_code, { + taskId: task.id, + taskName: task.taskName, + taskType: task.taskType, + status: 'completed', + progress: 100, + endTime: task.endTime + }); + console.log(`[任务队列] 设备 ${task.sn_code} 任务执行成功: ${task.taskName} (耗时: ${task.duration}ms)`); } catch (error) { @@ -650,6 +670,17 @@ class TaskQueue { }, { where: { id: task.id } } ); + + // 通知客户端任务状态变更 + await this.notifyTaskStatusChange(task.sn_code, { + taskId: task.id, + taskName: task.taskName, + taskType: task.taskType, + status: 'failed', + progress: 0, + errorMessage: task.errorMessage, + endTime: task.endTime + }); } catch (dbError) { console.error(`[任务队列] 更新任务失败状态到数据库失败:`, dbError); } @@ -994,6 +1025,35 @@ class TaskQueue { return null; } } + + /** + * 通知客户端任务状态变更 + * @param {string} sn_code - 设备SN码 + * @param {object} taskData - 任务数据 + */ + async notifyTaskStatusChange(sn_code, taskData) { + try { + const mqttClient = await this.getMqttClient(); + if (!mqttClient) { + return; // MQTT客户端不可用,静默失败 + } + + // 通过MQTT发布任务状态变更通知 + // 主题格式: task_status_{sn_code} + const topic = `task_status_${sn_code}`; + const message = JSON.stringify({ + action: 'task_status_update', + data: taskData, + timestamp: new Date().toISOString() + }); + + await mqttClient.publish(topic, message); + console.log(`[任务队列] 已通知客户端任务状态变更: ${sn_code} - ${taskData.taskName || taskData.taskType || '未知任务'} (${taskData.status})`); + } catch (error) { + // 通知失败不影响任务执行,只记录日志 + console.warn(`[任务队列] 通知客户端任务状态变更失败:`, error.message); + } + } } // 导出单例