重构动物认领页面和导航菜单,统一使用SVG图标并优化交互体验
This commit is contained in:
226
backend/scripts/check-database-structure.js
Normal file
226
backend/scripts/check-database-structure.js
Normal 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 };
|
||||
370
backend/scripts/create-database-schema.sql
Normal file
370
backend/scripts/create-database-schema.sql
Normal 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;
|
||||
219
backend/scripts/init-test-data-complete.js
Normal file
219
backend/scripts/init-test-data-complete.js
Normal 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 };
|
||||
124
backend/scripts/test-backend-connection.js
Normal file
124
backend/scripts/test-backend-connection.js
Normal 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 };
|
||||
204
backend/scripts/test-database-connection-fixed.js
Normal file
204
backend/scripts/test-database-connection-fixed.js
Normal 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 };
|
||||
Reference in New Issue
Block a user