flash-delivery-v2-frontend-handoff.md 14 KB

闪送 v2 多点取送前端对接说明

更新时间:2026-10-15。本文对应当前仓库中的后端实现,供用户端和骑手端接入;联调前需确认服务端部署版本和 10 月 14 日数据库执行说明。10 月 15 日交错取送与进度调整无新增字段、无新增 SQL。

本次要调整的页面和流程

页面/场景 前端调整
用户填写闪送订单 按客户顺序编辑一个 stops 数组,首站取货、末站收货,中间可以交错取送;物品信息只填写一次。根据 /home 的取货点和收货点数量上限控制可添加数量。
报价与下单 使用 /v2/quote 和 /v2/orders。创建时把本次报价的配置 ID、版本和费用原样回传;创建请求号在重试时保持一致。
用户/收件人订单列表和详情 展示取货、收货两个进度;站点使用 stops[].id,不可把数组下标当成站点 ID。逐站收货码按权限展示。
骑手履约 保留原五个 Tab 和名称,卡片增加取件、送达双进度。从详情的 currentTargetStopId 找目标,可自主选择其他未完成站;每站上传凭证并刷新详情。逐站送达使用固定路径的新接口,orderId、stopId、PIN 和照片全部放 body。
待接单改址 v2 使用新的完整站点报价与保存接口。保存成功后刷新详情,替换本地旧站点 ID 和收货码。
发起人消息 识别两种逐站进度事件,凭 ddId 打开订单详情,再读取最新状态。逐站收件人通知本期不接入。

所有下列新 POST 接口使用 token 请求头和 JSON body。只有新接口采用“业务参数统一放 body”的约定;现有 GET、旧取件、旧目标调整、旧订单接口及 LINE Pay 接口的参数格式保持原样。响应仍为项目通用 AjaxResult,业务数据在 data 中;订单详情的 data 直接是订单对象,不再套一层 order。

用户端:填写、报价、创建

先调用 GET /system/flashDelivery/home,使用 data.pickupStopLimit、data.deliveryStopLimit 控制添加站点按钮和提交数量。有效范围为 1–10,默认均为 1;对应上限大于 1 即可使用多站点,无需开关。兼容字段 showMultiPickup、showMultiDropoff 分别表示对应上限是否大于 1,showUrgentOption 始终为 true;加急选项直接展示。服务类型、配送等级和可用支付方式也从首页响应读取。

POST /system/flashDelivery/v2/quote 的示例 body:

{
  "serviceType": "HELP_SEND",
  "deliveryType": "NORMAL",
  "vehicleType": 1,
  "packageType": "DOCUMENT",
  "quantity": 2,
  "weightRange": "UP_TO_5_KG",
  "specification": "两个文件袋",
  "tipAmount": 0,
  "deliveryMode": "NOW",
  "payType": "4",
  "stops": [
    {"type":"PICKUP","name":"取件人甲","phone":"0911000001","address":"台北市信义区甲路1号","addressDetail":"1楼","longitude":121.5601,"latitude":25.0301,"remark":"取文件袋甲"},
    {"type":"PICKUP","name":"取件人乙","phone":"0911000002","address":"台北市信义区乙路2号","longitude":121.5602,"latitude":25.0302,"remark":"取文件袋乙"},
    {"type":"DELIVERY","name":"收件人丙","phone":"0911000003","address":"台北市大安区丙路3号","longitude":121.5603,"latitude":25.0303,"remark":"交给本人"},
    {"type":"PICKUP","name":"取件人戊","phone":"0911000005","address":"台北市大安区戊路5号","longitude":121.5605,"latitude":25.0305,"remark":"取给丁的文件"},
    {"type":"DELIVERY","name":"收件人丁","phone":"0911000004","address":"台北市大安区丁路4号","longitude":121.5604,"latitude":25.0304,"remark":"交前台"}
  ]
}

至少一个 PICKUP 和一个 DELIVERY,首站取货、末站收货,中间允许交错;默认路线及报价顺序就是数组顺序,不要把站点按取/送重新分组。同一地址或坐标不能重复。每站分别填写姓名、电话、完整地址和经纬度,可选门牌、交接方式、最长 200 字备注。客户在备注写清本站取什么/送什么,骑手负责核对,不需要提交取货站与收货站关联关系。v2 站点不要提交旧结构中的逐站物品字段;packageType、正整数 quantity、可选 weightRange 和 specification 只在订单级出现一次。SCHEDULED 订单还需提交 scheduledPickupStartAt、scheduledPickupEndAt,NOW 订单无需提交这两个字段。

创建订单使用 POST /system/flashDelivery/v2/orders。body 包含报价时的业务字段和完整 stops,再增加:

{
  "clientRequestId": "客户端生成并持久保存的本次下单请求号",
  "pricingId": 123,
  "pricingVersion": 1,
  "quotedBaseDeliveryFee": 80,
  "quotedDistanceFee": 10,
  "quotedUrgentFee": 0,
  "quotedAmount": 90,
  "pinRequired": true,
  "senderImageUrls": [],
  "userNote": "请注意文件袋"
}

上述价格数字只展示字段形状,实际值必须取本次报价 data 的 pricingId、pricingVersion、baseDeliveryFee、distanceFee、urgentFee、amount,不可使用示例数字。clientRequestId 最长 64 字符;同一次创建的网络重试使用相同值,新订单使用新值。pinRequired 省略时默认为 true,开启后每个收货站有独立四位收货码。报价在创建前变化时,接口会返回错误并在 data 附最新报价;展示新金额让用户重新确认后再提交。

订单详情、收件人和进度

  • 用户列表:GET /system/flashDelivery/orders?role=sender|receiver&page=1&size=10;receiver 包括每一个已匹配到系统普通用户的收货站。收件人列表卡片展示自己的收货地址。
  • 用户详情:GET /system/flashDelivery/orders/{id};按订单号打开消息:GET /system/flashDelivery/orders/byOrderNo?orderNo=...。
  • 骑手列表与详情:GET /system/flashDelivery/rider/orders、GET /system/flashDelivery/rider/orders/{id},沿用原有 tab、分页和可选坐标参数。

详情 data.stops 按客户原路线顺序返回。请求站点字段叫 type,响应字段叫 stopType;响应的 id 才是后续履约所需的 stopId,stopOrder 从 0 开始。每站有 status、remark、pickedUpAt/deliveredAt 和 imageUrls。列表与详情提供 pickupStopCount、pickedUpStopCount、deliveryStopCount、deliveredStopCount。未全部取完时整单仍为 ACCEPTED,即使有站点已送达;全部取完为 PICKED_UP;全部取送站完成为 DELIVERED;确认收货或平台/系统完成后为 COMPLETED。页面不要只靠整单 status 推算逐站进度。

骑手 Tab 保持原样,只增加进度

原 Tab(参数不变) 订单归属
新任务 newTask WAITING_ACCEPTANCE,沿用原有可抢范围规则
待取件 toPickup ACCEPTED,还有取货站未完成;可以已有部分送达
配送中 delivering PICKED_UP,已全部取货,还有收货站未完成
已送达 completed DELIVERED / COMPLETED
已取消 cancelled CANCELLED

卡片及详情同时显示 已取件 {pickedUpStopCount}/{pickupStopCount} · 已送达 {deliveredStopCount}/{deliveryStopCount},文本按 App 四语言规范接入。例如取1→取2→送1→取3→送2:送1后显示“已取件 2/3 · 已送达 1/2”,仍在待取件;取3后显示“已取件 3/3 · 已送达 1/2”,进入配送中;送2后进入已送达。不要把它拆成两条订单或同时放入两个 Tab,角标计订单数量而非站点数,继续使用原列表及 GET /system/flashDelivery/rider/orderCount。列表展示进度;进入详情后使用 currentTargetStopId 突出下一站的取/送类型。

v2 的订单级 deliveryPinCode 为空,收货码在各个 DELIVERY 站的 stops[].deliveryPinCode。发起人可查看全部收货码;已在系统内匹配的收件人只能查看自己站的收货码及联系人、精确地址和凭证;骑手看不到任何实际收货码。未匹配系统用户的收货站仍可下单,但此人不会进入“我收的”列表,也不会收到 App 推送;发起人需自行告知该站收货码。逐站收件人通知本期暂缓。

用户端取消按钮以 canUserCancel 为准;任一取货站已取货后不可自助取消。确认收货入口只在整单 DELIVERED 后提供;系统仍会检查全部收货站是否真的已送达。骑手收入账单只在订单进入 COMPLETED 时生成一次。

骑手端:目标、取货、送达

骑手从详情 currentTargetStopId 定位当前站点;v2 默认指向客户原序中最早未完成站,可以是取货站或收货站。骑手也可选择其他待完成站,例如先把附近三个取货点全部取完再送。实际调整不重排客户原站点、不重算运费。操作成功后重新请求详情,不继续使用旧的目标 ID、状态或版本。

操作 当前接口 提交内容
逐站取货 POST /system/flashDelivery/rider/orders/{id}/stops/{stopId}/pickup 现有接口,body 为 {"imageUrls":["/profile/pickup.jpg"]};如同时传 stopId,必须与路径一致。
选择目标站 POST /system/flashDelivery/rider/orders/{id}/stops/{stopId}/target 现有接口,body 含 orderVersion、clientRequestId;v2 的 reason 可选(≤500字),consentDeclared 可省略,不再要求客户同意声明。
逐站送达 POST /system/flashDelivery/rider/orders/stops/deliver 新接口,body 含 orderId、stopId、imageUrls,启用 PIN 时再传该站 pinCode。

