邮件模块 添加邮件发送api
parent
d1812761db
commit
d7305739d3
@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-framework</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-mail</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring 核心 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 监控相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.opentracing</groupId>
|
||||||
|
<artifactId>opentracing-util</artifactId> <!-- aliyun 短信需要,进行链路追踪 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<optional>true</optional> <!-- 设置为可选,因为使用到 @VisibleForTesting 用于单元测试 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.validation</groupId>
|
||||||
|
<artifactId>jakarta.validation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package cn.iocoder.yudao.framework.mail.core.client;
|
||||||
|
|
||||||
|
public interface MailClientFactory {
|
||||||
|
MailClient getMailClient();
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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<String, AbstractMailClient> 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<String> tos) {
|
||||||
|
try{
|
||||||
|
return MailUtil.send(from , title , content , false , null);
|
||||||
|
}catch (Exception e){
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.api.mail;
|
||||||
|
|
||||||
|
public interface MailSendApi {
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -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{
|
||||||
|
}
|
||||||
@ -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<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue