1. 程式人生 > >電商專案day16(購物車實現)

電商專案day16(購物車實現)

今日目標:

理解購物車的思路

購物車登陸後儲存到redis中

一.需求分析

資料結構分析:

       購物車與商家相關聯

我們由京東的案列,就是可以看出,不登入,也可以實現購物車的新增,不過天貓不可以,必須登陸後才能進行,新增到購物車

購物車列表中有多個購物車:[

              京東自營(購物車物件){

                      商家id,    sellerId 

                       商家名稱:sellerName

                       購物車商品明細列表:

                       [

                                 購物車明細物件(展示商品資訊){

                                           榮耀9i

                                            華為p20

                                    }

                       ]

              }

             疆界互聯旗艦店:[

             和京東自營儲存資料結構相同

             ]

]

上面就是常用的購物車的儲存結構

二.實現思路以及解決方案

新增商品到購物車,登陸和未登陸都能實現新增購物車

         未登陸時:

           1.儲存到cookie中,這個方法有個弊端,就是隻能存4kb

            2.localStorge  儲存能達到5kb       是基於html5儲存資料物件的

             JSON.stringify();
        JSON.parse();

            3.快取到redis中作為key-value儲存

     登陸後:

           1.快取到redis中

                      登陸後:使用者名稱    作為redis儲存購物車的可以

                     登陸後將購物車列表中的商品提交生成訂單後,在清除購物車中的資料

三.未登陸時,購物車功能實現

首先搭建購物車的工程  cart_web    cart_service   cart_interface

工程搭建完畢,分析後端新增購物車的邏輯思路:

非常重要:   

      首先根據商品的id查詢   該商家是否存在於購物車中

             1.商家對應的購物車不存在,購物車列表中

                       建立購物車物件,在存入購物車列表中

                        建立購物車物件時,指定該購物車的商家資訊,以及構建購物車明細物件和購物車明細列表

                        將購物車的明細物件新增到購物車明細列表中,將購物車明細列表新增到購物車物件,在將

                         購物車物件新增到購物車列表中

            2.商家對應的購物車物件存在於購物車列表中

                       判斷該商品是否存在於該購物車商品的明細列表中

                            1.如果該商品不存在購物車明細列表中

                                      則建立購物車明細物件,在新增到購物車明細裡列表中

                            2.如果該商品存在購物車明細列表中

                                     修改購物車明細物件的數量和小計金額

首先我們先編寫未登陸時,的介面方法

 /**
     * 新增商品到購物車
     */
    public List<Cart> addItemToCartList(List<Cart> cartList, Long itemId, Integer num);

    /**
     * 根據sessionId獲得購物車列表
     * @param sessionId
     * @return
     */
    List<Cart> selectCartListFromRedis(String sessionId);

    /**
     * 儲存該sessionid 的 cartList 購物車列表
     * @param sessionId
     * @param cartList
     */
    void saveCartListToRedis(String sessionId, List<Cart> cartList);

    /**
     * 合併新增前購物車的資料到登陸後的購物車列表
     * @param cartList_sessionId
     * @param cartList_username
     * @return
     */
    List<Cart> mergeCartList(List<Cart> cartList_sessionId, List<Cart> cartList_username);

    /**
     * 刪除之前未登陸的購物車的列表資料
     * @param cartList_sessionId
     */
    void deleteCartList(List<Cart> cartList_sessionId);

service實現的所有的功能,主要是:購物車的新增,以及通過sessionid獲取購物車列表,還有就是,未登陸時,購物車列表的資料,在登陸後會跟新到redis資料庫中,通過username的作為key值儲存,然後刪除登陸前購物車的資料

