首页 > 其他分享 >大厂必问 · 如何防止订单重复?

大厂必问 · 如何防止订单重复?

时间:2024-09-29 09:50:52浏览次数:3  
标签:必问 userId Redis 订单 Token 大厂 提交 import

在电商系统或任何涉及订单操作的场景中,用户多次点击“提交订单”按钮可能会导致重复订单提交,造成数据冗余和业务逻辑错误,导致库存问题、用户体验下降或财务上的错误。因此,防止订单重复提交是一个常见需求。

常见的重复提交场景

  1. 网络延迟:用户在提交订单后未收到确认,误以为订单未提交成功,连续点击提交按钮。
  2. 页面刷新:用户在提交订单后刷新页面,触发订单的重复提交。
  3. 用户误操作:用户无意中点击多次订单提交按钮。

防止重复提交的需求

  1. 幂等性保证:确保相同的请求多次提交只能被处理一次,最终结果是唯一的。
  2. 用户体验保障:避免由于重复提交导致用户感知的延迟或错误。

常用解决方案

前端防重机制:在前端按钮点击时禁用按钮或加锁,防止用户多次点击。

后端幂等处理

  • 利用Token机制:在订单生成前生成一个唯一的Token,保证每个订单提交时只允许携带一次Token。
  • 基于数据库的唯一索引:通过对订单字段(如订单号、用户ID)创建唯一索引来防止重复数据的插入。
  • 分布式锁:使用Redis等分布式缓存加锁,保证同一时间只允许处理一个订单请求。

功能实践

Spring Boot 提供了丰富的工具和库,今天我们基于Spring Boot框架,可以利用 Token机制Redis分布式锁 来防止订单的重复提交。

功能原理与技术实现

通过Redis的原子性操作,我们可以确保高并发情况下多个请求对同一个订单的操作不会冲突。

请在此添加图片描述

Token机制

Token机制是一种常见的防止重复提交的手段,通常的工作流程如下:

  1. Token生成:在用户开始提交订单时,服务器生成一个唯一的 OrderToken 并将其存储在 Redis 等缓存中,同时返回给客户端。
  2. Token验证:用户提交订单时,客户端会将 OrderToken 发送回服务器。服务器会验证此 OrderToken 是否有效。
  3. Token销毁:一旦验证通过,服务器会立即销毁 OrderToken,防止重复使用同一个Token提交订单。

这种机制确保每次提交订单时都需要一个有效且唯一的Token,从而有效防止重复提交。

Redis分布式锁

在多实例的分布式环境中,Token机制可以借助 Redis 来实现更高效的分布式锁:

  1. Token存储:生成的Token可以存储在Redis中,Token的存活时间通过设置TTL(如10分钟),保证Token在一定时间内有效。
  2. Token校验与删除:当用户提交订单时,服务器通过Redis查询该Token是否存在,并立即删除该Token,确保同一个订单只能提交一次。

流程设计

  1. 用户发起订单请求时,后端生成一个唯一的Token(例如UUID),并将其存储在Redis中,同时将该Token返回给前端。
  2. 前端提交订单时,将Token携带至后端。
  3. 后端校验该Token是否有效,若有效则执行订单创建流程,同时删除Redis中的该Token,确保该Token只能使用一次。
  4. 如果该Token已被使用或过期,则返回错误信息,提示用户不要重复提交。

功能实现

依赖配置(pom.xml)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application. properties

# Thymeleaf ??
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false

spring.redis.host=127.0.0.1
spring.redis.port=23456
spring.redis.password=pwd

订单Token生成服务

生成Token并存储到Redis: 当用户请求生成订单页面时,服务器生成一个唯一的UUID作为订单Token,并将其与用户ID一起存储在Redis中。

package com.example.demo.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class OrderTokenService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    // 生成订单Token
    public String generateOrderToken(String userId) {
        String token = UUID.randomUUID().toString();
        // 将Token存储在Redis中,设置有效期10分钟
        redisTemplate.opsForValue().set("orderToken:" + userId, token, 10, TimeUnit.MINUTES);
        return token;
    }
    // 验证订单Token
    public boolean validateOrderToken(String userId, String token) {
        String redisToken = redisTemplate.opsForValue().get("orderToken:" + userId);
        log.info("@@ 打印Redis中记录的redisToken :{} `VS` @@ 打印当前请求过来的token :{}", redisToken, token);
        if (token.equals(redisToken)) {
            // 验证成功,删除Token
            redisTemplate.delete("orderToken:" + userId);
            return true;
        }
        return false;
    }
}

