chore: delete unused binaries and project documentation

Remove leftover Sogou Input Picface Cloud binary files and their backup copies, as well as all project documentation files including AGENTS.md and the entire docs/ directory.
This commit is contained in:
duanshuwen
2026-07-04 10:50:45 +08:00
parent 1aac3c64b2
commit 2a3615901c
8 changed files with 0 additions and 1357 deletions

114
AGENTS.md
View File

@@ -1,114 +0,0 @@
# AGENTS.md
## 全局协作规则
- 默认全程使用中文回答;除非用户明确要求,不切换语言。
- 回答保持简洁直接,避免无效铺垫和空话。
- 需求不清晰时先提问确认;不要自行猜测并执行有风险操作。
- 默认只读优先。创建、修改、删除文件必须有用户明确授权。
- 严格按当前需求工作,不额外加功能、不扩大改动范围。
- 识别到密钥、Token、真实环境变量、账号密码、隐私配置时禁止展示、复述或输出。
- 不读取、输出或提交 `.env``.env.local` 等真实环境文件。
## 项目定位
`WonderQ-Admin` 是独立的 WonderQ 后端 API 服务,当前技术栈为 Python + FastAPI + SQLAlchemy 2 + Alembic + PostgreSQL + JWT + Pydantic。项目为 H5 前台提供 Public API为后台管理端提供 Admin API并通过 Docker Compose 部署 API、PostgreSQL 和 Redis。
## 当前目录结构
```text
WonderQ-Admin/
├─ app/
│ ├─ main.py # FastAPI 应用入口、CORS、错误处理、路由注册
│ ├─ config.py # 环境变量配置
│ ├─ database.py # SQLAlchemy engine/session/Base
│ ├─ models.py # ORM 模型,兼容原 Prisma 表结构
│ ├─ schemas.py # Pydantic 请求校验
│ ├─ auth.py # JWT 与后台鉴权
│ ├─ serializers.py # SQLAlchemy 对象响应序列化
│ ├─ content.py # Python seed 内容源
│ ├─ seed.py # 初始化/重置数据命令
│ └─ routers/
│ ├─ public.py # H5 Public API
│ ├─ admin.py # 后台 Admin API
│ └─ shared.py # 路由共享查询
├─ alembic/ # 数据库迁移 baseline
├─ data/generated-products.json
├─ tests/ # 单元测试和接口冒烟测试
├─ Dockerfile # API 镜像
├─ docker-compose.yml # api/postgres/redis 编排
├─ requirements.txt # Python 依赖
├─ pyproject.toml # Python 项目元数据与 pytest 配置
├─ .env.example # 环境变量模板
└─ README.md # 启动、部署、迁移说明
```
## 启动方式
本地开发:
```bash
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
docker compose up -d postgres redis
alembic upgrade head
python -m app.seed
uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload
```
手动重启 4001 端口服务:先停止当前监听进程及其 worker再手动运行 API。
```powershell
$port = 4001
$listenerPids = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique
foreach ($listenerPid in $listenerPids) {
Get-CimInstance Win32_Process |
Where-Object { $_.ParentProcessId -eq $listenerPid } |
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
Stop-Process -Id $listenerPid -Force -ErrorAction SilentlyContinue
}
uvicorn app.main:app --host 0.0.0.0 --port 4001 --reload
```
Docker 全量启动:
```bash
docker compose up --build
```
健康检查:`http://localhost:4000/health`
## 测试流程
- 修改 Python 代码后运行 `pytest`
- 修改数据库模型或迁移后运行 `alembic upgrade head`,并在空库验证 `python -m app.seed`
- 保留已有 PostgreSQL 数据时,先备份,再使用 `alembic stamp head` 标记 baseline。
- Docker 相关变更后运行 `docker compose up --build` 并检查 `/health`
## 开发准则
- 优先保持现有 API 路径和响应结构兼容,不主动重设计接口。
- 外部输入必须通过 Pydantic schema 校验。
- 数据库访问统一通过 `app/database.py` 提供的 Session。
- 表名和字段名需要兼容原 Prisma 生成的 mixed-case PostgreSQL 结构。
- Admin API 默认需要 `require_admin`,登录接口除外。
- 后台数据变更继续记录 `AuditLog`
- 真实密钥只从环境变量读取,禁止写入源码、测试或文档。
- `python -m app.seed` 会重置内容数据,生产环境使用前必须明确确认。
## 锁定核心文件
未经用户明确授权禁止修改:
- `.env``.env.local`、生产环境变量和任何密钥配置。
- `app/models.py``alembic/versions/*`:数据库结构和迁移。
- `app/auth.py`:后台鉴权逻辑。
- `app/routers/admin.py``app/routers/public.py`:核心 API 行为。
- `app/seed.py``app/content.py``data/generated-products.json`:初始化内容和迁移数据源。
- `Dockerfile``docker-compose.yml`:部署入口。
- `requirements.txt``pyproject.toml`:依赖和测试配置。
如确需修改上述文件,先说明原因、影响范围、验证方式,并等待用户确认。

