flash-delivery-app-api.md 52 KB

闪送功能 App 接口接入文档

本文档供用户端 App 和骑手端 App 接入闪送功能使用,以当前后端实现为准。文档只描述接口契约和业务流程,不包含 App 前端实现代码。

更新时间:2026-09-09。新增待接单订单修改发件/收件信息与追加小费:修改地址先重报价,用户确认后保存;保存时校验订单状态和版本。闪送仍不接入支付。

当前下单与订单编辑契约要点:

  • serviceType 只区分 HELP_SEND(帮送)和 HELP_PICKUP(帮取),不再使用 URGENT 作为服务类型。
  • 是否加急由独立字段 deliveryType=NORMAL/URGENT 表达;帮送和帮取使用同一套分时段基础运价。
  • 报价和创建订单都必须提交取件方式、物品类别、数量、四档重量范围、规格说明和骑手小费;立即取件使用 deliveryMode=NOW,预约取件使用 deliveryMode=SCHEDULED 并提交取件时间段。
  • App 必须先报价,再把报价 ID、版本及四个费用字段原样回传创建订单接口。后端重新计算不一致时不会创建订单,而会返回最新报价。

  • 本次新增两个业务操作、三个 POST 接口:修改地址预报价 quoteAddress、确认保存 updateAddress,以及追加小费 addTip,详见 7.8—7.10。

  • 三个新增接口仅供订单创建人操作仍未被骑手接走的订单,必须回传用户详情中的 orderVersion;追加小费传本次增量 additionalTipAmount。

1. 功能范围

当前闪送支持:

  • 帮送、帮取两种业务场景,以及普通配送、1 对 1 加急两种配送等级。
  • 物品类别、数量、设计稿四档重量范围和体积/规格。
  • 基础配送费、距离费、加急费、骑手小费和订单总金额明细。
  • 共享地址簿、路线报价、立即配送和预约配送。
  • 待接单订单修改发件/收件地址、联系人和电话,重新报价并确认保存;追加小费并同步更新总金额。
  • 用户发布订单,并按“我发的/我收的”查询订单;发件人可取消,发件人或当前绑定的收件人可确认收货。
  • 骑手查看待抢订单、抢单、确认取件和确认送达。
  • 可选的四位交付 PIN。
  • 寄件、取件和送达图片凭证。
  • 送达 24 小时后仍未由用户确认的订单自动完成。

当前不包含支付、退款、骑手收入、结算、代购垫付、自动派单和骑手放弃订单。

设计原型中的支付方式选择、已付款状态、附近骑手上线数量、预计接单时间和骑手收益金额均为静态示意,接口不提供这些数据,App 不应展示或请求它们。发布成功页统一使用静态文案“发布后等待附近骑手接单”。

2. 通用约定

2.1 基础地址和请求头

接口路径均为相对路径,实际请求地址为:

{baseUrl}{接口路径}

除特别说明外,用户端和骑手端接口都必须携带以下请求头:

请求头 必填 说明
token 是 App 登录后取得的 JWT。不要放在 Authorization 中
Content-Type POST JSON 接口必填 application/json

用户身份和骑手身份均由 token 解析,请求体中不提交 userId 或 riderId。

2.2 统一响应

成功响应:

{
  "code": 200,
  "msg": "操作成功",
  "data": {}
}

无返回数据的成功响应通常不含 data:

{
  "code": 200,
  "msg": "操作成功"
}

业务失败响应:

{
  "code": 500,
  "msg": "当前订单状态不允许此操作"
}

登录失效响应:

{
  "code": 401,
  "msg": "token已过期,请重新登录!"
}

App 必须以响应体 code 判断业务是否成功,不能只依赖 HTTP 状态码。msg 已由后端国际化,可直接用于错误提示。

2.3 分页响应

列表接口的分页数据位于 data 中,不使用若依传统的顶层 rows/total:

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "records": [],
    "total": 0,
    "current": 1,
    "size": 10,
    "pages": 0
  }
}
  • page 默认 1,小于 1 时按 1 处理。
  • size 默认 10,范围为 1 至 100。
  • App 至少读取 records、total、current 和 size。

2.4 时间、金额和距离

  • 请求中的预约时间使用 ISO 日期时间字符串,例如 2026-09-02T10:00:00+08:00。
  • 返回时间以服务端实际 JSON 时间格式为准,App 应按日期时间解析,不应依赖固定展示格式。
  • 金额单位为新台币,currency 固定为 TWD;startingFare、freight、distanceFee、baseDeliveryFee、minimumUrgentFee、urgentFee、tipAmount 和 amount 都是整数,不带小数。
  • distanceMeters 单位为米。
  • 运价配置中的 startingDistance、distance 以及报价中的 billableDistance 单位为公里,可保留两位小数。
  • estimatedDurationSeconds 单位为秒,路线服务降级时可能为 null。
  • distanceSource 为 ROUTE 时表示地图驾车路线,为 STRAIGHT_LINE 时表示地图服务不可用后使用直线距离降级。

3. 枚举和状态

3.1 服务类型 serviceType

值 含义
HELP_SEND 帮送
HELP_PICKUP 帮取

serviceType 只表达业务场景,不再表达是否加急。帮送和帮取共享同一套运价时段。

3.2 配送等级 deliveryType

值 含义
NORMAL 普通配送,加急费固定为 0
URGENT 1 对 1 加急配送,收取加急费并启用骑手独占规则

3.3 包裹类型 packageType

值 含义
DOCUMENT 文件
GIFT 礼品
CLOTHING 服饰
BEAUTY 美妆
DAILY_NECESSITIES 日用品
FOOD_INGREDIENTS 食材
ELECTRONICS 数码产品
SMALL_APPLIANCE 小家电
OTHER 其他

3.4 物品重量范围 weightRange

接口值 连续重量区间 UI 文案
UP_TO_5_KG 大于 0 且不超过 5 公斤 5 公斤以内
OVER_5_TO_10_KG 超过 5 且不超过 10 公斤 6–10 公斤
OVER_10_TO_15_KG 超过 10 且不超过 15 公斤 11–15 公斤
OVER_15_TO_20_KG 超过 15 且不超过 20 公斤 16–20 公斤

App 必须直接提交上述四个枚举之一,不再提交精确重量 totalWeightKg,也不再提交旧三档字段 packageSize。API 使用连续区间定义,UI 按设计稿显示整数范围;例如实际估重 5.5 公斤应选择 OVER_5_TO_10_KG。

weightRange 仅用于物品申报、订单展示和骑手判断是否适合承运,当前不参与基础配送费、距离费、加急费或总金额计算;选择不同重量范围不会改变报价。

