Update common registries page

This commit is contained in:
2025-10-23 10:56:11 +09:00
parent ddb92ccd5b
commit 0626ce0176
12 changed files with 490 additions and 57 deletions

View File

@ -26,6 +26,14 @@
<RadzenStack Style="height: 100%" JustifyContent="JustifyContent.SpaceBetween">
<RadzenPanelMenu>
<RadzenPanelMenuItem Path="/" Text="Главная" Icon="home" />
<AuthorizeView Roles="@($"{AppRole.ADMINISTRATOR_TYPE},{AppRole.OPERATOR_TYPE}")">
<Authorized>
<RadzenPanelMenuItem Text="Справочники" Icon="book_2">
<RadzenPanelMenuItem Path="/registry/common" Text="Общие" />
<RadzenPanelMenuItem Path="/registry/private" Text="Частные" />
</RadzenPanelMenuItem>
</Authorized>
</AuthorizeView>
<AuthorizeView Roles="@AppRole.ADMINISTRATOR_TYPE">
<Authorized>
<RadzenPanelMenuItem Text="Тестирование" Icon="simulation">

View File

@ -71,9 +71,9 @@
StateHasChanged();
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (state.User.Identity?.IsAuthenticated ?? false)
if (state.User.IsInRole(AppRole.ADMINISTRATOR_TYPE))
{
currentUserId = state.User.FindFirst(ClaimTypes.NameIdentifier).Value;
currentUserId = state.User.FindFirst(ClaimTypes.NameIdentifier)!.Value;
usersWithRoles = await UsersService.GetUsersWithRole();
}

View File

@ -1,5 +1,144 @@
<h3>Common</h3>
@page "/registry/common"
@using System.IdentityModel.Claims
@using Hcs.WebApp.Components.Dialogs
@using Hcs.WebApp.Services
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject OperationService OperationService
@inject RegistryService RegistryService
@inject DialogService DialogService
<PageTitle>Общие справочники подсистемы НСИ</PageTitle>
<AuthorizedContent Roles="@($"{AppRole.ADMINISTRATOR_TYPE},{AppRole.OPERATOR_TYPE}")">
<Content>
<RadzenStack>
<RadzenRow AlignItems="AlignItems.Center">
<RadzenColumn Size="12" SizeMD="6">
<RadzenText Text="Общие справочники подсистемы НСИ" TextStyle="TextStyle.H5" class="rz-m-0" />
</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" />
</RadzenStack>
</RadzenColumn>
</RadzenRow>
<RadzenRow>
<RadzenColumn SizeMD="12">
<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">
<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" />
</Template>
</RadzenDataGridColumn>
</Columns>
</RadzenDataGrid>
</RadzenColumn>
</RadzenRow>
</RadzenStack>
</Content>
</AuthorizedContent>
@code {
enum CommonPageState
{
Init,
Loading,
Idle,
OperationWaiting
}
IEnumerable<Registry> registries;
CommonPageState state;
string syncText = "...";
bool hasError;
string errorMessage;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
ChangeState(CommonPageState.Init);
StateHasChanged();
var finalState = CommonPageState.Idle;
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);
if (operationInProgress)
{
finalState = CommonPageState.OperationWaiting;
}
else
{
registries = await RegistryService.GetAllRegistries(true);
}
}
ChangeState(finalState);
StateHasChanged();
}
}
async Task SyncRegistries()
{
// TODO
}
async Task ViewRegistry(Registry userWithRole)
{
// TODO
}
async Task EditRegistry(Registry registry)
{
// TODO
}
async Task SyncRegistry(Registry registry)
{
// TODO
}
void ChangeState(CommonPageState state)
{
this.state = state;
SetSyncText();
}
void SetSyncText()
{
switch (state)
{
case CommonPageState.Init:
syncText = "...";
break;
case CommonPageState.Loading:
case CommonPageState.Idle:
syncText = "Синхронизировать";
break;
case CommonPageState.OperationWaiting:
syncText = "Синхронизация в процессе";
break;
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Hcs.WebApp.Data.Hcs
{
@ -7,5 +8,14 @@ namespace Hcs.WebApp.Data.Hcs
public DbSet<Registry> Registries { get; set; }
public DbSet<RegistryElement> Elements { get; set; }
public DbSet<Operation> Operations { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Operation>()
.Property(x => x.Type)
.HasConversion(new EnumToStringConverter<Operation.OperationType>());
}
}
}

View File

@ -0,0 +1,120 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hcs.WebApp.Data.Hcs.Migrations
{
[DbContext(typeof(HcsDbContext))]
[Migration("20251023014411_AddOperation")]
partial class AddOperation
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Operation", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<DateTime?>("EndedAt")
.HasColumnType("datetime2");
b.Property<string>("InitiatorId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("MessageGuid")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("StartedAt")
.HasColumnType("datetime2");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Operations");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Registry", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<bool>("IsCommon")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Number")
.HasColumnType("int");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2");
b.HasKey("Id");
b.ToTable("Registries");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.RegistryElement", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("GUID")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("RegistryId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("Xml")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RegistryId");
b.ToTable("Elements");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.RegistryElement", b =>
{
b.HasOne("Hcs.WebApp.Data.Hcs.Registry", "Registry")
.WithMany("Elements")
.HasForeignKey("RegistryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Registry");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Registry", b =>
{
b.Navigation("Elements");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hcs.WebApp.Data.Hcs.Migrations
{
/// <inheritdoc />
public partial class AddOperation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Name",
table: "Registries",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedAt",
table: "Registries",
type: "datetime2",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.CreateTable(
name: "Operations",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
Type = table.Column<string>(type: "nvarchar(max)", nullable: false),
InitiatorId = table.Column<string>(type: "nvarchar(max)", nullable: false),
StartedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
EndedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
MessageGuid = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Operations", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Operations");
migrationBuilder.DropColumn(
name: "Name",
table: "Registries");
migrationBuilder.DropColumn(
name: "UpdatedAt",
table: "Registries");
}
}
}

View File

@ -1,12 +1,8 @@
// <auto-generated />
using Hcs.WebApp.Data.Hcs;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Hcs.WebApp.Data.Hcs.Migrations
{
[DbContext(typeof(HcsDbContext))]
@ -21,65 +17,100 @@ namespace Hcs.WebApp.Data.Hcs.Migrations
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Registry", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Operation", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<bool>("IsCommon")
.HasColumnType("bit");
b.Property<DateTime?>("EndedAt")
.HasColumnType("datetime2");
b.Property<int>("Number")
.HasColumnType("int");
b.Property<string>("InitiatorId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.Property<string>("MessageGuid")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.ToTable("Registries");
});
b.Property<DateTime>("StartedAt")
.HasColumnType("datetime2");
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.RegistryElement", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.Property<string>("GUID")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("RegistryId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("Xml")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RegistryId");
b.ToTable("Elements");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.RegistryElement", b =>
{
b.HasOne("Hcs.WebApp.Data.Hcs.Registry", "Registry")
.WithMany("Elements")
.HasForeignKey("RegistryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Registry");
});
b.ToTable("Operations");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Registry", b =>
{
b.Navigation("Elements");
});
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<bool>("IsCommon")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Number")
.HasColumnType("int");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime2");
b.HasKey("Id");
b.ToTable("Registries");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.RegistryElement", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("GUID")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("RegistryId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("Xml")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("RegistryId");
b.ToTable("Elements");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.RegistryElement", b =>
{
b.HasOne("Hcs.WebApp.Data.Hcs.Registry", "Registry")
.WithMany("Elements")
.HasForeignKey("RegistryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Registry");
});
modelBuilder.Entity("Hcs.WebApp.Data.Hcs.Registry", b =>
{
b.Navigation("Elements");
});
#pragma warning restore 612, 618
}
}

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Hcs.WebApp.Data.Hcs
{
public class Operation
{
public enum OperationType
{
Nsi_15_7_0_1_ExportAllRegistryElements
}
public string Id { get; set; }
public OperationType Type { get; set; }
public string InitiatorId { get; set; }
public DateTime StartedAt { get; set; }
public DateTime? EndedAt { get; set; }
public string MessageGuid { get; set; }
[NotMapped]
public bool Completed => EndedAt.HasValue;
}
}

