This commit is contained in:
van
2026-04-09 01:11:23 +08:00
parent 16bcd45c63
commit a2c4589046
10 changed files with 739 additions and 31 deletions

View File

@@ -64,6 +64,30 @@ public class ErpGoofishOrderController extends BaseController {
return AjaxResult.success(data);
}
@PreAuthorize("@ss.hasPermi('jarvis:erpGoofishOrder:edit')")
@Log(title = "闲管家历史全量拉单", businessType = BusinessType.OTHER)
@PostMapping("/pull/{appKey}/full")
public AjaxResult pullOneFull(@PathVariable String appKey) {
int n = erpGoofishOrderService.pullOrdersForAppKeyFullHistory(appKey);
Map<String, Object> data = new LinkedHashMap<>();
data.put("processedItems", n);
data.put("pullFullHistoryDays", goofishProperties.getPullFullHistoryDays());
data.put("pullTimeChunkSeconds", goofishProperties.getPullTimeChunkSeconds());
return AjaxResult.success(data);
}
@PreAuthorize("@ss.hasPermi('jarvis:erpGoofishOrder:edit')")
@Log(title = "闲管家全账号历史全量拉单", businessType = BusinessType.OTHER)
@PostMapping("/pullAll/full")
public AjaxResult pullAllFull() {
int n = erpGoofishOrderService.pullAllEnabledFullHistory();
Map<String, Object> data = new LinkedHashMap<>();
data.put("processedItems", n);
data.put("pullFullHistoryDays", goofishProperties.getPullFullHistoryDays());
data.put("pullTimeChunkSeconds", goofishProperties.getPullTimeChunkSeconds());
return AjaxResult.success(data);
}
@PreAuthorize("@ss.hasPermi('jarvis:erpGoofishOrder:edit')")
@Log(title = "闲管家订单详情刷新", businessType = BusinessType.UPDATE)
@PostMapping("/refreshDetail/{id}")

View File

@@ -268,7 +268,14 @@ jarvis:
pull-lookback-hours: 72
pull-cron: "0 0/15 * * * ?"
auto-ship-cron: "0 2/10 * * * ?"
pull-max-pages-per-shop: 30
# 订单列表:每页条数(最大 100
pull-page-size: 100
# 每授权单次最大页数(最大 100与 page_size 乘积勿超 10000
pull-max-pages-per-shop: 100
# 全量拉单按 update_time 分段(秒),默认 7 天
pull-time-chunk-seconds: 604800
# 全量拉单起点:距今多少天(默认约 3 年)
pull-full-history-days: 1095
auto-ship-batch-size: 20

View File

