我有一篇博文是教大家怎么对接微信小程序对接支付的,然后看到有位答主是这么说:

createOrder有并发问题,同一个用户快速点击下单时可能发出了两次下单请求,会造成在后台生成两行预支付订单。

目录

一、前言

二、解决方案

三、开始开发

3.1 调试环境

3.2 引入依赖

3.3 application.yml文件

 3.4 可以自定义配置类(可选)

3.5 使用Redisson

3.6 如果任务一直没处理完怎么办?

四、有问题留言


一、前言

首先非常感谢这位答主的疑惑,在这里我问大家解答下如何防止用户重复提交订单。

原文链接:【微信支付】【java】Springboot对接开发微信支付_wechatpay-java-CSDN博客

二、解决方案

这里使用的是redis分布式锁来解决这个问题,Redisson。其他的不过多介绍,详情可以去官网查看。

三、开始开发

3.1 调试环境

开发语言:Java,框架:SpringBoot,仓库:Maven 3.6.3 及以上,开发工具:Idea

3.2 引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.20.0</version> <!-- 这里版本号可根据实际需求更新 -->
</dependency>

3.3 application.yml文件

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: your_password

如果是集群,对于集群模式、哨兵模式等,配置会稍有不同:

spring:
  redis:
    cluster:
      nodes:
        - 127.0.0.1:7000
        - 127.0.0.1:7001
        - 127.0.0.1:7002
        - 127.0.0.1:7003
        - 127.0.0.1:7004
        - 127.0.0.1:7005
    password: your_password

 3.4 可以自定义配置类(可选)

如果你不想用yml文件配置,也可以使用自定义配置文件类来实现(二选一)。

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        // 这里以单机模式为例,可根据实际需求修改为集群、哨兵等模式
        config.useSingleServer()
             .setAddress("redis://127.0.0.1:6379")
             .setPassword("your_password");

        return Redisson.create(config);
    }
}

3.5 使用Redisson

使用方式有多种,我们这里直接用@Autowired 来进行注入。

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LockService {

    @Autowired
    private RedissonClient redissonClient;

    public void doSomethingWithLock() {
        RLock lock = redissonClient.getLock("myLock");
        try {
            // 尝试获取锁,等待时间10秒,锁自动释放时间20秒
            boolean isLocked = lock.tryLock(10, 20, TimeUnit.SECONDS);
            if (isLocked) {
                // 获取到锁后执行的业务逻辑
                System.out.println("获取到锁,执行业务逻辑");
            } else {
                System.out.println("未获取到锁");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 无论是否获取到锁,最终都要释放锁
            lock.unlock();
        }
    }
}

上面是使用案例,那么问题回到微信支付这块,如何防止用户重复提交呢?

一般支付流程是:选择商品 ->进行下单 ->生成订单信息 ->然后支付。那么在提交支付的接口中是有用户id和订单orderId的,其他参数比如优惠券id我们暂时不考虑。那么按照这种情况来。

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class wxService {
    @Autowired
    private final RedissonClient redissonClient;


    public Boolean createOrder(Long userId,Long orderId) {
        //首先定义锁key
        String orderKey="wxService.createOrder::"+"userId:"+userId+",orderId:"+orderId;
        RLock lock = redissonClient.getLock(orderKey);
        try {
            // 尝试获取锁,等待时间10秒,锁自动释放时间30秒
            boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (!isLocked) {
                //相同的key再次加锁的时候,会返回false,你们可以直接抛出异常
                System.out.println("操作频繁,未获取到锁");
                Asserts.fail("操作频繁");
                return false;
            } 
            // 这里开始处理订单信息
            xxService.createOrder(xxx);

        } catch (InterruptedException e) {
            log.error("错误信息"+e.getMessage());
        } finally {
            // 无论是否获取到锁,最终都要释放锁
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

解释:我们将用户的id和订单的orderId作为redis分布式锁的key,当用户连续点击两次或者多次的时候,因为第一次已经加了锁了,所以再次加锁的时候就会失败,也就是不走下面订单支付这个动作了。我这里只是提供了个案例,具体还需要根据自己的业务来进一步处理。

3.6 如果任务一直没处理完怎么办?

其实我们在设置key的有效期是30s,那么如果30s内,任务没有处理完怎么办?然后30s后再次提交订单不就又重复提交了吗?

其实不用担心,redisson内部提供了一个看门狗机制,每10s钟就会监控线当前线程持有锁的状态,要是还在,就会进行key续费(续存活时期)。

四、有问题留言

如果你在微信支付期间遇到什么问题,可以在此博文下留言。

点个关注,不会迷路! 

Logo

更多推荐