- Add new database columns (coverImage, priceAmount, priceUnit, tags, status) to HotelGroup model - Create hotel_group_dict serializer to handle image/coverImage synchronization and tag formatting - Update site config endpoints to use the new serializer and filter published hotel groups - Add Alembic migration for the new database schema changes - Update test fixtures and API contract tests for the new fields - Revise public and admin API documentation to document the new hotel group fields and usage rules
522 lines
22 KiB
Markdown
522 lines
22 KiB
Markdown
# WonderQ-MiniAPP Public API 对接文档
|
||
|
||
最后更新:2026-07-02
|
||
|
||
本文档定义 `WonderQ-MiniAPP` 前台 H5/小程序对接 `WonderQ-Admin` 后端所需的 Public API 契约。当前 MiniAPP 主动调用站点配置、产品列表和线索提交 3 个接口;后端已存在的健康检查、产品详情和目的地列表接口建议继续保留,供后续前台按需接入。
|
||
|
||
## 基础约定
|
||
|
||
- 基础地址由 MiniAPP 环境变量 `VITE_API_BASE_URL` 控制;为空时前台按同源 `/api` 请求。
|
||
- Public API 不要求前台登录认证。
|
||
- 请求和响应均使用 JSON,字符集为 UTF-8。
|
||
- 图片字段应返回可被 H5 和微信小程序访问的 URL;现有前台兼容 `/assets/...` 形式。
|
||
- 列表字段建议返回空数组,不建议返回 `null`;MiniAPP 对站点配置和产品列表有本地兜底内容。
|
||
- 错误响应需提供可展示信息,兼容 `{ "message": "..." }` 或 FastAPI 默认 `{ "detail": "..." }`。不要暴露内部异常、真实环境变量、Token、JWT secret、客服链接或企业 ID。
|
||
|
||
## 数据类型
|
||
|
||
### `HeroSlide`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 轮播图 ID |
|
||
| `title` | `string` | 是 | 主标题 |
|
||
| `kicker` | `string \| null` | 否 | 辅助短文案 |
|
||
| `image` | `string` | 是 | 图片 URL |
|
||
| `targetType` | `string \| null` | 否 | 点击目标类型 |
|
||
| `targetValue` | `string \| null` | 否 | 点击目标值 |
|
||
| `isActive` | `boolean` | 否 | 是否启用 |
|
||
| `sortOrder` | `number` | 否 | 后台排序值,Public API 按该字段升序输出 |
|
||
|
||
### `Destination`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 目的地 ID |
|
||
| `name` | `string` | 是 | 目的地名称 |
|
||
| `image` | `string \| null` | 否 | 图片 URL |
|
||
| `isHot` | `boolean` | 否 | 是否热门 |
|
||
| `isActive` | `boolean` | 否 | 是否启用 |
|
||
| `sortOrder` | `number` | 否 | 后台排序值,Public API 按该字段升序输出 |
|
||
| `aliases` | `Array<{ id: string; alias: string }>` | 否 | 搜索别名 |
|
||
|
||
### `Theme`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 主题 ID |
|
||
| `label` | `string` | 是 | 主题名称 |
|
||
| `image` | `string` | 是 | 主题图片 URL |
|
||
| `targetType` | `string \| null` | 否 | 点击目标类型 |
|
||
| `targetValue` | `string \| null` | 否 | 点击目标值 |
|
||
| `isActive` | `boolean` | 否 | 是否启用 |
|
||
| `sortOrder` | `number` | 否 | 后台排序值,Public API 按该字段升序输出 |
|
||
|
||
### `CtaBanner`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 服务卡片 ID |
|
||
| `alt` | `string` | 是 | 服务标题,展示在“更多服务”卡片上 |
|
||
| `image` | `string` | 是 | 服务卡片背景图 URL |
|
||
| `targetType` | `string \| null` | 否 | 点击目标类型 |
|
||
| `targetValue` | `string \| null` | 否 | 点击目标值 |
|
||
| `isActive` | `boolean` | 否 | 是否启用 |
|
||
| `sortOrder` | `number` | 否 | 后台排序值,Public API 按该字段升序输出 |
|
||
|
||
### `Campaign`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 活动 ID |
|
||
| `slug` | `string` | 是 | 活动标识 |
|
||
| `title` | `string` | 是 | 活动标题 |
|
||
| `description` | `string \| null` | 否 | 活动描述 |
|
||
| `coverImage` | `string \| null` | 否 | 活动封面图 |
|
||
| `priceAmount` | `number \| null` | 否 | 参考起价,单位按 `priceUnit` 展示 |
|
||
| `priceUnit` | `string \| null` | 否 | 价格单位文案,默认 `起/人` |
|
||
| `tags` | `string[]` | 否 | 活动卡片标签,最多 3 个 |
|
||
| `status` | `string` | 是 | 活动状态;Public API 只返回 `published` |
|
||
| `startsAt` | `string \| null` | 否 | 活动开始时间 |
|
||
| `endsAt` | `string \| null` | 否 | 活动结束时间 |
|
||
|
||
### `RouteSection`
|
||
|
||
`RouteSection` 用于描述首页“精选线路”下的动态运营分组。后台可按任务新增、编辑、删除和排序分组;MiniAPP 不应依赖固定分组 ID,只按接口返回的分组顺序和 `productIds` 渲染。
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 后端生成的分组 ID,客户端只用于列表 key 和商品关联,不作为固定业务枚举 |
|
||
| `title` | `string` | 是 | 分组标题 |
|
||
| `subtitle` | `string \| null` | 否 | 分组副文案 |
|
||
| `productIds` | `string[]` | 是 | 该分组包含的产品 ID;产品详情来自 `/api/public/products.items` |
|
||
| `isActive` | `boolean` | 否 | 是否启用;Public API 通常只返回启用分组 |
|
||
| `sortOrder` | `number` | 否 | 后台展示顺序;Public API 按该字段升序输出 |
|
||
|
||
Public API 输出规则:
|
||
- `GET /api/public/site-config` 只返回启用的 `routeSections`。
|
||
- `routeSections[].productIds` 只包含已发布商品 ID;未发布、归档或不存在的商品不得出现在 Public 响应中。
|
||
- 动态分组按后台 `sortOrder` 升序返回,`productIds` 的顺序就是用户侧商品卡展示顺序。
|
||
- MiniAPP 会按 `productIds` 匹配 `/api/public/products.items[].id`;接口缺失、`routeSections` 为空或没有可匹配商品时回退 `src/content.ts` 的本地精选线路兜底内容。
|
||
|
||
### `HomeCard`
|
||
|
||
`HomeCard` 用于首页“万趣用车”等普通内容卡片模块。MiniAPP 只消费卡片展示字段,不在这些模块里读取商品本体或线路商品关联。
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 后端生成的卡片 ID |
|
||
| `title` | `string` | 是 | 卡片标题 |
|
||
| `description` | `string \| null` | 否 | 卡片描述 |
|
||
| `image` | `string \| null` | 否 | 卡片封面图 URL;为空时客户端可使用本地兜底图 |
|
||
| `isActive` | `boolean` | 否 | 是否启用;Public API 通常只返回启用卡片 |
|
||
| `sortOrder` | `number` | 否 | 后台展示顺序;Public API 按该字段升序输出 |
|
||
|
||
### `HotelCard`
|
||
|
||
`HotelCard` 用于首页“特色酒店”模块。该模块按管理端“特价优惠”同类配置方式维护标题、描述、价格、标签、封面图和发布状态,但仍然只代表首页酒店展示卡片,不绑定商品本体。
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 后端生成的酒店卡片 ID |
|
||
| `title` | `string` | 是 | 酒店卡片标题 |
|
||
| `description` | `string \| null` | 否 | 酒店卡片描述 |
|
||
| `image` | `string \| null` | 否 | 兼容旧字段;后端会与 `coverImage` 保持一致 |
|
||
| `coverImage` | `string \| null` | 否 | 酒店封面图主字段;MiniAPP 优先使用该字段 |
|
||
| `priceAmount` | `number \| null` | 否 | 价格数值,前端可按页面需要展示 |
|
||
| `priceUnit` | `string \| null` | 否 | 价格单位文案,例如 `起/晚` |
|
||
| `tags` | `string[]` | 否 | 酒店标签,最多 3 个 |
|
||
| `status` | `"draft" \| "published"` | 否 | 发布状态;Public API 只返回 `published` |
|
||
| `isActive` | `boolean` | 否 | 是否启用;Public API 只返回启用项 |
|
||
| `sortOrder` | `number` | 否 | 后台展示顺序;Public API 按该字段升序输出 |
|
||
|
||
Public API 输出规则:
|
||
- `GET /api/public/site-config` 只返回 `status="published"` 且 `isActive=true` 的 `hotelGroups`。
|
||
- `GET /api/public/site-config` 只返回启用的 `vehicleOptions`。
|
||
- 两个数组按后台 `sortOrder` 升序返回。
|
||
- `hotelGroups` 字段缺失、数组为空或没有可用图片时,MiniAPP 使用 `src/content.ts` 的本地特色酒店内容兜底;酒店图片优先取 `coverImage`,再取 `image`。
|
||
- `vehicleOptions` 字段缺失、数组为空或图片为空时,MiniAPP 使用 `src/content.ts` 的本地万趣用车内容兜底。
|
||
|
||
### `PublicProduct`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 是 | 后端产品 UUID |
|
||
| `sourceId` | `number \| null` | 否 | 历史采集产品 ID;前台可用于兼容旧数据 |
|
||
| `title` | `string` | 是 | 产品标题 |
|
||
| `subtitle` | `string \| null` | 否 | 副标题 |
|
||
| `destination` | `{ id: string; name: string } \| null` | 否 | 目的地信息 |
|
||
| `priceAmount` | `number \| null` | 否 | 参考起价,单位按后端内容约定 |
|
||
| `priceUnit` | `string \| null` | 否 | 价格单位文案 |
|
||
| `tags` | `string[]` | 否 | 标签列表 |
|
||
| `coverImage` | `string \| null` | 否 | 封面图 URL |
|
||
| `summary` | `string \| null` | 否 | 摘要 |
|
||
| `images` | `Array<ProductImage>` | 否 | 图集 |
|
||
| `detailSections` | `ProductDetailSection[] \| null` | 否 | 产品详情分区 |
|
||
| `status` | `string` | 否 | 产品状态,前台主要消费 `published` 内容 |
|
||
|
||
### `ProductImage`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `string` | 否 | 图片 ID |
|
||
| `url` | `string` | 是 | 图片 URL |
|
||
| `alt` | `string \| null` | 否 | 图片说明 |
|
||
| `sortOrder` | `number` | 是 | 排序值 |
|
||
|
||
### `ProductDetailSection`
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `key` | `string` | 是 | 分区 key,例如 `overview`、`itinerary`、`service`、`notice`、`price`、`manager` |
|
||
| `label` | `string` | 是 | Tab 展示文案 |
|
||
| `title` | `string \| null` | 否 | 分区标题 |
|
||
| `blocks` | `ProductDetailBlock[]` | 是 | 内容块 |
|
||
|
||
`ProductDetailBlock` 支持两种结构:
|
||
|
||
```json
|
||
{ "type": "text", "text": "文本内容" }
|
||
```
|
||
|
||
```json
|
||
{ "type": "image", "url": "/assets/example.jpg", "alt": "图片说明" }
|
||
```
|
||
|
||
## 接口清单
|
||
|
||
### `GET /health`
|
||
|
||
用于服务健康检查。
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"service": "miniapp-api"
|
||
}
|
||
```
|
||
|
||
### `GET /api/public/site-config`
|
||
|
||
用于首页轮播、目的地、主题入口和更多服务配置。MiniAPP 启动时会和产品列表并行请求该接口;接口不可用或关键数组为空时,前台会回退本地静态内容。
|
||
|
||
#### 响应字段
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `heroSlides` | `HeroSlide[]` | 首页顶部轮播 |
|
||
| `destinations` | `Destination[]` | 首页目的地入口 |
|
||
| `map` | `Array<{ id: string; image: string; isActive?: boolean }>` | 贵州地图图片;MiniAPP 当前消费 `map[0].image` |
|
||
| `themes` | `Theme[]` | 主题甄选入口 |
|
||
| `ctaBanners` | `CtaBanner[]` | “更多服务”卡片配置 |
|
||
| `campaigns` | `Campaign[]` | 活动元信息,可用于“特价优惠”入口;当前不包含活动产品结果列表 |
|
||
| `routeSections` | `RouteSection[]` | “精选线路”子分组定义 |
|
||
| `hotelGroups` | `HotelCard[]` | “特色酒店”卡片配置,只返回 `published` 且启用项 |
|
||
| `vehicleOptions` | `HomeCard[]` | “万趣用车”卡片配置,只返回启用项 |
|
||
|
||
#### 首页模块数据归属
|
||
|
||
| 首页模块 | 当前接口归属 | 说明 |
|
||
| --- | --- | --- |
|
||
| 特价优惠 | `site-config.campaigns` + `/api/public/products` | 当前 Public API 只返回活动元信息,不直接返回“特价优惠结果列表”。MiniAPP 若要展示活动线路,可按活动标题、标签或后续扩展的活动产品关联从 `/api/public/products` 中筛选。 |
|
||
| 精选线路 | `site-config.routeSections` + `/api/public/products` | `routeSections` 返回动态分组与 `productIds`;具体产品卡片数据由 `/api/public/products.items` 提供。后台可按任务新增、删除、停用和排序分组,用户侧不假设固定三组。 |
|
||
| 更多服务 | `site-config.ctaBanners` | 返回启用服务卡片,按后台排序展示;无有效配置时回退本地 `bottomCtas` 内容。 |
|
||
| 特色酒店 | `site-config.hotelGroups` | 返回 `published` 且启用的酒店卡片,按后台排序展示;MiniAPP 优先消费 `coverImage`,无有效配置时回退本地内容。 |
|
||
| 万趣用车 | `site-config.vehicleOptions` | 返回启用用车卡片,按后台排序展示;无有效配置时回退本地内容。 |
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"heroSlides": [
|
||
{
|
||
"id": "hero-1",
|
||
"title": "贵州小包团定制",
|
||
"kicker": "万趣,你的小包团首选",
|
||
"image": "/assets/guizhou/libo-xiaoqikong.jpg",
|
||
"targetType": "search",
|
||
"targetValue": "贵州",
|
||
"isActive": true
|
||
}
|
||
],
|
||
"destinations": [
|
||
{
|
||
"id": "dest-1",
|
||
"name": "荔波小七孔",
|
||
"image": "/assets/guizhou/libo-xiaoqikong.jpg",
|
||
"isHot": true,
|
||
"isActive": true,
|
||
"aliases": [{ "id": "alias-1", "alias": "小七孔" }]
|
||
}
|
||
],
|
||
"map": [
|
||
{
|
||
"id": "map-1",
|
||
"image": "/assets/guizhou/guizhou-map.jpg",
|
||
"isActive": true
|
||
}
|
||
],
|
||
"themes": [],
|
||
"ctaBanners": [],
|
||
"campaigns": [
|
||
{
|
||
"id": "campaign-1",
|
||
"slug": "classic-deal",
|
||
"title": "经典打卡特惠",
|
||
"description": "经典首游活动",
|
||
"coverImage": "/assets/guizhou/libo-xiaoqikong.jpg",
|
||
"priceAmount": 162500,
|
||
"priceUnit": "起/人",
|
||
"tags": ["臻藏旅位", "赛事庆典"],
|
||
"status": "published",
|
||
"startsAt": null,
|
||
"endsAt": null
|
||
}
|
||
],
|
||
"routeSections": [
|
||
{
|
||
"id": "route-section-001",
|
||
"title": "经典人文打卡线路",
|
||
"subtitle": "黄果树、荔波小七孔、千户苗寨、镇远古城、梵净山一次串联",
|
||
"productIds": ["8a6e7c4f-0000-4000-9000-000000000001"],
|
||
"isActive": true
|
||
},
|
||
{
|
||
"id": "route-section-002",
|
||
"title": "极限山野户外野咖线路",
|
||
"subtitle": "溶洞、峡谷、漂流、峰林骑行和山野咖啡组合",
|
||
"productIds": [],
|
||
"isActive": true
|
||
},
|
||
{
|
||
"id": "route-section-003",
|
||
"title": "人文+户外综合混搭线路",
|
||
"subtitle": "非遗村寨、古城夜游、自然轻探险和精品住宿同程安排",
|
||
"productIds": [],
|
||
"isActive": true
|
||
}
|
||
],
|
||
"hotelGroups": [
|
||
{
|
||
"id": "hotel-group-001",
|
||
"title": "经典酒店",
|
||
"description": "城市接驳、景区度假和温泉休整,适合首游贵州的小包团动线。",
|
||
"image": "/assets/guizhou/bailian-hot-spring.jpg",
|
||
"coverImage": "/assets/guizhou/bailian-hot-spring.jpg",
|
||
"priceAmount": 68000,
|
||
"priceUnit": "起/晚",
|
||
"tags": ["温泉", "亲子"],
|
||
"status": "published",
|
||
"isActive": true,
|
||
"sortOrder": 0
|
||
}
|
||
],
|
||
"vehicleOptions": [
|
||
{
|
||
"id": "vehicle-option-001",
|
||
"title": "5座舒适用车",
|
||
"description": "适合2-4人家庭或好友小团,城市接送、景区穿梭更灵活。",
|
||
"image": "/assets/guizhou/jiaxiu-tower.jpg",
|
||
"isActive": true,
|
||
"sortOrder": 0
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### `GET /api/public/products`
|
||
|
||
用于首页产品分区、搜索结果、活动页、目的地页、详情推荐和预订入口。当前 MiniAPP 一次拉取列表后在前端做搜索、筛选和推荐。
|
||
|
||
#### Query 参数
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `keyword` | `string` | 否 | 无 | 关键词搜索,建议匹配产品标题、副标题、标签、目的地名称和目的地别名 |
|
||
| `destinationId` | `string` | 否 | 无 | 按目的地 ID 筛选 |
|
||
| `status` | `string` | 否 | `published` | 产品状态 |
|
||
| `take` | `number` | 否 | `48` | 返回数量,后端当前限制 1-100 |
|
||
|
||
#### 响应字段
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `items` | `PublicProduct[]` | 产品列表 |
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "8a6e7c4f-0000-4000-9000-000000000001",
|
||
"sourceId": 101,
|
||
"title": "黄果树瀑布小包团",
|
||
"subtitle": "错峰入园,私家车接送",
|
||
"destination": { "id": "dest-anshun", "name": "黄果树" },
|
||
"priceAmount": 398000,
|
||
"priceUnit": "起/人",
|
||
"tags": ["贵州", "黄果树", "小包团"],
|
||
"coverImage": "/assets/guizhou/huangguoshu.jpg",
|
||
"summary": "适合首次到贵州的经典线路。",
|
||
"images": [
|
||
{
|
||
"id": "img-1",
|
||
"url": "/assets/guizhou/huangguoshu.jpg",
|
||
"alt": "黄果树瀑布",
|
||
"sortOrder": 0
|
||
}
|
||
],
|
||
"detailSections": [
|
||
{
|
||
"key": "overview",
|
||
"label": "行程概述",
|
||
"title": "小包团专属概览",
|
||
"blocks": [{ "type": "text", "text": "按同行人、预算和体力强度重排行程。" }]
|
||
}
|
||
],
|
||
"status": "published"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### `GET /api/public/products/{product_id}`
|
||
|
||
后端已存在,建议保留给 MiniAPP 后续详情页按需拉取。当前 MiniAPP 主要通过产品列表缓存进入详情。
|
||
|
||
#### Path 参数
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `product_id` | `string` | 产品 UUID;如果传入纯数字,后端按 `sourceId` 查询 |
|
||
|
||
#### 成功响应
|
||
|
||
返回单个 `PublicProduct`。
|
||
|
||
#### 异常响应
|
||
|
||
| 状态码 | 说明 |
|
||
| --- | --- |
|
||
| `404` | 产品不存在 |
|
||
|
||
示例:
|
||
|
||
```json
|
||
{
|
||
"detail": "线路不存在"
|
||
}
|
||
```
|
||
|
||
### `GET /api/public/destinations`
|
||
|
||
后端已存在,建议保留给 MiniAPP 后续目的地页独立拉取。当前 MiniAPP 首页目的地来自 `site-config.destinations`。
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"id": "dest-libo",
|
||
"name": "荔波小七孔",
|
||
"slug": "libo-xiaoqikong",
|
||
"region": "黔南",
|
||
"image": "/assets/guizhou/libo-xiaoqikong.jpg",
|
||
"isHot": true,
|
||
"sortOrder": 0,
|
||
"isActive": true,
|
||
"aliases": [{ "id": "alias-1", "alias": "小七孔" }]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### `POST /api/public/leads`
|
||
|
||
用于首页快速定制、搜索页快速定制、需求页和预订咨询页提交线索。
|
||
|
||
#### 请求字段
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `phone` | `string` | 是 | 联系方式。前台输入文案为“手机号 / 微信号”,后端当前会去除首尾空白并压缩连续空格 |
|
||
| `destination` | `string` | 否 | 目的地或玩法 |
|
||
| `travelDate` | `string` | 否 | 出行日期;MiniAPP 当前传 `YYYY-MM-DD` 字符串 |
|
||
| `peopleCount` | `number` | 否 | 出行人数,需大于 0 |
|
||
| `budgetMin` | `number` | 否 | 最低预算,需大于等于 0 |
|
||
| `budgetMax` | `number` | 否 | 最高预算,需大于等于 0 |
|
||
| `note` | `string` | 否 | 补充说明,后端当前限制最长 1000 字符 |
|
||
| `sourcePage` | `string` | 否 | 来源页面 |
|
||
| `sourceProductId` | `string` | 否 | 来源产品 UUID |
|
||
|
||
#### `sourcePage` 当前取值
|
||
|
||
| 值 | 来源 |
|
||
| --- | --- |
|
||
| `home_inline` | 首页快速定制入口 |
|
||
| `search_inline` | 搜索结果页快速定制入口 |
|
||
| `demand_page` | 提交需求页 |
|
||
| `product_consult` | 产品预订咨询页 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"destination": "荔波小七孔",
|
||
"phone": "187 8617 4929",
|
||
"travelDate": "2027-01-01",
|
||
"peopleCount": 2,
|
||
"note": "咨询线路:黄果树瀑布小包团;方案偏好:经典人文",
|
||
"sourcePage": "product_consult",
|
||
"sourceProductId": "8a6e7c4f-0000-4000-9000-000000000001"
|
||
}
|
||
```
|
||
|
||
#### 成功响应
|
||
|
||
状态码:`201`
|
||
|
||
```json
|
||
{
|
||
"id": "lead-uuid",
|
||
"status": "new"
|
||
}
|
||
```
|
||
|
||
#### 常见异常
|
||
|
||
| 状态码 | 场景 |
|
||
| --- | --- |
|
||
| `422` | 请求体校验失败,例如 `phone` 为空、`peopleCount` 小于等于 0、`note` 超长 |
|
||
| `500` | 服务端异常,响应不得暴露内部细节 |
|
||
|
||
## MiniAPP 当前依赖说明
|
||
|
||
- `site-config` 与 `products` 会在应用启动时并行请求;任一请求失败时,MiniAPP 会回退到本地静态内容。
|
||
- `products.items` 为空时,MiniAPP 会使用本地产品兜底数据。
|
||
- “精选线路”由 `site-config.routeSections` 定义动态分组标题、副文案和商品 ID 顺序,由 `/api/public/products.items` 提供产品详情;客户端不依赖固定分组 ID 或固定三组数量。
|
||
- `routeSections` 缺失、为空或无法匹配到有效商品时,MiniAPP 使用 `src/content.ts` 的本地精选线路内容回退。
|
||
- `ctaBanners` 缺失或为空时,MiniAPP 使用 `src/content.ts` 的本地 `bottomCtas` 内容回退。
|
||
- “特色酒店”和“万趣用车”分别由 `site-config.hotelGroups`、`site-config.vehicleOptions` 提供;特色酒店优先使用 `coverImage`,字段缺失、数组为空或图片为空时使用本地内容兜底。
|
||
- “特价优惠”当前没有独立 Public 结果列表字段;`site-config.campaigns` 只提供活动元信息,活动线路需通过产品标签/关键词筛选或后续扩展活动产品关联字段。
|
||
- 产品搜索当前主要在前端执行,依赖 `title`、`tags`、`destination.name`、`summary`。
|
||
- 产品详情页当前使用已加载的产品列表数据;后续可改为进入详情页时请求 `GET /api/public/products/{product_id}`。
|
||
- 收藏、浏览历史和最近咨询记录由 MiniAPP 本地存储处理,不需要后端接口。
|
||
- 企业微信客服由 MiniAPP 环境变量控制,不属于 `WonderQ-Admin` Public API;文档和接口不得写入真实链接或企业 ID。
|
||
|
||
## 后端验证建议
|
||
|
||
- 为 `GET /health` 增加或保留健康检查测试。
|
||
- 为 `GET /api/public/site-config` 验证返回 JSON 包含 `heroSlides`、`destinations`、`map`、`themes`、`ctaBanners`、`campaigns`、`routeSections`、`hotelGroups`、`vehicleOptions` 数组字段。
|
||
- 为 `GET /api/public/site-config` 验证 `routeSections` 表达“精选线路”子分组;接口返回当前已配置且启用的分组,未配置时返回空数组并由 MiniAPP 本地内容兜底。
|
||
- 为 `GET /api/public/site-config` 验证 `routeSections` 只返回启用分组,且 `productIds` 不包含未发布商品。
|
||
- 为 `GET /api/public/site-config` 验证 `hotelGroups` 只返回 `published` 且启用卡片,包含 `coverImage`、价格和标签字段;验证 `vehicleOptions` 只返回启用卡片,并按 `sortOrder` 升序。
|
||
- 为 `GET /api/public/products` 验证响应结构为 `{ items: [...] }`,并覆盖 `keyword`、`destinationId`、`status`、`take` 参数。
|
||
- 为 `GET /api/public/products/{product_id}` 验证 UUID、数字 `sourceId` 和 404 场景。
|
||
- 为 `GET /api/public/destinations` 验证只返回启用目的地及别名字段。
|
||
- 为 `POST /api/public/leads` 验证成功创建、`phone` 规范化、必填校验、人数/预算边界和备注长度限制。
|