diff --git a/Hcs.Broker.Mock/Api/MockNsiCommonApi.cs b/Hcs.Broker.Mock/Api/MockNsiCommonApi.cs index fcf671d..4550a8f 100644 --- a/Hcs.Broker.Mock/Api/MockNsiCommonApi.cs +++ b/Hcs.Broker.Mock/Api/MockNsiCommonApi.cs @@ -1,4 +1,5 @@ using Hcs.Broker.Api; +using Hcs.Broker.Api.Request; using Hcs.Service.Async.NsiCommon; namespace Hcs.Broker.Mock.Api @@ -28,5 +29,15 @@ namespace Hcs.Broker.Mock.Api return Guid.NewGuid().ToString(); } + + public async Task> GetExportNsiItemResultAsync(string messageGuid) + { + await Task.Delay(3000); + + return new RequestSingleResult() + { + Ready = false + }; + } } } diff --git a/Hcs.Broker/Api/INsiCommonApi.cs b/Hcs.Broker/Api/INsiCommonApi.cs index 22fe164..71cb34b 100644 --- a/Hcs.Broker/Api/INsiCommonApi.cs +++ b/Hcs.Broker/Api/INsiCommonApi.cs @@ -1,4 +1,5 @@ -using Hcs.Service.Async.NsiCommon; +using Hcs.Broker.Api.Request; +using Hcs.Service.Async.NsiCommon; namespace Hcs.Broker.Api { @@ -22,7 +23,6 @@ namespace Hcs.Broker.Api /// Перечень общесистемных справочников Task ExportNsiListAsync(ListGroup listGroup, CancellationToken token = default); - /// /// Запрашивает экспорт данных общесистемного справочника /// @@ -31,5 +31,12 @@ namespace Hcs.Broker.Api /// Токен отмены /// Идентификатор сообщения операции экспорта Task RequestExportNsiItemAsync(int registryNumber, ListGroup listGroup, CancellationToken token = default); + + /// + /// Возвращает результат экспорта данных общесистемного справочника + /// + /// Идентификатор сообщения операции экспорта + /// Результат запроса + Task> GetExportNsiItemResultAsync(string messageGuid); } } diff --git a/Hcs.Broker/Api/NsiCommonApi.cs b/Hcs.Broker/Api/NsiCommonApi.cs index 5579bae..b424f27 100644 --- a/Hcs.Broker/Api/NsiCommonApi.cs +++ b/Hcs.Broker/Api/NsiCommonApi.cs @@ -1,4 +1,5 @@ -using Hcs.Broker.Api.Request.Exception; +using Hcs.Broker.Api.Request; +using Hcs.Broker.Api.Request.Exception; using Hcs.Broker.Api.Request.NsiCommon; using Hcs.Service.Async.NsiCommon; @@ -41,5 +42,12 @@ namespace Hcs.Broker.Api var request = new ExportNsiItemRequest(client); return await request.SendAsync(registryNumber, listGroup, token); } + + /// + public async Task> GetExportNsiItemResultAsync(string messageGuid) + { + var request = new ExportNsiItemRequest(client); + return await request.GetResultAsync(messageGuid); + } } } diff --git a/Hcs.Broker/Api/Request/Adapter/IErrorMessage.cs b/Hcs.Broker/Api/Request/Adapter/IErrorMessage.cs index 4536629..e6e1c5a 100644 --- a/Hcs.Broker/Api/Request/Adapter/IErrorMessage.cs +++ b/Hcs.Broker/Api/Request/Adapter/IErrorMessage.cs @@ -5,5 +5,7 @@ string ErrorCode { get; } string Description { get; } + + string StackTrace { get; } } } diff --git a/Hcs.Broker/Api/Request/NsiCommon/ExportNsiItemRequest.cs b/Hcs.Broker/Api/Request/NsiCommon/ExportNsiItemRequest.cs index f31f5cf..0326353 100644 --- a/Hcs.Broker/Api/Request/NsiCommon/ExportNsiItemRequest.cs +++ b/Hcs.Broker/Api/Request/NsiCommon/ExportNsiItemRequest.cs @@ -1,4 +1,5 @@ -using Hcs.Broker.Internal; +using Hcs.Broker.Api.Request.Adapter; +using Hcs.Broker.Internal; using Hcs.Service.Async.NsiCommon; namespace Hcs.Broker.Api.Request.NsiCommon @@ -36,17 +37,29 @@ namespace Hcs.Broker.Api.Request.NsiCommon ListGroup = listGroup }; - return await StartSendAsync(request, async asyncClient => + return await SendAsync(request, async asyncClient => { var response = await asyncClient.exportNsiItemAsync(CreateRequestHeader(), request); return response.AckRequest.Ack; }, token); } - internal async Task GetResultAsync(string messageGuid) + internal async Task> GetResultAsync(string messageGuid) { var result = await ExecuteGetResultAsync(messageGuid); - return result?.Item as NsiItemType; + if (result == null) + { + return RequestSingleResult.CreateNotReady(); + } + if (result.Item is NsiItemType nsiItem) + { + return RequestSingleResult.CreateSuccessful(nsiItem); + } + else if (result.Item is IErrorMessage errorMessage) + { + return RequestSingleResult.CreateFailed(errorMessage); + } + return RequestSingleResult.CreateFailed(null); } } } diff --git a/Hcs.Broker/Api/Request/RequestBase.cs b/Hcs.Broker/Api/Request/RequestBase.cs index 023f306..0100d97 100644 --- a/Hcs.Broker/Api/Request/RequestBase.cs +++ b/Hcs.Broker/Api/Request/RequestBase.cs @@ -149,7 +149,7 @@ namespace Hcs.Broker.Api.Request } } - protected async Task StartSendAsync( + protected async Task SendAsync( object request, Func> sender, CancellationToken token) diff --git a/Hcs.Broker/Api/Request/RequestSingleResult.cs b/Hcs.Broker/Api/Request/RequestSingleResult.cs new file mode 100644 index 0000000..74d3ca8 --- /dev/null +++ b/Hcs.Broker/Api/Request/RequestSingleResult.cs @@ -0,0 +1,43 @@ +using Hcs.Broker.Api.Request.Adapter; + +namespace Hcs.Broker.Api.Request +{ + public class RequestSingleResult + { + public bool Ready { get; set; } + + public bool Success { get; set; } + + public T? Result { get; set; } + + public IErrorMessage? ErrorMessage { get; set; } + + public static RequestSingleResult CreateNotReady() + { + return new RequestSingleResult + { + Ready = false + }; + } + + public static RequestSingleResult CreateSuccessful(T result) + { + return new RequestSingleResult + { + Ready = true, + Success = true, + Result = result + }; + } + + public static RequestSingleResult CreateFailed(IErrorMessage? errorMessage) + { + return new RequestSingleResult + { + Ready = true, + Success = false, + ErrorMessage = errorMessage + }; + } + } +} diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs b/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs index 2cb13de..67d4f8b 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutors/ExecutorFactory.cs @@ -14,7 +14,7 @@ namespace Hcs.WebApp.BackgroundServices.OperationExecutors return new ExportNsiItemExecutor_15_7_0_1(client, scope, operation); } - throw new NotImplementedException(); + throw new NotImplementedException($"Не удалось создать выполнителя операции типа {operation.Type}"); } } } diff --git a/Hcs.WebApp/BackgroundServices/ResultGetters/IResultGetter.cs b/Hcs.WebApp/BackgroundServices/ResultGetters/IResultGetter.cs new file mode 100644 index 0000000..cb3d518 --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultGetters/IResultGetter.cs @@ -0,0 +1,7 @@ +namespace Hcs.WebApp.BackgroundServices.ResultGetters +{ + public interface IResultGetter + { + Task GetAsync(); + } +} diff --git a/Hcs.WebApp/BackgroundServices/ResultGetters/NsiCommon/ExportNsiItemGetter_15_7_0_1.cs b/Hcs.WebApp/BackgroundServices/ResultGetters/NsiCommon/ExportNsiItemGetter_15_7_0_1.cs new file mode 100644 index 0000000..76ed4d0 --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultGetters/NsiCommon/ExportNsiItemGetter_15_7_0_1.cs @@ -0,0 +1,29 @@ +using Hcs.Broker; +using Hcs.WebApp.Data.Hcs; +using Hcs.WebApp.Services; + +namespace Hcs.WebApp.BackgroundServices.ResultGetters.NsiCommon +{ + public class ExportNsiItemGetter_15_7_0_1(IClient client, IServiceScope scope, Operation operation) : ResultGetterBase(client, scope, operation) + { + public override async Task GetAsync() + { + var result = await client.NsiCommon.GetExportNsiItemResultAsync(operation.MessageGuid!); + if (!result.Ready) + { + return ResultGetterResponse.NotReady; + } + + if (result.Success) + { + var registryService = scope.ServiceProvider.GetRequiredService(); + var registry = await registryService.GetRegistryByOperationIdAsync(operation.Id); + // TODO + + return ResultGetterResponse.Successful; + } + + return ResultGetterResponse.Failed; + } + } +} diff --git a/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterBase.cs b/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterBase.cs new file mode 100644 index 0000000..f208e94 --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterBase.cs @@ -0,0 +1,14 @@ +using Hcs.Broker; +using Hcs.WebApp.Data.Hcs; + +namespace Hcs.WebApp.BackgroundServices.ResultGetters +{ + public abstract class ResultGetterBase(IClient client, IServiceScope scope, Operation operation) : IResultGetter + { + protected readonly IClient client = client; + protected readonly IServiceScope scope = scope; + protected readonly Operation operation = operation; + + public abstract Task GetAsync(); + } +} diff --git a/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterFactory.cs b/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterFactory.cs new file mode 100644 index 0000000..c3b975d --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterFactory.cs @@ -0,0 +1,21 @@ +using Hcs.Broker; +using Hcs.WebApp.BackgroundServices.ResultGetters.NsiCommon; +using Hcs.WebApp.Data.Hcs; + +namespace Hcs.WebApp.BackgroundServices.ResultGetters +{ + public class ResultGetterFactory + { + public IResultGetter CreateResultGetter(IServiceScope scope, IClient client, Operation operation) + { + switch (operation.Type) + { + case Operation.OperationType.NsiCommon_ExportNsiItem_15_7_0_1: + return new ExportNsiItemGetter_15_7_0_1(client, scope, operation); + } + + throw new NotImplementedException($"Не удалось создать получателя результата операции типа {operation.Type}"); + } + } + +} diff --git a/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterResponse.cs b/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterResponse.cs new file mode 100644 index 0000000..b74a0c6 --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultGetters/ResultGetterResponse.cs @@ -0,0 +1,9 @@ +namespace Hcs.WebApp.BackgroundServices.ResultGetters +{ + public enum ResultGetterResponse + { + NotReady, + Successful, + Failed + } +} diff --git a/Hcs.WebApp/BackgroundServices/ResultWaitService.cs b/Hcs.WebApp/BackgroundServices/ResultWaitService.cs index 2fbf31b..c2c353f 100644 --- a/Hcs.WebApp/BackgroundServices/ResultWaitService.cs +++ b/Hcs.WebApp/BackgroundServices/ResultWaitService.cs @@ -1,17 +1,32 @@ 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; namespace Hcs.WebApp.BackgroundServices { - public class ResultWaitService(ResultWaitState state, IServiceScopeFactory scopeFactory) : BackgroundService + public class ResultWaitService( + ResultWaitState state, + ResultGetterFactory resultGetterFactory, + IServiceScopeFactory scopeFactory) : BackgroundService { + private class WaitState + { + public int attempt; + public int timer; + public bool done; + } + + private const int ITERATION_TIME = 10000; private const int SLEEP_TIME = 30000; private readonly ResultWaitState state = state; + private readonly ResultGetterFactory resultGetterFactory = resultGetterFactory; private readonly IServiceScopeFactory scopeFactory = scopeFactory; + private readonly List<(Operation operation, WaitState state)> entries = []; private Broker.Logger.ILogger logger; private IMessageCapturer messageCapturer; @@ -28,9 +43,60 @@ namespace Hcs.WebApp.BackgroundServices while (state.TryDequeueOperation(out var operation)) { if (stoppingToken.IsCancellationRequested) return; + + entries.Add(new (operation, new WaitState())); } - await Task.Delay(SLEEP_TIME, stoppingToken); + 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 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(); + switch (response) + { + case ResultGetterResponse.Successful: + case ResultGetterResponse.Failed: + entry.state.done = true; + break; + } + } + catch (Exception e) + { + var headquartersService = scope.ServiceProvider.GetRequiredService(); + await headquartersService.SetOperationEndedWithFailAsync(entry.operation.Id, e.Message); + + entry.state.done = true; + } + + entry.state.attempt++; + entry.state.timer = 0; + } + } + + entries.RemoveAll(x => x.state.done); + } + else + { + await Task.Delay(SLEEP_TIME, stoppingToken); + } } } diff --git a/Hcs.WebApp/BackgroundServices/ResultWaiters/IResultWaiter.cs b/Hcs.WebApp/BackgroundServices/ResultWaiters/IResultWaiter.cs deleted file mode 100644 index 135f494..0000000 --- a/Hcs.WebApp/BackgroundServices/ResultWaiters/IResultWaiter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Hcs.WebApp.BackgroundServices.ResultWaiters -{ - public interface IResultWaiter - { - // TODO - } -} diff --git a/Hcs.WebApp/Program.cs b/Hcs.WebApp/Program.cs index 9c8b213..63a0857 100644 --- a/Hcs.WebApp/Program.cs +++ b/Hcs.WebApp/Program.cs @@ -1,6 +1,7 @@ using Hcs.WebApp.BackgroundServices; using Hcs.WebApp.BackgroundServices.CampaignManagers; using Hcs.WebApp.BackgroundServices.OperationExecutors; +using Hcs.WebApp.BackgroundServices.ResultGetters; using Hcs.WebApp.Components; using Hcs.WebApp.Components.Shared; using Hcs.WebApp.Data.Hcs; @@ -68,6 +69,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddHostedService();