fix(wallet): 系统账户划转前自动结算 settleableUsdt,解决全额划转余额不足
## 问题
省/市区域和团队账户收到的奖励进入 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 <noreply@anthropic.com>
This commit is contained in:
parent
5fad40cec1
commit
d3969710be
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue