• 1.10 MB
  • 2022-05-14 15:45:11 发布

在线手机销售系统—后台客户管理 毕业设计(论文)

  • 58页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
在线手机销售系统—后台客户管理目录摘要IIAbstractIII第1章绪论………………………………………………………………………5第2章管理端界面设计…………………………………………………………62.1管理端界面结构设计…………………………………………………………62.2界面头、尾设计………………………………………………………………62.3厂商管理模块…………………………………………………………………62.4商品(手机)维护模块………………………………………………………212.5会员维护模块………………………………………………………………382.6订单维护模块………………………………………………………………402.7查看发货模块………………………………………………………………472.8常见问题(FAQ)管理模块…………………………………………………492.9管理员维护模块……………………………………………………………492.10用户登录模块………………………………………………………………50第3章系统的扩展和移植……………………………………………………563.1系统的扩展…………………………………………………………………563.2系统的移植…………………………………………………………………563.3web.config文件的配置……………………………………………………57第4章小结………………………………………………………………………58第5章参考文献59第6章致谢60III 在线手机销售系统—后台客户管理摘要随着移动通信技术日新月异地发展,手机已经逐步成为人们日常生活中不可或缺的工具,多元化的销售模式更让手机市场的竞争变得尤为激烈。用户在消费购买手机产品之前有必要也有权利了解更多自己所关注产品的相关信息,手机以及其他数码产品的信息和行情应该受到广泛关注,包括价格、性能、使用体会等相关信息。因此,一个信息多元、交流方便的手机在线销售系统,在帮助消费者方便的了解更多更准的行情信息的同时,可以让用户便捷安全的在线购买喜爱的手机产品,让用户体验到电子商务给我们生活带来的快捷和便利。关键字:手机;导购;销售;ASPIII Onlinesalesofmobilephonesystems,backgroundmanagementAbstractAlongwiththemobilecommunicationtechnologyrapidlydevelopment,mobilephonehasgraduallybecameinthepeopledailylifeindispensabletools,diversifiedsalesmodeltomobilephonemarketcompetitionbecomesmoreintense.Usersintheconsumerpurchasemobilephoneproductsbeforeitisnecessarytohavetherighttoknowmoreoftheirconcernsandtherelativeinformationoftheproduct,mobilephoneandotherdigitalproductsmarketinformationandshouldbepaidcloseattentiontoextensively,includingprice,performance,theuseofrelevantinformation.Therefore,apluralityofcommunicationinformation,convenientonlinesalesofmobilephonesystems,tohelpconsumerstoeasilyunderstandthemoreaccuratemarketinformationatthesametime,canmaketheuserconvenientsafeonlinebuyfavoritemobilephoneproducts,allowuserstoexperiencetheelectroniccommercebroughttoourlifeconvenience.Keywords:mobilephonesales;ASP;guide;III 第一章绪论随着网络技术的日趋成熟,Internet变成了一种处理日常事务的交互式的环境。在互联网上开展各种服务已经成为许多企业和部门的急切需求。从传统行业到新兴的电子商务(ElectronicCommerce或ElectronicBusiness),基于Web技术的应用极大地改变着传统的服务模式。因此,Web己成为社会信息交流的一个平台,Web的普遍使用已经从根本上改变了人们的生活方式、工作方式,也改变了企业的经营方式和服务方式。越来越多的公司、企业以及政府希望通过利用国际互联网所提供的无地域、无国界、无时间限制的便利信息环境来开发自己的Web应用。基于数码产品的电子商务网站的设计融合了电子商务网站的购物特性和门户网站的个性化特性,成为最新网站技术的热点。对中小企业来说不可能有大量的资金投入构建电子商务网站。那么,如何充分利用现有资源、尽量减少专业技术人员的投入、对网站的维护和更新也不需要大量的人力、物力和财力已成为中小企业开展网上业务的关键。同时,中小企业的电子商务网站能否盈利,很大一部分因素将取决于其网站的个性化特性。因此对电子商务门户网站的研究将有助于我国中小企业开展网上商务。对促进电子商务在中国的快速发展有一定的实际意义。ASP(ActiveServerPages)是Microsoft提出的一种网络服务器端编程环境。ASP已经成为开发动态网站的重要而快速、有效的工具。ASP强大的功能使之成为一种优秀的服务器技术。随着网络技术的日益成熟,ASP技术在网络编程中也变得越来越重要。所以,在我们的毕业设计中,我们采用了ASP作为开发工具,构建了一个能实现简单的电子商务的小型动态商务网站——网上商品销售系统,我是负责系统的用户模块的设计。该系统能实现用户的注册、登录功能;能够实现商品的查询,订购等功能。该系统基本上具备一个网上商品销售系统应该具备的功能,该设计项目基本上体现了构建一个动态商务网站所需要的技术,可以说,目前的大型商务网站也就是我们这个小型网站在内容上的扩充和重复。在此次毕业设计中,本人所负责的系统则是进行数码产品网上商城系统的设计与开发,本系统是针对现在主要的物流中心问题,采用目前在网站开发中使用广泛的ASP技术,基于SQL55 Server数据库所开发的一套信息管理系统。本系统实现了商户在线对数据库进行管理等功能;界面友好,使用方便。在安全性方面,利用了目前广泛应用于网络站点系统中的md5加密技术,在实现系统功能的基础上最大限度的防止了非法窃取他人信息的行为的发生。通过此系统使商品信息更方便、高效,实现资源的共享以及自动化的管理,帮助网上商城有效的开展全面的信息化管理,尤其适合于网络销售信息化。55 第2章管理端界面设计客户端界面的大部分功能都是以管理端为前提的,因为客户端及显示的数据大部分需要通过管理端来添加。因此,我们首先来介绍管理端的设计与开发。管理端相对客户端要复杂一些,因为在这里需要对所有信息进行维护,包括浏览、增加、修改和删除等操作。前面已经提及,为了便于系统维护,页面的命名采用“模块名+功能名”的方式。概括起来,管理端具有以下功能。l厂商管理l商品(手机)维护。l会员维护。l订单维护。lFAQ(常见问题)管理。l管理员维护。在每个管理界面当中均直接或间接地采用<!-#includefile="checkuser.asp"-->方法引用了checkuser.asp文件,这种设计是为了检测管理员是否登录,防止没有管理权限的用户非法窜改系统数据。下面分别说明这些功能的实现方法,首先开始管理端界面结构设计。2.1管理端界面结构设计进行ASP应用程序开发的重要的一步是设计一个一般的界面结构,在这个结构确定了之后,就可以将不同的内容添加到这个框架中,这样既便于整个网站统一风格,也减少了界面设计的工作量。在本系统中的界面结构是由头(head.asp)、体(iframe)和尾(foot.asp)3部分组成的。由于使用了框架(iframe),于是只要将各个模块的页面装入iframe即可,并且在这些页面中无须包含头和尾的信息。页面结构在default.asp文件中定义,这个文件的代码如例程2-1所示(请注意其中的HTML注释)。例程2-1管理端页面结构default.asp<%ShowHeadAndMenu()%>55 从以上代码可以看到,这个页面非常简单,而且很清晰地分为3部分。每个模块的功能页面在名为“frmMain”的iframe中显示,下面来介绍界面头、尾的设计。2.2界面头、尾设计为了提高代码的重用性,我们把管理界面部分相同的头和尾做成两个文件分别命名尾head.asp和foot.asp。读者可以在附书光盘里本章源代码的admin/include文件夹下找到这两个文件。它们基本都是纯HTML代码,用法参加上面default.asp文件的代码。上述default.asp文件的运行效果如图1所示。图12.3厂商管理模块厂商管理模块的功能主要由以下几个文件来实现:l显示厂商列表:manuList.asp。l添加厂商页面:manuAdd.asp。l保存添加的厂商信息:manuAddSave.asp。l删除厂商:manuDel.asp。l保存厂商修改结果:manuModifySave.asp.55 厂商管理模块在所有管理模块中是比较简单的一个,同时也处于最基础的地位,所以我们先从这个模块开始系统的开发。就目前的需求,厂商信息中只要记录每一个手机厂商的名字即可。但这样想不免过于简单,客户的要求往往并不是一成不变的,也许,就在明天,他就要求厂商以下还可以分类,比如他可能要求按照诺基亚手机的不同系列将诺基亚这一厂商进一步分类,或者按GSM和CDMA来分类等。出于系统的伸缩性的考虑,在数据库设计时我们已经多加入了一个SuperId字段用于记录上级类型。现在,在添加分类(厂商)时,需要通过程序来使用户可以选择所添加分类的上级分类。树立了这样的思想,下面分别介绍这个模块中每个功能的实现方法。1.显示厂商列表厂商列表主要用到manuList.asp这个文件。图2为管理端厂商列表的运行结果,从图中可以看出,能够对厂商进行添加、修改和删除等操作,并且具有分页功能。图2此页的原理是每次访问此页时,都会从其URL参数中寻找一个名为“ID”的参数,它代表商品类型编号。然后在页面中显示这个ID的所有子类型,即在ProductType表中查找所有SuperId=ID的记录,然后以列表的方式显示出来。如果找不到此类型的话,则将ID的值设为“0”,即显示所有顶级类型。下面是实现这个功能的SQL语句:StrSQL="SELECT*FROMProductTypeWHERESuperID"&intID&"ORDERbyid"这条语句中的intID就是上述通过参数获得的ID,根据这样的思路,我们可以在manuList.asp文件中编写出类似例程2-2所示的代码。例程2-2显示厂商列表manuList.asp<%DimrsObj,rsSuper,strSQL55 Dimpage,thisUrl,i,bShowAdd,sAddPageDimintID,nSuperId,szSuperName,nID,szName"获取当前分类IdintID=RealString(Request.QueryString("id"))IfintID=""ThenintID="0""获取当前显示的页码page=RealString(Request.QueryString("page"))If(page=""OrIsEmpty(page))Thenpage=1thisUrl="manuList.asp?id="&intIDSession("adminOldUrl")=thisUrl&"&page="&pageSetrsObj=Server.CreateObject("ADODB.RecordSet")strSQL="SELECT*FROMProductTypeWHERE1=1ANDSuperID="&intID&"ORDERbyid""使用LockType和CursorType常量时必须在global.asa中用TypeLib声明ADOrsObj.OpenstrSQL,conn,adOpenKeyset,adLockReadOnlyrsObj.pagesize=conMaxPerPage"找它的父类,因为删除时需要传值(删除后返回父类),"返回父类"的链接也需要strSQL="SELECT*FROMProductTypeWHEREid="&intIDSetrsSuper=Server.CreateObject("ADODB.RecordSet")rsSuper.OpenstrSQL,conn,adOpenKeyset,adLockReadOnlyIfNotrsSuper.eofThennSuperId=rsSuper("SuperID")szSuperName=rsSuper("Name")ElsenSuperId=0EndIfrsSuper.Close()%>手机厂商管理
<%55 "如果父级ID不是0,即不是顶级类型,则显示修改名称功能ifintID<>"0"then%>>返回厂商列表">"><%endif%><%"如果从数据库中检索到数据,则以列表的方式示式ifnot(rsObj.eoforerr)then%>编号
厂商名称
修改
删除
<%rsObj.move(page-1)*conMaxPerPagei=1DoWhileNot(rsObj.EOFOrErr)55 nID=rsObj("id")nSuperId=rsObj("SuperId")szName=rsObj("Name")%><%=nID%>
><%=szName%>>修改&SuperID=<%=nSuperId%>"onclick="returnconfirm("确实要删除吗?");">删除<%i=i+1Ifi>conMaxPerPageThenExitDorsObj.MoveNext()loopendif"是否显示添加按钮bShowAdd=True"单击添加按钮后的转向页sAddPage="manuAdd.asp"%><%"手动关闭连接,将连接放入连接池rsObj.Close()SetrsObj=NothingCloseConn()%>在这个页面中,首先判断获取的ID是否是“0”,如果是,则表明所要显示的是顶级分类,此时只能以列表的方式显示,无法修改;当获取的ID非“0”时,则可以修改此ID对应的分类(厂商)名。接下来判断是否从数据库中检索到数据,如果检索到数据,则rsObj对象中的集合不为空,与是以列表方式显示检索到的厂商,并显示相应的修改、删除链接。55 在这个页面的后面,可以看到有如下语句:这句的作用是将一段实现分页显示的代码包含进来,因为分页显示的功能在每个模块中的列表页(如厂商列表、手机列表等)中都用到了,所以将其单独作为一个文件,以便于代码维护。例程2-3是这个文件的实现。例程2-3分页显示代码pageguide.aspfunctionjumpTo(i){if(i==1){this.document.location="<%=thisUrl%>";}if(i==2){this.document.location="<%=thisUrl%>&page=<%=page-1%>";}if(i==3){this.document.location="<%=thisUrl%>&page=<%=page+1%>";}if(i==4){this.document.location="<%=thisUrl%>&page=<%=rsObj.pageCount%>";}}<%ifbShowAddthen%>";"ID="Button1"><%endif%><%=rsObj.recordCount%>项,第<%=page%>/<%=rsObj.pageCount%>页 <%ifpage>1then%><%else%><%endififrsObj.recordCount>page*conMaxPerPagethen%>55 <%else%><%endif%>读者也许已经注意到了,这个文件实现的分页功能其实并不独立,它需要上文中的rsObj和bShowAdd等许多变量,但很容易将它改为一个比较通用的分页函数,不依赖于上下文。可以做一个过程或函数,将以上分页所需的变量通过参数传入,具体实现方法这里就不再详述了,读者可以参考本书前面“ASP开发必备知识”中所提供的分页函数。2.添加厂商添加厂商用的manuAdd.asp和manuAddSave.asp两个文件。图3为在厂商列表页面中单击“添加”按钮后所看到的添加厂商页面。从图中可以看到,添加厂商时,可以选择它的父类,因为这一功能现在尚未使用,所以默认设为“顶级类”,即所添加厂商的父类ID都是“0”.图3此页的原理是每次访问此页时,都会从数据库中读入现有的顶级分类,添加到下啦列表框中。其他的属于纯页面设计技巧,在此从略,读者可以参考manuAdd.asp这个文件。55 下面介绍如何将数据库中的顶级分类添加到下拉列表框中,参考例程2-4例程2-4将商品分类添加到下拉列表框manuAdd.asp(部分)后台管理

 

 

