新增 GET /mp/product/ai-recommend 免登录开放接口,供 AI 推荐 调用。支持多维筛选(关键词/目的地/天数/季节/标签/报价/出发 日期/人数/资源类型等),排除私人定制 CUSTOM。 Co-Authored-By: Claude <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,234 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "6207"
|
||||||
|
title: "AI 推荐产品多维筛选开放接口(免登录,排除私人定制)"
|
||||||
|
consumer: "mp"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "新增接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: "mmg"
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-08-23"
|
||||||
|
status_note: "GET /mp/product/ai-recommend 免登录开放接口,AI 推荐用。默认返回 PUBLISHED + CORE/GROUP(排除 CUSTOM 私人定制),支持多维筛选。后端/网关已测试服验证,前端待消费。"
|
||||||
|
updated_at: "2026-08-23"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 【新增接口·小程序端】AI 推荐产品多维筛选开放接口(#6207)
|
||||||
|
|
||||||
|
> **PR**: #6220 | **服务**: hl-mp-service / hl-product-service-v2 / hl-gateway | **作者**: wx | **更新时间**: 2026-08-23
|
||||||
|
|
||||||
|
> 免登录开放接口,供 AI 做产品推荐调用。**除私人定制(CUSTOM)以外的全部已上架(PUBLISHED)产品**,多维筛选,全部条件可选可组合。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、背景
|
||||||
|
|
||||||
|
AI 做产品推荐需要一个查询接口:能筛选除私人定制以外的全部已上架产品,条件越多越好(目的地、天数、报价范围、团期、包含资源、人数、季节等),**不需要登录**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | AI推荐产品查询 | GET | `/mp/product/ai-recommend` | 🆕 新增 | 免登录,多维筛选,分页 |
|
||||||
|
|
||||||
|
> 网关白名单已放行(`JwtAuthFilter` SKIP_URLS),无 token 可直接调用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. AI推荐产品查询 `GET /mp/product/ai-recommend`
|
||||||
|
|
||||||
|
BFF 透传至 product-service `GET /internal/mp/product/ai-query`。pageSize 最大 50。
|
||||||
|
|
||||||
|
**数据范围(硬性)**:
|
||||||
|
- `product.status = PUBLISHED`(已上架)
|
||||||
|
- `product_type IN (CORE, GROUP)`(**排除 CUSTOM 私人定制**)
|
||||||
|
|
||||||
|
#### 入参(全部可选,可任意组合)
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| keyword | Query | String | 否 | ≤100 字符 | 关键词/目的地(匹配产品名/副标题) |
|
||||||
|
| productType | Query | String | 否 | CORE/GROUP | 产品类型(默认 CORE+GROUP,传单一值可收窄) |
|
||||||
|
| category | Query | String | 否 | family/honeymoon/photography/experience/driving | 产品分类 |
|
||||||
|
| lineId | Query | Long | 否 | - | 产品线ID |
|
||||||
|
| minDays | Query | Integer | 否 | ≥1 | 最短天数 |
|
||||||
|
| maxDays | Query | Integer | 否 | ≥1 | 最长天数 |
|
||||||
|
| seasons | Query | String | 否 | spring/summer/autumn/winter,逗号分隔多值 | 季节 |
|
||||||
|
| tags | Query | String | 否 | 逗号分隔多值 | 产品标签 |
|
||||||
|
| minPrice | Query | BigDecimal | 否 | - | 最低起步价 |
|
||||||
|
| maxPrice | Query | BigDecimal | 否 | - | 最高起步价 |
|
||||||
|
| startDate | Query | String | 否 | yyyy-MM-dd | 出发日期起始 |
|
||||||
|
| endDate | Query | String | 否 | yyyy-MM-dd | 出发日期结束 |
|
||||||
|
| partySize | Query | Integer | 否 | ≥1 | 人数(仅与 startDate+endDate 组合时生效,过滤「订不下」的产品) |
|
||||||
|
| resourceType | Query | String | 否 | SCENIC/ACTIVITY/RESTAURANT/SERVICE/CUSTOM | 包含资源类型 |
|
||||||
|
| resourceKeyword | Query | String | 否 | - | 包含资源关键词 |
|
||||||
|
| orderBy | Query | String | 否 | sort/price_asc/price_desc/newest | 排序 |
|
||||||
|
| page | Query | Integer | 否 | 默认1,≥1 | 页码 |
|
||||||
|
| pageSize | Query | Integer | 否 | 默认10,1~50 | 每页条数(**最大 50**,>50 返 400) |
|
||||||
|
|
||||||
|
> **seasons/tags 传法**:逗号分隔多值,如 `seasons=summer,winter`、`tags=亲子,摄影`。
|
||||||
|
|
||||||
|
#### 出参 `Result<PageResult<MpProductAiQueryItemVO>>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| records[].productId | String | 产品ID(雪花,字符串防精度丢失) |
|
||||||
|
| records[].productNo | String | 产品编号 |
|
||||||
|
| records[].productType | String | CORE/GROUP |
|
||||||
|
| records[].name | String | 产品名称 |
|
||||||
|
| records[].subtitle | String | 副标题 |
|
||||||
|
| records[].introduction | String | 产品简介 |
|
||||||
|
| records[].category | String | 产品分类 |
|
||||||
|
| records[].tripDays / tripNights | Integer | 天数 / 晚数 |
|
||||||
|
| records[].coverImageUrl | String | 封面图 |
|
||||||
|
| records[].carouselImages | String[] | 轮播图 |
|
||||||
|
| records[].tags / seasons | String[] | 标签 / 季节 |
|
||||||
|
| records[].lineId | String | 产品线ID |
|
||||||
|
| records[].lineName | String | 产品线名称 |
|
||||||
|
| records[].startPrice | String | 起步价(字符串) |
|
||||||
|
| records[].startPriceLabel | String | `¥3980起/人` |
|
||||||
|
| records[].paymentType | String | FULL / DEPOSIT |
|
||||||
|
| records[].status | String | PUBLISHED |
|
||||||
|
| records[].sortOrder | Integer | 排序号 |
|
||||||
|
| records[].earliestBookingDate | String | 最早可订日期 yyyy-MM-dd |
|
||||||
|
| records[].availableDateSummary | String | 可订日期摘要(如 `2026-09-11 起`) |
|
||||||
|
| records[].availabilitySummary | String | 余位摘要(GROUP: `8个班期可报名,余位充足(2026-09-11 ~ 2026-10-30)`;CORE: `9天可订(2026-08-23 ~ 2026-08-31)`) |
|
||||||
|
| records[].matchedDestinations | String[] | 匹配到的目的地/途经点摘要 |
|
||||||
|
| total | Integer | 总条数 |
|
||||||
|
| page / pageSize | Integer | 页码 / 每页条数 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /mp/product/ai-recommend?productType=GROUP&seasons=summer&minPrice=0&maxPrice=20000&page=1&pageSize=5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例(测试服实测)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"productId": "2056947670512971778",
|
||||||
|
"productNo": "G260423004",
|
||||||
|
"productType": "GROUP",
|
||||||
|
"name": "测试小蒙马-多档-固定金额",
|
||||||
|
"subtitle": "",
|
||||||
|
"introduction": "测试产品简介",
|
||||||
|
"category": "photography",
|
||||||
|
"tripDays": 5,
|
||||||
|
"tripNights": 4,
|
||||||
|
"coverImageUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/...",
|
||||||
|
"tags": ["小蒙马"],
|
||||||
|
"seasons": ["summer"],
|
||||||
|
"lineId": "2001",
|
||||||
|
"lineName": "测试产品线",
|
||||||
|
"startPrice": "8800.00",
|
||||||
|
"startPriceLabel": "¥8800起/人",
|
||||||
|
"paymentType": "FULL",
|
||||||
|
"status": "PUBLISHED",
|
||||||
|
"sortOrder": 0,
|
||||||
|
"earliestBookingDate": "2026-09-11",
|
||||||
|
"availableDateSummary": "2026-09-11 起",
|
||||||
|
"availabilitySummary": "8个班期可报名,余位充足(2026-09-11 ~ 2026-10-30)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 5,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 5
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据响应(无匹配条件)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": { "records": [], "total": 0, "page": 1, "pageSize": 5 },
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 错误响应(参数防御)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"message": "每页条数不能大于50",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
### 筛选语义(后端为准)
|
||||||
|
|
||||||
|
- **所有条件 AND 组合**:同时传多个条件时,返回同时满足全部条件的产品。
|
||||||
|
- **seasons / tags 多值取 OR**:`seasons=summer,winter` 表示「夏季 **或** 冬季」。
|
||||||
|
- **报价范围按起步价过滤**:起步价 = GROUP 未来班期最低成人价 或 CORE 价格日历未来最低价,`minPrice/maxPrice` 作用于该值。
|
||||||
|
- **人数(partySize)过滤语义**:**仅当同时传了出发日期范围(startDate/endDate 至少一个)+ partySize 才生效**——过滤「订不下」的产品:
|
||||||
|
- GROUP:看日期范围内是否有余位 ≥ partySize 的可报名班期(ENROLLING/NEARLY_FULL)
|
||||||
|
- CORE:看日期范围内是否有库存(dailyStock - sold)≥ partySize 的日期
|
||||||
|
- **只传 partySize 不传日期 → 不触发人数过滤**。
|
||||||
|
- **目的地关键词**:匹配产品名/副标题 **或** 途经点名称 **或** 行程节点名称(任一处命中即算)。
|
||||||
|
- **日期倒置自动修正**:startDate 晚于 endDate 时后端自动交换,不报错。
|
||||||
|
|
||||||
|
### ✅ 正确 / ⚠️ 注意
|
||||||
|
|
||||||
|
| 场景 | 行为 |
|
||||||
|
|------|------|
|
||||||
|
| ✅ 无条件调用 | 返回全部在售(CORE+GROUP 且 PUBLISHED)分页 |
|
||||||
|
| ✅ `seasons=summer,winter` | 夏季或冬季的产品 |
|
||||||
|
| ✅ `minPrice=1000&maxPrice=5000` | 起步价在区间内的产品 |
|
||||||
|
| ✅ `startDate=2026-09-01&endDate=2026-10-01&partySize=2` | 该日期范围内能订下 2 人的产品 |
|
||||||
|
| ⚠️ `pageSize=999` | 400 拒绝(上限 50) |
|
||||||
|
| ⚠️ 只传 `partySize` 不传日期 | 人数条件不生效 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、测试环境已验证(2026-08-23)
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /mp/product/ai-recommend?page=1&pageSize=5 → 200 total=22 ✓
|
||||||
|
GET /mp/product/ai-recommend?keyword=草原&page=1&pageSize=5 → 200 total=13 ✓
|
||||||
|
GET /mp/product/ai-recommend?productType=GROUP&page=1&pageSize=3 → 200 total=6 ✓
|
||||||
|
GET /mp/product/ai-recommend?category=family&page=1&pageSize=5 → 200 total=12 ✓
|
||||||
|
GET /mp/product/ai-recommend?seasons=summer,winter&page=1&pageSize=5 → 200 total=19 ✓
|
||||||
|
GET /mp/product/ai-recommend?minPrice=1000&maxPrice=5000&page=1&pageSize=3 → 200 ✓
|
||||||
|
GET /mp/product/ai-recommend?orderBy=price_asc&page=1&pageSize=5 → 200 ✓(第一条约 2980 便宜档)
|
||||||
|
GET /mp/product/ai-recommend?productType=GROUP&seasons=summer&minPrice=0&maxPrice=20000 → 200 total=5 ✓
|
||||||
|
GET /mp/product/ai-recommend?startDate=2026-08-24&endDate=2026-10-22 → 200 total=14 ✓
|
||||||
|
GET /mp/product/ai-recommend?pageSize=999 → 400 每页条数不能大于50 ✓
|
||||||
|
GET /mp/product/ai-recommend?category=driving&seasons=winter&keyword=不存在xyz → 200 empty ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
免登录(无 token)经网关调用全部返回 200。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、相关历史
|
||||||
|
|
||||||
|
- 关联 Issue: [wx/HL#6207](https://git.1814.love:8443/wx/HL/issues/6207)
|
||||||
|
- 关联 PR: [wx/HL#6220](https://git.1814.love:8443/wx/HL/pulls/6220)
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- **后端负责人**: @wx
|
||||||
|
- **前端负责人**: @mmg
|
||||||
在新工单中引用
屏蔽一个用户