docs: 统一业务资源ID为服务端生成的稳定UUID

更新所有业务API文档,明确持久化资源的正式ID必须为服务端生成的稳定UUID,本地调试或接口失败时可使用语义ID作为fallback。新增数据库迁移脚本0022_opaque_ids,用于将历史语义ID转换为稳定UUID,并同步外键关联、详情记录的key字段以及审计日志的实体ID引用。新增该迁移的单元测试用例,验证ID替换与关联数据同步的逻辑正确性。调整MiniAPP前端代码,优化导航工具函数的格式,移除废弃函数并修改首页跳转逻辑,使用接口返回的UUID作为详情跳转参数。
This commit is contained in:
duanshuwen
2026-08-19 22:28:13 +08:00
parent e082bd2d98
commit d3a246a873
14 changed files with 314 additions and 24 deletions

View File

@@ -20,6 +20,7 @@
- 变更接口写入审计日志后再提交事务。
- 成功业务结果统一放在 `data`;创建成功为 HTTP/code `201`。
- 失败统一返回数字 `code`、用户可读 `msg`、`data: null`,业务错误码放在可选的 `errorCode`。
- 所有持久化资源的 `id` 由后端生成稳定 UUID 字符串。Admin UI 必须保存并复用接口返回的 ID,不能根据标题、文案或数组下标自行拼接,也不能假设 ID 是可读 slug。
## 接口清单

View File

@@ -24,6 +24,14 @@
`data` 内的业务字段保持各领域文档原有结构不变。也就是说,列表的 `items`、首页的 `experiences`、玩法的 `categories` 等字段都位于响应的 `data` 内,而不是与 `code` 同级。
## ID 规范
- 所有持久化资源的 `id` 都是服务端生成的稳定不透明字符串,当前实现统一使用 UUID v4 格式。
- ID 只在记录创建时生成,后续列表、详情、排序、编辑和删除响应必须保持不变;禁止在序列化或每次请求时重新随机生成。
- 玩法路线详情的 `DetailRecord.key` 等于对应的 `WanfaRoute.id`,因此路线 ID 迁移后详情 `key` 必须同步更新。
- 本地 fallback/mock 数据可以继续使用便于阅读的语义 ID,但这些 ID 不代表服务端正式 ID;接口成功后应以 API 返回的 UUID 为准。
- `0022_opaque_ids` 迁移只转换历史非 UUID ID,并同步外键、详情 key 和审计实体引用;新建记录继续由 ORM 默认生成 UUID。
## 成功响应
### 查询、更新、排序

View File

@@ -6,6 +6,8 @@
所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。
Admin 顾问记录的 `id` 为服务端生成的稳定 UUID 字符串;MiniAPP Public 响应按现有契约不返回顾问 ID。不要使用姓名、角色或本地 mock ID 作为正式顾问标识。
## 领域边界
管家管理只维护管家顾问卡片资料:

View File

@@ -20,7 +20,7 @@
- 订单、预订、收藏、评价或线索。
- 详情页底部的电话、管家联系和预订动作。
`key` 固定使用玩法路线 ID(即 `WanfaRoute.id`,例如 `family-water`),但 `DetailRecord` 不建立数据库外键。详情页通过 `/pages/detail/index?routeId={key}` 定位内容;详情记录删除不会跨领域级联删除路线或其他数据。
`key` 固定使用玩法路线 ID(即服务端生成的 `WanfaRoute.id` UUID),但 `DetailRecord` 不建立数据库外键。详情页通过 `/pages/detail/index?routeId={key}` 定位内容;详情记录删除不会跨领域级联删除路线或其他数据。
所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。

View File

@@ -21,6 +21,8 @@
`demandKeyword` 只是点击卡片后预填需求页的普通字符串,不是商品 ID、路线 ID、订单 ID,也不建立数据库外键。
首页正式接口返回的所有资源 `id` 都是稳定 UUID 字符串。不要把下方 fallback 文件中的语义 ID 当作服务端 ID,也不要在接口序列化时重新生成 ID;首页卡片跳转详情、排序和删除必须复用接口返回的同一 ID。
当前首页通过 `GET /api/public/home` 消费四类内容;三个卡片 mock 数组仍作为接口失败、空响应或字段缺失时的前台 fallback,玩法推荐无本地模拟数据时保持空态。Admin UI 通过本文件列出的 Admin API 维护正式数据。
本文件所有 JSON 示例的业务对象均位于统一响应的 `data` 字段内,完整包裹格式见 [api-response-contract.md](./api-response-contract.md)。
@@ -336,7 +338,7 @@ Admin 响应记录包含 `id`、`categoryId`、`categoryLabel`、`routeCount`、
## 当前 fallback 与迁移映射
迁移初始数据时应保留以下稳定 ID、字段值和当前数组顺序:
以下语义 ID 仅用于 MiniAPP 本地 fallback 和迁移前的内容识别;执行 `0022_opaque_ids` 后,正式 API 返回对应记录的稳定 UUID,字段值和当前数组顺序保持不变:
### 体验推荐

View File

