You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
15 KiB
340 lines
15 KiB
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); |
|
} |
|
} |
|
}
|
|
|