# 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-`,例如 `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 ```