9.1 购物车
一个功能完善的购物车,它的基本特点主要表现为以下两点:
·不一定要购买购物车中的产品,可以随时随地的从购物车取出自己不需要的产品。
·可以随时更改商品的数量。 如果最终决定想要购买购物车中的全部或部分商品, 首先要确定已经取消了购物车中所不需要的商品和商品数量,然后单击确认的“结帐”功能按钮即可。
要想实现以上的两个功能,就会涉及到购物车的实现机制,进一步讲就是购物车数据的存储问题。在通常情况下,购物车数据的保存有两种方式:一是在session会话中存储,一是在数据库中存储。这两种方式各有利弊,对系统开销的要求也不一样,具体的要视用户的需求而定。
如果放在session中,当session过期或者用户离开页面后,session的内容会消失,但同时应注意到一个事实:当我们离开超市而又未结账,那么再次进入时,上次选择的商品是要重新选择的。另外,放在session中的好处在于可以减少频繁的数据库操作。
如果放在数据库中,就不会出现用户数据丢失的情况,但一个问题就是如何来标识用户。如果在用户选购商品之前要求用户登录那是很烦的。所以最好用cookie值来标识用户,并且他下次登录时还可以显示他之前选过的商品。不支持cookie保存也没关系,不影响当次的在线购物,只是下次来时不能看到之前选过的商品。
下面讲解一个放在数据库情况下的实现思路。
·应该设置一个购物车的表,用来显示当前用户购买的商品。一旦用户选择结账,则把购物车表中的内容删除然后加入用户订单表中。
·在向购物车表中加入数据的时候最好用存储过程,这样一来有利于管理。
本实例中讲述的购物车,把用户的购物车数据保存在session中,同时也在数据库中保留一份,力求克服cookie记录数据的缺点。基本程序代码如程序9-1所示。
程序9-1 购物车的实现机制
<%@ page contentType="text/html; charset=gb2312" %> <%@ page session="true" %> <%@ page import="ch09.web.book.books" %> <jsp:useBean id="book" scope="page" class="ch09.web.booksmn" /> <jsp:useBean id="shop" scope="page" class="ch09.web.purchase" /> <% String mesg = ""; String submits = request.getParameter("Submit"); int Id=0; if (submits!=null && !submits.equals("")){ if (shop.addnew(request)){ mesg = "你要的图书已经放入你的购物车中!谢谢"; } else if (shop.getIsEmpty()){ mesg = "库存图书数量不足!只剩"+shop.getLeaveBook()+"本"; } else { mesg = "暂时不能购买!"; } }else { if (request.getParameter("bookid")==null || request.getParameter("bookid"). equals("")) { mesg = "你要购买的图书不存在!"; } else { try { Id = Integer.parseInt(request.getParameter("bookid")); if (!book.getOnebook(Id)){ mesg = "你要购买的图书不存在!"; } } catch (Exception e){ mesg = "你要购买的图书不存在!"; } } } %> <html> <head> <title>网络电子书店-购买图书</title> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <script language="javascript"> function openScript(url,name, width, height){ var Win = window.open(url,name,'width=' + width + ',height=' + height + ', resizable=1,scrollbars=yes,menubar=no,status=yes' ); }function check() { if (document.form1.amount.value<1){ alert("你的购买数量有问题"); document.form1.amount.focus(); return false; } return true; } </script> <link rel="stylesheet" href="books.css" type="text/css"> </head> <body bgcolor="#BF9AFE" text="#000000" onload="javascript:window.focus();"> <div align="center"> <p>网络电子书店,欢迎你<font color="#CC0066">选购图书</font>!</p> <% if(!mesg.equals("")){ out.println(mesg); } else { books bk = (books) book.getBooklist().elementAt(0); %> <table width="90%" border="0" cellspacing="2" cellpadding="1"> <form name="form1" method="post" action="purchase.jsp"> <tr> <td align="center">图书名:<%= bk.getBookName() %></td> </tr> <tr align="center"> <td>你想要的数量: <input type="text" name="amount" maxlength="4" size="3" value="1"> 本</td> </tr> <tr align="center"> <td> <input type="hidden" name="bookid" value="<%=Id %>"> <input type="submit" name="Submit" value="购 买" onclick="return(check());"> <input type="reset" name="Reset" value="取 消"> </td> </tr> <tr align="center"> <td><a href="#" onclick="openScript('showbook.jsp?bookid=<%= Id %>', 'show',400,450)" >查看详细资料</a> </td> </tr> </form> </table> <% } %> <br> <p><a href="javascript:window.close()">关闭窗口</a></p> </div> </body> </html> |
程序9-1简单说明如下。
·购物车会从session会话中取获取当前用户的购物数据信息。当用户添加新的物品时,调用购物车操作类(purchase.java)的addnew(request)方法,将数据加入到购物车中。
·在进行购物车的更新操作时,注意判断数据的有效性。比如,在购物车中添加一定数量的商品时,需要判断此时的库存商品数是否满足用户的选择。如果,某一件商品总库存只有10件,两个用户同时进行购物车的添加操作,一个购买5件,另一人购买6件,从数据库角度来说,晚来的人将不能购买足够的数量。当用户往购物车添加商品不成功时,需要判断此时商品的库存数,使用购物类的getIsEmpty方法。
购物车操作类(purchase.java)的关键代码解释如下。
(1)public boolean addnew(HttpServletRequest newrequest)
·基本功能:处理用户购物需求,主要是增加商品到购物车。
·参数意义:该函数的参数只有一个HttpServletRequest对象,程序从Request中解析相应的参数,本示例中主要是用户选择的商品ID和要购买商品的数量amount。根据这些参数进行相应的处理。
·返回结果:布尔型,如果添加成功,则返回成功TRUE,否则返回失败FALSE。
·实现思路:程序首先判断用户是不是第一次使用购物车,如果第一次使用,则重新初始化一个Vector purchaselist,然后把用户数据添加进去;如果不是第一次购买,则从当前用户会话Session中获取购物车数据列表,然后更新已有的用户数据,接着把更新过的数据添加到购物车数据列表purchaselist。最后把purchaselist放回session中,保证下一次用户看到的是更新过的数据。函数实现代码如程序9-2所示。
程序9-2 添加商品到购物车的实现代码
/** * 向购物车中增加新的物品 * @param newrequest * @return */ public boolean addnew(HttpServletRequest newrequest){ request = newrequest; String ID = request.getParameter("bookid"); String Amount = request.getParameter("amount"); long bookid = 0; int amount = 0; try { bookid = Long.parseLong(ID); amount = Integer.parseInt(Amount); } catch (Exception e) { e.printStackTrace(); return false; } if (amount<1) return false; session = request.getSession(false); if (session == null) { return false; } purchaselist = (Vector)session.getAttribute("shopcar"); sqlStr = "select leav_number from book where id=?"; try { prepstmt = conn.prepareStatement(sqlStr); prepstmt.setLong(1, bookid); rs = prepstmt.executeQuery(); if (rs.next()) { if (amount > rs.getInt(1)) { leaveBook = rs.getInt(1); isEmpty = true; return false; } } rs.close(); } catch (SQLException e) { return false; } indentlist iList = new indentlist(); iList.setBookNo(bookid); iList.setAmount(amount); boolean match = false; //是否购买过该图书 if (purchaselist==null) //第一次购买 { purchaselist = new Vector(); purchaselist.addElement(iList); } else { // 不是第一次购买 for (int i=0; i< purchaselist.size(); i++) { indentlist itList= (indentlist) purchaselist.elementAt(i); if ( iList.getBookNo() == itList.getBookNo() ) { itList.setAmount(itList.getAmount() + iList.getAmount()); purchaselist.setElementAt(itList,i); match = true; break; } //if name matches结束 } // for循环结束 if (!match) purchaselist.addElement(iList); } session.setAttribute("shopcar", purchaselist); return true; } |
(2)public boolean modiShoper(HttpServletRequest newrequest)
·基本功能:该函数负责修改购物车中商品数目。
·参数意义:该函数的参数只有一个HttpServletRequest对象,程序从Request中解析需要修改商品的参数、商品ID和要修改商品的数量。
·返回结果:布尔型,如果修改成功,则返回True,否则返回False。注意失败的情况会有多种,比如修改数量为负数,从Session会话中获取不到用户的购物车列表等。
·实现思路:该函数和上面的增加商品到购物车有类似的实现方法。程序从HttpServletRequest对象中获取需要修改的商品的关键信息,主要包括商品ID和要修改商品的数量。注意验证,库存的商品数量是否满足用户的需要这种情况,即库存的商品数量一定要不小于用户需求的数量时,才能继续修改的操作。该函数的实现代码如程序9-3所示。
程序9-3 修改购物车中的商品实现代码
/** * 修改购物车数据 * @param newrequest * @return */ public boolean modiShoper(HttpServletRequest newrequest) { request = newrequest; String ID = request.getParameter("bookid"); String Amount = request.getParameter("amount"); long bookid = 0; int amount = 0; try { bookid = Long.parseLong(ID); amount = Integer.parseInt(Amount); } catch (Exception e) { e.printStackTrace(); return false; } if (amount<1) return false; session = request.getSession(false); if (session == null) { return false; } purchaselist = (Vector)session.getAttribute("shopcar"); if (purchaselist==null) { return false; } sqlStr = "select leav_number from book where id=?"; try { prepstmt = conn.prepareStatement(sqlStr); prepstmt.setLong(1, bookid); rs = prepstmt.executeQuery(); if (rs.next()) { if (amount > rs.getInt(1)) { leaveBook = rs.getInt(1); isEmpty = true; return false; } } rs.close(); } catch (SQLException e) { return false; } for (int i=0; i< purchaselist.size(); i++) { indentlist itList= (indentlist) purchaselist.elementAt(i); if ( bookid == itList.getBookNo() ) { itList.setAmount(amount); purchaselist.setElementAt(itList,i); break; } } return true; } |
(3)public boolean delShoper(HttpServletRequest newrequest)
·基本功能:该函数负责从购物车中删除商品记录。
·参数意义:该函数的参数只有一个HttpServletRequest对象,程序从Request中解析需要删除商品的信息,只有一个商品唯一标识ID。
·返回结果:布尔型,如果成功删除商品数据,则返回成功标识True,否则返回失败False。该函数同样注意失败的情况会有多种。
·实现思路:程序从HttpServletRequest对象中获取需要删除的商品的关键信息,即商品唯一标识ID。注意判断,当用户的会话信息为空或者会话中的购物车列表为空时,直接返回失败标识,无须下一步的操作。程序依次遍历Vector中的数据,当商品的ID和HttpServletRequest对象中要删除商品的ID一致时,执行删除操作。该函数的实现代码如程序9-4所示。
程序9-4 从购物车中删除商品的实现代码
/** * 删除购物车 * @param newrequest * @return */ public boolean delShoper(HttpServletRequest newrequest) { request = newrequest; String ID = request.getParameter("bookid"); long bookid = 0; try{ bookid = Long.parseLong(ID); }catch (Exception e){ return false; } session = request.getSession(false); if (session == null){ return false; } purchaselist = (Vector)session.getAttribute("shopcar"); if (purchaselist==null){ return false; } for (int i=0; i< purchaselist.size(); i++) { indentlist itList= (indentlist) purchaselist.elementAt(i); if ( bookid == itList.getBookNo() ) { purchaselist.removeElementAt(i); break; } } return true; } |
(4)public boolean payout(HttpServletRequest newrequest)
·基本功能:该函数负责完成用户支付结算生成订单的功能。
·参数意义:该函数的参数只有一个HttpServletRequest对象,程序从Request中解析需要生成订单的相关信息,主要包括用户标识userid、用户的备注信息content和全部商品的价格totalprice。
·返回结果:布尔型,如果成功生成用户订单并更新关联的数据表,则返回成功标识TRUE,否则返回失败FALSE。该函数同样注意,会有多种失败的情况发生。
·异常处理:由于涉及到数据库的操作,所以程序会抛出SQLException,并给出了处理机制。
·实现思路:该函数并不是通常一般意义下的在线支付功能,而是生成订单计算用户需要支付的金额的数据库操作。程序会从HttpServletRequest对象中获取相应的商品数据信息,生成用户的订单列表并更新到数据库,程序为了全局的一致性,同时还要操作另外两个数据表,一个是订单列表indentlist,主要记录订单号、图书编号和图书的数量;另外一张表是商品表,本示例中的book表,主要是更新库存数据为原始数据减去用户已经购买的数量。函数的实现代码如程序9-5所示。
程序9-5 生成购物车订单的实现代码
/** * 支付并生成订单操作 * @param newrequest * @return * @throws Exception */ public boolean payout(HttpServletRequest newrequest) throws Exception { request = newrequest; session = request.getSession(false); if (session == null) { return false; } String Userid = (String) session.getAttribute("userid"); //取得用户ID号 long userid=0; if (Userid==null || Userid.equals("")) { isLogin = false; return false; }else { try { userid = Long.parseLong(Userid); } catch (NumberFormatException e) { return false; } } purchaselist = (Vector)session.getAttribute("shopcar"); if (purchaselist==null || purchaselist.size()<0) { return false; } String Content = request.getParameter("content"); if (Content==null) { Content=""; } Content = getGbk(Content); String IP = request.getRemoteAddr(); String TotalPrice = request.getParameter("totalprice"); sqlStr = "select max(id) from indent"; rs = stmt.executeQuery(sqlStr); if (rs.next()){ IndentNo = "HYD" + userid + "" + rs.getString(1); } else { IndentNo = "HYD" + userid + "0"; } rs.close(); sqlStr = "insert into indent (IndentNo,UserId,SubmitTime,ConsignmentTime, TotalPrice,content,IPAddress,IsPayoff,IsSales) values ('"; sqlStr = sqlStr + IndentNo + "','"; sqlStr = sqlStr + userid + "',getdate(),getdate()+7,'"; sqlStr = sqlStr + TotalPrice + "','"; sqlStr = sqlStr + strFormat.formatSql(Content) + "','"; sqlStr = sqlStr + IP + "',1,1)"; try{ stmt.execute(sqlStr); sqlStr= "select max(id) from indent where UserId = " + userid; rs = stmt.executeQuery(sqlStr); long indentid = 0; while (rs.next()){ indentid = rs.getLong(1); } rs.close(); for (int i=0; i<purchaselist.size() ;i++ ) { indentlist iList = (indentlist) purchaselist.elementAt(i); sqlStr = "insert into indentlist (IndentNo,BookNo,Amount) values ("; sqlStr = sqlStr + indentid + ",'"; sqlStr = sqlStr + iList.getBookNo() + "','"; sqlStr = sqlStr + iList.getAmount() + "')"; stmt.execute(sqlStr); sqlStr = "update book set leav_number=leav_number - " + iList.getAmount() + " where id = " + iList.getBookNo(); stmt.execute(sqlStr); } return true; }catch (SQLException e){ return false; } } |
(5)public boolean getIndent(long userid)
·基本功能:该函数会根据用户ID获取自己订单记录。
·参数意义:参数只有一个长整型数据,对应要操作的用户ID
·返回结果:返回结果为布尔型,如果成功获取用户订单并增加到全局变量my_indent中时返回成功标识,否则返回失败FALSE。
·实现思路:该函数也是数据库相关的操作,根据传递的参数用户ID,从数据库表中获取该用户的全部订单列表。并循环增加到全局变量my_indent中。需要注意的是,我的订单列表为Vector类型,里面存数的数据为订单indent对象。每个这样的对象都包含一条订单的相信信息。读者在编程中,应该学习这种方法,对外的接口全部传递对象参数,实际应用中再从对象中获取对应的信息记录。函数的实现代码如程序9-6所示。
程序9-6 根据用户ID获取自己订单记录的实现代码
/** * 获取自己的订单列表 * @param userid * @return */ public boolean getIndent(long userid) { sqlStr = "select * from indent where userid = ? order by id desc"; try { prepstmt = conn.prepareStatement(sqlStr); prepstmt.setLong(1, userid); rs = prepstmt.executeQuery(); my_indent = new Vector(); while (rs.next()) { indent ind = new indent(); ind.setId(rs.getLong("id")); ind.setIndentNo(rs.getString("indentNo")); ind.setUserId(rs.getLong("userid")); ind.setSubmitTime(rs.getString("submitTime")); ind.setConsignmentTime(rs.getString("ConsignmentTime")); ind.setTotalPrice(rs.getFloat("TotalPrice")); ind.setContent(rs.getString("content")); ind.setIPAddress(rs.getString("IpAddress")); if (rs.getInt("IsPayoff")==1) ind.setIsPayoff(false); else ind.setIsPayoff(true); if (rs.getInt("IsSales")==1) ind.setIsSales(false); else ind.setIsSales(true); my_indent.addElement(ind); } rs.close(); return true; } catch (SQLException e) { return false; } } |
(6)public boolean getOneIndent(long iid)
·基本功能:该函数负责从数据库中获取一条订单记录。
·参数意义:参数只有一个长整型数据,对应要获取订单记录的订单ID。
·返回结果:返回结果为布尔型,如果成功获取订单记录并增加到全局变量my_indent中时返回成功标识,否则返回失败FALSE。
·实现思路:该函数和上面讲述的getIndent(long userid)函数实现方法一致,所不同的是前者获取的结果可能包含多条订单记录,即用户对应的全部订单列表。而本函数返回的只有一条订单记录。请读者参考getIndent(long userid)函数来理解本函数的实现方法,这里不给出实现的代码。
(7)public boolean getIndent()
·基本功能:该函数负责获取全部订单记录。
·返回结果:返回结果为布尔型,如果成功获取订单记录并增加到全局变量my_indent中时返回成功标识,否则返回失败FALSE。
·实现思路:该函数还是获取订单记录的函数,和前面两个函数所不同的是,该函数获取的既不是属于某一个用户的记录信息,也不是某个唯一订单ID的记录信息。本函数获取的是截止到目前,系统中所有的订单记录。稍微需要注意的是,在函数中引入了分页机制,当订单数据比较多是,分页是显而易见的一种解决办法。本示例使用的是SQL Server数据库,需要注意分页语句的写法。这一点,在前面章节的模块封装中已经讲述过,读者可以参考前面的讲述加深对本函数的理解,分页函数类也可以直接使用第3章讲述的办法,感兴趣的读者可以尝试改进一下该函数。
(8)public boolean getIndentList(long nid)
·基本功能:该函数返回给定订单编码的全部订单信息。
·参数意义:函数只有一个参数,长整型数据nid为订单编号。
·返回结果:返回结果为布尔型,如果成功获取订单记录并增加到全局变量订单列表indent_list中时返回成功标识,否则返回失败FALSE。
·实现思路:该函数根据传递的参数获取对应的订单列表信息,把符合条件的数据循环添加到全局变量indent_list订单列表中。该函数的实现代码如程序9-7所示。
程序9-7 获取给定订单编码的全部订单信息
/** * 获取订单列表 * @param nid 订单ID * @return */ public boolean getIndentList(long nid) { sqlStr = "select * from indentlist where IndentNo = ?"; try { prepstmt = conn.prepareStatement(sqlStr); prepstmt.setLong(1, nid); rs = prepstmt.executeQuery(); indent_list = new Vector(); while (rs.next()) { indentlist identlist = new indentlist(); identlist.setId(rs.getLong("id")); identlist.setIndentNo(rs.getLong("IndentNo")); identlist.setBookNo(rs.getLong("BookNo")); identlist.setAmount(rs.getInt("Amount")); indent_list.addElement(identlist); } rs.close(); return true; } catch (SQLException e) { return false; } } |
(9)public boolean update(HttpServletRequest res)
·基本功能:该函数完成修改用户订单信息的操作。
·参数意义:该函数的参数只有一个HttpServletRequest对象,程序从Request中解析需要修改订单字段的相关信息。本示例中主要有两个字段需要修改,一个是该订单是否已经支付货款标识,一个是该订单是否还在销售的状态标识。
·返回结果:返回结果为布尔型,如果成功更新订单记录则返回成功标识,否则返回失败FALSE。
·实现思路:程序首先从HttpServletRequest对象中获取需要更新的相关字段信息,这里的参数有3个。分别为支付状态payoff、销售状态sales及订单标识indentid。该函数的实现代码如程序9-8所示。
程序9-8 修改修改用户订单信息的实现代码
/** * 更新订单 * @param res * @return */ public boolean update(HttpServletRequest res) { request = res; int payoff = 1; int sales = 1; long indentid =0; try { payoff = Integer.parseInt(request.getParameter("payoff")); sales = Integer.parseInt(request.getParameter("sales")); indentid = Long.parseLong(request.getParameter("indentid")); sqlStr = "update indent set IsPayoff = ?,IsSales=? where id =?"; prepstmt = conn.prepareStatement(sqlStr); prepstmt.setInt(1, payoff); prepstmt.setInt(2, sales); prepstmt.setLong(3, indentid); prepstmt.execute(); return true; } catch (Exception e) { return false; } } |
(10)public boolean delete(long id)
·基本功能:该函数会根据订单ID删除相应的用户订单记录。
·参数意义:该函数只有一个参数。长整型参数ID,即要删除的订单数据标识ID
·返回结果:返回结果为布尔型,如果成功删除订单记录则返回成功标识,否则返回失败标识FALSE。
·实现思路:该函数也是一个数据库操作函数,主要是从数据库用户订单列表中删除给定订单ID的信息,这里同时操作两张表。一张表用于存储用户的订单明细记录indent,一张表存储的是订单列表数据。在执行删除操作的函数时,这两个表中的记录需要同时删除。该函数的实现代码如程序9-9所示。
程序9-9 删除用户订单记录的实现代码
/** * 删除用户订单记录 * @param id * @return */ public boolean delete(long id) { try { sqlStr = "delete from indentlist where indentNo =" + id; stmt.execute(sqlStr); sqlStr = "delete from indent where id= " + id ; stmt.execute(sqlStr); return true; } catch (SQLException e) { return false; } } |
有关购物车操作类的更多详细代码和功能设置,读者可以参考本节的源代码。程序9-1的运行结果如图9-5所示。在该页面上可以设置要购买商品数量,同时还具有取消购买的功能和查看商品详细信息的链接。当点击“购买”后进入购物车清单界面,如图9-6所示。这里还可以进一步地修改购物数量、删除已经购买的物品及备注信息。
 |
| 图9-5 购物车界面 |
 |
| 图9-6 购物车的基本界面 |
【责任编辑:
杜书 TEL:(010)68476606】