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:
@@ -94,8 +94,8 @@ describe('PublicService family block (PUBLIC_DETAIL_FROM_FAMILY)', () => {
|
||||
|
||||
it('默认(未设开关):输出族块', async () => {
|
||||
delete process.env.PUBLIC_DETAIL_FROM_FAMILY
|
||||
const detail = await service.getGood(sdsGoodId);
|
||||
expect(detail.goodId).toBe(sdsGoodId);
|
||||
const detail = await service.getGood(familyId.toString());
|
||||
expect(detail.goodId).toBe(familyId.toString());
|
||||
expect(detail.family).toBeTruthy();
|
||||
expect(detail.family!.familyCode).toBe(`PF${stamp}`);
|
||||
expect(detail.family!.minPrice).toBe('25');
|
||||
@@ -105,12 +105,12 @@ describe('PublicService family block (PUBLIC_DETAIL_FROM_FAMILY)', () => {
|
||||
|
||||
it('显式关闭(false):响应完全不含 family 键(应急回退)', async () => {
|
||||
process.env.PUBLIC_DETAIL_FROM_FAMILY = 'false';
|
||||
const detail = await service.getGood(sdsGoodId);
|
||||
expect(detail.goodId).toBe(sdsGoodId);
|
||||
const detail = await service.getGood(familyId.toString());
|
||||
expect(detail.goodId).toBe(familyId.toString());
|
||||
expect('family' in detail).toBe(false);
|
||||
});
|
||||
|
||||
it('族变体并集:任何族成员链接的 sdsGoodId 均命中同一商品且变体含全体成员', async () => {
|
||||
it('族变体并集:族ID 命中商品且变体含全体成员;成员 sdsGoodId 不再可寻址', async () => {
|
||||
process.env.PUBLIC_DETAIL_FROM_FAMILY = 'true';
|
||||
// 再加一个同族成员(不同仓库段)
|
||||
const og2 = await prisma.originGood.create({
|
||||
@@ -136,18 +136,20 @@ describe('PublicService family block (PUBLIC_DETAIL_FROM_FAMILY)', () => {
|
||||
},
|
||||
});
|
||||
|
||||
// 用成员链接(非主链接)的 sdsGoodId 访问 → 命中同一商品(响应 goodId 仍为主链接)
|
||||
const detail = await service.getGood(`pubfam-m-${stamp}`);
|
||||
expect(detail.goodId).toBe(sdsGoodId);
|
||||
// 族 ID 是唯一公开键:成员变体并入同一详情
|
||||
const detail = await service.getGood(familyId.toString());
|
||||
expect(detail.goodId).toBe(familyId.toString());
|
||||
const skus = detail.variants.map((v) => v.sku);
|
||||
expect(skus).toContain(`PF-${stamp}-S`);
|
||||
expect(skus).toContain(`PF-${stamp}-M`); // 族成员变体并集
|
||||
// 成员链接的 sdsGoodId 不再可寻址
|
||||
await expect(service.getGood(`pubfam-m-${stamp}`)).rejects.toThrow();
|
||||
// 清理:把成员移出族避免影响其他用例
|
||||
await prisma.originGoodVariant.deleteMany({ where: { originGoodId: og2.id } });
|
||||
await prisma.originGood.update({ where: { id: og2.id }, data: { familyId: null } });
|
||||
});
|
||||
|
||||
it('无族商品:开关开启也不含 family 键', async () => {
|
||||
it('无族商品:公开端点不可见(列表不含 / 详情 404)', async () => {
|
||||
process.env.PUBLIC_DETAIL_FROM_FAMILY = 'true';
|
||||
const og2 = await prisma.originGood.create({
|
||||
data: {
|
||||
@@ -166,8 +168,13 @@ describe('PublicService family block (PUBLIC_DETAIL_FROM_FAMILY)', () => {
|
||||
},
|
||||
});
|
||||
createdGoodIds.push(good2.id);
|
||||
const detail = await service.getGood(`pubfam-2-${stamp}`);
|
||||
expect('family' in detail).toBe(false);
|
||||
const list = await service.getGoods({
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
countryId: createdCountryIds[0].toString(),
|
||||
});
|
||||
expect(list.items.map((i) => i.goodName)).not.toContain(`无族商品-${stamp}`);
|
||||
await expect(service.getGood(`pubfam-2-${stamp}`)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user