Files
parr_api/PARR.DAL/TaskServices/TaskManagementService.cs

125 lines
4.8 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.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.BLL.Contracts.Interfaces;
using PARR.Common.Domain;
using PARR.DAL.Contracts;
using PARR.DAL.Models.TaskModels;
using PARR.DAL.Services.Interfaces.TaskServices;
using System.Text.Encodings.Web;
using System.Text.Json;
namespace PARR.DAL.TaskServices
{
internal class TaskManagementService : ITaskManagementService
{
private readonly ILogger<TaskManagementService> logger;
private readonly ITaskTypeService taskTypeService;
private readonly ITaskService taskService;
public TaskManagementService(
ILogger<TaskManagementService> logger,
ITaskTypeService taskTypeService,
ITaskService taskService
)
{
this.logger = logger;
this.taskTypeService = taskTypeService;
this.taskService = taskService;
}
public async Task<Guid> CreateTaskAsync<T>(TaskTypeEnum typeCode, T payload, IHistoryInitiator initiator, IMqSettings mqSettings)
{
var taskType = await taskTypeService.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Code == typeCode);
if (taskType == null)
{
logger.LogError("Тип задачи {TypeCode} не найден в БД", typeCode);
throw new InvalidOperationException($"Тип задачи typeCode не найден в БД");
}
// Проверка IsSingleton: если задача уже активна — возвращаем её
if (taskType.IsSingleton)
{
var existingTask = await GetActiveSingletonTaskAsync(typeCode);
if (existingTask != null)
{
logger.LogInformation(
"Задача типа {TypeCode} уже активна (id: {ExistingId}). Возвращаем существующую.",
typeCode, existingTask.Id);
return existingTask.Id;
}
}
var payloadStr = PayloadToString(payload);
// Создаем новую запись задачи
var task = new TaskItem
{
Id = Guid.NewGuid(),
TypeCode = typeCode,
StatusCode = TaskItemStatusEnum.Pending,
Payload = payloadStr,
RetryCount = 0,
ProcessedAt = null,
InitiatorIp = initiator.InitiatorIp,
InitiatorParrComponentId = initiator.InitiatorParrComponentId,
InitiatorComment = initiator.InitiatorComment
};
if (!await taskService.CreateAsync(task) || !await taskService.CommitAsync())
{
logger.LogError("Ошибка при сохранении задачи в БД");
return default;
}
logger.LogInformation("Задача {TaskId} типа {TypeCode} сохранена в БД со статусом Pending", task.Id, typeCode);
// Публикуме задачу в очередь. Если вдруг даже не получится,
// задача останется в БД со статусом pending, и Reconciliation Task позже ее возмет в работу
// todo: опубликовать в очередь
// вопросы, зачем scoped? может можно AddTransient?
// нужно придумать шаблонную модель для очереди
return task.Id;
}
/// <summary>
/// Получить активную singleton задачу указанного типа
/// </summary>
/// <param name="typeCode"></param>
/// <returns></returns>
private async Task<TaskItem?> GetActiveSingletonTaskAsync(TaskTypeEnum typeCode)
{
return await taskService.Get().AsNoTracking()
.FirstOrDefaultAsync(t => t.TypeCode == typeCode && (
t.StatusCode == TaskItemStatusEnum.Pending
|| t.StatusCode == TaskItemStatusEnum.Processing
));
}
/// <summary>
/// Payload конвертировать в string
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="payload"></param>
/// <returns></returns>
private string PayloadToString<T>(T payload)
{
var jsonOptions = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
return JsonSerializer.Serialize(payload, jsonOptions);
}
}
}