購物車的原理以及實現


  今天模擬京東的購物車實現原理完成了購物車模塊的開發, 給大家分享下。

京東的購物車實現原理:在用戶登錄和不登錄的狀態下對購物車存入cookie還是持久化到redis中的實現。下面就來具體說次購物車的實現過程

 

兩種情況:

用戶登錄,購物車存入redis中

用戶未登錄,購物車存入cookie中

比較兩種方式的優缺點:

 cookie:優點:數據保存在用戶瀏覽器中,不占用服務端內存;用戶體檢效果好;代碼實現簡單

     缺點:cookie的存儲空間只有4k;更換設備時,購物車信息不能同步;cookie禁用,不提供保存

 redis:優點:數據能夠持久化;實現了購物車同步

     缺點:增加了數據庫的壓力,速度慢

 

先介紹使用cookie存儲購物車的實現思路

1、用戶未登錄狀態下,用戶添加購物車,首先從cookie中查詢購物車中的商品列表

2、 判斷cookie的商品列表中是否有要添加的商品信息

3、如果cookie中有該商品信息,將商品的數量相加

4、如果沒有,根據商品的id值查詢商品信息

5、將商品添加到購物車列表中

6、將購物車列表寫入cookie中,設置cookie的過期時間

7、將cookie返回給客戶端。

 購物車的實現:

這里直接使用商品作為購物項對象,在頁面中計算購物項的小計和購物車的總金額

package nyist.e3.pojo;

import java.io.Serializable;
import java.util.Date;

public class TbItem implements Serializable{
    private Long id;

    private String title;

    private String sellPoint;

    private Long price;

    private Integer num;//作為購物項購買的商品數量

    private String barcode;

    private String image;//展示購物項中的圖片

    private Long cid;

    private Byte status;

    private Date created;

    private Date updated;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title == null ? null : title.trim();
    }

    public String getSellPoint() {
        return sellPoint;
    }

    public void setSellPoint(String sellPoint) {
        this.sellPoint = sellPoint == null ? null : sellPoint.trim();
    }

    public Long getPrice() {
        return price;
    }

    public void setPrice(Long price) {
        this.price = price;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public String getBarcode() {
        return barcode;
    }

    public void setBarcode(String barcode) {
        this.barcode = barcode == null ? null : barcode.trim();
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image == null ? null : image.trim();
    }

    public Long getCid() {
        return cid;
    }

    public void setCid(Long cid) {
        this.cid = cid;
    }

    public Byte getStatus() {
        return status;
    }

    public void setStatus(Byte status) {
        this.status = status;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }
}

cookie中實現添加購物車的代碼:

@Controller
public class ShopCartController {

    @Autowired
    private TbItemService tbItemService;

    @Autowired
    private ShopCartService shopCartService;
    // 獲取過期時間
    @Value("${EXPIRE_KEY}")
    private Integer EXPIRE_KEY;

    @Value("${CART_COOKIE}")
    private String CART_COOKIE;

    /**
     * 需求:將商品加入購物車中未登錄狀態下,將購物超過添加到cookie中
     * 
     * 分析:1、從cookie中獲取購物車信息
     * 2、判斷購物車中的商品,如果添加的商品存在,數量相加,不存在,根據商品id查詢商品信息,添加到cookie中
     * 3、將購物車列表信息寫入cookie中
     * 
     * 
     * @param itemId
     * @param num
     * @return
     */
    @RequestMapping("/cart/add/{itemId}")
    public String addCart(@PathVariable Long itemId, @RequestParam(defaultValue = "1") Integer num,
            HttpServletRequest request, HttpServletResponse response) {
     // 1.獲得購物車列表
        List<TbItem> itemList = getCartItemList(request);
        // 用來判斷商品是否存在的標志
        boolean flag = false;
        // 2、循環遍列表中的商品信息
        for (TbItem tbItem : itemList) {
            // 3、判斷添加的商品是否存在
            if (tbItem.getId() == itemId.longValue()) {
                // 4、添加的商品在cookie中存在,將數量相加
                tbItem.setNum(tbItem.getNum() + num);
                // 重置標簽
                flag = true;
                // 跳出循環
                break;
            }
        }
        if (!flag) {
            // cookie中沒有添加的商品信息
            // 通過商品id查詢商品信息
            TbItem item = tbItemService.getItemById(itemId);
            item.setNum(num);
            if (StringUtils.isNotBlank(item.getImage())) {
                // 取一張圖片用於展示使用
                item.setImage(item.getImage().split(",")[0]);
            }
            // 將商品添加購物車
            itemList.add(item);
        }
        //將購物車寫入cookie中
        
        CookieUtils.setCookie(request, response, CART_COOKIE, JsonUtils.objectToJson(itemList),EXPIRE_KEY,true);
        
        return "cartSuccess";

    }


}

