Эх сурвалжийг харах

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

wangjingyi 3 жил өмнө
parent
commit
8bc5254e30
23 өөрчлөгдсөн 318 нэмэгдсэн , 132 устгасан
  1. 1 1
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
  2. 2 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java
  3. 7 18
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java
  4. 0 4
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java
  5. 6 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountCreateReqVO.java
  6. 3 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountUpdateReqVO.java
  7. 4 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java
  8. 2 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailAccountConvert.java
  9. 4 3
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailTemplateConvert.java
  10. 17 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java
  11. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java
  12. 29 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java
  13. 19 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java
  14. 24 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
  15. 19 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java
  16. 13 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
  17. 5 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java
  18. 7 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java
  19. 4 8
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java
  20. 75 11
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java
  21. 2 11
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailLogServiceImpl.java
  22. 26 15
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java
  23. 48 50
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailTemplateServiceImpl.java

+ 1 - 1
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 {
     // ========== 邮箱模版 1002021000 ==========
     ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002021000 , "邮箱模版不存在");
     ErrorCode MAIL_TEMPLATE_EXISTS = new ErrorCode(1002021001, "邮箱模版存在");
-    ErrorCode MAIL_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002021002, "存在关联邮箱模版");
+    ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002021002, "存在关联邮箱模版");
 
 }

+ 2 - 2
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 {
     }
 
     // TODO @wangjingyi:getMailAccount 和 getMailAccountPage 这两个接口,定义一个对应的 Resp 类哈,参考别的模块。主要不要返回 password 字段。
-    // 一个可以的做法,是 MailAccountBaseVO 不返回 password,然后 MailAccountCreateReqVO、MailAccountUpdateReqVO 添加这个字段
+    // 一个可以的做法,是 MailAccountBaseVO 不返回 password,然后 MailAccountCreateReqVO、MailAccountUpdateReqVO 添加这个字段 DONE
 
     @GetMapping("/get")
     @ApiOperation("获得邮箱账号")
@@ -76,7 +76,7 @@ public class MailAccountController {
         return success(MailAccountConvert.INSTANCE.convertPage(pageResult));
     }
 
-    // TODO @wangjingyi:getSimpleMailAccountList 单独定义一个类,只返回精简的信息,id,from 即可。像密码之类都是敏感信息,不应该返回
+    // TODO @wangjingyi:getSimpleMailAccountList 单独定义一个类,只返回精简的信息,id,from 即可。像密码之类都是敏感信息,不应该返回 DONE
 
     @GetMapping("/list-all-simple")
     @ApiOperation(value = "获得邮箱账号精简列表")

+ 7 - 18
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;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.send.MailReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateBaseVO;
-import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
-import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.*;
 import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
 import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
@@ -29,9 +26,9 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 @RequestMapping("/system/mail-template")
 public class MailTemplateController {
 
-    // TODO @wangjingyi:private
+    // TODO @wangjingyi:private DONE
     @Autowired
-    MailTemplateService mailTempleService;
+    private MailTemplateService mailTempleService;
 
     @PostMapping("/create")
     @ApiOperation("创建邮箱模版")
@@ -56,13 +53,13 @@ public class MailTemplateController {
         return success(true);
     }
 
-    // TODO @wangjingyi:下面几个 VO 也参考我在 account 给的建议
+    // TODO @wangjingyi:下面几个 VO 也参考我在 account 给的建议 DONE RespVO中需要BaseVO 中哪些字段
 
     @GetMapping("/get")
     @ApiOperation("获得邮箱模版")
     @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
     @PreAuthorize("@ss.hasPermission('system:mail-template:get')")
-    public CommonResult<MailTemplateBaseVO> getMailTemplate(@RequestParam("id") Long id) {
+    public CommonResult<MailTemplateRespVO> getMailTemplate(@RequestParam("id") Long id) {
         MailTemplateDO mailTemplateDO = mailTempleService.getMailTemplate(id);
         return success(MailTemplateConvert.INSTANCE.convert(mailTemplateDO));
     }
@@ -70,25 +67,17 @@ public class MailTemplateController {
     @GetMapping("/page")
     @ApiOperation("获得邮箱模版分页")
     @PreAuthorize("@ss.hasPermission('system:mail-template:query')")
-    public CommonResult<PageResult<MailTemplateBaseVO>> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) {
+    public CommonResult<PageResult<MailTemplateRespVO>> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) {
         PageResult<MailTemplateDO> pageResult = mailTempleService.getMailTemplatePage(pageReqVO);
         return success(MailTemplateConvert.INSTANCE.convertPage(pageResult));
     }
 
     @GetMapping("/list-all-simple")
     @ApiOperation(value = "获得邮箱模版精简列表")
-    public CommonResult<List<MailTemplateBaseVO>> getSimpleTemplateList() {
+    public CommonResult<List<MailTemplateRespVO>> getSimpleTemplateList() {
         List<MailTemplateDO> list = mailTempleService.getMailTemplateList();
         // 排序后,返回给前端
         list.sort(Comparator.comparing(MailTemplateDO::getId));
         return success(MailTemplateConvert.INSTANCE.convertList02(list));
     }
-
-    @PostMapping("/send")
-    @ApiOperation("发送邮件")
-    @PreAuthorize("@ss.hasPermission('system:mail-template:send')")
-    public CommonResult<Boolean> sendMail(@Valid @RequestBody MailReqVO mailReqVO){
-        mailTempleService.sendMail(mailReqVO);
-        return success(true);
-    }
 }

+ 0 - 4
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 {
     @Email(message = "必须是Email格式")
     private String username;
 
-    @ApiModelProperty(value = "密码",required = true,example = "123456")
-    @NotNull(message = "密码必填")
-    private String password;
-
     @ApiModelProperty(value = "网站",required = true,example = "www.iocoder.cn")
     @NotNull(message = "网站必填")
     private String host;

+ 6 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountCreateReqVO.java

@@ -1,14 +1,20 @@
 package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
 
 import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import javax.validation.constraints.NotNull;
+
 @ApiModel("管理后台 - 邮箱账号创建 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 public class MailAccountCreateReqVO extends MailAccountBaseVO {
 
+    @ApiModelProperty(value = "密码",required = true,example = "123456")
+    @NotNull(message = "密码必填")
+    private String password;
 }

+ 3 - 0
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 {
     @NotNull(message = "编号不能为空")
     private Long id;
 
+    @ApiModelProperty(value = "密码",required = true,example = "123456")
+    @NotNull(message = "密码必填")
+    private String password;
 }

+ 4 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java

@@ -0,0 +1,4 @@
+package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
+
+public class MailTemplateRespVO extends MailTemplateBaseVO{
+}

+ 2 - 1
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;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountBaseVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
@@ -23,7 +24,7 @@ public interface MailAccountConvert {
 
     List<MailAccountBaseVO> convertList02(List<MailAccountDO> list);
 
-    default MailAccount convertAccount(MailAccountDO mailAccountDO){
+    default MailAccount convertAccount(MailSendMessage mailAccountDO){
         return new MailAccount()
                 .setHost(mailAccountDO.getHost())
                 .setPort(mailAccountDO.getPort())

+ 4 - 3
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;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateBaseVO;
+import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateRespVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
@@ -14,9 +15,9 @@ public interface MailTemplateConvert {
 
     MailTemplateDO convert(MailTemplateBaseVO baseVO);
 
-    MailTemplateBaseVO convert(MailTemplateDO mailTemplateDO);
+    MailTemplateRespVO convert(MailTemplateDO mailTemplateDO);
 
-    PageResult<MailTemplateBaseVO> convertPage(PageResult<MailTemplateDO> pageResult);
+    PageResult<MailTemplateRespVO> convertPage(PageResult<MailTemplateDO> pageResult);
 
-    List<MailTemplateBaseVO> convertList02(List<MailTemplateDO> list);
+    List<MailTemplateRespVO> convertList02(List<MailTemplateDO> list);
 }

+ 17 - 0
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
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Date;
 
 @Mapper
 public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
@@ -25,4 +28,18 @@ public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
         return selectOne(new QueryWrapperX<MailAccountDO>()
                 .eqIfPresent("username" , userName));
     };
+
+    default MailAccountDO selectByUserNameAndId(String userName,Long id){
+        return selectOne(new QueryWrapperX<MailAccountDO>()
+                .eqIfPresent("username" , userName)
+                .neIfPresent("id" , id));
+    };
+
+    default MailAccountDO selectOneByFrom(String from){
+        return selectOne(new QueryWrapperX<MailAccountDO>()
+                .eqIfPresent("from" , from));
+    };
+
+    @Select("SELECT COUNT(*) FROM system_mail_account WHERE update_time > #{maxUpdateTime}")
+    Long selectCountByUpdateTimeGt(Date maxUpdateTime);
 }

+ 1 - 1
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<MailTemplateDO> {
                 .eqIfPresent("code" , code));
     };
 
-    @Select("SELECT id FROM system_mail_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
+    @Select("SELECT COUNT(*) FROM system_mail_template WHERE update_time > #{maxUpdateTime} LIMIT 1")
     Long selectByMaxUpdateTime(Date maxUpdateTime);
 
     default MailTemplateDO selectOneByAccountId(Long accountId){

+ 29 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.system.mq.consumer.mail;
+
+import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
+import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
+import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 针对 {@link MailTemplateRefreshMessage} 的消费者
+ *
+ * @author wangjingyi
+ */
+@Component
+@Slf4j
+public class MailTemplateRefreshConsumer extends AbstractChannelMessageListener<MailTemplateRefreshMessage> {
+
+    @Resource
+    private MailTemplateService mailTemplateService;
+
+    @Override
+    public void onMessage(MailTemplateRefreshMessage message) {
+        log.info("[onMessage][收到 MailTemplate 刷新信息]");
+        mailTemplateService.initLocalCache();
+    }
+}

+ 19 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.system.mq.message.mail;
+
+import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ *  邮箱账号的数据刷新 Message
+ *
+ * @author wangjingyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MailAccountRefreshMessage extends AbstractChannelMessage {
+    @Override
+    public String getChannel() {
+        return "system.mail-account.refresh";
+    }
+}

+ 24 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java

@@ -27,6 +27,16 @@ public class MailSendMessage extends AbstractStreamMessage {
      */
     @NotNull(message = "邮箱地址不能为空")
     private String from;
+    /**
+     * 用户名
+     */
+    @NotNull(message = "用户名不能为空")
+    private String username;
+    /**
+     * 密码
+     */
+    @NotNull(message = "密码不能为空")
+    private String password;
     /**
      * 邮箱模板编号
      */
@@ -45,6 +55,20 @@ public class MailSendMessage extends AbstractStreamMessage {
      * 内容
      */
     private String content;
+    /**
+     * 主机
+     */
+    @NotNull(message = "host不能为空")
+    private String host;
+    /**
+     * 端口
+     */
+    @NotNull(message = "端口号不能为空")
+    private Integer port;
+    /**
+     * 是否开启 SSL
+     */
+    private Boolean sslEnable;
 
     @Override
     public String getStreamKey() {

+ 19 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.system.mq.message.mail;
+
+import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ *  邮箱模板的数据刷新 Message
+ *
+ * @author wangjingyi
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MailTemplateRefreshMessage extends AbstractChannelMessage {
+    @Override
+    public String getChannel() {
+        return "system.mail-template.refresh";
+    }
+}

+ 13 - 6
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;
 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage;
 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
+import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
 import cn.iocoder.yudao.module.system.mq.message.sms.SmsChannelRefreshMessage;
 import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
 import cn.iocoder.yudao.module.system.mq.message.sms.SmsTemplateRefreshMessage;
@@ -28,18 +30,18 @@ public class MailProducer {
     private RedisMQTemplate redisMQTemplate;
 
     /**
-     * 发送 {@link SmsChannelRefreshMessage} 消息
+     * 发送 {@link MailTemplateRefreshMessage} 消息
      */
-    public void sendMailChannelRefreshMessage() {
-        SmsChannelRefreshMessage message = new SmsChannelRefreshMessage();
+    public void sendMailTemplateRefreshMessage() {
+        MailTemplateRefreshMessage message = new MailTemplateRefreshMessage();
         redisMQTemplate.send(message);
     }
 
     /**
-     * 发送 {@link SmsTemplateRefreshMessage} 消息
+     * 发送 {@link MailTemplateRefreshMessage} 消息
      */
-    public void sendMailTemplateRefreshMessage() {
-        SmsTemplateRefreshMessage message = new SmsTemplateRefreshMessage();
+    public void sendMailAccountRefreshMessage() {
+        MailAccountRefreshMessage message = new MailAccountRefreshMessage();
         redisMQTemplate.send(message);
     }
 
@@ -56,6 +58,11 @@ public class MailProducer {
         MailSendMessage message = new MailSendMessage();
         message.setContent(content);
         message.setFrom(mailAccountDO.getFrom());
+        message.setHost(mailAccountDO.getHost());
+        message.setPort(mailAccountDO.getPort());
+        message.setPassword(mailAccountDO.getPassword());
+        message.setUsername(mailAccountDO.getUsername());
+        message.setSslEnable(mailAccountDO.getSslEnable());
         message.setTemplateCode(mailTemplateDO.getCode());
         message.setTitle(title);
         message.setTos(tos);

+ 5 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java

@@ -17,6 +17,11 @@ import java.util.List;
  */
 public interface MailAccountService {
 
+    /**
+     * 初始化邮箱账号的本地缓存
+     */
+    void initLocalCache();
+
     /**
      * 创建邮箱账号
      *

+ 7 - 1
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 {
      */
     Long createMailLog(MailAccountDO mailAccountDO, MailTemplateDO mailTemplateDO, String from, String content, List<String> tos, String title, Boolean isSend);
 
-    Long updateSmsSendResult(Long logId, String result);
+    /**
+     * 更新邮件发送结果
+     *
+     * @param logId          发送日志Id
+     * @param result         发送结果 默认返回messageId
+     */
+    void updateMailSendResult(Long logId, String result);
 }

+ 4 - 8
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;
  */
 public interface MailTemplateService {
 
+    /**
+     * 初始化邮箱模版的本地缓存
+     */
     void initLocalCache();
 
     /**
@@ -73,18 +76,11 @@ public interface MailTemplateService {
      */
     MailTemplateDO getMailTemplateByCodeFromCache(String code);
 
-    /**
-     * 发送邮件
-     *
-     * @param mailReqVO 邮件发送信息
-     */
-    void sendMail(MailReqVO mailReqVO);
-
     /**
      * 邮件模版内容合成
      * @param content 邮箱模版
      * @param params 合成参数
      * @return
      */
-    String formateMailTemplateContent(String content, Map<String, String> params);
+    String formatMailTemplateContent(String content, Map<String, String> params);
 }

+ 75 - 11
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailAccountServiceImpl.java

@@ -1,20 +1,28 @@
 package cn.iocoder.yudao.module.system.service.mail.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
+import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
 import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
@@ -28,6 +36,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  */
 @Service
 @Validated
+@Slf4j
 public class MailAccountServiceImpl implements MailAccountService {
 
     @Resource
@@ -36,26 +45,71 @@ public class MailAccountServiceImpl implements MailAccountService {
     @Resource
     private MailTemplateMapper mailTemplateMapper;
 
+    @Resource
+    private MailProducer mailProducer;
+
+    /**
+     * 邮箱账号缓存
+     * key:邮箱账号编码 {@link MailAccountDO#getId()}
+     *
+     * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
+     */
+    private volatile Map<Long, MailAccountDO> mailAccountCache;
+
+    /**
+     * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
+     */
+    private volatile Date maxUpdateTime;
+
+    @Override
+    @PostConstruct
+    public void initLocalCache() {
+        List<MailAccountDO> mailAccountDOList = this.loadMailAccountIfUpdate(maxUpdateTime);
+        if (CollUtil.isEmpty(mailAccountDOList)) {
+            return;
+        }
+
+        // 写入缓存
+        mailAccountCache = CollectionUtils.convertMap(mailAccountDOList, MailAccountDO::getId);
+        maxUpdateTime = CollectionUtils.getMaxValue(mailAccountDOList, MailAccountDO::getUpdateTime);
+        log.info("[initLocalCache][初始化 MailAccount 数量为 {}]", mailAccountDOList.size());
+    }
+
+    private List<MailAccountDO> loadMailAccountIfUpdate(Date maxUpdateTime) {
+        //第一步 判断是否需要更新
+        if(null == maxUpdateTime){ // 如果更新时间为空,说明 DB 一定有新数据
+            log.info("[loadMailAccountIfUpdate][首次加载全量账号信息]");
+        }else{ // 判断数据库中是否有更新的账号信息
+            if (mailAccountMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
+                return null;
+            }
+            log.info("[loadMailAccountIfUpdate][增量加载全量账号信息]");
+        }
+        return mailAccountMapper.selectList();
+    }
+
     @Override
     public Long create(MailAccountCreateReqVO createReqVO) {
         // username 要校验唯一
-        validateMailAccountOnlyByUserName(createReqVO.getUsername());
+        this.validateMailAccountOnlyByUserName(createReqVO.getUsername());
         MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(createReqVO);
         mailAccountMapper.insert(mailAccountDO);
 
         // 更新
+        mailProducer.sendMailAccountRefreshMessage();
         return mailAccountDO.getId();
     }
 
     @Override
     public void update(MailAccountUpdateReqVO updateReqVO) {
-        // username 要校验唯一 TODO @wangjingyi:校验唯一的时候,需要排除掉自己
-        validateMailAccountExists(updateReqVO.getId());
+        // username 要校验唯一 TODO @wangjingyi:校验唯一的时候,需要排除掉自己 DONE
+        this.validateMailAccountOnlyByUserNameAndId(updateReqVO.getUsername(),updateReqVO.getId());
         MailAccountDO mailAccountDO = MailAccountConvert.INSTANCE.convert(updateReqVO);
         // 校验是否存在
         validateMailAccountExists(mailAccountDO.getId());
 
         // 更新
+        mailProducer.sendMailAccountRefreshMessage();
         mailAccountMapper.updateById(mailAccountDO);
     }
 
@@ -65,9 +119,10 @@ public class MailAccountServiceImpl implements MailAccountService {
         validateMailAccountExists(id);
         // 校验是否存在关联模版
         validateMailTemplateByAccountId(id);
-
         // 删除
         mailAccountMapper.deleteById(id);
+        // 更新
+        mailProducer.sendMailAccountRefreshMessage();
     }
 
     @Override
@@ -92,17 +147,26 @@ public class MailAccountServiceImpl implements MailAccountService {
     }
 
     private void validateMailAccountOnlyByUserName(String userName){
-        MailAccountDO mailAccountDO = mailAccountMapper.selectByUserName(userName);
-        if (mailAccountDO != null) {
-            throw exception(MAIL_ACCOUNT_EXISTS);
-        }
+        mailAccountCache.forEach((key,value)->{
+            if(value.getUsername().equals(userName)){
+                throw exception(MAIL_ACCOUNT_EXISTS);
+            }
+        });
+    }
+    private void validateMailAccountOnlyByUserNameAndId(String userName,Long id){
+        mailAccountCache.forEach((key , value)->{
+            if (value.getUsername().equals(userName)){
+                if (!key.equals(id)){
+                    throw exception(MAIL_ACCOUNT_EXISTS);
+                }
+            }
+        });
     }
-
     private void validateMailTemplateByAccountId(Long accountId){
         MailTemplateDO mailTemplateDO =  mailTemplateMapper.selectOneByAccountId(accountId);
         if (mailTemplateDO != null) {
-            // TODO wangjingyi:MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS
-            throw exception(MAIL_RELATE_TEMPLATE_EXISTS);
+            // TODO wangjingyi:MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS DONE
+            throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS);
         }
     }
 }

+ 2 - 11
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;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
 import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
-import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
 import cn.iocoder.yudao.module.system.service.mail.MailLogService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import java.sql.Timestamp;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
@@ -61,21 +59,14 @@ public class MailLogServiceImpl implements MailLogService {
         return mailLogDO.getId();
     }
 
-    // TODO @wangjingyi:不需要返回 id 呀
+    // TODO @wangjingyi:不需要返回 id 呀 DONE
     @Override
-    public Long updateSmsSendResult(Long logId, String result) {
+    public void updateMailSendResult(Long logId, String result) {
         MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
         logDOBuilder.id(logId);
         logDOBuilder.sendResult(result);
         MailLogDO mailLogDO = logDOBuilder.build();
         mailLogMapper.updateById(mailLogDO);
-        return logId;
     }
 
-    // TODO @wangjingyi:无用的方法,需要进行删除
-    public Long create(){
-        MailLogDO mailLogDO = new MailLogDO();
-        mailLogMapper.insert(mailLogDO);
-        return mailLogDO.getId();
-    }
 }

+ 26 - 15
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/impl/MailSendServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.system.service.mail.impl;
 
 import cn.hutool.extra.mail.MailAccount;
+import cn.hutool.extra.mail.MailUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
@@ -21,8 +22,7 @@ import java.util.List;
 import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_EXISTS;
-import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
 
 /**
  * 邮箱模版 服务实现类
@@ -49,36 +49,47 @@ public class MailSendServiceImpl implements MailSendService {
 
     @Override
     public void sendMail(String templateCode, String from , String content , List<String> tos , String title) {
-        // TODO @@wangjingyi:发送的时候,参考下短信;
+        // TODO @@wangjingyi:发送的时候,参考下短信;DONE
+        //校验邮箱模版是否合法
         MailTemplateDO mailTemplateDO =  this.checkMailTemplateValid(templateCode);
         // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
         Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(mailTemplateDO.getStatus());
-        //查询账号信息
-        MailAccountDO mailAccountDO = mailAccountMapper.selectOne(
-                "from", from
-        );
+        //校验账号信息是否合法
+        MailAccountDO mailAccountDO = this.checkMailAccountValid(from);
         Map<String , String> params = MailAccountConvert.INSTANCE.convertToMap(mailAccountDO , content);
-        content = mailTemplateService.formateMailTemplateContent(mailTemplateDO.getContent(), params);
+        content = mailTemplateService.formatMailTemplateContent(mailTemplateDO.getContent(), params);
         Long sendLogId = mailLogService.createMailLog(mailAccountDO , mailTemplateDO , from , content , tos , title , isSend);
 
         // 后续功能 TODO :附件查询
         //List<String> fileIds = mailSendVO.getFileIds();
 
-        //装载账号信息
-        MailAccount account  = MailAccountConvert.INSTANCE.convertAccount(mailAccountDO);
-
         // 发送 MQ 消息,异步执行发送短信
         if (isSend) {
             mailProducer.sendMailSendMessage(mailAccountDO , mailTemplateDO ,content , tos , title , sendLogId);
         }
     }
 
+    private MailAccountDO checkMailAccountValid(String from) {
+        MailAccountDO mailAccountDO = mailAccountMapper.selectOneByFrom(from);
+        if(null == mailAccountDO){
+            throw exception(MAIL_ACCOUNT_NOT_EXISTS);
+        }
+        return mailAccountDO;
+    }
+
     @Override
     public void doSendMail(MailSendMessage message) {
-        // TODO @wangjingyi:直接使用 hutool 发送,不要封装 mail client 哈,因为短信的客户端都是比较统一的
-        //MailClient mailClient = mailClientFactory.getMailClient();
-        //String result = mailClient.sendMail(message.getFrom() , message.getContent() , message.getTitle() , message.getTos());
-        //mailLogService.updateSmsSendResult(message.getLogId() , result);
+        // TODO @wangjingyi:直接使用 hutool 发送,不要封装 mail client 哈,因为短信的客户端都是比较统一的 DONE
+        //装载账号信息
+        MailAccount account  = MailAccountConvert.INSTANCE.convertAccount(message);
+        //发送邮件
+        try{
+            String messageId = MailUtil.send(account,message.getTos(),message.getTitle(),message.getContent(),false,null);
+            mailLogService.updateMailSendResult(message.getLogId() , messageId);
+        }catch (Exception e){
+            mailLogService.updateMailSendResult(message.getLogId() , e.getMessage());
+        }
+
     }
 
     private MailTemplateDO checkMailTemplateValid(String templateCode) {

+ 48 - 50
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;
 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
+import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
 import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -48,59 +49,53 @@ public class MailTemplateServiceImpl implements MailTemplateService {
     @Resource
     private MailTemplateMapper mailTemplateMapper;
     @Resource
-    private MailAccountMapper mailAccountMapper;
-
-    private volatile List<MailTemplateDO> mailTemplateDOList;
+    private MailProducer mailProducer;
 
     /**
      * 邮件模板缓存
-     * key:邮箱模板编码 {@link MailTemplateDO#getCode()}
+     * key:邮箱模板编码 {@link MailTemplateDO#getId()}
      *
      * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
      */
-    private volatile Map<String, MailTemplateDO> mailTemplateCache;
+    private volatile Map<Long, MailTemplateDO> mailTemplateCache;
 
     private volatile Date maxUpdateTime;
 
-    // TODO @wangjingyi:参考下别的模块的 initLocalCache 的实现
+    // TODO @wangjingyi:参考下别的模块的 initLocalCache 的实现 DONE
     @Override
     @PostConstruct
     public void initLocalCache() {
-        if(maxUpdateTime == null){
-            mailTemplateDOList = mailTemplateMapper.selectList();
-        }else{
-            if(mailTemplateMapper.selectByMaxUpdateTime(maxUpdateTime)<=0){
-                return;
-            }
-        }
+        List<MailTemplateDO> mailTemplateDOList = this.loadMailTemplateIfUpdate(maxUpdateTime);
         if (CollUtil.isEmpty(mailTemplateDOList)) {
             return;
         }
 
         // 写入缓存
-        mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getCode);
+        mailTemplateCache = CollectionUtils.convertMap(mailTemplateDOList, MailTemplateDO::getId);
         maxUpdateTime = CollectionUtils.getMaxValue(mailTemplateDOList, MailTemplateDO::getUpdateTime);
         log.info("[initLocalCache][初始化 mailTemplate 数量为 {}]", mailTemplateDOList.size());
     }
 
     @Override
     public Long create(MailTemplateCreateReqVO createReqVO) {
-        // code 要校验唯一
-        // TODO @wangjingyi:参考下我在 account 给的唯一校验的说明。
-        this.validateMailTemplateOnlyByCode(createReqVO.getCode());
+        //要校验存在
+        this.validateMailTemplateExists(createReqVO.getId());
         MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(createReqVO);
         mailTemplateMapper.insert(mailTemplateDO);
-        // TODO @wangjingyi:mq 更新
+        // TODO @wangjingyi:mq 更新 DONE
+        mailProducer.sendMailTemplateRefreshMessage();
         return mailTemplateDO.getId();
     }
 
     @Override
     public void update(@Valid MailTemplateUpdateReqVO updateReqVO) {
-        // 校验是否存在
-        this.validateMailTemplateExists(updateReqVO.getId());
+        // 校验是否唯一
+        // TODO @wangjingyi:参考下我在 account 给的唯一校验的说明。
+        this.validateMailTemplateOnlyByCode(updateReqVO.getId(),updateReqVO.getCode());
         MailTemplateDO mailTemplateDO = MailTemplateConvert.INSTANCE.convert(updateReqVO);
         mailTemplateMapper.updateById(mailTemplateDO);
-        // TODO @wangjingyi:mq 更新
+        // TODO @wangjingyi:mq 更新 DONE
+        mailProducer.sendMailTemplateRefreshMessage();
     }
 
     @Override
@@ -108,7 +103,8 @@ public class MailTemplateServiceImpl implements MailTemplateService {
         // 校验是否存在
         this.validateMailTemplateExists(id);
         mailTemplateMapper.deleteById(id);
-        // TODO @wangjingyi:mq 更新
+        // TODO @wangjingyi:mq 更新 DONE
+        mailProducer.sendMailTemplateRefreshMessage();
     }
 
     @Override
@@ -127,43 +123,45 @@ public class MailTemplateServiceImpl implements MailTemplateService {
         return mailTemplateCache.get(code);
     }
 
+    // TODO @@wangjingyi:单词拼写错误 DONE
     @Override
-    public void sendMail(MailReqVO mailReqVO) {
-        // TODO @@wangjingyi:发送的时候,参考下短信;
-        MailTemplateDO mailTemplateDO =  mailTemplateMapper.selectById(mailReqVO.getTemplateId());
-        //查询账号信息
-        MailAccountDO mailAccountDO = mailAccountMapper.selectOne(
-                "from", mailReqVO.getFrom()
-        );
-        String content = mailReqVO.getContent();
-        Map<String , String> params = MailAccountConvert.INSTANCE.convertToMap(mailAccountDO , content);
-        content = StrUtil.format(mailTemplateDO.getContent(), params);
-
-        // 后续功能 TODO :附件查询
-        //List<String> fileIds = mailSendVO.getFileIds();
-
-        //装载账号信息
-        MailAccount account  = MailAccountConvert.INSTANCE.convertAccount(mailAccountDO);
-
-        //发送
-        MailUtil.send(account , mailReqVO.getTos() , mailReqVO.getTitle() , content , false);
-    }
-
-    // TODO @@wangjingyi:单词拼写错误
-    @Override
-    public String formateMailTemplateContent(String content, Map<String, String> params) {
+    public String formatMailTemplateContent(String content, Map<String, String> params) {
         return StrUtil.format(content, params);
     }
 
     private void validateMailTemplateExists(Long id) {
-        if (mailTemplateMapper.selectById(id) == null) {
+        if (mailTemplateCache.get(id) == null) {
             throw exception(MAIL_TEMPLATE_NOT_EXISTS);
         }
     }
 
-    private void validateMailTemplateOnlyByCode(String code){
-        if (mailTemplateMapper.selectOneByCode(code) != null) {
-            throw exception(MAIL_TEMPLATE_EXISTS);
+    private void validateMailTemplateOnlyByCode(Long id ,String code){
+        mailTemplateCache.forEach((key,value)->{
+            if (value.getCode().equals(code)){
+                if (!key.equals(id)){
+                    throw exception(MAIL_TEMPLATE_EXISTS);
+                }
+            }
+        });
+    }
+    /**
+     * 如果邮件模板发生变化,从数据库中获取最新的全量邮件模板。
+     * 如果未发生变化,则返回空
+     *
+     * @param maxUpdateTime 当前邮件模板的最大更新时间
+     * @return 邮件模板列表
+     */
+    private List<MailTemplateDO> loadMailTemplateIfUpdate(Date maxUpdateTime) {
+        // 第一步,判断是否要更新。
+        if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
+            log.info("[loadMailTemplateIfUpdate][首次加载全量邮件模板]");
+        } else { // 判断数据库中是否有更新的邮件模板
+            if (mailTemplateMapper.selectByMaxUpdateTime(maxUpdateTime) == 0) {
+                return null;
+            }
+            log.info("[loadSmsTemplateIfUpdate][增量加载全量邮件模板]");
         }
+        // 第二步,如果有更新,则从数据库加载所有邮件模板
+        return mailTemplateMapper.selectList();
     }
 }