# Open API 接口说明（入参 / 出参）

通用请求头：`x-app-key`（必填，除特别说明外）。`Content-Type: application/json`（POST/PATCH/DELETE 且带 body 时）。

错误时常见 JSON：`{ "code": "UNAUTHORIZED" | "NOT_FOUND" | "...", "message": "..." }`。

---

## 1. 商品检索

### `POST /open/products/search`

**请求体**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| keyword | string | 是 | 搜索关键词 |
| category | string | 否 | 类目筛选 |
| priceMin | number | 否 | 最低价 |
| priceMax | number | 否 | 最高价 |
| sortBy | string | 否 | `price_asc` / `price_desc` |
| page | number | 否 | 默认 1 |
| pageSize | number | 否 | 默认 20 |

**响应**（示例结构）

```json
{
  "page": 1,
  "pageSize": 20,
  "items": [
    {
      "id": "spu_...",
      "title": "...",
      "item_url": "https://...",
      "bestOffer": { "couponPrice": 99, "promotionUrl": "..." }
    }
  ]
}
```

自有供应链商品 `sourceType` 为 `self_supply`，`item_url` / `bestOffer.promotionUrl` 为 MiniShop 落地链接（含 `productId`）。

---

## 2. 商品详情

### `GET /open/products/{productId}`

**路径参数**：`productId` — SPU ID。

**响应**：商品对象，含 `skus`、`offers` 等；兼容字段含 `item_url`（推广链接）等。

---

## 3. 开放管理 — 商品

### `GET /open/manage/products`

**Query**：`q`、`categoryL1`、`page`、`pageSize`（均可选）。

**响应**：`{ items: [...], total, page, pageSize }`（以实际返回为准）。

### `GET /open/manage/products/{productId}`

**响应**：管理视角商品详情。

### `POST /open/manage/products`

**请求体**（常用）：`title`（必填）、`imageUrl`（必填），及 `subtitle`、`brand`、`category`、`categoryL1`～`L3`、`mainImageUrl`、`imageUrls`、`status`、`url`、`spuCode`、`unit`、`sourceChannels`、`tags`、`attributes` 等可选。

### `PATCH /open/manage/products/{productId}`

**请求体**：部分字段更新。

### `DELETE /open/manage/products/{productId}`

无 body。

---

## 4. 开放管理 — 类目

### `GET /open/manage/categories`

**响应**：类目树。

### `POST /open/manage/categories`

**请求体**：`name`、`parentId`、`sortOrder` 等。

### `PATCH /open/manage/categories/{categoryId}`

**请求体**：`name`、`parentId`、`sortOrder` 等可选。

### `DELETE /open/manage/categories`

**请求体**：`{ "ids": ["id1", "id2"] }`（以路由实现为准）。

---

## 5. 自有供应链（商家入驻与商品草稿 / 发布）

路径前缀：`/open/manage/self-supply/`。商家 `id` 形如 `ssm_...`，自有供应链商品 `id` 形如 `ssp_...`。新建商品初始状态为 `draft`，需商家已「一键开店」后再「一键发布」到 MiniShop。

每个店铺有 **店铺名称**（`shopName`，展示用）与 **店铺域名**（`shopSlug`，URL 段，全局唯一，仅 `a-z`、`0-9`、连字符，2～40 位）。前台用户登录 AppKey 后可通过 **`/minishops/{shopSlug}`** 直达该店（与 `GET /open/minishops/catalog?shopSlug=...` 等价）。

### `GET /open/manage/self-supply/merchants`

**Query**：`q`（可选，关键词过滤店铺名、域名、联系人等）。

**响应**：`{ items: [{ id, name, shopSlug, shopName, contactName, contactPhone, status, ... }] }`（`name` 为展示名，与 `shopName` 对齐）。

### `POST /open/manage/self-supply/merchants`

**请求体**：`shopName`（必填，店铺名称）。`shopSlug`（可选，不传则按 `shopName` 自动生成合法域名；若与已有店铺冲突则 `400`）。`contactName`、`contactPhone`（可选）。兼容字段 `name` 与 `shopName` 同义。

**响应**：`{ ok: true, id, shopSlug }`。

### `PATCH /open/manage/self-supply/merchants/{merchantId}`

**商家入驻信息修改**：可更新 `shopName`、`shopSlug`（改域名时须唯一）、`name`（与 `shopName` 同义）、`contactName`、`contactPhone`。未传字段保持原值。

**请求体**（示例）：

```json
{ "shopName": "新店名", "shopSlug": "new-cafe", "contactName": "张三", "contactPhone": "13800000000" }
```

**响应**：`{ ok: true }`。`404`：商家不存在。

### `DELETE /open/manage/self-supply/merchants/{merchantId}`

**删除店铺**（危险操作）：将删除该商家下 **全部商品**（含已发布）及购物车中引用这些商品的行。

**请求体（必填）**：`{ "confirm": "DELETE_SHOP" }`（必须为上述 JSON，否则 `400`）。

**响应**：`{ ok: true }`。`404`：商家不存在。

### `POST /open/manage/self-supply/merchants/{merchantId}/one-click-open`