取货和送达凭证每站至少 1 张、最多 9 张。新送达接口中 orderId、stopId 均为正整数;只有一个收货站的 v2 订单也必须传 stopId。骑手在送达现场向收件人核对其本站收货码,不能使用别站或订单级收货码。新骑手端可统一根据详情中的站点 ID 使用逐站送达接口;旧 /rider/orders/{id}/deliver 只供旧客户端处理旧订单,v2 订单调用它会被拒绝。

取送非当前目标站会被拒绝。v2 改变顺序时先使用目标调整接口选站,服务端保留操作记录,选站本身不等于确认取送;实际物品由骑手根据备注核对。尚未取过任何货不能选择送货;尚有未取货点时不能确认最后一个未完成收货站,须先完成剩余取件。旧契约订单仍按原同意声明与先取后送规则履约。调整请求的 clientRequestId 在同一次操作重试时保持不变。某站已成功取货或送达的重试不会重复保存照片和进度;末站送达后主状态已变化时也是如此。

用户端:v2 待接单改址

仅待接单、未分配骑手的 v2 订单可编辑。先从详情读取 orderVersion 和完整站点,编辑后提交所有取货与收货站;只提交被修改的一站会丢失其余站点,因此服务端要求完整数组。

POST /system/flashDelivery/v2/orders/address/quote
token: <登录 token>
Content-Type: application/json
{"orderId":10,"orderVersion":0,"stops":[{"type":"PICKUP","name":"...","phone":"...","address":"...","longitude":121.56,"latitude":25.03},{"type":"DELIVERY","name":"...","phone":"...","address":"...","longitude":121.57,"latitude":25.04}]}

确认保存调用 POST /system/flashDelivery/v2/orders/address,body 保留相同的 orderId、orderVersion、完整 stops,再回传报价 data 中的 distanceMeters→quotedDistanceMeters、baseDeliveryFee→quotedBaseDeliveryFee、distanceFee→quotedDistanceFee、urgentFee→quotedUrgentFee、amount→quotedAmount。只改站点,物品、配送方式、小费和原运价快照继续沿用订单保存值。报价或版本变化时重新获取并让用户确认。保存成功后以返回详情更新全部 stops[].id 和各站收货码;旧站点 ID 已失效。旧改址入口会拒绝 v2 订单,旧订单仍沿用原入口。

发起人逐站通知

每个取货/收货站首次成功且事务提交后,服务端向订单发起人推送并写入消息中心。两种 eventType:FLASH_PICKUP_STOP_COMPLETED、FLASH_DELIVERY_STOP_COMPLETED。消息 payload 示例:

{
  "ddId": "订单号",
  "state": "PICKED_UP",
  "type": -1,
  "pushType": 3,
  "eventType": "FLASH_PICKUP_STOP_COMPLETED",
  "stopId": 13,
  "stopType": "PICKUP",
  "stopOrder": 0,
  "completedStopCount": 2,
  "totalStopCount": 2
}

state 是通知发生时真实的整单状态,不应写死为示例值;stopOrder 为路线数组的 0 基下标,通知文案的站号为 1 基。收到事件后按 ddId 请求 /orders/byOrderNo 并刷新详情;不要从消息体推断联系人、地址或 PIN。相同站点的重试不重复通知,最后一站也不叠发旧整单阶段通知。逐站收件人通知留待后续需求。

联调验收重点

  1. 单取单送和多取多送都走 v2 报价/创建;收货站只有一站时,送达仍提供 stopId。
  2. 验证取1→取2→送1→取3→送2,以及骑手自主改为先取完再送;送1后 ACCEPTED、进度 2/3 与 1/2,取3后 PICKED_UP,送2后 DELIVERED。五个 Tab 和名称不变,不能提前确认完成或记账。
  3. 修改第二个收货站后,其他站、订单级物品和原运价仍在;保存后旧站点 ID/PIN 不再使用。
  4. 发起人看全部收货码,匹配的第二站收件人只看自己的地址、联系人、凭证和 PIN;骑手看不到 PIN;未匹配用户不出现 App 收件人入口。
  5. 发起人收到每站一次进度消息并能按订单号打开详情;收件人没有逐站推送。真实设备推送和 App 页面尚需联调。

现有单点、支付及其他旧接口以 原闪送 App API 文档 为准。数据库结构和不同环境的执行方式见 10 月 14 日 SQL 说明;本次问题修复没有额外字段变更;2026-10-14 测试库已按旧版增量路径完成结构与字典更新,其他环境仍需按 SQL 说明执行。