| 1.? ?學(xué)習(xí)計(jì)劃1、購物車實(shí)現(xiàn) 2、未登錄狀態(tài)下使用購物車 3、登錄狀態(tài)下使用購物車 2.?? 購物車的實(shí)現(xiàn)2.1. 功能分析1、購物車是一個(gè)獨(dú)立的表現(xiàn)層工程。 2、添加購物車不要求登錄??梢灾付ㄙ徺I商品的數(shù)量。 3、展示購物車列表頁面 4、修改購物車商品數(shù)量 5、刪除購物車商品 2.2. 工程搭建可以參考e3-content創(chuàng)建。 e3-car(聚合工程pom) |--e3-car-interface(jar) |--e3-car-Service(war) e3-car-web(war) 3.? ?未登錄狀態(tài)下使用購物車3.1. 添加購物車3.1.1.??? 功能分析在不登陸的情況下也可以添加購物車。把購物車信息寫入cookie。 優(yōu)點(diǎn): 1、不占用服務(wù)端存儲(chǔ)空間 2、用戶體驗(yàn)好。 3、代碼實(shí)現(xiàn)簡單。 缺點(diǎn): 1、cookie中保存的容量有限。最大4k 2、把購物車信息保存在cookie中,更換設(shè)備購物車信息不能同步。 改造商品詳情頁面 
 
 請(qǐng)求的url:/cart/add/{itemId} 參數(shù): 1)商品id: Long itemId 業(yè)務(wù)邏輯: 1、從cookie中查詢商品列表。 2、判斷商品在商品列表中是否存在。 3、如果存在,商品數(shù)量相加。 4、不存在,根據(jù)商品id查詢商品信息。 5、把商品添加到購車列表。 6、把購車商品列表寫入cookie。 ? 返回值:邏輯視圖 ? Cookie保存購物車 1)key:TT_CART 2)Value:購物車列表轉(zhuǎn)換成json數(shù)據(jù)。需要對(duì)數(shù)據(jù)進(jìn)行編碼。 3)Cookie的有效期:保存7天。 ? 商品列表: List<TbItem>,每個(gè)商品數(shù)據(jù)使用TbItem保存。當(dāng)根據(jù)商品id查詢商品信息后,取第一張圖片保存到image屬性中即可。 讀寫cookie可以使用CookieUtils工具類實(shí)現(xiàn)。 3.1.2.??? Controller@Controller
public class CarController {
    
    @Value("${CART_EXPIRE}")
    private Integer CART_EXPIRE;
    @Autowired
    private ItemService itemService;
    
    @RequestMapping("/cart/add/{itemId}")
    public String addCartItem(@PathVariable Long itemId, Integer num,
            HttpServletRequest request, HttpServletResponse response) {
        // 1、從cookie中查詢商品列表。
        List<TbItem> cartList = getCartList(request);
        // 2、判斷商品在商品列表中是否存在。
        boolean hasItem = false;
        for (TbItem tbItem : cartList) {
            //對(duì)象比較的是地址,應(yīng)該是值的比較
            if (tbItem.getId() == itemId.longValue()) {
                // 3、如果存在,商品數(shù)量相加。
                tbItem.setNum(tbItem.getNum()   num);
                hasItem = true;
                break;
            }
        }
        if (!hasItem) {
            // 4、不存在,根據(jù)商品id查詢商品信息。
            TbItem tbItem = itemService.getItemById(itemId);
            //取一張圖片
            String image = tbItem.getImage();
            if (StringUtils.isNoneBlank(image)) {
                String[] images = image.split(",");
                tbItem.setImage(images[0]);
            }
            //設(shè)置購買商品數(shù)量
            tbItem.setNum(num);
            // 5、把商品添加到購車列表。
            cartList.add(tbItem);
        }
        // 6、把購車商品列表寫入cookie。
        CookieUtils.setCookie(request, response, "car", JsonUtils.objectToJson(cartList), CART_EXPIRE, true);
        return "cartSuccess";
    }
    
