删除home-api.md、team-building-api.md等废弃文档 统一替换所有文档中的`WonderQ-Admin-UI`为`WonderQ-Admin-UI-Vue` 更新README.md与联调文档的内容与路径 修正各API文档的过时描述,移除废弃的迁移说明与本地mock依赖 整理docs/README.md的文档索引,优化阅读路径
328 lines
10 KiB
Markdown
328 lines
10 KiB
Markdown
# WonderQ MiniAPP Public API
|
||
|
||
本文档是 `WonderQ-MiniAPP` 当前使用的 Public API 契约。接口提供站点内容、首页卡片、玩法展示、管家展示、登录和出行需求提交能力。
|
||
|
||
## 基础约定
|
||
|
||
- API 前缀:`/api/public`。
|
||
- 响应使用 JSON;时间使用 ISO 8601 字符串。
|
||
- 所有 `/health` 和 `/api/public/**` JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。
|
||
- 所有返回的持久化资源 `id` 都是稳定 UUID 字符串,不是标题、分类名或本地 mock 使用的语义 ID;详情、列表和跳转必须复用同一个 ID。
|
||
- H5 本地开发通过 `/api` 代理访问后端。
|
||
- 内容接口失败时,MiniAPP 使用 `src/content.ts` 的本地兜底内容。
|
||
|
||
## 接口清单
|
||
|
||
| 方法 | 路径 | 鉴权 | 用途 |
|
||
| ------ | ------------------------------ | ------------ | ------------------ |
|
||
| `GET` | `/health` | 否 | 服务健康检查 |
|
||
| `GET` | `/api/public/site-config` | 否 | 获取启用的站点内容 |
|
||
| `GET` | `/api/public/home` | 否 | 获取已启用的首页内容和玩法推荐 |
|
||
| `GET` | `/api/public/home/team-buildings/{teamBuildingId}` | 否 | 获取团队共创详情 |
|
||
| `GET` | `/api/public/home/wild-archives` | 否 | 获取客片案例更多列表 |
|
||
| `GET` | `/api/public/home/wild-archives/{archiveId}` | 否 | 获取客片案例详情和图片 |
|
||
| `GET` | `/api/public/wanfa/categories` | 否 | 获取玩法分类和路线 |
|
||
| `GET` | `/api/public/details/{key}` | 否 | 获取玩法路线详情 |
|
||
| `GET` | `/api/public/concierge/advisors` | 否 | 获取已启用的管家顾问 |
|
||
| `POST` | `/api/public/auth/phone-login` | 否 | 微信手机号登录 |
|
||
| `GET` | `/api/public/auth/me` | Customer JWT | 获取当前客户 |
|
||
| `POST` | `/api/public/leads` | 否 | 提交出行需求 |
|
||
|
||
## 站点配置
|
||
|
||
`GET /api/public/site-config` 返回以下稳定字段:
|
||
|
||
```ts
|
||
type SiteConfig = {
|
||
heroSlides: HeroSlide[];
|
||
destinationHero: DestinationHero[];
|
||
vehicleOptions: VehicleOption[];
|
||
demandHero: DemandHero[];
|
||
demandFeatureCards: DemandFeatureCard[];
|
||
demandForm: DemandForm[];
|
||
vehicleService: VehicleService | null;
|
||
};
|
||
|
||
type VehicleService = {
|
||
id: string;
|
||
introTitle: string;
|
||
intro: string;
|
||
serviceSections: Array<{ title: string; description: string }>;
|
||
advantages: string[];
|
||
processSteps: Array<{ title: string; description: string }>;
|
||
};
|
||
```
|
||
|
||
Public 响应只返回启用内容,其余模块按 `isActive` 过滤。
|
||
|
||
`vehicleService` 是用车需求页的启用单例配置;未配置或停用时返回 `null`,MiniAPP 使用本地安全文案兜底,不代表实时库存、价格或订单承诺。
|
||
|
||
### 用车需求提交
|
||
|
||
`POST /api/public/leads` 的通用线索仍允许游客提交;`leadType=vehicle` 必须携带客户 JWT。服务端从 JWT 写入 `customerId`,客户端不得传入该字段。
|
||
|
||
```json
|
||
{
|
||
"leadType": "vehicle",
|
||
"contactName": "联系人",
|
||
"phone": "13800000000",
|
||
"sourcePage": "home-vehicle",
|
||
"vehicleDemand": {
|
||
"serviceType": "charter",
|
||
"charterDuration": "fullDay",
|
||
"travelDate": "2026-08-25",
|
||
"pickupTime": "09:00",
|
||
"pickupLocation": "贵阳北站",
|
||
"dropoffLocation": "黄果树景区",
|
||
"peopleCount": 5,
|
||
"luggageCount": 3,
|
||
"vehicleOptionId": "vehicle-option-uuid",
|
||
"vehicleOptionTitle": "多人商务车",
|
||
"specialRequirements": "需要儿童座椅"
|
||
}
|
||
}
|
||
```
|
||
|
||
服务端从 `vehicleDemand` 归一化 `destination`、`travelDate` 和 `peopleCount` 摘要字段。未登录、参数不完整或服务异常分别返回 `401`、`422`、`500`,失败时 `data` 为 `null`。
|
||
|
||
## 玩法展示
|
||
|
||
### `GET /api/public/wanfa/categories`
|
||
|
||
无需鉴权。接口按后台维护的分类和路线顺序返回 MiniAPP 玩法页所需的最小展示字段,不返回后台排序、审计和时间字段。
|
||
|
||
成功响应:
|
||
|
||
```ts
|
||
type PublicWanfaResponse = {
|
||
categories: PublicWanfaCategory[];
|
||
};
|
||
|
||
type PublicWanfaCategory = {
|
||
id: string;
|
||
label: string;
|
||
routes: PublicWanfaRoute[];
|
||
};
|
||
|
||
type PublicWanfaRoute = {
|
||
id: string;
|
||
title: string;
|
||
subtitle: string;
|
||
image: string;
|
||
routeCount: number;
|
||
demandKeyword: string;
|
||
};
|
||
```
|
||
|
||
MiniAPP 使用 `demandKeyword` 作为需求页预填关键词;接口失败或响应为空时,玩法页展示对应的错误、重试或空态。
|
||
|
||
### 路线详情
|
||
|
||
`GET /api/public/details/{key}` 无需鉴权,`key` 使用玩法路线 ID,例如 `family-water`。成功响应只返回详情展示字段:
|
||
|
||
```ts
|
||
type PublicDetail = {
|
||
key: string;
|
||
eyebrow: string;
|
||
duration: string;
|
||
title: string;
|
||
subtitle: string;
|
||
intro: string;
|
||
highlights: string[];
|
||
included: string[];
|
||
excluded: string[];
|
||
notes: string[];
|
||
gallery: string[];
|
||
conciergeAdvisor: {
|
||
avatar: string;
|
||
name: string;
|
||
role: string;
|
||
details: Array<{ icon: string; label: string }>;
|
||
qrImage: string;
|
||
} | null;
|
||
};
|
||
```
|
||
|
||
不存在、停用或未配置详情返回 `404`。关联管家未配置、已删除或已停用时 `conciergeAdvisor` 为 `null`。MiniAPP 路线详情页通过 `/pages/detail/index?routeId={key}` 进入;接口失败或字段不完整时按路线 ID 使用本地网络图片和模拟文案,fallback 不伪造管家数据。仅当 `conciergeAdvisor` 有效时展示联系管家入口。
|
||
|
||
## 管家展示
|
||
|
||
### `GET /api/public/concierge/advisors`
|
||
|
||
无需鉴权。接口只返回后台启用的管家顾问,按后台维护顺序返回;不返回管理端 ID、启停状态、排序元数据和时间字段。
|
||
|
||
```ts
|
||
type PublicConciergeDetail = {
|
||
icon: string;
|
||
label: string;
|
||
};
|
||
|
||
type PublicConciergeAdvisor = {
|
||
avatar: string;
|
||
name: string;
|
||
role: string;
|
||
details: PublicConciergeDetail[];
|
||
qrImage: string;
|
||
};
|
||
|
||
type PublicConciergeResponse = {
|
||
advisors: PublicConciergeAdvisor[];
|
||
};
|
||
```
|
||
|
||
成功响应示例:
|
||
|
||
```json
|
||
{
|
||
"advisors": [
|
||
{
|
||
"avatar": "https://example.test/assets/advisor-avatar.jpg",
|
||
"name": "示例顾问",
|
||
"role": "SENIOR TRAVEL ADVISOR",
|
||
"details": [{ "icon": "calendar", "label": "服务经验:8年" }],
|
||
"qrImage": "https://example.test/assets/advisor-qr.png"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
无可用顾问时返回 `data: { "advisors": [] }`。MiniAPP 应处理 loading、错误、重试和空态,不能依赖固定顾问姓名或本地模拟数组。
|
||
|
||
## 首页内容
|
||
|
||
### `GET /api/public/home`
|
||
|
||
无需鉴权。接口只返回后台启用的首页玩法推荐、团队共创和极境视界内容,按后台排序返回,不暴露管理元数据。为统一前台消费模型,玩法推荐列表放在 `experiences` 字段,响应中不再返回 `playRecommendations`。
|
||
|
||
```ts
|
||
type PublicHomeResponse = {
|
||
experiences: HomeWanfaRecommendation[];
|
||
teamBuildings: HomeTeamBuilding[];
|
||
wildArchives: HomeWildArchive[];
|
||
};
|
||
|
||
type HomeWanfaRecommendation = {
|
||
id: string;
|
||
categoryId: string;
|
||
label: string;
|
||
routes: WanfaRoute[];
|
||
};
|
||
|
||
type WanfaRoute = {
|
||
id: string;
|
||
title: string;
|
||
subtitle: string;
|
||
image: string;
|
||
routeCount: number;
|
||
demandKeyword: string;
|
||
};
|
||
|
||
type HomeTeamBuilding = {
|
||
id: string;
|
||
tag: string;
|
||
title: string;
|
||
description: string;
|
||
image: string;
|
||
demandKeyword: string;
|
||
};
|
||
|
||
type PublicHomeTeamBuildingDetail = HomeTeamBuilding & {
|
||
detailSubtitle: string;
|
||
detailParagraphs: string[];
|
||
};
|
||
|
||
type HomeWildArchive = {
|
||
id: string;
|
||
title: string;
|
||
image: string;
|
||
demandKeyword: string;
|
||
photoCount: number;
|
||
};
|
||
```
|
||
|
||
无可用内容时,`experiences`、`teamBuildings` 和 `wildArchives` 均返回空数组。`experiences` 实际承载首页玩法推荐,由首页内容域关联玩法分类后生成,接口只返回启用的关联及分类当前路线;MiniAPP 接口失败或字段不完整时使用对应空态,不再读取 `playRecommendations`。
|
||
|
||
客片案例更多列表使用 `GET /api/public/home/wild-archives`,返回同样的摘要字段;详情使用 `GET /api/public/home/wild-archives/{archiveId}`,在摘要字段基础上增加 `images: string[]`。首页卡片点击详情,`查看更多` 点击案例列表,不再跳转需求页。
|
||
|
||
### 团队共创详情
|
||
|
||
`GET /api/public/home/team-buildings/{teamBuildingId}` 无需鉴权,只返回启用的团队共创详情。响应包含首页摘要字段,以及 `detailSubtitle` 和 `detailParagraphs`。不存在或已停用返回 `404`。
|
||
|
||
详情页接口失败时,MiniAPP 按 ID 使用 `homeTeamBuildingData.ts` 中的网络图片和模拟正文 fallback,并显示接口不可用提示;首页 `/api/public/home` 不返回 `detailParagraphs`,避免首页请求携带长正文。
|
||
|
||
### 通用字段
|
||
|
||
站点模块通常包含 `id`、`createdAt`、`updatedAt`、`isActive` 和 `sortOrder`。客户端按 `sortOrder` 消费排序模块,不依赖固定 ID。
|
||
|
||
### 需求配置
|
||
|
||
```ts
|
||
type DemandHero = {
|
||
id: string;
|
||
title: string;
|
||
kicker: string | null;
|
||
description: string | null;
|
||
steps: string[];
|
||
isActive: boolean;
|
||
sortOrder: number;
|
||
};
|
||
|
||
type DemandFeatureCard = {
|
||
id: string;
|
||
title: string;
|
||
description: string | null;
|
||
isActive: boolean;
|
||
sortOrder: number;
|
||
};
|
||
|
||
type DemandForm = {
|
||
id: string;
|
||
destinationLabel: string;
|
||
destinationPlaceholder: string | null;
|
||
phoneLabel: string;
|
||
phonePlaceholder: string | null;
|
||
noteLabel: string;
|
||
notePlaceholder: string | null;
|
||
submitLabel: string;
|
||
chips: string[];
|
||
isActive: boolean;
|
||
};
|
||
```
|
||
|
||
## 登录接口
|
||
|
||
### `POST /api/public/auth/phone-login`
|
||
|
||
请求:
|
||
|
||
```json
|
||
{ "code": "wechat-phone-code" }
|
||
```
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"token": "<customer-jwt>",
|
||
"customer": { "id": "customer-id", "phoneMasked": "138****0000" }
|
||
}
|
||
}
|
||
```
|
||
|
||
### `GET /api/public/auth/me`
|
||
|
||
请求头:`Authorization: Bearer <customer-jwt>`。
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": { "id": "customer-id", "phoneMasked": "138****0000" }
|
||
}
|
||
```
|