# 闪送配送 API 契约

## 通用约定

- 用户/骑手端要求请求头 `token`，身份字段不进入请求体。
- 平台权限：`flash:pricing:list/edit`、`flash:order:list/query/cancel/complete`。
- 响应沿用 `AjaxResult`：`{"code":200,"msg":"...","data":...}`；分页 data 为 `records,total,current,size`。
- 金额为整数 TWD；路线距离为米，运价配置距离为公里。
- `serviceType`：`HELP_SEND`、`HELP_PICKUP`。
- `deliveryType`：`NORMAL`、`URGENT`。
- `packageType`：`DOCUMENT`、`GIFT`、`CLOTHING`、`BEAUTY`、`DAILY_NECESSITIES`、`FOOD_INGREDIENTS`、`ELECTRONICS`、`SMALL_APPLIANCE`、`OTHER`。
- 物品信息提交 `packageType`、正整数 `quantity`、大于 0 且不超过 20 的两位小数 `totalWeightKg` 及最长 255 字符的可选 `specification`；`packageSize` 由服务端根据总重量生成。
- `deliveryMode`：`NOW`、`SCHEDULED`。预约起止时间使用 ISO 日期时间，时段固定 30 分钟且开始时间不晚于三天后。
- `status`：`WAITING_ACCEPTANCE`、`ACCEPTED`、`PICKED_UP`、`DELIVERED`、`COMPLETED`、`CANCELLED`。

## 地址对象

```json
{"name":"王小明","phone":"0912345678","address":"台北市信义区市府路1号","addressDetail":"3楼A室","longitude":121.5645,"latitude":25.0330,"country":"TW","province":"台北市","city":"台北市","area":"信义区","annexes":""}
```

| 方法 | 路径 | 参数/说明 |
|---|---|---|
| GET | `/system/address/getaddress` | header token；query `keyword?`；仅本人，搜索并置顶优先 |
| GET | `/system/address/getaddressxq` | header token；query `id`；仅本人 |
| POST | `/system/address/address` | header token；body 地址对象，可含 `id`；忽略 userId |
| DELETE | `/system/address/{id}` | header token；仅本人 |
| POST | `/system/address/{id}/top` | header token；仅本人 |

## 用户端

| 方法 | 路径 | 请求/说明 |
|---|---|---|
| GET | `/system/flashDelivery/home` | 返回 `serviceTypes=[HELP_SEND,HELP_PICKUP]`、`deliveryTypes=[NORMAL,URGENT]` 及当前时刻命中的统一运价摘要；不返回内部更新人、时间或地图密钥 |
| POST | `/system/flashDelivery/quote` | `serviceType,deliveryType,deliveryMode,scheduledPickupStartAt?,scheduledPickupEndAt?,packageType,quantity,totalWeightKg,specification?,tipAmount,pickup,delivery`；立即订单按当前时刻、预约订单按预约开始时刻匹配运价 |
| POST | `/system/flashDelivery/orders` | 报价请求字段 + `clientRequestId,pricingId,pricingVersion,quotedBaseDeliveryFee,quotedDistanceFee,quotedUrgentFee,quotedAmount,pinRequired?,senderImageUrls?,userNote?`；服务端复算并逐项校验后幂等创建 |
| GET | `/system/flashDelivery/orders` | `page,size,role?`；`role=sender/receiver`，省略为 `sender`；按创建时间倒序返回当前用户作为寄件人或已绑定收件人的订单摘要 |
| GET | `/system/flashDelivery/orders/{id}` | 订单创建人或创建时已绑定收件人的 App 订单详情；`data` 直接为订单字段，不返回原始状态日志 |
| POST | `/system/flashDelivery/orders/{id}/cancel` | `{"reason":"行程变化"}`；仅订单创建人且状态为待接/已接 |
| POST | `/system/flashDelivery/orders/{id}/confirmReceipt` | 无 body；订单创建人或已绑定收件人可对 DELIVERED 订单确认签收 |

用户列表摘要字段固定为：`id,orderNo,serviceType,deliveryType,status,packageType,quantity,totalWeightKg,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,estimatedDurationSeconds,baseDeliveryFee,urgentFee,tipAmount,amount,currency,deliveredAt,createTime`。列表不返回联系人、电话、精确坐标、照片、日志或内部计价审计字段。`receiver` 不是按当前手机号动态查询：订单只在创建时唯一匹配已注册普通用户并固化 `receiverUserId`，未匹配订单以后不追溯认领。

报价响应 data：`serviceType,deliveryType,deliveryMode,scheduledPickupStartAt?,scheduledPickupEndAt?,pricingId,startTime,endTime,distanceMeters,distanceSource,estimatedDurationSeconds?,startingDistance,startingFare,distance,freight,billableDistance,distanceFee,baseDeliveryFee,urgentRate,minimumUrgentFee,urgentFee,tipAmount,amount,currency,pricingVersion`。`billableDistance` 为按外卖规则处理 0.5 公里边界后的计费公里数；全部费用为整数 TWD，`urgentRate` 为百分比。`baseDeliveryFee=startingFare+distanceFee`；普通配送 `urgentFee=0`；加急配送 `urgentFee=max(minimumUrgentFee,roundHalfUp(baseDeliveryFee*urgentRate/100))`；`amount=baseDeliveryFee+urgentFee+tipAmount`。