View File

@@ -1,10 +0,0 @@
# WonderQ-Admin 文档
本目录存放后端 API 相关文档。
- `backend/README.md`:当前 Python + FastAPI 后端运行说明。
- `admin-ui-api-requirements.md`:后台管理前端当前必需的 Admin API 对接需求与字段契约。
- `miniapp-public-api.md`WonderQ-MiniAPP 前台对接 Public API 契约。
- `admin-backend-plan.md`:早期后台建设规划,保留作业务范围和阶段规划参考;其中技术栈建议已被当前 Python 迁移方案取代。
后台管理前端文档位于 `D:\www\znkj\WonderQ-Admin-UI\docs`

View File

@@ -1,36 +0,0 @@
# WonderQ-Admin 后端规划摘要
## 当前定位
`WonderQ-Admin` 是 WonderQ 的独立后端 API 服务,当前技术栈已调整为 Python + FastAPI + SQLAlchemy 2 + Alembic + PostgreSQL通过 Docker Compose 部署 API、PostgreSQL 和 Redis。
## 核心目标
- 为 H5 前台提供稳定的 Public API。
- 为后台管理端提供 JWT 鉴权的 Admin API。
- 保留现有 PostgreSQL 数据和主要接口路径,降低前端联调成本。
- 使用 Alembic 管理后续数据库迁移,已有数据库通过 `alembic stamp head` 接入 baseline。
## 当前业务模块
- 首页配置:轮播、目的地、主题卡片、底部 CTA、发布版本。
- 线路产品:列表、详情、图片、详情区块、状态和排序。
- 目的地:目的地基础信息、别名、热门状态。
- 活动专题:活动和产品关联。
- 线索:前台提交、后台列表、状态流转。
- 媒体:媒体资源登记。
- 审计:后台关键变更写入 `AuditLog`
## 近期优先级
1. 保持 Public/Admin API 与现有前端调用兼容。
2. 补充更多 PostgreSQL 集成测试覆盖迁移、seed 和核心接口。
3. 建立生产迁移流程:备份、`alembic stamp head`、后续增量迁移。
4. 按实际业务继续扩展权限、订单、客户和消息模块。
## 安全与部署原则
- 生产环境必须替换 `JWT_SECRET`,禁止使用示例值。
- 不提交 `.env`、日志、数据库备份和任何真实密钥。
- `python -m app.seed` 会重置内容数据,生产环境执行前必须明确确认。
- Docker Compose 适合本地和单机部署;生产可按相同环境变量拆分到托管数据库或容器平台。

View File

