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

Django中利用post_save信号实现资金回退时的资金到账判断异常问题

Django中利用post_save信号实现资金回退时的资金到账判断异常问题

嘿,我看了你这段代码,一眼就发现问题出在资金到账的判断逻辑上——你现在写的check_if_funds_received = Account.objects.filter(account_balance=amount).exists() 完全不是在判断“接收方有没有收到这笔钱”,而是在找“系统里有没有任何一个账户的余额刚好等于这笔交易的金额”,这逻辑完全跑偏了!

举个例子:如果接收方原来账户有50块,你转100块过去,接收方余额变成150,这时候你的判断会因为找不到余额等于100的账户(除非刚好有其他账户余额是100)而返回False,直接把交易标记为Reversed,还把钱退给发送方,这就导致明明转账成功了却被反转的问题;反过来,如果有个账户本来余额就是100,哪怕你根本没给这个账户转钱,你的判断也会返回True,错误标记交易为Success,这完全乱套了。

我给你梳理下正确的解决方案,分两种情况:


情况一:修正现有信号的判断逻辑

如果你想保留当前“先处理余额再创建交易”的流程,那正确的判断应该是针对当前交易的接收方账户,验证它的余额是否正确接收了这笔钱,而不是乱查所有账户。

另外,先纠正你代码里的两个小错误:

  • receiver_account = account.objects.get(...) 这里的account要大写成Account(模型类名是大写的),不然会报错;
  • 视图里创建Transaction时,account_number=receiver_account 应该改成account_number=receiver_account.account_number(因为Transaction的account_number是BigIntegerField,不能直接存Account对象)。

修正后的信号函数代码:

from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Transaction, Account

@receiver(post_save, sender=Transaction)
def transaction_status(sender, instance, created, **kwargs):
    if created:
        amount = instance.amount
        sender_account = instance.sender.account
        
        try:
            # 拿到当前交易对应的接收方账户
            receiver_account = Account.objects.get(account_number=instance.account_number)
            
            # 这里其实可以直接标记为Success,因为视图里已经完成了余额转账操作
            # 如果非要做验证,可以在Transaction模型里加一个字段记录接收方转账前的余额
            # 比如添加 original_receiver_balance = models.DecimalField(max_digits=12, decimal_places=6)
            # 然后在视图创建Transaction时,把这个字段设为receiver_account.account_balance(加钱之前的余额)
            # 然后这里验证:receiver_account.account_balance == instance.original_receiver_balance + instance.amount
            instance.status = 'Success'
            instance.save()
            
        except Account.DoesNotExist:
            # 如果接收方账户不存在,才执行反转操作
            instance.status = 'Reversed'
            instance.save()
            # 返还资金给发送方
            sender_account.account_balance += amount
            sender_account.save()

情况二:更严谨的流程设计(推荐)

其实你当前“先处理余额再创建交易”的流程有风险:如果处理完余额后,创建Transaction时出现异常,就会导致余额已经变动但没有交易记录的情况。

更合理的流程是:先创建一个状态为Pending的交易,再处理余额操作,成功就把交易改成Success,失败就改成Reversed并回滚余额。这样可以避免状态不一致的问题,而且也不需要依赖post_save信号来判断资金到账。

修改后的视图代码:

from decimal import Decimal
from django.contrib import messages
from .models import Account, Transaction

def create_transfer(request):
    if request.method == 'POST':
        amount = Decimal(request.POST.get('amount'))
        pin = request.POST.get('pin')
        sender_account = Account.objects.get(user=request.user)
        receiver_acc_num = request.POST.get('account_number')
        
        try:
            receiver_account = Account.objects.get(account_number=receiver_acc_num)
        except Account.DoesNotExist:
            messages.error(request, '接收方账户不存在')
            return redirect('Transaction')
        
        # 检查发送方余额是否足够
        if sender_account.account_balance >= amount:
            # 1. 先创建Pending状态的交易记录
            transaction = Transaction.objects.create(
                sender=sender_account.user,
                receiver=receiver_account.user,
                amount=amount,
                pin=pin,
                first_name=receiver_account.first_name,
                last_name=receiver_account.last_name,
                account_number=receiver_acc_num,
                status='Pending'  # 初始状态设为待处理
            )
            
            try:
                # 2. 处理余额转账
                sender_account.account_balance -= amount
                sender_account.save()
                
                receiver_account.account_balance += amount
                receiver_account.save()
                
                # 3. 转账成功,更新交易状态
                transaction.status = 'Success'
                transaction.save()
                messages.success(request, '转账成功!')
                
            except Exception as e:
                # 4. 转账失败,回滚余额并更新交易状态
                transaction.status = 'Reversed'
                transaction.save()
                
                sender_account.account_balance += amount
                sender_account.save()
                messages.error(request, f'转账失败:{str(e)}')
        else:
            messages.error(request, '账户余额不足')
            
        return redirect('Transaction')
    
    return render(request, 'create_transfer.html')

如果这时候你还需要用信号做一些后续操作(比如给用户发通知),可以写一个监听交易状态变化的信号:

@receiver(post_save, sender=Transaction)
def transaction_notification(sender, instance, created, **kwargs):
    if not created:  # 只在交易状态更新时触发
        if instance.status == 'Success':
            # 给发送方和接收方发送转账成功的通知
            pass
        elif instance.status == 'Reversed':
            # 给发送方发送转账失败的通知
            pass

最后再提醒你几个细节:

  • 处理Decimal类型的余额时,要注意精度问题,尽量用Django的DecimalField来存储,避免浮点运算误差;
  • 数据库操作尽量用原子性操作(比如用transaction.atomic()包裹余额修改的代码),避免并发情况下的余额错误;
  • 不要在信号里做太复杂的操作,信号是同步执行的,会影响请求的响应速度。

备注:内容来源于stack exchange,提问作者Bubble

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 08:13:09