taotao購物車2 解決購物車本地cookie和服務器redis不一樣步的問題

下面的思路邏輯必定要理清楚,比較繞html

思路; java

前面已經實現了在cookie本地維護購物車的功能,web

此次加入和服務器同步功能,redis

由於 購物車 操做比較頻繁,因此,後臺服務器 用redis存儲用戶的購物車信息spring

邏輯是:apache

寫一個後臺操做redis的接口(也能夠寫兩個):要實現的功能是json

1.經過用戶id從redis中取出用戶的購物車信息(購物車商品集合)服務器

2.經過用戶id向redis中寫入用戶的購物車信息cookie

 

1、用戶向購物車中添加商品時的邏輯:app

判斷用戶是否登陸

  若是沒有登陸,則繼續只操做cookie

  若是用戶登陸了,則調用遠程接口獲取用戶的 redis中的購物車信息 redisList,再獲取cookie的購物車信息cookieList

    將這兩個購物車中的商品合併(相同商品數量相加,不一樣商品都添加)組成新的購物車集合 newLIst

    而後再 將當期 要加入購物車的商品 和 newList 比較,若是已經存在,則增長數量,若是不存在,則增長商品,

    最後將 newList 調用遠程接口,寫入redis中

 

2、用戶修改購物車中商品數量時的邏輯

  修改購物車中商品的數量,必定是在已經打開購物車列表頁面,那麼也就是說,這時,確定已經明確過了用戶是否登陸,

  也就是說,若是用戶已經登陸了,那麼這時本地cookie和redis中的購物車必定是同步的

  因此,這時,

     不用先和遠程redis同步,能夠先修改本地cookie的商品數量,即cookieList,

    等修改完成後,再只執行遠程向redis中寫入購物車newList便可

 

3、刪除購物車中的商品,邏輯 基本 等同於 修改 購物車商品數量,即,只在修改完本地 cookie的list後,向redis寫入便可

 

4、展現購物車列表邏輯:

  先判斷用戶是否登陸,

    若是用戶沒有登陸,則只從cookie中取出cookieList 返回前臺展現便可;

    若是用戶已經登陸,則只須要從redis中取出 reidsList,返回前臺展現便可

    (緣由是:咱們這個方法只是展現購物車列表,並不會操做購物車列表,即不會產生本地cookie和redis中不一樣步的問題,而前面說過,只要會產生本地cookie和遠程redis不一樣步的方法,咱們在方法結束前都已經作過了同步,因此,這裏咱們要展現購物車列表,只須要從redis中取redisList便可,由於這時redisList和cookieList必定是同步的,因此,若是用戶已經登陸,那麼展現列表咱們根本不用考慮本地cookieList) 

 

portal門戶項目:

Controller代碼:

package com.taotao.portal.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.taotao.common.pojo.CartItem;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.portal.service.CartService;

/**
 * 購物車的Controller
 * @author Administrator
 */
@Controller
@RequestMapping("/cart")
public class CartController {
    
    @Autowired
    private CartService cartService;
    /**
     * 添加商品到購物車
     * @param itemId
     * @param num
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/add/{itemId}")
    public String addCartItem(@PathVariable Long itemId,
            @RequestParam(defaultValue="1")Integer num,
            HttpServletRequest request,HttpServletResponse response) {
        cartService.addCartItem(itemId, num, request, response);
//        return "cartSuccess"; //這樣會打開新的頁面,因此不用
        //爲了 避免每次添加完商品都打開新的頁面,因此這裏這樣重定向
        return "redirect:/cart/success.html";
//        return "forward:/cart/success.html"; //這種寫法也能夠(請求轉發)
    }
    
    //接收重定向的請求返回jsp頁面
    @RequestMapping("/success")
    public String showSuccess(){
        return "cartSuccess";
    }
    
    /**
     * 查詢購物車中的商品列表返回前臺展現
     * @param request
     * @param response
     * @param model
     * @return
     */
    @RequestMapping("/cart")
    public String showCart(HttpServletRequest request,HttpServletResponse response,Model model){
        List<CartItem> cartItemList = cartService.getCartItemList(request, response);
        model.addAttribute("cartList", cartItemList);
        return "cart";
    }
    
