【畅购商城】购物车模块之添加购物车

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【畅购商城】购物车模块之添加购物车

购物车数据2种形态:

       登录态:保存到服务器端的redis中

       没登录:保存在浏览器端 localStorage 中

搭建购物车服务:8095

步骤一:创建changgou4-service-cart 项目

步骤二:修改pom.xml文件,添加坐标

<dependencies>
    <!--自定义项目-->
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4-common-auth</artifactId>
    </dependency>
    <dependency>
        <groupId>com.czxy.changgou</groupId>
        <artifactId>changgou4-pojo</artifactId>
    </dependency>
    <!--web起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>
    <!-- nacos 客户端 -->
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
    </dependency>
    <!-- nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- openfeign 远程调用 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
    </dependency>
    <!--fastjson-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>
</dependencies>

步骤三:创建yml文件,

#端口号
server:
  port: 8095
spring:
  application:
    name: cart-service
  redis:
    host: 127.0.0.1
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848   #nacos服务地址
#自定义内容
sc:
  jwt:
    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
    pubKeyPath: D:/rsa/rsa.pub # 公钥地址
    priKeyPath: D:/rsa/rsa.pri # 私钥地址
    expire: 360 # 过期时间,单位分钟

步骤四:拷贝JWT配合类 + Swagger + Redis、


ba3ccda668cc4bb3ac8859cefbe26ca4.png

步骤五:启动类

package com.czxy.changgou4;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CGCartServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(CGCartServiceApplication.class, args);
    }
}

添加到购物车

整体分析

1ea5e1c9da25439c97d34c17a7df5307.png

接口

POST http://localhost:10010/cart-service/carts
{
    "skuid": 2600242,
    "count": 5,
    "checked": true
}

后端实现:JavaBean

eb6e2c1c50cf4e028e47d8a0938ed03e.png


购物车列表项对象:CartItem (某一件商品的购买情况:商品、购买数量 等)

package com.czxy.changgou4.cart;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class CartItem {
    private Integer skuid;
    private Integer spuid;
    @JsonProperty("goods_name")
    private String goodsName;
    private Double price;
    private Integer count;//购买数量
    private Boolean checked;
    private String midlogo;
    @JsonProperty("spec_info")
    private String specInfo;
}

购物车对象:Cart

package com.czxy.changgou4.cart;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class Cart {
    private Map<Integer , CartItem > data = new HashMap<>();
    private Double total;
    public Double getTotal() {
        double sum = 0.0;
        for (CartItem cartItem : data.values()) {
            //只统计勾选的价格
            if(cartItem.getChecked()){
                sum += ( cartItem.getPrice() * cartItem.getCount());
            }
        }
        return sum;
    }
    public void addCart(CartItem cartItem) {
        CartItem temp = data.get(cartItem.getSkuid());
        if(temp == null) {
            data.put( cartItem.getSkuid() , cartItem);
        } else {
            temp.setCount( cartItem.getCount() + temp.getCount() );
        }
    }
    public void updateCart(Integer skuid, Integer count , Boolean checked) {
        CartItem temp = data.get(skuid);
        if(temp != null) {
            temp.setCount( count );
            temp.setChecked(checked);
        }
    }
    public void deleteCart(Integer skuid) {
        data.remove( skuid );
    }
}

aba77d89974c4c82977d13e47c8046c7.png

购物车专门定制的对象

CartCategory

package com.czxy.changgou4.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class CartCategory {
    private Integer id;
    @JsonProperty("cat_name")
    private String catName;
    @JsonProperty("parent_id")
    private Integer parentId;
    @JsonProperty("is_parent")
    private Boolean isParent;
    //当前分类具有的所有孩子
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<CartCategory> children = new ArrayList<>();
}

CartSpecificationOption

package com.czxy.changgou4.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class CartSpecificationOption {
    private Integer id;
    @JsonProperty("spec_id")
    private Integer specId;                //外键,规格ID
    private CartSpecification specification; //外键对应对象
    @JsonProperty("option_name")
    private String optionName;             //选项名称
}

CartSpecification

package com.czxy.changgou4.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class CartSpecification {
    private Integer id;
    @JsonProperty("spec_name")
    private String specName;                    //规格名称
    private Integer categoryId;                     //分类外键
    private CartCategory category;                      //分类外键对应对象
    private List<CartSpecificationOption> options;      //一个规格,具有多个规格选项
}

CartOneSkuResult

package com.czxy.changgou4.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class CartOneSkuResult {
    private Integer skuid;
    private Integer spuid;
    @JsonProperty("goods_name")
    private String goodsName;
    private Double price;
    @JsonProperty("on_sale_date")
    private Date onSaleDate;
    @JsonProperty("comment_count")
    private Integer commentCount;
    @JsonProperty("comment_level")
    private Integer commentLevel;
    @JsonProperty("cat1_info")
    private CartCategory cat1Info;
    @JsonProperty("cat2_info")
    private CartCategory cat2Info;
    @JsonProperty("cat3_info")
    private CartCategory cat3Info;
    private Map<String, String> logo;
    private List<Map> photos;
    private String description;
    private String aftersale;
    private Integer stock;
    @JsonProperty("spec_list")
    private List<CartSpecification> specList;
    // id_list:'规格ID:选项ID|规格ID:选项ID|...',
    // id_txt:'规格名称:选项名称|规格名称:选项名称|...'
    @JsonProperty("spec_info")
    private Map<String, String> specInfo;
    @JsonProperty("sku_list")
    private List<Map<String, String>> skuList;
}