@@ -1,651 +0,0 @@
# WonderQ-Admin-UI Admin API 接口需求
本文档用于指导 `WonderQ-Admin` 后端按当前 `WonderQ-Admin-UI` 管理端完成 Admin API 对接。接口需求来源于前端 `src/api.ts``src/App.tsx` 的实际类型、请求封装和页面调用。
## 范围
- 本文只覆盖当前后台管理前端必需的 Admin API。
- 不包含 H5 Public API、订单、媒体库、审计日志、活动专题等后续规划能力。
- 后端现有 `GET /api/admin/me``GET /api/admin/media-assets` 不在当前 UI 必需范围内。
## 通用约定
- 基础路径:`/api/admin`
- 请求与响应均使用 JSON。
-`POST /api/admin/auth/login` 外,其余接口都需要后台登录态。
- 登录成功后前端会把返回的 `token` 存入本地,并在后续请求头中发送:
```http
Authorization: Bearer <token>
Content-Type: application/json
```
- 日期时间字段返回 ISO 8601 字符串。
- ID 字段按字符串处理。
- 前端错误提示优先读取响应体的 `message` 字段;如后端沿用 FastAPI 默认 `detail`,建议同时兼容输出 `message`,避免管理端展示兜底错误。
## 当前对接状态
| 接口 | 前端依赖 | 后端现状 | 备注 |
| --- | --- | --- | --- |
| `POST /api/admin/auth/login` | 登录页 | 已覆盖 | 返回 token 和 user |
| `GET /api/admin/dashboard` | 客户端已封装 | 已覆盖 | 当前 UI 暂未展示 |
| `GET /api/admin/products` | 商品维护、结构维护 | 已覆盖 | UI 当前只传 `keyword` |
| `POST /api/admin/products` | 新建商品 | 已覆盖 | 返回完整 Product |
| `PATCH /api/admin/products/{id}` | 编辑商品 | 已覆盖 | 返回完整 Product |
| `GET /api/admin/destinations` | 商品目的地下拉、目的地页 | 已覆盖 | 需要返回别名和商品数 |
| `GET /api/admin/site-config` | 首页/目的地/活动结构维护 | 已覆盖 | 需要包含未启用内容、已保存的 `routeSections``hotelGroups``vehicleOptions``ctaBanners` |
| `PATCH /api/admin/site-config/{module}/{item_id}` | 模块内容编辑 | 已覆盖 | 模块名需保持一致 |
| `GET /api/admin/leads` | 需求线索页 | 已覆盖 | UI 当前不传筛选参数 |
| `PATCH /api/admin/leads/{id}/status` | 线索状态流转 | 已覆盖 | UI 更新后会重新拉列表 |
| `POST /api/admin/publish` | 结构维护发布 | 已覆盖 | UI 使用 `title` 提示发布结果 |
| `POST /api/admin/reset-guizhou-content` | 贵州内容重置 | 已覆盖 | 高风险操作,需鉴权和审计 |
## 枚举
### ProductStatus
```ts
type ProductStatus = "draft" | "published" | "archived";
```
### LeadStatus
```ts
type LeadStatus = "new" | "assigned" | "contacted" | "planning" | "won" | "invalid";
```
### SiteModule
```ts
type SiteModule = "heroSlides" | "destinations" | "map" | "themes" | "campaigns" | "routeSections" | "hotelGroups" | "vehicleOptions" | "ctaBanners";
```
## 公共数据结构
### Destination
```ts
type Destination = {
id: string;
name: string;
slug: string;
region?: string | null;
image?: string | null;
isHot: boolean;
isActive: boolean;
sortOrder: number;
aliases?: Array<{ id: string; alias: string }>;
_count?: { products: number };
};
```
### Product
```ts
type Product = {
id: string;
sourceId?: number | null;
title: string;
subtitle?: string | null;
priceAmount?: number | null;
priceUnit: string;
tags: string[];
coverImage?: string | null;
summary?: string | null;
images?: Array<{ id?: string; url: string; alt?: string | null; sortOrder: number }>;
detailSections?: ProductDetailSection[] | null;
status: ProductStatus;
sortWeight: number;
updatedAt: string;
destination?: Destination | null;
destinationId?: string | null;
};
```
### ProductDetailSection
```ts
type ProductDetailBlock =
| { type: "text"; text: string }
| { type: "image"; url: string; alt?: string | null };
type ProductDetailSection = {
key: string;
label: string;
title?: string | null;
blocks: ProductDetailBlock[];
};
```
### ProductInput
`POST /products``PATCH /products/{id}` 复用该结构;`PATCH` 可以只提交需要修改的字段。
```ts
type ProductInput = {
title: string;
subtitle?: string;
destinationId?: string | null;
priceAmount?: number | null;
priceUnit?: string;
tags: string[];
coverImage?: string | null;
summary?: string | null;
images?: Array<{ url: string; alt?: string | null; sortOrder: number }>;
detailSections?: ProductDetailSection[];
status: ProductStatus;
sortWeight: number;
};
```
字段处理要求:
- `title` 必填,后端至少应校验非空;当前后端 schema 为最少 2 个字符。
- `priceAmount` 可为空;不为空时应为大于等于 0 的整数。
- `priceUnit` 为空时后端默认使用 `起/人`
- `images` 保存前按数组顺序重排 `sortOrder`
- `detailSections` 中空 key、空 label、空 blocks 的模块不应保存为有效详情模块。
-`status` 首次变为 `published` 时,后端可写入发布时间。
### Lead
```ts
type Lead = {
id: string;
destination?: string | null;
phone: string;
note?: string | null;
sourcePage?: string | null;
status: LeadStatus;
createdAt: string;
sourceProduct?: { id: string; title: string } | null;
assignedUser?: { id: string; name: string } | null;
};
```
后端可额外返回 `travelDate``peopleCount``budgetMin``budgetMax` 等字段,但以上字段是当前管理端展示所需的最小集合。手机号属于隐私信息,日志、错误和文档示例中不得输出真实号码。
### SiteConfig
```ts
type SiteConfig = {
heroSlides: Array<{
id: string;
title: string;
kicker?: string | null;
image: string;
targetType?: string | null;
targetValue?: string | null;
isActive: boolean;
}>;
destinations: Destination[];
themes: Array<{
id: string;
label: string;
image: string;
targetType?: string | null;
targetValue?: string | null;
isActive: boolean;
}>;
campaigns: Array<{
id: string;
slug: string;
title: string;
description?: string | null;
coverImage?: string | null;
priceAmount?: number | null;
priceUnit?: string | null;
tags: string[];
status: "draft" | "published";
startsAt?: string | null;
endsAt?: string | null;
}>;
routeSections: Array<{
id: string;
title: string;
subtitle?: string | null;
productIds: string[];
isActive: boolean;
sortOrder: number;
}>;
hotelGroups: Array<{
id: string;
title: string;
description?: string | null;
image?: string | null;
coverImage?: string | null;
priceAmount?: number | null;
priceUnit?: string | null;
tags?: string[];
status: "draft" | "published";
isActive: boolean;
sortOrder: number;
createdAt?: string;
updatedAt?: string;
}>;
vehicleOptions: Array<{
id: string;
title: string;
description?: string | null;
image?: string | null;
isActive: boolean;
sortOrder: number;
createdAt?: string;
updatedAt?: string;
}>;
ctaBanners: Array<{
id: string;
alt: string;
image: string;
targetType: string;
targetValue?: string | null;
isActive: boolean;
sortOrder: number;
}>;
};
```
### SiteItemPatch
```ts
type SiteItemPatch = {
title?: string;
subtitle?: string | null;
kicker?: string;
name?: string;
slug?: string;
region?: string | null;
label?: string;
alt?: string;
image?: string | null;
description?: string | null;
coverImage?: string | null;
priceAmount?: number | null;
priceUnit?: string | null;
tags?: string[];
targetType?: string | null;
targetValue?: string | null;
isHot?: boolean;
isActive?: boolean;
sortOrder?: number;
productIds?: string[];
status?: "draft" | "published";
startsAt?: string | null;
endsAt?: string | null;
};
```
模块字段映射:
| module | 可编辑字段 |
| --- | --- |
| `heroSlides` | `title``kicker``image``targetType``targetValue``isActive` |
| `destinations` | `name``image``isActive` |
| `themes` | `label``image``targetType``targetValue``isActive` |
| `campaigns` | `title``description``coverImage``priceAmount``priceUnit``tags``status` |
| `routeSections` | `title``subtitle``productIds``isActive``sortOrder` |
| `hotelGroups` | `title``description``image``coverImage``priceAmount``priceUnit``tags``status``isActive``sortOrder` |
| `vehicleOptions` | `title``description``image``isActive``sortOrder` |
| `ctaBanners` | `alt`(服务标题)、`image``targetType``targetValue``isActive``sortOrder` |
## 接口明细
### 登录
```http
POST /api/admin/auth/login
```
请求体:
```json
{
"email": "admin@example.com",
"password": "example-password"
}
```
响应体:
```ts
{
token: string;
user: {
id?: string;
name: string;
email: string;
role: string;
};
}
```
状态码要求:
- `200`:登录成功。
- `401`:账号不存在、密码错误或账号停用。
### 工作台统计
```http
GET /api/admin/dashboard
```
当前前端客户端已封装该接口,但页面暂未展示。后端保持兼容即可。
响应体:
```ts
{
stats: {
productCount: number;
publishedProductCount: number;
destinationCount: number;
newLeadCount: number;
leadCount: number;
campaignCount: number;
};
recentLeads: Lead[];
}
```
### 商品列表
```http
GET /api/admin/products?keyword=<keyword>
```
查询参数:
| 参数 | 类型 | 当前 UI 是否使用 | 说明 |
| --- | --- | --- | --- |
| `keyword` | `string` | 是 | 搜索商品标题、短标题或标签 |
| `status` | `ProductStatus` | 否 | 后端可支持状态筛选 |
| `take` | `number` | 否 | 后端当前可限制返回条数 |
响应体:
```ts
{
items: Product[];
}
```
排序建议:`sortWeight` 升序,再按 `updatedAt` 倒序。结构维护页和商品维护页都会读取该接口。
### 新建商品
```http
POST /api/admin/products
```
请求体:`ProductInput`
响应体:`Product`
状态码要求:
- `201`:创建成功。
- `422`:字段校验失败。
### 更新商品
```http
PATCH /api/admin/products/{id}
```
请求体:`Partial<ProductInput>`
响应体:`Product`
状态码要求:
- `200`:更新成功。
- `404`:商品不存在。
- `422`:字段校验失败。
当前 UI 保存商品后会使用响应体刷新编辑状态,因此后端需要返回完整 Product而不是只返回成功标记。
### 目的地列表
```http
GET /api/admin/destinations
```
响应体:
```ts
{
items: Destination[];
}
```
要求:
- 返回所有目的地,包括未启用项,便于后台维护。
-`sortOrder` 升序。
- 每个目的地包含 `aliases`
- 每个目的地建议包含 `_count.products`,用于后台判断关联商品数量。
### 站点配置
```http
GET /api/admin/site-config
```
响应体:`SiteConfig`
要求:
- 返回 `heroSlides``destinations``map``themes``campaigns``routeSections``hotelGroups``vehicleOptions``ctaBanners` 九个模块。
- Admin API 需要返回未启用内容Public API 才按发布/启用状态过滤。
- 各模块按 `sortOrder` 升序。
- `routeSections` 返回当前已保存的子分组,包含未启用分组和后台配置的全部 `productIds`;无数据时返回空数组。`hotelGroups``vehicleOptions` 返回全部后台卡片,包含停用项;无数据时返回空数组。
### 更新站点配置项
```http
PATCH /api/admin/site-config/{module}/{item_id}
```
路径参数:
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `module` | `SiteModule` | 只能为 `heroSlides``destinations``map``themes``campaigns``routeSections``hotelGroups``vehicleOptions``ctaBanners` |
| `item_id` | `string` | 对应模块内容项 ID |
请求体:`SiteItemPatch`
响应体:更新后的内容项对象。
状态码要求:
- `200`:更新成功。
- `400`:模块不存在。
- `404`:内容项不存在。
- `422`:字段校验失败。
当前 UI 保存后会重新调用 `GET /api/admin/site-config` 刷新页面,响应体只需保证是合法 JSON。
#### 精选线路 `routeSections`
`routeSections` 是首页“精选线路”的动态运营分组配置,不再限制为固定三组。管理端通过新增、更新、删除和排序接口维护闭环;商品本体仍由 `/api/admin/products` 维护,这里只保存首页分组、标题、副文案、启用状态、排序和关联商品 ID 顺序。
```http
POST /api/admin/site-config/routeSections
PATCH /api/admin/site-config/routeSections/{section_id}
DELETE /api/admin/site-config/routeSections/{section_id}
PATCH /api/admin/site-config/routeSections/reorder
```
新增请求至少包含 `title`,可包含 `subtitle``isActive``sortOrder``productIds``id` 由后端生成,不再使用 `routes` / `routes-outdoor` / `routes-mix` 固定槽位。更新请求可包含 `title``subtitle``isActive``sortOrder``productIds``productIds` 表示该分组关联的线路商品及展示顺序。
后端约束:
- `GET /api/admin/site-config` 返回全部后台分组,包含停用分组和后台配置的全部 `productIds`
- `productIds` 中的商品必须存在,且同一请求内不能重复。
- 同一商品不能同时出现在多个精选线路分组;冲突时返回 `409 ROUTE_SECTION_PRODUCT_CONFLICT`
- `DELETE /api/admin/site-config/routeSections/{section_id}` 只删除分组配置并解除关联,不删除商品本体;删除后后端重新整理剩余分组 `sortOrder`
- `PATCH /api/admin/site-config/routeSections/reorder``itemIds` 必须完整覆盖当前全部分组 ID不能缺失、重复或包含未知 ID。
- `GET /api/public/site-config` 只返回启用分组,且 `productIds` 只包含已发布商品;未发布、归档或不存在的商品不进入 Public 响应。
#### 特色酒店 `hotelGroups`
`hotelGroups` 对应首页“特色酒店”模块。该模块已按“特价优惠”的数据配置方式调整,运营可以新增、编辑、删除、排序酒店卡片,并维护标题、描述、价格、标签、封面图和前台展示状态。它只维护首页酒店卡片,不绑定商品本体,也不读取线路商品关联。
```http
POST /api/admin/site-config/hotelGroups
PATCH /api/admin/site-config/hotelGroups/{item_id}
DELETE /api/admin/site-config/hotelGroups/{item_id}
PATCH /api/admin/site-config/hotelGroups/reorder
```
字段规则:
- 新增请求至少包含 `title`,可包含 `description``image``coverImage``priceAmount``priceUnit``tags``status``isActive``sortOrder`
- 更新请求可包含 `title``description``image``coverImage``priceAmount``priceUnit``tags``status``isActive``sortOrder`
- `coverImage` 是酒店封面图主字段;为兼容旧前端,后端同时保留 `image`。当请求只传其中一个字段时,后端应同步另一个字段。
- `priceAmount` 为价格数值,`priceUnit` 为价格单位文案,默认建议为 `起/晚`
- `tags` 最多 3 个,保存时去掉空标签。
- `status` 只允许 `draft``published`;管理端“前台启用”开关会同步提交 `status``isActive`。Public API 只返回 `status="published"``isActive=true` 的酒店卡片。
- 删除只删除首页酒店卡片配置,不删除素材库资源;删除后后端重新整理剩余项 `sortOrder`
- `PATCH /reorder``itemIds` 必须完整覆盖当前 `hotelGroups` 全部配置项 ID不能缺失、重复或包含未知 ID。
#### 万趣用车 `vehicleOptions`
`vehicleOptions` 对应首页“万趣用车”模块,继续作为普通首页内容卡片维护,不复用酒店价格和标签字段。
```http
POST /api/admin/site-config/vehicleOptions
PATCH /api/admin/site-config/vehicleOptions/{item_id}
DELETE /api/admin/site-config/vehicleOptions/{item_id}
PATCH /api/admin/site-config/vehicleOptions/reorder
```
字段规则:
- 新增请求至少包含 `title`,可包含 `description``image``isActive``sortOrder`
- 更新请求可包含 `title``description``image``isActive``sortOrder`
- 删除只删除首页用车卡片配置,不删除素材库资源;删除后后端重新整理剩余项 `sortOrder`
- `PATCH /reorder``itemIds` 必须完整覆盖当前 `vehicleOptions` 全部配置项 ID不能缺失、重复或包含未知 ID。
- `GET /api/public/site-config` 只返回启用用车卡片,并按 `sortOrder` 升序MiniAPP 在字段缺失或空数组时使用本地内容兜底。
#### 更多服务 `ctaBanners`
`ctaBanners` 对应首页“更多服务”模块,维护权益、服务管家、目的地和需求入口等服务卡片。管理端按顶部轮播相同的配置方式提供新增、编辑、删除和排序;`alt` 是前台卡片标题,`image` 是卡片背景图。
```http
POST /api/admin/site-config/ctaBanners
PATCH /api/admin/site-config/ctaBanners/{item_id}
DELETE /api/admin/site-config/ctaBanners/{item_id}
PATCH /api/admin/site-config/ctaBanners/reorder
```
字段规则:
- 新增请求至少包含 `alt`,可包含 `image``targetType``targetValue``isActive``sortOrder`
- 更新请求可包含 `alt``image``targetType``targetValue``isActive``sortOrder`
- 删除只删除首页更多服务卡片配置,不删除素材库资源;删除后后端重新整理剩余项 `sortOrder`
- `GET /api/public/site-config` 只返回启用卡片,并按 `sortOrder` 升序。
### 线索列表
```http
GET /api/admin/leads
```
查询参数:
| 参数 | 类型 | 当前 UI 是否使用 | 说明 |
| --- | --- | --- | --- |
| `status` | `LeadStatus` | 否 | 后端可支持状态筛选 |
| `take` | `number` | 否 | 后端当前可限制返回条数 |
响应体:
```ts
{
items: Lead[];
}
```
排序建议:`createdAt` 倒序。当前 UI 展示客户手机号、创建时间、目的地/备注、来源商品/来源页面和状态。
### 更新线索状态
```http
PATCH /api/admin/leads/{id}/status
```
请求体:
```ts
{
status: LeadStatus;
}
```
响应体:更新后的 `Lead`,至少需要包含 `id``status`
状态码要求:
- `200`:更新成功。
- `404`:线索不存在。
- `422`:状态值非法。
当前 UI 更新后会重新调用 `GET /api/admin/leads`,因此响应体不会直接用于渲染列表。
### 发布站点配置
```http
POST /api/admin/publish
```
请求体:空 JSON 对象或无请求体均可兼容。
响应体:
```ts
{
id: string;
title: string;
publishedAt: string;
}
```
当前 UI 只读取 `title` 展示发布结果。后端可额外返回 `status``snapshot` 等字段。
### 重置贵州内容
```http
POST /api/admin/reset-guizhou-content
```
请求体:空 JSON 对象或无请求体均可兼容。
响应体:
```ts
{
heroSlides: number;
destinations: number;
themes: number;
ctaBanners: number;
routeSections?: number;
hotelGroups?: number;
vehicleOptions?: number;
products: number;
}
```
要求:
- 该接口会重置内容数据,必须走后台鉴权。
- 后端需要记录审计日志。
- 生产环境调用前应通过部署流程或权限控制额外确认。
## 后端实现注意事项
- Admin API 默认使用 `require_admin`,登录接口除外。
- 所有外部输入通过 Pydantic schema 校验。
- 变更类接口需要继续写入 `AuditLog`
- 响应字段使用 camelCase以匹配当前前端类型。
- 允许后端返回额外字段,但不要移除本文列出的前端依赖字段。
- 当前管理端不会直接上传图片,只维护图片 URL媒体库接口暂不属于本需求范围。

View File

@@ -1,25 +0,0 @@
# 后台 API 服务
`WonderQ-Admin` 当前是独立的 Python + FastAPI API 服务,使用 PostgreSQL 保存业务数据,通过 Docker Compose 启动 `api``postgres``redis`
## 本地 API 启动
```bash
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
docker compose up -d postgres redis
alembic upgrade head
python -m app.seed
uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload
```
健康检查:`http://localhost:4000/health`
## 前端联调
后台管理前端位于 `D:\www\znkj\WonderQ-Admin-UI`。如需连接本服务,在前端 `.env` 或环境变量中配置:
```text
VITE_API_BASE_URL="http://localhost:4000"
```

View File

@@ -1,521 +0,0 @@
# 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` 规范化、必填校验、人数/预算边界和备注长度限制。