using Hcs.Broker; using Hcs.Broker.Logger; using Hcs.Broker.MessageCapturer; using Hcs.WebApp.BackgroundServices.ResultGetters; using Hcs.WebApp.Config; using Hcs.WebApp.Data.Hcs; using Hcs.WebApp.Services; using Hcs.WebApp.Utils; namespace Hcs.WebApp.BackgroundServices { public class ResultGetService( ResultGetState state, ResultGetterFactory resultGetterFactory, IServiceScopeFactory scopeFactory, IWebHostEnvironment webHostEnvironment) : BackgroundService { private class GetState { public int attempt; public int timer; public bool done; } private const int ITERATION_TIME = 10000; private const int SLEEP_TIME = 30000; private const string INCOMING_DIR_NAME = "incoming"; private readonly ResultGetState state = state; private readonly ResultGetterFactory resultGetterFactory = resultGetterFactory; private readonly IServiceScopeFactory scopeFactory = scopeFactory; private readonly IWebHostEnvironment webHostEnvironment = webHostEnvironment; private readonly List<(Operation operation, GetState state)> entries = []; private Broker.Logger.ILogger logger; private IMessageCapturer messageCapturer; private IClient client; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await InitializeStateAsync(); InitializeClient(); while (!stoppingToken.IsCancellationRequested) { while (state.TryDequeueOperation(out var operation)) { state.SetProcessingOperation(operation); if (stoppingToken.IsCancellationRequested) return; entries.Add(new (operation, new GetState())); } if (entries.Count > 0) { await Task.Delay(ITERATION_TIME, stoppingToken); var scope = scopeFactory.CreateScope(); foreach (var entry in entries) { entry.state.timer += ITERATION_TIME; var endedAt = DateTime.MinValue; var failureReason = string.Empty; var send = entry.state.attempt switch { 0 => entry.state.timer >= 10000, 1 => entry.state.timer >= 60000, 2 => entry.state.timer >= 300000, 3 => entry.state.timer >= 900000, _ => entry.state.timer >= 1800000, }; if (send) { try { var resultGetter = resultGetterFactory.CreateResultGetter(scope, client, entry.operation); var response = await resultGetter.GetAsync(); if (response.success) { entry.state.done = true; endedAt = response.endedAt; } } catch (Exception e) { var headquartersService = scope.ServiceProvider.GetRequiredService(); endedAt = DateTime.Now; failureReason = e.CombineMessages(); await headquartersService.SetOperationEndedWithFailAsync(entry.operation.Id, endedAt, failureReason); entry.state.done = true; } entry.state.attempt++; entry.state.timer = 0; } if (entry.state.done) { state.UnsetProcessingOperation(entry.operation); state.InvokeOnOperationEnded(entry.operation.Id, entry.operation.CampaignId, endedAt, failureReason); } } entries.RemoveAll(x => x.state.done); } else { await Task.Delay(SLEEP_TIME, stoppingToken); } } } private async Task InitializeStateAsync() { using var scope = scopeFactory.CreateScope(); var headquartersService = scope.ServiceProvider.GetRequiredService(); var operations = await headquartersService.GetResultWaitingRemoteOperationsAsync(); foreach (var operation in operations) { state.EnqueueOperation(operation); } state.Ready = true; } private void InitializeClient() { logger = new ActionLogger(); messageCapturer = new FileMessageCapturer(Path.Combine(webHostEnvironment.WebRootPath, INCOMING_DIR_NAME), logger); using var scope = scopeFactory.CreateScope(); var configuration = scope.ServiceProvider.GetRequiredService(); var config = configuration.GetSection("BrokerConfig").Get()!; var clientProvider = scope.ServiceProvider.GetRequiredService(); client = clientProvider.CreateClient(config, logger, messageCapturer); } } }