From 88368d1705f2b2e52960c3aab1dfe3f75129e8a1 Mon Sep 17 00:00:00 2001 From: hailin Date: Thu, 15 Jan 2026 23:00:40 -0800 Subject: [PATCH] =?UTF-8?q?fix(wallet):=20=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20accountSequence=20=E6=9F=A5=E8=AF=A2=E9=92=B1?= =?UTF-8?q?=E5=8C=85=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=BD=AC=E8=B4=A6=E4=BD=99?= =?UTF-8?q?=E9=A2=9D=E4=B8=8D=E8=B6=B3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景:幽灵钱包 D26010800000 (user_id=133, 余额=0) 导致真实用户 D26010900000 (user_id=0, 余额=200465) 转账失败 原因: - D26010800000 是 2026-01-08 16:23 通过未知方式创建的脏数据 - 真实用户 D26010900000 在 18:40 注册时,user_id=133 已被占用 - getMyWallet 用 accountSequence 查询显示余额正确 - requestWithdrawal 用 userId 查询找到错误的空钱包 修复: - Controller: 传 user.accountSequence 而非 user.userId - Service: 移除 findByUserId fallback,仅用 findByAccountSequence - 从钱包记录获取 userId 用于订单、流水、事件关联 Co-Authored-By: Claude Opus 4.5 --- .../src/api/controllers/wallet.controller.ts | 8 +++++++- .../services/wallet-application.service.ts | 20 ++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/backend/services/wallet-service/src/api/controllers/wallet.controller.ts b/backend/services/wallet-service/src/api/controllers/wallet.controller.ts index 93e75d79..aacb52bd 100644 --- a/backend/services/wallet-service/src/api/controllers/wallet.controller.ts +++ b/backend/services/wallet-service/src/api/controllers/wallet.controller.ts @@ -117,8 +117,14 @@ export class WalletController { actualAddress = resolvedAddress; } + // [2026-01-16] 使用 accountSequence 而非 userId 查找钱包 + // 背景:发现幽灵钱包 D26010800000 (user_id=133, 余额=0) 导致真实用户 D26010900000 (user_id=0, 余额=200465) 转账失败 + // 原因:D26010800000 是 2026-01-08 16:23 通过未知方式创建的脏数据,占用了 user_id=133 + // 而真实用户 D26010900000 在 2026-01-08 18:40 注册时,因 user_id=133 已被占用,其钱包 user_id 被设为 0 + // 问题:getMyWallet 用 accountSequence 查询显示余额正确,但 requestWithdrawal 用 userId 查询找到错误的空钱包 + // 修复:统一使用 accountSequence 作为钱包查询的唯一标识 const command = new RequestWithdrawalCommand( - user.userId, + user.accountSequence, dto.amount, actualAddress, dto.chainType, diff --git a/backend/services/wallet-service/src/application/services/wallet-application.service.ts b/backend/services/wallet-service/src/application/services/wallet-application.service.ts index 8a34cea4..d5c0c346 100644 --- a/backend/services/wallet-service/src/application/services/wallet-application.service.ts +++ b/backend/services/wallet-service/src/application/services/wallet-application.service.ts @@ -1512,7 +1512,6 @@ export class WalletApplicationService { netAmount: number; status: string; }> { - const userId = BigInt(command.userId); const amount = Money.USDT(command.amount); // 从配置获取动态手续费 @@ -1524,8 +1523,6 @@ export class WalletApplicationService { ? `固定 ${feeValue} 绿积分` : `${(feeValue * 100).toFixed(2)}%`; - this.logger.log(`Processing withdrawal request for user ${userId}: ${command.amount} USDT to ${command.toAddress}, fee: ${feeAmount} (${feeDescription})`); - // 验证最小提现金额 if (command.amount < this.MIN_WITHDRAWAL_AMOUNT) { throw new Error(`最小提现金额为 ${this.MIN_WITHDRAWAL_AMOUNT} USDT`); @@ -1541,15 +1538,20 @@ export class WalletApplicationService { throw new BadRequestException(hotWalletCheck.error || '财务系统审计中,请稍后再试'); } - // 优先按 accountSequence 查找,如果未找到则按 userId 查找 - let wallet = await this.walletRepo.findByAccountSequence(command.userId); + // [2026-01-16] 仅按 accountSequence 查找钱包,不再 fallback 到 userId + // 背景:幽灵钱包 D26010800000 (user_id=133) 与真实用户 D26010900000 (user_id=0) 的 userId 冲突 + // 导致按 userId 查询时找到错误的空钱包,用户转账报"余额不足" + // 修复:command.userId 实际传入的是 accountSequence,直接按 accountSequence 查询 + const wallet = await this.walletRepo.findByAccountSequence(command.userId); if (!wallet) { - wallet = await this.walletRepo.findByUserId(userId); - } - if (!wallet) { - throw new WalletNotFoundError(`userId/accountSequence: ${command.userId}`); + throw new WalletNotFoundError(`accountSequence: ${command.userId}`); } + // 从钱包记录获取 userId(用于订单、流水、事件等关联) + const userId = wallet.userId.value; + + this.logger.log(`Processing withdrawal request for user ${userId} (${command.userId}): ${command.amount} USDT to ${command.toAddress}, fee: ${feeAmount} (${feeDescription})`); + // 验证余额是否足够 if (wallet.balances.usdt.available.lessThan(totalRequired)) { throw new BadRequestException(