diff --git a/apps/api/src/product-families/family-recompute.service.spec.ts b/apps/api/src/product-families/family-recompute.service.spec.ts index b238c99..efd7cbb 100644 --- a/apps/api/src/product-families/family-recompute.service.spec.ts +++ b/apps/api/src/product-families/family-recompute.service.spec.ts @@ -1,6 +1,10 @@ import { Test } from '@nestjs/testing'; import { Prisma } from '@prisma/client'; -import { FamilyRecomputeService, derivePriceMatrix } from './family-recompute.service'; +import { + FamilyRecomputeService, + derivePriceMatrix, + memberMatrixCombos, +} from './family-recompute.service'; import { ProductFamiliesService } from './product-families.service'; import { OrganizeService } from './organize.service'; import { PrismaService } from '../prisma/prisma.service'; @@ -441,7 +445,7 @@ describe('derivePriceMatrix 选项组排序', () => { it('幂等:同输入多跑两遍输出全等(含顺序),与成员遍历顺序无关', () => { const members = [ mkMember(1, { tags: ['直喷', '双面印花', '不包邮'] }), - mkMember(2, { source: 'CUSTOM', craftLabel: '丝印', logisticsLabel: '海运' }), + mkMember(2, { source: 'CUSTOM', tags: ['双面印花'], craftLabel: '丝印', logisticsLabel: '海运' }), mkMember(3, { tags: ['烫画', '单面印花', '包邮'] }), ]; const first = derivePriceMatrix(members, []); @@ -451,3 +455,47 @@ describe('derivePriceMatrix 选项组排序', () => { expect(JSON.stringify(reversed)).toBe(JSON.stringify(first)); }); }); + +/** + * 印刷工艺新增"热转印":矩阵维度词表放开准入 + 展示序追加末尾。 + * 自动派生规则不改 —— SDS 链接只能人工打标;不打标的链接/族零影响。 + */ +describe('热转印工艺', () => { + const mkMember = ( + id: number, + over: { tags?: string[]; source?: 'SDS' | 'CUSTOM'; craftLabel?: string | null; logisticsLabel?: string | null }, + ) => + ({ + id: BigInt(id), + source: over.source ?? 'SDS', + originGoodTags: (over.tags ?? []).map((t) => ({ tag: { tagName: t } })), + variants: [], + craftLabel: over.craftLabel ?? null, + logisticsLabel: over.logisticsLabel ?? null, + }) as any; + + it('memberMatrixCombos:打标热转印的链接产出矩阵组合(此前被词表过滤为空)', () => { + const combos = memberMatrixCombos({ tagNames: ['单面印花', '热转印', '包邮'] }); + expect(combos).toEqual([{ printCount: '单面印花', craft: '热转印', logistics: '包邮' }]); + }); + + it('derivePriceMatrix:crafts 展示序为 烫画→直喷→不打印→热转印(词表内,不再沉底)', () => { + const members = [ + mkMember(1, { tags: ['热转印', '单面印花', '包邮'] }), + mkMember(2, { tags: ['烫画', '单面印花', '包邮'] }), + mkMember(3, { tags: ['直喷', '单面印花', '不包邮'] }), + mkMember(4, { tags: ['不打印', '单面印花', '包邮'] }), + ]; + const m = derivePriceMatrix(members, []); + expect(m.crafts).toEqual(['烫画', '直喷', '不打印', '热转印']); + }); + + it('词表外自由文本(丝印/水洗)仍然沉底,与热转印区分', () => { + const members = [ + mkMember(1, { tags: ['热转印', '单面印花', '包邮'] }), + mkMember(2, { source: 'CUSTOM', tags: ['单面印花'], craftLabel: '丝印', logisticsLabel: '包邮' }), + ]; + const m = derivePriceMatrix(members, []); + expect(m.crafts).toEqual(['热转印', '丝印']); + }); +}); diff --git a/apps/api/src/product-families/family-recompute.service.ts b/apps/api/src/product-families/family-recompute.service.ts index daa3d26..c3b3ad0 100644 --- a/apps/api/src/product-families/family-recompute.service.ts +++ b/apps/api/src/product-families/family-recompute.service.ts @@ -66,20 +66,20 @@ type OverrideRow = Prisma.FamilyPriceOverrideGetPayload<{}>; /** 矩阵三个归因维度的合法取值(封闭集合,与派生标签组一致) */ const DIM_VALUES = { printCount: ['单面印花', '双面印花'], - craft: ['烫画', '直喷', '不打印'], + craft: ['烫画', '直喷', '不打印', '热转印'], logistics: ['包邮', '不包邮'], } as const satisfies Record; type DimKey = keyof typeof DIM_VALUES; /** - * 选项组展示顺序(业务确认):printCount 单面→双面;craft 烫画→直喷→不打印;logistics 不包邮→包邮。 + * 选项组展示顺序(业务确认):printCount 单面→双面;craft 烫画→直喷→不打印→热转印;logistics 不包邮→包邮。 * 与 DIM_VALUES 的"组合生成序"是两回事,物化前对聚合数组按此稳定排序; * 词表外自由文本(CUSTOM 标签,如"海运")沉底,相互间保持首次遇到序。 */ const OPTION_DISPLAY_ORDER = { printCount: ['单面印花', '双面印花'], - craft: ['烫画', '直喷', '不打印'], + craft: ['烫画', '直喷', '不打印', '热转印'], logistics: ['不包邮', '包邮'], } as const;