forked from ddrilling/AsbCloudServer
Оптимизирован WellboreService.GetWellboresAsync()
Добавлен WellOperationRepository.GetSectionsAsync() Оптимизирован WellOperationRepository.FirstOperationDate()
This commit is contained in:
parent
a6c7ddc94b
commit
01f04c7ea5
47
AsbCloudApp/Data/SectionByOperationsDto.cs
Normal file
47
AsbCloudApp/Data/SectionByOperationsDto.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AsbCloudApp.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Параметры секции определяемые по операциям из ГГД
|
||||
/// </summary>
|
||||
public class SectionByOperationsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Id скважины
|
||||
/// </summary>
|
||||
public int IdWell { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0 = план или 1 = факт или прогноз = 2
|
||||
/// </summary>
|
||||
public int IdType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// id секции скважины
|
||||
/// </summary>
|
||||
public int IdWellSectionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Глубина начала первой операции в секции, м
|
||||
/// </summary>
|
||||
[Range(0, 50_000)]
|
||||
public double DepthStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Дата начала первой операции в секции
|
||||
/// </summary>
|
||||
public DateTimeOffset DateStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Глубина после завершения последней операции операции в секции, м
|
||||
/// </summary>
|
||||
[Range(0, 50_000)]
|
||||
public double DepthEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Дата после завершения последней операции операции в секции
|
||||
/// </summary>
|
||||
public DateTimeOffset DateEnd { get; set; }
|
||||
}
|
@ -97,5 +97,13 @@ namespace AsbCloudApp.Repositories
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получить секции скважин из операций ГГД. Секцие поделены на плановые и фактические.
|
||||
/// </summary>
|
||||
/// <param name="idsWells"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token);
|
||||
}
|
||||
}
|
5
AsbCloudInfrastructure/Background/todo.md
Normal file
5
AsbCloudInfrastructure/Background/todo.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Проблемы фонового сервиса
|
||||
- нет понимания по загрузке очереди работ
|
||||
- нет управления сервисом. Для исключения его влияния на другие процессы сервера.
|
||||
- отключать/включать целиком
|
||||
- отключать/включать отдельную периодическую задачу
|
@ -13,18 +13,18 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Repository
|
||||
{
|
||||
namespace AsbCloudInfrastructure.Repository;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// репозиторий операций по скважине
|
||||
/// </summary>
|
||||
public class WellOperationRepository : IWellOperationRepository
|
||||
{
|
||||
private const string KeyCacheSections = "OperationsBySectionSummarties";
|
||||
private readonly IAsbCloudDbContext db;
|
||||
private readonly IMemoryCache memoryCache;
|
||||
private readonly IWellService wellService;
|
||||
private static Dictionary<int, DateTimeOffset?>? firstOperationsCache = null;
|
||||
|
||||
public WellOperationRepository(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService)
|
||||
{
|
||||
@ -63,8 +63,6 @@ namespace AsbCloudInfrastructure.Repository
|
||||
.OrderBy(s => s.Order)
|
||||
.Select(s => s.Adapt<WellSectionTypeDto>());
|
||||
|
||||
|
||||
|
||||
public async Task<WellOperationPlanDto> GetOperationsPlanAsync(int idWell, DateTime? currentDate, CancellationToken token)
|
||||
{
|
||||
var timezone = wellService.GetTimezone(idWell);
|
||||
@ -118,24 +116,77 @@ namespace AsbCloudInfrastructure.Repository
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTimeOffset? FirstOperationDate(int idWell)
|
||||
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
|
||||
{
|
||||
if (firstOperationsCache is null)
|
||||
var cache = await memoryCache.GetOrCreateAsync(KeyCacheSections, async (entry) =>
|
||||
{
|
||||
var query = db.WellOperations
|
||||
.GroupBy(o => o.IdWell)
|
||||
.Select(g => new Tuple<int, DateTimeOffset?, DateTimeOffset?>
|
||||
(
|
||||
g.Key,
|
||||
g.Where(o => o.IdType == WellOperation.IdOperationTypePlan).Min(o => o.DateStart),
|
||||
g.Where(o => o.IdType == WellOperation.IdOperationTypeFact).Min(o => o.DateStart)
|
||||
));
|
||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
|
||||
|
||||
firstOperationsCache = query
|
||||
.ToDictionary(f => f.Item1, f => f.Item3 ?? f.Item2);
|
||||
var query = db.Set<WellOperation>()
|
||||
.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;
|
||||
}
|
||||
|
||||
return firstOperationsCache?.GetValueOrDefault(idWell);
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@ -277,8 +328,11 @@ namespace AsbCloudInfrastructure.Repository
|
||||
db.WellOperations.Add(entity);
|
||||
}
|
||||
|
||||
return await db.SaveChangesAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
if (result > 0)
|
||||
memoryCache.Remove(KeyCacheSections);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@ -289,8 +343,11 @@ namespace AsbCloudInfrastructure.Repository
|
||||
var entity = dto.Adapt<WellOperation>();
|
||||
entity.DateStart = dto.DateStart.ToUtcDateTimeOffset(timezone.Hours);
|
||||
db.WellOperations.Update(entity);
|
||||
return await db.SaveChangesAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
if (result > 0)
|
||||
memoryCache.Remove(KeyCacheSections);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@ -299,8 +356,11 @@ namespace AsbCloudInfrastructure.Repository
|
||||
{
|
||||
var query = db.WellOperations.Where(e => ids.Contains(e.Id));
|
||||
db.WellOperations.RemoveRange(query);
|
||||
return await db.SaveChangesAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var result = await db.SaveChangesAsync(token);
|
||||
if (result > 0)
|
||||
memoryCache.Remove(KeyCacheSections);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -402,5 +462,3 @@ namespace AsbCloudInfrastructure.Repository
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public class WellboreService : IWellboreService
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<WellboreDto>> GetWellboresAsync(WellboreRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
CancellationToken token)
|
||||
{
|
||||
var wellbores = new List<WellboreDto>(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<WellWithTimezoneDto>(),
|
||||
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<IOrderedEnumerable<WellOperationDto>> GetFactOperationsAsync(int idWell, IEnumerable<int?> 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user