Files
WonderQ-Project/docs/home-api.md
duanshuwen cda8069630 docs: 清理过时文档,新增首页API契约并更新相关内容
- 删除backend-plan.md、backend-api-service.md等多份过时项目文档
- 新增home-api.md规范首页三类内容的Admin API补充契约
- 更新docs/README.md的文档清单与展示格式
- 优化integration-workflow.md、admin-api-requirements.md等文档的表格与内容
- 为WonderQ-MiniAPP的homeExperienceData.ts新增API适配类型与归一化函数
2026-08-18 20:00:25 +08:00

363 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 首页内容管理 Admin API
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。
>
> 状态:待实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts`、`homeTeamBuildingData.ts` 和 `homeWildArchivesData.ts` 定义首页卡片内容,以及管理端需要的查询、维护和排序接口。它是首页内容的 Admin API 补充契约,不是 MiniAPP Public API 文档。
## 领域边界
首页内容管理只维护三类首页卡片:
- 体验推荐:徽标、分类、标题、英文标题、封面和需求关键词。
- 团队共创:标签、标题、描述、封面和需求关键词。
- 极境视界:案例标题、封面和需求关键词。
本领域不负责:
- 顶部轮播、车辆选项、需求表单或其他站点模块;这些内容遵循 [Admin API 主契约](./admin-api-requirements.md)。
- 商品、Product、ProductImage、详情、价格、库存、订单或预订。
- 线索创建和线索跟进。
`demandKeyword` 只是点击卡片后预填需求页的普通字符串,不是商品 ID、路线 ID、订单 ID,也不建立数据库外键。
当前首页仍直接使用三个文件中的 mock 数组。Admin API 接入前,这些数组继续作为前台 fallback;本文件不代表接口已经在 `WonderQ-Admin` 或 `WonderQ-Admin-UI` 中实现。
## 接口清单
API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/api/admin/home/experiences` | 获取全部体验推荐卡片 |
| `POST` | `/api/admin/home/experiences` | 新增体验推荐卡片 |
| `PATCH` | `/api/admin/home/experiences/{experienceId}` | 编辑体验推荐卡片 |
| `DELETE` | `/api/admin/home/experiences/{experienceId}` | 删除体验推荐卡片 |
| `PATCH` | `/api/admin/home/experiences/reorder` | 调整体验推荐卡片顺序 |
| `GET` | `/api/admin/home/team-buildings` | 获取全部团队共创卡片 |
| `POST` | `/api/admin/home/team-buildings` | 新增团队共创卡片 |
| `PATCH` | `/api/admin/home/team-buildings/{teamBuildingId}` | 编辑团队共创卡片 |
| `DELETE` | `/api/admin/home/team-buildings/{teamBuildingId}` | 删除团队共创卡片 |
| `PATCH` | `/api/admin/home/team-buildings/reorder` | 调整团队共创卡片顺序 |
| `GET` | `/api/admin/home/wild-archives` | 获取全部极境视界案例 |
| `POST` | `/api/admin/home/wild-archives` | 新增极境视界案例 |
| `PATCH` | `/api/admin/home/wild-archives/{archiveId}` | 编辑极境视界案例 |
| `DELETE` | `/api/admin/home/wild-archives/{archiveId}` | 删除极境视界案例 |
| `PATCH` | `/api/admin/home/wild-archives/reorder` | 调整极境视界案例顺序 |
## 通用约定
- 请求和响应使用 JSON,字段使用 camelCase。
- 所有管理接口需要 `Authorization: Bearer <admin-jwt>`。
- `GET` 返回启用和停用的全部记录,按 `sortOrder` 升序返回,供 Admin UI 完整维护。
- 创建、编辑、删除和排序成功后写入审计日志,再提交事务。
- 创建和编辑返回最新记录;排序接口返回排序后的 `items`。
- ID 由后端生成并作为非空字符串返回。初始化迁移时应优先保留三个 mock 文件中的稳定 ID。
- 空集合返回 `[]`,不能返回 `null` 或省略字段。
- 所有资源的 `sortOrder` 从 `0` 开始,数值越小越靠前;新增未传排序时追加到末尾。
- 失败响应沿用 Admin API 主契约,包含 `message`、`code` 和可选的 `details`。
## 数据类型
### 体验推荐
`homeExperienceData.ts` 已包含前台渲染类型和 API 过渡类型。Admin API 的完整记录应补充管理元数据:
```ts
type HomeExperience = {
id: string;
badge: string;
category: string;
title: string;
englishTitle: string;
image: string;
demandKeyword: string;
};
type HomeExperienceApiItem = {
id?: string | null;
badge?: string | null;
category?: string | null;
title?: string | null;
englishTitle?: string | null;
image?: string | null;
demandKeyword?: string | null;
isActive?: boolean | null;
sortOrder?: number | null;
};
type HomeExperienceRecord = HomeExperience & {
isActive: boolean;
sortOrder: number;
createdAt: string;
updatedAt: string;
};
type HomeExperienceCreate = Omit<HomeExperience, "id"> & {
isActive?: boolean;
sortOrder?: number;
};
type HomeExperiencePatch = Partial<HomeExperienceCreate>;
```
### 团队共创
`homeTeamBuildingData.ts` 当前只包含前台渲染字段,管理端记录增加状态、排序和审计时间:
```ts
type HomeTeamBuilding = {
id: string;
tag: string;
title: string;
description: string;
image: string;
demandKeyword: string;
};
type HomeTeamBuildingRecord = HomeTeamBuilding & {
isActive: boolean;
sortOrder: number;
createdAt: string;
updatedAt: string;
};
type HomeTeamBuildingCreate = Omit<HomeTeamBuilding, "id"> & {
isActive?: boolean;
sortOrder?: number;
};
type HomeTeamBuildingPatch = Partial<HomeTeamBuildingCreate>;
```
### 极境视界
`homeWildArchivesData.ts` 当前只包含前台渲染字段,管理端记录增加状态、排序和审计时间:
```ts
type HomeWildArchive = {
id: string;
title: string;
image: string;
demandKeyword: string;
};
type HomeWildArchiveRecord = HomeWildArchive & {
isActive: boolean;
sortOrder: number;
createdAt: string;
updatedAt: string;
};
type HomeWildArchiveCreate = Omit<HomeWildArchive, "id"> & {
isActive?: boolean;
sortOrder?: number;
};
type HomeWildArchivePatch = Partial<HomeWildArchiveCreate>;
```
列表响应和排序请求统一使用以下结构:
```ts
type HomeListResponse<T> = {
items: T[];
};
type HomeReorderRequest = {
itemIds: string[];
};
```
## 字段约束
| 资源 | 字段 | 类型 | 必填 | 约束和用途 |
| --- | --- | --- | --- | --- |
| 体验推荐 | `badge` | `string` | 是 | 卡片徽标,去除首尾空白后不得为空。 |
| 体验推荐 | `category` | `string` | 是 | 体验分类文案,去除首尾空白后不得为空。 |
| 体验推荐 | `title` | `string` | 是 | 卡片主标题,去除首尾空白后不得为空。 |
| 体验推荐 | `englishTitle` | `string` | 是 | 卡片英文标题,去除首尾空白后不得为空。 |
| 体验推荐 | `image` | `string` | 是 | 可直接用于图片组件的封面 URL。 |
| 体验推荐 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 |
| 团队共创 | `tag` | `string` | 是 | 卡片标签,去除首尾空白后不得为空。 |
| 团队共创 | `title` | `string` | 是 | 卡片标题,去除首尾空白后不得为空。 |
| 团队共创 | `description` | `string` | 是 | 卡片描述,去除首尾空白后不得为空。 |
| 团队共创 | `image` | `string` | 是 | 可直接用于图片组件的封面 URL。 |
| 团队共创 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 |
| 极境视界 | `title` | `string` | 是 | 案例标题,去除首尾空白后不得为空。 |
| 极境视界 | `image` | `string` | 是 | 案例图片 URL。 |
| 极境视界 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 |
| 全部资源 | `isActive` | `boolean` | 响应必填 | 是否进入已发布前台内容,创建默认 `true`。 |
| 全部资源 | `sortOrder` | `number` | 响应必填 | 非负整数,数值越小越靠前。 |
| 全部资源 | `createdAt` | `string` | 响应必填 | ISO 8601 创建时间。 |
| 全部资源 | `updatedAt` | `string` | 响应必填 | ISO 8601 最后更新时间。 |
服务端应校验所有必填文本、URL 格式和非负整数排序值;具体文本最大长度由后端 schema 统一定义,并同步到 Admin UI 表单校验。图片字段只保存最终 URL,不接受 base64,不在首页内容记录中保存图片二进制。
## 接口详情
以下规则适用于三类资源;路径中的资源名和 ID 参数以接口清单为准。
### 获取列表
```http
GET /api/admin/home/experiences
Authorization: Bearer <admin-jwt>
```
团队共创和极境视界分别使用 `/api/admin/home/team-buildings`、`/api/admin/home/wild-archives`。
成功响应示例:
```json
{
"items": [
{
"id": "waterfall-descent",
"badge": "玩过推荐",
"category": "瀑降体验",
"title": "悬崖瀑降",
"englishTitle": "WATERFALL DESCENT",
"image": "https://example.test/assets/waterfall-descent.jpg",
"demandKeyword": "悬崖瀑降",
"isActive": true,
"sortOrder": 0,
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "2026-01-01T00:00:00Z"
}
]
}
```
接口返回启用和停用的全部记录;Admin UI 负责显示状态,不能让后端默认隐藏停用记录。
### 新增、编辑与删除
- `POST /api/admin/home/{resource}`:请求体为对应资源的 `Create` 类型,成功返回 `201` 和新建记录;未传 `sortOrder` 时追加到末尾。
- `PATCH /api/admin/home/{resource}/{id}`:请求体为对应资源的 `Patch` 类型,只更新提交字段;成功返回更新后的记录,不存在返回 `404`。
- `DELETE /api/admin/home/{resource}/{id}`:成功返回 `{ "id": "..." }`,删除后重新规范化同一资源剩余记录的 `sortOrder`。
其中 `{resource}` 只能是 `experiences`、`team-buildings` 或 `wild-archives`;对应路径参数分别为 `experienceId`、`teamBuildingId`、`archiveId`。删除不触发商品、订单、预订或线索级联操作。
### 调整顺序
```http
PATCH /api/admin/home/experiences/reorder
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
团队共创和极境视界分别使用 `/api/admin/home/team-buildings/reorder`、`/api/admin/home/wild-archives/reorder`。请求必须完整包含当前资源的全部 ID,不能重复:
```json
{ "itemIds": ["cave-exploration", "waterfall-descent"] }
```
成功响应为 `{ "items": [] }`,其中 `items` 是更新 `sortOrder` 后的完整记录列表。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400 HOME_REORDER_INVALID`。
## 当前 fallback 与迁移映射
迁移初始数据时应保留以下稳定 ID、字段值和当前数组顺序:
### 体验推荐
| 顺序 | ID | 标题 | 需求关键词 |
| --- | --- | --- | --- |
| 0 | `waterfall-descent` | 悬崖瀑降 | 悬崖瀑降 |
| 1 | `cave-exploration` | 森林&探洞 | 地心探险 |
`homeExperienceData.ts` 当前的 `normalizeHomeExperiences` 具有以下过渡行为:
- `isActive === false` 的 API 项被过滤。
- 缺少有效 `title` 的 API 项被过滤。
- 其余项目按 `sortOrder` 升序排列,未传排序时保持 API 原顺序。
- 文案和图片字段为空时使用当前 fallback 对应位置的值。
- API 没有有效项目时返回当前 `homeExperienceMocks` 的副本。
这些规则用于前台接入过渡,不应替代服务端校验。后端返回正式记录后,Admin UI 应提交完整字段,避免依赖位置 fallback。
### 团队共创
| 顺序 | ID | 标题 | 需求关键词 |
| --- | --- | --- | --- |
| 0 | `wild-challenge` | 山野挑战,共创极境 | 户外团建 |
| 1 | `canyon-teamwork` | 峡谷溯溪,默契同行 | 峡谷团建 |
| 2 | `village-gathering` | 苗寨共聚,认识彼此 | 贵州团建 |
### 极境视界
| 顺序 | ID | 标题 | 需求关键词 |
| --- | --- | --- | --- |
| 0 | `hundred-meter-descent` | 百米自降 | 悬崖瀑降 |
| 1 | `shilong-cave` | 石龙洞 | 地心探险 |
| 2 | `cliff-current` | 绝壁迎流 | 峡谷探险 |
| 3 | `canyon-streaming` | 峡谷溯溪 | 峡谷溯溪 |
三个 mock 文件中的图片 URL 只作为初始化内容来源。正式数据应通过媒体上传接口获得最终 URL;不要把 mock 文件中的远程图片地址当成图片存储协议。
## 图片与素材
Admin UI 使用现有素材上传接口获取图片 URL:
```http
POST /api/admin/media-assets/upload
```
建议首页内容使用 `group=home`,上传成功后将返回的 `url` 写入对应的 `image` 字段。接口只保存 URL,不接受 base64,也不创建 ProductImage 或商品图片关联。
## Admin UI 对接要求
1. 进入首页内容管理时分别请求三个列表接口,按 `sortOrder` 渲染对应分组。
2. 体验推荐表单维护 `badge`、`category`、`title`、`englishTitle`、`image` 和 `demandKeyword`。
3. 团队共创表单维护 `tag`、`title`、`description`、`image` 和 `demandKeyword`。
4. 极境视界表单维护 `title`、`image` 和 `demandKeyword`。
5. 三类资源都提供启用/停用、编辑、删除和上移/下移操作;排序时提交完整 ID 列表。
6. 每次变更成功后以接口返回的记录或列表更新本地状态,不直接假设本地修改已经保存。
7. 删除前要求二次确认;删除成功后接受服务端返回的重新排序结果。
8. 处理 `401`、`404`、`409`、`422` 和 `5xx`,保存、上传和排序进行中禁用重复提交。
9. 不在首页表单中出现 Product ID、ProductImage ID、价格、库存、订单或预订字段。
10. 预览点击行为使用 `demandKeyword`;该字段为空时禁止提交,而不是由前端猜测或拼接商品标识。
建议的 Admin UI API 封装函数:
```ts
getHomeExperiences();
createHomeExperience(input: HomeExperienceCreate);
updateHomeExperience(experienceId: string, input: HomeExperiencePatch);
deleteHomeExperience(experienceId: string);
reorderHomeExperiences(itemIds: string[]);
getHomeTeamBuildings();
createHomeTeamBuilding(input: HomeTeamBuildingCreate);
updateHomeTeamBuilding(teamBuildingId: string, input: HomeTeamBuildingPatch);
deleteHomeTeamBuilding(teamBuildingId: string);
reorderHomeTeamBuildings(itemIds: string[]);
getHomeWildArchives();
createHomeWildArchive(input: HomeWildArchiveCreate);
updateHomeWildArchive(archiveId: string, input: HomeWildArchivePatch);
deleteHomeWildArchive(archiveId: string);
reorderHomeWildArchives(itemIds: string[]);
```
当前首页的“查看更多”按钮固定跳转需求关键词“极境视界”,不属于 `HomeWildArchive` 记录字段;如果未来需要后台配置该按钮,应另行增加首页 CTA 配置契约。
## 前后台数据边界
- Admin API 返回启用和停用的完整记录,供管理端维护。
- 未来 Public API 只返回已发布且启用的首页内容;Public API 的响应字段应与本契约的渲染字段兼容,但不应暴露 `createdAt`、`updatedAt` 等管理元数据。
- MiniAPP 接入时再同步更新 `src/lib/types.ts`、`src/lib/data.ts`、首页组件和 `docs/public-api.md`;本次文档不改变现有 mock 消费路径。
- 后端不得把 `demandKeyword` 解析成 Product 或订单关联;如需线路、详情或预订能力,应另立领域契约。
## 后端落地边界
本契约落地时需要由 `WonderQ-Admin` 补充对应 ORM 模型、迁移、schema、路由、序列化和审计日志;由 `WonderQ-Admin-UI` 补充 API 类型、请求封装、首页内容列表、表单、图片上传和排序交互。当前后端没有 `/api/admin/home/*` 路由,Admin UI 也未接入这三类首页数据。
相关文档:
- [Admin API 主契约](./admin-api-requirements.md)
- [页面模块配置契约](./module-config-api.md)
- [Public API 契约](./public-api.md)
- [体验推荐数据](../WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts)
- [团队共创数据](../WonderQ-MiniAPP/src/pages/home/components/homeTeamBuildingData.ts)
- [极境视界数据](../WonderQ-MiniAPP/src/pages/home/components/homeWildArchivesData.ts)