cookie中查詢購物車列表:

思路:

1、根據cookie的name值直接取出商品列表信息

2、將購物車列表添加到model中,返回邏輯視圖

private List<TbItem> getCartItemList(HttpServletRequest request) {
        // 使用utf-8,需要設置第三個參數為true
        String json = CookieUtils.getCookieValue(request, CART_COOKIE, true);
        if (StringUtils.isNotBlank(json)) {
            // 返回cookie中取出的數據集合
            return JsonUtils.jsonToList(json, TbItem.class);
        }
        // 返回空集合對象
        return new ArrayList<>();
    }



@RequestMapping("/cart/cart")
    public String getCartList(HttpServletRequest request, HttpServletResponse response, Model model) {
        // 從cookie中取出商品信息,
        List<TbItem> itemList = getCartItemList(request);
        // 將購物車信息返回給頁面中
        model.addAttribute("cartList", itemList);
        // 跳轉邏輯視圖
        return "cart";
    }

cookie中實現刪除購物車中商品的功能:

1、接收頁面傳遞的善品id值

2、從cookie中取出購物車列表,進行循環遍歷,然后遍歷的每一個商品信息和要刪除的商品進行對比

3、如果存在就從購物車列表中將該商品移除

4、重新將購物車列表寫入cookie中

5、將cookie信息響應給客戶端

@RequestMapping("/cart/delete/{itemId}")
    public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request, HttpServletResponse response) {
        List<TbItem> list = getCartItemList(request);
        for (TbItem tbItem : list) {
            if (tbItem.getId() == itemId.longValue()) {
                list.remove(tbItem);
                break;
            }
        }
        // 刪除成功后,將購物車列表寫入cookie中
        CookieUtils.setCookie(request, response, CART_COOKIE, JsonUtils.objectToJson(list), EXPIRE_KEY, true);

        // 刪除成功后,重定向到購物車列表頁面
        return "redirect:/cart/cart.html";

    }

cookie購物車的添加,查詢,刪除已經實現實現,更改方法和刪除方法實現過程基本一樣

 

登錄狀態下redis購物車的實現

實現redis購物車添加功能

思路:

1、從request域中取出登錄用戶的信息

2、使用redis存儲購物車列表 使用redis中的hash數據類型  hash的key 使用登錄用戶id值,field的key使用商品的id值,將商品的信息作為field的value值

3、完成cookie存儲購物車列表的功能

實現的代碼:

