邮件模块 添加邮件发送api

pull/2/head
wangjingyi 2022-04-30 21:31:55 +08:00
parent d1812761db
commit d7305739d3
44 changed files with 1085 additions and 59 deletions

View File

@ -30,6 +30,7 @@
<module>yudao-spring-boot-starter-biz-operatelog</module> <module>yudao-spring-boot-starter-biz-operatelog</module>
<module>yudao-spring-boot-starter-biz-dict</module> <module>yudao-spring-boot-starter-biz-dict</module>
<module>yudao-spring-boot-starter-biz-sms</module> <module>yudao-spring-boot-starter-biz-sms</module>
<module>yudao-spring-boot-starter-biz-mail</module>
<module>yudao-spring-boot-starter-activiti</module> <module>yudao-spring-boot-starter-activiti</module>
<module>yudao-spring-boot-starter-biz-pay</module> <module>yudao-spring-boot-starter-biz-pay</module>
<module>yudao-spring-boot-starter-biz-weixin</module> <module>yudao-spring-boot-starter-biz-weixin</module>

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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<String> tos);
}

View File

@ -0,0 +1,5 @@
package cn.iocoder.yudao.framework.mail.core.client;
public interface MailClientFactory {
MailClient getMailClient();
}

View File

@ -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<String, ErrorCode> {
}

View File

@ -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<T> extends CommonResult<T> {
/**
* API
*
* 使 String
*/
private String apiCode;
/**
* API
*/
private String apiMsg;
/**
* API
*/
private String apiRequestId;
private MailCommonResult() {
}
public static <T> MailCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId,
T data, MailCodeMapping codeMapping) {
Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
MailCommonResult<T> result = new MailCommonResult<T>().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 <T> MailCommonResult<T> error(Throwable ex) {
MailCommonResult<T> result = new MailCommonResult<>();
result.setCode(MailFrameworkErrorCodeConstants.EXCEPTION.getCode());
result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
return result;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -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");
}
}

View File

@ -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 "";
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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, "调用异常");
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
package cn.iocoder.yudao.module.system.api.mail;
public interface MailSendApi {
}

View File

@ -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 @wangjingqi1, 不用空格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<String> tos;
@ApiModelProperty(value = "附件",example = "附件编码")
private List<String> fileIds;
}

View File

@ -126,5 +126,6 @@ public interface ErrorCodeConstants {
// ========== 邮箱模版 1002020000 ========== // ========== 邮箱模版 1002020000 ==========
ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002020000 , "邮箱模版不存在"); ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002020000 , "邮箱模版不存在");
ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002020001, "邮箱模版存在"); ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002020001, "邮箱模版存在");
ErrorCode MAIL_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002020002, "存在关联邮箱模版");
} }

View File

@ -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;
}

View File

@ -47,6 +47,10 @@
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId> <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-mail</artifactId>
</dependency>
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId> <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
@ -102,6 +106,7 @@
<artifactId>yudao-spring-boot-starter-excel</artifactId> <artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -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{
}

View File

@ -4,28 +4,34 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 邮箱账号基类 Base VO") @ApiModel("管理后台 - 邮箱账号基类 Base VO")
@Data @Data
public class MailAccountBaseVO { // TODO @wangjingqi1, 不用空格2from、username、password、host、sslEnable 都要参数校验非空3username 要 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; private String from;
@ApiModelProperty(value = "用户名" , required = true , example = "yudao") @ApiModelProperty(value = "用户名",required = true,example = "yudao")
@NotNull(message = "用户名必填") @NotNull(message = "用户名必填")
@Email(message = "必须是Email格式")
private String username; private String username;
@ApiModelProperty(value = "密码" , required = true , example = "123456") @ApiModelProperty(value = "密码",required = true,example = "123456")
@NotNull(message = "密码必填")
private String password; private String password;
@ApiModelProperty(value = "网站" , required = true , example = "www.iocoder.cn") @ApiModelProperty(value = "网站",required = true,example = "www.iocoder.cn")
@NotNull(message = "网站必填")
private String host; private String host;
@ApiModelProperty(value = "端口" , required = true , example = "80") @ApiModelProperty(value = "端口",required = true,example = "80")
private String port; private Integer port;
@ApiModelProperty(value = "是否开启ssl" , required = true , example = "2") @ApiModelProperty(value = "是否开启ssl",required = true,example = "2")
@NotNull(message = "是否开启ssl必填")
private Boolean sslEnable; private Boolean sslEnable;
} }

