重构后端API和配置,新增仪表板数据接口并优化本地开发环境配置
This commit is contained in:
181
backend/scripts/check-table-structure.js
Normal file
181
backend/scripts/check-table-structure.js
Normal file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* 检查数据库表结构脚本
|
||||
* 对比设计文档与实际表结构
|
||||
*/
|
||||
|
||||
const mysql = require('mysql2/promise');
|
||||
const config = require('../config/env');
|
||||
|
||||
async function checkTableStructure() {
|
||||
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 || 'utf8mb4',
|
||||
timezone: config.mysql.timezone || '+08:00'
|
||||
};
|
||||
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
console.log('✅ 数据库连接成功');
|
||||
|
||||
// 检查所有表的详细结构
|
||||
const [tables] = await connection.execute(
|
||||
`SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = ? ORDER BY table_name`,
|
||||
[dbConfig.database]
|
||||
);
|
||||
|
||||
console.log(`\n📊 数据库 ${dbConfig.database} 中共有 ${tables.length} 个表:`);
|
||||
|
||||
for (const table of tables) {
|
||||
const tableName = table.TABLE_NAME || table.table_name;
|
||||
console.log(`\n🔍 检查表: ${tableName}`);
|
||||
|
||||
// 获取表结构
|
||||
const [columns] = await connection.execute(
|
||||
`SELECT
|
||||
COLUMN_NAME,
|
||||
DATA_TYPE,
|
||||
IS_NULLABLE,
|
||||
COLUMN_DEFAULT,
|
||||
COLUMN_KEY,
|
||||
EXTRA,
|
||||
COLUMN_COMMENT
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE table_schema = ? AND table_name = ?
|
||||
ORDER BY ORDINAL_POSITION`,
|
||||
[dbConfig.database, tableName]
|
||||
);
|
||||
|
||||
// 获取表记录数
|
||||
const [countResult] = await connection.execute(`SELECT COUNT(*) AS count FROM ${tableName}`);
|
||||
const recordCount = countResult[0].count;
|
||||
|
||||
console.log(` 📊 记录数: ${recordCount}`);
|
||||
console.log(` 📋 字段结构 (${columns.length} 个字段):`);
|
||||
|
||||
columns.forEach(col => {
|
||||
const nullable = col.IS_NULLABLE === 'YES' ? 'NULL' : 'NOT NULL';
|
||||
const key = col.COLUMN_KEY ? `[${col.COLUMN_KEY}]` : '';
|
||||
const extra = col.EXTRA ? `[${col.EXTRA}]` : '';
|
||||
const defaultVal = col.COLUMN_DEFAULT !== null ? `DEFAULT: ${col.COLUMN_DEFAULT}` : '';
|
||||
const comment = col.COLUMN_COMMENT ? `// ${col.COLUMN_COMMENT}` : '';
|
||||
|
||||
console.log(` - ${col.COLUMN_NAME}: ${col.DATA_TYPE} ${nullable} ${key} ${extra} ${defaultVal} ${comment}`);
|
||||
});
|
||||
|
||||
// 获取索引信息
|
||||
const [indexes] = await connection.execute(
|
||||
`SELECT
|
||||
INDEX_NAME,
|
||||
COLUMN_NAME,
|
||||
NON_UNIQUE,
|
||||
INDEX_TYPE
|
||||
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] = {
|
||||
columns: [],
|
||||
unique: idx.NON_UNIQUE === 0,
|
||||
type: idx.INDEX_TYPE
|
||||
};
|
||||
}
|
||||
indexGroups[idx.INDEX_NAME].columns.push(idx.COLUMN_NAME);
|
||||
});
|
||||
|
||||
Object.entries(indexGroups).forEach(([indexName, info]) => {
|
||||
const uniqueStr = info.unique ? '[UNIQUE]' : '';
|
||||
console.log(` - ${indexName}: (${info.columns.join(', ')}) ${uniqueStr} [${info.type}]`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 检查外键约束
|
||||
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, COLUMN_NAME`,
|
||||
[dbConfig.database]
|
||||
);
|
||||
|
||||
if (foreignKeys.length > 0) {
|
||||
foreignKeys.forEach(fk => {
|
||||
console.log(` - ${fk.TABLE_NAME}.${fk.COLUMN_NAME} -> ${fk.REFERENCED_TABLE_NAME}.${fk.REFERENCED_COLUMN_NAME} [${fk.CONSTRAINT_NAME}]`);
|
||||
});
|
||||
} else {
|
||||
console.log(' ⚠️ 未发现外键约束');
|
||||
}
|
||||
|
||||
// 检查表大小
|
||||
console.log(`\n💾 检查表存储大小:`);
|
||||
const [tableSizes] = await connection.execute(
|
||||
`SELECT
|
||||
table_name,
|
||||
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'size_mb',
|
||||
table_rows
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = ?
|
||||
ORDER BY (data_length + index_length) DESC`,
|
||||
[dbConfig.database]
|
||||
);
|
||||
|
||||
tableSizes.forEach(size => {
|
||||
console.log(` - ${size.table_name}: ${size.size_mb} MB (${size.table_rows} 行)`);
|
||||
});
|
||||
|
||||
console.log('\n🎉 表结构检查完成!');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
tableCount: tables.length,
|
||||
foreignKeyCount: foreignKeys.length
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查表结构失败:', error.message);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔒 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是直接运行此文件,则执行检查
|
||||
if (require.main === module) {
|
||||
checkTableStructure()
|
||||
.then((result) => {
|
||||
process.exit(result.success ? 0 : 1);
|
||||
})
|
||||
.catch(() => process.exit(1));
|
||||
}
|
||||
|
||||
module.exports = { checkTableStructure };
|
||||
Reference in New Issue
Block a user