# 外部系统 API 对接说明

> 完整对接指南（含美美与共示例、前端职责、自测方法）见 **[第三方对接指南](./第三方对接指南.md)**。

Base URL：`https://<你的域名>`（示例：`https://if.dev.51track.vip`）

## 响应格式

```json
{ "code": 0, "message": "ok", "data": { } }
```

`code !== 0` 时为业务错误；询价业务失败时 HTTP 可能仍为 200，`data.status === "failed"`。

---

## 方式 A：宿主两步接口（推荐）

**接口路径不变**，鉴权由服务器 `.env` 控制：

| 模式 | `HOST_PUBLIC_API_ENABLED` | 请求头 |
|------|---------------------------|--------|
| 公开（联调/内网） | `true` | 只需 `Content-Type: application/json` |
| **鉴权（对接第三方）** | `false` | 见下方 Token 头 |

| 步骤 | 方法 | 路径 | 说明 |
|------|------|------|------|
| 1 | POST | `/api/host/quote/candidates` | 地址 + 货物 → 联想候选 + `host_session_id` |
| 2 | POST | `/api/host/quote/submit` | 确认候选 → **同步返回最终报价**（服务端内部轮询） |

**公开模式** 无需 `Authorization` / `X-Customer-Id`。只需：

```http
Content-Type: application/json
```

**鉴权模式** 须携带（Token 由我方在 `HOST_SERVICE_TOKENS` 中配置后发放）：

```http
Authorization: Bearer <Service Token>
X-Customer-Id: <Customer ID>
Content-Type: application/json
```

服务端 `.env` 示例（为美美与共单独发 Token）：

```env
HOST_PUBLIC_API_ENABLED=false
HOST_SERVICE_TOKENS={"meimei-2026":{"customerId":"CUST_001","permissions":["pricing:markup:write"]}}
CUSTOMER_REGISTRY=CUST_001
```

### 1. 联想地址

**请求体：** 见 [`host-candidates-req.example.json`](./host-candidates-req.example.json)

**成功响应 `data` 字段：**

| 字段 | 说明 |
|------|------|
| `host_session_id` | UUID，第二步必传；30 分钟有效 |
| `pickup_candidates` | 提货候选列表 |
| `delivery_candidates` | 派送候选列表 |
| `requires_selection` | 是否需用户从多条候选中选择 |

候选对象字段：`option_id`、`display_label`、`formatted_address`、`street`、`city`、`state`、`zip`、`selectable`、`unavailable_reason`

### 2. 确认并取报价

```json
{
  "host_session_id": "第一步返回的 UUID",
  "pickup_candidate": { "option_id": "...", "display_label": "...", "formatted_address": "...", "street": "...", "city": "...", "state": "CA", "zip": "90001" },
  "delivery_candidate": { "option_id": "...", "display_label": "...", "formatted_address": "...", "street": "...", "city": "...", "state": "TX", "zip": "75201" }
}
```

`pickup_candidate` / `delivery_candidate` 须为第一步返回列表中的完整对象（`option_id` 须匹配）。

**成功响应 `data`：** 与 `GET /api/quotes/{id}` 相同，主要字段：

| 字段 | 说明 |
|------|------|
| `status` | `done` 成功；`failed` 失败 |
| `quotes[]` | 多档报价 |
| `quotes[].final_total` | **对客户展示价**（USD，含加价） |
| `quotes[].carrier` | 承运商 |
| `quotes[].transit_days` | 时效 |
| `error_message` | `status=failed` 时的原因 |

**注意：**

- 第二步 HTTP 连接可能保持 **10–420 秒**，Nginx 须设 `proxy_read_timeout 420s;`
- 宿主**无需**再调 `GET /api/quotes/{id}` 轮询

### 货物字段（第一步 `cargo`）

| 字段 | 类型 | 说明 |
|------|------|------|
| `weight.value` | number | 单托重量 |
| `weight.unit` | `lb` \| `kg` | 重量单位 |
| `dimensions.length/width/height` | number | 单托尺寸 |
| `dimensions.unit` | `in` \| `cm` | 尺寸单位 |
| `pallet_count` | number | 托盘数 1–25 |
| `cargo_type` | string | 见下方枚举 |

**货物类型：** `general_freight` · `machinery` · `furniture` · `electronics` · `building_materials` · `auto_parts` · `food_nonperishable` · `apparel` · `other`

### 方式 A 错误码

| code | HTTP | 说明 |
|------|------|------|
| `VALIDATION_FAILED` | 400 | 参数无效；若 message 为「请求体格式无效」多为 JSON 格式错误 |
| `UNAUTHORIZED` | 401 | 鉴权模式下缺少或无效的 `Authorization` |
| `SESSION_EXPIRED` | 404 | `host_session_id` 过期或无效 |
| `ADDRESS_SESSION_MISMATCH` | 400 | 候选不在本次联想结果中 |
| `ADDRESS_NOT_SELECTABLE` | 400 | 选了不可用的候选 |
| `QUOTE_TIMEOUT` | 504 | 服务端轮询超时 |
| `RATE_LIMITED` | 429 | 限流 |

---

## 方式 B：Token 三步接口（旧流程，需客户端轮询）

**前置条件：** 在请求头携带 Token（与方式 A 鉴权模式相同）。

```http
Authorization: Bearer <Service Token>
X-Customer-Id: <Customer ID>
Content-Type: application/json
```

Token 在 `.env` 的 `HOST_SERVICE_TOKENS` 中配置。

| 步骤 | 方法 | 路径 |
|------|------|------|
| 1 | POST | `/api/addresses/mothership-candidates` |
| 2 | POST | `/api/quotes` |
| 3 | GET | `/api/quotes/{quote_id}`（每 2 秒轮询，最长约 7 分钟） |

第二步 body 须含 `customer_id`、`request_id`（UUID v4）、经 MotherShip 确认的 `pickup_address` / `delivery_address` 及货物字段。详情见 [第三方对接指南 §6](./第三方对接指南.md#6-方式btoken-三步接口)。

---

## 快速自测（方式 A）

**Windows PowerShell（推荐用 JSON 文件，避免引号转义问题）：**

```powershell
curl.exe -s -X POST "https://if.dev.51track.vip/api/host/quote/candidates" `
  -H "Content-Type: application/json" `
  --data-binary "@deploy/host-candidates-req.example.json"
```

返回 `"code":0` 且含 `host_session_id` 即成功。

**Linux / macOS：**

```bash
curl -s -X POST "https://if.dev.51track.vip/api/host/quote/candidates" \
  -H "Content-Type: application/json" \
  --data-binary "@deploy/host-candidates-req.example.json"
```

---

*文档版本：2026-06-30*
