From 19de2dd4f9c51f54b7844f31eeb9bc3188276c27 Mon Sep 17 00:00:00 2001 From: xueye Date: Fri, 2 Jul 2021 13:12:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=82=AE=E4=BB=B6=E9=80=9A=E7=9F=A5=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notify-email/pom.xml | 69 ++++++ .../simaek/notify/email/EmailProvider.java | 31 +++ .../simaek/notify/email/EmailTemplate.java | 95 ++++++++ .../notify/email/EmailTemplateParsed.java | 112 ++++++++++ .../email/embedded/DefaultEmailNotifier.java | 208 ++++++++++++++++++ .../DefaultEmailNotifierProvider.java | 108 +++++++++ .../embedded/DefaultEmailProperties.java | 109 +++++++++ .../cicdi/notify/email/DefaultEmailTest.java | 73 ++++++ 8 files changed, 805 insertions(+) create mode 100644 notify-email/src/main/java/com/simaek/notify/email/EmailProvider.java create mode 100644 notify-email/src/main/java/com/simaek/notify/email/EmailTemplate.java create mode 100644 notify-email/src/main/java/com/simaek/notify/email/EmailTemplateParsed.java create mode 100644 notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifier.java create mode 100644 notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifierProvider.java create mode 100644 notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailProperties.java create mode 100644 notify-email/src/test/java/com/cicdi/notify/email/DefaultEmailTest.java diff --git a/notify-email/pom.xml b/notify-email/pom.xml index 49d5551..8e574ef 100644 --- a/notify-email/pom.xml +++ b/notify-email/pom.xml @@ -16,4 +16,73 @@ 8 + + + com.cicdi + notify-core + 1.0-SNAPSHOT + + + org.springframework + spring-context-support + 5.2.15.RELEASE + + + com.alibaba + fastjson + 1.2.73 + + + com.sun.mail + jakarta.mail + 1.6.7 + + + org.springframework + spring-web + 5.2.10.RELEASE + + + org.hswebframework.web + hsweb-core + 4.0.11-SNAPSHOT + + + org.springframework.boot + spring-boot + 2.3.11.RELEASE + + + org.springframework.boot + spring-boot-starter-web + 2.3.5.RELEASE + + + org.springframework.boot + spring-boot-starter-webflux + 2.3.5.RELEASE + + + org.springframework.boot + spring-boot-autoconfigure + 2.3.11.RELEASE + + + org.jsoup + jsoup + 1.11.3 + + + org.junit.jupiter + junit-jupiter + RELEASE + test + + + junit + junit + 4.12 + test + + \ No newline at end of file diff --git a/notify-email/src/main/java/com/simaek/notify/email/EmailProvider.java b/notify-email/src/main/java/com/simaek/notify/email/EmailProvider.java new file mode 100644 index 0000000..ddb791a --- /dev/null +++ b/notify-email/src/main/java/com/simaek/notify/email/EmailProvider.java @@ -0,0 +1,31 @@ +package com.simaek.notify.email; + +import com.cicdi.notify.Provider; + +/** + * 邮件通知提供商 + * + * @author xueye + */ +public enum EmailProvider implements Provider { + + embedded("默认"), + ; + + private String name; + + EmailProvider(String name) { + this.name = name; + } + + @Override + public String getId() { + return name(); + } + + @Override + public String getName() { + return name; + } + +} diff --git a/notify-email/src/main/java/com/simaek/notify/email/EmailTemplate.java b/notify-email/src/main/java/com/simaek/notify/email/EmailTemplate.java new file mode 100644 index 0000000..c68848c --- /dev/null +++ b/notify-email/src/main/java/com/simaek/notify/email/EmailTemplate.java @@ -0,0 +1,95 @@ +package com.simaek.notify.email; + +import com.cicdi.notify.template.Template; + +import java.util.List; + +/** + * 邮件模板 + * + * @author xueye + */ +public class EmailTemplate implements Template { + + /** + * 收件人 + */ + private List sendTo; + + /** + * 邮件主题 + */ + private String subject; + + /** + * 邮件文本 + */ + private String text; + + /** + * 邮件附件 + */ + private List attachments; + + public static class Attachment { + + /** + * 附件名称 + */ + private String name; + + /** + * 附件位置 + */ + private String location; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + } + + public List getSendTo() { + return sendTo; + } + + public void setSendTo(List sendTo) { + this.sendTo = sendTo; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public List getAttachments() { + return attachments; + } + + public void setAttachments(List attachments) { + this.attachments = attachments; + } +} diff --git a/notify-email/src/main/java/com/simaek/notify/email/EmailTemplateParsed.java b/notify-email/src/main/java/com/simaek/notify/email/EmailTemplateParsed.java new file mode 100644 index 0000000..fd971b8 --- /dev/null +++ b/notify-email/src/main/java/com/simaek/notify/email/EmailTemplateParsed.java @@ -0,0 +1,112 @@ +package com.simaek.notify.email; + +import java.util.Map; + +/** + * 转换过的邮件模板 + * + * @author xueye + */ +public class EmailTemplateParsed { + + /** + * 邮件主题 + */ + private String subject; + + /** + * 邮件正文 + */ + private String text; + + /** + * 图片 key:text中图片占位符 value:图片uri + * 邮件图片 + */ + private Map images; + + /** + * 附件 key:附件名称 value:附件uri + * 邮件附件 + */ + private Map attachments; + + public EmailTemplateParsed() { + } + + public EmailTemplateParsed(EmailTemplateParsed target) { + this.subject = target.subject; + this.text = target.text; + this.images = target.images; + this.attachments = target.attachments; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Map getImages() { + return images; + } + + public void setImages(Map images) { + this.images = images; + } + + public Map getAttachments() { + return attachments; + } + + public void setAttachments(Map attachments) { + this.attachments = attachments; + } + + public static Builder builder(){ + return new Builder(); + }; + + public static class Builder { + private final EmailTemplateParsed target; + + public Builder() { + this.target = new EmailTemplateParsed(); + } + + public Builder subject(String subject) { + target.subject = subject; + return this; + } + + public Builder text(String text) { + target.text = text; + return this; + } + + public Builder images(Map images) { + target.images = images; + return this; + } + + public Builder attachments(Map attachments) { + target.attachments = attachments; + return this; + } + + public EmailTemplateParsed build() { + return new EmailTemplateParsed(target); + } + } + +} diff --git a/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifier.java b/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifier.java new file mode 100644 index 0000000..7dff7b8 --- /dev/null +++ b/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifier.java @@ -0,0 +1,208 @@ +package com.simaek.notify.email.embedded; + +import com.alibaba.fastjson.JSONObject; +import com.cicdi.notify.*; +import com.cicdi.notify.template.TemplateManager; +import com.simaek.notify.email.EmailProvider; +import com.simaek.notify.email.EmailTemplate; +import com.simaek.notify.email.EmailTemplateParsed; +import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeUtility; +import org.hswebframework.web.exception.BusinessException; +import org.hswebframework.web.id.IDGenerator; +import org.hswebframework.web.utils.ExpressionUtils; +import org.hswebframework.web.validator.ValidatorUtils; +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.http.MediaType; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.util.StringUtils; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +/** + * 使用javax.mail进行邮件发送 + * + * @author bsetfeng + * @author zhouhao + * @since 1.0 + **/ +public class DefaultEmailNotifier extends AbstractNotifier { + + private JavaMailSender javaMailSender; + + public JavaMailSender getJavaMailSender() { + return javaMailSender; + } + + public void setJavaMailSender(JavaMailSender javaMailSender) { + this.javaMailSender = javaMailSender; + } + + private String sender; + + public String getSender() { + return sender; + } + + public void setSender(String sender) { + this.sender = sender; + } + + private final String notifierId; + + @Override + public String getNotifierId() { + return notifierId; + } + +// public static Scheduler scheduler = Schedulers.newElastic("email-notifier"); + + public DefaultEmailNotifier(NotifierProperties properties, TemplateManager templateManager) { + super(templateManager); + notifierId = properties.getId(); + + DefaultEmailProperties emailProperties = new JSONObject(properties.getConfiguration()) + .toJavaObject(DefaultEmailProperties.class); +// ValidatorUtils.tryValidate(emailProperties); + + 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(); + this.javaMailSender = mailSender; + } + + @Override + public void send(EmailTemplate template, Map context) { + EmailTemplateParsed emailTemplateParsed = convert(template, context); + try { + this.doSend(emailTemplateParsed, template.getSendTo()); + } catch (MessagingException | UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + @Override + public void close() { + } + + @Override + public NotifyType getType() { + return DefaultNotifyType.email; + } + + @Override + public Provider getProvider() { + return EmailProvider.embedded; + } + + protected void doSend(EmailTemplateParsed template, List sendTo) throws MessagingException, UnsupportedEncodingException { + 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.setSubject(template.getSubject()); + helper.setText(new String(template.getText().getBytes(), StandardCharsets.UTF_8), true); + + Set> entries = template.getAttachments().entrySet(); + for (Map.Entry entry : entries) { + helper.addAttachment(MimeUtility.encodeText(entry.getKey()), convertResource(entry.getValue())); + } + Set> imageEntries = template.getImages().entrySet(); + for (Map.Entry image : imageEntries) { + helper.addInline(image.getKey(), convertResource(image.getValue()), MediaType.APPLICATION_OCTET_STREAM_VALUE); + } + this.javaMailSender.send(mimeMessage); + } + + + protected InputStreamSource convertResource(String resource) { + if (resource.startsWith("http")) { +// Mono resourceMono = +// InputStream is = new FileInputStream(); +// InputStreamSource iss = new InputStreamResource(is, null); + // TODO 使用其他工具实现,不用反应式框架 + WebClient.create() + .get() + .uri(resource) + .accept(MediaType.APPLICATION_OCTET_STREAM) + .exchange() + .flatMap(rep -> rep.bodyToMono(Resource.class)) + ; + } else { + try { + return new InputStreamResource(new FileInputStream(resource)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + return null; + } + + + protected EmailTemplateParsed convert(EmailTemplate template, Map context) { + 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); + List tempAttachments = template.getAttachments(); + Map attachments = new HashMap<>(); + if (tempAttachments != null) { + for (EmailTemplate.Attachment tempAttachment : tempAttachments) { + attachments.put(tempAttachment.getName(), render(tempAttachment.getLocation(), context)); + } + } + return EmailTemplateParsed.builder() + .attachments(attachments) + .images(extractSendTextImage(sendText)) + .text(sendText) + .subject(render(subject, context)) + .build(); + } + + + private Map extractSendTextImage(String sendText) { + Map images = new HashMap<>(); + Document doc = Jsoup.parse(sendText); + for (Element src : doc.getElementsByTag("img")) { + String s = src.attr("src"); + if (s.startsWith("http")) { + continue; + } + String tempKey = IDGenerator.MD5.generate(); + src.attr("src", "cid:".concat(tempKey)); + images.put(tempKey, s); + } + return images; + } + + private String render(String str, Map context) { + return ExpressionUtils.analytical(str, context, "spel"); + } + +} diff --git a/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifierProvider.java b/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifierProvider.java new file mode 100644 index 0000000..87d5413 --- /dev/null +++ b/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailNotifierProvider.java @@ -0,0 +1,108 @@ +package com.simaek.notify.email.embedded; + +import com.alibaba.fastjson.JSON; +import com.cicdi.notify.*; +import com.cicdi.notify.template.TemplateManager; +import com.cicdi.notify.template.TemplateProperties; +import com.cicdi.notify.template.TemplateProvider; +import com.simaek.notify.email.EmailProvider; +import com.simaek.notify.email.EmailTemplate; + +public class DefaultEmailNotifierProvider implements NotifierProvider, TemplateProvider { + + private final TemplateManager templateManager; + + public DefaultEmailNotifierProvider(TemplateManager templateManager) { + this.templateManager = templateManager; + } + + @Override + public NotifyType getType() { + return DefaultNotifyType.email; + } + + @Override + public Provider getProvider() { + return EmailProvider.embedded; + } + +// public static final DefaultConfigMetadata templateConfig; +// +// public static final DefaultConfigMetadata notifierConfig; +// +// static { +// { +// SimplePropertyMetadata name = new SimplePropertyMetadata(); +// name.setId("name"); +// name.setName("文件名"); +// name.setValueType(new StringType()); +// +// SimplePropertyMetadata location = new SimplePropertyMetadata(); +// location.setId("location"); +// location.setName("文件地址"); +// location.setValueType(new FileType() +// .bodyType(FileType.BodyType.url) +// .expand(allowInput.value(true))); +// +// templateConfig = new DefaultConfigMetadata("邮件模版", "") +// .add("subject", "标题", "标题,可使用变量", new StringType().expand(maxLength.value(255L))) +// .add("text", "内容", "", new StringType().expand(maxLength.value(5120L), isRichText.value(true))) +// .add("sendTo", "收件人", "", new ArrayType().elementType(new StringType())) +// .add("attachments", "附件列表", "", new ArrayType() +// .elementType(new ObjectType() +// .addPropertyMetadata(name) +// .addPropertyMetadata(location))); +// } +// +// { +// SimplePropertyMetadata name = new SimplePropertyMetadata(); +// name.setId("name"); +// name.setName("配置名称"); +// name.setValueType(new StringType()); +// +// SimplePropertyMetadata value = new SimplePropertyMetadata(); +// value.setId("value"); +// value.setName("配置值"); +// value.setValueType(new StringType()); +// +// SimplePropertyMetadata description = new SimplePropertyMetadata(); +// description.setId("description"); +// description.setName("说明"); +// description.setValueType(new StringType()); +// +// notifierConfig = new DefaultConfigMetadata("邮件配置", "") +// .add("host", "服务器地址", "例如: pop3.qq.com", new StringType().expand(maxLength.value(255L))) +// .add("port", "端口", "", new IntType().min(0).max(65536)) +// .add("sender", "发件人", "默认和用户名相同", new StringType()) +// .add("username", "用户名", "", new StringType()) +// .add("password", "密码", "", new PasswordType()) +// .add("properties", "其他配置", "", new ArrayType() +// .elementType(new ObjectType() +// .addPropertyMetadata(name) +// .addPropertyMetadata(value) +// .addPropertyMetadata(description))); +// } +// +// +// } +// +// @Override +// public ConfigMetadata getNotifierConfigMetadata() { +// return notifierConfig; +// } +// +// @Override +// public ConfigMetadata getTemplateConfigMetadata() { +// return templateConfig; +// } + + @Override + public DefaultEmailNotifier createNotifier(NotifierProperties properties) { + return new DefaultEmailNotifier(properties, templateManager); + } + + @Override + public EmailTemplate createTemplate(TemplateProperties properties) { + return JSON.parseObject(properties.getTemplate(), EmailTemplate.class); + } +} diff --git a/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailProperties.java b/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailProperties.java new file mode 100644 index 0000000..55dd8e9 --- /dev/null +++ b/notify-email/src/main/java/com/simaek/notify/email/embedded/DefaultEmailProperties.java @@ -0,0 +1,109 @@ +package com.simaek.notify.email.embedded; + +import java.util.List; +import java.util.Properties; + +public class DefaultEmailProperties { + private String host; + + private int port; + + private String username; + + private String password; + + private String sender; + + private List properties; + + public static class ConfigProperty { + + private String name; + + private String value; + + private String description; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + } + + public Properties createJavaMailProperties() { + Properties properties = new Properties(); + if (this.properties != null) { + for (ConfigProperty property : this.properties) { + properties.put(property.getName(), property.getValue()); + } + } + return properties; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSender() { + return sender; + } + + public void setSender(String sender) { + this.sender = sender; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } +} diff --git a/notify-email/src/test/java/com/cicdi/notify/email/DefaultEmailTest.java b/notify-email/src/test/java/com/cicdi/notify/email/DefaultEmailTest.java new file mode 100644 index 0000000..65645a1 --- /dev/null +++ b/notify-email/src/test/java/com/cicdi/notify/email/DefaultEmailTest.java @@ -0,0 +1,73 @@ +package com.cicdi.notify.email; + +import com.cicdi.notify.*; +import com.cicdi.notify.template.AbstractTemplateManager; +import com.cicdi.notify.template.Template; +import com.cicdi.notify.template.TemplateManager; +import com.cicdi.notify.template.TemplateProperties; +import com.simaek.notify.email.EmailProvider; +import com.simaek.notify.email.embedded.DefaultEmailNotifierProvider; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * @author xueye + */ +public class DefaultEmailTest { + + + @Test + public void testSend() { + // 通知器配置管理器 + NotifyConfigManager notifyConfigManager = new NotifyConfigManager() { + @Override + public NotifierProperties getNotifyConfig(NotifyType notifyType, String configId) { + NotifierProperties notifierProperties = new NotifierProperties(); + notifierProperties.setType(DefaultNotifyType.email.getId()); + notifierProperties.setProvider(EmailProvider.embedded.getId()); + notifierProperties.setId("12"); + + Map config = new HashMap<>(); + + config.put("host", "nas.xueye.io"); + config.put("port", 587); + config.put("username", "xueye"); + config.put("password", "snow@PX1314"); + config.put("sender", "master@simaek.com"); + Map p1 = new HashMap<>(); + p1.put("name", "mail.smtp.starttls.enable"); + p1.put("value", "true"); + Map p2 = new HashMap<>(); + p2.put("name", "mail.smtp.auth"); + p2.put("value", "true"); + config.put("properties", Arrays.asList(p1, p2)); + notifierProperties.setConfiguration(config); + return notifierProperties; + } + }; + + // 模板管理器 + TemplateManager templateManager = new AbstractTemplateManager() { + @Override + protected TemplateProperties getProperties(NotifyType type, String id) { + TemplateProperties templateProperties = new TemplateProperties(); + templateProperties.setType(DefaultNotifyType.email.getId()); + templateProperties.setProvider(EmailProvider.embedded.getId()); + templateProperties.setTemplate("{\"subject\":\"Hello\",\"sendTo\":[\"xueye404@qq.com\"],\"text\":\"

This is a test

\",\"attachments\":[]}"); + return templateProperties; + } + }; + + NotifierManager notifierManager = new DefaultNotifierManager(notifyConfigManager); + DefaultEmailNotifierProvider defaultEmailNotifierProvider = new DefaultEmailNotifierProvider(templateManager); + // register + notifierManager.registerProvider(defaultEmailNotifierProvider); + templateManager.register(defaultEmailNotifierProvider); + + Notifier