diff --git a/backend/services/mining-admin-service/src/api/controllers/config.controller.ts b/backend/services/mining-admin-service/src/api/controllers/config.controller.ts index 8d824ac0..0f89a18d 100644 --- a/backend/services/mining-admin-service/src/api/controllers/config.controller.ts +++ b/backend/services/mining-admin-service/src/api/controllers/config.controller.ts @@ -41,21 +41,45 @@ export class ConfigController { @ApiOperation({ summary: '获取挖矿状态' }) async getMiningStatus() { const miningServiceUrl = this.appConfigService.get('MINING_SERVICE_URL', 'http://localhost:3021'); + const contributionServiceUrl = this.appConfigService.get('CONTRIBUTION_SERVICE_URL', 'http://localhost:3020'); + this.logger.log(`Fetching mining status from ${miningServiceUrl}/api/v2/admin/status`); try { - const response = await fetch(`${miningServiceUrl}/api/v2/admin/status`); - if (!response.ok) { - throw new Error(`Failed to fetch mining status: ${response.status}`); + // 并行获取 mining-service 状态和 contribution-service 总算力 + const [miningResponse, contributionResponse] = await Promise.all([ + fetch(`${miningServiceUrl}/api/v2/admin/status`), + fetch(`${contributionServiceUrl}/api/v1/contribution/stats`).catch(() => null), + ]); + + if (!miningResponse.ok) { + throw new Error(`Failed to fetch mining status: ${miningResponse.status}`); } - const result = await response.json(); - this.logger.log(`Mining service response: ${JSON.stringify(result)}`); - if (result.data) { - return result.data; + const miningResult = await miningResponse.json(); + this.logger.log(`Mining service response: ${JSON.stringify(miningResult)}`); + + // 获取 contribution-service 的总有效算力 + let contributionTotal: string | null = null; + if (contributionResponse && contributionResponse.ok) { + const contributionResult = await contributionResponse.json(); + // contribution-service 返回的是 data.totalContribution + contributionTotal = contributionResult.data?.totalContribution || contributionResult.totalContribution || null; } + + const miningData = miningResult.data || miningResult; + const miningTotal = miningData.totalContribution || '0'; + + // 判断算力是否同步完成:两边总算力相等 + const isSynced = contributionTotal !== null && + parseFloat(contributionTotal) > 0 && + Math.abs(parseFloat(miningTotal) - parseFloat(contributionTotal)) < 0.01; + return { - initialized: false, - isActive: false, - error: 'Invalid response from mining service', + ...miningData, + contributionSyncStatus: { + isSynced, + miningTotal, + contributionTotal: contributionTotal || '0', + }, }; } catch (error) { this.logger.error('Failed to get mining status', error); @@ -63,6 +87,11 @@ export class ConfigController { initialized: false, isActive: false, error: `Unable to connect to mining service: ${error.message}`, + contributionSyncStatus: { + isSynced: false, + miningTotal: '0', + contributionTotal: '0', + }, }; } } diff --git a/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx b/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx index 46cba7f7..d6a3ba7a 100644 --- a/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx +++ b/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx @@ -12,7 +12,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from ' import { Label } from '@/components/ui/label'; import { Skeleton } from '@/components/ui/skeleton'; import { Badge } from '@/components/ui/badge'; -import { Pencil, Save, X, Play, Pause, AlertCircle, CheckCircle2 } from 'lucide-react'; +import { Pencil, Save, X, Play, Pause, AlertCircle, CheckCircle2, Loader2 } from 'lucide-react'; import type { SystemConfig } from '@/types/config'; const categoryLabels: Record = { @@ -154,6 +154,20 @@ export default function ConfigsPage() { )} + {/* 算力同步状态提示 */} + {miningStatus.contributionSyncStatus && !miningStatus.contributionSyncStatus.isSynced && ( +
+ +
+

全网算力同步中...

+

+ 已同步: {formatNumber(miningStatus.contributionSyncStatus.miningTotal)} / + 总计: {formatNumber(miningStatus.contributionSyncStatus.contributionTotal)} +

+
+
+ )} +
{miningStatus.isActive ? ( )}
diff --git a/frontend/mining-admin-web/src/features/configs/api/configs.api.ts b/frontend/mining-admin-web/src/features/configs/api/configs.api.ts index 242b1cd5..a9ef021d 100644 --- a/frontend/mining-admin-web/src/features/configs/api/configs.api.ts +++ b/frontend/mining-admin-web/src/features/configs/api/configs.api.ts @@ -1,6 +1,12 @@ import { apiClient } from '@/lib/api/client'; import type { SystemConfig } from '@/types/config'; +export interface ContributionSyncStatus { + isSynced: boolean; + miningTotal: string; + contributionTotal: string; +} + export interface MiningStatus { initialized: boolean; isActive: boolean; @@ -15,6 +21,7 @@ export interface MiningStatus { }; accountCount: number; totalContribution: string; + contributionSyncStatus?: ContributionSyncStatus; error?: string; } diff --git a/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts b/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts index d90631ad..93ec571e 100644 --- a/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts +++ b/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts @@ -52,7 +52,14 @@ export function useMiningStatus() { return useQuery({ queryKey: ['configs', 'mining-status'], queryFn: () => configsApi.getMiningStatus(), - refetchInterval: 30000, + // 当算力未同步完成时,每 3 秒刷新一次;同步完成后每 30 秒刷新一次 + refetchInterval: (query) => { + const data = query.state.data; + if (data?.contributionSyncStatus && !data.contributionSyncStatus.isSynced) { + return 3000; // 3 秒 + } + return 30000; // 30 秒 + }, }); }