请购成功的版本
This commit is contained in:
@@ -2,14 +2,23 @@ package com.rj.scheduler;
|
||||
|
||||
import com.rj.config.AppConfig;
|
||||
import com.rj.config.HxrAdminProperties;
|
||||
import com.rj.entity.LbThirdIntegrationConfig;
|
||||
import com.rj.entity.LbGoods;
|
||||
import com.rj.service.HxrAdminOrderSelectService;
|
||||
import com.rj.service.ILbGoodsService;
|
||||
import com.rj.service.ILbOrderRowService;
|
||||
import com.rj.service.ILbThirdIntegrationConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.DayOfWeek;
|
||||
@@ -29,6 +38,9 @@ public class LBAdminPullScheduler {
|
||||
|
||||
private static final long FIFTY_MINUTES_MS = 30L * 60L * 1000L;
|
||||
private static final long UnPay_MINUTES_MS = 20L * 60L * 1000L;
|
||||
private static final long GOODS_PULL_INTERVAL_MS = 1000L;
|
||||
private static final LocalTime GOODS_PULL_WINDOW_START = LocalTime.of(9, 59);
|
||||
private static final LocalTime GOODS_PULL_WINDOW_END = LocalTime.of(22, 3, 59);
|
||||
|
||||
private static final DateTimeFormatter BUY_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@@ -36,8 +48,16 @@ public class LBAdminPullScheduler {
|
||||
private final HxrAdminProperties hxrAdminProperties;
|
||||
private final HxrAdminOrderSelectService hxrAdminOrderSelectService;
|
||||
private final ILbOrderRowService lbOrderRowService;
|
||||
private final ILbThirdIntegrationConfigService lbThirdIntegrationConfigService;
|
||||
private final ILbGoodsService lbGoodsService;
|
||||
private final Clock clock;
|
||||
|
||||
/** 防止货品拉取上一轮未完成时重复执行。 */
|
||||
private final AtomicBoolean goodsPullRunning = new AtomicBoolean(false);
|
||||
|
||||
/** 最近一次调度拉取的货品:租户 ID -> 货品列表(线程安全)。 */
|
||||
private final ConcurrentHashMap<String, List<LbGoods>> tenantGoodsCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Scheduled(
|
||||
fixedRate = FIFTY_MINUTES_MS,
|
||||
initialDelayString = "${hxr.admin.pull-initial-delay-ms:0}")
|
||||
@@ -147,5 +167,134 @@ public class LBAdminPullScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取抢购的物品
|
||||
* 货品同步:每 1 秒触发一次,仅在工作日 9:59~20:03(应用时区)窗口内真正执行;
|
||||
* 若上一轮尚未结束则跳过,避免重复并发执行。
|
||||
*/
|
||||
// @Scheduled(
|
||||
// fixedRate = GOODS_PULL_INTERVAL_MS,
|
||||
// initialDelayString = "${hxr.admin.pull-initial-delay-ms:0}")
|
||||
public void pullGoodsFromThirdParty() {
|
||||
try {
|
||||
|
||||
ZoneId zone = ZoneId.of(appConfig.getTimezone());
|
||||
if (!isWithinGoodsPullWindow(zone)) {
|
||||
log.debug(
|
||||
"当前不在货品拉取窗口(周一至周五 {}~{}),跳过",
|
||||
GOODS_PULL_WINDOW_START,
|
||||
GOODS_PULL_WINDOW_END);
|
||||
return;
|
||||
}
|
||||
if (!goodsPullRunning.compareAndSet(false, true)) {
|
||||
log.debug("货品拉取正在运行中,跳过重复执行");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
syncEnabledGoodsConfigs(zone);
|
||||
} finally {
|
||||
goodsPullRunning.set(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
goodsPullRunning.set(false);
|
||||
log.error("货品拉取调度失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWithinGoodsPullWindow(ZoneId zone) {
|
||||
ZonedDateTime nowZdt = ZonedDateTime.now(clock.withZone(zone));
|
||||
DayOfWeek dow = nowZdt.getDayOfWeek();
|
||||
if (dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY) {
|
||||
return false;
|
||||
}
|
||||
LocalTime now = nowZdt.toLocalTime();
|
||||
return !now.isBefore(GOODS_PULL_WINDOW_START) && !now.isAfter(GOODS_PULL_WINDOW_END);
|
||||
}
|
||||
|
||||
private void syncEnabledGoodsConfigs(ZoneId zone) {
|
||||
List<LbThirdIntegrationConfig> configs = lbThirdIntegrationConfigService.lambdaQuery()
|
||||
.eq(LbThirdIntegrationConfig::getEnabled, 1)
|
||||
.list();
|
||||
if (configs == null || configs.isEmpty()) {
|
||||
log.debug("无启用的第三方集成配置,跳过货品拉取");
|
||||
return;
|
||||
}
|
||||
if (!isWithinGoodsPullWindow(zone)) {
|
||||
log.debug("货品拉取窗口已结束,跳过本次调度");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("货品拉取开始,启用租户数={}", configs.size());
|
||||
ConcurrentHashMap<String, List<LbGoods>> runGoodsMap = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<String, Map<String, Object>> runResultMap = new ConcurrentHashMap<>();
|
||||
AtomicInteger skippedCount = new AtomicInteger(0);
|
||||
|
||||
List<CompletableFuture<Void>> futures = configs.stream()
|
||||
.map(config -> CompletableFuture.runAsync(() -> {
|
||||
String tenantId = config.getTenantId();
|
||||
if (tenantId == null || tenantId.isBlank()) {
|
||||
log.warn("集成配置 id={} 缺少 tenantId,跳过", config.getId());
|
||||
skippedCount.incrementAndGet();
|
||||
return;
|
||||
}
|
||||
String tid = tenantId.trim();
|
||||
Map<String, Object> fetchResult = lbGoodsService.fetchGoodsFromHxrInMemory(tid, null);
|
||||
runResultMap.put(tid, fetchResult);
|
||||
if (Boolean.TRUE.equals(fetchResult.get("success"))) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<LbGoods> goods = (List<LbGoods>) fetchResult.get("data");
|
||||
runGoodsMap.put(tid, goods != null ? goods : List.of());
|
||||
} else {
|
||||
runGoodsMap.put(tid, List.of());
|
||||
}
|
||||
}))
|
||||
.toList();
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
|
||||
|
||||
tenantGoodsCache.clear();
|
||||
tenantGoodsCache.putAll(runGoodsMap);
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
int totalGoods = 0;
|
||||
StringBuilder detail = new StringBuilder();
|
||||
for (Map.Entry<String, Map<String, Object>> entry : runResultMap.entrySet()) {
|
||||
String tid = entry.getKey();
|
||||
Map<String, Object> fetchResult = entry.getValue();
|
||||
boolean success = Boolean.TRUE.equals(fetchResult.get("success"));
|
||||
int fetched = fetchResult.get("fetched") instanceof Number n ? n.intValue() : 0;
|
||||
if (success) {
|
||||
successCount++;
|
||||
totalGoods += fetched;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
detail.append(System.lineSeparator())
|
||||
.append(" tenantId=")
|
||||
.append(tid)
|
||||
.append(" success=")
|
||||
.append(success)
|
||||
.append(" fetched=")
|
||||
.append(fetched)
|
||||
.append(" message=")
|
||||
.append(fetchResult.get("message"));
|
||||
}
|
||||
|
||||
log.info(
|
||||
"货品拉取调度完成 配置租户数={} 跳过={} 成功={} 失败={} 内存租户数={} 总货品数={}{}",
|
||||
configs.size(),
|
||||
skippedCount.get(),
|
||||
successCount,
|
||||
failCount,
|
||||
tenantGoodsCache.size(),
|
||||
totalGoods,
|
||||
detail);
|
||||
}
|
||||
|
||||
/** 获取最近一次调度写入内存的租户货品映射(只读视图)。 */
|
||||
public Map<String, List<LbGoods>> getTenantGoodsCache() {
|
||||
return Map.copyOf(tenantGoodsCache);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user