DD.WellWorkover.Cloud/AsbCloudInfrastructure/Repository/WellOperationRepository.cs

382 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository;
public class WellOperationRepository : CrudRepositoryBase<WellOperationBaseDto, WellOperation>,
IWellOperationRepository
{
private const string keyCacheTemplate = "OperationsBySectionSummaries_{0}";
private const string cacheKeyWellOperations = "FirstAndLastFactWellsOperations";
private readonly IMemoryCache memoryCache;
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
private readonly IWellService wellService;
private Lazy<IDictionary<int, WellOperationCategoryDto>> LazyWellCategories { get; }
private Lazy<IDictionary<int, WellSectionTypeDto>> LazyWellSectionTypes { get; }
public WellOperationRepository(IAsbCloudDbContext context,
IMemoryCache memoryCache,
IWellOperationCategoryRepository wellOperationCategoryRepository,
IWellService wellService)
: base(context, dbSet => dbSet)
{
this.memoryCache = memoryCache;
this.wellOperationCategoryRepository = wellOperationCategoryRepository;
this.wellService = wellService;
LazyWellCategories = new(() => wellOperationCategoryRepository.Get(true, false).ToDictionary(c => c.Id));
LazyWellSectionTypes = new(() => GetSectionTypes().ToDictionary(c => c.Id));
}
public IEnumerable<WellSectionTypeDto> GetSectionTypes() =>
memoryCache
.GetOrCreateBasic(dbContext.WellSectionTypes)
.OrderBy(s => s.Order)
.Select(s => s.Adapt<WellSectionTypeDto>());
public async Task<int> InsertRangeAsync(IEnumerable<WellOperationBaseDto> dtos,
bool deleteBeforeInsert,
CancellationToken token)
{
EnsureValidWellOperations(dtos);
var result = 0;
if (!deleteBeforeInsert)
{
result = await InsertRangeAsync(dtos, token);
if (result > 0)
memoryCache.Remove(cacheKeyWellOperations);
return result;
}
var idType = dtos.First().IdType;
var idWell = dtos.First().IdWell;
var existingOperationIds = await dbContext.WellOperations
.Where(e => e.IdWell == idWell && e.IdType == idType)
.Select(e => e.Id)
.ToArrayAsync(token);
await DeleteRangeAsync(existingOperationIds, token);
result = await InsertRangeAsync(dtos, token);
if (result > 0)
memoryCache.Remove(cacheKeyWellOperations);
return result;
}
public override async Task<int> UpdateRangeAsync(IEnumerable<WellOperationBaseDto> dtos, CancellationToken token)
{
EnsureValidWellOperations(dtos);
var result = await base.UpdateRangeAsync(dtos, token);
if (result > 0)
memoryCache.Remove(cacheKeyWellOperations);
return result;
}
private static void EnsureValidWellOperations(IEnumerable<WellOperationBaseDto> dtos)
{
if (dtos.GroupBy(d => d.IdType).Count() > 1)
throw new ArgumentInvalidException(nameof(dtos), "Все операции должны быть одного типа");
if (dtos.GroupBy(d => d.IdType).Count() > 1)
throw new ArgumentInvalidException(nameof(dtos), "Все операции должны принадлежать одной скважине");
}
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
{
var result = new List<SectionByOperationsDto>();
var notFoundIds = new List<int>();
foreach (var idWell in idsWells)
{
var cacheKey = string.Format(keyCacheTemplate, idWell);
if (memoryCache.TryGetValue<IEnumerable<SectionByOperationsDto>>(cacheKey, out var section))
{
result.AddRange(section!);
}
else
{
notFoundIds.Add(idWell);
}
}
if (notFoundIds.Count != 0)
{
var query = dbContext.Set<WellOperation>()
.Where(operation => notFoundIds.Contains( operation.IdWell))
.GroupBy(operation => new
{
operation.IdWell,
operation.IdType,
operation.IdWellSectionType,
operation.WellSectionType.Caption,
})
.Select(group => new
{
group.Key.IdWell,
group.Key.IdType,
group.Key.IdWellSectionType,
group.Key.Caption,
First = group
.OrderBy(operation => operation.DateStart)
.Select(operation => new { operation.DateStart, operation.DepthStart, })
.First(),
Last = group
.OrderByDescending(operation => operation.DateStart)
.Select(operation => new
{
operation.DateStart,
operation.DurationHours,
operation.DepthEnd,
})
.First(),
});
var entities = await query.ToArrayAsync(token);
var dtos = entities.Select(
entity => new SectionByOperationsDto
{
IdWell = entity.IdWell,
IdType = entity.IdType,
IdWellSectionType = entity.IdWellSectionType,
Caption = entity.Caption,
DateStart = entity.First.DateStart,
DepthStart = entity.First.DepthStart,
DateEnd = entity.Last.DateStart.AddHours(entity.Last.DurationHours),
DepthEnd = entity.Last.DepthEnd,
})
.ToList();
result.AddRange(dtos);
var groupedByWellDtos = dtos
.GroupBy(dto => dto.IdWell);
foreach (var group in groupedByWellDtos)
{
var cacheKey = string.Format(keyCacheTemplate, group.Key);
memoryCache.Set(cacheKey, group.AsEnumerable(), TimeSpan.FromMinutes(30));
}
}
return result;
}
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken)
{
var query = dbContext.WellOperations.Where(o => o.IdWell == idWell && o.IdType == idType);
if (!await query.AnyAsync(cancellationToken))
return null;
var timeZoneOffset = wellService.GetTimezone(idWell).Offset;
var minDate = await query.MinAsync(o => o.DateStart, cancellationToken);
var maxDate = await query.MaxAsync(o => o.DateStart, cancellationToken);
return new DatesRangeDto
{
From = minDate.ToOffset(timeZoneOffset),
To = maxDate.ToOffset(timeZoneOffset)
};
}
public (WellOperationBaseDto First, WellOperationBaseDto Last)? GetFirstAndLastFact(int idWell)
{
var cachedDictionary = memoryCache.GetOrCreate(cacheKeyWellOperations, (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
var query = dbContext.Set<WellOperation>()
.Where(o => o.IdType == WellOperation.IdOperationTypeFact)
.GroupBy(o => o.IdWell)
.Select(group => new
{
IdWell = group.Key,
FirstFact = group.OrderBy(o => o.DateStart).First(),
LastFact = group.OrderBy(o => o.DateStart).Last(),
});
var entities = query.ToArray();
var dictionary = entities.ToDictionary(s => s.IdWell, s => (Convert(s.FirstFact), Convert(s.LastFact)));
entry.Value = dictionary;
return dictionary;
})!;
var firstAndLast = cachedDictionary.GetValueOrDefault(idWell);
return firstAndLast;
}
public override async Task<int> DeleteAsync(int id, CancellationToken token)
{
var result = await base.DeleteAsync(id, token);
if (result > 0)
memoryCache.Remove(cacheKeyWellOperations);
return result;
}
public override async Task<int> DeleteRangeAsync(IEnumerable<int> ids, CancellationToken token)
{
var result = await base.DeleteRangeAsync(ids, token);
if (result > 0)
memoryCache.Remove(cacheKeyWellOperations);
return result;
}
protected override WellOperation Convert(WellOperationBaseDto src)
{
var entity = src.Adapt<WellOperation>();
entity.DateStart = src.DateStart.UtcDateTime;
return entity;
}
private WellOperationBaseDto Convert(WellOperation src, TimeSpan timezoneOffset)
{
var dto = src.Adapt<WellOperationBaseDto>();
dto.DateStart = src.DateStart.ToOffset(timezoneOffset);
dto.LastUpdateDate = src.LastUpdateDate.ToOffset(timezoneOffset);
dto.OperationCategoryName = LazyWellCategories.Value.TryGetValue(src.IdCategory, out WellOperationCategoryDto? category) ? category.Name : string.Empty;
dto.WellSectionTypeCaption = LazyWellSectionTypes.Value.TryGetValue(src.IdWellSectionType, out WellSectionTypeDto? sectionType) ? sectionType.Caption : string.Empty;
return dto;
}
public async Task<IEnumerable<WellOperationBaseDto>> GetAll(WellOperationRequest request, CancellationToken token)
{
var timezoneOffsetDictionary = new Dictionary<int, TimeSpan>();
foreach (var idWell in request.IdsWell)
{
var offset = wellService.GetTimezone(idWell).Offset;
timezoneOffsetDictionary.Add(idWell, offset);
}
var query = GetQuery()
.Where(e => request.IdsWell.Contains(e.IdWell))
.OrderBy(e => e.DateStart)
.AsQueryable();
query = FilterByRequest(query, request);
var entities = await query.ToArrayAsync(token);
var dtos = entities.Select(o => Convert(o, timezoneOffsetDictionary[o.IdWell]));
return dtos;
}
public async Task<IEnumerable<WellOperationBaseDto>> GetAll(WellOperationRepositoryRequest request, CancellationToken token)
{
var timezoneOffsetDictionary = new Dictionary<int, TimeSpan>();
foreach (var idWell in request.IdsWell)
{
var offset = wellService.GetTimezone(idWell).Offset;
timezoneOffsetDictionary.Add(idWell, offset);
}
var query = GetQuery()
.Where(e => request.IdsWell.Contains(e.IdWell))
.OrderBy(e => e.DateStart)
.AsQueryable();
query = FilterByRequest(query, request);
var entities = await query.ToArrayAsync(token);
var dtos = entities.Select(o => Convert(o, timezoneOffsetDictionary[o.IdWell]));
return dtos;
}
public async Task<IEnumerable<WellOperationBaseDto>> GetAll(int idWell, CancellationToken token)
{
var offset = wellService.GetTimezone(idWell).Offset;
var query = GetQuery()
.Include(o => o.OperationCategory)
.Include(o => o.WellSectionType)
.Where(o => o.IdWell == idWell)
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthEnd);
var entities = await query.ToArrayAsync(token);
var dtos = entities.Select(o => Convert(o, offset));
return dtos;
}
public static IQueryable<WellOperation> FilterByRequest(IQueryable<WellOperation> entities, WellOperationRequest request)
{
if (request.OperationType.HasValue)
entities = entities.Where(e => e.IdType == request.OperationType.Value);
if (request.SectionTypeIds?.Any() is true)
entities = entities.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType));
if (request.OperationCategoryIds?.Any() is true)
entities = entities.Where(e => request.OperationCategoryIds.Contains(e.IdCategory));
if (request.GeDepth.HasValue)
entities = entities.Where(e => e.DepthEnd >= request.GeDepth.Value);
if (request.LeDepth.HasValue)
entities = entities.Where(e => e.DepthEnd <= request.LeDepth.Value);
if (request.GeDate.HasValue)
{
var geDateUtc = request.GeDate.Value.UtcDateTime;
entities = entities.Where(e => e.DateStart >= geDateUtc);
}
if (request.LeDate.HasValue)
{
var leDateUtc = request.LeDate.Value.UtcDateTime;
entities = entities.Where(e => e.DateStart <= leDateUtc);
}
if (request.SortFields?.Any() is true)
entities = entities.AsQueryable().SortBy(request.SortFields);
else
entities = entities.AsQueryable().OrderBy(e => e.DateStart);
return entities;
}
public static IQueryable<WellOperation> FilterByRequest(IQueryable<WellOperation> entities, WellOperationRepositoryRequest request)
{
if (request.OperationType.HasValue)
entities = entities.Where(e => e.IdType == request.OperationType.Value);
if (request.LeDepth.HasValue)
entities = entities.Where(e => e.DepthEnd <= request.LeDepth.Value);
if (request.LeDate.HasValue)
{
var leDateUtc = request.LeDate.Value.UtcDateTime;
entities = entities.Where(e => e.DateStart <= leDateUtc);
}
entities = entities.AsQueryable().OrderBy(e => e.DateStart);
return entities;
}
}