目录:
(1)购物车业务简介
(2)购物车模块搭建
(3)搭建service-cart服务
(4)功能—添加入购物车
(5)添加购物车功能开发
(1)购物车业务简介
购物车模块要能够存储顾客所选的的商品,记录下所选商品,还要能随时更新,当用户决定购买时,用户可以选择决定购买的商品进入结算页面。
功能要求:
- 利用缓存提高性能。
- 未登录状态也可以存入购物车,一旦用户登录要进行合并操作。
流程分析:
(2)购物车模块搭建
购物车添加展示流程:
(3)搭建service-cart服务
修改配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.atguigu.gmall</groupId>
<artifactId>service</artifactId>
<version>1.0</version>
</parent>
<version>1.0</version>
<artifactId>service-cart</artifactId>
<packaging>jar</packaging>
<name>service-cart</name>
<description>service-cart</description>
<dependencies>
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>service-product-client</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<finalName>service-cart</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
添加配置文件bootstrap.properties
spring.application.name=service-cart
spring.profiles.active=dev
spring.cloud.nacos.discovery.server-addr=192.168.200.129:8848
spring.cloud.nacos.config.server-addr=192.168.200.129:8848
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml
启动类
package com.atguigu.gmall.cart;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ComponentScan(basePackages = "com.atguigu.gmall")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.atguigu.gmall")
public class ServiceCartApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceCartApplication.class,args);
}
}
(4)功能—添加入购物车
功能解析:
- 商品详情页添加购物车
- 添加购物车,用户可以不需要登录,如果用户没有登录,则生成临时用户id,购物车商品与临时用户id关联,当用户登录后,将临时用户id的购物车商品与登录用户id的商品合并
- 商品详情添加购物车时,先判断用户是否登录,如果没登录,再判断是否存在临时用户,如果cookie中也没有临时用户,则生成临时用户
商品详情页
商品详情添加购物车页面方法(/item/index.html):
addToCart() { // 判断是否登录和是否存在临时用户,如果都没有,添加临时用户 if(!auth.isTokenExist() && !auth.isUserTempIdExist()) { auth.setUserTempId() } window.location.href = 'http://cart.gmall.com/addCart.html?skuId=' + this.skuId + '&skuNum=' + this.skuNum }
服务网关处理
思路:既然userId是从服务网关统一传递过来的,那么临时用户id我们也可以从网关传递过来,改造网关
网关中获取临时用户id
在server-gateway 项目中添加
/**
* 获取当前用户临时用户id
* @param request
* @return
*/
private String getUserTempId(ServerHttpRequest request) {
String userTempId = "";
List<String> tokenList = request.getHeaders().get("userTempId");
if(null != tokenList) {
userTempId = tokenList.get(0);
} else {
MultiValueMap<String, HttpCookie> cookieMultiValueMap = request.getCookies();
HttpCookie cookie = cookieMultiValueMap.getFirst("userTempId");
if(cookie != null){
userTempId = URLDecoder.decode(cookie.getValue());
}
}
return userTempId;
}
设置网关请求头
String userTempId = this.getUserTempId(request);
if(!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)) {
if(!StringUtils.isEmpty(userId)) {
//存储到request作用域
request.mutate().header("userId", userId).build();
}
if(!StringUtils.isEmpty(userTempId)) {
//存储到request作用域
request.mutate().header("userTempId", userTempId).build();
}
//将现在的request 变成 exchange对象
return chain.filter(exchange.mutate().request(request).build());
}
这里的代码可以获取临时用户id
(5)添加购物车功能开发
创建实体
@Data
@ApiModel(description = "购物车")
public class CartInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户id")
private String userId;
@ApiModelProperty(value = "skuid")
private Long skuId;
@ApiModelProperty(value = "放入购物车时价格")
private BigDecimal cartPrice;
@ApiModelProperty(value = "数量")
private Integer skuNum;
@ApiModelProperty(value = "图片文件")
private String imgUrl;
@ApiModelProperty(value = "sku名称 (冗余)")
private String skuName;
@ApiModelProperty(value = "isChecked")
private Integer isChecked = 1;
//实时价格 skuInfo.price
BigDecimal skuPrice;
}
创建添加购物车接口
package com.atguigu.gmall.cart.service;
public interface CartService {
// 添加购物车 用户Id,商品Id,商品数量。
void addToCart(Long skuId, String userId, Integer skuNum);
}
定义业务需要使用的常量,RedisConst类
public static final String USER_KEY_PREFIX = "user:";
public static final String USER_CART_KEY_SUFFIX = ":cart";
public static final long USER_CART_EXPIRE = 30000;
添加购物车实现类
@Service
public class CartServiceImpl implements CartService {
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private RedisTemplate redisTemplate;
@Override
public void addToCart(Long skuId, String userId, Integer skuNum) {
// 获取缓存key
String cartKey = getCartKey(userId);
BoundHashOperations<String, String, CartInfo> boundHashOps = this.redisTemplate.boundHashOps(cartKey);
CartInfo cartInfo = null;
//包含的话更新数量
if(boundHashOp
s.hasKey(skuId.toString())) {
cartInfo = boundHashOps.get(skuId.toString());
cartInfo.setSkuNum(cartInfo.getSkuNum()+skuNum);
cartInfo.setIsChecked(1);
cartInfo.setSkuPrice(productFeignClient.getSkuPrice(skuId));
cartInfo.setUpdateTime(new Date());
} else {
//购物车不存在数据,存入新的数据
cartInfo = new CartInfo();
// 给cartInfo 赋值! 远程请求sku详情数据
SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
// 给表的字段赋值!
cartInfo.setUserId(userId);
cartInfo.setSkuId(skuId);
cartInfo.setCartPrice(skuInfo.getPrice());
cartInfo.setSkuNum(skuNum);
cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
cartInfo.setSkuName(skuInfo.getSkuName());
cartInfo.setCreateTime(new Date());
cartInfo.setUpdateTime(new Date());
//实时价格
cartInfo.setSkuPrice(skuInfo.getPrice());
}
//存储
boundHashOps.put(skuId.toString(), cartInfo);
}
// 获取购物车的key
private String getCartKey(String userId) {
//定义key user:userId:cart
return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
}
}
添加购物车控制器
package com.atguigu.gmall.cart.controller;
@RestController
@RequestMapping("api/cart")
public class CartApiController {
@Autowired
private CartService cartService;
/**
* 添加购物车
* @param skuId
* @param skuNum
* @param request
* @return
*/
@RequestMapping("addToCart/{skuId}/{skuNum}")
public Result addToCart(@PathVariable("skuId") Long skuId,
@PathVariable("skuNum") Integer skuNum,
HttpServletRequest request) {
// 如何获取userId
String userId = AuthContextHolder.getUserId(request);
if (StringUtils.isEmpty(userId)) {
// 获取临时用户Id
userId = AuthContextHolder.getUserTempId(request);
}
cartService.addToCart(skuId, userId, skuNum);
return Result.ok();
}
}
未登录添加购物车:Redis存的key中的用户id是临时id
登录后key中用户id存的是登录的用户的id