# R1 商品目录动态化(feat/r1-catalog) **Goal:** 让首页、商品列表、沉浸式详情、商品详情四个页面改从后端商品/分类 API 取数, 前端不再直接依赖静态 `productConfig.ts` 作为线上数据源。 **Architecture:** 前端新增按域的 `api/product.ts` 与页面级加载状态;后端扩展 Product 表字段、 补齐 products/categories 查询能力、写入 5 个真实品类 seed;前后端通过 `docs/api-contract-v1.md` 冻结响应结构;静态 `productConfig.ts` 降级为离线兜底与图标映射,R1 合并验证前不删除。 **Tech Stack:** Taro 3.6 + React 18 + TypeScript;NestJS 11 + Prisma/PostgreSQL。 ## 1. 本分支要开发的内容 ### 前端 `wechat_wc` | 文件 | 职责 | |---|---| | `src/types/index.ts` 或 `src/types/product.ts` | ProductCategory 与后端字段对齐,新增 `subtitle/tone/story/scene/tags/specs/mask/originalPrice/leadTime/status` | | `src/utils/api/product.ts` | `fetchProducts(params)`、`fetchProduct(id)`、`fetchCategories()`,返回类型显式声明 | | `src/hooks/useProducts.ts`(新增) | 商品列表/详情加载、loading/error/retry、可选 60s 内存缓存 | | `src/pages/index/index.tsx` | 品类网格与搜索改走 API;成品轮播仍可静态,但价格/图片建议取商品数据 | | `src/pages/shop/index.tsx` | 商品网格改走 API,补加载骨架、空态、失败重试 | | `src/pages/shop/detail/index.tsx` | 按 `id` 拉详情,补 loading/error,保留沉浸式滚动动画 | | `src/pages/product/index.tsx` | 按 `id` 拉详情,`addDesign` 继续复用 `store/design.ts` | | `src/utils/productConfig.ts` | 保留为 seed 参考与离线兜底,不参与线上主流程 | ### 后端 `wxmp_backend` | 模块 | 内容 | |---|---| | `prisma/schema.prisma` | Product 增加 `subtitle/leadTime/originalPrice/tone/tags/specs/mask/story/scene/iconImg`,其中 `tone Int[]`、`specs Json`、`mask Json`、`tags String[]` | | `prisma/seed.ts` | 写入 5 个真实品类(笔记本小/大、杯垫、笔盒、书灯),图片用 OSS URL,mask 与前端现有一致 | | `products` | `GET /api/products`:分页、`categoryId`、`keyword`、`status=ON_SALE`;`GET /api/products/:id`:404 处理 | | `categories` | `GET /api/categories`:稳定排序返回,暂做扁平结构 | | `docs/api-contract-v1.md` | 商品/分类接口、分页结构、Decimal 数字格式、错误码 | ### 词云项目 `wordcloud` 不参与本分支。 ## 2. 设计注意事项 ### DO - DO 前端只消费后端返回的 `price`,下单链路以后也不允许前端自算金额。 - DO 在 API 层把 Decimal 字符串规范成本地 `number`,页面组件里不做字符串拼接运算。 - DO 所有列表/详情页补齐 loading、空态、失败重试,失败时给出可操作文案。 - DO 图片加载失败使用 `product.iconImg` 或 `四角星.svg` 兜底,单张图失败不阻塞页面。 - DO `mask`、`specs`、`tone` 等富字段作为后端 JSON 返回,前端类型用判别联合描述。 - DO 后端 seed 和前端 `productConfig.ts` 使用同一套稳定的业务 ID(如 `notebook-small`),方便前后端联调。 - DO 在 R1 分支内同步更新前端 `types` 与 Swagger 文档,一个 PR 成对评审。 - DO 商品列表接口先给分页参数,前端第一版可以每次取全部,但接口结构要能扩展。 - DO 公共接口统一 `auth:false`,不要携带 token。 - DO 保持项目事件规范:所有点击使用 `onTap`,不使用 `onClick`。 ### DON'T - DON'T 直接删除 `productConfig.ts`,R1 合并前它仍是离线兜底和图标映射来源。 - DON'T 在页面里直接写 `http.get('/api/products')`,必须收口到 `api/product.ts`。 - DON'T 在前端硬编码新的图片 CDN 地址,继续走 `assetUrl` 与 `OSS_BASE_URL`。 - DON'T 让前端根据 `originalPrice` 自行计算促销逻辑,促销后续由后端字段统一表达。 - DON'T 依赖数据库插入顺序,列表必须有 `sort/createdAt` 稳定排序。 - DON'T 在 seed 中用随机 cuid 造成不同环境商品 ID 漂移。 - DON'T 在 R1 里顺手改主题、TabBar、DIY 等无关文件。 - DON'T 把 `description` 塞进 JSON 大字段混用,普通段落继续用字符串字段。 ## 3. 补充内容(用户未列但建议纳入) ### 验收标准 - 前后端本地联调:首页/商品列表/沉浸式详情/商品详情四个页面数据来自 API,静态配置失效时页面有兜底而不会白屏。 - 后端 `npm run build`、前端 `npm run build:weapp` 通过。 - Swagger 中商品列表/详情/分类文档完整;前端类型与 Swagger 字段一一对应。 - seed 后数据库包含 5 个在售品类,图片 URL 可访问。 ### 合并与依赖 - 分支名:前端与后端均为 `feat/r1-catalog`。 - 合并顺序:R1 是整个交易链路的前提,优先合入;R3 的服务端金额重算依赖本分支的商品表。 - 合入前跑一次后端接口 smoke:`GET /api/categories`、`GET /api/products`、`GET /api/products/:id`。 ### 风险 - OSS 图片域名若未配到微信后台 `downloadFile` 合法域名,商品图会在真机白图,联调时先确认。 - `tone`、`mask` 等富字段若后端 JSON 序列化方式不统一,前端类型会悄悄失效,合入前要跑真实返回样例。 - 首页成品轮播目前混用静态文案与商品数据,R1 建议只把“热门品类”切 API,轮播数据源单独决策。 ### 测试要求 - 后端:至少补 products/categories 的 e2e(正常列表、空列表、404、keyword 过滤)。 - 前端:三个页面手测加载态、断网重试、空数据、关键词搜索无结果、图片 404 兜底。 - 不需要在 R1 引入自动化 UI 测试,保持手测清单即可。