购物车业务
购物车业务概要分析
购物车对象封装分析
购物车实体
public class BuyerCart implements Serializable {
private String sellerId;//商家ID
private String sellerName;//商家名称
private List<OrderItem> orderItemList;//购物车明细
//..getset
}
购物项实体(在这里也是订单项)
public class OrderItem implements Serializable {
private Long id;
/**
* 商品id
*/
private Long itemId;
/**
* SPU_ID
*/
private Long goodsId;
/**
* 订单id
*/
private Long orderId;
/**
* 商品标题
*/
private String title;
/**
* 商品单价
*/
private BigDecimal price;
/**
* 商品购买数量
*/
private Integer num;
/**
* 商品总金额
*/
private BigDecimal totalFee;
/**
* 商品图片地址
*/
private String picPath;
private String sellerId;
//...getset
}
购物车详细流程分析
一、添加购物车
二、总体业务流程
跨域访问问题
当controller中的方法进行简单地完成之后,进行测试,发现点击加入购物车按钮没有反应
原因:跨域请求被阻止
什么是跨域请求:
浏览器厂商在开发浏览器时,设置了同源策略。
同源策略:要求发送请求的url地址和返回响应的url地址必须保证协议,ip地址,端口号不能够发生改变,只要有一个发生变化,浏览器就会认为这个响应不安全,会拒绝接收响应数据,这就是跨域请求访问。
注意:如果前端window.location.href
进行跳转,跳转到不同服务器页面,不会触发跨域访问问题,只是页面跳转,不会有数据返回
解决方案
jsonp
:jquery如果发生ajax请求,可以将数据类型设置为jsonp,原理是jquery在发送数据的同时,会在数据中生产一个令牌,controller接收到数据后,进行处理,返回响应时,将令牌原样返回,jquery会判断令牌是否是当初发送的,如果是,则接收响应;反之,拒绝接收。cors
:是w3c支持的解决方法,原理是在响应体设置信息Access-Control-Allow-Origin
,如果使用SpringMVC4.2以上版本,可以使用@Cross
注解,就相当于设置了响应头信息
使用cors解决跨域问题
/**
* 添加商品到购物车
* @CrossOrigin注解,相当于设置了响应体信息,是w3c支持的一种跨域解决方案
* origins属性,设置的地址是返回响应给静态页面,静态页面所在服务器的地址
* @param itemId 商品库存id
* @param num 购买数量
* @return
*/
@RequestMapping("/addGoodsToCartList")
@CrossOrigin(origins = "http://localhost:8086", allowCredentials = "true")
public Result addGoodsToCartList(Long itemId, Integer num){
return new Result(true,"添加成功!");
}
//添加商品到购物车
$scope.addToCart=function(){
//alert('SKUID:'+$scope.sku.id );
$http.get('http://localhost:8080/cart/addGoodsToCartList.do?itemId='
+$scope.sku.id+'&num='+$scope.num ,{'withCredentials':true} ).success(
function(response){
if(response.success){
location.href='http://localhost:8080/cart.html';
}else{
alert(response.message);
}
}
);
}
购物车的controller方法
一、点击加入购物车按钮,将商品添加到购物车
二、返回给前端购物车列表
/**
* 购物车业务
*/
@RestController
@RequestMapping("/cart")
public class BuyerCartController {
@Reference
private CartService cartService;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
/**
* 添加商品到购物车
* @CrossOrigin注解,相当于设置了响应体信息,是w3c支持的一种跨域解决方案
* origins属性,设置的地址是返回响应给静态页面,静态页面所在服务器的地址
* @param itemId 商品库存id
* @param num 购买数量
* @return
*/
@RequestMapping("/addGoodsToCartList")
@CrossOrigin(origins = "http://localhost:8086", allowCredentials = "true")
public Result addGoodsToCartList(Long itemId, Integer num){
try {
//1. 获取当前登录用户名称
String username = SecurityContextHolder.getContext().getAuthentication().getName();
//2. 获取购物车列表
List<BuyerCart> cartList = findCartList();
//3. 将当前商品加入到购物车列表
cartList = cartService.addItemToCartList(cartList,itemId,num);
//4. 判断当前用户是否登录, 未登录用户名为"anonymousUser"
if ("anonymousUser".equals(username)){
//4.a.如果未登录, 则将购物车列表存入cookie中
CookieUtil.setCookie(request, response, Constants.CART_LIST_COOKIE, JSON.toJSONString(cartList), 60 * 60 * 24 * 30, "utf-8");
//4.b.如果已登录, 则将购物车列表存入redis中
cartService.setCartListToRedis(username, cartList);
}
return new Result(true,"添加成功!");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "添加失败!");
}
}
/**
* 获取购物车列表数据
* @return
*/
@RequestMapping("/findCartList")
public List<BuyerCart> findCartList(){
//1. 获取当前登录用户名称
String username = SecurityContextHolder.getContext().getAuthentication().getName();
//2. 从cookie中获取购物车列表json格式字符串
String cookieCartJson = CookieUtil.getCookieValue(request, Constants.CART_LIST_COOKIE, "utf-8");
//3. 如果购物车列表json串为空则返回"[]"
if (cookieCartJson==null || "".equals(cookieCartJson)){
cookieCartJson = "[]";
}
//4. 将购物车列表json转换为对象
List<BuyerCart> cookieCartList = JSON.parseArray(cookieCartJson, BuyerCart.class);
//5. 判断用户是否登录, 未登录用户为"anonymousUser"
if (!"anonymousUser".equals(username)){
//5.a. 未登录, 返回cookie中的购物车列表对象
return cookieCartList;
}else {
//5.b.1.已登录, 从redis中获取购物车列表对象
List<BuyerCart> redisCartList = cartService.getCartListToRedis(username);
//5.b.2.判断cookie中是否存在购物车列表
if (cookieCartList.size()>0){
//如果cookie中存在购物车列表则和redis中的购物车列表合并成一个对象
cartService.mergeCookieCartListAndRedisCartList(cookieCartList,redisCartList);
//删除cookie中购物车列表
CookieUtil.deleteCookie(request,response,Constants.CART_LIST_COOKIE);
//将合并后的购物车列表存入redis中
cartService.setCartListToRedis(username,redisCartList);
}
//5.b.3.返回购物车列表对象
return redisCartList;
}
}
}
购物车的service方法
package cn.itcast.core.service;
import cn.itcast.core.dao.item.ItemDao;
import cn.itcast.core.pojo.entity.BuyerCart;
import cn.itcast.core.pojo.item.Item;
import cn.itcast.core.pojo.order.OrderItem;
import cn.itcast.core.util.Constants;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Service
public class CartServiceImpl implements CartService {
@Autowired
private ItemDao itemDao;
@Autowired
private RedisTemplate redisTemplate;
@Override
public List<BuyerCart> addItemToCartList(List<BuyerCart> cartList, Long itemId, Integer num) {
//1. 根据商品SKU ID查询SKU商品信息
Item item = itemDao.selectByPrimaryKey(itemId);
//2. 判断商品是否存在不存在, 抛异常
if (item == null){
throw new RuntimeException("该商品不存在!");
}
//3. 判断商品状态是否为1已审核, 状态不对抛异常
if (!"1".equals(item.getStatus())){
throw new RuntimeException("该商品未审核!");
}
//4.获取当前商品的商家ID
String sellerId = item.getSellerId();
//5.根据商家ID查询购物车列表中是否存在该商家的购物车
BuyerCart buyerCart = findBuyerCartBySellerId(cartList, sellerId);
//6.判断如果购物车列表中不存在该商家的购物车
if (buyerCart == null) {
//6.a.1 新建购物车对象
buyerCart = new BuyerCart();
//设置新创建的购物车对象的卖家id
buyerCart.setSellerId(sellerId);
//设置新创建的购物车对象的卖家名称
buyerCart.setSellerName(item.getSeller());
//创建购物项集合
List<OrderItem> orderItemList = new ArrayList<>();
//创建购物项
OrderItem orderItem = createOrderItem(item, num);
//将购物项添加到购物项集合
orderItemList.add(orderItem);
//将购物项集合添加到购物车
buyerCart.setOrderItemList(orderItemList);
//6.a.2 将新建的购物车对象添加到购物车列表
cartList.add(buyerCart);
}else {
//6.b.1如果购物车列表中存在该商家的购物车 (查询购物车明细列表中是否存在该商品)
List<OrderItem> orderItemList = buyerCart.getOrderItemList();
OrderItem orderItem = findOrderItemByItemId(orderItemList, itemId);
//6.b.2判断购物车明细是否为空
if (orderItem == null){
//6.b.3为空,新增购物车明细
orderItem= createOrderItem(item, num);
//将新增购物项加入到购物项集合
orderItemList.add(orderItem);
}else {
//6.b.4不为空,在原购物车明细上添加数量,更改金额
orderItem.setNum(orderItem.getNum() + num);
//设置总价格
orderItem.setTotalFee(orderItem.getPrice().multiply(new BigDecimal(orderItem.getNum())));
}
//6.b.5如果购物车明细中数量操作后小于等于0,则移除
if (orderItem.getNum() <= 0){
orderItemList.remove(orderItem);
}
//6.b.6如果购物车中购物车明细列表为空,则移除
if (orderItemList.size() <= 0){
cartList.remove(buyerCart);
}
}
//7. 返回购物车列表对象
return cartList;
}
/**
* 判断当前购物项中是否存在这个商品
* @param orderItemList
* @param itemId
* @return
*/
private OrderItem findOrderItemByItemId(List<OrderItem> orderItemList, Long itemId){
if (orderItemList != null){
for (OrderItem orderItem : orderItemList) {
if (orderItem.getItemId().equals(itemId)){
return orderItem;
}
}
}
return null;
}
/**
* 创建购物项对象
* @param item
* @param num
* @return
*/
private OrderItem createOrderItem(Item item, Integer num){
if (num <= 0){
throw new RuntimeException("购买数量非法!");
}
OrderItem orderItem = new OrderItem();
orderItem.setNum(num);
orderItem.setGoodsId(item.getGoodsId());
orderItem.setItemId(item.getId());
orderItem.setPicPath(item.getImage());
orderItem.setPrice(item.getPrice());
orderItem.setSellerId(item.getSellerId());
orderItem.setTitle(item.getTitle());
orderItem.setTotalFee(item.getPrice().multiply(new BigDecimal(num)));
return orderItem;
}
/**
* 判断购物车中,是否有该商家的购物车
* @param cartList
* @param sellerId
* @return
*/
private BuyerCart findBuyerCartBySellerId(List<BuyerCart> cartList, String sellerId){
if (cartList != null){
for (BuyerCart buyerCart : cartList) {
if (buyerCart.getSellerId().equals(sellerId)){
return buyerCart;
}
}
}
return null;
}
@Override
public void setCartListToRedis(String username, List<BuyerCart> cartList) {
redisTemplate.boundHashOps(Constants.CART_LIST_REDIS).put(username,cartList);
}
@Override
public List<BuyerCart> getCartListToRedis(String username) {
List<BuyerCart> cartList = (List<BuyerCart>)redisTemplate.boundHashOps(Constants.CART_LIST_REDIS).get(username);
if (cartList == null){
cartList = new ArrayList<>();
}
return cartList;
}
@Override
public List<BuyerCart> mergeCookieCartListAndRedisCartList(List<BuyerCart> cookieCartList, List<BuyerCart> redisCartList) {
if (cookieCartList != null){
//遍历cookie购物车集合
for (BuyerCart cookieCart : cookieCartList) {
//遍历cookie购物车中的购物项集合
for (OrderItem cookieOrderItem : cookieCart.getOrderItemList()) {
//将cookie中的购物项加入到redis的购物车集合中
redisCartList = addItemToCartList(redisCartList,cookieOrderItem.getItemId(),cookieOrderItem.getNum());
}
}
}
return redisCartList;
}
}