From 2a3615901c6ab7ee9aae490e8b6c287afc25ae59 Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Sat, 4 Jul 2026 10:50:45 +0800 Subject: [PATCH] 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. --- .../Picface/Cloud/sgim_picface_cloud.bin | Bin 172152 -> 0 bytes .../Picface/Cloud/sgim_picface_cloud_bak.bin | Bin 172152 -> 0 bytes AGENTS.md | 114 --- docs/README.md | 10 - docs/admin-backend-plan.md | 36 - docs/admin-ui-api-requirements.md | 651 ------------------ docs/backend/README.md | 25 - docs/miniapp-public-api.md | 521 -------------- 8 files changed, 1357 deletions(-) delete mode 100644 %SystemDrive%/ProgramData/SogouInput/Components/Picface/Cloud/sgim_picface_cloud.bin delete mode 100644 %SystemDrive%/ProgramData/SogouInput/Components/Picface/Cloud/sgim_picface_cloud_bak.bin delete mode 100644 AGENTS.md delete mode 100644 docs/README.md delete mode 100644 docs/admin-backend-plan.md delete mode 100644 docs/admin-ui-api-requirements.md delete mode 100644 docs/backend/README.md delete mode 100644 docs/miniapp-public-api.md diff --git a/%SystemDrive%/ProgramData/SogouInput/Components/Picface/Cloud/sgim_picface_cloud.bin b/%SystemDrive%/ProgramData/SogouInput/Components/Picface/Cloud/sgim_picface_cloud.bin deleted file mode 100644 index 306921d17ea0219b7c0867ebd380f5d568685811..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172152 zcmeIup$&jQ3 -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= -``` - -查询参数: - -| 参数 | 类型 | 当前 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` - -响应体:`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;媒体库接口暂不属于本需求范围。 - diff --git a/docs/backend/README.md b/docs/backend/README.md deleted file mode 100644 index a3467c8..0000000 --- a/docs/backend/README.md +++ /dev/null @@ -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" -``` diff --git a/docs/miniapp-public-api.md b/docs/miniapp-public-api.md deleted file mode 100644 index 81809ff..0000000 --- a/docs/miniapp-public-api.md +++ /dev/null @@ -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` | 否 | 图集 | -| `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` 规范化、必填校验、人数/预算边界和备注长度限制。