添加手机类别类名父类顶级类<%dimrsObj,strSQLdimtempNamedimisetrsObj=Server.CreateObject("ADODB.RecordSet")strSQL="SELECT*FROMProductTypeWHERESuperID=0ORDERbyid"rsObj.OpenstrSQL,conn,adOpenKeyset,adLockReadOnlyi=055 dowhilenot(rsObj.eoforerr)tempName=rsObj("name")Response.Write""&tempName&""ifi>100thenexitdoi=i+1rsObj.MoveNextlooprsObj.CloseSetrsObj=NothingCloseConn()%> 

 

在这段代码的学习中,大家要注意一下几个技巧的运用。1)避免字符串拼接一般的初学者都非常想了解类似上面这样将数据库中的数据添加到一个下拉列表框或者树形结构等“控件”中是如何实现的,其实原理很简单,只要用Response.Write语句来组织输出一个Select元素中的Option项即可。不过注意下面这条语句:Response.Write""&tempName&""如果把它改为下面的语句:……strTemp=strTemp+""&tempName&""……Response.WritestrTemp即先把所有要输出的内容存储到一个strTemp变量中,然后再输出。注意这样的做法是极不可取的!因为字符串的多次拼接会使运行效率大大下降,原因是每次给strTemp赋值时,都会重新分配这个变量的大小,这样的效率可想而知。关于避免字符串的拼接的详细说明请参见本书前面“创建高性能的ASP应用程序”中的内容。55 2)用Form提交数据的基本技术在这个页面中还有两处需要注意,第一处是Form的定义,参见下面的代码:另外一处是“添加”按钮的定义,参见如下代码:这两行HTML代码中,action="manuAddSave.asp"表示当此表单提交时,会将数据传送到manuAddSave.asp这个文件来处理,而提交动作无疑是由“添加”按钮来实现的,所以这里必须设置“添加”按钮type="submit"。下面就来看manuAddSave.asp文件的实现。它将manuAdd.asp文件所提交的数据写到数据库中。实现原理就是将表单中的数据库利用ADO对象或SQL语句写入到数据库中,此处为了演示Command对象的用法,是利用Command对象和RecordSet.AddNew方法来联合实现的。代码参考例程2-5例程2-5将表单数据保存到数据库中manuAddSave.asp<%dimcmbObj,rsObjSetcmbObj=Server.CreateObject("ADODB.Command")SetrsObj=Server.CreateObject("ADODB.RecordSet")cmbObj.CommandText="SELECT*FROMProductTypeWHERE(idisnull)"cmbObj.CommandType=adCmdTextSetcmbObj.ActiveConnection=connrsObj.OpencmbObj,,adOpenKeyset,adLockOptimisticrsObj.AddNewrsObj("name")=Request.Form("name")rsObj("SuperID")=Request.Form("SuperID")rsObj.UpdatersObj.ClosesetrsObj=nothingCloseConn()%>下面介绍一个使用数据库连接池的技巧,注意到在这个文件的最后,有如下的语句:<%"手动关闭连接,将连接放入连接池rsObj.Close()SetrsObj=NothingCloseConn()%>虽然把这几句去掉后程序仍可以正常运行,但事实上它们非常关键。在这几行代码中,首先是关闭55 和释放当前记录集,然后再调用CloseConn关闭连接,这样操作之后可以将刚才使用过的连接放到连接池中,供其他页面使用。3.修改厂商修改厂商用到文件manuList.asp(部分)和manuModifySave.asp。图4为修改厂商的运行界面。从图中可以看到,修改厂商似乎要比添加厂商简单一些,事实上也的确如此,所以在本系统中没有将它单独做成一页,而是将其放在厂商列表页中,即manuList.asp文件中,当通过URL参数传入的ID不是“0”时,将显示修改界面。图4与添加厂商页面类似,当单击“保存”按钮时,会将表单中的数据发送到其action属性所指向的文件,此处是manuModifySave.asp。但这时就产生了一个问题,那就是页面上只有一个厂商明珠,通过表单如何传送厂商的ID呢?下面来回答这个问题。让我们再来回顾一下显示厂商列表manuList.asp中的一小段代码:">">与manuAddSave.asp类似,在这个页面中,仍然要注意操作完数据库以后,需要手工释放连接。另外,这个页面也没有进行数据合法性检查,即检查所修改的厂商名称是否已经存在,或者是否有其他问题等,事实上这类问题在一个完善的系统中是必不可少的,这里留给读者去思考。4.删除厂商删除厂商用到的文件是manuDel.asp,厂商ID通过URL参数传入,因此首先要从Request的QueryString集合中取出厂商ID,并需要将其去除两边的空格和可能存在的单引号。然后,分别检查是否存在下级类型和此类所属的产品,如果不存在,则可以将此类型删除。这个文件的实现代码参考例程2-7例程2-7删除厂商实现代码manuDel.asp<%dimstrSQL,rsObj,rsProductdimintIDintID=RealString(Request.QueryString("id"))55 "如果参数非法,防止传入类似manuDel.asp?id=3or1=1ifnotIsNumeric(intID)thenResponse.Write""Response.Write"alert("您输入的数据不合法!");"Response.Write"window.history.go(-1);"Response.Write""Response.EndendifstrSQL="SELECT*FROMProductTypeWHERESuperID="&intIDsetrsObj=Server.CreateObject("ADODB.RecordSet")rsObj.OpenstrSQL,conn,adOpenKeyset,adLockReadOnlyifrsObj.eofthenstrSQL="SELECT*FROMProductWHEREProductType="&intIDsetrsProduct=Server.CreateObject("ADODB.RecordSet")rsProduct.openstrSQL,conn,1,1ifrsProduct.eofthenstrSQL="DELETEFROMProductTypeWHEREid="&intID"Response.WritestrSQLconn.Execute(strSQL)%><%else%><%endif%><%else%><%endif%>在实现此项功能时,有一个非常值得关注的安全问题,下面加以详细讨论。此页面中所要删除的厂商ID是由URL参数中传入的,这就给一些专门的“高手”留了可乘之机。调用这个文件的URL类似如下:……manudel>asp?id=2对于这样的链接,就会有人模仿输入:……manudel.asp?id=2or1=1这样,如果你原来有一个SQL语句是如下的话:strSQL="DELETEFROMProductTypeWHEREid="&intID接受这样的输入,你的SQL语句最终极有可能变成下面这样:DELETEFROMProductTypeWHEREid=2or1=1这条小小的语句就可以很恐怖第将你ProductType表中的数据一下子全部删除!当然,这是最坏的情况,在大多数情况下由于本页面其他代码的影响及数据库完整性的限制,情况并没有这么糟,比如这种情况在本页就基本不会发生。在这里提出的目的是要告诉大家,在接收通过URL参数传入的数据时,要格外小心,可以从以下几个方面来考虑安全情况。用户是否有权访问此页,如本例中包含了checkuser.asp文件,未登录的管理员无法访问此页。对获取的参数进行验证,如本例中根据实际情况知道输入的必定是数字,于是就用IsNumeric函数来判断输入是否合法。在其他应用中读者可根据具体情况判断,如只能是字符、不包含空格等。要注意,从Form中获取的值同样存在此问题,如经常会有人在登录文本框中做类似的文章。至此,关于厂商管理的功能就全部实现了,这是本章所介绍的第一个模块,所以介绍得很详细。期间谈到的许多ASP开发的小技巧,非常值得去反复理解和运用。下面,我们进入其他模块的学习和开发。2.4商品(手机)维护模块l商品维护模块的功能主要由以下几个文件来实现。l显示商品列表:productList.aspl添加商品页面:proAdd.aspl保存添加的商品信息:proAddSave.aspl删除商品:proDel.aspl修改商品界面:proModify.aspl保存商品修改结果:proModifySave.aspl上传图片界面:proAddPic.aspl保存上传图片:proaddPicSave.aspl推荐或取消推荐商品:proRecommend.asp1.显示商品列表商品列表主要用到productList.asp这个文件。图5为管理端商品列表的运行结果,可以看到它与厂55 商管理界面非常相似,这样便于网站管理人员使用。图5此页的原理是每次访问此页时,都会从Request对象的QueryString集合和Form集合中寻找一下3个参数。Page:当前页号,位于QueryString集合中,如果找不到,默认为“1”。myKeyword:过滤产品的关键字,位于Form集合中,如果找不到,默认为空。ProductType:产品类型编号,位于Form集合中,如果找不到,默认为“0”.然后根据myKeyword和ProductType的值来组织SQL语句从Product表中检索数据。组织过程如下面的代码所示。"组织SQL语句,注意这里WHERE1=1的妙用strSQL="SELECT*FROMproductWHERE1=1"ifnot(myKeyword=""orIsEmpty(myKeyword))thenstrSQL=strSQL&"ANDnamelike"%"&myKeyword&"%""endififProductType>0thenstrSQL=strSQL&"ANDProductType="&ProductTypeendifstrSQL=strSQL&"ORDERbyiddesc"这个SQL语句比较复杂,在第一行可以看到有WHERE1=1这样的代码,它使用得非常巧妙,这样,在下面添加myKeyword和ProductType条件时可以放心第在前面写“ANDname……”,而不必担心由于if语句的条件不成立所带来的SQL语法错误。另外,由于本页需要在下拉列表中显示所有的厂商名称,并且在商品列表中也有相应的列需要显示厂商名称,所以需要在检索Product表之前先从ProductType表中检索出所有的厂商编号和名称。根据这样的思路,我们可以在productList.asp文件中编写出类似例程2-8所示的代码,其中省略了部分与manuList.asp中类似的组织输出HTML的代码。例程2-8显示商品列表实现代码productList.asp(部分)55 <%"========================================================""将所有厂商添加到下拉列表框中"说明:这里只添加ProductType表中SuperId=0的记录"参数:"nTypeId:当前选种的厂商名称ID"szCtlName:下拉列表框的名称,在获取表单中的数时用"========================================================functionTypeToSelect(nTypeId,szCtlName)dimstrSQLdimrsTypedimtempidimnID,szNameResponse.Write""Response.Write"全部分类"strSQL="SELECT*FROMProductTypeWHERESuperID=0"setrsType=Server.CreateObject("ADODB.RecordSet")rsType.OpenstrSQL,conn,adOpenKeyset,adLockReadOnlyifrsType.EOFAndrsType.BOFthen"如果没有检索到产品类型Response.Write""rsType.Close()SetrsType=NothingexitfunctionelsedowhilenotrsType.eofnID=rsType("id")szName=rsType("name")Response.Write""Response.WriteszName&""rsType.MoveNextloopendifResponse.Write""rsType.Close()SetrsType=Nothing55 endfunctiondimProductTypeDic,strSQL,rsObj,rsTypedimthisUrl"文件从此处开始执行,上面只是函数定义和变量定义"将商品类型编号和名称加入到ProductTypeDic备用SetProductTypeDic=Server.CreateObject("Scripting.Dictionary")strSQL="SELECT*FROMProductType"setrsType=conn.Execute(strSQL)dimtempID,tempNamedowhilenot(rsType.eoforerr)tempID=CStr(rsType("id"))tempName=rsType("Name")ProductTypeDic.AddtempID,tempNamersType.MoveNextloop"关闭并释放记录集,但不要关闭连接,因为下面还要用到rsType.Close()SetrsType=Nothingdimpage,myKeyword,ProductType"获取查询关键词myKeyword=RealString(Request.Form("myKeyword"))"获取所显示的手机厂商ProductType=RealString(Request.Form("ProductType"))ifProductType=""thenProductType="0""获取所示的页号page=Request.QueryString("page")if(page=""orIsEmpty(page))thenpage=1"thisUrl为分页用thisUrl="productList.asp?ProductType="&ProductType&"&myKeyword="&myKeywordSession("adminOldUrl")=thisUrl&"&page="&page"组织SQL语句,注意这里WHERE1=1的妙用strSQL="SELECT*FROMproductWHERE1=1"ifnot(myKeyword=""orIsEmpty(myKeyword))thenstrSQL=strSQL&"ANDnamelike"%"&myKeyword&"%""endififProductType>0thenstrSQL=strSQL&"ANDProductType="&ProductTypeendif55 strSQL=strSQL&"ORDERbyiddesc"setrsObj=Server.CreateObject("ADODB.RecordSet")rsObj.OpenstrSQL,conn,adOpenKeyset,adLockReadOnlyrsObj.pagesize=conMaxPerPage%>后台管理手机列表<%callTypeToSelect(ProductType,"ProductType")%>55 编号手机名称手机类别会员价(¥)市场价(¥)订购量手机图片删除<%dimnID,idimbShowAdd,sAddPagei=1ifnot(rsObj.eoforerr)thenrsObj.move(page-1)*conMaxPerPagedowhilenot(rsObj.eoforerr)nID=rsObj("id")%><%=nID%> ""><%=rsObj("name")%> 55 <%ifrsObj("recommend")=1then%>)">√<%else%>)">×<%endif%><%Response.WriteProductTypeDic.item(cstr(rsObj("ProductType")))"Response.WritersObj("ProductType")%><%=rsObj("memberPrice")%> <%=rsObj("marketPrice")%> <%=rsObj("buyNum")%><%ifrsObj("smallImg")="nothing"then%>","addPicture",400,80)"align="absmiddle"><%else%>","addPicture",400,80)"align="absmiddle"><%endif%> <%ifrsObj("bigImg")="nothing"then%>","addPicture",400,80)"align="absmiddle"><%else%>","addPicture",400,80)"align="absmiddle"><%endif%>55 )">删除<%i=i+1ifi>conMaxPerPagethenexitdorsObj.MoveNextloop%><%bShowAdd=TruesAddPage="proAdd.asp"%><%rsObj.Close()SetrsObj=NothingCloseConn()%>在这段代码的学习中,大家要注意以下几个技巧的运用。1)将通用的功能携程简单函数在页面的开头,TypeToSelect函数的作用是将数据库中的厂商添加到下拉列表框中。大家应该记得在添加厂商时也用到了这个功能,不过在这里将这个功能封装成了一个函数,在使用时只有如下调用即可:<%callTypeToSelect(nTypeId,szCtrlName)%>其中通过第一个参数nTypeId传入厂商编号,设定显示的是所选中的厂商:通过第二个参数szCtrlName以字符串的形式传入表单中SELECT元素的名称(name属性),在从Request对象的Form集合中获取参数时使用。如在本页中,获取用户在下拉列表框中选中的商品类型编号用下面的语句:"获取用户选中的厂商对应的厂商编号ProductType=RealString(Request.Form("ProductType"))这时也许就会有人问,使用Request.Form和Request.QueryString有什么区别?由这个问题,可以引出下面这个技巧。2)合理使用Request对象许多初学者对常见的用Request对象获取参数时所使用的以下3种方法的区别不太明白。lRequest.QueryString("var")l55 Request.Form("var")lRequest("var")简单来说,Request.QueryString用于获取URL后面带的参数,如:product.asp?id=40;Request.Form可以用来获取表单中的参数,但此时表单的Method属性必须设为“Post”,关于表单Method属性设为“Post”和“Get”的区别,请参见下一个技巧。对于第三种方式,用Request("var")这种非限定性的方式来获取参数,虽然可以省略集合名,直接在QueryString和Form等集合中获取变量值,但微软及其他专业人士强烈反对这种做法,因为这会使查询效率降低,而且还会产生由于变量重名而带来的让人费神的逻辑错误。事实上,Request对象一共有5个集合,当使用非限定性的方式查找参数时,会依次从这5个集合中查找,然后返回第一个查找到的结果。这5个集合的查找顺序是:lQueryStringlFormlCookieslClientCertificatelServerVariables可以看到,虽然QueryString和Form排的比较靠前,对效率的影响也许并不很严重,但养成一个良好的编程习惯对开发和维护都非常有好处。关于Request的详细使用,请参见本书前面“ASP开发必备知识”和“创建高性能的ASP应用程序”两章中的相关内容。3)Form中Method属性取值为“Get”和“Post”的区别二者的区别如下。lGet:Form中的数据会追加到action属性所指定的URL后面,此时只能从QueryString集合中查找参数,用Request.Form将找不到结果。lPost:Form中的数据不会追加到action属性所指定的URL后面,此时原URL后面本身的参数仍用QueryString获取,Form中的数据用Request.Form来获取。最后要注意,在Form中一般需要有一个type="submit"的input元素来提交表单数据。2.添加商品添加商品用到proAdd.asp和ProAddSave.asp两个文件。图6在商品列表页面中单击“添加”按钮后所看到的添加商品页面。从图中可以看到,添加商品时,仍需用一个下拉列表框指定它所属的厂商,显然,我们刚才所做的TypeToSelect函数可以派上用场了。55 图6在前面介绍的知识基础之上,此页面实现起来非常容易,首先调用刚才做好的TypeToSelect函数显示厂商列表框,然后剩下的纯属页面设计的任务了。不过这里一定要对输入进行验证,包括输入是否为空,所输入的价格必须是数字,以及输入长度的限制等。由于这些是纯HTML和JavaScript技巧,这里从略。在添加商品界面中单击“添加”按钮后,就会将数据提交到proAddSave.asp文件中,下面来看这个文件的实现。这个文件将proAdd.asp文件所提交的数据写到数据库中。实现原理与添加厂商很相似,就是将一个表单中的数据利用ADO对象或SQL语句写入到数据库中,不过由于数据库较大,此处就变得复杂多了。代码参考例程2-9.例程2-9将添加的商品数据保存到数据库中proAddSave.asp<%DimstrSQL,cmdObj,rsObjDimnID,nProductType,nMemberPrice,nMarketPrice"获取表单数据nID=RealString(Request.Form("id"))nProductType=Cint(Request.Form("ProductType"))nMemberPrice=Request.Form("memberPrice")nMarketPrice=Request.Form("marketPrice")ifnotIsNumeric(nMemberPrice)thennMemberPrice="1"ifnotIsNumeric(nMarketPrice)thennMarketPrice="1""创建对象SetcmdObj=Server.CreateObject("ADODB.Command")SetrsObj=Server.CreateObject("ADODB.RecordSet")cmdObj.CommandText="SELECTtop1*FROMproductWHEREId=null""ORDERbyiddesc"55 cmdObj.CommandType=adCmdTextcmdObj.ActiveConnection=conncallrsObj.Open(cmdObj,,adOpenKeyset,adLockOptimistic)"添加数据rsObj.AddNew()rsObj("ProductType")=nProductTypersObj("name")=Request.Form("name")rsObj("memberPrice")=nMemberPricersObj("marketPrice")=nMarketPricersObj("introduce")=Request.Form("introduce")rsObj("remark")=Request.Form("remark")"更新数据库rsObj.Update()"关闭连接,将连接放入连接池rsObj.ClosesetrsObj=nothingCloseConn()%>在这个页面中,仍然要注意操作完数据库以后,要手工释放连接。另外,这里只进行了简单的数据验证,如果提交的价格不是数字的话,将默认设置为1,而不能直接借用CInt转为“0”,否则在将来计算商品打折时由于分母为“0”会出错。更详细的验证清读者自行完成。2.修改商品修改商品用到文件proModify.asp和proModifySave.asp。图7为修改商品的运行界面。从图中可以看到,修改商品与添加商品的界面非常相似,事实上也的确如此,所不同的是,在proModify.asp文件中奖检索到的数据填入各表单元素中,而在proAdd.asp中根本就不存在检索商品数据的问题。55 图7例程2-10是修改商品页面的实现代码例程2-10修改商品页面代码proModify.asp<%dimrsObj,strSQLdimnID,nTypeId,szName,MemberPrice,MarketPrice,szIntro,szRemark"获取要修改的手机IDnID=RealString(Request.QueryString("id"))"组织SQL语句strSQL="SELECT*FROMproductWHEREid="&nID"查询结果,因为只是读取结果,所以用默认的CursorType和LockType即可setrsObj=conn.Execute(strSQL)ifrsObj.eoforerrthenResponse.Write"有错误发生,该手机可能已被删除,返回"Response.End()endifnTypeId=rsObj("ProductType")szName=rsObj("Name")MemberPrice=rsObj("MemberPrice")MarketPrice=rsObj("MarketPrice")szIntro=rsObj("Introduce")szRemark=rsObj("Remark")"关闭记录集,释放内存rsObj.Close()55 SetrsObj=Nothing"此时还不能关闭连接,因为下面用到了TypeToSelect函数%>手机修改
修改手机手机类别<%callTypeToSelect(nTypeId,"ProductType")"尽早关闭连接,将连接放入连接池CloseConn()%>">55 产品名称">会员价">¥市场价">¥产品简介<%=szIntro%>产品说明<%=szRemark%>55 ";">在这段代码的学习中,大家要注意一下几个技巧的运用。1)尽量利用客户端浏览器验证在这个页面中,注意下面的代码:其中onSubmit="returncheckForm()"的意思就是当提交这个表单时,将触发checkForm()函数,并根据这个函数的返回值决定是否提交表单。这些过程都是在客户端完成的,也就是说是在向服务器传送数据之前,因此,通常在这里放置一些输入验证的内容,因为在客户端利用浏览器验证要比在服务器验证速度快得多,同时也可以大大减轻服务器的负担。但这并不意味着服务器端可以不就行验证,为了防止有人故意绕过客户端验证而企图对服务器上的数据进行恶意破坏或窥探,在服务器端仍要进行必要的验证,记住只是必要的验证,否则便失去了客户端验证的意义。2)合理的页面结构设计在一个页面中,如果ASP脚本代码与HTML页面代码混杂在一起,不仅极不方便将来的代码维护,而且会导致页面执行效率大大下降。合理的解决办法是采用Include语句将其保护进来,或者将ASp脚本代码放在一页的最前面,在HTML代码中只需要用<%=var%>引用ASP脚本的执行结果即可。这点在上面的代码中体现得非常好,在页面开始部分,根据参数传入的商品ID检索数据库,读出商品信息,并将其存储到VBScript局部变量中,然后在下面的HTML代码中用<%=var&>将其输出,也即将商品信息显示在文本框中。接下来的保存修改结果文件proModifySave.asp实现起来与前面几个同类文件原理完全一样,就不在这里介绍了,读者可以参考附书光盘中的proModifySave.asp文件自行完成。2.删除商品删除商品用到的文件是proDel.asp,商品ID通过URL参数传入,因此首先要从Request的QueryString集合中取出商品ID,并需要将其两边的空格和可能存在的单引号去除,然后组织SQL语句删除。SQL语句的组织过程如下。strSQL="DELETEFROMProductWHEREid"="&nID这里的原理很简单,但仍然要注意的是安全问题,在用Request.QueryString获取参数id后,必须对其合法性进行判断。具体办法参见删除厂商部分。现在,关于商品管理的功能并没有全部完成,下面来介绍上传商品图片功能的实现。3.上传商品图片上传图片用到的文件是proAddPic.asp和proAddPicSave.asp,在商品列表中单击上传图片的链接即可弹出上传图片的窗口,如图8所示。55 图8此页的原理是每次访问此页时,会从Request对象的QueryString集合中寻找以下两个参数。lid:需要上传图片的商品编号。lType:上传的图片类型,分为大图片和小图片两种,取值分别是“bigImg”和“smallImg”。然后在hidden区域中保存它们的值,如下面的代码所示。">">其中input元素的type="file"表示是一个上传文件文本框,另外,出于简单考虑,这里没有对获取的参数id和type进行验证,而是直接将它们的值写到了hidden域,不过我们在proAddPicSave.asp文件中对其进行了最终验证。接下来有一个非常有用的技巧,也是许多ASP开发者都很关心的问题,就是如何上传文件,尤其是图片。上传文件与其他向服务器提交数据的原理一样,分位两部分,一部分是表单,另一部分是表单处理程序。对于表单需要设置其enctype属性为“multipart/form-data”,另外,表单中至少要有两个元素,分别是type等于“file”和“submit”的input元素。一个最简单的上传表单如下。另一部分就是需要在其处理程序中分析表单数据,将所上传的文件数据读出,过程既复杂有繁琐。事实上也不必担心,因为有许多第三方的上传组件或上传类,在本系统中使用的是化境无组件上传类,大家可以从化境在线:http://www.5xsoft.com免费下载,或者在那里寻找更多资源。文件数据提交后,需要由proAddPicSave文件处理。下面讲解如何将上传的文件保存到服务器硬盘,这个过程我们用到了化境无组件上传类V2.0,文件名为upload_5xsoft.inc,例程2-11是ProaddPicSave.asp的实现代码。例程2-11保存上传的文件proAddPicSave.asp<%dimuploadObj,szPath,fileObj,szFileName,tmpdimstrSQLdimnID,szType55 setuploadObj=newupload_5xSoftsetfileObj=uploadObj.File("filePic")"与上传表单中的file1相对应nID=uploadObj.Form("id")szType=uploadObj.Form("type")"判断参数是否正确ifnID=""orszType=""thenResponse.Write"有错误发生,无法获取上传参数
"Response.Write"[关闭]"Response.End()endifszPath="../"&szType&"/"iffileObj.FileSize>0then"如果FileSize>0说明有文件数据tmp=fileObj.FileName"以nID+原文件扩展名重新命名上传文件szFileName=nID&right(tmp,(len(tmp)-InStrRev(tmp,".")+1))Response.WriteszFileNamefileObj.SaveAsServer.MapPath(szPath&szFileName)"保存文件endifstrSQL="UPDATEproductSET"&szType&"=""&szFileName&""WHEREid="&nIDconn.Execute(strSQL)"释放对象,关闭连接setfileObj=nothingCloseConn()%>在这个文件中,首先将上传的文件通过上传类保存到硬盘上,然后更新数据库,并且要记得最后释放内存并关闭连接。2.设置商品的推荐状态设置商品的推荐状态用到的文件是proRecommend.asp。在商品列表中单击推荐列的图片,就可以将对应行的商品在推荐和不推荐两种状态之间切换。这个文件实现起来也比较简单,商品为推荐状态时,数据库中Product表的Recommend值为1,当不上推荐状态时,它的值为0.于是,有一个很巧妙的办法,用一条SQL语句就可以自动将它在推荐与不推荐状态间切换,而不是管它原来是什么状态。这条SQL语句如下。strSQL="UPDATEProductSETRecommend=(1-Recommend),"strSQL=strSQL+"RecommendDate""&Now()&""WHEREid="&nID"这里为了阅读方便将它写成两句,在实际的代码中,最好将它写成一句,即尽量减少字符串的拼接。proRecommend.asp文件的实现请参考例程2-12.55 例程2-12设置商品的推荐状态proRecommend.asp<%dimstrSQLdimnIDnID=RealString(Request.QueryString("id"))"如果参数非法,防止传入类似manuDel.asp?id=3or1=1ifnotIsNumeric(nID)thenResponse.Write""Response.Write"alert("您输入的数据不合法!");"Response.Write"window.history.go(-1);"Response.Write""Response.Endendif"组织SQL语句strSQL="UPDATEProductSETRecommend=(1-Recommend),"strSQL=strSQL+"RecommendDate=""&Now()&""WHEREid="&nID"执行SQL语句conn.execute(strSQL)CloseConn()%>2.5会员维护模块会员维护模块的功能主要由以下几个文件来实现。l显示会员列表:memberList.asp。l删除会员:memDel.asp。l修改会员:memModify.asp。l保存会员修改结果:memModifySave.asp。从文件数目可以看出来,会员维护模块相对比较简单,因为它没有添加功能,会员的添加是在客户端,通过注册页面来完成的。而其他的管理端功能,包括列表、删除和修改都与商品维护类似,所以下面只做简要介绍。图9是会员列表的显示界面,从图中可以看出,这个界面与厂商管理、商品维护风格完全一致,也具有搜索、修改和分页功能。55 图9由以上功能决定了memberList.asp页面也需要以下两个参数来控制页面的显示。lpage:当前页号,位于QueryString集合中,如果找不到,默认为“1”。lmyKeyword:过滤会员的关键字,位于Form集合中,如果找不到,默认为空。然后根据myKeyword的值来组织SQL语句从Member表中检索数据。组织过程如下面的代码所示。strSQL="SELECT*FROMproductWHERE1=1"ifnot(myKeyword=""orIsEmpty(myKeyword))thenstrSQL=strSQL&"ANDnamelike"%"&myKeyword&"%""endifstrSQL=strSQL&"ORDERbyMemberid"其他市县方法与商品列表类似,不再赘述,读者可以参考附书光盘中的memberList.asp文件来实现。图10为修改会员信息界面,选中图中的“修改密码”复选框后,修改密码功能才被激活,否则只会修改一般信息,密码保存不变。图1055 要注意在这个页面中,首先要判断复选框是否选中,然后再判断两次输入密码是否一致。关于如何判断复选框是否被选中,涉及到表单元素值传递的方式,于是又引出了另一个技巧,就是关于表单元素value属性的用法。表单是向服务器提交数据的一种有效手段,可以把表单看做一个容器,它里面有不同种类的元素。Input元素可以向服务器提交数据,可以通过它的type属性来设置它的显示类型,如常用的text、password、submit、button、reset和hidden,不管type属性如何设置,都可以给input元素指定它的value属性,然后在提交表单时,这个value属性就会被提交到服务器。下面介绍另外几个稍复杂一些的属性或元素。lInput,type=checkbox:如果它被选中,那么向服务器提交的并不是“Selected”或者“True”之类的值,而仍是其value属性的值,其value属性与input元素的其他类型一样,可以是任一个字符串;如果没被选中,则提交一个空字符串(或者不提交)。这点对于习惯VB或其他高级语言编程的朋友要注意。lInput,type=radio:与checkbox类似,当它被选中时,以字符串形式提交它的value属性值,如果没被选中,则不提交或者提交空值。lselect:是一个下拉列表框,它的特点与其他高级语言中的组合框类似,每一个选项有value和text(为适应大多数人习惯,姑且这么称吧,就是显示的文字),它向服务器提交当前选择项的value属性的值。会员修改页面和保存修改结果页面分别由嚜mModify.asp和memModifySave.asp文件来完成,具体的实现方法与前面界面的其他模块的修改功能很类似,读者可以参考附书光盘中的这两个文件来完成。2.6订单维护模块订单维护模块的功能主要由以下几个文件来实现。l显示订单列表:orderList.asp。l删除订单:orderDel.asp。l处理订单:orderProcess.asp。l保存订单处理结果:orderProcessSave.asp。订单尾货模块是本系统的本质所在,本系统的目的就是用来销售,自然最感兴趣的就是订单。但实际上订单维护模块从技术实现角度讲并不复杂,与会员维护类似,只有列表、删除与修改(处理)功能。所以下面对技术实现只做简要介绍,重点帮助读者理解其中的业务流程。1.订单列表图11是订单列表的显示界面,从图中可以看出,下订单的客户可以是会员,也可以是非会员,如果是会员,则显示其姓名,单击名字可以查看会员资料。图1155 显然,对于这样的界面,只需要一个page参数就够了,其余的工作只是讲数据从数据库中读出来,然后按照前面介绍过的方法以列表形式显示。不过仍有以下几点需要注意。1)只选取未处理订单数据库中的订单处理状态标志是state,当state=0时是未处理订单,于是选择未处理订单的SQL语句为:strSQL="SELECT*FROMOrderListWHEREstate=0ORDERbyiddesc"2)对非会员的处理如果是注册会员,在订单列表中显示会员名,并且有链接显示相应的会员信息,而如果是非会员,在订单列表中则显示“非会员”,且没有链接。是否是会员是由OrderList表中的字段memberID来区分的,如果memberID="0",则表示非会员。下面是显示会员与非会员的代码:IfrsObj("memberID")<>"()"then"非会员Response.WritersObj("memberID")elseResponse.Write"非会员"Endif其他具体实现请参考附书光盘中的orderList.asp文件,下面介绍订单处理过程。1.订单处理图12为订单处理界面,从图中可以看出,订单处理界面的上方显示订单详细资料,接下来是订货明细,最后是相关操作,可以删除或者发货,或者留做以后处理。图12此页面的实现原理是首先从QueryString集合中查找名为“id”的参数,这个是待处理的订单编号,然后根据次订单首先列出订单详细资料,在此之后是这个订单所包含的商品清单,最后给出这个订单的55 总价格。这里涉及两个数据表,与某一个订单相关的顾客信息存放在OrderList表中,从此表检索订单详细资料的SQL语句如下:strSQL"SELECT*FROMOrderListWHEREid"&nOrderId其中nOrderId是传入的订单ID。接下来的订单明细稍复杂一些,首先要明白一件事,那就是这里所说的一个订单,即OrderList表中的一条记录,可以包含多个商品,而客户对每个商品的购买量可能不止一个。在本系统中,将客户对每一种商品的购买关系和购买量存放在OrderDetail表中,作为其中的一条记录,二者靠订单号相联系。换句话说,一个订单,在OrderList表中是一条记录,然后在OrderDetail表中有多条记录存储对不同商品的购买量。明白了这个关系之后,下面来看显示订单明细及计算订单应付总款项的代码,如例程2-13所示。例程2-13显示订单明细及计算订单应付总款项orderProcess.asp(部分)<%dimrsObj,strSQLdimnOrderIdsetrsObj=Server.CreateObject("ADODB.RecordSet")nOrderId=RealString(Request.QueryString("id"))strSQL="SELECT*FROMOrderListWHEREid="&nOrderIdrsObj.OpenstrSQL,conn,adOpenKeyset,adLockReadOnly%>查看订单查看订单a订单详细资料……订单号<%=rsObj("id")%>客户帐号<%=rsObj("memberID")%> 收货人姓名<%=rsObj("customerName")%> 收货人地址<%=rsObj("address")%> 邮政编码<%=rsObj("Zipcode")%> 联系电话55 <%=rsObj("phone")%> 电子邮件<%=rsObj("email")%> 付款方式<%=rsObj("payment")%> 备注<%=rsObj("remark")%> 订货日期<%=rsObj("createDate")%> a订货手机明细……手机编号手机名称手机价格55 手机数量金额小计<%dimnSinglePrice,nOrderPrice,nTotalPrice,nQuantitynSinglePrice=0"单件商品的价格nQuantity=0"单件商品的购买数量nOrderPrice=0"购买某件商品的总价格(因为一件商品可能买多件),=单价X购买量nTotalPrice=0"购买所有商品的总价格"订单明细strSQL="SELECT*FROMorderDetailWHEREorderID="&nOrderIDSetrsObj=conn.Execute(strSQL)dowhile(notrsObj.eoforerr)nSinglePrice=rsObj("price")nQuantity=rsObj("Quantity")ifnSinglePrice<>""ANDnQuantity<>""thennOrderPrice=nSinglePrice*nQuantitynTotalPrice=nTotalPrice+nOrderPriceendif%><%=rsObj("productID")%> <%=rsObj("productName")%> <%=nSinglePrice%> <%=nQuantity%> <%=nOrderPrice%> <%rsObj.MoveNextLoop55 rsObj.Close()SetrsObj=NothingCloseConn()%>总计金额:<%=nTotalPrice%>元   发货备注已发货"> "">

