Commit 8bc5254e308ab0d774635f0629bc508f58c8041a

Authored by wangjingyi
1 parent ea92b841

邮件模块 添加邮箱账号缓存 修改校验方式

Showing 23 changed files with 318 additions and 132 deletions
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
... ... @@ -130,6 +130,6 @@ public interface ErrorCodeConstants {
130 130 // ========== 邮箱模版 1002021000 ==========
131 131 ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002021000 , "邮箱模版不存在");
132 132 ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002021001, "邮箱模版存在");
133   - ErrorCode MAIL_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002021002, "存在关联邮箱模版");
  133 + ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002021002, "存在关联邮箱模版");
134 134  
135 135 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java
... ... @@ -57,7 +57,7 @@ public class MailAccountController {
57 57 }
58 58  
59 59 // TODO @wangjingyi:getMailAccount 和 getMailAccountPage 这两个接口,定义一个对应的 Resp 类哈,参考别的模块。主要不要返回 password 字段。
60   - // 一个可以的做法,是 MailAccountBaseVO 不返回 password,然后 MailAccountCreateReqVO、MailAccountUpdateReqVO 添加这个字段
  60 + // 一个可以的做法,是 MailAccountBaseVO 不返回 password,然后 MailAccountCreateReqVO、MailAccountUpdateReqVO 添加这个字段 DONE
61 61  
62 62 @GetMapping("/get")
63 63 @ApiOperation("获得邮箱账号")
... ... @@ -76,7 +76,7 @@ public class MailAccountController {
76 76 return success(MailAccountConvert.INSTANCE.convertPage(pageResult));
77 77 }
78 78  
79   - // TODO @wangjingyi:getSimpleMailAccountList 单独定义一个类,只返回精简的信息,id,from 即可。像密码之类都是敏感信息,不应该返回
  79 + // TODO @wangjingyi:getSimpleMailAccountList 单独定义一个类,只返回精简的信息,id,from 即可。像密码之类都是敏感信息,不应该返回 DONE
80 80  
81 81 @GetMapping("/list-all-simple")
82 82 @ApiOperation(value = "获得邮箱账号精简列表")
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java
... ... @@ -3,10 +3,7 @@ package cn.iocoder.yudao.module.system.controller.admin.mail;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
6   -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateBaseVO;
7   -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
8   -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
9   -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.*;
10 7 import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
11 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
12 9 import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
... ... @@ -29,9 +26,9 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
29 26 @RequestMapping("/system/mail-template")
30 27 public class MailTemplateController {
31 28  
32   - // TODO @wangjingyi:private
  29 + // TODO @wangjingyi:private DONE
33 30 @Autowired
34   - MailTemplateService mailTempleService;
  31 + private MailTemplateService mailTempleService;
35 32  
36 33 @PostMapping("/create")
37 34 @ApiOperation("创建邮箱模版")
... ... @@ -56,13 +53,13 @@ public class MailTemplateController {
56 53 return success(true);
57 54 }
58 55  
59   - // TODO @wangjingyi:下面几个 VO 也参考我在 account 给的建议
  56 + // TODO @wangjingyi:下面几个 VO 也参考我在 account 给的建议 DONE RespVO中需要BaseVO 中哪些字段
60 57  
61 58 @GetMapping("/get")
62 59 @ApiOperation("获得邮箱模版")
63 60 @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
64 61 @PreAuthorize("@ss.hasPermission('system:mail-template:get')")
65   - public CommonResult<MailTemplateBaseVO> getMailTemplate(@RequestParam("id") Long id) {
  62 + public CommonResult<MailTemplateRespVO> getMailTemplate(@RequestParam("id") Long id) {
66 63 MailTemplateDO mailTemplateDO = mailTempleService.getMailTemplate(id);
67 64 return success(MailTemplateConvert.INSTANCE.convert(mailTemplateDO));
68 65 }
... ... @@ -70,25 +67,17 @@ public class MailTemplateController {
70 67 @GetMapping("/page")
71 68 @ApiOperation("获得邮箱模版分页")
72 69 @PreAuthorize("@ss.hasPermission('system:mail-template:query')")
73   - public CommonResult<PageResult<MailTemplateBaseVO>> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) {
  70 + public CommonResult<PageResult<MailTemplateRespVO>> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) {
74 71 PageResult<MailTemplateDO> pageResult = mailTempleService.getMailTemplatePage(pageReqVO);
75 72 return success(MailTemplateConvert.INSTANCE.convertPage(pageResult));
76 73 }
77 74  
78 75 @GetMapping("/list-all-simple")
79 76 @ApiOperation(value = "获得邮箱模版精简列表")
80   - public CommonResult<List<MailTemplateBaseVO>> getSimpleTemplateList() {
  77 + public CommonResult<List<MailTemplateRespVO>> getSimpleTemplateList() {
81 78 List<MailTemplateDO> list = mailTempleService.getMailTemplateList();
82 79 // 排序后,返回给前端
83 80 list.sort(Comparator.comparing(MailTemplateDO::getId));
84 81 return success(MailTemplateConvert.INSTANCE.convertList02(list));
85 82 }
86   -
87   - @PostMapping("/send")
88   - @ApiOperation("发送邮件")
89   - @PreAuthorize("@ss.hasPermission('system:mail-template:send')")
90   - public CommonResult<Boolean> sendMail(@Valid @RequestBody MailReqVO mailReqVO){
91   - mailTempleService.sendMail(mailReqVO);
92   - return success(true);
93   - }
94 83 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java
... ... @@ -20,10 +20,6 @@ public class MailAccountBaseVO {
20 20 @Email(message = "必须是Email格式")
21 21 private String username;
22 22  
23   - @ApiModelProperty(value = "密码",required = true,example = "123456")
24   - @NotNull(message = "密码必填")
25   - private String password;
26   -
27 23 @ApiModelProperty(value = "网站",required = true,example = "www.iocoder.cn")
28 24 @NotNull(message = "网站必填")
29 25 private String host;
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountCreateReqVO.java
1 1 package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
2 2  
3 3 import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
4 5 import lombok.Data;
5 6 import lombok.EqualsAndHashCode;
6 7 import lombok.ToString;
7 8  
  9 +import javax.validation.constraints.NotNull;
  10 +
8 11 @ApiModel("管理后台 - 邮箱账号创建 Request VO")
9 12 @Data
10 13 @EqualsAndHashCode(callSuper = true)
11 14 @ToString(callSuper = true)
12 15 public class MailAccountCreateReqVO extends MailAccountBaseVO {
13 16  
  17 + @ApiModelProperty(value = "密码",required = true,example = "123456")
  18 + @NotNull(message = "密码必填")
  19 + private String password;
14 20 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountUpdateReqVO.java
... ... @@ -18,4 +18,7 @@ public class MailAccountUpdateReqVO extends MailAccountBaseVO {
18 18 @NotNull(message = "编号不能为空")
19 19 private Long id;
20 20  
  21 + @ApiModelProperty(value = "密码",required = true,example = "123456")
  22 + @NotNull(message = "密码必填")
  23 + private String password;
21 24 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +public class MailTemplateRespVO extends MailTemplateBaseVO{
  4 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailAccountConvert.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.extra.mail.MailAccount;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountBaseVO;
6 6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  7 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
7 8 import org.mapstruct.Mapper;
8 9 import org.mapstruct.factory.Mappers;
9 10  
... ... @@ -23,7 +24,7 @@ public interface MailAccountConvert {
23 24  
24 25 List<MailAccountBaseVO> convertList02(List<MailAccountDO> list);
25 26  
26   - default MailAccount convertAccount(MailAccountDO mailAccountDO){
  27 + default MailAccount convertAccount(MailSendMessage mailAccountDO){
27 28 return new MailAccount()
28 29 .setHost(mailAccountDO.getHost())
29 30 .setPort(mailAccountDO.getPort())
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailTemplateConvert.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.convert.mail;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateBaseVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateRespVO;
5 6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
6 7 import org.mapstruct.Mapper;
7 8 import org.mapstruct.factory.Mappers;
... ... @@ -14,9 +15,9 @@ public interface MailTemplateConvert {
14 15  
15 16 MailTemplateDO convert(MailTemplateBaseVO baseVO);
16 17  
17   - MailTemplateBaseVO convert(MailTemplateDO mailTemplateDO);
  18 + MailTemplateRespVO convert(MailTemplateDO mailTemplateDO);
18 19  
19   - PageResult<MailTemplateBaseVO> convertPage(PageResult<MailTemplateDO> pageResult);
  20 + PageResult<MailTemplateRespVO> convertPage(PageResult<MailTemplateDO> pageResult);
20 21  
21   - List<MailTemplateBaseVO> convertList02(List<MailTemplateDO> list);
  22 + List<MailTemplateRespVO> convertList02(List<MailTemplateDO> list);
22 23 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java
... ... @@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
8 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
9 9 import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Select;
  11 +
  12 +import java.util.Date;
10 13  
11 14 @Mapper
12 15 public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
... ... @@ -25,4 +28,18 @@ public interface MailAccountMapper extends BaseMapperX&lt;MailAccountDO&gt; {
25 28 return selectOne(new QueryWrapperX<MailAccountDO>()
26 29 .eqIfPresent("username" , userName));
27 30 };
  31 +
  32 + default MailAccountDO selectByUserNameAndId(String userName,Long id){
  33 + return selectOne(new QueryWrapperX<MailAccountDO>()
  34 + .eqIfPresent("username" , userName)
  35 + .neIfPresent("id" , id));
  36 + };
  37 +
  38 + default MailAccountDO selectOneByFrom(String from){
  39 + return selectOne(new QueryWrapperX<MailAccountDO>()
  40 + .eqIfPresent("from" , from));
  41 + };
  42 +
  43 + @Select("SELECT COUNT(*) FROM system_mail_account WHERE update_time > #{maxUpdateTime}")
  44 + Long selectCountByUpdateTimeGt(Date maxUpdateTime);
28 45 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java
... ... @@ -30,7 +30,7 @@ public interface MailTemplateMapper extends BaseMapperX&lt;MailTemplateDO&gt; {
30 30 .eqIfPresent("code" , code));
31 31 };
32 32  
33   - @Select("SELECT id FROM system_mail_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
  33 + @Select("SELECT COUNT(*) FROM system_mail_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
34 34 Long selectByMaxUpdateTime(Date maxUpdateTime);
35 35  
36 36 default MailTemplateDO selectOneByAccountId(Long accountId){
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.consumer.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
  5 +import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
  6 +import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.annotation.Resource;
  11 +
  12 +/**
  13 + * 针对 {@link MailTemplateRefreshMessage} 的消费者
  14 + *
  15 + * @author wangjingyi
  16 + */
  17 +@Component
  18 +@Slf4j
  19 +public class MailTemplateRefreshConsumer extends AbstractChannelMessageListener<MailTemplateRefreshMessage> {
  20 +
  21 + @Resource
  22 + private MailTemplateService mailTemplateService;
  23 +
  24 + @Override
  25 + public void onMessage(MailTemplateRefreshMessage message) {
  26 + log.info("[onMessage][收到 MailTemplate 刷新信息]");
  27 + mailTemplateService.initLocalCache();
  28 + }
  29 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.message.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +
  7 +/**
  8 + * 邮箱账号的数据刷新 Message
  9 + *
  10 + * @author wangjingyi
  11 + */
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +public class MailAccountRefreshMessage extends AbstractChannelMessage {
  15 + @Override
  16 + public String getChannel() {
  17 + return "system.mail-account.refresh";
  18 + }
  19 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
... ... @@ -28,6 +28,16 @@ public class MailSendMessage extends AbstractStreamMessage {
28 28 @NotNull(message = "邮箱地址不能为空")
29 29 private String from;
30 30 /**
  31 + * 用户名
  32 + */
  33 + @NotNull(message = "用户名不能为空")
  34 + private String username;
  35 + /**
  36 + * 密码
  37 + */
  38 + @NotNull(message = "密码不能为空")
  39 + private String password;
  40 + /**
31 41 * 邮箱模板编号
32 42 */
33 43 @NotNull(message = "邮箱模板编号不能为空")
... ... @@ -45,6 +55,20 @@ public class MailSendMessage extends AbstractStreamMessage {
45 55 * 内容
46 56 */
47 57 private String content;
  58 + /**
  59 + * 主机
  60 + */
  61 + @NotNull(message = "host不能为空")
  62 + private String host;
  63 + /**
  64 + * 端口
  65 + */
  66 + @NotNull(message = "端口号不能为空")
  67 + private Integer port;
  68 + /**
  69 + * 是否开启 SSL
  70 + */
  71 + private Boolean sslEnable;
48 72  
49 73 @Override
50 74 public String getStreamKey() {
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.message.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +
  7 +/**
  8 + * 邮箱模板的数据刷新 Message
  9 + *
  10 + * @author wangjingyi
  11 + */
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +public class MailTemplateRefreshMessage extends AbstractChannelMessage {
  15 + @Override
  16 + public String getChannel() {
  17 + return "system.mail-template.refresh";
  18 + }
  19 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
... ... @@ -4,7 +4,9 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
4 4 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
5 5 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
6 6 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  7 +import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage;
7 8 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  9 +import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
8 10 import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;
9 11 import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
10 12 import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
... ... @@ -28,18 +30,18 @@ public class MailProducer {
28 30 private RedisMQTemplate redisMQTemplate;
29 31  
30 32 /**
31   - * 发送 {@link SmsChannelRefreshMessage} 消息
  33 + * 发送 {@link MailTemplateRefreshMessage} 消息
32 34 */
33   - public void sendMailChannelRefreshMessage() {
34   - SmsChannelRefreshMessage message = new SmsChannelRefreshMessage();
  35 + public void sendMailTemplateRefreshMessage() {
  36 + MailTemplateRefreshMessage message = new MailTemplateRefreshMessage();
35 37 redisMQTemplate.send(message);
36 38 }
37 39  
38 40 /**
39   - * 发送 {@link SmsTemplateRefreshMessage} 消息
  41 + * 发送 {@link MailTemplateRefreshMessage} 消息
40 42 */
41   - public void sendMailTemplateRefreshMessage() {
42   - SmsTemplateRefreshMessage message = new SmsTemplateRefreshMessage();
  43 + public void sendMailAccountRefreshMessage() {
  44 + MailAccountRefreshMessage message = new MailAccountRefreshMessage();
43 45 redisMQTemplate.send(message);
44 46 }
45 47  
... ... @@ -56,6 +58,11 @@ public class MailProducer {
56 58 MailSendMessage message = new MailSendMessage();
57 59 message.setContent(content);
58 60 message.setFrom(mailAccountDO.getFrom());
  61 + message.setHost(mailAccountDO.getHost());
  62 + message.setPort(mailAccountDO.getPort());
  63 + message.setPassword(mailAccountDO.getPassword());
  64 + message.setUsername(mailAccountDO.getUsername());
  65 + message.setSslEnable(mailAccountDO.getSslEnable());
59 66 message.setTemplateCode(mailTemplateDO.getCode());
60 67 message.setTitle(title);
61 68 message.setTos(tos);
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java
... ... @@ -18,6 +18,11 @@ import java.util.List;
18 18 public interface MailAccountService {
19 19  
20 20 /**
  21 + * 初始化邮箱账号的本地缓存
  22 + */
  23 + void initLocalCache();
  24 +
  25 + /**
21 26 * 创建邮箱账号
22 27 *
23 28 * @param createReqVO 邮箱账号信息
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java
... ... @@ -46,5 +46,11 @@ public interface MailLogService {
46 46 */
47 47 Long createMailLog(MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String from, String content, List<String> tos, String title, Boolean isSend);
48 48  
49   - Long updateSmsSendResult(Long logId, String result);
  49 + /**
  50 + * 更新邮件发送结果
  51 + *
  52 + * @param logId 发送日志Id
  53 + * @param result 发送结果 默认返回messageId
  54 + */
  55 + void updateMailSendResult(Long logId, String result);
50 56 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java
... ... @@ -19,6 +19,9 @@ import java.util.Map;
19 19 */
20 20 public interface MailTemplateService {
21 21  
  22 + /**
  23 + * 初始化邮箱模版的本地缓存
  24 + */
22 25 void initLocalCache();
23 26  
24 27 /**
... ... @@ -74,17 +77,10 @@ public interface MailTemplateService {
74 77 MailTemplateDO getMailTemplateByCodeFromCache(String code);
75 78  
76 79 /**
77   - * 发送邮件
78   - *
79   - * @param mailReqVO 邮件发送信息
80   - */
81   - void sendMail(MailReqVO mailReqVO);
82   -
83   - /**
84 80 * 邮件模版内容合成
85 81 * @param content 邮箱模版
86 82 * @param params 合成参数
87 83 * @return
88 84 */
89   - String formateMailTemplateContent(String content, Map<String, String> params);
  85 + String formatMailTemplateContent(String content, Map<String, String> params);
90 86 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.mail.impl;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 6 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
5 7 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
6 8 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
7 9 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
8 10 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
9 11 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  12 +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
10 13 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
11 14 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
  15 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
12 16 import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
  17 +import lombok.extern.slf4j.Slf4j;
13 18 import org.springframework.stereotype.Service;
14 19 import org.springframework.validation.annotation.Validated;
15 20  
  21 +import javax.annotation.PostConstruct;
16 22 import javax.annotation.Resource;
  23 +import java.util.Date;
17 24 import java.util.List;
  25 +import java.util.Map;
18 26  
19 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
... ... @@ -28,6 +36,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
28 36 */
29 37 @Service
30 38 @Validated
  39 +@Slf4j
31 40 public class MailAccountServiceImpl implements MailAccountService {
32 41  
33 42 @Resource
... ... @@ -36,26 +45,71 @@ public class MailAccountServiceImpl implements MailAccountService {
36 45 @Resource
37 46 private MailTemplateMapper mailTemplateMapper;
38 47  
  48 + @Resource
  49 + private MailProducer mailProducer;
  50 +
  51 + /**
  52 + * 邮箱账号缓存
  53 + * key:邮箱账号编码 {@link MailAccountDO#getId()}
  54 + *
  55 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  56 + */
  57 + private volatile Map<Long, MailAccountDO> mailAccountCache;
  58 +
  59 + /**
  60 + * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
  61 + */
  62 + private volatile Date maxUpdateTime;
  63 +
  64 + @Override
  65 + @PostConstruct
  66 + public void initLocalCache() {
  67 + List<MailAccountDO> mailAccountDOList = this.loadMailAccountIfUpdate(maxUpdateTime);
  68 + if (CollUtil.isEmpty(mailAccountDOList)) {
  69 + return;
  70 + }
  71 +
  72 + // 写入缓存
  73 + mailAccountCache = CollectionUtils.convertMap(mailAccountDOList, MailAccountDO::getId);
  74 + maxUpdateTime = CollectionUtils.getMaxValue(mailAccountDOList, MailAccountDO::getUpdateTime);
  75 + log.info("[initLocalCache][初始化 MailAccount 数量为 {}]", mailAccountDOList.size());
  76 + }
  77 +
  78 + private List<MailAccountDO> loadMailAccountIfUpdate(Date maxUpdateTime) {
  79 + //第一步 判断是否需要更新
  80 + if(null == maxUpdateTime){ // 如果更新时间为空,说明 DB 一定有新数据
  81 + log.info("[loadMailAccountIfUpdate][首次加载全量账号信息]");
  82 + }else{ // 判断数据库中是否有更新的账号信息
  83 + if (mailAccountMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
  84 + return null;
  85 + }
  86 + log.info("[loadMailAccountIfUpdate][增量加载全量账号信息]");
  87 + }
  88 + return mailAccountMapper.selectList();
  89 + }
  90 +
39 91 @Override
40 92 public Long create(MailAccountCreateReqVO createReqVO) {
41 93 // username 要校验唯一
42   - validateMailAccountOnlyByUserName(createReqVO.getUsername());
  94 + this.validateMailAccountOnlyByUserName(createReqVO.getUsername());
43 95 MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(createReqVO);
44 96 mailAccountMapper.insert(mailAccountDO);
45 97  
46 98 // 更新
  99 + mailProducer.sendMailAccountRefreshMessage();
47 100 return mailAccountDO.getId();
48 101 }
49 102  
50 103 @Override
51 104 public void update(MailAccountUpdateReqVO updateReqVO) {
52   - // username 要校验唯一 TODO @wangjingyi:校验唯一的时候,需要排除掉自己
53   - validateMailAccountExists(updateReqVO.getId());
  105 + // username 要校验唯一 TODO @wangjingyi:校验唯一的时候,需要排除掉自己 DONE
  106 + this.validateMailAccountOnlyByUserNameAndId(updateReqVO.getUsername(),updateReqVO.getId());
54 107 MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO);
55 108 // 校验是否存在
56 109 validateMailAccountExists(mailAccountDO.getId());
57 110  
58 111 // 更新
  112 + mailProducer.sendMailAccountRefreshMessage();
59 113 mailAccountMapper.updateById(mailAccountDO);
60 114 }
61 115  
... ... @@ -65,9 +119,10 @@ public class MailAccountServiceImpl implements MailAccountService {
65 119 validateMailAccountExists(id);
66 120 // 校验是否存在关联模版
67 121 validateMailTemplateByAccountId(id);
68   -
69 122 // 删除
70 123 mailAccountMapper.deleteById(id);
  124 + // 更新
  125 + mailProducer.sendMailAccountRefreshMessage();
71 126 }
72 127  
73 128 @Override
... ... @@ -92,17 +147,26 @@ public class MailAccountServiceImpl implements MailAccountService {
92 147 }
93 148  
94 149 private void validateMailAccountOnlyByUserName(String userName){
95   - MailAccountDO mailAccountDO = mailAccountMapper.selectByUserName(userName);
96   - if (mailAccountDO != null) {
97   - throw exception(MAIL_ACCOUNT_EXISTS);
98   - }
  150 + mailAccountCache.forEach((key,value)->{
  151 + if(value.getUsername().equals(userName)){
  152 + throw exception(MAIL_ACCOUNT_EXISTS);
  153 + }
  154 + });
  155 + }
  156 + private void validateMailAccountOnlyByUserNameAndId(String userName,Long id){
  157 + mailAccountCache.forEach((key , value)->{
  158 + if (value.getUsername().equals(userName)){
  159 + if (!key.equals(id)){
  160 + throw exception(MAIL_ACCOUNT_EXISTS);
  161 + }
  162 + }
  163 + });
99 164 }
100   -
101 165 private void validateMailTemplateByAccountId(Long accountId){
102 166 MailTemplateDO mailTemplateDO = mailTemplateMapper.selectOneByAccountId(accountId);
103 167 if (mailTemplateDO != null) {
104   - // TODO wangjingyi:MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS
105   - throw exception(MAIL_RELATE_TEMPLATE_EXISTS);
  168 + // TODO wangjingyi:MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS DONE
  169 + throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS);
106 170 }
107 171 }
108 172 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailLogServiceImpl.java
... ... @@ -8,13 +8,11 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
8 8 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
9 9 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
10 10 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
11   -import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
12 11 import cn.iocoder.yudao.module.system.service.mail.MailLogService;
13 12 import org.springframework.beans.factory.annotation.Autowired;
14 13 import org.springframework.stereotype.Service;
15 14 import org.springframework.validation.annotation.Validated;
16 15  
17   -import java.sql.Timestamp;
18 16 import java.util.Date;
19 17 import java.util.List;
20 18 import java.util.Objects;
... ... @@ -61,21 +59,14 @@ public class MailLogServiceImpl implements MailLogService {
61 59 return mailLogDO.getId();
62 60 }
63 61  
64   - // TODO @wangjingyi:不需要返回 id 呀
  62 + // TODO @wangjingyi:不需要返回 id 呀 DONE
65 63 @Override
66   - public Long updateSmsSendResult(Long logId, String result) {
  64 + public void updateMailSendResult(Long logId, String result) {
67 65 MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
68 66 logDOBuilder.id(logId);
69 67 logDOBuilder.sendResult(result);
70 68 MailLogDO mailLogDO = logDOBuilder.build();
71 69 mailLogMapper.updateById(mailLogDO);
72   - return logId;
73 70 }
74 71  
75   - // TODO @wangjingyi:无用的方法,需要进行删除
76   - public Long create(){
77   - MailLogDO mailLogDO = new MailLogDO();
78   - mailLogMapper.insert(mailLogDO);
79   - return mailLogDO.getId();
80   - }
81 72 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.mail.impl;
2 2  
3 3 import cn.hutool.extra.mail.MailAccount;
  4 +import cn.hutool.extra.mail.MailUtil;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 6 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
6 7 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
... ... @@ -21,8 +22,7 @@ import java.util.List;
21 22 import java.util.Map;
22 23  
23 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
24   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_EXISTS;
25   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
  25 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
26 26  
27 27 /**
28 28 * 邮箱模版 服务实现类
... ... @@ -49,36 +49,47 @@ public class MailSendServiceImpl implements MailSendService {
49 49  
50 50 @Override
51 51 public void sendMail(String templateCode, String from , String content , List<String> tos , String title) {
52   - // TODO @@wangjingyi:发送的时候,参考下短信;
  52 + // TODO @@wangjingyi:发送的时候,参考下短信;DONE
  53 + //校验邮箱模版是否合法
53 54 MailTemplateDO mailTemplateDO = this.checkMailTemplateValid(templateCode);
54 55 // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
55 56 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(mailTemplateDO.getStatus());
56   - //查询账号信息
57   - MailAccountDO mailAccountDO = mailAccountMapper.selectOne(
58   - "from", from
59   - );
  57 + //校验账号信息是否合法
  58 + MailAccountDO mailAccountDO = this.checkMailAccountValid(from);
60 59 Map<String , String> params = MailAccountConvert.INSTANCE.convertToMap(mailAccountDO , content);
61   - content = mailTemplateService.formateMailTemplateContent(mailTemplateDO.getContent(), params);
  60 + content = mailTemplateService.formatMailTemplateContent(mailTemplateDO.getContent(), params);
62 61 Long sendLogId = mailLogService.createMailLog(mailAccountDO , mailTemplateDO , from , content , tos , title , isSend);
63 62  
64 63 // 后续功能 TODO :附件查询
65 64 //List<String> fileIds = mailSendVO.getFileIds();
66 65  
67   - //装载账号信息
68   - MailAccount account = MailAccountConvert.INSTANCE.convertAccount(mailAccountDO);
69   -
70 66 // 发送 MQ 消息,异步执行发送短信
71 67 if (isSend) {
72 68 mailProducer.sendMailSendMessage(mailAccountDO , mailTemplateDO ,content , tos , title , sendLogId);
73 69 }
74 70 }
75 71  
  72 + private MailAccountDO checkMailAccountValid(String from) {
  73 + MailAccountDO mailAccountDO = mailAccountMapper.selectOneByFrom(from);
  74 + if(null == mailAccountDO){
  75 + throw exception(MAIL_ACCOUNT_NOT_EXISTS);
  76 + }
  77 + return mailAccountDO;
  78 + }
  79 +
76 80 @Override
77 81 public void doSendMail(MailSendMessage message) {
78   - // TODO @wangjingyi:直接使用 hutool 发送,不要封装 mail client 哈,因为短信的客户端都是比较统一的
79   - //MailClient mailClient = mailClientFactory.getMailClient();
80   - //String result = mailClient.sendMail(message.getFrom() , message.getContent() , message.getTitle() , message.getTos());
81   - //mailLogService.updateSmsSendResult(message.getLogId() , result);
  82 + // TODO @wangjingyi:直接使用 hutool 发送,不要封装 mail client 哈,因为短信的客户端都是比较统一的 DONE
  83 + //装载账号信息
  84 + MailAccount account = MailAccountConvert.INSTANCE.convertAccount(message);
  85 + //发送邮件
  86 + try{
  87 + String messageId = MailUtil.send(account,message.getTos(),message.getTitle(),message.getContent(),false,null);
  88 + mailLogService.updateMailSendResult(message.getLogId() , messageId);
  89 + }catch (Exception e){
  90 + mailLogService.updateMailSendResult(message.getLogId() , e.getMessage());
  91 + }
  92 +
82 93 }
83 94  
84 95 private MailTemplateDO checkMailTemplateValid(String templateCode) {
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java
... ... @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
18 18 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
19 19 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
20 20 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
  21 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
21 22 import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
22 23 import lombok.extern.slf4j.Slf4j;
23 24 import org.springframework.stereotype.Service;
... ... @@ -48,59 +49,53 @@ public class MailTemplateServiceImpl implements MailTemplateService {
48 49 @Resource
49 50 private MailTemplateMapper mailTemplateMapper;
50 51 @Resource
51   - private MailAccountMapper mailAccountMapper;
52   -
53   - private volatile List<MailTemplateDO> mailTemplateDOList;
  52 + private MailProducer mailProducer;
54 53  
55 54 /**
56 55 * 邮件模板缓存
57   - * key:邮箱模板编码 {@link MailTemplateDO#getCode()}
  56 + * key:邮箱模板编码 {@link MailTemplateDO#getId()}
58 57 *
59 58 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
60 59 */
61   - private volatile Map<String, MailTemplateDO> mailTemplateCache;
  60 + private volatile Map<Long, MailTemplateDO> mailTemplateCache;
62 61  
63 62 private volatile Date maxUpdateTime;
64 63  
65   - // TODO @wangjingyi:参考下别的模块的 initLocalCache 的实现
  64 + // TODO @wangjingyi:参考下别的模块的 initLocalCache 的实现 DONE
66 65 @Override
67 66 @PostConstruct
68 67 public void initLocalCache() {
69   - if(maxUpdateTime == null){
70   - mailTemplateDOList = mailTemplateMapper.selectList();
71   - }else{
72   - if(mailTemplateMapper.selectByMaxUpdateTime(maxUpdateTime)<=0){
73   - return;
74   - }
75   - }
  68 + List<MailTemplateDO> mailTemplateDOList = this.loadMailTemplateIfUpdate(maxUpdateTime);
76 69 if (CollUtil.isEmpty(mailTemplateDOList)) {
77 70 return;
78 71 }
79 72  
80 73 // 写入缓存
81   - mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getCode);
  74 + mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getId);
82 75 maxUpdateTime = CollectionUtils.getMaxValue(mailTemplateDOList, MailTemplateDO::getUpdateTime);
83 76 log.info("[initLocalCache][初始化 mailTemplate 数量为 {}]", mailTemplateDOList.size());
84 77 }
85 78  
86 79 @Override
87 80 public Long create(MailTemplateCreateReqVO createReqVO) {
88   - // code 要校验唯一
89   - // TODO @wangjingyi:参考下我在 account 给的唯一校验的说明。
90   - this.validateMailTemplateOnlyByCode(createReqVO.getCode());
  81 + //要校验存在
  82 + this.validateMailTemplateExists(createReqVO.getId());
91 83 MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(createReqVO);
92 84 mailTemplateMapper.insert(mailTemplateDO);
93   - // TODO @wangjingyi:mq 更新
  85 + // TODO @wangjingyi:mq 更新 DONE
  86 + mailProducer.sendMailTemplateRefreshMessage();
94 87 return mailTemplateDO.getId();
95 88 }
96 89  
97 90 @Override
98 91 public void update(@Valid MailTemplateUpdateReqVO updateReqVO) {
99   - // 校验是否存在
100   - this.validateMailTemplateExists(updateReqVO.getId());
  92 + // 校验是否唯一
  93 + // TODO @wangjingyi:参考下我在 account 给的唯一校验的说明。
  94 + this.validateMailTemplateOnlyByCode(updateReqVO.getId(),updateReqVO.getCode());
101 95 MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
102 96 mailTemplateMapper.updateById(mailTemplateDO);
103   - // TODO @wangjingyi:mq 更新
  97 + // TODO @wangjingyi:mq 更新 DONE
  98 + mailProducer.sendMailTemplateRefreshMessage();
104 99 }
105 100  
106 101 @Override
... ... @@ -108,7 +103,8 @@ public class MailTemplateServiceImpl implements MailTemplateService {
108 103 // 校验是否存在
109 104 this.validateMailTemplateExists(id);
110 105 mailTemplateMapper.deleteById(id);
111   - // TODO @wangjingyi:mq 更新
  106 + // TODO @wangjingyi:mq 更新 DONE
  107 + mailProducer.sendMailTemplateRefreshMessage();
112 108 }
113 109  
114 110 @Override
... ... @@ -127,43 +123,45 @@ public class MailTemplateServiceImpl implements MailTemplateService {
127 123 return mailTemplateCache.get(code);
128 124 }
129 125  
  126 + // TODO @@wangjingyi:单词拼写错误 DONE
130 127 @Override
131   - public void sendMail(MailReqVO mailReqVO) {
132   - // TODO @@wangjingyi:发送的时候,参考下短信;
133   - MailTemplateDO mailTemplateDO = mailTemplateMapper.selectById(mailReqVO.getTemplateId());
134   - //查询账号信息
135   - MailAccountDO mailAccountDO = mailAccountMapper.selectOne(
136   - "from", mailReqVO.getFrom()
137   - );
138   - String content = mailReqVO.getContent();
139   - Map<String , String> params = MailAccountConvert.INSTANCE.convertToMap(mailAccountDO , content);
140   - content = StrUtil.format(mailTemplateDO.getContent(), params);
141   -
142   - // 后续功能 TODO :附件查询
143   - //List<String> fileIds = mailSendVO.getFileIds();
144   -
145   - //装载账号信息
146   - MailAccount account = MailAccountConvert.INSTANCE.convertAccount(mailAccountDO);
147   -
148   - //发送
149   - MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false);
150   - }
151   -
152   - // TODO @@wangjingyi:单词拼写错误
153   - @Override
154   - public String formateMailTemplateContent(String content, Map<String, String> params) {
  128 + public String formatMailTemplateContent(String content, Map<String, String> params) {
155 129 return StrUtil.format(content, params);
156 130 }
157 131  
158 132 private void validateMailTemplateExists(Long id) {
159   - if (mailTemplateMapper.selectById(id) == null) {
  133 + if (mailTemplateCache.get(id) == null) {
160 134 throw exception(MAIL_TEMPLATE_NOT_EXISTS);
161 135 }
162 136 }
163 137  
164   - private void validateMailTemplateOnlyByCode(String code){
165   - if (mailTemplateMapper.selectOneByCode(code) != null) {
166   - throw exception(MAIL_TEMPLATE_EXISTS);
  138 + private void validateMailTemplateOnlyByCode(Long id ,String code){
  139 + mailTemplateCache.forEach((key,value)->{
  140 + if (value.getCode().equals(code)){
  141 + if (!key.equals(id)){
  142 + throw exception(MAIL_TEMPLATE_EXISTS);
  143 + }
  144 + }
  145 + });
  146 + }
  147 + /**
  148 + * 如果邮件模板发生变化,从数据库中获取最新的全量邮件模板。
  149 + * 如果未发生变化,则返回空
  150 + *
  151 + * @param maxUpdateTime 当前邮件模板的最大更新时间
  152 + * @return 邮件模板列表
  153 + */
  154 + private List<MailTemplateDO> loadMailTemplateIfUpdate(Date maxUpdateTime) {
  155 + // 第一步,判断是否要更新。
  156 + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
  157 + log.info("[loadMailTemplateIfUpdate][首次加载全量邮件模板]");
  158 + } else { // 判断数据库中是否有更新的邮件模板
  159 + if (mailTemplateMapper.selectByMaxUpdateTime(maxUpdateTime) == 0) {
  160 + return null;
  161 + }
  162 + log.info("[loadSmsTemplateIfUpdate][增量加载全量邮件模板]");
167 163 }
  164 + // 第二步,如果有更新,则从数据库加载所有邮件模板
  165 + return mailTemplateMapper.selectList();
168 166 }
169 167 }
... ...