This commit is contained in:
van
2026-05-09 01:31:01 +08:00
parent 6fb46cc203
commit e78cc67476
6 changed files with 240 additions and 36 deletions

View File

@@ -19,5 +19,10 @@ public interface ErpGoofishOrderMapper {
List<ErpGoofishOrder> selectPendingShip(@Param("statuses") List<Integer> statuses, @Param("limit") int limit); List<ErpGoofishOrder> selectPendingShip(@Param("statuses") List<Integer> statuses, @Param("limit") int limit);
/**
* 按闲鱼买家订单号(与 jd_order.third_party_order_no 对齐时)检索,用于京东运单就绪后补绑发货。
*/
List<ErpGoofishOrder> selectByGoofishOrderNo(@Param("orderNo") String orderNo);
int resetShipForRetry(@Param("id") Long id); int resetShipForRetry(@Param("id") Long id);
} }

View File

@@ -36,6 +36,8 @@ public class GoofishOrderPipeline {
private static final Logger log = LoggerFactory.getLogger(GoofishOrderPipeline.class); private static final Logger log = LoggerFactory.getLogger(GoofishOrderPipeline.class);
private static final String REDIS_WAYBILL_KEY_PREFIX = "logistics:waybill:order:"; private static final String REDIS_WAYBILL_KEY_PREFIX = "logistics:waybill:order:";
/** F-闲鱼等京东单:运单可按第三方单号=闲鱼单号写入 Redis见 LogisticsServiceImpl 镜像写入 */
public static final String REDIS_WAYBILL_GOOFISH_ORDER_PREFIX = "logistics:waybill:goofish:";
@Resource @Resource
private ErpGoofishOrderMapper erpGoofishOrderMapper; private ErpGoofishOrderMapper erpGoofishOrderMapper;
@@ -175,7 +177,7 @@ public class GoofishOrderPipeline {
if (StringUtils.isEmpty(orderNo)) { if (StringUtils.isEmpty(orderNo)) {
return; return;
} }
JDOrder jd = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo); JDOrder jd = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo.trim());
if (jd == null || jd.getId() == null) { if (jd == null || jd.getId() == null) {
return; return;
} }
@@ -415,38 +417,90 @@ public class GoofishOrderPipeline {
} }
public void syncWaybillFromRedis(ErpGoofishOrder row) { public void syncWaybillFromRedis(ErpGoofishOrder row) {
if (row == null || row.getId() == null || row.getJdOrderId() == null) { if (row == null || row.getId() == null) {
return; return;
} }
String key = REDIS_WAYBILL_KEY_PREFIX + row.getJdOrderId(); String wb = null;
String wb = stringRedisTemplate.opsForValue().get(key); if (row.getJdOrderId() != null) {
if (StringUtils.isEmpty(wb)) { String key = REDIS_WAYBILL_KEY_PREFIX + row.getJdOrderId();
wb = stringRedisTemplate.opsForValue().get(key);
}
if (StringUtils.isEmpty(wb) && StringUtils.isNotEmpty(row.getOrderNo())) {
String gk = REDIS_WAYBILL_GOOFISH_ORDER_PREFIX + row.getOrderNo().trim();
wb = stringRedisTemplate.opsForValue().get(gk);
}
if (wb == null || wb.trim().isEmpty()) {
return; return;
} }
if (wb.equals(row.getLocalWaybillNo())) { String nw = wb.trim();
String prevLocal = row.getLocalWaybillNo();
if (nw.equals(prevLocal != null ? prevLocal.trim() : "")) {
return; return;
} }
String prev = row.getLocalWaybillNo();
ErpGoofishOrder patch = new ErpGoofishOrder(); ErpGoofishOrder patch = new ErpGoofishOrder();
patch.setId(row.getId()); patch.setId(row.getId());
patch.setLocalWaybillNo(wb.trim()); patch.setLocalWaybillNo(nw);
patch.setUpdateTime(DateUtils.getNowDate()); patch.setUpdateTime(DateUtils.getNowDate());
erpGoofishOrderMapper.update(patch); erpGoofishOrderMapper.update(patch);
row.setLocalWaybillNo(wb.trim()); row.setLocalWaybillNo(nw);
if (goofishOrderChangeLogger != null) { if (goofishOrderChangeLogger != null) {
goofishOrderChangeLogger.append(row.getId(), row.getAppKey(), row.getOrderNo(), goofishOrderChangeLogger.append(row.getId(), row.getAppKey(), row.getOrderNo(),
GoofishOrderChangeLogger.TYPE_LOGISTICS, GoofishOrderChangeLogger.SOURCE_REDIS_WAYBILL, GoofishOrderChangeLogger.TYPE_LOGISTICS, GoofishOrderChangeLogger.SOURCE_REDIS_WAYBILL,
"本地运单 " + (prev == null || prev.isEmpty() ? "" : prev) + "" + wb.trim()); "本地运单 " + (prevLocal == null || prevLocal.isEmpty() ? "" : prevLocal) + "" + nw);
} }
} }
/**
* 与前端列表「运单号」展示对齐:除 local/detail 列外,从 detail_json 的 data 根取 waybill_no列未同步时列表仍可能有单号
*/
private String extractWaybillFromDetailJson(String detailJson) {
if (StringUtils.isEmpty(detailJson)) {
return null;
}
try {
JSONObject root = JSON.parseObject(detailJson);
if (root == null) {
return null;
}
JSONObject data = root.getJSONObject("data");
if (data == null) {
data = root;
}
String wb = firstNonEmpty(data, "waybill_no", "waybillNo", "mail_no", "mailNo", "express_no", "expressNo");
return StringUtils.isEmpty(wb) ? null : wb.trim();
} catch (Exception e) {
log.debug("从 detail_json 解析运单号失败: {}", e.getMessage());
return null;
}
}
private String resolveWaybillForShip(ErpGoofishOrder row) {
String waybill = row.getLocalWaybillNo();
if (StringUtils.isEmpty(waybill)) {
waybill = row.getDetailWaybillNo();
}
if (StringUtils.isEmpty(waybill)) {
waybill = extractWaybillFromDetailJson(row.getDetailJson());
}
return StringUtils.isEmpty(waybill) ? null : waybill.trim();
}
public void tryAutoShip(ErpGoofishOrder row) { public void tryAutoShip(ErpGoofishOrder row) {
tryAutoShip(row, false);
}
/**
* @param manualRetry true 时表示管理端「重试发货」:放宽「订单状态须在 auto-ship-order-statuses」校验与前端列表展示不一致时常因该条件直接 return
*/
public void tryAutoShip(ErpGoofishOrder row, boolean manualRetry) {
if (row == null || row.getId() == null) { if (row == null || row.getId() == null) {
return; return;
} }
List<Integer> awaiting = resolveAutoShipOrderStatuses(); List<Integer> awaiting = resolveAutoShipOrderStatuses();
if (row.getOrderStatus() == null || !awaiting.contains(row.getOrderStatus())) { if (!manualRetry) {
return; if (row.getOrderStatus() == null || !awaiting.contains(row.getOrderStatus())) {
return;
}
} }
if (row.getRefundStatus() != null && row.getRefundStatus() != 0) { if (row.getRefundStatus() != null && row.getRefundStatus() != 0) {
return; return;
@@ -454,10 +508,7 @@ public class GoofishOrderPipeline {
if (row.getShipStatus() != null && row.getShipStatus() == 1) { if (row.getShipStatus() != null && row.getShipStatus() == 1) {
return; return;
} }
String waybill = row.getLocalWaybillNo(); String waybill = resolveWaybillForShip(row);
if (StringUtils.isEmpty(waybill)) {
waybill = row.getDetailWaybillNo();
}
if (StringUtils.isEmpty(waybill)) { if (StringUtils.isEmpty(waybill)) {
return; return;
} }
@@ -1047,7 +1098,15 @@ public class GoofishOrderPipeline {
if (full == null) { if (full == null) {
continue; continue;
} }
tryLinkJdOrder(full);
syncWaybillFromRedis(full); syncWaybillFromRedis(full);
if ((full.getRefundStatus() == null || full.getRefundStatus() == 0)
&& statuses.contains(full.getOrderStatus())
&& StringUtils.isEmpty(full.getLocalWaybillNo())
&& StringUtils.isEmpty(full.getDetailWaybillNo())) {
refreshDetail(full);
syncWaybillFromRedis(full);
}
tryAutoShip(full); tryAutoShip(full);
n++; n++;
} }

View File

@@ -7,10 +7,12 @@ import com.ruoyi.jarvis.domain.ErpGoofishOrder;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLog; import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLog;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLogQuery; import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLogQuery;
import com.ruoyi.jarvis.domain.ErpOpenConfig; import com.ruoyi.jarvis.domain.ErpOpenConfig;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.dto.GoofishNotifyMessage; import com.ruoyi.jarvis.dto.GoofishNotifyMessage;
import com.ruoyi.jarvis.mapper.ErpGoofishOrderEventLogMapper; import com.ruoyi.jarvis.mapper.ErpGoofishOrderEventLogMapper;
import com.ruoyi.jarvis.mapper.ErpGoofishOrderMapper; import com.ruoyi.jarvis.mapper.ErpGoofishOrderMapper;
import com.ruoyi.jarvis.service.IErpGoofishOrderService; import com.ruoyi.jarvis.service.IErpGoofishOrderService;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.IErpOpenConfigService; import com.ruoyi.jarvis.service.IErpOpenConfigService;
import com.ruoyi.jarvis.service.goofish.GoofishNotifyAsyncFacade; import com.ruoyi.jarvis.service.goofish.GoofishNotifyAsyncFacade;
import com.ruoyi.jarvis.service.goofish.GoofishOrderChangeLogger; import com.ruoyi.jarvis.service.goofish.GoofishOrderChangeLogger;
@@ -20,13 +22,20 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ruoyi.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
@Service @Service
public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService { public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
private static final Logger log = LoggerFactory.getLogger(ErpGoofishOrderServiceImpl.class);
@Autowired @Autowired
private ObjectProvider<RocketMQTemplate> rocketMQTemplate; private ObjectProvider<RocketMQTemplate> rocketMQTemplate;
@@ -51,6 +60,9 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
@Resource @Resource
private GoofishOrderChangeLogger goofishOrderChangeLogger; private GoofishOrderChangeLogger goofishOrderChangeLogger;
@Resource
private IJDOrderService jdOrderService;
@Override @Override
public void publishOrProcessNotify(String appid, Long timestamp, JSONObject body) { public void publishOrProcessNotify(String appid, Long timestamp, JSONObject body) {
RocketMQTemplate mq = rocketMQTemplate.getIfAvailable(); RocketMQTemplate mq = rocketMQTemplate.getIfAvailable();
@@ -135,8 +147,10 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
erpGoofishOrderMapper.resetShipForRetry(id); erpGoofishOrderMapper.resetShipForRetry(id);
ErpGoofishOrder row = erpGoofishOrderMapper.selectById(id); ErpGoofishOrder row = erpGoofishOrderMapper.selectById(id);
if (row != null) { if (row != null) {
goofishOrderPipeline.refreshDetail(row);
goofishOrderPipeline.tryLinkJdOrder(row);
goofishOrderPipeline.syncWaybillFromRedis(row); goofishOrderPipeline.syncWaybillFromRedis(row);
goofishOrderPipeline.tryAutoShip(row); goofishOrderPipeline.tryAutoShip(row, true);
} }
} }
@@ -155,20 +169,72 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
if (jdOrderId == null) { if (jdOrderId == null) {
return; return;
} }
ErpGoofishOrder query = new ErpGoofishOrder(); JDOrder jd = jdOrderService.selectJDOrderById(jdOrderId);
query.setJdOrderId(jdOrderId); LinkedHashSet<Long> processedGoofishPk = new LinkedHashSet<>();
List<ErpGoofishOrder> list = erpGoofishOrderMapper.selectList(query);
if (list == null || list.isEmpty()) { ErpGoofishOrder queryByJd = new ErpGoofishOrder();
queryByJd.setJdOrderId(jdOrderId);
List<ErpGoofishOrder> linked = erpGoofishOrderMapper.selectList(queryByJd);
if (linked != null) {
for (ErpGoofishOrder ref : linked) {
processGoofishWaybillSyncAfterJdReady(ref.getId(), processedGoofishPk);
}
}
if (jd != null && org.springframework.util.StringUtils.hasText(jd.getThirdPartyOrderNo())
&& isFxianyuDistributionMark(jd.getDistributionMark())) {
List<ErpGoofishOrder> byNo =
erpGoofishOrderMapper.selectByGoofishOrderNo(jd.getThirdPartyOrderNo().trim());
if (byNo != null) {
for (ErpGoofishOrder ref : byNo) {
ErpGoofishOrder full = erpGoofishOrderMapper.selectById(ref.getId());
if (full == null) {
continue;
}
if (full.getJdOrderId() != null && !full.getJdOrderId().equals(jdOrderId)) {
log.warn(
"闲鱼单 {} 已关联本地 jd_order_id={},与本次京东单行主键 {} 不一致,跳过补绑",
full.getOrderNo(), full.getJdOrderId(), jdOrderId);
continue;
}
if (full.getJdOrderId() == null) {
attachGoofishToJdOrder(full.getId(), jdOrderId);
}
processGoofishWaybillSyncAfterJdReady(ref.getId(), processedGoofishPk);
}
}
}
}
/** 单次:读库、同步 Redis 运单(含镜像 key、尽力自动发货 */
private void processGoofishWaybillSyncAfterJdReady(Long erpGoofishPk,
LinkedHashSet<Long> processedGoofishPk) {
if (erpGoofishPk == null || processedGoofishPk.contains(erpGoofishPk)) {
return; return;
} }
for (ErpGoofishOrder ref : list) { ErpGoofishOrder full = erpGoofishOrderMapper.selectById(erpGoofishPk);
ErpGoofishOrder full = erpGoofishOrderMapper.selectById(ref.getId()); if (full == null) {
if (full == null) { return;
continue;
}
goofishOrderPipeline.syncWaybillFromRedis(full);
goofishOrderPipeline.tryAutoShip(full);
} }
goofishOrderPipeline.syncWaybillFromRedis(full);
goofishOrderPipeline.tryAutoShip(full);
processedGoofishPk.add(erpGoofishPk);
}
private void attachGoofishToJdOrder(long erpGoofishPk, long jdOrderDbId) {
ErpGoofishOrder p = new ErpGoofishOrder();
p.setId(erpGoofishPk);
p.setJdOrderId(jdOrderDbId);
p.setUpdateTime(DateUtils.getNowDate());
erpGoofishOrderMapper.update(p);
}
private static boolean isFxianyuDistributionMark(String distributionMark) {
if (!org.springframework.util.StringUtils.hasText(distributionMark)) {
return false;
}
String m = distributionMark.trim();
return m.startsWith("F") || m.contains("\u95f2\u9c7c");
} }
@Override @Override
@@ -187,10 +253,9 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
if (jdOrderId == null || goofishOrderChangeLogger == null) { if (jdOrderId == null || goofishOrderChangeLogger == null) {
return; return;
} }
ErpGoofishOrder query = new ErpGoofishOrder(); JDOrder jd = jdOrderService.selectJDOrderById(jdOrderId);
query.setJdOrderId(jdOrderId); LinkedHashSet<Long> ids = resolveGoofishRowIdsForJdLogisticsEvent(jdOrderId, jd);
List<ErpGoofishOrder> list = erpGoofishOrderMapper.selectList(query); if (ids.isEmpty()) {
if (list == null || list.isEmpty()) {
return; return;
} }
String wb = waybillNo != null ? waybillNo.trim() : ""; String wb = waybillNo != null ? waybillNo.trim() : "";
@@ -199,8 +264,9 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
if (msg.length() > 1000) { if (msg.length() > 1000) {
msg = msg.substring(0, 999) + ""; msg = msg.substring(0, 999) + "";
} }
for (ErpGoofishOrder row : list) { for (Long oid : ids) {
if (row.getId() == null) { ErpGoofishOrder row = erpGoofishOrderMapper.selectById(oid);
if (row == null || row.getId() == null) {
continue; continue;
} }
goofishOrderChangeLogger.append(row.getId(), row.getAppKey(), row.getOrderNo(), goofishOrderChangeLogger.append(row.getId(), row.getAppKey(), row.getOrderNo(),
@@ -208,6 +274,33 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
} }
} }
private LinkedHashSet<Long> resolveGoofishRowIdsForJdLogisticsEvent(Long jdOrderId, JDOrder jd) {
LinkedHashSet<Long> ids = new LinkedHashSet<>();
ErpGoofishOrder q = new ErpGoofishOrder();
q.setJdOrderId(jdOrderId);
List<ErpGoofishOrder> linked = erpGoofishOrderMapper.selectList(q);
if (linked != null) {
for (ErpGoofishOrder r : linked) {
if (r.getId() != null) {
ids.add(r.getId());
}
}
}
if (jd != null && org.springframework.util.StringUtils.hasText(jd.getThirdPartyOrderNo())
&& isFxianyuDistributionMark(jd.getDistributionMark())) {
List<ErpGoofishOrder> byNo =
erpGoofishOrderMapper.selectByGoofishOrderNo(jd.getThirdPartyOrderNo().trim());
if (byNo != null) {
for (ErpGoofishOrder r : byNo) {
if (r.getId() != null) {
ids.add(r.getId());
}
}
}
}
return ids;
}
@Override @Override
public List<ErpGoofishOrderEventLog> listEventLogsByOrderId(Long orderId) { public List<ErpGoofishOrderEventLog> listEventLogsByOrderId(Long orderId) {
if (orderId == null) { if (orderId == 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.service.goofish.GoofishOrderPipeline;
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;
@@ -283,6 +284,7 @@ public class LogisticsServiceImpl implements ILogisticsService {
*/ */
private void safeNotifyGoofishShip(Long jdOrderId, String waybillNo, String traceSummary) { private void safeNotifyGoofishShip(Long jdOrderId, String waybillNo, String traceSummary) {
try { try {
mirrorFxianyuGoofishSecondaryWaybillRedis(jdOrderId, waybillNo);
erpGoofishOrderService.traceJdLogisticsPushForGoofish(jdOrderId, waybillNo, traceSummary); erpGoofishOrderService.traceJdLogisticsPushForGoofish(jdOrderId, waybillNo, traceSummary);
erpGoofishOrderService.notifyJdWaybillReady(jdOrderId); erpGoofishOrderService.notifyJdWaybillReady(jdOrderId);
} catch (Exception e) { } catch (Exception e) {
@@ -290,6 +292,38 @@ public class LogisticsServiceImpl implements ILogisticsService {
} }
} }
/**
* F-闲鱼等:将运单镜像到 logistics:waybill:goofish:${第三方单号},即使 erp_goofish_order.jd_order_id 尚未关联也能被同步。
*/
private void mirrorFxianyuGoofishSecondaryWaybillRedis(Long jdOrderDbId, String waybillNo) {
if (jdOrderDbId == null || !StringUtils.hasText(waybillNo)) {
return;
}
JDOrder jd = jdOrderService.selectJDOrderById(jdOrderDbId);
if (jd == null) {
return;
}
if (!isFxianyuJdDistributionMark(jd.getDistributionMark())) {
return;
}
String tp = jd.getThirdPartyOrderNo();
if (!StringUtils.hasText(tp)) {
return;
}
stringRedisTemplate.opsForValue().set(
GoofishOrderPipeline.REDIS_WAYBILL_GOOFISH_ORDER_PREFIX + tp.trim(),
waybillNo.trim(),
30, TimeUnit.DAYS);
}
private static boolean isFxianyuJdDistributionMark(String distributionMark) {
if (!StringUtils.hasText(distributionMark)) {
return false;
}
String m = distributionMark.trim();
return m.startsWith("F") || m.contains("\u95f2\u9c7c");
}
@Override @Override
public boolean fetchLogisticsAndPush(JDOrder order) { public boolean fetchLogisticsAndPush(JDOrder order) {
if (order == null || order.getId() == null) { if (order == null || order.getId() == null) {

View File

@@ -46,6 +46,14 @@ public class GoofishScheduledTasks {
} }
if (n > 0) { if (n > 0) {
log.info("闲管家定时拉单本轮处理约 {} 条子订单项", n); log.info("闲管家定时拉单本轮处理约 {} 条子订单项", n);
try {
int synced = erpGoofishOrderService.syncWaybillAndTryShipBatch(goofishProperties.getAutoShipBatchSize());
if (synced > 0) {
log.info("拉单后轮询:运单同步/自动发货扫描处理 {} 条", synced);
}
} catch (Exception syncEx) {
log.warn("拉单后轮询运单同步/发货扫描异常 {}", syncEx.getMessage());
}
} }
} }

View File

@@ -100,8 +100,8 @@
<select id="selectPendingShip" resultMap="ErpGoofishOrderResult"> <select id="selectPendingShip" resultMap="ErpGoofishOrderResult">
<include refid="selectJoinVo"/> <include refid="selectJoinVo"/>
where e.jd_order_id is not null where (e.ship_status is null or e.ship_status != 1)
and (e.ship_status is null or e.ship_status != 1) and (e.refund_status is null or e.refund_status = 0)
<if test="statuses != null and statuses.size() &gt; 0"> <if test="statuses != null and statuses.size() &gt; 0">
and e.order_status in and e.order_status in
<foreach collection="statuses" item="st" open="(" separator="," close=")"> <foreach collection="statuses" item="st" open="(" separator="," close=")">
@@ -112,6 +112,11 @@
limit #{limit} limit #{limit}
</select> </select>
<select id="selectByGoofishOrderNo" resultMap="ErpGoofishOrderResult">
<include refid="selectJoinVo"/>
where e.order_no = #{orderNo}
</select>
<insert id="insert" parameterType="com.ruoyi.jarvis.domain.ErpGoofishOrder" useGeneratedKeys="true" keyProperty="id"> <insert id="insert" parameterType="com.ruoyi.jarvis.domain.ErpGoofishOrder" useGeneratedKeys="true" keyProperty="id">
insert into erp_goofish_order insert into erp_goofish_order
(app_key, seller_id, user_name, order_no, order_type, order_status, refund_status, modify_time, product_id, item_id, (app_key, seller_id, user_name, order_no, order_type, order_status, refund_status, modify_time, product_id, item_id,