購物車原理以及實現


本文講什么

可以看到,購物車這樣一個功能模塊,在各種購物類APP或者web應用中絕對是必不可少的東西.不論在大學中的課程設計,還是在實際的項目開發中,絕對非常重要且有些復雜的內容.
在實際操作中,身邊有很多的小伙伴遇到編寫購物車的代碼的時候,有時候真的是一臉懵逼,總是搞不明白設計的思路,這就是本文寫作的原因.
所以,本文適合搞不清楚購物車實現原理,知道原理但是實際編碼不知道如何下手的小伙伴,我將給出一個思路以及實際的代碼供大家參考.
在本文中,我將會用盡可能簡單的句子,表達出我想表達的意思.廢話不多說,開始我們的購物車實戰!

購物車的幾種實現方式

購物車的實現方式有很多,但是最常見的就三種:Cookie,Session,數據庫.三種方法各有優劣,適合的場景各不相同.

  • Cookie方法:通過把購物車中的商品數據寫入Cookie中,再通過瀏覽器進行讀取.這個方法,適合在用戶沒有登錄的情況下使用,但是有個非常嚴重的缺點,即在用戶禁用了Cookie的時候是無法使用的.
  • Session方法:通過Session來保存商品信息,這確實是個好的方法,適合用戶已經登錄的情況,將數據放在Session中,用戶就能讀取購物車中的商品信息,而且速度十分的快.但是缺點也很明顯,由於Session是建立在用戶客戶端和服務器之間的,在Session中保存數據,無疑會增加服務器的負擔.
  • 數據庫(Redis):數據庫無疑是一種非常棒的保存購物車中信息的有效途徑,且能夠持久化保存,但是問題也很明顯,那就是讀取速度會差強人意.

好了,下面來說一下幾種實現方式的應用場景.

  • 當用戶沒有登錄的情況下,用戶將商品加入購物車,此時的商品信息是寫入了Cookie中,並且會設置一個保存時間,即使關閉瀏覽器過一段時間訪問仍能看到購物車中的信息.(或者將購物數據寫入Session中,但是關閉瀏覽器,購物車中的信息也就不見了)
  • 用戶登陸后,如果在Session中存儲了商品信息且沒有關閉瀏覽器(如果在Cookie中存儲了商品信息且沒有過期),將會讀取其中的商品信息,並且將這些信息寫入數據庫中進行持久保存.

本文的行文方式說明

經過上面的講解,我想你一定對購物車有所了解,為了使讀者更加清晰的明白購物車的實現,我們省去了在未結算的狀態下的持久化數據庫.
也就是說,在文章中,我將使用Session來實現購物車,並且當用戶沒有登錄的情況下,禁止用戶將商品加入購物車.當然你不必為此擔憂,即使我這樣做,我的代碼已經包括了整個購物操作的絕大多數步驟.請耐心向下看.
此外,本文使用SSM框架作為行文代碼.
如果你是初學者也不必擔心,我將為你提供一套項目的源代碼,可以在我的GitHub中獲取—->https://github.com/roobtyan/dinner” target=”_blank”>餐廳點餐系統,這套系統是基於servlet+jsp+mysql開發的,注釋非常完善,當然最重要的模塊也就是下單模塊肯定是有的,而且非常完善,歡迎下載.

購物車模塊的實現

數據庫設計

  • 用戶表
字段 意義
id 用戶id
userName 用戶名
password 用戶密碼

- 商品表

字段 意義
id 商品id
commName 商品名稱
price 商品價格

- 訂單表

字段 意義
id 訂單id
commName 商品名稱
count 商品數量
subtotal 商品小計
total 總價

用戶登錄

為了實現我上述的思路,肯定是要求用戶先行登錄.代碼如下.
- LoginController

@Controller
public class LoginController {
    private LoginService loginService;
    private CommonService commonService;

    @Autowired
    public LoginController(LoginService loginService, CommonService commonService) {
        this.loginService = loginService;
        this.commonService = commonService;
    }

    @RequestMapping("/login")
    public String login(User user, HttpSession session, Model model) {
        //登錄驗證
        if (loginService.isUser(user)) {
            List<Common> commons = commonService.selectAllCommons();
            model.addAttribute("commons", commons);
            model.addAttribute("username", user.getUsername());
            //把用戶信息保存到session中
            session.setAttribute("user", user);
            return "shopping";
        } else {
            model.addAttribute("message", "用戶名或密碼錯誤");
            return "index";
        }
    }
}

