forked from ddrilling/AsbCloudServer
Рефакторинг сервисов парсинга
This commit is contained in:
parent
3865c330c2
commit
e361ccf4c1
@ -1,8 +1,19 @@
|
||||
namespace AsbCloudApp.Requests.ParserOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для параметров парсера
|
||||
/// Параметры парсинга
|
||||
/// </summary>
|
||||
public interface IParserOptionsRequest
|
||||
{
|
||||
private static DummyOptions empty => new();
|
||||
|
||||
private class DummyOptions : IParserOptionsRequest
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение пустого объекта опций
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IParserOptionsRequest Empty() => empty;
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
|
||||
namespace AsbCloudApp.Services.Parser;
|
||||
namespace AsbCloudApp.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Сервис парсинга файлов с доп. параметрами
|
||||
/// Сервис парсинга
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
/// <typeparam name="TOptions"></typeparam>
|
||||
public interface IParserServiceWithOptions<TDto, in TOptions>
|
||||
public interface IParserService<TDto, in TOptions> : IParserService
|
||||
where TDto : class, IId
|
||||
where TOptions : IParserOptionsRequest
|
||||
{
|
||||
@ -20,4 +22,17 @@ public interface IParserServiceWithOptions<TDto, in TOptions>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
ParserResultDto<TDto> Parse(Stream file, TOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Получение шаблона для заполнения
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Stream GetTemplateFile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сервис парсинга(интерфейс маркер)
|
||||
/// </summary>
|
||||
public interface IParserService
|
||||
{
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System.IO;
|
||||
using AsbCloudApp.Data;
|
||||
|
||||
namespace AsbCloudApp.Services.Parser;
|
||||
|
||||
/// <summary>
|
||||
/// Сервис парсинга файлов
|
||||
/// </summary>
|
||||
/// <typeparam name="TDto"></typeparam>
|
||||
public interface IParserService<TDto>
|
||||
where TDto : class, IId
|
||||
{
|
||||
/// <summary>
|
||||
/// Распарсить файл
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
ParserResultDto<TDto> Parse(Stream file);
|
||||
}
|
@ -4,17 +4,30 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudApp.Services;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace AsbCloudInfrastructure;
|
||||
|
||||
public static class XLParserExtensions
|
||||
public abstract class ParserServiceBase<TDto, TOptions> : IParserService<TDto, TOptions>
|
||||
where TDto : class, IId
|
||||
where TOptions : IParserOptionsRequest
|
||||
{
|
||||
public static ParserResultDto<TDto> Parse<TDto>(this IXLWorksheet sheet,
|
||||
protected readonly IServiceProvider serviceProvider;
|
||||
|
||||
protected ParserServiceBase(IServiceProvider serviceProvider)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public abstract ParserResultDto<TDto> Parse(Stream file, TOptions options);
|
||||
public abstract Stream GetTemplateFile();
|
||||
|
||||
protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet,
|
||||
Func<IXLRow, ValidationResultDto<TDto>> parseRow,
|
||||
int columnCount,
|
||||
int headerRowsCount = 0)
|
||||
where TDto : class, IId
|
||||
{
|
||||
if (sheet.RangeUsed().RangeAddress.LastAddress.ColumnNumber < columnCount)
|
||||
throw new FileFormatException($"Лист {sheet.Name} содержит меньшее количество столбцов.");
|
||||
@ -26,10 +39,10 @@ public static class XLParserExtensions
|
||||
|
||||
if (count <= 0)
|
||||
return new ParserResultDto<TDto>();
|
||||
|
||||
|
||||
var dtos = new List<ValidationResultDto<TDto>>(count);
|
||||
var warnings = new List<ValidationResult>();
|
||||
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var row = sheet.Row(1 + i + headerRowsCount);
|
||||
@ -45,7 +58,7 @@ public static class XLParserExtensions
|
||||
warnings.Add(warning);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var parserResult = new ParserResultDto<TDto>
|
||||
{
|
||||
Item = dtos
|
@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudApp.Services.Parser;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services;
|
||||
@ -12,30 +12,25 @@ public class ParserServiceFactory
|
||||
public const int IdTrajectoryFactManualParserService = 1;
|
||||
public const int IdTrajectoryPlanParserService = 2;
|
||||
|
||||
private readonly IDictionary<int, Func<object>> parsers = new Dictionary<int, Func<object>>
|
||||
{
|
||||
{ IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService() },
|
||||
{ IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService() }
|
||||
};
|
||||
private readonly IDictionary<int, Func<IParserService>> parsers;
|
||||
|
||||
public IParserService<TDto> GetParser<TDto>(int idParserService)
|
||||
where TDto : class, IId
|
||||
public ParserServiceFactory(IServiceProvider serviceProvider)
|
||||
{
|
||||
if (!parsers.TryGetValue(idParserService, out var parserService))
|
||||
throw new ArgumentNullException(nameof(idParserService), "Сервис не зарегистрирован");
|
||||
|
||||
return parserService.Invoke() as IParserService<TDto>
|
||||
?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа");
|
||||
parsers = new Dictionary<int, Func<IParserService>>
|
||||
{
|
||||
{ IdTrajectoryPlanParserService, () => new TrajectoryPlanParserService(serviceProvider) },
|
||||
{ IdTrajectoryFactManualParserService, () => new TrajectoryFactManualParserService(serviceProvider) }
|
||||
};
|
||||
}
|
||||
|
||||
public IParserServiceWithOptions<TDto, TOptions> GetParserWithOptions<TDto, TOptions>(int idParserService)
|
||||
public IParserService<TDto, TOptions> Create<TDto, TOptions>(int idParserService)
|
||||
where TDto : class, IId
|
||||
where TOptions : IParserOptionsRequest
|
||||
{
|
||||
if (!parsers.TryGetValue(idParserService, out var parserService))
|
||||
throw new ArgumentNullException(nameof(idParserService), "Сервис не зарегистрирован");
|
||||
throw new ArgumentNullException(nameof(idParserService), "Не правильный идентификатор парсера");
|
||||
|
||||
return parserService.Invoke() as IParserServiceWithOptions<TDto, TOptions>
|
||||
return parserService.Invoke() as IParserService<TDto, TOptions>
|
||||
?? throw new ArgumentNullException(nameof(idParserService), "Ошибка приведения типа");
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using AsbCloudApp.Data;
|
||||
using System;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
@ -7,7 +8,13 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
public class TrajectoryFactManualParserService : TrajectoryParserService<TrajectoryGeoFactDto>
|
||||
{
|
||||
protected override string SheetName => "Фактическая траектория";
|
||||
protected override string TemplateFileName => "TrajectoryFactManualTemplate.xlsx";
|
||||
|
||||
public TrajectoryFactManualParserService(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ValidationResultDto<TrajectoryGeoFactDto> ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoFactDto
|
||||
@ -22,9 +29,11 @@ public class TrajectoryFactManualParserService : TrajectoryParserService<Traject
|
||||
|
||||
//TODO: Добавить валидацию модели
|
||||
|
||||
return new ValidationResultDto<TrajectoryGeoFactDto>
|
||||
var validationResult = new ValidationResultDto<TrajectoryGeoFactDto>
|
||||
{
|
||||
Item = trajectoryRow
|
||||
};
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
@ -1,23 +1,36 @@
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using System;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Services.Parser;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
|
||||
public abstract class TrajectoryParserService<T> : IParserService<T>
|
||||
public abstract class TrajectoryParserService<T> : ParserServiceBase<T, IParserOptionsRequest>
|
||||
where T : TrajectoryGeoDto
|
||||
{
|
||||
private const int HeaderRowsCount = 2;
|
||||
private const int ColumnCount = 6;
|
||||
|
||||
protected TrajectoryParserService(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract string SheetName { get; }
|
||||
|
||||
protected abstract string TemplateFileName { get; }
|
||||
|
||||
protected abstract ValidationResultDto<T> ParseRow(IXLRow row);
|
||||
|
||||
public ParserResultDto<T> Parse(Stream file)
|
||||
public override Stream GetTemplateFile() =>
|
||||
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateFileName)
|
||||
?? throw new ArgumentNullException($"Файл '{TemplateFileName}' не найден");
|
||||
|
||||
public override ParserResultDto<T> Parse(Stream file, IParserOptionsRequest options)
|
||||
{
|
||||
using var workbook = new XLWorkbook(file, XLEventTracking.Disabled);
|
||||
|
||||
@ -25,7 +38,7 @@ public abstract class TrajectoryParserService<T> : IParserService<T>
|
||||
ws.Name.ToLower().Trim() == SheetName.ToLower().Trim())
|
||||
?? throw new FileFormatException($"Книга excel не содержит листа {SheetName}.");
|
||||
|
||||
var trajectoryRows = sheet.Parse(ParseRow, ColumnCount, HeaderRowsCount);
|
||||
var trajectoryRows = ParseExcelSheet(sheet, ParseRow, ColumnCount, HeaderRowsCount);
|
||||
return trajectoryRows;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using AsbCloudApp.Data;
|
||||
using System;
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
@ -7,7 +8,13 @@ namespace AsbCloudInfrastructure.Services.Trajectory.Parser;
|
||||
public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeoPlanDto>
|
||||
{
|
||||
protected override string SheetName => "Плановая траектория";
|
||||
protected override string TemplateFileName => "TrajectoryPlanTemplate.xlsx";
|
||||
|
||||
public TrajectoryPlanParserService(IServiceProvider serviceProvider)
|
||||
: base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ValidationResultDto<TrajectoryGeoPlanDto> ParseRow(IXLRow row)
|
||||
{
|
||||
var trajectoryRow = new TrajectoryGeoPlanDto
|
||||
@ -23,9 +30,11 @@ public class TrajectoryPlanParserService : TrajectoryParserService<TrajectoryGeo
|
||||
|
||||
//TODO: Добавить валидацию модели
|
||||
|
||||
return new ValidationResultDto<TrajectoryGeoPlanDto>
|
||||
var validationResult = new ValidationResultDto<TrajectoryGeoPlanDto>
|
||||
{
|
||||
Item = trajectoryRow
|
||||
};
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
@ -90,7 +90,7 @@ internal static class XLExtentions
|
||||
catch
|
||||
{
|
||||
throw new FileFormatException(
|
||||
$"Лист '{cell.Worksheet.Name}'. Ячейка: ({cell.Address.RowNumber},{cell.Address.ColumnNumber}) содержит некорректное значение");
|
||||
$"Лист '{cell.Worksheet.Name}'. {cell.Address.RowNumber} строка содержит некорректное значение в {cell.Address.ColumnNumber} столбце");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AsbCloudApp.Data.Trajectory;
|
||||
using AsbCloudApp.Requests.ParserOptions;
|
||||
using AsbCloudInfrastructure.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace AsbCloudWebApi.Tests.Services.Trajectory;
|
||||
@ -8,8 +12,20 @@ namespace AsbCloudWebApi.Tests.Services.Trajectory;
|
||||
public class TrajectoryParserTest
|
||||
{
|
||||
private const string UsingTemplateFile = "AsbCloudWebApi.Tests.Services.Trajectory.Templates";
|
||||
|
||||
private readonly IServiceProvider serviceProviderMock = Substitute.For<IServiceProvider, ISupportRequiredService>();
|
||||
private readonly IServiceScope serviceScopeMock = Substitute.For<IServiceScope>();
|
||||
private readonly IServiceScopeFactory serviceScopeFactoryMock = Substitute.For<IServiceScopeFactory>();
|
||||
|
||||
private static readonly ParserServiceFactory parserServiceFactory = new();
|
||||
private readonly ParserServiceFactory parserServiceFactory;
|
||||
|
||||
public TrajectoryParserTest()
|
||||
{
|
||||
serviceScopeFactoryMock.CreateScope().Returns(serviceScopeMock);
|
||||
((ISupportRequiredService)serviceProviderMock).GetRequiredService(typeof(IServiceScopeFactory)).Returns(serviceScopeFactoryMock);
|
||||
|
||||
parserServiceFactory = new ParserServiceFactory(serviceProviderMock);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_trajectory_plan()
|
||||
@ -20,8 +36,10 @@ public class TrajectoryParserTest
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var parserService = parserServiceFactory.GetParser<TrajectoryGeoPlanDto>(ParserServiceFactory.IdTrajectoryPlanParserService);
|
||||
var trajectoryRows = parserService.Parse(stream);
|
||||
var parserService = parserServiceFactory.Create<TrajectoryGeoPlanDto, IParserOptionsRequest>(
|
||||
ParserServiceFactory.IdTrajectoryPlanParserService);
|
||||
|
||||
var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty());
|
||||
|
||||
Assert.Equal(3, trajectoryRows.Item.Count());
|
||||
}
|
||||
@ -35,8 +53,10 @@ public class TrajectoryParserTest
|
||||
if (stream is null)
|
||||
Assert.Fail("Файла для импорта не существует");
|
||||
|
||||
var parserService = parserServiceFactory.GetParser<TrajectoryGeoFactDto>(ParserServiceFactory.IdTrajectoryFactManualParserService);
|
||||
var trajectoryRows = parserService.Parse(stream);
|
||||
var parserService = parserServiceFactory.Create<TrajectoryGeoFactDto, IParserOptionsRequest>(
|
||||
ParserServiceFactory.IdTrajectoryFactManualParserService);
|
||||
|
||||
var trajectoryRows = parserService.Parse(stream, IParserOptionsRequest.Empty());
|
||||
|
||||
Assert.Equal(4, trajectoryRows.Item.Count());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user