1. 程式人生 > >SSM商城專案(十二)

SSM商城專案(十二)

1.   學習計劃

1、購物車實現

2、未登入狀態下使用購物車

3、登入狀態下使用購物車

2.   購物車的實現

2.1. 功能分析

1、購物車是一個獨立的表現層工程。

2、新增購物車不要求登入。可以指定購買商品的數量。

3、展示購物車列表頁面

4、修改購物車商品數量

5、刪除購物車商品

2.2. 工程搭建

可以參考e3-content建立。

e3-car(聚合工程pom)

|--e3-car-interface(jar)

|--e3-car-Service(war)

e3-car-web(war)

3.   未登入狀態下使用購物車

3.1. 新增購物車

3.1.1.    功能分析

在不登陸的情況下也可以新增購物車。把購物車資訊寫入cookie。

優點:

1、不佔用服務端儲存空間

2、使用者體驗好。

3、程式碼實現簡單。

缺點:

1、cookie中儲存的容量有限。最大4k

2、把購物車資訊儲存在cookie中,更換裝置購物車資訊不能同步。

改造商品詳情頁面

請求的url:/cart/add/{itemId}

引數:

1)商品id: Long itemId
2)商品數量: int num

業務邏輯:

1、從cookie中查詢商品列表。

2、判斷商品在商品列表中是否存在。

3、如果存在,商品數量相加。

4、不存在,根據商品id查詢商品資訊。

5、把商品新增到購車列表。

6、把購車商品列表寫入cookie。

 

返回值:邏輯檢視

 

Cookie儲存購物車

1)key:TT_CART

2)Value:購物車列表轉換成json資料。需要對資料進行編碼。

3)Cookie的有效期:儲存7天。

 

商品列表:

List<TbItem>,每個商品資料使用TbItem儲存。當根據商品id查詢商品資訊後,取第一張圖片儲存到image屬性中即可。

讀寫cookie可以使用CookieUtils工具類實現。

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) {
            //物件比較的是地址,應該是值的比較
            if (tbItem.getId() == itemId.longValue()) {
                // 3、如果存在,商品數量相加。
                tbItem.setNum(tbItem.getNum() + num);
                hasItem = true;
                break;
            }
        }
        if (!hasItem) {
            // 4、不存在,根據商品id查詢商品資訊。
            TbItem tbItem = itemService.getItemById(itemId);
            //取一張圖片
            String image = tbItem.getImage();
            if (StringUtils.isNoneBlank(image)) {
                String[] images = image.split(",");
                tbItem.setImage(images[0]);
            }
            //設定購買商品數量
            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轉換成商品列表返回
            List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class);
            return list;
        }
        return new ArrayList<>();
    }
    
}

3.2. 展示購物車商品列表

請求的url:/cart/cart

引數:無

返回值:邏輯檢視

業務邏輯:

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. 修改購物車商品數量

3.3.1.    功能分析

1、在頁面中可以修改商品數量

2、重新計算小計和總計。

3、修改需要寫入cookie。

4、每次修改都需要向服務端傳送一個ajax請求,在服務端修改cookie中的商品數量。

 

請求的url:/cart/update/num/{itemId}/{num}

引數:long itemId、int num

業務邏輯:

1、接收兩個引數

2、從cookie中取商品列表

3、遍歷商品列表找到對應商品

4、更新商品數量

5、把商品列表寫入cookie。

6、響應e3Result。Json資料。

返回值:

 e3Result。Json資料

3.3.2.    Controller

/*修改購物車商品數量*/
    @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.    解決請求*.html字尾無法返回json資料的問題

在springmvc中請求*.html不可以返回json資料。

修改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.    功能分析

請求的url:/cart/delete/{itemId}

引數:商品id

返回值:展示購物車列表頁面。Url需要做redirect跳轉。

業務邏輯:

1、從url中取商品id

2、從cookie中取購物車商品列表

3、遍歷列表找到對應的商品

4、刪除商品。

5、把商品列表寫入cookie。

6、返回邏輯檢視:在邏輯檢視中做redirect跳轉。

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. 小結

使用cookie實現購物車:

優點:

1、實現簡單

2、不需要佔用服務端儲存空間。

缺點:

1、儲存容量有限

2、更換裝置購車資訊不能同步。

 

實現購車商品資料同步:

1、要求使用者登入。

2、把購物車商品列表儲存到資料庫中。推薦使用redis。

3、Key:使用者id,value:購車商品列表。推薦使用hash,hash的field:商品id,value:商品資訊。

4、在使用者未登入情況下寫cookie。當用戶登入後,訪問購物車列表時,

a)         把cookie中的資料同步到redis。

b)        把cookie中的資料刪除

c)         展示購物車列表時以redis為準。

d)        如果redis中有資料cookie中也有資料,需要做資料合併。相同商品數量相加,不同商品新增一個新商品。

5、如果使用者登入狀態,展示購物車列表以redis為準。如果未登入,以cookie為準。

