Files
KArtSell.Aegis/docs/Design/kbx-foundation-v36/backend/Shared/Excel/ClosedXmlWorkbookService.cs
T

114 lines
4.3 KiB
C#

using ClosedXML.Excel;
namespace Shared.Excel;
public sealed class ClosedXmlWorkbookService
{
public IReadOnlyList<string> ReadHeaders(Stream stream)
{
using var workbook = new XLWorkbook(stream);
var sheet = workbook.Worksheets.First();
var last = sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0;
if (last == 0) return [];
return Enumerable.Range(1, last)
.Select(i => sheet.Cell(1, i).GetFormattedString().Trim())
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToArray();
}
public IEnumerable<ImportRawRow> ReadRows(Stream stream, int maxRows)
{
using var workbook = new XLWorkbook(stream);
var sheet = workbook.Worksheets.First();
var lastColumn = sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0;
var lastRow = sheet.LastRowUsed()?.RowNumber() ?? 1;
var headers = Enumerable.Range(1, lastColumn)
.Select(i => sheet.Cell(1, i).GetFormattedString().Trim())
.ToArray();
if (lastRow - 1 > maxRows)
throw new InvalidOperationException($"최대 {maxRows:N0}행까지 업로드할 수 있습니다.");
for (var rowNo = 2; rowNo <= lastRow; rowNo++)
{
var values = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
var hasValue = false;
for (var column = 1; column <= lastColumn; column++)
{
var header = headers[column - 1];
if (string.IsNullOrWhiteSpace(header)) continue;
var value = sheet.Cell(rowNo, column).GetFormattedString().Trim();
if (!string.IsNullOrWhiteSpace(value)) hasValue = true;
values[header] = string.IsNullOrWhiteSpace(value) ? null : value;
}
if (hasValue) yield return new ImportRawRow(rowNo, values);
}
}
public MemoryStream CreateTemplate(ImportDefinition definition)
{
var workbook = new XLWorkbook();
var sheet = workbook.AddWorksheet("업로드");
var fields = definition.Fields.Where(x => x.Importable).ToArray();
for (var i = 0; i < fields.Length; i++)
{
var cell = sheet.Cell(1, i + 1);
cell.Value = fields[i].Label;
cell.Style.Font.Bold = true;
cell.Style.Fill.BackgroundColor = XLColor.LightGray;
cell.Comment.AddText(fields[i].Required ? "필수 입력" : "선택 입력");
}
sheet.SheetView.FreezeRows(1);
sheet.Columns(1, fields.Length).Width = 18;
sheet.Row(1).Height = 22;
sheet.Range(1, 1, 1, fields.Length).SetAutoFilter();
var help = workbook.AddWorksheet("필드설명");
help.Cell("A1").Value = "필드";
help.Cell("B1").Value = "필수";
help.Cell("C1").Value = "형식";
help.Cell("D1").Value = "허용 별칭";
help.Range("A1:D1").Style.Font.Bold = true;
for (var i = 0; i < fields.Length; i++)
{
var row = i + 2;
help.Cell(row, 1).Value = fields[i].Label;
help.Cell(row, 2).Value = fields[i].Required ? "Y" : "N";
help.Cell(row, 3).Value = fields[i].DataType;
help.Cell(row, 4).Value = string.Join(", ", fields[i].Aliases);
}
help.Columns().AdjustToContents(8, 35);
var output = new MemoryStream();
workbook.SaveAs(output);
output.Position = 0;
workbook.Dispose();
return output;
}
public MemoryStream CreateErrorWorkbook(
Stream source,
IReadOnlyDictionary<int, IReadOnlyList<ImportIssue>> issues)
{
using var workbook = new XLWorkbook(source);
var sheet = workbook.Worksheets.First();
var errorColumn = (sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0) + 1;
sheet.Cell(1, errorColumn).Value = "오류사유";
sheet.Cell(1, errorColumn).Style.Font.Bold = true;
foreach (var (rowNo, rowIssues) in issues)
{
sheet.Cell(rowNo, errorColumn).Value = string.Join(" | ", rowIssues.Select(x => x.Message));
}
sheet.Column(errorColumn).Width = 45;
var output = new MemoryStream();
workbook.SaveAs(output);
output.Position = 0;
return output;
}
}