商城網站--簡易購物車實現


      晚上心血來潮,為了復習之前學過的內容,溫故而至知新,所以決定寫一個簡易的網絡商城購物車,該小項目采用的技術非常簡單,基本上都是原生的,算是對基礎知識的復習。

一、項目實現

項目實現是基於Servlet的,數據庫采用MySql5.6,項目中用到了很多技術,下面通過代碼來說明。項目結構如圖所示:

      該項目采用MVC模式,Controller負責接收請求並處理結果,服務層實現業務邏輯,dao層負責和數據庫進行交互,視圖層測采用了普通的html頁面顯示結果,為了提高安全性與效率為HttpRequest和HttpResponse添加了ThreadLocal變量,提高線程的安全性。關於Cookie,單獨寫了一個類CookieTools,該類提供了對cookie的增、刪、改操作的方法。單獨編寫了一個過濾器,目的是對中文編碼utf-8進行過濾,防止出現亂碼。另外,購物車的表格稍微加了一點樣式,這樣看起來不是那么的單調。

      項目實現思想:該項目實現了兩個購物車,分別是公共購物車和私有購物車,公共購物車是在沒有登陸狀態下的所看到,任何人都可以操作,私有購物車是登陸以后才能看到的,公共購物車和私有購物車的商品可以合並,合並以后可以進行下單,下單后跳轉到支付頁面填寫支付收貨人信息等,收貨人信息實現了3級聯動。

下面詳細介紹項目的實現過程:

首先創建基於javaEE的項目shopping_cart,導入相關的jar包,這里主要用到的就是mysql-connector-java-5.1.7-bin.jar這個包,然后按照MVC模型的思想分別編寫各層的代碼。

1) 數據庫的設計

      該項目涉及到的數據表格有:userinfo表,主要存放的是用戶的信息,只有數據庫中的用戶才能夠登陸。還有另外3張,分別為省表、市表和縣表,供填寫收貨人信息和實現三級聯動使用。表結構如圖所示:

 2)封裝HttpRequest和HttpResponse

 該類位於utils包下:將HttpRequest和HttpResponse放在ThreadLocal變量中,采用set方法放入,get方法獲取,代碼如下:

 

 1 package utils;
 2 import javax.servlet.http.HttpServletRequest;
 3 import javax.servlet.http.HttpServletResponse;
 4 
 5 public class RequestResponseBox {
 6 
 7     private static ThreadLocal<HttpServletRequest> requestBox = new ThreadLocal<>();
 8     private static ThreadLocal<HttpServletResponse> responseBox = new ThreadLocal<>();
 9 
10     public static void setRequest(HttpServletRequest request) {
11         requestBox.set(request);
12     }
13 
14     public static void setResponse(HttpServletResponse response) {
15         responseBox.set(response);
16     }
17 
18     public static HttpServletRequest getRequest() {
19         return requestBox.get();
20     }
21 
22     public static HttpServletResponse getResponse() {
23         return responseBox.get();
24     }
25 
26 }

 

3)封裝cookie

該類提供對cookie的增刪改的操作,具體代碼如下:

 1 public class CookieTools {
 2 
 3     /**
 4      * 保存cookie
 5      * @param cookieName
 6      * @param cookieValue
 7      * @param maxAge
 8      * @return void
 9      * @author wangsj
10      */
11     public void save(String cookieName, String cookieValue, int maxAge) {
12         try {
13             cookieValue = java.net.URLEncoder.encode(cookieValue, "utf-8");
14             Cookie cookie = new Cookie(cookieName, cookieValue);
15             cookie.setMaxAge(maxAge);
16             RequestResponseBox.getResponse().addCookie(cookie);
17         } catch (UnsupportedEncodingException e) {
18             e.printStackTrace();
19         }
20     }
21 
22     /**
23      * 獲取cookie值
24      * @param cookieName
25      * @return
26      * @return String
27      * @author wangsj
28      */
29     public String getValue(String cookieName) {
30         String cookieValue = null;
31         try {
32             Cookie[] cookieArray = RequestResponseBox.getRequest().getCookies();
33             if (cookieArray != null) {
34                 for (int i = 0; i < cookieArray.length; i++) {
35                     if (cookieArray[i].getName().equals(cookieName)) {
36                         cookieValue = cookieArray[i].getValue();
37                         cookieValue = java.net.URLDecoder.decode(cookieValue, "utf-8");
38                         break;
39                     }
40                 }
41             }
42         } catch (UnsupportedEncodingException e) {
43             e.printStackTrace();
44         }
45         return cookieValue;
46     }
47 
48     /**
49      * 根據cookie名刪除cookie
50      * @param cookieName
51      * @return void
52      * @author wangsj
53      */
54     public void delete(String cookieName) {
55         Cookie cookie = new Cookie(cookieName, "");
56         cookie.setMaxAge(0);
57         RequestResponseBox.getResponse().addCookie(cookie);
58     }
59 
60 }

3)封裝Filter

過濾器的主要作用是對傳入的request,response提前過濾掉一些信息,或者提前設置一些參數(本項目主要進行中文編碼),然后再傳入servlet是,防止中文出現亂碼。比較簡單,代碼如下:

RequestResponseFilter:

 1 package filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13 
14 import utils.RequestResponseBox;
15 
16 public class RequestResponseFilter implements Filter {
17 
18     @Override
19     public void init(FilterConfig filterConfig) throws ServletException {
20     }
21 
22     @Override
23     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
24             throws IOException, ServletException {
25         RequestResponseBox.setRequest((HttpServletRequest) request);
26         RequestResponseBox.setResponse((HttpServletResponse) response);
27         chain.doFilter(request, response);
28     }
29 
30     @Override
31     public void destroy() {
32     }
33 
34 }

CharSetFilter:

 1 package filter;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.Filter;
 6 import javax.servlet.FilterChain;
 7 import javax.servlet.FilterConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 
12 public class CharSetFilter implements Filter {
13 
14     @Override
15     public void init(FilterConfig filterConfig) throws ServletException {
16     }
17 
18     @Override
19     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
20             throws IOException, ServletException {
21         request.setCharacterEncoding("utf-8");
22         response.setCharacterEncoding("utf-8");
23         chain.doFilter(request, response);
24     }
25 
26     @Override
27     public void destroy() {
28     }
29 
30 }

4)封裝數據庫連接工具

該類主要是對數據庫進行連接,對該類進行封裝,方便dao層調用。代碼如下:

GetConnectionType類

 1 package dbtools;
 2 
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.SQLException;
 6 
 7 public class GetConnectionType {
 8     public static Connection getConnection() throws SQLException, ClassNotFoundException {
 9         String url = "jdbc:mysql://localhost:3306/test";
10         String driver = "com.mysql.jdbc.Driver";
11         String username = "root";
12         String password = "123456";
13         Class.forName(driver);
14         Connection connection = DriverManager.getConnection(url, username, password);
15         return connection;
16     }
17 }

GetConnection類,該類依然使用了ThreadLocal變量,代碼如下:

 1 package dbtools;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 
 6 public class GetConnection {
 7     
 8     private static ThreadLocal<Connection> local = new ThreadLocal<>();
 9     //獲取鏈接
10     public static Connection getConnection() throws ClassNotFoundException, SQLException {
11         Connection conn = local.get();
12         if (conn == null) {
13             conn = GetConnectionType.getConnection();
14             conn.setAutoCommit(false);
15             local.set(conn);
16         }
17         return conn;
18     }
19     //提交事務
20     public static void commit() {
21         try {
22             if (local.get() != null) {
23                 local.get().commit();
24                 local.get().close();
25                 local.set(null);
26             }
27         } catch (SQLException e) {
28             e.printStackTrace();
29         }
30     }
31     //回滾
32     public static void rollback() {
33         try {
34             if (local.get() != null) {
35                 local.get().rollback();
36                 local.get().close();
37                 local.set(null);
38             }
39         } catch (SQLException e) {
40             e.printStackTrace();
41         }
42     }
43 }

5)實現第一個功能:登陸

      登陸的基本思想:對輸入的用戶名和密碼進行校驗,然后到數據庫去匹配,如果匹配成功則允許等錄,否則,提示用戶有誤。該功能涉及到UserinfoDao,UserinfoService,Login這些類以及index.js里面編寫的login方法以及html頁面。代碼如下:

UserinfoDao:

 1 package dao;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 
 8 import dbtools.GetConnection;
 9 import entity.Userinfo;
10 
11 public class UserinfoDao {
12 
13     public Userinfo getUserinfo(String username, String password) throws SQLException, ClassNotFoundException {
14         Userinfo userinfo = null;
15         String sql = "select * from userinfo where username=? and password=?";
16         Connection conn = GetConnection.getConnection();
17         PreparedStatement ps = conn.prepareStatement(sql);
18         ps.setString(1, username);
19         ps.setString(2, password);
20         ResultSet rs = ps.executeQuery();
21         while (rs.next()) {
22             int idDB = rs.getInt("id");
23             String usernameDB = rs.getString("username");
24             String passwordDB = rs.getString("password");
25             userinfo = new Userinfo();
26             userinfo.setId(idDB);
27             userinfo.setUsername(usernameDB);
28             userinfo.setPassword(passwordDB);
29         }
30         rs.close();
31         ps.close();
32         return userinfo;
33     }
34 
35     public Userinfo getUserinfo(String username) throws SQLException, ClassNotFoundException {
36         Userinfo userinfo = null;
37         String sql = "select * from userinfo where username=?";
38         Connection conn = GetConnection.getConnection();
39         PreparedStatement ps = conn.prepareStatement(sql);
40         ps.setString(1, username);
41         ResultSet rs = ps.executeQuery();
42         while (rs.next()) {
43             int idDB = rs.getInt("id");
44             String usernameDB = rs.getString("username");
45             String passwordDB = rs.getString("password");
46             userinfo = new Userinfo();
47             userinfo.setId(idDB);
48             userinfo.setUsername(usernameDB);
49             userinfo.setPassword(passwordDB);
50         }
51         rs.close();
52         ps.close();
53         return userinfo;
54     }
55 }

UserinfoService

 1 package service;
 2 
 3 import java.sql.SQLException;
 4 
 5 import cookietools.CookieTools;
 6 import dao.UserinfoDao;
 7 import entity.Userinfo;
 8 import f.F;
 9 
10 public class UserinfoService {
11 
12     private UserinfoDao userinfoDao = new UserinfoDao();
13     private CookieTools cookieTools = new CookieTools();
14 
15     public boolean login(String username, String password) throws SQLException, ClassNotFoundException {
16         Userinfo userinfo = userinfoDao.getUserinfo(username, password);
17         if (userinfo != null) {
18             cookieTools.save(F.CURRENT_LOGIN_USERNAME, username, 36000);
19             return true;
20         } else {
21             return false;
22         }
23     }
24 
25     public Userinfo getUserinfoByUsername(String username) throws SQLException, ClassNotFoundException {
26         Userinfo userinfo = userinfoDao.getUserinfo(username);
27         return userinfo;
28     }
29 
30     public String getCurrentLoginUsername() {
31         return cookieTools.getValue(F.CURRENT_LOGIN_USERNAME);
32     }
33 
34 }

index.js

 1 function login(){
 2     var usernameValue = $("#username").val();
 3     var passwordValue = $("#password").val();
 4     $.post("login?t=" + new Date().getTime(), {
 5         "username": usernameValue,
 6         "password": passwordValue
 7     }, function(data){
 8         if (data == 'true') {
 9             $("#loginForm").hide();
10             initWelcomeUI(usernameValue);
11         }
12         else {
13             alert("登陸失敗請重新輸入!");
14         }
15     });
16 }

index.html

 1 <div id="loginDIV">
 2             <div id="loginForm" style="display:none">
 3                 username:<input type="text" id="username">
 4                 <br/>
 5                 password:<input type="text" id="password">
 6                 <br/>
 7                 <input type="button" value="登陸" onclick="javascript:login()">
 8             </div>
 9             <div id="welcomeDIV" style="display:none">
10             </div>
11         </div>

 

 

 

 運行結果如下:比較單調,沒有加任何樣式

