/** * 验证PKB和RVW的数据库Schema状态 * * 目的: * 1. 检查pkb_schema是否存在及其表结构 * 2. 检查rvw_schema是否存在 * 3. 检查ReviewTask在哪个Schema中 * 4. 检查是否有旧数据需要迁移 * * 运行方式: * npx tsx scripts/verify-pkb-rvw-schema.ts */ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); interface SchemaInfo { schema_name: string; } interface TableInfo { table_schema: string; table_name: string; } interface ColumnInfo { column_name: string; data_type: string; is_nullable: string; column_default: string | null; } interface RowCount { count: bigint; } async function verifySchemas() { console.log('🔍 开始验证PKB和RVW的数据库Schema状态...\n'); console.log('='.repeat(80)); try { // ======================================== // 1. 检查所有Schema // ======================================== console.log('\n📦 1. 检查数据库中所有的Schema'); console.log('='.repeat(80)); const schemas = await prisma.$queryRaw` SELECT schema_name FROM information_schema.schemata WHERE schema_name IN ('pkb_schema', 'rvw_schema', 'public', 'platform_schema') ORDER BY schema_name `; console.log('✅ 找到以下Schema:'); schemas.forEach(s => console.log(` - ${s.schema_name}`)); const hasRvwSchema = schemas.some(s => s.schema_name === 'rvw_schema'); const hasPkbSchema = schemas.some(s => s.schema_name === 'pkb_schema'); console.log(`\n📊 Schema存在性检查:`); console.log(` pkb_schema: ${hasPkbSchema ? '✅ 存在' : '❌ 不存在'}`); console.log(` rvw_schema: ${hasRvwSchema ? '✅ 存在' : '❌ 不存在'}`); // ======================================== // 2. 检查PKB相关表 // ======================================== console.log('\n'); console.log('='.repeat(80)); console.log('📚 2. 检查PKB相关表(在pkb_schema中)'); console.log('='.repeat(80)); if (hasPkbSchema) { const pkbTables = await prisma.$queryRaw` SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'pkb_schema' ORDER BY table_name `; console.log(`✅ pkb_schema中共有 ${pkbTables.length} 个表:`); pkbTables.forEach(t => console.log(` - ${t.table_name}`)); // 检查每个表的行数 console.log('\n📊 PKB表数据统计:'); for (const table of pkbTables) { try { const result = await prisma.$queryRaw` SELECT COUNT(*) as count FROM pkb_schema.${prisma.$queryRawUnsafe(table.table_name)} `; const count = Number(result[0]?.count || 0); console.log(` - ${table.table_name}: ${count} 行`); } catch (error) { console.log(` - ${table.table_name}: 查询失败`); } } } else { console.log('❌ pkb_schema不存在!需要创建!'); } // ======================================== // 3. 检查RVW相关表 // ======================================== console.log('\n'); console.log('='.repeat(80)); console.log('📝 3. 检查RVW相关表(review_tasks)'); console.log('='.repeat(80)); // 查找review_tasks表在哪个schema const reviewTaskLocation = await prisma.$queryRaw` SELECT table_schema, table_name FROM information_schema.tables WHERE table_name = 'review_tasks' `; if (reviewTaskLocation.length > 0) { console.log('✅ 找到review_tasks表:'); reviewTaskLocation.forEach(t => { console.log(` - 位置: ${t.table_schema}.${t.table_name}`); }); // 查看review_tasks表结构 const reviewSchema = reviewTaskLocation[0].table_schema; console.log(`\n📋 review_tasks表结构(在${reviewSchema}中):`); const reviewColumns = await prisma.$queryRaw` SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = ${reviewSchema} AND table_name = 'review_tasks' ORDER BY ordinal_position `; console.log('='.repeat(80)); console.log( 'Column Name'.padEnd(25) + 'Data Type'.padEnd(20) + 'Nullable'.padEnd(12) + 'Default' ); console.log('='.repeat(80)); reviewColumns.forEach(col => { const colName = col.column_name.padEnd(25); const dataType = col.data_type.padEnd(20); const nullable = (col.is_nullable === 'YES' ? 'YES' : 'NO').padEnd(12); const defaultVal = col.column_default || ''; console.log(`${colName}${dataType}${nullable}${defaultVal}`); }); console.log('='.repeat(80)); console.log(`总计: ${reviewColumns.length} 个字段`); // 查询数据量 const reviewCount = await prisma.$queryRaw` SELECT COUNT(*) as count FROM ${prisma.$queryRawUnsafe(reviewSchema)}.review_tasks `; const count = Number(reviewCount[0]?.count || 0); console.log(`\n📊 review_tasks表数据量: ${count} 行`); if (reviewSchema === 'public') { console.log('\n⚠️ WARNING: review_tasks当前在public schema中!'); console.log(' 建议迁移到rvw_schema以保持架构一致性!'); } } else { console.log('❌ 未找到review_tasks表!'); } // ======================================== // 4. 检查rvw_schema状态 // ======================================== console.log('\n'); console.log('='.repeat(80)); console.log('🔍 4. 检查rvw_schema状态'); console.log('='.repeat(80)); if (hasRvwSchema) { const rvwTables = await prisma.$queryRaw` SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'rvw_schema' ORDER BY table_name `; if (rvwTables.length > 0) { console.log(`✅ rvw_schema中共有 ${rvwTables.length} 个表:`); rvwTables.forEach(t => console.log(` - ${t.table_name}`)); } else { console.log('⚠️ rvw_schema存在但为空(未创建任何表)'); } } else { console.log('❌ rvw_schema不存在!需要创建!'); } // ======================================== // 5. 检查User表状态 // ======================================== console.log('\n'); console.log('='.repeat(80)); console.log('👤 5. 检查User表状态'); console.log('='.repeat(80)); const userTables = await prisma.$queryRaw` SELECT table_schema, table_name FROM information_schema.tables WHERE table_name = 'users' AND table_schema IN ('public', 'platform_schema') `; console.log('✅ 找到以下users表:'); for (const userTable of userTables) { console.log(` - ${userTable.table_schema}.${userTable.table_name}`); const userCount = await prisma.$queryRaw` SELECT COUNT(*) as count FROM ${prisma.$queryRawUnsafe(userTable.table_schema)}.users `; const count = Number(userCount[0]?.count || 0); console.log(` 数据量: ${count} 行`); } // ======================================== // 6. 总结和建议 // ======================================== console.log('\n'); console.log('='.repeat(80)); console.log('📋 6. 迁移建议总结'); console.log('='.repeat(80)); console.log('\n✅ PKB模块迁移建议:'); if (hasPkbSchema && pkbTables.length > 0) { console.log(' 1. ✅ pkb_schema已存在且有表'); console.log(' 2. ✅ 可以直接迁移代码到新架构'); console.log(' 3. ⚠️ 注意检查旧代码是否连接到正确的schema'); } else { console.log(' 1. ❌ 需要先创建pkb_schema和相关表'); console.log(' 2. ❌ 需要运行Prisma迁移'); } console.log('\n⚠️ RVW模块迁移建议:'); if (reviewTaskLocation.length > 0) { const currentSchema = reviewTaskLocation[0].table_schema; if (currentSchema === 'public') { console.log(' 1. ⚠️ review_tasks当前在public schema'); console.log(' 2. 🔄 建议:先完整迁移功能,后续再迁移Schema'); console.log(' 3. 📋 迁移步骤:'); console.log(' a. 创建rvw_schema(如果不存在)'); console.log(' b. 在rvw_schema中创建新表'); console.log(' c. 迁移数据'); console.log(' d. 更新代码'); console.log(' e. 删除旧表'); } else if (currentSchema === 'rvw_schema') { console.log(' 1. ✅ review_tasks已在rvw_schema中'); console.log(' 2. ✅ 可以直接迁移代码'); } } else { console.log(' 1. ❌ review_tasks表不存在'); console.log(' 2. ❌ 需要从头创建'); } console.log('\n💡 推荐迁移策略:'); console.log(' 【方案A:渐进式迁移(推荐)】'); console.log(' 1. PKB: 直接迁移代码到新架构(Schema已就绪)'); console.log(' 2. RVW: 先迁移代码保持在public schema'); console.log(' 3. 验证功能完整性'); console.log(' 4. 后续独立任务:将RVW迁移到rvw_schema'); console.log(''); console.log(' 【方案B:一步到位(风险较高)】'); console.log(' 1. 同时迁移代码+Schema'); console.log(' 2. 需要数据迁移脚本'); console.log(' 3. 需要更长的测试时间'); console.log('\n='.repeat(80)); console.log('✅ 验证完成!'); console.log('='.repeat(80)); } catch (error) { console.error('❌ 验证过程中出错:', error); throw error; } finally { await prisma.$disconnect(); } } // 运行验证 verifySchemas() .catch((error) => { console.error('Fatal error:', error); process.exit(1); });