电商平台-常见购物车功能实现介绍
- 购物车里面的购物项常见字段
user_id、product_id、buy_num
- 购物车常见实现方式
实现方式一: 存储到数据库
性能存在瓶颈
实现方式二: 前端本地存储-localstorage-sessionstorage
localstorage在浏览器中存储 key/value 对,没有过期时间。
sessionstorage在浏览器中存储 key/value 对,在关闭会话窗口后将会删除这些数据。
实现方式三: 后端存储到缓存如redis
可以开启AOF持久化防止重启丢失(推荐)
可以存储海量的数据
实现方式四: 后端存储到缓存如redis-并同步更新到数据库
可能会想到缓存和数据库的一致性,加了用户唯一标识后,没高并发操作同一数据的场景
1. 代码落地 -购物车和购物项VO类开发
CartVO 购物车:一个购物车里面,存在多个购物项
/**
* @author:Carl
* @Description: 购物车 一个购物车里面,存在多个购物项
*/
public class CartVO {
/**
* 购物项集合
*/
@JsonProperty("cart_items")
private List<CartItemVO> cartItems;
/**
* 购物项总数量
*/
@JsonProperty("total_num")
private Integer totalNum;
/**
* 购物车总金额
*/
@JsonProperty("total_prices")
private BigDecimal totalPrices;
/**
* 最终真实支付价格
*/
@JsonProperty("real_pay_price")
private BigDecimal realPayPrice;
public List<CartItemVO> getCartItems() {
return cartItems;
}
public Integer getTotalNum() {
if (this.cartItems != null){
int total = cartItems.stream().mapToInt(CartItemVO::getBuyNum).sum();
return total;
}
return 0;
}
public BigDecimal getTotalPrices() {
BigDecimal amount = new BigDecimal("0");
if (this.cartItems != null){
for (CartItemVO cartItemVO : cartItems){
BigDecimal totalAmount = cartItemVO.getTotalAmount();
amount = amount.add(totalAmount);
}
}
return amount;
}
public BigDecimal getRealPayPrice() {
BigDecimal amount = new BigDecimal("0");
if (this.cartItems != null){
for (CartItemVO cartItemVO : cartItems){
BigDecimal totalAmount = cartItemVO.getTotalAmount();
amount = amount.add(totalAmount);
}
}
return amount;
}
public void setCartItems(List<CartItemVO> cartItems) {
this.cartItems = cartItems;
}
}CartItemVO 购物项:
- 商品id
- 购买数量
- 商品标题(冗余)
- 商品图片(冗余)
- 商品单价
- 总价格 ( 单价*数量 )
/**
* @author:Carl
* @Description:购物项
*/
public class CartItemVO {
/**
* 商品id
*/
@JsonProperty("product_id")
private Long productId;
/**
* 购买数量
*/
@JsonProperty("buy_num")
private Integer buyNum;
/**
* 商品标题
*/
@JsonProperty("product_title")
private String productTitle;
/**
* 商品图片
*/
@JsonProperty("product_img")
private String productImg;
/**
* 商品单价
*/
private BigDecimal amount;
/**
*总价格 ( 单价*数量 )
*/
@JsonProperty("total_amount")
private BigDecimal totalAmount;
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public Integer getBuyNum() {
return buyNum;
}
public void setBuyNum(Integer buyNum) {
this.buyNum = buyNum;
}
public String getProductTitle() {
return productTitle;
}
public void setProductTitle(String productTitle) {
this.productTitle = productTitle;
}
public String getProductImg() {
return productImg;
}
public void setProductImg(String productImg) {
this.productImg = productImg;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
/**
* 总价格 ( 单价*数量 )
* @return TotalAmount
*/
public BigDecimal getTotalAmount() {
return this.amount.multiply(new BigDecimal(this.buyNum));
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
}2.代码落地 - 购物车redis数据结构
- 购物车数据结构介绍
- 一个购物车里面,存在多个购物项
- 所以 购物车结构是一个双层Map:
- Map<String,Map<String,String>>
- 第一层Map,Key是用户id
- 第二层Map,Key是购物车中商品id,值是购物车数据
- 对应redis里面的存储
- redis里面有多种数据结构,应该使用哪种?
- 答案是 hash结构

3. 商品微服务购物车接口 - 代码落地
3.1 interfaces :
/**
添加到购物车
@param cartItemRequest
@return
*/
@RequestMapping(value = "add_to_cart",method = {RequestMethod.GET,RequestMethod.POST})
JsonData addToCart(@RequestBody CartItemRequest cartItemRequest){
cartService.addToCart(cartItemRequest);
return JsonData.buildSuccess();}
/**
* 清空购物车 只需要携带header头信息即可
*
* @return
*/
@RequestMapping(value = "clear_cart", method = {RequestMethod.GET, RequestMethod.POST})
JsonData clear() {
cartService.clear();
return JsonData.buildSuccess();
}
/**
* 查看我的购物车
*/
@RequestMapping(value = "my_cart", method = {RequestMethod.GET, RequestMethod.POST})
JsonData myCart() {
CartVO myCart = cartService.getMyCart();
return JsonData.buildSuccess(myCart);
}
/**
* 删除购物车中的购物项
*/
@RequestMapping(value = "remove_item", method = RequestMethod.DELETE)
JsonData removeItem(Long productId) {
cartService.removeItemByProductId(productId);
return JsonData.buildSuccess();
}
/**
* 修改购物项数量
*/
@RequestMapping(value = "change_Item", method = {RequestMethod.POST,RequestMethod.GET})
JsonData changItem(@RequestBody CartItemRequest cartItemRequest) {
cartService.changItem(cartItemRequest);
return JsonData.buildSuccess();
}3.2 拦截器配置:
public LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor())
.addPathPatterns("/api/cart/*/**")
//不拦截的路径
.excludePathPatterns("");
WebMvcConfigurer.super.addInterceptors(registry);
}3.3 application :
/**
* 添加商品到购物车接口
* @param cartItemRequest
*/
void addToCart(CartItemRequest cartItemRequest);
/**
* 清空购物车
*/
void clear();
/**
* 获得购物车详情
* @return
*/
CartVO getMyCart();
/**
* 删除一个购物车中的购物项通过商品id
* @param productId
*/
void removeItemByProductId(Long productId);
/**
* 修改购物项
* @param cartItemRequest
*/
void changItem(CartItemRequest cartItemRequest);其中addToCart :
Long productId = cartItemRequest.getProductId();
Integer buyNum = cartItemRequest.getBuyNum();
BoundHashOperations<String,Object,Object> mycart = getMyCartOPs();
Object cacheObj = mycart.get(productId);
String result = "";
if (cacheObj != null){
result = (String) cacheObj;
}
if (StringUtils.isBlank(result)){
//不存在则创建一个购物车项
CartItemVO cartItemVO = new CartItemVO();
ProductVO productVO = productService.findByDetail(productId);
if (productVO == null){
throw new BizException(BizCodeEnum.CART_FAIL);
}
cartItemVO.setAmount(productVO.getAmount());
cartItemVO.setBuyNum(buyNum);
cartItemVO.setProductId(productId);
cartItemVO.setProductImg(productVO.getCoverImg());
cartItemVO.setProductTitle(productVO.getTitle());
mycart.put(productId, JSON.toJSONString(cartItemVO));
}else {
//存在则新增数量
CartItemVO cartItemVO = JSON.parseObject(result,CartItemVO.class);
cartItemVO.setBuyNum(cartItemVO.getBuyNum()+buyNum);
mycart.put(productId,JSON.toJSONString(cartItemVO));
}