6)購物車的實現

      購物車實現比較麻煩,本項目采用的是字符串拼接的方式實現的,比較原始,稍微復雜一點,加入購物車代碼在CartService類中,

商品信息采用的是:A商品id_數量-B商品id_數量的形式實現,部分代碼如下:

 1 public void putCart(String bookIdParam) throws SQLException, ClassNotFoundException {
 2         String cartName = "";
 3         String cartValue = "";
 4         String newCartValue = "";
 5 
 6         String currentLoginUsername = userinfoService.getCurrentLoginUsername();
 7         if (currentLoginUsername == null) {
 8             cartName = F.PUBLIC_CART_NAME;
 9         } else {// privataCart_userId
10             int userId = userinfoService.getUserinfoByUsername(currentLoginUsername).getId();
11             cartName = F.PRIVATE_CART_NAME_PREFIX + userId;
12         }
13         cartValue = cookieTools.getValue(cartName);
14         if (cartValue == null) {
15             cartValue = bookIdParam + "_1";
16         } else {
17             String[] bookinfoArray = cartValue.split("\\-");
18             boolean isFindBookId = false;
19             for (int i = 0; i < bookinfoArray.length; i++) {
20                 String bookId = bookinfoArray[i].split("\\_")[0];
21                 String bookNum = bookinfoArray[i].split("\\_")[1];
22                 if (bookId.equals(bookIdParam)) {
23                     isFindBookId = true;
24                     break;
25                 }
26             }
27             if (isFindBookId == false) {
28                 cartValue = cartValue + "-" + bookIdParam + "_1";
29             } else {
30                 for (int i = 0; i < bookinfoArray.length; i++) {
31                     String bookId = bookinfoArray[i].split("\\_")[0];
32                     String bookNum = bookinfoArray[i].split("\\_")[1];
33                     if (bookId.equals(bookIdParam)) {
34                         newCartValue = newCartValue + "-" + bookId + "_" + ((Integer.parseInt(bookNum)) + 1);
35                     } else {
36                         newCartValue = newCartValue + "-" + bookId + "_" + bookNum;
37                     }
38                 }
39                 newCartValue = newCartValue.substring(1);
40                 cartValue = newCartValue;
41             }
42         }
43         System.out.println(cartValue + "     " + cartName);
44         cookieTools.save(cartName, cartValue, 36000);
45     }

 

商品信息的傳輸時采用以下的信息:

<list>
  <bookinfo>
    <id>1</id>
    <bookname>Java</bookname>
    <bookprice>33.56</bookprice>
  </bookinfo>
  <bookinfo>
    <id>2</id>
    <bookname>c++</bookname>
    <bookprice>56.45</bookprice>
  </bookinfo>
  <bookinfo>
    <id>3</id>
    <bookname>Hibernat</bookname>
    <bookprice>80.55</bookprice>
  </bookinfo>
  <bookinfo>
    <id>4</id>
    <bookname>Struts</bookname>
    <bookprice>53.45</bookprice>
  </bookinfo>
  <bookinfo>
    <id>5</id>
    <bookname>Java</bookname>
    <bookprice>34.22</bookprice>
  </bookinfo>
  <bookinfo>
    <id>6</id>
    <bookname>we</bookname>
    <bookprice>12.22</bookprice>
  </bookinfo>
</list>

 

 此外,書籍數量的更新采用ajax實現,實現頁面局部更新,從而避免頁面更新時的閃動,下面是部分的數量更新代碼:

 1 function updateCartBookNum(operateType, cartType, bookId){
 2     $.post("updateCartBookNum?t=" + new Date().getTime(), {
 3         "operateType": operateType,
 4         "cartType": cartType,
 5         "bookId": bookId
 6     }, function(data){
 7         if (data == '1') {
 8             updateCartBookNumTRInfo(cartType, bookId);
 9             setCartBottomInfoTR(cartType);
10         }
11         else {
12             alert("更新書籍數量失敗!");
13         }
14     });
15 }

 

7) 購物車實現效果

輸入用戶名登陸后的頁面如下所示:

當點擊放入購物車按鈕是會有一個動畫效果,顯示2秒然后消失,當點擊顯示購物車按鈕時,會顯示私有購物車的一些信息,如下所示:

 

