Initial_commit_SecMPS_v2
This commit is contained in:
210
api_sqlsugar/VolPro.Core/Generic/GenericExcelImportHelper.cs
Normal file
210
api_sqlsugar/VolPro.Core/Generic/GenericExcelImportHelper.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OfficeOpenXml;
|
||||
using VolPro.Core.Extensions;
|
||||
using VolPro.Core.UserManager;
|
||||
using VolPro.Core.Utilities;
|
||||
|
||||
namespace VolPro.Core.Generic
|
||||
{
|
||||
internal static class GenericExcelImportHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 从 Excel 流读取数据,按 CellOptions 校验表头和字典,
|
||||
/// 返回 WebResponseContent,成功时 Data 为 List<Dictionary<string,object>>
|
||||
/// </summary>
|
||||
public static WebResponseContent ReadRowsByCellOptions(string tableName, Stream excelStream, int importStartRowIndex = 1,
|
||||
HashSet<string> ignoreFields=null)
|
||||
{
|
||||
var response = WebResponseContent.Instance;
|
||||
var resultRows = new List<Dictionary<string, object>>();
|
||||
if (excelStream == null || !excelStream.CanRead)
|
||||
return response.Error("未能读取上传的文件".Translator());
|
||||
|
||||
var cellOptions = GetCellOptions(tableName);
|
||||
if (cellOptions == null || cellOptions.Count == 0)
|
||||
return response.Error($"未找到表【{tableName}】的导出配置".Translator());
|
||||
|
||||
using var package = new ExcelPackage(excelStream);
|
||||
var sheet = package.Workbook.Worksheets.FirstOrDefault();
|
||||
if (sheet?.Dimension == null || sheet.Dimension.End.Row <= importStartRowIndex)
|
||||
return response.Error("导入文件中没有数据".Translator());
|
||||
if (ignoreFields!=null)
|
||||
{
|
||||
cellOptions = cellOptions.Where(x => !ignoreFields.Contains(x.ColumnName)).ToList();
|
||||
}
|
||||
|
||||
if (!BindHeaderIndexes(sheet, cellOptions, importStartRowIndex, out string headerError))
|
||||
return response.Error(headerError);
|
||||
|
||||
var dicNoKeys = cellOptions
|
||||
.Where(x => !string.IsNullOrEmpty(x.DropNo) && x.KeyValues != null && x.KeyValues.Keys.Count > 0)
|
||||
.Select(x => (x.ColumnName, x.SearchType, x.EditType))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var multiSelectColumns = dicNoKeys
|
||||
.Where(x => x.SearchType == "checkbox" || x.SearchType == "selectList" || x.SearchType == "treeSelect"
|
||||
|| x.EditType == "checkbox" || x.EditType == "selectList" || x.EditType == "treeSelect")
|
||||
.Select(x => x.ColumnName)
|
||||
.ToArray();
|
||||
|
||||
// 日期字段类型(用于 Excel 数字日期转换)
|
||||
var dateFields = TableColumnContext.Data
|
||||
.Where(x => x.TableName == tableName && (x.ColumnType == "Date" || x.ColumnType == "DateTime"))
|
||||
.Select(s => s.ColumnName)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
int rowStart = importStartRowIndex + 1;
|
||||
int rowEnd = sheet.Dimension.End.Row;
|
||||
int colStart = sheet.Dimension.Start.Column;
|
||||
int colEnd = sheet.Dimension.End.Column;
|
||||
|
||||
for (int r = rowStart; r <= rowEnd; r++)
|
||||
{
|
||||
var rowDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
bool hasAnyValue = false;
|
||||
|
||||
for (int c = colStart; c <= colEnd; c++)
|
||||
{
|
||||
var opt = cellOptions.FirstOrDefault(x => x.Index == c);
|
||||
if (opt == null) continue;
|
||||
|
||||
string raw = sheet.Cells[r, c].Value?.ToString();
|
||||
raw = raw?.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(raw))
|
||||
{
|
||||
if (opt.Requierd)
|
||||
{
|
||||
string msg = "第[{$ts}]行,[{$ts}]验证未通过,不能为空"
|
||||
.TranslatorFormat(r, opt.ColumnCNName);
|
||||
return response.Error(msg);
|
||||
}
|
||||
|
||||
rowDict[opt.ColumnName] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
hasAnyValue = true;
|
||||
|
||||
// 日期校验与转换(参考 EPPlusHelper.ReadToDataTable 中的日期处理):
|
||||
// 如果当前字段在 dateFields 中,且值为长度 5 的数字(Excel 序列号),则转换成 DateTime。
|
||||
if (dateFields.Contains(opt.ColumnName)
|
||||
&& raw.Length == 5
|
||||
&& int.TryParse(raw, out int days))
|
||||
{
|
||||
var dt = new DateTime(1900, 1, 1).AddDays(days - 2);
|
||||
rowDict[opt.ColumnName] = dt;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(opt.DropNo))
|
||||
{
|
||||
rowDict[opt.ColumnName] = raw;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt.KeyValues == null)
|
||||
{
|
||||
return response.Error("[{$ts}]数据字典缺失".TranslatorFormat(opt.ColumnCNName));
|
||||
}
|
||||
|
||||
string key = null;
|
||||
if (multiSelectColumns.Contains(opt.ColumnName))
|
||||
{
|
||||
var cellValues = raw.Replace(",", ",")
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries);
|
||||
var keys = opt.KeyValues
|
||||
.Where(x => cellValues.Contains(x.Value))
|
||||
.Select(s => s.Key)
|
||||
.ToArray();
|
||||
if (cellValues.Length == keys.Length)
|
||||
{
|
||||
key = string.Join(",", keys);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key = opt.KeyValues
|
||||
.Where(x => x.Value == raw)
|
||||
.Select(s => s.Key)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
string values = string.Join(",",
|
||||
opt.KeyValues
|
||||
.Take(300)
|
||||
.Select(s => s.Value.Translator()));
|
||||
string msg = "第[{$ts}]行,[{$ts}]验证未通过,只能填写[{$ts}]"
|
||||
.TranslatorFormat(r, opt.ColumnCNName, values);
|
||||
return response.Error(msg);
|
||||
}
|
||||
rowDict[opt.ColumnName] = key;
|
||||
}
|
||||
|
||||
if (!hasAnyValue) continue;
|
||||
resultRows.Add(rowDict);
|
||||
}
|
||||
|
||||
return response.OK(null, resultRows);
|
||||
}
|
||||
|
||||
private static List<CellOptions> GetCellOptions(string tableName)
|
||||
{
|
||||
var mi = typeof(EPPlusHelper).GetMethod(
|
||||
"GetExportColumnInfo",
|
||||
BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (mi == null) return null;
|
||||
|
||||
return mi.Invoke(null, new object[] { tableName, false, false, null }) as List<CellOptions>;
|
||||
}
|
||||
|
||||
private static bool BindHeaderIndexes(
|
||||
ExcelWorksheet sheet,
|
||||
List<CellOptions> cellOptions,
|
||||
int headerRow,
|
||||
out string error)
|
||||
{
|
||||
error = null;
|
||||
|
||||
for (int j = sheet.Dimension.Start.Column, k = sheet.Dimension.End.Column; j <= k; j++)
|
||||
{
|
||||
string columnCNName = sheet.Cells[headerRow, j].Value?.ToString()?.Trim();
|
||||
if (string.IsNullOrEmpty(columnCNName)) continue;
|
||||
|
||||
var options = cellOptions
|
||||
.FirstOrDefault(x => x.ColumnCNName.Translator() == columnCNName);
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
error = "[{$ts}]不是模板中的列".TranslatorReplace(columnCNName, true);
|
||||
return false;
|
||||
}
|
||||
if (options.Index > 0)
|
||||
{
|
||||
error = "[{$ts}]列名重复".TranslatorReplace(columnCNName, true);
|
||||
return false;
|
||||
}
|
||||
options.Index = j;
|
||||
}
|
||||
|
||||
if (cellOptions.Exists(x => x.Index == 0))
|
||||
{
|
||||
var errorOps = cellOptions
|
||||
.Where(x => x.Index == 0)
|
||||
.Select(s => s.ColumnCNName.Translator() + "," + s.ColumnName);
|
||||
error = $"{"导入文件列未找到字段".Translator()}:{string.Join("; ", errorOps)}";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user