Files
autoAiWorkSys/api/middleware/schedule/utils.js
张成 5d7444cd65 1
2025-11-24 13:23:42 +08:00

265 lines
7.1 KiB
JavaScript

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<Array>} 执行结果数组
*/
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;