3.5 配送方式 deliveryMode

值 含义
NOW 立即配送,也是未传值时的默认值
SCHEDULED 预约配送

预约配送要求:

  • scheduledPickupStartAt 和 scheduledPickupEndAt 都必填。
  • 开始时间不得早于当前时间,且不得晚于创建订单时刻后三天。
  • 结束时间必须比开始时间晚 30 分钟。
  • 预约订单创建后仍为 WAITING_ACCEPTANCE,但在预约开始时间到达前不会出现在骑手待抢列表中。

立即配送时不得传 scheduledPickupStartAt 和 scheduledPickupEndAt。

3.6 订单状态 status

WAITING_ACCEPTANCE -> ACCEPTED -> PICKED_UP -> DELIVERED -> COMPLETED
          |               |
          +--- 发件人可取消 +--- 发件人可取消
值 含义 下一步
WAITING_ACCEPTANCE 待骑手接单 骑手抢单;发件人可修改取送信息、追加小费或取消
ACCEPTED 骑手已接单 骑手确认取件,或发件人取消
PICKED_UP 骑手已取件 骑手确认送达
DELIVERED 骑手已送达 发件人或当前绑定的收件人确认收货;超过 24 小时可由系统自动完成
COMPLETED 已完成 终态
CANCELLED 已取消 终态

4. 推荐调用流程

4.1 用户端

  1. 调用首页接口取得当前可用服务及价格摘要。
  2. 从共享地址簿选择地址,或填写取件和收件信息。
  3. 调用报价接口,展示服务端返回的路线和费用。
  4. 用户确认后调用创建订单接口。创建时服务端会重新计算路线和金额,最终以创建响应为准。
  5. 使用 role=sender 查看“我发的”,使用 role=receiver 查看“我收的”,并通过列表或详情刷新状态。当前闪送模块没有 App 实时推送接口。
  6. 在“我发的”待接单详情页,使用最新 orderVersion 调用地址预报价与确认保存接口,或调用追加小费接口;每次成功后使用返回的完整详情刷新页面及版本。
  7. 仅发件人可在 WAITING_ACCEPTANCE 或 ACCEPTED 状态取消;发件人和当前绑定的收件人都可在 DELIVERED 状态确认收货。

4.2 骑手端

  1. 调用待抢列表查看当前可接订单。
  2. 可先调用详情接口查看同一份脱敏摘要。
  3. 调用抢单接口。只有抢单成功后才能取得完整联系人和精确地址。
  4. 到达取件点后先上传图片,再调用确认取件接口。
  5. 到达收件点后先上传图片;如订单启用 PIN,向收件人取得四位 PIN,再调用确认送达接口。

抢单为并发原子操作。即使列表中仍显示订单,也可能已被其他骑手抢走;收到失败响应后应刷新待抢列表。

5. 图片上传

闪送订单接口不接收文件,只接收上传完成后的 HTTP(S) 图片 URL。

App 可复用现有上传接口:

方法 路径 Content-Type 参数
POST /utils/Upload multipart/form-data 文件字段名 file

成功响应示例:

{
  "code": 200,
  "msg": "上传成功",
  "data": "/profile/upload/2026/09/01/example.jpg"
}

该接口返回的 data 可能是相对资源路径。提交给闪送接口前,必须补全为外部可访问的绝对 URL,例如:

https://api.example.com/profile/upload/2026/09/01/example.jpg

图片 URL 规则:

  • 只接受 http:// 或 https:// URL,必须包含主机名。
  • 单个 URL 最长 1000 个字符。
  • 寄件图片 senderImageUrls 可不传,最多 9 张。
  • 取件图片和送达图片各至少 1 张、最多 9 张。

6. 公共数据对象

6.1 下单地址对象

报价、创建订单及修改订单地址中的 pickup、delivery 使用以下结构:

{
  "name": "王小明",
  "phone": "0912345678",
  "address": "台北市信义区市府路1号",
  "addressDetail": "3楼A室",
  "city": "台北市",
  "area": "信义区",
  "handoffMethod": "请电话联系",
  "longitude": 121.5645,
  "latitude": 25.033
}
字段 类型 必填 规则
name string 是 最长 64 字符
phone string 是 最长 32 字符
address string 是 完整主地址,最长 255 字符
addressDetail string 否 楼层、门牌等,最长 255 字符
city string 否 最长 64 字符;用于骑手抢单前的区域展示
area string 否 最长 64 字符;用于骑手抢单前的区域展示
handoffMethod string 否 交接说明,最长 40 字符
longitude number 是 -180 至 180
latitude number 是 -90 至 90

取件和收件地址不能是相同地址文本,也不能使用完全相同的经纬度。

6.2 订单字段

App 完整详情使用以下订单字段,响应的 data 直接返回这些字段,不再套用 order、images、logs 包装。

字段 类型 说明
id number 订单主键;确认修改地址和追加小费时,将此值放入请求体 orderId
orderVersion integer 用户详情新增的订单版本;修改地址及追加小费必传,成功后递增。用户列表和骑手响应不返回
orderNo string 闪送订单号
serviceType string 业务场景:HELP_SEND 或 HELP_PICKUP
deliveryType string 配送等级:NORMAL 或 URGENT
status string 当前状态
packageType string 包裹类型
quantity integer 物品数量,正整数
weightRange string 用户选择的四档重量范围,取值见 3.4
specification string/null 体积或规格说明,最长 255 字符
deliveryMode string NOW 或 SCHEDULED
scheduledPickupStartAt datetime/null 预约开始时间
scheduledPickupEndAt datetime/null 预约结束时间
pinRequired boolean 是否需要交付 PIN
pickup object 取件地址快照,结构见 6.1
delivery object 收件地址快照,结构见 6.1
distanceMeters number 配送距离,单位米
distanceSource string ROUTE 或 STRAIGHT_LINE
estimatedDurationSeconds number/null 预计时长,单位秒
baseDeliveryFee integer 基础配送费,即起送价加距离费
distanceFee integer 超出起送距离后的距离费
urgentFee integer 加急费;普通配送为 0
tipAmount integer 用户填写的骑手小费
amount integer 订单金额,整数 TWD
currency string TWD
userNote string/null 用户备注
acceptedAt datetime/null 接单时间
pickedUpAt datetime/null 取件时间
deliveredAt datetime/null 送达时间
completedAt datetime/null 完成时间
cancelledAt datetime/null 取消时间
cancelReason string/null 取消原因
createTime datetime 创建时间
updateTime datetime 更新时间

