重构后端API和配置,新增仪表板数据接口并优化本地开发环境配置

This commit is contained in:
ylweng
2025-09-21 23:18:08 +08:00
parent 14aca938de
commit 5fc1a4fcb9
33 changed files with 2990 additions and 321 deletions

View 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 };

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env node
/**
* 插入更多测试数据脚本
* 为数据库添加更丰富的测试数据
*/
const mysql = require('mysql2/promise');
const config = require('../config/env');
async function insertMoreTestData() {
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('✅ 数据库连接成功');
// 1. 插入更多用户数据
console.log('\n👤 插入更多用户数据...');
const newUsers = [
['user007', '$2b$10$hash7', 'user007@example.com', '13800000007', '张小明', 'https://example.com/avatar7.jpg', 'tourist', 'active', 1000.00, 150, 2],
['user008', '$2b$10$hash8', 'user008@example.com', '13800000008', '李小红', 'https://example.com/avatar8.jpg', 'farmer', 'active', 2500.00, 300, 3],
['user009', '$2b$10$hash9', 'user009@example.com', '13800000009', '王小刚', 'https://example.com/avatar9.jpg', 'merchant', 'active', 5000.00, 500, 4],
['user010', '$2b$10$hash10', 'user010@example.com', '13800000010', '赵小美', 'https://example.com/avatar10.jpg', 'tourist', 'active', 800.00, 120, 2],
['user011', '$2b$10$hash11', 'user011@example.com', '13800000011', '刘小强', 'https://example.com/avatar11.jpg', 'farmer', 'active', 3200.00, 400, 3],
['user012', '$2b$10$hash12', 'user012@example.com', '13800000012', '陈小丽', 'https://example.com/avatar12.jpg', 'tourist', 'active', 1500.00, 200, 2]
];
for (const user of newUsers) {
try {
await connection.execute(
`INSERT INTO users (username, password_hash, email, phone, real_name, avatar_url, user_type, status, balance, points, level, last_login_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
user
);
console.log(` ✅ 用户 ${user[0]} 插入成功`);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
console.log(` ⚠️ 用户 ${user[0]} 已存在,跳过`);
} else {
console.log(` ❌ 用户 ${user[0]} 插入失败: ${error.message}`);
}
}
}
// 2. 插入更多动物数据
console.log('\n🐄 插入更多动物数据...');
const newAnimals = [
['小花牛', 'cow', '荷斯坦', 24, 450.50, 'female', '温顺可爱的小花牛,喜欢在草地上悠闲地吃草', 'https://example.com/cow1.jpg', '["https://example.com/cow1_1.jpg","https://example.com/cow1_2.jpg"]', 1200.00, 15.00, '阳光农场', 2, 'available', 'healthy', '["疫苗A", "疫苗B"]'],
['小黑猪', 'pig', '黑毛猪', 12, 80.30, 'male', '活泼好动的小黑猪,很聪明', 'https://example.com/pig1.jpg', '["https://example.com/pig1_1.jpg","https://example.com/pig1_2.jpg"]', 800.00, 8.00, '绿野农场', 3, 'available', 'healthy', '["疫苗C", "疫苗D"]'],
['小白羊', 'sheep', '绵羊', 18, 35.20, 'female', '毛茸茸的小白羊,很温顺', 'https://example.com/sheep1.jpg', '["https://example.com/sheep1_1.jpg","https://example.com/sheep1_2.jpg"]', 600.00, 6.00, '山坡农场', 2, 'available', 'healthy', '["疫苗E"]'],
['小黄鸡', 'chicken', '土鸡', 6, 2.50, 'female', '活泼的小黄鸡,会下蛋', 'https://example.com/chicken1.jpg', '["https://example.com/chicken1_1.jpg"]', 150.00, 2.00, '家禽农场', 3, 'available', 'healthy', '["疫苗F"]'],
['小白鸭', 'duck', '白鸭', 8, 3.20, 'male', '游泳高手小白鸭', 'https://example.com/duck1.jpg', '["https://example.com/duck1_1.jpg"]', 200.00, 3.00, '水边农场', 2, 'available', 'healthy', '["疫苗G"]'],
['小灰兔', 'rabbit', '灰兔', 4, 1.80, 'female', '可爱的小灰兔,爱吃胡萝卜', 'https://example.com/rabbit1.jpg', '["https://example.com/rabbit1_1.jpg"]', 120.00, 1.50, '兔子农场', 3, 'available', 'healthy', '["疫苗H"]']
];
for (const animal of newAnimals) {
try {
await connection.execute(
`INSERT INTO animals (name, type, breed, age, weight, gender, description, image, images, price, daily_cost, location, farmer_id, status, health_status, vaccination_records)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
animal
);
console.log(` ✅ 动物 ${animal[0]} 插入成功`);
} catch (error) {
console.log(` ❌ 动物 ${animal[0]} 插入失败: ${error.message}`);
}
}
// 3. 插入更多旅行计划
console.log('\n✈ 插入更多旅行计划...');
const newTravelPlans = [
['云南大理古城深度游', '探索大理古城的历史文化,品尝当地美食,体验白族风情', '大理', '2025-04-15', '2025-04-20', 15, 2, 1800.00, '["住宿", "早餐", "导游"]', '["午餐", "晚餐", "购物"]', '["第一天:抵达大理", "第二天:古城游览", "第三天:洱海环游"]', '["https://example.com/dali1.jpg"]', '身体健康,无重大疾病', 1, 'published'],
['西藏拉萨朝圣之旅', '神圣的西藏之旅,感受藏族文化的魅力', '拉萨', '2025-05-10', '2025-05-18', 12, 1, 3500.00, '["住宿", "三餐", "导游", "门票"]', '["个人消费", "高原反应药物"]', '["第一天:抵达拉萨适应", "第二天:布达拉宫", "第三天:大昭寺"]', '["https://example.com/lasa1.jpg"]', '身体健康,适应高原环境', 2, 'published'],
['海南三亚海滨度假', '享受阳光沙滩,品尝海鲜美食', '三亚', '2025-03-20', '2025-03-25', 20, 5, 2200.00, '["住宿", "早餐", "接送机"]', '["午餐", "晚餐", "水上项目"]', '["第一天:抵达三亚", "第二天:天涯海角", "第三天:蜈支洲岛"]', '["https://example.com/sanya1.jpg"]', '会游泳者优先', 1, 'published'],
['张家界奇峰探险', '探索张家界的奇峰异石,体验玻璃桥刺激', '张家界', '2025-06-01', '2025-06-05', 18, 3, 1600.00, '["住宿", "早餐", "门票", "导游"]', '["午餐", "晚餐", "索道费用"]', '["第一天:森林公园", "第二天:天门山", "第三天:玻璃桥"]', '["https://example.com/zjj1.jpg"]', '不恐高,身体健康', 2, 'published']
];
for (const plan of newTravelPlans) {
try {
await connection.execute(
`INSERT INTO travel_plans (title, description, destination, start_date, end_date, max_participants, current_participants, price_per_person, includes, excludes, itinerary, images, requirements, created_by, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
plan
);
console.log(` ✅ 旅行计划 ${plan[0]} 插入成功`);
} catch (error) {
console.log(` ❌ 旅行计划 ${plan[0]} 插入失败: ${error.message}`);
}
}
// 4. 插入更多鲜花数据
console.log('\n🌸 插入更多鲜花数据...');
const newFlowers = [
['蓝色妖姬', 'Rosa Blue', 'rose', '蓝色', '神秘优雅的蓝色玫瑰,象征珍贵的爱', '避免阳光直射,保持适当湿度', 'https://example.com/blue_rose.jpg', '["https://example.com/blue_rose1.jpg"]', 25.00, 50, 2, 'available', '["春季", "夏季"]'],
['向日葵', 'Helianthus annuus', 'sunflower', '黄色', '阳光般灿烂的向日葵,象征希望和活力', '需要充足阳光,定期浇水', 'https://example.com/sunflower.jpg', '["https://example.com/sunflower1.jpg"]', 15.00, 80, 3, 'available', '["夏季", "秋季"]'],
['紫色薰衣草', 'Lavandula', 'other', '紫色', '芳香怡人的薰衣草,有助于放松心情', '喜欢干燥环境,不要过度浇水', 'https://example.com/lavender.jpg', '["https://example.com/lavender1.jpg"]', 18.00, 60, 2, 'available', '["春季", "夏季"]'],
['白色百合', 'Lilium candidum', 'lily', '白色', '纯洁高雅的白百合,象征纯真和高贵', '保持土壤湿润,避免积水', 'https://example.com/white_lily.jpg', '["https://example.com/white_lily1.jpg"]', 30.00, 40, 3, 'available', '["春季", "夏季", "秋季"]'],
['粉色康乃馨', 'Dianthus caryophyllus', 'carnation', '粉色', '温馨的粉色康乃馨,表达感恩和关爱', '适中浇水,保持通风', 'https://example.com/pink_carnation.jpg', '["https://example.com/pink_carnation1.jpg"]', 12.00, 100, 2, 'available', '["全年"]'],
['红色郁金香', 'Tulipa gesneriana', 'tulip', '红色', '热情的红色郁金香,象征热烈的爱情', '春季种植,夏季休眠', 'https://example.com/red_tulip.jpg', '["https://example.com/red_tulip1.jpg"]', 20.00, 70, 3, 'available', '["春季"]']
];
for (const flower of newFlowers) {
try {
await connection.execute(
`INSERT INTO flowers (name, scientific_name, category, color, description, care_instructions, image, images, price, stock_quantity, farmer_id, status, seasonal_availability)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
flower
);
console.log(` ✅ 鲜花 ${flower[0]} 插入成功`);
} catch (error) {
console.log(` ❌ 鲜花 ${flower[0]} 插入失败: ${error.message}`);
}
}
// 5. 插入更多订单数据
console.log('\n📦 插入更多订单数据...');
const newOrders = [
['ORD' + Date.now() + '001', 1, 'flower', 1, '购买蓝色妖姬', '为女朋友购买生日礼物', 75.00, 5.00, 70.00, 'paid', 'paid', 'wechat', '{"name":"张三","phone":"13800000001","address":"北京市朝阳区"}', '{"name":"张三","phone":"13800000001"}', '希望包装精美'],
['ORD' + Date.now() + '002', 2, 'animal_claim', 2, '认领小黑猪', '想要认领一只可爱的小猪', 2400.00, 0.00, 2400.00, 'processing', 'paid', 'alipay', null, '{"name":"李四","phone":"13800000002"}', '希望能经常看到小猪的照片'],
['ORD' + Date.now() + '003', 3, 'travel', 1, '云南大理古城深度游', '参加大理旅游团', 1800.00, 100.00, 1700.00, 'confirmed', 'paid', 'wechat', null, '{"name":"王五","phone":"13800000003"}', '素食主义者'],
['ORD' + Date.now() + '004', 4, 'flower', 3, '购买向日葵花束', '办公室装饰用花', 45.00, 0.00, 45.00, 'shipped', 'paid', 'bank_card', '{"name":"赵六","phone":"13800000004","address":"上海市浦东新区"}', '{"name":"赵六","phone":"13800000004"}', '需要开发票']
];
for (const order of newOrders) {
try {
await connection.execute(
`INSERT INTO orders (order_no, user_id, type, related_id, title, description, total_amount, discount_amount, final_amount, status, payment_status, payment_method, shipping_address, contact_info, notes, payment_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
order
);
console.log(` ✅ 订单 ${order[0]} 插入成功`);
} catch (error) {
console.log(` ❌ 订单 ${order[0]} 插入失败: ${error.message}`);
}
}
// 6. 插入更多动物认领记录
console.log('\n🐾 插入更多动物认领记录...');
const newClaims = [
['CLAIM' + Date.now() + '001', 6, 1, '喜欢小动物,想要体验农场生活', 90, 1080.00, '{"name":"张三","phone":"13800000001","email":"user001@example.com"}', 'approved', 1, '2025-01-15', '2025-04-15'],
['CLAIM' + Date.now() + '002', 7, 2, '想给孩子一个特别的生日礼物', 60, 480.00, '{"name":"李四","phone":"13800000002","email":"user002@example.com"}', 'approved', 1, '2025-02-01', '2025-04-01'],
['CLAIM' + Date.now() + '003', 8, 3, '支持农场发展,保护动物', 120, 720.00, '{"name":"王五","phone":"13800000003","email":"user003@example.com"}', 'pending', null, '2025-03-01', '2025-07-01']
];
for (const claim of newClaims) {
try {
await connection.execute(
`INSERT INTO animal_claims (claim_no, animal_id, user_id, claim_reason, claim_duration, total_amount, contact_info, status, reviewed_by, start_date, end_date, reviewed_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
claim
);
console.log(` ✅ 认领记录 ${claim[0]} 插入成功`);
} catch (error) {
console.log(` ❌ 认领记录 ${claim[0]} 插入失败: ${error.message}`);
}
}
// 7. 插入旅行报名记录
console.log('\n🎒 插入更多旅行报名记录...');
const newRegistrations = [
[4, 1, 2, '我和朋友一起参加,希望能安排同房间', '张三', '13900000001', 'approved'],
[5, 2, 1, '一个人旅行,希望能认识新朋友', '李四', '13900000002', 'approved'],
[6, 3, 3, '全家出游,有老人和小孩', '王五', '13900000003', 'pending'],
[7, 4, 1, '摄影爱好者,希望多拍照', '赵六', '13900000004', 'approved']
];
for (const reg of newRegistrations) {
try {
await connection.execute(
`INSERT INTO travel_registrations (travel_plan_id, user_id, participants, message, emergency_contact, emergency_phone, status, responded_at)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())`,
reg
);
console.log(` ✅ 旅行报名记录插入成功`);
} catch (error) {
console.log(` ❌ 旅行报名记录插入失败: ${error.message}`);
}
}
// 8. 统计最终数据
console.log('\n📊 统计最终数据量...');
const tables = ['users', 'animals', 'travel_plans', 'flowers', 'orders', 'animal_claims', 'travel_registrations'];
for (const table of tables) {
const [result] = await connection.execute(`SELECT COUNT(*) AS count FROM ${table}`);
console.log(` 📋 ${table}: ${result[0].count} 条记录`);
}
console.log('\n🎉 测试数据插入完成!');
console.log('✅ 数据库现在包含了丰富的测试数据,可以进行各种功能测试');
return { success: true };
} catch (error) {
console.error('❌ 插入测试数据失败:', error.message);
return { success: false, error: error.message };
} finally {
if (connection) {
await connection.end();
console.log('🔒 数据库连接已关闭');
}
}
}
// 如果是直接运行此文件,则执行插入
if (require.main === module) {
insertMoreTestData()
.then((result) => {
process.exit(result.success ? 0 : 1);
})
.catch(() => process.exit(1));
}
module.exports = { insertMoreTestData };

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env node
/**
* 数据库连接测试脚本 - 简化版
* 用于验证MySQL数据库连接配置的正确性
*/
const mysql = require('mysql2/promise');
const config = require('../config/env');
async function testDatabaseConnection() {
let connection;
try {
console.log('🚀 开始数据库连接测试...');
console.log(`📊 环境: ${process.env.NODE_ENV || 'development'}`);
// 使用env.js中的mysql配置
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'
};
console.log(`🔗 连接信息: ${dbConfig.host}:${dbConfig.port}/${dbConfig.database}`);
console.log('='.repeat(50));
// 测试连接
console.log('🔍 测试数据库连接...');
connection = await mysql.createConnection(dbConfig);
console.log('✅ 数据库连接成功');
// 测试基本查询
console.log('🔍 测试基本查询...');
const [rows] = await connection.execute('SELECT 1 + 1 AS result');
console.log(`✅ 查询测试成功: ${rows[0].result}`);
// 检查数据库版本
console.log('🔍 检查数据库版本...');
const [versionRows] = await connection.execute('SELECT VERSION() AS version');
console.log(`📊 MySQL版本: ${versionRows[0].version}`);
// 检查当前时间
console.log('🔍 检查服务器时间...');
const [timeRows] = await connection.execute('SELECT NOW() AS server_time');
console.log(`⏰ 服务器时间: ${timeRows[0].server_time}`);
// 检查数据库字符集
console.log('🔍 检查数据库字符集...');
const [charsetRows] = await connection.execute(
'SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?',
[dbConfig.database]
);
if (charsetRows.length > 0) {
console.log(`📝 数据库字符集: ${charsetRows[0].DEFAULT_CHARACTER_SET_NAME}`);
console.log(`📝 数据库排序规则: ${charsetRows[0].DEFAULT_COLLATION_NAME}`);
}
// 检查表结构
console.log('🔍 检查核心表结构...');
const tablesToCheck = [
'admins', 'users', 'merchants', 'orders', 'payments',
'animals', 'animal_claims', 'travel_plans', 'travel_registrations',
'flowers', 'refunds'
];
const existingTables = [];
const missingTables = [];
for (const table of tablesToCheck) {
try {
const [tableInfo] = await connection.execute(
`SELECT COUNT(*) AS count FROM information_schema.tables
WHERE table_schema = ? AND table_name = ?`,
[dbConfig.database, table]
);
if (tableInfo[0].count > 0) {
console.log(`✅ 表存在: ${table}`);
existingTables.push(table);
// 检查表记录数
const [countRows] = await connection.execute(`SELECT COUNT(*) AS count FROM ${table}`);
console.log(` 📊 记录数: ${countRows[0].count}`);
} else {
console.log(`⚠️ 表不存在: ${table}`);
missingTables.push(table);
}
} catch (error) {
console.log(`❌ 检查表失败: ${table} - ${error.message}`);
missingTables.push(table);
}
}
console.log('\n📋 数据库状态总结:');
console.log(`✅ 存在的表: ${existingTables.length}/${tablesToCheck.length}`);
if (missingTables.length > 0) {
console.log(`⚠️ 缺失的表: ${missingTables.join(', ')}`);
console.log('💡 建议运行数据库迁移脚本创建缺失的表');
}
console.log('\n🎉 数据库连接测试完成!');
console.log('✅ 数据库连接正常');
return {
success: true,
existingTables,
missingTables,
dbConfig: {
host: dbConfig.host,
port: dbConfig.port,
database: dbConfig.database,
user: dbConfig.user
}
};
} catch (error) {
console.error('❌ 数据库连接测试失败:', error.message);
console.error('💡 可能的原因:');
console.error(' - 数据库服务未启动');
console.error(' - 连接配置错误');
console.error(' - 网络连接问题');
console.error(' - 数据库权限不足');
console.error(' - 防火墙限制');
console.error(' - IP地址未授权');
if (error.code) {
console.error(`🔍 错误代码: ${error.code}`);
}
console.error('🔍 连接详情:', {
host: config.mysql.host,
port: config.mysql.port,
user: config.mysql.user,
database: config.mysql.database
});
return {
success: false,
error: error.message,
code: error.code
};
} finally {
if (connection) {
await connection.end();
console.log('🔒 数据库连接已关闭');
}
}
}
// 如果是直接运行此文件,则执行测试
if (require.main === module) {
testDatabaseConnection()
.then((result) => {
process.exit(result.success ? 0 : 1);
})
.catch(() => process.exit(1));
}
module.exports = { testDatabaseConnection };