feat(mining-admin): 算力同步完成前禁用激活挖矿按钮
- 后端:getMiningStatus 接口并行获取 contribution-service 总算力,对比两边是否一致 - 前端:未同步时显示"全网算力同步中..."提示,禁用激活按钮 - 前端:同步中每 3 秒刷新状态,同步完成后恢复 30 秒刷新 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
72b3b44d37
commit
7c00c900a0
|
|
@ -41,21 +41,45 @@ export class ConfigController {
|
|||
@ApiOperation({ summary: '获取挖矿状态' })
|
||||
async getMiningStatus() {
|
||||
const miningServiceUrl = this.appConfigService.get<string>('MINING_SERVICE_URL', 'http://localhost:3021');
|
||||
const contributionServiceUrl = this.appConfigService.get<string>('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',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, string> = {
|
||||
|
|
@ -154,6 +154,20 @@ export default function ConfigsPage() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* 算力同步状态提示 */}
|
||||
{miningStatus.contributionSyncStatus && !miningStatus.contributionSyncStatus.isSynced && (
|
||||
<div className="flex items-center gap-2 p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
|
||||
<Loader2 className="h-4 w-4 animate-spin text-yellow-600" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-yellow-800 dark:text-yellow-200">全网算力同步中...</p>
|
||||
<p className="text-xs text-yellow-600 dark:text-yellow-400">
|
||||
已同步: {formatNumber(miningStatus.contributionSyncStatus.miningTotal)} /
|
||||
总计: {formatNumber(miningStatus.contributionSyncStatus.contributionTotal)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end pt-4 border-t">
|
||||
{miningStatus.isActive ? (
|
||||
<Button
|
||||
|
|
@ -167,10 +181,19 @@ export default function ConfigsPage() {
|
|||
) : (
|
||||
<Button
|
||||
onClick={() => activateMining.mutate()}
|
||||
disabled={activateMining.isPending}
|
||||
disabled={activateMining.isPending || (miningStatus.contributionSyncStatus && !miningStatus.contributionSyncStatus.isSynced)}
|
||||
>
|
||||
<Play className="h-4 w-4 mr-2" />
|
||||
{activateMining.isPending ? '激活中...' : '激活挖矿'}
|
||||
{miningStatus.contributionSyncStatus && !miningStatus.contributionSyncStatus.isSynced ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
全网算力同步中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Play className="h-4 w-4 mr-2" />
|
||||
{activateMining.isPending ? '激活中...' : '激活挖矿'}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 秒
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue