1. 新增OperationLogAttribute与OperationLogActionFilter,实现自动化操作日志记录 2. 新增OperationLogRecordInput输入模型,重构IOperationLogService日志接口 3. 为所有业务控制器接口添加操作日志注解 4. 调整期刊AI批改任务的执行周期与方法适配异步调用 5. 优化Hangfire定时任务注册逻辑,支持异步任务 6. 补充完善操作日志类型与目标类型枚举
202 lines
8.3 KiB
C#
202 lines
8.3 KiB
C#
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;
|
||
}
|
||
}
|