119 lines
4.6 KiB
C#
119 lines
4.6 KiB
C#
using Laservall.Solidworks.Model;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Laservall.Solidworks.Server.Handlers
|
|
{
|
|
internal sealed class ExportHandler
|
|
{
|
|
private readonly Func<BomLoadResult> _getLastResult;
|
|
private readonly IBomDataProvider _dataProvider;
|
|
|
|
public ExportHandler(Func<BomLoadResult> getLastResult, IBomDataProvider dataProvider)
|
|
{
|
|
_getLastResult = getLastResult;
|
|
_dataProvider = dataProvider;
|
|
}
|
|
|
|
public async Task HandleExport(HttpListenerContext context, CancellationToken ct)
|
|
{
|
|
var result = _getLastResult();
|
|
if (result == null || result.Items == null || result.Items.Count == 0)
|
|
{
|
|
context.Response.StatusCode = 404;
|
|
byte[] err = Encoding.UTF8.GetBytes("{\"error\":\"No BOM data loaded\"}");
|
|
context.Response.ContentType = "application/json; charset=utf-8";
|
|
context.Response.ContentLength64 = err.Length;
|
|
await context.Response.OutputStream.WriteAsync(err, 0, err.Length, ct);
|
|
context.Response.Close();
|
|
return;
|
|
}
|
|
|
|
bool isFlatView = context.Request.QueryString["flat"] == "true";
|
|
var items = isFlatView ? _dataProvider.GetFlatBomItems(result.Items) : result.Items;
|
|
var settings = result.Settings;
|
|
|
|
var csv = BuildCsv(items, result.DynamicKeys, settings, isFlatView);
|
|
|
|
// UTF-8 BOM for Excel compatibility
|
|
byte[] bom = new byte[] { 0xEF, 0xBB, 0xBF };
|
|
byte[] csvBytes = Encoding.UTF8.GetBytes(csv);
|
|
byte[] body = new byte[bom.Length + csvBytes.Length];
|
|
Buffer.BlockCopy(bom, 0, body, 0, bom.Length);
|
|
Buffer.BlockCopy(csvBytes, 0, body, bom.Length, csvBytes.Length);
|
|
|
|
context.Response.StatusCode = 200;
|
|
context.Response.ContentType = "text/csv; charset=utf-8";
|
|
context.Response.Headers.Set("Content-Disposition", "attachment; filename=\"BOM_Export.csv\"");
|
|
context.Response.ContentLength64 = body.Length;
|
|
await context.Response.OutputStream.WriteAsync(body, 0, body.Length, ct);
|
|
context.Response.Close();
|
|
}
|
|
|
|
private static string BuildCsv(
|
|
List<BomItemModel> items,
|
|
List<string> dynamicKeys,
|
|
BomSettings settings,
|
|
bool isFlatView)
|
|
{
|
|
var builtInDefs = new List<(string Name, Func<BomItemModel, string> Getter)>
|
|
{
|
|
("层级", item => item.LevelDisplay ?? ""),
|
|
("图号", item => item.DrawingNo ?? ""),
|
|
("零件名称", item => item.ConfigName ?? ""),
|
|
("属性", item => item.Classification ?? ""),
|
|
("材料", item => item.MaterialProp ?? ""),
|
|
("材质", item => item.Material ?? ""),
|
|
("数量", item => item.Quantity.ToString()),
|
|
("装配体", item => item.IsAssembly ? "是" : "否"),
|
|
("外购件", item => item.IsOutSourcing ? "是" : "否")
|
|
};
|
|
|
|
var exportBuiltIns = builtInDefs
|
|
.Where(d => settings.IsColumnExport(d.Name))
|
|
.Where(d => !isFlatView || (d.Name != "层级" && d.Name != "装配体"))
|
|
.ToList();
|
|
|
|
var exportDynamicKeys = (dynamicKeys ?? new List<string>())
|
|
.Where(k => settings.IsColumnExport(k))
|
|
.ToList();
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
var headers = exportBuiltIns.Select(d => d.Name).Concat(exportDynamicKeys).ToList();
|
|
sb.AppendLine(string.Join(",", headers.Select(EscapeCsvField)));
|
|
|
|
foreach (var item in items)
|
|
{
|
|
var fields = new List<string>();
|
|
foreach (var def in exportBuiltIns)
|
|
{
|
|
fields.Add(def.Getter(item));
|
|
}
|
|
foreach (var key in exportDynamicKeys)
|
|
{
|
|
fields.Add(item.GetProp(key));
|
|
}
|
|
sb.AppendLine(string.Join(",", fields.Select(EscapeCsvField)));
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string EscapeCsvField(string field)
|
|
{
|
|
if (string.IsNullOrEmpty(field)) return "";
|
|
if (field.Contains(",") || field.Contains("\"") || field.Contains("\n"))
|
|
{
|
|
return "\"" + field.Replace("\"", "\"\"") + "\"";
|
|
}
|
|
return field;
|
|
}
|
|
}
|
|
}
|