feat: 新增客片案例与团队共创模块,重构玩法与配置

- 新增客片案例全流程功能,包含前台页面、管理端配置、后端API、数据库迁移与文档
- 新增团队共创详情页面与对应接口
- 重构玩法模块:将静态playData替换为动态API获取,拆分类型定义到playTypes.ts
- 优化vite构建配置与环境变量处理,调整详情页操作栏文案
- 更新全量相关文档与配图,新增客片案例、团队共创API文档
- 新增测试用例,完善数据归一化逻辑
- 调整路由配置与环境变量示例文件
This commit is contained in:
duanshuwen
2026-08-19 20:23:04 +08:00
parent cda8069630
commit 7ba92f3c5d
76 changed files with 7970 additions and 137 deletions

View File

@@ -11,7 +11,8 @@
3. `wanfa-api.md`
4. `concierge-api.md`
5. `detail-api.md`
6. `public-api.md`
6. `team-building-api.md`
7. `public-api.md`
管理前端开发:
@@ -21,13 +22,15 @@
4. `wanfa-api.md`
5. `concierge-api.md`
6. `detail-api.md`
7. `module-config-api.md`
7. `team-building-api.md`
8. `module-config-api.md`
MiniAPP 前台开发:
1. `integration-workflow.md`
2. `public-api.md`
3. `development-status.md`
2. `team-building-api.md`
3. `public-api.md`
4. `development-status.md`
## 文档清单
@@ -40,10 +43,12 @@ MiniAPP 前台开发:
| `backend-plan.md` | 当前 FastAPI 后端定位、业务模块、近期优先级和安全部署原则 | 后端 |
| `admin-api-requirements.md` | Admin UI 必需的 Admin API 主契约 | 后端、管理前端 |
| `module-config-api.md` | 页面模块配置 CRUD 的唯一细节契约 | 后端、管理前端 |
| `home-api.md` | 首页体验、团队共创和极境视界内容的 Admin API 补充契约 | 后端、管理前端 |
| `home-api.md` | 首页内容和玩法推荐关联的 Admin API 补充契约 | 后端、管理前端 |
| `wild-archives-api.md` | 客片案例列表、详情和图片字段契约 | 三端 |
| `wanfa-api.md` | 玩法分类和路线管理 API 的补充契约 | 后端、管理前端 |
| `concierge-api.md` | 管家顾问资料管理 API 的补充契约 | 后端、管理前端 |
| `detail-api.md` | 详情展示内容管理 API 的补充契约 | 后端、管理前端 |
| `team-building-api.md` | 团队共创详情字段、CRUD 与 Public 详情接口契约 | 三端 |
| `public-api.md` | MiniAPP 对接后端的 Public API 契约 | 后端、MiniAPP |
## 文档边界

View File

