1. 新增PetSkinImageTypeEnum.Cover枚举,添加封面图类型支持 2. 为PetSkin、PetTemplate实体添加封面图片地址字段 3. 新增补偿任务按用户ID筛选功能,优化UsersService查询逻辑 4. 重构宠物模板创建流程,支持批量创建进化链与图片 5. 移除PetEvolution实体的基础属性字段,简化进化形态结构 6. 新增OSS域名辅助工具与SDK注册扩展类 7. 优化宠物皮肤、进化阶段的DTO与服务层逻辑
178 lines
6.8 KiB
C#
178 lines
6.8 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 = (int)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 == (int)CompensationTaskStatusEnum.Pending || t.Status == (int)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 = (CompensationTaskStatusEnum)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.UserId.HasValue)
|
||
query = query.Where(t => t.UserId == input.UserId.Value);
|
||
|
||
if (input.Status.HasValue)
|
||
query = query.Where(t => t.Status == (int)input.Status);
|
||
|
||
if (input.TaskType.HasValue)
|
||
query = query.Where(t => t.TaskType == (int)input.TaskType);
|
||
|
||
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 = (CompensationTaskStatusEnum)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 == (int)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 == (int)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);
|
||
}
|
||
}
|