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

202 lines
8.3 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 Newtonsoft.Json;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.Compensation;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 补偿任务管理服务实现
/// </summary>
public class CompensationManageService(
BaseRepository<CompensationTask> compensationTaskRepository,
IOperationLogService operationLogService,
ILogger<CompensationManageService> logger)
: BaseRepository<CompensationTask>, ICompensationManageService
{
/// <summary>
/// 分页查询补偿任务(含用户昵称)
/// </summary>
public async Task<PageListModel<CompensationManageOutput>> GetListAsync(CompensationTaskQueryInput 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 compensationTaskRepository.Queryable()
.LeftJoin<Users>((t, u) => t.UserId == u.Id)
.WhereIF(input.UserId.HasValue, (t, u) => t.UserId == input.UserId.Value)
.WhereIF(input.TaskType.HasValue, (t, u) => t.TaskType == (int)input.TaskType.Value)
.WhereIF(input.Status.HasValue, (t, u) => t.Status == (int)input.Status)
.WhereIF(!string.IsNullOrWhiteSpace(input.BusinessSource), (t, u) => t.BusinessSource == input.BusinessSource)
.OrderByDescending((t, u) => t.CreatedAt)
.Select((t, u) => new CompensationManageOutput
{
Id = t.Id,
TaskType = (CompensationTaskTypeEnum)t.TaskType,
BusinessSource = t.BusinessSource,
BusinessId = t.BusinessId,
UserId = t.UserId,
UserName = u.Name,
Payload = t.Payload,
ErrorMessage = t.ErrorMessage,
ErrorSource = t.ErrorSource,
RetryCount = t.RetryCount,
MaxRetries = t.MaxRetries,
Status = (CompensationTaskStatusEnum)t.Status,
ProcessedAt = t.ProcessedAt,
ScheduledAt = t.ScheduledAt,
ResultMessage = t.ResultMessage,
CreatedAt = t.CreatedAt
})
.ToPageListAsync(input.PageIndex, input.PageSize, totalNumber);
return new PageListModel<CompensationManageOutput>(pageResult, input.PageIndex, input.PageSize, totalNumber);
}
/// <summary>
/// 手动重试补偿任务
/// </summary>
public async Task RetryAsync(long taskId, long operatorId, string operatorName, CompensationRetryInput input, string? ipAddress = null)
{
logger.LogInformation("手动重试补偿任务TaskId: {TaskId}, Operator: {Operator}", taskId, operatorName);
var task = await compensationTaskRepository.GetByIdAsync(taskId);
if (task == null)
throw new BusinessException("补偿任务不存在", ResultCode.NOT_FOUND);
if (task.Status != (int)CompensationTaskStatusEnum.Failed && task.Status != (int)CompensationTaskStatusEnum.Cancelled)
throw new BusinessException($"只有失败或已取消的任务才能重试,当前状态: {task.Status}", ResultCode.BAD_REQUEST);
// 重置任务状态为 Pending清零重试次数设置立即执行
await compensationTaskRepository.Context.Updateable<CompensationTask>()
.SetColumns(t => t.Status == (int)CompensationTaskStatusEnum.Pending)
.SetColumns(t => t.RetryCount == 0)
.SetColumns(t => t.ScheduledAt == DateTime.Now)
.SetColumns(t => t.ResultMessage == $"管理员手动重试: {input.Reason}")
.SetColumns(t => t.UpdatedBy == operatorName)
.SetColumns(t => t.UpdatedAt == DateTime.Now)
.Where(t => t.Id == taskId && !t.IsDeleted)
.ExecuteCommandAsync();
// 记录操作日志
var detail = JsonConvert.SerializeObject(new
{
OriginalStatus = task.Status,
Reason = input.Reason,
TaskType = task.TaskType,
UserId = task.UserId
});
await operationLogService.LogAsync(new OperationLogRecordInput
{
OperatorId = operatorId,
OperatorName = operatorName,
ActionType = OperationLogActionType.CompensationRetry,
TargetType = OperationLogTargetType.CompensationTask,
TargetId = taskId,
Detail = detail,
IpAddress = ipAddress
});
logger.LogInformation("补偿任务手动重试成功TaskId: {TaskId}", taskId);
}
/// <summary>
/// 标记补偿任务为已解决
/// </summary>
public async Task ResolveAsync(long taskId, long operatorId, string operatorName, CompensationResolveInput input, string? ipAddress = null)
{
logger.LogInformation("标记补偿任务已解决TaskId: {TaskId}, Operator: {Operator}", taskId, operatorName);
var task = await compensationTaskRepository.GetByIdAsync(taskId);
if (task == null)
throw new BusinessException("补偿任务不存在", ResultCode.NOT_FOUND);
if (task.Status == (int)CompensationTaskStatusEnum.Success)
throw new BusinessException("该任务已经是成功状态,无需标记", ResultCode.BAD_REQUEST);
// 标记为 Success
await compensationTaskRepository.Context.Updateable<CompensationTask>()
.SetColumns(t => t.Status == (int)CompensationTaskStatusEnum.Success)
.SetColumns(t => t.ResultMessage == $"管理员手动标记已解决: {input.ResolveNote}")
.SetColumns(t => t.ProcessedAt == DateTime.Now)
.SetColumns(t => t.UpdatedBy == operatorName)
.SetColumns(t => t.UpdatedAt == DateTime.Now)
.Where(t => t.Id == taskId && !t.IsDeleted)
.ExecuteCommandAsync();
// 记录操作日志
var detail = JsonConvert.SerializeObject(new
{
OriginalStatus = task.Status,
ResolveNote = input.ResolveNote,
TaskType = task.TaskType,
UserId = task.UserId
});
await operationLogService.LogAsync(new OperationLogRecordInput
{
OperatorId = operatorId,
OperatorName = operatorName,
ActionType = OperationLogActionType.CompensationResolve,
TargetType = OperationLogTargetType.CompensationTask,
TargetId = taskId,
Detail = detail,
IpAddress = ipAddress
});
logger.LogInformation("补偿任务标记已解决成功TaskId: {TaskId}", taskId);
}
/// <summary>
/// 获取补偿任务详情
/// </summary>
public async Task<CompensationManageDetailOutput> GetDetailAsync(long taskId)
{
logger.LogInformation("获取补偿任务详情TaskId: {TaskId}", taskId);
var result = await compensationTaskRepository.Queryable()
.LeftJoin<Users>((t, u) => t.UserId == u.Id)
.Where((t, u) => t.Id == taskId && !t.IsDeleted)
.Select((t, u) => new CompensationManageDetailOutput
{
Id = t.Id,
TaskType = (CompensationTaskTypeEnum)t.TaskType,
BusinessSource = t.BusinessSource,
BusinessId = t.BusinessId,
UserId = t.UserId,
UserName = u.Name,
Payload = t.Payload,
ErrorMessage = t.ErrorMessage,
ErrorSource = t.ErrorSource,
RetryCount = t.RetryCount,
MaxRetries = t.MaxRetries,
Status = (CompensationTaskStatusEnum)t.Status,
ProcessedAt = t.ProcessedAt,
ScheduledAt = t.ScheduledAt,
ResultMessage = t.ResultMessage,
CreatedAt = t.CreatedAt,
CreatedBy = t.CreatedBy,
UpdatedBy = t.UpdatedBy,
UpdatedAt = t.UpdatedAt
})
.FirstAsync();
if (result == null)
throw new BusinessException("补偿任务不存在", ResultCode.NOT_FOUND);
return result;
}
}