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;
|