@Override
    public E3Result addCart(Long userId, Long itemId, Integer num) {
        try {
            // 從redis中取出購物車,判斷是否已經有購物項
            Boolean hexists = jedisClient.hexists(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "");
            if (hexists) {
                // 表示購物車中已經有該商品,只需要將該商品的數量相加即可
                String hget = jedisClient.hget(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "");
                // 將數量相加
                TbItem item = JsonUtils.jsonToPojo(hget, TbItem.class);
                item.setNum(item.getNum() + num);
                // 將商品重新放入購物車中
                jedisClient.hset(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "", JsonUtils.objectToJson(item));
                return E3Result.ok();
            }

            // 表示購物車中沒有要添加的商品信息
            // 根據商品的id查詢商品的信息
            TbItem item = itemMapper.selectByPrimaryKey(itemId);
            item.setNum(num);
            if (StringUtils.isNotBlank(item.getImage())) {
                item.setImage(item.getImage().split(",")[0]);
            }
            // 將商品信息存入購物車中
            jedisClient.hset(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "", JsonUtils.objectToJson(item));
            return E3Result.ok();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return E3Result.build(400, "商品添加購物車失敗");

    }

展示登錄狀態下的購物車列表:需要將cookie中的購物車和redis中的購物車整合

1、從cookie中取出購物車列表對象

2、從redis中取出購物車對象

3、將cookie中的購物車列表和redis中的購物車列表整合(取出cookie中的購物車列表,然后添加到redis購物車中即可)

5、最終展示的結果以redis中的購物車為主

/**
     * cookie中的購物車和redis中的購物車進行整合
     */
    @Override
    public E3Result mergeCart(Long userId, List<TbItem> itemList) {
        for (TbItem tbItem : itemList) {
            // 只需要調用登錄狀態下添加購物車業務處理邏輯即可
            addCart(userId, tbItem.getId(), tbItem.getNum());
        }
        return E3Result.ok();
    }

redis購物車中刪除購物項

將用戶的id值和商品的id值分別作為hahs的key和field的key,調用redis中的hdel(String key,String...field)即可完成刪除功能

/**
     * 刪除購物車
     * 
     * @return
     * 
     */
    @Override
    public E3Result deleteCartItem(Long id, Long itemId) {
        Long hdel = jedisClient.hdel(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "");
        System.out.println("刪除購物車購物項為"+hdel);
        return E3Result.ok();
    }

redis購物車中更新購買商品的數量

    /**
     * 更新購物車中商品的數量
     */
    @Override
    public E3Result updateRedisNum(Long id, Long itemId, Integer num) {
        // 取出需要更改數量的商品信息
        String hget = jedisClient.hget(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "");
        // 將取出的json數據轉換為商品對象,然后更新數量
        TbItem item = JsonUtils.jsonToPojo(hget, TbItem.class);
        item.setNum(num);
        // 更新成功后,將數據寫到redis購物車中
        jedisClient.hset(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "", JsonUtils.objectToJson(item));
        return E3Result.ok();
    }

當用戶點擊去結算時:跳轉到訂單確認頁面

1、生成訂單詳情

2、配送地址信息

3、選擇支付方式

在確認訂單之前, 應該判斷用戶是否是登錄裝態,可以使用攔截器實現

1、自定義攔截器實現HandlerInteceptor接口

2、從cookie中去token消息(登錄認證的令牌)

3、判斷token的值是否為空,如果為空,就跳轉到用戶登錄頁面完成登錄,同時需要將當前地址欄的url作為參數傳遞(在登錄的業務邏輯中,接收該url,完成登錄后,跳轉會該頁面)

4、如果token不為空,根據token查詢用戶信息,然后將用戶信息寫入request域中,攔截器執行放行操作

5、此時獲取到的購物車列表是從redis中讀出的和cookie整合的最新的購物車。

 

攔截器的實現過程:

public class LoginInterceptor implements HandlerInterceptor {
    
    @Value("${TT_TOKEN}")
    private String TT_TOKEN;
    @Value("${SSO_LOGIN_URL}")
    private String SSO_LOGIN_URL;
    
    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //執行Handler之前執行此方法
        // a)從cookie中取token。
        String token = CookieUtils.getCookieValue(request, TT_TOKEN);
        if (StringUtils.isBlank(token)) {
            //取當前請求的url
            String url = request.getRequestURL().toString();
            // b)沒有token,需要跳轉到登錄頁面。
            response.sendRedirect(SSO_LOGIN_URL + "?redirectUrl=" + url);
            //攔截
            return false;
        }
        // c)有token。調用sso系統的服務,根據token查詢用戶信息。
        e3Result result = userService.getUserByToken(token);
        if (result.getStatus() != 200) {
            // d)如果查不到用戶信息。用戶登錄已經過期。需要跳轉到登錄頁面。
            //取當前請求的url
            String url = request.getRequestURL().toString();
            // b)沒有token,需要跳轉到登錄頁面。
            response.sendRedirect(SSO_LOGIN_URL + "?redirectUrl=" + url);
            //攔截
            return false;
        }
        // e)查詢到用戶信息。放行。
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // 執行Handler之后返回ModelAndView之前

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 返回ModelAndView之后,執行。異常處理。

    }

}





@Override
    public E3Result getToken(String token) {
        try {
            // 從redis中取值
            String json = jedisClient.get("USER_INFO:" + token);
            if (StringUtils.isBlank(json)) {
                // json為空,表示已經過期
                return E3Result.build(400, "session已經過期,請重新登錄");
            }
            //將json對象轉化為pojo對象
            TbUser user = JsonUtils.jsonToPojo(json, TbUser.class);
            //重新設置用戶登錄信息的過期時間
            jedisClient.expire("USER_INFO:" + token, 1800);
            //將獲取的user信息使用E3Result包裝后返回
            return E3Result.ok(user);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

攔截器定義好之后,需要在springmvc中配置

<mvc:interceptors>
        <mvc:interceptor>
            <!-- 攔截所有請求 -->
            <mvc:mapping path="/**" />
            <!-- 注冊自定義攔截器 -->
            <bean class="nyist.e3.interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

在登錄頁面接收url,實現sso系統的回調 接收的redirectUrl即為攔截中請求登錄頁面傳遞的參數

 

 至此:購物車模塊的功能基本實現,錯誤的地方希望大家多多指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM