using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Data.WellOperationImport; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services.WellOperationImport; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.WellOperationImport.Constants; namespace AsbCloudInfrastructure.Services.WellOperationImport; public class WellOperationImportService : IWellOperationImportService { private readonly IEnumerable excelParsers; private readonly IWellOperationRepository wellOperationRepository; private static readonly DateTime dateLimitMin = new(2001, 1, 1, 0, 0, 0); private static readonly DateTime dateLimitMax = new(2099, 1, 1, 0, 0, 0); private static readonly TimeSpan drillingDurationLimitMax = TimeSpan.FromDays(366); public WellOperationImportService(IEnumerable excelParsers, IWellOperationRepository wellOperationRepository) { this.excelParsers = excelParsers; this.wellOperationRepository = wellOperationRepository; } public async Task ImportAsync(int idWell, int idUser, int idType, Stream stream, WellOperationParserOptionsDto options, bool deleteWellOperationsBeforeImport, CancellationToken cancellationToken) { var excelParser = excelParsers.FirstOrDefault(p => p.IdTemplate == options.IdTemplate && p.IdTypes.Contains(idType)) ?? throw new ArgumentInvalidException(nameof(options.IdTemplate), "Невозможно импортировать файл"); if (idType != WellOperation.IdOperationTypePlan && idType != WellOperation.IdOperationTypeFact) throw new ArgumentInvalidException(nameof(idType), "Операции не существует"); RowDto[] rows; var validationErrors = new List(); var sections = wellOperationRepository.GetSectionTypes(); var categories = wellOperationRepository.GetCategories(false); switch (options.IdTemplate) { case 0: options.SheetName = idType == WellOperation.IdOperationTypePlan ? DefaultTemplateInfo.SheetNamePlan : DefaultTemplateInfo.SheetNameFact; rows = excelParser.Parse(stream, options).ToArray(); break; default: if (string.IsNullOrWhiteSpace(options.SheetName)) throw new FileFormatException("Не указано название листа"); rows = excelParser.Parse(stream, options).ToArray(); break; } var operations = new List(); foreach (var row in rows) { try { var section = sections.FirstOrDefault(s => string.Equals(s.Caption, row.Section, StringComparison.CurrentCultureIgnoreCase)); if (section is null) throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить секцию"); var category = categories.FirstOrDefault(c => string.Equals(c.Name, row.Category, StringComparison.CurrentCultureIgnoreCase)); if (category is null) throw new FileFormatException($"Лист '{options.SheetName}'. В строке '{row.Number}' не удалось определить операцию"); if (row.DepthStart is not (>= 0d and <= 20_000d)) throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на начало операции"); if (row.DepthEnd is not (>= 0d and <= 20_000d)) throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная глубина на конец операции"); if (row.Date < dateLimitMin && row.Date > dateLimitMax) throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' неправильно получена дата начала операции"); if (operations.LastOrDefault()?.DateStart > row.Date) throw new FileFormatException($"Лист '{options.SheetName}' строка '{row.Number}' дата позднее даты предыдущей операции"); if (row.Duration is not (>= 0d and <= 240d)) throw new FileFormatException($"Лист '{options.SheetName}'. Строка '{row.Number}' некорректная длительность операции"); operations.Add(new WellOperationDto { IdWell = idWell, IdUser = idUser, IdType = idType, IdWellSectionType = section.Id, IdCategory = category.Id, CategoryInfo = row.CategoryInfo, DepthStart = row.DepthStart, DepthEnd = row.DepthEnd, DateStart = row.Date, DurationHours = row.Duration }); } catch (FileFormatException ex) { validationErrors.Add(ex.Message); } } if (operations.Any() && operations.Min(o => o.DateStart) - operations.Max(o => o.DateStart) > drillingDurationLimitMax) validationErrors.Add($"Лист {options.SheetName} содержит диапазон дат больше {drillingDurationLimitMax}"); if (validationErrors.Any()) throw new FileFormatException(string.Join("\r\n", validationErrors)); if(!operations.Any()) return; if (deleteWellOperationsBeforeImport) { var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest { IdWell = idWell }, cancellationToken); await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken); } await wellOperationRepository.InsertRangeAsync(operations, cancellationToken); } }