235 lines
8.3 KiB
C#
235 lines
8.3 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text.RegularExpressions;
|
|||
|
|
using DocumentFormat.OpenXml.Packaging;
|
|||
|
|
using DocumentFormat.OpenXml.Wordprocessing;
|
|||
|
|
using DocumentFormat.OpenXml.Drawing.Wordprocessing;
|
|||
|
|
using Furion.DependencyInjection;
|
|||
|
|
|
|||
|
|
namespace YD_Event.Core.Certificate
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// DOCX模板处理器
|
|||
|
|
/// </summary>
|
|||
|
|
public class DocxTemplateProcessor : ITransient
|
|||
|
|
{
|
|||
|
|
private const string PlaceholderPattern = @"\{\{([^}]+)\}\}";
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理DOCX模板,替换占位符
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="templateStream">模板文件流</param>
|
|||
|
|
/// <param name="data">数据字典</param>
|
|||
|
|
/// <param name="qrCodeData">二维码数据字典</param>
|
|||
|
|
/// <returns>处理后的文件流</returns>
|
|||
|
|
public Stream ProcessTemplate(Stream templateStream, Dictionary<string, string> data, Dictionary<string, string> qrCodeData = null)
|
|||
|
|
{
|
|||
|
|
var outputStream = new MemoryStream();
|
|||
|
|
|
|||
|
|
// 复制模板到输出流
|
|||
|
|
templateStream.Position = 0;
|
|||
|
|
templateStream.CopyTo(outputStream);
|
|||
|
|
outputStream.Position = 0;
|
|||
|
|
|
|||
|
|
using (var document = WordprocessingDocument.Open(outputStream, true))
|
|||
|
|
{
|
|||
|
|
var body = document.MainDocumentPart?.Document?.Body;
|
|||
|
|
if (body == null) return outputStream;
|
|||
|
|
|
|||
|
|
// 处理段落中的占位符
|
|||
|
|
ProcessParagraphs(body, data, qrCodeData);
|
|||
|
|
|
|||
|
|
// 处理表格中的占位符
|
|||
|
|
ProcessTables(body, data, qrCodeData);
|
|||
|
|
|
|||
|
|
// 处理页眉页脚中的占位符
|
|||
|
|
ProcessHeaderFooter(document, data, qrCodeData);
|
|||
|
|
|
|||
|
|
document.Save();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
outputStream.Position = 0;
|
|||
|
|
return outputStream;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理段落中的占位符
|
|||
|
|
/// </summary>
|
|||
|
|
private void ProcessParagraphs(Body body, Dictionary<string, string> data, Dictionary<string, string> qrCodeData)
|
|||
|
|
{
|
|||
|
|
foreach (var paragraph in body.Elements<Paragraph>())
|
|||
|
|
{
|
|||
|
|
ProcessParagraph(paragraph, data, qrCodeData);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理单个段落
|
|||
|
|
/// </summary>
|
|||
|
|
private void ProcessParagraph(Paragraph paragraph, Dictionary<string, string> data, Dictionary<string, string> qrCodeData)
|
|||
|
|
{
|
|||
|
|
var runs = paragraph.Elements<Run>().ToList();
|
|||
|
|
|
|||
|
|
foreach (var run in runs)
|
|||
|
|
{
|
|||
|
|
var texts = run.Elements<Text>().ToList();
|
|||
|
|
foreach (var text in texts)
|
|||
|
|
{
|
|||
|
|
var processedText = ReplacePlaceholders(text.Text, data, qrCodeData);
|
|||
|
|
|
|||
|
|
// 检查是否是二维码占位符
|
|||
|
|
if (IsQRCodePlaceholder(text.Text))
|
|||
|
|
{
|
|||
|
|
var placeholder = ExtractPlaceholderKey(text.Text);
|
|||
|
|
if (qrCodeData != null && qrCodeData.ContainsKey(placeholder))
|
|||
|
|
{
|
|||
|
|
// 替换为二维码图片
|
|||
|
|
ReplaceTextWithQRCode(run, qrCodeData[placeholder]);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
text.Text = processedText;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
text.Text = processedText;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理表格中的占位符
|
|||
|
|
/// </summary>
|
|||
|
|
private void ProcessTables(Body body, Dictionary<string, string> data, Dictionary<string, string> qrCodeData)
|
|||
|
|
{
|
|||
|
|
foreach (var table in body.Elements<Table>())
|
|||
|
|
{
|
|||
|
|
foreach (var row in table.Elements<TableRow>())
|
|||
|
|
{
|
|||
|
|
foreach (var cell in row.Elements<TableCell>())
|
|||
|
|
{
|
|||
|
|
foreach (var paragraph in cell.Elements<Paragraph>())
|
|||
|
|
{
|
|||
|
|
ProcessParagraph(paragraph, data, qrCodeData);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 处理页眉页脚中的占位符
|
|||
|
|
/// </summary>
|
|||
|
|
private void ProcessHeaderFooter(WordprocessingDocument document,
|
|||
|
|
Dictionary<string, string> data, Dictionary<string, string> qrCodeData)
|
|||
|
|
{
|
|||
|
|
// 暂时跳过页眉页脚处理,因为API兼容性问题
|
|||
|
|
// 实际项目中需要根据具体的DocumentFormat.OpenXml版本调整
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 替换文本中的占位符
|
|||
|
|
/// </summary>
|
|||
|
|
private string ReplacePlaceholders(string text, Dictionary<string, string> data, Dictionary<string, string> qrCodeData)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(text)) return text;
|
|||
|
|
|
|||
|
|
return Regex.Replace(text, PlaceholderPattern, match =>
|
|||
|
|
{
|
|||
|
|
var key = match.Groups[1].Value.Trim();
|
|||
|
|
|
|||
|
|
// 优先使用二维码数据
|
|||
|
|
if (qrCodeData != null && qrCodeData.ContainsKey(key))
|
|||
|
|
{
|
|||
|
|
return qrCodeData[key];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用普通数据
|
|||
|
|
if (data != null && data.ContainsKey(key))
|
|||
|
|
{
|
|||
|
|
return data[key] ?? string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 返回原始占位符(未找到数据)
|
|||
|
|
return match.Value;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 检查是否是二维码占位符
|
|||
|
|
/// </summary>
|
|||
|
|
private bool IsQRCodePlaceholder(string text)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(text)) return false;
|
|||
|
|
|
|||
|
|
var match = Regex.Match(text, PlaceholderPattern);
|
|||
|
|
if (!match.Success) return false;
|
|||
|
|
|
|||
|
|
var key = match.Groups[1].Value.Trim().ToLowerInvariant();
|
|||
|
|
return key.Contains("qr") || key.Contains("qrcode");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 提取占位符键名
|
|||
|
|
/// </summary>
|
|||
|
|
private string ExtractPlaceholderKey(string text)
|
|||
|
|
{
|
|||
|
|
var match = Regex.Match(text, PlaceholderPattern);
|
|||
|
|
return match.Success ? match.Groups[1].Value.Trim() : string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 将文本替换为二维码图片
|
|||
|
|
/// </summary>
|
|||
|
|
private void ReplaceTextWithQRCode(Run run, string qrCodeContent)
|
|||
|
|
{
|
|||
|
|
// 注意:这里需要实现二维码图片生成和插入逻辑
|
|||
|
|
// 由于DocumentFormat.OpenXml处理图片比较复杂,这里先简化处理
|
|||
|
|
// 实际实现需要使用QRCodeService生成图片,然后插入到文档中
|
|||
|
|
|
|||
|
|
// 临时实现:用文本替代
|
|||
|
|
var texts = run.Elements<Text>().ToList();
|
|||
|
|
foreach (var text in texts)
|
|||
|
|
{
|
|||
|
|
text.Text = $"[QR: {qrCodeContent}]";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 验证数据完整性
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="requiredPlaceholders">必填占位符列表</param>
|
|||
|
|
/// <param name="data">数据字典</param>
|
|||
|
|
/// <returns>验证结果</returns>
|
|||
|
|
public TemplateValidationResult ValidateTemplateData(List<string> requiredPlaceholders, Dictionary<string, string> data)
|
|||
|
|
{
|
|||
|
|
var result = new TemplateValidationResult { IsValid = true };
|
|||
|
|
|
|||
|
|
foreach (var placeholder in requiredPlaceholders)
|
|||
|
|
{
|
|||
|
|
if (!data.ContainsKey(placeholder) || string.IsNullOrWhiteSpace(data[placeholder]))
|
|||
|
|
{
|
|||
|
|
result.IsValid = false;
|
|||
|
|
result.MissingPlaceholders.Add(placeholder);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 模板验证结果
|
|||
|
|
/// </summary>
|
|||
|
|
public class TemplateValidationResult
|
|||
|
|
{
|
|||
|
|
public bool IsValid { get; set; }
|
|||
|
|
public List<string> MissingPlaceholders { get; set; } = new List<string>();
|
|||
|
|
public List<string> Warnings { get; set; } = new List<string>();
|
|||
|
|
}
|
|||
|
|
}
|