package cn.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;

import cn.constant.CommonConst;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.time.Duration;
import static java.util.Collections.singletonMap;

/**
 * Redis配置类
 * 默认情况下RedisTemplate模板只能支持字符串，自定义一个RedisTemplate，
 * 设置序列化器，这样可以很方便的操作实例对象。
 */
@Configuration
@EnableCaching // 开启缓存支持
public class RedisConfig
{
	/**
	 * RedisTemplate配置
	 *
	 * @param lettuceConnectionFactory
	 * @return
	 */
	@Bean
	public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) 
	{
		//JSON序列化配置,使用Json解析任何序列化对象
		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
		//需要通过ObjectMapper进行转义，然后才能使用;
		ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
        //启用字段可见性ANY 
		objectMapper.setVisibility(
				PropertyAccessor.ALL, 
				JsonAutoDetect.Visibility.ANY
				);
		objectMapper.activateDefaultTyping(
				LaissezFaireSubTypeValidator.instance,
				ObjectMapper.DefaultTyping.NON_FINAL, 
				JsonTypeInfo.As.PROPERTY
				);
		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

		// 配置redisTemplate
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
		// 开启事务
        //redisTemplate.setEnableTransactionSupport(true);
		//设置连接工厂
		redisTemplate.setConnectionFactory(lettuceConnectionFactory);

		//String的序列化
		RedisSerializer<?> stringSerializer = new StringRedisSerializer();
		//key采用String的序列化方式
		redisTemplate.setKeySerializer(stringSerializer);
		//value采用jackson的方式序列化
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化

		//hash的key也采用String的序列化方式
		redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
		//hash的value采用jackson的方式序列化
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
		//将所有的配置放进去
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	/**
	 * 缓存配置管理器
	 *
	 * @param factory
	 * @return
	 */
	@Bean
	public RedisCacheManager cacheManager(LettuceConnectionFactory factory) {
		// 配置序列化（缓存默认有效期 6小时）
		RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
		RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

		// 以锁写入的方式创建RedisCacheWriter对象
		//RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
		// 创建默认缓存配置对象
		/* 默认配置，设置缓存有效期 1小时*/
		// RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
		/* 自定义配置test:demo 的超时时间为 5分钟*/
		RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration)
				.withInitialCacheConfigurations(singletonMap(CommonConst.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
				.transactionAware().build();
		return cacheManager;
	}
}


