using Mapster; using Microsoft.EntityFrameworkCore; using Persistence.Database.Model; using Persistence.Models; using Persistence.Models.Requests; using Persistence.Repositories; namespace Persistence.Repository.Repositories; public class ChangeLogRepository : IChangeLogRepository { private DbContext db; public ChangeLogRepository(DbContext db) { this.db = db; } public Task InsertRange(Guid idUser, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { foreach (var dto in dtos) { var entity = CreateEntityFromDto(idUser, idDiscriminator, dto); db.Set().Add(entity); } var result = db.SaveChangesAsync(token); return result; } public async Task MarkAsDeleted(Guid idUser, IEnumerable ids, CancellationToken token) { var query = db.Set().Where(s => ids.Contains(s.Id)); var entities = await query.ToArrayAsync(token); var result = await Clear(idUser, entities, token); return result; } public async Task MarkAsDeleted(Guid idUser, Guid idDiscriminator, CancellationToken token) { var query = db.Set() .Where(s => s.IdDiscriminator == idDiscriminator) .Where(e => e.Obsolete == null); var entities = await query.ToArrayAsync(token); var result = await Clear(idUser, entities, token); return result; } private async Task Clear(Guid idUser, IEnumerable entities, CancellationToken token) { var updateTime = DateTimeOffset.UtcNow; foreach (var entity in entities) { entity.Obsolete = updateTime; entity.IdEditor = idUser; } return await db.SaveChangesAsync(token); } public async Task ClearAndInsertRange(Guid idUser, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { var result = 0; using var transaction = await db.Database.BeginTransactionAsync(token); try { result += await MarkAsDeleted(idUser, idDiscriminator, token); result += await InsertRange(idUser, idDiscriminator, dtos, token); await transaction.CommitAsync(token); return result; } catch { await transaction.RollbackAsync(token); throw; } } public async Task UpdateRange(Guid idUser, Guid idDiscriminator, IEnumerable dtos, CancellationToken token) { var dbSet = db.Set(); var updatedIds = dtos.Select(d => d.Id); var updatedEntities = dbSet .Where(s => updatedIds.Contains(s.Id)) .ToDictionary(s => s.Id); var result = 0; using var transaction = await db.Database.BeginTransactionAsync(token); try { foreach (var dto in dtos) { var newEntity = CreateEntityFromDto(idUser, idDiscriminator, dto); dbSet.Add(newEntity); var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id)!; updatedEntity.IdNext = newEntity.Id; updatedEntity.Obsolete = DateTimeOffset.UtcNow; updatedEntity.IdEditor = idUser; } await db.SaveChangesAsync(token); await transaction.CommitAsync(token); return result; } catch { await transaction.RollbackAsync(token); throw; } } public async Task> GetByDate( Guid idDiscriminator, DateTimeOffset momentUtc, SectionPartRequest request, CancellationToken token) { var query = BuildQuery(idDiscriminator, momentUtc, request); var result = await BuildPaginationContainer(query, request, token); return result; } private IQueryable BuildQuery(Guid idDiscriminator, DateTimeOffset momentUtc, SectionPartRequest request) { var query = db.Set() .Where(e => e.IdDiscriminator == idDiscriminator) .Where(e => e.Creation <= momentUtc) .Where(e => e.Obsolete == null || e.Obsolete >= momentUtc); if (request.IdSection.HasValue) { query = query.Where(e => e.IdSection == request.IdSection); } if (request.DepthStart.HasValue) { query = query.Where(e => e.DepthStart >= request.DepthStart); } if (request.DepthEnd.HasValue) { query = query.Where(e => e.DepthEnd <= request.DepthEnd); } return query; } public async Task> GetChangeLogForDate(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) { var query = db.Set().Where(s => s.IdDiscriminator == idDiscriminator); var min = new DateTimeOffset(dateBegin.Year, dateBegin.Month, dateBegin.Day, 0, 0, 0, TimeSpan.Zero); var max = new DateTimeOffset(dateEnd.Year, dateEnd.Month, dateEnd.Day, 0, 0, 0, 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; } private async Task> BuildPaginationContainer(IQueryable query, SectionPartRequest request, CancellationToken token) { var result = new PaginationContainer { Skip = request.Skip ?? 0, Take = request.Take ?? 32, Items = Enumerable.Empty(), Count = await query.CountAsync(token) }; if (!String.IsNullOrEmpty(request.SortSettings)) { query = query.SortBy(request.SortSettings); } else { query = query .OrderBy(e => e.IdSection) .ThenBy(e => e.DepthStart) .ThenBy(e => e.DepthEnd); } var entities = await query .Skip(result.Skip) .Take(result.Take) .ToArrayAsync(token); var dtos = entities.Select(e => e.Adapt()); result.Items = dtos; return result; } 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 = Enumerable.Concat(datesCreate, datesUpdate); var datesOnly = dates .Select(d => new DateOnly(d.Year, d.Month, d.Day)) .Distinct() .OrderBy(d => d); return datesOnly; } public Task>> GetGtDate(DateTimeOffset dateBegin, CancellationToken token) { throw new NotImplementedException(); } private ChangeLog CreateEntityFromDto(Guid idUser, Guid idDiscriminator, DataWithWellDepthAndSectionDto dto) { var entity = new ChangeLog() { Id = default, Creation = DateTimeOffset.UtcNow, IdAuthor = idUser, IdDiscriminator = idDiscriminator, IdEditor = idUser, Value = dto.Value, IdSection = dto.IdSection, DepthStart = dto.DepthStart, DepthEnd = dto.DepthEnd, }; return entity; } public async Task> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token) { var date = dateBegin.ToUniversalTime(); var query = this.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(e => e.Adapt()); return dtos; } public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) { var query = this.db.Set().Where(e => e.IdDiscriminator == idDiscriminator); var minDate = await query.MinAsync(o => o.Creation, token); var maxDate = await query.MaxAsync(o => o.Creation, token); return new DatesRangeDto { From = minDate, To = maxDate }; } }