feat: 新增标签分类体系与固定类型筛选优化

This commit is contained in:
2026-02-21 14:14:45 +08:00
parent 56eada659b
commit fb33b76ff5
15 changed files with 1601 additions and 128 deletions
+136
View File
@@ -0,0 +1,136 @@
import { PrismaClient } from '@prisma/client'
import {
FIXED_PROJECT_TYPE_TAGS,
inferProjectTypeSlug,
inferTagCategory,
} from '../src/lib/tag-taxonomy'
const prisma = new PrismaClient()
async function ensureFixedProjectTypeTags() {
const result = await Promise.all(
FIXED_PROJECT_TYPE_TAGS.map((item) =>
prisma.tag.upsert({
where: { slug: item.slug },
update: {
name: item.name,
nameEn: item.nameEn,
category: 'FIXED_PROJECT_TYPE',
},
create: {
name: item.name,
nameEn: item.nameEn,
slug: item.slug,
category: 'FIXED_PROJECT_TYPE',
},
})
)
)
return new Map(result.map((tag) => [tag.slug, tag.id]))
}
async function backfillTagCategories() {
const tags = await prisma.tag.findMany()
let updated = 0
for (const tag of tags) {
const inferredCategory = inferTagCategory({
slug: tag.slug,
name: tag.name,
nameEn: tag.nameEn,
})
if (tag.category !== inferredCategory) {
await prisma.tag.update({
where: { id: tag.id },
data: { category: inferredCategory },
})
updated += 1
}
}
return updated
}
async function backfillProjectFixedTypes(fixedTypeIdMap: Map<string, string>) {
const projects = await prisma.project.findMany({
where: { status: 'ACTIVE' },
include: {
tags: {
include: {
tag: true,
},
},
},
})
const createData: Array<{ projectId: string; tagId: string }> = []
for (const project of projects) {
const fixedTypeSlug = inferProjectTypeSlug({
name: project.name,
nameEn: project.nameEn,
description: project.description,
descriptionEn: project.descriptionEn,
tags: project.tags.map((tag) => ({
slug: tag.tag.slug,
name: tag.tag.name,
nameEn: tag.tag.nameEn,
})),
})
const fixedTypeTagId = fixedTypeIdMap.get(fixedTypeSlug)
if (!fixedTypeTagId) {
throw new Error(`Missing fixed project type tag: ${fixedTypeSlug}`)
}
createData.push({
projectId: project.id,
tagId: fixedTypeTagId,
})
}
const removed = await prisma.projectTag.deleteMany({
where: {
tag: {
category: 'FIXED_PROJECT_TYPE',
},
},
})
const inserted = await prisma.projectTag.createMany({
data: createData,
skipDuplicates: true,
})
return {
projectCount: projects.length,
inserted: inserted.count,
removed: removed.count,
}
}
async function main() {
console.log('[taxonomy] start')
const fixedTypeIdMap = await ensureFixedProjectTypeTags()
const updatedTagCategoryCount = await backfillTagCategories()
const projectResult = await backfillProjectFixedTypes(fixedTypeIdMap)
console.log('[taxonomy] done', {
updatedTagCategoryCount,
projectCount: projectResult.projectCount,
insertedProjectTypeLinks: projectResult.inserted,
removedOldProjectTypeLinks: projectResult.removed,
})
}
main()
.catch((error) => {
console.error('[taxonomy] failed', error)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})