revert: 回滚 settleToBalance 的直接 Prisma 实现,准备用 Unit of Work 模式重新实现
This commit is contained in:
parent
4c6e64a604
commit
7dc25b75d2
|
|
@ -909,89 +909,42 @@ export class WalletApplicationService {
|
|||
}> {
|
||||
this.logger.log(`Settling ${params.usdtAmount} USDT to balance for ${params.accountSequence}`);
|
||||
|
||||
// 生成结算ID
|
||||
const settlementId = `STL_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
let balanceAfter = 0;
|
||||
let userId: bigint = BigInt(0);
|
||||
|
||||
try {
|
||||
// 使用事务确保账户余额更新和流水记录的原子性
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
// 1. 查找钱包
|
||||
const walletRecord = await tx.walletAccount.findUnique({
|
||||
where: { accountSequence: params.accountSequence },
|
||||
});
|
||||
if (!walletRecord) {
|
||||
throw new WalletNotFoundError(`accountSequence: ${params.accountSequence}`);
|
||||
}
|
||||
// 1. 查找钱包
|
||||
const wallet = await this.walletRepo.findByAccountSequence(params.accountSequence);
|
||||
if (!wallet) {
|
||||
throw new WalletNotFoundError(`accountSequence: ${params.accountSequence}`);
|
||||
}
|
||||
|
||||
userId = walletRecord.userId;
|
||||
const usdtAmount = Money.USDT(params.usdtAmount);
|
||||
const userId = wallet.userId.value;
|
||||
|
||||
// 2. 验证钱包状态是否 ACTIVE
|
||||
if (walletRecord.status !== 'ACTIVE') {
|
||||
throw new Error(`Wallet is not active: ${walletRecord.status}`);
|
||||
}
|
||||
// 2. 生成结算ID
|
||||
const settlementId = `STL_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const Decimal = (await import('decimal.js')).default;
|
||||
const usdtAmountDecimal = new Decimal(params.usdtAmount);
|
||||
const currentSettleable = new Decimal(walletRecord.settleableUsdt.toString());
|
||||
const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
|
||||
const currentSettledTotal = new Decimal(walletRecord.settledTotalUsdt.toString());
|
||||
// 3. 执行钱包结算
|
||||
wallet.settleToBalance(usdtAmount, settlementId);
|
||||
await this.walletRepo.save(wallet);
|
||||
|
||||
// 3. 验证可结算余额足够
|
||||
if (currentSettleable.lessThan(usdtAmountDecimal)) {
|
||||
throw new Error(`Insufficient settleable balance: ${currentSettleable} < ${usdtAmountDecimal}`);
|
||||
}
|
||||
|
||||
// 4. 计算新余额
|
||||
const newSettleable = currentSettleable.minus(usdtAmountDecimal);
|
||||
const newAvailable = currentAvailable.plus(usdtAmountDecimal);
|
||||
const newSettledTotal = currentSettledTotal.plus(usdtAmountDecimal);
|
||||
balanceAfter = newAvailable.toNumber();
|
||||
|
||||
// 5. 更新钱包账户(在事务内,使用乐观锁)
|
||||
const currentVersion = walletRecord.version;
|
||||
const updateResult = await tx.walletAccount.updateMany({
|
||||
where: {
|
||||
accountSequence: params.accountSequence,
|
||||
version: currentVersion, // 乐观锁:只有版本匹配才更新
|
||||
},
|
||||
data: {
|
||||
settleableUsdt: newSettleable.toFixed(8),
|
||||
usdtAvailable: newAvailable.toFixed(8),
|
||||
settledTotalUsdt: newSettledTotal.toFixed(8),
|
||||
version: currentVersion + 1,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
if (updateResult.count === 0) {
|
||||
throw new Error(`Optimistic lock conflict for wallet ${params.accountSequence}`);
|
||||
}
|
||||
|
||||
// 6. 创建流水记录(在事务内)
|
||||
await tx.ledgerEntry.create({
|
||||
data: {
|
||||
accountSequence: params.accountSequence,
|
||||
userId: walletRecord.userId,
|
||||
entryType: LedgerEntryType.REWARD_SETTLED,
|
||||
amount: usdtAmountDecimal.toFixed(8),
|
||||
assetType: 'USDT',
|
||||
balanceAfter: newAvailable.toFixed(8),
|
||||
refOrderId: settlementId,
|
||||
memo: params.memo || `结算 ${params.usdtAmount} 绿积分到钱包余额`,
|
||||
payloadJson: {
|
||||
settlementType: 'SETTLE_TO_BALANCE',
|
||||
rewardEntryIds: params.rewardEntryIds,
|
||||
rewardCount: params.rewardEntryIds.length,
|
||||
breakdown: params.breakdown,
|
||||
},
|
||||
createdAt: new Date(),
|
||||
},
|
||||
});
|
||||
// 4. 记录账本流水(含详细来源信息)
|
||||
const ledgerEntry = LedgerEntry.create({
|
||||
accountSequence: wallet.accountSequence,
|
||||
userId: UserId.create(userId),
|
||||
entryType: LedgerEntryType.REWARD_SETTLED,
|
||||
amount: usdtAmount,
|
||||
balanceAfter: wallet.balances.usdt.available,
|
||||
refOrderId: settlementId,
|
||||
memo: params.memo || `结算 ${params.usdtAmount} 绿积分到钱包余额`,
|
||||
payloadJson: {
|
||||
settlementType: 'SETTLE_TO_BALANCE',
|
||||
rewardEntryIds: params.rewardEntryIds,
|
||||
rewardCount: params.rewardEntryIds.length,
|
||||
breakdown: params.breakdown,
|
||||
},
|
||||
});
|
||||
await this.ledgerRepo.save(ledgerEntry);
|
||||
|
||||
// 7. 使缓存失效(在事务外,事务成功后执行)
|
||||
// 5. 使缓存失效
|
||||
await this.walletCacheService.invalidateWallet(userId);
|
||||
|
||||
this.logger.log(`Successfully settled ${params.usdtAmount} USDT to balance for ${params.accountSequence}`);
|
||||
|
|
@ -1000,7 +953,7 @@ export class WalletApplicationService {
|
|||
success: true,
|
||||
settlementId,
|
||||
settledAmount: params.usdtAmount,
|
||||
balanceAfter,
|
||||
balanceAfter: wallet.balances.usdt.available.value,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to settle to balance for ${params.accountSequence}: ${error.message}`);
|
||||
|
|
|
|||
Loading…
Reference in New Issue