购物车业务

一、分析购物车vo

(1)添加成功页
在这里插入图片描述

public class CartItemVo implements Serializable {

    /**
     * 商品id
     */
    private Long skuId;

    /**
     * 是否选中
     */
    private Boolean check = true;

    /**
     * 商品标题
     */
    private String title;

    /**
     * 商品图片
     */
    private String image;

    /**
     * 商品属性
     */
    private List<String> skuAttrValues;

    /**
     * 商品价格
     */
    private BigDecimal price;

    /**
     * 商品数量
     */
    private Integer count;

    /**
     * 商品总价 = 价格 * 数量
     */
    private BigDecimal totalPrice;




    public Long getSkuId() {
        return skuId;
    }

    public void setSkuId(Long skuId) {
        this.skuId = skuId;
    }

    public Boolean getCheck() {
        return check;
    }

    public void setCheck(Boolean check) {
        this.check = check;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public List<String> getSkuAttrValues() {
        return skuAttrValues;
    }

    public void setSkuAttrValues(List<String> skuAttrValues) {
        this.skuAttrValues = skuAttrValues;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    /**
     * 计算当前购物项总价
     * @return
     */
    public BigDecimal getTotalPrice() {
        return this.price.multiply(new BigDecimal(this.count.toString()));
    }

    public void setTotalPrice(BigDecimal totalPrice) {
        this.totalPrice = totalPrice;
    }

    @Override
    public String toString() {
        return "CartItemVo{" +
                "skuId=" + skuId +
                ", check=" + check +
                ", title='" + title + '\'' +
                ", image='" + image + '\'' +
                ", skuAttrValues=" + skuAttrValues +
                ", price=" + price +
                ", count=" + count +
                ", totalPrice=" + totalPrice +
                '}';
    }
}

(2)结算页
在这里插入图片描述

public class Cart {

    /**
     * 购物车子项信息
     */
    List<CartItemVo> items;

    /**
     * 购物车商品总数
     */
    private Integer countNum;

    /**
     * 商品类型数量,有几种商品
     */
    private Integer countType;

    /**
     * 结算价格 = 选中的商品价格 - 优惠价格
     */
    private BigDecimal totalAmount;

    /**
     * 减免价格
     */
    private BigDecimal reduce = new BigDecimal("0.00");;



    public List<CartItemVo> getItems() {
        return items;
    }

    public void setItems(List<CartItemVo> items) {
        this.items = items;
    }

    public Integer getCountNum() {
        int count = 0;
        if (items != null && items.size() > 0) {
            for (CartItemVo item : items) {
                count += item.getCount();
            }
        }
        return count;
    }

    public Integer getCountType() {
        int count = 0;
        if (items != null && items.size() > 0) {
            for (CartItemVo item : items) {
                count += 1;
            }
        }
        return count;
    }


    public BigDecimal getTotalAmount() {
        BigDecimal amount = new BigDecimal("0");
        // 计算购物项总价
        if (!CollectionUtils.isEmpty(items)) {
            for (CartItemVo cartItem : items) {
                if (cartItem.getCheck()) {
                    amount = amount.add(cartItem.getTotalPrice());
                }
            }
        }
        // 计算优惠后的价格
        return amount.subtract(getReduce());
    }

    public BigDecimal getReduce() {
        return reduce;
    }

    public void setReduce(BigDecimal reduce) {
        this.reduce = reduce;
    }
}

二、添加商品到购物车

购物车存储结构: Map<(redis的key)用户id, Map<商品skuId, 商品信息>>
1.通过ThreadLocal获取cookie中保存的用户信息,判断用户是否登陆,分别配置redis的key(boundHashOps)
2.如果购物车中已经存在该商品,只需要修改商品件数,不需要重新添加
3.feign获取sku商品信息、属性列表 ,使用CompletableFuture异步编排提升效率

为什么线程池不能放在common中?
为什么使用同一个线程池?
如果不添加自定义线程池会怎么样? 如果不指定线程池,默认使用 ForkJoinPool,是守护线程,主线程结束,线程池关闭,自定义线程池的需要手动关闭,
为什么通过调用get方法来完成操作,CompletableFuture.anyOf(skuRunAsync,skuAttrRunAsync).get(); ? 等待所有线程执行结束
为什么自定义线程后,调用线程却是调用 ThreadPoolExecutor ?
user-key是什么时候生成的?

此方法是将商品数据存入redis,这里选用redis是因为添加购物车操作会频繁的交互数据库,但是redis的持久性有没有数据库好,所有需要增强redis的持久性,redis存数据会使用序列化操作转成字符流进行保存,我们查看数据和获取都不方便,这里直接转换为JSON格式进行存储

/**
     * 加入购物车
     */
    @Override
    public CartItemVo addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {

        //绑定key值,不用要一直set key值,使用hash存储方便查询数据

        CartItemVo cartItemVo = new CartItemVo();
        BoundHashOperations<String, Object, Object> boundHashOperations = extracted();


        /**
         * 1.如果redis中已经保存了该商品,则只需要添加件数
         */
        String res = (String) boundHashOperations.get(Long.toString(skuId));
        if (!StringUtils.isEmpty(res)){
            CartItemVo itemVo = JSON.parseObject(res, CartItemVo.class);
            itemVo.setCount(itemVo.getCount() + num);
            String jsonString = JSON.toJSONString(itemVo);
            //这里是直接覆盖?
            boundHashOperations.put(Long.toString(skuId),jsonString);
            return itemVo;
        }

        /**
         * 2.如果redis中未保存该商品
         */
        //查询sku商品信息
        CompletableFuture<Void> skuRunAsync = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                R<SkuInfoVo> info = productFeignService.info(skuId);
                if (info.getCode() == 0) {
                    SkuInfoVo skuInfoVo = info.getData(new TypeReference<SkuInfoVo>() {
                    });
                    cartItemVo.setSkuId(skuId);
                    cartItemVo.setCheck(true);
                    cartItemVo.setTitle(skuInfoVo.getSkuTitle());
                    cartItemVo.setImage(skuInfoVo.getSkuDefaultImg());
                    cartItemVo.setPrice(skuInfoVo.getPrice());
                    cartItemVo.setCount(num);
                }
            }
        }, executor);

        //查询sku属性列表
        CompletableFuture<Void> skuAttrRunAsync = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                R<List<String>> skuAttr = productFeignService.getSkuAttr(skuId);
                if (skuAttr.getCode() == 0) {
                    List<String> skuAttrData = skuAttr.getData(new TypeReference<List<String>>() {
                    });
                    cartItemVo.setSkuAttrValues(skuAttrData);
                }
            }
        }, executor);

        //等待所有的异步任务全部完成
        CompletableFuture.allOf(skuRunAsync,skuAttrRunAsync).get();

        /**
         * redis存数据会使用序列号操作转成字符流进行保存,我们查询数据和获取都不方便,这里直接转换为JSON格式进行存储
         */
        //存入redis
        String cartJson = JSON.toJSONString(cartItemVo);
        boundHashOperations.put(Long.toString(skuId),cartJson);

        System.out.println();
        return cartItemVo;
    }

封装redis的prefix前缀

 /**
     * 封装redis的prefix前缀
     */
    private BoundHashOperations<String, Object, Object> extracted() {
        UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();
        String redisKey = "";
        //判断用户是否登陆->封装前缀
        if (userInfoTo.getUserId() != null){
            //gulimall:cart:1
            redisKey = CartConstant.CART_PREFIX + userInfoTo.getUserId();
        }else {
            redisKey = CartConstant.CART_PREFIX + userInfoTo.getUserKey();
        }
        //绑定key值
        BoundHashOperations<String, Object, Object> boundHashOperations = redisTemplate.boundHashOps(redisKey);
        return boundHashOperations;
    }

三、合并购物车(离线购物车与在线购物车合并)

 (1)已登录
     1. 获取离线购物车数据
          如果有离线购物数据,有就取出数据,将离线购物数据与已登录购物数据合并,直接将离线数据添加到已登录的redis中,删除离线的redis
	            (1)如果离线购物车没数据,则只需要返回在线购物车的数据
	            (2)如果离线购物车有数据就合并数据
	            上面两种判断都涉及到离线购物车的数据,所以应该先做离线操作,
	            并且我们只需要返回在线购物车的数据,所以获取在线购物车的操作更因为放在最后执行
	            总结:以后再碰见 if(xx == null){}else(xx != null){} 的操作应该直接走 if(xx != null){}
	 2.获取已登录购物车数据,返回
 (2)离线
      获取离线购物车数据,返回
