package com.yunhai.cms.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
/**
* Caffeine 本地缓存装配(方案 §8.4 工程约束)。
*
*
* - TTL 随机抖动 ±60s 防雪崩:自定义 {@link JitterExpiry} 按条目随机;
* - null 哨兵 30s 防穿透(爬虫扫随机 ID):写入值为 Spring NullValue 标记时短 TTL;
* - CacheErrorHandler:缓存异常按 miss 降级 + error 日志(dirty data risk, TTL 自愈)。
*
* 切 Redis 时仅替换本配置与 spring.cache.type(方案 §1.2/§12);
* yml 中 spring.cache.caffeine.spec 供默认管理器/演进期使用,本管理器按 CacheNames 显式装配。
*/
@Configuration
@EnableCaching
public class CacheConfig implements CachingConfigurer {
/** 抖动幅度:±60s */
private static final Duration JITTER = Duration.ofSeconds(60);
/** null 哨兵 TTL */
private static final Duration NULL_TTL = Duration.ofSeconds(30);
/** 抖动后 TTL 下限,避免抖出负数/过短 */
private static final Duration MIN_TTL = Duration.ofSeconds(30);
private final LoggingCacheErrorHandler errorHandler;
public CacheConfig(LoggingCacheErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
@Bean
@Override
public CacheManager cacheManager() {
SimpleCacheManager manager = new SimpleCacheManager();
manager.setCaches(Arrays.asList(
new CaffeineCache(CacheNames.SITE, caffeine(Duration.ofMinutes(30)).build()),
new CaffeineCache(CacheNames.ARTICLE, caffeine(Duration.ofMinutes(5)).build()),
new CaffeineCache(CacheNames.LIST, caffeine(Duration.ofMinutes(5)).build())));
return manager;
}
@Override
public CacheErrorHandler errorHandler() {
return errorHandler;
}
private Caffeine