fix(mining-admin): 修复仪表板待解锁算力显示为0的问题
- mining-admin-service: 新增 fetchContributionServiceStats() 方法, 从 contribution-service API 获取完整的 pending 数据 - mining-admin-service: 重构 getDetailedContributionStats(),优先 使用 API 数据,失败时回退到本地数据 - mining-service: 修复 publishMiningConfigUpdated 中使用已废弃的 minuteDistribution 字段导致的错误 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e80e672ffe
commit
22702e898b
|
|
@ -220,8 +220,119 @@ export class DashboardService {
|
|||
|
||||
/**
|
||||
* 获取详细算力分解统计(按用户需求)
|
||||
* 优先从 contribution-service API 获取完整数据(包含 pending),
|
||||
* 如果 API 调用失败则回退到本地数据
|
||||
*/
|
||||
private async getDetailedContributionStats() {
|
||||
// 尝试从 contribution-service 获取完整数据
|
||||
const contributionServiceData = await this.fetchContributionServiceStats();
|
||||
if (contributionServiceData) {
|
||||
return contributionServiceData;
|
||||
}
|
||||
|
||||
// 回退:从本地同步数据计算
|
||||
return this.getDetailedContributionStatsFromLocal();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 contribution-service API 获取详细算力统计
|
||||
*/
|
||||
private async fetchContributionServiceStats(): Promise<any | null> {
|
||||
const contributionServiceUrl = this.configService.get<string>(
|
||||
'CONTRIBUTION_SERVICE_URL',
|
||||
'http://localhost:3020',
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${contributionServiceUrl}/api/v2/contribution/stats`);
|
||||
if (!response.ok) {
|
||||
this.logger.warn(`Contribution service returned ${response.status}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const data = result.data || result;
|
||||
|
||||
// 获取系统账户实际值(本地数据)
|
||||
const systemAccounts = await this.prisma.syncedSystemContribution.findMany();
|
||||
let operationActual = new Decimal(0);
|
||||
let provinceActual = new Decimal(0);
|
||||
let cityActual = new Decimal(0);
|
||||
for (const account of systemAccounts) {
|
||||
const balance = new Decimal(account.contributionBalance || 0);
|
||||
if (account.accountType === 'OPERATION') operationActual = operationActual.plus(balance);
|
||||
else if (account.accountType === 'PROVINCE') provinceActual = provinceActual.plus(balance);
|
||||
else if (account.accountType === 'CITY') cityActual = cityActual.plus(balance);
|
||||
}
|
||||
|
||||
return {
|
||||
totalTrees: data.totalTrees || 0,
|
||||
// 理论值
|
||||
networkTotalTheory: data.networkTotalContribution || '0',
|
||||
personalTheory: data.personalTotalContribution || '0',
|
||||
operationTheory: data.operationTotalContribution || '0',
|
||||
provinceTheory: data.provinceTotalContribution || '0',
|
||||
cityTheory: data.cityTotalContribution || '0',
|
||||
levelTheory: data.levelContribution?.total || '0',
|
||||
bonusTheory: data.bonusContribution?.total || '0',
|
||||
|
||||
// 实际值
|
||||
operationActual: operationActual.toString(),
|
||||
provinceActual: provinceActual.toString(),
|
||||
cityActual: cityActual.toString(),
|
||||
|
||||
// 层级算力详情(包含正确的 pending 数据)
|
||||
levelContribution: {
|
||||
total: data.levelContribution?.total || '0',
|
||||
unlocked: data.levelContribution?.unlocked || '0',
|
||||
pending: data.levelContribution?.pending || '0',
|
||||
byTier: {
|
||||
tier1: {
|
||||
unlocked: data.levelContribution?.byTier?.tier1?.unlocked || '0',
|
||||
pending: data.levelContribution?.byTier?.tier1?.pending || '0',
|
||||
},
|
||||
tier2: {
|
||||
unlocked: data.levelContribution?.byTier?.tier2?.unlocked || '0',
|
||||
pending: data.levelContribution?.byTier?.tier2?.pending || '0',
|
||||
},
|
||||
tier3: {
|
||||
unlocked: data.levelContribution?.byTier?.tier3?.unlocked || '0',
|
||||
pending: data.levelContribution?.byTier?.tier3?.pending || '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// 团队奖励算力详情(包含正确的 pending 数据)
|
||||
bonusContribution: {
|
||||
total: data.bonusContribution?.total || '0',
|
||||
unlocked: data.bonusContribution?.unlocked || '0',
|
||||
pending: data.bonusContribution?.pending || '0',
|
||||
byTier: {
|
||||
tier1: {
|
||||
unlocked: data.bonusContribution?.byTier?.tier1?.unlocked || '0',
|
||||
pending: data.bonusContribution?.byTier?.tier1?.pending || '0',
|
||||
},
|
||||
tier2: {
|
||||
unlocked: data.bonusContribution?.byTier?.tier2?.unlocked || '0',
|
||||
pending: data.bonusContribution?.byTier?.tier2?.pending || '0',
|
||||
},
|
||||
tier3: {
|
||||
unlocked: data.bonusContribution?.byTier?.tier3?.unlocked || '0',
|
||||
pending: data.bonusContribution?.byTier?.tier3?.pending || '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to fetch contribution service stats: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从本地同步数据计算详细算力统计(回退方案)
|
||||
*/
|
||||
private async getDetailedContributionStatsFromLocal() {
|
||||
// 获取总树数
|
||||
const adoptionStats = await this.prisma.syncedAdoption.aggregate({
|
||||
where: { status: 'MINING_ENABLED' },
|
||||
|
|
@ -288,7 +399,7 @@ export class DashboardService {
|
|||
const levelTheory = networkTotal.mul(RATE_LEVEL_TOTAL);
|
||||
const bonusTheory = networkTotal.mul(RATE_BONUS_TOTAL);
|
||||
|
||||
// 计算未解锁(理论 - 已解锁)
|
||||
// 计算未解锁(理论 - 已解锁)- 仅用于总数,各档位无法获取
|
||||
const levelPending = levelTheory.minus(levelUnlocked).greaterThan(0)
|
||||
? levelTheory.minus(levelUnlocked)
|
||||
: new Decimal(0);
|
||||
|
|
@ -323,27 +434,27 @@ export class DashboardService {
|
|||
provinceActual: provinceActual.toString(),
|
||||
cityActual: cityActual.toString(),
|
||||
|
||||
// 层级算力详情
|
||||
// 层级算力详情(本地无法获取各档位 pending,显示为 N/A)
|
||||
levelContribution: {
|
||||
total: levelTheory.toString(),
|
||||
unlocked: levelUnlocked.toString(),
|
||||
pending: levelPending.toString(),
|
||||
byTier: {
|
||||
tier1: { unlocked: levelTier1.toString(), pending: '0' },
|
||||
tier2: { unlocked: levelTier2.toString(), pending: '0' },
|
||||
tier3: { unlocked: levelTier3.toString(), pending: '0' },
|
||||
tier1: { unlocked: levelTier1.toString(), pending: 'N/A' },
|
||||
tier2: { unlocked: levelTier2.toString(), pending: 'N/A' },
|
||||
tier3: { unlocked: levelTier3.toString(), pending: 'N/A' },
|
||||
},
|
||||
},
|
||||
|
||||
// 团队奖励算力详情
|
||||
// 团队奖励算力详情(本地无法获取各档位 pending,显示为 N/A)
|
||||
bonusContribution: {
|
||||
total: bonusTheory.toString(),
|
||||
unlocked: bonusUnlocked.toString(),
|
||||
pending: bonusPending.toString(),
|
||||
byTier: {
|
||||
tier1: { unlocked: bonusTier1.toString(), pending: '0' },
|
||||
tier2: { unlocked: bonusTier2.toString(), pending: '0' },
|
||||
tier3: { unlocked: bonusTier3.toString(), pending: '0' },
|
||||
tier1: { unlocked: bonusTier1.toString(), pending: 'N/A' },
|
||||
tier2: { unlocked: bonusTier2.toString(), pending: 'N/A' },
|
||||
tier3: { unlocked: bonusTier3.toString(), pending: 'N/A' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -726,7 +726,7 @@ export class MiningDistributionService {
|
|||
remainingDistribution: newRemaining.toString(),
|
||||
halvingPeriodYears: config.halvingPeriodYears,
|
||||
currentEra: config.currentEra,
|
||||
minuteDistribution: config.minuteDistribution.toString(),
|
||||
secondDistribution: config.secondDistribution.toString(),
|
||||
isActive: config.isActive,
|
||||
activatedAt: config.activatedAt?.toISOString(),
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue