购物车需求分析与解决方案

目标1:说出品优购购物车的实现思路
目标2:运用Cookie存储购物车
目标3:编写购物车前端代码
目标4:运用Redis存储购物车

1.购物车需求分析与解决方案

1.1需求分析

用户在商品详细页点击加入购物车,提交商品SKU编号和购买数量,添加到购物车。购物车展示页面如下:

简单的可以理解为每个商品的属性:颜色/尺码等信息(比如红色M码是一个SKU;红色L码又是一个SKU)。
SKU=Stock Keeping Unit(库存量单位),即库存进出计量的单位,可以是以件,盒,托盘等为单位。SKU是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。当下已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。
针对电商而言,SKU有另外的注解:
1、SKU是指一款商品,每款都有出现一个SKU,便于电商品牌识别商品。
2、一款商品多色,则是有多个SKU,例:一件衣服,有红色、白色、蓝色,则SKU编码也不相同,如相同则会出现混淆,发错货。

1.2实现思路

购物车数据的存储结构如下:


京东实现思路: 当用户在未登录的情况下,将此购物车存入cookies , 在用户登陆的情况下,将购物车数据存入redis 。如果用户登陆时,cookies中存在购物车,需要将cookies的购物车合并到redis中存储,清空cookies中的购物车。

1.3购物车实体类

public class Cart implements Serializable{
    private String sellerId;//商家ID
    private String sellerName;//商家名称
    private List<TbOrderItem> orderItemList;//购物车明细
    //getter  and setter  ......
}

这个类是对每个商家的购物车进行的封装

2.Cookie存储购物车

2.1需求分析

使用cookie存储购物车数据。服务层负责逻辑,控制层负责读写cookie 。

2.2服务接口层

(1)服务层接口CartService

/**
 * 购物车服务接口 
 * @author Administrator
 */
public interface CartService {
    /**
     * 添加商品到购物车
     * @param cartList
     * @param itemId 商品SKU码
     * @param num 数量
     * @return
     */
    public List<Cart> addGoodsToCartList(List<Cart> cartList,Long itemId,Integer num );
}

2.3服务实现层

实现思路:

        //1.根据商品SKU ID查询SKU商品信息

        //2.获取该商品对应的商家ID        

        //3.根据商家ID判断购物车列表中是否存在该商家的购物车   
    
        //4.如果购物车列表中不存在该商家的购物车

        //4.1 新建购物车对象

        //4.2 将新建的购物车对象添加到购物车列表     

        //5.如果购物车列表中存在该商家的购物车   
    
        // 查询购物车明细列表中是否存在该商品

        //5.1. 如果没有,新增购物车明细 
    
        //5.2. 如果有,在原购物车明细上添加数量,更改金额

购物车服务实现类:

/**
 * 购物车服务实现类
 * @author Administrator
 *
 */
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    private TbItemMapper itemMapper;
    
    @Override
    public List<Cart> addGoodsToCartList(List<Cart> cartList, Long itemId, Integer num) {
    
        //1.根据商品SKU ID查询SKU商品信息
        TbItem item = itemMapper.selectByPrimaryKey(itemId);
        if(item==null){
            throw new RuntimeException("商品不存在");
        }
        if(!item.getStatus().equals("1")){
            throw new RuntimeException("商品状态无效");
        }
        
        //2.获取商家ID      
        String sellerId = item.getSellerId();
        
        //3.根据商家ID判断购物车列表中是否存在该商家的购物车       
        Cart cart = searchCartBySellerId(cartList,sellerId);
        
        //4.如果购物车列表中不存在该商家的购物车
        if(cart==null){     
            
            //4.1 新建购物车对象 ,
            cart=new Cart();
            cart.setSellerId(sellerId);
            cart.setSellerName(item.getSeller());                       
            TbOrderItem orderItem = createOrderItem(item,num);
            List orderItemList=new ArrayList();
orderItemList.add(orderItem);
            cart.setOrderItemList(orderItemList);
            
            //4.2将购物车对象添加到购物车列表
            cartList.add(cart);
            
        }else{
            //5.如果购物车列表中存在该商家的购物车           
            // 判断购物车明细列表中是否存在该商品
            TbOrderItem orderItem = searchOrderItemByItemId(cart.getOrderItemList(),itemId);
                        
            if(orderItem==null){
                //5.1. 如果没有,新增购物车明细             
                orderItem=createOrderItem(item,num);
                cart.getOrderItemList().add(orderItem);
            }else{
                //5.2. 如果有,在原购物车明细上添加数量,更改金额
                orderItem.setNum(orderItem.getNum()+num);           
                orderItem.setTotalFee(new BigDecimal(orderItem.getNum()*orderItem.getPrice().doubleValue())  );
                //如果数量操作后小于等于0,则移除
                if(orderItem.getNum()<=0){
                    cart.getOrderItemList().remove(orderItem);//移除购物车明细 
                }
                //如果移除后cart的明细数量为0,则将cart移除
                if(cart.getOrderItemList().size()==0){
                    cartList.remove(cart);
                }
            }           
        }           
        return cartList;
    }
    
    
    /**
     * 根据商家ID查询购物车对象
     * @param cartList
     * @param sellerId
     * @return
     */
    private Cart searchCartBySellerId(List<Cart> cartList, String sellerId){
        for(Cart cart:cartList){
            if(cart.getSellerId().equals(sellerId)){
                return cart;
            }       
        }
        return null;
    }
    
    /**
     * 根据商品明细ID查询
     * @param orderItemList
     * @param itemId
     * @return
     */
    private TbOrderItem searchOrderItemByItemId(List<TbOrderItem> orderItemList ,Long itemId ){
        for(TbOrderItem orderItem :orderItemList){
            if(orderItem.getItemId().longValue()==itemId.longValue()){
                return orderItem;               
            }           
        }
        return null;
    }
    
    /**
     * 创建订单明细
     * @param item
     * @param num
     * @return
     */
    private TbOrderItem createOrderItem(TbItem item,Integer num){
        if(num<=0){
            throw new RuntimeException("数量非法");
        }
        
        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(new BigDecimal(item.getPrice().doubleValue()*num));
        return orderItem;
    }
}

