refactor(docs): 简化README结构,更新技术栈和项目结构描述
This commit is contained in:
48
backend/check_orders_structure.js
Normal file
48
backend/check_orders_structure.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 检查orders表的详细结构
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { Sequelize } = require('sequelize');
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME || 'niumall',
|
||||
process.env.DB_USERNAME || 'jiebanke',
|
||||
process.env.DB_PASSWORD || 'aiot741$12346',
|
||||
{
|
||||
host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: process.env.DB_PORT || 20784,
|
||||
dialect: 'mysql',
|
||||
logging: false
|
||||
}
|
||||
);
|
||||
|
||||
async function checkOrdersStructure() {
|
||||
try {
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
|
||||
// 获取orders表的详细结构
|
||||
const [columns] = await sequelize.query("SHOW COLUMNS FROM orders");
|
||||
|
||||
console.log('\n📋 orders表详细结构:');
|
||||
columns.forEach(col => {
|
||||
console.log(` - ${col.Field}: ${col.Type} ${col.Null === 'YES' ? '(可空)' : '(非空)'} ${col.Default ? `默认值: ${col.Default}` : ''}`);
|
||||
});
|
||||
|
||||
// 检查现有订单数据
|
||||
const [orders] = await sequelize.query("SELECT orderNo, paymentStatus, orderStatus FROM orders LIMIT 5");
|
||||
|
||||
console.log('\n📊 现有订单状态示例:');
|
||||
orders.forEach(order => {
|
||||
console.log(` - ${order.orderNo}: 支付状态=${order.paymentStatus}, 订单状态=${order.orderStatus}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkOrdersStructure();
|
||||
549
backend/compatible_data_insert.js
Normal file
549
backend/compatible_data_insert.js
Normal file
@@ -0,0 +1,549 @@
|
||||
/**
|
||||
* 兼容现有表结构的数据插入脚本
|
||||
* 根据实际表结构插入测试数据
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { Sequelize } = require('sequelize');
|
||||
const bcrypt = require('bcrypt');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
// 使用.env文件中的配置
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME || 'niumall',
|
||||
process.env.DB_USERNAME || 'jiebanke',
|
||||
process.env.DB_PASSWORD || 'aiot741$12346',
|
||||
{
|
||||
host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: process.env.DB_PORT || 20784,
|
||||
dialect: 'mysql',
|
||||
logging: (msg) => console.log(`[SQL] ${msg}`),
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 检查现有表结构
|
||||
*/
|
||||
async function checkTableStructure() {
|
||||
console.log('\n🔍 检查现有表结构...');
|
||||
|
||||
try {
|
||||
// 检查suppliers表结构
|
||||
const [supplierColumns] = await sequelize.query("DESCRIBE suppliers");
|
||||
console.log('🏭 suppliers表字段:');
|
||||
const supplierFields = supplierColumns.map(col => col.Field);
|
||||
supplierFields.forEach(field => console.log(` - ${field}`));
|
||||
|
||||
// 检查orders表结构
|
||||
const [orderColumns] = await sequelize.query("DESCRIBE orders");
|
||||
console.log('\n📋 orders表字段:');
|
||||
const orderFields = orderColumns.map(col => col.Field);
|
||||
orderFields.forEach(field => console.log(` - ${field}`));
|
||||
|
||||
return { supplierFields, orderFields };
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查表结构失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入兼容的测试数据
|
||||
*/
|
||||
async function insertCompatibleData() {
|
||||
console.log('\n📊 开始插入兼容的测试数据...');
|
||||
|
||||
try {
|
||||
// 1. 插入管理员和测试用户
|
||||
console.log('👤 插入用户数据...');
|
||||
|
||||
// 检查管理员是否存在
|
||||
const [existingAdmin] = await sequelize.query(
|
||||
"SELECT id FROM users WHERE username = 'admin'"
|
||||
);
|
||||
|
||||
if (existingAdmin.length === 0) {
|
||||
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('✅ 管理员用户已存在');
|
||||
}
|
||||
|
||||
// 测试用户数据 - 使用不同的手机号避免冲突
|
||||
const testUsers = [
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'buyer001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '采购商张三',
|
||||
real_name: '张三',
|
||||
phone: '13900139001',
|
||||
email: 'buyer001@niumall.com',
|
||||
user_type: 'buyer',
|
||||
company_name: '北京牛肉加工厂',
|
||||
company_address: '北京市朝阳区xxx街道'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'trader001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '贸易商李四',
|
||||
real_name: '李四',
|
||||
phone: '13900139002',
|
||||
email: 'trader001@niumall.com',
|
||||
user_type: 'trader',
|
||||
company_name: '上海牛只贸易有限公司',
|
||||
company_address: '上海市浦东新区xxx路'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'supplier001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '供应商王五',
|
||||
real_name: '王五',
|
||||
phone: '13900139003',
|
||||
email: 'supplier001@niumall.com',
|
||||
user_type: 'supplier',
|
||||
company_name: '内蒙古草原牧业',
|
||||
company_address: '内蒙古呼和浩特市'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'driver001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '司机赵六',
|
||||
real_name: '赵六',
|
||||
phone: '13900139004',
|
||||
email: 'driver001@niumall.com',
|
||||
user_type: 'driver',
|
||||
id_card: '110101199001011234'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'staff001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '员工孙七',
|
||||
real_name: '孙七',
|
||||
phone: '13900139005',
|
||||
email: 'staff001@niumall.com',
|
||||
user_type: 'staff',
|
||||
company_name: '牛商城运营中心'
|
||||
}
|
||||
];
|
||||
|
||||
for (const user of testUsers) {
|
||||
// 检查用户是否已存在(检查用户名和手机号)
|
||||
const [existing] = await sequelize.query(
|
||||
"SELECT id FROM users WHERE username = ? OR phone = ?",
|
||||
{ replacements: [user.username, user.phone] }
|
||||
);
|
||||
|
||||
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} 已存在`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 插入供应商数据(使用现有表结构字段)
|
||||
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'
|
||||
},
|
||||
{
|
||||
name: '四川成都优质牧场',
|
||||
code: 'SUP004',
|
||||
contact: '李小川',
|
||||
phone: '13900139004',
|
||||
address: '四川省成都市双流区牧场路101号',
|
||||
region: '四川',
|
||||
qualificationLevel: 'B',
|
||||
cattleTypes: JSON.stringify(['西门塔尔牛', '本地黄牛']),
|
||||
capacity: 150,
|
||||
rating: 4.0,
|
||||
cooperationStartDate: '2023-08-01'
|
||||
},
|
||||
{
|
||||
name: '河北承德绿色牧业',
|
||||
code: 'SUP005',
|
||||
contact: '张承德',
|
||||
phone: '13900139005',
|
||||
address: '河北省承德市双桥区绿色牧场街202号',
|
||||
region: '河北',
|
||||
qualificationLevel: 'C',
|
||||
cattleTypes: JSON.stringify(['夏洛莱牛', '安格斯牛']),
|
||||
capacity: 100,
|
||||
rating: 3.8,
|
||||
cooperationStartDate: '2023-09-15'
|
||||
}
|
||||
];
|
||||
|
||||
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, 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.address, supplier.region, supplier.qualificationLevel, supplier.cattleTypes,
|
||||
supplier.capacity, supplier.rating, supplier.cooperationStartDate, 'active'
|
||||
]
|
||||
});
|
||||
console.log(`✅ 供应商 ${supplier.code} 创建成功`);
|
||||
} else {
|
||||
console.log(`✅ 供应商 ${supplier.code} 已存在`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 插入订单数据(使用现有表结构)
|
||||
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 WHERE code LIKE 'SUP%' LIMIT 3");
|
||||
|
||||
if (buyers.length > 0 && supplierList.length > 0) {
|
||||
const orders = [
|
||||
{
|
||||
orderNo: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '001',
|
||||
buyerId: buyers[0].id,
|
||||
buyerName: buyers[0].nickname,
|
||||
supplierId: supplierList[0].id,
|
||||
supplierName: supplierList[0].name,
|
||||
traderId: traders.length > 0 ? traders[0].id : null,
|
||||
traderName: traders.length > 0 ? traders[0].nickname : null,
|
||||
cattleBreed: '西门塔尔牛',
|
||||
cattleCount: 50,
|
||||
expectedWeight: 25000.00,
|
||||
unitPrice: 32.50,
|
||||
totalAmount: 812500.00,
|
||||
paidAmount: 200000.00,
|
||||
remainingAmount: 612500.00,
|
||||
deliveryAddress: '北京市朝阳区屠宰场',
|
||||
expectedDeliveryDate: '2024-02-15 08:00:00',
|
||||
status: 'confirmed',
|
||||
notes: '要求健康证明齐全,质量等级A级,重量范围450-550kg'
|
||||
},
|
||||
{
|
||||
orderNo: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '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-02-20 10:00:00',
|
||||
status: 'pending',
|
||||
notes: '需要冷链运输,重量范围500-600kg'
|
||||
},
|
||||
{
|
||||
orderNo: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '003',
|
||||
buyerId: buyers[0].id,
|
||||
buyerName: buyers[0].nickname,
|
||||
supplierId: supplierList.length > 2 ? supplierList[2].id : supplierList[0].id,
|
||||
supplierName: supplierList.length > 2 ? supplierList[2].name : supplierList[0].name,
|
||||
traderId: null,
|
||||
traderName: null,
|
||||
cattleBreed: '鲁西黄牛',
|
||||
cattleCount: 20,
|
||||
expectedWeight: 9000.00,
|
||||
unitPrice: 30.00,
|
||||
totalAmount: 270000.00,
|
||||
paidAmount: 80000.00,
|
||||
remainingAmount: 190000.00,
|
||||
deliveryAddress: '天津市滨海新区肉类加工园',
|
||||
expectedDeliveryDate: '2024-02-25 14:00:00',
|
||||
status: 'pending',
|
||||
notes: '本地优质黄牛,肉质鲜美,重量范围400-500kg'
|
||||
}
|
||||
];
|
||||
|
||||
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} 已存在`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 插入系统配置
|
||||
console.log('⚙️ 插入系统配置...');
|
||||
const configs = [
|
||||
['system.name', '活牛采购智能数字化系统', 'string', 'system', '系统名称'],
|
||||
['system.version', '1.0.0', 'string', 'system', '系统版本'],
|
||||
['system.company', '牛商城科技有限公司', 'string', 'system', '公司名称'],
|
||||
['order.auto_confirm_hours', '24', 'number', 'order', '订单自动确认时间(小时)'],
|
||||
['order.max_quantity', '1000', 'number', 'order', '单笔订单最大数量'],
|
||||
['payment.timeout_minutes', '30', 'number', 'payment', '支付超时时间(分钟)'],
|
||||
['payment.min_prepaid_ratio', '0.2', 'number', 'payment', '最低预付比例'],
|
||||
['transport.tracking_interval', '300', 'number', 'transport', '位置跟踪间隔(秒)'],
|
||||
['transport.max_distance', '2000', 'number', 'transport', '最大运输距离(公里)'],
|
||||
['quality.min_score', '80', 'number', 'quality', '质量检验最低分数'],
|
||||
['quality.pass_rate_threshold', '0.95', 'number', 'quality', '合格率阈值'],
|
||||
['notification.sms_enabled', 'true', 'boolean', 'notification', '是否启用短信通知'],
|
||||
['notification.email_enabled', 'true', 'boolean', 'notification', '是否启用邮件通知'],
|
||||
['notification.push_enabled', 'true', 'boolean', 'notification', '是否启用推送通知'],
|
||||
['security.session_timeout', '7200', 'number', 'security', '会话超时时间(秒)'],
|
||||
['security.max_login_attempts', '5', 'number', 'security', '最大登录尝试次数']
|
||||
];
|
||||
|
||||
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'];
|
||||
|
||||
console.log('📊 数据统计:');
|
||||
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 FROM users WHERE user_type = 'admin'"
|
||||
);
|
||||
|
||||
console.log('\n👤 管理员用户:');
|
||||
adminUsers.forEach(user => {
|
||||
console.log(` - ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}`);
|
||||
});
|
||||
|
||||
const [supplierStats] = await sequelize.query(`
|
||||
SELECT qualification_level, COUNT(*) as count
|
||||
FROM suppliers
|
||||
WHERE code LIKE 'SUP%'
|
||||
GROUP BY qualification_level
|
||||
ORDER BY qualification_level
|
||||
`);
|
||||
|
||||
console.log('\n🏭 新增供应商等级分布:');
|
||||
supplierStats.forEach(stat => {
|
||||
console.log(` - 等级${stat.qualification_level}: ${stat.count}家`);
|
||||
});
|
||||
|
||||
const [orderStats] = await sequelize.query(`
|
||||
SELECT status, COUNT(*) as count
|
||||
FROM orders
|
||||
WHERE orderNo LIKE 'ORD2024%'
|
||||
GROUP BY status
|
||||
ORDER BY status
|
||||
`);
|
||||
|
||||
console.log('\n📋 新增订单状态分布:');
|
||||
orderStats.forEach(stat => {
|
||||
console.log(` - ${stat.status}: ${stat.count}个`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 数据验证失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
try {
|
||||
console.log('\n🚀 ===== 兼容数据插入开始 =====');
|
||||
|
||||
// 1. 测试连接
|
||||
console.log('\n📡 连接远程MySQL数据库...');
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
console.log(`📍 连接信息: ${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`);
|
||||
|
||||
// 2. 检查表结构
|
||||
await checkTableStructure();
|
||||
|
||||
// 3. 插入兼容数据
|
||||
await insertCompatibleData();
|
||||
|
||||
// 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('💓 健康检查: http://localhost:4330/health');
|
||||
|
||||
console.log('\n🔑 测试账户:');
|
||||
console.log(' 采购商: buyer001 / 123456');
|
||||
console.log(' 贸易商: trader001 / 123456');
|
||||
console.log(' 供应商: supplier001 / 123456');
|
||||
console.log(' 司机: driver001 / 123456');
|
||||
console.log(' 员工: staff001 / 123456');
|
||||
|
||||
console.log('\n📈 数据概览:');
|
||||
console.log(' - 5个不同类型的测试用户');
|
||||
console.log(' - 5家不同等级的供应商');
|
||||
console.log(' - 3个不同状态的订单');
|
||||
console.log(' - 16项系统配置参数');
|
||||
console.log(' - 完全兼容现有表结构');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ 数据插入失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n🔌 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行脚本
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { main };
|
||||
843
backend/complete_db_setup.js
Normal file
843
backend/complete_db_setup.js
Normal file
@@ -0,0 +1,843 @@
|
||||
/**
|
||||
* 完整的数据库设置脚本
|
||||
* 连接远程MySQL,更新表结构,插入测试数据
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { Sequelize, DataTypes } = 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}`),
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 检查并创建/更新表结构
|
||||
*/
|
||||
async function updateTableStructures() {
|
||||
console.log('\n🔧 开始更新表结构...');
|
||||
|
||||
try {
|
||||
// 1. 更新用户表结构
|
||||
console.log('👤 更新用户表结构...');
|
||||
await sequelize.query(`
|
||||
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='用户基础表'
|
||||
`);
|
||||
|
||||
// 2. 更新供应商表结构
|
||||
console.log('🏭 更新供应商表结构...');
|
||||
await sequelize.query(`
|
||||
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='供应商表'
|
||||
`);
|
||||
|
||||
// 3. 更新订单表结构
|
||||
console.log('📋 更新订单表结构...');
|
||||
await sequelize.query(`
|
||||
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 '完成时间',
|
||||
|
||||
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='订单主表'
|
||||
`);
|
||||
|
||||
// 4. 创建支付记录表
|
||||
console.log('💰 创建支付记录表...');
|
||||
await sequelize.query(`
|
||||
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 '更新时间',
|
||||
|
||||
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='支付记录表'
|
||||
`);
|
||||
|
||||
// 5. 创建运输任务表
|
||||
console.log('🚛 创建运输任务表...');
|
||||
await sequelize.query(`
|
||||
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 '更新时间',
|
||||
|
||||
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='运输任务表'
|
||||
`);
|
||||
|
||||
// 6. 创建质量检验表
|
||||
console.log('🔍 创建质量检验表...');
|
||||
await sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS quality_inspections (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '检验ID',
|
||||
inspection_no VARCHAR(50) UNIQUE NOT NULL COMMENT '检验单号',
|
||||
order_id BIGINT NOT NULL COMMENT '订单ID',
|
||||
inspector_id BIGINT NOT NULL COMMENT '检验员ID',
|
||||
inspection_time DATETIME NOT NULL COMMENT '检验时间',
|
||||
inspection_location VARCHAR(200) COMMENT '检验地点',
|
||||
cattle_count INT NOT NULL COMMENT '检验牛只数量',
|
||||
passed_count INT DEFAULT 0 COMMENT '合格数量',
|
||||
failed_count INT DEFAULT 0 COMMENT '不合格数量',
|
||||
average_weight DECIMAL(8,2) COMMENT '平均重量',
|
||||
total_weight DECIMAL(10,2) COMMENT '总重量',
|
||||
health_status ENUM('excellent', 'good', 'fair', 'poor') DEFAULT 'good' COMMENT '健康状况',
|
||||
quality_grade ENUM('A+', 'A', 'B+', 'B', 'C', 'D') DEFAULT 'B' COMMENT '质量等级',
|
||||
inspection_result ENUM('passed', 'failed', 'conditional_pass') DEFAULT 'passed' COMMENT '检验结果',
|
||||
defect_description TEXT COMMENT '缺陷描述',
|
||||
improvement_suggestions TEXT COMMENT '改进建议',
|
||||
inspector_notes TEXT COMMENT '检验员备注',
|
||||
photos JSON COMMENT '检验照片URLs',
|
||||
certificates JSON COMMENT '相关证书信息',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
INDEX idx_inspection_no (inspection_no),
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_inspector_id (inspector_id),
|
||||
INDEX idx_inspection_time (inspection_time),
|
||||
INDEX idx_quality_grade (quality_grade),
|
||||
INDEX idx_inspection_result (inspection_result)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='质量检验表'
|
||||
`);
|
||||
|
||||
// 7. 创建系统配置表
|
||||
console.log('⚙️ 创建系统配置表...');
|
||||
await sequelize.query(`
|
||||
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='系统配置表'
|
||||
`);
|
||||
|
||||
console.log('✅ 表结构更新完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 表结构更新失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入完整测试数据
|
||||
*/
|
||||
async function insertCompleteTestData() {
|
||||
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
|
||||
]
|
||||
});
|
||||
|
||||
// 测试用户数据
|
||||
const testUsers = [
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'buyer001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '采购商张三',
|
||||
real_name: '张三',
|
||||
phone: '13800138001',
|
||||
email: 'buyer001@niumall.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@niumall.com',
|
||||
user_type: 'trader',
|
||||
company_name: '上海牛只贸易有限公司',
|
||||
company_address: '上海市浦东新区xxx路'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'supplier001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '供应商王五',
|
||||
real_name: '王五',
|
||||
phone: '13800138003',
|
||||
email: 'supplier001@niumall.com',
|
||||
user_type: 'supplier',
|
||||
company_name: '内蒙古草原牧业',
|
||||
company_address: '内蒙古呼和浩特市'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'driver001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '司机赵六',
|
||||
real_name: '赵六',
|
||||
phone: '13800138004',
|
||||
email: 'driver001@niumall.com',
|
||||
user_type: 'driver',
|
||||
id_card: '110101199001011234'
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
username: 'staff001',
|
||||
password: await bcrypt.hash('123456', 10),
|
||||
nickname: '员工孙七',
|
||||
real_name: '孙七',
|
||||
phone: '13800138005',
|
||||
email: 'staff001@niumall.com',
|
||||
user_type: 'staff',
|
||||
company_name: '牛商城运营中心'
|
||||
}
|
||||
];
|
||||
|
||||
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
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 插入供应商数据
|
||||
console.log('🏭 插入供应商数据...');
|
||||
const suppliers = [
|
||||
{
|
||||
name: '内蒙古草原牧业有限公司',
|
||||
code: 'SUP001',
|
||||
contact: '赵大牛',
|
||||
phone: '13900139001',
|
||||
email: 'sup001@niumall.com',
|
||||
address: '内蒙古呼和浩特市赛罕区草原路123号',
|
||||
region: '内蒙古',
|
||||
qualification_level: 'A',
|
||||
cattle_types: JSON.stringify(['西门塔尔牛', '安格斯牛', '夏洛莱牛']),
|
||||
capacity: 500,
|
||||
rating: 4.8,
|
||||
cooperation_start_date: '2023-01-01',
|
||||
bank_account: '6228480402564890018',
|
||||
bank_name: '农业银行呼和浩特分行',
|
||||
tax_number: '91150100MA0N2XQJ2K'
|
||||
},
|
||||
{
|
||||
name: '新疆天山畜牧合作社',
|
||||
code: 'SUP002',
|
||||
contact: '马小羊',
|
||||
phone: '13900139002',
|
||||
email: 'sup002@niumall.com',
|
||||
address: '新疆乌鲁木齐市天山区畜牧街456号',
|
||||
region: '新疆',
|
||||
qualification_level: 'A',
|
||||
cattle_types: JSON.stringify(['哈萨克牛', '新疆褐牛']),
|
||||
capacity: 300,
|
||||
rating: 4.5,
|
||||
cooperation_start_date: '2023-03-15',
|
||||
bank_account: '6228480402564890019',
|
||||
bank_name: '建设银行乌鲁木齐分行',
|
||||
tax_number: '91650100MA0N2XQJ3L'
|
||||
},
|
||||
{
|
||||
name: '山东鲁西黄牛养殖场',
|
||||
code: 'SUP003',
|
||||
contact: '孙大强',
|
||||
phone: '13900139003',
|
||||
email: 'sup003@niumall.com',
|
||||
address: '山东省济南市历城区养殖园区789号',
|
||||
region: '山东',
|
||||
qualification_level: 'B',
|
||||
cattle_types: JSON.stringify(['鲁西黄牛', '利木赞牛']),
|
||||
capacity: 200,
|
||||
rating: 4.2,
|
||||
cooperation_start_date: '2023-06-01',
|
||||
bank_account: '6228480402564890020',
|
||||
bank_name: '工商银行济南分行',
|
||||
tax_number: '91370100MA0N2XQJ4M'
|
||||
},
|
||||
{
|
||||
name: '四川成都优质牧场',
|
||||
code: 'SUP004',
|
||||
contact: '李小川',
|
||||
phone: '13900139004',
|
||||
email: 'sup004@niumall.com',
|
||||
address: '四川省成都市双流区牧场路101号',
|
||||
region: '四川',
|
||||
qualification_level: 'B',
|
||||
cattle_types: JSON.stringify(['西门塔尔牛', '本地黄牛']),
|
||||
capacity: 150,
|
||||
rating: 4.0,
|
||||
cooperation_start_date: '2023-08-01',
|
||||
bank_account: '6228480402564890021',
|
||||
bank_name: '中国银行成都分行',
|
||||
tax_number: '91510100MA0N2XQJ5N'
|
||||
},
|
||||
{
|
||||
name: '河北承德绿色牧业',
|
||||
code: 'SUP005',
|
||||
contact: '张承德',
|
||||
phone: '13900139005',
|
||||
email: 'sup005@niumall.com',
|
||||
address: '河北省承德市双桥区绿色牧场街202号',
|
||||
region: '河北',
|
||||
qualification_level: 'C',
|
||||
cattle_types: JSON.stringify(['夏洛莱牛', '安格斯牛']),
|
||||
capacity: 100,
|
||||
rating: 3.8,
|
||||
cooperation_start_date: '2023-09-15',
|
||||
bank_account: '6228480402564890022',
|
||||
bank_name: '交通银行承德分行',
|
||||
tax_number: '91130800MA0N2XQJ6O'
|
||||
}
|
||||
];
|
||||
|
||||
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,
|
||||
bank_account, bank_name, tax_number, 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',
|
||||
supplier.bank_account, supplier.bank_name, supplier.tax_number
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 插入订单数据
|
||||
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 3");
|
||||
|
||||
if (buyers.length > 0 && supplierList.length > 0) {
|
||||
const orders = [
|
||||
{
|
||||
order_no: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '001',
|
||||
client_id: buyers[0].id,
|
||||
trader_id: traders.length > 0 ? traders[0].id : null,
|
||||
supplier_id: supplierList[0].id,
|
||||
cattle_type: '西门塔尔牛',
|
||||
quantity: 50,
|
||||
weight_range: '450-550kg',
|
||||
estimated_weight: 25000.00,
|
||||
unit_price: 32.50,
|
||||
price_type: 'per_kg',
|
||||
total_amount: 812500.00,
|
||||
prepaid_amount: 200000.00,
|
||||
pickup_address: '内蒙古呼和浩特市赛罕区草原路123号',
|
||||
delivery_address: '北京市朝阳区屠宰场',
|
||||
delivery_time: '2024-02-15 08:00:00',
|
||||
status: 'confirmed',
|
||||
special_requirements: '要求健康证明齐全,质量等级A级',
|
||||
quality_standards: JSON.stringify({
|
||||
min_weight: 450,
|
||||
max_weight: 550,
|
||||
health_grade: 'A',
|
||||
age_range: '18-24个月'
|
||||
})
|
||||
},
|
||||
{
|
||||
order_no: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '002',
|
||||
client_id: buyers[0].id,
|
||||
supplier_id: supplierList.length > 1 ? supplierList[1].id : supplierList[0].id,
|
||||
cattle_type: '安格斯牛',
|
||||
quantity: 30,
|
||||
weight_range: '500-600kg',
|
||||
estimated_weight: 16500.00,
|
||||
unit_price: 35.00,
|
||||
price_type: 'per_kg',
|
||||
total_amount: 577500.00,
|
||||
prepaid_amount: 150000.00,
|
||||
pickup_address: '新疆乌鲁木齐市天山区畜牧街456号',
|
||||
delivery_address: '上海市浦东新区加工厂',
|
||||
delivery_time: '2024-02-20 10:00:00',
|
||||
status: 'pending',
|
||||
special_requirements: '需要冷链运输,重量范围500-600kg',
|
||||
quality_standards: JSON.stringify({
|
||||
min_weight: 500,
|
||||
max_weight: 600,
|
||||
health_grade: 'A',
|
||||
age_range: '20-26个月'
|
||||
})
|
||||
},
|
||||
{
|
||||
order_no: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '003',
|
||||
client_id: buyers[0].id,
|
||||
supplier_id: supplierList.length > 2 ? supplierList[2].id : supplierList[0].id,
|
||||
cattle_type: '鲁西黄牛',
|
||||
quantity: 20,
|
||||
weight_range: '400-500kg',
|
||||
estimated_weight: 9000.00,
|
||||
unit_price: 30.00,
|
||||
price_type: 'per_kg',
|
||||
total_amount: 270000.00,
|
||||
prepaid_amount: 80000.00,
|
||||
pickup_address: '山东省济南市历城区养殖园区789号',
|
||||
delivery_address: '天津市滨海新区肉类加工园',
|
||||
delivery_time: '2024-02-25 14:00:00',
|
||||
status: 'draft',
|
||||
special_requirements: '本地优质黄牛,肉质鲜美',
|
||||
quality_standards: JSON.stringify({
|
||||
min_weight: 400,
|
||||
max_weight: 500,
|
||||
health_grade: 'B+',
|
||||
age_range: '16-22个月'
|
||||
})
|
||||
}
|
||||
];
|
||||
|
||||
for (const order of orders) {
|
||||
await sequelize.query(`
|
||||
INSERT IGNORE INTO orders (
|
||||
order_no, client_id, trader_id, supplier_id, cattle_type, quantity,
|
||||
weight_range, estimated_weight, unit_price, price_type, total_amount, prepaid_amount,
|
||||
pickup_address, 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.weight_range, order.estimated_weight,
|
||||
order.unit_price, order.price_type, order.total_amount, order.prepaid_amount,
|
||||
order.pickup_address, order.delivery_address, order.delivery_time,
|
||||
order.status, order.special_requirements, order.quality_standards
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 插入支付记录
|
||||
console.log('💰 插入支付记录...');
|
||||
const [orderList] = await sequelize.query("SELECT id, order_no, client_id, prepaid_amount FROM orders WHERE prepaid_amount > 0 LIMIT 2");
|
||||
|
||||
for (const order of orderList) {
|
||||
const paymentNo = 'PAY' + Date.now() + Math.floor(Math.random() * 1000);
|
||||
await sequelize.query(`
|
||||
INSERT IGNORE INTO payments (
|
||||
payment_no, order_id, user_id, amount, paid_amount, payment_method,
|
||||
payment_channel, status, payment_time, confirmed_time, notes, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`, {
|
||||
replacements: [
|
||||
paymentNo, order.id, order.client_id, order.prepaid_amount, order.prepaid_amount,
|
||||
'bank_transfer', '银行转账', 'success', new Date(), new Date(),
|
||||
`订单${order.order_no}的预付款支付`
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 5. 插入运输任务
|
||||
console.log('🚛 插入运输任务...');
|
||||
const [drivers] = await sequelize.query("SELECT id FROM users WHERE user_type = 'driver' LIMIT 1");
|
||||
const [confirmedOrders] = await sequelize.query("SELECT id, order_no FROM orders WHERE status = 'confirmed' LIMIT 1");
|
||||
|
||||
if (drivers.length > 0 && confirmedOrders.length > 0) {
|
||||
const taskNo = 'TASK' + Date.now();
|
||||
await sequelize.query(`
|
||||
INSERT IGNORE INTO transport_tasks (
|
||||
task_no, order_id, driver_id, vehicle_no, vehicle_type, vehicle_capacity,
|
||||
start_location, end_location, planned_distance, planned_start_time, planned_end_time,
|
||||
status, transport_fee, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`, {
|
||||
replacements: [
|
||||
taskNo, confirmedOrders[0].id, drivers[0].id, '蒙A12345', '大型货车', 25.0,
|
||||
'内蒙古呼和浩特市', '北京市朝阳区', 450.5, '2024-02-14 06:00:00', '2024-02-15 08:00:00',
|
||||
'assigned', 8000.00
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 插入质量检验记录
|
||||
console.log('🔍 插入质量检验记录...');
|
||||
const [staff] = await sequelize.query("SELECT id FROM users WHERE user_type = 'staff' LIMIT 1");
|
||||
|
||||
if (staff.length > 0 && confirmedOrders.length > 0) {
|
||||
const inspectionNo = 'INS' + Date.now();
|
||||
await sequelize.query(`
|
||||
INSERT IGNORE INTO quality_inspections (
|
||||
inspection_no, order_id, inspector_id, inspection_time, inspection_location,
|
||||
cattle_count, passed_count, failed_count, average_weight, total_weight,
|
||||
health_status, quality_grade, inspection_result, inspector_notes, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`, {
|
||||
replacements: [
|
||||
inspectionNo, confirmedOrders[0].id, staff[0].id, new Date(), '北京市朝阳区检验站',
|
||||
50, 48, 2, 502.5, 25125.0, 'excellent', 'A', 'passed',
|
||||
'整体质量优秀,2头牛只重量略低于标准,但健康状况良好'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// 7. 插入系统配置
|
||||
console.log('⚙️ 插入系统配置...');
|
||||
const configs = [
|
||||
['system.name', '活牛采购智能数字化系统', 'string', 'system', '系统名称'],
|
||||
['system.version', '1.0.0', 'string', 'system', '系统版本'],
|
||||
['system.company', '牛商城科技有限公司', 'string', 'system', '公司名称'],
|
||||
['order.auto_confirm_hours', '24', 'number', 'order', '订单自动确认时间(小时)'],
|
||||
['order.max_quantity', '1000', 'number', 'order', '单笔订单最大数量'],
|
||||
['payment.timeout_minutes', '30', 'number', 'payment', '支付超时时间(分钟)'],
|
||||
['payment.min_prepaid_ratio', '0.2', 'number', 'payment', '最低预付比例'],
|
||||
['transport.tracking_interval', '300', 'number', 'transport', '位置跟踪间隔(秒)'],
|
||||
['transport.max_distance', '2000', 'number', 'transport', '最大运输距离(公里)'],
|
||||
['quality.min_score', '80', 'number', 'quality', '质量检验最低分数'],
|
||||
['quality.pass_rate_threshold', '0.95', 'number', 'quality', '合格率阈值'],
|
||||
['notification.sms_enabled', 'true', 'boolean', 'notification', '是否启用短信通知'],
|
||||
['notification.email_enabled', 'true', 'boolean', 'notification', '是否启用邮件通知'],
|
||||
['notification.push_enabled', 'true', 'boolean', 'notification', '是否启用推送通知'],
|
||||
['security.session_timeout', '7200', 'number', 'security', '会话超时时间(秒)'],
|
||||
['security.max_login_attempts', '5', 'number', 'security', '最大登录尝试次数']
|
||||
];
|
||||
|
||||
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 validateDatabase() {
|
||||
console.log('\n🔍 验证数据库完整性...');
|
||||
|
||||
try {
|
||||
const tables = [
|
||||
'users', 'suppliers', 'orders', 'payments',
|
||||
'transport_tasks', 'quality_inspections', 'system_configs'
|
||||
];
|
||||
|
||||
console.log('📊 数据统计:');
|
||||
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 FROM users WHERE user_type = 'admin'"
|
||||
);
|
||||
|
||||
console.log('\n👤 管理员用户:');
|
||||
adminUsers.forEach(user => {
|
||||
console.log(` - ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}`);
|
||||
});
|
||||
|
||||
const [supplierStats] = await sequelize.query(`
|
||||
SELECT qualification_level, COUNT(*) as count
|
||||
FROM suppliers
|
||||
GROUP BY qualification_level
|
||||
ORDER BY qualification_level
|
||||
`);
|
||||
|
||||
console.log('\n🏭 供应商等级分布:');
|
||||
supplierStats.forEach(stat => {
|
||||
console.log(` - 等级${stat.qualification_level}: ${stat.count}家`);
|
||||
});
|
||||
|
||||
const [orderStats] = await sequelize.query(`
|
||||
SELECT status, COUNT(*) as count
|
||||
FROM orders
|
||||
GROUP BY status
|
||||
ORDER BY status
|
||||
`);
|
||||
|
||||
console.log('\n📋 订单状态分布:');
|
||||
orderStats.forEach(stat => {
|
||||
console.log(` - ${stat.status}: ${stat.count}个`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 数据验证失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
try {
|
||||
console.log('\n🚀 ===== 远程MySQL数据库完整设置开始 =====');
|
||||
|
||||
// 1. 测试连接
|
||||
console.log('\n📡 连接远程MySQL数据库...');
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
console.log(`📍 连接信息: ${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`);
|
||||
|
||||
// 2. 更新表结构
|
||||
await updateTableStructures();
|
||||
|
||||
// 3. 插入测试数据
|
||||
await insertCompleteTestData();
|
||||
|
||||
// 4. 验证数据
|
||||
await validateDatabase();
|
||||
|
||||
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('💓 健康检查: http://localhost:4330/health');
|
||||
|
||||
console.log('\n🔑 测试账户:');
|
||||
console.log(' 采购商: buyer001 / 123456');
|
||||
console.log(' 贸易商: trader001 / 123456');
|
||||
console.log(' 供应商: supplier001 / 123456');
|
||||
console.log(' 司机: driver001 / 123456');
|
||||
console.log(' 员工: staff001 / 123456');
|
||||
|
||||
console.log('\n📈 数据概览:');
|
||||
console.log(' - 5个不同类型的测试用户');
|
||||
console.log(' - 5家不同等级的供应商');
|
||||
console.log(' - 3个不同状态的订单');
|
||||
console.log(' - 完整的支付、运输、质检记录');
|
||||
console.log(' - 16项系统配置参数');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ 数据库设置失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n🔌 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行脚本
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { main };
|
||||
329
backend/correct_data_insert.js
Normal file
329
backend/correct_data_insert.js
Normal file
@@ -0,0 +1,329 @@
|
||||
/**
|
||||
* 使用正确枚举值的数据插入脚本
|
||||
*/
|
||||
|
||||
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 || 'jiebanke',
|
||||
process.env.DB_PASSWORD || 'aiot741$12346',
|
||||
{
|
||||
host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: process.env.DB_PORT || 20784,
|
||||
dialect: 'mysql',
|
||||
logging: (msg) => console.log(`[SQL] ${msg}`),
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 插入正确的测试数据
|
||||
*/
|
||||
async function insertCorrectData() {
|
||||
console.log('\n📊 开始插入正确的测试数据...');
|
||||
|
||||
try {
|
||||
// 1. 插入供应商数据
|
||||
console.log('🏭 插入供应商数据...');
|
||||
const suppliers = [
|
||||
{
|
||||
name: '内蒙古草原牧业有限公司',
|
||||
code: 'SUP001',
|
||||
contact: '赵大牛',
|
||||
phone: '15900159001',
|
||||
email: 'sup001@niumall.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: '15900159002',
|
||||
email: 'sup002@niumall.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: '15900159003',
|
||||
email: 'sup003@niumall.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) {
|
||||
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, 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'
|
||||
]
|
||||
});
|
||||
console.log(`✅ 供应商 ${supplier.code} 创建成功`);
|
||||
} else {
|
||||
console.log(`✅ 供应商 ${supplier.code} 已存在`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 插入订单数据(使用正确的枚举值)
|
||||
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 WHERE code LIKE 'SUP%' LIMIT 3");
|
||||
|
||||
if (buyers.length > 0 && supplierList.length > 0) {
|
||||
const orders = [
|
||||
{
|
||||
orderNo: 'ORD202509001',
|
||||
buyerId: buyers[0].id,
|
||||
buyerName: buyers[0].nickname,
|
||||
supplierId: supplierList[0].id,
|
||||
supplierName: supplierList[0].name,
|
||||
traderId: traders.length > 0 ? traders[0].id : null,
|
||||
traderName: traders.length > 0 ? traders[0].nickname : null,
|
||||
cattleBreed: '西门塔尔牛',
|
||||
cattleCount: 50,
|
||||
expectedWeight: 25000.00,
|
||||
unitPrice: 32.50,
|
||||
totalAmount: 812500.00,
|
||||
deliveryAddress: '北京市朝阳区屠宰场',
|
||||
deliveryDate: '2024-02-15',
|
||||
paymentMethod: 'bank_transfer',
|
||||
paymentStatus: 'partial_paid',
|
||||
orderStatus: 'confirmed',
|
||||
notes: '要求健康证明齐全,质量等级A级,重量范围450-550kg'
|
||||
},
|
||||
{
|
||||
orderNo: 'ORD202509002',
|
||||
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,
|
||||
deliveryAddress: '上海市浦东新区加工厂',
|
||||
deliveryDate: '2024-02-20',
|
||||
paymentMethod: 'online_payment',
|
||||
paymentStatus: 'unpaid',
|
||||
orderStatus: 'pending',
|
||||
notes: '需要冷链运输,重量范围500-600kg'
|
||||
},
|
||||
{
|
||||
orderNo: 'ORD202509003',
|
||||
buyerId: buyers[0].id,
|
||||
buyerName: buyers[0].nickname,
|
||||
supplierId: supplierList.length > 2 ? supplierList[2].id : supplierList[0].id,
|
||||
supplierName: supplierList.length > 2 ? supplierList[2].name : supplierList[0].name,
|
||||
traderId: null,
|
||||
traderName: null,
|
||||
cattleBreed: '鲁西黄牛',
|
||||
cattleCount: 20,
|
||||
expectedWeight: 9000.00,
|
||||
unitPrice: 30.00,
|
||||
totalAmount: 270000.00,
|
||||
deliveryAddress: '天津市滨海新区肉类加工园',
|
||||
deliveryDate: '2024-02-25',
|
||||
paymentMethod: 'cash',
|
||||
paymentStatus: 'paid',
|
||||
orderStatus: 'in_production',
|
||||
notes: '本地优质黄牛,肉质鲜美,重量范围400-500kg'
|
||||
}
|
||||
];
|
||||
|
||||
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,
|
||||
deliveryAddress, deliveryDate, paymentMethod, paymentStatus, orderStatus, 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.deliveryAddress, order.deliveryDate, order.paymentMethod,
|
||||
order.paymentStatus, order.orderStatus, order.notes
|
||||
]
|
||||
});
|
||||
console.log(`✅ 订单 ${order.orderNo} 创建成功`);
|
||||
} else {
|
||||
console.log(`✅ 订单 ${order.orderNo} 已存在`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ 正确数据插入完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 插入数据失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据完整性
|
||||
*/
|
||||
async function validateCorrectData() {
|
||||
console.log('\n🔍 验证数据完整性...');
|
||||
|
||||
try {
|
||||
// 统计数据
|
||||
const [userCount] = await sequelize.query("SELECT COUNT(*) as count FROM users");
|
||||
const [supplierCount] = await sequelize.query("SELECT COUNT(*) as count FROM suppliers");
|
||||
const [orderCount] = await sequelize.query("SELECT COUNT(*) as count FROM orders");
|
||||
|
||||
console.log('📊 数据统计:');
|
||||
console.log(` 用户: ${userCount[0].count} 条记录`);
|
||||
console.log(` 供应商: ${supplierCount[0].count} 条记录`);
|
||||
console.log(` 订单: ${orderCount[0].count} 条记录`);
|
||||
|
||||
// 检查新增供应商
|
||||
const [newSuppliers] = await sequelize.query(`
|
||||
SELECT code, name, qualification_level
|
||||
FROM suppliers
|
||||
WHERE code LIKE 'SUP%'
|
||||
ORDER BY code
|
||||
`);
|
||||
|
||||
console.log('\n🏭 新增供应商:');
|
||||
newSuppliers.forEach(supplier => {
|
||||
console.log(` - ${supplier.code}: ${supplier.name} (等级${supplier.qualification_level})`);
|
||||
});
|
||||
|
||||
// 检查新增订单
|
||||
const [newOrders] = await sequelize.query(`
|
||||
SELECT orderNo, cattleBreed, cattleCount, paymentStatus, orderStatus
|
||||
FROM orders
|
||||
WHERE orderNo LIKE 'ORD202509%'
|
||||
ORDER BY orderNo
|
||||
`);
|
||||
|
||||
console.log('\n📋 新增订单:');
|
||||
newOrders.forEach(order => {
|
||||
console.log(` - ${order.orderNo}: ${order.cattleBreed} ${order.cattleCount}头 (支付:${order.paymentStatus}, 状态:${order.orderStatus})`);
|
||||
});
|
||||
|
||||
// 检查管理员用户
|
||||
const [adminUsers] = await sequelize.query(
|
||||
"SELECT id, username, nickname FROM users WHERE user_type = 'admin'"
|
||||
);
|
||||
|
||||
console.log('\n👤 管理员用户:');
|
||||
adminUsers.forEach(user => {
|
||||
console.log(` - ID: ${user.id}, 用户名: ${user.username}, 昵称: ${user.nickname}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 数据验证失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
try {
|
||||
console.log('\n🚀 ===== 正确数据插入开始 =====');
|
||||
|
||||
// 1. 测试连接
|
||||
console.log('\n📡 连接远程MySQL数据库...');
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
console.log(`📍 连接信息: ${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`);
|
||||
|
||||
// 2. 插入正确数据
|
||||
await insertCorrectData();
|
||||
|
||||
// 3. 验证数据
|
||||
await validateCorrectData();
|
||||
|
||||
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('💓 健康检查: http://localhost:4330/health');
|
||||
|
||||
console.log('\n🔑 测试账户:');
|
||||
console.log(' 采购商: buyer001 / 123456');
|
||||
console.log(' 贸易商: trader001 / 123456');
|
||||
console.log(' 供应商: supplier001 / 123456');
|
||||
console.log(' 司机: driver001 / 123456');
|
||||
console.log(' 员工: staff001 / 123456');
|
||||
|
||||
console.log('\n📈 数据概览:');
|
||||
console.log(' - 3家不同等级的供应商 (A级2家, B级1家)');
|
||||
console.log(' - 3个不同状态的订单 (待处理、已确认、生产中)');
|
||||
console.log(' - 3种支付方式 (银行转账、在线支付、现金)');
|
||||
console.log(' - 3种支付状态 (未支付、部分支付、已支付)');
|
||||
console.log(' - 完全符合数据库表结构和约束');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ 数据插入失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n🔌 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行脚本
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { main };
|
||||
345
backend/final_data_insert.js
Normal file
345
backend/final_data_insert.js
Normal file
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* 最终版本 - 完全兼容现有表结构的数据插入脚本
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { Sequelize } = require('sequelize');
|
||||
const bcrypt = require('bcrypt');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
// 使用.env文件中的配置
|
||||
const sequelize = new Sequelize(
|
||||
process.env.DB_NAME || 'niumall',
|
||||
process.env.DB_USERNAME || 'jiebanke',
|
||||
process.env.DB_PASSWORD || 'aiot741$12346',
|
||||
{
|
||||
host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: process.env.DB_PORT || 20784,
|
||||
dialect: 'mysql',
|
||||
logging: (msg) => console.log(`[SQL] ${msg}`),
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 插入完全兼容的测试数据
|
||||
*/
|
||||
async function insertFinalData() {
|
||||
console.log('\n📊 开始插入最终测试数据...');
|
||||
|
||||
try {
|
||||
// 1. 插入供应商数据(使用正确的字段名)
|
||||
console.log('🏭 插入供应商数据...');
|
||||
const suppliers = [
|
||||
{
|
||||
name: '内蒙古草原牧业有限公司',
|
||||
code: 'SUP001',
|
||||
contact: '赵大牛',
|
||||
phone: '15900159001',
|
||||
email: 'sup001@niumall.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: '15900159002',
|
||||
email: 'sup002@niumall.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: '15900159003',
|
||||
email: 'sup003@niumall.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) {
|
||||
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, 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'
|
||||
]
|
||||
});
|
||||
console.log(`✅ 供应商 ${supplier.code} 创建成功`);
|
||||
} else {
|
||||
console.log(`✅ 供应商 ${supplier.code} 已存在`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 插入订单数据(使用正确的字段名)
|
||||
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 WHERE code LIKE 'SUP%' LIMIT 3");
|
||||
|
||||
if (buyers.length > 0 && supplierList.length > 0) {
|
||||
const orders = [
|
||||
{
|
||||
orderNo: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '001',
|
||||
buyerId: buyers[0].id,
|
||||
buyerName: buyers[0].nickname,
|
||||
supplierId: supplierList[0].id,
|
||||
supplierName: supplierList[0].name,
|
||||
traderId: traders.length > 0 ? traders[0].id : null,
|
||||
traderName: traders.length > 0 ? traders[0].nickname : null,
|
||||
cattleBreed: '西门塔尔牛',
|
||||
cattleCount: 50,
|
||||
expectedWeight: 25000.00,
|
||||
unitPrice: 32.50,
|
||||
totalAmount: 812500.00,
|
||||
deliveryAddress: '北京市朝阳区屠宰场',
|
||||
deliveryDate: '2024-02-15 08:00:00',
|
||||
paymentStatus: 'partial',
|
||||
orderStatus: 'confirmed',
|
||||
notes: '要求健康证明齐全,质量等级A级,重量范围450-550kg'
|
||||
},
|
||||
{
|
||||
orderNo: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '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,
|
||||
deliveryAddress: '上海市浦东新区加工厂',
|
||||
deliveryDate: '2024-02-20 10:00:00',
|
||||
paymentStatus: 'pending',
|
||||
orderStatus: 'pending',
|
||||
notes: '需要冷链运输,重量范围500-600kg'
|
||||
},
|
||||
{
|
||||
orderNo: 'ORD' + new Date().getFullYear() + String(new Date().getMonth() + 1).padStart(2, '0') + '003',
|
||||
buyerId: buyers[0].id,
|
||||
buyerName: buyers[0].nickname,
|
||||
supplierId: supplierList.length > 2 ? supplierList[2].id : supplierList[0].id,
|
||||
supplierName: supplierList.length > 2 ? supplierList[2].name : supplierList[0].name,
|
||||
traderId: null,
|
||||
traderName: null,
|
||||
cattleBreed: '鲁西黄牛',
|
||||
cattleCount: 20,
|
||||
expectedWeight: 9000.00,
|
||||
unitPrice: 30.00,
|
||||
totalAmount: 270000.00,
|
||||
deliveryAddress: '天津市滨海新区肉类加工园',
|
||||
deliveryDate: '2024-02-25 14:00:00',
|
||||
paymentStatus: 'pending',
|
||||
orderStatus: 'pending',
|
||||
notes: '本地优质黄牛,肉质鲜美,重量范围400-500kg'
|
||||
}
|
||||
];
|
||||
|
||||
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,
|
||||
deliveryAddress, deliveryDate, paymentStatus, orderStatus, 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.deliveryAddress, order.deliveryDate, order.paymentStatus, order.orderStatus, order.notes
|
||||
]
|
||||
});
|
||||
console.log(`✅ 订单 ${order.orderNo} 创建成功`);
|
||||
} else {
|
||||
console.log(`✅ 订单 ${order.orderNo} 已存在`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 插入系统配置(如果表存在)
|
||||
console.log('⚙️ 插入系统配置...');
|
||||
try {
|
||||
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', '位置跟踪间隔(秒)']
|
||||
];
|
||||
|
||||
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} 已存在`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('⚠️ 系统配置表不存在,跳过配置插入');
|
||||
}
|
||||
|
||||
console.log('✅ 最终数据插入完成');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 插入数据失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据完整性
|
||||
*/
|
||||
async function validateFinalData() {
|
||||
console.log('\n🔍 验证最终数据完整性...');
|
||||
|
||||
try {
|
||||
const tables = ['users', 'suppliers', 'orders'];
|
||||
|
||||
console.log('📊 数据统计:');
|
||||
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 [newSuppliers] = await sequelize.query(`
|
||||
SELECT code, name, qualification_level
|
||||
FROM suppliers
|
||||
WHERE code LIKE 'SUP%'
|
||||
ORDER BY code
|
||||
`);
|
||||
|
||||
console.log('\n🏭 新增供应商:');
|
||||
newSuppliers.forEach(supplier => {
|
||||
console.log(` - ${supplier.code}: ${supplier.name} (等级${supplier.qualification_level})`);
|
||||
});
|
||||
|
||||
// 检查新增订单
|
||||
const [newOrders] = await sequelize.query(`
|
||||
SELECT orderNo, cattleBreed, cattleCount, orderStatus
|
||||
FROM orders
|
||||
WHERE orderNo LIKE 'ORD2024%'
|
||||
ORDER BY orderNo
|
||||
`);
|
||||
|
||||
console.log('\n📋 新增订单:');
|
||||
newOrders.forEach(order => {
|
||||
console.log(` - ${order.orderNo}: ${order.cattleBreed} ${order.cattleCount}头 (${order.orderStatus})`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 数据验证失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
try {
|
||||
console.log('\n🚀 ===== 最终数据插入开始 =====');
|
||||
|
||||
// 1. 测试连接
|
||||
console.log('\n📡 连接远程MySQL数据库...');
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
console.log(`📍 连接信息: ${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`);
|
||||
|
||||
// 2. 插入最终数据
|
||||
await insertFinalData();
|
||||
|
||||
// 3. 验证数据
|
||||
await validateFinalData();
|
||||
|
||||
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('💓 健康检查: http://localhost:4330/health');
|
||||
|
||||
console.log('\n📈 数据概览:');
|
||||
console.log(' - 3家不同等级的供应商');
|
||||
console.log(' - 3个不同状态的订单');
|
||||
console.log(' - 5项系统配置参数');
|
||||
console.log(' - 完全兼容现有表结构');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ 数据插入失败:', error);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
console.log('\n🔌 数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行脚本
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { main };
|
||||
85
scripts/database/check_remote_table_structure.js
Normal file
85
scripts/database/check_remote_table_structure.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function checkRemoteTableStructure() {
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection(config);
|
||||
console.log('正在检查远程suppliers表结构...');
|
||||
|
||||
// 检查suppliers表结构
|
||||
const [columns] = await connection.execute(`
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'niumall'
|
||||
AND TABLE_NAME = 'suppliers'
|
||||
ORDER BY ORDINAL_POSITION
|
||||
`);
|
||||
|
||||
console.log('\nsuppliers表当前字段:');
|
||||
console.table(columns.map(col => ({
|
||||
字段名: col.COLUMN_NAME,
|
||||
数据类型: col.DATA_TYPE,
|
||||
允许空值: col.IS_NULLABLE,
|
||||
默认值: col.COLUMN_DEFAULT,
|
||||
注释: col.COLUMN_COMMENT
|
||||
})));
|
||||
|
||||
// 检查是否缺少必要字段
|
||||
const requiredFields = ['bank_account', 'bank_name', 'tax_number', 'notes'];
|
||||
const existingFields = columns.map(col => col.COLUMN_NAME);
|
||||
const missingFields = requiredFields.filter(field => !existingFields.includes(field));
|
||||
|
||||
console.log(`\n字段检查结果:`);
|
||||
console.log(`总字段数: ${columns.length}`);
|
||||
console.log(`缺失字段: ${missingFields.length > 0 ? missingFields.join(', ') : '无'}`);
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
console.log('\n需要添加的字段:');
|
||||
missingFields.forEach(field => {
|
||||
let fieldDef = '';
|
||||
switch(field) {
|
||||
case 'bank_account':
|
||||
fieldDef = 'varchar(50) DEFAULT NULL COMMENT \'银行账号\'';
|
||||
break;
|
||||
case 'bank_name':
|
||||
fieldDef = 'varchar(100) DEFAULT NULL COMMENT \'开户银行\'';
|
||||
break;
|
||||
case 'tax_number':
|
||||
fieldDef = 'varchar(30) DEFAULT NULL COMMENT \'税务登记号\'';
|
||||
break;
|
||||
case 'notes':
|
||||
fieldDef = 'text DEFAULT NULL COMMENT \'备注信息\'';
|
||||
break;
|
||||
}
|
||||
console.log(`- ${field}: ${fieldDef}`);
|
||||
});
|
||||
}
|
||||
|
||||
// 检查数据量
|
||||
const [countResult] = await connection.execute('SELECT COUNT(*) as count FROM suppliers');
|
||||
console.log(`\nsuppliers表数据量: ${countResult[0].count} 条记录`);
|
||||
|
||||
await connection.end();
|
||||
return { missingFields, totalRecords: countResult[0].count };
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查表结构失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行检查
|
||||
checkRemoteTableStructure().then(result => {
|
||||
console.log('\n✅ 表结构检查完成');
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
39
scripts/database/check_table_structure.js
Normal file
39
scripts/database/check_table_structure.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { Sequelize } = require('../../backend/node_modules/sequelize');
|
||||
|
||||
async function checkTableStructure() {
|
||||
const sequelize = new Sequelize('niumall', 'root', '123456', {
|
||||
host: 'localhost',
|
||||
dialect: 'mysql',
|
||||
logging: false
|
||||
});
|
||||
|
||||
try {
|
||||
console.log('正在检查suppliers表结构...');
|
||||
|
||||
const [results] = await sequelize.query(`
|
||||
DESCRIBE suppliers
|
||||
`);
|
||||
|
||||
console.log('suppliers表字段列表:');
|
||||
console.table(results);
|
||||
|
||||
// 检查是否有bank_account字段
|
||||
const hasBank = results.some(field => field.Field === 'bank_account');
|
||||
console.log(`\nbank_account字段存在: ${hasBank ? '是' : '否'}`);
|
||||
|
||||
if (!hasBank) {
|
||||
console.log('\n需要添加的字段:');
|
||||
console.log('- bank_account');
|
||||
console.log('- bank_name');
|
||||
console.log('- tax_number');
|
||||
console.log('- notes');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查失败:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
checkTableStructure().catch(console.error);
|
||||
72
scripts/database/check_users_table.js
Normal file
72
scripts/database/check_users_table.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function checkUsersTable() {
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection(config);
|
||||
console.log('正在检查users表...');
|
||||
|
||||
// 检查users表是否存在
|
||||
const [tables] = await connection.execute(`
|
||||
SELECT TABLE_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = 'niumall'
|
||||
AND TABLE_NAME = 'users'
|
||||
`);
|
||||
|
||||
if (tables.length === 0) {
|
||||
console.log('❌ users表不存在');
|
||||
await connection.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('✅ users表存在');
|
||||
|
||||
// 检查用户数据
|
||||
const [users] = await connection.execute(`
|
||||
SELECT id, uuid, username, display_name, real_name, phone, email, role, status
|
||||
FROM users
|
||||
ORDER BY id
|
||||
`);
|
||||
|
||||
console.log(`\nusers表数据 (${users.length}条记录):`);
|
||||
console.table(users);
|
||||
|
||||
// 检查是否有管理员用户
|
||||
const [adminUsers] = await connection.execute(`
|
||||
SELECT username, role, status
|
||||
FROM users
|
||||
WHERE role = 'admin' AND status = 'active'
|
||||
`);
|
||||
|
||||
console.log(`\n管理员用户 (${adminUsers.length}个):`);
|
||||
if (adminUsers.length > 0) {
|
||||
console.table(adminUsers);
|
||||
} else {
|
||||
console.log('❌ 没有找到活跃的管理员用户');
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查users表失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行检查
|
||||
checkUsersTable().then(() => {
|
||||
console.log('\n✅ users表检查完成');
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
75
scripts/database/check_users_table_simple.js
Normal file
75
scripts/database/check_users_table_simple.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function checkUsersTableSimple() {
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection(config);
|
||||
console.log('正在检查users表结构...');
|
||||
|
||||
// 先检查表结构
|
||||
const [columns] = await connection.execute(`
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'niumall'
|
||||
AND TABLE_NAME = 'users'
|
||||
ORDER BY ORDINAL_POSITION
|
||||
`);
|
||||
|
||||
console.log('\nusers表字段结构:');
|
||||
console.table(columns.map(col => ({
|
||||
字段名: col.COLUMN_NAME,
|
||||
数据类型: col.DATA_TYPE,
|
||||
允许空值: col.IS_NULLABLE,
|
||||
默认值: col.COLUMN_DEFAULT,
|
||||
注释: col.COLUMN_COMMENT
|
||||
})));
|
||||
|
||||
// 检查用户数据(使用实际存在的字段)
|
||||
const [users] = await connection.execute(`SELECT * FROM users LIMIT 10`);
|
||||
|
||||
console.log(`\nusers表数据 (前10条记录):`);
|
||||
console.table(users);
|
||||
|
||||
// 检查管理员用户
|
||||
const [adminCheck] = await connection.execute(`
|
||||
SELECT COUNT(*) as admin_count
|
||||
FROM users
|
||||
WHERE role = 'admin'
|
||||
`);
|
||||
|
||||
console.log(`\n管理员用户数量: ${adminCheck[0].admin_count}`);
|
||||
|
||||
if (adminCheck[0].admin_count > 0) {
|
||||
const [adminUsers] = await connection.execute(`
|
||||
SELECT username, role, status
|
||||
FROM users
|
||||
WHERE role = 'admin'
|
||||
`);
|
||||
console.log('\n管理员用户列表:');
|
||||
console.table(adminUsers);
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 检查users表失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行检查
|
||||
checkUsersTableSimple().then(() => {
|
||||
console.log('\n✅ users表检查完成');
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
62
scripts/database/fix_suppliers_table.js
Normal file
62
scripts/database/fix_suppliers_table.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function fixSuppliersTable() {
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: 'root',
|
||||
password: '123456',
|
||||
database: 'niumall'
|
||||
});
|
||||
|
||||
try {
|
||||
console.log('正在修复suppliers表结构...');
|
||||
|
||||
// 检查字段是否已存在
|
||||
const [columns] = await connection.execute(`
|
||||
SELECT COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'niumall'
|
||||
AND TABLE_NAME = 'suppliers'
|
||||
AND COLUMN_NAME IN ('bank_account', 'bank_name', 'tax_number', 'notes')
|
||||
`);
|
||||
|
||||
const existingColumns = columns.map(col => col.COLUMN_NAME);
|
||||
console.log('已存在的字段:', existingColumns);
|
||||
|
||||
// 添加缺失的字段
|
||||
const fieldsToAdd = [
|
||||
{ name: 'bank_account', sql: 'ADD COLUMN bank_account varchar(50) DEFAULT NULL COMMENT \'银行账号\'' },
|
||||
{ name: 'bank_name', sql: 'ADD COLUMN bank_name varchar(100) DEFAULT NULL COMMENT \'开户银行\'' },
|
||||
{ name: 'tax_number', sql: 'ADD COLUMN tax_number varchar(30) DEFAULT NULL COMMENT \'税务登记号\'' },
|
||||
{ name: 'notes', sql: 'ADD COLUMN notes text DEFAULT NULL COMMENT \'备注信息\'' }
|
||||
];
|
||||
|
||||
for (const field of fieldsToAdd) {
|
||||
if (!existingColumns.includes(field.name)) {
|
||||
console.log(`添加字段: ${field.name}`);
|
||||
await connection.execute(`ALTER TABLE suppliers ${field.sql}`);
|
||||
} else {
|
||||
console.log(`字段 ${field.name} 已存在,跳过`);
|
||||
}
|
||||
}
|
||||
|
||||
// 修改status字段的枚举值
|
||||
console.log('更新status字段枚举值...');
|
||||
await connection.execute(`
|
||||
ALTER TABLE suppliers
|
||||
MODIFY COLUMN status enum('active','inactive','suspended','blacklisted')
|
||||
NOT NULL DEFAULT 'active' COMMENT '状态'
|
||||
`);
|
||||
|
||||
console.log('✅ suppliers表结构修复完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 修复失败:', error.message);
|
||||
throw error;
|
||||
} finally {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
// 执行修复
|
||||
fixSuppliersTable().catch(console.error);
|
||||
67
scripts/database/fix_test_data.js
Normal file
67
scripts/database/fix_test_data.js
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs').promises;
|
||||
|
||||
async function fixTestData() {
|
||||
console.log('开始修复测试数据的时间戳字段...');
|
||||
|
||||
try {
|
||||
// 读取原始文件
|
||||
const content = await fs.readFile('./init_test_data.sql', 'utf8');
|
||||
|
||||
// 需要添加时间戳字段的表和对应的INSERT语句模式
|
||||
const tableFields = {
|
||||
'suppliers': ', `created_at`, `updated_at`',
|
||||
'drivers': ', `created_at`, `updated_at`',
|
||||
'vehicles': ', `created_at`, `updated_at`',
|
||||
'orders': ', `created_at`, `updated_at`',
|
||||
'payments': ', `created_at`, `updated_at`',
|
||||
'transports': ', `created_at`, `updated_at`',
|
||||
'transport_tracking': ', `created_at`, `updated_at`',
|
||||
'quality_inspections': ', `created_at`, `updated_at`',
|
||||
'settlements': ', `created_at`, `updated_at`'
|
||||
};
|
||||
|
||||
let fixedContent = content;
|
||||
|
||||
// 为每个表修复INSERT语句
|
||||
for (const [tableName, timeFields] of Object.entries(tableFields)) {
|
||||
// 查找INSERT语句的模式
|
||||
const insertPattern = new RegExp(
|
||||
`(INSERT INTO \`${tableName}\` \\([^)]+\\)) VALUES\\s*\\n([\\s\\S]*?);`,
|
||||
'g'
|
||||
);
|
||||
|
||||
fixedContent = fixedContent.replace(insertPattern, (match, insertPart, valuesPart) => {
|
||||
// 在字段列表中添加时间戳字段
|
||||
const newInsertPart = insertPart + timeFields;
|
||||
|
||||
// 在每个VALUES行的末尾添加时间戳值
|
||||
const fixedValuesPart = valuesPart.replace(/\),?\s*$/gm, (valueMatch) => {
|
||||
if (valueMatch.includes('),')) {
|
||||
return ', NOW(), NOW()),';
|
||||
} else {
|
||||
return ', NOW(), NOW())';
|
||||
}
|
||||
});
|
||||
|
||||
return `${newInsertPart} VALUES\n${fixedValuesPart};`;
|
||||
});
|
||||
}
|
||||
|
||||
// 写入修复后的文件
|
||||
await fs.writeFile('./init_test_data_fixed.sql', fixedContent);
|
||||
console.log('✅ 测试数据修复完成,已保存为 init_test_data_fixed.sql');
|
||||
|
||||
// 显示修复的统计信息
|
||||
const originalInserts = (content.match(/INSERT INTO/g) || []).length;
|
||||
const fixedInserts = (fixedContent.match(/INSERT INTO/g) || []).length;
|
||||
console.log(`原始INSERT语句数量: ${originalInserts}`);
|
||||
console.log(`修复后INSERT语句数量: ${fixedInserts}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('修复失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
fixTestData().catch(console.error);
|
||||
@@ -70,7 +70,11 @@ CREATE TABLE `suppliers` (
|
||||
`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 '状态',
|
||||
`status` enum('active','inactive','suspended','blacklisted') NOT NULL DEFAULT 'active' COMMENT '状态',
|
||||
`bank_account` varchar(50) DEFAULT NULL COMMENT '银行账号',
|
||||
`bank_name` varchar(100) DEFAULT NULL COMMENT '开户银行',
|
||||
`tax_number` varchar(30) DEFAULT NULL COMMENT '税务登记号',
|
||||
`notes` text 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`),
|
||||
@@ -337,40 +341,9 @@ CREATE TABLE `settlements` (
|
||||
) 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;
|
||||
|
||||
@@ -11,13 +11,13 @@ 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');
|
||||
INSERT INTO `users` (`id`, `uuid`, `username`, `password_hash`, `nickname`, `real_name`, `phone`, `email`, `user_type`, `company_name`, `status`, `registration_source`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'admin-uuid-001', 'admin', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '系统管理员', '张三', '13800138001', 'admin@niumall.com', 'admin', '牛牛商城', 'active', 'admin_create', NOW(), NOW()),
|
||||
(2, 'buyer-uuid-001', 'buyer001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '采购商李四', '李四', '13800138002', 'buyer001@example.com', 'buyer', '大华肉业有限公司', 'active', 'web', NOW(), NOW()),
|
||||
(3, 'buyer-uuid-002', 'buyer002', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '采购商王五', '王五', '13800138003', 'buyer002@example.com', 'buyer', '鑫源食品集团', 'active', 'web', NOW(), NOW()),
|
||||
(4, 'trader-uuid-001', 'trader001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '贸易商赵六', '赵六', '13800138004', 'trader001@example.com', 'trader', '中原贸易公司', 'active', 'web', NOW(), NOW()),
|
||||
(5, 'staff-uuid-001', 'staff001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '质检员小明', '陈明', '13800138005', 'staff001@niumall.com', 'staff', '牛牛商城', 'active', 'admin_create', NOW(), NOW()),
|
||||
(6, 'staff-uuid-002', 'staff002', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '质检员小红', '李红', '13800138006', 'staff002@niumall.com', 'staff', '牛牛商城', 'active', 'admin_create', NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 2. 供应商测试数据
|
||||
|
||||
128
scripts/database/init_test_data_fixed.sql
Normal file
128
scripts/database/init_test_data_fixed.sql
Normal 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`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'admin-uuid-001', 'admin', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '系统管理员', '张三', '13800138001', 'admin@niumall.com', 'admin', '牛牛商城', 'active', 'admin_create', NOW(), NOW()),
|
||||
(2, 'buyer-uuid-001', 'buyer001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '采购商李四', '李四', '13800138002', 'buyer001@example.com', 'buyer', '大华肉业有限公司', 'active', 'web', NOW(), NOW()),
|
||||
(3, 'buyer-uuid-002', 'buyer002', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '采购商王五', '王五', '13800138003', 'buyer002@example.com', 'buyer', '鑫源食品集团', 'active', 'web', NOW(), NOW()),
|
||||
(4, 'trader-uuid-001', 'trader001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '贸易商赵六', '赵六', '13800138004', 'trader001@example.com', 'trader', '中原贸易公司', 'active', 'web', NOW(), NOW()),
|
||||
(5, 'staff-uuid-001', 'staff001', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '质检员小明', '陈明', '13800138005', 'staff001@niumall.com', 'staff', '牛牛商城', 'active', 'admin_create', NOW(), NOW()),
|
||||
(6, 'staff-uuid-002', 'staff002', '$2b$10$N9qo8uLOickgx2ZMRZoMye.IjPeGGvse/rXhd0.UpFrF6wcE.iy/i', '质检员小红', '李红', '13800138006', 'staff002@niumall.com', 'staff', '牛牛商城', 'active', 'admin_create', NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 2. 供应商测试数据
|
||||
-- =====================================================
|
||||
INSERT INTO `suppliers` (`id`, `name`, `code`, `contact`, `phone`, `email`, `address`, `region`, `qualification_level`, `cattle_types`, `capacity`, `rating`, `cooperation_start_date`, `status`), `created_at`, `updated_at` VALUES
|
||||
(1, '内蒙古草原牧业有限公司', 'SUP001', '张牧民', '13900139001', 'contact@nmgcy.com', '内蒙古呼和浩特市赛罕区草原路123号', '内蒙古', 'A', '["西门塔尔牛", "安格斯牛", "夏洛莱牛"]', 500, 4.8, '2023-01-15', 'active', NOW(), NOW()),
|
||||
(2, '山东鲁西黄牛养殖场', 'SUP002', '李养牛', '13900139002', 'contact@sdlx.com', '山东济宁市嘉祥县畜牧路456号', '山东', 'A', '["鲁西黄牛", "利木赞牛"]', 300, 4.6, '2023-03-20', 'active', NOW(), NOW()),
|
||||
(3, '河南豫南肉牛合作社', 'SUP003', '王合作', '13900139003', 'contact@hnyn.com', '河南南阳市宛城区牧业大道789号', '河南', 'B', '["西门塔尔牛", "夏洛莱牛"]', 200, 4.2, '2023-05-10', 'active', NOW(), NOW()),
|
||||
(4, '新疆天山牧业集团', 'SUP004', '马天山', '13900139004', 'contact@xjts.com', '新疆乌鲁木齐市天山区牧场路321号', '新疆', 'A', '["安格斯牛", "海福特牛"]', 400, 4.7, '2023-02-28', 'active', NOW(), NOW()),
|
||||
(5, '黑龙江北大荒牧业', 'SUP005', '刘北方', '13900139005', 'contact@hlbdh.com', '黑龙江哈尔滨市道里区牧业街654号', '黑龙江', 'B', '["西门塔尔牛", "利木赞牛"]', 250, 4.3, '2023-04-15', 'active', NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 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`), `created_at`, `updated_at` VALUES
|
||||
(1, '张运输', '13700137001', '110101198001011234', 'A2001234567890', 'A2', '2025-12-31', '张妻子', '13700137101', 'full_time', '2023-01-10', 8000.00, 4.5, 'active', NOW(), NOW()),
|
||||
(2, '李司机', '13700137002', '110101198002022345', 'A2001234567891', 'A2', '2026-06-30', '李父亲', '13700137102', 'full_time', '2023-02-15', 7500.00, 4.3, 'active', NOW(), NOW()),
|
||||
(3, '王师傅', '13700137003', '110101198003033456', 'A2001234567892', 'A2', '2025-09-30', '王儿子', '13700137103', 'full_time', '2023-03-20', 7800.00, 4.6, 'active', NOW(), NOW()),
|
||||
(4, '赵老板', '13700137004', '110101198004044567', 'A2001234567893', 'A2', '2026-03-31', '赵妻子', '13700137104', 'contract', '2023-04-10', 9000.00, 4.8, 'active', NOW(), NOW()),
|
||||
(5, '陈快递', '13700137005', '110101198005055678', 'A2001234567894', 'A2', '2025-11-30', '陈母亲', '13700137105', 'part_time', '2023-05-05', 6500.00, 4.1, 'active', NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 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`), `created_at`, `updated_at` VALUES
|
||||
(1, '京A12345', '大型货车', 15000, 1, 'available', '2024-01-15', '2024-04-15', '2024-12-31', '2025-01-10', NOW(), NOW()),
|
||||
(2, '京B23456', '中型货车', 10000, 2, 'available', '2024-01-20', '2024-04-20', '2024-11-30', '2025-02-15', NOW(), NOW()),
|
||||
(3, '京C34567', '大型货车', 18000, 3, 'in_use', '2024-01-10', '2024-04-10', '2024-10-31', '2025-03-20', NOW(), NOW()),
|
||||
(4, '京D45678', '特大型货车', 25000, 4, 'available', '2024-01-25', '2024-04-25', '2025-01-31', '2025-04-10', NOW(), NOW()),
|
||||
(5, '京E56789', '中型货车', 12000, 5, 'maintenance', '2024-01-05', '2024-04-05', '2024-09-30', '2025-05-05', NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 5. 订单测试数据
|
||||
-- =====================================================
|
||||
INSERT INTO `orders` (`id`, `orderNo`, `buyerId`, `buyerName`, `supplierId`, `supplierName`, `traderId`, `traderName`, `cattleBreed`, `cattleCount`, `expectedWeight`, `actualWeight`, `unitPrice`, `totalAmount`, `deliveryAddress`, `deliveryDate`, `paymentMethod`, `paymentStatus`, `orderStatus`), `created_at`, `updated_at` VALUES
|
||||
(1, 'ORD202401001', 2, '大华肉业有限公司', 1, '内蒙古草原牧业有限公司', 4, '中原贸易公司', '西门塔尔牛', 50, 25000.00, 24800.00, 28.50, 706800.00, '北京市朝阳区肉联厂路100号', '2024-02-15', 'bank_transfer', 'partial_paid', 'shipped', NOW(), NOW()),
|
||||
(2, 'ORD202401002', 3, '鑫源食品集团', 2, '山东鲁西黄牛养殖场', NULL, NULL, '鲁西黄牛', 30, 18000.00, NULL, 26.80, 482400.00, '上海市浦东新区食品工业园区200号', '2024-02-20', 'bank_transfer', 'unpaid', 'confirmed', NOW(), NOW()),
|
||||
(3, 'ORD202401003', 2, '大华肉业有限公司', 3, '河南豫南肉牛合作社', NULL, NULL, '夏洛莱牛', 40, 22000.00, NULL, 29.20, 642400.00, '北京市朝阳区肉联厂路100号', '2024-02-25', 'online_payment', 'unpaid', 'pending', NOW(), NOW()),
|
||||
(4, 'ORD202401004', 3, '鑫源食品集团', 4, '新疆天山牧业集团', 4, '中原贸易公司', '安格斯牛', 60, 32000.00, NULL, 32.00, 1024000.00, '上海市浦东新区食品工业园区200号', '2024-03-01', 'bank_transfer', 'unpaid', 'pending', NOW(), NOW()),
|
||||
(5, 'ORD202401005', 2, '大华肉业有限公司', 5, '黑龙江北大荒牧业', NULL, NULL, '西门塔尔牛', 35, 19500.00, NULL, 27.50, 536250.00, '北京市朝阳区肉联厂路100号', '2024-03-05', 'bank_transfer', 'unpaid', 'pending', NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 6. 支付测试数据
|
||||
-- =====================================================
|
||||
INSERT INTO `payments` (`id`, `order_id`, `user_id`, `amount`, `paid_amount`, `payment_type`, `payment_method`, `payment_no`, `third_party_id`, `status`, `paid_time`), `created_at`, `updated_at` VALUES
|
||||
(1, 1, 2, 353400.00, 353400.00, 'bank', 'web', 'PAY202401001001', 'BANK20240115001', 'paid', '2024-01-15 14:30:00', NOW(), NOW()),
|
||||
(2, 1, 2, 353400.00, NULL, 'bank', 'web', 'PAY202401001002', NULL, 'pending', NULL, NOW(), NOW()),
|
||||
(3, 2, 3, 241200.00, NULL, 'bank', 'web', 'PAY202401002001', NULL, 'pending', NULL, NOW(), NOW()),
|
||||
(4, 2, 3, 241200.00, NULL, 'bank', 'web', 'PAY202401002002', NULL, 'pending', NULL, NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 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`), `created_at`, `updated_at` 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, NOW(), NOW()),
|
||||
(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, NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 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`), `created_at`, `updated_at` 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', NOW(), NOW()),
|
||||
(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, NOW(), NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 更新司机当前车辆关联
|
||||
-- =====================================================
|
||||
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;
|
||||
@@ -24,7 +24,10 @@ const defaultConfig = {
|
||||
port: 3306,
|
||||
user: 'root',
|
||||
password: '',
|
||||
database: 'niumall'
|
||||
database: 'niumall',
|
||||
skipStructure: false,
|
||||
skipData: false,
|
||||
dataFile: 'init_test_data.sql'
|
||||
};
|
||||
|
||||
// 显示帮助信息
|
||||
@@ -41,11 +44,13 @@ function showHelp() {
|
||||
console.log(' --database DB 数据库名称 (默认: niumall)');
|
||||
console.log(' --skip-structure 跳过表结构创建');
|
||||
console.log(' --skip-data 跳过测试数据插入');
|
||||
console.log(' --data-file FILE 指定测试数据文件 (默认: init_test_data.sql)');
|
||||
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');
|
||||
console.log(' node init_with_node.js --data-file init_test_data_fixed.sql');
|
||||
}
|
||||
|
||||
// 解析命令行参数
|
||||
@@ -78,6 +83,9 @@ function parseArgs() {
|
||||
case '--skip-data':
|
||||
skipData = true;
|
||||
break;
|
||||
case '--data-file':
|
||||
config.dataFile = args[++i];
|
||||
break;
|
||||
case '--help':
|
||||
showHelp();
|
||||
process.exit(0);
|
||||
@@ -261,7 +269,7 @@ async function main() {
|
||||
|
||||
// 4. 插入测试数据
|
||||
if (!skipData && success) {
|
||||
const dataFile = path.join(__dirname, 'init_test_data.sql');
|
||||
const dataFile = path.join(__dirname, config.dataFile);
|
||||
if (!await executeSqlFile(config, dataFile, '测试数据插入')) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
179
scripts/database/insert_suppliers_test_data.js
Normal file
179
scripts/database/insert_suppliers_test_data.js
Normal file
@@ -0,0 +1,179 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function insertSuppliersTestData() {
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection(config);
|
||||
console.log('正在插入suppliers测试数据...');
|
||||
|
||||
// 先清空现有数据(可选)
|
||||
console.log('清空现有suppliers数据...');
|
||||
await connection.execute('DELETE FROM suppliers');
|
||||
await connection.execute('ALTER TABLE suppliers AUTO_INCREMENT = 1');
|
||||
|
||||
// 插入测试数据,包含新增字段
|
||||
const testData = [
|
||||
{
|
||||
id: 1,
|
||||
name: '内蒙古草原牧业有限公司',
|
||||
code: 'SUP001',
|
||||
contact: '张牧民',
|
||||
phone: '13900139001',
|
||||
email: 'contact@nmgcy.com',
|
||||
address: '内蒙古呼和浩特市赛罕区草原路123号',
|
||||
region: '内蒙古',
|
||||
qualification_level: 'A',
|
||||
cattle_types: JSON.stringify(['西门塔尔牛', '安格斯牛', '夏洛莱牛']),
|
||||
capacity: 500,
|
||||
rating: 4.8,
|
||||
cooperation_start_date: '2023-01-15',
|
||||
status: 'active',
|
||||
bank_account: '6228480012345678901',
|
||||
bank_name: '中国农业银行呼和浩特分行',
|
||||
tax_number: '91150100123456789X',
|
||||
notes: '优质供应商,合作稳定,产品质量优秀'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '山东鲁西黄牛养殖场',
|
||||
code: 'SUP002',
|
||||
contact: '李养牛',
|
||||
phone: '13900139002',
|
||||
email: 'contact@sdlx.com',
|
||||
address: '山东济宁市嘉祥县畜牧路456号',
|
||||
region: '山东',
|
||||
qualification_level: 'A',
|
||||
cattle_types: JSON.stringify(['鲁西黄牛', '利木赞牛']),
|
||||
capacity: 300,
|
||||
rating: 4.6,
|
||||
cooperation_start_date: '2023-03-20',
|
||||
status: 'active',
|
||||
bank_account: '6228480023456789012',
|
||||
bank_name: '中国工商银行济宁分行',
|
||||
tax_number: '91370829234567890A',
|
||||
notes: '专业黄牛养殖,品种纯正'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '河南豫南肉牛合作社',
|
||||
code: 'SUP003',
|
||||
contact: '王合作',
|
||||
phone: '13900139003',
|
||||
email: 'contact@hnyn.com',
|
||||
address: '河南南阳市宛城区牧业大道789号',
|
||||
region: '河南',
|
||||
qualification_level: 'B',
|
||||
cattle_types: JSON.stringify(['西门塔尔牛', '夏洛莱牛']),
|
||||
capacity: 200,
|
||||
rating: 4.2,
|
||||
cooperation_start_date: '2023-05-10',
|
||||
status: 'active',
|
||||
bank_account: '6228480034567890123',
|
||||
bank_name: '中国建设银行南阳分行',
|
||||
tax_number: '91411303345678901B',
|
||||
notes: '合作社模式,农户联合经营'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '新疆天山牧业集团',
|
||||
code: 'SUP004',
|
||||
contact: '马天山',
|
||||
phone: '13900139004',
|
||||
email: 'contact@xjts.com',
|
||||
address: '新疆乌鲁木齐市天山区牧场路321号',
|
||||
region: '新疆',
|
||||
qualification_level: 'A',
|
||||
cattle_types: JSON.stringify(['安格斯牛', '海福特牛']),
|
||||
capacity: 400,
|
||||
rating: 4.7,
|
||||
cooperation_start_date: '2023-02-28',
|
||||
status: 'active',
|
||||
bank_account: '6228480045678901234',
|
||||
bank_name: '中国银行乌鲁木齐分行',
|
||||
tax_number: '91650100456789012C',
|
||||
notes: '大型牧业集团,规模化经营'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '黑龙江北大荒牧业',
|
||||
code: 'SUP005',
|
||||
contact: '刘北方',
|
||||
phone: '13900139005',
|
||||
email: 'contact@hlbdh.com',
|
||||
address: '黑龙江哈尔滨市道里区牧业街654号',
|
||||
region: '黑龙江',
|
||||
qualification_level: 'B',
|
||||
cattle_types: JSON.stringify(['西门塔尔牛', '利木赞牛']),
|
||||
capacity: 250,
|
||||
rating: 4.3,
|
||||
cooperation_start_date: '2023-04-15',
|
||||
status: 'active',
|
||||
bank_account: '6228480056789012345',
|
||||
bank_name: '中国邮政储蓄银行哈尔滨分行',
|
||||
tax_number: '91230102567890123D',
|
||||
notes: '北大荒品牌,信誉良好'
|
||||
}
|
||||
];
|
||||
|
||||
// 批量插入数据
|
||||
const insertSql = `
|
||||
INSERT INTO suppliers (
|
||||
id, name, code, contact, phone, email, address, region,
|
||||
qualification_level, cattle_types, capacity, rating,
|
||||
cooperation_start_date, status, bank_account, bank_name,
|
||||
tax_number, notes, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`;
|
||||
|
||||
for (const supplier of testData) {
|
||||
const values = [
|
||||
supplier.id, 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, supplier.status,
|
||||
supplier.bank_account, supplier.bank_name, supplier.tax_number, supplier.notes
|
||||
];
|
||||
|
||||
await connection.execute(insertSql, values);
|
||||
console.log(`✅ 插入供应商: ${supplier.name}`);
|
||||
}
|
||||
|
||||
// 验证插入结果
|
||||
const [result] = await connection.execute('SELECT COUNT(*) as count FROM suppliers');
|
||||
console.log(`\n插入完成,suppliers表共有 ${result[0].count} 条记录`);
|
||||
|
||||
// 显示插入的数据
|
||||
const [suppliers] = await connection.execute(`
|
||||
SELECT id, name, code, contact, phone, bank_account, bank_name, tax_number, status
|
||||
FROM suppliers
|
||||
ORDER BY id
|
||||
`);
|
||||
|
||||
console.log('\n插入的供应商数据:');
|
||||
console.table(suppliers);
|
||||
|
||||
await connection.end();
|
||||
console.log('\n✅ suppliers测试数据插入完成!');
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 插入测试数据失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行插入
|
||||
insertSuppliersTestData().then(() => {
|
||||
console.log('测试数据插入成功');
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
238
scripts/database/mysql_operations.js
Normal file
238
scripts/database/mysql_operations.js
Normal file
@@ -0,0 +1,238 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
|
||||
// 数据库连接配置
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
class MySQLOperations {
|
||||
constructor() {
|
||||
this.connection = null;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
try {
|
||||
this.connection = await mysql.createConnection(config);
|
||||
console.log('✅ 成功连接到远程MySQL数据库');
|
||||
console.log(`主机: ${config.host}:${config.port}`);
|
||||
console.log(`数据库: ${config.database}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ 连接数据库失败:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
if (this.connection) {
|
||||
await this.connection.end();
|
||||
console.log('数据库连接已关闭');
|
||||
}
|
||||
}
|
||||
|
||||
async showTables() {
|
||||
try {
|
||||
const [tables] = await this.connection.execute('SHOW TABLES');
|
||||
console.log('\n📋 数据库中的表:');
|
||||
tables.forEach((table, index) => {
|
||||
console.log(`${index + 1}. ${Object.values(table)[0]}`);
|
||||
});
|
||||
return tables;
|
||||
} catch (error) {
|
||||
console.error('❌ 获取表列表失败:', error.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async describeTable(tableName) {
|
||||
try {
|
||||
const [columns] = await this.connection.execute(`DESCRIBE ${tableName}`);
|
||||
console.log(`\n📊 表 ${tableName} 的结构:`);
|
||||
console.table(columns);
|
||||
return columns;
|
||||
} catch (error) {
|
||||
console.error(`❌ 获取表 ${tableName} 结构失败:`, error.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async executeSQL(sql) {
|
||||
try {
|
||||
const [result] = await this.connection.execute(sql);
|
||||
console.log('✅ SQL执行成功');
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ SQL执行失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async executeSQLFile(filePath) {
|
||||
try {
|
||||
const sqlContent = await fs.readFile(filePath, 'utf8');
|
||||
|
||||
// 分割SQL语句(简单处理,按分号分割)
|
||||
const statements = sqlContent
|
||||
.split(';')
|
||||
.map(stmt => stmt.trim())
|
||||
.filter(stmt => stmt.length > 0 && !stmt.startsWith('--'));
|
||||
|
||||
console.log(`\n📄 执行SQL文件: ${filePath}`);
|
||||
console.log(`包含 ${statements.length} 条SQL语句`);
|
||||
|
||||
for (let i = 0; i < statements.length; i++) {
|
||||
const stmt = statements[i];
|
||||
if (stmt.toLowerCase().startsWith('select') ||
|
||||
stmt.toLowerCase().startsWith('show') ||
|
||||
stmt.toLowerCase().startsWith('describe')) {
|
||||
// 查询语句
|
||||
const [result] = await this.connection.execute(stmt);
|
||||
console.log(`语句 ${i + 1}: ${stmt.substring(0, 50)}...`);
|
||||
if (result.length > 0) {
|
||||
console.log(`返回 ${result.length} 行数据`);
|
||||
}
|
||||
} else {
|
||||
// 其他语句(INSERT, UPDATE, DELETE等)
|
||||
const [result] = await this.connection.execute(stmt);
|
||||
console.log(`语句 ${i + 1}: ${stmt.substring(0, 50)}...`);
|
||||
if (result.affectedRows !== undefined) {
|
||||
console.log(`影响 ${result.affectedRows} 行`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ SQL文件执行完成');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ 执行SQL文件失败:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkTableData(tableName, limit = 5) {
|
||||
try {
|
||||
const [rows] = await this.connection.execute(`SELECT * FROM ${tableName} LIMIT ${limit}`);
|
||||
const [count] = await this.connection.execute(`SELECT COUNT(*) as total FROM ${tableName}`);
|
||||
|
||||
console.log(`\n📊 表 ${tableName} 数据概览:`);
|
||||
console.log(`总记录数: ${count[0].total}`);
|
||||
|
||||
if (rows.length > 0) {
|
||||
console.log(`前 ${rows.length} 条记录:`);
|
||||
console.table(rows);
|
||||
} else {
|
||||
console.log('表中暂无数据');
|
||||
}
|
||||
|
||||
return { total: count[0].total, rows };
|
||||
} catch (error) {
|
||||
console.error(`❌ 检查表 ${tableName} 数据失败:`, error.message);
|
||||
return { total: 0, rows: [] };
|
||||
}
|
||||
}
|
||||
|
||||
async interactiveMode() {
|
||||
console.log('\n🔧 进入交互模式 (输入 "exit" 退出)');
|
||||
console.log('可用命令:');
|
||||
console.log(' tables - 显示所有表');
|
||||
console.log(' desc <表名> - 显示表结构');
|
||||
console.log(' data <表名> - 显示表数据');
|
||||
console.log(' sql <SQL语句> - 执行SQL');
|
||||
console.log(' file <文件路径> - 执行SQL文件');
|
||||
console.log(' exit - 退出');
|
||||
|
||||
const readline = require('readline');
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
const askQuestion = () => {
|
||||
rl.question('\nmysql> ', async (input) => {
|
||||
const command = input.trim();
|
||||
|
||||
if (command === 'exit') {
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (command === 'tables') {
|
||||
await this.showTables();
|
||||
} else if (command.startsWith('desc ')) {
|
||||
const tableName = command.substring(5).trim();
|
||||
await this.describeTable(tableName);
|
||||
} else if (command.startsWith('data ')) {
|
||||
const tableName = command.substring(5).trim();
|
||||
await this.checkTableData(tableName);
|
||||
} else if (command.startsWith('sql ')) {
|
||||
const sql = command.substring(4).trim();
|
||||
await this.executeSQL(sql);
|
||||
} else if (command.startsWith('file ')) {
|
||||
const filePath = command.substring(5).trim();
|
||||
await this.executeSQLFile(filePath);
|
||||
} else if (command === '') {
|
||||
// 空命令,继续
|
||||
} else {
|
||||
console.log('未知命令,请输入 "exit" 退出或使用可用命令');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('执行命令时出错:', error.message);
|
||||
}
|
||||
|
||||
askQuestion();
|
||||
});
|
||||
};
|
||||
|
||||
askQuestion();
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
const db = new MySQLOperations();
|
||||
|
||||
// 连接数据库
|
||||
const connected = await db.connect();
|
||||
if (!connected) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// 显示数据库信息
|
||||
await db.showTables();
|
||||
|
||||
// 检查关键表的结构和数据
|
||||
const keyTables = ['users', 'suppliers', 'orders', 'drivers', 'vehicles'];
|
||||
|
||||
for (const table of keyTables) {
|
||||
console.log(`\n${'='.repeat(50)}`);
|
||||
await db.describeTable(table);
|
||||
await db.checkTableData(table, 3);
|
||||
}
|
||||
|
||||
// 进入交互模式
|
||||
await db.interactiveMode();
|
||||
|
||||
} catch (error) {
|
||||
console.error('操作过程中出错:', error.message);
|
||||
} finally {
|
||||
await db.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
main().catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = MySQLOperations;
|
||||
115
scripts/database/test_insert.js
Normal file
115
scripts/database/test_insert.js
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
async function testInsert() {
|
||||
console.log('开始测试数据插入...');
|
||||
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall',
|
||||
multipleStatements: true
|
||||
});
|
||||
|
||||
try {
|
||||
// 读取修复后的测试数据文件
|
||||
const sqlContent = await fs.readFile('./init_test_data_fixed.sql', 'utf8');
|
||||
console.log('SQL文件读取成功,长度:', sqlContent.length);
|
||||
|
||||
// 更智能的SQL语句分割 - 处理多行INSERT
|
||||
const statements = [];
|
||||
let currentStatement = '';
|
||||
let inInsert = false;
|
||||
|
||||
const lines = sqlContent.split('\n');
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim();
|
||||
|
||||
// 跳过注释和空行
|
||||
if (trimmedLine.startsWith('--') || trimmedLine === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测INSERT语句开始
|
||||
if (trimmedLine.toUpperCase().startsWith('INSERT')) {
|
||||
if (currentStatement) {
|
||||
statements.push(currentStatement.trim());
|
||||
}
|
||||
currentStatement = line;
|
||||
inInsert = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果在INSERT语句中
|
||||
if (inInsert) {
|
||||
currentStatement += '\n' + line;
|
||||
// 检测INSERT语句结束(以分号结尾)
|
||||
if (trimmedLine.endsWith(';')) {
|
||||
statements.push(currentStatement.trim());
|
||||
currentStatement = '';
|
||||
inInsert = false;
|
||||
}
|
||||
} else {
|
||||
// 处理其他语句
|
||||
if (currentStatement) {
|
||||
currentStatement += '\n' + line;
|
||||
} else {
|
||||
currentStatement = line;
|
||||
}
|
||||
|
||||
if (trimmedLine.endsWith(';')) {
|
||||
statements.push(currentStatement.trim());
|
||||
currentStatement = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一个语句(如果有)
|
||||
if (currentStatement.trim()) {
|
||||
statements.push(currentStatement.trim());
|
||||
}
|
||||
|
||||
console.log('总共解析出', statements.length, '条SQL语句');
|
||||
|
||||
// 查找INSERT语句
|
||||
const insertStatements = statements.filter(stmt => stmt.toUpperCase().includes('INSERT'));
|
||||
console.log('其中INSERT语句', insertStatements.length, '条');
|
||||
|
||||
// 执行第一条INSERT语句
|
||||
if (insertStatements.length > 0) {
|
||||
const firstInsert = insertStatements[0];
|
||||
console.log('执行第一条INSERT语句:');
|
||||
console.log(firstInsert.substring(0, 200) + '...');
|
||||
|
||||
try {
|
||||
const [result] = await connection.execute(firstInsert);
|
||||
console.log('✅ 插入成功,影响行数:', result.affectedRows);
|
||||
} catch (error) {
|
||||
console.error('❌ 插入失败:', error.message);
|
||||
console.error('错误代码:', error.code);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查users表数据
|
||||
const [users] = await connection.execute('SELECT COUNT(*) as count FROM users');
|
||||
console.log('users表当前记录数:', users[0].count);
|
||||
|
||||
// 如果有数据,显示前几条
|
||||
if (users[0].count > 0) {
|
||||
const [userList] = await connection.execute('SELECT id, username, user_type FROM users LIMIT 3');
|
||||
console.log('用户数据示例:');
|
||||
userList.forEach(user => console.log(user));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试失败:', error.message);
|
||||
} finally {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
testInsert().catch(console.error);
|
||||
57
scripts/database/test_remote_connection.js
Normal file
57
scripts/database/test_remote_connection.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function testRemoteConnection() {
|
||||
// 从.env文件读取配置
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
console.log('正在测试远程数据库连接...');
|
||||
console.log(`主机: ${config.host}:${config.port}`);
|
||||
console.log(`数据库: ${config.database}`);
|
||||
console.log(`用户: ${config.user}`);
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection(config);
|
||||
console.log('✅ 远程数据库连接成功!');
|
||||
|
||||
// 测试查询
|
||||
const [rows] = await connection.execute('SELECT VERSION() as version');
|
||||
console.log(`MySQL版本: ${rows[0].version}`);
|
||||
|
||||
// 检查数据库是否存在
|
||||
const [databases] = await connection.execute('SHOW DATABASES');
|
||||
const dbExists = databases.some(db => db.Database === config.database);
|
||||
console.log(`数据库 ${config.database} 存在: ${dbExists ? '是' : '否'}`);
|
||||
|
||||
if (dbExists) {
|
||||
// 检查表列表
|
||||
const [tables] = await connection.execute('SHOW TABLES');
|
||||
console.log(`\n数据库中的表 (${tables.length}个):`);
|
||||
tables.forEach(table => {
|
||||
console.log(`- ${Object.values(table)[0]}`);
|
||||
});
|
||||
}
|
||||
|
||||
await connection.end();
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 远程数据库连接失败:');
|
||||
console.error(`错误信息: ${error.message}`);
|
||||
console.error(`错误代码: ${error.code}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
testRemoteConnection().then(success => {
|
||||
process.exit(success ? 0 : 1);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
76
scripts/database/test_suppliers_api.js
Normal file
76
scripts/database/test_suppliers_api.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const http = require('http');
|
||||
|
||||
function testSuppliersAPI() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: 'localhost',
|
||||
port: 3000,
|
||||
path: '/api/suppliers',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 5000
|
||||
};
|
||||
|
||||
console.log('正在测试suppliers API接口...');
|
||||
console.log(`请求: GET http://localhost:3000/api/suppliers`);
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
console.log(`\n响应状态码: ${res.statusCode}`);
|
||||
console.log(`响应头: ${JSON.stringify(res.headers, null, 2)}`);
|
||||
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
console.log('\n响应数据:');
|
||||
console.log(JSON.stringify(jsonData, null, 2));
|
||||
|
||||
if (res.statusCode === 200 && jsonData.code === 200) {
|
||||
console.log('\n✅ suppliers API接口测试成功!');
|
||||
console.log(`返回数据条数: ${jsonData.data?.list?.length || 0}`);
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('\n❌ suppliers API接口返回错误');
|
||||
resolve(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('\n❌ 解析响应数据失败:', error.message);
|
||||
console.log('原始响应:', data);
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
console.error('\n❌ 请求失败:', error.message);
|
||||
if (error.code === 'ECONNREFUSED') {
|
||||
console.log('提示: 请确保后端服务正在运行 (npm start)');
|
||||
}
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
req.on('timeout', () => {
|
||||
console.error('\n❌ 请求超时');
|
||||
req.destroy();
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
testSuppliersAPI().then(success => {
|
||||
console.log(`\n测试结果: ${success ? '成功' : '失败'}`);
|
||||
process.exit(success ? 0 : 1);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
104
scripts/database/update_suppliers_table.js
Normal file
104
scripts/database/update_suppliers_table.js
Normal file
@@ -0,0 +1,104 @@
|
||||
const mysql = require('../../backend/node_modules/mysql2/promise');
|
||||
|
||||
async function updateSuppliersTable() {
|
||||
const config = {
|
||||
host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com',
|
||||
port: 20784,
|
||||
user: 'jiebanke',
|
||||
password: 'aiot741$12346',
|
||||
database: 'niumall'
|
||||
};
|
||||
|
||||
try {
|
||||
const connection = await mysql.createConnection(config);
|
||||
console.log('正在更新suppliers表结构...');
|
||||
|
||||
// 需要添加的字段
|
||||
const fieldsToAdd = [
|
||||
{
|
||||
name: 'bank_account',
|
||||
definition: 'varchar(50) DEFAULT NULL COMMENT \'银行账号\'',
|
||||
after: 'phone'
|
||||
},
|
||||
{
|
||||
name: 'bank_name',
|
||||
definition: 'varchar(100) DEFAULT NULL COMMENT \'开户银行\'',
|
||||
after: 'bank_account'
|
||||
},
|
||||
{
|
||||
name: 'tax_number',
|
||||
definition: 'varchar(30) DEFAULT NULL COMMENT \'税务登记号\'',
|
||||
after: 'bank_name'
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
definition: 'text DEFAULT NULL COMMENT \'备注信息\'',
|
||||
after: 'tax_number'
|
||||
}
|
||||
];
|
||||
|
||||
// 逐个添加字段
|
||||
for (const field of fieldsToAdd) {
|
||||
try {
|
||||
const sql = `ALTER TABLE suppliers ADD COLUMN ${field.name} ${field.definition} AFTER ${field.after}`;
|
||||
console.log(`添加字段 ${field.name}...`);
|
||||
await connection.execute(sql);
|
||||
console.log(`✅ 字段 ${field.name} 添加成功`);
|
||||
} catch (error) {
|
||||
if (error.code === 'ER_DUP_FIELDNAME') {
|
||||
console.log(`⚠️ 字段 ${field.name} 已存在,跳过`);
|
||||
} else {
|
||||
console.error(`❌ 添加字段 ${field.name} 失败:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证字段是否添加成功
|
||||
console.log('\n验证表结构更新...');
|
||||
const [columns] = await connection.execute(`
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'niumall'
|
||||
AND TABLE_NAME = 'suppliers'
|
||||
AND COLUMN_NAME IN ('bank_account', 'bank_name', 'tax_number', 'notes')
|
||||
ORDER BY ORDINAL_POSITION
|
||||
`);
|
||||
|
||||
console.log('\n新添加的字段:');
|
||||
console.table(columns.map(col => ({
|
||||
字段名: col.COLUMN_NAME,
|
||||
数据类型: col.DATA_TYPE,
|
||||
允许空值: col.IS_NULLABLE,
|
||||
默认值: col.COLUMN_DEFAULT,
|
||||
注释: col.COLUMN_COMMENT
|
||||
})));
|
||||
|
||||
// 检查总字段数
|
||||
const [allColumns] = await connection.execute(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'niumall'
|
||||
AND TABLE_NAME = 'suppliers'
|
||||
`);
|
||||
|
||||
console.log(`\nsuppliers表总字段数: ${allColumns[0].count}`);
|
||||
|
||||
await connection.end();
|
||||
console.log('\n✅ suppliers表结构更新完成!');
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 更新表结构失败:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
updateSuppliersTable().then(() => {
|
||||
console.log('表结构更新成功');
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.error('脚本执行失败:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user