Преглед изворни кода

!1481 feat(mail): 增强邮件模板参数解析和HTML内容格式化功能
Merge pull request !1481 from C77/master-jdk17-email

芋道源码 пре 6 месеци
родитељ
комит
e6dcbde8a7

+ 102 - 5
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java

@@ -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);
     }
 
-}
+}

Разлика између датотеке није приказан због своје велике величине
+ 169 - 0
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java