package com.pinyougou.cart.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.cart.service.CartService;
import com.pinyougou.groupentity.Cart;
import com.pinyougou.mapper.TbItemMapper;
import com.pinyougou.pojo.TbItem;
import com.pinyougou.pojo.TbOrderItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Transactional
public class CartServiceImpl implements CartService {
    @Autowired
    private TbItemMapper itemMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 新增商品到購物車
     * @param cartList
     * @param itemId
     * @param num
     * @return
     *  首先根據商品的id查詢   該商家是否存在於購物車中
                 1.商家對應的購物車不存在,購物車列表中
                           建立購物車物件,在存入購物車列表中
                            建立購物車物件時,指定該購物車的商家資訊,以及構建購物車明細物件和購物車明細列表
                            將購物車的明細物件新增到購物車明細列表中,將購物車明細列表新增到購物車物件,在將
                             購物車物件新增到購物車列表中
                2.商家對應的購物車物件存在於購物車列表中
                           判斷該商品是否存在於該購物車商品的明細列表中
                                1.如果該商品不存在購物車明細列表中
                                          則建立購物車明細物件,在新增到購物車明細裡列表中
                                2.如果該商品存在購物車明細列表中
                                         修改購物車明細物件的數量和小計金額
     */
    @Override
    public List<Cart> addItemToCartList(List<Cart> cartList, Long itemId, Integer num) {
        //首先根據商品的id查詢   該商家是否存在於購物車中
        TbItem item = itemMapper.selectByPrimaryKey(itemId);
        //優化操作,新增購物車的時候,剛好該商品下架了

        if(item==null){
            throw new RuntimeException("商品不存在");
        }

        //如果商品的狀態不為1則無效
        if (!item.getStatus().equals("1")){
            throw  new RuntimeException("商品無效");
        }
        String sellerId = item.getSellerId();
        //在購物車列表中基於商品id 查詢購物車物件
        Cart cart = searchCartBySellerId(cartList,sellerId);
        //判斷購物車是否在空
        if (cart==null){//購物車為空
            //建立購物車物件,
            cart = new Cart();
            //指定該購物車的商家資訊,以及構建購物車明細物件和購物車明細列表
            cart.setSellerId(sellerId);
            cart.setSellerName(item.getSeller());
            //以及構建購物車明細物件和購物車明細列表
            List<TbOrderItem> orderItemList = new ArrayList<>();
            //構建商品明細物件
            TbOrderItem orderItem = createOrderItem(item,num);
            //購物車的明細物件新增到購物車明細列表中
            orderItemList.add(orderItem);
            //購物車明細列表新增到購物車物件
            cart.setOrderItemList(orderItemList);
            //購物車物件新增到購物車列表中
            cartList.add(cart);
        }else{//該商品的存在
            //先通過購物車獲取當前購物車的商品明細物件
            List<TbOrderItem> orderItemList = cart.getOrderItemList();
            //判斷該商品是否存在於該購物車商品的明細列表中
            TbOrderItem orderItem = searchOrderIdByItemId(orderItemList,itemId);
            if (orderItem==null){//在商品列表中不存在
                //如果該商品不存在購物車明細列表中
                //則建立購物車明細物件,在新增到購物車明細裡列表中
                orderItem = createOrderItem(item,num);
                //在新增到購物車明細裡列表中
                orderItemList.add(orderItem);
            }else{//存在於商品列表中
                //修改購物車明細物件的數量和小計金額
                orderItem.setNum(orderItem.getNum()+num);
                //小計金額
                orderItem.setTotalFee(new BigDecimal(orderItem.getPrice().doubleValue()*orderItem.getNum()));

                //如果商品數量小於1 則刪除該商品
                if(orderItem.getNum()<1){
                    orderItemList.remove(orderItem);
                }
                //如果購物車商品明細列表中沒有商品了, 則直接從購物車裡列表中移除
                if (orderItemList.size()<=0){
                    cartList.remove(cart);
                }
            }
        }
        return cartList;
    }



    //判斷該商品是否存在於該購物車商品的明細列表中
    private TbOrderItem searchOrderIdByItemId(List<TbOrderItem> orderItems, Long itemId) {
        for (TbOrderItem orderItem : orderItems) {
            if(orderItem.getItemId().longValue()==itemId.longValue()){//存在商品列表中
                return orderItem;
            }
        }
        return null;
    }

    //建立商品的明細物件
    private TbOrderItem createOrderItem(TbItem item, Integer num) {
        //優化操作
        //建立商品的數量如果為負數
        if(num<1){
            throw new RuntimeException("新新增商品到購物車,商品數量不能小於1");
        }
        /*
          `item_id` bigint(20) NOT NULL COMMENT '商品id',
          `goods_id` bigint(20) DEFAULT NULL COMMENT 'SPU_ID',
          `title` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品標題',
          `price` decimal(20,2) DEFAULT NULL COMMENT '商品單價',
          `num` int(10) DEFAULT NULL COMMENT '商品購買數量',
          `total_fee` decimal(20,2) DEFAULT NULL COMMENT '商品總金額',
          `pic_path` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品圖片地址',
          `seller_id` varchar(100) COLLATE utf8_bin DEFAULT NULL,
         */
        TbOrderItem orderItem = new TbOrderItem();
        orderItem.setItemId(item.getId());
        orderItem.setGoodsId(item.getGoodsId());
        orderItem.setTitle(item.getTitle());
        orderItem.setPrice(item.getPrice());
        orderItem.setNum(num);
        orderItem.setTotalFee(new BigDecimal(item.getPrice().doubleValue()*num));
        orderItem.setPicPath(item.getImage());
        orderItem.setSellerId(item.getSellerId());
        return orderItem;
    }

