重构动物认领页面和导航菜单,统一使用SVG图标并优化交互体验

This commit is contained in:
ylweng
2025-09-21 21:12:27 +08:00
parent 467a4ead10
commit 14aca938de
64 changed files with 25067 additions and 18256 deletions

View File

@@ -0,0 +1,226 @@
#!/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 };

View File

@@ -0,0 +1,370 @@
-- 解班客数据库完整结构创建脚本
-- 创建时间: 2024年
-- 数据库: jbkdata
-- 设置字符集
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 使用数据库
USE jbkdata;
-- ================================
-- 1. 管理员表
-- ================================
CREATE TABLE IF NOT EXISTS `admins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`role` enum('super_admin','admin','operator') DEFAULT 'admin' COMMENT '角色',
`status` tinyint(1) DEFAULT 1 COMMENT '状态 1:启用 0:禁用',
`last_login` timestamp NULL DEFAULT NULL COMMENT '最后登录时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `idx_email` (`email`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='管理员表';
-- ================================
-- 2. 用户表
-- ================================
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password_hash` varchar(255) NOT NULL COMMENT '密码哈希',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`avatar_url` varchar(255) DEFAULT NULL COMMENT '头像URL',
`user_type` enum('farmer','tourist','merchant','admin') DEFAULT 'tourist' COMMENT '用户类型',
`status` enum('active','inactive','banned') DEFAULT 'active' COMMENT '状态',
`balance` decimal(15,2) DEFAULT 0.00 COMMENT '余额',
`points` int(11) DEFAULT 0 COMMENT '积分',
`level` tinyint(4) DEFAULT 1 COMMENT '用户等级',
`last_login_at` timestamp NULL DEFAULT NULL COMMENT '最后登录时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `phone` (`phone`),
KEY `idx_user_type` (`user_type`),
KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- ================================
-- 3. 商家表
-- ================================
CREATE TABLE IF NOT EXISTS `merchants` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '关联用户ID',
`business_name` varchar(100) NOT NULL COMMENT '商家名称',
`business_type` enum('restaurant','hotel','farm','attraction','transport') NOT NULL COMMENT '商家类型',
`description` text COMMENT '商家描述',
`address` varchar(255) DEFAULT NULL COMMENT '地址',
`latitude` decimal(10,8) DEFAULT NULL COMMENT '纬度',
`longitude` decimal(11,8) DEFAULT NULL COMMENT '经度',
`contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`business_hours` json DEFAULT NULL COMMENT '营业时间',
`images` json DEFAULT NULL COMMENT '商家图片',
`rating` decimal(3,2) DEFAULT 0.00 COMMENT '评分',
`review_count` int(11) DEFAULT 0 COMMENT '评价数量',
`status` enum('pending','approved','rejected','suspended') DEFAULT 'pending' COMMENT '状态',
`verified_at` timestamp NULL DEFAULT NULL COMMENT '认证时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `idx_business_type` (`business_type`),
KEY `idx_status` (`status`),
KEY `idx_location` (`latitude`,`longitude`),
CONSTRAINT `merchants_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商家表';
-- ================================
-- 4. 动物表
-- ================================
CREATE TABLE IF NOT EXISTS `animals` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '动物名称',
`type` enum('chicken','duck','pig','cow','sheep','rabbit','fish') NOT NULL COMMENT '动物类型',
`breed` varchar(50) DEFAULT NULL COMMENT '品种',
`age` int(11) DEFAULT NULL COMMENT '年龄(月)',
`weight` decimal(8,2) DEFAULT NULL COMMENT '重量(kg)',
`gender` enum('male','female','unknown') DEFAULT 'unknown' COMMENT '性别',
`description` text COMMENT '描述',
`image` varchar(255) DEFAULT NULL COMMENT '图片URL',
`images` json DEFAULT NULL COMMENT '多张图片',
`price` decimal(10,2) NOT NULL COMMENT '认领价格',
`daily_cost` decimal(8,2) DEFAULT 0.00 COMMENT '每日费用',
`location` varchar(100) DEFAULT NULL COMMENT '所在位置',
`farmer_id` int(11) DEFAULT NULL COMMENT '农户ID',
`status` enum('available','claimed','sold','deceased') DEFAULT 'available' COMMENT '状态',
`health_status` enum('healthy','sick','recovering') DEFAULT 'healthy' COMMENT '健康状态',
`vaccination_records` json DEFAULT NULL COMMENT '疫苗记录',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_type` (`type`),
KEY `idx_status` (`status`),
KEY `idx_farmer_id` (`farmer_id`),
KEY `idx_price` (`price`),
CONSTRAINT `animals_ibfk_1` FOREIGN KEY (`farmer_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='动物表';
-- ================================
-- 5. 动物认领表
-- ================================
CREATE TABLE IF NOT EXISTS `animal_claims` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`claim_no` varchar(50) NOT NULL COMMENT '认领订单号',
`animal_id` int(11) NOT NULL COMMENT '动物ID',
`user_id` int(11) NOT NULL COMMENT '认领用户ID',
`claim_reason` text COMMENT '认领原因',
`claim_duration` int(11) NOT NULL COMMENT '认领时长(天)',
`total_amount` decimal(10,2) NOT NULL COMMENT '总费用',
`contact_info` json DEFAULT NULL COMMENT '联系信息',
`status` enum('pending','approved','rejected','cancelled','completed') DEFAULT 'pending' COMMENT '状态',
`reviewed_by` int(11) DEFAULT NULL COMMENT '审核人ID',
`reviewed_at` timestamp NULL DEFAULT NULL COMMENT '审核时间',
`review_note` text COMMENT '审核备注',
`start_date` date DEFAULT NULL COMMENT '开始日期',
`end_date` date DEFAULT NULL COMMENT '结束日期',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`),
UNIQUE KEY `claim_no` (`claim_no`),
KEY `animal_id` (`animal_id`),
KEY `user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`),
CONSTRAINT `animal_claims_ibfk_1` FOREIGN KEY (`animal_id`) REFERENCES `animals` (`id`) ON DELETE CASCADE,
CONSTRAINT `animal_claims_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='动物认领表';
-- ================================
-- 6. 旅行计划表
-- ================================
CREATE TABLE IF NOT EXISTS `travel_plans` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL COMMENT '标题',
`description` text COMMENT '描述',
`destination` varchar(100) NOT NULL COMMENT '目的地',
`start_date` date NOT NULL COMMENT '开始日期',
`end_date` date NOT NULL COMMENT '结束日期',
`max_participants` int(11) DEFAULT 20 COMMENT '最大参与人数',
`current_participants` int(11) DEFAULT 0 COMMENT '当前参与人数',
`price_per_person` decimal(10,2) NOT NULL COMMENT '每人价格',
`includes` json DEFAULT NULL COMMENT '包含项目',
`excludes` json DEFAULT NULL COMMENT '不包含项目',
`itinerary` json DEFAULT NULL COMMENT '行程安排',
`images` json DEFAULT NULL COMMENT '图片',
`requirements` text COMMENT '参与要求',
`created_by` int(11) NOT NULL COMMENT '创建者ID',
`status` enum('draft','published','cancelled','completed') DEFAULT 'draft' COMMENT '状态',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `created_by` (`created_by`),
KEY `idx_status` (`status`),
KEY `idx_start_date` (`start_date`),
KEY `idx_destination` (`destination`),
CONSTRAINT `travel_plans_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='旅行计划表';
-- ================================
-- 7. 旅行报名表
-- ================================
CREATE TABLE IF NOT EXISTS `travel_registrations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`travel_plan_id` int(11) NOT NULL COMMENT '旅行计划ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`participants` int(11) DEFAULT 1 COMMENT '参与人数',
`message` text COMMENT '留言',
`emergency_contact` varchar(50) DEFAULT NULL COMMENT '紧急联系人',
`emergency_phone` varchar(20) DEFAULT NULL COMMENT '紧急联系电话',
`status` enum('pending','approved','rejected','cancelled') DEFAULT 'pending' COMMENT '状态',
`reject_reason` text COMMENT '拒绝原因',
`applied_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '申请时间',
`responded_at` timestamp NULL DEFAULT NULL COMMENT '响应时间',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_registration` (`travel_plan_id`,`user_id`),
KEY `user_id` (`user_id`),
KEY `idx_status` (`status`),
CONSTRAINT `travel_registrations_ibfk_1` FOREIGN KEY (`travel_plan_id`) REFERENCES `travel_plans` (`id`) ON DELETE CASCADE,
CONSTRAINT `travel_registrations_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='旅行报名表';
-- ================================
-- 8. 鲜花表
-- ================================
CREATE TABLE IF NOT EXISTS `flowers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '花卉名称',
`scientific_name` varchar(100) DEFAULT NULL COMMENT '学名',
`category` enum('rose','lily','tulip','sunflower','orchid','carnation','other') NOT NULL COMMENT '花卉类别',
`color` varchar(30) DEFAULT NULL COMMENT '颜色',
`description` text COMMENT '描述',
`care_instructions` text COMMENT '养护说明',
`image` varchar(255) DEFAULT NULL COMMENT '主图片',
`images` json DEFAULT NULL COMMENT '多张图片',
`price` decimal(8,2) NOT NULL COMMENT '价格',
`stock_quantity` int(11) DEFAULT 0 COMMENT '库存数量',
`farmer_id` int(11) DEFAULT NULL COMMENT '农户ID',
`status` enum('available','out_of_stock','discontinued') DEFAULT 'available' COMMENT '状态',
`seasonal_availability` json DEFAULT NULL COMMENT '季节性供应',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_category` (`category`),
KEY `idx_status` (`status`),
KEY `idx_farmer_id` (`farmer_id`),
KEY `idx_price` (`price`),
CONSTRAINT `flowers_ibfk_1` FOREIGN KEY (`farmer_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='鲜花表';
-- ================================
-- 9. 订单表
-- ================================
CREATE TABLE IF NOT EXISTS `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_no` varchar(50) NOT NULL COMMENT '订单号',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`type` enum('animal_claim','travel','flower','service') NOT NULL COMMENT '订单类型',
`related_id` int(11) DEFAULT NULL COMMENT '关联ID',
`title` varchar(200) NOT NULL COMMENT '订单标题',
`description` text COMMENT '订单描述',
`total_amount` decimal(15,2) NOT NULL COMMENT '总金额',
`discount_amount` decimal(15,2) DEFAULT 0.00 COMMENT '优惠金额',
`final_amount` decimal(15,2) NOT NULL COMMENT '实付金额',
`status` enum('pending','paid','processing','shipped','completed','cancelled','refunded') DEFAULT 'pending' COMMENT '订单状态',
`payment_status` enum('unpaid','paid','refunded','partial_refund') DEFAULT 'unpaid' COMMENT '支付状态',
`payment_method` varchar(50) DEFAULT NULL COMMENT '支付方式',
`payment_time` timestamp NULL DEFAULT NULL COMMENT '支付时间',
`shipping_address` json DEFAULT NULL COMMENT '收货地址',
`contact_info` json DEFAULT NULL COMMENT '联系信息',
`notes` text COMMENT '备注',
`ordered_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `order_no` (`order_no`),
KEY `user_id` (`user_id`),
KEY `idx_type` (`type`),
KEY `idx_status` (`status`),
KEY `idx_payment_status` (`payment_status`),
KEY `idx_ordered_at` (`ordered_at`),
CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表';
-- ================================
-- 10. 支付表
-- ================================
CREATE TABLE IF NOT EXISTS `payments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`payment_no` varchar(50) NOT NULL COMMENT '支付订单号',
`order_id` int(11) NOT NULL COMMENT '订单ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`amount` decimal(15,2) NOT NULL COMMENT '支付金额',
`payment_method` enum('wechat','alipay','bank_card','balance') NOT NULL COMMENT '支付方式',
`status` enum('pending','paid','failed','cancelled','refunded') DEFAULT 'pending' COMMENT '支付状态',
`transaction_id` varchar(100) DEFAULT NULL COMMENT '第三方交易号',
`paid_amount` decimal(15,2) DEFAULT NULL COMMENT '实际支付金额',
`paid_at` timestamp NULL DEFAULT NULL COMMENT '支付时间',
`failure_reason` varchar(255) DEFAULT NULL COMMENT '失败原因',
`return_url` varchar(255) DEFAULT NULL COMMENT '返回URL',
`notify_url` varchar(255) DEFAULT NULL COMMENT '通知URL',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`),
UNIQUE KEY `payment_no` (`payment_no`),
KEY `order_id` (`order_id`),
KEY `user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_payment_method` (`payment_method`),
KEY `idx_created_at` (`created_at`),
CONSTRAINT `payments_ibfk_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE,
CONSTRAINT `payments_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付表';
-- ================================
-- 11. 退款表
-- ================================
CREATE TABLE IF NOT EXISTS `refunds` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`refund_no` varchar(50) NOT NULL COMMENT '退款订单号',
`payment_id` int(11) NOT NULL COMMENT '支付ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`refund_amount` decimal(15,2) NOT NULL COMMENT '退款金额',
`refund_reason` varchar(255) NOT NULL COMMENT '退款原因',
`status` enum('pending','processing','completed','rejected') DEFAULT 'pending' COMMENT '退款状态',
`processed_by` int(11) DEFAULT NULL COMMENT '处理人ID',
`processed_at` timestamp NULL DEFAULT NULL COMMENT '处理时间',
`process_remark` text COMMENT '处理备注',
`refund_transaction_id` varchar(100) DEFAULT NULL COMMENT '退款交易号',
`refunded_at` timestamp NULL DEFAULT NULL COMMENT '退款完成时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`),
UNIQUE KEY `refund_no` (`refund_no`),
KEY `payment_id` (`payment_id`),
KEY `user_id` (`user_id`),
KEY `idx_status` (`status`),
CONSTRAINT `refunds_ibfk_1` FOREIGN KEY (`payment_id`) REFERENCES `payments` (`id`) ON DELETE CASCADE,
CONSTRAINT `refunds_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退款表';
-- ================================
-- 12. 辅助表
-- ================================
-- 邮箱验证表
CREATE TABLE IF NOT EXISTS `email_verifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(100) NOT NULL COMMENT '邮箱',
`code` varchar(10) NOT NULL COMMENT '验证码',
`expires_at` timestamp NOT NULL COMMENT '过期时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
KEY `idx_expires_at` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邮箱验证表';
-- 密码重置表
CREATE TABLE IF NOT EXISTS `password_resets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`token` varchar(255) NOT NULL COMMENT '重置令牌',
`expires_at` timestamp NOT NULL COMMENT '过期时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`),
UNIQUE KEY `token` (`token`),
KEY `idx_expires_at` (`expires_at`),
CONSTRAINT `password_resets_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='密码重置表';
-- 登录尝试表
CREATE TABLE IF NOT EXISTS `login_attempts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`identifier` varchar(100) NOT NULL COMMENT '标识符(用户名/邮箱/IP)',
`attempts` int(11) DEFAULT 1 COMMENT '尝试次数',
`last_attempt` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '最后尝试时间',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `identifier` (`identifier`),
KEY `idx_last_attempt` (`last_attempt`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='登录尝试表';
SET FOREIGN_KEY_CHECKS = 1;
-- 创建完成提示
SELECT '数据库表结构创建完成!' as message;

View File

@@ -0,0 +1,219 @@
#!/usr/bin/env node
/**
* 完整测试数据初始化脚本
* 用于开发环境创建完整的测试数据
*/
const mysql = require('mysql2/promise');
const bcrypt = require('bcryptjs');
const config = require('../config/env');
async function initCompleteTestData() {
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('🧹 清理现有测试数据...');
const tablesToClean = [
'refunds', 'payments', 'orders', 'travel_registrations', 'travel_plans',
'animal_claims', 'animals', 'flowers', 'merchants', 'users', 'admins',
'email_verifications', 'password_resets', 'login_attempts'
];
for (const table of tablesToClean) {
try {
await connection.execute(`DELETE FROM ${table} WHERE id > 0`);
await connection.execute(`ALTER TABLE ${table} AUTO_INCREMENT = 1`);
console.log(` 清理表: ${table}`);
} catch (error) {
console.log(` 跳过表: ${table} (${error.message})`);
}
}
// 1. 插入管理员数据
console.log('👨‍💼 插入管理员数据...');
const adminPassword = await bcrypt.hash('admin123', 10);
const managerPassword = await bcrypt.hash('manager123', 10);
await connection.execute(`
INSERT INTO admins (username, password, email, nickname, role, status) VALUES
('admin', ?, 'admin@jiebanke.com', '超级管理员', 'super_admin', 1),
('manager', ?, 'manager@jiebanke.com', '运营经理', 'admin', 1),
('operator', ?, 'operator@jiebanke.com', '运营专员', 'operator', 1)
`, [adminPassword, managerPassword, await bcrypt.hash('operator123', 10)]);
console.log(' ✅ 管理员数据插入完成');
// 2. 插入用户数据
console.log('👥 插入用户数据...');
const userPassword = await bcrypt.hash('user123', 10);
const farmerPassword = await bcrypt.hash('farmer123', 10);
const merchantPassword = await bcrypt.hash('merchant123', 10);
await connection.execute(`
INSERT INTO users (username, password_hash, email, phone, real_name, user_type, status, balance, points) VALUES
('tourist1', ?, 'tourist1@jiebanke.com', '13800138001', '张三', 'tourist', 'active', 1000.00, 100),
('tourist2', ?, 'tourist2@jiebanke.com', '13800138002', '李四', 'tourist', 'active', 500.00, 50),
('farmer1', ?, 'farmer1@jiebanke.com', '13800138003', '王农夫', 'farmer', 'active', 2000.00, 200),
('farmer2', ?, 'farmer2@jiebanke.com', '13800138004', '赵农民', 'farmer', 'active', 1500.00, 150),
('merchant1', ?, 'merchant1@jiebanke.com', '13800138005', '刘老板', 'merchant', 'active', 5000.00, 500),
('merchant2', ?, 'merchant2@jiebanke.com', '13800138006', '陈经理', 'merchant', 'active', 3000.00, 300)
`, [userPassword, userPassword, farmerPassword, farmerPassword, merchantPassword, merchantPassword]);
console.log(' ✅ 用户数据插入完成');
// 3. 插入商家数据
console.log('🏪 插入商家数据...');
await connection.execute(`
INSERT INTO merchants (user_id, business_name, business_type, description, address, contact_phone, status) VALUES
(5, '山水农家乐', 'restaurant', '提供正宗农家菜和住宿服务', '北京市密云区某某村', '13800138005', 'approved'),
(6, '绿野仙踪度假村', 'hotel', '生态度假村,环境优美', '河北省承德市某某镇', '13800138006', 'approved')
`);
console.log(' ✅ 商家数据插入完成');
// 4. 插入动物数据
console.log('🐷 插入动物数据...');
await connection.execute(`
INSERT INTO animals (name, type, breed, age, weight, gender, description, price, daily_cost, farmer_id, status) VALUES
('小花', 'pig', '土猪', 6, 50.5, 'female', '健康活泼的小母猪', 800.00, 5.00, 3, 'available'),
('大黄', 'chicken', '土鸡', 8, 2.5, 'male', '散养公鸡,肉质鲜美', 120.00, 2.00, 3, 'available'),
('小白', 'sheep', '绵羊', 12, 35.0, 'female', '温顺的小绵羊', 600.00, 4.00, 4, 'available'),
('老黑', 'cow', '黄牛', 24, 300.0, 'male', '强壮的耕牛', 2000.00, 10.00, 4, 'available'),
('小灰', 'rabbit', '肉兔', 3, 1.8, 'female', '可爱的小兔子', 80.00, 1.50, 3, 'available')
`);
console.log(' ✅ 动物数据插入完成');
// 5. 插入鲜花数据
console.log('🌸 插入鲜花数据...');
await connection.execute(`
INSERT INTO flowers (name, category, color, description, price, stock_quantity, farmer_id, status) VALUES
('红玫瑰', 'rose', '红色', '经典红玫瑰,象征爱情', 15.00, 100, 3, 'available'),
('白百合', 'lily', '白色', '纯洁的白百合,适合送礼', 25.00, 50, 3, 'available'),
('黄郁金香', 'tulip', '黄色', '明亮的黄郁金香', 20.00, 80, 4, 'available'),
('向日葵', 'sunflower', '黄色', '阳光般的向日葵', 12.00, 120, 4, 'available'),
('粉康乃馨', 'carnation', '粉色', '温馨的粉色康乃馨', 18.00, 90, 3, 'available')
`);
console.log(' ✅ 鲜花数据插入完成');
// 6. 插入旅行计划数据
console.log('✈️ 插入旅行计划数据...');
await connection.execute(`
INSERT INTO travel_plans (title, description, destination, start_date, end_date, max_participants, price_per_person, created_by, status) VALUES
('密云水库生态游', '体验密云水库的自然风光,品尝农家美食', '北京密云', '2024-05-01', '2024-05-03', 20, 299.00, 5, 'published'),
('承德避暑山庄文化游', '探访承德避暑山庄,了解清朝历史文化', '河北承德', '2024-06-15', '2024-06-17', 15, 599.00, 6, 'published'),
('农场体验亲子游', '带孩子体验农场生活,学习农业知识', '北京郊区', '2024-07-01', '2024-07-02', 25, 199.00, 3, 'published')
`);
console.log(' ✅ 旅行计划数据插入完成');
// 7. 插入动物认领数据
console.log('🐾 插入动物认领数据...');
await connection.execute(`
INSERT INTO animal_claims (claim_no, animal_id, user_id, claim_reason, claim_duration, total_amount, status, start_date, end_date) VALUES
('AC202401001', 1, 1, '想体验养猪的乐趣', 30, 950.00, 'approved', '2024-04-01', '2024-04-30'),
('AC202401002', 2, 2, '孩子喜欢小鸡', 15, 150.00, 'pending', '2024-04-15', '2024-04-29'),
('AC202401003', 3, 1, '认领小羊作为宠物', 60, 840.00, 'approved', '2024-03-01', '2024-04-29')
`);
console.log(' ✅ 动物认领数据插入完成');
// 8. 插入旅行报名数据
console.log('📝 插入旅行报名数据...');
await connection.execute(`
INSERT INTO travel_registrations (travel_plan_id, user_id, participants, message, emergency_contact, emergency_phone, status) VALUES
(1, 1, 2, '期待这次旅行', '张三妻子', '13900139001', 'approved'),
(1, 2, 1, '第一次参加农家游', '李四父亲', '13900139002', 'pending'),
(2, 1, 3, '全家一起出游', '张三妻子', '13900139001', 'approved'),
(3, 2, 2, '带孩子体验农场', '李四妻子', '13900139003', 'pending')
`);
console.log(' ✅ 旅行报名数据插入完成');
// 9. 插入订单数据
console.log('📦 插入订单数据...');
await connection.execute(`
INSERT INTO orders (order_no, user_id, type, related_id, title, total_amount, final_amount, status, payment_status) VALUES
('ORD202401001', 1, 'animal_claim', 1, '认领小花猪30天', 950.00, 950.00, 'completed', 'paid'),
('ORD202401002', 2, 'animal_claim', 2, '认领大黄鸡15天', 150.00, 150.00, 'pending', 'unpaid'),
('ORD202401003', 1, 'travel', 1, '密云水库生态游 2人', 598.00, 598.00, 'completed', 'paid'),
('ORD202401004', 1, 'flower', 1, '红玫瑰 10支', 150.00, 150.00, 'completed', 'paid')
`);
console.log(' ✅ 订单数据插入完成');
// 10. 插入支付数据
console.log('💳 插入支付数据...');
await connection.execute(`
INSERT INTO payments (payment_no, order_id, user_id, amount, payment_method, status, paid_amount, paid_at) VALUES
('PAY202401001', 1, 1, 950.00, 'wechat', 'paid', 950.00, '2024-04-01 10:30:00'),
('PAY202401002', 3, 1, 598.00, 'alipay', 'paid', 598.00, '2024-04-02 14:20:00'),
('PAY202401003', 4, 1, 150.00, 'wechat', 'paid', 150.00, '2024-04-03 16:45:00')
`);
console.log(' ✅ 支付数据插入完成');
// 统计插入的数据
console.log('\n📊 数据统计:');
const tables = ['admins', 'users', 'merchants', 'animals', 'flowers', 'travel_plans', 'animal_claims', 'travel_registrations', 'orders', 'payments'];
for (const table of tables) {
try {
const [rows] = await connection.execute(`SELECT COUNT(*) as count FROM ${table}`);
console.log(` ${table}: ${rows[0].count} 条记录`);
} catch (error) {
console.log(` ${table}: 表不存在或查询失败`);
}
}
console.log('\n🎉 完整测试数据初始化完成!');
console.log('📋 测试账号信息:');
console.log(' 管理员账号:');
console.log(' admin / admin123 (超级管理员)');
console.log(' manager / manager123 (运营经理)');
console.log(' operator / operator123 (运营专员)');
console.log(' 用户账号:');
console.log(' tourist1 / user123 (游客)');
console.log(' tourist2 / user123 (游客)');
console.log(' farmer1 / farmer123 (农户)');
console.log(' farmer2 / farmer123 (农户)');
console.log(' merchant1 / merchant123 (商家)');
console.log(' merchant2 / merchant123 (商家)');
} catch (error) {
console.error('❌ 初始化测试数据失败:', error.message);
if (error.code === 'ER_NO_SUCH_TABLE') {
console.log('💡 提示: 请先运行数据库结构创建脚本');
}
throw error;
} finally {
if (connection) {
await connection.end();
console.log('🔒 数据库连接已关闭');
}
}
}
// 如果是直接运行此文件,则执行初始化
if (require.main === module) {
initCompleteTestData()
.then(() => {
console.log('✅ 脚本执行成功');
process.exit(0);
})
.catch((error) => {
console.error('❌ 脚本执行失败:', error.message);
process.exit(1);
});
}
module.exports = { initCompleteTestData };

View File

@@ -0,0 +1,124 @@
#!/usr/bin/env node
/**
* 后端数据库连接测试脚本
* 测试backend中的数据库连接配置是否正常工作
*/
const { testConnection, query } = require('../src/config/database');
async function testBackendConnection() {
console.log('🚀 测试后端数据库连接配置...');
console.log('='.repeat(50));
try {
// 测试数据库连接
console.log('🔍 测试数据库连接...');
const isConnected = await testConnection();
if (!isConnected) {
console.error('❌ 数据库连接失败');
return false;
}
// 测试查询功能
console.log('🔍 测试查询功能...');
const testResult = await query('SELECT 1 + 1 as result, NOW() as server_time');
console.log(`✅ 查询测试成功: ${testResult[0].result}, 时间: ${testResult[0].server_time}`);
// 测试管理员数据查询
console.log('🔍 测试管理员数据查询...');
const admins = await query('SELECT id, username, role, status FROM admins LIMIT 3');
console.log(`📊 管理员数据: ${admins.length} 条记录`);
admins.forEach(admin => {
console.log(` - ID:${admin.id} ${admin.username} (${admin.role}, 状态:${admin.status})`);
});
// 测试用户数据查询
console.log('🔍 测试用户数据查询...');
const users = await query('SELECT id, username, user_type, status, balance FROM users LIMIT 5');
console.log(`📊 用户数据: ${users.length} 条记录`);
users.forEach(user => {
console.log(` - ID:${user.id} ${user.username} (${user.user_type}, 余额:${user.balance})`);
});
// 测试动物数据查询
console.log('🔍 测试动物数据查询...');
const animals = await query('SELECT id, name, type, price, status FROM animals LIMIT 5');
console.log(`📊 动物数据: ${animals.length} 条记录`);
animals.forEach(animal => {
console.log(` - ID:${animal.id} ${animal.name} (${animal.type}, 价格:${animal.price})`);
});
// 测试订单数据查询
console.log('🔍 测试订单数据查询...');
const orders = await query(`
SELECT o.id, o.order_no, o.type, o.final_amount, o.status, u.username
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
LIMIT 5
`);
console.log(`📊 订单数据: ${orders.length} 条记录`);
orders.forEach(order => {
console.log(` - ${order.order_no} (${order.type}, ¥${order.final_amount}, ${order.status}) - ${order.username}`);
});
// 测试支付数据查询
console.log('🔍 测试支付数据查询...');
const payments = await query(`
SELECT p.id, p.payment_no, p.amount, p.payment_method, p.status, u.username
FROM payments p
LEFT JOIN users u ON p.user_id = u.id
LIMIT 5
`);
console.log(`📊 支付数据: ${payments.length} 条记录`);
payments.forEach(payment => {
console.log(` - ${payment.payment_no}${payment.amount}, ${payment.payment_method}, ${payment.status}) - ${payment.username}`);
});
// 统计数据
console.log('\n📊 数据库统计:');
const stats = await query(`
SELECT
(SELECT COUNT(*) FROM admins) as admin_count,
(SELECT COUNT(*) FROM users) as user_count,
(SELECT COUNT(*) FROM merchants) as merchant_count,
(SELECT COUNT(*) FROM animals) as animal_count,
(SELECT COUNT(*) FROM flowers) as flower_count,
(SELECT COUNT(*) FROM travel_plans) as travel_plan_count,
(SELECT COUNT(*) FROM orders) as order_count,
(SELECT COUNT(*) FROM payments) as payment_count
`);
const stat = stats[0];
console.log(` 管理员: ${stat.admin_count}`);
console.log(` 用户: ${stat.user_count}`);
console.log(` 商家: ${stat.merchant_count}`);
console.log(` 动物: ${stat.animal_count}`);
console.log(` 鲜花: ${stat.flower_count}`);
console.log(` 旅行计划: ${stat.travel_plan_count}`);
console.log(` 订单: ${stat.order_count}`);
console.log(` 支付记录: ${stat.payment_count}`);
console.log('\n🎉 后端数据库连接测试完成!');
console.log('✅ 所有功能正常');
return true;
} catch (error) {
console.error('❌ 后端数据库连接测试失败:', error.message);
console.error('🔍 错误详情:', error);
return false;
}
}
// 如果是直接运行此文件,则执行测试
if (require.main === module) {
testBackendConnection()
.then((success) => {
process.exit(success ? 0 : 1);
})
.catch(() => process.exit(1));
}
module.exports = { testBackendConnection };

