fix(wallet): 统一使用 accountSequence 查询钱包,修复转账余额不足问题

背景:幽灵钱包 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 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-15 23:00:40 -08:00
parent 974d660544
commit 88368d1705
2 changed files with 18 additions and 10 deletions

View File

@ -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,

View File

@ -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(