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; 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, Stream stream, WellOperationParserOptionsDto options, bool deleteBeforeImport, CancellationToken cancellationToken) { var excelParser = excelParsers.FirstOrDefault(p => p.IdTemplate == options.IdTemplate && p.IdTypes.Contains(options.IdType)) ?? throw new ArgumentInvalidException(nameof(options.IdTemplate), "Невозможно импортировать файл"); IEnumerable rows; var validationErrors = new List(); var sections = wellOperationRepository.GetSectionTypes(); var categories = wellOperationRepository.GetCategories(false); rows = options.IdTemplate switch { 0 => excelParser.Parse(stream, options), _ => excelParser.Parse(stream, options) }; 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 = options.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 (deleteBeforeImport) { var existingOperations = await wellOperationRepository.GetAsync(new WellOperationRequest { IdWell = idWell }, cancellationToken); await wellOperationRepository.DeleteAsync(existingOperations.Select(o => o.Id), cancellationToken); } await wellOperationRepository.InsertRangeAsync(operations, cancellationToken); } }