init:代码初始化
This commit is contained in:
159
scripts/import-data.js
Normal file
159
scripts/import-data.js
Normal file
@@ -0,0 +1,159 @@
|
||||
require('dotenv').config();
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Price = require('../src/models/Price');
|
||||
|
||||
/**
|
||||
* 数据导入脚本
|
||||
* 从 JSON 文件导入钢材价格数据到数据库
|
||||
*/
|
||||
|
||||
// 数据文件映射
|
||||
const dataFiles = [
|
||||
{ file: '刚协指导价.json', source: '云南钢协', priceField: 'PR_PRICESET_HANGPRICE' },
|
||||
{ file: '钢材网架.json', source: '我的钢铁', priceField: 'PR_PRICESET_HANGPRICE' },
|
||||
{ file: '钢厂指导价.json', source: '德钢指导价', priceField: 'PR_PRICESET_HANGPRICE' }
|
||||
];
|
||||
|
||||
/**
|
||||
* 转换数据格式
|
||||
*/
|
||||
function transformData(rawData, source, priceField) {
|
||||
if (!rawData || !rawData.data || !rawData.data.page || !rawData.data.page.result) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return rawData.data.page.result.map(item => {
|
||||
return {
|
||||
price_id: item.PRICE_ID || null,
|
||||
goods_material: item.GOODS_MATERIAL || '未知',
|
||||
goods_spec: item.GOODS_SPEC || '未知',
|
||||
partsname_name: item.PARTSNAME_NAME || '未知',
|
||||
productarea_name: item.PRODUCTAREA_NAME || '未知',
|
||||
price_source: item.PR_PRICE_SOURCE || source,
|
||||
price_region: item.PR_PRICE_REGION || '未知',
|
||||
pntree_name: item.PNTREE_NAME || '钢筋',
|
||||
price_date: item.PIRCE_DATE || null,
|
||||
make_price: item.PR_PRICESET_MAKEPRICE || null,
|
||||
hang_price: item[priceField] || 0,
|
||||
last_make_price: item.PR_LAST_PRICESET_MAKEPRICE || null,
|
||||
last_hang_price: item.PR_LAST_PRICESET_HANGPRICE || 0,
|
||||
make_price_updw: item.PR_MAKEPRICE_UPDW || null,
|
||||
hang_price_updw: item.PR_HANGPRICE_UPDW || '0',
|
||||
operator_code: item.OPERATOR_CODE || null,
|
||||
operator_name: item.OPERATOR_NAME || null
|
||||
};
|
||||
}).filter(item => item.price_date && item.hang_price > 0); // 过滤无效数据
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入单个数据文件
|
||||
*/
|
||||
async function importFile(filePath, source, priceField) {
|
||||
try {
|
||||
console.log(`\n📄 正在读取文件: ${path.basename(filePath)}`);
|
||||
|
||||
// 读取 JSON 文件
|
||||
const rawData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
|
||||
// 转换数据格式
|
||||
const prices = transformData(rawData, source, priceField);
|
||||
console.log(`✅ 解析到 ${prices.length} 条有效数据`);
|
||||
|
||||
if (prices.length === 0) {
|
||||
console.log('⚠️ 没有有效数据可导入');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 批量插入数据库(每批 1000 条)
|
||||
const batchSize = 1000;
|
||||
let totalImported = 0;
|
||||
|
||||
for (let i = 0; i < prices.length; i += batchSize) {
|
||||
const batch = prices.slice(i, i + batchSize);
|
||||
const imported = await Price.batchInsert(batch);
|
||||
totalImported += imported;
|
||||
console.log(` 进度: ${Math.min(i + batchSize, prices.length)}/${prices.length} 条`);
|
||||
}
|
||||
|
||||
console.log(`✅ 成功导入 ${totalImported} 条数据`);
|
||||
return totalImported;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ 导入文件失败 ${path.basename(filePath)}:`, error.message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主导入函数
|
||||
*/
|
||||
async function importAllData() {
|
||||
console.log('🚀 开始导入钢材价格数据...\n');
|
||||
|
||||
let totalImported = 0;
|
||||
const dataDir = path.join(__dirname, '../data');
|
||||
|
||||
for (const { file, source, priceField } of dataFiles) {
|
||||
const filePath = path.join(dataDir, file);
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.log(`⚠️ 文件不存在,跳过: ${file}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const count = await importFile(filePath, source, priceField);
|
||||
totalImported += count;
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log(`🎉 数据导入完成!总计导入 ${totalImported} 条数据`);
|
||||
console.log('='.repeat(50));
|
||||
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看导入统计
|
||||
*/
|
||||
async function showStats() {
|
||||
try {
|
||||
const total = await Price.count();
|
||||
const stats = await Price.getStats({});
|
||||
|
||||
console.log('\n📊 数据库统计信息:');
|
||||
console.log(` 总记录数: ${total}`);
|
||||
|
||||
// 处理 avgPrice 可能是字符串或 null 的情况
|
||||
if (stats.avgPrice) {
|
||||
const avgPrice = typeof stats.avgPrice === 'number'
|
||||
? stats.avgPrice.toFixed(2)
|
||||
: parseFloat(stats.avgPrice).toFixed(2);
|
||||
console.log(` 平均价格: ${avgPrice} 元/吨`);
|
||||
} else {
|
||||
console.log(` 平均价格: N/A`);
|
||||
}
|
||||
|
||||
console.log(` 最低价格: ${stats.minPrice || 'N/A'} 元/吨`);
|
||||
console.log(` 最高价格: ${stats.maxPrice || 'N/A'} 元/吨`);
|
||||
} catch (error) {
|
||||
console.error('❌ 获取统计信息失败:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
importAllData()
|
||||
.then(() => showStats())
|
||||
.then(() => {
|
||||
console.log('\n✅ 脚本执行完成');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('\n❌ 脚本执行失败:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { importAllData, importFile, transformData };
|
||||
94
scripts/init-db.js
Normal file
94
scripts/init-db.js
Normal file
@@ -0,0 +1,94 @@
|
||||
require('dotenv').config();
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
/**
|
||||
* 数据库初始化脚本
|
||||
* 创建数据库和表结构
|
||||
*/
|
||||
async function initDatabase() {
|
||||
const connection = await mysql.createConnection({
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_PORT || '3306'),
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
multipleStatements: true
|
||||
});
|
||||
|
||||
try {
|
||||
const dbName = process.env.DB_NAME || 'steel_prices';
|
||||
|
||||
// 创建数据库
|
||||
console.log(`📦 正在创建数据库: ${dbName}`);
|
||||
await connection.execute(
|
||||
`CREATE DATABASE IF NOT EXISTS ${dbName} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`
|
||||
);
|
||||
console.log('✅ 数据库创建成功');
|
||||
|
||||
// 切换到目标数据库
|
||||
await connection.changeUser({ database: dbName });
|
||||
|
||||
// 创建价格表
|
||||
console.log('📋 正在创建价格表...');
|
||||
const createTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS prices (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '自增主键',
|
||||
price_id VARCHAR(64) UNIQUE NOT NULL COMMENT '价格唯一ID',
|
||||
goods_material VARCHAR(32) NOT NULL COMMENT '材质牌号',
|
||||
goods_spec VARCHAR(16) NOT NULL COMMENT '规格型号',
|
||||
partsname_name VARCHAR(32) NOT NULL COMMENT '品名',
|
||||
productarea_name VARCHAR(64) NOT NULL COMMENT '产地/钢厂',
|
||||
price_source VARCHAR(32) NOT NULL COMMENT '价格来源',
|
||||
price_region VARCHAR(32) NOT NULL COMMENT '价格地区',
|
||||
pntree_name VARCHAR(32) NOT NULL COMMENT '分类名称',
|
||||
price_date DATETIME NOT NULL COMMENT '价格日期',
|
||||
make_price INT DEFAULT NULL COMMENT '钢厂价(元/吨)',
|
||||
hang_price INT NOT NULL COMMENT '挂牌价(元/吨)',
|
||||
last_make_price INT DEFAULT NULL COMMENT '上次钢厂价',
|
||||
last_hang_price INT DEFAULT NULL COMMENT '上次挂牌价',
|
||||
make_price_updw VARCHAR(8) DEFAULT NULL COMMENT '钢厂价涨跌',
|
||||
hang_price_updw VARCHAR(8) DEFAULT NULL COMMENT '挂牌价涨跌',
|
||||
operator_code VARCHAR(16) DEFAULT NULL COMMENT '操作员代码',
|
||||
operator_name VARCHAR(32) DEFAULT NULL COMMENT '操作员名称',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
INDEX idx_price_date (price_date),
|
||||
INDEX idx_region_material (price_region, goods_material),
|
||||
INDEX idx_source_date (price_source, price_date),
|
||||
INDEX idx_goods_spec (goods_spec)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='钢材价格表'
|
||||
`;
|
||||
|
||||
await connection.execute(createTableSQL);
|
||||
console.log('✅ 价格表创建成功');
|
||||
|
||||
// 显示表结构信息
|
||||
const [tables] = await connection.execute('SHOW TABLES');
|
||||
console.log('\n📊 当前数据库表:');
|
||||
tables.forEach(table => {
|
||||
console.log(` - ${Object.values(table)[0]}`);
|
||||
});
|
||||
|
||||
console.log('\n🎉 数据库初始化完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库初始化失败:', error.message);
|
||||
throw error;
|
||||
} finally {
|
||||
await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
initDatabase()
|
||||
.then(() => {
|
||||
console.log('✅ 脚本执行完成');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('❌ 脚本执行失败:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = initDatabase;
|
||||
Reference in New Issue
Block a user