/** * 对比本地数据库和RDS数据库的差异 */ const { Client } = require('pg'); // 本地数据库配置 const localConfig = { host: 'localhost', port: 5432, database: 'ai_clinical_research', user: 'postgres', password: 'postgres123', }; // RDS数据库配置(测试环境) const rdsConfig = { host: 'pgm-2zex1m2y3r23hdn5xo.pg.rds.aliyuncs.com', // 外网地址 port: 5432, database: 'ai_clinical_research_test', user: 'airesearch', password: 'Xibahe@fengzhibo117', connectionTimeoutMillis: 30000, }; async function getSchemas(client) { const result = await client.query(` SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast', 'pg_temp_1', 'pg_toast_temp_1') ORDER BY schema_name `); return result.rows.map(r => r.schema_name); } async function getTables(client) { const result = await client.query(` SELECT table_schema, table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema') ORDER BY table_schema, table_name `); return result.rows.map(r => `${r.table_schema}.${r.table_name}`); } async function getExtensions(client) { const result = await client.query(` SELECT extname, extversion FROM pg_extension ORDER BY extname `); return result.rows; } async function main() { console.log('=' .repeat(60)); console.log('数据库对比工具'); console.log('=' .repeat(60)); // 连接本地数据库 console.log('\n[1] 连接本地数据库...'); const localClient = new Client(localConfig); try { await localClient.connect(); console.log('✅ 本地数据库连接成功'); } catch (err) { console.log('❌ 本地数据库连接失败:', err.message); return; } // 连接RDS数据库 console.log('\n[2] 连接RDS数据库...'); const rdsClient = new Client(rdsConfig); try { await rdsClient.connect(); console.log('✅ RDS数据库连接成功'); } catch (err) { console.log('❌ RDS数据库连接失败:', err.message); await localClient.end(); return; } try { // 对比 Extensions console.log('\n' + '=' .repeat(60)); console.log('📦 Extensions 对比'); console.log('=' .repeat(60)); const localExtensions = await getExtensions(localClient); const rdsExtensions = await getExtensions(rdsClient); console.log('\n本地 Extensions:'); localExtensions.forEach(e => console.log(` - ${e.extname} (${e.extversion})`)); console.log('\nRDS Extensions:'); rdsExtensions.forEach(e => console.log(` - ${e.extname} (${e.extversion})`)); // 对比 Schemas console.log('\n' + '=' .repeat(60)); console.log('📁 Schema 对比'); console.log('=' .repeat(60)); const localSchemas = await getSchemas(localClient); const rdsSchemas = await getSchemas(rdsClient); const schemasOnlyInLocal = localSchemas.filter(s => !rdsSchemas.includes(s)); const schemasOnlyInRds = rdsSchemas.filter(s => !localSchemas.includes(s)); const commonSchemas = localSchemas.filter(s => rdsSchemas.includes(s)); console.log(`\n共同 Schemas (${commonSchemas.length}个):`); commonSchemas.forEach(s => console.log(` ✅ ${s}`)); if (schemasOnlyInLocal.length > 0) { console.log(`\n⚠️ 仅在本地存在的 Schemas (${schemasOnlyInLocal.length}个):`); schemasOnlyInLocal.forEach(s => console.log(` 🔴 ${s}`)); } if (schemasOnlyInRds.length > 0) { console.log(`\n仅在RDS存在的 Schemas (${schemasOnlyInRds.length}个):`); schemasOnlyInRds.forEach(s => console.log(` 🟡 ${s}`)); } // 对比 Tables console.log('\n' + '=' .repeat(60)); console.log('📊 Table 对比'); console.log('=' .repeat(60)); const localTables = await getTables(localClient); const rdsTables = await getTables(rdsClient); const tablesOnlyInLocal = localTables.filter(t => !rdsTables.includes(t)); const tablesOnlyInRds = rdsTables.filter(t => !localTables.includes(t)); console.log(`\n本地表总数: ${localTables.length}`); console.log(`RDS表总数: ${rdsTables.length}`); if (tablesOnlyInLocal.length > 0) { console.log(`\n⚠️ 仅在本地存在的表 (${tablesOnlyInLocal.length}个) - 需要同步到RDS:`); tablesOnlyInLocal.forEach(t => console.log(` 🔴 ${t}`)); } else { console.log('\n✅ 没有仅在本地存在的表'); } if (tablesOnlyInRds.length > 0) { console.log(`\n仅在RDS存在的表 (${tablesOnlyInRds.length}个):`); tablesOnlyInRds.forEach(t => console.log(` 🟡 ${t}`)); } // 检查关键表的字段差异 console.log('\n' + '=' .repeat(60)); console.log('🔍 关键表字段检查'); console.log('=' .repeat(60)); // 检查 prompt_templates 表的 knowledge_config 字段 const checkColumn = async (client, schema, table, column) => { const result = await client.query(` SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND column_name = $3 `, [schema, table, column]); return result.rows.length > 0; }; const localHasKnowledgeConfig = await checkColumn(localClient, 'capability_schema', 'prompt_templates', 'knowledge_config'); const rdsHasKnowledgeConfig = await checkColumn(rdsClient, 'capability_schema', 'prompt_templates', 'knowledge_config'); console.log('\ncapability_schema.prompt_templates.knowledge_config:'); console.log(` 本地: ${localHasKnowledgeConfig ? '✅ 存在' : '❌ 不存在'}`); console.log(` RDS: ${rdsHasKnowledgeConfig ? '✅ 存在' : '❌ 不存在'}`); // 总结 console.log('\n' + '=' .repeat(60)); console.log('📋 同步建议'); console.log('=' .repeat(60)); const needSync = schemasOnlyInLocal.length > 0 || tablesOnlyInLocal.length > 0 || (localHasKnowledgeConfig && !rdsHasKnowledgeConfig); if (needSync) { console.log('\n⚠️ 需要同步以下内容到RDS:'); if (schemasOnlyInLocal.length > 0) { console.log(` - ${schemasOnlyInLocal.length} 个 Schema`); } if (tablesOnlyInLocal.length > 0) { console.log(` - ${tablesOnlyInLocal.length} 个表`); } if (localHasKnowledgeConfig && !rdsHasKnowledgeConfig) { console.log(' - prompt_templates.knowledge_config 字段'); } console.log('\n建议执行: npx prisma migrate deploy'); } else { console.log('\n✅ 数据库结构已同步,无需迁移'); } } finally { await localClient.end(); await rdsClient.end(); console.log('\n数据库连接已关闭'); } } main().catch(console.error);