- 新增后台登录图形验证码功能,完善登录安全防护 - 新增登录rememberMe参数,控制Refresh Token的会话持久化策略 - 实现OSS私有桶媒体URL自动签名,统一处理图片资源的临时访问签名 - 新增素材库数据库表与上传API,规范媒体资源管理流程 - 统一前端UI图标使用@element-plus/icons-vue,重构布局图标组件 - 登录页新增验证码输入、刷新功能,添加账号记忆与记住密码逻辑 - 更新全套文档,补充API契约、技术决策记录与集成流程说明 - 修复多个业务页面的图标展示问题,新增认证流程相关测试用例
9.5 KiB
WonderQ MiniAPP Public API
本文档是 WonderQ-MiniAPP 当前使用的 Public API 契约。接口提供站点内容、首页卡片、玩法展示、管家展示、登录和出行需求提交能力。
基础约定
- API 前缀:
/api/public。 - 响应使用 JSON;时间使用 ISO 8601 字符串。
- 所有
/health和/api/public/**JSON 响应遵循 三端统一 API 响应契约,成功业务对象位于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 返回以下稳定字段:
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,客户端不得传入该字段。
{
"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 玩法页所需的最小展示字段,不返回后台排序、审计和时间字段。
成功响应:
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。成功响应只返回详情展示字段:
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、启停状态、排序元数据和时间字段。
type PublicConciergeDetail = {
icon: string;
label: string;
};
type PublicConciergeAdvisor = {
avatar: string;
name: string;
role: string;
details: PublicConciergeDetail[];
qrImage: string;
};
type PublicConciergeResponse = {
advisors: PublicConciergeAdvisor[];
};
成功响应示例:
{
"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。
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
请求:
{ "code": "wechat-phone-code" }
成功响应:
{
"code": 200,
"msg": "success",
"data": {
"token": "<customer-jwt>",
"customer": { "id": "customer-id", "phoneMasked": "138****0000" }
}
}
GET /api/public/auth/me
请求头:Authorization: Bearer <customer-jwt>。
成功响应:
{
"code": 200,
"msg": "success",
"data": { "id": "customer-id", "phoneMasked": "138****0000" }
}