From a36cc7eecaf36c92ebdde20aa87aa3ef1fb0a647 Mon Sep 17 00:00:00 2001 From: Bleachtred Date: Thu, 4 Jan 2024 00:34:22 +0000 Subject: [PATCH] =?UTF-8?q?!125=20add=20=E6=96=B0=E5=A2=9Esms4j-email-jaka?= =?UTF-8?q?rta=E5=88=86=E6=94=AF=20*=20update=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=E5=86=85=E5=AE=B9=20*=20add=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9Esms4j-email-jakarta=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 23 +- sms4j-Email-plugin/pom.xml | 5 - sms4j-Email-plugin/sms4j-Email-core/pom.xml | 11 - sms4j-email-jakarta/pom.xml | 63 +++++ .../sms4j-email-jakarta-api/pom.xml | 23 ++ .../dromara/email/jakarta/api/Blacklist.java | 14 ++ .../dromara/email/jakarta/api/MailClient.java | 14 ++ .../dromara/email/jakarta/api/Monitor.java | 21 ++ .../sms4j-email-jakarta-comm/pom.xml | 33 +++ .../jakarta/comm/config/MailImapConfig.java | 19 ++ .../jakarta/comm/config/MailSmtpConfig.java | 67 ++++++ .../jakarta/comm/constants/FileConstants.java | 9 + .../jakarta/comm/entity/MailMessage.java | 178 ++++++++++++++ .../jakarta/comm/entity/MonitorMessage.java | 40 +++ .../email/jakarta/comm/entity/Parameter.java | 11 + .../jakarta/comm/errors/MailException.java | 23 ++ .../email/jakarta/comm/utils/BaseUtil.java | 15 ++ .../email/jakarta/comm/utils/HtmlUtil.java | 130 ++++++++++ .../email/jakarta/comm/utils/ReflectUtil.java | 36 +++ .../email/jakarta/comm/utils/ZipUtils.java | 111 +++++++++ .../sms4j-email-jakarta-core/pom.xml | 27 +++ .../jakarta/core/factory/MailFactory.java | 63 +++++ .../jakarta/core/factory/MonitorFactory.java | 65 +++++ .../email/jakarta/core/package-info.java | 6 + .../email/jakarta/core/service/MailBuild.java | 107 +++++++++ .../jakarta/core/service/MailService.java | 227 ++++++++++++++++++ .../jakarta/core/service/MonitorService.java | 133 ++++++++++ 27 files changed, 1449 insertions(+), 25 deletions(-) create mode 100644 sms4j-email-jakarta/pom.xml create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-api/pom.xml create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Blacklist.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/MailClient.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Monitor.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/pom.xml create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailImapConfig.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailSmtpConfig.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/constants/FileConstants.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MailMessage.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MonitorMessage.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/Parameter.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/errors/MailException.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/BaseUtil.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/HtmlUtil.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ReflectUtil.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ZipUtils.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/pom.xml create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MailFactory.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MonitorFactory.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/package-info.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailBuild.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailService.java create mode 100644 sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MonitorService.java diff --git a/pom.xml b/pom.xml index 500edef5..d744a170 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ sms4j-javase-plugin sms4j-Email-plugin sms4j-oa-plugin + sms4j-email-jakarta @@ -55,16 +56,15 @@ UTF-8 UTF-8 - 2.7.15 + 2.7.18 2.5.4 3.17.0 1.3.3 - 5.8.20 + 5.8.24 2.3.0 1.1.1 1.6.2 1.2.0 - 1.2.2 2.0 @@ -77,6 +77,16 @@ ${spring.boot.version} pom import + + + jakarta.activation + jakarta.activation-api + + + jakarta.mail + jakarta.mail-api + + @@ -169,12 +179,6 @@ ${sunactivation.version} - - jakarta.activation - jakarta.activation-api - ${jakarta.activation.version} - - @@ -311,4 +315,5 @@ + diff --git a/sms4j-Email-plugin/pom.xml b/sms4j-Email-plugin/pom.xml index 7b76fae9..475943d4 100644 --- a/sms4j-Email-plugin/pom.xml +++ b/sms4j-Email-plugin/pom.xml @@ -57,11 +57,6 @@ ${sunactivation.version} - - jakarta.activation - jakarta.activation-api - ${jakarta.activation.version} - cn.hutool hutool-cron diff --git a/sms4j-Email-plugin/sms4j-Email-core/pom.xml b/sms4j-Email-plugin/sms4j-Email-core/pom.xml index c1467f68..c3b1b61b 100644 --- a/sms4j-Email-plugin/sms4j-Email-core/pom.xml +++ b/sms4j-Email-plugin/sms4j-Email-core/pom.xml @@ -13,10 +13,6 @@ sms4j-Email-core sms4j-Email-core - - - - org.dromara.sms4j @@ -27,13 +23,6 @@ com.sun.activation javax.activation - - - jakarta.activation - jakarta.activation-api - - - diff --git a/sms4j-email-jakarta/pom.xml b/sms4j-email-jakarta/pom.xml new file mode 100644 index 00000000..dcc70646 --- /dev/null +++ b/sms4j-email-jakarta/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + org.dromara.sms4j + sms4j + ${revision} + ../pom.xml + + pom + sms4j-email-jakarta + + + sms4j-email-jakarta-api + sms4j-email-jakarta-comm + sms4j-email-jakarta-core + + + sms4j-email-jakarta + JDK 11 或更高版本中使用 + + + 2.1.2 + + + + + + + org.dromara.sms4j + sms4j-email-jakarta-api + ${revision} + + + + org.dromara.sms4j + sms4j-email-jakarta-comm + ${revision} + + + + org.dromara.sms4j + sms4j-email-jakarta-core + ${revision} + + + + jakarta.activation + jakarta.activation-api + ${jakarta-mail.version} + + + + jakarta.mail + jakarta.mail-api + ${jakarta-mail.version} + + + + + + diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-api/pom.xml b/sms4j-email-jakarta/sms4j-email-jakarta-api/pom.xml new file mode 100644 index 00000000..6afcc6d0 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-api/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + org.dromara.sms4j + sms4j-email-jakarta + ${revision} + + + sms4j-email-jakarta-api + email-api + + + + + org.dromara.sms4j + sms4j-email-jakarta-comm + + + + + diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Blacklist.java b/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Blacklist.java new file mode 100644 index 00000000..7530064a --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Blacklist.java @@ -0,0 +1,14 @@ +package org.dromara.email.jakarta.api; + +import java.util.List; + +/** + * Blacklist + *

黑名单实现 实现此接口,发送邮件时将自动排除调黑名单中的收件人 + * @author :Wind + * 2023/6/8 23:05 + **/ +public interface Blacklist { + + List getBlacklist(); +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/MailClient.java b/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/MailClient.java new file mode 100644 index 00000000..ee0e5e0a --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/MailClient.java @@ -0,0 +1,14 @@ +package org.dromara.email.jakarta.api; + +import org.dromara.email.jakarta.comm.entity.MailMessage; + +public interface MailClient { + + /** + * 发送邮件,可以通过对象构造群体发送或者单体发送,取决于添加进去的收件人,同时可以添加 + * 密送人,抄送人,附件等参数 + * @param mailMessage 发送邮件参数对象 + * @author :Wind + */ + void send(MailMessage mailMessage); +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Monitor.java b/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Monitor.java new file mode 100644 index 00000000..1aec16e9 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-api/src/main/java/org/dromara/email/jakarta/api/Monitor.java @@ -0,0 +1,21 @@ +package org.dromara.email.jakarta.api; + +import org.dromara.email.jakarta.comm.entity.MonitorMessage; + +/** + * Monitor + *

监听接口,实现此接口用于监听并获取邮件消息 + * @author :Wind + * 2023/7/18 15:57 + **/ +public interface Monitor { + + /** + * monitor + *

监听系统的邮件消息, + * @param monitorMessage 系统监听到的消息内容 + * @return true为标记已读,否则不标记 + * @author :Wind + */ + boolean monitor(MonitorMessage monitorMessage); +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/pom.xml b/sms4j-email-jakarta/sms4j-email-jakarta-comm/pom.xml new file mode 100644 index 00000000..e324c428 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + org.dromara.sms4j + sms4j-email-jakarta + ${revision} + + + sms4j-email-jakarta-comm + email通用配置 + + + + + cn.hutool + hutool-cron + + + + cn.hutool + hutool-http + + + + jakarta.mail + jakarta.mail-api + + + + + diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailImapConfig.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailImapConfig.java new file mode 100644 index 00000000..9ed336cb --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailImapConfig.java @@ -0,0 +1,19 @@ +package org.dromara.email.jakarta.comm.config; + +import lombok.Data; + +@Data +public class MailImapConfig { + + /** imap服务地址*/ + private String imapServer; + + /** 要监听的邮箱账号*/ + private String username; + + /** 要监听的邮箱授权码或密码*/ + private String accessToken; + + /** 监听周期(秒)*/ + private Integer cycle = 5; +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailSmtpConfig.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailSmtpConfig.java new file mode 100644 index 00000000..61b696b1 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/config/MailSmtpConfig.java @@ -0,0 +1,67 @@ +package org.dromara.email.jakarta.comm.config; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * MailSmtpConfig + *

smtp协议配置文件 + * @author :Wind + * 2023/6/7 21:19 + **/ +@Builder +@ToString +@Getter +@EqualsAndHashCode +public class MailSmtpConfig { + /** + * 端口号 + * */ + private String port; + + /** + * 发件人地址 + * */ + private String fromAddress; + + /** + * 服务器地址 + * */ + private String smtpServer; + + /** + * 账号 + * */ + private String username; + + /** + * 密码 + * */ + private String password; + + /** + * 是否开启ssl 默认开启 + * */ + @Builder.Default + private String isSSL = "true"; + + /** + * 是否开启验证 默认开启 + * */ + @Builder.Default + private String isAuth = "true"; + + /** + * 重试间隔(单位:秒),默认为5秒 + */ + @Builder.Default + private int retryInterval = 5; + + /** + * 重试次数,默认为1次 + */ + @Builder.Default + private int maxRetries = 1; +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/constants/FileConstants.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/constants/FileConstants.java new file mode 100644 index 00000000..c1eb2528 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/constants/FileConstants.java @@ -0,0 +1,9 @@ +package org.dromara.email.jakarta.comm.constants; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FileConstants { + public static final String IO_FILE_TYPE = "application/octet-stream"; +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MailMessage.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MailMessage.java new file mode 100644 index 00000000..829715ad --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MailMessage.java @@ -0,0 +1,178 @@ +package org.dromara.email.jakarta.comm.entity; + +import lombok.Getter; +import org.dromara.email.jakarta.comm.utils.ReflectUtil; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Getter +public class MailMessage { + + /** 收件人地址*/ + private List mailAddress; + + /** 邮件主题*/ + private String title; + + /** 文字正文*/ + private String body; + + /** html模板文件路径(resources目录下的路径)*/ + private String htmlPath; + + /** html模板文件的输入流,可来自任意可读取位置*/ + private InputStream htmlInputStream; + + /** html 模板参数*/ + private Map htmlValues; + + /** 抄送人*/ + private List cc; + + /** 密送人*/ + private List bcc; + + /** 附件*/ + private Map files; + + /** 压缩文件名称*/ + private String zipName; + + public static MailsBuilder Builder(){ + return new MailsBuilder(); + } + + public static class MailsBuilder{ + private final MailMessage mailMessage = new MailMessage(); + public MailsBuilder() { + } + public MailMessage build(){ + return mailMessage; + } + + /** 收件人地址*/ + public MailsBuilder mailAddress(List mailAddress) { + mailMessage.mailAddress = mailAddress; + return this; + } + + /** 收件人地址*/ + public MailsBuilder mailAddress(String mailAddress){ + if ( mailMessage.mailAddress == null){ + mailMessage.mailAddress = new ArrayList<>(); + } + mailMessage.mailAddress.add(mailAddress); + return this; + } + + /** 邮件主题*/ + public MailsBuilder title(String title){ + mailMessage.title = title; + return this; + } + + /** 文字正文*/ + public MailsBuilder body(String body){ + mailMessage.body = body; + return this; + } + + /** 抄送人*/ + public MailsBuilder cc(List cc){ + mailMessage.cc = cc; + return this; + } + + /** 抄送人*/ + public MailsBuilder cc(String cc){ + if (mailMessage.cc == null){ + mailMessage.cc = new ArrayList<>(); + } + mailMessage.cc.add(cc); + return this; + } + + /** 密送人*/ + public MailsBuilder bcc(List bcc){ + mailMessage.bcc = bcc; + return this; + } + + /** 密送人*/ + public MailsBuilder bcc(String bcc){ + if (mailMessage.bcc == null){ + mailMessage.bcc = new ArrayList<>(); + } + mailMessage.bcc.add(bcc); + return this; + } + + /** 附件*/ + public MailsBuilder files(Map files){ + if (mailMessage.files == null){ + mailMessage.files = new HashMap<>(); + } + mailMessage.files.putAll(files); + return this; + } + + /** 附件*/ + public MailsBuilder files(String fileName,String filePath){ + if (mailMessage.files == null){ + mailMessage.files = new HashMap<>(); + } + mailMessage.files.put(fileName,filePath); + return this; + } + + /** html模板文件路径(resources目录下的路径)*/ + public MailsBuilder html(String htmlPath){ + mailMessage.htmlPath = htmlPath; + return this; + } + + /** html模板文件的输入流,可来自任意可读取位置*/ + public MailsBuilder html(InputStream htmlInputStream){ + mailMessage.htmlInputStream = htmlInputStream; + return this; + } + + /** html 模板参数*/ + public MailsBuilder htmlValues(String key, String value){ + if (mailMessage.htmlValues == null){ + mailMessage.htmlValues = new HashMap<>(); + } + mailMessage.htmlValues.put(key,value); + return this; + } + + /** html 模板参数*/ + public MailsBuilder htmlValues(Map htmlValues){ + if (mailMessage.htmlValues == null){ + mailMessage.htmlValues = new HashMap<>(); + } + mailMessage.htmlValues.putAll(htmlValues); + return this; + } + + /** html 模板参数*/ + public MailsBuilder htmlValues(Parameter parameter){ + Map values = ReflectUtil.getValues(parameter); + if (mailMessage.htmlValues == null){ + mailMessage.htmlValues = new HashMap<>(); + } + mailMessage.htmlValues.putAll(values); + return this; + } + + /** 压缩文件名称*/ + public MailsBuilder zipName(String zipName){ + mailMessage.zipName = zipName; + return this; + } + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MonitorMessage.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MonitorMessage.java new file mode 100644 index 00000000..d593f151 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/MonitorMessage.java @@ -0,0 +1,40 @@ +package org.dromara.email.jakarta.comm.entity; + +import lombok.Data; +import jakarta.mail.Multipart; + +import java.util.Date; + +/** + * MonitorMessage + *

监听获取到的邮件内容 + * @author :Wind + * 2023/7/18 15:59 + **/ +@Data +public class MonitorMessage { + + /** 邮件主题*/ + private String title; + + /** 邮件文字内容*/ + private String text; + + /** 解析的html内容*/ + private String htmlText; + + /** 邮件发送时间*/ + private Date sendDate; + + /** 邮件内容(复杂内容)*/ + private Multipart body; + + /** 发送人*/ + private String fromAddress; + + /** 邮件消息编号*/ + private Integer messageIndex; + + /** 接收时间*/ + private Long acceptTime; +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/Parameter.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/Parameter.java new file mode 100644 index 00000000..4a5b590c --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/entity/Parameter.java @@ -0,0 +1,11 @@ +package org.dromara.email.jakarta.comm.entity; + +/** + * Parameter + *

空接口,用于标定用户自己的实体类型 + * 用于发送html模板邮件时候 用户传递自己的实体序列化进行的类型标定 + * @author :Wind + * 2023/6/8 19:36 + **/ +public interface Parameter { +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/errors/MailException.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/errors/MailException.java new file mode 100644 index 00000000..8b5a9adb --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/errors/MailException.java @@ -0,0 +1,23 @@ +package org.dromara.email.jakarta.comm.errors; + +public class MailException extends RuntimeException { + public MailException() { + super(); + } + + public MailException(String message) { + super(message); + } + + public MailException(String message, Throwable cause) { + super(message, cause); + } + + public MailException(Throwable cause) { + super(cause); + } + + protected MailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/BaseUtil.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/BaseUtil.java new file mode 100644 index 00000000..99f17fac --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/BaseUtil.java @@ -0,0 +1,15 @@ +package org.dromara.email.jakarta.comm.utils; + +public class BaseUtil { + + /** + * getPathName + *

分隔文件路径,并获取文件名 + * @param path 文件路径 + * @author :Wind + */ + public static String getPathName(String path) { + String[] split = path.split("/"); + return split[split.length - 1]; + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/HtmlUtil.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/HtmlUtil.java new file mode 100644 index 00000000..d4c0a8e4 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/HtmlUtil.java @@ -0,0 +1,130 @@ +package org.dromara.email.jakarta.comm.utils; + +import org.dromara.email.jakarta.comm.errors.MailException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * HtmlUtil + *

Html相关工具 + * + * @author :Wind + * 2023/6/7 20:15 + **/ +public final class HtmlUtil { + + private static final HtmlUtil htmlUtil = new HtmlUtil(); + + private HtmlUtil() { + } + + /** + * readHtml + *

从resource读取模板文件 + * + * @param name 模板文件名 + * @author :Wind + */ + public static List readHtml(String name) throws MailException { + try (InputStream is = HtmlUtil.class.getResourceAsStream("/template/" + name)) { + return readHtml(is); + } catch (IOException e) { + throw new MailException(e); + } + } + + /** + * readHtml + *

从自定义路径读取模板文件 + * + * @param file 自定义路径file + * @author :Wind + */ + public static List readHtml(File file) throws MailException { + try (InputStream ip = Files.newInputStream(file.toPath())) { + return readHtml(ip); + } catch (IOException e) { + throw new MailException(e); + } + + } + + /** + * readHtml + *

从输入流读取模板文件 + * + * @param inputStream 输入流 + * @author :Wind + */ + public static List readHtml(InputStream inputStream) throws MailException { + List data = new ArrayList<>(); + if (Objects.isNull(inputStream)) { + throw new MailException("The template could not be found!"); + } + try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = br.readLine()) != null) { + data.add(line); + } + } catch (IOException e) { + throw new MailException(e); + } + return data; + } + + /** + * replacePlaceholder + *

将所包含占位符的字符串替换为固定值 + * + * @param data 源数据 + * @param parameter key为占位符名称 value为占位符应替换的值 + * @author :Wind + */ + public static List replacePlaceholder(List data, Map parameter) { + for (int i = 0; i < data.size(); i++) { + for (Map.Entry s : parameter.entrySet()) { + String piece = piece(s.getKey()); + if (data.get(i).contains(piece)){ + String replace = data.get(i).replace(piece, s.getValue()); + data.set(i,replace); + } + } + } + return data; + } + + /** + * pieceHtml + *

将数据拼合为html + * + * @param data 需要拼合的数据 + * @author :Wind + */ + public static String pieceHtml(List data) { + StringBuilder sb = new StringBuilder(); + for (String datum : data) { + sb.append(datum); + sb.append("\r\n"); + } + return sb.toString(); + } + + /** + * piece + *

将参数拼合为完整占位符 + * + * @author :Wind + */ + public static String piece(String parameter) { + return "#{" + parameter + "}"; + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ReflectUtil.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ReflectUtil.java new file mode 100644 index 00000000..a508d563 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ReflectUtil.java @@ -0,0 +1,36 @@ +package org.dromara.email.jakarta.comm.utils; + +import org.dromara.email.jakarta.comm.entity.Parameter; +import org.dromara.email.jakarta.comm.errors.MailException; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +public class ReflectUtil { + + /** + * 反射获取接口对象的原类名 + */ + public static String getObjectName(Parameter parameter) { + return parameter.getClass().getTypeName(); + } + + /** + * 将对象的属性和属性值变为map + * */ + public static Map getValues(Parameter parameter) { + try { + Map map = new HashMap<>(); + Class clazz = Class.forName(getObjectName(parameter)); + Field[] declaredFields = clazz.getDeclaredFields(); + for (Field declaredField : declaredFields) { + declaredField.setAccessible(true); + map.put(declaredField.getName(), (String) declaredField.get(parameter)); + } + return map; + } catch (Exception e) { + throw new MailException(e); + } + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ZipUtils.java b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ZipUtils.java new file mode 100644 index 00000000..d41fad89 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-comm/src/main/java/org/dromara/email/jakarta/comm/utils/ZipUtils.java @@ -0,0 +1,111 @@ +package org.dromara.email.jakarta.comm.utils; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ZipUtil; +import cn.hutool.http.HttpUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.Pipe; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 压缩包处理类 + * + * @author Bleachtred + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ZipUtils extends ZipUtil { + private final static Integer TEMP_SIZE = 2048; + + /** + * 压缩方法(支持 本地文件/目录 + oss网络路径 混合) + * @param files 文件列表 + * @author Bleachtred + */ + public static void zipFilePip(Map files, OutputStream outputStream) { + try(WritableByteChannel out = Channels.newChannel(outputStream)) { + Pipe pipe = Pipe.open(); + //异步任务 + CompletableFuture.runAsync(() -> runTask(pipe, files)); + //获取读通道 + try (ReadableByteChannel readableByteChannel = pipe.source()) { + ByteBuffer buffer = ByteBuffer.allocate(TEMP_SIZE); + while (readableByteChannel.read(buffer) >= 0) { + buffer.flip(); + out.write(buffer); + buffer.clear(); + } + } + }catch (Exception e){ + e.printStackTrace(); + } + } + + private static void runTask(Pipe pipe, Map files) { + try(ZipOutputStream zos = new ZipOutputStream(Channels.newOutputStream(pipe.sink())); + WritableByteChannel out = Channels.newChannel(zos)) { + for (Map.Entry entry : files.entrySet()) { + taskFunction(zos, out, entry.getKey(), entry.getValue()); + } + }catch (Exception e){ + e.printStackTrace(); + } + } + + /** + * 打包文件 + * @param zos 压缩包输出 + * @param out 缓冲区通道 + * @param fileName 文件名称 + * @param file 文件 + * @throws IOException IOException + */ + private static void taskFunction(ZipOutputStream zos, WritableByteChannel out, String fileName, File file) throws IOException { + // 是否为目录 + if (file.isDirectory()) { + File[] files = file.listFiles(); + fileName = StrUtil.isEmpty(fileName) ? file.getName() + "/" : fileName + "/"; + if (files == null || files.length == 0){ + return; + } + for (File child : files) { + taskFunction(zos, out, fileName + child.getName(), child); + } + } else { + fileName = StrUtil.isEmpty(fileName) ? file.getName() : fileName; + zos.putNextEntry(new ZipEntry(fileName)); + try(FileInputStream fis = new FileInputStream(file.getAbsolutePath())){ + FileChannel fileChannel = fis.getChannel(); + fileChannel.transferTo(0, fileChannel.size(), out); + fileChannel.close(); + }catch (IOException e){ + e.printStackTrace(); + } + } + + } + + private static void taskFunction(ZipOutputStream zos, WritableByteChannel out, String fileName, String file) throws IOException { + // 网络文件 + if (file.startsWith("http")) { + zos.putNextEntry(new ZipEntry(fileName)); + byte[] bytes = HttpUtil.downloadBytes(file); + out.write(ByteBuffer.wrap(bytes)); + }else { + taskFunction(zos, out, fileName, new File(file)); + } + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/pom.xml b/sms4j-email-jakarta/sms4j-email-jakarta-core/pom.xml new file mode 100644 index 00000000..36de0a77 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.dromara.sms4j + sms4j-email-jakarta + ${revision} + + + sms4j-email-jakarta-core + email依赖引入包 + + + + + org.dromara.sms4j + sms4j-email-jakarta-api + + + + jakarta.activation + jakarta.activation-api + + + + diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MailFactory.java b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MailFactory.java new file mode 100644 index 00000000..09192b67 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MailFactory.java @@ -0,0 +1,63 @@ +package org.dromara.email.jakarta.core.factory; + +import org.dromara.email.jakarta.api.Blacklist; +import org.dromara.email.jakarta.api.MailClient; +import org.dromara.email.jakarta.comm.config.MailSmtpConfig; +import org.dromara.email.jakarta.comm.errors.MailException; +import org.dromara.email.jakarta.core.service.MailBuild; + +import jakarta.mail.MessagingException; +import java.util.HashMap; +import java.util.Map; + +/** + * MailFactory + *

配置工厂 + * @author :Wind + * 2023/6/8 22:35 + **/ +public class MailFactory{ + private static final Map configs = new HashMap<>(); + + /** + * createMailClient + *

从工厂获取一个邮件发送实例 + * @param key 配置的标识key + * @author :Wind + */ + public static MailClient createMailClient(Object key){ + try { + return MailBuild.build(configs.get(key)); + } catch (MessagingException e) { + throw new MailException(e); + } + } + + + /** + * createMailClient + *

从工厂获取一个邮件发送实例,该实例发送短信将依照黑名单中的数据进行过滤 + * @param key 配置的标识key + * @param blacklist 黑名单接口,实例将从这里获取黑名单数据 + * @author :Wind + */ + public static MailClient createMailClient(Object key, Blacklist blacklist){ + try { + return MailBuild.build(configs.get(key),blacklist); + } catch (MessagingException e) { + throw new MailException(e); + } + } + + /** + * set + *

将一个配置对象交给工厂 + * @param key 标识 + * @param config 配置对象 + * @author :Wind + */ + public static void put(Object key, MailSmtpConfig config){ + configs.put(key,config); + } + +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MonitorFactory.java b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MonitorFactory.java new file mode 100644 index 00000000..0b8db34a --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/factory/MonitorFactory.java @@ -0,0 +1,65 @@ +package org.dromara.email.jakarta.core.factory; + +import org.dromara.email.jakarta.api.Monitor; +import org.dromara.email.jakarta.comm.config.MailImapConfig; +import org.dromara.email.jakarta.core.service.MonitorService; + +import java.util.HashMap; +import java.util.Map; + +/** + * MonitorFactory + *

监听系统工厂,通过向工厂添加配置,可以启动或关闭监听 + * 监听器通过连接指定的imap服务器监听指定的邮箱,并以异步轮询的形式进行消息的监听,除去轮询时间开销外,个别imap服务器本身存在延迟,故而邮件的监听可能存在较大延迟, + * 在配置中可以设置轮询的间隔时间,以增大或缩小监听灵敏度。灵敏度调节周期为秒级,默认周期为5秒。 + *

需要注意的是,监听器所使用的线程并非任何线程池中的线程,如想停止某邮箱的监听,只需要调用stop方法即可,他会在完成当前接收的任务后正常的终结线程。 + * 现成终结后可以通过调用start方法重新启用。 + * @author :Wind + * 2023/7/18 17:06 + **/ +public class MonitorFactory { + + private final static Map services = new HashMap<>(); + + /** + * put + *

添加一个配置至系统中,并绑定接收消息的对象 + * @param key 监听标识 + * @param config 监听配置 + * @param monitor 回调对象 + * @author :Wind + */ + public static void put(String key, MailImapConfig config, Monitor monitor){ + services.put(key,new MonitorService(config,monitor)); + } + + /** + * start + *

开始监听指定标识的邮箱 + * @param key 标识 + * @author :Wind + */ + public static void start(String key){ + services.get(key).start(); + } + + /** + * stop + *

停止监听指定标识的邮箱 + * @param key 标识 + * @author :Wind + */ + public static void stop(String key){ + services.get(key).stop(); + } + + /** + * getConfig + *

获取指定标识的配置信息 + * @param key 标识 + * @author :Wind + */ + public static MailImapConfig getConfig(String key) { + return services.get(key).getMailImapConfig(); + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/package-info.java b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/package-info.java new file mode 100644 index 00000000..106607f4 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/package-info.java @@ -0,0 +1,6 @@ +/** + *

邮件插件核心模块 + * @author :Wind + * 2023/7/27 10:58 + **/ +package org.dromara.email.jakarta.core; \ No newline at end of file diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailBuild.java b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailBuild.java new file mode 100644 index 00000000..0a1c67e9 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailBuild.java @@ -0,0 +1,107 @@ +package org.dromara.email.jakarta.core.service; + +import cn.hutool.core.collection.CollUtil; +import lombok.Data; +import org.dromara.email.jakarta.api.Blacklist; +import org.dromara.email.jakarta.api.MailClient; +import org.dromara.email.jakarta.comm.config.MailSmtpConfig; +import org.dromara.email.jakarta.comm.errors.MailException; + +import jakarta.mail.Authenticator; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.PasswordAuthentication; +import jakarta.mail.Session; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +@Data +public class MailBuild { + + private Message message; + + private Session session; + + private MailSmtpConfig config; + + private Blacklist blacklist; + + private int retryInterval; + + private int maxRetries; + + private MailBuild(MailSmtpConfig config) throws MessagingException { + Properties props = new Properties(); + props.put("mail.smtp.host", config.getSmtpServer()); + props.put("mail.smtp.auth", config.getIsAuth()); + props.put("mail.smtp.port", config.getPort()); + props.put("mail.smtp.ssl.enable", config.getIsSSL()); +// props.put("mail.smtp.ssl.socketFactory", new MailSSLSocketFactory()); + this.session = Session.getInstance(props, new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(config.getUsername(), config.getPassword()); + } + }); + this.message = new MimeMessage(session); + this.message.setFrom(new InternetAddress(config.getFromAddress())); + this.config = config; + this.retryInterval = config.getRetryInterval(); + this.maxRetries = config.getMaxRetries(); + } + + private MailBuild(MailSmtpConfig config,Blacklist blacklist)throws MessagingException{ + Properties props = new Properties(); + props.put("mail.smtp.host", config.getSmtpServer()); + props.put("mail.smtp.auth", config.getIsAuth()); + props.put("mail.smtp.port", config.getPort()); + props.put("mail.smtp.ssl.enable", config.getIsSSL()); +// props.put("mail.smtp.ssl.socketFactory", new MailSSLSocketFactory()); + this.session = Session.getInstance(props, + new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(config.getUsername(), config.getPassword()); + } + }); + this.message = new MimeMessage(session); + this.message.setFrom(new InternetAddress(config.getFromAddress())); + this.config = config; + this.blacklist = blacklist; + this.retryInterval = config.getRetryInterval(); + this.maxRetries = config.getMaxRetries(); + } + + public static MailClient build(MailSmtpConfig config) throws MessagingException { + return MailService.instance(new MailBuild(config)); + } + public static MailClient build(MailSmtpConfig config,Blacklist blacklist)throws MessagingException { + return MailService.instance(new MailBuild(config,blacklist)); + } + + /** + * eliminate + *

过滤黑名单内容 + * @param source 需要过滤的源数据 + * @author :Wind + */ + public InternetAddress[] eliminate(List source) { + List list = new ArrayList<>(); + try { + if (Objects.isNull(blacklist)) { + return InternetAddress.parse(Objects.requireNonNull(CollUtil.join(source, ","))); + } + for (String s : blacklist.getBlacklist()) { + if (!source.contains(s)) { + list.add(s); + } + } + return InternetAddress.parse(CollUtil.join(list, ",")); + } catch (AddressException e) { + throw new MailException(e); + } + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailService.java b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailService.java new file mode 100644 index 00000000..2e3cff25 --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MailService.java @@ -0,0 +1,227 @@ +package org.dromara.email.jakarta.core.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import jakarta.mail.util.ByteArrayDataSource; +import org.dromara.email.jakarta.api.MailClient; +import org.dromara.email.jakarta.comm.constants.FileConstants; +import org.dromara.email.jakarta.comm.entity.MailMessage; +import org.dromara.email.jakarta.comm.errors.MailException; +import org.dromara.email.jakarta.comm.utils.HtmlUtil; +import org.dromara.email.jakarta.comm.utils.ZipUtils; + +import jakarta.activation.DataHandler; +import jakarta.activation.DataSource; +import jakarta.activation.FileDataSource; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Transport; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMultipart; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +public class MailService implements MailClient { + + private static Logger logger = Logger.getLogger("mailLog"); + private MailBuild mailBuild; + + private MailService(MailBuild mailBuild) { + this.mailBuild = mailBuild; + } + + public static MailClient instance(MailBuild mailBuild) { + return new MailService(mailBuild); + } + + @Override + public void send(MailMessage mailMessage) { + List html = null; + if (mailMessage.getHtmlInputStream() != null) { + html = HtmlUtil.readHtml(mailMessage.getHtmlInputStream()); + } + if (StrUtil.isNotBlank(mailMessage.getHtmlPath())){ + html = HtmlUtil.readHtml(mailMessage.getHtmlPath()); + } + send(mailMessage.getMailAddress(), + mailMessage.getTitle(), + mailMessage.getBody(), + html, + mailMessage.getHtmlValues(), + mailMessage.getZipName(), + mailMessage.getFiles(), + mailMessage.getCc(), + mailMessage.getBcc() + ); + } + + private void forFiles(Multipart multipart, Map files) throws MessagingException { + for (Map.Entry entry : files.entrySet()) { + String k = entry.getKey(); + String v = entry.getValue(); + // 设置附件消息部分 + MimeBodyPart messageBodyPart = new MimeBodyPart(); + DataSource source; + if (v.startsWith("http")) { + byte[] bytes = HttpUtil.downloadBytes(v); + source = new ByteArrayDataSource(bytes, FileConstants.IO_FILE_TYPE); + } else { + source = new FileDataSource(v); + } + messageBodyPart.setDataHandler(new DataHandler(source)); + messageBodyPart.setFileName(k); + multipart.addBodyPart(messageBodyPart); + } + } + + private void zipFiles(Multipart multipart, String zipName, Map files) throws MessagingException, IOException { + // 设置附件消息部分 + MimeBodyPart messageBodyPart = new MimeBodyPart(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ZipUtils.zipFilePip(files, os); + ByteArrayInputStream stream = IoUtil.toStream(os); + DataSource source = new ByteArrayDataSource(stream, FileConstants.IO_FILE_TYPE); + messageBodyPart.setDataHandler(new DataHandler(source)); + messageBodyPart.setFileName(StrUtil.isNotBlank(zipName) ? zipName : UUID.fastUUID() + ".zip"); + multipart.addBodyPart(messageBodyPart); + } + + private void send(List mailAddress, + String title, + String body, + List html, + Map parameter, + String zipName, + Map files, + List cc, + List bcc) { + try { + Message message = messageBuild(mailAddress, title, body, html, parameter, zipName, cc, bcc, files); + Transport.send(message); + logger.info("邮件发送成功!^_^"); + } catch (MessagingException | IOException e) { + // 防止 maxRetries 数值小于0带来的其他问题 + if (mailBuild.getMaxRetries() > 0) { + ReSendList(mailAddress, title, body, html, parameter, zipName, files, cc, bcc); + } else { + logger.warning(e.getMessage()); + throw new MailException(e); + } + } + } + + private void ReSendList(List mailAddress, + String title, + String body, + List html, + Map parameter, + String zipName, + Map files, + List cc, + List bcc) { + int maxRetries = mailBuild.getMaxRetries(); + int retryCount = 1; // 初始值为1;则while循环中少发送一次,最后一次发送在判断 retryCount >= maxRetries 这里。 + boolean retryOnFailure = true; + + while (retryOnFailure && retryCount < maxRetries) { + try { + logger.warning("邮件第 {" + retryCount + "} 次重新发送"); + Message message; + if (html != null || parameter != null) { + message = messageBuild(mailAddress, title, body, html, parameter, zipName, cc, bcc, files); + } else { + message = messageBuild(mailAddress, title, body, null, null, zipName, cc, bcc, files); + } + Transport.send(message); + retryOnFailure = false; // 发送成功,停止重试 + } catch (MessagingException | IOException e) { + retryCount++; + try { + // 间隔秒数 + TimeUnit.SECONDS.sleep(mailBuild.getRetryInterval()); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + + if (retryCount >= maxRetries) { + try { + Message message; + if (html != null || parameter != null) { + message = messageBuild(mailAddress, title, body, html, parameter, null, cc, bcc, files); + } else { + message = messageBuild(mailAddress, title, body, null, null, null, cc, bcc, files); + } + Transport.send(message); + } catch (MessagingException | IOException e) { + throw new MailException(e); + } + } + } + + public Message messageBuild(List mailAddress, + String title, + String body, + List html, + Map parameter, + String zipName, + List cc, + List bcc, + Map files) throws MessagingException, IOException { + Message message = mailBuild.getMessage(); + message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(mailAddress)); + message.setSubject(title); + + Multipart multipart = new MimeMultipart("alternative"); + if (CollUtil.isNotEmpty(html) && MapUtil.isNotEmpty(parameter)) { + //读取模板并进行变量替换 + List strings = HtmlUtil.replacePlaceholder(html, parameter); + //拼合HTML数据 + String htmlData = HtmlUtil.pieceHtml(strings); + MimeBodyPart htmlPart = new MimeBodyPart(); + htmlPart.setContent(htmlData, "text/html;charset=UTF-8"); + multipart.addBodyPart(htmlPart); + } + + if (StrUtil.isNotBlank(body)) { + // 创建文本正文部分 + MimeBodyPart textPart = new MimeBodyPart(); + textPart.setText(body); + multipart.addBodyPart(textPart); + } + //添加附件 + if (MapUtil.isNotEmpty(files) && StrUtil.isNotBlank(zipName)) { + zipFiles(multipart, zipName, files); + } else { + if (MapUtil.isNotEmpty(files)) { + forFiles(multipart, files); + message.setContent(multipart); + } + } + if (CollUtil.isNotEmpty(cc) || CollUtil.isNotEmpty(bcc)) { + addCC(cc, bcc, message); + } + message.setContent(multipart); + return message; + } + + private void addCC(List cc, List bcc, Message message) throws MessagingException { + if (CollUtil.isNotEmpty(cc)) { + message.addRecipients(Message.RecipientType.CC, mailBuild.eliminate(cc)); + } + if (CollUtil.isNotEmpty(bcc)) { + message.addRecipients(Message.RecipientType.BCC, mailBuild.eliminate(bcc)); + } + } +} diff --git a/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MonitorService.java b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MonitorService.java new file mode 100644 index 00000000..d8e0006c --- /dev/null +++ b/sms4j-email-jakarta/sms4j-email-jakarta-core/src/main/java/org/dromara/email/jakarta/core/service/MonitorService.java @@ -0,0 +1,133 @@ +package org.dromara.email.jakarta.core.service; + +import org.dromara.email.jakarta.api.Monitor; +import org.dromara.email.jakarta.comm.config.MailImapConfig; +import org.dromara.email.jakarta.comm.entity.MonitorMessage; +import org.dromara.email.jakarta.comm.errors.MailException; + +import jakarta.mail.BodyPart; +import jakarta.mail.Flags; +import jakarta.mail.Folder; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Session; +import jakarta.mail.Store; +import jakarta.mail.search.FlagTerm; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Properties; +import java.util.Timer; +import java.util.TimerTask; + +/** + * MonitorService + *

监听器服务 + * + * @author :Wind + * 2023/7/18 16:10 + **/ +public class MonitorService{ + private final Store store; + private Monitor monitor; + private MailImapConfig mailImapConfig; + private Timer timer; + + public MonitorService(MailImapConfig config, Monitor monitor) { + Properties props = System.getProperties(); + props.setProperty("mail.store.protocol", "imaps"); + try { + Session session = Session.getDefaultInstance(props, null); + Store store = session.getStore("imaps"); + store.connect(config.getImapServer(), config.getUsername(), config.getAccessToken()); + this.store = store; + this.monitor = monitor; + this.mailImapConfig = config; + } catch (Exception e) { + throw new MailException(e); + } + } + + private void startListening() { + try { + Folder inbox = store.getFolder("Inbox"); + inbox.open(Folder.READ_WRITE); + Message[] messages = inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); + for (Message message : messages) { + MonitorMessage monitorMessage = new MonitorMessage(); + // 获取邮件的发送者 + monitorMessage.setFromAddress(message.getFrom()[0].toString()); + // 获取邮件主题 + monitorMessage.setTitle(message.getSubject()); + // 获取邮件的内容 + if (message.isMimeType("text/plain")) { + Object content = message.getContent(); + if (content == null){ + StringBuilder stringBuilder = getStringBuilder(message); + content = stringBuilder.toString(); + } + monitorMessage.setText(content.toString()); + } else if (message.isMimeType("multipart/*")) { + Multipart mp = (Multipart) message.getContent(); + for (int i = 0; i < mp.getCount();i++){ + BodyPart bodyPart = mp.getBodyPart(i); + String contentType = bodyPart.getContentType().toLowerCase(); + if (contentType.startsWith("text/plain")) { + // 纯文本内容 + monitorMessage.setText(bodyPart.getContent().toString()); + } else if (contentType.startsWith("text/html")) { + // HTML内容 + monitorMessage.setHtmlText(bodyPart.getContent().toString()); + } + } + monitorMessage.setBody(mp); + } + monitorMessage.setMessageIndex(message.getMessageNumber()); + monitorMessage.setSendDate(message.getSentDate()); + monitorMessage.setAcceptTime(System.currentTimeMillis()); + if (this.monitor.monitor(monitorMessage)) { + message.setFlag(Flags.Flag.SEEN, true); + } + } + inbox.close(); + } catch (Exception e) { + throw new MailException(e); + } + } + + private static StringBuilder getStringBuilder(Message message) throws IOException, MessagingException { + InputStream inputStream = message.getInputStream(); + + // 解析输入流以获取内容 + StringBuilder stringBuilder = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + } + } + return stringBuilder; + } + + public MonitorService start(){ + Timer timer = new Timer(); + this.timer = timer; + timer.schedule(new TimerTask() { + @Override + public void run() { + startListening(); + } + },0,mailImapConfig.getCycle()*1000); + return this; + } + + public void stop(){ + timer.cancel(); + } + + public MailImapConfig getMailImapConfig() { + return mailImapConfig; + } +}