158 lines
4.3 KiB
JavaScript
158 lines
4.3 KiB
JavaScript
|
|
const OperationLog = require('../models/OperationLog');
|
||
|
|
const LoginLog = require('../models/LoginLog');
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 记录操作日志
|
||
|
|
* @param {Object} options 日志选项
|
||
|
|
* @param {number} options.userId 用户ID
|
||
|
|
* @param {string} options.username 用户名
|
||
|
|
* @param {string} options.module 操作模块
|
||
|
|
* @param {string} options.action 操作类型
|
||
|
|
* @param {string} options.description 操作描述
|
||
|
|
* @param {string} options.method 请求方法
|
||
|
|
* @param {string} options.url 请求URL
|
||
|
|
* @param {string} options.ip IP地址
|
||
|
|
* @param {Object} options.params 请求参数
|
||
|
|
* @param {Object} options.result 操作结果
|
||
|
|
* @param {string} options.status 操作状态 success/fail
|
||
|
|
* @param {number} options.duration 执行时长(毫秒)
|
||
|
|
*/
|
||
|
|
async function logOperation(options) {
|
||
|
|
try {
|
||
|
|
await OperationLog.create({
|
||
|
|
userId: options.userId,
|
||
|
|
username: options.username,
|
||
|
|
module: options.module,
|
||
|
|
action: options.action,
|
||
|
|
description: options.description,
|
||
|
|
method: options.method,
|
||
|
|
url: options.url,
|
||
|
|
ip: options.ip,
|
||
|
|
params: options.params ? JSON.stringify(options.params) : null,
|
||
|
|
result: options.result ? JSON.stringify(options.result) : null,
|
||
|
|
status: options.status || 'success',
|
||
|
|
duration: options.duration
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('记录操作日志失败:', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 记录登录日志
|
||
|
|
* @param {Object} options 日志选项
|
||
|
|
* @param {number} options.userId 用户ID
|
||
|
|
* @param {string} options.username 用户名
|
||
|
|
* @param {string} options.loginType 登录类型 login/logout
|
||
|
|
* @param {string} options.ip IP地址
|
||
|
|
* @param {string} options.userAgent 浏览器信息
|
||
|
|
* @param {string} options.status 登录状态 success/fail
|
||
|
|
* @param {string} options.message 登录信息/失败原因
|
||
|
|
*/
|
||
|
|
async function logLogin(options) {
|
||
|
|
try {
|
||
|
|
await LoginLog.create({
|
||
|
|
userId: options.userId,
|
||
|
|
username: options.username,
|
||
|
|
loginType: options.loginType,
|
||
|
|
ip: options.ip,
|
||
|
|
userAgent: options.userAgent,
|
||
|
|
status: options.status || 'success',
|
||
|
|
message: options.message
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('记录登录日志失败:', error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取客户端IP地址
|
||
|
|
* @param {Object} req Express请求对象
|
||
|
|
* @returns {string} IP地址
|
||
|
|
*/
|
||
|
|
function getClientIp(req) {
|
||
|
|
return req.headers['x-forwarded-for'] ||
|
||
|
|
req.headers['x-real-ip'] ||
|
||
|
|
req.connection.remoteAddress ||
|
||
|
|
req.socket.remoteAddress ||
|
||
|
|
'unknown';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 操作日志中间件
|
||
|
|
* 自动记录API请求日志
|
||
|
|
*/
|
||
|
|
function operationLogMiddleware(options = {}) {
|
||
|
|
const { module = '系统', excludePaths = ['/api/auth/login'] } = options;
|
||
|
|
|
||
|
|
return async (req, res, next) => {
|
||
|
|
// 排除指定路径
|
||
|
|
if (excludePaths.some(path => req.path.includes(path))) {
|
||
|
|
return next();
|
||
|
|
}
|
||
|
|
|
||
|
|
const startTime = Date.now();
|
||
|
|
const originalSend = res.send;
|
||
|
|
|
||
|
|
// 捕获响应数据
|
||
|
|
res.send = function(data) {
|
||
|
|
res.responseData = data;
|
||
|
|
return originalSend.call(this, data);
|
||
|
|
};
|
||
|
|
|
||
|
|
res.on('finish', async () => {
|
||
|
|
const duration = Date.now() - startTime;
|
||
|
|
const user = req.user || {};
|
||
|
|
|
||
|
|
try {
|
||
|
|
await OperationLog.create({
|
||
|
|
userId: user.id,
|
||
|
|
username: user.username,
|
||
|
|
module: module,
|
||
|
|
action: getActionFromMethod(req.method),
|
||
|
|
description: `${req.method} ${req.path}`,
|
||
|
|
method: req.method,
|
||
|
|
url: req.originalUrl,
|
||
|
|
ip: getClientIp(req),
|
||
|
|
params: JSON.stringify({
|
||
|
|
body: req.body,
|
||
|
|
query: req.query,
|
||
|
|
params: req.params
|
||
|
|
}),
|
||
|
|
result: res.responseData ? res.responseData.substring(0, 2000) : null,
|
||
|
|
status: res.statusCode >= 200 && res.statusCode < 300 ? 'success' : 'fail',
|
||
|
|
duration: duration
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('记录操作日志失败:', error);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
next();
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 根据HTTP方法获取操作类型
|
||
|
|
* @param {string} method HTTP方法
|
||
|
|
* @returns {string} 操作类型
|
||
|
|
*/
|
||
|
|
function getActionFromMethod(method) {
|
||
|
|
const actionMap = {
|
||
|
|
'GET': '查询',
|
||
|
|
'POST': '新增',
|
||
|
|
'PUT': '修改',
|
||
|
|
'PATCH': '修改',
|
||
|
|
'DELETE': '删除'
|
||
|
|
};
|
||
|
|
return actionMap[method] || '其他';
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
logOperation,
|
||
|
|
logLogin,
|
||
|
|
getClientIp,
|
||
|
|
operationLogMiddleware,
|
||
|
|
getActionFromMethod
|
||
|
|
};
|