From 1e87523ab948ce168385456cf1ad9ee6dcd6b9e0 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Thu, 28 Nov 2024 08:55:50 +0500 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=BD=D0=B5=D1=81=D1=82=D0=B8=20=D0=B8?= =?UTF-8?q?=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/TechMessagesController.cs | 6 +- Persistence.API/DependencyInjection.cs | 9 ++ Persistence.API/Startup.cs | 3 + .../20241126044756_TechMessageMigration.cs | 59 ------------- ...27123045_TechMessageMigration.Designer.cs} | 68 +++++++++++++-- .../20241127123045_TechMessageMigration.cs | 83 +++++++++++++++++++ .../PersistenceDbContextModelSnapshot.cs | 66 ++++++++++++++- Persistence.Database/Entity/ADSystem.cs | 16 ++++ Persistence.Database/Entity/TechMessage.cs | 9 +- .../Controllers/TechMessagesControllerTest.cs | 24 +++--- .../Repositories/TechMessagesRepository.cs | 71 +++++++++++++--- Persistence/API/ITechMessages.cs | 35 -------- Persistence/Models/ADSystemDto.cs | 22 +++++ Persistence/Models/TechMessageDto.cs | 19 +++-- .../Repositories/ITechMessagesRepository.cs | 7 +- 15 files changed, 355 insertions(+), 142 deletions(-) delete mode 100644 Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs rename Persistence.Database.Postgres/Migrations/{20241126044756_TechMessageMigration.Designer.cs => 20241127123045_TechMessageMigration.Designer.cs} (71%) create mode 100644 Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs create mode 100644 Persistence.Database/Entity/ADSystem.cs delete mode 100644 Persistence/API/ITechMessages.cs create mode 100644 Persistence/Models/ADSystemDto.cs diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index 7411c19..fb3315e 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -11,7 +11,7 @@ namespace Persistence.API.Controllers; [ApiController] [Authorize] [Route("api/[controller]")] -public class TechMessagesController : ControllerBase, ITechMessages +public class TechMessagesController : ControllerBase { private readonly ITechMessagesRepository techMessagesRepository; @@ -41,8 +41,8 @@ public class TechMessagesController : ControllerBase, ITechMessages /// /// /// - [HttpGet("statistics")] - public async Task> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + [HttpGet("statistics/{autoDrillingSystem}")] + public async Task> GetStatistics([FromRoute] string? autoDrillingSystem, int? importantId, CancellationToken token) { var result = await techMessagesRepository.GetStatistics(importantId, autoDrillingSystem, token); diff --git a/Persistence.API/DependencyInjection.cs b/Persistence.API/DependencyInjection.cs index 5d7c646..19cedc9 100644 --- a/Persistence.API/DependencyInjection.cs +++ b/Persistence.API/DependencyInjection.cs @@ -1,9 +1,12 @@ using System.Reflection; using System.Text.Json.Nodes; +using Mapster; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; +using Persistence.Database.Entity; +using Persistence.Models; using Persistence.Models.Configurations; using Swashbuckle.AspNetCore.SwaggerGen; @@ -11,6 +14,12 @@ namespace Persistence.API; public static class DependencyInjection { + public static void MapsterSetup() + { + TypeAdapterConfig.GlobalSettings.Default.Config + .ForType() + .Ignore(dest => dest.System, dest => dest.SystemId); + } public static void AddSwagger(this IServiceCollection services, IConfiguration configuration) { services.AddSwaggerGen(c => diff --git a/Persistence.API/Startup.cs b/Persistence.API/Startup.cs index e074845..98ad4aa 100644 --- a/Persistence.API/Startup.cs +++ b/Persistence.API/Startup.cs @@ -23,6 +23,9 @@ public class Startup services.AddInfrastructure(); services.AddPersistenceDbContext(Configuration); services.AddJWTAuthentication(Configuration); + services.AddMemoryCache(); + + DependencyInjection.MapsterSetup(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs b/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs deleted file mode 100644 index 46a71a0..0000000 --- a/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Persistence.Database.Postgres.Migrations -{ - /// - public partial class TechMessageMigration : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Created", - table: "Setpoint", - type: "timestamp with time zone", - nullable: false, - comment: "Дата создания уставки", - oldClrType: typeof(DateTimeOffset), - oldType: "timestamp with time zone", - oldComment: "Дата изменения уставки"); - - migrationBuilder.CreateTable( - name: "TechMessage", - columns: table => new - { - EventId = table.Column(type: "uuid", nullable: false, comment: "Id события"), - ImportantId = table.Column(type: "integer", nullable: false, comment: "Id Категории важности"), - OccurrenceDate = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), - Depth = table.Column(type: "double precision", nullable: true, comment: "Глубина забоя"), - MessageText = table.Column(type: "varchar(512)", nullable: true, comment: "Текст сообщения"), - AutoDrillingSystem = table.Column(type: "varchar(256)", nullable: true, comment: "Система автобурения, к которой относится сообщение"), - UserId = table.Column(type: "uuid", nullable: false, comment: "Id пользователя за пультом бурильщика") - }, - constraints: table => - { - table.PrimaryKey("PK_TechMessage", x => x.EventId); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "TechMessage"); - - migrationBuilder.AlterColumn( - name: "Created", - table: "Setpoint", - type: "timestamp with time zone", - nullable: false, - comment: "Дата изменения уставки", - oldClrType: typeof(DateTimeOffset), - oldType: "timestamp with time zone", - oldComment: "Дата создания уставки"); - } - } -} diff --git a/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs b/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.Designer.cs similarity index 71% rename from Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs rename to Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.Designer.cs index 5d6817e..2e5a30a 100644 --- a/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs +++ b/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.Designer.cs @@ -12,7 +12,7 @@ using Persistence.Database.Model; namespace Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistenceDbContext))] - [Migration("20241126044756_TechMessageMigration")] + [Migration("20241127123045_TechMessageMigration")] partial class TechMessageMigration { /// @@ -27,6 +27,27 @@ namespace Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Persistence.Database.Entity.ADSystem", b => + { + b.Property("SystemId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Id системы автобурения"); + + b.Property("Description") + .HasColumnType("text") + .HasComment("Описание системы автобурения"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(256)") + .HasComment("Наименование системы автобурения"); + + b.HasKey("SystemId"); + + b.ToTable("ADSystem"); + }); + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => { b.Property("EventId") @@ -34,10 +55,6 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id события"); - b.Property("AutoDrillingSystem") - .HasColumnType("varchar(256)") - .HasComment("Система автобурения, к которой относится сообщение"); - b.Property("Depth") .HasColumnType("double precision") .HasComment("Глубина забоя"); @@ -47,6 +64,7 @@ namespace Persistence.Database.Postgres.Migrations .HasComment("Id Категории важности"); b.Property("MessageText") + .IsRequired() .HasColumnType("varchar(512)") .HasComment("Текст сообщения"); @@ -54,15 +72,44 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата возникновения"); + b.Property("SystemId") + .HasColumnType("uuid") + .HasComment("Id системы автобурения, к которой относится сообщение"); + b.Property("UserId") .HasColumnType("uuid") .HasComment("Id пользователя за пультом бурильщика"); b.HasKey("EventId"); + b.HasIndex("SystemId"); + b.ToTable("TechMessage"); }); + modelBuilder.Entity("Persistence.Database.Entity.TimestampedSet", b => + { + b.Property("IdDiscriminator") + .HasColumnType("uuid") + .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasComment("Отметка времени, строго в UTC"); + + b.Property("Set") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Набор сохраняемых данных"); + + b.HasKey("IdDiscriminator", "Timestamp"); + + b.ToTable("TimestampedSets", t => + { + t.HasComment("Общая таблица данных временных рядов"); + }); + }); + modelBuilder.Entity("Persistence.Database.Model.DataSaub", b => { b.Property("Date") @@ -169,6 +216,17 @@ namespace Persistence.Database.Postgres.Migrations b.ToTable("Setpoint"); }); + + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => + { + b.HasOne("Persistence.Database.Entity.ADSystem", "System") + .WithMany() + .HasForeignKey("SystemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("System"); + }); #pragma warning restore 612, 618 } } diff --git a/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs b/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs new file mode 100644 index 0000000..d3a5ac2 --- /dev/null +++ b/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs @@ -0,0 +1,83 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistence.Database.Postgres.Migrations +{ + /// + public partial class TechMessageMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ADSystem", + columns: table => new + { + SystemId = table.Column(type: "uuid", nullable: false, comment: "Id системы автобурения"), + Name = table.Column(type: "varchar(256)", nullable: false, comment: "Наименование системы автобурения"), + Description = table.Column(type: "text", nullable: true, comment: "Описание системы автобурения") + }, + constraints: table => + { + table.PrimaryKey("PK_ADSystem", x => x.SystemId); + }); + + migrationBuilder.CreateTable( + name: "TimestampedSets", + columns: table => new + { + IdDiscriminator = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"), + Set = table.Column(type: "jsonb", nullable: false, comment: "Набор сохраняемых данных") + }, + constraints: table => + { + table.PrimaryKey("PK_TimestampedSets", x => new { x.IdDiscriminator, x.Timestamp }); + }, + comment: "Общая таблица данных временных рядов"); + + migrationBuilder.CreateTable( + name: "TechMessage", + columns: table => new + { + EventId = table.Column(type: "uuid", nullable: false, comment: "Id события"), + ImportantId = table.Column(type: "integer", nullable: false, comment: "Id Категории важности"), + OccurrenceDate = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), + Depth = table.Column(type: "double precision", nullable: true, comment: "Глубина забоя"), + MessageText = table.Column(type: "varchar(512)", nullable: false, comment: "Текст сообщения"), + SystemId = table.Column(type: "uuid", nullable: false, comment: "Id системы автобурения, к которой относится сообщение"), + UserId = table.Column(type: "uuid", nullable: false, comment: "Id пользователя за пультом бурильщика") + }, + constraints: table => + { + table.PrimaryKey("PK_TechMessage", x => x.EventId); + table.ForeignKey( + name: "FK_TechMessage_ADSystem_SystemId", + column: x => x.SystemId, + principalTable: "ADSystem", + principalColumn: "SystemId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_TechMessage_SystemId", + table: "TechMessage", + column: "SystemId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TechMessage"); + + migrationBuilder.DropTable( + name: "TimestampedSets"); + + migrationBuilder.DropTable( + name: "ADSystem"); + } + } +} diff --git a/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs index 4446a1c..317c445 100644 --- a/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs +++ b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs @@ -24,6 +24,27 @@ namespace Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Persistence.Database.Entity.ADSystem", b => + { + b.Property("SystemId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Id системы автобурения"); + + b.Property("Description") + .HasColumnType("text") + .HasComment("Описание системы автобурения"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(256)") + .HasComment("Наименование системы автобурения"); + + b.HasKey("SystemId"); + + b.ToTable("ADSystem"); + }); + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => { b.Property("EventId") @@ -31,10 +52,6 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id события"); - b.Property("AutoDrillingSystem") - .HasColumnType("varchar(256)") - .HasComment("Система автобурения, к которой относится сообщение"); - b.Property("Depth") .HasColumnType("double precision") .HasComment("Глубина забоя"); @@ -44,6 +61,7 @@ namespace Persistence.Database.Postgres.Migrations .HasComment("Id Категории важности"); b.Property("MessageText") + .IsRequired() .HasColumnType("varchar(512)") .HasComment("Текст сообщения"); @@ -51,15 +69,44 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата возникновения"); + b.Property("SystemId") + .HasColumnType("uuid") + .HasComment("Id системы автобурения, к которой относится сообщение"); + b.Property("UserId") .HasColumnType("uuid") .HasComment("Id пользователя за пультом бурильщика"); b.HasKey("EventId"); + b.HasIndex("SystemId"); + b.ToTable("TechMessage"); }); + modelBuilder.Entity("Persistence.Database.Entity.TimestampedSet", b => + { + b.Property("IdDiscriminator") + .HasColumnType("uuid") + .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasComment("Отметка времени, строго в UTC"); + + b.Property("Set") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Набор сохраняемых данных"); + + b.HasKey("IdDiscriminator", "Timestamp"); + + b.ToTable("TimestampedSets", t => + { + t.HasComment("Общая таблица данных временных рядов"); + }); + }); + modelBuilder.Entity("Persistence.Database.Model.DataSaub", b => { b.Property("Date") @@ -166,6 +213,17 @@ namespace Persistence.Database.Postgres.Migrations b.ToTable("Setpoint"); }); + + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => + { + b.HasOne("Persistence.Database.Entity.ADSystem", "System") + .WithMany() + .HasForeignKey("SystemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("System"); + }); #pragma warning restore 612, 618 } } diff --git a/Persistence.Database/Entity/ADSystem.cs b/Persistence.Database/Entity/ADSystem.cs new file mode 100644 index 0000000..525134c --- /dev/null +++ b/Persistence.Database/Entity/ADSystem.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Persistence.Database.Entity; +public class ADSystem +{ + [Key, Comment("Id системы автобурения")] + public Guid SystemId { get; set; } + + [Required, Column(TypeName = "varchar(256)"), Comment("Наименование системы автобурения")] + public required string Name { get; set; } + + [Comment("Описание системы автобурения")] + public string? Description { get; set; } +} diff --git a/Persistence.Database/Entity/TechMessage.cs b/Persistence.Database/Entity/TechMessage.cs index b4135c1..dce897c 100644 --- a/Persistence.Database/Entity/TechMessage.cs +++ b/Persistence.Database/Entity/TechMessage.cs @@ -19,10 +19,13 @@ namespace Persistence.Database.Entity public double? Depth { get; set; } [Column(TypeName = "varchar(512)"), Comment("Текст сообщения")] - public string? MessageText { get; set; } + public required string MessageText { get; set; } - [Column(TypeName = "varchar(256)"), Comment("Система автобурения, к которой относится сообщение")] - public string? AutoDrillingSystem { get; set; } + [Required, Comment("Id системы автобурения, к которой относится сообщение")] + public required Guid SystemId { get; set; } + + [Required, ForeignKey(nameof(SystemId)), Comment("Система автобурения, к которой относится сообщение")] + public virtual required ADSystem System { get; set; } [Comment("Id пользователя за пультом бурильщика")] public Guid UserId { get; set; } diff --git a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs index 9eb0e9d..73414c4 100644 --- a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs @@ -29,7 +29,7 @@ namespace Persistence.IntegrationTests.Controllers { Skip = 1, Take = 2, - SortSettings = nameof(TechMessageDto.ImportantId) + SortSettings = nameof(TechMessageDto.CategoryId) }; //act @@ -53,7 +53,7 @@ namespace Persistence.IntegrationTests.Controllers { Skip = 0, Take = 2, - SortSettings = nameof(TechMessageDto.ImportantId) + SortSettings = nameof(TechMessageDto.CategoryId) }; //act @@ -90,7 +90,7 @@ namespace Persistence.IntegrationTests.Controllers //arrange var dtos = await InsertRange(); var systems = dtos - .Select(e => e.AutoDrillingSystem) + .Select(e => e.System) .Distinct() .ToArray(); @@ -110,7 +110,7 @@ namespace Persistence.IntegrationTests.Controllers //arrange dbContext.CleanupDbSet(); var imortantId = 1; - var autoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem); + var autoDrillingSystem = nameof(TechMessageDto.System); //act var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken()); @@ -125,9 +125,9 @@ namespace Persistence.IntegrationTests.Controllers { //arrange var imortantId = 1; - var autoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem); + var autoDrillingSystem = nameof(TechMessageDto.System); var dtos = await InsertRange(); - var filteredDtos = dtos.Where(e => e.ImportantId == imortantId && e.AutoDrillingSystem == e.AutoDrillingSystem); + var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == e.System); //act var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken()); @@ -145,21 +145,21 @@ namespace Persistence.IntegrationTests.Controllers new TechMessageDto() { EventId = Guid.NewGuid(), - ImportantId = 1, - OccurrenceDate = DateTimeOffset.UtcNow, + CategoryId = 1, + Timestamp = DateTimeOffset.UtcNow, Depth = 1.11, MessageText = nameof(TechMessageDto.MessageText), - AutoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem), + System = nameof(TechMessageDto.System), UserId = Guid.NewGuid() }, new TechMessageDto() { EventId = Guid.NewGuid(), - ImportantId = 2, - OccurrenceDate = DateTimeOffset.UtcNow, + CategoryId = 2, + Timestamp = DateTimeOffset.UtcNow, Depth = 2.22, MessageText = nameof(TechMessageDto.MessageText), - AutoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem), + System = nameof(TechMessageDto.System), UserId = Guid.NewGuid() } }; diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index 9abda86..427346b 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -1,5 +1,7 @@ using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json.Linq; using Persistence.Database.Entity; using Persistence.Models; using Persistence.Repositories; @@ -9,9 +11,13 @@ namespace Persistence.Repository.Repositories { public class TechMessagesRepository : ITechMessagesRepository { + private static readonly string SystemCacheKey = $"{typeof(ADSystem).FullName}CacheKey"; + private readonly IMemoryCache memoryCache; private DbContext db; - public TechMessagesRepository(DbContext db) + + public TechMessagesRepository(DbContext db, IMemoryCache memoryCache) { + this.memoryCache = memoryCache; this.db = db; } @@ -36,36 +42,75 @@ namespace Persistence.Repository.Repositories return dto; } - public async Task GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + public async Task> GetStatistics(int? importantId, string? autoDrillingSystem, CancellationToken token) { var query = GetQueryReadOnly(); var count = await query - .Where(e => e.ImportantId == importantId && e.AutoDrillingSystem == autoDrillingSystem) - .CountAsync(); + .Where(e => importantId == null || e.ImportantId == importantId) + .Where(e => autoDrillingSystem == null || e.System.Name == autoDrillingSystem) + .GroupBy(e => e.System.Name) + .ToDictionaryAsync(e => e.Key, v => v.Count()); return count; } - public async Task> GetSystems(CancellationToken token) + public async Task> GetSystems(CancellationToken token) { - var query = GetQueryReadOnly(); - var entities = await query - .Select(e => e.AutoDrillingSystem ?? string.Empty) - .Distinct() - .ToArrayAsync(token); - var dtos = entities.Order(); + var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async f => + { + f.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); - return dtos; + var query = db.Set(); + var entities = await query.ToListAsync(); + var dtos = entities.Select(e => e.Adapt()); + + return dtos; + }); + + return systems ?? []; } public async Task InsertRange(IEnumerable dtos, CancellationToken token) { - var entities = dtos.Select(d => d.Adapt()); + var entities = dtos.Select(dto => + { + var task = Task.Run(async () => + { + var entity = dto.Adapt(); + var systems = await GetSystems(token); + var systemId = systems.FirstOrDefault(e => e.Name == dto.System)?.SystemId + ?? await CreateSystem(dto.System); + + entity.SystemId = systemId; + + return entity; + }); + task.Wait(); + + return task.Result; + }); await db.Set().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); return result; } + + private async Task CreateSystem(string name) + { + memoryCache.Remove(SystemCacheKey); + + var systemId = Guid.NewGuid(); + var entity = new ADSystem() + { + SystemId = systemId, + Name = name + }; + + await db.Set().AddAsync(entity); + await db.SaveChangesAsync(); + + return Guid.NewGuid(); + } } } diff --git a/Persistence/API/ITechMessages.cs b/Persistence/API/ITechMessages.cs deleted file mode 100644 index 95af161..0000000 --- a/Persistence/API/ITechMessages.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Persistence.Models; - -namespace Persistence.API -{ - /// - /// Интерфейс для API сообщений о состояниях работы систем автобурения (АБ) - /// - public interface ITechMessages : ITableDataApi - { - /// - /// Добавление новых сообщений - /// - /// - /// - /// - Task> InsertRange(IEnumerable dtos, CancellationToken token); - - /// - /// Получение списка систем АБ - /// - /// - /// - Task>> GetSystems(CancellationToken token); - - /// - /// Получение статистики - /// - /// Id Категории важности - /// Система АБ - /// - /// - Task> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); - } -} diff --git a/Persistence/Models/ADSystemDto.cs b/Persistence/Models/ADSystemDto.cs new file mode 100644 index 0000000..d11dfc7 --- /dev/null +++ b/Persistence/Models/ADSystemDto.cs @@ -0,0 +1,22 @@ +namespace Persistence.Models; + +/// +/// Модель системы автобурения +/// +public class ADSystemDto +{ + /// + /// Ключ + /// + public Guid SystemId { get; set; } + + /// + /// Наименование + /// + public required string Name { get; set; } + + /// + /// Описание + /// + public string? Description { get; set; } +} diff --git a/Persistence/Models/TechMessageDto.cs b/Persistence/Models/TechMessageDto.cs index 625b22f..84da656 100644 --- a/Persistence/Models/TechMessageDto.cs +++ b/Persistence/Models/TechMessageDto.cs @@ -1,4 +1,6 @@ -namespace Persistence.Models +using System.ComponentModel.DataAnnotations; + +namespace Persistence.Models { /// /// Модель технологического сообщения @@ -8,32 +10,39 @@ /// /// Id события /// + [Required] public Guid EventId { get; set; } /// /// Id Категории важности /// - public int ImportantId { get; set; } + [Range(0, int.MaxValue, ErrorMessage = "Id Категории важности не может быть меньше 0")] + public int CategoryId { get; set; } /// /// Дата возникновения /// - public DateTimeOffset OccurrenceDate { get; set; } + public DateTimeOffset Timestamp { get; set; } /// /// Глубина забоя /// + [Range(0, double.MaxValue, ErrorMessage = "Глубина забоя не может быть меньше 0")] public double? Depth { get; set; } /// /// Текст сообщения /// - public string? MessageText { get; set; } + [Required] + [StringLength(512, MinimumLength = 1, ErrorMessage = "Допустимая длина текста сообщения от 1 до 512 символов")] + public required string MessageText { get; set; } /// /// Система автобурения, к которой относится сообщение /// - public string? AutoDrillingSystem { get; set; } + [Required] + [StringLength(256, MinimumLength = 1, ErrorMessage = "Допустимая длина наименования системы АБ от 1 до 256 символов")] + public required string System { get; set; } /// /// Id пользователя за пультом бурильщика diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs index 1be90e9..b8fb194 100644 --- a/Persistence/Repositories/ITechMessagesRepository.cs +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -1,4 +1,5 @@ -using Persistence.Models; +using System.Threading.Tasks; +using Persistence.Models; namespace Persistence.Repositories { @@ -28,7 +29,7 @@ namespace Persistence.Repositories /// /// /// - Task> GetSystems(CancellationToken token); + Task> GetSystems(CancellationToken token); /// /// Получение количества сообщений по категориям и системам автобурения @@ -37,6 +38,6 @@ namespace Persistence.Repositories /// Система автобурения /// /// - Task GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); + Task> GetStatistics(int? importantId, string? autoDrillingSystem, CancellationToken token); } }