| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  | 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( | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                         `SELECT status
 | 
					
						
							|  |  |  |  |                          FROM transfers | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                          WHERE matching_order_id = ?`,
 | 
					
						
							|  |  |  |  |                         [transfer.matching_order_id] | 
					
						
							|  |  |  |  |                     ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                     let matchingOrderStatus; | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                     // 根据所有相关transfers的状态来决定matching_order的状态
 | 
					
						
							|  |  |  |  |                     const transferStatuses = allTransfers.map(t => t.status); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                     console.log(transferStatuses, 'transferStatuses'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                     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'; | 
					
						
							|  |  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                     await connection.execute( | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                         `UPDATE matching_orders
 | 
					
						
							|  |  |  |  |                          SET status     = ?, | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                              updated_at = NOW() | 
					
						
							|  |  |  |  |                          WHERE id = ?`,
 | 
					
						
							|  |  |  |  |                         [matchingOrderStatus, transfer.matching_order_id] | 
					
						
							|  |  |  |  |                     ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                     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(); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 获取转账列表
 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |     async getTransfers(filters = {}, pagination = {}, user_type = 'user_to_user') { | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |         const db = getDB(); | 
					
						
							| 
									
										
										
										
											2025-09-26 14:40:02 +08:00
										 |  |  |  |         const {page = 1, limit = 10, sort = 'id', order = 'desc'} = pagination; | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |         const pageNum = parseInt(page, 10) || 1; | 
					
						
							|  |  |  |  |         const limitNum = parseInt(limit, 10) || 10; | 
					
						
							|  |  |  |  |         const offset = (pageNum - 1) * limitNum; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |         let whereClause = 'WHERE  '; | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |         const params = []; | 
					
						
							| 
									
										
										
										
											2025-09-17 14:00:46 +08:00
										 |  |  |  |         whereClause += `t.transfer_type='${user_type}'`; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |         // 构建查询条件
 | 
					
						
							|  |  |  |  |         if (filters.user_id) { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:00:46 +08:00
										 |  |  |  |             whereClause += ' AND (t.from_user_id = ? OR t.to_user_id = ?)'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |             params.push(filters.user_id, filters.user_id); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         if (filters.status) { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:00:46 +08:00
										 |  |  |  |             whereClause += ' AND t.status = ?'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |             params.push(filters.status); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         if (filters.transfer_type) { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:00:46 +08:00
										 |  |  |  |             whereClause += ' AND t.transfer_type = ?'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |             params.push(filters.transfer_type); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         if (filters.start_date) { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:00:46 +08:00
										 |  |  |  |             whereClause += ' AND t.created_at >= ?'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |             params.push(filters.start_date); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         if (filters.end_date) { | 
					
						
							| 
									
										
										
										
											2025-09-17 14:00:46 +08:00
										 |  |  |  |             whereClause += ' AND t.created_at <= ?'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |             params.push(filters.end_date); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |         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']; | 
					
						
							| 
									
										
										
										
											2025-09-26 14:40:02 +08:00
										 |  |  |  |         const sortField = validSortFields.includes(sort) ? sort : 'id'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |         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, | 
					
						
							| 
									
										
										
										
											2025-09-26 14:40:02 +08:00
										 |  |  |  |                         fu.balance as from_balance, | 
					
						
							|  |  |  |  |                         tu.balance as to_balance, | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |                         tu.username  as to_username, | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                         tu.real_name as to_real_name, | 
					
						
							|  |  |  |  |                         f_p.name as from_province, | 
					
						
							|  |  |  |  |                         f_c.name as from_city, | 
					
						
							|  |  |  |  |                         f_d.name as from_district, | 
					
						
							|  |  |  |  |                         t_p.name as to_province, | 
					
						
							|  |  |  |  |                         t_c.name as to_city, | 
					
						
							|  |  |  |  |                         t_d.name as to_district | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |                  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 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                           LEFT JOIN china_regions f_p ON f_p.code = fu.province | 
					
						
							|  |  |  |  |                           LEFT JOIN china_regions f_c ON f_c.code = fu.city | 
					
						
							|  |  |  |  |                           LEFT JOIN china_regions f_d ON f_d.code = fu.district_id | 
					
						
							|  |  |  |  |                           LEFT JOIN china_regions t_p ON t_p.code = tu.province | 
					
						
							|  |  |  |  |                           LEFT JOIN china_regions t_c ON t_c.code = tu.city | 
					
						
							|  |  |  |  |                           LEFT JOIN china_regions t_d ON t_d.code = tu.district_id | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |                      ${whereClause} ${orderClause} | 
					
						
							| 
									
										
										
										
											2025-09-26 14:40:02 +08:00
										 |  |  |  |                  LIMIT ${limitNum} OFFSET ${offset}`,
 | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |                 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; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-16 17:39:51 +08:00
										 |  |  |  |     async getTransfersHistory(filters = {}, pagination = {}, user_type = 'manual') { | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  |         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 = []; | 
					
						
							| 
									
										
										
										
											2025-09-16 17:39:51 +08:00
										 |  |  |  |         whereClause += `AND source_type != '${user_type}'`; | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |         // 构建查询条件
 | 
					
						
							|  |  |  |  |         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'; | 
					
						
							| 
									
										
										
										
											2025-09-10 18:10:40 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |         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} | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                  LIMIT ${limitNum} OFFSET ${offset}`,
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                 params | 
					
						
							|  |  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |             //获取总数
 | 
					
						
							|  |  |  |  |             const  stats = {}; | 
					
						
							|  |  |  |  |             //获取系统转给融豆的总数
 | 
					
						
							|  |  |  |  |             let [total_to_admin] = await db.execute(`SELECT SUM(t.amount) as total FROM transfers t WHERE t.source_type = 'system'`) | 
					
						
							|  |  |  |  |             stats.total_to_admin = total_to_admin[0].total || 0 | 
					
						
							|  |  |  |  |             //转给代理的融豆总数
 | 
					
						
							|  |  |  |  |             let [total_to_agent] = await db.execute(`SELECT SUM(t.amount) as total FROM transfers t WHERE t.source_type = 'agent'`) | 
					
						
							|  |  |  |  |             stats.total_to_agent = total_to_agent[0].total || 0 | 
					
						
							|  |  |  |  |             //转给直营代理的融豆数量
 | 
					
						
							|  |  |  |  |             let [total_to_agent_directly] = await db.execute(`SELECT SUM(t.amount) as total FROM transfers t WHERE t.source_type = 'operated_agent'`) | 
					
						
							|  |  |  |  |             stats.total_to_agent_directly = total_to_agent_directly[0].total || 0 | 
					
						
							|  |  |  |  |             //转给直营的融豆总数
 | 
					
						
							|  |  |  |  |             let [total_to_directly_operated] = await db.execute(`SELECT SUM(t.amount) as total FROM transfers t WHERE t.source_type = 'directly_operated'`) | 
					
						
							|  |  |  |  |             stats.total_to_directly_operated = total_to_directly_operated[0].total || 0 | 
					
						
							|  |  |  |  |             //提现总数
 | 
					
						
							|  |  |  |  |             let [total_get] = await db.execute(`SELECT SUM(t.amount) as total FROM transfers t WHERE t.source_type = 'withdraw'`) | 
					
						
							|  |  |  |  |             stats.total_get = total_get[0].total || 0 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |             return { | 
					
						
							|  |  |  |  |                 transfers, | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                 stats, | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                 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 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 = ?', | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                         [TRANSFER_STATUS.REJECTED, transferId] | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                     ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                     // 注意:在新逻辑下,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状态
 | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                     if (transfer.matching_order_id) { | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         // 查询该matching_order下所有source_type为allocation的transfers状态
 | 
					
						
							|  |  |  |  |                         const [allTransfers] = await connection.execute( | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                             `SELECT status
 | 
					
						
							|  |  |  |  |                              FROM transfers | 
					
						
							|  |  |  |  |                              WHERE matching_order_id = ? | 
					
						
							|  |  |  |  |                                AND source_type = 'allocation'`,
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                             [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( | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                             `UPDATE matching_orders
 | 
					
						
							|  |  |  |  |                              SET status = ?, | 
					
						
							|  |  |  |  |                                  updated_at = NOW() | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                              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( | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                             `SELECT status
 | 
					
						
							|  |  |  |  |                              FROM transfers | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                              WHERE matching_order_id = ?`,
 | 
					
						
							|  |  |  |  |                             [transfer.matching_order_id] | 
					
						
							|  |  |  |  |                         ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         let matchingOrderStatus; | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         // 根据所有相关transfers的状态来决定matching_order的状态
 | 
					
						
							|  |  |  |  |                         const transferStatuses = allTransfers.map(t => t.status); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                         console.log(transferStatuses, 'transferStatuses'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 16:48:53 +08:00
										 |  |  |  |                         if (transferStatuses.every(status => status === 'cancelled' || status === 'rejected' || status === 'not_received' || status === 'confirmed' || status === 'received')) { | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                             // 如果所有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')) { | 
					
						
							|  |  |  |  |                             // 如果有任何一个transfer被取消/拒绝/未收到,或者有transfers已确认或已收到,匹配订单为进行中状态
 | 
					
						
							|  |  |  |  |                             matchingOrderStatus = 'matching'; | 
					
						
							|  |  |  |  |                         } else { | 
					
						
							|  |  |  |  |                             // 其他情况为待处理状态
 | 
					
						
							|  |  |  |  |                             matchingOrderStatus = 'pending'; | 
					
						
							|  |  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         console.log('matchingOrderStatus', matchingOrderStatus); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         await connection.execute( | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                             `UPDATE matching_orders
 | 
					
						
							|  |  |  |  |                              SET status     = ?, | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                                  updated_at = NOW() | 
					
						
							|  |  |  |  |                              WHERE id = ?`,
 | 
					
						
							|  |  |  |  |                             [matchingOrderStatus, transfer.matching_order_id] | 
					
						
							|  |  |  |  |                         ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         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 = ?', | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                                     [transfer.amount, transfer.to_user_id] | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                                 ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                                 logger.info('Receiver balance and points deducted due to status change', { | 
					
						
							|  |  |  |  |                                     transferId, | 
					
						
							|  |  |  |  |                                     userId: transfer.to_user_id, | 
					
						
							|  |  |  |  |                                     amount: transfer.amount, | 
					
						
							|  |  |  |  |                                     oldStatus, | 
					
						
							|  |  |  |  |                                     newStatus | 
					
						
							|  |  |  |  |                                 }); | 
					
						
							|  |  |  |  |                             } | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                             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] | 
					
						
							|  |  |  |  |                                 ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                                 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] | 
					
						
							|  |  |  |  |                             ); | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                             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 = ?', | 
					
						
							| 
									
										
										
										
											2025-09-15 17:27:13 +08:00
										 |  |  |  |                             [transfer.amount, transfer.to_user_id] | 
					
						
							| 
									
										
										
										
											2025-08-26 10:06:23 +08:00
										 |  |  |  |                         ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                         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(); |