From 153f5894adcda5792854a26ec179aa35a5fc6eee Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Mon, 25 Nov 2024 13:49:07 +0500 Subject: [PATCH 01/12] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=85=D1=80=D0=B0=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=85=D0=BD=D0=BE=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D1=85=20=D1=81=D0=BE?= =?UTF-8?q?=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/TechMessagesController.cs | 44 ++++++++++++++++ .../PersistenceDbContext.cs | 5 +- Persistence.Database/Entity/TechMessage.cs | 30 +++++++++++ .../Model/IPersistenceDbContext.cs | 2 + .../Repositories/TechMessagesRepository.cs | 51 +++++++++++++++++++ Persistence/API/ITechMessages.cs | 35 +++++++++++++ Persistence/Models/TechMessageDto.cs | 40 +++++++++++++++ .../Repositories/ITechMessagesRepository.cs | 34 +++++++++++++ 8 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 Persistence.API/Controllers/TechMessagesController.cs create mode 100644 Persistence.Database/Entity/TechMessage.cs create mode 100644 Persistence.Repository/Repositories/TechMessagesRepository.cs create mode 100644 Persistence/API/ITechMessages.cs create mode 100644 Persistence/Models/TechMessageDto.cs create mode 100644 Persistence/Repositories/ITechMessagesRepository.cs diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs new file mode 100644 index 0000000..0eb183d --- /dev/null +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; +using Persistence.Models; +using Persistence.Repositories; + +namespace Persistence.API.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class TechMessagesController : ControllerBase, ITechMessages + { + private readonly ITechMessagesRepository techMessagesRepository; + + public TechMessagesController(ITechMessagesRepository techMessagesRepository) + { + this.techMessagesRepository = techMessagesRepository; + } + + public Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage(RequestDto request, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task<ActionResult<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + { + var result = await techMessagesRepository.GetStatistics(importantId, autoDrillingSystem, token); + + return Ok(result); + } + + public async Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token) + { + var result = await techMessagesRepository.GetSystems(token); + + return Ok(result); + } + + public async Task<ActionResult<int>> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) + { + var result = await techMessagesRepository.InsertRange(dtos, token); + + return Ok(result); + } + } +} diff --git a/Persistence.Database.Postgres/PersistenceDbContext.cs b/Persistence.Database.Postgres/PersistenceDbContext.cs index 58d201c..75fd28a 100644 --- a/Persistence.Database.Postgres/PersistenceDbContext.cs +++ b/Persistence.Database.Postgres/PersistenceDbContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Persistence.Database.Entity; using System.Data.Common; namespace Persistence.Database.Model; @@ -8,7 +9,9 @@ public partial class PersistenceDbContext : DbContext, IPersistenceDbContext public DbSet<Setpoint> Setpoint => Set<Setpoint>(); - public PersistenceDbContext() + public DbSet<TechMessage> TechMessage => Set<TechMessage>(); + + public PersistenceDbContext() : base() { diff --git a/Persistence.Database/Entity/TechMessage.cs b/Persistence.Database/Entity/TechMessage.cs new file mode 100644 index 0000000..b4135c1 --- /dev/null +++ b/Persistence.Database/Entity/TechMessage.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Persistence.Database.Entity +{ + public class TechMessage + { + [Key, Comment("Id события")] + public Guid EventId { get; set; } + + [Comment("Id Категории важности")] + public int ImportantId { get; set; } + + [Comment("Дата возникновения")] + public DateTimeOffset OccurrenceDate { get; set; } + + [Comment("Глубина забоя")] + public double? Depth { get; set; } + + [Column(TypeName = "varchar(512)"), Comment("Текст сообщения")] + public string? MessageText { get; set; } + + [Column(TypeName = "varchar(256)"), Comment("Система автобурения, к которой относится сообщение")] + public string? AutoDrillingSystem { get; set; } + + [Comment("Id пользователя за пультом бурильщика")] + public Guid UserId { get; set; } + } +} diff --git a/Persistence.Database/Model/IPersistenceDbContext.cs b/Persistence.Database/Model/IPersistenceDbContext.cs index 2c1aebb..759c4a2 100644 --- a/Persistence.Database/Model/IPersistenceDbContext.cs +++ b/Persistence.Database/Model/IPersistenceDbContext.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Persistence.Database.Entity; using System; using System.Collections.Generic; using System.Diagnostics.Metrics; @@ -12,6 +13,7 @@ public interface IPersistenceDbContext : IDisposable { DbSet<DataSaub> DataSaub { get; } DbSet<Setpoint> Setpoint { get; } + DbSet<TechMessage> TechMessage { get; } DatabaseFacade Database { get; } Task<int> SaveChangesAsync(CancellationToken cancellationToken); } diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs new file mode 100644 index 0000000..e7a37bc --- /dev/null +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -0,0 +1,51 @@ +using Mapster; +using Microsoft.EntityFrameworkCore; +using Persistence.Database.Entity; +using Persistence.Models; +using Persistence.Repositories; + +namespace Persistence.Repository.Repositories +{ + public class TechMessagesRepository : ITechMessagesRepository + { + private DbContext db; + public TechMessagesRepository(DbContext db) + { + this.db = db; + } + + protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>(); + + public async Task<int> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + { + var query = GetQueryReadOnly(); + var count = await query + .Where(e => e.ImportantId == importantId && e.AutoDrillingSystem == autoDrillingSystem) + .CountAsync(); + + return count; + } + + public async Task<IEnumerable<string>> GetSystems(CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query + .Select(e => e.AutoDrillingSystem ?? string.Empty) + .Distinct() + .ToArrayAsync(token); + var dtos = entities.Order(); + + return dtos; + } + + public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) + { + var entities = dtos.Select(d => d.Adapt<TechMessage>()); + + await db.Set<TechMessage>().AddRangeAsync(entities, token); + var result = await db.SaveChangesAsync(token); + + return result; + } + } +} diff --git a/Persistence/API/ITechMessages.cs b/Persistence/API/ITechMessages.cs new file mode 100644 index 0000000..95af161 --- /dev/null +++ b/Persistence/API/ITechMessages.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc; +using Persistence.Models; + +namespace Persistence.API +{ + /// <summary> + /// Интерфейс для API сообщений о состояниях работы систем автобурения (АБ) + /// </summary> + public interface ITechMessages : ITableDataApi<TechMessageDto, RequestDto> + { + /// <summary> + /// Добавление новых сообщений + /// </summary> + /// <param name="dtos"></param> + /// <param name="token"></param> + /// <returns></returns> + Task<ActionResult<int>> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token); + + /// <summary> + /// Получение списка систем АБ + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token); + + /// <summary> + /// Получение статистики + /// </summary> + /// <param name="importantId">Id Категории важности</param> + /// <param name="autoDrillingSystem">Система АБ</param> + /// <param name="token"></param> + /// <returns></returns> + Task<ActionResult<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); + } +} diff --git a/Persistence/Models/TechMessageDto.cs b/Persistence/Models/TechMessageDto.cs new file mode 100644 index 0000000..274a1d5 --- /dev/null +++ b/Persistence/Models/TechMessageDto.cs @@ -0,0 +1,40 @@ +namespace Persistence.Models +{ + public class TechMessageDto + { + /// <summary> + /// Id события + /// </summary> + public Guid EventId { get; set; } + + /// <summary> + /// Id Категории важности + /// </summary> + public int ImportantId { get; set; } + + /// <summary> + /// Дата возникновения + /// </summary> + public DateTimeOffset OccurrenceDate { get; set; } + + /// <summary> + /// Глубина забоя + /// </summary> + public double? Depth { get; set; } + + /// <summary> + /// Текст сообщения + /// </summary> + public string? MessageText { get; set; } + + /// <summary> + /// Система автобурения, к которой относится сообщение + /// </summary> + public string? AutoDrillingSystem { get; set; } + + /// <summary> + /// Id пользователя за пультом бурильщика + /// </summary> + public Guid UserId { get; set; } + } +} diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs new file mode 100644 index 0000000..c681184 --- /dev/null +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -0,0 +1,34 @@ +using Persistence.Models; + +namespace Persistence.Repositories +{ + /// <summary> + /// Интерфейс по работе с технологическими сообщениями + /// </summary> + public interface ITechMessagesRepository + { + /// <summary> + /// Добавление новых сообщений + /// </summary> + /// <param name="dtos"></param> + /// <param name="token"></param> + /// <returns></returns> + Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token); + + /// <summary> + /// Получение списка уникальных названий систем АБ + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + Task<IEnumerable<string>> GetSystems(CancellationToken token); + + /// <summary> + /// Получение количества сообщений по категориям и системам автобурения + /// </summary> + /// <param name="importantId">Id Категории важности</param> + /// <param name="autoDrillingSystem">Система автобурения</param> + /// <param name="token"></param> + /// <returns></returns> + Task<int> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); + } +} -- 2.45.2 From acc9e6494a9df663deb0843a7f35394962857cdb Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Tue, 26 Nov 2024 10:23:48 +0500 Subject: [PATCH 02/12] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D1=8C=20=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=82=D0=B5=D1=85=D0=BD=D0=BE=D0=BB=D0=BE=D0=B3?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D1=85=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/TechMessagesController.cs | 12 +- ...126044756_TechMessageMigration.Designer.cs | 175 ++++++++++++ .../20241126044756_TechMessageMigration.cs | 59 ++++ .../PersistenceDbContextModelSnapshot.cs | 38 ++- Persistence.Repository/DependencyInjection.cs | 1 + .../Extensions/EFExtensionsSortBy.cs | 267 ++++++++++++++++++ .../Repositories/TechMessagesRepository.cs | 19 ++ .../Repositories/ISetpointRepository.cs | 14 +- .../Repositories/ITechMessagesRepository.cs | 8 + 9 files changed, 582 insertions(+), 11 deletions(-) create mode 100644 Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs create mode 100644 Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs create mode 100644 Persistence.Repository/Extensions/EFExtensionsSortBy.cs diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index 0eb183d..16db89c 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -15,11 +15,15 @@ namespace Persistence.API.Controllers this.techMessagesRepository = techMessagesRepository; } - public Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage(RequestDto request, CancellationToken token) + [HttpGet] + public async Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage([FromQuery] RequestDto request, CancellationToken token) { - throw new NotImplementedException(); + var result = await techMessagesRepository.GetPage(request, token); + + return Ok(result); } + [HttpGet("statistics")] public async Task<ActionResult<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) { var result = await techMessagesRepository.GetStatistics(importantId, autoDrillingSystem, token); @@ -27,6 +31,7 @@ namespace Persistence.API.Controllers return Ok(result); } + [HttpGet("systems")] public async Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token) { var result = await techMessagesRepository.GetSystems(token); @@ -34,7 +39,8 @@ namespace Persistence.API.Controllers return Ok(result); } - public async Task<ActionResult<int>> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) + [HttpPost] + public async Task<ActionResult<int>> InsertRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) { var result = await techMessagesRepository.InsertRange(dtos, token); diff --git a/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs b/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs new file mode 100644 index 0000000..5d6817e --- /dev/null +++ b/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.Designer.cs @@ -0,0 +1,175 @@ +// <auto-generated /> +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Persistence.Database.Model; + +#nullable disable + +namespace Persistence.Database.Postgres.Migrations +{ + [DbContext(typeof(PersistenceDbContext))] + [Migration("20241126044756_TechMessageMigration")] + partial class TechMessageMigration + { + /// <inheritdoc /> + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("Russian_Russia.1251") + .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => + { + b.Property<Guid>("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Id события"); + + b.Property<string>("AutoDrillingSystem") + .HasColumnType("varchar(256)") + .HasComment("Система автобурения, к которой относится сообщение"); + + b.Property<double?>("Depth") + .HasColumnType("double precision") + .HasComment("Глубина забоя"); + + b.Property<int>("ImportantId") + .HasColumnType("integer") + .HasComment("Id Категории важности"); + + b.Property<string>("MessageText") + .HasColumnType("varchar(512)") + .HasComment("Текст сообщения"); + + b.Property<DateTimeOffset>("OccurrenceDate") + .HasColumnType("timestamp with time zone") + .HasComment("Дата возникновения"); + + b.Property<Guid>("UserId") + .HasColumnType("uuid") + .HasComment("Id пользователя за пультом бурильщика"); + + b.HasKey("EventId"); + + b.ToTable("TechMessage"); + }); + + modelBuilder.Entity("Persistence.Database.Model.DataSaub", b => + { + b.Property<DateTimeOffset>("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property<double?>("AxialLoad") + .HasColumnType("double precision") + .HasColumnName("axialLoad"); + + b.Property<double?>("BitDepth") + .HasColumnType("double precision") + .HasColumnName("bitDepth"); + + b.Property<double?>("BlockPosition") + .HasColumnType("double precision") + .HasColumnName("blockPosition"); + + b.Property<double?>("BlockSpeed") + .HasColumnType("double precision") + .HasColumnName("blockSpeed"); + + b.Property<double?>("Flow") + .HasColumnType("double precision") + .HasColumnName("flow"); + + b.Property<double?>("HookWeight") + .HasColumnType("double precision") + .HasColumnName("hookWeight"); + + b.Property<int>("IdFeedRegulator") + .HasColumnType("integer") + .HasColumnName("idFeedRegulator"); + + b.Property<int?>("Mode") + .HasColumnType("integer") + .HasColumnName("mode"); + + b.Property<double?>("Mse") + .HasColumnType("double precision") + .HasColumnName("mse"); + + b.Property<short>("MseState") + .HasColumnType("smallint") + .HasColumnName("mseState"); + + b.Property<double?>("Pressure") + .HasColumnType("double precision") + .HasColumnName("pressure"); + + b.Property<double?>("Pump0Flow") + .HasColumnType("double precision") + .HasColumnName("pump0Flow"); + + b.Property<double?>("Pump1Flow") + .HasColumnType("double precision") + .HasColumnName("pump1Flow"); + + b.Property<double?>("Pump2Flow") + .HasColumnType("double precision") + .HasColumnName("pump2Flow"); + + b.Property<double?>("RotorSpeed") + .HasColumnType("double precision") + .HasColumnName("rotorSpeed"); + + b.Property<double?>("RotorTorque") + .HasColumnType("double precision") + .HasColumnName("rotorTorque"); + + b.Property<string>("User") + .HasColumnType("text") + .HasColumnName("user"); + + b.Property<double?>("WellDepth") + .HasColumnType("double precision") + .HasColumnName("wellDepth"); + + b.HasKey("Date"); + + b.ToTable("DataSaub"); + }); + + modelBuilder.Entity("Persistence.Database.Model.Setpoint", b => + { + b.Property<Guid>("Key") + .HasColumnType("uuid") + .HasComment("Ключ"); + + b.Property<DateTimeOffset>("Created") + .HasColumnType("timestamp with time zone") + .HasComment("Дата создания уставки"); + + b.Property<int>("IdUser") + .HasColumnType("integer") + .HasComment("Id автора последнего изменения"); + + b.Property<object>("Value") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Значение уставки"); + + b.HasKey("Key", "Created"); + + b.ToTable("Setpoint"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs b/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs new file mode 100644 index 0000000..46a71a0 --- /dev/null +++ b/Persistence.Database.Postgres/Migrations/20241126044756_TechMessageMigration.cs @@ -0,0 +1,59 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistence.Database.Postgres.Migrations +{ + /// <inheritdoc /> + public partial class TechMessageMigration : Migration + { + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn<DateTimeOffset>( + 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<Guid>(type: "uuid", nullable: false, comment: "Id события"), + ImportantId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"), + OccurrenceDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), + Depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина забоя"), + MessageText = table.Column<string>(type: "varchar(512)", nullable: true, comment: "Текст сообщения"), + AutoDrillingSystem = table.Column<string>(type: "varchar(256)", nullable: true, comment: "Система автобурения, к которой относится сообщение"), + UserId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id пользователя за пультом бурильщика") + }, + constraints: table => + { + table.PrimaryKey("PK_TechMessage", x => x.EventId); + }); + } + + /// <inheritdoc /> + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TechMessage"); + + migrationBuilder.AlterColumn<DateTimeOffset>( + 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/PersistenceDbContextModelSnapshot.cs b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs index f41f669..4446a1c 100644 --- a/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs +++ b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs @@ -24,6 +24,42 @@ namespace Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => + { + b.Property<Guid>("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Id события"); + + b.Property<string>("AutoDrillingSystem") + .HasColumnType("varchar(256)") + .HasComment("Система автобурения, к которой относится сообщение"); + + b.Property<double?>("Depth") + .HasColumnType("double precision") + .HasComment("Глубина забоя"); + + b.Property<int>("ImportantId") + .HasColumnType("integer") + .HasComment("Id Категории важности"); + + b.Property<string>("MessageText") + .HasColumnType("varchar(512)") + .HasComment("Текст сообщения"); + + b.Property<DateTimeOffset>("OccurrenceDate") + .HasColumnType("timestamp with time zone") + .HasComment("Дата возникновения"); + + b.Property<Guid>("UserId") + .HasColumnType("uuid") + .HasComment("Id пользователя за пультом бурильщика"); + + b.HasKey("EventId"); + + b.ToTable("TechMessage"); + }); + modelBuilder.Entity("Persistence.Database.Model.DataSaub", b => { b.Property<DateTimeOffset>("Date") @@ -115,7 +151,7 @@ namespace Persistence.Database.Postgres.Migrations b.Property<DateTimeOffset>("Created") .HasColumnType("timestamp with time zone") - .HasComment("Дата изменения уставки"); + .HasComment("Дата создания уставки"); b.Property<int>("IdUser") .HasColumnType("integer") diff --git a/Persistence.Repository/DependencyInjection.cs b/Persistence.Repository/DependencyInjection.cs index 27063cb..a8a08cc 100644 --- a/Persistence.Repository/DependencyInjection.cs +++ b/Persistence.Repository/DependencyInjection.cs @@ -17,6 +17,7 @@ public static class DependencyInjection services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataRepository<DataSaub, DataSaubDto>>(); services.AddTransient<ISetpointRepository, SetpointRepository>(); + services.AddTransient<ITechMessagesRepository, TechMessagesRepository>(); return services; } diff --git a/Persistence.Repository/Extensions/EFExtensionsSortBy.cs b/Persistence.Repository/Extensions/EFExtensionsSortBy.cs new file mode 100644 index 0000000..03b9c65 --- /dev/null +++ b/Persistence.Repository/Extensions/EFExtensionsSortBy.cs @@ -0,0 +1,267 @@ +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; + +namespace Persistence.Repository.Extensions; + +public static class EFExtensionsSortBy +{ + struct TypeAccessor + { + public LambdaExpression KeySelector { get; set; } + public MethodInfo OrderBy { get; set; } + public MethodInfo OrderByDescending { get; set; } + public MethodInfo ThenBy { get; set; } + public MethodInfo ThenByDescending { get; set; } + } + + private static ConcurrentDictionary<Type, Dictionary<string, TypeAccessor>> TypePropSelectors { get; set; } = + new(); + + private static readonly MethodInfo methodOrderBy = GetExtOrderMethod("OrderBy"); + + private static readonly MethodInfo methodOrderByDescending = GetExtOrderMethod("OrderByDescending"); + + private static readonly MethodInfo methodThenBy = GetExtOrderMethod("ThenBy"); + + private static readonly MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending"); + + private static MethodInfo GetExtOrderMethod(string methodName) + => typeof(Queryable) + .GetMethods() + .Where(m => m.Name == methodName && + m.IsGenericMethodDefinition && + m.GetParameters().Length == 2 && + m.GetParameters()[1].ParameterType.IsAssignableTo(typeof(LambdaExpression))) + .Single(); + + private static Dictionary<string, TypeAccessor> MakeTypeAccessors(Type type) + { + var propContainer = new Dictionary<string, TypeAccessor>(); + var properties = type.GetProperties(); + foreach (var propertyInfo in properties) + { + var name = propertyInfo.Name.ToLower(); + ParameterExpression arg = Expression.Parameter(type, "x"); + MemberExpression property = Expression.Property(arg, propertyInfo.Name); + var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); + var typeAccessor = new TypeAccessor + { + KeySelector = selector, + OrderBy = methodOrderBy.MakeGenericMethod(type, propertyInfo.PropertyType), + OrderByDescending = methodOrderByDescending.MakeGenericMethod(type, propertyInfo.PropertyType), + ThenBy = methodThenBy.MakeGenericMethod(type, propertyInfo.PropertyType), + ThenByDescending = methodThenByDescending.MakeGenericMethod(type, propertyInfo.PropertyType), + }; + + propContainer.Add(name, typeAccessor); + } + + return propContainer; + } + + /// <summary> + /// Добавить в запрос сортировку по возрастанию или убыванию. + /// </summary> + /// <typeparam name="TSource"></typeparam> + /// <param name="query"></param> + /// <param name="propertySort"> + /// Свойство сортировки. + /// Состоит из названия свойства (в любом регистре) + /// и опционально указания направления сортировки "asc" или "desc" + /// </param> + /// <example> + /// var query = query("Date desc"); + /// </example> + /// <returns>Запрос с примененной сортировкой</returns> + public static IOrderedQueryable<TSource> SortBy<TSource>( + this IQueryable<TSource> query, + IEnumerable<string> propertySorts) + { + if (propertySorts?.Any() != true) + return (IOrderedQueryable<TSource>)query; + + var sortEnum = propertySorts.GetEnumerator(); + sortEnum.MoveNext(); + var orderedQuery = query.SortBy(sortEnum.Current); + + while (sortEnum.MoveNext()) + orderedQuery = orderedQuery.ThenSortBy(sortEnum.Current); + + return orderedQuery; + } + + /// <summary> + /// Добавить в запрос сортировку по возрастанию или убыванию. + /// Этот метод сбросит ранее наложенные сортировки. + /// </summary> + /// <typeparam name="TSource"></typeparam> + /// <param name="query"></param> + /// <param name="propertySort"> + /// Свойство сортировки. + /// Состоит из названия свойства (в любом регистре) + /// и опционально указания направления сортировки "asc" или "desc" + /// </param> + /// <example> + /// var query = query("Date desc"); + /// </example> + /// <returns>Запрос с примененной сортировкой</returns> + public static IOrderedQueryable<TSource> SortBy<TSource>( + this IQueryable<TSource> query, + string propertySort) + { + var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries); + var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc"; + var propertyName = parts[0]; + + var newQuery = query.SortBy(propertyName, isDesc); + return newQuery; + } + + /// <summary> + /// Добавить в запрос дополнительную сортировку по возрастанию или убыванию. + /// </summary> + /// <typeparam name="TSource"></typeparam> + /// <param name="query"></param> + /// <param name="propertySort"> + /// Свойство сортировки. + /// Состоит из названия свойства (в любом регистре) + /// и опционально указания направления сортировки "asc" или "desc" + /// </param> + /// <example> + /// var query = query("Date desc"); + /// </example> + /// <returns>Запрос с примененной сортировкой</returns> + public static IOrderedQueryable<TSource> ThenSortBy<TSource>( + this IOrderedQueryable<TSource> query, + string propertySort) + { + var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries); + var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc"; + var propertyName = parts[0]; + + var newQuery = query.ThenSortBy(propertyName, isDesc); + return newQuery; + } + + /// <summary> + /// Добавить в запрос сортировку по возрастанию или убыванию + /// </summary> + /// <typeparam name="TSource"></typeparam> + /// <param name="query"></param> + /// <param name="propertyName">Название свойства (в любом регистре)</param> + /// <param name="isDesc">Сортировать по убыванию</param> + /// <returns>Запрос с примененной сортировкой</returns> + public static IOrderedQueryable<TSource> SortBy<TSource>( + this IQueryable<TSource> query, + string propertyName, + bool isDesc) + { + Type rootType = typeof(TSource); + var typePropSelector = TypePropSelectors.GetOrAdd(rootType, MakeTypeAccessors); + var propertyNameLower = propertyName.ToLower(); + + MethodInfo orderByDescending; + MethodInfo orderByAscending; + + LambdaExpression? lambdaExpression = null; + + if (propertyName.Contains('.')) + { + Type type = rootType; + ParameterExpression rootExpression = Expression.Parameter(rootType, "x"); + Expression expr = rootExpression; + + var propertyPath = propertyName.Split(".", StringSplitOptions.RemoveEmptyEntries); + + for (int i = 0; i < propertyPath.Length; i++) + { + PropertyInfo pi = type.GetProperty(propertyPath[i])!; + expr = Expression.Property(expr, pi); + type = pi.PropertyType; + } + + Type delegateType = typeof(Func<,>).MakeGenericType(rootType, type); + lambdaExpression = Expression.Lambda(delegateType, expr, rootExpression); + + orderByAscending = methodOrderBy.MakeGenericMethod(rootType, type); + orderByDescending = methodOrderByDescending.MakeGenericMethod(rootType, type); + } + else + { + var rootTypeAccessor = typePropSelector[propertyNameLower]; + orderByAscending = rootTypeAccessor.OrderBy; + orderByDescending = rootTypeAccessor.OrderByDescending; + lambdaExpression = rootTypeAccessor.KeySelector; + } + + var genericMethod = isDesc + ? orderByDescending + : orderByAscending; + + var newQuery = (IOrderedQueryable<TSource>)genericMethod + .Invoke(genericMethod, new object[] { query, lambdaExpression })!; + return newQuery; + } + + /// <summary> + /// Добавить в запрос дополнительную сортировку по возрастанию или убыванию + /// </summary> + /// <typeparam name="TSource"></typeparam> + /// <param name="query"></param> + /// <param name="propertyName">Название свойства (в любом регистре)</param> + /// <param name="isDesc">Сортировать по убыванию</param> + /// <returns>Запрос с примененной сортировкой</returns> + public static IOrderedQueryable<TSource> ThenSortBy<TSource>( + this IOrderedQueryable<TSource> query, + string propertyName, + bool isDesc) + { + Type rootType = typeof(TSource); + var typePropSelector = TypePropSelectors.GetOrAdd(rootType, MakeTypeAccessors); + var propertyNameLower = propertyName.ToLower(); + + MethodInfo orderByDescending; + MethodInfo orderByAscending; + + LambdaExpression? lambdaExpression = null; + + // TODO: Устранить дублирование кода + if (propertyName.Contains('.')) + { + Type type = rootType; + ParameterExpression rootExpression = Expression.Parameter(rootType, "x"); + Expression expr = rootExpression; + + var propertyPath = propertyName.Split(".", StringSplitOptions.RemoveEmptyEntries); + + for (int i = 0; i < propertyPath.Length; i++) + { + PropertyInfo pi = type.GetProperty(propertyPath[i])!; + expr = Expression.Property(expr, pi); + type = pi.PropertyType; + } + + Type delegateType = typeof(Func<,>).MakeGenericType(rootType, type); + lambdaExpression = Expression.Lambda(delegateType, expr, rootExpression); + + orderByAscending = methodThenBy.MakeGenericMethod(rootType, type); + orderByDescending = methodThenByDescending.MakeGenericMethod(rootType, type); + } + else + { + var rootTypeAccessor = typePropSelector[propertyNameLower]; + orderByAscending = rootTypeAccessor.ThenBy; + orderByDescending = rootTypeAccessor.ThenByDescending; + lambdaExpression = rootTypeAccessor.KeySelector; + } + + var genericMethod = isDesc + ? orderByDescending + : orderByAscending; + + var newQuery = (IOrderedQueryable<TSource>)genericMethod + .Invoke(genericMethod, new object[] { query, lambdaExpression })!; + return newQuery; + } +} \ No newline at end of file diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index e7a37bc..f5ddb35 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -16,6 +16,25 @@ namespace Persistence.Repository.Repositories protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>(); + public async Task<PaginationContainer<TechMessageDto>> GetPage(RequestDto request, CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query + .SortBy(request.SortSettings) + .Skip(request.Skip) + .Take(request.Take) + .ToListAsync(); + var dto = new PaginationContainer<TechMessageDto>() + { + Skip = request.Skip, + Take = request.Take, + Count = entities.Count, + Items = entities.Select(e => e.Adapt<TechMessageDto>()) + }; + + return dto; + } + public async Task<int> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) { var query = GetQueryReadOnly(); diff --git a/Persistence/Repositories/ISetpointRepository.cs b/Persistence/Repositories/ISetpointRepository.cs index 1d82b16..db6838c 100644 --- a/Persistence/Repositories/ISetpointRepository.cs +++ b/Persistence/Repositories/ISetpointRepository.cs @@ -7,13 +7,13 @@ namespace Persistence.Repositories; /// </summary> public interface ISetpointRepository { - /// <summary> - /// Получить значения уставок по набору ключей - /// </summary> - /// <param name="setpointKeys"></param> - /// <param name="token"></param> - /// <returns></returns> - Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token); + /// <summary> + /// Получить значения уставок по набору ключей + /// </summary> + /// <param name="setpointKeys"></param> + /// <param name="token"></param> + /// <returns></returns> + Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token); /// <summary> /// Получить значения уставок за определенный момент времени diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs index c681184..1be90e9 100644 --- a/Persistence/Repositories/ITechMessagesRepository.cs +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -7,6 +7,14 @@ namespace Persistence.Repositories /// </summary> public interface ITechMessagesRepository { + /// <summary> + /// Получить страницу списка объектов + /// </summary> + /// <param name="request"></param> + /// <param name="token"></param> + /// <returns></returns> + Task<PaginationContainer<TechMessageDto>> GetPage(RequestDto request, CancellationToken token); + /// <summary> /// Добавление новых сообщений /// </summary> -- 2.45.2 From 7807e4aa1e956694f72dfc71541d6ed258f6d896 Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Tue, 26 Nov 2024 12:27:52 +0500 Subject: [PATCH 03/12] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=82=D0=B5=D1=85=D0=BD=D0=BE=D0=BB=D0=BE=D0=B3=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D1=85=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/TechMessagesController.cs | 6 +- Persistence.Client/Clients/ISetpointClient.cs | 2 +- .../Clients/ITechMessagesClient.cs | 25 +++ .../Clients/ITimeSeriesClient.cs | 3 +- .../Controllers/TechMessagesControllerTest.cs | 178 ++++++++++++++++++ .../Extensions/EFCoreExtensions.cs | 14 ++ Persistence.Repository/Data/SetpointDto.cs | 28 --- .../Repositories/TechMessagesRepository.cs | 1 + Persistence/Models/TechMessageDto.cs | 3 + 9 files changed, 227 insertions(+), 33 deletions(-) create mode 100644 Persistence.Client/Clients/ITechMessagesClient.cs create mode 100644 Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs create mode 100644 Persistence.IntegrationTests/Extensions/EFCoreExtensions.cs delete mode 100644 Persistence.Repository/Data/SetpointDto.cs diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index 16db89c..47d79d5 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -1,10 +1,12 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Persistence.Models; using Persistence.Repositories; namespace Persistence.API.Controllers { [ApiController] + [Authorize] [Route("api/[controller]")] public class TechMessagesController : ControllerBase, ITechMessages { @@ -44,7 +46,7 @@ namespace Persistence.API.Controllers { var result = await techMessagesRepository.InsertRange(dtos, token); - return Ok(result); + return CreatedAtAction(nameof(InsertRange), result); } } } diff --git a/Persistence.Client/Clients/ISetpointClient.cs b/Persistence.Client/Clients/ISetpointClient.cs index 49733f0..72d76e1 100644 --- a/Persistence.Client/Clients/ISetpointClient.cs +++ b/Persistence.Client/Clients/ISetpointClient.cs @@ -4,7 +4,7 @@ using Refit; namespace Persistence.Client.Clients; /// <summary> -/// Интерфейс для тестирования API, предназначенного для работы с уставками +/// Интерфейс клиента для работы с уставками /// </summary> public interface ISetpointClient { diff --git a/Persistence.Client/Clients/ITechMessagesClient.cs b/Persistence.Client/Clients/ITechMessagesClient.cs new file mode 100644 index 0000000..1426b54 --- /dev/null +++ b/Persistence.Client/Clients/ITechMessagesClient.cs @@ -0,0 +1,25 @@ +using Persistence.Models; +using Refit; + +namespace Persistence.Client.Clients +{ + /// <summary> + /// Интерфейс клиента для хранения технологических сообщений + /// </summary> + public interface ITechMessagesClient + { + private const string BaseRoute = "/api/techMessages"; + + [Get($"{BaseRoute}")] + Task<IApiResponse<PaginationContainer<TechMessageDto>>> GetPage([Query] RequestDto request, CancellationToken token); + + [Post($"{BaseRoute}")] + Task<IApiResponse<int>> InsertRange([Body] IEnumerable<TechMessageDto> dtos, CancellationToken token); + + [Get($"{BaseRoute}/systems")] + Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token); + + [Get($"{BaseRoute}/statistics")] + Task<IApiResponse<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); + } +} diff --git a/Persistence.Client/Clients/ITimeSeriesClient.cs b/Persistence.Client/Clients/ITimeSeriesClient.cs index 8f7ef0e..349337b 100644 --- a/Persistence.Client/Clients/ITimeSeriesClient.cs +++ b/Persistence.Client/Clients/ITimeSeriesClient.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc; -using Persistence.Models; +using Persistence.Models; using Refit; namespace Persistence.Client.Clients; diff --git a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs new file mode 100644 index 0000000..9eb0e9d --- /dev/null +++ b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs @@ -0,0 +1,178 @@ +using System.Net; +using Microsoft.Extensions.DependencyInjection; +using Persistence.Client; +using Persistence.Client.Clients; +using Persistence.Database.Entity; +using Persistence.Models; +using Xunit; + +namespace Persistence.IntegrationTests.Controllers +{ + public class TechMessagesControllerTest : BaseIntegrationTest + { + private readonly ITechMessagesClient techMessagesClient; + public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory) + { + var scope = factory.Services.CreateScope(); + var persistenceClientFactory = scope.ServiceProvider + .GetRequiredService<PersistenceClientFactory>(); + + techMessagesClient = persistenceClientFactory.GetClient<ITechMessagesClient>(); + } + + [Fact] + public async Task GetPage_returns_success() + { + //arrange + dbContext.CleanupDbSet<TechMessage>(); + var requestDto = new RequestDto() + { + Skip = 1, + Take = 2, + SortSettings = nameof(TechMessageDto.ImportantId) + }; + + //act + var response = await techMessagesClient.GetPage(requestDto, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Empty(response.Content.Items); + Assert.Equal(requestDto.Skip, response.Content.Skip); + Assert.Equal(requestDto.Take, response.Content.Take); + } + + [Fact] + public async Task GetPage_AfterSave_returns_success() + { + //arrange + var dtos = await InsertRange(); + var dtosCount = dtos.Count(); + var requestDto = new RequestDto() + { + Skip = 0, + Take = 2, + SortSettings = nameof(TechMessageDto.ImportantId) + }; + + //act + var response = await techMessagesClient.GetPage(requestDto, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Equal(dtosCount, response.Content.Count); + } + + [Fact] + public async Task InsertRange_returns_success() + { + await InsertRange(); + } + + [Fact] + public async Task GetSystems_returns_success() + { + //act + dbContext.CleanupDbSet<TechMessage>(); + var response = await techMessagesClient.GetSystems(new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Empty(response.Content); + } + + [Fact] + public async Task GetSystems_AfterSave_returns_success() + { + //arrange + var dtos = await InsertRange(); + var systems = dtos + .Select(e => e.AutoDrillingSystem) + .Distinct() + .ToArray(); + + //act + var response = await techMessagesClient.GetSystems(new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + string?[]? content = response.Content?.ToArray(); + Assert.Equal(systems, content); + } + + [Fact] + public async Task GetStatistics_returns_success() + { + //arrange + dbContext.CleanupDbSet<TechMessage>(); + var imortantId = 1; + var autoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem); + + //act + var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(0, response.Content); + } + + [Fact] + public async Task GetStatistics_AfterSave_returns_success() + { + //arrange + var imortantId = 1; + var autoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem); + var dtos = await InsertRange(); + var filteredDtos = dtos.Where(e => e.ImportantId == imortantId && e.AutoDrillingSystem == e.AutoDrillingSystem); + + //act + var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(filteredDtos.Count(), response.Content); + } + + public async Task<IEnumerable<TechMessageDto>> InsertRange() + { + //arrange + var dtos = new List<TechMessageDto>() + { + new TechMessageDto() + { + EventId = Guid.NewGuid(), + ImportantId = 1, + OccurrenceDate = DateTimeOffset.UtcNow, + Depth = 1.11, + MessageText = nameof(TechMessageDto.MessageText), + AutoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem), + UserId = Guid.NewGuid() + }, + new TechMessageDto() + { + EventId = Guid.NewGuid(), + ImportantId = 2, + OccurrenceDate = DateTimeOffset.UtcNow, + Depth = 2.22, + MessageText = nameof(TechMessageDto.MessageText), + AutoDrillingSystem = nameof(TechMessageDto.AutoDrillingSystem), + UserId = Guid.NewGuid() + } + }; + + + //act + var response = await techMessagesClient.InsertRange(dtos, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal(dtos.Count, response.Content); + + return dtos; + } + } +} diff --git a/Persistence.IntegrationTests/Extensions/EFCoreExtensions.cs b/Persistence.IntegrationTests/Extensions/EFCoreExtensions.cs new file mode 100644 index 0000000..6b09587 --- /dev/null +++ b/Persistence.IntegrationTests/Extensions/EFCoreExtensions.cs @@ -0,0 +1,14 @@ +using Persistence.Database.Model; + +namespace Persistence.IntegrationTests.Extensions; + +public static class EFCoreExtensions +{ + public static void CleanupDbSet<T>(this PersistenceDbContext dbContext) + where T : class + { + var dbset = dbContext.Set<T>(); + dbset.RemoveRange(dbset); + dbContext.SaveChanges(); + } +} diff --git a/Persistence.Repository/Data/SetpointDto.cs b/Persistence.Repository/Data/SetpointDto.cs deleted file mode 100644 index 4a20aa4..0000000 --- a/Persistence.Repository/Data/SetpointDto.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Persistence.Repository.Data -{ - /// <summary> - /// Модель для работы с уставкой - /// </summary> - public class SetpointDto - { - /// <summary> - /// Идентификатор уставки - /// </summary> - public int Id { get; set; } - - /// <summary> - /// Значение уставки - /// </summary> - public required object Value { get; set; } - - /// <summary> - /// Дата сохранения уставки - /// </summary> - public DateTimeOffset Edit { get; set; } - - /// <summary> - /// Ключ пользователя - /// </summary> - public int IdUser { get; set; } - } -} diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index f5ddb35..9abda86 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Persistence.Database.Entity; using Persistence.Models; using Persistence.Repositories; +using Persistence.Repository.Extensions; namespace Persistence.Repository.Repositories { diff --git a/Persistence/Models/TechMessageDto.cs b/Persistence/Models/TechMessageDto.cs index 274a1d5..625b22f 100644 --- a/Persistence/Models/TechMessageDto.cs +++ b/Persistence/Models/TechMessageDto.cs @@ -1,5 +1,8 @@ namespace Persistence.Models { + /// <summary> + /// Модель технологического сообщения + /// </summary> public class TechMessageDto { /// <summary> -- 2.45.2 From 81474ea297cf252ee3c6bd7ff1132eaded62db42 Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Tue, 26 Nov 2024 14:07:36 +0500 Subject: [PATCH 04/12] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/TechMessagesController.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index 47d79d5..676ae8e 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -48,5 +48,20 @@ namespace Persistence.API.Controllers return CreatedAtAction(nameof(InsertRange), result); } + + [HttpGet("categories")] + public ActionResult<Dictionary<int, string>> GetImportantCategories() + { + var result = new Dictionary<int, string>() + { + { 0, "System" }, + { 1, "Авария" }, + { 2, "Предупреждение" }, + { 3, "Инфо" }, + { 4, "Прочее" } + }; + + return Ok(result); + } } } -- 2.45.2 From 0234f2096d53e730518d07a92dbee6db7b1b2dec Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Wed, 27 Nov 2024 10:45:31 +0500 Subject: [PATCH 05/12] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=B0=D0=B2?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B7=20Keycloak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Persistence.API/Persistence.API.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Persistence.API/Persistence.API.csproj b/Persistence.API/Persistence.API.csproj index 2b8cb73..c14b509 100644 --- a/Persistence.API/Persistence.API.csproj +++ b/Persistence.API/Persistence.API.csproj @@ -8,7 +8,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" /> + <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" /> + <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.2.1" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> </ItemGroup> -- 2.45.2 From dc66522c0ff4c8f7c073ed0b2a07bbe8d5773341 Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Wed, 27 Nov 2024 13:08:06 +0500 Subject: [PATCH 06/12] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20DocumentationFile,=20=D0=BE=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D0=BB=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataSaubController.cs | 4 + .../Controllers/SetpointController.cs | 100 ++++++++----- .../Controllers/TechMessagesController.cs | 140 +++++++++++------- .../Controllers/TimeSeriesController.cs | 40 ++++- Persistence.API/DependencyInjection.cs | 10 +- Persistence.API/Persistence.API.csproj | 2 + 6 files changed, 193 insertions(+), 103 deletions(-) diff --git a/Persistence.API/Controllers/DataSaubController.cs b/Persistence.API/Controllers/DataSaubController.cs index 63069a9..437aee3 100644 --- a/Persistence.API/Controllers/DataSaubController.cs +++ b/Persistence.API/Controllers/DataSaubController.cs @@ -4,6 +4,10 @@ using Persistence.Repositories; using Persistence.Repository.Data; namespace Persistence.API.Controllers; + +/// <summary> +/// ������ � ���������� ������� +/// </summary> [ApiController] [Authorize] [Route("api/[controller]")] diff --git a/Persistence.API/Controllers/SetpointController.cs b/Persistence.API/Controllers/SetpointController.cs index 9a6bd61..5a5cb52 100644 --- a/Persistence.API/Controllers/SetpointController.cs +++ b/Persistence.API/Controllers/SetpointController.cs @@ -3,51 +3,79 @@ using Microsoft.AspNetCore.Mvc; using Persistence.Models; using Persistence.Repositories; -namespace Persistence.API.Controllers +namespace Persistence.API.Controllers; + +/// <summary> +/// Работа с уставками +/// </summary> +[ApiController] +[Authorize] +[Route("api/[controller]")] +public class SetpointController : ControllerBase, ISetpointApi { - [ApiController] - [Authorize] - [Route("api/[controller]")] - public class SetpointController : ControllerBase, ISetpointApi + private readonly ISetpointRepository setpointRepository; + + public SetpointController(ISetpointRepository setpointRepository) { - private readonly ISetpointRepository setpointRepository; + this.setpointRepository = setpointRepository; + } - public SetpointController(ISetpointRepository setpointRepository) - { - this.setpointRepository = setpointRepository; - } + /// <summary> + /// Получить актуальные значения уставок + /// </summary> + /// <param name="setpointKeys"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("current")] + public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrent([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token) + { + var result = await setpointRepository.GetCurrent(setpointKeys, token); - [HttpGet("current")] - public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetCurrent([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token) - { - var result = await setpointRepository.GetCurrent(setpointKeys, token); + return Ok(result); + } - return Ok(result); - } + /// <summary> + /// Получить значения уставок за определенный момент времени + /// </summary> + /// <param name="setpointKeys"></param> + /// <param name="historyMoment"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("history")] + public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistory([FromQuery] IEnumerable<Guid> setpointKeys, [FromQuery] DateTimeOffset historyMoment, CancellationToken token) + { + var result = await setpointRepository.GetHistory(setpointKeys, historyMoment, token); - [HttpGet("history")] - public async Task<ActionResult<IEnumerable<SetpointValueDto>>> GetHistory([FromQuery] IEnumerable<Guid> setpointKeys, [FromQuery] DateTimeOffset historyMoment, CancellationToken token) - { - var result = await setpointRepository.GetHistory(setpointKeys, historyMoment, token); + return Ok(result); + } - return Ok(result); - } + /// <summary> + /// Получить историю изменений значений уставок + /// </summary> + /// <param name="setpointKeys"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("log")] + public async Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token) + { + var result = await setpointRepository.GetLog(setpointKeys, token); - [HttpGet("log")] - public async Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([FromQuery] IEnumerable<Guid> setpointKeys, CancellationToken token) - { - var result = await setpointRepository.GetLog(setpointKeys, token); + return Ok(result); + } - return Ok(result); - } + /// <summary> + /// Сохранить уставку + /// </summary> + /// <param name="setpointKey"></param> + /// <param name="newValue"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpPost] + public async Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token) + { + // ToDo: вычитка idUser + await setpointRepository.Save(setpointKey, newValue, 0, token); - [HttpPost] - public async Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token) - { - // ToDo: вычитка idUser - await setpointRepository.Save(setpointKey, newValue, 0, token); - - return Ok(); - } + return Ok(); } } diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index 676ae8e..7411c19 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -3,65 +3,95 @@ using Microsoft.AspNetCore.Mvc; using Persistence.Models; using Persistence.Repositories; -namespace Persistence.API.Controllers +namespace Persistence.API.Controllers; + +/// <summary> +/// Работа с состояниями систем автобурения (АБ) +/// </summary> +[ApiController] +[Authorize] +[Route("api/[controller]")] +public class TechMessagesController : ControllerBase, ITechMessages { - [ApiController] - [Authorize] - [Route("api/[controller]")] - public class TechMessagesController : ControllerBase, ITechMessages + private readonly ITechMessagesRepository techMessagesRepository; + + public TechMessagesController(ITechMessagesRepository techMessagesRepository) { - private readonly ITechMessagesRepository techMessagesRepository; + this.techMessagesRepository = techMessagesRepository; + } - public TechMessagesController(ITechMessagesRepository techMessagesRepository) + /// <summary> + /// Получить список технологических сообщений в виде страницы + /// </summary> + /// <param name="request"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet] + public async Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage([FromQuery] RequestDto request, CancellationToken token) + { + var result = await techMessagesRepository.GetPage(request, token); + + return Ok(result); + } + + /// <summary> + /// Получить статистику по системам + /// </summary> + /// <param name="importantId"></param> + /// <param name="autoDrillingSystem"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("statistics")] + public async Task<ActionResult<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + { + var result = await techMessagesRepository.GetStatistics(importantId, autoDrillingSystem, token); + + return Ok(result); + } + + /// <summary> + /// Получить список всех систем + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("systems")] + public async Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token) + { + var result = await techMessagesRepository.GetSystems(token); + + return Ok(result); + } + + /// <summary> + /// Добавить новые технологические сообщения + /// </summary> + /// <param name="dtos"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpPost] + public async Task<ActionResult<int>> InsertRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) + { + var result = await techMessagesRepository.InsertRange(dtos, token); + + return CreatedAtAction(nameof(InsertRange), result); + } + + /// <summary> + /// Получить словарь категорий + /// </summary> + /// <returns></returns> + [HttpGet("categories")] + public ActionResult<Dictionary<int, string>> GetImportantCategories() + { + var result = new Dictionary<int, string>() { - this.techMessagesRepository = techMessagesRepository; - } + { 0, "System" }, + { 1, "Авария" }, + { 2, "Предупреждение" }, + { 3, "Инфо" }, + { 4, "Прочее" } + }; - [HttpGet] - public async Task<ActionResult<PaginationContainer<TechMessageDto>>> GetPage([FromQuery] RequestDto request, CancellationToken token) - { - var result = await techMessagesRepository.GetPage(request, token); - - return Ok(result); - } - - [HttpGet("statistics")] - public async Task<ActionResult<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) - { - var result = await techMessagesRepository.GetStatistics(importantId, autoDrillingSystem, token); - - return Ok(result); - } - - [HttpGet("systems")] - public async Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token) - { - var result = await techMessagesRepository.GetSystems(token); - - return Ok(result); - } - - [HttpPost] - public async Task<ActionResult<int>> InsertRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) - { - var result = await techMessagesRepository.InsertRange(dtos, token); - - return CreatedAtAction(nameof(InsertRange), result); - } - - [HttpGet("categories")] - public ActionResult<Dictionary<int, string>> GetImportantCategories() - { - var result = new Dictionary<int, string>() - { - { 0, "System" }, - { 1, "Авария" }, - { 2, "Предупреждение" }, - { 3, "Инфо" }, - { 4, "Прочее" } - }; - - return Ok(result); - } + return Ok(result); } } diff --git a/Persistence.API/Controllers/TimeSeriesController.cs b/Persistence.API/Controllers/TimeSeriesController.cs index a4a860a..a4f00f3 100644 --- a/Persistence.API/Controllers/TimeSeriesController.cs +++ b/Persistence.API/Controllers/TimeSeriesController.cs @@ -4,6 +4,7 @@ using Persistence.Models; using Persistence.Repositories; namespace Persistence.API.Controllers; + [ApiController] [Authorize] [Route("api/[controller]")] @@ -12,12 +13,18 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt { private ITimeSeriesDataRepository<TDto> timeSeriesDataRepository; - public TimeSeriesController(ITimeSeriesDataRepository<TDto> timeSeriesDataRepository) - { - this.timeSeriesDataRepository = timeSeriesDataRepository; + public TimeSeriesController(ITimeSeriesDataRepository<TDto> timeSeriesDataRepository) + { + this.timeSeriesDataRepository = timeSeriesDataRepository; } - [HttpGet] + /// <summary> + /// �������� ������ ��������, ��������������� ��������� ��� + /// </summary> + /// <param name="dateBegin"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] public async Task<IActionResult> Get(DateTimeOffset dateBegin, CancellationToken token) { @@ -25,21 +32,40 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt return Ok(result); } - [HttpGet("datesRange")] + /// <summary> + /// �������� �������� ���, ��� ������� ���� ������ � ����������� + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("datesRange")] public async Task<IActionResult> GetDatesRange(CancellationToken token) { var result = await this.timeSeriesDataRepository.GetDatesRange(token); return Ok(result); } - [HttpGet("resampled")] + /// <summary> + /// �������� ������ �������� � �������������, ��������������� ��������� ��� + /// </summary> + /// <param name="dateBegin"></param> + /// <param name="intervalSec"></param> + /// <param name="approxPointsCount"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("resampled")] public async Task<IActionResult> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { var result = await this.timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token); return Ok(result); } - [HttpPost] + /// <summary> + /// �������� ������ + /// </summary> + /// <param name="dtos"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpPost] public async Task<IActionResult> InsertRange(IEnumerable<TDto> dtos, CancellationToken token) { var result = await this.timeSeriesDataRepository.InsertRange(dtos, token); diff --git a/Persistence.API/DependencyInjection.cs b/Persistence.API/DependencyInjection.cs index cdfca4c..5d7c646 100644 --- a/Persistence.API/DependencyInjection.cs +++ b/Persistence.API/DependencyInjection.cs @@ -1,3 +1,4 @@ +using System.Reflection; using System.Text.Json.Nodes; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; @@ -38,11 +39,10 @@ public static class DependencyInjection c.AddKeycloackSecurity(configuration); else c.AddDefaultSecurity(configuration); - //var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; - //var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); - //var includeControllerXmlComment = true; - //options.IncludeXmlComments(xmlPath, includeControllerXmlComment); - //options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "AsbCloudApp.xml"), includeControllerXmlComment); + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + var includeControllerXmlComment = true; + c.IncludeXmlComments(xmlPath, includeControllerXmlComment); }); } diff --git a/Persistence.API/Persistence.API.csproj b/Persistence.API/Persistence.API.csproj index c14b509..dfff363 100644 --- a/Persistence.API/Persistence.API.csproj +++ b/Persistence.API/Persistence.API.csproj @@ -5,6 +5,8 @@ <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn);1591</NoWarn> </PropertyGroup> <ItemGroup> -- 2.45.2 From 1e87523ab948ce168385456cf1ad9ee6dcd6b9e0 Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Thu, 28 Nov 2024 08:55:50 +0500 Subject: [PATCH 07/12] =?UTF-8?q?=D0=92=D0=BD=D0=B5=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=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 /// <param name="autoDrillingSystem"></param> /// <param name="token"></param> /// <returns></returns> - [HttpGet("statistics")] - public async Task<ActionResult<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + [HttpGet("statistics/{autoDrillingSystem}")] + public async Task<ActionResult<int>> 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<TechMessageDto, TechMessage>() + .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 -{ - /// <inheritdoc /> - public partial class TechMessageMigration : Migration - { - /// <inheritdoc /> - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn<DateTimeOffset>( - 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<Guid>(type: "uuid", nullable: false, comment: "Id события"), - ImportantId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"), - OccurrenceDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), - Depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина забоя"), - MessageText = table.Column<string>(type: "varchar(512)", nullable: true, comment: "Текст сообщения"), - AutoDrillingSystem = table.Column<string>(type: "varchar(256)", nullable: true, comment: "Система автобурения, к которой относится сообщение"), - UserId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id пользователя за пультом бурильщика") - }, - constraints: table => - { - table.PrimaryKey("PK_TechMessage", x => x.EventId); - }); - } - - /// <inheritdoc /> - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "TechMessage"); - - migrationBuilder.AlterColumn<DateTimeOffset>( - 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 { /// <inheritdoc /> @@ -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<Guid>("SystemId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Id системы автобурения"); + + b.Property<string>("Description") + .HasColumnType("text") + .HasComment("Описание системы автобурения"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("varchar(256)") + .HasComment("Наименование системы автобурения"); + + b.HasKey("SystemId"); + + b.ToTable("ADSystem"); + }); + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => { b.Property<Guid>("EventId") @@ -34,10 +55,6 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id события"); - b.Property<string>("AutoDrillingSystem") - .HasColumnType("varchar(256)") - .HasComment("Система автобурения, к которой относится сообщение"); - b.Property<double?>("Depth") .HasColumnType("double precision") .HasComment("Глубина забоя"); @@ -47,6 +64,7 @@ namespace Persistence.Database.Postgres.Migrations .HasComment("Id Категории важности"); b.Property<string>("MessageText") + .IsRequired() .HasColumnType("varchar(512)") .HasComment("Текст сообщения"); @@ -54,15 +72,44 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата возникновения"); + b.Property<Guid>("SystemId") + .HasColumnType("uuid") + .HasComment("Id системы автобурения, к которой относится сообщение"); + b.Property<Guid>("UserId") .HasColumnType("uuid") .HasComment("Id пользователя за пультом бурильщика"); b.HasKey("EventId"); + b.HasIndex("SystemId"); + b.ToTable("TechMessage"); }); + modelBuilder.Entity("Persistence.Database.Entity.TimestampedSet", b => + { + b.Property<Guid>("IdDiscriminator") + .HasColumnType("uuid") + .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + + b.Property<DateTimeOffset>("Timestamp") + .HasColumnType("timestamp with time zone") + .HasComment("Отметка времени, строго в UTC"); + + b.Property<string>("Set") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Набор сохраняемых данных"); + + b.HasKey("IdDiscriminator", "Timestamp"); + + b.ToTable("TimestampedSets", t => + { + t.HasComment("Общая таблица данных временных рядов"); + }); + }); + modelBuilder.Entity("Persistence.Database.Model.DataSaub", b => { b.Property<DateTimeOffset>("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 +{ + /// <inheritdoc /> + public partial class TechMessageMigration : Migration + { + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ADSystem", + columns: table => new + { + SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы автобурения"), + Name = table.Column<string>(type: "varchar(256)", nullable: false, comment: "Наименование системы автобурения"), + Description = table.Column<string>(type: "text", nullable: true, comment: "Описание системы автобурения") + }, + constraints: table => + { + table.PrimaryKey("PK_ADSystem", x => x.SystemId); + }); + + migrationBuilder.CreateTable( + name: "TimestampedSets", + columns: table => new + { + IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"), + Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"), + Set = table.Column<string>(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<Guid>(type: "uuid", nullable: false, comment: "Id события"), + ImportantId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"), + OccurrenceDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), + Depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина забоя"), + MessageText = table.Column<string>(type: "varchar(512)", nullable: false, comment: "Текст сообщения"), + SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы автобурения, к которой относится сообщение"), + UserId = table.Column<Guid>(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"); + } + + /// <inheritdoc /> + 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<Guid>("SystemId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasComment("Id системы автобурения"); + + b.Property<string>("Description") + .HasColumnType("text") + .HasComment("Описание системы автобурения"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("varchar(256)") + .HasComment("Наименование системы автобурения"); + + b.HasKey("SystemId"); + + b.ToTable("ADSystem"); + }); + modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => { b.Property<Guid>("EventId") @@ -31,10 +52,6 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id события"); - b.Property<string>("AutoDrillingSystem") - .HasColumnType("varchar(256)") - .HasComment("Система автобурения, к которой относится сообщение"); - b.Property<double?>("Depth") .HasColumnType("double precision") .HasComment("Глубина забоя"); @@ -44,6 +61,7 @@ namespace Persistence.Database.Postgres.Migrations .HasComment("Id Категории важности"); b.Property<string>("MessageText") + .IsRequired() .HasColumnType("varchar(512)") .HasComment("Текст сообщения"); @@ -51,15 +69,44 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата возникновения"); + b.Property<Guid>("SystemId") + .HasColumnType("uuid") + .HasComment("Id системы автобурения, к которой относится сообщение"); + b.Property<Guid>("UserId") .HasColumnType("uuid") .HasComment("Id пользователя за пультом бурильщика"); b.HasKey("EventId"); + b.HasIndex("SystemId"); + b.ToTable("TechMessage"); }); + modelBuilder.Entity("Persistence.Database.Entity.TimestampedSet", b => + { + b.Property<Guid>("IdDiscriminator") + .HasColumnType("uuid") + .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + + b.Property<DateTimeOffset>("Timestamp") + .HasColumnType("timestamp with time zone") + .HasComment("Отметка времени, строго в UTC"); + + b.Property<string>("Set") + .IsRequired() + .HasColumnType("jsonb") + .HasComment("Набор сохраняемых данных"); + + b.HasKey("IdDiscriminator", "Timestamp"); + + b.ToTable("TimestampedSets", t => + { + t.HasComment("Общая таблица данных временных рядов"); + }); + }); + modelBuilder.Entity("Persistence.Database.Model.DataSaub", b => { b.Property<DateTimeOffset>("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<TechMessage>(); 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<int> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token) + public async Task<Dictionary<string, int>> 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<IEnumerable<string>> GetSystems(CancellationToken token) + public async Task<IEnumerable<ADSystemDto>> 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<ADSystem>(); + var entities = await query.ToListAsync(); + var dtos = entities.Select(e => e.Adapt<ADSystemDto>()); + + return dtos; + }); + + return systems ?? []; } public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) { - var entities = dtos.Select(d => d.Adapt<TechMessage>()); + var entities = dtos.Select(dto => + { + var task = Task.Run(async () => + { + var entity = dto.Adapt<TechMessage>(); + 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<TechMessage>().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); return result; } + + private async Task<Guid> CreateSystem(string name) + { + memoryCache.Remove(SystemCacheKey); + + var systemId = Guid.NewGuid(); + var entity = new ADSystem() + { + SystemId = systemId, + Name = name + }; + + await db.Set<ADSystem>().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 -{ - /// <summary> - /// Интерфейс для API сообщений о состояниях работы систем автобурения (АБ) - /// </summary> - public interface ITechMessages : ITableDataApi<TechMessageDto, RequestDto> - { - /// <summary> - /// Добавление новых сообщений - /// </summary> - /// <param name="dtos"></param> - /// <param name="token"></param> - /// <returns></returns> - Task<ActionResult<int>> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token); - - /// <summary> - /// Получение списка систем АБ - /// </summary> - /// <param name="token"></param> - /// <returns></returns> - Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token); - - /// <summary> - /// Получение статистики - /// </summary> - /// <param name="importantId">Id Категории важности</param> - /// <param name="autoDrillingSystem">Система АБ</param> - /// <param name="token"></param> - /// <returns></returns> - Task<ActionResult<int>> 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; + +/// <summary> +/// Модель системы автобурения +/// </summary> +public class ADSystemDto +{ + /// <summary> + /// Ключ + /// </summary> + public Guid SystemId { get; set; } + + /// <summary> + /// Наименование + /// </summary> + public required string Name { get; set; } + + /// <summary> + /// Описание + /// </summary> + 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 { /// <summary> /// Модель технологического сообщения @@ -8,32 +10,39 @@ /// <summary> /// Id события /// </summary> + [Required] public Guid EventId { get; set; } /// <summary> /// Id Категории важности /// </summary> - public int ImportantId { get; set; } + [Range(0, int.MaxValue, ErrorMessage = "Id Категории важности не может быть меньше 0")] + public int CategoryId { get; set; } /// <summary> /// Дата возникновения /// </summary> - public DateTimeOffset OccurrenceDate { get; set; } + public DateTimeOffset Timestamp { get; set; } /// <summary> /// Глубина забоя /// </summary> + [Range(0, double.MaxValue, ErrorMessage = "Глубина забоя не может быть меньше 0")] public double? Depth { get; set; } /// <summary> /// Текст сообщения /// </summary> - public string? MessageText { get; set; } + [Required] + [StringLength(512, MinimumLength = 1, ErrorMessage = "Допустимая длина текста сообщения от 1 до 512 символов")] + public required string MessageText { get; set; } /// <summary> /// Система автобурения, к которой относится сообщение /// </summary> - public string? AutoDrillingSystem { get; set; } + [Required] + [StringLength(256, MinimumLength = 1, ErrorMessage = "Допустимая длина наименования системы АБ от 1 до 256 символов")] + public required string System { get; set; } /// <summary> /// 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 /// </summary> /// <param name="token"></param> /// <returns></returns> - Task<IEnumerable<string>> GetSystems(CancellationToken token); + Task<IEnumerable<ADSystemDto>> GetSystems(CancellationToken token); /// <summary> /// Получение количества сообщений по категориям и системам автобурения @@ -37,6 +38,6 @@ namespace Persistence.Repositories /// <param name="autoDrillingSystem">Система автобурения</param> /// <param name="token"></param> /// <returns></returns> - Task<int> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); + Task<Dictionary<string, int>> GetStatistics(int? importantId, string? autoDrillingSystem, CancellationToken token); } } -- 2.45.2 From 097b422310ae70aef9c94c59d0e38fcc4535385f Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Thu, 28 Nov 2024 13:13:07 +0500 Subject: [PATCH 08/12] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20TechMessagesRepository,=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D1=82=D0=B5=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/ITechMessagesClient.cs | 7 +- ...28074729_TechMessageMigration.Designer.cs} | 18 ++--- ...=> 20241128074729_TechMessageMigration.cs} | 21 +----- .../PersistenceDbContextModelSnapshot.cs | 16 ++--- .../PersistenceDbContext.cs | 13 +++- Persistence.Database/Entity/TechMessage.cs | 4 +- .../Controllers/TechMessagesControllerTest.cs | 44 ++++++++++-- .../Repositories/TechMessagesRepository.cs | 67 ++++++++++--------- .../Repositories/ITechMessagesRepository.cs | 2 +- 9 files changed, 110 insertions(+), 82 deletions(-) rename Persistence.Database.Postgres/Migrations/{20241127123045_TechMessageMigration.Designer.cs => 20241128074729_TechMessageMigration.Designer.cs} (98%) rename Persistence.Database.Postgres/Migrations/{20241127123045_TechMessageMigration.cs => 20241128074729_TechMessageMigration.cs} (70%) diff --git a/Persistence.Client/Clients/ITechMessagesClient.cs b/Persistence.Client/Clients/ITechMessagesClient.cs index 1426b54..43839fe 100644 --- a/Persistence.Client/Clients/ITechMessagesClient.cs +++ b/Persistence.Client/Clients/ITechMessagesClient.cs @@ -1,4 +1,5 @@ -using Persistence.Models; +using Microsoft.AspNetCore.Mvc; +using Persistence.Models; using Refit; namespace Persistence.Client.Clients @@ -19,7 +20,7 @@ namespace Persistence.Client.Clients [Get($"{BaseRoute}/systems")] Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token); - [Get($"{BaseRoute}/statistics")] - Task<IApiResponse<int>> GetStatistics(int importantId, string autoDrillingSystem, CancellationToken token); + [Get($"{BaseRoute}/statistics/" + "{autoDrillingSystem}")] + Task<IApiResponse<int>> GetStatistics(string? autoDrillingSystem, int? importantId, CancellationToken token); } } diff --git a/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.Designer.cs b/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.Designer.cs similarity index 98% rename from Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.Designer.cs rename to Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.Designer.cs index 2e5a30a..6678078 100644 --- a/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.Designer.cs +++ b/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.Designer.cs @@ -12,7 +12,7 @@ using Persistence.Database.Model; namespace Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistenceDbContext))] - [Migration("20241127123045_TechMessageMigration")] + [Migration("20241128074729_TechMessageMigration")] partial class TechMessageMigration { /// <inheritdoc /> @@ -55,27 +55,27 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id события"); + b.Property<int>("CategoryId") + .HasColumnType("integer") + .HasComment("Id Категории важности"); + b.Property<double?>("Depth") .HasColumnType("double precision") .HasComment("Глубина забоя"); - b.Property<int>("ImportantId") - .HasColumnType("integer") - .HasComment("Id Категории важности"); - b.Property<string>("MessageText") .IsRequired() .HasColumnType("varchar(512)") .HasComment("Текст сообщения"); - b.Property<DateTimeOffset>("OccurrenceDate") - .HasColumnType("timestamp with time zone") - .HasComment("Дата возникновения"); - b.Property<Guid>("SystemId") .HasColumnType("uuid") .HasComment("Id системы автобурения, к которой относится сообщение"); + b.Property<DateTimeOffset>("Timestamp") + .HasColumnType("timestamp with time zone") + .HasComment("Дата возникновения"); + b.Property<Guid>("UserId") .HasColumnType("uuid") .HasComment("Id пользователя за пультом бурильщика"); diff --git a/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs b/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.cs similarity index 70% rename from Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs rename to Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.cs index d3a5ac2..047ad52 100644 --- a/Persistence.Database.Postgres/Migrations/20241127123045_TechMessageMigration.cs +++ b/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.cs @@ -24,27 +24,13 @@ namespace Persistence.Database.Postgres.Migrations table.PrimaryKey("PK_ADSystem", x => x.SystemId); }); - migrationBuilder.CreateTable( - name: "TimestampedSets", - columns: table => new - { - IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"), - Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"), - Set = table.Column<string>(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<Guid>(type: "uuid", nullable: false, comment: "Id события"), - ImportantId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"), - OccurrenceDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), + CategoryId = table.Column<int>(type: "integer", nullable: false, comment: "Id Категории важности"), + Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата возникновения"), Depth = table.Column<double>(type: "double precision", nullable: true, comment: "Глубина забоя"), MessageText = table.Column<string>(type: "varchar(512)", nullable: false, comment: "Текст сообщения"), SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы автобурения, к которой относится сообщение"), @@ -73,9 +59,6 @@ namespace Persistence.Database.Postgres.Migrations 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 317c445..f5533cc 100644 --- a/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs +++ b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs @@ -52,27 +52,27 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("uuid") .HasComment("Id события"); + b.Property<int>("CategoryId") + .HasColumnType("integer") + .HasComment("Id Категории важности"); + b.Property<double?>("Depth") .HasColumnType("double precision") .HasComment("Глубина забоя"); - b.Property<int>("ImportantId") - .HasColumnType("integer") - .HasComment("Id Категории важности"); - b.Property<string>("MessageText") .IsRequired() .HasColumnType("varchar(512)") .HasComment("Текст сообщения"); - b.Property<DateTimeOffset>("OccurrenceDate") - .HasColumnType("timestamp with time zone") - .HasComment("Дата возникновения"); - b.Property<Guid>("SystemId") .HasColumnType("uuid") .HasComment("Id системы автобурения, к которой относится сообщение"); + b.Property<DateTimeOffset>("Timestamp") + .HasColumnType("timestamp with time zone") + .HasComment("Дата возникновения"); + b.Property<Guid>("UserId") .HasColumnType("uuid") .HasComment("Id пользователя за пультом бурильщика"); diff --git a/Persistence.Database.Postgres/PersistenceDbContext.cs b/Persistence.Database.Postgres/PersistenceDbContext.cs index f9f9a16..89b09db 100644 --- a/Persistence.Database.Postgres/PersistenceDbContext.cs +++ b/Persistence.Database.Postgres/PersistenceDbContext.cs @@ -12,7 +12,7 @@ public partial class PersistenceDbContext : DbContext public DbSet<TechMessage> TechMessage => Set<TechMessage>(); - public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>(); + public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>(); public PersistenceDbContext() : base() @@ -42,5 +42,14 @@ public partial class PersistenceDbContext : DbContext modelBuilder.Entity<TimestampedSet>() .Property(e => e.Set) .HasJsonConversion(); - } + + modelBuilder.Entity<TechMessage>(entity => + { + entity.HasOne(t => t.System) + .WithMany() + .HasForeignKey(t => t.SystemId) + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + } } diff --git a/Persistence.Database/Entity/TechMessage.cs b/Persistence.Database/Entity/TechMessage.cs index dce897c..eacc234 100644 --- a/Persistence.Database/Entity/TechMessage.cs +++ b/Persistence.Database/Entity/TechMessage.cs @@ -10,10 +10,10 @@ namespace Persistence.Database.Entity public Guid EventId { get; set; } [Comment("Id Категории важности")] - public int ImportantId { get; set; } + public int CategoryId { get; set; } [Comment("Дата возникновения")] - public DateTimeOffset OccurrenceDate { get; set; } + public DateTimeOffset Timestamp { get; set; } [Comment("Глубина забоя")] public double? Depth { get; set; } diff --git a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs index 73414c4..3902681 100644 --- a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs @@ -1,4 +1,5 @@ using System.Net; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Persistence.Client; using Persistence.Client.Clients; @@ -10,7 +11,9 @@ namespace Persistence.IntegrationTests.Controllers { public class TechMessagesControllerTest : BaseIntegrationTest { + private static readonly string SystemCacheKey = $"{typeof(ADSystem).FullName}CacheKey"; private readonly ITechMessagesClient techMessagesClient; + private readonly IMemoryCache memoryCache; public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory) { var scope = factory.Services.CreateScope(); @@ -18,6 +21,7 @@ namespace Persistence.IntegrationTests.Controllers .GetRequiredService<PersistenceClientFactory>(); techMessagesClient = persistenceClientFactory.GetClient<ITechMessagesClient>(); + memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>(); } [Fact] @@ -29,7 +33,7 @@ namespace Persistence.IntegrationTests.Controllers { Skip = 1, Take = 2, - SortSettings = nameof(TechMessageDto.CategoryId) + SortSettings = nameof(TechMessage.CategoryId) }; //act @@ -53,7 +57,7 @@ namespace Persistence.IntegrationTests.Controllers { Skip = 0, Take = 2, - SortSettings = nameof(TechMessageDto.CategoryId) + SortSettings = nameof(TechMessage.CategoryId) }; //act @@ -71,11 +75,39 @@ namespace Persistence.IntegrationTests.Controllers await InsertRange(); } + [Fact] + public async Task InsertRange_returns_BadRequest() + { + //arrange + var dtos = new List<TechMessageDto>() + { + new TechMessageDto() + { + EventId = Guid.NewGuid(), + CategoryId = -1, // < 0 + Timestamp = DateTimeOffset.UtcNow, + Depth = -1, // < 0 + MessageText = string.Empty, // length < 0 + System = string.Concat(Enumerable.Repeat(nameof(TechMessageDto.System), 100)), // length > 256 + UserId = Guid.NewGuid() + } + }; + + //act + var response = await techMessagesClient.InsertRange(dtos, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + [Fact] public async Task GetSystems_returns_success() { + //arrange + dbContext.CleanupDbSet<ADSystem>(); + memoryCache.Remove(SystemCacheKey); + //act - dbContext.CleanupDbSet<TechMessage>(); var response = await techMessagesClient.GetSystems(new CancellationToken()); //assert @@ -113,7 +145,7 @@ namespace Persistence.IntegrationTests.Controllers var autoDrillingSystem = nameof(TechMessageDto.System); //act - var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken()); + var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken()); //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -124,13 +156,13 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetStatistics_AfterSave_returns_success() { //arrange - var imortantId = 1; + var imortantId = 0; var autoDrillingSystem = nameof(TechMessageDto.System); var dtos = await InsertRange(); var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == e.System); //act - var response = await techMessagesClient.GetStatistics(imortantId, autoDrillingSystem, new CancellationToken()); + var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken()); //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index 427346b..478ba0f 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -1,7 +1,6 @@ using Mapster; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; -using Newtonsoft.Json.Linq; using Persistence.Database.Entity; using Persistence.Models; using Persistence.Repositories; @@ -21,7 +20,8 @@ namespace Persistence.Repository.Repositories this.db = db; } - protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>(); + protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>() + .Include(e => e.System); public async Task<PaginationContainer<TechMessageDto>> GetPage(RequestDto request, CancellationToken token) { @@ -46,7 +46,7 @@ namespace Persistence.Repository.Repositories { var query = GetQueryReadOnly(); var count = await query - .Where(e => importantId == null || e.ImportantId == importantId) + .Where(e => importantId == null || e.CategoryId == importantId) .Where(e => autoDrillingSystem == null || e.System.Name == autoDrillingSystem) .GroupBy(e => e.System.Name) .ToDictionaryAsync(e => e.Key, v => v.Count()); @@ -54,7 +54,37 @@ namespace Persistence.Repository.Repositories return count; } - public async Task<IEnumerable<ADSystemDto>> GetSystems(CancellationToken token) + public async Task<IEnumerable<string>> GetSystems(CancellationToken token) + { + var entities = await GetSystems(); + var systems = entities.Select(e => e.Name); + + return systems ?? []; + } + + public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) + { + + var entities = new List<TechMessage>(); + foreach (var dto in dtos) + { + var entity = dto.Adapt<TechMessage>(); + var systems = await GetSystems(); + var systemId = systems.FirstOrDefault(e => e.Name == dto.System)?.SystemId + ?? await CreateSystem(dto.System); + + entity.SystemId = systemId; + + entities.Add(entity); + } + + await db.Set<TechMessage>().AddRangeAsync(entities, token); + var result = await db.SaveChangesAsync(token); + + return result; + } + + private async Task<IEnumerable<ADSystemDto>> GetSystems() { var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async f => { @@ -69,33 +99,6 @@ namespace Persistence.Repository.Repositories return systems ?? []; } - - public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) - { - var entities = dtos.Select(dto => - { - var task = Task.Run(async () => - { - var entity = dto.Adapt<TechMessage>(); - 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<TechMessage>().AddRangeAsync(entities, token); - var result = await db.SaveChangesAsync(token); - - return result; - } - private async Task<Guid> CreateSystem(string name) { memoryCache.Remove(SystemCacheKey); @@ -110,7 +113,7 @@ namespace Persistence.Repository.Repositories await db.Set<ADSystem>().AddAsync(entity); await db.SaveChangesAsync(); - return Guid.NewGuid(); + return systemId; } } } diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs index b8fb194..74b2bf5 100644 --- a/Persistence/Repositories/ITechMessagesRepository.cs +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -29,7 +29,7 @@ namespace Persistence.Repositories /// </summary> /// <param name="token"></param> /// <returns></returns> - Task<IEnumerable<ADSystemDto>> GetSystems(CancellationToken token); + Task<IEnumerable<string>> GetSystems(CancellationToken token); /// <summary> /// Получение количества сообщений по категориям и системам автобурения -- 2.45.2 From efed33e6486ec657d08eef95975078afcef1c43b Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Mon, 2 Dec 2024 15:14:00 +0500 Subject: [PATCH 09/12] =?UTF-8?q?=D0=92=D0=BD=D0=B5=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8=20=D0=B2?= =?UTF-8?q?=20Setpoint=20=D0=B8=20TechMessage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataSaubController.cs | 2 +- .../Controllers/SetpointController.cs | 38 +++++- .../Controllers/TechMessagesController.cs | 73 ++++++++--- .../Controllers/TimeSeriesController.cs | 16 +-- Persistence.Client/Clients/ISetpointClient.cs | 6 + .../Clients/ITechMessagesClient.cs | 13 +- Persistence.Client/Helpers/ApiTokenHelper.cs | 2 + .../20241118052225_SetpointMigration.cs | 2 +- ...02072250_TechMessageMigration.Designer.cs} | 12 +- ...=> 20241202072250_TechMessageMigration.cs} | 10 +- .../PersistenceDbContextModelSnapshot.cs | 10 +- .../Entity/{ADSystem.cs => DrillingSystem.cs} | 2 +- Persistence.Database/Entity/Setpoint.cs | 2 +- Persistence.Database/Entity/TechMessage.cs | 2 +- .../Controllers/SetpointControllerTest.cs | 67 ++++++++++ .../Controllers/TechMessagesControllerTest.cs | 94 ++++++++++++-- .../Repositories/SetpointRepository.cs | 34 ++++- .../Repositories/TechMessagesRepository.cs | 117 +++++++++++++----- Persistence/API/ISetpointApi.cs | 18 +-- Persistence/API/ISyncApi.cs | 2 +- .../{ADSystemDto.cs => DrillingSystemDto.cs} | 2 +- Persistence/Models/MessagesStatisticDto.cs | 17 +++ Persistence/Models/SetpointLogDto.cs | 2 +- .../Repositories/ISetpointRepository.cs | 39 ++++-- .../Repositories/ITechMessagesRepository.cs | 20 ++- 25 files changed, 482 insertions(+), 120 deletions(-) rename Persistence.Database.Postgres/Migrations/{20241128074729_TechMessageMigration.Designer.cs => 20241202072250_TechMessageMigration.Designer.cs} (96%) rename Persistence.Database.Postgres/Migrations/{20241128074729_TechMessageMigration.cs => 20241202072250_TechMessageMigration.cs} (91%) rename Persistence.Database/Entity/{ADSystem.cs => DrillingSystem.cs} (95%) rename Persistence/Models/{ADSystemDto.cs => DrillingSystemDto.cs} (92%) create mode 100644 Persistence/Models/MessagesStatisticDto.cs diff --git a/Persistence.API/Controllers/DataSaubController.cs b/Persistence.API/Controllers/DataSaubController.cs index 437aee3..202e527 100644 --- a/Persistence.API/Controllers/DataSaubController.cs +++ b/Persistence.API/Controllers/DataSaubController.cs @@ -6,7 +6,7 @@ using Persistence.Repository.Data; namespace Persistence.API.Controllers; /// <summary> -/// ������ � ���������� ������� +/// Работа с временными данными /// </summary> [ApiController] [Authorize] diff --git a/Persistence.API/Controllers/SetpointController.cs b/Persistence.API/Controllers/SetpointController.cs index 5a5cb52..108c3ef 100644 --- a/Persistence.API/Controllers/SetpointController.cs +++ b/Persistence.API/Controllers/SetpointController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Authorization; +using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Persistence.Models; using Persistence.Repositories; @@ -63,18 +64,47 @@ public class SetpointController : ControllerBase, ISetpointApi return Ok(result); } + /// <summary> + /// Получить диапазон дат, для которых есть данные в репозитории + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("range")] + public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token) + { + var result = await setpointRepository.GetDatesRangeAsync(token); + + return Ok(result); + } + + /// <summary> + /// Получить порцию записей, начиная с заданной даты + /// </summary> + /// <param name="dateBegin"></param> + /// <param name="take"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("part")] + public async Task<ActionResult<IEnumerable<SetpointLogDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) + { + var result = await setpointRepository.GetPart(dateBegin, take, token); + + return Ok(result); + } + /// <summary> /// Сохранить уставку /// </summary> /// <param name="setpointKey"></param> /// <param name="newValue"></param> + /// <param name="idUser"></param> /// <param name="token"></param> /// <returns></returns> [HttpPost] - public async Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token) + [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] + public async Task<IActionResult> Save(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) { - // ToDo: вычитка idUser - await setpointRepository.Save(setpointKey, newValue, 0, token); + await setpointRepository.Save(setpointKey, newValue, idUser, token); return Ok(); } diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index fb3315e..4e91802 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Authorization; +using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Persistence.Models; using Persistence.Repositories; @@ -14,6 +15,14 @@ namespace Persistence.API.Controllers; public class TechMessagesController : ControllerBase { private readonly ITechMessagesRepository techMessagesRepository; + private static readonly Dictionary<int, string> categories = new Dictionary<int, string>() + { + { 0, "System" }, + { 1, "Авария" }, + { 2, "Предупреждение" }, + { 3, "Инфо" }, + { 4, "Прочее" } + }; public TechMessagesController(ITechMessagesRepository techMessagesRepository) { @@ -37,14 +46,14 @@ public class TechMessagesController : ControllerBase /// <summary> /// Получить статистику по системам /// </summary> - /// <param name="importantId"></param> /// <param name="autoDrillingSystem"></param> + /// <param name="categoryIds"></param> /// <param name="token"></param> /// <returns></returns> - [HttpGet("statistics/{autoDrillingSystem}")] - public async Task<ActionResult<int>> GetStatistics([FromRoute] string? autoDrillingSystem, int? importantId, CancellationToken token) + [HttpGet("statistics")] + public async Task<ActionResult<IEnumerable<MessagesStatisticDto>>> GetStatistics([FromQuery] IEnumerable<string> autoDrillingSystem, [FromQuery] IEnumerable<int> categoryIds, CancellationToken token) { - var result = await techMessagesRepository.GetStatistics(importantId, autoDrillingSystem, token); + var result = await techMessagesRepository.GetStatistics(autoDrillingSystem, categoryIds, token); return Ok(result); } @@ -55,13 +64,41 @@ public class TechMessagesController : ControllerBase /// <param name="token"></param> /// <returns></returns> [HttpGet("systems")] - public async Task<ActionResult<IEnumerable<string>>> GetSystems(CancellationToken token) + public async Task<ActionResult<Dictionary<string, int>>> GetSystems(CancellationToken token) { var result = await techMessagesRepository.GetSystems(token); return Ok(result); } + /// <summary> + /// Получить диапазон дат, для которых есть данные в репозитории + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("range")] + public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token) + { + var result = await techMessagesRepository.GetDatesRangeAsync(token); + + return Ok(result); + } + + /// <summary> + /// Получить порцию записей, начиная с заданной даты + /// </summary> + /// <param name="dateBegin"></param> + /// <param name="take"></param> + /// <param name="token"></param> + /// <returns></returns> + [HttpGet("part")] + public async Task<ActionResult<IEnumerable<SetpointLogDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) + { + var result = await techMessagesRepository.GetPart(dateBegin, take, token); + + return Ok(result); + } + /// <summary> /// Добавить новые технологические сообщения /// </summary> @@ -69,9 +106,16 @@ public class TechMessagesController : ControllerBase /// <param name="token"></param> /// <returns></returns> [HttpPost] - public async Task<ActionResult<int>> InsertRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) + [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] + public async Task<IActionResult> InsertRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) { - var result = await techMessagesRepository.InsertRange(dtos, token); + var userId = User.GetUserId<Guid>(); + foreach (var dto in dtos) + { + dto.UserId = userId; + } + + var result = await techMessagesRepository.InsertRange(dtos, token); return CreatedAtAction(nameof(InsertRange), result); } @@ -83,15 +127,6 @@ public class TechMessagesController : ControllerBase [HttpGet("categories")] public ActionResult<Dictionary<int, string>> GetImportantCategories() { - var result = new Dictionary<int, string>() - { - { 0, "System" }, - { 1, "Авария" }, - { 2, "Предупреждение" }, - { 3, "Инфо" }, - { 4, "Прочее" } - }; - - return Ok(result); + return Ok(categories); } -} +} \ No newline at end of file diff --git a/Persistence.API/Controllers/TimeSeriesController.cs b/Persistence.API/Controllers/TimeSeriesController.cs index a4f00f3..90c78c8 100644 --- a/Persistence.API/Controllers/TimeSeriesController.cs +++ b/Persistence.API/Controllers/TimeSeriesController.cs @@ -19,7 +19,7 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt } /// <summary> - /// �������� ������ ��������, ��������������� ��������� ��� + /// Получить список объектов, удовлетворяющий диапазону дат /// </summary> /// <param name="dateBegin"></param> /// <param name="token"></param> @@ -28,24 +28,24 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt [ProducesResponseType(StatusCodes.Status200OK)] public async Task<IActionResult> Get(DateTimeOffset dateBegin, CancellationToken token) { - var result = await this.timeSeriesDataRepository.GetGtDate(dateBegin, token); + var result = await timeSeriesDataRepository.GetGtDate(dateBegin, token); return Ok(result); } /// <summary> - /// �������� �������� ���, ��� ������� ���� ������ � ����������� + /// Получить диапазон дат, для которых есть данные в репозиторие /// </summary> /// <param name="token"></param> /// <returns></returns> [HttpGet("datesRange")] public async Task<IActionResult> GetDatesRange(CancellationToken token) { - var result = await this.timeSeriesDataRepository.GetDatesRange(token); + var result = await timeSeriesDataRepository.GetDatesRange(token); return Ok(result); } /// <summary> - /// �������� ������ �������� � �������������, ��������������� ��������� ��� + /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат /// </summary> /// <param name="dateBegin"></param> /// <param name="intervalSec"></param> @@ -55,12 +55,12 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt [HttpGet("resampled")] public async Task<IActionResult> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { - var result = await this.timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token); + var result = await timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token); return Ok(result); } /// <summary> - /// �������� ������ + /// Добавить записи /// </summary> /// <param name="dtos"></param> /// <param name="token"></param> @@ -68,7 +68,7 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt [HttpPost] public async Task<IActionResult> InsertRange(IEnumerable<TDto> dtos, CancellationToken token) { - var result = await this.timeSeriesDataRepository.InsertRange(dtos, token); + var result = await timeSeriesDataRepository.InsertRange(dtos, token); return Ok(result); } diff --git a/Persistence.Client/Clients/ISetpointClient.cs b/Persistence.Client/Clients/ISetpointClient.cs index 72d76e1..886e034 100644 --- a/Persistence.Client/Clients/ISetpointClient.cs +++ b/Persistence.Client/Clients/ISetpointClient.cs @@ -19,6 +19,12 @@ public interface ISetpointClient [Get($"{BaseRoute}/log")] Task<IApiResponse<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog([Query(CollectionFormat.Multi)] IEnumerable<Guid> setpointKeys); + [Get($"{BaseRoute}/range")] + Task<IApiResponse<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token); + + [Get($"{BaseRoute}/part")] + Task<IApiResponse<IEnumerable<SetpointLogDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token); + [Post($"{BaseRoute}/")] Task<IApiResponse> Save(Guid setpointKey, object newValue); } diff --git a/Persistence.Client/Clients/ITechMessagesClient.cs b/Persistence.Client/Clients/ITechMessagesClient.cs index 43839fe..cbdf7ef 100644 --- a/Persistence.Client/Clients/ITechMessagesClient.cs +++ b/Persistence.Client/Clients/ITechMessagesClient.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Mvc; -using Persistence.Models; +using Persistence.Models; using Refit; namespace Persistence.Client.Clients @@ -20,7 +19,13 @@ namespace Persistence.Client.Clients [Get($"{BaseRoute}/systems")] Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token); - [Get($"{BaseRoute}/statistics/" + "{autoDrillingSystem}")] - Task<IApiResponse<int>> GetStatistics(string? autoDrillingSystem, int? importantId, CancellationToken token); + [Get($"{BaseRoute}/range")] + Task<IApiResponse<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token); + + [Get($"{BaseRoute}/part")] + Task<IApiResponse<IEnumerable<TechMessageDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token); + + [Get($"{BaseRoute}/statistics")] + Task<IApiResponse<IEnumerable<MessagesStatisticDto>>> GetStatistics([Query] string autoDrillingSystem, [Query] int categoryId, CancellationToken token); } } diff --git a/Persistence.Client/Helpers/ApiTokenHelper.cs b/Persistence.Client/Helpers/ApiTokenHelper.cs index e508922..5eed66e 100644 --- a/Persistence.Client/Helpers/ApiTokenHelper.cs +++ b/Persistence.Client/Helpers/ApiTokenHelper.cs @@ -29,8 +29,10 @@ public static class ApiTokenHelper private static string CreateDefaultJwtToken(this AuthUser authUser) { + var nameIdetifier = Guid.NewGuid().ToString(); var claims = new List<Claim>() { + new(ClaimTypes.NameIdentifier, nameIdetifier), new("client_id", authUser.ClientId), new("username", authUser.Username), new("password", authUser.Password), diff --git a/Persistence.Database.Postgres/Migrations/20241118052225_SetpointMigration.cs b/Persistence.Database.Postgres/Migrations/20241118052225_SetpointMigration.cs index 49e438a..ea6fccf 100644 --- a/Persistence.Database.Postgres/Migrations/20241118052225_SetpointMigration.cs +++ b/Persistence.Database.Postgres/Migrations/20241118052225_SetpointMigration.cs @@ -18,7 +18,7 @@ namespace Persistence.Database.Postgres.Migrations Key = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ"), Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата изменения уставки"), Value = table.Column<object>(type: "jsonb", nullable: false, comment: "Значение уставки"), - IdUser = table.Column<int>(type: "integer", nullable: false, comment: "Id автора последнего изменения") + IdUser = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id автора последнего изменения") }, constraints: table => { diff --git a/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.Designer.cs b/Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.Designer.cs similarity index 96% rename from Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.Designer.cs rename to Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.Designer.cs index 6678078..6ed33f7 100644 --- a/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.Designer.cs +++ b/Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.Designer.cs @@ -12,7 +12,7 @@ using Persistence.Database.Model; namespace Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistenceDbContext))] - [Migration("20241128074729_TechMessageMigration")] + [Migration("20241202072250_TechMessageMigration")] partial class TechMessageMigration { /// <inheritdoc /> @@ -27,7 +27,7 @@ namespace Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Persistence.Database.Entity.ADSystem", b => + modelBuilder.Entity("Persistence.Database.Entity.DrillingSystem", b => { b.Property<Guid>("SystemId") .ValueGeneratedOnAdd() @@ -45,7 +45,7 @@ namespace Persistence.Database.Postgres.Migrations b.HasKey("SystemId"); - b.ToTable("ADSystem"); + b.ToTable("DrillingSystem"); }); modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => @@ -203,8 +203,8 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата создания уставки"); - b.Property<int>("IdUser") - .HasColumnType("integer") + b.Property<Guid>("IdUser") + .HasColumnType("uuid") .HasComment("Id автора последнего изменения"); b.Property<object>("Value") @@ -219,7 +219,7 @@ namespace Persistence.Database.Postgres.Migrations modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => { - b.HasOne("Persistence.Database.Entity.ADSystem", "System") + b.HasOne("Persistence.Database.Entity.DrillingSystem", "System") .WithMany() .HasForeignKey("SystemId") .OnDelete(DeleteBehavior.Cascade) diff --git a/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.cs b/Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.cs similarity index 91% rename from Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.cs rename to Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.cs index 047ad52..ccf18d4 100644 --- a/Persistence.Database.Postgres/Migrations/20241128074729_TechMessageMigration.cs +++ b/Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.cs @@ -12,7 +12,7 @@ namespace Persistence.Database.Postgres.Migrations protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "ADSystem", + name: "DrillingSystem", columns: table => new { SystemId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id системы автобурения"), @@ -21,7 +21,7 @@ namespace Persistence.Database.Postgres.Migrations }, constraints: table => { - table.PrimaryKey("PK_ADSystem", x => x.SystemId); + table.PrimaryKey("PK_DrillingSystem", x => x.SystemId); }); migrationBuilder.CreateTable( @@ -40,9 +40,9 @@ namespace Persistence.Database.Postgres.Migrations { table.PrimaryKey("PK_TechMessage", x => x.EventId); table.ForeignKey( - name: "FK_TechMessage_ADSystem_SystemId", + name: "FK_TechMessage_DrillingSystem_SystemId", column: x => x.SystemId, - principalTable: "ADSystem", + principalTable: "DrillingSystem", principalColumn: "SystemId", onDelete: ReferentialAction.Cascade); }); @@ -60,7 +60,7 @@ namespace Persistence.Database.Postgres.Migrations name: "TechMessage"); migrationBuilder.DropTable( - name: "ADSystem"); + name: "DrillingSystem"); } } } diff --git a/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs index f5533cc..e53c81a 100644 --- a/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs +++ b/Persistence.Database.Postgres/Migrations/PersistenceDbContextModelSnapshot.cs @@ -24,7 +24,7 @@ namespace Persistence.Database.Postgres.Migrations NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Persistence.Database.Entity.ADSystem", b => + modelBuilder.Entity("Persistence.Database.Entity.DrillingSystem", b => { b.Property<Guid>("SystemId") .ValueGeneratedOnAdd() @@ -42,7 +42,7 @@ namespace Persistence.Database.Postgres.Migrations b.HasKey("SystemId"); - b.ToTable("ADSystem"); + b.ToTable("DrillingSystem"); }); modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => @@ -200,8 +200,8 @@ namespace Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата создания уставки"); - b.Property<int>("IdUser") - .HasColumnType("integer") + b.Property<Guid>("IdUser") + .HasColumnType("uuid") .HasComment("Id автора последнего изменения"); b.Property<object>("Value") @@ -216,7 +216,7 @@ namespace Persistence.Database.Postgres.Migrations modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b => { - b.HasOne("Persistence.Database.Entity.ADSystem", "System") + b.HasOne("Persistence.Database.Entity.DrillingSystem", "System") .WithMany() .HasForeignKey("SystemId") .OnDelete(DeleteBehavior.Cascade) diff --git a/Persistence.Database/Entity/ADSystem.cs b/Persistence.Database/Entity/DrillingSystem.cs similarity index 95% rename from Persistence.Database/Entity/ADSystem.cs rename to Persistence.Database/Entity/DrillingSystem.cs index 525134c..6588fb0 100644 --- a/Persistence.Database/Entity/ADSystem.cs +++ b/Persistence.Database/Entity/DrillingSystem.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; namespace Persistence.Database.Entity; -public class ADSystem +public class DrillingSystem { [Key, Comment("Id системы автобурения")] public Guid SystemId { get; set; } diff --git a/Persistence.Database/Entity/Setpoint.cs b/Persistence.Database/Entity/Setpoint.cs index ef6b5dc..6ca2c27 100644 --- a/Persistence.Database/Entity/Setpoint.cs +++ b/Persistence.Database/Entity/Setpoint.cs @@ -16,6 +16,6 @@ namespace Persistence.Database.Model public DateTimeOffset Created { get; set; } [Comment("Id автора последнего изменения")] - public int IdUser { get; set; } + public Guid IdUser { get; set; } } } diff --git a/Persistence.Database/Entity/TechMessage.cs b/Persistence.Database/Entity/TechMessage.cs index eacc234..ea29cc2 100644 --- a/Persistence.Database/Entity/TechMessage.cs +++ b/Persistence.Database/Entity/TechMessage.cs @@ -25,7 +25,7 @@ namespace Persistence.Database.Entity public required Guid SystemId { get; set; } [Required, ForeignKey(nameof(SystemId)), Comment("Система автобурения, к которой относится сообщение")] - public virtual required ADSystem System { get; set; } + public virtual required DrillingSystem System { get; set; } [Comment("Id пользователя за пультом бурильщика")] public Guid UserId { get; set; } diff --git a/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs b/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs index faa0147..d314cec 100644 --- a/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Persistence.Client; using Persistence.Client.Clients; +using Persistence.Database.Model; using Xunit; namespace Persistence.IntegrationTests.Controllers @@ -131,6 +132,72 @@ namespace Persistence.IntegrationTests.Controllers Assert.Equal(setpointKey, response.Content.FirstOrDefault().Key); } + [Fact] + public async Task GetDatesRange_returns_success() + { + //arrange + dbContext.CleanupDbSet<Setpoint>(); + + //act + var response = await setpointClient.GetDatesRangeAsync(new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Equal(DateTimeOffset.MinValue, response.Content?.From); + Assert.Equal(DateTimeOffset.MaxValue, response.Content?.To); + } + + [Fact] + public async Task GetDatesRange_AfterSave_returns_success() + { + //arrange + dbContext.CleanupDbSet<Setpoint>(); + await Save(); + + //act + var response = await setpointClient.GetDatesRangeAsync(new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.NotNull(response.Content?.From); + Assert.NotNull(response.Content?.To); + } + + [Fact] + public async Task GetPart_returns_success() + { + //arrange + var dateBegin = DateTimeOffset.UtcNow; + var take = 2; + + //act + var response = await setpointClient.GetPart(dateBegin, take, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Empty(response.Content); + } + + [Fact] + public async Task GetPart_AfterSave_returns_success() + { + //arrange + var dateBegin = DateTimeOffset.UtcNow; + var take = 1; + await Save(); + + //act + var response = await setpointClient.GetPart(dateBegin, take, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.NotEmpty(response.Content); + } + [Fact] public async Task Save_returns_success() { diff --git a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs index 3902681..ec4e40a 100644 --- a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs @@ -11,7 +11,7 @@ namespace Persistence.IntegrationTests.Controllers { public class TechMessagesControllerTest : BaseIntegrationTest { - private static readonly string SystemCacheKey = $"{typeof(ADSystem).FullName}CacheKey"; + private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey"; private readonly ITechMessagesClient techMessagesClient; private readonly IMemoryCache memoryCache; public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory) @@ -28,7 +28,10 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetPage_returns_success() { //arrange + memoryCache.Remove(SystemCacheKey); dbContext.CleanupDbSet<TechMessage>(); + dbContext.CleanupDbSet<Database.Entity.DrillingSystem>(); + var requestDto = new RequestDto() { Skip = 1, @@ -104,8 +107,9 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetSystems_returns_success() { //arrange - dbContext.CleanupDbSet<ADSystem>(); memoryCache.Remove(SystemCacheKey); + dbContext.CleanupDbSet<TechMessage>(); + dbContext.CleanupDbSet<Database.Entity.DrillingSystem>(); //act var response = await techMessagesClient.GetSystems(new CancellationToken()); @@ -140,7 +144,10 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetStatistics_returns_success() { //arrange + memoryCache.Remove(SystemCacheKey); dbContext.CleanupDbSet<TechMessage>(); + dbContext.CleanupDbSet<Database.Entity.DrillingSystem>(); + var imortantId = 1; var autoDrillingSystem = nameof(TechMessageDto.System); @@ -149,7 +156,8 @@ namespace Persistence.IntegrationTests.Controllers //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(0, response.Content); + Assert.NotNull(response.Content); + Assert.Empty(response.Content); } [Fact] @@ -159,19 +167,89 @@ namespace Persistence.IntegrationTests.Controllers var imortantId = 0; var autoDrillingSystem = nameof(TechMessageDto.System); var dtos = await InsertRange(); - var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == e.System); + var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == autoDrillingSystem); //act var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken()); //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(filteredDtos.Count(), response.Content); + Assert.NotNull(response.Content); + var categories = response.Content + .FirstOrDefault()?.Categories + .FirstOrDefault(e => e.Key == 0).Value; + Assert.Equal(filteredDtos.Count(), categories); } - public async Task<IEnumerable<TechMessageDto>> InsertRange() + [Fact] + public async Task GetDatesRange_returns_success() + { + //act + var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + //Assert.Equal(DateTimeOffset.MinValue, response.Content?.From); + //Assert.Equal(DateTimeOffset.MaxValue, response.Content?.To); + } + + [Fact] + public async Task GetDatesRange_AfterSave_returns_success() { //arrange + await InsertRange(); + + //act + var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.NotNull(response.Content?.From); + Assert.NotNull(response.Content?.To); + } + + [Fact] + public async Task GetPart_returns_success() + { + //arrange + var dateBegin = DateTimeOffset.UtcNow; + var take = 2; + + //act + var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Empty(response.Content); + } + + [Fact] + public async Task GetPart_AfterSave_returns_success() + { + //arrange + var dateBegin = DateTimeOffset.UtcNow; + var take = 1; + await InsertRange(); + + //act + var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken()); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.NotEmpty(response.Content); + } + + private async Task<IEnumerable<TechMessageDto>> InsertRange() + { + //arrange + memoryCache.Remove(SystemCacheKey); + dbContext.CleanupDbSet<TechMessage>(); + dbContext.CleanupDbSet<DrillingSystem>(); + var dtos = new List<TechMessageDto>() { new TechMessageDto() @@ -181,7 +259,7 @@ namespace Persistence.IntegrationTests.Controllers Timestamp = DateTimeOffset.UtcNow, Depth = 1.11, MessageText = nameof(TechMessageDto.MessageText), - System = nameof(TechMessageDto.System), + System = nameof(TechMessageDto.System).ToLower(), UserId = Guid.NewGuid() }, new TechMessageDto() @@ -191,7 +269,7 @@ namespace Persistence.IntegrationTests.Controllers Timestamp = DateTimeOffset.UtcNow, Depth = 2.22, MessageText = nameof(TechMessageDto.MessageText), - System = nameof(TechMessageDto.System), + System = nameof(TechMessageDto.System).ToLower(), UserId = Guid.NewGuid() } }; diff --git a/Persistence.Repository/Repositories/SetpointRepository.cs b/Persistence.Repository/Repositories/SetpointRepository.cs index 7f81c29..b4557aa 100644 --- a/Persistence.Repository/Repositories/SetpointRepository.cs +++ b/Persistence.Repository/Repositories/SetpointRepository.cs @@ -43,6 +43,38 @@ namespace Persistence.Repository.Repositories return dtos; } + public async Task<IEnumerable<SetpointLogDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query + .Where(e => e.Created > dateBegin) + .Take(take) + .ToArrayAsync(token); + var dtos = entities + .Select(e => e.Adapt<SetpointLogDto>()); + + return dtos; + } + + public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token) + { + var query = GetQueryReadOnly() + .GroupBy(e => 1) + .Select(group => new + { + Min = group.Min(e => e.Created), + Max = group.Max(e => e.Created), + }); + var values = await query.FirstOrDefaultAsync(token); + var result = new DatesRangeDto() + { + From = values?.Min ?? DateTimeOffset.MinValue, + To = values?.Max ?? DateTimeOffset.MaxValue + }; + + return result; + } + public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token) { var query = GetQueryReadOnly(); @@ -56,7 +88,7 @@ namespace Persistence.Repository.Repositories return dtos; } - public async Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token) + public async Task Save(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) { var entity = new Setpoint() { diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index 478ba0f..fad8acf 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -10,7 +10,8 @@ namespace Persistence.Repository.Repositories { public class TechMessagesRepository : ITechMessagesRepository { - private static readonly string SystemCacheKey = $"{typeof(ADSystem).FullName}CacheKey"; + private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey"; + private const int CacheExpirationInMinutes = 60; private readonly IMemoryCache memoryCache; private DbContext db; @@ -26,40 +27,65 @@ namespace Persistence.Repository.Repositories public async Task<PaginationContainer<TechMessageDto>> GetPage(RequestDto request, CancellationToken token) { var query = GetQueryReadOnly(); + var count = await query.CountAsync(token); + + var sort = request.SortSettings != string.Empty + ? request.SortSettings + : nameof(TechMessage.Timestamp); var entities = await query .SortBy(request.SortSettings) .Skip(request.Skip) .Take(request.Take) - .ToListAsync(); + .ToArrayAsync(token); + var dto = new PaginationContainer<TechMessageDto>() { Skip = request.Skip, Take = request.Take, - Count = entities.Count, + Count = count, Items = entities.Select(e => e.Adapt<TechMessageDto>()) }; return dto; } - public async Task<Dictionary<string, int>> GetStatistics(int? importantId, string? autoDrillingSystem, CancellationToken token) + public async Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token) { var query = GetQueryReadOnly(); - var count = await query - .Where(e => importantId == null || e.CategoryId == importantId) - .Where(e => autoDrillingSystem == null || e.System.Name == autoDrillingSystem) - .GroupBy(e => e.System.Name) - .ToDictionaryAsync(e => e.Key, v => v.Count()); + var systems = autoDrillingSystem.Select(s => s.ToLower().Trim()); + var result = await query + .Where(e => systems.Count() == 0 || systems.Contains(e.System.Name.ToLower().Trim())) + .GroupBy(e => e.System.Name, (key, group) => new + { + System = key, + Categories = group + .Where(g => categoryIds.Count() == 0 || categoryIds.Contains(g.CategoryId)) + }) + .ToArrayAsync(token); - return count; + var entities = new List<MessagesStatisticDto>(); + foreach (var e in result) + { + var categories = e.Categories + .GroupBy(g => g.CategoryId) + .ToDictionary(c => c.Key, v => v.Count()); + var entity = new MessagesStatisticDto() + { + System = e.System, + Categories = categories + }; + entities.Add(entity); + } + + return entities; } public async Task<IEnumerable<string>> GetSystems(CancellationToken token) { - var entities = await GetSystems(); - var systems = entities.Select(e => e.Name); + var entities = await GetDrillingSystems(token); + var result = entities.Select(e => e.Name); - return systems ?? []; + return result; } public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) @@ -69,9 +95,9 @@ namespace Persistence.Repository.Repositories foreach (var dto in dtos) { var entity = dto.Adapt<TechMessage>(); - var systems = await GetSystems(); - var systemId = systems.FirstOrDefault(e => e.Name == dto.System)?.SystemId - ?? await CreateSystem(dto.System); + var systems = await GetDrillingSystems(token); + var systemId = systems.FirstOrDefault(e => e.Name.ToLower().Trim() == dto.System.ToLower().Trim())?.SystemId + ?? await CreateDrillingSystem(dto.System, token); entity.SystemId = systemId; @@ -84,36 +110,67 @@ namespace Persistence.Repository.Repositories return result; } - private async Task<IEnumerable<ADSystemDto>> GetSystems() + public async Task<IEnumerable<TechMessageDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) + { + var query = GetQueryReadOnly(); + var entities = await query + .Where(e => e.Timestamp > dateBegin) + .Take(take) + .ToArrayAsync(token); + var dtos = entities + .Select(e => e.Adapt<TechMessageDto>()); + + return dtos; + } + + public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token) + { + var query = GetQueryReadOnly() + .GroupBy(e => 1) + .Select(group => new + { + Min = group.Min(e => e.Timestamp), + Max = group.Max(e => e.Timestamp), + }); + var values = await query.FirstOrDefaultAsync(token); + var result = new DatesRangeDto() + { + From = values?.Min ?? DateTimeOffset.MinValue, + To = values?.Max ?? DateTimeOffset.MaxValue + }; + + return result; + } + + private async Task<IEnumerable<Models.DrillingSystemDto>> GetDrillingSystems(CancellationToken token) { var systems = await memoryCache.GetOrCreateAsync(SystemCacheKey, async f => { - f.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); + f.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheExpirationInMinutes); - var query = db.Set<ADSystem>(); - var entities = await query.ToListAsync(); - var dtos = entities.Select(e => e.Adapt<ADSystemDto>()); + var query = db.Set<Database.Entity.DrillingSystem>(); + var entities = await query.ToListAsync(token); + var dtos = entities.Select(e => e.Adapt<Models.DrillingSystemDto>()); return dtos; }); - return systems ?? []; + return systems!; } - private async Task<Guid> CreateSystem(string name) + private async Task<Guid> CreateDrillingSystem(string name, CancellationToken token) { memoryCache.Remove(SystemCacheKey); - var systemId = Guid.NewGuid(); - var entity = new ADSystem() + var entity = new Database.Entity.DrillingSystem() { - SystemId = systemId, - Name = name + SystemId = default, + Name = name.ToLower().Trim() }; - await db.Set<ADSystem>().AddAsync(entity); - await db.SaveChangesAsync(); + await db.Set<Database.Entity.DrillingSystem>().AddAsync(entity); + await db.SaveChangesAsync(token); - return systemId; + return entity.SystemId; } } } diff --git a/Persistence/API/ISetpointApi.cs b/Persistence/API/ISetpointApi.cs index 7af0895..d05fe12 100644 --- a/Persistence/API/ISetpointApi.cs +++ b/Persistence/API/ISetpointApi.cs @@ -6,7 +6,7 @@ namespace Persistence.API; /// <summary> /// Интерфейс для API, предназначенного для работы с уставками /// </summary> -public interface ISetpointApi +public interface ISetpointApi : ISyncApi<SetpointLogDto> { /// <summary> /// Получить актуальные значения уставок @@ -33,12 +33,12 @@ public interface ISetpointApi /// <returns></returns> Task<ActionResult<Dictionary<Guid, IEnumerable<SetpointLogDto>>>> GetLog(IEnumerable<Guid> setpoitKeys, CancellationToken token); - /// <summary> - /// Метод сохранения уставки - /// </summary> - /// <param name="setpointKey">ключ уставки</param> - /// <param name="newValue">значение</param> - /// <param name="token"></param> - /// <returns></returns> - Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token); + /// <summary> + /// Метод сохранения уставки + /// </summary> + /// <param name="setpointKey">ключ уставки</param> + /// <param name="newValue">значение</param> + /// <param name="token"></param> + /// <returns></returns> + Task<IActionResult> Save(Guid setpointKey, object newValue, Guid userId, CancellationToken token); } diff --git a/Persistence/API/ISyncApi.cs b/Persistence/API/ISyncApi.cs index 7f72812..e630ee7 100644 --- a/Persistence/API/ISyncApi.cs +++ b/Persistence/API/ISyncApi.cs @@ -6,7 +6,7 @@ namespace Persistence.API; /// <summary> /// Интерфейс для API, предназначенного для синхронизации данных /// </summary> -public interface ISyncApi<TDto> where TDto : class, new() +public interface ISyncApi<TDto> { /// <summary> /// Получить порцию записей, начиная с заданной даты diff --git a/Persistence/Models/ADSystemDto.cs b/Persistence/Models/DrillingSystemDto.cs similarity index 92% rename from Persistence/Models/ADSystemDto.cs rename to Persistence/Models/DrillingSystemDto.cs index d11dfc7..c2e7abc 100644 --- a/Persistence/Models/ADSystemDto.cs +++ b/Persistence/Models/DrillingSystemDto.cs @@ -3,7 +3,7 @@ /// <summary> /// Модель системы автобурения /// </summary> -public class ADSystemDto +public class DrillingSystemDto { /// <summary> /// Ключ diff --git a/Persistence/Models/MessagesStatisticDto.cs b/Persistence/Models/MessagesStatisticDto.cs new file mode 100644 index 0000000..08f0edd --- /dev/null +++ b/Persistence/Models/MessagesStatisticDto.cs @@ -0,0 +1,17 @@ +namespace Persistence.Models; + +/// <summary> +/// Статистика сообщений по системам бурения +/// </summary> +public class MessagesStatisticDto +{ + /// <summary> + /// Система бурения + /// </summary> + public required string System { get; set; } + + /// <summary> + /// Количество сообщений в соответствии с категориями важности + /// </summary> + public required Dictionary<int, int> Categories { get; set; } +} diff --git a/Persistence/Models/SetpointLogDto.cs b/Persistence/Models/SetpointLogDto.cs index 484be7a..4aa61b5 100644 --- a/Persistence/Models/SetpointLogDto.cs +++ b/Persistence/Models/SetpointLogDto.cs @@ -13,5 +13,5 @@ public class SetpointLogDto : SetpointValueDto /// <summary> /// Ключ пользователя /// </summary> - public int IdUser { get; set; } + public Guid IdUser { get; set; } } diff --git a/Persistence/Repositories/ISetpointRepository.cs b/Persistence/Repositories/ISetpointRepository.cs index db6838c..1fe41de 100644 --- a/Persistence/Repositories/ISetpointRepository.cs +++ b/Persistence/Repositories/ISetpointRepository.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Mvc; using Persistence.Models; namespace Persistence.Repositories; @@ -32,15 +33,31 @@ public interface ISetpointRepository /// <returns></returns> Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token); - /// <summary> - /// Метод сохранения уставки - /// </summary> - /// <param name="setpointKey">ключ операции</param> - /// <param name="idUser">ключ пользователя</param> - /// <param name="newValue">значение</param> - /// <param name="token"></param> - /// <returns></returns> - /// to do - /// id User учесть в соответствующем методе репозитория - Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token); + /// <summary> + /// Получить порцию записей, начиная с заданной даты + /// </summary> + /// <param name="dateBegin"></param> + /// <param name="take"></param> + /// <param name="token"></param> + /// <returns></returns> + Task<IEnumerable<SetpointLogDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token); + + /// <summary> + /// Получить диапазон дат, для которых есть данные в репозитории + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token); + + /// <summary> + /// Метод сохранения уставки + /// </summary> + /// <param name="setpointKey">ключ операции</param> + /// <param name="idUser">ключ пользователя</param> + /// <param name="newValue">значение</param> + /// <param name="token"></param> + /// <returns></returns> + /// to do + /// id User учесть в соответствующем методе репозитория + Task Save(Guid setpointKey, object newValue, Guid idUser, CancellationToken token); } diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs index 74b2bf5..853f234 100644 --- a/Persistence/Repositories/ITechMessagesRepository.cs +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -34,10 +34,26 @@ namespace Persistence.Repositories /// <summary> /// Получение количества сообщений по категориям и системам автобурения /// </summary> - /// <param name="importantId">Id Категории важности</param> + /// <param name="categoryId">Id Категории важности</param> /// <param name="autoDrillingSystem">Система автобурения</param> /// <param name="token"></param> /// <returns></returns> - Task<Dictionary<string, int>> GetStatistics(int? importantId, string? autoDrillingSystem, CancellationToken token); + Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token); + + /// <summary> + /// Получить порцию записей, начиная с заданной даты + /// </summary> + /// <param name="dateBegin"></param> + /// <param name="take"></param> + /// <param name="token"></param> + /// <returns></returns> + Task<IEnumerable<TechMessageDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token); + + /// <summary> + /// Получить диапазон дат, для которых есть данные в репозитории + /// </summary> + /// <param name="token"></param> + /// <returns></returns> + Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token); } } -- 2.45.2 From 7092324a5461ff6402de11484159a2eb4e0139f9 Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Wed, 4 Dec 2024 14:18:57 +0500 Subject: [PATCH 10/12] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B8=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20Insert=20=D0=BD?= =?UTF-8?q?=D0=B0=20Add?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Persistence.API/Controllers/SetpointController.cs | 6 +++--- Persistence.API/Controllers/TechMessagesController.cs | 6 +++--- Persistence.API/Controllers/TimeSeriesController.cs | 4 ++-- Persistence.API/Controllers/TimestampedSetController.cs | 4 ++-- Persistence.Repository/Repositories/SetpointRepository.cs | 2 +- .../Repositories/TechMessagesRepository.cs | 2 +- .../Repositories/TimeSeriesDataCachedRepository.cs | 4 ++-- .../Repositories/TimeSeriesDataRepository.cs | 2 +- .../Repositories/TimestampedSetRepository.cs | 2 +- Persistence/API/ISetpointApi.cs | 2 +- Persistence/API/ITimeSeriesDataApi.cs | 2 +- Persistence/Repositories/AbstractChangeLogRepository.cs | 2 +- Persistence/Repositories/ISetpointRepository.cs | 2 +- Persistence/Repositories/ITechMessagesRepository.cs | 2 +- Persistence/Repositories/ITimeSeriesDataRepository.cs | 2 +- Persistence/Repositories/ITimestampedSetRepository.cs | 2 +- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Persistence.API/Controllers/SetpointController.cs b/Persistence.API/Controllers/SetpointController.cs index 108c3ef..1bae2c6 100644 --- a/Persistence.API/Controllers/SetpointController.cs +++ b/Persistence.API/Controllers/SetpointController.cs @@ -102,10 +102,10 @@ public class SetpointController : ControllerBase, ISetpointApi /// <returns></returns> [HttpPost] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - public async Task<IActionResult> Save(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) + public async Task<IActionResult> Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) { - await setpointRepository.Save(setpointKey, newValue, idUser, token); + await setpointRepository.Add(setpointKey, newValue, idUser, token); - return Ok(); + return CreatedAtAction(nameof(Add), true); } } diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index 4e91802..bb26c6a 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -107,7 +107,7 @@ public class TechMessagesController : ControllerBase /// <returns></returns> [HttpPost] [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] - public async Task<IActionResult> InsertRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) + public async Task<IActionResult> AddRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) { var userId = User.GetUserId<Guid>(); foreach (var dto in dtos) @@ -115,9 +115,9 @@ public class TechMessagesController : ControllerBase dto.UserId = userId; } - var result = await techMessagesRepository.InsertRange(dtos, token); + var result = await techMessagesRepository.AddRange(dtos, token); - return CreatedAtAction(nameof(InsertRange), result); + return CreatedAtAction(nameof(AddRange), result); } /// <summary> diff --git a/Persistence.API/Controllers/TimeSeriesController.cs b/Persistence.API/Controllers/TimeSeriesController.cs index 90c78c8..6991759 100644 --- a/Persistence.API/Controllers/TimeSeriesController.cs +++ b/Persistence.API/Controllers/TimeSeriesController.cs @@ -66,9 +66,9 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt /// <param name="token"></param> /// <returns></returns> [HttpPost] - public async Task<IActionResult> InsertRange(IEnumerable<TDto> dtos, CancellationToken token) + public async Task<IActionResult> AddRange(IEnumerable<TDto> dtos, CancellationToken token) { - var result = await timeSeriesDataRepository.InsertRange(dtos, token); + var result = await timeSeriesDataRepository.AddRange(dtos, token); return Ok(result); } diff --git a/Persistence.API/Controllers/TimestampedSetController.cs b/Persistence.API/Controllers/TimestampedSetController.cs index f18e4c8..bd0e97e 100644 --- a/Persistence.API/Controllers/TimestampedSetController.cs +++ b/Persistence.API/Controllers/TimestampedSetController.cs @@ -32,9 +32,9 @@ public class TimestampedSetController : ControllerBase /// <returns>кол-во затронутых записей</returns> [HttpPost] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - public async Task<IActionResult> InsertRange([FromRoute]Guid idDiscriminator, [FromBody]IEnumerable<TimestampedSetDto> sets, CancellationToken token) + public async Task<IActionResult> AddRange([FromRoute]Guid idDiscriminator, [FromBody]IEnumerable<TimestampedSetDto> sets, CancellationToken token) { - var result = await repository.InsertRange(idDiscriminator, sets, token); + var result = await repository.AddRange(idDiscriminator, sets, token); return Ok(result); } diff --git a/Persistence.Repository/Repositories/SetpointRepository.cs b/Persistence.Repository/Repositories/SetpointRepository.cs index b4557aa..8f0492c 100644 --- a/Persistence.Repository/Repositories/SetpointRepository.cs +++ b/Persistence.Repository/Repositories/SetpointRepository.cs @@ -88,7 +88,7 @@ namespace Persistence.Repository.Repositories return dtos; } - public async Task Save(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) + public async Task Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) { var entity = new Setpoint() { diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index fad8acf..04bab02 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -88,7 +88,7 @@ namespace Persistence.Repository.Repositories return result; } - public async Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) + public async Task<int> AddRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) { var entities = new List<TechMessage>(); diff --git a/Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs b/Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs index 20dbe00..2037f09 100644 --- a/Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs +++ b/Persistence.Repository/Repositories/TimeSeriesDataCachedRepository.cs @@ -48,9 +48,9 @@ public class TimeSeriesDataCachedRepository<TEntity, TDto> : TimeSeriesDataRepos return items; } - public override async Task<int> InsertRange(IEnumerable<TDto> dtos, CancellationToken token) + public override async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token) { - var result = await base.InsertRange(dtos, token); + var result = await base.AddRange(dtos, token); if (result > 0) { diff --git a/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs b/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs index d73caf7..0e58c76 100644 --- a/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs +++ b/Persistence.Repository/Repositories/TimeSeriesDataRepository.cs @@ -41,7 +41,7 @@ public class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository return dtos; } - public virtual async Task<int> InsertRange(IEnumerable<TDto> dtos, CancellationToken token) + public virtual async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token) { var entities = dtos.Select(d => d.Adapt<TEntity>()); diff --git a/Persistence.Repository/Repositories/TimestampedSetRepository.cs b/Persistence.Repository/Repositories/TimestampedSetRepository.cs index ad9a6cf..a67b823 100644 --- a/Persistence.Repository/Repositories/TimestampedSetRepository.cs +++ b/Persistence.Repository/Repositories/TimestampedSetRepository.cs @@ -20,7 +20,7 @@ public class TimestampedSetRepository : ITimestampedSetRepository this.db = db; } - public Task<int> InsertRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token) + public Task<int> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token) { var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set)); var dbSet = db.Set<TimestampedSet>(); diff --git a/Persistence/API/ISetpointApi.cs b/Persistence/API/ISetpointApi.cs index d05fe12..0d23d69 100644 --- a/Persistence/API/ISetpointApi.cs +++ b/Persistence/API/ISetpointApi.cs @@ -40,5 +40,5 @@ public interface ISetpointApi : ISyncApi<SetpointLogDto> /// <param name="newValue">значение</param> /// <param name="token"></param> /// <returns></returns> - Task<IActionResult> Save(Guid setpointKey, object newValue, Guid userId, CancellationToken token); + Task<IActionResult> Add(Guid setpointKey, object newValue, Guid userId, CancellationToken token); } diff --git a/Persistence/API/ITimeSeriesDataApi.cs b/Persistence/API/ITimeSeriesDataApi.cs index a2406aa..51b9332 100644 --- a/Persistence/API/ITimeSeriesDataApi.cs +++ b/Persistence/API/ITimeSeriesDataApi.cs @@ -28,7 +28,7 @@ public interface ITimeSeriesDataApi<TDto> : ITimeSeriesBaseDataApi<TDto> /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> - Task<IActionResult> InsertRange(IEnumerable<TDto> dtos, CancellationToken token); + Task<IActionResult> AddRange(IEnumerable<TDto> dtos, CancellationToken token); } diff --git a/Persistence/Repositories/AbstractChangeLogRepository.cs b/Persistence/Repositories/AbstractChangeLogRepository.cs index 88cf511..d7b1cbc 100644 --- a/Persistence/Repositories/AbstractChangeLogRepository.cs +++ b/Persistence/Repositories/AbstractChangeLogRepository.cs @@ -74,7 +74,7 @@ namespace Persistence.Repositories; // throw new NotImplementedException(); // } -// public async Task<int> InsertRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token) +// public async Task<int> AddRange(int idUser, IEnumerable<TDto> dtos, CancellationToken token) // { // using var transaction = dbContext.Database.BeginTransaction(); // try diff --git a/Persistence/Repositories/ISetpointRepository.cs b/Persistence/Repositories/ISetpointRepository.cs index 1fe41de..502b44a 100644 --- a/Persistence/Repositories/ISetpointRepository.cs +++ b/Persistence/Repositories/ISetpointRepository.cs @@ -59,5 +59,5 @@ public interface ISetpointRepository /// <returns></returns> /// to do /// id User учесть в соответствующем методе репозитория - Task Save(Guid setpointKey, object newValue, Guid idUser, CancellationToken token); + Task Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token); } diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs index 853f234..2ddf71c 100644 --- a/Persistence/Repositories/ITechMessagesRepository.cs +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -22,7 +22,7 @@ namespace Persistence.Repositories /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> - Task<int> InsertRange(IEnumerable<TechMessageDto> dtos, CancellationToken token); + Task<int> AddRange(IEnumerable<TechMessageDto> dtos, CancellationToken token); /// <summary> /// Получение списка уникальных названий систем АБ diff --git a/Persistence/Repositories/ITimeSeriesDataRepository.cs b/Persistence/Repositories/ITimeSeriesDataRepository.cs index 9de7fc7..aa2c9ff 100644 --- a/Persistence/Repositories/ITimeSeriesDataRepository.cs +++ b/Persistence/Repositories/ITimeSeriesDataRepository.cs @@ -15,5 +15,5 @@ public interface ITimeSeriesDataRepository<TDto> : ISyncRepository<TDto>, ITimeS /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> - Task<int> InsertRange(IEnumerable<TDto> dtos, CancellationToken token); + Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token); } diff --git a/Persistence/Repositories/ITimestampedSetRepository.cs b/Persistence/Repositories/ITimestampedSetRepository.cs index 27627c3..c350739 100644 --- a/Persistence/Repositories/ITimestampedSetRepository.cs +++ b/Persistence/Repositories/ITimestampedSetRepository.cs @@ -55,5 +55,5 @@ public interface ITimestampedSetRepository /// <param name="sets"></param> /// <param name="token"></param> /// <returns></returns> - Task<int> InsertRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token); + Task<int> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets, CancellationToken token); } \ No newline at end of file -- 2.45.2 From dd888793fd5d4a54cdd63400b5d62d3534ba4d8d Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Wed, 4 Dec 2024 14:44:33 +0500 Subject: [PATCH 11/12] =?UTF-8?q?=D0=92=D0=BD=D0=B5=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Persistence.Client/Clients/ISetpointClient.cs | 2 +- .../Clients/ITechMessagesClient.cs | 2 +- .../Clients/ITimeSeriesClient.cs | 2 +- .../Clients/ITimestampedSetClient.cs | 2 +- .../Controllers/SetpointControllerTest.cs | 41 +++++++++++++------ .../Controllers/TechMessagesControllerTest.cs | 4 +- .../TimeSeriesBaseControllerTest.cs | 2 +- .../TimestampedSetControllerTest.cs | 18 ++++---- .../Repositories/SetpointRepository.cs | 2 +- .../Repositories/TechMessagesRepository.cs | 2 +- 10 files changed, 46 insertions(+), 31 deletions(-) diff --git a/Persistence.Client/Clients/ISetpointClient.cs b/Persistence.Client/Clients/ISetpointClient.cs index 886e034..0b81ffa 100644 --- a/Persistence.Client/Clients/ISetpointClient.cs +++ b/Persistence.Client/Clients/ISetpointClient.cs @@ -26,5 +26,5 @@ public interface ISetpointClient Task<IApiResponse<IEnumerable<SetpointLogDto>>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token); [Post($"{BaseRoute}/")] - Task<IApiResponse> Save(Guid setpointKey, object newValue); + Task<IApiResponse> Add(Guid setpointKey, object newValue); } diff --git a/Persistence.Client/Clients/ITechMessagesClient.cs b/Persistence.Client/Clients/ITechMessagesClient.cs index cbdf7ef..878c6cf 100644 --- a/Persistence.Client/Clients/ITechMessagesClient.cs +++ b/Persistence.Client/Clients/ITechMessagesClient.cs @@ -14,7 +14,7 @@ namespace Persistence.Client.Clients Task<IApiResponse<PaginationContainer<TechMessageDto>>> GetPage([Query] RequestDto request, CancellationToken token); [Post($"{BaseRoute}")] - Task<IApiResponse<int>> InsertRange([Body] IEnumerable<TechMessageDto> dtos, CancellationToken token); + Task<IApiResponse<int>> AddRange([Body] IEnumerable<TechMessageDto> dtos, CancellationToken token); [Get($"{BaseRoute}/systems")] Task<IApiResponse<IEnumerable<string>>> GetSystems(CancellationToken token); diff --git a/Persistence.Client/Clients/ITimeSeriesClient.cs b/Persistence.Client/Clients/ITimeSeriesClient.cs index 349337b..8e97836 100644 --- a/Persistence.Client/Clients/ITimeSeriesClient.cs +++ b/Persistence.Client/Clients/ITimeSeriesClient.cs @@ -8,7 +8,7 @@ public interface ITimeSeriesClient<TDto> private const string BaseRoute = "/api/dataSaub"; [Post($"{BaseRoute}")] - Task<IApiResponse<int>> InsertRange(IEnumerable<TDto> dtos); + Task<IApiResponse<int>> AddRange(IEnumerable<TDto> dtos); [Get($"{BaseRoute}")] Task<IApiResponse<IEnumerable<TDto>>> Get(DateTimeOffset dateBegin, DateTimeOffset dateEnd); diff --git a/Persistence.Client/Clients/ITimestampedSetClient.cs b/Persistence.Client/Clients/ITimestampedSetClient.cs index 95e8bd1..bbff603 100644 --- a/Persistence.Client/Clients/ITimestampedSetClient.cs +++ b/Persistence.Client/Clients/ITimestampedSetClient.cs @@ -20,7 +20,7 @@ public interface ITimestampedSetClient /// <param name="sets"></param> /// <returns></returns> [Post(baseUrl)] - Task<IApiResponse<int>> InsertRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets); + Task<IApiResponse<int>> AddRange(Guid idDiscriminator, IEnumerable<TimestampedSetDto> sets); /// <summary> /// Получение данных с фильтрацией. Значение фильтра null - отключен diff --git a/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs b/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs index d314cec..2c455a0 100644 --- a/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs @@ -1,4 +1,5 @@ using System.Net; +using Microsoft.AspNetCore.Mvc.TagHelpers.Cache; using Microsoft.Extensions.DependencyInjection; using Persistence.Client; using Persistence.Client.Clients; @@ -47,7 +48,7 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetCurrent_AfterSave_returns_success() { //arrange - var setpointKey = await Save(); + var setpointKey = await Add(); //act var response = await setpointClient.GetCurrent([setpointKey]); @@ -83,7 +84,7 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetHistory_AfterSave_returns_success() { //arrange - var setpointKey = await Save(); + var setpointKey = await Add(); var historyMoment = DateTimeOffset.UtcNow; historyMoment = historyMoment.AddDays(1); @@ -120,7 +121,7 @@ namespace Persistence.IntegrationTests.Controllers public async Task GetLog_AfterSave_returns_success() { //arrange - var setpointKey = await Save(); + var setpointKey = await Add(); //act var response = await setpointClient.GetLog([setpointKey]); @@ -144,8 +145,8 @@ namespace Persistence.IntegrationTests.Controllers //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); - Assert.Equal(DateTimeOffset.MinValue, response.Content?.From); - Assert.Equal(DateTimeOffset.MaxValue, response.Content?.To); + Assert.Equal(DateTimeOffset.MinValue, response.Content!.From); + Assert.Equal(DateTimeOffset.MaxValue, response.Content!.To); } [Fact] @@ -153,7 +154,12 @@ namespace Persistence.IntegrationTests.Controllers { //arrange dbContext.CleanupDbSet<Setpoint>(); - await Save(); + + await Add(); + + var dateBegin = DateTimeOffset.MinValue; + var take = 1; + var part = await setpointClient.GetPart(dateBegin, take, new CancellationToken()); //act var response = await setpointClient.GetDatesRangeAsync(new CancellationToken()); @@ -161,8 +167,17 @@ namespace Persistence.IntegrationTests.Controllers //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); - Assert.NotNull(response.Content?.From); - Assert.NotNull(response.Content?.To); + + var expectedValue = part.Content! + .FirstOrDefault()!.Created + .ToString("dd.MM.yyyy-HH:mm:ss"); + var actualValueFrom = response.Content.From + .ToString("dd.MM.yyyy-HH:mm:ss"); + Assert.Equal(expectedValue, actualValueFrom); + + var actualValueTo = response.Content.To + .ToString("dd.MM.yyyy-HH:mm:ss"); + Assert.Equal(expectedValue, actualValueTo); } [Fact] @@ -187,7 +202,7 @@ namespace Persistence.IntegrationTests.Controllers //arrange var dateBegin = DateTimeOffset.UtcNow; var take = 1; - await Save(); + await Add(); //act var response = await setpointClient.GetPart(dateBegin, take, new CancellationToken()); @@ -201,10 +216,10 @@ namespace Persistence.IntegrationTests.Controllers [Fact] public async Task Save_returns_success() { - await Save(); + await Add(); } - private async Task<Guid> Save() + private async Task<Guid> Add() { //arrange var setpointKey = Guid.NewGuid(); @@ -215,10 +230,10 @@ namespace Persistence.IntegrationTests.Controllers }; //act - var response = await setpointClient.Save(setpointKey, setpointValue); + var response = await setpointClient.Add(setpointKey, setpointValue); //assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(HttpStatusCode.Created, response.StatusCode); return setpointKey; } diff --git a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs index ec4e40a..1f194ad 100644 --- a/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/TechMessagesControllerTest.cs @@ -97,7 +97,7 @@ namespace Persistence.IntegrationTests.Controllers }; //act - var response = await techMessagesClient.InsertRange(dtos, new CancellationToken()); + var response = await techMessagesClient.AddRange(dtos, new CancellationToken()); //assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); @@ -276,7 +276,7 @@ namespace Persistence.IntegrationTests.Controllers //act - var response = await techMessagesClient.InsertRange(dtos, new CancellationToken()); + var response = await techMessagesClient.AddRange(dtos, new CancellationToken()); //assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); diff --git a/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs b/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs index 87efa43..08bb11e 100644 --- a/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/TimeSeriesBaseControllerTest.cs @@ -30,7 +30,7 @@ public abstract class TimeSeriesBaseControllerTest<TEntity, TDto> : BaseIntegrat var expected = dto.Adapt<TDto>(); //act - var response = await timeSeriesClient.InsertRange(new TDto[] { expected }); + var response = await timeSeriesClient.AddRange(new TDto[] { expected }); //assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs b/Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs index aa33e1b..3fc7dff 100644 --- a/Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs +++ b/Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs @@ -25,7 +25,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest IEnumerable<TimestampedSetDto> testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); // act - var response = await client.InsertRange(idDiscriminator, testSets); + var response = await client.AddRange(idDiscriminator, testSets); // assert Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); @@ -39,7 +39,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); int count = 10; IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); // act var response = await client.Get(idDiscriminator, null, null, 0, int.MaxValue); @@ -58,7 +58,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); int count = 10; IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); string[] props = ["A"]; // act @@ -86,7 +86,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest var dateMin = DateTimeOffset.Now; var dateMax = DateTimeOffset.Now.AddSeconds(count); IEnumerable<TimestampedSetDto> testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); var tail = testSets.OrderBy(t => t.Timestamp).Skip(count / 2).Take(int.MaxValue); var geDate = tail.First().Timestamp; var tolerance = TimeSpan.FromSeconds(1); @@ -111,7 +111,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); int count = 10; IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); var expectedCount = count / 2; // act @@ -133,7 +133,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest var expectedCount = 1; int count = 10 + expectedCount; IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); // act var response = await client.Get(idDiscriminator, null, null, count - expectedCount, count); @@ -152,7 +152,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); int count = 10; IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); var expectedCount = 8; // act @@ -174,7 +174,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest var dateMin = DateTimeOffset.Now; var dateMax = DateTimeOffset.Now.AddSeconds(count-1); IEnumerable<TimestampedSetDto> testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); var tolerance = TimeSpan.FromSeconds(1); // act @@ -195,7 +195,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); int count = 144; IEnumerable<TimestampedSetDto> testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); - var insertResponse = await client.InsertRange(idDiscriminator, testSets); + var insertResponse = await client.AddRange(idDiscriminator, testSets); // act var response = await client.Count(idDiscriminator); diff --git a/Persistence.Repository/Repositories/SetpointRepository.cs b/Persistence.Repository/Repositories/SetpointRepository.cs index 8f0492c..f0e921f 100644 --- a/Persistence.Repository/Repositories/SetpointRepository.cs +++ b/Persistence.Repository/Repositories/SetpointRepository.cs @@ -47,7 +47,7 @@ namespace Persistence.Repository.Repositories { var query = GetQueryReadOnly(); var entities = await query - .Where(e => e.Created > dateBegin) + .Where(e => e.Created >= dateBegin) .Take(take) .ToArrayAsync(token); var dtos = entities diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index 04bab02..38d24e8 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -114,7 +114,7 @@ namespace Persistence.Repository.Repositories { var query = GetQueryReadOnly(); var entities = await query - .Where(e => e.Timestamp > dateBegin) + .Where(e => e.Timestamp >= dateBegin) .Take(take) .ToArrayAsync(token); var dtos = entities -- 2.45.2 From 2228c841397a1532c492e7d52dcfba36938a82d4 Mon Sep 17 00:00:00 2001 From: Roman Efremov <rs.efremov@digitaldrilling.ru> Date: Wed, 4 Dec 2024 16:29:15 +0500 Subject: [PATCH 12/12] =?UTF-8?q?=D0=94=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Persistence.API/Controllers/SetpointController.cs | 7 ++++--- Persistence.API/Controllers/TechMessagesController.cs | 6 +----- .../Repositories/TechMessagesRepository.cs | 3 ++- Persistence/API/ISetpointApi.cs | 2 +- Persistence/Repositories/ITechMessagesRepository.cs | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Persistence.API/Controllers/SetpointController.cs b/Persistence.API/Controllers/SetpointController.cs index 1bae2c6..42a5ff5 100644 --- a/Persistence.API/Controllers/SetpointController.cs +++ b/Persistence.API/Controllers/SetpointController.cs @@ -101,10 +101,11 @@ public class SetpointController : ControllerBase, ISetpointApi /// <param name="token"></param> /// <returns></returns> [HttpPost] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - public async Task<IActionResult> Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token) + [ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)] + public async Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token) { - await setpointRepository.Add(setpointKey, newValue, idUser, token); + var userId = User.GetUserId<Guid>(); + await setpointRepository.Add(setpointKey, newValue, userId, token); return CreatedAtAction(nameof(Add), true); } diff --git a/Persistence.API/Controllers/TechMessagesController.cs b/Persistence.API/Controllers/TechMessagesController.cs index bb26c6a..d2691c1 100644 --- a/Persistence.API/Controllers/TechMessagesController.cs +++ b/Persistence.API/Controllers/TechMessagesController.cs @@ -110,12 +110,8 @@ public class TechMessagesController : ControllerBase public async Task<IActionResult> AddRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token) { var userId = User.GetUserId<Guid>(); - foreach (var dto in dtos) - { - dto.UserId = userId; - } - var result = await techMessagesRepository.AddRange(dtos, token); + var result = await techMessagesRepository.AddRange(dtos, userId, token); return CreatedAtAction(nameof(AddRange), result); } diff --git a/Persistence.Repository/Repositories/TechMessagesRepository.cs b/Persistence.Repository/Repositories/TechMessagesRepository.cs index 38d24e8..c838619 100644 --- a/Persistence.Repository/Repositories/TechMessagesRepository.cs +++ b/Persistence.Repository/Repositories/TechMessagesRepository.cs @@ -88,7 +88,7 @@ namespace Persistence.Repository.Repositories return result; } - public async Task<int> AddRange(IEnumerable<TechMessageDto> dtos, CancellationToken token) + public async Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token) { var entities = new List<TechMessage>(); @@ -100,6 +100,7 @@ namespace Persistence.Repository.Repositories ?? await CreateDrillingSystem(dto.System, token); entity.SystemId = systemId; + entity.UserId = userId; entities.Add(entity); } diff --git a/Persistence/API/ISetpointApi.cs b/Persistence/API/ISetpointApi.cs index 0d23d69..b1504a2 100644 --- a/Persistence/API/ISetpointApi.cs +++ b/Persistence/API/ISetpointApi.cs @@ -40,5 +40,5 @@ public interface ISetpointApi : ISyncApi<SetpointLogDto> /// <param name="newValue">значение</param> /// <param name="token"></param> /// <returns></returns> - Task<IActionResult> Add(Guid setpointKey, object newValue, Guid userId, CancellationToken token); + Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token); } diff --git a/Persistence/Repositories/ITechMessagesRepository.cs b/Persistence/Repositories/ITechMessagesRepository.cs index 2ddf71c..92e8f70 100644 --- a/Persistence/Repositories/ITechMessagesRepository.cs +++ b/Persistence/Repositories/ITechMessagesRepository.cs @@ -22,7 +22,7 @@ namespace Persistence.Repositories /// <param name="dtos"></param> /// <param name="token"></param> /// <returns></returns> - Task<int> AddRange(IEnumerable<TechMessageDto> dtos, CancellationToken token); + Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token); /// <summary> /// Получение списка уникальных названий систем АБ -- 2.45.2