**一键开店**：将商家状态置为已开店（需先完成入驻信息）。

**响应**：`{ ok: true, merchantId, status: "active" }`。

### `GET /open/manage/self-supply/products`

**Query**：`q`、`merchantId`（均可选）。

**响应**：`{ items: [{ id, merchantId, title, imageUrl, price, originalPrice, category, status, publishedAt, ... }] }`。其中 **`price` 为优惠后价**；**`originalPrice`** 为可选划线原价（可 `null`），若有则须 ≥ `price`。

### `GET /open/manage/self-supply/product-categories`

**Query**：`q`（可选，按路径、名称、类目 id 模糊筛选）。

**响应**：`{ items: [{ id, path, name, level }] }`。其中 **`path`** 为与【开放管理-类目】一致的完整路径（如 `食品 / 零食`），新建或修改商品时 **`category` 字段须传该 `path`**，或传 **`id`**；不可随意填写未在系统中维护的字符串。若某末级名称在树中唯一，也可仅传该名称，否则须用完整路径或 id。

### `POST /open/manage/self-supply/products`

**请求体**：`merchantId`（必填）、`title`（必填）、`imageUrl`、`price`（必填，非负数，**表示优惠后价**）、`originalPrice`（可选，非负数，**划线原价**，须 ≥ `price`；不传或 `null` 表示无原价）、`category`（可选，**须合法**：`path`、`id` 或唯一末级名，见上节）。

**响应**：`{ ok: true, id }`（新商品 `id`，状态 `draft`）。

### `PATCH /open/manage/self-supply/products/{productId}`

**商品草稿修改**（发布前改标题、价格、主图、类目或归属商家等）：字段均可选；未传字段保持原值。传 **`category: null`** 可清空类目；传 **`originalPrice: null`** 可清除原价。

**请求体**（示例）：

```json
{ "title": "新标题", "price": 19.9, "originalPrice": 29.9, "imageUrl": "https://...", "category": "食品 / 零食", "merchantId": "ssm_xxx" }
```

**响应**：`{ ok: true }`。`404`：商品不存在。

### `DELETE /open/manage/self-supply/products/{productId}`

**删除商品**：删除草稿或已发布记录，并移除购物车中该 `productId`。

**请求体（必填）**：`{ "confirm": "DELETE_PRODUCT" }`。

**响应**：`{ ok: true }`。`404`：商品不存在。

### `POST /open/manage/self-supply/products/{productId}/one-click-publish`

**一键发布**：将草稿发布到商城；要求商家已 `active`（已一键开店）。

**响应**：成功时返回发布结果（结构以实际为准）。`400`：商家未开店等。

---

## 6. MiniShop（自有供应链商城 Open API）

### `GET /open/access-clients/me`

**响应**（示例）：

```json
{
  "appKey": "...",
  "remark": "备注/昵称",
  "createdAt": "...",
  "apiCallCount": 0
}
```

### `GET /open/minishops/catalog`

**Query**（均可选）：`keyword`、`category`、`shopId`（商家内部 ID）、`shopSlug`（店铺域名，与 `shopId` 二选一即可；若域名不存在则返回空列表）。

**响应**：

```json
{
  "shops": [{ "id": "...", "name": "..." }],
  "categories": ["..."],
  "products": [
    {
      "id": "...",
      "merchantId": "...",
      "title": "...",
      "imageUrl": "...",
      "price": 9.9,
      "originalPrice": null,
      "category": null
    }
  ]
}
```

### `GET /open/minishops/products/{productId}`

**响应**：单条 `products` 同结构字段（含 `price` 优惠后价、可选 `originalPrice`），另含 `merchantName`（若 SQL 返回）。

### `GET /open/minishops/cart`

**响应**：

```json
{ "items": [{ "productId": "...", "qty": 1, "product": { ... } }] }
```

### `DELETE /open/minishops/cart`

清空购物车。**响应**：与 GET 结构一致或 `{ ok: true }`（以实际为准）。

### `POST /open/minishops/cart/items`

**请求体**：

```json
{ "productId": "ssp_...", "qty": 2 }
```

### `PATCH /open/minishops/cart/items/{productId}`

**请求体**：`{ "qty": 3 }`

### `DELETE /open/minishops/cart/items/{productId}`

删除一行。

### `POST /open/minishops/pay/prepare`

**请求体**：

```json
{
  "type": "wxpay",
  "items": [{ "productId": "ssp_...", "qty": 1 }]
}
```

**响应**（成功）：`formAction`、`fields`（表单字段）、`outTradeNo`；前端需 POST 跳转支付网关。

### `GET /open/minishops/pay/status`

**Query**：`out_trade_no`（必填）。

**响应**：

```json
{
  "outTradeNo": "...",
  "status": "pending" | "paid",
  "money": "9.90",
  "name": "...",
  "paidAt": null
}
```

`404`：`{ "code": "NOT_FOUND", "outTradeNo": "..." }`

---

## 7. 鉴权补充

详见仓库内 `docs/open-api-signing.md`（鉴权、申请 appKey、错误码等）。
