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

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

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

常见的重复提交场景网络延迟:用户在提交订单后未收到确认,误以为订单未提交成功,连续点击提交按钮。页面刷新:用户在提交订单后刷新页面,触发订单的重复提交。用户误操作:用户无意中点击多次订单提交按钮。防止重复提交的需求幂等性保证:确保相同的请求多次提交只能被处理一次,最终结果是唯一的。用户体验保障:避免由于重复提交导致用户感知的延迟或错误。常用解决方案前端防重机制:在前端按钮点击时禁用按钮或加锁,防止用户多次点击。

后端幂等处理:

利用Token机制:在订单生成前生成一个唯一的Token,保证每个订单提交时只允许携带一次Token。基于数据库的唯一索引:通过对订单字段(如订单号、用户ID)创建唯一索引来防止重复数据的插入。分布式锁:使用Redis等分布式缓存加锁,保证同一时间只允许处理一个订单请求。功能实践Spring Boot 提供了丰富的工具和库,今天我们基于Spring Boot框架,可以利用 Token机制 和 Redis分布式锁 来防止订单的重复提交。

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

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

Token生成:在用户开始提交订单时,服务器生成一个唯一的 OrderToken 并将其存储在 Redis 等缓存中,同时返回给客户端。Token验证:用户提交订单时,客户端会将 OrderToken 发送回服务器。服务器会验证此 OrderToken 是否有效。Token销毁:一旦验证通过,服务器会立即销毁 OrderToken,防止重复使用同一个Token提交订单。这种机制确保每次提交订单时都需要一个有效且唯一的Token,从而有效防止重复提交。

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

Token存储:生成的Token可以存储在Redis中,Token的存活时间通过设置TTL(如10分钟),保证Token在一定时间内有效。Token校验与删除:当用户提交订单时,服务器通过Redis查询该Token是否存在,并立即删除该Token,确保同一个订单只能提交一次。流程设计用户发起订单请求时,后端生成一个唯一的Token(例如UUID),并将其存储在Redis中,同时将该Token返回给前端。前端提交订单时,将Token携带至后端。后端校验该Token是否有效,若有效则执行订单创建流程,同时删除Redis中的该Token,确保该Token只能使用一次。如果该Token已被使用或过期,则返回错误信息,提示用户不要重复提交。功能实现依赖配置(pom.xml)

代码语言:xml复制

org.springframework.boot

spring-boot-starter-data-redis

org.springframework.boot

spring-boot-starter-web

application. properties

代码语言:yaml复制# 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中。

代码语言:java复制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 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只能提交一次。

代码语言:java复制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 getOrderToken(@RequestParam String userId) {

String token = orderTokenService.generateOrderToken(userId);

return ResponseEntity.ok(token);

}

// 提交订单

@PostMapping("/submitOrder")

public ResponseEntity submitOrder(Order order) {

// 校验Token

if (!orderTokenService.validateOrderToken(order.getUserId(), order.getOrderToken())) {

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("订单重复提交,请勿重复操作");

}

// 此处处理订单逻辑

// ...

// 假设订单提交成功

return ResponseEntity.ok("订单提交成功");

}

}

前端实现

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

代码语言:html复制

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

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

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

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

Token的唯一性与时效性:确保每次订单提交前都有唯一且有效的Token。Token的原子性验证与删除:在验证Token的同时删除它,防止同一个Token被多次使用。Redis的高效存储与分布式锁:通过Redis在高并发环境中提供稳定的锁机制,保证并发提交的准确性。这套基于Token机制和Redis的解决方案具有简单、高效、可扩展的特点,适合各种高并发场景下防止重复订单提交。

相关文章

超详细的月饼问题大搜罗!塌陷、涨腰、开裂、上色等 365bet中文体育在线

超详细的月饼问题大搜罗!塌陷、涨腰、开裂、上色等

📅 10-19 👁️ 7305
网上交电费的app有哪些?交电费软件大全 365bet中文体育在线

网上交电费的app有哪些?交电费软件大全

📅 06-28 👁️ 2833
煎土豆片的做法 365体育足球中文版

煎土豆片的做法

📅 07-23 👁️ 6266