对接数据库,模拟第三方接入信息
This commit is contained in:
@@ -68,6 +68,7 @@
|
||||
测试/预发环境(不使用 mock_* 表也能联调):
|
||||
- 仍然执行 [express_tracking_platform_upgrade.sql](express_tracking_platform_upgrade.sql) 创建“生产同款”三表。
|
||||
- 再执行 [seed_platform_express_test_data.sql](seed_platform_express_test_data.sql) 向 `platform_express_*` 写入少量 TEST_ 前缀示例数据,用于页面联调与排障演示(可随时清理)。
|
||||
- 说明:当前脚本会插入 9 条 `TEST_` 运单(含 1 条 `PENDING/未发货`),并为每条运单插入 1~3 条不等的轨迹事件(幂等,可重复执行)。
|
||||
|
||||
### Ubuntu 上的 Supabase(测试/预发)怎么执行
|
||||
|
||||
@@ -93,6 +94,73 @@
|
||||
清理测试数据:
|
||||
- 种子脚本底部自带清理 SQL(按 `tracking_no LIKE 'TEST_%'` 删除),需要时复制执行即可。
|
||||
|
||||
### 执行成功后的校验 SQL(建议)
|
||||
|
||||
在 Supabase SQL Editor 执行以下查询,确认数据是否写入成功、事件是否按时间线可查:
|
||||
|
||||
1) 查看全部 TEST_ 运单(应看到 9 条):
|
||||
```sql
|
||||
select
|
||||
carrier,
|
||||
tracking_no,
|
||||
current_status_code,
|
||||
current_status_text,
|
||||
last_event_time,
|
||||
last_synced_at,
|
||||
created_at
|
||||
from public.platform_express_waybills
|
||||
where tracking_no like 'TEST_%'
|
||||
order by created_at desc;
|
||||
```
|
||||
|
||||
2) 按运单统计事件数量(确认每单都有事件):
|
||||
```sql
|
||||
select
|
||||
w.carrier,
|
||||
w.tracking_no,
|
||||
count(e.id) as event_count,
|
||||
min(e.event_time) as first_event_time,
|
||||
max(e.event_time) as last_event_time
|
||||
from public.platform_express_waybills w
|
||||
left join public.platform_express_tracking_events e
|
||||
on e.waybill_id = w.id
|
||||
where w.tracking_no like 'TEST_%'
|
||||
group by w.carrier, w.tracking_no
|
||||
order by last_event_time desc nulls last;
|
||||
```
|
||||
|
||||
3) 查看单个运单时间线(按 event_time 升序):
|
||||
```sql
|
||||
select
|
||||
event_time,
|
||||
event_code,
|
||||
event_text,
|
||||
status_code,
|
||||
node_name,
|
||||
node_location,
|
||||
source,
|
||||
dedupe_key
|
||||
from public.platform_express_tracking_events
|
||||
where tracking_no = 'TEST_YT_20260206_0002'
|
||||
order by event_time asc;
|
||||
```
|
||||
|
||||
4) 查看原始接收留痕(可选,用于排障/审计):
|
||||
```sql
|
||||
select
|
||||
received_at,
|
||||
source,
|
||||
carrier,
|
||||
tracking_no,
|
||||
signature_valid,
|
||||
request_id,
|
||||
dedupe_key
|
||||
from public.platform_express_event_raw
|
||||
where tracking_no like 'TEST_%'
|
||||
order by received_at desc
|
||||
limit 50;
|
||||
```
|
||||
|
||||
### 如何“伪造第三方推送到数据库”(无需后端)
|
||||
|
||||
如果你暂时没有 webhook 接收服务,但希望测试环境表现得像“第三方已经推送了轨迹”,可以直接写数据库:
|
||||
|
||||
@@ -26,7 +26,13 @@ INSERT INTO public.platform_express_waybills (
|
||||
VALUES
|
||||
(NULL, 'ORD_TEST_20260206001', 'YUNDA', 'TEST_YD_20260206_0001', 'mock', 'IN_TRANSIT', '运输中', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206002', 'YTO', 'TEST_YT_20260206_0002', 'mock', 'OUT_FOR_DELIVERY', '派送中', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206003', 'ZTO', 'TEST_ZT_20260206_0003', 'mock', 'DELIVERED', '已签收', NULL, NOW())
|
||||
(NULL, 'ORD_TEST_20260206003', 'ZTO', 'TEST_ZT_20260206_0003', 'mock', 'DELIVERED', '已签收', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206004', 'STO', 'TEST_STO_20260206_0004', 'mock', 'EXCEPTION', '异常', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206005', 'SF', 'TEST_SF_20260206_0005', 'mock', 'ARRIVED_HUB', '到达网点', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206006', 'YUNDA', 'TEST_YD_20260206_0006', 'mock', 'SHIPPED', '已发货', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206007', 'YTO', 'TEST_YT_20260206_0007', 'mock', 'IN_TRANSIT', '运输中', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206008', 'ZTO', 'TEST_ZT_20260206_0008', 'mock', 'OUT_FOR_DELIVERY', '派送中', NULL, NOW()),
|
||||
(NULL, 'ORD_TEST_20260206009', 'YUNDA', 'TEST_YD_20260206_0009', 'mock', 'PENDING', '未发货', NULL, NOW())
|
||||
ON CONFLICT (carrier, tracking_no) DO NOTHING;
|
||||
|
||||
|
||||
@@ -34,7 +40,17 @@ ON CONFLICT (carrier, tracking_no) DO NOTHING;
|
||||
WITH w AS (
|
||||
SELECT id, carrier, tracking_no
|
||||
FROM public.platform_express_waybills
|
||||
WHERE tracking_no IN ('TEST_YD_20260206_0001', 'TEST_YT_20260206_0002', 'TEST_ZT_20260206_0003')
|
||||
WHERE tracking_no IN (
|
||||
'TEST_YD_20260206_0001',
|
||||
'TEST_YT_20260206_0002',
|
||||
'TEST_ZT_20260206_0003',
|
||||
'TEST_STO_20260206_0004',
|
||||
'TEST_SF_20260206_0005',
|
||||
'TEST_YD_20260206_0006',
|
||||
'TEST_YT_20260206_0007',
|
||||
'TEST_ZT_20260206_0008',
|
||||
'TEST_YD_20260206_0009'
|
||||
)
|
||||
)
|
||||
INSERT INTO public.platform_express_tracking_events (
|
||||
waybill_id,
|
||||
@@ -185,6 +201,198 @@ SELECT * FROM (
|
||||
NOW(),
|
||||
'webhook',
|
||||
'test_e_3003'
|
||||
|
||||
-- 运单 4:异常
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_STO_20260206_0004'),
|
||||
'STO',
|
||||
'TEST_STO_20260206_0004',
|
||||
'test_e_4001',
|
||||
NOW() - INTERVAL '20 hours',
|
||||
'PICKED',
|
||||
'包裹已揽收',
|
||||
'ARRIVED_HUB',
|
||||
'深圳南山网点',
|
||||
'深圳市 南山区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'poll',
|
||||
'test_e_4001'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_STO_20260206_0004'),
|
||||
'STO',
|
||||
'TEST_STO_20260206_0004',
|
||||
'test_e_4002',
|
||||
NOW() - INTERVAL '2 hours',
|
||||
'EXCEPTION',
|
||||
'【包裹异常】收件地址不详,等待处理(测试数据)',
|
||||
'EXCEPTION',
|
||||
'深圳南山网点',
|
||||
'深圳市 南山区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'webhook',
|
||||
'test_e_4002'
|
||||
|
||||
-- 运单 5:到达网点
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_SF_20260206_0005'),
|
||||
'SF',
|
||||
'TEST_SF_20260206_0005',
|
||||
'test_e_5001',
|
||||
NOW() - INTERVAL '10 hours',
|
||||
'PICKED',
|
||||
'包裹已揽收',
|
||||
'ARRIVED_HUB',
|
||||
'南京江宁集散中心',
|
||||
'南京市 江宁区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'poll',
|
||||
'test_e_5001'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_SF_20260206_0005'),
|
||||
'SF',
|
||||
'TEST_SF_20260206_0005',
|
||||
'test_e_5002',
|
||||
NOW() - INTERVAL '30 minutes',
|
||||
'ARRIVAL',
|
||||
'快件已到达【南京江宁网点】,等待派送(测试数据)',
|
||||
'ARRIVED_HUB',
|
||||
'南京江宁网点',
|
||||
'南京市 江宁区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'webhook',
|
||||
'test_e_5002'
|
||||
|
||||
-- 运单 6:已发货(待揽收)
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_YD_20260206_0006'),
|
||||
'YUNDA',
|
||||
'TEST_YD_20260206_0006',
|
||||
'test_e_6001',
|
||||
NOW() - INTERVAL '5 hours',
|
||||
'SHIPPED',
|
||||
'商家已发货,等待快递公司揽收(测试数据)',
|
||||
'SHIPPED',
|
||||
NULL,
|
||||
'杭州市 余杭区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'manual',
|
||||
'test_e_6001'
|
||||
|
||||
-- 运单 7:运输中
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_YT_20260206_0007'),
|
||||
'YTO',
|
||||
'TEST_YT_20260206_0007',
|
||||
'test_e_7001',
|
||||
NOW() - INTERVAL '18 hours',
|
||||
'PICKED',
|
||||
'包裹已揽收',
|
||||
'ARRIVED_HUB',
|
||||
'武汉江夏网点',
|
||||
'武汉市 江夏区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'poll',
|
||||
'test_e_7001'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_YT_20260206_0007'),
|
||||
'YTO',
|
||||
'TEST_YT_20260206_0007',
|
||||
'test_e_7002',
|
||||
NOW() - INTERVAL '7 hours',
|
||||
'TRANSIT',
|
||||
'快件离开【武汉江夏网点】,已发往【长沙转运中心】(测试数据)',
|
||||
'IN_TRANSIT',
|
||||
'武汉江夏网点',
|
||||
'武汉市 江夏区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'poll',
|
||||
'test_e_7002'
|
||||
|
||||
-- 运单 8:派送中
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_ZT_20260206_0008'),
|
||||
'ZTO',
|
||||
'TEST_ZT_20260206_0008',
|
||||
'test_e_8001',
|
||||
NOW() - INTERVAL '9 hours',
|
||||
'ARRIVAL',
|
||||
'快件已到达【西安高新网点】(测试数据)',
|
||||
'IN_TRANSIT',
|
||||
'西安高新网点',
|
||||
'西安市 雁塔区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'poll',
|
||||
'test_e_8001'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_ZT_20260206_0008'),
|
||||
'ZTO',
|
||||
'TEST_ZT_20260206_0008',
|
||||
'test_e_8002',
|
||||
NOW() - INTERVAL '20 minutes',
|
||||
'OUT_FOR_DELIVERY',
|
||||
'派送员正在派件(测试数据)',
|
||||
'OUT_FOR_DELIVERY',
|
||||
'西安高新网点',
|
||||
'西安市 雁塔区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'webhook',
|
||||
'test_e_8002'
|
||||
|
||||
-- 运单 9:未发货(占位事件,便于前端展示)
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM w WHERE tracking_no = 'TEST_YD_20260206_0009'),
|
||||
'YUNDA',
|
||||
'TEST_YD_20260206_0009',
|
||||
'test_e_9001',
|
||||
NOW() - INTERVAL '3 hours',
|
||||
'PENDING',
|
||||
'商家待发货(测试数据)',
|
||||
'PENDING',
|
||||
NULL,
|
||||
'杭州市 余杭区',
|
||||
NULL,
|
||||
'[]'::jsonb,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
'manual',
|
||||
'test_e_9001'
|
||||
) AS rows_to_insert
|
||||
WHERE rows_to_insert.waybill_id IS NOT NULL
|
||||
ON CONFLICT (waybill_id, dedupe_key) DO NOTHING;
|
||||
@@ -224,6 +432,39 @@ VALUES (
|
||||
'raw_test_0001'
|
||||
);
|
||||
|
||||
INSERT INTO public.platform_express_event_raw (
|
||||
received_at,
|
||||
source,
|
||||
client_id,
|
||||
carrier,
|
||||
tracking_no,
|
||||
signature_valid,
|
||||
signature,
|
||||
ts_header,
|
||||
request_id,
|
||||
remote_ip,
|
||||
headers,
|
||||
body,
|
||||
parse_error,
|
||||
dedupe_key
|
||||
)
|
||||
VALUES (
|
||||
NOW(),
|
||||
'poll',
|
||||
'test_client',
|
||||
'STO',
|
||||
'TEST_STO_20260206_0004',
|
||||
NULL,
|
||||
NULL,
|
||||
EXTRACT(EPOCH FROM NOW())::text,
|
||||
'req_test_0002',
|
||||
'127.0.0.1',
|
||||
'{"content-type":"application/json"}'::jsonb,
|
||||
'{"tracking_no":"TEST_STO_20260206_0004","status_code":"EXCEPTION","event_text":"【包裹异常】收件地址不详,等待处理(测试数据)"}'::jsonb,
|
||||
NULL,
|
||||
'raw_test_0002'
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- =====================================================================================
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
- 平台接收 Mock Server 的 Webhook 推送(推荐)。
|
||||
- 平台亦可主动调用 Mock Server 查询轨迹(模拟轮询补偿)。
|
||||
|
||||
范围说明(避免误解):
|
||||
- 若要实现“电子面单/在线下单自动获取运单号”,需要对接第三方(快递公司直连或聚合平台)提供的下单/面单接口。
|
||||
- 该类“下单/面单”接口不属于本 Mock Server 接口规范范围;本文只覆盖轨迹事件的推送/查询与相关验签、幂等与字段口径。
|
||||
|
||||
## 四、接口定义(示例)
|
||||
|
||||
1) Mock Server -> 平台:事件推送(Webhook)
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
与 Mock 的关系:
|
||||
- Mock 用于第三方未联通阶段的替代数据源与故障注入;生产环境默认关闭。
|
||||
|
||||
### 方案 B:商家自选配送(商家自找承运方 / 平台只做订单)
|
||||
适用:平台招商、品类/区域差异大、平台希望给商家“选择承运方”的灵活性。
|
||||
### 方案 B:商家自选配送(商家自选承运商并发货 / 平台负责运单绑定与轨迹展示)
|
||||
适用:平台招商、品类/区域差异大、平台希望给商家“选择承运方”的灵活性;平台不承担“实际配送执行”,但需要提供统一的运单绑定入口与轨迹展示底座。
|
||||
|
||||
重要说明(商家只是平台商户的常见情况):
|
||||
- 默认不要求商家自建系统或自行对接第三方 API。
|
||||
@@ -75,6 +75,30 @@
|
||||
- 在平台选择承运方并完成发货动作:提供/回填 `tracking_no` 与必要的订单关联信息(如 `order_no`)。
|
||||
- 若商家坚持使用“商家与第三方的独立合同/账号”,可向平台提交第三方对接所需材料,由平台统一配置与代接入(不要求商家自建回调服务)。
|
||||
|
||||
可选增强:商家在平台内“自助对接第三方”(无需商家开发)
|
||||
- 定位:可选增强能力,非一期必做。
|
||||
- 目标:让商家在平台后台完成第三方账号授权/配置,从而在平台内完成下单打单、获取运单号、查询/订阅轨迹;平台仍统一入库与展示。
|
||||
- 常见两种形态(可二选一,也可并存):
|
||||
1) 聚合平台授权开通(推荐优先):平台统一对接一家聚合服务商;商家在平台内完成开通/授权后即可使用多家快递能力。
|
||||
2) 商家自带第三方账号(BYO Account):商家已与某快递/聚合平台签约;在平台后台录入密钥或走 OAuth 授权,平台代为调用第三方 API。
|
||||
- 商家后台最小页面/流程建议:
|
||||
- 【物流渠道管理】:选择渠道类型(聚合/直连)、开启/停用
|
||||
- 【授权与密钥】:录入/更新 `appKey/appSecret/token` 或 OAuth 绑定;展示“最后一次连通性检测”结果
|
||||
- 【回调配置提示】:展示平台 Webhook 地址与白名单要求(若第三方需要配置回调)
|
||||
- 【测试与排障】:一键“连通性测试/拉取一条轨迹/模拟下单”,失败给出可读错误
|
||||
- 安全要求(必须):
|
||||
- 第三方密钥加密存储、最小权限、变更审计;仅商家管理员可配置。
|
||||
- 不在前端暴露密钥;平台服务端代调用第三方接口。
|
||||
- 轨迹/运单数据仍写入平台统一表结构与事件模型,避免不同渠道把差异带到前端。
|
||||
|
||||
是否需要做(决策建议):
|
||||
- 暂不需要(建议先不做)的场景:一期目标仅是“商家回填运单号 + 平台展示第三方轨迹关键节点”,且商家规模不大/对接资源有限。
|
||||
- 建议需要(做了收益明显)的场景:商家量大且运单号回填错误率高、客服投诉“查不到物流/不更新”多;或明确要上“电子面单/平台一键下单”。
|
||||
- 推荐分期:
|
||||
- Phase 1:回填运单号 + 轨迹接入与统一展示底座(本项目当前优先级)。
|
||||
- Phase 2:先接入 1 家聚合平台做统一下单/面单/轨迹(降低多家直连成本)。
|
||||
- Phase 3:再开放 BYO Account(商家自带账号)自助配置(安全与运维成本最高)。
|
||||
|
||||
优点:平台轻、商家灵活。
|
||||
风险/成本:体验碎片化风险大;如果不强制回传规范,平台客服/前端会被迫处理多样差异,长期维护成本更高。
|
||||
|
||||
@@ -85,6 +109,18 @@
|
||||
- `tracking_no`:运单号生成与回传方式(商家生成/第三方返回/平台生成)。
|
||||
- 订单关联信息:`order_no`(或平台侧可解析的业务单号),用于把轨迹绑定到订单详情页。
|
||||
|
||||
运单号(`tracking_no`)获取方式(两种常见落地,二选一或并存):
|
||||
1) 回填运单号(最小模式,推荐先上线):
|
||||
- 商家在线下/快递官方系统/聚合平台完成下单与交接,获得运单号。
|
||||
- 商家在平台后台“发货”时选择承运商并回填 `tracking_no`。
|
||||
- 平台不需要调用第三方“下单/面单”接口,只需后续接入轨迹(Webhook/轮询)用于展示。
|
||||
2) 电子面单 / 在线下单(增强模式,体验更好):
|
||||
- 平台(或平台集成的服务商)需要对接第三方提供的“下单/面单”接口,向第三方提交发货所需信息(收件人/地址/重量/件数等),并获得:运单号 `tracking_no` + 面单文件/面单号等。
|
||||
- 第三方可以是:
|
||||
- 直连快递公司接口(每家快递一套协议);或
|
||||
- 快递聚合平台接口(一套协议覆盖多家快递)。
|
||||
- 兜底策略建议:若第三方下单失败/超时,允许商家改为“手工回填运单号”完成发货闭环。
|
||||
|
||||
轨迹数据接入方式(平台统一接入,推荐第一种):
|
||||
1) 第三方 -> 平台 Webhook 推送(推荐):
|
||||
- 平台与第三方完成订阅/回调配置;第三方直接回调平台 Webhook。
|
||||
|
||||
Reference in New Issue
Block a user