feat(public): family-first contract — goodId=familyId, strict 5-dim price matrix
公开契约族化(前端只需知道款号/族): - GET /public/goods 一族一条(goodId=族ID,price=族起价,分页作用于分组后); 无族商品(自定义)不进任何公开端点(列表/首页/分类树/标签统计) - GET /public/goods/:goodId 仅认族 ID;公共字段取代表 Good,变体=全体成员并集, 尺码表/包装规格=族物化并集;旧 SDS 链接 ID 寻址 404 - priceMatrix 严格五维:尺码×颜色×印花数量×工艺×物流;维度来源改为链接级 标签(人工接管按人工标签),弃用原始 craftLabel;CUSTOM 成员尊重显式标签 - 名称派生补裸「单面/双面」写法(直喷双面→双面印花+直喷,18 条存量链接修复) - family_price_overrides 加 print_count 列(五键唯一),PUT/DELETE/校验五键化 - admin 编辑弹窗矩阵消费适配(SKU 列直读 printCount,成员格子三维匹配, 改价 payload 带 printCount) - 存量 339 族已全量重算;api 162/162、admin 22/22、双端构建绿
This commit is contained in:
@@ -100,31 +100,42 @@ pnpm --filter @inkreach/api backfill:product-families
|
||||
# → [1/2] 解析全部链接名 [2/2] 自动建族 [3/3] 全量族重算(幂等,可随时重跑)
|
||||
```
|
||||
|
||||
**公开读路径(三期,默认开启)**:`PUBLIC_DETAIL_FROM_FAMILY=false` 可应急回退旧行为。
|
||||
`GET /public/goods/:goodId` 行为:
|
||||
**公开读路径(四期族化契约,默认开启)**:`PUBLIC_DETAIL_FROM_FAMILY=false` 可应急回退旧行为。
|
||||
公开端点**以款(族)为一等公民**:
|
||||
|
||||
- **变体并集(替代旧副源机制)**:`variants` = 主链接 ∪ 全体族成员 ∪ 旧副源(过渡期),
|
||||
按 `(链接, 变体)` 去重;`mediaByColor` 同源。任何族成员链接的 sdsGoodId 均命中同一商品。
|
||||
- **族块**(默认输出):`family` 含并集尺码表/包装 + 五维价格矩阵 + 族起价:
|
||||
- `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": [...], "crafts": ["单面印花", ...], "logistics": ["包邮", ...],
|
||||
"sizes": [...], "colors": [...],
|
||||
"printCounts": ["单面印花", "双面印花"], "crafts": ["烫画", "直喷", "不打印"], "logistics": ["包邮", "不包邮"],
|
||||
"sizeChart": { /* 并集 */ }, "packageSpecs": { /* 并集 */ },
|
||||
"priceMatrix": { /* 五维矩阵:rows 为 {sizeId, colorId, craft, logistics, price, manual, sources} */ },
|
||||
"priceMatrix": { /* rows 为 {sizeId, colorId, printCount, craft, logistics, price, manual, sources} */ },
|
||||
"minPrice": "29.5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实测(DG015 族):4 工艺 × 2 物流 × 8 尺码 × 12 颜色 = 653 格矩阵,族起价 ¥29.5
|
||||
(主源单链接价为 ¥68.74——族视角展示了光板/单面的更低档价格)。前端本地按五维联动
|
||||
`priceMatrix` 即可实时算价,无需新增查价端点(设计 D4)。
|
||||
矩阵维度来源是**链接级标签**(印花数量/印刷工艺/物流渠道三组;人工接管后按人工标签),
|
||||
不再用原始链接名的第 3 段(`craftLabel` 含「直喷单面/光板不打印」等噪声)。名称派生规则已
|
||||
覆盖裸「单面/双面」写法(如「直喷双面」→ 双面印花 + 直喷)。光板/不打印链接无印花面概念,
|
||||
矩阵中印花数量维回退「单面印花」占位。前端本地按五维联动 `priceMatrix` 即可实时算价
|
||||
(设计 D4)。
|
||||
|
||||
**边界声明**:商品列表/筛选/排序仍基于主源 `goodPrice`(SQL 层无法廉价解析族矩阵 JSON,
|
||||
且避免展示价与筛选价不一致);`good_origin_goods` 转只读保留,观察期后另行删除。
|
||||
**边界声明**:价格区间筛选(minPrice/maxPrice)仍作用于链接 `goodPrice`(族内任一链接命中即
|
||||
返回该族;SQL 层无法廉价解析族矩阵 JSON);`good_origin_goods` 转只读保留,观察期后另行删除。
|
||||
|
||||
**后台操作入口(族替代旧主源/副源,界面保持原有布局)**:
|
||||
|
||||
|
||||
@@ -113,8 +113,8 @@ apps/api/
|
||||
| `/public/countries` `GET` | 公开国家列表(仅含已挂商品的国家) | 公开 |
|
||||
| `/public/tags` `GET` | 公开标签列表(带 `group` 字段,按 group 排序) | 公开 |
|
||||
| `/public/tag-groups` `GET` | 公开标签分组列表 | 公开 |
|
||||
| `/public/goods` `GET` | 分页商品(支持 `countryId/categoryId/tagIds(逗号分隔)/keyword/page/pageSize`,`tagIds` 为 AND 关系) | 公开 |
|
||||
| `/public/goods/:id` `GET` | 商品详情;默认输出 `family` 块(并集尺码表/包装 + 五维价格矩阵 + 族起价),`variants` = 主链接 ∪ 族成员 ∪ 旧副源(去重);`PUBLIC_DETAIL_FROM_FAMILY=false` 应急回退旧行为 | 公开 |
|
||||
| `/public/goods` `GET` | 分页商品(**族化契约:一族一条**,`goodId`=族ID,`price`=族起价;无族商品不返回;支持 `countryId/categoryId/tags(JSON)/keyword/minPrice/maxPrice/sort/page/pageSize`) | 公开 |
|
||||
| `/public/goods/:id` `GET` | 款级详情,`:id` = **族 ID**(唯一公开键,SDS 链接 ID 404);公共字段取代表 Good,`variants` = 全体族成员 ∪ 旧副源(去重),`sizeChart/packageSpecs` = 族并集;默认输出 `family` 块(并集尺码表/包装 + 严格五维价格矩阵 尺码×颜色×印花数量×工艺×物流 + 族起价);`PUBLIC_DETAIL_FROM_FAMILY=false` 应急回退 | 公开 |
|
||||
| `/categories` `/tags` `/tag-groups` `/countries` `/positions` | 后台 CRUD | JWT |
|
||||
| `/tags/sort` `PATCH` | 批量更新 tag 排序和分组归属 | JWT |
|
||||
| `/tag-groups/sort` `PATCH` | 批量更新分组排序 | JWT |
|
||||
@@ -127,7 +127,7 @@ apps/api/
|
||||
| `/product-families/:id/recompute` `POST` | 手动重算并集与价格矩阵 | JWT |
|
||||
| `/product-families/:id/members` `POST` | 成员增删 `{addOriginGoodIds, removeOriginGoodIds}`;移除主链接后 primary 落到剩余成员 | JWT |
|
||||
| `/product-families/:id/members/custom` `POST` | 族内创建自定义成员(人工商品:物流/工艺归因必填 + 变体价格 + 可选尺码表/包装) | JWT |
|
||||
| `/product-families/:id/price-overrides` `GET/PUT/DELETE` | 人工改价:查(含推导价对照与差额)/ 批量 upsert / 按格删除恢复推导价;维度必须存在于族矩阵选项 | JWT |
|
||||
| `/product-families/:id/price-overrides` `GET/PUT/DELETE` | 人工改价:查(含推导价对照与差额)/ 批量 upsert / 按格删除恢复推导价;格子五键 `sizeId+colorId+printCount+craft+logistics` 必须存在于族矩阵选项 | JWT |
|
||||
| `/goods` | 后台商品 CRUD + `POST /goods/batch` + `PATCH /goods/batch-priority` | JWT |
|
||||
| `/sync/categories` `POST` | 手动触发分类同步 | JWT |
|
||||
| `/sync/products` `POST` | 手动触发商品同步 | JWT |
|
||||
|
||||
Reference in New Issue
Block a user