# 闪送功能 App 接口接入文档 本文档供用户端 App 和骑手端 App 接入闪送功能使用,以当前后端实现为准。文档只描述接口契约和业务流程,不包含 App 前端实现代码。 ## 1. 功能范围 当前闪送支持: - 帮送、帮取、加急送三种服务。 - 共享地址簿、路线报价、立即配送和预约配送。 - 用户发布订单、查询订单、取消订单和确认收货。 - 骑手查看待抢订单、抢单、确认取件和确认送达。 - 可选的四位交付 PIN。 - 寄件、取件和送达图片凭证。 - 送达 24 小时后仍未由用户确认的订单自动完成。 当前不包含支付、退款、骑手收入、结算、代购垫付、自动派单和骑手放弃订单。 设计原型中的支付方式选择、已付款状态、附近骑手上线数量、预计接单时间和骑手收益金额均为静态示意,接口不提供这些数据,App 不应展示或请求它们。 ## 2. 通用约定 ### 2.1 基础地址和请求头 接口路径均为相对路径,实际请求地址为: ```text {baseUrl}{接口路径} ``` 除特别说明外,用户端和骑手端接口都必须携带以下请求头: | 请求头 | 必填 | 说明 | |---|---:|---| | `token` | 是 | App 登录后取得的 JWT。不要放在 `Authorization` 中 | | `Content-Type` | POST JSON 接口必填 | `application/json` | 用户身份和骑手身份均由 `token` 解析,请求体中不提交 `userId` 或 `riderId`。 ### 2.2 统一响应 成功响应: ```json { "code": 200, "msg": "操作成功", "data": {} } ``` 无返回数据的成功响应通常不含 `data`: ```json { "code": 200, "msg": "操作成功" } ``` 业务失败响应: ```json { "code": 500, "msg": "当前订单状态不允许此操作" } ``` 登录失效响应: ```json { "code": 401, "msg": "token已过期,请重新登录!" } ``` App 必须以响应体 `code` 判断业务是否成功,不能只依赖 HTTP 状态码。`msg` 已由后端国际化,可直接用于错误提示。 ### 2.3 分页响应 列表接口的分页数据位于 `data` 中,不使用若依传统的顶层 `rows/total`: ```json { "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` 和 `amount` 都是整数,不带小数。 - `distanceMeters` 单位为米。 - 运价配置中的 `startingDistance`、`distance` 以及报价中的 `billableDistance` 单位为公里,可保留两位小数。 - `estimatedDurationSeconds` 单位为秒,路线服务降级时可能为 `null`。 - `distanceSource` 为 `ROUTE` 时表示地图驾车路线,为 `STRAIGHT_LINE` 时表示地图服务不可用后使用直线距离降级。 ## 3. 枚举和状态 ### 3.1 服务类型 `serviceType` | 值 | 含义 | |---|---| | `HELP_SEND` | 帮送 | | `HELP_PICKUP` | 帮取 | | `URGENT` | 加急送 | 是否可下单以首页接口当前实际返回的服务为准。运价没有启停状态;只有当前时间命中已配置运价时段的服务才会返回,不要在 App 中假定三种服务始终全部可用。 ### 3.2 包裹类型 `packageType` | 值 | 含义 | |---|---| | `DOCUMENT` | 文件 | | `GIFT` | 礼品 | | `CLOTHING` | 服饰 | | `BEAUTY` | 美妆 | | `DAILY_NECESSITIES` | 日用品 | | `FOOD_INGREDIENTS` | 食材 | | `ELECTRONICS` | 数码产品 | | `SMALL_APPLIANCE` | 小家电 | | `OTHER` | 其他 | ### 3.3 包裹重量档 `packageSize` | 值 | 含义 | |---|---| | `SMALL` | 不超过 5kg | | `MEDIUM` | 不超过 12kg | | `LARGE` | 不超过 20kg | 接口只提交重量档,不提交精确重量。 ### 3.4 配送方式 `deliveryMode` | 值 | 含义 | |---|---| | `NOW` | 立即配送,也是未传值时的默认值 | | `SCHEDULED` | 预约配送 | 预约配送要求: - `scheduledPickupStartAt` 和 `scheduledPickupEndAt` 都必填。 - 开始时间不得早于当前时间,且不得晚于创建订单时刻后三天。 - 结束时间必须比开始时间晚 30 分钟。 - 预约订单创建后仍为 `WAITING_ACCEPTANCE`,但在预约开始时间到达前不会出现在骑手待抢列表中。 立即配送时不得传 `scheduledPickupStartAt` 和 `scheduledPickupEndAt`。 ### 3.5 订单状态 `status` ```text WAITING_ACCEPTANCE -> ACCEPTED -> PICKED_UP -> DELIVERED -> COMPLETED | | +---- 用户可取消 +---- 用户可取消 ``` | 值 | 含义 | 下一步 | |---|---|---| | `WAITING_ACCEPTANCE` | 待骑手接单 | 骑手抢单,或用户取消 | | `ACCEPTED` | 骑手已接单 | 骑手确认取件,或用户取消 | | `PICKED_UP` | 骑手已取件 | 骑手确认送达 | | `DELIVERED` | 骑手已送达 | 用户确认收货;超过 24 小时可由系统自动完成 | | `COMPLETED` | 已完成 | 终态 | | `CANCELLED` | 已取消 | 终态 | ## 4. 推荐调用流程 ### 4.1 用户端 1. 调用首页接口取得当前可用服务及价格摘要。 2. 从共享地址簿选择地址,或填写取件和收件信息。 3. 调用报价接口,展示服务端返回的路线和费用。 4. 用户确认后调用创建订单接口。创建时服务端会重新计算路线和金额,最终以创建响应为准。 5. 通过订单列表或详情刷新状态。当前闪送模块没有 App 实时推送接口。 6. 在 `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` | 成功响应示例: ```json { "code": 200, "msg": "上传成功", "data": "/profile/upload/2026/09/01/example.jpg" } ``` 该接口返回的 `data` 可能是相对资源路径。提交给闪送接口前,必须补全为外部可访问的绝对 URL,例如: ```text 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` 使用以下结构: ```json { "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 | 订单主键,后续接口路径使用此值 | | `orderNo` | string | 闪送订单号 | | `serviceType` | string | 服务类型 | | `status` | string | 当前状态 | | `packageType` | string | 包裹类型 | | `packageSize` | string | 包裹重量档 | | `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 | 预计时长,单位秒 | | `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,status,packageType,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,estimatedDurationSeconds,amount,currency,deliveredAt,createTime`。用户列表不返回联系人、电话、精确坐标、照片、日志或计价审计字段。 骑手列表摘要固定字段为:`id,orderNo,serviceType,status,packageType,packageSize,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pinRequired,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,pickupDistanceMeters,distanceMeters,estimatedDurationSeconds,amount,currency,createTime`。`newTask` 分页响应额外返回 `nearbyTaskCount` 和 `highestOrderAmount`,不返回尖峰倍率或骑手收入字段。 ### 6.3 App 完整详情对象 用户创建订单、用户订单详情、中单骑手详情及抢单成功响应的 `data` 结构为: ```json { "id": 101, "orderNo": "FD1234567890", "serviceType": "URGENT", "status": "ACCEPTED", "packageType": "DOCUMENT", "packageSize": "SMALL", "deliveryMode": "NOW", "scheduledPickupStartAt": null, "scheduledPickupEndAt": null, "pinRequired": true, "pickup": {}, "delivery": {}, "distanceMeters": 4200, "distanceSource": "ROUTE", "estimatedDurationSeconds": 900, "amount": 90, "currency": "TWD", "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" } ``` - `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 获取闪送首页配置 ```http GET /system/flashDelivery/home ``` 响应 `data`: ```json { "services": [ { "serviceType": "HELP_SEND", "pricingId": 1, "startTime": "00:00", "endTime": "08:00", "startingDistance": 3.00, "startingFare": 60, "distance": 1.00, "freight": 10, "pricingVersion": 1, "currency": "TWD" } ] } ``` 只返回当前时间命中运价时段的服务。`startTime` 包含、`endTime` 不包含,均为业务当地时间的 `HH:mm`;`24:00` 只会作为结束时间。`services` 为空表示当前时段没有可下单的闪送服务。 ### 7.2 获取实时报价 ```http POST /system/flashDelivery/quote ``` 请求体: ```json { "serviceType": "HELP_SEND", "packageType": "DOCUMENT", "packageSize": "SMALL", "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 } } ``` 响应 `data`: ```json { "serviceType": "HELP_SEND", "pricingId": 1, "startTime": "00:00", "endTime": "08:00", "distanceMeters": 4200, "distanceSource": "ROUTE", "estimatedDurationSeconds": 900, "startingDistance": 3.00, "startingFare": 60, "distance": 1.00, "freight": 10, "billableDistance": 1.20, "distanceFee": 12, "amount": 72, "currency": "TWD", "pricingVersion": 1 } ``` 计价规则与现有外卖订单一致:先用路线公里数减去 `startingDistance`;超出不足 `0.5` 公里时 `billableDistance` 为 `0`,达到 `0.5` 但不足 `1` 公里时按 `1` 公里,达到 `1` 公里后按实际超出距离。里程费用按 `billableDistance / distance * freight` 计算并四舍五入为整数 TWD,最终 `amount = startingFare + distanceFee`。预计时长不参与计价,也没有最低价、每分钟价格或尖峰倍率。 报价仅用于展示。创建订单时后端会重新匹配当前运价时段并计算最新路线和金额,因此最终金额可能与先前报价不同。当前时间没有匹配时段时,接口返回“当前时段暂无可用运价”。 ### 7.3 创建闪送订单 ```http POST /system/flashDelivery/orders ``` 请求体在报价请求字段基础上增加: ```json { "serviceType": "HELP_SEND", "packageType": "DOCUMENT", "packageSize": "SMALL", "pickup": {}, "delivery": {}, "clientRequestId": "FD-20260901-USER1001-0001", "deliveryMode": "SCHEDULED", "scheduledPickupStartAt": "2026-09-02T10:00:00+08:00", "scheduledPickupEndAt": "2026-09-02T10:30:00+08:00", "pinRequired": true, "senderImageUrls": [ "https://api.example.com/profile/upload/sender-1.jpg" ], "userNote": "文件请勿折叠" } ``` | 新增字段 | 类型 | 必填 | 说明 | |---|---|---:|---| | `clientRequestId` | string | 是 | 客户端幂等请求号,去除首尾空格后最长 64 字符 | | `deliveryMode` | string | 否 | 不传时默认为 `NOW` | | `scheduledPickupStartAt` | datetime | 预约时必填 | 预约开始时间 | | `scheduledPickupEndAt` | datetime | 预约时必填 | 必须比开始时间晚 30 分钟 | | `pinRequired` | boolean | 否 | 不传时默认为 `true` | | `senderImageUrls` | string[] | 否 | 寄件图片,最多 9 张 | | `userNote` | string | 否 | 最长 500 字符 | 同一用户使用相同 `clientRequestId` 重试时,后端返回第一次创建的订单,不会重复创建。一次下单动作生成一个请求号;网络超时重试必须复用原请求号,新下单必须生成新请求号。使用旧请求号但修改请求体,仍会返回旧订单。 成功响应 `data` 为完整详情对象,初始状态为 `WAITING_ACCEPTANCE`。创建响应中的 `amount` 是最终整数订单金额。 ### 7.4 查询用户订单列表 ```http GET /system/flashDelivery/orders?page=1&size=10 ``` 查询参数: | 参数 | 必填 | 说明 | |---|---:|---| | `page` | 否 | 默认 `1`,小于 `1` 时按 `1` 处理 | | `size` | 否 | 默认 `10`,范围为 `1` 至 `100` | 仅查询当前用户,按创建时间倒序返回全部状态的订单摘要。接口不接受 `status`、`scene` 或 `serviceType`;响应 `data.records` 中每项为用户列表摘要对象。 设计原型中的“待接单、配送中、已完成、已取消”筛选页签当前没有对应查询参数。第一版建议只展示“全部”列表;如需页签,只能基于当前页数据本地过滤,且不应依赖它得到准确的分页总数。 ### 7.5 查询用户订单详情 ```http GET /system/flashDelivery/orders/{id} ``` 仅订单所属用户可访问。响应 `data` 直接为 App 完整订单字段,包括订单快照、三类图片数组、骑手摘要和可选的交付 PIN,不返回原始状态日志。响应不包含支付状态或支付方式字段,详情页不要展示“已付款”等支付信息。 ### 7.6 用户取消订单 ```http POST /system/flashDelivery/orders/{id}/cancel ``` 请求体: ```json { "reason": "行程有变,不需要配送" } ``` - `reason` 必填,去除首尾空格后不能为空,最长 500 字符。 - 仅 `WAITING_ACCEPTANCE` 和 `ACCEPTED` 状态允许用户取消。 - `PICKED_UP` 之后需要平台介入,用户端不能直接取消。 - 设计原型中“骑士接单前可免费取消”的文案比接口口径更严格:接口允许骑手已接单但尚未取件时取消,且当前没有支付,取消不产生任何费用;接入支付后取消规则需重新对齐。 ### 7.7 用户确认收货 ```http POST /system/flashDelivery/orders/{id}/confirmReceipt ``` 无请求体。仅 `DELIVERED` 状态可操作,成功后状态变为 `COMPLETED`。 ## 8. 骑手端接口 所有接口均要求骑手登录 `token`,且账号 `userType` 必须为 `2`。 ### 8.1 查询骑手订单列表 ```http 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` 使用骑手列表摘要字段: ```json { "id": 101, "orderNo": "FD1234567890", "serviceType": "URGENT", "status": "WAITING_ACCEPTANCE", "packageType": "DOCUMENT", "packageSize": "SMALL", "deliveryMode": "NOW", "scheduledPickupStartAt": null, "scheduledPickupEndAt": null, "pinRequired": true, "pickupAddress": "台北市信义区市府路1号", "pickupDetailAddress": "3楼A室", "deliveryAddress": "台北市大安区信义路三段56号", "deliveryDetailAddress": "1楼", "pickupDistanceMeters": 420, "distanceMeters": 4200, "estimatedDurationSeconds": 900, "amount": 90, "currency": "TWD", "createTime": "2026-09-01T09:00:00+08:00" } ``` 接单前列表可展示完整的取件、送达文字地址(包括详细地址),但不得返回联系人、电话、实际 PIN、精确坐标、用户备注、图片凭证、状态日志、用户 ID、幂等号或计价审计字段。列表中的文字地址不能作为精确坐标使用。 ### 8.2 查询骑手订单详情 ```http GET /system/flashDelivery/rider/orders/{id} ``` 响应 `data` 始终直接返回订单字段,顶层结构保持稳定,不使用 `{order,images,logs}` 包装,也不要求 App 判断 `data.order`。接单前隐藏联系人、电话、实际 PIN、精确坐标和履约图片;联系人姓名与电话一样都不返回,列表和详情不要按设计原型渲染“·联系人姓名”。当前骑手接单后,详情接口才补齐其有权限查看的完整履约信息。 ### 8.3 抢单 ```http POST /system/flashDelivery/rider/orders/{id}/accept ``` 无请求体。抢单成功后状态变为 `ACCEPTED`,响应 `data` 直接返回完整订单字段;订单已被其他骑手抢走时返回“订单已被其他骑手接走”。 ### 8.4 确认取件 ```http POST /system/flashDelivery/rider/orders/{id}/pickup ``` 请求体: ```json { "imageUrls": [ "https://api.example.com/profile/upload/pickup-1.jpg" ] } ``` 仅订单骑手可在 `ACCEPTED` 状态调用。图片至少 1 张、最多 9 张;成功后状态变为 `PICKED_UP`。 ### 8.5 确认送达 ```http POST /system/flashDelivery/rider/orders/{id}/deliver ``` 启用 PIN 的订单: ```json { "imageUrls": [ "https://api.example.com/profile/upload/delivery-1.jpg" ], "pinCode": "4821" } ``` 未启用 PIN 的订单可不传 `pinCode`: ```json { "imageUrls": [ "https://api.example.com/profile/upload/delivery-1.jpg" ] } ``` 仅订单骑手可在 `PICKED_UP` 状态调用。图片至少 1 张、最多 9 张。PIN 校验失败时不会保存送达图片,也不会改变订单状态;成功后状态变为 `DELIVERED`。 ## 9. 共享地址簿接口 地址簿由闪送和现有收货业务共用。以下接口都要求用户登录 `token`,并且只能操作当前用户自己的地址。 ### 9.1 查询地址列表 ```http GET /system/address/getaddress?keyword=王小明 ``` `keyword` 可选,同时搜索姓名、电话、主地址和详细地址。置顶地址优先,响应 `data` 为地址数组。 ### 9.2 查询地址详情 ```http GET /system/address/getaddressxq?id={addressId} ``` 响应 `data` 为地址对象。 ### 9.3 新增或修改地址 ```http POST /system/address/address ``` 新增地址不传 `id`;修改地址传本人地址的 `id`: ```json { "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 删除地址 ```http DELETE /system/address/{id} ``` 无请求体,仅可删除本人地址。 ### 9.5 置顶地址 ```http POST /system/address/{id}/top ``` 无请求体。置顶只影响当前用户地址簿的排序。 ## 10. 常见业务错误与处理建议 | `msg` 含义 | 常见原因 | App 处理建议 | |---|---|---| | token 已过期 | 登录态失效 | 清理登录态并重新登录 | | 当前时段暂无可用运价 | 当前时间没有匹配所选服务的运价时段 | 刷新首页服务列表或稍后重试 | | 取件地址和收件地址不能相同 | 地址文本相同或经纬度完全相同 | 要求用户修改地址 | | 配送距离不能超过 40 公里 | 服务端路线距离超限 | 提示当前路线不可下单 | | 预约取件时段无效 | 时间已过、超过三天或不是 30 分钟 | 重新选择预约时段 | | 订单已被其他骑手接走 | 并发抢单失败 | 刷新待抢列表 | | 当前订单状态不允许此操作 | 页面状态已过期或重复操作 | 重新拉取订单详情 | | 交付 PIN 不正确 | 骑手提交 PIN 错误 | 保留页面并重新输入 PIN | | 闪送订单不存在或无权访问 | ID 不存在或不属于当前账号 | 返回列表并刷新数据 | | 凭证图片地址无效 | URL 不是完整 HTTP(S) 地址 | 检查上传结果并补全资源域名 | ## 11. 接入验收清单 - 首页只展示 `/home` 返回的当前时段可用服务,不在 App 中维护启停状态或默认价格。 - 报价和创建订单都提交完整取件、收件坐标,且不使用客户端自行计算的金额。 - 金额按整数 TWD 展示,不显示小数、时长费、最低价或尖峰费用。 - 创建订单具备稳定的 `clientRequestId` 重试策略。 - 预约时间满足未来三天内、固定 30 分钟的约束。 - App 使用响应体 `code` 判断成功或失败。 - 分页列表从 `data.records` 和 `data.total` 取值。 - 图片先上传,再向闪送接口提交完整 HTTP(S) URL。 - 用户只在待接单或已接单状态显示取消入口,只在已送达状态显示确认收货入口。 - 骑手抢单前可展示完整文字地址但不使用坐标导航;抢单成功后再读取联系人、电话、精确坐标和履约图片。 - 骑手严格按 `ACCEPTED -> PICKED_UP -> DELIVERED` 顺序操作。 - 启用 PIN 时,骑手送达必须提交用户端详情显示的四位 PIN。 - 对抢单冲突、状态变化和重复点击,均以刷新后的服务端订单状态为准。 - 用户订单列表第一版只展示“全部”;如实现状态页签,仅基于当前页数据本地过滤。 - 骑手端金额文案统一为“订单金额”,不使用“收益”;联系骑手/用户通过 IM,不提供通话。