Change events args

This commit is contained in:
2025-11-05 18:17:19 +09:00
parent 8fd7d4a571
commit e58221c430
11 changed files with 96 additions and 65 deletions

View File

@ -5,7 +5,6 @@ using Hcs.WebApp.BackgroundServices.OperationExecutors;
using Hcs.WebApp.Config; using Hcs.WebApp.Config;
using Hcs.WebApp.Services; using Hcs.WebApp.Services;
using Hcs.WebApp.Utils; using Hcs.WebApp.Utils;
using System.Transactions;
namespace Hcs.WebApp.BackgroundServices namespace Hcs.WebApp.BackgroundServices
{ {
@ -40,25 +39,28 @@ namespace Hcs.WebApp.BackgroundServices
var messageGuid = string.Empty; var messageGuid = string.Empty;
try try
{ {
var startedAt = DateTime.UtcNow;
await headquartersService.SetOperationStartedAsync(operation.Id, startedAt);
state.InvokeOnOperationStarted(operation.Id, operation.CampaignId, startedAt);
var executor = executorFactory.CreateExecutor(scope, client, operation); var executor = executorFactory.CreateExecutor(scope, client, operation);
await headquartersService.SetOperationStartedAsync(operation.Id);
state.InvokeOnOperationStarted(operation);
messageGuid = await executor.ExecuteAsync(stoppingToken); messageGuid = await executor.ExecuteAsync(stoppingToken);
} }
catch (Exception e) catch (Exception e)
{ {
await headquartersService.SetOperationEndedWithFailAsync(operation.Id, e.CombineMessages()); var endedAt = DateTime.UtcNow;
var failureReason = e.CombineMessages();
await headquartersService.SetOperationEndedWithFailAsync(operation.Id, endedAt, failureReason);
state.InvokeOnOperationEnded(operation); state.InvokeOnOperationEnded(operation.Id, operation.CampaignId, endedAt, failureReason);
} }
if (!string.IsNullOrEmpty(messageGuid)) if (!string.IsNullOrEmpty(messageGuid))
{ {
await headquartersService.SetOperationMessageGuidAsync(operation.Id, messageGuid); await headquartersService.SetOperationMessageGuidAsync(operation.Id, messageGuid);
state.InvokeOnOperationExecuted(operation); state.InvokeOnOperationExecuted(operation.Id, operation.CampaignId, messageGuid);
} }
state.UnsetProcessingOperation(operation); state.UnsetProcessingOperation(operation);

View File

@ -5,14 +5,18 @@ namespace Hcs.WebApp.BackgroundServices
{ {
public class OperationExecutionState public class OperationExecutionState
{ {
public delegate void OperationStarted(int operationId, int campaignId, DateTime startedAt);
public delegate void OperationExecuted(int operationId, int campaignId, string messageGuid);
public delegate void OperationEnded(int operationId, int campaignId, DateTime endedAt, string failureReason);
private readonly ConcurrentQueue<Operation> operationsInQueue = new(); private readonly ConcurrentQueue<Operation> operationsInQueue = new();
private readonly HashSet<Operation> operationsInProcess = []; private readonly HashSet<Operation> operationsInProcess = [];
public bool Ready { get; set; } public bool Ready { get; set; }
public event Action<Operation> OnOperationStarted; public event OperationStarted OnOperationStarted;
public event Action<Operation> OnOperationExecuted; public event OperationExecuted OnOperationExecuted;
public event Action<Operation> OnOperationEnded; public event OperationEnded OnOperationEnded;
public void EnqueueOperation(Operation operation) public void EnqueueOperation(Operation operation)
{ {
@ -39,29 +43,29 @@ namespace Hcs.WebApp.BackgroundServices
return operationsInQueue.Any(x => x.CampaignId == campaignId) || operationsInProcess.Any(x => x.CampaignId == campaignId); return operationsInQueue.Any(x => x.CampaignId == campaignId) || operationsInProcess.Any(x => x.CampaignId == campaignId);
} }
public void InvokeOnOperationStarted(Operation operation) public void InvokeOnOperationStarted(int operationId, int campaignId, DateTime startedAt)
{ {
try try
{ {
OnOperationStarted?.Invoke(operation); OnOperationStarted?.Invoke(operationId, campaignId, startedAt);
} }
catch { } catch { }
} }
public void InvokeOnOperationExecuted(Operation operation) public void InvokeOnOperationExecuted(int operationId, int campaignId, string messageGuid)
{ {
try try
{ {
OnOperationExecuted?.Invoke(operation); OnOperationExecuted?.Invoke(operationId, campaignId, messageGuid);
} }
catch { } catch { }
} }
public void InvokeOnOperationEnded(Operation operation) public void InvokeOnOperationEnded(int operationId, int campaignId, DateTime endedAt, string failureReason)
{ {
try try
{ {
OnOperationEnded?.Invoke(operation); OnOperationEnded?.Invoke(operationId, campaignId, endedAt, failureReason);
} }
catch { } catch { }
} }

View File

@ -2,6 +2,6 @@
{ {
public interface IResultGetter public interface IResultGetter
{ {
Task<bool> GetAsync(); Task<ResultGetterResponse> GetAsync();
} }
} }

View File

@ -8,16 +8,21 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters.Nsi
{ {
public class ExportDataProviderNsiItemGetter_15_7_0_1(IClient client, IServiceScope scope, Operation operation) : ResultGetterBase(client, scope, operation) public class ExportDataProviderNsiItemGetter_15_7_0_1(IClient client, IServiceScope scope, Operation operation) : ResultGetterBase(client, scope, operation)
{ {
public override async Task<bool> GetAsync() public override async Task<ResultGetterResponse> GetAsync()
{ {
var result = await client.Nsi.GetExportDataProviderNsiItemResultAsync(operation.MessageGuid!); var result = await client.Nsi.GetExportDataProviderNsiItemResultAsync(operation.MessageGuid!);
if (!result.Ready) if (!result.Ready)
{ {
return false; return new ResultGetterResponse()
{
success = false
};
} }
if (result.Success) if (result.Success)
{ {
DateTime endedAt = default;
var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>(); var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
var registryService = scope.ServiceProvider.GetRequiredService<RegistryService>(); var registryService = scope.ServiceProvider.GetRequiredService<RegistryService>();
@ -47,7 +52,8 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters.Nsi
} }
await context.SaveChangesAsync(); await context.SaveChangesAsync();
await headquartersService.SetOperationEndedAsync(context, operation.Id); endedAt = DateTime.UtcNow;
await headquartersService.SetOperationEndedAsync(context, operation.Id, endedAt);
await transaction.CommitAsync(); await transaction.CommitAsync();
} }
@ -59,7 +65,11 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters.Nsi
} }
}); });
return true; return new ResultGetterResponse()
{
success = true,
endedAt = endedAt
};
} }
throw Failure(result.ErrorMessage); throw Failure(result.ErrorMessage);

View File

@ -8,16 +8,21 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters.NsiCommon
{ {
public class ExportNsiItemGetter_15_7_0_1(IClient client, IServiceScope scope, Operation operation) : ResultGetterBase(client, scope, operation) public class ExportNsiItemGetter_15_7_0_1(IClient client, IServiceScope scope, Operation operation) : ResultGetterBase(client, scope, operation)
{ {
public override async Task<bool> GetAsync() public override async Task<ResultGetterResponse> GetAsync()
{ {
var result = await client.NsiCommon.GetExportNsiItemResultAsync(operation.MessageGuid!); var result = await client.NsiCommon.GetExportNsiItemResultAsync(operation.MessageGuid!);
if (!result.Ready) if (!result.Ready)
{ {
return false; return new ResultGetterResponse()
{
success = false
};
} }
if (result.Success) if (result.Success)
{ {
DateTime endedAt = default;
var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>(); var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
var registryService = scope.ServiceProvider.GetRequiredService<RegistryService>(); var registryService = scope.ServiceProvider.GetRequiredService<RegistryService>();
@ -47,7 +52,8 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters.NsiCommon
} }
await context.SaveChangesAsync(); await context.SaveChangesAsync();
await headquartersService.SetOperationEndedAsync(context, operation.Id); endedAt = DateTime.UtcNow;
await headquartersService.SetOperationEndedAsync(context, operation.Id, endedAt);
await transaction.CommitAsync(); await transaction.CommitAsync();
} }
@ -59,7 +65,11 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters.NsiCommon
} }
}); });
return true; return new ResultGetterResponse()
{
success = true,
endedAt = endedAt
};
} }
throw Failure(result.ErrorMessage); throw Failure(result.ErrorMessage);

