- 新增30+业务枚举类型覆盖用户、宠物、商城、社区、积分等模块 - 完成实体类、DTO、服务层的字符串枚举替换为强类型枚举 - 修复用户状态枚举名称变更,将Frozen改为Disabled - 新增批量发布社区消息接口与控制器实现 - 新增操作日志、积分管理、补偿任务相关服务与DTO
175 lines
6.7 KiB
C#
175 lines
6.7 KiB
C#
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);
|
||
}
|
||
}
|