diff --git a/backend/services/wallet-service/src/infrastructure/infrastructure.module.ts b/backend/services/wallet-service/src/infrastructure/infrastructure.module.ts index a6e258cf..5f455d77 100644 --- a/backend/services/wallet-service/src/infrastructure/infrastructure.module.ts +++ b/backend/services/wallet-service/src/infrastructure/infrastructure.module.ts @@ -20,8 +20,6 @@ import { import { RedisModule } from './redis'; import { KafkaModule } from './kafka'; import { IdentityModule } from './external/identity'; -// OTP: One-Time fix for D25122600004 -> D25122600006 transfer (remove after fix) -import { TransferFixService } from './otp/transfer-fix.service'; const repositories = [ { @@ -54,7 +52,7 @@ const repositories = [ @Global() @Module({ imports: [RedisModule, KafkaModule, IdentityModule], - providers: [PrismaService, ...repositories, TransferFixService], // OTP: remove TransferFixService after fix + providers: [PrismaService, ...repositories], exports: [PrismaService, RedisModule, KafkaModule, IdentityModule, FeeConfigRepositoryImpl, ...repositories], }) export class InfrastructureModule {} diff --git a/backend/services/wallet-service/src/infrastructure/otp/transfer-fix.service.ts b/backend/services/wallet-service/src/infrastructure/otp/transfer-fix.service.ts deleted file mode 100644 index a687bfb8..00000000 --- a/backend/services/wallet-service/src/infrastructure/otp/transfer-fix.service.ts +++ /dev/null @@ -1,185 +0,0 @@ -/** - * One-Time-Fix: 修复 D25122600004 -> D25122600006 的转账 - * - * 问题:由于并发修改导致冻结余额少了 2 USDT (手续费) - * 解决:修复冻结余额,完成转账,更新订单状态 - * - * 幂等性:检查订单状态,已 CONFIRMED 则跳过 - * 部署后删除此文件和 infrastructure.module.ts 中的引用 - */ - -import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; -import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service'; -import { LedgerEntryType, WithdrawalStatus } from '@/domain/value-objects'; -import Decimal from 'decimal.js'; - -@Injectable() -export class TransferFixService implements OnModuleInit { - private readonly logger = new Logger(TransferFixService.name); - - // 需要修复的订单号 - private readonly ORDER_NO = 'WD1766719397843H90GUW'; - private readonly SENDER_ACCOUNT = 'D25122600004'; - private readonly RECEIVER_ACCOUNT = 'D25122600006'; - private readonly MISSING_FEE = new Decimal('2'); // 缺失的手续费 - - constructor(private readonly prisma: PrismaService) {} - - async onModuleInit() { - // 延迟 5 秒执行,确保所有服务都已启动 - setTimeout(() => this.executeFixOnce(), 5000); - } - - private async executeFixOnce(): Promise { - this.logger.log('========================================'); - this.logger.log('[OTP-FIX] Starting one-time transfer fix'); - this.logger.log(`[OTP-FIX] Order: ${this.ORDER_NO}`); - this.logger.log(`[OTP-FIX] From: ${this.SENDER_ACCOUNT} -> To: ${this.RECEIVER_ACCOUNT}`); - this.logger.log('========================================'); - - try { - // 检查订单状态 - const order = await this.prisma.withdrawalOrder.findUnique({ - where: { orderNo: this.ORDER_NO }, - }); - - if (!order) { - this.logger.log('[OTP-FIX] Order not found, skipping (may be wrong environment)'); - return; - } - - // 如果已经确认,说明已修复过(幂等性) - if (order.status === WithdrawalStatus.CONFIRMED) { - this.logger.log('[OTP-FIX] Order already CONFIRMED, skipping'); - return; - } - - if (order.status !== WithdrawalStatus.FROZEN) { - this.logger.warn(`[OTP-FIX] Unexpected order status: ${order.status}, skipping`); - return; - } - - // 执行修复 - await this.prisma.$transaction(async (tx) => { - // 1. 获取发送方钱包 - const senderWallet = await tx.walletAccount.findUnique({ - where: { accountSequence: this.SENDER_ACCOUNT }, - }); - - if (!senderWallet) { - throw new Error(`Sender wallet not found: ${this.SENDER_ACCOUNT}`); - } - - // 2. 获取接收方钱包 - const receiverWallet = await tx.walletAccount.findUnique({ - where: { accountSequence: this.RECEIVER_ACCOUNT }, - }); - - if (!receiverWallet) { - throw new Error(`Receiver wallet not found: ${this.RECEIVER_ACCOUNT}`); - } - - const amount = new Decimal(order.amount.toString()); - const fee = new Decimal(order.fee.toString()); - const totalAmount = amount.add(fee); - - this.logger.log(`[OTP-FIX] Amount: ${amount}, Fee: ${fee}, Total: ${totalAmount}`); - - // 3. 修复发送方冻结余额 (加上缺失的手续费) 并同时扣除 - const senderCurrentFrozen = new Decimal(senderWallet.usdtFrozen.toString()); - // 加上缺失的手续费后再扣除全部 - const senderNewFrozen = senderCurrentFrozen.add(this.MISSING_FEE).minus(totalAmount); - - this.logger.log(`[OTP-FIX] Sender frozen: ${senderCurrentFrozen} + ${this.MISSING_FEE} - ${totalAmount} = ${senderNewFrozen}`); - - await tx.walletAccount.update({ - where: { id: senderWallet.id }, - data: { - usdtFrozen: senderNewFrozen, - version: senderWallet.version + 1, - updatedAt: new Date(), - }, - }); - - // 4. 增加接收方余额 - const receiverCurrentAvailable = new Decimal(receiverWallet.usdtAvailable.toString()); - const receiverNewAvailable = receiverCurrentAvailable.add(amount); - - this.logger.log(`[OTP-FIX] Receiver available: ${receiverCurrentAvailable} + ${amount} = ${receiverNewAvailable}`); - - await tx.walletAccount.update({ - where: { id: receiverWallet.id }, - data: { - usdtAvailable: receiverNewAvailable, - version: receiverWallet.version + 1, - updatedAt: new Date(), - }, - }); - - // 5. 创建发送方流水 (TRANSFER_OUT) - await tx.ledgerEntry.create({ - data: { - accountSequence: order.accountSequence, - userId: order.userId, - entryType: LedgerEntryType.TRANSFER_OUT, - amount: amount.negated(), - assetType: 'USDT', - balanceAfter: senderWallet.usdtAvailable, - refOrderId: order.orderNo, - refTxHash: 'OTP-FIX-INTERNAL', - memo: `转账至 ${order.toAccountSequence} (OTP修复)`, - payloadJson: { - toAccountSequence: order.toAccountSequence, - toUserId: order.toUserId?.toString(), - fee: order.fee.toString(), - fixNote: 'One-time fix for concurrent modification issue', - }, - }, - }); - - // 6. 创建接收方流水 (TRANSFER_IN) - await tx.ledgerEntry.create({ - data: { - accountSequence: order.toAccountSequence!, - userId: order.toUserId!, - entryType: LedgerEntryType.TRANSFER_IN, - amount: amount, - assetType: 'USDT', - balanceAfter: receiverNewAvailable, - refOrderId: order.orderNo, - refTxHash: 'OTP-FIX-INTERNAL', - memo: `来自 ${order.accountSequence} 的转账 (OTP修复)`, - payloadJson: { - fromAccountSequence: order.accountSequence, - fromUserId: order.userId.toString(), - fixNote: 'One-time fix for concurrent modification issue', - }, - }, - }); - - // 7. 更新订单状态为 CONFIRMED - await tx.withdrawalOrder.update({ - where: { id: order.id }, - data: { - status: WithdrawalStatus.CONFIRMED, - txHash: 'OTP-FIX-INTERNAL', - broadcastedAt: new Date(), - confirmedAt: new Date(), - }, - }); - - this.logger.log('[OTP-FIX] Transaction completed successfully'); - }); - - this.logger.log('========================================'); - this.logger.log('[OTP-FIX] Transfer fix completed!'); - this.logger.log(`[OTP-FIX] ${this.SENDER_ACCOUNT} -> ${this.RECEIVER_ACCOUNT}: ${this.prisma}`); - this.logger.log('[OTP-FIX] Order status: CONFIRMED'); - this.logger.log('[OTP-FIX] Please remove this file after deployment'); - this.logger.log('========================================'); - - } catch (error) { - this.logger.error('[OTP-FIX] Failed to execute fix', error); - } - } -}