购物车数据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、
步骤五:启动类
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); } }
添加到购物车
整体分析
接口
POST http://localhost:10010/cart-service/carts { "skuid": 2600242, "count": 5, "checked": true }
后端实现:JavaBean
购物车列表项对象: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 ); } }
购物车专门定制的对象
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; }
后端实现
步骤一:创建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("添加成功"); } }
步骤六:测试
前端实现:购买数量
步骤一:修改Goods.vue ,为文本框添加键盘事件,用于校验输入的数据
步骤二:修改Goods.vue ,完成updateCount函数
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; } },
步骤三:检查+和-已完成功能
前端实现
步骤一:修改 api.js ,完成“添加到购物车”方法
//添加到购物车 addToCart : ( params ) => { return axios.post("/cart-service/carts", params ) },
步骤二:修改Goods.vue,给“加入购物车”绑定点击事件 addToCartFn
步骤三:修改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页面
<template> <div> 购物车 </div> </template> <script> export default { } </script> <style> </style>