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

- 修改邮件模板创建和更新逻辑,同时解析标题和内容中的参数
- 增强邮件内容格式化方法,支持HTML特殊字符反转义
- 实现代码块格式化,为<pre><code>标签添加样式
- 替换最外层pre标签为div标签以改善显示效果
- 添加完整的HTML邮件模板处理测试用例
- 扩展参数解析方法以合并标题和内容中的参数并去重
This commit is contained in:
C77
2025-12-22 10:40:21 +08:00
parent 4caae18d13
commit abed96ecc9
2 changed files with 271 additions and 5 deletions

View File

@@ -19,8 +19,10 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -53,7 +55,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
// 插入
MailTemplateDO template = BeanUtils.toBean(createReqVO, MailTemplateDO.class)
.setParams(parseTemplateContentParams(createReqVO.getContent()));
.setParams(parseTemplateTitleAndContentParams(createReqVO.getTitle(), createReqVO.getContent()));
mailTemplateMapper.insert(template);
return template.getId();
}
@@ -69,7 +71,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
// 更新
MailTemplateDO updateObj = BeanUtils.toBean(updateReqVO, MailTemplateDO.class)
.setParams(parseTemplateContentParams(updateReqVO.getContent()));
.setParams(parseTemplateTitleAndContentParams(updateReqVO.getTitle(), updateReqVO.getContent()));
mailTemplateMapper.updateById(updateObj);
}
@@ -129,13 +131,108 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override
public String formatMailTemplateContent(String content, Map<String, Object> params) {
return StrUtil.format(content, params);
// 先替换模板变量
String formattedContent = StrUtil.format(content, params);
// 反转义HTML特殊字符
formattedContent = unescapeHtml(formattedContent);
// 处理代码块(确保<pre><code>标签格式正确)
formattedContent = formatHtmlCodeBlocks(formattedContent);
// 将最外层的pre标签替换为div标签
formattedContent = replaceOuterPreWithDiv(formattedContent);
return formattedContent;
}
private String replaceOuterPreWithDiv(String content) {
if (content == null) {
return null;
}
// 使用正则表达式匹配所有的<pre>标签,包括嵌套的<code>标签
String regex = "(?s)<pre[^>]*>(.*?)</pre>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
// 提取<pre>标签内的内容
String innerContent = matcher.group(1);
// 返回div标签包裹的内容
matcher.appendReplacement(sb, "<div>" + innerContent + "</div>");
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* 反转义HTML特殊字符
*
* @param input 输入字符串
* @return 反转义后的字符串
*/
private String unescapeHtml(String input) {
if (input == null) {
return null;
}
return input
.replace("&amp;", "&")
.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&quot;", "\"")
.replace("&#39;", "'")
.replace("&nbsp;", " ");
}
/**
* 格式化HTML中的代码块
*
* @param content 邮件内容
* @return 格式化后的邮件内容
*/
private String formatHtmlCodeBlocks(String content) {
// 匹配<pre><code>标签的代码块
Pattern codeBlockPattern = Pattern.compile("<pre\\s*.*?><code\\s*.*?>(.*?)</code></pre>", Pattern.DOTALL);
Matcher matcher = codeBlockPattern.matcher(content);
StringBuffer sb = new StringBuffer();
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();
}
@Override
public long getMailTemplateCountByAccountId(Long accountId) {
return mailTemplateMapper.selectCountByAccountId(accountId);
}
/**
* 解析标题和内容中的参数
*/
@VisibleForTesting
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) {
if (!allParams.contains(param)) {
allParams.add(param);
}
}
return allParams;
}
/**
* 获得邮件模板中的参数,形如 {key}
@@ -143,8 +240,8 @@ public class MailTemplateServiceImpl implements MailTemplateService {
* @param content 内容
* @return 参数列表
*/
private List<String> parseTemplateContentParams(String content) {
List<String> parseTemplateContentParams(String content) {
return ReUtil.findAllGroup1(PATTERN_PARAMS, content);
}
}
}