1. 程式人生 > >分散式商城購物車實現

分散式商城購物車實現

完成購物工程和訂單工程

購物工程流程

購物車新增

業務方法

@Override
public String addCart(Cart cart, User user, String cartToken) {
    //是否登入進行不同的處理
    if(user == null){//沒有使用者登入,資料放到redis快取中
        if(cartToken == null){
            //第一次新增購物車建立uuid
            cartToken = UUID.randomUUID().toString();
        }
        //把資料放在redis中
        redisTemplate.opsForList().leftPush(cartToken, cart);
        //設定失效時間
        redisTemplate.expire(cartToken, 365, TimeUnit.DAYS);
    }else{//使用者登入直接調動Dao放到資料庫中
        cart.setUid(user.getId());
        cartDao.addCart(cart);
    }
    //第一次新增購物車將令牌返回
    return cartToken;
}

購物車資訊的存到Cookie中

@SystemLogAnno(info = "新增商品到購物車",logType = 3)
@IsLogin//預設不需要強制登入
@RequestMapping("/addCart")
public String addCart(@CookieValue(value = Constant.CART_TOKEN,required = false) String cartToken,
        Cart cart,User user,
        HttpServletResponse response){
    cartToken = cartService.addCart(cart, user, cartToken);
    //如果不為空則是第一次登陸,即Service有返回值
    if(cartToken != null){
        Cookie cookie = new Cookie(Constant.CART_TOKEN, cartToken);
        cookie.setPath("/");
        cookie.setMaxAge(60 * 60 * 24 * 365);
        response.addCookie(cookie);
    }
    return "success";
}

購物車資訊的展示

獲得購物車資訊

@Override
public List<Cart> cartList(String cartToken, User user) {
    List<Cart> carts = null;
    //如果沒有登入就去快取中查
    //為了合併購物車不論有沒有登入我都先去快取中查一遍
    if(cartToken != null){
        Long size = redisTemplate.opsForList().size(cartToken);
        carts = redisTemplate.opsForList().range(cartToken, 0, size);
        //因為快取中的購物車是沒有商品其他資訊的,需要在資料庫中再查詢商品的資訊,放到carts中
        for (int i = 0; i < carts.size(); i++) {
            GoodsInfo goodsInfo = goodsInfoDao.queryGoodsInfoById(carts.get(i).getGid());
            carts.get(i).setGoodsInfo(goodsInfo);
        }
    }
    //在判斷是否已經登入
    if(user != null){
        //已經登入,快取中可能還有資料
        //此時已經登陸了,將快取中的資料放到資料庫中
        if(carts != null ){
            for (int i = 0; i < carts.size(); i++) {
                carts.get(i).setUid(user.getId());
                cartDao.addCart(carts.get(i));
            }
        }
        //合併後清空快取中的資料
        redisTemplate.delete(cartToken);
        //再去資料庫中查詢
        carts = cartDao.queryByUid(user.getId());
    }
    return carts;
}

1.首頁工程使用json傳送請求展示縮圖

/**
 * jsonp+redis+cookie實現購物縮圖展示
 * @param cartToken
 * @param user
 * @return
 */
@SystemLogAnno(info = "jsonp檢視購物車資料",logType = 6)
@IsLogin//預設不需要強制登入
@RequestMapping("/cartList")
@ResponseBody
public String cartList(@CookieValue(value = Constant.CART_TOKEN,required = false) String cartToken,
                       User user){
    //直接呼叫業務層獲得資料
    List<Cart> carts = cartService.cartList(cartToken, user);
    return "cartList(" + new Gson().toJson(carts) + ")";
}

2.購物車頁面

//此時使用者依舊可以使遊客狀態,一旦使用者需要進入訂單模組,必須強制登陸
@SystemLogAnno(info = "跳轉到購物車頁面檢視購物車資料",logType = 7)
@IsLogin//預設不需要強制登入
@RequestMapping("/showCartList")
public String showCartList(@CookieValue(value = Constant.CART_TOKEN,required = false) String cartToken,
                       User user, Model model){
    //直接呼叫業務層獲得資料
    List<Cart> carts = cartService.cartList(cartToken, user);
    model.addAttribute("carts",carts);
    return "cartList";
}

3.購物車頁面設計
使用者可以對購物車列表進行CRUD包括數量的修改,對於數量的修改,我們可以設計為點選事件每一次點選都發送一個非同步請求修改資料庫中的值,並且重新整理頁面資料

訂單工程流程

訂單編輯頁面

分析:
訂單編輯頁面所需的資料:
1.使用者id->通過id獲取使用者的地址資訊
2.選擇的商品資訊以及商品數量也就是我們的購物車物件,將其當作一個表單提交
業務方法:

@RequestMapping("/editOrder")
@IsLogin(true)
public String editOrder(
       //此時可能是從購物車頁面強制跳轉到登陸頁面,需要先將購物車新增到資料庫中
       @CookieValue(value = Constant.CART_TOKEN,required = false) String token,
       Integer[] gid,
       User user,
       Model model){
   //首先要查到當前使用者的收貨地址
   List<Address> addressList = null;
   if(user != null){
       addressList = addressService.queryAddressByUid(user.getId());
   }

   //總計
   double sumPrice = 0.0;

   //當前使用者的購物車
   List<Cart> cartList = cartService.cartList(token, user);
   //使用者所需要結算的購物車
   List<Cart> realCartList = new ArrayList<>();
   for (Cart cart : cartList) {
       for (Integer id : gid) {
           if(cart.getGoodsInfo().getId() == id){
               sumPrice += cart.getMoney();
               realCartList.add(cart);
           }
       }
   }

   model.addAttribute("addressList",addressList);
   model.addAttribute("cartList",realCartList);
   model.addAttribute("sumPrice",sumPrice);
   return "orderedit";
}

