refactor(docs): 简化README结构,更新技术栈和项目结构描述

This commit is contained in:
2025-09-21 17:20:04 +08:00
parent b8c9e5c959
commit 198d10f4f9
17 changed files with 3634 additions and 30 deletions

View File

@@ -1,8 +1,8 @@
# 数据库配置
DB_HOST=129.211.213.226
DB_PORT=9527
DB_USERNAME=root
DB_PASSWORD=aiotAiot123!
DB_HOST=nj-cdb-3pwh2kz1.sql.tencentcdb.com
DB_PORT=20784
DB_USERNAME=jiebanke
DB_PASSWORD=aiot741$12346
DB_NAME=niumall
# JWT配置

View File

@@ -0,0 +1,71 @@
/**
* 检查现有表结构
*/
require('dotenv').config();
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: false
}
);
async function checkTables() {
try {
console.log('🔍 检查现有表结构...\n');
// 获取所有表
const [tables] = await sequelize.query("SHOW TABLES");
console.log('📋 现有表列表:');
tables.forEach(table => {
const tableName = table[`Tables_in_${process.env.DB_NAME || 'niumall'}`];
console.log(` - ${tableName}`);
});
// 检查suppliers表结构
console.log('\n🏭 suppliers表结构:');
try {
const [columns] = await sequelize.query("DESCRIBE suppliers");
columns.forEach(col => {
console.log(` - ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : ''} ${col.Key ? `(${col.Key})` : ''}`);
});
} catch (error) {
console.log(' 表不存在或查询失败');
}
// 检查users表结构
console.log('\n👤 users表结构:');
try {
const [columns] = await sequelize.query("DESCRIBE users");
columns.forEach(col => {
console.log(` - ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : ''} ${col.Key ? `(${col.Key})` : ''}`);
});
} catch (error) {
console.log(' 表不存在或查询失败');
}
// 检查orders表结构
console.log('\n📋 orders表结构:');
try {
const [columns] = await sequelize.query("DESCRIBE orders");
columns.forEach(col => {
console.log(` - ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : ''} ${col.Key ? `(${col.Key})` : ''}`);
});
} catch (error) {
console.log(' 表不存在或查询失败');
}
} catch (error) {
console.error('❌ 检查失败:', error.message);
} finally {
await sequelize.close();
}
}
checkTables();

View File

@@ -5,7 +5,7 @@ module.exports = {
development: {
username: process.env.DB_USERNAME || 'jiebanke',
password: process.env.DB_PASSWORD || 'aiot741$12346',
database: process.env.DB_NAME || 'jbkdata',
database: process.env.DB_NAME || 'niumall',
host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
port: process.env.DB_PORT || 20784,
dialect: 'mysql',

View File

@@ -0,0 +1,100 @@
/**
* 创建管理员用户
*/
require('dotenv').config();
const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid');
const { Sequelize, DataTypes } = require('sequelize');
// 创建数据库连接
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: false
}
);
async function createAdminUser() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
// 检查是否已存在管理员用户
const [existingUsers] = await sequelize.query(
"SELECT * FROM users WHERE username = 'admin' OR user_type = 'admin'"
);
if (existingUsers.length > 0) {
console.log('⚠️ 管理员用户已存在:');
existingUsers.forEach(user => {
console.log(`- ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}, 类型: ${user.user_type}`);
});
// 更新现有管理员用户的密码
const hashedPassword = await bcrypt.hash('admin123', 10);
await sequelize.query(
"UPDATE users SET username = 'admin', password_hash = ?, user_type = 'admin', status = 'active' WHERE id = ?",
{
replacements: [hashedPassword, existingUsers[0].id]
}
);
console.log('✅ 已更新现有用户为管理员,用户名: admin, 密码: admin123');
} else {
// 创建新的管理员用户
const hashedPassword = await bcrypt.hash('admin123', 10);
const uuid = uuidv4();
await sequelize.query(
`INSERT INTO users (
uuid, username, password_hash, openid, nickname, user_type, status,
registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
{
replacements: [
uuid,
'admin',
hashedPassword,
'admin_' + uuid.substring(0, 8), // 为管理员生成一个唯一的openid
'系统管理员',
'admin',
'active',
'admin_create',
0
]
}
);
console.log('✅ 管理员用户创建成功!');
}
console.log('\n📋 管理员登录信息:');
console.log('用户名: admin');
console.log('密码: admin123');
console.log('登录地址: http://localhost:3000/');
// 验证创建结果
const [adminUsers] = await sequelize.query(
"SELECT id, username, nickname, user_type, status FROM users WHERE user_type = 'admin'"
);
console.log('\n当前管理员用户列表:');
adminUsers.forEach(user => {
console.log(`- ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}, 状态: ${user.status}`);
});
} catch (error) {
console.error('❌ 创建管理员用户失败:', error);
} finally {
await sequelize.close();
console.log('\n数据库连接已关闭');
}
}
// 运行创建脚本
createAdminUser();

View File

@@ -0,0 +1,676 @@
/**
* 数据库结构完整性检查和自动修复工具
*
* 功能:
* 1. 比对现有结构与设计文档规范
* 2. 识别缺失的表、字段、约束或索引
* 3. 生成并执行必要的DDL语句补全结构
* 4. 验证数据完整性约束
* 5. 对缺失的基础数据执行初始化插入
*
* @author NiuMall Team
* @version 1.0.0
*/
require('dotenv').config();
const { Sequelize, DataTypes, QueryTypes } = require('sequelize');
const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid');
// 创建数据库连接
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: (msg) => console.log(`[SQL] ${msg}`)
}
);
// 操作日志记录
const operationLogs = [];
function logOperation(operation, status, details = '') {
const log = {
timestamp: new Date().toISOString(),
operation,
status,
details
};
operationLogs.push(log);
console.log(`[${status}] ${operation}: ${details}`);
}
/**
* 数据库表结构定义(基于设计文档)
*/
const expectedTables = {
// 用户基础表
users: {
columns: {
id: 'BIGINT PRIMARY KEY AUTO_INCREMENT',
uuid: 'VARCHAR(36) UNIQUE',
username: 'VARCHAR(50) UNIQUE',
password_hash: 'VARCHAR(255)',
openid: 'VARCHAR(64)',
unionid: 'VARCHAR(64)',
nickname: 'VARCHAR(50) NOT NULL',
real_name: 'VARCHAR(50)',
avatar: 'VARCHAR(255)',
gender: "ENUM('male','female','other')",
birthday: 'DATETIME',
phone: 'VARCHAR(20) UNIQUE',
email: 'VARCHAR(100) UNIQUE',
user_type: "ENUM('buyer','trader','supplier','driver','staff','admin') DEFAULT 'buyer'",
company_name: 'VARCHAR(100)',
company_address: 'VARCHAR(200)',
business_license: 'VARCHAR(255)',
id_card: 'VARCHAR(18)',
status: "ENUM('active','inactive','suspended','pending_approval') DEFAULT 'pending_approval'",
last_login_at: 'DATETIME',
login_count: 'INT DEFAULT 0',
registration_source: "ENUM('miniprogram','web','admin_create') DEFAULT 'miniprogram'",
approval_notes: 'TEXT',
created_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
updated_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
},
indexes: [
'INDEX idx_uuid (uuid)',
'INDEX idx_username (username)',
'INDEX idx_phone (phone)',
'INDEX idx_email (email)',
'INDEX idx_openid (openid)',
'INDEX idx_user_type (user_type)',
'INDEX idx_status (status)'
]
},
// 用户详情表
user_profiles: {
columns: {
id: 'BIGINT PRIMARY KEY AUTO_INCREMENT',
user_id: 'BIGINT NOT NULL',
company_name: 'VARCHAR(200)',
business_license: 'VARCHAR(500)',
license_number: 'VARCHAR(100)',
legal_person: 'VARCHAR(100)',
contact_address: 'TEXT',
emergency_contact: 'VARCHAR(100)',
emergency_phone: 'VARCHAR(20)',
bank_account: 'VARCHAR(50)',
bank_name: 'VARCHAR(200)',
tax_number: 'VARCHAR(50)',
qualification_docs: 'JSON',
created_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
updated_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
},
foreignKeys: [
'FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE'
],
indexes: [
'UNIQUE KEY uk_user_id (user_id)',
'INDEX idx_company_name (company_name)',
'INDEX idx_license_number (license_number)'
]
},
// 订单主表
orders: {
columns: {
id: 'BIGINT PRIMARY KEY AUTO_INCREMENT',
order_no: 'VARCHAR(50) UNIQUE NOT NULL',
client_id: 'BIGINT NOT NULL',
trader_id: 'BIGINT',
supplier_id: 'BIGINT',
cattle_type: 'VARCHAR(50) NOT NULL',
quantity: 'INT NOT NULL',
weight_range: 'VARCHAR(50)',
estimated_weight: 'DECIMAL(8,2)',
actual_weight: 'DECIMAL(8,2)',
unit_price: 'DECIMAL(10,2) NOT NULL',
price_type: "ENUM('per_kg','per_head') DEFAULT 'per_kg'",
total_amount: 'DECIMAL(12,2) NOT NULL',
prepaid_amount: 'DECIMAL(12,2) DEFAULT 0',
final_amount: 'DECIMAL(12,2)',
pickup_address: 'TEXT',
delivery_address: 'TEXT NOT NULL',
pickup_time: 'DATETIME',
delivery_time: 'DATETIME',
actual_delivery_time: 'DATETIME',
status: "ENUM('draft','pending','confirmed','preparing','loading','transporting','arrived','inspecting','accepted','completed','cancelled') DEFAULT 'draft'",
cancel_reason: 'TEXT',
special_requirements: 'TEXT',
quality_standards: 'JSON',
created_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
updated_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
confirmed_at: 'TIMESTAMP NULL',
completed_at: 'TIMESTAMP NULL'
},
foreignKeys: [
'FOREIGN KEY (client_id) REFERENCES users(id)',
'FOREIGN KEY (trader_id) REFERENCES users(id)',
'FOREIGN KEY (supplier_id) REFERENCES users(id)'
],
indexes: [
'INDEX idx_order_no (order_no)',
'INDEX idx_client_id (client_id)',
'INDEX idx_trader_id (trader_id)',
'INDEX idx_supplier_id (supplier_id)',
'INDEX idx_status (status)',
'INDEX idx_created_at (created_at)',
'INDEX idx_delivery_time (delivery_time)',
'INDEX idx_cattle_type (cattle_type)'
]
},
// 供应商表
suppliers: {
columns: {
id: 'BIGINT PRIMARY KEY AUTO_INCREMENT',
name: 'VARCHAR(100) NOT NULL',
code: 'VARCHAR(20) UNIQUE NOT NULL',
contact: 'VARCHAR(50) NOT NULL',
phone: 'VARCHAR(20) UNIQUE NOT NULL',
email: 'VARCHAR(100)',
address: 'VARCHAR(200) NOT NULL',
region: 'VARCHAR(20) NOT NULL',
business_license: 'VARCHAR(255)',
animal_quarantine_certificate: 'VARCHAR(255)',
qualification_level: "ENUM('A','B','C','D') DEFAULT 'C'",
certifications: 'JSON',
cattle_types: 'JSON',
capacity: 'INT DEFAULT 0',
rating: 'DECIMAL(3,2) DEFAULT 0.00',
cooperation_start_date: 'DATE',
status: "ENUM('active','inactive','suspended','blacklisted') DEFAULT 'active'",
bank_account: 'VARCHAR(50)',
bank_name: 'VARCHAR(100)',
tax_number: 'VARCHAR(30)',
notes: 'TEXT',
created_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
updated_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
},
indexes: [
'INDEX idx_code (code)',
'INDEX idx_phone (phone)',
'INDEX idx_region (region)',
'INDEX idx_qualification_level (qualification_level)',
'INDEX idx_status (status)',
'INDEX idx_rating (rating)'
]
},
// 运输任务表
transport_tasks: {
columns: {
id: 'BIGINT PRIMARY KEY AUTO_INCREMENT',
task_no: 'VARCHAR(50) UNIQUE NOT NULL',
order_id: 'BIGINT NOT NULL',
driver_id: 'BIGINT NOT NULL',
vehicle_no: 'VARCHAR(20) NOT NULL',
vehicle_type: 'VARCHAR(50)',
vehicle_capacity: 'DECIMAL(8,2)',
driver_license: 'VARCHAR(50)',
start_location: 'VARCHAR(200)',
end_location: 'VARCHAR(200)',
start_latitude: 'DECIMAL(10,6)',
start_longitude: 'DECIMAL(10,6)',
end_latitude: 'DECIMAL(10,6)',
end_longitude: 'DECIMAL(10,6)',
planned_distance: 'DECIMAL(8,2)',
actual_distance: 'DECIMAL(8,2)',
planned_start_time: 'DATETIME',
actual_start_time: 'DATETIME',
planned_end_time: 'DATETIME',
actual_end_time: 'DATETIME',
estimated_arrival_time: 'DATETIME',
status: "ENUM('assigned','preparing','loading','started','transporting','arrived','unloading','completed','cancelled') DEFAULT 'assigned'",
transport_fee: 'DECIMAL(10,2)',
fuel_cost: 'DECIMAL(10,2)',
toll_cost: 'DECIMAL(10,2)',
other_cost: 'DECIMAL(10,2)',
notes: 'TEXT',
cancel_reason: 'TEXT',
created_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
updated_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
},
foreignKeys: [
'FOREIGN KEY (order_id) REFERENCES orders(id)',
'FOREIGN KEY (driver_id) REFERENCES users(id)'
],
indexes: [
'INDEX idx_task_no (task_no)',
'INDEX idx_order_id (order_id)',
'INDEX idx_driver_id (driver_id)',
'INDEX idx_vehicle_no (vehicle_no)',
'INDEX idx_status (status)',
'INDEX idx_planned_start_time (planned_start_time)',
'INDEX idx_created_at (created_at)'
]
},
// 支付记录表
payments: {
columns: {
id: 'BIGINT PRIMARY KEY AUTO_INCREMENT',
payment_no: 'VARCHAR(50) UNIQUE NOT NULL',
order_id: 'BIGINT NOT NULL',
user_id: 'BIGINT NOT NULL',
amount: 'DECIMAL(12,2) NOT NULL',
paid_amount: 'DECIMAL(12,2)',
currency: 'VARCHAR(10) DEFAULT "CNY"',
payment_method: "ENUM('bank_transfer','alipay','wechat_pay','cash','check','other') NOT NULL",
payment_channel: 'VARCHAR(100)',
third_party_order_no: 'VARCHAR(100)',
third_party_transaction_id: 'VARCHAR(100)',
payer_bank_account: 'VARCHAR(50)',
payer_bank_name: 'VARCHAR(200)',
payee_bank_account: 'VARCHAR(50)',
payee_bank_name: 'VARCHAR(200)',
status: "ENUM('pending','processing','success','failed','cancelled','refunded') DEFAULT 'pending'",
failure_reason: 'TEXT',
payment_time: 'DATETIME',
confirmed_time: 'DATETIME',
notes: 'TEXT',
receipt_url: 'VARCHAR(500)',
created_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
updated_at: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
},
foreignKeys: [
'FOREIGN KEY (order_id) REFERENCES orders(id)',
'FOREIGN KEY (user_id) REFERENCES users(id)'
],
indexes: [
'INDEX idx_payment_no (payment_no)',
'INDEX idx_order_id (order_id)',
'INDEX idx_user_id (user_id)',
'INDEX idx_status (status)',
'INDEX idx_payment_method (payment_method)',
'INDEX idx_payment_time (payment_time)',
'INDEX idx_third_party_order_no (third_party_order_no)'
]
}
};
/**
* 检查表是否存在
*/
async function checkTableExists(tableName) {
try {
const [results] = await sequelize.query(
`SELECT COUNT(*) as count FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`,
{
replacements: [process.env.DB_NAME || 'niumall', tableName],
type: QueryTypes.SELECT
}
);
return results[0].count > 0;
} catch (error) {
logOperation(`检查表 ${tableName}`, 'ERROR', error.message);
return false;
}
}
/**
* 获取表的列信息
*/
async function getTableColumns(tableName) {
try {
const [results] = await sequelize.query(
`SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_KEY, EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?
ORDER BY ORDINAL_POSITION`,
{
replacements: [process.env.DB_NAME || 'niumall', tableName],
type: QueryTypes.SELECT
}
);
return results;
} catch (error) {
logOperation(`获取表 ${tableName} 列信息`, 'ERROR', error.message);
return [];
}
}
/**
* 获取表的索引信息
*/
async function getTableIndexes(tableName) {
try {
const [results] = await sequelize.query(
`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`,
{
replacements: [process.env.DB_NAME || 'niumall', tableName],
type: QueryTypes.SELECT
}
);
return results;
} catch (error) {
logOperation(`获取表 ${tableName} 索引信息`, 'ERROR', error.message);
return [];
}
}
/**
* 创建缺失的表
*/
async function createMissingTable(tableName, tableDefinition) {
try {
logOperation(`创建表 ${tableName}`, 'INFO', '开始创建表');
// 构建CREATE TABLE语句
const columns = Object.entries(tableDefinition.columns)
.map(([name, definition]) => `${name} ${definition}`)
.join(',\n ');
const foreignKeys = tableDefinition.foreignKeys || [];
const fkConstraints = foreignKeys.length > 0 ? ',\n ' + foreignKeys.join(',\n ') : '';
const createTableSQL = `
CREATE TABLE ${tableName} (
${columns}${fkConstraints}
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='${tableName}表'
`;
await sequelize.query(createTableSQL);
logOperation(`创建表 ${tableName}`, 'SUCCESS', '表创建成功');
// 创建索引
if (tableDefinition.indexes) {
for (const indexSQL of tableDefinition.indexes) {
try {
await sequelize.query(`ALTER TABLE ${tableName} ADD ${indexSQL}`);
logOperation(`创建索引`, 'SUCCESS', `${tableName}: ${indexSQL}`);
} catch (error) {
logOperation(`创建索引`, 'WARNING', `${tableName}: ${indexSQL} - ${error.message}`);
}
}
}
} catch (error) {
logOperation(`创建表 ${tableName}`, 'ERROR', error.message);
throw error;
}
}
/**
* 添加缺失的列
*/
async function addMissingColumn(tableName, columnName, columnDefinition) {
try {
const alterSQL = `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDefinition}`;
await sequelize.query(alterSQL);
logOperation(`添加列`, 'SUCCESS', `${tableName}.${columnName}`);
} catch (error) {
if (error.message.includes('Duplicate column name')) {
logOperation(`添加列`, 'WARNING', `${tableName}.${columnName} 已存在`);
} else {
logOperation(`添加列`, 'ERROR', `${tableName}.${columnName} - ${error.message}`);
throw error;
}
}
}
/**
* 创建缺失的索引
*/
async function createMissingIndex(tableName, indexSQL) {
try {
await sequelize.query(`ALTER TABLE ${tableName} ADD ${indexSQL}`);
logOperation(`创建索引`, 'SUCCESS', `${tableName}: ${indexSQL}`);
} catch (error) {
if (error.message.includes('Duplicate key name') || error.message.includes('already exists')) {
logOperation(`创建索引`, 'WARNING', `${tableName}: ${indexSQL} 已存在`);
} else {
logOperation(`创建索引`, 'ERROR', `${tableName}: ${indexSQL} - ${error.message}`);
}
}
}
/**
* 初始化基础数据
*/
async function initializeBaseData() {
try {
logOperation('初始化基础数据', 'INFO', '开始检查和创建基础数据');
// 检查是否存在管理员用户
const [adminUsers] = await sequelize.query(
"SELECT * FROM users WHERE user_type = 'admin' LIMIT 1"
);
if (adminUsers.length === 0) {
// 创建默认管理员用户
const hashedPassword = await bcrypt.hash('admin123', 10);
const uuid = uuidv4();
await sequelize.query(
`INSERT INTO users (
uuid, username, password_hash, openid, nickname, user_type, status,
registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
{
replacements: [
uuid,
'admin',
hashedPassword,
'admin_' + uuid.substring(0, 8),
'系统管理员',
'admin',
'active',
'admin_create',
0
]
}
);
logOperation('创建管理员用户', 'SUCCESS', '用户名: admin, 密码: admin123');
} else {
logOperation('检查管理员用户', 'INFO', '管理员用户已存在');
}
// 检查系统配置表数据
const configTableExists = await checkTableExists('system_configs');
if (configTableExists) {
const [configs] = await sequelize.query("SELECT COUNT(*) as count FROM system_configs");
if (configs[0].count === 0) {
// 插入默认系统配置
const defaultConfigs = [
['system.name', '活牛采购智能数字化系统', 'string', 'system', '系统名称'],
['system.version', '1.0.0', 'string', 'system', '系统版本'],
['order.auto_confirm_hours', '24', 'number', 'order', '订单自动确认时间(小时)'],
['payment.timeout_minutes', '30', 'number', 'payment', '支付超时时间(分钟)'],
['transport.tracking_interval', '300', 'number', 'transport', '位置跟踪间隔(秒)']
];
for (const [key, value, type, category, description] of defaultConfigs) {
await sequelize.query(
`INSERT INTO system_configs (config_key, config_value, config_type, category, description, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, NOW(), NOW())`,
{ replacements: [key, value, type, category, description] }
);
}
logOperation('初始化系统配置', 'SUCCESS', `插入 ${defaultConfigs.length} 条配置记录`);
}
}
} catch (error) {
logOperation('初始化基础数据', 'ERROR', error.message);
throw error;
}
}
/**
* 验证数据完整性
*/
async function validateDataIntegrity() {
try {
logOperation('验证数据完整性', 'INFO', '开始数据完整性检查');
const issues = [];
// 检查外键约束
const [orphanedOrders] = await sequelize.query(`
SELECT o.id, o.order_no, o.client_id
FROM orders o
LEFT JOIN users u ON o.client_id = u.id
WHERE u.id IS NULL
LIMIT 10
`);
if (orphanedOrders.length > 0) {
issues.push(`发现 ${orphanedOrders.length} 个订单的客户ID无效`);
}
// 检查重复数据
const [duplicateUsers] = await sequelize.query(`
SELECT phone, COUNT(*) as count
FROM users
WHERE phone IS NOT NULL
GROUP BY phone
HAVING COUNT(*) > 1
`);
if (duplicateUsers.length > 0) {
issues.push(`发现 ${duplicateUsers.length} 个重复的手机号`);
}
// 检查必填字段空值
const [emptyNicknames] = await sequelize.query(`
SELECT COUNT(*) as count
FROM users
WHERE nickname IS NULL OR nickname = ''
`);
if (emptyNicknames[0].count > 0) {
issues.push(`发现 ${emptyNicknames[0].count} 个用户昵称为空`);
}
if (issues.length > 0) {
logOperation('数据完整性检查', 'WARNING', `发现 ${issues.length} 个问题: ${issues.join('; ')}`);
} else {
logOperation('数据完整性检查', 'SUCCESS', '未发现数据完整性问题');
}
return issues;
} catch (error) {
logOperation('验证数据完整性', 'ERROR', error.message);
return [`数据完整性检查失败: ${error.message}`];
}
}
/**
* 主要的数据库检查和修复函数
*/
async function checkAndRepairDatabase() {
try {
console.log('\n🔍 ===== 数据库结构完整性检查开始 =====\n');
// 1. 测试数据库连接
logOperation('数据库连接测试', 'INFO', '正在连接数据库...');
await sequelize.authenticate();
logOperation('数据库连接测试', 'SUCCESS', '数据库连接成功');
// 2. 检查和创建缺失的表
logOperation('表结构检查', 'INFO', '开始检查表结构...');
for (const [tableName, tableDefinition] of Object.entries(expectedTables)) {
const exists = await checkTableExists(tableName);
if (!exists) {
logOperation(`表检查`, 'WARNING', `${tableName} 不存在,准备创建`);
await createMissingTable(tableName, tableDefinition);
} else {
logOperation(`表检查`, 'SUCCESS', `${tableName} 存在`);
// 检查列结构
const existingColumns = await getTableColumns(tableName);
const existingColumnNames = existingColumns.map(col => col.COLUMN_NAME);
for (const [columnName, columnDefinition] of Object.entries(tableDefinition.columns)) {
if (!existingColumnNames.includes(columnName)) {
logOperation(`列检查`, 'WARNING', `${tableName} 缺少列 ${columnName}`);
await addMissingColumn(tableName, columnName, columnDefinition);
}
}
// 检查索引
if (tableDefinition.indexes) {
for (const indexSQL of tableDefinition.indexes) {
await createMissingIndex(tableName, indexSQL);
}
}
}
}
// 3. 初始化基础数据
await initializeBaseData();
// 4. 验证数据完整性
const integrityIssues = await validateDataIntegrity();
// 5. 生成检查报告
console.log('\n📊 ===== 数据库检查报告 =====\n');
const successCount = operationLogs.filter(log => log.status === 'SUCCESS').length;
const warningCount = operationLogs.filter(log => log.status === 'WARNING').length;
const errorCount = operationLogs.filter(log => log.status === 'ERROR').length;
console.log(`✅ 成功操作: ${successCount}`);
console.log(`⚠️ 警告信息: ${warningCount}`);
console.log(`❌ 错误信息: ${errorCount}`);
if (integrityIssues.length > 0) {
console.log(`\n🔍 数据完整性问题: ${integrityIssues.length}`);
integrityIssues.forEach((issue, index) => {
console.log(` ${index + 1}. ${issue}`);
});
}
// 6. 输出详细日志
console.log('\n📝 ===== 详细操作日志 =====\n');
operationLogs.forEach(log => {
const icon = log.status === 'SUCCESS' ? '✅' :
log.status === 'WARNING' ? '⚠️' :
log.status === 'ERROR' ? '❌' : '';
console.log(`${icon} [${log.timestamp}] ${log.operation}: ${log.details}`);
});
console.log('\n🎉 ===== 数据库结构检查完成 =====\n');
// 7. 输出连接信息
console.log('📋 系统信息:');
console.log(`🌐 后端服务: http://localhost:4330`);
console.log(`🎨 管理后台: http://localhost:3000`);
console.log(`👤 管理员账户: admin / admin123`);
console.log(`📚 API文档: http://localhost:4330/api-docs`);
} catch (error) {
console.error('\n❌ 数据库检查过程中发生严重错误:', error);
logOperation('数据库检查', 'ERROR', error.message);
} finally {
await sequelize.close();
console.log('\n🔌 数据库连接已关闭');
}
}
// 运行检查和修复
if (require.main === module) {
checkAndRepairDatabase().catch(console.error);
}
module.exports = {
checkAndRepairDatabase,
expectedTables,
operationLogs
};

92
backend/fix_user_table.js Normal file
View File

@@ -0,0 +1,92 @@
/**
* 修复用户表结构 - 添加缺失的字段
*/
require('dotenv').config();
const { Sequelize, DataTypes } = require('sequelize');
// 创建数据库连接
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: console.log
}
);
async function checkColumnExists(tableName, columnName) {
try {
const [results] = await sequelize.query(
`SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${process.env.DB_NAME || 'niumall'}' AND TABLE_NAME = '${tableName}' AND COLUMN_NAME = '${columnName}'`
);
return results.length > 0;
} catch (error) {
return false;
}
}
async function fixUserTable() {
try {
console.log('连接数据库...');
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
console.log('\n开始修复用户表结构...');
// 定义需要添加的字段
const fieldsToAdd = [
{ name: 'username', sql: 'ALTER TABLE users ADD COLUMN username VARCHAR(50) UNIQUE AFTER id' },
{ name: 'password_hash', sql: 'ALTER TABLE users ADD COLUMN password_hash VARCHAR(255) AFTER username' },
{ name: 'user_type', sql: "ALTER TABLE users ADD COLUMN user_type ENUM('buyer', 'trader', 'supplier', 'driver', 'staff', 'admin') DEFAULT 'buyer' AFTER password_hash" },
{ name: 'real_name', sql: 'ALTER TABLE users ADD COLUMN real_name VARCHAR(50) AFTER nickname' },
{ name: 'unionid', sql: 'ALTER TABLE users ADD COLUMN unionid VARCHAR(64) AFTER openid' },
{ name: 'company_name', sql: 'ALTER TABLE users ADD COLUMN company_name VARCHAR(100) AFTER user_type' },
{ name: 'company_address', sql: 'ALTER TABLE users ADD COLUMN company_address VARCHAR(200) AFTER company_name' },
{ name: 'business_license', sql: 'ALTER TABLE users ADD COLUMN business_license VARCHAR(255) AFTER company_address' },
{ name: 'id_card', sql: 'ALTER TABLE users ADD COLUMN id_card VARCHAR(18) AFTER business_license' },
{ name: 'status', sql: "ALTER TABLE users ADD COLUMN status ENUM('active', 'inactive', 'suspended', 'pending_approval') DEFAULT 'pending_approval' AFTER id_card" },
{ name: 'last_login_at', sql: 'ALTER TABLE users ADD COLUMN last_login_at DATETIME AFTER status' },
{ name: 'login_count', sql: 'ALTER TABLE users ADD COLUMN login_count INT DEFAULT 0 AFTER last_login_at' },
{ name: 'registration_source', sql: "ALTER TABLE users ADD COLUMN registration_source ENUM('miniprogram', 'web', 'admin_create') DEFAULT 'miniprogram' AFTER login_count" },
{ name: 'approval_notes', sql: 'ALTER TABLE users ADD COLUMN approval_notes TEXT AFTER registration_source' }
];
for (const field of fieldsToAdd) {
try {
const exists = await checkColumnExists('users', field.name);
if (exists) {
console.log(`⚠️ 字段 ${field.name} 已存在,跳过`);
continue;
}
console.log(`添加字段: ${field.name}`);
await sequelize.query(field.sql);
console.log('✅ 成功');
} catch (error) {
console.log(`❌ 添加字段 ${field.name} 失败:`, error.message);
}
}
console.log('\n✅ 用户表结构修复完成!');
// 检查表结构
console.log('\n检查修复后的表结构...');
const [results] = await sequelize.query('SHOW FULL COLUMNS FROM users');
console.log('用户表字段列表:');
results.forEach(column => {
console.log(`- ${column.Field}: ${column.Type} ${column.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${column.Key ? `(${column.Key})` : ''}`);
});
} catch (error) {
console.error('❌ 修复失败:', error);
} finally {
await sequelize.close();
console.log('\n数据库连接已关闭');
}
}
// 运行修复脚本
fixUserTable();

View File

@@ -0,0 +1,630 @@
/**
* 数据库初始化脚本 - 创建表和测试数据
*
* 功能:
* 1. 创建所有必要的数据表
* 2. 插入测试数据
* 3. 验证数据完整性
*
* @author NiuMall Team
* @version 1.0.0
*/
require('dotenv').config();
const { Sequelize, QueryTypes } = require('sequelize');
const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid');
// 创建数据库连接
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: (msg) => console.log(`[SQL] ${msg}`)
}
);
/**
* 创建所有数据表的SQL语句
*/
const createTableSQLs = {
// 用户表
users: `
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
uuid VARCHAR(36) UNIQUE COMMENT '用户唯一标识符',
username VARCHAR(50) UNIQUE COMMENT '用户名',
password_hash VARCHAR(255) COMMENT '密码哈希值',
openid VARCHAR(64) COMMENT '微信小程序OpenID',
unionid VARCHAR(64) COMMENT '微信UnionID',
nickname VARCHAR(50) NOT NULL COMMENT '用户昵称',
real_name VARCHAR(50) COMMENT '真实姓名',
avatar VARCHAR(255) COMMENT '头像URL',
gender ENUM('male', 'female', 'other') COMMENT '性别',
birthday DATETIME COMMENT '生日',
phone VARCHAR(20) UNIQUE COMMENT '手机号码',
email VARCHAR(100) UNIQUE COMMENT '邮箱地址',
user_type ENUM('buyer', 'trader', 'supplier', 'driver', 'staff', 'admin') DEFAULT 'buyer' COMMENT '用户类型',
company_name VARCHAR(100) COMMENT '公司名称',
company_address VARCHAR(200) COMMENT '公司地址',
business_license VARCHAR(255) COMMENT '营业执照文件路径',
id_card VARCHAR(18) COMMENT '身份证号',
status ENUM('active', 'inactive', 'suspended', 'pending_approval') DEFAULT 'pending_approval' COMMENT '用户状态',
last_login_at DATETIME COMMENT '最后登录时间',
login_count INT DEFAULT 0 COMMENT '登录次数',
registration_source ENUM('miniprogram', 'web', 'admin_create') DEFAULT 'miniprogram' COMMENT '注册来源',
approval_notes TEXT COMMENT '审核备注',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_uuid (uuid),
INDEX idx_username (username),
INDEX idx_phone (phone),
INDEX idx_email (email),
INDEX idx_openid (openid),
INDEX idx_user_type (user_type),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户基础表'
`,
// 供应商表
suppliers: `
CREATE TABLE IF NOT EXISTS suppliers (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '供应商ID',
name VARCHAR(100) NOT NULL COMMENT '供应商名称',
code VARCHAR(20) UNIQUE NOT NULL COMMENT '供应商编码',
contact VARCHAR(50) NOT NULL COMMENT '联系人姓名',
phone VARCHAR(20) UNIQUE NOT NULL COMMENT '联系电话',
email VARCHAR(100) COMMENT '邮箱地址',
address VARCHAR(200) NOT NULL COMMENT '详细地址',
region VARCHAR(20) NOT NULL COMMENT '所属区域',
business_license VARCHAR(255) COMMENT '营业执照文件路径',
animal_quarantine_certificate VARCHAR(255) COMMENT '动物防疫条件合格证文件路径',
qualification_level ENUM('A', 'B', 'C', 'D') DEFAULT 'C' COMMENT '资质等级',
certifications JSON COMMENT '其他认证证书信息',
cattle_types JSON COMMENT '可供应的牛只品种',
capacity INT DEFAULT 0 COMMENT '月供应能力(头数)',
rating DECIMAL(3,2) DEFAULT 0.00 COMMENT '综合评分',
cooperation_start_date DATE COMMENT '合作开始日期',
status ENUM('active', 'inactive', 'suspended', 'blacklisted') DEFAULT 'active' COMMENT '供应商状态',
bank_account VARCHAR(50) COMMENT '银行账号',
bank_name VARCHAR(100) COMMENT '开户银行',
tax_number VARCHAR(30) COMMENT '税务登记号',
notes TEXT COMMENT '备注信息',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_code (code),
INDEX idx_phone (phone),
INDEX idx_region (region),
INDEX idx_qualification_level (qualification_level),
INDEX idx_status (status),
INDEX idx_rating (rating)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='供应商表'
`,
// 订单表
orders: `
CREATE TABLE IF NOT EXISTS orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
order_no VARCHAR(50) UNIQUE NOT NULL COMMENT '订单号',
client_id BIGINT NOT NULL COMMENT '采购人ID',
trader_id BIGINT COMMENT '贸易商ID',
supplier_id BIGINT COMMENT '供应商ID',
cattle_type VARCHAR(50) NOT NULL COMMENT '牛只品种',
quantity INT NOT NULL COMMENT '数量(头)',
weight_range VARCHAR(50) COMMENT '重量范围',
estimated_weight DECIMAL(8,2) COMMENT '预估总重量kg',
actual_weight DECIMAL(8,2) COMMENT '实际总重量kg',
unit_price DECIMAL(10,2) NOT NULL COMMENT '单价(元/kg或元/头)',
price_type ENUM('per_kg', 'per_head') DEFAULT 'per_kg' COMMENT '计价方式',
total_amount DECIMAL(12,2) NOT NULL COMMENT '订单总金额',
prepaid_amount DECIMAL(12,2) DEFAULT 0 COMMENT '预付金额',
final_amount DECIMAL(12,2) COMMENT '最终结算金额',
pickup_address TEXT COMMENT '取货地址',
delivery_address TEXT NOT NULL COMMENT '交货地址',
pickup_time DATETIME COMMENT '取货时间',
delivery_time DATETIME COMMENT '要求交货时间',
actual_delivery_time DATETIME COMMENT '实际交货时间',
status ENUM('draft', 'pending', 'confirmed', 'preparing', 'loading', 'transporting', 'arrived', 'inspecting', 'accepted', 'completed', 'cancelled') DEFAULT 'draft' COMMENT '订单状态',
cancel_reason TEXT COMMENT '取消原因',
special_requirements TEXT COMMENT '特殊要求',
quality_standards JSON COMMENT '质量标准JSON',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
confirmed_at TIMESTAMP NULL COMMENT '确认时间',
completed_at TIMESTAMP NULL COMMENT '完成时间',
FOREIGN KEY (client_id) REFERENCES users(id),
FOREIGN KEY (trader_id) REFERENCES users(id),
FOREIGN KEY (supplier_id) REFERENCES suppliers(id),
INDEX idx_order_no (order_no),
INDEX idx_client_id (client_id),
INDEX idx_trader_id (trader_id),
INDEX idx_supplier_id (supplier_id),
INDEX idx_status (status),
INDEX idx_created_at (created_at),
INDEX idx_delivery_time (delivery_time),
INDEX idx_cattle_type (cattle_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单主表'
`,
// 支付记录表
payments: `
CREATE TABLE IF NOT EXISTS payments (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '支付ID',
payment_no VARCHAR(50) UNIQUE NOT NULL COMMENT '支付单号',
order_id BIGINT NOT NULL COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '付款用户ID',
amount DECIMAL(12,2) NOT NULL COMMENT '支付金额',
paid_amount DECIMAL(12,2) COMMENT '实际支付金额',
currency VARCHAR(10) DEFAULT 'CNY' COMMENT '货币类型',
payment_method ENUM('bank_transfer', 'alipay', 'wechat_pay', 'cash', 'check', 'other') NOT NULL COMMENT '支付方式',
payment_channel VARCHAR(100) COMMENT '支付渠道',
third_party_order_no VARCHAR(100) COMMENT '第三方订单号',
third_party_transaction_id VARCHAR(100) COMMENT '第三方交易ID',
payer_bank_account VARCHAR(50) COMMENT '付款账户',
payer_bank_name VARCHAR(200) COMMENT '付款银行',
payee_bank_account VARCHAR(50) COMMENT '收款账户',
payee_bank_name VARCHAR(200) COMMENT '收款银行',
status ENUM('pending', 'processing', 'success', 'failed', 'cancelled', 'refunded') DEFAULT 'pending' COMMENT '支付状态',
failure_reason TEXT COMMENT '失败原因',
payment_time DATETIME COMMENT '支付时间',
confirmed_time DATETIME COMMENT '确认时间',
notes TEXT COMMENT '支付备注',
receipt_url VARCHAR(500) COMMENT '支付凭证URL',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX idx_payment_no (payment_no),
INDEX idx_order_id (order_id),
INDEX idx_user_id (user_id),
INDEX idx_status (status),
INDEX idx_payment_method (payment_method),
INDEX idx_payment_time (payment_time),
INDEX idx_third_party_order_no (third_party_order_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付记录表'
`,
// 运输任务表
transport_tasks: `
CREATE TABLE IF NOT EXISTS transport_tasks (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '任务ID',
task_no VARCHAR(50) UNIQUE NOT NULL COMMENT '任务编号',
order_id BIGINT NOT NULL COMMENT '订单ID',
driver_id BIGINT NOT NULL COMMENT '司机ID',
vehicle_no VARCHAR(20) NOT NULL COMMENT '车牌号',
vehicle_type VARCHAR(50) COMMENT '车辆类型',
vehicle_capacity DECIMAL(8,2) COMMENT '载重量(吨)',
driver_license VARCHAR(50) COMMENT '驾驶证号',
start_location VARCHAR(200) COMMENT '起始地点',
end_location VARCHAR(200) COMMENT '目的地点',
start_latitude DECIMAL(10,6) COMMENT '起始纬度',
start_longitude DECIMAL(10,6) COMMENT '起始经度',
end_latitude DECIMAL(10,6) COMMENT '目的纬度',
end_longitude DECIMAL(10,6) COMMENT '目的经度',
planned_distance DECIMAL(8,2) COMMENT '计划距离(公里)',
actual_distance DECIMAL(8,2) COMMENT '实际距离(公里)',
planned_start_time DATETIME COMMENT '计划开始时间',
actual_start_time DATETIME COMMENT '实际开始时间',
planned_end_time DATETIME COMMENT '计划结束时间',
actual_end_time DATETIME COMMENT '实际结束时间',
estimated_arrival_time DATETIME COMMENT '预计到达时间',
status ENUM('assigned', 'preparing', 'loading', 'started', 'transporting', 'arrived', 'unloading', 'completed', 'cancelled') DEFAULT 'assigned' COMMENT '任务状态',
transport_fee DECIMAL(10,2) COMMENT '运输费用',
fuel_cost DECIMAL(10,2) COMMENT '燃油费用',
toll_cost DECIMAL(10,2) COMMENT '过路费',
other_cost DECIMAL(10,2) COMMENT '其他费用',
notes TEXT COMMENT '备注',
cancel_reason TEXT COMMENT '取消原因',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (driver_id) REFERENCES users(id),
INDEX idx_task_no (task_no),
INDEX idx_order_id (order_id),
INDEX idx_driver_id (driver_id),
INDEX idx_vehicle_no (vehicle_no),
INDEX idx_status (status),
INDEX idx_planned_start_time (planned_start_time),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='运输任务表'
`,
// 系统配置表
system_configs: `
CREATE TABLE IF NOT EXISTS system_configs (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '配置ID',
config_key VARCHAR(100) UNIQUE NOT NULL COMMENT '配置键',
config_value TEXT NOT NULL COMMENT '配置值',
config_type ENUM('string', 'number', 'boolean', 'json', 'array') DEFAULT 'string' COMMENT '配置类型',
category VARCHAR(50) NOT NULL COMMENT '配置分类',
description TEXT COMMENT '配置描述',
is_public BOOLEAN DEFAULT FALSE COMMENT '是否公开配置',
is_editable BOOLEAN DEFAULT TRUE COMMENT '是否可编辑',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_config_key (config_key),
INDEX idx_category (category),
INDEX idx_is_public (is_public)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统配置表'
`
};
/**
* 创建表
*/
async function createTables() {
console.log('\n📋 开始创建数据表...');
for (const [tableName, sql] of Object.entries(createTableSQLs)) {
try {
console.log(`📝 创建表: ${tableName}`);
await sequelize.query(sql);
console.log(`✅ 表 ${tableName} 创建成功`);
} catch (error) {
console.log(`⚠️ 表 ${tableName}: ${error.message}`);
}
}
}
/**
* 插入测试数据
*/
async function insertTestData() {
console.log('\n📊 开始插入测试数据...');
try {
// 1. 插入管理员用户
console.log('👤 创建管理员用户...');
const adminPassword = await bcrypt.hash('admin123', 10);
const adminUuid = uuidv4();
await sequelize.query(`
INSERT IGNORE INTO users (
uuid, username, password_hash, openid, nickname, real_name,
user_type, status, registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
adminUuid, 'admin', adminPassword, 'admin_' + adminUuid.substring(0, 8),
'系统管理员', '管理员', 'admin', 'active', 'admin_create', 0
]
});
// 2. 插入测试用户
console.log('👥 创建测试用户...');
const testUsers = [
{
uuid: uuidv4(),
username: 'buyer001',
password: await bcrypt.hash('123456', 10),
nickname: '采购商张三',
real_name: '张三',
phone: '13800138001',
email: 'buyer001@example.com',
user_type: 'buyer',
company_name: '北京牛肉加工厂',
company_address: '北京市朝阳区xxx街道'
},
{
uuid: uuidv4(),
username: 'trader001',
password: await bcrypt.hash('123456', 10),
nickname: '贸易商李四',
real_name: '李四',
phone: '13800138002',
email: 'trader001@example.com',
user_type: 'trader',
company_name: '上海牛只贸易有限公司',
company_address: '上海市浦东新区xxx路'
},
{
uuid: uuidv4(),
username: 'driver001',
password: await bcrypt.hash('123456', 10),
nickname: '司机王五',
real_name: '王五',
phone: '13800138003',
email: 'driver001@example.com',
user_type: 'driver',
id_card: '110101199001011234'
}
];
for (const user of testUsers) {
await sequelize.query(`
INSERT IGNORE INTO users (
uuid, username, password_hash, openid, nickname, real_name, phone, email,
user_type, company_name, company_address, id_card, status,
registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
user.uuid, user.username, user.password, user.username + '_openid',
user.nickname, user.real_name, user.phone, user.email, user.user_type,
user.company_name || null, user.company_address || null, user.id_card || null,
'active', 'admin_create', 0
]
});
}
// 3. 插入供应商数据
console.log('🏭 创建供应商数据...');
const suppliers = [
{
name: '内蒙古草原牧业有限公司',
code: 'SUP001',
contact: '赵大牛',
phone: '13900139001',
email: 'sup001@example.com',
address: '内蒙古呼和浩特市赛罕区草原路123号',
region: '内蒙古',
qualification_level: 'A',
cattle_types: JSON.stringify(['西门塔尔牛', '安格斯牛', '夏洛莱牛']),
capacity: 500,
rating: 4.8,
cooperation_start_date: '2023-01-01'
},
{
name: '新疆天山畜牧合作社',
code: 'SUP002',
contact: '马小羊',
phone: '13900139002',
email: 'sup002@example.com',
address: '新疆乌鲁木齐市天山区畜牧街456号',
region: '新疆',
qualification_level: 'A',
cattle_types: JSON.stringify(['哈萨克牛', '新疆褐牛']),
capacity: 300,
rating: 4.5,
cooperation_start_date: '2023-03-15'
},
{
name: '山东鲁西黄牛养殖场',
code: 'SUP003',
contact: '孙大强',
phone: '13900139003',
email: 'sup003@example.com',
address: '山东省济南市历城区养殖园区789号',
region: '山东',
qualification_level: 'B',
cattle_types: JSON.stringify(['鲁西黄牛', '利木赞牛']),
capacity: 200,
rating: 4.2,
cooperation_start_date: '2023-06-01'
}
];
for (const supplier of suppliers) {
await sequelize.query(`
INSERT IGNORE INTO suppliers (
name, code, contact, phone, email, address, region, qualification_level,
cattle_types, capacity, rating, cooperation_start_date, status, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
supplier.name, supplier.code, supplier.contact, supplier.phone, supplier.email,
supplier.address, supplier.region, supplier.qualification_level, supplier.cattle_types,
supplier.capacity, supplier.rating, supplier.cooperation_start_date, 'active'
]
});
}
// 4. 插入订单数据
console.log('📋 创建订单数据...');
// 获取用户ID
const [buyers] = await sequelize.query("SELECT id FROM users WHERE user_type = 'buyer' LIMIT 1");
const [traders] = await sequelize.query("SELECT id FROM users WHERE user_type = 'trader' LIMIT 1");
const [supplierIds] = await sequelize.query("SELECT id FROM suppliers LIMIT 2");
if (buyers.length > 0 && supplierIds.length > 0) {
const orders = [
{
order_no: 'ORD' + Date.now() + '001',
client_id: buyers[0].id,
trader_id: traders.length > 0 ? traders[0].id : null,
supplier_id: supplierIds[0].id,
cattle_type: '西门塔尔牛',
quantity: 50,
estimated_weight: 25000.00,
unit_price: 32.50,
price_type: 'per_kg',
total_amount: 812500.00,
prepaid_amount: 200000.00,
delivery_address: '北京市朝阳区屠宰场',
delivery_time: '2024-01-15 08:00:00',
status: 'confirmed',
special_requirements: '要求健康证明齐全',
quality_standards: JSON.stringify({
min_weight: 450,
max_weight: 550,
health_grade: 'A'
})
},
{
order_no: 'ORD' + Date.now() + '002',
client_id: buyers[0].id,
supplier_id: supplierIds.length > 1 ? supplierIds[1].id : supplierIds[0].id,
cattle_type: '安格斯牛',
quantity: 30,
estimated_weight: 16500.00,
unit_price: 35.00,
price_type: 'per_kg',
total_amount: 577500.00,
prepaid_amount: 150000.00,
delivery_address: '上海市浦东新区加工厂',
delivery_time: '2024-01-20 10:00:00',
status: 'pending',
special_requirements: '需要冷链运输',
quality_standards: JSON.stringify({
min_weight: 500,
max_weight: 600,
health_grade: 'A'
})
}
];
for (const order of orders) {
await sequelize.query(`
INSERT IGNORE INTO orders (
order_no, client_id, trader_id, supplier_id, cattle_type, quantity,
estimated_weight, unit_price, price_type, total_amount, prepaid_amount,
delivery_address, delivery_time, status, special_requirements, quality_standards,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
order.order_no, order.client_id, order.trader_id, order.supplier_id,
order.cattle_type, order.quantity, order.estimated_weight, order.unit_price,
order.price_type, order.total_amount, order.prepaid_amount, order.delivery_address,
order.delivery_time, order.status, order.special_requirements, order.quality_standards
]
});
}
}
// 5. 插入系统配置
console.log('⚙️ 创建系统配置...');
const configs = [
['system.name', '活牛采购智能数字化系统', 'string', 'system', '系统名称'],
['system.version', '1.0.0', 'string', 'system', '系统版本'],
['order.auto_confirm_hours', '24', 'number', 'order', '订单自动确认时间(小时)'],
['payment.timeout_minutes', '30', 'number', 'payment', '支付超时时间(分钟)'],
['transport.tracking_interval', '300', 'number', 'transport', '位置跟踪间隔(秒)'],
['quality.min_score', '80', 'number', 'quality', '质量检验最低分数'],
['notification.sms_enabled', 'true', 'boolean', 'notification', '是否启用短信通知'],
['notification.email_enabled', 'true', 'boolean', 'notification', '是否启用邮件通知']
];
for (const [key, value, type, category, description] of configs) {
await sequelize.query(`
INSERT IGNORE INTO system_configs (
config_key, config_value, config_type, category, description,
is_public, is_editable, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [key, value, type, category, description, true, true]
});
}
console.log('✅ 测试数据插入完成');
} catch (error) {
console.error('❌ 插入测试数据失败:', error.message);
throw error;
}
}
/**
* 验证数据
*/
async function validateData() {
console.log('\n🔍 验证数据完整性...');
try {
// 统计各表数据量
const tables = ['users', 'suppliers', 'orders', 'payments', 'transport_tasks', 'system_configs'];
for (const table of tables) {
try {
const [result] = await sequelize.query(`SELECT COUNT(*) as count FROM ${table}`);
console.log(`📊 ${table}: ${result[0].count} 条记录`);
} catch (error) {
console.log(`⚠️ 表 ${table} 不存在或查询失败`);
}
}
// 检查管理员用户
const [adminUsers] = await sequelize.query(
"SELECT id, username, nickname, user_type FROM users WHERE user_type = 'admin'"
);
console.log('\n👤 管理员用户:');
adminUsers.forEach(user => {
console.log(` - ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}`);
});
// 检查供应商
const [suppliers] = await sequelize.query(
"SELECT id, name, code, region, qualification_level FROM suppliers LIMIT 5"
);
console.log('\n🏭 供应商列表:');
suppliers.forEach(supplier => {
console.log(` - ${supplier.code}: ${supplier.name} (${supplier.region}, 等级${supplier.qualification_level})`);
});
// 检查订单
const [orders] = await sequelize.query(
"SELECT id, order_no, cattle_type, quantity, total_amount, status FROM orders LIMIT 5"
);
console.log('\n📋 订单列表:');
orders.forEach(order => {
console.log(` - ${order.order_no}: ${order.cattle_type} ${order.quantity}头, ¥${order.total_amount} (${order.status})`);
});
} catch (error) {
console.error('❌ 数据验证失败:', error.message);
}
}
/**
* 主函数
*/
async function initializeDatabase() {
try {
console.log('\n🚀 ===== 数据库初始化开始 =====');
// 1. 测试连接
console.log('\n📡 测试数据库连接...');
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
// 2. 创建表
await createTables();
// 3. 插入测试数据
await insertTestData();
// 4. 验证数据
await validateData();
console.log('\n🎉 ===== 数据库初始化完成 =====');
console.log('\n📋 系统信息:');
console.log('🌐 后端服务: http://localhost:4330');
console.log('🎨 管理后台: http://localhost:3000');
console.log('👤 管理员账户: admin / admin123');
console.log('📚 API文档: http://localhost:4330/api-docs');
console.log('\n🔑 测试账户:');
console.log(' 采购商: buyer001 / 123456');
console.log(' 贸易商: trader001 / 123456');
console.log(' 司机: driver001 / 123456');
} catch (error) {
console.error('\n❌ 数据库初始化失败:', error);
} finally {
await sequelize.close();
console.log('\n🔌 数据库连接已关闭');
}
}
// 运行初始化
if (require.main === module) {
initializeDatabase();
}
module.exports = { initializeDatabase };

View File

@@ -0,0 +1,415 @@
/**
* 兼容现有表结构的测试数据插入脚本
*/
require('dotenv').config();
const { Sequelize } = require('sequelize');
const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid');
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: (msg) => console.log(`[SQL] ${msg}`)
}
);
/**
* 插入测试数据
*/
async function insertTestData() {
console.log('\n📊 开始插入兼容的测试数据...');
try {
// 1. 检查并插入管理员用户
console.log('👤 检查管理员用户...');
const [existingAdmin] = await sequelize.query(
"SELECT id FROM users WHERE username = 'admin'"
);
if (existingAdmin.length === 0) {
console.log('创建管理员用户...');
const adminPassword = await bcrypt.hash('admin123', 10);
const adminUuid = uuidv4();
await sequelize.query(`
INSERT INTO users (
uuid, username, password_hash, openid, nickname, real_name,
user_type, status, registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
adminUuid, 'admin', adminPassword, 'admin_' + adminUuid.substring(0, 8),
'系统管理员', '管理员', 'admin', 'active', 'admin_create', 0
]
});
console.log('✅ 管理员用户创建成功');
} else {
console.log('✅ 管理员用户已存在');
}
// 2. 插入测试用户
console.log('👥 创建测试用户...');
const testUsers = [
{
uuid: uuidv4(),
username: 'buyer001',
password: await bcrypt.hash('123456', 10),
nickname: '采购商张三',
real_name: '张三',
phone: '13800138001',
email: 'buyer001@example.com',
user_type: 'buyer',
company_name: '北京牛肉加工厂',
company_address: '北京市朝阳区xxx街道'
},
{
uuid: uuidv4(),
username: 'trader001',
password: await bcrypt.hash('123456', 10),
nickname: '贸易商李四',
real_name: '李四',
phone: '13800138002',
email: 'trader001@example.com',
user_type: 'trader',
company_name: '上海牛只贸易有限公司',
company_address: '上海市浦东新区xxx路'
},
{
uuid: uuidv4(),
username: 'driver001',
password: await bcrypt.hash('123456', 10),
nickname: '司机王五',
real_name: '王五',
phone: '13800138003',
email: 'driver001@example.com',
user_type: 'driver',
id_card: '110101199001011234'
}
];
for (const user of testUsers) {
// 检查用户是否已存在
const [existing] = await sequelize.query(
"SELECT id FROM users WHERE username = ?",
{ replacements: [user.username] }
);
if (existing.length === 0) {
await sequelize.query(`
INSERT INTO users (
uuid, username, password_hash, openid, nickname, real_name, phone, email,
user_type, company_name, company_address, id_card, status,
registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
user.uuid, user.username, user.password, user.username + '_openid',
user.nickname, user.real_name, user.phone, user.email, user.user_type,
user.company_name || null, user.company_address || null, user.id_card || null,
'active', 'admin_create', 0
]
});
console.log(`✅ 用户 ${user.username} 创建成功`);
} else {
console.log(`✅ 用户 ${user.username} 已存在`);
}
}
// 3. 插入供应商数据(使用现有表结构)
console.log('🏭 创建供应商数据...');
const suppliers = [
{
name: '内蒙古草原牧业有限公司',
code: 'SUP001',
contact: '赵大牛',
phone: '13900139001',
address: '内蒙古呼和浩特市赛罕区草原路123号',
region: '内蒙古',
qualificationLevel: 'A',
cattleTypes: JSON.stringify(['西门塔尔牛', '安格斯牛', '夏洛莱牛']),
capacity: 500,
rating: 4.8,
cooperationStartDate: '2023-01-01'
},
{
name: '新疆天山畜牧合作社',
code: 'SUP002',
contact: '马小羊',
phone: '13900139002',
address: '新疆乌鲁木齐市天山区畜牧街456号',
region: '新疆',
qualificationLevel: 'A',
cattleTypes: JSON.stringify(['哈萨克牛', '新疆褐牛']),
capacity: 300,
rating: 4.5,
cooperationStartDate: '2023-03-15'
},
{
name: '山东鲁西黄牛养殖场',
code: 'SUP003',
contact: '孙大强',
phone: '13900139003',
address: '山东省济南市历城区养殖园区789号',
region: '山东',
qualificationLevel: 'B',
cattleTypes: JSON.stringify(['鲁西黄牛', '利木赞牛']),
capacity: 200,
rating: 4.2,
cooperationStartDate: '2023-06-01'
}
];
for (const supplier of suppliers) {
// 检查供应商是否已存在
const [existing] = await sequelize.query(
"SELECT id FROM suppliers WHERE code = ?",
{ replacements: [supplier.code] }
);
if (existing.length === 0) {
await sequelize.query(`
INSERT INTO suppliers (
name, code, contact, phone, address, region, qualificationLevel,
cattleTypes, capacity, rating, cooperationStartDate, status, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
supplier.name, supplier.code, supplier.contact, supplier.phone,
supplier.address, supplier.region, supplier.qualificationLevel, supplier.cattleTypes,
supplier.capacity, supplier.rating, supplier.cooperationStartDate, 'active'
]
});
console.log(`✅ 供应商 ${supplier.code} 创建成功`);
} else {
console.log(`✅ 供应商 ${supplier.code} 已存在`);
}
}
// 4. 插入订单数据(使用现有表结构)
console.log('📋 创建订单数据...');
// 获取用户和供应商ID
const [buyers] = await sequelize.query("SELECT id, nickname FROM users WHERE user_type = 'buyer' LIMIT 1");
const [traders] = await sequelize.query("SELECT id, nickname FROM users WHERE user_type = 'trader' LIMIT 1");
const [supplierList] = await sequelize.query("SELECT id, name FROM suppliers LIMIT 2");
if (buyers.length > 0 && supplierList.length > 0) {
const orders = [
{
orderNo: 'ORD' + Date.now().toString().slice(-8) + '001',
buyerId: buyers[0].id,
buyerName: buyers[0].nickname,
supplierId: supplierList[0].id,
supplierName: supplierList[0].name,
traderId: null,
traderName: null,
cattleBreed: '西门塔尔牛',
cattleCount: 50,
expectedWeight: 25000.00,
unitPrice: 32.50,
totalAmount: 812500.00,
paidAmount: 200000.00,
remainingAmount: 612500.00,
deliveryAddress: '北京市朝阳区屠宰场',
expectedDeliveryDate: '2024-01-15 08:00:00',
status: 'confirmed',
notes: '要求健康证明齐全质量等级A级'
},
{
orderNo: 'ORD' + Date.now().toString().slice(-8) + '002',
buyerId: buyers[0].id,
buyerName: buyers[0].nickname,
supplierId: supplierList.length > 1 ? supplierList[1].id : supplierList[0].id,
supplierName: supplierList.length > 1 ? supplierList[1].name : supplierList[0].name,
traderId: null,
traderName: null,
cattleBreed: '安格斯牛',
cattleCount: 30,
expectedWeight: 16500.00,
unitPrice: 35.00,
totalAmount: 577500.00,
paidAmount: 150000.00,
remainingAmount: 427500.00,
deliveryAddress: '上海市浦东新区加工厂',
expectedDeliveryDate: '2024-01-20 10:00:00',
status: 'pending',
notes: '需要冷链运输重量范围500-600kg'
}
];
for (const order of orders) {
// 检查订单是否已存在
const [existing] = await sequelize.query(
"SELECT id FROM orders WHERE orderNo = ?",
{ replacements: [order.orderNo] }
);
if (existing.length === 0) {
await sequelize.query(`
INSERT INTO orders (
orderNo, buyerId, buyerName, supplierId, supplierName, traderId, traderName,
cattleBreed, cattleCount, expectedWeight, unitPrice, totalAmount, paidAmount,
remainingAmount, deliveryAddress, expectedDeliveryDate, status, notes,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [
order.orderNo, order.buyerId, order.buyerName, order.supplierId, order.supplierName,
order.traderId, order.traderName, order.cattleBreed, order.cattleCount,
order.expectedWeight, order.unitPrice, order.totalAmount, order.paidAmount,
order.remainingAmount, order.deliveryAddress, order.expectedDeliveryDate,
order.status, order.notes
]
});
console.log(`✅ 订单 ${order.orderNo} 创建成功`);
} else {
console.log(`✅ 订单 ${order.orderNo} 已存在`);
}
}
}
// 5. 插入系统配置
console.log('⚙️ 创建系统配置...');
const configs = [
['system.name', '活牛采购智能数字化系统', 'string', 'system', '系统名称'],
['system.version', '1.0.0', 'string', 'system', '系统版本'],
['order.auto_confirm_hours', '24', 'number', 'order', '订单自动确认时间(小时)'],
['payment.timeout_minutes', '30', 'number', 'payment', '支付超时时间(分钟)'],
['transport.tracking_interval', '300', 'number', 'transport', '位置跟踪间隔(秒)'],
['quality.min_score', '80', 'number', 'quality', '质量检验最低分数'],
['notification.sms_enabled', 'true', 'boolean', 'notification', '是否启用短信通知'],
['notification.email_enabled', 'true', 'boolean', 'notification', '是否启用邮件通知']
];
for (const [key, value, type, category, description] of configs) {
// 检查配置是否已存在
const [existing] = await sequelize.query(
"SELECT id FROM system_configs WHERE config_key = ?",
{ replacements: [key] }
);
if (existing.length === 0) {
await sequelize.query(`
INSERT INTO system_configs (
config_key, config_value, config_type, category, description,
is_public, is_editable, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`, {
replacements: [key, value, type, category, description, true, true]
});
console.log(`✅ 配置 ${key} 创建成功`);
} else {
console.log(`✅ 配置 ${key} 已存在`);
}
}
console.log('✅ 测试数据插入完成');
} catch (error) {
console.error('❌ 插入测试数据失败:', error.message);
throw error;
}
}
/**
* 验证数据
*/
async function validateData() {
console.log('\n🔍 验证数据完整性...');
try {
// 统计各表数据量
const tables = ['users', 'suppliers', 'orders', 'payments', 'system_configs'];
for (const table of tables) {
try {
const [result] = await sequelize.query(`SELECT COUNT(*) as count FROM ${table}`);
console.log(`📊 ${table}: ${result[0].count} 条记录`);
} catch (error) {
console.log(`⚠️ 表 ${table} 不存在或查询失败`);
}
}
// 检查管理员用户
const [adminUsers] = await sequelize.query(
"SELECT id, username, nickname, user_type FROM users WHERE user_type = 'admin'"
);
console.log('\n👤 管理员用户:');
adminUsers.forEach(user => {
console.log(` - ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}`);
});
// 检查供应商
const [suppliers] = await sequelize.query(
"SELECT id, name, code, region, qualificationLevel FROM suppliers LIMIT 5"
);
console.log('\n🏭 供应商列表:');
suppliers.forEach(supplier => {
console.log(` - ${supplier.code}: ${supplier.name} (${supplier.region}, 等级${supplier.qualificationLevel})`);
});
// 检查订单
const [orders] = await sequelize.query(
"SELECT id, orderNo, cattleBreed, cattleCount, totalAmount, status FROM orders LIMIT 5"
);
console.log('\n📋 订单列表:');
orders.forEach(order => {
console.log(` - ${order.orderNo}: ${order.cattleBreed} ${order.cattleCount}头, ¥${order.totalAmount} (${order.status})`);
});
} catch (error) {
console.error('❌ 数据验证失败:', error.message);
}
}
/**
* 主函数
*/
async function main() {
try {
console.log('\n🚀 ===== 兼容数据插入开始 =====');
// 1. 测试连接
console.log('\n📡 测试数据库连接...');
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
// 2. 插入测试数据
await insertTestData();
// 3. 验证数据
await validateData();
console.log('\n🎉 ===== 数据插入完成 =====');
console.log('\n📋 系统信息:');
console.log('🌐 后端服务: http://localhost:4330');
console.log('🎨 管理后台: http://localhost:3000');
console.log('👤 管理员账户: admin / admin123');
console.log('📚 API文档: http://localhost:4330/api-docs');
console.log('\n🔑 测试账户:');
console.log(' 采购商: buyer001 / 123456');
console.log(' 贸易商: trader001 / 123456');
console.log(' 司机: driver001 / 123456');
} catch (error) {
console.error('\n❌ 操作失败:', error);
} finally {
await sequelize.close();
console.log('\n🔌 数据库连接已关闭');
}
}
// 运行
if (require.main === module) {
main();
}
module.exports = { main };

View File

@@ -31,43 +31,40 @@
"author": "NiuMall Team",
"license": "MIT",
"dependencies": {
"bcrypt": "^5.1.1",
"bcryptjs": "^3.0.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-rate-limit": "^7.1.5",
"express-validator": "^7.0.1",
"helmet": "^7.1.0",
"joi": "^17.11.0",
"jsonwebtoken": "^9.0.2",
"moment": "^2.29.4",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"mysql2": "^3.6.5",
"sequelize": "^6.35.2",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"uuid": "^9.0.1",
"mysql2": "^3.6.5",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"joi": "^17.11.0",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"express-rate-limit": "^7.1.5",
"compression": "^1.7.4",
"morgan": "^1.10.0",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1",
"yamljs": "^0.3.0"
"dotenv": "^16.3.1",
"multer": "^1.4.5-lts.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"express-validator": "^7.0.1",
"moment": "^2.29.4",
"uuid": "^9.0.1"
},
"devDependencies": {
"nodemon": "^3.0.2",
"jest": "^29.7.0",
"supertest": "^6.3.3",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.7.0",
"nodemon": "^3.0.2",
"prettier": "^3.1.0",
"sequelize-cli": "^6.6.2",
"sqlite3": "^5.1.7",
"supertest": "^6.3.3"
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"sequelize-cli": "^6.6.2"
},
"jest": {
"testEnvironment": "node",

View File

@@ -0,0 +1,242 @@
/**
* 简化版数据库结构检查和修复工具
*/
require('dotenv').config();
const { Sequelize, QueryTypes } = require('sequelize');
const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid');
// 创建数据库连接
const sequelize = new Sequelize(
process.env.DB_NAME || 'niumall',
process.env.DB_USERNAME || 'root',
process.env.DB_PASSWORD || 'aiotAiot123!',
{
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
dialect: 'mysql',
logging: false
}
);
async function checkAndRepairDatabase() {
try {
console.log('\n🔍 ===== 数据库结构完整性检查 =====\n');
// 1. 测试数据库连接
console.log('📡 测试数据库连接...');
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
// 2. 检查现有表
console.log('\n📋 检查现有表结构...');
const tables = await sequelize.query(
"SHOW TABLES",
{ type: QueryTypes.SELECT }
);
const tableNames = tables.map(table => Object.values(table)[0]);
console.log(`📊 发现 ${tableNames.length} 个表: ${tableNames.join(', ')}`);
// 3. 检查users表结构
if (tableNames.includes('users')) {
console.log('\n🔍 检查users表结构...');
const columns = await sequelize.query(
"SHOW COLUMNS FROM users",
{ type: QueryTypes.SELECT }
);
const columnNames = columns.map(col => col.Field);
console.log(`📝 users表字段 (${columnNames.length}个): ${columnNames.join(', ')}`);
// 检查关键字段
const requiredFields = ['username', 'password_hash', 'user_type', 'status'];
const missingFields = requiredFields.filter(field => !columnNames.includes(field));
if (missingFields.length > 0) {
console.log(`⚠️ 缺少关键字段: ${missingFields.join(', ')}`);
} else {
console.log('✅ 关键字段完整');
}
}
// 4. 检查suppliers表
if (!tableNames.includes('suppliers')) {
console.log('\n⚠ suppliers表不存在创建中...');
await sequelize.query(`
CREATE TABLE suppliers (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
code VARCHAR(20) UNIQUE NOT NULL,
contact VARCHAR(50) NOT NULL,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(100),
address VARCHAR(200) NOT NULL,
region VARCHAR(20) NOT NULL,
business_license VARCHAR(255),
animal_quarantine_certificate VARCHAR(255),
qualification_level ENUM('A','B','C','D') DEFAULT 'C',
certifications JSON,
cattle_types JSON,
capacity INT DEFAULT 0,
rating DECIMAL(3,2) DEFAULT 0.00,
cooperation_start_date DATE,
status ENUM('active','inactive','suspended','blacklisted') DEFAULT 'active',
bank_account VARCHAR(50),
bank_name VARCHAR(100),
tax_number VARCHAR(30),
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
console.log('✅ suppliers表创建成功');
} else {
console.log('✅ suppliers表已存在');
}
// 5. 检查orders表
if (!tableNames.includes('orders')) {
console.log('\n⚠ orders表不存在创建中...');
await sequelize.query(`
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
orderNo VARCHAR(50) UNIQUE NOT NULL,
buyerId BIGINT NOT NULL,
buyerName VARCHAR(100) NOT NULL,
supplierId BIGINT,
supplierName VARCHAR(100),
traderId BIGINT,
traderName VARCHAR(100),
cattleBreed VARCHAR(20) NOT NULL,
cattleCount INT NOT NULL,
expectedWeight DECIMAL(10,2) NOT NULL,
actualWeight DECIMAL(10,2),
unitPrice DECIMAL(10,2) NOT NULL,
totalAmount DECIMAL(15,2) NOT NULL,
paidAmount DECIMAL(15,2) DEFAULT 0,
remainingAmount DECIMAL(15,2) DEFAULT 0,
status ENUM('pending','confirmed','loading','shipping','delivered','completed','cancelled') DEFAULT 'pending',
deliveryAddress VARCHAR(255) NOT NULL,
expectedDeliveryDate DATETIME NOT NULL,
actualDeliveryDate DATETIME,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
console.log('✅ orders表创建成功');
} else {
console.log('✅ orders表已存在');
}
// 6. 检查payments表
if (!tableNames.includes('payments')) {
console.log('\n⚠ payments表不存在创建中...');
await sequelize.query(`
CREATE TABLE payments (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
amount DECIMAL(15,2) NOT NULL,
paid_amount DECIMAL(15,2),
payment_type ENUM('wechat','alipay','bank') NOT NULL,
payment_method ENUM('mini_program','app','web') NOT NULL,
payment_no VARCHAR(50) UNIQUE NOT NULL,
third_party_id VARCHAR(100),
status ENUM('pending','paid','failed','refunded') DEFAULT 'pending',
paid_time DATETIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
console.log('✅ payments表创建成功');
} else {
console.log('✅ payments表已存在');
}
// 7. 检查管理员用户
console.log('\n👤 检查管理员用户...');
const adminUsers = await sequelize.query(
"SELECT * FROM users WHERE user_type = 'admin' OR username = 'admin'",
{ type: QueryTypes.SELECT }
);
if (adminUsers.length === 0) {
console.log('⚠️ 未找到管理员用户,创建中...');
const hashedPassword = await bcrypt.hash('admin123', 10);
const uuid = uuidv4();
await sequelize.query(
`INSERT INTO users (
uuid, username, password_hash, openid, nickname, user_type, status,
registration_source, login_count, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
{
replacements: [
uuid,
'admin',
hashedPassword,
'admin_' + uuid.substring(0, 8),
'系统管理员',
'admin',
'active',
'admin_create',
0
]
}
);
console.log('✅ 管理员用户创建成功');
} else {
console.log(`✅ 发现 ${adminUsers.length} 个管理员用户`);
adminUsers.forEach(user => {
console.log(` - ID: ${user.id}, 用户名: ${user.username || 'N/A'}, 昵称: ${user.nickname}`);
});
}
// 8. 数据完整性检查
console.log('\n🔍 数据完整性检查...');
// 检查用户表数据
const userCount = await sequelize.query(
"SELECT COUNT(*) as count FROM users",
{ type: QueryTypes.SELECT }
);
console.log(`📊 用户总数: ${userCount[0].count}`);
// 检查订单表数据
if (tableNames.includes('orders')) {
const orderCount = await sequelize.query(
"SELECT COUNT(*) as count FROM orders",
{ type: QueryTypes.SELECT }
);
console.log(`📊 订单总数: ${orderCount[0].count}`);
}
// 检查供应商表数据
if (tableNames.includes('suppliers')) {
const supplierCount = await sequelize.query(
"SELECT COUNT(*) as count FROM suppliers",
{ type: QueryTypes.SELECT }
);
console.log(`📊 供应商总数: ${supplierCount[0].count}`);
}
console.log('\n🎉 ===== 数据库检查完成 =====');
console.log('\n📋 系统信息:');
console.log('🌐 后端服务: http://localhost:4330');
console.log('🎨 管理后台: http://localhost:3000');
console.log('👤 管理员账户: admin / admin123');
console.log('📚 API文档: http://localhost:4330/api-docs');
} catch (error) {
console.error('\n❌ 数据库检查失败:', error.message);
} finally {
await sequelize.close();
console.log('\n🔌 数据库连接已关闭');
}
}
// 运行检查
checkAndRepairDatabase();

View File

@@ -64,6 +64,7 @@ app.use(helmet({
app.use(cors({
origin: [
'http://localhost:3000',
'http://localhost:3001',
'http://localhost:5173',
'https://wapi.nanniwan.com',
'https://ad.nanniwan.com',

196
scripts/database/README.md Normal file
View File

@@ -0,0 +1,196 @@
# 数据库初始化脚本
本目录包含牛牛商城项目的数据库初始化脚本,用于创建数据库表结构和插入测试数据。
## 文件说明
### 1. `init_database.sql`
- **功能**: 创建所有必要的数据库表结构
- **包含内容**:
- 用户表 (users)
- 供应商表 (suppliers)
- 司机表 (drivers)
- 车辆表 (vehicles)
- 订单表 (orders)
- 支付表 (payments)
- 运输表 (transports)
- 运输跟踪表 (transport_tracks)
- 质检记录表 (quality_records)
- 结算表 (settlements)
- 外键约束关系
### 2. `init_test_data.sql`
- **功能**: 插入测试数据
- **包含内容**:
- 管理员、采购商、贸易商、质检员等用户数据
- 5个供应商的基本信息
- 5个司机和车辆的信息
- 5个订单及相关的支付、运输、质检、结算记录
- 运输跟踪轨迹数据
### 3. `run_init.sh`
- **功能**: 一键执行数据库初始化的Shell脚本
- **特性**:
- 支持命令行参数配置数据库连接
- 自动检测数据库连接
- 可选择跳过表结构创建或测试数据插入
- 彩色输出和进度提示
- 执行完成后显示统计信息
## 使用方法
### 前置条件
1. **安装MySQL客户端**
```bash
# macOS
brew install mysql-client
# 或者安装完整的MySQL
brew install mysql
```
2. **确保数据库服务可访问**
- 本地MySQL服务已启动
- 或者远程数据库连接正常
### 执行初始化
#### 方法一使用Shell脚本推荐
```bash
# 进入脚本目录
cd /Users/aiotagro/vue/niumall/scripts/database
# 使用默认配置localhost:3306
./run_init.sh -u root -p your_password -d niumall
# 使用远程数据库
./run_init.sh \
--host nj-cdb-3pwh2kz1.sql.tencentcdb.com \
--port 20784 \
--user jiebanke \
--password 'aiot741$12346' \
--database niumall
# 只创建表结构,跳过测试数据
./run_init.sh -u root -p password --skip-data
# 只插入测试数据,跳过表结构创建
./run_init.sh -u root -p password --skip-structure
```
#### 方法二手动执行SQL文件
```bash
# 1. 创建表结构
mysql -h host -P port -u username -p database_name < init_database.sql
# 2. 插入测试数据
mysql -h host -P port -u username -p database_name < init_test_data.sql
```
### 脚本参数说明
| 参数 | 简写 | 说明 | 默认值 |
|------|------|------|--------|
| `--host` | `-h` | 数据库主机地址 | localhost |
| `--port` | `-P` | 数据库端口 | 3306 |
| `--user` | `-u` | 数据库用户名 | root |
| `--password` | `-p` | 数据库密码 | 无 |
| `--database` | `-d` | 数据库名称 | niumall |
| `--skip-structure` | 无 | 跳过表结构创建 | false |
| `--skip-data` | 无 | 跳过测试数据插入 | false |
| `--help` | 无 | 显示帮助信息 | - |
## 测试数据说明
### 用户数据
- **管理员**: admin/123456
- **采购商**: buyer001/123456, buyer002/123456
- **贸易商**: trader001/123456
- **质检员**: staff001/123456, staff002/123456
### 业务数据
- **供应商**: 5个不同地区的供应商内蒙古、山东、河南、新疆、黑龙江
- **订单**: 5个订单涵盖不同的牛种类型和交易状态
- **运输**: 包含运输中和已安排的运输任务
- **质检**: 装车前和到货质检记录
- **结算**: 预付款和尾款结算记录
## 数据库表关系
```
users (用户表)
├── orders.buyerId (买方)
├── orders.traderId (贸易商)
├── payments.user_id (支付用户)
├── quality_records.inspector_id (质检员)
└── settlements.approver_id (审批人)
suppliers (供应商表)
└── orders.supplierId (供应商)
drivers (司机表)
├── vehicles.driver_id (车辆司机)
├── transports.driver_id (运输司机)
└── transport_tracks.driver_id (跟踪司机)
vehicles (车辆表)
├── drivers.current_vehicle_id (司机当前车辆)
└── transports.vehicle_id (运输车辆)
orders (订单表)
├── payments.order_id (订单支付)
├── transports.order_id (订单运输)
├── transport_tracks.order_id (运输跟踪)
├── quality_records.order_id (质检记录)
└── settlements.order_id (结算记录)
transports (运输表)
└── transport_tracks.transport_id (运输跟踪)
```
## 注意事项
1. **权限要求**: 执行脚本的数据库用户需要有CREATE、DROP、INSERT、SELECT权限
2. **数据清理**: 脚本会删除已存在的表,请确保备份重要数据
3. **字符集**: 所有表使用utf8mb4字符集支持emoji和特殊字符
4. **外键约束**: 表之间设置了外键约束,删除数据时需注意依赖关系
5. **测试环境**: 建议在测试环境中先验证脚本的正确性
## 故障排除
### 1. MySQL客户端未安装
```bash
# macOS安装MySQL客户端
brew install mysql-client
# 添加到PATH如果需要
echo 'export PATH="/opt/homebrew/opt/mysql-client/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
```
### 2. 数据库连接失败
- 检查数据库服务是否启动
- 验证主机地址、端口、用户名、密码是否正确
- 确认网络连接和防火墙设置
### 3. 权限不足
```sql
-- 为用户授予必要权限
GRANT CREATE, DROP, INSERT, SELECT, UPDATE, DELETE ON niumall.* TO 'username'@'%';
FLUSH PRIVILEGES;
```
### 4. 外键约束错误
- 确保按照正确顺序创建表
- 检查外键引用的表和字段是否存在
- 验证数据类型是否匹配
## 更新日志
- **2024-01-21**: 初始版本,包含完整的表结构和测试数据
- 支持10个核心业务表
- 提供一键初始化脚本
- 包含丰富的测试数据用于开发调试

View File

@@ -0,0 +1,380 @@
-- =====================================================
-- 牛牛商城数据库初始化脚本
-- 创建时间: 2024-01-21
-- 描述: 创建所有必要的数据表和初始化数据
-- =====================================================
-- 设置字符集和时区
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
SET time_zone = '+08:00';
-- =====================================================
-- 1. 用户表 (users)
-- =====================================================
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`uuid` varchar(36) DEFAULT NULL COMMENT '用户唯一标识符',
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`password_hash` varchar(255) DEFAULT NULL COMMENT '密码哈希值',
`openid` varchar(64) DEFAULT NULL COMMENT '微信小程序OpenID',
`unionid` varchar(64) DEFAULT NULL COMMENT '微信UnionID',
`nickname` varchar(50) NOT NULL COMMENT '用户昵称',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像URL',
`gender` enum('male','female','other') DEFAULT NULL COMMENT '性别',
`birthday` date DEFAULT NULL COMMENT '生日',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱地址',
`user_type` enum('buyer','trader','supplier','driver','staff','admin') NOT NULL DEFAULT 'buyer' COMMENT '用户类型',
`company_name` varchar(100) DEFAULT NULL COMMENT '公司名称',
`company_address` varchar(200) DEFAULT NULL COMMENT '公司地址',
`business_license` varchar(255) DEFAULT NULL COMMENT '营业执照文件路径',
`id_card` varchar(18) DEFAULT NULL COMMENT '身份证号',
`status` enum('active','inactive','suspended','pending_approval') NOT NULL DEFAULT 'pending_approval' COMMENT '用户状态',
`last_login_at` datetime DEFAULT NULL COMMENT '最后登录时间',
`login_count` int(11) NOT NULL DEFAULT '0' COMMENT '登录次数',
`registration_source` enum('miniprogram','web','admin_create') NOT NULL DEFAULT 'miniprogram' COMMENT '注册来源',
`approval_notes` text COMMENT '审核备注',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `phone` (`phone`),
UNIQUE KEY `email` (`email`),
KEY `idx_openid` (`openid`),
KEY `idx_user_type` (`user_type`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- =====================================================
-- 2. 供应商表 (suppliers)
-- =====================================================
DROP TABLE IF EXISTS `suppliers`;
CREATE TABLE `suppliers` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '供应商ID',
`name` varchar(100) NOT NULL COMMENT '供应商名称',
`code` varchar(20) NOT NULL COMMENT '供应商编码',
`contact` varchar(50) NOT NULL COMMENT '联系人姓名',
`phone` varchar(20) NOT NULL COMMENT '联系电话',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱地址',
`address` varchar(200) NOT NULL COMMENT '详细地址',
`region` varchar(20) NOT NULL COMMENT '所属区域',
`business_license` varchar(255) DEFAULT NULL COMMENT '营业执照文件路径',
`animal_quarantine_certificate` varchar(255) DEFAULT NULL COMMENT '动物防疫条件合格证文件路径',
`qualification_level` enum('A','B','C','D') NOT NULL DEFAULT 'C' COMMENT '资质等级',
`certifications` json DEFAULT NULL COMMENT '认证证书(JSON格式)',
`cattle_types` json DEFAULT NULL COMMENT '可供应牛种类型(JSON格式)',
`capacity` int(11) DEFAULT NULL COMMENT '供应能力(头/月)',
`rating` decimal(3,2) DEFAULT '0.00' COMMENT '供应商评级(0-5分)',
`cooperation_start_date` date DEFAULT NULL COMMENT '合作开始日期',
`status` enum('active','inactive','suspended') NOT NULL DEFAULT 'active' COMMENT '状态',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`),
UNIQUE KEY `phone` (`phone`),
KEY `idx_region` (`region`),
KEY `idx_status` (`status`),
KEY `idx_qualification_level` (`qualification_level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商表';
-- =====================================================
-- 3. 司机表 (drivers)
-- =====================================================
DROP TABLE IF EXISTS `drivers`;
CREATE TABLE `drivers` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '司机ID',
`name` varchar(50) NOT NULL COMMENT '司机姓名',
`phone` varchar(20) NOT NULL COMMENT '联系电话',
`id_card` varchar(18) NOT NULL COMMENT '身份证号',
`driver_license` varchar(20) NOT NULL COMMENT '驾驶证号',
`license_type` enum('A1','A2','B1','B2','C1','C2') NOT NULL COMMENT '驾驶证类型',
`license_expiry_date` date NOT NULL COMMENT '驾驶证到期日期',
`qualification_certificate` varchar(255) DEFAULT NULL COMMENT '从业资格证文件路径',
`qualification_expiry_date` date DEFAULT NULL COMMENT '从业资格证到期日期',
`emergency_contact` varchar(50) DEFAULT NULL COMMENT '紧急联系人',
`emergency_phone` varchar(20) DEFAULT NULL COMMENT '紧急联系电话',
`current_vehicle_id` bigint(20) DEFAULT NULL COMMENT '当前使用车辆ID',
`employment_type` enum('full_time','part_time','contract') NOT NULL DEFAULT 'full_time' COMMENT '雇佣类型',
`hire_date` date DEFAULT NULL COMMENT '入职日期',
`salary` decimal(10,2) DEFAULT NULL COMMENT '薪资',
`performance_rating` decimal(3,2) DEFAULT '0.00' COMMENT '绩效评分(0-5分)',
`status` enum('active','inactive','on_leave','suspended') NOT NULL DEFAULT 'active' COMMENT '状态',
`notes` text COMMENT '备注',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `phone` (`phone`),
UNIQUE KEY `id_card` (`id_card`),
UNIQUE KEY `driver_license` (`driver_license`),
KEY `idx_status` (`status`),
KEY `idx_license_type` (`license_type`),
KEY `idx_current_vehicle_id` (`current_vehicle_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='司机表';
-- =====================================================
-- 4. 车辆表 (vehicles)
-- =====================================================
DROP TABLE IF EXISTS `vehicles`;
CREATE TABLE `vehicles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '车辆ID',
`license_plate` varchar(20) NOT NULL COMMENT '车牌号',
`vehicle_type` varchar(50) NOT NULL COMMENT '车辆类型',
`capacity` int(11) NOT NULL COMMENT '载重能力(公斤)',
`driver_id` bigint(20) NOT NULL COMMENT '司机ID',
`status` enum('available','in_use','maintenance','retired') NOT NULL DEFAULT 'available' COMMENT '车辆状态',
`last_maintenance_date` date DEFAULT NULL COMMENT '上次维护日期',
`next_maintenance_date` date DEFAULT NULL COMMENT '下次维护日期',
`insurance_expiry_date` date DEFAULT NULL COMMENT '保险到期日期',
`registration_expiry_date` date DEFAULT NULL COMMENT '注册到期日期',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `license_plate` (`license_plate`),
KEY `idx_driver_id` (`driver_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆表';
-- =====================================================
-- 5. 订单表 (orders)
-- =====================================================
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`orderNo` varchar(50) NOT NULL COMMENT '订单编号',
`buyerId` bigint(20) NOT NULL COMMENT '买方ID',
`buyerName` varchar(100) NOT NULL COMMENT '买方名称',
`supplierId` bigint(20) NOT NULL COMMENT '供应商ID',
`supplierName` varchar(100) NOT NULL COMMENT '供应商名称',
`traderId` bigint(20) DEFAULT NULL COMMENT '贸易商ID',
`traderName` varchar(100) DEFAULT NULL COMMENT '贸易商名称',
`cattleBreed` varchar(20) NOT NULL COMMENT '牛的品种',
`cattleCount` int(11) NOT NULL COMMENT '牛的数量',
`expectedWeight` decimal(10,2) NOT NULL COMMENT '预计总重量',
`actualWeight` decimal(10,2) DEFAULT NULL COMMENT '实际总重量',
`unitPrice` decimal(10,2) NOT NULL COMMENT '单价(元/公斤)',
`totalAmount` decimal(15,2) NOT NULL COMMENT '订单总金额',
`deliveryAddress` varchar(200) NOT NULL COMMENT '交货地址',
`deliveryDate` date NOT NULL COMMENT '交货日期',
`paymentMethod` enum('cash','bank_transfer','online_payment') NOT NULL COMMENT '支付方式',
`paymentStatus` enum('unpaid','partial_paid','paid','refunded') NOT NULL DEFAULT 'unpaid' COMMENT '支付状态',
`orderStatus` enum('pending','confirmed','in_production','shipped','delivered','completed','cancelled') NOT NULL DEFAULT 'pending' COMMENT '订单状态',
`notes` text COMMENT '订单备注',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `orderNo` (`orderNo`),
KEY `idx_buyerId` (`buyerId`),
KEY `idx_supplierId` (`supplierId`),
KEY `idx_traderId` (`traderId`),
KEY `idx_orderStatus` (`orderStatus`),
KEY `idx_paymentStatus` (`paymentStatus`),
KEY `idx_deliveryDate` (`deliveryDate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
-- =====================================================
-- 6. 支付表 (payments)
-- =====================================================
DROP TABLE IF EXISTS `payments`;
CREATE TABLE `payments` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '支付ID',
`order_id` bigint(20) NOT NULL COMMENT '订单ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`amount` decimal(15,2) NOT NULL COMMENT '支付金额',
`paid_amount` decimal(15,2) DEFAULT NULL COMMENT '实际支付金额',
`payment_type` enum('wechat','alipay','bank') NOT NULL COMMENT '支付类型',
`payment_method` enum('mini_program','app','web') NOT NULL COMMENT '支付方式',
`payment_no` varchar(50) NOT NULL COMMENT '支付单号',
`third_party_id` varchar(100) DEFAULT NULL COMMENT '第三方支付ID',
`status` enum('pending','paid','failed','refunded') NOT NULL DEFAULT 'pending' COMMENT '支付状态',
`paid_time` datetime DEFAULT NULL COMMENT '支付时间',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `payment_no` (`payment_no`),
KEY `idx_order_id` (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付表';
-- =====================================================
-- 7. 运输表 (transports)
-- =====================================================
DROP TABLE IF EXISTS `transports`;
CREATE TABLE `transports` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '运输ID',
`order_id` bigint(20) NOT NULL COMMENT '关联订单ID',
`driver_id` bigint(20) NOT NULL COMMENT '司机ID',
`vehicle_id` bigint(20) NOT NULL COMMENT '车辆ID',
`start_location` varchar(255) NOT NULL COMMENT '起始地点',
`end_location` varchar(255) NOT NULL COMMENT '目的地',
`scheduled_start_time` datetime NOT NULL COMMENT '计划开始时间',
`actual_start_time` datetime DEFAULT NULL COMMENT '实际开始时间',
`scheduled_end_time` datetime NOT NULL COMMENT '计划结束时间',
`actual_end_time` datetime DEFAULT NULL COMMENT '实际结束时间',
`status` enum('scheduled','in_transit','completed','cancelled') NOT NULL DEFAULT 'scheduled' COMMENT '运输状态',
`estimated_arrival_time` datetime DEFAULT NULL COMMENT '预计到达时间',
`distance` decimal(8,2) DEFAULT NULL COMMENT '运输距离(公里)',
`fuel_cost` decimal(10,2) DEFAULT NULL COMMENT '燃油费用',
`toll_cost` decimal(10,2) DEFAULT NULL COMMENT '过路费',
`other_cost` decimal(10,2) DEFAULT NULL COMMENT '其他费用',
`total_cost` decimal(10,2) DEFAULT NULL COMMENT '总费用',
`notes` text COMMENT '运输备注',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_driver_id` (`driver_id`),
KEY `idx_vehicle_id` (`vehicle_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运输表';
-- =====================================================
-- 8. 运输跟踪表 (transport_tracks)
-- =====================================================
DROP TABLE IF EXISTS `transport_tracks`;
CREATE TABLE `transport_tracks` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '跟踪记录ID',
`transport_id` bigint(20) NOT NULL COMMENT '运输ID',
`order_id` bigint(20) NOT NULL COMMENT '订单ID',
`driver_id` bigint(20) NOT NULL COMMENT '司机ID',
`latitude` decimal(10,8) NOT NULL COMMENT '纬度',
`longitude` decimal(11,8) NOT NULL COMMENT '经度',
`speed` decimal(5,2) DEFAULT NULL COMMENT '速度(km/h)',
`direction` decimal(5,2) DEFAULT NULL COMMENT '方向角度',
`cattle_status` varchar(20) DEFAULT NULL COMMENT '牛只状态',
`temperature` decimal(5,2) DEFAULT NULL COMMENT '温度(℃)',
`humidity` decimal(5,2) DEFAULT NULL COMMENT '湿度(%)',
`video_url` varchar(255) DEFAULT NULL COMMENT '视频URL',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_transport_id` (`transport_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_driver_id` (`driver_id`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运输跟踪表';
-- =====================================================
-- 9. 质检记录表 (quality_records)
-- =====================================================
DROP TABLE IF EXISTS `quality_records`;
CREATE TABLE `quality_records` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '质检记录ID',
`order_id` bigint(20) NOT NULL COMMENT '关联订单ID',
`inspector_id` bigint(20) NOT NULL COMMENT '检验员ID',
`inspection_type` enum('pre_loading','in_transit','arrival','final') NOT NULL COMMENT '检验类型',
`inspection_date` datetime NOT NULL COMMENT '检验日期',
`location` varchar(200) NOT NULL COMMENT '检验地点',
`cattle_count_expected` int(11) NOT NULL COMMENT '预期牛只数量',
`cattle_count_actual` int(11) NOT NULL COMMENT '实际牛只数量',
`weight_expected` decimal(10,2) DEFAULT NULL COMMENT '预期总重量(kg)',
`weight_actual` decimal(10,2) DEFAULT NULL COMMENT '实际总重量(kg)',
`health_status` enum('excellent','good','fair','poor') NOT NULL COMMENT '健康状况',
`breed_verification` tinyint(1) NOT NULL DEFAULT '1' COMMENT '品种验证是否通过',
`age_range_verification` tinyint(1) NOT NULL DEFAULT '1' COMMENT '年龄范围验证是否通过',
`quarantine_certificate` varchar(255) DEFAULT NULL COMMENT '检疫证明文件路径',
`health_certificate` varchar(255) DEFAULT NULL COMMENT '健康证明文件路径',
`photos` json DEFAULT NULL COMMENT '检验照片路径数组(JSON格式)',
`videos` json DEFAULT NULL COMMENT '检验视频路径数组(JSON格式)',
`quality_issues` json DEFAULT NULL COMMENT '质量问题记录(JSON格式)',
`overall_rating` decimal(3,2) NOT NULL DEFAULT '0.00' COMMENT '综合评分(0-5分)',
`pass_status` enum('passed','conditional_pass','failed') NOT NULL COMMENT '检验结果',
`rejection_reason` text COMMENT '拒收原因',
`inspector_notes` text COMMENT '检验员备注',
`buyer_confirmation` tinyint(1) NOT NULL DEFAULT '0' COMMENT '买方确认',
`buyer_notes` text COMMENT '买方备注',
`confirmation_time` datetime DEFAULT NULL COMMENT '确认时间',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_inspector_id` (`inspector_id`),
KEY `idx_inspection_type` (`inspection_type`),
KEY `idx_inspection_date` (`inspection_date`),
KEY `idx_pass_status` (`pass_status`),
KEY `idx_buyer_confirmation` (`buyer_confirmation`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质检记录表';
-- =====================================================
-- 10. 结算表 (settlements)
-- =====================================================
DROP TABLE IF EXISTS `settlements`;
CREATE TABLE `settlements` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '结算记录ID',
`order_id` bigint(20) NOT NULL COMMENT '关联订单ID',
`settlement_no` varchar(50) NOT NULL COMMENT '结算单号',
`settlement_type` enum('advance','final','full') NOT NULL COMMENT '结算类型',
`cattle_count` int(11) NOT NULL COMMENT '结算牛只数量',
`unit_price` decimal(10,2) NOT NULL COMMENT '单价(元/公斤)',
`total_weight` decimal(10,2) NOT NULL COMMENT '总重量(公斤)',
`gross_amount` decimal(15,2) NOT NULL COMMENT '应付总金额',
`deduction_amount` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '扣款金额',
`deduction_reason` text COMMENT '扣款原因',
`net_amount` decimal(15,2) NOT NULL COMMENT '实付金额',
`payment_method` enum('cash','bank_transfer','check') NOT NULL COMMENT '支付方式',
`payment_status` enum('pending','paid','overdue') NOT NULL DEFAULT 'pending' COMMENT '支付状态',
`payment_date` date DEFAULT NULL COMMENT '支付日期',
`invoice_required` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否需要发票',
`invoice_type` enum('ordinary','special') DEFAULT NULL COMMENT '发票类型',
`invoice_status` enum('not_issued','issued','received') DEFAULT NULL COMMENT '发票状态',
`invoice_number` varchar(50) DEFAULT NULL COMMENT '发票号码',
`approver_id` bigint(20) DEFAULT NULL COMMENT '审批人ID',
`approval_status` enum('pending','approved','rejected') NOT NULL DEFAULT 'pending' COMMENT '审批状态',
`approval_time` datetime DEFAULT NULL COMMENT '审批时间',
`approval_notes` text COMMENT '审批备注',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `settlement_no` (`settlement_no`),
KEY `idx_order_id` (`order_id`),
KEY `idx_settlement_type` (`settlement_type`),
KEY `idx_payment_status` (`payment_status`),
KEY `idx_approval_status` (`approval_status`),
KEY `idx_approver_id` (`approver_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='结算表';
-- =====================================================
-- 外键约束
-- =====================================================
-- 订单表外键
ALTER TABLE `orders` ADD CONSTRAINT `fk_orders_buyer` FOREIGN KEY (`buyerId`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `orders` ADD CONSTRAINT `fk_orders_supplier` FOREIGN KEY (`supplierId`) REFERENCES `suppliers` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `orders` ADD CONSTRAINT `fk_orders_trader` FOREIGN KEY (`traderId`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- 支付表外键
ALTER TABLE `payments` ADD CONSTRAINT `fk_payments_order` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `payments` ADD CONSTRAINT `fk_payments_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- 车辆表外键
ALTER TABLE `vehicles` ADD CONSTRAINT `fk_vehicles_driver` FOREIGN KEY (`driver_id`) REFERENCES `drivers` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- 司机表外键
ALTER TABLE `drivers` ADD CONSTRAINT `fk_drivers_vehicle` FOREIGN KEY (`current_vehicle_id`) REFERENCES `vehicles` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- 运输表外键
ALTER TABLE `transports` ADD CONSTRAINT `fk_transports_order` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `transports` ADD CONSTRAINT `fk_transports_driver` FOREIGN KEY (`driver_id`) REFERENCES `drivers` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `transports` ADD CONSTRAINT `fk_transports_vehicle` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicles` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- 运输跟踪表外键
ALTER TABLE `transport_tracks` ADD CONSTRAINT `fk_transport_tracks_transport` FOREIGN KEY (`transport_id`) REFERENCES `transports` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `transport_tracks` ADD CONSTRAINT `fk_transport_tracks_order` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `transport_tracks` ADD CONSTRAINT `fk_transport_tracks_driver` FOREIGN KEY (`driver_id`) REFERENCES `drivers` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- 质检记录表外键
ALTER TABLE `quality_records` ADD CONSTRAINT `fk_quality_records_order` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `quality_records` ADD CONSTRAINT `fk_quality_records_inspector` FOREIGN KEY (`inspector_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- 结算表外键
ALTER TABLE `settlements` ADD CONSTRAINT `fk_settlements_order` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `settlements` ADD CONSTRAINT `fk_settlements_approver` FOREIGN KEY (`approver_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- 恢复外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- =====================================================
-- 完成数据库表结构创建
-- =====================================================

View File

@@ -0,0 +1,128 @@
-- =====================================================
-- 牛牛商城测试数据初始化脚本
-- 创建时间: 2024-01-21
-- 描述: 插入测试数据,用于开发和测试环境
-- =====================================================
-- 设置字符集
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- =====================================================
-- 1. 用户测试数据
-- =====================================================
INSERT INTO `users` (`id`, `uuid`, `username`, `password_hash`, `nickname`, `real_name`, `phone`, `email`, `user_type`, `company_name`, `status`, `registration_source`) VALUES
(1, 'admin-uuid-001', 'admin', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '系统管理员', '张三', '13800138001', 'admin@niumall.com', 'admin', '牛牛商城', 'active', 'admin_create'),
(2, 'buyer-uuid-001', 'buyer001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '采购商李四', '李四', '13800138002', 'buyer001@example.com', 'buyer', '大华肉业有限公司', 'active', 'web'),
(3, 'buyer-uuid-002', 'buyer002', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '采购商王五', '王五', '13800138003', 'buyer002@example.com', 'buyer', '鑫源食品集团', 'active', 'web'),
(4, 'trader-uuid-001', 'trader001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '贸易商赵六', '赵六', '13800138004', 'trader001@example.com', 'trader', '中原贸易公司', 'active', 'web'),
(5, 'staff-uuid-001', 'staff001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '质检员小明', '陈明', '13800138005', 'staff001@niumall.com', 'staff', '牛牛商城', 'active', 'admin_create'),
(6, 'staff-uuid-002', 'staff002', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '质检员小红', '李红', '13800138006', 'staff002@niumall.com', 'staff', '牛牛商城', 'active', 'admin_create');
-- =====================================================
-- 2. 供应商测试数据
-- =====================================================
INSERT INTO `suppliers` (`id`, `name`, `code`, `contact`, `phone`, `email`, `address`, `region`, `qualification_level`, `cattle_types`, `capacity`, `rating`, `cooperation_start_date`, `status`) VALUES
(1, '内蒙古草原牧业有限公司', 'SUP001', '张牧民', '13900139001', 'contact@nmgcy.com', '内蒙古呼和浩特市赛罕区草原路123号', '内蒙古', 'A', '["西门塔尔牛", "安格斯牛", "夏洛莱牛"]', 500, 4.8, '2023-01-15', 'active'),
(2, '山东鲁西黄牛养殖场', 'SUP002', '李养牛', '13900139002', 'contact@sdlx.com', '山东济宁市嘉祥县畜牧路456号', '山东', 'A', '["鲁西黄牛", "利木赞牛"]', 300, 4.6, '2023-03-20', 'active'),
(3, '河南豫南肉牛合作社', 'SUP003', '王合作', '13900139003', 'contact@hnyn.com', '河南南阳市宛城区牧业大道789号', '河南', 'B', '["西门塔尔牛", "夏洛莱牛"]', 200, 4.2, '2023-05-10', 'active'),
(4, '新疆天山牧业集团', 'SUP004', '马天山', '13900139004', 'contact@xjts.com', '新疆乌鲁木齐市天山区牧场路321号', '新疆', 'A', '["安格斯牛", "海福特牛"]', 400, 4.7, '2023-02-28', 'active'),
(5, '黑龙江北大荒牧业', 'SUP005', '刘北方', '13900139005', 'contact@hlbdh.com', '黑龙江哈尔滨市道里区牧业街654号', '黑龙江', 'B', '["西门塔尔牛", "利木赞牛"]', 250, 4.3, '2023-04-15', 'active');
-- =====================================================
-- 3. 司机测试数据
-- =====================================================
INSERT INTO `drivers` (`id`, `name`, `phone`, `id_card`, `driver_license`, `license_type`, `license_expiry_date`, `emergency_contact`, `emergency_phone`, `employment_type`, `hire_date`, `salary`, `performance_rating`, `status`) VALUES
(1, '张运输', '13700137001', '110101198001011234', 'A2001234567890', 'A2', '2025-12-31', '张妻子', '13700137101', 'full_time', '2023-01-10', 8000.00, 4.5, 'active'),
(2, '李司机', '13700137002', '110101198002022345', 'A2001234567891', 'A2', '2026-06-30', '李父亲', '13700137102', 'full_time', '2023-02-15', 7500.00, 4.3, 'active'),
(3, '王师傅', '13700137003', '110101198003033456', 'A2001234567892', 'A2', '2025-09-30', '王儿子', '13700137103', 'full_time', '2023-03-20', 7800.00, 4.6, 'active'),
(4, '赵老板', '13700137004', '110101198004044567', 'A2001234567893', 'A2', '2026-03-31', '赵妻子', '13700137104', 'contract', '2023-04-10', 9000.00, 4.8, 'active'),
(5, '陈快递', '13700137005', '110101198005055678', 'A2001234567894', 'A2', '2025-11-30', '陈母亲', '13700137105', 'part_time', '2023-05-05', 6500.00, 4.1, 'active');
-- =====================================================
-- 4. 车辆测试数据
-- =====================================================
INSERT INTO `vehicles` (`id`, `license_plate`, `vehicle_type`, `capacity`, `driver_id`, `status`, `last_maintenance_date`, `next_maintenance_date`, `insurance_expiry_date`, `registration_expiry_date`) VALUES
(1, '京A12345', '大型货车', 15000, 1, 'available', '2024-01-15', '2024-04-15', '2024-12-31', '2025-01-10'),
(2, '京B23456', '中型货车', 10000, 2, 'available', '2024-01-20', '2024-04-20', '2024-11-30', '2025-02-15'),
(3, '京C34567', '大型货车', 18000, 3, 'in_use', '2024-01-10', '2024-04-10', '2024-10-31', '2025-03-20'),
(4, '京D45678', '特大型货车', 25000, 4, 'available', '2024-01-25', '2024-04-25', '2025-01-31', '2025-04-10'),
(5, '京E56789', '中型货车', 12000, 5, 'maintenance', '2024-01-05', '2024-04-05', '2024-09-30', '2025-05-05');
-- =====================================================
-- 5. 订单测试数据
-- =====================================================
INSERT INTO `orders` (`id`, `orderNo`, `buyerId`, `buyerName`, `supplierId`, `supplierName`, `traderId`, `traderName`, `cattleBreed`, `cattleCount`, `expectedWeight`, `actualWeight`, `unitPrice`, `totalAmount`, `deliveryAddress`, `deliveryDate`, `paymentMethod`, `paymentStatus`, `orderStatus`) VALUES
(1, 'ORD202401001', 2, '大华肉业有限公司', 1, '内蒙古草原牧业有限公司', 4, '中原贸易公司', '西门塔尔牛', 50, 25000.00, 24800.00, 28.50, 706800.00, '北京市朝阳区肉联厂路100号', '2024-02-15', 'bank_transfer', 'partial_paid', 'shipped'),
(2, 'ORD202401002', 3, '鑫源食品集团', 2, '山东鲁西黄牛养殖场', NULL, NULL, '鲁西黄牛', 30, 18000.00, NULL, 26.80, 482400.00, '上海市浦东新区食品工业园区200号', '2024-02-20', 'bank_transfer', 'unpaid', 'confirmed'),
(3, 'ORD202401003', 2, '大华肉业有限公司', 3, '河南豫南肉牛合作社', NULL, NULL, '夏洛莱牛', 40, 22000.00, NULL, 29.20, 642400.00, '北京市朝阳区肉联厂路100号', '2024-02-25', 'online_payment', 'unpaid', 'pending'),
(4, 'ORD202401004', 3, '鑫源食品集团', 4, '新疆天山牧业集团', 4, '中原贸易公司', '安格斯牛', 60, 32000.00, NULL, 32.00, 1024000.00, '上海市浦东新区食品工业园区200号', '2024-03-01', 'bank_transfer', 'unpaid', 'pending'),
(5, 'ORD202401005', 2, '大华肉业有限公司', 5, '黑龙江北大荒牧业', NULL, NULL, '西门塔尔牛', 35, 19500.00, NULL, 27.50, 536250.00, '北京市朝阳区肉联厂路100号', '2024-03-05', 'bank_transfer', 'unpaid', 'pending');
-- =====================================================
-- 6. 支付测试数据
-- =====================================================
INSERT INTO `payments` (`id`, `order_id`, `user_id`, `amount`, `paid_amount`, `payment_type`, `payment_method`, `payment_no`, `third_party_id`, `status`, `paid_time`) VALUES
(1, 1, 2, 353400.00, 353400.00, 'bank', 'web', 'PAY202401001001', 'BANK20240115001', 'paid', '2024-01-15 14:30:00'),
(2, 1, 2, 353400.00, NULL, 'bank', 'web', 'PAY202401001002', NULL, 'pending', NULL),
(3, 2, 3, 241200.00, NULL, 'bank', 'web', 'PAY202401002001', NULL, 'pending', NULL),
(4, 2, 3, 241200.00, NULL, 'bank', 'web', 'PAY202401002002', NULL, 'pending', NULL);
-- =====================================================
-- 7. 运输测试数据
-- =====================================================
INSERT INTO `transports` (`id`, `order_id`, `driver_id`, `vehicle_id`, `start_location`, `end_location`, `scheduled_start_time`, `actual_start_time`, `scheduled_end_time`, `actual_end_time`, `status`, `estimated_arrival_time`, `distance`, `fuel_cost`, `toll_cost`, `other_cost`, `total_cost`) VALUES
(1, 1, 3, 3, '内蒙古呼和浩特市赛罕区草原路123号', '北京市朝阳区肉联厂路100号', '2024-02-10 08:00:00', '2024-02-10 08:30:00', '2024-02-12 18:00:00', NULL, 'in_transit', '2024-02-12 16:00:00', 450.5, 1800.00, 200.00, 100.00, 2100.00),
(2, 2, 2, 2, '山东济宁市嘉祥县畜牧路456号', '上海市浦东新区食品工业园区200号', '2024-02-18 06:00:00', NULL, '2024-02-20 20:00:00', NULL, 'scheduled', '2024-02-20 18:00:00', 680.2, 2400.00, 350.00, 150.00, 2900.00);
-- =====================================================
-- 8. 运输跟踪测试数据
-- =====================================================
INSERT INTO `transport_tracks` (`id`, `transport_id`, `order_id`, `driver_id`, `latitude`, `longitude`, `speed`, `direction`, `cattle_status`, `temperature`, `humidity`, `created_at`) VALUES
(1, 1, 1, 3, 40.8518, 111.7519, 65.5, 135.2, '正常', 2.5, 45.0, '2024-02-10 10:00:00'),
(2, 1, 1, 3, 40.9234, 111.6789, 68.2, 140.8, '正常', 3.2, 42.0, '2024-02-10 12:00:00'),
(3, 1, 1, 3, 41.0567, 111.5432, 62.8, 138.5, '正常', 4.1, 38.0, '2024-02-10 14:00:00'),
(4, 1, 1, 3, 41.2345, 111.3456, 70.1, 142.3, '正常', 5.8, 35.0, '2024-02-10 16:00:00'),
(5, 1, 1, 3, 41.4567, 111.1234, 66.7, 139.7, '正常', 6.5, 32.0, '2024-02-10 18:00:00');
-- =====================================================
-- 9. 质检记录测试数据
-- =====================================================
INSERT INTO `quality_records` (`id`, `order_id`, `inspector_id`, `inspection_type`, `inspection_date`, `location`, `cattle_count_expected`, `cattle_count_actual`, `weight_expected`, `weight_actual`, `health_status`, `breed_verification`, `age_range_verification`, `overall_rating`, `pass_status`, `inspector_notes`, `buyer_confirmation`) VALUES
(1, 1, 5, 'pre_loading', '2024-02-10 07:00:00', '内蒙古呼和浩特市赛罕区草原路123号', 50, 50, 25000.00, 24800.00, 'excellent', 1, 1, 4.8, 'passed', '牛只健康状况良好,品种纯正,符合订单要求', 0),
(2, 1, 6, 'arrival', '2024-02-12 15:30:00', '北京市朝阳区肉联厂路100号', 50, 50, 24800.00, 24800.00, 'good', 1, 1, 4.5, 'passed', '运输过程中牛只状态稳定,无明显损失', 1);
-- =====================================================
-- 10. 结算测试数据
-- =====================================================
INSERT INTO `settlements` (`id`, `order_id`, `settlement_no`, `settlement_type`, `cattle_count`, `unit_price`, `total_weight`, `gross_amount`, `deduction_amount`, `deduction_reason`, `net_amount`, `payment_method`, `payment_status`, `payment_date`, `invoice_required`, `invoice_type`, `invoice_status`, `approver_id`, `approval_status`, `approval_time`) VALUES
(1, 1, 'SET202401001', 'advance', 50, 28.50, 24800.00, 706800.00, 0.00, NULL, 353400.00, 'bank_transfer', 'paid', '2024-01-15', 1, 'special', 'issued', 1, 'approved', '2024-01-14 16:00:00'),
(2, 1, 'SET202401002', 'final', 50, 28.50, 24800.00, 706800.00, 0.00, NULL, 353400.00, 'bank_transfer', 'pending', NULL, 1, 'special', 'not_issued', 1, 'pending', NULL);
-- =====================================================
-- 更新司机当前车辆关联
-- =====================================================
UPDATE `drivers` SET `current_vehicle_id` = 1 WHERE `id` = 1;
UPDATE `drivers` SET `current_vehicle_id` = 2 WHERE `id` = 2;
UPDATE `drivers` SET `current_vehicle_id` = 3 WHERE `id` = 3;
UPDATE `drivers` SET `current_vehicle_id` = 4 WHERE `id` = 4;
UPDATE `drivers` SET `current_vehicle_id` = 5 WHERE `id` = 5;
-- 恢复外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- =====================================================
-- 完成测试数据插入
-- =====================================================
SELECT '测试数据插入完成!' as message;
SELECT
(SELECT COUNT(*) FROM users) as users_count,
(SELECT COUNT(*) FROM suppliers) as suppliers_count,
(SELECT COUNT(*) FROM drivers) as drivers_count,
(SELECT COUNT(*) FROM vehicles) as vehicles_count,
(SELECT COUNT(*) FROM orders) as orders_count,
(SELECT COUNT(*) FROM payments) as payments_count,
(SELECT COUNT(*) FROM transports) as transports_count,
(SELECT COUNT(*) FROM transport_tracks) as transport_tracks_count,
(SELECT COUNT(*) FROM quality_records) as quality_records_count,
(SELECT COUNT(*) FROM settlements) as settlements_count;

View File

@@ -0,0 +1,292 @@
#!/usr/bin/env node
/**
* 基于Node.js的数据库初始化脚本
* 使用mysql2库连接数据库并执行SQL脚本
*/
const mysql = require('../../backend/node_modules/mysql2/promise');
const fs = require('fs').promises;
const path = require('path');
// 颜色输出
const colors = {
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
reset: '\x1b[0m'
};
// 默认配置
const defaultConfig = {
host: 'localhost',
port: 3306,
user: 'root',
password: '',
database: 'niumall'
};
// 显示帮助信息
function showHelp() {
console.log('数据库初始化脚本 (Node.js版本)');
console.log('');
console.log('用法: node init_with_node.js [选项]');
console.log('');
console.log('选项:');
console.log(' --host HOST 数据库主机地址 (默认: localhost)');
console.log(' --port PORT 数据库端口 (默认: 3306)');
console.log(' --user USER 数据库用户名 (默认: root)');
console.log(' --password PASS 数据库密码');
console.log(' --database DB 数据库名称 (默认: niumall)');
console.log(' --skip-structure 跳过表结构创建');
console.log(' --skip-data 跳过测试数据插入');
console.log(' --help 显示此帮助信息');
console.log('');
console.log('示例:');
console.log(' node init_with_node.js --user root --password secret');
console.log(' node init_with_node.js --host 192.168.1.100 --database test');
}
// 解析命令行参数
function parseArgs() {
const args = process.argv.slice(2);
const config = { ...defaultConfig };
let skipStructure = false;
let skipData = false;
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--host':
config.host = args[++i];
break;
case '--port':
config.port = parseInt(args[++i]);
break;
case '--user':
config.user = args[++i];
break;
case '--password':
config.password = args[++i];
break;
case '--database':
config.database = args[++i];
break;
case '--skip-structure':
skipStructure = true;
break;
case '--skip-data':
skipData = true;
break;
case '--help':
showHelp();
process.exit(0);
break;
default:
console.error(`未知参数: ${args[i]}`);
showHelp();
process.exit(1);
}
}
return { config, skipStructure, skipData };
}
// 测试数据库连接
async function testConnection(config) {
console.log(`${colors.yellow}正在测试数据库连接...${colors.reset}`);
try {
const connection = await mysql.createConnection({
host: config.host,
port: config.port,
user: config.user,
password: config.password
});
await connection.execute('SELECT 1');
await connection.end();
console.log(`${colors.green}✅ 数据库连接成功!${colors.reset}`);
return true;
} catch (error) {
console.error(`${colors.red}❌ 数据库连接失败!${colors.reset}`);
console.error(`错误信息: ${error.message}`);
return false;
}
}
// 创建数据库
async function createDatabase(config) {
console.log(`${colors.yellow}正在创建数据库 '${config.database}'...${colors.reset}`);
try {
const connection = await mysql.createConnection({
host: config.host,
port: config.port,
user: config.user,
password: config.password
});
await connection.execute(`CREATE DATABASE IF NOT EXISTS \`${config.database}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
await connection.end();
console.log(`${colors.green}✅ 数据库创建成功!${colors.reset}`);
return true;
} catch (error) {
console.error(`${colors.red}❌ 数据库创建失败!${colors.reset}`);
console.error(`错误信息: ${error.message}`);
return false;
}
}
// 执行SQL文件
async function executeSqlFile(config, filePath, description) {
console.log(`${colors.yellow}正在执行 ${description}...${colors.reset}`);
try {
// 读取SQL文件
const sqlContent = await fs.readFile(filePath, 'utf8');
// 连接到指定数据库
const connection = await mysql.createConnection({
host: config.host,
port: config.port,
user: config.user,
password: config.password,
database: config.database,
multipleStatements: true
});
// 分割SQL语句并执行
const statements = sqlContent
.split(';')
.map(stmt => stmt.trim())
.filter(stmt => stmt.length > 0 && !stmt.startsWith('--'));
let successCount = 0;
let errorCount = 0;
for (const statement of statements) {
try {
await connection.execute(statement);
successCount++;
} catch (error) {
errorCount++;
console.warn(`${colors.yellow}⚠️ SQL执行警告: ${error.message}${colors.reset}`);
console.warn(`SQL语句: ${statement.substring(0, 100)}...`);
}
}
await connection.end();
console.log(`${colors.green}${description}完成!${colors.reset}`);
console.log(`成功执行: ${successCount} 条语句,警告: ${errorCount}`);
return true;
} catch (error) {
console.error(`${colors.red}${description}失败!${colors.reset}`);
console.error(`错误信息: ${error.message}`);
return false;
}
}
// 验证表结构
async function verifyTables(config) {
console.log(`${colors.yellow}正在验证表结构...${colors.reset}`);
try {
const connection = await mysql.createConnection({
host: config.host,
port: config.port,
user: config.user,
password: config.password,
database: config.database
});
const [tables] = await connection.execute('SHOW TABLES');
await connection.end();
const tableNames = tables.map(row => Object.values(row)[0]);
console.log(`${colors.green}✅ 数据库验证完成!${colors.reset}`);
console.log(`创建的表 (${tableNames.length}个):`);
tableNames.forEach(name => console.log(` - ${name}`));
return tableNames.length > 0;
} catch (error) {
console.error(`${colors.red}❌ 表结构验证失败!${colors.reset}`);
console.error(`错误信息: ${error.message}`);
return false;
}
}
// 主函数
async function main() {
console.log(`${colors.blue}数据库初始化工具 (Node.js版本)${colors.reset}`);
console.log('==================================');
console.log('');
const { config, skipStructure, skipData } = parseArgs();
// 显示配置信息
console.log(`${colors.blue}=== 数据库初始化配置 ===${colors.reset}`);
console.log(`主机: ${config.host}`);
console.log(`端口: ${config.port}`);
console.log(`用户: ${config.user}`);
console.log(`数据库: ${config.database}`);
console.log(`跳过表结构: ${skipStructure}`);
console.log(`跳过测试数据: ${skipData}`);
console.log('');
let success = true;
// 1. 测试数据库连接
if (!await testConnection(config)) {
process.exit(1);
}
// 2. 创建数据库
if (!await createDatabase(config)) {
process.exit(1);
}
// 3. 执行表结构创建
if (!skipStructure) {
const structureFile = path.join(__dirname, 'init_database.sql');
if (!await executeSqlFile(config, structureFile, '表结构创建')) {
success = false;
}
}
// 4. 插入测试数据
if (!skipData && success) {
const dataFile = path.join(__dirname, 'init_test_data.sql');
if (!await executeSqlFile(config, dataFile, '测试数据插入')) {
success = false;
}
}
// 5. 验证结果
if (success) {
await verifyTables(config);
console.log('');
console.log(`${colors.green}🎉 数据库初始化完成!${colors.reset}`);
process.exit(0);
} else {
console.log('');
console.log(`${colors.red}💥 数据库初始化失败!${colors.reset}`);
process.exit(1);
}
}
// 错误处理
process.on('unhandledRejection', (error) => {
console.error(`${colors.red}未处理的错误:${colors.reset}`, error);
process.exit(1);
});
// 执行主函数
if (require.main === module) {
main();
}

209
scripts/database/run_init.sh Executable file
View File

@@ -0,0 +1,209 @@
#!/bin/bash
# =====================================================
# 牛牛商城数据库初始化执行脚本
# 创建时间: 2024-01-21
# 描述: 一键执行数据库初始化和测试数据插入
# =====================================================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 默认数据库配置
DEFAULT_HOST="localhost"
DEFAULT_PORT="3306"
DEFAULT_USER="root"
DEFAULT_DATABASE="niumall"
# 显示帮助信息
show_help() {
echo -e "${BLUE}牛牛商城数据库初始化脚本${NC}"
echo ""
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " -h, --host HOST 数据库主机地址 (默认: $DEFAULT_HOST)"
echo " -P, --port PORT 数据库端口 (默认: $DEFAULT_PORT)"
echo " -u, --user USER 数据库用户名 (默认: $DEFAULT_USER)"
echo " -p, --password PASS 数据库密码"
echo " -d, --database DB 数据库名称 (默认: $DEFAULT_DATABASE)"
echo " --skip-structure 跳过表结构创建"
echo " --skip-data 跳过测试数据插入"
echo " --help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 -h localhost -u root -p password123 -d niumall"
echo " $0 --host nj-cdb-3pwh2kz1.sql.tencentcdb.com --port 20784 --user jiebanke --password 'aiot741\$12346'"
echo ""
}
# 初始化变量
HOST="$DEFAULT_HOST"
PORT="$DEFAULT_PORT"
USER="$DEFAULT_USER"
PASSWORD=""
DATABASE="$DEFAULT_DATABASE"
SKIP_STRUCTURE=false
SKIP_DATA=false
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-h|--host)
HOST="$2"
shift 2
;;
-P|--port)
PORT="$2"
shift 2
;;
-u|--user)
USER="$2"
shift 2
;;
-p|--password)
PASSWORD="$2"
shift 2
;;
-d|--database)
DATABASE="$2"
shift 2
;;
--skip-structure)
SKIP_STRUCTURE=true
shift
;;
--skip-data)
SKIP_DATA=true
shift
;;
--help)
show_help
exit 0
;;
*)
echo -e "${RED}错误: 未知参数 $1${NC}"
show_help
exit 1
;;
esac
done
# 如果没有提供密码,提示输入
if [ -z "$PASSWORD" ]; then
echo -e "${YELLOW}请输入数据库密码:${NC}"
read -s PASSWORD
fi
# 构建MySQL连接参数
MYSQL_CMD="mysql -h$HOST -P$PORT -u$USER -p$PASSWORD"
# 显示配置信息
echo -e "${BLUE}=== 数据库初始化配置 ===${NC}"
echo -e "主机: ${GREEN}$HOST${NC}"
echo -e "端口: ${GREEN}$PORT${NC}"
echo -e "用户: ${GREEN}$USER${NC}"
echo -e "数据库: ${GREEN}$DATABASE${NC}"
echo -e "跳过表结构: ${GREEN}$SKIP_STRUCTURE${NC}"
echo -e "跳过测试数据: ${GREEN}$SKIP_DATA${NC}"
echo ""
# 测试数据库连接
echo -e "${YELLOW}正在测试数据库连接...${NC}"
if ! $MYSQL_CMD -e "SELECT 1;" > /dev/null 2>&1; then
echo -e "${RED}❌ 数据库连接失败!请检查连接参数。${NC}"
exit 1
fi
echo -e "${GREEN}✅ 数据库连接成功!${NC}"
# 检查数据库是否存在,如果不存在则创建
echo -e "${YELLOW}正在检查数据库 '$DATABASE' 是否存在...${NC}"
DB_EXISTS=$($MYSQL_CMD -e "SHOW DATABASES LIKE '$DATABASE';" | grep -c "$DATABASE")
if [ $DB_EXISTS -eq 0 ]; then
echo -e "${YELLOW}数据库 '$DATABASE' 不存在,正在创建...${NC}"
if $MYSQL_CMD -e "CREATE DATABASE IF NOT EXISTS \`$DATABASE\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"; then
echo -e "${GREEN}✅ 数据库 '$DATABASE' 创建成功!${NC}"
else
echo -e "${RED}❌ 数据库 '$DATABASE' 创建失败!${NC}"
exit 1
fi
else
echo -e "${GREEN}✅ 数据库 '$DATABASE' 已存在。${NC}"
fi
# 执行表结构创建
if [ "$SKIP_STRUCTURE" = false ]; then
echo -e "${YELLOW}正在创建数据库表结构...${NC}"
if $MYSQL_CMD $DATABASE < "$SCRIPT_DIR/init_database.sql"; then
echo -e "${GREEN}✅ 数据库表结构创建成功!${NC}"
else
echo -e "${RED}❌ 数据库表结构创建失败!${NC}"
exit 1
fi
else
echo -e "${YELLOW}⏭️ 跳过表结构创建。${NC}"
fi
# 执行测试数据插入
if [ "$SKIP_DATA" = false ]; then
echo -e "${YELLOW}正在插入测试数据...${NC}"
if $MYSQL_CMD $DATABASE < "$SCRIPT_DIR/init_test_data.sql"; then
echo -e "${GREEN}✅ 测试数据插入成功!${NC}"
else
echo -e "${RED}❌ 测试数据插入失败!${NC}"
exit 1
fi
else
echo -e "${YELLOW}⏭️ 跳过测试数据插入。${NC}"
fi
# 显示完成信息
echo ""
echo -e "${GREEN}🎉 数据库初始化完成!${NC}"
echo ""
echo -e "${BLUE}=== 初始化摘要 ===${NC}"
# 显示表统计信息
echo -e "${YELLOW}正在统计表记录数...${NC}"
$MYSQL_CMD $DATABASE -e "
SELECT
'用户表' as table_name, COUNT(*) as record_count FROM users
UNION ALL
SELECT
'供应商表' as table_name, COUNT(*) as record_count FROM suppliers
UNION ALL
SELECT
'司机表' as table_name, COUNT(*) as record_count FROM drivers
UNION ALL
SELECT
'车辆表' as table_name, COUNT(*) as record_count FROM vehicles
UNION ALL
SELECT
'订单表' as table_name, COUNT(*) as record_count FROM orders
UNION ALL
SELECT
'支付表' as table_name, COUNT(*) as record_count FROM payments
UNION ALL
SELECT
'运输表' as table_name, COUNT(*) as record_count FROM transports
UNION ALL
SELECT
'运输跟踪表' as table_name, COUNT(*) as record_count FROM transport_tracks
UNION ALL
SELECT
'质检记录表' as table_name, COUNT(*) as record_count FROM quality_records
UNION ALL
SELECT
'结算表' as table_name, COUNT(*) as record_count FROM settlements;
"
echo ""
echo -e "${GREEN}数据库初始化脚本执行完毕!${NC}"
echo -e "${BLUE}您现在可以启动应用程序进行测试。${NC}"

View File

@@ -0,0 +1,175 @@
#!/bin/bash
# 数据库连接测试脚本
# 用于验证数据库连接配置是否正确
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 默认配置
HOST="localhost"
PORT="3306"
USER="root"
PASSWORD=""
DATABASE="niumall"
# 显示帮助信息
show_help() {
echo "数据库连接测试脚本"
echo ""
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " -h, --host HOST 数据库主机地址 (默认: localhost)"
echo " -P, --port PORT 数据库端口 (默认: 3306)"
echo " -u, --user USER 数据库用户名 (默认: root)"
echo " -p, --password PASS 数据库密码"
echo " -d, --database DB 数据库名称 (默认: niumall)"
echo " --help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 -u root -p password"
echo " $0 --host 192.168.1.100 --port 3306 -u admin -p secret"
}
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-h|--host)
HOST="$2"
shift 2
;;
-P|--port)
PORT="$2"
shift 2
;;
-u|--user)
USER="$2"
shift 2
;;
-p|--password)
PASSWORD="$2"
shift 2
;;
-d|--database)
DATABASE="$2"
shift 2
;;
--help)
show_help
exit 0
;;
*)
echo "未知参数: $1"
show_help
exit 1
;;
esac
done
# 检查MySQL客户端是否安装
check_mysql_client() {
if ! command -v mysql &> /dev/null; then
echo -e "${RED}❌ MySQL客户端未安装${NC}"
echo ""
echo "请安装MySQL客户端"
echo " macOS: brew install mysql-client"
echo " Ubuntu: sudo apt-get install mysql-client"
echo " CentOS: sudo yum install mysql"
return 1
fi
return 0
}
# 测试数据库连接
test_connection() {
echo -e "${BLUE}=== 数据库连接测试 ===${NC}"
echo "主机: $HOST"
echo "端口: $PORT"
echo "用户: $USER"
echo "数据库: $DATABASE"
echo ""
# 构建MySQL连接命令
MYSQL_CMD="mysql -h$HOST -P$PORT -u$USER"
if [[ -n "$PASSWORD" ]]; then
MYSQL_CMD="$MYSQL_CMD -p$PASSWORD"
fi
echo -e "${YELLOW}正在测试连接...${NC}"
# 测试基本连接
if echo "SELECT 1;" | $MYSQL_CMD 2>/dev/null | grep -q "1"; then
echo -e "${GREEN}✅ 数据库连接成功!${NC}"
else
echo -e "${RED}❌ 数据库连接失败!${NC}"
echo ""
echo "可能的原因:"
echo "1. 数据库服务未启动"
echo "2. 主机地址或端口错误"
echo "3. 用户名或密码错误"
echo "4. 网络连接问题"
echo "5. 防火墙阻止连接"
return 1
fi
# 测试数据库是否存在
echo -e "${YELLOW}检查数据库是否存在...${NC}"
if echo "USE $DATABASE;" | $MYSQL_CMD 2>/dev/null; then
echo -e "${GREEN}✅ 数据库 '$DATABASE' 存在${NC}"
else
echo -e "${YELLOW}⚠️ 数据库 '$DATABASE' 不存在,需要创建${NC}"
fi
# 检查用户权限
echo -e "${YELLOW}检查用户权限...${NC}"
GRANTS=$(echo "SHOW GRANTS;" | $MYSQL_CMD 2>/dev/null)
if [[ $? -eq 0 ]]; then
echo -e "${GREEN}✅ 用户权限查询成功${NC}"
echo "用户权限:"
echo "$GRANTS" | grep -E "(GRANT|CREATE|DROP|INSERT|SELECT|UPDATE|DELETE)" | head -3
else
echo -e "${RED}❌ 无法查询用户权限${NC}"
fi
# 检查数据库版本
echo -e "${YELLOW}检查数据库版本...${NC}"
VERSION=$(echo "SELECT VERSION();" | $MYSQL_CMD 2>/dev/null | tail -n +2)
if [[ -n "$VERSION" ]]; then
echo -e "${GREEN}✅ MySQL版本: $VERSION${NC}"
else
echo -e "${RED}❌ 无法获取数据库版本${NC}"
fi
return 0
}
# 主函数
main() {
echo -e "${BLUE}数据库连接测试工具${NC}"
echo "=========================="
echo ""
# 检查MySQL客户端
if ! check_mysql_client; then
exit 1
fi
# 测试连接
if test_connection; then
echo ""
echo -e "${GREEN}🎉 连接测试完成!数据库配置正常。${NC}"
exit 0
else
echo ""
echo -e "${RED}💥 连接测试失败!请检查配置。${NC}"
exit 1
fi
}
# 执行主函数
main