瀏覽代碼

更新闪送 App 接口文档

同步时段运价、整数新台币计价、当前时段匹配及最新报价字段。

移除最低价、分钟费、尖峰倍率和运价启停等旧接口说明。
qmj 3 天之前
父節點
當前提交
53efb1901d
共有 1 個文件被更改,包括 793 次插入0 次删除
  1. 793 0
      docs/flash-delivery-app-api.md

+ 793 - 0
docs/flash-delivery-app-api.md

@@ -0,0 +1,793 @@
+# 闪送功能 App 接口接入文档
+
+本文档供用户端 App 和骑手端 App 接入闪送功能使用,以当前后端实现为准。文档只描述接口契约和业务流程,不包含 App 前端实现代码。
+
+## 1. 功能范围
+
+当前闪送支持:
+
+- 帮送、帮取、加急送三种服务。
+- 共享地址簿、路线报价、立即配送和预约配送。
+- 用户发布订单、查询订单、取消订单和确认收货。
+- 骑手查看待抢订单、抢单、确认取件和确认送达。
+- 可选的四位交付 PIN。
+- 寄件、取件和送达图片凭证。
+- 送达 24 小时后仍未由用户确认的订单自动完成。
+
+当前不包含支付、退款、骑手收入、结算、代购垫付、自动派单和骑手放弃订单。
+
+## 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
+  }
+}
+```
+
+- `pageNum` 默认 `1`,小于 `1` 时按 `1` 处理。
+- `pageSize` 默认 `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 订单摘要 `order`
+
+用户订单列表、骑手已接订单列表和完整详情中的 `order` 使用以下字段:
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| `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 | 更新时间 |
+
+### 6.3 完整详情对象
+
+用户详情、中单骑手详情及抢单成功响应的数据结构为:
+
+```json
+{
+  "order": {},
+  "images": [
+    {
+      "proofType": "PICKUP",
+      "imageUrl": "https://api.example.com/profile/upload/pickup.jpg",
+      "sortOrder": 0,
+      "createTime": "2026-09-01T10:30:00+08:00"
+    }
+  ],
+  "logs": [
+    {
+      "fromStatus": "ACCEPTED",
+      "toStatus": "PICKED_UP",
+      "operatorType": "RIDER",
+      "reason": null,
+      "createTime": "2026-09-01T10:30:00+08:00"
+    }
+  ],
+  "rider": {
+    "name": "陈骑手",
+    "avatar": "https://api.example.com/avatar.jpg",
+    "rating": 4.9,
+    "imUserId": "10086",
+    "longitude": 121.56,
+    "latitude": 25.03
+  },
+  "deliveryPinCode": "4821"
+}
+```
+
+- `proofType` 为 `SENDER`、`PICKUP` 或 `DELIVERY`。
+- `operatorType` 为 `USER`、`RIDER`、`ADMIN` 或 `SYSTEM`。
+- `deliveryPinCode` 只向订单所属用户返回,并且只在启用 PIN 时出现;骑手响应永不返回此字段。
+- `rider` 只向用户返回。订单未接单时为 `null`。
+- 骑手位置只在订单状态为 `ACCEPTED` 或 `PICKED_UP` 时向用户返回,其他状态下经纬度为 `null`。
+
+## 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?pageNum=1&pageSize=10&scene=progress&serviceType=HELP_SEND
+```
+
+查询参数:
+
+| 参数 | 必填 | 说明 |
+|---|---:|---|
+| `pageNum` | 否 | 默认 `1` |
+| `pageSize` | 否 | 默认 `10`,最大 `100` |
+| `status` | 否 | 精确状态筛选 |
+| `scene` | 否 | 页面状态分组,见下表 |
+| `serviceType` | 否 | 服务类型筛选 |
+
+`scene` 可选值:
+
+| 值 | 包含状态 |
+|---|---|
+| `all` 或不传 | 全部 |
+| `matching` | `WAITING_ACCEPTANCE` |
+| `progress` | `ACCEPTED`、`PICKED_UP`、`DELIVERED` |
+| `done` | `COMPLETED` |
+| `cancelled` | `CANCELLED` |
+
+`status` 和 `scene` 不能同时传。响应 `data.records` 中每项为订单摘要对象。
+
+### 7.5 查询用户订单详情
+
+```http
+GET /system/flashDelivery/orders/{id}
+```
+
+仅订单所属用户可访问。响应 `data` 为完整详情对象,包括订单快照、图片、状态日志、骑手摘要和可选的交付 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/available?pageNum=1&pageSize=10&serviceType=URGENT
+```
+
+查询参数:`pageNum`、`pageSize`、可选的 `serviceType`。
+
+返回规则:
+
+- 只返回已到可接单时间的 `WAITING_ACCEPTANCE` 订单。
+- 加急订单优先,其余按创建时间升序。
+- 只返回脱敏安全字段,不返回联系人、电话、门牌、详细地址、精确坐标、用户备注、PIN、图片或日志。
+
+`data.records` 每项结构:
+
+```json
+{
+  "id": 101,
+  "orderNo": "FD1234567890",
+  "serviceType": "URGENT",
+  "status": "WAITING_ACCEPTANCE",
+  "packageType": "DOCUMENT",
+  "packageSize": "SMALL",
+  "deliveryMode": "NOW",
+  "scheduledPickupStartAt": null,
+  "scheduledPickupEndAt": null,
+  "pickupCity": "台北市",
+  "pickupArea": "信义区",
+  "deliveryCity": "台北市",
+  "deliveryArea": "大安区",
+  "pickupLatitudeApprox": 25.03,
+  "pickupLongitudeApprox": 121.56,
+  "deliveryLatitudeApprox": 25.03,
+  "deliveryLongitudeApprox": 121.54,
+  "distanceMeters": 4200,
+  "estimatedDurationSeconds": 900,
+  "amount": 90,
+  "currency": "TWD",
+  "createTime": "2026-09-01T09:00:00+08:00"
+}
+```
+
+近似经纬度只保留两位小数,不得当作取件或送达导航终点。
+
+### 8.2 查询骑手自己的订单
+
+```http
+GET /system/flashDelivery/rider/orders/mine?pageNum=1&pageSize=10&scene=pickup
+```
+
+查询参数:
+
+| 参数 | 必填 | 说明 |
+|---|---:|---|
+| `pageNum` | 否 | 默认 `1` |
+| `pageSize` | 否 | 默认 `10`,最大 `100` |
+| `status` | 否 | 精确状态筛选 |
+| `scene` | 否 | 页面状态分组 |
+
+`scene` 可选值:
+
+| 值 | 包含状态 |
+|---|---|
+| `all` 或不传 | 全部 |
+| `pickup` | `ACCEPTED` |
+| `delivering` | `PICKED_UP` |
+| `completed` | `DELIVERED`、`COMPLETED` |
+| `cancelled` | `CANCELLED` |
+
+`status` 和 `scene` 不能同时传。响应 `data.records` 中每项为完整订单摘要,包括联系人、地址和精确坐标。
+
+### 8.3 查询骑手订单详情
+
+```http
+GET /system/flashDelivery/rider/orders/{id}
+```
+
+响应具有两种结构:
+
+- 订单仍待接且未被其他骑手接走:`data` 直接返回 8.1 的脱敏安全对象。
+- 当前骑手已经中单:`data` 返回 6.3 的完整详情对象。
+
+App 可通过 `data.order` 是否存在区分:存在表示完整详情,不存在表示接单前安全摘要。被其他骑手接走、预约时间未到或无权访问时返回业务错误。
+
+### 8.4 抢单
+
+```http
+POST /system/flashDelivery/rider/orders/{id}/accept
+```
+
+无请求体。抢单成功后状态变为 `ACCEPTED`,响应 `data` 为完整详情对象;订单已被其他骑手抢走时返回“订单已被其他骑手接走”。
+
+### 8.5 确认取件
+
+```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.6 确认送达
+
+```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。
+- 对抢单冲突、状态变化和重复点击,均以刷新后的服务端订单状态为准。