@Override
    public Cart getCart() throws ExecutionException, InterruptedException {
        Cart cart = new Cart();
        UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();
        //在线购物车(已登录用户的redis的key)
        String onlineKey = CartConstant.CART_PREFIX + userInfoTo.getUserId();
        //离线购物车(临时用户的redis的key)
        String offlineKey = CartConstant.CART_PREFIX + userInfoTo.getUserKey();
        /**
         * 已登录
         */
        if (userInfoTo.getUserId() != null){

            /**
             * 这里是要做两次判断的
             * (1)如果离线购物车没数据,则只需要返回在线购物车的数据
             * (2)如果离线购物车有数据就合并数据
             *  上面两种判断都涉及到离线购物车的数据,所以应该先做离线操作,
             *  并且我们只需要返回在线购物车的数据,所以获取在线购物车的操作更因为放在最后执行
             *  总结:以后再碰见 if(xx == null){}else(xx != null){} 的操作应该直接走 if(xx != null){}
             */
            //判断离线是否有购物数据
            List<CartItemVo> offlineVos = getCartFromRedis(offlineKey);
            if (offlineVos != null){
                //如果离线购物车有数据,就合并数据->将离线数据添加到已登录的数据中
                for (CartItemVo offlineVo : offlineVos) {
                    /**
                     * 因为上面已经判断了用户已经登录,证明UserInfo的userId是存在的
                     * addToCart()也会通过extracted()得到已登录的redisKey
                     * 所有这里执行添加购物车的操作会直接添加到已登录的购物车中
                     */
                    addToCart(offlineVo.getSkuId(),offlineVo.getCount());
                    //删除离线数据
                    redisTemplate.delete(offlineKey);
                }
            }
            //再次查询在线购物车的数据,并返回
            cart.setItems(getCartFromRedis(onlineKey));
        }else {
            /**
             * 离线
             */
            cart.setItems(getCartFromRedis(offlineKey));
        }
        return cart;
    }

