.-
网上订餐系统
1. 课程设计的目的
当计算机与互联网飞速的发展和使用,越来越多的公司、单位和个人通过网站提供服务、公布信息、交流知识等使互联网越来越方便我们的生活。在Web开发中,Java语言以其可扩展性、灵活性、可靠性和跨平台等特点,成为进行web编程的首选。更重要的是,开源社区提供了一大批功能强大的框架技术,Structs、Spring、Hibernate等,利用这些技术使我们更高效的维护和建立我们自己的网站同时也让网站的开发更加方便和快捷。
在这学期课堂上所学到的Java知识,能够为我们解决一些实际问题的能力。利用一周时间,使用Java框架技术完成网上订餐系统的分析设计和开发。有了网上订餐系统,人们可以在家随意挑选自己喜爱的食物。现在正是经济全球化的时代,利用网络,我们可以浏览不同地区甚至不同国家的商品,这也改变着人们的消费观,网上订餐系统正是符合了这一趋势的发展,越来越多的人能够不出门就能吃上美味可口的饭菜。
2.设计方案论证
2.1需求分析
随着科技的进步、计算机技术的发展,越来越多的人选择用网络来满足自己的需求,网上订餐的出现,正是体现了这一点。不少商家都把眼观放在了网上销售这一方面,把实体销售渐渐转变为网上销售。现在人们越来越重视节省时间,提高效率,不管是消费者还是商家都如此,有了网上订餐系统,可以让订餐者与餐厅进行互动,足不出户,选择自己喜爱的菜品,对与商家而言,这样做节省了成本,不需要为实体店的地址而烦恼,这样做对于商家和消费者来说无疑是双赢的。
2.2系统总体设计
本系统为了便于各种操作,采用多页面的模式。用户可在不同网页进行相应操作:
⑴在管理员信息表中,包括的数据项:帐号,密码,权限,注册时间。
⑵在订餐客户信息表中,包括的数据项:用户编号,用户名,密码,姓名,电话,身份证,注册时间等。
⑶在菜品信息表中,包括的数据项:菜品编号,菜品名称,菜品价格,内容,类别,添加时间等,每道菜都有它的菜名、介绍、价格等信息,为了让菜品的这些信息在页面有个很好的呈现,我们把这些信息存入菜品信息表中进行维护。。
⑷在定单列表中,包括的数据项:定单列表编号,用户名,菜品编号,下单时间等。
⑸在购物车信息,包括的数据项:购物车信息唯一标识,食品id,订餐用户名,添加时间等。当顾客选购菜品放入购物车时,我们会把这些菜品的id、数量、价格记录存入订单关联表中,通过订单关联表中的信息算出会员将要支付的金额,再通过菜品id这个主健把相应的信息存入个人订单这张表中进行维护。
⑹为了有效地管理菜品,在新增菜品时加了一个分类属性,在此需要建张分类表进行维护,系统总体功能图如图1所示。
后台
菜
肴
价
目
信
息
发
布
餐厅销售统计
用户留言及售后服务
前台
用户管理
菜单浏览
订单管理
用户反馈
网上订餐系统
图1 系统功能框图
2.3 系统流程设计
课程设计信息管理系统,包括用户注册、管理员登录、菜品查询、菜品详单等。任务执行的流程如图2所示。
进入系统主界面
管理员登录
浏览页面
用户注册
用户登录
菜品搜索
选购商品
用户注册
数据库
数据库
操作
页面
加入购物车
订单查询
后台管理
系统公告
结账
注销登录
图2 系统流程图
2.4 数据库设计
该系统是借助MySQL数据库对数据进行统一管理的。在网上订餐系统中需MySQL创建数据库“订餐管理系统”, MySQL数据库中数据的操作可以分为四种不同的类型,分别是添加、删除、查询和修改。该数据库包含的表有菜品信息表、管理员表、订单表等。
2.4.1菜品信息表
菜品信息表,记录系统中的菜品信息。把汉字转化为汉语拼音,作为字段名。把ID设为主键,且不为空,bianhao、mingcheng、leibie、tupian、jianjie、jiage、faburen、addtime与之相关联,如表1所示。
表1菜品信息表
字段名
类型
长度
是否为主键
是否为空
说明
id
int
11
是
否
菜品编号
bianhao
varchar
20
否
是
菜品编码
mingcheng
varchar
50
否
是
菜品名称
leibie
varchar
20
否
是
菜品类别
tupian
varchar
50
否
是
菜品图片
jianjie
varchar
50
否
是
菜品简介
faburen
varchar
30
否
是
发布人
2.4.2 管理员信息表
管理员信息表,存储系统中的管理员信息。把汉字转化为汉语拼音,作为字段名。其中,ID设置为int型,并设为主键,且不能为空;username、pwd设置为varchar型,如表2所示。
表2 管理员信息表
字段名
类型
长度
是否为主键
是否为空
说明
Id
Int
10
是
否
用户编号
username
Varchar
30
否
是
用户姓名
Pwd
Varchar
30
否
是
用户密码
2.4.3用户注册信息表
用户注册信息表,记录系统中的注册用户信息。把汉字转化为汉语拼音,作为字段名。把ID设为主键,且不为空,zhanghao、mima、ximgming、diqu、zhaopian、issh与之相关联。
表3 用户注册信息表
字段名
类型
长度
是否为主键
是否为空
说明
id
int
11
是
否
编号
Zhanghao
varchar
30
否
是
账号
mima
varchar
20
否
是
密码
Xingming
varchar
30
否
是
姓名
Diqu
varchar
50
否
是
地区
Zhaopian
varchar
50
否
是
照片
Issh
varchar
10
否
是
备注
2.5 主要功能
用struts框架中的异常处理机制,本系统自定义了异常类SystemException和异常处理类SystemExceptionHandler。在MessageResources.properties中定义需要在Dao中抛出异常时声明的内容。
在struts-config.xml中配置公共异常处理文件
,加入资源文件 在Dao中抛出自定义异常。并在公共异常处理页面中使用struts标签 进行异常信息显示。
顾客模块为封装的异常跳转页面如下图3所示:
图3
在struts-config.xml文件中的配置信息如下:
在配置文件中可见,异常是以键值对的方式在struts1.1中存放,type属性是指异常类,handler属性是指异常的处理类,path属性是指出现异常后跳转的错误页面。
3. 设计结果与分析
3.1订餐系统登录界面设计
该系统通过输入用户名和密码进行登录,登录界面如图4所示。
图4 登录界面
功能描述:顾客登陆管理,包括登陆时管理员需要输入用户名、密码、验证码、也可已选择是否记录用户名和密码以便以后登录。如果管理员选择记住用户名和密码则下一次登陆的时候只需要输入验证码,即可直接登录。
(1) 登录页面控制模块Login相关代码如下:
login.jsp:
<%@ page language="java"
import="java.util.*,com.actions.*"
pageEncoding="gbk"%>
<%String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<%@ taglib prefix="s" uri="/struts-tags"%>
登录页面
/head>
订餐系统
public class ALogin
{ public void doLogin ( Hashtable inputData, Hashtable outputData,
HttpSession mySession )
throws Exception
{//获取输入信息
String sUsername = (String)inputData.get("username");
String sPassword = (String)inputData.get("password");
//校验用户输入信息
LUserlUser=(LUser)GlobalObjectProvider.getLogicService(CommonConst.LOGIC_KEY_USER);
//如果对应的类没有的话,报错并返回login页面
if ( lUser == null )
{ throw new Exception("用户名或密码错误请重新输入!" );}
//获取用户信息
User userInfo = lUser.getUserInfo( sUsername, sPassword );
if ( userInfo == null )
{
outputData.put( "pageId", CommonConst.VIEWID_LOGIN );
outputData.put( "errMsg", "用户名密码检查失败!请重新输入。" );
outputData.put( "username", sUsername );
return;
}
Else
{
outputData.put( "pageId", CommonConst.VIEWID_MENU);
//往session中设置用户信息
mySession.setAttribute( "loginUser", userInfo );
return;
}}}
3.2订餐系统主页面模块设计
用户可以通过订餐系统主页面进行订餐如图5所示。
图5
功能描述:顾客登陆管理,包括
(1) 登陆时可以以游客或者会员的两种方式进行订餐操作,如果是会员登陆,先前需要录入用户名、密码、验证码,以便系统确认登陆成功。
(2) 会员个人信息管理,会员可以修改个人资料(如:电话,地址等),也可以查看订餐信息,了解当前订餐的动态(详见会员登录流程图)。
(3) 选购美食,作为顾客,在挑选美食的过程中,可以直接将称心的美食选进购物车中也可通过一系列查询,进一步了解后,再选择美食(详见选购美食流程图)。
选购美食部分代码如下:
……//得到在Spring中动态注入的Service层对象
……//得到日志对象log
public ActionForward queryForAll(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String forward = "first";
if(request.getParameter("user").equals("manager")){
forward = "queryDishes" ;
}
Food food = new Food() ;
List foodList = null ;
List topList = null ;
int allRecorders = foodService.queryForAllRecorders(food) ;
//当不是第一次登陆页面时
if(request.getParameter("cp")!=null&&request.getParameter("ls")!=null){
int currentPage = Integer.parseInt(request.getParameter("cp")) ;
int lineSize = Integer.parseInt(request.getParameter("ls")) ;
if(currentPage>1&&lineSize>allRecorders){
currentPage = 1 ;
}
food.setCurrentPage(currentPage) ;
food.setLineSize(lineSize) ;
foodList= foodService.queryForFood(food) ;
}else{
//第一次来到页面,top5和食品图片等信息
foodList = foodService.queryForFood() ;
}
int pageSize = (allRecorders + food.getLineSize() - 1) / food.getLineSize() ;
request.setAttribute("pageSize", pageSize) ;
request.setAttribute("allRecorders", allRecorders) ;
topList = foodService.queryForTop() ;
//下拉列表中的信息
List foodAttributeList = foodService.querySelectInfo();
//传给top10的list
request.setAttribute("topList", topList);
//传给视频图片等信息的list
request.setAttribute("foodList", foodList);
//传给下拉列表的list
request.setAttribute("foodAttributeList", foodAttributeList) ;
return mapping.findForward(forward);
}
首先判断是从哪个页面跳到到这里来调用这一方法,随着调用者的不同,方法也会将最后的传输到不同的页面中去。然后查询出所有美食的记录数,再对是否是第一次登录到此页面进行判断,以为,第一次登录时当前页数和每页显示行数是默认的,而此后登录到此页面时有可能发生改变,最后再用Spring中动态注入的服务层中食品类的对象调用查询食品信息的方法,再将结果通过request对象传输到对应的页面中去。
public String getFoodInfo(String src) {
//图片绝对路径
String picsrc = null;
String foodInfo = null ;
//只保留相对路径,将绝对路径去掉
String temp[] = src.split("/");
for (int i = 0; i < temp.length; i++) {
//得到图片的相对路径
picsrc = temp[temp.length - 1];
}
Food fd = foodDAO.query(picsrc);
foodInfo = fd.getFoodDescription() ;
return foodInfo;
}
此方法要在jsp页面中有DWR的对象调用,其中得到的路径是图片在服务器上的绝对路径,首先用String类中的split方法将其截成数据库中所存入得图片路径形式,然后再作为查询条件在数据库中查询,查询出美食的信息。然后DWR对象会将查询出来的信息作为jsp页面中回调函数的参数传回页面端。
3.3 美食信息添加功能模块
功能描述:对菜品信息进行维护,包括:
⑴添加菜品,添加菜品图品,价格,是否是推荐菜品等。
⑵菜品过季下架,用于菜品原料缺少或菜品过季等情况,进行逻辑删除。
⑶菜品的查询,可以按菜品的名称、价格、销售量、是否是推荐菜品进行查询,默认是推荐菜品查询。
⑷修改维护菜品信息,主要针对菜品原料的价格更变菜品的价格及菜品是打折时的价格更变,如图6所示。
图6
美食添加中图片上传功能代码如下:
/**图片上传**/
……//定义局部变量
try {
IPTimeStamp its = new IPTimeStamp(request.getRemoteAddr()) ;
//自动为上传图片命名,以确定图片的唯一性,以防覆盖
foodPictureAddr = its.getIPTimeStampRand()+"."+its.getLastName(file.getFileName()) ;
stream = file.getInputStream();
filePath = this.getServlet().getServletContext().getRealPath("/images/food")+"/"+foodPictureAddr;
bos = new FileOutputStream(filePath);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}catch (Exception e) {
e.printStackTrace() ;
}finally{
try {
bos.close();
stream.close();
} catch (IOException e) {
e.printStackTrace();
} catch(Exception e){}
}
file.destroy();
/**图片上传结束**/
首先,有系统自动未上传图片命名,防止覆盖,然后通过字符串拼接的方式作为图片的地址存入数据库(例如:图片名称叫aaa,扩展名为jpg,在存入数据库中的地址就是相对地址系统自动生成的名字.jpg),在通过输入输出流的方式将图片上传到服务器上。即由从form中得到的图片路径以流的方式服务器上的一本系统自动并命好的文件上,最后关闭输入流和输入流,并将文件损毁。
另附图片自动命名类的部分代码如下
//为了得到不重复的图片名称(首先用数字零给本机IP地址的点补位,然后再加上当前时间(精确到毫秒),最后再加上三个随机数)
……//定义局部变量
public String getIPTimeStampRand() {
StringBuffer buf = new StringBuffer();
if (ip != null) {
String str[] = this.ip.split("\\.");
for (int i = 0; i < str.length; i++) {
buf.append(this.addZero(str[i], 3));
}
}
buf.append(this.getTimeStamp());
Random rand = new Random();
for (int i = 0; i < 3; i++) {
buf.append(rand.nextInt(10)) ;
}
return buf.toString() ;
}
ip已经在实例化自动命名类对象是赋值,即是本机的ip地址,将ip地址通过“.”字符来分开有字符串数组接收,再将每部分未满三位的进行补零操作,再加上时间戳,最后加上三位随机数,再将StringBuffer转换成String对象最后得到的内容既是系统为图片自动赋好的名字。
另附美食类别处理代码如下:
if((!attributeId.trim().equals("0"))&&!(attributeId.trim().equals(""))){
food.setFoodTypeId(attributeId) ;
}else{
//新类别
foodAttributeValue = foodService.queryFoodAttributeId() ;
foodAttributeId = foodAttributeValue.getAttributeId() ;
try {
//调用bean中的changeAttributeId方法将foodAttributeId转换成foodattribute表中的格式
attributeid = foodAttributeValue.changeAttributeId(foodAttributeId) ;
} catch (Exception e) {
// 超过3位,提示减少不必要的食品类型,或者联系hhq
e.printStackTrace();
log.error("系统添加食品类别信息过多!",e);
throw new SystemException("foodattributelargenum.error",e);
}
foodattribute = new FoodAttribute() ;
foodattribute.setAttributeId(attributeid) ;
foodattribute.setAttributeName(newAttributeName) ;
//将新类别向属性表中插值
try {
isAdd = foodService.addFoodAttribute(foodattribute) ;
} catch (Exception e) {
e.printStackTrace();
log.error("系统添加食品类别信息时错误!",e);
throw new SystemException("foodattributeadd.error",e);
}
if(isAdd==true){
food.setFoodTypeId(attributeid) ;
}
}
基于美食的类别操作的可添加性,则需要判断是在文本框中填写的美食内容(即:新类别),还是在下拉菜单中选择的内容(即:老类别),如果是老类别则可以利用原有的类别编号直接填入数据库中。而如果是新类别则需要生成新的类别编号,再将类别编号填入到数据库中。
3.4订单查询
功能描述:对顾客的购物车所下的订单进行管理,包括:
⑴查看订单,可按订单号、用户姓名进行查询管理员可以查看到当前餐厅的订单,并可以进行打印订单以确认发货。
⑵修改订单,管理员对不在自己店范围内的订单可进行手动或者可以在订单状态为等待中时为顾客修改菜品的数量及品种,若订单状态为以下单则不能为顾客修改。
⑶打印订单,若打印订单表示以确认发货,并自动修改订单状态为已发送。
⑷取消订单,若顾客不方便上网打电话要取消订餐的话,管理员查看订单状态若为已下单则不能为顾客取消订单,若订单状态在等待中,则可以为顾客取消订单,如图7所示。
图7 订单信息查询页面
查询订单部分代码如下:
SELECT temp.* FROM(
select o.orderid as orderId ,
to_char(o.ordertime ,
yyyy-MM-dd HH24:mi:ss ) as orderTime ,
o.receivername as receiverName ,
o.receiveraddr as receiverAddr ,
o.receivertel as receiverTel ,
o.orderallprice as orderAllPrice ,
a.attributename as orderStatus ,
o.nodes as nodes ,
rownum as rn
from ordertable o, attributetable a
where o.orderstatusid = a.attributeid
and o.orderstatusid not in (06001,06002)
and o.memberusername like #memberUserName#
and
)temp
WHERE ( #currentPage# - 1 ) * #pageSize# ]]>
由于iBatis框架多持久层进行封装,所以SQL语句在写法上会有些不同,针对物理分页这一要求,采用由rownum来控制输出的列数,在查询结果上,也用到了oracle自己的函数to-char来对日期类型进行处理。
其中,在select标签中,id属性对应在DAO的实现类中对象smctemplate所调用的queryForList方法的第一个参数,借此找到此SQL,来进行查询操作。resultClass与parameterClass分别表示,结果类的类型和参数类的类型,其中还有标签是iBatis中使用rownum来进行分页操作所必须应用到的。
而如果在页面上点击“查看订单详情”则会弹出一个模态对话框,该对话框由两部分组成,上半部分是美食的信息,有美食名称、美食份数、美食单价、美食总价四个属性,而其中有“沿着此处剪开”的字样,下半部分则是顾客的信息有订单号、订单状态、收货人、地址、联系电话、总价、支付方式、订单时间、备注信息几项组成。
4.设计体会
经过一段时间的设计和开发,网上订餐系统基本开发完毕。其功能基本符合大众需求,能够完成菜肴的更新,菜肴的查询,菜肴的下单,后台的管理等各种功能但是由于课程设计时间较短和本人水平所限,虽然谢老师给予了我很多的指导,但是该系统还有许多不尽如人意的地方,对于Java的框架技术我还需要深入的进行学习。如今信息化的普及,要想使该系统能在海量的网上订餐系统中脱颖而出,以我们的编写能力,及我们对需求的分析和了解以及业务上的熟练程度都显得远远的不够。还有一些后续工作需要完成,下一步的改进一定做到层次更清晰,功能划分更明确,以实现更好的扩展性和重用性。
5.参考文献
[1] 严璋鹏.基于B-S的学生学籍管理系统的设计与实现[J].价值工程.2013.19:33-41
[2] 李刚. 轻量级J2EE企业应用实战—Struts+Spring+Hibernate整合开发[M] .北京:电子工业出版社, 2011.3:46-75
[3] 贾素玲,王强. JSP应用开发技术[M] .北京:清华大学出版社,2011.7:35-76
[4] 王海涛,贾宗璞.基于Struts和Hibernate的Web应用开发[J].计算机工程,2011, 37(9):113.
[5] 沈应逵. Java Web数据库系统应用开发与实例M].北京:人民邮电出版社,2009.9:102-134.
[6]胡涛涛. 基于MVC模式的课程管理系统的功能设计[J]. 山西煤炭管理干部学院学报,2013,04:140-142.
[7]任广财. 基于JSP的高校学生工作管理系统的设计与开发[J]. 科技经济市场,2013,12:119-120.
[8]施阳,张海燕,戴德伟. 基于JavaEE的毕业设计管理系统设计与实现[J]. 软件导刊,2015,02:86-88.
[9]赵春生. 浅谈JavaEE程序设计课程教学改革[J]. 科教导刊(中旬刊),2014,08:119-120.
[10] 刘晓华,张健,周慧贞.JSP应用开发详解(第三版) [M].北京:电子工业出版社, 2007.1
6.附录:
public class CommonServlet extends HttpServlet
{
//保存各页面Id对应的action类的对象
private Hashtable hPageHandler = new Hashtable();
//配置文件的存放位置
private JXPathContext configContext = null;
public void init()
{
//取得配置文件,并获得其中的dom元素
String filePath = getInitParameter("configXML");
String fileRealPath = getServletContext().getRealPath(filePath);
//尝试建立配置文件的DOM
try
{
org.jdom.input.SAXBuilder builder = new SAXBuilder();
org.jdom.Document pDoc = builder.build(fileRealPath );
configContext = JXPathContext.newContext(pDoc);
GlobalObjectProvider.init( configContext );
}
catch(Exception e)
{
System.out.println("Servlet初始化失败!");
}
//初始化共通类以获取页面信息
CommonConst.init();
}
//每一种动作第一次执行的时候,初始化对应的类
public void doPost ( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
//设置提交表单的中文编码
request.setCharacterEncoding("GBK");
HttpSession mySession = request.getSession(true);
//得到用户输入信息
String sPageId = request.getParameter("pageId");
String sActi