You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

359 lines
8.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# MotherShip 查询接口调用文档
本文档只说明 **MotherShip / Axel** 查询报价接口如何调用,适合直接发给第三方开发人员使用。
Base URL`https://if.dev.51track.vip`
---
## 1. 推荐接口
当前使用 **两步宿主接口**(须鉴权):
1. `POST /api/host/quote/candidates`
2. `POST /api/host/quote/submit`
特点:
- 第一步返回联想地址候选
- 第二步服务端内部等待并轮询
- 调用方**不需要自己轮询**
---
## 2. 鉴权(必传)
当前服务器已开启鉴权(`HOST_PUBLIC_API_ENABLED=false`**每次请求**须携带以下请求头:
```http
Authorization: Bearer <Service Token>
X-Customer-Id: CUST_001
Content-Type: application/json
```
| 字段 | 值 | 说明 |
|------|-----|------|
| `Authorization` | `Bearer <Service Token>` | 我方单独发放的 API Token |
| `X-Customer-Id` | `CUST_001` | 固定客户 ID须与 Token 绑定一致 |
**示例(联调 Token**
```http
Authorization: Bearer demo-host-token
X-Customer-Id: CUST_001
Content-Type: application/json
```
> 生产环境 Token 由我方另行发放,格式同上;请勿将 Token 写入前端页面或公开仓库。
### 鉴权错误
| HTTP | message | 说明 |
|------|---------|------|
| 401 | 缺少 Authorization 头 | 未带 `Authorization: Bearer ...` |
| 401 | Service Token 无效 | Token 拼写错误或不在白名单 |
| 401 | 令牌无效或已过期 | 误用了管理员 JWT应使用 Service Token |
### Apifox 环境变量建议
| 变量 | 示例值 |
|------|--------|
| `baseUrl` | `https://if.dev.51track.vip` |
| `token` | `demo-host-token`(联调)或我方发放的生产 Token |
| `customerId` | `CUST_001` |
在「Auth」或「Headers」中配置
- `Authorization``Bearer {{token}}`
- `X-Customer-Id``{{customerId}}`
- `Content-Type``application/json`
---
## 3. 第一步:获取联想地址候选
### 接口
```http
POST /api/host/quote/candidates
```
### 请求头示例
```http
Authorization: Bearer demo-host-token
X-Customer-Id: CUST_001
Content-Type: application/json
```
### 请求体
```json
{
"pickup_address": {
"street": "1234 Warehouse Blvd",
"city": "Los Angeles",
"state": "CA",
"zip": "90001"
},
"delivery_address": {
"street": "5678 Distribution Dr",
"city": "Dallas",
"state": "TX",
"zip": "75201"
},
"cargo": {
"weight": { "value": 500, "unit": "kg" },
"dimensions": { "length": 120, "width": 100, "height": 150, "unit": "cm" },
"pallet_count": 2,
"cargo_type": "general_freight"
}
}
```
### 字段说明
| 字段 | 说明 |
|------|------|
| `pickup_address` | 提货草稿地址 |
| `delivery_address` | 派送草稿地址 |
| `street/city/state` | 必填,`state` 为美国州二字码 |
| `zip` | 可选,建议传 |
| `cargo.weight.unit` | `kg``lb` |
| `cargo.dimensions.unit` | `cm``in` |
| `cargo.pallet_count` | 托盘数125 |
| `cargo.cargo_type` | `general_freight` 等 |
### 成功响应示例
```json
{
"code": 0,
"message": "ok",
"data": {
"host_session_id": "2dd352e6-9a1d-4563-8f13-ebff18aabdb9",
"pickup_candidates": [
{
"option_id": "Eisx...",
"display_label": "1234 Warehouse Street, Los Angeles, CA, USA",
"formatted_address": "1234 Warehouse Street, Los Angeles, CA, USA",
"street": "1234 Warehouse Street",
"city": "Los Angeles",
"state": "CA",
"zip": "90001",
"selectable": true
}
],
"delivery_candidates": [
{
"option_id": "EihE...",
"display_label": "Distribution Dr, Wilmer, Dallas, TX, USA",
"formatted_address": "Distribution Dr, Wilmer, Dallas, TX, USA",
"street": "5678 Distribution Dr, Wilmer",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"selectable": true
}
],
"requires_selection": true
}
}
```
### 第一步调用后的处理
1. 保存 `data.host_session_id`(约 30 分钟有效)
2.`pickup_candidates` 中选择 1 条 `selectable: true`
3.`delivery_candidates` 中选择 1 条 `selectable: true`
4. 将选中的**完整候选对象**用于第二步请求
不要使用 `selectable: false` 的候选。
---
## 4. 第二步:提交候选并直接获取最终报价
### 接口
```http
POST /api/host/quote/submit
```
### 请求头
与第一步相同(须带 `Authorization``X-Customer-Id`)。
### 请求体
```json
{
"host_session_id": "2dd352e6-9a1d-4563-8f13-ebff18aabdb9",
"pickup_candidate": {
"option_id": "Eisx...",
"display_label": "1234 Warehouse Street, Los Angeles, CA, USA",
"formatted_address": "1234 Warehouse Street, Los Angeles, CA, USA",
"street": "1234 Warehouse Street",
"city": "Los Angeles",
"state": "CA",
"zip": "90001",
"selectable": true
},
"delivery_candidate": {
"option_id": "EihE...",
"display_label": "Distribution Dr, Wilmer, Dallas, TX, USA",
"formatted_address": "Distribution Dr, Wilmer, Dallas, TX, USA",
"street": "5678 Distribution Dr, Wilmer",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"selectable": true
}
}
```
### 注意
- `host_session_id` 必须来自第一步返回值
- `pickup_candidate` / `delivery_candidate` 必须是第一步候选列表中的**完整对象**
- 第二步会等待 1030 秒,最长约 420 秒
- 第二步**不会**返回 `processing` 让你继续轮询;服务端会自己处理轮询
### 成功响应示例
```json
{
"code": 0,
"message": "ok",
"data": {
"quote_id": "QTE_20260630_0003",
"status": "done",
"currency": "USD",
"valid_until": "2026-06-30T10:30:00.000Z",
"quotes": [
{
"service_level": "standard",
"rate_option": "lowest",
"carrier": "Frontline Freight",
"transit_days": "Est. 5 business days",
"final_total": 337.06
}
]
}
}
```
### 应展示给用户的字段
| 字段 | 说明 |
|------|------|
| `quotes[].final_total` | 最终展示价USD |
| `quotes[].carrier` | 承运商 |
| `quotes[].transit_days` | 时效 |
| `valid_until` | 报价有效期 |
---
## 5. 调用步骤总结
### 业务步骤
1. 带鉴权头调 `POST /api/host/quote/candidates`
2. 取得 `host_session_id`
3. 让用户在候选地址中各选一条可用地址
4. 带鉴权头调 `POST /api/host/quote/submit`
5. 直接拿最终报价并展示
### 第三方系统需要做的事
- 在服务端保存 Token**不要**暴露在前端
- 每次请求携带 `Authorization` + `X-Customer-Id: CUST_001`
- 第一步展示候选地址选择 UI
- 第二步显示 Loading建议超时 ≥ 420 秒)
- 第二步返回后展示报价
### 第三方系统不需要做的事
- 不需要 `GET /api/quotes/{id}` 轮询
- 不需要自己做 kg/cm 到 lb/in 的换算
---
## 6. 单位说明
接口支持:
- 重量:`kg` / `lb`
- 尺寸:`cm` / `in`
例如:
```json
"weight": { "value": 500, "unit": "kg" },
"dimensions": { "length": 120, "width": 100, "height": 150, "unit": "cm" }
```
服务端会自动换算为 MotherShip 需要的英制单位。
---
## 7. Apifox 测试方法
### 环境变量
| 变量 | 值 |
|------|-----|
| `baseUrl` |https://if.dev.51track.vip |
| `token` | `demo-host-token` |
| `customerId` | `CUST_001` |
### 第一步
- 方法:`POST`
- URL`{{baseUrl}}/api/host/quote/candidates`
- Headers
- `Authorization``Bearer {{token}}`
- `X-Customer-Id``{{customerId}}`
- `Content-Type``application/json`
- BodyJSON可参考 `deploy/host-candidates-req.example.json`
### 第二步
- 方法:`POST`
- URL`{{baseUrl}}/api/host/quote/submit`
- Headers与第一步相同
- Body填入第一步返回的 `host_session_id` 和选中的完整候选对象
Apifox 请求超时建议:`420000` ms420 秒)
### cURL 示例(第一步)
```bash
curl -X POST "https://if.dev.51track.vip/api/host/quote/candidates" \
-H "Authorization: Bearer demo-host-token" \
-H "X-Customer-Id: CUST_001" \
-H "Content-Type: application/json" \
--data-binary @deploy/host-candidates-req.example.json
```
---
## 8. 常见错误
| code | HTTP | 说明 | 处理方式 |
|------|------|------|----------|
| `UNAUTHORIZED` | 401 | 缺少或无效 Token | 检查 `Authorization`、`X-Customer-Id` |
| `VALIDATION_FAILED` | 400 | 参数不合法 | 检查 JSON、字段名、单位值 |
| `SESSION_EXPIRED` | 404 | 会话过期 | 重新调第一步 |
| `ADDRESS_SESSION_MISMATCH` | 400 | 第二步候选与第一步不一致 | 使用第一步返回的完整候选对象 |
| `ADDRESS_NOT_SELECTABLE` | 400 | 选了不可用候选 | 改用 `selectable: true` 的候选 |
| `QUOTE_TIMEOUT` | 504 | 服务端等待超时 | 稍后重试 |
| `RATE_LIMITED` | 429 | 请求过于频繁 | 降低调用频率 |
---
## 9. 联系与支持
- Token 失效或需单独开通生产 Token请联系我方运维
- `X-Customer-Id` 当前固定为 `CUST_001`,须与发放的 Token 一致