在前面几节中已经完成了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 }
View Code

还是吐槽一句:前端真心不熟啊。。。。js,jQuery什么的,要学的东西还有很多啊。

到现在前端的编写算是完成了,下一节讲的是怎么优化这个业务让其能承受更多的并发量,呼呼呼,写到这里不容易啊,哪里搞错了的望指正,谢谢。

posted on 2016-11-18 15:58  f91og  阅读(1090)  评论(0编辑  收藏  举报