diff --git a/backend/services/mining-service/src/api/controllers/admin.controller.ts b/backend/services/mining-service/src/api/controllers/admin.controller.ts
index 4cfe80b2..a3165c1c 100644
--- a/backend/services/mining-service/src/api/controllers/admin.controller.ts
+++ b/backend/services/mining-service/src/api/controllers/admin.controller.ts
@@ -176,4 +176,77 @@ export class AdminController {
total: accounts.length,
};
}
+
+ @Get('mining/status')
+ @Public()
+ @ApiOperation({ summary: '获取挖矿进度状态(类似销毁进度)' })
+ async getMiningStatus() {
+ const config = await this.prisma.miningConfig.findFirst();
+
+ if (!config) {
+ return {
+ totalDistributed: '0',
+ distributionPool: '0',
+ remainingDistribution: '0',
+ miningProgress: '0',
+ minuteDistribution: '0',
+ remainingMinutes: 0,
+ lastMiningMinute: null,
+ isActive: false,
+ currentEra: 0,
+ };
+ }
+
+ // 获取用户已分配总量
+ const userMiningStats = await this.prisma.miningAccount.aggregate({
+ _sum: { totalMined: true },
+ });
+
+ // 获取系统账户已分配总量
+ const systemMiningStats = await this.prisma.systemMiningAccount.aggregate({
+ _sum: { totalMined: true },
+ });
+
+ // 总分配量 = 用户分配 + 系统账户分配
+ const userDistributed = userMiningStats._sum.totalMined || 0;
+ const systemDistributed = systemMiningStats._sum.totalMined || 0;
+ const totalDistributedDecimal = Number(userDistributed) + Number(systemDistributed);
+
+ // 获取最后分配时间(从分钟统计表查询)
+ const lastMinuteStat = await this.prisma.minuteMiningStat.findFirst({
+ orderBy: { minute: 'desc' },
+ select: { minute: true },
+ });
+
+ // 计算每分钟分配量 (秒分配量 * 60)
+ const secondDistribution = Number(config.secondDistribution || 0);
+ const minuteDistribution = secondDistribution * 60;
+
+ // 计算剩余分配量
+ const distributionPool = Number(config.distributionPool || 0);
+ const remainingDistribution = Number(config.remainingDistribution || 0);
+
+ // 计算进度百分比
+ const distributed = distributionPool - remainingDistribution;
+ const miningProgress = distributionPool > 0
+ ? (distributed / distributionPool) * 100
+ : 0;
+
+ // 计算剩余分钟数
+ const remainingMinutes = minuteDistribution > 0
+ ? Math.ceil(remainingDistribution / minuteDistribution)
+ : 0;
+
+ return {
+ totalDistributed: totalDistributedDecimal.toFixed(8),
+ distributionPool: distributionPool.toFixed(8),
+ remainingDistribution: remainingDistribution.toFixed(8),
+ miningProgress: miningProgress.toFixed(4),
+ minuteDistribution: minuteDistribution.toFixed(8),
+ remainingMinutes,
+ lastMiningMinute: lastMinuteStat?.minute || null,
+ isActive: config.isActive,
+ currentEra: config.currentEra,
+ };
+ }
}
diff --git a/frontend/mining-admin-web/next.config.js b/frontend/mining-admin-web/next.config.js
index 2a60a2fa..ea43d815 100644
--- a/frontend/mining-admin-web/next.config.js
+++ b/frontend/mining-admin-web/next.config.js
@@ -9,23 +9,30 @@ const nextConfig = {
const apiGatewayUrl = process.env.API_GATEWAY_URL || 'https://rwaapi.szaiai.com';
const miningAdminUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3023';
const tradingServiceUrl = process.env.TRADING_SERVICE_URL || 'http://localhost:3022';
+ const miningServiceUrl = process.env.MINING_SERVICE_URL || 'http://localhost:3021';
// 检查是否是生产环境(使用 Kong 网关)
const isProduction = process.env.NODE_ENV === 'production';
// 移除末尾可能存在的路径避免重复
- const cleanMiningUrl = miningAdminUrl.replace(/\/api\/v2.*$/, '');
+ const cleanMiningAdminUrl = miningAdminUrl.replace(/\/api\/v2.*$/, '');
const cleanTradingUrl = tradingServiceUrl.replace(/\/api\/v2.*$/, '');
+ const cleanMiningUrl = miningServiceUrl.replace(/\/api\/v2.*$/, '');
if (isProduction) {
// 生产环境:通过 Kong 网关
// /api/trading/* -> Kong -> trading-service
+ // /api/mining/* -> Kong -> mining-service
// /api/* -> Kong -> mining-admin-service
return [
{
source: '/api/trading/:path*',
destination: `${apiGatewayUrl}/api/v2/trading/:path*`,
},
+ {
+ source: '/api/mining/:path*',
+ destination: `${apiGatewayUrl}/api/v2/mining/:path*`,
+ },
{
source: '/api/:path*',
destination: `${apiGatewayUrl}/api/v2/mining-admin/:path*`,
@@ -39,9 +46,13 @@ const nextConfig = {
destination: `${cleanTradingUrl}/api/v2/:path*`,
},
{
- source: '/api/:path*',
+ source: '/api/mining/:path*',
destination: `${cleanMiningUrl}/api/v2/:path*`,
},
+ {
+ source: '/api/:path*',
+ destination: `${cleanMiningAdminUrl}/api/v2/:path*`,
+ },
];
}
},
diff --git a/frontend/mining-admin-web/src/app/(dashboard)/dashboard/page.tsx b/frontend/mining-admin-web/src/app/(dashboard)/dashboard/page.tsx
index 379cf841..5e336e9e 100644
--- a/frontend/mining-admin-web/src/app/(dashboard)/dashboard/page.tsx
+++ b/frontend/mining-admin-web/src/app/(dashboard)/dashboard/page.tsx
@@ -5,6 +5,7 @@ import { StatsCards } from '@/features/dashboard/components/stats-cards';
import { RealtimePanel } from '@/features/dashboard/components/realtime-panel';
import { PriceOverview } from '@/features/dashboard/components/price-overview';
import { ContributionBreakdown } from '@/features/dashboard/components/contribution-breakdown';
+import { MiningProgress } from '@/features/mining/components/mining-progress';
export default function DashboardPage() {
return (
@@ -13,6 +14,11 @@ export default function DashboardPage() {
无法获取挖矿数据
+已分配
+{formatNumber(miningStatus.totalDistributed, 8)}
+剩余分配池
+{formatNumber(miningStatus.remainingDistribution, 8)}
+剩余分钟
+{miningStatus.remainingMinutes?.toLocaleString()}
+每分钟分配
+{formatNumber(miningStatus.minuteDistribution, 8)}
++ 最后分配: {new Date(miningStatus.lastMiningMinute).toLocaleString()} +
+ )} +