#!/usr/bin/env node /** * 数据库结构检查脚本 * 检查数据库表结构和数据完整性 */ const mysql = require('mysql2/promise'); const config = require('../config/env'); async function checkDatabaseStructure() { let connection; try { console.log('🔍 开始检查数据库结构...'); // 创建数据库连接 const dbConfig = { host: config.mysql.host, port: config.mysql.port, user: config.mysql.user, password: config.mysql.password, database: config.mysql.database, charset: config.mysql.charset, timezone: config.mysql.timezone }; connection = await mysql.createConnection(dbConfig); console.log('✅ 数据库连接成功'); console.log(`📊 数据库: ${dbConfig.database}`); console.log('='.repeat(60)); // 测试基本查询 const [testRows] = await connection.execute('SELECT 1 + 1 AS result'); console.log(`✅ 基本查询测试: ${testRows[0].result}`); // 检查数据库版本和字符集 const [versionRows] = await connection.execute('SELECT VERSION() as version'); console.log(`📊 MySQL版本: ${versionRows[0].version}`); // 获取所有表 console.log('\n📋 检查数据库表结构:'); const [tables] = await connection.execute(` SELECT TABLE_NAME, TABLE_ROWS, DATA_LENGTH, INDEX_LENGTH, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME `, [dbConfig.database]); if (tables.length === 0) { console.log('⚠️ 数据库中没有找到任何表'); console.log('💡 建议运行数据库结构创建脚本'); return { success: false, message: '数据库为空' }; } console.log(`📊 找到 ${tables.length} 个表:`); let totalRows = 0; for (const table of tables) { const rowCount = table.TABLE_ROWS || 0; totalRows += rowCount; const dataSize = (table.DATA_LENGTH / 1024).toFixed(2); const indexSize = (table.INDEX_LENGTH / 1024).toFixed(2); console.log(` 📄 ${table.TABLE_NAME.padEnd(25)} | ${String(rowCount).padStart(6)} 行 | ${dataSize.padStart(8)} KB | ${table.TABLE_COMMENT || '无注释'}`); } console.log(`\n📊 总记录数: ${totalRows}`); // 检查核心表的详细结构 console.log('\n🔍 检查核心表结构:'); const coreTables = ['admins', 'users', 'merchants', 'animals', 'orders', 'payments']; for (const tableName of coreTables) { const tableExists = tables.find(t => t.TABLE_NAME === tableName); if (tableExists) { console.log(`\n📋 表: ${tableName}`); // 获取表结构 const [columns] = await connection.execute(` SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION `, [dbConfig.database, tableName]); console.log(' 字段结构:'); for (const col of columns.slice(0, 10)) { // 只显示前10个字段 const nullable = col.IS_NULLABLE === 'YES' ? '可空' : '非空'; const defaultVal = col.COLUMN_DEFAULT ? `默认:${col.COLUMN_DEFAULT}` : ''; console.log(` ${col.COLUMN_NAME.padEnd(20)} | ${col.DATA_TYPE.padEnd(15)} | ${nullable.padEnd(4)} | ${col.COLUMN_COMMENT || '无注释'}`); } if (columns.length > 10) { console.log(` ... 还有 ${columns.length - 10} 个字段`); } // 检查索引 const [indexes] = await connection.execute(` SELECT INDEX_NAME, COLUMN_NAME, NON_UNIQUE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY INDEX_NAME, SEQ_IN_INDEX `, [dbConfig.database, tableName]); if (indexes.length > 0) { console.log(' 索引:'); const indexGroups = {}; indexes.forEach(idx => { if (!indexGroups[idx.INDEX_NAME]) { indexGroups[idx.INDEX_NAME] = []; } indexGroups[idx.INDEX_NAME].push(idx.COLUMN_NAME); }); Object.entries(indexGroups).forEach(([indexName, columns]) => { const type = indexName === 'PRIMARY' ? '主键' : '索引'; console.log(` ${type}: ${indexName} (${columns.join(', ')})`); }); } // 检查数据样例 try { const [sampleData] = await connection.execute(`SELECT * FROM ${tableName} LIMIT 3`); if (sampleData.length > 0) { console.log(` 📊 数据样例: ${sampleData.length} 条记录`); } else { console.log(' 📊 数据样例: 表为空'); } } catch (error) { console.log(` ❌ 无法获取数据样例: ${error.message}`); } } else { console.log(`❌ 核心表不存在: ${tableName}`); } } // 检查外键约束 console.log('\n🔗 检查外键约束:'); const [foreignKeys] = await connection.execute(` SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME, CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = ? AND REFERENCED_TABLE_NAME IS NOT NULL ORDER BY TABLE_NAME `, [dbConfig.database]); if (foreignKeys.length > 0) { console.log(`📊 找到 ${foreignKeys.length} 个外键约束:`); foreignKeys.forEach(fk => { console.log(` ${fk.TABLE_NAME}.${fk.COLUMN_NAME} -> ${fk.REFERENCED_TABLE_NAME}.${fk.REFERENCED_COLUMN_NAME}`); }); } else { console.log('⚠️ 没有找到外键约束'); } // 数据完整性检查 console.log('\n🔍 数据完整性检查:'); // 检查管理员数据 if (tables.find(t => t.TABLE_NAME === 'admins')) { const [adminCount] = await connection.execute('SELECT COUNT(*) as count FROM admins'); console.log(`👨‍💼 管理员数量: ${adminCount[0].count}`); if (adminCount[0].count > 0) { const [admins] = await connection.execute('SELECT username, role, status FROM admins LIMIT 5'); admins.forEach(admin => { console.log(` - ${admin.username} (${admin.role}, 状态: ${admin.status})`); }); } } // 检查用户数据 if (tables.find(t => t.TABLE_NAME === 'users')) { const [userCount] = await connection.execute('SELECT COUNT(*) as count FROM users'); console.log(`👥 用户数量: ${userCount[0].count}`); if (userCount[0].count > 0) { const [userStats] = await connection.execute(` SELECT user_type, COUNT(*) as count FROM users GROUP BY user_type `); userStats.forEach(stat => { console.log(` - ${stat.user_type}: ${stat.count} 人`); }); } } console.log('\n🎉 数据库结构检查完成!'); return { success: true, tableCount: tables.length, totalRows: totalRows, tables: tables.map(t => t.TABLE_NAME) }; } catch (error) { console.error('❌ 数据库结构检查失败:', error.message); console.error('🔍 错误代码:', error.code); return { success: false, error: error.message }; } finally { if (connection) { await connection.end(); console.log('🔒 数据库连接已关闭'); } } } // 如果是直接运行此文件,则执行检查 if (require.main === module) { checkDatabaseStructure() .then((result) => { process.exit(result.success ? 0 : 1); }) .catch(() => process.exit(1)); } module.exports = { checkDatabaseStructure };