這是最常規的用戶登錄的代碼,思路就是拿着用戶名到數據庫里面查詢,如果能查到,則說明用戶名無誤,如果查不到則說明沒有此用戶,提示用戶注冊.如果用戶名存在,再比對密碼,一般密碼不會像我們這樣直接在數據庫里面使用明文密碼,都是會加鹽的(MD5算法).如果比對密碼的結果為true,則用戶可以登錄.比對過程isUser的代碼如下.

@Service
public class LoginService {
    private UserMapper userMapper;

    @Autowired
    public LoginService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    /** * 判斷用戶名或密碼是否正確 * @param user * @return */
    public boolean isUser(User user){
        UserExample example = new UserExample();
        UserExample.Criteria criteria  = example.createCriteria();
        criteria.andUsernameEqualTo(user.getUsername());
        List<User> eqUser = userMapper.selectByExample(example);
        //如果沒有查詢到user,則返回false
        if (eqUser == null){
            return false;
        }
        return eqUser.get(0).getPassword().equals(user.getPassword());
    }

}

確認用戶登錄以后,需要把用戶信息放在session中以便后續過程使用.

用戶購物模塊

當用戶登錄以后,展示在用戶眼前的界面是這樣的(頁面模板來自菜鳥教程,經過改編):

左欄中的數據來自登錄代碼中的

List<Common> commons = commonService.selectAllCommons();
model.addAttribute("commons", commons);

當點擊加入購物車以后,觸發onlick事件:onclick="javascript:joinCart(${common.id})"
詳細代碼如下:

function joinCart(id) {
        $.ajax({
            url: "${pageContext.request.contextPath}/shop/joinCart",
            data: "id=" + id,
            type: "POST",
            success: function (result) {
                alert("加入購物車成功!");
                //清空購物車列表
                $("#shop_cart tbody").empty();
                //動態構建購物車列表
                var obj = result;
                $.each(obj,function (index,item) {
                   var emptyTd = $("<td></td>").append("#");
                   var commnameTd = $("<td></td>").append(item.commname);
                   var countTd = $("<td></td>").append(item.count);
                   var subtotalTd = $("<td></td>").append(item.subtotal);

                   $("<tr></tr>")
                       .append(emptyTd)
                       .append(commnameTd)
                       .append(countTd)
                       .append(subtotalTd)
                       .appendTo("#shop_cart tbody");
                   //設置總金額
                   var totalSpan = document.getElementById("subtotal");
                   totalSpan.innerHTML = item.total;
                });


            }
        })
    }

可以看到,當觸發這個方法時,實際上是使用了異步請求的方式向服務端發送數據,服務端做相應處理以后,封裝購物車列表,然后把購物車商品列表以JSON格式傳回,也就是封裝在result中,利用js,動態構建購物車列表.於是就出現下面這種情況.
當將商品加入購物車以后:

首先提示用戶已經加入購物車,然后在利用異步請求構建整個購物車,如果你對前端的了解並不是很深,不必擔心,這部分內容實際上很簡單,你可以隨便百度一下這個知識點,記住就好了.實際上就是利用js操作json數據而已.

其實對於初學者來說,感覺上面的過程挺神奇的,其實不然.前端的代碼看完了,我就來給你詳細的解釋一下后端的代碼.
首先,可以將后段代碼分成兩部分,一部分是判斷,各種判斷,另外一部分是封裝數據,相對簡單.

 //標識符:判斷是否存在此商品
boolean flag = false;
//通過id查詢商品信息
Common common = shopService.selectCommById(Integer.parseInt(id));
//從session中獲取購物車信息
List<Order> shopcart = (List<Order>) session.getAttribute("shopcart");
//獲取用戶信息
User user = (User) session.getAttribute("user");
if (user == null) {
    //如果用戶為空,則直接返回,讓用戶登錄
    throw new RuntimeException("用戶未登錄");
}
//判斷購物車列表是否為空
if (shopcart == null) {
    //new 一個集合
    shopcart = new ArrayList<>();
}

先判斷用戶是否已經登錄,如果用戶已經登錄,則執行后續流程,如果用戶沒有登錄,則直接拋出異常,交給異步請求的error處理,提示用戶登錄即可.
如果用戶已經登錄,則繼續下面的步驟,判斷購物車是否為空,為空也就是說明用戶沒有將任何商品加入購物車,則需要創建一個新的ArrayList集合,同時利用商品id查詢出的商品信息,封裝訂單對象.

else {
    for (Order order : shopcart) {
        //判斷是否存在此商品,存在則數量+1
        if (order.getCommname().equals(common.getCommname())) {
            flag = true;
            order.setCount(order.getCount() + 1);
            order.setSubtotal(order.getCount() * common.getPrice());
        }
    }
}

在遍歷的過程中,如果遍歷的數據的commName和查到的name相同的話,則證明是同一件商品,則將此商品的數量+1,然后再設置小計價格即可.同時將flag設置為true,表示遍歷到了同樣的數據.
如果沒有遍歷到名稱相同的商品,則直接新建一個對象,封裝數據,加入集合.

//如果購物車中沒有當前商品的信息,則新增商品
if (!flag) {
    Order order = new Order();
    order.setCommname(common.getCommname());
    order.setCount(1);
    order.setSubtotal(common.getPrice());
    //把商品加入集合
    shopcart.add(order);
}

最后一步就是計算總價格,封裝數據即可.

//計算總價格
Double total = 0d;
for (Order order : shopcart) {
    total += order.getSubtotal();
}
for (Order order : shopcart) {
    order.setTotal(total);
}
//設置session
session.setAttribute("shopcart", shopcart);
//返回list
return shopcart;

購物車全部代碼如下:

Controller
@RequestMapping("/shop")
public class ShopController {
    private ShopService shopService;

    public ShopController(ShopService shopService) {
        this.shopService = shopService;
    }

    @RequestMapping("/joinCart")
    @ResponseBody
    public List<Order> joinCart(String id, HttpSession session, Model model) {
        //標識符:判斷是否存在此商品
        boolean flag = false;
        //通過id查詢商品信息
        Common common = shopService.selectCommById(Integer.parseInt(id));
        //從session中獲取購物車信息
        List<Order> shopcart = (List<Order>) session.getAttribute("shopcart");
        //獲取用戶信息
        User user = (User) session.getAttribute("user");
        if (user == null) {
            //如果用戶為空,則直接返回,讓用戶登錄
            throw new RuntimeException("用戶未登錄");
        }
        //判斷購物車列表是否為空
        if (shopcart == null) {
            //new 一個集合
            shopcart = new ArrayList<>();
        } else {
            for (Order order : shopcart) {
                //判斷是否存在此商品,存在則數量+1
                if (order.getCommname().equals(common.getCommname())) {
                    flag = true;
                    order.setCount(order.getCount() + 1);
                    order.setSubtotal(order.getCount() * common.getPrice());
                }
            }
        }
        //如果購物車中沒有當前商品的信息,則新增商品
        if (!flag) {
            Order order = new Order();
            order.setCommname(common.getCommname());
            order.setCount(1);
            order.setSubtotal(common.getPrice());
            //把商品加入集合
            shopcart.add(order);
        }
        //計算總價格
        Double total = 0d;
        for (Order order : shopcart) {
            total += order.getSubtotal();
        }
        for (Order order : shopcart) {
            order.setTotal(total);
        }
        //設置session
        session.setAttribute("shopcart", shopcart);
        //返回list
        return shopcart;
    }

}

關於操作購物車商品數量及結算

首先說操作購物車商品數量,既然我們能夠按照通過id加商品的數量,肯定也是能夠按照商品id減商品的數量,這部分無需多說,相信按照上面的代碼,以你的聰明才智,肯定是能夠做出來的.
至於結算操作,就更加單了.
用戶點擊結算按鈕以后,跳轉到后台,通過后台代碼,先把session中保存的數據取出,然后遍歷將數據寫入數據庫,進行持久化的操作即可.
以上.
獲取文中項目代碼:https://download.csdn.net/download/yanmiao0715/10570386
如果您的積分不夠,歡迎關注我的微信公眾號:最高權限比特流,回復”購物車源代碼”進行下載.

結語

感謝您的閱讀,如果您對文章有任何問題,歡迎留言,歡迎聯系我:roobtyan@outlook.com.
也歡迎您關注我的微信公眾號:最高權限比特流.
以及我的個人博客:www.roobtyan.cn


免責聲明!

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



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