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 InsertAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken) { if (await dailyReportRepository.AnyAsync(idWell, dateStart, cancellationToken)) throw new ArgumentInvalidException(nameof(dateStart), "Суточный отчёт уже существует"); var dailyReport = new DailyReportDto { IdWell = idWell, DateStart = dateStart, }; return await dailyReportRepository.InsertAsync(dailyReport, cancellationToken); } public async Task UpdateBlockAsync(int idDailyReport, int idUser, TBlock editableBlock, CancellationToken cancellationToken) where TBlock : EditableBlock { var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idDailyReport, cancellationToken) ?? throw new ArgumentInvalidException(nameof(idDailyReport), $"Суточный отчёт с Id: {idDailyReport} не найден"); editableBlock.IdUser = idUser; dailyReport.DateLastUpdate = DateTime.UtcNow; switch (editableBlock) { case SubsystemBlockDto subsystemBlock: dailyReport.SubsystemBlock = subsystemBlock; break; case TimeBalanceBlockDto timeBalanceBlock: dailyReport.TimeBalanceBlock = timeBalanceBlock; break; case SignBlockDto signBlock: dailyReport.SignBlock = signBlock; break; } return await dailyReportRepository.UpdateAsync(dailyReport, cancellationToken); } public async Task GetAsync(int idWell, DateTime dateStart, CancellationToken cancellationToken) { var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken); if (well is null) throw new ArgumentInvalidException($"Скважина с Id: {idWell} не найдена", nameof(idWell)); var dailyReport = await dailyReportRepository.GetOrDefaultAsync(idWell, dateStart, cancellationToken) ?? new DailyReportDto { DateStart = dateStart.Date, IdWell = well.Id }; var factWellOperations = await GetFactWellOperationsAsync(idWell, dailyReport.DateStart, dailyReport.DateEnd, cancellationToken); dailyReport.WellName = 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.Min(o => o.DepthStart); dailyReport.DepthEnd = factWellOperations.Max(o => o.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 well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) ?? throw new ArgumentInvalidException(nameof(idWell), "Скважина не найдена"); var datesRange = await GetDatesRangeAsync(idWell, cancellationToken); if (datesRange is null) return result; var dailyReports = new List(); var existingDailyReports = await dailyReportRepository.GetAsync(idWell, request, 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 dateStart = datesRange.To.AddDays(-day); AddDailyReport(dateStart); } } else { for (var day = result.Skip; day - result.Skip < result.Take && datesRange.From.AddDays(day) <= datesRange.To; day++) { var dateStart = datesRange.From.AddDays(day); AddDailyReport(dateStart); } } result.Items = dailyReports; return result; void AddDailyReport(DateTime dateStart) { var existingDailyReport = existingDailyReports.FirstOrDefault(d => d.IdWell == idWell && d.DateStart == dateStart); if (existingDailyReport is not null) { dailyReports.Add(existingDailyReport); return; } dailyReports.Add(new DailyReportDto { DateStart = dateStart, IdWell = well.Id }); } } public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken) { var factOperations = await GetFactWellOperationsAsync(idWell, null, null, cancellationToken); if (!factOperations.Any()) return null; return new DatesRangeDto { From = factOperations.Min(o => o.DateStart).Date, To = factOperations.Max(o => o.DateStart).Date }; } private async Task UpdateTimeBalanceBlockAsync(DailyReportDto dailyReport, IEnumerable factWellOperations, CancellationToken cancellationToken) { const int idWellOperationSlipsTime = 5011; if (dailyReport.TimeBalanceBlock is not null) { dailyReport.TimeBalanceBlock.CountWellOperationSlipsTime = (await detectedOperationService.GetAsync( new DetectedOperationRequest { IdsCategories = new[] { idWellOperationSlipsTime }, IdWell = dailyReport.IdWell, GtDate = dailyReport.DateStart, LtDate = dailyReport.DateEnd }, cancellationToken))?.Stats.Sum(s => s.Count); dailyReport.TimeBalanceBlock.WellDepthFact = 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.DateStart, LtDate = dailyReport.DateEnd }, cancellationToken)).LastOrDefault(); 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.DateStart, 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.Modules = await GetModulesAsync(); async Task> GetModulesAsync() { var modules = new List(); var statSubsystemOperationTimePerDaily = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest { IdWell = dailyReport.IdWell, GtDate = dailyReport.DateStart, LtDate = dailyReport.DateEnd }, cancellationToken)).Select(s => { var subsystemRecord = s.Adapt(); subsystemRecord.IdTimeInterval = 1; return subsystemRecord; }); var statSubsystemOperationTimePerWell = (await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest { IdWell = dailyReport.IdWell }, cancellationToken)).Select(s => { var subsystemRecord = s.Adapt(); subsystemRecord.IdTimeInterval = 2; return subsystemRecord; }); modules.AddRange(statSubsystemOperationTimePerDaily); modules.AddRange(statSubsystemOperationTimePerWell); if (dailyReport.SubsystemBlock?.Modules != null && dailyReport.SubsystemBlock.Modules.Any()) modules.AddRange(dailyReport.SubsystemBlock.Modules); return modules; } } private async Task AddProcessMapWellDrillingBlockAsync(DailyReportDto dailyReport, CancellationToken cancellationToken) { dailyReport.ProcessMapWellDrillingBlock = (await processMapReportWellDrillingService.GetAsync(dailyReport.IdWell, cancellationToken)).Where(p => p.DateStart >= dailyReport.DateStart && p.DateStart <= dailyReport.DateEnd && p.IdMode.HasValue) .GroupBy(p => p.IdMode) .Select(g => new ProcessMapWellDrillingRecordDto { IdMode = g.Key!.Value, WellBoreDepth = g.Sum(p => p.DeltaDepth), Rop = new PlanFactDto { Plan = g.Sum(p => p.RopPlan), Fact = g.Sum(p => p.RopFact) }, MechDrillingHours = g.Sum(p => p.MechDrillingHours) }); } private void AddFactWellOperationBlock(DailyReportDto dailyReport, IEnumerable factWellOperations) { const int idWellOperationCategoryDrilling = 4001; dailyReport.FactWellOperationBlock = new WellOperationBlockDto { WellOperations = factWellOperations.GroupBy(o => o.IdParentCategory) .Select(g => new WellOperationRecordDto { IdWellCategory = g.Key, DurationHours = g.Sum(o => o.DurationHours) }), DurationHoursDrillingPerSection = factWellOperations .Where(o => o.IdParentCategory is idWellOperationCategoryDrilling) .Sum(o => o.DurationHours) }; } private Task> GetFactWellOperationsAsync(int idWell, DateTime? dailyReportDateStart, DateTime? dailyReportDateEnd, CancellationToken cancellationToken) => wellOperationRepository.GetAsync(new WellOperationRequest { IdWell = idWell, OperationType = WellOperation.IdOperationTypeFact, GeDate = dailyReportDateStart, LtDate = dailyReportDateEnd }, cancellationToken); }