@@ -2,7 +2,7 @@
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。
>
> 状态:实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/concierge/components/conciergeTypes.ts` 定义管家顾问资料,以及管理端需要的查询、维护和排序接口。它不是 MiniAPP Public API 文档
> 状态:实现。本文档约定 `WonderQ-Admin`、`WonderQ-Admin-UI` 的管家管理接口,并记录 MiniAPP 使用的对应 Public API。
## 领域边界
@@ -19,7 +19,7 @@
- 订单、预订、商品和商品图片关联。
- 管家页面 Hero 文案和服务原则内容。
当前 `WonderQ-MiniAPP/src/pages/concierge/index.vue` 中的 Hero、原则和顾问数据仍是本地静态内容;本契约落地后,Admin UI 维护顾问数据MiniAPP 通过独立的 Public API 消费已发布内容
当前 `WonderQ-MiniAPP/src/pages/concierge/index.vue` 中的 Hero 和服务原则仍是本地内容;顾问卡片由 Admin UI 维护MiniAPP 通过独立的 Public API 消费已启用顾问
## 接口清单
@@ -33,6 +33,12 @@ API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。
| `DELETE` | `/api/admin/concierge/advisors/{advisorId}` | 删除管家顾问 |
| `PATCH` | `/api/admin/concierge/advisors/reorder` | 调整管家顾问展示顺序 |
MiniAPP Public API
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/api/public/concierge/advisors` | 获取已启用的管家顾问展示数据 |
## 通用约定
- 请求和响应使用 JSON字段使用 camelCase。
@@ -227,6 +233,22 @@ Content-Type: application/json
其中 `items` 为更新 `sortOrder` 后的 `ConciergeAdvisorRecord[]`。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400 CONCIERGE_REORDER_INVALID`
### MiniAPP Public API
```http
GET /api/public/concierge/advisors
```
无需鉴权。接口只返回 `isActive === true` 的顾问,按 `sortOrder` 升序排列,并移除 `id``sortOrder`、时间和其他管理字段:
```ts
type PublicConciergeResponse = {
advisors: ConciergeAdvisorContent[];
};
```
顾问列表为空时返回 `{ "advisors": [] }`。MiniAPP 应在请求期间展示 loading失败时展示错误和重试入口响应字段缺失时通过归一化函数过滤无效顾问。
## 图片与素材
Admin UI 使用现有素材上传接口获取图片 URL
@@ -292,7 +314,7 @@ const advisor: ConciergeAdvisor = {
## 后端落地边界
本契约落地时需要`WonderQ-Admin` 补充对应 ORM 模型、迁移、schema、路由、序列化和审计日志;由 `WonderQ-Admin-UI` 补充 API 类型、请求封装、管家列表、表单和排序交互。当前后端没有 `/api/admin/concierge/*` 路由Admin UI 的管家入口仍是空态MiniAPP 顾问数据仍在 `index.vue` 中静态定义
当前实现`WonderQ-Admin` 提供 ORM 模型、`0016_concierge_advisors` 迁移、schema、Admin/Public 路由、序列化和审计日志;由 `WonderQ-Admin-UI` 提供 API 类型、请求封装、管家列表、表单、图片上传、启停、排序和删除确认;由 `WonderQ-MiniAPP` 调用 Public API 并归一化顾问数据。Hero 和服务原则仍不在管家表中维护
相关文档:

View File

@@ -2,15 +2,16 @@
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。
>
> 状态:实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts`、`homeTeamBuildingData.ts` 和 `homeWildArchivesData.ts` 定义首页卡片内容,以及管理端需要的查询、维护和排序接口。它是首页内容的 Admin API 补充契约,不是 MiniAPP Public API 文档
> 状态:实现。本文件约定首页内容的 Admin API,并记录 MiniAPP 使用的对应 Public API。首页玩法推荐不复制玩法文案,而是关联 `WanfaCategory`
## 领域边界
首页内容管理维护类首页卡片
首页内容管理维护类首页内容
- 体验推荐:徽标、分类、标题、英文标题、封面和需求关键词。
- 团队共创:标签、标题、描述、封面需求关键词。
- 极境视界:案例标题、封面和需求关键词。
- 团队共创:标签、标题、描述、封面需求关键词、详情副标题和详情正文段落
- 极境视界:案例标题、封面、详情图片和需求关键词;首页卡片可进入客片案例详情
- 玩法推荐:已关联的玩法分类、启用状态和首页展示顺序;分类名称及路线由玩法领域维护。
本领域不负责:
@@ -20,7 +21,7 @@
`demandKeyword` 只是点击卡片后预填需求页的普通字符串,不是商品 ID、路线 ID、订单 ID也不建立数据库外键。
当前首页仍直接使用三个文件中的 mock 数组。Admin API 接入前,这些数组继续作为前台 fallback本文件不代表接口已经在 `WonderQ-Admin``WonderQ-Admin-UI` 中实现
当前首页通过 `GET /api/public/home` 消费四类内容;三个卡片 mock 数组仍作为接口失败、空响应或字段缺失时的前台 fallback玩法推荐无本地模拟数据时保持空态。Admin UI 通过本文件列出的 Admin API 维护正式数据
## 接口清单
@@ -43,6 +44,20 @@ API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。
| `PATCH` | `/api/admin/home/wild-archives/{archiveId}` | 编辑极境视界案例 |
| `DELETE` | `/api/admin/home/wild-archives/{archiveId}` | 删除极境视界案例 |
| `PATCH` | `/api/admin/home/wild-archives/reorder` | 调整极境视界案例顺序 |
| `GET` | `/api/admin/home/play-recommendations` | 获取首页已关联的玩法分类 |
| `POST` | `/api/admin/home/play-recommendations` | 关联一个玩法分类到首页 |
| `PATCH` | `/api/admin/home/play-recommendations/{recommendationId}` | 修改关联的启用状态或玩法分类 |
| `DELETE` | `/api/admin/home/play-recommendations/{recommendationId}` | 移除首页玩法分类关联,不删除玩法分类 |
| `PATCH` | `/api/admin/home/play-recommendations/reorder` | 调整首页玩法推荐顺序 |
MiniAPP Public API
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/api/public/home` | 获取已启用的首页内容和玩法推荐 |
| `GET` | `/api/public/home/team-buildings/{teamBuildingId}` | 获取单个团队共创详情 |
| `GET` | `/api/public/home/wild-archives` | 获取客片案例更多列表 |
| `GET` | `/api/public/home/wild-archives/{archiveId}` | 获取单个客片案例详情 |
## 通用约定
@@ -112,6 +127,8 @@ type HomeTeamBuilding = {
description: string;
image: string;
demandKeyword: string;
detailSubtitle: string;
detailParagraphs: string[];
};
type HomeTeamBuildingRecord = HomeTeamBuilding & {
@@ -138,6 +155,7 @@ type HomeWildArchive = {
id: string;
title: string;
image: string;
images: string[];
demandKeyword: string;
};
@@ -183,8 +201,11 @@ type HomeReorderRequest = {
| 团队共创 | `description` | `string` | 是 | 卡片描述,去除首尾空白后不得为空。 |
| 团队共创 | `image` | `string` | 是 | 可直接用于图片组件的封面 URL。 |
| 团队共创 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 |
| 团队共创 | `detailSubtitle` | `string` | 是 | 详情页首屏副标题;旧数据回退为 `description`。 |
| 团队共创 | `detailParagraphs` | `string[]` | 是 | 详情页正文段落;至少一段,旧数据回退为 `[description]`。 |
| 极境视界 | `title` | `string` | 是 | 案例标题,去除首尾空白后不得为空。 |
| 极境视界 | `image` | `string` | 是 | 案例图片 URL。 |
| 极境视界 | `image` | `string` | 是 | 案例封面图片 URL。 |
| 极境视界 | `images` | `string[]` | 是 | 案例详情图片 URL 列表,至少一张;第一张建议与封面一致。 |
| 极境视界 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 |
| 全部资源 | `isActive` | `boolean` | 响应必填 | 是否进入已发布前台内容,创建默认 `true`。 |
| 全部资源 | `sortOrder` | `number` | 响应必填 | 非负整数,数值越小越靠前。 |
@@ -254,6 +275,57 @@ Content-Type: application/json
成功响应为 `{ "items": [] }`,其中 `items` 是更新 `sortOrder` 后的完整记录列表。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400 HOME_REORDER_INVALID`
### MiniAPP Public API
```http
GET /api/public/home
```
无需鉴权。接口只返回 `isActive === true` 的记录,首页卡片和玩法推荐分别按 `sortOrder` 升序返回,并移除管理端状态、排序和审计时间字段:
```ts
type PublicHomeResponse = {
experiences: HomeExperience[];
teamBuildings: HomeTeamBuilding[];
wildArchives: HomeWildArchive[];
playRecommendations: HomeWanfaRecommendation[];
};
type HomeWanfaRecommendation = {
id: string;
categoryId: string;
label: string;
routes: WanfaRoute[];
};
type WanfaRoute = {
id: string;
title: string;
subtitle: string;
image: string;
routeCount: number;
demandKeyword: string;
};
```
四组列表始终返回数组没有可用内容时返回空数组。玩法推荐只返回启用的关联记录并展开关联分类当前的路线。MiniAPP 应在数据层归一化字段并保留对应 mock fallback不在页面组件内直接请求接口。
### 玩法推荐关联
`POST /api/admin/home/play-recommendations` 请求体:
```ts
type HomeWanfaRecommendationCreate = {
categoryId: string;
isActive?: boolean;
sortOrder?: number;
};
type HomeWanfaRecommendationPatch = Partial<HomeWanfaRecommendationCreate>;
```
Admin 响应记录包含 `id``categoryId``categoryLabel``routeCount``isActive``sortOrder``createdAt``updatedAt`。同一个玩法分类只能关联一次;分类不存在或已关联时分别返回 `404``409 HOME_WANFA_CATEGORY_DUPLICATE`。移除关联不会删除 `WanfaCategory` 或其路线。被首页推荐关联的玩法分类不能直接删除,需先移除首页关联。
## 当前 fallback 与迁移映射
迁移初始数据时应保留以下稳定 ID、字段值和当前数组顺序
@@ -306,16 +378,17 @@ POST /api/admin/media-assets/upload
## Admin UI 对接要求
1. 进入首页内容管理时分别请求三个列表接口,按 `sortOrder` 渲染对应分组。
1. 进入首页内容管理时请求三类首页卡片列表,并同时请求玩法分类和首页玩法推荐关联,按 `sortOrder` 渲染对应分组。
2. 体验推荐表单维护 `badge``category``title``englishTitle``image``demandKeyword`
3. 团队共创表单维护 `tag``title``description``image``demandKeyword`
4. 极境视界表单维护 `title``image``demandKeyword`
5. 三类资源都提供启用/停用、编辑、删除和上移/下移操作;排序时提交完整 ID 列表。
3. 团队共创表单维护 `tag``title``description``image``demandKeyword``detailSubtitle``detailParagraphs`;正文使用空行分隔多个段落
4. 极境视界表单维护 `title``image``images``demandKeyword`;详情图片按一行一个 URL 编辑
5. 三类首页卡片提供启用/停用、编辑、删除和上移/下移操作;玩法推荐提供启用/停用、移除和上移/下移操作;排序时提交完整 ID 列表。
6. 每次变更成功后以接口返回的记录或列表更新本地状态,不直接假设本地修改已经保存。
7. 删除前要求二次确认;删除成功后接受服务端返回的重新排序结果。
8. 处理 `401``404``409``422``5xx`,保存、上传和排序进行中禁用重复提交。
9. 不在首页表单中出现 Product ID、ProductImage ID、价格、库存、订单或预订字段。
10. 预览点击行为使用 `demandKeyword`;该字段为空时禁止提交,而不是由前端猜测或拼接商品标识。
11. 玩法推荐管理应先加载玩法分类,再加载首页关联;新增只能从未关联分类中选择,移除只取消关联,不能删除玩法分类。
建议的 Admin UI API 封装函数:
@@ -337,20 +410,51 @@ createHomeWildArchive(input: HomeWildArchiveCreate);
updateHomeWildArchive(archiveId: string, input: HomeWildArchivePatch);
deleteHomeWildArchive(archiveId: string);
reorderHomeWildArchives(itemIds: string[]);
getHomeWanfaRecommendations();
createHomeWanfaRecommendation(input: HomeWanfaRecommendationCreate);
updateHomeWanfaRecommendation(recommendationId: string, input: HomeWanfaRecommendationPatch);
deleteHomeWanfaRecommendation(recommendationId: string);
reorderHomeWanfaRecommendations(itemIds: string[]);
```
当前首页的“查看更多”按钮固定跳转需求关键词“极境视界”,不属于 `HomeWildArchive` 记录字段;如果未来需要后台配置该按钮,应另行增加首页 CTA 配置契约
当前首页的“查看更多”按钮跳转客片案例列表;首页案例卡片跳转 `/pages/wild-archives/detail?id={archiveId}`,不再跳转需求页
## 前后台数据边界
- Admin API 返回启用和停用的完整记录,供管理端维护。
- 未来 Public API 只返回已发布且启用的首页内容Public API 的响应字段应与本契约的渲染字段兼容,但不应暴露 `createdAt``updatedAt` 等管理元数据。
- MiniAPP 接入时再同步更新 `src/lib/types.ts``src/lib/data.ts`、首页组件和 `docs/public-api.md`;本次文档不改变现有 mock 消费路径
- MiniAPP 已在 `src/lib/api.ts``src/lib/types.ts``src/lib/store.ts` 接入 Public API首页组件通过共享状态消费归一化后的四类内容玩法推荐按关联分类展示
- 后端不得把 `demandKeyword` 解析成 Product 或订单关联;如需线路、详情或预订能力,应另立领域契约。
## 团队共创详情
团队共创首页卡片点击后跳转 MiniAPP `/pages/team-buildings/detail?id={teamBuildingId}`,详情页再按 ID 请求 Public API。首页接口只返回卡片摘要不携带正文段落。
```http
GET /api/public/home/team-buildings/{teamBuildingId}
```
成功响应在首页摘要基础上增加详情字段:
```ts
type PublicHomeTeamBuildingDetail = HomeTeamBuilding & {
detailSubtitle: string;
detailParagraphs: string[];
};
```
规则:
- 仅返回 `isActive === true` 的团队共创;不存在或已停用返回 `404`
- `detailSubtitle` 为空时回退为 `description`
- `detailParagraphs` 为空时回退为 `[description]`
- MiniAPP 请求失败时按 `teamBuildingId` 使用本地模拟详情,并提示当前为模拟数据;找不到对应 fallback 时展示未找到状态。
- 详情封面复用 `image` 字段,不新增独立 hero 图片或详情表。
## 后端落地边界
本契约落地时需要`WonderQ-Admin` 补充对应 ORM 模型、迁移、schema、路由、序列化和审计日志`WonderQ-Admin-UI` 补充 API 类型、请求封装、首页内容列表、表单、图片上传和排序交互。当前后端没有 `/api/admin/home/*` 路由Admin UI 也未接入这三类首页数据
当前实现`WonderQ-Admin` 提供三张首页内容表、`HomeWanfaRecommendation` 关联表、`0017_home_content``0018_home_wanfa_recommendations``0019_home_wild_archive_images``0020_home_team_building_details` 迁移、schema、Admin/Public 路由、序列化和审计日志;由 `WonderQ-Admin-UI` 提供首页玩法推荐入口、分类关联、启停、移除、排序、客片详情图片和团队共创详情维护;由 `WonderQ-MiniAPP` 调用 `/api/public/home`、团队共创详情和客片案例列表/详情接口
相关文档:
@@ -359,4 +463,5 @@ reorderHomeWildArchives(itemIds: string[]);
- [Public API 契约](./public-api.md)
- [体验推荐数据](../WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts)
- [团队共创数据](../WonderQ-MiniAPP/src/pages/home/components/homeTeamBuildingData.ts)
- [团队共创详情接口](./team-building-api.md)
- [极境视界数据](../WonderQ-MiniAPP/src/pages/home/components/homeWildArchivesData.ts)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,6 +1,6 @@
# WonderQ MiniAPP Public API
本文档是 `WonderQ-MiniAPP` 当前使用的 Public API 契约。接口提供站点内容、登录和出行需求提交能力。
本文档是 `WonderQ-MiniAPP` 当前使用的 Public API 契约。接口提供站点内容、首页卡片、玩法展示、管家展示、登录和出行需求提交能力。
## 基础约定
@@ -15,6 +15,12 @@
| ------ | ------------------------------ | ------------ | ------------------ |
| `GET` | `/health` | 否 | 服务健康检查 |
| `GET` | `/api/public/site-config` | 否 | 获取启用的站点内容 |
| `GET` | `/api/public/home` | 否 | 获取已启用的首页内容和玩法推荐 |
| `GET` | `/api/public/home/team-buildings/{teamBuildingId}` | 否 | 获取团队共创详情 |
| `GET` | `/api/public/home/wild-archives` | 否 | 获取客片案例更多列表 |
| `GET` | `/api/public/home/wild-archives/{archiveId}` | 否 | 获取客片案例详情和图片 |
| `GET` | `/api/public/wanfa/categories` | 否 | 获取玩法分类和路线 |
| `GET` | `/api/public/concierge/advisors` | 否 | 获取已启用的管家顾问 |
| `POST` | `/api/public/auth/phone-login` | 否 | 微信手机号登录 |
| `GET` | `/api/public/auth/me` | Customer JWT | 获取当前客户 |
| `POST` | `/api/public/leads` | 否 | 提交出行需求 |
@@ -36,6 +42,153 @@ type SiteConfig = {
Public 响应只返回启用内容,其余模块按 `isActive` 过滤。
## 玩法展示
### `GET /api/public/wanfa/categories`
无需鉴权。接口按后台维护的分类和路线顺序返回 MiniAPP 玩法页所需的最小展示字段,不返回后台排序、审计和时间字段。
成功响应:
```ts
type PublicWanfaResponse = {
categories: PublicWanfaCategory[];
};
type PublicWanfaCategory = {
id: string;
label: string;
routes: PublicWanfaRoute[];
};
type PublicWanfaRoute = {
id: string;
title: string;
subtitle: string;
image: string;
routeCount: number;
demandKeyword: string;
};
```
MiniAPP 使用 `demandKeyword` 作为需求页预填关键词;接口失败或响应为空时,玩法页展示对应的错误、重试或空态,不再读取 `playData.ts` 模拟数据。
## 管家展示
### `GET /api/public/concierge/advisors`
无需鉴权。接口只返回后台启用的管家顾问,按后台维护顺序返回;不返回管理端 ID、启停状态、排序元数据和时间字段。
```ts
type PublicConciergeDetail = {
icon: string;
label: string;
};
type PublicConciergeAdvisor = {
avatar: string;
name: string;
role: string;
details: PublicConciergeDetail[];
qrImage: string;
};
type PublicConciergeResponse = {
advisors: PublicConciergeAdvisor[];
};
```
成功响应示例:
```json
{
"advisors": [
{
"avatar": "https://example.test/assets/advisor-avatar.jpg",
"name": "示例顾问",
"role": "SENIOR TRAVEL ADVISOR",
"details": [{ "icon": "calendar", "label": "服务经验8年" }],
"qrImage": "https://example.test/assets/advisor-qr.png"
}
]
}
```
无可用顾问时返回 `{ "advisors": [] }`。MiniAPP 应处理 loading、错误、重试和空态不能依赖固定顾问姓名或本地模拟数组。
## 首页内容
### `GET /api/public/home`
无需鉴权。接口只返回后台启用的首页体验推荐、团队共创和极境视界内容,按后台排序返回,不暴露管理元数据。
```ts
type PublicHomeResponse = {
experiences: HomeExperience[];
teamBuildings: HomeTeamBuilding[];
wildArchives: HomeWildArchive[];
playRecommendations: HomeWanfaRecommendation[];
};
type HomeWanfaRecommendation = {
id: string;
categoryId: string;
label: string;
routes: WanfaRoute[];
};
type WanfaRoute = {
id: string;
title: string;
subtitle: string;
image: string;
routeCount: number;
demandKeyword: string;
};
type HomeExperience = {
id: string;
badge: string;
category: string;
title: string;
englishTitle: string;
image: string;
demandKeyword: string;
};
type HomeTeamBuilding = {
id: string;
tag: string;
title: string;
description: string;
image: string;
demandKeyword: string;
};
type PublicHomeTeamBuildingDetail = HomeTeamBuilding & {
detailSubtitle: string;
detailParagraphs: string[];
};
type HomeWildArchive = {
id: string;
title: string;
image: string;
demandKeyword: string;
photoCount: number;
};
```
无可用内容时,`experiences``teamBuildings``wildArchives``playRecommendations` 均返回空数组。玩法推荐由首页内容域关联玩法分类后生成接口只返回启用的关联及分类当前路线MiniAPP 接口失败或字段不完整时使用首页对应 fallback玩法推荐缺省为空态。
客片案例更多列表使用 `GET /api/public/home/wild-archives`,返回同样的摘要字段;详情使用 `GET /api/public/home/wild-archives/{archiveId}`,在摘要字段基础上增加 `images: string[]`。首页卡片点击详情,`查看更多` 点击案例列表,不再跳转需求页。
### 团队共创详情
`GET /api/public/home/team-buildings/{teamBuildingId}` 无需鉴权,只返回启用的团队共创详情。响应包含首页摘要字段,以及 `detailSubtitle``detailParagraphs`。不存在或已停用返回 `404`
详情页接口失败时MiniAPP 按 ID 使用 `homeTeamBuildingData.ts` 中的网络图片和模拟正文 fallback并显示接口不可用提示首页 `/api/public/home` 不返回 `detailParagraphs`,避免首页请求携带长正文。
### 通用字段
站点模块通常包含 `id``createdAt``updatedAt``isActive``sortOrder`。客户端按 `sortOrder` 消费排序模块,不依赖固定 ID。

155
docs/team-building-api.md Normal file
View File

@@ -0,0 +1,155 @@
# 团队共创详情接口契约
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`、`WonderQ-MiniAPP`。
> 目标:复用 `HomeTeamBuilding` 表,维护首页团队共创卡片和沉浸式详情页内容。
## 领域边界
- 团队共创卡片和详情共用一条 `HomeTeamBuilding` 记录。
- 详情封面复用 `image`,不新增独立详情表或第二张 hero 图片。
- `detailSubtitle` 是详情首屏副标题。
- `detailParagraphs` 是按阅读顺序保存的正文段落数组。
- Admin UI 使用正文 textarea 的“空行分隔段落”约定;后端保存前会清洗首尾空白和空段落。
- 首页卡片仍保留 `demandKeyword`,但点击行为改为详情页;该字段仅作为其他需求入口的普通文案,不建立商品或订单外键。
## 数据类型
```ts
type HomeTeamBuilding = {
id: string;
tag: string;
title: string;
description: string;
image: string;
demandKeyword: string;
detailSubtitle: string;
detailParagraphs: string[];
isActive: boolean;
sortOrder: number;
createdAt: string;
updatedAt: string;
};
type HomeTeamBuildingCreate = {
tag: string;
title: string;
description: string;
image: string;
demandKeyword: string;
detailSubtitle?: string;
detailParagraphs?: string[];
isActive?: boolean;
sortOrder?: number;
};
type HomeTeamBuildingPatch = Partial<HomeTeamBuildingCreate>;
```
兼容规则:旧客户端不提交详情字段时,后端使用模型默认值;迁移 `0020_home_team_building_details` 会把已有记录的副标题回填为 `description`,正文回填为 `[description]`。Public 序列化时仍会对空值做相同 fallback。
## Admin API
前缀为 `/api/admin`,需要 `Authorization: Bearer <admin-jwt>`。既有列表、创建、编辑、删除和排序路径保持不变:
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `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` | 调整团队共创顺序 |
创建或编辑请求示例:
```json
{
"tag": "户外挑战",
"title": "山野挑战,共创极境",
"description": "洞穴、瀑降与协作,适合 10-30 人。",
"image": "https://example.test/assets/team-building.jpg",
"demandKeyword": "户外团建",
"detailSubtitle": "越过山丘,向来处去",
"detailParagraphs": [
"真正的贵州,从未被写进流水线的攻略里。",
"把会议室换成山野,让团队重新认识彼此。"
],
"isActive": true
}
```
约束:
- 文本字段去除首尾空白后不得为空;详情副标题最大 160 字,正文最多 30 段。
- `detailParagraphs` 保存时过滤空段落;编辑接口显式传空正文会返回 `422`
- 图片只接受 HTTP(S) URL排序值为非负整数。
- 列表接口返回启用和停用记录,按 `sortOrder` 升序;删除和排序继续写审计日志。
- 迁移只新增两列,不改变旧字段和现有接口路径;本次实现不自动执行迁移。
## Public API
### 首页摘要
```http
GET /api/public/home
```
首页响应中的 `teamBuildings` 只返回卡片摘要:
```ts
type PublicHomeTeamBuildingSummary = {
id: string;
tag: string;
title: string;
description: string;
image: string;
demandKeyword: string;
};
```
### 详情
```http
GET /api/public/home/team-buildings/{teamBuildingId}
```
无需鉴权。成功响应:
```ts
type PublicHomeTeamBuildingDetail = PublicHomeTeamBuildingSummary & {
detailSubtitle: string;
detailParagraphs: string[];
};
```
错误响应:
| 状态码 | 场景 |
| --- | --- |
| `404` | ID 不存在或团队共创已停用 |
| `5xx` | 服务端异常MiniAPP 进入本地 fallback |
响应中的 `detailSubtitle` 为空时返回 `description``detailParagraphs` 为空时返回 `[description]`。Public API 不返回管理端状态、排序和审计字段。
## MiniAPP 联调约定
1. 首页通过 `/api/public/home` 加载团队共创卡片。
2. `openTeamBuilding(item)` 调用 `goTeamBuildingDetail(item.id)`,跳转 `/pages/team-buildings/detail?id={teamBuildingId}`
3. 详情页调用 `fetchPublicTeamBuildingDetail(teamBuildingId)`,成功后通过 `normalizeHomeTeamBuildingDetail` 归一化。
4. 请求失败时按 ID 查找 `homeTeamBuildingFallback`;命中则展示模拟网络图片、标题、副标题和正文,并提示“接口暂不可用,当前展示模拟数据”。
5. 无 ID、ID 不存在且无 fallback 时展示未找到状态,并提供返回首页操作。
6. 页面必须覆盖 loading、接口失败、空正文和未找到状态详情布局使用 TailwindCSS不新增页面级自定义样式。
## 三端联调顺序
1.`WonderQ-Admin` 执行 `python -m alembic upgrade head`,仅在确认目标数据库后执行。
2. 启动后端并验证 `GET /health`
3. 在 Admin UI 创建或编辑团队共创,填写详情副标题和正文;正文 textarea 使用空行分段。
4. 验证 Admin API 返回详情字段Public 首页只返回摘要。
5. 在 MiniAPP 首页点击团队共创卡片,确认 URL 携带正确 ID。
6. 验证详情接口成功展示最新内容;停止后端后确认按 ID fallback 并显示提示。
## 变更文件
- 后端:`WonderQ-Admin/app/models.py``app/schemas.py``app/serializers.py``app/routers/public.py``alembic/versions/0020_home_team_building_details.py`
- 管理端:`WonderQ-Admin-UI/src/api.ts``src/pages/structure/HomeContentPage.tsx``src/components/admin/HomeContentEditor.tsx``src/components/admin/HomeContentCardRail.tsx`
- 前台:`WonderQ-MiniAPP/src/pages/home/components/homeTeamBuildingData.ts``src/lib/types.ts``src/lib/api.ts``src/lib/navigation.ts``src/pages/home/index.vue``src/pages/team-buildings/detail.vue``src/pages.json`

View File

@@ -2,7 +2,9 @@
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。
>
> 状态:实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/play/components/playData.ts` 定义玩法分类和路线的数据结构,以及管理端需要的查询、维护和排序接口。它不是 MiniAPP Public API 文档。
> 状态:实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/play/components/playData.ts` 定义玩法分类和路线的数据结构,以及管理端需要的查询、维护和排序接口。它不是 MiniAPP Public API 文档。
当前实现:`WonderQ-Admin` 通过迁移 `0015_wanfa` 创建 `WanfaCategory``WanfaRoute` 表并导入稳定初始 ID`WonderQ-Admin-UI` 已接入分类和路线的查询、新增、编辑、删除及排序操作。
## 领域边界
@@ -11,6 +13,7 @@
- 分类名称和展示顺序。
- 路线标题、副标题、封面、路线数量和需求关键词。
- 分类与路线的新增、编辑、删除和排序。
- 首页玩法推荐与分类的关联由首页内容域维护,本域只提供可被关联的分类和路线数据。
玩法领域不负责:
@@ -47,6 +50,7 @@ API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。
- 分类和路线的数组顺序就是管理端和前台的展示顺序,接口不要求前端依赖 `sortOrder` 字段。
- 空集合返回 `[]`,不能返回 `null` 或省略字段。
- 失败响应沿用 Admin API 约定,包含 `message``code` 和可选的 `details`
- 被首页玩法推荐关联的分类不能直接删除;需先调用首页内容域的移除关联接口,否则返回 `409 WANFA_CATEGORY_RECOMMENDED`
## 数据类型
@@ -316,7 +320,7 @@ reorderWanfaRoutes(categoryId: string, itemIds: string[]);
## 后端落地边界
本契约落地时需要`WonderQ-Admin` 补充对应 ORM 模型、迁移、schema、路由、序列化和审计日志`WonderQ-Admin-UI` 补充 API 类型、请求封装、表单和排序交互。接口实现前不要把 `playData.ts` 的静态数据误认为已存在的数据库或 API 数据。
玩法数据`WonderQ-Admin` `WanfaCategory``WanfaRoute` ORM 模型和 Admin API 负责持久化;`WonderQ-Admin-UI` 负责 API 类型、请求封装、表单、删除确认和排序交互。`playData.ts` 仅作为迁移初始数据来源,不能视为数据库或 API 数据。
相关文档:

147
docs/wild-archives-api.md Normal file
View File

@@ -0,0 +1,147 @@
# 客片案例 API 契约
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`、`WonderQ-MiniAPP`。
>
> 目标:维护“极境视界”首页卡片,并提供客片案例更多列表和案例详情页面。
## 领域边界
客片案例是首页内容领域的一类展示内容不属于商品、Product、ProductImage、订单或预订领域。
- `image` 是首页卡片封面。
- `images` 是详情页纵向展示的图片 URL 列表。
- `demandKeyword` 仅为兼容已有需求入口的普通文案,本次案例卡片和详情不跳转需求页。
- 图片只保存 URL不保存 base64不建立商品图片关联。
## 数据类型
```ts
type WildArchiveRecord = {
id: string;
title: string;
image: string;
images: string[];
demandKeyword: string;
isActive: boolean;
sortOrder: number;
createdAt: string;
updatedAt: string;
};
type PublicWildArchiveSummary = {
id: string;
title: string;
image: string;
demandKeyword: string;
photoCount: number;
};
type PublicWildArchiveDetail = PublicWildArchiveSummary & {
images: string[];
};
type WildArchiveCreate = {
title: string;
image: string;
images?: string[];
demandKeyword: string;
isActive?: boolean;
sortOrder?: number;
};
type WildArchivePatch = Partial<WildArchiveCreate>;
```
`images` 未传时服务端兼容为 `[image]`;传入后至少包含一张合法 `http``https` 图片 URL最多 30 张。`photoCount` 始终由服务端根据详情图片列表返回。
## Admin API
所有 Admin API 需要 `Authorization: Bearer <admin-jwt>`
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `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` | 按完整 ID 列表调整顺序 |
新增示例:
```json
{
"title": "石龙洞——客片案例",
"image": "https://example.test/cover.jpg",
"images": [
"https://example.test/cover.jpg",
"https://example.test/cave-1.jpg"
],
"demandKeyword": "地心探险",
"isActive": true
}
```
Admin UI 的“极境视界”表单必须同时维护封面和详情图片,详情图片使用一行一个 URL删除需要二次确认排序提交完整 `itemIds`
## Public API
### 获取更多列表
```http
GET /api/public/home/wild-archives
```
无需鉴权。只返回 `isActive === true` 的记录,按 `sortOrder` 升序:
```json
{
"items": [
{
"id": "shilong-cave",
"title": "石龙洞——客片案例",
"image": "https://example.test/cover.jpg",
"demandKeyword": "地心探险",
"photoCount": 5
}
]
}
```
### 获取案例详情
```http
GET /api/public/home/wild-archives/{archiveId}
```
无需鉴权。停用或不存在的案例返回 `404`,成功返回:
```json
{
"id": "shilong-cave",
"title": "石龙洞——客片案例",
"image": "https://example.test/cover.jpg",
"demandKeyword": "地心探险",
"photoCount": 5,
"images": [
"https://example.test/cover.jpg",
"https://example.test/cave-1.jpg"
]
}
```
## MiniAPP 页面约定
- `pages/wild-archives/index`:客片案例更多列表。
- `pages/wild-archives/detail?id={archiveId}`:客片案例详情。
- 首页“极境视界”卡片调用详情页;“查看更多”调用列表页。
- 页面数据层优先调用 Public API接口失败时列表和详情允许使用 `homeWildArchivesData.ts` 中的网络图片 mock 数据。
- 详情页按 `images` 顺序纵向展示,保留图片原始比例;空列表时展示空态,不渲染破损图片。
## 迁移与验证
- 数据模型:`HomeWildArchive.images` 使用 JSONB 保存 URL 数组。
- 迁移:`WonderQ-Admin/alembic/versions/0019_home_wild_archive_images.py`
- 迁移会将历史 `image` 自动转为第一张详情图片,保证旧数据可打开详情页。
- 三端联调顺序:执行迁移 → 启动 Admin API → 配置 Admin UI 案例 → 访问 MiniAPP 列表和详情。
相关契约:[首页内容 API](./home-api.md)、[Public API](./public-api.md)。