haixunMaster/lib/services/product-profile.ts

110 lines
2.9 KiB
TypeScript

import { prisma } from "@/lib/db";
import {
attachBrandToTopic,
buildMergedProductContext,
migrateLegacyProductCatalog,
syncTopicProductSnapshot,
} from "@/lib/services/product-catalog";
import { hasProductContext, parseProductContext } from "@/lib/types/product-context";
/** 將主題內嵌的 productContext 補寫進 ProductProfile 表(舊資料相容)。 */
export async function migrateOrphanProductContexts(accountIds: string[]) {
if (accountIds.length === 0) return;
await migrateLegacyProductCatalog(accountIds);
const orphans = await prisma.topic.findMany({
where: {
accountId: { in: accountIds },
topicGoal: "placement",
productProfileId: null,
productContext: { not: null },
},
});
for (const topic of orphans) {
const context = topic.productContext?.trim();
if (!context || !hasProductContext(context)) continue;
const fields = parseProductContext(context);
const label =
[fields.brand, fields.product].filter(Boolean).join(" · ") || topic.label;
const existing = topic.accountId
? await prisma.productProfile.findFirst({
where: {
accountId: topic.accountId,
context,
},
})
: null;
const profile =
existing ??
(await prisma.productProfile.create({
data: {
accountId: topic.accountId ?? undefined,
label,
context,
},
}));
await prisma.topic.update({
where: { id: topic.id },
data: { productProfileId: profile.id },
});
}
}
export async function syncProfileContextToTopics(profileId: string, context: string) {
await prisma.topic.updateMany({
where: { productProfileId: profileId },
data: { productContext: context.trim() || null },
});
}
export async function attachProfileToTopic(topicId: string, profileId: string | null) {
if (!profileId) {
const topic = await prisma.topic.update({
where: { id: topicId },
data: { productProfileId: null },
});
await syncTopicProductSnapshot(topicId);
return topic;
}
const profile = await prisma.productProfile.findUnique({
where: { id: profileId },
include: { brand: true },
});
if (!profile) throw new Error("找不到產品");
if (profile.brandId) {
return attachBrandToTopic(topicId, profile.brandId, profileId);
}
const context = buildMergedProductContext(
profile.brand?.name ?? parseProductContext(profile.context).brand,
profile.context,
profile.label
);
return prisma.topic.update({
where: { id: topicId },
data: {
productProfileId: profileId,
productContext: context,
},
});
}
export async function attachBrandSelectionToTopic(
topicId: string,
brandId: string | null,
productId: string | null
) {
return attachBrandToTopic(topicId, brandId, productId);
}
export function validateProfileContext(context: string | null | undefined): boolean {
return hasProductContext(context);
}