|
|
# MotherShip 查询接口调用文档
|
|
|
|
|
|
> **第三方正式对接请优先阅读**:同目录 [`api对接文档.md`](./api对接文档.md)(v1.1,**四路线**:MotherShip/Flock × 免账号/登录后)。
|
|
|
> 本文档保留 MotherShip 宿主两步 + 管理端接口细项,适合内部运维对照;Flock / 登录后字段以对接文档为准。
|
|
|
|
|
|
本文档说明 **MotherShip / Axel** 查询报价相关接口(偏免账号宿主两步),适合内部运维对照使用。
|
|
|
|
|
|
Base URL:`https://if.dev.51track.vip`
|
|
|
|
|
|
统一响应格式:
|
|
|
|
|
|
```json
|
|
|
{ "code": 0, "message": "ok", "data": { } }
|
|
|
```
|
|
|
|
|
|
`code !== 0` 为业务/鉴权错误。询价业务失败时 HTTP 可能仍为 `200`,此时看 `data.status === "failed"`。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. 推荐接口(第三方宿主)
|
|
|
|
|
|
当前推荐使用 **两步宿主接口**(须鉴权):
|
|
|
|
|
|
1. `POST /api/host/quote/candidates`
|
|
|
2. `POST /api/host/quote/submit`
|
|
|
|
|
|
特点:
|
|
|
|
|
|
- 第一步返回提货 / 派送地址候选
|
|
|
- 第二步服务端内部等待询价并同步返回最终报价
|
|
|
- 调用方**不需要**自己轮询 `GET /api/quotes/{id}`
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2. 鉴权(必传)
|
|
|
|
|
|
当前服务器已关闭公开免 Token 模式(`HOST_PUBLIC_API_ENABLED=false`),**每次请求**须携带:
|
|
|
|
|
|
```http
|
|
|
Authorization: Bearer <API Key 或 Service Token>
|
|
|
X-Customer-Id: <Customer ID>
|
|
|
Content-Type: application/json
|
|
|
```
|
|
|
|
|
|
| 字段 | 说明 |
|
|
|
|------|------|
|
|
|
| `Authorization` | `Bearer` + 我方发放的凭证 |
|
|
|
| `X-Customer-Id` | 客户 ID,必须与凭证绑定一致 |
|
|
|
| `Content-Type` | 固定 `application/json` |
|
|
|
|
|
|
### 2.1 凭证来源(二选一)
|
|
|
|
|
|
| 类型 | 形态 | 来源 |
|
|
|
|------|------|------|
|
|
|
| **客户 API Key(推荐给第三方)** | 通常以 `chj_` 开头 | 管理端「客户管理」新建客户 / 轮换 Key 后发放 |
|
|
|
| Service Token | 自定义字符串,如 `demo-host-token` | 服务器 `.env` 的 `HOST_SERVICE_TOKENS`(联调/内网) |
|
|
|
|
|
|
第三方正式对接请使用管理端发放的 **API Key**,不要把 Key 写进前端页面或公开仓库。
|
|
|
|
|
|
### 2.2 请求头示例
|
|
|
|
|
|
```http
|
|
|
Authorization: Bearer chj_xxxxxxxxxxxxxxxxxxxxx
|
|
|
X-Customer-Id: CUST_003
|
|
|
Content-Type: application/json
|
|
|
```
|
|
|
|
|
|
联调可用 env Token(若服务器已配置):
|
|
|
|
|
|
```http
|
|
|
Authorization: Bearer demo-host-token
|
|
|
X-Customer-Id: CUST_001
|
|
|
Content-Type: application/json
|
|
|
```
|
|
|
|
|
|
### 2.3 鉴权错误
|
|
|
|
|
|
| HTTP | message | 说明 |
|
|
|
|------|---------|------|
|
|
|
| 401 | 缺少 Authorization 头 | 未带 Bearer |
|
|
|
| 401 | Service Token 无效 | Key/Token 错误或已轮换失效 |
|
|
|
| 403 | customer_id 与令牌租户不匹配 | `X-Customer-Id` 与 Key 绑定不一致 |
|
|
|
| 401 | 令牌无效或已过期 | 误用了管理员 JWT,应使用 API Key / Service Token |
|
|
|
|
|
|
### 2.4 Apifox 环境变量
|
|
|
|
|
|
| 变量 | 示例值 |
|
|
|
|------|--------|
|
|
|
| `baseUrl` | `https://if.dev.51track.vip` |
|
|
|
| `token` | 管理端发放的 `chj_...` 或联调 `demo-host-token` |
|
|
|
| `customerId` | 与 Token 绑定的客户 ID,如 `CUST_001` |
|
|
|
|
|
|
Headers:
|
|
|
|
|
|
- `Authorization`:`Bearer {{token}}`
|
|
|
- `X-Customer-Id`:`{{customerId}}`
|
|
|
- `Content-Type`:`application/json`
|
|
|
|
|
|
---
|
|
|
|
|
|
## 3. 第一步:获取联想地址候选
|
|
|
|
|
|
### 接口
|
|
|
|
|
|
```http
|
|
|
POST /api/host/quote/candidates
|
|
|
```
|
|
|
|
|
|
### 请求头
|
|
|
|
|
|
```http
|
|
|
Authorization: Bearer <API Key>
|
|
|
X-Customer-Id: <Customer ID>
|
|
|
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` | 否 | 建议传,格式 `12345` 或 `12345-6789` |
|
|
|
| `cargo.weight.value` | 是 | 单托重量 |
|
|
|
| `cargo.weight.unit` | 是 | `lb`、`kg` 或 `t`/`tonne`(公制吨;禁止 `ton`/`tons`) |
|
|
|
| `cargo.dimensions.*` | 是 | 单托长宽高 |
|
|
|
| `cargo.dimensions.unit` | 是 | `in`、`cm` 或 `m` |
|
|
|
| `cargo.pallet_count` | 是 | 托盘数,1–25 |
|
|
|
| `cargo.cargo_type` | 是 | 见下方枚举 |
|
|
|
|
|
|
**货物类型 `cargo_type`:**
|
|
|
|
|
|
`general_freight` · `machinery` · `furniture` · `electronics` · `building_materials` · `auto_parts` · `food_nonperishable` · `apparel` · `other`
|
|
|
|
|
|
服务端会自动把 `lb`/`kg`/`t`、`in`/`cm`/`m` 换算为 MotherShip 需要的英制单位(小数进一)。
|
|
|
|
|
|
### 成功响应示例
|
|
|
|
|
|
```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. 第二步提交时传回选中的**完整候选对象**(不要只传 `option_id`)
|
|
|
|
|
|
不要使用 `selectable: false` 的候选(可能带 `unavailable_reason`)。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 4. 第二步:提交候选并获取最终报价
|
|
|
|
|
|
### 接口
|
|
|
|
|
|
```http
|
|
|
POST /api/host/quote/submit
|
|
|
```
|
|
|
|
|
|
### 请求头
|
|
|
|
|
|
与第一步相同。
|
|
|
|
|
|
### 请求体
|
|
|
|
|
|
```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` 必须来自第一步
|
|
|
- 候选对象必须是第一步列表中的完整对象(`option_id` 须匹配)
|
|
|
- HTTP 连接可能保持 **10–420 秒**;客户端 / Nginx 超时建议 ≥ **420 秒**
|
|
|
- **不会**返回 `processing` 让你继续轮询;服务端内部完成等待
|
|
|
|
|
|
### 成功响应示例
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"code": 0,
|
|
|
"message": "ok",
|
|
|
"data": {
|
|
|
"quote_id": "QTE_20260630_0003",
|
|
|
"request_id": "xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx",
|
|
|
"status": "done",
|
|
|
"currency": "USD",
|
|
|
"valid_until": "2026-06-30T10:30:00.000Z",
|
|
|
"source_type": "rpa",
|
|
|
"quotes": [
|
|
|
{
|
|
|
"service_level": "standard",
|
|
|
"rate_option": "lowest",
|
|
|
"carrier": "Frontline Freight",
|
|
|
"transit_days": "Est. 5 business days",
|
|
|
"final_total": 337.06
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
| 字段 | 说明 |
|
|
|
|------|------|
|
|
|
| `status` | `done` 成功;`failed` 失败(看 `error_message`) |
|
|
|
| `quotes[].final_total` | **对客户展示价(USD,含加价)** |
|
|
|
| `quotes[].carrier` | 承运商 |
|
|
|
| `quotes[].transit_days` | 时效 |
|
|
|
| `valid_until` | 报价有效期 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 5. 调用步骤总结
|
|
|
|
|
|
1. 带鉴权头调 `POST /api/host/quote/candidates`
|
|
|
2. 保存 `host_session_id`,让用户各选一条可用地址
|
|
|
3. 带鉴权头调 `POST /api/host/quote/submit`
|
|
|
4. 直接展示最终报价
|
|
|
|
|
|
### 第三方需要做
|
|
|
|
|
|
- API Key 放服务端,不暴露前端
|
|
|
- 每次请求带 `Authorization` + `X-Customer-Id`
|
|
|
- 第一步做地址选择 UI;第二步 Loading(超时 ≥ 420s)
|
|
|
|
|
|
### 第三方不需要做
|
|
|
|
|
|
- 不需要 `GET /api/quotes/{id}` 轮询
|
|
|
- 不需要自己做公制 → 英制换算(支持 `kg`/`t`/`cm`/`m`,服务端统一进一取整为 lb/in)
|
|
|
|
|
|
---
|
|
|
|
|
|
## 6. Apifox / cURL
|
|
|
|
|
|
### Apifox
|
|
|
|
|
|
| 步骤 | 方法 | URL |
|
|
|
|------|------|-----|
|
|
|
| 1 | POST | `{{baseUrl}}/api/host/quote/candidates` |
|
|
|
| 2 | POST | `{{baseUrl}}/api/host/quote/submit` |
|
|
|
|
|
|
第二步超时建议:`420000` ms。
|
|
|
|
|
|
### cURL(第一步)
|
|
|
|
|
|
```bash
|
|
|
curl -X POST "https://if.dev.51track.vip/api/host/quote/candidates" \
|
|
|
-H "Authorization: Bearer <API Key>" \
|
|
|
-H "X-Customer-Id: CUST_001" \
|
|
|
-H "Content-Type: application/json" \
|
|
|
--data-binary @host-candidates-req.json
|
|
|
```
|
|
|
|
|
|
PowerShell(本机测公网,不要写 `127.0.0.1`,不要加 `:30325`):
|
|
|
|
|
|
```powershell
|
|
|
curl.exe -s -w "`nHTTP: %{http_code}`n" `
|
|
|
-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"
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 7. 常见错误
|
|
|
|
|
|
| code | HTTP | 说明 | 处理 |
|
|
|
|------|------|------|------|
|
|
|
| `UNAUTHORIZED` | 401 | 缺少或无效 Token/API Key | 检查 Bearer 与客户绑定 |
|
|
|
| `FORBIDDEN` | 403 | 客户 ID 与令牌不匹配 | 核对 `X-Customer-Id` |
|
|
|
| `VALIDATION_FAILED` | 400 | 参数不合法 / JSON 无效 | 检查字段、单位、州码 |
|
|
|
| `SESSION_EXPIRED` | 404 | `host_session_id` 过期 | 重新调第一步 |
|
|
|
| `ADDRESS_SESSION_MISMATCH` | 400 | 候选不在本次联想结果中 | 使用第一步返回的完整对象 |
|
|
|
| `ADDRESS_NOT_SELECTABLE` | 400 | 选了不可用候选 | 改用 `selectable: true` |
|
|
|
| `QUOTE_TIMEOUT` | 504 | 服务端等待超时 | 稍后重试 |
|
|
|
| `RATE_LIMITED` | 429 | 限流 | 降低频率 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 8. 客户管理接口(管理员)
|
|
|
|
|
|
供**管理端 / 内部运维**使用,不提供给第三方宿主直接调用。
|
|
|
|
|
|
### 8.1 鉴权
|
|
|
|
|
|
```http
|
|
|
Authorization: Bearer <Admin JWT>
|
|
|
Content-Type: application/json
|
|
|
```
|
|
|
|
|
|
管理员 JWT 通过 `POST /api/auth/login` 获取(账号密码登录)。
|
|
|
|
|
|
### 8.2 获取客户列表
|
|
|
|
|
|
```http
|
|
|
GET /api/admin/customers?page=1&size=10&keyword=CUST_001
|
|
|
```
|
|
|
|
|
|
| 参数 | 必填 | 说明 |
|
|
|
|------|------|------|
|
|
|
| `page` | 是 | 页码,从 1 开始 |
|
|
|
| `size` | 是 | 每页 1–100 |
|
|
|
| `keyword` | 否 | 按客户 ID / 名称模糊搜索 |
|
|
|
|
|
|
成功时 `data.list[]` 主要字段:
|
|
|
|
|
|
| 字段 | 说明 |
|
|
|
|------|------|
|
|
|
| `customer_id` | 客户 ID |
|
|
|
| `status` | `active` / `disabled` |
|
|
|
| `embed_password_set` | 是否已设 `/embed-demo` 密码 |
|
|
|
| `active_api_key` | 当前有效 API Key;历史未保存原文时可能为 `null` |
|
|
|
| `api_keys[]` | Key 摘要(不含完整原文,仅前缀等) |
|
|
|
|
|
|
### 8.3 新建客户
|
|
|
|
|
|
```http
|
|
|
POST /api/admin/customers
|
|
|
```
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"name": "演示客户 003",
|
|
|
"remark": "重点客户",
|
|
|
"embed_password": "123456"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
| 字段 | 必填 | 说明 |
|
|
|
|------|------|------|
|
|
|
| `name` | 是 | 客户名称 |
|
|
|
| `remark` | 否 | 备注 |
|
|
|
| `embed_password` | 否 | `/embed-demo` 登录密码;不传默认 `123456` |
|
|
|
|
|
|
响应中 `api_key` **仅在新建时额外返回一次**;同时写入 `active_api_key`。请立即保存发给第三方。
|
|
|
|
|
|
### 8.4 更新客户
|
|
|
|
|
|
```http
|
|
|
PATCH /api/admin/customers/{customer_id}
|
|
|
```
|
|
|
|
|
|
可更新:`name`、`remark`、`status`(`active`/`disabled`)、`embed_password`(至少 6 位)。
|
|
|
|
|
|
### 8.5 轮换 API Key
|
|
|
|
|
|
```http
|
|
|
POST /api/admin/customers/{customer_id}/rotate-key
|
|
|
```
|
|
|
|
|
|
- 原有效 Key 立即失效
|
|
|
- 响应中的 `api_key` 与 `active_api_key` 为新 Key
|
|
|
- 轮换后须通知第三方更换凭证
|
|
|
|
|
|
### 8.6 承运商官网账密(管理端)
|
|
|
|
|
|
```http
|
|
|
GET /api/admin/customers/{customer_id}/provider-credentials
|
|
|
PUT /api/admin/customers/{customer_id}/provider-credentials
|
|
|
```
|
|
|
|
|
|
用于绑定客户在 MotherShip / Flock 官网的登录账密(RPA 使用),**非第三方宿主对接接口**。
|
|
|
|
|
|
PUT 请求体示例:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"provider": "mothership",
|
|
|
"email": "user@example.com",
|
|
|
"password": "********"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
| 约束 | 说明 |
|
|
|
|------|------|
|
|
|
| `provider` | 仅 `mothership` 或 `flock` |
|
|
|
| 写入策略 | 仅允许首次绑定;已绑定后不可修改 / 清除 |
|
|
|
| 清除 | 不支持 `clear`;更换需新建客户 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 9. 嵌入演示登录
|
|
|
|
|
|
供 `/embed-demo` 演示页使用,不是正式宿主 API。
|
|
|
|
|
|
### 9.1 登录
|
|
|
|
|
|
```http
|
|
|
POST /api/embed-demo/login
|
|
|
```
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"customer_id": "CUST_003",
|
|
|
"password": "cust003@2026"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
- 密码以管理端「客户管理」中为该客户设置的为准
|
|
|
- 成功后写入 HttpOnly Cookie:`embed_demo_session`
|
|
|
|
|
|
### 9.2 当前会话 / 退出
|
|
|
|
|
|
```http
|
|
|
GET /api/embed-demo/me
|
|
|
POST /api/embed-demo/logout
|
|
|
```
|
|
|
|
|
|
`me` 依赖登录 Cookie,返回当前客户及加价配置。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 10. 其它渠道说明(非推荐宿主对接)
|
|
|
|
|
|
以下接口同样需要 Service Token / API Key,但**返回 `processing`,需客户端轮询**,不作为第三方默认同步对接方案:
|
|
|
|
|
|
| 渠道 | 提交 | 轮询 |
|
|
|
|------|------|------|
|
|
|
| MotherShip 旧三步 | `POST /api/quotes` 等 | `GET /api/quotes/{quote_id}` |
|
|
|
| Priority1 | `POST /api/priority1/quotes` | 同上 |
|
|
|
| Flock | `POST /api/flock/quotes` | 同上 |
|
|
|
|
|
|
第三方正式对接请优先使用本文档第 1–4 节的两步宿主接口。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 11. 联系与支持
|
|
|
|
|
|
- Token / API Key 失效或需开通新客户:联系我方运维 / 管理端操作
|
|
|
- `X-Customer-Id` 必须与发放凭证绑定一致,不可串用
|
|
|
- 老客户若 `active_api_key` 为空:在管理端「轮换 Key」一次后再查看
|