乐优商城笔记十一:购物车
完成乐优商城购物车模块。

购物车功能分析

需求

  • 用户可以在登录状态下将商品添加到购物车
    • 放入数据库
    • 放入redis(采用)
  • 用户可以在未登录状态下将商品添加到购物车
    • 放入localstorage
  • 用户可以使用购物车一起结算下单
  • 用户可以查询自己的购物车
  • 用户可以在购物车中修改购买商品的数量。
  • 用户可以在购物车中删除商品。
  • 在购物车中展示商品优惠信息
  • 提示购物车商品价格变化

流程图

这幅图主要描述了两个功能:新增商品到购物车、查询购物车。

新增商品:

  • 判断是否登录
    • 是:则添加商品到后台Redis中
    • 否:则添加商品到本地的Localstorage

无论哪种新增,完成后都需要查询购物车列表:

  • 判断是否登录
    • 否:直接查询localstorage中数据并展示
    • 是:已登录,则需要先看本地是否有数据,
      • 有:需要提交到后台添加到redis,合并数据,而后查询
      • 否:直接去后台查询redis,而后返回

未登录购物车

准备工作

购物车的数据结构

首先分析一下未登录购物车的数据结构。

看下页面展示需要什么数据:

因此每一个购物车信息,都是一个对象,包含:

1
2
3
4
5
6
7
8
{
skuId:2131241,
title:"小米6",
image:"",
price:190000,
num:1,
ownSpec:"{"机身颜色":"陶瓷黑尊享版","内存":"6GB","机身存储":"128GB"}"
}

另外,购物车中不止一条数据,因此最终会是对象的数组。即:

1
2
3
[
{...},{...},{...}
]

web本地存储

知道了数据结构,下一个问题,就是如何保存购物车数据。前面我们分析过,可以使用Localstorage来实现。Localstorage是web本地存储的一种,那么,什么是web本地存储呢?

什么是web本地存储?

web本地存储主要有两种方式:

  • LocalStoragelocalStorage 方法存储的数据没有时间限制。第二天、第二周或下一年之后,数据依然可用。
  • SessionStoragesessionStorage 方法针对一个 session 进行数据存储。当用户关闭浏览器窗口后,数据会被删除。

LocalStorage的用法

语法非常简单:

1
2
3
localStorage.setItem("key","value"); // 存储数据
localStorage.getItem("key"); // 获取数据
localStorage.removeItem("key"); // 删除数据

注意:localStorage和SessionStorage都只能保存字符串

不过,在common.js中,已经对localStorage进行了简单的封装:

示例:

1533739929733

获取num

ly-page项目中的item.html模板中做如下修改:

  • 添加购物车需要知道购物的数量,所以我们需要获取数量大小。我们在Vue中定义num,保存数量

  • 编写方法,用于数量的增加和减少

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    methods: {
    incrment() {
    this.num++
    },
    decrment() {
    if (this.num > 1) {
    this.num--
    }
    }
    }
  • 将num与页面的input框绑定,同时给+-的按钮绑定事件

添加商品到购物车

ly-page项目中的item.html模板中做如下修改:

  • 加入购物车按钮添加单击事件

  • 编写addCart方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    addCart(){
    ly.verifyUser().then(res=>{
    // 已登录发送信息到后台,保存到redis中

    }).catch(()=>{
    // 未登录保存在浏览器本地的localStorage中
    // 1、查询本地购物车
    let carts = ly.store.get("carts") || [];
    let cart = carts.find(c=>c.skuId===this.sku.id);
    // 2、判断是否存在
    if (cart) {
    // 3、存在更新数量
    cart.num += this.num;
    } else {
    // 4、不存在,新增
    cart = {
    skuId: this.sku.id,
    title: this.sku.title,
    price: this.sku.price,
    image: this.sku.images,
    num: this.num,
    ownSpec: this.sku.ownSpec
    }
    carts.push(cart);
    }
    // 把carts写回localstorage
    ly.store.set("carts", carts);
    // 跳转
    window.location.href = "http://www.leyou.com/cart.html";
    });
    }
  • common.js中加入verifyUser()方法

  • 测试

加入购物车成功。

查询购物车

页面加载获取购物车

  • 购物车页面加载时,就应该去查询购物车。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <script type="text/javascript">
    var cartVm = new Vue({
    el: "#cartApp",
    data: {
    ly,
    carts: [],// 购物车数据
    },
    created() {
    this.loadCarts();
    },
    methods: {
    loadCarts() {
    // 先判断登录状态
    ly.verifyUser().then(() => {
    // 已登录

    }).catch(() => {
    // 未登录
    this.carts = ly.store.get("carts") || [];
    })
    }
    },
    components: {
    shortcut: () => import("/js/pages/shortcut.js")
    }
    })
    </script>
  • 查看Vue实例中的购物车数据

渲染购物车数据

在页面中展示carts的数据

修改数量

  • 给页面的 +-绑定点击事件,修改num 的值

  • 编写incrmentdecrment方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    increment(c) {
    c.num++;
    ly.verifyUser().then(() => {
    // TODO 已登录,向后台发起请求
    }).catch(() => {
    // 未登录,直接操作本地数据
    ly.store.set("carts", this.carts);
    })
    },
    decrement(c) {
    if (c.num <= 1) {
    return;
    }
    c.num--;
    ly.verifyUser().then(() => {
    // TODO 已登录,向后台发起请求
    }).catch(() => {
    // 未登录,直接操作本地数据
    ly.store.set("carts", this.carts);
    })
    }

删除购物车项

  • 删除按钮绑定单击事件

  • 编写deleteCart方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    deleteCart(i){
    ly.verifyUser().then(res=>{
    // TODO,已登录购物车
    }).catch(()=>{
    // 未登录购物车
    this.carts.splice(i, 1);
    ly.store.set("carts", this.carts);
    })
    }

选中商品

选中单个

  • 在Vue中定义变量,记录被选中的购物车项

  • selected与页面的选择框绑定,值为当前购物车项

初始化全部选中

  • 修改loadCarts方法

计算所有商品总价

  • 在Vue加入计算方法

    1
    2
    3
    4
    5
    computed: {
    totalPrice() {
    return ly.formatPrice(this.selected.reduce((c1, c2) => c1 + c2.num * c2.price, 0));
    }
    }
  • 页面调用计算总价

效果

登录购物车

完成已登录购物车。

在刚才的未登录购物车编写时,已经预留好了编写代码的位置,逻辑也基本一致。

搭建购物车微服务

创建module

  • GroupId:com.leyou.service
  • ArtifactId:ly-cart

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>com.leyou</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<groupId>com.leyou.service</groupId>
<artifactId>ly-cart</artifactId>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

</project>

启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.leyou;

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

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8008
spring:
application:
name: cart-service
redis:
host: 192.168.136.103
port: 6379
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9999/eureka

用户鉴权

引入依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.leyou.auth</groupId>
<artifactId>ly-auth-common</artifactId>
<version>${leyou.latest.version}</version>
</dependency>
<dependency>
<groupId>com.leyou.common</groupId>
<artifactId>ly-common</artifactId>
<version>${leyou.latest.version}</version>
</dependency>

配置公钥

application.yml中新增公钥配置

1
2
3
4
ly:
jwt:
pubKeyPath: c:\\key\\rsa.pub # 公钥地址
cookieName: LY_TOKEN # cookie的名称

JwtProperties

ly-gateway中复制过来即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.leyou.cart.config;

import com.leyou.auth.utils.RsaUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;
import java.security.PublicKey;

@Data
@Slf4j
@ConfigurationProperties(prefix = "ly.jwt")
public class JwtProperties {

private String pubKeyPath;// 公钥

private PublicKey publicKey; // 公钥

private String cookieName;

@PostConstruct
public void init(){
try {
// 获取公钥和私钥
this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
} catch (Exception e) {
log.error("初始化公钥失败!", e);
throw new RuntimeException();
}
}

}

编写拦截器

编写连接器对所有的请求进行统一鉴权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.leyou.cart.interceptor;

import com.leyou.auth.entity.UserInfo;
import com.leyou.auth.utils.JwtUtils;
import com.leyou.cart.config.JwtProperties;
import com.leyou.common.util.CookieUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* 用户鉴权拦截器
*/
@Slf4j
public class UserInterceptor extends HandlerInterceptorAdapter {

private JwtProperties jwtProperties;

// 将用户信息存放到当前线程中
private static ThreadLocal<UserInfo> userInfoThreadLocal = new ThreadLocal<>();

public UserInterceptor(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}

/**
* 前置拦截, 从cookie中获取User信息
*
* @param request http请求
* @param response http响应
* @param handler 响应的处理器, 可以自定义controller处理响应
* @return boolean 获取到User信息返回true, 否则返回false
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
// 从cookie中获取token
String token = CookieUtils.getCookieValue(request, jwtProperties.getCookieName());
// 从token中解析User信息
UserInfo userInfo = JwtUtils.getUserInfo(jwtProperties.getPublicKey(), token);
if (userInfo.getId() == null) {
log.warn("[购物车服务] 解析用户凭证失败");
return false;
}
userInfoThreadLocal.set(userInfo);

return true;
} catch (Exception e) {
log.error("[购物车服务] 用户权发生异常, ", e);
return false;
}
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
userInfoThreadLocal.remove();
}

/**
* 获取用户信息
*
* @return UserInfo 用户信息
*/
public static UserInfo getUserInfo() {
return userInfoThreadLocal.get();
}
}
  • 这里我们使用了ThreadLocal来存储查询到的用户信息,线程内共享,因此请求到达Controller后可以共享User。
  • 并且对外提供了静态的方法:getLoginUser()来获取User信息。

配置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.leyou.cart.config;

import com.leyou.cart.interceptor.UserInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class MvcConfiguration implements WebMvcConfigurer {

@Autowired
private JwtProperties jwtProperties;

@Bean
public UserInterceptor getUserInterceptor() {
return new UserInterceptor(jwtProperties);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求
registry.addInterceptor(getUserInterceptor()).addPathPatterns("/**");
}
}

购物车设计

当用户登录时,需要把购物车数据保存到后台,可以选择保存在数据库。但是购物车是一个读写频率很高的数据。因此这里选择读写效率比较高的Redis作为购物车存储。

Redis有5种不同数据结构,这里选择哪一种比较合适呢?

  • 首先不同用户应该有独立的购物车,因此购物车应该以用户的作为key来存储,Value是用户的所有购物车信息。这样看来基本的k-v结构就可以了。
  • 但是,对购物车中的商品进行增、删、改操作,基本都需要根据商品id进行判断,为了方便后期处理,购物车也应该是k-v结构,key是商品id,value才是这个商品的购物车信息。

综上所述,购物车结构是一个双层Map:Map<String,Map<String,String>>

  • 第一层Map,Key是用户id
  • 第二层Map,Key是购物车中商品id,值是购物车数据

购物车实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.leyou.cart.pojo;

import lombok.Data;

/**
* 购物车实体类
*/
@Data
public class Cart {

private Long userId;// 用户id
private Long skuId;// 商品id
private String title;// 标题
private String image;// 图片
private Long price;// 加入购物车时的价格
private Integer num;// 购买数量
private String ownSpec;// 商品规格参数

}

添加商品到购物车