用户列表摘要固定字段为:id,orderNo,serviceType,deliveryType,status,packageType,quantity,weightRange,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,estimatedDurationSeconds,baseDeliveryFee,urgentFee,tipAmount,amount,currency,deliveredAt,createTime。用户列表不返回联系人、电话、精确坐标、照片、日志或内部计价审计字段。

骑手列表摘要固定字段为:id,orderNo,serviceType,deliveryType,status,packageType,quantity,weightRange,specification,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pinRequired,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,pickupDistanceMeters,distanceMeters,estimatedDurationSeconds,baseDeliveryFee,distanceFee,urgentFee,tipAmount,amount,currency,payType,createTime。newTask 分页响应额外返回 nearbyTaskCount 和 highestOrderAmount,不返回内部加急比例、最低加急费或骑手收入字段。

6.3 App 完整详情对象

用户创建订单、用户订单详情、中单骑手详情及抢单成功响应的 data 结构为:

{
  "id": 101,
  "orderNo": "FD1234567890",
  "serviceType": "HELP_SEND",
  "deliveryType": "URGENT",
  "status": "ACCEPTED",
  "packageType": "DOCUMENT",
  "quantity": 1,
  "weightRange": "UP_TO_5_KG",
  "specification": "30 x 20 x 10 cm",
  "deliveryMode": "NOW",
  "scheduledPickupStartAt": null,
  "scheduledPickupEndAt": null,
  "pinRequired": true,
  "pickup": {},
  "delivery": {},
  "distanceMeters": 4200,
  "distanceSource": "ROUTE",
  "estimatedDurationSeconds": 900,
  "baseDeliveryFee": 72,
  "distanceFee": 12,
  "urgentFee": 15,
  "tipAmount": 5,
  "amount": 92,
  "currency": "TWD",
  "payType": "4",
  "userNote": null,
  "acceptedAt": "2026-09-01T10:20:00+08:00",
  "pickedUpAt": null,
  "deliveredAt": null,
  "completedAt": null,
  "cancelledAt": null,
  "cancelReason": null,
  "createTime": "2026-09-01T10:00:00+08:00",
  "updateTime": "2026-09-01T10:20:00+08:00",
  "senderImageUrls": [
    "https://api.example.com/profile/upload/sender-1.jpg"
  ],
  "pickupImageUrls": [],
  "deliveryImageUrls": [],
  "rider": {
    "name": "陈骑手",
    "avatar": "https://api.example.com/avatar.jpg",
    "rating": 4.9,
    "imUserId": "10086",
    "longitude": 121.56,
    "latitude": 25.03
  },
  "deliveryPinCode": "4821"
}
  • 用户创建、详情及编辑成功的响应还包含 orderVersion;骑手响应不包含。编辑前从用户详情取得最新版本,每次成功后替换本地版本。
  • senderImageUrls、pickupImageUrls 和 deliveryImageUrls 分别表示寄件、取件和送达图片数组。
  • App 详情不返回原始状态日志;页面使用 status、acceptedAt、pickedUpAt、deliveredAt、completedAt 和 cancelledAt 展示进度。
  • deliveryPinCode 只向订单发件人和当前绑定的收件人返回,并且只在启用 PIN 时出现;骑手响应永不返回此字段。
  • rider 只向用户返回。订单未接单时为 null。骑手对象不包含真实电话号码,联系入口使用 imUserId 打开 IM 会话,原型中的通话按钮没有接口支撑。
  • 骑手位置只在订单状态为 ACCEPTED 或 PICKED_UP 时向用户返回,其他状态下经纬度为 null。

平台订单详情是审计专用契约,仍可返回 {order,images,logs};App 不应依赖或解析该结构。

7. 用户端接口

所有接口均要求用户登录 token。

7.1 获取闪送首页配置

GET /system/flashDelivery/home

响应 data:

{
  "serviceTypes": ["HELP_SEND", "HELP_PICKUP"],
  "deliveryTypes": ["NORMAL", "URGENT"],
  "services": [
    {
      "serviceType": "HELP_SEND",
      "pricingId": 1,
      "startTime": "00:00",
      "endTime": "08:00",
      "startingDistance": 3.00,
      "startingFare": 60,
      "distance": 1.00,
      "freight": 10,
      "urgentRate": 20.00,
      "minimumUrgentFee": 15,
      "pricingVersion": 1,
      "currency": "TWD"
    },
    {
      "serviceType": "HELP_PICKUP",
      "pricingId": 1,
      "startTime": "00:00",
      "endTime": "08:00",
      "startingDistance": 3.00,
      "startingFare": 60,
      "distance": 1.00,
      "freight": 10,
      "urgentRate": 20.00,
      "minimumUrgentFee": 15,
      "pricingVersion": 1,
      "currency": "TWD"
    }
  ]
}

帮送和帮取会返回两条业务场景数据,但两条数据引用同一个 pricingId、pricingVersion 和费用配置。startTime 包含、endTime 不包含,均为台北时区的 HH:mm;24:00 只会作为结束时间。services 为空表示当前时刻没有匹配的统一运价。

7.2 获取实时报价

POST /system/flashDelivery/quote

请求体:

{
  "serviceType": "HELP_SEND",
  "deliveryType": "URGENT",
  "deliveryMode": "SCHEDULED",
  "scheduledPickupStartAt": "2026-09-08T16:30:00+08:00",
  "scheduledPickupEndAt": "2026-09-08T17:00:00+08:00",
  "packageType": "DOCUMENT",
  "quantity": 1,
  "weightRange": "UP_TO_5_KG",
  "specification": "30 x 20 x 10 cm",
  "tipAmount": 5,
  "pickup": {
    "name": "王小明",
    "phone": "0912345678",
    "address": "台北市信义区市府路1号",
    "addressDetail": "3楼A室",
    "city": "台北市",
    "area": "信义区",
    "handoffMethod": "请电话联系",
    "longitude": 121.5645,
    "latitude": 25.033
  },
  "delivery": {
    "name": "李小华",
    "phone": "0987654321",
    "address": "台北市大安区信义路三段56号",
    "addressDetail": "1楼",
    "city": "台北市",
    "area": "大安区",
    "handoffMethod": "交给本人",
    "longitude": 121.538,
    "latitude": 25.0335
  }
}

报价请求字段:

