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',