item-service新增查询sku接口

  • controller

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 查询sku信息
    *
    * @param skuId skuId
    * @return Sku 商品sku信息
    */
    @GetMapping("/sku/{skuId}")
    public ResponseEntity<Sku> querySkuById(@PathVariable("skuId") Long skuId) {
    return ResponseEntity.ok(goodsService.querySkuById(skuId));
    }
  • service

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 查询sku信息
    *
    * @param skuId skuId
    * @return Sku 商品sku信息
    */
    public Sku querySkuById(Long skuId) {
    Sku sku = skuMapper.selectByPrimaryKey(skuId);
    if (sku == null || sku.getId() == null) {
    throw new LyException(LyExceptionEnum.SKU_NOT_FOUND);
    }

    return sku;
    }
  • goodsApi

    GoodsApi对外开放接口

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 查询sku信息
    *
    * @param skuId skuId
    * @return Sku 商品sku信息
    */
    @GetMapping("goods/sku/{skuId}")
    Sku querySkuById(@PathVariable("skuId") Long skuId);

GoodsClient

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.leyou.cart.client;

import com.leyou.api.GoodsApi;
import com.leyou.common.util.LeyouConstants;
import org.springframework.cloud.openfeign.FeignClient;

/**
* GoodsClient
*/
@FeignClient(LeyouConstants.SERVICE_ITEM)
public interface GoodsClient extends GoodsApi {

}

需要引入ly-item-interface的依赖。

CartController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.leyou.cart.controller;

import com.leyou.cart.interceptor.UserInterceptor;
import com.leyou.cart.pojo.Cart;
import com.leyou.cart.service.CartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CartController {

@Autowired
private CartService cartService;

@PostMapping
public ResponseEntity<Void> addCart(@RequestBody Cart cart) {
// 将userid放入cart
cart.setUserId(UserInterceptor.getUserInfo().getId());
cartService.saveCart(cart);
return ResponseEntity.ok().build();
}
}

CartService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.leyou.cart.service;

import com.leyou.cart.client.GoodsClient;
import com.leyou.cart.pojo.Cart;
import com.leyou.common.util.JsonUtils;
import com.leyou.pojo.Sku;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class CartService {


@Autowired
private GoodsClient goodsClient;

@Autowired
private StringRedisTemplate redisTemplate;

static final String KEY_PREFIX = "ly:cart:uid:";


/**
* 保存购物车
* @param cart 购物车数据
*/
public void saveCart(Cart cart) {
String key = KEY_PREFIX + cart.getUserId();
// 获取当前用户购物车信息
BoundHashOperations<String, Object, Object> userCartData = redisTemplate.boundHashOps(key);

// 拿出cart中的关键数据
Integer num = cart.getNum();
Long skuId = cart.getSkuId();

// 判断是否存在购物车
if (userCartData.hasKey(skuId.toString())) {
// 存在该商品项,增加数量
String json = userCartData.get(cart.getSkuId()).toString();
cart = JsonUtils.parse(json, Cart.class);
cart.setNum(cart.getNum() + num);
} else {
// 不存在该商品项,新增该商品到购物车
Sku sku = this.goodsClient.querySkuById(skuId);
cart.setImage(StringUtils.isBlank(sku.getImages()) ? "" : StringUtils.split(sku.getImages(), ",")[0]);
cart.setPrice(sku.getPrice());
cart.setTitle(sku.getTitle());
cart.setOwnSpec(sku.getOwnSpec());
}

// 将购物车信息存入redis
userCartData.put(cart.getSkuId().toString(), JsonUtils.serialize(cart));
}
}

测试

前台登录过后,点击加入购物车,前往redis查询。

这里使用的是redis desktop manager,一款redis的GUI客户端。

查询购物车

页面请求

修改cart.html中的loadCarts方法。

CartController

新增方法:queryCartList

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 查询当前用户购物车
*
* @return List 购物车商品列表
*/
@GetMapping
public ResponseEntity<List<Cart>> queryCartList() {
List<Cart> carts = cartService.queryCartList();
if (carts == null || carts.isEmpty()) {
throw new LyException(LyExceptionEnum.CURRENT_USER_CART_NOT_EXIST);
}
return ResponseEntity.ok(carts);
}

