This commit is contained in:
van
2026-04-11 00:48:37 +08:00
parent fed0158444
commit 5205d8c155
8 changed files with 111 additions and 12 deletions

View File

@@ -204,6 +204,12 @@ jarvis:
health-path: /health health-path: /health
# 每次定时任务最多处理多少条企微分享链待队列RPUSH 入队、LPOP 出队) # 每次定时任务最多处理多少条企微分享链待队列RPUSH 入队、LPOP 出队)
adhoc-pending-batch-size: 50 adhoc-pending-batch-size: 50
# 物流扫描LogisticsScanTask轮询 JD 单拉运单 + drain 分享链队列
scan:
cron: "0 */20 * * * ?"
order-delay-ms: 250
# 0=不限制;例如 40 可控制单轮最长耗时(余下下轮再扫)
max-orders-per-round: 0
# 获取评论接口服务地址(后端转发,避免前端跨域) # 获取评论接口服务地址(后端转发,避免前端跨域)
fetch-comments: fetch-comments:
base-url: http://192.168.8.60:5008 base-url: http://192.168.8.60:5008

View File

@@ -203,6 +203,10 @@ jarvis:
fetch-path: /fetch_logistics fetch-path: /fetch_logistics
health-path: /health health-path: /health
adhoc-pending-batch-size: 50 adhoc-pending-batch-size: 50
scan:
cron: "0 */20 * * * ?"
order-delay-ms: 250
max-orders-per-round: 0
# 获取评论接口服务地址(后端转发) # 获取评论接口服务地址(后端转发)
fetch-comments: fetch-comments:
base-url: http://192.168.8.60:5008 base-url: http://192.168.8.60:5008

View File

@@ -45,6 +45,9 @@ public interface IErpGoofishOrderService {
/** 京东物流服务在写 Redis / 企微货主推送后、触发闲鱼同步前记一笔source=JD_LOGISTICS_PUSH。 */ /** 京东物流服务在写 Redis / 企微货主推送后、触发闲鱼同步前记一笔source=JD_LOGISTICS_PUSH。 */
void traceJdLogisticsPushForGoofish(Long jdOrderId, String waybillNo, String summary); void traceJdLogisticsPushForGoofish(Long jdOrderId, String waybillNo, String summary);
/** 是否存在关联本京东单的闲管家订单(用于物流企微走闲鱼自建应用)。 */
boolean hasLinkedGoofishOrder(Long jdOrderId);
/** 订单状态 / 物流 / 发货 变更日志(新→旧) */ /** 订单状态 / 物流 / 发货 变更日志(新→旧) */
List<ErpGoofishOrderEventLog> listEventLogsByOrderId(Long orderId); List<ErpGoofishOrderEventLog> listEventLogsByOrderId(Long orderId);

View File

@@ -28,7 +28,9 @@ public class GoofishOrderChangeLogger {
public static final String TYPE_LOGISTICS = "LOGISTICS_SYNC"; public static final String TYPE_LOGISTICS = "LOGISTICS_SYNC";
public static final String TYPE_SHIP = "SHIP"; public static final String TYPE_SHIP = "SHIP";
/** 京东物流扫描服务Redis + 企微货主推送链路(见 LogisticsServiceImpl不再二次走 wxSend 闲鱼应用避免重复打扰 */ /**
* 京东物流扫描:货主通知已由 LogisticsServiceImpl 走闲鱼自建应用或 PDD此处仅落库避免再调 wxSend 重复推送。
*/
public static final String SOURCE_JD_LOGISTICS_PUSH = "JD_LOGISTICS_PUSH"; public static final String SOURCE_JD_LOGISTICS_PUSH = "JD_LOGISTICS_PUSH";
@Resource @Resource

View File

@@ -171,6 +171,17 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
} }
} }
@Override
public boolean hasLinkedGoofishOrder(Long jdOrderId) {
if (jdOrderId == null) {
return false;
}
ErpGoofishOrder query = new ErpGoofishOrder();
query.setJdOrderId(jdOrderId);
List<ErpGoofishOrder> list = erpGoofishOrderMapper.selectList(query);
return list != null && !list.isEmpty();
}
@Override @Override
public void traceJdLogisticsPushForGoofish(Long jdOrderId, String waybillNo, String summary) { public void traceJdLogisticsPushForGoofish(Long jdOrderId, String waybillNo, String summary) {
if (jdOrderId == null || goofishOrderChangeLogger == null) { if (jdOrderId == null || goofishOrderChangeLogger == null) {

View File

@@ -9,6 +9,7 @@ import com.ruoyi.jarvis.mapper.WeComShareLinkLogisticsJobMapper;
import com.ruoyi.jarvis.service.IErpGoofishOrderService; import com.ruoyi.jarvis.service.IErpGoofishOrderService;
import com.ruoyi.jarvis.service.ILogisticsService; import com.ruoyi.jarvis.service.ILogisticsService;
import com.ruoyi.jarvis.service.IJDOrderService; import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.wecom.WxSendGoofishNotifyClient;
import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISysConfigService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -85,6 +86,9 @@ public class LogisticsServiceImpl implements ILogisticsService {
@Resource @Resource
private IErpGoofishOrderService erpGoofishOrderService; private IErpGoofishOrderService erpGoofishOrderService;
@Resource
private WxSendGoofishNotifyClient wxSendGoofishNotifyClient;
@PostConstruct @PostConstruct
public void init() { public void init() {
@@ -847,8 +851,23 @@ public class LogisticsServiceImpl implements ILogisticsService {
// 运单号 // 运单号
pushContent.append("运单号:").append("\n").append("\n").append("\n").append("\n").append(waybillNo).append("\n"); pushContent.append("运单号:").append("\n").append("\n").append("\n").append("\n").append(waybillNo).append("\n");
boolean useGoofishWecom = erpGoofishOrderService.hasLinkedGoofishOrder(order.getId())
// 调用企业微信推送接口 || (distributionMark != null && distributionMark.contains("闲鱼"));
if (useGoofishWecom) {
String touserGoofish = getTouserByDistributionMark(distributionMark);
String fullText = "JD物流信息推送\n" + pushContent;
logger.info("闲鱼关联或分销含「闲鱼」:尝试企微闲鱼自建应用 - 订单ID: {}, 分销标识: {}, 接收人: {}",
order.getId(), distributionMark,
StringUtils.hasText(touserGoofish) ? touserGoofish : "(jarvis.wecom.goofish-notify-touser)");
if (wxSendGoofishNotifyClient.pushGoofishAgentText(touserGoofish, fullText)) {
logger.info("企微闲鱼应用推送成功 - 订单ID: {}, 订单号: {}, waybill_no: {}",
order.getId(), order.getOrderId(), waybillNo);
return true;
}
logger.warn("企微闲鱼应用推送失败或未配置 wxSend回退 PDD 通道 - 订单ID: {}", order.getId());
}
// 调用企业微信推送接口PDD 自建应用)
JSONObject pushParam = new JSONObject(); JSONObject pushParam = new JSONObject();
pushParam.put("title", "JD物流信息推送"); pushParam.put("title", "JD物流信息推送");
pushParam.put("text", pushContent.toString()); pushParam.put("text", pushContent.toString());

View File

@@ -5,15 +5,18 @@ import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.ILogisticsService; import com.ruoyi.jarvis.service.ILogisticsService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 物流信息扫描定时任务 * 物流信息扫描定时任务
* 每15分钟扫描一次分销标记为F或PDD的订单最近30天获取物流信息并推送;结束后处理企微分享链 adhoc 队列 * 按配置周期(默认每 20 分钟扫描分销标记为 F/PDD的订单(最近 30 天),拉物流并推送;
* 结束后处理企微分享链 adhoc 队列。
*/ */
@Component @Component
public class LogisticsScanTask { public class LogisticsScanTask {
@@ -25,11 +28,18 @@ public class LogisticsScanTask {
@Resource @Resource
private ILogisticsService logisticsService; 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;
/** /**
* 定时任务每15分钟执行一次与 @Scheduled 中 cron 一致) * 只扫描最近 30 天的订单SQL 固定);周期与单轮上限见 jarvis.server.logistics.scan.*
* 只扫描最近30天的订单
*/ */
@Scheduled(cron = "0 */15 * * * ?") @Scheduled(cron = "${jarvis.server.logistics.scan.cron:0 */20 * * * ?}")
public void scanAndFetchLogistics() { public void scanAndFetchLogistics() {
long t0 = System.currentTimeMillis(); long t0 = System.currentTimeMillis();
int orderCandidates = 0; int orderCandidates = 0;
@@ -47,8 +57,14 @@ public class LogisticsScanTask {
if (orders == null || orders.isEmpty()) { if (orders == null || orders.isEmpty()) {
logger.info("订单扫描:候选 0 条最近30天 F/PDD 有物流链)"); logger.info("订单扫描:候选 0 条最近30天 F/PDD 有物流链)");
} else { } 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(); orderCandidates = orders.size();
logger.info("订单扫描:候选 {} 条最近30天 F/PDD 有物流链)", orderCandidates); logger.info("订单扫描:本轮处理列表 {} 条最近30天 F/PDD 有物流链)", orderCandidates);
for (JDOrder order : orders) { for (JDOrder order : orders) {
try { try {
@@ -57,7 +73,7 @@ public class LogisticsScanTask {
continue; continue;
} }
logger.info("订单扫描:处理中 id={} orderId={} mark={}", logger.debug("订单扫描:处理中 id={} orderId={} mark={}",
order.getId(), order.getOrderId(), order.getDistributionMark()); order.getId(), order.getOrderId(), order.getDistributionMark());
boolean success = logisticsService.fetchLogisticsAndPush(order); boolean success = logisticsService.fetchLogisticsAndPush(order);
@@ -70,7 +86,9 @@ public class LogisticsScanTask {
logger.warn("订单扫描:未成功 id={} orderId={}", order.getId(), order.getOrderId()); logger.warn("订单扫描:未成功 id={} orderId={}", order.getId(), order.getOrderId());
} }
Thread.sleep(500); if (orderDelayMs > 0) {
Thread.sleep(orderDelayMs);
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.error("定时任务被中断", e); logger.error("定时任务被中断", e);
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();

View File

@@ -70,6 +70,37 @@ public class WxSendGoofishNotifyClient {
} }
} }
/**
* 京东物流扫描等全文发往企微「闲鱼」自建应用wxSend qywx.app.goofishAgentId
*
* @param optionalToUser 非空时用与 logistics.push.touser.* 相同的成员 ID逗号/| 亦可);空则用 goofish-notify-touser
* @return HTTP 2xx 为 true未配置密钥或 toUser 为空返回 false
*/
public boolean pushGoofishAgentText(String optionalToUser, String content) {
if (!StringUtils.hasText(wxsendBaseUrl) || !StringUtils.hasText(goofishPushSecret)) {
log.debug("wxSend 闲鱼应用物流推送跳过:未配置 wxsend-base-url 或 goofish-push-secret");
return false;
}
String rawTouser = StringUtils.hasText(optionalToUser) ? optionalToUser : goofishNotifyTouser;
String toUser = buildTouserParam(rawTouser);
if (!StringUtils.hasText(toUser)) {
log.warn("wxSend 闲鱼应用物流推送跳过:无接收人(请配置 jarvis.wecom.goofish-notify-touser 或 logistics.push.touser");
return false;
}
String text = content != null ? content : "";
if (text.length() > CONTENT_MAX) {
text = text.substring(0, CONTENT_MAX - 1) + "";
}
try {
String base = normalizeBase(wxsendBaseUrl);
String url = base + "/wecom/goofish-active-push";
return postJsonReturnsOk(url, toUser, text);
} catch (Exception e) {
log.warn("wxSend goofish-active-push 物流全文失败 err={}", e.toString());
return false;
}
}
private static String buildContent(String orderNo, String eventType, String source, String message) { private static String buildContent(String orderNo, String eventType, String source, String message) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("【闲鱼订单】").append(orderNo != null ? orderNo : "-").append("\n"); sb.append("【闲鱼订单】").append(orderNo != null ? orderNo : "-").append("\n");
@@ -120,6 +151,10 @@ public class WxSendGoofishNotifyClient {
} }
private void postJson(String url, String toUser, String content) throws Exception { private void postJson(String url, String toUser, String content) throws Exception {
postJsonReturnsOk(url, toUser, content);
}
private boolean postJsonReturnsOk(String url, String toUser, String content) throws Exception {
JSONObject body = new JSONObject(); JSONObject body = new JSONObject();
body.put("toUser", toUser); body.put("toUser", toUser);
body.put("content", content); body.put("content", content);
@@ -141,9 +176,10 @@ public class WxSendGoofishNotifyClient {
String resp = readAll(is); String resp = readAll(is);
if (code < 200 || code >= 300) { if (code < 200 || code >= 300) {
log.warn("wxSend goofish-active-push HTTP {} body={}", code, resp); log.warn("wxSend goofish-active-push HTTP {} body={}", code, resp);
} else { return false;
log.debug("wxSend goofish-active-push OK http={} resp={}", code, resp);
} }
log.debug("wxSend goofish-active-push OK http={} resp={}", code, resp);
return true;
} finally { } finally {
if (conn != null) { if (conn != null) {
conn.disconnect(); conn.disconnect();