From a5f6b23a9503e7027b40da5a477d10e24e5bb33c Mon Sep 17 00:00:00 2001 From: hailin Date: Tue, 3 Feb 2026 02:26:31 -0800 Subject: [PATCH] =?UTF-8?q?feat(pool-account):=20=E6=B1=A0=E8=B4=A6?= =?UTF-8?q?=E6=88=B7=E5=85=85=E5=80=BC=E5=BC=B9=E7=AA=97=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E5=8C=96=E5=85=85=E5=80=BC=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 与做市商管理一致,充值弹窗包含"中心化充值"和"区块链充值"两个 tab。 中心化充值直接调整池账户余额(ADJUSTMENT 类型分类账),无需区块链交易。 变更: - wallet-service: pool-account.service 新增 centralizedDeposit 方法 - wallet-service: pool-account.controller 新增 POST /centralized-deposit - admin-service: pool-account.controller 新增 POST /:walletName/centralized-deposit 代理 - frontend: configs.api + use-configs hook + configs page 充值弹窗 Tabs UI Co-Authored-By: Claude Opus 4.5 --- .../controllers/pool-account.controller.ts | 55 ++++- .../controllers/pool-account.controller.ts | 13 ++ .../services/pool-account.service.ts | 34 +++ .../src/app/(dashboard)/configs/page.tsx | 206 ++++++++++++------ .../src/features/configs/api/configs.api.ts | 9 + .../src/features/configs/hooks/use-configs.ts | 15 ++ 6 files changed, 265 insertions(+), 67 deletions(-) diff --git a/backend/services/mining-admin-service/src/api/controllers/pool-account.controller.ts b/backend/services/mining-admin-service/src/api/controllers/pool-account.controller.ts index 23cac4fb..02e58099 100644 --- a/backend/services/mining-admin-service/src/api/controllers/pool-account.controller.ts +++ b/backend/services/mining-admin-service/src/api/controllers/pool-account.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Post, Body, Param, Logger, BadRequestException, HttpException, HttpStatus } from '@nestjs/common'; +import { Controller, Get, Post, Body, Param, Req, Logger, BadRequestException, HttpException, HttpStatus } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam } from '@nestjs/swagger'; import { ConfigService } from '@nestjs/config'; @@ -200,4 +200,57 @@ export class PoolAccountController { blockNumber: blockchainData.blockNumber, }; } + + @Post(':walletName/centralized-deposit') + @ApiOperation({ summary: '池账户中心化充值(管理员手动调整余额)' }) + @ApiParam({ name: 'walletName', description: '池钱包名称(MPC用户名)' }) + async centralizedDeposit( + @Param('walletName') walletName: string, + @Body() body: { amount: string }, + @Req() req: any, + ) { + const poolInfo = this.walletNameMap[walletName]; + if (!poolInfo) { + throw new BadRequestException(`Unknown wallet name: ${walletName}`); + } + + if (!body.amount || parseFloat(body.amount) <= 0) { + throw new BadRequestException('amount must be greater than 0'); + } + + const adminId = req.admin?.id || 'admin'; + this.logger.log(`[centralized-deposit] ${poolInfo.name}: ${body.amount} by admin ${adminId}`); + + try { + const walletResponse = await fetch( + `${this.walletServiceUrl}/api/v2/pool-accounts/centralized-deposit`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + poolType: poolInfo.walletPoolType, + amount: body.amount, + adminId: String(adminId), + }), + }, + ); + + if (!walletResponse.ok) { + const errResult = await walletResponse.json().catch(() => ({})); + throw new HttpException( + errResult.message || '中心化充值失败', + walletResponse.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + return { success: true, message: `${poolInfo.name}充值成功` }; + } catch (error) { + if (error instanceof HttpException) throw error; + this.logger.error(`[centralized-deposit] Failed: ${error}`); + throw new HttpException( + `中心化充值失败: ${error instanceof Error ? error.message : error}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/backend/services/mining-wallet-service/src/api/controllers/pool-account.controller.ts b/backend/services/mining-wallet-service/src/api/controllers/pool-account.controller.ts index 51bb050c..88345657 100644 --- a/backend/services/mining-wallet-service/src/api/controllers/pool-account.controller.ts +++ b/backend/services/mining-wallet-service/src/api/controllers/pool-account.controller.ts @@ -148,4 +148,17 @@ export class PoolAccountController { ); return { success: true }; } + + @Post('centralized-deposit') + @Public() + @ApiOperation({ summary: '中心化充值(仅限内网调用)' }) + @ApiResponse({ status: 200, description: '充值成功' }) + async centralizedDeposit(@Body() dto: { poolType: PoolAccountType; amount: string; adminId?: string }) { + await this.poolAccountService.centralizedDeposit( + dto.poolType, + new Decimal(dto.amount), + dto.adminId, + ); + return { success: true }; + } } diff --git a/backend/services/mining-wallet-service/src/application/services/pool-account.service.ts b/backend/services/mining-wallet-service/src/application/services/pool-account.service.ts index d85e06a2..1f848ad3 100644 --- a/backend/services/mining-wallet-service/src/application/services/pool-account.service.ts +++ b/backend/services/mining-wallet-service/src/application/services/pool-account.service.ts @@ -575,4 +575,38 @@ export class PoolAccountService { `Blockchain withdraw: ${amount.toFixed(8)} fUSDT from ${poolType} (tx: ${metadata.txHash.slice(0, 10)}...)`, ); } + + /** + * 中心化充值(管理员手动调整余额) + */ + async centralizedDeposit( + poolType: PoolAccountType, + amount: Decimal, + adminId?: string, + ): Promise { + if (amount.isZero() || amount.isNegative()) { + throw new DomainException('充值金额必须大于0', 'INVALID_AMOUNT'); + } + + const poolName = poolType === 'SHARE_POOL_A' ? '100亿销毁池' : '200万挖矿池'; + const memo = `中心化充值, ${poolName}, 金额${amount.toFixed(8)}, 操作员${adminId || 'admin'}`; + + await this.poolAccountRepo.updateBalanceWithTransaction( + poolType, + amount, + { + transactionType: 'ADJUSTMENT', + counterpartyType: 'SYSTEM_ACCOUNT', + referenceType: 'ADMIN_DEPOSIT', + memo, + metadata: { + type: 'centralized_deposit', + amount: amount.toString(), + adminId: adminId || 'admin', + }, + }, + ); + + this.logger.log(`Centralized deposit: ${amount.toFixed(8)} to ${poolType} by ${adminId || 'admin'}`); + } } diff --git a/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx b/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx index e6545b59..29c7bc1c 100644 --- a/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx +++ b/frontend/mining-admin-web/src/app/(dashboard)/configs/page.tsx @@ -3,9 +3,10 @@ import { useState, useEffect } from 'react'; import { QRCodeSVG } from 'qrcode.react'; import { PageHeader } from '@/components/layout/page-header'; -import { useConfigs, useUpdateConfig, useTransferEnabled, useSetTransferEnabled, useMiningStatus, useActivateMining, useDeactivateMining, useP2pTransferFee, useSetP2pTransferFee, usePoolAccountBalance, usePoolAccountBlockchainWithdraw } from '@/features/configs/hooks/use-configs'; +import { useConfigs, useUpdateConfig, useTransferEnabled, useSetTransferEnabled, useMiningStatus, useActivateMining, useDeactivateMining, useP2pTransferFee, useSetP2pTransferFee, usePoolAccountBalance, usePoolAccountBlockchainWithdraw, usePoolAccountCentralizedDeposit } from '@/features/configs/hooks/use-configs'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; @@ -50,17 +51,20 @@ export default function ConfigsPage() { const { data: burnPoolBalance, isLoading: burnPoolLoading } = usePoolAccountBalance(BURN_POOL_WALLET_NAME); const { data: miningPoolBalance, isLoading: miningPoolLoading } = usePoolAccountBalance(MINING_POOL_WALLET_NAME); const blockchainWithdrawMutation = usePoolAccountBlockchainWithdraw(); + const centralizedDepositMutation = usePoolAccountCentralizedDeposit(); const [editingConfig, setEditingConfig] = useState(null); const [editValue, setEditValue] = useState(''); const [feeValue, setFeeValue] = useState(''); const [minAmountValue, setMinAmountValue] = useState(''); - // 池账户区块链提现状态 + // 池账户状态 const [burnWithdrawAddress, setBurnWithdrawAddress] = useState(''); const [burnWithdrawAmount, setBurnWithdrawAmount] = useState(''); const [miningWithdrawAddress, setMiningWithdrawAddress] = useState(''); const [miningWithdrawAmount, setMiningWithdrawAmount] = useState(''); + const [burnDepositAmount, setBurnDepositAmount] = useState(''); + const [miningDepositAmount, setMiningDepositAmount] = useState(''); const [copiedAddress, setCopiedAddress] = useState(null); useEffect(() => { @@ -271,7 +275,7 @@ export default function ConfigsPage() { )}
- {/* 区块链充值 */} + {/* 充值(中心化 + 区块链) */} + + + +
+ 向以下地址转入 fUSDT(积分值代币) +
+ {burnPoolBalance?.walletAddress ? ( +
+
+ +
+
+ +
+ + {burnPoolBalance.walletAddress} + + +
+
+
+ + 转账后系统将自动检测并入账(约需12个区块确认) +
-
-
- - 转账后系统将自动检测并入账(约需12个区块确认) -
- - ) : ( -
- -

钱包地址未配置,请在后端 .env 中配置 BURN_POOL_WALLET_ADDRESS

-
- )} + ) : ( +
+ +

钱包地址未配置

+
+ )} + + @@ -419,7 +458,7 @@ export default function ConfigsPage() { )}
- {/* 区块链充值 */} + {/* 充值(中心化 + 区块链) */} + + + +
+ 向以下地址转入 fUSDT(积分值代币) +
+ {miningPoolBalance?.walletAddress ? ( +
+
+ +
+
+ +
+ + {miningPoolBalance.walletAddress} + + +
+
+
+ + 转账后系统将自动检测并入账(约需12个区块确认) +
-
-
- - 转账后系统将自动检测并入账(约需12个区块确认) -
- - ) : ( -
- -

钱包地址未配置,请在后端 .env 中配置 MINING_POOL_WALLET_ADDRESS

-
- )} + ) : ( +
+ +

钱包地址未配置

+
+ )} + + diff --git a/frontend/mining-admin-web/src/features/configs/api/configs.api.ts b/frontend/mining-admin-web/src/features/configs/api/configs.api.ts index 95634e96..5aa36202 100644 --- a/frontend/mining-admin-web/src/features/configs/api/configs.api.ts +++ b/frontend/mining-admin-web/src/features/configs/api/configs.api.ts @@ -92,4 +92,13 @@ export const configsApi = { const response = await apiClient.post(`/admin/pool-accounts/${walletName}/blockchain-withdraw`, { toAddress, amount }); return response.data.data; }, + + // 中心化充值(管理员手动调整余额) + poolAccountCentralizedDeposit: async (walletName: string, amount: string): Promise<{ + success: boolean; + message: string; + }> => { + const response = await apiClient.post(`/admin/pool-accounts/${walletName}/centralized-deposit`, { amount }); + return response.data.data; + }, }; diff --git a/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts b/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts index 8fae4091..af9d8d9f 100644 --- a/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts +++ b/frontend/mining-admin-web/src/features/configs/hooks/use-configs.ts @@ -151,3 +151,18 @@ export function usePoolAccountBlockchainWithdraw() { }, }); } + +export function usePoolAccountCentralizedDeposit() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ walletName, amount }: { walletName: string; amount: string }) => + configsApi.poolAccountCentralizedDeposit(walletName, amount), + onSuccess: (data) => { + toast({ title: data.message || '充值成功' }); + queryClient.invalidateQueries({ queryKey: ['configs', 'pool-account'] }); + }, + onError: (error: any) => { + toast({ title: '充值失败', description: error.response?.data?.message || '中心化充值失败', variant: 'destructive' }); + }, + }); +}