feat(route-detail): 实现路线详情独立管理全链路功能
添加`DetailRecord`数据库模型、迁移脚本及完整的CRUD管理接口,将路线详情从玩法路线表解耦,实现独立存储。 在Admin UI的路线编辑器中集成详情配置面板,支持编辑详情文案、图片等展示内容。 完善MiniAPP路线详情页,新增导航函数、API调用及数据归一化逻辑,并添加对应单元测试。 更新所有相关文档,明确领域边界、联调流程及接口契约规范。 调整首页和玩法页的点击跳转逻辑,优先跳转路线详情页而非原需求页。 新增数值格式化工具函数优化内容展示效果。
This commit is contained in:
1 parent
7ba92f3c5d
commit
c143b67275
32 files changed
+1343
-259
No files matched your search
+5
-3
@@ -28,9 +28,11 @@
|
||||
MiniAPP 前台开发:
|
||||
|
||||
1. `integration-workflow.md`
|
||||
2. `team-building-api.md`
|
||||
3. `public-api.md`
|
||||
4. `development-status.md`
|
||||
2. `wanfa-api.md`
|
||||
3. `detail-api.md`
|
||||
4. `team-building-api.md`
|
||||
5. `public-api.md`
|
||||
6. `development-status.md`
|
||||
|
||||
## 文档清单
|
||||
|
||||
|
||||
+45
-12
@@ -2,7 +2,7 @@
|
||||
|
||||
> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。
|
||||
>
|
||||
> 状态:待实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/detail/components/detailPresentation.ts` 定义详情页展示数据。当前 `detailPresentation.ts` 仍依赖已移除的 `Product` 类型,后端也没有详情管理接口;本契约采用与商品领域解耦的详情展示模型,不恢复 Product、ProductImage 或订单关联。
|
||||
> 状态:已实现契约。本文件定义独立路线详情展示模型,供 `WonderQ-Admin`、`WonderQ-Admin-UI` 和 `WonderQ-MiniAPP` 三端联调使用。详情不恢复 Product、ProductImage 或订单关联。
|
||||
|
||||
## 领域边界
|
||||
|
||||
@@ -20,16 +20,15 @@
|
||||
- 订单、预订、收藏、评价或线索。
|
||||
- 详情页底部的电话、管家联系和预订动作。
|
||||
|
||||
`key` 是详情展示内容自己的稳定业务键,不得设计为 Product ID 外键。详情页如何从玩法、页面入口或其他前台上下文定位 `key`,由前台导航契约另行约定。
|
||||
`key` 固定使用玩法路线 ID(即 `WanfaRoute.id`,例如 `family-water`),但 `DetailRecord` 不建立数据库外键。详情页通过 `/pages/detail/index?routeId={key}` 定位内容;详情记录删除不会跨领域级联删除路线或其他数据。
|
||||
|
||||
## 与 `detailPresentation.ts` 的关系
|
||||
|
||||
`detailPresentation.ts` 当前是前台展示适配器,不是持久化模型:
|
||||
`detailPresentation.ts` 是前台展示适配器,不是持久化模型:
|
||||
|
||||
- `eyebrow`、`duration`、`title`、`subtitle`、`intro`、`highlights`、`included`、`excluded`、`notes`、`gallery` 组成最终展示对象。
|
||||
- 当前实现从 `Product` 的标题、摘要、标签、详情区块和图片数组推导部分字段。
|
||||
- 当前实现对洞穴/探险路线生成另一组固定亮点,并使用固定费用说明和注意事项。
|
||||
- 当前实现会将主图、接口图片和 fallback 图片去重后截取前 6 张。
|
||||
- 当前实现优先消费 Public API 返回的最终展示字段。
|
||||
- 接口失败、字段不完整或详情未配置时,MiniAPP 按路线 ID 使用本地网络图片和模拟文案兜底。
|
||||
|
||||
新的 Admin API 应直接维护最终展示字段;Admin UI 不应复刻这些推导逻辑,也不应依赖已移除的 Product 字段。
|
||||
|
||||
@@ -63,6 +62,7 @@ API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。
|
||||
|
||||
```ts
|
||||
type DetailPresentation = {
|
||||
key: string;
|
||||
eyebrow: string;
|
||||
duration: string;
|
||||
title: string;
|
||||
@@ -84,6 +84,10 @@ type DetailRecord = DetailPresentation & {
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type PublicDetail = Omit<DetailPresentation, "key"> & {
|
||||
key: string;
|
||||
};
|
||||
|
||||
type DetailCreate = DetailPresentation & {
|
||||
key: string;
|
||||
isActive?: boolean;
|
||||
@@ -106,7 +110,7 @@ type DetailReorderRequest = {
|
||||
| 字段 | 类型 | 必填 | 约束和用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| `id` | `string` | 响应必填 | 后端生成的记录 ID,仅供管理端识别记录。 |
|
||||
| `key` | `string` | 是 | 详情展示稳定键;建议使用小写字母、数字和中划线,例如 `classic-panorama`。不得关联 Product 表。 |
|
||||
| `key` | `string` | 是 | 玩法路线 ID,例如 `family-water`;必须唯一,不建立数据库外键。 |
|
||||
| `eyebrow` | `string` | 是 | 详情页顶部眉标,例如“玩法推荐”。 |
|
||||
| `duration` | `string` | 是 | 展示用时长,例如“5天4晚”;接口保存最终文案,不要求前端从标题正则提取。 |
|
||||
| `title` | `string` | 是 | 详情页主标题。 |
|
||||
@@ -226,6 +230,34 @@ Content-Type: application/json
|
||||
|
||||
其中 `items` 为更新 `sortOrder` 后的 `DetailRecord[]`。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400 DETAIL_REORDER_INVALID`。
|
||||
|
||||
## MiniAPP Public API
|
||||
|
||||
### 获取路线详情
|
||||
|
||||
```http
|
||||
GET /api/public/details/{key}
|
||||
```
|
||||
|
||||
无需鉴权。`key` 为 `WanfaRoute.id`,例如 `family-water`。接口只返回启用且已配置的展示字段,不返回管理端 ID、状态、排序和审计时间:
|
||||
|
||||
```ts
|
||||
type PublicDetail = {
|
||||
key: string;
|
||||
eyebrow: string;
|
||||
duration: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
intro: string;
|
||||
highlights: string[];
|
||||
included: string[];
|
||||
excluded: string[];
|
||||
notes: string[];
|
||||
gallery: string[];
|
||||
};
|
||||
```
|
||||
|
||||
不存在、停用或未配置详情时返回 `404`。MiniAPP 请求失败或字段不完整时,按 `key` 查找本地 fallback;找不到 fallback 时展示未找到和重试状态。该页面暂不提供价格、收藏、在线订阅、预订、订单或管家联系动作。
|
||||
|
||||
## 图片与素材
|
||||
|
||||
Admin UI 使用现有素材上传接口获取图片 URL:
|
||||
@@ -242,14 +274,14 @@ POST /api/admin/media-assets/upload
|
||||
|
||||
Admin UI 应按以下方式调用:
|
||||
|
||||
1. 进入详情管理页时调用 `GET /api/admin/details`,按 `sortOrder` 渲染详情列表。
|
||||
2. 新增和编辑表单维护 `key`、眉标、时长、标题、副标题、介绍和四组列表文案。
|
||||
1. 玩法页加载时同时调用玩法分类和 `GET /api/admin/details`;详情编辑器嵌入现有路线编辑抽屉,不新增侧边菜单。
|
||||
2. 新增和编辑表单维护眉标、时长、标题、副标题、介绍和四组列表文案;保存路线时以已保存的路线 ID 作为详情 `key`。
|
||||
3. `highlights`、`included`、`excluded`、`notes` 使用可增删的重复字段编辑器,提交时保留数组顺序,不拼接成换行字符串。
|
||||
4. 使用图片上传接口维护 `gallery`,支持新增、删除和调整图片顺序。
|
||||
5. 上移或下移详情时提交完整详情 ID 列表,不直接修改本地 `sortOrder` 后假设保存成功。
|
||||
6. 删除前要求二次确认;删除成功后以接口返回或重新查询的数据更新列表。
|
||||
7. 处理 `401`、`404`、`409`、`422` 和 `5xx`,保存、上传或排序进行中禁用重复提交。
|
||||
8. 详情编辑器不得出现 Product ID、ProductImage ID、库存、订单或预订字段。
|
||||
8. 详情编辑器不得出现 Product ID、ProductImage ID、库存、订单、价格或预订字段;详情保存失败时明确提示路线摘要已保存、详情需要重试。
|
||||
|
||||
建议的 Admin UI API 封装函数:
|
||||
|
||||
@@ -268,6 +300,7 @@ Admin API 返回的 `DetailRecord` 可以映射为 `DetailPresentation`:
|
||||
|
||||
```ts
|
||||
const presentation: DetailPresentation = {
|
||||
key: record.key,
|
||||
eyebrow: record.eyebrow,
|
||||
duration: record.duration,
|
||||
title: record.title,
|
||||
@@ -281,11 +314,11 @@ const presentation: DetailPresentation = {
|
||||
};
|
||||
```
|
||||
|
||||
接入时应优先使用接口已保存的最终文案和图片顺序,不再依赖 `stripTitle`、标题时长正则、洞穴路线分支或 fallbackGallery 生成同一字段。
|
||||
接入时应优先使用接口已保存的最终文案和图片顺序;本地 fallback 只用于接口失败、字段不完整或详情未配置的前台容错。
|
||||
|
||||
## 后端落地边界
|
||||
|
||||
本契约落地时需要由 `WonderQ-Admin` 补充对应 ORM 模型、迁移、schema、路由、序列化和审计日志;由 `WonderQ-Admin-UI` 补充 API 类型、请求封装、详情列表、编辑器、图片管理和排序交互。当前后端没有 `/api/admin/details` 路由,Admin UI 没有详情管理入口,`detailPresentation.ts` 也不是可直接作为后端契约的完整类型来源。
|
||||
当前实现由 `WonderQ-Admin` 的 `DetailRecord` 模型、`0021_detail_records` 迁移、Admin/Public 路由和审计日志提供能力;由 `WonderQ-Admin-UI` 在玩法路线编辑抽屉中维护详情;由 `WonderQ-MiniAPP` 调用 Public 详情接口并按路线 ID fallback。迁移文件只负责生成和初始化详情记录,不自动执行数据库升级。
|
||||
|
||||
相关文档:
|
||||
|
||||
|
||||
@@ -310,6 +310,8 @@ type WanfaRoute = {
|
||||
|
||||
四组列表始终返回数组;没有可用内容时返回空数组。玩法推荐只返回启用的关联记录,并展开关联分类当前的路线。MiniAPP 应在数据层归一化字段并保留对应 mock fallback,不在页面组件内直接请求接口。
|
||||
|
||||
首页玩法推荐点击行为:当 `routes` 存在第一条路线时,MiniAPP 使用该路线的 `id` 调用 `goWanfaRouteDetail`,进入 `/pages/detail/index?routeId={route.id}`;没有关联路线时才使用 `demandKeyword` 或分类名称进入需求页。路线详情字段和 Public 详情接口以 [详情展示契约](./detail-api.md) 为准。
|
||||
|
||||
### 玩法推荐关联
|
||||
|
||||
`POST /api/admin/home/play-recommendations` 请求体:
|
||||
|
||||
@@ -85,6 +85,19 @@ MiniAPP 联调重点:
|
||||
- 首页模块按 Public API 字段渲染,不依赖后台未发布或未启用数据。
|
||||
- 线索提交调用 `POST /api/public/leads`,失败时显示可理解错误。
|
||||
|
||||
## 路线详情三端联调
|
||||
|
||||
路线详情使用独立 `DetailRecord`,不修改 `WanfaRoute` 表结构,也不建立商品、订单或预订关联。联调顺序如下:
|
||||
|
||||
1. 执行数据库迁移,确认 `0021_detail_records` 已创建详情表并为已有路线生成基础记录;生产环境执行前按迁移规范单独确认。
|
||||
2. 在 Admin UI 进入“玩法”,编辑路线摘要和详情字段,保存时先保存路线,再以路线 ID 作为 `DetailRecord.key` 创建或更新详情。
|
||||
3. 检查 `GET /api/public/wanfa/categories` 仍只返回路线摘要;检查 `GET /api/public/home` 的玩法推荐携带关联路线摘要。
|
||||
4. 在 MiniAPP 首页玩法推荐或玩法页点击路线,确认跳转 `/pages/detail/index?routeId={routeId}`,并请求 `GET /api/public/details/{routeId}`。
|
||||
5. 修改 Admin UI 的详情内容后刷新 MiniAPP,确认标题、正文、费用说明、注意事项和画廊更新;停用或删除详情时确认 Public API 返回 `404`。
|
||||
6. 关闭后端接口,确认 MiniAPP 按路线 ID 展示本地网络图片和模拟文案,并提示当前为模拟数据;未知路线展示未找到和重试状态。
|
||||
|
||||
路线详情页当前不包含价格、收藏、在线订阅、预订、订单或管家联系动作。字段和错误约定以 `detail-api.md`、`public-api.md` 为准。
|
||||
|
||||
## 接口变更流程
|
||||
|
||||
1. 先更新契约文档。
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
| `GET` | `/api/public/home/wild-archives` | 否 | 获取客片案例更多列表 |
|
||||
| `GET` | `/api/public/home/wild-archives/{archiveId}` | 否 | 获取客片案例详情和图片 |
|
||||
| `GET` | `/api/public/wanfa/categories` | 否 | 获取玩法分类和路线 |
|
||||
| `GET` | `/api/public/details/{key}` | 否 | 获取玩法路线详情 |
|
||||
| `GET` | `/api/public/concierge/advisors` | 否 | 获取已启用的管家顾问 |
|
||||
| `POST` | `/api/public/auth/phone-login` | 否 | 微信手机号登录 |
|
||||
| `GET` | `/api/public/auth/me` | Customer JWT | 获取当前客户 |
|
||||
@@ -73,6 +74,28 @@ type PublicWanfaRoute = {
|
||||
|
||||
MiniAPP 使用 `demandKeyword` 作为需求页预填关键词;接口失败或响应为空时,玩法页展示对应的错误、重试或空态,不再读取 `playData.ts` 模拟数据。
|
||||
|
||||
### 路线详情
|
||||
|
||||
`GET /api/public/details/{key}` 无需鉴权,`key` 使用玩法路线 ID,例如 `family-water`。成功响应只返回详情展示字段:
|
||||
|
||||
```ts
|
||||
type PublicDetail = {
|
||||
key: string;
|
||||
eyebrow: string;
|
||||
duration: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
intro: string;
|
||||
highlights: string[];
|
||||
included: string[];
|
||||
excluded: string[];
|
||||
notes: string[];
|
||||
gallery: string[];
|
||||
};
|
||||
```
|
||||
|
||||
不存在、停用或未配置详情返回 `404`。MiniAPP 路线详情页通过 `/pages/detail/index?routeId={key}` 进入;接口失败或字段不完整时按路线 ID 使用本地网络图片和模拟文案。该详情页暂不提供价格、收藏、在线订阅、预订、订单或管家联系动作。
|
||||
|
||||
## 管家展示
|
||||
|
||||
### `GET /api/public/concierge/advisors`
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
当前实现:`WonderQ-Admin` 通过迁移 `0015_wanfa` 创建 `WanfaCategory`、`WanfaRoute` 表并导入稳定初始 ID;`WonderQ-Admin-UI` 已接入分类和路线的查询、新增、编辑、删除及排序操作。
|
||||
|
||||
路线接口只维护分类和路线摘要字段。路线详情不写入 `WanfaRoute`,由独立 `DetailRecord` 通过 `docs/detail-api.md` 管理,详情记录的 `key` 等于路线 ID。首页玩法推荐和玩法页路线点击后统一跳转 `/pages/detail/index?routeId={route.id}`;无关联路线时才回退到需求页。
|
||||
|
||||
## 领域边界
|
||||
|
||||
玩法管理只维护玩法分类和路线卡片内容:
|
||||
|
||||
Reference in new issue
Block a user