diff --git a/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/DefaultEmailNotifier.java b/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/DefaultEmailNotifier.java index 218ad2c9..69ac93e8 100644 --- a/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/DefaultEmailNotifier.java +++ b/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 { @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) { + this(properties.getId(), + new JSONObject(properties.getConfiguration()).toJavaObject(DefaultEmailProperties.class), + templateManager); + } + + public DefaultEmailNotifier(String id, + DefaultEmailProperties properties, + TemplateManager templateManager) { super(templateManager); - notifierId = properties.getId(); - - DefaultEmailProperties emailProperties = new JSONObject(properties.getConfiguration()) - .toJavaObject(DefaultEmailProperties.class); - ValidatorUtils.tryValidate(emailProperties); - + 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 { @Override public Mono 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 { return EmailProvider.embedded; } - protected Mono doSend(ParsedEmailTemplate template, List sendTo) { + protected Mono 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 { protected Mono 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 context) { + public static ParsedEmailTemplate convert(EmailTemplate template, Map context) { + List 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 tempAttachments = template.getAttachments(); Map attachments = new HashMap<>(); + if (tempAttachments != null) { - for (EmailTemplate.Attachment tempAttachment : tempAttachments) { + List 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 extractSendTextImage(String sendText) { + private static Map extractSendTextImage(String sendText) { Map images = new HashMap<>(); Document doc = Jsoup.parse(sendText); for (Element src : doc.getElementsByTag("img")) { @@ -197,7 +227,21 @@ public class DefaultEmailNotifier extends AbstractNotifier { return images; } - private String render(String str, Map context) { + private static String render(String str, Map context) { + return render(str, context, false); + } + + private static String render(String str, Map 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"); } diff --git a/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/EmailTemplate.java b/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/EmailTemplate.java index f7789c07..fdb9e16c 100644 --- a/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/EmailTemplate.java +++ b/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; diff --git a/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/ParsedEmailTemplate.java b/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/ParsedEmailTemplate.java index 1b7fcf76..36116209 100644 --- a/jetlinks-components/notify-component/notify-email/src/main/java/org/jetlinks/community/notify/email/embedded/ParsedEmailTemplate.java +++ b/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 images; + //邮件主题 private String subject; + //邮件内容 private String text; + + //发送人集合 + private List sendTo; }