fix: 邀请函 PDF 落款机构名/姓名占位符兼容每字空格排版

- 落款机构 ORG_TEXT 扩展为无空格与每字空格两种写法
  ("北京整合医学学会" / "北 京 整 合 医 学 学 会")
- 姓名占位符扩展为 "xxx" / "x x x" 两种, 大小写由 toLowerCase 归一
- 盖章取全文最后一个匹配, 未匹配日志文案同步更新

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-09-17 17:16:43 +00:00
co-authored by Claude Code
parent 0553ca4fa3
commit 8e1b1fc209
@@ -56,10 +56,10 @@ public class InvitationPdfService {
/** 公章尺寸 (宽高, px), 复用 PdfService 劳务协议章口径 */ /** 公章尺寸 (宽高, px), 复用 PdfService 劳务协议章口径 */
private static final float SEAL_WIDTH = 120f; private static final float SEAL_WIDTH = 120f;
private static final float SEAL_HEIGHT = 120f; private static final float SEAL_HEIGHT = 120f;
/** 需盖章的落款机构名 (取全文最后一个) */ /** 需盖章的落款机构名 (取全文最后一个), 兼容无空格/每字空格两种排版 */
private static final String ORG_TEXT = "北京整合医学学会"; private static final String[] ORG_TEXTS = {"北京整合医学学会", "北 京 整 合 医 学 学 会"};
/** 姓名占位符 (忽略大小写, 兼容 xxx / XXX) */ /** 姓名占位符 (忽略大小写, 由 toLowerCase 归一), 兼容无空格/带空格两种排版 */
private static final String PLACEHOLDER = "xxx"; private static final String[] PLACEHOLDERS = {"xxx", "x x x"};
/** 按 URL 下载邀请函模板 PDF 字节 (批量发送时对同一会议只调用一次) */ /** 按 URL 下载邀请函模板 PDF 字节 (批量发送时对同一会议只调用一次) */
public byte[] downloadTemplate(String url) { public byte[] downloadTemplate(String url) {
@@ -144,21 +144,27 @@ public class InvitationPdfService {
} }
String lower = full.toLowerCase(Locale.ROOT); String lower = full.toLowerCase(Locale.ROOT);
// 姓名占位符: 兼容无空格/带空格两种写法 (xxx / x x x, 大小写由 toLowerCase 归一)
for (String ph : PLACEHOLDERS) {
int i = 0; int i = 0;
while ((i = lower.indexOf(PLACEHOLDER, i)) >= 0) { while ((i = lower.indexOf(ph, i)) >= 0) {
nameTargets.add(buildNameTarget(charInfos, i, PLACEHOLDER.length(), p)); nameTargets.add(buildNameTarget(charInfos, i, ph.length(), p));
i += PLACEHOLDER.length(); i += ph.length();
} }
}
// 落款机构: 兼容无空格/每字空格两种写法, 取全文最后一个匹配
for (String org : ORG_TEXTS) {
int j = 0; int j = 0;
while ((j = full.indexOf(ORG_TEXT, j)) >= 0) { while ((j = full.indexOf(org, j)) >= 0) {
seal = buildSealTarget(charInfos, j, ORG_TEXT.length(), p); seal = buildSealTarget(charInfos, j, org.length(), p);
j += ORG_TEXT.length(); j += org.length();
}
} }
} }
// 第二遍: 叠加 // 第二遍: 叠加
if (nameTargets.isEmpty()) { if (nameTargets.isEmpty()) {
log.warn("[invitation] 未找到占位符 xxx, 跳过姓名替换"); log.warn("[invitation] 未找到姓名占位符 (xxx / x x x), 跳过姓名替换");
} else { } else {
PdfFont cjkFont = PdfFontFactory.createFont("simsun.ttc,0", PdfEncodings.IDENTITY_H, false); PdfFont cjkFont = PdfFontFactory.createFont("simsun.ttc,0", PdfEncodings.IDENTITY_H, false);
for (NameTarget t : nameTargets) { for (NameTarget t : nameTargets) {
@@ -166,7 +172,7 @@ public class InvitationPdfService {
} }
} }
if (seal == null) { if (seal == null) {
log.warn("[invitation] 未找到 '{}', 跳过盖章", ORG_TEXT); log.warn("[invitation] 未找到落款机构名 (北京整合医学学会), 跳过盖章");
} else { } else {
drawSeal(doc, seal); drawSeal(doc, seal);
} }