支持模板预览/字段字典、产品绑定 LabelTemplateId、订单 Pending→Ready 生成 PDF,以及 order/print、批量与仓内 printUrl。 Co-authored-by: Cursor <cursoragent@cursor.com>
200 lines
8.0 KiB
C#
200 lines
8.0 KiB
C#
using System.Globalization;
|
||
using Oms.Application.Dtos.Orders;
|
||
|
||
namespace Oms.Application.Labeling;
|
||
|
||
/// <summary>
|
||
/// 将订单出参映射为 hiprint PrintData(camelCase)。
|
||
/// </summary>
|
||
public static class LabelPrintDataBuilder
|
||
{
|
||
public static IReadOnlyDictionary<string, object?> Build(
|
||
OrderOutput order,
|
||
string? operatorName = null,
|
||
DateTime? printTime = null)
|
||
{
|
||
var receiver = order.Receiver;
|
||
var sender = order.Sender;
|
||
var weight = PickWeight(order.WeightList);
|
||
var data = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
["serviceNo"] = order.ServiceNo,
|
||
["clientNo"] = order.ClientNo,
|
||
["trackNo"] = order.TrackNo,
|
||
["requestId"] = order.RequestId,
|
||
["productCode"] = order.ProductCode,
|
||
["salesProductName"] = order.ProductName,
|
||
["channelCode"] = order.ChannelCode,
|
||
["channelName"] = order.ChannelName,
|
||
["warehouseName"] = order.Warehouse,
|
||
["customerCode"] = order.CustomerCode,
|
||
["customerName"] = order.CustomerName,
|
||
|
||
["recipientName"] = receiver?.RecipientsName,
|
||
["recipientCompany"] = receiver?.CompanyName,
|
||
["recipientPhone"] = FirstNonEmpty(receiver?.Phone, receiver?.TelePhone),
|
||
["recipientEmail"] = receiver?.Email,
|
||
["recipientZip"] = receiver?.ZipCode,
|
||
["recipientCountry"] = order.ConsigneeCountry,
|
||
["recipientCountryName"] = order.ConsigneeCountryName,
|
||
["recipientProvince"] = receiver?.Province,
|
||
["recipientCity"] = receiver?.City,
|
||
["recipientStreet1"] = receiver?.StreetAddress1,
|
||
["recipientStreet2"] = receiver?.StreetAddress2,
|
||
["recipientStreet3"] = receiver?.StreetAddress3,
|
||
["recipientAddressFull"] = BuildAddressFull(
|
||
receiver?.StreetAddress1,
|
||
receiver?.StreetAddress2,
|
||
receiver?.StreetAddress3,
|
||
receiver?.StreetNumber,
|
||
receiver?.City,
|
||
receiver?.Province,
|
||
receiver?.ZipCode,
|
||
order.ConsigneeCountryName ?? order.ConsigneeCountry),
|
||
["recipientTaxNo"] = receiver?.TaxNo,
|
||
["vatNo"] = receiver?.Vat,
|
||
["eori"] = receiver?.Eori,
|
||
|
||
["senderName"] = sender?.SenderName,
|
||
["senderCompany"] = sender?.Company,
|
||
["senderPhone"] = FirstNonEmpty(sender?.Phone, sender?.TelePhone),
|
||
["senderEmail"] = sender?.Email,
|
||
["senderZip"] = sender?.ZipCode,
|
||
["senderProvince"] = sender?.Province,
|
||
["senderCity"] = sender?.City,
|
||
["senderStreet1"] = sender?.StreetAddress1,
|
||
["senderStreet2"] = sender?.StreetAddress2,
|
||
["senderStreet3"] = sender?.StreetAddress3,
|
||
["senderAddressFull"] = BuildAddressFull(
|
||
sender?.StreetAddress1,
|
||
sender?.StreetAddress2,
|
||
sender?.StreetAddress3,
|
||
sender?.HouseNumber,
|
||
sender?.City,
|
||
sender?.Province,
|
||
sender?.ZipCode,
|
||
null),
|
||
|
||
["actualWeight"] = weight?.ActualWeight,
|
||
["volumetricWeight"] = weight?.VolumetricWeight,
|
||
["chargeableWeight"] = order.ChargeableWeight ?? weight?.ChargeableWeight,
|
||
["length"] = weight?.Length,
|
||
["width"] = weight?.Width,
|
||
["height"] = weight?.Height,
|
||
["weightUnit"] = "KG",
|
||
["dimUnit"] = "CM",
|
||
|
||
["printTime"] = (printTime ?? DateTime.Now).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
|
||
["operatorName"] = operatorName
|
||
};
|
||
|
||
data["items"] = order.Items.Select(x => new Dictionary<string, object?>
|
||
{
|
||
["skuCode"] = x.Sku,
|
||
["skuName"] = FirstNonEmpty(x.CnName, x.EnName),
|
||
["qty"] = x.Quantity,
|
||
["weight"] = x.Weight ?? x.UnitWeight,
|
||
["declareNameEn"] = x.EnName,
|
||
["declareValue"] = x.UnitPrice ?? x.Amount
|
||
}).ToList();
|
||
|
||
return data;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设计预览用的 Mock PrintData。
|
||
/// </summary>
|
||
public static IReadOnlyDictionary<string, object?> BuildMock() =>
|
||
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
["serviceNo"] = "S202607180001",
|
||
["clientNo"] = "C-10086",
|
||
["trackNo"] = "YT1234567890",
|
||
["requestId"] = "REQ-DEMO",
|
||
["productCode"] = "DEMO-PRODUCT",
|
||
["salesProductName"] = "专线-普货",
|
||
["channelCode"] = "CH-01",
|
||
["channelName"] = "Demo Channel",
|
||
["warehouseName"] = "SZ-01",
|
||
["customerCode"] = "AF0A1",
|
||
["customerName"] = "Demo Customer",
|
||
["recipientName"] = "John Lee",
|
||
["recipientCompany"] = "ACME Inc",
|
||
["recipientPhone"] = "+1-555-0100",
|
||
["recipientEmail"] = "john@example.com",
|
||
["recipientZip"] = "90001",
|
||
["recipientCountry"] = "US",
|
||
["recipientCountryName"] = "United States",
|
||
["recipientProvince"] = "CA",
|
||
["recipientCity"] = "Los Angeles",
|
||
["recipientStreet1"] = "100 Demo Street",
|
||
["recipientStreet2"] = "Suite 8",
|
||
["recipientStreet3"] = null,
|
||
["recipientAddressFull"] = "100 Demo Street, Suite 8, Los Angeles, CA 90001, United States",
|
||
["recipientTaxNo"] = null,
|
||
["vatNo"] = null,
|
||
["eori"] = null,
|
||
["senderName"] = "OMS Warehouse",
|
||
["senderCompany"] = "OMS Co",
|
||
["senderPhone"] = "+86-755-0000000",
|
||
["senderEmail"] = "ops@example.com",
|
||
["senderZip"] = "518000",
|
||
["senderProvince"] = "Guangdong",
|
||
["senderCity"] = "Shenzhen",
|
||
["senderStreet1"] = "Warehouse Rd 1",
|
||
["senderStreet2"] = null,
|
||
["senderStreet3"] = null,
|
||
["senderAddressFull"] = "Warehouse Rd 1, Shenzhen, Guangdong 518000",
|
||
["actualWeight"] = 1.25m,
|
||
["volumetricWeight"] = 1.10m,
|
||
["chargeableWeight"] = 1.25m,
|
||
["length"] = 30m,
|
||
["width"] = 20m,
|
||
["height"] = 10m,
|
||
["weightUnit"] = "KG",
|
||
["dimUnit"] = "CM",
|
||
["printTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
|
||
["operatorName"] = "preview",
|
||
["items"] = new List<Dictionary<string, object?>>
|
||
{
|
||
new()
|
||
{
|
||
["skuCode"] = "SKU001",
|
||
["skuName"] = "T-Shirt",
|
||
["qty"] = 2,
|
||
["weight"] = 0.2m,
|
||
["declareNameEn"] = "T-Shirt",
|
||
["declareValue"] = 9.9m
|
||
}
|
||
}
|
||
};
|
||
|
||
private static OrderWeightOutput? PickWeight(IReadOnlyList<OrderWeightOutput> list)
|
||
{
|
||
if (list.Count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return list.FirstOrDefault(x =>
|
||
string.Equals(x.Type, "receive", StringComparison.OrdinalIgnoreCase)
|
||
|| string.Equals(x.Type, "收货", StringComparison.OrdinalIgnoreCase))
|
||
?? list.FirstOrDefault(x =>
|
||
string.Equals(x.Type, "forecast", StringComparison.OrdinalIgnoreCase)
|
||
|| string.Equals(x.Type, "预报", StringComparison.OrdinalIgnoreCase))
|
||
?? list[0];
|
||
}
|
||
|
||
private static string? BuildAddressFull(params string?[] parts)
|
||
{
|
||
var values = parts
|
||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||
.Select(x => x!.Trim())
|
||
.ToArray();
|
||
return values.Length == 0 ? null : string.Join(", ", values);
|
||
}
|
||
|
||
private static string? FirstNonEmpty(params string?[] values) =>
|
||
values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
|
||
}
|