公开契约族化(前端只需知道款号/族): - 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、双端构建绿
4.6 KiB
4.6 KiB
公开契约族化: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_overrides0 行 → 加维度无数据回填负担- 无族 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 加 printCount(prisma migration)
- 新列
print_count(默认单面印花,迁移时 0 行数据) - 唯一键扩展为 (familyId, sizeId, colorId, printCount, craft, logistics)
C. product-families.service 覆盖价三处五键化
PriceOverrideItemDto+putPriceOverrides校验(allowed.printCounts)listPriceOverrides行匹配五键
D. public 契约族化(public.service.ts)
getGoods:过滤条件不变(作用于 Good),取全量匹配后内存按族分组 (规模 241 条 Good,注释说明;无族 Good 各自成组)。- 组代表 = 现有排序的第一条(priority desc → createdAt desc)
goodId:有族 →familyId;无族 →sdsGoodId(自定义商品)price:族 minPrice(批量查 priceMatrix),回退 og pricetotal= 分组数,分页在分组后
getGood(id):纯数字先按族 ID 解析(族 + 其 Good + 成员变体并集), 未命中再回退 sdsGoodId 旧路径(自定义商品/向后兼容)。 族详情:公共字段取代表 Good;detail 文案取主链接;尺码表/包装规格用族并集; media/mediaByColor/variants 全成员并集;family块 = 五维矩阵 + minPrice。 族下无在售 Good → 404。PUBLIC_DETAIL_FROM_FAMILY=false应急开关行为不变。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
- admin:vue-tsc + 现有 22 测试
- 文档:docs/references/product-center.md、structs.md
风险 / 边界
- 数字族 ID 与数字 sdsGoodId 理论碰撞:族 ID 优先(sdsGoodId 为 SDS 系统 6 位数, 实际不碰撞),Swagger 注明
- 列表内存分组在商品量级 10⁴ 前可接受,注释留优化方向(物化族表)
- 旧前端(website)以 sdsGoodId 寻址仍可达(回退路径),后续 website 侧再切换