天天看點

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

文章目錄

  • 1.購物車實作
    • 1.1 項目搭建
      • 1.1.1 項目搭建問題分析
    • 1.2 購物車展現
    • 1.3 購物數量修改
    • 1.4購物車删除
    • 1.5 購物車新增
    • 1.6 京淘項目實作權限控制
    • 1.7 ThreadLocal
  • 總結

上一節我們使用dubbo實作了單點登入,以及服務之間的調用,這一節我們繼續完善項目中的購物車項目

1.購物車實作

1.1 項目搭建

建立購物車項目jt-cart

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

建立好了,繼續老套路,把依賴都配好

pom檔案

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

父級pom檔案中加入jt-cart的module

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

yml檔案配置直接按照jt-sso的來就行,隻改端口号即可。

1.1.1 項目搭建問題分析

有時候我們建立子子產品的時候出現差錯了,就會删除重建立立,但是吧,會發現再次建立的項目的

src/main/java不是Sources類型的,以及resources也不是指向Resources。 當我們進行手動點選後,會發現啟動類無法加載,編譯錯誤。配置也沒問題,父子依賴也有。就是pom的依賴不過來。

解決方法,

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

當看到有那個打√的,原來被忽視了,取消√。然後應用并儲存。重新加載maven

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

項目依賴成功

1.2 購物車展現

在jt-common中添加POJO對象

@TableName("tb_cart")
@Data
@Accessors(chain = true)
public class Cart extends BasePojo{
	private Long id;		//購物車ID
	private Long userId;	//使用者Id
	private Long itemId;	//商品Id
	private String itemTitle;	//商品标題
	private String itemImage;	//商品圖檔
	private Long itemPrice;		//商品價格
	private Integer num;		//商品數量
}
           

頁面url分析

當使用者點選購物車按鈕時,跳轉購物車展現頁面

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

編輯Controller(jt-web)頁面的實作都是在jt-web中處理的,具體的業務是在jt-cart中實作的

建立CartContrller ,屬于服務消費者

@Controller
@RequestMapping("/cart")
public class CartController {
	@Reference(timeout=3000)
	private DubboCartService cartService;
	/**
	 * 1.根據使用者資訊擷取購物車清單資料
	 */
	@RequestMapping("/show")
	public String show(Model model) {
		Long userId = 7L; //暫時寫死
		List<Cart> cartList = cartService.findCartListByUserId(userId);
		Double totalPrice=0.0;
		if(CollectionUtils.isNotEmpty(cartList)){
			totalPrice= cartList.stream().collect(Collectors.summingDouble(Cart::getItemPrice));
		}
		model.addAttribute("totalPrice",totalPrice);
		model.addAttribute("cartList", cartList);
		return "/cart";
	}
}
           

在jt-common的service中建立DubboCartService ,也可稱之為服務注冊中心

public interface DubboCartService {
	List<Cart> findCartListByUserId(Long userId);
}
           

在jt-cart中建立實作類,也稱之為服務提供者

@Service(timeout = 3000)
public class DubboCartServiceImpl implements DubboCartService {
	@Autowired
	private CartMapper cartMapper;
	@Override
	public List<Cart> findCartListByUserId(Long userId) {
		QueryWrapper<Cart> queryWrapper = new QueryWrapper<Cart>();
		queryWrapper.eq("user_id", userId);
		return cartMapper.selectList(queryWrapper);
	}
}
           

注意這裡的@service注解是dubbo的注解。

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

CartMapper:

@Mapper
public interface CartMapper extends BaseMapper<Cart>{
}
           

大家不可誤解這隻是單純的接口和實作。那是因為注解幫你很多事

我們看一下服務消費者的注解,@Reference這也是dubbo的,是用于發現服務用的

@Reference(timeout=3000)
	private DubboCartService cartService;
           

CartMapper.xml,注意路徑

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ly.mapper.CartMapper">
</mapper>
           
springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

我們看一下效果,頁面可到我的GitHub中檔一下。頁面使用的是thymeleaf模版引擎渲染的資料。可根據自己需要進行修改。

我們重新整理一下,看一下效果

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

1.3 購物數量修改

點選數量增減按鈕,看一下請求

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

看一下js源碼

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

編寫Controller

@PostMapping("/update/num/{itemId}/{num}")
	@ResponseBody
	public SysResult updateNum(@PathVariable Long itemId,@PathVariable Integer num) {
		Long userId = 7L;
		Cart cart = new Cart();
		cart.setUserId(userId).setItemId(itemId).setNum(num);
		cartService.updateNum(cart);
		return SysResult.success();
	}
           

編輯Service

@Override
	public void updateNum(Cart cart) {
		Cart cartTemp = new Cart();
		cartTemp.setNum(cart.getNum())
				.setUpdated(new Date());
		UpdateWrapper<Cart> updateWrapper = new UpdateWrapper<Cart>();
		updateWrapper.eq("user_id", cart.getUserId())
					 .eq("item_id", cart.getItemId());
		cartMapper.update(cartTemp, updateWrapper);
	}
           

1.4購物車删除

當使用者點選删除按鈕時,應該删除購物車記錄,同時重定向到購物車清單頁面.

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

編輯CartController:

@RequestMapping("/delete/{itemId}")
	public String deleteCart(Cart cart) {
		Long userId = 7L;
		cart.setUserId(userId);
		cartService.deleteCart(cart);
		return "redirect:/cart/show";
	}
           

