llm-chat/backend/utils/helpers.js

327 lines
6.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
/**
* 生成随机字符串
*/
function generateRandomString(length = 32, charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') {
let result = '';
for (let i = 0; i < length; i++) {
result += charset.charAt(Math.floor(Math.random() * charset.length));
}
return result;
}
/**
* 生成UUID
*/
function generateUUID() {
return crypto.randomUUID();
}
/**
* 生成MD5哈希
*/
function generateMD5(data) {
return crypto.createHash('md5').update(data).digest('hex');
}
/**
* 生成SHA256哈希
*/
function generateSHA256(data) {
return crypto.createHash('sha256').update(data).digest('hex');
}
/**
* 延迟函数
*/
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 深度克隆对象
*/
function 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 => deepClone(item));
}
if (typeof obj === 'object') {
const clonedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
}
/**
* 格式化文件大小
*/
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
/**
* 格式化时间差
*/
function formatTimeDiff(startTime, endTime = Date.now()) {
const diff = endTime - startTime;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}${hours % 24}小时`;
if (hours > 0) return `${hours}小时${minutes % 60}分钟`;
if (minutes > 0) return `${minutes}分钟${seconds % 60}`;
return `${seconds}`;
}
/**
* 验证邮箱格式
*/
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
/**
* 验证URL格式
*/
function isValidURL(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
/**
* 清理字符串移除HTML标签和特殊字符
*/
function sanitizeString(str) {
if (typeof str !== 'string') return '';
return str
.replace(/<[^>]*>/g, '') // 移除HTML标签
.replace(/[<>'"&]/g, '') // 移除危险字符
.trim();
}
/**
* 截断字符串
*/
function truncateString(str, maxLength, suffix = '...') {
if (typeof str !== 'string') return '';
if (str.length <= maxLength) return str;
return str.substring(0, maxLength - suffix.length) + suffix;
}
/**
* 检查文件是否存在
*/
function fileExists(filePath) {
try {
return fs.existsSync(filePath);
} catch {
return false;
}
}
/**
* 确保目录存在
*/
function ensureDirectoryExists(dirPath) {
try {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
return true;
} catch (error) {
console.error('创建目录失败:', error);
return false;
}
}
/**
* 读取JSON文件
*/
function readJSONFile(filePath) {
try {
const data = fs.readFileSync(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('读取JSON文件失败:', error);
return null;
}
}
/**
* 写入JSON文件
*/
function writeJSONFile(filePath, data) {
try {
const jsonString = JSON.stringify(data, null, 2);
fs.writeFileSync(filePath, jsonString, 'utf8');
return true;
} catch (error) {
console.error('写入JSON文件失败:', error);
return false;
}
}
/**
* 获取文件扩展名
*/
function getFileExtension(filename) {
return path.extname(filename).toLowerCase();
}
/**
* 获取文件名(不含扩展名)
*/
function getFileNameWithoutExtension(filename) {
return path.basename(filename, path.extname(filename));
}
/**
* 转换为驼峰命名
*/
function toCamelCase(str) {
return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
}
/**
* 转换为kebab命名
*/
function toKebabCase(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* 防抖函数
*/
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
/**
* 节流函数
*/
function throttle(func, limit) {
let inThrottle;
return function executedFunction(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
/**
* 重试函数
*/
async function retry(fn, maxAttempts = 3, delay = 1000) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt === maxAttempts) {
throw lastError;
}
console.warn(`尝试 ${attempt} 失败,${delay}ms后重试:`, error.message);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
/**
* 并行执行函数并限制并发数
*/
async function parallelLimit(tasks, limit = 5) {
const results = [];
const executing = [];
for (const task of tasks) {
const promise = Promise.resolve().then(() => task()).then(result => {
executing.splice(executing.indexOf(promise), 1);
return result;
});
results.push(promise);
if (results.length >= limit) {
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
}
return Promise.all(results);
}
module.exports = {
generateRandomString,
generateUUID,
generateMD5,
generateSHA256,
delay,
deepClone,
formatFileSize,
formatTimeDiff,
isValidEmail,
isValidURL,
sanitizeString,
truncateString,
fileExists,
ensureDirectoryExists,
readJSONFile,
writeJSONFile,
getFileExtension,
getFileNameWithoutExtension,
toCamelCase,
toKebabCase,
debounce,
throttle,
retry,
parallelLimit
};