diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java index e0d2dbfa..dcbdbffa 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ShopManageService.java @@ -26,6 +26,7 @@ public class ShopManageService { private final ShopManageMapper shopManageMapper; private final ShopManageGroupService shopManageGroupService; private final ShopCredentialCryptoService shopCredentialCryptoService; + private final ZiniaoShopIndexRefreshTrigger ziniaoShopIndexRefreshTrigger; public ShopManagePageVo page(long page, long pageSize, Long groupId, String shopName, Long operatorId, boolean superAdmin) { long safePage = Math.max(page, 1); @@ -98,6 +99,7 @@ public class ShopManageService { entity.setAccount(normalizeRequired(request.getAccount(), "账号不能为空")); entity.setPassword(shopCredentialCryptoService.encrypt(normalizeRequired(request.getPassword(), "密码不能为空"))); shopManageMapper.insert(entity); + ziniaoShopIndexRefreshTrigger.trigger("create:" + entity.getShopName()); ShopManageEntity saved = getById(entity.getId()); return toItemVo(saved, group.getGroupName()); } @@ -121,6 +123,7 @@ public class ShopManageService { entity.setAccount(normalizeRequired(request.getAccount(), "账号不能为空")); entity.setPassword(shopCredentialCryptoService.encrypt(normalizeRequired(request.getPassword(), "密码不能为空"))); shopManageMapper.updateById(entity); + ziniaoShopIndexRefreshTrigger.trigger("update:" + entity.getShopName()); ShopManageEntity updated = getById(id); return toItemVo(updated, group.getGroupName()); } diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ZiniaoShopIndexRefreshTrigger.java b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ZiniaoShopIndexRefreshTrigger.java new file mode 100644 index 00000000..59209d21 --- /dev/null +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/shopkey/service/ZiniaoShopIndexRefreshTrigger.java @@ -0,0 +1,55 @@ +package com.nanri.aiimage.modules.shopkey.service; + +import com.nanri.aiimage.modules.ziniao.service.ZiniaoShopIndexRefreshService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 后台店铺添加/改名后异步触发一次紫鸟店铺索引刷新。 + * 调用方不阻塞;带防抖(合并窗口内的多次触发);拿不到分布式锁或刷新失败不影响添加流程。 + */ +@Service +@Slf4j +public class ZiniaoShopIndexRefreshTrigger { + + /** 防抖:触发后 N 分钟内的重复触发合并掉(刷新一轮本身要几十秒)。 */ + private static final long DEBOUNCE_MILLIS = Duration.ofMinutes(2).toMillis(); + + private final ZiniaoShopIndexRefreshService ziniaoShopIndexRefreshService; + private final AtomicLong lastTriggerAtMillis = new AtomicLong(0); + private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(runnable -> { + Thread thread = new Thread(runnable, "ziniao-index-refresh-trigger"); + thread.setDaemon(true); + return thread; + }); + + public ZiniaoShopIndexRefreshTrigger(ZiniaoShopIndexRefreshService ziniaoShopIndexRefreshService) { + this.ziniaoShopIndexRefreshService = ziniaoShopIndexRefreshService; + } + + public void trigger(String source) { + long now = System.currentTimeMillis(); + long last = lastTriggerAtMillis.get(); + if (now - last < DEBOUNCE_MILLIS && !lastTriggerAtMillis.compareAndSet(last, now)) { + log.info("[ziniao-index] 触发防抖合并 source={} 距上次触发不足2分钟", source); + return; + } + lastTriggerAtMillis.set(now); + // 延迟 10 秒:等添加事务提交 & 躲开定时轮刚启动的瞬间 + executor.schedule(() -> { + try { + ziniaoShopIndexRefreshService.refreshShopIndexManually(); + log.info("[ziniao-index] 店铺变动后增量刷新完成 source={}", source); + } catch (Exception ex) { + log.info("[ziniao-index] 店铺变动后触发刷新未执行(可能有定时轮在刷新),source={} msg={}", + source, ex.getMessage()); + } + }, 10, TimeUnit.SECONDS); + } +}