本文講什么
可以看到,購物車這樣一個功能模塊,在各種購物類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