feat(origin-goods): include merged secondary references in tree stats
This commit is contained in:
@@ -77,4 +77,69 @@ describe('OriginGoodsService', () => {
|
|||||||
expect(result.total).toBe(0);
|
expect(result.total).toBe(0);
|
||||||
expect(result.items.length).toBe(0);
|
expect(result.items.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getTree merged references', () => {
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
function findOgNode(
|
||||||
|
treeResponse: { tree: any[] },
|
||||||
|
ogId: string,
|
||||||
|
): { configuredCount: number; configuredCountries: string[] } {
|
||||||
|
let found: { configuredCount: number; configuredCountries: string[] } | null = null;
|
||||||
|
const walk = (nodes: any[]) => {
|
||||||
|
for (const n of nodes) {
|
||||||
|
const hit = (n.originGoods ?? []).find((o: any) => o.id === ogId);
|
||||||
|
if (hit) {
|
||||||
|
found = hit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (n.children?.length) walk(n.children);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
walk(treeResponse.tree);
|
||||||
|
if (!found) throw new Error(`og node ${ogId} not found in tree`);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('counts secondary references as configured', async () => {
|
||||||
|
const sdsCat = `tree-cat-${stamp}`;
|
||||||
|
await prisma.originGood.createMany({
|
||||||
|
data: [
|
||||||
|
{ sdsGoodId: `tree-a-${stamp}`, goodName: `Tree A ${stamp}`, sdsCategoryId: sdsCat },
|
||||||
|
{ sdsGoodId: `tree-b-${stamp}`, goodName: `Tree B ${stamp}`, sdsCategoryId: sdsCat },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
createdSds.push(`tree-a-${stamp}`, `tree-b-${stamp}`);
|
||||||
|
const originA = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: `tree-a-${stamp}` } });
|
||||||
|
const originB = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: `tree-b-${stamp}` } });
|
||||||
|
|
||||||
|
const cat = await prisma.category.create({
|
||||||
|
data: { categoryName: `Tree Cat ${stamp}`, sdsCategoryId: sdsCat },
|
||||||
|
});
|
||||||
|
const country = await prisma.country.create({
|
||||||
|
data: { countryName: `Tree Country ${stamp}` },
|
||||||
|
});
|
||||||
|
const good = await prisma.good.create({
|
||||||
|
data: {
|
||||||
|
goodName: `Tree Good ${stamp}`,
|
||||||
|
originGoodId: originA.id,
|
||||||
|
countryId: country.id,
|
||||||
|
categoryId: cat.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.goodOriginGood.create({
|
||||||
|
data: { goodId: good.id, originGoodId: originB.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tree = await service.getTree();
|
||||||
|
const nodeB = findOgNode(tree, originB.id.toString());
|
||||||
|
expect(nodeB.configuredCount).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(nodeB.configuredCountries).toContain(`Tree Country ${stamp}`);
|
||||||
|
} finally {
|
||||||
|
await prisma.good.delete({ where: { id: good.id } }).catch(() => undefined);
|
||||||
|
await prisma.country.delete({ where: { id: country.id } }).catch(() => undefined);
|
||||||
|
await prisma.category.delete({ where: { id: cat.id } }).catch(() => undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export class OriginGoodsService {
|
|||||||
* under a synthetic "未分类" root node.
|
* under a synthetic "未分类" root node.
|
||||||
*/
|
*/
|
||||||
async getTree(): Promise<OriginGoodsTreeResponse> {
|
async getTree(): Promise<OriginGoodsTreeResponse> {
|
||||||
const [allCategories, allOriginGoods, configCounts, goodsWithCountries, goodsWithTags] =
|
const [allCategories, allOriginGoods, configCounts, goodsWithCountries, goodsWithTags, mergedCounts, mergedWithCountries] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.prisma.category.findMany({
|
this.prisma.category.findMany({
|
||||||
where: { sdsCategoryId: { not: null } },
|
where: { sdsCategoryId: { not: null } },
|
||||||
@@ -144,7 +144,12 @@ export class OriginGoodsService {
|
|||||||
}),
|
}),
|
||||||
this.prisma.goodTag.findMany({
|
this.prisma.goodTag.findMany({
|
||||||
select: {
|
select: {
|
||||||
good: { select: { originGoodId: true } },
|
good: {
|
||||||
|
select: {
|
||||||
|
originGoodId: true,
|
||||||
|
mergedOriginGoods: { select: { originGoodId: true } },
|
||||||
|
},
|
||||||
|
},
|
||||||
tag: {
|
tag: {
|
||||||
select: {
|
select: {
|
||||||
tagName: true,
|
tagName: true,
|
||||||
@@ -157,26 +162,57 @@ export class OriginGoodsService {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
// Secondary-source references (good_origin_goods)
|
||||||
|
this.prisma.goodOriginGood.groupBy({
|
||||||
|
by: ['originGoodId'],
|
||||||
|
_count: { _all: true },
|
||||||
|
}),
|
||||||
|
this.prisma.goodOriginGood.findMany({
|
||||||
|
select: {
|
||||||
|
originGoodId: true,
|
||||||
|
good: { select: { country: { select: { countryName: true } } } },
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const countMap = new Map<string, number>();
|
const countMap = new Map<string, number>();
|
||||||
configCounts.forEach((c) =>
|
configCounts.forEach((c) =>
|
||||||
countMap.set(c.originGoodId.toString(), c._count._all),
|
countMap.set(c.originGoodId.toString(), c._count._all),
|
||||||
);
|
);
|
||||||
|
// Secondary (merged) references count towards configured status too.
|
||||||
const countryMap = new Map<string, string[]>();
|
mergedCounts.forEach((c) => {
|
||||||
goodsWithCountries.forEach((g) => {
|
const key = c.originGoodId.toString();
|
||||||
const key = g.originGoodId.toString();
|
countMap.set(key, (countMap.get(key) ?? 0) + c._count._all);
|
||||||
const name = g.country?.countryName;
|
|
||||||
if (!name) return;
|
|
||||||
const arr = countryMap.get(key);
|
|
||||||
if (arr) arr.push(name);
|
|
||||||
else countryMap.set(key, [name]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const countryMap = new Map<string, string[]>();
|
||||||
|
const addCountry = (key: string, name?: string | null) => {
|
||||||
|
if (!name) return;
|
||||||
|
const arr = countryMap.get(key);
|
||||||
|
if (!arr?.includes(name)) {
|
||||||
|
countryMap.set(key, [...(arr ?? []), name]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
goodsWithCountries.forEach((g) =>
|
||||||
|
addCountry(g.originGoodId.toString(), g.country?.countryName),
|
||||||
|
);
|
||||||
|
mergedWithCountries.forEach((m) =>
|
||||||
|
addCountry(m.originGoodId.toString(), m.good.country?.countryName),
|
||||||
|
);
|
||||||
|
|
||||||
const tagMap = new Map<string, { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]>();
|
const tagMap = new Map<string, { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]>();
|
||||||
|
const addTag = (
|
||||||
|
key: string,
|
||||||
|
tagInfo: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number },
|
||||||
|
) => {
|
||||||
|
const arr = tagMap.get(key);
|
||||||
|
if (arr) {
|
||||||
|
if (!arr.some((t) => t.tagName === tagInfo.tagName)) arr.push(tagInfo);
|
||||||
|
} else {
|
||||||
|
tagMap.set(key, [tagInfo]);
|
||||||
|
}
|
||||||
|
};
|
||||||
goodsWithTags.forEach((gt) => {
|
goodsWithTags.forEach((gt) => {
|
||||||
const key = gt.good.originGoodId.toString();
|
|
||||||
const tagInfo = {
|
const tagInfo = {
|
||||||
tagName: gt.tag.tagName,
|
tagName: gt.tag.tagName,
|
||||||
tagColor: gt.tag.tagColor,
|
tagColor: gt.tag.tagColor,
|
||||||
@@ -185,11 +221,10 @@ export class OriginGoodsService {
|
|||||||
tagGroupName: gt.tag.tagGroup?.groupName ?? null,
|
tagGroupName: gt.tag.tagGroup?.groupName ?? null,
|
||||||
sortOrder: gt.tag.sortOrder,
|
sortOrder: gt.tag.sortOrder,
|
||||||
};
|
};
|
||||||
const arr = tagMap.get(key);
|
addTag(gt.good.originGoodId.toString(), tagInfo);
|
||||||
if (arr) {
|
// A good's tags also mark its secondary origin goods as configured.
|
||||||
if (!arr.some((t) => t.tagName === tagInfo.tagName)) arr.push(tagInfo);
|
for (const m of gt.good.mergedOriginGoods) {
|
||||||
} else {
|
addTag(m.originGoodId.toString(), tagInfo);
|
||||||
tagMap.set(key, [tagInfo]);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user