feat(public): merge secondary media gallery images with URL dedup
This commit is contained in:
@@ -435,6 +435,7 @@ describe('PublicService', () => {
|
||||
sizeChart: { rows: [{ sizeName: 'S', measurements: [{ key: 'chest', cm: '94' }] }, { sizeName: 'M', measurements: [{ key: 'chest', cm: '100' }] }] },
|
||||
packageSpecs: { rows: [{ sizeName: 'S' }] },
|
||||
options: { sizes: [{ name: 'S' }, { name: 'M' }] },
|
||||
media: { images: ['http://img/pri-a', 'http://img/pri-b'], primaryImageUrl: 'http://img/pri-a' },
|
||||
},
|
||||
});
|
||||
// Secondary: duplicate Black|S with a DIFFERENT price (must be dropped,
|
||||
@@ -451,6 +452,7 @@ describe('PublicService', () => {
|
||||
sizeChart: { rows: [{ sizeName: 'XXXL', measurements: [{ key: 'chest', cm: '120' }] }] },
|
||||
packageSpecs: { rows: [{ sizeName: 'M' }, { sizeName: 'XXXL' }] },
|
||||
options: { sizes: [{ name: 'XXXL' }] },
|
||||
media: { images: ['http://img/pri-a', 'http://img/sec-x'], primaryImageUrl: 'http://img/pri-a' },
|
||||
},
|
||||
});
|
||||
const mergedGood = await prisma.good.create({
|
||||
@@ -483,6 +485,11 @@ describe('PublicService', () => {
|
||||
// Options: sizes unioned S/M + XXXL.
|
||||
const optSizes = (detail.options as any).sizes.map((s: any) => s.name).sort();
|
||||
expect(optSizes).toEqual(['M', 'S', 'XXXL']);
|
||||
// Media gallery: primary images first, secondary-only URL appended,
|
||||
// duplicate URL (pri-a) kept once.
|
||||
const media = detail.media as any;
|
||||
expect(media.images).toEqual(['http://img/pri-a', 'http://img/pri-b', 'http://img/sec-x']);
|
||||
expect(media.primaryImageUrl).toBe('http://img/pri-a');
|
||||
} finally {
|
||||
await prisma.goodOriginGood.deleteMany({ where: { goodId: mergedGood.id } });
|
||||
await prisma.good.delete({ where: { id: mergedGood.id } });
|
||||
|
||||
@@ -385,7 +385,7 @@ export class PublicService {
|
||||
designArea: detail?.designArea ?? null,
|
||||
pictureRequest: detail?.pictureRequest ?? null,
|
||||
},
|
||||
media: (detail?.media as Record<string, unknown> | null) ?? null,
|
||||
media: this.mergeMedia(detail, secondaryDetails),
|
||||
mediaByColor: this.groupImagesByColor(allVariants),
|
||||
options: this.mergeOptions(detail, secondaryDetails),
|
||||
sizeChart: this.mergeRowsByKey(detail, secondaryDetails, 'sizeChart'),
|
||||
@@ -496,6 +496,35 @@ export class PublicService {
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the gallery `media` across details: primary images first,
|
||||
* secondary-only image URLs appended (URL-deduped). `primaryImageUrl`
|
||||
* stays the primary's.
|
||||
*/
|
||||
private mergeMedia(
|
||||
primary: PublicGoodRow['originGood']['detail'],
|
||||
secondaries: NonNullable<PublicGoodRow['originGood']['detail']>[],
|
||||
): Record<string, unknown> | null {
|
||||
const toImages = (media: unknown): string[] => {
|
||||
const images = (media as { images?: unknown } | null)?.images;
|
||||
return Array.isArray(images) ? images.filter((u): u is string => typeof u === 'string') : [];
|
||||
};
|
||||
const primaryMedia = (primary?.media as Record<string, unknown> | null) ?? null;
|
||||
if (!primaryMedia && secondaries.length === 0) return null;
|
||||
const images = toImages(primaryMedia);
|
||||
const seen = new Set(images);
|
||||
for (const sec of secondaries) {
|
||||
for (const url of toImages(sec.media)) {
|
||||
if (!seen.has(url)) {
|
||||
seen.add(url);
|
||||
images.push(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (images.length === 0) return null;
|
||||
return { ...(primaryMedia ?? {}), images };
|
||||
}
|
||||
|
||||
private async buildTagGroupFilters(
|
||||
selectedGroups: PublicTagFilterDto[],
|
||||
): Promise<Prisma.GoodWhereInput[]> {
|
||||
|
||||
Reference in New Issue
Block a user