SSM框架學習之高併發秒殺業務--筆記4-- web層

在前面幾節中已經完成了service層和dao層,到目前爲止只是後端的設計與編寫,這節就要設計到前端的設計了。下面開始總結下這個秒殺業務前端有哪些要點:javascript

1. 前端頁面的流程css

   首先是列表頁,點某個商品進入詳情頁,在這裏會有個判斷是否用戶已登陸的邏輯。若是已登陸則進入詳情頁展現邏輯,若是用戶未登陸則讓用戶登陸,將用戶的信息寫入cookie後再進入展現邏輯。對於詳情頁,它首先拿到系統的當前時間,將其與當前秒殺單的秒殺開始時間和秒殺結束時間做比較。若大於秒殺結束結束時間則顯示秒殺已結束,若小於秒殺開始時間則顯示秒殺未開始和倒計時。若在秒殺時間以內或者倒計時結束則顯示商品秒殺地址,用戶點擊,執行秒殺,返回執行結果。html

2. Restful接口設計前端

 具體什麼是Restful呢?他是一種url設計規範,一種資源狀態和資源狀態的轉移,關於Restful知識的具體講解能夠看這篇博文:我所理解的RESTful Web APIjava

 業務的秒殺API設計以下:node

   Get/seckill/list     秒殺列表jquery

   Get/seckill/{id}/detail   詳情頁web

   Get/seckill/time/now  系統時間ajax

   Post/seckill/{id}/exposer  暴露秒殺spring

   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什麼的,要學的東西還有不少啊。

到如今前端的編寫算是完成了,下一節講的是怎麼優化這個業務讓其能承受更多的併發量,呼呼呼,寫到這裏不容易啊,哪裏搞錯了的望指正,謝謝。

相關文章
相關標籤/搜索