This commit is contained in:
van
2026-05-16 02:27:52 +08:00
parent 78610826a3
commit 810625e0a9
5 changed files with 208 additions and 24 deletions

View File

@@ -9,6 +9,7 @@ import com.ruoyi.jarvis.config.JarvisGoofishProperties;
import com.ruoyi.jarvis.domain.ErpGoofishOrder;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLog;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLogQuery;
import com.ruoyi.jarvis.dto.GoofishShipPreviewVo;
import com.ruoyi.jarvis.service.IErpGoofishOrderService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -47,6 +48,13 @@ public class ErpGoofishOrderController extends BaseController {
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('jarvis:erpGoofishOrder:query')")
@GetMapping("/shipPreview/{id}")
public AjaxResult shipPreview(@PathVariable Long id) {
GoofishShipPreviewVo vo = erpGoofishOrderService.shipPreview(id);
return AjaxResult.success(vo);
}
@PreAuthorize("@ss.hasPermi('jarvis:erpGoofishOrder:query')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {

View File

@@ -0,0 +1,50 @@
package com.ruoyi.jarvis.dto;
import lombok.Data;
/**
* 闲管家订单发货预览:闲鱼订单侧收件信息 vs 拟提交开放平台发货参数(可含京东兜底)
*/
@Data
public class GoofishShipPreviewVo {
private String orderNo;
/** 管家 goods.title表或详情 */
private String goodsTitle;
/** 关联京东单的型号 */
private String jdModelNumber;
/** 关联京东单的整段收件地址快照 */
private String jdAddressFull;
/** 仅从闲鱼开放平台落库的收件人姓名 */
private String goofishOrderReceiverName;
private String goofishOrderReceiverMobile;
/**
* 闲鱼订单侧完整收件串:省市区镇 + detail与发货流水线中 receiverFieldsToShipParts 一致,不含京东)
*/
private String goofishOrderFullAddress;
/** 拟调用发货接口使用的收件人(可能由京东兜底补姓名) */
private String shipReceiverName;
private String shipReceiverMobile;
/** 拟提交的发货详情地址字符串 */
private String shipFullAddress;
/**
* 发货收件地址是否与「闲鱼订单侧」归一化后一致(订单侧任一字段为空时为 null
*/
private Boolean receiverAddressMatchesGoofish;
/** 是否与京东整段地址归一化后一致(无视京东或未维护京东地址时为 null */
private Boolean receiverAddressMatchesJd;
/** 简短说明比对结论或数据来源 */
private String compareHint;
}

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.jarvis.domain.ErpGoofishOrder;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLog;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLogQuery;
import com.ruoyi.jarvis.dto.GoofishShipPreviewVo;
import java.util.List;
@@ -31,6 +32,11 @@ public interface IErpGoofishOrderService {
void refreshDetail(Long id);
/**
* 发货预览:闲鱼订单收件串 vs 即将提交开放平台地址(可与京东快照比对)。
*/
GoofishShipPreviewVo shipPreview(Long id);
void retryShip(Long id);
int syncWaybillAndTryShipBatch(int limit);

View File

@@ -14,6 +14,7 @@ import com.ruoyi.jarvis.config.JarvisGoofishProperties;
import com.ruoyi.jarvis.domain.ErpGoofishOrder;
import com.ruoyi.jarvis.domain.ErpOpenConfig;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.dto.GoofishShipPreviewVo;
import com.ruoyi.jarvis.mapper.ErpGoofishOrderMapper;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.IErpOpenConfigService;
@@ -310,6 +311,10 @@ public class GoofishOrderPipeline {
patch.setReceiverAddress(recv.getString("address"));
}
}
// 开放平台常拆分为省市区 + 明文 detail明细列为空时用行政区拼接兜底便于列表与发货解析
if (StringUtils.isEmpty(patch.getReceiverAddress()) && StringUtils.isNotEmpty(regionJoined)) {
patch.setReceiverAddress(regionJoined.trim());
}
Integer os = firstInt(data, "order_status", "orderStatus");
if (os != null) {
patch.setOrderStatus(os);
@@ -527,25 +532,7 @@ public class GoofishOrderPipeline {
row.getAppKey(), row.getOrderNo());
return;
}
ShipAddressParts addr = parseShipAddress(row.getDetailJson());
if (addr == null) {
addr = new ShipAddressParts();
}
enrichShipAddressFromJd(row, addr);
if (StringUtils.isEmpty(addr.shipAddress)) {
ShipAddressParts fromRow = receiverFieldsToShipParts(row);
if (fromRow != null) {
if (StringUtils.isEmpty(addr.shipName)) {
addr.shipName = fromRow.shipName;
}
if (StringUtils.isEmpty(addr.shipMobile)) {
addr.shipMobile = fromRow.shipMobile;
}
if (StringUtils.isEmpty(addr.shipAddress)) {
addr.shipAddress = fromRow.shipAddress;
}
}
}
ShipAddressParts addr = resolveShippingAddressParts(row);
if (StringUtils.isEmpty(addr.shipName) || StringUtils.isEmpty(addr.shipMobile)
|| StringUtils.isEmpty(addr.shipAddress)) {
patchShipError(row, "缺少收货人/手机/地址:详情无字段时请关联京东单并维护地址,或等平台返回收货字段");
@@ -704,9 +691,6 @@ public class GoofishOrderPipeline {
p.shipCityName = firstNonEmpty(data, "ship_city_name", "receiver_city", "city", "city_name");
p.shipAreaName = firstNonEmpty(data, "ship_area_name", "receiver_area", "area", "district", "area_name");
String shipTown = firstNonEmpty(data, "town_name", "ship_town_name", "receiver_town");
if (StringUtils.isNotEmpty(shipTown) && StringUtils.isEmpty(p.shipAddress)) {
p.shipAddress = shipTown;
}
JSONObject recv = data.getJSONObject("receiver");
if (recv != null) {
if (StringUtils.isEmpty(p.shipName)) {
@@ -722,9 +706,135 @@ public class GoofishOrderPipeline {
p.shipAddress = recv.getString("address");
}
}
mergeRegionIntoShipAddress(p, shipTown);
return p;
}
/**
* 与列表/落库拆分字段对齐:开放平台若只给省市区镇 + detail拼成单行供发货调用。
* 已在 detail 中包含省名开头或与整段行政区一致时不重复前缀。
*/
private static void mergeRegionIntoShipAddress(ShipAddressParts p, String shipTownName) {
if (p == null) {
return;
}
String regionLine = joinWithSpace(p.shipProvName, p.shipCityName, p.shipAreaName, shipTownName);
if (StringUtils.isEmpty(regionLine)) {
return;
}
String detail = p.shipAddress;
if (StringUtils.isEmpty(detail)) {
p.shipAddress = regionLine;
return;
}
detail = detail.trim();
String prov = StringUtils.isNotEmpty(p.shipProvName) ? p.shipProvName.trim() : "";
if (StringUtils.isNotEmpty(prov) && detail.startsWith(prov)) {
return;
}
if (detail.length() >= regionLine.length() && detail.startsWith(regionLine.trim())) {
return;
}
p.shipAddress = regionLine + " " + detail;
}
/**
* 与 {@link #tryAutoShip(ErpGoofishOrder, boolean)} 对齐detail_json → 京东兜底 → 列表/库字段补全。
*/
private ShipAddressParts resolveShippingAddressParts(ErpGoofishOrder row) {
ShipAddressParts addr = parseShipAddress(row != null ? row.getDetailJson() : null);
if (addr == null) {
addr = new ShipAddressParts();
}
enrichShipAddressFromJd(row, addr);
if (StringUtils.isEmpty(addr.shipAddress)) {
ShipAddressParts fromRow = receiverFieldsToShipParts(row);
if (fromRow != null) {
if (StringUtils.isEmpty(addr.shipName)) {
addr.shipName = fromRow.shipName;
}
if (StringUtils.isEmpty(addr.shipMobile)) {
addr.shipMobile = fromRow.shipMobile;
}
if (StringUtils.isEmpty(addr.shipAddress)) {
addr.shipAddress = fromRow.shipAddress;
}
}
}
return addr;
}
/**
* 管理端发货预览:闲鱼收件串 vs 实际将提交开放平台地址(可对齐运单回填前自检)。
*/
public GoofishShipPreviewVo buildShipPreview(ErpGoofishOrder row) {
GoofishShipPreviewVo vo = new GoofishShipPreviewVo();
if (row == null) {
vo.setCompareHint("订单不存在");
return vo;
}
vo.setOrderNo(row.getOrderNo());
vo.setGoodsTitle(row.getGoodsTitle());
vo.setJdModelNumber(row.getJdModelNumber());
vo.setJdAddressFull(row.getJdAddress());
ShipAddressParts orderSide = receiverFieldsToShipParts(row);
if (orderSide != null) {
vo.setGoofishOrderReceiverName(orderSide.shipName);
vo.setGoofishOrderReceiverMobile(orderSide.shipMobile);
vo.setGoofishOrderFullAddress(orderSide.shipAddress != null ? orderSide.shipAddress.trim() : "");
}
ShipAddressParts ship = resolveShippingAddressParts(row);
vo.setShipReceiverName(ship.shipName);
vo.setShipReceiverMobile(ship.shipMobile);
vo.setShipFullAddress(StringUtils.isNotEmpty(ship.shipAddress) ? ship.shipAddress.trim() : "");
String nGoofishAddr = normalizeAddrComparable(vo.getGoofishOrderFullAddress());
String nShipAddr = normalizeAddrComparable(vo.getShipFullAddress());
boolean goofishHasAddr = StringUtils.isNotEmpty(nGoofishAddr);
boolean goofishHasContact = StringUtils.isNotEmpty(vo.getGoofishOrderReceiverName())
|| StringUtils.isNotEmpty(vo.getGoofishOrderReceiverMobile());
vo.setReceiverAddressMatchesGoofish(goofishHasAddr ? Objects.equals(nGoofishAddr, nShipAddr) : null);
String nJd = normalizeAddrComparable(vo.getJdAddressFull());
boolean jdHasAddr = StringUtils.isNotEmpty(nJd);
vo.setReceiverAddressMatchesJd(jdHasAddr ? Objects.equals(nJd, nShipAddr) : null);
StringBuilder hint = new StringBuilder();
if (!goofishHasAddr && StringUtils.isNotEmpty(vo.getShipFullAddress())) {
hint.append("闲鱼侧未合并到收件地址文本,发货参数可能仅靠详情解析或京东补全");
}
if (jdHasAddr && Boolean.FALSE.equals(vo.getReceiverAddressMatchesJd())
&& StringUtils.isNotEmpty(vo.getShipFullAddress())) {
if (hint.length() > 0) {
hint.append('');
}
hint.append("与京东单整段地址不完全一致(请核对)");
}
if (Boolean.FALSE.equals(vo.getReceiverAddressMatchesGoofish())
&& goofishHasAddr && StringUtils.isNotEmpty(vo.getShipFullAddress())) {
if (hint.length() > 0) {
hint.append('');
}
hint.append("与闲鱼订单收件串不完全一致(可能已用京东补全或未刷新详情)");
}
if (hint.length() == 0) {
hint.append(goofishHasContact || goofishHasAddr ? "可对照下方两项文本核对收件信息" : "暂无闲鱼收件信息,请先刷新详情或关联京东单");
}
vo.setCompareHint(hint.toString());
return vo;
}
private static String normalizeAddrComparable(String addr) {
if (addr == null) {
return "";
}
return addr.trim().replaceAll("\\s+", " ");
}
/** 开放平台 goods.images 首元素,或其它常见单图字段 */
private static String firstGoodsCoverUrl(JSONObject goods) {
if (goods == null) {

View File

@@ -6,9 +6,8 @@ import com.ruoyi.jarvis.config.JarvisGoofishProperties;
import com.ruoyi.jarvis.domain.ErpGoofishOrder;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLog;
import com.ruoyi.jarvis.domain.ErpGoofishOrderEventLogQuery;
import com.ruoyi.jarvis.domain.ErpOpenConfig;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.dto.GoofishNotifyMessage;
import com.ruoyi.jarvis.dto.GoofishShipPreviewVo;
import com.ruoyi.jarvis.mapper.ErpGoofishOrderEventLogMapper;
import com.ruoyi.jarvis.mapper.ErpGoofishOrderMapper;
import com.ruoyi.jarvis.service.IErpGoofishOrderService;
@@ -142,6 +141,17 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
}
}
@Override
public GoofishShipPreviewVo shipPreview(Long id) {
ErpGoofishOrder row = erpGoofishOrderMapper.selectById(id);
if (row == null) {
GoofishShipPreviewVo empty = new GoofishShipPreviewVo();
empty.setCompareHint("订单不存在");
return empty;
}
return goofishOrderPipeline.buildShipPreview(row);
}
@Override
public void retryShip(Long id) {
erpGoofishOrderMapper.resetShipForRetry(id);