fix(wallet): 区分直接入账和批量转换的 REWARD_TO_SETTLEABLE

新增 REWARD_PENDING_CONVERTED 类型用于批量转换(待领取→可结算),
REWARD_TO_SETTLEABLE 保留给直接入账(hasPlanted=true时的新收入)。

统计排除:REWARD_PENDING_CONVERTED + REWARD_SETTLED(状态转换)
统计计入:REWARD_PENDING + REWARD_TO_SETTLEABLE(首次入账)

已迁移7条历史数据的 entry_type。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-03-01 10:30:47 -08:00
parent 31e6f9e15a
commit d876dd1591
3 changed files with 27 additions and 21 deletions

View File

@ -2442,10 +2442,10 @@ export class WalletApplicationService {
data: {
accountSequence,
userId: walletRecord.userId,
entryType: LedgerEntryType.REWARD_TO_SETTLEABLE,
entryType: LedgerEntryType.REWARD_PENDING_CONVERTED,
amount: totalUsdt,
assetType: 'USDT',
memo: `${pendingRewards.length} pending rewards settled`,
memo: `[状态转换] ${pendingRewards.length}笔待领取奖励转为可结算`,
},
});
}
@ -2673,17 +2673,20 @@ export class WalletApplicationService {
}
// 计算收支
// 排除临时性流水类型(冻结/解冻)和状态转换类型,避免双重计算
// 例如:认种流程会产生 PLANT_FREEZE(-100) + PLANT_PAYMENT(-100),实际只支出 100
// 例如:奖励流程 REWARD_PENDING(+720) → REWARD_TO_SETTLEABLE(+720) → REWARD_SETTLED(+720)
// 是同一笔钱的三个阶段,只在 REWARD_PENDING 计入收入
// 排除临时性流水和状态转换类型,避免双重计算
// 认种PLANT_FREEZE(-100) + PLANT_PAYMENT(-100) → 只计 PLANT_PAYMENT
// 奖励两条路径:
// 未认种时REWARD_PENDING(+720) → REWARD_PENDING_CONVERTED(+720) → REWARD_SETTLED(+720)
// 只在 REWARD_PENDING 计入
// 已认种时REWARD_TO_SETTLEABLE(+720) → REWARD_SETTLED(+720)
// 只在 REWARD_TO_SETTLEABLE 计入(这是该笔钱的首次入账)
const excludeFromStats = new Set([
'PLANT_FREEZE', // 认种冻结(临时)
'PLANT_UNFREEZE', // 认种解冻(回滚)
'FREEZE', // 通用冻结(临时)
'UNFREEZE', // 通用解冻(回滚)
'REWARD_TO_SETTLEABLE', // 待领取→可结算状态转换REWARD_PENDING 已计入)
'REWARD_SETTLED', // 可结算→结算到余额(状态转换,REWARD_PENDING 已计入)
'PLANT_FREEZE', // 认种冻结(临时)
'PLANT_UNFREEZE', // 认种解冻(回滚)
'FREEZE', // 通用冻结(临时)
'UNFREEZE', // 通用解冻(回滚)
'REWARD_PENDING_CONVERTED', // 待领取批量转可结算状态转换REWARD_PENDING 已计入)
'REWARD_SETTLED', // 可结算→结算到余额(状态转换,首次入账已计入)
]);
let totalIncome = 0;
@ -2770,14 +2773,14 @@ export class WalletApplicationService {
}
// 填充数据
// 排除临时性流水类型(冻结/解冻)和状态转换类型,避免双重计算
// 排除临时性流水和状态转换类型,避免双重计算(与上方 getLedgerStatistics 一致)
const excludeFromStats = new Set([
'PLANT_FREEZE', // 认种冻结(临时)
'PLANT_UNFREEZE', // 认种解冻(回滚)
'FREEZE', // 通用冻结(临时)
'UNFREEZE', // 通用解冻(回滚)
'REWARD_TO_SETTLEABLE', // 待领取→可结算状态转换REWARD_PENDING 已计入
'REWARD_SETTLED', // 可结算→结算到余额(状态转换REWARD_PENDING 已计入
'PLANT_FREEZE', // 认种冻结(临时)
'PLANT_UNFREEZE', // 认种解冻(回滚)
'FREEZE', // 通用冻结(临时)
'UNFREEZE', // 通用解冻(回滚)
'REWARD_PENDING_CONVERTED', // 待领取批量转可结算(状态转换
'REWARD_SETTLED', // 可结算→结算到余额(状态转换
]);
let periodIncome = 0;
@ -2833,7 +2836,8 @@ export class WalletApplicationService {
PLANT_FREEZE: '认种冻结',
PLANT_UNFREEZE: '认种解冻',
REWARD_PENDING: '待领取奖励',
REWARD_TO_SETTLEABLE: '奖励转可结算',
REWARD_TO_SETTLEABLE: '分享收益',
REWARD_PENDING_CONVERTED: '待领取转可结算',
REWARD_EXPIRED: '奖励过期',
REWARD_SETTLED: '提取',
TRANSFER_TO_POOL: '转入矿池',

View File

@ -7,6 +7,7 @@ export enum LedgerEntryType {
REWARD_PENDING = 'REWARD_PENDING',
REWARD_TO_SETTLEABLE = 'REWARD_TO_SETTLEABLE',
REWARD_EXPIRED = 'REWARD_EXPIRED',
REWARD_PENDING_CONVERTED = 'REWARD_PENDING_CONVERTED', // 待领取批量转为可结算(状态转换,非新收入)
REWARD_SETTLED = 'REWARD_SETTLED',
TRANSFER_TO_POOL = 'TRANSFER_TO_POOL',
SWAP_EXECUTED = 'SWAP_EXECUTED',

View File

@ -1474,7 +1474,8 @@ class LedgerEntry {
'PLANT_FREEZE': '认种冻结',
'PLANT_UNFREEZE': '认种解冻',
'REWARD_PENDING': '待领取奖励',
'REWARD_TO_SETTLEABLE': '奖励转可结算',
'REWARD_TO_SETTLEABLE': '分享收益',
'REWARD_PENDING_CONVERTED': '待领取转可结算',
'REWARD_EXPIRED': '奖励过期',
'REWARD_SETTLED': '奖励结算',
'TRANSFER_TO_POOL': '转入矿池',