/** * 商品扩展配置数据迁移脚本 - Node.js * Feature: database-migration, Property 2: 数据记录数一致性 * Validates: Requirements 2.6 * * 源表: MySQL goods_extend (34 条记录) * 目标表: SQL Server goods_extensions * * 字段映射 (直接映射,无需转换): * - id -> id * - goods_id -> goods_id * - pay_wechat -> pay_wechat * - pay_balance -> pay_balance * - pay_currency -> pay_currency * - pay_currency2 -> pay_currency2 * - pay_coupon -> pay_coupon * - is_deduction -> is_deduction */ const mysql = require('mysql2/promise'); const sql = require('mssql'); // MySQL 配置 const mysqlConfig = { host: '192.168.195.16', port: 1887, user: 'root', password: 'Dbt@com@123', database: 'youdas', charset: 'utf8mb4' }; // SQL Server 配置 const sqlServerConfig = { server: '192.168.195.15', port: 1433, user: 'sa', password: 'Dbt@com@123', database: 'honey_box', options: { encrypt: false, trustServerCertificate: true } }; // 获取已迁移的商品扩展ID列表 async function getMigratedIds(pool) { const result = await pool.request().query('SELECT id FROM goods_extensions'); return new Set(result.recordset.map(r => r.id)); } // 批量插入商品扩展数据 async function insertGoodsExtensionsBatch(pool, extensions) { if (extensions.length === 0) return 0; let insertedCount = 0; for (const ext of extensions) { try { const insertSql = ` SET IDENTITY_INSERT goods_extensions ON; INSERT INTO goods_extensions ( id, goods_id, pay_wechat, pay_balance, pay_currency, pay_currency2, pay_coupon, is_deduction ) VALUES ( ${ext.id}, ${ext.goods_id || 0}, ${ext.pay_wechat || 0}, ${ext.pay_balance || 0}, ${ext.pay_currency || 0}, ${ext.pay_currency2 || 0}, ${ext.pay_coupon || 0}, ${ext.is_deduction || 0} ); SET IDENTITY_INSERT goods_extensions OFF;`; await pool.request().batch(insertSql); insertedCount++; } catch (err) { console.error(`插入商品扩展 ${ext.id} (goods_id: ${ext.goods_id}) 失败:`, err.message); } } return insertedCount; } async function main() { console.log('========================================'); console.log('商品扩展配置数据迁移脚本 - Node.js'); console.log('========================================\n'); let mysqlConn = null; let sqlPool = null; try { // 连接 MySQL console.log('正在连接 MySQL...'); mysqlConn = await mysql.createConnection(mysqlConfig); console.log('MySQL 连接成功\n'); // 连接 SQL Server console.log('正在连接 SQL Server...'); sqlPool = await sql.connect(sqlServerConfig); console.log('SQL Server 连接成功\n'); // 获取已迁移的ID console.log('正在获取已迁移的商品扩展ID...'); const migratedIds = await getMigratedIds(sqlPool); console.log(`已迁移商品扩展数: ${migratedIds.size}\n`); // 从 MySQL 获取所有商品扩展数据 console.log('正在从 MySQL 读取商品扩展数据...'); const [rows] = await mysqlConn.execute(` SELECT id, goods_id, pay_wechat, pay_balance, pay_currency, pay_currency2, pay_coupon, is_deduction FROM goods_extend ORDER BY id `); console.log(`MySQL 商品扩展总数: ${rows.length}\n`); // 过滤出未迁移的商品扩展 const extensionsToMigrate = rows.filter(ext => !migratedIds.has(ext.id)); console.log(`待迁移商品扩展数: ${extensionsToMigrate.length}\n`); if (extensionsToMigrate.length === 0) { console.log('所有商品扩展数据已迁移完成!'); } else { // 批量迁移(每批20条) const batchSize = 20; let totalInserted = 0; for (let i = 0; i < extensionsToMigrate.length; i += batchSize) { const batch = extensionsToMigrate.slice(i, i + batchSize); const inserted = await insertGoodsExtensionsBatch(sqlPool, batch); totalInserted += inserted; console.log(`进度: ${Math.min(i + batchSize, extensionsToMigrate.length)}/${extensionsToMigrate.length} (本批插入: ${inserted})`); } console.log(`\n迁移完成!共插入 ${totalInserted} 条记录`); } // 验证迁移结果 console.log('\n========================================'); console.log('迁移结果验证'); console.log('========================================'); const [mysqlCount] = await mysqlConn.execute('SELECT COUNT(*) as count FROM goods_extend'); const sqlResult = await sqlPool.request().query('SELECT COUNT(*) as count FROM goods_extensions'); console.log(`MySQL goods_extend 表记录数: ${mysqlCount[0].count}`); console.log(`SQL Server goods_extensions 表记录数: ${sqlResult.recordset[0].count}`); if (mysqlCount[0].count === sqlResult.recordset[0].count) { console.log('\n✅ 数据迁移完成,记录数一致!'); } else { console.log(`\n⚠️ 记录数不一致,差异: ${mysqlCount[0].count - sqlResult.recordset[0].count}`); } // 验证支付方式配置分布 console.log('\n========================================'); console.log('支付方式配置验证'); console.log('========================================'); const [mysqlPayWechat] = await mysqlConn.execute('SELECT COUNT(*) as count FROM goods_extend WHERE pay_wechat = 1'); const sqlPayWechatResult = await sqlPool.request().query('SELECT COUNT(*) as count FROM goods_extensions WHERE pay_wechat = 1'); console.log(`MySQL 支持微信支付的商品扩展数: ${mysqlPayWechat[0].count}`); console.log(`SQL Server 支持微信支付的商品扩展数: ${sqlPayWechatResult.recordset[0].count}`); const [mysqlPayBalance] = await mysqlConn.execute('SELECT COUNT(*) as count FROM goods_extend WHERE pay_balance = 1'); const sqlPayBalanceResult = await sqlPool.request().query('SELECT COUNT(*) as count FROM goods_extensions WHERE pay_balance = 1'); console.log(`MySQL 支持余额支付的商品扩展数: ${mysqlPayBalance[0].count}`); console.log(`SQL Server 支持余额支付的商品扩展数: ${sqlPayBalanceResult.recordset[0].count}`); const [mysqlPayCoupon] = await mysqlConn.execute('SELECT COUNT(*) as count FROM goods_extend WHERE pay_coupon = 1'); const sqlPayCouponResult = await sqlPool.request().query('SELECT COUNT(*) as count FROM goods_extensions WHERE pay_coupon = 1'); console.log(`MySQL 支持优惠券支付的商品扩展数: ${mysqlPayCoupon[0].count}`); console.log(`SQL Server 支持优惠券支付的商品扩展数: ${sqlPayCouponResult.recordset[0].count}`); if (mysqlPayWechat[0].count === sqlPayWechatResult.recordset[0].count && mysqlPayBalance[0].count === sqlPayBalanceResult.recordset[0].count && mysqlPayCoupon[0].count === sqlPayCouponResult.recordset[0].count) { console.log('\n✅ 支付方式配置迁移正确!'); } else { console.log('\n⚠️ 支付方式配置数量不一致!'); } } catch (err) { console.error('迁移过程中发生错误:', err); process.exit(1); } finally { // 关闭连接 if (mysqlConn) await mysqlConn.end(); if (sqlPool) await sqlPool.close(); } } main();