--- 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>` | 字段 | 类型 | 说明 | |------|------|------| | 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