防止订单重复提交是电商系统中的核心需求,需通过前端和后端协同机制实现。以下是综合解决方案及实践建议:
一、常见重复提交场景
-
网络延迟 :用户多次点击提交按钮未收到响应,导致重复提交;
-
页面刷新/重定向 :用户刷新页面或通过转发机制重复请求;
-
用户误操作 :意外多次点击提交按钮。
二、核心解决方案
1. 前端防重机制
-
按钮禁用/加锁 :用户点击提交后禁用按钮,防止重复点击;
-
Token机制 :生成唯一Token,每个请求携带该Token,后端验证Token唯一性;
-
分布式锁 :使用Redis等工具对订单操作加锁,同一时间仅处理一个请求。
2. 后端幂等处理
-
数据库唯一索引 :在订单字段(如订单号、用户ID)添加唯一索引,防止重复数据插入;
-
Redis过期机制 :以用户ID+商品ID为key,设置过期时间,请求前查询key是否存在;
-
消息队列去重 :使用消息队列(如MQ)存储支付结果,处理失败时重试或记录异常。
三、技术实现示例
1. 使用Redis分布式锁(Spring Boot)
@Autowired
private StringRedisTemplate redisTemplate;
public boolean processOrder(Order order) {
String lockKey = "lock:user:" + order.getUserId() + ":" + order.getProductId();
Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS);
if (lockAcquired != null && lockAcquired) {
try {
// 处理订单逻辑
return true;
} finally {
redisTemplate.delete(lockKey);
}
}
return false;
}
2. 数据库唯一索引约束
CREATE TABLE tb_order (
id BIGINT UNSIGNED NOT NULL,
order_no VARCHAR(100) NOT NULL,
user_id VARCHAR(36) NOT NULL,
request_id VARCHAR(36) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uniq_request_id (request_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 前端Token机制
// 生成Token并存储在localStorage
function generateToken() {
return Math.random().toString(36).substr(2, 16);
}
// 发送请求时携带Token
fetch('/api/order', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token
},
body: JSON.stringify(orderData)
});
// 后端验证Token
@PostMapping("/api/order")
public ResponseEntity<?> createOrder(@RequestHeader("Authorization") String token, @RequestBody Order order) {
if (!tokenValidator.validate(token)) {
return ResponseEntity.status(403).body("Invalid Token");
}
// 处理订单逻辑
}
四、注意事项
-
接口幂等性 :确保支付结果接口幂等,多次调用仅处理一次;
-
超时处理 :设置合理超时时间,主动查询支付状态(如30秒内未响应则重试);
-
系统资源保护 :防止恶意重复提交导致系统资源耗尽。
通过上述方案,可有效防止订单重复提交,保障数据一致性和用户体验。