购物车数据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

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

步骤五:启动类

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);
}
}

添加到购物车

整体分析

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

接口

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

后端实现:JavaBean

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

购物车列表项对象: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 );
}

}

【畅购商城】购物车模块之添加购物车_前端_04

购物车专门定制的对象

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;

}

后端实现

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

步骤一:创建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("添加成功");


}

}

步骤六:测试

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

前端实现:购买数量

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

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

<input type="text" name="amount" v-model="buyCount" @keyup.prevent="updateCount($event)" value="1" class="amount"/>

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

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

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;
}
},

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

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

前端实现

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

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

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

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

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

<input type="submit" value="" class="add_btn" @click.prevent="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页面

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

<template>
<div>
购物车
</div>
</template>

<script>
export default {

}
</script>

<style>

</style>