diff --git a/pages.json b/pages.json index c2b7c1fa..a14a82d7 100644 --- a/pages.json +++ b/pages.json @@ -365,6 +365,55 @@ "navigationBarTitleText": "设置", "navigationStyle": "custom" } + }, + { + "path": "test/index", + "style": { + "navigationBarTitleText": "配送模块测试索引", + "navigationStyle": "custom" + } + }, + { + "path": "test/merchant-order-list", + "style": { + "navigationBarTitleText": "商家发货管理", + "navigationStyle": "custom" + } + }, + { + "path": "test/merchant-order-detail", + "style": { + "navigationBarTitleText": "订单物流详情", + "navigationStyle": "custom" + } + }, + { + "path": "test/platform-tracking-query", + "style": { + "navigationBarTitleText": "轨迹排障查询", + "navigationStyle": "custom" + } + }, + { + "path": "test/platform-webhook-logs", + "style": { + "navigationBarTitleText": "Webhook日志", + "navigationStyle": "custom" + } + }, + { + "path": "test/platform-config-center", + "style": { + "navigationBarTitleText": "配送配置中心", + "navigationStyle": "custom" + } + }, + { + "path": "test/consumer-logistics-detail", + "style": { + "navigationBarTitleText": "用户查快递", + "navigationStyle": "custom" + } } ] }, diff --git a/pages/mall/delivery/doc/需求文档/README.md b/pages/mall/delivery/doc/需求文档/README.md new file mode 100644 index 00000000..0894388e --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/README.md @@ -0,0 +1,59 @@ +# 配送模块文档目录(Mock 三通一达后台联调) + +本目录聚焦“模拟三通一达后台(Mock 承运方 Server)”的联调与测试需求,用于在未签约真实承运方前验证平台侧的 Webhook 接收、验签、幂等、入库与前端时间线展示。 + +## 文档分层(先看什么、后看什么) +- 需求(做什么):`配送模块需求文档.md` +- 页面(后台长什么样):`后台页面设计说明.md` +- 接口(怎么对接):`接口规范.md` +- 映射(状态怎么统一):`状态映射表.md` +- 前端契约(怎么展示):`前端字段清单.md` +- 补齐清单(还缺什么):`缺口与待补充清单.md` + +## 先读这个:这套文档在解决什么 +你现在要做的是一个“像三通一达一样会产生物流节点并回调的平台外部系统”。真实承运方接口通常需要签约与联调周期,因此先通过 Mock 承运方 Server 把平台侧能力跑通: +- 事件接收:平台接收并处理 Webhook 回调 +- 安全与可靠性:验签、防重放、幂等去重、乱序/重复/延迟处理 +- 数据闭环:事件入库 -> 前端按时间线展示(类似京东/淘宝订单物流关键节点) + +说明:本目录文档服务于“平台系统的一部分”(订单详情页的物流区块、客服/履约排障与对接联调)。 +- 平台端通常需要在订单详情中查看与履约相关的信息(订单号、运单号、承运方、物流时间线、异常记录等)。 +- 本目录不展开订单商品/支付/营销等业务字段,仅覆盖“订单详情里的物流子模块”与其对接/入库/展示规范。 + +## 核心思想(设计原则) +- 以“事件流”为中心:物流展示本质就是一组按时间发生的事件(揽收/中转/在途/派送/签收/异常)。 +- Webhook + 控制面分层:Webhook 用来模拟“承运方回调”,控制面 API 用来“造数据/控节奏/注入故障”。 +- 统一状态码 + 原文并存:平台用 `status_code` 做标签/统计/告警;用 `event_text` 展示承运方原文,并保留 `raw_payload` 便于客服核查。 +- 故障注入必须:重复推送、乱序、延迟、签名错误、缺字段等是外部对接最常见问题,必须在联调阶段覆盖。 + +## 阅读顺序(推荐) +1) 先看 `配送模块需求文档.md`:理解 Mock Server 要做什么、有哪些验收与故障注入。 +2) 再看 `后台页面设计说明.md`:明确平台后台/商家后台需要哪些页面与最小展示/权限要求。 +3) 再看 `接口规范.md`: + - Mock Server -> 平台:Webhook 推送格式与验签 + - 平台/测试工具 -> Mock Server:控制面 API(创建运单/追加事件/跑场景/触发推送) +4) 看 `状态映射表.md`:明确 `event_code` 如何映射到平台统一 `status_code`。 +5) 看 `前端字段清单.md`:前端时间线组件到底需要哪些字段(以及 `source` 如何标注)。 +6) 最后看 `缺口与待补充清单.md`:把联调验收所需材料补齐,避免漏项。 + +## 快速上手(联调路径) +1) 平台准备一个 Webhook 接收地址:`/webhook/express/status`,并实现 `X-Signature` 验签与幂等去重(按 `event_id`)。 +2) 使用 Mock Server 控制面配置目标:设置 `target_webhook_url`、`client_id`、`secret`。 +3) 创建运单(tracking_no)并运行场景(例如 `standard_delivered`),选择是否立即推送。 +4) 在平台查看:事件是否按预期入库、时间线是否按 `event_time` 展示、异常注入是否触发告警/日志。 + +## 常见问题(为什么这么设计) +- 为什么不直接做真实韵达/圆通对接?因为联调成本高且不可控,Mock 能让你先把平台侧工程能力打牢。 +- 为什么需要控制面 API?因为测试需要“可复现/可回放/可注入”,而不是只等外部推送。 +- 为什么要状态映射?因为不同承运方文案与事件码不稳定,统一状态才能做稳定 UI 与统计。 + +文件列表: +- 配送模块需求文档.md:Mock 承运方 Server 的目标、范围、流程与验收。 +- 接口规范.md:Mock Server 控制面 API + Mock -> 平台 Webhook 推送规范。 +- 状态映射表.md:Mock 事件码到平台统一状态映射建议。 +- 前端字段清单.md:时间线组件所需字段契约与展示规则。 + +数据库: +- `mall_sql/schemas/express_tracking_mock_platform.sql`:本体系建议的 Postgres 建表脚本(平台统一入库 + Mock 持久化)。 + +建议下一步:在平台侧实现一个可切换的“Mock 数据源”开关(仅测试环境),并在 QA 用例中覆盖重复/乱序/验签失败等注入场景。 diff --git a/pages/mall/delivery/doc/需求文档/express_tracking_mock_platform.sql b/pages/mall/delivery/doc/需求文档/express_tracking_mock_platform.sql new file mode 100644 index 00000000..f0f6c44a --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/express_tracking_mock_platform.sql @@ -0,0 +1,294 @@ +-- 物流轨迹(第三方/Mock)数据库设计(PostgreSQL / Supabase 兼容) +-- 目标: +-- 1) 平台侧:统一接收不同第三方事件 -> 幂等去重/乱序入库 -> 给前端提供稳定时间线 +-- 2) Mock 承运方侧:可持久化运单/事件/场景/推送日志,支持故障注入与回放 +-- 说明:本文件仅提供表结构与索引建议;RLS/权限策略按项目实际补充。 + +BEGIN; + +-- Supabase / Postgres 常用扩展 +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +-- 通用 updated_at 维护函数(如项目已有同名函数,可忽略重复定义) +CREATE OR REPLACE FUNCTION public.set_updated_at() +RETURNS TRIGGER +LANGUAGE plpgsql +AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$; + +-- ===================================================================== +-- A. 平台侧(platform):统一轨迹模型入库与查询 +-- ===================================================================== + +CREATE TABLE IF NOT EXISTS platform_express_waybills ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + -- 可选:若平台订单已存在,可关联 + order_id uuid NULL, + order_no varchar(64) NULL, + + carrier varchar(32) NOT NULL, -- YUNDA/YTO/ZTO/STO/KDN... + tracking_no varchar(64) NOT NULL, + source varchar(16) NOT NULL DEFAULT 'mock', -- mock/carrier/aggregator + + current_status_code varchar(32) NOT NULL DEFAULT 'PENDING', + current_status_text text NULL, + + eta timestamptz NULL, + last_synced_at timestamptz NULL, + + created_at timestamptz NOT NULL DEFAULT NOW(), + updated_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_platform_waybill UNIQUE (carrier, tracking_no) +); + +CREATE TRIGGER trg_platform_waybills_updated_at +BEFORE UPDATE ON platform_express_waybills +FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + +CREATE INDEX IF NOT EXISTS idx_platform_waybills_order_no ON platform_express_waybills(order_no); +CREATE INDEX IF NOT EXISTS idx_platform_waybills_tracking_no ON platform_express_waybills(tracking_no); +CREATE INDEX IF NOT EXISTS idx_platform_waybills_status ON platform_express_waybills(current_status_code); + + +-- 统一事件表:前端时间线/告警/统计的主数据来源 +CREATE TABLE IF NOT EXISTS platform_express_tracking_events ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + waybill_id uuid NOT NULL REFERENCES platform_express_waybills(id) ON DELETE CASCADE, + carrier varchar(32) NOT NULL, + tracking_no varchar(64) NOT NULL, + + -- 第三方事件唯一标识(可能缺失) + event_id varchar(128) NULL, + + event_time timestamptz NOT NULL, + event_code varchar(64) NOT NULL, + event_text text NOT NULL, + + status_code varchar(32) NOT NULL, -- 平台统一状态 + + node_name varchar(128) NULL, + location text NULL, + description text NULL, + + evidence_urls jsonb NOT NULL DEFAULT '[]'::jsonb, + + -- 原始回文(用于审计/排障;如担心体积可移到 raw 表或只存引用) + raw_payload jsonb NULL, + + -- 接收侧信息 + received_at timestamptz NOT NULL DEFAULT NOW(), + source varchar(16) NOT NULL DEFAULT 'webhook', -- webhook/poll/manual + + -- 幂等去重键:优先 event_id;缺失时用 tracking_no+event_code+event_time(+可选字段) 构造 + dedupe_key varchar(256) NOT NULL, + + created_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_platform_event_dedupe UNIQUE (waybill_id, dedupe_key) +); + +CREATE INDEX IF NOT EXISTS idx_platform_events_waybill_time ON platform_express_tracking_events(waybill_id, event_time); +CREATE INDEX IF NOT EXISTS idx_platform_events_tracking_time ON platform_express_tracking_events(tracking_no, event_time); +CREATE INDEX IF NOT EXISTS idx_platform_events_status ON platform_express_tracking_events(status_code); +CREATE INDEX IF NOT EXISTS idx_platform_events_received_at ON platform_express_tracking_events(received_at); +CREATE INDEX IF NOT EXISTS gin_platform_events_raw_payload ON platform_express_tracking_events USING gin (raw_payload); + + +-- 原始接收表:记录每一次 webhook/轮询原文、验签结果与解析错误 +CREATE TABLE IF NOT EXISTS platform_express_event_raw ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + received_at timestamptz NOT NULL DEFAULT NOW(), + source varchar(16) NOT NULL DEFAULT 'webhook', + + -- 请求侧标识 + client_id varchar(64) NULL, + carrier varchar(32) NULL, + tracking_no varchar(64) NULL, + + -- 安全审计 + signature_valid boolean NULL, + signature text NULL, + ts_header text NULL, + + request_id varchar(64) NULL, + remote_ip inet NULL, + headers jsonb NULL, + + body jsonb NULL, + parse_error text NULL, + + -- 去重辅助(可选):用于识别完全重复的原始请求 + dedupe_key varchar(256) NULL +); + +CREATE INDEX IF NOT EXISTS idx_platform_raw_received_at ON platform_express_event_raw(received_at); +CREATE INDEX IF NOT EXISTS idx_platform_raw_tracking_no ON platform_express_event_raw(tracking_no); +CREATE INDEX IF NOT EXISTS idx_platform_raw_signature_valid ON platform_express_event_raw(signature_valid); +CREATE INDEX IF NOT EXISTS gin_platform_raw_body ON platform_express_event_raw USING gin (body); + + +-- ===================================================================== +-- B. Mock 承运方侧(mock):运单/事件/场景/推送日志 +-- ===================================================================== + +CREATE TABLE IF NOT EXISTS mock_carrier_config ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + name varchar(64) NOT NULL DEFAULT 'default', + target_webhook_url text NULL, + + client_id varchar(64) NULL, + secret text NULL, + default_carrier varchar(32) NOT NULL DEFAULT 'YUNDA', + + is_active boolean NOT NULL DEFAULT true, + + created_at timestamptz NOT NULL DEFAULT NOW(), + updated_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_mock_config_name UNIQUE (name) +); + +CREATE TRIGGER trg_mock_carrier_config_updated_at +BEFORE UPDATE ON mock_carrier_config +FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +CREATE TABLE IF NOT EXISTS mock_waybills ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + carrier varchar(32) NOT NULL, + tracking_no varchar(64) NOT NULL, + order_no varchar(64) NULL, + + created_at timestamptz NOT NULL DEFAULT NOW(), + updated_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_mock_waybill UNIQUE (carrier, tracking_no) +); + +CREATE TRIGGER trg_mock_waybills_updated_at +BEFORE UPDATE ON mock_waybills +FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +CREATE TABLE IF NOT EXISTS mock_tracking_events ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + waybill_id uuid NOT NULL REFERENCES mock_waybills(id) ON DELETE CASCADE, + carrier varchar(32) NOT NULL, + tracking_no varchar(64) NOT NULL, + + event_id varchar(128) NULL, + event_time timestamptz NOT NULL, + event_code varchar(64) NOT NULL, + event_text text NOT NULL, + + node_name varchar(128) NULL, + location text NULL, + description text NULL, + evidence_urls jsonb NOT NULL DEFAULT '[]'::jsonb, + + raw_payload jsonb NULL, + + -- 生成幂等键,便于 Mock 内部也能去重 + dedupe_key varchar(256) NOT NULL, + + created_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_mock_event_dedupe UNIQUE (waybill_id, dedupe_key) +); + +CREATE INDEX IF NOT EXISTS idx_mock_events_waybill_time ON mock_tracking_events(waybill_id, event_time); + + +-- 场景定义:标准签收/拒收退回/地址异常等 +CREATE TABLE IF NOT EXISTS mock_scenarios ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + scenario_code varchar(64) NOT NULL, + scenario_name varchar(128) NOT NULL, + description text NULL, + + is_active boolean NOT NULL DEFAULT true, + + created_at timestamptz NOT NULL DEFAULT NOW(), + updated_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_mock_scenario_code UNIQUE (scenario_code) +); + +CREATE TRIGGER trg_mock_scenarios_updated_at +BEFORE UPDATE ON mock_scenarios +FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- 场景步骤:相对时间偏移 + 事件内容 +CREATE TABLE IF NOT EXISTS mock_scenario_steps ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + scenario_id uuid NOT NULL REFERENCES mock_scenarios(id) ON DELETE CASCADE, + + step_no int NOT NULL, + offset_seconds int NOT NULL DEFAULT 0, + + event_code varchar(64) NOT NULL, + event_text text NOT NULL, + status_code varchar(32) NULL, + + node_name varchar(128) NULL, + location text NULL, + description text NULL, + evidence_urls jsonb NOT NULL DEFAULT '[]'::jsonb, + + created_at timestamptz NOT NULL DEFAULT NOW(), + + CONSTRAINT uk_mock_scenario_step UNIQUE (scenario_id, step_no) +); + + +-- 场景运行记录:便于复现/回放 +CREATE TABLE IF NOT EXISTS mock_scenario_runs ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + waybill_id uuid NOT NULL REFERENCES mock_waybills(id) ON DELETE CASCADE, + scenario_id uuid NOT NULL REFERENCES mock_scenarios(id), + + inject jsonb NOT NULL DEFAULT '{}'::jsonb, -- delay_ms/duplicate/out_of_order/bad_signature/timestamp_skew_seconds/drop_fields... + pushed boolean NOT NULL DEFAULT false, + + started_at timestamptz NOT NULL DEFAULT NOW(), + finished_at timestamptz NULL +); + +CREATE INDEX IF NOT EXISTS idx_mock_runs_waybill ON mock_scenario_runs(waybill_id, started_at); + + +-- 推送日志:记录每次 push 的请求/响应摘要 +CREATE TABLE IF NOT EXISTS mock_push_logs ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + + waybill_id uuid NOT NULL REFERENCES mock_waybills(id) ON DELETE CASCADE, + event_id varchar(128) NULL, + dedupe_key varchar(256) NULL, + + target_url text NULL, + request_headers jsonb NULL, + request_body jsonb NULL, + + response_status int NULL, + response_body text NULL, + error_message text NULL, + + created_at timestamptz NOT NULL DEFAULT NOW() +); + +CREATE INDEX IF NOT EXISTS idx_mock_push_waybill ON mock_push_logs(waybill_id, created_at); + +COMMIT; diff --git a/pages/mall/delivery/doc/需求文档/前端字段清单.md b/pages/mall/delivery/doc/需求文档/前端字段清单.md new file mode 100644 index 00000000..b1194f5d --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/前端字段清单.md @@ -0,0 +1,63 @@ +# 前端字段清单(订单详情页物流时间线|安卓 & Web) + +说明:本清单用于平台前端展示“物流关键节点时间线”。数据来源为平台后端消费 Mock Server(或未来真实承运方)的事件后形成的统一模型。 + +本文定位:只定义“平台后端 -> 客户端”的展示字段契约与规则;Mock Server 的接口与字段以 `接口规范.md` 为准。 + +关联文档: +- `配送模块需求文档.md`:目标/范围/验收与故障注入 +- `接口规范.md`:统一事件模型、平台接收 Webhook、Mock 控制面 API +- `状态映射表.md`:event_code -> status_code 映射建议 + +适用端: +- 安卓端(App):重点是用户体验(弱网/图片预览/快速刷新)。 +- Web 端(H5/PC):重点是排障效率(筛选/搜索/原始事件折叠展示)。 + +1. 订单详情(delivery block) +- `order_no`:订单号 +- `tracking_no` / `waybill_no`:运单号(可能为空) +- `carrier`:承运商标识(YUNDA/YTO/KDN 等) +- `status`:平台统一状态码(PENDING/IN_TRANSIT/ARRIVED_HUB/OUT_FOR_DELIVERY/DELIVERED/EXCEPTION/RETURNED) +- `status_history`:事件数组,元素结构 `{event_id, event_time, event_code, event_text, status_code, node_name, location, description, evidence_urls, source}` +- `eta`:预计到达时间(ISO8601,若承运方或 ETA 服务提供) +- `pod`:签收凭证数组,元素 `{type: photo|signature, url, timestamp, remark}` +- `receiver_name`:收件人姓名(仅客服可查看完整) +- `receiver_masked_phone`:脱敏手机号(前端展示) +- `receiver_address`:收货地址(可按需隐藏部分字段) +- `last_synced_at`:最后同步时间(平台本地) +- `source`:来源(mock / carrier / aggregator),用于 UI 标注与排障 + +2. 管理/调试视图字段(可选) +- `mock_enabled`:是否开启 mock 数据 +- `mock_carrier`:当前模拟承运方 +- `webhook_last_result`:最近一次 webhook 推送结果摘要(仅调试页面) + +3. 行为与按钮映射(平台侧) +- 刷新物流 -> GET /api/v1/delivery/express/track(平台查询自身已入库事件;或平台发起轮询) +- 上报异常 -> POST /api/v1/delivery/express/report-exception + +(联调阶段可选)平台侧调试按钮(调用 Mock Server 控制面 API): +- 运行标准场景 -> POST /mock/v1/waybills/{tracking_no}/run-scenario +- 触发推送 -> POST /mock/v1/waybills/{tracking_no}/push + +4. 展示规则 & 隐私 +- 所有时间使用 ISO8601(含时区)。 +- 手机号必须脱敏(如 138****8000),完整手机号仅在客服/运维界面显示并记录访问审计。 +- 证据 URL 应带鉴权或短期有效,前端需支持预览与下载。 + +5. 安卓端适配要点 +- 缓存:将最近一次 `status_history` 缓存在本地(仅该用户的订单范围),离线可展示并提示“数据可能不是最新”。 +- 图片:POD/证据图片支持大图预览与失败重试;弱网下优先加载文本节点。 +- 刷新:进入页面自动拉取;下拉刷新;显示 `last_synced_at`。 + +6. Web 端适配要点 +- 调试信息:`raw_payload` 默认折叠,仅客服/运维可展开查看。 +- 筛选搜索:按 `status_code`、时间范围、承运方 `carrier` 过滤;支持按 `tracking_no` 搜索。 +- 安全:避免在前端日志中打印 `raw_payload` 与敏感字段。 + +7. 兼容性 +- 前端组件可接收 `raw_payload`(仅供客服/运维查看)并折叠显示。 +- 时间线组件需支持展开单条事件详情、证据大图预览与来源标注(承运方/聚合方)。 + +--- +(以上为前端字段与契约建议,实际字段名可与后端协商最终确认) diff --git a/pages/mall/delivery/doc/需求文档/后台页面设计说明.md b/pages/mall/delivery/doc/需求文档/后台页面设计说明.md new file mode 100644 index 00000000..3e93c427 --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/后台页面设计说明.md @@ -0,0 +1,194 @@ +# 后台页面设计说明(配送模块|平台对接第三方轨迹) + +本文定位: +- 定义“后台应该有什么样的页面”与每个页面的最小展示/操作/权限要求。 +- 本文服务于平台系统的一部分:订单详情页物流区块 + 客服/履约/对接运维排障。 + +口径说明:本目录文档作为实现与验收唯一口径;如页面形态与本文不一致,以本文更新为准。 + +关联文档: +- `配送模块需求文档.md`:范围/目标/验收与总体定位 +- `接口规范.md`:Webhook/控制面接口与入库建议 +- `前端字段清单.md`:订单详情页物流区块字段契约(安卓/Web) +- `状态映射表.md`:event_code/event_text -> status_code 映射与一致性治理 +- `缺口与待补充清单.md`:需补齐的验收/场景/权限矩阵等 + +--- + +## 1. 角色与权限(页面设计前提) + +角色建议: +- 普通用户(消费者端):仅查看自己订单物流(不在本后台范围内) +- 商家(商户后台):发货与查看订单物流(不看 raw_payload) +- 客服:查看订单物流、辅助处理异常(有限查看 raw_payload,需审计) +- 履约/运营:看指标、看异常趋势、做配置(按权限) +- 对接运维:看 webhook 日志、验签结果、配置密钥与回调、排障(可看 raw_payload,需审计) + +强约束: +- `raw_payload`、完整手机号/地址、签收凭证(POD)属于敏感信息:默认折叠、按角色授权、访问需审计。 + +### 1.1 页面拆分原则 & 权限控制原则(避免跑偏) + +结论:页面是否拆分不只由“权限”决定;更主要由“工作流/信息密度/风险”决定,权限用于控制同一页面内的可见字段与可操作按钮。 + +页面拆分建议(什么时候需要单独页面/专区): +- 工作流不同:商家做发货/运单绑定;客服做工单/异常处理;运维做对接配置/排障。 +- 信息密度不同:面向商家的页面应短而清晰;面向运维的页面需要更细的日志与诊断信息。 +- 风险不同:密钥/证书、回调配置、`raw_payload` 查看等高敏能力建议放在“对接运维专区”或独立页面,降低误操作与暴露面。 + +权限控制建议(同一页面内怎么按角色展示): +- 字段级:例如 `raw_payload`、完整手机号/地址、POD 仅授权角色可见;默认折叠。 +- 操作级:例如“触发回查/补采”“修改接入配置/密钥”“启用 Mock 开关”等仅运维可操作。 +- 审计级:对敏感字段访问与关键配置变更必须记录审计。 + +推荐做法: +- 信息架构先按人群拆两条导航线(商家后台 vs 平台后台)。 +- 在每个页面内部再做字段/按钮的权限控制(同页不同角色看到的内容不同)。 + +--- + +## 2. 信息架构(IA)与导航建议 + +建议将后台拆成两条导航线: + +A. 商家后台(商户侧) +- 订单 + - 订单列表 + - 订单详情(含物流区块 + 发货/运单绑定) +- 设置(可选) + - 默认承运方/发货偏好 + +B. 平台后台(客服/运维侧) +- 订单 + - 订单查询 + - 订单详情(含物流区块) +- 物流对接 + - 运单/轨迹查询(排障) + - Webhook 接收日志 + - 接入配置中心(承运方/聚合配置) + - 监控与告警 + - 状态映射治理(可选增强) + +--- + +## 3. 商家后台页面(最小集) + +### 3.1 订单列表页(商家) +目的:商家快速定位需要发货/查看物流的订单。 + +最小展示: +- `order_no`、下单时间、收件人(脱敏)、订单状态、发货状态 +- 物流摘要(若已发货):`carrier`、`tracking_no`、当前 `status_code` + +最小操作: +- 查看订单详情 +- 发货(跳转/弹窗进入“承运方选择 + 运单号绑定”) + +### 3.2 订单详情页(商家,含物流区块) +目的:完成发货动作,并查看物流时间线。 + +物流区块展示(以 `前端字段清单.md` 为准): +- `carrier`、`tracking_no`、`status`、`status_history`、`last_synced_at` + +操作: +- 发货:选择承运方 + 录入/回填 `tracking_no` +- 刷新物流:调用平台统一查询(不直连第三方) + +禁止/限制: +- 不展示 `raw_payload` +- 收件信息按平台规则脱敏 + +--- + +## 4. 平台后台页面(客服/履约/对接运维) + +### 4.1 订单查询页(平台) +目的:客服/履约按订单维度查找并进入详情处理。 + +筛选: +- `order_no`、`tracking_no`、`carrier`、`status_code`、时间范围 + +展示: +- 订单摘要 + 物流摘要(当前状态、最后同步时间) + +### 4.2 订单详情页(平台,含物流区块加强版) +目的:在订单上下文内查看完整物流与异常。 + +展示: +- 物流区块:同商家侧字段 + 事件来源标注(mock/承运方/聚合) +- 异常标记:长时间无更新、签收失败、退回等(按规则) +- POD(可选):签收凭证预览(按权限) + +操作: +- 刷新/回查(可选增强):触发平台轮询补偿或第三方回查 +- 备注/工单关联(可选):记录客服处理信息 + +### 4.3 运单/轨迹查询页(平台排障核心) +目的:脱离订单也能按运单快速排障。 + +筛选: +- `tracking_no`、`carrier`、`status_code`、时间范围、来源(mock/承运方/聚合) + +展示: +- 事件时间线(入库后统一模型) +- 幂等/乱序结果提示(例如:去重命中次数、最新事件时间) + +操作: +- 复制对接诊断信息(不含敏感字段) + +### 4.4 Webhook 接收日志页(平台对接运维) +目的:定位“回调未到/验签失败/重复推送/入库失败”。 + +筛选: +- 时间范围、`carrier`、`tracking_no`、验签结果、HTTP 状态、处理结果 + +展示: +- 请求摘要:`X-Client-Id`、`X-Timestamp`、签名校验结果 +- 处理摘要:解析成功/失败原因、幂等键、入库结果 +- 原始回文:`raw_payload` 折叠展示(仅授权角色可见,且记录访问审计) + +### 4.5 接入配置中心(平台对接运维) +目的:管理承运方/聚合接入与回调配置。 + +配置项: +- 承运方列表与状态(启用/停用) +- 验签密钥/证书、公网 IP 白名单(如需要) +- Webhook 目标地址与环境(测试/预发/生产) +- 轮询补偿策略:频率、阈值(多久无更新触发)、熔断 +- 测试环境 Mock 开关(默认关闭,需显式开启) + +### 4.6 监控与告警页(平台履约/运维) +目的:按承运方/接口维度观察质量。 + +指标建议: +- webhook 接收成功率、验签失败率、平均同步延迟 +- 超过阈值未更新的运单数 +- 第三方接口错误率/超时率(轮询补偿场景) + +### 4.7 状态映射治理页(可选增强) +目的:让 `event_code -> status_code` 可治理、可追溯。 + +展示: +- 当前映射表、变更记录、(可选)映射版本 + +--- + +## 5. MVP(最小可上线范围) + +商家后台: +- 订单列表 + 订单详情(含发货与物流区块) + +平台后台: +- 运单/轨迹查询 +- Webhook 接收日志(含验签/去重/入库结果) +- 接入配置中心(至少支持配置承运方/密钥/回调目标) +- 监控与告警(至少有基础指标与列表) + +--- + +## 6. 验收要点(页面层) + +- 订单详情页物流区块字段与展示规则符合 `前端字段清单.md` +- 平台后台能从运单号定位到:事件时间线 + webhook 接收记录 + 验签结果 + 入库结果 +- `raw_payload` 默认不可见/折叠,且按权限展示并有审计 +- Mock 开关默认关闭,且仅在测试环境允许启用 diff --git a/pages/mall/delivery/doc/需求文档/接口规范.md b/pages/mall/delivery/doc/需求文档/接口规范.md new file mode 100644 index 00000000..773fe154 --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/接口规范.md @@ -0,0 +1,512 @@ +# 配送对接接口规范(模拟三通一达后台 / Mock 承运方 Server) + +本文档用于定义“模拟承运方后台服务(Mock Server)”的接口与行为,供平台在未接入真实承运方前完成联调与测试。 + +本文定位: +- 规定 Mock Server 与平台后端之间的交互(Webhook 推送 + 控制面 API)。 +- 规定平台侧建议的统一事件模型(用于入库、时间线展示、监控告警)。 + +关联文档: +- `配送模块需求文档.md`:目标/范围/验收与必须的故障注入 +- `状态映射表.md`:状态码映射建议(Mock event_code -> 平台 status_code) +- `前端字段清单.md`:安卓/Web 时间线展示字段契约 + +包含两类接口: +1) Mock Server -> 平台:Webhook 事件推送(模拟承运方回调) +2) 平台/测试工具 -> Mock Server:控制面 API(创建运单、追加事件、运行场景、触发推送、查询轨迹) + +## 统一约定 +- 时间格式:ISO 8601(示例:"2026-02-05T10:30:00+08:00") +- 所有接口请求/响应均为 JSON,Content-Type: application/json +- 写操作建议携带 `request_id` 用于幂等与排查 +- 所有第三方回调应保存 `raw_payload` 用于审计 + +补充说明(安卓端 & Web 端使用场景): +- 本文主要规范 Mock Server 与平台后端的交互;安卓/Web 客户端不应直接调用 Mock Server。 +- Web 端联调时如需浏览器直连平台接口,应由平台网关配置 CORS(仅测试环境白名单),避免将 Mock Server 暴露给公网。 + +## 一、目标与约束 +- 目标:用可控数据模拟关键配送节点与异常场景,覆盖验签、幂等、乱序/重复、延迟等测试。 +- 约束:本规范服务于测试/预发布环境,不用于生产承运方直连。 + +## 二、核心数据模型(建议统一存储字段) +- `carrier`:承运方标识(例如:YUNDA、YTO、KDN) +- `tracking_no`:运单号 +- `event_id`:第三方事件唯一ID(用于去重) +- `event_time`:事件时间(ISO8601) +- `event_code`:第三方原始事件码(尽量不改写,用于审计与一致性对齐) +- `event_text`:第三方原始事件文案(前端时间线默认展示) +- `status_code`:平台统一状态(PENDING, IN_TRANSIT, ARRIVED_HUB, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION) +- `node_name`:节点名称(中转站/网点/城市) +- `location`:节点位置描述(城市/网点地址,注意隐私) +- `description`:事件详细描述 +- `evidence_urls`:证据照片/签名链接数组 +- `estimated_arrival`:ETA(若承运方提供,可选) +- `raw_payload`:原始第三方 JSON(审计) +- `last_synced_at`:本地同步时间 + +## 三、接入模式 +- 平台接收 Mock Server 的 Webhook 推送(推荐)。 +- 平台亦可主动调用 Mock Server 查询轨迹(模拟轮询补偿)。 + +## 四、接口定义(示例) + +1) Mock Server -> 平台:事件推送(Webhook) +- URL: POST /webhook/express/status +- 描述: 承运方/聚合方主动推送运单事件(单条或批量事件)。平台应校验签名并返回 HTTP 200 表示成功接收。 +- HTTP 头部(建议): + - `Content-Type: application/json` + - `X-Client-Id`: 承运方或聚合方ID + - `X-Timestamp`: 推送时间戳(防重放) + - `X-Signature`: HMAC-SHA256(body + X-Timestamp) 使用双方共享 secret +- 回调示例(单事件): +{ + "tracking_no":"YD123456789", + "carrier":"YUNDA", + "event_id":"e_20260205_0001", + "event_code":"ARRIVED_HUB", + "event_text":"到达北京分拨中心", + "event_time":"2026-02-05T14:32:00+08:00", + "node_name":"北京分拨中心", + "location":"北京市朝阳区XXX", + "evidence_urls":[], + "raw_payload":{/* 原始承运方 JSON */} +} + +处理要求: +- 平台校验 `X-Signature` 和 `X-Timestamp`;若验签失败或时间差过大返回 4xx。若成功返回 HTTP 200。接收后异步完成映射与入库。 +- 幂等:基于 `event_id` 或 `tracking_no+event_code+event_time` 去重。 + +2) 平台 -> Mock Server:轨迹主动拉取(模拟轮询) +- URL: GET /mock/v1/track?tracking_no={tracking_no}&carrier={carrier} +- 描述: 平台主动查询单运单轨迹,返回事件数组。 +- 响应示例: +{ + "tracking_no":"YD123456789", + "carrier":"YUNDA", + "events":[ + {"event_id":"e1","event_code":"PICKED","event_text":"已揽收","event_time":"2026-02-04T18:00:00+08:00","node_name":"门店揽收网点","location":"北京市顺义"}, + {"event_id":"e2","event_code":"ARRIVED_HUB","event_text":"到达北京分拨中心","event_time":"2026-02-05T14:32:00+08:00","node_name":"北京分拨中心","location":"北京市朝阳区"} + ] +} + +3) 平台:异常上报 API(用户/客服触发,平台侧,不属于 Mock Server) +- URL: POST /api/v1/delivery/express/report-exception +- 说明: 该接口为平台自身能力,Mock Server 不强制实现。 +- 请求示例: +{ + "tracking_no":"YD123456789", + "reported_by":"user", + "report_type":"damaged", + "description":"包裹外包装破损,有压痕", + "evidence_urls":["https://.../img1.jpg"] +} +- 平台可选地回传给真实承运方(若未来接入)并记录回传结果。 + +4) 平台/测试工具 -> Mock Server:控制面 API(建议实现) + +4.1 配置回调目标 +- URL: POST /mock/v1/config +- 描述: 设置 Mock Server 推送到平台的 Webhook 目标地址与验签参数。 +- 请求示例: +{ + "target_webhook_url":"https://api.yourplatform.com/webhook/express/status", + "client_id":"carrier_mock_yunda", + "secret":"shared_secret", + "default_carrier":"YUNDA" +} + +4.2 创建运单 +- URL: POST /mock/v1/waybills +- 描述: 创建一个 mock 运单,可由调用方指定或由服务生成。 +- 请求示例: +{ + "carrier":"YUNDA", + "tracking_no":"YD123456789", + "order_no":"202602050001" +} +- 响应示例: +{ + "carrier":"YUNDA", + "tracking_no":"YD123456789", + "created_at":"2026-02-05T14:00:00+08:00" +} + +4.3 追加事件(不推送,仅入库) +- URL: POST /mock/v1/waybills/{tracking_no}/events +- 请求示例: +{ + "event_id":"e_20260205_0002", + "event_code":"ARRIVED_HUB", + "event_text":"到达北京分拨中心", + "event_time":"2026-02-05T14:32:00+08:00", + "node_name":"北京分拨中心", + "location":"北京市朝阳区", + "evidence_urls":[] +} + +4.4 运行预置场景(生成一组事件并可选立即推送) +- URL: POST /mock/v1/waybills/{tracking_no}/run-scenario +- 请求示例: +{ + "scenario":"standard_delivered", + "push":true, + "inject":{ + "delay_ms":300, + "duplicate":0, + "out_of_order":false, + "bad_signature":false, + "timestamp_skew_seconds":0 + } +} + +4.5 触发推送(把该运单事件推到平台) +- URL: POST /mock/v1/waybills/{tracking_no}/push +- 请求示例: +{ + "mode":"all", + "inject":{ + "delay_ms":0, + "duplicate":1, + "out_of_order":true + } +} + +4.6 查询运单轨迹(Mock Server 自身查询接口) +- URL: GET /mock/v1/waybills/{tracking_no}/track +- 响应示例: +{ + "carrier":"YUNDA", + "tracking_no":"YD123456789", + "events":[ + {"event_id":"e1","event_code":"PICKED","event_text":"已揽收","event_time":"2026-02-04T18:00:00+08:00"} + ] +} + +4.7 健康检查 +- URL: GET /mock/v1/health +- 响应示例: +{"status":"ok"} + +## 五、字段映射与状态对照(示例) +- 平台统一状态 `OUT_FOR_DELIVERY` 映射关系示例: + - 韵达:承运方 `派送中` -> 平台 `OUT_FOR_DELIVERY` + - 聚合:原始 `deliver` -> 平台 `OUT_FOR_DELIVERY` + +状态一致性策略(尽量保持平台与第三方一致): +- 一致性定义:平台保存的 `event_code/event_text` 与 `raw_payload` 应与第三方可查询到的轨迹语义一致;平台对外展示的 `status_code` 必须由映射表确定性生成(同一承运方同一 `event_code` 在同一版本映射下得到同一 `status_code`)。 +- 字段保真:`event_code/event_text` 仅做透传与脱敏展示,不建议“为了统一文案”而改写原文;统一展示口径使用 `status_code` 的标签/颜色/筛选实现。 +- 映射表治理:映射表变更需要版本化(例如 `mapping_version`)并走发布流程;避免在生产环境频繁调整导致历史轨迹“同码不同态”。如必须调整,建议补充回放/回填策略以保证历史一致。 +- 回查纠偏(可选增强):平台定期或按需对“关键单/争议单”触发第三方轨迹回查;若发现缺失事件则补采入库;若发现语义冲突,优先保留原始事件并追加“平台侧纠偏事件/备注”,避免静默覆盖造成审计对不上。 + +## 六、验签与安全 +- 建议使用 HMAC-SHA256 签名,签名字段为 `body + X-Timestamp`,服务端使用共享 `secret` 校验。 +- 防重放:校验 `X-Timestamp` 与当前时间差(例如 < 5 分钟),并保存最近 N 个 `X-Signature` 或 `event_id` 用于去重。 + +## 七、幂等与重试策略 +- webhook:承运方可能重试多次,平台必须基于 `event_id` 或 (`tracking_no`+`event_code`+`event_time`) 去重。 +- 主动调用承运方接口失败时采用指数退避重试;关键操作(打单)建议入队异步重试并人工告警。 + +入库层(幂等去重、乱序处理)建议: +- 幂等目标:同一条事件无论推送/重试多少次,最终数据库只保留一条(或同一条被安全更新),避免重复入库造成时间线膨胀。 +- 幂等键优先级: + - 优先使用第三方提供的 `event_id`。 + - 若缺少 `event_id`,使用兜底组合键:`tracking_no + event_code + event_time`(必要时可补充 `node_name/location` 以降低碰撞)。 +- 数据库约束:建议在事件表上建立唯一约束(例如 `carrier + tracking_no + event_id` 或 `tracking_no + dedupe_key`),写入采用 Upsert/Insert-Ignore,应用层无需“先查再写”来保证并发安全。 +- 乱序处理:允许事件乱序写入(以免晚到的历史节点被丢弃);查询展示时按 `event_time` 排序生成时间线。 +- 状态不回退(平台侧可选规则):当先收到终态(如 `DELIVERED`)后又收到更早时间的在途事件时,不应将订单状态从终态回退;可采用“以最新 `event_time` 事件计算 current_status”或“按状态流转等级仅允许前进”的策略。 + +## 八、展示与 UI 要求(与前端对接点) +- 时间线按 `event_time` 展示,标注 `carrier` 来源与 `last_synced_at`。 +- 节点可展开查看 `description`、`node_name`、`location`、`evidence_urls`(签收照片/回单)。若无证据则隐藏预览。 +- 提供“查看第三方原文”链接,展示 `raw_payload`(仅客服/运维可见)。 + +平台对客户端返回建议(安卓/Web): +- 客户端请求应面向平台统一接口(例如:`GET /api/v1/delivery/express/track?order_no=...`),由平台返回统一的 `status_history`。 +- 响应中建议包含:`carrier`、`tracking_no`、`status`、`status_history`、`last_synced_at`,以及可选的 `eta`。 +- `raw_payload` 不建议下发给普通用户端;仅在 Web 客服/运维界面按权限下发并记录审计。 + +## 九、监控与告警 +- 指标:webhook 接收成功率、平均同步时延、同步失败率、超过阈值未更新的运单数。 +- 告警:承运方 5xx 增加、webhook 验签失败率异常、单运单长时间无更新。 + +## 十、示例 cURL(Webhook 验证场景模拟) +``` +curl -X POST https://api.yourplatform.com/webhook/express/status \ + -H "Content-Type: application/json" \ + -H "X-Client-Id: carrier_yunda" \ + -H "X-Timestamp: 2026-02-05T14:32:00+08:00" \ + -H "X-Signature: " \ + -d '{"tracking_no":"YD123456789","event_id":"e_20260205_0001","event_code":"ARRIVED_HUB","event_time":"2026-02-05T14:32:00+08:00","event_text":"到达北京分拨中心"}' +``` + +## 十一、日志与保留策略 +- 建议保留 `raw_payload` 与入库审计日志至少 30 天以便纠纷处理,审计记录包括接收时间、IP、头部信息与处理结果。 + +## 十二、兼容与扩展建议 +- 建议实现承运商 Adapter 层:承运方差异在 Adapter 层转换为统一模型,便于后续扩展新承运方或替换聚合方。 +- 若需签收照片或更高粒度信息,应优先与承运方签订企业级合同或直连,并在 UI 端明确注明凭证来源。 + +### 十二点一、平台后端适配架构(推荐实现方式) + +目标:当不同第三方(直连承运方/聚合平台)API 结构、鉴权方式、事件码不同的时候,平台侧**不修改核心业务与数据库结构**即可接入。 + +核心原则: +- **统一领域模型入库**:第三方差异在入库前完成映射;数据库保存统一事件字段 + `raw_payload`。 +- **Adapter 可插拔**:每家第三方实现一个 Adapter(验签/解析/查询/映射),核心服务只处理统一模型。 +- **接入入口两条线**:Webhook(推送)与 Poller(轮询补偿)最终都走同一条 `EventIngestService`。 + +模块划分(建议): +- `WebhookController`:接收回调、路由到对应 Adapter、快速返回 200(业务异步处理)。 +- `CarrierAdapter`(每家一个):验签、解析、字段映射、状态映射、(可选)轨迹查询。 +- `AdapterRegistry/Router`:按 `carrier` 或 `X-Client-Id` 选择 Adapter(支持灰度与多配置)。 +- `EventIngestService`:幂等去重、乱序处理策略、统一入库、写审计日志。 +- `TrackQueryService`:给安卓/Web 提供统一查询接口(只读),不透出第三方差异。 +- `PollerJob`:定时对“长时间无更新”的运单做轮询补偿(可按承运方频率配置)。 + +幂等与乱序建议(平台侧): +- 幂等键优先级:`event_id`(优先) > `tracking_no + event_code + event_time`(兜底)。 +- 入库:允许乱序写入;查询展示时按 `event_time` 排序。 +- 兼容缺字段:缺少 `event_id` 时必须使用兜底组合键;缺少 `location/node_name` 时 UI 降级仅展示 `event_text`。 + +### 架构图(Mermaid) +```mermaid +flowchart LR + subgraph Clients[客户端] + A[安卓 App] -->|GET 物流时间线| PAPI[平台 API] + W[Web/H5/PC] -->|GET 物流时间线| PAPI + end + + subgraph Platform[平台后端] + PAPI --> TQS[TrackQueryService\n统一查询] + TQS --> DB[(Logistics DB\nWaybill + TrackingEvent)] + + WH[WebhookController\n接收回调] --> REG[AdapterRegistry/Router\n按carrier或X-Client-Id路由] + REG --> AD1[YUNDA Adapter] + REG --> AD2[YTO Adapter] + REG --> AD3[Aggregator Adapter\n(快递鸟/快递100)] + + AD1 --> ING[EventIngestService\n幂等/映射/入库] + AD2 --> ING + AD3 --> ING + ING --> DB + + PJ[PollerJob\n轮询补偿] --> REG + PJ -->|queryTrack| AD1 + PJ -->|queryTrack| AD2 + PJ -->|queryTrack| AD3 + end + + subgraph ThirdParty[第三方/Mock] + MS[Mock Server 或真实承运方/聚合] -->|Webhook 推送| WH + end +``` + +### 回调时序图(Webhook 推送 -> 入库 -> 客户端展示) +```mermaid +sequenceDiagram + participant TP as Mock/第三方 + participant WH as WebhookController + participant REG as AdapterRegistry + participant AD as CarrierAdapter + participant ING as EventIngestService + participant DB as LogisticsDB + participant API as 平台查询API + participant APP as 安卓/Web + + TP->>WH: POST /webhook/express/status (headers + body) + WH->>REG: resolveAdapter(carrier/X-Client-Id) + REG->>AD: getAdapter() + WH->>AD: verify + parseWebhook(rawBody) + AD-->>WH: NormalizedEvent[] + WH-->>TP: 200 OK (快速返回) + WH->>ING: ingest(NormalizedEvent[]) + ING->>DB: upsert events (幂等去重) + + APP->>API: GET /api/v1/delivery/express/track?order_no=... + API->>DB: query events (order_no/tracking_no) + DB-->>API: events ordered by event_time + API-->>APP: timeLine(status_history + last_synced_at) +``` + +备注:Mermaid 图在不支持的 Markdown 渲染器中会降级为代码块,不影响内容阅读。 + +## 十三、附录:不同第三方 API 形态示例(用于理解差异) + +说明:以下为“常见形态示例”,用于帮助团队理解不同第三方接口在鉴权、字段与能力上的差异。 +- 不保证与任一承运方/聚合平台的官方文档 100% 一致;对接时必须以官方文档、沙箱与实际回文为准。 +- 推荐做法:在平台侧实现 Adapter/Mapper,把第三方差异映射为本文定义的统一事件模型再入库。 + +### 13.1 聚合平台常见形态(如快递100/快递鸟类) + +典型能力: +- 轨迹查询 API:传入 `company_code + tracking_no`(有些场景需要收件人手机号后四位) +- 订阅/推送:先订阅,后续以 Webhook 回调推送事件 +- 鉴权:常见 `appKey + sign`(MD5/HMAC)或 Token + +示例:轨迹查询(示例) +```http +POST https://api.aggregator.example.com/v1/track/query +Content-Type: application/json + +{ + "company_code": "yunda", + "tracking_no": "430123456789", + "phone_last4": "8000", + "nonce": "n_123", + "timestamp": 1738735200, + "sign": "md5_or_hmac_signature" +} +``` + +示例响应(示例) +```json +{ + "success": true, + "tracking_no": "430123456789", + "company_code": "yunda", + "state": "in_transit", + "events": [ + { "time": "2026-02-05T10:12:00+08:00", "context": "已揽收", "location": "深圳市" }, + { "time": "2026-02-05T22:01:00+08:00", "context": "到达广州分拨中心", "location": "广州市" } + ] +} +``` + +差异点: +- `state` / `status` 枚举不统一;事件字段可能是 `context/desc`、`time/timestamp`。 +- 不同快递公司可能要求手机号参与查询或订阅验证。 + +### 13.2 承运方直连常见形态(企业接口) + +典型能力: +- 轨迹查询、签收回单(POD)查询可能拆成多个接口 +- 鉴权更严格:HMAC、证书、IP 白名单或 OAuth2 + +示例:轨迹查询(示例) +```http +POST https://open.carrier.example.com/v2/route/query +Content-Type: application/json +X-Client-Id: your_client_id +X-Timestamp: 2026-02-05T10:30:00+08:00 +X-Signature: hmac_sha256(body + timestamp) + +{ + "tracking_no": "SF1234567890", + "include_pod": true +} +``` + +示例响应(示例) +```json +{ + "tracking_no": "SF1234567890", + "routes": [ + { "code": "PICKED", "desc": "已揽收", "time": "2026-02-05T09:10:00+08:00", "station": "深圳XX营业点" }, + { "code": "ARRIVED_HUB", "desc": "到达分拨中心", "time": "2026-02-05T21:45:00+08:00", "station": "广州分拨中心" } + ], + "pod": { "signed": false, "photo_url": null } +} +``` + +差异点: +- 字段更细(网点编码、操作员等),但不统一;POD 能力通常受权限/合同影响。 + +### 13.3 Webhook 推送常见形态(订阅后回调) + +典型能力: +- 订阅后由第三方主动推送轨迹事件到平台 +- 具备重试机制,因此平台必须做幂等去重、乱序处理 + +示例:回调事件(示例,字段名因第三方而异) +```json +{ + "tracking_no": "430123456789", + "carrier": "YUNDA", + "event_id": "evt_0001", + "event_time": "2026-02-05T22:01:00+08:00", + "event_text": "到达广州分拨中心", + "location": "广州市", + "extra": { "pod_photo": null } +} +``` + +接入建议: +- 无论第三方字段如何变化,平台入库前统一映射到本文“核心数据模型”,并保留 `raw_payload`。 +- 平台用 `event_id`(优先)或组合键(`tracking_no+event_code+event_time`)实现幂等。 + +### 13.4 圆通(YTO)物流轨迹查询接口(官方文档摘要) + +文档入口: +- https://open.yto.net.cn/interfaceDocument/menu251/submenu258 + +定位:该文档描述“根据圆通运单号查询物流轨迹”的接口形态,典型属于“平台主动查询(轮询)”模式。 + +关键交互要点(按官方文档整理,具体以控制台配置与最新文档为准): +- 传输:HTTPS,POST。 +- 报文结构:请求体包含 `timestamp`、`param`、`format`、`sign`。 +- `param`:以 JSON/XML 字符串承载业务参数;轨迹查询场景下包含圆通运单号字段(示例为 `NUMBER`,一次查询一个单号)。 + +签名规则(按文档描述抽象): +- 参与签名的明文:`param + method + v`(其中 `method` 与 `v` 来自控制台为该接口生成的配置)。 +- 将上述明文与客户密钥(`secret`)拼接后做 MD5,再对 MD5 的字节结果进行 Base64 编码,得到 `sign`。 +- 伪公式:`sign = base64(md5((param + method + v) + secret))` + +响应字段(按文档列举的 JSON 返回字段抽象): +- 运单号:`waybill_No` +- 走件时间:`upload_Time`(yyyy-MM-dd HH:mm:ss) +- 物流状态码:`infoContent`(示例枚举包括 GOT/ARRIVAL/DEPARTURE/SENT_SCAN/SIGNED 等) +- 物流信息文案:`processInfo` +- 城市/区县:`city`、`district`(可选) +- 重量:`weight`(可选) + +平台侧映射建议(把第三方差异收敛到统一模型): +- 时间:`upload_Time` -> 平台 `event_time` +- 文案:`processInfo` -> 平台 `event_text`(可另存 `description`) +- 状态:`infoContent` -> 平台 `event_code`(保留原码),再映射到平台统一 `status_code`(见 `状态映射表.md`) + +`infoContent` 到平台统一 `status_code` 的建议映射(示例): +- GOT(已揽收)-> IN_TRANSIT +- ARRIVAL(已收入/到达)-> ARRIVED_HUB +- DEPARTURE(已发出/离开节点)-> IN_TRANSIT +- SENT_SCAN(派件)-> OUT_FOR_DELIVERY +- INBOUND(自提柜入柜)-> IN_TRANSIT(或按业务定义为 OUT_FOR_DELIVERY) +- SIGNED(签收成功)-> DELIVERED +- FAILED(签收失败)-> EXCEPTION +- TMS_RETURN(退回)-> RETURNED + +落库建议: +- 将圆通原始返回(整个数组或单条对象)保存到 `raw_payload`,便于客服/运维对照圆通官网。 +- 幂等去重:若第三方无稳定 `event_id`,可用组合键 `tracking_no + infoContent + upload_Time` 生成 `dedupe_key`。 + +### 13.5 韵达(YUNDA)开放平台(官方文档入口与调研清单) + +文档入口: +- https://open.yundaex.com/api/apiDoc + +说明:韵达开放平台文档页面存在较多交互式内容(需登录/控制台配置后才能看到每个接口的 `method/v/测试地址` 等关键信息)。因此本节先固化“对接时必须确认的要点清单”,避免对接过程中遗漏。 + +从官方文档导航可见的能力分类(用于判断覆盖范围): +- API 鉴权说明 +- 电子面单、散件下单 +- 物流轨迹 +- 售后服务、国际件、基础服务等 + +韵达轨迹对接需要在控制台确认/落盘的信息(建议形成《承运方接入配置表》): +- 鉴权方式:签名算法(HMAC/MD5 等)、参与签名字段、编码/排序规则、是否包含时间戳与 nonce。 +- 接口要素:轨迹查询接口 URL、`method`、`v`(如平台/第三方采用“method+version+param”体系)。 +- 订阅/回调能力:是否支持轨迹订阅与回调推送、回调重试策略、回调验签字段。 +- 返回字段:事件时间字段、事件码/状态字段、事件文案字段、地点字段(城市/网点)与可选 POD 能力。 + +平台侧落地方式保持不变: +- 无论韵达返回结构如何,统一通过 Adapter 映射为平台 `TrackingEvent` 领域模型入库,并保留 `raw_payload`。 + +附:Mock 控制面通用错误码(示例) +- 40001: invalid_parameter +- 40002: missing_required_field +- 40901: duplicate_request +- 50001: internal_error + +可选增强(非本期必需): +- 生成 OpenAPI 文档(控制面 API + Webhook 示例) +- 补齐“字段必填矩阵/容错矩阵”(配合 `drop_fields` 故障注入) diff --git a/pages/mall/delivery/doc/需求文档/状态映射表.md b/pages/mall/delivery/doc/需求文档/状态映射表.md new file mode 100644 index 00000000..478b97f4 --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/状态映射表.md @@ -0,0 +1,64 @@ +# 状态映射表(Mock 承运方 Server -> 平台统一状态) + +本表用于将 Mock Server 产生的 `event_code/event_text` 映射为平台统一 `status_code`,以保证前端时间线与告警规则可复用。 + +关联文档: +- `接口规范.md`:统一事件模型与接入模式 +- `前端字段清单.md`:时间线字段契约(status_history/status_code) + +平台统一状态(建议): +- PENDING:待揽收 +- IN_TRANSIT:运输中 +- ARRIVED_HUB:到达中转/分拨中心 +- OUT_FOR_DELIVERY:派送中 +- DELIVERED:已签收 +- EXCEPTION:异常 +- RETURNED:退回 + +Mock 事件码(建议) -> 平台统一状态 +- CREATED / ORDERED -> PENDING +- PICKED / COLLECTED -> IN_TRANSIT +- ARRIVED_HUB -> ARRIVED_HUB +- DEPARTED_HUB / IN_TRANSIT -> IN_TRANSIT +- ARRIVED_DEST_CITY -> IN_TRANSIT +- OUT_FOR_DELIVERY -> OUT_FOR_DELIVERY +- SIGNED / DELIVERED -> DELIVERED +- REJECTED -> EXCEPTION +- ADDRESS_INVALID -> EXCEPTION +- DAMAGED -> EXCEPTION +- LOST -> EXCEPTION +- RETURNED / RETURNING -> RETURNED + +示例(用于 UI 文案) +- ARRIVED_HUB:到达北京分拨中心 +- OUT_FOR_DELIVERY:快件正在派送中 +- SIGNED:客户已签收(可带 POD 图片) + +注意事项 +- 平台入库请保留原始 `event_text` 与 `raw_payload`,前端默认展示 `event_text`,状态标签使用 `status_code`。 +- Mock Server 应支持故障注入(重复/乱序/延迟/验签失败),平台状态映射必须在幂等入库后执行。 + +为尽量保证事件状态保持一致(基础要求): +- `event_code/event_text` 视为第三方“事实记录”,尽量不改写;统一口径(标签、颜色、筛选)交由 `status_code` 承担。 +- 映射必须确定性:同一承运方、同一 `event_code` 在同一套映射规则下应得到同一 `status_code`。 + +备选方案:映射版本化(`mapping_version`)(可选增强) + +为什么要做: +- 防止“状态漂移”:映射规则一旦调整,历史订单的已入库事件如果重新计算 `status_code`,可能出现“昨天运输中,今天变异常/又变回运输中”,引发用户与客服争议。 +- 提升可审计性:当出现纠纷或对账问题时,需要能回答“当时按哪一版规则映射出来的”。 +- 降低排障成本:多承运方/多渠道(直连+聚合)并存时,版本信息能快速定位是“第三方事件变了”还是“平台映射变了”。 + +什么时候启用: +- 状态映射会随运营/产品频繁调整,且希望历史展示稳定、可解释。 +- 客服/售后对轨迹状态一致性有明确 SLA 或合规/审计要求。 + +怎么落地(从轻到重): +- 轻量做法(推荐起步):将映射表作为配置/代码资产管理,使用 Git tag/发布号作为 `mapping_version`,并在发布记录中保留变更原因与生效时间。 +- 加强做法:在事件入库时把 `mapping_version` 一并存入事件记录(或运单聚合记录),对外查询接口可返回该版本用于排查。 +- 变更策略建议: + - 小改动优先“仅影响未来事件”(不回刷历史)。 + - 如必须修正历史映射,采用回放/回填任务,并对已对外展示的结果做备注或审计记录,避免静默改写。 + +代价与取舍: +- 会引入配置治理与发布流程的管理成本;若当前映射很稳定、承运方少、无强审计诉求,可以先不启用该方案。 diff --git a/pages/mall/delivery/doc/需求文档/缺口与待补充清单.md b/pages/mall/delivery/doc/需求文档/缺口与待补充清单.md new file mode 100644 index 00000000..00105acf --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/缺口与待补充清单.md @@ -0,0 +1,82 @@ +# 缺口与待补充清单(配送模块|第三方轨迹对接 + Mock 联调) + +本文定位: +- 用“交付物清单”的方式回答:当前这套配送对接文档还缺什么、为什么缺、补了带来什么收益。 +- 不改变现有方案(统一事件模型 + Adapter + 幂等乱序入库 + 前端统一契约),只补齐落地时最容易卡住的材料。 + +关联文档: +- `README.md`:阅读入口与联调路径 +- `配送模块需求文档.md`:目标/范围/验收口径 +- `接口规范.md`:Webhook + 控制面 API + 入库建议 +- `状态映射表.md`:event_code/event_text -> status_code + 一致性治理(含备选方案) +- `前端字段清单.md`:安卓/Web 展示契约 + +--- + +## P0(建议本期补齐,影响联调与验收能否顺利推进) + +1) 联调/验收 Checklist(平台侧 + Mock 侧) +- 为什么要补:现在“验收标准”是目标描述,但缺少可勾选的操作性检查项,QA/对接会反复口头对齐。 +- 补齐收益:把“验签/幂等/乱序/重复/延迟/缺字段”等要求落到可执行项,减少漏测。 +- 建议产物:一份表格/列表(可以后续直接贴到测试用例/测试报告里)。 + +2) 场景库清单(scenario catalog)+ 每个场景的事件序列期望 +- 为什么要补:当前只提了 `standard_delivered` 等示例名,但没有定义“这个场景到底要生成哪些关键节点、每个节点对应哪些 event_code/status_code、时间间隔怎样”。 +- 补齐收益:前端可稳定对照 UI;后端可稳定断言幂等与排序;QA 可复现回归。 +- 建议产物: + - 场景列表(标准签收/拒收退回/地址异常/破损/丢失/长时间无更新/签收失败后成功等) + - 每个场景:事件数、关键事件码、是否包含 POD、是否包含异常、是否需要乱序/重复/延迟注入。 + +3) Webhook 接收契约的“ACK/重试”约定(平台与第三方的边界) +- 为什么要补:目前只写“成功返回 200”,但缺少“平台什么时候返回 2xx、什么时候 4xx/5xx、第三方是否重试、重试间隔与上限”的共识。 +- 补齐收益:避免线上/联调出现“平台处理慢导致第三方重推爆量”“平台 4xx 被第三方当作可重试”之类问题。 +- 建议产物:在 `接口规范.md` 增补一小节: + - 验签失败/缺字段/无法解析 -> 4xx(不建议重试) + - 平台内部暂时不可用 -> 5xx(允许重试) + - 成功接收但异步入库失败 -> 2xx 还是 5xx 的取舍(建议:接收成功就 2xx,入库失败走内部告警与补偿队列)。 + +4) 承运方接入配置表模板(Carrier Integration Profile) +- 为什么要补:你已经发现韵达/圆通等第三方细节差异大,缺少一个“把关键信息落盘”的标准表格,后续每接一家都要重新摸索。 +- 补齐收益:缩短接新承运方周期;把差异收敛到 Adapter/配置层;减少口头信息丢失。 +- 建议字段(模板): + - `carrier`、接入模式(webhook/polling/both) + - 鉴权类型与签名算法、参与签名字段、是否含 timestamp/nonce + - 轨迹查询接口(URL/method/v/字段示例)、回调接口(回调验签字段/重试策略) + - 是否提供稳定 `event_id`、是否要求手机号后四位 + - 时间格式/时区、地点粒度 + - 状态码枚举来源与映射版本 + +--- + +## P1(建议尽快补齐,影响长期维护与一致性/审计能力) + +1) 权限矩阵与审计要求(谁能看 raw_payload / POD / 全量地址手机号) +- 原因:`raw_payload` 可包含敏感字段;Web 客服/运维能力需要最小权限与审计。 +- 产物:角色(普通用户/商家/客服/运维/管理员)× 字段(raw/POD/手机号/地址)的可见性矩阵 + 审计日志要求。 + +2) 错误码与返回体统一(控制面 API) +- 原因:目前 `接口规范.md` 仅列举了示例错误码,但缺少标准响应结构。 +- 产物:统一响应:`{success, code, message, request_id, data}`;并约定 HTTP 状态码与业务码的对应。 + +3) OpenAPI(可选) +- 原因:接口多了以后靠 Markdown 容易漂移。 +- 产物:控制面 API + Webhook payload 的 OpenAPI(至少给 Mock 侧)。 + +4) “状态不回退/一致性”策略的明确算法(平台侧) +- 原因:`接口规范.md` 提了可选规则,但未选型;当出现终态后补到更早事件时,当前状态如何计算需要一口径。 +- 产物:选定一种算法并写清:以 `event_time` 最新为准 / 状态等级只进不退 / 双轨(展示按时间,current_status 不回退)。 + +--- + +## P2(可后补,锦上添花) +- DB 字典与 ER 图(便于新人理解 schema) +- 场景导入导出(JSON)格式规范 +- 对接灰度/熔断/降级策略的配置说明 + +--- + +## 建议补充顺序(最省时间) +1) 先补 P0-1(Checklist)+ P0-2(场景库清单)——直接提升联调效率 +2) 再补 P0-3(ACK/重试约定)——避免重复推送与误判 +3) 再补 P0-4(承运方接入配置表模板)——为后续接真实承运方铺路 +4) 最后补 P1 项——为线上长期治理做准备 diff --git a/pages/mall/delivery/doc/需求文档/配送模块需求文档.md b/pages/mall/delivery/doc/需求文档/配送模块需求文档.md new file mode 100644 index 00000000..a773fcfb --- /dev/null +++ b/pages/mall/delivery/doc/需求文档/配送模块需求文档.md @@ -0,0 +1,251 @@ +# 配送模块需求文档(模拟三通一达后台 / Mock 承运方 Server) + +本文定位:定义 Mock 承运方 Server 的目标、范围、流程、故障注入与验收标准;接口与字段细节以 `接口规范.md`、`前端字段清单.md` 为准。 + +口径说明:本目录文档作为联调、验收与实现的唯一口径;如与口头沟通或临时讨论结论不一致,以本文档及其关联文档(接口/字段/映射)为准,并通过文档更新同步。 + +关联文档: +- `README.md`:阅读入口与联调路径 +- `接口规范.md`:Webhook 推送规范 + Mock 控制面 API +- `状态映射表.md`:event_code/event_text -> 平台 status_code +- `前端字段清单.md`:安卓/Web 时间线字段契约 + +## 1. 背景 +当前配送链路依赖第三方承运方(常见为“三通一达”等快递公司或聚合平台)。在真实承运方接口尚未签约/联调完成前,需要一个**可控的模拟承运方后台服务(Mock Server)**,用于开发与测试平台侧的:Webhook 接收、验签、幂等入库、时间线展示、异常处理与监控告警。 + +系统定位与边界: +- 本模块属于平台系统的一部分,最终呈现在“订单详情页”的物流区块,并同时服务客服/履约/对接排障。 +- 平台端需要查看订单的履约详情(至少包括:`order_no`、`tracking_no`、`carrier`、物流时间线 `status_history`、异常与处理记录)。 +- 但本需求不覆盖商品明细、支付、营销等订单业务域的展示与流程;这里聚焦“物流轨迹对接、入库与展示”。 + +多端归属与页面边界(统一口径): +- 本需求产出的“物流能力”不是一个独立的骑手/配送员端 App,而是作为同一套物流模块能力,分别嵌入到三个端(按权限展示不同颗粒度): + 1) 消费者端(C 端):订单详情的物流时间线/物流详情(只读为主,展示 `event_text` + `status_code`,不展示 `raw_payload`)。 + 2) 商家端(B 端):发货与运单绑定(选择承运方、录入/回填 `tracking_no`)+ 查看物流(不包含对接配置与敏感调试信息)。 + 3) 管理端(平台后台:客服/履约/对接运维):订单/运单查询、Webhook 接收日志、接入配置、(可选)监控告警与审计能力。 +- 三端展示字段口径以统一事件模型与前端字段契约为准(见 `接口规范.md`、`前端字段清单.md`),避免出现“不同端展示不一致/状态口径不一致”。 +- 若未来要做“自营配送/同城骑手端”(接单、导航、到店取货、送达签收等),需单独立项与另写需求文档,不在本需求范围内。 + +## 2. 目标 +- 适用阶段:主要用于真实第三方承运方未签约/未联调/不可控时的替代数据源;即便后续已接入真实承运方,也保留用于回归测试、故障注入与排障复现(生产环境默认关闭)。 +- 提供一个可配置的 Mock 承运方服务,模拟多承运方(圆通/韵达/中通/申通)轨迹事件与签收凭证。 +- 支持将轨迹事件按既定规范推送到平台 Webhook(模拟承运方 -> 平台)。 +- 支持主动查询轨迹(平台轮询场景)与“预置场景脚本”快速生成整条物流生命周期。 +- 支持故障注入:延迟、重复推送、乱序、签名错误、时间戳偏移、缺字段等,用于验证平台鲁棒性。 + +## 2.1 配送合作模式(不同解决方案) + +说明:以下方案是“商城履约/配送合作关系”的产品与工程取舍;无论选哪一种,只要前端要展示稳定一致的物流时间线,平台都建议建设统一的轨迹模型与查询接口(见 `接口规范.md` 与数据库 Schema)。本 Mock 体系用于在第三方未就绪或不可控时,替代外部系统快速联调与做故障注入回归。 + +### 方案 A:平台统一配送(平台与第三方合作 / 统采统接) +适用:希望提供强一致的履约体验、统一客服口径、统一监控与统计;平台愿意承担对接与运维成本。 + +平台职责: +- 统一签约/选择承运方或聚合平台;提供平台侧发货能力(下单、订阅/回调、轨迹查询与回单能力按合同)。 +- 平台统一事件模型入库(幂等去重、乱序处理、状态不回退),对客户端输出统一 `status_history`。 + +商家职责: +- 在平台内选择配送方案并发货;不需要自行对接承运方轨迹接口(或仅提供必要发货信息)。 + +优点:体验一致,平台可观测性强;扩展新承运方主要改 Adapter,不影响前端契约。 +风险/成本:平台对接与运维成本高;履约兜底责任更集中在平台侧。 + +与 Mock 的关系: +- Mock 用于第三方未联通阶段的替代数据源与故障注入;生产环境默认关闭。 + +### 方案 B:商家自选配送(商家自找承运方 / 平台只做订单) +适用:平台招商、品类/区域差异大、平台希望给商家“选择承运方”的灵活性。 + +重要说明(商家只是平台商户的常见情况): +- 默认不要求商家自建系统或自行对接第三方 API。 +- “商家自选”指商家在平台提供的承运方/聚合平台范围内进行选择,第三方对接、验签、幂等、入库与前端展示由平台统一承担。 + +平台职责(建议至少做到“统一展示底座”): +- 提供统一轨迹查询接口给前端;平台内部仍建议使用统一事件模型入库。 +- 平台负责与第三方承运方/聚合平台对接:订阅/回调(Webhook)或轨迹查询(轮询补偿)、验签、防重放、幂等去重、乱序处理与统一入库。 +- 平台提供“发货与运单绑定”的入口(承运方选择、运单号录入/回填/打单),把差异收敛在平台 Adapter/映射规则,避免前端直接适配多家第三方。 + +商家职责: +- 在平台选择承运方并完成发货动作:提供/回填 `tracking_no` 与必要的订单关联信息(如 `order_no`)。 +- 若商家坚持使用“商家与第三方的独立合同/账号”,可向平台提交第三方对接所需材料,由平台统一配置与代接入(不要求商家自建回调服务)。 + +优点:平台轻、商家灵活。 +风险/成本:体验碎片化风险大;如果不强制回传规范,平台客服/前端会被迫处理多样差异,长期维护成本更高。 + +#### 商家自选配送需要提供的内容(接入清单) + +基础信息(发货侧最小闭环): +- `carrier`:承运方标识(可为直连承运方或聚合平台,如 YUNDA/YTO/ZTO/STO/KDN 等)。 +- `tracking_no`:运单号生成与回传方式(商家生成/第三方返回/平台生成)。 +- 订单关联信息:`order_no`(或平台侧可解析的业务单号),用于把轨迹绑定到订单详情页。 + +轨迹数据接入方式(平台统一接入,推荐第一种): +1) 第三方 -> 平台 Webhook 推送(推荐): + - 平台与第三方完成订阅/回调配置;第三方直接回调平台 Webhook。 + - 平台统一完成:验签、防重放、字段映射、状态映射、幂等去重、乱序入库。 +2) 平台 -> 第三方 轮询拉取(可选补偿): + - 平台按承运方策略轮询“长时间无更新”的运单,补齐轨迹。 + - 适用于第三方不稳定、回调丢失或仅提供查询能力的场景,但平台运维成本更高。 + +(高级模式,可选,适用于有自建系统的大商家) +- 商家系统 -> 平台:商家按平台统一事件模型回传轨迹事件;平台仍执行幂等/乱序入库与审计。 + +对接材料(按商家选择的接入方式提供): +- 若商家仅在“平台已接入承运方范围内自选”,通常无需商家提供第三方接口材料。 +- 若商家使用“独立合同/账号”并要求平台代接入,则需要商家提供或协助申请: + - 鉴权信息:`appKey/appSecret`、`token`、证书、公网 IP 白名单等(因第三方而异)。 + - 回调能力:是否支持订阅 + Webhook(以及重试策略、签名算法、回调白名单)。 + - 能力说明:是否提供 `event_id`、是否提供 POD/签收凭证、是否需要手机号后四位参与查询等。 + +映射配置(用于消除差异化,平台侧必须落库为配置或代码映射): +- 事件码/文案 -> 平台 `status_code` 的映射(至少覆盖:揽收/在途/派送中/签收/异常/退回)。 +- 幂等去重策略:优先 `event_id`;缺失时使用组合键(见 `接口规范.md` 的入库层建议)。 + +#### 差异化来源与平台收敛策略 +差异化来源(商家自选必然存在): +- 鉴权差异:Token/HMAC/证书/IP 白名单等。 +- 字段差异:时间格式、地点粒度、是否有 `event_id`、异常字段命名等。 +- 状态差异:同一状态不同叫法/码值,甚至“派送/签收/退回”的边界不同。 +- 交互差异:仅查询(轮询)/订阅回调(Webhook)/两者并存;重试与乱序程度不同。 + +平台收敛策略(避免差异扩散到前端与数据库): +- 前端只消费平台统一查询接口与统一字段(见 `前端字段清单.md`),不得直接依赖第三方字段。 +- 平台统一入库模型与幂等/乱序规则(见 `接口规范.md`),第三方差异仅在 Adapter/配置映射层处理。 +- 分级能力与降级: + - 达标(可回传轨迹或可稳定拉取 + 通过验收)-> 展示完整时间线 + 可做告警统计。 + - 不达标(仅能提供运单号或字段缺失严重)-> 降级为“仅展示运单号/跳转外部查询”,不承诺完整时间线。 + +验收建议(商家自配准入): +- 必测:重复推送、乱序、延迟、验签失败、缺字段(对应本 Mock 的故障注入项)。 +- 通过后才允许全量展示/告警统计;未通过仅允许降级展示。 + +与 Mock 的关系: +- Mock 可作为“平台对接与回归”的验收工具:在第三方未就绪或不可控时,先用 Mock 场景验证幂等/乱序/缺字段容错等;接入真实第三方后也可用于故障注入回归。 + +### 方案 C:混合模式(平台默认合作 + 允许商家自配)【推荐】 +适用:既要平台可控的默认体验,又要给大商家/特殊品类保留弹性。 + +规则建议: +- 默认:商家走平台合作承运方(覆盖大多数订单,保证体验一致)。 +- 例外:允许商家自配,但必须满足平台回传规范(至少事件时间/文案/状态映射可得);否则只提供降级展示能力。 +- 前端:永远只消费平台统一查询接口,不直接依赖第三方字段。 + +落地建议(最小闭环): +- 平台统一事件表与查询 API 先上线;Mock 先跑通联调与回归。 +- Phase 1:接 1 家聚合或 1 家主力承运方(平台统一配送)。 +- Phase 2:开放商家自配(按准入与验收清单接入)。 + +## 3. 范围 +### 3.1 包含 +- Mock Server 对外提供控制面 API:创建/查询运单、追加事件、运行场景、触发推送、配置回调目标。 +- Mock Server 向平台推送 Webhook:按统一事件模型、支持 HMAC 验签头。 +- 事件与场景数据本地持久化(最小可用可内存;建议支持文件落盘,便于复现)。 + +### 3.2 不包含 +- 真实承运方业务能力(计费、真实网点、真实 POD 获取) +- 配送员实时定位与地图 +- 平台侧业务实现(本需求仅定义 Mock Server 的接口与行为) + +## 4. 目标用户 +- 平台后端开发:联调 Webhook、验签、幂等与入库。 +- 平台前端开发:使用固定运单/事件数据验证时间线展示。 +- QA 测试:用故障注入覆盖异常链路与回归。 +- 运维/对接:用于排查“回调未到/重复到/乱序到”等问题。 + +## 4.1 使用端(主要投放端) +- 安卓端(App):订单详情页展示物流时间线、签收凭证预览、异常上报。 +- Web 端(H5/PC 管理后台或用户 Web):同样展示物流时间线,提供更完整的原始事件/审计信息(按权限)。 + +## 4.2 平台后台展示建议(客服/履约/对接运维最小集) + +说明:本节定义“平台侧管理后台”在物流模块上建议具备的最小展示与排障能力(不等同于完整订单后台)。 + +1) 订单详情页(物流区块) +- 基础信息:`order_no`、`carrier`、`tracking_no`、当前 `status_code`、`last_synced_at` +- 物流时间线:`status_history`(按 `event_time` 排序,展示 `event_text`,标签使用 `status_code`) +- 异常提示:长时间无更新、签收失败、退回等(按 `status_code` 与规则触发) + +2) 运单/轨迹查询页(排障入口) +- 支持按 `tracking_no/order_no/carrier/status_code` 查询 +- 展示最近 N 条事件与来源(mock/承运方/聚合),便于快速定位 + +3) 对接日志与审计(运维/对接用) +- Webhook 接收日志:验签结果、去重命中、入库结果/错误原因(按权限) +- 原始回文:`raw_payload` 折叠展示(仅客服/运维可见,记录访问审计) + +4) 配置中心(对接运维用) +- 承运方/聚合平台配置、Webhook 目标与密钥/证书、轮询补偿开关与频率 +- 测试环境 Mock 开关(默认关闭,需显式开启) + +(可选增强)一致性回查/纠偏入口:按需触发第三方回查,补采缺失事件并保留审计记录。 + +## 4.3 商家后台展示建议(商户侧最小集) + +说明:商家只是平台商户时,建议仅提供“发货与运单绑定 + 物流查看”的最小能力。 + +- 发货:选择承运方、录入/回填 `tracking_no`、发货确认 +- 订单详情(物流区块):查看时间线与当前状态(不展示 `raw_payload`) + +端侧差异化要求: +- 安卓端需支持弱网/后台切换恢复后的快速刷新;证据图片需支持点击预览/下载。 +- Web 端需支持表格/筛选/搜索(便于客服排障);对调试信息(raw_payload)默认折叠。 + +## 5. 核心概念 +- 承运方(carrier):YUNDA / YTO / ZTO / STO(可扩展) +- 运单(tracking_no):可由 Mock Server 生成或由调用方指定 +- 事件(event):包含 event_id、event_time、event_code、event_text、node_name、location、evidence_urls 等 +- 场景(scenario):一组按时间顺序生成的事件脚本(如“标准签收”“拒收退回”“地址异常”) + +## 6. 关键流程 +1) 配置推送目标:设置平台 Webhook URL、`client_id`、`secret`。 +2) 创建运单:选择 carrier,生成/指定 tracking_no。 +3) 运行场景:Mock Server 生成一系列关键节点事件并入库。 +4) 触发推送:Mock Server 按顺序(或按乱序/延迟策略)将事件推送到平台。 +5) 平台侧验证:校验验签、去重、入库、前端展示与告警。 + +## 7. 功能需求 +### 7.1 控制面 API(Mock Server) +- 配置:设置 webhook 目标、密钥、默认承运方。 +- 运单:创建/查询/列表。 +- 事件:追加/查询/清空;支持批量追加。 +- 场景:运行预置脚本(标准流程/异常流程)。 +- 推送:立即推送指定运单的全部/增量事件;支持模拟重试与重复。 + +### 7.2 Webhook 推送(Mock -> 平台) +- 请求头包含 `X-Client-Id`、`X-Timestamp`、`X-Signature`。 +- body 结构遵循统一事件模型(见 `接口规范.md`)。 +- 幂等:对同一事件可重复推送,平台需以 `event_id` 去重。 + +### 7.3 故障注入(必须) +- `delay_ms`:推送延迟 +- `duplicate`:重复推送次数 +- `out_of_order`:乱序推送 +- `bad_signature`:签名错误 +- `timestamp_skew_seconds`:时间戳偏移 +- `drop_fields`:缺字段(用于校验平台必填校验与容错) + +## 8. 非功能需求 +- 易用性:一条命令启动;提供最少的配置即可发送事件。 +- 可复现:场景运行应可导出/导入(或提供固定随机种子)。 +- 可观测:每次推送记录 request/response 摘要与 request_id。 +- 安全:仅用于内网/测试环境;支持简单 Token 或 IP 白名单(可选)。 + +端侧体验要求(安卓/Web): +- 刷新策略:页面进入时拉取一次平台侧轨迹;用户可手动“刷新物流”;显示 `last_synced_at`。 +- 离线降级:无网络时展示上次缓存的时间线并提示“网络不可用”。 +- 权限与隐私:手机号脱敏;`raw_payload` 仅客服/运维可见并记录审计;不展示配送员精确地址/定位。 + +## 9. 验收标准 +- 能创建运单并运行“标准签收”场景,向平台推送至少 6 个关键节点(揽收/到达中转/在途/到达目的地/派送中/签收)。 +- 能注入重复与乱序事件,平台侧仍能正确去重并按时间线展示。 +- 能注入验签失败事件,平台侧能拒绝并记录告警。 + +## 10. 文档与交付物 +- `接口规范.md`:Mock Server 控制面 API + Webhook 推送规范 +- `状态映射表.md`:事件码/原文到平台统一状态映射建议 +- `前端字段清单.md`:前端展示字段契约(供时间线组件使用) +- 数据库 Schema:`mall_sql/schemas/express_tracking_mock_platform.sql`(平台统一轨迹入库 + Mock 承运方持久化表) + +## 11. 待补充项(备忘与下一步) +为保证联调、验收与后续接真实承运方不走弯路,建议按优先级补齐: +- `缺口与待补充清单.md`