在前面几节中已经完成了service层和dao层,到目前为止只是后端的设计与编写,这节就要设计到前端的设计了。下面开始总结下这个秒杀业务前端有哪些要点:
1. 前端页面的流程
首先是列表页,点某个商品进入详情页,在这里会有个判断是否用户已登录的逻辑。如果已登录则进入详情页展示逻辑,如果用户未登录则让用户登录,将用户的信息写入cookie后再进入展示逻辑。对于详情页,它首先拿到系统的当前时间,将其与当前秒杀单的秒杀开始时间和秒杀结束时间作比较。若大于秒杀结束结束时间则显示秒杀已结束,若小于秒杀开始时间则显示秒杀未开始和倒计时。若在秒杀时间之内或者倒计时结束则显示商品秒杀地址,用户点击,执行秒杀,返回执行结果。
2. Restful接口设计
具体什么是Restful呢?他是一种url设计规范,一种资源状态和资源状态的转移,关于Restful知识的具体讲解可以看这篇博文:我所理解的RESTful Web API
业务的秒杀API设计如下:
Get/seckill/list 秒杀列表
Get/seckill/{id}/detail 详情页
Get/seckill/time/now 系统时间
Post/seckill/{id}/exposer 暴露秒杀
Post/seckill/{id}/execution 执行秒杀
其中:Get表示查询操作,Post表示添加/修改操作, Put表示修改操作,DELETE表示删除操作。通过不同的提交方式来表示它的动作,而后面的url设计遵循的规范是:/模块/资源/{标识}/集合1/...
3. SpringMVC框架
这个应该是这个案例的重点知识点了吧,前面介绍了Mybatis和Spring的整合,现在来介绍Spring如何整合SpringMVC。首先是在web.xml中注册SpringMVC的核心控制器DispatcherServlet,并配置DispatcherServlet需要加载的配置文件。
web.xml
1 <web-app xmlns="http://java.sun.com/xml/ns/javaee" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 4 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 5 version="3.0" 6 metadata-complete="true"> 7 <!--修改servlet版本为3.0--> 8 9 <!-- 配置DispatcherServlet--> 10 <servlet> 11 <servlet-name>seckill-servlet</servlet-name> 12 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 13 <!--配置springMVC需要加载的配置文件 14 spring-dao.xml, spring-service.xml, spring-web.xml 15 框架整合顺序:mybatis->spring->springMVC 16 --> 17 <init-param> 18 <param-name>contextConfigLocation</param-name> 19 <param-value>classpath:spring/spring-*.xml</param-value> 20 </init-param> 21 </servlet> 22 23 <servlet-mapping> 24 <servlet-name>seckill-servlet</servlet-name> 25 <!--默认匹配所有的请求--> 26 <url-pattern>/</url-pattern> 27 </servlet-mapping> 28 29 </web-app>
可以看到SpringMVC需要加载的文件分别是Spring的三个配置文件,spring-dao.xml,spring-service.xml,spring-web.xml。这三个配置文件分别配置了不同层上的东西,dao层,service层和web层。现在还没有spring-web.xml这个文件,在resource目录下的spring目录下新建这个文件用来配置web层的有关配置。
spring-web.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:mvc="http://www.springframework.org/schema/mvc" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-3.0.xsd 10 http://www.springframework.org/schema/mvc 11 http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 12 <!--配置springMVC --> 13 <!--1:开启springMVC注解模式--> 14 <!--简化配置: 15 (1)自动注册DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter 16 (2)提供一系列:数据绑定,数字和日期format @NumberFormat,@DataTimeFormat, 17 xml,json默认读写支持--> 18 <mvc:annotation-driven/> 19 20 <!-- 2:servlet-mapping 映射路劲:"/" --> 21 <!-- 静态资源默认servlet配置 22 (1).加入对静态资源的处理:js,gif,png 23 (2).允许使用"/"做整体映射 24 --> 25 <mvc:default-servlet-handler/> 26 27 <!--3:配置jsp显示ViewResolver--> 28 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 29 <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> 30 <property name="prefix" value="/WEB-INF/jsp/"/> 31 <property name="suffix" value=".jsp"/> 32 </bean> 33 34 <!--4:扫描web相关的bean--> 35 <context:component-scan base-package="org.seckill.web"/> 36 </beans>
SpringMVC框架配置完成之后便开始编写业务的Controller,分发调度请求并调用Service层中相应的service来处理这些请求并返回模型和视图。在org.seckill目录下新建文件目录web用来存放Controller。新建SeckillController,负责对于Seckill这个资源模块的请求调度,代码如下:
SeckillController
1 package org.seckill.web; 2 3 import org.seckill.dao.SeckillResult; 4 import org.seckill.dto.Exposer; 5 import org.seckill.dto.SeckillExecution; 6 import org.seckill.entity.Seckill; 7 import org.seckill.enums.SeckillStatEnum; 8 import org.seckill.exception.RepeatKillException; 9 import org.seckill.exception.SeckillCloseException; 10 import org.seckill.exception.SeckillException; 11 import org.seckill.service.SeckillService; 12 import org.slf4j.Logger; 13 import org.slf4j.LoggerFactory; 14 import org.springframework.beans.factory.annotation.Autowired; 15 import org.springframework.stereotype.Controller; 16 import org.springframework.ui.Model; 17 import org.springframework.web.bind.annotation.*; 18 19 import java.util.Date; 20 import java.util.List; 21 22 /** 23 * Created by yuxue on 2016/10/17. 24 */ 25 @Controller 26 @RequestMapping("/seckill")// url:/模块/资源/{id}/细分 / seckill/list 27 public class SeckillController { 28 private final Logger logger= LoggerFactory.getLogger(this.getClass()); 29 30 @Autowired 31 private SeckillService seckillService; 32 33 @RequestMapping(value="/list", method = RequestMethod.GET ) 34 public String list(Model model) { 35 //获取列表页 36 List<Seckill> list=seckillService.getSeckillList(); 37 model.addAttribute("list",list); 38 //list.jsp+model=ModelAndView 39 return "list"; 40 } 41 42 @RequestMapping(value = "/{seckillId}/detail",method=RequestMethod.GET) 43 public String detail(@PathVariable("seckillId") Long seckillId,Model model){ 44 if(seckillId==null){ 45 return "redirect:/seckill/list";//请求重定向 46 } 47 Seckill seckill=seckillService.getById(seckillId); 48 if(seckill==null){ 49 return "forward:/seckill/list"; //请求转发 50 } 51 model.addAttribute("seckill",seckill); 52 return "detail"; 53 } 54 55 //ajax json 56 /* 57 @ResponseBody表示该方法的返回结果直接写入HTTP response body中 58 一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径, 59 加上@ResponseBody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。 61 */ 62 @RequestMapping(value = "/{seckillId}/exposer",method=RequestMethod.GET, 63 produces={"application/json;charset=UTF-8"}) 64 @ResponseBody 65 public SeckillResult<Exposer> exposer(@PathVariable Long seckillId){ 66 SeckillResult<Exposer> result; 67 try{ 68 Exposer exposer=seckillService.exportSeckillUrl(seckillId); 69 result=new SeckillResult<Exposer>(true,exposer); 70 }catch(Exception e){ 71 logger.error(e.getMessage(),e); 72 result=new SeckillResult<Exposer>(false,e.getMessage()); 73 } 74 return result; 75 } 76 77 /* 78 从客户端的Cookie中获得用户手机号码,将required属性设置为false,否则若浏览器的cookie中未保存 79 手机号码的话Spring会报错,这里设置为false,将对用户手机号的验证写在我们自己的判断逻辑中 80 */ 81 @RequestMapping(value="/{seckillId}/{md5}/execution",method=RequestMethod.POST, 82 produces = "application/json;charset=UTF-8") 83 @ResponseBody 84 public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") Long seckillId, 85 @PathVariable("md5") String md5, 86 @CookieValue(value="killphone",required=false)Long phone){ 87 if(phone==null){ 88 return new SeckillResult<SeckillExecution>(false,"未注册"); 89 } 90 SeckillResult<SeckillExecution> result; 91 try {
94 SeckillExecution execution = seckillService.executeSeckill(seckillId, phone, md5); 95 return new SeckillResult<SeckillExecution>(true,execution); 96 }catch (RepeatKillException e){ 97 //logger.error(e.getMessage(),e);没有必要打印日志,因为是系统允许的异常 98 SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL); 99 return new SeckillResult<SeckillExecution>(false,seckillExecution); 100 }catch(SeckillCloseException e){ 101 SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStatEnum.END); 102 return new SeckillResult<SeckillExecution>(false,seckillExecution); 103 }catch (Exception e){ 104 logger.error(e.getMessage(),e); 105 SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR); 106 return new SeckillResult<SeckillExecution>(false,seckillExecution); 107 } 108 } 109 110 @RequestMapping(value="/time/now", method = RequestMethod.GET)
public SeckillResult<Long> time(){ 113 Date now=new Date(); 114 return new SeckillResult<Long>(true,now.getTime()); 115 } 116 117 }
分析:
1.return "redirect:/seckill/list"和return "forward:/seckill/list" 分别实现了请求重定向和请求转发。
2.关于注解@ResponseBody和@RequestMapping中的produces
1)@ResponseBody
该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。即我们的Controller执行后不需要跳转到别的什么页面,只需要返回某种格式(json,xml)的数据时候就可以用使用这个注解啦!而@RequestMapping中的produces="application/json;charset=UTF-8"便指定了返回的内容类型为json,编码格式为UTF-8。具体解释见这篇博文:Spring MVC之@RequestMapping 详解以及这篇@RequestBody, @ResponseBody 注解详解。
3.@CookieValue注解: 从Http请求头中的Cookie提取指定的某个Cookie,这里required=false表示如果没有这个cookie的话不让SpringMVC报错,而是在我们自己写的逻辑中处理。
4.public SeckillResult<SeckillExecution> execute() 这个方法中用到了个泛型SeckillResult<T>,这个类也是个数据传输对象,在dto包下新建类SeckillResult
SeckillResult
1 package org.seckill.dao; 2 3 /** 4 * Created by yuxue on 2016/10/18. 5 */ 6 //所有ajax请求返回类型,封装json结果 7 public class SeckillResult<T> { 8 9 private boolean success; 10 11 private T data; 12 13 private String error; 14 15 public SeckillResult(boolean success, T data) { 16 this.success = success; 17 this.data = data; 18 } 19 20 21 public SeckillResult(boolean success, String error) { 22 this.success = success; 23 this.error = error; 24 } 25 26 public boolean isSuccess() { 27 return success; 28 } 29 30 public void setSuccess(boolean success) { 31 this.success = success; 32 } 33 34 public T getData() { 35 return data; 36 } 37 38 public void setData(T data) { 39 this.data = data; 40 } 41 42 public String getError() { 43 return error; 44 } 45 46 public void setError(String error) { 47 this.error = error; 48 } 49 }
分析:
一开始我不明白为什么要用这个类,打完代码后豁然醒悟:这个类其实封装了所有的请求返回类型!!!对于与Seckill有关的业务SeckillService,它里面有多个方法,一些方法要返回给前端数据,这里的问题便是数据类型可能会有很多种,比如暴露秒杀地址方法返回的是Exposer对象而execute方法返回的是SeckillExecution, 那么便用SeckillResult这个泛型来统一封装来自SeckillService的返回类型。前端要用到后端传来的数据时直接就从这里面去取相应的类型就好。这里是个很重要的设计思路:对于上层只提供统一的一致的接口,而底层复杂的细节则由这个接口封装起来,这些是我个人的理解,不知道对不对。
4. 基于Bootstrap开发页面
终于到了开发前端页面的时候了,老实说的话前端真的是不是很明白啊!感觉前端要记的东西太多了,各种杂七杂八的东西。这个案例是基于Bootstrap这个框架来开发前端页面的,Bootstrap其实帮你写好了前端控件的样式,拿过来直接用就可以开发出显示效果不错的前端页面了,自己不需要去写css样式。总共要写的页面有两个:list.jsp秒杀商品列表页和detail.jsp秒杀上屏详情页。
首先是编写list.jsp页面,为了使用Bootstrap框架,这个业务采用的是在线引入的方式所以必须要联网,总共要引入的文件有3个:Bootstrap的样式css文件,依赖的jQuery文件和核心javascript文件。关于Bootstrap的安装使用方式可以去Bootstrap中文网去查看一下。在webapp/WEB-INF目录下新建jsp目录来存放我们写的jsp文件
list.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <%--引入jstl--%> 3 <%@include file="common/tag.jsp"%> 4 <!DOCTYPE html> 5 <head> 6 <title>秒杀列表页</title> 7 <%--jsp页面静态包含--%> 8 <%@include file="common/head.jsp"%> 9 </head> 10 <body> 11 <%--页面显示部分--%> 12 <div class="container"> 13 <div class="panel panel-default"> 14 <div clas="panel-heading text-center"> 15 <h2>秒杀列表</h2> 16 </div> 17 <div class="panel-body"> 18 <table class="table table-hover"> 19 <thead> 20 <tr> 21 <th>名称</th> 22 <th>库存</th> 23 <th>开始时间</th> 24 <th>结束时间</th> 25 <th>创建时间</th> 26 <th>详情页</th> 27 </tr> 28 </thead> 29 <tbody> 30 <c:forEach var="sk" items="${list}"> 31 <tr> 32 <td>${sk.name}</td> 33 <td>${sk.number}</td> 34 <td> 35 <fmt:formatDate value="${sk.startTime}" pattern="yyyy-MM-dd HH:mm:ss"/> 36 </td> 37 <td> 38 <fmt:formatDate value="${sk.endTime}" pattern="yyyy-MM-dd HH:mm:ss"/> 39 </td> 40 <td> 41 <fmt:formatDate value="${sk.createTime}" pattern="yyyy-MM-dd HH:mm:ss"/> 42 </td> 43 <td> 44 <%--target="_blank" 使得点击超链接后弹出的是新的页面--%> 45 <a class="btn btn-info" href="/seckill/${sk.seckillId}/detail" target="_blank">link</a> 46 </td> 47 </tr> 48 </c:forEach> 49 </tbody> 50 </table> 51 </div> 52 </div> 53 </div> 54 </body> 55 <!-- jQuery文件。务必在bootstrap.min.js 之前引入 --> 56 <script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script> 57 <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> 58 <script src="http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> 59 </html>
这里将页面要用到的jstl标签单独放到tag.jsp中
1 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 2 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
还有引入的head.jsp,作用是兼容平板,手机的页面显示,这也是bootstrap作为响应式布局的特点
1 <meta charset="utf-8"> 2 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 3 <meta name="viewport" content="width=device-width, initial-scale=1"> 4 <!-- Bootstrap --> 5 <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css">
在webapp/WEB-INF目录下新建详情页面detail.jsp
detail.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <!DOCTYPE html> 3 <html lang="zh-cn"> 4 <head> 5 <title>秒杀详情页</title> 6 <%--jsp页面静态包含--%> 7 <%@include file="common/head.jsp"%> 8 </head> 9 <body> 10 <div class="container"> 11 <div class="panel panel-default text-center"> 12 <div class="pannel-heading"> 13 <h1>${seckill.name}</h1> 14 </div> 15 <div class="panel-body"> 16 <h2 class="text-danger"> 17 <%--显示time图标--%> 18 <span class="glyphicon glyphicon-time"></span> 19 <%--展示倒计时--%> 20 <span class="glyphicon" id="seckill-box"></span> 21 </h2> 22 </div> 23 </div> 24 </div> 25 <%--登录弹出层,输出电话号码--%> 26 <div id="killPhoneModal" class="modal fade"> 27 <div class="modal-dialog"> 28 <div class="modal-content"> 29 <div class="modal-header"> 30 <h3 class="modal-title text-center"> 31 <span class="glyphicon glyphicon-phone"></span> 32 </h3> 33 </div> 34 <div class="modal-body"> 35 <div class="row"> 36 <div class="col-xs-8 col-xs-offset-2"> 37 <input type="text" name="killphone" id="killPhoneKey" 38 placeholder="填手机号^o^" class="form-control" > 39 </div> 40 </div> 41 </div> 42 43 <div class="modal-footer"> 44 <!--验证信息--> 45 <span id="killPhoneMessage" class="glyphicon"></span> 46 <button type="button" id="killPhoneBtn" class="btn btn-success"> 47 <span class="glyphicon glyphicon-phone"></span> 48 </button> 49 </div> 50 </div> 51 </div> 52 </div> 53 </body> 54 <!-- jQuery文件。务必在bootstrap.min.js 之前引入 --> 55 <script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script> 56 <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> 57 <script src="http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> 58 59 <%--使用CDN获取公共js http://www.bootcdn.cn/ --%> 60 <%--jQuery cookie操作插件--%> 61 <script src="http://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script> 62 <%--jQuery countdown倒计时插件--%> 63 <script src="http://cdn.bootcss.com/jquery.countdown/2.1.0/jquery.countdown.min.js"></script> 64 <%--开始编写交互逻辑--%> 65 <script src="/resources/script/seckill.js" type="text/javascript"></script><%--老师说这里有个小坑,结尾必须写</script>这种形式,不能是/>,否则下面的js不会加载--%> 66 <script type="text/javascript"> 67 $(function () { 68 //使用El表达式传入参数 69 seckill.detail.init({ 70 seckillId:${seckill.seckillId}, 71 startTime:${seckill.startTime.time}, 72 endTime:${seckill.endTime.time} 73 }); 74 }); 75 </script> 76 </html>
1.开启tomacat服务器,测试网页能否正常访问,跳转。这里出现了个小问题:list.jsp中的跳转的detail.jsp的超链接<a class="btn btn-info" href="/seckill/${sk.seckillId}/detail" target="_blank">link</a>我把其中的detail写成了detail.jsp结果Controller不能拦截到路径,查了下Controller中RequestMapping的配置路径为@RequestMapping(value = "/{seckillId}/detail",method=RequestMethod.GET),明白了,原来这个路径要“一模一样”,我之前认为只要在SpringMVC的DispatcherServlet里配置拦截路径为所有路径的话,那么就是拦截所有的html和jsp,所以后缀名什么的应该不重要,只要名字相同就可......傻叉了一回....,这个细节以后要注意。
2.关于<div id="killPhoneModal" class="modal fade">,这是个Bootstrap中的模态框插件,模态框(Modal)是覆盖在父窗体上的子窗体。通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动。子窗体可提供信息、交互等。在模态框中需要注意两点:第一是 .modal,用来把 <div> 的内容识别为模态框。第二是 .fade class。当模态框被切换时,它会引起内容淡入淡出。具体的使用见:http://www.runoob.com/bootstrap/bootstrap-modal-plugin.html
在webapp/resources目录下新建script目录,用来存放我们写的的页面交互脚本。新建seckill.js
1 //存放主要交互逻辑js代码 2 //javascript 模块化 3 var seckill={ 4 //封装秒杀相关ajax的url 5 URL:{ 6 now:function () { 7 return '/seckill/time/now'; 8 }, 9 exposer:function (seckillId) { 10 return '/seckill/'+seckillId+'/exposer'; 11 }, 12 execution:function(seckillId,md5){ 13 return 'seckill/'+seckillId+'/'+md5+'/execution'; 14 } 15 }, 16 handleSeckillkill:function (seckillId,node) { 17 //处理秒杀逻辑,控制现实逻辑,执行秒杀 18 node.hide().html('<button class="btn btn-primary btn-lg" id="killBtn">开始秒杀</button>'); 19 $.post(seckill.URL.exposer(seckillId),{},function (result) { 20 //在回调函数中,执行交互流程 21 if(result&&result['success']){ 22 var exposer=result['data']; 23 if(exposer['exposed']){ 24 //开启秒杀 25 var md5=exposer['md5']; 26 var killUrl=seckill.URL.execution(seckillId,md5); 27 console.log("killUrl:"+killUrl); 28 //绑定一次点击事件 29 $('#killBtn').one('click',function () { 30 //执行秒杀请求 31 //1.先禁用按钮 32 $(this).addClass('disabled'); 33 //2.发送秒杀请求执行秒杀 34 $.post(killUrl,{},function (result) { 35 if(result&&result['success']){ 36 var killResult=result['data']; 37 var state=killResult['data']; 38 var stateInfo=killResult['stateInfo']; 39 //3.显示秒杀结果 40 node.html('<span class="label label-success">'+stateInfo+'</span>'); 41 } 42 }); 43 }); 44 node.show(); 45 }else{ 46 //未开启秒杀 47 var now=exposer['now']; 48 var start=exposer['start']; 49 var end=exposer['end']; 50 //重新计算计时逻辑 51 seckillId.countdown(seckillId,now,start,end); 52 } 53 }else{ 54 console.log('result:'+result); 55 } 56 }); 57 }, 58 //验证手机号 59 validatePhone: function (phone) { 60 if (phone && phone.length == 11 && !isNaN(phone)) { 61 return true; 62 } else { 63 return false; 64 } 65 }, 66 countdown:function (seckillId,nowTime,startTime,endTime) { 67 var seckillBox=$('#seckill-box'); 68 //时间判断 69 if(nowTime>endTime){ 70 //秒杀结束 71 seckillBox.html('秒杀结束!'); 72 }else if(nowTime<startTime){ 73 //秒杀未开始,计时事件绑定 74 var killTime=new Date(startTime+1000); 75 seckillBox.countdown(killTime,function(event){ 76 //时间格式 77 var format=event.strftime('秒杀倒计时:%D天 %H时 %M分 %S秒'); 78 seckillBox.html(format); 79 /*时间完成后回调事件*/ 80 }).on('finish.countdown',function () { 81 //获取秒杀地址,控制现实逻辑,执行秒杀 82 seckill.handleSeckillkill(seckillId,seckillBox); 83 }); 84 }else{ 85 //秒杀开始 86 seckill.handleSeckillkill(seckillId,seckillBox); 87 } 88 }, 89 //详情秒杀页逻辑 90 detail: { 91 //详情页初始化 92 init: function (params) { 93 //手机验证和登陆,计时交互 94 //规划我们的交互流程 95 //在cookie中查找手机号 96 var killPhone = $.cookie('killPhone'); 97 var startTime = params['startTime']; 98 var endTime = params['endTime']; 99 var seckillId = params['seckillId']; 100 //验证手机号码 101 if (!seckill.validatePhone(killPhone)){ 102 //绑定phone 103 //控制输出 104 var killPhoneModal=$('#killPhoneModal'); 105 //显示弹出层 106 killPhoneModal.modal({ 107 show:true,//显示弹出层 108 backdrop:'static',//禁止位置关闭 109 keyboard:false//关闭键盘事件 110 }); 111 $('#killPhoneBtn').click(function(){ 112 var inputPhone=$('#killPhoneKey').val(); 113 console.log('inputPhone='+inputPhone);//TODO 114 if(seckill.validatePhone(inputPhone)){ 115 //电话写入cookie 116 $.cookie('killPhone',inputPhone,{expires:7,path:'/seckill'}); 117 //刷新页面 118 window.location.reload(); 119 }else{ 120 $('#killPhoneMessage').hide().html('<label class="label label-danger">手机号错误!</label>').show(300); 121 } 122 }); 123 } 124 //已经登录 125 var startTime = params['startTime']; 126 var endTime = params['endTime']; 127 var seckillId = params['seckillId']; 128 $.get(seckill.URL.now(),{},function(result){ 129 if(result&&result['success']){ 130 var nowTime=result['data']; 131 //时间判断,计时交互 132 seckill.countdown(seckillId,nowTime,startTime,endTime); 133 }else{ 134 console.log('result:'+result); 135 } 136 } 137 ); 138 } 139 } 140 }
还是吐槽一句:前端真心不熟啊。。。。js,jQuery什么的,要学的东西还有很多啊。
到现在前端的编写算是完成了,下一节讲的是怎么优化这个业务让其能承受更多的并发量,呼呼呼,写到这里不容易啊,哪里搞错了的望指正,谢谢。