flnxi-Wu/scripts/verify-template-data.ts
WuYongJie 63ec6b7dfb fix: 修复排行榜按钮消失、tag排序和输入框浅色模式问题
1. 排行榜悬浮窗关闭后小按钮消失问题
   - 修复面板居中模式关闭时按钮位置错误
   - 保存并恢复按钮的居中状态

2. 首页tag按钮按素材数量排序
   - 添加按素材数量降序排序逻辑

3. 上传弹窗输入框浅色模式文字不可见
   - 添加 text-slate-900 确保浅色模式下文字可见
   - 添加 placeholder 样式确保占位符正确显示
2025-12-14 21:41:29 +08:00

271 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 数据验证脚本:验证模板系统数据完整性
*
* 功能:
* 1. 验证所有模板的必填字段
* 2. 验证素材的templateId引用
* 3. 验证字段的templateId引用
* 4. 检查孤立数据
*/
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
interface ValidationResult {
category: string
passed: boolean
message: string
details?: any
}
async function verifyTemplateData(): Promise<boolean> {
console.log('开始数据验证...\n')
const results: ValidationResult[] = []
let allPassed = true
try {
// 1. 验证模板数据
console.log('验证 1: 检查模板数据...')
// 1.1 检查必填字段
const templatesWithoutName = await prisma.uploadTemplate.count({
where: { name: { equals: '' } }
})
results.push({
category: '模板',
passed: templatesWithoutName === 0,
message: `模板名称完整性`,
details: templatesWithoutName === 0 ? '✓ 所有模板都有名称' : `✗ ${templatesWithoutName} 个模板没有名称`
})
// 1.2 检查version字段
const templatesWithoutVersion = await prisma.uploadTemplate.count({
where: {
OR: [
{ version: { equals: 0 } },
{ version: { lt: 1 } }
]
}
})
results.push({
category: '模板',
passed: templatesWithoutVersion === 0,
message: `模板version字段`,
details: templatesWithoutVersion === 0 ? '✓ 所有模板都有version' : `✗ ${templatesWithoutVersion} 个模板没有version`
})
// 1.3 检查materialKind字段
const validKinds = ['IMAGE', 'VIDEO', 'OTHER']
const templatesWithInvalidKind = await prisma.uploadTemplate.count({
where: {
materialKind: {
notIn: validKinds
}
}
})
results.push({
category: '模板',
passed: templatesWithInvalidKind === 0,
message: `模板materialKind有效性`,
details: templatesWithInvalidKind === 0 ? '✓ 所有模板的materialKind都有效' : `✗ ${templatesWithInvalidKind} 个模板的materialKind无效`
})
// 2. 验证素材数据
console.log('验证 2: 检查素材数据...')
// 2.1 检查未删除素材的templateId
const materialsWithoutTemplate = await prisma.material.count({
where: {
templateId: null,
deletedAt: null
}
})
results.push({
category: '素材',
passed: materialsWithoutTemplate === 0,
message: `素材templateId完整性`,
details: materialsWithoutTemplate === 0 ? '✓ 所有素材都有templateId' : `✗ ${materialsWithoutTemplate} 个素材没有templateId`
})
// 2.2 检查templateId引用的有效性
const materialsWithInvalidTemplate = await prisma.material.findMany({
where: {
NOT: { templateId: null },
deletedAt: null,
template: null
},
select: { id: true, templateId: true }
})
results.push({
category: '素材',
passed: materialsWithInvalidTemplate.length === 0,
message: `素材templateId引用有效性`,
details: materialsWithInvalidTemplate.length === 0
? '✓ 所有素材的templateId引用都有效'
: `✗ ${materialsWithInvalidTemplate.length} 个素材的templateId引用无效`
})
// 3. 验证字段数据
console.log('验证 3: 检查字段数据...')
// 3.1 检查字段的templateId
const fieldsWithoutTemplate = await prisma.uploadField.count({
where: {
OR: [
{ templateId: { equals: '' } },
{ templateId: { equals: null as any } }
]
}
})
results.push({
category: '字段',
passed: fieldsWithoutTemplate === 0,
message: `字段templateId完整性`,
details: fieldsWithoutTemplate === 0 ? '✓ 所有字段都有templateId' : `✗ ${fieldsWithoutTemplate} 个字段没有templateId`
})
// 3.2 检查字段key的唯一性(同一模板内)
const duplicateFieldKeys = await prisma.$queryRaw<Array<{ templateId: string, key: string, count: number }>>`
SELECT "templateId", "key", COUNT(*) as count
FROM "UploadField"
GROUP BY "templateId", "key"
HAVING COUNT(*) > 1
`
results.push({
category: '字段',
passed: duplicateFieldKeys.length === 0,
message: `字段key唯一性`,
details: duplicateFieldKeys.length === 0
? '✓ 所有字段key在模板内唯一'
: `✗ ${duplicateFieldKeys.length} 个重复的字段key`
})
// 4. 验证字段池数据
console.log('验证 4: 检查字段池数据...')
// 4.1 检查字段池key的唯一性
const duplicateFieldDefKeys = await prisma.$queryRaw<Array<{ key: string, count: number }>>`
SELECT "key", COUNT(*) as count
FROM "FieldDefinition"
GROUP BY "key"
HAVING COUNT(*) > 1
`
results.push({
category: '字段池',
passed: duplicateFieldDefKeys.length === 0,
message: `字段池key唯一性`,
details: duplicateFieldDefKeys.length === 0
? '✓ 所有字段池key唯一'
: `✗ ${duplicateFieldDefKeys.length} 个重复的字段池key`
})
// 5. 检查孤立数据
console.log('验证 5: 检查孤立数据...')
// 5.1 检查没有字段的模板
const templatesWithoutFields = await prisma.uploadTemplate.findMany({
where: {
fields: { none: {} }
},
select: { id: true, status: true }
})
results.push({
category: '孤立数据',
passed: true, // 这不是错误,只是警告
message: `没有字段的模板`,
details: templatesWithoutFields.length === 0
? '✓ 所有模板都有字段'
: `⚠ ${templatesWithoutFields.length} 个模板没有字段(可能是草稿)`
})
// 5.2 检查没有素材的模板
const templatesWithoutMaterials = await prisma.uploadTemplate.findMany({
where: {
materials: { none: {} }
},
select: { id: true, status: true }
})
results.push({
category: '孤立数据',
passed: true, // 这不是错误,只是警告
message: `没有素材的模板`,
details: templatesWithoutMaterials.length === 0
? '✓ 所有模板都有素材'
: `⚠ ${templatesWithoutMaterials.length} 个模板没有素材(可能是新建的)`
})
// 6. 生成验证报告
console.log('\n' + '='.repeat(80))
console.log('验证报告')
console.log('='.repeat(80))
const groupedResults = results.reduce((acc, result) => {
if (!acc[result.category]) {
acc[result.category] = []
}
acc[result.category].push(result)
return acc
}, {} as Record<string, ValidationResult[]>)
for (const [category, categoryResults] of Object.entries(groupedResults)) {
console.log(`\n${category}:`)
for (const result of categoryResults) {
const icon = result.passed ? '✓' : '✗'
console.log(` ${icon} ${result.message}`)
console.log(` ${result.details}`)
if (!result.passed) {
allPassed = false
}
}
}
// 7. 统计信息
console.log('\n' + '='.repeat(80))
console.log('统计信息')
console.log('='.repeat(80))
const totalTemplates = await prisma.uploadTemplate.count()
const activeTemplates = await prisma.uploadTemplate.count({ where: { status: 'active' } })
const totalFields = await prisma.uploadField.count()
const totalMaterials = await prisma.material.count({ where: { deletedAt: null } })
const totalFieldDefs = await prisma.fieldDefinition.count()
const enabledFieldDefs = await prisma.fieldDefinition.count({ where: { isFilterEnabled: true } })
console.log(`模板总数: ${totalTemplates} (活跃: ${activeTemplates})`)
console.log(`字段总数: ${totalFields}`)
console.log(`素材总数: ${totalMaterials}`)
console.log(`字段池定义: ${totalFieldDefs} (启用筛选: ${enabledFieldDefs})`)
console.log('='.repeat(80))
if (allPassed) {
console.log('\n✓ 所有验证通过!')
} else {
console.warn('\n⚠ 部分验证失败,请检查上述错误')
}
return allPassed
} catch (error) {
console.error('\n✗ 验证过程出错:', error)
throw error
} finally {
await prisma.$disconnect()
}
}
// 执行验证
if (require.main === module) {
verifyTemplateData()
.then((success) => {
process.exit(success ? 0 : 1)
})
.catch((error) => {
console.error('验证脚本执行失败:', error)
process.exit(1)
})
}
export { verifyTemplateData }