Files
inkreach-official-website/plans/fix/price-matrix-option-sort-fix.md
T

3.3 KiB
Raw Blame History

Fix:价格矩阵选项组按词表序输出(印花/工艺/物流按钮顺序稳定)

日期:2026-09-02 类型:Bug 修复(fix 影响面:apps/api/src/product-families/family-recompute.service.ts 及其测试、一次性全库重算

背景与问题

详情接口 family.priceMatrixprintCounts / crafts / logistics 是物化进 JSONB 的字符串数组,H5 按数组原序渲染印花/工艺/物流按钮组(无前端排序)。数组顺序 = 重算时 Set 去重的"首次遇到序",且成员查询 originGoodsorderBy,导致:

  1. 每个族的按钮顺序不一致(实测 goodId=1 为 ['双面印花','单面印花']['不包邮','包邮'],与词表相反);
  2. 同一族两次重算顺序理论上可能漂移(Postgres 无顺序保证)。

对比:尺码/颜色按钮组有 variants.sortOrder,顺序稳定;本修复把印花/工艺/物流对齐到同样可预期。

目标顺序(业务确认)

维度 展示顺序
printCount 单面印花 → 双面印花
craft 烫画 → 直喷 → 不打印
logistics 不包邮 → 包邮

无论族内实际出现哪个子集,相对顺序恒定;词表外自由文本(如 CUSTOM 的"海运")沉到最后,相互间保持首次遇到序。

数据边界(2026-09-02 全库实测)

  • crafts 中出现 双面印花×2、logistics 中出现 海运×2:来自 3 条 CUSTOM 链接 craftLabel/logisticsLabel 自由文本(含维度填错,属数据清理问题,本修复只保证其沉底,不改数据)。

方案

  1. derivePriceMatrix:聚合(Set 去重)+ 人工覆盖 append 之后,对三个数组做稳定排序:
    • 新增 OPTION_DISPLAY_ORDER 常量(printCount/craft/logistics 三组词表序,logistics 与 DIM_VALUES 顺序相反是业务要求;DIM_VALUES 组合生成语义不动);
    • 排序键 = 词表下标,未知值 = canon.length(沉底),稳定排序保持未知值间首次遇到序。
  2. recomputeFamily 成员查询加 orderBy: { id: 'asc' },使"遇到序"确定(未知值之间顺序也确定)。
  3. 只影响 autoManaged 族物化;非自动族人工字段照旧不碰(现有不变量)。
  4. 一次性脚本:逐族调用 recomputeFamily 全库刷新存量(166 个族)。

非目标

  • 不改 API 输出结构(仍是 string[]),前端零改动;
  • 不清理 CUSTOM 维度填错数据(另行处理)。

测试计划(TDD

测试文件:apps/api/src/product-families/family-recompute.service.spec.tsderivePriceMatrix 为纯函数,可直接单测)。

  1. 打乱输入仍输出词表序:成员顺序颠倒,crafts 输出仍 烫画→直喷→不打印(修复前为遇到序,RED)。
  2. 子集保序:只含 直喷+烫画 → 输出 烫画,直喷;只含 双面 → 单元素。
  3. 未知值沉底logistics 含 海运不包邮,包邮,海运;未知值相互保持遇到序。
  4. 覆盖 append 值参与同一排序:override 新增取值后数组仍词表序。
  5. 重算幂等(含顺序):同输入跑两遍输出 deep-equal。
  6. 既有用例全绿;全量 jest + tsc 通过。

风险与回滚

  • 纯物化顺序变化,无 schema/契约变更;回滚 = revert commit + 再跑一次全库重算(旧逻辑会把顺序"洗回"遇到序,仅顺序变化无数据损坏)。