@@ -33,6 +33,21 @@ CREATE TABLE IF NOT EXISTS erp_goofish_order (
modify_time bigint(20) DEFAULT NULL COMMENT '订单更新时间(秒)',
product_id bigint(20) DEFAULT NULL COMMENT '管家商品ID',
item_id bigint(20) DEFAULT NULL COMMENT '闲鱼商品ID',
goods_title varchar(512) DEFAULT NULL COMMENT '商品标题(详情 goods.title)',
goods_image_url varchar(1024) DEFAULT NULL COMMENT '商品主图 URL(goods.images 首图)',
buyer_nick varchar(256) DEFAULT NULL COMMENT '买家昵称(详情 buyer_nick)',
pay_amount bigint(20) DEFAULT NULL COMMENT '实付金额(分) pay_amount',
detail_waybill_no varchar(128) DEFAULT NULL COMMENT '闲管家详情回传运单号 waybill_no',
detail_express_code varchar(64) DEFAULT NULL COMMENT '详情快递编码 express_code',
detail_express_name varchar(128) DEFAULT NULL COMMENT '详情快递名称 express_name',
receiver_name varchar(128) DEFAULT NULL COMMENT '收货人(详情有则落库)',
receiver_mobile varchar(64) DEFAULT NULL COMMENT '收货手机',
receiver_address varchar(1000) DEFAULT NULL COMMENT '收货详细地址(address)',
receiver_region varchar(256) DEFAULT NULL COMMENT '省市区街道等拼接展示',
recv_prov_name varchar(64) DEFAULT NULL COMMENT 'prov_name',
recv_city_name varchar(64) DEFAULT NULL COMMENT 'city_name',
recv_area_name varchar(64) DEFAULT NULL COMMENT 'area_name',
recv_town_name varchar(128) DEFAULT NULL COMMENT 'town_name',
detail_json longtext COMMENT '订单详情接口全量 JSON',
last_notify_json longtext COMMENT '最近一次推送原文 JSON',
jd_order_id bigint(20) DEFAULT NULL COMMENT '关联 jd_order.id第三方单号=闲鱼单号)',
@@ -61,3 +76,22 @@ CREATE TABLE IF NOT EXISTS erp_goofish_order (
-- 按钮权限示例:
-- jarvis:erpOpenConfig:list,query,add,edit,remove
-- jarvis:erpGoofishOrder:list,query,edit
-- —— 已建表升级:详情摘要字段(若列已存在会报错,可逐条执行并忽略)——
-- ALTER TABLE erp_goofish_order ADD COLUMN goods_title varchar(512) NULL COMMENT '商品标题' AFTER item_id;
-- ALTER TABLE erp_goofish_order ADD COLUMN goods_image_url varchar(1024) NULL COMMENT '商品主图URL' AFTER goods_title;
-- ALTER TABLE erp_goofish_order ADD COLUMN buyer_nick varchar(256) NULL COMMENT '买家昵称' AFTER goods_image_url;
-- ALTER TABLE erp_goofish_order ADD COLUMN pay_amount bigint(20) NULL COMMENT '实付金额(分)' AFTER buyer_nick;
-- ALTER TABLE erp_goofish_order ADD COLUMN detail_waybill_no varchar(128) NULL COMMENT '详情运单号' AFTER pay_amount;
-- ALTER TABLE erp_goofish_order ADD COLUMN detail_express_code varchar(64) NULL COMMENT '详情快递编码' AFTER detail_waybill_no;
-- ALTER TABLE erp_goofish_order ADD COLUMN detail_express_name varchar(128) NULL COMMENT '详情快递名称' AFTER detail_express_code;
-- ALTER TABLE erp_goofish_order ADD COLUMN receiver_name varchar(128) NULL COMMENT '收货人' AFTER detail_express_name;
-- ALTER TABLE erp_goofish_order ADD COLUMN receiver_mobile varchar(64) NULL COMMENT '收货手机' AFTER receiver_name;
-- ALTER TABLE erp_goofish_order ADD COLUMN receiver_address varchar(1000) NULL COMMENT '收货地址' AFTER receiver_mobile;
-- ALTER TABLE erp_goofish_order ADD COLUMN receiver_region varchar(256) NULL COMMENT '省市区' AFTER receiver_address;
-- ALTER TABLE erp_goofish_order ADD COLUMN recv_prov_name varchar(64) NULL COMMENT 'prov_name' AFTER receiver_region;
-- ALTER TABLE erp_goofish_order ADD COLUMN recv_city_name varchar(64) NULL COMMENT 'city_name' AFTER recv_prov_name;
-- ALTER TABLE erp_goofish_order ADD COLUMN recv_area_name varchar(64) NULL COMMENT 'area_name' AFTER recv_city_name;
-- ALTER TABLE erp_goofish_order ADD COLUMN recv_town_name varchar(128) NULL COMMENT 'town_name' AFTER recv_area_name;
-- 已建库一键升级(可重复执行、自动判存):请使用同目录 erp_goofish_upgrade.sql

View File

@@ -0,0 +1,195 @@
-- =============================================================================
-- 闲管家 ERPerp_open_config / erp_goofish_order 一键升级脚本
-- =============================================================================
-- 用法:
-- 1. 先备份数据库;连接目标库后执行(或在文件开头加 USE your_database;
-- 2. 可重复执行:已存在的列/索引会自动跳过
-- 3. ADD COLUMN 一律不指定 AFTER避免旧表缺中间列时升级失败新列落在表尾不影响业务
-- 环境MySQL 5.7+ / 8.xMariaDB 未逐项验证)
-- =============================================================================
SET @schema := DATABASE();
-- -----------------------------------------------------------------------------
-- 1) 表不存在时创建完整结构(与 erp_goofish_init.sql 一致)
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS erp_open_config (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
app_key varchar(64) NOT NULL COMMENT '开放平台 AppKey(appid)',
app_secret varchar(128) NOT NULL COMMENT '开放平台 AppSecret',
xy_user_name varchar(128) DEFAULT NULL COMMENT '默认闲鱼会员名(展示)',
express_code varchar(64) DEFAULT NULL COMMENT '发货用快递公司编码(日日顺物流在官方列表中为 rrs',
express_name varchar(64) DEFAULT NULL COMMENT '快递公司名称(展示)',
status char(1) NOT NULL DEFAULT '0' COMMENT '0正常 1停用',
order_num int(11) NOT NULL DEFAULT 0 COMMENT '排序(小优先)',
create_by varchar(64) DEFAULT '',
create_time datetime DEFAULT NULL,
update_by varchar(64) DEFAULT '',
update_time datetime DEFAULT NULL,
remark varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (id),
UNIQUE KEY uk_app_key (app_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='闲管家开放平台应用配置';
CREATE TABLE IF NOT EXISTS erp_goofish_order (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
app_key varchar(64) NOT NULL COMMENT 'AppKey',
seller_id bigint(20) DEFAULT NULL COMMENT '商家ID',
user_name varchar(128) DEFAULT NULL COMMENT '闲鱼会员名',
order_no varchar(64) NOT NULL COMMENT '闲鱼订单号',
order_type int(11) DEFAULT NULL COMMENT '订单类型',
order_status int(11) DEFAULT NULL COMMENT '订单状态(推送/列表)',
refund_status int(11) DEFAULT NULL COMMENT '退款状态',
modify_time bigint(20) DEFAULT NULL COMMENT '订单更新时间(秒)',
product_id bigint(20) DEFAULT NULL COMMENT '管家商品ID',
item_id bigint(20) DEFAULT NULL COMMENT '闲鱼商品ID',
goods_title varchar(512) DEFAULT NULL COMMENT '商品标题(详情 goods.title)',
goods_image_url varchar(1024) DEFAULT NULL COMMENT '商品主图 URL(goods.images 首图)',
buyer_nick varchar(256) DEFAULT NULL COMMENT '买家昵称(详情 buyer_nick)',
pay_amount bigint(20) DEFAULT NULL COMMENT '实付金额(分) pay_amount',
detail_waybill_no varchar(128) DEFAULT NULL COMMENT '闲管家详情回传运单号 waybill_no',
detail_express_code varchar(64) DEFAULT NULL COMMENT '详情快递编码 express_code',
detail_express_name varchar(128) DEFAULT NULL COMMENT '详情快递名称 express_name',
receiver_name varchar(128) DEFAULT NULL COMMENT '收货人(详情有则落库)',
receiver_mobile varchar(64) DEFAULT NULL COMMENT '收货手机',
receiver_address varchar(1000) DEFAULT NULL COMMENT '收货详细地址(address)',
receiver_region varchar(256) DEFAULT NULL COMMENT '省市区街道等拼接展示',
recv_prov_name varchar(64) DEFAULT NULL COMMENT 'prov_name',
recv_city_name varchar(64) DEFAULT NULL COMMENT 'city_name',
recv_area_name varchar(64) DEFAULT NULL COMMENT 'area_name',
recv_town_name varchar(128) DEFAULT NULL COMMENT 'town_name',
detail_json longtext COMMENT '订单详情接口全量 JSON',
last_notify_json longtext COMMENT '最近一次推送原文 JSON',
jd_order_id bigint(20) DEFAULT NULL COMMENT '关联 jd_order.id第三方单号=闲鱼单号)',
local_waybill_no varchar(128) DEFAULT NULL COMMENT '本地物流扫描得到的运单号',
ship_status tinyint(4) NOT NULL DEFAULT '0' COMMENT '0未发货 1已调用发货成功 2发货失败',
ship_error varchar(500) DEFAULT NULL COMMENT '发货失败原因',
ship_time datetime DEFAULT NULL COMMENT '发货调用成功时间',
ship_express_code varchar(64) DEFAULT NULL COMMENT '实际发货使用的快递编码',
create_time datetime DEFAULT NULL,
update_time datetime DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_app_order (app_key, order_no),
KEY idx_jd_order (jd_order_id),
KEY idx_order_status (order_status),
KEY idx_modify_time (modify_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='闲管家 ERP 订单(全量跟踪)';
-- -----------------------------------------------------------------------------
-- 2) 存储过程:列不存在则 ADD
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS jarvis_erp_goofish_add_column;
DROP PROCEDURE IF EXISTS jarvis_erp_goofish_add_index;
DELIMITER $$
CREATE PROCEDURE jarvis_erp_goofish_add_column(
IN p_table VARCHAR(64),
IN p_column VARCHAR(64),
IN p_ddl TEXT
)
BEGIN
DECLARE col_exists INT DEFAULT 0;
SELECT COUNT(*) INTO col_exists
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @schema
AND TABLE_NAME = p_table
AND COLUMN_NAME = p_column;
IF col_exists = 0 THEN
SET @stmt := CONCAT('ALTER TABLE `', p_table, '` ADD COLUMN `', p_column, '` ', p_ddl);
PREPARE ps FROM @stmt;
EXECUTE ps;
DEALLOCATE PREPARE ps;
END IF;
END$$
CREATE PROCEDURE jarvis_erp_goofish_add_index(
IN p_table VARCHAR(64),
IN p_index VARCHAR(64),
IN p_columns VARCHAR(200)
)
BEGIN
DECLARE idx_exists INT DEFAULT 0;
SELECT COUNT(*) INTO idx_exists
FROM information_schema.statistics
WHERE TABLE_SCHEMA = @schema
AND TABLE_NAME = p_table
AND INDEX_NAME = p_index;
IF idx_exists = 0 THEN
SET @stmt := CONCAT('ALTER TABLE `', p_table, '` ADD INDEX `', p_index, '` (', p_columns, ')');
PREPARE ps FROM @stmt;
EXECUTE ps;
DEALLOCATE PREPARE ps;
END IF;
END$$
DELIMITER ;
-- -----------------------------------------------------------------------------
-- 3) erp_goofish_order补齐缺列与当前 Java 实体 / erp_goofish_init.sql 一致)
-- -----------------------------------------------------------------------------
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'seller_id', 'bigint(20) NULL COMMENT ''商家ID''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'user_name', 'varchar(128) NULL COMMENT ''闲鱼会员名''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'order_type', 'int(11) NULL COMMENT ''订单类型''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'order_status', 'int(11) NULL COMMENT ''订单状态(推送/列表)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'refund_status', 'int(11) NULL COMMENT ''退款状态''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'modify_time', 'bigint(20) NULL COMMENT ''订单更新时间(秒)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'product_id', 'bigint(20) NULL COMMENT ''管家商品ID''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'item_id', 'bigint(20) NULL COMMENT ''闲鱼商品ID''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'goods_title', 'varchar(512) NULL COMMENT ''商品标题(详情 goods.title)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'goods_image_url', 'varchar(1024) NULL COMMENT ''商品主图 URL(goods.images 首图)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'buyer_nick', 'varchar(256) NULL COMMENT ''买家昵称(详情 buyer_nick)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'pay_amount', 'bigint(20) NULL COMMENT ''实付金额(分) pay_amount''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'detail_waybill_no', 'varchar(128) NULL COMMENT ''闲管家详情回传运单号 waybill_no''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'detail_express_code', 'varchar(64) NULL COMMENT ''详情快递编码 express_code''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'detail_express_name', 'varchar(128) NULL COMMENT ''详情快递名称 express_name''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'receiver_name', 'varchar(128) NULL COMMENT ''收货人(详情有则落库)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'receiver_mobile', 'varchar(64) NULL COMMENT ''收货手机''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'receiver_address', 'varchar(1000) NULL COMMENT ''收货详细地址(address)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'receiver_region', 'varchar(256) NULL COMMENT ''省市区街道等拼接展示''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'recv_prov_name', 'varchar(64) NULL COMMENT ''prov_name''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'recv_city_name', 'varchar(64) NULL COMMENT ''city_name''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'recv_area_name', 'varchar(64) NULL COMMENT ''area_name''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'recv_town_name', 'varchar(128) NULL COMMENT ''town_name''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'detail_json', 'longtext NULL COMMENT ''订单详情接口全量 JSON''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'last_notify_json', 'longtext NULL COMMENT ''最近一次推送原文 JSON''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'jd_order_id', 'bigint(20) NULL COMMENT ''关联 jd_order.id第三方单号=闲鱼单号)''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'local_waybill_no', 'varchar(128) NULL COMMENT ''本地物流扫描得到的运单号''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'ship_status', 'tinyint(4) NOT NULL DEFAULT 0 COMMENT ''0未发货 1已调用发货成功 2发货失败''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'ship_error', 'varchar(500) NULL COMMENT ''发货失败原因''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'ship_time', 'datetime NULL COMMENT ''发货调用成功时间''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'ship_express_code', 'varchar(64) NULL COMMENT ''实际发货使用的快递编码''');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'create_time', 'datetime NULL');
CALL jarvis_erp_goofish_add_column('erp_goofish_order', 'update_time', 'datetime NULL');
-- -----------------------------------------------------------------------------
-- 4) 索引(手工建表可能缺)
-- -----------------------------------------------------------------------------
CALL jarvis_erp_goofish_add_index('erp_goofish_order', 'idx_jd_order', '`jd_order_id`');
CALL jarvis_erp_goofish_add_index('erp_goofish_order', 'idx_order_status', '`order_status`');
CALL jarvis_erp_goofish_add_index('erp_goofish_order', 'idx_modify_time', '`modify_time`');
-- -----------------------------------------------------------------------------
-- 5) 唯一键 uk_app_order
-- 若已存在同名约束则跳过;若 (app_key,order_no) 有重复行会报错,需先清洗数据。
-- 若已通过其它名称建了 (app_key,order_no) 唯一索引,请勿重复执行本节(可能报 Duplicate key
-- -----------------------------------------------------------------------------
SET @uk := (
SELECT COUNT(*) FROM information_schema.table_constraints
WHERE table_schema = @schema AND table_name = 'erp_goofish_order'
AND constraint_name = 'uk_app_order' AND constraint_type = 'UNIQUE'
);
SET @sql_uk := IF(@uk = 0,
'ALTER TABLE erp_goofish_order ADD UNIQUE KEY uk_app_order (app_key, order_no)',
'SELECT ''uk_app_order 已存在,跳过'' AS note');
PREPARE puk FROM @sql_uk;
EXECUTE puk;
DEALLOCATE PREPARE puk;
-- -----------------------------------------------------------------------------
-- 6) 清理存储过程
-- -----------------------------------------------------------------------------
DROP PROCEDURE IF EXISTS jarvis_erp_goofish_add_column;
DROP PROCEDURE IF EXISTS jarvis_erp_goofish_add_index;
SELECT 'erp_goofish_upgrade.sql 执行结束' AS message;

