const dayjs = require('dayjs'); /** * 调度系统工具函数 * 提供通用的辅助功能 */ class ScheduleUtils { /** * 生成唯一任务ID * @returns {string} 任务ID */ static generateTaskId() { return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * 生成唯一指令ID * @returns {string} 指令ID */ static generateCommandId() { return `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * 格式化时间戳 * @param {number} timestamp - 时间戳 * @returns {string} 格式化的时间 */ static formatTimestamp(timestamp) { return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss'); } /** * 格式化持续时间 * @param {number} ms - 毫秒数 * @returns {string} 格式化的持续时间 */ static formatDuration(ms) { if (ms < 1000) { return `${ms}ms`; } else if (ms < 60 * 1000) { return `${(ms / 1000).toFixed(1)}s`; } else if (ms < 60 * 60 * 1000) { return `${(ms / (60 * 1000)).toFixed(1)}min`; } else { return `${(ms / (60 * 60 * 1000)).toFixed(1)}h`; } } /** * 深度克隆对象 * @param {object} obj - 要克隆的对象 * @returns {object} 克隆后的对象 */ static deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof Array) { return obj.map(item => this.deepClone(item)); } const cloned = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = this.deepClone(obj[key]); } } return cloned; } /** * 安全解析JSON * @param {string} jsonString - JSON字符串 * @param {any} defaultValue - 默认值 * @returns {any} 解析结果或默认值 */ static safeJsonParse(jsonString, defaultValue = null) { try { return JSON.parse(jsonString); } catch (error) { return defaultValue; } } /** * 安全序列化JSON * @param {any} obj - 要序列化的对象 * @param {string} defaultValue - 默认值 * @returns {string} JSON字符串或默认值 */ static safeJsonStringify(obj, defaultValue = '{}') { try { return JSON.stringify(obj); } catch (error) { return defaultValue; } } /** * 延迟执行 * @param {number} ms - 延迟毫秒数 * @returns {Promise} Promise对象 */ static delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 重试执行函数 * @param {function} fn - 要执行的函数 * @param {number} maxRetries - 最大重试次数 * @param {number} delay - 重试延迟(毫秒) * @returns {Promise} 执行结果 */ static async retry(fn, maxRetries = 3, delay = 1000) { let lastError; for (let i = 0; i <= maxRetries; i++) { try { return await fn(); } catch (error) { lastError = error; if (i < maxRetries) { console.log(`[工具函数] 执行失败,${delay}ms后重试 (${i + 1}/${maxRetries + 1}):`, error.message); await this.delay(delay); } } } throw lastError; } /** * 限制并发执行数量 * @param {Array} tasks - 任务数组 * @param {number} concurrency - 并发数量 * @returns {Promise} 执行结果数组 */ static async limitConcurrency(tasks, concurrency = 5) { const results = []; const executing = []; for (const task of tasks) { const promise = Promise.resolve(task()).then(result => { executing.splice(executing.indexOf(promise), 1); return result; }); results.push(promise); if (tasks.length >= concurrency) { executing.push(promise); if (executing.length >= concurrency) { await Promise.race(executing); } } } return Promise.all(results); } /** * 创建带超时的Promise * @param {Promise} promise - 原始Promise * @param {number} timeout - 超时时间(毫秒) * @param {string} timeoutMessage - 超时错误消息 * @returns {Promise} 带超时的Promise */ static withTimeout(promise, timeout, timeoutMessage = 'Operation timed out') { return Promise.race([ promise, new Promise((_, reject) => { setTimeout(() => reject(new Error(timeoutMessage)), timeout); }) ]); } /** * 获取今天的日期字符串 * @returns {string} 日期字符串 YYYY-MM-DD */ static getTodayString() { return dayjs().format('YYYY-MM-DD'); } /** * 检查日期是否为今天 * @param {string|Date} date - 日期 * @returns {boolean} 是否为今天 */ static isToday(date) { return dayjs(date).format('YYYY-MM-DD') === this.getTodayString(); } /** * 获取随机延迟时间 * @param {number} min - 最小延迟(毫秒) * @param {number} max - 最大延迟(毫秒) * @returns {number} 随机延迟时间 */ static getRandomDelay(min = 1000, max = 5000) { return Math.floor(Math.random() * (max - min + 1)) + min; } /** * 格式化文件大小 * @param {number} bytes - 字节数 * @returns {string} 格式化的文件大小 */ static formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } /** * 计算成功率 * @param {number} success - 成功次数 * @param {number} total - 总次数 * @returns {string} 百分比字符串 */ static calculateSuccessRate(success, total) { if (total === 0) return '0%'; return ((success / total) * 100).toFixed(2) + '%'; } /** * 验证设备SN码格式 * @param {string} sn_code - 设备SN码 * @returns {boolean} 是否有效 */ static isValidSnCode(sn_code) { return typeof sn_code === 'string' && sn_code.length > 0 && sn_code.length <= 50; } /** * 清理对象中的空值 * @param {object} obj - 要清理的对象 * @returns {object} 清理后的对象 */ static cleanObject(obj) { const cleaned = {}; for (const key in obj) { if (obj.hasOwnProperty(key) && obj[key] !== null && obj[key] !== undefined && obj[key] !== '') { cleaned[key] = obj[key]; } } return cleaned; } } module.exports = ScheduleUtils;