import fs from 'fs'; import path from 'path'; // 日志文件路径 const LOG_FILE = './logs/app.log'; const LOG_DIR = './logs'; /** * 确保日志目录存在 */ function ensureLogDirectory() { if (!fs.existsSync(LOG_DIR)) { fs.mkdirSync(LOG_DIR, { recursive: true }); } } /** * 写入日志到文件 * @param {string} level - 日志级别 (INFO, ERROR, WARN) * @param {string} message - 日志消息 */ export function writeLog(level, message) { ensureLogDirectory(); const timestamp = new Date().toISOString(); const logEntry = `[${timestamp}] [${level}] ${message}\n`; try { fs.appendFileSync(LOG_FILE, logEntry); } catch (error) { console.error('写入日志文件失败:', error); } } /** * 增强的 console.log,同时输出到控制台和文件 * @param {...any} args - 要记录的参数 */ export function log(...args) { const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg) ).join(' '); console.log(...args); writeLog('INFO', message); } /** * 增强的 console.error,同时输出到控制台和文件 * @param {...any} args - 要记录的参数 */ export function logError(...args) { const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg) ).join(' '); console.error(...args); writeLog('ERROR', message); } /** * 读取最近的日志 * @param {number} lines - 要读取的行数,默认100行 * @returns {Array} 日志行数组 */ export function getRecentLogs(lines = 100) { try { if (!fs.existsSync(LOG_FILE)) { return []; } const content = fs.readFileSync(LOG_FILE, 'utf8'); const logLines = content.trim().split('\n').filter(line => line.length > 0); // 返回最后 N 行 return logLines.slice(-lines); } catch (error) { console.error('读取日志文件失败:', error); return []; } } /** * 创建人类可读的时间戳 * @returns {string} 格式化的时间戳 YYYY-MM-DD_HH-MM-SS */ export function getHumanReadableTimestamp() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); const seconds = String(now.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`; } /** * 确保截图目录存在 * @param {string} dir - 目录路径 */ export function ensureScreenshotDirectory(dir) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); console.log(`创建截图目录: ${dir}`); } } /** * 检查 Cookie 文件是否存在 * @param {string} cookieFile - Cookie 文件路径 * @returns {boolean} 文件是否存在 */ export function checkCookieFile(cookieFile) { if (!fs.existsSync(cookieFile)) { console.error(`Cookie文件不存在: ${cookieFile}`); console.log('请先运行 npm run login 进行登录'); return false; } return true; } /** * 读取并解析 Cookie 文件 * @param {string} cookieFile - Cookie 文件路径 * @returns {Array} Cookie 数组 */ export function loadCookies(cookieFile) { try { const cookies = JSON.parse(fs.readFileSync(cookieFile, 'utf8')); console.log(`已加载 ${cookies.length} 个cookies`); return cookies; } catch (error) { console.error('读取 Cookie 文件失败:', error); throw error; } } /** * 保存截图 * @param {Object} page - Playwright 页面对象 * @param {string} screenshotDir - 截图目录 * @param {string} prefix - 文件名前缀 * @returns {string} 截图文件路径 */ export async function saveScreenshot(page, screenshotDir, prefix = 'screenshot') { ensureScreenshotDirectory(screenshotDir); const timestamp = getHumanReadableTimestamp(); const screenshotPath = path.join(screenshotDir, `${prefix}-${timestamp}.png`); await page.screenshot({ path: screenshotPath }); console.log(`截图已保存: ${screenshotPath}`); return screenshotPath; }