LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

如何防止重复提交订单?

liguoquan
2025年10月20日 16:21 本文热度 52
:如何防止重复提交订单?


如何防止重复提交订单?

 

如何防止重复提交订单?

作者:Java后端开发工程师


一、背景介绍:为什么会产生重复提交?

在电商平台中,用户提交订单是一个非常敏感的动作。这通常涉及:

  • 库存扣减
  • 优惠券核销
  • 支付下单
  • 消息发送

但用户总喜欢:

  • 点两次“提交订单”按钮
  • 网络卡顿时刷新页面
  • 使用浏览器回退再次提交

结果就是:重复提交订单,造成资源浪费,甚至业务损失!


二、问题分析:重复提交的常见场景

场景示例
用户行为多次点击按钮、浏览器刷新
接口幂等性差接口无幂等校验,每次都生成新订单
网络重试客户端自动重发请求(如超时)
分布式系统多个节点并发处理同一订单请求

三、防止重复提交的核心原则

要解决重复提交问题,必须从接口幂等性 + 请求唯一性 + 服务端锁控制三方面入手:

  1. 控制请求的唯一标识(token/nonce)
  2. 对订单操作进行幂等处理
  3. 引入缓存或分布式锁限制重复提交

四、解决方案:基于Token机制 + Redis锁的防重复提交设计

✅ 设计思路:

  1. 前端在创建订单前从服务端获取一个唯一 token(防重复提交标识)
  2. 提交订单时将 token 附带传入
  3. 后端验证 token 是否存在(Redis)
  4. 校验通过 → 执行下单逻辑 → 删除 token
  5. 若 token 已被使用 → 拒绝重复提交

五、代码实现(Spring Boot + Redis)

1. 前端获取防重复提交 Token 接口

@RestController @RequestMapping("/api/order") public class OrderTokenController {    @Autowired    private RedisTemplate<String, String> redisTemplate;    @GetMapping("/token")    public ResponseEntity<String> getToken() {        String token = UUID.randomUUID().toString();        String key = "order:token:" + token;        // 设置有效期5分钟        redisTemplate.opsForValue().set(key, "valid", Duration.ofMinutes(5));        return ResponseEntity.ok(token);    } }

2. 提交订单接口(验证token + 删除token)

@RestController @RequestMapping("/api/order") public class OrderController {    @Autowired    private OrderService orderService;    @PostMapping("/submit")    public ResponseEntity<String> submitOrder(@RequestBody OrderRequest request,                                              @RequestHeader("X-Order-Token") String token) {        boolean success = orderService.submitOrder(request, token);        if (success) {            return ResponseEntity.ok("订单提交成功");        } else {            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("请勿重复提交订单");        }    } }

3. OrderService 实现防重复提交逻辑

@Service public class OrderService {    @Autowired    private RedisTemplate<String, String> redisTemplate;    /**     * 提交订单接口,防止重复提交     */    public boolean submitOrder(OrderRequest request, String token) {        String redisKey = "order:token:" + token;        // 利用 Redis 的 delete + check 保证幂等性(原子性)        Boolean result = redisTemplate.delete(redisKey);        if (Boolean.TRUE.equals(result)) {            // token存在并删除 → 第一次提交            // 执行正常订单创建逻辑            createOrder(request);            return true;        } else {            // token 不存在 → 重复提交            return false;        }    }    private void createOrder(OrderRequest request) {        // 实际业务处理:生成订单号、校验库存、扣减库存、写库、发MQ等        System.out.println("处理订单:" + request);    } }

4. 请求对象 OrderRequest 示例

@Data public class OrderRequest {    private Long userId;    private List<Long> productIds;    private BigDecimal totalAmount; }

六、进阶优化建议

1. 使用 Lua 脚本保证 Redis 操作原子性

Redis delete 操作不是强原子性的,建议使用 Lua 脚本执行 “判断 + 删除” 逻辑。

// Lua 脚本实现原子删除 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +                "return redis.call('del', KEYS[1]) else return 0 end"; DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(script); redisScript.setResultType(Long.class); Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), "valid");

2. 给订单接口加限流或熔断保护(如 Sentinel)

  • 防止恶意刷接口
  • 降低重复提交带来的系统压力

3. 数据库层幂等校验(双保险)

即便应用层失效,也可以通过数据库约束(如订单号唯一)+ INSERT IGNORE 或 ON DUPLICATE KEY 防止重复插入。


七、总结

面对用户重复提交订单的问题,我们不能只靠前端“禁用按钮”了,而是应该从后端保障:

  • 请求唯一性
  • 接口幂等性
  • 服务端锁机制

✅ 实战建议:

  • Redis 是处理幂等控制的利器
  • token机制简单实用,适用于下单、支付、秒杀等场景
  • 多层防御更安全:应用层 + 数据库层

📌 最后

这篇文章分享了我在实际项目中防止订单重复提交的完整方案,希望对你有所帮助!

如果你也在做订单系统、支付系统,欢迎留言交流你的经验。


该文章在 2025/10/20 16:21:11 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved