增加抢单接口
This commit is contained in:
136
src/main/java/com/rj/service/HxrAdminBuyService.java
Normal file
136
src/main/java/com/rj/service/HxrAdminBuyService.java
Normal 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) + "...";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user