Merge from dev
This commit is contained in:
commit
c66b0bbc2f
@ -4,6 +4,10 @@ using Persistence.Repositories;
|
||||
using Persistence.Repository.Data;
|
||||
|
||||
namespace Persistence.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Работа с временными данными
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/[controller]")]
|
||||
|
@ -1,15 +1,19 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
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)
|
||||
@ -17,6 +21,12 @@ namespace Persistence.API.Controllers
|
||||
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)
|
||||
{
|
||||
@ -25,6 +35,13 @@ namespace Persistence.API.Controllers
|
||||
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)
|
||||
{
|
||||
@ -33,6 +50,12 @@ namespace Persistence.API.Controllers
|
||||
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)
|
||||
{
|
||||
@ -41,13 +64,49 @@ namespace Persistence.API.Controllers
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token)
|
||||
/// <summary>
|
||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("range")]
|
||||
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(CancellationToken token)
|
||||
{
|
||||
// ToDo: вычитка idUser
|
||||
await setpointRepository.Save(setpointKey, newValue, 0, token);
|
||||
var result = await setpointRepository.GetDatesRangeAsync(token);
|
||||
|
||||
return Ok();
|
||||
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]
|
||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||
public async Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token)
|
||||
{
|
||||
var userId = User.GetUserId<Guid>();
|
||||
await setpointRepository.Add(setpointKey, newValue, userId, token);
|
||||
|
||||
return CreatedAtAction(nameof(Add), true);
|
||||
}
|
||||
}
|
||||
|
128
Persistence.API/Controllers/TechMessagesController.cs
Normal file
128
Persistence.API/Controllers/TechMessagesController.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Persistence.Models;
|
||||
using Persistence.Repositories;
|
||||
|
||||
namespace Persistence.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Работа с состояниями систем автобурения (АБ)
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/[controller]")]
|
||||
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)
|
||||
{
|
||||
this.techMessagesRepository = 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="autoDrillingSystem"></param>
|
||||
/// <param name="categoryIds"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("statistics")]
|
||||
public async Task<ActionResult<IEnumerable<MessagesStatisticDto>>> GetStatistics([FromQuery] IEnumerable<string> autoDrillingSystem, [FromQuery] IEnumerable<int> categoryIds, CancellationToken token)
|
||||
{
|
||||
var result = await techMessagesRepository.GetStatistics(autoDrillingSystem, categoryIds, token);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список всех систем
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("systems")]
|
||||
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>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||
public async Task<IActionResult> AddRange([FromBody] IEnumerable<TechMessageDto> dtos, CancellationToken token)
|
||||
{
|
||||
var userId = User.GetUserId<Guid>();
|
||||
|
||||
var result = await techMessagesRepository.AddRange(dtos, userId, token);
|
||||
|
||||
return CreatedAtAction(nameof(AddRange), result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить словарь категорий
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("categories")]
|
||||
public ActionResult<Dictionary<int, string>> GetImportantCategories()
|
||||
{
|
||||
return Ok(categories);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using Persistence.Models;
|
||||
using Persistence.Repositories;
|
||||
|
||||
namespace Persistence.API.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/[controller]")]
|
||||
@ -17,32 +18,57 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt
|
||||
this.timeSeriesDataRepository = timeSeriesDataRepository;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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>
|
||||
/// <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);
|
||||
var result = await timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавить записи
|
||||
/// </summary>
|
||||
/// <param name="dtos"></param>
|
||||
/// <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 this.timeSeriesDataRepository.InsertRange(dtos, token);
|
||||
var result = await timeSeriesDataRepository.AddRange(dtos, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
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;
|
||||
using System.Text.Json.Nodes;
|
||||
@ -10,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 =>
|
||||
@ -38,11 +48,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;
|
||||
//c.IncludeXmlComments(xmlPath, includeControllerXmlComment);
|
||||
//c.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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -24,6 +24,9 @@ public class Startup
|
||||
services.AddPersistenceDbContext(Configuration);
|
||||
services.AddAuthorization();
|
||||
services.AddJWTAuthentication(Configuration);
|
||||
services.AddMemoryCache();
|
||||
|
||||
DependencyInjection.MapsterSetup();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
@ -4,7 +4,7 @@ using Refit;
|
||||
namespace Persistence.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для тестирования API, предназначенного для работы с уставками
|
||||
/// Интерфейс клиента для работы с уставками
|
||||
/// </summary>
|
||||
public interface ISetpointClient
|
||||
{
|
||||
@ -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);
|
||||
Task<IApiResponse> Add(Guid setpointKey, object newValue);
|
||||
}
|
||||
|
31
Persistence.Client/Clients/ITechMessagesClient.cs
Normal file
31
Persistence.Client/Clients/ITechMessagesClient.cs
Normal file
@ -0,0 +1,31 @@
|
||||
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>> AddRange([Body] IEnumerable<TechMessageDto> dtos, CancellationToken token);
|
||||
|
||||
[Get($"{BaseRoute}/systems")]
|
||||
Task<IApiResponse<IEnumerable<string>>> GetSystems(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);
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Persistence.Models;
|
||||
using Persistence.Models;
|
||||
using Refit;
|
||||
|
||||
namespace Persistence.Client.Clients;
|
||||
@ -9,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);
|
||||
|
@ -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 - отключен
|
||||
|
@ -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),
|
||||
|
@ -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 =>
|
||||
{
|
||||
|
233
Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.Designer.cs
generated
Normal file
233
Persistence.Database.Postgres/Migrations/20241202072250_TechMessageMigration.Designer.cs
generated
Normal file
@ -0,0 +1,233 @@
|
||||
// <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("20241202072250_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.DrillingSystem", 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("DrillingSystem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
|
||||
{
|
||||
b.Property<Guid>("EventId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Id события");
|
||||
|
||||
b.Property<int>("CategoryId")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("Id Категории важности");
|
||||
|
||||
b.Property<double?>("Depth")
|
||||
.HasColumnType("double precision")
|
||||
.HasComment("Глубина забоя");
|
||||
|
||||
b.Property<string>("MessageText")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(512)")
|
||||
.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 пользователя за пультом бурильщика");
|
||||
|
||||
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")
|
||||
.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<Guid>("IdUser")
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Id автора последнего изменения");
|
||||
|
||||
b.Property<object>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasComment("Значение уставки");
|
||||
|
||||
b.HasKey("Key", "Created");
|
||||
|
||||
b.ToTable("Setpoint");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
|
||||
{
|
||||
b.HasOne("Persistence.Database.Entity.DrillingSystem", "System")
|
||||
.WithMany()
|
||||
.HasForeignKey("SystemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("System");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
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: "DrillingSystem",
|
||||
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_DrillingSystem", x => x.SystemId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TechMessage",
|
||||
columns: table => new
|
||||
{
|
||||
EventId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id события"),
|
||||
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 системы автобурения, к которой относится сообщение"),
|
||||
UserId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id пользователя за пультом бурильщика")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TechMessage", x => x.EventId);
|
||||
table.ForeignKey(
|
||||
name: "FK_TechMessage_DrillingSystem_SystemId",
|
||||
column: x => x.SystemId,
|
||||
principalTable: "DrillingSystem",
|
||||
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: "DrillingSystem");
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,89 @@ namespace Persistence.Database.Postgres.Migrations
|
||||
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Persistence.Database.Entity.DrillingSystem", 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("DrillingSystem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
|
||||
{
|
||||
b.Property<Guid>("EventId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Id события");
|
||||
|
||||
b.Property<int>("CategoryId")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("Id Категории важности");
|
||||
|
||||
b.Property<double?>("Depth")
|
||||
.HasColumnType("double precision")
|
||||
.HasComment("Глубина забоя");
|
||||
|
||||
b.Property<string>("MessageText")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(512)")
|
||||
.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 пользователя за пультом бурильщика");
|
||||
|
||||
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.ChangeLog", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -169,10 +252,10 @@ namespace Persistence.Database.Postgres.Migrations
|
||||
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("Дата изменения уставки");
|
||||
.HasComment("Дата создания уставки");
|
||||
|
||||
b.Property<int>("IdUser")
|
||||
.HasColumnType("integer")
|
||||
b.Property<Guid>("IdUser")
|
||||
.HasColumnType("uuid")
|
||||
.HasComment("Id автора последнего изменения");
|
||||
|
||||
b.Property<object>("Value")
|
||||
@ -184,6 +267,17 @@ namespace Persistence.Database.Postgres.Migrations
|
||||
|
||||
b.ToTable("Setpoint");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
|
||||
{
|
||||
b.HasOne("Persistence.Database.Entity.DrillingSystem", "System")
|
||||
.WithMany()
|
||||
.HasForeignKey("SystemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("System");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ public partial class PersistenceDbContext : DbContext
|
||||
|
||||
public DbSet<Setpoint> Setpoint => Set<Setpoint>();
|
||||
|
||||
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
|
||||
|
||||
public DbSet<TimestampedSet> TimestampedSets => Set<TimestampedSet>();
|
||||
|
||||
public PersistenceDbContext()
|
||||
@ -42,6 +44,16 @@ public partial class PersistenceDbContext : DbContext
|
||||
.Property(e => e.Set)
|
||||
.HasJsonConversion();
|
||||
|
||||
modelBuilder.Entity<TechMessage>(entity =>
|
||||
{
|
||||
entity.HasOne(t => t.System)
|
||||
.WithMany()
|
||||
.HasForeignKey(t => t.SystemId)
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
}
|
||||
|
||||
modelBuilder.Entity<ChangeLog>()
|
||||
.Property(e => e.Value)
|
||||
.HasJsonConversion();
|
||||
|
16
Persistence.Database/Entity/DrillingSystem.cs
Normal file
16
Persistence.Database/Entity/DrillingSystem.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Persistence.Database.Entity;
|
||||
public class DrillingSystem
|
||||
{
|
||||
[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; }
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
33
Persistence.Database/Entity/TechMessage.cs
Normal file
33
Persistence.Database/Entity/TechMessage.cs
Normal file
@ -0,0 +1,33 @@
|
||||
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 CategoryId { get; set; }
|
||||
|
||||
[Comment("Дата возникновения")]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
|
||||
[Comment("Глубина забоя")]
|
||||
public double? Depth { get; set; }
|
||||
|
||||
[Column(TypeName = "varchar(512)"), Comment("Текст сообщения")]
|
||||
public required string MessageText { get; set; }
|
||||
|
||||
[Required, Comment("Id системы автобурения, к которой относится сообщение")]
|
||||
public required Guid SystemId { get; set; }
|
||||
|
||||
[Required, ForeignKey(nameof(SystemId)), Comment("Система автобурения, к которой относится сообщение")]
|
||||
public virtual required DrillingSystem System { get; set; }
|
||||
|
||||
[Comment("Id пользователя за пультом бурильщика")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Mvc.TagHelpers.Cache;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Persistence.Client;
|
||||
using Persistence.Client.Clients;
|
||||
using Persistence.Database.Model;
|
||||
using Xunit;
|
||||
|
||||
namespace Persistence.IntegrationTests.Controllers
|
||||
@ -46,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]);
|
||||
@ -82,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);
|
||||
|
||||
@ -119,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]);
|
||||
@ -132,12 +134,92 @@ namespace Persistence.IntegrationTests.Controllers
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Save_returns_success()
|
||||
public async Task GetDatesRange_returns_success()
|
||||
{
|
||||
await Save();
|
||||
//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);
|
||||
}
|
||||
|
||||
private async Task<Guid> Save()
|
||||
[Fact]
|
||||
public async Task GetDatesRange_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
dbContext.CleanupDbSet<Setpoint>();
|
||||
|
||||
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());
|
||||
|
||||
//assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content);
|
||||
|
||||
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]
|
||||
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 Add();
|
||||
|
||||
//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()
|
||||
{
|
||||
await Add();
|
||||
}
|
||||
|
||||
private async Task<Guid> Add()
|
||||
{
|
||||
//arrange
|
||||
var setpointKey = Guid.NewGuid();
|
||||
@ -148,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;
|
||||
}
|
||||
|
@ -0,0 +1,288 @@
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
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 static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey";
|
||||
private readonly ITechMessagesClient techMessagesClient;
|
||||
private readonly IMemoryCache memoryCache;
|
||||
public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory)
|
||||
{
|
||||
var scope = factory.Services.CreateScope();
|
||||
var persistenceClientFactory = scope.ServiceProvider
|
||||
.GetRequiredService<PersistenceClientFactory>();
|
||||
|
||||
techMessagesClient = persistenceClientFactory.GetClient<ITechMessagesClient>();
|
||||
memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPage_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
|
||||
|
||||
var requestDto = new RequestDto()
|
||||
{
|
||||
Skip = 1,
|
||||
Take = 2,
|
||||
SortSettings = nameof(TechMessage.CategoryId)
|
||||
};
|
||||
|
||||
//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(TechMessage.CategoryId)
|
||||
};
|
||||
|
||||
//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 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.AddRange(dtos, new CancellationToken());
|
||||
|
||||
//assert
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSystems_returns_success()
|
||||
{
|
||||
//arrange
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
|
||||
|
||||
//act
|
||||
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.System)
|
||||
.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
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
dbContext.CleanupDbSet<TechMessage>();
|
||||
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
|
||||
|
||||
var imortantId = 1;
|
||||
var autoDrillingSystem = nameof(TechMessageDto.System);
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken());
|
||||
|
||||
//assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content);
|
||||
Assert.Empty(response.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStatistics_AfterSave_returns_success()
|
||||
{
|
||||
//arrange
|
||||
var imortantId = 0;
|
||||
var autoDrillingSystem = nameof(TechMessageDto.System);
|
||||
var dtos = await InsertRange();
|
||||
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.NotNull(response.Content);
|
||||
var categories = response.Content
|
||||
.FirstOrDefault()?.Categories
|
||||
.FirstOrDefault(e => e.Key == 0).Value;
|
||||
Assert.Equal(filteredDtos.Count(), categories);
|
||||
}
|
||||
|
||||
[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()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = 1,
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Depth = 1.11,
|
||||
MessageText = nameof(TechMessageDto.MessageText),
|
||||
System = nameof(TechMessageDto.System).ToLower(),
|
||||
UserId = Guid.NewGuid()
|
||||
},
|
||||
new TechMessageDto()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
CategoryId = 2,
|
||||
Timestamp = DateTimeOffset.UtcNow,
|
||||
Depth = 2.22,
|
||||
MessageText = nameof(TechMessageDto.MessageText),
|
||||
System = nameof(TechMessageDto.System).ToLower(),
|
||||
UserId = Guid.NewGuid()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//act
|
||||
var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
|
||||
|
||||
//assert
|
||||
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
|
||||
Assert.Equal(dtos.Count, response.Content);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
14
Persistence.IntegrationTests/Extensions/EFCoreExtensions.cs
Normal file
14
Persistence.IntegrationTests/Extensions/EFCoreExtensions.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ public static class DependencyInjection
|
||||
services.AddTransient<ITimeSeriesDataRepository<DataSaubDto>, TimeSeriesDataCachedRepository<DataSaub, DataSaubDto>>();
|
||||
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
|
||||
services.AddTransient<ITimestampedSetRepository, TimestampedSetRepository>();
|
||||
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
267
Persistence.Repository/Extensions/EFExtensionsSortBy.cs
Normal file
267
Persistence.Repository/Extensions/EFExtensionsSortBy.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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 Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token)
|
||||
{
|
||||
var entity = new Setpoint()
|
||||
{
|
||||
|
177
Persistence.Repository/Repositories/TechMessagesRepository.cs
Normal file
177
Persistence.Repository/Repositories/TechMessagesRepository.cs
Normal file
@ -0,0 +1,177 @@
|
||||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Persistence.Database.Entity;
|
||||
using Persistence.Models;
|
||||
using Persistence.Repositories;
|
||||
using Persistence.Repository.Extensions;
|
||||
|
||||
namespace Persistence.Repository.Repositories
|
||||
{
|
||||
public class TechMessagesRepository : ITechMessagesRepository
|
||||
{
|
||||
private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey";
|
||||
private const int CacheExpirationInMinutes = 60;
|
||||
private readonly IMemoryCache memoryCache;
|
||||
private DbContext db;
|
||||
|
||||
public TechMessagesRepository(DbContext db, IMemoryCache memoryCache)
|
||||
{
|
||||
this.memoryCache = memoryCache;
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
|
||||
.Include(e => e.System);
|
||||
|
||||
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)
|
||||
.ToArrayAsync(token);
|
||||
|
||||
var dto = new PaginationContainer<TechMessageDto>()
|
||||
{
|
||||
Skip = request.Skip,
|
||||
Take = request.Take,
|
||||
Count = count,
|
||||
Items = entities.Select(e => e.Adapt<TechMessageDto>())
|
||||
};
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MessagesStatisticDto>> GetStatistics(IEnumerable<string> autoDrillingSystem, IEnumerable<int> categoryIds, CancellationToken token)
|
||||
{
|
||||
var query = GetQueryReadOnly();
|
||||
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);
|
||||
|
||||
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 GetDrillingSystems(token);
|
||||
var result = entities.Select(e => e.Name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token)
|
||||
{
|
||||
|
||||
var entities = new List<TechMessage>();
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
var entity = dto.Adapt<TechMessage>();
|
||||
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;
|
||||
entity.UserId = userId;
|
||||
|
||||
entities.Add(entity);
|
||||
}
|
||||
|
||||
await db.Set<TechMessage>().AddRangeAsync(entities, token);
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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(CacheExpirationInMinutes);
|
||||
|
||||
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!;
|
||||
}
|
||||
private async Task<Guid> CreateDrillingSystem(string name, CancellationToken token)
|
||||
{
|
||||
memoryCache.Remove(SystemCacheKey);
|
||||
|
||||
var entity = new Database.Entity.DrillingSystem()
|
||||
{
|
||||
SystemId = default,
|
||||
Name = name.ToLower().Trim()
|
||||
};
|
||||
|
||||
await db.Set<Database.Entity.DrillingSystem>().AddAsync(entity);
|
||||
await db.SaveChangesAsync(token);
|
||||
|
||||
return entity.SystemId;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -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>());
|
||||
|
||||
|
@ -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>();
|
||||
|
@ -6,7 +6,7 @@ namespace Persistence.API;
|
||||
/// <summary>
|
||||
/// Интерфейс для API, предназначенного для работы с уставками
|
||||
/// </summary>
|
||||
public interface ISetpointApi
|
||||
public interface ISetpointApi : ISyncApi<SetpointLogDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить актуальные значения уставок
|
||||
@ -40,5 +40,5 @@ public interface ISetpointApi
|
||||
/// <param name="newValue">значение</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<ActionResult<int>> Save(Guid setpointKey, object newValue, CancellationToken token);
|
||||
Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace Persistence.API;
|
||||
/// <summary>
|
||||
/// Интерфейс для API, предназначенного для синхронизации данных
|
||||
/// </summary>
|
||||
public interface ISyncApi<TDto> where TDto : class
|
||||
public interface ISyncApi<TDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить порцию записей, начиная с заданной даты
|
||||
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
22
Persistence/Models/DrillingSystemDto.cs
Normal file
22
Persistence/Models/DrillingSystemDto.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Persistence.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Модель системы автобурения
|
||||
/// </summary>
|
||||
public class DrillingSystemDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Ключ
|
||||
/// </summary>
|
||||
public Guid SystemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Наименование
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Описание
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
}
|
17
Persistence/Models/MessagesStatisticDto.cs
Normal file
17
Persistence/Models/MessagesStatisticDto.cs
Normal file
@ -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; }
|
||||
}
|
@ -13,5 +13,5 @@ public class SetpointLogDto : SetpointValueDto
|
||||
/// <summary>
|
||||
/// Ключ пользователя
|
||||
/// </summary>
|
||||
public int IdUser { get; set; }
|
||||
public Guid IdUser { get; set; }
|
||||
}
|
||||
|
52
Persistence/Models/TechMessageDto.cs
Normal file
52
Persistence/Models/TechMessageDto.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Persistence.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Модель технологического сообщения
|
||||
/// </summary>
|
||||
public class TechMessageDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Id события
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid EventId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id Категории важности
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "Id Категории важности не может быть меньше 0")]
|
||||
public int CategoryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Дата возникновения
|
||||
/// </summary>
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Глубина забоя
|
||||
/// </summary>
|
||||
[Range(0, double.MaxValue, ErrorMessage = "Глубина забоя не может быть меньше 0")]
|
||||
public double? Depth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Текст сообщения
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(512, MinimumLength = 1, ErrorMessage = "Допустимая длина текста сообщения от 1 до 512 символов")]
|
||||
public required string MessageText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Система автобурения, к которой относится сообщение
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(256, MinimumLength = 1, ErrorMessage = "Допустимая длина наименования системы АБ от 1 до 256 символов")]
|
||||
public required string System { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id пользователя за пультом бурильщика
|
||||
/// </summary>
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Persistence.Models;
|
||||
|
||||
namespace Persistence.Repositories;
|
||||
@ -32,6 +33,22 @@ public interface ISetpointRepository
|
||||
/// <returns></returns>
|
||||
Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, 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>
|
||||
@ -42,5 +59,5 @@ public interface ISetpointRepository
|
||||
/// <returns></returns>
|
||||
/// to do
|
||||
/// id User учесть в соответствующем методе репозитория
|
||||
Task Save(Guid setpointKey, object newValue, int idUser, CancellationToken token);
|
||||
Task Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token);
|
||||
}
|
||||
|
59
Persistence/Repositories/ITechMessagesRepository.cs
Normal file
59
Persistence/Repositories/ITechMessagesRepository.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System.Threading.Tasks;
|
||||
using Persistence.Models;
|
||||
|
||||
namespace Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс по работе с технологическими сообщениями
|
||||
/// </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>
|
||||
/// <param name="dtos"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> AddRange(IEnumerable<TechMessageDto> dtos, Guid userId, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение списка уникальных названий систем АБ
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<string>> GetSystems(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение количества сообщений по категориям и системам автобурения
|
||||
/// </summary>
|
||||
/// <param name="categoryId">Id Категории важности</param>
|
||||
/// <param name="autoDrillingSystem">Система автобурения</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user