从抢货接口读取货物包

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 "";

View File

@@ -52,6 +52,8 @@ public class LbDeductionAmountServiceImpl
/** 优先匹配「申请…抵扣…金额数字」,避免误用「已提现金额」。 */
private static final Pattern DIKOU_AMT_PATTERN =
Pattern.compile("申请[^\\n]*?抵扣[^\\n]*?金额\\s*(\\d+(?:\\.\\d+)?)", Pattern.CASE_INSENSITIVE);
private static final Pattern TIXIAN_AMT_PATTERN =
Pattern.compile("已提现金额\\s*(\\d+(?:\\.\\d+)?)", Pattern.CASE_INSENSITIVE);
private static final String PARSE_SYSTEM_PROMPT = """
你是信息抽取助手。用户会提供「抵扣金记录」自然语言,常见为多行固定格式,例如:
@@ -64,11 +66,12 @@ public class LbDeductionAmountServiceImpl
1. 首行或文中「M月d日」如 5月21日仅作业务日期参考不要写入 JSON服务端会从原文补 createTime
2. 「姓名 + 空格 + 11位手机号」同一行userName 为姓名userPhone 为手机号。
3. dikouAmt 取含「申请」且含「抵扣」且含「金额」那一行中的数字(如上例 2678不要用「已提现金额」作为 dikouAmt。
4. FXJ、SJF 等字母为业务代号,忽略即可
5. 金额只输出数字,不要带「元」等单位
4. tixianAmt 取含「已提现金额」那一行中的数字(如上例 2678若无该行可省略
5. FXJ、SJF 等字母为业务代号,忽略即可
6. 金额只输出数字,不要带「元」等单位。
请只输出一个 JSON 对象,不要 markdown 代码块,不要解释性文字。
字段名必须完全一致userName、userPhone、dikouAmt。
字段名必须完全一致userName、userPhone、dikouAmt、tixianAmt
createTime 若填写,格式 yyyy-MM-dd HH:mm:ss服务端仅将非当前年的年份校正为系统当前年月日不变。
不要输出 id、tenantId、originalText、updateTime。
""";
@@ -132,10 +135,11 @@ public class LbDeductionAmountServiceImpl
enrichFromDeductionText(entity, trimmedText);
log.debug(
"parseFromTextByLlmAndSaveuserName={} userPhone={} dikouAmt={} createTime={}",
"parseFromTextByLlmAndSaveuserName={} userPhone={} dikouAmt={} tixianAmt={} createTime={}",
entity.getUserName(),
entity.getUserPhone(),
entity.getDikouAmt(),
entity.getTixianAmt(),
entity.getCreateTime());
return add(entity);
@@ -185,6 +189,7 @@ public class LbDeductionAmountServiceImpl
fillCreateTimeFromText(entity, text);
// 含「申请…抵扣…金额」时以该行为准,避免模型误用「已提现金额」
fillDikouAmtFromText(entity, text);
fillTixianAmtFromText(entity, text);
if (entity.getUserPhone() == null || entity.getUserPhone().isBlank()) {
fillPhoneFromText(entity, text);
}
@@ -221,6 +226,13 @@ public class LbDeductionAmountServiceImpl
}
}
private void fillTixianAmtFromText(LbDeductionAmount entity, String text) {
Matcher matcher = TIXIAN_AMT_PATTERN.matcher(text);
if (matcher.find()) {
entity.setTixianAmt(new BigDecimal(matcher.group(1)));
}
}
private void fillPhoneFromText(LbDeductionAmount entity, String text) {
Matcher matcher = MOBILE_PHONE_PATTERN.matcher(text);
if (matcher.find()) {
@@ -283,6 +295,9 @@ public class LbDeductionAmountServiceImpl
if (entity.getDikouAmt() == null) {
entity.setDikouAmt(BigDecimal.ZERO);
}
if (entity.getTixianAmt() == null) {
entity.setTixianAmt(BigDecimal.ZERO);
}
if (entity.getId() == null || entity.getId().trim().isEmpty()) {
entity.setId(UUID.randomUUID().toString());

View File

@@ -262,7 +262,7 @@ public class LbGoodsServiceImpl extends ServiceImpl<LbGoodsMapper, LbGoods> impl
if (opt.isEmpty()) {
if (page == 1) {
result.put("success", false);
result.put("message", "未拉取到货品(请检查 hxr.admin.goods-api-token、网络或第三方返回");
result.put("message", "未拉取到货品(请检查 hxr.admin.goods-api-token、goods-api-app-str 或网络)");
result.put("synced", 0);
return result;
}