Files
nxxmdata/backend/models/OperationLog.js
2025-09-12 20:08:42 +08:00

304 lines
8.0 KiB
JavaScript
Raw 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.

/**
* 操作日志模型
* @file OperationLog.js
* @description 记录系统用户的操作日志,包括新增、编辑、删除操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
class OperationLog extends BaseModel {
static init(sequelize) {
return super.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '主键ID'
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '操作用户ID'
},
username: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '操作用户名'
},
user_role: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '操作用户角色'
},
operation_type: {
type: DataTypes.ENUM('CREATE', 'UPDATE', 'DELETE', 'READ', 'LOGIN', 'LOGOUT', 'EXPORT', 'IMPORT', 'BATCH_DELETE', 'BATCH_UPDATE'),
allowNull: false,
comment: '操作类型CREATE-新增UPDATE-编辑DELETE-删除READ-查看LOGIN-登录LOGOUT-登出EXPORT-导出IMPORT-导入BATCH_DELETE-批量删除BATCH_UPDATE-批量更新'
},
module_name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '操作模块名称'
},
table_name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '操作的数据表名'
},
record_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '操作的记录ID'
},
operation_desc: {
type: DataTypes.TEXT,
allowNull: false,
comment: '操作描述'
},
old_data: {
type: DataTypes.JSON,
allowNull: true,
comment: '操作前的数据(编辑和删除时记录)'
},
new_data: {
type: DataTypes.JSON,
allowNull: true,
comment: '操作后的数据(新增和编辑时记录)'
},
ip_address: {
type: DataTypes.STRING(45),
allowNull: true,
comment: '操作IP地址'
},
user_agent: {
type: DataTypes.TEXT,
allowNull: true,
comment: '用户代理信息'
},
request_url: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '请求URL'
},
request_method: {
type: DataTypes.STRING(10),
allowNull: true,
comment: '请求方法'
},
response_status: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '响应状态码'
},
execution_time: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '执行时间(毫秒)'
},
error_message: {
type: DataTypes.TEXT,
allowNull: true,
comment: '错误信息(如果有)'
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '创建时间'
}
}, {
sequelize,
tableName: 'operation_logs',
comment: '操作日志表',
indexes: [
{
name: 'idx_user_id',
fields: ['user_id']
},
{
name: 'idx_operation_type',
fields: ['operation_type']
},
{
name: 'idx_module_name',
fields: ['module_name']
},
{
name: 'idx_table_name',
fields: ['table_name']
},
{
name: 'idx_created_at',
fields: ['created_at']
},
{
name: 'idx_user_operation',
fields: ['user_id', 'operation_type']
},
{
name: 'idx_module_operation',
fields: ['module_name', 'operation_type']
}
]
});
}
/**
* 记录操作日志
* @param {Object} logData 日志数据
* @returns {Promise<OperationLog>} 创建的日志记录
*/
static async recordOperation(logData) {
try {
const {
userId,
username,
userRole,
operationType,
moduleName,
tableName,
recordId,
operationDesc,
oldData,
newData,
ipAddress,
userAgent,
requestUrl,
requestMethod,
responseStatus,
executionTime,
errorMessage
} = logData;
return await this.create({
user_id: userId,
username: username,
user_role: userRole,
operation_type: operationType,
module_name: moduleName,
table_name: tableName,
record_id: recordId,
operation_desc: operationDesc,
old_data: oldData,
new_data: newData,
ip_address: ipAddress,
user_agent: userAgent,
request_url: requestUrl,
request_method: requestMethod,
response_status: responseStatus,
execution_time: executionTime,
error_message: errorMessage
});
} catch (error) {
console.error('记录操作日志失败:', error);
throw error;
}
}
/**
* 获取用户操作统计
* @param {number} userId 用户ID
* @param {string} startDate 开始日期
* @param {string} endDate 结束日期
* @returns {Promise<Object>} 统计结果
*/
static async getUserOperationStats(userId, startDate, endDate) {
try {
const whereClause = {
user_id: userId
};
if (startDate && endDate) {
whereClause.created_at = {
[this.sequelize.Sequelize.Op.between]: [startDate, endDate]
};
}
const stats = await this.findAll({
where: whereClause,
attributes: [
'operation_type',
[this.sequelize.fn('COUNT', this.sequelize.col('id')), 'count']
],
group: ['operation_type'],
raw: true
});
return stats.reduce((acc, stat) => {
acc[stat.operation_type] = parseInt(stat.count);
return acc;
}, {});
} catch (error) {
console.error('获取用户操作统计失败:', error);
throw error;
}
}
/**
* 获取模块操作统计
* @param {string} moduleName 模块名称
* @param {string} startDate 开始日期
* @param {string} endDate 结束日期
* @returns {Promise<Object>} 统计结果
*/
static async getModuleOperationStats(moduleName, startDate, endDate) {
try {
const whereClause = {
module_name: moduleName
};
if (startDate && endDate) {
whereClause.created_at = {
[this.sequelize.Sequelize.Op.between]: [startDate, endDate]
};
}
const stats = await this.findAll({
where: whereClause,
attributes: [
'operation_type',
[this.sequelize.fn('COUNT', this.sequelize.col('id')), 'count']
],
group: ['operation_type'],
raw: true
});
return stats.reduce((acc, stat) => {
acc[stat.operation_type] = parseInt(stat.count);
return acc;
}, {});
} catch (error) {
console.error('获取模块操作统计失败:', error);
throw error;
}
}
/**
* 清理过期日志
* @param {number} daysToKeep 保留天数
* @returns {Promise<number>} 删除的记录数
*/
static async cleanExpiredLogs(daysToKeep = 90) {
try {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
const deletedCount = await this.destroy({
where: {
created_at: {
[this.sequelize.Sequelize.Op.lt]: cutoffDate
}
}
});
console.log(`清理了 ${deletedCount} 条过期操作日志`);
return deletedCount;
} catch (error) {
console.error('清理过期日志失败:', error);
throw error;
}
}
}
module.exports = OperationLog;