    //根據商品的id查詢是否存在購物車物件
    private Cart searchCartBySellerId(List<Cart> cartList, String sellerId) {
        for (Cart cart : cartList) {
            if (cart.getSellerId().equals(sellerId)){
                return cart;
            }
        }
        return null;
    }
    //通過sessionid獲得購物車列表
    @Override
    public List<Cart> selectCartListFromRedis(String sessionId) {
        //從redis中獲取
        List<Cart> cartList = (List<Cart>) redisTemplate.boundValueOps(sessionId).get();
        //判斷cartList是否為空,因為你要返回一個list列表,如果為空,前臺就不能通過fastJson解析了
        if (cartList==null){
            //為空我們可以直接從新建立一個新的arrayList
            cartList = new ArrayList<>();
        }
        return cartList;
    }
    //儲存購物車列表到redis中
    @Override
    public void saveCartListToRedis(String sessionId, List<Cart> cartList) {
        redisTemplate.boundValueOps(sessionId).set(cartList,7L, TimeUnit.DAYS);
    }
    //合併登陸前購物車的資料到登陸後的購物車資料中
    @Override
    public List<Cart> mergeCartList(List<Cart> cartList_sessionId, List<Cart> cartList_username) {
        //注意我們只需要構建我們需要新增的資料,因為我們在上面已經做過判斷,我們只需要,拼裝資料即可
        for (Cart cart : cartList_sessionId) {
            //獲取購車列表
            List<TbOrderItem> itemList = cart.getOrderItemList();
            for (TbOrderItem orderItem : itemList) {
                //獲取num和ItemId的值
                Integer num = orderItem.getNum();
                Long itemId = orderItem.getItemId();
                //購物車列表我們直接用登陸的就可以了
                cartList_username=   addItemToCartList(cartList_username,itemId,num);
            }
        }
        return cartList_username;
    }
    //刪除登陸前購物車的資料
    @Override
    public void deleteCartList(List<Cart> cartList_sessionId) {
        redisTemplate.delete(cartList_sessionId);
    }
}

controller層:

思路分析:我們主要做兩件事    1.購物車列表資料的展示     2.新增商品到購物車列表中   還有一個非常重要就是獲取sessionid的方法,我們瀏覽器的存在的cookie是一次會話,我們想要使用者的商品儲存一週,我們必須後臺進行,重新建立cookie,設定為一週過期,通過一個工具類

CookieUtil來獲取和儲存

@RestController
@RequestMapping("/cart")
public class CartController {
    @Autowired
    private HttpSession session;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;
    @Reference
    private CartService cartService;
    /**
     * 獲得sessionid的方法
     */
    private String getSessionId(){

        //先嚐試從"cartCookie"中獲得sessionId資訊
        String sessionId = CookieUtil.getCookieValue(request, "cartList", "utf-8");
        if (sessionId==null){
            //在從瀏覽器中獲取sessionid
             sessionId = session.getId();
             //將瀏覽器獲取的sessionId儲存一週
            CookieUtil.setCookie(request,response,"cartList",sessionId,3600*24*7,"utf-8");
        }
        return sessionId;
    }
    /**
     * 展示購物車列表資料
     */
    @RequestMapping("/findCartList")
    public List<Cart> findCartList(){
        //獲取登陸人使用者名稱
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        //未登陸時,基於sessionid從redis中獲取購物車資料列表
        String sessionId = getSessionId();
        //從redis中獲取
        List<Cart>  cartList_sessionId =  cartService.selectCartListFromRedis(sessionId);
        if ("anonymousUser".equals(username)){//未登陸
            System.out.println("selectCartListFromRedis by sessionId....");
            return cartList_sessionId;
        }else{//已登入
            System.out.println("selectCartListFromRedis by username....");
            List<Cart>  cartList_username =  cartService.selectCartListFromRedis(username);
            //使用者登入前,如果已經新增商品到購物車列表中。
            if(cartList_sessionId!=null&&cartList_sessionId.size()>0){//說明新增前已經存在商品了
                //登陸後,將登陸前的購物車列表資料合併到登陸後的購物車列表中
             cartList_username = cartService.mergeCartList(cartList_sessionId,cartList_username);
             //將合併後的結果從新放到快取中
                cartService.saveCartListToRedis(username,cartList_username);
                //清除合併前的購物車列表資料
                cartService.deleteCartList(cartList_sessionId);
            }
            return cartList_username;
        }

    }

