Merge pull request 'Добавить таблицу для учета комментариев и действий пользователя для вывода статистики по ChangeLog' (#30) from feature/#956-change-log-table-comment into master
Some checks failed
Unit tests / tests-and-publication (push) Failing after 1m13s

Reviewed-on: #30
Reviewed-by: Никита Фролов <ng.frolov@digitaldrilling.ru>
This commit is contained in:
on.nemtina 2025-02-24 12:59:27 +05:00
commit ba0c748659
25 changed files with 1150 additions and 397 deletions

View File

@ -1,107 +1,124 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using DD.Persistence.API;
using DD.Persistence.API.Services;
using DD.Persistence.Models;
using DD.Persistence.Models.Requests;
using DD.Persistence.Repositories;
using System.Net;
using DD.Persistence.Models.Common;
using DD.Persistence.Models.Requests;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net;
using UuidExtensions;
namespace DD.Persistence.API.Controllers;
/// <summary>
/// Контроллер по работе с журналом изменений
/// </summary>
[ApiController]
[Authorize]
[Route("api/[controller]")]
public class ChangeLogController : ControllerBase, IChangeLogApi
{
private readonly IChangeLogRepository repository;
private readonly ChangeLogService service;
public ChangeLogController(IChangeLogRepository repository)
/// <summary>
/// ctor
/// </summary>
/// <param name="service"></param>
public ChangeLogController(ChangeLogService service)
{
this.repository = repository;
this.service = service;
}
/// <summary>
/// Добавить записи в журнал изменений по дискриминатору
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("{idDiscriminator}")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> Add(
[FromRoute] Guid idDiscriminator,
[FromBody] ChangeLogValuesDto dto,
CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.AddRange(userId, idDiscriminator, [dto], token);
return CreatedAtAction(nameof(Add), result);
}
[HttpPost("range/{idDiscriminator}")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> AddRange(
[FromRoute] Guid idDiscriminator,
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
string? comment,
CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.AddRange(userId, idDiscriminator, dtos, token);
var changeLogCommitRequest = new CreateChangeLogCommitRequest(Uuid7.Guid(), comment);
var result = await service.AddRange(idDiscriminator, changeLogCommitRequest, dtos, token);
return CreatedAtAction(nameof(AddRange), result);
}
/// <summary>
/// Удалить записи в журнале изменений
/// </summary>
/// <param name="ids"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpDelete]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Delete(Guid id, CancellationToken token)
public async Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, string comment, CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.MarkAsDeleted(userId, [id], token);
var changeLogCommitRequest = new CreateChangeLogCommitRequest(userId, comment);
var result = await service.MarkAsDeleted(ids, changeLogCommitRequest, token);
return Ok(result);
}
[HttpDelete("range")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.MarkAsDeleted(userId, ids, token);
return Ok(result);
}
/// <summary>
/// Очистить все записи в журнале изменений (по дискриминатору) и добавить новые
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost("replace/{idDiscriminator}")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> ClearAndAddRange(
[FromRoute] Guid idDiscriminator,
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
string comment,
CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.ClearAndAddRange(userId, idDiscriminator, dtos, token);
var changeLogCommitRequest = new CreateChangeLogCommitRequest(userId, comment);
var result = await service.ClearAndAddRange(idDiscriminator, changeLogCommitRequest, dtos, token);
return Ok(result);
}
/// <summary>
/// сохранить изменения в записях журнала изменений
/// </summary>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPut]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Update(
ChangeLogValuesDto dto,
CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.UpdateRange(userId, [dto], token);
return Ok(result);
}
[HttpPut("range")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> UpdateRange(
IEnumerable<ChangeLogValuesDto> dtos,
string comment,
CancellationToken token)
{
var userId = User.GetUserId<Guid>();
var result = await repository.UpdateRange(userId, dtos, token);
var changeLogCommitRequest = new CreateChangeLogCommitRequest(userId, comment);
var result = await service.UpdateRange(changeLogCommitRequest, dtos, token);
return Ok(result);
}
/// <summary>
/// Получение актуальных записей (с пагинацией)
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="paginationRequest"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("{idDiscriminator}")]
[ProducesResponseType(typeof(PaginationContainer<ChangeLogValuesDto>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetCurrent(
@ -110,11 +127,19 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
CancellationToken token)
{
var moment = new DateTimeOffset(3000, 1, 1, 0, 0, 0, TimeSpan.Zero);
var result = await repository.GetByDate(idDiscriminator, moment, paginationRequest, token);
var result = await service.GetByDate(idDiscriminator, moment, paginationRequest, token);
return Ok(result);
}
/// <summary>
/// Получение записей на определенный момент времени (с пагинацией)
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="moment"></param>
/// <param name="paginationRequest"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("moment/{idDiscriminator}")]
[ProducesResponseType(typeof(PaginationContainer<ChangeLogValuesDto>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetByDate(
@ -123,11 +148,19 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[FromQuery] PaginationRequest paginationRequest,
CancellationToken token)
{
var result = await repository.GetByDate(idDiscriminator, moment, paginationRequest, token);
var result = await service.GetByDate(idDiscriminator, moment, paginationRequest, token);
return Ok(result);
}
/// <summary>
/// Получение измененных записей за период времени
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dateBegin"></param>
/// <param name="dateEnd"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("history/{idDiscriminator}")]
[ProducesResponseType(typeof(IEnumerable<ChangeLogDto>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
@ -137,37 +170,57 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
DateTimeOffset dateEnd,
CancellationToken token)
{
var result = await repository.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token);
var result = await service.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token);
return Ok(result);
}
/// <summary>
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("datesChange/{idDiscriminator}")]
[ProducesResponseType(typeof(IEnumerable<DateOnly>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<IActionResult> GetDatesChange([FromRoute] Guid idDiscriminator, CancellationToken token)
{
var result = await repository.GetDatesChange(idDiscriminator, token);
var result = await service.GetDatesChange(idDiscriminator, token);
return Ok(result);
}
/// <summary>
/// Получение данных, начиная с определенной даты
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dateBegin"></param>
/// <param name="take"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("part/{idDiscriminator}")]
[ProducesResponseType(typeof(IEnumerable<ChangeLogValuesDto>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<ActionResult<IEnumerable<ChangeLogValuesDto>>> GetPart([FromRoute] Guid idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default)
{
var result = await repository.GetGtDate(idDiscriminator, dateBegin, token);
var result = await service.GetGtDate(idDiscriminator, dateBegin, token);
return Ok(result);
}
/// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("datesRange/{idDiscriminator}")]
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync([FromRoute] Guid idDiscriminator, CancellationToken token)
{
var result = await repository.GetDatesRange(idDiscriminator, token);
var result = await service.GetDatesRange(idDiscriminator, token);
if (result is null)
return NoContent();

View File

@ -9,6 +9,8 @@ using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text.Json.Nodes;
using DD.Persistence.Database.Entity;
using DD.Persistence.API.Services;
namespace DD.Persistence.API;
@ -53,6 +55,7 @@ public static class DependencyInjection
{
services.AddTransient<IWitsDataService, WitsDataService>();
services.AddTransient<ITimestampedValuesService, TimestampedValuesService>();
services.AddTransient<ChangeLogService>();
}
#region Authentication

View File

@ -0,0 +1,192 @@
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Models.Requests;
using DD.Persistence.Repositories;
using Microsoft.Extensions.Caching.Memory;
namespace DD.Persistence.API.Services;
/// <summary>
/// Сервис по работе с журналом изменений
/// </summary>
public class ChangeLogService
{
private readonly IMemoryCache memoryCache;
private readonly IChangeLogCommitRepository commitRepository;
private readonly IChangeLogRepository repository;
private readonly TimeSpan? AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60);
/// <summary>
/// ctor
/// </summary>
/// <param name="memoryCache"></param>
/// <param name="commitRepository"></param>
/// <param name="repository"></param>
public ChangeLogService(
IMemoryCache memoryCache,
IChangeLogCommitRepository commitRepository,
IChangeLogRepository repository)
{
this.memoryCache = memoryCache;
this.commitRepository = commitRepository;
this.repository = repository;
}
/// <summary>
/// Чтение или создание нового коммита
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
private async Task<ChangeLogCommitDto> GetOrCreateCommitAsync(CreateChangeLogCommitRequest request, CancellationToken token)
{
var key = (request.IdAuthor, request.Comment);
var commit = await memoryCache.GetOrCreateAsync(key, async (cacheEntry) =>
{
cacheEntry.AbsoluteExpirationRelativeToNow = AbsoluteExpirationRelativeToNow;
var commit = await commitRepository.Add(request, token);
return commit;
});
return commit!;
}
/// <summary>
/// Добавление записи в журнал изменений
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="request"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<int> AddRange(Guid idDiscriminator, CreateChangeLogCommitRequest request, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var commit = await GetOrCreateCommitAsync(request, token);
var result = await repository.AddRange(idDiscriminator, commit, dtos, token);
return result;
}
/// <summary>
/// Пометить запись журнала изменений как удаленную
/// </summary>
/// <param name="ids"></param>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<int> MarkAsDeleted(IEnumerable<Guid> ids, CreateChangeLogCommitRequest request, CancellationToken token)
{
var commit = await GetOrCreateCommitAsync(request, token);
var result = await repository.MarkAsDeleted(ids, commit, token);
return result;
}
/// <summary>
/// Очистить старые и добавить новые записи в журнал изменений
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="request"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<int> ClearAndAddRange(Guid idDiscriminator, CreateChangeLogCommitRequest request, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var commit = await GetOrCreateCommitAsync(request, token);
var result = await repository.ClearAndAddRange(idDiscriminator, commit, dtos, token);
return result;
}
/// <summary>
/// Обновить записи в журнале изменений
/// </summary>
/// <param name="commitRequest"></param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<int> UpdateRange(CreateChangeLogCommitRequest commitRequest, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var commit = await GetOrCreateCommitAsync(commitRequest, token);
var result = await repository.UpdateRange(commit, dtos, token);
return result;
}
/// <summary>
/// Получение актуальных записей на определенный момент времени (с пагинацией)
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="momentUtc"></param>
/// <param name="paginationRequest"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<PaginationContainer<ChangeLogValuesDto>> GetByDate(
Guid idDiscriminator,
DateTimeOffset momentUtc,
PaginationRequest paginationRequest,
CancellationToken token)
{
var result = await repository.GetByDate(idDiscriminator, momentUtc, paginationRequest, token);
return result;
}
/// <summary>
/// Получение измененных записей за период времени
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dateBegin"></param>
/// <param name="dateEnd"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{
var result = await repository.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token);
return result;
}
/// <summary>
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
{
var result = await repository.GetDatesChange(idDiscriminator, token);
return result;
}
/// <summary>
/// Получить данные, начиная с определенной даты
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dateBegin"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
{
var result = await repository.GetGtDate(idDiscriminator, dateBegin, token);
return result;
}
/// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
{
var result = await repository.GetDatesRange(idDiscriminator, token);
return result;
}
}

View File

@ -19,10 +19,10 @@ public class ChangeLogClient : BaseClient, IChangeLogClient
}
/// <inheritdoc/>
public async Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
public async Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token)
{
var result = await ExecuteGetResponse(
async () => await refitChangeLogClient.ClearAndAddRange(idDiscriminator, dtos, token), token);
async () => await refitChangeLogClient.ClearAndAddRange(idDiscriminator, dtos, comment, token), token);
return result;
}
@ -47,55 +47,28 @@ public class ChangeLogClient : BaseClient, IChangeLogClient
}
/// <inheritdoc/>
public async Task<int> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token)
public async Task<int> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token)
{
var result = await ExecutePostResponse(
async () => await refitChangeLogClient.Add(idDiscriminator, dto, token), token);
async () => await refitChangeLogClient.AddRange(idDiscriminator, dtos, comment, token), token);
return result;
}
/// <inheritdoc/>
public async Task<int> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
public async Task<int> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token)
{
var result = await ExecutePostResponse(
async () => await refitChangeLogClient.AddRange(idDiscriminator, dtos, token), token);
async () => await refitChangeLogClient.UpdateRange(dtos, comment, token), token);
return result;
}
/// <inheritdoc/>
public async Task<int> Update(ChangeLogValuesDto dto, CancellationToken token)
public async Task<int> DeleteRange(IEnumerable<Guid> ids, string comment, CancellationToken token)
{
var result = await ExecutePostResponse(
async () => await refitChangeLogClient.Update(dto, token), token);
return result;
}
/// <inheritdoc/>
public async Task<int> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var result = await ExecutePostResponse(
async () => await refitChangeLogClient.UpdateRange(dtos, token), token);
return result;
}
/// <inheritdoc/>
public async Task<int> Delete(Guid id, CancellationToken token)
{
var result = await ExecutePostResponse(
async () => await refitChangeLogClient.Delete(id, token), token);
return result;
}
/// <inheritdoc/>
public async Task<int> DeleteRange(IEnumerable<Guid> ids, CancellationToken token)
{
var result = await ExecutePostResponse(
async () => await refitChangeLogClient.DeleteRange(ids, token), token);
async () => await refitChangeLogClient.DeleteRange(ids, comment, token), token);
return result;
}

View File

@ -9,48 +9,34 @@ namespace DD.Persistence.Client.Clients.Interfaces;
/// </summary>
public interface IChangeLogClient : IDisposable
{
/// <summary>
/// Добавить одну запись
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token);
/// <summary>
/// Добавить несколько записей
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<int> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Импорт с заменой: удаление старых строк и добавление новых
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Удалить одну запись
/// </summary>
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Delete(Guid id, CancellationToken token);
Task<int> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Удалить несколько записей
/// </summary>
/// <param name="ids"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> DeleteRange(IEnumerable<Guid> ids, CancellationToken token);
Task<int> DeleteRange(IEnumerable<Guid> ids, string comment, CancellationToken token);
/// <summary>
/// Получение актуальных данных на определенную дату (с пагинацией)
@ -80,19 +66,12 @@ public interface IChangeLogClient : IDisposable
/// <returns></returns>
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
/// <summary>
/// Обновить одну запись
/// </summary>
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> Update(ChangeLogValuesDto dto, CancellationToken token);
/// <summary>
/// Обновить несколько записей
/// </summary>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<int> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
}

View File

@ -16,7 +16,7 @@ public interface IRefitChangeLogClient : IRefitClient, IDisposable
/// Импорт с заменой: удаление старых строк и добавление новых
/// </summary>
[Post($"{BaseRoute}/replace/{{idDiscriminator}}")]
Task<IApiResponse<int>> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<IApiResponse<int>> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Получение актуальных данных на определенную дату (с пагинацией)
@ -34,41 +34,23 @@ public interface IRefitChangeLogClient : IRefitClient, IDisposable
[Get($"{BaseRoute}/history/{{idDiscriminator}}")]
Task<IApiResponse<IEnumerable<ChangeLogDto>>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
/// <summary>
/// Добавить одну запись
/// </summary>
[Post($"{BaseRoute}/{{idDiscriminator}}")]
Task<IApiResponse<int>> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token);
/// <summary>
/// Добавить несколько записей
/// </summary>
[Post($"{BaseRoute}/range/{{idDiscriminator}}")]
Task<IApiResponse<int>> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Обновить одну запись
/// </summary>
[Put($"{BaseRoute}")]
Task<IApiResponse<int>> Update(ChangeLogValuesDto dto, CancellationToken token);
[Post($"{BaseRoute}/{{idDiscriminator}}")]
Task<IApiResponse<int>> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Обновить несколько записей
/// </summary>
[Put($"{BaseRoute}/range")]
Task<IApiResponse<int>> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Удалить одну запись
/// </summary>
[Delete($"{BaseRoute}")]
Task<IApiResponse<int>> Delete(Guid id, CancellationToken token);
[Put($"{BaseRoute}")]
Task<IApiResponse<int>> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Удалить несколько записей
/// </summary>
[Delete($"{BaseRoute}/range")]
Task<IApiResponse<int>> DeleteRange([Body] IEnumerable<Guid> ids, CancellationToken token);
[Delete($"{BaseRoute}")]
Task<IApiResponse<int>> DeleteRange([Body] IEnumerable<Guid> ids, string comment, CancellationToken token);
/// <summary>
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)