View File

@ -6,6 +6,7 @@ import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.sql.Timestamp; 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; 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) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ExcelProperty(value = "发送时间" ) @ExcelProperty(value = "发送时间" )
private Timestamp sendTime; private Date sendTime;
@ExcelProperty(value = "发送状态") @ExcelProperty(value = "发送状态")
private Boolean sendStatus; private Integer sendStatus;
@ExcelProperty(value = "发送结果") @ExcelProperty(value = "发送结果")
private String sendResult; private String sendResult;

View File

@ -36,7 +36,7 @@ public class MailLogRespVO {
private Timestamp sendTime; private Timestamp sendTime;
@ApiModelProperty(value = "发送状态" , required = false , example = "1") @ApiModelProperty(value = "发送状态" , required = false , example = "1")
private Boolean sendStatus; private Integer sendStatus;
@ApiModelProperty(value = "发送结果" , required = false , example = "yudaoyuanma@123.com") @ApiModelProperty(value = "发送结果" , required = false , example = "yudaoyuanma@123.com")
private String sendResult; private String sendResult;

View File

@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.List; import java.util.List;
@ -11,25 +12,26 @@ import java.util.List;
@Data @Data
public class MailReqVO { // TODO @wangjingqi1, 不用空格2应该只要传递 templateCode、参数就好title、from、content、附件应该都是参数里的 public class MailReqVO { // TODO @wangjingqi1, 不用空格2应该只要传递 templateCode、参数就好title、from、content、附件应该都是参数里的
@ApiModelProperty(value = "邮箱" , required = true , example = "yudaoyuanma@123.com") @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
@NotNull(message = "邮箱账号不能为空") @NotNull(message = "邮箱账号不能为空")
@Email(message = "邮箱账号格式错误")
private String from; private String from;
@ApiModelProperty(value = "标题" , example = "标题") @ApiModelProperty(value = "标题",example = "标题")
private String title; private String title;
@ApiModelProperty(value = "内容" , example = "内容") @ApiModelProperty(value = "内容",example = "内容")
private String content; private String content;
@ApiModelProperty(value = "邮箱模版id" , example = "1024") @ApiModelProperty(value = "邮箱模版id",example = "1024")
@NotNull(message = "邮箱模版id不能为空") @NotNull(message = "邮箱模版id不能为空")
private Integer templateId; private Integer templateId;
@ApiModelProperty(value = "收件人" , required = true , example = "yudaoyuanma@123.com") @ApiModelProperty(value = "收件人",required = true,example = "yudaoyuanma@123.com")
@NotNull(message = "收件人不能为空") @NotNull(message = "收件人不能为空")
private List<String> tos; private List<String> tos;
@ApiModelProperty(value = "附件" , example = "附件编码") @ApiModelProperty(value = "附件",example = "附件编码")
private List<String> fileIds; private List<String> fileIds;

View File

@ -4,15 +4,18 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 邮箱模版基类 Base VO") @ApiModel("管理后台 - 邮箱模版基类 Base VO")
@Data @Data
public class MailTemplateBaseVO { // TODO @wangjingqi1swagger 注解不完善2id、name、code、username、title、content、status 是不是要参数校验呀 public class MailTemplateBaseVO {
@ApiModelProperty("主键") @ApiModelProperty("主键")
@NotNull(message = "主键不能为空")
private Long id; private Long id;
@ApiModelProperty("名称") @ApiModelProperty("名称")
@NotNull(message = "名称不能为空")
private String name; private String name;
@ApiModelProperty("标识") @ApiModelProperty("标识")
@ -20,16 +23,21 @@ public class MailTemplateBaseVO { // TODO @wangjingqi1swagger 注解不
private String code; private String code;
@ApiModelProperty("发件人") @ApiModelProperty("发件人")
@NotNull(message = "发件人不能为空")
@Email(message = "发件人格式有误")
private String username; private String username;
@ApiModelProperty("标题") @ApiModelProperty("标题")
@NotNull(message = "标题不能为空")
private String title; private String title;
@ApiModelProperty("内容") @ApiModelProperty("内容")
@NotNull(message = "内容不能为空")
private String content; private String content;
@ApiModelProperty("状态") @ApiModelProperty("状态")
private String status; @NotNull(message = "状态不能为空")
private Integer status;
@ApiModelProperty("备注") @ApiModelProperty("备注")
private String remark; private String remark;

View File

@ -31,7 +31,7 @@ public class MailTemplatePageReqVO extends PageParam {
private String content; private String content;
@ApiModelProperty("状态") @ApiModelProperty("状态")
private String status; private Integer status;
@ApiModelProperty("备注") @ApiModelProperty("备注")
private String remark; private String remark;

View File

@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.system.dal.dataobject.mail; package cn.iocoder.yudao.module.system.dal.dataobject.mail;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 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 com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.*;
import lombok.EqualsAndHashCode;
import java.io.Serializable; 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) @TableName(value = "system_mail_log", autoResultMap = true)
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MailLogDO extends BaseDO implements Serializable { 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如果是冗余字段记得 @ 下; // 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 Integer sendStatus;
private Boolean sendStatus;
/** /**
* *
*
*/ */
private String sendResult; private String sendResult;

View File

@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.Date;
/** /**
* *
* *
@ -29,11 +31,10 @@ public class MailTemplateDO extends BaseDO {
* *
*/ */
private String code; private String code;
// TODO @wangjingyi应该使用 accountId 呀
/** /**
* *
*/ */
private String username; private Long accountId;
/** /**
* *
*/ */
@ -47,7 +48,7 @@ public class MailTemplateDO extends BaseDO {
* *
* {@link CommonStatusEnum} * {@link CommonStatusEnum}
*/ */
private String status; // TODO @wangjingyiInteger private Integer status;
/** /**
* *
*/ */

View File

@ -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.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; 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.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
@ -21,9 +22,7 @@ public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
} }
default MailAccountDO selectByUserName(String userName){ default MailAccountDO selectByUserName(String userName){
// TODO @wangjingyiselectOne 有封装的方法;然后,编码一定要学会使用泛型呀。例如说 QueryWrapperX<MailAccountDO> queryWrapperX = new QueryWrapperX<>(); return selectOne(new QueryWrapperX<MailAccountDO>()
QueryWrapperX<MailAccountDO> queryWrapperX = new QueryWrapperX<>(); .eqIfPresent("username" , userName));
queryWrapperX.eqIfPresent("username", userName);
return this.selectOne(queryWrapperX);
}; };
} }

View File

@ -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.controller.admin.mail.vo.template.MailTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
@Mapper @Mapper
@ -23,7 +26,15 @@ public interface MailTemplateMapper extends BaseMapperX<MailTemplateDO> {
} }
default MailTemplateDO selectOneByCode(String code){ default MailTemplateDO selectOneByCode(String code){
// TODO @wangjingyi优先使用 lambada 查询 return selectOne(new QueryWrapperX<MailTemplateDO>()
return selectOne("code" , code); .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<MailTemplateDO>()
.eqIfPresent("account_id" , accountId));
}; };
} }

View File

@ -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.framework.mq.core.stream.AbstractStreamMessageListener;
import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; 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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
// TODO 芋艿:这个暂未实现 import javax.annotation.Resource;
@Component @Component
@Slf4j @Slf4j
public class MailSendConsumer extends AbstractStreamMessageListener<MailSendMessage> { public class MailSendConsumer extends AbstractStreamMessageListener<MailSendMessage> {
@Resource
private MailSendService mailSendService;
@Override @Override
public void onMessage(MailSendMessage message) { public void onMessage(MailSendMessage message) {
log.info("[onMessage][消息内容({})]", message); log.info("[onMessage][消息内容({})]", message);
mailSendService.doSendMail(message);
} }
} }

View File

@ -5,6 +5,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -16,29 +17,34 @@ import java.util.Map;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class MailSendMessage extends AbstractStreamMessage { public class MailSendMessage extends AbstractStreamMessage {
/**
* id
*/
@NotNull(message = "邮箱日志id不能为空")
private Long logId;
/** /**
* *
*/ */
@NotNull(message = "邮箱地址不能为空") @NotNull(message = "邮箱地址不能为空")
private String address; private String from;
/** /**
* *
*/ */
@NotNull(message = "短信模板编号不能为空") @NotNull(message = "邮箱模板编号不能为空")
private String templateCode; private String templateCode;
/** /**
* *
*/ */
private Map<String, Object> templateParams; @NotNull(message = "收件人不能为空")
private List<String> tos;
/** /**
* *
*/ */
private Integer userId; private String title;
/** /**
* *
*/ */
private Integer userType; private String content;
@Override @Override
public String getStreamKey() { public String getStreamKey() {

View File

@ -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);
}
}

View File

@ -4,7 +4,9 @@ package cn.iocoder.yudao.module.system.service.mail;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.MailLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; 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.MailLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import java.util.List; import java.util.List;
@ -28,4 +30,18 @@ public interface MailLogService {
* @return * @return
*/ */
List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO); List<MailLogDO> 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<String> tos, String title, Boolean isSend);
Long updateSmsSendResult(Long logId, String result);
} }