后端实现

4a9eb0eae8ec433c8686ad5a884fdfcc.png


步骤一:创建CartVo,用于封装请求参数

package com.czxy.changgou4.vo;
import lombok.Data;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
public class CartVo {
    private Integer skuid ;     //"SKUID",
    private Integer count;      //"购买数量"
    private Boolean checked;    //"是否选中"
}

步骤二:创建SkuClient,用于查询详情

package com.czxy.changgou4.feign;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.OneSkuResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@FeignClient(value="web-service",path = "/sku")
public interface SkuClient {
    @GetMapping("/goods/{skuid}")
    public BaseResult<OneSkuResult> findSkuById(@PathVariable("skuid") Integer skuid);
}

步骤三:创建CartService接口,用于完成添加业务逻辑

package com.czxy.changgou4.service;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.vo.CartVo;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
public interface CartService {
    /**
     * 给指定用户添加商品
     * @param user
     * @param cartVo
     */
    public void addCart(User user , CartVo cartVo);
}

步骤四:创建CartService实现类

package com.czxy.changgou4.service.impl;
import com.alibaba.fastjson.JSON;
import com.czxy.changgou4.cart.Cart;
import com.czxy.changgou4.cart.CartItem;
import com.czxy.changgou4.feign.SkuClient;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.CartService;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.CartOneSkuResult;
import com.czxy.changgou4.vo.CartVo;
import com.czxy.changgou4.vo.OneSkuResult;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Service
@Transactional
public class CartServiceImpl implements CartService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private SkuClient skuClient;
    @Override
    public void addCart(User user, CartVo cartVo) {
        //1 获得购物车
        Cart cart;
        String key = "cart" + user.getId();
        String cartStr = stringRedisTemplate.opsForValue().get(key);
        // 处理是否有购物车,没有创建,有转换(jsonStr --> java对象 )
        if(cartStr != null){
            //如果有,将json字符串转换购物车对象
            cart = JSON.parseObject( cartStr , Cart.class);
        } else {
            //如果没有创建一个
            cart = new Cart();
        }
        //2 保存商品
        // 2.1 确定购物买商品
        BaseResult<CartOneSkuResult> entity = skuClient.findSkuById(cartVo.getSkuid());
        CartOneSkuResult oneSkuResult = entity.getData();
        // * 将OneSkuResult 转换成 CartItem
        CartItem cartItem = new CartItem();
        cartItem.setSkuid( oneSkuResult.getSkuid() );
        cartItem.setSpuid( oneSkuResult.getSpuid() );
        cartItem.setGoodsName( oneSkuResult.getGoodsName() );
        cartItem.setPrice( oneSkuResult.getPrice() );
        cartItem.setCount( cartVo.getCount() );        //购买数量,用户传递的
        cartItem.setChecked(true);
        cartItem.setMidlogo( oneSkuResult.getLogo().get("biglogo"));
        cartItem.setSpecInfo( JSON.toJSONString( oneSkuResult.getSpecInfo() ) );   //将对象转换json字符串
        // 2.2 添加到购物车
        cart.addCart( cartItem );
        System.out.println(JSON.toJSONString(cart) );
        //3 保存购物车
        stringRedisTemplate.opsForValue().set( key , JSON.toJSONString(cart) );
    }
}

步骤五:创建CartController

package com.czxy.changgou4.controller;
import com.czxy.changgou4.config.JwtProperties;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.CartService;
import com.czxy.changgou4.utils.JwtUtils;
import com.czxy.changgou4.vo.BaseResult;
import com.czxy.changgou4.vo.CartVo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@RestController
@RequestMapping("/carts")
public class CartController {
    @Resource
    private CartService cartService;
    @Resource
    private HttpServletRequest request;
    @Resource
    private JwtProperties jwtProperties;
    @PostMapping
    public BaseResult addCart(@RequestBody CartVo cartVo){
        //1 获得用户信息
        // 1.1 获得token
        String token = request.getHeader("Authorization");
        // 1.2 解析token
        User loginUser = null;
        try {
            loginUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(),User.class);
        } catch (Exception e) {
            return BaseResult.error("token失效或未登录");
        }
        //2 添加操作
        cartService.addCart( loginUser , cartVo );
        //3 提示
        return BaseResult.ok("添加成功");
    }
}

步骤六:测试

9d788201cadd464fa2973445792030d3.png

前端实现:购买数量

步骤一:修改Goods.vue ,为文本框添加键盘事件,用于校验输入的数据


67e58ffd732c42e7aff9ce2feab0f6f7.png


步骤二:修改Goods.vue ,完成updateCount函数


