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

@ -1,15 +1,20 @@
package com.ruoyi.web.controller.monitor; package com.ruoyi.web.controller.monitor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired; 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.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize; 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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
@ -27,6 +32,9 @@ public class CacheController
@Autowired @Autowired
private RedisTemplate<String, String> redisTemplate; private RedisTemplate<String, String> redisTemplate;
@Autowired
private CacheManager cacheManager;
@PreAuthorize("@ss.hasPermi('monitor:cache:list')") @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping() @GetMapping()
public AjaxResult getInfo() throws Exception public AjaxResult getInfo() throws Exception
@ -50,4 +58,62 @@ public class CacheController
result.put("commandStats", pieList); result.put("commandStats", pieList);
return AjaxResult.success(result); 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> <artifactId>ruoyi-system</artifactId>
</dependency> </dependency>
<!-- Caffeine 本地缓存 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

@ -1,9 +1,14 @@
package com.ruoyi.framework.config; 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.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript; 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.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/** /**
* redis * redis
* *
@ -76,4 +84,52 @@ public class RedisConfig extends CachingConfigurerSupport
"end\n" + "end\n" +
"return tonumber(current);"; "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.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired; 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.stereotype.Service;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.Collection; import java.util.Collection;
@ -48,6 +50,7 @@ public class SysConfigServiceImpl implements ISysConfigService
*/ */
@Override @Override
@DataSource(DataSourceType.MASTER) @DataSource(DataSourceType.MASTER)
@Cacheable(value = "config", key = "#configId", unless = "#result == null")
public SysConfig selectConfigById(Long configId) public SysConfig selectConfigById(Long configId)
{ {
SysConfig config = new SysConfig(); SysConfig config = new SysConfig();
@ -62,6 +65,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return * @return
*/ */
@Override @Override
@Cacheable(value = "config", key = "#configKey", unless = "#result == null || #result.isEmpty()")
public String selectConfigByKey(String configKey) public String selectConfigByKey(String configKey)
{ {
String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
@ -115,6 +119,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return * @return
*/ */
@Override @Override
@CacheEvict(value = "config", allEntries = true)
public int insertConfig(SysConfig config) public int insertConfig(SysConfig config)
{ {
int row = configMapper.insertConfig(config); int row = configMapper.insertConfig(config);
@ -132,6 +137,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return * @return
*/ */
@Override @Override
@CacheEvict(value = "config", allEntries = true)
public int updateConfig(SysConfig config) public int updateConfig(SysConfig config)
{ {
int row = configMapper.updateConfig(config); int row = configMapper.updateConfig(config);
@ -149,6 +155,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* @return * @return
*/ */
@Override @Override
@CacheEvict(value = "config", allEntries = true)
public void deleteConfigByIds(Long[] configIds) public void deleteConfigByIds(Long[] configIds)
{ {
for (Long configId : configIds) for (Long configId : configIds)
@ -180,6 +187,7 @@ public class SysConfigServiceImpl implements ISysConfigService
* *
*/ */
@Override @Override
@CacheEvict(value = "config", allEntries = true)
public void clearConfigCache() public void clearConfigCache()
{ {
Collection<String> keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*"); 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.mapper.SysDictTypeMapper;
import com.ruoyi.system.service.ISysDictTypeService; import com.ruoyi.system.service.ISysDictTypeService;
import org.springframework.beans.factory.annotation.Autowired; 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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -68,6 +70,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return * @return
*/ */
@Override @Override
@Cacheable(value = "dict", key = "#dictType", unless = "#result == null || #result.isEmpty()")
public List<SysDictData> selectDictDataByType(String dictType) public List<SysDictData> selectDictDataByType(String dictType)
{ {
List<SysDictData> dictDatas = DictUtils.getDictCache(dictType); List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
@ -79,9 +82,8 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
if (StringUtils.isNotEmpty(dictDatas)) if (StringUtils.isNotEmpty(dictDatas))
{ {
DictUtils.setDictCache(dictType, dictDatas); DictUtils.setDictCache(dictType, dictDatas);
return dictDatas;
} }
return null; return dictDatas;
} }
/** /**
@ -115,6 +117,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return * @return
*/ */
@Override @Override
@CacheEvict(value = "dict", allEntries = true)
public void deleteDictTypeByIds(Long[] dictIds) public void deleteDictTypeByIds(Long[] dictIds)
{ {
for (Long dictId : dictIds) for (Long dictId : dictIds)
@ -156,6 +159,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* *
*/ */
@Override @Override
@CacheEvict(value = "dict", allEntries = true)
public void resetDictCache() public void resetDictCache()
{ {
clearDictCache(); clearDictCache();
@ -169,6 +173,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return * @return
*/ */
@Override @Override
@CacheEvict(value = "dict", allEntries = true)
public int insertDictType(SysDictType dict) public int insertDictType(SysDictType dict)
{ {
int row = dictTypeMapper.insertDictType(dict); int row = dictTypeMapper.insertDictType(dict);
@ -186,6 +191,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* @return * @return
*/ */
@Override @Override
@CacheEvict(value = "dict", allEntries = true)
@Transactional @Transactional
public int updateDictType(SysDictType dict) public int updateDictType(SysDictType dict)
{ {

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

Loading…
Cancel
Save