This commit is contained in:
van
2026-06-10 17:09:07 +08:00
parent 08b675d409
commit 999844ed3b
3 changed files with 233 additions and 0 deletions

View File

@@ -551,6 +551,66 @@ public class JDInnerController {
* 返回:{ message, success }
* 注意请将skey放在请求Body中JSON格式不是Query参数
*/
/**
* 回填历史订单 goodsInfo店铺名、商品图
* Body: { "skey": "...", "orderIds": "352543902480387,3525433002460987" }
* 或: { "skey": "...", "missing": true, "limit": 100 }
*/
@PostMapping("/backfillGoodsInfo")
public Object backfillGoodsInfo(@RequestBody(required = false) Map<String, Object> body) {
if (body == null || body.isEmpty()) {
return error("请求Body不能为空示例: {\"skey\":\"...\",\"orderIds\":\"订单号1,订单号2\"}");
}
String skey = body.get("skey") != null ? String.valueOf(body.get("skey")) : null;
if (checkSkey(skey)) {
return error("invalid skey");
}
try {
boolean missing = body.get("missing") != null && Boolean.parseBoolean(String.valueOf(body.get("missing")));
int limit = parseInt(body.get("limit"), 50);
if (missing) {
return jdScheduleJob.backfillMissingGoodsInfo(limit);
}
List<String> orderNos = parseOrderIdList(body.get("orderIds"));
if (orderNos.isEmpty()) {
return error("请提供 orderIds逗号分隔或 missing=true");
}
return jdScheduleJob.backfillGoodsInfoByOrderNos(orderNos);
} catch (Exception e) {
logger.error("backfillGoodsInfo error", e);
return error("backfillGoodsInfo failed: " + e.getMessage());
}
}
private static List<String> parseOrderIdList(Object raw) {
List<String> result = new ArrayList<>();
if (raw == null) {
return result;
}
if (raw instanceof Collection<?> collection) {
for (Object item : collection) {
if (item != null) {
String s = String.valueOf(item).trim();
if (!s.isEmpty()) {
result.add(s);
}
}
}
return result;
}
String text = String.valueOf(raw).trim();
if (text.isEmpty()) {
return result;
}
for (String part : text.split("[,\\s]+")) {
String s = part.trim();
if (!s.isEmpty()) {
result.add(s);
}
}
return result;
}
@PostMapping("/cleanRedisData")
public Object cleanRedisData(@RequestBody(required = false) Map<String, Object> body) {
// 兼容处理如果body为空返回友好提示

View File

@@ -8,6 +8,7 @@ package cn.van.business.repository;
*/
import cn.van.business.model.jd.OrderRow;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -62,6 +63,9 @@ public interface OrderRowRepository extends JpaRepository<OrderRow, String> {
List<OrderRow> findByUnionId(long l);
@Query("SELECT DISTINCT o.orderId FROM OrderRow o WHERE o.goodsInfoId IS NULL AND o.orderId IS NOT NULL ORDER BY o.orderTime DESC")
List<Long> findDistinctOrderIdsWithNullGoodsInfo(Pageable pageable);
//// 在OrderRowRepository中添加模糊查询方法
//// 模糊查询收件人姓名或地址(包含分页)
//@Query("SELECT o FROM OrderRow o WHERE " + "o.recipientName LIKE %:keyword% OR " + "o.address LIKE %:keyword% " + "ORDER BY o.orderTime DESC")

View File

@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
@@ -199,6 +200,174 @@ public class JDScheduleJob {
return goodsInfoRepository.save(vo);
}
/**
* 按订单号批量回填 goodsInfo店铺名、商品图
*
* @param orderNos 订单号列表(子单号或父单号)
*/
public Map<String, Object> backfillGoodsInfoByOrderNos(List<String> orderNos) {
Map<String, Object> result = new LinkedHashMap<>();
List<Map<String, Object>> details = new ArrayList<>();
int updated = 0;
int failed = 0;
int skipped = 0;
List<SuperAdmin> admins = activeSuperAdmins();
if (admins.isEmpty()) {
result.put("success", false);
result.put("message", "未找到可用的联盟 appKey/secretKey");
return result;
}
if (orderNos == null || orderNos.isEmpty()) {
result.put("success", false);
result.put("message", "orderIds 不能为空");
return result;
}
for (String orderNo : orderNos) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("orderId", orderNo);
if (orderNo == null || orderNo.isBlank()) {
item.put("status", "skipped");
item.put("reason", "空订单号");
skipped++;
details.add(item);
continue;
}
try {
Long orderId = Long.parseLong(orderNo.trim());
int rows = backfillGoodsInfoForOrderId(orderId, admins);
if (rows > 0) {
item.put("status", "updated");
item.put("rows", rows);
updated++;
} else {
item.put("status", "skipped");
item.put("reason", "京东未返回 goodsInfo 或本地无匹配行");
skipped++;
}
} catch (Exception e) {
item.put("status", "failed");
item.put("reason", e.getMessage());
failed++;
logger.warn("回填 goodsInfo 失败 orderId={}", orderNo, e);
}
details.add(item);
sleepQuietly(200);
}
result.put("success", failed == 0);
result.put("total", orderNos.size());
result.put("updated", updated);
result.put("skipped", skipped);
result.put("failed", failed);
result.put("details", details);
return result;
}
/**
* 自动回填 goods_info_id 为空的最近订单
*/
public Map<String, Object> backfillMissingGoodsInfo(int limit) {
int safeLimit = limit <= 0 ? 50 : Math.min(limit, 500);
List<Long> orderIds = orderRowRepository.findDistinctOrderIdsWithNullGoodsInfo(PageRequest.of(0, safeLimit));
List<String> orderNos = orderIds.stream().map(String::valueOf).collect(Collectors.toList());
Map<String, Object> result = backfillGoodsInfoByOrderNos(orderNos);
result.put("mode", "missing");
result.put("limit", safeLimit);
return result;
}
private int backfillGoodsInfoForOrderId(Long orderId, List<SuperAdmin> admins) throws Exception {
for (SuperAdmin admin : admins) {
int updated = backfillGoodsInfoForOrderId(orderId, admin.getAppKey(), admin.getSecretKey());
if (updated > 0) {
return updated;
}
}
return 0;
}
private int backfillGoodsInfoForOrderId(Long orderId, String appKey, String secretKey) throws Exception {
int updated = 0;
int pageIndex = 1;
boolean hasMore = true;
while (hasMore) {
UnionOpenOrderRowQueryResponse response = queryByOrderId(orderId, pageIndex, appKey, secretKey);
if (response == null || response.getQueryResult() == null
|| response.getQueryResult().getCode() != 200
|| response.getQueryResult().getData() == null) {
break;
}
OrderRowResp[] data = response.getQueryResult().getData();
for (OrderRowResp resp : data) {
if (resp == null) {
continue;
}
updated += applyGoodsInfoFromResponse(resp);
}
hasMore = Boolean.TRUE.equals(response.getQueryResult().getHasMore());
pageIndex++;
}
return updated;
}
private int applyGoodsInfoFromResponse(OrderRowResp resp) {
GoodsInfoVO goodsInfoVO = resolveGoodsInfo(resp.getGoodsInfo(), resp.getSkuId());
if (goodsInfoVO == null) {
return 0;
}
int updated = 0;
if (resp.getId() != null) {
Optional<OrderRow> local = orderRowRepository.findById(resp.getId());
if (local.isPresent()) {
OrderRow row = local.get();
if (row.getGoodsInfoId() == null || !row.getGoodsInfoId().equals(goodsInfoVO.getId())) {
row.setGoodsInfoId(goodsInfoVO.getId());
orderRowRepository.save(row);
updated++;
}
return updated;
}
}
OrderRow orderRow = createOrderRow(resp);
orderRowRepository.save(orderRow);
return 1;
}
public UnionOpenOrderRowQueryResponse queryByOrderId(Long orderId, int pageIndex, String appKey, String secretKey) throws Exception {
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, appKey, secretKey);
UnionOpenOrderRowQueryRequest request = new UnionOpenOrderRowQueryRequest();
OrderRowReq orderReq = new OrderRowReq();
orderReq.setOrderId(orderId);
orderReq.setFields("goodsInfo");
orderReq.setPageIndex(pageIndex);
orderReq.setPageSize(200);
request.setOrderReq(orderReq);
request.setVersion("1.0");
request.setSignmethod("md5");
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
request.setTimestamp(simpleDateFormat.format(date));
return client.execute(request);
}
private List<SuperAdmin> activeSuperAdmins() {
return super_admins.values().stream()
.filter(a -> a.getAppKey() != null && !a.getAppKey().isBlank()
&& a.getSecretKey() != null && !a.getSecretKey().isBlank())
.collect(Collectors.toList());
}
private void sleepQuietly(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 根据指定的日期时间拉取订单
*