docs: 小程序已完成定制产品查询接口前端对接指南

新增2个C端接口:
- GET /mp/custom/product/{productId} 查询单个定制产品详情
- GET /mp/custom/products 我的已完成定制产品列表

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot 2026-03-19 11:13:02 +08:00
父节点 4f48d34da6
当前提交 7c3531e98a

查看文件

@ -0,0 +1,397 @@
# 小程序已完成定制产品查询 - 前端对接指南
> **日期**: 2026-03-19
> **后端状态**: ✅ 已完成,测试服务器已部署
> **涉及模块**: hl-mp-serviceBFF层、hl-product-service产品详情、hl-order-service定制需求关联
> **页面路由**: 定制需求详情页 → 查看定制产品
---
## 功能说明
用户提交定制需求后,定制师会设计一个定制产品CUSTOM类型。产品设计完成后状态变为 `COMPLETED`,用户下单后变为 `ORDERED`
本次新增两个小程序端接口:
1. **产品详情** — 根据产品ID查询单个已完成的定制产品定制需求详情页展示产品方案
2. **产品列表** — 查询当前登录用户所有已完成的定制产品("我的定制"页展示列表)
### 业务流程
```
用户提交定制需求 → 定制师接单 → 设计产品(DRAFT)
→ 完成设计(COMPLETED) → 用户查看产品方案 → 下单(ORDERED)
本次新增接口覆盖此环节
```
### 聚合调用链
```
小程序 → /mp/custom/products (BFF)
├─ Feign → order-service: 获取用户定制需求关联的 productId 列表
└─ Feign → product-service: 批量查询 CUSTOM + COMPLETED/ORDERED 产品
```
---
## 接口清单
| # | 接口 | 方法 | 路径 | 认证 | 说明 |
|---|------|------|------|------|------|
| 1 | 已完成定制产品详情 | GET | `/mp/custom/product/{productId}` | ✅ 需登录 | 根据产品ID查询定制产品详情 |
| 2 | 我的已完成定制产品列表 | GET | `/mp/custom/products` | ✅ 需登录 | 当前用户的所有已完成定制产品 |
---
## 接口 1已完成定制产品详情
**使用场景**:定制需求详情页中,当定制师完成产品设计后(状态 QUOTED/COMPLETED,前端通过定制需求中的 `productId` 调用此接口展示完整产品方案。
```
GET /mp/custom/product/{productId}
```
### 请求参数
| 参数 | 类型 | 位置 | 必填 | 说明 |
|------|------|------|------|------|
| productId | Long | 路径 | ✅ | 定制产品ID来自定制需求详情中的 `productId` 字段) |
### 请求头
| Header | 说明 |
|--------|------|
| Authorization | Bearer {token}JWT令牌 |
### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"productId": "2026279738275856385",
"productNo": "P20260319001",
"productType": "CUSTOM",
"name": "呼伦贝尔7日定制游-张三家庭",
"subtitle": "专属家庭定制方案",
"status": "COMPLETED",
"tripDays": 7,
"tripNights": 6,
"departureCity": "北京",
"destinationCity": "呼伦贝尔",
"coverImageUrl": "https://oss.example.com/cover.jpg",
"bannerImageUrls": ["https://oss.example.com/banner1.jpg"],
"tags": ["草原", "亲子", "定制"],
"seasons": ["SUMMER"],
"seasonLabels": ["夏季"],
"highlights": "...",
"description": "...",
"inclusions": "费用包含说明",
"exclusions": "费用不含说明",
"notes": "注意事项",
"routeMapUrl": "https://oss.example.com/route-map.jpg",
"itinerary": [
{
"dayIndex": 1,
"title": "北京 → 海拉尔",
"description": "...",
"nodes": [...]
}
],
"staffConfigs": [
{
"staffType": "GUIDE",
"staffName": "李导",
"staffCount": 1,
"dailyCost": 500.00
}
],
"families": [
{
"familyIndex": 1,
"familyName": "张三家庭",
"adultCount": 2,
"childCount": 1
}
],
"creatorAvatarUrl": "https://oss.example.com/designer-avatar.jpg",
"customizerId": "2026279738275856300",
"createTime": "2026-03-19T10:30:00",
"updateTime": "2026-03-19T15:00:00"
}
}
```
### 响应字段
| 字段 | 类型 | 说明 |
|------|------|------|
| productId | String | 产品ID雪花ID,用String接收 |
| productNo | String | 产品编号P开头 |
| productType | String | 产品类型,固定 `CUSTOM` |
| name | String | 产品名称 |
| subtitle | String | 副标题 |
| status | String | 产品状态:`COMPLETED`=已完成可下单、`ORDERED`=已有订单 |
| tripDays | Integer | 行程天数 |
| tripNights | Integer | 住宿晚数 |
| departureCity | String | 出发城市 |
| destinationCity | String | 目的地城市 |
| coverImageUrl | String | 封面图URL |
| bannerImageUrls | Array\<String\> | 轮播图URL列表 |
| tags | Array\<String\> | 产品标签 |
| seasons | Array\<String\> | 适用季节编码 |
| seasonLabels | Array\<String\> | 季节中文标签 |
| highlights | String | 产品亮点 |
| description | String | 详细描述 |
| inclusions | String | 费用包含 |
| exclusions | String | 费用不含 |
| notes | String | 注意事项 |
| routeMapUrl | String | 路线图URL |
| itinerary | Array | 行程安排(按天组织) |
| itinerary[].dayIndex | Integer | 第几天 |
| itinerary[].title | String | 当天标题 |
| itinerary[].description | String | 当天描述 |
| itinerary[].nodes | Array | 行程节点列表 |
| **staffConfigs** | Array | **人员配置(定制产品特有)** |
| staffConfigs[].staffType | String | 人员类型GUIDE=导游等) |
| staffConfigs[].staffName | String | 人员姓名 |
| staffConfigs[].staffCount | Integer | 人数 |
| staffConfigs[].dailyCost | BigDecimal | 日费用 |
| **families** | Array | **家庭分组(定制产品特有)** |
| families[].familyIndex | Integer | 家庭序号 |
| families[].familyName | String | 家庭名称 |
| families[].adultCount | Integer | 成人数 |
| families[].childCount | Integer | 儿童数 |
| customizerId | String | 定制师ID |
| creatorAvatarUrl | String | 定制师头像URL |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
### 错误响应
| code | message | 说明 |
|------|---------|------|
| 404 | 产品不存在 | productId 无效 |
| 404 | 产品不存在(非定制产品) | 不是 CUSTOM 类型 |
| 404 | 定制产品尚未完成 | 状态不是 COMPLETED/ORDERED |
### 前端实现建议
- 定制需求详情页中,当 `status``QUOTED``COMPLETED``productId` 不为空时,展示"查看定制方案"按钮
- 点击按钮调用此接口,跳转到产品详情页展示完整方案
- `staffConfigs``families` 是定制产品特有字段,需单独展示区域
---
## 接口 2我的已完成定制产品列表
**使用场景**"我的定制"页面展示用户所有已完成的定制产品列表。
```
GET /mp/custom/products
```
### 请求参数
无(自动取当前登录用户)
### 请求头
| Header | 说明 |
|--------|------|
| Authorization | Bearer {token}JWT令牌 |
### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [
{
"productId": "2026279738275856385",
"productType": "CUSTOM",
"name": "呼伦贝尔7日定制游-张三家庭",
"subtitle": "专属家庭定制方案",
"tripDays": 7,
"tripNights": 6,
"departureCity": "北京",
"destinationCity": "呼伦贝尔",
"coverImageUrl": "https://oss.example.com/cover.jpg",
"tags": ["草原", "亲子", "定制"],
"seasons": ["SUMMER"],
"seasonLabels": ["夏季"],
"routeMapUrl": "https://oss.example.com/route-map.jpg",
"creatorAvatarUrl": "https://oss.example.com/designer-avatar.jpg",
"startPrice": 29800.00,
"paymentMode": "DEPOSIT",
"lineName": null
}
]
}
```
### 响应字段
| 字段 | 类型 | 说明 |
|------|------|------|
| productId | String | 产品ID |
| productType | String | 产品类型,固定 `CUSTOM` |
| name | String | 产品名称 |
| subtitle | String | 副标题 |
| tripDays | Integer | 行程天数 |
| tripNights | Integer | 住宿晚数 |
| departureCity | String | 出发城市 |
| destinationCity | String | 目的地城市 |
| coverImageUrl | String | 封面图URL |
| tags | Array\<String\> | 产品标签 |
| seasons | Array\<String\> | 适用季节编码 |
| seasonLabels | Array\<String\> | 季节中文标签 |
| routeMapUrl | String | 路线图URL |
| creatorAvatarUrl | String | 定制师头像 |
| startPrice | BigDecimal | 起步价整单售价,可能为null未设置价格日历 |
| paymentMode | String | 支付方式:`FULL`-全额、`DEPOSIT`-定金 |
| lineName | String | 产品线名称,可能为null |
### 前端实现建议
- 用 `uni-list` 或自定义卡片列表展示
- 每张卡片展示:封面图 + 名称 + 目的地 + 天数 + 起步价
- 点击卡片跳转到接口1查看完整详情
- 列表为空时展示空状态:"暂无定制产品,去提交定制需求吧"
- 定制产品为**整单计价**perOrderStock`startPrice` 是整单价格,不是每人价格
---
## 关联字典
| 字典类型 | 字典值 | 中文标签 | 说明 |
|----------|--------|----------|------|
| **product_type** | CUSTOM | 私人定制 | 定制产品类型 |
| **product_status** | COMPLETED | 已完成 | 定制师设计完成,可下单 |
| | ORDERED | 已下单 | 已有活跃订单 |
| **customize_request_status** | QUOTED | 已报价 | 定制需求中产品已关联 |
| | COMPLETED | 已完成 | 定制需求流程完结 |
| **season** | SPRING | 春季 | |
| | SUMMER | 夏季 | |
| | AUTUMN | 秋季 | |
| | WINTER | 冬季 | |
| | ALL_SEASON | 全年 | |
---
## 校验规则
- **接口1**productId 必须为 CUSTOM 类型且状态为 COMPLETED 或 ORDERED,否则返回404
- **接口2**自动关联当前用户的定制需求status=QUOTED/COMPLETED 且 productId 不为空),无需传参
- 定制产品**一单一产品**:同一个定制产品同时只能有一个有效订单
- 定制产品为**整单计价**:价格日历中的 `adultSellPrice` 即整单售价,不按人头乘以
---
## 页面布局建议
### "我的定制"列表页
```
┌─────────────────────────────────────────┐
│ 我的定制产品 │
├─────────────────────────────────────────┤
│ ┌───────────────────────────────────┐ │
│ │ [封面图] 呼伦贝尔7日定制游-张三家庭 │ │
│ │ 北京 → 呼伦贝尔 · 7天6晚 │ │
│ │ ¥29,800/整单 │ │
│ │ 定制师: [头像] 李设计师 │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ [封面图] 三亚5日定制游-王先生 │ │
│ │ 上海 → 三亚 · 5天4晚 │ │
│ │ ¥18,600/整单 │ │
│ └───────────────────────────────────┘ │
│ │
│ ── 没有更多了 ── │
└─────────────────────────────────────────┘
```
### 定制产品详情页接口1展示
```
┌─────────────────────────────────────────┐
│ [轮播图 bannerImageUrls] │
├─────────────────────────────────────────┤
│ 呼伦贝尔7日定制游-张三家庭 │
│ 专属家庭定制方案 │
│ 北京 → 呼伦贝尔 · 7天6晚 │
│ 标签: [草原] [亲子] [定制] │
├─────────────────────────────────────────┤
│ 👨‍👩‍👧 家庭分组 │
│ 家庭1: 张三家庭2大1小
├─────────────────────────────────────────┤
│ 👤 人员配置 │
│ 导游: 李导 × 1人 │
├─────────────────────────────────────────┤
│ 📅 行程安排 │
│ Day 1: 北京 → 海拉尔 │
│ Day 2: 莫日格勒河 → 额尔古纳 │
│ ... │
├─────────────────────────────────────────┤
│ [路线图 routeMapUrl] │
├─────────────────────────────────────────┤
│ 费用包含 / 费用不含 / 注意事项 │
├─────────────────────────────────────────┤
│ [立即预订 ¥29,800] │
└─────────────────────────────────────────┘
```
---
## 完整调用示例
### 示例1从定制需求跳转查看产品
```javascript
// 1. 获取定制需求详情(已有接口)
const detail = await request.get('/mp/custom/detail', { params: { id: requestId } })
const { productId, status } = detail.data
// 2. 如果定制师已完成产品设计,跳转查看
if (productId && ['QUOTED', 'COMPLETED'].includes(status)) {
uni.navigateTo({
url: `/pages/custom/product-detail?productId=${productId}`
})
}
// 3. 在产品详情页加载数据
const product = await request.get(`/mp/custom/product/${productId}`)
// product.data 包含完整产品信息
```
### 示例2展示"我的定制产品"列表
```javascript
// 1. 获取列表
const res = await request.get('/mp/custom/products')
const products = res.data // Array
// 2. 列表为空时展示空状态
if (!products || products.length === 0) {
// 展示 "暂无定制产品"
return
}
// 3. 渲染列表
products.forEach(item => {
// item.name, item.coverImageUrl, item.tripDays, item.startPrice
// 注意: startPrice 是整单价格
})
// 4. 点击跳转详情
function onItemClick(productId) {
uni.navigateTo({
url: `/pages/custom/product-detail?productId=${productId}`
})
}
```
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)