feat(goods): support merged secondary origin goods in create/update/detail
This commit is contained in:
@@ -6,33 +6,58 @@ export interface GoodRelations {
|
||||
category?: { id: bigint; categoryName: string; categoryIcon: string | null } | null;
|
||||
tag?: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } | null;
|
||||
position?: { id: bigint; indexVal: number } | null;
|
||||
originGood?: {
|
||||
originGood?: {
|
||||
id: bigint;
|
||||
sdsGoodId: string;
|
||||
source: 'SDS' | 'CUSTOM';
|
||||
sdsGoodId: string;
|
||||
source: 'SDS' | 'CUSTOM';
|
||||
goodName: string | null;
|
||||
goodImage: string | null;
|
||||
goodPrice: unknown;
|
||||
detail?: {
|
||||
productCode: string | null;
|
||||
syncedAt: Date;
|
||||
sizeChart: unknown;
|
||||
packageSpecs: unknown;
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
variants?: Array<{
|
||||
sdsVariantId: string;
|
||||
sku: string;
|
||||
sizeName: string | null;
|
||||
colorName: string | null;
|
||||
colorHex: string | null;
|
||||
price: unknown;
|
||||
enabled: boolean;
|
||||
[key: string]: unknown;
|
||||
}>;
|
||||
_count?: { variants: number };
|
||||
goodPrice: unknown;
|
||||
detail?: {
|
||||
productCode: string | null;
|
||||
syncedAt: Date;
|
||||
sizeChart: unknown;
|
||||
packageSpecs: unknown;
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
variants?: Array<{
|
||||
sdsVariantId: string;
|
||||
sku: string;
|
||||
sizeName: string | null;
|
||||
colorName: string | null;
|
||||
colorHex: string | null;
|
||||
price: unknown;
|
||||
enabled: boolean;
|
||||
[key: string]: unknown;
|
||||
}>;
|
||||
_count?: { variants: number };
|
||||
} | null;
|
||||
goodTags?: { tag: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } }[];
|
||||
mergedOriginGoods?: Array<{
|
||||
originGood: {
|
||||
id: bigint;
|
||||
sdsGoodId: string;
|
||||
source: 'SDS' | 'CUSTOM';
|
||||
goodName: string | null;
|
||||
goodImage: string | null;
|
||||
goodPrice: unknown;
|
||||
detail?: { syncedAt: Date } | Record<string, unknown> | null;
|
||||
variants?: Array<{ [key: string]: unknown }>;
|
||||
_count?: { variants: number };
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface MergedOriginGoodSummary {
|
||||
id: string;
|
||||
sdsGoodId: string;
|
||||
source: 'SDS' | 'CUSTOM';
|
||||
isCustom: boolean;
|
||||
goodName: string | null;
|
||||
goodImage: string | null;
|
||||
goodPrice: string | null;
|
||||
hasDetail: boolean;
|
||||
variantCount: number;
|
||||
}
|
||||
|
||||
export class GoodDto {
|
||||
@@ -81,24 +106,27 @@ export class GoodDto {
|
||||
@ApiProperty({ required: false, type: Array })
|
||||
tags!: Array<{ id: string; tagName: string; tagColor: string | null; tagFontColor: string | null }>;
|
||||
|
||||
@ApiProperty({ required: false, type: Array })
|
||||
mergedOriginGoods!: MergedOriginGoodSummary[];
|
||||
|
||||
@ApiProperty({ required: false, nullable: true })
|
||||
position?: { id: string; indexVal: number } | null;
|
||||
|
||||
@ApiProperty({ required: false, nullable: true })
|
||||
originGood?: {
|
||||
originGood?: {
|
||||
id: string;
|
||||
sdsGoodId: string;
|
||||
source: 'SDS' | 'CUSTOM';
|
||||
isCustom: boolean;
|
||||
sdsGoodId: string;
|
||||
source: 'SDS' | 'CUSTOM';
|
||||
isCustom: boolean;
|
||||
goodName: string | null;
|
||||
goodImage: string | null;
|
||||
goodPrice: string | null;
|
||||
hasDetail: boolean;
|
||||
detailSyncedAt: string | null;
|
||||
variantCount: number;
|
||||
sizeRowCount: number;
|
||||
packageRowCount: number;
|
||||
productCode: string | null;
|
||||
goodPrice: string | null;
|
||||
hasDetail: boolean;
|
||||
detailSyncedAt: string | null;
|
||||
variantCount: number;
|
||||
sizeRowCount: number;
|
||||
packageRowCount: number;
|
||||
productCode: string | null;
|
||||
} | null;
|
||||
|
||||
static from(
|
||||
@@ -147,70 +175,108 @@ export class GoodDto {
|
||||
tagFontColor: gt.tag.tagFontColor,
|
||||
}))
|
||||
: [],
|
||||
mergedOriginGoods: (rel.mergedOriginGoods ?? []).map((m) => ({
|
||||
id: m.originGood.id.toString(),
|
||||
sdsGoodId: m.originGood.sdsGoodId,
|
||||
source: m.originGood.source,
|
||||
isCustom: m.originGood.source === 'CUSTOM',
|
||||
goodName: m.originGood.goodName,
|
||||
goodImage: m.originGood.goodImage,
|
||||
goodPrice:
|
||||
m.originGood.goodPrice === null || m.originGood.goodPrice === undefined
|
||||
? null
|
||||
: (m.originGood.goodPrice as { toString(): string }).toString(),
|
||||
hasDetail: Boolean(m.originGood.detail),
|
||||
variantCount:
|
||||
m.originGood._count?.variants ?? m.originGood.variants?.length ?? 0,
|
||||
})),
|
||||
position: rel.position
|
||||
? {
|
||||
id: rel.position.id.toString(),
|
||||
indexVal: rel.position.indexVal,
|
||||
}
|
||||
: null,
|
||||
originGood: rel.originGood
|
||||
? {
|
||||
originGood: rel.originGood
|
||||
? {
|
||||
id: rel.originGood.id.toString(),
|
||||
sdsGoodId: rel.originGood.sdsGoodId,
|
||||
source: rel.originGood.source,
|
||||
isCustom: rel.originGood.source === 'CUSTOM',
|
||||
sdsGoodId: rel.originGood.sdsGoodId,
|
||||
source: rel.originGood.source,
|
||||
isCustom: rel.originGood.source === 'CUSTOM',
|
||||
goodName: rel.originGood.goodName,
|
||||
goodImage: rel.originGood.goodImage,
|
||||
goodPrice:
|
||||
rel.originGood.goodPrice === null ||
|
||||
rel.originGood.goodPrice === undefined
|
||||
? null
|
||||
: (rel.originGood.goodPrice as { toString(): string }).toString(),
|
||||
hasDetail: Boolean(rel.originGood.detail),
|
||||
detailSyncedAt: rel.originGood.detail?.syncedAt.toISOString() ?? null,
|
||||
variantCount: rel.originGood._count?.variants ?? rel.originGood.variants?.length ?? 0,
|
||||
sizeRowCount: GoodDto.jsonRows(rel.originGood.detail?.sizeChart),
|
||||
packageRowCount: GoodDto.jsonRows(rel.originGood.detail?.packageSpecs),
|
||||
productCode: rel.originGood.detail?.productCode ?? null,
|
||||
}
|
||||
: (rel.originGood.goodPrice as { toString(): string }).toString(),
|
||||
hasDetail: Boolean(rel.originGood.detail),
|
||||
detailSyncedAt: rel.originGood.detail?.syncedAt.toISOString() ?? null,
|
||||
variantCount: rel.originGood._count?.variants ?? rel.originGood.variants?.length ?? 0,
|
||||
sizeRowCount: GoodDto.jsonRows(rel.originGood.detail?.sizeChart),
|
||||
packageRowCount: GoodDto.jsonRows(rel.originGood.detail?.packageSpecs),
|
||||
productCode: rel.originGood.detail?.productCode ?? null,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
private static jsonRows(value: unknown): number {
|
||||
if (!value || typeof value !== 'object' || !('rows' in value)) return 0;
|
||||
const rows = (value as { rows?: unknown }).rows;
|
||||
return Array.isArray(rows) ? rows.length : 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class GoodDetailDto extends GoodDto {
|
||||
@ApiProperty({ nullable: true, type: Object })
|
||||
originDetail!: Record<string, unknown> | null;
|
||||
|
||||
@ApiProperty({ type: Array })
|
||||
variants!: Array<Record<string, unknown>>;
|
||||
|
||||
static fromGood(good: PrismaGood, rel: GoodRelations): GoodDetailDto {
|
||||
const base = GoodDto.from(good, rel);
|
||||
const detail = rel.originGood?.detail;
|
||||
return {
|
||||
...base,
|
||||
originDetail: detail ? { ...detail, syncedAt: detail.syncedAt.toISOString() } : null,
|
||||
variants: (rel.originGood?.variants ?? []).map((variant) => ({
|
||||
...variant,
|
||||
price:
|
||||
variant.price === null || variant.price === undefined
|
||||
? null
|
||||
: (variant.price as { toString(): string }).toString(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static jsonRows(value: unknown): number {
|
||||
if (!value || typeof value !== 'object' || !('rows' in value)) return 0;
|
||||
const rows = (value as { rows?: unknown }).rows;
|
||||
return Array.isArray(rows) ? rows.length : 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class GoodDetailDto extends GoodDto {
|
||||
@ApiProperty({ nullable: true, type: Object })
|
||||
originDetail!: Record<string, unknown> | null;
|
||||
|
||||
@ApiProperty({ type: Array })
|
||||
variants!: Array<Record<string, unknown>>;
|
||||
|
||||
static fromGood(good: PrismaGood, rel: GoodRelations): GoodDetailDto {
|
||||
const base = GoodDto.from(good, rel);
|
||||
const detail = rel.originGood?.detail;
|
||||
const toAnnotated = (
|
||||
variant: Record<string, unknown>,
|
||||
originGoodId: string,
|
||||
originGoodName: string | null,
|
||||
) => ({
|
||||
...variant,
|
||||
price:
|
||||
variant.price === null || variant.price === undefined
|
||||
? null
|
||||
: (variant.price as unknown as { toString(): string }).toString(),
|
||||
originGoodId,
|
||||
originGoodName,
|
||||
});
|
||||
const primaryId = good.originGoodId.toString();
|
||||
const primaryName = rel.originGood?.goodName ?? null;
|
||||
const mergedVariants = [
|
||||
...(rel.originGood?.variants ?? []).map((variant) =>
|
||||
toAnnotated(variant as Record<string, unknown>, primaryId, primaryName),
|
||||
),
|
||||
...(rel.mergedOriginGoods ?? []).flatMap((m) =>
|
||||
(m.originGood.variants ?? []).map((variant) =>
|
||||
toAnnotated(
|
||||
variant,
|
||||
m.originGood.id.toString(),
|
||||
m.originGood.goodName,
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
return {
|
||||
...base,
|
||||
originDetail: detail ? { ...detail, syncedAt: detail.syncedAt.toISOString() } : null,
|
||||
variants: mergedVariants,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface PaginatedGoods {
|
||||
items: GoodDto[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user