@@ -93,13 +93,20 @@ MiniAPP 联调重点:
路线详情使用独立 `DetailRecord`,不修改 `WanfaRoute` 表结构,也不建立商品、订单或预订关联。联调顺序如下:
1. 执行数据库迁移,确认 `0021_detail_records` 已创建详情表并为已有路线生成基础记录;生产环境执行前按迁移规范单独确认。
1. 执行数据库迁移,确认 `0021_detail_records` 已创建详情表并为已有路线生成基础记录;确认 `0022_opaque_ids` 已将历史语义 ID 转换为稳定 UUID,并同步玩法外键、详情 `key` 和审计引用。生产环境执行前按迁移规范单独确认。
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 展示本地网络图片和模拟文案,并提示当前为模拟数据;未知路线展示未找到和重试状态。
## 稳定 ID 联调检查
1. 执行 `0022_opaque_ids` 后,检查首页、玩法、管家、团队共创和客片案例列表返回的 `id` 均为 UUID 字符串。
2. 从列表复制一个 ID 请求对应详情、排序或删除接口,确认同一 ID 可连续复用,不能每次响应变化。
3. 检查首页玩法推荐的 `categoryId`、路线 `id` 与 `GET /api/public/details/{key}` 的 `key` 关联正确。
4. MiniAPP 本地 fallback 的语义 ID 只在接口失败时使用,不得覆盖接口成功返回的 UUID。
路线详情页当前不包含价格、收藏、在线订阅、预订、订单或管家联系动作。字段和错误约定以 `detail-api.md`、`public-api.md` 为准。
## 接口变更流程

View File

@@ -7,6 +7,7 @@
- API 前缀:`/api/public`。
- 响应使用 JSON;时间使用 ISO 8601 字符串。
- 所有 `/health` 和 `/api/public/**` JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。
- 所有返回的持久化资源 `id` 都是稳定 UUID 字符串,不是标题、分类名或本地 mock 使用的语义 ID;详情、列表和跳转必须复用同一个 ID。
- H5 本地开发通过 `/api` 代理访问后端。
- 内容接口失败时,MiniAPP 使用 `src/content.ts` 的本地兜底内容。

View File

@@ -5,6 +5,8 @@
所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。
团队共创记录的正式 `id` 为服务端生成的稳定 UUID 字符串;MiniAPP 本地 fallback 可以保留语义 ID,但接口成功后必须以 UUID 作为详情请求参数。
## 领域边界
- 团队共创卡片和详情共用一条 `HomeTeamBuilding` 记录。

View File

@@ -4,12 +4,14 @@
>
> 状态:已实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/play/components/playData.ts` 定义玩法分类和路线的数据结构,以及管理端需要的查询、维护和排序接口。它不是 MiniAPP Public API 文档。
当前实现:`WonderQ-Admin` 通过迁移 `0015_wanfa` 创建 `WanfaCategory`、`WanfaRoute` 表并导入稳定初始 ID;`WonderQ-Admin-UI` 已接入分类和路线的查询、新增、编辑、删除及排序操作。
当前实现:`WonderQ-Admin` 通过迁移 `0015_wanfa` 创建 `WanfaCategory`、`WanfaRoute` 表并导入初始数据;`0022_opaque_ids` 将历史语义 ID 转换为稳定 UUID;`WonderQ-Admin-UI` 已接入分类和路线的查询、新增、编辑、删除及排序操作。
所有 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 只用于 MiniAPP fallback 和迁移前数据识别,不作为正式接口响应 ID;不要在序列化时临时随机生成 ID。
## 领域边界
玩法管理只维护玩法分类和路线卡片内容:
@@ -107,10 +109,10 @@ type WanfaReorderRequest = {
| 字段 | 类型 | 必填 | 约束和用途 |
| --- | --- | --- | --- |
| `category.id` | `string` | 响应必填 | 分类稳定标识,例如 `family-route`。创建时由后端生成。 |
| `category.id` | `string` | 响应必填 | 分类稳定标识,服务端生成 UUID;创建后在列表、详情、排序和删除请求中保持不变。 |
| `category.label` | `string` | 是 | 左侧分类显示名称,去除首尾空白后不得为空。 |
| `category.routes` | `WanfaRoute[]` | 响应必填 | 当前分类下的路线,按展示顺序返回。 |
| `route.id` | `string` | 响应必填 | 路线稳定标识,例如 `family-water`。创建时由后端生成。 |
| `route.id` | `string` | 响应必填 | 路线稳定标识,服务端生成 UUID;详情 `key` 与该 ID 一致。 |
| `route.title` | `string` | 是 | 路线卡片标题,去除首尾空白后不得为空。 |
| `route.subtitle` | `string` | 是 | 路线卡片副标题或目的地说明。 |
| `route.image` | `string` | 是 | 可直接用于图片组件的封面 URL。 |

View File

@@ -6,6 +6,8 @@
所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。
客片案例记录的正式 `id` 为服务端生成的稳定 UUID 字符串;列表、详情和删除使用同一 ID。本地 fallback 的语义 ID 仅用于接口失败时定位模拟内容。
## 领域边界
客片案例是首页内容领域的一类展示内容,不属于商品、Product、ProductImage、订单或预订领域。