ソースを参照

优化邮件发送

zhou-hao 3 年 前
コミット
cb11d4ce16

+ 84 - 40
jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/DefaultEmailNotifier.java

@@ -5,9 +5,11 @@ import io.vavr.control.Try;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
 import org.hswebframework.web.exception.BusinessException;
 import org.hswebframework.web.id.IDGenerator;
 import org.hswebframework.web.utils.ExpressionUtils;
+import org.hswebframework.web.utils.TemplateParser;
 import org.hswebframework.web.validator.ValidatorUtils;
 import org.jetlinks.community.notify.*;
 import org.jetlinks.community.notify.email.EmailProvider;
@@ -16,9 +18,7 @@ import org.jetlinks.core.Values;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
-import org.springframework.core.io.InputStreamResource;
-import org.springframework.core.io.InputStreamSource;
-import org.springframework.core.io.Resource;
+import org.springframework.core.io.*;
 import org.springframework.http.MediaType;
 import org.springframework.mail.javamail.JavaMailSender;
 import org.springframework.mail.javamail.JavaMailSenderImpl;
@@ -40,6 +40,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * 使用javax.mail进行邮件发送
@@ -63,24 +64,30 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
     @Getter
     private final String notifierId;
 
+    @Setter
+    private boolean enableFileSystemAttachment = Boolean.getBoolean("email.attach.local-file.enabled");
 
-    public static Scheduler scheduler = Schedulers.newElastic("email-notifier");
+    public static Scheduler scheduler = Schedulers.elastic();
 
     public DefaultEmailNotifier(NotifierProperties properties, TemplateManager templateManager) {
-        super(templateManager);
-        notifierId = properties.getId();
-
-        DefaultEmailProperties emailProperties = new JSONObject(properties.getConfiguration())
-            .toJavaObject(DefaultEmailProperties.class);
-        ValidatorUtils.tryValidate(emailProperties);
+        this(properties.getId(),
+             new JSONObject(properties.getConfiguration()).toJavaObject(DefaultEmailProperties.class),
+             templateManager);
+    }
 
+    public DefaultEmailNotifier(String id,
+                                DefaultEmailProperties properties,
+                                TemplateManager templateManager) {
+        super(templateManager);
+        ValidatorUtils.tryValidate(properties);
         JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
-        mailSender.setHost(emailProperties.getHost());
-        mailSender.setPort(emailProperties.getPort());
-        mailSender.setUsername(emailProperties.getUsername());
-        mailSender.setPassword(emailProperties.getPassword());
-        mailSender.setJavaMailProperties(emailProperties.createJavaMailProperties());
-        this.sender = emailProperties.getSender();
+        mailSender.setHost(properties.getHost());
+        mailSender.setPort(properties.getPort());
+        mailSender.setUsername(properties.getUsername());
+        mailSender.setPassword(properties.getPassword());
+        mailSender.setJavaMailProperties(properties.createJavaMailProperties());
+        this.notifierId = id;
+        this.sender = properties.getSender();
         this.javaMailSender = mailSender;
     }
 
@@ -88,8 +95,8 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
     @Override
     public Mono<Void> send(@Nonnull EmailTemplate template, @Nonnull Values context) {
         return Mono.just(template)
-            .map(temp -> convert(temp, context.getAllValues()))
-            .flatMap(temp -> doSend(temp, template.getSendTo()));
+                   .map(temp -> convert(temp, context.getAllValues()))
+                   .flatMap(this::doSend);
     }
 
     @Nonnull
@@ -110,32 +117,36 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
         return EmailProvider.embedded;
     }
 
