fix(pool-account): 修复 seed.ts 覆盖池余额 + 激活挖矿前校验池余额

1. seed.ts upsert 的 update 子句移除 balance/targetBurn/remainingBurn,
   避免重新部署时将已有池余额清零(导致 BurnConsumer 和
   MiningDistributionConsumer 因余额不足崩溃)
2. activateMining 激活前检查 SHARE_POOL_A/B 余额,任一为0则阻止激活
   并返回 400 错误提示先通过区块链充值

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-03 02:00:59 -08:00
parent 4f5b18be48
commit 46f85b13b0
2 changed files with 29 additions and 4 deletions

View File

@ -152,6 +152,32 @@ export class ConfigController {
@ApiOperation({ summary: '激活挖矿' })
async activateMining(@Req() req: any) {
const miningServiceUrl = this.appConfigService.get<string>('MINING_SERVICE_URL', 'http://localhost:3021');
const walletServiceUrl = this.appConfigService.get<string>('MINING_WALLET_SERVICE_URL', 'http://localhost:3025');
// 安全检查池账户余额不能为0
try {
const [poolARes, poolBRes] = await Promise.all([
fetch(`${walletServiceUrl}/api/v2/pool-accounts/SHARE_POOL_A`),
fetch(`${walletServiceUrl}/api/v2/pool-accounts/SHARE_POOL_B`),
]);
if (poolARes.ok && poolBRes.ok) {
const poolA = await poolARes.json();
const poolB = await poolBRes.json();
const balA = parseFloat(poolA?.data?.balance ?? poolA?.balance ?? '0');
const balB = parseFloat(poolB?.data?.balance ?? poolB?.balance ?? '0');
if (balA <= 0 || balB <= 0) {
const msg = `池账户余额不足无法激活挖矿。积分股池A余额: ${balA}积分股池B余额: ${balB}。请先通过区块链充值。`;
this.logger.warn(msg);
throw new BadRequestException(msg);
}
} else {
this.logger.warn('无法获取池账户余额,跳过安全检查');
}
} catch (error) {
if (error instanceof BadRequestException) throw error;
this.logger.warn('池账户余额检查失败,跳过安全检查', error);
}
try {
const response = await fetch(`${miningServiceUrl}/api/v2/mining/admin/activate`, {
method: 'POST',
@ -163,6 +189,7 @@ export class ConfigController {
this.logger.log(`Mining activated by admin ${req.admin?.id}`);
return result;
} catch (error) {
if (error instanceof BadRequestException) throw error;
this.logger.error('Failed to activate mining', error);
return { success: false, message: 'Failed to activate mining' };
}

View File

@ -66,7 +66,7 @@ async function main() {
// 积分股池A: 100亿 (10,000,000,000) - 用于销毁
// 积分股池B: 200万 (2,000,000) - 用于挖矿分配
// 总计: 100.02亿 (10,002,000,000)
// 直接 upsert 覆盖,不检查是否存在
// upsert: 已存在时只更新元数据name/description不覆盖余额等运营数据
const poolAccounts = [
{
poolType: 'SHARE_POOL_A',
@ -99,10 +99,8 @@ async function main() {
const created = await prisma.poolAccount.upsert({
where: { poolType: pool.poolType as any },
update: {
// 仅更新元数据,绝不覆盖 balance/targetBurn/remainingBurn
name: pool.name,
balance: pool.balance,
targetBurn: pool.targetBurn,
remainingBurn: pool.targetBurn,
description: pool.description,
isActive: true,
},