网络延迟导致供货商结算多次支付

我们的采购人员使用微信小程序采购的简化流程如下:
1、录入采购单;
2、点击支付按钮给供货商打款。
采购人员反馈当天点击付款后,收到支付失败的消息,但供货商收到了两笔款。
查看生产日志,发现同一秒调用两次支付请求:

2019-01-07 19:13:05|100.116.251.32|role=apiAdmin|id=120|username=18974060489|name=胡建军|/api/admin/purchase/payment.jhtml|success|-|2532
2019-01-07 19:13:05|100.116.251.12|role=apiAdmin|id=120|username=18974060489|name=胡建军|/api/admin/purchase/payment.jhtml|success|-|2337

后端支付的简化逻辑:
在这里插入图片描述
当两个线程同时进行上述流程时,首先汇集采购记录表数据,创建支付记录数据(sn不同);调用微信支付的支付接口,此时都能付款成功并都返回支付结果;将支付成功的结果回写支付记录表,再修改采购记录表数据的采购状态。此次发生异常,同一条记录不能被修改两次采购状态,整个事务回滚,导致并没有创建支付记录但确实调用微信支付付款成功了。

上述流程有几点改进:
1、微信小程序端支付按钮设置锁,只允许点击一次;
2、支付整个流程同步化(生产环境调用支付的频次并不高,付款整个流程测试时间大约2秒,采用最粗暴的方法,整个代码块加锁)。

使用如下单元测试测试支付:

    @Test
    public void testpayment() {
        Purchase purchase = purchaseService.find(59503L);
        Admin admin = adminService.find(3L);
        // 单线程示例
        // Message msg =
        // purchaseOrderService.payment(Lists.newArrayList(purchase), admin);
        // System.err.println(msg);

        System.err.println(Thread.currentThread().getName());
        // 多线程示例
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 3; ++i) {
            Task task = new Task(purchase, admin);
            FutureTask<Message> futureTask = new FutureTask<Message>(task);
            executor.submit(futureTask);

            try {
                System.out.println("task运行结果" + futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executor.shutdown();

        System.out.println("所有任务执行完毕");

    }

    class Task implements Callable<Message> {

        private Purchase purchase;
        private Admin admin;

        public Task() {
            super();
        }

        public Task(Purchase purchase, Admin admin) {
            super();
            this.purchase = purchase;
            this.admin = admin;
        }

        @Override
        public Message call() throws Exception {
            System.err.println("子线程在进行计算");
            Message msg = purchaseOrderService.payment(Lists.newArrayList(purchase), admin);
            System.err.println(msg);
            return msg;
        }
    }

调用微信的截图发现不加synchronized,三个线程会产生三次支付。
在这里插入图片描述
加synchronized,三个线程只会产生一次支付,测试日志如下,另外两个线程阻塞住,在第一个线程支付成功后,再去汇集采购记录表数据,但此时采购的状态已经被改变不会再汇集,报“请选择同一个供货商,且状态为等待主管审核或会计审核通过!”错误。

success
task运行结果success
子线程在进行计算
请选择同一个供货商,且状态为等待主管审核或会计审核通过!
task运行结果请选择同一个供货商,且状态为等待主管审核或会计审核通过!
子线程在进行计算
请选择同一个供货商,且状态为等待主管审核或会计审核通过!
task运行结果请选择同一个供货商,且状态为等待主管审核或会计审核通过!
所有任务执行完毕

知识点
1、synchronized

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值