304 lines
8.0 KiB
JavaScript
304 lines
8.0 KiB
JavaScript
|
|
/**
|
|||
|
|
* 操作日志模型
|
|||
|
|
* @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;
|