fix(batch-mining): 修复重复用户计算问题
- 添加 userBatchContributions 按用户-批次跟踪算力 - 修复阶段计算时同一用户被重复计算的问题 - 修复输出结果时同一用户金额被重复累加的问题 - 使用 processedInPhase Set 避免同一阶段重复处理同一用户 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f14ad0b7ad
commit
2358b3ea17
|
|
@ -189,10 +189,30 @@ export class BatchMiningService {
|
|||
this.logger.log(`[preview] 批次${batchNum}算力: ${batchTotal.toFixed(2)}`);
|
||||
}
|
||||
|
||||
// 计算每个用户的算力
|
||||
// 计算每个用户在每个批次中的算力
|
||||
// key: `${accountSequence}-${batch}`, value: 该用户在该批次的算力
|
||||
const userBatchContributions = new Map<string, Decimal>();
|
||||
for (const item of items) {
|
||||
const key = `${item.accountSequence}-${item.batch}`;
|
||||
const contribution = this.calculateUserContribution(item.treeCount);
|
||||
const existing = userBatchContributions.get(key);
|
||||
if (existing) {
|
||||
userBatchContributions.set(key, existing.plus(contribution));
|
||||
} else {
|
||||
userBatchContributions.set(key, contribution);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算每个用户的总算力(用于显示)
|
||||
const userContributions = new Map<string, Decimal>();
|
||||
for (const item of items) {
|
||||
userContributions.set(item.accountSequence, this.calculateUserContribution(item.treeCount));
|
||||
const contribution = this.calculateUserContribution(item.treeCount);
|
||||
const existing = userContributions.get(item.accountSequence);
|
||||
if (existing) {
|
||||
userContributions.set(item.accountSequence, existing.plus(contribution));
|
||||
} else {
|
||||
userContributions.set(item.accountSequence, contribution);
|
||||
}
|
||||
}
|
||||
|
||||
// 定义挖矿阶段
|
||||
|
|
@ -214,6 +234,7 @@ export class BatchMiningService {
|
|||
this.logger.log(`[preview] 每日产出: ${dailyDistribution.toFixed(8)}, 每日补发额度(70%): ${dailyAllocation.toFixed(8)}`);
|
||||
|
||||
// 计算每个用户在各阶段的收益
|
||||
// 使用 Set 记录已处理的用户,避免重复计算
|
||||
const userAmounts = new Map<string, Decimal>();
|
||||
for (const item of items) {
|
||||
userAmounts.set(item.accountSequence, new Decimal(0));
|
||||
|
|
@ -223,12 +244,26 @@ export class BatchMiningService {
|
|||
// 该阶段的总补发额 = 每日补发额度 × 阶段天数
|
||||
const phaseAllocation = dailyAllocation.times(phase.daysInPhase);
|
||||
|
||||
// 用 Set 记录本阶段已处理的用户,避免重复
|
||||
const processedInPhase = new Set<string>();
|
||||
|
||||
for (const item of items) {
|
||||
// 检查该用户的批次是否参与此阶段
|
||||
if (phase.participatingBatches.includes(item.batch)) {
|
||||
const userContribution = userContributions.get(item.accountSequence)!;
|
||||
// 用户收益 = 阶段补发额 × (用户算力 / 当前参与总算力)
|
||||
const ratio = userContribution.dividedBy(phase.participatingContribution);
|
||||
// 检查该用户的批次是否参与此阶段,且该用户在本阶段尚未处理
|
||||
if (phase.participatingBatches.includes(item.batch) && !processedInPhase.has(item.accountSequence)) {
|
||||
processedInPhase.add(item.accountSequence);
|
||||
|
||||
// 计算用户在该阶段参与的批次中的总算力
|
||||
let userPhaseContribution = new Decimal(0);
|
||||
for (const batch of phase.participatingBatches) {
|
||||
const key = `${item.accountSequence}-${batch}`;
|
||||
const batchContrib = userBatchContributions.get(key);
|
||||
if (batchContrib) {
|
||||
userPhaseContribution = userPhaseContribution.plus(batchContrib);
|
||||
}
|
||||
}
|
||||
|
||||
// 用户收益 = 阶段补发额 × (用户在该阶段的算力 / 当前参与总算力)
|
||||
const ratio = userPhaseContribution.dividedBy(phase.participatingContribution);
|
||||
const phaseAmount = phaseAllocation.times(ratio);
|
||||
|
||||
const currentAmount = userAmounts.get(item.accountSequence)!;
|
||||
|
|
@ -254,6 +289,8 @@ export class BatchMiningService {
|
|||
|
||||
const users: BatchMiningPreviewResult['batches'][0]['users'] = [];
|
||||
let batchTotalAmount = new Decimal(0);
|
||||
// 跟踪本批次已处理的用户,避免重复累加金额
|
||||
const processedUsersInBatch = new Set<string>();
|
||||
|
||||
for (const item of batchItems) {
|
||||
const userContribution = userContributions.get(item.accountSequence)!;
|
||||
|
|
@ -272,7 +309,11 @@ export class BatchMiningService {
|
|||
estimatedAmount: amount.toFixed(8),
|
||||
});
|
||||
|
||||
batchTotalAmount = batchTotalAmount.plus(amount);
|
||||
// 只在第一次遇到该用户时累加金额
|
||||
if (!processedUsersInBatch.has(item.accountSequence)) {
|
||||
processedUsersInBatch.add(item.accountSequence);
|
||||
batchTotalAmount = batchTotalAmount.plus(amount);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算到当前批次的累计算力
|
||||
|
|
@ -311,16 +352,15 @@ export class BatchMiningService {
|
|||
*
|
||||
* 根据批次的"提前天数"构建各挖矿阶段:
|
||||
* - preMineDays 表示该批次比下一批次提前开始的天数
|
||||
* - 批次1的 preMineDays=3 意味着批次1比批次2提前3天
|
||||
* - 批次1的 preMineDays=3 意味着批次1先独挖3天
|
||||
* - 批次2的 preMineDays=2 意味着批次1+2一起挖2天
|
||||
* - 批次3的 preMineDays=1 意味着批次1+2+3一起挖1天
|
||||
* - 批次1的 preMineDays=3 意味着批次1比批次2提前3天,所以批次1先独挖3天
|
||||
* - 批次2的 preMineDays=2 意味着批次2比批次3提前2天,所以批次1+2一起挖2天
|
||||
* - 批次3的 preMineDays=1 意味着批次3比批次4提前1天,所以批次1+2+3一起挖1天
|
||||
* - 批次4的 preMineDays=0 意味着批次4是最后一批,所有批次一起挖剩余天数
|
||||
*
|
||||
* 阶段划分:
|
||||
* - 阶段1: 只有批次1,持续 preMineDays 天
|
||||
* - 阶段2: 批次1+2,持续 preMineDays 天
|
||||
* - 阶段3: 批次1+2+3,持续 preMineDays 天
|
||||
* - ...
|
||||
* 正确的阶段划分:
|
||||
* - 阶段1: 只有批次1,持续批次1的 preMineDays 天(在批次2加入之前)
|
||||
* - 阶段2: 批次1+2,持续批次2的 preMineDays 天(在批次3加入之前)
|
||||
* - 阶段3: 批次1+2+3,持续批次3的 preMineDays 天(在批次4加入之前)
|
||||
* - 最后阶段: 所有批次一起挖 (totalMiningDays - 所有提前天数之和)
|
||||
*/
|
||||
private buildMiningPhases(
|
||||
|
|
@ -359,19 +399,18 @@ export class BatchMiningService {
|
|||
let participatingBatches: number[] = [];
|
||||
let usedDays = 0; // 已分配的天数
|
||||
|
||||
// 按批次顺序添加阶段(提前挖矿阶段)
|
||||
// 每个批次加入后,挖该批次的 preMineDays 天
|
||||
// 按批次顺序添加阶段
|
||||
// 关键:先用当前批次的 preMineDays 创建阶段,然后下一批次加入
|
||||
for (let i = 0; i < sortedBatches.length; i++) {
|
||||
const currentBatch = sortedBatches[i];
|
||||
const currentPreMineDays = batchPreMineDays.get(currentBatch) || 0;
|
||||
|
||||
// 当前批次加入挖矿
|
||||
// 先把当前批次加入参与列表
|
||||
participatingBatches.push(currentBatch);
|
||||
|
||||
// 该阶段持续天数 = 当前批次的提前天数
|
||||
const phaseDays = currentPreMineDays;
|
||||
|
||||
if (phaseDays > 0) {
|
||||
// 该阶段持续天数 = 当前批次的 preMineDays(当前批次比下一批次提前的天数)
|
||||
// 如果 preMineDays > 0,说明当前参与批次要在下一批次加入前独挖这些天
|
||||
if (currentPreMineDays > 0) {
|
||||
// 计算该阶段参与用户的总算力
|
||||
let participatingContribution = new Decimal(0);
|
||||
for (const batch of participatingBatches) {
|
||||
|
|
@ -382,14 +421,14 @@ export class BatchMiningService {
|
|||
phaseNumber: currentPhase,
|
||||
startDate: new Date(),
|
||||
endDate: new Date(),
|
||||
daysInPhase: phaseDays,
|
||||
daysInPhase: currentPreMineDays,
|
||||
participatingContribution,
|
||||
participatingBatches: [...participatingBatches],
|
||||
});
|
||||
|
||||
this.logger.log(`[buildMiningPhases] 阶段${currentPhase}: ${phaseDays}天, 批次[${participatingBatches.join(',')}], 参与算力=${participatingContribution.toFixed(2)}`);
|
||||
this.logger.log(`[buildMiningPhases] 阶段${currentPhase}: ${currentPreMineDays}天, 批次[${participatingBatches.join(',')}], 参与算力=${participatingContribution.toFixed(2)}`);
|
||||
currentPhase++;
|
||||
usedDays += phaseDays;
|
||||
usedDays += currentPreMineDays;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -458,10 +497,29 @@ export class BatchMiningService {
|
|||
batchContributions.set(batchNum, batchTotal);
|
||||
}
|
||||
|
||||
// 计算每个用户的算力
|
||||
// 计算每个用户在每个批次中的算力
|
||||
const userBatchContributions = new Map<string, Decimal>();
|
||||
for (const item of items) {
|
||||
const key = `${item.accountSequence}-${item.batch}`;
|
||||
const contribution = this.calculateUserContribution(item.treeCount);
|
||||
const existing = userBatchContributions.get(key);
|
||||
if (existing) {
|
||||
userBatchContributions.set(key, existing.plus(contribution));
|
||||
} else {
|
||||
userBatchContributions.set(key, contribution);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算每个用户的总算力(用于记录)
|
||||
const userContributions = new Map<string, Decimal>();
|
||||
for (const item of items) {
|
||||
userContributions.set(item.accountSequence, this.calculateUserContribution(item.treeCount));
|
||||
const contribution = this.calculateUserContribution(item.treeCount);
|
||||
const existing = userContributions.get(item.accountSequence);
|
||||
if (existing) {
|
||||
userContributions.set(item.accountSequence, existing.plus(contribution));
|
||||
} else {
|
||||
userContributions.set(item.accountSequence, contribution);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建挖矿阶段
|
||||
|
|
@ -480,10 +538,24 @@ export class BatchMiningService {
|
|||
for (const phase of phases) {
|
||||
const phaseAllocation = dailyAllocation.times(phase.daysInPhase);
|
||||
|
||||
// 用 Set 记录本阶段已处理的用户,避免重复
|
||||
const processedInPhase = new Set<string>();
|
||||
|
||||
for (const item of items) {
|
||||
if (phase.participatingBatches.includes(item.batch)) {
|
||||
const userContribution = userContributions.get(item.accountSequence)!;
|
||||
const ratio = userContribution.dividedBy(phase.participatingContribution);
|
||||
if (phase.participatingBatches.includes(item.batch) && !processedInPhase.has(item.accountSequence)) {
|
||||
processedInPhase.add(item.accountSequence);
|
||||
|
||||
// 计算用户在该阶段参与的批次中的总算力
|
||||
let userPhaseContribution = new Decimal(0);
|
||||
for (const batch of phase.participatingBatches) {
|
||||
const key = `${item.accountSequence}-${batch}`;
|
||||
const batchContrib = userBatchContributions.get(key);
|
||||
if (batchContrib) {
|
||||
userPhaseContribution = userPhaseContribution.plus(batchContrib);
|
||||
}
|
||||
}
|
||||
|
||||
const ratio = userPhaseContribution.dividedBy(phase.participatingContribution);
|
||||
const phaseAmount = phaseAllocation.times(ratio);
|
||||
|
||||
const currentAmount = userAmounts.get(item.accountSequence)!;
|
||||
|
|
|
|||
Loading…
Reference in New Issue