订单控制器

订单提交与验证Token: 提交订单时,系统会检查用户传递的Token是否有效,若有效则允许提交并删除该Token,确保同一Token只能提交一次。

package com.example.demo.controller;

import com.example.demo.entity.Order;
import com.example.demo.service.OrderTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderTokenService orderTokenService;
    // 获取订单提交的Token
    @GetMapping("/getOrderToken")
    public ResponseEntity<String> getOrderToken(@RequestParam String userId) {
        String token = orderTokenService.generateOrderToken(userId);
        return ResponseEntity.ok(token);
    }
    // 提交订单
    @PostMapping("/submitOrder")
    public ResponseEntity<String> submitOrder(Order order) {
        // 校验Token
        if (!orderTokenService.validateOrderToken(order.getUserId(), order.getOrderToken())) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("订单重复提交,请勿重复操作");
        }

        // 此处处理订单逻辑
        // ...
        
        // 假设订单提交成功
        return ResponseEntity.ok("订单提交成功");
    }
}

前端实现

前端通过表单提交订单,并在每次提交前从服务器获取唯一的订单Token:


<script>

    document.getElementById('orderForm').addEventListener('submit', function(event) {
    event.preventDefault();

    const userId = document.getElementById('userId').value;
    if (!userId) {
        alert("请填写用户ID");
        return;
    }

    // 先获取Token,再提交订单
    fetch(`/order/getOrderToken?userId=${userId}`)
        .then(response => response.text())
        .then(token => {
            document.getElementById('orderToken').value = token;

            // 提交订单请求
            const formData = new FormData(document.getElementById('orderForm'));
            fetch('/order/submitOrder', {
                method: 'POST',
                body: formData
            })
            .then(response => response.text())
            .then(result => {
                document.getElementById('message').textContent = result;
            })
            .catch(error => {
                document.getElementById('message').textContent = '订单提交失败,请重试';
            });
        })
        .catch(error => {
            document.getElementById('message').textContent = '获取Token失败';
        });
});

</script>

为了验证功能,我们在代码中增加 Thread.sleep(2000); 来进行阻塞。

请在此添加图片描述

然后快速点击提交表单,可以看到提示表单重复提价的信息

请在此添加图片描述

技术选型与优化: 通过Redis结合Token机制,我们有效地防止了订单的重复提交,并通过Token的唯一性和时效性保证了订单操作的幂等性。

  • Redis缓存:通过Redis的分布式锁和高并发处理能力,确保系统在高并发情况下仍然可以正常运行,并发订单提交的场景中不会出现Token重复使用问题。
  • UUID:使用UUID生成唯一的Token,保证Token的唯一性和安全性。
  • Token时效性:Token通过设置Redis的TTL(过期时间)来控制有效期,避免无效Token长期占用资源。

总结

防止订单重复提交的关键在于:

  1. Token的唯一性与时效性:确保每次订单提交前都有唯一且有效的Token。
  2. Token的原子性验证与删除:在验证Token的同时删除它,防止同一个Token被多次使用。
  3. Redis的高效存储与分布式锁:通过Redis在高并发环境中提供稳定的锁机制,保证并发提交的准确性。

这套基于Token机制和Redis的解决方案具有简单、高效、可扩展的特点,适合各种高并发场景下防止重复订单提交。

标签:必问,userId,Redis,订单,Token,大厂,提交,import
From: https://blog.csdn.net/u012263509/article/details/142584185

