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