玩轉購物車功能
一、購物車子產品
1.建立cart服務
我們需要先建立一個cart的微服務,然後添加相關的依賴,設定配置,放開注解。
<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>
然後屬性檔案中的配置
server.port=40000
spring.application.name=mall-cart
spring.cloud.nacos.discovery.server-addr=192.168.56.100:8848
spring.thymeleaf.cache=false
然後再添加配置中心的配置:bootstrap.yml檔案
spring.application.name=mall-cart
spring.cloud.nacos.config.server-addr=192.168.56.100:8848
放開注冊中心的注解
2.Nginx配置
首先在windows中的host指定對應域名
拷貝對應的靜态資源到Nginx的static/cart目錄中
然後修改Nginx的配置檔案
然後重新開機nginx服務
docker restart nginx
3.配置網關服務
Nginx接收了
cart.msb.com
這個域名的通路,那麼會把服務反向代理給網關服務,這時網關服務就需要把該請求路由到購物車服務中。我們需要修改網關服務的配置
最後調整下模闆頁面中的靜态資源的路徑就可以了
然後啟動服務通路即可
二、購物車功能
1.購物車模式處理
讨論購物車中資料的存儲方式。我們在購物車中可以有多見商品
然後對應的資料我們可以選擇存儲在Redis中,對應的資料存儲結構我們要慎重的考慮,因為有多條記錄,如果用List來存儲
[
{skuId:1,subTile:'華為',price:666}
,{skuId:1,subTile:'華為',price:666}
,{skuId:1,subTile:'華為',price:666}
]
那麼我們後面要對商品做添加删除和修改商品數量的時候就會比較麻煩,我們需要取出List中的整個資料,然後找到我們要操作的資料,然後把所有資料回寫到Redis中,這種方式顯然不可取,這時我們可以考慮hash的方式來存儲:
這樣我們就可以一條一條來處理了,相比上面會更加的靈活。然後我們在後端服務中存儲的結構為
Map<String,Map<String,CartItemVo>>
2.購物車VO
針對購物車的資訊存儲,我們建立兩個對應的VO對象。
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.認證資訊
我們需要在購物車服務中根據目前登入用的使用者資訊去Redis中查詢對應的購物車資訊。首先我們需要導入Redis的相關依賴,同時我們要借助于前面講解的SpringSession來共享認證的Session資訊。
<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>
添加屬性檔案資訊
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
添加Cookie的配置資訊
@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();
}
}
添加自定義的攔截器
/**
* 我們自定義的攔截器:幫助我們擷取目前登入的使用者資訊
* 通過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;
}
}
注冊攔截器
@Configuration
public class MyWebInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**");
}
}
然後登入後通路controller服務測試
4.頁面跳轉
從商品詳情頁面點選添加購物車完成添加購物車的邏輯。
5.添加購物車邏輯
具體完成添加購物車的邏輯,也service中我們擷取到商品的SKUId和商品數量後,我們要實作的邏輯
具體核心代碼
/**
* 把商品添加到購物車中
* @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;
}
6.購物車首頁
添加商品進入購物車後,我們可以點選結算進入購物車頁面,那麼我們需要在背景查詢出所有的目前登入使用者的購物車商品資訊,然後在頁面中展示
然後在頁面中處理資料