|
|
|
|
using Infrastructure.Abstructions;
|
|
|
|
|
using Infrastructure.Excel.Attributes;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using NPOI.HSSF.UserModel;
|
|
|
|
|
using NPOI.SS.UserModel;
|
|
|
|
|
using NPOI.XSSF.UserModel;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace Infrastructure.Excel
|
|
|
|
|
{
|
|
|
|
|
public class Parser:ITransientDependency,IParser
|
|
|
|
|
{
|
|
|
|
|
public IWorkbook ReadExcel(string path)
|
|
|
|
|
{
|
|
|
|
|
if (!File.Exists(path))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("该文件不存在");
|
|
|
|
|
}
|
|
|
|
|
IWorkbook workBook = null;
|
|
|
|
|
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var fileType = Path.GetExtension(path);
|
|
|
|
|
if (fileType == ".xlsx")
|
|
|
|
|
{
|
|
|
|
|
//2007以上版本
|
|
|
|
|
workBook = new XSSFWorkbook(stream);
|
|
|
|
|
}
|
|
|
|
|
else if (fileType == ".xls")
|
|
|
|
|
{
|
|
|
|
|
//2007一下版本
|
|
|
|
|
workBook = new HSSFWorkbook(stream);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("非Excel文件不可解析");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return workBook;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<T> ParseExcel<T>(IWorkbook workbook) where T : class, new()
|
|
|
|
|
{
|
|
|
|
|
var type = typeof(T);
|
|
|
|
|
var sheetName = GetSheetName(type);
|
|
|
|
|
var sheet = workbook.GetSheet(sheetName);
|
|
|
|
|
if (sheet == null)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception($"该Excel表格没有名为{sheetName}的sheet");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = ParseSheet<T>(workbook, sheet);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IWorkbook BuildWorkbook(Type type)
|
|
|
|
|
{
|
|
|
|
|
var workbook = new XSSFWorkbook();
|
|
|
|
|
BuildSheet(type, workbook);
|
|
|
|
|
return workbook;
|
|
|
|
|
}
|
|
|
|
|
public ISheet BuildSheet(Type type, IWorkbook book)
|
|
|
|
|
{
|
|
|
|
|
var sheetName = GetSheetName(type);
|
|
|
|
|
var tableColumns = GetTableColumns(type);
|
|
|
|
|
var sheet = book.CreateSheet(sheetName);
|
|
|
|
|
var depth = tableColumns.Max(c => c.Depth);
|
|
|
|
|
var firstRow = sheet.CreateRow(0);
|
|
|
|
|
var cellIndex = 0;
|
|
|
|
|
foreach (var columnInfo in tableColumns)
|
|
|
|
|
{
|
|
|
|
|
SetTableColumns(sheet, firstRow, columnInfo, ref cellIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sheet;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetTableColumns(ISheet sheet, IRow row, TableColumnInfo columnInfo, ref int cellIndex)
|
|
|
|
|
{
|
|
|
|
|
var cell = row.CreateCell(cellIndex++);
|
|
|
|
|
cell.SetCellValue(columnInfo.Attribute.Name);
|
|
|
|
|
if (columnInfo.ChildHeaderInfos != null && columnInfo.ChildHeaderInfos.Any())
|
|
|
|
|
{
|
|
|
|
|
var nextRow = sheet.CreateRow(row.RowNum++);
|
|
|
|
|
foreach (var childColumnInfo in columnInfo.ChildHeaderInfos)
|
|
|
|
|
{
|
|
|
|
|
SetTableColumns(sheet, nextRow, childColumnInfo, ref cellIndex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private List<TType> ParseSheet<TType>(IWorkbook workBook, ISheet sheet)
|
|
|
|
|
{
|
|
|
|
|
var type = typeof(TType);
|
|
|
|
|
var result = new List<object>();
|
|
|
|
|
var tableHeaders = GetTableHeaders(type);
|
|
|
|
|
var tableColumns = GetTableColumns(type);
|
|
|
|
|
var depth = tableHeaders.Count + tableColumns.Max(c => c.Depth);
|
|
|
|
|
var columnInfos = GetColumnInfos(tableColumns);
|
|
|
|
|
var rowCount = sheet.LastRowNum;
|
|
|
|
|
var cellCount = sheet.GetRow(depth)?.LastCellNum;
|
|
|
|
|
if (!cellCount.HasValue)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("该Excel文档没有数据");
|
|
|
|
|
}
|
|
|
|
|
for (var i = depth; i <= rowCount; i++)
|
|
|
|
|
{
|
|
|
|
|
var rootInstance = System.Activator.CreateInstance(type);
|
|
|
|
|
var row = sheet.GetRow(i);
|
|
|
|
|
var instanceDict = new Dictionary<Type, object> { { type, rootInstance } };
|
|
|
|
|
bool allBlank = true;
|
|
|
|
|
for (int j = 0; j < cellCount; j++)
|
|
|
|
|
{
|
|
|
|
|
var cell = row.GetCell(j);
|
|
|
|
|
if (cell == null || cell.CellType == CellType.Blank)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allBlank = false;
|
|
|
|
|
var columnInfo = columnInfos[j];
|
|
|
|
|
object instance = null;
|
|
|
|
|
if (!instanceDict.ContainsKey(columnInfo.Property.ReflectedType))
|
|
|
|
|
{
|
|
|
|
|
instance = System.Activator.CreateInstance(columnInfo.Property.ReflectedType);
|
|
|
|
|
instanceDict.Add(columnInfo.Property.ReflectedType, instance);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
instance = instanceDict[columnInfo.Property.ReflectedType];
|
|
|
|
|
}
|
|
|
|
|
var baseType = Nullable.GetUnderlyingType(columnInfo.Property.PropertyType);
|
|
|
|
|
baseType = baseType == null ? columnInfo.Property.PropertyType : baseType;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (cell.CellType == CellType.Numeric && DateUtil.IsValidExcelDate(cell.NumericCellValue) && DateUtil.IsCellDateFormatted(cell))//日期类型
|
|
|
|
|
{
|
|
|
|
|
var value = cell.DateCellValue;
|
|
|
|
|
columnInfo.Property.SetValue(instance,
|
|
|
|
|
Convert.ChangeType(value, baseType), null);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(cell.ToString()))
|
|
|
|
|
{
|
|
|
|
|
switch (cell.CellType)
|
|
|
|
|
{
|
|
|
|
|
case CellType.String:
|
|
|
|
|
columnInfo.Property.SetValue(instance, Convert.ChangeType(cell.StringCellValue, baseType), null);
|
|
|
|
|
break;
|
|
|
|
|
case CellType.Boolean:
|
|
|
|
|
columnInfo.Property.SetValue(instance, Convert.ChangeType(cell.BooleanCellValue, baseType), null);
|
|
|
|
|
break;
|
|
|
|
|
case CellType.Numeric:
|
|
|
|
|
{
|
|
|
|
|
columnInfo.Property.SetValue(instance,
|
|
|
|
|
DateUtil.IsCellDateFormatted(cell)
|
|
|
|
|
? Convert.ChangeType(cell.DateCellValue, baseType)
|
|
|
|
|
: Convert.ChangeType(cell.NumericCellValue, baseType), null);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CellType.Blank:
|
|
|
|
|
columnInfo.Property.SetValue(instance, Convert.ChangeType(cell.ToString().Trim(), baseType), null);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception("不支持的单元格式");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (allBlank)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
var baseTypePropertites = type.GetProperties(System.Reflection.BindingFlags.Instance |
|
|
|
|
|
System.Reflection.BindingFlags.Public).ToList();
|
|
|
|
|
foreach (var property in baseTypePropertites)
|
|
|
|
|
{
|
|
|
|
|
if (instanceDict.ContainsKey(property.PropertyType))
|
|
|
|
|
{
|
|
|
|
|
var subInstance = instanceDict[property.PropertyType];
|
|
|
|
|
property.SetValue(rootInstance, subInstance);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result.Add(rootInstance);
|
|
|
|
|
}
|
|
|
|
|
return result.OfType<TType>().ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string GetSheetName(Type type)
|
|
|
|
|
{
|
|
|
|
|
var importAttr = type.GetCustomAttributes(typeof(TableDtoAttribute), false).FirstOrDefault();
|
|
|
|
|
if (importAttr == null)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("非法类型,请检查该类型是否有Importable特性");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sheetName = ((TableDtoAttribute)importAttr).Name;
|
|
|
|
|
return sheetName;
|
|
|
|
|
}
|
|
|
|
|
private List<HeaderColumnInfo> GetColumnInfos(List<TableColumnInfo> tableColumnInfos)
|
|
|
|
|
{
|
|
|
|
|
var result = new List<HeaderColumnInfo>();
|
|
|
|
|
tableColumnInfos.ForEach(c =>
|
|
|
|
|
{
|
|
|
|
|
var columnInfos = GetColumnInfos(c, 0);
|
|
|
|
|
result.AddRange(columnInfos);
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<HeaderColumnInfo> GetColumnInfos(TableColumnInfo tableColumnInfo, int baseSort)
|
|
|
|
|
{
|
|
|
|
|
var result = new List<HeaderColumnInfo>();
|
|
|
|
|
if (tableColumnInfo.ChildHeaderInfos != null && tableColumnInfo.ChildHeaderInfos.Any())
|
|
|
|
|
{
|
|
|
|
|
var children = tableColumnInfo.ChildHeaderInfos.OrderBy(info => info.Attribute.Sort).ToList();
|
|
|
|
|
children.ForEach(child =>
|
|
|
|
|
{
|
|
|
|
|
var childColumnInfos = GetColumnInfos(child, tableColumnInfo.Attribute.Sort);
|
|
|
|
|
result.AddRange(childColumnInfos);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var columnInfo = new HeaderColumnInfo { Attribute = tableColumnInfo.Attribute, Property = tableColumnInfo.Property };
|
|
|
|
|
result.Add(columnInfo);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
private List<TableHeaderAttribute> GetTableHeaders(Type type)
|
|
|
|
|
{
|
|
|
|
|
var headerAttrObjs = type.GetCustomAttributes(typeof(TableHeaderAttribute), true).ToList();
|
|
|
|
|
var headerAttrs = from obj in headerAttrObjs
|
|
|
|
|
select (TableHeaderAttribute)obj;
|
|
|
|
|
return headerAttrs.ToList();
|
|
|
|
|
}
|
|
|
|
|
private List<TableColumnInfo> GetTableColumns(Type type)
|
|
|
|
|
{
|
|
|
|
|
var result = new List<TableColumnInfo>();
|
|
|
|
|
var properties = type.GetProperties(System.Reflection.BindingFlags.Instance |
|
|
|
|
|
System.Reflection.BindingFlags.Public).ToList();
|
|
|
|
|
if (properties == null || !properties.Any())
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("该类型没有公共属性供生成Excel");
|
|
|
|
|
}
|
|
|
|
|
foreach (var property in properties)
|
|
|
|
|
{
|
|
|
|
|
var columnAttrObj = property.GetCustomAttributes(typeof(ExcelColumnAttribute), true).FirstOrDefault();
|
|
|
|
|
if (columnAttrObj != null)
|
|
|
|
|
{
|
|
|
|
|
var header = new TableColumnInfo
|
|
|
|
|
{ Attribute = (ExcelColumnAttribute)columnAttrObj, Property = property, ChildHeaderInfos = new List<TableColumnInfo>() };
|
|
|
|
|
var propertyType = property.PropertyType;
|
|
|
|
|
var columnDtoAttr = propertyType.GetCustomAttributes(typeof(TableColumnDtoAttribute), true).FirstOrDefault();
|
|
|
|
|
if (columnDtoAttr != null)
|
|
|
|
|
{
|
|
|
|
|
var childHeaders = GetTableColumns(propertyType);
|
|
|
|
|
header.ChildHeaderInfos.AddRange(childHeaders);
|
|
|
|
|
}
|
|
|
|
|
result.Add(header);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var childImportDtoAttrObj = property.GetCustomAttributes(typeof(ChildImportDtoAttribute), true).FirstOrDefault();
|
|
|
|
|
if (childImportDtoAttrObj != null)
|
|
|
|
|
{
|
|
|
|
|
var childType = ((ChildImportDtoAttribute)childImportDtoAttrObj).DtoType;
|
|
|
|
|
var childImportAttr = childType.GetCustomAttributes(typeof(TableDtoAttribute), false).FirstOrDefault();
|
|
|
|
|
if (childImportAttr == null)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("非法类型,请检查该类型是否有Importable特性");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result.OrderBy(r => r.Attribute.Sort).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Tuple<List<HeaderColumnInfo>, List<ImportDtoInfo>> GetImportPropertiesAndDtos(Type importType)
|
|
|
|
|
{
|
|
|
|
|
var properties = importType.GetProperties(System.Reflection.BindingFlags.Instance |
|
|
|
|
|
System.Reflection.BindingFlags.Public).ToList();
|
|
|
|
|
if (properties == null || !properties.Any())
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("该类型没有公共属性供生成Excel");
|
|
|
|
|
}
|
|
|
|
|
var columnProperties = new List<HeaderColumnInfo>();
|
|
|
|
|
var childDtoAttrs = new List<ImportDtoInfo>();
|
|
|
|
|
foreach (var property in properties)
|
|
|
|
|
{
|
|
|
|
|
var columnAttrObj = property.GetCustomAttributes(typeof(ExcelColumnAttribute), true).FirstOrDefault();
|
|
|
|
|
if (columnAttrObj != null)
|
|
|
|
|
{
|
|
|
|
|
var headerInfo = new HeaderColumnInfo
|
|
|
|
|
{ Attribute = (ExcelColumnAttribute)columnAttrObj, Property = property };
|
|
|
|
|
columnProperties.Add(headerInfo);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var childImportDtoAttrObj = property.GetCustomAttributes(typeof(ChildImportDtoAttribute), true).FirstOrDefault();
|
|
|
|
|
if (childImportDtoAttrObj != null)
|
|
|
|
|
{
|
|
|
|
|
var childType = ((ChildImportDtoAttribute)childImportDtoAttrObj).DtoType;
|
|
|
|
|
var childImportAttr = childType.GetCustomAttributes(typeof(TableDtoAttribute), false).FirstOrDefault();
|
|
|
|
|
if (childImportAttr == null)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("非法类型,请检查该类型是否有Importable特性");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
childDtoAttrs.Add(new ImportDtoInfo
|
|
|
|
|
{ Attribute = (TableDtoAttribute)childImportAttr, Type = childType, Property = property });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return new Tuple<List<HeaderColumnInfo>, List<ImportDtoInfo>>(columnProperties, childDtoAttrs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|