拉取买家的卖家订单

This commit is contained in:
2026-06-07 08:47:53 +08:00
parent b36b37bd51
commit 2b026cba2c
2 changed files with 162 additions and 65 deletions

View File

@@ -56,6 +56,9 @@ public class LbBuyerShoppingController {
description =
"按 tenantId 从 lb_third_integration_config 读取 login_api_path、buyer_order_list_path 等配置,"
+ "对每个买方手机号模拟登录获取 token再分页调用买方订单列表 API 并解析入库;"
+ "拉取过程中将订单中的卖家手机号缓存到 cacheSellerPhones已拉取过的号码不再缓存"
+ "买方手机号全部处理完成后,仅对尚未拉取过的卖家手机号模拟登录、拉取订单并保存;"
+ "同一请求内重复手机号自动跳过,卖家阶段不再扩展缓存,避免重复拉取与死循环;"
+ "未传 password 时默认密码为 123456")
public ResponseEntity<Map<String, Object>> pullFromThird(
@Parameter(description = "租户 id 与买方手机号列表", required = true)

View File

@@ -27,9 +27,11 @@ import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* 买方购物表 lb_buyer_shopping 服务实现
@@ -340,16 +342,24 @@ public class LbBuyerShoppingServiceImpl
}
HxrUserLoginApiContext loginCtx = loginCtxOpt.get();
Set<String> cacheSellerPhones = new LinkedHashSet<>();
Set<String> processedMobiles = new LinkedHashSet<>();
List<Map<String, Object>> items = new ArrayList<>();
int successCount = 0;
int failCount = 0;
int skippedCount = 0;
int totalSaved = 0;
int totalFetched = 0;
String requestPassword = request.getPassword() != null ? request.getPassword().trim() : "";
String password = !requestPassword.isEmpty()
? requestPassword
: DEFAULT_SIMULATE_LOGIN_PASSWORD;
for (String rawMobile : request.getMobiles()) {
Map<String, Object> item = new LinkedHashMap<>();
if (rawMobile == null || rawMobile.trim().isEmpty()) {
String mobile = rawMobile != null ? rawMobile.trim() : "";
if (mobile.isEmpty()) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("mobile", rawMobile);
item.put("loginSuccess", false);
item.put("orderFetchSuccess", false);
@@ -359,69 +369,58 @@ public class LbBuyerShoppingServiceImpl
failCount++;
continue;
}
if (processedMobiles.contains(mobile)) {
Map<String, Object> item = buildSkippedPullItem(mobile, "手机号已拉取,跳过重复");
items.add(item);
skippedCount++;
continue;
}
processedMobiles.add(mobile);
String mobile = rawMobile.trim();
item.put("mobile", mobile);
String password = !requestPassword.isEmpty()
? requestPassword
: DEFAULT_SIMULATE_LOGIN_PASSWORD;
try {
HxrAdminUserLoginService.LoginApiResult loginResult =
hxrAdminUserLoginService.login(mobile, password, loginCtx);
item.put("httpStatus", loginResult.httpStatus());
item.put("apiCode", loginResult.apiCode());
item.put("apiMsg", loginResult.apiMsg());
item.put("loginSuccess", loginResult.success());
item.put("parsed", loginResult.parsed());
if (!loginResult.success()) {
item.put("orderFetchSuccess", false);
failCount++;
items.add(item);
continue;
}
String token = HxrAdminUserLoginService.extractToken(loginResult.parsed());
if (token == null || token.isBlank()) {
item.put("orderFetchSuccess", false);
item.put("orderFetchMsg", "响应中无 data.userinfo.token");
failCount++;
items.add(item);
continue;
}
Optional<HxrBuyerOrderApiContext> orderCtxOpt =
lbThirdIntegrationConfigService.resolveBuyerOrderApiContext(tenantId, token);
if (orderCtxOpt.isEmpty()) {
item.put("orderFetchSuccess", false);
item.put("orderFetchMsg",
"未找到买方订单 API 配置或配置未启用、URL/凭证不完整(请检查 lb_third_integration_config");
failCount++;
items.add(item);
continue;
}
HxrBuyerOrderApiContext orderCtx = orderCtxOpt.get();
item.put("buyerOrderListUrl", orderCtx.buyerOrderListBaseUrl());
Map<String, Object> fetchResult = fetchAndSaveBuyerOrders(orderCtx, mobile);
item.putAll(fetchResult);
if (Boolean.TRUE.equals(fetchResult.get("orderFetchSuccess"))) {
successCount++;
totalSaved += toInt(fetchResult.get("ordersSaved"));
totalFetched += toInt(fetchResult.get("ordersFetched"));
} else {
failCount++;
}
} catch (Exception e) {
log.warn("买方购物第三方拉取异常 mobile={} tenantId={}", mobile, tenantId, e);
item.put("loginSuccess", false);
item.put("orderFetchSuccess", false);
item.put("apiCode", -1);
item.put("apiMsg", "请求异常:" + e.getMessage());
Map<String, Object> item = pullOrdersForMobile(
mobile, password, tenantId, loginCtx, cacheSellerPhones, processedMobiles);
items.add(item);
if (Boolean.TRUE.equals(item.get("orderFetchSuccess"))) {
successCount++;
totalSaved += toInt(item.get("ordersSaved"));
totalFetched += toInt(item.get("ordersFetched"));
} else {
failCount++;
}
items.add(item);
}
List<String> sellerQueue = new ArrayList<>();
for (String sellerMobile : cacheSellerPhones) {
if (!processedMobiles.contains(sellerMobile)) {
sellerQueue.add(sellerMobile);
}
}
List<Map<String, Object>> sellerItems = new ArrayList<>();
int sellerSuccessCount = 0;
int sellerFailCount = 0;
int sellerSkippedCount = 0;
int sellerTotalSaved = 0;
int sellerTotalFetched = 0;
for (String sellerMobile : sellerQueue) {
if (processedMobiles.contains(sellerMobile)) {
sellerItems.add(buildSkippedPullItem(sellerMobile, "卖家手机号已拉取,跳过重复"));
sellerSkippedCount++;
continue;
}
processedMobiles.add(sellerMobile);
Map<String, Object> sellerItem = pullOrdersForMobile(
sellerMobile, password, tenantId, loginCtx, null, processedMobiles);
sellerItem.put("source", "cacheSellerPhones");
sellerItems.add(sellerItem);
if (Boolean.TRUE.equals(sellerItem.get("orderFetchSuccess"))) {
sellerSuccessCount++;
sellerTotalSaved += toInt(sellerItem.get("ordersSaved"));
sellerTotalFetched += toInt(sellerItem.get("ordersFetched"));
} else {
sellerFailCount++;
}
}
result.put("success", true);
@@ -429,8 +428,18 @@ public class LbBuyerShoppingServiceImpl
result.put("data", items);
result.put("successCount", successCount);
result.put("failCount", failCount);
result.put("skippedCount", skippedCount);
result.put("ordersSaved", totalSaved);
result.put("ordersFetched", totalFetched);
result.put("cacheSellerPhones", new ArrayList<>(cacheSellerPhones));
result.put("sellerQueue", sellerQueue);
result.put("sellerData", sellerItems);
result.put("sellerSuccessCount", sellerSuccessCount);
result.put("sellerFailCount", sellerFailCount);
result.put("sellerSkippedCount", sellerSkippedCount);
result.put("sellerOrdersSaved", sellerTotalSaved);
result.put("sellerOrdersFetched", sellerTotalFetched);
result.put("processedMobiles", new ArrayList<>(processedMobiles));
result.put("loginApiUrl", loginCtx.loginApiUrl());
return result;
} catch (Exception e) {
@@ -441,7 +450,75 @@ public class LbBuyerShoppingServiceImpl
}
}
private Map<String, Object> fetchAndSaveBuyerOrders(HxrBuyerOrderApiContext ctx, String buyerMobile) {
private Map<String, Object> buildSkippedPullItem(String mobile, String reason) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("mobile", mobile);
item.put("skipped", true);
item.put("skipReason", reason);
item.put("loginSuccess", false);
item.put("orderFetchSuccess", false);
return item;
}
private Map<String, Object> pullOrdersForMobile(
String mobile,
String password,
String tenantId,
HxrUserLoginApiContext loginCtx,
Set<String> sellerPhoneCollector,
Set<String> processedMobiles) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("mobile", mobile);
try {
HxrAdminUserLoginService.LoginApiResult loginResult =
hxrAdminUserLoginService.login(mobile, password, loginCtx);
item.put("httpStatus", loginResult.httpStatus());
item.put("apiCode", loginResult.apiCode());
item.put("apiMsg", loginResult.apiMsg());
item.put("loginSuccess", loginResult.success());
item.put("parsed", loginResult.parsed());
if (!loginResult.success()) {
item.put("orderFetchSuccess", false);
return item;
}
String token = HxrAdminUserLoginService.extractToken(loginResult.parsed());
if (token == null || token.isBlank()) {
item.put("orderFetchSuccess", false);
item.put("orderFetchMsg", "响应中无 data.userinfo.token");
return item;
}
Optional<HxrBuyerOrderApiContext> orderCtxOpt =
lbThirdIntegrationConfigService.resolveBuyerOrderApiContext(tenantId, token);
if (orderCtxOpt.isEmpty()) {
item.put("orderFetchSuccess", false);
item.put("orderFetchMsg",
"未找到买方订单 API 配置或配置未启用、URL/凭证不完整(请检查 lb_third_integration_config");
return item;
}
HxrBuyerOrderApiContext orderCtx = orderCtxOpt.get();
item.put("buyerOrderListUrl", orderCtx.buyerOrderListBaseUrl());
Map<String, Object> fetchResult =
fetchAndSaveBuyerOrders(orderCtx, mobile, sellerPhoneCollector, processedMobiles);
item.putAll(fetchResult);
} catch (Exception e) {
log.warn("买方购物第三方拉取异常 mobile={} tenantId={}", mobile, tenantId, e);
item.put("loginSuccess", false);
item.put("orderFetchSuccess", false);
item.put("apiCode", -1);
item.put("apiMsg", "请求异常:" + e.getMessage());
}
return item;
}
private Map<String, Object> fetchAndSaveBuyerOrders(
HxrBuyerOrderApiContext ctx,
String loginMobile,
Set<String> sellerPhoneCollector,
Set<String> processedMobiles) {
Map<String, Object> result = new LinkedHashMap<>();
int page = 1;
int totalSaved = 0;
@@ -495,8 +572,9 @@ public class LbBuyerShoppingServiceImpl
if (entity.getId() == null) {
continue;
}
collectSellerPhone(entity, sellerPhoneCollector, processedMobiles);
if (entity.getBuyerMobile() == null || entity.getBuyerMobile().isBlank()) {
entity.setBuyerMobile(buyerMobile);
entity.setBuyerMobile(loginMobile);
}
applyDefaults(entity);
if (entity.getCreatedAt() == null) {
@@ -539,7 +617,7 @@ public class LbBuyerShoppingServiceImpl
result.put("orderPages", page);
return result;
} catch (Exception e) {
log.warn("拉取买方订单异常 buyerMobile={}", buyerMobile, e);
log.warn("拉取买方订单异常 loginMobile={}", loginMobile, e);
result.put("orderFetchSuccess", false);
result.put("orderFetchMsg", "拉取买方订单异常:" + e.getMessage());
result.put("ordersSaved", totalSaved);
@@ -791,6 +869,22 @@ public class LbBuyerShoppingServiceImpl
private record StatAggregate(BigDecimal totalMoney, int orderCount, BigDecimal avgAmount) {
}
private static void collectSellerPhone(
LbBuyerShopping entity, Set<String> collector, Set<String> processedMobiles) {
if (collector == null || entity == null) {
return;
}
String phone = entity.getSellerMobile();
if (phone == null || phone.isBlank()) {
return;
}
String trimmed = phone.trim();
if (processedMobiles != null && processedMobiles.contains(trimmed)) {
return;
}
collector.add(trimmed);
}
private static int toInt(Object value) {
if (value instanceof Number number) {
return number.intValue();