1
This commit is contained in:
@@ -551,6 +551,66 @@ public class JDInnerController {
|
|||||||
* 返回:{ message, success }
|
* 返回:{ message, success }
|
||||||
* 注意:请将skey放在请求Body中(JSON格式),不是Query参数
|
* 注意:请将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")
|
@PostMapping("/cleanRedisData")
|
||||||
public Object cleanRedisData(@RequestBody(required = false) Map<String, Object> body) {
|
public Object cleanRedisData(@RequestBody(required = false) Map<String, Object> body) {
|
||||||
// 兼容处理:如果body为空,返回友好提示
|
// 兼容处理:如果body为空,返回友好提示
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package cn.van.business.repository;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import cn.van.business.model.jd.OrderRow;
|
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.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
@@ -62,6 +63,9 @@ public interface OrderRowRepository extends JpaRepository<OrderRow, String> {
|
|||||||
|
|
||||||
List<OrderRow> findByUnionId(long l);
|
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中添加模糊查询方法
|
//// 在OrderRowRepository中添加模糊查询方法
|
||||||
//// 模糊查询收件人姓名或地址(包含分页)
|
//// 模糊查询收件人姓名或地址(包含分页)
|
||||||
//@Query("SELECT o FROM OrderRow o WHERE " + "o.recipientName LIKE %:keyword% OR " + "o.address LIKE %:keyword% " + "ORDER BY o.orderTime DESC")
|
//@Query("SELECT o FROM OrderRow o WHERE " + "o.recipientName LIKE %:keyword% OR " + "o.address LIKE %:keyword% " + "ORDER BY o.orderTime DESC")
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
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.HashOperations;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
@@ -199,6 +200,174 @@ public class JDScheduleJob {
|
|||||||
return goodsInfoRepository.save(vo);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据指定的日期时间拉取订单
|
* 根据指定的日期时间拉取订单
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user