    /**
     * 修改商品數量(包括點加減號和收到寫值)
     * @param itemId
     * @param num
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/update/{itemId}")
    @ResponseBody
    public String updateCartItemNum(@PathVariable Long itemId,
            @RequestParam(defaultValue="0")Integer num,
            HttpServletRequest request,HttpServletResponse response) {
        cartService.updateCartItmeNum(itemId, num, request, response);
//        return "redirect:/cart/cart.html";
        return "";
    }
    
    /**
     * 刪除購物車中的指定商品
     * @param itemId
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/delete/{itemId}")
    public String delCartItem(@PathVariable Long itemId,
            HttpServletRequest request,HttpServletResponse response){
        cartService.delCartItem(itemId, request, response);
        return "redirect:/cart/cart.html";
    }
}

 

Service層代碼:

package com.taotao.portal.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.taotao.common.pojo.CartItem;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.common.utils.CookieUtils;
import com.taotao.common.utils.HttpClientUtil;
import com.taotao.common.utils.JsonUtils;
import com.taotao.pojo.TbItem;
import com.taotao.pojo.TbUser;
import com.taotao.portal.service.CartService;
import com.taotao.portal.service.UserService;
/**
 * 購物車service
 * @author Administrator
 */
@Service
public class CartServiceImpl implements CartService {

    //rest服務基礎url
    @Value("${REST_BASE_URL}")
    private String REST_BASE_URL;
    //獲取商品信息的url
    @Value("${ITEM_INFO_URL}")
    private String ITEM_INFO_URL;
    
    //同步購物車的url
    @Value("${REST_CART_SYNC_URL}")
    private String REST_CART_SYNC_URL;
    
    @Autowired
    private UserService userService;
    
    /**
     * 添加購物車商品
     * 
        一、接收controller傳遞過來的商品id,根據商品id查詢商品信息。
        二、從cookie中取出購物車信息,轉換成商品pojo列表。
        三、把商品信息添加到商品列表中。
        參數:
        一、商品id
        二、Request
        三、response
        返回值:
        TaoTaoResult
     */
    @Override
    public TaotaoResult addCartItem(long itemId, int num,
            HttpServletRequest request,HttpServletResponse response) {
        
        //合併購物車
        Map<String, Object> map = mergeCart(request);
        List<CartItem> newList = (List<CartItem>) map.get("list");
        
        //當前id對應的購物車商品
        CartItem cartItem = null;
        //先判斷原購物車列表中是否有當前商品
        for (CartItem item : newList) {
            //若是存在
            if (item.getId()==itemId) {
                //增長商品數量
                item.setNum(item.getNum()+num);
                cartItem = item;
                break;
            }
        }
        //若是原來沒有此商品,則新建
        if (cartItem==null) {
            cartItem = new CartItem();
            //調用rest服務獲取商品信息
            String doGetResult = HttpClientUtil.doGet(REST_BASE_URL+ITEM_INFO_URL+itemId);
            TaotaoResult taoTaoResult = TaotaoResult.formatToPojo(doGetResult, TbItem.class);
            if (taoTaoResult.getStatus()==200) {
                //封裝成購物車商品對象
                TbItem item = (TbItem) taoTaoResult.getData();
                cartItem.setId(itemId);
                cartItem.setImage(item.getImage()==null?"":item.getImage().split(",")[0]);
                cartItem.setPrice(item.getPrice());
                cartItem.setTitle(item.getTitle());
                cartItem.setSellPoint(item.getSellPoint());
                cartItem.setNum(num);
                //將商品加入到購物車列表中
                newList.add(cartItem);
            }
        }
        
        //若是登陸則向redis發送數據同步購物車
        Boolean isLogin = (Boolean) map.get("isLogin");
        if (isLogin) {
            Long userId = (Long) map.get("userId");
            syncCart(userId, JsonUtils.objectToJson(newList));
        }
        //將購物車列表寫回cookie
        String listJson = JsonUtils.objectToJson(newList);
        //最後一個參數 true,對數據進行編碼,這樣在 cookie中就看不到中文原始數據了
        CookieUtils.setCookie(request, response, "TT_CART", listJson,true);
        return TaotaoResult.ok();
    }

