From 8e63547a3efba36ed71b0e449ef406d85f9344f6 Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 31 Jan 2026 06:24:35 -0800 Subject: [PATCH] =?UTF-8?q?fix(transfer):=20=E4=BF=AE=E5=A4=8D=E7=A7=AF?= =?UTF-8?q?=E5=88=86=E8=82=A1=E5=88=92=E8=BD=AC=E5=88=B0=E5=88=86=E9=85=8D?= =?UTF-8?q?=E8=B4=A6=E6=88=B7=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: - transfer.service.ts: Error → BadRequestException,业务错误返回HTTP 400而非500 - transfer.controller.ts: Error → UnauthorizedException,正确返回HTTP 401 - 错误信息改为中文:余额不足、账户不存在等提示更明确 前端: - asset_display.dart: 新增 tradingAvailableShares 计算属性(总可用 - 挖矿可用) - trading_page.dart: 划转弹窗显示可用余额(扣除冻结)而非总余额 - trading_page.dart: 划转失败时显示后端具体错误信息而非通用提示 Co-Authored-By: Claude Opus 4.5 --- .../src/api/controllers/transfer.controller.ts | 8 ++++---- .../src/application/services/transfer.service.ts | 14 +++++++------- .../lib/domain/entities/asset_display.dart | 8 ++++++++ .../presentation/pages/trading/trading_page.dart | 11 +++++++---- 4 files changed, 26 insertions(+), 15 deletions(-) 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, ), );