Skip to content

Commit 87eee46

Browse files
committed
refactor: cache manager and sentry to use interfaces and impl classes
1 parent c66cfc0 commit 87eee46

6 files changed

Lines changed: 206 additions & 172 deletions

File tree

src/main/java/top/howiehz/halo/plugin/extra/api/finder/ExtraApiStatsFinderImpl.java renamed to src/main/java/top/howiehz/halo/plugin/extra/api/finder/impl/ExtraApiStatsFinderImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
package top.howiehz.halo.plugin.extra.api.finder;
1+
package top.howiehz.halo.plugin.extra.api.finder.impl;
22

33
import java.util.*;
44
import java.math.BigInteger;
55
import org.springframework.stereotype.Component;
66
import reactor.core.publisher.Mono;
77
import run.halo.app.theme.finders.Finder;
8+
import top.howiehz.halo.plugin.extra.api.finder.ExtraApiStatsFinder;
89
import top.howiehz.halo.plugin.extra.api.service.PostWordCountService;
910

1011
/**
Lines changed: 8 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,20 @@
11
package top.howiehz.halo.plugin.extra.api.service;
22

3-
import lombok.RequiredArgsConstructor;
4-
import lombok.extern.slf4j.Slf4j;
53
import org.springframework.lang.Nullable;
6-
import org.springframework.stereotype.Component;
7-
import java.util.concurrent.ConcurrentHashMap;
8-
import java.util.concurrent.atomic.AtomicReference;
94
import java.math.BigInteger;
105

11-
/**
12-
* Stats Data Manager - 统计数据管理器。
13-
* 分别缓存发布版本和草稿版本的字数统计。
14-
*/
15-
@Slf4j
16-
@Component
17-
@RequiredArgsConstructor
18-
public class PostStatsDataCacheManager {
19-
20-
// 文章发布版本字数统计缓存 / Total & per-post caches for release version
21-
private final AtomicReference<BigInteger> totalReleasePostWordCount =
22-
new AtomicReference<>(null);
23-
final ConcurrentHashMap<String, BigInteger> releasePostWordCounts = new ConcurrentHashMap<>();
24-
// 文章草稿版本字数统计缓存 / Total & per-post caches for draft version
25-
private final AtomicReference<BigInteger> totalDraftPostWordCount = new AtomicReference<>(null);
26-
final ConcurrentHashMap<String, BigInteger> draftPostWordCounts = new ConcurrentHashMap<>();
27-
28-
/**
29-
* 获取缓存的总字数。
30-
* Get cached total word count.
31-
*
32-
* @param isDraft 是否为草稿版本 / Whether it's draft version
33-
* @return 总字数,未缓存时返回 null / Total word count, returns null if not cached
34-
*/
6+
public interface PostStatsDataCacheManager {
357
@Nullable
36-
public BigInteger getCachedTotalWordCount(boolean isDraft) {
37-
return isDraft ? totalDraftPostWordCount.get() : totalReleasePostWordCount.get();
38-
}
8+
BigInteger getCachedTotalWordCount(boolean isDraft);
399

40-
/**
41-
* 获取缓存的文章字数。
42-
* Get cached word count for a specific post.
43-
*
44-
* @param postName 文章名称 / Post name
45-
* @param isDraft 是否为草稿版本 / Whether it's draft version
46-
* @return 文章字数,未缓存时返回 null / Post word count, returns null if not cached
47-
*/
4810
@Nullable
49-
public BigInteger getCachedPostWordCount(String postName, boolean isDraft) {
50-
return isDraft ? draftPostWordCounts.get(postName) : releasePostWordCounts.get(postName);
51-
}
52-
53-
/**
54-
* 设置/更新总字数缓存。
55-
* Set/Update total word count cache.
56-
*
57-
* @param count 总字数 / Total word count
58-
* @param isDraft 是否为草稿版本 / Whether it's draft version
59-
*/
60-
public void setTotalPostWordCount(BigInteger count, boolean isDraft) {
61-
if (isDraft) {
62-
totalDraftPostWordCount.set(count);
63-
log.debug("Set total DRAFT word count cache: {}", count);
64-
} else {
65-
totalReleasePostWordCount.set(count);
66-
log.debug("Set total RELEASE word count cache: {}", count);
67-
}
68-
}
11+
BigInteger getCachedPostWordCount(String postName, boolean isDraft);
6912

70-
/**
71-
* 设置/更新特定文章的字数缓存。
72-
* Set/Update word count cache for a specific post.
73-
*
74-
* @param postName 文章名称 / Post name
75-
* @param count 字数 / Word count
76-
* @param isDraft 是否为草稿版本 / Whether it's draft version
77-
*/
78-
public void setPostWordCount(String postName, BigInteger count, boolean isDraft) {
79-
if (isDraft) {
80-
draftPostWordCounts.put(postName, count);
81-
log.debug("Set DRAFT word count cache for post {}: {}", postName, count);
82-
} else {
83-
releasePostWordCounts.put(postName, count);
84-
log.debug("Set RELEASE word count cache for post {}: {}", postName, count);
85-
}
86-
}
13+
void setTotalPostWordCount(BigInteger count, boolean isDraft);
8714

88-
/**
89-
* 清除总字数缓存。
90-
* Clear total word count cache.
91-
*
92-
* @param isDraft 是否为草稿版本 / Whether it's draft version
93-
*/
94-
public void clearTotalPostWordCountCache(boolean isDraft) {
95-
if (isDraft) {
96-
totalDraftPostWordCount.set(null);
97-
log.debug("Cleared total DRAFT word count cache");
98-
} else {
99-
totalReleasePostWordCount.set(null);
100-
log.debug("Cleared total RELEASE word count cache");
101-
}
102-
}
15+
void setPostWordCount(String postName, BigInteger count, boolean isDraft);
10316

104-
/**
105-
* 清除特定文章的字数缓存。
106-
* Clear word count cache for a specific post.
107-
*
108-
* @param postName 文章名称 / Post name
109-
* @param isDraft 是否为草稿版本 / Whether it's draft version
110-
*/
111-
public void clearPostWordCountCache(String postName, boolean isDraft) {
112-
if (postName == null || postName.trim().isEmpty()) {
113-
log.warn(
114-
"Post name is null or empty, skipping cache clear / 文章名称为空,跳过缓存清理");
115-
return;
116-
}
17+
void clearTotalPostWordCountCache(boolean isDraft);
11718

118-
if (isDraft) {
119-
draftPostWordCounts.remove(postName);
120-
log.debug("Cleared DRAFT word count cache for post");
121-
} else {
122-
releasePostWordCounts.remove(postName);
123-
log.debug("Cleared RELEASE word count cache for post");
124-
}
125-
}
126-
}
19+
void clearPostWordCountCache(String postName, boolean isDraft);
20+
}
Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,9 @@
11
package top.howiehz.halo.plugin.extra.api.service;
22

3-
import lombok.extern.slf4j.Slf4j;
4-
import org.springframework.boot.context.event.ApplicationReadyEvent;
53
import org.springframework.context.event.EventListener;
6-
import org.springframework.stereotype.Component;
7-
import reactor.core.scheduler.Schedulers;
84
import run.halo.app.event.post.PostUpdatedEvent;
95

10-
/**
11-
* Sentry for evicting and updating word count cache when posts are updated.
12-
* 文章更新时负责清理和更新字数缓存的哨兵。
13-
*/
14-
@Slf4j
15-
@Component
16-
public class PostStatsDataCacheSentry {
17-
private final PostWordCountService postWordCountService;
18-
19-
public PostStatsDataCacheSentry(PostWordCountService postWordCountService) {
20-
this.postWordCountService = postWordCountService;
21-
}
22-
23-
/**
24-
* Handle post updated event and refresh caches asynchronously for release and draft.
25-
* 处理文章更新事件,并异步刷新发布版与草稿版的缓存。
26-
*/
27-
@EventListener
28-
void onPostUpdated(PostUpdatedEvent event) {
29-
String postName = event.getName();
30-
// Update release cache and invalidate total
31-
// 异步更新发布版本缓存
32-
postWordCountService.refreshPostCountCache(postName, false)
33-
.subscribeOn(Schedulers.parallel())
34-
.then(postWordCountService.refreshTotalPostCountFromCache(false)).subscribe(v -> {
35-
}, e -> log.warn("Update release word count failed for {}: {}", postName,
36-
e.toString()));
37-
38-
// Update draft cache and invalidate total
39-
// 异步更新草稿版本缓存
40-
postWordCountService.refreshPostCountCache(postName, true)
41-
.subscribeOn(Schedulers.parallel())
42-
.then(postWordCountService.refreshTotalPostCountFromCache(true)).subscribe(v -> {
43-
}, e -> log.warn("Update draft word count failed for {}: {}", postName, e.toString()));
44-
45-
log.info("Received post updated event, and refresh page count cache");
46-
}
47-
6+
public interface PostStatsDataCacheSentry {
487
@EventListener
49-
void onAppReady(ApplicationReadyEvent event) {
50-
postWordCountService.warmUpAllCache();
51-
log.info("App is ready, warm up all post word count caches");
52-
}
8+
void onPostUpdated(PostUpdatedEvent event);
539
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package top.howiehz.halo.plugin.extra.api.service.impl;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.lang.Nullable;
6+
import org.springframework.stereotype.Component;
7+
import top.howiehz.halo.plugin.extra.api.service.PostStatsDataCacheManager;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.concurrent.atomic.AtomicReference;
10+
import java.math.BigInteger;
11+
12+
/**
13+
* Stats Data Manager - 统计数据管理器。
14+
* 分别缓存发布版本和草稿版本的字数统计。
15+
*/
16+
@Slf4j
17+
@Component
18+
@RequiredArgsConstructor
19+
public class PostStatsDataCacheManagerImpl implements PostStatsDataCacheManager {
20+
21+
// 文章发布版本字数统计缓存 / Total & per-post caches for release version
22+
private final AtomicReference<BigInteger> totalReleasePostWordCount =
23+
new AtomicReference<>(null);
24+
final ConcurrentHashMap<String, BigInteger> releasePostWordCounts = new ConcurrentHashMap<>();
25+
// 文章草稿版本字数统计缓存 / Total & per-post caches for draft version
26+
private final AtomicReference<BigInteger> totalDraftPostWordCount = new AtomicReference<>(null);
27+
final ConcurrentHashMap<String, BigInteger> draftPostWordCounts = new ConcurrentHashMap<>();
28+
29+
/**
30+
* 获取缓存的总字数。
31+
* Get cached total word count.
32+
*
33+
* @param isDraft 是否为草稿版本 / Whether it's draft version
34+
* @return 总字数,未缓存时返回 null / Total word count, returns null if not cached
35+
*/
36+
@Nullable
37+
@Override
38+
public BigInteger getCachedTotalWordCount(boolean isDraft) {
39+
return isDraft ? totalDraftPostWordCount.get() : totalReleasePostWordCount.get();
40+
}
41+
42+
/**
43+
* 获取缓存的文章字数。
44+
* Get cached word count for a specific post.
45+
*
46+
* @param postName 文章名称 / Post name
47+
* @param isDraft 是否为草稿版本 / Whether it's draft version
48+
* @return 文章字数,未缓存时返回 null / Post word count, returns null if not cached
49+
*/
50+
@Nullable
51+
@Override
52+
public BigInteger getCachedPostWordCount(String postName, boolean isDraft) {
53+
return isDraft ? draftPostWordCounts.get(postName) : releasePostWordCounts.get(postName);
54+
}
55+
56+
/**
57+
* 设置/更新总字数缓存。
58+
* Set/Update total word count cache.
59+
*
60+
* @param count 总字数 / Total word count
61+
* @param isDraft 是否为草稿版本 / Whether it's draft version
62+
*/
63+
@Override
64+
public void setTotalPostWordCount(BigInteger count, boolean isDraft) {
65+
if (isDraft) {
66+
totalDraftPostWordCount.set(count);
67+
log.debug("Set total DRAFT word count cache: {}", count);
68+
} else {
69+
totalReleasePostWordCount.set(count);
70+
log.debug("Set total RELEASE word count cache: {}", count);
71+
}
72+
}
73+
74+
/**
75+
* 设置/更新特定文章的字数缓存。
76+
* Set/Update word count cache for a specific post.
77+
*
78+
* @param postName 文章名称 / Post name
79+
* @param count 字数 / Word count
80+
* @param isDraft 是否为草稿版本 / Whether it's draft version
81+
*/
82+
@Override
83+
public void setPostWordCount(String postName, BigInteger count, boolean isDraft) {
84+
if (isDraft) {
85+
draftPostWordCounts.put(postName, count);
86+
log.debug("Set DRAFT word count cache for post {}: {}", postName, count);
87+
} else {
88+
releasePostWordCounts.put(postName, count);
89+
log.debug("Set RELEASE word count cache for post {}: {}", postName, count);
90+
}
91+
}
92+
93+
/**
94+
* 清除总字数缓存。
95+
* Clear total word count cache.
96+
*
97+
* @param isDraft 是否为草稿版本 / Whether it's draft version
98+
*/
99+
@Override
100+
public void clearTotalPostWordCountCache(boolean isDraft) {
101+
if (isDraft) {
102+
totalDraftPostWordCount.set(null);
103+
log.debug("Cleared total DRAFT word count cache");
104+
} else {
105+
totalReleasePostWordCount.set(null);
106+
log.debug("Cleared total RELEASE word count cache");
107+
}
108+
}
109+
110+
/**
111+
* 清除特定文章的字数缓存。
112+
* Clear word count cache for a specific post.
113+
*
114+
* @param postName 文章名称 / Post name
115+
* @param isDraft 是否为草稿版本 / Whether it's draft version
116+
*/
117+
@Override
118+
public void clearPostWordCountCache(String postName, boolean isDraft) {
119+
if (postName == null || postName.trim().isEmpty()) {
120+
log.warn(
121+
"Post name is null or empty, skipping cache clear / 文章名称为空,跳过缓存清理");
122+
return;
123+
}
124+
125+
if (isDraft) {
126+
draftPostWordCounts.remove(postName);
127+
log.debug("Cleared DRAFT word count cache for post");
128+
} else {
129+
releasePostWordCounts.remove(postName);
130+
log.debug("Cleared RELEASE word count cache for post");
131+
}
132+
}
133+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package top.howiehz.halo.plugin.extra.api.service.impl;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.context.event.EventListener;
5+
import org.springframework.stereotype.Component;
6+
import reactor.core.scheduler.Schedulers;
7+
import run.halo.app.event.post.PostUpdatedEvent;
8+
import top.howiehz.halo.plugin.extra.api.service.PostStatsDataCacheSentry;
9+
import top.howiehz.halo.plugin.extra.api.service.PostWordCountService;
10+
11+
/**
12+
* Sentry for evicting and updating word count cache when posts are updated.
13+
* 文章更新时负责清理和更新字数缓存的哨兵。
14+
*/
15+
@Slf4j
16+
@Component
17+
public class PostStatsDataCacheSentryImpl implements PostStatsDataCacheSentry {
18+
private final PostWordCountService postWordCountService;
19+
20+
public PostStatsDataCacheSentryImpl(PostWordCountService postWordCountService) {
21+
this.postWordCountService = postWordCountService;
22+
}
23+
24+
/**
25+
* Handle post updated event and refresh caches asynchronously for release and draft.
26+
* 处理文章更新事件,并异步刷新发布版与草稿版的缓存。
27+
*/
28+
@EventListener
29+
@Override
30+
public void onPostUpdated(PostUpdatedEvent event) {
31+
String postName = event.getName();
32+
// Update release cache and invalidate total
33+
// 异步更新发布版本缓存
34+
postWordCountService.refreshPostCountCache(postName, false)
35+
.subscribeOn(Schedulers.parallel())
36+
.then(postWordCountService.refreshTotalPostCountFromCache(false)).subscribe(v -> {
37+
}, e -> log.warn("Update release word count failed for {}: {}", postName,
38+
e.toString()));
39+
40+
// Update draft cache and invalidate total
41+
// 异步更新草稿版本缓存
42+
postWordCountService.refreshPostCountCache(postName, true)
43+
.subscribeOn(Schedulers.parallel())
44+
.then(postWordCountService.refreshTotalPostCountFromCache(true)).subscribe(v -> {
45+
}, e -> log.warn("Update draft word count failed for {}: {}", postName, e.toString()));
46+
47+
log.info("Received post updated event, and refresh page count cache");
48+
}
49+
}

0 commit comments

Comments
 (0)