2.4后端控制层

实现思路:
(1)从cookie中取出购物车
(2)向购物车添加商品
(3)将购物车存入cookie

新建CartController.java

@RestController
@RequestMapping("/cart")
public class CartController {

    @Reference
    private CartService cartService;
    
    @Autowired
    private  HttpServletRequest request;
    
    @Autowired
    private  HttpServletResponse response;
    
    
    /**
     * 购物车列表
     * @param request
     * @return
     */
    @RequestMapping("/findCartList")
    public List<Cart> findCartList(){
        String cartListString = util.CookieUtil.getCookieValue(request, "cartList","UTF-8");
        if(cartListString==null || cartListString.equals("")){
            cartListString="[]";
        }
        List<Cart> cartList_cookie = JSON.parseArray(cartListString, Cart.class);
        return cartList_cookie; 
    }
    
    /**
     * 添加商品到购物车
     * @param request
     * @param response
     * @param itemId
     * @param num
     * @return
     */
    @RequestMapping("/addGoodsToCartList")
    public Result addGoodsToCartList(Long itemId,Integer num){
        try {           
            List<Cart> cartList =findCartList();//获取购物车列表
            cartList = cartService.addGoodsToCartList(cartList, itemId, num);   
            util.CookieUtil.setCookie(request, response, "cartList", JSON.toJSONString(cartList),3600*24,"UTF-8");
            return new Result(true, "添加成功");
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, "添加失败");
        }
    }   
}

浏览器测试:

查看购物车:http://localhost:9105/cart/findCartList.do
添加商品到购物车 :
http://localhost:9105/cart/addGoodsToCartList.do?itemId=1369280&num=100

4.Redis存储购物车

4.1需求分析

判断当前用户是否登陆,如果未登录采用Cookie存储,如果登录则采用Redis存储。登录后要进行Cookie购物车与Redis购物车的合并操作,并清除Cookie购物车。

4.2判断当前用户是否登陆

判断当前用户是否登陆,这可以借助于token从redis获取用户信息,如果能查询到用户信息证明当前用户已经登录

4.3远程购物车存取

4.3.1服务接口层

CartService.java定义方法

   /**
     * 从redis中查询购物车
     * @param username
     * @return
     */
    public List<Cart> findCartListFromRedis(String username);
    
    /**
     * 将购物车保存到redis
     * @param username
     * @param cartList
     */
    public void saveCartListToRedis(String username,List<Cart> cartList);

4.3.2服务实现层

CartServiceImpl.java实现方法

@Autowired
    private RedisTemplate redisTemplate;
    @Override
    public List<Cart> findCartListFromRedis(String username) {
        System.out.println("从redis中提取购物车数据....."+username);
        List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(username);
        if(cartList==null){
            cartList=new ArrayList();
        }
        return cartList;
    }
    @Override
    public void saveCartListToRedis(String username, List<Cart> cartList) {
        System.out.println("向redis存入购物车数据....."+username);
        redisTemplate.boundHashOps("cartList").put(username, cartList);
    }