View File

@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace DD.Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistencePostgresContext))]
[Migration("20250210055116_Init")]
[Migration("20250221053248_Init")]
partial class Init
{
/// <inheritdoc />
@ -41,18 +41,18 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
b.Property<Guid>("IdCreatedCommit")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");
.HasComment("Id коммита на создание записи");
b.Property<Guid?>("IdNext")
.HasColumnType("uuid")
.HasComment("Id заменяющей записи");
b.Property<Guid?>("IdObsoletedCommit")
.HasColumnType("uuid")
.HasComment("Id коммита на устаревание записи");
b.Property<DateTimeOffset?>("Obsolete")
.HasColumnType("timestamp with time zone")
.HasComment("Дата устаревания (например при удалении)");
@ -64,9 +64,38 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Id");
b.HasIndex("IdCreatedCommit");
b.HasIndex("IdObsoletedCommit");
b.ToTable("change_log");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id коммита");
b.Property<string>("Comment")
.IsRequired()
.HasColumnType("text")
.HasComment("Комментарий к коммиту");
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания коммита");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Пользователь, создавший коммит");
b.HasKey("Id");
b.ToTable("change_log_commit");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
{
b.Property<Guid>("SystemId")
@ -214,6 +243,23 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.ToTable("timestamped_values");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
{
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "CreatedCommit")
.WithMany("ChangeLogCreatedItems")
.HasForeignKey("IdCreatedCommit")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "ObsoletedCommit")
.WithMany("ChangeLogObsoletedItems")
.HasForeignKey("IdObsoletedCommit");
b.Navigation("CreatedCommit");
b.Navigation("ObsoletedCommit");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
@ -224,6 +270,13 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.Navigation("System");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
{
b.Navigation("ChangeLogCreatedItems");
b.Navigation("ChangeLogObsoletedItems");
});
#pragma warning restore 612, 618
}
}

View File

@ -13,21 +13,17 @@ namespace DD.Persistence.Database.Postgres.Migrations
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "change_log",
name: "change_log_commit",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Автор изменения"),
IdEditor = table.Column<Guid>(type: "uuid", nullable: true, comment: "Редактор"),
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),
Obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "Дата устаревания (например при удалении)"),
IdNext = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
Value = table.Column<string>(type: "jsonb", nullable: false, comment: "Значение")
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id коммита"),
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Пользователь, создавший коммит"),
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания коммита"),
Comment = table.Column<string>(type: "text", nullable: false, comment: "Комментарий к коммиту")
},
constraints: table =>
{
table.PrimaryKey("PK_change_log", x => x.Id);
table.PrimaryKey("PK_change_log_commit", x => x.Id);
});
migrationBuilder.CreateTable(
@ -98,6 +94,35 @@ namespace DD.Persistence.Database.Postgres.Migrations
table.PrimaryKey("PK_timestamped_values", x => new { x.DiscriminatorId, x.Timestamp });
});
migrationBuilder.CreateTable(
name: "change_log",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),
Obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "Дата устаревания (например при удалении)"),
IdNext = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
Value = table.Column<string>(type: "jsonb", nullable: false, comment: "Значение"),
IdCreatedCommit = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id коммита на создание записи"),
IdObsoletedCommit = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id коммита на устаревание записи")
},
constraints: table =>
{
table.PrimaryKey("PK_change_log", x => x.Id);
table.ForeignKey(
name: "FK_change_log_change_log_commit_IdCreatedCommit",
column: x => x.IdCreatedCommit,
principalTable: "change_log_commit",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_change_log_change_log_commit_IdObsoletedCommit",
column: x => x.IdObsoletedCommit,
principalTable: "change_log_commit",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "tech_message",
columns: table => new
@ -120,6 +145,16 @@ namespace DD.Persistence.Database.Postgres.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_change_log_IdCreatedCommit",
table: "change_log",
column: "IdCreatedCommit");
migrationBuilder.CreateIndex(
name: "IX_change_log_IdObsoletedCommit",
table: "change_log",
column: "IdObsoletedCommit");
migrationBuilder.CreateIndex(
name: "IX_tech_message_SystemId",
table: "tech_message",
@ -147,6 +182,9 @@ namespace DD.Persistence.Database.Postgres.Migrations
migrationBuilder.DropTable(
name: "timestamped_values");
migrationBuilder.DropTable(
name: "change_log_commit");
migrationBuilder.DropTable(
name: "data_source_system");
}

View File

@ -38,18 +38,18 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
b.Property<Guid>("IdCreatedCommit")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");
.HasComment("Id коммита на создание записи");
b.Property<Guid?>("IdNext")
.HasColumnType("uuid")
.HasComment("Id заменяющей записи");
b.Property<Guid?>("IdObsoletedCommit")
.HasColumnType("uuid")
.HasComment("Id коммита на устаревание записи");
b.Property<DateTimeOffset?>("Obsolete")
.HasColumnType("timestamp with time zone")
.HasComment("Дата устаревания (например при удалении)");
@ -61,9 +61,38 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.HasKey("Id");
b.HasIndex("IdCreatedCommit");
b.HasIndex("IdObsoletedCommit");
b.ToTable("change_log");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id коммита");
b.Property<string>("Comment")
.IsRequired()
.HasColumnType("text")
.HasComment("Комментарий к коммиту");
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания коммита");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Пользователь, создавший коммит");
b.HasKey("Id");
b.ToTable("change_log_commit");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
{
b.Property<Guid>("SystemId")
@ -211,6 +240,23 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.ToTable("timestamped_values");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
{
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "CreatedCommit")
.WithMany("ChangeLogCreatedItems")
.HasForeignKey("IdCreatedCommit")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "ObsoletedCommit")
.WithMany("ChangeLogObsoletedItems")
.HasForeignKey("IdObsoletedCommit");
b.Navigation("CreatedCommit");
b.Navigation("ObsoletedCommit");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
@ -221,6 +267,13 @@ namespace DD.Persistence.Database.Postgres.Migrations
b.Navigation("System");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
{
b.Navigation("ChangeLogCreatedItems");
b.Navigation("ChangeLogObsoletedItems");
});
#pragma warning restore 612, 618
}
}