a392153c33c64ad59a561aa035e683af.png

 updateCount : function(e){
      // e.target.value 获得用户输入的数据
      //使用正则处理数字
      if( /^\d+$/.test(e.target.value) ){
        //如果是数字,小于1,默认为1
        if( e.target.value < 1) {
          this.buyCount = 1;
        }
      } else {
        //默认为1
        this.buyCount = 1;
      }
    },

步骤三:检查+和-已完成功能

58bd3f6e959b44cd8467e72a7ebdae44.png

前端实现

步骤一:修改 api.js ,完成“添加到购物车”方法

1420c12785d04540b0a9ed2e006941f2.png

  //添加到购物车
  addToCart : ( params ) => {
    return axios.post("/cart-service/carts", params )
  },

步骤二:修改Goods.vue,给“加入购物车”绑定点击事件 addToCartFn

bd81d0ea41c840a3ad66e92ac224dbec.png

步骤三:修改Goods.vue,完成addToCartFn功能

未登录:保存到sessionStorage

登录:保存到redis

待完善功能:用户登录时,将sessionStorage保存的商品信息合并到redis中


async addToCartFn(){
      //获得登录标识
      let token = sessionStorage.getItem("token");
      if(token != null){
        //登录:发送ajax进行添加
        let newGoods = {
          skuid: this.$route.query.id,
          count:this.buyCount
        };
        //登录状态下的添加商品到购物车操作
        let {data} = await this.$request.addToCart(newGoods)
        if(data.code == 20000){
          //location.href = "flow1"
          this.$router.push('flow1')
        } else {
          alert(data.data.errmsg);
        }
        return;
      }
      //未登录:在浏览器保存
      //1 准备添加物品数据
      var newGoods = {
        skuid: this.goodsInfo.skuid,
        goods_name:this.goodsInfo.goods_name,
        price:this.goodsInfo.price,
        count:this.buyCount,
        checked:true,
        midlogo:this.goodsInfo.logo.smlogo,
        spec_info: JSON.stringify(this.goodsInfo.spec_info)
      };
      //2 维护数据:本地已经存储信息 和 新添加信息 合并
      var cartStr = localStorage.getItem("cart");
      var cart;
      if(cartStr == null) {
        // 2.1 第一次添加,直接已数组方式添加
        cart = [newGoods];
      } else {
        //判断是否存在(将字符串转换数组、依次遍历)
        cart = JSON.parse(cartStr);
        //是否为新物品,默认为新的
        let isNew = true;
        cart.forEach( g => {
          //已有数据的id 和 新物品id 是否一样
          if(g.skuid == newGoods.skuid){
            //不是新物品
            isNew = false;
            // 2.3 已有,重复,先获得对应,修改数量
            g.count += parseInt(newGoods.count);
          }
        });
        if(isNew == true){
          // 2.2 已有,不重复,先获得数组,后追加
          cart.push(newGoods);
        }
      }
      //3 存放到浏览器
      var cartStr = JSON.stringify(cart);
      localStorage.setItem("cart" , cartStr );
      //4 跳转页面
      location.href = "flow1"
    }

步骤四:编写flow1页面

64b7efe44a634ca58dc2db28bce56593.png

<template>
  <div>
    购物车
  </div>
</template>
<script>
export default {
}
</script>
<style>
</style>



相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
JavaScript 数据安全/隐私保护
|
3月前
|
存储 SQL NoSQL
商城业务---购物车
这篇文章讨论了商城业务中购物车功能的实现,包括用户登录状态下和未登录状态下购物车的处理方式。文章详细描述了购物车的基本需求、流程分析、判断用户登录状态的拦截器实现、临时购物车和登录购物车的数据操作,以及添加商品到购物车的过程。此外,还提到了可能遇到的问题和解决方案,以及在Redis中存储商品信息的不同数据结构选择。最后,文章展示了未登录和登录状态下购物车的使用效果和Redis中数据的存储情况。
商城业务---购物车
|
6月前
|
JavaScript
基础购物车
基础购物车
39 1
|
SQL JSON 前端开发
加入购物车【项目 商城】
加入购物车【项目 商城】
54 0
|
存储 JSON 前端开发
从零玩转系列之微信支付实战PC端装修我的订单页面2
从零玩转系列之微信支付实战PC端装修我的订单页面
120 0
|
前端开发 小程序 安全
从零玩转系列之微信支付实战PC端装修我的订单页面1
从零玩转系列之微信支付实战PC端装修我的订单页面
92 0
|
存储 NoSQL MongoDB
商城业务:购物车
商城业务:购物车
超市购物车功能
超市购物车功能
88 0
|
存储 前端开发 NoSQL
【畅购商城】购物车模块之查看购物车
【畅购商城】购物车模块之查看购物车
154 0
【畅购商城】购物车模块之查看购物车
|
前端开发 JavaScript
【畅购商城】购物车模块之修改购物车以及结算
【畅购商城】购物车模块之修改购物车以及结算
173 0
【畅购商城】购物车模块之修改购物车以及结算