字段 类型 必填 规则
serviceType string 是 HELP_SEND 或 HELP_PICKUP
deliveryType string 是 NORMAL 或 URGENT
deliveryMode string 否 NOW 或 SCHEDULED,不传按 NOW 处理
scheduledPickupStartAt datetime 预约时是 SCHEDULED 时为未来三天内的预约开始时间;NOW 时不得传
scheduledPickupEndAt datetime 预约时是 必须比开始时间晚 30 分钟;NOW 时不得传
packageType string 是 取值见 3.3
quantity integer 是 大于 0 的整数
weightRange string 是 取值见 3.4,只能提交四个重量范围枚举之一
specification string 否 体积或规格说明,去除首尾空格后最长 255 字符
tipAmount integer 是 骑手小费,非负整数 TWD;没有小费时传 0
pickup object 是 完整取件信息,结构和规则见 6.1
delivery object 是 完整收件信息,结构和规则见 6.1

只要业务场景、配送等级、取件时间、物品信息、小费或取送地址发生变化,App 都必须重新调用报价接口,不能继续使用变化前的报价。

响应 data:

{
  "serviceType": "HELP_SEND",
  "deliveryType": "URGENT",
  "deliveryMode": "SCHEDULED",
  "scheduledPickupStartAt": "2026-09-08T16:30:00+08:00",
  "scheduledPickupEndAt": "2026-09-08T17:00:00+08:00",
  "pricingId": 1,
  "startTime": "16:00",
  "endTime": "18:00",
  "distanceMeters": 4200,
  "distanceSource": "ROUTE",
  "estimatedDurationSeconds": 900,
  "startingDistance": 3.00,
  "startingFare": 60,
  "distance": 1.00,
  "freight": 10,
  "urgentRate": 20.00,
  "minimumUrgentFee": 15,
  "billableDistance": 1.20,
  "distanceFee": 12,
  "baseDeliveryFee": 72,
  "urgentFee": 15,
  "tipAmount": 5,
  "amount": 92,
  "currency": "TWD",
  "pricingVersion": 1
}

距离规则与现有外卖订单一致:先用路线公里数减去 startingDistance;超出不足 0.5 公里时 billableDistance 为 0,达到 0.5 但不足 1 公里时按 1 公里,达到 1 公里后按实际超出距离。distanceFee = roundHalfUp(billableDistance / distance × freight),baseDeliveryFee = startingFare + distanceFee。普通配送的 urgentFee=0;加急配送的 urgentFee=max(minimumUrgentFee, roundHalfUp(baseDeliveryFee × urgentRate / 100));amount=baseDeliveryFee+urgentFee+tipAmount。小费不参与加急费计算,预计时长也不参与计价。

注意:baseDeliveryFee 是已经包含 distanceFee 的基础配送费小计。费用合计时不要再次把 distanceFee 加入总金额;distanceFee 仅用于向用户解释基础配送费中有多少是距离附加费。

立即配送按报价时刻匹配运价,预约配送按 scheduledPickupStartAt 的台北时间匹配运价。创建订单必须回传本次报价;后端会重新计算并逐项精确校验,任何变化都不会创建订单,而是返回最新报价供用户重新确认。

7.3 创建闪送订单

POST /system/flashDelivery/orders

创建请求必须在同一个 JSON 对象中完整提交 7.2 的所有报价入参,并增加报价回传、幂等号、PIN、图片和备注字段。weightRange 与报价接口的字段名及枚举规则完全一致,不能改传 totalWeightKg 或 packageSize。

{
  "serviceType": "HELP_SEND",
  "deliveryType": "URGENT",
  "deliveryMode": "SCHEDULED",
  "scheduledPickupStartAt": "2026-09-08T16:30:00+08:00",
  "scheduledPickupEndAt": "2026-09-08T17:00:00+08:00",
  "packageType": "DOCUMENT",
  "quantity": 1,
  "weightRange": "UP_TO_5_KG",
  "specification": "30 x 20 x 10 cm",
  "tipAmount": 5,
  "pickup": {
    "name": "王小明",
    "phone": "0912345678",
    "address": "台北市信义区市府路1号",
    "addressDetail": "3楼A室",
    "city": "台北市",
    "area": "信义区",
    "handoffMethod": "请电话联系",
    "longitude": 121.5645,
    "latitude": 25.033
  },
  "delivery": {
    "name": "李小华",
    "phone": "0987654321",
    "address": "台北市大安区信义路三段56号",
    "addressDetail": "1楼",
    "city": "台北市",
    "area": "大安区",
    "handoffMethod": "交给本人",
    "longitude": 121.538,
    "latitude": 25.0335
  },
  "clientRequestId": "FD-20260908-USER1001-0001",
  "pricingId": 1,
  "pricingVersion": 1,
  "quotedBaseDeliveryFee": 72,
  "quotedDistanceFee": 12,
  "quotedUrgentFee": 15,
  "quotedAmount": 92,
  "pinRequired": true,
  "senderImageUrls": [
    "https://api.example.com/profile/upload/sender-1.jpg"
  ],
  "userNote": "文件请勿折叠"
}

其中 serviceType 至 delivery 的字段定义与 7.2 报价请求表一致;以下是创建订单额外增加的字段:

新增字段 类型 必填 说明
clientRequestId string 是 客户端幂等请求号,去除首尾空格后最长 64 字符
pricingId integer 是 报价响应的运价配置 ID
pricingVersion integer 是 报价响应的配置版本
quotedBaseDeliveryFee integer 是 报价响应的基础配送费
quotedDistanceFee integer 是 报价响应的距离费
quotedUrgentFee integer 是 报价响应的加急费
quotedAmount integer 是 报价响应的总金额
pinRequired boolean 否 不传时默认为 true
payType string 否 支付方式:4=现金(默认)、6=线下转账。闪送款项直达骑手,仅允许线下方式,下单后锁定
senderImageUrls string[] 否 寄件图片,最多 9 张
userNote string 否 最长 500 字符

同一用户使用相同 clientRequestId 重试时,后端返回第一次创建的订单,不会重复创建。一次下单动作生成一个请求号;网络超时重试必须复用原请求号,新下单必须生成新请求号。使用旧请求号但修改请求体,仍会返回旧订单。

成功响应 data 为完整详情对象,初始状态为 WAITING_ACCEPTANCE。创建响应中的 amount 是最终整数订单金额。

若运价版本、路线或费用发生变化,响应 code=500、msg=报价已变化,请重新确认最新费用,并在 data 返回与报价接口相同的完整最新报价。App 必须更新费用明细并要求用户再次确认,不能自动用旧报价重试。

报价变化响应示例:

