关于课程:
《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); } } }
一,用/time/now可以减轻服务器负担,exposer还得每一秒都查询数据库
其余不知道
//存放主要的交互逻辑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() { // 倒计时结束的回调函数 // 倒计时结束 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); } } }
这是我参考老师的代码,按照自己的理解写的逻辑代码,我感觉这样更好理解一些,经测试可以实现功能。