下面的思路邏輯必定要理清楚,比較繞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