View File

@ -45,6 +45,7 @@ public static class DependencyInjection
//services.AddTransient(typeof(PersistenceRepository<TimestampedValues>));
services.AddTransient<ISetpointRepository, SetpointRepository>();
services.AddTransient<IChangeLogCommitRepository, ChangeLogCommitRepository>();
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();

View File

@ -1,6 +1,5 @@
using DD.Persistence.Database.EntityAbstractions;
using DD.Persistence.ModelsAbstractions;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@ -19,15 +18,15 @@ public class ChangeLog : IDiscriminatorItem, IChangeLog
[Comment("Дискриминатор таблицы")]
public Guid DiscriminatorId { get; set; }
[Comment("Автор изменения")]
public Guid IdAuthor { get; set; }
[Comment("Редактор")]
public Guid? IdEditor { get; set; }
/// <summary>
/// то же самое поле присутствует в связанной таблице change_log_commit (поле creation)
/// </summary>
[Comment("Дата создания записи")]
public DateTimeOffset Creation { get; set; }
/// <summary>
/// то же самое поле присутствует в связанной таблице change_log_commit (поле creation)
/// </summary>
[Comment("Дата устаревания (например при удалении)")]
public DateTimeOffset? Obsolete { get; set; }
@ -36,4 +35,22 @@ public class ChangeLog : IDiscriminatorItem, IChangeLog
[Column(TypeName = "jsonb"), Comment("Значение")]
public required IDictionary<string, object> Value { get; set; }
[Required, Comment("Id коммита на создание записи")]
public Guid IdCreatedCommit { get; set; }
[Comment("Id коммита на устаревание записи")]
public Guid? IdObsoletedCommit { get; set; }
/// <summary>
/// коммит для актуальной записи
/// </summary>
[Required, ForeignKey(nameof(IdCreatedCommit)), Comment("Коммит пользователя на создание записи")]
public virtual ChangeLogCommit CreatedCommit { get; set; } = null!;
/// <summary>
/// коммит для устаревшей записи
/// </summary>
[ForeignKey(nameof(IdObsoletedCommit)), Comment("Коммит пользователя на устаревание записи")]
public virtual ChangeLogCommit? ObsoletedCommit { get; set; }
}

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.Entity;
/// <summary>
/// Таблица c коммитами пользователей
/// </summary>
[Table("change_log_commit")]
public class ChangeLogCommit
{
[Key, Comment("Id коммита")]
public Guid Id { get; set; }
[Comment("Пользователь, создавший коммит")]
public Guid IdAuthor { get; set; }
[Comment("Дата создания коммита")]
public DateTimeOffset Creation { get; set; }
[Comment("Комментарий к коммиту")]
public required string Comment { get; set; }
[Required, InverseProperty(nameof(ChangeLog.CreatedCommit)), Comment("Записи, добавленные в журнал изменений")]
public virtual ICollection<ChangeLog> ChangeLogCreatedItems { get; set; } = null!;
[InverseProperty(nameof(ChangeLog.ObsoletedCommit)), Comment("Устаревшие записи в журнале изменений")]
public virtual ICollection<ChangeLog>? ChangeLogObsoletedItems { get; set; } = null!;
}

View File

@ -10,16 +10,6 @@ public interface IChangeLog
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Автор изменения
/// </summary>
public Guid IdAuthor { get; set; }
/// <summary>
/// Редактор
/// </summary>
public Guid? IdEditor { get; set; }
/// <summary>
/// Дата создания записи
/// </summary>

View File

@ -16,6 +16,8 @@ public class PersistenceDbContext : DbContext
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
public DbSet<ChangeLogCommit> ChangeLogCommit => Set<ChangeLogCommit>();
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
public DbSet<ParameterData> ParameterData => Set<ParameterData>();

View File

@ -0,0 +1,41 @@
using DD.Persistence.Database.Entity;
using DD.Persistence.Models.Requests;
using DD.Persistence.Repositories;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UuidExtensions;
namespace DD.Persistence.Database.Repositories;
public class ChangeLogCommitRepository : IChangeLogCommitRepository
{
private DbContext db;
public ChangeLogCommitRepository(DbContext db)
{
this.db = db;
}
public async Task<ChangeLogCommitDto> Add(CreateChangeLogCommitRequest commitRequest, CancellationToken token)
{
var commit = new ChangeLogCommit()
{
Id = Uuid7.Guid(),
IdAuthor = commitRequest.IdAuthor,
Comment = commitRequest.Comment ?? string.Empty,
Creation = commitRequest.Creation,
};
db.Add(commit);
await db.SaveChangesAsync();
var commitDto = commit.Adapt<ChangeLogCommitDto>();
return commitDto;
}
}

View File

