2026-03-02 21:09:03 +08:00
|
|
|
|
using System;
|
2025-12-27 12:53:54 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using QuestPDF.Fluent;
|
|
|
|
|
|
using QuestPDF.Helpers;
|
|
|
|
|
|
using QuestPDF.Infrastructure;
|
2026-03-02 21:09:03 +08:00
|
|
|
|
using YD_Event.Core.Entities;
|
|
|
|
|
|
using System.Text.Json;
|
2025-12-27 12:53:54 +08:00
|
|
|
|
|
|
|
|
|
|
namespace YD_Event.Core.Certificate
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2026-03-02 21:09:03 +08:00
|
|
|
|
/// 动态证书生成文档
|
2025-12-27 12:53:54 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class CertificateDocument : IDocument
|
|
|
|
|
|
{
|
2026-03-02 21:09:03 +08:00
|
|
|
|
private readonly Entities.Certificate _certificate;
|
|
|
|
|
|
private readonly Dictionary<string, object> _data;
|
|
|
|
|
|
private readonly byte[] _templateBytes;
|
2025-12-27 12:53:54 +08:00
|
|
|
|
|
2026-03-02 21:09:03 +08:00
|
|
|
|
public CertificateDocument(Entities.Certificate certificate, Dictionary<string, object> data, byte[] templateBytes)
|
2025-12-27 12:53:54 +08:00
|
|
|
|
{
|
2026-03-02 21:09:03 +08:00
|
|
|
|
_certificate = certificate;
|
|
|
|
|
|
_data = data;
|
|
|
|
|
|
_templateBytes = templateBytes;
|
2025-12-27 12:53:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
|
|
|
|
|
|
|
|
|
|
|
public void Compose(IDocumentContainer container)
|
|
|
|
|
|
{
|
|
|
|
|
|
container.Page(page =>
|
|
|
|
|
|
{
|
2026-03-02 21:09:03 +08:00
|
|
|
|
// 设置页面大小
|
|
|
|
|
|
page.Size(new PageSize((float)_certificate.Width, (float)_certificate.Height));
|
|
|
|
|
|
page.Margin(0); // 无边距,背景铺满
|
|
|
|
|
|
page.DefaultTextStyle(x => x.FontFamily("Source Han Sans CN")); // 默认字体
|
2025-12-27 12:53:54 +08:00
|
|
|
|
|
2026-03-02 21:09:03 +08:00
|
|
|
|
// 设置背景
|
|
|
|
|
|
page.Background().Image(_templateBytes);
|
2025-12-27 12:53:54 +08:00
|
|
|
|
|
2026-03-02 21:09:03 +08:00
|
|
|
|
// 内容层
|
|
|
|
|
|
page.Content().Layers(layers =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 解析占位符配置
|
|
|
|
|
|
var placeholders = string.IsNullOrEmpty(_certificate.Placeholders)
|
|
|
|
|
|
? new List<CertificatePlaceholder>()
|
|
|
|
|
|
: JsonSerializer.Deserialize<List<CertificatePlaceholder>>(_certificate.Placeholders);
|
2025-12-27 12:53:54 +08:00
|
|
|
|
|
2026-03-02 21:09:03 +08:00
|
|
|
|
foreach (var p in placeholders)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取数据对象
|
|
|
|
|
|
var dataObj = _data.ContainsKey(p.Key) ? _data[p.Key] : null;
|
|
|
|
|
|
if (dataObj == null) continue;
|
2025-12-27 12:53:54 +08:00
|
|
|
|
|
2026-03-02 21:09:03 +08:00
|
|
|
|
// 定位层
|
|
|
|
|
|
layers.Layer()
|
|
|
|
|
|
.TranslateX(p.X)
|
|
|
|
|
|
.TranslateY(p.Y)
|
|
|
|
|
|
.Width(p.Width)
|
|
|
|
|
|
.Height(p.Height)
|
|
|
|
|
|
.Element(element =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p.Type == "Image")
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dataObj is byte[] imgBytes)
|
|
|
|
|
|
{
|
|
|
|
|
|
element.Image(imgBytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (dataObj is string url && !string.IsNullOrEmpty(url))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 暂不支持直接URL渲染,需预先下载转为byte[]
|
|
|
|
|
|
element.Border(1).BorderColor(Colors.Red.Medium).AlignCenter().AlignMiddle().Text($"Image: {p.Key}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (p.Type == "QRCode")
|
|
|
|
|
|
{
|
|
|
|
|
|
// 暂无二维码生成库,显示占位符
|
|
|
|
|
|
// 实际项目建议引入 QRCoder 或类似库生成 byte[] 后渲染
|
|
|
|
|
|
element.Border(1).BorderColor(Colors.Black).AlignCenter().AlignMiddle().Text("QR Code");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 文本处理
|
|
|
|
|
|
string value = dataObj.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
// 处理日期格式化
|
|
|
|
|
|
if (!string.IsNullOrEmpty(p.FormatString))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dataObj is DateTime dt)
|
|
|
|
|
|
{
|
|
|
|
|
|
value = dt.ToString(p.FormatString);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (DateTime.TryParse(value, out var dtParsed))
|
|
|
|
|
|
{
|
|
|
|
|
|
value = dtParsed.ToString(p.FormatString);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var text = element.Text(value);
|
|
|
|
|
|
|
|
|
|
|
|
// 样式设置
|
|
|
|
|
|
var style = TextStyle.Default.FontSize(p.FontSize > 0 ? p.FontSize : 12);
|
|
|
|
|
|
if (!string.IsNullOrEmpty(p.FontFamily)) style = style.FontFamily(p.FontFamily);
|
|
|
|
|
|
if (!string.IsNullOrEmpty(p.Color)) style = style.FontColor(p.Color);
|
|
|
|
|
|
|
|
|
|
|
|
text.Style(style);
|
|
|
|
|
|
|
|
|
|
|
|
// 对齐方式
|
|
|
|
|
|
if (p.TextAlign == "Center") element.AlignCenter();
|
|
|
|
|
|
else if (p.TextAlign == "Right") element.AlignRight();
|
|
|
|
|
|
else element.AlignLeft();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-12-27 12:53:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|