const fs = require('fs'); const path = require('path'); /** * 代码更新脚本:将所有 order_allocations 表引用更新为 transfers 表 * * 更新策略: * 1. 将 order_allocations 表名替换为 transfers * 2. 更新相关的字段映射和查询逻辑 * 3. 添加必要的 WHERE 条件来过滤 allocation 类型的记录 */ class CodeUpdater { constructor() { this.filesToUpdate = [ 'services/matchingService.js', 'routes/matchingAdmin.js', 'routes/transfers.js', 'routes/matching.js' ]; // 字段映射关系 this.fieldMappings = { // order_allocations 字段 -> transfers 字段 'matching_order_id': 'matching_order_id', 'from_user_id': 'from_user_id', 'to_user_id': 'to_user_id', 'amount': 'amount', 'cycle_number': 'cycle_number', 'status': 'status', 'transfer_id': 'id', // order_allocations.transfer_id 对应 transfers.id 'created_at': 'created_at', 'updated_at': 'updated_at', 'confirmed_at': 'confirmed_at', 'outbound_date': 'outbound_date', 'return_date': 'return_date', 'can_return_after': 'can_return_after' }; } /** * 更新单个文件 * @param {string} filePath - 文件路径 */ async updateFile(filePath) { const fullPath = path.join(process.cwd(), filePath); if (!fs.existsSync(fullPath)) { console.log(`文件不存在: ${filePath}`); return; } console.log(`正在更新文件: ${filePath}`); let content = fs.readFileSync(fullPath, 'utf8'); let originalContent = content; // 1. 替换表名 content = content.replace(/\border_allocations\b/g, 'transfers'); // 2. 添加 source_type 过滤条件 content = this.addSourceTypeFilters(content); // 3. 更新 INSERT 语句 content = this.updateInsertStatements(content); // 4. 更新特定的查询逻辑 content = this.updateSpecificQueries(content, filePath); // 5. 更新注释 content = this.updateComments(content); if (content !== originalContent) { // 创建备份 fs.writeFileSync(fullPath + '.backup', originalContent); // 写入更新后的内容 fs.writeFileSync(fullPath, content); console.log(`✓ 已更新: ${filePath}`); console.log(`✓ 备份已创建: ${filePath}.backup`); } else { console.log(`- 无需更新: ${filePath}`); } } /** * 添加 source_type 过滤条件 * @param {string} content - 文件内容 * @returns {string} 更新后的内容 */ addSourceTypeFilters(content) { // 在 FROM transfers 后添加 WHERE source_type = 'allocation' 条件 // 但要避免重复添加 // 匹配 FROM transfers 但不包含 source_type 的情况 content = content.replace( /FROM transfers(?!.*source_type)([\s\S]*?)(?=WHERE|ORDER|GROUP|LIMIT|;|$)/gi, (match, afterFrom) => { // 如果已经有 WHERE 子句,添加 AND 条件 if (afterFrom.includes('WHERE')) { return match.replace(/WHERE/, 'WHERE source_type = \'allocation\' AND'); } else { // 如果没有 WHERE 子句,添加新的 WHERE 条件 const beforeNextClause = match.match(/(ORDER|GROUP|LIMIT|;|$)/); if (beforeNextClause) { return match.replace(beforeNextClause[0], ` WHERE source_type = 'allocation' ${beforeNextClause[0]}`); } else { return match + " WHERE source_type = 'allocation'"; } } } ); return content; } /** * 更新 INSERT 语句 * @param {string} content - 文件内容 * @returns {string} 更新后的内容 */ updateInsertStatements(content) { // 更新 INSERT INTO transfers 语句,添加必要的字段 content = content.replace( /INSERT INTO transfers\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/gi, (match, fields, values) => { // 如果字段列表中没有 source_type,添加它 if (!fields.includes('source_type')) { const fieldList = fields.trim() + ', source_type'; const valueList = values.trim() + ', \'allocation\''; return `INSERT INTO transfers (${fieldList}) VALUES (${valueList})`; } return match; } ); return content; } /** * 更新特定的查询逻辑 * @param {string} content - 文件内容 * @param {string} filePath - 文件路径 * @returns {string} 更新后的内容 */ updateSpecificQueries(content, filePath) { // 根据不同文件进行特定更新 if (filePath.includes('matchingService.js')) { // 更新 matchingService.js 中的特定逻辑 content = this.updateMatchingServiceQueries(content); } if (filePath.includes('matchingAdmin.js')) { // 更新 matchingAdmin.js 中的特定逻辑 content = this.updateMatchingAdminQueries(content); } return content; } /** * 更新 matchingService.js 中的查询 * @param {string} content - 文件内容 * @returns {string} 更新后的内容 */ updateMatchingServiceQueries(content) { // 更新确认分配的逻辑 content = content.replace( /UPDATE transfers SET status = "confirmed", transfer_id = \?, confirmed_at = NOW\(\) WHERE id = \?/g, 'UPDATE transfers SET status = "confirmed", confirmed_at = NOW() WHERE id = ? AND source_type = \'allocation\'' ); // 更新获取分配记录的查询 content = content.replace( /SELECT \* FROM transfers WHERE id = \? AND from_user_id = \?/g, 'SELECT * FROM transfers WHERE id = ? AND from_user_id = ? AND source_type = \'allocation\'' ); return content; } /** * 更新 matchingAdmin.js 中的查询 * @param {string} content - 文件内容 * @returns {string} 更新后的内容 */ updateMatchingAdminQueries(content) { // 更新管理员查询逻辑,确保只查询 allocation 类型的记录 content = content.replace( /FROM transfers oa/g, 'FROM transfers oa WHERE oa.source_type = \'allocation\'' ); return content; } /** * 更新注释 * @param {string} content - 文件内容 * @returns {string} 更新后的内容 */ updateComments(content) { content = content.replace(/order_allocations/g, 'transfers (allocation type)'); content = content.replace(/订单分配/g, '转账分配'); content = content.replace(/分配表/g, '转账表(分配类型)'); return content; } /** * 执行所有文件的更新 */ async updateAllFiles() { console.log('开始更新代码引用...'); console.log('=' .repeat(60)); for (const filePath of this.filesToUpdate) { try { await this.updateFile(filePath); } catch (error) { console.error(`更新文件 ${filePath} 失败:`, error.message); } } console.log('\n' + '=' .repeat(60)); console.log('✓ 代码更新完成!'); console.log('\n注意事项:'); console.log('1. 所有原始文件已备份为 .backup 文件'); console.log('2. 请测试更新后的代码功能是否正常'); console.log('3. 如有问题,可以使用备份文件恢复'); console.log('4. 确认无误后可删除 .backup 文件'); } /** * 恢复所有备份文件 */ async restoreBackups() { console.log('开始恢复备份文件...'); for (const filePath of this.filesToUpdate) { const fullPath = path.join(process.cwd(), filePath); const backupPath = fullPath + '.backup'; if (fs.existsSync(backupPath)) { fs.copyFileSync(backupPath, fullPath); console.log(`✓ 已恢复: ${filePath}`); } } console.log('✓ 备份恢复完成!'); } } async function main() { const updater = new CodeUpdater(); const args = process.argv.slice(2); if (args.includes('--restore')) { await updater.restoreBackups(); } else { await updater.updateAllFiles(); } } // 如果直接运行此脚本 if (require.main === module) { main().catch(console.error); } module.exports = CodeUpdater;