根据key获取redis中存的值

 /**
     * 根据key获取redis中存的值
     */
    @Override
    public List<CartItemVo> getCartFromRedis(String redisKey) {
        BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(redisKey);
        List<Object> values = ops.values();
        if (values != null && values.size() > 0){
            List<CartItemVo> collect = values.stream().map(obj -> {
                //因为我们存购物数据是使用的JSON格式,所有取数据时直接json字符串转java对象
                String str = (String) obj;
                CartItemVo itemVo = JSON.parseObject(str, CartItemVo.class);
                return itemVo;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/34184.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何优雅的将 Docker 镜像从 1.43G 瘦身到 22.4MB

Docker 镜像的大小对于系统的 CI/CD 等都有影响&#xff0c;尤其是云部署场景。我们在生产实践中都会做瘦身的操作&#xff0c;尽最大的可能使用 Size 小的镜像完成功能。下文是一个简单的 ReactJS 程序上线的瘦身体验&#xff0c;希望可以帮助大家找到镜像瘦身的方向和灵感。 …

C++之GNU C的__attribute__((constructor))优先级使用(一百四十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【期末不挂科 学习数据结构】

期末不挂科 学习数据结构 第一章绪论1.1数据结构的基本概念1.1.1基本概念和术语1.数据2.数据元素3.数据对象4.数据类型5.数据结构 1.1.2数据结构三要素1.数据的逻辑结构2.数据的存储结构3.数据的运算 第一章绪论 1.1数据结构的基本概念 1.1.1基本概念和术语 1.数据 数据是信…

Flutter学习四:Flutter开发基础(四)包管理

目录 0 引言 1 包管理 1.1 简介 1.2 Pub仓库 1.3 依赖Pub仓库 1.3.1 查找包 1.3.2 添加包 1.3.3 下载包 1.3.4 引入包 1.3.5 使用包 1.4 其他依赖方式 1.4.1 依赖本地包 1.4.2 依赖git仓库 1.4.3 不常用的依赖方式 0 引言 本文是对第二版序 | 《Flutter实战第二版…

【ArcGIS】使用ArcMap进行北京1954-120E坐标转WGS84坐标系

背景 在进行青岛地市GIS数据迁移&#xff0c;涉及坐标转换&#xff0c;经过几天摸索终于找到迁移方法 投影坐标系 北京1954-120E坐标 对应为高斯-克吕格投影 300000 3000001 0 0&#xff08;青岛本地坐标&#xff09; 增量:-300000 -3000001&#xff08;此处为示例&#xff0c…

(15)第一人称视角视频

文章目录 前言 15.1 推荐的零件 15.2 连接图示 15.3 通过任务计划器最小化OSD设置 15.4 集成式OSD 15.5 用户视频/博客 15.6 与FPV飞行特别相关的安全警告 15.7 政府/地方法规 前言 第一人称视角在飞行时为你提供了真正的飞行员视角&#xff0c;它将视频摄像机和发射器…

什么是信号槽机制,如何实现,有什么用?(Qt面试题)

1. 什么是信号槽机制&#xff1f; 信号槽机制&#xff08;Signal-Slot mechanism&#xff09;是一种在软件开发中常用的设计模式&#xff0c;用于实现对象间的通信和事件处理。该机制最初由Qt框架引入并广泛应用&#xff0c;后来也被其他编程框架和库所采用。 信号槽机制通过定…

Spring Boot 属性配置解析

基于Spring Boot 3.1.0 系列文章 Spring Boot 源码阅读初始化环境搭建Spring Boot 框架整体启动流程详解Spring Boot 系统初始化器详解Spring Boot 监听器详解Spring Boot banner详解 属性配置介绍 Spring Boot 3.1.0 支持的属性配置方式与2.x版本没有什么变动&#xff0c;按照…

C#使用XML和Treeview结合实现复杂数据采集功能

一个项目的数据表暂时没有定下来&#xff0c;但是有了一些确定性&#xff1a;   1、比较复杂&#xff0c;可能变化&#xff1b;   2、大部分是选择项目&#xff0c;因为输入项目都差不多&#xff1b;   3、应用程序是C/S的窗体应用。   对于这样的用户需求&#xff0c;…

搭建个人hMailServer 邮件服务实现远程发送邮件

文章目录 1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 转载自cpolar极点云文章&#xff1a;搭建个人hMailServer 邮件服务实现远程发送邮件 hM…

如何在编程中中实现负载均衡和容错处理

什么是容错 容错是指系统&#xff08;计算机、网络、云集群等&#xff09;在其一个或多个组件发生故障时继续运行而不会中断的能力。 创建容错系统的目的是防止由单点故障引起的中断&#xff0c;确保任务关键型应用程序或系统的高可用性和业务连续性。 容错系统使用备份组件…

【Twitter爬虫】Twitter网络爬虫

利用selenium爬取Twitter 从2月9日起&#xff0c;Twitter不再支持免费访问Twitter API&#xff0c;继续使用Twitter API支付较高的费用。下面将介绍一种绕过Twitter API爬取推文的方式 Selenium Webdriver框架 首先介绍一下Selenium Webdriver&#xff0c;这是一款web自动化…

计算机专业大学如何自学?常用网站和工具

耗时5小时&#xff0c;第一个B站视频&#xff0c;满足分享欲 计算机专业现状 or 困境&#xff1f;如何自学&#xff1f;常用网站科普&#xff01;_哔哩哔哩_bilibili &#x1f446;发了个视频&#xff0c;结合文章中的链接&#xff0c;保存到自己浏览器收藏夹里就完了 目录 …

React修改Antd组件的样式

修改默认的antd组件&#xff0c;需要使用global import React, { useState, useEffect } from react; import { Tabs, Rate, Steps } from antd; import styles from ./index.less;const Index (props) >{return (<div className{styles.class_steps}><Stepsprog…

maven 环境配置踩坑

今晚在跟着视频学习spring的时候&#xff0c;创建maven工程&#xff0c;一直提示Sync 下载异常。搞了一晚上终于搞定了环境。下面给出一下今晚的总结。 1、确保maven安装并配置好环境变量。 下载并安装maven后&#xff0c;还需要在电脑上配置maven的环境变量。这部分参考网络教…

Hyperledger Fabric网络快速启动

目录 1、网络服务配置 2、关联的docker-compose-base.yaml 各Peer节点容器设置如下信息。 3、被关联的Peer-base.yaml 4、启动网络 2、完成通道的创建 2.1将节点加入应用通道 更新锚节点 2.为什么要创建节点并将其加入应用通道中&#xff1f; 1、网络服务配置 由于要启动…

『赠书活动 | 第十三期』《算力经济:从超级计算到云计算》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第十三期』 本期书籍&#xff1a;《算力经济&#xff1a;从超级计算到云计算》 赠书规则&#xff1a;评论区&#xff1a;点赞&#xff5c;收…

SFP6012-ASEMI代理MHCHXM(海矽美)二极管SFP6012

编辑&#xff1a;ll SFP6012-ASEMI代理MHCHXM&#xff08;海矽美&#xff09;二极管SFP6012 型号&#xff1a;SFP6012 品牌&#xff1a;MHCHXM&#xff08;海矽美&#xff09; 封装&#xff1a;TO-247AB 恢复时间&#xff1a;≤75ns 正向电流&#xff1a;30A 反向耐压&a…

基于Python的电影票房爬取与可视化系统的设计与实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

FlutterUnit 已上架 iOS,暗色模式全面支持

theme: cyanosis 一、FlutterUnit 的全平台支持 FlutterUnit 是我的一个开源项目&#xff0c;基于 Flutter 构建的一个 全平台 应用程序。现在很荣幸地宣布: FlutterUnit 已经上架 iOS 的 App Store &#xff0c;自此主流的几大平台均已提供体验。 项目地址: https://github.co…