商城网站--简易购物车实现

      晚上心血来潮,为了复习之前学过的内容,温故而至知新,所以决定写一个简易的网络商城购物车,该小项目采用的技术非常简单,基本上都是原生的,算是对基础知识的复习。

一、项目实现

项目实现是基于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

posted @ 2017-05-03 16:23 我心自在 阅读(...) 评论(...) 编辑 收藏