今日目标:
(1)理解品优购购物车的实现思路
(2)运用Cookie存储购物车
(3)编写购物车前端代码
(4)运用 Redis 存储购物车
目录
1、购物车工程搭建
1.1 需求分析
1.2 工程搭建
2、Cookie存储购物车
2.1 需求分析
2.2 后端-添加商品到购物车
2.3 购物车列表展示
2.4 商品数量加减
2.5 计算总金额和总数量
3、Redis 存储购物车
3.1 需求分析
3.2 后端代码
4、购物车合并
4.1 需求分析
4.2 后端
用户在商品详细页点击加入购物车,提交商品SKU编号和购买数量,添加到购物车。购物车展示页面如下:
(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 orderItemList;
}
使用cookie存储购物车数据。服务层负责逻辑,控制层负责读写cookie 。
(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
*/
List addGoodsToCartList(List 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 addGoodsToCartList(List 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 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 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 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 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
*/
@RequestMapping("/findCartList")
public List findCartList() {
// 从cookie中获取购物车列表
String cartListJson = CookieUtil.getCookieValue(request, CARTLIST_COOKIENAME, "UTF-8");
List cartList = JSON.parseArray(cartListJson, Cart.class);
return cartList;
}
}
(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)查看购物车
(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)前端:页面 加减号 绑定单击事件
(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)页面绑定变量
判断当前用户是否登陆,如果未登录采用Cookie存储,如果登录则采用Redis存储。登录后要进行Cookie购物车与Redis购物车的合并操作,并清除Cookie购物车。
(1)后端:修改放行cart/*.do的配置,将其配置为匿名角色访问权限
(2)后端:服务层接口(cart-interface),在CartService中新增方法
/**
* 从redis中获取购物车列表
*
* @param
* @return java.util.List
*/
List findCartListFromRedis(String username);
/**
* 将购物车列表保存到redis中
*
* @param username 当前登录用户名
* @param cartList 购物车列表
*/
void saveCartListToRedis(String username, List cartList);
(3)后端:服务层实现(cart-service),在CartServiceImpl中新增实现
@Override
public List findCartListFromRedis(String username) {
System.out.println("从redis中获取当前用户的购物车");
// 从购物车中获取
List cartList = (List) redisTemplate.boundHashOps(REDIS_CARTLIST_KEY).get(username);
if (cartList == null) {
cartList = new ArrayList<>();
}
return cartList;
}
@Override
public void saveCartListToRedis(String username, List 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 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
*/
@RequestMapping("/findCartList")
public List 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 cartList_cookie = JSON.parseArray(cartListJson, Cart.class);
return cartList_cookie;
} else {// 不是匿名权限,登录状态
// 从redis中获取购物车列表
List cartList_redis = cartService.findCartListFromRedis(loginUser);
return cartList_redis;
}
}
(5)单点登录对接购物车
当登录成功时,将cookie购物车中的购物车列表和redis中的购物车列表进行合并
(1)服务层接口(cart-interface),新增方法
/**
* 合并购物车
*
* @param cartList1 购物车1
* @param cartList2 购物车2
* @return java.util.List
*/
List mergeCartList(List cartList1, List cartList2);
(2)服务层实现(cart-service),新增实现
@Override
public List mergeCartList(List cartList1, List 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
*/
@RequestMapping("/findCartList")
public List findCartList() {
// 获取当前登录用户名
String loginUser = SecurityContextHolder.getContext().getAuthentication().getName();
// 从cookie中获取购物车列表
String cartListJson = CookieUtil.getCookieValue(request, CARTLIST_COOKIENAME, "UTF-8");
if (cartListJson == null || cartListJson.equals("")) {
cartListJson = "[]";
}
List cartList_cookie = JSON.parseArray(cartListJson, Cart.class);
// 判断是否为匿名权限
if (loginUser.equals(ROLE_ANONYMOUSUSER)) {// 为匿名权限,未登录状态
return cartList_cookie;
} else {// 不是匿名权限,登录状态
// 从redis中获取购物车列表
List 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;
}
}