今日目標:
(1)理解品優購購物車的實現思路
(2)運用Cookie存儲購物車
(3)編寫購物車前端代碼
(4)運用 Redis 存儲購物車
目錄
1、購物車工程搭建
1.1 需求分析
用戶在商品詳細頁點擊加入購物車,提交商品SKU編號和購買數量,添加到購物車。購物車展示頁面如下:
1.2 工程搭建
(1)參考之前的工程進行搭建(分別有:pinyougou-cart-interface、pinyougou-cart-service和pinyougou-cart-web),其中cart-web參考user-web
(2)創建購物車組合實體類
package com.pinyougou.pojogroup;
import com.pinyougou.pojo.TbOrderItem;
import java.io.Serializable;
import java.util.List;
/**
* 購物車組合實體類
* Author xushuai
* Description
*/
public class Cart implements Serializable {
/** 商家ID */
private String sellerId;
/** 商家名稱 */
private String sellerName;
/** 購物車商品明細列表 */
private List<TbOrderItem> orderItemList;
}
2、Cookie存儲購物車
2.1 需求分析
使用cookie存儲購物車數據。服務層負責邏輯,控制層負責讀寫cookie 。
2.2 後端-添加商品到購物車
(1)服務層接口(cart-interface),新增CartService
package com.pinyougou.cart.service;
import com.pinyougou.pojogroup.Cart;
import java.util.List;
/**
* 購物車接口
* Author xushuai
* Description
*/
public interface CartService {
/**
* 添加商品到購物車中
*
* @param cartList 購物車列表
* @param itemId 商品ID
* @param num 添加的數量
* @return java.util.List<com.pinyougou.pojogroup.Cart>
*/
List<Cart> addGoodsToCartList(List<Cart> cartList, Long itemId, Integer num);
}
(2)服務層實現(cart-service),新增CartServiceImpl
package com.pinyougou.cart.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.cart.service.CartService;
import com.pinyougou.mapper.TbItemMapper;
import com.pinyougou.pojo.TbItem;
import com.pinyougou.pojo.TbOrderItem;
import com.pinyougou.pojogroup.Cart;
import exception.PinyougouException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 購物車實現
* Author xushuai
* Description
*/
@Service
@Transactional
public class CartServiceImpl implements CartService {
@Autowired
private TbItemMapper itemMapper;
@Override
public List<Cart> addGoodsToCartList(List<Cart> cartList, Long itemId, Integer num) {
/*
* 1.使用SKUID查詢SKU商品對象
* 2.使用商品對象獲取商家信息
* 3.根據商家ID在購物車列表中查詢購物車對象
* 4.購物車列表中不存在該商家的購物車
* 4.1 創建新的購物車對象,將新的購物車對象添加到購物車列表
* 5.購物車列表中存在該商家的購物車
* 5.1 判斷該購物車是否存在該商品的明細
* 5.1.1 不存在,新增該明細到明細列表
* 5.1.2 存在,在原有的數量上加上新增的數量,並更改金額
*/
// 1.查詢SKU商品對象
TbItem item = itemMapper.selectByPrimaryKey(itemId);
if (item == null) {
throw new PinyougouException("商品不存在!");
}
if (!item.getStatus().equals(TbItem.STATUS_NORMAL)) {
throw new PinyougouException("商品狀態異常");
}
// 2.獲取商家信息
String sellerId = item.getSellerId();
String sellerName = item.getSeller();
// 3.根據商家ID查詢購物車中的對象
Cart cart = searchCartBySellerId(cartList, sellerId);
if (cart == null) {// 4.購物車列表中不存在該商家的購物車
// 創建購物車對象
cart = new Cart();
cart.setSellerId(sellerId);
cart.setSellerName(sellerName);
List<TbOrderItem> orderItemList = new ArrayList<>();
// 使用item對象生成購物車明細
TbOrderItem tbOrderItem = itemToOrderItem(item, num);
orderItemList.add(tbOrderItem);
cart.setOrderItemList(orderItemList);
// 將購物車放入購物車列表
cartList.add(cart);
} else {// 5.購物車列表中存在該商家的購物車
// 5.1 判斷該購物車是否存在該商品的明細
TbOrderItem orderItem = searchOrderItemByItemId(cart.getOrderItemList(), item.getId());
if (orderItem == null) {// 5.1.1 不存在,新增該明細到明細列表
orderItem = itemToOrderItem(item, num);
// 添加到明細列表
cart.getOrderItemList().add(orderItem);
} else {// 5.1.2 存在,在原有的數量上加上新增的數量,並更改金額
// 判斷操作後的購物車情況
if (orderItem.getNum() + num < 1) {// 數量小於1
// 移除該明細
cart.getOrderItemList().remove(orderItem);
if (cart.getOrderItemList().size() == 0) {// 明細列表中無數據
// 移除該購物車
cartList.remove(cart);
}
}
// 修改數量
orderItem.setNum(orderItem.getNum() + num);
// 修改金額
orderItem.setTotalFee(BigDecimal.valueOf(orderItem.getPrice().doubleValue() * orderItem.getNum()));
}
}
return cartList;
}
/**
* 根據itemId查詢購物車明細列表
*
* @param orderItemList 購物車明細列表
* @param itemId 商品ID
* @return com.pinyougou.pojo.TbOrderItem
*/
private TbOrderItem searchOrderItemByItemId(List<TbOrderItem> orderItemList, Long itemId) {
// 遍歷購物車明細列表
for (TbOrderItem orderItem : orderItemList) {
if (orderItem.getItemId().longValue() == itemId.longValue()) {
return orderItem;
}
}
return null;
}
/**
* 將item對象轉換爲OrderItem對象
*
* @param item 商品對象
* @param num 數量
* @return com.pinyougou.pojo.TbOrderItem
*/
private TbOrderItem itemToOrderItem(TbItem item, Integer num) {
TbOrderItem orderItem = new TbOrderItem();
orderItem.setGoodsId(item.getGoodsId());
orderItem.setItemId(item.getId());
orderItem.setNum(num);
orderItem.setPicPath(item.getImage());
orderItem.setPrice(item.getPrice());
orderItem.setSellerId(item.getSellerId());
orderItem.setTitle(item.getTitle());
orderItem.setTotalFee(BigDecimal.valueOf(item.getPrice().doubleValue() * num));
return orderItem;
}
/**
* 根據商家ID查詢購物車列表
*
* @param cartList 購物車列表
* @param sellerId 商家ID
* @return com.pinyougou.pojogroup.Cart
*/
private Cart searchCartBySellerId(List<Cart> cartList, String sellerId) {
// 遍歷購物車列表
for (Cart cart : cartList) {
if (cart.getSellerId().equals(sellerId)) {
return cart;
}
}
return null;
}
}
(3)控制層(cart-web),新增CartController
package com.pinyougou.cart.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.pinyougou.cart.service.CartService;
import com.pinyougou.pojogroup.Cart;
import entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import util.CookieUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 購物車控制層
* Author xushuai
* Description
*/
@RestController
@RequestMapping("/cart")
public class CartController {
/** cookieName:cookie名稱 */
private final String CARTLIST_COOKIENAME = "cartList";
/** cookie_maxAge:cookie存活時間 */
private final int MAXAGE_COOKIE = 3600 * 60;
@Reference
private CartService cartService;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
/**
* 添加商品到購物車
*
* @param itemId 商品ID
* @param num 數量
* @return entity.Result
*/
@RequestMapping("/addGoodsToCartList")
public Result addGoodsToCartList(Long itemId, Integer num) {
try {
List<Cart> cartList = findCartList();
// 添加商品到購物車
cartList = cartService.addGoodsToCartList(cartList, itemId, num);
// 將購物車列表轉換爲json字符串
String cookieValue = JSON.toJSONString(cartList);
// 將購物車添加到cookie中
CookieUtil.setCookie(request, response, CARTLIST_COOKIENAME, cookieValue, MAXAGE_COOKIE, "UTF-8");
return Result.success("添加商品到購物車成功");
}catch (Exception e){
e.printStackTrace();
return Result.error("添加商品到購物車失敗");
}
}
/**
* 獲取購物車列表
*
* @return java.util.List<com.pinyougou.pojogroup.Cart>
*/
@RequestMapping("/findCartList")
public List<Cart> findCartList() {
// 從cookie中獲取購物車列表
String cartListJson = CookieUtil.getCookieValue(request, CARTLIST_COOKIENAME, "UTF-8");
List<Cart> cartList = JSON.parseArray(cartListJson, Cart.class);
return cartList;
}
}
2.3 購物車列表展示
(1)前端:編寫cartService.js
//購物車服務層
app.service('cartService', function ($http) {
//購物車列表
this.findCartList = function () {
return $http.get('cart/findCartList.do');
}
});
(2)前端:編寫cartController.js
//購物車控制層
app.controller('cartController', function ($scope, cartService) {
//查詢購物車列表
$scope.findCartList = function () {
cartService.findCartList().success(
function (response) {
$scope.cartList = response;
}
);
}
});
(3)頁面引入JS文件和基礎指令,並初始化執行查詢購物車列表方法
(4)頁面綁定變量
(5)添加商品到購物車
(6)查看購物車
2.4 商品數量加減
(1)前端:在cartService.js中新增方法
//添加商品到購物車
this.addGoodsToCartList = function (itemId, num) {
return $http.get('cart/addGoodsToCartList.do?itemId=' + itemId + '&num=' + num);
}
(2)前端:在cartController.js中新增方法
//添加商品到購物車
$scope.addGoodsToCartList = function (itemId, num) {
cartService.addGoodsToCartList(itemId, num).success(
function (response) {
if (response.success) {
$scope.findCartList();//刷新列表
} else {
alert(response.message);//彈出錯誤提示
}
}
);
}
(3)前端:頁面 加減號 綁定單擊事件
2.5 計算總金額和總數量
(1)前端:在cartService.js中新增方法
// 合計金額
this.sum = function (cartList) {
// 返回結果集
var totalValue = {totalNum: 0, totalMoney: 0};
// 遍歷購物車列表
for (var i = 0; i < cartList.length; i++) {
var cart = cartList[i];
for (var j = 0; j < cart.orderItemList.length; j++) {
// 累加數量
totalValue.totalNum += cart.orderItemList[j].num;
// 累加金額
totalValue.totalMoney += cart.orderItemList[j].totalFee;
}
}
return totalValue;
}
(2)前端:在cartController.js中的findCartList中新增邏輯
(3)頁面綁定變量
3、Redis 存儲購物車
3.1 需求分析
判斷當前用戶是否登陸,如果未登錄採用Cookie存儲,如果登錄則採用Redis存儲。登錄後要進行Cookie購物車與Redis購物車的合併操作,並清除Cookie購物車。
3.2 後端代碼
(1)後端:修改放行cart/*.do的配置,將其配置爲匿名角色訪問權限
(2)後端:服務層接口(cart-interface),在CartService中新增方法
/**
* 從redis中獲取購物車列表
*
* @param
* @return java.util.List<com.pinyougou.pojogroup.Cart>
*/
List<Cart> findCartListFromRedis(String username);
/**
* 將購物車列表保存到redis中
*
* @param username 當前登錄用戶名
* @param cartList 購物車列表
*/
void saveCartListToRedis(String username, List<Cart> cartList);
(3)後端:服務層實現(cart-service),在CartServiceImpl中新增實現
@Override
public List<Cart> findCartListFromRedis(String username) {
System.out.println("從redis中獲取當前用戶的購物車");
// 從購物車中獲取
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps(REDIS_CARTLIST_KEY).get(username);
if (cartList == null) {
cartList = new ArrayList<>();
}
return cartList;
}
@Override
public void saveCartListToRedis(String username, List<Cart> cartList) {
System.out.println("將購物車存入redis中");
// 將購物車保存到redis
redisTemplate.boundHashOps(REDIS_CARTLIST_KEY).put(username, cartList);
}
(4)後端:控制層修改findCartList和addGoodsToCartList方法
/**
* 添加商品到購物車
*
* @param itemId 商品ID
* @param num 數量
* @return entity.Result
*/
@RequestMapping("/addGoodsToCartList")
public Result addGoodsToCartList(Long itemId, Integer num) {
// 獲取當前登錄用戶名
String loginUser = SecurityContextHolder.getContext().getAuthentication().getName();
try {
List<Cart> cartList = findCartList();
// 添加商品到購物車
cartList = cartService.addGoodsToCartList(cartList, itemId, num);
// 判斷是否存入redis
if (loginUser == ROLE_ANONYMOUSUSER) {// 未登錄
// 將購物車列表轉換爲json字符串
String cookieValue = JSON.toJSONString(cartList);
// 將購物車添加到cookie中
CookieUtil.setCookie(request, response, CARTLIST_COOKIENAME, cookieValue, MAXAGE_COOKIE, "UTF-8");
} else {// 已登錄
// 存入redis
cartService.saveCartListToRedis(loginUser, cartList);
}
return Result.success("添加商品到購物車成功");
}catch (Exception e){
e.printStackTrace();
return Result.error("添加商品到購物車失敗");
}
}
/**
* 獲取購物車列表
*
* @return java.util.List<com.pinyougou.pojogroup.Cart>
*/
@RequestMapping("/findCartList")
public List<Cart> findCartList() {
// 獲取當前登錄用戶名
String loginUser = SecurityContextHolder.getContext().getAuthentication().getName();
// 判斷是否爲匿名權限
if (loginUser.equals(ROLE_ANONYMOUSUSER)) {// 爲匿名權限,未登錄狀態
// 從cookie中獲取購物車列表
String cartListJson = CookieUtil.getCookieValue(request, CARTLIST_COOKIENAME, "UTF-8");
if (cartListJson == null || cartListJson.equals("")) {
cartListJson = "[]";
}
List<Cart> cartList_cookie = JSON.parseArray(cartListJson, Cart.class);
return cartList_cookie;
} else {// 不是匿名權限,登錄狀態
// 從redis中獲取購物車列表
List<Cart> cartList_redis = cartService.findCartListFromRedis(loginUser);
return cartList_redis;
}
}
(5)單點登錄對接購物車
4、購物車合併
4.1 需求分析
當登錄成功時,將cookie購物車中的購物車列表和redis中的購物車列表進行合併
4.2 後端
(1)服務層接口(cart-interface),新增方法
/**
* 合併購物車
*
* @param cartList1 購物車1
* @param cartList2 購物車2
* @return java.util.List<com.pinyougou.pojogroup.Cart>
*/
List<Cart> mergeCartList(List<Cart> cartList1, List<Cart> cartList2);
(2)服務層實現(cart-service),新增實現
@Override
public List<Cart> mergeCartList(List<Cart> cartList1, List<Cart> cartList2) {
// 遍歷任意購物車
for (Cart cart : cartList1) {
// 遍歷購物車明細列表
for (TbOrderItem orderItem : cart.getOrderItemList()) {
// 進行合併操作
cartList2 = addGoodsToCartList(cartList2, orderItem.getItemId(), orderItem.getNum());
}
}
return cartList2;
}
(3)控制層,修改findCartList方法中的邏輯
/**
* 獲取購物車列表
*
* @return java.util.List<com.pinyougou.pojogroup.Cart>
*/
@RequestMapping("/findCartList")
public List<Cart> findCartList() {
// 獲取當前登錄用戶名
String loginUser = SecurityContextHolder.getContext().getAuthentication().getName();
// 從cookie中獲取購物車列表
String cartListJson = CookieUtil.getCookieValue(request, CARTLIST_COOKIENAME, "UTF-8");
if (cartListJson == null || cartListJson.equals("")) {
cartListJson = "[]";
}
List<Cart> cartList_cookie = JSON.parseArray(cartListJson, Cart.class);
// 判斷是否爲匿名權限
if (loginUser.equals(ROLE_ANONYMOUSUSER)) {// 爲匿名權限,未登錄狀態
return cartList_cookie;
} else {// 不是匿名權限,登錄狀態
// 從redis中獲取購物車列表
List<Cart> cartList_redis = cartService.findCartListFromRedis(loginUser);
// cookie購物車中存在數據
if (cartList_cookie.size() > 0) {
// 進行購物車合併
cartList_redis = cartService.mergeCartList(cartList_cookie, cartList_redis);
// 清除cookie中的購物車數據
CookieUtil.deleteCookie(request, response, CARTLIST_COOKIENAME);
// 將合併後的購物車數據存入reids
cartService.saveCartListToRedis(loginUser, cartList_redis);
}
return cartList_redis;
}
}