Files
AIclinicalresearch/backend/scripts/compare-databases.cjs
HaHafeng 4b9b90ffb8 feat(iit): Complete REDCap production deployment on Alibaba Cloud ECS
Summary:

- Deploy REDCap 15.8.0 on ECS with Docker CE 26.1.3

- Configure RDS MySQL 8.0 database (redcap_prod)

- Setup Nginx reverse proxy with HTTPS/SSL

- Domain configured: https://redcap.xunzhengyixue.com/

Documentation:

- Add ECS deployment guide

- Add deployment info record

- Update system status document (v4.5 -> v4.6)

Status: REDCap production environment fully operational
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 22:27:05 +08:00

201 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
/**
* 对比本地数据库和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);