From 999844ed3b4c4201db666963ea5100722361cc4a Mon Sep 17 00:00:00 2001 From: van Date: Wed, 10 Jun 2026 17:09:07 +0800 Subject: [PATCH] 1 --- .../controller/jd/JDInnerController.java | 60 +++++++ .../repository/OrderRowRepository.java | 4 + .../cn/van/business/util/JDScheduleJob.java | 169 ++++++++++++++++++ 3 files changed, 233 insertions(+) diff --git a/src/main/java/cn/van/business/controller/jd/JDInnerController.java b/src/main/java/cn/van/business/controller/jd/JDInnerController.java index 73f8d57..ec2e2e9 100644 --- a/src/main/java/cn/van/business/controller/jd/JDInnerController.java +++ b/src/main/java/cn/van/business/controller/jd/JDInnerController.java @@ -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 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 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 parseOrderIdList(Object raw) { + List 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 body) { // 兼容处理:如果body为空,返回友好提示 diff --git a/src/main/java/cn/van/business/repository/OrderRowRepository.java b/src/main/java/cn/van/business/repository/OrderRowRepository.java index 28e2619..4c87744 100644 --- a/src/main/java/cn/van/business/repository/OrderRowRepository.java +++ b/src/main/java/cn/van/business/repository/OrderRowRepository.java @@ -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 { List 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 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") diff --git a/src/main/java/cn/van/business/util/JDScheduleJob.java b/src/main/java/cn/van/business/util/JDScheduleJob.java index 451dc27..e27530d 100644 --- a/src/main/java/cn/van/business/util/JDScheduleJob.java +++ b/src/main/java/cn/van/business/util/JDScheduleJob.java @@ -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 backfillGoodsInfoByOrderNos(List orderNos) { + Map result = new LinkedHashMap<>(); + List> details = new ArrayList<>(); + int updated = 0; + int failed = 0; + int skipped = 0; + + List 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 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 backfillMissingGoodsInfo(int limit) { + int safeLimit = limit <= 0 ? 50 : Math.min(limit, 500); + List orderIds = orderRowRepository.findDistinctOrderIdsWithNullGoodsInfo(PageRequest.of(0, safeLimit)); + List orderNos = orderIds.stream().map(String::valueOf).collect(Collectors.toList()); + Map result = backfillGoodsInfoByOrderNos(orderNos); + result.put("mode", "missing"); + result.put("limit", safeLimit); + return result; + } + + private int backfillGoodsInfoForOrderId(Long orderId, List 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 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 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(); + } + } + /** * 根据指定的日期时间拉取订单 *