View File

@@ -0,0 +1,204 @@
#!/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,
timezone: config.mysql.timezone
};
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, NOW() as current_time');
console.log(`✅ 查询测试成功: ${rows[0].result}, 服务器时间: ${rows[0].current_time}`);
// 检查数据库版本
console.log('🔍 检查数据库版本...');
const [versionRows] = await connection.execute('SELECT VERSION() as version');
console.log(`📊 MySQL版本: ${versionRows[0].version}`);
// 检查数据库字符集
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', 'flower_orders'
];
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);
}
}
// 检查管理员表数据
if (existingTables.includes('admins')) {
console.log('🔍 检查管理员数据...');
try {
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');
console.log('👥 管理员样例:');
admins.forEach(admin => {
console.log(` - ${admin.username} (${admin.role}, 状态: ${admin.status})`);
});
}
} catch (error) {
console.log('❌ 检查管理员数据失败:', error.message);
}
}
// 检查用户表数据
if (existingTables.includes('users')) {
console.log('🔍 检查用户数据...');
try {
const [userCount] = await connection.execute('SELECT COUNT(*) as count FROM users');
console.log(`📊 用户记录数: ${userCount[0].count}`);
if (userCount[0].count > 0) {
const [users] = await connection.execute('SELECT username, user_type, status FROM users LIMIT 5');
console.log('👤 用户样例:');
users.forEach(user => {
console.log(` - ${user.username} (${user.user_type || '未知'}, 状态: ${user.status})`);
});
}
} catch (error) {
console.log('❌ 检查用户数据失败:', error.message);
}
}
// 检查连接池配置
console.log('🔍 检查连接配置...');
console.log(`📈 连接池限制: ${config.mysql.connectionLimit || 10}`);
console.log(`🔤 字符集: ${config.mysql.charset}`);
console.log(`⏰ 时区: ${config.mysql.timezone}`);
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 };