feat(product-family): auto-derive logistics/craft tags from family members
This commit is contained in:
@@ -33,7 +33,7 @@ export interface GoodRelations {
|
||||
}>;
|
||||
_count?: { variants: number };
|
||||
} | null;
|
||||
goodTags?: { tag: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } }[];
|
||||
goodTags?: { tag: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: bigint | null } }[];
|
||||
mergedOriginGoods?: Array<{
|
||||
originGood: {
|
||||
id: bigint;
|
||||
@@ -175,6 +175,7 @@ export class GoodDto {
|
||||
tagName: gt.tag.tagName,
|
||||
tagColor: gt.tag.tagColor,
|
||||
tagFontColor: gt.tag.tagFontColor,
|
||||
tagGroupId: gt.tag.tagGroupId?.toString() ?? null,
|
||||
}))
|
||||
: [],
|
||||
mergedOriginGoods: (rel.mergedOriginGoods ?? []).map((m) => ({
|
||||
|
||||
@@ -128,6 +128,7 @@ export class GoodsService {
|
||||
where: { id: BigInt(dto.originGoodId) },
|
||||
select: { familyId: true },
|
||||
});
|
||||
const tagIds = await this.stripAutoGroupTags(dto.tagIds ?? [], primary?.familyId ?? null);
|
||||
const created = await tx.good.create({
|
||||
data: {
|
||||
goodName: dto.goodName,
|
||||
@@ -140,9 +141,9 @@ export class GoodsService {
|
||||
goodPriority: dto.goodPriority ?? 0,
|
||||
},
|
||||
});
|
||||
if (dto.tagIds && dto.tagIds.length > 0) {
|
||||
if (tagIds.length > 0) {
|
||||
await tx.goodTag.createMany({
|
||||
data: dto.tagIds.map((tagId) => ({
|
||||
data: tagIds.map((tagId) => ({
|
||||
goodId: created.id,
|
||||
tagId: BigInt(tagId),
|
||||
})),
|
||||
@@ -170,6 +171,10 @@ export class GoodsService {
|
||||
mergedOriginGoods: result.mergedOriginGoods,
|
||||
});
|
||||
});
|
||||
if (result.originGood?.family?.familyId) {
|
||||
// 建商品后立即同步族派生标签(物流/工艺/位置组)
|
||||
await this.familyRecompute.syncFamilyTags(BigInt(result.originGood.family.familyId));
|
||||
}
|
||||
if (
|
||||
result.originGood?.source === 'SDS' &&
|
||||
result.originGood.sdsGoodId &&
|
||||
@@ -353,12 +358,30 @@ export class GoodsService {
|
||||
}
|
||||
}
|
||||
|
||||
// 有族商品的自动组(物流/工艺/位置)标签为族派生数据,不接受手动写入
|
||||
let familyIdForTags: bigint | null;
|
||||
if (dto.familyId !== undefined) {
|
||||
familyIdForTags = dto.familyId === null ? null : BigInt(dto.familyId);
|
||||
} else {
|
||||
familyIdForTags =
|
||||
(
|
||||
await this.prisma.good.findUnique({
|
||||
where: { id },
|
||||
select: { familyId: true },
|
||||
})
|
||||
)?.familyId ?? null;
|
||||
}
|
||||
const updateTagIds =
|
||||
dto.tagIds !== undefined
|
||||
? await this.stripAutoGroupTags(dto.tagIds, familyIdForTags)
|
||||
: undefined;
|
||||
|
||||
const result = await this.prisma.$transaction(async (tx) => {
|
||||
if (dto.tagIds !== undefined) {
|
||||
if (updateTagIds !== undefined) {
|
||||
await tx.goodTag.deleteMany({ where: { goodId: id } });
|
||||
if (dto.tagIds.length > 0) {
|
||||
if (updateTagIds.length > 0) {
|
||||
await tx.goodTag.createMany({
|
||||
data: dto.tagIds.map((tagId) => ({
|
||||
data: updateTagIds.map((tagId) => ({
|
||||
goodId: id,
|
||||
tagId: BigInt(tagId),
|
||||
})),
|
||||
@@ -391,6 +414,10 @@ export class GoodsService {
|
||||
mergedOriginGoods: updated.mergedOriginGoods,
|
||||
});
|
||||
});
|
||||
if (familyIdForTags) {
|
||||
// 更新后重同步族派生标签(人工编辑不会破坏派生集合)
|
||||
await this.familyRecompute.syncFamilyTags(familyIdForTags);
|
||||
}
|
||||
if (
|
||||
result.originGood?.source === 'SDS' &&
|
||||
result.originGood.sdsGoodId &&
|
||||
@@ -592,8 +619,37 @@ export class GoodsService {
|
||||
if (!c) throw new BadRequestException(`Category ${id} not found`);
|
||||
}
|
||||
|
||||
private async ensureTag(id: number) {
|
||||
const t = await this.prisma.tag.findUnique({ where: { id: BigInt(id) } });
|
||||
/** 有族商品:剔除自动组(物流/工艺/位置)标签——它们由族同步管理 */
|
||||
private async stripAutoGroupTags(
|
||||
tagIds: number[],
|
||||
familyId: bigint | null,
|
||||
): Promise<number[]> {
|
||||
if (!familyId || !tagIds.length) return tagIds;
|
||||
const autoGroups = await this.prisma.tagGroup.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ groupName: { contains: '物流' } },
|
||||
{ groupName: { contains: '工艺' } },
|
||||
{ groupName: { contains: '位置' } },
|
||||
],
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (!autoGroups.length) return tagIds;
|
||||
const autoIds = new Set(autoGroups.map((g) => g.id.toString()));
|
||||
const tags = await this.prisma.tag.findMany({
|
||||
where: { id: { in: tagIds.map((id) => BigInt(id)) } },
|
||||
select: { id: true, tagGroupId: true },
|
||||
});
|
||||
const blocked = new Set(
|
||||
tags
|
||||
.filter((t) => t.tagGroupId && autoIds.has(t.tagGroupId.toString()))
|
||||
.map((t) => t.id.toString()),
|
||||
);
|
||||
return blocked.size ? tagIds.filter((id) => !blocked.has(String(id))) : tagIds;
|
||||
}
|
||||
|
||||
private async ensureTag(id: number) { const t = await this.prisma.tag.findUnique({ where: { id: BigInt(id) } });
|
||||
if (!t) throw new BadRequestException(`Tag ${id} not found`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user