feat(api): category-based family grouping, tree family info and custom goods family attribution

This commit is contained in:
yeuimu
2026-08-28 12:43:58 +08:00
parent d63f1a6f35
commit c8531bfe08
10 changed files with 190 additions and 36 deletions
+26 -11
View File
@@ -673,21 +673,36 @@ export class SyncService {
}
/**
* 新链接自动挂族:3 段分组键恰好命中唯一族才挂载(多族/零族留给管理员裁决)。
* 新链接自动挂族:优先按 SDS 分类(=产品模型)匹配已有族的成员;
* 无分类时回退名称 3 段键。恰好命中唯一族才挂载(多族/零族留给管理员裁决)。
* 锁定族(autoManaged=false)不吸收新成员,只置 stale 提示。
*/
private async tryAutoAttachToFamily(originGoodId: bigint, goodName: string): Promise<void> {
const key = originGroupKey(goodName);
if (!key) return;
const candidates = await this.prisma.originGood.findMany({
where: { familyId: { not: null }, goodName: { startsWith: key } },
select: { familyId: true, goodName: true },
const self = await this.prisma.originGood.findUnique({
where: { id: originGoodId },
select: { sdsCategoryId: true },
});
const familyIds = new Set(
candidates
.filter((c) => originGroupKey(c.goodName) === key && c.familyId !== null)
.map((c) => c.familyId!.toString()),
);
let familyIds: Set<string>;
if (self?.sdsCategoryId) {
const siblings = await this.prisma.originGood.findMany({
where: { sdsCategoryId: self.sdsCategoryId, familyId: { not: null } },
select: { familyId: true },
distinct: ['familyId'],
});
familyIds = new Set(siblings.map((s) => s.familyId!.toString()));
} else {
const key = originGroupKey(goodName);
if (!key) return;
const candidates = await this.prisma.originGood.findMany({
where: { familyId: { not: null }, goodName: { startsWith: key } },
select: { familyId: true, goodName: true },
});
familyIds = new Set(
candidates
.filter((c) => originGroupKey(c.goodName) === key && c.familyId !== null)
.map((c) => c.familyId!.toString()),
);
}
if (familyIds.size !== 1) return;
const familyId = BigInt([...familyIds][0]);
const family = await this.prisma.productFamily.findUnique({