Рефаткоринг инфраструктуры экспорта/импорта

This commit is contained in:
Степанов Дмитрий 2024-03-22 07:37:06 +03:00
parent 151e481a98
commit 1ccfa84e45
15 changed files with 316 additions and 11 deletions

View File

@ -3,12 +3,12 @@ using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
namespace AsbCloudApp.Services;
namespace AsbCloudApp.Services.Export;
/// <summary>
/// Экспорт данных
/// </summary>
public interface IExportService<in TOptions>
public interface IExportService<in TOptions> : IExportService
where TOptions : IExportOptionsRequest
{
/// <summary>
@ -18,4 +18,12 @@ public interface IExportService<in TOptions>
/// <param name="token"></param>
/// <returns></returns>
Task<(string FileName, Stream File)> ExportAsync(TOptions options, CancellationToken token);
}
/// <summary>
/// Экспорт данных
/// </summary>
public interface IExportService
{
}

View File

@ -0,0 +1,20 @@
using AsbCloudApp.Requests.ExportOptions;
namespace AsbCloudApp.Services.Export;
/// <summary>
/// Фабрика создания сервисов для экспорта
/// </summary>
/// <typeparam name="TId"></typeparam>
public interface IExportServiceFactory<in TId>
where TId : struct
{
/// <summary>
/// Создать сервис экспорта
/// </summary>
/// <param name="id"></param>
/// <typeparam name="TOptions"></typeparam>
/// <returns></returns>
IExportService<TOptions> CreateExportService<TOptions>(TId id)
where TOptions : IExportOptionsRequest;
}

View File

@ -0,0 +1,23 @@
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
namespace AsbCloudApp.Services.Parsers;
/// <summary>
/// Фабрика для создания сервиса парсинга
/// </summary>
/// <typeparam name="TId"></typeparam>
/// <typeparam name="TDto"></typeparam>
public interface IParserFactory<in TId, TDto>
where TId : struct
where TDto : class, IId
{
/// <summary>
/// Создать парсер
/// </summary>
/// <param name="id"></param>
/// <typeparam name="TOptions"></typeparam>
/// <returns></returns>
IParserService<TDto, TOptions> CreateParser<TOptions>(TId id)
where TOptions : IParserOptionsRequest;
}

View File

@ -2,14 +2,14 @@ using System.IO;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
namespace AsbCloudApp.Services;
namespace AsbCloudApp.Services.Parsers;
/// <summary>
/// Сервис парсинга
/// </summary>
/// <typeparam name="TDto"></typeparam>
/// <typeparam name="TOptions"></typeparam>
public interface IParserService<TDto, in TOptions>
public interface IParserService<TDto, in TOptions> : IParserService
where TDto : class, IId
where TOptions : IParserOptionsRequest
{
@ -26,4 +26,11 @@ public interface IParserService<TDto, in TOptions>
/// </summary>
/// <returns></returns>
Stream GetTemplateFile();
}
/// <summary>
/// Сервис парсинга
/// </summary>
public interface IParserService
{
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services.Export;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using ClosedXML.Excel;
using Mapster;
namespace AsbCloudInfrastructure.Services.ExcelServices;
public abstract class ExcelExportService<TDto, TOptions, TTemplate> : IExportService<TOptions>
where TOptions : IExportOptionsRequest
where TTemplate : class, ITemplateParameters, new()
{
protected TTemplate TemplateParameters => new();
protected abstract Task<string> BuildFileNameAsync(TOptions options, CancellationToken token);
protected abstract Task<IEnumerable<TDto>> GetDtosAsync(TOptions options, CancellationToken token);
public async Task<(string FileName, Stream File)> ExportAsync(TOptions options, CancellationToken token)
{
var dtos = await GetDtosAsync(options, token);
var fileName = await BuildFileNameAsync(options, token);
var file = BuildFile(dtos);
return (fileName, file);
}
private Stream BuildFile(IEnumerable<TDto> dtos)
{
using var template = GetTemplateFile();
using var workbook = new XLWorkbook(template);
AddDtosToWorkbook(workbook, dtos);
var memoryStream = new MemoryStream();
workbook.SaveAs(memoryStream, new SaveOptions { });
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
private void AddDtosToWorkbook(XLWorkbook workbook, IEnumerable<TDto> dtos)
{
var dtosToArray = dtos.ToArray();
if (!dtosToArray.Any())
return;
var sheet = workbook.GetWorksheet(TemplateParameters.SheetName);
for (var i = 0; i < dtosToArray.Length; i++)
{
var row = sheet.Row(1 + i + TemplateParameters.HeaderRowsCount);
AddRow(row, dtosToArray[i]);
}
}
private void AddRow(IXLRow xlRow, TDto dto)
{
var properties = dto.Adapt<IDictionary<string, object>>();
foreach (var (name, cellValue) in properties)
{
if (TemplateParameters.Cells.TryGetValue(name, out var cell))
xlRow.Cell(cell.ColumnNumber).SetCellValue(cellValue);
}
}
private Stream GetTemplateFile() =>
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateParameters.FileName)
?? throw new ArgumentNullException($"Файл '{TemplateParameters.FileName}' не найден");
}

View File

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Reflection;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Services.Parsers;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using ClosedXML.Excel;
using Mapster;
namespace AsbCloudInfrastructure.Services.ExcelServices;
public abstract class ExcelParser<TDto, TOptions, TTemplate> : IParserService<TDto, TOptions>
where TDto : class, IValidatableObject, IId
where TOptions : IParserOptionsRequest
where TTemplate : class, ITemplateParameters, new()
{
protected TTemplate TemplateParameters => new();
public virtual ParserResultDto<TDto> Parse(Stream file, TOptions options)
{
using var workbook = new XLWorkbook(file);
var sheet = workbook.GetWorksheet(TemplateParameters.SheetName);
var dtos = ParseExcelSheet(sheet);
return dtos;
}
public virtual Stream GetTemplateFile() =>
Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateParameters.FileName)
?? throw new ArgumentNullException($"Файл '{TemplateParameters.FileName}' не найден");
protected virtual IDictionary<string, object?> ParseRow(IXLRow xlRow)
{
var cells = TemplateParameters.Cells.ToDictionary(x => x.Key, x =>
{
var columnNumber = x.Value.ColumnNumber;
var xlCell = xlRow.Cell(columnNumber);
var cellValue = x.Value.GetValueFromCell(xlCell);
return cellValue;
});
return cells;
}
protected virtual TDto BuildDto(IDictionary<string, object?> row, int rowNumber)
{
var dto = row.Adapt<TDto>();
return dto;
}
private ValidationResultDto<TDto> Validate(TDto dto, int rowNumber)
{
var validationResults = new List<ValidationResult>();
var isValid = dto.Validate(validationResults);
if (isValid)
{
var validDto = new ValidationResultDto<TDto>
{
Item = dto
};
return validDto;
}
var columnsDict = TemplateParameters.Cells.ToDictionary(x => x.Key, x => x.Value.ColumnNumber);
var invalidDto = new ValidationResultDto<TDto>
{
Item = dto,
Warnings = validationResults
.SelectMany(v => v.MemberNames
.Where(columnsDict.ContainsKey)
.Select(m =>
{
var columnNumber = columnsDict[m];
var errorMessage = v.ErrorMessage;
var warningMessage = string.Format(XLExtentions.ProblemDetailsTemplate,
TemplateParameters.SheetName,
rowNumber,
columnNumber,
errorMessage);
var warning = new ValidationResult(warningMessage, new[] { m });
return warning;
}))
};
return invalidDto;
}
protected virtual ParserResultDto<TDto> ParseExcelSheet(IXLWorksheet sheet)
{
var count = sheet.RowsUsed().Count() - TemplateParameters.HeaderRowsCount;
if (count <= 0)
return new ParserResultDto<TDto>();
var valiationResults = new List<ValidationResultDto<TDto>>(count);
var warnings = new List<ValidationResult>();
for (var i = 0; i < count; i++)
{
var xlRow = sheet.Row(1 + i + TemplateParameters.HeaderRowsCount);
var rowNumber = xlRow.RowNumber();
try
{
var row = ParseRow(xlRow);
var dto = BuildDto(row, rowNumber);
var validationResult = Validate(dto, rowNumber);
valiationResults.Add(validationResult);
}
catch (FileFormatException ex)
{
var warning = new ValidationResult(ex.Message);
warnings.Add(warning);
}
}
var parserResult = new ParserResultDto<TDto>
{
Item = valiationResults
};
if (warnings.Any())
parserResult.Warnings = warnings;
return parserResult;
}
}

View File

