Files
inkreach-official-website/docs/references/product-center.md
T

161 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 产品中心
官网产品中心位于 `apps/website/app/pages/product-center.vue`,访问路径为 `/product-center`
## 默认展示
页面首次加载时展示全部商品和全部国家,不默认选择分类、物流或工艺标签。每页默认请求 20 条商品,用户操作筛选项后再按真实业务 ID 查询。
## 筛选与分页
- 左侧分类树:选择父分类或子分类。
- 国家:点击国家胶囊筛选,点击“全部”取消国家筛选。
- 物流与工艺:按标签组展示下拉选项,每个分组内暂为单选,不同分组可各选择一项。
- 筛选菜单在选择选项或点击组件外部后自动关闭;已选 Tag 使用独立标签行。仅选择工艺时标签从最左侧开始;物流与工艺同时选择时按物流、工艺顺序与上方筛选项对应排列。
- 搜索:输入关键词后按回车或点击“搜索”。
- 商品卡:整卡链接到 `https://inkpod.vip/portal/detail/{商品ID}`,在新窗口打开。
- 公共商品接口的 `id` 来自 `originGood.sdsGoodId`,即 InkPOD 商品 ID`goods.good_id` 仅作为本站数据库内部主键使用。
- URL 国家筛选:支持 `/product-center?countryId={后台国家ID}`,仅接受后台已配置的国家 ID。
- 无结果:展示设计稿空状态,可通过“查看全部商品”或“清空筛选”恢复完整列表。
- 加载中:展示两行共八个商品骨架卡,筛选栏保持可操作。
- 分页:支持页码、前后翻页、每页数量和页码跳转。
所有查询通过 `useProductCenter.ts` 调用 `/api/backend/*` 代理路由,不在页面中硬编码业务 ID。
## 后台配置联动
- 品类树、国家、Tag 与 Tag Group 均来自 NestJS `/public/*` 接口。
- 国家旗帜读取 `countryIcon`,品类图标读取 `categoryIcon`;前端不提供外部旗帜 CDN 或固定名称兜底。
- `/uploads/*``/assets/*` 等后台相对资源地址由 `useProductCenter.ts` 根据 `runtimeConfig.public.backendUrl` 转为完整 URL。
- 物流和工艺筛选分别由后台名称包含“物流”和“工艺”的 Tag Group 生成;“印刷位置”等其他分组仍可用于商品卡片标签,但不会自动成为顶部筛选项。
- Figma 基准图标位于 `apps/api/public/product-center/`,通过后端 `/assets/product-center/*` 提供。
- 更新现有数据库图标配置:`pnpm --filter @inkreach/api configure:product-center-icons`。脚本按中文名称幂等更新,不创建缺失的国家或品类。
## 响应式
桌面采用 240px 分类栏,页面左右保留 `2456px` 自适应留白,分类栏可滚动但隐藏可见 scrollbar。商品网格以 240px 为最小卡片宽度自动增加或减少列数:1440px 基准宽度保持四列,1920px 可显示六列。国家筛选区按内容自然增高,不保留固定空白。
左侧品类栏不显示最左侧边线,一级品类之间保留 10px 间距;国家筛选区及物流、工艺、搜索工具区的底部分隔线从内容区向右延伸至页面边缘。
宽度低于 1000px 后分类栏变为抽屉;移动端商品网格降为两列或单列,国家筛选仅在自身区域横向滚动,不会撑宽页面。
## 多源合并商品(已废弃,由产品族替代)
> **2026-08-28 起,该机制已被「产品族」完全替代**:多链接合并由族承载(尺码/包装并集 +
> 五维价格矩阵),配置/编辑商品不再写入 `mergedOriginGoodIds``good_origin_goods` 表
> 只读保留(历史数据仍可命中详情),观察期后删除。以下为历史行为记录:
- 数据层:主源存 `goods.origin_good_id`,副源存中间表 `good_origin_goods`
- 详情可达性:官网上通过**任一**关联原产品的 `sdsGoodId` 都能访问到该商品详情(族机制下
同样成立:族内任何成员链接的 sdsGoodId 均命中同一商品)。
> 该 Good 级合并能力保留可用;新一级的「产品族(SPU)合并」见下节,公开读路径接入族数据属于三期范围。
## 产品族(SPU 层,一期后端已上线)
设计文档:`docs/superpowers/specs/2026-08-28-product-family-merge-design.md`
**模型**SDS 叶子分类即产品模型(如分类 `DG001 180G纯棉T恤(JSA002` 下 16 条链接),
族 = 同分类链接的合并体;链接名解析列(`国家(物流)品名-SKU-工艺[-仓库]`)提供
物流/工艺归因。例:DG001 族 = 16 链接 × 3 工艺 × 3 物流 × 9 尺码(S~XXXXXL 并集)× 20 颜色 ≈ 470 格价格矩阵,起价取光板(不打印)最低价。
**价格**`价格 = f(尺码, 颜色, 物流, 工艺[含印花数量])`,全部从成员链接变体推导透传
(同格子多仓库取最低价,来源全保留);`family_price_overrides` 表支持按格人工改价,
删覆盖即恢复推导价。
**关键端点用法示例**JWT):
```bash
# 1. 自动成族:先预览
curl -X POST /product-families/auto-group -H "Authorization: Bearer $T" \
-d '{"apply": false}'
# → { applied: 0, groups: [{ groupKey: "cat:8240", familyName: "DG001 180G纯棉T恤(JSA002", memberCount: 16, ... }] }
# 2. 应用(幂等,可重复执行)
curl -X POST /product-families/auto-group -H "Authorization: Bearer $T" -d '{"apply": true}'
# 3. 族详情:成员 + 并集尺码表/包装 + 五维价格矩阵
curl /product-families/12 -H "Authorization: Bearer $T"
# 4. 人工改价(维度必须存在于族矩阵选项,否则 400 并列出非法键)
curl -X PUT /product-families/12/price-overrides -H "Authorization: Bearer $T" \
-d '{"items": [{"sizeId":"size_S","colorId":"color_blk","craft":"单面印花","logistics":"包邮","price": 23, "note": "促销"}]}'
# 5. 删覆盖恢复推导价
curl -X DELETE /product-families/12/price-overrides -H "Authorization: Bearer $T" \
-d '{"cells": [{"sizeId":"size_S","colorId":"color_blk","craft":"单面印花","logistics":"包邮"}]}'
# 6. 族内创建自定义商品(人工补链接,物流/工艺归因必填)
curl -X POST /product-families/12/members/custom -H "Authorization: Bearer $T" \
-d '{"goodName":"美国(海运)180g纯棉T恤-DG001-烫画","logisticsLabel":"海运","craftLabel":"烫画",
"variants":[{"sku":"C-M","sizeId":"size_M","sizeName":"M","colorId":"color_red","colorName":"红色","price": 33}]}'
```
**同步联动**:商品同步落库时刷新解析列;新链接与已有族**同分类唯一命中**时自动挂族
(多族/零族留给管理员;锁定族只置 `stale`);详情同步提交后异步重算受影响族
(进程内去重、幂等)。回填/修复脚本:
```bash
pnpm --filter @inkreach/api backfill:product-families
# → [1/2] 解析全部链接名 [2/2] 自动建族 [3/3] 全量族重算(幂等,可随时重跑)
```
**公开读路径(三期,默认开启)**`PUBLIC_DETAIL_FROM_FAMILY=false` 可应急回退旧行为。
`GET /public/goods/:goodId` 行为:
- **变体并集(替代旧副源机制)**`variants` = 主链接 ∪ 全体族成员 ∪ 旧副源(过渡期),
`(链接, 变体)` 去重;`mediaByColor` 同源。任何族成员链接的 sdsGoodId 均命中同一商品。
- **族块**(默认输出):`family` 含并集尺码表/包装 + 五维价格矩阵 + 族起价:
```jsonc
{
"family": {
"familyId": "12", "familyCode": "DG015", "familyName": "DG015 180G纯棉T恤",
"sizes": [...], "colors": [...], "crafts": ["单面印花", ...], "logistics": ["包邮", ...],
"sizeChart": { /* 并集 */ }, "packageSpecs": { /* 并集 */ },
"priceMatrix": { /* 五维矩阵:rows {sizeId, colorId, craft, logistics, price, manual, sources} */ },
"minPrice": "29.5"
}
}
```
实测(DG015 族):4 工艺 × 2 物流 × 8 尺码 × 12 颜色 = 653 格矩阵,族起价 ¥29.5
(主源单链接价为 ¥68.74——族视角展示了光板/单面的更低档价格)。前端本地按五维联动
`priceMatrix` 即可实时算价,无需新增查价端点(设计 D4)。
**边界声明**:商品列表/筛选/排序仍基于主源 `goodPrice`(SQL 层无法廉价解析族矩阵 JSON,
且避免展示价与筛选价不一致);`good_origin_goods` 转只读保留,观察期后另行删除。
**后台操作入口(族替代旧主源/副源,界面保持原有布局)**
- 商品配置页布局不变(左树=官网商品、右树=原产品库分类平铺);配置弹窗保持原「合并同名」
勾选流程,提交时**静默**把勾选链接与主链接归入同一族(无族自动成族);
- 编辑弹窗的原「关联原产品(主源 + 副源)」区块改为「**关联原产品(族成员)**」:
显示族编码与链接数、成员列表(`设为主链接` / `移除出族`)、搜索添加成员——
操作直接作用于族(并集与价格矩阵随重算更新);
- 人工改价/自动成族等族管理 API`/product-families/*`)保留,供脚本或后续界面使用。
**族派生标签(物流/工艺/印刷位置标签自动化)**
- 标签组名含「物流」「工艺」「位置」的组视为**自动组**;有族商品的这些标签由族成员的
`logisticsLabel/craftLabel` **自动生成**(前缀匹配取最长命中:单面印花→单面印、
不包邮光板→不包邮),每次族重算/成员变更/商品创建更新时同步到族下所有商品;
- 表单中自动组不再出现在标签下拉里(只读展示"族自动:…"),后端也会剔除手动传入的
自动组标签;无族商品(如独立自定义商品)仍可手动打标签;
- 其他分组(如风格类)不受影响,保持人工管理;
- 实测(DG015,10 链接):商品自动获得 `包邮、不包邮、双面印、直喷、单面印`
官网物流/工艺筛选直接命中合并后的完整链接集合。
## 验证
```bash
pnpm --filter @inkreach/website test --run
pnpm --filter @inkreach/website build
pnpm --filter @inkreach/website dev
pnpm --filter @inkreach/api test -- --runInBand
pnpm --filter @inkreach/api build
```