@ -6,6 +6,7 @@ using DD.Persistence.Models.Requests;
using DD.Persistence.Repositories;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using UuidExtensions;
namespace DD.Persistence.Database.Repositories;
@ -18,12 +19,20 @@ public class ChangeLogRepository : IChangeLogRepository
this.db = db;
}
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
public async Task<int> AddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var entities = new List<ChangeLog>();
foreach (var dto in dtos)
foreach (var values in dtos)
{
var entity = CreateEntityFromDto(idAuthor, idDiscriminator, dto);
var entity = new ChangeLog()
{
Id = Uuid7.Guid(),
Creation = commitDto.Creation,
DiscriminatorId = idDiscriminator,
Value = values.Value,
IdCreatedCommit = commitDto.Id,
};
entities.Add(entity);
}
db.Set<ChangeLog>().AddRange(entities);
@ -33,7 +42,7 @@ public class ChangeLogRepository : IChangeLogRepository
return result;
}
public async Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token)
public async Task<int> MarkAsDeleted(IEnumerable<Guid> ids, ChangeLogCommitDto commit, CancellationToken token)
{
var query = db.Set<ChangeLog>()
.Where(s => ids.Contains(s.Id))
@ -46,12 +55,16 @@ public class ChangeLogRepository : IChangeLogRepository
var entities = await query.ToArrayAsync(token);
var result = await MarkAsObsolete(idEditor, entities, token);
return result;
foreach (var entity in entities)
{
entity.Obsolete = commit.Creation;
entity.IdObsoletedCommit = commit.Id;
}
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
return await db.SaveChangesAsync(token);
}
public async Task<int> MarkAsDeleted(Guid idDiscriminator, ChangeLogCommitDto commit, CancellationToken token)
{
var query = db.Set<ChangeLog>()
.Where(s => s.DiscriminatorId == idDiscriminator)
@ -59,40 +72,34 @@ public class ChangeLogRepository : IChangeLogRepository
var entities = await query.ToArrayAsync(token);
var result = await MarkAsObsolete(idEditor, entities, token);
return result;
}
private async Task<int> MarkAsObsolete(Guid idEditor, IEnumerable<ChangeLog> entities, CancellationToken token)
{
var updateTime = DateTimeOffset.UtcNow;
foreach (var entity in entities)
{
entity.Obsolete = updateTime;
entity.IdEditor = idEditor;
entity.Obsolete = commit.Creation;
entity.DiscriminatorId = commit.Id;
}
return await db.SaveChangesAsync(token);
}
public async Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
public async Task<int> ClearAndAddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var result = 0;
var changeLogIds = dtos.Select(c => c.Id);
var comment = commitDto.Comment;
using var transaction = await db.Database.BeginTransactionAsync(token);
result += await MarkAsDeleted(idAuthor, idDiscriminator, token);
result += await AddRange(idAuthor, idDiscriminator, dtos, token);
result += await MarkAsDeleted(idDiscriminator, commitDto, token);
result += await AddRange(idDiscriminator, commitDto, dtos, token);
await transaction.CommitAsync(token);
return result;
}
public async Task<int> UpdateRange(Guid idEditor, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
public async Task<int> UpdateRange(ChangeLogCommitDto commitDto, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
{
var dbSet = db.Set<ChangeLog>();
@ -101,7 +108,6 @@ public class ChangeLogRepository : IChangeLogRepository
.Where(s => updatedIds.Contains(s.Id))
.ToDictionary(s => s.Id);
var result = 0;
using var transaction = await db.Database.BeginTransactionAsync(token);
foreach (var dto in dtos)
@ -112,20 +118,25 @@ public class ChangeLogRepository : IChangeLogRepository
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
}
var newEntity = CreateEntityFromDto(idEditor, updatedEntity.DiscriminatorId, dto);
var newEntity = new ChangeLog()
{
Id = Uuid7.Guid(),
Creation = commitDto.Creation,
DiscriminatorId = updatedEntity.DiscriminatorId,
Value = dto.Value,
IdCreatedCommit = commitDto.Id,
};
dbSet.Add(newEntity);
updatedEntity.IdNext = newEntity.Id;
updatedEntity.Obsolete = DateTimeOffset.UtcNow;
updatedEntity.IdEditor = idEditor;
updatedEntity.Obsolete = commitDto.Creation;
updatedEntity.IdObsoletedCommit = commitDto.Id;
}
result = await db.SaveChangesAsync(token);
var result = await db.SaveChangesAsync(token);
await transaction.CommitAsync(token);
return result;
}
public async Task<PaginationContainer<ChangeLogValuesDto>> GetByDate(
@ -195,22 +206,6 @@ public class ChangeLogRepository : IChangeLogRepository
return datesOnly;
}
private static ChangeLog CreateEntityFromDto(Guid idAuthor, Guid idDiscriminator, ChangeLogValuesDto dto)
{
var entity = new ChangeLog()
{
Id = Uuid7.Guid(),
Creation = DateTimeOffset.UtcNow,
IdAuthor = idAuthor,
DiscriminatorId = idDiscriminator,
IdEditor = idAuthor,
Value = dto.Value
};
return entity;
}
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
{
var date = dateBegin.ToUniversalTime();

View File

@ -15,6 +15,7 @@ namespace DD.Persistence.IntegrationTests.Controllers;
public class ChangeLogControllerTest : BaseIntegrationTest
{
private readonly IChangeLogClient client;
private readonly PaginationRequest paginationRequest;
private static readonly Random generatorRandomDigits = new();
public ChangeLogControllerTest(WebAppFactoryFixture factory) : base(factory)
@ -25,22 +26,13 @@ public class ChangeLogControllerTest : BaseIntegrationTest
client = scope.ServiceProvider
.GetRequiredService<IChangeLogClient>();
}
[Fact]
public async Task ClearAndInsertRange_InEmptyDb()
paginationRequest = new PaginationRequest()
{
// arrange
dbContext.CleanupDbSet<ChangeLog>();
var idDiscriminator = Guid.NewGuid();
var dtos = Generate(2);
// act
var result = await client.ClearAndAddRange(idDiscriminator, dtos, new CancellationToken());
// assert
Assert.Equal(2, result);
Skip = 0,
Take = 10,
SortSettings = String.Empty,
};
}
[Fact]
@ -48,31 +40,24 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{
// arrange
var insertedCount = 10;
var createdResult = CreateChangeLogItems(insertedCount, (-15, 15));
var idDiscriminator = createdResult.Item1;
var dtos = createdResult.Item2.Select(e => e.Adapt<ChangeLogValuesDto>());
var newEntitiesData = await CreateAndReturnNewDtos(insertedCount, (-15, -1));
var idDiscriminator = newEntitiesData.Item1;
var dtos = newEntitiesData.Item2;
// act
var result = await client.ClearAndAddRange(idDiscriminator, dtos, new CancellationToken());
var newEntitiesData2 = await CreateAndReturnNewDtos(insertedCount, (-15, -1));
var idDiscriminator2 = newEntitiesData2.Item1;
var dtos2 = newEntitiesData2.Item2;
//act
var result = await client.ClearAndAddRange(idDiscriminator, dtos, "Добавление новых элементов и очистка старых", CancellationToken.None);
var changeLogActualWithIdDiscriminator = await client.GetByDate(idDiscriminator2, DateTimeOffset.UtcNow, paginationRequest, CancellationToken.None);
var changeLogActualWithIdDiscriminator2 = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow, paginationRequest, CancellationToken.None);
// assert
Assert.Equal(insertedCount * 2, result);
}
Assert.Equal(insertedCount, changeLogActualWithIdDiscriminator.Count);
Assert.Equal(insertedCount, changeLogActualWithIdDiscriminator2.Count);
[Fact]
public async Task Add_returns_success()
{
// arrange
var count = 1;
var idDiscriminator = Guid.NewGuid();
var dtos = Generate(count);
var dto = dtos.FirstOrDefault()!;
// act
var result = await client.Add(idDiscriminator, dto, new CancellationToken());
// assert
Assert.Equal(count, result);
}
[Fact]
@ -82,9 +67,10 @@ public class ChangeLogControllerTest : BaseIntegrationTest
var count = 3;
var idDiscriminator = Guid.NewGuid();
var dtos = Generate(count);
var comment = "Создаю 3 элемента";
// act
var result = await client.AddRange(idDiscriminator, dtos, new CancellationToken());
var result = await client.AddRange(idDiscriminator, dtos, comment, CancellationToken.None);
// assert
Assert.Equal(count, result);
@ -93,13 +79,14 @@ public class ChangeLogControllerTest : BaseIntegrationTest
[Fact]
public async Task Update_returns_success()
{
// arrange
//arrange
dbContext.CleanupDbSet<ChangeLog>();
var idDiscriminator = Guid.NewGuid();
var dtos = Generate(1);
var dto = dtos.FirstOrDefault()!;
var result = await client.Add(idDiscriminator, dto, new CancellationToken());
var comment = "Создаю 1 элемент";
var result = await client.AddRange(idDiscriminator, [dto], comment, CancellationToken.None);
var entity = dbContext.ChangeLog
.Where(x => x.DiscriminatorId == idDiscriminator)
@ -107,7 +94,8 @@ public class ChangeLogControllerTest : BaseIntegrationTest
dto = entity.Adapt<ChangeLogValuesDto>();
// act
result = await client.Update(dto, new CancellationToken());
comment = "Обновляю 1 элемент";
result = await client.UpdateRange([dto], comment, CancellationToken.None);
// assert
Assert.Equal(2, result);
@ -139,71 +127,61 @@ public class ChangeLogControllerTest : BaseIntegrationTest
[Fact]
public async Task UpdateRange_returns_success()
{
// arrange
var count = 2;
var idDiscriminator = Guid.NewGuid();
var dtos = Generate(count);
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
dbContext.ChangeLog.AddRange(entities);
dbContext.SaveChanges();
dtos = entities.Select(c => new ChangeLogValuesDto()
{
Id = c.Id,
Value = c.Value
}).ToArray();
var comment = "Создаю 3 элемента";
// act
var result = await client.UpdateRange(dtos, new CancellationToken());
var result = await client.AddRange(idDiscriminator, dtos, comment, CancellationToken.None);
var paginatedResult = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow.AddDays(1), paginationRequest, CancellationToken.None);
// act
comment = "Обновляю 3 элемента";
result = await client.UpdateRange(paginatedResult.Items, comment, CancellationToken.None);
// assert
Assert.Equal(count * 2, result);
}
[Fact]
public async Task Delete_returns_success()
{
// arrange
var dtos = Generate(1);
var dto = dtos.FirstOrDefault()!;
var entity = dto.Adapt<ChangeLog>();
dbContext.ChangeLog.Add(entity);
dbContext.SaveChanges();
// act
var result = await client.Delete(entity.Id, new CancellationToken());
// assert
Assert.Equal(1, result);
}
[Fact]
public async Task DeleteRange_returns_success()
{
// arrange
var count = 10;
var dtos = Generate(count);
var entities = dtos.Select(d => d.Adapt<ChangeLog>()).ToArray();
dbContext.ChangeLog.AddRange(entities);
dbContext.SaveChanges();
var insertedCount = 10;
var newEntitiesData = await CreateAndReturnNewDtos(insertedCount, (-15, -1));
var idDiscriminator = newEntitiesData.Item1;
var dtos = newEntitiesData.Item2;
var newEntitiesData2 = await CreateAndReturnNewDtos(insertedCount, (-15, -1));
var idDiscriminator2 = newEntitiesData2.Item1;
var dtos2 = newEntitiesData2.Item2;
// act
var ids = entities.Select(e => e.Id);
var result = await client.DeleteRange(ids, new CancellationToken());
var ids = dtos.Select(e => e.Id);
var result = await client.DeleteRange(ids, "Удаление нескольких записей", CancellationToken.None);
// assert
Assert.Equal(count, result);
Assert.Equal(insertedCount, result);
// act
var actualWithIdDescriminator = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow, paginationRequest, CancellationToken.None);
var actualWithIdDescriminator2 = await client.GetByDate(idDiscriminator2, DateTimeOffset.UtcNow, paginationRequest, CancellationToken.None);
// assert
Assert.Equal(0, actualWithIdDescriminator.Count);
Assert.Equal(insertedCount, actualWithIdDescriminator2.Count);
}
[Fact]
public async Task GetDatesRange_returns_success()
{
// arrange
var changeLogItems = CreateChangeLogItems(3, (-15, 15));
//arrange
var changeLogItems = await CreateAndReturnNewEntities(3, (-15, -1));
var idDiscriminator = changeLogItems.Item1;
var entities = changeLogItems.Item2.OrderBy(e => e.Creation);
var entities = changeLogItems.Item2.OrderBy(c => c.Creation);
// act
var result = await client.GetDatesRange(idDiscriminator, new CancellationToken());
var result = await client.GetDatesRange(idDiscriminator, CancellationToken.None);
// assert
Assert.NotNull(result);
@ -228,7 +206,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
//создаем записи
var count = 5;
var changeLogItems = CreateChangeLogItems(count, (-15, 15));
var changeLogItems = await CreateAndReturnNewDtos(count, (-15, -1));
var idDiscriminator = changeLogItems.Item1;
var entities = changeLogItems.Item2;
@ -237,14 +215,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
var ids = entities.Select(e => e.Id);
var idsToDelete = ids.Skip(2);
var deletedCount = await client.DeleteRange(idsToDelete, new CancellationToken());
var paginationRequest = new PaginationRequest()
{
Skip = 0,
Take = 10,
SortSettings = String.Empty,
};
var deletedCount = await client.DeleteRange(idsToDelete, "Удаление нескольких записей", CancellationToken.None);
var moment = DateTimeOffset.UtcNow.AddDays(16);
var result = await client.GetByDate(idDiscriminator, moment, paginationRequest, new CancellationToken());
@ -260,8 +231,8 @@ public class ChangeLogControllerTest : BaseIntegrationTest
}
[Theory]
[InlineData(5, -15, 15, -20, 20, 10)]
[InlineData(5, -15, -10, -16, -9, 5)]
[InlineData(5, -15, -5, -20, 20, 10)]
[InlineData(5, -15, -10, -16, 9, 10)]
public async Task GetChangeLogForInterval_returns_success(
int insertedCount,
int daysBeforeNowChangeLog,
@ -276,17 +247,16 @@ public class ChangeLogControllerTest : BaseIntegrationTest
//создаем записи
var count = insertedCount;
var daysRange = (daysBeforeNowChangeLog, daysAfterNowChangeLog);
var changeLogItems = CreateChangeLogItems(count, daysRange);
var changeLogItems = await CreateAndReturnNewDtos(count, daysRange);
var idDiscriminator = changeLogItems.Item1;
var entities = changeLogItems.Item2;
var dtos = changeLogItems.Item2;
var dtos = entities.Select(e => e.Adapt<ChangeLogValuesDto>()).ToArray();
await client.UpdateRange(dtos, new CancellationToken());
await client.UpdateRange(dtos, "Обновляем несколько записей", CancellationToken.None);
//act
var dateBegin = DateTimeOffset.UtcNow.AddDays(daysBeforeNowFilter);
var dateEnd = DateTimeOffset.UtcNow.AddDays(daysAfterNowFilter);
var result = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, new CancellationToken());
var result = await client.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, CancellationToken.None);
//assert
Assert.NotNull(result);
@ -308,7 +278,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
}
private (Guid, ChangeLog[]) CreateChangeLogItems(int count, (int, int) daysRange)
private async Task<(Guid, IEnumerable<ChangeLogValuesDto>)> CreateAndReturnNewDtos(int count, (int, int) daysRange)
{
var minDayCount = daysRange.Item1;
var maxDayCount = daysRange.Item2;
@ -323,8 +293,43 @@ public class ChangeLogControllerTest : BaseIntegrationTest
return entity;
}).ToArray();
dtos = entities.Select(e => e.Adapt<ChangeLogValuesDto>());
// act
var result = await client.AddRange(idDiscriminator, dtos, "Добавление элементов", CancellationToken.None);
var paginatedResult = await client.GetByDate(idDiscriminator, DateTimeOffset.UtcNow.AddDays(1), paginationRequest, CancellationToken.None);
return (idDiscriminator, paginatedResult.Items);
}
private async Task<(Guid, IEnumerable<ChangeLog>)> CreateAndReturnNewEntities(int count, (int, int) daysRange)
{
var commit = new ChangeLogCommit()
{
Comment = "Комментарий к коммиту",
Creation = DateTimeOffset.UtcNow,
Id = Guid.NewGuid(),
};
dbContext.ChangeLogCommit.Add(commit);
await dbContext.SaveChangesAsync();
var minDayCount = daysRange.Item1;
var maxDayCount = daysRange.Item2;
Guid idDiscriminator = Guid.NewGuid();
var dtos = Generate(count);
var entities = dtos.Select(d =>
{
var entity = d.Adapt<ChangeLog>();
entity.DiscriminatorId = idDiscriminator;
entity.Creation = DateTimeOffset.UtcNow.AddDays(generatorRandomDigits.Next(minDayCount, maxDayCount));
entity.IdCreatedCommit = commit.Id;
return entity;
}).ToArray();
dbContext.ChangeLog.AddRange(entities);
dbContext.SaveChanges();
await dbContext.SaveChangesAsync();
return (idDiscriminator, entities);
}