這里顯示的是雙購物車,即私有購物車和公共購物車,雙購物車中的商品可以進行合並,合並實現的思想是首先判斷商品id,如果id相同則進行數量的相加,否則進行字符串的拼接。代碼如下:

 1 public void putPrivateCart(String publicCart_id_num_String) throws ClassNotFoundException, SQLException {
 2         String cartName = "";
 3         String cartValue = "";
 4         String newCartValue = "";
 5 
 6         String currentLoginUsername = userinfoService.getCurrentLoginUsername();
 7         int userId = userinfoService.getUserinfoByUsername(currentLoginUsername).getId();
 8         cartName = F.PRIVATE_CART_NAME_PREFIX + userId;
 9         cartValue = cookieTools.getValue(cartName);
10 
11         if (cartValue != null) {
12 
13             String[] publicBookinfoArray = publicCart_id_num_String.split("-");
14             String[] privateBookinfoArray = cartValue.split("-");
15 
16             String differenceString = "";
17 
18             for (int i = 0; i < publicBookinfoArray.length; i++) {
19                 String bookIdPublic = publicBookinfoArray[i].split("\\_")[0];
20                 String bookNumPublic = publicBookinfoArray[i].split("\\_")[1];
21                 boolean isFindBookId = false;
22                 for (int j = 0; j < privateBookinfoArray.length; j++) {
23                     String bookIdPrivate = privateBookinfoArray[j].split("\\_")[0];
24                     String bookNumPrivate = privateBookinfoArray[j].split("\\_")[1];
25                     if (bookIdPublic.equals(bookIdPrivate)) {
26                         privateBookinfoArray[j] = bookIdPrivate + "_"
27                                 + ((Integer.parseInt(bookNumPublic)) + (Integer.parseInt(bookNumPrivate)));
28                         isFindBookId = true;
29                     }
30                 }
31                 if (isFindBookId == false) {
32                     differenceString = differenceString + "-" + bookIdPublic + "_" + bookNumPublic;
33                 }
34             }
35             for (int i = 0; i < privateBookinfoArray.length; i++) {
36                 newCartValue = newCartValue + "-" + privateBookinfoArray[i];
37             }
38             newCartValue = newCartValue + differenceString;
39             newCartValue = newCartValue.substring(1);
40             cartValue = newCartValue;
41         } else {
42             cartValue = publicCart_id_num_String;
43         }
44         System.out.println(cartValue);
45         cookieTools.save(cartName, cartValue, 36000);
46     }

 

 點擊putPrivateCart按鈕,如果左側沒有商品被選中的時候,會彈出提示框,如圖所示:

選中,左側按鈕所對應的商品,便可以成功放入私有購物車,放入購物車就可以進行下單操作了,如圖點擊左下角的輸入訂單信息,便可以進入,訂單頁面,填好訂單,可以進行購物車的確認以及訂單的提交。

二、總結

     到這里,一個簡易的購物車就基本實現了,但這僅僅只是宇哥雛形,改進的地方還有很多,鑒於篇幅和時間的關系,只貼出了部分代碼,其他源碼,感興趣的朋友可以下載附件研究改進,待改進的地方:

1、數據庫的設計有待完善,數據庫中的字段可以添加數量的實時更新,顯示庫存等。

2、商品添加購物車采用的字符串拼接比較麻煩,可以采用json格式的字符串和前台進行交互。

3、異常的捕捉不是很好,可以考慮多加入異常的處理。

4、沒有日志的記錄,可以采用log4j對日志進行處理,方便調試,明確問題出在哪里。

5、可以考慮引入Spring框架,進行充分解耦。

6、登錄功能的設計有待完善,后期可以加入注冊功能,登錄功能可以用SpringMVC或者Struts2框架進行改進,另外登陸驗證可以采用前后台雙向校驗的方式進行驗證。

  以上這些均是該項目有待改進的地方,還有很多需要完善的地方,希望對大家的學習有所幫助。

 源碼下載地址:http://files.cnblogs.com/files/10158wsj/shopping_cart.rar


免責聲明!

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



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