fix(mining): move progress endpoint to MiningController for correct Kong routing

- Add /api/v2/mining/progress endpoint in MiningController
- Update frontend API to call /progress instead of /admin/mining/status
- Kong routes /api/v2/mining/* with strip_path=false, so endpoint must
  be under /mining controller path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-17 00:55:58 -08:00
parent 1aaf32cbb3
commit 1e33ab178d
2 changed files with 74 additions and 4 deletions

View File

@ -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 })

View File

@ -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<MiningStatus> => {
const response = await miningClient.get('/admin/mining/status');
const response = await miningClient.get('/progress');
// 后端返回 { success, data, timestamp }
return response.data.data || response.data;
},