Commit d7305739d3fcef6efe25c12d33c02b903c181308

Authored by wangjingyi
1 parent d1812761

邮件模块 添加邮件发送api

Showing 44 changed files with 1085 additions and 59 deletions
yudao-framework/pom.xml
@@ -30,6 +30,7 @@ @@ -30,6 +30,7 @@
30 <module>yudao-spring-boot-starter-biz-operatelog</module> 30 <module>yudao-spring-boot-starter-biz-operatelog</module>
31 <module>yudao-spring-boot-starter-biz-dict</module> 31 <module>yudao-spring-boot-starter-biz-dict</module>
32 <module>yudao-spring-boot-starter-biz-sms</module> 32 <module>yudao-spring-boot-starter-biz-sms</module>
  33 + <module>yudao-spring-boot-starter-biz-mail</module>
33 <module>yudao-spring-boot-starter-activiti</module> 34 <module>yudao-spring-boot-starter-activiti</module>
34 <module>yudao-spring-boot-starter-biz-pay</module> 35 <module>yudao-spring-boot-starter-biz-pay</module>
35 <module>yudao-spring-boot-starter-biz-weixin</module> 36 <module>yudao-spring-boot-starter-biz-weixin</module>
yudao-framework/yudao-spring-boot-starter-biz-mail/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao-framework</artifactId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>yudao-spring-boot-starter-biz-mail</artifactId>
  12 + <packaging>jar</packaging>
  13 +
  14 + <dependencies>
  15 + <dependency>
  16 + <groupId>cn.iocoder.boot</groupId>
  17 + <artifactId>yudao-common</artifactId>
  18 + </dependency>
  19 +
  20 + <!-- Spring 核心 -->
  21 + <dependency>
  22 + <groupId>org.springframework.boot</groupId>
  23 + <artifactId>spring-boot-starter</artifactId>
  24 + </dependency>
  25 +
  26 + <!-- 监控相关 -->
  27 + <dependency>
  28 + <groupId>io.opentracing</groupId>
  29 + <artifactId>opentracing-util</artifactId> <!-- aliyun 短信需要,进行链路追踪 -->
  30 + </dependency>
  31 +
  32 + <!-- Test 测试相关 -->
  33 + <dependency>
  34 + <groupId>cn.iocoder.boot</groupId>
  35 + <artifactId>yudao-spring-boot-starter-test</artifactId>
  36 + <scope>test</scope>
  37 + </dependency>
  38 +
  39 + <!-- 工具类相关 -->
  40 + <dependency>
  41 + <groupId>com.google.guava</groupId>
  42 + <artifactId>guava</artifactId>
  43 + <optional>true</optional> <!-- 设置为可选,因为使用到 @VisibleForTesting 用于单元测试 -->
  44 + </dependency>
  45 +
  46 + <dependency>
  47 + <groupId>com.fasterxml.jackson.core</groupId>
  48 + <artifactId>jackson-databind</artifactId>
  49 + </dependency>
  50 + <dependency>
  51 + <groupId>com.fasterxml.jackson.core</groupId>
  52 + <artifactId>jackson-core</artifactId>
  53 + </dependency>
  54 +
  55 + <dependency>
  56 + <groupId>jakarta.validation</groupId>
  57 + <artifactId>jakarta.validation-api</artifactId>
  58 + </dependency>
  59 + <dependency>
  60 + <groupId>org.projectlombok</groupId>
  61 + <artifactId>lombok</artifactId>
  62 + </dependency>
  63 + </dependencies>
  64 +
  65 +</project>
0 \ No newline at end of file 66 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/config/YudaoMailAutoConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.config;
  2 +
  3 +import cn.iocoder.yudao.framework.mail.core.client.MailClientFactory;
  4 +import cn.iocoder.yudao.framework.mail.core.client.impl.MailClientFactoryImpl;
  5 +import org.springframework.context.annotation.Bean;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +/**
  9 + * 邮箱配置类
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Configuration
  14 +public class YudaoMailAutoConfiguration {
  15 +
  16 + @Bean
  17 + public MailClientFactory mailClientFactory() {
  18 + return new MailClientFactoryImpl();
  19 + }
  20 +
  21 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client;
  2 +
  3 +import java.util.List;
  4 +
  5 +/**
  6 + * 邮件客户端,用于对接各邮箱平台的 SDK,实现邮件发送等功能
  7 + *
  8 + * @author wangjingyi
  9 + * @date 2021/4/19 19:21
  10 + */
  11 +public interface MailClient {
  12 +
  13 + /**
  14 + * 发送邮件
  15 + *
  16 + * @param from 邮箱账号
  17 + * @param content 内容
  18 + * @param title 标题
  19 + * @param tos 收件人
  20 + * @return 邮件发送结果
  21 + */
  22 + String sendMail(String from, String content, String title, List<String> tos);
  23 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailClientFactory.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client;
  2 +
  3 +public interface MailClientFactory {
  4 + MailClient getMailClient();
  5 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCodeMapping.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  4 +import cn.iocoder.yudao.framework.mail.core.enums.MailFrameworkErrorCodeConstants;
  5 +
  6 +import java.util.function.Function;
  7 +
  8 +/**
  9 + * 将 API 的错误码,转换为通用的错误码
  10 + *
  11 + * @see MailCommonResult
  12 + * @see MailFrameworkErrorCodeConstants
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface MailCodeMapping extends Function<String, ErrorCode> {
  17 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/MailCommonResult.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client;
  2 +
  3 +import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  6 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +import lombok.ToString;
  10 +import cn.iocoder.yudao.framework.mail.core.enums.MailFrameworkErrorCodeConstants;
  11 +
  12 +/**
  13 + * 短信的 CommonResult 拓展类
  14 + *
  15 + * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
  16 + *
  17 + * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Data
  22 +@EqualsAndHashCode(callSuper = true)
  23 +@ToString(callSuper = true)
  24 +public class MailCommonResult<T> extends CommonResult<T> {
  25 +
  26 + /**
  27 + * API 返回错误码
  28 + *
  29 + * 由于第三方的错误码可能是字符串,所以使用 String 类型
  30 + */
  31 + private String apiCode;
  32 + /**
  33 + * API 返回提示
  34 + */
  35 + private String apiMsg;
  36 +
  37 + /**
  38 + * API 请求编号
  39 + */
  40 + private String apiRequestId;
  41 +
  42 + private MailCommonResult() {
  43 + }
  44 +
  45 + public static <T> MailCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId,
  46 + T data, MailCodeMapping codeMapping) {
  47 + Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
  48 + MailCommonResult<T> result = new MailCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId);
  49 + result.setData(data);
  50 + // 翻译错误码
  51 + if (codeMapping != null) {
  52 + ErrorCode errorCode = codeMapping.apply(apiCode);
  53 + if (errorCode == null) {
  54 + errorCode = MailFrameworkErrorCodeConstants.MAIL_UNKNOWN;
  55 + }
  56 + result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
  57 + }
  58 + return result;
  59 + }
  60 +
  61 + public static <T> MailCommonResult<T> error(Throwable ex) {
  62 + MailCommonResult<T> result = new MailCommonResult<>();
  63 + result.setCode(MailFrameworkErrorCodeConstants.EXCEPTION.getCode());
  64 + result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
  65 + return result;
  66 + }
  67 +
  68 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailReceiveRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * 消息接收 Response DTO
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +public class MailReceiveRespDTO {
  14 +
  15 + /**
  16 + * 是否接收成功
  17 + */
  18 + private Boolean success;
  19 + /**
  20 + * API 接收结果的编码
  21 + */
  22 + private String errorCode;
  23 + /**
  24 + * API 接收结果的说明
  25 + */
  26 + private String errorMsg;
  27 +
  28 + /**
  29 + * 手机号
  30 + */
  31 + private String mobile;
  32 + /**
  33 + * 用户接收时间
  34 + */
  35 + private Date receiveTime;
  36 +
  37 + /**
  38 + * 短信 API 发送返回的序号
  39 + */
  40 + private String serialNo;
  41 + /**
  42 + * 短信日志编号
  43 + *
  44 + * 对应 SysSmsLogDO 的编号
  45 + */
  46 + private Long logId;
  47 +
  48 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailSendRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 短信发送 Response DTO
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class MailSendRespDTO {
  12 +
  13 + /**
  14 + * 短信 API 发送返回的序号
  15 + */
  16 + private String serialNo;
  17 +
  18 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/dto/MailTemplateRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 短信模板 Response DTO
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class MailTemplateRespDTO {
  12 +
  13 + /**
  14 + * 模板编号
  15 + */
  16 + private String id;
  17 + /**
  18 + * 短信内容
  19 + */
  20 + private String content;
  21 + /**
  22 + * 审核状态
  23 + *
  24 + */
  25 + private Integer auditStatus;
  26 + /**
  27 + * 审核未通过的理由
  28 + */
  29 + private String auditReason;
  30 +
  31 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/AbstractMailClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.impl;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import cn.iocoder.yudao.framework.mail.core.client.MailClient;
  5 +
  6 +/**
  7 + * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码
  8 + *
  9 + * @author zzf
  10 + * @date 2021/2/1 9:28
  11 + */
  12 +@Slf4j
  13 +public abstract class AbstractMailClient implements MailClient {
  14 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/MailClientFactoryImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.impl;
  2 +
  3 +import cn.iocoder.yudao.framework.mail.core.client.MailClient;
  4 +import cn.iocoder.yudao.framework.mail.core.client.impl.hutool.HutoolMailClient;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import cn.iocoder.yudao.framework.mail.core.client.MailClientFactory;
  7 +import cn.iocoder.yudao.framework.mail.core.enums.MailChannelEnum;
  8 +import org.springframework.validation.annotation.Validated;
  9 +
  10 +import java.util.Arrays;
  11 +import java.util.concurrent.ConcurrentHashMap;
  12 +import java.util.concurrent.ConcurrentMap;
  13 +@Validated
  14 +@Slf4j
  15 +public class MailClientFactoryImpl implements MailClientFactory {
  16 +
  17 + private final ConcurrentMap<String, AbstractMailClient> channelCodeClients = new ConcurrentHashMap<>();
  18 +
  19 + public MailClientFactoryImpl (){
  20 + Arrays.stream(MailChannelEnum.values()).forEach(mailChannelEnum -> {
  21 + AbstractMailClient abstractMailClient = createMailClient(mailChannelEnum);
  22 + channelCodeClients.put(mailChannelEnum.getCode() , abstractMailClient);
  23 + });
  24 + }
  25 +
  26 + private AbstractMailClient createMailClient(MailChannelEnum mailChannelEnum) {
  27 + switch (mailChannelEnum){
  28 + case HUTOOL: return new HutoolMailClient();
  29 + }
  30 + // 创建失败,错误日志 + 抛出异常
  31 + log.error("[createMailClient][配置({}) 找不到合适的客户端实现]" , mailChannelEnum);
  32 + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", mailChannelEnum));
  33 + }
  34 +
  35 + @Override
  36 + public MailClient getMailClient() {
  37 + return channelCodeClients.get("HUTOOL");
  38 + }
  39 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.impl.hutool;
  2 +
  3 +import cn.hutool.extra.mail.MailUtil;
  4 +import cn.iocoder.yudao.framework.mail.core.client.impl.AbstractMailClient;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +
  7 +import java.util.List;
  8 +
  9 +
  10 +/**
  11 + * 邮件客户端实现
  12 + *
  13 + * @author wangjingyi
  14 + * @date 2021/4/25 14:25
  15 + */
  16 +@Slf4j
  17 +public class HutoolMailClient extends AbstractMailClient {
  18 +
  19 + @Override
  20 + public String sendMail(String from, String content, String title, List<String> tos) {
  21 + try{
  22 + return MailUtil.send(from , title , content , false , null);
  23 + }catch (Exception e){
  24 + log.error(e.getMessage());
  25 + }
  26 + return "";
  27 + }
  28 +
  29 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/client/impl/hutool/HutoolMailCodeMapping.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.client.impl.hutool;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  4 +import cn.iocoder.yudao.framework.mail.core.client.MailCodeMapping;
  5 +
  6 +/**
  7 + * 阿里云的 SmsCodeMapping 实现类
  8 + *
  9 + * 参见 https://help.aliyun.com/document_detail/101346.htm 文档
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +public class HutoolMailCodeMapping implements MailCodeMapping {
  14 +
  15 + @Override
  16 + public ErrorCode apply(String apiCode) {
  17 + return null;
  18 + }
  19 +
  20 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailChannelEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.enums;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +/**
  8 + * 短信渠道枚举
  9 + *
  10 + * @author zzf
  11 + * @date 2021/1/25 10:56
  12 + */
  13 +@Getter
  14 +@AllArgsConstructor
  15 +public enum MailChannelEnum {
  16 + HUTOOL("HUTOOL" , "HUTOOL"),
  17 + ;
  18 +
  19 + /**
  20 + * 编码
  21 + */
  22 + private final String code;
  23 + /**
  24 + * 名字
  25 + */
  26 + private final String name;
  27 +
  28 + public static MailChannelEnum getByCode(String code) {
  29 + return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
  30 + }
  31 +
  32 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailFrameworkErrorCodeConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.enums;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  4 +
  5 +/**
  6 + * 短信框架的错误码枚举
  7 + *
  8 + * 短信框架,使用 2-001-000-000 段
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface MailFrameworkErrorCodeConstants {
  13 +
  14 + ErrorCode MAIL_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析");
  15 +
  16 + // ========== 权限 / 限流等相关 2001000100 ==========
  17 +
  18 + ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");
  19 + // 云片:可以配置 IP 白名单,只有在白名单中才可以发送短信
  20 + ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");
  21 +
  22 + // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
  23 + ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流");
  24 + // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
  25 + ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流");
  26 +
  27 + ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词");
  28 +
  29 + // ========== 模板相关 2001000200 ==========
  30 + ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在
  31 + ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确");
  32 +
  33 + // ========== 签名相关 2001000300 ==========
  34 + ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用");
  35 +
  36 + // ========== 账户相关 2001000400 ==========
  37 + ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足");
  38 + ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在");
  39 +
  40 + // ========== 其它相关 2001000900 开头 ==========
  41 + ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失");
  42 + ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确");
  43 + ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中");
  44 +
  45 + ErrorCode EXCEPTION = new ErrorCode(2001000999, "调用异常");
  46 +
  47 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/enums/MailTemplateAuditStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 短信模板的审核状态枚举
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@AllArgsConstructor
  12 +@Getter
  13 +public enum MailTemplateAuditStatusEnum {
  14 +
  15 + CHECKING(1),
  16 + SUCCESS(2),
  17 + FAIL(3);
  18 +
  19 + private final Integer status;
  20 +
  21 +}
yudao-framework/yudao-spring-boot-starter-biz-mail/src/main/java/cn/iocoder/yudao/framework/mail/core/property/MailChannelProperties.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mail.core.property;
  2 +
  3 +import lombok.Data;
  4 +import cn.iocoder.yudao.framework.mail.core.enums.MailChannelEnum;
  5 +import org.springframework.validation.annotation.Validated;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +/**
  11 + * 短信渠道配置类
  12 + *
  13 + * @author zzf
  14 + * @date 2021/1/25 17:01
  15 + */
  16 +@Data
  17 +@Validated
  18 +public class MailChannelProperties {
  19 +
  20 + /**
  21 + * 渠道编号
  22 + */
  23 + @NotNull(message = "短信渠道 ID 不能为空")
  24 + private Long id;
  25 + /**
  26 + * 短信签名
  27 + */
  28 + @NotEmpty(message = "短信签名不能为空")
  29 + private String signature;
  30 + /**
  31 + * 渠道编码
  32 + *
  33 + * 枚举 {@link MailChannelEnum}
  34 + */
  35 + @NotEmpty(message = "渠道编码不能为空")
  36 + private String code;
  37 + /**
  38 + * 短信 API 的账号
  39 + */
  40 + @NotEmpty(message = "短信 API 的账号不能为空")
  41 + private String apiKey;
  42 + /**
  43 + * 短信 API 的秘钥
  44 + */
  45 + @NotEmpty(message = "短信 API 的秘钥不能为空")
  46 + private String apiSecret;
  47 + /**
  48 + * 短信发送回调 URL
  49 + */
  50 + private String callbackUrl;
  51 +
  52 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.mail;
  2 +
  3 +public interface MailSendApi {
  4 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.mail.dto;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.Email;
  8 +import javax.validation.constraints.NotNull;
  9 +import java.util.List;
  10 +
  11 +@ApiModel("管理后台 - 邮件发送 Req VO")
  12 +@Data
  13 +public class MailSendReqDTO { // TODO @wangjingqi:1), 不用空格;2)应该只要传递 templateCode、参数就好,title、from、content、附件应该都是参数里的
  14 +
  15 + @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
  16 + @NotNull(message = "邮箱账号不能为空")
  17 + @Email(message = "邮箱账号格式错误")
  18 + private String from;
  19 +
  20 + @ApiModelProperty(value = "标题",example = "标题")
  21 + private String title;
  22 +
  23 + @ApiModelProperty(value = "内容",example = "内容")
  24 + private String content;
  25 +
  26 + @ApiModelProperty(value = "邮箱模版id",example = "1024")
  27 + @NotNull(message = "邮箱模版id不能为空")
  28 + private Integer templateId;
  29 +
  30 + @ApiModelProperty(value = "收件人",required = true,example = "yudaoyuanma@123.com")
  31 + @NotNull(message = "收件人不能为空")
  32 + private List<String> tos;
  33 +
  34 + @ApiModelProperty(value = "附件",example = "附件编码")
  35 + private List<String> fileIds;
  36 +
  37 +
  38 +}
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 { @@ -126,5 +126,6 @@ public interface ErrorCodeConstants {
126 // ========== 邮箱模版 1002020000 ========== 126 // ========== 邮箱模版 1002020000 ==========
127 ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002020000 , "邮箱模版不存在"); 127 ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002020000 , "邮箱模版不存在");
128 ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002020001, "邮箱模版存在"); 128 ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002020001, "邮箱模版存在");
  129 + ErrorCode MAIL_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002020002, "存在关联邮箱模版");
129 130
130 } 131 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.enums.mail;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 邮件的发送状态枚举
  8 + *
  9 + * @author wangjingyi
  10 + * @date 2022/4/10 13:39
  11 + */
  12 +@Getter
  13 +@AllArgsConstructor
  14 +public enum MailSendStatusEnum {
  15 +
  16 + INIT(0), // 初始化
  17 + SUCCESS(10), // 发送成功
  18 + FAILURE(20), // 发送失败
  19 + IGNORE(30), // 忽略,即不发送
  20 + ;
  21 +
  22 + private final int status;
  23 +
  24 +}
yudao-module-system/yudao-module-system-impl/pom.xml
@@ -49,6 +49,10 @@ @@ -49,6 +49,10 @@
49 </dependency> 49 </dependency>
50 <dependency> 50 <dependency>
51 <groupId>cn.iocoder.boot</groupId> 51 <groupId>cn.iocoder.boot</groupId>
  52 + <artifactId>yudao-spring-boot-starter-biz-mail</artifactId>
  53 + </dependency>
  54 + <dependency>
  55 + <groupId>cn.iocoder.boot</groupId>
52 <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId> 56 <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
53 </dependency> 57 </dependency>
54 <dependency> 58 <dependency>
@@ -102,6 +106,7 @@ @@ -102,6 +106,7 @@
102 <artifactId>yudao-spring-boot-starter-excel</artifactId> 106 <artifactId>yudao-spring-boot-starter-excel</artifactId>
103 </dependency> 107 </dependency>
104 108
  109 +
105 </dependencies> 110 </dependencies>
106 111
107 </project> 112 </project>
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.mail;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import org.springframework.validation.annotation.Validated;
  5 +
  6 +/**
  7 + * 邮件发送 API 接口
  8 + *
  9 + * @author wangjingyi
  10 + */
  11 +@Service
  12 +@Validated
  13 +public class MailSendApiImpl implements MailSendApi{
  14 +}
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; @@ -4,28 +4,34 @@ import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 5 import lombok.Data;
6 6
  7 +import javax.validation.constraints.Email;
7 import javax.validation.constraints.NotNull; 8 import javax.validation.constraints.NotNull;
8 9
9 @ApiModel("管理后台 - 邮箱账号基类 Base VO") 10 @ApiModel("管理后台 - 邮箱账号基类 Base VO")
10 @Data 11 @Data
11 -public class MailAccountBaseVO { // TODO @wangjingqi:1), 不用空格;2)from、username、password、host、sslEnable 都要参数校验,非空;3)username 要 Email 格式;port Integer; 12 +public class MailAccountBaseVO {
12 13
13 - @ApiModelProperty(value = "邮箱" , required = true , example = "yudaoyuanma@123.com") 14 + @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
  15 + @NotNull(message = "邮箱必填")
14 private String from; 16 private String from;
15 17
16 - @ApiModelProperty(value = "用户名" , required = true , example = "yudao") 18 + @ApiModelProperty(value = "用户名",required = true,example = "yudao")
17 @NotNull(message = "用户名必填") 19 @NotNull(message = "用户名必填")
  20 + @Email(message = "必须是Email格式")
18 private String username; 21 private String username;
19 22
20 - @ApiModelProperty(value = "密码" , required = true , example = "123456") 23 + @ApiModelProperty(value = "密码",required = true,example = "123456")
  24 + @NotNull(message = "密码必填")
21 private String password; 25 private String password;
22 26
23 - @ApiModelProperty(value = "网站" , required = true , example = "www.iocoder.cn") 27 + @ApiModelProperty(value = "网站",required = true,example = "www.iocoder.cn")
  28 + @NotNull(message = "网站必填")
24 private String host; 29 private String host;
25 30
26 - @ApiModelProperty(value = "端口" , required = true , example = "80")  
27 - private String port; 31 + @ApiModelProperty(value = "端口",required = true,example = "80")
  32 + private Integer port;
28 33
29 - @ApiModelProperty(value = "是否开启ssl" , required = true , example = "2") 34 + @ApiModelProperty(value = "是否开启ssl",required = true,example = "2")
  35 + @NotNull(message = "是否开启ssl必填")
30 private Boolean sslEnable; 36 private Boolean sslEnable;
31 } 37 }
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; @@ -6,6 +6,7 @@ import lombok.Data;
6 import org.springframework.format.annotation.DateTimeFormat; 6 import org.springframework.format.annotation.DateTimeFormat;
7 7
8 import java.sql.Timestamp; 8 import java.sql.Timestamp;
  9 +import java.util.Date;
9 10
10 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 11 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
11 12
@@ -30,10 +31,10 @@ public class MailLogExcelVO { @@ -30,10 +31,10 @@ public class MailLogExcelVO {
30 31
31 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 32 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
32 @ExcelProperty(value = "发送时间" ) 33 @ExcelProperty(value = "发送时间" )
33 - private Timestamp sendTime; 34 + private Date sendTime;
34 35
35 @ExcelProperty(value = "发送状态") 36 @ExcelProperty(value = "发送状态")
36 - private Boolean sendStatus; 37 + private Integer sendStatus;
37 38
38 @ExcelProperty(value = "发送结果") 39 @ExcelProperty(value = "发送结果")
39 private String sendResult; 40 private String sendResult;
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 { @@ -36,7 +36,7 @@ public class MailLogRespVO {
36 private Timestamp sendTime; 36 private Timestamp sendTime;
37 37
38 @ApiModelProperty(value = "发送状态" , required = false , example = "1") 38 @ApiModelProperty(value = "发送状态" , required = false , example = "1")
39 - private Boolean sendStatus; 39 + private Integer sendStatus;
40 40
41 @ApiModelProperty(value = "发送结果" , required = false , example = "yudaoyuanma@123.com") 41 @ApiModelProperty(value = "发送结果" , required = false , example = "yudaoyuanma@123.com")
42 private String sendResult; 42 private String sendResult;
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; @@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 5 import lombok.Data;
6 6
  7 +import javax.validation.constraints.Email;
7 import javax.validation.constraints.NotNull; 8 import javax.validation.constraints.NotNull;
8 import java.util.List; 9 import java.util.List;
9 10
@@ -11,25 +12,26 @@ import java.util.List; @@ -11,25 +12,26 @@ import java.util.List;
11 @Data 12 @Data
12 public class MailReqVO { // TODO @wangjingqi:1), 不用空格;2)应该只要传递 templateCode、参数就好,title、from、content、附件应该都是参数里的 13 public class MailReqVO { // TODO @wangjingqi:1), 不用空格;2)应该只要传递 templateCode、参数就好,title、from、content、附件应该都是参数里的
13 14
14 - @ApiModelProperty(value = "邮箱" , required = true , example = "yudaoyuanma@123.com") 15 + @ApiModelProperty(value = "邮箱",required = true,example = "yudaoyuanma@123.com")
15 @NotNull(message = "邮箱账号不能为空") 16 @NotNull(message = "邮箱账号不能为空")
  17 + @Email(message = "邮箱账号格式错误")
16 private String from; 18 private String from;
17 19
18 - @ApiModelProperty(value = "标题" , example = "标题") 20 + @ApiModelProperty(value = "标题",example = "标题")
19 private String title; 21 private String title;
20 22
21 - @ApiModelProperty(value = "内容" , example = "内容") 23 + @ApiModelProperty(value = "内容",example = "内容")
22 private String content; 24 private String content;
23 25
24 - @ApiModelProperty(value = "邮箱模版id" , example = "1024") 26 + @ApiModelProperty(value = "邮箱模版id",example = "1024")
25 @NotNull(message = "邮箱模版id不能为空") 27 @NotNull(message = "邮箱模版id不能为空")
26 private Integer templateId; 28 private Integer templateId;
27 29
28 - @ApiModelProperty(value = "收件人" , required = true , example = "yudaoyuanma@123.com") 30 + @ApiModelProperty(value = "收件人",required = true,example = "yudaoyuanma@123.com")
29 @NotNull(message = "收件人不能为空") 31 @NotNull(message = "收件人不能为空")
30 private List<String> tos; 32 private List<String> tos;
31 33
32 - @ApiModelProperty(value = "附件" , example = "附件编码") 34 + @ApiModelProperty(value = "附件",example = "附件编码")
33 private List<String> fileIds; 35 private List<String> fileIds;
34 36
35 37
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; @@ -4,15 +4,18 @@ import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 5 import lombok.Data;
6 6
  7 +import javax.validation.constraints.Email;
7 import javax.validation.constraints.NotNull; 8 import javax.validation.constraints.NotNull;
8 9
9 @ApiModel("管ç†åŽå° - 邮箱模版基类 Base VO") 10 @ApiModel("管ç†åŽå° - 邮箱模版基类 Base VO")
10 @Data 11 @Data
11 -public class MailTemplateBaseVO { // TODO @wangjingqi:1)swagger 注解ä¸å®Œå–„ï¼›2)idã€nameã€codeã€usernameã€titleã€contentã€status æ˜¯ä¸æ˜¯è¦å‚数校验呀 12 +public class MailTemplateBaseVO {
12 @ApiModelProperty("主键") 13 @ApiModelProperty("主键")
  14 + @NotNull(message = "主键ä¸èƒ½ä¸ºç©º")
13 private Long id; 15 private Long id;
14 16
15 @ApiModelProperty("åç§°") 17 @ApiModelProperty("åç§°")
  18 + @NotNull(message = "åç§°ä¸èƒ½ä¸ºç©º")
16 private String name; 19 private String name;
17 20
18 @ApiModelProperty("标识") 21 @ApiModelProperty("标识")
@@ -20,16 +23,21 @@ public class MailTemplateBaseVO { // TODO @wangjingqi:1)swagger 注解ä¸å® @@ -20,16 +23,21 @@ public class MailTemplateBaseVO { // TODO @wangjingqi:1)swagger 注解ä¸å®
20 private String code; 23 private String code;
21 24
22 @ApiModelProperty("å‘件人") 25 @ApiModelProperty("å‘件人")
  26 + @NotNull(message = "å‘件人ä¸èƒ½ä¸ºç©º")
  27 + @Email(message = "å‘ä»¶äººæ ¼å¼æœ‰è¯¯")
23 private String username; 28 private String username;
24 29
25 @ApiModelProperty("标题") 30 @ApiModelProperty("标题")
  31 + @NotNull(message = "标题ä¸èƒ½ä¸ºç©º")
26 private String title; 32 private String title;
27 33
28 @ApiModelProperty("内容") 34 @ApiModelProperty("内容")
  35 + @NotNull(message = "内容ä¸èƒ½ä¸ºç©º")
29 private String content; 36 private String content;
30 37
31 @ApiModelProperty("状æ€") 38 @ApiModelProperty("状æ€")
32 - private String status; 39 + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º")
  40 + private Integer status;
33 41
34 @ApiModelProperty("备注") 42 @ApiModelProperty("备注")
35 private String remark; 43 private String remark;
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 { @@ -31,7 +31,7 @@ public class MailTemplatePageReqVO extends PageParam {
31 private String content; 31 private String content;
32 32
33 @ApiModelProperty("状态") 33 @ApiModelProperty("状态")
34 - private String status; 34 + private Integer status;
35 35
36 @ApiModelProperty("备注") 36 @ApiModelProperty("备注")
37 private String remark; 37 private String remark;
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java
1 package cn.iocoder.yudao.module.system.dal.dataobject.mail; 1 package cn.iocoder.yudao.module.system.dal.dataobject.mail;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
4 import com.baomidou.mybatisplus.annotation.TableName; 5 import com.baomidou.mybatisplus.annotation.TableName;
5 -import lombok.Data;  
6 -import lombok.EqualsAndHashCode; 6 +import lombok.*;
7 7
8 import java.io.Serializable; 8 import java.io.Serializable;
9 -import java.sql.Timestamp; 9 +import java.util.Date;
10 10
11 /** 11 /**
12 * 邮箱日志 12 * 邮箱日志
@@ -18,6 +18,10 @@ import java.sql.Timestamp; @@ -18,6 +18,10 @@ import java.sql.Timestamp;
18 @TableName(value = "system_mail_log", autoResultMap = true) 18 @TableName(value = "system_mail_log", autoResultMap = true)
19 @Data 19 @Data
20 @EqualsAndHashCode(callSuper = true) 20 @EqualsAndHashCode(callSuper = true)
  21 +@ToString(callSuper = true)
  22 +@Builder
  23 +@AllArgsConstructor
  24 +@NoArgsConstructor
21 public class MailLogDO extends BaseDO implements Serializable { 25 public class MailLogDO extends BaseDO implements Serializable {
22 26
23 /** 27 /**
@@ -29,7 +33,7 @@ public class MailLogDO extends BaseDO implements Serializable { @@ -29,7 +33,7 @@ public class MailLogDO extends BaseDO implements Serializable {
29 /** 33 /**
30 * 邮箱账号编号 34 * 邮箱账号编号
31 */ 35 */
32 - private String accountCode; 36 + private Long accountId;
33 37
34 // TODO @wangjingyi:如果是冗余字段,记得 @ 下; 38 // TODO @wangjingyi:如果是冗余字段,记得 @ 下;
35 /** 39 /**
@@ -40,7 +44,7 @@ public class MailLogDO extends BaseDO implements Serializable { @@ -40,7 +44,7 @@ public class MailLogDO extends BaseDO implements Serializable {
40 /** 44 /**
41 * 模版主键 45 * 模版主键
42 */ 46 */
43 - private String templateId; 47 + private Long templateId;
44 48
45 /** 49 /**
46 * 模版编号 50 * 模版编号
@@ -65,16 +69,18 @@ public class MailLogDO extends BaseDO implements Serializable { @@ -65,16 +69,18 @@ public class MailLogDO extends BaseDO implements Serializable {
65 /** 69 /**
66 * 发送时间 70 * 发送时间
67 */ 71 */
68 - private Timestamp sendTime; 72 + private Date sendTime;
69 73
70 /** 74 /**
71 * 发送状态 75 * 发送状态
  76 + *
  77 + * 枚举 {@link MailSendStatusEnum}
72 */ 78 */
73 - // TODO @wangjingyi:四个状态,参考短信模块  
74 - private Boolean sendStatus; 79 + private Integer sendStatus;
75 80
76 /** 81 /**
77 * 发送结果 82 * 发送结果
  83 + *
78 */ 84 */
79 private String sendResult; 85 private String sendResult;
80 86
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; @@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
6 import lombok.Data; 6 import lombok.Data;
7 import lombok.EqualsAndHashCode; 7 import lombok.EqualsAndHashCode;
8 8
  9 +import java.util.Date;
  10 +
9 /** 11 /**
10 * 邮箱模版 12 * 邮箱模版
11 * 13 *
@@ -29,11 +31,10 @@ public class MailTemplateDO extends BaseDO { @@ -29,11 +31,10 @@ public class MailTemplateDO extends BaseDO {
29 * 模版编号 31 * 模版编号
30 */ 32 */
31 private String code; 33 private String code;
32 - // TODO @wangjingyi:应该使用 accountId 呀  
33 /** 34 /**
34 - * 用户名 35 + * 邮箱账号主键
35 */ 36 */
36 - private String username; 37 + private Long accountId;
37 /** 38 /**
38 * 标题 39 * 标题
39 */ 40 */
@@ -47,7 +48,7 @@ public class MailTemplateDO extends BaseDO { @@ -47,7 +48,7 @@ public class MailTemplateDO extends BaseDO {
47 * 48 *
48 * 枚举 {@link CommonStatusEnum} 49 * 枚举 {@link CommonStatusEnum}
49 */ 50 */
50 - private String status; // TODO @wangjingyi:Integer 51 + private Integer status;
51 /** 52 /**
52 * 备注 53 * 备注
53 */ 54 */
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; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
8 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
9 10
10 @Mapper 11 @Mapper
@@ -21,9 +22,7 @@ public interface MailAccountMapper extends BaseMapperX&lt;MailAccountDO&gt; { @@ -21,9 +22,7 @@ public interface MailAccountMapper extends BaseMapperX&lt;MailAccountDO&gt; {
21 } 22 }
22 23
23 default MailAccountDO selectByUserName(String userName){ 24 default MailAccountDO selectByUserName(String userName){
24 - // TODO @wangjingyi:selectOne 有封装的方法;然后,编码一定要学会使用泛型呀。例如说 QueryWrapperX<MailAccountDO> queryWrapperX = new QueryWrapperX<>();  
25 - QueryWrapperX<MailAccountDO> queryWrapperX = new QueryWrapperX<>();  
26 - queryWrapperX.eqIfPresent("username", userName);  
27 - return this.selectOne(queryWrapperX); 25 + return selectOne(new QueryWrapperX<MailAccountDO>()
  26 + .eqIfPresent("username" , userName));
28 }; 27 };
29 } 28 }
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; @@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Select;
  10 +
  11 +import java.util.Date;
9 12
10 13
11 @Mapper 14 @Mapper
@@ -23,7 +26,15 @@ public interface MailTemplateMapper extends BaseMapperX&lt;MailTemplateDO&gt; { @@ -23,7 +26,15 @@ public interface MailTemplateMapper extends BaseMapperX&lt;MailTemplateDO&gt; {
23 } 26 }
24 27
25 default MailTemplateDO selectOneByCode(String code){ 28 default MailTemplateDO selectOneByCode(String code){
26 - // TODO @wangjingyi:优先使用 lambada 查询  
27 - return selectOne("code" , code); 29 + return selectOne(new QueryWrapperX<MailTemplateDO>()
  30 + .eqIfPresent("code" , code));
  31 + };
  32 +
  33 + @Select("SELECT id FROM system_mail_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
  34 + Long selectByMaxUpdateTime(Date maxUpdateTime);
  35 +
  36 + default MailTemplateDO selectOneByAccountId(Long accountId){
  37 + return selectOne(new QueryWrapperX<MailTemplateDO>()
  38 + .eqIfPresent("account_id" , accountId));
28 }; 39 };
29 } 40 }
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; @@ -2,17 +2,23 @@ package cn.iocoder.yudao.module.system.mq.consumer.mail;
2 2
3 import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; 3 import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
4 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; 4 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  5 +import cn.iocoder.yudao.module.system.service.mail.MailSendService;
5 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
6 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
7 8
8 -// TODO 芋艿:这个暂未实现 9 +import javax.annotation.Resource;
  10 +
  11 +
9 @Component 12 @Component
10 @Slf4j 13 @Slf4j
11 public class MailSendConsumer extends AbstractStreamMessageListener<MailSendMessage> { 14 public class MailSendConsumer extends AbstractStreamMessageListener<MailSendMessage> {
12 15
  16 + @Resource
  17 + private MailSendService mailSendService;
13 @Override 18 @Override
14 public void onMessage(MailSendMessage message) { 19 public void onMessage(MailSendMessage message) {
15 log.info("[onMessage][消息内容({})]", message); 20 log.info("[onMessage][消息内容({})]", message);
  21 + mailSendService.doSendMail(message);
16 } 22 }
17 23
18 } 24 }
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; @@ -5,6 +5,7 @@ import lombok.Data;
5 import lombok.EqualsAndHashCode; 5 import lombok.EqualsAndHashCode;
6 6
7 import javax.validation.constraints.NotNull; 7 import javax.validation.constraints.NotNull;
  8 +import java.util.List;
8 import java.util.Map; 9 import java.util.Map;
9 10
10 /** 11 /**
@@ -17,28 +18,33 @@ import java.util.Map; @@ -17,28 +18,33 @@ import java.util.Map;
17 public class MailSendMessage extends AbstractStreamMessage { 18 public class MailSendMessage extends AbstractStreamMessage {
18 19
19 /** 20 /**
  21 + * 日志id
  22 + */
  23 + @NotNull(message = "邮箱日志id不能为空")
  24 + private Long logId;
  25 + /**
20 * 邮箱地址 26 * 邮箱地址
21 */ 27 */
22 @NotNull(message = "邮箱地址不能为空") 28 @NotNull(message = "邮箱地址不能为空")
23 - private String address; 29 + private String from;
24 /** 30 /**
25 - * 短信模板编号 31 + * 邮箱模板编号
26 */ 32 */
27 - @NotNull(message = "短信模板编号不能为空") 33 + @NotNull(message = "邮箱模板编号不能为空")
28 private String templateCode; 34 private String templateCode;
29 /** 35 /**
30 - * 短信模板参数 36 + * 收件人
31 */ 37 */
32 - private Map<String, Object> templateParams;  
33 - 38 + @NotNull(message = "收件人不能为空")
  39 + private List<String> tos;
34 /** 40 /**
35 - * 用户编号,允许空 41 + * 标题
36 */ 42 */
37 - private Integer userId; 43 + private String title;
38 /** 44 /**
39 - * 用户类型,允许空 45 + * 内容
40 */ 46 */
41 - private Integer userType; 47 + private String content;
42 48
43 @Override 49 @Override
44 public String getStreamKey() { 50 public String getStreamKey() {
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.producer.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  4 +import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  6 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  7 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  8 +import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;
  9 +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
  10 +import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import javax.annotation.Resource;
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * Mail 邮件相关消息的 Producer
  19 + *
  20 + * @author wangjingyi
  21 + * @date 2021/4/19 13:33
  22 + */
  23 +@Slf4j
  24 +@Component
  25 +public class MailProducer {
  26 +
  27 + @Resource
  28 + private RedisMQTemplate redisMQTemplate;
  29 +
  30 + /**
  31 + * 发送 {@link SmsChannelRefreshMessage} 消息
  32 + */
  33 + public void sendMailChannelRefreshMessage() {
  34 + SmsChannelRefreshMessage message = new SmsChannelRefreshMessage();
  35 + redisMQTemplate.send(message);
  36 + }
  37 +
  38 + /**
  39 + * 发送 {@link SmsTemplateRefreshMessage} 消息
  40 + */
  41 + public void sendMailTemplateRefreshMessage() {
  42 + SmsTemplateRefreshMessage message = new SmsTemplateRefreshMessage();
  43 + redisMQTemplate.send(message);
  44 + }
  45 +
  46 + /**
  47 + * 发送 {@link MailSendMessage} 消息
  48 + *
  49 + * @param mailAccountDO 邮箱账号信息
  50 + * @param mailTemplateDO 邮箱模版信息
  51 + * @param content 内容
  52 + * @param tos 收件人
  53 + * @param title 标题
  54 + */
  55 + public void sendMailSendMessage(MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String content, List<String> tos, String title , Long sendLogId) {
  56 + MailSendMessage message = new MailSendMessage();
  57 + message.setContent(content);
  58 + message.setFrom(mailAccountDO.getFrom());
  59 + message.setTemplateCode(mailTemplateDO.getCode());
  60 + message.setTitle(title);
  61 + message.setTos(tos);
  62 + message.setLogId(sendLogId);
  63 + redisMQTemplate.send(message);
  64 + }
  65 +}
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; @@ -4,7 +4,9 @@ package cn.iocoder.yudao.module.system.service.mail;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogExportReqVO; 5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogExportReqVO;
6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
8 10
9 import java.util.List; 11 import java.util.List;
10 12
@@ -28,4 +30,18 @@ public interface MailLogService { @@ -28,4 +30,18 @@ public interface MailLogService {
28 * @return 30 * @return
29 */ 31 */
30 List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO); 32 List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO);
  33 +
  34 + /**
  35 + * 创建邮箱日志
  36 + * @param mailAccountDO 邮箱账号信息
  37 + * @param mailTemplateDO 模版信息
  38 + * @param from 邮箱
  39 + * @param content 内容
  40 + * @param tos 收件人
  41 + * @param title 标题
  42 + * @param isSend 是否发送成功
  43 + */
  44 + Long createMailLog(MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String from, String content, List<String> tos, String title, Boolean isSend);
  45 +
  46 + Long updateSmsSendResult(Long logId, String result);
31 } 47 }
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  9 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  10 +
  11 +import javax.validation.Valid;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +/**
  16 + * 邮箱模版服务类
  17 + *
  18 + * @author wangjingyi
  19 + * @since 2022-03-21
  20 + */
  21 +public interface MailSendService {
  22 +
  23 + /**
  24 + * 发送邮件
  25 + *
  26 + * @param templateCode 邮件模版编码
  27 + * @param from 邮箱
  28 + * @param content 内容
  29 + * @param tos 收件人
  30 + * @param title 标题
  31 + */
  32 + void sendMail(String templateCode, String from , String content , List<String> tos , String title);
  33 +
  34 + /**
  35 + * 执行真正的邮件发送
  36 + * 注意,该方法仅仅提供给 MQ Consumer 使用
  37 + *
  38 + * @param message 邮件
  39 + */
  40 + void doSendMail(MailSendMessage message);
  41 +}
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; @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
9 9
10 import javax.validation.Valid; 10 import javax.validation.Valid;
11 import java.util.List; 11 import java.util.List;
  12 +import java.util.Map;
12 13
13 /** 14 /**
14 * 邮箱模版服务类 15 * 邮箱模版服务类
@@ -18,6 +19,7 @@ import java.util.List; @@ -18,6 +19,7 @@ import java.util.List;
18 */ 19 */
19 public interface MailTemplateService { 20 public interface MailTemplateService {
20 21
  22 + void initLocalCache();
21 /** 23 /**
22 * 邮箱模版创建 24 * 邮箱模版创建
23 * 25 *
@@ -62,6 +64,13 @@ public interface MailTemplateService { @@ -62,6 +64,13 @@ public interface MailTemplateService {
62 * @return 模版数组 64 * @return 模版数组
63 */ 65 */
64 List<MailTemplateDO> getMailTemplateList(); 66 List<MailTemplateDO> getMailTemplateList();
  67 + /**
  68 + *从缓存中获取邮箱模版
  69 + *
  70 + * @param code 模板编码
  71 + * @return 邮箱模板
  72 + */
  73 + MailTemplateDO getMailTemplateByCodeFromCache(String code);
65 74
66 /** 75 /**
67 * 发送邮件 76 * 发送邮件
@@ -69,4 +78,12 @@ public interface MailTemplateService { @@ -69,4 +78,12 @@ public interface MailTemplateService {
69 * @param mailReqVO 邮件发送信息 78 * @param mailReqVO 邮件发送信息
70 */ 79 */
71 void sendMail(MailReqVO mailReqVO); 80 void sendMail(MailReqVO mailReqVO);
  81 +
  82 + /**
  83 + * 邮件模版内容合成
  84 + * @param content 邮箱模版
  85 + * @param params 合成参数
  86 + * @return
  87 + */
  88 + String formateMailTemplateContent(String content, Map<String, String> params);
72 } 89 }
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 @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou
6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
7 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert; 7 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
9 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; 10 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
10 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; 11 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
11 import cn.iocoder.yudao.module.system.service.mail.MailAccountService; 12 import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
@@ -16,8 +17,7 @@ import javax.annotation.Resource; @@ -16,8 +17,7 @@ import javax.annotation.Resource;
16 import java.util.List; 17 import java.util.List;
17 18
18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
19 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_EXISTS;  
20 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; 20 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
21 21
22 22
23 /** 23 /**
@@ -47,8 +47,8 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -47,8 +47,8 @@ public class MailAccountServiceImpl implements MailAccountService {
47 47
48 @Override 48 @Override
49 public void update(MailAccountUpdateReqVO updateReqVO) { 49 public void update(MailAccountUpdateReqVO updateReqVO) {
50 - // username 要校验唯一 // TODO @wangjingyi:更新的就是自己,username 这样写,会重复呀。  
51 - this.validateMailAccountOnlyByUserName(updateReqVO.getUsername()); 50 + // username 要校验唯一
  51 + this.validateMailAccountExists(updateReqVO.getId());
52 MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO); 52 MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO);
53 // 校验是否存在 53 // 校验是否存在
54 this.validateMailAccountExists(mailAccountDO.getId()); 54 this.validateMailAccountExists(mailAccountDO.getId());
@@ -57,9 +57,10 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -57,9 +57,10 @@ public class MailAccountServiceImpl implements MailAccountService {
57 57
58 @Override 58 @Override
59 public void delete(Long id) { 59 public void delete(Long id) {
60 - // TODO @wangjingyi:删除时,要判断是否有使用的模板  
61 - // 校验是否存在 60 + // 校验是否存在账号
62 this.validateMailAccountExists(id); 61 this.validateMailAccountExists(id);
  62 + // 校验是否存在关联模版
  63 + this.validateMailTemplateByAccountId(id);
63 mailAccountMapper.deleteById(id); 64 mailAccountMapper.deleteById(id);
64 } 65 }
65 66
@@ -90,4 +91,11 @@ public class MailAccountServiceImpl implements MailAccountService { @@ -90,4 +91,11 @@ public class MailAccountServiceImpl implements MailAccountService {
90 throw exception(MAIL_ACCOUNT_EXISTS); 91 throw exception(MAIL_ACCOUNT_EXISTS);
91 } 92 }
92 } 93 }
  94 +
  95 + private void validateMailTemplateByAccountId(Long accountId){
  96 + MailTemplateDO mailTemplateDO = mailTemplateMapper.selectOneByAccountId(accountId);
  97 + if (mailTemplateDO != null) {
  98 + throw exception(MAIL_RELATE_TEMPLATE_EXISTS);
  99 + }
  100 + }
93 } 101 }
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; @@ -4,14 +4,21 @@ package cn.iocoder.yudao.module.system.service.mail.impl;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogExportReqVO; 5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogExportReqVO;
6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
8 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; 10 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
  11 +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
  12 +import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
9 import cn.iocoder.yudao.module.system.service.mail.MailLogService; 13 import cn.iocoder.yudao.module.system.service.mail.MailLogService;
10 import org.springframework.beans.factory.annotation.Autowired; 14 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
12 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
13 17
  18 +import java.sql.Timestamp;
  19 +import java.util.Date;
14 import java.util.List; 20 import java.util.List;
  21 +import java.util.Objects;
15 22
16 /** 23 /**
17 * 邮箱日志实现类 24 * 邮箱日志实现类
@@ -35,4 +42,39 @@ public class MailLogServiceImpl implements MailLogService { @@ -35,4 +42,39 @@ public class MailLogServiceImpl implements MailLogService {
35 public List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO) { 42 public List<MailLogDO> getMailLogList(MailLogExportReqVO exportReqVO) {
36 return mailLogMapper.selectList(exportReqVO); 43 return mailLogMapper.selectList(exportReqVO);
37 } 44 }
  45 +
  46 + @Override
  47 + public Long createMailLog(MailAccountDO mailAccountDO , MailTemplateDO mailTemplateDO , String from, String content, List<String> tos, String title, Boolean isSend) {
  48 + MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
  49 + logDOBuilder.from(mailAccountDO.getFrom());
  50 + logDOBuilder.accountId(mailAccountDO.getId());
  51 + logDOBuilder.content(content);
  52 + logDOBuilder.title(title);
  53 + logDOBuilder.templateCode(mailTemplateDO.getCode());
  54 + logDOBuilder.templateId(mailTemplateDO.getId());
  55 + logDOBuilder.to(tos.toString());
  56 + logDOBuilder.sendTime(new Date());
  57 + logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()
  58 + : MailSendStatusEnum.IGNORE.getStatus());
  59 +
  60 + MailLogDO mailLogDO = logDOBuilder.build();
  61 + mailLogMapper.insert(mailLogDO);
  62 + return mailLogDO.getId();
  63 + }
  64 +
  65 + @Override
  66 + public Long updateSmsSendResult(Long logId, String result) {
  67 + MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
  68 + logDOBuilder.id(logId);
  69 + logDOBuilder.sendResult(result);
  70 + MailLogDO mailLogDO = logDOBuilder.build();
  71 + mailLogMapper.updateById(mailLogDO);
  72 + return logId;
  73 + }
  74 +
  75 + public Long create(){
  76 + MailLogDO mailLogDO = new MailLogDO();
  77 + mailLogMapper.insert(mailLogDO);
  78 + return mailLogDO.getId();
  79 + }
38 } 80 }
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail.impl;
  2 +
  3 +
  4 +import cn.hutool.extra.mail.MailAccount;
  5 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  6 +import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  9 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
  10 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
  11 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  12 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  13 +import cn.iocoder.yudao.module.system.service.mail.MailLogService;
  14 +import cn.iocoder.yudao.module.system.service.mail.MailSendService;
  15 +import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
  16 +import lombok.extern.slf4j.Slf4j;
  17 +import cn.iocoder.yudao.framework.mail.core.client.MailClientFactory;
  18 +import org.springframework.stereotype.Service;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import cn.iocoder.yudao.framework.mail.core.client.MailClient;
  21 +
  22 +import javax.annotation.Resource;
  23 +import java.util.List;
  24 +import java.util.Map;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  27 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_EXISTS;
  28 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
  29 +
  30 +/**
  31 + * 邮箱模版 服务实现类
  32 + *
  33 + * @author wangjingyi
  34 + * @since 2022-03-21
  35 + */
  36 +@Service
  37 +@Validated
  38 +@Slf4j
  39 +public class MailSendServiceImpl implements MailSendService {
  40 +
  41 + @Resource
  42 + private MailTemplateMapper mailTemplateMapper;
  43 + @Resource
  44 + private MailAccountMapper mailAccountMapper;
  45 + @Resource
  46 + private MailTemplateService mailTemplateService;
  47 + @Resource
  48 + private MailLogService mailLogService;
  49 + @Resource
  50 + private MailClientFactory mailClientFactory;
  51 + @Resource
  52 + private MailProducer mailProducer;
  53 +
  54 +
  55 + @Override
  56 + public void sendMail(String templateCode, String from , String content , List<String> tos , String title) {
  57 + // TODO @@wangjingyi:发送的时候,参考下短信;
  58 + MailTemplateDO mailTemplateDO = this.checkMailTemplateValid(templateCode);
  59 + // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
  60 + Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(mailTemplateDO.getStatus());
  61 + //查询账号信息
  62 + MailAccountDO mailAccountDO = mailAccountMapper.selectOne(
  63 + "from", from
  64 + );
  65 + Map<String , String> params = MailAccountConvert.INSTANCE.convertToMap(mailAccountDO , content);
  66 + content = mailTemplateService.formateMailTemplateContent(mailTemplateDO.getContent(), params);
  67 + Long sendLogId = mailLogService.createMailLog(mailAccountDO , mailTemplateDO , from , content , tos , title , isSend);
  68 +
  69 + // 后续功能 TODO :附件查询
  70 + //List<String> fileIds = mailSendVO.getFileIds();
  71 +
  72 + //装载账号信息
  73 + MailAccount account = MailAccountConvert.INSTANCE.convertAccount(mailAccountDO);
  74 +
  75 + // 发送 MQ 消息,异步执行发送短信
  76 + if (isSend) {
  77 + mailProducer.sendMailSendMessage(mailAccountDO , mailTemplateDO ,content , tos , title , sendLogId);
  78 + }
  79 + }
  80 +
  81 + @Override
  82 + public void doSendMail(MailSendMessage message) {
  83 + MailClient mailClient = mailClientFactory.getMailClient();
  84 + String result = mailClient.sendMail(message.getFrom() , message.getContent() , message.getTitle() , message.getTos());
  85 + mailLogService.updateSmsSendResult(message.getLogId() , result);
  86 + }
  87 +
  88 + private MailTemplateDO checkMailTemplateValid(String templateCode) {
  89 + MailTemplateDO mailTemplateDO = mailTemplateService.getMailTemplateByCodeFromCache(templateCode);
  90 + if (mailTemplateDO == null){
  91 + throw exception(MAIL_TEMPLATE_NOT_EXISTS);
  92 + }
  93 + return mailTemplateDO;
  94 + }
  95 +
  96 + private void validateMailTemplateExists(Long id) {
  97 + if (mailTemplateMapper.selectById(id) == null) {
  98 + throw exception(MAIL_TEMPLATE_NOT_EXISTS);
  99 + }
  100 + }
  101 +
  102 + private void validateMailTemplateOnlyByCode(String code){
  103 + if (mailTemplateMapper.selectOneByCode(code) != null) {
  104 + throw exception(MAIL_TEMPLATE_EXISTS);
  105 + }
  106 + }
  107 +}
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.mail.impl; 1 package cn.iocoder.yudao.module.system.service.mail.impl;
2 2
3 3
  4 +import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.extra.mail.MailAccount; 6 import cn.hutool.extra.mail.MailAccount;
6 import cn.hutool.extra.mail.MailUtil; 7 import cn.hutool.extra.mail.MailUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO; 10 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO; 11 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
10 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; 12 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; @@ -13,14 +15,18 @@ import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
13 import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert; 15 import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
14 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; 16 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
15 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; 17 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  18 +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
16 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; 19 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
17 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; 20 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
18 import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; 21 import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
  22 +import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
20 import org.springframework.validation.annotation.Validated; 24 import org.springframework.validation.annotation.Validated;
21 25
  26 +import javax.annotation.PostConstruct;
22 import javax.annotation.Resource; 27 import javax.annotation.Resource;
23 import javax.validation.Valid; 28 import javax.validation.Valid;
  29 +import java.util.Date;
24 import java.util.List; 30 import java.util.List;
25 import java.util.Map; 31 import java.util.Map;
26 32
@@ -36,6 +42,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPL @@ -36,6 +42,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPL
36 */ 42 */
37 @Service 43 @Service
38 @Validated 44 @Validated
  45 +@Slf4j
39 public class MailTemplateServiceImpl implements MailTemplateService { 46 public class MailTemplateServiceImpl implements MailTemplateService {
40 47
41 @Resource 48 @Resource
@@ -43,6 +50,38 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -43,6 +50,38 @@ public class MailTemplateServiceImpl implements MailTemplateService {
43 @Resource 50 @Resource
44 private MailAccountMapper mailAccountMapper; 51 private MailAccountMapper mailAccountMapper;
45 52
  53 + private volatile List<MailTemplateDO> mailTemplateDOList;
  54 +
  55 + /**
  56 + * 邮件模板缓存
  57 + * key:邮箱模板编码 {@link MailTemplateDO#getCode()}
  58 + *
  59 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  60 + */
  61 + private volatile Map<String, MailTemplateDO> mailTemplateCache;
  62 +
  63 + private volatile Date maxUpdateTime;
  64 +
  65 + @Override
  66 + @PostConstruct
  67 + public void initLocalCache() {
  68 + if(maxUpdateTime == null){
  69 + mailTemplateDOList = mailTemplateMapper.selectList();
  70 + }else{
  71 + if(mailTemplateMapper.selectByMaxUpdateTime(maxUpdateTime)<=0){
  72 + return;
  73 + }
  74 + }
  75 + if (CollUtil.isEmpty(mailTemplateDOList)) {
  76 + return;
  77 + }
  78 +
  79 + // 写入缓存
  80 + mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getCode);
  81 + maxUpdateTime = CollectionUtils.getMaxValue(mailTemplateDOList, MailTemplateDO::getUpdateTime);
  82 + log.info("[initLocalCache][初始化 mailTemplate 数量为 {}]", mailTemplateDOList.size());
  83 + }
  84 +
46 @Override 85 @Override
47 public Long create(MailTemplateCreateReqVO createReqVO) { 86 public Long create(MailTemplateCreateReqVO createReqVO) {
48 // code 要校验唯一 87 // code 要校验唯一
@@ -54,11 +93,9 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -54,11 +93,9 @@ public class MailTemplateServiceImpl implements MailTemplateService {
54 93
55 @Override 94 @Override
56 public void update(@Valid MailTemplateUpdateReqVO updateReqVO) { 95 public void update(@Valid MailTemplateUpdateReqVO updateReqVO) {
57 - // code 要校验唯一  
58 - this.validateMailTemplateOnlyByCode(updateReqVO.getCode()); // TODO @wangjingyi:code 这样写,修改自己会有问题  
59 - MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);  
60 // 校验是否存在 96 // 校验是否存在
61 - this.validateMailTemplateExists(mailTemplateDO.getId()); 97 + this.validateMailTemplateExists(updateReqVO.getId());
  98 + MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
62 mailTemplateMapper.updateById(mailTemplateDO); 99 mailTemplateMapper.updateById(mailTemplateDO);
63 } 100 }
64 101
@@ -81,6 +118,11 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -81,6 +118,11 @@ public class MailTemplateServiceImpl implements MailTemplateService {
81 public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();} 118 public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();}
82 119
83 @Override 120 @Override
  121 + public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
  122 + return mailTemplateCache.get(code);
  123 + }
  124 +
  125 + @Override
84 public void sendMail(MailReqVO mailReqVO) { 126 public void sendMail(MailReqVO mailReqVO) {
85 // TODO @@wangjingyi:发送的时候,参考下短信; 127 // TODO @@wangjingyi:发送的时候,参考下短信;
86 MailTemplateDO mailTemplateDO = mailTemplateMapper.selectById(mailReqVO.getTemplateId()); 128 MailTemplateDO mailTemplateDO = mailTemplateMapper.selectById(mailReqVO.getTemplateId());
@@ -102,6 +144,11 @@ public class MailTemplateServiceImpl implements MailTemplateService { @@ -102,6 +144,11 @@ public class MailTemplateServiceImpl implements MailTemplateService {
102 MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false); 144 MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false);
103 } 145 }
104 146
  147 + @Override
  148 + public String formateMailTemplateContent(String content, Map<String, String> params) {
  149 + return StrUtil.format(content, params);
  150 + }
  151 +
105 private void validateMailTemplateExists(Long id) { 152 private void validateMailTemplateExists(Long id) {
106 if (mailTemplateMapper.selectById(id) == null) { 153 if (mailTemplateMapper.selectById(id) == null) {
107 throw exception(MAIL_TEMPLATE_NOT_EXISTS); 154 throw exception(MAIL_TEMPLATE_NOT_EXISTS);