diff --git a/yudao-framework/pom.xml b/yudao-framework/pom.xml
index 73bb614cd..42c887c56 100644
--- a/yudao-framework/pom.xml
+++ b/yudao-framework/pom.xml
@@ -30,6 +30,7 @@
yudao-spring-boot-starter-biz-operatelog
yudao-spring-boot-starter-biz-dict
yudao-spring-boot-starter-biz-sms
+ yudao-spring-boot-starter-biz-mail
yudao-spring-boot-starter-activiti
yudao-spring-boot-starter-biz-pay
yudao-spring-boot-starter-biz-weixin
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-mail/pom.xml
new file mode 100644
index 000000000..8cca21778
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/pom.xml
@@ -0,0 +1,65 @@
+
+
+
+ cn.iocoder.boot
+ yudao-framework
+ ${revision}
+
+ 4.0.0
+ yudao-spring-boot-starter-biz-mail
+ jar
+
+
+
+ cn.iocoder.boot
+ yudao-common
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ io.opentracing
+ opentracing-util
+
+
+
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-test
+ test
+
+
+
+
+ com.google.guava
+ guava
+ true
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+ org.projectlombok
+ lombok
+
+
+
+
\ No newline at end of file
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/config/YudaoMailAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/config/YudaoMailAutoConfiguration.java
new file mode 100644
index 000000000..c83b1e5f1
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/config/YudaoMailAutoConfiguration.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.mail.config;
+
+import cn.iocoder.yudao.framework.mail.core.client.MailClientFactory;
+import cn.iocoder.yudao.framework.mail.core.client.impl.MailClientFactoryImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 邮箱配置类
+ *
+ * @author 芋道源码
+ */
+@Configuration
+public class YudaoMailAutoConfiguration {
+
+ @Bean
+ public MailClientFactory mailClientFactory() {
+ return new MailClientFactoryImpl();
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClient.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClient.java
new file mode 100644
index 000000000..a17260410
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClient.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.framework.mail.core.client;
+
+import java.util.List;
+
+/**
+ * 邮件客户端,用于对接各邮箱平台的 SDK,实现邮件发送等功能
+ *
+ * @author wangjingyi
+ * @date 2021/4/19 19:21
+ */
+public interface MailClient {
+
+ /**
+ * 发送邮件
+ *
+ * @param from 邮箱账号
+ * @param content 内容
+ * @param title 标题
+ * @param tos 收件人
+ * @return 邮件发送结果
+ */
+ String sendMail(String from, String content, String title, List tos);
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClientFactory.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClientFactory.java
new file mode 100644
index 000000000..3244dfe76
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClientFactory.java
@@ -0,0 +1,5 @@
+package cn.iocoder.yudao.framework.mail.core.client;
+
+public interface MailClientFactory {
+ MailClient getMailClient();
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCodeMapping.java
new file mode 100644
index 000000000..c33b4be3e
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCodeMapping.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.framework.mail.core.client;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+import cn.iocoder.yudao.framework.mail.core.enums.MailFrameworkErrorCodeConstants;
+
+import java.util.function.Function;
+
+/**
+ * 将 API 的错误码,转换为通用的错误码
+ *
+ * @see MailCommonResult
+ * @see MailFrameworkErrorCodeConstants
+ *
+ * @author 芋道源码
+ */
+public interface MailCodeMapping extends Function {
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCommonResult.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCommonResult.java
new file mode 100644
index 000000000..a53a3b989
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCommonResult.java
@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.framework.mail.core.client;
+
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import cn.iocoder.yudao.framework.mail.core.enums.MailFrameworkErrorCodeConstants;
+
+/**
+ * 短信的 CommonResult 拓展类
+ *
+ * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
+ *
+ * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
+ *
+ * @author 芋道源码
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MailCommonResult extends CommonResult {
+
+ /**
+ * API 返回错误码
+ *
+ * 由于第三方的错误码可能是字符串,所以使用 String 类型
+ */
+ private String apiCode;
+ /**
+ * API 返回提示
+ */
+ private String apiMsg;
+
+ /**
+ * API 请求编号
+ */
+ private String apiRequestId;
+
+ private MailCommonResult() {
+ }
+
+ public static MailCommonResult build(String apiCode, String apiMsg, String apiRequestId,
+ T data, MailCodeMapping codeMapping) {
+ Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
+ MailCommonResult result = new MailCommonResult().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId);
+ result.setData(data);
+ // 翻译错误码
+ if (codeMapping != null) {
+ ErrorCode errorCode = codeMapping.apply(apiCode);
+ if (errorCode == null) {
+ errorCode = MailFrameworkErrorCodeConstants.MAIL_UNKNOWN;
+ }
+ result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
+ }
+ return result;
+ }
+
+ public static MailCommonResult error(Throwable ex) {
+ MailCommonResult result = new MailCommonResult<>();
+ result.setCode(MailFrameworkErrorCodeConstants.EXCEPTION.getCode());
+ result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
+ return result;
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailReceiveRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailReceiveRespDTO.java
new file mode 100644
index 000000000..11f27f58a
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailReceiveRespDTO.java
@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.framework.mail.core.client.dto;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 消息接收 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class MailReceiveRespDTO {
+
+ /**
+ * 是否接收成功
+ */
+ private Boolean success;
+ /**
+ * API 接收结果的编码
+ */
+ private String errorCode;
+ /**
+ * API 接收结果的说明
+ */
+ private String errorMsg;
+
+ /**
+ * 手机号
+ */
+ private String mobile;
+ /**
+ * 用户接收时间
+ */
+ private Date receiveTime;
+
+ /**
+ * 短信 API 发送返回的序号
+ */
+ private String serialNo;
+ /**
+ * 短信日志编号
+ *
+ * 对应 SysSmsLogDO 的编号
+ */
+ private Long logId;
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailSendRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailSendRespDTO.java
new file mode 100644
index 000000000..b4a94a634
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailSendRespDTO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.framework.mail.core.client.dto;
+
+import lombok.Data;
+
+/**
+ * 短信发送 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class MailSendRespDTO {
+
+ /**
+ * 短信 API 发送返回的序号
+ */
+ private String serialNo;
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailTemplateRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailTemplateRespDTO.java
new file mode 100644
index 000000000..14bdeacd8
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailTemplateRespDTO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.framework.mail.core.client.dto;
+
+import lombok.Data;
+
+/**
+ * 短信模板 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class MailTemplateRespDTO {
+
+ /**
+ * 模板编号
+ */
+ private String id;
+ /**
+ * 短信内容
+ */
+ private String content;
+ /**
+ * 审核状态
+ *
+ */
+ private Integer auditStatus;
+ /**
+ * 审核未通过的理由
+ */
+ private String auditReason;
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/AbstractMailClient.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/AbstractMailClient.java
new file mode 100644
index 000000000..097c8e095
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/AbstractMailClient.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.framework.mail.core.client.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import cn.iocoder.yudao.framework.mail.core.client.MailClient;
+
+/**
+ * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码
+ *
+ * @author zzf
+ * @date 2021/2/1 9:28
+ */
+@Slf4j
+public abstract class AbstractMailClient implements MailClient {
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/MailClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/MailClientFactoryImpl.java
new file mode 100644
index 000000000..9923bd3df
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/MailClientFactoryImpl.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.framework.mail.core.client.impl;
+
+import cn.iocoder.yudao.framework.mail.core.client.MailClient;
+import cn.iocoder.yudao.framework.mail.core.client.impl.hutool.HutoolMailClient;
+import lombok.extern.slf4j.Slf4j;
+import cn.iocoder.yudao.framework.mail.core.client.MailClientFactory;
+import cn.iocoder.yudao.framework.mail.core.enums.MailChannelEnum;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.Arrays;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+@Validated
+@Slf4j
+public class MailClientFactoryImpl implements MailClientFactory {
+
+ private final ConcurrentMap channelCodeClients = new ConcurrentHashMap<>();
+
+ public MailClientFactoryImpl (){
+ Arrays.stream(MailChannelEnum.values()).forEach(mailChannelEnum -> {
+ AbstractMailClient abstractMailClient = createMailClient(mailChannelEnum);
+ channelCodeClients.put(mailChannelEnum.getCode() , abstractMailClient);
+ });
+ }
+
+ private AbstractMailClient createMailClient(MailChannelEnum mailChannelEnum) {
+ switch (mailChannelEnum){
+ case HUTOOL: return new HutoolMailClient();
+ }
+ // 创建失败,错误日志 + 抛出异常
+ log.error("[createMailClient][配置({}) 找不到合适的客户端实现]" , mailChannelEnum);
+ throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", mailChannelEnum));
+ }
+
+ @Override
+ public MailClient getMailClient() {
+ return channelCodeClients.get("HUTOOL");
+ }
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailClient.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailClient.java
new file mode 100644
index 000000000..457ad7e37
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailClient.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.framework.mail.core.client.impl.hutool;
+
+import cn.hutool.extra.mail.MailUtil;
+import cn.iocoder.yudao.framework.mail.core.client.impl.AbstractMailClient;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+
+/**
+ * 邮件客户端实现
+ *
+ * @author wangjingyi
+ * @date 2021/4/25 14:25
+ */
+@Slf4j
+public class HutoolMailClient extends AbstractMailClient {
+
+ @Override
+ public String sendMail(String from, String content, String title, List tos) {
+ try{
+ return MailUtil.send(from , title , content , false , null);
+ }catch (Exception e){
+ log.error(e.getMessage());
+ }
+ return "";
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailCodeMapping.java
new file mode 100644
index 000000000..711d36950
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailCodeMapping.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.framework.mail.core.client.impl.hutool;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+import cn.iocoder.yudao.framework.mail.core.client.MailCodeMapping;
+
+/**
+ * 阿里云的 SmsCodeMapping 实现类
+ *
+ * 参见 https://help.aliyun.com/document_detail/101346.htm 文档
+ *
+ * @author 芋道源码
+ */
+public class HutoolMailCodeMapping implements MailCodeMapping {
+
+ @Override
+ public ErrorCode apply(String apiCode) {
+ return null;
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailChannelEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailChannelEnum.java
new file mode 100644
index 000000000..51d2f1fca
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailChannelEnum.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.mail.core.enums;
+
+import cn.hutool.core.util.ArrayUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 短信渠道枚举
+ *
+ * @author zzf
+ * @date 2021/1/25 10:56
+ */
+@Getter
+@AllArgsConstructor
+public enum MailChannelEnum {
+ HUTOOL("HUTOOL" , "HUTOOL"),
+ ;
+
+ /**
+ * 编码
+ */
+ private final String code;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ public static MailChannelEnum getByCode(String code) {
+ return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailFrameworkErrorCodeConstants.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailFrameworkErrorCodeConstants.java
new file mode 100644
index 000000000..e5ef74cf3
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailFrameworkErrorCodeConstants.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.framework.mail.core.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * 短信框架的错误码枚举
+ *
+ * 短信框架,使用 2-001-000-000 段
+ *
+ * @author 芋道源码
+ */
+public interface MailFrameworkErrorCodeConstants {
+
+ ErrorCode MAIL_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析");
+
+ // ========== 权限 / 限流等相关 2001000100 ==========
+
+ ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");
+ // 云片:可以配置 IP 白名单,只有在白名单中才可以发送短信
+ ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");
+
+ // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
+ ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流");
+ // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
+ ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流");
+
+ ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词");
+
+ // ========== 模板相关 2001000200 ==========
+ ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在
+ ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确");
+
+ // ========== 签名相关 2001000300 ==========
+ ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用");
+
+ // ========== 账户相关 2001000400 ==========
+ ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足");
+ ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在");
+
+ // ========== 其它相关 2001000900 开头 ==========
+ ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失");
+ ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确");
+ ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中");
+
+ ErrorCode EXCEPTION = new ErrorCode(2001000999, "调用异常");
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailTemplateAuditStatusEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailTemplateAuditStatusEnum.java
new file mode 100644
index 000000000..95c3c31a3
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailTemplateAuditStatusEnum.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.mail.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 短信模板的审核状态枚举
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+@Getter
+public enum MailTemplateAuditStatusEnum {
+
+ CHECKING(1),
+ SUCCESS(2),
+ FAIL(3);
+
+ private final Integer status;
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/property/MailChannelProperties.java b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/property/MailChannelProperties.java
new file mode 100644
index 000000000..1f19ac12b
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/property/MailChannelProperties.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.framework.mail.core.property;
+
+import lombok.Data;
+import cn.iocoder.yudao.framework.mail.core.enums.MailChannelEnum;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 短信渠道配置类
+ *
+ * @author zzf
+ * @date 2021/1/25 17:01
+ */
+@Data
+@Validated
+public class MailChannelProperties {
+
+ /**
+ * 渠道编号
+ */
+ @NotNull(message = "短信渠道 ID 不能为空")
+ private Long id;
+ /**
+ * 短信签名
+ */
+ @NotEmpty(message = "短信签名不能为空")
+ private String signature;
+ /**
+ * 渠道编码
+ *
+ * 枚举 {@link MailChannelEnum}
+ */
+ @NotEmpty(message = "渠道编码不能为空")
+ private String code;
+ /**
+ * 短信 API 的账号
+ */
+ @NotEmpty(message = "短信 API 的账号不能为空")
+ private String apiKey;
+ /**
+ * 短信 API 的秘钥
+ */
+ @NotEmpty(message = "短信 API 的秘钥不能为空")
+ private String apiSecret;
+ /**
+ * 短信发送回调 URL
+ */
+ private String callbackUrl;
+
+}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java
new file mode 100644
index 000000000..2ffe0d7cb
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java
@@ -0,0 +1,4 @@
+package cn.iocoder.yudao.module.system.api.mail;
+
+public interface MailSendApi {
+}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendReqDTO.java
new file mode 100644
index 000000000..a3b0f2e35
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendReqDTO.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.system.api.mail.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@ApiModel("管理后台 - 邮件发送 Req VO")
+@Data
+public class MailSendReqDTO { // TODO @wangjingqi:1), 不用空格;2)应该只要传递 templateCode、参数就好,title、from、content、附件应该都是参数里的
+
+ @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
+ @NotNull(message = "邮箱账号不能为空")
+ @Email(message = "邮箱账号格式错误")
+ private String from;
+
+ @ApiModelProperty(value = "标题",example = "标题")
+ private String title;
+
+ @ApiModelProperty(value = "内容",example = "内容")
+ private String content;
+
+ @ApiModelProperty(value = "邮箱模版id",example = "1024")
+ @NotNull(message = "邮箱模版id不能为空")
+ private Integer templateId;
+
+ @ApiModelProperty(value = "收件人",required = true,example = "yudaoyuanma@123.com")
+ @NotNull(message = "收件人不能为空")
+ private List tos;
+
+ @ApiModelProperty(value = "附件",example = "附件编码")
+ private List fileIds;
+
+
+}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
index 8520cb115..07322ccd7 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
@@ -126,5 +126,6 @@ public interface ErrorCodeConstants {
// ========== 邮箱模版 1002020000 ==========
ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002020000 , "邮箱模版不存在");
ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002020001, "邮箱模版存在");
+ ErrorCode MAIL_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002020002, "存在关联邮箱模版");
}
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java
new file mode 100644
index 000000000..8936ea9c7
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.system.enums.mail;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 邮件的发送状态枚举
+ *
+ * @author wangjingyi
+ * @date 2022/4/10 13:39
+ */
+@Getter
+@AllArgsConstructor
+public enum MailSendStatusEnum {
+
+ INIT(0), // 初始化
+ SUCCESS(10), // 发送成功
+ FAILURE(20), // 发送失败
+ IGNORE(30), // 忽略,即不发送
+ ;
+
+ private final int status;
+
+}
diff --git a/yudao-module-system/yudao-module-system-impl/pom.xml b/yudao-module-system/yudao-module-system-impl/pom.xml
index b297830b5..65dc74d03 100644
--- a/yudao-module-system/yudao-module-system-impl/pom.xml
+++ b/yudao-module-system/yudao-module-system-impl/pom.xml
@@ -47,6 +47,10 @@
cn.iocoder.boot
yudao-spring-boot-starter-biz-dict
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-mail
+
cn.iocoder.boot
yudao-spring-boot-starter-biz-data-permission
@@ -102,6 +106,7 @@
yudao-spring-boot-starter-excel
+
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java
new file mode 100644
index 000000000..cce0ed388
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.system.api.mail;
+
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * 邮件发送 API 接口
+ *
+ * @author wangjingyi
+ */
+@Service
+@Validated
+public class MailSendApiImpl implements MailSendApi{
+}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java
index 67cf9ba3a..d8650b52e 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java
@@ -4,28 +4,34 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
+import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 邮箱账号基类 Base VO")
@Data
-public class MailAccountBaseVO { // TODO @wangjingqi:1), 不用空格;2)from、username、password、host、sslEnable 都要参数校验,非空;3)username 要 Email 格式;port Integer;
+public class MailAccountBaseVO {
- @ApiModelProperty(value = "邮箱" , required = true , example = "yudaoyuanma@123.com")
+ @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
+ @NotNull(message = "邮箱必填")
private String from;
- @ApiModelProperty(value = "用户名" , required = true , example = "yudao")
+ @ApiModelProperty(value = "用户名",required = true,example = "yudao")
@NotNull(message = "用户名必填")
+ @Email(message = "必须是Email格式")
private String username;
- @ApiModelProperty(value = "密码" , required = true , example = "123456")
+ @ApiModelProperty(value = "密码",required = true,example = "123456")
+ @NotNull(message = "密码必填")
private String password;
- @ApiModelProperty(value = "网站" , required = true , example = "www.iocoder.cn")
+ @ApiModelProperty(value = "网站",required = true,example = "www.iocoder.cn")
+ @NotNull(message = "网站必填")
private String host;
- @ApiModelProperty(value = "端口" , required = true , example = "80")
- private String port;
+ @ApiModelProperty(value = "端口",required = true,example = "80")
+ private Integer port;
- @ApiModelProperty(value = "是否开启ssl" , required = true , example = "2")
+ @ApiModelProperty(value = "是否开启ssl",required = true,example = "2")
+ @NotNull(message = "是否开启ssl必填")
private Boolean sslEnable;
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogExcelVO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogExcelVO.java
index 442a101a3..d341ce678 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogExcelVO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogExcelVO.java
@@ -6,6 +6,7 @@ import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.sql.Timestamp;
+import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -30,10 +31,10 @@ public class MailLogExcelVO {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ExcelProperty(value = "发送时间" )
- private Timestamp sendTime;
+ private Date sendTime;
@ExcelProperty(value = "发送状态")
- private Boolean sendStatus;
+ private Integer sendStatus;
@ExcelProperty(value = "发送结果")
private String sendResult;
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java
index af8e81698..024e075f4 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java
@@ -36,7 +36,7 @@ public class MailLogRespVO {
private Timestamp sendTime;
@ApiModelProperty(value = "发送状态" , required = false , example = "1")
- private Boolean sendStatus;
+ private Integer sendStatus;
@ApiModelProperty(value = "发送结果" , required = false , example = "yudaoyuanma@123.com")
private String sendResult;
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/send/MailReqVO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/send/MailReqVO.java
index 4ce06a718..65142ee3f 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/send/MailReqVO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/send/MailReqVO.java
@@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
+import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import java.util.List;
@@ -11,25 +12,26 @@ import java.util.List;
@Data
public class MailReqVO { // TODO @wangjingqi:1), 不用空格;2)应该只要传递 templateCode、参数就好,title、from、content、附件应该都是参数里的
- @ApiModelProperty(value = "邮箱" , required = true , example = "yudaoyuanma@123.com")
+ @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
@NotNull(message = "邮箱账号不能为空")
+ @Email(message = "邮箱账号格式错误")
private String from;
- @ApiModelProperty(value = "标题" , example = "标题")
+ @ApiModelProperty(value = "标题",example = "标题")
private String title;
- @ApiModelProperty(value = "内容" , example = "内容")
+ @ApiModelProperty(value = "内容",example = "内容")
private String content;
- @ApiModelProperty(value = "邮箱模版id" , example = "1024")
+ @ApiModelProperty(value = "邮箱模版id",example = "1024")
@NotNull(message = "邮箱模版id不能为空")
private Integer templateId;
- @ApiModelProperty(value = "收件人" , required = true , example = "yudaoyuanma@123.com")
+ @ApiModelProperty(value = "收件人",required = true,example = "yudaoyuanma@123.com")
@NotNull(message = "收件人不能为空")
private List tos;
- @ApiModelProperty(value = "附件" , example = "附件编码")
+ @ApiModelProperty(value = "附件",example = "附件编码")
private List fileIds;
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateBaseVO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateBaseVO.java
index e3c9797ab..1e44336aa 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateBaseVO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateBaseVO.java
@@ -4,15 +4,18 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
+import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 邮箱模版基类 Base VO")
@Data
-public class MailTemplateBaseVO { // TODO @wangjingqi:1)swagger 注解不完善;2)id、name、code、username、title、content、status 是不是要参数校验呀
+public class MailTemplateBaseVO {
@ApiModelProperty("主键")
+ @NotNull(message = "主键不能为空")
private Long id;
@ApiModelProperty("名称")
+ @NotNull(message = "名称不能为空")
private String name;
@ApiModelProperty("标识")
@@ -20,16 +23,21 @@ public class MailTemplateBaseVO { // TODO @wangjingqi:1)swagger 注解不
private String code;
@ApiModelProperty("发件人")
+ @NotNull(message = "发件人不能为空")
+ @Email(message = "发件人格式有误")
private String username;
@ApiModelProperty("标题")
+ @NotNull(message = "标题不能为空")
private String title;
@ApiModelProperty("内容")
+ @NotNull(message = "内容不能为空")
private String content;
@ApiModelProperty("状态")
- private String status;
+ @NotNull(message = "状态不能为空")
+ private Integer status;
@ApiModelProperty("备注")
private String remark;
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java
index 398e6ac03..a48c5a91a 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java
@@ -31,7 +31,7 @@ public class MailTemplatePageReqVO extends PageParam {
private String content;
@ApiModelProperty("状态")
- private String status;
+ private Integer status;
@ApiModelProperty("备注")
private String remark;
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java
index f6b9e689c..7454d2acb 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java
@@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.system.dal.dataobject.mail;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import lombok.*;
import java.io.Serializable;
-import java.sql.Timestamp;
+import java.util.Date;
/**
* 邮箱日志
@@ -18,6 +18,10 @@ import java.sql.Timestamp;
@TableName(value = "system_mail_log", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
public class MailLogDO extends BaseDO implements Serializable {
/**
@@ -29,7 +33,7 @@ public class MailLogDO extends BaseDO implements Serializable {
/**
* 邮箱账号编号
*/
- private String accountCode;
+ private Long accountId;
// TODO @wangjingyi:如果是冗余字段,记得 @ 下;
/**
@@ -40,7 +44,7 @@ public class MailLogDO extends BaseDO implements Serializable {
/**
* 模版主键
*/
- private String templateId;
+ private Long templateId;
/**
* 模版编号
@@ -65,16 +69,18 @@ public class MailLogDO extends BaseDO implements Serializable {
/**
* 发送时间
*/
- private Timestamp sendTime;
+ private Date sendTime;
/**
* 发送状态
+ *
+ * 枚举 {@link MailSendStatusEnum}
*/
- // TODO @wangjingyi:四个状态,参考短信模块
- private Boolean sendStatus;
+ private Integer sendStatus;
/**
* 发送结果
+ *
*/
private String sendResult;
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java
index b86e39e95..96f9ba4ab 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java
@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import java.util.Date;
+
/**
* 邮箱模版
*
@@ -29,11 +31,10 @@ public class MailTemplateDO extends BaseDO {
* 模版编号
*/
private String code;
- // TODO @wangjingyi:应该使用 accountId 呀
/**
- * 用户名
+ * 邮箱账号主键
*/
- private String username;
+ private Long accountId;
/**
* 标题
*/
@@ -47,7 +48,7 @@ public class MailTemplateDO extends BaseDO {
*
* 枚举 {@link CommonStatusEnum}
*/
- private String status; // TODO @wangjingyi:Integer
+ private Integer status;
/**
* 备注
*/
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java
index 8697b2151..651f5c75f 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
@@ -21,9 +22,7 @@ public interface MailAccountMapper extends BaseMapperX {
}
default MailAccountDO selectByUserName(String userName){
- // TODO @wangjingyi:selectOne 有封装的方法;然后,编码一定要学会使用泛型呀。例如说 QueryWrapperX queryWrapperX = new QueryWrapperX<>();
- QueryWrapperX queryWrapperX = new QueryWrapperX<>();
- queryWrapperX.eqIfPresent("username", userName);
- return this.selectOne(queryWrapperX);
+ return selectOne(new QueryWrapperX()
+ .eqIfPresent("username" , userName));
};
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java
index d65887e54..a5cbf0b2a 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java
@@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Date;
@Mapper
@@ -23,7 +26,15 @@ public interface MailTemplateMapper extends BaseMapperX {
}
default MailTemplateDO selectOneByCode(String code){
- // TODO @wangjingyi:优先使用 lambada 查询
- return selectOne("code" , code);
+ return selectOne(new QueryWrapperX()
+ .eqIfPresent("code" , code));
+ };
+
+ @Select("SELECT id FROM system_mail_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
+ Long selectByMaxUpdateTime(Date maxUpdateTime);
+
+ default MailTemplateDO selectOneByAccountId(Long accountId){
+ return selectOne(new QueryWrapperX()
+ .eqIfPresent("account_id" , accountId));
};
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java
index 4b02f760e..978ea5997 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java
@@ -2,17 +2,23 @@ package cn.iocoder.yudao.module.system.mq.consumer.mail;
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
+import cn.iocoder.yudao.module.system.service.mail.MailSendService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
-// TODO 芋艿:这个暂未实现
+import javax.annotation.Resource;
+
+
@Component
@Slf4j
public class MailSendConsumer extends AbstractStreamMessageListener {
+ @Resource
+ private MailSendService mailSendService;
@Override
public void onMessage(MailSendMessage message) {
log.info("[onMessage][消息内容({})]", message);
+ mailSendService.doSendMail(message);
}
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
index aee02c76e..275a45825 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
@@ -5,6 +5,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
+import java.util.List;
import java.util.Map;
/**
@@ -16,29 +17,34 @@ import java.util.Map;
@EqualsAndHashCode(callSuper = true)
public class MailSendMessage extends AbstractStreamMessage {
+ /**
+ * 日志id
+ */
+ @NotNull(message = "邮箱日志id不能为空")
+ private Long logId;
/**
* 邮箱地址
*/
@NotNull(message = "邮箱地址不能为空")
- private String address;
+ private String from;
/**
- * 短信模板编号
+ * 邮箱模板编号
*/
- @NotNull(message = "短信模板编号不能为空")
+ @NotNull(message = "邮箱模板编号不能为空")
private String templateCode;
/**
- * 短信模板参数
+ * 收件人
*/
- private Map templateParams;
-
+ @NotNull(message = "收件人不能为空")
+ private List tos;
/**
- * 用户编号,允许空
+ * 标题
*/
- private Integer userId;
+ private String title;
/**
- * 用户类型,允许空
+ * 内容
*/
- private Integer userType;
+ private String content;
@Override
public String getStreamKey() {
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
new file mode 100644
index 000000000..ed82c24a4
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.system.mq.producer.mail;
+
+import cn.iocoder.yudao.framework.common.core.KeyValue;
+import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
+import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;
+import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
+import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * Mail 邮件相关消息的 Producer
+ *
+ * @author wangjingyi
+ * @date 2021/4/19 13:33
+ */
+@Slf4j
+@Component
+public class MailProducer {
+
+ @Resource
+ private RedisMQTemplate redisMQTemplate;
+
+ /**
+ * 发送 {@link SmsChannelRefreshMessage} 消息
+ */
+ public void sendMailChannelRefreshMessage() {
+ SmsChannelRefreshMessage message = new SmsChannelRefreshMessage();
+ redisMQTemplate.send(message);
+ }
+
+ /**
+ * 发送 {@link SmsTemplateRefreshMessage} 消息
+ */
+ public void sendMailTemplateRefreshMessage() {
+ SmsTemplateRefreshMessage message = new SmsTemplateRefreshMessage();
+ redisMQTemplate.send(message);
+ }
+
+ /**
+ * 发送 {@link MailSendMessage} 消息
+ *
+ * @param mailAccountDO 邮箱账号信息
+ * @param mailTemplateDO 邮箱模版信息
+ * @param content 内容
+ * @param tos 收件人
+ * @param title 标题
+ */
+ public void sendMailSendMessage(MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String content, List tos, String title , Long sendLogId) {
+ MailSendMessage message = new MailSendMessage();
+ message.setContent(content);
+ message.setFrom(mailAccountDO.getFrom());
+ message.setTemplateCode(mailTemplateDO.getCode());
+ message.setTitle(title);
+ message.setTos(tos);
+ message.setLogId(sendLogId);
+ redisMQTemplate.send(message);
+ }
+}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java
index fa195259b..0a5d25ba2 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java
@@ -4,7 +4,9 @@ package cn.iocoder.yudao.module.system.service.mail;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import java.util.List;
@@ -28,4 +30,18 @@ public interface MailLogService {
* @return
*/
List getMailLogList(MailLogExportReqVO exportReqVO);
+
+ /**
+ * 创建邮箱日志
+ * @param mailAccountDO 邮箱账号信息
+ * @param mailTemplateDO 模版信息
+ * @param from 邮箱
+ * @param content 内容
+ * @param tos 收件人
+ * @param title 标题
+ * @param isSend 是否发送成功
+ */
+ Long createMailLog(MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String from, String content, List tos, String title, Boolean isSend);
+
+ Long updateSmsSendResult(Long logId, String result);
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java
new file mode 100644
index 000000000..31f6a3f3e
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.system.service.mail;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 邮箱模版服务类
+ *
+ * @author wangjingyi
+ * @since 2022-03-21
+ */
+public interface MailSendService {
+
+ /**
+ * 发送邮件
+ *
+ * @param templateCode 邮件模版编码
+ * @param from 邮箱
+ * @param content 内容
+ * @param tos 收件人
+ * @param title 标题
+ */
+ void sendMail(String templateCode, String from , String content , List tos , String title);
+
+ /**
+ * 执行真正的邮件发送
+ * 注意,该方法仅仅提供给 MQ Consumer 使用
+ *
+ * @param message 邮件
+ */
+ void doSendMail(MailSendMessage message);
+}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java
index 1071eae5d..b8101f0af 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import javax.validation.Valid;
import java.util.List;
+import java.util.Map;
/**
* 邮箱模版服务类
@@ -18,6 +19,7 @@ import java.util.List;
*/
public interface MailTemplateService {
+ void initLocalCache();
/**
* 邮箱模版创建
*
@@ -62,6 +64,13 @@ public interface MailTemplateService {
* @return 模版数组
*/
List getMailTemplateList();
+ /**
+ *从缓存中获取邮箱模版
+ *
+ * @param code 模板编码
+ * @return 邮箱模板
+ */
+ MailTemplateDO getMailTemplateByCodeFromCache(String code);
/**
* 发送邮件
@@ -69,4 +78,12 @@ public interface MailTemplateService {
* @param mailReqVO 邮件发送信息
*/
void sendMail(MailReqVO mailReqVO);
+
+ /**
+ * 邮件模版内容合成
+ * @param content 邮箱模版
+ * @param params 合成参数
+ * @return
+ */
+ String formateMailTemplateContent(String content, Map params);
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java
index aa1fb1d46..36ca05213 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
@@ -16,8 +17,7 @@ import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_EXISTS;
-import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
@@ -47,8 +47,8 @@ public class MailAccountServiceImpl implements MailAccountService {
@Override
public void update(MailAccountUpdateReqVO updateReqVO) {
- // username 要校验唯一 // TODO @wangjingyi:更新的就是自己,username 这样写,会重复呀。
- this.validateMailAccountOnlyByUserName(updateReqVO.getUsername());
+ // username 要校验唯一
+ this.validateMailAccountExists(updateReqVO.getId());
MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO);
// 校验是否存在
this.validateMailAccountExists(mailAccountDO.getId());
@@ -57,9 +57,10 @@ public class MailAccountServiceImpl implements MailAccountService {
@Override
public void delete(Long id) {
- // TODO @wangjingyi:删除时,要判断是否有使用的模板
- // 校验是否存在
+ // 校验是否存在账号
this.validateMailAccountExists(id);
+ // 校验是否存在关联模版
+ this.validateMailTemplateByAccountId(id);
mailAccountMapper.deleteById(id);
}
@@ -90,4 +91,11 @@ public class MailAccountServiceImpl implements MailAccountService {
throw exception(MAIL_ACCOUNT_EXISTS);
}
}
+
+ private void validateMailTemplateByAccountId(Long accountId){
+ MailTemplateDO mailTemplateDO = mailTemplateMapper.selectOneByAccountId(accountId);
+ if (mailTemplateDO != null) {
+ throw exception(MAIL_RELATE_TEMPLATE_EXISTS);
+ }
+ }
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailLogServiceImpl.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailLogServiceImpl.java
index c6b492498..bdceb0d72 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailLogServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailLogServiceImpl.java
@@ -4,14 +4,21 @@ package cn.iocoder.yudao.module.system.service.mail.impl;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
+import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
+import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
import cn.iocoder.yudao.module.system.service.mail.MailLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import java.sql.Timestamp;
+import java.util.Date;
import java.util.List;
+import java.util.Objects;
/**
* 邮箱日志实现类
@@ -35,4 +42,39 @@ public class MailLogServiceImpl implements MailLogService {
public List getMailLogList(MailLogExportReqVO exportReqVO) {
return mailLogMapper.selectList(exportReqVO);
}
+
+ @Override
+ public Long createMailLog(MailAccountDO mailAccountDO , MailTemplateDO mailTemplateDO , String from, String content, List tos, String title, Boolean isSend) {
+ MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
+ logDOBuilder.from(mailAccountDO.getFrom());
+ logDOBuilder.accountId(mailAccountDO.getId());
+ logDOBuilder.content(content);
+ logDOBuilder.title(title);
+ logDOBuilder.templateCode(mailTemplateDO.getCode());
+ logDOBuilder.templateId(mailTemplateDO.getId());
+ logDOBuilder.to(tos.toString());
+ logDOBuilder.sendTime(new Date());
+ logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()
+ : MailSendStatusEnum.IGNORE.getStatus());
+
+ MailLogDO mailLogDO = logDOBuilder.build();
+ mailLogMapper.insert(mailLogDO);
+ return mailLogDO.getId();
+ }
+
+ @Override
+ public Long updateSmsSendResult(Long logId, String result) {
+ MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
+ logDOBuilder.id(logId);
+ logDOBuilder.sendResult(result);
+ MailLogDO mailLogDO = logDOBuilder.build();
+ mailLogMapper.updateById(mailLogDO);
+ return logId;
+ }
+
+ public Long create(){
+ MailLogDO mailLogDO = new MailLogDO();
+ mailLogMapper.insert(mailLogDO);
+ return mailLogDO.getId();
+ }
}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java
new file mode 100644
index 000000000..61e428048
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java
@@ -0,0 +1,107 @@
+package cn.iocoder.yudao.module.system.service.mail.impl;
+
+
+import cn.hutool.extra.mail.MailAccount;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
+import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
+import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
+import cn.iocoder.yudao.module.system.service.mail.MailLogService;
+import cn.iocoder.yudao.module.system.service.mail.MailSendService;
+import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
+import lombok.extern.slf4j.Slf4j;
+import cn.iocoder.yudao.framework.mail.core.client.MailClientFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+import cn.iocoder.yudao.framework.mail.core.client.MailClient;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_EXISTS;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
+
+/**
+ * 邮箱模版 服务实现类
+ *
+ * @author wangjingyi
+ * @since 2022-03-21
+ */
+@Service
+@Validated
+@Slf4j
+public class MailSendServiceImpl implements MailSendService {
+
+ @Resource
+ private MailTemplateMapper mailTemplateMapper;
+ @Resource
+ private MailAccountMapper mailAccountMapper;
+ @Resource
+ private MailTemplateService mailTemplateService;
+ @Resource
+ private MailLogService mailLogService;
+ @Resource
+ private MailClientFactory mailClientFactory;
+ @Resource
+ private MailProducer mailProducer;
+
+
+ @Override
+ public void sendMail(String templateCode, String from , String content , List tos , String title) {
+ // TODO @@wangjingyi:发送的时候,参考下短信;
+ MailTemplateDO mailTemplateDO = this.checkMailTemplateValid(templateCode);
+ // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
+ Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(mailTemplateDO.getStatus());
+ //查询账号信息
+ MailAccountDO mailAccountDO = mailAccountMapper.selectOne(
+ "from", from
+ );
+ Map params = MailAccountConvert.INSTANCE.convertToMap(mailAccountDO , content);
+ content = mailTemplateService.formateMailTemplateContent(mailTemplateDO.getContent(), params);
+ Long sendLogId = mailLogService.createMailLog(mailAccountDO , mailTemplateDO , from , content , tos , title , isSend);
+
+ // 后续功能 TODO :附件查询
+ //List fileIds = mailSendVO.getFileIds();
+
+ //装载账号信息
+ MailAccount account = MailAccountConvert.INSTANCE.convertAccount(mailAccountDO);
+
+ // 发送 MQ 消息,异步执行发送短信
+ if (isSend) {
+ mailProducer.sendMailSendMessage(mailAccountDO , mailTemplateDO ,content , tos , title , sendLogId);
+ }
+ }
+
+ @Override
+ public void doSendMail(MailSendMessage message) {
+ MailClient mailClient = mailClientFactory.getMailClient();
+ String result = mailClient.sendMail(message.getFrom() , message.getContent() , message.getTitle() , message.getTos());
+ mailLogService.updateSmsSendResult(message.getLogId() , result);
+ }
+
+ private MailTemplateDO checkMailTemplateValid(String templateCode) {
+ MailTemplateDO mailTemplateDO = mailTemplateService.getMailTemplateByCodeFromCache(templateCode);
+ if (mailTemplateDO == null){
+ throw exception(MAIL_TEMPLATE_NOT_EXISTS);
+ }
+ return mailTemplateDO;
+ }
+
+ private void validateMailTemplateExists(Long id) {
+ if (mailTemplateMapper.selectById(id) == null) {
+ throw exception(MAIL_TEMPLATE_NOT_EXISTS);
+ }
+ }
+
+ private void validateMailTemplateOnlyByCode(String code){
+ if (mailTemplateMapper.selectOneByCode(code) != null) {
+ throw exception(MAIL_TEMPLATE_EXISTS);
+ }
+ }
+}
diff --git a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java
index 2bd74ac4c..e1d212186 100644
--- a/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java
@@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.system.service.mail.impl;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
@@ -13,14 +15,18 @@ import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.Valid;
+import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -36,6 +42,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPL
*/
@Service
@Validated
+@Slf4j
public class MailTemplateServiceImpl implements MailTemplateService {
@Resource
@@ -43,6 +50,38 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Resource
private MailAccountMapper mailAccountMapper;
+ private volatile List mailTemplateDOList;
+
+ /**
+ * 邮件模板缓存
+ * key:邮箱模板编码 {@link MailTemplateDO#getCode()}
+ *
+ * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+ */
+ private volatile Map mailTemplateCache;
+
+ private volatile Date maxUpdateTime;
+
+ @Override
+ @PostConstruct
+ public void initLocalCache() {
+ if(maxUpdateTime == null){
+ mailTemplateDOList = mailTemplateMapper.selectList();
+ }else{
+ if(mailTemplateMapper.selectByMaxUpdateTime(maxUpdateTime)<=0){
+ return;
+ }
+ }
+ if (CollUtil.isEmpty(mailTemplateDOList)) {
+ return;
+ }
+
+ // 写入缓存
+ mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getCode);
+ maxUpdateTime = CollectionUtils.getMaxValue(mailTemplateDOList, MailTemplateDO::getUpdateTime);
+ log.info("[initLocalCache][初始化 mailTemplate 数量为 {}]", mailTemplateDOList.size());
+ }
+
@Override
public Long create(MailTemplateCreateReqVO createReqVO) {
// code 要校验唯一
@@ -54,11 +93,9 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override
public void update(@Valid MailTemplateUpdateReqVO updateReqVO) {
- // code 要校验唯一
- this.validateMailTemplateOnlyByCode(updateReqVO.getCode()); // TODO @wangjingyi:code 这样写,修改自己会有问题
- MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
// 校验是否存在
- this.validateMailTemplateExists(mailTemplateDO.getId());
+ this.validateMailTemplateExists(updateReqVO.getId());
+ MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
mailTemplateMapper.updateById(mailTemplateDO);
}
@@ -80,6 +117,11 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override
public List getMailTemplateList() {return mailTemplateMapper.selectList();}
+ @Override
+ public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
+ return mailTemplateCache.get(code);
+ }
+
@Override
public void sendMail(MailReqVO mailReqVO) {
// TODO @@wangjingyi:发送的时候,参考下短信;
@@ -102,6 +144,11 @@ public class MailTemplateServiceImpl implements MailTemplateService {
MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false);
}
+ @Override
+ public String formateMailTemplateContent(String content, Map params) {
+ return StrUtil.format(content, params);
+ }
+
private void validateMailTemplateExists(Long id) {
if (mailTemplateMapper.selectById(id) == null) {
throw exception(MAIL_TEMPLATE_NOT_EXISTS);