-    protected Mono<Void> doSend(ParsedEmailTemplate template, List<String> sendTo) {
+    protected Mono<Void> doSend(ParsedEmailTemplate template) {
         return Mono
             .fromCallable(() -> {
                 MimeMessage mimeMessage = this.javaMailSender.createMimeMessage();
                 MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8");
 
                 helper.setFrom(this.sender);
-                helper.setTo(sendTo.toArray(new String[0]));
+                helper.setTo(template.getSendTo().toArray(new String[0]));
                 helper.setSubject(template.getSubject());
                 helper.setText(new String(template.getText().getBytes(), StandardCharsets.UTF_8), true);
 
-                return Flux.fromIterable(template.getAttachments().entrySet())
+                return Flux
+                    .fromIterable(template.getAttachments().entrySet())
                     .flatMap(entry -> Mono.zip(Mono.just(entry.getKey()), convertResource(entry.getValue())))
-                    .doOnNext(tp -> Try.run(() -> helper.addAttachment(MimeUtility.encodeText(tp.getT1()), tp.getT2())).get())
-                    .then(
-                        Flux.fromIterable(template.getImages().entrySet())
-                            .flatMap(entry -> Mono.zip(Mono.just(entry.getKey()), convertResource(entry.getValue())))
-                            .doOnNext(tp -> Try.run(() -> helper.addInline(tp.getT1(), tp.getT2(), MediaType.APPLICATION_OCTET_STREAM_VALUE)).get())
-                            .then()
+                    .doOnNext(tp -> Try
+                        .run(() -> helper.addAttachment(MimeUtility.encodeText(tp.getT1()), tp.getT2())).get())
+                    .then(Flux
+                              .fromIterable(template.getImages().entrySet())
+                              .flatMap(entry -> Mono.zip(Mono.just(entry.getKey()), convertResource(entry.getValue())))
+                              .doOnNext(tp -> Try
+                                  .run(() -> helper.addInline(tp.getT1(), tp.getT2(), MediaType.APPLICATION_OCTET_STREAM_VALUE))
+                                  .get())
+                              .then()
                     ).thenReturn(mimeMessage)
                     ;
 
             })
-            .publishOn(scheduler)
             .flatMap(Function.identity())
             .doOnNext(message -> this.javaMailSender.send(message))
+            .subscribeOn(scheduler)
             .then()
             ;
     }
@@ -143,46 +154,65 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
 
     protected Mono<InputStreamSource> convertResource(String resource) {
         if (resource.startsWith("http")) {
-            return WebClient.create()
+            return WebClient
+                .create()
                 .get()
                 .uri(resource)
                 .accept(MediaType.APPLICATION_OCTET_STREAM)
                 .exchange()
                 .flatMap(rep -> rep.bodyToMono(Resource.class));
+        } else if (resource.startsWith("data:") && resource.contains(";base64,")) {
+            String base64 = resource.substring(resource.indexOf(";base64,") + 8);
+            return Mono.just(
+                new ByteArrayResource(Base64.decodeBase64(base64))
+            );
+        } else if (enableFileSystemAttachment) {
+            return Mono.just(
+                new FileSystemResource(resource)
+            );
         } else {
-            try {
-                return Mono.just(new InputStreamResource(new FileInputStream(resource)));
-            } catch (FileNotFoundException e) {
-                return Mono.error(e);
-            }
+            throw new UnsupportedOperationException("不支持的文件地址:" + resource);
         }
     }
 
 
-    protected ParsedEmailTemplate convert(EmailTemplate template, Map<String, Object> context) {
+    public static ParsedEmailTemplate convert(EmailTemplate template, Map<String, Object> context) {
+        List<String> sendTo = template.getSendTo()
+                                      .stream()
+                                      .map(s -> render(s, context))
+                                      .collect(Collectors.toList());
         String subject = template.getSubject();
         String text = template.getText();
         if (StringUtils.isEmpty(subject) || StringUtils.isEmpty(text)) {
             throw new BusinessException("模板内容错误,text 或者 subject 不能为空.");
         }
-        String sendText = render(text, context);
+        String sendText = render(text, context, true);
         List<EmailTemplate.Attachment> tempAttachments = template.getAttachments();
         Map<String, String> attachments = new HashMap<>();
+
         if (tempAttachments != null) {
-            for (EmailTemplate.Attachment tempAttachment : tempAttachments) {
+            List<EmailTemplate.Attachment> distinctAttachment = tempAttachments
+                .stream()
+                .distinct()
+                .collect(Collectors.toList());
+            for (EmailTemplate.Attachment tempAttachment : distinctAttachment) {
                 attachments.put(tempAttachment.getName(), render(tempAttachment.getLocation(), context));
             }
         }
-        return ParsedEmailTemplate.builder()
+
+
+        return ParsedEmailTemplate
+            .builder()
             .attachments(attachments)
             .images(extractSendTextImage(sendText))
             .text(sendText)
             .subject(render(subject, context))
+            .sendTo(sendTo)
             .build();
     }
 
 
-    private Map<String, String> extractSendTextImage(String sendText) {
+    private static Map<String, String> extractSendTextImage(String sendText) {
         Map<String, String> images = new HashMap<>();
         Document doc = Jsoup.parse(sendText);
         for (Element src : doc.getElementsByTag("img")) {
@@ -197,7 +227,21 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
         return images;
     }
 
-    private String render(String str, Map<String, Object> context) {
+    private static String render(String str, Map<String, Object> context) {
+        return render(str, context, false);
+    }
+
+    private static String render(String str, Map<String, Object> context, boolean html) {
+        if (StringUtils.isEmpty(str)) {
+            return "";
+        }
+        if (html) {
+            return TemplateParser.parse(str, expr ->
+                ExpressionUtils.analytical("${" + Jsoup
+                    .parse(expr)
+                    .text() + "}", context, "spel"));
+        }
+
         return ExpressionUtils.analytical(str, context, "spel");
     }
 

+ 4 - 2
jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/EmailTemplate.java

@@ -1,7 +1,6 @@
 package org.jetlinks.community.notify.email.embedded;
 
-import lombok.Getter;
-import lombok.Setter;
+import lombok.*;
 import org.jetlinks.community.notify.template.Template;
 
 import java.util.List;
@@ -20,6 +19,9 @@ public class EmailTemplate implements Template {
 
     @Getter
     @Setter
+    @AllArgsConstructor
+    @NoArgsConstructor
+    @EqualsAndHashCode(of = "name")
     public static class Attachment {
 
         private String name;

+ 6 - 0
jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/ParsedEmailTemplate.java

@@ -5,6 +5,7 @@ import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -23,7 +24,12 @@ public class ParsedEmailTemplate {
     //图片 key:text中图片占位符 value:图片uri
     private Map<String, String> images;
 
+    //邮件主题
     private String subject;
 
+    //邮件内容
     private String text;
+
+    //发送人集合
+    private List<String> sendTo;
 }