From 22702e898bf4b0d6afe100d22092c885e0aee9bc Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 17 Jan 2026 00:09:40 -0800 Subject: [PATCH] =?UTF-8?q?fix(mining-admin):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BB=AA=E8=A1=A8=E6=9D=BF=E5=BE=85=E8=A7=A3=E9=94=81=E7=AE=97?= =?UTF-8?q?=E5=8A=9B=E6=98=BE=E7=A4=BA=E4=B8=BA0=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mining-admin-service: 新增 fetchContributionServiceStats() 方法, 从 contribution-service API 获取完整的 pending 数据 - mining-admin-service: 重构 getDetailedContributionStats(),优先 使用 API 数据,失败时回退到本地数据 - mining-service: 修复 publishMiningConfigUpdated 中使用已废弃的 minuteDistribution 字段导致的错误 Co-Authored-By: Claude Opus 4.5 --- .../application/services/dashboard.service.ts | 129 ++++++++++++++++-- .../services/mining-distribution.service.ts | 2 +- 2 files changed, 121 insertions(+), 10 deletions(-) diff --git a/backend/services/mining-admin-service/src/application/services/dashboard.service.ts b/backend/services/mining-admin-service/src/application/services/dashboard.service.ts index b6a796fe..0d36b49d 100644 --- a/backend/services/mining-admin-service/src/application/services/dashboard.service.ts +++ b/backend/services/mining-admin-service/src/application/services/dashboard.service.ts @@ -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 { + const contributionServiceUrl = this.configService.get( + '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' }, }, }, }; diff --git a/backend/services/mining-service/src/application/services/mining-distribution.service.ts b/backend/services/mining-service/src/application/services/mining-distribution.service.ts index 28069b61..09baaeca 100644 --- a/backend/services/mining-service/src/application/services/mining-distribution.service.ts +++ b/backend/services/mining-service/src/application/services/mining-distribution.service.ts @@ -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(), },