feat(catalog): add product catalog APIs
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# R1 商品目录接口对接说明
|
||||
|
||||
**提供方:成员 1(R1 商品目录)**
|
||||
|
||||
**消费者:成员 2(设计清单)、成员 3(订单)、成员 4(设计/生产数据)**
|
||||
|
||||
**分支:`feat/r1-catalog`**
|
||||
|
||||
本文档是 R1 向其他路线交付商品数据的简明使用说明。字段、路径与
|
||||
[`api-contract-v1.md`](./api-contract-v1.md) 第 3 节一致;发生冲突时以 API 契约为准。
|
||||
|
||||
## 1. 使用边界
|
||||
|
||||
- 商品与分类查询都是公开接口,调用时必须传 `auth: false`,不依赖登录态。
|
||||
- 消费方只能读取在售商品;下架或不存在的商品详情返回 `404`。
|
||||
- `productId` 是跨路线的稳定业务 ID,不能用数据库生成顺序、数组下标或展示名称替代。
|
||||
- `price` 是服务端权威价格。前端可展示,**不能**作为下单金额依据。
|
||||
|
||||
## 2. 稳定商品 ID
|
||||
|
||||
| productId | 商品 |
|
||||
|---|---|
|
||||
| `notebook-small` | 微雕笔记本(小) |
|
||||
| `notebook-large` | 微雕笔记本(大) |
|
||||
| `coaster` | 铜质杯垫 |
|
||||
| `penbox` | 竹制笔盒 |
|
||||
| `booklamp` | 书本型灯 |
|
||||
|
||||
每个商品都对应一个稳定分类 ID:`cat-<productId>`,例如 `cat-penbox`。
|
||||
|
||||
## 3. 查询接口
|
||||
|
||||
### `GET /api/categories`
|
||||
|
||||
返回扁平分类数组,已按 `sort` 升序排列。
|
||||
|
||||
```ts
|
||||
type CategoryDTO = {
|
||||
id: string
|
||||
name: string
|
||||
parentId?: string | null
|
||||
sort: number
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/products`
|
||||
|
||||
查询参数均可选:
|
||||
|
||||
```ts
|
||||
{
|
||||
page?: number // 默认 1
|
||||
pageSize?: number // 默认 20,最大 100
|
||||
categoryId?: string
|
||||
keyword?: string // 匹配名称、副标题、描述
|
||||
}
|
||||
```
|
||||
|
||||
响应 `data`:
|
||||
|
||||
```ts
|
||||
{
|
||||
list: ProductDTO[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/products/:id`
|
||||
|
||||
返回一个在售商品;商品不存在或不是 `ON_SALE` 时返回 `404`。
|
||||
|
||||
```ts
|
||||
type ProductDTO = {
|
||||
id: string
|
||||
name: string
|
||||
categoryId?: string
|
||||
price: number // 元,服务端已转为 number
|
||||
originalPrice?: number
|
||||
leadTime: string
|
||||
subtitle?: string
|
||||
description?: string
|
||||
story?: string
|
||||
scene?: string
|
||||
tags: string[]
|
||||
specs: [string, string][]
|
||||
tone: [number, number, number]
|
||||
mask: {
|
||||
shape: 'rect' | 'circle'
|
||||
width: number
|
||||
height: number
|
||||
borderRadius?: number
|
||||
}
|
||||
images: string[]
|
||||
iconImg?: string
|
||||
status: 'ON_SALE'
|
||||
sort: number
|
||||
}
|
||||
```
|
||||
|
||||
所有接口沿用统一响应包裹:`{ code: 0, message: 'ok', data }`。
|
||||
|
||||
## 4. 前端调用方式
|
||||
|
||||
前端消费者统一从 R1 API 层调用,不能在页面直接拼 HTTP 请求:
|
||||
|
||||
```ts
|
||||
import { fetchCategories, fetchProduct, fetchProducts } from '../../utils/api/product'
|
||||
|
||||
const { list } = await fetchProducts({ keyword: '笔记本', page: 1, pageSize: 20 })
|
||||
const product = await fetchProduct('penbox')
|
||||
const categories = await fetchCategories()
|
||||
```
|
||||
|
||||
## 5. 给成员 2:设计清单
|
||||
|
||||
设计清单保存商品相关数据时使用:
|
||||
|
||||
```ts
|
||||
{
|
||||
productId: product.id,
|
||||
productName: product.name,
|
||||
unitPrice: product.price, // 仅展示/设计清单快照;不是订单结算依据
|
||||
count: quantity,
|
||||
designData: {
|
||||
category: { id: product.id, mask: product.mask, tone: product.tone },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`mask` 与 `tone` 必须原样保留,供 R4 构造生产画布;不要把 `productName` 当作关联键。
|
||||
|
||||
## 6. 给成员 3:订单
|
||||
|
||||
R3 创建订单时,客户端请求只传:
|
||||
|
||||
```ts
|
||||
{
|
||||
items: [{ productId: 'penbox', quantity: 1 }]
|
||||
}
|
||||
```
|
||||
|
||||
R3 服务端必须:
|
||||
|
||||
1. 按 `productId` 查询商品并确认 `status === ON_SALE`;
|
||||
2. 以服务端 `price` 重算订单总价;
|
||||
3. 将 `productId`、`name`、`price`、`quantity` 写进 `OrderItem` 快照;
|
||||
4. 忽略客户端传入的单价、商品名和总金额。
|
||||
|
||||
## 7. 给成员 4:设计与生产
|
||||
|
||||
R4 若需要从商品恢复画布尺寸或主题色,使用 `ProductDTO.mask` 和 `ProductDTO.tone`。
|
||||
生产任务内保存的是设计快照;不要仅凭商品名称重新定位商品。
|
||||
|
||||
## 8. 联调前置条件
|
||||
|
||||
R1 合入或本地联调前,后端需要先执行数据库迁移与种子数据:
|
||||
|
||||
```bash
|
||||
npm run prisma:migrate
|
||||
npm run prisma:seed
|
||||
```
|
||||
|
||||
最小 smoke 验收:
|
||||
|
||||
```text
|
||||
GET /api/categories
|
||||
GET /api/products?page=1&pageSize=20
|
||||
GET /api/products/penbox
|
||||
GET /api/products/not-on-sale-or-missing -> 404
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
# R1 商品目录交付说明
|
||||
|
||||
**提供方:成员 1(R1 商品目录)**
|
||||
|
||||
**可使用成员:成员 2(设计清单)、成员 3(订单)、成员 4(设计与生产)**
|
||||
|
||||
**分支:`feat/r1-catalog`**
|
||||
|
||||
R1 商品目录已完成,可以对外提供商品数据。商品页面现在从后端接口读取商品信息,不再以写死在前端的商品数据作为主数据源。本说明中的验证结果用于证明商品目录已经真实可用;接口说明用于方便其他成员接入这份目录数据。
|
||||
|
||||
## 已完成内容
|
||||
|
||||
| 完成项 | 结果 |
|
||||
| --- | --- |
|
||||
| 后端商品数据 | 已建立商品分类及 5 个在售商品的本机数据。 |
|
||||
| 商品接口 | 已提供商品列表、单个商品详情和分类列表接口。 |
|
||||
| 前端商品页面 | 首页、商品列表、商品详情、立即定制页均已接入商品接口。 |
|
||||
| 本机联调环境 | Docker 中的后端、数据库、Redis 已启动;微信开发者工具可正常运行前端。 |
|
||||
|
||||
## 已完成前后端数据通路验证
|
||||
|
||||
这次验证不只是确认接口能访问,而是确认后端数据变化会传到前端页面,证明前端展示的确实是后端商品数据。
|
||||
|
||||
| 验证步骤 | 验证结果 |
|
||||
| --- | --- |
|
||||
| 读取商品初始值 | `notebook-small` 的价格为 12 元,划线原价为 18 元。 |
|
||||
| 修改后端数据库 | 将该商品测试价格改为 19 元,测试划线原价改为 29 元。 |
|
||||
| 重新编译小程序 | 微信开发者工具重新编译后,商品卡片和详情页同步显示 19 元、29 元。 |
|
||||
| 验证结论 | 前端展示随数据库和接口返回同步改变,前后端商品数据通路正常。 |
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
**结论:** R1 商品页面读取的是后端真实商品数据;后端数据变化能够传递到前端页面。因此以下接口已完成实际联调验证,可作为其他路线的商品数据来源。
|
||||
|
||||
## 对外使用约定
|
||||
|
||||
| 约定 | 说明 |
|
||||
| --- | --- |
|
||||
| 稳定商品 ID | 跨模块使用 `productId` 关联商品,例如 `notebook-small`、`coaster`、`penbox`。不要使用数组下标或商品名称作为关联键。 |
|
||||
| 公开查询 | 商品与分类查询不依赖登录态;前端调用时使用 `auth: false`。 |
|
||||
| 价格来源 | `price` 是服务端权威价格。前端可展示价格,订单金额必须由 R3 服务端重新查询并计算。 |
|
||||
| 在售限制 | 商品列表与详情只返回 `ON_SALE` 商品;不存在或下架商品的详情返回 `404`。 |
|
||||
|
||||
## 商品接口
|
||||
|
||||
所有接口统一返回:`{ code: 0, message: 'ok', data }`。实际业务数据位于 `data` 中。
|
||||
|
||||
| 接口 | 用途 | 参数 | 验证状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET /api/categories` | 获取按 `sort` 排序的分类列表 | 无 | 已验证 |
|
||||
| `GET /api/products` | 获取在售商品分页列表 | `page`、`pageSize`、`categoryId`、`keyword` 均可选 | 已验证 |
|
||||
| `GET /api/products/:id` | 获取一个在售商品的完整详情 | `id` 为稳定商品 ID | 已验证 |
|
||||
|
||||
### 商品数据字段
|
||||
|
||||
| 字段 | 含义 |
|
||||
| --- | --- |
|
||||
| `id`、`name`、`categoryId`、`status`、`sort` | 商品标识、名称、分类、在售状态和稳定排序。 |
|
||||
| `price`、`originalPrice`、`leadTime` | 商品价格、划线原价和制作周期。 |
|
||||
| `subtitle`、`description`、`story`、`scene` | 商品卖点、介绍、设计理念和使用场景。 |
|
||||
| `images`、`iconImg`、`tone` | 商品图片、图标与详情页视觉主题色。 |
|
||||
| `tags`、`specs`、`mask` | 商品标签、规格参数和定制画布信息。 |
|
||||
|
||||
### 可用商品 ID
|
||||
|
||||
| productId | 商品名称 | 对应分类 ID |
|
||||
| --- | --- | --- |
|
||||
| `notebook-small` | 微雕笔记本(小) | `cat-notebook-small` |
|
||||
| `notebook-large` | 微雕笔记本(大) | `cat-notebook-large` |
|
||||
| `coaster` | 铜质杯垫 | `cat-coaster` |
|
||||
| `penbox` | 竹制笔盒 | `cat-penbox` |
|
||||
| `booklamp` | 书本型灯 | `cat-booklamp` |
|
||||
|
||||
## 给其他成员的使用说明
|
||||
|
||||
| 成员 | 可以使用的商品数据 |
|
||||
| --- | --- |
|
||||
| 成员 2:设计清单 | 保存 `productId`、`productName`、`price`、`count`;设计数据中原样保留 `mask` 和 `tone`。 |
|
||||
| 成员 3:订单 | 客户端只传 `productId` 与 `quantity`;订单服务端必须重新读取 `price` 计算金额。 |
|
||||
| 成员 4:设计与生产 | 使用 `mask` 确定画布尺寸,使用 `tone` 作为主题色;生产任务保存商品 ID 与设计快照。 |
|
||||
|
||||
## 本机联调地址
|
||||
|
||||
- 后端接口:`http://127.0.0.1:3090`
|
||||
- Swagger 接口页面:`http://127.0.0.1:3090/docs`
|
||||
- 小程序导入目录:`wechat_wc/dist`
|
||||
|
||||
以上地址只适用于本机模拟器联调。真机和生产环境需要替换为已配置合法域名的 HTTPS 地址。
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
更新当前用户资料。请求体:`{ nickname?: string, avatar?: string }`。
|
||||
|
||||
## 3. 商品与分类(R1,待实现)
|
||||
## 3. 商品与分类(R1)
|
||||
|
||||
### GET /api/categories
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
|
||||
仅返回 `status=ON_SALE` 的商品。
|
||||
|
||||
响应 `data`:`{ list: ProductDTO[], total: number, page: number, pageSize: number }`。
|
||||
|
||||
### GET /api/products/:id
|
||||
|
||||
公开接口。返回单个在售商品;不存在返回 404。
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# 本机容器化联调教程
|
||||
|
||||
这套方案的目标是:**Windows 只负责编辑源码和运行微信开发者工具;PostgreSQL、Redis、Node.js/NestJS、Prisma 都运行在 Linux 容器中。** 因此本机行为尽量贴近未来 Linux 服务器,避免把 Windows 的 Node、数据库服务或路径习惯带进部署产物。
|
||||
|
||||
## 先理解两种 Compose 运行方式
|
||||
|
||||
| 目的 | 命令 | 特性 |
|
||||
| --- | --- | --- |
|
||||
| 日常开发、改代码自动重载 | `docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build` | 源码挂载进 Linux 开发容器,NestJS 热重载 |
|
||||
| 发布前模拟 | `docker compose up -d --build` | 使用 `docker/Dockerfile` 的多阶段生产构建,不挂载源码 |
|
||||
|
||||
两种方式共用 PostgreSQL、Redis、迁移文件和 `.env`;不要同时启动它们。日常使用第一种,准备交付或部署前再使用第二种。
|
||||
|
||||
> Windows 要运行 `node:alpine`、`postgres:alpine` 这类 Linux 镜像,Docker Desktop 底层必须使用 **WSL 2、Hyper-V 或 Docker VMM** 之一。你不需要在 WSL 里写代码或打开 Ubuntu;它只是 Docker 的 Linux 运行底座。若完全不允许这些虚拟化能力,就只能改用远程 Linux 测试机,无法在 Windows 本机运行 Linux Compose。
|
||||
|
||||
## 一次性安装
|
||||
|
||||
1. 在 BIOS/UEFI 确认开启 CPU 虚拟化(Intel VT-x / AMD-V)。
|
||||
2. 以管理员身份打开 PowerShell,执行 `wsl --install`;已安装时执行 `wsl --update`,按提示重启。
|
||||
3. 安装 Docker Desktop for Windows,首次启动时选择 **Use WSL 2 instead of Hyper-V**。无需在 WSL 内安装 Node、PostgreSQL 或 Redis。
|
||||
4. 重启 Docker Desktop 后,在普通 PowerShell 验证:
|
||||
|
||||
```powershell
|
||||
docker version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
安装依据见 [Docker Desktop for Windows 官方文档](https://docs.docker.com/desktop/setup/install/windows-install/) 与 [WSL 2 后端说明](https://docs.docker.com/desktop/features/wsl/)。
|
||||
|
||||
## 启动后端联调环境
|
||||
|
||||
以下命令均在 `G:\wordcloud_wechat\wxmp_backend` 执行。
|
||||
|
||||
1. 创建只属于本机的配置文件(该文件被 Git 忽略):
|
||||
|
||||
```powershell
|
||||
Copy-Item .env.example .env
|
||||
```
|
||||
|
||||
2. 打开 `.env`,至少改成下列本机联调值。不要提交 `.env`,真实微信密钥也不要放入前端。
|
||||
|
||||
```dotenv
|
||||
NODE_ENV=development
|
||||
APP_PORT=3090
|
||||
JWT_SECRET=dev-only-change-this-to-a-long-random-string
|
||||
WX_APPID=local-dev-appid
|
||||
WX_SECRET=local-dev-secret
|
||||
WX_MOCK_LOGIN=1
|
||||
```
|
||||
|
||||
`DATABASE_URL` 和 `REDIS_URL` 可保持示例值:Compose 会在容器内自动改为 `postgres`、`redis` 服务地址。
|
||||
|
||||
3. 首次先拉取所有公开测试镜像,再构建并启动开发环境:
|
||||
|
||||
```powershell
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml pull
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml ps
|
||||
```
|
||||
|
||||
预期 `postgres`、`redis`、`app` 都是 running/healthy。查看后端实时日志:
|
||||
|
||||
```powershell
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f app
|
||||
```
|
||||
|
||||
4. 导入 R1 商品初始数据(只需首次,或想恢复演示数据时执行):
|
||||
|
||||
```powershell
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec app npm run prisma:seed
|
||||
```
|
||||
|
||||
5. 在浏览器检查:
|
||||
|
||||
- `http://127.0.0.1:3090/docs`:Swagger 接口页
|
||||
- `http://127.0.0.1:3090/api/products`:R1 商品列表 JSON
|
||||
|
||||
修改 `src/` 或 `prisma/` 后,开发 Compose 的 `app` 会热重载;变更依赖或 Dockerfile 后重新执行 `up -d --build`。停止环境用 `docker compose -f docker-compose.yml -f docker-compose.dev.yml down`。这不会删除数据库数据卷;需要完全清空数据时才使用 `down -v`。
|
||||
|
||||
## 微信开发者工具:打开哪里、怎样看到页面
|
||||
|
||||
应导入 **前端项目根目录**:`G:\wordcloud_wechat\wechat_wc`。
|
||||
|
||||
不要导入工作区总目录 `G:\wordcloud_wechat`,也不要直接导入 `dist`。原因是前端根目录的 `project.config.json` 中已经声明 `miniprogramRoot: "dist/"`;开发者工具从该根目录读取项目配置,再把 `dist` 当作实际小程序输出。
|
||||
|
||||
首次操作如下:
|
||||
|
||||
1. 在一个 PowerShell 窗口进入前端目录,安装依赖并创建本机环境文件:
|
||||
|
||||
```powershell
|
||||
cd G:\wordcloud_wechat\wechat_wc
|
||||
npm ci
|
||||
Copy-Item .env.example .env
|
||||
npm run dev:weapp
|
||||
```
|
||||
|
||||
最后一条命令保持运行。Taro 会持续把源码编译到 `dist/`;它不是服务器,也不需要在 Windows 安装后端 Node。
|
||||
|
||||
2. 打开微信开发者工具,选择 **导入项目**,目录选择 `G:\wordcloud_wechat\wechat_wc`。若提示 AppID,使用项目已有 AppID;仅查看 UI 也可选择测试号/游客模式(以工具实际选项为准)。
|
||||
3. 等待终端首次构建完成,在开发者工具点顶部 **编译**。中间的 **模拟器** 就是小程序运行画面;切换底部或右侧的 **Console** 看前端异常、**Network** 看接口请求。以后保存 `src` 中的文件,Taro 会更新 `dist`,工具会自动或在点“编译”后刷新。
|
||||
4. 为本机 HTTP 接口联调,打开开发者工具的 **详情 → 本地设置**,勾选“**不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书**”。仅本机开发使用,提交/真机预览前必须关闭。
|
||||
|
||||
前端 `.env` 已提供:
|
||||
|
||||
```dotenv
|
||||
TARO_APP_API_BASE_URL=http://127.0.0.1:3090
|
||||
```
|
||||
|
||||
它会在 `npm run dev:weapp` 编译时注入;`src/utils/request.ts` 因而请求本机 Docker 后端。更换 `.env` 后必须重启该 Taro 命令。Taro 环境变量与构建调试方式可参阅 [Taro 官方文档](https://docs.taro.zone/docs/env-mode-config) 和 [调试文档](https://docs.taro.zone/docs/envs-debug)。
|
||||
|
||||
## 联调检查顺序
|
||||
|
||||
```text
|
||||
浏览器 /docs、/api/products 正常
|
||||
↓
|
||||
Taro 终端构建成功,dist/ 更新
|
||||
↓
|
||||
微信开发者工具导入 wechat_wc 并编译
|
||||
↓
|
||||
Network 中商品请求为 http://127.0.0.1:3090/api/products,状态 200
|
||||
↓
|
||||
商品页展示 R1 数据
|
||||
```
|
||||
|
||||
注意:`127.0.0.1` 仅代表运行开发者工具的这台 Windows 电脑。它适合模拟器,不适合真实手机;真机或他人联调时,应改为可访问的 HTTPS 测试域名,并在微信公众平台配置合法 request 域名。生产环境通常让 Nginx/Caddy 作为 HTTPS 入口,应用、PostgreSQL、Redis 仍留在 Docker 内部网络,不对公网暴露。
|
||||
Reference in New Issue
Block a user