View File

@ -10,7 +10,7 @@ namespace Hcs.WebApp.BackgroundServices.ResultGetters
protected readonly IServiceScope scope = scope; protected readonly IServiceScope scope = scope;
protected readonly Operation operation = operation; protected readonly Operation operation = operation;
public abstract Task<bool> GetAsync(); public abstract Task<ResultGetterResponse> GetAsync();
protected Exception Failure(IErrorMessage? errorMessage) protected Exception Failure(IErrorMessage? errorMessage)
{ {

View File

@ -0,0 +1,8 @@
namespace Hcs.WebApp.BackgroundServices.ResultGetters
{
public struct ResultGetterResponse
{
public bool success;
public DateTime endedAt;
}
}

View File

@ -59,6 +59,9 @@ namespace Hcs.WebApp.BackgroundServices
{ {
entry.state.timer += ITERATION_TIME; entry.state.timer += ITERATION_TIME;
var endedAt = DateTime.MinValue;
var failureReason = string.Empty;
var send = entry.state.attempt switch var send = entry.state.attempt switch
{ {
0 => entry.state.timer >= 10000, 0 => entry.state.timer >= 10000,
@ -72,20 +75,22 @@ namespace Hcs.WebApp.BackgroundServices
try try
{ {
var resultGetter = resultGetterFactory.CreateResultGetter(scope, client, entry.operation); var resultGetter = resultGetterFactory.CreateResultGetter(scope, client, entry.operation);
var success = await resultGetter.GetAsync(); var response = await resultGetter.GetAsync();
if (success) if (response.success)
{ {
entry.state.done = true; entry.state.done = true;
endedAt = response.endedAt;
} }
} }
catch (Exception e) catch (Exception e)
{ {
var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>(); var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
await headquartersService.SetOperationEndedWithFailAsync(entry.operation.Id, e.CombineMessages()); endedAt = DateTime.UtcNow;
failureReason = e.CombineMessages();
await headquartersService.SetOperationEndedWithFailAsync(entry.operation.Id, endedAt, failureReason);
entry.state.done = true; entry.state.done = true;
state.InvokeOnOperationEnded(entry.operation);
} }
entry.state.attempt++; entry.state.attempt++;
@ -95,7 +100,7 @@ namespace Hcs.WebApp.BackgroundServices
if (entry.state.done) if (entry.state.done)
{ {
state.UnsetProcessingOperation(entry.operation); state.UnsetProcessingOperation(entry.operation);
state.InvokeOnOperationEnded(entry.operation); state.InvokeOnOperationEnded(entry.operation.Id, entry.operation.CampaignId, endedAt, failureReason);
} }
} }

View File

@ -5,12 +5,14 @@ namespace Hcs.WebApp.BackgroundServices
{ {
public class ResultWaitState public class ResultWaitState
{ {
public delegate void OperationEnded(int operationId, int campaignId, DateTime endedAt, string failureReason);
private readonly ConcurrentQueue<Operation> operationsInQueue = new(); private readonly ConcurrentQueue<Operation> operationsInQueue = new();
private readonly HashSet<Operation> operationsInProcess = []; private readonly HashSet<Operation> operationsInProcess = [];
public bool Ready { get; set; } public bool Ready { get; set; }
public event Action<Operation> OnOperationEnded; public event OperationEnded OnOperationEnded;
public void EnqueueOperation(Operation operation) public void EnqueueOperation(Operation operation)
{ {
@ -37,11 +39,11 @@ namespace Hcs.WebApp.BackgroundServices
return operationsInQueue.Any(x => x.CampaignId == campaignId) || operationsInProcess.Any(x => x.CampaignId == campaignId); return operationsInQueue.Any(x => x.CampaignId == campaignId) || operationsInProcess.Any(x => x.CampaignId == campaignId);
} }
public void InvokeOnOperationEnded(Operation operation) public void InvokeOnOperationEnded(int operationId, int campaignId, DateTime endedAt, string failureReason)
{ {
try try
{ {
OnOperationEnded?.Invoke(operation); OnOperationEnded?.Invoke(operationId, campaignId, endedAt, failureReason);
} }
catch { } catch { }
} }

View File

@ -146,8 +146,6 @@
{ {
targetCampaign.StartedAt = campaign.StartedAt; targetCampaign.StartedAt = campaign.StartedAt;
StateHasChanged();
campaignsDataGrid.Reload(); campaignsDataGrid.Reload();
} }
@ -168,8 +166,6 @@
targetCampaign.Step = campaign.Step; targetCampaign.Step = campaign.Step;
targetCampaign.Progress = campaign.Progress; targetCampaign.Progress = campaign.Progress;
StateHasChanged();
campaignsDataGrid.Reload(); campaignsDataGrid.Reload();
if (expandedCampaign != null && expandedCampaign.Id == campaign.Id) if (expandedCampaign != null && expandedCampaign.Id == campaign.Id)
@ -185,18 +181,16 @@
Task.Run(RefreshCampaigns); Task.Run(RefreshCampaigns);
} }
void OnOperationStarted(Operation operation) void OnOperationStarted(int operationId, int campaignId, DateTime startedAt)
{ {
InvokeAsync(() => InvokeAsync(() =>
{ {
if (expandedCampaign != null && expandedCampaign.Id == operation.CampaignId) if (expandedCampaign != null && expandedCampaign.Id == campaignId)
{ {
var targetOperation = expandedCampaign.Operations?.FirstOrDefault(x => x.Id == operation.Id); var targetOperation = expandedCampaign.Operations?.FirstOrDefault(x => x.Id == operationId);
if (targetOperation != null) if (targetOperation != null)
{ {
targetOperation.StartedAt = operation.StartedAt; targetOperation.StartedAt = startedAt;
StateHasChanged();
operationsDataGrid.Reload(); operationsDataGrid.Reload();
} }
@ -204,18 +198,16 @@
}); });
} }
void OnOperationExecuted(Operation operation) void OnOperationExecuted(int operationId, int campaignId, string messageGuid)
{ {
InvokeAsync(() => InvokeAsync(() =>
{ {
if (expandedCampaign != null && expandedCampaign.Id == operation.CampaignId) if (expandedCampaign != null && expandedCampaign.Id == campaignId)
{ {
var targetOperation = expandedCampaign.Operations?.FirstOrDefault(x => x.Id == operation.Id); var targetOperation = expandedCampaign.Operations?.FirstOrDefault(x => x.Id == operationId);
if (targetOperation != null) if (targetOperation != null)
{ {
targetOperation.MessageGuid = operation.MessageGuid; targetOperation.MessageGuid = messageGuid;
StateHasChanged();
operationsDataGrid.Reload(); operationsDataGrid.Reload();
} }
@ -223,19 +215,17 @@
}); });
} }
void OnOperationEnded(Operation operation) void OnOperationEnded(int operationId, int campaignId, DateTime endedAt, string failureReason)
{ {
InvokeAsync(() => InvokeAsync(() =>
{ {
if (expandedCampaign != null && expandedCampaign.Id == operation.CampaignId) if (expandedCampaign != null && expandedCampaign.Id == campaignId)
{ {
var targetOperation = expandedCampaign.Operations?.FirstOrDefault(x => x.Id == operation.Id); var targetOperation = expandedCampaign.Operations?.FirstOrDefault(x => x.Id == operationId);
if (targetOperation != null) if (targetOperation != null)
{ {
targetOperation.EndedAt = operation.EndedAt; targetOperation.EndedAt = endedAt;
targetOperation.FailureReason = operation.FailureReason; targetOperation.FailureReason = failureReason;
StateHasChanged();
operationsDataGrid.Reload(); operationsDataGrid.Reload();
} }

View File

@ -91,13 +91,13 @@ namespace Hcs.WebApp.Services
} }
} }
public async Task SetOperationStartedAsync(int operationId) public async Task SetOperationStartedAsync(int operationId, DateTime startedAt)
{ {
using var context = GetNewContext(); using var context = GetNewContext();
var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId);
if (operation != null) if (operation != null)
{ {
operation.StartedAt = DateTime.UtcNow; operation.StartedAt = startedAt;
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
@ -133,24 +133,24 @@ namespace Hcs.WebApp.Services
} }
} }
public async Task SetOperationEndedAsync(HcsDbContext context, int operationId) public async Task SetOperationEndedAsync(HcsDbContext context, int operationId, DateTime endedAt)
{ {
var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId);
if (operation != null) if (operation != null)
{ {
operation.EndedAt = DateTime.UtcNow; operation.EndedAt = endedAt;
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task SetOperationEndedWithFailAsync(int operationId, string failureReason) public async Task SetOperationEndedWithFailAsync(int operationId, DateTime endedAt, string failureReason)
{ {
using var context = GetNewContext(); using var context = GetNewContext();
var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId);
if (operation != null) if (operation != null)
{ {
operation.EndedAt = DateTime.UtcNow; operation.EndedAt = endedAt;
operation.FailureReason = failureReason; operation.FailureReason = failureReason;
await context.SaveChangesAsync(); await context.SaveChangesAsync();