226 lines
7.8 KiB
JavaScript
226 lines
7.8 KiB
JavaScript
#!/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 }; |