缓存抢购货物

This commit is contained in:
2026-06-07 16:20:25 +08:00
parent dd0cf8d74d
commit 6e249db5a1
5 changed files with 279 additions and 17 deletions

View File

@@ -522,6 +522,12 @@ public class LbBuyAccountServiceImpl
private static String formatRushBuyAccountLabel(String accountId, LbBuyAccount account) {
StringBuilder sb = new StringBuilder("抢单账号");
if (account != null) {
if (account.getTenantId() != null && !account.getTenantId().trim().isEmpty()) {
sb.append(" tenantId=").append(account.getTenantId().trim());
}
if (account.getTenantName() != null && !account.getTenantName().trim().isEmpty()) {
sb.append(" tenantName=").append(account.getTenantName().trim());
}
if (account.getLoginAccount() != null && !account.getLoginAccount().trim().isEmpty()) {
sb.append(" loginAccount=").append(account.getLoginAccount().trim());
}
@@ -649,7 +655,8 @@ public class LbBuyAccountServiceImpl
}
private static String formatRushBuySuccessDetail(Map<?, ?> detail) {
return "货品ID=" + detail.get("id")
return formatRushBuyDetailPrefix(detail)
+ "货品ID=" + detail.get("id")
+ ",金额=" + detail.get("totalMoney");
}
@@ -659,12 +666,21 @@ public class LbBuyAccountServiceImpl
String reason = apiCode != null
? "接口返回码=" + apiCode + (apiMsg != null ? "" + apiMsg : "")
: (apiMsg != null ? apiMsg.toString() : "未知原因");
return "货品ID=" + detail.get("id")
return formatRushBuyDetailPrefix(detail)
+ "货品ID=" + detail.get("id")
+ ",金额=" + detail.get("totalMoney")
+ ",原因=" + reason
+ ",返回信息=" + (apiMsg != null ? apiMsg : "");
}
private static String formatRushBuyDetailPrefix(Map<?, ?> detail) {
Object tenantId = detail.get("tenantId");
if (tenantId == null || tenantId.toString().trim().isEmpty()) {
return "";
}
return "tenantId=" + tenantId.toString().trim() + ",";
}
private static String truncateRushBuyResult(String text) {
if (text == null) {
return null;

View File

@@ -22,6 +22,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -558,15 +560,8 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
String previousTenantId = TenantContextHolder.getTenantId();
TenantContextHolder.setTenantId(tid);
try {
LocalDateTime updatedAfter = LocalDateTime.now().minusDays(1);
LambdaQueryWrapper<LbGoods> w = new LambdaQueryWrapper<>();
w.eq(LbGoods::getTenantId, tid);
w.isNotNull(LbGoods::getSellerId);
w.gt(LbGoods::getTotalMoney, RUSH_BUY_MIN_TOTAL_MONEY);
w.le(LbGoods::getTotalMoney, RUSH_BUY_MAX_TOTAL_MONEY);
w.ge(LbGoods::getUpdatedAt, updatedAfter);
w.orderByDesc(LbGoods::getTotalMoney);
List<LbGoods> goodsList = this.list(w);
log.info("查询-待抢购-货物列表......");
List<LbGoods> goodsList = resolveRushBuyGoodsList(tid, accountTag, goodsCoordinator);//
if (goodsList.isEmpty()) {
result.put("success", false);
result.put("message", "lb_goods 中无可抢购货品金额需大于25000且不超过38000且更新时间需在24小时内");
@@ -587,6 +582,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
boolean stoppedByDailyLimit = false;
boolean stoppedByLoginRequired = false;
boolean stoppedByActivityNotStarted = false;
boolean stoppedByActivityEnded = false;
boolean tokenRefreshAttempted = false;
boolean tokenRefreshed = false;
boolean restartAfterTokenRefresh;
@@ -598,6 +594,10 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
stoppedByActivityNotStarted = true;
break;
}
if (goodsCoordinator != null && goodsCoordinator.isActivityEnded()) {
stoppedByActivityEnded = true;
break;
}
if (successCount >= maxBuyCount) {
stoppedByMax = true;
break;
@@ -614,6 +614,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
&& !goodsCoordinator.tryAcquire(tid, goods.getId(), accountId)) {
skippedCount++;
Map<String, Object> skippedItem = new LinkedHashMap<>();
skippedItem.put("tenantId", tid);
skippedItem.put("id", goods.getId());
skippedItem.put("sellerId", goods.getSellerId());
skippedItem.put("title", goods.getTitle());
@@ -635,6 +636,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
attemptCount++;
Map<String, Object> item = new LinkedHashMap<>();
item.put("tenantId", tid);
item.put("id", goods.getId());
item.put("sellerId", goods.getSellerId());
item.put("title", goods.getTitle());
@@ -690,6 +692,18 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
}
break;
}
if (buyResult.activityEnded()) {
stoppedByActivityEnded = true;
if (goodsCoordinator != null) {
goodsCoordinator.markActivityEnded();
}
if (accountTag.isEmpty()) {
log.info("活动已结束,停止继续抢购");
} else {
log.info("{} 活动已结束,停止继续抢购", accountTag);
}
break;
}
if (buyResult.loginRequired()) {
if (tokenRefreshContext != null && !tokenRefreshAttempted) {
if (accountTag.isEmpty()) {
@@ -752,11 +766,17 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
? HxrAdminBuyService.MSG_ACTIVITY_NOT_STARTED
+ ",已停止抢购;成功 " + successCount + " 笔,失败 " + failCount + ""
: HxrAdminBuyService.MSG_ACTIVITY_NOT_STARTED);
} else if (stoppedByActivityEnded) {
result.put("message", successCount > 0
? HxrAdminBuyService.MSG_ACTIVITY_ENDED
+ ",已停止抢购;成功 " + successCount + " 笔,失败 " + failCount + ""
: HxrAdminBuyService.MSG_ACTIVITY_ENDED);
} else {
result.put("message", successCount > 0
? "抢购完成,成功 " + successCount + " 笔,失败 " + failCount + ""
: "抢购未成功");
}
result.put("tenantId", tid);
result.put("successCount", successCount);
result.put("failCount", failCount);
result.put("skippedCount", skippedCount);
@@ -767,6 +787,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
result.put("stoppedByDailyLimit", stoppedByDailyLimit);
result.put("stoppedByLoginRequired", stoppedByLoginRequired);
result.put("stoppedByActivityNotStarted", stoppedByActivityNotStarted);
result.put("stoppedByActivityEnded", stoppedByActivityEnded);
result.put("tokenRefreshed", tokenRefreshed);
result.put("details", details);
if (accountTag.isEmpty()) {
@@ -801,6 +822,63 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
return rushBuyAccountLabel.trim();
}
private List<LbGoods> resolveRushBuyGoodsList(
String tid, String accountTag, LbRushBuyGoodsCoordinator goodsCoordinator) {
if (goodsCoordinator == null) {
return loadRushBuyGoodsList(tid, accountTag);
}
List<LbGoods> cached = goodsCoordinator.peekRushBuyGoodsList(tid);
if (cached != null) {
if (accountTag.isEmpty()) {
log.info("rushBuy 复用同批次已缓存的可抢货品tenantId={},结果数量={}", tid, cached.size());
} else {
log.info("{} rushBuy 复用同批次已缓存的可抢货品tenantId={},结果数量={}",
accountTag, tid, cached.size());
}
return cached;
}
return goodsCoordinator.resolveRushBuyGoodsList(tid, () -> loadRushBuyGoodsList(tid, accountTag));
}
private List<LbGoods> loadRushBuyGoodsList(String tid, String accountTag) {
LocalDateTime updatedAfter = resolveRushBuyUpdatedAfter();
LambdaQueryWrapper<LbGoods> w = new LambdaQueryWrapper<>();
w.isNotNull(LbGoods::getSellerId);
w.gt(LbGoods::getTotalMoney, RUSH_BUY_MIN_TOTAL_MONEY);
w.le(LbGoods::getTotalMoney, RUSH_BUY_MAX_TOTAL_MONEY);
w.ge(LbGoods::getUpdatedAt, updatedAfter);
w.orderByDesc(LbGoods::getTotalMoney);
long goodsQueryStartedAt = System.nanoTime();
List<LbGoods> goodsList = this.list(w);
long goodsQueryElapsedMs = (System.nanoTime() - goodsQueryStartedAt) / 1_000_000L;
if (accountTag.isEmpty()) {
log.info("rushBuy 查询可抢货品耗时 {} mstenantId={}updatedAfter={},结果数量={}",
goodsQueryElapsedMs, tid, updatedAfter, goodsList.size());
} else {
log.info("{} rushBuy 查询可抢货品耗时 {} mstenantId={}updatedAfter={},结果数量={}",
accountTag, goodsQueryElapsedMs, tid, updatedAfter, goodsList.size());
}
return goodsList;
}
/**
* 周二至周五:取当前时间往前 24 小时;周六、周日、周一:取最近一个周五 00:00:00。
*/
private static LocalDateTime resolveRushBuyUpdatedAfter() {
LocalDate today = LocalDate.now();
DayOfWeek dow = today.getDayOfWeek();
if (dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY || dow == DayOfWeek.MONDAY) {
int daysToLastFriday = switch (dow) {
case SATURDAY -> 1;
case SUNDAY -> 2;
case MONDAY -> 3;
default -> throw new IllegalStateException("unreachable");
};
return today.minusDays(daysToLastFriday).atStartOfDay();
}
return LocalDateTime.now().minusDays(1);
}
private static HxrGoodsApiContext withToken(HxrGoodsApiContext ctx, String token) {
return new HxrGoodsApiContext(
ctx.goodsApiBaseUrl(),