From 01f04c7ea5d44ab8ae55723dc944bbe8286030a2 Mon Sep 17 00:00:00 2001 From: Frolov-Nikita Date: Fri, 6 Oct 2023 15:19:02 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=20WellboreService.GetWellb?= =?UTF-8?q?oresAsync()=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=20WellOperationRepository.GetSectionsAsync()=20=D0=9E=D0=BF?= =?UTF-8?q?=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=20WellOperationRepository.FirstOperationDate()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/SectionByOperationsDto.cs | 47 + .../Repositories/IWellOperationRepository.cs | 8 + AsbCloudInfrastructure/Background/todo.md | 5 + .../Repository/WellOperationRepository.cs | 828 ++++++++++-------- .../Services/WellboreService.cs | 59 +- 5 files changed, 532 insertions(+), 415 deletions(-) create mode 100644 AsbCloudApp/Data/SectionByOperationsDto.cs create mode 100644 AsbCloudInfrastructure/Background/todo.md diff --git a/AsbCloudApp/Data/SectionByOperationsDto.cs b/AsbCloudApp/Data/SectionByOperationsDto.cs new file mode 100644 index 00000000..80d2a3b4 --- /dev/null +++ b/AsbCloudApp/Data/SectionByOperationsDto.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudApp.Data; + +/// +/// Параметры секции определяемые по операциям из ГГД +/// +public class SectionByOperationsDto +{ + /// + /// Id скважины + /// + public int IdWell { get; set; } + + /// + /// 0 = план или 1 = факт или прогноз = 2 + /// + public int IdType { get; set; } + + /// + /// id секции скважины + /// + public int IdWellSectionType { get; set; } + + /// + /// Глубина начала первой операции в секции, м + /// + [Range(0, 50_000)] + public double DepthStart { get; set; } + + /// + /// Дата начала первой операции в секции + /// + public DateTimeOffset DateStart { get; set; } + + /// + /// Глубина после завершения последней операции операции в секции, м + /// + [Range(0, 50_000)] + public double DepthEnd { get; set; } + + /// + /// Дата после завершения последней операции операции в секции + /// + public DateTimeOffset DateEnd { get; set; } +} diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs index b02d3e42..faab324f 100644 --- a/AsbCloudApp/Repositories/IWellOperationRepository.cs +++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs @@ -97,5 +97,13 @@ namespace AsbCloudApp.Repositories /// /// Task DeleteAsync(IEnumerable ids, CancellationToken token); + + /// + /// Получить секции скважин из операций ГГД. Секцие поделены на плановые и фактические. + /// + /// + /// + /// + Task> GetSectionsAsync(IEnumerable idsWells, CancellationToken token); } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Background/todo.md b/AsbCloudInfrastructure/Background/todo.md new file mode 100644 index 00000000..f30ce6c4 --- /dev/null +++ b/AsbCloudInfrastructure/Background/todo.md @@ -0,0 +1,5 @@ +# +- +- . . + - / + - / diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs index f0eb5f6d..cd913980 100644 --- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs +++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs @@ -13,394 +13,452 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Repository +namespace AsbCloudInfrastructure.Repository; + + +/// +/// репозиторий операций по скважине +/// +public class WellOperationRepository : IWellOperationRepository { + private const string KeyCacheSections = "OperationsBySectionSummarties"; + private readonly IAsbCloudDbContext db; + private readonly IMemoryCache memoryCache; + private readonly IWellService wellService; - /// - /// репозиторий операций по скважине - /// - public class WellOperationRepository : IWellOperationRepository + public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService) { - private readonly IAsbCloudDbContext db; - private readonly IMemoryCache memoryCache; - private readonly IWellService wellService; - private static Dictionary? firstOperationsCache = null; - - public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService) - { - this.db = db; - this.memoryCache = memoryCache; - this.wellService = wellService; - } - - /// - public IEnumerable GetCategories(bool includeParents) - { - var categories = memoryCache - .GetOrCreateBasic(db.Set()); - - if (!includeParents) - { - var parentIds = categories - .Select(o => o.IdParent) - .Distinct(); - - categories = categories - .Where(o => !parentIds.Contains(o.Id)); - } - - var result = categories - .OrderBy(o => o.Name) - .Adapt>(); - - return result; - } - - /// - public IEnumerable GetSectionTypes() => - memoryCache - .GetOrCreateBasic(db.Set()) - .OrderBy(s => s.Order) - .Select(s => s.Adapt()); - - - - public async Task GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token) - { - var timezone = wellService.GetTimezone(idWell); - var request = new WellOperationRequest() - { - IdWell = idWell, - OperationType = WellOperation.IdOperationTypePlan, - }; - - var entities = await BuildQuery(request) - .AsNoTracking() - .ToArrayAsync(token) - .ConfigureAwait(false); - - var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token); - - var result = new WellOperationPlanDto() - { - WellOperationsPlan = entities, - DateLastAssosiatedPlanOperation = dateLastAssosiatedPlanOperation - }; - - return result; - } - - private async Task GetDateLastAssosiatedPlanOperationAsync( - int idWell, - DateTime? lessThenDate, - double timeZoneHours, - CancellationToken token) - { - if (lessThenDate is null) - return null; - - var currentDateOffset = lessThenDate.Value.ToUtcDateTimeOffset(timeZoneHours); - var timeZoneOffset = TimeSpan.FromHours(timeZoneHours); - - var lastFactOperation = await db.WellOperations - .Where(o => o.IdWell == idWell) - .Where(o => o.IdType == WellOperation.IdOperationTypeFact) - .Where(o => o.IdPlan != null) - .Where(o => o.DateStart < currentDateOffset) - .Include(x => x.OperationPlan) - .OrderByDescending(x => x.DateStart) - .FirstOrDefaultAsync(token) - .ConfigureAwait(false); - - if (lastFactOperation is not null) - return DateTime.SpecifyKind(lastFactOperation.OperationPlan.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified); - return null; - } - - /// - public DateTimeOffset? FirstOperationDate(int idWell) - { - if (firstOperationsCache is null) - { - var query = db.WellOperations - .GroupBy(o => o.IdWell) - .Select(g => new Tuple - ( - g.Key, - g.Where(o => o.IdType == WellOperation.IdOperationTypePlan).Min(o => o.DateStart), - g.Where(o => o.IdType == WellOperation.IdOperationTypeFact).Min(o => o.DateStart) - )); - - firstOperationsCache = query - .ToDictionary(f => f.Item1, f => f.Item3 ?? f.Item2); - } - - return firstOperationsCache?.GetValueOrDefault(idWell); - } - - /// - public async Task> GetAsync( - WellOperationRequest request, - CancellationToken token) - { - var query = BuildQuery(request) - .AsNoTracking(); - var result = await query.ToArrayAsync(token); - return result; - } - - /// - public async Task> GetPageAsync( - WellOperationRequest request, - CancellationToken token) - { - var query = BuildQuery(request) - .AsNoTracking(); - - var result = new PaginationContainer - { - Skip = request.Skip ?? 0, - Take = request.Take ?? 32, - Count = await query.CountAsync(token).ConfigureAwait(false), - }; - - query = query - .Skip(result.Skip) - .Take(result.Take); - - result.Items = await query.ToArrayAsync(token); - return result; - } - - /// - public async Task GetOrDefaultAsync(int id, - CancellationToken token) - { - var entity = await db.WellOperations - .Include(s => s.WellSectionType) - .Include(s => s.OperationCategory) - .FirstOrDefaultAsync(e => e.Id == id, token) - .ConfigureAwait(false); - - if (entity is null) - return null; - - var timezone = wellService.GetTimezone(entity.IdWell); - - var dto = entity.Adapt(); - dto.WellSectionTypeName = entity.WellSectionType.Caption; - dto.DateStart = entity.DateStart.ToRemoteDateTime(timezone.Hours); - dto.CategoryName = entity.OperationCategory.Name; - return dto; - } - - /// - public async Task> GetGroupOperationsStatAsync( - WellOperationRequest request, - CancellationToken token) - { - // TODO: Rename controller method - request.OperationType = WellOperation.IdOperationTypeFact; - var query = BuildQuery(request); - var entities = await query - .Select(o => new - { - o.IdCategory, - DurationMinutes = o.DurationHours * 60, - DurationDepth = o.DepthEnd - o.DepthStart - }) - .ToListAsync(token); - var parentRelationDictionary = GetCategories(true) - .ToDictionary(c => c.Id, c => new - { - c.Name, - c.IdParent - }); - - var dtos = entities - .GroupBy(o => o.IdCategory) - .Select(g => new WellGroupOpertionDto - { - IdCategory = g.Key, - Category = parentRelationDictionary[g.Key].Name, - Count = g.Count(), - MinutesAverage = g.Average(o => o.DurationMinutes), - MinutesMin = g.Min(o => o.DurationMinutes), - MinutesMax = g.Max(o => o.DurationMinutes), - TotalMinutes = g.Sum(o => o.DurationMinutes), - DeltaDepth = g.Sum(o => o.DurationDepth), - IdParent = parentRelationDictionary[g.Key].IdParent - }); - - while (dtos.All(x => x.IdParent != null)) - { - dtos = dtos - .GroupBy(o => o.IdParent!) - .Select(g => { - var idCategory = g.Key ?? int.MinValue; - var category = parentRelationDictionary.GetValueOrDefault(idCategory); - var newDto = new WellGroupOpertionDto - { - IdCategory = idCategory, - Category = category?.Name ?? "unknown", - Count = g.Sum(o => o.Count), - DeltaDepth = g.Sum(o => o.DeltaDepth), - TotalMinutes = g.Sum(o => o.TotalMinutes), - Items = g.ToList(), - IdParent = category?.IdParent, - }; - return newDto; - }); - } - return dtos; - } - - /// - public async Task InsertRangeAsync( - IEnumerable wellOperationDtos, - CancellationToken token) - { - var firstOperation = wellOperationDtos - .FirstOrDefault(); - if (firstOperation is null) - return 0; - - var idWell = firstOperation.IdWell; - - var timezone = wellService.GetTimezone(idWell); - foreach (var dto in wellOperationDtos) - { - var entity = dto.Adapt(); - entity.Id = default; - entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours); - entity.IdWell = idWell; - db.WellOperations.Add(entity); - } - - return await db.SaveChangesAsync(token) - .ConfigureAwait(false); - } - - /// - public async Task UpdateAsync( - WellOperationDto dto, CancellationToken token) - { - var timezone = wellService.GetTimezone(dto.IdWell); - var entity = dto.Adapt(); - entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours); - db.WellOperations.Update(entity); - return await db.SaveChangesAsync(token) - .ConfigureAwait(false); - } - - /// - public async Task DeleteAsync(IEnumerable ids, - CancellationToken token) - { - var query = db.WellOperations.Where(e => ids.Contains(e.Id)); - db.WellOperations.RemoveRange(query); - return await db.SaveChangesAsync(token) - .ConfigureAwait(false); - } - - /// - /// В результате попрежнему требуется конвертировать дату - /// - /// - /// - private IQueryable BuildQuery(WellOperationRequest request) - { - var timezone = wellService.GetTimezone(request.IdWell); - var timeZoneOffset = TimeSpan.FromHours(timezone.Hours); - - var query = db.WellOperations - .Include(s => s.WellSectionType) - .Include(s => s.OperationCategory) - .Where(o => o.IdWell == request.IdWell); - - - if (request.OperationType.HasValue) - query = query.Where(e => e.IdType == request.OperationType.Value); - - if (request.SectionTypeIds?.Any() == true) - query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType)); - - if (request.OperationCategoryIds?.Any() == true) - query = query.Where(e => request.OperationCategoryIds.Contains(e.IdCategory)); - - if (request.GeDepth.HasValue) - query = query.Where(e => e.DepthEnd >= request.GeDepth.Value); - - if (request.LeDepth.HasValue) - query = query.Where(e => e.DepthEnd <= request.LeDepth.Value); - - if (request.GeDate.HasValue) - { - var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours); - query = query.Where(e => e.DateStart >= geDateOffset); - } - - if (request.LtDate.HasValue) - { - var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timezone.Hours); - query = query.Where(e => e.DateStart < ltDateOffset); - } - - var currentWellOperations = db.WellOperations - .Where(subOp => subOp.IdWell == request.IdWell); - - var wellOperationsWithCategoryNPT = currentWellOperations - .Where(subOp => subOp.IdType == 1) - .Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory)); - - var result = query.Select(o => new WellOperationDto - { - Id = o.Id, - IdPlan = o.IdPlan, - IdType = o.IdType, - IdWell = o.IdWell, - IdWellSectionType = o.IdWellSectionType, - IdCategory = o.IdCategory, - IdParentCategory = o.OperationCategory.IdParent, - - CategoryName = o.OperationCategory.Name, - WellSectionTypeName = o.WellSectionType.Caption, - - DateStart = DateTime.SpecifyKind(o.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified), - DepthStart = o.DepthStart, - DepthEnd = o.DepthEnd, - DurationHours = o.DurationHours, - CategoryInfo = o.CategoryInfo, - Comment = o.Comment, - - NptHours = wellOperationsWithCategoryNPT - .Where(subOp => subOp.DateStart <= o.DateStart) - .Select(subOp => subOp.DurationHours) - .Sum(), - - Day = (o.DateStart - currentWellOperations - .Where(subOp => subOp.IdType == o.IdType) - .Where(subOp => subOp.DateStart <= o.DateStart) - .Min(subOp => subOp.DateStart)) - .TotalDays, - IdUser = o.IdUser, - LastUpdateDate = o.LastUpdateDate.ToOffset(TimeSpan.FromHours(timezone.Hours)) - }); - - if (request.SortFields?.Any() == true) - { - result = result.SortBy(request.SortFields); - } - else - { - result = result - .OrderBy(e => e.DateStart) - .ThenBy(e => e.DepthEnd) - .ThenBy(e => e.Id); - }; - - return result; - } + this.db = db; + this.memoryCache = memoryCache; + this.wellService = wellService; } + /// + public IEnumerable GetCategories(bool includeParents) + { + var categories = memoryCache + .GetOrCreateBasic(db.Set()); + + if (!includeParents) + { + var parentIds = categories + .Select(o => o.IdParent) + .Distinct(); + + categories = categories + .Where(o => !parentIds.Contains(o.Id)); + } + + var result = categories + .OrderBy(o => o.Name) + .Adapt>(); + + return result; + } + + /// + public IEnumerable GetSectionTypes() => + memoryCache + .GetOrCreateBasic(db.Set()) + .OrderBy(s => s.Order) + .Select(s => s.Adapt()); + + public async Task GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token) + { + var timezone = wellService.GetTimezone(idWell); + var request = new WellOperationRequest() + { + IdWell = idWell, + OperationType = WellOperation.IdOperationTypePlan, + }; + + var entities = await BuildQuery(request) + .AsNoTracking() + .ToArrayAsync(token) + .ConfigureAwait(false); + + var dateLastAssosiatedPlanOperation = await GetDateLastAssosiatedPlanOperationAsync(idWell, currentDate, timezone.Hours, token); + + var result = new WellOperationPlanDto() + { + WellOperationsPlan = entities, + DateLastAssosiatedPlanOperation = dateLastAssosiatedPlanOperation + }; + + return result; + } + + private async Task GetDateLastAssosiatedPlanOperationAsync( + int idWell, + DateTime? lessThenDate, + double timeZoneHours, + CancellationToken token) + { + if (lessThenDate is null) + return null; + + var currentDateOffset = lessThenDate.Value.ToUtcDateTimeOffset(timeZoneHours); + var timeZoneOffset = TimeSpan.FromHours(timeZoneHours); + + var lastFactOperation = await db.WellOperations + .Where(o => o.IdWell == idWell) + .Where(o => o.IdType == WellOperation.IdOperationTypeFact) + .Where(o => o.IdPlan != null) + .Where(o => o.DateStart < currentDateOffset) + .Include(x => x.OperationPlan) + .OrderByDescending(x => x.DateStart) + .FirstOrDefaultAsync(token) + .ConfigureAwait(false); + + if (lastFactOperation is not null) + return DateTime.SpecifyKind(lastFactOperation.OperationPlan.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified); + return null; + } + + /// + public async Task> GetSectionsAsync(IEnumerable idsWells, CancellationToken token) + { + var cache = await memoryCache.GetOrCreateAsync(KeyCacheSections, async (entry) => + { + entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30); + + var query = db.Set() + .GroupBy(operation => new + { + operation.IdWell, + operation.IdType, + operation.IdWellSectionType, + }) + .Select(group => new + { + group.Key.IdWell, + group.Key.IdType, + group.Key.IdWellSectionType, + + 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 dbData = await query.ToArrayAsync(token); + var sections = dbData.Select( + item => new SectionByOperationsDto + { + IdWell = item.IdWell, + IdType = item.IdType, + IdWellSectionType = item.IdWellSectionType, + + DateStart = item.First.DateStart, + DepthStart = item.First.DepthStart, + + DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours), + DepthEnd = item.Last.DepthEnd, + }) + .ToArray() + .AsEnumerable(); + + entry.Value = sections; + return sections; + }); + + var sections = cache.Where(s => idsWells.Contains(s.IdWell)); + return sections; + } + + /// + public DateTimeOffset? FirstOperationDate(int idWell) + { + var sections = GetSectionsAsync(new[] { idWell }, CancellationToken.None).Result; + var first = sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypeFact) + ?? sections.FirstOrDefault(section => section.IdType == WellOperation.IdOperationTypePlan); + + return first?.DateStart; + } + + /// + public async Task> GetAsync( + WellOperationRequest request, + CancellationToken token) + { + var query = BuildQuery(request) + .AsNoTracking(); + var result = await query.ToArrayAsync(token); + return result; + } + + /// + public async Task> GetPageAsync( + WellOperationRequest request, + CancellationToken token) + { + var query = BuildQuery(request) + .AsNoTracking(); + + var result = new PaginationContainer + { + Skip = request.Skip ?? 0, + Take = request.Take ?? 32, + Count = await query.CountAsync(token).ConfigureAwait(false), + }; + + query = query + .Skip(result.Skip) + .Take(result.Take); + + result.Items = await query.ToArrayAsync(token); + return result; + } + + /// + public async Task GetOrDefaultAsync(int id, + CancellationToken token) + { + var entity = await db.WellOperations + .Include(s => s.WellSectionType) + .Include(s => s.OperationCategory) + .FirstOrDefaultAsync(e => e.Id == id, token) + .ConfigureAwait(false); + + if (entity is null) + return null; + + var timezone = wellService.GetTimezone(entity.IdWell); + + var dto = entity.Adapt(); + dto.WellSectionTypeName = entity.WellSectionType.Caption; + dto.DateStart = entity.DateStart.ToRemoteDateTime(timezone.Hours); + dto.CategoryName = entity.OperationCategory.Name; + return dto; + } + + /// + public async Task> GetGroupOperationsStatAsync( + WellOperationRequest request, + CancellationToken token) + { + // TODO: Rename controller method + request.OperationType = WellOperation.IdOperationTypeFact; + var query = BuildQuery(request); + var entities = await query + .Select(o => new + { + o.IdCategory, + DurationMinutes = o.DurationHours * 60, + DurationDepth = o.DepthEnd - o.DepthStart + }) + .ToListAsync(token); + var parentRelationDictionary = GetCategories(true) + .ToDictionary(c => c.Id, c => new + { + c.Name, + c.IdParent + }); + + var dtos = entities + .GroupBy(o => o.IdCategory) + .Select(g => new WellGroupOpertionDto + { + IdCategory = g.Key, + Category = parentRelationDictionary[g.Key].Name, + Count = g.Count(), + MinutesAverage = g.Average(o => o.DurationMinutes), + MinutesMin = g.Min(o => o.DurationMinutes), + MinutesMax = g.Max(o => o.DurationMinutes), + TotalMinutes = g.Sum(o => o.DurationMinutes), + DeltaDepth = g.Sum(o => o.DurationDepth), + IdParent = parentRelationDictionary[g.Key].IdParent + }); + + while (dtos.All(x => x.IdParent != null)) + { + dtos = dtos + .GroupBy(o => o.IdParent!) + .Select(g => { + var idCategory = g.Key ?? int.MinValue; + var category = parentRelationDictionary.GetValueOrDefault(idCategory); + var newDto = new WellGroupOpertionDto + { + IdCategory = idCategory, + Category = category?.Name ?? "unknown", + Count = g.Sum(o => o.Count), + DeltaDepth = g.Sum(o => o.DeltaDepth), + TotalMinutes = g.Sum(o => o.TotalMinutes), + Items = g.ToList(), + IdParent = category?.IdParent, + }; + return newDto; + }); + } + return dtos; + } + + /// + public async Task InsertRangeAsync( + IEnumerable wellOperationDtos, + CancellationToken token) + { + var firstOperation = wellOperationDtos + .FirstOrDefault(); + if (firstOperation is null) + return 0; + + var idWell = firstOperation.IdWell; + + var timezone = wellService.GetTimezone(idWell); + foreach (var dto in wellOperationDtos) + { + var entity = dto.Adapt(); + entity.Id = default; + entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours); + entity.IdWell = idWell; + db.WellOperations.Add(entity); + } + + var result = await db.SaveChangesAsync(token); + if (result > 0) + memoryCache.Remove(KeyCacheSections); + return result; + + } + + /// + public async Task UpdateAsync( + WellOperationDto dto, CancellationToken token) + { + var timezone = wellService.GetTimezone(dto.IdWell); + var entity = dto.Adapt(); + entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours); + db.WellOperations.Update(entity); + + var result = await db.SaveChangesAsync(token); + if (result > 0) + memoryCache.Remove(KeyCacheSections); + return result; + } + + /// + public async Task DeleteAsync(IEnumerable ids, + CancellationToken token) + { + var query = db.WellOperations.Where(e => ids.Contains(e.Id)); + db.WellOperations.RemoveRange(query); + + var result = await db.SaveChangesAsync(token); + if (result > 0) + memoryCache.Remove(KeyCacheSections); + return result; + } + + /// + /// В результате попрежнему требуется конвертировать дату + /// + /// + /// + private IQueryable BuildQuery(WellOperationRequest request) + { + var timezone = wellService.GetTimezone(request.IdWell); + var timeZoneOffset = TimeSpan.FromHours(timezone.Hours); + + var query = db.WellOperations + .Include(s => s.WellSectionType) + .Include(s => s.OperationCategory) + .Where(o => o.IdWell == request.IdWell); + + + if (request.OperationType.HasValue) + query = query.Where(e => e.IdType == request.OperationType.Value); + + if (request.SectionTypeIds?.Any() == true) + query = query.Where(e => request.SectionTypeIds.Contains(e.IdWellSectionType)); + + if (request.OperationCategoryIds?.Any() == true) + query = query.Where(e => request.OperationCategoryIds.Contains(e.IdCategory)); + + if (request.GeDepth.HasValue) + query = query.Where(e => e.DepthEnd >= request.GeDepth.Value); + + if (request.LeDepth.HasValue) + query = query.Where(e => e.DepthEnd <= request.LeDepth.Value); + + if (request.GeDate.HasValue) + { + var geDateOffset = request.GeDate.Value.ToUtcDateTimeOffset(timezone.Hours); + query = query.Where(e => e.DateStart >= geDateOffset); + } + + if (request.LtDate.HasValue) + { + var ltDateOffset = request.LtDate.Value.ToUtcDateTimeOffset(timezone.Hours); + query = query.Where(e => e.DateStart < ltDateOffset); + } + + var currentWellOperations = db.WellOperations + .Where(subOp => subOp.IdWell == request.IdWell); + + var wellOperationsWithCategoryNPT = currentWellOperations + .Where(subOp => subOp.IdType == 1) + .Where(subOp => WellOperationCategory.NonProductiveTimeSubIds.Contains(subOp.IdCategory)); + + var result = query.Select(o => new WellOperationDto + { + Id = o.Id, + IdPlan = o.IdPlan, + IdType = o.IdType, + IdWell = o.IdWell, + IdWellSectionType = o.IdWellSectionType, + IdCategory = o.IdCategory, + IdParentCategory = o.OperationCategory.IdParent, + + CategoryName = o.OperationCategory.Name, + WellSectionTypeName = o.WellSectionType.Caption, + + DateStart = DateTime.SpecifyKind(o.DateStart.UtcDateTime + timeZoneOffset, DateTimeKind.Unspecified), + DepthStart = o.DepthStart, + DepthEnd = o.DepthEnd, + DurationHours = o.DurationHours, + CategoryInfo = o.CategoryInfo, + Comment = o.Comment, + + NptHours = wellOperationsWithCategoryNPT + .Where(subOp => subOp.DateStart <= o.DateStart) + .Select(subOp => subOp.DurationHours) + .Sum(), + + Day = (o.DateStart - currentWellOperations + .Where(subOp => subOp.IdType == o.IdType) + .Where(subOp => subOp.DateStart <= o.DateStart) + .Min(subOp => subOp.DateStart)) + .TotalDays, + IdUser = o.IdUser, + LastUpdateDate = o.LastUpdateDate.ToOffset(TimeSpan.FromHours(timezone.Hours)) + }); + + if (request.SortFields?.Any() == true) + { + result = result.SortBy(request.SortFields); + } + else + { + result = result + .OrderBy(e => e.DateStart) + .ThenBy(e => e.DepthEnd) + .ThenBy(e => e.Id); + }; + + return result; + } } diff --git a/AsbCloudInfrastructure/Services/WellboreService.cs b/AsbCloudInfrastructure/Services/WellboreService.cs index 5e0e439e..396243d6 100644 --- a/AsbCloudInfrastructure/Services/WellboreService.cs +++ b/AsbCloudInfrastructure/Services/WellboreService.cs @@ -35,7 +35,7 @@ public class WellboreService : IWellboreService } public async Task> GetWellboresAsync(WellboreRequest request, - CancellationToken cancellationToken) + CancellationToken token) { var wellbores = new List(request.Ids.Count()); var skip = request.Skip ?? 0; @@ -44,26 +44,43 @@ public class WellboreService : IWellboreService var sections = wellOperationRepository.GetSectionTypes() .ToDictionary(w => w.Id, w => w); - var ids = request.Ids.GroupBy(i => i.idWell); + var ids = request.Ids.GroupBy(i => i.idWell, i => i.idSection); + + var idsWells = request.Ids.Select(i => i.idWell); + + var allSections = await wellOperationRepository.GetSectionsAsync(idsWells, token); foreach (var id in ids) { - var well = await wellService.GetOrDefaultAsync(id.Key, cancellationToken); + var well = await wellService.GetOrDefaultAsync(id.Key, token); if (well is null) continue; - var wellOperations = await GetFactOperationsAsync(well.Id, id.Select(i => i.idSection), cancellationToken); - var groupedOperations = wellOperations.GroupBy(o => o.IdWellSectionType); - var wellWellbores = groupedOperations.Select(group => new WellboreDto { - Id = group.Key, - Name = sections[group.Key].Caption, + var wellTimezoneOffset = TimeSpan.FromHours(well.Timezone.Hours); + + var wellFactSections = allSections + .Where(section => section.IdWell == id.Key) + .Where(section => section.IdType == WellOperation.IdOperationTypeFact); + + var idsSections = id + .Where(i => i.HasValue) + .Select(i => i!.Value); + + if (idsSections.Any()) + wellFactSections = wellFactSections + .Where(section => idsSections.Contains(section.IdWellSectionType)); + + var wellWellbores = wellFactSections.Select(section => new WellboreDto { + Id = section.IdWellSectionType, + Name = sections[section.IdWellSectionType].Caption, Well = well.Adapt(), - DateStart = group.Min(operation => operation.DateStart).ToUtcDateTimeOffset(well.Timezone.Hours).ToOffset(TimeSpan.FromHours(well.Timezone.Hours)), - DateEnd = group.Max(operation => operation.DateStart.AddHours(operation.DurationHours)).ToUtcDateTimeOffset(well.Timezone.Hours).ToOffset(TimeSpan.FromHours(well.Timezone.Hours)), - DepthStart = group.Min(operation => operation.DepthStart), - DepthEnd = group.Max(operation => operation.DepthEnd), + DateStart = section.DateStart.ToOffset(wellTimezoneOffset), + DateEnd = section.DateEnd.ToOffset(wellTimezoneOffset), + DepthStart = section.DepthStart, + DepthEnd = section.DepthEnd, }); + wellbores.AddRange(wellWellbores); } @@ -71,22 +88,4 @@ public class WellboreService : IWellboreService .OrderBy(w => w.Well.Id).ThenBy(w => w.Id) .Skip(skip).Take(take); } - - private async Task> GetFactOperationsAsync(int idWell, IEnumerable idsSections, - CancellationToken cancellationToken) - { - var request = new WellOperationRequest - { - IdWell = idWell, - OperationType = WellOperation.IdOperationTypeFact, - SortFields = new[] { "DateStart asc" }, - }; - - request.SectionTypeIds = idsSections.All(i => i.HasValue) - ? idsSections.Select(i => i!.Value) - : null; - - return (await wellOperationRepository.GetAsync(request, cancellationToken)) - .OrderBy(o => o.DateStart); - } } \ No newline at end of file