import { Database } from 'sqlite3'; import { getDbInstance, runDb, getDb as getDbRow, allDb } from '../database/connection'; import { AuditLogEntry, AuditLogActionType } from '../types/audit.types'; type DbAuditLogRow = AuditLogEntry; export class AuditLogRepository { /** * 添加一条审计日志记录。 * @param actionType 操作类型。 * @param details 可选的详细信息(对象或字符串)。 */ async addLog(actionType: AuditLogActionType, details?: Record | string | null): Promise { const timestamp = Math.floor(Date.now() / 1000); let detailsString: string | null = null; if (details) { try { detailsString = typeof details === 'string' ? details : JSON.stringify(details); } catch (error: any) { console.error(`[审计日志] 序列化操作 ${actionType} 的详情失败:`, error.message); detailsString = JSON.stringify({ error: 'Failed to stringify details', originalDetails: String(details) }); // Ensure originalDetails is stringifiable } } const sql = 'INSERT INTO audit_logs (timestamp, action_type, details) VALUES (?, ?, ?)'; const params = [timestamp, actionType, detailsString]; try { const db = await getDbInstance(); await runDb(db, sql, params); // --- 添加日志清理逻辑 --- await this.cleanupOldLogs(db); // --- 清理逻辑结束 --- } catch (err: any) { console.error(`[审计日志] 添加操作 ${actionType} 的日志条目时出错: ${err.message}`); // 决定日志记录失败是应该抛出错误还是仅记录日志 } } /** * 清理旧的审计日志,保持最多 MAX_LOG_ENTRIES 条记录。 * @param db - 数据库实例。 */ private async cleanupOldLogs(db: Database): Promise { const MAX_LOG_ENTRIES = 50000; // 设置最大日志条数 const countSql = 'SELECT COUNT(*) as total FROM audit_logs'; const deleteSql = ` DELETE FROM audit_logs WHERE id IN ( SELECT id FROM audit_logs ORDER BY timestamp ASC LIMIT ? ) `; // 假设有自增的 id 列,并且 timestamp 能准确反映顺序 try { const countRow = await getDbRow<{ total: number }>(db, countSql); const total = countRow?.total ?? 0; if (total > MAX_LOG_ENTRIES) { const logsToDelete = total - MAX_LOG_ENTRIES; console.log(`[审计日志] 日志数量 (${total}) 超过限制 (${MAX_LOG_ENTRIES})。正在删除 ${logsToDelete} 条最旧的记录。`); await runDb(db, deleteSql, [logsToDelete]); } } catch (err: any) { console.error(`[审计日志] 日志清理过程中出错: ${err.message}`); // 清理失败不应阻止主日志记录流程,仅记录错误。 } } /** * 获取审计日志列表(支持分页和基本过滤)。 * @param limit 每页数量。 * @param offset 偏移量。 * @param actionType 可选的操作类型过滤。 * @param startDate 可选的开始时间戳(秒)。 * @param endDate 可选的结束时间戳(秒)。 * @param searchTerm 可选的搜索关键词(模糊匹配 details)。 */ async getLogs( limit: number = 50, offset: number = 0, actionType?: AuditLogActionType, startDate?: number, endDate?: number, searchTerm?: string // 添加 searchTerm 参数 ): Promise<{ logs: AuditLogEntry[], total: number }> { let baseSql = 'SELECT * FROM audit_logs'; let countSql = 'SELECT COUNT(*) as total FROM audit_logs'; const whereClauses: string[] = []; const params: (string | number)[] = []; const countParams: (string | number)[] = []; if (actionType) { whereClauses.push('action_type = ?'); params.push(actionType); countParams.push(actionType); } // 添加 searchTerm 的过滤逻辑 if (searchTerm) { // 搜索 details 字段,使用 LIKE 进行模糊匹配 whereClauses.push('details LIKE ?'); const searchTermLike = `%${searchTerm}%`; params.push(searchTermLike); countParams.push(searchTermLike); } if (whereClauses.length > 0) { const whereSql = ` WHERE ${whereClauses.join(' AND ')}`; baseSql += whereSql; countSql += whereSql; } baseSql += ' ORDER BY timestamp DESC LIMIT ? OFFSET ?'; params.push(limit, offset); try { const db = await getDbInstance(); const countRow = await getDbRow<{ total: number }>(db, countSql, countParams); const total = countRow?.total ?? 0; const logs = await allDb(db, baseSql, params); return { logs, total }; } catch (err: any) { console.error(`获取审计日志时出错:`, err.message); throw new Error(`获取审计日志时出错: ${err.message}`); } } }