using DD.Persistence.Database.Entity; using DD.Persistence.Database.Postgres.Helpers; using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; using DD.Persistence.Repositories; using Mapster; using Microsoft.EntityFrameworkCore; using System; using UuidExtensions; namespace DD.Persistence.Database.Repositories; public class ChangeLogRepository : IChangeLogRepository { private readonly DbContext db; public ChangeLogRepository(DbContext db) { this.db = db; } public async Task AddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, CancellationToken token) { var entities = new List(); foreach (var values in commitDto.ChangeLogItems) { var entity = new ChangeLog() { Id = Uuid7.Guid(), Creation = commitDto.Creation, IdAuthor = commitDto.IdAuthor, IdDiscriminator = idDiscriminator, Value = values.Value, IdCommit = commitDto.Id, }; entities.Add(entity); } db.Set().AddRange(entities); var result = await db.SaveChangesAsync(token); return result; } public async Task MarkAsDeleted(Guid idCommit, IEnumerable ids, DateTimeOffset updateTime, CancellationToken token) { var query = db.Set() .Where(s => ids.Contains(s.Id)) .Where(s => s.Obsolete == null); if (query.Count() != ids.Count()) { throw new ArgumentException("Count of active items not equal count of ids", nameof(ids)); } var entities = await query.ToArrayAsync(token); foreach (var entity in entities) { entity.Obsolete = updateTime; entity.IdDiscriminator = idCommit; } return await db.SaveChangesAsync(token); } public async Task MarkAsDeleted(Guid idDiscriminator, Guid idCommit, DateTimeOffset updateTime, CancellationToken token) { var query = db.Set() .Where(s => s.IdDiscriminator == idDiscriminator) .Where(e => e.Obsolete == null); var entities = await query.ToArrayAsync(token); foreach (var entity in entities) { entity.Obsolete = updateTime; entity.IdDiscriminator = idCommit; } return await db.SaveChangesAsync(token); } public async Task ClearAndAddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, CancellationToken token) { var result = 0; var changeLogIds = commitDto.ChangeLogItems.Select(c => c.Id); var comment = commitDto.Comment; using var transaction = await db.Database.BeginTransactionAsync(token); result += await MarkAsDeleted(commitDto.IdAuthor, changeLogIds, commitDto.Creation, token); result += await AddRange(idDiscriminator, commitDto, token); await transaction.CommitAsync(token); return result; } public async Task UpdateRange(ChangeLogCommitDto commitDto, CancellationToken token) { var dbSet = db.Set(); var updatedIds = commitDto.ChangeLogItems.Select(d => d.Id); var updatedEntities = dbSet .Where(s => updatedIds.Contains(s.Id)) .ToDictionary(s => s.Id); using var transaction = await db.Database.BeginTransactionAsync(token); foreach (var dto in commitDto.ChangeLogItems) { var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id); if (updatedEntity is null) { throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto)); } var newEntity = new ChangeLog() { Id = Uuid7.Guid(), Creation = commitDto.Creation, IdAuthor = commitDto.IdAuthor, IdDiscriminator = updatedEntity.IdDiscriminator, Value = dto.Value, IdCommit = commitDto.Id, }; dbSet.Add(newEntity); updatedEntity.IdNext = newEntity.Id; updatedEntity.Obsolete = commitDto.Creation; } var result = await db.SaveChangesAsync(token); await transaction.CommitAsync(token); return result; } public async Task> GetByDate( Guid idDiscriminator, DateTimeOffset momentUtc, PaginationRequest paginationRequest, CancellationToken token) { var query = CreateQuery(idDiscriminator); query = query.Apply(momentUtc); var result = await query.ApplyPagination(paginationRequest, Convert, token); return result; } private IQueryable CreateQuery(Guid idDiscriminator) { var query = db.Set().Where(e => e.IdDiscriminator == idDiscriminator); return query; } public async Task> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) { var query = db.Set().Where(s => s.IdDiscriminator == idDiscriminator); var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero); var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero); var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max); var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max); query = createdQuery.Union(editedQuery); var entities = await query.ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); return dtos; } public async Task> GetDatesChange(Guid idDiscriminator, CancellationToken token) { var query = db.Set().Where(e => e.IdDiscriminator == idDiscriminator); var datesCreateQuery = query .Select(e => e.Creation) .Distinct(); var datesCreate = await datesCreateQuery.ToArrayAsync(token); var datesUpdateQuery = query .Where(e => e.Obsolete != null) .Select(e => e.Obsolete!.Value) .Distinct(); var datesUpdate = await datesUpdateQuery.ToArrayAsync(token); var dates = datesCreate.Concat(datesUpdate); var datesOnly = dates .Select(d => new DateOnly(d.Year, d.Month, d.Day)) .Distinct() .OrderBy(d => d); return datesOnly; } public async Task> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token) { var date = dateBegin.ToUniversalTime(); var query = db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .Where(e => e.Creation >= date || e.Obsolete >= date); var entities = await query.ToArrayAsync(token); var dtos = entities.Select(Convert); return dtos; } public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) { var query = db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .GroupBy(e => 1) .Select(group => new { Min = group.Min(e => e.Creation), Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation ? e.Obsolete.Value : e.Creation), }); var values = await query.FirstOrDefaultAsync(token); if (values is null) { return null; } return new DatesRangeDto { From = values.Min, To = values.Max, }; } private ChangeLogValuesDto Convert(ChangeLog entity) => entity.Adapt(); }