admin/src/Oms.Application/Labeling/Hiprint/HiprintTemplateParser.cs
zzh b679151eb1 feat: Hiprint模板转QuestPDF异步出面单,并接通打印链路
支持模板预览/字段字典、产品绑定 LabelTemplateId、订单 Pending→Ready 生成 PDF,以及 order/print、批量与仓内 printUrl。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-18 14:52:39 +08:00

202 lines
6.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Globalization;
using System.Text.Json;
using Fake.Application;
using Fake.DependencyInjection;
namespace Oms.Application.Labeling.Hiprint;
/// <summary>
/// 将 hiprint TemplateJson 转为中间绘制 IR。
/// </summary>
public sealed class HiprintTemplateParser : ITransientDependency
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true
};
public LabelPageIr Parse(string templateJson, IReadOnlyDictionary<string, object?> printData)
{
if (string.IsNullOrWhiteSpace(templateJson))
{
throw new ArgumentException("模板 JSON 为空", nameof(templateJson));
}
var doc = JsonSerializer.Deserialize<HiprintTemplateDocument>(templateJson, JsonOptions)
?? throw new BusinessException("模板 JSON 无法解析");
var panel = doc.Panels.OrderBy(x => x.Index).FirstOrDefault()
?? throw new BusinessException("模板缺少 panels");
var widthMm = panel.Width > 0 ? panel.Width : 100f;
var heightMm = panel.Height > 0 ? panel.Height : 150f;
var ops = new List<LabelDrawOp>();
foreach (var element in panel.PrintElements)
{
var op = MapElement(element, printData);
if (op != null)
{
ops.Add(op);
}
}
return new LabelPageIr
{
WidthMm = widthMm,
HeightMm = heightMm,
Ops = ops
};
}
private static LabelDrawOp? MapElement(
HiprintPrintElement element,
IReadOnlyDictionary<string, object?> printData)
{
var type = element.PrintElementType?.Type?.Trim().ToLowerInvariant() ?? "text";
var opt = element.Options;
var left = opt.Left;
var top = opt.Top;
var width = Math.Max(opt.Width, 1f);
var height = Math.Max(opt.Height, 1f);
switch (type)
{
case "hline":
return new LabelLineOp
{
LeftPt = left,
TopPt = top,
WidthPt = width,
HeightPt = Math.Max(opt.BorderWidth ?? 1f, 0.5f),
Vertical = false,
ColorHex = opt.Color,
ThicknessPt = opt.BorderWidth ?? 1f
};
case "vline":
return new LabelLineOp
{
LeftPt = left,
TopPt = top,
WidthPt = Math.Max(opt.BorderWidth ?? 1f, 0.5f),
HeightPt = height,
Vertical = true,
ColorHex = opt.Color,
ThicknessPt = opt.BorderWidth ?? 1f
};
case "rect":
return new LabelRectOp
{
LeftPt = left,
TopPt = top,
WidthPt = width,
HeightPt = height,
BorderColorHex = opt.Color,
BorderWidthPt = opt.BorderWidth ?? 1f
};
case "image":
return new LabelImageOp
{
LeftPt = left,
TopPt = top,
WidthPt = width,
HeightPt = height,
Src = ResolveValue(opt.Field, opt.Src ?? opt.Title, printData)
};
case "longtext":
return BuildTextOp(opt, left, top, width, height, printData, wrap: true);
case "text":
default:
{
var textType = opt.TextType?.Trim().ToLowerInvariant();
if (textType is "barcode" or "qrcode")
{
var content = ResolveValue(opt.Field, opt.Title, printData);
if (string.IsNullOrWhiteSpace(content))
{
return null;
}
return new LabelBarcodeOp
{
LeftPt = left,
TopPt = top,
WidthPt = width,
HeightPt = height,
Content = content,
QrCode = textType == "qrcode"
};
}
return BuildTextOp(opt, left, top, width, height, printData, wrap: false);
}
}
}
private static LabelTextOp? BuildTextOp(
HiprintElementOptions opt,
float left,
float top,
float width,
float height,
IReadOnlyDictionary<string, object?> printData,
bool wrap)
{
var value = ResolveValue(opt.Field, null, printData);
string text;
if (!string.IsNullOrWhiteSpace(opt.Field))
{
// 有 field:默认只打字段值;hideTitle=false 且有 title 时拼「标题:值」
if (opt.HideTitle == false && !string.IsNullOrWhiteSpace(opt.Title) && !string.IsNullOrWhiteSpace(value))
{
text = $"{opt.Title}:{value}";
}
else
{
text = value ?? string.Empty;
}
}
else
{
text = opt.Title ?? string.Empty;
}
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
var bold = string.Equals(opt.FontWeight, "bold", StringComparison.OrdinalIgnoreCase)
|| opt.FontWeight is "700" or "600";
return new LabelTextOp
{
LeftPt = left,
TopPt = top,
WidthPt = width,
HeightPt = height,
Text = text,
FontSizePt = opt.FontSize is > 0 ? opt.FontSize.Value : 10f,
Bold = bold,
ColorHex = opt.Color,
Align = (opt.TextAlign ?? "left").ToLowerInvariant(),
Wrap = wrap
};
}
private static string? ResolveValue(
string? field,
string? fallback,
IReadOnlyDictionary<string, object?> printData)
{
if (!string.IsNullOrWhiteSpace(field) && printData.TryGetValue(field, out var raw) && raw != null)
{
return Convert.ToString(raw, CultureInfo.InvariantCulture);
}
return fallback;
}
}