从抢货接口读取货物包

This commit is contained in:
2026-05-27 08:13:30 +08:00
parent 947bb21c8b
commit 0258079d49
12 changed files with 281 additions and 19 deletions

View File

@@ -1,12 +1,14 @@
package com.rj.service;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.rj.config.HxrAdminProperties;
import com.rj.dto.hxr.HxrLbGoodsSelectResponse;
import com.rj.util.HxrGoodsSignUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -16,11 +18,14 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
/**
* 调用 hxrd {@code /api/order/goods} 货品列表(请求头 {@code token} 鉴权)。
* 调用 hxrd {@code /api/order/goods} 货品列表(请求头 {@code token} + {@code S/T/N} 鉴权)。
*/
@Slf4j
@Service
@@ -29,6 +34,12 @@ public class HxrAdminGoodsService {
public static final int GOODS_PAGE_SIZE = 20;
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)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
@@ -48,25 +59,46 @@ public class HxrAdminGoodsService {
log.warn("hxr.admin 未配置 goods-api-token跳过 /api/order/goods");
return Optional.empty();
}
String appStr = properties.getGoodsApiAppStr();
if (appStr == null || appStr.isBlank()) {
log.warn("hxr.admin 未配置 goods-api-app-str跳过 /api/order/goods");
return Optional.empty();
}
int safePage = Math.max(1, page);
String uri = UriComponentsBuilder.fromUriString(properties.getGoodsApiUrl())
.replaceQueryParam("page", Math.max(1, page))
.replaceQueryParam("page", safePage)
.replaceQueryParam("limit", GOODS_PAGE_SIZE)
.build()
.encode()
.toUriString();
long timestamp = System.currentTimeMillis() / 1000;
String noncestr = randomNoncestr();
Map<String, Object> signParams = new LinkedHashMap<>();
signParams.put("page", safePage);
signParams.put("limit", GOODS_PAGE_SIZE);
signParams.put("timestamp", timestamp);
signParams.put("noncestr", noncestr);
String sign = HxrGoodsSignUtil.computeSign(signParams, appStr.trim());
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(35))
.connectTimeout(Duration.ofSeconds(60))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.timeout(Duration.ofSeconds(120))
.header("Accept", "application/json, text/plain, */*")
.header("Accept", "application/json,*/*")
.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
.header("Origin", properties.getGoodsApiOrigin())
.header("Referer", properties.getGoodsApiReferer())
.header("User-Agent", USER_AGENT)
.header("token", token.trim())
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
.header("S", sign)
.header("T", String.valueOf(timestamp))
.header("N", noncestr)
.GET()
.build();
@@ -74,24 +106,58 @@ public class HxrAdminGoodsService {
int status = response.statusCode();
if (status < 200 || status >= 300) {
log.warn("hxr /api/order/goods HTTP {} page={} bodyPrefix={}",
status, page, abbreviate(response.body(), 400));
status, safePage, abbreviate(response.body(), 400));
return Optional.empty();
}
String bodyText = response.body();
if (bodyText == null || bodyText.isBlank()) {
log.warn("hxr /api/order/goods empty body page={}", page);
log.warn("hxr /api/order/goods empty body page={}", safePage);
return Optional.empty();
}
HxrLbGoodsSelectResponse body = JSON.readValue(bodyText, HxrLbGoodsSelectResponse.class);
if (body.code() != 0) {
log.warn("hxr /api/order/goods api code={} msg={} page={}", body.code(), body.msg(), page);
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/goods api code={} msg={} page={} bodyPrefix={}",
code, msg, safePage, abbreviate(bodyText, 400));
return Optional.empty();
}
JsonNode dataNode = root.get("data");
if (isErrorPayload(dataNode)) {
log.warn("hxr /api/order/goods 返回异常 data={} page={} msg={}", dataNode, safePage, msg);
return Optional.empty();
}
HxrLbGoodsSelectResponse body = JSON.treeToValue(root, HxrLbGoodsSelectResponse.class);
return Optional.of(body);
}
/** 与前端 {@code Math.random().toString(36).slice(-5)} 接近的 5 位随机串。 */
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 boolean isErrorPayload(JsonNode dataNode) {
if (dataNode == null || dataNode.isNull()) {
return false;
}
if (!dataNode.isArray() || dataNode.isEmpty()) {
return false;
}
return dataNode.get(0).isTextual() || dataNode.get(0).isNumber();
}
private static String abbreviate(String s, int maxLen) {
if (s == null) {
return "";