2 系统设计
2.1架构设计
SpringMVC是当下流行的Java框架之一,同时也引入二级缓存。在点餐系统中使用本地redis缓存,可以提高性能;springboot省去了很多配置文件。
图 2.1 MVC架构
视图层选用jsp,控制层用基于注解的Controller,并增加了业务层;在dao层引入了mybatis框架,在application.properties文件中MySQL数据库。基于注解的方式,省去了很多配置文件信息,与此同时,代码不再依赖于具体的数据库,提高了代码的可修改性。
2.2结构设计
由需求分析可得,系统主要模块如图 2.2 所示,大致分为顾客模块和商家模块,具有明显的权限区分。
图 2.2 系统的主要模块
3 开发工具和运行环境
1、开发工具
编程语言:Java
前、后台开发工具:IDEA
数据库发开工具:Navicat
测试工具:Jmeter(性能测试),Selenium(web自动化测试),Junit(单元测试)
2、运行环境
硬件需求:
CPU:IntelCorei5-7300HQ @ 2.50GHz 2.50GHz
内存: Samsung 8.0 GB
软件需求:
操作系统版本:Windows 10
后台服务器:Apache Tomcat 9.0
数据库版本:Mysql8.0
浏览器:Google Chrome
4 详细设计与实现
4.1数据库设计
4.1.1概念结构设计
(1)顾客实体和商家实体 E-R 图如图 4.1和 4.2 所示,虽然两个实体具有的属性相同,但是权限不一样。
图 4.1顾客实体E-R图 图 4.2商家实体E-R图
(2)分类实体和菜品实体 E-R 图如图 4.3 和 4.4所示。
图 4.3分类实体E-R图 图 4.4菜品实体E-R图
(3)订单项实体和订单实体 E-R 图如图4.5和4.6所示。
图 4.5订单项实体E-R图 图 4.6订单实体E-R图
(4)评价实体 E-R 图如图4.7所示。
图 4.7评价实体E-R图
(5)系统整体 E-R 图如图 4.8所示,图中省略了各个实体的属性,只保留了实体和实体之间的联系以及关系。
图 4.8系统整体E-R图
4.1.2逻辑结构设计
关系模型
(1)商家(账户、密码、姓名、电话)
(2)顾客(账户、密码、姓名、电话)
(3)分类(编 、名称)
(4)座位(桌 、座位 、状态)
(5)菜品(编 、分类编 、名称、单价、描述、图片)
(6)订单项(编 、订单编 、菜品编 、订购数量、小计)
(7)订单(编 、顾客账户、总额、日期、状态、备注)
基本表的设计
根据 E-R 模型和关系模型,点餐系统建立了 9 个基本表和两个视图,下面是各视图和基本表的详细说明。
(1)customer表主要是记录了顾客的基本信息,表结构设计如表4.1所示。
表 4.1顾客表
(2)merchant表主要是记录了商家的基本信息,表结构设计如表4.2所示。
表 4.2商家表
(3)dish表主要是记录了菜品的基本信息。表结构设计如表4.3所示。dish表中的外键约束:categoryNum属性参照category表的主键(num)。
表 4.3菜品表
(5)category表主要是记录了菜品分类的基本信息,表结构设计如表4.4所示。
表 4.4菜品分类表
(6)Lineitem表主要是记录了订单项的基本信息。表结构设计如表4.5所示。Lineltem表中的外键约束:dishNum属性参照dish表的主键(num),orderNum属性参照order表的主键(num)。
表 4.5订单项(明细)表
(7)orders表主要是记录了订单的基本信息。表结构设计如表4.6所示。ordes表中的外键约束:customer属性参照customer表的主键(num)。
表 4.6订单表
(7)comment表主要是记录了顾客评价的基本信息。表结构设计如表4.7所示。ordes表中的外键约束:customer属性参照customer表的主键(num),orderNum属性参照order表的主键(num)。
表 4.7评价表
视图设计
(1)dishInfo视图主要是记录了菜品的基本信息。由于dish表中存储的是分类编 ,看起来很不方便,于是分类名称添加到视图中,视图的定义如表4.8所示。
表4.8 dishInfo视图定义
SELECT
. AS ,
. AS ,
. AS ,
. AS ,
. AS ,
. AS
FROM
( JOIN )
WHERE
( . = . )
(2)lineitemInfo视图主要是记录了顾客可见的订单项基本信,记录了基本的菜品信息和订单信息,视图定义如表4.9所示。
表4.9 lineitemInfo视图定义
SELECT
. AS ,
. AS ,
. AS ,
. AS ,
. AS ,
. AS ,
. AS
FROM
( JOIN )
WHERE
( . = . )
4.2类的设计
LoginRegisterController
customerService:CustomerService
merchantService:MerchantService
+sendSMS(tel:String):String
+login(account:String,pwd:String,identity: String):String
+telLogin(tel:String,verifyCode:String,identity:String):String
+register(tel:String,verifyCode:String,username:String,password:String,rePwd:String):String
图4.9 LoginRegisterController类图
(1)sendSMS函数,调用工具类,实现发送验证码的功能。
(2)telLogin函数,提供手机-验证码登录。
(3)register函数,顾客注册功能。
CustomerController
categoryService:CategoryService
dishService:DishService
commentService:CommentService
orderService:OrderService
lineitemService:LineitemService
+addDishitem(quantity:String,num:String,name:String, price: String, picture:String):String
+deleteDishitem(index:String):String
+payPage(remark:String):String
+confirmTrade(orderNum:String):String
+cancelTrade(orderNum:String):String
+applyRefund(orderNum:String):String
-query(out_trade_no:String):String
-upload(file:MultipartFile,fileName:String):String
+addComment(file:MultipartFile,orderNum:String, number:String,content:String):String
图4.10 CustomerController类图
(1)addDishitem函数:顾客加餐。
(2)deleteDishitem函数:在顾客准备下单时,选择性的删除商品。
(3)payPage函数:支付宝沙箱环境支付。
(4)confirmTrade函数:顾客确认收获,交易完成。
(5)cancelTrade函数:顾客未付款,取消订单。
(6)applyRefund函数:顾客申请退款,由商家进行处理。
(7)query函数:支付交易查询。
(8)upload函数:上传评价时的配图。
(9)addComment函数:顾客在确认收货后,进行订单的评价。
MerchantController
categoryService:CategoryService
dishService:DishService
orderService:OrderService
merchantService:MerchantService
+modifyPass(mpass:String mpass, newpass:String):String
+pageShow(previous:String,next:String,all:String,nextPage:String ):String
+dishManagePage():String
+uploadDishPicture(file:MultipartFile):String
-upload(file:MultipartFile, filePath:String):String
+addDishDetail(categoryNum: String, name:String):String
+selectOrder(begin:String,end:String):String
-refund(orderNum:String, amount:String):String
图4.11 MerchantController类图
(1)modifyPass函数:商家修改密码。
(2)pageShow函数:菜品分页显示。
(3)dishManagePage函数:显示所有菜品。
(4)uploadDishPicture函数:商家添加菜品时,上传的菜品图片。
(5)upload函数:实现上传功能。
(6)addDishDetail函数:商家添加商品时,填写的菜品信息。
(7)selectOrder函数:根据时间段查询订单。
(8)refund函数:商家对订单进行退款。
图4.12 CategoryService类图
图4.13 CategoryDAOByMybatis类图
CategoryService类主要是调用CategoryDAOByMybatis类,实现具体功能。
(1)findAllCategories函数:查询所有的分类信息。
(2)findAllCategoryNames函数:查询所有的分类名称。
图4.14 CommentService类图
图4.15 CommentDAOByMybatis类图
CommentService类主要是调用CommentDAOByMybatis类实现功能。
(1)findAllComments方法是从数据库中查询所有的评价信息。
(2)findAllLabels方法查询的是与评价相关的所有标签。
(3)updateLabelByName方法当顾客评价时,更新选择的标签。
(4)addComment实现插入一条评论记录。
(5)updatePictureByNum方法是顾客评价时,附加配图,评价的文字信息和图片上传是分开处理的,所以需要在插入评论后再设置图片。
(6)selectAverageGrade方法用于查询平均评分,在店铺评价页面显示。
图4.16 DishService类图
图4.17 DishDAOByMybatis类图
DishService类主要调用DishDAOByMybatis类实现功能:
(1)findByCategoryNum函数实现根据分类编 查询商品。
(2)findAllDishInfo函数实现查询所有菜品信息。
(3)findDishByNum函数:根据菜品编 查询菜品的详细信息。
(4)addDish函数:插入一条菜品记录。
(5)deleteDish函数:删除一条菜品记录。
(6)selectDishInfoByKeyword函数:根据关键字查询菜品信息。
图4.18 LineitemService类图
图4.19 LineitemDAOByMybatis类图
LineitemService类主要调用LineitemDAOByMybatis类实现功能:
(1)addLineitem函数:插入一条订单项。
(2)selectByOrderNum函数:根据订单编 ,查询此订单所有的订单项。
图4.20 OrderService类图
图4.21 OrderDAOByMybaits类图
OrderService类调用OrderDAOByMybaits类中的函数实现功能:
(1)addOrder函数:插入一条订单记录。
(2)findByCustomer函数:根据顾客账户,查询该顾客的所有订单。
(3)deleteByNum函数:根据订单编 ,删除一个订单。
(4)findAllOrder函数:查询所有的订单信息。
(5)findBetweenAnd函数:根据时间段查询订单。
(6)updateStatusByNum函数:根据订单编 更新订单状态。如:从“支付成功”变为“交易成功”。
4.3具体功能实现
4.3.1按类别浏览菜品
顾客进入系统后,可以选择类别来查看对应的商品。界面图如4.22所示。
图4.22 浏览菜品
Controller层
/**
- 根据菜品分类编 ,显示某一类菜品
- @param request 请求
- @param categoryNum 分类编
- @return 分类编 对应的菜品页面
/
@RequestMapping(“/dish”)
public String dish(HttpServletRequest request, String categoryNum) {
request.setAttribute(“dishes”, dishService.findByCategoryNum(categoryNum));
return “/customers/dish”;
}
Service层
/* - 根据商品编 查询商品.
- @param categoryNum 分类编
- @return 商品
/
public List findByCategoryNum(String categoryNum) {
return dishDAOByMybatis.findByCategoryNum(categoryNum);
}
DAO层
/* - 根据分类编 查询商品.
- @param categoryNum 分类编
- @return 某个分类的所有商品
*/
@Select(“select * from dish where categoryNum=#{categoryNum}”)
List findByCategoryNum(@Param(“categoryNum”) String categoryNum);
4.3.2查看历史订单
顾客成功登录后,可以在导航栏处点击我的订单来查看自己的所有订单,我的订单页面上按时间降序显示了订单的简略信息,可以点击详情查看订单详情。
图4.23 浏览订单
图4.24 浏览订单详情
Controller层
/**
- 顾客查看自己所有的订单
- @return jsp页面
/
@RequestMapping(“/ordersPage”)
public String ordersPage(HttpServletRequest request) {
request.setAttribute(“orders”, orderService.findByCustomer(getCustomerNum(request)));
return “/customers/orders”;
}
Service层
/* - 根据顾客编 查询订单
- @param customer 顾客编
- @return 顾客的所有订单
/
public List findByCustomer(int customer){
return orderDAOByMybatis.findByCustomer(customer);
}
DAO层
/* - 根据顾客编 查询订单
- @param customer 顾客编
- @return 顾客的所有订单
*/
@Select(“select * from orders where customer=#{customer} order by birth desc”)
List findByCustomer(@Param(“customer”) int customer);
4.3.3购物篮
顾客成功登录后,可以点击查看自己的购物信息,购物篮页面显示了用户添加到购物篮的所有菜品以及数量,顾客可以删除菜品品、给商家留言,之后用户可以结账提交订单。页面如图4.25所示:
图4.25 购物篮详情
Controller层
/**
- 显示订单项的页面
*/
@RequestMapping(value = “/lineitems”)
public String lineitems() {
return “/customers/lineitems”;
}
4.3.4订单评价
图4.26 添加订单评价
图4.27 店铺评价页面
Controller层
/**
- 用户确认收货后,对一个订单进行评价
- @param request 请求
- @param orderNum 订单
- @param number 评价等级
- @param content 评价内容
- @param choose 是否配图;当取值为’yes’时,代表选择了配图;当取值为’no’时,说明顾客没有选择配图
- @param file 选择的文件
- @return 评价完成后, 返回店铺所有评价页面
/
@RequestMapping(“/addComment”)
public String addComment(HttpServletRequest request, MultipartFile file, String orderNum, String number, String content, String choose) throws IOException {
/获取checkbox所有的选中值/
String[] label = request.getParameterValues(“label”);
for (String name : label) {
/在数据库中跟新标签出现的次数/
commentService.updateLabelByName(name);
}
/插入一条评论,并返回评论的编 /
int commentNum = commentService.addComment(orderNum, getCustomerNum(request)
, Integer.parseInt(number), content);
if (“yes”.equals(choose)) {
/上传的文件名/
String uploadFileName = file.getOriginalFilename();
/文件后缀/
String suffix = uploadFileName.substring(uploadFileName.lastIndexOf(“.”));
/对上传的图片进行重命名/
String fileName = String.valueOf(commentNum).concat(suffix);
/上传图片/
upload(file, request, fileName);
/更新数据库中评论的文件名/
commentService.updatePictureByNum(commentNum, fileName);
}
return “redirect:/customer/commentsPage”;
}
Service层
/** - 插入一条评价
- @param orderNum 订单编
- @param customerNum 顾客编
- @param grade 评价等级
- @param content 评价内容
- @return 评价编
/
public int addComment(String orderNum, int customerNum, int grade, String content) {
Comment comment = new Comment();
comment.setCustomerNum(customerNum);
comment.setGrade(grade);
comment.setContent(content);
comment.setOrderNum(orderNum);
commentDAOByMybatis.addComment(comment);
return comment.getNum();
}
DAO层
/* - 添加一条评价
- @param comment 待添加的评价
图4.28 菜品管理页面
在添加菜品页面,商家可以填写菜品信息,新增一个菜品,如图4.29所示。
图4.29 添加菜品页面
Controller层
/**
- 显示所有菜品的页面
- @param request 请求
- @return jsp页面
/
@RequestMapping(“/dishManagePage”)
public String dishManagePage(HttpServletRequest request) {
List dishInfos = dishService.findAllDishInfo();
if (request.getAttribute(“dishInfo”) == null) {
request.setAttribute(“dishInfo”, dishInfos);
}
HttpSession session = request.getSession();
session.setAttribute(“begin”, 0);
session.setAttribute(“end”, dishInfos.size() > 5 4 : dishInfos.size() – 1);
return “/merchants/allDish”;
}
Service层
/* - 视图中查询所有菜品信息
- @return 所有的菜品信息
/
public List findAllDishInfo() {
return dishDAOByMybatis.findAllDishInfo();
}
DAO层
/* - 从视图中查询所有的菜品信息
- @return 菜品信息
*/
@Select(“select * from dishinfo”)
@Cacheable(“dishinfos”)
List findAllDishInfo();
4.3.6订单管理
商家在登录成功后,在订单管理页面,可以查看所有订单,也可以按时间段查看订单信息,在订单页面也可以处理退款,如图4.30所示。
图4.30 订单管理页面
Controller层
/**
- 退款
- @param request 请求
- @return 是否退款成功
- @throws AlipayApiException 退款异常
/
@RequestMapping(“/refund”)
public String refund(HttpServletRequest request, String orderNum, String amount) throws AlipayApiException {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, “json”, AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//设置请求参数
AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
//商户订单 ,商户 站订单系统中唯一订单
String out_trade_no = orderNum;
//需要退款的金额,该金额不能大于订单金额,必填
String refund_amount = amount;
AlipayTradeRefundResponse response = alipayClient.execute(alipayRequest);
outPut(response.getBody());
if (response.isSuccess()) {
/在数据库中更新订单状态/
orderService.updateStatusByNum(orderNum, Order.TRADE_STATUS.get(Order.CLOSED));
request.setAttribute(“success”, “退款成功”);
}
return “forward:/merchant/allOrder”;
}
Service层
/* - 根据订单编 ,更新订单状态
- @param orderNum 订单编
- @param status 订单状态
/
public void updateStatusByNum(String orderNum, String status) {
orderDAOByMybatis.updateStatusByNum(orderNum,status);
}
DAO层
/* - 根据订单编 ,更新订单状态
- @param num 订单编
- @param status 订单状态
*/
@CacheEvict(value = “orders”, allEntries = true)
@Update(“update orders set status=#{status} where num=#{num}”)
int updateStatusByNum(@Param(“num”) String num, @Param(“status”) String status);
5 系统测试
5.1功能测试
测试工具:selenium。开启录制后,在页面进行点击操作。通过回放的方式多次运行程序,还可以进行代码的调试。
图 5.1 测试工具
5.1.1 登录模块测试
根据登录模块,设计如表5.1所示的测试用例。在登录模块中,所设计的测试用例经测试,均通过。
表 5.1 登录模块测试用例
输入参数 预期输出 实际输出 是否通过
用户名:null
密码:null
验证码:null 弹出错误提示,验证码错误 登录失败,请用户重新输入 是
用户名:null
密码:null 弹出错误提示,用户名错误 登录失败,提示用户重新输入 是
用户名:201716040128
密码:null 弹出错误提示,密码错误 登录失败,提示用户重新输入 是
用户名:201716040128
密码:123456 弹出错误提示,密码错误 登录失败,提示用户重新输入 是
用户名:201716040128
密码:040128 登录成功,进入个人主页 登录成功,进入系统 是
5.1.2 点餐模块测试
根据点餐模块,设计如表5.2所示的测试用例。在点餐模块中,当用户手工输入非数字时,没有任何提示,如表5.3所示需要修改JavaScript代码,修改后测试用例通过,如图5.1所示。
表 5.2 点餐模块测试用例
输入参数 预期输出 实际输出 是否通过
点击加 按钮、点击减 按钮 订购数量数量加1、减一 订购数量数量加1、减一 是
输入数量为非数字 提示用户无法订餐 没有任何提示 否
输入数字5 提示用户加餐成功 提示用户加餐成功 是
输入数字12 提示用户最多只能加餐9份 提示用户加餐姿势不对 是
表 5.3 修改点餐模块错误
if(isNaN(count))//输入非数字
con = ‘小主加餐姿势不对哦
请重新输入’
layer.msg(con, {
time: 1500, //1.5s后自动关闭
btn: [‘明白了’, ‘知道了’, ‘哦’]
});
图5.2 提示用户加餐姿势不对
5.1.3 订单提交模块测试
根据订单提交模块,设计如表5.4所示的测试用例。在点餐模块中,当选择提交后时,出现了异常情况,如表5.5所示需要修改JavaScript代码。
表 5.4 订单提交模块的测试用例
测试用例 预期结果 实际结果 是否通过
删除订单项,选择确认删除 订单项从当前订单中移除 订单项从当前订单中移除 是
将订单项全部删除 显示没有可以结算的页面 显示顾客还没有订购任何东西 是
顾客留言为空 插入默认的留言信息 插入了默认的留言信息 是
点击提交按钮 转到支付页面 出现空指针异常 否
由于在支付页面的时候,订单编 是自动生成的,顾客无法进行改变,于是将input标签的disabled=“disabled”,当提交form的时候,却无法接收到参数,改成readonly即可。
表 5.5 修改订单提交模块错误
document.getElementById(“WIDout_trade_no”).setAttribute(“readonly”,true)
5.1.4 菜品管理模块测试
根据菜品管理模块,设计如表5.6所示的测试用例。在菜品管理模块中,当没有选择上传文件点击时出现了异常情况,如表5.7所示修改代码,修改后测试用例通过。
表 5.6 菜品管理模块的测试用例
输入参数 预期输出 实际输出 是否通过
上传文件为null 不进行用户响应 严重异常 否
选择上传文件龙猫.gif 显示上传成功 上传成功 是
填写价格为abc 提示管理员价格不合法 提示错误消息 否
填写价格为-2 提示管理员价格不合法 提示错误消息 是
填写价格为32 提示管理员添加成功 提示管理员添加成功 是
将菜品名称或者菜品描述设置为空 提示管理员此项是必填项 提示管理员菜品名称不能为空 是
菜品名称大于10个字符或者菜品描述大于255个字符 提示管理员信息输入过长,无法保存 提示管理员信息输入的太多 是
表 5.7 修改菜品管理模块上传文件为空错误
// 解析请求的内容提取文件数据
List formItems = upload.parseRequest(new ServletRequestContext(request));
if (formItems != null && formItems.size() > 0) {
表 5.8 改正添加菜品时价格判断模块
function inputCheck() {
const price = document.getElementById(“price”).value;
if (isNaN(price) || price < 0) {
alert(“菜品价格不合法”);
return false;
}
else
return true;
}
5.1.5 订单管理模块测试
根据订单管理模块,设计如表5.9所示的测试用例。在订单查询模块中,当时间起点大于时间终点时,是逻辑错误,但是预期结果和实际结果不符,如表5.10所示,修改JavaScript代码,修改后测试用例通过,如图5.3所示。
表 5.9 订单管理模块的测试用例
输入参数 预期输出 实际输出 是否通过
点击数据右侧的删除按钮 删除选择的订单 删除该行订单 是
输入时间起点为空 提示用户此项为必填项 提示用户错误 是
输入时间终点为空 提示用户此项为必填项 提示用户错误 是
时间起点大于时间终点 提示用户时间起点需要小于时间终点 没有任何提示,无订单的查询结果 否
由于Javascript执行错误,在进行查询的时候,当起点大于终点时并不能提示相应的信息。
表 5.10 修改订单管理模块的错误
form.verify({
select : function(value) {
var begin = document.getElementById(‘date’).value;
var end = document.getElementById(‘date2’).value;
if(begin > end){
return ‘日期选择不合法,起点需要小于等于终点’;
} } });
图 5.3 改正后的错误提示
5.2 性能测试
性能是指系统能够满足吞吐量或时延要求程度的指标,用每秒执行事务的数量或单个事务耗费的时间来表示。系统中采用了redis做缓存来提高性能,使用了jmeter工具来进行了检测,测试过程如图5.4、5.5所示,结果如图5.6所示。
图5.4 线程组配置
图5.5 HTTP请求配置
图5.6 性能测试结果
不加redis缓存时,进行测试,结果如图5.6所示。
图5.7 不加redis缓存测试结果
结果图上的属性含义如下:
(1)#Samples:表示这次测试中一共发出了多少个请求。
(2)Average:平均响应时间—默认情况下是单个 Request 的平均响应时间。
(3)Min:最小响应时间。
(4)Maximum:最大响应时间。
(5)Throughput:吞吐量——默认情况下表示每秒完成的请求数。
在有缓存的情况下,系统平均响应时间更低,两者的最小相应时间相差不多,但还是有缓存时低,最大响应时间相差较大,也还是有缓存比较占优势,而吞吐量最为明显的表明了有缓存时性能得到了较大的提高。
6 总结
6.1问题和解决
(1)订单添加时出错。如图6.1所示,出现异常,原因是:表中int类型属性的类型为null,而类的属性int不能为null,出现无法进行赋值的问题,解决的办法是将类的属性int改为integer。
图 6.1 添加订单出错
(2)添加菜品时出错。由于商品描述中含有空格键,在执行时出现如图6.2所示的错误,解决的办法是在执行的时候,不再设置参数,而是直接将sql字符串“+”商品描述。
图 6.2 添加菜品出错
(3)input标签中,如果加上disabled属性,不能再提交表单。使用request.getParameter()无法获取到,得到的值总是null。
链接: https://pan.baidu.com/s/1ReGYoyy3R1yYIHsyU87E9A
提取码: 2mcd
内含实验 告
大哥大姐跑代码的时候,注册的时候,别忘了改手机 ,默认是我的,我已经收到很多验证码了

文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树使用JDBC操作数据库数据库操作91484 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!