怎么感觉web倒计时交互不该像老师这样写,我感觉这样不太合适,难道我的想法错误?
关于课程:
《Java高并发秒杀API之web层》
怎么感觉web倒计时交互不该像老师这样写,我感觉这样不太合适,难道我的想法错误?
一、controller里的 /time/now 这个方法完全没有必要,获取秒杀地址Exposer里就包含了当前系统时间
下面看Exposer的属性:
public class Exposer {
// 是否要暴露的,表示是否开启秒杀
private boolean exposed;
// md5加密措施
private String md5;
// 秒杀的id
private long seckillId;
// 系统当前时间 毫秒
private long now;
// 秒杀开始时间 毫秒
private long start;
// 秒杀结束时间 毫秒
private long end;
.......倒计时交互流程我觉得应该是下面这样:
在详情页jsp通过“/{seckillId}/exposer"直接获取这个Exposer 的封装json对象SeckillResult<Exposer>。
判断exposed属性,
1.如果是false,则拿now分别比较start和end,如果在start前面则开始倒计时,如果在end后面则显示秒杀结束。
2.如果是true,则处于秒杀有效时间内,显示秒杀按钮。
获取的Exposer对象中的数据,已经是服务端执行了业务逻辑才放入的,直接可以拿来用。我不明白为什么jsp里还要写这么多逻辑,jsp最大的作用只是用来展示。而且对于以后维护也不方便,需要服务端和客户端都要改。
二、另外关于异常处理,我也感觉不合理。比如在SeckillController的方法executeSeckill中:
try {
SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5);
return new SeckillResult<SeckillExecution>(true,execution);
}catch (RepeatkillException e){
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL);
return new SeckillResult<SeckillExecution>(false,execution);
}catch (SeckillCloseException e){
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.END);
return new SeckillResult<SeckillExecution>(false,execution);
}catch (Exception e){
logger.error(e.getMessage(),e);
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR);
return new SeckillResult<SeckillExecution>(false,execution);
}catch到异常就应该把秒杀失败的标志,以及错误信息返回给前段就行了,为什么是SeckillResult<SeckillExecution>(false,execution); 其中false我可以理解,但是execution有什么用,SeckillResult不是有(boolean success, String error)的构造器吗?一个String的信息就行,需要传对象过去吗?
三、还有SeckillResult的boolean success,SeckillExecution 的 int state,Exposer的boolean exposed;我觉得概念设计的很混乱,老师讲解的也不是很清楚。
我认为,SeckillResult有boolean success和String error表示是否执行成功和异常信息就好,Exposer的boolean exposed表示秒杀是否开启。SeckillExecution 的 int state完全没有必要,枚举应该用在异常类里(可以给父类SeckillException加一个state的属性),这些信息只会在秒杀失败的时候返回给前段,直接捕捉异常,得到异常的message以字符转返回就行了,秒杀失败的时候根本不需要返回SeckillResult对象。
感觉有些混乱和冗余。
以上只是我个人的一些想法,希望大家可以看一下我想的对不对,如果有不对指正。如果本课的老师可以看到,真诚希望可以解疑,拜谢。
另外:
这是我参考老师的代码,按照自己的理解写的seckil.js逻辑代码,我感觉这样更好理解一些,经测试可以实现功能。
//存放主要的交互逻辑js代码
//javaScript模块化,可以模拟java的分包。
//seckill 对象有属性 URL(URL本身可能还有别的属性),属性也可以为函数,因为在script中函数也是对象。
//调用方法类似:seckill.detail.init(params);
var seckill = {
// :(冒号)对象表达法 冒号在这里用来分割对象的属性和属性值。
// 秒杀相关ajax的url
URL : {
nowUrl : function () {
return '/seckill/seckill/time/now';
},
exposerUrl : function (seckillId) {
return "/seckill/seckill/" + seckillId + "/exposer";
},
seckillUrl : function (seckillId, md5) {
return "/seckill/seckill/" + seckillId + "/" + md5 + "/execution";
}
},
// 校验手机号
validatePhone : function (phone) {
// if(phone){}表示不为空,isNaN(phone)表示非数字
if (phone && phone.length == 11 && !isNaN(phone)) {
return true;
} else {
return false;
}
},
seckillView : function (seckillId) {
$
.post(
seckill.URL.exposerUrl(seckillId), {},
function (result) {
if (result && result['success']) { // 请求成功
var exposer = result['data'];
var seckillId = exposer['seckillId'];
var seckillBox = $('#seckill-box');
if (exposer['exposed']) { // 可以秒杀
var md5 = exposer['md5'];
var killUrl = seckill.URL.seckillUrl(
seckillId, md5);
seckillBox
.html('<button class="btn btn-primary btn-lg" id="killBtn">开始秒杀</button>'); // 按钮
// 绑定点击事件(绑定一次)
$("#killBtn")
.one(
'click',
function () {
// 执行秒杀的请求操作
$(this).addClass(
'disabled');
// 发送秒杀的请求
$
.post(
killUrl, {},
function (
result) {
if (result
&& result['success']) {
var seckillExecution = result['data'];
var state = seckillExecution['state'];
var stateInfo = seckillExecution['stateInfo'];
// 显示秒杀结果
seckillBox
.html('<span class="label label-success">'
+ stateInfo
+ '</span>');
}
})
})
} else { // 不可以秒杀
var now = exposer['now'];
var start = exposer['start'];
var end = exposer['end'];
if (now < start) { // 还未开始 倒计时
seckill.countdown(seckillId, now,
start, seckillBox);
} else { // 已经结束(exposed为false只能是未开始或者结束)
seckillBox.html('秒杀结束');
}
}
} else { // 请求失败
console.log("result:" + result);
}
})
},
// 倒计时处理
countdown : function (seckillId, now, start, seckillBox) {
// 计时事件绑定
var killTime = new Date(Number(start) + 1000); // 加一秒的时间偏移。(运算计时的消耗时间,后来细想应该是减去消耗时间,但是因为无法知道消耗的时间多少,减多了会提前显示出点击按钮,并不合理,所以最好不加不减。)
// jquery倒计时插件,监听时间变化循环回调函数
seckillBox.countdown(killTime, function (event) {
// 格式化时间
var format = event.strftime('秒杀计时: %D天 %H小时 %M分钟 %S秒');
// 更新倒计时节点组件
seckillBox.html(format);
}).on('finish.countdown', function () { // 倒计时结束的回调函数
// 倒计时结束,以防倒计时误差,需要重新请求exposer,判断是否已经开始,来显示秒杀按钮或者更正倒计时
seckill.seckillView(seckillId);
});
},
// 详情页秒杀逻辑
detail : {
// 详情页初始化
init : function (params) {
// ******1。手机验证和登录,2。计时交互
// *******规划我们的交互流程
// 利用jquery的cookie插件在cookie中查找手机号
var killPhone = $.cookie('killPhone');
// 访问参数中对应的数据(javascript的访问方式)
var seckillId = params['seckillId'];
var startTime = params['startTime'];
var endTime = params['endTime'];
// 验证手机号
if (!seckill.validatePhone(killPhone)) { // 未登录
// 获取弹出层对象
var killPhoneModal = $("#killPhoneModal");
// 显示弹出层
killPhoneModal.modal({
show : true, // 显示弹出层
backdrop : 'static', // 禁止位置关闭
keyboard : false
// 关闭键盘事件
});
// 为手机号的提交按钮绑定点击事件
$('#submitPhone')
.click(
function () {
// 获取用户输入的手机号
var inputPhone = $('#killPhone').val();
// 提交的电话号码有效
if (seckill.validatePhone(inputPhone)) {
// 电话写入cookie
// {expires:7,path:'/seckill'}表示这个cookie的有效期为7天,
// 只有访问本域名下/seckill路径才会在request中带上这个数据到cookie
$.cookie('killPhone', inputPhone, {
expires : 7,
path : '/seckill'
})
// 刷新页面
window.location.reload();
} else { // 提交的电话号码无效
// 显示手机号输入错误的提示信息(先隐藏节点,再写入数据,再显示出来,添加缓慢出现的显示效果)
$('#killPhoneMessage')
.hide()
.html(
'<label class="label label-danger">手机号错误!</label>')
.show(500);
}
});
}
// 已经登录
seckill.seckillView(seckillId);
}
}
}