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

pull/2/head
wangjingyi 2022-05-05 03:06:00 +08:00
parent ea92b84121
commit 8bc5254e30
23 changed files with 318 additions and 132 deletions

View File

@ -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, "存在关联邮箱模版");
}

View File

@ -57,7 +57,7 @@ public class MailAccountController {
}
// TODO @wangjingyigetMailAccount 和 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 @wangjingyigetSimpleMailAccountList 单独定义一个类只返回精简的信息idfrom 即可。像密码之类都是敏感信息,不应该返回
// TODO @wangjingyigetSimpleMailAccountList 单独定义一个类只返回精简的信息idfrom 即可。像密码之类都是敏感信息,不应该返回 DONE
@GetMapping("/list-all-simple")
@ApiOperation(value = "获得邮箱账号精简列表")

View File

@ -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 @wangjingyiprivate
// TODO @wangjingyiprivate 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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

@ -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())

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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){

View File

@ -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();
}
}

View File

@ -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";
}
}

View File

@ -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() {

View File

@ -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";
}
}

View File

@ -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);

View File

@ -17,6 +17,11 @@ import java.util.List;
*/
public interface MailAccountService {
/**
*
*/
void initLocalCache();
/**
*
*

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 wangjingyiMAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS
throw exception(MAIL_RELATE_TEMPLATE_EXISTS);
// TODO wangjingyiMAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS DONE
throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS);
}
}
}

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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 @wangjingyimq 更新
// TODO @wangjingyimq 更新 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 @wangjingyimq 更新
// TODO @wangjingyimq 更新 DONE
mailProducer.sendMailTemplateRefreshMessage();
}
@Override
@ -108,7 +103,8 @@ public class MailTemplateServiceImpl implements MailTemplateService {
// 校验是否存在
this.validateMailTemplateExists(id);
mailTemplateMapper.deleteById(id);
// TODO @wangjingyimq 更新
// TODO @wangjingyimq 更新 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();
}
}