相关文章

  • uniapp [安卓苹果App端] - 详细实现手机蓝牙连接打印机及打印票据小票/标签/面单/热敏
    前言网上的教程乱七八糟,文本提供优质示例代码。在uni-appApp端(安卓APP|苹果APP)开发中,详解实现“手机蓝牙连接并使用蓝牙打印机”,uniAppApp端手机使用蓝牙连接打印机进行打印的相关功能,uniapp苹果安卓app实现开启蓝牙并搜索附近范围的蓝牙打印机对接全流程,支持打印......
  • AI Agent如何落地?来看看在教育行业大厂的落地实践
    大家好,我是Shelly,一个专注于输出AI工具和科技前沿内容的AI应用教练,体验过300+款以上的AI应用工具。关注科技及大模型领域对社会的影响10年+。关注我一起驾驭AI工具,拥抱AI时代的到来。以正教育AIAgent以正教育大模型AIAgent是由蚂蚁云科技集团推出的一款基于以正教育大模型......
  • 大厂面试题-如何计算最大同时在线人数
    1/题目背景现有各直播间的用户访问记录表(live_events),表中每一条记录表示,一个用户何时进入了一个直播间,又在何时离开了该直播间。现请你统计各直播间最大同时在线人数。2/数据准备--创建live_events表CREATETABLElive_events(event_idINTPRIMARYKEYAUTO_INCR......
  • 8,(经典面试题:分组求topN)Python数分之Pandas训练,力扣,1532. 最近的三笔订单
    学习:知识的初次邂逅复习:知识的温故知新练习:知识的实践应用目录一,原题力扣链接二,题干三,建表语句四,分析五,Pandas解答六,验证七,知识点总结一,原题力扣链接.-力扣(LeetCode)二,题干表:Customers+---------------+---------+|ColumnName|Type|+------......
  • 26,【经典大厂面试题】【连续问题的困难题】Python数分之Pandas训练,力扣,2173. 最多连胜
    学习:知识的初次邂逅复习:知识的温故知新练习:知识的实践应用目录一,原题力扣链接二,题干三,建表语句四,分析五,SQL解答六,验证七,知识点总结一,原题力扣链接.-力扣(LeetCode)二,题干表: Matches+-------------+------+|ColumnName|Type|+-------------+-----......
  • Java如何解决同时出库入库订单号自动获取问题
    在Java中处理同时出库和入库的订单号自动获取问题,通常涉及到多线程环境下的并发控制。为了确保订单号的唯一性和连续性,我们可以使用多种策略,如数据库的自增ID、分布式锁、或者利用Java的并发工具类如AtomicLong等。这里,我将提供一个基于AtomicLong的简单示例,适用于单机环境。1.场......
  • Java如何解决同时出库入库订单号自动获取问题
    在Java中处理同时出库和入库的订单号自动获取问题,通常涉及到多线程环境下的并发控制。为了确保订单号的唯一性和连续性,我们可以使用多种策略,如数据库的自增ID、分布式锁、或者利用Java的并发工具类如AtomicLong等。这里,我将提供一个基于AtomicLong的简单示例,适用于单机环境。1.场......
  • 智界R7订单爆了,它凭什么抢了Model Y的风头?
    作者|曾响铃文|响铃说鸿蒙智行的又一个爆款稳了。9月24日,智界R7正式上市,早在9月10日已开启小订的它,到上市当天小订数已经突破30000台。上市24小时大定数突破6000台。这一成绩殊不简单,要知道,智界R7所走的轿跑SUV路线,本身是一个非常小众的市场,除了ModelY销量一枝独秀,其他玩家都......
  • 谁说35岁是程序员的中年危机?那是他还不知道这些新路子_程序员年龄大了还能进大厂吗?
    文章目录一、年纪大能不能进大厂?二、为什么说35是危机?1.精力衰退2.脑力衰退3.知识/技术迭代三、年龄大的程序员有哪些出路?1.技术管理2.创业3.技术外包4.做老师5.做自媒体6.写书四、结语我自己今年已有44了,从2021年开始就已经不上班了,在家里已经休息了2年了,我目前也......
  • 山海优选电商平台卷轴模式订单系统核心架构解析
    山海优选卷轴模式的订单核心源码是涉及订单处理、支付、搜索、状态管理等关键功能的代码部分。由于直接提供完整的源代码可能涉及版权和隐私保护问题,我将基于参考文章中的信息,概述该模式订单核心源码的主要结构和功能点。订单核心源码概述在山海优选卷轴模式的订单系统中,订单核心源......