Files
WonderQ-Project/docs/public-api.md
duanshuwen 6296392e80 refactor(home-api): 统一首页API响应格式并迁移配套代码
完成首页数据模型的统一重构,具体变更如下:
1.  重构公共首页API接口,移除`playRecommendations`字段,将玩法推荐数据统一放入`experiences`字段
2.  更新前后端类型定义、序列化逻辑与前端页面组件,适配新的API响应结构
3.  为微信登录相关接口添加`trust_env=True`配置,支持企业代理环境并新增`socksio`依赖
4.  新增图片画廊上传组件,重构SingleImageUploader组件支持自定义宽高比
5.  重构Toast与Select组件的实现与样式,统一后台UI设计系统
6.  移除个人中心页面不必要的返回事件与顶部标题组件,优化详情卡片布局
7.  新增微信接口单元测试,更新官方文档与测试用例适配变更
8.  删除过期的文档图片资源
2026-08-20 21:18:27 +08:00

281 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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[];
};
```
Public 响应只返回启用内容,其余模块按 `isActive` 过滤。
## 玩法展示
### `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` 作为需求页预填关键词;接口失败或响应为空时,玩法页展示对应的错误、重试或空态,不再读取 `playData.ts` 模拟数据。
### 路线详情
`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[];
};
```
不存在、停用或未配置详情返回 `404`。MiniAPP 路线详情页通过 `/pages/detail/index?routeId={key}` 进入;接口失败或字段不完整时按路线 ID 使用本地网络图片和模拟文案。该详情页暂不提供价格、收藏、在线订阅、预订、订单或管家联系动作。
## 管家展示
### `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" }
}
```