修改抢单逻辑, 指定货物包的开始日期

This commit is contained in:
2026-07-11 08:25:19 +08:00
parent 62440c68db
commit 42fa82ce07
6 changed files with 55 additions and 28 deletions

View File

@@ -90,11 +90,11 @@ public class LbBuyAccountController {
"根据 ids 查询 lb_buy_account按 tenant_id 关联 lb_third_integration_config 获取 URL 与 appStr" "根据 ids 查询 lb_buy_account按 tenant_id 关联 lb_third_integration_config 获取 URL 与 appStr"
+ "各账号并发抢购每账号一线程accountResults 顺序与 ids 一致;同租户同货品 ID 仅允许一个账号尝试;" + "各账号并发抢购每账号一线程accountResults 顺序与 ids 一致;同租户同货品 ID 仅允许一个账号尝试;"
+ "每个账号使用 token_front 作为 hxrd 请求 Tokenmax_grab_count 作为最大成功抢购笔数," + "每个账号使用 token_front 作为 hxrd 请求 Tokenmax_grab_count 作为最大成功抢购笔数,"
+ "抢购逻辑同 LbGoodsController#rushBuy") + "抢购逻辑同 LbGoodsController#rushBuygoodsBeginTime 指定货物包开始时间,查询 lb_goods 时 updated_at 需大于此时间")
public ResponseEntity<Map<String, Object>> rushBuy( public ResponseEntity<Map<String, Object>> rushBuy(
@Parameter(description = "抢单账号 ID 列表", required = true) @Parameter(description = "抢单账号 ID 列表", required = true)
@RequestBody LbBuyAccountRushBuyRequest request) { @RequestBody LbBuyAccountRushBuyRequest request) {
Map<String, Object> result = lbBuyAccountService.rushBuyByIds(request.getIds()); Map<String, Object> result = lbBuyAccountService.rushBuyByIds(request.getIds(), request.getGoodsBeginTime());
Boolean success = (Boolean) result.get("success"); Boolean success = (Boolean) result.get("success");
if (success != null && success) { if (success != null && success) {
return ResponseEntity.ok(result); return ResponseEntity.ok(result);

View File

@@ -15,4 +15,7 @@ public class LbBuyAccountRushBuyRequest {
@Schema(description = "抢单账号主键 ID 列表lb_buy_account.id", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "抢单账号主键 ID 列表lb_buy_account.id", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> ids; private List<String> ids;
@Schema(description = "货物包开始时间,格式 yyyy-MM-dd HH:mm:ss查询 lb_goods 时 updated_at 需大于此时间")
private String goodsBeginTime;
} }

View File

@@ -28,6 +28,8 @@ public interface ILbBuyAccountService extends IService<LbBuyAccount> {
/** /**
* 按账号 ID 列表批量抢购:每个账号使用其 token_front 与 max_grab_count * 按账号 ID 列表批量抢购:每个账号使用其 token_front 与 max_grab_count
* tenant_id 关联 lb_third_integration_config 解析 URL 与 appStr。 * tenant_id 关联 lb_third_integration_config 解析 URL 与 appStr。
*
* @param goodsBeginTime 货物包开始时间,格式 yyyy-MM-dd HH:mm:ss查询 lb_goods 时 updated_at 需大于此时间
*/ */
Map<String, Object> rushBuyByIds(List<String> ids); Map<String, Object> rushBuyByIds(List<String> ids, String goodsBeginTime);
} }

View File

@@ -54,11 +54,11 @@ public interface ILbGoodsService extends IService<LbGoods> {
* @param token 失效时,按账号凭证自动登录并刷新 {@code lb_buy_account.token_front} * @param token 失效时,按账号凭证自动登录并刷新 {@code lb_buy_account.token_front}
*/ */
default Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount) { default Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount) {
return rushBuy(tenantId, token, maxBuyCount, null, null, null, null, null); return rushBuy(tenantId, token, maxBuyCount, null, null, null, null, null, null);
} }
default Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount, String rushBuyAccountLabel) { default Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount, String rushBuyAccountLabel) {
return rushBuy(tenantId, token, maxBuyCount, rushBuyAccountLabel, null, null, null, null); return rushBuy(tenantId, token, maxBuyCount, rushBuyAccountLabel, null, null, null, null, null);
} }
default Map<String, Object> rushBuy( default Map<String, Object> rushBuy(
@@ -67,7 +67,7 @@ public interface ILbGoodsService extends IService<LbGoods> {
Integer maxBuyCount, Integer maxBuyCount,
String rushBuyAccountLabel, String rushBuyAccountLabel,
LbBuyAccountRushBuyContext tokenRefreshContext) { LbBuyAccountRushBuyContext tokenRefreshContext) {
return rushBuy(tenantId, token, maxBuyCount, rushBuyAccountLabel, tokenRefreshContext, null, null, null); return rushBuy(tenantId, token, maxBuyCount, rushBuyAccountLabel, tokenRefreshContext, null, null, null, null);
} }
Map<String, Object> rushBuy( Map<String, Object> rushBuy(
@@ -78,5 +78,6 @@ public interface ILbGoodsService extends IService<LbGoods> {
LbBuyAccountRushBuyContext tokenRefreshContext, LbBuyAccountRushBuyContext tokenRefreshContext,
LbRushBuyGoodsCoordinator goodsCoordinator, LbRushBuyGoodsCoordinator goodsCoordinator,
String accountId, String accountId,
Integer maxGrabAmount); Integer maxGrabAmount,
String goodsBeginTime);
} }