{
  "code": 500,
  "msg": "报价已变化,请重新确认最新费用",
  "data": {
    "serviceType": "HELP_SEND",
    "deliveryType": "URGENT",
    "deliveryMode": "SCHEDULED",
    "scheduledPickupStartAt": "2026-09-08T16:30:00+08:00",
    "scheduledPickupEndAt": "2026-09-08T17:00:00+08:00",
    "pricingId": 2,
    "startTime": "16:00",
    "endTime": "18:00",
    "distanceMeters": 4200,
    "distanceSource": "ROUTE",
    "estimatedDurationSeconds": 900,
    "startingDistance": 3.00,
    "startingFare": 60,
    "distance": 1.00,
    "freight": 10,
    "urgentRate": 25.00,
    "minimumUrgentFee": 15,
    "billableDistance": 1.20,
    "distanceFee": 12,
    "baseDeliveryFee": 72,
    "urgentFee": 18,
    "tipAmount": 5,
    "amount": 95,
    "currency": "TWD",
    "pricingVersion": 2
  }
}

创建时,后端会移除收件手机号中的空白、+、-、(、) 后,匹配有效且未删除的普通用户账号。仅在恰好匹配一个账号时把该账号固化为收件人;无匹配或重复匹配均不绑定。此绑定只执行一次,历史订单不会因收件人之后注册或修改手机号而自动认领。

7.4 查询用户订单列表

GET /system/flashDelivery/orders?page=1&size=10&role=sender

查询参数:

参数 必填 说明
page 否 默认 1,小于 1 时按 1 处理
size 否 默认 10,范围为 1 至 100
role 否 sender 查询“我发的”,receiver 查询“我收的”;默认 sender

仅查询当前用户作为指定角色的订单,按创建时间倒序返回全部状态的订单摘要。接口不接受 status、scene 或 serviceType;响应 data.records 中每项为用户列表摘要对象。

role 只接受 sender 或 receiver,不区分大小写;传空值按 sender 处理,传其他值返回业务错误。“我收的”依据订单当前绑定的收件账号;创建时匹配,确认修改取送信息时重新匹配收件电话,查询时不会重新按手机号匹配。

设计原型中的“待接单、配送中、已完成、已取消”筛选页签当前没有对应查询参数。第一版建议只展示“全部”列表;如需页签,只能基于当前页数据本地过滤,且不应依赖它得到准确的分页总数。

7.5 查询用户订单详情

GET /system/flashDelivery/orders/{id}

订单发件人和当前绑定的收件人均可访问。响应 data 直接为 App 完整订单字段,包括订单快照、三类图片数组、骑手摘要和可选的交付 PIN,不返回原始状态日志。响应不包含支付状态或支付方式字段,详情页不要展示“已付款”等支付信息。

7.6 用户取消订单

POST /system/flashDelivery/orders/{id}/cancel

请求体:

{
  "reason": "行程有变,不需要配送"
}
  • reason 必填,去除首尾空格后不能为空,最长 500 字符。
  • 仅订单发件人可以取消;收件人不能取消。
  • 仅 WAITING_ACCEPTANCE 和 ACCEPTED 状态允许用户取消。
  • PICKED_UP 之后需要平台介入,用户端不能直接取消。
  • 设计原型中“骑士接单前可免费取消”的文案比接口口径更严格:接口允许骑手已接单但尚未取件时取消,且当前没有支付,取消不产生任何费用;接入支付后取消规则需重新对齐。

7.7 用户确认收货

POST /system/flashDelivery/orders/{id}/confirmReceipt

无请求体。订单发件人或当前绑定的收件人都可操作;仅 DELIVERED 状态可确认,成功后状态变为 COMPLETED。

7.8 修改发件/收件信息:预报价

POST /system/flashDelivery/orders/{id}/address/quote

后端方法:quoteAddress;请求 DTO:FlashDeliveryAddressChangeRequest。

请求体:

{
  "orderVersion": 2,
  "pickup": {
    "name": "王小明",
    "phone": "0912345678",
    "address": "台北市信义区市府路1号",
    "addressDetail": "3楼A室",
    "city": "台北市",
    "area": "信义区",
    "handoffMethod": "请电话联系",
    "longitude": 121.5645,
    "latitude": 25.033
  },
  "delivery": {
    "name": "李小华",
    "phone": "0922222222",
    "address": "台北市大安区信义路三段56号",
    "addressDetail": "交前台",
    "city": "台北市",
    "area": "大安区",
    "handoffMethod": "交前台",
    "longitude": 121.543,
    "latitude": 25.0337
  }
}
字段 必填 说明
orderVersion 是 用户详情返回的非负整数订单版本,不是 pricingVersion
pickup 是 完整发件信息,结构和校验见 6.1
delivery 是 完整收件信息,结构和校验见 6.1
  • 可以只修改一端,也可以两端一起修改;请求始终提交两端完整信息,未修改的一端原样回传。可选字段不传或传 null 表示清空。
  • 仅订单创建人可调用,收件人不能调用。订单必须仍为 WAITING_ACCEPTANCE 且未分配骑手。
  • 服务端沿用订单原有计价规则和已有小费,重新计算路线、距离、预计时长和费用。后台调价或跨时段不会切换这笔订单的计价规则;仍待接单的预约单即使已到预约时间也可修改。
  • 此接口不保存地址、费用或状态;App 展示报价供用户确认后,再调用 7.9。
  • 修改的是当前订单,公共地址簿不会同步修改。

成功 data 使用 7.2 的完整报价结构,并增加 orderVersion。以下为需要展示和回传的关键字段示例(费用以实际响应为准):

{
  "orderVersion": 2,
  "distanceMeters": 4500,
  "distanceSource": "ROUTE",
  "estimatedDurationSeconds": 900,
  "baseDeliveryFee": 113,
  "distanceFee": 23,
  "urgentFee": 0,
  "tipAmount": 10,
  "amount": 123,
  "currency": "TWD"
}

distanceFee 已包含在 baseDeliveryFee 中,总金额为 baseDeliveryFee + urgentFee + tipAmount。地址编辑使用本接口报价,不使用 7.2 的新订单报价接口。

7.9 修改发件/收件信息:确认保存

POST /system/flashDelivery/orders/address

后端方法:updateAddress;请求 DTO:FlashDeliveryAddressConfirmRequest。

订单 ID 不放 URL,必须在请求体中提交 orderId(用户订单详情的 id,正整数)。同时提交与 7.8 同一次报价对应的完整 pickup、delivery 和 orderVersion,并增加以下必填字段:

