From 28cf0b7769117046fc92ffb7e1befe7169d1c4b2 Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 1 Mar 2026 11:15:19 -0800 Subject: [PATCH] =?UTF-8?q?fix(wallet):=20settleUserPendingRewards=20?= =?UTF-8?q?=E8=A1=A5=E5=88=9B=E5=BB=BA=20REWARD=5FTO=5FSETTLEABLE=20?= =?UTF-8?q?=E6=B5=81=E6=B0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 转换 PENDING→SETTLEABLE 时,为每笔奖励创建带来源信息的 REWARD_TO_SETTLEABLE 流水,解决"分享收益"筛选缺失问题。 统计排除逻辑同步更新,通过 convertedFromPending 标记避免双重计算。 Co-Authored-By: Claude Opus 4.6 --- .../services/wallet-application.service.ts | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/backend/services/wallet-service/src/application/services/wallet-application.service.ts b/backend/services/wallet-service/src/application/services/wallet-application.service.ts index b7d6e03f..a46b404e 100644 --- a/backend/services/wallet-service/src/application/services/wallet-application.service.ts +++ b/backend/services/wallet-service/src/application/services/wallet-application.service.ts @@ -2450,6 +2450,33 @@ export class WalletApplicationService { }); } + // 4. 为每笔 pending reward 创建 REWARD_TO_SETTLEABLE 流水(保留来源信息,供"分享收益"筛选) + // 注意:payloadJson.convertedFromPending = true 用于统计时排除(避免与 REWARD_PENDING 双重计算) + for (const reward of pendingRewards) { + if (reward.usdtAmount.value <= 0) continue; + // 查找原始 REWARD_PENDING 流水,获取来源信息 + const originalEntry = await tx.ledgerEntry.findFirst({ + where: { + accountSequence, + entryType: LedgerEntryType.REWARD_PENDING, + refOrderId: reward.sourceOrderId, + }, + }); + const originalPayload = (originalEntry?.payloadJson as Record) || {}; + await tx.ledgerEntry.create({ + data: { + accountSequence, + userId: walletRecord.userId, + entryType: LedgerEntryType.REWARD_TO_SETTLEABLE, + amount: reward.usdtAmount.value, + assetType: 'USDT', + refOrderId: reward.sourceOrderId, + memo: originalEntry?.memo?.replace('待领取', '可结算') || `[预种] ${reward.allocationType} | 可结算`, + payloadJson: { ...originalPayload, convertedFromPending: true }, + }, + }); + } + this.logger.log(`[settleUserPendingRewards] Transaction committed: ${pendingRewards.length} rewards settled for ${accountSequence}`); } else { this.logger.warn(`[settleUserPendingRewards] Wallet not found for ${accountSequence}, skipping wallet update`); @@ -2698,9 +2725,12 @@ export class WalletApplicationService { // 只计算非临时性流水的收支统计 // 兼容历史数据:老的批量转换记录 entry_type 仍为 REWARD_TO_SETTLEABLE, - // 通过 memo 中 "pending rewards settled" 识别并排除 + // 通过 memo 中 "pending rewards settled" 或 payloadJson.convertedFromPending 识别并排除 const isExcluded = excludeFromStats.has(entry.entryType) || - (entry.entryType === 'REWARD_TO_SETTLEABLE' && entry.memo?.includes('pending rewards settled')); + (entry.entryType === 'REWARD_TO_SETTLEABLE' && ( + entry.memo?.includes('pending rewards settled') || + (entry.payloadJson as Record)?.convertedFromPending === true + )); if (!isExcluded) { if (amount > 0) { totalIncome += amount; @@ -2797,7 +2827,10 @@ export class WalletApplicationService { // 只计算非临时性流水的收支统计(兼容历史批量转换数据) const isExcluded = excludeFromStats.has(entry.entryType) || - (entry.entryType === 'REWARD_TO_SETTLEABLE' && entry.memo?.includes('pending rewards settled')); + (entry.entryType === 'REWARD_TO_SETTLEABLE' && ( + entry.memo?.includes('pending rewards settled') || + (entry.payloadJson as Record)?.convertedFromPending === true + )); if (!isExcluded) { if (amount > 0) { existing.income += amount;