TypeORM事务异常:用户间加密货币转账无法修改CryptoWallet
问题分析与修复方案
你的代码核心问题在于没有正确从数据库获取用户实体,直接操作了请求里的ID字符串,导致钱包余额修改完全无效。以下是具体问题和修复步骤:
核心问题点
fromUser和toUser只是从req.body获取的ID字符串,不是数据库查询到的UserEntity实例,修改CryptoWallet不会同步到数据库- 错误地对字符串执行
await操作,完全无效 - 未校验转出用户的余额是否充足
- 未验证用户是否存在
- 冗余传递
Request/Response对象,不符合NestJS的最佳实践
修复后的代码
TransactionsService
import { TransactionDto } from './dto.transactions/transaction.dto'; import { UserService } from 'src/user/user.service'; import { DataSource, Repository } from 'typeorm'; import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { UserEntity } from 'src/user/entities/user.entity'; @Injectable() export class TransactionsService { constructor( @InjectRepository(UserEntity) private readonly userRepository: Repository<UserEntity>, private readonly userService: UserService, private readonly dataSource: DataSource, ) {} async cryptoTransactions(transactionDto: TransactionDto): Promise<UserEntity> { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.startTransaction(); const { fromId, toId, quantityTransfered } = transactionDto; try { // 从数据库查询真实的用户实体(使用queryRunner保证事务一致性) const fromUser = await queryRunner.manager.findOne(UserEntity, { where: { id: fromId } }); const toUser = await queryRunner.manager.findOne(UserEntity, { where: { id: toId } }); // 验证用户是否存在 if (!fromUser) { throw new NotFoundException(`转出用户ID ${fromId} 不存在`); } if (!toUser) { throw new NotFoundException(`转入用户ID ${toId} 不存在`); } // 校验转出余额是否充足 if (fromUser.CryptoWallet < quantityTransfered) { throw new BadRequestException('转出余额不足'); } // 修改钱包余额 fromUser.CryptoWallet -= quantityTransfered; toUser.CryptoWallet += quantityTransfered; // 保存修改(使用queryRunner的manager确保在事务中执行) await queryRunner.manager.save(fromUser); await queryRunner.manager.save(toUser); await queryRunner.commitTransaction(); return fromUser; } catch (err) { await queryRunner.rollbackTransaction(); // 重新抛出错误,让NestJS处理响应 throw err; } finally { await queryRunner.release(); } } }
TransactionsController
import { TransactionsService } from './transactions.service'; import { Body, Controller, Post } from '@nestjs/common'; import { TransactionDto } from './dto.transactions/transaction.dto'; @Controller('transactions') export class TransactionsController { constructor(private readonly transactionsService: TransactionsService) {} @Post() async cryptoTransactions(@Body() transactionDto: TransactionDto) { return this.transactionsService.cryptoTransactions(transactionDto); } }
关键修改说明
- 正确获取用户实体:使用
queryRunner.manager.findOne从数据库查询真实的UserEntity实例,确保后续修改能同步到数据库 - 添加校验逻辑:验证用户存在性和余额充足性,避免无效操作和异常
- 移除冗余的req/res传递:直接使用
TransactionDto传递参数,符合NestJS的DTO设计规范 - 规范事务操作:全程使用
queryRunner的manager执行查询和保存,保证事务的原子性 - 错误处理优化:抛出NestJS内置的HTTP异常,自动处理响应状态码和信息
内容的提问来源于stack exchange,提问作者PatrickAngrezani
相关产品推荐
相关产品推荐

