缓存改造:NotifyTemplate 使用 Redis 作为缓存

This commit is contained in:
YunaiV 2023-03-02 22:21:33 +08:00
parent 7ccdf86d3a
commit d23d72c8bb
9 changed files with 38 additions and 152 deletions

View File

@ -66,4 +66,12 @@ public interface RedisKeyConstants {
*/
String OAUTH_CLIENT = "oauth_client";
/**
* 站内信模版的缓存
*
* KEY 格式notify_template::{code}
* VALUE 数据格式String 模版信息
*/
String NOTIFY_TEMPLATE = "notify_template";
}

View File

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

View File

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

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.system.mq.producer.notify;
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* Notify 站内信相关消息的 Producer
*
* @author xrcoder
* @since 2022-08-06
*/
@Slf4j
@Component
public class NotifyProducer {
@Resource
private RedisMQTemplate redisMQTemplate;
/**
* 发送 {@link NotifyTemplateRefreshMessage} 消息
*/
public void sendNotifyTemplateRefreshMessage() {
NotifyTemplateRefreshMessage message = new NotifyTemplateRefreshMessage();
redisMQTemplate.send(message);
}
}

View File

@ -16,19 +16,6 @@ import java.util.Map;
*/
public interface NotifyTemplateService {
/**
* 初始化站内信模板的本地缓存
*/
void initLocalCache();
/**
* 获得站内信模板从缓存中
*
* @param code 模板编码
* @return 站内信模板
*/
NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
* 创建站内信模版
*
@ -59,6 +46,14 @@ public interface NotifyTemplateService {
*/
NotifyTemplateDO getNotifyTemplate(Long id);
/**
* 获得站内信模板从缓存中
*
* @param code 模板编码
* @return 站内信模板
*/
NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
* 获得站内信模版分页
*

View File

@ -3,27 +3,28 @@ package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
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.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import com.google.common.annotations.VisibleForTesting;
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.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
/**
* 站内信模版 Service 实现类
@ -43,36 +44,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@Resource
private NotifyProducer notifyProducer;
/**
* 站内信模板缓存
* key站内信模板编码 {@link NotifyTemplateDO#getCode()}
* <p>
* 这里声明 volatile 修饰的原因是每次刷新时直接修改指向
*/
private volatile Map<String, NotifyTemplateDO> notifyTemplateCache;
/**
* 初始化站内信模板的本地缓存
*/
@Override
@PostConstruct
public void initLocalCache() {
// 第一步查询数据
List<NotifyTemplateDO> templates = notifyTemplateMapper.selectList();
log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size());
// 第二步构建缓存
notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode);
}
@Override
public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
return notifyTemplateCache.get(code);
}
@Override
public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) {
// 校验站内信编码是否重复
@ -82,13 +53,12 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO);
notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent()));
notifyTemplateMapper.insert(notifyTemplate);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
return notifyTemplate.getId();
}
@Override
@CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
allEntries = true) // allEntries 清空所有缓存因为可能修改到 code 字段不好清理
public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) {
// 校验存在
validateNotifyTemplateExists(updateReqVO.getId());
@ -99,9 +69,6 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO);
updateObj.setParams(parseTemplateContentParams(updateObj.getContent()));
notifyTemplateMapper.updateById(updateObj);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
}
@VisibleForTesting
@ -110,13 +77,13 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
}
@Override
@CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE,
allEntries = true) // allEntries 清空所有缓存因为 id 不是直接的缓存 code不好清理
public void deleteNotifyTemplate(Long id) {
// 校验存在
validateNotifyTemplateExists(id);
// 删除
notifyTemplateMapper.deleteById(id);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
}
private void validateNotifyTemplateExists(Long id) {
@ -130,13 +97,20 @@ public class NotifyTemplateServiceImpl implements NotifyTemplateService {
return notifyTemplateMapper.selectById(id);
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code",
unless = "#result == null")
public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
return notifyTemplateMapper.selectByCode(code);
}
@Override
public PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) {
return notifyTemplateMapper.selectPage(pageReqVO);
}
@VisibleForTesting
public void validateNotifyTemplateCodeDuplicate(Long id, String code) {
void validateNotifyTemplateCodeDuplicate(Long id, String code) {
NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code);
if (template == null) {
return;

View File

@ -100,7 +100,8 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
}
@Override
@Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId")
@Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId",
unless = "#result == null")
public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) {
return oauth2ClientMapper.selectByClientId(clientId);
}

View File

@ -171,7 +171,8 @@ public class RoleServiceImpl implements RoleService {
}
@Override
@Cacheable(value = RedisKeyConstants.ROLE, key = "#id", unless = "#result == null")
@Cacheable(value = RedisKeyConstants.ROLE, key = "#id",
unless = "#result == null")
public RoleDO getRoleFromCache(Long id) {
return roleMapper.selectById(id);
}

View File

@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.Notify
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
@ -25,7 +23,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
/**
* {@link NotifyTemplateServiceImpl} 的单元测试类
@ -41,9 +38,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@MockBean
private NotifyProducer notifyProducer;
@Test
public void testCreateNotifyTemplate_success() {
// 准备参数
@ -57,7 +51,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
// 校验记录的属性是否正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId);
assertPojoEquals(reqVO, notifyTemplate);
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
@ -76,7 +69,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
// 校验是否更新正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, notifyTemplate);
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
@ -100,7 +92,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
notifyTemplateService.deleteNotifyTemplate(id);
// 校验数据不存在了
assertNull(notifyTemplateMapper.selectById(id));
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
@ -164,7 +155,6 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);
notifyTemplateService.initLocalCache();
// 准备参数
String code = dbNotifyTemplate.getCode();
@ -173,7 +163,7 @@ public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
// 断言
assertPojoEquals(dbNotifyTemplate, notifyTemplate);
}
@Test
public void testFormatNotifyTemplateContent() {
// 准备参数