Files
WonderQ-Project/docs/wanfa-api.md
duanshuwen e8eb8614f0 docs: 清理过时文档并更新管理端名称
删除home-api.md、team-building-api.md等废弃文档
统一替换所有文档中的`WonderQ-Admin-UI`为`WonderQ-Admin-UI-Vue`
更新README.md与联调文档的内容与路径
修正各API文档的过时描述,移除废弃的迁移说明与本地mock依赖
整理docs/README.md的文档索引,优化阅读路径
2026-08-26 19:41:15 +08:00

338 lines
11 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-Vue`。
>
> 状态:已实现契约。本文件定义玩法分类和路线的 Admin API不替代 MiniAPP Public API 文档。
当前实现:`WonderQ-Admin` 提供 `WanfaCategory``WanfaRoute` 的查询、新增、编辑、删除及排序接口;`WonderQ-Admin-UI-Vue` 已接入对应管理操作。
所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data``null`
路线接口只维护分类和路线摘要字段。路线详情不写入 `WanfaRoute`,由独立 `DetailRecord` 通过 `docs/detail-api.md` 管理,详情记录的 `key` 等于路线 ID。首页玩法推荐和玩法页路线点击后统一跳转 `/pages/detail/index?routeId={route.id}`;无关联路线时才回退到需求页。
分类和路线的正式 `id` 均为服务端生成的稳定 UUID 字符串;不要在序列化时临时生成 ID也不要用标题或数组下标代替 ID。
## 领域边界
玩法管理只维护玩法分类和路线卡片内容:
- 分类名称和展示顺序。
- 路线标题、副标题、封面、路线数量和需求关键词。
- 分类与路线的新增、编辑、删除和排序。
- 首页玩法推荐与分类的关联由首页内容域维护,本域只提供可被关联的分类和路线数据。
玩法领域不负责:
- 商品、商品详情、预订或订单。
- 目的地实体和目的地维护。
- 线索创建或线索跟进。
- Product、ProductImage 或其他已移除商品关联表。
`routeCount` 只是前台卡片展示数量,不是 Product 表的外键或实时关联统计。
## 接口清单
API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/api/admin/wanfa/categories` | 获取全部玩法分类及其路线 |
| `POST` | `/api/admin/wanfa/categories` | 新增玩法分类 |
| `PATCH` | `/api/admin/wanfa/categories/{categoryId}` | 编辑玩法分类 |
| `DELETE` | `/api/admin/wanfa/categories/{categoryId}` | 删除玩法分类 |
| `PATCH` | `/api/admin/wanfa/categories/reorder` | 调整玩法分类顺序 |
| `POST` | `/api/admin/wanfa/categories/{categoryId}/routes` | 新增分类路线 |
| `PATCH` | `/api/admin/wanfa/categories/{categoryId}/routes/{routeId}` | 编辑分类路线 |
| `DELETE` | `/api/admin/wanfa/categories/{categoryId}/routes/{routeId}` | 删除分类路线 |
| `PATCH` | `/api/admin/wanfa/categories/{categoryId}/routes/reorder` | 调整分类内路线顺序 |
## 通用约定
- 请求和响应使用 JSON字段使用 camelCase。
- 所有管理接口需要 `Authorization: Bearer <admin-jwt>`
- 创建、编辑、删除和排序成功后写入审计日志,再提交事务。
- 变更接口返回最新变更对象;排序接口返回排序后的 `items`
- ID 由后端生成并作为稳定 UUID 返回;管理端必须保存并复用接口返回的 ID。
- 分类和路线的数组顺序就是管理端和前台的展示顺序,接口不要求前端依赖 `sortOrder` 字段。
- 空集合返回 `[]`,不能返回 `null` 或省略字段。
- 成功结果放入 `data`;失败返回数字 `code``msg``data: null`,可选 `errorCode``details`
- 被首页玩法推荐关联的分类不能直接删除;需先调用首页内容域的移除关联接口,否则返回 `409 WANFA_CATEGORY_RECOMMENDED`
## 数据类型
以下类型与 Public API 的前台展示模型保持字段兼容。管理端接口不应向前台模型增加商品 ID、预订 ID 或数据库关联字段。
```ts
type WanfaConfig = {
categories: WanfaCategory[];
};
type WanfaCategory = {
id: string;
label: string;
routes: WanfaRoute[];
};
type WanfaRoute = {
id: string;
title: string;
subtitle: string;
image: string;
routeCount: number;
demandKeyword: string;
};
type WanfaCategoryCreate = {
label: string;
};
type WanfaCategoryPatch = {
label?: string;
};
type WanfaRouteCreate = {
title: string;
subtitle: string;
image: string;
routeCount: number;
demandKeyword: string;
};
type WanfaRoutePatch = Partial<WanfaRouteCreate>;
type WanfaReorderRequest = {
itemIds: string[];
};
```
## 字段约束
| 字段 | 类型 | 必填 | 约束和用途 |
| --- | --- | --- | --- |
| `category.id` | `string` | 响应必填 | 分类稳定标识,服务端生成 UUID创建后在列表、详情、排序和删除请求中保持不变。 |
| `category.label` | `string` | 是 | 左侧分类显示名称,去除首尾空白后不得为空。 |
| `category.routes` | `WanfaRoute[]` | 响应必填 | 当前分类下的路线,按展示顺序返回。 |
| `route.id` | `string` | 响应必填 | 路线稳定标识,服务端生成 UUID详情 `key` 与该 ID 一致。 |
| `route.title` | `string` | 是 | 路线卡片标题,去除首尾空白后不得为空。 |
| `route.subtitle` | `string` | 是 | 路线卡片副标题或目的地说明。 |
| `route.image` | `string` | 是 | 可直接用于图片组件的封面 URL。 |
| `route.routeCount` | `number` | 是 | 卡片右下角展示的路线数量,服务端应校验为不小于 0 的整数。 |
| `route.demandKeyword` | `string` | 是 | 跳转需求页时可使用的预填关键词,去除首尾空白后不得为空。 |
字段最大长度应由后端 schema 统一定义,并同步到 Admin UI 表单校验;在未形成统一长度常量前,前端不能通过截断文本代替服务端校验。
## 接口详情
### 获取分类及路线
```http
GET /api/admin/wanfa/categories
Authorization: Bearer <admin-jwt>
```
成功响应:
```json
{
"code": 200,
"msg": "success",
"data": {
"categories": [
{
"id": "family-route",
"label": "亲子路线",
"routes": [
{
"id": "family-water",
"title": "亲子玩水",
"subtitle": "贵州·轻松节奏与自然课堂",
"image": "https://example.test/assets/family-water.jpg",
"routeCount": 4,
"demandKeyword": "亲子玩水"
}
]
}
]
}
}
```
### 新增分类
```http
POST /api/admin/wanfa/categories
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
请求:
```json
{ "label": "亲子路线" }
```
成功返回 `201``data` 内的新分类对象,初始 `routes``[]`,并追加到分类列表末尾。
### 编辑分类
```http
PATCH /api/admin/wanfa/categories/{categoryId}
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
请求只允许修改分类名称:
```json
{ "label": "家庭路线" }
```
成功返回 `data` 内的更新后 `WanfaCategory`。不存在的分类返回 `404`,业务码为 `WANFA_CATEGORY_NOT_FOUND`
### 删除分类
```http
DELETE /api/admin/wanfa/categories/{categoryId}
Authorization: Bearer <admin-jwt>
```
为避免误删路线,分类仍包含路线时不得级联删除,应返回 `409`,业务码为 `WANFA_CATEGORY_NOT_EMPTY`。删除前由 Admin UI 提示先移除分类内路线。删除成功返回:
```json
{
"code": 200,
"msg": "success",
"data": { "id": "family-route" }
}
```
### 分类排序
```http
PATCH /api/admin/wanfa/categories/reorder
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
请求必须完整包含当前全部分类 ID不能重复
```json
{ "itemIds": ["photo-route", "family-route", "healing-route"] }
```
成功返回:
```json
{
"code": 200,
"msg": "success",
"data": { "items": [] }
}
```
其中 `items` 为排序后的 `WanfaCategory[]`。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400`,业务码为 `WANFA_CATEGORY_REORDER_INVALID`
### 新增路线
```http
POST /api/admin/wanfa/categories/{categoryId}/routes
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
请求:
```json
{
"title": "亲子玩水",
"subtitle": "贵州·轻松节奏与自然课堂",
"image": "https://example.test/assets/family-water.jpg",
"routeCount": 4,
"demandKeyword": "亲子玩水"
}
```
成功返回 `201``data` 内新建的 `WanfaRoute`,并追加到对应分类路线末尾。分类不存在返回 `404`,业务码为 `WANFA_CATEGORY_NOT_FOUND`
### 编辑路线
```http
PATCH /api/admin/wanfa/categories/{categoryId}/routes/{routeId}
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
请求体为 `WanfaRoutePatch`,只更新提交的字段。成功返回 `data` 内更新后的 `WanfaRoute`;分类或路线不存在时分别返回 `404`,业务码为 `WANFA_CATEGORY_NOT_FOUND``WANFA_ROUTE_NOT_FOUND`
### 删除路线
```http
DELETE /api/admin/wanfa/categories/{categoryId}/routes/{routeId}
Authorization: Bearer <admin-jwt>
```
成功返回 `data: { "id": "..." }`。删除后,分类内路线保持原有相对顺序。
### 分类内路线排序
```http
PATCH /api/admin/wanfa/categories/{categoryId}/routes/reorder
Authorization: Bearer <admin-jwt>
Content-Type: application/json
```
请求必须完整包含该分类当前全部路线 ID不能重复
```json
{ "itemIds": ["family-grassland", "family-water", "family-village"] }
```
成功返回排序后的路线:
```json
{
"code": 200,
"msg": "success",
"data": { "items": [] }
}
```
分类不存在返回 `404`,业务码为 `WANFA_CATEGORY_NOT_FOUND`;路线 ID 不完整、重复或不属于该分类时返回 `400`,业务码为 `WANFA_ROUTE_REORDER_INVALID`
图片字段保存最终 URL管理端通过媒体上传接口获取 URL 后再提交 `image`。本地 fallback 数据不属于 Admin API 契约。
## Admin UI 对接要求
Admin UI 应按以下方式调用:
1. 进入玩法管理页时调用 `GET /api/admin/wanfa/categories`,以返回数组顺序渲染分类和路线。
2. 添加分类调用 `POST /categories`;编辑和删除分类分别调用对应 `PATCH``DELETE`
3. 添加、编辑和删除路线使用分类嵌套路由。
4. 上移或下移分类时提交完整分类 ID 列表;上移或下移路线时提交完整路线 ID 列表。
5. 每次变更成功后以接口返回数据更新本地状态;必要时重新请求列表,不直接拼接数据库字段。
6. 删除非空分类前展示阻止性提示,不自动级联删除路线。
7. 处理 `401``404``409``422``5xx`,并在保存中禁用重复提交。
建议的 Admin UI API 封装函数:
```ts
getWanfaCategories();
createWanfaCategory(input: WanfaCategoryCreate);
updateWanfaCategory(categoryId: string, input: WanfaCategoryPatch);
deleteWanfaCategory(categoryId: string);
reorderWanfaCategories(itemIds: string[]);
createWanfaRoute(categoryId: string, input: WanfaRouteCreate);
updateWanfaRoute(categoryId: string, routeId: string, input: WanfaRoutePatch);
deleteWanfaRoute(categoryId: string, routeId: string);
reorderWanfaRoutes(categoryId: string, itemIds: string[]);
```
## 后端落地边界
玩法数据由 `WonderQ-Admin``WanfaCategory``WanfaRoute` ORM 模型和 Admin API 负责持久化;`WonderQ-Admin-UI-Vue` 负责 API 类型、请求封装、表单、删除确认和排序交互。本地 fallback 数据不能视为数据库或 API 数据。
相关文档:
- [Admin API 主契约](./admin-api-requirements.md)
- [页面模块配置契约](./module-config-api.md)