diff --git a/backend/services/mining-service/src/api/controllers/mining.controller.ts b/backend/services/mining-service/src/api/controllers/mining.controller.ts index 42311cbf..a5b4cedd 100644 --- a/backend/services/mining-service/src/api/controllers/mining.controller.ts +++ b/backend/services/mining-service/src/api/controllers/mining.controller.ts @@ -3,6 +3,7 @@ import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/ import { GetMiningAccountQuery } from '../../application/queries/get-mining-account.query'; import { GetMiningStatsQuery } from '../../application/queries/get-mining-stats.query'; import { Public } from '../../shared/guards/jwt-auth.guard'; +import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service'; @ApiTags('Mining') @Controller('mining') @@ -11,6 +12,7 @@ export class MiningController { constructor( private readonly getAccountQuery: GetMiningAccountQuery, private readonly getStatsQuery: GetMiningStatsQuery, + private readonly prisma: PrismaService, ) {} @Get('stats') @@ -20,6 +22,72 @@ export class MiningController { return this.getStatsQuery.execute(); } + @Get('progress') + @ApiOperation({ summary: '获取挖矿进度状态(类似销毁进度)' }) + @ApiResponse({ status: 200, description: '挖矿进度状态' }) + async getMiningProgress() { + 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 totalDistributedDecimal = + Number(userMiningStats._sum.totalMined || 0) + + Number(systemMiningStats._sum.totalMined || 0); + + // 查询最后分配时间 + 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, + }; + } + @Get('ranking') @ApiOperation({ summary: '获取挖矿排行榜' }) @ApiQuery({ name: 'limit', required: false, description: '返回数量限制', type: Number }) diff --git a/frontend/mining-admin-web/src/features/mining/api/mining.api.ts b/frontend/mining-admin-web/src/features/mining/api/mining.api.ts index a0b95b13..58dbf00a 100644 --- a/frontend/mining-admin-web/src/features/mining/api/mining.api.ts +++ b/frontend/mining-admin-web/src/features/mining/api/mining.api.ts @@ -1,8 +1,10 @@ import axios from 'axios'; -// Mining API 需要独立的 baseURL,因为它走不同的路由 -// 生产环境: 通过 Next.js rewrite /api/mining/* -> Kong -> mining-service -// 开发环境: 通过 Next.js rewrite /api/mining/* -> mining-service +// Mining Service API 客户端 +// 生产环境: /api/mining/* -> Kong /api/v2/mining/* -> mining-service /api/v2/* +// 开发环境: /api/mining/* -> mining-service /api/v2/* +// 注意: Kong 的路由是 /api/v2/mining/* 映射到 mining-service 的 /api/v2/* +// 所以前端调用 /api/mining/admin/mining/status 会被转发到 mining-service 的 /api/v2/admin/mining/status const miningBaseURL = '/api/mining'; const miningClient = axios.create({ @@ -52,7 +54,7 @@ export interface MiningStatus { export const miningApi = { // 获取挖矿进度状态 getMiningStatus: async (): Promise => { - const response = await miningClient.get('/admin/mining/status'); + const response = await miningClient.get('/progress'); // 后端返回 { success, data, timestamp } return response.data.data || response.data; },