|
@@ -5,9 +5,11 @@ import io.vavr.control.Try;
|
|
import lombok.Getter;
|
|
import lombok.Getter;
|
|
import lombok.Setter;
|
|
import lombok.Setter;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.apache.commons.codec.binary.Base64;
|
|
import org.hswebframework.web.exception.BusinessException;
|
|
import org.hswebframework.web.exception.BusinessException;
|
|
import org.hswebframework.web.id.IDGenerator;
|
|
import org.hswebframework.web.id.IDGenerator;
|
|
import org.hswebframework.web.utils.ExpressionUtils;
|
|
import org.hswebframework.web.utils.ExpressionUtils;
|
|
|
|
+import org.hswebframework.web.utils.TemplateParser;
|
|
import org.hswebframework.web.validator.ValidatorUtils;
|
|
import org.hswebframework.web.validator.ValidatorUtils;
|
|
import org.jetlinks.community.notify.*;
|
|
import org.jetlinks.community.notify.*;
|
|
import org.jetlinks.community.notify.email.EmailProvider;
|
|
import org.jetlinks.community.notify.email.EmailProvider;
|
|
@@ -16,9 +18,7 @@ import org.jetlinks.core.Values;
|
|
import org.jsoup.Jsoup;
|
|
import org.jsoup.Jsoup;
|
|
import org.jsoup.nodes.Document;
|
|
import org.jsoup.nodes.Document;
|
|
import org.jsoup.nodes.Element;
|
|
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.http.MediaType;
|
|
import org.springframework.mail.javamail.JavaMailSender;
|
|
import org.springframework.mail.javamail.JavaMailSender;
|
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
|
@@ -40,6 +40,7 @@ import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
import java.util.function.Function;
|
|
import java.util.function.Function;
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
/**
|
|
* 使用javax.mail进行邮件发送
|
|
* 使用javax.mail进行邮件发送
|
|
@@ -63,24 +64,30 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
|
|
@Getter
|
|
@Getter
|
|
private final String notifierId;
|
|
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) {
|
|
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();
|
|
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;
|
|
this.javaMailSender = mailSender;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -88,8 +95,8 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
|
|
@Override
|
|
@Override
|
|
public Mono<Void> send(@Nonnull EmailTemplate template, @Nonnull Values context) {
|
|
public Mono<Void> send(@Nonnull EmailTemplate template, @Nonnull Values context) {
|
|
return Mono.just(template)
|
|
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
|
|
@Nonnull
|
|
@@ -110,32 +117,36 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
|
|
return EmailProvider.embedded;
|
|
return EmailProvider.embedded;
|
|
}
|
|
}
|
|
|
|
|
|
- protected Mono<Void> doSend(ParsedEmailTemplate template, List<String> sendTo) {
|
|
|
|
|
|
+ protected Mono<Void> doSend(ParsedEmailTemplate template) {
|
|
return Mono
|
|
return Mono
|
|
.fromCallable(() -> {
|
|
.fromCallable(() -> {
|
|
MimeMessage mimeMessage = this.javaMailSender.createMimeMessage();
|
|
MimeMessage mimeMessage = this.javaMailSender.createMimeMessage();
|
|
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8");
|
|
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8");
|
|
|
|
|
|
helper.setFrom(this.sender);
|
|
helper.setFrom(this.sender);
|
|
- helper.setTo(sendTo.toArray(new String[0]));
|
|
|
|
|
|
+ helper.setTo(template.getSendTo().toArray(new String[0]));
|
|
helper.setSubject(template.getSubject());
|
|
helper.setSubject(template.getSubject());
|
|
helper.setText(new String(template.getText().getBytes(), StandardCharsets.UTF_8), true);
|
|
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())))
|
|
.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)
|
|
).thenReturn(mimeMessage)
|
|
;
|
|
;
|
|
|
|
|
|
})
|
|
})
|
|
- .publishOn(scheduler)
|
|
|
|
.flatMap(Function.identity())
|
|
.flatMap(Function.identity())
|
|
.doOnNext(message -> this.javaMailSender.send(message))
|
|
.doOnNext(message -> this.javaMailSender.send(message))
|
|
|
|
+ .subscribeOn(scheduler)
|
|
.then()
|
|
.then()
|
|
;
|
|
;
|
|
}
|
|
}
|
|
@@ -143,46 +154,65 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
|
|
|
|
|
|
protected Mono<InputStreamSource> convertResource(String resource) {
|
|
protected Mono<InputStreamSource> convertResource(String resource) {
|
|
if (resource.startsWith("http")) {
|
|
if (resource.startsWith("http")) {
|
|
- return WebClient.create()
|
|
|
|
|
|
+ return WebClient
|
|
|
|
+ .create()
|
|
.get()
|
|
.get()
|
|
.uri(resource)
|
|
.uri(resource)
|
|
.accept(MediaType.APPLICATION_OCTET_STREAM)
|
|
.accept(MediaType.APPLICATION_OCTET_STREAM)
|
|
.exchange()
|
|
.exchange()
|
|
.flatMap(rep -> rep.bodyToMono(Resource.class));
|
|
.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 {
|
|
} 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 subject = template.getSubject();
|
|
String text = template.getText();
|
|
String text = template.getText();
|
|
if (StringUtils.isEmpty(subject) || StringUtils.isEmpty(text)) {
|
|
if (StringUtils.isEmpty(subject) || StringUtils.isEmpty(text)) {
|
|
throw new BusinessException("模板内容错误,text 或者 subject 不能为空.");
|
|
throw new BusinessException("模板内容错误,text 或者 subject 不能为空.");
|
|
}
|
|
}
|
|
- String sendText = render(text, context);
|
|
|
|
|
|
+ String sendText = render(text, context, true);
|
|
List<EmailTemplate.Attachment> tempAttachments = template.getAttachments();
|
|
List<EmailTemplate.Attachment> tempAttachments = template.getAttachments();
|
|
Map<String, String> attachments = new HashMap<>();
|
|
Map<String, String> attachments = new HashMap<>();
|
|
|
|
+
|
|
if (tempAttachments != null) {
|
|
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));
|
|
attachments.put(tempAttachment.getName(), render(tempAttachment.getLocation(), context));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- return ParsedEmailTemplate.builder()
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return ParsedEmailTemplate
|
|
|
|
+ .builder()
|
|
.attachments(attachments)
|
|
.attachments(attachments)
|
|
.images(extractSendTextImage(sendText))
|
|
.images(extractSendTextImage(sendText))
|
|
.text(sendText)
|
|
.text(sendText)
|
|
.subject(render(subject, context))
|
|
.subject(render(subject, context))
|
|
|
|
+ .sendTo(sendTo)
|
|
.build();
|
|
.build();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
- private Map<String, String> extractSendTextImage(String sendText) {
|
|
|
|
|
|
+ private static Map<String, String> extractSendTextImage(String sendText) {
|
|
Map<String, String> images = new HashMap<>();
|
|
Map<String, String> images = new HashMap<>();
|
|
Document doc = Jsoup.parse(sendText);
|
|
Document doc = Jsoup.parse(sendText);
|
|
for (Element src : doc.getElementsByTag("img")) {
|
|
for (Element src : doc.getElementsByTag("img")) {
|
|
@@ -197,7 +227,21 @@ public class DefaultEmailNotifier extends AbstractNotifier<EmailTemplate> {
|
|
return images;
|
|
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");
|
|
return ExpressionUtils.analytical(str, context, "spel");
|
|
}
|
|
}
|
|
|
|
|