修复文件模版占位符错误问题

This commit is contained in:
fuenmao
2026-09-15 16:43:45 +08:00
parent 43a498162e
commit 20ea1bc70c
2 changed files with 72 additions and 0 deletions
@@ -35,6 +35,7 @@ public static class DocxTemplateProcessor
foreach (var paragraph in document.Descendants(WordNamespace + "p"))
{
var text = string.Concat(paragraph.Descendants(WordNamespace + "t").Select(node => node.Value));
ValidatePlaceholderSyntax(text);
foreach (global::System.Text.RegularExpressions.Match match in PlaceholderRegex.Matches(text))
codes.Add(match.Groups["code"].Value.Trim());
}
@@ -52,6 +53,72 @@ public static class DocxTemplateProcessor
}
}
/// <summary>
/// 占位符提取正则只会匹配正确格式,因此必须先主动检查错误格式,
/// 避免缺少花括号或变量编码写错时被静默忽略并继续创建版本。
/// </summary>
private static void ValidatePlaceholderSyntax(string text)
{
if (string.IsNullOrEmpty(text))
return;
if (text.Contains("{{", StringComparison.Ordinal)
|| text.Contains("}}", StringComparison.Ordinal))
{
throw Oops.Bah(
"DOCX 占位符格式错误:检测到中文全角花括号。请使用英文半角双花括号,例如 {{employee.EmployeeName}}。");
}
if (text.Contains("{{{", StringComparison.Ordinal)
|| text.Contains("}}}", StringComparison.Ordinal))
{
throw Oops.Bah(
"DOCX 占位符格式错误:检测到多余的花括号。占位符必须使用成对的英文半角双花括号,例如 {{employee.EmployeeName}}。");
}
var searchStart = 0;
while (searchStart < text.Length)
{
var openIndex = text.IndexOf("{{", searchStart, StringComparison.Ordinal);
var closeIndex = text.IndexOf("}}", searchStart, StringComparison.Ordinal);
if (closeIndex >= 0 && (openIndex < 0 || closeIndex < openIndex))
{
throw Oops.Bah(
$"DOCX 占位符格式错误:{BuildPlaceholderPreview(text, closeIndex)} 缺少对应的起始符“{{{{”。正确格式示例:{{{{employee.EmployeeName}}}}。");
}
if (openIndex < 0)
return;
closeIndex = text.IndexOf("}}", openIndex + 2, StringComparison.Ordinal);
if (closeIndex < 0)
{
throw Oops.Bah(
$"DOCX 占位符格式错误:{BuildPlaceholderPreview(text, openIndex)} 缺少结束符“}}}}”。正确格式示例:{{{{employee.EmployeeName}}}}。");
}
var placeholder = text.Substring(openIndex, closeIndex + 2 - openIndex);
var match = PlaceholderRegex.Match(placeholder);
if (!match.Success || match.Length != placeholder.Length)
{
throw Oops.Bah(
$"DOCX 占位符格式错误:“{placeholder}”。变量编码只能包含英文字母、数字、下划线、点和短横线,并且必须使用成对的英文半角双花括号。");
}
searchStart = closeIndex + 2;
}
}
private static string BuildPlaceholderPreview(string text, int markerIndex)
{
const int maxLength = 60;
var preview = text[markerIndex..].Trim();
if (preview.Length > maxLength)
preview = preview[..maxLength] + "…";
return $"“{preview}”";
}
/// <summary>
/// 使用任务变量快照替换 DOCX 中的占位符,支持占位符跨多个 Word 文本节点。
/// </summary>
@@ -37,6 +37,11 @@ public class OnboardingContractTemplateController
if (!string.Equals(suffix, ".docx", StringComparison.OrdinalIgnoreCase))
throw Oops.Bah("合同模板仅支持 DOCX 文件");
await using var input = file.OpenReadStream();
using var bytes = new MemoryStream();
await input.CopyToAsync(bytes);
DocxTemplateProcessor.ExtractVariableCodes(bytes.ToArray());
return await _fileService.UploadFile(SysDictConst.FILE_ENGINE_ALIYUN_OSS, file);
}