first commit

This commit is contained in:
abcv7
2026-02-25 15:08:40 +08:00
commit 7f1d83ada7
2003 changed files with 144362 additions and 0 deletions
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>sfbx-framework</artifactId>
<groupId>com.itheima.sfbx</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<!--基础模块-redis支持-->
<artifactId>framework-redis</artifactId>
<name>framework-redis</name>
<dependencies>
<dependency>
<groupId>com.itheima.sfbx</groupId>
<artifactId>framework-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,37 @@
package com.itheima.sfbx.framework.redis.config;
import lombok.RequiredArgsConstructor;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
@Configuration
@RequiredArgsConstructor
@ConditionalOnClass(RedissonClient.class)
public class CustomRedissonConfig {
private final RedisProperties redisProperties;
@Bean(
destroyMethod = "shutdown"
)
@Primary
public RedissonClient redisson() {
Config config = new Config();
String prefix = "redis://";
((SingleServerConfig)config.useSingleServer()
.setAddress(prefix + this.redisProperties.getHost() + ":" + this.redisProperties.getPort()))
.setDatabase(this.redisProperties.getDatabase())
.setPassword(this.redisProperties.getPassword());
config.setCodec(JsonJacksonCodec.INSTANCE);
return Redisson.create(config);
}
}
@@ -0,0 +1,66 @@
package com.itheima.sfbx.framework.redis.config;
import com.itheima.sfbx.framework.redis.properties.DataSecurityCacheProperties;
import com.itheima.sfbx.framework.redis.resolver.CustomRedisCacheManager;
import com.itheima.sfbx.framework.redis.properties.TenantCacheProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.CacheKeyPrefix;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching
@EnableConfigurationProperties({TenantCacheProperties.class,DataSecurityCacheProperties.class})
public class RedisCacheConfig {
@Autowired
TenantCacheProperties tenantCacheProperties;
@Autowired
DataSecurityCacheProperties dataSecurityCacheProperties;
/**
* 修改spring-cache默认前缀为:
*/
@Bean
public CacheKeyPrefix cacheKeyPrefix() {
return cacheName -> cacheName + ":";
}
/**
* 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut
* 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,
* 将数据添加到缓存之中或者从缓存中移除某个值
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//对key的序列化操作:String
RedisSerializer<String> keySerializer = new StringRedisSerializer();
//对value的序列化操作:json
GenericJackson2JsonRedisSerializer valuesSerializer = new GenericJackson2JsonRedisSerializer();
//配置config,指定超时时间记得key val 序列化处理
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.computePrefixWith(cacheKeyPrefix())
//指定全局默认超时时间【600S】
.entryTtl(Duration.ofSeconds(1))
//配置key的序列化方式
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
//配置value的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valuesSerializer));
//关闭空值的存储
//.disableCachingNullValues();
//使用CustomRedisCacheManager进行初始化
return new CustomRedisCacheManager(redisConnectionFactory, config, tenantCacheProperties,dataSecurityCacheProperties);
}
}
@@ -0,0 +1,21 @@
package com.itheima.sfbx.framework.redis.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* @ClassName TenantProperties.java
* @Description spring-cache多租户属性
*/
@Data
@NoArgsConstructor
@ConfigurationProperties(prefix = "spring.cache")
public class DataSecurityCacheProperties {
//需要忽略的数据权限的表
public List<String> ignoreDataSecurityTables;
}
@@ -0,0 +1,26 @@
package com.itheima.sfbx.framework.redis.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import java.util.List;
/**
* @ClassName TenantProperties.java
* @Description spring-cache多租户属性
*/
@Data
@NoArgsConstructor
@ConfigurationProperties(prefix = "spring.cache")
public class TenantCacheProperties {
//需要忽略的企业ID的表
public List<String> ignoreCompanyTables;
//默认企业编号Id
public String defaultCompanyNo;
}
@@ -0,0 +1,56 @@
package com.itheima.sfbx.framework.redis.resolver;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
/**
* @ClassName CustomRedisCache.java
* @Description redis缓存自定义处理
*/
public class CustomRedisCache extends RedisCache {
private final String name;
private final RedisCacheWriter cacheWriter;
private final ConversionService conversionService;
protected CustomRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
super(name, cacheWriter, cacheConfig);
this.name = name;
this.cacheWriter = cacheWriter;
this.conversionService = cacheConfig.getConversionService();
}
/**
*
* @Title: evict
* @Description: 重写删除的方法
* @param @param key
* @throws
*
*/
@Override
public void evict(Object key) {
if (key instanceof String) {
String keyString = key.toString();
// 后缀删除
if (keyString.endsWith("*")) {
evictLikeSuffix(keyString);
return;
}
}
// 删除指定的key
super.evict(key);
}
/**
* 后缀匹配匹配
*
* @param key
*/
private void evictLikeSuffix(String key) {
byte[] pattern = this.conversionService.convert(this.createCacheKey(key), byte[].class);
this.cacheWriter.clean(this.name, pattern);
}
}
@@ -0,0 +1,152 @@
package com.itheima.sfbx.framework.redis.resolver;
import com.itheima.sfbx.framework.commons.dto.security.DataSecurityVO;
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
import com.itheima.sfbx.framework.commons.utils.SubjectContent;
import com.itheima.sfbx.framework.redis.properties.DataSecurityCacheProperties;
import com.itheima.sfbx.framework.redis.properties.TenantCacheProperties;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.lang.Nullable;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName CustomCacheManager.java
* @Description 自定义缓存管理者
*/
public class CustomRedisCacheManager extends RedisCacheManager {
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private TenantCacheProperties tenantCacheProperties;
private DataSecurityCacheProperties dataSecurityCacheProperties;
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomRedisCacheManager(RedisConnectionFactory redisConnectionFactory, RedisCacheConfiguration cacheConfiguration, TenantCacheProperties tenantCacheProperties, DataSecurityCacheProperties dataSecurityCacheProperties) {
this(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),cacheConfiguration);
this.tenantCacheProperties =tenantCacheProperties;
this.dataSecurityCacheProperties=dataSecurityCacheProperties;
}
/**
* 覆盖父类创建RedisCache,采用自定义的RedisCacheResolver
* @Title: createRedisCache
* @Description: TODO
* @param @param name
* @param @param cacheConfig
* @param @return
* @throws
*
*/
@Override
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
//自定义缓存时间例如
//@Cacheable(value = DataDictCacheConstant.PARENT_KEY+"=-1",key = "#parentKey")
String[] split = name.split("&");
if (split.length==1){
name=split[0];
}else if (split.length==2){
name=split[0];
long ttl = Long.valueOf(split[1].replaceAll("ttl=",""));
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
}else {
throw new RuntimeException("自定义缓存失效时间异常");
}
return new CustomRedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
/**
* 从上下文中获取租户ID,重写key值添加多租户模式
* @param name
* @return
*/
@Override
public Cache getCache(String name) {
//多租户及数据权限缓存key处理
String companyNo = SubjectContent.getCompanyNo();
String dataSecurity = null;
DataSecurityVO dataSecurityVO = SubjectContent.getDataSecurityVO();
//查看个人数据
if (!EmptyUtil.isNullOrEmpty(dataSecurityVO)&&dataSecurityVO.getYouselfData()){
dataSecurity = String.valueOf(SubjectContent.getUserVO().getId().hashCode());
}
//查看授权数据
if (!EmptyUtil.isNullOrEmpty(dataSecurityVO)&&!dataSecurityVO.getYouselfData()){
dataSecurity = dataSecurity+":"+SubjectContent.getUserVO().getDataSecurityVO().hashCode();
}
String[] nameTime = name.split("&");
String targetTab = "tab_"+name.split(":")[0].replace("-","_");
if (!EmptyUtil.isNullOrEmpty(companyNo)&&
!tenantCacheProperties.getDefaultCompanyNo().equals(companyNo)){
boolean isIgnoreCompanyNo = false;
if (!EmptyUtil.isNullOrEmpty(tenantCacheProperties.getIgnoreCompanyTables())){
isIgnoreCompanyNo = !tenantCacheProperties.getIgnoreCompanyTables().contains(targetTab);
}
if (isIgnoreCompanyNo){
nameTime[0] = nameTime[0]+":"+companyNo;
}
}
boolean isIgnoreDataSecurity = false;
if (!EmptyUtil.isNullOrEmpty(dataSecurityCacheProperties.getIgnoreDataSecurityTables())){
isIgnoreDataSecurity = !dataSecurityCacheProperties.getIgnoreDataSecurityTables().contains(targetTab);
}
if (isIgnoreDataSecurity){
nameTime[0] = nameTime[0]+":"+dataSecurity;
}
if (nameTime.length==2){
name = nameTime[0]+"&"+nameTime[1];
}else{
name = nameTime[0];
}
//默认企业则不做缓存租户处理
return super.getCache(name);
}
@Override
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(getCacheNames().size());
getCacheNames().forEach(it -> {
RedisCache cache = CustomRedisCache.class.cast(lookupCache(it));
configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
});
return Collections.unmodifiableMap(configurationMap);
}
}
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.itheima.sfbx.framework.redis.config.CustomRedissonConfig