View File

@ -0,0 +1,30 @@
namespace DD.Persistence.Models.Requests;
/// <summary>
/// Модель коммита с изменениями
/// </summary>
public class ChangeLogCommitDto : CreateChangeLogCommitRequest
{
/// <summary>
///
/// </summary>
public ChangeLogCommitDto()
{
}
/// <summary>
/// ctor
/// </summary>
/// <param name="idAuthor"></param>
/// <param name="comment"></param>
public ChangeLogCommitDto(Guid idAuthor, string? comment) : base(idAuthor, comment)
{
}
/// <summary>
/// Id
/// </summary>
public Guid Id { get; set; }
}

View File

@ -0,0 +1,41 @@
namespace DD.Persistence.Models.Requests;
/// <summary>
/// Модель для создания коммита
/// </summary>
public class CreateChangeLogCommitRequest
{
/// <summary>
/// Дата создания
/// </summary>
public DateTimeOffset Creation { get; set; }
/// <summary>
/// Пользователь, совершающий коммит
/// </summary>
public Guid IdAuthor { get; set; }
/// <summary>
/// Комментарий
/// </summary>
public string? Comment { get; set; }
/// <summary>
///
/// </summary>
public CreateChangeLogCommitRequest()
{
}
/// <summary>
///
/// </summary>
public CreateChangeLogCommitRequest(Guid idAuthor, string? comment)
{
IdAuthor = idAuthor;
Comment = comment ?? string.Empty;
Creation = DateTimeOffset.UtcNow;
}
}

