Add operation executor

This commit is contained in:
2025-10-25 20:30:49 +09:00
parent 59470b49d1
commit 891d462af1
19 changed files with 144 additions and 52 deletions

View File

@ -0,0 +1,13 @@
using Hcs.Broker;
using Hcs.WebApp.Data.Hcs;
namespace Hcs.WebApp.BackgroundServices.Executors._15_7_0_1.NsiCommon
{
public class ExportAllRegistryElementsExecutor(IClient client, Operation operation) : ExecutorBase(client, operation)
{
public override async Task<string> ExecuteAsync(CancellationToken cancellationToken)
{
return await client.NsiCommon.RequestExportNsiItemAsync(1, Service.Async.NsiCommon.ListGroup.NSI, cancellationToken);
}
}
}

View File

@ -0,0 +1,13 @@
using Hcs.Broker;
using Hcs.WebApp.Data.Hcs;
namespace Hcs.WebApp.BackgroundServices.Executors
{
public abstract class ExecutorBase(IClient client, Operation operation) : IExecutor
{
protected readonly IClient client = client;
protected readonly Operation operation = operation;
public abstract Task<string> ExecuteAsync(CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,20 @@
using Hcs.Broker;
using Hcs.WebApp.BackgroundServices.Executors._15_7_0_1.NsiCommon;
using Hcs.WebApp.Data.Hcs;
namespace Hcs.WebApp.BackgroundServices.Executors
{
public class ExecutorFactory
{
public IExecutor CreateExecutor(IClient client, Operation operation)
{
switch (operation.Type)
{
case Operation.OperationType.NsiCommon_15_7_0_1_ExportAllRegistryElements:
return new ExportAllRegistryElementsExecutor(client, operation);
}
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,7 @@
namespace Hcs.WebApp.BackgroundServices.Executors
{
public interface IExecutor
{
Task<string> ExecuteAsync(CancellationToken cancellationToken);
}
}

View File

@ -1,6 +1,7 @@
using Hcs.Broker;
using Hcs.Broker.Logger;
using Hcs.Broker.MessageCapturer;
using Hcs.WebApp.BackgroundServices.Executors;
using Hcs.WebApp.Config;
using Hcs.WebApp.Services;
@ -12,6 +13,7 @@ namespace Hcs.WebApp.BackgroundServices
private readonly OperationExecutionState state = state;
private readonly IServiceScopeFactory scopeFactory = scopeFactory;
private readonly ExecutorFactory executorFactory = new();
private Broker.Logger.ILogger logger;
private IMessageCapturer messageCapturer;
@ -19,7 +21,7 @@ namespace Hcs.WebApp.BackgroundServices
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await InitializeState();
await InitializeStateAsync();
InitializeClient();
@ -30,19 +32,33 @@ namespace Hcs.WebApp.BackgroundServices
{
while (state.TryDequeueOperation(out var operation))
{
await operationService.SetOperationMessageGuid(operation.Id, Guid.NewGuid().ToString());
var messageGuid = string.Empty;
try
{
var executor = executorFactory.CreateExecutor(client, operation);
messageGuid = await executor.ExecuteAsync(stoppingToken);
}
catch
{
state.EnqueueOperation(operation);
}
if (!string.IsNullOrEmpty(messageGuid))
{
await operationService.SetOperationMessageGuidAsync(operation.Id, messageGuid);
}
}
await Task.Delay(SLEEP_TIME, stoppingToken);
}
}
private async Task InitializeState()
private async Task InitializeStateAsync()
{
using var scope = scopeFactory.CreateScope();
var operationService = scope.ServiceProvider.GetRequiredService<OperationService>();
var operations = await operationService.GetInitiatedOperations();
var operations = await operationService.GetInitiatedOperationsAsync();
foreach (var operation in operations)
{
state.EnqueueOperation(operation);

View File

@ -11,7 +11,7 @@
<AuthorizedContent Roles="@AppRole.ADMINISTRATOR_TYPE">
<Content>
<RadzenTemplateForm TItem="InputModel" Data=@Input Submit="@DoAddUser">
<RadzenTemplateForm TItem="InputModel" Data=@Input Submit="@DoAddUserAsync">
<RadzenAlert Visible="@hasError" AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false">
@errorMessage
</RadzenAlert>
@ -88,7 +88,7 @@
Input.Role = roles.First();
}
async Task DoAddUser(InputModel input)
async Task DoAddUserAsync(InputModel input)
{
if (inProgress) return;
@ -97,7 +97,7 @@
try
{
var result = await UsersService.CreateUser(input.UserName, input.Role.Name, input.Password);
var result = await UsersService.CreateUserAsync(input.UserName, input.Role.Name, input.Password);
if (result.Succeeded)
{
DialogService.Close(true);

View File

@ -12,7 +12,7 @@
<AuthorizedContent Roles="@AppRole.ADMINISTRATOR_TYPE">
<Content>
<RadzenTemplateForm TItem="InputModel" Data=@Input Submit="@DoEditUser">
<RadzenTemplateForm TItem="InputModel" Data=@Input Submit="@DoEditUserAsync">
<RadzenAlert Visible="@hasError" AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false">
@errorMessage
</RadzenAlert>
@ -90,7 +90,7 @@
roles = await RoleManager.Roles.OrderBy(x => x.Priority).ToListAsync();
}
async Task DoEditUser(InputModel input)
async Task DoEditUserAsync(InputModel input)
{
if (inProgress) return;
@ -99,7 +99,7 @@
try
{
await UsersService.UpdateUser(UserId, input.RoleId, input.Password);
await UsersService.UpdateUserAsync(UserId, input.RoleId, input.Password);
DialogService.Close(true);
}

View File

@ -13,7 +13,7 @@
<Tabs>
<RadzenTabsItem Text="Пароль">
<div style="max-width: 420px">
<RadzenTemplateForm TItem="PasswordInputModel" Data=@PasswordInput Method="post" Submit="@ChangePassword">
<RadzenTemplateForm TItem="PasswordInputModel" Data=@PasswordInput Method="post" Submit="@ChangePasswordAsync">
<RadzenAlert AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter" Visible="@hasError">
@errorMessage
</RadzenAlert>
@ -78,7 +78,7 @@
[SupplyParameterFromForm]
PasswordInputModel PasswordInput { get; set; } = new();
async Task ChangePassword()
async Task ChangePasswordAsync()
{
hasError = false;
hasSuccess = false;
@ -87,7 +87,7 @@
{
busyOverlay.Show();
await IdentityService.ChangePassword(PasswordInput.OldPassword, PasswordInput.NewPassword);
await IdentityService.ChangePasswordAsync(PasswordInput.OldPassword, PasswordInput.NewPassword);
hasSuccess = true;
}

View File

@ -24,7 +24,7 @@
</RadzenColumn>
<RadzenColumn Size="12" SizeMD="6">
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.End" Gap="0.5rem">
<RadzenButton Icon="add_circle_outline" Text="Создать" Click="@AddUser" ButtonStyle="ButtonStyle.Primary" />
<RadzenButton Icon="add_circle_outline" Text="Создать" Click="@AddUserAsync" ButtonStyle="ButtonStyle.Primary" />
</RadzenStack>
</RadzenColumn>
</RadzenRow>
@ -33,7 +33,7 @@
<RadzenAlert Visible="@hasError" AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false">
@errorMessage
</RadzenAlert>
<RadzenDataGrid TItem="AppUserWithRole" Data="@usersWithRoles" RowSelect="@EditUser" IsLoading="@isLoading" AllowFiltering="true" AllowPaging="true" ShowPagingSummary="true" PageSizeOptions=@(new int[] { 5, 10, 20, 30 }) AllowSorting="true">
<RadzenDataGrid TItem="AppUserWithRole" Data="@usersWithRoles" RowSelect="@EditUserAsync" IsLoading="@isLoading" AllowFiltering="true" AllowPaging="true" ShowPagingSummary="true" PageSizeOptions=@(new int[] { 5, 10, 20, 30 }) AllowSorting="true">
<Columns>
<RadzenDataGridColumn TItem="AppUserWithRole" Property="User.UserName" Title="Логин" />
<RadzenDataGridColumn TItem="AppUserWithRole" Property="Role.Name" Title="Роль" />
@ -41,7 +41,7 @@
<Template Context="userWithRole">
@if (userWithRole.User.Id != currentUserId)
{
<RadzenButton Click="@(() => DeleteUser(userWithRole))" @onclick:stopPropagation="true" ButtonStyle="ButtonStyle.Danger" Icon="close" Size="ButtonSize.Small" />
<RadzenButton Click="@(() => DeleteUserAsync(userWithRole))" @onclick:stopPropagation="true" ButtonStyle="ButtonStyle.Danger" Icon="close" Size="ButtonSize.Small" />
}
</Template>
</RadzenDataGridColumn>
@ -74,7 +74,7 @@
if (state.User.IsInRole(AppRole.ADMINISTRATOR_TYPE))
{
currentUserId = state.User.FindFirst(ClaimTypes.NameIdentifier)!.Value;
usersWithRoles = await UsersService.GetUsersWithRole();
usersWithRoles = await UsersService.GetUsersWithRoleAsync();
}
isLoading = false;
@ -83,7 +83,7 @@
}
}
async Task AddUser()
async Task AddUserAsync()
{
var success = await DialogService.OpenAsync<AddUser>(
"Создание пользователя",
@ -98,11 +98,11 @@
if (success)
{
await UpdateGrid();
await UpdateGridAsync();
}
}
async Task EditUser(AppUserWithRole userWithRole)
async Task EditUserAsync(AppUserWithRole userWithRole)
{
var success = await DialogService.OpenAsync<EditUser>(
"Редактирование пользователя",
@ -122,11 +122,11 @@
if (success)
{
await UpdateGrid();
await UpdateGridAsync();
}
}
async Task DeleteUser(AppUserWithRole userWithRole)
async Task DeleteUserAsync(AppUserWithRole userWithRole)
{
try
{
@ -141,10 +141,10 @@
{
isLoading = true;
var result = await UsersService.DeleteUser(userWithRole.User.Id);
var result = await UsersService.DeleteUserAsync(userWithRole.User.Id);
if (result.Succeeded)
{
await UpdateGrid();
await UpdateGridAsync();
}
else
{
@ -162,12 +162,12 @@
}
}
async Task UpdateGrid()
async Task UpdateGridAsync()
{
isLoading = true;
hasError = false;
usersWithRoles = await UsersService.GetUsersWithRole();
usersWithRoles = await UsersService.GetUsersWithRoleAsync();
isLoading = false;
}

View File

@ -29,7 +29,7 @@
</RadzenColumn>
<RadzenColumn Size="12" SizeMD="6">
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.End" Gap="0.5rem">
<RadzenButton Icon="sync" Text="@syncText" Disabled="@(state != CommonPageState.Idle)" Click="@SyncRegistries" ButtonStyle="ButtonStyle.Primary" />
<RadzenButton Icon="sync" Text="@syncText" Disabled="@(state != CommonPageState.Idle)" Click="@SyncRegistriesAsync" ButtonStyle="ButtonStyle.Primary" />
</RadzenStack>
</RadzenColumn>
</RadzenRow>
@ -38,14 +38,14 @@
<RadzenAlert Visible="@hasError" AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false">
@errorMessage
</RadzenAlert>
<RadzenDataGrid TItem="Registry" Data="@registries" RowSelect="@ViewRegistry" IsLoading="@(state != CommonPageState.Idle)" AllowFiltering="true" AllowPaging="true" ShowPagingSummary="true" PageSizeOptions=@(new int[] { 5, 10, 20, 30 }) AllowSorting="true">
<RadzenDataGrid TItem="Registry" Data="@registries" RowSelect="@ViewRegistryAsync" IsLoading="@(state != CommonPageState.Idle)" AllowFiltering="true" AllowPaging="true" ShowPagingSummary="true" PageSizeOptions=@(new int[] { 5, 10, 20, 30 }) AllowSorting="true">
<Columns>
<RadzenDataGridColumn TItem="Registry" Property="Number" Title="Номер" />
<RadzenDataGridColumn TItem="Registry" Property="Name" Title="Название" />
<RadzenDataGridColumn TItem="Registry" Filterable="false" Sortable="false" TextAlign="TextAlign.Center" Width="70px">
<Template Context="registry">
<RadzenButton Click="@(() => EditRegistry(registry))" @onclick:stopPropagation="true" ButtonStyle="ButtonStyle.Primary" Icon="edit" Size="ButtonSize.Small" />
<RadzenButton Click="@(() => SyncRegistry(registry))" @onclick:stopPropagation="true" ButtonStyle="ButtonStyle.Primary" Icon="sync" Size="ButtonSize.Small" />
<RadzenButton Click="@(() => EditRegistryAsync(registry))" @onclick:stopPropagation="true" ButtonStyle="ButtonStyle.Primary" Icon="edit" Size="ButtonSize.Small" />
<RadzenButton Click="@(() => SyncRegistryAsync(registry))" @onclick:stopPropagation="true" ButtonStyle="ButtonStyle.Primary" Icon="sync" Size="ButtonSize.Small" />
</Template>
</RadzenDataGridColumn>
</Columns>
@ -83,14 +83,14 @@
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (state.User.IsInRole(AppRole.ADMINISTRATOR_TYPE) || state.User.IsInRole(AppRole.OPERATOR_TYPE))
{
var operationInProgress = await OperationService.HasActiveOperation(Operation.OperationType.Nsi_15_7_0_1_ExportAllRegistryElements);
var operationInProgress = await OperationService.HasActiveOperationAsync(Operation.OperationType.NsiCommon_15_7_0_1_ExportAllRegistryElements);
if (operationInProgress)
{
finalState = CommonPageState.OperationWaiting;
}
else
{
registries = await RegistryService.GetAllRegistries(true);
registries = await RegistryService.GetAllRegistriesAsync(true);
}
OperationExecutionState.OnOperationStarted += OnOperationStarted;
@ -100,34 +100,34 @@
}
}
async Task SyncRegistries()
async Task SyncRegistriesAsync()
{
if (state == CommonPageState.OperationWaiting) return;
ChangeState(CommonPageState.OperationWaiting);
if (await OperationService.HasActiveOperation(Operation.OperationType.Nsi_15_7_0_1_ExportAllRegistryElements))
if (await OperationService.HasActiveOperationAsync(Operation.OperationType.NsiCommon_15_7_0_1_ExportAllRegistryElements))
{
ChangeState(CommonPageState.Idle);
}
else
{
var operation = await OperationService.InitiateOperation(Operation.OperationType.Nsi_15_7_0_1_ExportAllRegistryElements, "");
var operation = await OperationService.InitiateOperationAsync(Operation.OperationType.NsiCommon_15_7_0_1_ExportAllRegistryElements, "");
OperationExecutionState.EnqueueOperation(operation);
}
}
async Task ViewRegistry(Registry userWithRole)
async Task ViewRegistryAsync(Registry userWithRole)
{
// TODO
}
async Task EditRegistry(Registry registry)
async Task EditRegistryAsync(Registry registry)
{
// TODO
}
async Task SyncRegistry(Registry registry)
async Task SyncRegistryAsync(Registry registry)
{
// TODO
}
@ -163,7 +163,7 @@
void OnOperationStarted(Operation operation)
{
if (operation.Type == Operation.OperationType.Nsi_15_7_0_1_ExportAllRegistryElements)
if (operation.Type == Operation.OperationType.NsiCommon_15_7_0_1_ExportAllRegistryElements)
{
InvokeAsync(() => ChangeState(CommonPageState.OperationWaiting));
}

View File

@ -29,7 +29,7 @@
<RadzenStack JustifyContent="JustifyContent.SpaceBetween" Gap="1rem">
<RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Start" JustifyContent="JustifyContent.Normal">
<RadzenText TextStyle="TextStyle.H6">Сервис nsi</RadzenText>
<RadzenButton Click=@(() => OnNsiExportItem1Click()) Disabled=@inputDisabled Text="Экспорт НСИ 1" ButtonStyle="ButtonStyle.Primary" />
<RadzenButton Click="@OnNsiExportItem1ClickAsync" Disabled=@inputDisabled Text="Экспорт НСИ 1" ButtonStyle="ButtonStyle.Primary" />
</RadzenStack>
</RadzenStack>
</RadzenCard>
@ -92,7 +92,7 @@
}
}
async Task OnNsiExportItem1Click()
async Task OnNsiExportItem1ClickAsync()
{
try
{

View File

@ -6,7 +6,7 @@ namespace Hcs.WebApp.Data.Hcs
{
public enum OperationType
{
Nsi_15_7_0_1_ExportAllRegistryElements
NsiCommon_15_7_0_1_ExportAllRegistryElements
}
public int Id { get; set; }

View File

@ -7,7 +7,7 @@ namespace Hcs.WebApp.Services
private readonly Uri baseUri = new($"{navigationManager.BaseUri}identity/");
private readonly HttpClient httpClient = factory.CreateClient("WithIdentity");
public async Task ChangePassword(string oldPassword, string newPassword)
public async Task ChangePasswordAsync(string oldPassword, string newPassword)
{
var uri = new Uri($"{baseUri}change-password");
var content = new FormUrlEncodedContent(new Dictionary<string, string> {

View File

@ -7,13 +7,13 @@ namespace Hcs.WebApp.Services
{
private readonly IDbContextFactory<HcsDbContext> factory = factory;
public async Task<bool> HasActiveOperation(Operation.OperationType type)
public async Task<bool> HasActiveOperationAsync(Operation.OperationType type)
{
using var context = factory.CreateDbContext();
return await context.Operations.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue);
}
public async Task<Operation> InitiateOperation(Operation.OperationType type, string initiatorId)
public async Task<Operation> InitiateOperationAsync(Operation.OperationType type, string initiatorId)
{
using var context = factory.CreateDbContext();
var operation = new Operation()
@ -27,7 +27,7 @@ namespace Hcs.WebApp.Services
return operation;
}
public async Task<IEnumerable<Operation>> GetInitiatedOperations()
public async Task<IEnumerable<Operation>> GetInitiatedOperationsAsync()
{
using var context = factory.CreateDbContext();
return await (from operation in context.Operations
@ -35,7 +35,7 @@ namespace Hcs.WebApp.Services
select operation).ToListAsync();
}
public async Task SetOperationMessageGuid(int operationId, string messageGuid)
public async Task SetOperationMessageGuidAsync(int operationId, string messageGuid)
{
using var context = factory.CreateDbContext();
var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId);

View File

@ -7,7 +7,7 @@ namespace Hcs.WebApp.Services
{
private readonly IDbContextFactory<HcsDbContext> factory = factory;
public async Task<IEnumerable<Registry>> GetAllRegistries(bool isCommon)
public async Task<IEnumerable<Registry>> GetAllRegistriesAsync(bool isCommon)
{
using var context = factory.CreateDbContext();
return await (from registry in context.Registries

View File

@ -13,7 +13,7 @@ namespace Hcs.WebApp.Services
private readonly UserManager<AppUser> userManager = userManager;
private readonly IPasswordHasher<AppUser> passwordHasher = passwordHasher;
public async Task<IEnumerable<AppUserWithRole>> GetUsersWithRole()
public async Task<IEnumerable<AppUserWithRole>> GetUsersWithRoleAsync()
{
using var context = factory.CreateDbContext();
return await (from user in context.Users
@ -26,7 +26,7 @@ namespace Hcs.WebApp.Services
}).ToListAsync();
}
public async Task<IdentityResult> CreateUser(string userName, string roleName, string password)
public async Task<IdentityResult> CreateUserAsync(string userName, string roleName, string password)
{
var user = new AppUser()
{
@ -41,7 +41,7 @@ namespace Hcs.WebApp.Services
return result;
}
public async Task UpdateUser(string userId, string roleId, string password)
public async Task UpdateUserAsync(string userId, string roleId, string password)
{
using var context = factory.CreateDbContext();
using var transaction = await context.Database.BeginTransactionAsync();
@ -89,7 +89,7 @@ namespace Hcs.WebApp.Services
await transaction.CommitAsync();
}
public async Task<IdentityResult> DeleteUser(string userId)
public async Task<IdentityResult> DeleteUserAsync(string userId)
{
var user = await userManager.FindByIdAsync(userId);
return await userManager.DeleteAsync(user);