文章目錄
- 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
建立好了,繼續老套路,把依賴都配好
pom檔案
父級pom檔案中加入jt-cart的module
yml檔案配置直接按照jt-sso的來就行,隻改端口号即可。
1.1.1 項目搭建問題分析
有時候我們建立子子產品的時候出現差錯了,就會删除重建立立,但是吧,會發現再次建立的項目的
src/main/java不是Sources類型的,以及resources也不是指向Resources。 當我們進行手動點選後,會發現啟動類無法加載,編譯錯誤。配置也沒問題,父子依賴也有。就是pom的依賴不過來。
解決方法,
當看到有那個打√的,原來被忽視了,取消√。然後應用并儲存。重新加載maven
項目依賴成功
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分析
當使用者點選購物車按鈕時,跳轉購物車展現頁面
編輯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的注解。
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>
我們看一下效果,頁面可到我的GitHub中檔一下。頁面使用的是thymeleaf模版引擎渲染的資料。可根據自己需要進行修改。
我們重新整理一下,看一下效果
1.3 購物數量修改
點選數量增減按鈕,看一下請求
看一下js源碼
編寫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購物車删除
當使用者點選删除按鈕時,應該删除購物車記錄,同時重定向到購物車清單頁面.
編輯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);
}
看一下效果:
1.5 購物車新增
回到首頁,點選商品,這裡我做了修改。需要重新檔一下index和item頁面。
(一開始我做的時候使用的jsp,故引用了僞靜态技術,是以前面做的時候沒注意連結的字尾名.html)
進到詳情頁,點選加入購物車
看一下js,這裡使用了thymeleaf的擷取後端值的格式
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.應該跳轉到使用者登入頁面.
思路:
- 攔截器實作使用者權限判斷!!!
- Shiro安全架構 資料在session中共享!!!
攔截器工作原理
說明:servlet為了基于request對象和response對象滿足使用者不同的業務需求,開發了攔截器機制.
說明:攔截器構成要素=攔截之後實作業務+path(路徑)
攔截器攔截時期
編輯攔截器類
@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
名稱:本地線程變量
特點:線程安全的
可以實作在同一個線程内資料共享!!!
看一下引用過程
我們上面在寫購物車功能時,是将使用者的id寫死了,我們現在将其改為動态擷取
修改,自行修改其他的值吧
重新開機服務,進行測試吧。
總結
本節講解了實作購物車的功能,以及用到了攔截器判斷使用者是否登陸,以及使用了ThreadLocal的方式存放使用者資訊,這也提高了安全性!!