View File

@ -11,8 +11,8 @@
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Shouldly" Version="4.2.1" />
<PackageReference Include="Testcontainers" Version="4.1.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.1.0" />
<PackageReference Include="Testcontainers" Version="4.2.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.2.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />

View File

@ -0,0 +1,274 @@
using DD.Persistence.API.Services;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Models.Requests;
using DD.Persistence.Repositories;
using Microsoft.Extensions.Caching.Memory;
using NSubstitute;
using UuidExtensions;
namespace DD.Persistence.Test;
public class ChangeLogTest
{
private readonly IChangeLogCommitRepository changeLogCommitRepository = Substitute.For<IChangeLogCommitRepository>();
private readonly IChangeLogRepository changeLogRepository = Substitute.For<IChangeLogRepository>();
private ChangeLogService service;
public ChangeLogTest()
{
var memoryCache = new MemoryCache(new MemoryCacheOptions());
service = new ChangeLogService(memoryCache, changeLogCommitRepository, changeLogRepository);
}
[Fact]
public async Task AddRange()
{
//arrange
var discriminatorId = Uuid7.Guid();
var commitRequest = new CreateChangeLogCommitRequest(Uuid7.Guid(), "Добавление нескольких значений");
var commit = new ChangeLogCommitDto(commitRequest.IdAuthor, commitRequest.Comment);
commit.Id = Uuid7.Guid();
var dtos = GenerateChangeLogValuesDto(2);
changeLogCommitRepository.Add(Arg.Any<CreateChangeLogCommitRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
changeLogRepository
.AddRange(
Arg.Any<Guid>(),
Arg.Any<ChangeLogCommitDto>(),
Arg.Any<IEnumerable<ChangeLogValuesDto>>(),
Arg.Any<CancellationToken>())
.Returns(2);
//act
var addRangeResult = await service
.AddRange(discriminatorId, commitRequest, dtos, CancellationToken.None);
addRangeResult = await service
.AddRange(discriminatorId, commitRequest, dtos, CancellationToken.None);
//assert
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
await changeLogRepository.Received(2).AddRange(discriminatorId, Arg.Any<ChangeLogCommitDto>(), dtos, CancellationToken.None);
}
[Fact]
public async Task UpdateRange()
{
//arrange
var discriminatorId = Uuid7.Guid();
var commitRequest = new CreateChangeLogCommitRequest(Uuid7.Guid(), "Изменение нескольких значений");
var commit = new ChangeLogCommitDto(commitRequest.IdAuthor, commitRequest.Comment);
commit.Id = Uuid7.Guid();
var dtos = GenerateChangeLogValuesDto(2);
changeLogCommitRepository.Add(Arg.Any<CreateChangeLogCommitRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
changeLogRepository
.UpdateRange(
Arg.Any<ChangeLogCommitDto>(),
Arg.Any<IEnumerable<ChangeLogValuesDto>>(),
Arg.Any<CancellationToken>())
.Returns(2);
//act
var updateRangeResult = await service
.UpdateRange(commitRequest, dtos, CancellationToken.None);
updateRangeResult = await service
.UpdateRange(commitRequest, dtos, CancellationToken.None);
updateRangeResult = await service
.UpdateRange(commitRequest, dtos, CancellationToken.None);
//assert
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
await changeLogRepository.Received(3).UpdateRange(Arg.Any<ChangeLogCommitDto>(), dtos, CancellationToken.None);
}
[Fact]
public async Task MarkAsDeleted()
{
//arrange
var discriminatorId = Uuid7.Guid();
var commitRequest = new CreateChangeLogCommitRequest(Uuid7.Guid(), "Удаление нескольких значений");
var commit = new ChangeLogCommitDto(commitRequest.IdAuthor, commitRequest.Comment);
commit.Id = Uuid7.Guid();
var dtos = GenerateChangeLogValuesDto(2);
var dtoIds = dtos.Select(d => d.Id);
changeLogCommitRepository.Add(Arg.Any<CreateChangeLogCommitRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
changeLogRepository
.MarkAsDeleted(
Arg.Any<IEnumerable<Guid>>(),
Arg.Any<ChangeLogCommitDto>(),
Arg.Any<CancellationToken>())
.Returns(2);
//act
var markAsDeletedResult = await service
.MarkAsDeleted(dtoIds, commitRequest, CancellationToken.None);
markAsDeletedResult = await service
.MarkAsDeleted(dtoIds, commitRequest, CancellationToken.None);
//assert
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
await changeLogRepository.Received(2).MarkAsDeleted(dtoIds, commit, CancellationToken.None);
}
[Fact]
public async Task ClearAndAddRange()
{
//arrange
var discriminatorId = Uuid7.Guid();
var commitRequest = new CreateChangeLogCommitRequest(Uuid7.Guid(), "Удаление и добавление нескольких значений");
var commit = new ChangeLogCommitDto(commitRequest.IdAuthor, commitRequest.Comment);
commit.Id = Uuid7.Guid();
var dtos = GenerateChangeLogValuesDto(2);
var dtoIds = dtos.Select(d => d.Id);
changeLogCommitRepository.Add(Arg.Any<CreateChangeLogCommitRequest>(), Arg.Any<CancellationToken>()).Returns(commit);
changeLogRepository
.ClearAndAddRange(
Arg.Any<Guid>(),
Arg.Any<ChangeLogCommitDto>(),
Arg.Any<IEnumerable<ChangeLogValuesDto>>(),
Arg.Any<CancellationToken>())
.Returns(2);
//act
var clearAndAddResult = await service
.ClearAndAddRange(discriminatorId, commitRequest, dtos, CancellationToken.None);
clearAndAddResult = await service
.ClearAndAddRange(discriminatorId, commitRequest, dtos, CancellationToken.None);
//assert
await changeLogCommitRepository.Received(1).Add(commitRequest, CancellationToken.None);
await changeLogRepository.Received(2).ClearAndAddRange(discriminatorId, Arg.Any<ChangeLogCommitDto>(), dtos, CancellationToken.None);
}
[Fact]
public async Task GetByDate()
{
//arrange
var discriminatorId = Uuid7.Guid();
var paginationRequest = new PaginationRequest()
{
Skip = 0,
Take = 1000
};
var dtos = GenerateChangeLogValuesDto(5);
var items = new PaginationContainer<ChangeLogValuesDto>()
{
Take = paginationRequest.Take,
Skip = paginationRequest.Skip,
Items = dtos,
Count = 10
};
var momentDate = DateTime.UtcNow;
changeLogRepository
.GetByDate(
Arg.Any<Guid>(),
Arg.Any<DateTimeOffset>(),
Arg.Any<PaginationRequest>(),
Arg.Any<CancellationToken>())
.Returns(items);
//act
var actualItems = await service
.GetByDate(discriminatorId, momentDate, paginationRequest, CancellationToken.None);
//assert
await changeLogRepository.Received(1).GetByDate(discriminatorId, momentDate, paginationRequest, CancellationToken.None);
}
[Fact]
public async Task GetChangeLogForInterval()
{
//arrange
var discriminatorId = Uuid7.Guid();
var dtos = GenerateChangeLogDto(5);
var dateBegin = DateTimeOffset.UtcNow.AddDays(-5);
var dateEnd = DateTimeOffset.UtcNow;
changeLogRepository
.GetChangeLogForInterval(
Arg.Any<Guid>(),
Arg.Any<DateTimeOffset>(),
Arg.Any<DateTimeOffset>(),
Arg.Any<CancellationToken>())
.Returns(dtos);
//act
var actualItems = await service
.GetChangeLogForInterval(discriminatorId, dateBegin, dateEnd, CancellationToken.None);
//assert
await changeLogRepository.Received(1).GetChangeLogForInterval(discriminatorId, dateBegin, dateEnd, CancellationToken.None);
}
[Fact]
public async Task GetDatesChange()
{
//arrange
var discriminatorId = Uuid7.Guid();
var dateBegin = DateTimeOffset.UtcNow.AddDays(-5);
var dateEnd = DateTimeOffset.UtcNow;
var dateOnlyBegin = new DateOnly(dateBegin.Year, dateBegin.Month, dateBegin.Day);
var dateOnlyEnd = new DateOnly(dateEnd.Year, dateEnd.Month, dateEnd.Day);
var dtos = new List<DateOnly>() { dateOnlyBegin, dateOnlyEnd };
changeLogRepository
.GetDatesChange(
Arg.Any<Guid>(),
Arg.Any<CancellationToken>())
.Returns(dtos);
//act
var actualItems = await service
.GetDatesChange(discriminatorId, CancellationToken.None);
//assert
await changeLogRepository.Received(1).GetDatesChange(discriminatorId, CancellationToken.None);
}
private IEnumerable<ChangeLogValuesDto> GenerateChangeLogValuesDto(int count)
{
var items = new List<ChangeLogValuesDto>();
for (int i = 0; i < count; i++)
{
items.Add(new ChangeLogValuesDto()
{
Id = Uuid7.Guid(),
Value = new Dictionary<string, object>
{
{ "1", 1 },
{ "2", 2 }
}
});
}
return items;
}
private IEnumerable<ChangeLogDto> GenerateChangeLogDto(int count)
{
var items = new List<ChangeLogDto>();
for (int i = 0; i < count; i++)
{
items.Add(new ChangeLogDto()
{
Id = Uuid7.Guid(),
});
}
return items;
}
}

View File

@ -16,6 +16,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DD.Persistence.API\DD.Persistence.API.csproj" />
<ProjectReference Include="..\DD.Persistence.Database.Postgres\DD.Persistence.Database.Postgres.csproj" />
</ItemGroup>

View File

@ -14,9 +14,10 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<ChangeLogValuesDto>
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dtos"></param>
/// <param name="comment"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<IActionResult> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Получение данных на текущую дату (с пагинацией)
@ -47,55 +48,33 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<ChangeLogValuesDto>
/// <returns></returns>
Task<IActionResult> GetChangeLogForDate(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
/// <summary>
/// Добавить одну запись
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token);
/// <summary>
/// Добавить несколько записей
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dtos"></param>
/// <param name="comment">комментарий</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Обновить одну запись
/// </summary>
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> Update(ChangeLogValuesDto dto, CancellationToken token);
Task<IActionResult> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Обновить несколько записей
/// </summary>
/// <param name="dtos"></param>
/// <param name="comment">комментарий</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Удалить одну запись
/// </summary>
/// <param name="id"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> Delete(Guid id, CancellationToken token);
Task<IActionResult> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
/// <summary>
/// Удалить несколько записей
/// </summary>
/// <param name="ids"></param>
/// <param name="comment">комментарий к удалению</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, CancellationToken token);
Task<IActionResult> DeleteRange(IEnumerable<Guid> ids, string comment, CancellationToken token);
/// <summary>
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)

View File

@ -0,0 +1,17 @@
using DD.Persistence.Models.Requests;
namespace DD.Persistence.Repositories;
/// <summary>
/// Интерфейс для работы с коммитами журнала изменений
/// </summary>
public interface IChangeLogCommitRepository
{
/// <summary>
/// Добавить коммит для журнала изменений
/// </summary>
/// <param name="commitDto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<ChangeLogCommitDto> Add(CreateChangeLogCommitRequest commitDto, CancellationToken token);
}

View File

@ -7,55 +7,54 @@ namespace DD.Persistence.Repositories;
/// <summary>
/// Интерфейс для работы с историческими данными
/// </summary>
/// <typeparam name="TDto"></typeparam>
public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<ChangeLogValuesDto>
{
/// <summary>
/// Добавление записей
/// </summary>
/// <param name="idAuthor">пользователь, который добавляет</param>
/// <param name="idDiscriminator">ключ справочника</param>
/// <param name="dto">коммит с изменениями</param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<int> AddRange(Guid idDiscriminator, ChangeLogCommitDto dto, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Пометить записи как удаленные
/// </summary>
/// <param name="idEditor"></param>
/// <param name="ids">ключи записей</param>
/// <param name="commit"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token);
Task<int> MarkAsDeleted(IEnumerable<Guid> ids, ChangeLogCommitDto commit, CancellationToken token);
/// <summary>
/// Пометить записи как удаленные
/// </summary>
/// <param name="idEditor"></param>
/// <param name="idDiscriminator">дискриминатор таблицы</param>
/// <param name="commit"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token);
Task<int> MarkAsDeleted(Guid idDiscriminator, ChangeLogCommitDto commit, CancellationToken token);
/// <summary>
/// Очистить и добавить новые
/// </summary>
/// <param name="idAuthor"></param>
/// <param name="idDiscriminator"></param>
/// <param name="dto">коммит с изменениями</param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<int> ClearAndAddRange(Guid idDiscriminator, ChangeLogCommitDto dto, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Редактирование записей
/// </summary>
/// <param name="idEditor">пользователь, который редактирует</param>
/// <param name="commitDto">коммит с изменениями</param>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> UpdateRange(Guid idEditor, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
Task<int> UpdateRange(ChangeLogCommitDto commitDto, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
/// <summary>
/// Получение актуальных записей на определенный момент времени (с пагинацией)