55import com .yqz .openblog .article .dto .ArticleListItemResponse ;
66import com .yqz .openblog .article .dto .ArticlePublishedContentCachePayload ;
77import com .yqz .openblog .common .PageResult ;
8- import com .yqz .openblog .config .CacheProperties ;
8+ import com .yqz .openblog .redis .config .RedisProperties ;
9+ import com .yqz .openblog .redis .core .RedisKeys ;
10+ import com .yqz .openblog .redis .core .RedisOps ;
911import org .slf4j .Logger ;
1012import org .slf4j .LoggerFactory ;
11- import org .springframework .data .redis .core .StringRedisTemplate ;
1213import org .springframework .stereotype .Service ;
1314
1415import java .time .Duration ;
2829@ Service
2930public class ArticlePublishedContentCacheService {
3031
31- private static final String KEY_PREFIX = "openblog:article:published:content:" ;
32- private static final String LIST_KEY_PREFIX = "openblog:article:published:list:v" ;
33- private static final String LIST_VERSION_KEY = "openblog:article:published:list:version" ;
34-
3532 private static final TypeReference <PageResult <ArticleListItemResponse >> LIST_PAGE_TYPE =
3633 new TypeReference <>() {};
3734
3835 private static final Logger log = LoggerFactory .getLogger (ArticlePublishedContentCacheService .class );
3936
40- private final StringRedisTemplate redisTemplate ;
37+ private final RedisOps redisOps ;
4138 private final ObjectMapper objectMapper ;
42- private final CacheProperties cacheProperties ;
39+ private final RedisProperties redisProperties ;
4340
4441 public ArticlePublishedContentCacheService (
45- StringRedisTemplate redisTemplate ,
42+ RedisOps redisOps ,
4643 ObjectMapper objectMapper ,
47- CacheProperties cacheProperties ) {
48- this .redisTemplate = redisTemplate ;
44+ RedisProperties redisProperties ) {
45+ this .redisOps = redisOps ;
4946 this .objectMapper = objectMapper ;
50- this .cacheProperties = cacheProperties ;
47+ this .redisProperties = redisProperties ;
5148 }
5249
5350 // ==================== 文章正文缓存(个体) ====================
@@ -56,14 +53,14 @@ public Optional<ArticlePublishedContentCachePayload> get(Long articleId) {
5653 if (articleId == null ) {
5754 return Optional .empty ();
5855 }
56+ String json = redisOps .get (RedisKeys .articleBody (articleId )).orElse (null );
57+ if (json == null || json .isBlank ()) {
58+ return Optional .empty ();
59+ }
5960 try {
60- String json = redisTemplate .opsForValue ().get (key (articleId ));
61- if (json == null || json .isBlank ()) {
62- return Optional .empty ();
63- }
6461 return Optional .of (objectMapper .readValue (json , ArticlePublishedContentCachePayload .class ));
6562 } catch (Exception e ) {
66- log .warn ("读取文章正文缓存失败,降级为数据库 。articleId={}" , articleId , e );
63+ log .warn ("反序列化文章正文缓存失败 。articleId={}" , articleId , e );
6764 return Optional .empty ();
6865 }
6966 }
@@ -72,24 +69,20 @@ public void put(Long articleId, ArticlePublishedContentCachePayload payload) {
7269 if (articleId == null || payload == null ) {
7370 return ;
7471 }
75- int minutes = Math .max (1 , cacheProperties .getArticlePublishedTtlMinutes ());
72+ int minutes = Math .max (1 , redisProperties .getArticlePublishedTtlMinutes ());
7673 try {
7774 String json = objectMapper .writeValueAsString (payload );
78- redisTemplate . opsForValue (). set (key (articleId ), json , Duration .ofMinutes (minutes ));
75+ redisOps . set (RedisKeys . articleBody (articleId ), json , Duration .ofMinutes (minutes ));
7976 } catch (Exception e ) {
80- log .warn ("写入文章正文缓存失败 (已忽略,不影响响应 )。articleId={}" , articleId , e );
77+ log .warn ("序列化文章正文缓存失败 (已忽略)。articleId={}" , articleId , e );
8178 }
8279 }
8380
8481 public void evict (Long articleId ) {
8582 if (articleId == null ) {
8683 return ;
8784 }
88- try {
89- redisTemplate .delete (key (articleId ));
90- } catch (Exception e ) {
91- log .warn ("删除文章正文缓存失败(已忽略)。articleId={}" , articleId , e );
92- }
85+ redisOps .delete (RedisKeys .articleBody (articleId ));
9386 }
9487
9588 // ==================== 文章列表缓存(分页) ====================
@@ -98,37 +91,35 @@ public void evict(Long articleId) {
9891 * 获取当前列表版本号。版本号用于构造缓存 key,不存在时默认为 0。
9992 */
10093 private long getListVersion () {
101- try {
102- String v = redisTemplate .opsForValue ().get (LIST_VERSION_KEY );
103- return v == null ? 0L : Long .parseLong (v );
104- } catch (Exception e ) {
105- log .warn ("读取列表版本号失败,降级为 version=0。" , e );
106- return 0L ;
107- }
94+ return redisOps .get (RedisKeys .CONTENT_ARTICLE_LIST_VERSION )
95+ .map (v -> {
96+ try {
97+ return Long .parseLong (v );
98+ } catch (NumberFormatException e ) {
99+ return 0L ;
100+ }
101+ })
102+ .orElse (0L );
108103 }
109104
110105 /**
111106 * 递增列表版本号,使所有旧版本列表缓存自然过期(靠 TTL 清理)。
112107 * 任何影响已发布列表的写操作(发布、取消发布、更新已发布文章、删除)都应调用此方法。
113108 */
114109 public void evictPublishedList () {
115- try {
116- redisTemplate .opsForValue ().increment (LIST_VERSION_KEY );
117- } catch (Exception e ) {
118- log .warn ("递增列表版本号失败(已忽略)。" , e );
119- }
110+ redisOps .increment (RedisKeys .CONTENT_ARTICLE_LIST_VERSION );
120111 }
121112
122113 public Optional <PageResult <ArticleListItemResponse >> getList (Long categoryId , int page , int size ) {
114+ long version = getListVersion ();
115+ String json = redisOps .get (RedisKeys .articleList (version , categoryId , page , size )).orElse (null );
116+ if (json == null || json .isBlank ()) {
117+ return Optional .empty ();
118+ }
123119 try {
124- long version = getListVersion ();
125- String json = redisTemplate .opsForValue ().get (listKey (version , categoryId , page , size ));
126- if (json == null || json .isBlank ()) {
127- return Optional .empty ();
128- }
129120 return Optional .of (objectMapper .readValue (json , LIST_PAGE_TYPE ));
130121 } catch (Exception e ) {
131- log .warn ("读取文章列表缓存失败,降级为数据库 。categoryId={}, page={}, size={}" , categoryId , page , size , e );
122+ log .warn ("反序列化文章列表缓存失败 。categoryId={}, page={}, size={}" , categoryId , page , size , e );
132123 return Optional .empty ();
133124 }
134125 }
@@ -137,23 +128,13 @@ public void putList(Long categoryId, int page, int size, PageResult<ArticleListI
137128 if (payload == null ) {
138129 return ;
139130 }
140- int minutes = Math .max (1 , cacheProperties .getArticleListTtlMinutes ());
131+ int minutes = Math .max (1 , redisProperties .getArticleListTtlMinutes ());
141132 try {
142133 long version = getListVersion ();
143134 String json = objectMapper .writeValueAsString (payload );
144- redisTemplate . opsForValue (). set (listKey (version , categoryId , page , size ), json , Duration .ofMinutes (minutes ));
135+ redisOps . set (RedisKeys . articleList (version , categoryId , page , size ), json , Duration .ofMinutes (minutes ));
145136 } catch (Exception e ) {
146- log .warn ("写入文章列表缓存失败 (已忽略)。categoryId={}, page={}, size={}" , categoryId , page , size , e );
137+ log .warn ("序列化文章列表缓存失败 (已忽略)。categoryId={}, page={}, size={}" , categoryId , page , size , e );
147138 }
148139 }
149-
150- // ==================== Key 构造 ====================
151-
152- private static String key (Long articleId ) {
153- return KEY_PREFIX + articleId ;
154- }
155-
156- private static String listKey (long version , Long categoryId , int page , int size ) {
157- return LIST_KEY_PREFIX + version + ":" + (categoryId == null ? "all" : categoryId ) + ":" + page + ":" + size ;
158- }
159140}
0 commit comments