确认字段 类型 从地址报价响应取值
orderId integer 用户订单详情的 id,不是订单号
quotedDistanceMeters integer distanceMeters
quotedBaseDeliveryFee integer baseDeliveryFee
quotedDistanceFee integer distanceFee
quotedUrgentFee integer urgentFee;普通配送也必须传 0
quotedAmount integer amount

例如在上节完整请求体中增加 orderId=101、quotedDistanceMeters=4500、quotedBaseDeliveryFee=113、quotedDistanceFee=23、quotedUrgentFee=0、quotedAmount=123。不要在确认报价后更换地址;更换地址后应重新预报价。

保存规则:

  • 服务端再次计算路线及费用,逐项核对上述距离与费用。任一不一致或漏传时,返回 code=500、国际化“报价已变化,请重新确认”及 data 中的最新完整报价,订单保持原值。App 应重新展示,并等待用户确认后再提交。
  • 只有订单归属、待接单状态、骑手为空及 orderVersion 同时匹配,才能原子保存。报价后被骑手接走、被取消或发生其他编辑时,保存会失败。
  • 成功时 data 直接返回完整订单详情,含更新后的两端信息、路线、费用和递增后的 orderVersion;订单 ID、编号、物品、原计价规则、预约信息和已有小费保持不变。
  • 收件电话重新匹配当前可绑定账号;无法唯一匹配时清空原收件人绑定。原账号不再是当前收件人时,随即失去“我收的”、详情及签收权限;创建人的权限不受影响。

7.10 追加骑手小费

POST /system/flashDelivery/orders/tip

后端方法:addTip;请求 DTO:FlashDeliveryTipAddRequest。

请求体:

{
  "orderId": 101,
  "orderVersion": 2,
  "additionalTipAmount": 20
}
字段 必填 规则
orderVersion 是 用户详情返回的最新订单版本
orderId 是 用户订单详情的 id,正整数;不放 URL
additionalTipAmount 是 本次追加的正整数 TWD;小数、零、负数、空值及超出金额范围均拒绝
  • 仅订单创建人能在待接单、无骑手时追加。金额是本次增量,例如原小费为 10,追加 20 后小费变为 30;不是把小费设为 20。
  • 仅增加 tipAmount 和 amount,基础配送费、加急费、路线和距离不变。此接口不发起支付或扣款。
  • 成功 data 直接返回更新后的完整订单详情,含递增后的 orderVersion。App 必须用响应刷新小费、总金额和版本。
  • 同一请求成功后,重复提交旧版本会被拒绝,不会重复加小费。网络超时应先刷新详情核对结果,不能自动换新版本再次追加。
  • 如果地址预报价后追加了小费,先前地址确认使用的版本已过期,需刷新详情并重新获取地址报价。

新增接口通用失败处理:

  • 缺失或负数版本:提示提供有效的订单版本,重新读取详情。
  • 旧版本或保存时订单发生变化:提示状态变化,刷新详情后由用户决定后续操作。
  • 已接单、已取消或已有骑手:关闭地址修改和追加小费入口。
  • 收件人及其他用户无权调用:按订单不存在或无权访问处理。

8. 骑手端接口

所有接口均要求骑手登录 token,且账号 userType 必须为 2。

接单使用共享 Redis 锁 lock:delivery:rider:{riderId} 串行执行跨外卖、闪送的资格检查和订单条件更新,并在事务完成后释放。该锁只覆盖接单事务,不会一直持有到配送完成;持续独占由每次接新单时查询骑手进行中的订单保证,Redis 锁用于防止两个并发接单请求同时绕过查询。

deliveryType=URGENT 的急送要求骑手没有任何进行中的外卖或闪送;骑手已有进行中的急送时,也不能再接普通闪送或外卖。普通闪送不会阻止骑手承接外卖,外卖也不会阻止骑手承接普通闪送。Redis 不可用或 3 秒内未取得锁时采用失败关闭策略,本次接单直接失败,不更新订单。

8.1 查询骑手订单列表

GET /system/flashDelivery/rider/orders?page=1&size=10&tab=newTask&longitude=121.5&latitude=25.0

查询参数:

参数 必填 说明
page 否 默认 1,小于 1 时按 1 处理
size 否 默认 10,范围为 1 至 100
tab 否 默认 newTask;可选 newTask、toPickup、delivering、completed 或 cancelled
longitude 否 骑手当前位置经度;newTask 传入时用于附近范围、取件点距离和排序,范围 -180 至 180
latitude 否 骑手当前位置纬度;newTask 传入时用于附近范围、取件点距离和排序,范围 -90 至 90

页签映射:

tab 对应状态 查询范围
newTask WAITING_ACCEPTANCE 当前可抢任务
toPickup ACCEPTED 当前骑手本人任务
delivering PICKED_UP 当前骑手本人任务
completed DELIVERED、COMPLETED 当前骑手本人任务
cancelled CANCELLED 当前骑手本人任务

除 newTask 外只查询当前骑手本人任务。闪送没有退款流程,不提供外卖列表中的 refund 页签。

newTask 的 longitude 和 latitude 仅用于附近范围筛选、计算取件点距离和排序;其他页签忽略坐标。列表按页签和骑手范围完成服务端过滤后再分页,不由 App 拉取完整集合后过滤。

newTask 分页响应的 data 除 records、total、current、size 外,还返回 nearbyTaskCount 和 highestOrderAmount。不返回尖峰倍率或骑手收入字段。amount 是服务端计算的订单报价金额,当前不会发起支付或扣款,页面文案不得表述为“预估收益”或“收益”;nearbyTaskCount 是附近待抢订单数,不是上线骑手数。

所有页签的 data.records 使用骑手列表摘要字段:

{
  "id": 101,
  "orderNo": "FD1234567890",
  "serviceType": "HELP_SEND",
  "deliveryType": "URGENT",
  "status": "WAITING_ACCEPTANCE",
  "packageType": "DOCUMENT",
  "quantity": 1,
  "weightRange": "UP_TO_5_KG",
  "specification": "30 x 20 x 10 cm",
  "deliveryMode": "NOW",
  "scheduledPickupStartAt": null,
  "scheduledPickupEndAt": null,
  "pinRequired": true,
  "pickupAddress": "台北市信义区市府路1号",
  "pickupDetailAddress": "3楼A室",
  "deliveryAddress": "台北市大安区信义路三段56号",
  "deliveryDetailAddress": "1楼",
  "pickupDistanceMeters": 420,
  "distanceMeters": 4200,
  "estimatedDurationSeconds": 900,
  "baseDeliveryFee": 72,
  "distanceFee": 12,
  "urgentFee": 15,
  "tipAmount": 5,
  "amount": 92,
  "currency": "TWD",
  "createTime": "2026-09-01T09:00:00+08:00"
}

