缓存改造:MailTemplate、MailAccount 使用 Redis 作为缓存

This commit is contained in:
YunaiV 2023-03-02 23:28:21 +08:00
parent d23d72c8bb
commit 5dda7a3c05
11 changed files with 54 additions and 219 deletions

View File

@ -74,4 +74,19 @@ public interface RedisKeyConstants {
*/ */
String NOTIFY_TEMPLATE = "notify_template"; String NOTIFY_TEMPLATE = "notify_template";
/**
* 邮件账号的缓存
*
* KEY 格式sms_template::{id}
* VALUE 数据格式String 账号信息
*/
String MAIL_ACCOUNT = "mail_account";
/**
* 邮件模版的缓存
*
* KEY 格式mail_template::{code}
* VALUE 数据格式String 模版信息
*/
String MAIL_TEMPLATE = "mail_template";
} }

View File

@ -1,31 +0,0 @@
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.MailAccountRefreshMessage;
import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 针对 {@link MailAccountRefreshMessage} 的消费者
*
* @author wangjingyi
*/
@Component
@Slf4j
public class MailAccountRefreshConsumer extends AbstractChannelMessageListener<MailAccountRefreshMessage> {
@Resource
private MailAccountService mailAccountService;
@Override
public void onMessage(MailAccountRefreshMessage message) {
log.info("[onMessage][收到 Mail Account 刷新信息]");
mailAccountService.initLocalCache();
}
}

View File

@ -1,29 +0,0 @@
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.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][收到 Mail Template 刷新信息]");
mailTemplateService.initLocalCache();
}
}

View File

@ -1,21 +0,0 @@
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

@ -1,21 +0,0 @@
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

