#127 Хранение технологических сообщений #4
@ -6,7 +6,7 @@ using Persistence.Repository.Data;
|
||||
namespace Persistence.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// <EFBFBD>אבמעא ס גנולוםם<D79D>לט האםם<D79D>לט
|
||||
/// Работа с временными данными
|
||||
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
|
@ -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);
|
||||
|
||||
on.nemtina
commented
var userId = User.GetUserId(); var userId = User.GetUserId<Guid>();
rs.efremov
commented
Вынесено в параметр Вынесено в параметр
|
||||
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();
|
||||
on.nemtina
commented
Вроде бы договорились CreatedAtAction возвращать? Вот, например: var result = await techMessagesRepository.InsertRange(dtos, token); Еще название самого метода Save путает. По коду видно, что это именно вставка, а не просто сохранение...Но, может, так надо... Вроде бы договорились CreatedAtAction возвращать?
А еще метод репозитория setpointRepository.Save ничего не возвращает, хотя в других подлобных методах возвращается int.
Вот, например:
var result = await techMessagesRepository.InsertRange(dtos, token);
return CreatedAtAction(nameof(InsertRange), result);
Еще название самого метода Save путает. По коду видно, что это именно вставка, а не просто сохранение...Но, может, так надо...
rs.efremov
commented
1) setpointRepository.Save ничего не возвращает т.к. количество сохраненных записей всегда будет == 1
2) Для единообразия можно сделать "Add" "AddRange"
|
||||
}
|
||||
|
@ -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>
|
||||
/// Получить статистику по системам
|
||||
on.nemtina
commented
Лучше все переменные и свойства importantId переименовать на categoryId или на categoryImportantId. Нужно, чтобы слово "категория" была, а то не очень понятно. Лучше все переменные и свойства importantId переименовать на categoryId или на categoryImportantId. Нужно, чтобы слово "категория" была, а то не очень понятно.
|
||||
/// </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>
|
||||
on.nemtina
commented
Здесь нужно вычислить пользователя, чтобы передать ключ пользователя далее внутрь метода InsertRange репозитория (для вставки в базу, так как у TechMessage есть UserId) Здесь нужно вычислить пользователя, чтобы передать ключ пользователя далее внутрь метода InsertRange репозитория (для вставки в базу, так как у TechMessage есть UserId)
|
||||
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||
/// </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);
|
||||
}
|
||||
|
||||
on.nemtina
commented
Желательно вынести в константу из метода контроллера. Желательно вынести в константу из метода контроллера.
Желательно эту константу хранить где-то на уровне репозитория (спросить у Никиты)
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ public class TimeSeriesController<TDto> : ControllerBase, ITimeSeriesDataApi<TDt
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ïîëó÷èòü ñïèñîê îáúåêòîâ, óäîâëåòâîðÿþùèé äèàïàçîíó äàò
|
||||
/// Получить список объектов, удовлетворяющий диапазону дат
|
||||
on.nemtina
commented
Тут тоже кодировка полетела Тут тоже кодировка полетела
|
||||
/// </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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
on.nemtina
commented
Здесь тоже нужно поправить тип возвращаемых данных Здесь тоже нужно поправить тип возвращаемых данных
$"{BaseRoute}/statistics/" + "{autoDrillingSystem}" -> можно написать слитно, а то так хуже читается
rs.efremov
commented
не актуально, autoDrillingSystem ныне приходит не с роута не актуально, autoDrillingSystem ныне приходит не с роута
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
@ -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 =>
|
||||
{
|
||||
|
@ -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)
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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; }
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
on.nemtina
commented
Тут по идее на null уже можно не проверять response.Content?.From, потому что выше есть проверка Assert.NotNull(response.Content). Если студия ругается, то можно так написать: response.Content!.From Ниже приложила скрин подобного тестового метода, только в ChangeLogTestController Тут по идее на null уже можно не проверять response.Content?.From, потому что выше есть проверка Assert.NotNull(response.Content). Если студия ругается, то можно так написать: response.Content!.From
А еще, думаю, нужно в качестве теста заинсертить туда несколько записей, и ,зная, какие у них максимальные и минимальные даты - сверять их с тем, что вернет GetDatesRangeAsyncю.
Ниже приложила скрин подобного тестового метода, только в ChangeLogTestController
rs.efremov
commented
Сверять максимальные и минимальные даты необъодимо будет в методе GetDatesRange_AfterSave_returns_success Сверять максимальные и минимальные даты необъодимо будет в методе GetDatesRange_AfterSave_returns_success
|
||||
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);
|
||||
on.nemtina
commented
Assert.NotNull(response.Content?.From); Assert.NotNull(response.Content?.From);
Тут также выше уже была проверка на null: Assert.NotNull(response.Content);
Поэтому ниже можно не писать знак вопроса: response.Content?.From.
А если не писать знак вопроса, то response.Content.From тоже не нужно проверять на null,потому что From не nullable.
Тут тоже желательно сравнить From и To с каким-то конкретными значениями, которые ты ожиадешь
|
||||
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()
|
||||
{
|
||||
|
@ -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);
|
||||
on.nemtina
commented
Для тестов, которые что-то возвращают, необходимо предварительно записывать данные. И сравнивать уже получаемые данные с теми, что туда записывались. Сейчас здесь просто проверка на то, что метод вернул 200 ответ и что пришла пустая коллекция. Это верно, потому что в базе ничего нет. Но не факт, что тест бы прошел, если бы в базе что-то было. Для тестов, которые что-то возвращают, необходимо предварительно записывать данные. И сравнивать уже получаемые данные с теми, что туда записывались.
Например, в данном методе нужно было предварительно что-то записать в базу, на основании чего бы рассчиталась статистика, а потом этот расчет уже сравнить с тем, что ты ожидаешь получить.
Сейчас здесь просто проверка на то, что метод вернул 200 ответ и что пришла пустая коллекция. Это верно, потому что в базе ничего нет. Но не факт, что тест бы прошел, если бы в базе что-то было.
rs.efremov
commented
У каждого returns_success теста есть аналог с AfterSave - в котором, как раз таки, проверяются сохраненные данные. Так сделано для тестирования работоспособности отдельных методов вне зависимости от метода сохранения. У каждого returns_success теста есть аналог с AfterSave - в котором, как раз таки, проверяются сохраненные данные. Так сделано для тестирования работоспособности отдельных методов вне зависимости от метода сохранения.
|
||||
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);
|
||||
}
|
||||
|
||||
on.nemtina
commented
Не очень понятно, что такое e.System == e.System, оно же всегда будет true давать? Не очень понятно, что такое e.System == e.System, оно же всегда будет true давать?
|
||||
[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());
|
||||
on.nemtina
commented
Перед проверкой необходимо в базу что-то записывать и уже потом проверять сам метод контроллера. Сейчас же здесь просто проверка на то, что метод вернул 200 ответ и что пришла пустая коллекция. Это верно, потому что в базе ничего нет. Но не факт, что тест бы прошел, если бы в базе что-то было. Перед проверкой необходимо в базу что-то записывать и уже потом проверять сам метод контроллера.
Сейчас же здесь просто проверка на то, что метод вернул 200 ответ и что пришла пустая коллекция. Это верно, потому что в базе ничего нет. Но не факт, что тест бы прошел, если бы в базе что-то было.
rs.efremov
commented
У каждого returns_success теста есть аналог с AfterSave - в котором, как раз таки, проверяются сохраненные данные. Так сделано для тестирования работоспособности отдельных методов вне зависимости от метода сохранения. У каждого returns_success теста есть аналог с AfterSave - в котором, как раз таки, проверяются сохраненные данные. Так сделано для тестирования работоспособности отдельных методов вне зависимости от метода сохранения.
|
||||
|
||||
//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()
|
||||
}
|
||||
};
|
||||
|
@ -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)
|
||||
on.nemtina
commented
Тут нужно у Никиты спросить, строго или не строгое неравенство нужно. Я почему-то думала, что нужно >= писать....Но лучше у Никиты спросить) Тут нужно у Никиты спросить, строго или не строгое неравенство нужно. Я почему-то думала, что нужно >= писать....Но лучше у Никиты спросить)
ng.frolov
commented
Чтобы такие вопросы не возникали мы называем переменные для фильтрации:
Конкретно в этом случае нужен ≥ Чтобы такие вопросы не возникали мы называем переменные для фильтрации:
- Gt* (greater-than ">")
- Lt* (less-than "<")
- Ge* (greater-than or equals "≥")
- Lt* (less-than or equals "≤")
Конкретно в этом случае нужен ≥
|
||||
.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()
|
||||
{
|
||||
|
@ -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
|
||||
on.nemtina
commented
По код-стандарту договорились, что материализация будет происходить в массив. Поэтому лучше ToArrayAsync(token). Должен быть еще токен отмены. Плюс желательно сортировку по умолчанию иметь, если вдруг request.SortSettings будет пустым По код-стандарту договорились, что материализация будет происходить в массив. Поэтому лучше ToArrayAsync(token). Должен быть еще токен отмены.
Плюс желательно сортировку по умолчанию иметь, если вдруг request.SortSettings будет пустым
|
||||
: nameof(TechMessage.Timestamp);
|
||||
var entities = await query
|
||||
.SortBy(request.SortSettings)
|
||||
.Skip(request.Skip)
|
||||
.Take(request.Take)
|
||||
on.nemtina
commented
Count должен рассчитываться до Skip и Take, - то есть Count показывает сколько всего элементов существует Count должен рассчитываться до Skip и Take, - то есть Count показывает сколько всего элементов существует
|
||||
.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>())
|
||||
};
|
||||
|
||||
on.nemtina
commented
Название переменной желательно поправить. Можно назвать result. Название переменной желательно поправить. Можно назвать result.
|
||||
return dto;
|
||||
}
|
||||
on.nemtina
commented
Вообще, думаю, что во всех местах, где есть поиск или фильтрация по имени, необходимо обе сравниваемые переменные приводить к одному регистру и убирать пробелы. Вообще, думаю, что во всех местах, где есть поиск или фильтрация по имени, необходимо обе сравниваемые переменные приводить к одному регистру и убирать пробелы.
|
||||
|
||||
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
|
||||
{
|
||||
on.nemtina
commented
Нужно токен передать внутрь приватного метода Нужно токен передать внутрь приватного метода
|
||||
System = key,
|
||||
Categories = group
|
||||
.Where(g => categoryIds.Count() == 0 || categoryIds.Contains(g.CategoryId))
|
||||
on.nemtina
commented
Здесь можно не писать Здесь можно не писать ```?? []```, по идее в systems не может быть null, там может быть только пустая коллекция
|
||||
})
|
||||
.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()
|
||||
on.nemtina
commented
Здесь тоже нужно токен передать Здесь тоже нужно токен передать
|
||||
{
|
||||
on.nemtina
commented
Здесь точно e.Name == dto.System необходимо приводить к одному регистру и убирать пробелы. Также при создании системы CreateSystem лучше предварительно убирать пробелы. Здесь точно e.Name == dto.System необходимо приводить к одному регистру и убирать пробелы.
Пользователь может передать dto.System, например, с пробелом. Тогда поиск по системам не найдет необходимого элемента и создаст новый с таким же названием и с пробелом.
Также при создании системы CreateSystem лучше предварительно убирать пробелы.
|
||||
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)
|
||||
on.nemtina
commented
60 минут - лучше в отдельное поле вынести 60 минут - лучше в отдельное поле вынести
|
||||
@ -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);
|
||||
on.nemtina
commented
Здесь тоже можно убрать Здесь тоже можно убрать ``` ?? []```, так как внутри systems не может быть null.
Чтобы гарантированно это обозначить, можно написать так: return systems!
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace Persistence.API;
|
||||
/// <summary>
|
||||
/// Интерфейс для API, предназначенного для синхронизации данных
|
||||
/// </summary>
|
||||
public interface ISyncApi<TDto> where TDto : class, new()
|
||||
public interface ISyncApi<TDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Получить порцию записей, начиная с заданной даты
|
||||
|
@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Модель системы автобурения
|
||||
/// </summary>
|
||||
public class ADSystemDto
|
||||
public class DrillingSystemDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Ключ
|
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; }
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
ng.frolov
commented
Аргументы лучше местами поменять, чтобы их порядок быть от более крупной сущности к более мелкой (Плюс тогда сигнатура будет больше похожа на сигнатуру веб-апи). В запросе от пользователя также лучше сразу предусмотреть возможность множественного выбора систем и категорий. Прим.: Возвращаемый Dictionary<string, int> не особо понятно, что именно возвращает.
Аргументы лучше местами поменять, чтобы их порядок быть от более крупной сущности к более мелкой (Плюс тогда сигнатура будет больше похожа на сигнатуру веб-апи).
В запросе от пользователя также лучше сразу предусмотреть возможность множественного выбора систем и категорий. Прим.: `IEnumerable<int>? CategoriesIds`
Возвращаемый Dictionary<string, int> не особо понятно, что именно возвращает.
Предлагаю возвращать:
```
[
{
"system": "System_1",
"categories": [
{"1": 1}, // Аварии
{"2": 5}, // Предупреждения
{"3": 15},
...
]
},
...
]
```
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
Кодировка