diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs b/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs index 45359c8..f8bd9d5 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs @@ -27,24 +27,38 @@ namespace Hcs.WebApp.BackgroundServices if (stoppingToken.IsCancellationRequested) return; using var scope = scopeFactory.CreateScope(); - try + var manager = managerFactory.CreateManager(campaign); + if (manager != null) { - var manager = managerFactory.CreateManager(campaign); - await manager.StartAsync(scope); + try + { + await manager.StartAsync(scope); - managers.Add(manager); + managers.Add(manager); + } + catch (Exception e) + { + await manager.EndWithFailAsync(scope, e); + } } - catch (Exception e) + else { var headquartersService = scope.ServiceProvider.GetRequiredService(); - await headquartersService.SetCampaignEndedWithFailAsync(campaign.Id, e.Message); + await headquartersService.SetCampaignEndedWithFailAsync(campaign.Id, "Не удалось найти подходящий менеджер кампании"); } } foreach (var manager in managers) { using var scope = scopeFactory.CreateScope(); - await manager.CheckStateAsync(scope); + try + { + await manager.CheckStateAsync(scope); + } + catch (Exception e) + { + await manager.EndWithFailAsync(scope, e); + } } managers.RemoveAll(x => x.State == IManager.ManagerState.Ended); diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportCommonRegistryElementsManager_15_7_0_1.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportCommonRegistryElementsManager_15_7_0_1.cs index c7f88d6..07ded7f 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportCommonRegistryElementsManager_15_7_0_1.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ExportCommonRegistryElementsManager_15_7_0_1.cs @@ -3,8 +3,16 @@ using Hcs.WebApp.Services; namespace Hcs.WebApp.BackgroundServices.CampaignManagers { - public class ExportCommonRegistryElementsManager_15_7_0_1(OperationExecutionState operationExecutionState, Campaign campaign) : ManagerBase(operationExecutionState, campaign) + public class ExportCommonRegistryElementsManager_15_7_0_1(OperationExecutionState operationExecutionState, ResultWaitState resultWaitState, Campaign campaign) : ManagerBase(operationExecutionState, resultWaitState, campaign) { + private enum Step + { + Start = 0, + Execution = 1, + Wait = 2, + End = 3 + } + public override async Task StartAsync(IServiceScope scope) { if (campaign.StartedAt.HasValue) @@ -22,10 +30,14 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers try { await headquartersService.SetCampaignStartedAsync(context, campaign.Id); - await headquartersService.SetCampaignStepAsync(context, campaign.Id, 1); + + campaign.Step = (int)Step.Execution; + await headquartersService.UpdateCampaignStepAsync(context, campaign); + var registryCount = await registryService.GetRegistryCountAsync(context, true); operations = await headquartersService.InitiateOperationsAsync(context, registryCount, campaign.Id, Operation.OperationType.NsiCommon_ExportNsiItem_15_7_0_1); await registryService.SetOperationsToRegistriesAsync(context, true, operations); + await transaction.CommitAsync(); } catch @@ -50,13 +62,51 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers { if (State == IManager.ManagerState.Started) { - var headquartersService = scope.ServiceProvider.GetRequiredService(); - var hasActiveOperations = await headquartersService.HasActiveOperationsAsync(campaign.Id); - if (!hasActiveOperations) + switch ((Step)campaign.Step) { - await headquartersService.SetCampaignEndedAsync(campaign.Id); + case Step.Execution: + if (!operationExecutionState.HasCampaignOperation(campaign.Id)) + { + campaign.Step = (int)Step.Wait; + var headquartersService = scope.ServiceProvider.GetRequiredService(); + await headquartersService.UpdateCampaignStepAsync(campaign); - State = IManager.ManagerState.Ended; + var operations = await headquartersService.GetOperationsAsync(campaign.Id); + foreach (var operation in operations) + { + if (!string.IsNullOrEmpty(operation.MessageGuid)) + { + resultWaitState.EnqueueOperation(operation); + } + } + } + break; + + case Step.Wait: + if (!resultWaitState.HasCampaignOperation(campaign.Id)) + { + var headquartersService = scope.ServiceProvider.GetRequiredService(); + using var context = headquartersService.GetNewContext(); + using var transaction = await context.Database.BeginTransactionAsync(); + try + { + campaign.Step = (int)Step.End; + await headquartersService.UpdateCampaignStepAsync(context, campaign); + + await headquartersService.SetCampaignEndedAsync(campaign.Id); + + await transaction.CommitAsync(); + } + catch + { + transaction?.Rollback(); + + throw; + } + + State = IManager.ManagerState.Ended; + } + break; } } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs index a96ab44..7154935 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs @@ -14,5 +14,7 @@ Task StartAsync(IServiceScope scope); Task CheckStateAsync(IServiceScope scope); + + Task EndWithFailAsync(IServiceScope scope, Exception e); } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs index a7277b2..bbe097e 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs @@ -1,10 +1,12 @@ using Hcs.WebApp.Data.Hcs; +using Hcs.WebApp.Services; namespace Hcs.WebApp.BackgroundServices.CampaignManagers { - public abstract class ManagerBase(OperationExecutionState operationExecutionState, Campaign campaign) : IManager + public abstract class ManagerBase(OperationExecutionState operationExecutionState, ResultWaitState resultWaitState, Campaign campaign) : IManager { protected readonly OperationExecutionState operationExecutionState = operationExecutionState; + protected readonly ResultWaitState resultWaitState = resultWaitState; protected readonly Campaign campaign = campaign; public IManager.ManagerState State { get; protected set; } = IManager.ManagerState.Created; @@ -12,5 +14,13 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers public abstract Task StartAsync(IServiceScope scope); public abstract Task CheckStateAsync(IServiceScope scope); + + public async Task EndWithFailAsync(IServiceScope scope, Exception e) + { + State = IManager.ManagerState.Ended; + + var headquartersService = scope.ServiceProvider.GetRequiredService(); + await headquartersService.SetCampaignEndedWithFailAsync(campaign.Id, e.Message); + } } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs index d47f816..bddf285 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerFactory.cs @@ -2,19 +2,20 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers { - public class ManagerFactory(OperationExecutionState state) + public class ManagerFactory(OperationExecutionState operationExecutionState, ResultWaitState resultWaitState) { - protected readonly OperationExecutionState state = state; + protected readonly OperationExecutionState operationExecutionState = operationExecutionState; + protected readonly ResultWaitState resultWaitState; - public IManager CreateManager(Campaign campaign) + public IManager? CreateManager(Campaign campaign) { switch (campaign.Type) { case Campaign.CampaignType.ExportCommonRegistryElements_15_7_0_1: - return new ExportCommonRegistryElementsManager_15_7_0_1(state, campaign); + return new ExportCommonRegistryElementsManager_15_7_0_1(operationExecutionState, resultWaitState, campaign); } - throw new NotImplementedException(); + return null; } } } diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutionState.cs b/Hcs.WebApp/BackgroundServices/OperationExecutionState.cs index e59db0d..a1480f2 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutionState.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutionState.cs @@ -16,5 +16,10 @@ namespace Hcs.WebApp.BackgroundServices { return operations.TryDequeue(out operation); } + + public bool HasCampaignOperation(int campaignId) + { + return operations.Any(x => x.CampaignId == campaignId); + } } } diff --git a/Hcs.WebApp/BackgroundServices/ResultWaitService.cs b/Hcs.WebApp/BackgroundServices/ResultWaitService.cs new file mode 100644 index 0000000..554d772 --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultWaitService.cs @@ -0,0 +1,7 @@ +namespace Hcs.WebApp.BackgroundServices +{ + public class ResultWaitService + { + // TODO + } +} diff --git a/Hcs.WebApp/BackgroundServices/ResultWaitState.cs b/Hcs.WebApp/BackgroundServices/ResultWaitState.cs new file mode 100644 index 0000000..f88c3d8 --- /dev/null +++ b/Hcs.WebApp/BackgroundServices/ResultWaitState.cs @@ -0,0 +1,25 @@ +using Hcs.WebApp.Data.Hcs; +using System.Collections.Concurrent; + +namespace Hcs.WebApp.BackgroundServices +{ + public class ResultWaitState + { + private readonly ConcurrentQueue operations = new(); + + public void EnqueueOperation(Operation operation) + { + operations.Enqueue(operation); + } + + public bool TryDequeueOperation(out Operation operation) + { + return operations.TryDequeue(out operation); + } + + public bool HasCampaignOperation(int campaignId) + { + return operations.Any(x => x.CampaignId == campaignId); + } + } +} diff --git a/Hcs.WebApp/Data/Hcs/Registry.cs b/Hcs.WebApp/Data/Hcs/Registry.cs index 2147757..22bff16 100644 --- a/Hcs.WebApp/Data/Hcs/Registry.cs +++ b/Hcs.WebApp/Data/Hcs/Registry.cs @@ -16,8 +16,6 @@ public virtual Operation LastSyncOperation { get; set; } - public string? LastSyncError { get; set; } - public virtual ICollection Elements { get; set; } = []; } } diff --git a/Hcs.WebApp/Services/HeadquartersService.cs b/Hcs.WebApp/Services/HeadquartersService.cs index a52a323..3a982f5 100644 --- a/Hcs.WebApp/Services/HeadquartersService.cs +++ b/Hcs.WebApp/Services/HeadquartersService.cs @@ -11,12 +11,6 @@ namespace Hcs.WebApp.Services return await context.Campaigns.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue); } - public async Task HasActiveOperationsAsync(int campaignId) - { - using var context = GetNewContext(); - return await context.Operations.CountAsync(x => x.CampaignId == campaignId && !x.EndedAt.HasValue) > 0; - } - public async Task> GetNotEndedCampaignsAsync() { using var context = GetNewContext(); @@ -33,6 +27,14 @@ namespace Hcs.WebApp.Services select operation).ToListAsync(); } + public async Task> GetOperationsAsync(int campaignId) + { + using var context = GetNewContext(); + return await (from operation in context.Operations + where operation.CampaignId == campaignId + select operation).ToListAsync(); + } + public async Task InitiateCampaignAsync(Campaign.CampaignType type, string initiatorId) { using var context = GetNewContext(); @@ -137,12 +139,18 @@ namespace Hcs.WebApp.Services } } - public async Task SetCampaignStepAsync(HcsDbContext context, int campaignId, int step) + public async Task UpdateCampaignStepAsync(Campaign campaign) { - var campaign = await context.Campaigns.FirstOrDefaultAsync(x => x.Id == campaignId); - if (campaign != null) + using var context = GetNewContext(); + await UpdateCampaignStepAsync(context, campaign); + } + + public async Task UpdateCampaignStepAsync(HcsDbContext context, Campaign campaign) + { + var targetCampaign = await context.Campaigns.FirstOrDefaultAsync(x => x.Id == campaign.Id); + if (targetCampaign != null) { - campaign.Step = step; + targetCampaign.Step = campaign.Step; await context.SaveChangesAsync(); }