zoukankan      html  css  js  c++  java
  • 购物车的原理以及实现

      今天模拟京东的购物车实现原理完成了购物车模块的开发, 给大家分享下。

    京东的购物车实现原理:在用户登录和不登录的状态下对购物车存入cookie还是持久化到redis中的实现。下面就来具体说次购物车的实现过程

    两种情况:

    用户登录,购物车存入redis中

    用户未登录,购物车存入cookie中

    比较两种方式的优缺点:

     cookie:优点:数据保存在用户浏览器中,不占用服务端内存;用户体检效果好;代码实现简单

         缺点:cookie的存储空间只有4k;更换设备时,购物车信息不能同步;cookie禁用,不提供保存

     redis:优点:数据能够持久化;实现了购物车同步

         缺点:增加了数据库的压力,速度慢

    先介绍使用cookie存储购物车的实现思路

    1、用户未登录状态下,用户添加购物车,首先从cookie中查询购物车中的商品列表

    2、 判断cookie的商品列表中是否有要添加的商品信息

    3、如果cookie中有该商品信息,将商品的数量相加

    4、如果没有,根据商品的id值查询商品信息

    5、将商品添加到购物车列表中

    6、将购物车列表写入cookie中,设置cookie的过期时间

    7、将cookie返回给客户端。

     购物车的实现:

    这里直接使用商品作为购物项对象,在页面中计算购物项的小计和购物车的总金额

    package nyist.e3.pojo;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class TbItem implements Serializable{
        private Long id;
    
        private String title;
    
        private String sellPoint;
    
        private Long price;
    
        private Integer num;//作为购物项购买的商品数量
    
        private String barcode;
    
        private String image;//展示购物项中的图片
    
        private Long cid;
    
        private Byte status;
    
        private Date created;
    
        private Date updated;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title == null ? null : title.trim();
        }
    
        public String getSellPoint() {
            return sellPoint;
        }
    
        public void setSellPoint(String sellPoint) {
            this.sellPoint = sellPoint == null ? null : sellPoint.trim();
        }
    
        public Long getPrice() {
            return price;
        }
    
        public void setPrice(Long price) {
            this.price = price;
        }
    
        public Integer getNum() {
            return num;
        }
    
        public void setNum(Integer num) {
            this.num = num;
        }
    
        public String getBarcode() {
            return barcode;
        }
    
        public void setBarcode(String barcode) {
            this.barcode = barcode == null ? null : barcode.trim();
        }
    
        public String getImage() {
            return image;
        }
    
        public void setImage(String image) {
            this.image = image == null ? null : image.trim();
        }
    
        public Long getCid() {
            return cid;
        }
    
        public void setCid(Long cid) {
            this.cid = cid;
        }
    
        public Byte getStatus() {
            return status;
        }
    
        public void setStatus(Byte status) {
            this.status = status;
        }
    
        public Date getCreated() {
            return created;
        }
    
        public void setCreated(Date created) {
            this.created = created;
        }
    
        public Date getUpdated() {
            return updated;
        }
    
        public void setUpdated(Date updated) {
            this.updated = updated;
        }
    }

    cookie中实现添加购物车的代码:

    @Controller
    public class ShopCartController {
    
        @Autowired
        private TbItemService tbItemService;
    
        @Autowired
        private ShopCartService shopCartService;
        // 获取过期时间
        @Value("${EXPIRE_KEY}")
        private Integer EXPIRE_KEY;
    
        @Value("${CART_COOKIE}")
        private String CART_COOKIE;
    
        /**
         * 需求:将商品加入购物车中未登录状态下,将购物超过添加到cookie中
         * 
         * 分析:1、从cookie中获取购物车信息
         * 2、判断购物车中的商品,如果添加的商品存在,数量相加,不存在,根据商品id查询商品信息,添加到cookie中
         * 3、将购物车列表信息写入cookie中
         * 
         * 
         * @param itemId
         * @param num
         * @return
         */
        @RequestMapping("/cart/add/{itemId}")
        public String addCart(@PathVariable Long itemId, @RequestParam(defaultValue = "1") Integer num,
                HttpServletRequest request, HttpServletResponse response) {
         // 1.获得购物车列表
            List<TbItem> itemList = getCartItemList(request);
            // 用来判断商品是否存在的标志
            boolean flag = false;
            // 2、循环遍列表中的商品信息
            for (TbItem tbItem : itemList) {
                // 3、判断添加的商品是否存在
                if (tbItem.getId() == itemId.longValue()) {
                    // 4、添加的商品在cookie中存在,将数量相加
                    tbItem.setNum(tbItem.getNum() + num);
                    // 重置标签
                    flag = true;
                    // 跳出循环
                    break;
                }
            }
            if (!flag) {
                // cookie中没有添加的商品信息
                // 通过商品id查询商品信息
                TbItem item = tbItemService.getItemById(itemId);
                item.setNum(num);
                if (StringUtils.isNotBlank(item.getImage())) {
                    // 取一张图片用于展示使用
                    item.setImage(item.getImage().split(",")[0]);
                }
                // 将商品添加购物车
                itemList.add(item);
            }
            //将购物车写入cookie中
            
            CookieUtils.setCookie(request, response, CART_COOKIE, JsonUtils.objectToJson(itemList),EXPIRE_KEY,true);
            
            return "cartSuccess";
    
        }
    
    
    }

    cookie中查询购物车列表:

    思路:

    1、根据cookie的name值直接取出商品列表信息

    2、将购物车列表添加到model中,返回逻辑视图

    private List<TbItem> getCartItemList(HttpServletRequest request) {
            // 使用utf-8,需要设置第三个参数为true
            String json = CookieUtils.getCookieValue(request, CART_COOKIE, true);
            if (StringUtils.isNotBlank(json)) {
                // 返回cookie中取出的数据集合
                return JsonUtils.jsonToList(json, TbItem.class);
            }
            // 返回空集合对象
            return new ArrayList<>();
        }
    
    
    
    @RequestMapping("/cart/cart")
        public String getCartList(HttpServletRequest request, HttpServletResponse response, Model model) {
            // 从cookie中取出商品信息,
            List<TbItem> itemList = getCartItemList(request);
            // 将购物车信息返回给页面中
            model.addAttribute("cartList", itemList);
            // 跳转逻辑视图
            return "cart";
        }

    cookie中实现删除购物车中商品的功能:

    1、接收页面传递的善品id值

    2、从cookie中取出购物车列表,进行循环遍历,然后遍历的每一个商品信息和要删除的商品进行对比

    3、如果存在就从购物车列表中将该商品移除

    4、重新将购物车列表写入cookie中

    5、将cookie信息响应给客户端

    @RequestMapping("/cart/delete/{itemId}")
        public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request, HttpServletResponse response) {
            List<TbItem> list = getCartItemList(request);
            for (TbItem tbItem : list) {
                if (tbItem.getId() == itemId.longValue()) {
                    list.remove(tbItem);
                    break;
                }
            }
            // 删除成功后,将购物车列表写入cookie中
            CookieUtils.setCookie(request, response, CART_COOKIE, JsonUtils.objectToJson(list), EXPIRE_KEY, true);
    
            // 删除成功后,重定向到购物车列表页面
            return "redirect:/cart/cart.html";
    
        }

    cookie购物车的添加,查询,删除已经实现实现,更改方法和删除方法实现过程基本一样

    登录状态下redis购物车的实现

    实现redis购物车添加功能

    思路:

    1、从request域中取出登录用户的信息

    2、使用redis存储购物车列表 使用redis中的hash数据类型  hash的key 使用登录用户id值,field的key使用商品的id值,将商品的信息作为field的value值

    3、完成cookie存储购物车列表的功能

    实现的代码:

    @Override
        public E3Result addCart(Long userId, Long itemId, Integer num) {
            try {
                // 从redis中取出购物车,判断是否已经有购物项
                Boolean hexists = jedisClient.hexists(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "");
                if (hexists) {
                    // 表示购物车中已经有该商品,只需要将该商品的数量相加即可
                    String hget = jedisClient.hget(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "");
                    // 将数量相加
                    TbItem item = JsonUtils.jsonToPojo(hget, TbItem.class);
                    item.setNum(item.getNum() + num);
                    // 将商品重新放入购物车中
                    jedisClient.hset(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "", JsonUtils.objectToJson(item));
                    return E3Result.ok();
                }
    
                // 表示购物车中没有要添加的商品信息
                // 根据商品的id查询商品的信息
                TbItem item = itemMapper.selectByPrimaryKey(itemId);
                item.setNum(num);
                if (StringUtils.isNotBlank(item.getImage())) {
                    item.setImage(item.getImage().split(",")[0]);
                }
                // 将商品信息存入购物车中
                jedisClient.hset(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "", JsonUtils.objectToJson(item));
                return E3Result.ok();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return E3Result.build(400, "商品添加购物车失败");
    
        }

    展示登录状态下的购物车列表:需要将cookie中的购物车和redis中的购物车整合

    1、从cookie中取出购物车列表对象

    2、从redis中取出购物车对象

    3、将cookie中的购物车列表和redis中的购物车列表整合(取出cookie中的购物车列表,然后添加到redis购物车中即可)

    5、最终展示的结果以redis中的购物车为主

    /**
         * cookie中的购物车和redis中的购物车进行整合
         */
        @Override
        public E3Result mergeCart(Long userId, List<TbItem> itemList) {
            for (TbItem tbItem : itemList) {
                // 只需要调用登录状态下添加购物车业务处理逻辑即可
                addCart(userId, tbItem.getId(), tbItem.getNum());
            }
            return E3Result.ok();
        }

    redis购物车中删除购物项

    将用户的id值和商品的id值分别作为hahs的key和field的key,调用redis中的hdel(String key,String...field)即可完成删除功能

    /**
         * 删除购物车
         * 
         * @return
         * 
         */
        @Override
        public E3Result deleteCartItem(Long id, Long itemId) {
            Long hdel = jedisClient.hdel(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "");
            System.out.println("删除购物车购物项为"+hdel);
            return E3Result.ok();
        }

    redis购物车中更新购买商品的数量

        /**
         * 更新购物车中商品的数量
         */
        @Override
        public E3Result updateRedisNum(Long id, Long itemId, Integer num) {
            // 取出需要更改数量的商品信息
            String hget = jedisClient.hget(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "");
            // 将取出的json数据转换为商品对象,然后更新数量
            TbItem item = JsonUtils.jsonToPojo(hget, TbItem.class);
            item.setNum(num);
            // 更新成功后,将数据写到redis购物车中
            jedisClient.hset(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "", JsonUtils.objectToJson(item));
            return E3Result.ok();
        }

    当用户点击去结算时:跳转到订单确认页面

    1、生成订单详情

    2、配送地址信息

    3、选择支付方式

    在确认订单之前, 应该判断用户是否是登录装态,可以使用拦截器实现

    1、自定义拦截器实现HandlerInteceptor接口

    2、从cookie中去token消息(登录认证的令牌)

    3、判断token的值是否为空,如果为空,就跳转到用户登录页面完成登录,同时需要将当前地址栏的url作为参数传递(在登录的业务逻辑中,接收该url,完成登录后,跳转会该页面)

    4、如果token不为空,根据token查询用户信息,然后将用户信息写入request域中,拦截器执行放行操作

    5、此时获取到的购物车列表是从redis中读出的和cookie整合的最新的购物车。

    拦截器的实现过程:

    public class LoginInterceptor implements HandlerInterceptor {
        
        @Value("${TT_TOKEN}")
        private String TT_TOKEN;
        @Value("${SSO_LOGIN_URL}")
        private String SSO_LOGIN_URL;
        
        @Autowired
        private UserService userService;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            //执行Handler之前执行此方法
            // a)从cookie中取token。
            String token = CookieUtils.getCookieValue(request, TT_TOKEN);
            if (StringUtils.isBlank(token)) {
                //取当前请求的url
                String url = request.getRequestURL().toString();
                // b)没有token,需要跳转到登录页面。
                response.sendRedirect(SSO_LOGIN_URL + "?redirectUrl=" + url);
                //拦截
                return false;
            }
            // c)有token。调用sso系统的服务,根据token查询用户信息。
            e3Result result = userService.getUserByToken(token);
            if (result.getStatus() != 200) {
                // d)如果查不到用户信息。用户登录已经过期。需要跳转到登录页面。
                //取当前请求的url
                String url = request.getRequestURL().toString();
                // b)没有token,需要跳转到登录页面。
                response.sendRedirect(SSO_LOGIN_URL + "?redirectUrl=" + url);
                //拦截
                return false;
            }
            // e)查询到用户信息。放行。
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            // 执行Handler之后返回ModelAndView之前
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            // 返回ModelAndView之后,执行。异常处理。
    
        }
    
    }





    @Override
        public E3Result getToken(String token) {
            try {
                // 从redis中取值
                String json = jedisClient.get("USER_INFO:" + token);
                if (StringUtils.isBlank(json)) {
                    // json为空,表示已经过期
                    return E3Result.build(400, "session已经过期,请重新登录");
                }
                //将json对象转化为pojo对象
                TbUser user = JsonUtils.jsonToPojo(json, TbUser.class);
                //重新设置用户登录信息的过期时间
                jedisClient.expire("USER_INFO:" + token, 1800);
                //将获取的user信息使用E3Result包装后返回
                return E3Result.ok(user);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

    拦截器定义好之后,需要在springmvc中配置

    <mvc:interceptors>
            <mvc:interceptor>
                <!-- 拦截所有请求 -->
                <mvc:mapping path="/**" />
                <!-- 注册自定义拦截器 -->
                <bean class="nyist.e3.interceptor.LoginInterceptor"></bean>
            </mvc:interceptor>
        </mvc:interceptors>

    在登录页面接收url,实现sso系统的回调 接收的redirectUrl即为拦截中请求登录页面传递的参数

     至此:购物车模块的功能基本实现,错误的地方希望大家多多指正。

  • 相关阅读:
    Arthur J.Riel的61条面向对象设计的经验原则[ZT]
    06年的CS Sub,挺像考研考纲的。。平时学习的时候,可以参考一下~
    Interop时候.Net和Win32对应数据类型
    Asp.Net使用POST方法最简单的实现
    在MasterPage中实现本地化
    最近MS比较High。。。
    语无伦次一下~
    初试Mono~ Virtual Server 果然强大~
    电竟3周年了,纪念一下。。
    又见了点市面~
  • 原文地址:https://www.cnblogs.com/shuai-server/p/8996625.html
Copyright © 2011-2022 走看看