feat(mail): 增强邮件模板参数解析和内容格式化功能

This commit is contained in:
YunaiV
2025-12-25 09:59:34 +08:00
parent e6dcbde8a7
commit 315a024ccc
2 changed files with 35 additions and 43 deletions

View File

@@ -131,32 +131,28 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override
public String formatMailTemplateContent(String content, Map<String, Object> params) {
// 先替换模板变量
// 1. 先替换模板变量
String formattedContent = StrUtil.format(content, params);
// 反转义HTML特殊字符
// 关联 Pull Requesthttps://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1461 讨论
// 2.1 反转义HTML特殊字符
formattedContent = unescapeHtml(formattedContent);
// 处理代码块(确保<pre><code>标签格式正确)
// 2.2 处理代码块(确保<pre><code>标签格式正确)
formattedContent = formatHtmlCodeBlocks(formattedContent);
// 将最外层的pre标签替换为div标签
// 2.3 将最外层的 pre 标签替换为 div 标签
formattedContent = replaceOuterPreWithDiv(formattedContent);
return formattedContent;
}
private String replaceOuterPreWithDiv(String content) {
if (content == null) {
return null;
if (StrUtil.isEmpty(content)) {
return content;
}
// 使用正则表达式匹配所有的 <pre> 标签,包括嵌套的 <code> 标签
String regex = "(?s)<pre[^>]*>(.*?)</pre>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
// 提取 <pre> 标签内的内容
String innerContent = matcher.group(1);
@@ -164,7 +160,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
matcher.appendReplacement(sb, "<div>" + innerContent + "</div>");
}
matcher.appendTail(sb);
return sb.toString();
}
@@ -175,8 +170,8 @@ public class MailTemplateServiceImpl implements MailTemplateService {
* @return 反转义后的字符串
*/
private String unescapeHtml(String input) {
if (input == null) {
return null;
if (StrUtil.isEmpty(input)) {
return input;
}
return input
.replace("&amp;", "&")
@@ -197,18 +192,15 @@ public class MailTemplateServiceImpl implements MailTemplateService {
// 匹配 <pre><code> 标签的代码块
Pattern codeBlockPattern = Pattern.compile("<pre\\s*.*?><code\\s*.*?>(.*?)</code></pre>", Pattern.DOTALL);
Matcher matcher = codeBlockPattern.matcher(content);
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
// 获取代码块内容
String codeBlock = matcher.group(1);
// 为代码块添加样式
String replacement = "<pre style=\"background-color: #f5f5f5; padding: 10px; border-radius: 5px; overflow-x: auto;\"><code>" + codeBlock + "</code></pre>";
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
return sb.toString();
}
@@ -216,6 +208,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
public long getMailTemplateCountByAccountId(Long accountId) {
return mailTemplateMapper.selectCountByAccountId(accountId);
}
/**
* 解析标题和内容中的参数
*/
@@ -223,7 +216,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
public List<String> parseTemplateTitleAndContentParams(String title, String content) {
List<String> titleParams = ReUtil.findAllGroup1(PATTERN_PARAMS, title);
List<String> contentParams = ReUtil.findAllGroup1(PATTERN_PARAMS, content);
// 合并参数并去重
List<String> allParams = new ArrayList<>(titleParams);
for (String param : contentParams) {

View File

@@ -354,10 +354,10 @@ public class MailTemplateServiceImplTest extends BaseDbUnitTest {
// 断言:只提取了纯参数名称,没有 HTML 格式
assertEquals(6, parsedParams.size());
// 检查所有参数类型
assertTrue(parsedParams.stream().filter("username"::equals).count() == 3);
assertTrue(parsedParams.stream().filter("email"::equals).count() == 1);
assertTrue(parsedParams.stream().filter("role"::equals).count() == 1);
assertTrue(parsedParams.stream().filter("activationLink"::equals).count() == 1);
assertEquals(3, parsedParams.stream().filter("username"::equals).count());
assertEquals(1, parsedParams.stream().filter("email"::equals).count());
assertEquals(1, parsedParams.stream().filter("role"::equals).count());
assertEquals(1, parsedParams.stream().filter("activationLink"::equals).count());
// 断言:没有包含任何 HTML 标签
for (String param : parsedParams) {
assertFalse(param.contains("<"));