實現地址新增功能

問題:
1.新增了一個地址需要選擇是否是預設地址,如果選擇了是預設地址,需要在新增時同時將資料庫中預設地址設定為0,我們此處使用儲存過程來實現該操作
2.儲存過程的事務的控制:
可以在兩處地方實現事務控制,1.業務層中新增@Transaction註解即可,2.儲存過程中手動開啟事務和提交事務,抓取SQLException進行事務的回滾,但是此處一旦進行了控制,我們會發現程式中我們無法捕獲異常

儲存過程

delimiter &&
create PROCEDURE insertaddress(in p_person VARCHAR(20),in p_phone VARCHAR(20),in p_address VARCHAR(100),in p_uid int,in p_isdefault int)
BEGIN
    if p_isdefault = 1 THEN
        update t_address set isdefault = 0 where isdefault = 1 and uid = p_uid;
    END IF;
        insert into t_address values(null,p_person,p_phone,p_address,p_uid,p_isdefault);
END &&
delimiter ;

控制層

@RequestMapping("/addAddress")
@ResponseBody
@IsLogin(true)
public Integer addAddress(Address address,User user){
    if(user != null){
        address.setUid(user.getId());
    }
    return addressService.insertAddress(address);
}

頁面

<form id="formid" method="post">
</form>
<script>
  //新增收貨地址
  function addaddress(){
      $.ajax({
          url:"/order/addAddress",
          //將表單資料序列化作為引數傳到後臺
          data: $("#formid").serialize(),
          success:function(data){
              if(data == 1){
                  //重新整理頁面
                  location.reload();
              } else {
                  alert("地址新增失敗!!!!");
              }
          }
      });
  }
</script>

去支付後,新增訂單和訂單詳情(必須使用事務控制)

1.新增訂單
2.新增訂單詳情
3.刪除購物車

控制層

@RequestMapping("/addOrder")
@ResponseBody
@IsLogin(true)
public String addOrder(Integer[] cids,Integer aid,User user){
    //System.out.println(Arrays.toString(cids) + "   " + aid);
    //新增訂單和訂單詳情
    if(user != null){
        return orderService.addOrder(cids,aid,user);
    }
    return null;
}

啟動方法上新增事務管理器註冊:

@EnableTransactionManagement

業務層

@Override
@Transactional
public String addOrder(Integer[] cids, Integer aid, User user) {
   Orders order = new Orders();

   //查詢地址
   Address address = addressDao.queryAddressById(aid);
   //隨機訂單id
   String orderid = UUID.randomUUID().toString().replace("-","").trim();
   //封裝資訊
   order.setAddress(address.getAddress());
   order.setPerson(address.getPerson());
   //計算總金額
   double sumPrice = 0;

   List<Cart> cartList = cartDao.queryByIds(cids);

   if(cartList != null && !cartList.isEmpty()){
       for (Cart cart : cartList) {
           sumPrice += cart.getMoney();
       }
   }
   
   order.setOprice(sumPrice);
   order.setOrdertime(new Date());
   order.setOrderid(orderid);
   order.setPhone(address.getPhone());
   //預設狀態為0,未支付
   order.setStatus(0);
   order.setUid(user.getId());
   //新增訂單,主鍵回填,下面的訂單詳情需要設定訂單id
   orderDao.addOrder(order);
   //有幾個購物車物件就有幾條訂單詳情記錄
   List<OrderDetail> orderDetailList = new ArrayList<>();
   for (Cart cart : cartList) {
       //訂單詳情
       OrderDetail orderDetail = new OrderDetail();
       //封裝
       orderDetail.setGcount(cart.getGcount());
       orderDetail.setGid(cart.getGid());
       orderDetail.setGimage(cart.getGoodsInfo().getGoodsPic());
       orderDetail.setGinfo(cart.getGoodsInfo().getGoodsDescription());
       //主鍵回填後
       orderDetail.setOid(order.getId());
       orderDetail.setPrice(cart.getMoney());
       orderDetail.setGname(cart.getGoodsInfo().getGoodsName());
	   //封裝後放到集合中
       orderDetailList.add(orderDetail);
   }
   //必須判斷集合是不是為空,否則語法錯誤
   if(orderDetailList!= null && !orderDetailList.isEmpty()){
       orderDao.addOrderDetails(orderDetailList);
   }
   //訂單一旦生成,購物車中的資料需要刪除,刪除購物車
   cartDao.deleteCarts(cids);
   //訂單頁面需要使用到訂單id
   return order.getOrderid();
}

訂單展示

//通過uid查詢其訂單資訊即可
@RequestMapping("/list")
@IsLogin(true)
public String orderList(User user, Model model){
    List<Orders> orders = orderService.queryByUid(user.getId());
    model.addAttribute("orders", orders);
    return "orderlist";
}