Files
ruoyi-java/ruoyi-system/src/main/java/com/ruoyi/jarvis/task/LogisticsScanTask.java
2026-04-11 00:48:37 +08:00

130 lines
6.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.ruoyi.jarvis.task;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.ILogisticsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 物流信息扫描定时任务
* 按配置周期(默认每 20 分钟)扫描分销标记为 F/PDD 等的订单(最近 30 天),拉物流并推送;
* 结束后处理企微分享链 adhoc 队列。
*/
@Component
public class LogisticsScanTask {
private static final Logger logger = LoggerFactory.getLogger(LogisticsScanTask.class);
@Resource
private IJDOrderService jdOrderService;
@Resource
private ILogisticsService logisticsService;
/** 每条订单处理后的间隔(毫秒),减轻下游物流 HTTP 压力0 表示不睡眠 */
@Value("${jarvis.server.logistics.scan.order-delay-ms:250}")
private long orderDelayMs;
/** 单轮最多处理的订单数0 表示不限制(候选很多时可限制单轮耗时) */
@Value("${jarvis.server.logistics.scan.max-orders-per-round:0}")
private int maxOrdersPerRound;
/**
* 只扫描最近 30 天的订单SQL 固定);周期与单轮上限见 jarvis.server.logistics.scan.*
*/
@Scheduled(cron = "${jarvis.server.logistics.scan.cron:0 */20 * * * ?}")
public void scanAndFetchLogistics() {
long t0 = System.currentTimeMillis();
int orderCandidates = 0;
int orderProcessed = 0;
int orderSkipped = 0;
int orderSuccess = 0;
int orderFailed = 0;
int adhocQueuePopped = 0;
int adhocReconciled = 0;
logger.info("========== 物流扫描定时任务开始(订单 F/PDD + 企微分享链队列) ==========");
try {
List<JDOrder> orders = jdOrderService.selectJDOrderListByDistributionMarkFOrPDD();
if (orders == null || orders.isEmpty()) {
logger.info("订单扫描:候选 0 条最近30天 F/PDD 有物流链)");
} else {
int totalFromDb = orders.size();
if (maxOrdersPerRound > 0 && orders.size() > maxOrdersPerRound) {
logger.info("订单扫描:库中候选 {} 条,本轮按 max-orders-per-round={} 仅处理前 {} 条(余下轮次继续)",
totalFromDb, maxOrdersPerRound, maxOrdersPerRound);
orders = new ArrayList<>(orders.subList(0, maxOrdersPerRound));
}
orderCandidates = orders.size();
logger.info("订单扫描:本轮处理列表 {} 条最近30天 F/PDD 有物流链)", orderCandidates);
for (JDOrder order : orders) {
try {
if (logisticsService.isOrderProcessed(order.getId())) {
orderSkipped++;
continue;
}
logger.debug("订单扫描:处理中 id={} orderId={} mark={}",
order.getId(), order.getOrderId(), order.getDistributionMark());
boolean success = logisticsService.fetchLogisticsAndPush(order);
orderProcessed++;
if (success) {
orderSuccess++;
logger.info("订单扫描:成功 id={} orderId={}", order.getId(), order.getOrderId());
} else {
orderFailed++;
logger.warn("订单扫描:未成功 id={} orderId={}", order.getId(), order.getOrderId());
}
if (orderDelayMs > 0) {
Thread.sleep(orderDelayMs);
}
} catch (InterruptedException e) {
logger.error("定时任务被中断", e);
Thread.currentThread().interrupt();
break;
} catch (Exception e) {
orderFailed++;
logger.error("订单扫描:异常 id={} err={}", order.getId(), e.getMessage(), e);
}
}
}
} catch (Exception e) {
logger.error("订单扫描阶段异常", e);
} finally {
try {
logger.info("---------- 企微分享链 adhoc落库对账入队含 ABANDONED 复位入队,一月内 PENDING/WAITING/IMPORTED 等) ----------");
adhocReconciled = logisticsService.reconcileShareLinkJobsIntoPendingQueue();
logger.info("企微分享链 adhoc对账入队 {} 条(余者可能尚在限频窗口内)", adhocReconciled);
} catch (Exception e) {
logger.error("企微分享链 adhoc 对账入队异常", e);
}
try {
logger.info("---------- 企微分享链 adhoc 队列:开始 drain ----------");
adhocQueuePopped = logisticsService.drainPendingShareLinkQueue();
} catch (Exception e) {
logger.error("企微分享链 adhoc 队列 drain 异常", e);
}
}
long elapsed = System.currentTimeMillis() - t0;
logger.info("========== 物流扫描任务统计(本轮) ==========");
logger.info("订单:候选 {} 条 | 跳过(Redis已处理) {} | 本次已拉取处理 {} | 成功 {} | 失败或未推送 {}",
orderCandidates, orderSkipped, orderProcessed, orderSuccess, orderFailed);
logger.info("企微分享链队列:对账入队 {} 条 | Redis 弹出并处理 {} 条(未出单等情况会重入队,与逐条日志一致)",
adhocReconciled, adhocQueuePopped);
logger.info("总耗时 {} ms", elapsed);
logger.info("==========================================");
}
}