Files
inkreach-official-website/docs/references/product-center.md
T
yeuimu d625b40873 fix(admin): merge-candidates by SKU/family; config dialog merge block always shown; cfg badge adjacency
- 修正合并同名候选匹配:此前用名称 3 段组键(含工艺段),工艺写法不同的
  同款(如 光板不打印 vs 单面印花(前印))全部漏配导致候选为空、合并同名
  区块被 v-if 隐藏 —— 用户看到的"checkbox 和搜索框被吞"即此因。
  改为 族归属精确匹配 或 款号(名称第 2 段,如 BRTF004)一致,全局跨分类;
  区块常显,无候选时空态提示
- 修左树「N 个配置」徽标错位:good-name-col 改 flex 布局,徽标紧贴名称
- 验证:admin typecheck + 22/22 + 构建绿;DB 实测 BRTF004 光板链接候选池
  恢复(单面印花(前印),同族 857)
2026-08-29 02:16:35 +08:00

213 lines
15 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`:**一族一条**(族内多条 Good 去重,代表行=排序第一条),
`goodId` = **族 ID**`total` = 族数,分页作用于分组后;`price` = 族矩阵最低价(起价),
价格排序按族起价重排;**无族商品(自定义)完全不返回**(列表/首页/分类树/标签统计同理)。
- `GET /public/goods/:goodId``goodId` 即**族 ID**(唯一公开寻址键;旧 SDS 链接 ID 与
自定义商品 sdsGoodId 均 404)。响应为款级聚合:
- 公共信息取代表 Good(名称/主图/国家/分类/标签)+ 主链接(详情文案);
- `variants` = 全体族成员 ∪ 旧副源(过渡期),按 `(链接, 变体)` 再按 `颜色+尺码` 去重;
`mediaByColor` 同源;`options/media` 并入族成员 detail
- `sizeChart` / `packageSpecs` = 族物化并集(空并集回退主链接合并结果);
- **族块**(默认输出):`family` 含并集尺码表/包装 + **严格五维价格矩阵** + 族起价:
```jsonc
{
"family": {
"familyId": "12", "familyCode": "DG015", "familyName": "DG015 180G纯棉T恤",
"sizes": [...], "colors": [...],
"printCounts": ["单面印花", "双面印花"], "crafts": ["烫画", "直喷", "不打印"], "logistics": ["包邮", "不包邮"],
"sizeChart": { /* 并集 */ }, "packageSpecs": { /* 并集 */ },
"priceMatrix": { /* rows {sizeId, colorId, printCount, craft, logistics, price, manual, sources} */ },
"minPrice": "29.5"
}
}
```
矩阵维度来源是**链接级标签**(印花数量/印刷工艺/物流渠道三组;人工接管后按人工标签),
不再用原始链接名的第 3 段(`craftLabel` 含「直喷单面/光板不打印」等噪声)。名称派生规则已
覆盖裸「单面/双面」写法(如「直喷双面」→ 双面印花 + 直喷)。光板/不打印链接无印花面概念,
矩阵中印花数量维回退「单面印花」占位。前端本地按五维联动 `priceMatrix` 即可实时算价
(设计 D4)。
**边界声明**:价格区间筛选(minPrice/maxPrice)仍作用于链接 `goodPrice`(族内任一链接命中即
返回该族;SQL 层无法廉价解析族矩阵 JSON);`good_origin_goods` 转只读保留,观察期后另行删除。
**后台操作入口(族替代旧主源/副源,界面保持原有布局)**
- 商品配置页布局不变(左树=官网商品、右树=原产品库分类平铺);配置弹窗保持原「合并同名」
勾选流程,提交时**静默**把勾选链接与主链接归入同一族(无族自动成族);族内**不区分主次**
(成员列表无设为主链接按钮,主链接仅作为详情数据源的内部实现);
- 编辑弹窗的「**关联原产品**」区块:显示族编码与链接数,成员行显示**原始链接名**(便于核对)
并可**逐个展开**——查看成员详情(SDS ID / 链接价格 / SKU 数 / 物流备注 / 工艺位置 / 仓库)、
**配置该链接的标签**(保存后人工接管;「恢复自动」回到按名称派生)、
**改价格**(尺码/颜色/价格 可编辑表格,写入族价格覆盖)、
**查看成员自己的详情**(商品详情 / 尺码表 / 包装规格 / SKU 四个页签,懒加载
`GET /origin-goods/:id`,SKU 表带 物流/工艺/印花数量 维度列;未同步时可在面板内直接同步);
SDS 商品不再在弹窗底部展示单一链接的旧详情区(自定义商品的编辑表单仍保留在底部);
- 成员列表下方有**族级并集视图**页签:尺码表 / 包装规格(族物化并集)与
**SKU(全族动态合并)**——SKU 即价格矩阵行,各链接变体按 物流×工艺×印花数量×尺码×颜色
归并(同组合取最低价,「链接数」列显示来源数,人工价带标签),无需额外请求;
- 编辑弹窗商品「名称」预填解析后的「品名 型号」,保存即采用解析名;右侧原产品库与配置弹窗
始终显示**原始链接名**
- 配置弹窗「合并同名」区块**常显**,候选为**全局同款链接**:按 链接款号(名称第 2 段,
如 BRTF004)或族归属跨分类匹配,同款不同工艺/物流/仓库均在列(含已配置链接),
支持搜索过滤;无候选时显示空态提示。
- 右树徽标语义(**族感知**,公开契约一族一款):**已配置 N**(绿,本链接已配置为官网商品,
N=国数)/ **已配置**(绿,本链接未单独建商品但所属族已配置)/ **成族**(蓝,已归族且全族
未配置)/ **未配置**(橙);「定位到官网商品」在自身或同族已配置时出现(按 主链接 → 同族 →
旧副源顺序定位并高亮),「配置」在本链接未单独建商品时可用(含族已配置的情形,供调整归族;
注意重复配置会产生官网不可见的冗余商品);
- 族分组头部计数与「仅未配置」筛选同样按族语义(族已配置 → 计入已配置)。
- **左树一族一行**(国家/品类模式):同款多条配置只显示排序最前的一条,名称旁
「N 个配置」徽标悬浮列出其余配置(可打开编辑),分组头部计数仍按全部商品统计。
- 人工改价/自动成族等族管理 API`/product-families/*`)保留,供脚本或后续界面使用;
- 页面组件化:`GoodsView.vue` 拆出 `components/` 下的 GoodsEditDialog(编辑弹窗)、
GoodsConfigDialog(配置弹窗)、CustomGoodDialog(自定义商品)、TagFilterPopover(标签筛选弹层)。
> 注意:历史上每族多点「配置」会产生多个同名的官网商品(重复项可手动删除)。现在族已配置后
> 「配置」入口会被拦截;存量重复商品(如早期 DG120/AUTM003 测试期间产生的)仍建议清理。
**链接级标签(自动派生 + 人工修正,2026-08 规则改版)**
- 标签与**产品链接一一对应**(每条链接因印花数量/工艺/物流不同而价格不同),
落库在链接级 `origin_good_tags` 表(`manual` 标记人工/派生);商品的自动组标签是其
**主链接标签的镜像**
- 解析规则(`apps/api/src/product-families/auto-tag-rules.ts`,与 admin 端
`utils/origin-name.ts#deriveLinkTagNames` 同构):
- **印花数量**:名称含「双面印花」→ `双面印花`;否则含「单面印花」→ `单面印花`(组「印花数量」);
- **工艺**:名称含「直喷」→ `直喷`;含「不打印」或「光板」→ `不打印`**光板即为不打印**
组「印刷工艺」);都不含 → 默认 `烫画`
- **物流**:含「不包邮」→ `不包邮`;否则含「包邮」→ `包邮`(组「物流渠道」,先判不包邮防子串误命中);
- 每次族重算/成员变更/商品创建更新时:未接管的 SDS 链接按名称刷新派生行;
**人工接管的链接(`tagsManual=true`)永不被覆盖**;商品镜像其链接的有效标签;
缺失的组与标签自动补建;
- **人工修正入口**:编辑弹窗成员行展开 → 修改标签 → 保存标签(全量替换为人工行,
`PUT /origin-goods/:id/tags`);「恢复自动」清掉人工行回到派生(`DELETE /origin-goods/:id/tags`);
- 其他分组(如风格类)不受影响,保持人工管理;自定义链接(无名称可解析)同样支持人工配置;
- 实测:`美国(包邮)…-DG001-单面印花``包邮 / 烫画 / 单面印花`
`美国(不包邮光板)…-JSA002-不打印``不包邮 / 不打印 / 光板`
`美国(不包邮)…-DG501-双面印花``不包邮 / 烫画 / 双面印花`
**链接名称展示解析**:后台所有链接名展示位(左右树、族成员列表、配置/编辑弹窗、搜索候选)
统一解析为「品名 型号」(如 `美国(包邮)180g纯棉T恤成人款-DG001-单面印花`
`180g纯棉T恤成人款 DG001`),悬停 tooltip 保留原始全名;搜索同时匹配原始名与解析名
`utils/origin-name.ts#cleanLinkName`)。
## 验证
```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
```