diff --git a/src/app/api/webhook/projects/route.ts b/src/app/api/webhook/projects/route.ts index 56d99a4..ddb1844 100644 --- a/src/app/api/webhook/projects/route.ts +++ b/src/app/api/webhook/projects/route.ts @@ -7,6 +7,80 @@ import { type ProjectInput, } from '@/lib/validations' +/** + * 多级去重策略:查找已存在的项目 + * + * 优先级: + * 1. GitHub URL 完全匹配(最准确) + * 2. Website URL 完全匹配 + * 3. slug 匹配(兜底) + * + * @param projectData - 项目数据 + * @returns 已存在的项目,如果不存在则返回 null + */ +async function findExistingProject(projectData: ProjectInput) { + // 优先级1: 通过 GitHub URL 匹配 + const githubLink = projectData.links.find((link) => link.type === 'GITHUB') + if (githubLink) { + const existingByGithub = await prisma.externalLink.findFirst({ + where: { + type: 'GITHUB', + url: githubLink.url, + }, + include: { + project: true, + }, + }) + + if (existingByGithub) { + console.log( + `[Webhook] Found existing project by GitHub URL: ${githubLink.url}` + ) + return existingByGithub.project + } + } + + // 优先级2: 通过 Website URL 匹配 + const websiteLink = projectData.links.find( + (link) => link.type === 'WEBSITE' + ) + if (websiteLink) { + const existingByWebsite = await prisma.externalLink.findFirst({ + where: { + type: 'WEBSITE', + url: websiteLink.url, + }, + include: { + project: true, + }, + }) + + if (existingByWebsite) { + console.log( + `[Webhook] Found existing project by Website URL: ${websiteLink.url}` + ) + return existingByWebsite.project + } + } + + // 优先级3: 通过 slug 匹配(兜底) + const slug = + projectData.nameEn?.toLowerCase().replace(/\s+/g, '-') || + projectData.name.toLowerCase().replace(/\s+/g, '-') + + const existingBySlug = await prisma.project.findUnique({ + where: { slug }, + }) + + if (existingBySlug) { + console.log(`[Webhook] Found existing project by slug: ${slug}`) + return existingBySlug + } + + console.log(`[Webhook] No existing project found, will create new one`) + return null +} + export async function POST(request: NextRequest) { const startTime = Date.now() @@ -77,6 +151,9 @@ export async function POST(request: NextRequest) { try { const validProject = projectValidation.data as ProjectInput + // 多级去重:查找已存在的项目 + const existingProject = await findExistingProject(validProject) + // Upsert tags const tagConnections = await Promise.all( validProject.tags.map(async (tag) => { @@ -101,13 +178,21 @@ export async function POST(request: NextRequest) { validProject.nameEn?.toLowerCase().replace(/\s+/g, '-') || validProject.name.toLowerCase().replace(/\s+/g, '-') - // Upsert project - const existingProject = await prisma.project.findUnique({ - where: { slug }, - }) - if (existingProject) { // Update existing project + const matchMethod = existingProject.slug === slug + ? 'slug' + : validProject.links.some( + (l) => + existingProject.links.some((el) => el.url === l.url) + ) + ? 'url' + : 'unknown' + + console.log( + `[Webhook] Updating project "${validProject.name}" (matched by ${matchMethod}, id: ${existingProject.id})` + ) + await prisma.project.update({ where: { id: existingProject.id }, data: { @@ -142,6 +227,10 @@ export async function POST(request: NextRequest) { results.updated++ } else { // Create new project + console.log( + `[Webhook] Creating new project "${validProject.name}" (slug: ${slug})` + ) + await prisma.project.create({ data: { name: validProject.name,