重构抢单代码
This commit is contained in:
@@ -3,6 +3,8 @@ package com.rj.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.rj.dto.LbBuyAccountRushBuyContext;
|
||||
import com.rj.dto.LbRushBuyGoodsCoordinator;
|
||||
import com.rj.entity.LbBuyAccount;
|
||||
import com.rj.mapper.LbBuyAccountMapper;
|
||||
import com.rj.service.ILbBuyAccountService;
|
||||
@@ -19,7 +21,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -34,6 +36,9 @@ public class LbBuyAccountServiceImpl
|
||||
/** 与表字段 rush_buy_result(TEXT)长度上限一致 */
|
||||
private static final int RUSH_BUY_RESULT_MAX_LEN = 65535;
|
||||
|
||||
/** 单次批量抢单最多并发线程数(与账号数取 min,每账号一线程) */
|
||||
private static final int RUSH_BUY_MAX_PARALLEL_THREADS = 64;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@@ -231,68 +236,35 @@ public class LbBuyAccountServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
int accountCount = normalizedIds.size();
|
||||
AtomicInteger threadSeq = new AtomicInteger(0);
|
||||
ExecutorService batchExecutor = Executors.newFixedThreadPool(accountCount, r -> {
|
||||
LbRushBuyGoodsCoordinator goodsCoordinator = new LbRushBuyGoodsCoordinator();
|
||||
int poolSize = Math.min(normalizedIds.size(), RUSH_BUY_MAX_PARALLEL_THREADS);
|
||||
AtomicInteger threadSeq = new AtomicInteger();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(poolSize, r -> {
|
||||
Thread t = new Thread(r, "lb-rush-buy-" + threadSeq.incrementAndGet());
|
||||
t.setDaemon(true);
|
||||
t.setDaemon(false);
|
||||
return t;
|
||||
});
|
||||
log.info("批量抢单启动,账号数={},独立线程数={}", accountCount, accountCount);
|
||||
log.info("批量抢单启动,账号数={},并发线程数={}(每账号一线程),同租户同货品仅一个账号抢购",
|
||||
normalizedIds.size(), poolSize);
|
||||
|
||||
List<Map<String, Object>> accountResults;
|
||||
int successAccounts;
|
||||
int failAccounts;
|
||||
try {
|
||||
CountDownLatch startLatch = new CountDownLatch(1);
|
||||
CountDownLatch readyLatch = new CountDownLatch(accountCount);
|
||||
CountDownLatch doneLatch = new CountDownLatch(accountCount);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object>[] resultArray = new Map[accountCount];
|
||||
|
||||
for (int i = 0; i < accountCount; i++) {
|
||||
final int index = i;
|
||||
String id = normalizedIds.get(i);
|
||||
List<CompletableFuture<Map<String, Object>>> futures = new ArrayList<>(normalizedIds.size());
|
||||
for (String id : normalizedIds) {
|
||||
LbBuyAccount account = accountMap.get(id);
|
||||
batchExecutor.submit(() -> {
|
||||
readyLatch.countDown();
|
||||
try {
|
||||
startLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
resultArray[index] = buildRushBuyTaskErrorItem(id, account, "抢单被中断");
|
||||
persistRushBuyOutcomeFromItem(account, resultArray[index]);
|
||||
doneLatch.countDown();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resultArray[index] = processSingleAccountRushBuy(id, account);
|
||||
} catch (Exception e) {
|
||||
log.error("抢单账号任务执行异常,accountId={},loginAccount={}",
|
||||
id, account != null ? account.getLoginAccount() : null, e);
|
||||
resultArray[index] = buildRushBuyTaskErrorItem(id, account, e.getMessage());
|
||||
persistRushBuyOutcomeFromItem(account, resultArray[index]);
|
||||
} finally {
|
||||
doneLatch.countDown();
|
||||
}
|
||||
});
|
||||
futures.add(CompletableFuture.supplyAsync(
|
||||
() -> runSingleAccountRushBuyTask(id, account, goodsCoordinator),
|
||||
executor));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
|
||||
|
||||
if (!readyLatch.await(30, TimeUnit.SECONDS)) {
|
||||
result.put("success", false);
|
||||
result.put("message", "抢单线程就绪超时");
|
||||
return result;
|
||||
}
|
||||
log.info("全部 {} 个抢单线程已就绪,同时启动", accountCount);
|
||||
startLatch.countDown();
|
||||
|
||||
if (!doneLatch.await(120, TimeUnit.SECONDS)) {
|
||||
result.put("success", false);
|
||||
result.put("message", "批量抢单执行超时");
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> accountResults = new ArrayList<>(accountCount);
|
||||
int successAccounts = 0;
|
||||
int failAccounts = 0;
|
||||
for (Map<String, Object> item : resultArray) {
|
||||
accountResults = new ArrayList<>(normalizedIds.size());
|
||||
successAccounts = 0;
|
||||
failAccounts = 0;
|
||||
for (CompletableFuture<Map<String, Object>> future : futures) {
|
||||
Map<String, Object> item = future.join();
|
||||
accountResults.add(item);
|
||||
if (Boolean.TRUE.equals(item.get("success"))) {
|
||||
successAccounts++;
|
||||
@@ -300,27 +272,29 @@ public class LbBuyAccountServiceImpl
|
||||
failAccounts++;
|
||||
}
|
||||
}
|
||||
|
||||
result.put("success", successAccounts > 0);
|
||||
result.put("message", successAccounts > 0
|
||||
? "批量抢购完成,成功账号 " + successAccounts + " 个,失败 " + failAccounts + " 个"
|
||||
: "批量抢购未成功");
|
||||
result.put("totalAccounts", accountCount);
|
||||
result.put("successAccounts", successAccounts);
|
||||
result.put("failAccounts", failAccounts);
|
||||
result.put("accountResults", accountResults);
|
||||
return result;
|
||||
} finally {
|
||||
batchExecutor.shutdown();
|
||||
executor.shutdown();
|
||||
try {
|
||||
if (!batchExecutor.awaitTermination(120, TimeUnit.SECONDS)) {
|
||||
batchExecutor.shutdownNow();
|
||||
if (!executor.awaitTermination(2, TimeUnit.HOURS)) {
|
||||
log.warn("批量抢单线程池未在 2 小时内结束,执行 shutdownNow");
|
||||
executor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
batchExecutor.shutdownNow();
|
||||
executor.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("批量抢单等待线程结束被中断", e);
|
||||
}
|
||||
}
|
||||
|
||||
result.put("success", successAccounts > 0);
|
||||
result.put("message", successAccounts > 0
|
||||
? "批量抢购完成,成功账号 " + successAccounts + " 个,失败 " + failAccounts + " 个"
|
||||
: "批量抢购未成功");
|
||||
result.put("totalAccounts", normalizedIds.size());
|
||||
result.put("successAccounts", successAccounts);
|
||||
result.put("failAccounts", failAccounts);
|
||||
result.put("accountResults", accountResults);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("抢单账号批量抢购异常", e);
|
||||
result.put("success", false);
|
||||
@@ -329,6 +303,20 @@ public class LbBuyAccountServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> runSingleAccountRushBuyTask(
|
||||
String id, LbBuyAccount account, LbRushBuyGoodsCoordinator goodsCoordinator) {
|
||||
try {
|
||||
return processSingleAccountRushBuy(id, account, goodsCoordinator);
|
||||
} catch (Exception e) {
|
||||
log.error("抢单账号任务执行异常,accountId={},loginAccount={},thread={}",
|
||||
id, account != null ? account.getLoginAccount() : null,
|
||||
Thread.currentThread().getName(), e);
|
||||
Map<String, Object> item = buildRushBuyTaskErrorItem(id, account, e.getMessage());
|
||||
persistRushBuyOutcomeFromItem(account, item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> buildRushBuyTaskErrorItem(String accountId, LbBuyAccount account, String reason) {
|
||||
String accountLabel = formatRushBuyAccountLabel(accountId, account);
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
@@ -344,9 +332,10 @@ public class LbBuyAccountServiceImpl
|
||||
}
|
||||
|
||||
/**
|
||||
* 单账号抢单逻辑,供线程池并行调用。
|
||||
* 单账号抢单逻辑;批量入口为每账号一线程并发调用,共享 {@link LbRushBuyGoodsCoordinator}。
|
||||
*/
|
||||
private Map<String, Object> processSingleAccountRushBuy(String id, LbBuyAccount account) {
|
||||
private Map<String, Object> processSingleAccountRushBuy(
|
||||
String id, LbBuyAccount account, LbRushBuyGoodsCoordinator goodsCoordinator) {
|
||||
String accountLabel = formatRushBuyAccountLabel(id, account);
|
||||
log.info("抢单任务开始,{},thread={}", accountLabel, Thread.currentThread().getName());
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
@@ -378,24 +367,41 @@ public class LbBuyAccountServiceImpl
|
||||
if (maxGrabCount == null || maxGrabCount <= 0) {
|
||||
resultMessage = attachAccountLabel(accountLabel, formatPreRushBuyFailure("maxGrabCount必须大于0"));
|
||||
} else {
|
||||
LbBuyAccountRushBuyContext tokenRefreshContext = null;
|
||||
if (account.getLoginAccount() != null && !account.getLoginAccount().trim().isEmpty()
|
||||
&& account.getLoginPassword() != null && !account.getLoginPassword().trim().isEmpty()) {
|
||||
tokenRefreshContext = new LbBuyAccountRushBuyContext(
|
||||
account.getId(),
|
||||
account.getLoginAccount().trim(),
|
||||
account.getLoginPassword().trim());
|
||||
}
|
||||
Map<String, Object> rushBuyResult = lbGoodsService.rushBuy(
|
||||
account.getTenantId().trim(),
|
||||
account.getTokenFront().trim(),
|
||||
maxGrabCount,
|
||||
accountLabel);
|
||||
accountLabel,
|
||||
tokenRefreshContext,
|
||||
goodsCoordinator,
|
||||
account.getId());
|
||||
item.put("rushBuyResult", rushBuyResult);
|
||||
rushSuccess = rushBuyResult.get("success") instanceof Boolean
|
||||
? (Boolean) rushBuyResult.get("success")
|
||||
: null;
|
||||
resultMessage = attachAccountLabel(
|
||||
accountLabel, formatRushBuyResultMessage(rushBuyResult));
|
||||
persistRushBuyOutcome(
|
||||
account.getId(),
|
||||
accountLabel,
|
||||
formatRushBuyResultForDb(accountLabel, rushBuyResult));
|
||||
}
|
||||
}
|
||||
|
||||
item.put("success", Boolean.TRUE.equals(rushSuccess));
|
||||
item.put("message", resultMessage);
|
||||
log.info("抢单任务结束,{},success={},message={}", accountLabel, item.get("success"), resultMessage);
|
||||
persistRushBuyOutcome(account.getId(), accountLabel, resultMessage);
|
||||
if (account != null && account.getId() != null && item.get("rushBuyResult") == null) {
|
||||
persistRushBuyOutcome(account.getId(), accountLabel, resultMessage);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -574,7 +580,7 @@ public class LbBuyAccountServiceImpl
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (summaryMsg != null) {
|
||||
sb.append("汇总:").append(summaryMsg);
|
||||
sb.append(summaryMsg);
|
||||
}
|
||||
Object successCount = rushBuyResult.get("successCount");
|
||||
Object failCount = rushBuyResult.get("failCount");
|
||||
@@ -597,6 +603,39 @@ public class LbBuyAccountServiceImpl
|
||||
return truncateRushBuyResult(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 落库文案:将 {@code rushBuyResult.message} 置于最前,便于在库中快速识别本次抢购结论。
|
||||
*/
|
||||
private static String formatRushBuyResultForDb(String accountLabel, Map<String, Object> rushBuyResult) {
|
||||
if (rushBuyResult == null || rushBuyResult.isEmpty()) {
|
||||
return truncateRushBuyResult(formatPreRushBuyFailure("抢购未返回结果"));
|
||||
}
|
||||
|
||||
Object summaryMsg = rushBuyResult.get("message");
|
||||
String summary = summaryMsg != null ? summaryMsg.toString().trim() : "";
|
||||
String formattedBody = formatRushBuyResultMessage(rushBuyResult);
|
||||
|
||||
Object detailsObj = rushBuyResult.get("details");
|
||||
if (!(detailsObj instanceof List<?> details) || details.isEmpty()) {
|
||||
if (summary.isEmpty()) {
|
||||
return truncateRushBuyResult(attachAccountLabel(accountLabel, formattedBody));
|
||||
}
|
||||
return truncateRushBuyResult(summary + " | " + accountLabel);
|
||||
}
|
||||
|
||||
if (summary.isEmpty()) {
|
||||
return truncateRushBuyResult(attachAccountLabel(accountLabel, formattedBody));
|
||||
}
|
||||
|
||||
int sep = formattedBody.indexOf(" | ");
|
||||
if (sep < 0) {
|
||||
return truncateRushBuyResult(summary + " | " + accountLabel);
|
||||
}
|
||||
String head = formattedBody.substring(0, sep);
|
||||
String tail = formattedBody.substring(sep + 3);
|
||||
return truncateRushBuyResult(head + " | " + accountLabel + " | " + tail);
|
||||
}
|
||||
|
||||
/** rushBuy 未产生逐笔明细(配置缺失、无可抢货品、异常等) */
|
||||
private static String formatRushBuyWithoutDetails(Map<String, Object> rushBuyResult, Object summaryMsg) {
|
||||
Boolean overallSuccess = rushBuyResult.get("success") instanceof Boolean
|
||||
|
||||
@@ -305,7 +305,7 @@ public class LbFanManagementServiceImpl
|
||||
item.put("parsed", loginResult.parsed());
|
||||
if (loginResult.success()) {
|
||||
successCount++;
|
||||
Map<String, Object> userinfo = extractUserinfo(loginResult.parsed());
|
||||
Map<String, Object> userinfo = HxrAdminUserLoginService.extractUserinfo(loginResult.parsed());
|
||||
if (userinfo != null) {
|
||||
Map<String, Object> saveResult =
|
||||
saveFanFromLoginUserinfo(userinfo, tenantId);
|
||||
@@ -438,22 +438,6 @@ public class LbFanManagementServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> extractUserinfo(Object parsed) {
|
||||
if (!(parsed instanceof Map<?, ?> root)) {
|
||||
return null;
|
||||
}
|
||||
Object dataObj = root.get("data");
|
||||
if (!(dataObj instanceof Map<?, ?> data)) {
|
||||
return null;
|
||||
}
|
||||
Object userinfoObj = data.get("userinfo");
|
||||
if (!(userinfoObj instanceof Map<?, ?>)) {
|
||||
return null;
|
||||
}
|
||||
return (Map<String, Object>) userinfoObj;
|
||||
}
|
||||
|
||||
private Map<String, Object> saveFanFromLoginUserinfo(Map<String, Object> userinfo, String tenantId) {
|
||||
Map<String, Object> saveResult = new LinkedHashMap<>();
|
||||
Long fanId = parseLong(userinfo.get("id"));
|
||||
|
||||
@@ -6,12 +6,15 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.rj.dto.hxr.HxrGoodsApiContext;
|
||||
import com.rj.dto.hxr.HxrLbGoodsPageData;
|
||||
import com.rj.dto.hxr.HxrLbGoodsSelectResponse;
|
||||
import com.rj.dto.LbBuyAccountRushBuyContext;
|
||||
import com.rj.dto.LbRushBuyGoodsCoordinator;
|
||||
import com.rj.entity.LbGoods;
|
||||
import com.rj.mapper.LbGoodsMapper;
|
||||
import com.rj.service.HxrAdminBuyService;
|
||||
import com.rj.service.HxrAdminGoodsService;
|
||||
import com.rj.service.ILbGoodsService;
|
||||
import com.rj.service.ILbThirdIntegrationConfigService;
|
||||
import com.rj.service.LbBuyAccountTokenService;
|
||||
import com.rj.tenant.TenantContextHolder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -35,6 +38,7 @@ import java.util.Optional;
|
||||
public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> implements ILbGoodsService {
|
||||
|
||||
private static final BigDecimal RUSH_BUY_MIN_TOTAL_MONEY = new BigDecimal("25000");
|
||||
private static final BigDecimal RUSH_BUY_MAX_TOTAL_MONEY = new BigDecimal("38000");
|
||||
|
||||
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@@ -44,6 +48,8 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
|
||||
private final ILbThirdIntegrationConfigService lbThirdIntegrationConfigService;
|
||||
|
||||
private final LbBuyAccountTokenService lbBuyAccountTokenService;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> add(LbGoods entity) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
@@ -513,7 +519,14 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount, String rushBuyAccountLabel) {
|
||||
public Map<String, Object> rushBuy(
|
||||
String tenantId,
|
||||
String token,
|
||||
Integer maxBuyCount,
|
||||
String rushBuyAccountLabel,
|
||||
LbBuyAccountRushBuyContext tokenRefreshContext,
|
||||
LbRushBuyGoodsCoordinator goodsCoordinator,
|
||||
String accountId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
String accountTag = formatRushBuyAccountLogTag(rushBuyAccountLabel);
|
||||
try {
|
||||
@@ -550,28 +563,41 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
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);
|
||||
if (goodsList.isEmpty()) {
|
||||
result.put("success", false);
|
||||
result.put("message", "lb_goods 中无可抢购货品(金额需大于29000,且更新时间需在24小时内)");
|
||||
result.put("message", "lb_goods 中无可抢购货品(金额需大于25000且不超过38000,且更新时间需在24小时内)");
|
||||
result.put("successCount", 0);
|
||||
result.put("failCount", 0);
|
||||
result.put("details", List.of());
|
||||
return result;
|
||||
}
|
||||
|
||||
int maxAttempts = maxBuyCount * 100;
|
||||
int maxAttempts = maxBuyCount * 150;
|
||||
List<Map<String, Object>> details = new ArrayList<>();
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
int skippedCount = 0;
|
||||
int attemptCount = 0;
|
||||
boolean stoppedByMax = false;
|
||||
boolean stoppedByMaxAttempts = false;
|
||||
boolean stoppedByDailyLimit = false;
|
||||
boolean stoppedByLoginRequired = false;
|
||||
boolean stoppedByActivityNotStarted = false;
|
||||
boolean tokenRefreshAttempted = false;
|
||||
boolean tokenRefreshed = false;
|
||||
boolean restartAfterTokenRefresh;
|
||||
|
||||
do {
|
||||
restartAfterTokenRefresh = false;
|
||||
for (LbGoods goods : goodsList) {
|
||||
if (goodsCoordinator != null && goodsCoordinator.isActivityNotStarted()) {
|
||||
stoppedByActivityNotStarted = true;
|
||||
break;
|
||||
}
|
||||
if (successCount >= maxBuyCount) {
|
||||
stoppedByMax = true;
|
||||
break;
|
||||
@@ -584,6 +610,29 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
continue;
|
||||
}
|
||||
|
||||
if (goodsCoordinator != null && accountId != null
|
||||
&& !goodsCoordinator.tryAcquire(tid, goods.getId(), accountId)) {
|
||||
skippedCount++;
|
||||
Map<String, Object> skippedItem = new LinkedHashMap<>();
|
||||
skippedItem.put("id", goods.getId());
|
||||
skippedItem.put("sellerId", goods.getSellerId());
|
||||
skippedItem.put("title", goods.getTitle());
|
||||
skippedItem.put("totalMoney", goods.getTotalMoney());
|
||||
skippedItem.put("skipped", true);
|
||||
skippedItem.put("success", false);
|
||||
String skipReason = goodsCoordinator.isUnavailable(tid, goods.getId())
|
||||
? "该货品已被抢订,不可再抢,我自动跳过"
|
||||
: "该货品已被其他账号占用,我自动跳过";
|
||||
skippedItem.put("message", skipReason);
|
||||
details.add(skippedItem);
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("跳过货品 {},{}", goods.getId(), skipReason);
|
||||
} else {
|
||||
log.info("{} 跳过货品 {},{}", accountTag, goods.getId(), skipReason);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
attemptCount++;
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
item.put("id", goods.getId());
|
||||
@@ -604,8 +653,22 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
|
||||
if (buyResult.success()) {
|
||||
successCount++;
|
||||
Thread.sleep(500);
|
||||
} else {
|
||||
failCount++;
|
||||
if (buyResult.orderAlreadyGrabbed()) {
|
||||
if (goodsCoordinator != null) {
|
||||
goodsCoordinator.markUnavailable(tid, goods.getId());
|
||||
}
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("货品 {} 返回「此订单已被抢订」,本批次同租户不再尝试", goods.getId());
|
||||
} else {
|
||||
log.info("{} 货品 {} 返回「此订单已被抢订」,本批次同租户不再尝试",
|
||||
accountTag, goods.getId());
|
||||
}
|
||||
} else if (goodsCoordinator != null && accountId != null) {
|
||||
goodsCoordinator.release(tid, goods.getId(), accountId);
|
||||
}
|
||||
if (buyResult.dailyLimitExceeded()) {
|
||||
stoppedByDailyLimit = true;
|
||||
if (accountTag.isEmpty()) {
|
||||
@@ -615,14 +678,80 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (buyResult.activityNotStarted()) {
|
||||
stoppedByActivityNotStarted = true;
|
||||
if (goodsCoordinator != null) {
|
||||
goodsCoordinator.markActivityNotStarted();
|
||||
}
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("活动未开始,停止继续抢购");
|
||||
} else {
|
||||
log.info("{} 活动未开始,停止继续抢购", accountTag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (buyResult.loginRequired()) {
|
||||
if (tokenRefreshContext != null && !tokenRefreshAttempted) {
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("token 失效或未登录,尝试自动登录刷新 token_front");
|
||||
} else {
|
||||
log.info("{} token 失效或未登录,尝试自动登录刷新 token_front", accountTag);
|
||||
}
|
||||
var newTokenOpt = lbBuyAccountTokenService.loginAndPersistTokenFront(
|
||||
tokenRefreshContext.accountId(),
|
||||
tid,
|
||||
tokenRefreshContext.loginAccount(),
|
||||
tokenRefreshContext.loginPassword());
|
||||
if (newTokenOpt.isPresent()) {
|
||||
tokenRefreshAttempted = true;
|
||||
tokenRefreshed = true;
|
||||
apiContext = withToken(apiContext, newTokenOpt.get());
|
||||
if (!details.isEmpty()) {
|
||||
details.remove(details.size() - 1);
|
||||
}
|
||||
details.removeIf(d -> !Boolean.TRUE.equals(d.get("success")));
|
||||
skippedCount = 0;
|
||||
failCount = 0;
|
||||
restartAfterTokenRefresh = true;
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("token 已刷新,重新抢购所有货品");
|
||||
} else {
|
||||
log.info("{} token 已刷新,重新抢购所有货品", accountTag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buyResult.loginRequired()) {
|
||||
stoppedByLoginRequired = true;
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("token 失效或未登录,停止继续抢购");
|
||||
} else {
|
||||
log.info("{} token 失效或未登录,停止继续抢购", accountTag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (restartAfterTokenRefresh);
|
||||
|
||||
result.put("success", successCount > 0);
|
||||
if (stoppedByDailyLimit) {
|
||||
if (stoppedByLoginRequired) {
|
||||
String loginMsg = tokenRefreshed
|
||||
? "token 已自动刷新但仍未登录,已停止抢购"
|
||||
: HxrAdminBuyService.MSG_LOGIN_REQUIRED;
|
||||
result.put("message", successCount > 0
|
||||
? loginMsg + ";成功 " + successCount + " 笔,失败 " + failCount + " 笔"
|
||||
: loginMsg);
|
||||
} else if (stoppedByDailyLimit) {
|
||||
result.put("message", successCount > 0
|
||||
? "当日可抢订单数已满,已停止抢购;成功 " + successCount + " 笔,失败 " + failCount + " 笔"
|
||||
: HxrAdminBuyService.MSG_DAILY_LIMIT_EXCEEDED);
|
||||
} else if (stoppedByActivityNotStarted) {
|
||||
result.put("message", successCount > 0
|
||||
? HxrAdminBuyService.MSG_ACTIVITY_NOT_STARTED
|
||||
+ ",已停止抢购;成功 " + successCount + " 笔,失败 " + failCount + " 笔"
|
||||
: HxrAdminBuyService.MSG_ACTIVITY_NOT_STARTED);
|
||||
} else {
|
||||
result.put("message", successCount > 0
|
||||
? "抢购完成,成功 " + successCount + " 笔,失败 " + failCount + " 笔"
|
||||
@@ -630,16 +759,20 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
}
|
||||
result.put("successCount", successCount);
|
||||
result.put("failCount", failCount);
|
||||
result.put("skippedCount", skippedCount);
|
||||
result.put("attemptCount", attemptCount);
|
||||
result.put("maxAttempts", maxAttempts);
|
||||
result.put("stoppedByMax", stoppedByMax);
|
||||
result.put("stoppedByMaxAttempts", stoppedByMaxAttempts);
|
||||
result.put("stoppedByDailyLimit", stoppedByDailyLimit);
|
||||
result.put("stoppedByLoginRequired", stoppedByLoginRequired);
|
||||
result.put("stoppedByActivityNotStarted", stoppedByActivityNotStarted);
|
||||
result.put("tokenRefreshed", tokenRefreshed);
|
||||
result.put("details", details);
|
||||
if (accountTag.isEmpty()) {
|
||||
log.info("抢购结束,success={},message={}", successCount > 0, result.get("message"));
|
||||
log.info("message={},抢购结束,success={}", result.get("message"),successCount > 0);
|
||||
} else {
|
||||
log.info("{} 抢购结束,success={},message={}", accountTag, successCount > 0, result.get("message"));
|
||||
log.info("message={}, {} 抢购结束,success={}", result.get("message"), accountTag, successCount > 0);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
@@ -668,6 +801,17 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
||||
return rushBuyAccountLabel.trim();
|
||||
}
|
||||
|
||||
private static HxrGoodsApiContext withToken(HxrGoodsApiContext ctx, String token) {
|
||||
return new HxrGoodsApiContext(
|
||||
ctx.goodsApiBaseUrl(),
|
||||
ctx.buyApiUrl(),
|
||||
ctx.pageLimit(),
|
||||
ctx.origin(),
|
||||
ctx.referer(),
|
||||
token,
|
||||
ctx.appStr());
|
||||
}
|
||||
|
||||
private static void applyDefaults(LbGoods entity) {
|
||||
if (entity.getPrice() == null) {
|
||||
entity.setPrice(BigDecimal.ZERO);
|
||||
|
||||
Reference in New Issue
Block a user