创建时服务端重新获取路线、按配送方式匹配运价并复算费用。`pricingId`、`pricingVersion`、`quotedBaseDeliveryFee`、`quotedDistanceFee`、`quotedUrgentFee`、`quotedAmount` 任一不一致时不创建订单，返回国际化“报价已变化，请重新确认”及完整最新报价。地址快照字段为联系人、电话、市/区、交付方式、地址、详细地址和经纬度。用户创建和详情响应的 `data` 直接为订单详情字段，照片分别为 `senderImageUrls,pickupImageUrls,deliveryImageUrls`，不返回原始状态日志；`deliveryPinCode` 仅在启用时向订单参与用户返回，用户仅在 `ACCEPTED`、`PICKED_UP` 阶段获得骑手位置。

## 骑手端

所有接口要求 token 用户 `userType=2`。

| 方法 | 路径 | 请求/说明 |
|---|---|---|
| GET | `/system/flashDelivery/rider/orders` | `page,size,tab,longitude?,latitude?`；参数方式与骑手外卖列表一致，`tab=newTask/toPickup/delivering/completed/cancelled`；坐标用于新任务附近范围、取件点距离和排序 |
| GET | `/system/flashDelivery/rider/orders/{id}` | `data` 直接为稳定的骑手订单详情；未接单隐藏敏感字段，接单后仅订单骑手可见完整履约信息 |
| POST | `/system/flashDelivery/rider/orders/{id}/accept` | 原子抢单，成功后 `data` 直接返回骑手订单详情 |
| POST | `/system/flashDelivery/rider/orders/{id}/pickup` | `{"imageUrls":["https://.../pickup.jpg"]}`；至少一张 |
| POST | `/system/flashDelivery/rider/orders/{id}/deliver` | `{"imageUrls":["https://.../delivery.jpg"],"pinCode":"4821"}`；图片至少一张，启用 PIN 时必须正确 |

骑手页签状态映射：`newTask=WAITING_ACCEPTANCE`、`toPickup=ACCEPTED`、`delivering=PICKED_UP`、`completed=DELIVERED+COMPLETED`、`cancelled=CANCELLED`。除 `newTask` 外只返回当前骑手本人任务；闪送没有 `refund` 页签。

骑手列表摘要字段固定为：`id,orderNo,serviceType,deliveryType,status,packageType,quantity,totalWeightKg,specification,packageSize,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pinRequired,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,pickupDistanceMeters,distanceMeters,estimatedDurationSeconds,baseDeliveryFee,urgentFee,tipAmount,amount,currency,createTime`。`newTask` 分页 data 在 `records,total,current,size` 外增加 `nearbyTaskCount,highestOrderAmount`；不返回加急比例、最低加急费或骑手收入字段。

骑手接单前可按原型查看完整取送文字地址，但不返回用户 ID、联系人、电话、实际 PIN、精确经纬度、备注、幂等请求号、履约凭证或日志。抢单成功后才向中单骑手返回完整联系方式、坐标和有权限查看的照片数组。骑手详情在接单前后使用同一个直接 DTO，不返回 `{order,images,logs}` 包装，也不要求 App 判断 `data.order`。

## 平台端

| 方法 | 路径 | 请求/说明 |
|---|---|---|
| GET | `/system/flashDelivery/admin/pricing` | 返回帮送和帮取共享的全部统一运价时段，按开始时间排序 |
| POST | `/system/flashDelivery/admin/pricing` | `startTime,endTime,startingDistance,startingFare,distance,freight,urgentRate,minimumUrgentFee`；保存即生效 |
| PUT | `/system/flashDelivery/admin/pricing/{id}` | 与新增相同的完整请求体；版本原子加一，并发覆盖返回业务错误 |
| DELETE | `/system/flashDelivery/admin/pricing/{id}` | 删除运价时段并立即停止用于报价 |
| GET | `/system/flashDelivery/admin/orders` | 分页；可按状态、类型、订单号、用户、骑手筛选 |
| GET | `/system/flashDelivery/admin/orders/{id}` | 完整详情、凭证、日志 |
| POST | `/system/flashDelivery/admin/orders/{id}/cancel` | `reason` 必填；非终态可取消 |
| POST | `/system/flashDelivery/admin/orders/{id}/complete` | 无 body；仅 DELIVERED |

本期没有支付、退款、取消费、退回费、结算、骑手收入、代购/垫付、商品金额、违禁品电子确认、骑手放弃或自动派单字段和接口。所有闪送金额保持整数 TWD；创建订单后直接待接单，不存在待支付状态。`deliveryType=URGENT` 为 1 对 1 独占配送，相关外卖与闪送接单入口必须执行跨订单独占检查。骑手小费仅为订单费用明细，不代表已付款。

运价时段使用业务当地时间和 `HH:mm` 格式，采用开始时间包含、结束时间不包含的匹配语义；`24:00` 只允许作为结束时间，跨午夜时段必须拆成两个配置。全部时间段不得重叠。起送距离和计价距离必须为正数，起送价格和计价金额必须为正整数，加急比例和最低加急费必须为非负数；立即订单按当前时刻、预约订单按预约开始时刻匹配，目标时刻没有匹配时段时报“对应取件时段暂无可用运价”。
