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 1259 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
1260 1260 `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号',
1261 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 1265 `account_id` bigint NOT NULL COMMENT '邮箱账号编号',
1266 1266 `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址',
1267 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 21  
22 22 @Override
23 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 26 reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
26 27 }
27 28  
28 29 @Override
29 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 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 4  
5 5 import jakarta.validation.constraints.Email;
6 6 import jakarta.validation.constraints.NotNull;
  7 +
  8 +import java.util.List;
7 9 import java.util.Map;
8 10  
9 11 /**
... ... @@ -16,13 +18,24 @@ public class MailSendSingleToUserReqDTO {
16 18  
17 19 /**
18 20 * 用户编号
  21 + *
  22 + * 如果非空,则加载对应用户的邮箱,添加到 {@link #toMails} 中
19 23 */
20 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 91 @Operation(summary = "发送短信")
92 92 @PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')")
93 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 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 4 import lombok.Data;
5 5  
6 6 import java.time.LocalDateTime;
  7 +import java.util.List;
7 8 import java.util.Map;
8 9  
9 10 @Schema(description = "管理后台 - 邮件日志 Response VO")
... ... @@ -20,13 +21,13 @@ public class MailLogRespVO {
20 21 private Byte userType;
21 22  
22 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 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 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 32 @Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18107")
32 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 2  
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
5 6 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
6 7 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
7 8 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -12,6 +13,7 @@ import lombok.*;
12 13  
13 14 import java.io.Serializable;
14 15 import java.time.LocalDateTime;
  16 +import java.util.List;
15 17 import java.util.Map;
16 18  
17 19 /**
... ... @@ -47,18 +49,23 @@ public class MailLogDO extends BaseDO implements Serializable {
47 49 * 枚举 {@link UserTypeEnum}
48 50 */
49 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 1 package cn.iocoder.yudao.module.system.dal.mysql.mail;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 8 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
7 9 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
8 10 import org.apache.ibatis.annotations.Mapper;
... ... @@ -14,11 +16,12 @@ public interface MailLogMapper extends BaseMapperX&lt;MailLogDO&gt; {
14 16 return selectPage(reqVO, new LambdaQueryWrapperX<MailLogDO>()
15 17 .eqIfPresent(MailLogDO::getUserId, reqVO.getUserId())
16 18 .eqIfPresent(MailLogDO::getUserType, reqVO.getUserType())
17   - .likeIfPresent(MailLogDO::getToMail, reqVO.getToMail())
18 19 .eqIfPresent(MailLogDO::getAccountId, reqVO.getAccountId())
19 20 .eqIfPresent(MailLogDO::getTemplateId, reqVO.getTemplateId())
20 21 .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus())
21 22 .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime())
  23 + .apply(StrUtil.isNotBlank(reqVO.getToMail()),
  24 + MyBatisUtils.findInSet("to_mails", reqVO.getToMail()))
22 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 5 import jakarta.validation.constraints.NotEmpty;
6 6 import jakarta.validation.constraints.NotNull;
7 7  
  8 +import java.util.Collection;
8 9 import java.util.List;
9 10  
10 11 /**
... ... @@ -24,15 +25,15 @@ public class MailSendMessage {
24 25 * 接收邮件地址
25 26 */
26 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 7  
8 8 import jakarta.annotation.Resource;
9 9  
  10 +import java.util.Collection;
10 11 import java.util.List;
11 12  
12 13 import static java.util.Collections.singletonList;
... ... @@ -28,21 +29,6 @@ public class MailProducer {
28 29 * 发送 {@link MailSendMessage} 消息
29 30 *
30 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 32 * @param toMails 接收邮件地址
47 33 * @param ccMails 抄送邮件地址
48 34 * @param bccMails 密送邮件地址
... ... @@ -51,7 +37,8 @@ public class MailProducer {
51 37 * @param title 邮件标题
52 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 42 Long accountId, String nickname, String title, String content) {
56 43 MailSendMessage message = new MailSendMessage()
57 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 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
8 8  
  9 +import java.util.Collection;
9 10 import java.util.List;
10 11 import java.util.Map;
11 12  
... ... @@ -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 40 * @param userId 用户编码
57 41 * @param userType 用户类型
58 42 * @param toMails 收件人邮件
... ... @@ -65,7 +49,8 @@ public interface MailLogService {
65 49 * @param isSend 是否发送成功
66 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 54 MailAccountDO account, MailTemplateDO template,
70 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 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 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
6 6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
... ... @@ -13,9 +13,7 @@ import org.springframework.validation.annotation.Validated;
13 13  
14 14 import jakarta.annotation.Resource;
15 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 18 import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage;
21 19  
... ... @@ -43,7 +41,8 @@ public class MailLogServiceImpl implements MailLogService {
43 41 }
44 42  
45 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 46 MailAccountDO account, MailTemplateDO template,
48 47 String templateContent, Map<String, Object> templateParams, Boolean isSend) {
49 48 MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
... ... @@ -51,32 +50,8 @@ public class MailLogServiceImpl implements MailLogService {
51 50 logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()
52 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 55 .accountId(account.getId()).fromMail(account.getMail())
81 56 // 模板相关字段
82 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 1 package cn.iocoder.yudao.module.system.service.mail;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
3 4 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
4 5  
5   -import java.util.List;
  6 +import java.util.Collection;
6 7 import java.util.Map;
7 8  
8 9 /**
... ... @@ -16,82 +17,54 @@ public interface MailSendService {
16 17 /**
17 18 * 发送单条邮件给管理后台的用户
18 19 *
19   - * @param mail 邮箱
20 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 21 * @param toMails 收件邮箱
57 22 * @param ccMails 抄送邮箱
58 23 * @param bccMails 密送邮箱
59   - * @param userId 用户编码
60 24 * @param templateCode 邮件模版编码
61 25 * @param templateParams 邮件模版参数
62 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 39 * @param toMails 收件邮箱
71 40 * @param ccMails 抄送邮箱
72 41 * @param bccMails 密送邮箱
73   - * @param userId 用户编码
74 42 * @param templateCode 邮件模版编码
75 43 * @param templateParams 邮件模版参数
76 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 56 * @param toMails 收件邮箱
85 57 * @param ccMails 抄送邮箱
86 58 * @param bccMails 密送邮箱
87   - * @param userId 用户编码
  59 + * @param userId 用户编号
88 60 * @param userType 用户类型
89 61 * @param templateCode 邮件模版编码
90 62 * @param templateParams 邮件模版参数
91 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 1 package cn.iocoder.yudao.module.system.service.mail;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Validator;
4 5 import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 7 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
... ... @@ -14,16 +15,17 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService;
14 15 import com.google.common.annotations.VisibleForTesting;
15 16 import jakarta.annotation.Resource;
16 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 20 import org.springframework.stereotype.Service;
19 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 25 import java.util.Map;
23 26  
24 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
26   -import static java.util.Collections.singletonList;
27 29  
28 30 /**
29 31 * 邮箱发送 Service 实现类
... ... @@ -52,120 +54,67 @@ public class MailSendServiceImpl implements MailSendService {
52 54 private MailProducer mailProducer;
53 55  
54 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 59 String templateCode, Map<String, Object> templateParams) {
82   - // 校验邮箱模版是否合法
  60 + // 1.1 校验邮箱模版是否合法
83 61 MailTemplateDO template = validateMailTemplate(templateCode);
84   - // 校验邮箱账号是否合法
  62 + // 1.2 校验邮箱账号是否合法
85 63 MailAccountDO account = validateMailAccount(template.getAccountId());
86   -
87   - // 校验邮箱是否存在
88   - mail = validateMail(mail);
  64 + // 1.3 校验邮件参数是否缺失
89 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 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 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 89 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
157 90 String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams);
158 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 93 account, template, content, templateParams, isSend);
161 94 // 发送 MQ 消息,异步执行发送短信
162 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 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 118 @Override
170 119 public void doSendMail(MailSendMessage message) {
171 120 // 1. 创建发送账号
... ... @@ -213,14 +162,6 @@ public class MailSendServiceImpl implements MailSendService {
213 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 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
11 11 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
12 12 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
  13 +import org.assertj.core.util.Lists;
13 14 import org.junit.jupiter.api.Test;
14 15 import org.springframework.context.annotation.Import;
15 16  
16 17 import jakarta.annotation.Resource;
  18 +import java.util.Collection;
17 19 import java.util.Map;
18 20  
19 21 import static cn.hutool.core.util.RandomUtil.randomEle;
... ... @@ -43,7 +45,9 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
43 45 // 准备参数
44 46 Long userId = randomLongId();
45 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 51 MailAccountDO account = randomPojo(MailAccountDO.class);
48 52 MailTemplateDO template = randomPojo(MailTemplateDO.class);
49 53 String templateContent = randomString();
... ... @@ -52,14 +56,20 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
52 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 62 MailLogDO log = mailLogMapper.selectById(logId);
58 63 assertNotNull(log);
59 64 assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus());
60 65 assertEquals(userId, log.getUserId());
61 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 73 assertEquals(account.getId(), log.getAccountId());
64 74 assertEquals(account.getMail(), log.getFromMail());
65 75 assertEquals(template.getId(), log.getTemplateId());
... ... @@ -136,7 +146,9 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
136 146 MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到
137 147 o.setUserId(1L);
138 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 152 o.setAccountId(10L);
141 153 o.setTemplateId(100L);
142 154 o.setSendStatus(MailSendStatusEnum.INIT.getStatus());
... ... @@ -148,8 +160,8 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
148 160 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L)));
149 161 // 测试 userType 不匹配
150 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 165 // 测试 accountId 不匹配
154 166 mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L)));
155 167 // 测试 templateId 不匹配
... ... @@ -162,7 +174,7 @@ public class MailLogServiceImplTest extends BaseDbUnitTest {
162 174 MailLogPageReqVO reqVO = new MailLogPageReqVO();
163 175 reqVO.setUserId(1L);
164 176 reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
165   - reqVO.setToMail("768");
  177 +// reqVO.setToMail("768@qq.com");
166 178 reqVO.setAccountId(10L);
167 179 reqVO.setTemplateId(100L);
168 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 20 import org.mockito.Mock;
21 21 import org.mockito.MockedStatic;
22 22  
  23 +import java.util.Collection;
23 24 import java.util.HashMap;
24 25 import java.util.Map;
25 26  
... ... @@ -65,14 +66,18 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
65 66 }
66 67  
67 68 @Test
68   - public void testSendSingleMailToAdmin() {
  69 + public void testSendSingleMail_success() {
69 70 // 准备参数
70 71 Long userId = randomLongId();
71 72 String templateCode = RandomUtils.randomString();
72 73 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
73 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 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 81 when(adminUserService.getUser(eq(userId))).thenReturn(user);
77 82  
78 83 // mock MailTemplateService 的方法
... ... @@ -93,61 +98,27 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
93 98 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
94 99 // mock MailLogService 的方法
95 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 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 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 117 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));
147 118 }
148 119  
149 120 /**
150   - * 发送成功,当短信模板开启时
  121 + * 发送成功,当邮件模板开启时
151 122 */
152 123 @Test
153 124 public void testSendSingleMail_successWhenMailTemplateEnable() {
... ... @@ -158,6 +129,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
158 129 String templateCode = RandomUtils.randomString();
159 130 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
160 131 .put("op", "login").build();
  132 + Collection<String> toMails = Lists.newArrayList(mail);
  133 +
161 134 // mock MailTemplateService 的方法
162 135 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
163 136 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
... ... @@ -176,23 +149,29 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
176 149 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
177 150 // mock MailLogService 的方法
178 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 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 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 167 eq(account.getId()), eq(template.getNickname()), eq(title), eq(content));
189 168 }
190 169  
191 170 /**
192   - * 发送成功,当短信模板关闭时
  171 + * 发送成功,当邮件模板关闭时
193 172 */
194 173 @Test
195   - public void testSendSingleMail_successWhenSmsTemplateDisable() {
  174 + public void testSendSingleMail_successWhenMailTemplateDisable() {
196 175 // 准备参数
197 176 String mail = randomEmail();
198 177 Long userId = randomLongId();
... ... @@ -200,6 +179,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
200 179 String templateCode = RandomUtils.randomString();
201 180 Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
202 181 .put("op", "login").build();
  182 + Collection<String> toMails = Lists.newArrayList(mail);
  183 +
203 184 // mock MailTemplateService 的方法
204 185 MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
205 186 o.setStatus(CommonStatusEnum.DISABLE.getStatus());
... ... @@ -218,15 +199,18 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
218 199 when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
219 200 // mock MailLogService 的方法
220 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 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 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 214 anyLong(), anyString(), anyString(), anyString());
231 215 }
232 216  
... ... @@ -255,12 +239,29 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
255 239 }
256 240  
257 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 265 MAIL_SEND_MAIL_NOT_EXISTS);
265 266 }
266 267  
... ... @@ -286,7 +287,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
286 287 assertEquals(account.getPort(), mailAccount.getPort());
287 288 assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
288 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 292 .thenReturn(messageId);
291 293  
292 294 // 调用
... ... @@ -317,7 +319,8 @@ public class MailSendServiceImplTest extends BaseMockitoUnitTest {
317 319 assertEquals(account.getPort(), mailAccount.getPort());
318 320 assertEquals(account.getSslEnable(), mailAccount.isSslEnable());
319 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 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 553 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
554 554 "user_id" bigint,
555 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 559 "account_id" bigint NOT NULL,
558 560 "from_mail" varchar NOT NULL,
559 561 "template_id" bigint NOT NULL,
... ...