Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/OperationLogService.cs
glz 766be485d0 feat: 新增操作日志功能并优化定时任务配置
1.  新增OperationLogAttribute与OperationLogActionFilter,实现自动化操作日志记录
2.  新增OperationLogRecordInput输入模型,重构IOperationLogService日志接口
3.  为所有业务控制器接口添加操作日志注解
4.  调整期刊AI批改任务的执行周期与方法适配异步调用
5.  优化Hangfire定时任务注册逻辑,支持异步任务
6.  补充完善操作日志类型与目标类型枚举
2026-07-09 18:01:37 +08:00

169 lines
6.0 KiB
C#
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.

using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Repository;
using Serilog.Core;
using SqlSugar;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 操作日志服务实现
/// </summary>
public class OperationLogService(
BaseRepository<OperationLog> operationLogRepository,
BaseRepository<AdminUser> adminUserRepository,
BaseRepository<Users> usersRepository,
ILogger<OperationLogService> logger) : BaseRepository<OperationLog>, IOperationLogService
{
/// <summary>
/// 记录操作日志
/// </summary>
public async Task LogAsync(long operatorId, string operatorName, string actionType, string targetType, long targetId, string? targetName = null, string? detail = null, string? ipAddress = null)
{
await LogAsync(new OperationLogRecordInput
{
OperatorId = operatorId,
OperatorName = operatorName,
ActionType = actionType,
TargetType = targetType,
TargetId = targetId,
TargetName = targetName,
Detail = detail,
IpAddress = ipAddress
});
}
/// <summary>
/// 记录操作日志
/// </summary>
public async Task LogAsync(OperationLogRecordInput input)
{
try
{
var log = new OperationLog
{
OperatorId = input.OperatorId,
OperatorName = input.OperatorName,
ActionType = input.ActionType,
TargetType = input.TargetType,
TargetId = input.TargetId,
TargetName = input.TargetName,
Detail = input.Detail,
IpAddress = input.IpAddress,
IsDeleted = false,
CreatedBy = input.OperatorName,
CreatedAt = DateTime.Now,
UpdatedBy = input.OperatorName,
UpdatedAt = DateTime.Now
};
await operationLogRepository.InsertAsync(log);
logger.LogInformation(
"记录操作日志Operator: {Operator}, Action: {Action}, Target: {TargetType}/{TargetId}",
input.OperatorName, input.ActionType, input.TargetType, input.TargetId);
}
catch (Exception ex)
{
// 日志记录不应影响主业务流程
logger.LogError(ex, "记录操作日志失败Operator: {Operator}, Action: {Action}", input.OperatorName, input.ActionType);
}
}
/// <summary>
/// 分页查询操作日志
/// </summary>
public async Task<PageListModel<OperationLogOutput>> GetListAsync(OperationLogQueryInput input)
{
if (input.PageIndex <= 0)
input.PageIndex = 1;
if (input.PageSize <= 0 || input.PageSize > 100)
input.PageSize = 10;
RefAsync<int> totalNumber = 0;
var pageResult = await operationLogRepository.Queryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.OperatorName), l => l.OperatorName.Contains(input.OperatorName))
.WhereIF(!string.IsNullOrWhiteSpace(input.ActionType), l => l.ActionType == input.ActionType)
.WhereIF(!string.IsNullOrWhiteSpace(input.TargetType), l => l.TargetType == input.TargetType)
.WhereIF(input.TargetId.HasValue, l => l.TargetId == input.TargetId.Value)
.OrderByDescending(l => l.CreatedAt)
.Select(l => new OperationLogOutput
{
Id = l.Id,
OperatorId = l.OperatorId,
OperatorName = l.OperatorName,
ActionType = l.ActionType,
TargetType = l.TargetType,
TargetId = l.TargetId,
TargetName = l.TargetName,
Detail = l.Detail,
IpAddress = l.IpAddress,
CreatedAt = l.CreatedAt
})
.ToPageListAsync(input.PageIndex, input.PageSize, totalNumber);
return new PageListModel<OperationLogOutput>(pageResult, input.PageIndex, input.PageSize, totalNumber);
}
/// <summary>
/// 获取操作日志详情
/// </summary>
public async Task<OperationLogDetailOutput> GetDetailAsync(long id)
{
var log = await operationLogRepository.Queryable()
.Where(l => l.Id == id && !l.IsDeleted)
.FirstAsync();
if (log == null)
{
throw new BusinessException("操作日志记录不存在", ResultCode.NOT_FOUND);
}
var result = new OperationLogDetailOutput
{
Id = log.Id,
ActionType = log.ActionType,
TargetType = log.TargetType,
TargetId = log.TargetId,
TargetName = log.TargetName,
Detail = log.Detail,
IpAddress = log.IpAddress,
CreatedAt = log.CreatedAt,
OperatorName = log.OperatorName
};
// 查询操作人详细信息
var adminUser = await adminUserRepository.GetByIdAsync(log.OperatorId);
if (adminUser != null)
{
result.OperatorRole = adminUser.Type.ToString();
}
// 当目标类型为用户时,查询被操作人信息
if (log.TargetType == OperationLogTargetType.User)
{
var targetUser = await usersRepository.GetByIdAsync(log.TargetId);
if (targetUser != null)
{
result.TargetUserName = targetUser.Name;
result.TargetUserAvatar = targetUser.AvatarUrl;
// Phone 已迁移到 WxUser 表,通过 WxUserId 关联查询
var wxUser = await usersRepository.Context.Queryable<WxUser>()
.Where(w => w.Id == targetUser.WxUserId && !w.IsDeleted)
.FirstAsync();
result.TargetUserPhone = wxUser?.Phone;
}
}
return result;
}
}