4.   登入狀態下使用購物車

4.1. 功能分析

1、購物車資料儲存的位置:

未登入狀態下,把購物車資料儲存到cookie中。

登入狀態下,需要把購物車資料儲存到服務端。需要永久儲存,可以儲存到資料庫中。可以把購物車資料儲存到redis中。

2、redis使用的資料型別

  a)使用hash資料型別

  b)Hash的Key應該是使用者id。Hash中的field是商品id,value可以把商品資訊轉成json

3、新增購物車

  登入狀態下直接把商品資料儲存到redis中。

  未登入狀態儲存到cookie中。

4、如何判斷是否登入

  a)從cookie中取token

  b)取不到未登入

  c)取到token,到redis中查詢token是否過期

  d)如果過期,未登入狀態

  e)沒過期,登入狀態

4.2. 判斷使用者是否登入

4.2.1.   功能分析

應該使用攔截器實現。

1、實現一個HandlerInterceptor介面。

2、在執行handler方法之前做業務處理

3、從cookie中取token。使用CookieUtils工具類實現。

4、沒有取到token,使用者未登入,放行。

5、取到token,呼叫sso系統的服務,根據token查詢使用者資訊。

6、沒有返回使用者資訊。登入已經過期,未登入,放行。

7.返回使用者資訊。使用者是登入狀態。可以把使用者物件儲存到request中,在Controller中可以通過判斷request中是否包含使用者物件,確定是否為登入狀態。

4.2.2.   攔截器

pom.xml

        <dependency>
            <groupId>cn.e3mall</groupId>
            <artifactId>e3-sso-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

引用服務

    <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 {
        // 前處理,執行handler之前執行此方法
        //返回true,放行   false:攔截
        //從cookie中取token
        String token=CookieUtils.getCookieValue(request, "token");
        //如果沒有token,未登入,直接放行
        if(StringUtils.isBlank(token)){
            return true;
        }
        //取到token,需要呼叫sso的服務,根據token取使用者資訊
        E3Result e3Result = tokenService.getUserByToken(token);
        //沒有取到,登入過期,放行。
        if(e3Result.getStatus()!=200){
            return true;
        }
        //取到使用者資訊,登入狀態。
        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
public
class CartServiceImpl implements CartService{ @Autowired private JedisClient jedisClient; @Autowired private TbItemMapper itemMapper; @Override public E3Result addCart(long userId, long itemId, int num) { //判斷商品是否存在 Boolean hexists = jedisClient.hexists("Cart:" + userId, itemId + ""); //如果存在數量相加 if (hexists) { String json = jedisClient.hget("Cart:" + userId, itemId + ""); //把json轉換成TbItem TbItem item = JsonUtils.jsonToPojo(json, TbItem.class); item.setNum(item.getNum() + num); //寫回redis jedisClient.hset("Cart:" + userId, itemId + "", JsonUtils.objectToJson(item)); return E3Result.ok(); } //如果不存在,根據商品id取商品資訊 TbItem item = itemMapper.selectByPrimaryKey(itemId); //設定購物車資料量 item.setNum(num); //取一張圖片 String image = item.getImage(); if (StringUtils.isNotBlank(image)) { item.setImage(image.split(",")[0]); } //新增到購物車列表 jedisClient.hset("Cart:" + userId, itemId + "", JsonUtils.objectToJson(item)); return E3Result.ok(); } }

釋出服務

<dubbo:service interface="cn.e3mall.car.service.CartService" ref="cartServiceImpl" timeout="600000"/>

4.3.2.   Controller

 

引用服務 

 <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) {
        //遍歷商品列表
        //把列表新增到購物車
        //判斷購物車是否有此商品
        //如果有, 數量相加
        //如果沒有,新增新的商品
        for (TbItem tbItem : itemList) {
            addCart(userId, tbItem.getId(), tbItem.getNum());
        }
        return E3Result.ok();
    }

    //獲得購物車列表
    @Override
    public List<TbItem> getCartList(long userId) {
        //根據使用者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);
        //判斷使用者是否為登入狀態
        TbUser user = (TbUser) request.getAttribute("user");
        //如果是登入狀態
        if (user != null) {
            //從cookie中取購物車列表
            //如果不為空,把cookie中的購物車商品和服務端的購物車商品合併。
            cartService.mergeCart(user.getId(), cartList);
            //把cookie中的購物車刪除
            CookieUtils.deleteCookie(request, response, "cart");
            //從服務端取購物車列表
            cartList = cartService.getCartList(user.getId());
            
        }
        //傳遞給頁面
        model.addAttribute("cartList", cartList);
        return "cart";
    }

4.5. 更新購物車數量

4.5.1.   Service

    @Override
    public E3Result updateCart(long userId, long itemId, int num) {
        //從redis中取商品資訊
        String json = jedisClient.hget("Cart:" + userId, itemId + "");
        //更新商品數量
        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