欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java防止重復(fù)提交訂單的實(shí)現(xiàn)示例

 更新時(shí)間:2025年11月12日 09:33:33   作者:天天摸魚(yú)的java工程師  
本文主要介紹了Java防止重復(fù)提交訂單的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、背景介紹:為什么會(huì)產(chǎn)生重復(fù)提交?

在電商平臺(tái)中,用戶提交訂單是一個(gè)非常敏感的動(dòng)作。這通常涉及:

  • 庫(kù)存扣減
  • 優(yōu)惠券核銷(xiāo)
  • 支付下單
  • 消息發(fā)送

但用戶總喜歡:

  • 點(diǎn)兩次“提交訂單”按鈕
  • 網(wǎng)絡(luò)卡頓時(shí)刷新頁(yè)面
  • 使用瀏覽器回退再次提交

結(jié)果就是:重復(fù)提交訂單,造成資源浪費(fèi),甚至業(yè)務(wù)損失!

二、問(wèn)題分析:重復(fù)提交的常見(jiàn)場(chǎng)景

場(chǎng)景示例
用戶行為多次點(diǎn)擊按鈕、瀏覽器刷新
接口冪等性差接口無(wú)冪等校驗(yàn),每次都生成新訂單
網(wǎng)絡(luò)重試客戶端自動(dòng)重發(fā)請(qǐng)求(如超時(shí))
分布式系統(tǒng)多個(gè)節(jié)點(diǎn)并發(fā)處理同一訂單請(qǐng)求

三、防止重復(fù)提交的核心原則

要解決重復(fù)提交問(wèn)題,必須從接口冪等性 + 請(qǐng)求唯一性 + 服務(wù)端鎖控制三方面入手:

  1. 控制請(qǐng)求的唯一標(biāo)識(shí)(token/nonce)
  2. 對(duì)訂單操作進(jìn)行冪等處理
  3. 引入緩存或分布式鎖限制重復(fù)提交

四、解決方案:基于Token機(jī)制 + Redis鎖的防重復(fù)提交設(shè)計(jì)

? 設(shè)計(jì)思路:

  1. 前端在創(chuàng)建訂單前從服務(wù)端獲取一個(gè)唯一 token(防重復(fù)提交標(biāo)識(shí))
  2. 提交訂單時(shí)將 token 附帶傳入
  3. 后端驗(yàn)證 token 是否存在(Redis)
  4. 校驗(yàn)通過(guò) → 執(zhí)行下單邏輯 → 刪除 token
  5. 若 token 已被使用 → 拒絕重復(fù)提交

五、代碼實(shí)現(xiàn)(Spring Boot + Redis)

1. 前端獲取防重復(fù)提交 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;

        // 設(shè)置有效期5分鐘
        redisTemplate.opsForValue().set(key, "valid", Duration.ofMinutes(5));

        return ResponseEntity.ok(token);
    }
}

2. 提交訂單接口(驗(yàn)證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("請(qǐng)勿重復(fù)提交訂單");
        }
    }
}

3. OrderService 實(shí)現(xiàn)防重復(fù)提交邏輯

@Service
public class OrderService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 提交訂單接口,防止重復(fù)提交
     */
    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存在并刪除 → 第一次提交
            // 執(zhí)行正常訂單創(chuàng)建邏輯
            createOrder(request);
            return true;
        } else {
            // token 不存在 → 重復(fù)提交
            return false;
        }
    }

    private void createOrder(OrderRequest request) {
        // 實(shí)際業(yè)務(wù)處理:生成訂單號(hào)、校驗(yàn)庫(kù)存、扣減庫(kù)存、寫(xiě)庫(kù)、發(fā)MQ等
        System.out.println("處理訂單:" + request);
    }
}

4. 請(qǐng)求對(duì)象 OrderRequest 示例

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

六、進(jìn)階優(yōu)化建議

1. 使用 Lua 腳本保證 Redis 操作原子性

Redis delete 操作不是強(qiáng)原子性的,建議使用 Lua 腳本執(zhí)行 “判斷 + 刪除” 邏輯。