View File

@@ -212,7 +212,7 @@ public class LbBuyAccountServiceImpl
} }
@Override @Override
public Map<String, Object> rushBuyByIds(List<String> ids) { public Map<String, Object> rushBuyByIds(List<String> ids, String goodsBeginTime) {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
try { try {
if (ids == null || ids.isEmpty()) { if (ids == null || ids.isEmpty()) {
@@ -249,8 +249,8 @@ public class LbBuyAccountServiceImpl
t.setDaemon(false); t.setDaemon(false);
return t; return t;
}); });
log.info("批量抢单启动,账号数={},并发线程数={}(每账号一线程),同租户同货品仅一个账号抢购", log.info("批量抢单启动,账号数={},并发线程数={}(每账号一线程),同租户同货品仅一个账号抢购goodsBeginTime={}",
normalizedIds.size(), poolSize); normalizedIds.size(), poolSize, goodsBeginTime);
List<Map<String, Object>> accountResults; List<Map<String, Object>> accountResults;
int successAccounts; int successAccounts;
@@ -260,7 +260,7 @@ public class LbBuyAccountServiceImpl
for (String id : normalizedIds) { for (String id : normalizedIds) {
LbBuyAccount account = accountMap.get(id); LbBuyAccount account = accountMap.get(id);
futures.add(CompletableFuture.supplyAsync( futures.add(CompletableFuture.supplyAsync(
() -> runSingleAccountRushBuyTask(id, account, goodsCoordinator), () -> runSingleAccountRushBuyTask(id, account, goodsCoordinator, goodsBeginTime),
executor)); executor));
} }
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
@@ -309,9 +309,9 @@ public class LbBuyAccountServiceImpl
} }
private Map<String, Object> runSingleAccountRushBuyTask( private Map<String, Object> runSingleAccountRushBuyTask(
String id, LbBuyAccount account, LbRushBuyGoodsCoordinator goodsCoordinator) { String id, LbBuyAccount account, LbRushBuyGoodsCoordinator goodsCoordinator, String goodsBeginTime) {
try { try {
return processSingleAccountRushBuy(id, account, goodsCoordinator); return processSingleAccountRushBuy(id, account, goodsCoordinator, goodsBeginTime);
} catch (Exception e) { } catch (Exception e) {
log.error("抢单账号任务执行异常accountId={}loginAccount={}thread={}", log.error("抢单账号任务执行异常accountId={}loginAccount={}thread={}",
id, account != null ? account.getLoginAccount() : null, id, account != null ? account.getLoginAccount() : null,
@@ -340,7 +340,7 @@ public class LbBuyAccountServiceImpl
* 单账号抢单逻辑;批量入口为每账号一线程并发调用,共享 {@link LbRushBuyGoodsCoordinator}。 * 单账号抢单逻辑;批量入口为每账号一线程并发调用,共享 {@link LbRushBuyGoodsCoordinator}。
*/ */
private Map<String, Object> processSingleAccountRushBuy( private Map<String, Object> processSingleAccountRushBuy(
String id, LbBuyAccount account, LbRushBuyGoodsCoordinator goodsCoordinator) { String id, LbBuyAccount account, LbRushBuyGoodsCoordinator goodsCoordinator, String goodsBeginTime) {
String accountLabel = formatRushBuyAccountLabel(id, account); String accountLabel = formatRushBuyAccountLabel(id, account);
log.info("抢单任务开始,{}thread={}", accountLabel, Thread.currentThread().getName()); log.info("抢单任务开始,{}thread={}", accountLabel, Thread.currentThread().getName());
Map<String, Object> item = new LinkedHashMap<>(); Map<String, Object> item = new LinkedHashMap<>();
@@ -389,7 +389,8 @@ public class LbBuyAccountServiceImpl
tokenRefreshContext, tokenRefreshContext,
goodsCoordinator, goodsCoordinator,
account.getId(), account.getId(),
maxGrabAmount); maxGrabAmount,
goodsBeginTime);
item.put("rushBuyResult", rushBuyResult); item.put("rushBuyResult", rushBuyResult);
rushSuccess = rushBuyResult.get("success") instanceof Boolean rushSuccess = rushBuyResult.get("success") instanceof Boolean
? (Boolean) rushBuyResult.get("success") ? (Boolean) rushBuyResult.get("success")

View File

@@ -529,7 +529,8 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
LbBuyAccountRushBuyContext tokenRefreshContext, LbBuyAccountRushBuyContext tokenRefreshContext,
LbRushBuyGoodsCoordinator goodsCoordinator, LbRushBuyGoodsCoordinator goodsCoordinator,
String accountId, String accountId,
Integer maxGrabAmount) { Integer maxGrabAmount,
String goodsBeginTime) {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
String accountTag = formatRushBuyAccountLogTag(rushBuyAccountLabel); String accountTag = formatRushBuyAccountLogTag(rushBuyAccountLabel);
try { try {
@@ -562,10 +563,13 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
TenantContextHolder.setTenantId(tid); TenantContextHolder.setTenantId(tid);
try { try {
log.info("查询-待抢购-货物列表......"); log.info("查询-待抢购-货物列表......");
List<LbGoods> goodsList = resolveRushBuyGoodsList(tid, accountTag, goodsCoordinator);// List<LbGoods> goodsList = resolveRushBuyGoodsList(tid, accountTag, goodsCoordinator, goodsBeginTime);
if (goodsList.isEmpty()) { if (goodsList.isEmpty()) {
result.put("success", false); result.put("success", false);
result.put("message", "lb_goods 中无可抢购货品金额需大于25000且不超过38000且更新时间需在24小时内"); String timeCondition = goodsBeginTime != null && !goodsBeginTime.trim().isEmpty()
? "且更新时间需大于 goodsBeginTime(" + goodsBeginTime + ")"
: "且更新时间需在24小时内";
result.put("message", "lb_goods 中无可抢购货品金额需大于25000且不超过38000" + timeCondition + "");
result.put("successCount", 0); result.put("successCount", 0);
result.put("failCount", 0); result.put("failCount", 0);
result.put("details", List.of()); result.put("details", List.of());
@@ -849,9 +853,9 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
} }
private List<LbGoods> resolveRushBuyGoodsList( private List<LbGoods> resolveRushBuyGoodsList(
String tid, String accountTag, LbRushBuyGoodsCoordinator goodsCoordinator) { String tid, String accountTag, LbRushBuyGoodsCoordinator goodsCoordinator, String goodsBeginTime) {
if (goodsCoordinator == null) { if (goodsCoordinator == null) {
return loadRushBuyGoodsList(tid, accountTag); return loadRushBuyGoodsList(tid, accountTag, goodsBeginTime);
} }
List<LbGoods> cached = goodsCoordinator.peekRushBuyGoodsList(tid); List<LbGoods> cached = goodsCoordinator.peekRushBuyGoodsList(tid);
if (cached != null) { if (cached != null) {
@@ -863,27 +867,43 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
} }
return cached; return cached;
} }
return goodsCoordinator.resolveRushBuyGoodsList(tid, () -> loadRushBuyGoodsList(tid, accountTag)); return goodsCoordinator.resolveRushBuyGoodsList(tid, () -> loadRushBuyGoodsList(tid, accountTag, goodsBeginTime));
} }
private List<LbGoods> loadRushBuyGoodsList(String tid, String accountTag) { private List<LbGoods> loadRushBuyGoodsList(String tid, String accountTag, String goodsBeginTime) {
LocalDateTime updatedAfter = resolveRushBuyUpdatedAfter(); LocalDateTime updatedAfter;
boolean useGoodsBeginTime = goodsBeginTime != null && !goodsBeginTime.trim().isEmpty();
if (useGoodsBeginTime) {
try {
updatedAfter = LocalDateTime.parse(goodsBeginTime.trim(), DATETIME_FMT);
} catch (DateTimeParseException e) {
log.warn("goodsBeginTime 格式错误忽略该参数使用默认时间范围goodsBeginTime={}", goodsBeginTime);
updatedAfter = resolveRushBuyUpdatedAfter();
useGoodsBeginTime = false;
}
} else {
updatedAfter = resolveRushBuyUpdatedAfter();
}
LambdaQueryWrapper<LbGoods> w = new LambdaQueryWrapper<>(); LambdaQueryWrapper<LbGoods> w = new LambdaQueryWrapper<>();
w.eq(LbGoods::getTenantId, tid); w.eq(LbGoods::getTenantId, tid);
w.isNotNull(LbGoods::getSellerId); w.isNotNull(LbGoods::getSellerId);
w.gt(LbGoods::getTotalMoney, RUSH_BUY_MIN_TOTAL_MONEY); w.gt(LbGoods::getTotalMoney, RUSH_BUY_MIN_TOTAL_MONEY);
w.le(LbGoods::getTotalMoney, RUSH_BUY_MAX_TOTAL_MONEY); w.le(LbGoods::getTotalMoney, RUSH_BUY_MAX_TOTAL_MONEY);
if (useGoodsBeginTime) {
w.gt(LbGoods::getUpdatedAt, updatedAfter);
} else {
w.ge(LbGoods::getUpdatedAt, updatedAfter); w.ge(LbGoods::getUpdatedAt, updatedAfter);
}
w.orderByDesc(LbGoods::getTotalMoney); w.orderByDesc(LbGoods::getTotalMoney);
long goodsQueryStartedAt = System.nanoTime(); long goodsQueryStartedAt = System.nanoTime();
List<LbGoods> goodsList = this.list(w); List<LbGoods> goodsList = this.list(w);
long goodsQueryElapsedMs = (System.nanoTime() - goodsQueryStartedAt) / 1_000_000L; long goodsQueryElapsedMs = (System.nanoTime() - goodsQueryStartedAt) / 1_000_000L;
if (accountTag.isEmpty()) { if (accountTag.isEmpty()) {
log.info("rushBuy 查询可抢货品耗时 {} mstenantId={}updatedAfter={},结果数量={}", log.info("rushBuy 查询可抢货品耗时 {} mstenantId={}updatedAfter={}goodsBeginTime={}结果数量={}",
goodsQueryElapsedMs, tid, updatedAfter, goodsList.size()); goodsQueryElapsedMs, tid, updatedAfter, goodsBeginTime, goodsList.size());
} else { } else {
log.info("{} rushBuy 查询可抢货品耗时 {} mstenantId={}updatedAfter={},结果数量={}", log.info("{} rushBuy 查询可抢货品耗时 {} mstenantId={}updatedAfter={}goodsBeginTime={}结果数量={}",
accountTag, goodsQueryElapsedMs, tid, updatedAfter, goodsList.size()); accountTag, goodsQueryElapsedMs, tid, updatedAfter, goodsBeginTime, goodsList.size());
} }
return goodsList; return goodsList;
} }