View File

@@ -17,7 +17,7 @@ public class JarvisGoofishProperties {
private String consumerGroup = "jarvis-goofish-order-consumer";
/** 回溯拉单小时数 */
/** 回溯拉单小时数(定时/增量) */
private int pullLookbackHours = 72;
/** 拉单定时 cron */
@@ -26,8 +26,25 @@ public class JarvisGoofishProperties {
/** 同步运单 + 自动发货 cron */
private String autoShipCron = "0 2/10 * * * ?";
/** 单次拉单每店最大页数防护 */
private int pullMaxPagesPerShop = 30;
/**
* 订单列表 page_size开放平台最大 100
*/
private int pullPageSize = 100;
/**
* 单次拉单每授权最大页数(开放平台 page_no 最大 100page_no×page_size 勿超过 10000
*/
private int pullMaxPagesPerShop = 100;
/**
* 全量/长历史拉单时,按 update_time 切片的窗口长度(秒),避免单窗内订单量过大触发平台限制
*/
private int pullTimeChunkSeconds = 604800;
/**
* 全量拉单从「当前时间」往前推多少天作为起点(仅 full 接口;可自行改大)
*/
private int pullFullHistoryDays = 1095;
private int autoShipBatchSize = 20;
}

View File

@@ -22,6 +22,27 @@ public class ErpGoofishOrder {
private Long modifyTime;
private Long productId;
private Long itemId;
/** 详情 goods.title */
private String goodsTitle;
/** goods.images 首张或其它单图字段 */
private String goodsImageUrl;
private String buyerNick;
/** 开放平台 pay_amount单位分 */
private Long payAmount;
/** 闲管家详情 waybill_no */
private String detailWaybillNo;
private String detailExpressCode;
private String detailExpressName;
private String receiverName;
private String receiverMobile;
private String receiverAddress;
/** 省市区拼接 */
private String receiverRegion;
/** 待发货等状态下开放平台返回的分级地址(与 prov_name/city_name/area_name/town_name 一致) */
private String recvProvName;
private String recvCityName;
private String recvAreaName;
private String recvTownName;
private String detailJson;
private String lastNotifyJson;
private Long jdOrderId;
@@ -41,4 +62,6 @@ public class ErpGoofishOrder {
private String jdThirdPartyOrderNo;
/** 联查:内部备注单号 */
private String jdRemark;
/** 联查:本地京东单收件地址 jd_order.address闲鱼详情常不返回明文地址 */
private String jdAddress;
}

View File

@@ -22,6 +22,11 @@ public interface IErpGoofishOrderService {
int pullAllEnabled(int lookbackHours);
/** 按配置的起始天数 + 时间分段尽量拉全历史订单update_time */
int pullOrdersForAppKeyFullHistory(String appKey);
int pullAllEnabledFullHistory();
void refreshDetail(Long id);
void retryShip(Long id);

View File

@@ -53,6 +53,7 @@ public class GoofishOrderPipeline {
try {
ErpGoofishOrder row = upsertFromNotify(appid, notifyBody, notifyBody.toJSONString());
tryLinkJdOrder(row);
mergeSummaryFromOrderDetailShape(row, notifyBody);
refreshDetail(row);
syncWaybillFromRedis(row);
tryAutoShip(row);
@@ -67,6 +68,7 @@ public class GoofishOrderPipeline {
}
ErpGoofishOrder row = upsertFromNotify(appKey, item, lastNotifyJson);
tryLinkJdOrder(row);
mergeSummaryFromOrderDetailShape(row, item);
refreshDetail(row);
syncWaybillFromRedis(row);
tryAutoShip(row);
@@ -151,7 +153,7 @@ public class GoofishOrderPipeline {
if (jo != null && jo.getIntValue("code") == 0) {
JSONObject data = jo.getJSONObject("data");
if (data != null) {
mergeSummaryFromDetail(row, data);
mergeSummaryFromOrderDetailShape(row, data);
}
}
} catch (Exception ex) {
@@ -159,34 +161,212 @@ public class GoofishOrderPipeline {
}
}
private void mergeSummaryFromDetail(ErpGoofishOrder row, JSONObject data) {
/**
* 合并「订单详情 / 订单列表 / 推送」等同一 schemaorder_detail上的展示与发货摘要字段。
* 列表与推送含 prov_name、city_name、area_name、town_name、address详情接口常不含明文地址。
*/
private void mergeSummaryFromOrderDetailShape(ErpGoofishOrder row, JSONObject data) {
if (row == null || row.getId() == null || data == null) {
return;
}
ErpGoofishOrder patch = new ErpGoofishOrder();
patch.setId(row.getId());
boolean any = false;
JSONObject goods = data.getJSONObject("goods");
if (goods != null) {
String title = goods.getString("title");
if (StringUtils.isNotEmpty(title)) {
patch.setGoodsTitle(title.trim());
}
String cover = firstGoodsCoverUrl(goods);
if (StringUtils.isNotEmpty(cover)) {
patch.setGoodsImageUrl(cover);
}
Long gPid = goods.getLong("product_id");
if (gPid != null) {
patch.setProductId(gPid);
}
Long gIid = goods.getLong("item_id");
if (gIid != null) {
patch.setItemId(gIid);
}
}
String wb = data.getString("waybill_no");
if (StringUtils.isNotEmpty(wb)) {
patch.setDetailWaybillNo(wb.trim());
}
String dec = data.getString("express_code");
if (StringUtils.isNotEmpty(dec)) {
patch.setDetailExpressCode(dec.trim());
}
String den = data.getString("express_name");
if (StringUtils.isNotEmpty(den)) {
patch.setDetailExpressName(den.trim());
}
String bn = data.getString("buyer_nick");
if (StringUtils.isNotEmpty(bn)) {
patch.setBuyerNick(bn.trim());
}
Long payFen = data.getLong("pay_amount");
if (payFen != null) {
patch.setPayAmount(payFen);
}
String seller = data.getString("seller_name");
if (StringUtils.isEmpty(row.getUserName()) && StringUtils.isNotEmpty(seller)) {
patch.setUserName(seller.trim());
}
patch.setReceiverName(firstNonEmpty(data, "receiver_name", "ship_name", "consignee_name", "contact_name"));
patch.setReceiverMobile(firstNonEmpty(data, "receiver_mobile", "ship_mobile", "receiver_phone", "contact_mobile"));
patch.setReceiverAddress(firstNonEmpty(data, "receiver_address", "ship_address", "detail_address",
"full_address", "address"));
String prov = firstNonEmpty(data, "receiver_province", "ship_prov_name", "prov", "prov_name");
String city = firstNonEmpty(data, "receiver_city", "ship_city_name", "city", "city_name");
String area = firstNonEmpty(data, "receiver_district", "receiver_area", "ship_area_name", "district", "area_name");
String town = firstNonEmpty(data, "town_name", "receiver_town", "ship_town_name", "street_name");
String regionJoined = joinWithSpace(prov, city, area, town);
if (StringUtils.isNotEmpty(regionJoined)) {
patch.setReceiverRegion(regionJoined);
}
if (StringUtils.isNotEmpty(prov)) {
patch.setRecvProvName(prov.trim());
}
if (StringUtils.isNotEmpty(city)) {
patch.setRecvCityName(city.trim());
}
if (StringUtils.isNotEmpty(area)) {
patch.setRecvAreaName(area.trim());
}
if (StringUtils.isNotEmpty(town)) {
patch.setRecvTownName(town.trim());
}
JSONObject recv = data.getJSONObject("receiver");
if (recv != null) {
if (StringUtils.isEmpty(patch.getReceiverName())) {
patch.setReceiverName(recv.getString("name"));
}
if (StringUtils.isEmpty(patch.getReceiverMobile())) {
String m = recv.getString("mobile");
if (StringUtils.isEmpty(m)) {
m = recv.getString("phone");
}
patch.setReceiverMobile(m);
}
if (StringUtils.isEmpty(patch.getReceiverAddress())) {
patch.setReceiverAddress(recv.getString("address"));
}
}
Integer os = data.getInteger("order_status");
if (os != null) {
patch.setOrderStatus(os);
row.setOrderStatus(os);
any = true;
}
Integer rs = data.getInteger("refund_status");
if (rs != null) {
patch.setRefundStatus(rs);
row.setRefundStatus(rs);
any = true;
}
Long mt = data.getLong("modify_time");
if (mt == null) {
mt = data.getLong("order_modify_time");
}
if (mt == null) {
mt = data.getLong("update_time");
}
if (mt != null) {
patch.setModifyTime(mt);
row.setModifyTime(mt);
any = true;
}
if (any) {
patch.setUpdateTime(DateUtils.getNowDate());
erpGoofishOrderMapper.update(patch);
fillReceiverFromJdOrder(patch, row);
patch.setUpdateTime(DateUtils.getNowDate());
erpGoofishOrderMapper.update(patch);
applySummaryPatchToRow(row, patch);
}
/** 闲鱼详情常不返回明文地址:用已关联的 jd_order.address 兜底;平台已回收货人/分级地址时不覆盖姓名 */
private void fillReceiverFromJdOrder(ErpGoofishOrder patch, ErpGoofishOrder row) {
if (row.getJdOrderId() == null) {
return;
}
boolean platformCare = StringUtils.isNotEmpty(patch.getReceiverName())
|| StringUtils.isNotEmpty(patch.getReceiverMobile())
|| StringUtils.isNotEmpty(patch.getRecvProvName())
|| StringUtils.isNotEmpty(patch.getRecvCityName())
|| StringUtils.isNotEmpty(patch.getRecvAreaName())
|| StringUtils.isNotEmpty(patch.getRecvTownName());
JDOrder jd = jdOrderService.selectJDOrderById(row.getJdOrderId());
if (jd == null) {
return;
}
if (StringUtils.isEmpty(patch.getReceiverAddress()) && StringUtils.isNotEmpty(jd.getAddress())) {
patch.setReceiverAddress(jd.getAddress().trim());
}
if (platformCare) {
return;
}
if (StringUtils.isEmpty(patch.getReceiverName()) && StringUtils.isNotEmpty(jd.getBuyer())) {
patch.setReceiverName(jd.getBuyer().trim());
}
}
private void applySummaryPatchToRow(ErpGoofishOrder row, ErpGoofishOrder patch) {
if (patch.getGoodsTitle() != null) {
row.setGoodsTitle(patch.getGoodsTitle());
}
if (patch.getGoodsImageUrl() != null) {
row.setGoodsImageUrl(patch.getGoodsImageUrl());
}
if (patch.getBuyerNick() != null) {
row.setBuyerNick(patch.getBuyerNick());
}
if (patch.getPayAmount() != null) {
row.setPayAmount(patch.getPayAmount());
}
if (patch.getDetailWaybillNo() != null) {
row.setDetailWaybillNo(patch.getDetailWaybillNo());
}
if (patch.getDetailExpressCode() != null) {
row.setDetailExpressCode(patch.getDetailExpressCode());
}
if (patch.getDetailExpressName() != null) {
row.setDetailExpressName(patch.getDetailExpressName());
}
if (patch.getReceiverName() != null) {
row.setReceiverName(patch.getReceiverName());
}
if (patch.getReceiverMobile() != null) {
row.setReceiverMobile(patch.getReceiverMobile());
}
if (patch.getReceiverAddress() != null) {
row.setReceiverAddress(patch.getReceiverAddress());
}
if (patch.getReceiverRegion() != null) {
row.setReceiverRegion(patch.getReceiverRegion());
}
if (patch.getRecvProvName() != null) {
row.setRecvProvName(patch.getRecvProvName());
}
if (patch.getRecvCityName() != null) {
row.setRecvCityName(patch.getRecvCityName());
}
if (patch.getRecvAreaName() != null) {
row.setRecvAreaName(patch.getRecvAreaName());
}
if (patch.getRecvTownName() != null) {
row.setRecvTownName(patch.getRecvTownName());
}
if (patch.getUserName() != null) {
row.setUserName(patch.getUserName());
}
if (patch.getOrderStatus() != null) {
row.setOrderStatus(patch.getOrderStatus());
}
if (patch.getRefundStatus() != null) {
row.setRefundStatus(patch.getRefundStatus());
}
if (patch.getModifyTime() != null) {
row.setModifyTime(patch.getModifyTime());
}
if (patch.getProductId() != null) {
row.setProductId(patch.getProductId());
}
if (patch.getItemId() != null) {
row.setItemId(patch.getItemId());
}
}
@@ -223,7 +403,11 @@ public class GoofishOrderPipeline {
if (row.getShipStatus() != null && row.getShipStatus() == 1) {
return;
}
if (StringUtils.isEmpty(row.getLocalWaybillNo())) {
String waybill = row.getLocalWaybillNo();
if (StringUtils.isEmpty(waybill)) {
waybill = row.getDetailWaybillNo();
}
if (StringUtils.isEmpty(waybill)) {
return;
}
ErpOpenConfig cfg = erpOpenConfigService.selectByAppKey(row.getAppKey());
@@ -234,9 +418,27 @@ public class GoofishOrderPipeline {
return;
}
ShipAddressParts addr = parseShipAddress(row.getDetailJson());
if (addr == null || StringUtils.isEmpty(addr.shipName) || StringUtils.isEmpty(addr.shipMobile)
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;
}
}
}
if (StringUtils.isEmpty(addr.shipName) || StringUtils.isEmpty(addr.shipMobile)
|| StringUtils.isEmpty(addr.shipAddress)) {
patchShipError(row, "详情中缺少收货人/手机/地址,请核对开放平台订单详情 JSON 字段");
patchShipError(row, "缺少收货人/手机/地址:详情无字段时请关联京东单并维护地址,或等平台返回收货字段");
return;
}
try {
@@ -258,7 +460,7 @@ public class GoofishOrderPipeline {
if (StringUtils.isNotEmpty(addr.shipAreaName)) {
ship.setShipAreaName(addr.shipAreaName);
}
ship.setWaybillNo(row.getLocalWaybillNo());
ship.setWaybillNo(waybill.trim());
ship.setExpressCode(expressCode);
ship.setExpressName(expressName);
String resp = ship.getResponseBody();
@@ -308,6 +510,47 @@ public class GoofishOrderPipeline {
String shipAreaName;
}
private void enrichShipAddressFromJd(ErpGoofishOrder row, ShipAddressParts p) {
if (row == null || p == null || row.getJdOrderId() == null) {
return;
}
JDOrder jd = jdOrderService.selectJDOrderById(row.getJdOrderId());
if (jd == null) {
return;
}
if (StringUtils.isEmpty(p.shipAddress) && StringUtils.isNotEmpty(jd.getAddress())) {
p.shipAddress = jd.getAddress().trim();
}
if (StringUtils.isEmpty(p.shipName) && StringUtils.isNotEmpty(jd.getBuyer())) {
p.shipName = jd.getBuyer().trim();
}
}
private ShipAddressParts receiverFieldsToShipParts(ErpGoofishOrder row) {
if (row == null) {
return null;
}
String regionLine = row.getReceiverRegion();
if (StringUtils.isEmpty(regionLine)) {
regionLine = joinWithSpace(row.getRecvProvName(), row.getRecvCityName(), row.getRecvAreaName(), row.getRecvTownName());
}
if (StringUtils.isEmpty(row.getReceiverAddress()) && StringUtils.isEmpty(regionLine)) {
return null;
}
ShipAddressParts p = new ShipAddressParts();
p.shipName = row.getReceiverName();
p.shipMobile = row.getReceiverMobile();
p.shipAddress = row.getReceiverAddress();
if (StringUtils.isNotEmpty(regionLine)) {
if (StringUtils.isNotEmpty(p.shipAddress)) {
p.shipAddress = regionLine + " " + p.shipAddress;
} else {
p.shipAddress = regionLine;
}
}
return p;
}
private ShipAddressParts parseShipAddress(String detailJson) {
if (StringUtils.isEmpty(detailJson)) {
return null;
@@ -323,11 +566,15 @@ public class GoofishOrderPipeline {
ShipAddressParts p = new ShipAddressParts();
p.shipName = firstNonEmpty(data, "ship_name", "receiver_name", "consignee", "contact_name");
p.shipMobile = firstNonEmpty(data, "ship_mobile", "receiver_mobile", "receiver_phone", "contact_mobile");
p.shipAddress = firstNonEmpty(data, "ship_address", "receiver_address", "detail_address");
p.shipAddress = firstNonEmpty(data, "ship_address", "receiver_address", "detail_address", "address");
p.shipDistrictId = firstInt(data, "ship_district_id", "receiver_district_id");
p.shipProvName = firstNonEmpty(data, "ship_prov_name", "receiver_province", "prov");
p.shipCityName = firstNonEmpty(data, "ship_city_name", "receiver_city", "city");
p.shipAreaName = firstNonEmpty(data, "ship_area_name", "receiver_area", "area", "district");
p.shipProvName = firstNonEmpty(data, "ship_prov_name", "receiver_province", "prov", "prov_name");
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)) {
@@ -346,6 +593,43 @@ public class GoofishOrderPipeline {
return p;
}
/** 开放平台 goods.images 首元素,或其它常见单图字段 */
private static String firstGoodsCoverUrl(JSONObject goods) {
if (goods == null) {
return null;
}
for (String k : new String[]{"image", "pic", "cover", "main_image", "img", "item_pic"}) {
String v = goods.getString(k);
if (StringUtils.isNotEmpty(v)) {
return v.trim();
}
}
JSONArray images = goods.getJSONArray("images");
if (images == null || images.isEmpty()) {
return null;
}
Object first = images.get(0);
if (first == null) {
return null;
}
String u = first.toString().trim();
return StringUtils.isNotEmpty(u) ? u : null;
}
/** 省、市、区、街道等与列表接口 prov_name/city_name/area_name/town_name 对齐拼接 */
private static String joinWithSpace(String... parts) {
StringBuilder sb = new StringBuilder();
for (String p : parts) {
if (StringUtils.isNotEmpty(p)) {
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(p.trim());
}
}
return sb.length() == 0 ? null : sb.toString();
}
private static String firstNonEmpty(JSONObject o, String... keys) {
if (o == null) {
return null;
@@ -372,23 +656,76 @@ public class GoofishOrderPipeline {
return null;
}
/**
* 增量拉单:按 update_time ∈ [nowlookbackHours, now]
*/
public int pullForAppKey(String appKey, int lookbackHours) {
if (StringUtils.isEmpty(appKey)) {
return 0;
}
IERPAccount cred = erpAccountResolver.resolve(appKey);
long now = System.currentTimeMillis() / 1000;
long start = now - (long) lookbackHours * 3600L;
return pullForAppKeyUpdateTimeRange(appKey, start, now);
}
/**
* 长历史全量:从「当前时间 pullFullHistoryDays」起按 pullTimeChunkSeconds 切 update_time 窗口逐段拉取;
* 每段内 page_size、page_no 与开放平台上限对齐,降低 page_no×page_size 超 1 万的风险。
*/
public int pullForAppKeyFullHistory(String appKey) {
if (StringUtils.isEmpty(appKey)) {
return 0;
}
long now = System.currentTimeMillis() / 1000;
int days = goofishProperties.getPullFullHistoryDays();
if (days < 1) {
days = 1;
}
long start = now - (long) days * 86400L;
int chunk = goofishProperties.getPullTimeChunkSeconds();
if (chunk < 3600) {
chunk = 86400;
}
int total = 0;
for (long t = start; t <= now; t += chunk) {
long end = Math.min(t + chunk - 1, now);
if (end < t) {
break;
}
total += pullForAppKeyUpdateTimeRange(appKey, t, end);
try {
Thread.sleep(150);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
return total;
}
/**
* 按开放平台「订单列表」接口,限定 update_time 时间戳区间(秒,闭区间)拉取并落库。
*/
public int pullForAppKeyUpdateTimeRange(String appKey, long updateTimeStartSec, long updateTimeEndSec) {
if (StringUtils.isEmpty(appKey) || updateTimeEndSec < updateTimeStartSec) {
return 0;
}
IERPAccount cred = erpAccountResolver.resolve(appKey);
List<Long> authorizeIds = fetchAuthorizeIds(cred);
int pageSize = Math.min(100, Math.max(1, goofishProperties.getPullPageSize()));
int maxPages = Math.min(100, Math.max(1, goofishProperties.getPullMaxPagesPerShop()));
if ((long) maxPages * pageSize > 10000L) {
maxPages = 10000 / pageSize;
log.warn("闲管家拉单: pull-max-pages 与 pull-page-size 乘积超过 10000已收敛 maxPages={}", maxPages);
}
int saved = 0;
int maxPages = goofishProperties.getPullMaxPagesPerShop();
for (Long aid : authorizeIds) {
int page = 1;
while (page <= maxPages) {
OrderListQueryRequest q = new OrderListQueryRequest(cred);
q.setAuthorizeId(aid);
q.setUpdateTime(start, now);
q.setPage(page, 20);
q.setUpdateTime(updateTimeStartSec, updateTimeEndSec);
q.setPage(page, pageSize);
String resp;
try {
resp = q.getResponseBody();
@@ -411,7 +748,12 @@ public class GoofishOrderPipeline {
applyListOrNotifyItem(appKey, item, item.toJSONString());
saved++;
}
if (list.size() < 20) {
if (list.size() < pageSize) {
break;
}
if (page == maxPages) {
log.warn("闲管家拉单已达最大页数 appKey={} aid={} 区间[{},{}];若订单更多请缩小 pull-time-chunk-seconds",
appKey, aid, updateTimeStartSec, updateTimeEndSec);
break;
}
page++;

View File

@@ -90,6 +90,27 @@ public class ErpGoofishOrderServiceImpl implements IErpGoofishOrderService {
return total;
}
@Override
public int pullOrdersForAppKeyFullHistory(String appKey) {
return goofishOrderPipeline.pullForAppKeyFullHistory(appKey);
}
@Override
public int pullAllEnabledFullHistory() {
int total = 0;
List<ErpOpenConfig> cfgs = erpOpenConfigService.selectEnabledOrderBySort();
if (cfgs == null) {
return 0;
}
for (ErpOpenConfig c : cfgs) {
if (c.getAppKey() == null) {
continue;
}
total += goofishOrderPipeline.pullForAppKeyFullHistory(c.getAppKey());
}
return total;
}
@Override
public void refreshDetail(Long id) {
ErpGoofishOrder row = erpGoofishOrderMapper.selectById(id);

View File

@@ -14,6 +14,21 @@
<result property="modifyTime" column="modify_time"/>
<result property="productId" column="product_id"/>
<result property="itemId" column="item_id"/>
<result property="goodsTitle" column="goods_title"/>
<result property="goodsImageUrl" column="goods_image_url"/>
<result property="buyerNick" column="buyer_nick"/>
<result property="payAmount" column="pay_amount"/>
<result property="detailWaybillNo" column="detail_waybill_no"/>
<result property="detailExpressCode" column="detail_express_code"/>
<result property="detailExpressName" column="detail_express_name"/>
<result property="receiverName" column="receiver_name"/>
<result property="receiverMobile" column="receiver_mobile"/>
<result property="receiverAddress" column="receiver_address"/>
<result property="receiverRegion" column="receiver_region"/>
<result property="recvProvName" column="recv_prov_name"/>
<result property="recvCityName" column="recv_city_name"/>
<result property="recvAreaName" column="recv_area_name"/>
<result property="recvTownName" column="recv_town_name"/>
<result property="detailJson" column="detail_json"/>
<result property="lastNotifyJson" column="last_notify_json"/>
<result property="jdOrderId" column="jd_order_id"/>
@@ -26,13 +41,17 @@
<result property="updateTime" column="update_time"/>
<result property="jdThirdPartyOrderNo" column="jd_third_party_order_no"/>
<result property="jdRemark" column="jd_remark"/>
<result property="jdAddress" column="jd_address"/>
</resultMap>
<sql id="selectJoinVo">
select e.id, e.app_key, e.seller_id, e.user_name, e.order_no, e.order_type, e.order_status, e.refund_status,
e.modify_time, e.product_id, e.item_id, e.detail_json, e.last_notify_json, e.jd_order_id, e.local_waybill_no,
e.modify_time, e.product_id, e.item_id, e.goods_title, e.goods_image_url, e.buyer_nick, e.pay_amount, e.detail_waybill_no,
e.detail_express_code, e.detail_express_name, e.receiver_name, e.receiver_mobile, e.receiver_address,
e.receiver_region, e.recv_prov_name, e.recv_city_name, e.recv_area_name, e.recv_town_name,
e.detail_json, e.last_notify_json, e.jd_order_id, e.local_waybill_no,
e.ship_status, e.ship_error, e.ship_time, e.ship_express_code, e.create_time, e.update_time,
o.third_party_order_no as jd_third_party_order_no, o.remark as jd_remark
o.third_party_order_no as jd_third_party_order_no, o.remark as jd_remark, o.address as jd_address
from erp_goofish_order e
left join jd_order o on e.jd_order_id = o.id
</sql>
@@ -68,10 +87,16 @@
<insert id="insert" parameterType="com.ruoyi.jarvis.domain.ErpGoofishOrder" useGeneratedKeys="true" keyProperty="id">
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,
goods_title, goods_image_url, buyer_nick, pay_amount, detail_waybill_no, detail_express_code, detail_express_name,
receiver_name, receiver_mobile, receiver_address, receiver_region,
recv_prov_name, recv_city_name, recv_area_name, recv_town_name,
detail_json, last_notify_json, jd_order_id, local_waybill_no, ship_status, ship_error, ship_time, ship_express_code,
create_time, update_time)
values
(#{appKey}, #{sellerId}, #{userName}, #{orderNo}, #{orderType}, #{orderStatus}, #{refundStatus}, #{modifyTime}, #{productId}, #{itemId},
#{goodsTitle}, #{goodsImageUrl}, #{buyerNick}, #{payAmount}, #{detailWaybillNo}, #{detailExpressCode}, #{detailExpressName},
#{receiverName}, #{receiverMobile}, #{receiverAddress}, #{receiverRegion},
#{recvProvName}, #{recvCityName}, #{recvAreaName}, #{recvTownName},
#{detailJson}, #{lastNotifyJson}, #{jdOrderId}, #{localWaybillNo}, #{shipStatus}, #{shipError}, #{shipTime}, #{shipExpressCode},
#{createTime}, #{updateTime})
</insert>
@@ -87,6 +112,21 @@
<if test="modifyTime != null">modify_time = #{modifyTime},</if>
<if test="productId != null">product_id = #{productId},</if>
<if test="itemId != null">item_id = #{itemId},</if>
<if test="goodsTitle != null">goods_title = #{goodsTitle},</if>
<if test="goodsImageUrl != null">goods_image_url = #{goodsImageUrl},</if>
<if test="buyerNick != null">buyer_nick = #{buyerNick},</if>
<if test="payAmount != null">pay_amount = #{payAmount},</if>
<if test="detailWaybillNo != null">detail_waybill_no = #{detailWaybillNo},</if>
<if test="detailExpressCode != null">detail_express_code = #{detailExpressCode},</if>
<if test="detailExpressName != null">detail_express_name = #{detailExpressName},</if>
<if test="receiverName != null">receiver_name = #{receiverName},</if>
<if test="receiverMobile != null">receiver_mobile = #{receiverMobile},</if>
<if test="receiverAddress != null">receiver_address = #{receiverAddress},</if>
<if test="receiverRegion != null">receiver_region = #{receiverRegion},</if>
<if test="recvProvName != null">recv_prov_name = #{recvProvName},</if>
<if test="recvCityName != null">recv_city_name = #{recvCityName},</if>
<if test="recvAreaName != null">recv_area_name = #{recvAreaName},</if>
<if test="recvTownName != null">recv_town_name = #{recvTownName},</if>
<if test="detailJson != null">detail_json = #{detailJson},</if>
<if test="lastNotifyJson != null">last_notify_json = #{lastNotifyJson},</if>
<if test="jdOrderId != null">jd_order_id = #{jdOrderId},</if>