    //合併購物車的方法
    private Map<String, Object> mergeCart(HttpServletRequest request) {
        
        List<CartItem> cookieCartList = getCartItemList(request);
        Map<String, Object> map = new HashMap<>();
        //是否登陸
        Boolean isLogin = false;
        Long userId = null;
        
        List<CartItem> newList;
        //準備合併redis和當前cookie的購物車
        //先判斷用戶是否登陸
        TbUser currentUser = getCurrentUser(request);
        if (currentUser!=null) {
            //若是登陸,先獲取redis中的購物車
            userId = currentUser.getId();
            isLogin = true;
            List<CartItem> redisCart = syncCart(userId, null);
            if (redisCart!=null&&redisCart.size()>0) {
                //將兩個購物車列表合併
                Iterator<CartItem> it = redisCart.iterator();
                //遍歷redis購物車,對比,若是有和cookie同樣的商品,則把數量加到Cookie中,刪除自身
                while (it.hasNext()) {
                    CartItem redisItem = it.next();
                    for (CartItem cookieItem : cookieCartList) {
                        if (redisItem.getId().equals(cookieItem.getId())) {
                            System.out.println(redisItem.getId());
                            System.out.println(cookieItem.getId());
                            cookieItem.setNum(redisItem.getNum()+cookieItem.getNum());
                            //從resisList中刪除
                            it.remove();
                            break;
                        }
                    }
                }
            }
            newList = new ArrayList<CartItem>();
            //合併
            if (redisCart!=null && redisCart.size()>0) {
                newList.addAll(redisCart);
            }
            if (cookieCartList!=null && cookieCartList.size()>0) {
                newList.addAll(cookieCartList);
            }
            //向redis發送數據同步購物車
            syncCart(userId, JsonUtils.objectToJson(newList));
        }else{
            //用戶沒有登陸時
            newList = cookieCartList;
        }
        
        map.put("list", newList);
        map.put("isLogin", isLogin);
        map.put("userId", userId);
        
        return map;
    
        
    }
    
    /**
     * 從cookie中取商品列表
     * @param request
     * @return
     */
    private List<CartItem> getCartItemList(HttpServletRequest request) {
        //從cookie中取商品列表
        String cartJson = CookieUtils.getCookieValue(request, "TT_CART",true);
        if (cartJson==null) {
            return new ArrayList<CartItem>();
        }
        //把json轉換爲商品列表
        try {
            List<CartItem>  list = JsonUtils.jsonToList(cartJson, CartItem.class);
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList<CartItem>();
    }

    /**
     * 獲取 購物車商品列表供前臺展現
     */
    @Override
    public List<CartItem> getCartItemList(HttpServletRequest request, HttpServletResponse response) {
        //展現列表的邏輯:
        //判斷用戶是否登陸,若是未登陸,直接取cookie中的數據
        //若是已登陸,直接取redis中的數據(不涉及到合併及同步問題,由於這個只是展現,合併只有在 用戶添加商品到購物車時、用戶修改購物車時才須要)
        //定義要返回前臺的數據
        List<CartItem> list = null;
        //判斷用戶是否已經登陸,若是登陸了,再同步
        TbUser currentUser = getCurrentUser(request);
        if (currentUser!=null) {
            //若是登陸則從redis中取數據到前臺
            list = syncCart(currentUser.getId(),null);
        }else{
            list = getCartItemList(request);
        }
        return list;
    }
    
    /**
     * 直接輸入數量,更新購物車中商品的數量(在購物車列表展現頁面中使用)
     */
    @Override
    public TaotaoResult updateCartItmeNum(long itemId, int num,
            HttpServletRequest request, HttpServletResponse response){
        //執行此方法是,必定是已經打開了購物車列表頁,也就是必定能夠肯定了用戶是否已經登陸
        //先進行未登陸的正常邏輯
        //當前id對應的購物車商品
        CartItem cartItem = null;
        //從cookie中獲取購物車列表
        List<CartItem> cartList = getCartItemList(request);
        
        //先判斷原購物車列表中是否有當前商品
        for (CartItem item : cartList) {
            //若是存在
            if (item.getId()==itemId) {
                //修改商品數量
                item.setNum(num);
                cartItem = item;
                break;
            }
        }
        
        //判斷用戶是否已經登陸,若是登陸了,再同步
        TbUser currentUser = getCurrentUser(request);
        if (currentUser!=null) {
            //若是登陸則向redis發送數據同步購物車
            syncCart(currentUser.getId(), JsonUtils.objectToJson(cartList));
        }
        
        //將購物車列表寫回cookie
        String listJson = JsonUtils.objectToJson(cartList);
        //最後一個參數 true,對數據進行編碼,這樣在 cookie中就看不到中文原始數據了
        CookieUtils.setCookie(request, response, "TT_CART", listJson,true);
        return TaotaoResult.ok();
    }
    
    /**
     * 刪除購物車中商品
     * @param itemId
     * @param request
     * @param response
     * @return
     */
    @Override
    public TaotaoResult delCartItem(long itemId,
            HttpServletRequest request, HttpServletResponse response){
        
        //執行此方法是,必定是已經打開了購物車列表頁,也就是必定能夠肯定了用戶是否已經登陸
        //當前id對應的購物車商品
        CartItem cartItem = null;
        //從cookie中獲取購物車列表
        List<CartItem> cartList = getCartItemList(request);
        
        Iterator<CartItem> iterator = cartList.iterator();
        //遍歷
        while (iterator.hasNext()) {
            CartItem item = iterator.next();
            //找到對應的商品
            if (item.getId()==itemId) {
                //執行刪除動做
                iterator.remove();
                break;
            }
        }
        //判斷用戶是否已經登陸,若是登陸了,再同步
        TbUser currentUser = getCurrentUser(request);
        if (currentUser!=null) {
            //若是登陸則向redis發送數據同步購物車
            syncCart(currentUser.getId(), JsonUtils.objectToJson(cartList));
        }
                
        //將購物車列表寫回cookie
        String listJson = JsonUtils.objectToJson(cartList);
        //最後一個參數 true,對數據進行編碼,這樣在 cookie中就看不到中文原始數據了
        CookieUtils.setCookie(request, response, "TT_CART", listJson,true);
        return TaotaoResult.ok();
    }
    
    /**
     * 從redis中獲取購物車信息/同步購物車
     * @param userId
     * @param cartList 當此參數爲null時,爲獲取redis中的購物車信息;不然爲向redis中同步購物車信息
     * @return
     */
    private List<CartItem> syncCart(long userId, String cartList){
        //定義返回值
        List<CartItem> returnList = new ArrayList<CartItem>();
        
        HashMap<String, String> map = new HashMap<String,String>();
        map.put("userId", userId+"");
        map.put("cartList", cartList);
        String url = REST_BASE_URL+REST_CART_SYNC_URL;
        String json = HttpClientUtil.doPost(url, map);
        if (StringUtils.isNotEmpty(json)) {
            TaotaoResult taotaoResult = TaotaoResult.formatToList(json, CartItem.class);
            if (taotaoResult.getStatus()==200) {
                returnList = (ArrayList<CartItem>) taotaoResult.getData();
            }
        }
        return returnList;
    }
    
    //獲取當前用戶信息
    public TbUser getCurrentUser(HttpServletRequest request) {
        //判斷用戶是否登陸
        //從cookie中取token
        String token = CookieUtils.getCookieValue(request, "TT_TOKEN");
        //根據token換取用戶信息,調用sso系統的接口
        ///這裏須要寫一些業務 邏輯,不要在攔截器中寫,單寫一個service,只在這裏注入並調用
        TbUser user = userService.getUserByToken(token);
        return user;
    }

}

配置文件:

#服務層屬性定義
#服務層基礎 url
REST_BASE_URL = http://localhost:8081/rest
#首頁大 廣告位
REST_INDEX_AD_URL = /content/list/89
#同步購物車的url
REST_CART_SYNC_URL=/cart/syncCart

 

cart.js代碼:

var TTCart = {
    load : function(){ // 加載購物車數據
        
    },
    itemNumChange : function(){
        $(".increment").click(function(){//
            var _thisInput = $(this).siblings("input");
            _thisInput.val(eval(_thisInput.val()) + 1);
            $.post("/cart/update/"+_thisInput.attr("itemId")+".html?num="+_thisInput.val(),function(data){
                TTCart.refreshTotalPrice();
            });
        });
        $(".decrement").click(function(){//-
            var _thisInput = $(this).siblings("input");
            if(eval(_thisInput.val()) == 1){
                return ;
            }
            _thisInput.val(eval(_thisInput.val()) - 1);
            $.post("/cart/update/"+_thisInput.attr("itemId")+".html?num="+_thisInput.val(),function(data){
                TTCart.refreshTotalPrice();
            });
        });
        $(".quantity-form .quantity-text").rnumber(1);//限制只能輸入數字
        $(".quantity-form .quantity-text").change(function(){
            var _thisInput = $(this);
            $.post("/cart/update/"+_thisInput.attr("itemId")+".html?num="+_thisInput.val(),function(data){
                debugger
                TTCart.refreshTotalPrice();
            });
        });
    },
    refreshTotalPrice : function(){ //從新計算總價
        var total = 0;
        $(".quantity-form .quantity-text").each(function(i,e){
            var _this = $(e);
            total += (eval(_this.attr("itemPrice")) * 10000 * eval(_this.val())) / 10000;
        });
        $(".totalSkuPrice").html(new Number(total/100).toFixed(2)).priceFormat({ //價格格式化插件
             prefix: '¥',
             thousandsSeparator: ',',
             centsLimit: 2
        });
    }
};

$(function(){
    TTCart.load();
    TTCart.itemNumChange();
});

 

而後是遠程接口服務層代碼:

Controller層:

package com.taotao.rest.controller;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.taotao.common.pojo.CartItem;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.rest.service.CartService;

/**購物車Controller
 * @author Administrator
 *
 *當用戶登陸時,同步cookie購物車和redis中購物車的 商品list
 *也能夠在每次購物車中商品數量、添加商品、刪除商品、清空購物車時,都調用這個同步方法
 */
@Controller
@RequestMapping("/cart")
public class CartCotroller {
    