    /**
     * 從cookie中取購物車列表
     * <p>Title: getCartList</p>
     * <p>Description: </p>
     * @param request
     * @return
     */
    private List<TbItem> getCartList(HttpServletRequest request) {
        //取購物車列表
        String json = CookieUtils.getCookieValue(request, "car", true);
        //判斷json是否為null
        if (StringUtils.isNotBlank(json)) {
            //把json轉(zhuǎn)換成商品列表返回
            List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class);
            return list;
        }
        return new ArrayList<>();
    }
    
}3.2. 展示購物車商品列表請(qǐng)求的url:/cart/cart 參數(shù):無 返回值:邏輯視圖 業(yè)務(wù)邏輯: 1、從cookie中取商品列表。 2、把商品列表傳遞給頁面。 ?Controller    @RequestMapping("/cart/cart")
    public String showCartList(HttpServletRequest request, Model model) {
        //取購物車商品列表
        List<TbItem> cartList = getCartList(request);
        //傳遞給頁面
        model.addAttribute("cartList", cartList);
        return "cart";
    }3.3. 修改購物車商品數(shù)量3.3.1.??? 功能分析1、在頁面中可以修改商品數(shù)量 2、重新計(jì)算小計(jì)和總計(jì)。 3、修改需要寫入cookie。 4、每次修改都需要向服務(wù)端發(fā)送一個(gè)ajax請(qǐng)求,在服務(wù)端修改cookie中的商品數(shù)量。 ? 請(qǐng)求的url:/cart/update/num/{itemId}/{num} 參數(shù):long itemId、int num 業(yè)務(wù)邏輯: 1、接收兩個(gè)參數(shù) 2、從cookie中取商品列表 3、遍歷商品列表找到對(duì)應(yīng)商品 4、更新商品數(shù)量 5、把商品列表寫入cookie。 6、響應(yīng)e3Result。Json數(shù)據(jù)。 返回值: ?e3Result。Json數(shù)據(jù) 3.3.2.??? Controller/*修改購物車商品數(shù)量*/
    @RequestMapping("/cart/update/num/{itemId}/{num}")
    public E3Result updateNum(@PathVariable Long itemId,@PathVariable Integer num,HttpServletRequest request,HttpServletResponse response){
        //從cookie中查詢商品列表。
        List<TbItem> cartList = getCartList(request);
        for (TbItem tbItem : cartList) {
            if(tbItem.getId()==itemId.longValue()){
                tbItem.setNum(num);
            }
        }
        CookieUtils.setCookie(request, response, "car", JsonUtils.objectToJson(cartList), CART_EXPIRE, true);
        return E3Result.ok();
    }3.3.3.??? 解決請(qǐng)求*.html后綴無法返回json數(shù)據(jù)的問題在springmvc中請(qǐng)求*.html不可以返回json數(shù)據(jù)。 修改web.xml,添加url攔截格式。 </servlet-mapping>
        <servlet-mapping>
        <servlet-name>e3-car-web</servlet-name>
        <url-pattern>*.action</url-pattern>
</servlet-mapping>3.4. 刪除購物車商品3.4.1.??? 功能分析請(qǐng)求的url:/cart/delete/{itemId} 參數(shù):商品id 返回值:展示購物車列表頁面。Url需要做redirect跳轉(zhuǎn)。 業(yè)務(wù)邏輯: 1、從url中取商品id 2、從cookie中取購物車商品列表 3、遍歷列表找到對(duì)應(yīng)的商品 4、刪除商品。 5、把商品列表寫入cookie。 6、返回邏輯視圖:在邏輯視圖中做redirect跳轉(zhuǎn)。 3.4.2.??? Controller    @RequestMapping("/cart/delete/{itemId}")
    public String deleteCarItem(@PathVariable Long itemId,HttpServletRequest request,HttpServletResponse response){
        //從cookie中查詢商品列表。
                List<TbItem> cartList = getCartList(request);
                for (TbItem tbItem : cartList) {
                    if(tbItem.getId()==itemId.longValue()){
                        cartList.remove(tbItem);
                        break;
                    }
                }
        CookieUtils.setCookie(request, response, "car", JsonUtils.objectToJson(cartList), CART_EXPIRE, true);
        return "redirect:/cart/cart.html";
    }3.5. 小結(jié)使用cookie實(shí)現(xiàn)購物車: 優(yōu)點(diǎn): 1、實(shí)現(xiàn)簡單 2、不需要占用服務(wù)端存儲(chǔ)空間。 缺點(diǎn): 1、存儲(chǔ)容量有限 2、更換設(shè)備購車信息不能同步。 ? 實(shí)現(xiàn)購車商品數(shù)據(jù)同步: 1、要求用戶登錄。 2、把購物車商品列表保存到數(shù)據(jù)庫中。推薦使用redis。 3、Key:用戶id,value:購車商品列表。推薦使用hash,hash的field:商品id,value:商品信息。 4、在用戶未登錄情況下寫cookie。當(dāng)用戶登錄后,訪問購物車列表時(shí), a)???????? 把cookie中的數(shù)據(jù)同步到redis。 b)??????? 把cookie中的數(shù)據(jù)刪除 c)???????? 展示購物車列表時(shí)以redis為準(zhǔn)。 d)??????? 如果redis中有數(shù)據(jù)cookie中也有數(shù)據(jù),需要做數(shù)據(jù)合并。相同商品數(shù)量相加,不同商品添加一個(gè)新商品。 5、如果用戶登錄狀態(tài),展示購物車列表以redis為準(zhǔn)。如果未登錄,以cookie為準(zhǔn)。 4.? ?登錄狀態(tài)下使用購物車4.1. 功能分析1、購物車數(shù)據(jù)保存的位置: 未登錄狀態(tài)下,把購物車數(shù)據(jù)保存到cookie中。 登錄狀態(tài)下,需要把購物車數(shù)據(jù)保存到服務(wù)端。需要永久保存,可以保存到數(shù)據(jù)庫中。可以把購物車數(shù)據(jù)保存到redis中。 2、redis使用的數(shù)據(jù)類型 a)使用hash數(shù)據(jù)類型 b)Hash的Key應(yīng)該是用戶id。Hash中的field是商品id,value可以把商品信息轉(zhuǎn)成json 3、添加購物車 登錄狀態(tài)下直接把商品數(shù)據(jù)保存到redis中。 未登錄狀態(tài)保存到cookie中。 4、如何判斷是否登錄 a)從cookie中取token b)取不到未登錄 c)取到token,到redis中查詢token是否過期 d)如果過期,未登錄狀態(tài) e)沒過期,登錄狀態(tài) 4.2. 判斷用戶是否登錄4.2.1.? ?功能分析應(yīng)該使用攔截器實(shí)現(xiàn)。 1、實(shí)現(xiàn)一個(gè)HandlerInterceptor接口。 2、在執(zhí)行handler方法之前做業(yè)務(wù)處理 3、從cookie中取token。使用CookieUtils工具類實(shí)現(xiàn)。 4、沒有取到token,用戶未登錄,放行。 5、取到token,調(diào)用sso系統(tǒng)的服務(wù),根據(jù)token查詢用戶信息。 6、沒有返回用戶信息。登錄已經(jīng)過期,未登錄,放行。 7.返回用戶信息。用戶是登錄狀態(tài)??梢园延脩魧?duì)象保存到request中,在Controller中可以通過判斷request中是否包含用戶對(duì)象,確定是否為登錄狀態(tài)。 4.2.2.? ?攔截器pom.xml         <dependency>
            <groupId>cn.e3mall</groupId>
            <artifactId>e3-sso-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>引用服務(wù) <dubbo:reference interface="cn.e3mall.sso.service.TokenService" id="tokenService" /> ? public class LoginInterceptor implements HandlerInterceptor{
    @Autowired
    private TokenService tokenService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 前處理,執(zhí)行handler之前執(zhí)行此方法
        //返回true,放行   false:攔截
        //從cookie中取token
        String token=CookieUtils.getCookieValue(request, "token");
        //如果沒有token,未登錄,直接放行
        if(StringUtils.isBlank(token)){
            return true;
        }
        //取到token,需要調(diào)用sso的服務(wù),根據(jù)token取用戶信息
        E3Result e3Result = tokenService.getUserByToken(token);
        //沒有取到,登錄過期,放行。
        if(e3Result.getStatus()!=200){
            return true;
        }
        //取到用戶信息,登錄狀態(tài)。
        TbUser user = (TbUser) e3Result.getData();
        //把用戶信息放到request中。只需要在Controller中判斷request中是否包含user信息,放行
        request.setAttribute("user", user);
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
        
    }
}4.2.3.? ?配置攔截器    <!-- 攔截器配置 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="cn.e3mall.car.interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>4.3. 添加購物車4.3.1.? ?Service@Service 發(fā)布服務(wù) <dubbo:service interface="cn.e3mall.car.service.CartService" ref="cartServiceImpl" timeout="600000"/> 4.3.2.? ?Controller? 引用服務(wù)? <dubbo:reference interface="cn.e3mall.car.service.CartService" id="cartService" /> ? 4.4. 展示購物車4.4.1.? ?Service    @Override
    public E3Result mergeCart(long userId, List<TbItem> itemList) {
        //遍歷商品列表
        //把列表添加到購物車
        //判斷購物車是否有此商品
        //如果有, 數(shù)量相加
        //如果沒有,添加新的商品
        for (TbItem tbItem : itemList) {
            addCart(userId, tbItem.getId(), tbItem.getNum());
        }
        return E3Result.ok();
    }
    //獲得購物車列表
    @Override
    public List<TbItem> getCartList(long userId) {
        //根據(jù)用戶id查詢購物車列表
        List<String> jsonList = jedisClient.hvals("Cart:"   userId);
        ArrayList<TbItem> itemList = new ArrayList<>();
        for (String string : jsonList) {
            TbItem item = JsonUtils.jsonToPojo(string, TbItem.class);
            itemList.add(item);
        }
        return itemList;
    }4.4.2.? ?Controller@RequestMapping("/cart/cart")
    public String showCartList(HttpServletRequest request, HttpServletResponse response,Model model) {
        //從cookie中取購物車列表
        List<TbItem> cartList = getCartList(request);
        //判斷用戶是否為登錄狀態(tài)
        TbUser user = (TbUser) request.getAttribute("user");
        //如果是登錄狀態(tài)
        if (user != null) {
            //從cookie中取購物車列表
            //如果不為空,把cookie中的購物車商品和服務(wù)端的購物車商品合并。
            cartService.mergeCart(user.getId(), cartList);
            //把cookie中的購物車刪除
            CookieUtils.deleteCookie(request, response, "cart");
            //從服務(wù)端取購物車列表
            cartList = cartService.getCartList(user.getId());
            
        }
        //傳遞給頁面
        model.addAttribute("cartList", cartList);
        return "cart";
    }4.5. 更新購物車數(shù)量4.5.1.? ?Service    @Override
    public E3Result updateCart(long userId, long itemId, int num) {
        //從redis中取商品信息
        String json = jedisClient.hget("Cart:"   userId, itemId   "");
        //更新商品數(shù)量
        TbItem tbItem = JsonUtils.jsonToPojo(json, TbItem.class);
        tbItem.setNum(num);
        //寫入redis
        jedisClient.hset("Cart:"   userId, itemId   "",JsonUtils.objectToJson(tbItem));
        return E3Result.ok();
    }4.5.2.? ?Controller
 4.6. 刪除購物車4.6.1.? ?Service    @Override
    public E3Result deleteCartItem(long userId, long itemId) {
        //刪除購物車商品
        jedisClient.hdel("Cart:"   userId, itemId   "");
        return E3Result.ok();
    }4.6.2.? ?Controller
 | 
|  |