You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
  }
}

关键修改说明

  1. 正确获取用户实体:使用queryRunner.manager.findOne从数据库查询真实的UserEntity实例,确保后续修改能同步到数据库
  2. 添加校验逻辑:验证用户存在性和余额充足性,避免无效操作和异常
  3. 移除冗余的req/res传递:直接使用TransactionDto传递参数,符合NestJS的DTO设计规范
  4. 规范事务操作:全程使用queryRunner的manager执行查询和保存,保证事务的原子性
  5. 错误处理优化:抛出NestJS内置的HTTP异常,自动处理响应状态码和信息

内容的提问来源于stack exchange,提问作者PatrickAngrezani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 03:55:30