2026-06-08 16:57:44 +08:00
|
|
|
|
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)
|
2026-06-08 17:54:36 +08:00
|
|
|
|
.WhereIF(input.Status.HasValue, (t, u) => t.Status == (int)input.Status)
|
2026-06-08 16:57:44 +08:00
|
|
|
|
.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,
|
2026-06-08 17:54:36 +08:00
|
|
|
|
Status = (CompensationTaskStatusEnum)t.Status,
|
2026-06-08 16:57:44 +08:00
|
|
|
|
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)
|
2026-06-29 16:34:26 +08:00
|
|
|
|
throw new BusinessException("补偿任务不存在", ResultCode.NOT_FOUND);
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
2026-06-08 17:54:36 +08:00
|
|
|
|
if (task.Status != (int)CompensationTaskStatusEnum.Failed && task.Status != (int)CompensationTaskStatusEnum.Cancelled)
|
2026-06-29 16:34:26 +08:00
|
|
|
|
throw new BusinessException($"只有失败或已取消的任务才能重试,当前状态: {task.Status}", ResultCode.BAD_REQUEST);
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 重置任务状态为 Pending,清零重试次数,设置立即执行
|
|
|
|
|
|
await compensationTaskRepository.Context.Updateable<CompensationTask>()
|
2026-06-08 17:54:36 +08:00
|
|
|
|
.SetColumns(t => t.Status == (int)CompensationTaskStatusEnum.Pending)
|
2026-06-08 16:57:44 +08:00
|
|
|
|
.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
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-09 18:01:37 +08:00
|
|
|
|
await operationLogService.LogAsync(new OperationLogRecordInput
|
|
|
|
|
|
{
|
|
|
|
|
|
OperatorId = operatorId,
|
|
|
|
|
|
OperatorName = operatorName,
|
|
|
|
|
|
ActionType = OperationLogActionType.CompensationRetry,
|
|
|
|
|
|
TargetType = OperationLogTargetType.CompensationTask,
|
|
|
|
|
|
TargetId = taskId,
|
|
|
|
|
|
Detail = detail,
|
|
|
|
|
|
IpAddress = ipAddress
|
|
|
|
|
|
});
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
2026-06-29 16:34:26 +08:00
|
|
|
|
throw new BusinessException("补偿任务不存在", ResultCode.NOT_FOUND);
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
2026-06-08 17:54:36 +08:00
|
|
|
|
if (task.Status == (int)CompensationTaskStatusEnum.Success)
|
2026-06-29 16:34:26 +08:00
|
|
|
|
throw new BusinessException("该任务已经是成功状态,无需标记", ResultCode.BAD_REQUEST);
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 标记为 Success
|
|
|
|
|
|
await compensationTaskRepository.Context.Updateable<CompensationTask>()
|
2026-06-08 17:54:36 +08:00
|
|
|
|
.SetColumns(t => t.Status == (int)CompensationTaskStatusEnum.Success)
|
2026-06-08 16:57:44 +08:00
|
|
|
|
.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
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-09 18:01:37 +08:00
|
|
|
|
await operationLogService.LogAsync(new OperationLogRecordInput
|
|
|
|
|
|
{
|
|
|
|
|
|
OperatorId = operatorId,
|
|
|
|
|
|
OperatorName = operatorName,
|
|
|
|
|
|
ActionType = OperationLogActionType.CompensationResolve,
|
|
|
|
|
|
TargetType = OperationLogTargetType.CompensationTask,
|
|
|
|
|
|
TargetId = taskId,
|
|
|
|
|
|
Detail = detail,
|
|
|
|
|
|
IpAddress = ipAddress
|
|
|
|
|
|
});
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
|
|
|
|
|
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,
|
2026-06-08 17:54:36 +08:00
|
|
|
|
Status = (CompensationTaskStatusEnum)t.Status,
|
2026-06-08 16:57:44 +08:00
|
|
|
|
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)
|
2026-06-29 16:34:26 +08:00
|
|
|
|
throw new BusinessException("补偿任务不存在", ResultCode.NOT_FOUND);
|
2026-06-08 16:57:44 +08:00
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|