4.3.3控制层

修改CartController.java的findCartList方法

/**
 * 购物车列表
 * @param request
 * @return
 */
@RequestMapping("/findCartList")
public List<Cart> findCartList(){
    String userInfo = 根据token获取用户信息;
    if(userInfo){//如果未登录
        //读取本地购物车//
        ..........
        return cartList_cookie;
    }else{//如果已登录                   
        List<Cart> cartList_redis =cartService.findCartListFromRedis(username);//从redis中提取              
        return cartList_redis;
    }           
}

修改addGoodsToCartList方法

/**
 * 添加商品到购物车
 * @param request
 * @param response
 * @param itemId
 * @param num
 * @return
 */
@RequestMapping("/addGoodsToCartList")
public Result addGoodsToCartList(Long itemId,Integer num){
    String userInfo = 根据token获取用户信息;

    try {           
        List<Cart> cartList =findCartList();//获取购物车列表
        cartList = cartService.addGoodsToCartList(cartList, itemId, num);
        if(xxx){ //如果是未登录,保存到cookie
            CookieUtil.setCookie(request, response, "cartList", JSON.toJSONString(cartList),3600*24 ,"UTF-8");
            System.out.println("向cookie存入数据");
        }else{//如果是已登录,保存到redis
            cartService.saveCartListToRedis(username, cartList);            
        }
        return new Result(true, "添加成功");
    }  catch (RuntimeException e) {
        e.printStackTrace();
        return new Result(false, e.getMessage());
    }catch (Exception e) {
        e.printStackTrace();
        return new Result(false, "添加失败");
    }
}

4.4购物车合并

4.4.1服务接口层

CartService.java定义方法

/**
     * 合并购物车
     * @param cartList1
     * @param cartList2
     * @return
     */
    public List<Cart> mergeCartList(List<Cart> cartList1,List<Cart> cartList2);

4.4.2服务实现层

CartServiceImpl.java实现方法

public List<Cart> mergeCartList(List<Cart> cartList1, List<Cart> cartList2) {
        System.out.println("合并购物车");
        for(Cart cart: cartList2){
            for(TbOrderItem orderItem:cart.getOrderItemList()){
                cartList1= addGoodsToCartList(cartList1,orderItem.getItemId(),orderItem.getNum());      
            }           
        }       
        return cartList1;
    }

4.4.3控制层

CartController类的findCartList方法

@RequestMapping("/findCartList")
    public List<Cart> findCartList(){
        String username = SecurityContextHolder.getContext().getAuthentication().getName(); 
        String cartListString  = util.CookieUtil.getCookieValue(request, "cartList", "UTF-8");
        if(cartListString==null || cartListString.equals("")){
            cartListString="[]";
        }
        List<Cart> cartList_cookie = JSON.parseArray(cartListString, Cart.class);
        if(username.equals("anonymousUser")){//如果未登录            
            return cartList_cookie;         
        }else{
            List<Cart> cartList_redis =cartService.findCartListFromRedis(username);//从redis中提取  
            if(cartList_cookie.size()>0){//如果本地存在购物车
                //合并购物车
                cartList_redis=cartService.mergeCartList(cartList_redis, cartList_cookie);  
                //清除本地cookie的数据
                util.CookieUtil.deleteCookie(request, response, "cartList");
                //将合并后的数据存入redis 
                cartService.saveCartListToRedis(username, cartList_redis); 
            }           
            return cartList_redis;          
        }   
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 【论语导读】4.15 忠恕之道 《论语》里仁第四篇第十五章,子曰:“参乎!吾道一以贯之。”曾子曰:“唯。”子出,门...
    赛德传播阅读 1,470评论 0 1
  • 我有一个秘密 在一天晚上 把他悄悄埋进了坟墓 不见天日的心结 是他四处叫嚣的囚笼 龇牙利嘴 红着眼祈求放他出去 可...
    你我本是一场清欢阅读 263评论 0 1
  • 渡江边口送客人, 应知渡口四处事。 来日若归他乡处, 莫忘故里几行人。 2016.12.8
    只是喜欢苏东坡阅读 178评论 0 0
  • 不喜欢纠缠也讨厌反复 情绪太多我就面无表情 在这个人世间,但凡与情有关,总能拉扯出太多的故事,或同或异。我是听故事...
    卡西巫阅读 229评论 0 0
  • 你现在所有的来日方长,生活都会一另一种方式回馈给你。如果你有想法,就去执行,不要给自己一个来日方长的借口,不...
    晴织雨耕阅读 419评论 1 0