編輯DubboCartServiceImpl:

@Override
 @Transactional
    public void deleteCart(Cart cart) {
        QueryWrapper<Cart> queryWrapper = new QueryWrapper<Cart>(cart);
        //根據對象中不為null的屬性,充當where條件
        cartMapper.delete(queryWrapper);
    }
           

看一下效果:

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

1.5 購物車新增

回到首頁,點選商品,這裡我做了修改。需要重新檔一下index和item頁面。

(一開始我做的時候使用的jsp,故引用了僞靜态技術,是以前面做的時候沒注意連結的字尾名.html)

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

進到詳情頁,點選加入購物車

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結
springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

看一下js,這裡使用了thymeleaf的擷取後端值的格式

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

Controller

/**
	 * 完成購物車新增
	 */
	@RequestMapping("/add/{itemId}")
	public String saveCart(Cart cart) {
		Long userId = 7L;
		cart.setUserId(userId);
		cartService.insertCart(cart);
		return "redirect:/cart/show";
	}
           

編輯service:

@Override
    @Transactional
    public void insertCart(Cart cart) {
        QueryWrapper<Cart> queryWrapper = new QueryWrapper<Cart>();
        queryWrapper.eq("user_id", cart.getUserId())
                .eq("item_id", cart.getItemId());
        Cart cartDB = cartMapper.selectOne(queryWrapper);
        if (cartDB == null) {
            cart.setCreated(new Date())
                    .setUpdated(cart.getCreated());
            cartMapper.insert(cart);
        } else {
            int num = cart.getNum() + cartDB.getNum();
            Cart cartTemp = new Cart();
            cartTemp.setNum(num)
                    .setUpdated(new Date());
            UpdateWrapper<Cart> upWrapper = new UpdateWrapper<>();
            upWrapper.eq("id", cartDB.getId());
            cartMapper.update(cartTemp, upWrapper);
        }
    }
           

1.6 京淘項目實作權限控制

業務說明

當使用者在沒有登入的條件下,不允許通路特殊的子產品例如jt-cart/jt-order.應該跳轉到使用者登入頁面.

思路:

  1. 攔截器實作使用者權限判斷!!!
  2. Shiro安全架構 資料在session中共享!!!

攔截器工作原理

說明:servlet為了基于request對象和response對象滿足使用者不同的業務需求,開發了攔截器機制.

說明:攔截器構成要素=攔截之後實作業務+path(路徑)

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

攔截器攔截時期

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

編輯攔截器類

@Component
public class UserInterceptor implements HandlerInterceptor{
	@Autowired(required = false)
	private JedisCluster jedisCluster;
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//1.擷取使用者cookie資訊
		Cookie[] cookies = request.getCookies();
		String ticket = null;
		if(cookies.length > 0) {
			for (Cookie cookie : cookies) {
				if("JT_TICKET".equals(cookie.getName())) {
					ticket = cookie.getValue();
					break;
				}
			}
		}
		//判斷取值是否有效 不為null時校驗資訊
		if(!StringUtils.isEmpty(ticket)) {
			String userJSON = jedisCluster.get(ticket);
			if(!StringUtils.isEmpty(userJSON)) {
				//方式1:該方式公司中初級程式員必會 request
				//request.setAttribute("JT_USER", user);
				
				//方式2:使用ThreadLocal實作
				User user = JsonUtil.toObject(userJSON, User.class);
				UserThreadLocal.set(user);
				return true;	
			}
		}
		//重定向到使用者登入頁面
		response.sendRedirect("/user/login");
		return false; //表示請求攔截 
	}
}
           

配置攔截器:

@Configuration
public class MvcConfigurer implements WebMvcConfigurer{
	
	@Autowired
	private UserInterceptor userInterceptor;
	
	//開啟比對字尾型配置
	@Override
	public void configurePathMatch(PathMatchConfigurer configurer) {
		
		configurer.setUseSuffixPatternMatch(true);
	}
	
	//添加攔截器配制
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		
		registry.addInterceptor(userInterceptor)
		.addPathPatterns("/cart/**","/order/**");
		//如果有多個攔截器,可以addInterceptor多次
	}
	
}
           

建立UserThreadLocal :

public class UserThreadLocal {
	/**
	 * 1.如果需要存儲單個對象 寫對象類型
	 * 2.如果需要儲存多個資料 使用Map集合
	 */
	private static ThreadLocal<User> thread = new ThreadLocal<>();
	public static void set(User user) {
		thread.set(user);
	}
	public static User get() {
		return thread.get();
	}
	public static void remove() {
		thread.remove();
	}
}
           

在上面中可以看到有倆種方式存儲使用者登入資訊,一般情況下,普遍将使用者登入資訊放在session中,但我們這次不放在session中。放線上程裡

1.7 ThreadLocal

名稱:本地線程變量

特點:線程安全的

可以實作在同一個線程内資料共享!!!

看一下引用過程

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

我們上面在寫購物車功能時,是将使用者的id寫死了,我們現在将其改為動态擷取

springboot-分布式執行個體開發(十 二)-購物車1.購物車實作總結

修改,自行修改其他的值吧

重新開機服務,進行測試吧。

總結

本節講解了實作購物車的功能,以及用到了攔截器判斷使用者是否登陸,以及使用了ThreadLocal的方式存放使用者資訊,這也提高了安全性!!

繼續閱讀