fix(c2c-bot): 调整purchaseOrder操作顺序,先扣余额再链上转账

问题:原流程先执行不可逆的链上dUSDT转账,再扣减卖家DB余额。
若转账成功但扣减失败(余额不足、DB异常),会导致:
- dUSDT已转出(链上不可回退)
- 订单仍为PENDING(可能被重复处理)
- 卖家余额未扣减

修复:
1. 将deductSellerBalance移至transferDusdt之前(可逆操作先行)
2. 链上转账失败时调用restoreSellerBalance回补余额
3. 新增restoreSellerBalance方法,失败时记录CRITICAL级别日志

修改后流程:扣余额(可逆) → 链上转账(不可逆) → 更新订单状态(DB)
任何步骤失败都不会导致资金损失。

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-01 05:02:19 -08:00
parent 251e37f772
commit c802519ec2
1 changed files with 30 additions and 9 deletions

View File

@ -47,15 +47,24 @@ export class C2cBotService {
}
this.logger.log(`[BOT] Seller Kava address: ${kavaAddress}`);
// 2. 计算 dUSDT 支付金额(积分值 = dUSDT1:1 兑换
// 2. 先扣减卖家积分值余额可逆的DB操作放在不可逆的链上转账之前
const paymentAmount = order.totalAmount;
this.logger.log(`[BOT] Payment amount: ${paymentAmount} dUSDT`);
await this.deductSellerBalance(order.makerAccountSequence, paymentAmount);
this.logger.log(`[BOT] Deducted seller balance, proceeding with on-chain transfer`);
// 3. 调用 mining-blockchain-service 转账 dUSDT
const transferResult = await this.blockchainClient.transferDusdt(kavaAddress, paymentAmount);
// 3. 调用 mining-blockchain-service 转账 dUSDT不可逆
let transferResult;
try {
transferResult = await this.blockchainClient.transferDusdt(kavaAddress, paymentAmount);
} catch (error: any) {
this.logger.error(`[BOT] Transfer exception: ${error.message}, restoring seller balance`);
await this.restoreSellerBalance(order.makerAccountSequence, paymentAmount);
return false;
}
if (!transferResult.success) {
this.logger.error(`[BOT] Transfer failed: ${transferResult.error}`);
this.logger.error(`[BOT] Transfer failed: ${transferResult.error}, restoring seller balance`);
await this.restoreSellerBalance(order.makerAccountSequence, paymentAmount);
return false;
}
@ -67,9 +76,6 @@ export class C2cBotService {
paymentTxHash: transferResult.txHash!,
});
// 5. 扣减卖家的积分值余额
await this.deductSellerBalance(order.makerAccountSequence, order.totalAmount);
this.logger.log(`[BOT] Order ${order.orderNo} completed successfully`);
return true;
} catch (error: any) {
@ -96,7 +102,6 @@ export class C2cBotService {
throw new Error(`Insufficient cash balance for ${accountSequence}`);
}
// 扣减余额
await this.tradingAccountRepository.updateCashBalance(
accountSequence,
amountDecimal.negated().toString(),
@ -105,6 +110,22 @@ export class C2cBotService {
this.logger.log(`[BOT] Deducted ${amount} from ${accountSequence}'s cash balance`);
}
/**
*
*/
private async restoreSellerBalance(accountSequence: string, amount: string): Promise<void> {
try {
await this.tradingAccountRepository.updateCashBalance(
accountSequence,
amount, // 正数,加回余额
);
this.logger.log(`[BOT] Restored ${amount} to ${accountSequence}'s cash balance`);
} catch (error: any) {
// 回补失败是严重问题,必须告警
this.logger.error(`[BOT] CRITICAL: Failed to restore ${amount} to ${accountSequence}: ${error.message}`);
}
}
/**
* Bot
*/