# 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[]; vehicleOptions: VehicleOption[]; }; ``` Public 响应只返回 `heroSlides` 和 `vehicleOptions` 的启用内容,其余旧首页配置模块不再返回。首页玩法推荐、团队共创和极境视界由 `/api/public/home` 及其详情接口提供;用车需求说明使用 MiniAPP 本地文案,实际提交走 `/api/public/leads`,不再依赖旧的 `vehicleService` 配置。 ### 用车需求提交 `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。 图片字段(如 `image`、`gallery`、`avatar`、`qrImage`)始终返回可直接请求的 HTTP(S) URL。OSS 配置为私有读时,WonderQ-Admin 会在响应中生成短时 GET 签名 URL;客户端应直接使用返回值,不应持久化或自行修改签名参数。 旧需求页主视觉、特色卡片和需求表单已移除;需求页面只通过 `POST /api/public/leads` 提交实时线索,不再读取已删除的站点配置表。 ## 登录接口 ### `POST /api/public/auth/phone-login` 请求: ```json { "code": "wechat-phone-code" } ``` 成功响应: ```json { "code": 200, "msg": "success", "data": { "token": "", "customer": { "id": "customer-id", "phoneMasked": "138****0000" } } } ``` ### `GET /api/public/auth/me` 请求头:`Authorization: Bearer `。 成功响应: ```json { "code": 200, "msg": "success", "data": { "id": "customer-id", "phoneMasked": "138****0000" } } ```