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

This commit is contained in:
2025-09-21 18:30:38 +08:00
parent 198d10f4f9
commit cc2a351f84
21 changed files with 3435 additions and 43 deletions

View 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();

View 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 };

View 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 };

View 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 };

View 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 };

View 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);
});

View 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);

View 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);
});

View 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);
});

View 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);

View 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);

View File

@@ -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;

View File

@@ -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. 供应商测试数据

View File

@@ -0,0 +1,128 @@
-- =====================================================
-- 牛牛商城测试数据初始化脚本
-- 创建时间: 2024-01-21
-- 描述: 插入测试数据,用于开发和测试环境
-- =====================================================
-- 设置字符集
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- =====================================================
-- 1. 用户测试数据
-- =====================================================
INSERT INTO `users` (`id`, `uuid`, `username`, `password_hash`, `nickname`, `real_name`, `phone`, `email`, `user_type`, `company_name`, `status`, `registration_source`, `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;

View File

@@ -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;
}

View 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);
});

View 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;

View 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);

View 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);
});

View 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);
});

View 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);
});