1
This commit is contained in:
265
api/middleware/schedule/utils.js
Normal file
265
api/middleware/schedule/utils.js
Normal file
@@ -0,0 +1,265 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user