using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Models.Requests; using DD.Persistence.Models.Requests.ChangeLog; using DD.Persistence.Repositories; using Microsoft.Extensions.Caching.Memory; namespace DD.Persistence.API.Services; /// /// Сервис по работе с журналом изменений /// public class ChangeLogService { private readonly IMemoryCache memoryCache; private readonly IChangeLogCommitRepository commitRepository; private readonly IChangeLogRepository repository; private readonly TimeSpan? AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60); /// /// ctor /// /// /// /// public ChangeLogService( IMemoryCache memoryCache, IChangeLogCommitRepository commitRepository, IChangeLogRepository repository) { this.memoryCache = memoryCache; this.commitRepository = commitRepository; this.repository = repository; } /// /// Чтение или создание нового коммита /// /// /// /// private async Task 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!; } /// /// Добавление записи в журнал изменений /// /// /// /// /// /// public async Task AddRange(Guid idDiscriminator, CreateChangeLogCommitRequest request, IEnumerable dtos, CancellationToken token) { var commit = await GetOrCreateCommitAsync(request, token); var result = await repository.AddRange(idDiscriminator, commit, dtos, token); return result; } /// /// Пометить запись журнала изменений как удаленную /// /// /// /// /// public async Task MarkAsDeleted(IEnumerable ids, CreateChangeLogCommitRequest request, CancellationToken token) { var commit = await GetOrCreateCommitAsync(request, token); var result = await repository.MarkAsDeleted(ids, commit, token); return result; } /// /// Очистить старые и добавить новые записи в журнал изменений /// /// /// /// /// /// public async Task ClearAndAddRange(Guid idDiscriminator, CreateChangeLogCommitRequest request, IEnumerable dtos, CancellationToken token) { var commit = await GetOrCreateCommitAsync(request, token); var result = await repository.ClearAndAddRange(idDiscriminator, commit, dtos, token); return result; } /// /// Обновить записи в журнале изменений /// /// /// /// /// public async Task UpdateRange(CreateChangeLogCommitRequest commitRequest, IEnumerable dtos, CancellationToken token) { var commit = await GetOrCreateCommitAsync(commitRequest, token); var result = await repository.UpdateRange(commit, dtos, token); return result; } /// /// Получение актуальных записей на определенный момент времени (с пагинацией) /// /// /// /// /// /// public async Task> GetByDate( Guid idDiscriminator, DateTimeOffset momentUtc, PaginationRequest paginationRequest, CancellationToken token) { var result = await repository.GetByDate(idDiscriminator, momentUtc, paginationRequest, token); return result; } /// /// Получение измененных записей за период времени /// /// /// /// /// /// public async Task> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) { var result = await repository.GetChangeLogForInterval(idDiscriminator, dateBegin, dateEnd, token); return result; } /// /// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени) /// /// /// /// public async Task> GetDatesChange(Guid idDiscriminator, CancellationToken token) { var result = await repository.GetDatesChange(idDiscriminator, token); return result; } /// /// Получить данные, начиная с определенной даты /// /// /// /// /// public async Task> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token) { var result = await repository.GetGtDate(idDiscriminator, dateBegin, token); return result; } /// /// Получить диапазон дат, для которых есть данные в репозитории /// /// /// /// public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) { var result = await repository.GetDatesRange(idDiscriminator, token); return result; } /// /// Метод получения статистики пользователя по количеству изменений в разрезе дней /// /// /// /// public async Task> GetUserStatisticsCount(ChangeLogRequest request, CancellationToken token) { var commitData = await commitRepository.GetCreatedAndObsoleted(request.DiscriminatorId, request.UserId, token); var commitCreated = commitData.Item1.OrderBy(c => c.Creation); var commitObsoleted = commitData.Item2.OrderBy(c => c.Creation); if (!commitCreated.Any()) return Enumerable.Empty(); var minDateTime = commitCreated.FirstOrDefault()!.Creation; var minDate = new DateOnly(minDateTime.Year, minDateTime.Month, minDateTime.Day); var lastCommitCreated = commitCreated.Last(); var lastCommitObsoleted = commitObsoleted.LastOrDefault(); var maxDateTime = lastCommitCreated.Creation; if ((lastCommitObsoleted != null) && (lastCommitObsoleted.Creation > maxDateTime)) maxDateTime = lastCommitObsoleted.Creation; var maxDate = new DateOnly(maxDateTime.Year, maxDateTime.Month, maxDateTime.Day); var result = new List(); while (minDate <= maxDate) { var createdCount = commitCreated .Where(c => new DateOnly(c.Creation.Year, c.Creation.Month, c.Creation.Day) == minDate) .Count(); var obsoletedCount = commitObsoleted .Where(c => new DateOnly(c.Creation.Year, c.Creation.Month, c.Creation.Day) == minDate) .Count(); result.Add(new StatisticsChangeLogDto() { Date = minDate, CreatedCount = createdCount, ObsoletedCount = obsoletedCount, Count = createdCount + obsoletedCount }); } return result; } /// /// /// /// /// /// /// public async Task> GetHistoryChangeLog(ChangeLogRequest request, CancellationToken token) { var commits = await commitRepository.Get(request.DiscriminatorId, request.UserId, token); if (!commits.Any()) return Enumerable.Empty(); var result = commits.Select(c => new HistoryChangeLogDto() { ChangeLogItems = new List(), Comment = c.Comment ?? string.Empty, DateTime = c.Creation, User = new UserDto() { DisplayName = "", Id = c.IdAuthor, } }); return result; } }