@ -0,0 +1,23 @@
using System.ComponentModel.DataAnnotations;
using System.IO;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
namespace AsbCloudInfrastructure.Services.ExcelServices;
public abstract class ExcelWellRelatedParser<TDto, TOptions, TTemplate> : ExcelParser<TDto, TOptions, TTemplate>
where TDto : class, IValidatableObject, IId, IWellRelated
where TOptions : WellRelatedParserRequest
where TTemplate : class, ITemplateParameters, new()
{
public override ParserResultDto<TDto> Parse(Stream file, TOptions options)
{
var result = base.Parse(file, options);
foreach (var dto in result.Item)
dto.Item.IdWell = options.IdWell;
return result;
}
}

View File

@ -6,13 +6,14 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Export;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using ClosedXML.Excel;
using Mapster;
namespace AsbCloudInfrastructure.Services.ExcelServices;
[Obsolete]
public abstract class ExportExcelService<TDto, TOptions> : IExportService<TOptions>
where TOptions : IExportOptionsRequest
{

View File

@ -6,13 +6,14 @@ using System.Linq;
using System.Reflection;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Services;
using AsbCloudApp.Services.Parsers;
using AsbCloudInfrastructure.Services.ExcelServices.Templates;
using ClosedXML.Excel;
using Mapster;
namespace AsbCloudInfrastructure.Services.ExcelServices;
[Obsolete]
public abstract class ParserExcelService<TDto, TOptions> : IParserService<TDto, TOptions>
where TDto : class, IValidatableObject, IId
where TOptions : IParserOptionsRequest

View File

@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation;
namespace AsbCloudInfrastructure.Services.ProcessMaps.Report;
@ -57,7 +58,7 @@ public class ProcessMapReportDrillingService : IProcessMapReportDrillingService
var requestWellOperationFact = new WellOperationRequest()
{
IdWell = idWell,
IdsWell = new[] { idWell },
OperationType = WellOperation.IdOperationTypeFact,
GeDepth = geDepth,
LeDepth = leDepth

View File

@ -9,6 +9,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data.WellOperation;
namespace AsbCloudInfrastructure.Services.WellOperationService
{
@ -89,7 +90,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
{
var row = sheet.Row(i + headerRowsCount);
SetCell(row, columnRowNumber, $"{i}");
SetCell(row, columnCaption, $"{tvdItem.CategoryName} {tvdItem.CategoryInfo}".Trim());
SetCell(row, columnCaption, $"{tvdItem.OperationCategoryName} {tvdItem.CategoryInfo}".Trim());
SetCell(row, columnWellDepthStart, tvdItem.DepthStart);
SetCell(row, columnWellDepthEnd, tvdItem.DepthEnd);
SetCell(row, columnDuration, tvdItem.DurationHours);
@ -150,7 +151,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
var row = sheet.Row(1 + i + headerRowsCount);
SetCell(row, columnRowNumber, $"{1 + i}");
SetCell(row, columnCaption, $"{operation.CategoryName} {operation.CategoryInfo}".Trim());
SetCell(row, columnCaption, $"{operation.OperationCategoryName} {operation.CategoryInfo}".Trim());
SetCell(row, columnWellDepthStartPlan, tvdItem.Plan?.DepthStart);
SetCell(row, columnWellDepthStartFact, tvdItem.Fact?.DepthStart);

View File

@ -17,10 +17,16 @@ public static class XLExtentions
public static IXLCell SetCellValue<T>(this IXLCell cell, T value, string? format = null)
{
if (typeof(T) == typeof(DateTime))
if (value is DateTime || value is DateTimeOffset)
{
cell.Style.DateFormat.Format = format ?? "DD.MM.YYYY HH:MM:SS";
}
if (value is DateTimeOffset dateTimeOffset)
{
cell.Value = XLCellValue.FromObject(dateTimeOffset.DateTime);
return cell;
}
}
cell.Value = XLCellValue.FromObject(value);

View File

@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;

View File

@ -16,6 +16,8 @@ using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Data.ProcessMaps;
using System.ComponentModel.DataAnnotations;
using AsbCloudApp.Requests.ExportOptions;
using AsbCloudApp.Services.Export;
using AsbCloudApp.Services.Parsers;
namespace AsbCloudWebApi.Controllers.ProcessMaps;

View File

@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Requests.ParserOptions;
using AsbCloudApp.Services.Parsers;
using AsbCloudInfrastructure.Services.Trajectory.Export;
namespace AsbCloudWebApi.Controllers.Trajectory