Commit 93cb8bad066c077d9ddc8de7a9f698e0b48fe52c

Authored by YunaiV
1 parent 82890199

feat:【system 系统功能】邮箱增加抄送、密送,支持多个

Showing 16 changed files with 209 additions and 303 deletions
sql/mysql/ruoyi-vue-pro.sql
@@ -1259,9 +1259,9 @@ CREATE TABLE `system_mail_log` ( @@ -1259,9 +1259,9 @@ CREATE TABLE `system_mail_log` (
1259 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', 1259 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
1260 `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号', 1260 `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号',
1261 `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型', 1261 `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型',
1262 - `to_mail` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收邮箱地址',  
1263 - `cc_mail` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '抄送邮箱地址',  
1264 - `bcc_mail` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密送邮箱地址', 1262 + `to_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收邮箱地址',
  1263 + `cc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '抄送邮箱地址',
  1264 + `bcc_mails` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密送邮箱地址',
1265 `account_id` bigint NOT NULL COMMENT '邮箱账号编号', 1265 `account_id` bigint NOT NULL COMMENT '邮箱账号编号',
1266 `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址', 1266 `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址',
1267 `template_id` bigint NOT NULL COMMENT '模板编号', 1267 `template_id` bigint NOT NULL COMMENT '模板编号',
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java
@@ -21,13 +21,15 @@ public class MailSendApiImpl implements MailSendApi { @@ -21,13 +21,15 @@ public class MailSendApiImpl implements MailSendApi {
21 21
22 @Override 22 @Override
23 public Long sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) { 23 public Long sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) {
24 - return mailSendService.sendSingleMailToAdmin(reqDTO.getMail(), reqDTO.getUserId(), 24 + return mailSendService.sendSingleMailToAdmin(reqDTO.getUserId(),
  25 + reqDTO.getToMails(), reqDTO.getCcMails(), reqDTO.getBccMails(),
25 reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); 26 reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
26 } 27 }
27 28
28 @Override 29 @Override
29 public Long sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) { 30 public Long sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) {
30 - return mailSendService.sendSingleMailToMember(reqDTO.getMail(), reqDTO.getUserId(), 31 + return mailSendService.sendSingleMailToMember(reqDTO.getUserId(),
  32 + reqDTO.getToMails(), reqDTO.getCcMails(), reqDTO.getBccMails(),
31 reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); 33 reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
32 } 34 }
33 35
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java
@@ -4,6 +4,8 @@ import lombok.Data; @@ -4,6 +4,8 @@ import lombok.Data;
4 4
5 import jakarta.validation.constraints.Email; 5 import jakarta.validation.constraints.Email;
6 import jakarta.validation.constraints.NotNull; 6 import jakarta.validation.constraints.NotNull;
  7 +
  8 +import java.util.List;
7 import java.util.Map; 9 import java.util.Map;
8 10
9 /** 11 /**
@@ -16,13 +18,24 @@ public class MailSendSingleToUserReqDTO { @@ -16,13 +18,24 @@ public class MailSendSingleToUserReqDTO {
16 18
17 /** 19 /**
18 * 用户编号 20 * 用户编号
  21 + *
  22 + * 如果非空,则加载对应用户的邮箱,添加到 {@link #toMails} 中
19 */ 23 */
20 private Long userId; 24 private Long userId;
  25 +
  26 + /**
  27 + * 收件邮箱
  28 + */
  29 + private List<@Email String> toMails;
21 /** 30 /**
22 - * 邮箱 31 + * 抄送邮箱
23 */ 32 */
24 - @Email  
25 - private String mail; 33 + private List<@Email String> ccMails;
  34 + /**
  35 + * 密送邮箱
  36 + */
  37 + private List<@Email String> bccMails;
  38 +
26 39
27 /** 40 /**
28 * 邮件模板编号 41 * 邮件模板编号
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java
@@ -91,7 +91,8 @@ public class MailTemplateController { @@ -91,7 +91,8 @@ public class MailTemplateController {
91 @Operation(summary = "发送短信") 91 @Operation(summary = "发送短信")
92 @PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')") 92 @PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')")
93 public CommonResult<Long> sendMail(@Valid @RequestBody MailTemplateSendReqVO sendReqVO) { 93 public CommonResult<Long> sendMail(@Valid @RequestBody MailTemplateSendReqVO sendReqVO) {
94 - return success(mailSendService.sendMultipleMailToAdmin(sendReqVO.getToMails(), sendReqVO.getCcMails(), sendReqVO.getBccMails(), getLoginUserId(), 94 + return success(mailSendService.sendSingleMailToAdmin(getLoginUserId(),
  95 + sendReqVO.getToMails(), sendReqVO.getCcMails(), sendReqVO.getBccMails(),
95 sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); 96 sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
96 } 97 }
97 98
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
  7 +import java.util.List;
7 import java.util.Map; 8 import java.util.Map;
8 9
9 @Schema(description = "管理后台 - 邮件日志 Response VO") 10 @Schema(description = "管理后台 - 邮件日志 Response VO")
@@ -20,13 +21,13 @@ public class MailLogRespVO { @@ -20,13 +21,13 @@ public class MailLogRespVO {
20 private Byte userType; 21 private Byte userType;
21 22
22 @Schema(description = "接收邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user1@example.com, user2@example.com") 23 @Schema(description = "接收邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user1@example.com, user2@example.com")
23 - private String toMail; 24 + private List<String> toMails;
24 25
25 @Schema(description = "抄送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user3@example.com, user4@example.com") 26 @Schema(description = "抄送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user3@example.com, user4@example.com")
26 - private String ccMail; 27 + private List<String> ccMails;
27 28
28 @Schema(description = "密送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user5@example.com, user6@example.com") 29 @Schema(description = "密送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user5@example.com, user6@example.com")
29 - private String bccMail; 30 + private List<String> bccMails;
30 31
31 @Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18107") 32 @Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18107")
32 private Long accountId; 33 private Long accountId;
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.mail; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.mail;
2 2
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
5 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 6 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
6 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; 7 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
7 import com.baomidou.mybatisplus.annotation.KeySequence; 8 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -12,6 +13,7 @@ import lombok.*; @@ -12,6 +13,7 @@ import lombok.*;
12 13
13 import java.io.Serializable; 14 import java.io.Serializable;
14 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
  16 +import java.util.List;
15 import java.util.Map; 17 import java.util.Map;
16 18
17 /** 19 /**
@@ -47,18 +49,23 @@ public class MailLogDO extends BaseDO implements Serializable { @@ -47,18 +49,23 @@ public class MailLogDO extends BaseDO implements Serializable {
47 * 枚举 {@link UserTypeEnum} 49 * 枚举 {@link UserTypeEnum}
48 */ 50 */
49 private Integer userType; 51 private Integer userType;
  52 +
50 /** 53 /**
51 * 接收邮箱地址 54 * 接收邮箱地址
52 */ 55 */
53 - private String toMail; 56 + @TableField(typeHandler = StringListTypeHandler.class)
  57 + private List<String> toMails;
54 /** 58 /**
55 * 接收邮箱地址 59 * 接收邮箱地址
56 */ 60 */
57 - private String ccMail; 61 + @TableField(typeHandler = StringListTypeHandler.class)
  62 + private List<String> ccMails;
58 /** 63 /**
59 - * 接收邮箱地址 64 + * 密送邮箱地址
60 */ 65 */
61 - private String bccMail; 66 + @TableField(typeHandler = StringListTypeHandler.class)
  67 + private List<String> bccMails;
  68 +
62 /** 69 /**
63 * 邮箱账号编号 70 * 邮箱账号编号
64 * 71 *
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java
1 package cn.iocoder.yudao.module.system.dal.mysql.mail; 1 package cn.iocoder.yudao.module.system.dal.mysql.mail;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
8 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
@@ -14,11 +16,12 @@ public interface MailLogMapper extends BaseMapperX&lt;MailLogDO&gt; { @@ -14,11 +16,12 @@ public interface MailLogMapper extends BaseMapperX&lt;MailLogDO&gt; {
14 return selectPage(reqVO, new LambdaQueryWrapperX<MailLogDO>() 16 return selectPage(reqVO, new LambdaQueryWrapperX<MailLogDO>()
15 .eqIfPresent(MailLogDO::getUserId, reqVO.getUserId()) 17 .eqIfPresent(MailLogDO::getUserId, reqVO.getUserId())
16 .eqIfPresent(MailLogDO::getUserType, reqVO.getUserType()) 18 .eqIfPresent(MailLogDO::getUserType, reqVO.getUserType())
17 - .likeIfPresent(MailLogDO::getToMail, reqVO.getToMail())  
18 .eqIfPresent(MailLogDO::getAccountId, reqVO.getAccountId()) 19 .eqIfPresent(MailLogDO::getAccountId, reqVO.getAccountId())
19 .eqIfPresent(MailLogDO::getTemplateId, reqVO.getTemplateId()) 20 .eqIfPresent(MailLogDO::getTemplateId, reqVO.getTemplateId())
20 .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus()) 21 .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus())
21 .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime()) 22 .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime())
  23 + .apply(StrUtil.isNotBlank(reqVO.getToMail()),
  24 + MyBatisUtils.findInSet("to_mails", reqVO.getToMail()))
22 .orderByDesc(MailLogDO::getId)); 25 .orderByDesc(MailLogDO::getId));
23 } 26 }
24 27
yudao-module-system/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 jakarta.validation.constraints.NotEmpty; 5 import jakarta.validation.constraints.NotEmpty;
6 import jakarta.validation.constraints.NotNull; 6 import jakarta.validation.constraints.NotNull;
7 7
  8 +import java.util.Collection;
8 import java.util.List; 9 import java.util.List;
9 10
10 /** 11 /**
@@ -24,15 +25,15 @@ public class MailSendMessage { @@ -24,15 +25,15 @@ public class MailSendMessage {
24 * 接收邮件地址 25 * 接收邮件地址
25 */ 26 */
26 @NotEmpty(message = "接收邮件地址不能为空") 27 @NotEmpty(message = "接收邮件地址不能为空")
27 - private List<String> toMails; 28 + private Collection<String> toMails;
28 /** 29 /**
29 * 抄送邮件地址 30 * 抄送邮件地址
30 */ 31 */
31 - private List<String> ccMails; 32 + private Collection<String> ccMails;
32 /** 33 /**
33 * 密送邮件地址 34 * 密送邮件地址
34 */ 35 */
35 - private List<String> bccMails; 36 + private Collection<String> bccMails;
36 /** 37 /**
37 * 邮件账号编号 38 * 邮件账号编号
38 */ 39 */
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
@@ -7,6 +7,7 @@ import org.springframework.stereotype.Component; @@ -7,6 +7,7 @@ import org.springframework.stereotype.Component;
7 7
8 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
9 9
  10 +import java.util.Collection;
10 import java.util.List; 11 import java.util.List;
11 12
12 import static java.util.Collections.singletonList; 13 import static java.util.Collections.singletonList;
@@ -28,21 +29,6 @@ public class MailProducer { @@ -28,21 +29,6 @@ public class MailProducer {
28 * 发送 {@link MailSendMessage} 消息 29 * 发送 {@link MailSendMessage} 消息
29 * 30 *
30 * @param sendLogId 发送日志编码 31 * @param sendLogId 发送日志编码
31 - * @param mail 接收邮件地址  
32 - * @param accountId 邮件账号编号  
33 - * @param nickname 邮件发件人  
34 - * @param title 邮件标题  
35 - * @param content 邮件内容  
36 - */  
37 - public void sendMailSendMessage(Long sendLogId, String mail, Long accountId,  
38 - String nickname, String title, String content) {  
39 - sendMailSendMessage(sendLogId, singletonList(mail), null, null, accountId, nickname, title, content);  
40 - }  
41 -  
42 - /**  
43 - * 发送 {@link MailSendMessage} 消息  
44 - *  
45 - * @param sendLogId 发送日志编码  
46 * @param toMails 接收邮件地址 32 * @param toMails 接收邮件地址
47 * @param ccMails 抄送邮件地址 33 * @param ccMails 抄送邮件地址
48 * @param bccMails 密送邮件地址 34 * @param bccMails 密送邮件地址
@@ -51,7 +37,8 @@ public class MailProducer { @@ -51,7 +37,8 @@ public class MailProducer {
51 * @param title 邮件标题 37 * @param title 邮件标题
52 * @param content 邮件内容 38 * @param content 邮件内容
53 */ 39 */
54 - public void sendMailSendMessage(Long sendLogId, List<String> toMails, List<String> ccMails, List<String> bccMails, 40 + public void sendMailSendMessage(Long sendLogId,
  41 + Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
55 Long accountId, String nickname, String title, String content) { 42 Long accountId, String nickname, String title, String content) {
56 MailSendMessage message = new MailSendMessage() 43 MailSendMessage message = new MailSendMessage()
57 .setLogId(sendLogId) 44 .setLogId(sendLogId)
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; 6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
8 8
  9 +import java.util.Collection;
9 import java.util.List; 10 import java.util.List;
10 import java.util.Map; 11 import java.util.Map;
11 12
@@ -36,23 +37,6 @@ public interface MailLogService { @@ -36,23 +37,6 @@ public interface MailLogService {
36 /** 37 /**
37 * 创建邮件日志 38 * 创建邮件日志
38 * 39 *
39 - * @param userId 用户编码  
40 - * @param userType 用户类型  
41 - * @param toMail 收件人邮件  
42 - * @param account 邮件账号信息  
43 - * @param template 模版信息  
44 - * @param templateContent 模版内容  
45 - * @param templateParams 模版参数  
46 - * @param isSend 是否发送成功  
47 - * @return 日志编号  
48 - */  
49 - Long createMailLog(Long userId, Integer userType, String toMail,  
50 - MailAccountDO account, MailTemplateDO template ,  
51 - String templateContent, Map<String, Object> templateParams, Boolean isSend);  
52 -  
53 - /**  
54 - * 创建邮件日志  
55 - *  
56 * @param userId 用户编码 40 * @param userId 用户编码
57 * @param userType 用户类型 41 * @param userType 用户类型
58 * @param toMails 收件人邮件 42 * @param toMails 收件人邮件
@@ -65,7 +49,8 @@ public interface MailLogService { @@ -65,7 +49,8 @@ public interface MailLogService {
65 * @param isSend 是否发送成功 49 * @param isSend 是否发送成功
66 * @return 日志编号 50 * @return 日志编号
67 */ 51 */
68 - Long createMailLog(Long userId, Integer userType, List<String> toMails, List<String> ccMails, List<String> bccMails, 52 + Long createMailLog(Long userId, Integer userType,
  53 + Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
69 MailAccountDO account, MailTemplateDO template, 54 MailAccountDO account, MailTemplateDO template,
70 String templateContent, Map<String, Object> templateParams, Boolean isSend); 55 String templateContent, Map<String, Object> templateParams, Boolean isSend);
71 56
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.mail; 1 package cn.iocoder.yudao.module.system.service.mail;
2 2
3 -import cn.hutool.core.collection.CollUtil; 3 +import cn.hutool.core.collection.ListUtil;
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.MailLogPageReqVO; 5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; 6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
@@ -13,9 +13,7 @@ import org.springframework.validation.annotation.Validated; @@ -13,9 +13,7 @@ import org.springframework.validation.annotation.Validated;
13 13
14 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
15 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
16 -import java.util.List;  
17 -import java.util.Map;  
18 -import java.util.Objects; 16 +import java.util.*;
19 17
20 import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage; 18 import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage;
21 19
@@ -43,7 +41,8 @@ public class MailLogServiceImpl implements MailLogService { @@ -43,7 +41,8 @@ public class MailLogServiceImpl implements MailLogService {
43 } 41 }
44 42
45 @Override 43 @Override
46 - public Long createMailLog(Long userId, Integer userType, String toMail, 44 + public Long createMailLog(Long userId, Integer userType,
  45 + Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
47 MailAccountDO account, MailTemplateDO template, 46 MailAccountDO account, MailTemplateDO template,
48 String templateContent, Map<String, Object> templateParams, Boolean isSend) { 47 String templateContent, Map<String, Object> templateParams, Boolean isSend) {
49 MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder(); 48 MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
@@ -51,32 +50,8 @@ public class MailLogServiceImpl implements MailLogService { @@ -51,32 +50,8 @@ public class MailLogServiceImpl implements MailLogService {
51 logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus() 50 logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()
52 : MailSendStatusEnum.IGNORE.getStatus()) 51 : MailSendStatusEnum.IGNORE.getStatus())
53 // 用户信息 52 // 用户信息
54 - .userId(userId).userType(userType).toMail(toMail)  
55 - .accountId(account.getId()).fromMail(account.getMail())  
56 - // 模板相关字段  
57 - .templateId(template.getId()).templateCode(template.getCode()).templateNickname(template.getNickname())  
58 - .templateTitle(template.getTitle()).templateContent(templateContent).templateParams(templateParams);  
59 -  
60 - // 插入数据库  
61 - MailLogDO logDO = logDOBuilder.build();  
62 - mailLogMapper.insert(logDO);  
63 - return logDO.getId();  
64 - }  
65 -  
66 - @Override  
67 - public Long createMailLog(Long userId, Integer userType, List<String> toMails, List<String> ccMails, List<String> bccMails,  
68 - MailAccountDO account, MailTemplateDO template,  
69 - String templateContent, Map<String, Object> templateParams, Boolean isSend) {  
70 - String toMail = CollUtil.isEmpty(toMails) ? "" : String.join(",", toMails);  
71 - String ccMail = CollUtil.isEmpty(ccMails) ? "" : String.join(",", ccMails);  
72 - String bccMail = CollUtil.isEmpty(bccMails) ? "" : String.join(",", bccMails);  
73 -  
74 - MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();  
75 - // 根据是否要发送,设置状态  
76 - logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()  
77 - : MailSendStatusEnum.IGNORE.getStatus())  
78 - // 用户信息  
79 - .userId(userId).userType(userType).toMail(toMail).ccMail(ccMail).bccMail(bccMail) 53 + .userId(userId).userType(userType)
  54 + .toMails(ListUtil.toList(toMails)).ccMails(ListUtil.toList(ccMails)).bccMails(ListUtil.toList(bccMails))
80 .accountId(account.getId()).fromMail(account.getMail()) 55 .accountId(account.getId()).fromMail(account.getMail())
81 // 模板相关字段 56 // 模板相关字段
82 .templateId(template.getId()).templateCode(template.getCode()).templateNickname(template.getNickname()) 57 .templateId(template.getId()).templateCode(template.getCode()).templateNickname(template.getNickname())
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java
1 package cn.iocoder.yudao.module.system.service.mail; 1 package cn.iocoder.yudao.module.system.service.mail;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
3 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; 4 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
4 5
5 -import java.util.List; 6 +import java.util.Collection;
6 import java.util.Map; 7 import java.util.Map;
7 8
8 /** 9 /**
@@ -16,82 +17,54 @@ public interface MailSendService { @@ -16,82 +17,54 @@ public interface MailSendService {
16 /** 17 /**
17 * 发送单条邮件给管理后台的用户 18 * 发送单条邮件给管理后台的用户
18 * 19 *
19 - * @param mail 邮箱  
20 * @param userId 用户编码 20 * @param userId 用户编码
21 - * @param templateCode 邮件模版编码  
22 - * @param templateParams 邮件模版参数  
23 - * @return 发送日志编号  
24 - */  
25 - Long sendSingleMailToAdmin(String mail, Long userId,  
26 - String templateCode, Map<String, Object> templateParams);  
27 -  
28 - /**  
29 - * 发送单条邮件给用户 APP 的用户  
30 - *  
31 - * @param mail 邮箱  
32 - * @param userId 用户编码  
33 - * @param templateCode 邮件模版编码  
34 - * @param templateParams 邮件模版参数  
35 - * @return 发送日志编号  
36 - */  
37 - Long sendSingleMailToMember(String mail, Long userId,  
38 - String templateCode, Map<String, Object> templateParams);  
39 -  
40 - /**  
41 - * 发送单条邮件给用户  
42 - *  
43 - * @param mail 邮箱  
44 - * @param userId 用户编码  
45 - * @param userType 用户类型  
46 - * @param templateCode 邮件模版编码  
47 - * @param templateParams 邮件模版参数  
48 - * @return 发送日志编号  
49 - */  
50 - Long sendSingleMail(String mail, Long userId, Integer userType,  
51 - String templateCode, Map<String, Object> templateParams);  
52 -  
53 - /**  
54 - * 发送多条邮件给管理后台的用户  
55 - *  
56 * @param toMails 收件邮箱 21 * @param toMails 收件邮箱
57 * @param ccMails 抄送邮箱 22 * @param ccMails 抄送邮箱
58 * @param bccMails 密送邮箱 23 * @param bccMails 密送邮箱
59 - * @param userId 用户编码  
60 * @param templateCode 邮件模版编码 24 * @param templateCode 邮件模版编码
61 * @param templateParams 邮件模版参数 25 * @param templateParams 邮件模版参数
62 * @return 发送日志编号 26 * @return 发送日志编号
63 */ 27 */
64 - Long sendMultipleMailToAdmin(List<String> toMails, List<String> ccMails, List<String> bccMails,  
65 - Long userId, String templateCode, Map<String, Object> templateParams); 28 + default Long sendSingleMailToAdmin(Long userId,
  29 + Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
  30 + String templateCode, Map<String, Object> templateParams) {
  31 + return sendSingleMail(toMails, ccMails, bccMails, userId, UserTypeEnum.ADMIN.getValue(),
  32 + templateCode, templateParams);
  33 + }
66 34
67 /** 35 /**
68 - * 发送多条邮件给用户 APP 的用户 36 + * 发送单条邮件给用户 APP 的用户
69 * 37 *
  38 + * @param userId 用户编码
70 * @param toMails 收件邮箱 39 * @param toMails 收件邮箱
71 * @param ccMails 抄送邮箱 40 * @param ccMails 抄送邮箱
72 * @param bccMails 密送邮箱 41 * @param bccMails 密送邮箱
73 - * @param userId 用户编码  
74 * @param templateCode 邮件模版编码 42 * @param templateCode 邮件模版编码
75 * @param templateParams 邮件模版参数 43 * @param templateParams 邮件模版参数
76 * @return 发送日志编号 44 * @return 发送日志编号
77 */ 45 */
78 - Long sendMultipleMailToMember(List<String> toMails, List<String> ccMails, List<String> bccMails,  
79 - Long userId, String templateCode, Map<String, Object> templateParams); 46 + default Long sendSingleMailToMember(Long userId,
  47 + Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
  48 + String templateCode, Map<String, Object> templateParams) {
  49 + return sendSingleMail(toMails, ccMails, bccMails, userId, UserTypeEnum.MEMBER.getValue(),
  50 + templateCode, templateParams);
  51 + }
80 52
81 /** 53 /**
82 - * 发送单条邮件给用户 54 + * 发送单条邮件
83 * 55 *
84 * @param toMails 收件邮箱 56 * @param toMails 收件邮箱
85 * @param ccMails 抄送邮箱 57 * @param ccMails 抄送邮箱
86 * @param bccMails 密送邮箱 58 * @param bccMails 密送邮箱
87 - * @param userId 用户编码 59 + * @param userId 用户编号
88 * @param userType 用户类型 60 * @param userType 用户类型
89 * @param templateCode 邮件模版编码 61 * @param templateCode 邮件模版编码
90 * @param templateParams 邮件模版参数 62 * @param templateParams 邮件模版参数
91 * @return 发送日志编号 63 * @return 发送日志编号
92 */ 64 */
93 - Long sendMultipleMail(List<String> toMails, List<String> ccMails, List<String> bccMails,  
94 - Long userId, Integer userType, String templateCode, Map<String, Object> templateParams); 65 + Long sendSingleMail(Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
  66 + Long userId, Integer userType,
  67 + String templateCode, Map<String, Object> templateParams);
95 68
96 /** 69 /**
97 * 执行真正的邮件发送 70 * 执行真正的邮件发送
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.mail; 1 package cn.iocoder.yudao.module.system.service.mail;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Validator;
4 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 7 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
@@ -14,16 +15,17 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService; @@ -14,16 +15,17 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService;
14 import com.google.common.annotations.VisibleForTesting; 15 import com.google.common.annotations.VisibleForTesting;
15 import jakarta.annotation.Resource; 16 import jakarta.annotation.Resource;
16 import lombok.extern.slf4j.Slf4j; 17 import lombok.extern.slf4j.Slf4j;
17 -import org.dromara.hutool.extra.mail.*; 18 +import org.dromara.hutool.extra.mail.MailAccount;
  19 +import org.dromara.hutool.extra.mail.MailUtil;
18 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
19 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
20 22
21 -import java.util.List; 23 +import java.util.Collection;
  24 +import java.util.LinkedHashSet;
22 import java.util.Map; 25 import java.util.Map;
23 26
24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
26 -import static java.util.Collections.singletonList;  
27 29
28 /** 30 /**
29 * 邮箱发送 Service 实现类 31 * 邮箱发送 Service 实现类
@@ -52,120 +54,67 @@ public class MailSendServiceImpl implements MailSendService { @@ -52,120 +54,67 @@ public class MailSendServiceImpl implements MailSendService {
52 private MailProducer mailProducer; 54 private MailProducer mailProducer;
53 55
54 @Override 56 @Override
55 - public Long sendSingleMailToAdmin(String mail, Long userId,  
56 - String templateCode, Map<String, Object> templateParams) {  
57 - // 如果 mail 为空,则加载用户编号对应的邮箱  
58 - if (StrUtil.isEmpty(mail)) {  
59 - AdminUserDO user = adminUserService.getUser(userId);  
60 - if (user != null) {  
61 - mail = user.getEmail();  
62 - }  
63 - }  
64 - // 执行发送  
65 - return sendSingleMail(mail, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);  
66 - }  
67 -  
68 - @Override  
69 - public Long sendSingleMailToMember(String mail, Long userId,  
70 - String templateCode, Map<String, Object> templateParams) {  
71 - // 如果 mail 为空,则加载用户编号对应的邮箱  
72 - if (StrUtil.isEmpty(mail)) {  
73 - mail = memberService.getMemberUserEmail(userId);  
74 - }  
75 - // 执行发送  
76 - return sendSingleMail(mail, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);  
77 - }  
78 -  
79 - @Override  
80 - public Long sendSingleMail(String mail, Long userId, Integer userType, 57 + public Long sendSingleMail(Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
  58 + Long userId, Integer userType,
81 String templateCode, Map<String, Object> templateParams) { 59 String templateCode, Map<String, Object> templateParams) {
82 - // 校验邮箱模版是否合法 60 + // 1.1 校验邮箱模版是否合法
83 MailTemplateDO template = validateMailTemplate(templateCode); 61 MailTemplateDO template = validateMailTemplate(templateCode);
84 - // 校验邮箱账号是否合法 62 + // 1.2 校验邮箱账号是否合法
85 MailAccountDO account = validateMailAccount(template.getAccountId()); 63 MailAccountDO account = validateMailAccount(template.getAccountId());
86 -  
87 - // 校验邮箱是否存在  
88 - mail = validateMail(mail); 64 + // 1.3 校验邮件参数是否缺失
89 validateTemplateParams(template, templateParams); 65 validateTemplateParams(template, templateParams);
90 66
91 - // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志  
92 - Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());  
93 - String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams);  
94 - String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams);  
95 - Long sendLogId = mailLogService.createMailLog(userId, userType, mail,  
96 - account, template, content, templateParams, isSend);  
97 - // 发送 MQ 消息,异步执行发送短信  
98 - if (isSend) {  
99 - mailProducer.sendMailSendMessage(sendLogId, mail, account.getId(),  
100 - template.getNickname(), title, content); 67 + // 2. 组装邮箱
  68 + String userMail = getUserMail(userId, userType);
  69 + Collection<String> toMailSet = new LinkedHashSet<>();
  70 + Collection<String> ccMailSet = new LinkedHashSet<>();
  71 + Collection<String> bccMailSet = new LinkedHashSet<>();
  72 + if (Validator.isEmail(userMail)) {
  73 + toMailSet.add(userMail);
101 } 74 }
102 - return sendLogId;  
103 - }  
104 -  
105 - @Override  
106 - public Long sendMultipleMailToAdmin(List<String> toMails, List<String> ccMails, List<String> bccMails, Long userId, String templateCode, Map<String, Object> templateParams) {  
107 - // 如果 mail 为空,则加载用户编号对应的邮箱  
108 - if (CollUtil.isEmpty(toMails)) {  
109 - AdminUserDO user = adminUserService.getUser(userId);  
110 - if (user != null) {  
111 - toMails = singletonList(user.getEmail());  
112 - }  
113 - }  
114 - // 执行发送  
115 - return sendMultipleMail(toMails, ccMails, bccMails, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);  
116 - }  
117 -  
118 - @Override  
119 - public Long sendMultipleMailToMember(List<String> toMails, List<String> ccMails, List<String> bccMails, Long userId, String templateCode, Map<String, Object> templateParams) {  
120 - // 如果 mail 为空,则加载用户编号对应的邮箱  
121 - if (CollUtil.isEmpty(toMails)) {  
122 - toMails = singletonList(memberService.getMemberUserEmail(userId));  
123 - }  
124 - // 执行发送  
125 - return sendMultipleMail(toMails, ccMails, bccMails, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);  
126 - }  
127 -  
128 - @Override  
129 - public Long sendMultipleMail(List<String> toMails, List<String> ccMails, List<String> bccMails, Long userId, Integer userType, String templateCode, Map<String, Object> templateParams) {  
130 - // 校验邮箱模版是否合法  
131 - MailTemplateDO template = validateMailTemplate(templateCode);  
132 - // 校验邮箱账号是否合法  
133 - MailAccountDO account = validateMailAccount(template.getAccountId());  
134 - // 校验邮件参数是否缺失  
135 - validateTemplateParams(template, templateParams);  
136 -  
137 - if (CollUtil.isEmpty(toMails)) {  
138 - throw exception(MAIL_SEND_MAIL_NOT_EXISTS);  
139 - }  
140 - // 校验邮箱是否存在  
141 - for (String mail : toMails) {  
142 - validateMail(mail); 75 + if (CollUtil.isNotEmpty(toMails)) {
  76 + toMails.stream().filter(Validator::isEmail).forEach(toMailSet::add);
143 } 77 }
144 if (CollUtil.isNotEmpty(ccMails)) { 78 if (CollUtil.isNotEmpty(ccMails)) {
145 - for (String mail : ccMails) {  
146 - validateMail(mail);  
147 - } 79 + ccMails.stream().filter(Validator::isEmail).forEach(ccMailSet::add);
148 } 80 }
149 if (CollUtil.isNotEmpty(bccMails)) { 81 if (CollUtil.isNotEmpty(bccMails)) {
150 - for (String mail : bccMails) {  
151 - validateMail(mail);  
152 - } 82 + bccMails.stream().filter(Validator::isEmail).forEach(bccMailSet::add);
  83 + }
  84 + if (CollUtil.isEmpty(toMailSet)) {
  85 + throw exception(MAIL_SEND_MAIL_NOT_EXISTS);
153 } 86 }
154 87
155 // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 88 // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
156 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); 89 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
157 String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams); 90 String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams);
158 String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams); 91 String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams);
159 - Long sendLogId = mailLogService.createMailLog(userId, userType, toMails, ccMails, bccMails, 92 + Long sendLogId = mailLogService.createMailLog(userId, userType, toMailSet, ccMailSet, bccMailSet,
160 account, template, content, templateParams, isSend); 93 account, template, content, templateParams, isSend);
161 // 发送 MQ 消息,异步执行发送短信 94 // 发送 MQ 消息,异步执行发送短信
162 if (isSend) { 95 if (isSend) {
163 - mailProducer.sendMailSendMessage(sendLogId, toMails, ccMails, bccMails, account.getId(),  
164 - template.getNickname(), title, content); 96 + mailProducer.sendMailSendMessage(sendLogId, toMailSet, ccMailSet, bccMailSet,
  97 + account.getId(), template.getNickname(), title, content);
165 } 98 }
166 return sendLogId; 99 return sendLogId;
167 } 100 }
168 101
  102 + private String getUserMail(Long userId, Integer userType) {
  103 + if (userId == null || userType == null) {
  104 + return null;
  105 + }
  106 + if (UserTypeEnum.ADMIN.getValue().equals(userType)) {
  107 + AdminUserDO user = adminUserService.getUser(userId);
  108 + if (user != null) {
  109 + return user.getEmail();
  110 + }
  111 + }
  112 + if (UserTypeEnum.MEMBER.getValue().equals(userType)) {
  113 + return memberService.getMemberUserEmail(userId);
  114 + }
  115 + return null;
  116 + }
  117 +
169 @Override 118 @Override
170 public void doSendMail(MailSendMessage message) { 119 public void doSendMail(MailSendMessage message) {
171 // 1. 创建发送账号 120 // 1. 创建发送账号
@@ -213,14 +162,6 @@ public class MailSendServiceImpl implements MailSendService { @@ -213,14 +162,6 @@ public class MailSendServiceImpl implements MailSendService {
213 return account; 162 return account;
214 } 163 }
215 164
216 - @VisibleForTesting  
217 - String validateMail(String mail) {  
218 - if (StrUtil.isEmpty(mail)) {  
219 - throw exception(MAIL_SEND_MAIL_NOT_EXISTS);  
220 - }  
221 - return mail;  
222 - }  
223 -  
224 /** 165 /**
225 * 校验邮件参数是否缺失 166 * 校验邮件参数是否缺失
226 * 167 *
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java
@@ -10,10 +10,12 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; @@ -10,10 +10,12 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; 11 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
12 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; 12 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
  13 +import org.assertj.core.util.Lists;
13 import org.junit.jupiter.api.Test; 14 import org.junit.jupiter.api.Test;
14 import org.springframework.context.annotation.Import; 15 import org.springframework.context.annotation.Import;
15 16
16 import jakarta.annotation.Resource; 17 import jakarta.annotation.Resource;
  18 +import java.util.Collection;
17 import java.util.Map; 19 import java.util.Map;
18 20
19 import static cn.hutool.core.util.RandomUtil.randomEle; 21 import static cn.hutool.core.util.RandomUtil.randomEle;
@@ -43,7 +45,9 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -43,7 +45,9 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
43 // 准备参数 45 // 准备参数
44 Long userId = randomLongId(); 46 Long userId = randomLongId();
45 Integer userType = randomEle(UserTypeEnum.values()).getValue(); 47 Integer userType = randomEle(UserTypeEnum.values()).getValue();
46 - String toMail = randomEmail(); 48 + Collection<String> toMails = Lists.newArrayList(randomEmail(), randomEmail());
  49 + Collection<String> ccMails = Lists.newArrayList(randomEmail());
  50 + Collection<String> bccMails = Lists.newArrayList(randomEmail());
47 MailAccountDO account = randomPojo(MailAccountDO.class); 51 MailAccountDO account = randomPojo(MailAccountDO.class);
48 MailTemplateDO template = randomPojo(MailTemplateDO.class); 52 MailTemplateDO template = randomPojo(MailTemplateDO.class);
49 String templateContent = randomString(); 53 String templateContent = randomString();
@@ -52,14 +56,20 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -52,14 +56,20 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
52 // mock 方法 56 // mock 方法
53 57
54 // 调用 58 // 调用
55 - Long logId = mailLogService.createMailLog(userId, userType, toMail, account, template, templateContent, templateParams, isSend); 59 + Long logId = mailLogService.createMailLog(userId, userType, toMails, ccMails, bccMails,
  60 + account, template, templateContent, templateParams, isSend);
56 // 断言 61 // 断言
57 MailLogDO log = mailLogMapper.selectById(logId); 62 MailLogDO log = mailLogMapper.selectById(logId);
58 assertNotNull(log); 63 assertNotNull(log);
59 assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus()); 64 assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus());
60 assertEquals(userId, log.getUserId()); 65 assertEquals(userId, log.getUserId());
61 assertEquals(userType, log.getUserType()); 66 assertEquals(userType, log.getUserType());
62 - assertEquals(toMail, log.getToMail()); 67 + assertEquals(toMails.size(), log.getToMails().size());
  68 + assertTrue(log.getToMails().containsAll(toMails));
  69 + assertEquals(ccMails.size(), log.getCcMails().size());
  70 + assertTrue(log.getCcMails().containsAll(ccMails));
  71 + assertEquals(bccMails.size(), log.getBccMails().size());
  72 + assertTrue(log.getBccMails().containsAll(bccMails));
63 assertEquals(account.getId(), log.getAccountId()); 73 assertEquals(account.getId(), log.getAccountId());
64 assertEquals(account.getMail(), log.getFromMail()); 74 assertEquals(account.getMail(), log.getFromMail());
65 assertEquals(template.getId(), log.getTemplateId()); 75 assertEquals(template.getId(), log.getTemplateId());
@@ -136,7 +146,9 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -136,7 +146,9 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
136 MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到 146 MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到
137 o.setUserId(1L); 147 o.setUserId(1L);
138 o.setUserType(UserTypeEnum.ADMIN.getValue()); 148 o.setUserType(UserTypeEnum.ADMIN.getValue());
139 - o.setToMail("768@qq.com"); 149 + o.setToMails(Lists.newArrayList("768@qq.com"));
  150 + o.setCcMails(Lists.newArrayList());
  151 + o.setBccMails(Lists.newArrayList());
140 o.setAccountId(10L); 152 o.setAccountId(10L);
141 o.setTemplateId(100L); 153 o.setTemplateId(100L);
142 o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); 154 o.setSendStatus(MailSendStatusEnum.INIT.getStatus());
@@ -148,8 +160,8 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -148,8 +160,8 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
148 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L))); 160 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L)));
149 // 测试 userType 不匹配 161 // 测试 userType 不匹配
150 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); 162 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
151 - // 测试 toMail 不匹配  
152 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMail("788@.qq.com"))); 163 + // 测试 toMails 不匹配(特殊:find_in_set 无法单测)
  164 +// mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMails(Lists.newArrayList("788@qq.com"))));
153 // 测试 accountId 不匹配 165 // 测试 accountId 不匹配
154 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L))); 166 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L)));
155 // 测试 templateId 不匹配 167 // 测试 templateId 不匹配
@@ -162,7 +174,7 @@ public class MailLogServiceImplTest extends BaseDbUnitTest { @@ -162,7 +174,7 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
162 MailLogPageReqVO reqVO = new MailLogPageReqVO(); 174 MailLogPageReqVO reqVO = new MailLogPageReqVO();
163 reqVO.setUserId(1L); 175 reqVO.setUserId(1L);
164 reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); 176 reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
165 - reqVO.setToMail("768"); 177 +// reqVO.setToMail("768@qq.com");
166 reqVO.setAccountId(10L); 178 reqVO.setAccountId(10L);
167 reqVO.setTemplateId(100L); 179 reqVO.setTemplateId(100L);
168 reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus()); 180 reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus());
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java
@@ -20,6 +20,7 @@ import org.mockito.InjectMocks; @@ -20,6 +20,7 @@ import org.mockito.InjectMocks;
20 import org.mockito.Mock; 20 import org.mockito.Mock;
21 import org.mockito.MockedStatic; 21 import org.mockito.MockedStatic;
22 22
  23 +import java.util.Collection;
23 import java.util.HashMap; 24 import java.util.HashMap;
24 import java.util.Map; 25 import java.util.Map;
25 26
@@ -65,14 +66,18 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -65,14 +66,18 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
65 } 66 }
66 67
67 @Test 68 @Test
68 - public void testSendSingleMailToAdmin() { 69 + public void testSendSingleMail_success() {
69 // 准备参数 70 // 准备参数
70 Long userId = randomLongId(); 71 Long userId = randomLongId();
71 String templateCode = RandomUtils.randomString(); 72 String templateCode = RandomUtils.randomString();
72 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") 73 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
73 .put("op", "login").build(); 74 .put("op", "login").build();
  75 + Collection<String> toMails = Lists.newArrayList("admin@test.com");
  76 + Collection<String> ccMails = Lists.newArrayList("cc@test.com");
  77 + Collection<String> bccMails = Lists.newArrayList("bcc@test.com");
  78 +
74 // mock adminUserService 的方法 79 // mock adminUserService 的方法
75 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); 80 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setEmail("admin@example.com"));
76 when(adminUserService.getUser(eq(userId))).thenReturn(user); 81 when(adminUserService.getUser(eq(userId))).thenReturn(user);
77 82
78 // mock MailTemplateService 的方法 83 // mock MailTemplateService 的方法
@@ -93,61 +98,27 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -93,61 +98,27 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
93 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); 98 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
94 // mock MailLogService 的方法 99 // mock MailLogService 的方法
95 Long mailLogId = randomLongId(); 100 Long mailLogId = randomLongId();
96 - when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(user.getEmail()), 101 + when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.ADMIN.getValue()),
  102 + argThat(toMailSet -> toMailSet.contains(user.getEmail()) && toMailSet.contains("admin@test.com")),
  103 + argThat(ccMailSet -> ccMailSet.contains("cc@test.com")),
  104 + argThat(bccMailSet -> bccMailSet.contains("bcc@test.com")),
97 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); 105 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId);
98 106
99 // 调用 107 // 调用
100 - Long resultMailLogId = mailSendService.sendSingleMailToAdmin(null, userId, templateCode, templateParams); 108 + Long resultMailLogId = mailSendService.sendSingleMail(toMails, ccMails, bccMails, userId,
  109 + UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
101 // 断言 110 // 断言
102 assertEquals(mailLogId, resultMailLogId); 111 assertEquals(mailLogId, resultMailLogId);
103 // 断言调用 112 // 断言调用
104 - verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(user.getEmail()),  
105 - eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));  
106 - }  
107 -  
108 - @Test  
109 - public void testSendSingleMailToMember() {  
110 - // 准备参数  
111 - Long userId = randomLongId();  
112 - String templateCode = RandomUtils.randomString();  
113 - Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")  
114 - .put("op", "login").build();  
115 - // mock memberService 的方法  
116 - String mail = randomEmail();  
117 - when(memberService.getMemberUserEmail(eq(userId))).thenReturn(mail);  
118 -  
119 - // mock MailTemplateService 的方法  
120 - MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {  
121 - o.setStatus(CommonStatusEnum.ENABLE.getStatus());  
122 - o.setContent("验证码为{code}, 操作为{op}");  
123 - o.setParams(Lists.newArrayList("code", "op"));  
124 - });  
125 - when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);  
126 - String title = RandomUtils.randomString();  
127 - when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams)))  
128 - .thenReturn(title);  
129 - String content = RandomUtils.randomString();  
130 - when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams)))  
131 - .thenReturn(content);  
132 - // mock MailAccountService 的方法  
133 - MailAccountDO account = randomPojo(MailAccountDO.class);  
134 - when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);  
135 - // mock MailLogService 的方法  
136 - Long mailLogId = randomLongId();  
137 - when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(mail),  
138 - eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId);  
139 -  
140 - // 调用  
141 - Long resultMailLogId = mailSendService.sendSingleMailToMember(null, userId, templateCode, templateParams);  
142 - // 断言  
143 - assertEquals(mailLogId, resultMailLogId);  
144 - // 断言调用  
145 - verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), 113 + verify(mailProducer).sendMailSendMessage(eq(mailLogId),
  114 + argThat(toMailSet -> toMailSet.contains(user.getEmail()) && toMailSet.contains("admin@test.com")),
  115 + argThat(ccMailSet -> ccMailSet.contains("cc@test.com")),
  116 + argThat(bccMailSet -> bccMailSet.contains("bcc@test.com")),
146 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); 117 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));
147 } 118 }
148 119
149 /** 120 /**
150 - * 发送成功,当短信模板开启时 121 + * 发送成功,当邮件模板开启时
151 */ 122 */
152 @Test 123 @Test
153 public void testSendSingleMail_successWhenMailTemplateEnable() { 124 public void testSendSingleMail_successWhenMailTemplateEnable() {
@@ -158,6 +129,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -158,6 +129,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
158 String templateCode = RandomUtils.randomString(); 129 String templateCode = RandomUtils.randomString();
159 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") 130 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
160 .put("op", "login").build(); 131 .put("op", "login").build();
  132 + Collection<String> toMails = Lists.newArrayList(mail);
  133 +
161 // mock MailTemplateService 的方法 134 // mock MailTemplateService 的方法
162 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { 135 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
163 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 136 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -176,23 +149,29 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -176,23 +149,29 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
176 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); 149 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
177 // mock MailLogService 的方法 150 // mock MailLogService 的方法
178 Long mailLogId = randomLongId(); 151 Long mailLogId = randomLongId();
179 - when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), 152 + when(mailLogService.createMailLog(eq(userId), eq(userType),
  153 + argThat(toMailSet -> toMailSet.contains(mail)),
  154 + argThat(Collection::isEmpty),
  155 + argThat(Collection::isEmpty),
180 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); 156 eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId);
181 157
182 // 调用 158 // 调用
183 - Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); 159 + Long resultMailLogId = mailSendService.sendSingleMail(toMails, null, null, userId, userType, templateCode, templateParams);
184 // 断言 160 // 断言
185 assertEquals(mailLogId, resultMailLogId); 161 assertEquals(mailLogId, resultMailLogId);
186 // 断言调用 162 // 断言调用
187 - verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), 163 + verify(mailProducer).sendMailSendMessage(eq(mailLogId),
  164 + argThat(toMailSet -> toMailSet.contains(mail)),
  165 + argThat(Collection::isEmpty),
  166 + argThat(Collection::isEmpty),
188 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); 167 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));
189 } 168 }
190 169
191 /** 170 /**
192 - * 发送成功,当短信模板关闭时 171 + * 发送成功,当邮件模板关闭时
193 */ 172 */
194 @Test 173 @Test
195 - public void testSendSingleMail_successWhenSmsTemplateDisable() { 174 + public void testSendSingleMail_successWhenMailTemplateDisable() {
196 // 准备参数 175 // 准备参数
197 String mail = randomEmail(); 176 String mail = randomEmail();
198 Long userId = randomLongId(); 177 Long userId = randomLongId();
@@ -200,6 +179,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -200,6 +179,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
200 String templateCode = RandomUtils.randomString(); 179 String templateCode = RandomUtils.randomString();
201 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234") 180 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
202 .put("op", "login").build(); 181 .put("op", "login").build();
  182 + Collection<String> toMails = Lists.newArrayList(mail);
  183 +
203 // mock MailTemplateService 的方法 184 // mock MailTemplateService 的方法
204 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { 185 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
205 o.setStatus(CommonStatusEnum.DISABLE.getStatus()); 186 o.setStatus(CommonStatusEnum.DISABLE.getStatus());
@@ -218,15 +199,18 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -218,15 +199,18 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
218 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); 199 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
219 // mock MailLogService 的方法 200 // mock MailLogService 的方法
220 Long mailLogId = randomLongId(); 201 Long mailLogId = randomLongId();
221 - when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), 202 + when(mailLogService.createMailLog(eq(userId), eq(userType),
  203 + argThat(toMailSet -> toMailSet.contains(mail)),
  204 + argThat(Collection::isEmpty),
  205 + argThat(Collection::isEmpty),
222 eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId); 206 eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId);
223 207
224 // 调用 208 // 调用
225 - Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); 209 + Long resultMailLogId = mailSendService.sendSingleMail(toMails, null, null, userId, userType, templateCode, templateParams);
226 // 断言 210 // 断言
227 assertEquals(mailLogId, resultMailLogId); 211 assertEquals(mailLogId, resultMailLogId);
228 // 断言调用 212 // 断言调用
229 - verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), anyString(), 213 + verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), any(), any(), any(),
230 anyLong(), anyString(), anyString(), anyString()); 214 anyLong(), anyString(), anyString(), anyString());
231 } 215 }
232 216
@@ -255,12 +239,29 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -255,12 +239,29 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
255 } 239 }
256 240
257 @Test 241 @Test
258 - public void testValidateMail_notExists() { 242 + public void testSendSingleMail_noValidEmail() {
259 // 准备参数 243 // 准备参数
260 - // mock 方法 244 + Long userId = randomLongId();
  245 + String templateCode = RandomUtils.randomString();
  246 + Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
  247 + .put("op", "login").build();
  248 + Collection<String> toMails = Lists.newArrayList("invalid-email"); // 非法邮箱
  249 +
  250 + // mock MailTemplateService 的方法
  251 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
  252 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  253 + o.setContent("验证码为{code}, 操作为{op}");
  254 + o.setParams(Lists.newArrayList("code", "op"));
  255 + });
  256 + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
  257 +
  258 + // mock MailAccountService 的方法
  259 + MailAccountDO account = randomPojo(MailAccountDO.class);
  260 + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