CartService

新增方法:queryCartList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 查询当前用户购物车
*
* @return List 购物车商品列表
*/
public List<Cart> queryCartList() {
// 获取用户信息
UserInfo userInfo = UserInterceptor.getUserInfo();
String key = KEY_PREFIX + userInfo.getId();
// 查询该用户购物车
if (!redisTemplate.hasKey(key)) {
throw new LyException(LyExceptionEnum.CURRENT_USER_CART_NOT_EXIST);
}
BoundHashOperations<String, Object, Object> userCartData = redisTemplate.boundHashOps(key);
List<Object> values = userCartData.values();
if (CollectionUtils.isEmpty(values)) {
throw new LyException(LyExceptionEnum.CURRENT_USER_CART_NOT_EXIST);
}
// 序列化并返回
return values.stream().map(cart -> JsonUtils.parse(cart.toString(), Cart.class)).collect(Collectors.toList());
}

测试

查询购物车成功。

修改数量

页面请求

incrementdecrement中新增逻辑:

CartController

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 更新购物车中指定商品数量
*
* @param skuId 商品ID
* @param num 数量
*/
@PutMapping
public ResponseEntity<Void> updateNum(@RequestParam("skuId") Long skuId,
@RequestParam("num") Integer num) {
cartService.updateNum(skuId, num);
return ResponseEntity.ok().build();
}

CartService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 更新购物车中指定商品数量
*
* @param skuId 商品ID
* @param num 数量
*/
public void updateNum(Long skuId, Integer num) {
// 获取登录用户
UserInfo user = UserInterceptor.getUserInfo();
String key = KEY_PREFIX + user.getId();
BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(key);
// 获取购物车
String json = hashOps.get(skuId.toString()).toString();
Cart cart = JsonUtils.parse(json, Cart.class);
cart.setNum(num);
// 写入购物车
hashOps.put(skuId.toString(), JsonUtils.serialize(cart));
}

删除购物车商品

页面请求

CartController

1
2
3
4
5
6
7
8
9
10
/**
* 删除购物车中的指定商品
*
* @param skuId 商品ID
*/
@DeleteMapping("{skuId}")
public ResponseEntity<Void> deleteCart(@PathVariable("skuId") String skuId) {
cartService.deleteCart(skuId);
return ResponseEntity.ok().build();
}

CartService

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 删除购物车中的指定商品
*
* @param skuId 商品ID
*/
public void deleteCart(String skuId) {
// 获取登录用户
UserInfo user = UserInterceptor.getUserInfo();
String key = KEY_PREFIX + user.getId();
BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(key);
// 删除该hashKey
hashOps.delete(skuId);
}

登录时合并购物车

页面请求

CartController

1
2
3
4
5
6
7
8
9
10
/**
* 新增本地购物车商品到登陆用户购物车中
*
* @param carts 商品数据
*/
@PostMapping("/merge")
public ResponseEntity<Void> addCart(List<Cart> carts) {
cartService.mergeCarts(carts);
return ResponseEntity.ok().build();
}

CartService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 新增本地购物车商品到登陆用户购物车中
*
* @param carts 商品数据
*/
public void mergeCarts(List<Cart> carts) {
// 获取当前用户信息
UserInfo user = UserInterceptor.getUserInfo();
// 遍历购物车并保存
carts.forEach(cart -> {
cart.setUserId(user.getId());
saveCart(cart);
});
}

写在最后

我自己做的乐优基本上就到这里了,后面的订单支付模块我没做,因为和我之前看的品优购基本上没什么区别,只是用的Spring Boot而已,有兴趣做的,可以去看下品优购订单支付模块。

个人感觉,黑马的电商项目基本上都差不多了,只是用的技术的区别。

文章作者: imxushuai
文章链接: https://www.imxushuai.com/2002/01/01/乐优商城笔记十一:购物车/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 imxushuai
支付宝打赏
微信打赏