    @Autowired
    private CartService cartService;
    
    @RequestMapping("/syncCart")
    @ResponseBody
    public TaotaoResult syncCart(long userId,String cartList) {
        /**
         先拿到cookie中的購物車商品列表listCookie,而後再拿到redis中的購物車商品列表listRedis
        而後遍歷比對,將二者合併,合併時以cookie中的爲準,
        具體規則:
            若是c中有而r中沒有,則合併後都有
            若是c中沒有而r中有,則合併後都有
            二者都有時,把數量相加
         */
        /**
         * 由於這裏是rest項目只提供接口,不方便操做cookie,因此這裏只提供以下方法,其餘邏輯在 portal項目中完成
         一種是前臺根據用戶id獲取redis中存儲的 購物車信息(適用於用戶登陸時)
         一種是前臺向後臺提供 購物車中的商品集合,後臺寫入redis
         */
        List<CartItem> list = cartService.syncCart(userId, cartList);
        return TaotaoResult.ok(list);
    }
}

 

service層:

package com.taotao.rest.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.taotao.common.pojo.CartItem;
import com.taotao.common.utils.JsonUtils;
import com.taotao.rest.dao.JedisClient;
import com.taotao.rest.service.CartService;
@Service
public class CartServiceImpl implements CartService {
    
    @Value("REDIS_CART_LIST_KEY")
    private String REDIS_CART_LIST_KEY;
    
    @Autowired
    private JedisClient jedisClient;

    @Override
    public List<CartItem> syncCart(long userId, String cartList) {
        /**
         * 由於這裏是rest項目只提供接口,不方便操做cookie,因此這裏只提供以下方法,其餘邏輯在 portal項目中完成
         一種是前臺根據用戶id獲取redis中存儲的 購物車信息(適用於用戶登陸時)
         一種是前臺向後臺提供 購物車中的商品集合,後臺寫入redis
         */
        //定義返回值
        List<CartItem> list = new ArrayList<CartItem>();
        
        if (StringUtils.isEmpty(cartList)) {
            //說明是用戶請求list
            //從redis中取出用戶的購物車信息返回給前臺
            String json = jedisClient.hget(REDIS_CART_LIST_KEY, userId+"");
            if (StringUtils.isNotEmpty(json)) {
                list = JsonUtils.jsonToList(json, CartItem.class);
            }
        }else{
            //說明是用戶向後臺提供購物成信息,準備保存到redis中
            jedisClient.hset(REDIS_CART_LIST_KEY, userId+"", cartList);
        }
        return list;
    }

}

 

配置文件:

#購物車商品列表在redis中保存的key
REDIS_CART_LIST_KEY=REDIS_CART_LIST_KEY
相關文章
相關標籤/搜索