fix(wallet-service): 添加钱包状态检查,确保只有 ACTIVE 钱包可结算
This commit is contained in:
parent
5204d24c88
commit
65cb574f59
|
|
@ -596,7 +596,8 @@
|
|||
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(authorization\\): 火柴人排名过滤已撤销授权的考核记录\n\n- findRankingsByMonthAndRegion 和 findRankingsByMonthAndRoleType 增加过滤条件\n- 排除 authorization.status = ''REVOKED'' 的记录\n- 解决同一用户因有多条授权记录(含已撤销)而重复显示的问题\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
||||
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(reward-service\\): 修复 WalletServiceClient 未正确解析 wallet-service 响应格式的 Bug\n\n问题原因:\nwallet-service 使用全局 TransformInterceptor 拦截器,会将所有响应包装成:\n{ success: true, data: { success: boolean, ... }, timestamp: \"...\" }\n\n原代码直接读取外层的 success 字段(始终为 true),导致即使业务失败\n(内层 data.success = false)也被误判为成功。\n\n具体案例:\n用户 D25122700024 点击结算时,wallet-service 因余额不足返回:\n{ success: true, data: { success: false, error: \"Insufficient...\" }, ... }\nreward-service 误读为成功,导致奖励被标记为 SETTLED 但钱包余额未变更。\n\n修复内容:\n1. settleToBalance: 解析 response_data.data 获取真实业务结果\n2. confirmPlantingDeduction: 同上\n3. allocateFunds: 同上\n\n所有方法现在会:\n- 使用 response_data.data || response_data 兼容包装和非包装格式\n- 严格检查 data.success !== true 来判断业务是否成功\n- 失败时记录详细错误日志\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
||||
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(wallet-service\\): 统一奖励分配到 settleable_usdt,与 reward-service 保持一致\n\n问题原因:\nwallet-service 对不同类型奖励的分配方式不一致:\n- SHARE_RIGHT: 正确使用 addSettleableReward\\(\\) → settleable_usdt\n- CITY_TEAM_RIGHT/COMMUNITY_RIGHT: 错误使用 addAvailableBalance\\(\\) → usdt_available\n\n这导致 reward-service 记录的 SETTLEABLE 奖励总额与 wallet-service 的\nsettleable_usdt 字段不匹配。用户 D25122700024 的案例中:\n- reward-service: 3条奖励共 4464 USDT \\(SHARE_RIGHT 3600 + CITY_TEAM_RIGHT 288 + COMMUNITY_RIGHT 576\\)\n- wallet-service: settleable_usdt = 3600 \\(仅 SHARE_RIGHT\\)\n差额 864 USDT 被错误地放入了 usdt_available\n\n修复内容:\n1. allocateCommunityRight: 改用 addSettleableReward\\(\\) 替代 addAvailableBalance\\(\\)\n2. allocateToRegionAccount: 改用 addSettleableReward\\(\\) 替代 addAvailableBalance\\(\\)\n3. 流水类型统一使用 REWARD_TO_SETTLEABLE 替代 SYSTEM_ALLOCATION\n4. 日志和备注更新以反映新的分配方式\n\n设计原则:\n- reward-service 是奖励的权威来源\n- wallet-service 应跟随 reward-service 的设计\n- 所有奖励都应进入 settleable_usdt,用户主动结算后才转入 usdt_available\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
||||
"Bash(ls \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\backend\\\\services\\\\reward-service\\\\prisma\"\" 2>/dev/null || dir \"c:UsersdongDesktoprwadurianbackendservicesreward-serviceprisma\"\")"
|
||||
"Bash(ls \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\backend\\\\services\\\\reward-service\\\\prisma\"\" 2>/dev/null || dir \"c:UsersdongDesktoprwadurianbackendservicesreward-serviceprisma\"\")",
|
||||
"Bash(git commit -m \"$\\(cat <<''EOF''\nfix\\(wallet-service\\): 修复 settleToBalance 方法缺少事务保护的严重 Bug\n\n问题原因:\nsettleToBalance 方法先执行 wallet.save\\(\\) 更新账户余额,再执行\nledgerRepo.save\\(\\) 写入流水记录。两个操作不在同一个事务中。\n\n当流水写入失败时(如 memo 字段超过 VarChar\\(500\\) 限制),账户余额\n已经被修改,但流水记录未写入,导致数据不一致。\n\n具体案例:\n用户 D25122700023 点击结算时,memo 内容超长(66笔奖励详情),\nwallet-service 先把 settleable_usdt 转入 usdt_available,然后\n写流水失败。账户余额被改但没有对应流水。\n\n修复内容:\n1. settleToBalance: 使用 prisma.$transaction 确保原子性\n - 账户余额更新和流水记录在同一事务中\n - 任一操作失败整个事务回滚\n2. schema: memo 字段从 VarChar\\(500\\) 改为 Text 类型,无长度限制\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
|
|
@ -926,24 +926,30 @@ export class WalletApplicationService {
|
|||
}
|
||||
|
||||
userId = walletRecord.userId;
|
||||
|
||||
// 2. 验证钱包状态是否 ACTIVE
|
||||
if (walletRecord.status !== 'ACTIVE') {
|
||||
throw new Error(`Wallet is not active: ${walletRecord.status}`);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// 2. 验证可结算余额足够
|
||||
// 3. 验证可结算余额足够
|
||||
if (currentSettleable.lessThan(usdtAmountDecimal)) {
|
||||
throw new Error(`Insufficient settleable balance: ${currentSettleable} < ${usdtAmountDecimal}`);
|
||||
}
|
||||
|
||||
// 3. 计算新余额
|
||||
// 4. 计算新余额
|
||||
const newSettleable = currentSettleable.minus(usdtAmountDecimal);
|
||||
const newAvailable = currentAvailable.plus(usdtAmountDecimal);
|
||||
const newSettledTotal = currentSettledTotal.plus(usdtAmountDecimal);
|
||||
balanceAfter = newAvailable.toNumber();
|
||||
|
||||
// 4. 更新钱包账户(在事务内)
|
||||
// 5. 更新钱包账户(在事务内)
|
||||
await tx.walletAccount.update({
|
||||
where: { accountSequence: params.accountSequence },
|
||||
data: {
|
||||
|
|
@ -954,7 +960,7 @@ export class WalletApplicationService {
|
|||
},
|
||||
});
|
||||
|
||||
// 5. 创建流水记录(在事务内)
|
||||
// 6. 创建流水记录(在事务内)
|
||||
await tx.ledgerEntry.create({
|
||||
data: {
|
||||
accountSequence: params.accountSequence,
|
||||
|
|
@ -976,7 +982,7 @@ export class WalletApplicationService {
|
|||
});
|
||||
});
|
||||
|
||||
// 6. 使缓存失效(在事务外,事务成功后执行)
|
||||
// 7. 使缓存失效(在事务外,事务成功后执行)
|
||||
await this.walletCacheService.invalidateWallet(userId);
|
||||
|
||||
this.logger.log(`Successfully settled ${params.usdtAmount} USDT to balance for ${params.accountSequence}`);
|
||||
|
|
|
|||
Loading…
Reference in New Issue