抢购配置读取从表
This commit is contained in:
@@ -141,12 +141,14 @@ public class LbGoodsController {
|
|||||||
description =
|
description =
|
||||||
"从 lb_goods 按 total_money 从大到小选取金额小于39000的货品,调用 hxrd POST /api/order/buy(body: id、seller_id);"
|
"从 lb_goods 按 total_money 从大到小选取金额小于39000的货品,调用 hxrd POST /api/order/buy(body: id、seller_id);"
|
||||||
+ "成功笔数达到 maxBuyCount 后停止,且循环抢购总次数不超过 maxBuyCount 的5倍;"
|
+ "成功笔数达到 maxBuyCount 后停止,且循环抢购总次数不超过 maxBuyCount 的5倍;"
|
||||||
+ "token 可选,未传时使用 hxr.admin.goods-api-token")
|
+ "token、URL、appStr 等从 lb_third_integration_config 按 tenantId 读取;"
|
||||||
|
+ "token 可选,传入时覆盖库中 goodsApiToken")
|
||||||
public ResponseEntity<Map<String, Object>> rushBuy(
|
public ResponseEntity<Map<String, Object>> rushBuy(
|
||||||
@Parameter(description = "maxBuyCount 必填,token 可选", required = true)
|
@Parameter(description = "tenantId、maxBuyCount 必填,token 可选", required = true)
|
||||||
@RequestBody LbGoodsRushBuyRequest request) {
|
@RequestBody LbGoodsRushBuyRequest request) {
|
||||||
Map<String, Object> result =
|
Map<String, Object> result =
|
||||||
lbGoodsService.rushBuy(request.getToken(), request.getMaxBuyCount());
|
lbGoodsService.rushBuy(
|
||||||
|
request.getTenantId(), request.getToken(), request.getMaxBuyCount());
|
||||||
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);
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import lombok.Data;
|
|||||||
@Schema(description = "LB 货品抢购请求")
|
@Schema(description = "LB 货品抢购请求")
|
||||||
public class LbGoodsRushBuyRequest {
|
public class LbGoodsRushBuyRequest {
|
||||||
|
|
||||||
@Schema(description = "hxrd 接口请求头 token;未传时使用 hxr.admin.goods-api-token")
|
@Schema(description = "租户 id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private String tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "hxrd 接口请求头 token;未传时使用 lb_third_integration_config 中的 goodsApiToken")
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
@Schema(description = "最多成功抢购笔数(达到后停止继续请求)", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "最多成功抢购笔数(达到后停止继续请求)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package com.rj.dto.hxr;
|
|||||||
*/
|
*/
|
||||||
public record HxrGoodsApiContext(
|
public record HxrGoodsApiContext(
|
||||||
String goodsApiBaseUrl,
|
String goodsApiBaseUrl,
|
||||||
|
String buyApiUrl,
|
||||||
int pageLimit,
|
int pageLimit,
|
||||||
String origin,
|
String origin,
|
||||||
String referer,
|
String referer,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||||
import com.rj.config.HxrAdminProperties;
|
import com.rj.config.HxrAdminProperties;
|
||||||
|
import com.rj.dto.hxr.HxrGoodsApiContext;
|
||||||
import com.rj.util.HxrGoodsSignUtil;
|
import com.rj.util.HxrGoodsSignUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -50,6 +51,36 @@ public class HxrAdminBuyService {
|
|||||||
if (appStr == null || appStr.isBlank()) {
|
if (appStr == null || appStr.isBlank()) {
|
||||||
return BuyApiResult.failure(-1, "未配置 hxr.admin.goods-api-app-str");
|
return BuyApiResult.failure(-1, "未配置 hxr.admin.goods-api-app-str");
|
||||||
}
|
}
|
||||||
|
HxrGoodsApiContext ctx = new HxrGoodsApiContext(
|
||||||
|
stripQuery(properties.getGoodsApiUrl()),
|
||||||
|
properties.getBuyApiUrl(),
|
||||||
|
20,
|
||||||
|
properties.getGoodsApiOrigin(),
|
||||||
|
properties.getGoodsApiReferer(),
|
||||||
|
resolvedToken,
|
||||||
|
appStr.trim());
|
||||||
|
return buy(goodsId, sellerId, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抢购(使用租户 {@code lb_third_integration_config} 解析出的运行时配置)。
|
||||||
|
*/
|
||||||
|
public BuyApiResult buy(long goodsId, long sellerId, HxrGoodsApiContext ctx) throws Exception {
|
||||||
|
if (ctx == null) {
|
||||||
|
return BuyApiResult.failure(-1, "抢购 API 配置为空");
|
||||||
|
}
|
||||||
|
String resolvedToken = ctx.token();
|
||||||
|
if (resolvedToken == null || resolvedToken.isBlank()) {
|
||||||
|
return BuyApiResult.failure(-1, "未提供 token 且 lb_third_integration_config 中未配置 goodsApiToken");
|
||||||
|
}
|
||||||
|
String appStr = ctx.appStr();
|
||||||
|
if (appStr == null || appStr.isBlank()) {
|
||||||
|
return BuyApiResult.failure(-1, "lb_third_integration_config 中未配置 goodsApiAppStr");
|
||||||
|
}
|
||||||
|
String buyApiUrl = ctx.buyApiUrl();
|
||||||
|
if (buyApiUrl == null || buyApiUrl.isBlank()) {
|
||||||
|
return BuyApiResult.failure(-1, "lb_third_integration_config 中未配置 buyApiUrl");
|
||||||
|
}
|
||||||
|
|
||||||
long timestamp = System.currentTimeMillis() / 1000;
|
long timestamp = System.currentTimeMillis() / 1000;
|
||||||
String noncestr = randomNoncestr();
|
String noncestr = randomNoncestr();
|
||||||
@@ -71,13 +102,13 @@ public class HxrAdminBuyService {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(properties.getBuyApiUrl()))
|
.uri(URI.create(buyApiUrl.trim()))
|
||||||
.timeout(Duration.ofSeconds(120))
|
.timeout(Duration.ofSeconds(120))
|
||||||
.header("Accept", "application/json,*/*")
|
.header("Accept", "application/json,*/*")
|
||||||
.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
|
.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
|
||||||
.header("Content-Type", "application/json;charset=UTF-8")
|
.header("Content-Type", "application/json;charset=UTF-8")
|
||||||
.header("Origin", properties.getGoodsApiOrigin())
|
.header("Origin", ctx.origin())
|
||||||
.header("Referer", properties.getGoodsApiReferer())
|
.header("Referer", ctx.referer())
|
||||||
.header("User-Agent", USER_AGENT)
|
.header("User-Agent", USER_AGENT)
|
||||||
.header("token", resolvedToken)
|
.header("token", resolvedToken)
|
||||||
.header("S", sign)
|
.header("S", sign)
|
||||||
@@ -146,4 +177,12 @@ public class HxrAdminBuyService {
|
|||||||
}
|
}
|
||||||
return s.length() <= maxLen ? s : s.substring(0, maxLen) + "...";
|
return s.length() <= maxLen ? s : s.substring(0, maxLen) + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String stripQuery(String url) {
|
||||||
|
if (url == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int q = url.indexOf('?');
|
||||||
|
return q >= 0 ? url.substring(0, q) : url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ public class HxrAdminGoodsService {
|
|||||||
}
|
}
|
||||||
HxrGoodsApiContext ctx = new HxrGoodsApiContext(
|
HxrGoodsApiContext ctx = new HxrGoodsApiContext(
|
||||||
stripQuery(properties.getGoodsApiUrl()),
|
stripQuery(properties.getGoodsApiUrl()),
|
||||||
|
properties.getBuyApiUrl(),
|
||||||
GOODS_PAGE_SIZE,
|
GOODS_PAGE_SIZE,
|
||||||
properties.getGoodsApiOrigin(),
|
properties.getGoodsApiOrigin(),
|
||||||
properties.getGoodsApiReferer(),
|
properties.getGoodsApiReferer(),
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public interface ILbGoodsService extends IService<LbGoods> {
|
|||||||
/**
|
/**
|
||||||
* 按 {@code total_money} 从大到小选取金额小于39000的 {@code lb_goods},调用 hxrd {@code /api/order/buy} 抢购;
|
* 按 {@code total_money} 从大到小选取金额小于39000的 {@code lb_goods},调用 hxrd {@code /api/order/buy} 抢购;
|
||||||
* 成功笔数达到 {@code maxBuyCount} 后停止,且循环抢购总次数不超过 {@code maxBuyCount} 的5倍。
|
* 成功笔数达到 {@code maxBuyCount} 后停止,且循环抢购总次数不超过 {@code maxBuyCount} 的5倍。
|
||||||
|
*
|
||||||
|
* @param token 可选;非空时作为请求头 token,否则使用 {@code lb_third_integration_config} 中的 goodsApiToken
|
||||||
*/
|
*/
|
||||||
Map<String, Object> rushBuy(String token, Integer maxBuyCount);
|
Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -399,16 +399,39 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> rushBuy(String token, Integer maxBuyCount) {
|
public Map<String, Object> rushBuy(String tenantId, String token, Integer maxBuyCount) {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
try {
|
try {
|
||||||
|
if (tenantId == null || tenantId.trim().isEmpty()) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "tenantId不能为空");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
if (maxBuyCount == null || maxBuyCount <= 0) {
|
if (maxBuyCount == null || maxBuyCount <= 0) {
|
||||||
result.put("success", false);
|
result.put("success", false);
|
||||||
result.put("message", "maxBuyCount必须大于0");
|
result.put("message", "maxBuyCount必须大于0");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String tid = tenantId.trim();
|
||||||
|
Optional<HxrGoodsApiContext> apiContextOpt =
|
||||||
|
lbThirdIntegrationConfigService.resolveGoodsApiContext(tid, token);
|
||||||
|
if (apiContextOpt.isEmpty()) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message",
|
||||||
|
"未找到该租户的第三方集成配置,或配置未启用、URL/凭证不完整(请检查 lb_third_integration_config)");
|
||||||
|
result.put("successCount", 0);
|
||||||
|
result.put("failCount", 0);
|
||||||
|
result.put("details", List.of());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
HxrGoodsApiContext apiContext = apiContextOpt.get();
|
||||||
|
|
||||||
|
String previousTenantId = TenantContextHolder.getTenantId();
|
||||||
|
TenantContextHolder.setTenantId(tid);
|
||||||
|
try {
|
||||||
LambdaQueryWrapper<LbGoods> w = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<LbGoods> w = new LambdaQueryWrapper<>();
|
||||||
|
w.eq(LbGoods::getTenantId, tid);
|
||||||
w.isNotNull(LbGoods::getSellerId);
|
w.isNotNull(LbGoods::getSellerId);
|
||||||
w.lt(LbGoods::getTotalMoney, RUSH_BUY_MAX_TOTAL_MONEY);
|
w.lt(LbGoods::getTotalMoney, RUSH_BUY_MAX_TOTAL_MONEY);
|
||||||
w.orderByDesc(LbGoods::getTotalMoney).orderByDesc(LbGoods::getId);
|
w.orderByDesc(LbGoods::getTotalMoney).orderByDesc(LbGoods::getId);
|
||||||
@@ -451,7 +474,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
|||||||
item.put("totalMoney", goods.getTotalMoney());
|
item.put("totalMoney", goods.getTotalMoney());
|
||||||
log.info("正在抢购货品是:{}",item.toString());
|
log.info("正在抢购货品是:{}",item.toString());
|
||||||
HxrAdminBuyService.BuyApiResult buyResult =
|
HxrAdminBuyService.BuyApiResult buyResult =
|
||||||
hxrAdminBuyService.buy(goods.getId(), goods.getSellerId(), token);
|
hxrAdminBuyService.buy(goods.getId(), goods.getSellerId(), apiContext);
|
||||||
item.put("apiCode", buyResult.apiCode());
|
item.put("apiCode", buyResult.apiCode());
|
||||||
item.put("message", buyResult.apiMsg());
|
item.put("message", buyResult.apiMsg());
|
||||||
item.put("success", buyResult.success());
|
item.put("success", buyResult.success());
|
||||||
@@ -476,6 +499,13 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
|
|||||||
result.put("stoppedByMaxAttempts", stoppedByMaxAttempts);
|
result.put("stoppedByMaxAttempts", stoppedByMaxAttempts);
|
||||||
result.put("details", details);
|
result.put("details", details);
|
||||||
return result;
|
return result;
|
||||||
|
} finally {
|
||||||
|
if (previousTenantId != null) {
|
||||||
|
TenantContextHolder.setTenantId(previousTenantId);
|
||||||
|
} else {
|
||||||
|
TenantContextHolder.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("rushBuy failed", e);
|
log.error("rushBuy failed", e);
|
||||||
result.put("success", false);
|
result.put("success", false);
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ public class LbThirdIntegrationConfigServiceImpl
|
|||||||
try {
|
try {
|
||||||
return Optional.of(new HxrGoodsApiContext(
|
return Optional.of(new HxrGoodsApiContext(
|
||||||
LbThirdIntegrationConfigUtil.resolveGoodsApiBaseUrl(config),
|
LbThirdIntegrationConfigUtil.resolveGoodsApiBaseUrl(config),
|
||||||
|
LbThirdIntegrationConfigUtil.resolveBuyApiUrl(config),
|
||||||
LbThirdIntegrationConfigUtil.resolveGoodsPageLimit(config),
|
LbThirdIntegrationConfigUtil.resolveGoodsPageLimit(config),
|
||||||
LbThirdIntegrationConfigUtil.resolveGoodsApiOrigin(config),
|
LbThirdIntegrationConfigUtil.resolveGoodsApiOrigin(config),
|
||||||
LbThirdIntegrationConfigUtil.resolveGoodsApiReferer(config),
|
LbThirdIntegrationConfigUtil.resolveGoodsApiReferer(config),
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ public final class LbThirdIntegrationConfigUtil {
|
|||||||
return base + path;
|
return base + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String resolveBuyApiUrl(LbThirdIntegrationConfig config) {
|
||||||
|
String base = trimTrailingSlash(requireText(config.getAdminBaseUrl(), "adminBaseUrl"));
|
||||||
|
String path = config.getBuyApiPath();
|
||||||
|
if (!StringUtils.hasText(path)) {
|
||||||
|
path = LbThirdIntegrationConstants.DEFAULT_BUY_API_PATH;
|
||||||
|
}
|
||||||
|
path = path.trim();
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
return base + path;
|
||||||
|
}
|
||||||
|
|
||||||
public static String resolveGoodsApiOrigin(LbThirdIntegrationConfig config) {
|
public static String resolveGoodsApiOrigin(LbThirdIntegrationConfig config) {
|
||||||
if (StringUtils.hasText(config.getGoodsApiOrigin())) {
|
if (StringUtils.hasText(config.getGoodsApiOrigin())) {
|
||||||
return config.getGoodsApiOrigin().trim();
|
return config.getGoodsApiOrigin().trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user