    /**
     * 新增商品到購物車
     */
    @RequestMapping("/addItemToCartList")
    public Result addItemToCartList(Long itemId, Integer num){
        try {

            //獲取登陸人使用者名稱
            String username = SecurityContextHolder.getContext().getAuthentication().getName();
            System.out.println(username);
            //獲取sessionId
            String sessionId = getSessionId();
            //1.查詢購物車列表
            List<Cart> cartList = findCartList();
            if ("anonymousUser".equals(username)){//未登陸
                System.out.println("saveCartListToRedis by sessionId.....");
                //2.新增商品到購物車
                cartList =  cartService.addItemToCartList(cartList,itemId, num);
                //3.儲存購物車列表到redis中
                cartService.saveCartListToRedis(sessionId,cartList);
            }else{//已登入
                System.out.println("saveCartListToRedis by username.....");
                cartService.saveCartListToRedis(username,cartList);

            }

            return new Result(true,"新增購物車成功");
        } catch (RuntimeException e) {
            e.printStackTrace();
            return new Result(false,e.getMessage());
        }
        catch (Exception e) {
            e.printStackTrace();
            return new Result(false,"新增購物車失敗");
        }
    }

}

前臺實現:

service層:

//服務層
app.service('cartService',function($http){
	    	
	//查詢購物車列表資料
	this.findCartList=function(){
		return $http.get('cart/findCartList.do');
	}

	//查詢購物車列表資料
	this.addItemToCartList=function (itemId,num) {
		return $http.get('cart/addItemToCartList.do?itemId='+itemId+'&num='+num)
    }

});

controller層:

 //控制層 
app.controller('cartController' ,function($scope,$controller   ,cartService){
	
	$controller('baseController',{$scope:$scope});//繼承
	
    //讀取列表資料繫結到表單中  
	$scope.findCartList=function(){
        cartService.findCartList().success(
			function(response){
				$scope.cartList=response;
				sum();
			}			
		);
	}

	//新增商品到購物車
	$scope.addItemToCartList=function (itemId,num) {
		cartService.addItemToCartList(itemId,num).success(function (response) {
			if (response.success){
				//新增購物車工程
                $scope.findCartList();
			}else{
				//新增購物車失敗
				alert(response.message)
			}
        })
    }
	//統計商品的數量和總計
	sum=function () {
		//總數量 和總金額
		$scope.totalNum = 0;
		$scope.totalMoney=0.00;
		//遍歷購物車列表
		for (var i = 0;i<$scope.cartList.length;i++){
			//獲取購物車物件
			var cart = $scope.cartList[i];
			var orderItemList = cart.orderItemList;//獲取商品明細列表
			//遍歷商品購物車明細列表
			for(var j = 0;j<orderItemList.length;j++){
				$scope.totalNum = orderItemList[i].num;
				$scope.totalMoney= orderItemList[i].totalFee;
			}
		}
    }

});	

1.頁面的設定:

注意:如果我們未登陸,則要新增購物車,我們需要獲取使用者名稱:則通過一個spring-security.xml的配置

//之前增加的放行,刪掉,否則所有訪問cart下的訪問都不進入到springSecurity中
<http pattern="/cart/*.do" security="none"></http>
//但是我們需要購物車可以不登入也能增加到購物車中的操作,IS_AUTHENTICATED_ANONYMOUSLY和上面的配置區別在於,可以匿名訪問springSecurity(加在入口點引用中,注意攔截規則小範圍的在上面)
<intercept-url pattern="/cart/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"/> 
//在此攔截器上<intercept-url pattern="/**" access="ROLE_USER"/> 

2.頁面跳轉的問題

思路分析:我們設定一個login.html頁面這個頁面就做一件事,就是用於轉發跳轉到cart.html頁面,因為我們在spring-sceurity.xml中沒有放行,所以會先進入cas登陸介面,我們登陸後,會轉發到cart.html頁面

程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跳轉頁面</title>
    <script type="text/javascript">
        location.href="cart.html";
    </script>
</head>
<body>

</body>
</html>