@ -1,9 +1,7 @@
package cn.iocoder.yudao.module.system.mq.producer.mail; package cn.iocoder.yudao.module.system.mq.producer.mail;
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
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.MailSendMessage;
import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -22,22 +20,6 @@ public class MailProducer {
@Resource @Resource
private RedisMQTemplate redisMQTemplate; private RedisMQTemplate redisMQTemplate;
/**
* 发送 {@link MailTemplateRefreshMessage} 消息
*/
public void sendMailTemplateRefreshMessage() {
MailTemplateRefreshMessage message = new MailTemplateRefreshMessage();
redisMQTemplate.send(message);
}
/**
* 发送 {@link MailAccountRefreshMessage} 消息
*/
public void sendMailAccountRefreshMessage() {
MailAccountRefreshMessage message = new MailAccountRefreshMessage();
redisMQTemplate.send(message);
}
/** /**
* 发送 {@link MailSendMessage} 消息 * 发送 {@link MailSendMessage} 消息
* *

View File

@ -17,19 +17,6 @@ import java.util.List;
*/ */
public interface MailAccountService { public interface MailAccountService {
/**
* 初始化邮箱账号的本地缓存
*/
void initLocalCache();
/**
* 从缓存中获取邮箱账号
*
* @param id 编号
* @return 邮箱账号
*/
MailAccountDO getMailAccountFromCache(Long id);
/** /**
* 创建邮箱账号 * 创建邮箱账号
* *
@ -60,6 +47,14 @@ public interface MailAccountService {
*/ */
MailAccountDO getMailAccount(Long id); MailAccountDO getMailAccount(Long id);
/**
* 从缓存中获取邮箱账号
*
* @param id 编号
* @return 邮箱账号
*/
MailAccountDO getMailAccountFromCache(Long id);
/** /**
* 获取邮箱账号分页信息 * 获取邮箱账号分页信息
* *

View File

@ -7,20 +7,18 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccou
import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert; 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.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS;
/** /**
* 邮箱账号 Service 实现类 * 邮箱账号 Service 实现类
@ -39,46 +37,16 @@ public class MailAccountServiceImpl implements MailAccountService {
@Resource @Resource
private MailTemplateService mailTemplateService; private MailTemplateService mailTemplateService;
@Resource
private MailProducer mailProducer;
/**
* 邮箱账号缓存
* key邮箱账号编码 {@link MailAccountDO#getId()}
*
* 这里声明 volatile 修饰的原因是每次刷新时直接修改指向
*/
@Getter
private volatile Map<Long, MailAccountDO> mailAccountCache;
@Override
@PostConstruct
public void initLocalCache() {
// 第一步查询数据
List<MailAccountDO> accounts = mailAccountMapper.selectList();
log.info("[initLocalCache][缓存邮箱账号,数量:{}]", accounts.size());
// 第二步构建缓存
mailAccountCache = convertMap(accounts, MailAccountDO::getId);
}
@Override
public MailAccountDO getMailAccountFromCache(Long id) {
return mailAccountCache.get(id);
}
@Override @Override
public Long createMailAccount(MailAccountCreateReqVO createReqVO) { public Long createMailAccount(MailAccountCreateReqVO createReqVO) {
// 插入 // 插入
MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO); MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO);
mailAccountMapper.insert(account); mailAccountMapper.insert(account);
// 发送刷新消息
mailProducer.sendMailAccountRefreshMessage();
return account.getId(); return account.getId();
} }
@Override @Override
@Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#updateReqVO.id")
public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) { public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) {
// 校验是否存在 // 校验是否存在
validateMailAccountExists(updateReqVO.getId()); validateMailAccountExists(updateReqVO.getId());
@ -86,11 +54,10 @@ public class MailAccountServiceImpl implements MailAccountService {
// 更新 // 更新
MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO); MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO);
mailAccountMapper.updateById(updateObj); mailAccountMapper.updateById(updateObj);
// 发送刷新消息
mailProducer.sendMailAccountRefreshMessage();
} }
@Override @Override
@Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id")
public void deleteMailAccount(Long id) { public void deleteMailAccount(Long id) {
// 校验是否存在账号 // 校验是否存在账号
validateMailAccountExists(id); validateMailAccountExists(id);
@ -101,8 +68,6 @@ public class MailAccountServiceImpl implements MailAccountService {
// 删除 // 删除
mailAccountMapper.deleteById(id); mailAccountMapper.deleteById(id);
// 发送刷新消息
mailProducer.sendMailAccountRefreshMessage();
} }
private void validateMailAccountExists(Long id) { private void validateMailAccountExists(Long id) {
@ -116,6 +81,12 @@ public class MailAccountServiceImpl implements MailAccountService {
return mailAccountMapper.selectById(id); return mailAccountMapper.selectById(id);
} }
@Override
@Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id", unless = "#result == null")
public MailAccountDO getMailAccountFromCache(Long id) {
return getMailAccount(id);
}
@Override @Override
public PageResult<MailAccountDO> getMailAccountPage(MailAccountPageReqVO pageReqVO) { public PageResult<MailAccountDO> getMailAccountPage(MailAccountPageReqVO pageReqVO) {
return mailAccountMapper.selectPage(pageReqVO); return mailAccountMapper.selectPage(pageReqVO);

View File

@ -18,11 +18,6 @@ import java.util.Map;
*/ */
public interface MailTemplateService { public interface MailTemplateService {
/**
* 初始化邮件模版的本地缓存
*/
void initLocalCache();
/** /**
* 邮件模版创建 * 邮件模版创建
* *

View File

@ -10,14 +10,14 @@ import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemp
import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert; 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.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; 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.dal.redis.RedisKeyConstants;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List; import java.util.List;
@ -25,8 +25,8 @@ import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_CODE_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
/** /**
* 邮箱模版 Service 实现类 * 邮箱模版 Service 实现类
@ -47,29 +47,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Resource @Resource
private MailTemplateMapper mailTemplateMapper; private MailTemplateMapper mailTemplateMapper;
@Resource
private MailProducer mailProducer;
/**
* 邮件模板缓存
* key邮件模版标识 {@link MailTemplateDO#getCode()}
*
* 这里声明 volatile 修饰的原因是每次刷新时直接修改指向
*/
@Getter
private volatile Map<String, MailTemplateDO> mailTemplateCache;
@Override
@PostConstruct
public void initLocalCache() {
// 第一步查询数据
List<MailTemplateDO> templates = mailTemplateMapper.selectList();
log.info("[initLocalCache][缓存邮件模版,数量:{}]", templates.size());
// 第二步构建缓存
mailTemplateCache = convertMap(templates, MailTemplateDO::getCode);
}
@Override @Override
public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) { public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) {
// 校验 code 是否唯一 // 校验 code 是否唯一
@ -79,12 +56,12 @@ public class MailTemplateServiceImpl implements MailTemplateService {
MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO) MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO)
.setParams(parseTemplateContentParams(createReqVO.getContent())); .setParams(parseTemplateContentParams(createReqVO.getContent()));
mailTemplateMapper.insert(template); mailTemplateMapper.insert(template);
// 发送刷新消息
mailProducer.sendMailTemplateRefreshMessage();
return template.getId(); return template.getId();
} }
@Override @Override
@CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
allEntries = true) // allEntries 清空所有缓存因为可能修改到 code 字段不好清理
public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) { public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) {
// 校验是否存在 // 校验是否存在
validateMailTemplateExists(updateReqVO.getId()); validateMailTemplateExists(updateReqVO.getId());
@ -95,12 +72,10 @@ public class MailTemplateServiceImpl implements MailTemplateService {
MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO) MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO)
.setParams(parseTemplateContentParams(updateReqVO.getContent())); .setParams(parseTemplateContentParams(updateReqVO.getContent()));
mailTemplateMapper.updateById(updateObj); mailTemplateMapper.updateById(updateObj);
// 发送刷新消息
mailProducer.sendMailTemplateRefreshMessage();
} }
@VisibleForTesting @VisibleForTesting
public void validateCodeUnique(Long id, String code) { void validateCodeUnique(Long id, String code) {
MailTemplateDO template = mailTemplateMapper.selectByCode(code); MailTemplateDO template = mailTemplateMapper.selectByCode(code);
if (template == null) { if (template == null) {
return; return;
@ -113,14 +88,14 @@ public class MailTemplateServiceImpl implements MailTemplateService {
} }
@Override @Override
@CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
allEntries = true) // allEntries 清空所有缓存因为 id 不是直接的缓存 code不好清理
public void deleteMailTemplate(Long id) { public void deleteMailTemplate(Long id) {
// 校验是否存在 // 校验是否存在
validateMailTemplateExists(id); validateMailTemplateExists(id);
// 删除 // 删除
mailTemplateMapper.deleteById(id); mailTemplateMapper.deleteById(id);
// 发送刷新消息
mailProducer.sendMailTemplateRefreshMessage();
} }
private void validateMailTemplateExists(Long id) { private void validateMailTemplateExists(Long id) {
@ -132,6 +107,12 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override @Override
public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);} public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);}
@Override
@Cacheable(value = RedisKeyConstants.MAIL_TEMPLATE, key = "#code", unless = "#result == null")
public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
return mailTemplateMapper.selectByCode(code);
}
@Override @Override
public PageResult<MailTemplateDO> getMailTemplatePage(MailTemplatePageReqVO pageReqVO) { public PageResult<MailTemplateDO> getMailTemplatePage(MailTemplatePageReqVO pageReqVO) {
return mailTemplateMapper.selectPage(pageReqVO); return mailTemplateMapper.selectPage(pageReqVO);
@ -140,11 +121,6 @@ public class MailTemplateServiceImpl implements MailTemplateService {
@Override @Override
public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();} public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();}
@Override
public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
return mailTemplateCache.get(code);
}
@Override @Override
public String formatMailTemplateContent(String content, Map<String, Object> params) { public String formatMailTemplateContent(String content, Map<String, Object> params) {
return StrUtil.format(content, params); return StrUtil.format(content, params);

View File

@ -192,6 +192,9 @@ yudao:
ignore-caches: ignore-caches:
- permission_menu_ids - permission_menu_ids
- oauth_client - oauth_client
- notify_template
- mail_account
- mail_template
sms-code: # 短信验证码相关的配置项 sms-code: # 短信验证码相关的配置项
expire-times: 10m expire-times: 10m
send-frequency: 1m send-frequency: 1m