From 3ea0f008b578cbd0df3a1e5a8ce0261c429a0168 Mon Sep 17 00:00:00 2001 From: not-like-juvenile <16056107+not-like-juvenile@user.noreply.gitee.com> Date: Mon, 9 Feb 2026 08:54:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=EF=BC=8C=E6=A8=A1=E6=8B=9F=E7=AC=AC=E4=B8=89=E6=96=B9=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/mall/delivery/doc/需求文档/README.md | 68 ++ .../seed_platform_express_test_data.sql | 245 +++++- pages/mall/delivery/doc/需求文档/接口规范.md | 4 + .../delivery/doc/需求文档/配送模块需求文档.md | 40 +- pages/mall/delivery/test/api-simulator.uvue | 51 +- .../test/consumer-logistics-detail.uvue | 38 +- .../delivery/test/consumer-order-list.uvue | 42 +- pages/mall/delivery/test/index.uvue | 15 +- .../delivery/test/merchant-order-detail.uvue | 27 +- .../delivery/test/merchant-order-list.uvue | 35 +- pages/mall/delivery/test/mock-service.uts | 729 ++++++++---------- .../delivery/test/platform-config-center.uvue | 13 + .../test/platform-tracking-query.uvue | 54 +- .../delivery/test/platform-webhook-logs.uvue | 23 +- 14 files changed, 882 insertions(+), 502 deletions(-) diff --git a/pages/mall/delivery/doc/需求文档/README.md b/pages/mall/delivery/doc/需求文档/README.md index 01fb1629..4bcbba03 100644 --- a/pages/mall/delivery/doc/需求文档/README.md +++ b/pages/mall/delivery/doc/需求文档/README.md @@ -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 接收服务,但希望测试环境表现得像“第三方已经推送了轨迹”,可以直接写数据库: diff --git a/pages/mall/delivery/doc/需求文档/seed_platform_express_test_data.sql b/pages/mall/delivery/doc/需求文档/seed_platform_express_test_data.sql index 3672cebc..8f064d5b 100644 --- a/pages/mall/delivery/doc/需求文档/seed_platform_express_test_data.sql +++ b/pages/mall/delivery/doc/需求文档/seed_platform_express_test_data.sql @@ -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; -- ===================================================================================== diff --git a/pages/mall/delivery/doc/需求文档/接口规范.md b/pages/mall/delivery/doc/需求文档/接口规范.md index 773fe154..562df9e7 100644 --- a/pages/mall/delivery/doc/需求文档/接口规范.md +++ b/pages/mall/delivery/doc/需求文档/接口规范.md @@ -49,6 +49,10 @@ - 平台接收 Mock Server 的 Webhook 推送(推荐)。 - 平台亦可主动调用 Mock Server 查询轨迹(模拟轮询补偿)。 +范围说明(避免误解): +- 若要实现“电子面单/在线下单自动获取运单号”,需要对接第三方(快递公司直连或聚合平台)提供的下单/面单接口。 +- 该类“下单/面单”接口不属于本 Mock Server 接口规范范围;本文只覆盖轨迹事件的推送/查询与相关验签、幂等与字段口径。 + ## 四、接口定义(示例) 1) Mock Server -> 平台:事件推送(Webhook) diff --git a/pages/mall/delivery/doc/需求文档/配送模块需求文档.md b/pages/mall/delivery/doc/需求文档/配送模块需求文档.md index c3a7fe59..a2dace9a 100644 --- a/pages/mall/delivery/doc/需求文档/配送模块需求文档.md +++ b/pages/mall/delivery/doc/需求文档/配送模块需求文档.md @@ -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。 diff --git a/pages/mall/delivery/test/api-simulator.uvue b/pages/mall/delivery/test/api-simulator.uvue index cf3ef8fe..bd392901 100644 --- a/pages/mall/delivery/test/api-simulator.uvue +++ b/pages/mall/delivery/test/api-simulator.uvue @@ -1,6 +1,7 @@