feat: add Redis cache support for dict, config and user services

- Add Caffeine local cache dependency for high-frequency access scenarios
- Configure Spring Cache with Caffeine as primary CacheManager and Redis as secondary
- Add @Cacheable/@CacheEvict annotations to SysDictTypeServiceImpl
- Add @Cacheable/@CacheEvict annotations to SysConfigServiceImpl
- Add @Cacheable/@CacheEvict annotations to SysUserServiceImpl
- Create CacheUtils utility class for programmatic cache operations
- Extend CacheController with cache management endpoints (list, clear)
feature/20260628/refactor-frontend-modernization
Lxy 3 weeks ago
parent 99ddd5d781
commit 7f500ae421

@ -33,6 +33,7 @@
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<caffeine.version>3.1.8</caffeine.version>
</properties>
<!-- 依赖声明 -->
@ -218,6 +219,13 @@
<version>${ruoyi.version}</version>
</dependency>
<!-- Caffeine 本地缓存 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${caffeine.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

@ -1,15 +1,20 @@
package com.ruoyi.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.domain.AjaxResult;
@ -27,6 +32,9 @@ public class CacheController
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private CacheManager cacheManager;
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
@ -50,4 +58,62 @@ public class CacheController
result.put("commandStats", pieList);
return AjaxResult.success(result);
}
/**
* Spring Cache
*/
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/names")
public AjaxResult getCacheNames()
{
Collection<String> cacheNames = cacheManager.getCacheNames();
List<Map<String, Object>> result = new ArrayList<>();
for (String cacheName : cacheNames)
{
Cache cache = cacheManager.getCache(cacheName);
Map<String, Object> cacheInfo = new HashMap<>();
cacheInfo.put("cacheName", cacheName);
if (cache != null)
{
cacheInfo.put("nativeCache", cache.getNativeCache().getClass().getSimpleName());
}
result.add(cacheInfo);
}
return AjaxResult.success(result);
}
/**
*
*/
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clear/{cacheName}")
public AjaxResult clearCache(@PathVariable String cacheName)
{
Cache cache = cacheManager.getCache(cacheName);
if (cache != null)
{
cache.clear();
return AjaxResult.success("缓存已清空: " + cacheName);
}
return AjaxResult.error("缓存不存在: " + cacheName);
}
/**
* Spring Cache
*/
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearAll")
public AjaxResult clearAllCache()
{
Collection<String> cacheNames = cacheManager.getCacheNames();
for (String cacheName : cacheNames)
{
Cache cache = cacheManager.getCache(cacheName);
if (cache != null)
{
cache.clear();
}
}
return AjaxResult.success("所有缓存已清空");
}
}

@ -0,0 +1,96 @@
package com.ruoyi.common.core.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
/**
*
*
* @author ruoyi
*/
@Component
public class CacheUtils
{
@Autowired
private CacheManager cacheManager;
/**
*
*
* @param cacheName
* @return Cache
*/
public Cache getCache(String cacheName)
{
return cacheManager.getCache(cacheName);
}
/**
*
*
* @param cacheName
* @param key
* @return
*/
@SuppressWarnings("unchecked")
public <T> T get(String cacheName, Object key)
{
Cache cache = cacheManager.getCache(cacheName);
if (cache != null)
{
Cache.ValueWrapper wrapper = cache.get(key);
if (wrapper != null)
{
return (T) wrapper.get();
}
}
return null;
}
/**
*
*
* @param cacheName
* @param key
* @param value
*/
public void put(String cacheName, Object key, Object value)
{
Cache cache = cacheManager.getCache(cacheName);
if (cache != null)
{
cache.put(key, value);
}
}
/**
*
*
* @param cacheName
* @param key
*/
public void evict(String cacheName, Object key)
{
Cache cache = cacheManager.getCache(cacheName);
if (cache != null)
{
cache.evict(key);
}
}
/**
*
*
* @param cacheName
*/
public void clear(String cacheName)
{
Cache cache = cacheManager.getCache(cacheName);
if (cache != null)
{
cache.clear();
}
}
}

@ -59,6 +59,12 @@
<artifactId>ruoyi-system</artifactId>
</dependency>
<!-- Caffeine 本地缓存 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
</dependencies>
</project>

