重构后端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 };
|
||||
227
backend/scripts/insert-more-test-data.js
Normal file
227
backend/scripts/insert-more-test-data.js
Normal 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 };
|
||||
165
backend/scripts/test-db-connection.js
Normal file
165
backend/scripts/test-db-connection.js
Normal 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 };
|
||||
Reference in New Issue
Block a user