feat(admin): add route sections admin management module
This commit adds the complete admin workflow for managing homepage curated route subgroups: - Add `RouteSection` type and extend `SiteConfig`/`SiteModule` to support the new module - Create all required admin components: panel, editors, product picker, and utility functions - Update admin utilities and API types to handle routeSections CRUD operations - Update documentation to include the new routeSections API contract - Add supporting CSS styles for the new UI elements
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 前台页面模块中的配置数据,例如首页轮播、目的地宫格、贵州地图、主题卡片、特价优惠、精选线路分组和底部运营入口。
|
||||
|
||||
## 适用模块
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
| `map` | 贵州地图 | 首页「探索贵州」区域内的地图图片素材 |
|
||||
| `themes` | 主题甄选 | 首页主题卡片和跳转 |
|
||||
| `campaigns` | 特价优惠 | 首页特价优惠活动元信息 |
|
||||
| `routeSections` | 精选线路子分组 | 首页“精选线路”按运营任务新增分组,维护标题、副文案、启用状态和关联商品 |
|
||||
| `ctaBanners` | 底部运营入口 | 权益卡、管家入口、需求入口等 CTA |
|
||||
|
||||
商品池、活动商品池和线索跟进继续走独立业务接口,不混入本契约。
|
||||
商品本体、活动商品池和线索跟进继续走独立业务接口,不混入本契约。`routeSections` 只维护精选线路子分组和商品 ID 关联,不重复编辑商品详情。
|
||||
|
||||
## 通用约定
|
||||
|
||||
@@ -40,21 +41,25 @@
|
||||
|
||||
WonderQ-Admin 后端实现页面模块配置接口时,需要把 `map` 和 `campaigns` 作为正式模块接入,而不是只在前端展示:
|
||||
|
||||
- 模块白名单必须包含 `heroSlides`、`destinations`、`map`、`themes`、`campaigns`、`ctaBanners`。
|
||||
- 模块白名单必须包含 `heroSlides`、`destinations`、`map`、`themes`、`campaigns`、`routeSections`、`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。
|
||||
- `map` 只维护一张图片,只需要支持查询、创建、更新、删除,不需要排序接口。
|
||||
- `campaigns` 维护活动元信息,只需要支持查询、创建、更新、删除,不需要排序接口。
|
||||
- `routeSections` 允许按运营任务新增多个子分组;已创建分组支持更新字段、商品关联、删除和分组顺序。
|
||||
- 同一商品不能同时出现在多个 `routeSections` 子分组;更新 `productIds` 时后端需要校验互斥。
|
||||
- 图片上传仍走 `POST /api/admin/media-assets/upload`,模块保存接口只接收上传结果里的 OSS `url` 字段并写入 `image`。
|
||||
|
||||
## 类型定义
|
||||
|
||||
```ts
|
||||
type SiteModule = "heroSlides" | "destinations" | "map" | "themes" | "campaigns" | "ctaBanners";
|
||||
type SiteModule = "heroSlides" | "destinations" | "map" | "themes" | "campaigns" | "routeSections" | "ctaBanners";
|
||||
|
||||
type SiteItemPatch = {
|
||||
title?: string;
|
||||
subtitle?: string | null;
|
||||
kicker?: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
@@ -69,6 +74,7 @@ type SiteItemPatch = {
|
||||
isHot?: boolean;
|
||||
isActive?: boolean;
|
||||
sortOrder?: number;
|
||||
productIds?: string[];
|
||||
status?: "draft" | "published";
|
||||
startsAt?: string | null;
|
||||
endsAt?: string | null;
|
||||
@@ -140,6 +146,17 @@ type CampaignCreateInput = {
|
||||
};
|
||||
|
||||
type CampaignUpdateInput = Partial<CampaignCreateInput>;
|
||||
|
||||
type RouteSection = {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string | null;
|
||||
productIds: string[];
|
||||
isActive: boolean;
|
||||
sortOrder: number;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
```
|
||||
|
||||
各模块字段要求:
|
||||
@@ -151,6 +168,7 @@ type CampaignUpdateInput = Partial<CampaignCreateInput>;
|
||||
| `map` | `image` | `isActive` |
|
||||
| `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` |
|
||||
|
||||
后端可以在创建时补全 `id`、默认 `isActive=true`、默认 `sortOrder=当前模块最后一位`。
|
||||
@@ -414,9 +432,68 @@ DELETE /api/admin/site-config/campaigns/:id
|
||||
特价优惠不定义 `sortOrder`、`isActive`、`targetType` 和 `targetValue`;兼容期如果请求体携带这些字段,后端可以忽略,但不要写入 `Campaign` 业务数据。
|
||||
`tags` 保存前需要 trim 并过滤空字符串;有效标签超过 3 个时返回 `422 MODULE_CONFIG_VALIDATION_ERROR`,`details` 为 `{ "field": "tags", "max": 3 }`。
|
||||
|
||||
## 精选线路子分组 `routeSections` 专用契约
|
||||
|
||||
`routeSections` 对应首页“精选线路”的运营分组。运营可按任务新增分组,并维护分组标题、副文案、启用状态、分组顺序、关联线路商品和商品顺序。
|
||||
|
||||
字段语义:
|
||||
|
||||
| 字段 | 类型 | 更新 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `id` | `string` | 不允许修改 | 系统生成分组 ID |
|
||||
| `title` | `string` | 可选 | 分组标题,不能为空 |
|
||||
| `subtitle` | `string \| null` | 可选 | 分组副文案 |
|
||||
| `productIds` | `string[]` | 可选 | 关联商品 ID,按数组顺序展示 |
|
||||
| `isActive` | `boolean` | 可选 | 用户侧是否展示该分组 |
|
||||
| `sortOrder` | `number` | 可选 | 分组展示顺序 |
|
||||
|
||||
更新分组:
|
||||
|
||||
```http
|
||||
POST /api/admin/site-config/routeSections
|
||||
```
|
||||
|
||||
请求体示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "经典人文打卡线路",
|
||||
"subtitle": "黄果树、荔波小七孔、千户苗寨、镇远古城、梵净山一次串联"
|
||||
}
|
||||
```
|
||||
|
||||
响应状态码 `201`,返回创建后的子分组。`id` 由后端生成;精选线路分组按运营任务动态新增,不再限制为固定三组,也不再使用 `routes` / `routes-outdoor` / `routes-mix` 作为固定槽位。
|
||||
|
||||
```http
|
||||
PATCH /api/admin/site-config/routeSections/:id
|
||||
```
|
||||
|
||||
请求体示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "经典人文打卡线路",
|
||||
"subtitle": "黄果树、荔波小七孔、千户苗寨、镇远古城、梵净山一次串联",
|
||||
"isActive": true,
|
||||
"productIds": ["product-uuid-1", "product-uuid-2"]
|
||||
}
|
||||
```
|
||||
|
||||
调整分组顺序:
|
||||
|
||||
```http
|
||||
PATCH /api/admin/site-config/routeSections/reorder
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `productIds` 中的商品必须存在。
|
||||
- 同一商品不能出现在其他精选线路子分组;冲突时返回 `409 ROUTE_SECTION_PRODUCT_CONFLICT`。
|
||||
- `DELETE /api/admin/site-config/routeSections/:id` 删除分组配置并返回被删除 ID;只移除首页分组,不删除关联商品本体。
|
||||
|
||||
## 接口列表
|
||||
|
||||
以下路径由六类页面模块复用;`heroSlides`、`map`、`campaigns` 的请求体和响应体以各自专用契约为准。
|
||||
以下路径由七类页面模块复用;`heroSlides`、`map`、`campaigns`、`routeSections` 的请求体和响应体以各自专用契约为准。
|
||||
|
||||
### 获取完整站点配置
|
||||
|
||||
@@ -433,12 +510,14 @@ type SiteConfig = {
|
||||
map: MapImage[];
|
||||
themes: ThemeCard[];
|
||||
campaigns: Campaign[];
|
||||
routeSections: RouteSection[];
|
||||
ctaBanners: CtaBanner[];
|
||||
};
|
||||
```
|
||||
|
||||
`map` 字段必须稳定返回数组;无数据时返回空数组 `[]`。
|
||||
`campaigns` 字段必须稳定返回数组;无数据时返回空数组 `[]`。
|
||||
`routeSections` 字段必须稳定返回数组;无数据时返回 `[]`,由管理端通过“新增”逐个创建子分组。
|
||||
|
||||
### 新增模块配置项
|
||||
|
||||
@@ -504,7 +583,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`,不要创建任何排序数据。
|
||||
`map` 和 `campaigns` 模块不提供排序能力。若收到 `PATCH /api/admin/site-config/map/reorder` 或 `PATCH /api/admin/site-config/campaigns/reorder`,后端应返回 `400` 或 `405`,不要创建任何排序数据。`routeSections` 支持排序,但 `itemIds` 必须刚好包含当前已保存的子分组 ID。
|
||||
|
||||
响应状态码 `200`:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user