购物车模块
前言
上周 ‘事多’(懒)没更新,落下的进度后续会更新,现在更新下目前的进度
购物车数据2种形态
- 登录态:保存到服务器端的redis中
- 没登录:保存在浏览器端 localStorage 中
1.搭建购物车服务
1.1步骤一:创建changgou4-service-cart 项目
1.2步骤二:修改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>
1.3步骤三:创建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: # 登录校验的密钥
pubKeyPath: # 公钥地址
priKeyPath: # 私钥地址
expire: 360 # 过期时间,单位分钟
1.4步骤四:拷贝JWT配合类 + Swagger + Redis
1.5步骤五:启动类
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;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CartApplication {
public static void main(String[] args) {
SpringApplication.run(CartApplication.class,args);
}
}
2.添加到购物车
2.1 整体分析
2.2接口
POST http://localhost:10010/cart-service/carts
{
"skuid": 2600242,
"count": 5,
"checked": true
}
2.3后端:分析
2.4后端实现:JavaBean
- 购物车列表项对象:CartItem (某一件商品的购买情况:商品、购买数量 等)
package com.czxy.changgou4.cart;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@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;
@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;
@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;
@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;
@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;
@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;
}
2.5后端实现
步骤一:创建CartVo,用于封装请求参数
package com.czxy.changgou4.vo;
import lombok.Data;
@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;
@FeignClient(value="changgou4-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;
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;
@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;
@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("添加成功");
}
}
步骤六:测试
2.6前端实现:购买数量
步骤一:修改 api.js ,完成“添加到购物车”方法
//添加到购物车
addToCart : ( params ) => {
return axios.post("/cart-service/carts", params )
},
步骤二:修改Goods.vue,给“加入购物车”绑定点击事件 addToCartFn
<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页面
<template>
<div>购物车</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
感谢宁看到这里,你的三连就是我更新的动力 冲冲冲!!!!!!