SSM 框架搭建 sckill 秒杀系统
—— step.5 web展示层建设
索引:
- SSM 框架搭建 sckill (秒杀系统) —— step.1 前期准备 + 框架配置
- SSM 框架搭建 sckill (秒杀系统) —— step.2 持久层建设
- SSM 框架搭建 sckill 秒杀系统 —— step.3 业务逻辑层建设
- SSM 框架搭建 sckill 秒杀系统 —— step.4 web控制层建设
- SSM 框架搭建 sckill 秒杀系统 —— step.5 web展示层建设
一、web 展示层(view)简介
展示层是一个系统与实际用户之间的交互层级,用图形化界面的方式,将后端的业务逻辑进行包装。最终呈现给用户一个简单易懂的可交互页面。因此,通常而言,这个层级是在系统设计中占有的设计时间比编码时间要更长。(不过就本系统而言,因为更注重的是逻辑和业务的实现,所以并没有在展示层的设计上下多大功夫,只是简单的展示而已。)
二、MVC – web 展示层设计框架
但凡设计系统前端,那么对 MVC 这个缩写一定都不陌生。MVC (model - view - controller)作为交互系统的设计框架,将交互方式分成了: 数据装载与搬运部分( model );展示与交互界面( view );和逻辑流程控制部分( controller )。在本系统的设计中,model 部分由 DTO(Data - Transfer - Object ) 承担,而 controller 部分则由 Spring 进行统一调度,至于 view 部分,则是由 jsp 进行承担。
三、web 交互界面标准化框架
在 web - UI 界面的构建中,市面上有许多成熟、优美的开源框架,比如:Bootstrap、Vue、React 等。这些开源框架通过编写原生 HTML 、CSS、JavaScript、jQuery ,将一些通用的功能和页面设计封装成库类插件。通过调用这些开源的库类,可以省去我们很多写页面 UI 设计的时间。同样的,在该系统中,由于更注重系统逻辑编写,在交互界面的设计上,采用了简单的 bootstrap 的标准化设计。
(1)web 目录结构
web 界面的搭建,首先是进行目录结构的规划,web 前端相关代码,均放置于与 java 同级的 webapp 文件夹下。目录内容如下:
目录结构简述如下:
webapp │ ├─resources :用于存放 js、css 等配置文件 │ └─script │ *.js │ └─WEB-INF │ web.xml : web 配置文件 │ ├─jsp :用于放置 web 端的界面代码,将页面代码放在 WEB-INF 文件夹下的好处是,可以将 web 端页面代码保护起来,用户在访问系统时,无法通过输入 URL 绝对路径的方式对页面进行访问。 │ │ *.jsp │ │ │ └─common :放置 web 端公有页面配置,如界面设计中的基本导入插件等。 │ *.jsp │ └─lib :放置一些需要导入项目的 jar 包(不过在有 maven 工具和 Spring 的统一管理下,基本没什么作用。
(2)项目编写
由于 web 界面较为简单,此处不再将 jsp 代码附上。在 web 前端,最核心的逻辑部分是实现交互的 JavaScript 代码,视频中给出了一种逻辑编码的新思路,即不再将编码封装成函数进行调用,而是将整个逻辑写成 json 的形式,这样做的好处是整个逻辑被封装到了一块,形成所谓的模块化,代码所有的函数形成内部调用。但个人还是不太习惯用这种方式,因为这样的写法,一个模块信息量太大,不利于后续的维护。
以下是 JavaScript 的核心代码块。需要注意的是,在导入该 JavaScript 片段前,必须确保已经在 jsp 页面中加载了 bootstrap 框架,否则代码中引用 bootstrap 的代码块片段将会失效。
// 存放只要交互逻辑 js 代码
// javascript 模块化
var seckill = {
// 封装秒杀相关 ajax 的url
URL : {
now : '/seckill/time/now',
exposer : function (seckillId){
return '/seckill/' + seckillId + '/exposer';
},
execution : function (seckillId, md5) {
return '/seckill/' + seckillId + md5 + '/execution';
}
},
validatePhone : function (phone){
if(phone && phone.length == 11 && !isNaN(phone)){
console.log("return true : ", phone, phone.length, isNaN(phone) );
return true;
}else {
console.log("return false : ", phone, isNaN(phone) );
return false;
}
},
handleSeckill : function (seckillId, node) {
// 处理秒杀逻辑
node.hide()
.html('<botton class="btn btn-primary brn-lg" id="killBtn">开始秒杀</botton>');
$.post(seckill.URL.exposer(seckillId), {}, function (result) {
// 在回调函数中执行交互流程
if(result && result['success']){
var exposer = result['data'];
if(exposer['exposed']){
// 开启秒杀, 获取秒杀地址
var md5 =exposer['md5'];
var killUrl = seckill.URL.execution(seckillId, md5);
console.log('killURL : ' + kilUrl);
// 绑定一次点击事件
$('#killBtn').one('click', function (){
// 执行秒杀请求
// 1.禁用按钮
$(this).addClass('disabled');
// 2.发送请求执行秒杀
$.post(killUrl, {}, function (result) {
if(result && result['success']){
var killResult = result['data'];
var state = killResult['state'];
var stateInfo = killResult['stateInfo'];
// 3.显示秒杀结果
node.html('<span class="label label-success">' + stateInfo +'</span>');
};
});
});
node.show();
}else{
// 未开启秒杀
var now = exposer['now'];
var start = exposer['start'];
var end = exposer['end'];
// 重新开始计算计时逻辑
seckill.countdown(seckillId, now, start, end);
}
}else{
console.log('result: ' + result);
}
})
},
countdown : function (seckillId, nowTime, startTime, endTime) {
var seckillBox = $('#seckill-box');
// 时间判断
if(nowTime > endTime){
seckillBox.html('秒杀结束!');
}else if(nowTime < startTime){
// 秒杀未开始,计时事件绑定
var killTime = new Date(startTime + 1000); // 防止用户端计时时间偏移
seckillBox.countdown(killTime, function (event){
var format = event.strftime('秒杀倒计时: %D天 %H时 %M分 %S秒');
seckillBox.html(format);
}).on('finish.countdown', function () {
// 时间完成后回调事件 -- 获取秒杀地址,实现逻辑,执行秒杀
seckill.handleSeckill(seckillId, seckillBox);
});
}else {
// 秒杀开始
seckill.handleSeckill(seckillId, seckillBox);
}
},
// 详情页秒杀逻辑
detail : {
// 详情页初始化
init : function (params) {
// 手机验证和登录,计时交互
// 规划交互流程
// 在cookie中查找手机号
var killPhone = $.cookie('killPhone');
var seckillId = params['seckillId'];
var startTime = params['startTime'];
var endTime = params['endTime'];
// 验证手机号
if(!seckill.validatePhone(killPhone)){
// 绑定phone
var killPhoneModel = $("#killPhoneModel");
// 控制输出,显示弹出层
killPhoneModel.modal({
show : true, // 显示弹出层
backdrop : 'static', //禁止位置关闭
keyboard : false // 关闭键盘事件
});
$('#killPhoneBtn').click(function(){
var inputPhone = $('#killPhoneKey').val();
if(seckill.validatePhone(inputPhone)){
// 将 phone 写入 cookie
$.cookie('killPhone', inputPhone, {
expires : 7,
path : '/seckill'
});
// 刷新页面
window.location.reload();
}else {
$('#killPhoneMessage').hide().html('<label class="label label-danger">手机号错误</label>').show(300);
}
});
}
// 登录之后: 计时交互
$.get(seckill.URL.now, {}, function (result) {
if(result && result['success']){
var nowTime = result['data'];
// 时间判断,计时交互
seckill.countdown(seckillId, nowTime, startTime, endTime);
}
});
}
}
}
(3)启动项目
当代码全部编写完毕,就是项目的启动,不知道有没有人跟我踩到了同样的坑。那就是明明代码编写无误,启动时却来了个 404 !!(找不到页面,说明 URL 定向错误,这就很过分,明明页面在的说。)
由于确定页面在物理上是存在的,那么可能的原因只能是 URL 在定向时发生了错误,导致页面未被索引成功。这个是视频中没被提到的,因为 IDEA 在创建 java - web 项目时,默认在服务器根目录下创建一个与项目名相同的文件夹,以此存放 web 项目,但本项目的存放位置是 root 因此无法被索引到。解决方法如下:
在菜单栏找到 Edit Configurations… 单击,在弹出的窗口中,找到对应的 Tomcat Server 启动项,选中 Deployment ,将 ‘Application context’ 改为 ‘/’ 点击 ‘OK’, 此时,修改完毕。重新启动服务器,可以看到问题解决了!
如果成功,可以看到类似的界面,此时,功能符合预期,项目建立完毕。
小贴士:HTTP状态码分类
分类 | 分类描述 |
---|---|
1XX | 表服务器接收到信息请求,客户端应继续请求 |
2XX | 表成功(最佳状态),客户端的操作被成功接收并处理后返回给客户端 |
3XX | 表重定向,需要客户端进一步操作 |
4XX | 表客户端错误(最头疼的错误),说明访问资源不存在或请求无法完成 |
5XX | 表服务器错误(通常来讲就是服务器挂掉或者出现代码bug了) |