const {getDB} = require('../database'); const {logger, auditLogger} = require('../config/logger'); const {AppError} = require('../middleware/errorHandler'); const {TRANSFER_TYPES, TRANSFER_STATUS, ERROR_CODES, HTTP_STATUS} = require('../config/constants'); class TransferService { // 创建转账记录 async createTransfer(fromUserId, transferData) { const {to_user_id, amount, transfer_type, description, voucher_url} = transferData; const db = getDB(); try { // 验证用户是否存在 await this.validateUser(to_user_id); // 验证转账类型 if (!Object.values(TRANSFER_TYPES).includes(transfer_type)) { throw new AppError('无效的转账类型', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 检查余额(如果是用户转账)- 允许负余额转账 if (transfer_type === TRANSFER_TYPES.USER_TO_USER || transfer_type === TRANSFER_TYPES.USER_TO_SYSTEM) { if (!fromUserId) { throw new AppError('用户转账必须指定发送方用户', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 获取当前余额但不检查是否足够,允许负余额转账 await this.checkUserBalance(fromUserId, amount); } // 系统转账时,from_user_id 设为 null const actualFromUserId = transfer_type === TRANSFER_TYPES.SYSTEM_TO_USER ? null : fromUserId; // 生成批次ID const batch_id = this.generateBatchId(); // 插入转账记录 const currentTime = new Date(); const [result] = await db.execute( `INSERT INTO transfers (from_user_id, to_user_id, amount, transfer_type, status, description, voucher_url, batch_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [actualFromUserId, to_user_id, amount, transfer_type, TRANSFER_STATUS.PENDING, description || null, voucher_url || null, batch_id, currentTime] ); const transferId = result.insertId; // 记录审计日志 auditLogger.info('Transfer created', { transferId, fromUserId, toUserId: to_user_id, amount, transferType: transfer_type, batchId: batch_id }); logger.info('Transfer created successfully', {transferId, fromUserId, amount}); return { transfer_id: transferId, batch_id, status: TRANSFER_STATUS.PENDING }; } catch (error) { logger.error('Failed to create transfer', { error: error.message, fromUserId, transferData }); throw error; } } // 管理员解除坏账 async removeBadDebt(transferId, adminId, reason) { const db = getDB(); try { // 获取转账记录 const transfer = await this.getTransferById(transferId); if (!transfer) { throw new AppError('转账记录不存在', HTTP_STATUS.NOT_FOUND, ERROR_CODES.NOT_FOUND); } if (!transfer.is_bad_debt) { throw new AppError('该转账未被标记为坏账', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 解除坏账标记 await db.execute( 'UPDATE transfers SET is_bad_debt = 0 WHERE id = ?', [transferId] ); // 记录管理员操作日志 await db.execute( `INSERT INTO admin_operation_logs (admin_id, operation_type, target_type, target_id, description, created_at) VALUES (?, 'remove_bad_debt', 'transfer', ?, ?, NOW())`, [adminId, transferId, reason || `管理员解除转账${transferId}的坏账标记`] ); // 记录审计日志 auditLogger.info('Bad debt removed by admin', { transferId, adminId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount, reason }); logger.info('Bad debt removed successfully', {transferId, adminId, reason}); return {success: true}; } catch (error) { logger.error('Failed to remove bad debt', { error: error.message, transferId, adminId }); throw error; } } // 确认转账 async confirmTransfer(transferId, note, operatorId) { const mysql = require('mysql2/promise'); const {dbConfig} = require('../database'); // 创建单独的连接用于事务处理 const connection = await mysql.createConnection(dbConfig); try { // 获取转账记录 const transfer = await this.getTransferById(transferId); if (!transfer) { throw new AppError('转账记录不存在', HTTP_STATUS.NOT_FOUND, ERROR_CODES.NOT_FOUND); } if (transfer.status !== TRANSFER_STATUS.PENDING) { throw new AppError('转账记录状态不允许确认', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 检查是否为坏账 if (transfer.is_bad_debt) { throw new AppError('该转账已被标记为坏账,无法确认。请联系管理员处理', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 开始事务 await connection.beginTransaction(); try { // 更新转账状态 await connection.execute( 'UPDATE transfers SET status = ? WHERE id = ?', [TRANSFER_STATUS.CONFIRMED, transferId] ); // 如果存在匹配订单ID,更新匹配订单状态 if (transfer.matching_order_id) { // 查询该匹配订单下所有transfers的状态 const [allTransfers] = await connection.execute( `SELECT status FROM transfers WHERE matching_order_id = ?`, [transfer.matching_order_id] ); let matchingOrderStatus; // 根据所有相关transfers的状态来决定matching_order的状态 const transferStatuses = allTransfers.map(t => t.status); if (transferStatuses.every(status => status === 'cancelled' || status === 'rejected' || status === 'not_received')) { // 如果所有transfers都被取消/拒绝/未收到,匹配订单标记为已完成 matchingOrderStatus = 'completed'; } else if (transferStatuses.every(status => status === 'received')) { // 如果所有transfers都已收到,匹配订单完成 matchingOrderStatus = 'completed'; } else if (transferStatuses.includes('cancelled') || transferStatuses.includes('rejected') || transferStatuses.includes('not_received') || transferStatuses.some(status => status === 'confirmed' || status === 'received')) { // 如果有任何一个transfer被取消/拒绝/未收到,或者有transfers已确认或已收到,匹配订单为进行中状态 matchingOrderStatus = 'matching'; } else { // 其他情况为待处理状态 matchingOrderStatus = 'matching'; } await connection.execute( `UPDATE matching_orders SET status = ?, updated_at = NOW() WHERE id = ?`, [matchingOrderStatus, transfer.matching_order_id] ); logger.info('Matching order status updated after transfer confirmation', { matchingOrderId: transfer.matching_order_id, transferId: transferId, newMatchingOrderStatus: matchingOrderStatus, allTransferStatuses: transferStatuses }); } // 注意:发送方余额将在接收方确认收款时扣除,而不是在确认转账时扣除 // 这样可以避免资金被锁定但收款方未确认的情况 await connection.commit(); // 记录审计日志 auditLogger.info('Transfer confirmed', { transferId, operatorId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount }); logger.info('Transfer confirmed successfully', {transferId, operatorId}); return {success: true}; } catch (error) { await connection.rollback(); throw error; } } catch (error) { logger.error('Failed to confirm transfer', { error: error.message, transferId, operatorId }); throw error; } finally { await connection.end(); } } // 获取转账列表 async getTransfers(filters = {}, pagination = {}) { const db = getDB(); const {page = 1, limit = 10, sort = 'created_at', order = 'desc'} = pagination; const pageNum = parseInt(page, 10) || 1; const limitNum = parseInt(limit, 10) || 10; const offset = (pageNum - 1) * limitNum; let whereClause = 'WHERE 1=1'; const params = []; // 构建查询条件 if (filters.user_id) { whereClause += ' AND (from_user_id = ? OR to_user_id = ?)'; params.push(filters.user_id, filters.user_id); } if (filters.status) { whereClause += ' AND status = ?'; params.push(filters.status); } if (filters.transfer_type) { whereClause += ' AND transfer_type = ?'; params.push(filters.transfer_type); } if (filters.start_date) { whereClause += ' AND created_at >= ?'; params.push(filters.start_date); } if (filters.end_date) { whereClause += ' AND created_at <= ?'; params.push(filters.end_date); } if (filters.search) { whereClause += ' AND (fu.username LIKE ? OR fu.real_name LIKE ? OR tu.username LIKE ? OR tu.real_name LIKE ?)'; const searchPattern = `%${filters.search}%`; params.push(searchPattern, searchPattern, searchPattern, searchPattern); } // 构建排序子句 const validSortFields = ['id', 'amount', 'created_at', 'updated_at', 'status']; const sortField = validSortFields.includes(sort) ? sort : 'created_at'; const sortOrder = order && order.toLowerCase() === 'asc' ? 'ASC' : 'DESC'; const orderClause = `ORDER BY t.${sortField} ${sortOrder}`; try { // 获取总数 const [countResult] = await db.execute( `SELECT COUNT(*) as total FROM transfers t LEFT JOIN users fu ON t.from_user_id = fu.id LEFT JOIN users tu ON t.to_user_id = tu.id ${whereClause}`, params ); const total = countResult[0].total; // 获取数据 const [transfers] = await db.execute( `SELECT t.*, fu.username as from_username, fu.real_name as from_real_name, tu.username as to_username, tu.real_name as to_real_name FROM transfers t LEFT JOIN users fu ON t.from_user_id = fu.id LEFT JOIN users tu ON t.to_user_id = tu.id ${whereClause} ${orderClause} LIMIT ${limitNum} OFFSET ${offset}`, params ); return { transfers, pagination: { page: pageNum, limit: limitNum, total, pages: Math.ceil(total / limitNum) } }; } catch (error) { logger.error('Failed to get transfers', {error: error.message, filters}); throw error; } } // 验证用户是否存在 async validateUser(userId) { const db = getDB(); const [users] = await db.execute('SELECT id FROM users WHERE id = ?', [userId]); if (users.length === 0) { throw new AppError('用户不存在', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } } // 检查用户余额(现在检查balance字段,允许负数) async checkUserBalance(userId, amount) { const db = getDB(); const [users] = await db.execute('SELECT balance FROM users WHERE id = ?', [userId]); if (users.length === 0) { throw new AppError('用户不存在', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 余额可以为负数,所以不需要检查余额不足 return users[0].balance; } // 获取转账记录 async getTransferById(transferId) { const db = getDB(); const [transfers] = await db.execute('SELECT * FROM transfers WHERE id = ?', [transferId]); return transfers[0] || null; } // 更新用户余额(新的余额系统) async updateUserBalance(transfer) { const {from_user_id, to_user_id, amount, transfer_type} = transfer; const db = getDB(); // 扣除发送方余额(使用行锁防止并发问题) if (transfer_type === TRANSFER_TYPES.USER_TO_USER || transfer_type === TRANSFER_TYPES.USER_TO_SYSTEM) { if (from_user_id) { await db.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [amount, from_user_id] ); } } // 增加接收方余额 if (transfer_type === TRANSFER_TYPES.SYSTEM_TO_USER || transfer_type === TRANSFER_TYPES.USER_TO_USER) { await db.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [amount, to_user_id] ); } } // 使用指定连接更新用户余额(用于事务处理) async updateUserBalanceWithConnection(transfer, connection) { const {from_user_id, to_user_id, amount, transfer_type} = transfer; // 扣除发送方余额(使用行锁防止并发问题) if (transfer_type === TRANSFER_TYPES.USER_TO_USER || transfer_type === TRANSFER_TYPES.USER_TO_SYSTEM) { if (from_user_id) { await connection.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [amount, from_user_id] ); } } // 增加接收方余额 if (transfer_type === TRANSFER_TYPES.SYSTEM_TO_USER || transfer_type === TRANSFER_TYPES.USER_TO_USER) { await connection.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [amount, to_user_id] ); } } // 用户确认收到转账 async confirmReceived(transferId, userId) { const mysql = require('mysql2/promise'); const {dbConfig} = require('../database'); // 创建单独的连接用于事务处理 const connection = await mysql.createConnection(dbConfig); try { // 获取转账记录 const transfer = await this.getTransferById(transferId); if (!transfer) { throw new AppError('转账记录不存在', HTTP_STATUS.NOT_FOUND, ERROR_CODES.NOT_FOUND); } // 检查用户权限:必须是收款方本人或管理员 const [userRows] = await connection.execute( 'SELECT role FROM users WHERE id = ?', [userId] ); const isAdmin = userRows[0]?.role === 'admin'; const isRecipient = transfer.to_user_id === userId; if (!isRecipient && !isAdmin) { throw new AppError('无权限操作此转账', HTTP_STATUS.FORBIDDEN, ERROR_CODES.VALIDATION_ERROR); } if (transfer.status !== TRANSFER_STATUS.CONFIRMED) { throw new AppError('转账状态不允许确认收款', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 检查是否为坏账 if (transfer.is_bad_debt) { throw new AppError('该转账已被标记为坏账,无法确认收款。请联系管理员处理', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 开始事务 await connection.beginTransaction(); try { // 更新转账状态为已收到 await connection.execute( 'UPDATE transfers SET status = ? WHERE id = ?', [TRANSFER_STATUS.RECEIVED, transferId] ); // 扣除发送方余额(在接收方确认收款时扣除) if (transfer.from_user_id) { await connection.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [transfer.amount, transfer.from_user_id] ); } // 所有类型的转账都需要在接收方确认收到时增加接收方余额 // 这与 confirmTransfer 方法的修改保持一致 await connection.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [transfer.amount, transfer.to_user_id] ); // 给发起人发放相应的积分(转账金额 = 积分数量) await connection.execute( 'UPDATE users SET points = points + ? WHERE id = ?', [transfer.amount, transfer.from_user_id] ); // 记录积分历史 await connection.execute( `INSERT INTO points_history (user_id, amount, type, description, created_at) VALUES (?, ?, 'earn', ?, NOW())`, [transfer.from_user_id, transfer.amount, `转账确认收款奖励积分,转账ID: ${transferId}`] ); // 记录详细的余额变更审计日志 auditLogger.info('Balance adjustment - confirm received', { transferId: transferId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount, operation: 'add_receiver_balance', operatorId: userId, operatorType: isAdmin ? 'admin' : 'user', timestamp: new Date().toISOString() }); await connection.commit(); // 记录审计日志 auditLogger.info('Transfer received confirmed', { transferId, userId, amount: transfer.amount, pointsAwarded: transfer.amount, pointsAwardedTo: transfer.from_user_id }); logger.info('Transfer received confirmed successfully', { transferId, userId, pointsAwarded: transfer.amount, pointsAwardedTo: transfer.from_user_id }); // 检查并处理代理佣金(转账完成后) try { const matchingService = require('./matchingService'); await matchingService.checkAndProcessAgentCommission(transfer.from_user_id); logger.info('Agent commission check completed', { transferId, fromUserId: transfer.from_user_id }); } catch (commissionError) { // 代理佣金处理失败不影响主流程 logger.error('Agent commission processing failed', { transferId, fromUserId: transfer.from_user_id, error: commissionError.message }); } return {success: true}; } catch (error) { await connection.rollback(); throw error; } } catch (error) { logger.error('Failed to confirm received transfer', { error: error.message, transferId, userId }); throw error; } finally { await connection.end(); } } /** * 用户确认未收到转账 * 当用户确认未收到款项时,将转账状态改为not_received并回滚发送方余额 * @param {number} transferId - 转账ID * @param {number} userId - 操作用户ID * @returns {Object} 操作结果 */ async confirmNotReceived(transferId, userId) { const db = getDB(); const connection = await db.getConnection(); try { // 获取转账记录 const transfer = await this.getTransferById(transferId); if (!transfer) { throw new AppError('转账记录不存在', HTTP_STATUS.NOT_FOUND, ERROR_CODES.NOT_FOUND); } // 检查用户权限:必须是收款方本人或管理员 const [userRows] = await db.execute( 'SELECT role FROM users WHERE id = ?', [userId] ); const isAdmin = userRows[0]?.role === 'admin'; const isRecipient = transfer.to_user_id === userId; if (!isRecipient && !isAdmin) { throw new AppError('无权限操作此转账', HTTP_STATUS.FORBIDDEN, ERROR_CODES.VALIDATION_ERROR); } if (transfer.status !== TRANSFER_STATUS.CONFIRMED) { throw new AppError('转账状态不允许确认未收款', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 开始事务 await connection.beginTransaction(); try { // 更新转账状态为未收到 await connection.execute( 'UPDATE transfers SET status = ? WHERE id = ?', [TRANSFER_STATUS.NOT_RECEIVED, transferId] ); // 注意:在新逻辑下,CONFIRMED状态时发送方余额还没有被扣除,所以无需回滚 logger.info('Transfer marked as not received - no balance adjustment needed', { transferId, userId: transfer.from_user_id, amount: transfer.amount, operatorId: userId, note: 'Sender balance was not deducted in confirmed status under new logic' }); await connection.commit(); // 记录审计日志 auditLogger.info('Transfer not received confirmed', { transferId, userId, amount: transfer.amount, fromUserId: transfer.from_user_id, balanceRestored: false, note: 'No balance restoration needed under new logic' }); logger.info('Transfer not received confirmed successfully', { transferId, userId, balanceRestored: false, note: 'No balance restoration needed under new logic' }); return {success: true}; } catch (error) { await connection.rollback(); throw error; } finally { connection.release(); } } catch (error) { logger.error('Failed to confirm not received transfer', { error: error.message, transferId, userId }); throw error; } } // 拒绝转账 async rejectTransfer(transferId, note, operatorId) { const db = getDB(); let connection; try { // 从连接池获取连接用于事务处理 connection = await db.getConnection(); try { // 获取转账记录 const transfer = await this.getTransferById(transferId); if (!transfer) { throw new AppError('转账记录不存在', HTTP_STATUS.NOT_FOUND, ERROR_CODES.NOT_FOUND); } if (transfer.status !== TRANSFER_STATUS.PENDING) { throw new AppError('转账记录状态不允许拒绝', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 检查是否已超时 if (transfer.is_overdue) { throw new AppError('已超时的转账不能拒绝,异常状态只能后台解除', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 检查是否有截止时间且已过期 if (transfer.deadline_at) { const deadline = new Date(transfer.deadline_at); const now = new Date(); if (now > deadline) { throw new AppError('已超时的转账不能拒绝,异常状态只能后台解除', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } } // 开始事务 await connection.beginTransaction(); try { // 更新转账状态 await connection.execute( 'UPDATE transfers SET status = ? WHERE id = ?', [TRANSFER_STATUS.REJECTED, transferId] ); // 注意:在新逻辑下,CONFIRMED状态时发送方余额还没有被扣除,所以无需回滚 // 只有在RECEIVED状态时才需要回滚余额,但RECEIVED状态的转账不应该被拒绝 logger.info('Transfer rejected - no balance adjustment needed', { transferId, userId: transfer.from_user_id, amount: transfer.amount, message: 'Sender balance was not deducted in confirmed status under new logic' }); // 如果是分配类型的转账,需要更新对应的matching_order状态 if ( transfer.matching_order_id) { // 查询该matching_order下所有source_type为allocation的transfers状态 const [allTransfers] = await connection.execute( `SELECT status FROM transfers WHERE matching_order_id = ? AND source_type = 'allocation'`, [transfer.matching_order_id] ); // 统计各种状态的数量 const statusCounts = { cancelled: 0, rejected: 0, not_received: 0, confirmed: 0, received: 0, pending: 0 }; allTransfers.forEach(t => { if (statusCounts.hasOwnProperty(t.status)) { statusCounts[t.status]++; } }); const totalTransfers = allTransfers.length; const problemTransfers = statusCounts.cancelled + statusCounts.rejected + statusCounts.not_received; const completedTransfers = statusCounts.received; const activeTransfers = statusCounts.confirmed + statusCounts.received; let matchingOrderStatus; if (problemTransfers === totalTransfers) { // 所有transfers都是问题状态,matching_order为已完成 matchingOrderStatus = 'completed'; } else if (completedTransfers === totalTransfers) { // 所有transfers都已收到,matching_order为已完成 matchingOrderStatus = 'completed'; } else if (problemTransfers > 0 || activeTransfers > 0) { // 有问题transfers或有活跃transfers,matching_order为进行中 matchingOrderStatus = 'matching'; } else { // 其他情况为等待中 matchingOrderStatus = 'pending'; } // 更新matching_order状态 await connection.execute( `UPDATE matching_orders SET status = ?, updated_at = NOW() WHERE id = ?`, [matchingOrderStatus, transfer.matching_order_id] ); logger.info('Updated matching_order status after transfer rejection', { matchingOrderId: transfer.matching_order_id, newStatus: matchingOrderStatus, transferId, statusCounts }); } await connection.commit(); // 记录审计日志 auditLogger.info('Transfer rejected', { transferId, operatorId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount, note, balanceRestored: false, // 在新逻辑下无需回滚余额 balanceRestoredNote: 'No balance restoration needed under new logic' }); logger.info('Transfer rejected successfully', {transferId, operatorId}); return {success: true}; } catch (error) { await connection.rollback(); throw error; } } catch (error) { logger.error('Failed to reject transfer', { error: error.message, transferId, operatorId }); throw error; } finally { if (connection) { connection.release(); // 释放连接回连接池 } } } catch (error) { logger.error('Failed to get database connection for reject transfer', { error: error.message, transferId, operatorId }); throw error; } } /** * 强制变更转账状态(管理员权限) * 用于处理货款纠纷等异常情况 * @param {number} transferId - 转账ID * @param {string} newStatus - 新状态 * @param {string} reason - 变更原因 * @param {number} adminId - 管理员ID * @param {boolean} adjust_balance - 是否调整余额 */ async forceChangeTransferStatus(transferId, newStatus, reason, adminId, adjust_balance = false) { const db = getDB(); let connection; try { // 从连接池获取连接用于事务处理 connection = await db.getConnection(); try { // 获取转账记录 const transfer = await this.getTransferById(transferId); if (!transfer) { throw new AppError('转账记录不存在', HTTP_STATUS.NOT_FOUND, ERROR_CODES.NOT_FOUND); } const oldStatus = transfer.status; // 验证新状态 const validStatuses = [ TRANSFER_STATUS.PENDING, TRANSFER_STATUS.CONFIRMED, TRANSFER_STATUS.RECEIVED, TRANSFER_STATUS.REJECTED, TRANSFER_STATUS.CANCELLED, TRANSFER_STATUS.NOT_RECEIVED ]; if (!validStatuses.includes(newStatus)) { throw new AppError('无效的转账状态', HTTP_STATUS.BAD_REQUEST, ERROR_CODES.VALIDATION_ERROR); } // 开始事务 await connection.beginTransaction(); try { // 更新转账状态 await connection.execute( `UPDATE transfers SET status = ?, admin_note = ?, admin_modified_at = NOW(), admin_modified_by = ? WHERE id = ?`, [newStatus, reason, adminId, transferId] ); // 同步更新matching_orders表的状态 if (transfer.matching_order_id) { // 查询该匹配订单下所有transfers的状态 const [allTransfers] = await connection.execute( `SELECT status FROM transfers WHERE matching_order_id = ?`, [transfer.matching_order_id] ); let matchingOrderStatus; // 根据所有相关transfers的状态来决定matching_order的状态 const transferStatuses = allTransfers.map(t => t.status); if (transferStatuses.every(status => status === 'cancelled' || status === 'rejected' || status === 'not_received')) { // 如果所有transfers都被取消/拒绝/未收到,匹配订单标记为已完成 matchingOrderStatus = 'completed'; } else if (transferStatuses.every(status => status === 'received')) { // 如果所有transfers都已收到,匹配订单完成 matchingOrderStatus = 'completed'; } else if (transferStatuses.includes('cancelled') || transferStatuses.includes('rejected') || transferStatuses.includes('not_received') || transferStatuses.some(status => status === 'confirmed' || status === 'received')) { // 如果有任何一个transfer被取消/拒绝/未收到,或者有transfers已确认或已收到,匹配订单为进行中状态 matchingOrderStatus = 'matching'; } else { // 其他情况为待处理状态 matchingOrderStatus = 'pending'; } console.log('matchingOrderStatus', matchingOrderStatus); await connection.execute( `UPDATE matching_orders SET status = ?, updated_at = NOW() WHERE id = ?`, [matchingOrderStatus, transfer.matching_order_id] ); logger.info('Matching order status updated based on all transfers', { matchingOrderId: transfer.matching_order_id, transferId: transferId, oldTransferStatus: oldStatus, newTransferStatus: newStatus, allTransferStatuses: transferStatuses, newMatchingOrderStatus: matchingOrderStatus, adminId }); } // 根据状态变更调整余额 if (adjust_balance && transfer.from_user_id) { if (oldStatus === TRANSFER_STATUS.CONFIRMED && (newStatus === TRANSFER_STATUS.REJECTED || newStatus === TRANSFER_STATUS.CANCELLED || newStatus === TRANSFER_STATUS.NOT_RECEIVED)) { // 从已确认变为拒绝/取消/未收到:由于新逻辑下CONFIRMED状态时发送方余额未扣除,所以无需回滚 logger.info('Status change from confirmed to rejected/cancelled/not_received - no balance adjustment needed', { transferId, userId: transfer.from_user_id, amount: transfer.amount, oldStatus, newStatus, note: 'Sender balance was not deducted in confirmed status under new logic' }); // 记录详细的余额变更审计日志 auditLogger.info('Balance adjustment - status change (no action needed)', { transferId: transferId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount, operation: 'no_action_needed', oldStatus: oldStatus, newStatus: newStatus, adminId: adminId, reason: reason, note: 'Sender balance was not deducted in confirmed status under new logic', timestamp: new Date().toISOString() }); } else if ((oldStatus === TRANSFER_STATUS.PENDING || oldStatus === TRANSFER_STATUS.REJECTED || oldStatus === TRANSFER_STATUS.CANCELLED || oldStatus === TRANSFER_STATUS.NOT_RECEIVED) && newStatus === TRANSFER_STATUS.CONFIRMED) { // 从待处理/拒绝/取消/未收到变为确认:新逻辑下CONFIRMED状态不扣除发送方余额 logger.info('Status change to confirmed - no balance deduction needed', { transferId, userId: transfer.from_user_id, amount: transfer.amount, oldStatus, newStatus, note: 'Sender balance will be deducted when receiver confirms receipt' }); } else if ((oldStatus === TRANSFER_STATUS.PENDING || oldStatus === TRANSFER_STATUS.REJECTED || oldStatus === TRANSFER_STATUS.CANCELLED || oldStatus === TRANSFER_STATUS.NOT_RECEIVED) && newStatus === TRANSFER_STATUS.RECEIVED) { // 从待处理/拒绝/取消/未收到变为已收到:扣除发送方余额和积分 await connection.execute( 'UPDATE users SET balance = balance - ?, points = points + ? WHERE id = ?', [transfer.amount, transfer.amount, transfer.from_user_id] ); logger.info('Balance and points deducted due to status change to received', { transferId, userId: transfer.from_user_id, amount: transfer.amount, oldStatus, newStatus }); } else if (oldStatus === TRANSFER_STATUS.RECEIVED && (newStatus === TRANSFER_STATUS.REJECTED || newStatus === TRANSFER_STATUS.CANCELLED || newStatus === TRANSFER_STATUS.NOT_RECEIVED)) { // 从已收到变为拒绝/取消/未收到:需要从接收方扣除余额和积分,并回滚发送方余额和积分 if (transfer.to_user_id) { await connection.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [transfer.amount, transfer.to_user_id] ); logger.info('Receiver balance and points deducted due to status change', { transferId, userId: transfer.to_user_id, amount: transfer.amount, oldStatus, newStatus }); } await connection.execute( 'UPDATE users SET balance = balance + ?, points = points - ? WHERE id = ?', [transfer.amount, transfer.amount, transfer.from_user_id] ); logger.info('Sender balance and points restored due to status change', { transferId, userId: transfer.from_user_id, amount: transfer.amount, oldStatus, newStatus }); // 记录详细的余额变更审计日志 auditLogger.info('Balance adjustment - status change deduction', { transferId: transferId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount, operation: 'deduct_sender_balance', oldStatus: oldStatus, newStatus: newStatus, adminId: adminId, reason: reason, timestamp: new Date().toISOString() }); } else if (oldStatus === TRANSFER_STATUS.RECEIVED && newStatus === TRANSFER_STATUS.CONFIRMED) { // 从已收到变为已确认:需要从接收方扣除余额和积分(因为confirmed状态下接收方不应该有余额) if (transfer.to_user_id) { await connection.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [transfer.amount, transfer.to_user_id] ); logger.info('Receiver balance and points deducted due to status change from received to confirmed', { transferId, userId: transfer.to_user_id, amount: transfer.amount, oldStatus, newStatus }); } } else if (oldStatus === TRANSFER_STATUS.CONFIRMED && newStatus === TRANSFER_STATUS.RECEIVED) { // 从已确认变为已收到:新逻辑下需要扣除发送方余额和积分(因为CONFIRMED状态下未扣除) await connection.execute( 'UPDATE users SET balance = balance - ?, points = points + ? WHERE id = ?', [transfer.amount, transfer.amount, transfer.from_user_id] ); logger.info('Status change from confirmed to received - sender balance and points deducted', { transferId, userId: transfer.from_user_id, amount: transfer.amount, oldStatus, newStatus, note: 'Sender balance and points deducted as per new logic' }); } } // 如果变更为received状态,需要增加接收方余额和积分 if (adjust_balance && newStatus === TRANSFER_STATUS.RECEIVED && oldStatus !== TRANSFER_STATUS.RECEIVED && transfer.to_user_id) { await connection.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [transfer.amount, transfer.to_user_id] ); logger.info('Receiver balance and points increased due to status change', { transferId, userId: transfer.to_user_id, amount: transfer.amount, oldStatus, newStatus }); // 记录详细的余额变更审计日志 auditLogger.info('Balance adjustment - receiver balance and points increase', { transferId: transferId, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount, operation: 'add_receiver_balance_and_points', oldStatus: oldStatus, newStatus: newStatus, adminId: adminId, reason: reason, timestamp: new Date().toISOString() }); } await connection.commit(); // 记录审计日志 auditLogger.info('Transfer status force changed by admin', { transferId, adminId, oldStatus, newStatus, reason, adjust_balance, fromUserId: transfer.from_user_id, toUserId: transfer.to_user_id, amount: transfer.amount }); logger.info('Transfer status force changed successfully', { transferId, adminId, oldStatus, newStatus }); return {success: true, oldStatus, newStatus}; } catch (error) { await connection.rollback(); throw error; } } catch (error) { logger.error('Failed to force change transfer status', { error: error.message, transferId, adminId, newStatus }); throw error; } finally { if (connection) { connection.release(); // 释放连接回连接池 } } } catch (error) { logger.error('Failed to get database connection for force change transfer status', { error: error.message, transferId, adminId, newStatus }); throw error; } } // 生成批次ID generateBatchId() { return `T${Date.now()}${Math.random().toString(36).substr(2, 9)}`; } } module.exports = new TransferService();