接单前列表可展示完整的取件、送达文字地址(包括详细地址),但不得返回联系人、电话、实际 PIN、精确坐标、用户备注、图片凭证、状态日志、用户 ID、幂等号或计价审计字段。列表中的文字地址不能作为精确坐标使用。

8.2 查询骑手订单详情

GET /system/flashDelivery/rider/orders/{id}

响应 data 始终直接返回订单字段,顶层结构保持稳定,不使用 {order,images,logs} 包装,也不要求 App 判断 data.order。接单前隐藏联系人、电话、实际 PIN、精确坐标和履约图片;联系人姓名与电话一样都不返回,列表和详情不要按设计原型渲染“·联系人姓名”。当前骑手接单后,详情接口才补齐其有权限查看的完整履约信息。

8.3 抢单

POST /system/flashDelivery/rider/orders/{id}/accept

无请求体。服务端取得骑手锁后重新校验,不信任待抢列表的过滤结果。骑手必须已审核、账号启用、userType=2、offline=0 并开通闪送配送;同时校验进行中闪送容量、外卖与闪送合计容量及急送独占。容量分别由 sys_rider_flash_active_order_limit、sys_rider_total_active_order_limit 控制,外卖接单另使用 sys_rider_food_active_order_limit;配置只接受 1..20,缺失、空值、非法、越界或读取异常一律按 1 执行,不会变成不限单。

抢单成功后状态变为 ACCEPTED,响应 data 直接返回完整订单字段。失败会返回明确原因,包括账号未审核或停用、骑手离线、未开通闪送、闪送任务已满、配送任务总量已满、急送独占冲突或订单已被其他骑手接走;App 应展示 msg、保留在列表页并刷新可接任务。

当前版本尚未把容量规则用于待抢列表,也未在接单时校验服务端位置更新时间或接单距离,因此列表可见不代表一定能接;最终以本接口复核为准。

8.4 确认取件

POST /system/flashDelivery/rider/orders/{id}/pickup

请求体:

{
  "imageUrls": [
    "https://api.example.com/profile/upload/pickup-1.jpg"
  ]
}

仅订单骑手可在 ACCEPTED 状态调用。图片至少 1 张、最多 9 张;成功后状态变为 PICKED_UP。

8.5 确认送达

POST /system/flashDelivery/rider/orders/{id}/deliver

启用 PIN 的订单:

{
  "imageUrls": [
    "https://api.example.com/profile/upload/delivery-1.jpg"
  ],
  "pinCode": "4821"
}

未启用 PIN 的订单可不传 pinCode:

{
  "imageUrls": [
    "https://api.example.com/profile/upload/delivery-1.jpg"
  ]
}

仅订单骑手可在 PICKED_UP 状态调用。图片至少 1 张、最多 9 张。PIN 校验失败时不会保存送达图片,也不会改变订单状态;成功后状态变为 DELIVERED。

9. 共享地址簿接口

地址簿由闪送和现有收货业务共用。以下接口都要求用户登录 token,并且只能操作当前用户自己的地址。

9.1 查询地址列表

GET /system/address/getaddress?keyword=王小明

keyword 可选,同时搜索姓名、电话、主地址和详细地址。置顶地址优先,响应 data 为地址数组。

9.2 查询地址详情

GET /system/address/getaddressxq?id={addressId}

响应 data 为地址对象。

9.3 新增或修改地址

POST /system/address/address

新增地址不传 id;修改地址传本人地址的 id:

{
  "id": null,
  "name": "王小明",
  "phone": "0912345678",
  "address": "台北市信义区市府路1号",
  "addressDetail": "3楼A室",
  "longitude": 121.5645,
  "latitude": 25.033,
  "country": "TW",
  "province": "台北市",
  "city": "台北市",
  "area": "信义区",
  "annexes": null
}
字段 必填 规则
name 是 最长 64 字符
phone 是 最长 32 字符
address 是 最长 255 字符
addressDetail 否 最长 255 字符
longitude 是 -180 至 180
latitude 是 -90 至 90
country、province、city、area 否 各最长 64 字符
annexes 否 最长 1000 字符

后端忽略客户端身份字段,地址所有者始终取自 token。保存成功后 data 返回保存后的地址对象。

地址簿对象不含 handoffMethod。把地址用于闪送报价或下单时,需要映射联系人、地址和坐标,并按本次订单另行填写交接方式。

9.4 删除地址

DELETE /system/address/{id}

无请求体,仅可删除本人地址。

9.5 置顶地址

POST /system/address/{id}/top

无请求体。置顶只影响当前用户地址簿的排序。

10. 常见业务错误与处理建议

msg 含义 常见原因 App 处理建议
token 已过期 登录态失效 清理登录态并重新登录
对应取件时段暂无可用运价 立即时刻或预约开始时刻没有匹配统一运价 重新选择取件时间或稍后重试
报价已变化,请重新确认最新费用 创建订单报价不一致,或修改地址的确认距离/费用与复算结果不一致 用响应 data 刷新费用并让用户再次确认
物品数量、重量范围或规格信息不正确 数量非正整数、weightRange 缺失或不属于四个合法枚举、规格过长 返回物品信息页修正
取件地址和收件地址不能相同 地址文本相同或经纬度完全相同 要求用户修改地址
配送距离不能超过 40 公里 服务端路线距离超限 提示当前路线不可下单
预约取件时段无效 时间已过、超过三天或不是 30 分钟 重新选择预约时段
订单已被其他骑手接走 并发抢单失败 刷新待抢列表
骑手存在进行中的独占配送任务 当前订单或骑手已有急送冲突 保留当前页并刷新可接任务
系统繁忙,请稍后重试 Redis 不可用或 3 秒内未取得骑手锁 不重复提交,稍后刷新并重试
当前订单状态不允许此操作 页面状态已过期或重复操作 重新拉取订单详情
仅待接单且未分配骑手的订单可以修改 修改地址/追加小费时已接单、已取消或已有骑手 刷新详情并关闭编辑入口
请提供有效的订单版本 缺失或负数 orderVersion 读取用户详情取得版本
订单状态已变化,请刷新后重试 编辑使用旧版本,或最终保存时发生接单/取消/其他编辑 刷新详情;地址重新报价;小费先核对是否已追加
追加小费必须为正整数且不能超出金额范围 小数、零、负数、空值或金额溢出 修正本次追加金额
交付 PIN 不正确 骑手提交 PIN 错误 保留页面并重新输入 PIN
闪送订单不存在或无权访问 ID 不存在或不属于当前账号 返回列表并刷新数据
凭证图片地址无效 URL 不是完整 HTTP(S) 地址 检查上传结果并补全资源域名

