Shopping cart function
1. Shopping cart module
1.Create cart service
We need to first create a cart microservice, then add relevant dependencies, set configurations, and release annotations.
<dependencies>
<dependency>
<groupId>com.msb.mall</groupId>
<artifactId>mall-commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Then the configuration in the properties file
server.port=40000
spring.application.name=mall-cart
spring.cloud.nacos.discovery.server-addr=192.168.56.100:8848
spring.thymeleaf.cache=false
Then add the configuration of the configuration center: bootstrap.yml file
spring.application.name=mall-cart
spring.cloud.nacos.config.server-addr=192.168.56.100:8848
Release the annotations of the registration center
2.Nginx configuration
First, specify the corresponding domain name in host in windows
Copy the corresponding static resources to the static/cart directory of Nginx
Then modify the Nginx configuration file
Then restart the nginx service
docker restart nginx
3. Configure gateway service
When Nginx receives access to cart.msb.com
this domain name, it will reverse proxy the service to the gateway service. At this time, the gateway service needs to route the request to the shopping cart service. We need to modify the configuration of the gateway service
Finally, just adjust the path of the static resources in the template page.
Then start the service access
2. Shopping cart function
1. Shopping cart mode processing
Discuss how data in the shopping cart is stored. We can see multiple items in the shopping cart
Then we can choose to store the corresponding data in Redis. We must carefully consider the corresponding data storage structure, because there are many records. If we use List to store
[
{
skuId:1,subTile:'华为',price:666}
,{
skuId:1,subTile:'华为',price:666}
,{
skuId:1,subTile:'华为',price:666}
]
Then it will be more troublesome when we add, delete, and modify the number of products later. We need to take out the entire data in the List, then find the data we want to operate, and then write all the data back to Redis. This way Obviously not advisable, at this time we can consider hashing to store:
In this way, we can deal with it one by one, which will be more flexible than the above. Then the structure we store in the backend service is
Map<String,Map<String,CartItemVo>>
2. Shopping Cart VO
For the information storage of the shopping cart, we create two corresponding VO objects.
package com.msb.mall.vo;
import java.math.BigDecimal;
import java.util.List;
/**
* 购物车中的商品信息
*/
public class CartItem {
// 商品的编号 SkuId
private Long skuId;
// 商品的图片
private String image;
// 商品的标题
private String title;
// 是否选中
private boolean check = true;
// 商品的销售属性
private List<String> skuAttr;
// 商品的单价
private BigDecimal price;
// 购买的数量
private Integer count;
// 商品的总价
private BigDecimal totalPrice;
public Long getSkuId() {
return skuId;
}
public void setSkuId(Long skuId) {
this.skuId = skuId;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isCheck() {
return check;
}
public void setCheck(boolean check) {
this.check = check;
}
public List<String> getSkuAttr() {
return skuAttr;
}
public void setSkuAttr(List<String> skuAttr) {
this.skuAttr = skuAttr;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public BigDecimal getTotalPrice() {
// 商品的总价 price * count
return price.multiply(new BigDecimal(count));
}
}
Cart
package com.msb.mall.vo;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
/**
* 购物车
*/
public class Cart {
// 购物车中的商品种类
private Integer countType;
// 选中的商品数量
private Integer checkCountNum;
// 选中商品的总价
private BigDecimal totalAmount;
// 购物中存储的商品信息
private List<CartItem> items;
public Integer getCountType() {
return items.size();
}
public Integer getCheckCountNum() {
Integer count = 0;
for (CartItem item : items) {
if (item.isCheck()){
count += item.getCount();
}
}
return count;
}
public BigDecimal getTotalAmount() {
BigDecimal amount = new BigDecimal(0);
for (CartItem item : items) {
if (item.isCheck()){
amount = amount.add(item.getTotalPrice());
}
}
return amount;
}
public List<CartItem> getItems() {
return items;
}
public void setItems(List<CartItem> items) {
this.items = items;
}
}
3. Certification information
We need to query the corresponding shopping cart information in Redis based on the current login user information in the shopping cart service. First, we need to import the relevant dependencies of Redis, and at the same time, we need to use the SpringSession explained earlier to share the authenticated Session information.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
Add properties file information
server.port=40000
spring.application.name=mall-cart
spring.cloud.nacos.discovery.server-addr=192.168.56.100:8848
spring.thymeleaf.cache=false
spring.redis.host=192.168.56.100
spring.redis.port=6379
spring.thymeleaf.enabled=false
spring.session.store-type=redis
server.servlet.session.timeout=30m
spring.session.redis.namespace=spring:session
Add cookie configuration information
@Configuration
public class MySessionConfig {
/**
* 自定义Cookie的配置
* @return
*/
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("msb.com"); // 设置session对应的一级域名
cookieSerializer.setCookieName("msbsession");
return cookieSerializer;
}
/**
* 对存储在Redis中的数据指定序列化的方式
* @return
*/
@Bean
public RedisSerializer<Object> redisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
Add custom interceptor
/**
* 我们自定义的拦截器:帮助我们获取当前登录的用户信息
* 通过Session共享获取的
*/
public class AuthInterceptor implements HandlerInterceptor {
// 本地线程对象 Map<thread,Object>
public static ThreadLocal<MemberVO> threadLocal = new ThreadLocal();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 通过HttpSession获取当前登录的用户信息
HttpSession session = request.getSession();
Object attribute = session.getAttribute(AuthConstant.AUTH_SESSION_REDIS);
if(attribute != null){
MemberVO memberVO = (MemberVO) attribute;
threadLocal.set(memberVO);
}
return true;
}
}
register interceptor
@Configuration
public class MyWebInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**");
}
}
Then log in and access the controller service test
4. Page jump
Click Add Shopping Cart from the product details page to complete the logic of adding a shopping cart.
5. Add shopping cart logic
Specifically complete the logic of adding the shopping cart, and also the logic we want to implement after we obtain the SKUId and quantity of the product in the service.
Specific core code
/**
* 把商品添加到购物车中
* @param skuId
* @param num
* @return
*/
@Override
public CartItem addCart(Long skuId, Integer num) throws Exception {
BoundHashOperations<String, Object, Object> hashOperations = getCartKeyOperation();
// 如果Redis存储在商品的信息,那么我们只需要修改商品的数量就可以了
Object o = hashOperations.get(skuId.toString());
if(o != null){
// 说明已经存在了这个商品那么修改商品的数量即可
String json = (String) o;
CartItem item = JSON.parseObject(json, CartItem.class);
item.setCount(item.getCount()+num);
hashOperations.put(skuId.toString(),JSON.toJSONString(item));
return item;
}
CartItem item = new CartItem();
CompletableFuture future1 = CompletableFuture.runAsync(()->{
// 1.远程调用获取 商品信息
R r = productFeignService.info(skuId);
String skuInfoJSON = (String) r.get("skuInfoJSON");
SkuInfoVo vo = JSON.parseObject(skuInfoJSON,SkuInfoVo.class);
item.setCheck(true);
item.setCount(num);
item.setPrice(vo.getPrice());
item.setImage(vo.getSkuDefaultImg());
item.setSkuId(skuId);
item.setTitle(vo.getSkuTitle());
},executor);
CompletableFuture future2 = CompletableFuture.runAsync(()->{
// 2.获取商品的销售属性
List<String> skuSaleAttrs = productFeignService.getSkuSaleAttrs(skuId);
item.setSkuAttr(skuSaleAttrs);
},executor);
CompletableFuture.allOf(future1,future2).get();
// 3.把数据存储在Redis中
String json = JSON.toJSONString(item);
hashOperations.put(skuId.toString(),json);
return item;
}
private BoundHashOperations<String, Object, Object> getCartKeyOperation() {
// hash key: cart:1 skuId:cartItem
MemberVO memberVO = AuthInterceptor.threadLocal.get();
String cartKey = CartConstant.CART_PERFIX + memberVO.getId();
BoundHashOperations<String, Object, Object> hashOperations = redisTemplate.boundHashOps(cartKey);
return hashOperations;
}
6. Shopping cart home page
After adding products to the shopping cart, we can click checkout to enter the shopping cart page. Then we need to query the shopping cart product information of all currently logged in users in the background, and then display it on the page.
Then process the data in the page