Files
inkreach-official-website/plans/feature/public-family-id-feature.md
T
yeuimu 8a052773cd 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、双端构建绿
2026-08-28 18:43:30 +08:00

87 lines
4.6 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.
# 公开契约族化:goodId = 族ID + 五维价格矩阵
## 背景 / 动机
- 现状 `GET /public/goods/{goodId}` 以 SDS 链接 ID 寻址,`findFirst` 任选一条 Good
一个族在库内常配置多条 Good(92 个已配置族中 78 个是多条,如加拿大 CATM001 一族 4 条),
列表因此出现同款重复,前端被迫理解"族背后有多少商品"。
- 价格矩阵把 印花数量+工艺 混在一个 `craft` 维(原始链接名第 3 段,含
`直喷单面`/`光板不打印`/解析噪声,且 47 个成员为 null),不是严格的五维。
目标契约(用户拍板):**前端只知道款号(族)**——
- `goodId` = 族 ID;前端不知道族背后有几条 Good / 几个链接
- 详情 = 款的公共信息(名称、主图、国家、分类、商品详情文案、尺码表、包装规格)
+ 价格矩阵
- 价格严格五维:**尺码 × 颜色 × 物流 × 工艺 × 印花数量 → 价格**
## 事实核查(2026-08-28
- 0 个族跨国家/跨分类 → 国家、分类可作为族的公共属性
- `family_price_overrides` 0 行 → 加维度无数据回填负担
- 无族 Good 6 条(source=CUSTOM)→ 必须保留 sdsGoodId 寻址回退
- 链接级标签已全覆盖(536/536),三组派生标签是矩阵维度的可靠来源
## 方案
### A. priceMatrix 五维化(family-recompute.service.ts
- `PriceMatrixRow` 增加 `printCount``PriceMatrix` 增加 `printCounts: string[]`
- 成员维度来源改为**有效标签**(OriginGoodTag ∋ 物流渠道/印花数量/印刷工艺三组;
人工接管时用人工标签),三组各自取值做笛卡尔(通常 1×1×1);任一组缺失则该成员不进矩阵
- 弃用 `craftLabel` 作为维度(保留字段用于展示回退)
- 同格子多来源取最低价、sources 全保留、人工覆盖并入 —— 规则不变
### B. FamilyPriceOverride 加 printCountprisma migration
- 新列 `print_count`(默认 `单面印花`,迁移时 0 行数据)
- 唯一键扩展为 (familyId, sizeId, colorId, printCount, craft, logistics)
### C. product-families.service 覆盖价三处五键化
- `PriceOverrideItemDto` + `putPriceOverrides` 校验(allowed.printCounts
- `listPriceOverrides` 行匹配五键
### D. public 契约族化(public.service.ts
1. `getGoods`:过滤条件不变(作用于 Good),**取全量匹配后内存按族分组**
(规模 241 条 Good,注释说明;无族 Good 各自成组)。
- 组代表 = 现有排序的第一条(priority desc → createdAt desc
- `goodId`:有族 → `familyId`;无族 → `sdsGoodId`(自定义商品)
- `price`:族 minPrice(批量查 priceMatrix),回退 og price
- `total` = 分组数,分页在分组后
2. `getGood(id)`:**纯数字先按族 ID 解析**(族 + 其 Good + 成员变体并集),
未命中再回退 sdsGoodId 旧路径(自定义商品/向后兼容)。
族详情:公共字段取代表 Good;detail 文案取主链接;尺码表/包装规格用族并集;
media/mediaByColor/variants 全成员并集;`family` 块 = 五维矩阵 + minPrice。
族下无在售 Good → 404。`PUBLIC_DETAIL_FROM_FAMILY=false` 应急开关行为不变。
3. `getHomeGoods`:按族去重(排序不变,同族保留最前一条)。
### E. admin 适配(GoodsEditDialog.vue
- SKU 列:印花数量列直接读 `row.printCount`(不再从 craft 字符串猜测)
- `loadMemberPrices`:按 printCount+craft+logistics 三键匹配该成员格子;
维度优先取成员 `originGoodTags`(人工接管后仍准确),回退 `linkDims(goodName)`
- 保存覆盖价 payload 带 printCount
### F. 存量族全量重算
一次性脚本:对所有 ProductFamily 逐个 `recomputeFamily`(矩阵物化 JSON 结构变更)。
### G. 测试与文档
- family-recompute spec:五维矩阵(含 人工标签 覆盖派生、缺组跳过、跨成员最低价)
- product-families spec:覆盖价五键校验
- public spec:列表按族去重(同族 N 条 → 1 条,goodId=familyId)、getGood(族ID)、
自定义商品回退、home-goods 去重
- public-family-block spec:矩阵 printCounts
- adminvue-tsc + 现有 22 测试
- 文档:docs/references/product-center.md、structs.md
## 风险 / 边界
- 数字族 ID 与数字 sdsGoodId 理论碰撞:族 ID 优先(sdsGoodId 为 SDS 系统 6 位数,
实际不碰撞),Swagger 注明
- 列表内存分组在商品量级 10⁴ 前可接受,注释留优化方向(物化族表)
- 旧前端(website)以 sdsGoodId 寻址仍可达(回退路径),后续 website 侧再切换