/** * 测试采购-付款-物流-退库一体化流程数据库表结构 * 验证所有表是否正确创建,字段是否完整 */ const sqlite3 = require('sqlite3').verbose(); const path = require('path'); const dbPath = path.join(__dirname, 'company_finance.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('数据库连接失败:', err.message); process.exit(1); } console.log('SQLite数据库连接成功:', dbPath); }); const runAllSQL = (sql, params = []) => { return new Promise((resolve, reject) => { db.all(sql, params, (err, rows) => { if (err) { reject(err); } else { resolve(rows); } }); }); }; async function testTableStructure() { let passed = 0; let failed = 0; const errors = []; try { console.log('\n========================================'); console.log('测试数据库表结构'); console.log('========================================\n'); const expectedTables = { 'logistics_companies': [ 'id', 'code', 'name', 'address', 'phone', 'email', 'quotation_description', 'status', 'remark', 'created_at', 'updated_at' ], 'logistics_company_payment_infos': [ 'id', 'logistics_company_id', 'account_name', 'account_number', 'bank_name', 'qr_code', 'is_default', 'created_at', 'updated_at' ], 'logistics_records': [ 'id', 'code', 'purchase_order_id', 'ship_from', 'logistics_company_id', 'logistics_company', 'tracking_number', 'ship_date', 'ship_location', 'estimated_arrival_date', 'customs_arrival_date', 'customs_clearance_date', 'use_hub', 'hub_arrival_date', 'hub_receiver', 'hub_verified_quantity', 'second_ship_date', 'primary_freight', 'primary_freight_currency', 'primary_freight_status', 'primary_freight_document', 'secondary_freight', 'secondary_freight_currency', 'secondary_freight_status', 'driver_phone', 'cargo_weight', 'transport_distance', 'final_arrival_date', 'final_location', 'status', 'remark', 'created_by', 'created_at', 'updated_at' ], 'verification_records': [ 'id', 'code', 'purchase_order_id', 'logistics_record_id', 'verification_type', 'verification_date', 'verifier', 'items', 'total_ordered', 'total_received', 'total_verified', 'total_rejected', 'project_id', 'storage_type', 'status', 'remark', 'attachments', 'created_at' ], 'return_records': [ 'id', 'code', 'project_id', 'return_type', 'return_date', 'applicant', 'items', 'total_quantity', 'total_amount', 'cost_adjustment', 'refund_amount', 'status', 'remark', 'attachments', 'created_at' ], 'material_price_history': [ 'id', 'product_id', 'purchase_order_id', 'supplier_id', 'supplier_country', 'unit_price', 'currency', 'quantity', 'purchase_date', 'created_at' ], 'project_material_inventory': [ 'id', 'project_id', 'product_id', 'product_name', 'unit', 'purchased_quantity', 'received_quantity', 'used_quantity', 'returned_quantity', 'current_quantity', 'total_amount', 'average_price', 'created_at', 'updated_at' ] }; const expectedExtendedFields = { 'purchase_orders': ['project_id', 'supplier_country', 'estimated_amount', 'paid_amount', 'contract_url', 'quotation_url', 'actual_delivery_date', 'remark'], 'purchase_order_items': ['received_quantity', 'verified_quantity'], 'payment_plans': ['stage', 'planned_date', 'planned_amount', 'planned_percentage', 'actual_amount', 'actual_date', 'payment_request_id', 'reminder_days', 'remark'], 'payment_requests': ['payment_type', 'purchase_order_id', 'logistics_company_id', 'logistics_document_url', 'driver_phone', 'cargo_weight', 'transport_distance'], 'suppliers': ['supply_category', 'country', 'address', 'phone', 'email', 'status'], 'purchase_requests': ['expected_date'] }; console.log('--- 测试新创建的表 ---\n'); for (const [tableName, expectedColumns] of Object.entries(expectedTables)) { console.log(`测试表: ${tableName}`); const tableInfo = await runAllSQL(`PRAGMA table_info(${tableName})`); if (tableInfo.length === 0) { console.log(` ❌ 表 ${tableName} 不存在`); failed++; errors.push(`表 ${tableName} 不存在`); continue; } const actualColumns = tableInfo.map(col => col.name); let tablePassed = true; for (const col of expectedColumns) { if (!actualColumns.includes(col)) { console.log(` ❌ 缺少字段: ${col}`); tablePassed = false; errors.push(`表 ${tableName} 缺少字段 ${col}`); } } if (tablePassed) { console.log(` ✅ 表 ${tableName} 结构正确 (${actualColumns.length} 个字段)`); passed++; } else { failed++; } } console.log('\n--- 测试扩展的字段 ---\n'); for (const [tableName, expectedColumns] of Object.entries(expectedExtendedFields)) { console.log(`测试表扩展字段: ${tableName}`); const tableInfo = await runAllSQL(`PRAGMA table_info(${tableName})`); if (tableInfo.length === 0) { console.log(` ❌ 表 ${tableName} 不存在`); failed++; continue; } const actualColumns = tableInfo.map(col => col.name); let tablePassed = true; for (const col of expectedColumns) { if (!actualColumns.includes(col)) { console.log(` ❌ 缺少扩展字段: ${col}`); tablePassed = false; errors.push(`表 ${tableName} 缺少扩展字段 ${col}`); } } if (tablePassed) { console.log(` ✅ 表 ${tableName} 扩展字段正确`); passed++; } else { failed++; } } console.log('\n--- 测试索引 ---\n'); const expectedIndexes = [ { table: 'logistics_companies', index: 'idx_logistics_companies_code' }, { table: 'logistics_companies', index: 'idx_logistics_companies_status' }, { table: 'logistics_records', index: 'idx_logistics_records_code' }, { table: 'logistics_records', index: 'idx_logistics_records_order' }, { table: 'logistics_records', index: 'idx_logistics_records_status' }, { table: 'verification_records', index: 'idx_verification_records_code' }, { table: 'verification_records', index: 'idx_verification_records_order' }, { table: 'return_records', index: 'idx_return_records_code' }, { table: 'return_records', index: 'idx_return_records_project' }, { table: 'material_price_history', index: 'idx_material_price_history_product' }, { table: 'project_material_inventory', index: 'idx_project_material_inventory_project' }, { table: 'purchase_orders', index: 'idx_purchase_orders_project' }, { table: 'payment_requests', index: 'idx_payment_requests_type' } ]; for (const { table, index } of expectedIndexes) { const indexList = await runAllSQL(`PRAGMA index_list(${table})`); const indexNames = indexList.map(i => i.name); if (indexNames.includes(index)) { console.log(` ✅ 索引 ${index} 存在`); passed++; } else { console.log(` ❌ 索引 ${index} 不存在`); failed++; errors.push(`索引 ${index} 不存在`); } } console.log('\n========================================'); console.log('测试结果汇总'); console.log('========================================\n'); console.log(`通过: ${passed}`); console.log(`失败: ${failed}`); if (errors.length > 0) { console.log('\n错误详情:'); errors.forEach(err => console.log(` - ${err}`)); } console.log('\n========================================'); if (failed === 0) { console.log('✅ 所有测试通过!数据库结构符合设计方案。'); } else { console.log('❌ 部分测试失败,请检查错误详情。'); } console.log('========================================\n'); db.close(); process.exit(failed > 0 ? 1 : 0); } catch (error) { console.error('测试失败:', error); db.close(); process.exit(1); } } testTableStructure();