feat(admin): add hotel groups and vehicle options site modules
Add support for two new standalone site content modules in the admin dashboard: hotel groups and vehicle options. This commit includes: - Added TypeScript types for HotelGroup and VehicleOption, extended SiteConfig and SiteModule enum - Updated admin utilities (primary key lookup, empty drafts, payload compaction) for the new modules - Added management UI panels and editors in the structure page for configuring the new modules - Adjusted module rail styling and interactive states for consistent UI across the admin - Updated ctaBanners type definition to include missing sortOrder, createdAt, and updatedAt fields - Updated API and admin documentation to cover the new modules' CRUD endpoints and field contracts
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 页面模块配置 Admin API 契约
|
||||
|
||||
本文档定义 WonderQ-Admin 后端需要为 WonderQ-Admin-UI 实现的页面模块配置 CRUD 接口。接口用于维护小程序/H5 前台页面模块中的配置数据,例如首页轮播、目的地宫格、贵州地图、主题卡片、特价优惠、精选线路分组和底部运营入口。
|
||||
本文档定义 WonderQ-Admin 后端需要为 WonderQ-Admin-UI 实现的页面模块配置 CRUD 接口。接口用于维护小程序/H5 前台页面模块中的配置数据,例如首页轮播、目的地宫格、贵州地图、主题卡片、特价优惠、精选线路分组、特色酒店、万趣用车和更多服务。
|
||||
|
||||
## 适用模块
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
| `themes` | 主题甄选 | 首页主题卡片和跳转 |
|
||||
| `campaigns` | 特价优惠 | 首页特价优惠活动元信息 |
|
||||
| `routeSections` | 精选线路子分组 | 首页“精选线路”按运营任务新增分组,维护标题、副文案、启用状态和关联商品 |
|
||||
| `ctaBanners` | 底部运营入口 | 权益卡、管家入口、需求入口等 CTA |
|
||||
| `hotelGroups` | 特色酒店 | 首页“特色酒店”卡片,维护标题、描述、封面图、启用状态和排序 |
|
||||
| `vehicleOptions` | 万趣用车 | 首页“万趣用车”卡片,维护标题、描述、封面图、启用状态和排序 |
|
||||
| `ctaBanners` | 更多服务 | 权益、管家、目的地和需求入口等更多服务卡片 |
|
||||
|
||||
商品本体、活动商品池和线索跟进继续走独立业务接口,不混入本契约。`routeSections` 只维护精选线路子分组和商品 ID 关联,不重复编辑商品详情。
|
||||
商品本体、活动商品池和线索跟进继续走独立业务接口,不混入本契约。`routeSections` 只维护精选线路子分组和商品 ID 关联,不重复编辑商品详情;`hotelGroups` 和 `vehicleOptions` 只维护首页卡片内容,不绑定商品本体。
|
||||
|
||||
## 通用约定
|
||||
|
||||
@@ -41,21 +43,21 @@
|
||||
|
||||
WonderQ-Admin 后端实现页面模块配置接口时,需要把 `map` 和 `campaigns` 作为正式模块接入,而不是只在前端展示:
|
||||
|
||||
- 模块白名单必须包含 `heroSlides`、`destinations`、`map`、`themes`、`campaigns`、`routeSections`、`ctaBanners`。
|
||||
- 模块白名单必须包含 `heroSlides`、`destinations`、`map`、`themes`、`campaigns`、`routeSections`、`hotelGroups`、`vehicleOptions`、`ctaBanners`。
|
||||
- 权限校验、模块路由、服务层分发和数据模型映射都必须识别 `map` 和 `campaigns`,否则前端会收到 `MODULE_CONFIG_FORBIDDEN` 并以 toast 展示失败原因。
|
||||
- `GET /api/admin/site-config` 即使没有地图数据,也必须返回 `map: []`,不要省略 `map` 字段。
|
||||
- `GET /api/admin/site-config` 即使没有特价优惠数据,也必须返回 `campaigns: []`,不要省略 `campaigns` 字段。
|
||||
- `GET /api/admin/site-config` 必须返回 `routeSections`,包含未启用分组和后台已配置的全部商品 ID。
|
||||
- `GET /api/admin/site-config` 必须返回 `routeSections`,包含未启用分组和后台已配置的全部商品 ID。`hotelGroups`、`vehicleOptions` 也必须稳定返回数组,无数据时返回 `[]`。
|
||||
- `map` 只维护一张图片,只需要支持查询、创建、更新、删除,不需要排序接口。
|
||||
- `campaigns` 维护活动元信息,只需要支持查询、创建、更新、删除,不需要排序接口。
|
||||
- `routeSections` 允许按运营任务新增多个子分组;已创建分组支持更新字段、商品关联、删除和分组顺序。
|
||||
- `routeSections` 允许按运营任务新增多个子分组;已创建分组支持更新字段、商品关联、删除和分组顺序。`hotelGroups`、`vehicleOptions` 支持新增、更新、删除和排序。
|
||||
- 同一商品不能同时出现在多个 `routeSections` 子分组;更新 `productIds` 时后端需要校验互斥。
|
||||
- 图片上传仍走 `POST /api/admin/media-assets/upload`,模块保存接口只接收上传结果里的 OSS `url` 字段并写入 `image`。
|
||||
|
||||
## 类型定义
|
||||
|
||||
```ts
|
||||
type SiteModule = "heroSlides" | "destinations" | "map" | "themes" | "campaigns" | "routeSections" | "ctaBanners";
|
||||
type SiteModule = "heroSlides" | "destinations" | "map" | "themes" | "campaigns" | "routeSections" | "hotelGroups" | "vehicleOptions" | "ctaBanners";
|
||||
|
||||
type SiteItemPatch = {
|
||||
title?: string;
|
||||
@@ -157,6 +159,29 @@ type RouteSection = {
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
|
||||
type SiteCardItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
image: string | null;
|
||||
isActive: boolean;
|
||||
sortOrder: number;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
|
||||
type CtaBanner = {
|
||||
id: string;
|
||||
alt: string;
|
||||
image: string;
|
||||
targetType: string | null;
|
||||
targetValue: string | null;
|
||||
isActive: boolean;
|
||||
sortOrder: number;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
```
|
||||
|
||||
各模块字段要求:
|
||||
@@ -169,7 +194,9 @@ type RouteSection = {
|
||||
| `themes` | `label` | `image`、`targetType`、`targetValue`、`isActive`、`sortOrder` |
|
||||
| `campaigns` | `title`、`slug` | `description`、`coverImage`、`priceAmount`、`priceUnit`、`tags`、`status`、`startsAt`、`endsAt` |
|
||||
| `routeSections` | `title` | `subtitle`、`productIds`、`isActive`、`sortOrder` |
|
||||
| `ctaBanners` | `alt` | `image`、`targetType`、`targetValue`、`isActive`、`sortOrder` |
|
||||
| `hotelGroups` | `title` | `description`、`image`、`isActive`、`sortOrder` |
|
||||
| `vehicleOptions` | `title` | `description`、`image`、`isActive`、`sortOrder` |
|
||||
| `ctaBanners` | `alt`(服务标题) | `image`、`targetType`、`targetValue`、`isActive`、`sortOrder` |
|
||||
|
||||
后端可以在创建时补全 `id`、默认 `isActive=true`、默认 `sortOrder=当前模块最后一位`。
|
||||
`campaigns` 创建时默认 `status="draft"`,不会进入 Public `site-config.campaigns`;只有 `status="published"` 的活动会进入 H5 Public API。
|
||||
@@ -491,9 +518,67 @@ PATCH /api/admin/site-config/routeSections/reorder
|
||||
- 同一商品不能出现在其他精选线路子分组;冲突时返回 `409 ROUTE_SECTION_PRODUCT_CONFLICT`。
|
||||
- `DELETE /api/admin/site-config/routeSections/:id` 删除分组配置并返回被删除 ID;只移除首页分组,不删除关联商品本体。
|
||||
|
||||
## 特色酒店和万趣用车 `hotelGroups` / `vehicleOptions` 专用契约
|
||||
|
||||
`hotelGroups` 对应首页“特色酒店”卡片,`vehicleOptions` 对应首页“万趣用车”卡片。两者都是普通首页内容卡片,不维护商品详情和商品关联。
|
||||
|
||||
字段语义:
|
||||
|
||||
| 字段 | 类型 | 创建 | 更新 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `id` | `string` | 后端生成 | 不允许修改 | 卡片唯一 id |
|
||||
| `title` | `string` | 必填 | 可选 | 卡片标题,提交时 trim 后不能为空 |
|
||||
| `description` | `string \| null` | 可选 | 可选 | 卡片描述,空字符串可归一化为 `null` |
|
||||
| `image` | `string \| null` | 可选 | 可选 | 卡片封面图 OSS URL;上传仍走媒体接口 |
|
||||
| `isActive` | `boolean` | 可选 | 可选 | 用户侧是否展示;未传默认 `true` |
|
||||
| `sortOrder` | `number` | 可选 | 可选 | 展示顺序;未传时追加到模块末尾 |
|
||||
|
||||
两类模块均复用通用接口:
|
||||
|
||||
```http
|
||||
POST /api/admin/site-config/hotelGroups
|
||||
PATCH /api/admin/site-config/hotelGroups/:id
|
||||
DELETE /api/admin/site-config/hotelGroups/:id
|
||||
PATCH /api/admin/site-config/hotelGroups/reorder
|
||||
|
||||
POST /api/admin/site-config/vehicleOptions
|
||||
PATCH /api/admin/site-config/vehicleOptions/:id
|
||||
DELETE /api/admin/site-config/vehicleOptions/:id
|
||||
PATCH /api/admin/site-config/vehicleOptions/reorder
|
||||
```
|
||||
|
||||
删除只删除首页卡片配置,不删除任何商品、目的地或素材库资源。Public API 只返回启用项,并按 `sortOrder` 升序输出;无启用项时 MiniAPP 使用本地 `src/content.ts` 兜底内容。
|
||||
|
||||
## 更多服务 `ctaBanners` 专用契约
|
||||
|
||||
`ctaBanners` 对应首页“更多服务”模块,用于维护权益、管家、目的地和需求入口等服务卡片。管理端复用顶部轮播的列表式操作:新增卡片、编辑标题和背景图、删除卡片、上移/下移排序,以及控制前台启用状态。
|
||||
|
||||
字段语义:
|
||||
|
||||
| 字段 | 类型 | 创建 | 更新 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `id` | `string` | 后端生成 | 不允许修改 | 服务卡片唯一 id |
|
||||
| `alt` | `string` | 必填 | 可选 | 服务标题,展示在“更多服务”卡片上,提交时 trim 后不能为空 |
|
||||
| `image` | `string \| null` | 可选 | 可选 | 服务卡片背景图 OSS URL;上传仍走媒体接口 |
|
||||
| `targetType` | `string \| null` | 可选 | 可选 | 点击目标类型,例如权益、管家、目的地或需求入口 |
|
||||
| `targetValue` | `string \| null` | 可选 | 可选 | 点击目标值;无额外参数时可为空 |
|
||||
| `isActive` | `boolean` | 可选 | 可选 | 用户侧是否展示;未传默认 `true` |
|
||||
| `sortOrder` | `number` | 可选 | 可选 | 展示顺序;未传时追加到模块末尾 |
|
||||
|
||||
复用通用接口:
|
||||
|
||||
```http
|
||||
POST /api/admin/site-config/ctaBanners
|
||||
PATCH /api/admin/site-config/ctaBanners/:id
|
||||
DELETE /api/admin/site-config/ctaBanners/:id
|
||||
PATCH /api/admin/site-config/ctaBanners/reorder
|
||||
```
|
||||
|
||||
删除只删除首页“更多服务”卡片配置,不删除任何素材库资源。Public API 只返回启用项,并按 `sortOrder` 升序输出;无启用项时 MiniAPP 使用本地 `src/content.ts` 兜底内容。
|
||||
|
||||
## 接口列表
|
||||
|
||||
以下路径由七类页面模块复用;`heroSlides`、`map`、`campaigns`、`routeSections` 的请求体和响应体以各自专用契约为准。
|
||||
以下路径由九类页面模块复用;`heroSlides`、`map`、`campaigns`、`routeSections`、`hotelGroups`、`vehicleOptions`、`ctaBanners` 的请求体和响应体以各自专用契约为准。
|
||||
|
||||
### 获取完整站点配置
|
||||
|
||||
@@ -511,13 +596,15 @@ type SiteConfig = {
|
||||
themes: ThemeCard[];
|
||||
campaigns: Campaign[];
|
||||
routeSections: RouteSection[];
|
||||
hotelGroups: SiteCardItem[];
|
||||
vehicleOptions: SiteCardItem[];
|
||||
ctaBanners: CtaBanner[];
|
||||
};
|
||||
```
|
||||
|
||||
`map` 字段必须稳定返回数组;无数据时返回空数组 `[]`。
|
||||
`campaigns` 字段必须稳定返回数组;无数据时返回空数组 `[]`。
|
||||
`routeSections` 字段必须稳定返回数组;无数据时返回 `[]`,由管理端通过“新增”逐个创建子分组。
|
||||
`routeSections` 字段必须稳定返回数组;无数据时返回 `[]`,由管理端通过“新增”逐个创建子分组。`hotelGroups`、`vehicleOptions` 字段也必须稳定返回数组;无数据时返回 `[]`。
|
||||
|
||||
### 新增模块配置项
|
||||
|
||||
@@ -583,7 +670,7 @@ PATCH /api/admin/site-config/:module/reorder
|
||||
- 不允许混入其他模块 id。
|
||||
- 后端按数组顺序写入 `sortOrder`,从 0 开始。
|
||||
|
||||
`map` 和 `campaigns` 模块不提供排序能力。若收到 `PATCH /api/admin/site-config/map/reorder` 或 `PATCH /api/admin/site-config/campaigns/reorder`,后端应返回 `400` 或 `405`,不要创建任何排序数据。`routeSections` 支持排序,但 `itemIds` 必须刚好包含当前已保存的子分组 ID。
|
||||
`map` 和 `campaigns` 模块不提供排序能力。若收到 `PATCH /api/admin/site-config/map/reorder` 或 `PATCH /api/admin/site-config/campaigns/reorder`,后端应返回 `400` 或 `405`,不要创建任何排序数据。`routeSections`、`hotelGroups`、`vehicleOptions` 支持排序,但 `itemIds` 必须刚好包含当前已保存的同模块配置项 ID。
|
||||
|
||||
响应状态码 `200`:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user