init local code
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package com.yunhai.cms.event;
|
||||
|
||||
/**
|
||||
* 文章内容变更事件(保存修改/下架/删除)。事务提交后触发缓存失效(方案 §8.4)。
|
||||
*/
|
||||
public class ArticleChangedEvent {
|
||||
|
||||
private final Integer articleId;
|
||||
private final Integer siteId;
|
||||
|
||||
public ArticleChangedEvent(Integer articleId, Integer siteId) {
|
||||
this.articleId = articleId;
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public Integer getArticleId() {
|
||||
return articleId;
|
||||
}
|
||||
|
||||
public Integer getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.yunhai.cms.event;
|
||||
|
||||
/**
|
||||
* 文章发布事件(方案 §7.4 微信预留挂点):事务提交后由 AFTER_COMMIT 监听器消费。
|
||||
* 本期监听方 = 缓存失效 + WeChatPushService(Noop 实现,零外联);
|
||||
* 启用《微信平台对接方案》时仅替换 Noop 实现,零表结构变更。
|
||||
*/
|
||||
public class ArticlePublishedEvent {
|
||||
|
||||
private final Integer articleId;
|
||||
private final Integer siteId;
|
||||
private final String title;
|
||||
|
||||
public ArticlePublishedEvent(Integer articleId, Integer siteId, String title) {
|
||||
this.articleId = articleId;
|
||||
this.siteId = siteId;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Integer getArticleId() {
|
||||
return articleId;
|
||||
}
|
||||
|
||||
public Integer getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.yunhai.cms.event;
|
||||
|
||||
/**
|
||||
* 站点内容/结构变更事件:站点本身、栏目树、轮播、友链 任一变更即发,
|
||||
* 门户站点快照缓存(cms:site)整体失效 + 列表缓存清除(方案 §8.4)。
|
||||
*/
|
||||
public class SiteContentChangedEvent {
|
||||
|
||||
private final Integer siteId;
|
||||
|
||||
/** 变更来源说明(日志用):site/category/banner/link */
|
||||
private final String scope;
|
||||
|
||||
public SiteContentChangedEvent(Integer siteId, String scope) {
|
||||
this.siteId = siteId;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public Integer getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.yunhai.cms.event.listener;
|
||||
|
||||
import com.yunhai.cms.config.CacheNames;
|
||||
import com.yunhai.cms.event.ArticleChangedEvent;
|
||||
import com.yunhai.cms.event.ArticlePublishedEvent;
|
||||
import com.yunhai.cms.event.SiteContentChangedEvent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
/**
|
||||
* 门户缓存失效监听(方案 §8.4):全部 AFTER_COMMIT(fallbackExecution=true 兼容
|
||||
* 定时任务等无事务路径),失效失败仅记日志,靠 TTL 自愈。
|
||||
*
|
||||
* <p>cms:list / cms:site 的 key 含分页与站点维度,MVP 直接整缓存 clear:
|
||||
* 单实例 Caffeine、站点 ≤20,clear 成本可忽略;精确 evict 留给 Redis 演进。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PortalCacheEvictListener {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
|
||||
public void onArticlePublished(ArticlePublishedEvent event) {
|
||||
evictArticle(event.getArticleId());
|
||||
clear(CacheNames.LIST);
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
|
||||
public void onArticleChanged(ArticleChangedEvent event) {
|
||||
evictArticle(event.getArticleId());
|
||||
clear(CacheNames.LIST);
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
|
||||
public void onSiteContentChanged(SiteContentChangedEvent event) {
|
||||
clear(CacheNames.SITE);
|
||||
clear(CacheNames.LIST);
|
||||
}
|
||||
|
||||
private void evictArticle(Integer articleId) {
|
||||
Cache cache = cacheManager.getCache(CacheNames.ARTICLE);
|
||||
if (cache != null && articleId != null) {
|
||||
cache.evict(articleId);
|
||||
}
|
||||
}
|
||||
|
||||
private void clear(String cacheName) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yunhai.cms.event.listener;
|
||||
|
||||
import com.yunhai.cms.event.ArticlePublishedEvent;
|
||||
import com.yunhai.cms.service.WeChatPushService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
/**
|
||||
* 文章发布 → 微信推送挂点(方案 §7.4 后端层预留)。
|
||||
* 事务提交后才触发,保证推送方读到的数据已落库;推送失败不影响业务事务。
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WeChatPushListener {
|
||||
|
||||
private final WeChatPushService weChatPushService;
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
|
||||
public void onArticlePublished(ArticlePublishedEvent event) {
|
||||
weChatPushService.onArticlePublished(event);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user