# 小程序新增「全部已完成定制产品列表」接口

**日期**: 2026-04-19
**PR**: [#870](https://git.1814.love:8443/wx/HL/pulls/870) (BFF) + [#883](https://git.1814.love:8443/wx/HL/pulls/883) (后端 v2) + [#886](https://git.1814.love:8443/wx/HL/pulls/886) (回归修复)
**影响端**: 小程序 (hl-ui-mp)
**状态**: 已部署测试环境，curl 200 + 真实数据验证通过

## 用途

面向**所有小程序用户的浏览入口**，展示定制师设计完成的全部定制产品（不限当前用户），用作**案例参考 / 灵感来源**。区别于已有 `/mp/custom/products`（仅展示**当前登录用户**的已完成定制产品）。

## 接口

| 项 | 值 |
|---|---|
| **URL** | `GET /mp/custom/products/all` |
| **鉴权** | **不需要登录**（与 `/mp/custom/product/{id}` 详情一致） |
| **参数** | `page`(默认 1) / `pageSize`(默认 20, 上限 100) |
| **返回** | `Result<PageResult<MpCustomProductListVO>>` |

## 请求示例

```
GET /mp/custom/products/all?page=1&pageSize=20
```

## 响应示例（实测）

```json
{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "total": 2,
    "page": 1,
    "pageSize": 20,
    "records": [
      {
        "productId": "2045390643479412737",
        "productType": "CUSTOM",
        "name": "测试定制wx-01",
        "subtitle": "",
        "tripDays": 2,
        "tripNights": 1,
        "departureCity": null,
        "destinationCity": null,
        "tags": ["露营"],
        "coverImageUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/test/material/2026/03/07/xxx.jpg",
        "startPrice": null,
        "paymentMode": null,
        "routeMapUrl": null,
        "creatorAvatarUrl": null
      },
      {
        "productId": "2043696016590327809",
        "productType": "CUSTOM",
        "name": "7天6晚草原VIP私定",
        "subtitle": "专属行程·私人管家·全程越野车·星空别墅",
        "tripDays": 7,
        "tripNights": 6,
        "tags": ["定制","深度体验","草原"],
        "coverImageUrl": "https://...",
        "startPrice": null,
        "paymentMode": null,
        "routeMapUrl": null,
        "creatorAvatarUrl": null
      }
    ]
  }
}
```

## 字段说明

| 字段 | 类型 | 说明 |
|---|---|---|
| `productId` | String | 产品 ID（雪花 ID 字符串化防 JS 精度丢失，**必须当字符串处理**） |
| `productType` | String | 字典 `product_type`：固定 `CUSTOM`=定制产品 |
| `name` | String | 产品名称 |
| `subtitle` | String | 副标题（可空） |
| `tripDays` | Integer | 行程天数 |
| `tripNights` | Integer | 行程晚数 |
| `departureCity` | String? | 出发城市（v2 当前可能 null） |
| `destinationCity` | String? | 目的地城市（v2 当前可能 null） |
| `tags` | String[] | 标签数组（如 ["定制","草原"]） |
| `coverImageUrl` | String | 封面图 URL |
| `startPrice` | String? | 起步价（BigDecimal 字符串化，**当前 null** — 后续接入起步价计算） |
| `paymentMode` | String? | 字典 `payment_mode`：FULL=全款 / DEPOSIT=订金+尾款（**当前 null** — 后续接入） |
| `routeMapUrl` | String? | 路书地图 URL（可空） |
| `creatorAvatarUrl` | String? | 定制师头像 URL（**当前 null** — 后续接入） |

## 过滤逻辑（后端固定）

- `productType = 'CUSTOM'`
- `status IN ('COMPLETED', 'ORDERED')`（已完成设计 / 已下单）
- 软删除过滤
- 按 `update_time DESC` 排序（最近完成优先）

## 边界

- `pageSize` 上限 100（超出自动截到 100）
- `page < 1` 自动设为 1
- 无数据返回 `records: []` + `total: 0`，不抛异常

## 关联接口

- `GET /mp/custom/product/{productId}` 详情（已存在，免登录）— 拿到 productId 后查详情
- `GET /mp/custom/products` 我的已完成定制（已存在，需登录）— 区别于本接口

## 部分字段当前为 null 的说明

`startPrice` / `paymentMode` / `creatorAvatarUrl` 等字段当前可能返 null。原因：v2 后端这一轮迁移按"严格不编造"原则，跨表聚合字段（起步价 / 支付模式 / 头像）尚未实现，按设计标记 NULL。**后续 PR 会补齐**，前端可先按 null 兼容处理（不展示 / 占位）。

## 不影响

- 已有 `/mp/custom/products`（我的已完成定制）行为不变
- 已有 `/mp/custom/product/{id}` 详情行为不变
- 列表接口完全独立，无副作用

## 测试环境验证

```bash
curl https://api.test.1814.love:9443/mp/custom/products/all?page=1&pageSize=3
# HTTP 200, 返回 2 条真实 CUSTOM 产品
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)
