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 notifier = notifierManager.getNotifier(DefaultNotifyType.email, "123");
+ notifier.send(templateManager.getTemplate(DefaultNotifyType.email, "124"), new HashMap<>());
+ }
+}