增加抢单接口

This commit is contained in:
2026-05-28 18:45:12 +08:00
parent b0494ff5b6
commit 412aa1f8d8
7 changed files with 304 additions and 1 deletions

View File

@@ -0,0 +1,136 @@
package com.rj.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.rj.config.HxrAdminProperties;
import com.rj.util.HxrGoodsSignUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 调用 hxrd {@code POST /api/order/buy} 抢购(请求头 {@code token} + {@code S/T/N} 鉴权)。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class HxrAdminBuyService {
private static final SecureRandom RANDOM = new SecureRandom();
private static final String USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0";
private static final ObjectMapper JSON = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
private final HxrAdminProperties properties;
/**
* @return {@code apiCode} 为 -1 表示 HTTP 或解析失败
*/
public BuyApiResult buy(long goodsId, long sellerId, String token) throws Exception {
if (token == null || token.isBlank()) {
return BuyApiResult.failure(-1, "token不能为空");
}
String appStr = properties.getGoodsApiAppStr();
if (appStr == null || appStr.isBlank()) {
return BuyApiResult.failure(-1, "未配置 hxr.admin.goods-api-app-str");
}
long timestamp = System.currentTimeMillis() / 1000;
String noncestr = randomNoncestr();
Map<String, Object> signParams = new LinkedHashMap<>();
signParams.put("id", goodsId);
signParams.put("seller_id", sellerId);
signParams.put("timestamp", timestamp);
signParams.put("noncestr", noncestr);
String sign = HxrGoodsSignUtil.computeSign(signParams, appStr.trim());
Map<String, Object> body = new LinkedHashMap<>();
body.put("id", goodsId);
body.put("seller_id", sellerId);
String bodyJson = JSON.writeValueAsString(body);
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(60))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(properties.getBuyApiUrl()))
.timeout(Duration.ofSeconds(120))
.header("Accept", "application/json,*/*")
.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
.header("Content-Type", "application/json;charset=UTF-8")
.header("Origin", properties.getGoodsApiOrigin())
.header("Referer", properties.getGoodsApiReferer())
.header("User-Agent", USER_AGENT)
.header("token", token.trim())
.header("S", sign)
.header("T", String.valueOf(timestamp))
.header("N", noncestr)
.POST(HttpRequest.BodyPublishers.ofString(bodyJson, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
int status = response.statusCode();
String bodyText = response.body();
if (status < 200 || status >= 300) {
log.warn("hxr /api/order/buy HTTP {} id={} sellerId={} bodyPrefix={}",
status, goodsId, sellerId, abbreviate(bodyText, 400));
return BuyApiResult.failure(-1, "HTTP " + status);
}
if (bodyText == null || bodyText.isBlank()) {
log.warn("hxr /api/order/buy empty body id={} sellerId={}", goodsId, sellerId);
return BuyApiResult.failure(-1, "响应为空");
}
JsonNode root = JSON.readTree(bodyText);
int code = root.path("code").asInt(-1);
String msg = root.path("msg").asText("");
if (code != 0) {
log.warn("hxr /api/order/buy api code={} msg={} id={} sellerId={}",
code, msg, goodsId, sellerId);
}
return new BuyApiResult(code == 0, code, msg);
}
public record BuyApiResult(boolean success, int apiCode, String apiMsg) {
public static BuyApiResult failure(int code, String msg) {
return new BuyApiResult(false, code, msg);
}
}
private static String randomNoncestr() {
String base36 = Long.toUnsignedString(Math.abs(RANDOM.nextLong()), 36);
if (base36.length() >= 5) {
return base36.substring(base36.length() - 5);
}
StringBuilder sb = new StringBuilder(base36);
while (sb.length() < 5) {
sb.append(Integer.toString(RANDOM.nextInt(36), 36));
}
return sb.toString();
}
private static String abbreviate(String s, int maxLen) {
if (s == null) {
return "";
}
return s.length() <= maxLen ? s : s.substring(0, maxLen) + "...";
}
}