11. 接入验收清单

  • 首页只展示 /home 返回的当前时段可用服务,不在 App 中维护启停状态或默认价格。
  • 报价和创建订单都提交 serviceType、deliveryType、配送方式、packageType、quantity、weightRange、规格、骑手小费及取送坐标。
  • 创建订单原样回传报价的配置 ID、版本、基础配送费、距离费、加急费和总金额;报价变化时展示最新明细并重新确认。
  • 金额按整数 TWD 展示;baseDeliveryFee 已包含 distanceFee,距离费只作为基础配送费的明细展示,总金额只按 baseDeliveryFee + urgentFee + tipAmount 计算;不显示支付成功、骑手收入、时长费或尖峰费用。
  • 创建订单具备稳定的 clientRequestId 重试策略。
  • 待接单详情的“改地址电话”“加小费”仅向订单创建人开放;三个新增接口均携带最新 orderVersion。
  • 修改地址先调用 7.8 展示新路线和费用,用户确认后调用 7.9;报价变化必须再次确认,不提前更新本地订单。
  • 追加小费传本次增量,成功后同步展示返回的小费及总额;超时先查询,不自动更换版本重复追加。
  • 报价后接单、取消或追加小费导致版本过期时,旧地址确认必须失败;编辑成功后用返回的版本替换本地版本。
  • 预约时间满足未来三天内、固定 30 分钟的约束。
  • App 使用响应体 code 判断成功或失败。
  • 分页列表从 data.records 和 data.total 取值。
  • 图片先上传,再向闪送接口提交完整 HTTP(S) URL。
  • “我发的”只在待接单或已接单状态显示取消入口;“我发的”和“我收的”都只在已送达状态显示确认收货入口。
  • 骑手抢单前可展示完整文字地址但不使用坐标导航;抢单成功后再读取联系人、电话、精确坐标和履约图片。
  • 骑手严格按 ACCEPTED -> PICKED_UP -> DELIVERED 顺序操作。
  • 启用 PIN 时,骑手送达必须提交用户端详情显示的四位 PIN。
  • 对抢单冲突、状态变化和重复点击,均以刷新后的服务端订单状态为准。
  • 用户订单列表使用 role=sender/receiver 切换“我发的”和“我收的”;状态页签如需展示,仅基于当前页数据本地过滤。
  • “我收的”按订单当前绑定的账号展示;创建或确认修改取送信息时匹配收件电话,清空或更换绑定后旧收件人不再有访问权;查询时不动态认领历史订单。
  • 发布成功页显示“发布后等待附近骑手接单”,不显示虚构的骑手数量或预计接单时长。
  • 骑手端金额文案统一为“订单金额”,不使用“收益”;联系骑手/用户通过 IM,不提供通话。

2026-09-15:多取货点客户端接入

本节覆盖上文单点输入、订单级取件和仅按主状态显示取消按钮的旧说明。完整字段与异常规则见 API 契约多取货点增量。

下单与编辑

首页读取 pickupStopLimit/showMultiPickup/showUrgentOption。用户可新增取货点、填写每站独立物品,并在报价前排序;最终 pickups 数组顺序即报价顺序。每个数组项直接包含地址字段和 packageType/quantity/weightRange/specification。数组模式不再传旧 pickup 或订单级物品。唯一 delivery 保持原格式。

{
  "serviceType": "HELP_SEND", "deliveryType": "NORMAL", "deliveryMode": "NOW", "tipAmount": 0,
  "pickups": [
    {"name":"寄件人 A","phone":"0912345678","address":"台北市信义区 A 点","latitude":25.033,"longitude":121.5645,"packageType":"DOCUMENT","quantity":1,"weightRange":"UP_TO_5_KG"},
    {"name":"寄件人 B","phone":"0922345678","address":"台北市大安区 B 点","latitude":25.026,"longitude":121.543,"packageType":"GIFT","quantity":2,"weightRange":"OVER_5_TO_10_KG"}
  ],
  "delivery":{"name":"收件人","phone":"0932345678","address":"台北市中山区收件点","latitude":25.052,"longitude":121.531}
}

创建时再附加既有幂等号、报价版本和金额回传字段;待接单编辑使用完整数组,调整顺序同样先重报价并确认。下单页面提示“默认按此顺序取件,骑手如需调整将先与您沟通”。用户原顺序及金额不会因骑手调整访问先后而改变。

骑手目标与实际取件

  1. 详情读取 stops,currentTargetStopId,orderVersion,默认引导至用户顺序中的最早未取站。站点 id 是服务端 ID,不是数组下标。
  2. 骑手需要先去 B 时,先联系下单人取得同意,再调用 POST /system/flashDelivery/rider/orders/{id}/stops/{stopId}/target:

    {"orderVersion":3,"clientRequestId":"route-change-uuid","reason":"A 尚未备好,下单人同意先去 B","consentDeclared":true}
    
  3. 请求成功后刷新详情,以服务端目标导航。重试沿用同一请求号;重新调整用新请求号及最新版本。声明仅表示骑手作出了声明,不展示成用户已在 App 审批。订单聊天可留作沟通依据。

  4. 导航与取件按钮分开。实际取到货后,调用 POST /rider/orders/{id}/stops/{stopId}/pickup 提交 imageUrls;也可使用原 /pickup,多点必须提供 body stopId,单点可省略。

  5. B 取件后默认回到 A;所有取货点完成前不能送达。某站未备好可以沟通后换顺序,但站点不能删除,也不能用虚假取件跳过;最终无法取货须联系平台处理。

进度、取消与导航

各端显示 pickedUpStopCount/pickupStopCount,保留原站点顺序展示实际进度;部分已取仍为 ACCEPTED,用户取消按钮使用 canUserCancel 且结合当前用户角色。任意取件、取消、调整或刷新后重新读取最新详情,不能仅按主状态判断按钮。

导航目标由 currentTargetStopId 定位,全部取完后为唯一收货点。客户端生成 Google Maps URL,使用 api=1&travelmode=two-wheeler&dir_action=navigate,地址参数正确编码,起点取设备当前位置。没有定位或无法启动外部地图时,保留目标地址并允许重试;不得因此自动取件。后端不返回导航 URL。

上线前用户、骑手端必须完成数组、逐站凭证、目标调整及状态按钮适配再开启多点开关。当前工作区未提供对应 App 页面实现,本节为客户端交付契约,不能作为 App 已联调证据。