@ -1,9 +1,14 @@
package com.ruoyi.framework.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.ruoyi.framework.config.FastJson2JsonRedisSerializer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
@ -14,6 +19,9 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
* redis
*
@ -76,4 +84,52 @@ public class RedisConfig extends CachingConfigurerSupport
"end\n" +
"return tonumber(current);";
}
/**
* Caffeine
* 访
*/
@Bean
@Primary
public CacheManager cacheManager()
{
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats());
// 预定义缓存名称
cacheManager.setCacheNames(Arrays.asList(
"dict", // 字典缓存
"config", // 配置缓存
"user", // 用户缓存
"menu", // 菜单缓存
"dept" // 部门缓存
));
return cacheManager;
}
/**
* Redis
* @Cacheable(cacheManager = "redisCacheManager") 使
*/
@Bean("redisCacheManager")
public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory)
{
org.springframework.data.redis.cache.RedisCacheConfiguration config =
org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(java.time.Duration.ofMinutes(30))
.serializeValuesWith(
org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair
.fromSerializer(new FastJson2JsonRedisSerializer<>(Object.class))
)
.disableCachingNullValues();
return org.springframework.data.redis.cache.RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}

@ -12,6 +12,8 @@ import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Collection;
@ -48,6 +50,7 @@ public class SysConfigServiceImpl implements ISysConfigService
*/
@Override
@DataSource(DataSourceType.MASTER)
@Cacheable(value = "config", key = "#configId", unless = "#result == null")
public SysConfig selectConfigById(Long configId)
{
SysConfig config = new SysConfig();
@ -62,6 +65,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return
*/
@Override
@Cacheable(value = "config", key = "#configKey", unless = "#result == null || #result.isEmpty()")
public String selectConfigByKey(String configKey)
{
String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
@ -115,6 +119,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return
*/
@Override
@CacheEvict(value = "config", allEntries = true)
public int insertConfig(SysConfig config)
{
int row = configMapper.insertConfig(config);
@ -132,6 +137,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return
*/
@Override
@CacheEvict(value = "config", allEntries = true)
public int updateConfig(SysConfig config)
{
int row = configMapper.updateConfig(config);
@ -149,6 +155,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return
*/
@Override
@CacheEvict(value = "config", allEntries = true)
public void deleteConfigByIds(Long[] configIds)
{
for (Long configId : configIds)
@ -180,6 +187,7 @@ public class SysConfigServiceImpl implements ISysConfigService
*
*/
@Override
@CacheEvict(value = "config", allEntries = true)
public void clearConfigCache()
{
Collection<String> keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*");

@ -10,6 +10,8 @@ import com.ruoyi.system.mapper.SysDictDataMapper;
import com.ruoyi.system.mapper.SysDictTypeMapper;
import com.ruoyi.system.service.ISysDictTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
@ -68,6 +70,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return
*/
@Override
@Cacheable(value = "dict", key = "#dictType", unless = "#result == null || #result.isEmpty()")
public List<SysDictData> selectDictDataByType(String dictType)
{
List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
@ -79,9 +82,8 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
if (StringUtils.isNotEmpty(dictDatas))
{
DictUtils.setDictCache(dictType, dictDatas);
return dictDatas;
}
return null;
return dictDatas;
}
/**
@ -115,6 +117,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return
*/
@Override
@CacheEvict(value = "dict", allEntries = true)
public void deleteDictTypeByIds(Long[] dictIds)
{
for (Long dictId : dictIds)
@ -156,6 +159,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
*
*/
@Override
@CacheEvict(value = "dict", allEntries = true)
public void resetDictCache()
{
clearDictCache();
@ -169,6 +173,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return
*/
@Override
@CacheEvict(value = "dict", allEntries = true)
public int insertDictType(SysDictType dict)
{
int row = dictTypeMapper.insertDictType(dict);
@ -186,6 +191,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return
*/
@Override
@CacheEvict(value = "dict", allEntries = true)
@Transactional
public int updateDictType(SysDictType dict)
{

@ -7,6 +7,8 @@ import javax.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@ -107,6 +109,7 @@ public class SysUserServiceImpl implements ISysUserService
* @return
*/
@Override
@Cacheable(value = "user", key = "#userName", unless = "#result == null")
public SysUser selectUserByUserName(String userName)
{
return userMapper.selectUserByUserName(userName);
@ -119,6 +122,7 @@ public class SysUserServiceImpl implements ISysUserService
* @return
*/
@Override
@Cacheable(value = "user", key = "#userId", unless = "#result == null")
public SysUser selectUserById(Long userId)
{
return userMapper.selectUserById(userId);
@ -252,6 +256,7 @@ public class SysUserServiceImpl implements ISysUserService
* @return
*/
@Override
@CacheEvict(value = "user", allEntries = true)
@Transactional
public int insertUser(SysUser user)
{
@ -283,6 +288,7 @@ public class SysUserServiceImpl implements ISysUserService
* @return
*/
@Override
@CacheEvict(value = "user", key = "#user.userId")
@Transactional
public int updateUser(SysUser user)
{
@ -476,6 +482,7 @@ public class SysUserServiceImpl implements ISysUserService
* @return
*/
@Override
@CacheEvict(value = "user", allEntries = true)
@Transactional
public int deleteUserByIds(Long[] userIds)
{

Loading…
Cancel
Save