// Lua 腳本實(shí)現(xiàn)原子刪除
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. 給訂單接口加限流或熔斷保護(hù)(如 Sentinel)

  • 防止惡意刷接口
  • 降低重復(fù)提交帶來(lái)的系統(tǒng)壓力

3. 數(shù)據(jù)庫(kù)層冪等校驗(yàn)(雙保險(xiǎn))

即便應(yīng)用層失效,也可以通過(guò)數(shù)據(jù)庫(kù)約束(如訂單號(hào)唯一)+ INSERT IGNOREON DUPLICATE KEY 防止重復(fù)插入。

七、總結(jié)

面對(duì)用戶重復(fù)提交訂單的問(wèn)題,我們不能只靠前端“禁用按鈕”了,而是應(yīng)該從后端保障:

  • 請(qǐng)求唯一性
  • 接口冪等性
  • 服務(wù)端鎖機(jī)制

? 實(shí)戰(zhàn)建議:

  • Redis 是處理冪等控制的利器
  • token機(jī)制簡(jiǎn)單實(shí)用,適用于下單、支付、秒殺等場(chǎng)景
  • 多層防御更安全:應(yīng)用層 + 數(shù)據(jù)庫(kù)層

?? 最后

到此這篇關(guān)于Java防止重復(fù)提交訂單的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Java防止重復(fù)提交訂單內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring中的注解@Value("#{}")與@Value("${}")的區(qū)別介紹

    Spring中的注解@Value("#{}")與@Value("${}")的區(qū)別

    這篇文章主要介紹了Spring中的注解@Value(“#{}“)與@Value(“${}“)的區(qū)別到底是什么,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • Spring Boot mybatis-config 和 log4j 輸出sql 日志的方式

    Spring Boot mybatis-config 和 log4j 輸出sql 日志的方式

    這篇文章主要介紹了Spring Boot mybatis-config 和 log4j 輸出sql 日志的方式,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-07-07
  • MyBatis使用Map與模糊查詢的方法示例

    MyBatis使用Map與模糊查詢的方法示例

    這篇文章主要給大家介紹了關(guān)于MyBatis使用Map與模糊查詢的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 解決springboot文件上傳提示臨時(shí)文件夾不存在問(wèn)題

    解決springboot文件上傳提示臨時(shí)文件夾不存在問(wèn)題

    這篇文章主要介紹了解決springboot文件上傳提示臨時(shí)文件夾不存在問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringBoot環(huán)境下junit單元測(cè)試速度優(yōu)化方式

    SpringBoot環(huán)境下junit單元測(cè)試速度優(yōu)化方式

    這篇文章主要介紹了SpringBoot環(huán)境下junit單元測(cè)試速度優(yōu)化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java使用嵌套循環(huán)模擬ATM機(jī)取款業(yè)務(wù)操作示例

    Java使用嵌套循環(huán)模擬ATM機(jī)取款業(yè)務(wù)操作示例

    這篇文章主要介紹了Java使用嵌套循環(huán)模擬ATM機(jī)取款業(yè)務(wù)操作,結(jié)合實(shí)例形式分析了Java模擬ATM機(jī)取款業(yè)務(wù)操作的相關(guān)流程控制、數(shù)值判斷等操作技巧,需要的朋友可以參考下
    2019-11-11
  • SpringMVC執(zhí)行步驟、Model的使用詳解

    SpringMVC執(zhí)行步驟、Model的使用詳解

    這篇文章主要介紹了SpringMVC執(zhí)行步驟、Model的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 基于Java實(shí)現(xiàn)動(dòng)態(tài)切換ubuntu壁紙功能

    基于Java實(shí)現(xiàn)動(dòng)態(tài)切換ubuntu壁紙功能

    這篇文章主要為大家詳細(xì)介紹了如何使用 Java 在 Ubuntu Linux 系統(tǒng)中實(shí)現(xiàn)自動(dòng)切換壁紙的示例程序,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • Java 線程池原理深入分析

    Java 線程池原理深入分析

    這篇文章主要介紹了Java 線程池原理深入分析的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java內(nèi)置GUI Frame類(lèi)的使用

    Java內(nèi)置GUI Frame類(lèi)的使用

    這篇文章主要介紹了Java內(nèi)置GUI Frame類(lèi)的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05

最新評(píng)論