背景描述
这是一个开源分销商城中出现的问题。分销员在分销一件商品后,系统会基于商品的金额计算分销佣金,并累积到分销员的分销账户上。如果发生商品退款,用户的钱要原路返回,并且要从分销员的分销账户中扣除该商品产生的分销佣金。这是一个简单又通用的业务流程。
文章绝对原创,没有剽窃行为,大家放心食用。若要转载,烦请标明出处,谢谢。
具体退款操作,大概流程如下:
- 退款审核通过后会生成一条退款流水数据插入数据库。
- 更新订单和售后单的状态。
- 调用微信退款接口。
- 分销佣金扣除
分销佣金的扣除是通过RocketMQ发送售后单状态变化
消息,consumer衔接后续分销佣金扣除的操作。
以上操作执行完后就到了consumer消费的阶段了,消费者会衔接上面的流程,根据售后单SN号查询退款流水。consumer会尝试通过SN号查询退款流水,查到了就执行分销佣金的扣减,查不到就结束。
没错,没错,退款流水没查到,导致分销佣金扣减的流程没走。订单退款了,分销佣金没有扣减,这是相当致命的问题。
什么原因导致的呢?
我怀疑是consumer在退款流水生成前执行了扣除操作。
作者否认了我的猜想。他的理由是生成退款流水和发送MQ是同步操作,没有生成退款流水是不可能发送MQ的,不发送MQ消息也不可能出现consumer消费的情况。以下是审核通过操作的部分代码截图:
由于版权问题,覆盖了不相关的代码
最终确认了问题原因,是跟事务相关,原因如下:
- 审核方法加了
@Transactional(rollbackFor = Exception.class)
,也就是说这个方法是有事务的。事务范围内的所有数据库操作在方法结束后不报错的情况下一起提交。 - 在审核方法中还包含了
rocketMQTemplate.asyncSend(xxxx)
的调用,也就是说发送消息的方法也包含在事务范围内,但不受事务的约束。执行到asyncSend
即可将数据发送出去。 - 如果在事务还没有来得及提交,刚好consumer开始消费
售后单状态变化
消息,那么consumer将查询不到刚插入还没有提交的退款流水,就无法执行分销佣金扣减。问题就出现了。
解决方案
扣除佣金的操作改为同步调用
退款操作涉及两个比较关键的操作,退款和分销账户佣金扣除。退款是调用微信的退款接口,扣除是通过MQ做的异步操作。再没有影响的情况下,把佣金扣除的操作改为同步,去除中间层的MQ。保障扣除的时候能查到退款流水。
总结
问题比较低级,但不仔细看的情况下也很难发现。解决方案也比较粗暴,由于时间问题没有深入研究更好的解决方案。若小伙伴有更好的解决方案,可以私信我或在评论区沟通。