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; using AsbCloudApp.Data.Trajectory; namespace AsbCloudInfrastructure.Services.DailyReport; public class DailyReportService : IDailyReportService { private readonly IWellService wellService; private readonly ITrajectoryNnbRepository trajectoryFactNnbRepository; 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, ITrajectoryNnbRepository trajectoryFactNnbRepository, IDailyReportRepository dailyReportRepository, IScheduleRepository scheduleRepository, IWellOperationRepository wellOperationRepository, ISubsystemOperationTimeService subsystemOperationTimeService, IProcessMapReportWellDrillingService processMapReportWellDrillingService, IDetectedOperationService detectedOperationService) { this.wellService = wellService; this.trajectoryFactNnbRepository = trajectoryFactNnbRepository; this.dailyReportRepository = dailyReportRepository; this.scheduleRepository = scheduleRepository; this.wellOperationRepository = wellOperationRepository; this.subsystemOperationTimeService = subsystemOperationTimeService; this.processMapReportWellDrillingService = processMapReportWellDrillingService; this.detectedOperationService = detectedOperationService; } public async Task UpdateOrInsertAsync(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 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> GetAsync(int idWell, FileReportRequest request, CancellationToken cancellationToken) { var result = new PaginationContainer { Skip = request.Skip ?? 0, Take = request.Take ?? 10, Items = Enumerable.Empty() }; var datesRange = await GetDatesRangeAsync(idWell, cancellationToken); if (datesRange is null) return result; var dailyReports = new List(); 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 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 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 trajectoryFactNnbRepository.GetByRequestAsync(new TrajectoryRequest { 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> 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(), UsagePerWell = subsystemOperationTime.Adapt() }).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 { 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 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> 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 IsDateDailyReportInRangeAsync(int idWell, DateTime dateDailyReport, CancellationToken cancellationToken) { var datesRange = await GetDatesRangeAsync(idWell, cancellationToken); return dateDailyReport >= datesRange?.From && dateDailyReport <= datesRange.To; } }