feat(admin): manage and sync product details

This commit is contained in:
yeuimu
2026-08-21 10:18:57 +08:00
parent 7d09077f1d
commit d375df810d
18 changed files with 507 additions and 106 deletions
@@ -13,6 +13,9 @@ export interface PaginatedOriginGoods {
sdsCategoryId: string | null;
createdAt: string;
updatedAt: string;
hasDetail: boolean;
detailSyncedAt: string | null;
variantCount: number;
}>;
total: number;
page: number;
@@ -33,6 +36,11 @@ export interface OriginGoodsTreeNode {
configuredCount: number;
configuredCountries: string[];
configuredTags: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[];
hasDetail: boolean;
detailSyncedAt: string | null;
variantCount: number;
sizeRowCount: number;
packageRowCount: number;
}
/** A category node in the hierarchical tree, with origin goods as leaves. */
@@ -68,6 +76,7 @@ export class OriginGoodsService {
this.prisma.originGood.findMany({
where,
orderBy: { id: 'desc' },
include: { detail: true, _count: { select: { variants: true } } },
skip: (page - 1) * pageSize,
take: pageSize,
}),
@@ -83,6 +92,9 @@ export class OriginGoodsService {
sdsCategoryId: r.sdsCategoryId,
createdAt: r.createdAt.toISOString(),
updatedAt: r.updatedAt.toISOString(),
hasDetail: Boolean(r.detail),
detailSyncedAt: r.detail?.syncedAt.toISOString() ?? null,
variantCount: r._count.variants,
})),
total,
page,
@@ -111,7 +123,11 @@ export class OriginGoodsService {
parentCategoryId: true,
},
}),
this.prisma.originGood.findMany({ where: { delisted: false }, orderBy: { goodName: 'asc' } }),
this.prisma.originGood.findMany({
where: { delisted: false },
orderBy: { goodName: 'asc' },
include: { detail: true, _count: { select: { variants: true } } },
}),
this.prisma.good.groupBy({
by: ['originGoodId'],
_count: { _all: true },
@@ -212,6 +228,11 @@ export class OriginGoodsService {
configuredCount: countMap.get(og.id.toString()) ?? 0,
configuredCountries: countryMap.get(og.id.toString()) ?? [],
configuredTags: tagMap.get(og.id.toString()) ?? [],
hasDetail: Boolean(og.detail),
detailSyncedAt: og.detail?.syncedAt.toISOString() ?? null,
variantCount: og._count.variants,
sizeRowCount: this.jsonRows(og.detail?.sizeChart),
packageRowCount: this.jsonRows(og.detail?.packageSpecs),
}));
const childTotal = childNodes.reduce((s, n) => s + n.totalCount, 0);
@@ -258,6 +279,11 @@ export class OriginGoodsService {
configuredCount: countMap.get(og.id.toString()) ?? 0,
configuredCountries: countryMap.get(og.id.toString()) ?? [],
configuredTags: tagMap.get(og.id.toString()) ?? [],
hasDetail: Boolean(og.detail),
detailSyncedAt: og.detail?.syncedAt.toISOString() ?? null,
variantCount: og._count.variants,
sizeRowCount: this.jsonRows(og.detail?.sizeChart),
packageRowCount: this.jsonRows(og.detail?.packageSpecs),
})),
});
}
@@ -274,4 +300,10 @@ export class OriginGoodsService {
configuredCount: totalConfigured,
};
}
private 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;
}
}