From d3969710be5c06add701f54b94594eec89d281a2 Mon Sep 17 00:00:00 2001 From: hailin Date: Mon, 2 Mar 2026 06:30:34 -0800 Subject: [PATCH] =?UTF-8?q?fix(wallet):=20=E7=B3=BB=E7=BB=9F=E8=B4=A6?= =?UTF-8?q?=E6=88=B7=E5=88=92=E8=BD=AC=E5=89=8D=E8=87=AA=E5=8A=A8=E7=BB=93?= =?UTF-8?q?=E7=AE=97=20settleableUsdt=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=85=A8?= =?UTF-8?q?=E9=A2=9D=E5=88=92=E8=BD=AC=E4=BD=99=E9=A2=9D=E4=B8=8D=E8=B6=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 省/市区域和团队账户收到的奖励进入 settleableUsdt, 但划转验证只检查 usdtAvailable,导致全额划转报"余额不足"。 ## 修复 在 requestSystemWithdrawal 事务中,划转前自动执行结算: 1. 检测账户是否为区域/团队账户(6/7/8/9开头) 2. 若 settleableUsdt > 0,自动结算到 usdtAvailable 3. 记录 REWARD_SETTLED 流水明细(含 trigger=SYSTEM_WITHDRAWAL_AUTO_SETTLE) 4. 结算和划转在同一事务中,保证原子性 ## 审计流水 每次自动结算会产生一条独立的 REWARD_SETTLED 流水: - memo: "系统账户自动结算: 待结算 X 绿积分转入可用余额(划转前自动执行)" - payloadJson: { settledAmount, previousAvailable, newAvailable, trigger } ## 其他 - 划转白名单扩展支持 6xxx(市团队)、7xxx(省团队) - getSystemAccountName 添加省团队/市团队名称映射 Co-Authored-By: Claude Opus 4.6 --- .../system-withdrawal-application.service.ts | 75 ++++++++++++++++--- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/backend/services/wallet-service/src/application/services/system-withdrawal-application.service.ts b/backend/services/wallet-service/src/application/services/system-withdrawal-application.service.ts index 551a4d79..2e0f2c19 100644 --- a/backend/services/wallet-service/src/application/services/system-withdrawal-application.service.ts +++ b/backend/services/wallet-service/src/application/services/system-withdrawal-application.service.ts @@ -95,6 +95,16 @@ export class SystemWithdrawalApplicationService { return `市区域(${accountSequence.substring(1)})`; } + // 省团队账户: 7 + 省代码 + if (accountSequence.startsWith('7') && accountSequence.length === 7) { + return `省团队(${accountSequence.substring(1)})`; + } + + // 市团队账户: 6 + 市代码 + if (accountSequence.startsWith('6') && accountSequence.length === 7) { + return `市团队(${accountSequence.substring(1)})`; + } + return `系统账户(${accountSequence})`; } @@ -107,13 +117,8 @@ export class SystemWithdrawalApplicationService { return true; } - // 省区域账户: 9 + 省代码 - if (accountSequence.startsWith('9') && accountSequence.length === 7) { - return true; - } - - // 市区域账户: 8 + 市代码 - if (accountSequence.startsWith('8') && accountSequence.length === 7) { + // 区域/团队账户: 6=市团队, 7=省团队, 8=市区域, 9=省区域 + if (/^[6-9]\d{6}$/.test(accountSequence)) { return true; } @@ -166,12 +171,60 @@ export class SystemWithdrawalApplicationService { throw new BadRequestException(`系统账户不存在: ${command.fromAccountSequence}`); } - const currentBalance = new Decimal(systemWallet.usdtAvailable.toString()); + let currentAvailable = new Decimal(systemWallet.usdtAvailable.toString()); + const settleableAmount = new Decimal(systemWallet.settleableUsdt?.toString() || '0'); const withdrawAmount = new Decimal(command.amount); - if (currentBalance.lessThan(withdrawAmount)) { + // 6.1.1 如果是区域/团队账户且有待结算余额,先自动结算 + // 6=市团队, 7=省团队, 8=市区域, 9=省区域 — 这些账户的奖励都进 settleableUsdt + const isRegionAccount = /^[6-9]/.test(command.fromAccountSequence); + if (isRegionAccount && settleableAmount.greaterThan(0)) { + this.logger.log( + `[SYSTEM_WITHDRAWAL] 区域账户 ${command.fromAccountSequence} 自动结算: ` + + `settleableUsdt=${settleableAmount.toFixed(2)} → usdtAvailable` + ); + + // 结算:settleableUsdt → usdtAvailable + const newAvailable = currentAvailable.plus(settleableAmount); + await tx.walletAccount.update({ + where: { id: systemWallet.id }, + data: { + usdtAvailable: newAvailable, + settleableUsdt: new Decimal(0), + updatedAt: new Date(), + }, + }); + + // 记录结算流水(审计用) + await tx.ledgerEntry.create({ + data: { + accountSequence: command.fromAccountSequence, + userId: systemWallet.userId, + entryType: LedgerEntryType.REWARD_SETTLED, + amount: settleableAmount, + assetType: 'USDT', + balanceAfter: newAvailable, + memo: `系统账户自动结算: 待结算 ${settleableAmount.toFixed(2)} 绿积分转入可用余额(划转前自动执行)`, + payloadJson: { + settledAmount: settleableAmount.toFixed(2), + previousAvailable: currentAvailable.toFixed(2), + newAvailable: newAvailable.toFixed(2), + trigger: 'SYSTEM_WITHDRAWAL_AUTO_SETTLE', + }, + }, + }); + + // 更新本地变量 + currentAvailable = newAvailable; + this.logger.log( + `[SYSTEM_WITHDRAWAL] 自动结算完成: 可用余额 ${currentAvailable.toFixed(2)} 绿积分` + ); + } + + // 6.1.2 验证可用余额是否足够 + if (currentAvailable.lessThan(withdrawAmount)) { throw new BadRequestException( - `余额不足: 当前 ${currentBalance.toFixed(2)} 绿积分, 需要 ${withdrawAmount.toFixed(2)} 绿积分` + `余额不足: 当前可用 ${currentAvailable.toFixed(2)} 绿积分, 需要 ${withdrawAmount.toFixed(2)} 绿积分` ); } @@ -179,7 +232,7 @@ export class SystemWithdrawalApplicationService { const orderNo = this.generateOrderNo(); // 6.3 扣减系统账户余额 - const newBalance = currentBalance.minus(withdrawAmount); + const newBalance = currentAvailable.minus(withdrawAmount); await tx.walletAccount.update({ where: { id: systemWallet.id }, data: {