View File

@ -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<String> tos , String title);
/**
*
* MQ Consumer 使
*
* @param message
*/
void doSendMail(MailSendMessage message);
}

View File

@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* *
@ -18,6 +19,7 @@ import java.util.List;
*/ */
public interface MailTemplateService { public interface MailTemplateService {
void initLocalCache();
/** /**
* *
* *
@ -62,6 +64,13 @@ public interface MailTemplateService {
* @return * @return
*/ */
List<MailTemplateDO> getMailTemplateList(); List<MailTemplateDO> getMailTemplateList();
/**
*
*
* @param code
* @return
*/
MailTemplateDO getMailTemplateByCodeFromCache(String code);
/** /**
* *
@ -69,4 +78,12 @@ public interface MailTemplateService {
* @param mailReqVO * @param mailReqVO
*/ */
void sendMail(MailReqVO mailReqVO); void sendMail(MailReqVO mailReqVO);
/**
*
* @param content
* @param params
* @return
*/
String formateMailTemplateContent(String content, Map<String, String> params);
} }

View File

@ -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.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert; 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.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.MailAccountMapper;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
import cn.iocoder.yudao.module.system.service.mail.MailAccountService; import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
@ -16,8 +17,7 @@ import javax.annotation.Resource;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS;
/** /**
@ -47,8 +47,8 @@ public class MailAccountServiceImpl implements MailAccountService {
@Override @Override
public void update(MailAccountUpdateReqVO updateReqVO) { public void update(MailAccountUpdateReqVO updateReqVO) {
// username 要校验唯一 // TODO @wangjingyi更新的就是自己username 这样写,会重复呀。 // username 要校验唯一
this.validateMailAccountOnlyByUserName(updateReqVO.getUsername()); this.validateMailAccountExists(updateReqVO.getId());
MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO); MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO);
// 校验是否存在 // 校验是否存在
this.validateMailAccountExists(mailAccountDO.getId()); this.validateMailAccountExists(mailAccountDO.getId());
@ -57,9 +57,10 @@ public class MailAccountServiceImpl implements MailAccountService {
@Override @Override
public void delete(Long id) { public void delete(Long id) {
// TODO @wangjingyi删除时要判断是否有使用的模板 // 校验是否存在账号
// 校验是否存在
this.validateMailAccountExists(id); this.validateMailAccountExists(id);
// 校验是否存在关联模版
this.validateMailTemplateByAccountId(id);
mailAccountMapper.deleteById(id); mailAccountMapper.deleteById(id);
} }
@ -90,4 +91,11 @@ public class MailAccountServiceImpl implements MailAccountService {
throw exception(MAIL_ACCOUNT_EXISTS); throw exception(MAIL_ACCOUNT_EXISTS);
} }
} }
private void validateMailTemplateByAccountId(Long accountId){
MailTemplateDO mailTemplateDO = mailTemplateMapper.selectOneByAccountId(accountId);
if (mailTemplateDO != null) {
throw exception(MAIL_RELATE_TEMPLATE_EXISTS);
}
}
} }

View File

@ -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.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.MailLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; 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.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.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 cn.iocoder.yudao.module.system.service.mail.MailLogService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* *
@ -35,4 +42,39 @@ public class MailLogServiceImpl implements MailLogService {
public List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO) { public List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO) {
return mailLogMapper.selectList(exportReqVO); return mailLogMapper.selectList(exportReqVO);
} }
@Override
public Long createMailLog(MailAccountDO mailAccountDO , MailTemplateDO mailTemplateDO , String from, String content, List<String> 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();
}
} }

View File

@ -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<String> 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<String , String> 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<String> 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);
}
}
}

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.system.service.mail.impl; package cn.iocoder.yudao.module.system.service.mail.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.MailAccount; import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil; import cn.hutool.extra.mail.MailUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.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.MailTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; 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.convert.mail.MailTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; 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.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.MailAccountMapper;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -36,6 +42,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPL
*/ */
@Service @Service
@Validated @Validated
@Slf4j
public class MailTemplateServiceImpl implements MailTemplateService { public class MailTemplateServiceImpl implements MailTemplateService {
@Resource @Resource
@ -43,6 +50,38 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Resource @Resource
private MailAccountMapper mailAccountMapper; private MailAccountMapper mailAccountMapper;
private volatile List<MailTemplateDO> mailTemplateDOList;
/**
*
* key {@link MailTemplateDO#getCode()}
*
* volatile
*/
private volatile Map<String, MailTemplateDO> 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 @Override
public Long create(MailTemplateCreateReqVO createReqVO) { public Long create(MailTemplateCreateReqVO createReqVO) {
// code 要校验唯一 // code 要校验唯一
@ -54,11 +93,9 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override @Override
public void update(@Valid MailTemplateUpdateReqVO updateReqVO) { public void update(@Valid MailTemplateUpdateReqVO updateReqVO) {
// code 要校验唯一
this.validateMailTemplateOnlyByCode(updateReqVO.getCode()); // TODO @wangjingyicode 这样写,修改自己会有问题
MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
// 校验是否存在 // 校验是否存在
this.validateMailTemplateExists(mailTemplateDO.getId()); this.validateMailTemplateExists(updateReqVO.getId());
MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
mailTemplateMapper.updateById(mailTemplateDO); mailTemplateMapper.updateById(mailTemplateDO);
} }
@ -80,6 +117,11 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override @Override
public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();} public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();}
@Override
public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
return mailTemplateCache.get(code);
}
@Override @Override
public void sendMail(MailReqVO mailReqVO) { public void sendMail(MailReqVO mailReqVO) {
// TODO @@wangjingyi发送的时候参考下短信 // TODO @@wangjingyi发送的时候参考下短信
@ -102,6 +144,11 @@ public class MailTemplateServiceImpl implements MailTemplateService {
MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false); MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false);
} }
@Override
public String formateMailTemplateContent(String content, Map<String, String> params) {
return StrUtil.format(content, params);
}
private void validateMailTemplateExists(Long id) { private void validateMailTemplateExists(Long id) {
if (mailTemplateMapper.selectById(id) == null) { if (mailTemplateMapper.selectById(id) == null) {
throw exception(MAIL_TEMPLATE_NOT_EXISTS); throw exception(MAIL_TEMPLATE_NOT_EXISTS);