Add result wait service

This commit is contained in:
2025-10-30 12:25:03 +09:00
parent 3bd7341ecc
commit e31a075f58
10 changed files with 152 additions and 32 deletions

View File

@ -27,24 +27,38 @@ namespace Hcs.WebApp.BackgroundServices
if (stoppingToken.IsCancellationRequested) return; if (stoppingToken.IsCancellationRequested) return;
using var scope = scopeFactory.CreateScope(); using var scope = scopeFactory.CreateScope();
try var manager = managerFactory.CreateManager(campaign);
if (manager != null)
{ {
var manager = managerFactory.CreateManager(campaign); try
await manager.StartAsync(scope); {
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<HeadquartersService>(); var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
await headquartersService.SetCampaignEndedWithFailAsync(campaign.Id, e.Message); await headquartersService.SetCampaignEndedWithFailAsync(campaign.Id, "Не удалось найти подходящий менеджер кампании");
} }
} }
foreach (var manager in managers) foreach (var manager in managers)
{ {
using var scope = scopeFactory.CreateScope(); 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); managers.RemoveAll(x => x.State == IManager.ManagerState.Ended);

View File

@ -3,8 +3,16 @@ using Hcs.WebApp.Services;
namespace Hcs.WebApp.BackgroundServices.CampaignManagers 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) public override async Task StartAsync(IServiceScope scope)
{ {
if (campaign.StartedAt.HasValue) if (campaign.StartedAt.HasValue)
@ -22,10 +30,14 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers
try try
{ {
await headquartersService.SetCampaignStartedAsync(context, campaign.Id); 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); var registryCount = await registryService.GetRegistryCountAsync(context, true);
operations = await headquartersService.InitiateOperationsAsync(context, registryCount, campaign.Id, Operation.OperationType.NsiCommon_ExportNsiItem_15_7_0_1); operations = await headquartersService.InitiateOperationsAsync(context, registryCount, campaign.Id, Operation.OperationType.NsiCommon_ExportNsiItem_15_7_0_1);
await registryService.SetOperationsToRegistriesAsync(context, true, operations); await registryService.SetOperationsToRegistriesAsync(context, true, operations);
await transaction.CommitAsync(); await transaction.CommitAsync();
} }
catch catch
@ -50,13 +62,51 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers
{ {
if (State == IManager.ManagerState.Started) if (State == IManager.ManagerState.Started)
{ {
var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>(); switch ((Step)campaign.Step)
var hasActiveOperations = await headquartersService.HasActiveOperationsAsync(campaign.Id);
if (!hasActiveOperations)
{ {
await headquartersService.SetCampaignEndedAsync(campaign.Id); case Step.Execution:
if (!operationExecutionState.HasCampaignOperation(campaign.Id))
{
campaign.Step = (int)Step.Wait;
var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
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<HeadquartersService>();
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;
} }
} }
} }

View File

@ -14,5 +14,7 @@
Task StartAsync(IServiceScope scope); Task StartAsync(IServiceScope scope);
Task CheckStateAsync(IServiceScope scope); Task CheckStateAsync(IServiceScope scope);
Task EndWithFailAsync(IServiceScope scope, Exception e);
} }
} }

View File

@ -1,10 +1,12 @@
using Hcs.WebApp.Data.Hcs; using Hcs.WebApp.Data.Hcs;
using Hcs.WebApp.Services;
namespace Hcs.WebApp.BackgroundServices.CampaignManagers 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 OperationExecutionState operationExecutionState = operationExecutionState;
protected readonly ResultWaitState resultWaitState = resultWaitState;
protected readonly Campaign campaign = campaign; protected readonly Campaign campaign = campaign;
public IManager.ManagerState State { get; protected set; } = IManager.ManagerState.Created; 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 StartAsync(IServiceScope scope);
public abstract Task CheckStateAsync(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<HeadquartersService>();
await headquartersService.SetCampaignEndedWithFailAsync(campaign.Id, e.Message);
}
} }
} }

View File

@ -2,19 +2,20 @@
namespace Hcs.WebApp.BackgroundServices.CampaignManagers 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) switch (campaign.Type)
{ {
case Campaign.CampaignType.ExportCommonRegistryElements_15_7_0_1: 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;
} }
} }
} }

View File

@ -16,5 +16,10 @@ namespace Hcs.WebApp.BackgroundServices
{ {
return operations.TryDequeue(out operation); return operations.TryDequeue(out operation);
} }
public bool HasCampaignOperation(int campaignId)
{
return operations.Any(x => x.CampaignId == campaignId);
}
} }
} }

View File

@ -0,0 +1,7 @@
namespace Hcs.WebApp.BackgroundServices
{
public class ResultWaitService
{
// TODO
}
}

View File

@ -0,0 +1,25 @@
using Hcs.WebApp.Data.Hcs;
using System.Collections.Concurrent;
namespace Hcs.WebApp.BackgroundServices
{
public class ResultWaitState
{
private readonly ConcurrentQueue<Operation> 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);
}
}
}

View File

@ -16,8 +16,6 @@
public virtual Operation LastSyncOperation { get; set; } public virtual Operation LastSyncOperation { get; set; }
public string? LastSyncError { get; set; }
public virtual ICollection<RegistryElement> Elements { get; set; } = []; public virtual ICollection<RegistryElement> Elements { get; set; } = [];
} }
} }

View File

@ -11,12 +11,6 @@ namespace Hcs.WebApp.Services
return await context.Campaigns.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue); return await context.Campaigns.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue);
} }
public async Task<bool> HasActiveOperationsAsync(int campaignId)
{
using var context = GetNewContext();
return await context.Operations.CountAsync(x => x.CampaignId == campaignId && !x.EndedAt.HasValue) > 0;
}
public async Task<IEnumerable<Campaign>> GetNotEndedCampaignsAsync() public async Task<IEnumerable<Campaign>> GetNotEndedCampaignsAsync()
{ {
using var context = GetNewContext(); using var context = GetNewContext();
@ -33,6 +27,14 @@ namespace Hcs.WebApp.Services
select operation).ToListAsync(); select operation).ToListAsync();
} }
public async Task<IEnumerable<Operation>> GetOperationsAsync(int campaignId)
{
using var context = GetNewContext();
return await (from operation in context.Operations
where operation.CampaignId == campaignId
select operation).ToListAsync();
}
public async Task<Campaign> InitiateCampaignAsync(Campaign.CampaignType type, string initiatorId) public async Task<Campaign> InitiateCampaignAsync(Campaign.CampaignType type, string initiatorId)
{ {
using var context = GetNewContext(); 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); using var context = GetNewContext();
if (campaign != null) 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(); await context.SaveChangesAsync();
} }