DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/DailyReport/DailyReportService.cs

377 lines
14 KiB
C#
Raw Normal View History

using AsbCloudApp.Data.DailyReport;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.DailyReport.Blocks;
using AsbCloudApp.Data.DailyReport.Blocks.Sign;
using AsbCloudApp.Data.DailyReport.Blocks.Subsystems;
using AsbCloudApp.Data.DailyReport.Blocks.TimeBalance;
using AsbCloudApp.Data.DailyReport.Blocks.WellOperation;
using AsbCloudApp.Requests;
using AsbCloudApp.Services.DailyReport;
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model;
using Mapster;
namespace AsbCloudInfrastructure.Services.DailyReport;
public class DailyReportService : IDailyReportService
{
private readonly IWellService wellService;
private readonly ITrajectoryFactRepository trajectoryFactRepository;
private readonly IDailyReportRepository dailyReportRepository;
private readonly IScheduleRepository scheduleRepository;
private readonly IWellOperationRepository wellOperationRepository;
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
private readonly IDetectedOperationService detectedOperationService;
public DailyReportService(IWellService wellService,
ITrajectoryFactRepository trajectoryFactRepository,
IDailyReportRepository dailyReportRepository,
IScheduleRepository scheduleRepository,
IWellOperationRepository wellOperationRepository,
ISubsystemOperationTimeService subsystemOperationTimeService,
IProcessMapReportWellDrillingService processMapReportWellDrillingService,
IDetectedOperationService detectedOperationService)
{
this.wellService = wellService;
this.trajectoryFactRepository = trajectoryFactRepository;
this.dailyReportRepository = dailyReportRepository;
this.scheduleRepository = scheduleRepository;
this.wellOperationRepository = wellOperationRepository;
this.subsystemOperationTimeService = subsystemOperationTimeService;
this.processMapReportWellDrillingService = processMapReportWellDrillingService;
this.detectedOperationService = detectedOperationService;
}
public async Task<int> UpdateOrInsertAsync<TBlock>(int idWell, DateTime dateDailyReport, int idUser, TBlock editableBlock,
CancellationToken cancellationToken)
where TBlock : ItemInfoDto
{
if (!await IsDateDailyReportInRangeAsync(idWell, dateDailyReport, cancellationToken))
throw new ArgumentInvalidException(nameof(dateDailyReport), "Невозможно обновить суточный отчёт");
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ??
new DailyReportDto
{
IdWell = idWell,
Date = dateDailyReport
};
switch (editableBlock)
{
case SubsystemBlockDto subsystemBlock:
dailyReport.SubsystemBlock = subsystemBlock;
break;
case TimeBalanceBlockDto timeBalanceBlock:
dailyReport.TimeBalanceBlock = timeBalanceBlock;
break;
case SignBlockDto signBlock:
dailyReport.SignBlock = signBlock;
break;
default:
throw new InvalidOperationException("Unexpected type of editableBlock");
}
editableBlock.IdUser = idUser;
editableBlock.LastUpdateDate = DateTime.UtcNow;
dailyReport.DateLastUpdate = DateTime.UtcNow;
if (dailyReport.Id == 0)
return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken);
return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken);
}
public async Task<DailyReportDto> GetAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken)
{
var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken);
if (well is null)
throw new ArgumentNullException(nameof(idWell), $"Скважина с Id: {idWell} не найдена");
if (!await IsDateDailyReportInRangeAsync(idWell, dateDailyReport, cancellationToken))
throw new ArgumentInvalidException(nameof(dateDailyReport), "Невозможно получить суточный отчёт");
var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateDailyReport, cancellationToken) ?? new DailyReportDto
{
Date = dateDailyReport.Date,
IdWell = well.Id
};
var factWellOperations = (await GetFactWellOperationsAsync(idWell, dailyReport.Date, cancellationToken))
.OrderBy(o => o.DateStart)
.ThenBy(o => o.DepthStart);
dailyReport.WellCaption = well.Caption;
dailyReport.WellType = well.WellType;
dailyReport.Cluster = well.Cluster;
dailyReport.Deposit = well.Deposit;
dailyReport.Customer = well.Companies.FirstOrDefault(c => c.IdCompanyType == 1)?.Caption;
dailyReport.Contractor = well.Companies.FirstOrDefault(c => c.IdCompanyType == 2)?.Caption;
dailyReport.DepthStart = factWellOperations.FirstOrDefault()?.DepthStart;
dailyReport.DepthEnd = factWellOperations.LastOrDefault()?.DepthEnd;
await UpdateTimeBalanceBlockAsync(dailyReport, factWellOperations, cancellationToken);
await UpdateSubsystemBlockAsync(dailyReport, cancellationToken);
await AddTrajectoryBlockAsync(dailyReport, cancellationToken);
await AddScheduleBlockAsync(dailyReport, cancellationToken);
await AddProcessMapWellDrillingBlockAsync(dailyReport, cancellationToken);
AddFactWellOperationBlock(dailyReport, factWellOperations);
return dailyReport;
}
public async Task<PaginationContainer<DailyReportDto>> GetAsync(int idWell, FileReportRequest request,
CancellationToken cancellationToken)
{
var result = new PaginationContainer<DailyReportDto>
{
Skip = request.Skip ?? 0,
Take = request.Take ?? 10,
Items = Enumerable.Empty<DailyReportDto>()
};
var datesRange = await GetDatesRangeAsync(idWell, cancellationToken);
if (datesRange is null)
return result;
var dailyReports = new List<DailyReportDto>();
var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, cancellationToken);
var factWellOperations = await GetFactWellOperationsAsync(idWell, null, cancellationToken);
if (request.GeDate.HasValue)
{
var startDate = new DateTime(request.GeDate.Value.Year, request.GeDate.Value.Month,
request.GeDate.Value.Day);
if (startDate.Date >= datesRange.From.Date)
datesRange.From = startDate;
}
if (request.LeDate.HasValue)
{
var finishDate = new DateTime(request.LeDate.Value.Year, request.LeDate.Value.Month,
request.LeDate.Value.Day);
if (finishDate.Date <= datesRange.To.Date)
datesRange.To = finishDate;
}
if (datesRange.From.AddDays(result.Skip) <= datesRange.To)
result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) -
Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays));
if (request.SortFields?.Contains("DateStart desc") == true)
{
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.To.AddDays(-day) >= datesRange.From; day++)
{
var dateDailyReport = datesRange.To.AddDays(-day).Date;
AddDailyReport(dateDailyReport);
}
}
else
{
for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++)
{
var dateDailyReport = datesRange.From.AddDays(day).Date;
AddDailyReport(dateDailyReport);
}
}
result.Items = dailyReports;
return result;
void AddDailyReport(DateTime date)
{
var dailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.Date == date)
?? new DailyReportDto
{
Date = date,
IdWell = idWell
};
AddFactWellOperationBlock(dailyReport, factWellOperations.Where(o => o.DateStart >= date && o.DateStart <= date.AddDays(1)));
dailyReports.Add(dailyReport);
}
}
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
{
var factOperations = await GetFactWellOperationsAsync(idWell, null, cancellationToken);
if (!factOperations.Any())
return null;
var minDateStart = factOperations.Min(o => o.DateStart).Date;
var maxDateStart = factOperations.Max(o => o.DateStart).Date;
return new DatesRangeDto
{
From = minDateStart.AddDays(1) <= DateTime.UtcNow ? minDateStart : DateTime.UtcNow.Date.AddDays(-1),
To = maxDateStart.AddDays(1) <= DateTime.UtcNow ? maxDateStart : DateTime.UtcNow.Date.AddDays(-1)
};
}
private async Task UpdateTimeBalanceBlockAsync(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations,
CancellationToken cancellationToken)
{
const int idWellOperationSlipsTime = 5011;
if (dailyReport.TimeBalanceBlock is not null)
{
dailyReport.TimeBalanceBlock.SectionName = wellOperationRepository.GetSectionTypes()
.FirstOrDefault(s => s.Id == dailyReport.TimeBalanceBlock.IdSection)?.Caption;
dailyReport.TimeBalanceBlock.WellOperationSlipsTimeCount = (await detectedOperationService.GetAsync(
new DetectedOperationRequest
{
IdsCategories = new[] { idWellOperationSlipsTime },
IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date,
LtDate = dailyReport.Date.AddHours(24)
}, cancellationToken))?.Stats.Sum(s => s.Count);
dailyReport.TimeBalanceBlock.WellDepth.Fact = factWellOperations
.Where(o => o.IdWellSectionType == dailyReport.TimeBalanceBlock.IdSection)
.Sum(o => o.DepthEnd - o.DepthStart);
}
}
private async Task AddTrajectoryBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{
var trajectory = (await trajectoryFactRepository.GetAsync(new TrajectoryGeoFactRequest
{
IdWell = dailyReport.IdWell,
GeDate = dailyReport.Date,
LeDate = dailyReport.Date.AddHours(24)
}, cancellationToken)).MaxBy(t => t.WellboreDepth);
dailyReport.TrajectoryBlock = new TrajectoryBlockDto
{
WellboreDepth = trajectory?.WellboreDepth,
VerticalDepth = trajectory?.VerticalDepth,
ZenithAngle = trajectory?.ZenithAngle,
AzimuthGeo = trajectory?.AzimuthGeo
};
}
private async Task AddScheduleBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) =>
dailyReport.ScheduleBlock = (await scheduleRepository.GetAsync(dailyReport.IdWell, dailyReport.Date, cancellationToken))
.Select(s => new ScheduleRecordDto
{
ShiftStart = s.ShiftStart,
ShiftEnd = s.ShiftEnd,
Name = s.Driller?.Name,
Surname = s.Driller?.Surname,
Patronymic = s.Driller?.Patronymic
});
private async Task UpdateSubsystemBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{
dailyReport.SubsystemBlock ??= new SubsystemBlockDto();
dailyReport.SubsystemBlock.Subsystems = await GetSubsystemsAsync();
async Task<IEnumerable<SubsystemRecordDto>> GetSubsystemsAsync()
{
var subsystemOperationTimesPerWell = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
{
IdWell = dailyReport.IdWell
}, cancellationToken);
var subsystemOperationTimesPerDay = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
{
IdWell = dailyReport.IdWell,
GtDate = dailyReport.Date,
LtDate = dailyReport.Date.AddHours(24)
}, cancellationToken);
var subsystems = subsystemOperationTimesPerWell
.Select(subsystemOperationTime => new SubsystemRecordDto
{
Name = subsystemOperationTime.SubsystemName,
UsagePerDay = subsystemOperationTimesPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemOperationTime.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
UsagePerWell = subsystemOperationTime.Adapt<SubsystemParametersDto>()
}).ToList();
if (dailyReport.SubsystemBlock?.Subsystems != null && dailyReport.SubsystemBlock.Subsystems.Any())
subsystems.AddRange(dailyReport.SubsystemBlock.Subsystems);
return subsystems.OrderBy(s => s.Name);
}
}
private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken)
{
dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell,
cancellationToken)).Where(p => p.DateStart >= dailyReport.Date &&
p.DateStart <= dailyReport.Date.AddHours(24))
.GroupBy(p => p.DrillingMode)
.Select(g => new ProcessMapWellDrillingRecordDto
{
DrillingMode = g.Key,
WellBoreDepth = g.Sum(p => p.DeltaDepth),
Rop = new PlanFactDto<double?>
{
Plan = g.Sum(p => p.Rop.Plan),
Fact = g.Sum(p => p.Rop.Fact)
},
MechDrillingHours = g.Sum(p => p.MechDrillingHours)
});
}
private static void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable<WellOperationDto> factWellOperations)
{
const int idWellOperationCategoryDrilling = 4001;
dailyReport.FactWellOperationBlock = new WellOperationBlockDto
{
WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory)
.Select(g => new WellOperationRecordDto
{
CategoryName = g.First().CategoryName,
DurationHours = g.Sum(o => o.DurationHours)
}),
SectionDrillingHours = factWellOperations
.Where(o => o.IdParentCategory is idWellOperationCategoryDrilling)
.Sum(o => o.DurationHours)
};
}
private Task<IEnumerable<WellOperationDto>> GetFactWellOperationsAsync(int idWell, DateTime? dateDailyReport, CancellationToken cancellationToken) =>
wellOperationRepository.GetAsync(new WellOperationRequest
{
IdWell = idWell,
OperationType = WellOperation.IdOperationTypeFact,
GeDate = dateDailyReport,
LtDate = dateDailyReport?.AddHours(24)
}, cancellationToken);
private async Task<bool> IsDateDailyReportInRangeAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken)
{
var datesRange = await GetDatesRangeAsync(idWell, cancellationToken);
return dateDailyReport >= datesRange?.From && dateDailyReport <= datesRange.To;
}
}