View File

@ -6,8 +6,12 @@
public int Number { get; set; }
public string Name { get; set; }
public bool IsCommon { get; set; }
public DateTime UpdatedAt { get; set; }
public virtual ICollection<RegistryElement> Elements { get; set; } = [];
}
}

View File

@ -57,6 +57,8 @@ builder.Services.AddTransient<IClientProvider, ClientProvider>();
#endif
builder.Services.AddScoped<IdentityService>();
builder.Services.AddScoped<UsersService>();
builder.Services.AddScoped<OperationService>();
builder.Services.AddScoped<RegistryService>();
var activator = new RadzenComponentActivator();
activator.Override(typeof(RadzenDataGrid<>), typeof(LocalizedRadzenDataGrid<>));

View File

@ -0,0 +1,16 @@
using Hcs.WebApp.Data.Hcs;
using Microsoft.EntityFrameworkCore;
namespace Hcs.WebApp.Services
{
public class OperationService(IDbContextFactory<HcsDbContext> factory)
{
private readonly IDbContextFactory<HcsDbContext> factory = factory;
public async Task<bool> HasActiveOperation(Operation.OperationType type)
{
using var context = factory.CreateDbContext();
return await context.Operations.AnyAsync(x => x.Type == type && !x.EndedAt.HasValue);
}
}
}

View File

@ -0,0 +1,18 @@
using Hcs.WebApp.Data.Hcs;
using Microsoft.EntityFrameworkCore;
namespace Hcs.WebApp.Services
{
public class RegistryService(IDbContextFactory<HcsDbContext> factory)
{
private readonly IDbContextFactory<HcsDbContext> factory = factory;
public async Task<IEnumerable<Registry>> GetAllRegistries(bool isCommon)
{
using var context = factory.CreateDbContext();
return await (from registry in context.Registries
where registry.IsCommon == isCommon
select registry).ToListAsync();
}
}
}