Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/CompensationTaskService.cs
glz 78b686f9e5 refactor: 批量新增枚举类型并完成实体、DTO、服务层枚举替换
- 新增30+业务枚举类型覆盖用户、宠物、商城、社区、积分等模块
- 完成实体类、DTO、服务层的字符串枚举替换为强类型枚举
- 修复用户状态枚举名称变更,将Frozen改为Disabled
- 新增批量发布社区消息接口与控制器实现
- 新增操作日志、积分管理、补偿任务相关服务与DTO
2026-06-08 16:57:44 +08:00

175 lines
6.7 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.Dto.Compensation;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 补偿任务服务实现(仅负责记录,处理逻辑由外部 Hangfire 项目完成)
/// </summary>
public class CompensationTaskService(
BaseRepository<CompensationTask> taskRepository,
ILogger<CompensationTaskService> logger)
: BaseRepository<CompensationTask>, ICompensationTaskService
{
/// <summary>
/// 创建补偿任务 — 记录失败操作,供后续补偿处理
/// </summary>
public async Task<long> CreateTaskAsync(CreateCompensationTaskInput input)
{
logger.LogWarning(
"创建补偿任务TaskType: {TaskType}, BusinessSource: {BusinessSource}, UserId: {UserId}, ErrorSource: {ErrorSource}, Error: {ErrorMessage}",
input.TaskType, input.BusinessSource, input.UserId, input.ErrorSource, input.ErrorMessage);
var payloadJson = input.Payload is string str ? str : JsonConvert.SerializeObject(input.Payload);
var task = new CompensationTask
{
TaskType = (int)input.TaskType,
BusinessSource = input.BusinessSource,
BusinessId = input.BusinessId,
UserId = input.UserId,
Payload = payloadJson,
ErrorMessage = input.ErrorMessage,
ErrorSource = input.ErrorSource,
RetryCount = 0,
MaxRetries = input.MaxRetries > 0 ? input.MaxRetries : 3,
Status = CompensationTaskStatusEnum.Pending,
ScheduledAt = DateTime.Now,
IsDeleted = false,
CreatedBy = "System",
CreatedAt = DateTime.Now,
UpdatedBy = "System",
UpdatedAt = DateTime.Now
};
var result = await taskRepository.InsertReturnEntityAsync(task);
logger.LogInformation("补偿任务创建成功TaskId: {TaskId}", result.Id);
return result.Id;
}
/// <summary>
/// 获取待处理的补偿任务列表
/// </summary>
public async Task<List<CompensationTaskOutput>> GetPendingTasksAsync(int limit = 50)
{
var now = DateTime.Now;
var tasks = await taskRepository.Queryable()
.Where(t => (t.Status == CompensationTaskStatusEnum.Pending || t.Status == CompensationTaskStatusEnum.Processing)
&& !t.IsDeleted
&& (t.ScheduledAt == null || t.ScheduledAt <= now))
.OrderBy(t => t.CreatedAt)
.Take(limit)
.Select(t => new CompensationTaskOutput
{
Id = t.Id,
TaskType = (CompensationTaskTypeEnum)t.TaskType,
BusinessSource = t.BusinessSource,
BusinessId = t.BusinessId,
UserId = t.UserId,
Payload = t.Payload,
ErrorMessage = t.ErrorMessage,
ErrorSource = t.ErrorSource,
RetryCount = t.RetryCount,
MaxRetries = t.MaxRetries,
Status = t.Status,
ProcessedAt = t.ProcessedAt,
ScheduledAt = t.ScheduledAt,
ResultMessage = t.ResultMessage,
CreatedAt = t.CreatedAt
})
.ToListAsync();
return tasks;
}
/// <summary>
/// 根据业务来源和类型查询补偿任务(用于外部项目按条件拉取)
/// </summary>
public async Task<List<CompensationTaskOutput>> GetTasksAsync(GetCompensationTasksInput input)
{
var query = taskRepository.Queryable()
.Where(t => !t.IsDeleted);
if (input.Status.HasValue)
query = query.Where(t => t.Status == input.Status.Value);
if (input.TaskType.HasValue)
query = query.Where(t => t.TaskType == (int)input.TaskType.Value);
if (!string.IsNullOrEmpty(input.BusinessSource))
query = query.Where(t => t.BusinessSource == input.BusinessSource);
var tasks = await query
.OrderBy(t => t.CreatedAt)
.Take(input.Limit > 0 ? input.Limit : 50)
.Select(t => new CompensationTaskOutput
{
Id = t.Id,
TaskType = (CompensationTaskTypeEnum)t.TaskType,
BusinessSource = t.BusinessSource,
BusinessId = t.BusinessId,
UserId = t.UserId,
Payload = t.Payload,
ErrorMessage = t.ErrorMessage,
ErrorSource = t.ErrorSource,
RetryCount = t.RetryCount,
MaxRetries = t.MaxRetries,
Status = t.Status,
ProcessedAt = t.ProcessedAt,
ScheduledAt = t.ScheduledAt,
ResultMessage = t.ResultMessage,
CreatedAt = t.CreatedAt
})
.ToListAsync();
return tasks;
}
/// <summary>
/// 更新补偿任务状态(供外部处理项目回调更新结果)
/// </summary>
public async Task UpdateTaskStatusAsync(long taskId, UpdateCompensationStatusInput input)
{
var update = taskRepository.Context.Updateable<CompensationTask>()
.SetColumns(t => t.Status == input.Status)
.SetColumns(t => t.ResultMessage == input.ResultMessage)
.SetColumns(t => t.ProcessedAt == DateTime.Now)
.SetColumns(t => t.UpdatedAt == DateTime.Now);
// 如果外部传入了重试相关字段,一并更新
if (input.RetryCount.HasValue)
update = update.SetColumns(t => t.RetryCount == input.RetryCount.Value);
if (input.ScheduledAt.HasValue)
update = update.SetColumns(t => t.ScheduledAt == input.ScheduledAt.Value);
await update.Where(t => t.Id == taskId && !t.IsDeleted)
.ExecuteCommandAsync();
logger.LogInformation("补偿任务状态更新TaskId: {TaskId}, Status: {Status}", taskId, input.Status);
}
/// <summary>
/// 取消补偿任务
/// </summary>
public async Task CancelTaskAsync(long taskId, string reason)
{
await taskRepository.Context.Updateable<CompensationTask>()
.SetColumns(t => t.Status == CompensationTaskStatusEnum.Cancelled)
.SetColumns(t => t.ResultMessage == reason)
.SetColumns(t => t.UpdatedAt == DateTime.Now)
.Where(t => t.Id == taskId && !t.IsDeleted)
.ExecuteCommandAsync();
logger.LogInformation("补偿任务已取消TaskId: {TaskId}, Reason: {Reason}", taskId, reason);
}
}