token機制實現冪等(訂單一致性問題)


如何保證訂單狀態一致性?如何保證接口的冪等性?訂單系統保證冪等性?

  • 保證冪等性是指保證用戶多次重復操作或請求造成的結果是一致的,不會產生任何副作用

token機制原理和session的區別

image-20211104112036934

1. 冪等實現流程

以訂單系統為例,假設用戶在付款流程中(此時頁面跳轉到第三方支付頁面),用戶在第三方支付成功之后,切換頁面回到待付款頁面手動關閉訂單,此時的訂單的狀態就出錯了,先被更新成已付款狀態,后又變更成已關閉,造成了訂單狀態不一致的現象出現!這種不加任何防護手段的場景,就違背了冪等性的原則

image-20211104111155678

2.代碼實現

  1. 自定義注解

//target表示作用對象,所有類型的方法
@Target(ElementType.METHOD)
//注解的生命周期,運行時
@Retention(RetentionPolicy.RUNTIME)
public @interface MiDeng{

}
  1. 編寫攔截器,處理攔截到的請求

//冪等攔截器
public class MiDengInterceptor implements HandlerInterceptor {
   @Autowired
   private RedisTemplate redisTemplate;  //使用redis存儲token

   //處理冪等問題
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       response.setCharacterEncoding("utf-8");
       response.setContentType("text/html;charset=utf-8");
       //首先判斷攔截的請求是否是方法
       if (handler instanceof HandlerMethod) {
           HandlerMethod handlerMethod = (HandlerMethod) handler;
           //判斷該方法是否包含冪等注解
           if (handlerMethod.hasMethodAnnotation(MiDeng.class)) {
               //對包含注解的進行處理
               String uniqueId = "";
               //在request中獲取id,如果沒有,在請求頭中獲取一次
               if ((uniqueId = request.getParameter("uniqueId")) == null) {
                   uniqueId = request.getHeader("uniqueId");
              }
               //如果uniqueId為空,則表明當前沒有執行支付流程
               String key="uniqueId:"+uniqueId;
               if (redisTemplate.hasKey(key)){//當前已經在執行流程
                   response.getWriter().write("請勿重復執行操作");
                   return false;
              }else {//沒有執行過,在redis中創建taken,設置存活時間為2min,使用util包下的SHA1加密生成一個唯一的32位字符串
                   redisTemplate.boundValueOps(key).set(SHA1Util.encode(uniqueId));
                   redisTemplate.expire(key, 120, TimeUnit.SECONDS);
                   return true;
              }
          } else {
               //不包含冪等注解,放行
               return true;
          }
      } else {
           //攔截的請求不是方法,放行
           return true;
      }
  }
}
  1. 在springMVC中配置攔截器

        <mvc:interceptor>
           <!--攔截的路徑所有-->
           <mvc:mapping path="/**"/>
           <!--不攔共同資源-->
           <mvc:exclude-mapping path="/static/**"/>
           <!--攔截器的位置-->
           <bean class="com.oracle.shop.security.MiDengInterceptor"></bean>
       </mvc:interceptor>
  1. 在需要的方法上添加注解

 //取消訂單
   @MiDeng
   @RequestMapping("/toCancelOrders")
   public String toCancelOrders(int uniqueId, HttpSession session)
   //跳轉到阿里支付頁面,
   @MiDeng
   @RequestMapping("/toAlipay")
   public void toAlipay(int uniqueId, HttpServletResponse response) throws AlipayApiException, IOException
  1. 在支付完成后,刪除該token,支付完成后的回調頁面

//流程執行結束后,刪除redis當中的token
redisTemplate.delete("uniqueId"+orders.getId());


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM