diff --git a/backend/services/trading-service/src/api/controllers/transfer.controller.ts b/backend/services/trading-service/src/api/controllers/transfer.controller.ts index bb187bdf..1ac75135 100644 --- a/backend/services/trading-service/src/api/controllers/transfer.controller.ts +++ b/backend/services/trading-service/src/api/controllers/transfer.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Post, Param, Query, Body, Req } from '@nestjs/common'; +import { Controller, Get, Post, Param, Query, Body, Req, UnauthorizedException } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { IsString } from 'class-validator'; import { TransferService } from '../../application/services/transfer.service'; @@ -19,7 +19,7 @@ export class TransferController { async transferIn(@Body() dto: TransferDto, @Req() req: any) { const accountSequence = req.user?.accountSequence; if (!accountSequence) { - throw new Error('Unauthorized'); + throw new UnauthorizedException('未登录'); } return this.transferService.transferIn(accountSequence, dto.amount); @@ -30,7 +30,7 @@ export class TransferController { async transferOut(@Body() dto: TransferDto, @Req() req: any) { const accountSequence = req.user?.accountSequence; if (!accountSequence) { - throw new Error('Unauthorized'); + throw new UnauthorizedException('未登录'); } return this.transferService.transferOut(accountSequence, dto.amount); @@ -47,7 +47,7 @@ export class TransferController { ) { const accountSequence = req.user?.accountSequence; if (!accountSequence) { - throw new Error('Unauthorized'); + throw new UnauthorizedException('未登录'); } return this.transferService.getTransferHistory(accountSequence, page ?? 1, pageSize ?? 50); diff --git a/backend/services/trading-service/src/application/services/transfer.service.ts b/backend/services/trading-service/src/application/services/transfer.service.ts index 6702c35a..17fcc1b4 100644 --- a/backend/services/trading-service/src/application/services/transfer.service.ts +++ b/backend/services/trading-service/src/application/services/transfer.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable, Logger, BadRequestException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository'; import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service'; @@ -27,7 +27,7 @@ export class TransferService { const transferAmount = new Money(amount); if (transferAmount.value.lessThan(this.minTransferAmount)) { - throw new Error(`Minimum transfer amount is ${this.minTransferAmount}`); + throw new BadRequestException(`最低划转数量为 ${this.minTransferAmount}`); } const transferNo = this.generateTransferNo(); @@ -48,7 +48,7 @@ export class TransferService { const response = await this.callMiningServiceTransferOut(accountSequence, amount, transferNo); if (!response.success) { - throw new Error(response.message || 'Mining service transfer failed'); + throw new BadRequestException(response.message || '挖矿服务划转失败'); } // 增加交易账户余额 @@ -94,16 +94,16 @@ export class TransferService { const transferAmount = new Money(amount); if (transferAmount.value.lessThan(this.minTransferAmount)) { - throw new Error(`Minimum transfer amount is ${this.minTransferAmount}`); + throw new BadRequestException(`最低划转数量为 ${this.minTransferAmount}`); } const account = await this.accountRepository.findByAccountSequence(accountSequence); if (!account) { - throw new Error('Trading account not found'); + throw new BadRequestException('交易账户不存在'); } if (account.availableShares.isLessThan(transferAmount)) { - throw new Error('Insufficient available shares'); + throw new BadRequestException('可用积分股不足(部分积分股可能已被 C2C 订单冻结)'); } const transferNo = this.generateTransferNo(); @@ -131,7 +131,7 @@ export class TransferService { // 回滚交易账户余额 account.transferSharesIn(transferAmount, `${transferNo}_rollback`); await this.accountRepository.save(account); - throw new Error(response.message || 'Mining service transfer failed'); + throw new BadRequestException(response.message || '挖矿服务划转失败'); } // 更新划转记录 diff --git a/frontend/mining-app/lib/domain/entities/asset_display.dart b/frontend/mining-app/lib/domain/entities/asset_display.dart index 503d5486..4c0259af 100644 --- a/frontend/mining-app/lib/domain/entities/asset_display.dart +++ b/frontend/mining-app/lib/domain/entities/asset_display.dart @@ -67,6 +67,14 @@ class AssetDisplay extends Equatable { required this.totalSold, }); + /// 交易账户可用积分股(扣除冻结部分)= 总可用积分股 - 挖矿账户可用积分股 + String get tradingAvailableShares { + final totalAvailable = double.tryParse(availableShares) ?? 0; + final miningAvailable = double.tryParse(miningShareBalance) ?? 0; + final result = totalAvailable - miningAvailable; + return result < 0 ? '0' : result.toStringAsFixed(8); + } + /// 总积分股余额 = 可用 + 冻结 String get totalShareBalance { final available = double.tryParse(availableShares) ?? 0; diff --git a/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart b/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart index cb30d06b..817bc0dd 100644 --- a/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart +++ b/frontend/mining-app/lib/presentation/pages/trading/trading_page.dart @@ -402,8 +402,10 @@ class _TradingPageState extends ConsumerState { // 挖矿账户积分股(可划转卖出) final miningShareBalance = asset?.miningShareBalance ?? '0'; - // 交易账户积分股(可直接卖出) + // 交易账户积分股总余额(含冻结,用于显示) final tradingShareBalance = asset?.tradingShareBalance ?? '0'; + // 交易账户可用积分股(扣除冻结,用于划转) + final tradingAvailableShares = asset?.tradingAvailableShares ?? '0'; // 可用积分股(总计:挖矿 + 交易) final availableShares = asset?.availableShares ?? '0'; // 可用积分值(现金) @@ -612,7 +614,7 @@ class _TradingPageState extends ConsumerState { Align( alignment: Alignment.centerRight, child: GestureDetector( - onTap: () => _showTransferDialog(miningShareBalance, tradingShareBalance), + onTap: () => _showTransferDialog(miningShareBalance, tradingAvailableShares), child: const Text( '划转', style: TextStyle( @@ -1626,9 +1628,10 @@ class _TransferBottomSheetState extends ConsumerState<_TransferBottomSheet> { ), ); } else { + final errorMsg = ref.read(tradingNotifierProvider).error ?? '划转失败,请稍后重试'; ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('划转失败,请稍后重试'), + SnackBar( + content: Text(errorMsg), backgroundColor: Colors.red, ), );