接下来可以进行的操作有删除和发货。删除在前面几个模块中我们已经做了很多遍了,原理相似。发货操作由orderProcessSave.asp处理完成,基本原理是对数据库的更新,将OrderList表中的state字段更新为“1”,表示订单已处理,此外还有一些其他附带的更新信息,如记录发货时间、附带说明等。orderProcessSave.asp的代码如例程2-14所示。例程2-14发货处理orderProcessSave.asp55 <%dimstrSQL,cmdObj,rsObjdimnIDnID=RealString(Request.Form("id"))SetcmdObj=Server.CreateObject("ADODB.Command")SetrsObj=Server.CreateObject("ADODB.RecordSet")cmdObj.CommandText="SELECT*FROMOrderListWHEREid="&nIDcmdObj.CommandType=adCmdTextSetcmdObj.ActiveConnection=conn"查找待修改的记录rsObj.OpencmdObj,,adOpenKeyset,adLockOptimisticrsObj("state")=1"设置为已处理rsObj("treatedDate")=now()"处理日期rsObj("treatedRemark")=Request.Form("treatedRemark")"处理说明rsObj.Update"关闭连接,将连接放放连接池rsObj.ClosesetrsObj=nothingsetcmdObj=nothingCloseConn()%>2.7查看发货模块查看发货模块的功能主义由以下几个文件来实现。l显示发货列表:treatedList.asp。l查看已发货订单详细信息:treatedView.asp。订单处理完毕后,便自动进入到已发货的行列里,可以通过查看发货模块来浏览,以及查看详细信息。考虑到安全性和实际的实用价值,本部分没有做删除功能,读者如果由兴趣可以参考前面几个模块的删除功能自己来完成。图13是已发货的订单浏览界面。很明显,它与订单列表非常相似,只是检索记录的标准不同,这里选择的是已处理的订单。55 图13选择已处理订单的SQL语句如下所示。strSQL="SELECT*FROMOrderListWHEREstate=1ORDERbyiddesc"此页的具体实现过程请参见treatedList.asp文件。图14是查看已处理订单的详细信息。图14查看已处理订单页面其实就是订单处理页面的一个子集,把订单处理页面去掉删除和发货等操作,然后在所显示的信息中稍加修改即可,读者可以参考treatedView.asp这个文件来比较和学习。查看发货模块非常简单,所以我们介绍得比较简略,具体实现过程,相信读者在前面的基础之上一定可以轻松完成。下面的几个模块都与商品或订单没有什么直接关系,但也是一个在线销售系统所必不可少的。下面首先介绍在线帮助模块,即常见问题模块。55 2.8常见问题(FAQ)管理模块l常见问题列表:faqList.asp.l添加常见问题:faqAdd.asp。l保存添加的常见问题信息:faqAddSave.asp。l删除常见问题:faqDel.asp。l修改常见问题:faqModify.asp。l保存常见问题修改结果:faqModifySave.asp。从文件结构可以看出,常见问题管理模块的功能与商品维护模块类似,也比较完成,包括添加、修改和删除3种功能。图15是常见问题列表,从这个界面可以执行添加、修改和删除全部操作。图15单击常见问题的标题可以对其内容进行修改,与商品维护中相应功能的原理完全相同,在此从略,读者可以参考附书光盘中的上述相关文件来学习。2.9管理员维护模块管理员维护模块的功能主要由以下几个文件来实现。l显示管理员列表:userList.asp。l添加管理员:userAdd.asp。l保存添加的管理员信息:userAddSave.asp。l删除管理员:useDel.asp。l修改管理员:userModify.asp。l保存管理员修改结果:userModifySave.asp。从文件结构可以看出,管理员模块的功能也比较完整,包括添加、修改和删除。不过在管理员维护模块的实现过程中有以下几点需要注意。l55 默认管理员admin不可以删除。l添加和修改管理员时要验证两次输入密码是否一致。l管理员账号是以文本格式存储的,因此要手工判断不可重名。这里对管理员已经非常简化,一个安全性要求很高的系统,对管理员需要以组或角色来分类,然后还要有详细的权限定义字典,在权限定义字典中规定了各项功能与用户角色之间的对应关系。太详细的讨论已经超过本章的要求,读者可以参考本书其他案例或其他资料来学习。2.10用户登录模块用户登录模块需要以下几个文件的支持。l登录页面:login.asp。l登录基础函数:adminbase.asp。l注销页面:loginout.asp。用户登录模块是管理员进入后台管理系统所看到的第一个页面,但放在这里讲的目的是因为登录功能是针对最后使用而设计的,在前期开发调试阶段,登录功能往往让你心乱如麻,因为在你刚编写完一个页面准备调试时,却发现需要登录,问题不仅仅是登录过程会浪费一定的时间,更重要的是会干扰开发者的思路,导致一些更隐形的效率下降。1.登录页面图16为管理端登录页面的运行结果,相信对这个页面功能无须做过多解释。图16此页面并不是像其他模块中的页面那样通过框架嵌入到default.asp页中,而是在login.asp文件中包含head.asp和foot.asp两个文件。原因很简单,因为菜单是包含在default.asp上方的head区的,如果login.asp嵌入到default.asp的框架中的话,无法正确控制上方操作菜单的显示。例程2-15是登录页面login.asp的实现代码。例程2-15登录页面login.asp<%dimstrUserId,strPwdstrUserId=RealString(Request.Form("userID"))strPwd=RealString(Request.Form("password"))ifstrUserId<>""AndstrPwd<>""then55 "用户名和密码都不为空,则进行登录验证callCheckAdminLogin(strUserId,strPwd)endif%><%ShowHead()%><%callShowAdminLogin()%>55 很显然,这个页面与default.asp页面的结构很相似,只是把ShowHeadAndMenu()换成了ShowHead(),并把Default.asp页面中的iframe部分换成了如下代码。<%callShowAdminLogin()%>ShowAdminLogin函数用来显示登录表单,将这个函数放在adminbase.asp文件中的目的是为了减少login.asp文件的代码量,这样可以单独用Dreamweaver之类的工具来设计界面,然后只要用类似的函数调用或文件包含等方式就爱那个功能代码加入到设计好的页面结构中即可,从而使得页面结构清晰,便于维护和更新。注意到在这个页面中用到了一段客户端JavaScript,代码很少,只有如下几句:if(self!=top){top.location=self.location;alert("您没有登录或登录超时,请重新登录!");}它的作用是可以保证login.asp页面始终位于浏览器中的顶层,而不会被嵌在框架中。在本系统中用于防治管理员因超时等原因需要重新登录时,出现困境嵌套的情况,如果没这段JavaScript代码,会出现如图17所示的情形。图17在login.asp文件的头部调用的CheckAdminLogin过程也位于adminbase.asp文件中,它的作用是对以参数形式传入的用户名和密码进行校验。如果正确,则转向main.asp,进入后台管理系统,反之则给出错误提示。Adminbase.asp文件中CheckAdminLogin过程的代码如例程2-16所示。例程2-16CheckAdminLoin过程adminbase.asp(部分)<%"================================================="过程名:CheckAdminLogin"作用:管理员登录"参数:55 "strUserId:从表单传来的用户名原文(未加密)"strPwd:从表单传来的用户密码原文(未加密)"=================================================SubCheckAdminLogin(strUserId,strPwd)dimstrSQL,rsObjifstrUserId<>""thenstrSQL="SELECT*FROMAdminsWHEREAccount=""&strUserId&"""setrsObj=conn.Execute(strSQL)ifnot(rsObj.eoforerr)thenifstrPwd=rsObj("Pwd")thenSession("AdminAccount")=rsObj("Account")Session("userClass")=1"关闭记录集合和数据库连接rsObj.CloseSetrsObj=NothingCloseConn()Response.ClearServer.Transfer"default.asp"endifendif"关闭记录集合和数据库连接rsObj.CloseSetrsObj=NothingCloseConn()"用户不存在或密码不正确Response.Write""Response.Write"alert("错误!请重新输入");"Response.Write"window.history.go(-1);"Response.Write""endifendsub"================================================="过程名:ShowAdminLogin"作用:显示管理员登录"参数:无"=================================================SubShowAdminLogin()55 %>登陆系统管理员帐号管理员密码<%endSub%>1.注销页面管理员注销页面logout.asp非常简单,只需要将Session清空,然后转向客户端首页面即可,它的代码如例程2-17所示。例程2-17注销页面logout.asp<%55 Session.Contents.RemoveAllResponse.Redirect"../"%>至此,管理端代码已经设计完毕,其实已经可以作为一个单独的小系统来进行性能测试了,这里建议除了使用WAST进行性能测试之外,处于安全性考虑,最好再进行一些安全性测试,防止恶意入浸。55 第3章系统的扩展和移植由于本系统针对的是中小型的模式,功能和性能考虑有许多可以扩展的地方。在本节首先讲一些系统的扩展,然后介绍如何移植该系统。3.1系统的扩展1.性能上的扩展作为企业级应用,本系统有些地方可以进行优化以达到更高的性能。l可以采用存储过程。存储过程是一些定义好之后存储在数据库中的SQL语句,它经过了DBMS的优化和预编译,因此性能比直接使用SQL语句好许多。l在程序中使用SQL语句尽量多用些技巧。比如尽可能少地选择列,使用top语句等。l更加细化地使用输出缓存,这可以大大提高效率。l对于SqlDataSource控件,可以考虑使用SQL依赖缓存,争取使用可以起到非常好的效果。除此之外,还有更多的技巧来提高性能,开发者可以在开发和学习过程中总结自己的经验。2.功能上的扩展由于时间和篇幅的限制,有许多功能和新技术在本系统中没有采用。l最新的构架模型:最近.NET又推出一种五层结构模型,这种模型充分考虑了系统的可扩展性和移植性。lWebService技术:这是一种分布式计算技术,每个WebService提供一个借口,采用它可以提高系统的可扩展性。l用户定制界面功能:允许用户定制个人偏好,比如哪个品牌、哪个价位,这一功能显得更加个性化。l用户登录以后显示所有已下订单:这样用户可以查看所有已下订单,并添加功能包括选择年份和月份等。l检查地址信息:当用户输入地址信息后,检查用户地址是否真实。可扩展的功能还有很多,开发者可以充分发挥自己的想象,使自己的系统更加友善。3.2系统的移植1.创建ASP.NET应用程序在IIS信息服务配置工具里,新建一个虚拟目录,命名为mobileshop,如图所示。选择程序所在目录,如图所示。在虚拟目录上面单击鼠标右键,选择“属性”,出现如图所示界面。单击“创建”按钮,在“应用程序名”处输入mobileshop即完成应用程序的设置。55 1.数据库的导入打开SQLServer2000企业管理器,连接上想要存储数据库的服务器。在数据库文件夹单击鼠标右键,选择“所有任务”“附加数据库”,出现如图所示的对话框。选择数据库文件,在“附加为”处输入mobileshop并指定数据库所有者,单击“确定”按钮即可完成数据库的导入。3.3web.config文件的配置默认数据库用户名是sa,密码是空,存储在web.config文件的节中,可以根据需要进行更改。55 第4章小结一个功能比较完备的在线手机销售系统的实例就构建完毕了。由于篇幅的限制,文中只讲解了部分源代码,不过只要理解了这部分内容,读懂未讲解的那部分源代码,相信不在话下。本章应用软件工程的设计思想,带领读者轻描淡写地走完了一个系统的开发流程,相信读者通过本例的学习,对软件工程的理解,对ASP开发技巧的掌握,以及对面向对象思想的理解能上一个新台阶。55 第5章参考文献[1]樊晓建.ASP.NET+ADO.NET项目开发实例[M],北京:清华大学出版社,2004,49-59.[2]石志国.ASP.NET应用教程[M],北京:北京交通大学出版社,2005,2-3.[3]蔡国强,李怡娜.管理信息系统生命周期系统管理[J]。科学学与科学技术管理,2002,23(10):77-80.[4]比特纳.用例建模[M],北京:清华大学出版社,2003,35-38.[5]罗森堡.用例驱动的UML对象建模应用[M],北京:人民邮电出版社,2005,74-85.[6]厄尔曼.ASP.NET1.1入门经典-VisualC#.NET2003编程篇[M],北京:清华大学出版社,2004,75-88.[7]曹衍龙.ASP.NET数据库开发实用工程案例精选[M],北京:人民邮电出版社,2004,368-369.[8]李明刚.ASP.NETWeb站点高级编程范例[M],北京:清华大学出版社,2004,125-132.[9]李正希.ASP.NET案例开发[M],北京:中国水利水电出版社,2004,76-82.[10]HungQ.Nguyen.WEB应用测试[M],北京:电子工业出版社,112-115.[11]MicrosoftACE小组.Microsoft.NETWeb应用程序性能测试[M],北京清华大学出版社,94-113.[12]DavidSceppa.ADO.NET技术内幕[M],北京:清华大学出版社,2003,203-429.[13]萨师煊.数据库系统概论[M],北京:高等教育出版社,2004,45-149.[14]StephenR.Schach.面向对象与传统软件工程[M],北京:机械工业出版社,2003,23-35209-226.[15]普莱斯曼.SoftwareEngineering-Apractitioner’sapproach[M],北京:机械工业出版社,2004,122-149.55