261 261
262 // 调用,并断言异常 262 // 调用,并断言异常
263 - assertServiceException(() -> mailSendService.validateMail(null), 263 + assertServiceException(() -> mailSendService.sendSingleMail(toMails, null, null, userId,
  264 + UserTypeEnum.ADMIN.getValue(), templateCode, templateParams),
264 MAIL_SEND_MAIL_NOT_EXISTS); 265 MAIL_SEND_MAIL_NOT_EXISTS);
265 } 266 }
266 267
@@ -286,7 +287,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -286,7 +287,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
286 assertEquals(account.getPort(), mailAccount.getPort()); 287 assertEquals(account.getPort(), mailAccount.getPort());
287 assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); 288 assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
288 return true; 289 return true;
289 - }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) 290 + }), eq(message.getToMails()), eq(message.getCcMails()), eq(message.getBccMails()),
  291 + eq(message.getTitle()), eq(message.getContent()), eq(true)))
290 .thenReturn(messageId); 292 .thenReturn(messageId);
291 293
292 // 调用 294 // 调用
@@ -317,7 +319,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest { @@ -317,7 +319,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
317 assertEquals(account.getPort(), mailAccount.getPort()); 319 assertEquals(account.getPort(), mailAccount.getPort());
318 assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); 320 assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
319 return true; 321 return true;
320 - }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))).thenThrow(e); 322 + }), eq(message.getToMails()), eq(message.getCcMails()), eq(message.getBccMails()),
  323 + eq(message.getTitle()), eq(message.getContent()), eq(true))).thenThrow(e);
321 324
322 // 调用 325 // 调用
323 mailSendService.doSendMail(message); 326 mailSendService.doSendMail(message);
yudao-module-system/src/test/resources/sql/create_tables.sql
@@ -553,7 +553,9 @@ CREATE TABLE IF NOT EXISTS &quot;system_mail_log&quot; ( @@ -553,7 +553,9 @@ CREATE TABLE IF NOT EXISTS &quot;system_mail_log&quot; (
553 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 553 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
554 "user_id" bigint, 554 "user_id" bigint,
555 "user_type" varchar, 555 "user_type" varchar,
556 - "to_mail" varchar NOT NULL, 556 + "to_mails" varchar NOT NULL,
  557 + "cc_mails" varchar,
  558 + "bcc_mails" varchar,
557 "account_id" bigint NOT NULL, 559 "account_id" bigint NOT NULL,
558 "from_mail" varchar NOT NULL, 560 "from_mail" varchar NOT NULL,
559 "template_id" bigint NOT NULL, 561 "template_id" bigint NOT NULL,