/** * 操作日志中间件 * @file operationLogger.js * @description 自动记录用户操作的中间件 */ const { OperationLog } = require('../models'); /** * 操作日志中间件 * @param {Object} options 配置选项 * @param {string} options.moduleName 模块名称 * @param {string} options.tableName 数据表名 * @param {Function} options.getRecordId 获取记录ID的函数 * @param {Function} options.getOperationDesc 获取操作描述的函数 * @param {Function} options.getOldData 获取操作前数据的函数(可选) * @param {Function} options.getNewData 获取操作后数据的函数(可选) * @returns {Function} 中间件函数 */ const operationLogger = (options = {}) => { const { moduleName, tableName, getRecordId = () => null, getOperationDesc = () => '未知操作', getOldData = () => null, getNewData = () => null } = options; return async (req, res, next) => { const startTime = Date.now(); const originalSend = res.send; let responseBody = null; // 拦截响应数据 res.send = function(data) { responseBody = data; return originalSend.call(this, data); }; // 在响应完成后记录日志 res.on('finish', async () => { try { const executionTime = Date.now() - startTime; const operationType = getOperationType(req.method); if (!operationType) { return; // 只记录增删改操作 } const recordId = getRecordId(req, res); const operationDesc = getOperationDesc(req, res); const oldData = getOldData(req, res); const newData = getNewData(req, res); // 获取用户信息 const user = req.user; if (!user) { return; // 没有用户信息不记录日志 } // 获取IP地址 const ipAddress = req.ip || req.connection.remoteAddress || req.socket.remoteAddress || (req.connection.socket ? req.connection.socket.remoteAddress : null) || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || 'unknown'; // 记录操作日志 await OperationLog.recordOperation({ userId: user.id, username: user.username, userRole: user.roles, operationType, moduleName, tableName, recordId, operationDesc, oldData, newData, ipAddress, userAgent: req.get('User-Agent'), requestUrl: req.originalUrl, requestMethod: req.method, responseStatus: res.statusCode, executionTime, errorMessage: res.statusCode >= 400 ? responseBody?.message || '操作失败' : null }); } catch (error) { console.error('记录操作日志失败:', error); // 不抛出错误,避免影响主业务 } }); next(); }; }; /** * 根据HTTP方法获取操作类型 * @param {string} method HTTP方法 * @returns {string|null} 操作类型 */ const getOperationType = (method) => { switch (method.toUpperCase()) { case 'POST': return 'CREATE'; case 'PUT': case 'PATCH': return 'UPDATE'; case 'DELETE': return 'DELETE'; default: return null; } }; /** * 创建操作日志记录器 * @param {Object} options 配置选项 * @returns {Function} 中间件函数 */ const createOperationLogger = (options) => { return operationLogger(options); }; /** * 批量操作日志记录器 * @param {Array} operations 操作配置数组 * @returns {Function} 中间件函数 */ const createBatchOperationLogger = (operations) => { return async (req, res, next) => { const startTime = Date.now(); const originalSend = res.send; let responseBody = null; // 拦截响应数据 res.send = function(data) { responseBody = data; return originalSend.call(this, data); }; // 在响应完成后记录日志 res.on('finish', async () => { try { const executionTime = Date.now() - startTime; const operationType = getOperationType(req.method); if (!operationType) { return; } const user = req.user; if (!user) { return; } const ipAddress = req.ip || req.connection.remoteAddress || req.socket.remoteAddress || (req.connection.socket ? req.connection.socket.remoteAddress : null) || req.headers['x-forwarded-for']?.split(',')[0]?.trim() || 'unknown'; // 为每个操作配置记录日志 for (const operation of operations) { const { moduleName, tableName, getRecordId = () => null, getOperationDesc = () => '未知操作', getOldData = () => null, getNewData = () => null } = operation; const recordId = getRecordId(req, res); const operationDesc = getOperationDesc(req, res); const oldData = getOldData(req, res); const newData = getNewData(req, res); await OperationLog.recordOperation({ userId: user.id, username: user.username, userRole: user.roles, operationType, moduleName, tableName, recordId, operationDesc, oldData, newData, ipAddress, userAgent: req.get('User-Agent'), requestUrl: req.originalUrl, requestMethod: req.method, responseStatus: res.statusCode, executionTime, errorMessage: res.statusCode >= 400 ? responseBody?.message || '操作失败' : null }); } } catch (error) { console.error('记录批量操作日志失败:', error); } }); next(); }; }; module.exports = { operationLogger, createOperationLogger, createBatchOperationLogger, getOperationType };