1、首先引入springMVC
~、修改web.xml,引入springMVC的DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>imoocDemo1</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 修改servlet版本為3.1 -->
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>seckill-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springMVC需要接在的配置文件
spring-dao.xml,spring-service.xml,spring-web.xml
Mybatis -》spring ->springMVC
-->
<!-- 此處如果不配置的話仅乓,spring會默認的去WEB-INF下去查找默認規(guī)范的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>seckill-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2、編寫Controller
Controller // 類似與@Service和@Component
@RequestMapping("/seckill") // url:/模塊/資源/{id}/細分
public class SeckillController {
// private org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());
public SeckillController() {
}
@Autowired
private SeckillService seckillService;
/**
* 進入秒殺列表
*
* @param model 模型數(shù)據(jù)蓬戚,存放秒殺商品的信息
* @return 秒殺列表詳情頁面
*/
@RequestMapping(value = "/{showPage}/list", method = RequestMethod.GET)
public String list(@PathVariable("showPage") int showPage, Model model) {
System.out.println("經(jīng)過controler來到list界面");
List<Seckill> seckillList = seckillService.getSeckillList(showPage);
int pageCount = seckillService.getPageCount();
model.addAttribute("list", seckillList);
model.addAttribute("pageCount", pageCount);
return "list"; // WEN-INF/list.jsp
}
@RequestMapping(value = "/{showPage}/listPage", method = RequestMethod.GET)
@ResponseBody
public List<Seckill> pageList(@PathVariable("showPage") int showPage, Model model) {
List<Seckill> seckillList = seckillService.getSeckillList(showPage);
Iterator iterator = seckillList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("=======================");
model.addAttribute("list", seckillList);
return seckillList; // WEN-INF/list.jsp
}
/**
* 根據(jù)id搜索商品夸楣,展示商品
*
* @param seckillId
* @param model
* @return
*/
@RequestMapping(value = "/{seckillId}/detail", method = RequestMethod.GET)
public String detail(@PathVariable("seckillId") Long seckillId, Model model) {
if (seckillId == null) {
return "redirect:/seckill/list";
}
Seckill seckill = seckillService.getById(seckillId);
if (seckill == null) {
return "forward:/seckill/list";
}
model.addAttribute("seckill", seckill);
return "detail";
}
/**
* 暴露秒殺的接口的方法
*
* @param seckillId
* @return 根據(jù)用戶秒殺的商品id進行業(yè)務邏輯判斷,返回不同的json實體結(jié)果
*/
@RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.POST)
@ResponseBody // 告訴springmvc,返回的類型作為ajax輸出
public SeckillResult<Exposer> exposer(@PathVariable("seckillId") Long seckillId) {
SeckillResult<Exposer> result;
try {
Exposer exposer = seckillService.exportSeckillUrl(seckillId);
result = new SeckillResult<Exposer>(true, exposer);
} catch (Exception e) {
// logger.error(e.getMessage(), e);
result = new SeckillResult<Exposer>(false, e.getMessage());
}
return result;
}
/**
* 用戶執(zhí)行秒殺裕偿,在頁面點擊相關的秒殺連接洞慎,進入后獲取對應的參數(shù)進行判斷 返回響應的json實體結(jié)果,前端在進行處理
*
* @param seckillId 秒殺的商品嘿棘,對應的秒殺時的id
* @param md5 一個被鹽值加密過的md5加密值
* @param userPhone 參與秒殺用戶的手機號碼劲腿,當做賬號密碼使用
* @return 參與秒殺的結(jié)果,為json數(shù)據(jù)
*/
@RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST)
@ResponseBody
public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") long seckillId,
@PathVariable("md5") String md5, @CookieValue(value = "userPhone", required = false) Long userPhone) {
// 如果用戶的手機號碼為空鸟妙,說明用戶沒有完成注冊
if (userPhone == null) {
return new SeckillResult<>(false, "沒有注冊");
}
try {
// 這里換成儲存過程
//SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5);
SeckillExecution execution = seckillService.executeSeckillProcedure(seckillId, userPhone, md5);
return new SeckillResult<>(true, execution);
} catch (RepeaKillException e1) {
// 重復秒殺
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL);
return new SeckillResult<>(false, execution);
} catch (SeckillCloseException e2) {
// 秒殺關閉
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.END);
return new SeckillResult<>(false, execution);
} catch (SeckillException e) {
// 不能判斷的異常
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR);
return new SeckillResult<>(false, execution);
}
}
@RequestMapping(value = "/time/now", method = RequestMethod.GET)
@ResponseBody
public SeckillResult<Date> time() {
Date date = new Date();
return new SeckillResult<>(true, date);
}
}
3焦人、建立一個全局ajax請求返回類,返回json數(shù)據(jù)
com.seckill.dto.SeckillResult
package com.seckill.dto;
/**
* 封裝所有的ajax請求返回類型重父,方便返回json
*
* @author hyh47
*
*/
public class SeckillResult<T> {
//執(zhí)行的結(jié)果
private boolean success;
//泛型數(shù)據(jù)
private T data;
//字符串類型的具體error
private String error;
public SeckillResult(boolean success, T data) {
super();
this.success = success;
this.data = data;
}
public SeckillResult(boolean success, String error) {
super();
this.success = success;
this.error = error;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
@Override
public String toString() {
return "SeckillResult [success=" + success + ", data=" + data + ", error=" + error + "]";
}
}
4花椭、前臺頁面的編寫
image.png
對于jQuery和Bootstrap的引入,可以使用本地路徑方式和CDN鏈接兩種引入方式
首先需要一個列表頁面:
<%@page import="com.fasterxml.jackson.annotation.JsonInclude.Include" %>
<%@ page language="java" import="java.util.*" pageEncoding="utf-8" %>
<%@include file="common/tag.jsp" %>
<html lang="zh-CN">
<head>
<%@include file="common/head.jsp" %>
<!-- 上述3個meta標簽*必須*放在最前面房午,任何其他內(nèi)容都*必須*跟隨其后矿辽! -->
<title>秒殺列表頁面</title>
<script type="text/javascript" src="<%=path%>/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="<%=path%>/js/jquery.paginate.js"></script>
<script type="text/javascript" src="<%=path%>/js/bootstrap.min.js"></script>
<style>
.demo {
width: 580px;
padding: 10px;
margin: 10px auto;
border: 1px solid #fff;
background-color: #f7f7f7;
}
</style>
</head>
<body>
<div class="panel panel-default">
<div class="panel-heading text-center">
<h2>秒殺列表</h2>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
<th>名稱</th>
<th>庫存</th>
<th>開始時間</th>
<th>結(jié)束時間</th>
<th>創(chuàng)建時間</th>
<th>詳情頁</th>
</tr>
</thead>
<tbody>
<div id="listTable">
<c:forEach items="${list}" var="sk" varStatus="skStatus">
<tr>
<td id="name${skStatus.index}">${sk.name}</td>
<td id="number${skStatus.index}">${sk.number}</td>
<td id="startTime${skStatus.index}"><fmt:formatDate value="${sk.startTime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td id="endTime${skStatus.index}"><fmt:formatDate value="${sk.endTime }" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td id="createTime${skStatus.index}"><fmt:formatDate value="${sk.createTime }" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td><a id="seckillHref" class="btn btn-info" href="/seckill/${sk.seckillId}/detail" target="_blank">link</a></td>
</tr>
</c:forEach>
</div>
</tbody>
</table>
<div id="paginationdemo" class="demo">
<div id="demo4"></div>
</div>
</div>
</div>
<script type="text/javascript">
$(function () {
$("#demo4").paginate({
count: ${pageCount},
start: 1,
display: 5,
border: false,
text_color: '#79B5E3',
background_color: 'none',
text_hover_color: '#2573AF',
background_hover_color: 'none',
images: false,
mouse: 'press',
onChange: function (page) {
//$('._current','#paginationdemo').removeClass('_current').hide();
//$('#p'+page).addClass('_current').show();
$('._current', '#paginationdemo').text("page" + page);
//此處使用ajax.post方法進行訪問
$.get("/seckill/" + page + "/listPage", {}, function (result) {
var temp;
for (var i = 4; i >= 0; i--) {
temp = result[i];
if (temp != null) {
var startTime = new Date(temp['startTime']);
var endTime = new Date(temp['endTime']);
var createTime = new Date(temp['createTime']);
var seckillId = temp['seckillId'];
console.log(seckillId);
$('#name' + i).text(temp['name']);
$('#number' + i).text(temp['number']);
$('#startTime' + i).text(startTime.toLocaleString());
$('#endTime' + i).text(endTime.toLocaleString());
$('#createTime' + i).text(createTime.toLocaleString());
document.getElementById("seckillHref").setAttribute("href", "/seckill/" + seckillId + "/detail");
} else {
$('#name' + i).text(null);
$('#number' + i).text(null);
$('#startTime' + i).text(null);
$('#endTime' + i).text(null);
$('#createTime' + i).text(null);
}
}
});
}
});
});
</script>
</body>
</html>
此頁面中使用了jquery.paginate.js的分頁插件http://www.jq22.com/jquery-info34
然后在前面部分對于一些通用的JSP設置和靜態(tài)文件(CSS等)的導入,采用了JSP的靜態(tài)導入:
<%@include file="common/tag.jsp" %>郭厌、<%@include file="common/head.jsp" %>
而相應的jsp如下:
tag.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
head.jsp:
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 檢測設備終端的寬度和高度 -->
<meta charset="utf-8">
<link rel="stylesheet" href="<%=path%>/css/bootstrap.min.css"
type="text/css">
<link rel="stylesheet" href="<%=path%>/css/bootstrap-theme.min.css"
type="text/css">
<link rel="stylesheet" href="<%=path%>/css/style.css" type="text/css">
點擊商品詳情后袋倔,需要的是商品秒殺詳情頁面:
detail.jsp:
<%@page import="com.fasterxml.jackson.annotation.JsonInclude.Include" %>
<%@ page language="java" import="java.util.*" pageEncoding="utf-8" %>
<%@include file="common/tag.jsp" %>
<html lang="zh-CN">
<head>
<%@include file="common/head.jsp" %>
<!-- 上述3個meta標簽*必須*放在最前面,任何其他內(nèi)容都*必須*跟隨其后折柠! -->
<title>秒殺詳情頁面</title>
<script type="text/javascript" src="<%=path%>/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="<%=path%>/js/bootstrap.min.js"></script>
<script type="text/javascript"
src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script type="text/javascript"
src="https://cdn.bootcss.com/jquery.countdown/2.2.0/jquery.countdown.min.js"></script>
<!-- <script type="text/javascript"
src="https://cdn.bootcss.com/bootstrap-modal/2.2.6/css/bootstrap-modal-bs3patch.min.css"></script>
</head> -->
<body>
<div class="container">
<div class="panel panel-default text-center">
<div class="panel-heading">
<h1>${seckill.name}</h1>
</div>
<div class="panel-body ">
<h2 class="text-danger">
<!-- 顯示time圖標 -->
<span class="glyphicon glyphicon-time"></span>
<!-- 顯示倒計時 -->
<span class="glyphicon" id="seckill-box">開始秒殺</span>
</h2>
</div>
</div>
</div>
<!-- 登錄彈出層宾娜,輸入電話 -->
<div id="userPhoneModal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title text-center">
<span class="glyphicon glyphicon-phone"></span>秒殺電話:
</h3>
</div>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<input type="text" name="userPhone" id="userPhoneKey"
placeholder="填寫手機號碼" class="form-control">
</div>
</div>
</div>
<div class="modal-footer">
<span id="userPhoneMessage" class="glyphicon"></span>
<button type="button" id="userPhoneBtn" class="btn btn-success">
<span class="glyphicon glyphicon-phone"></span> 提交
</button>
</div>
</div>
</div>
</body>
<script type="text/javascript" src="<%=path%>/js/seckill.js"></script>
<script type="text/javascript">
//采用jquery自動加載$(function(){});
//初始化init,init內(nèi)部存在著秒殺的邏輯
$(function () {
//詳情頁初始化
seckill.detail.init({
//采用jstl獲取需要的數(shù)據(jù),作為init函數(shù)的參數(shù)
//在函數(shù)內(nèi)部獲取參數(shù)的形式為: param['seckillId']
seckillId: ${seckill.seckillId},
startTime: ${seckill.startTime.time},//毫秒
endTime: ${seckill.endTime.time}
});
});
</script>
</html>
detail.jsp中再執(zhí)行過程中相應的一些js邏輯:
//存放主要交互邏輯js代碼
/**
* 使用模塊化javascript
*/
var seckill = {
//封裝秒殺相關ajax的url
URL: {
now: function () {
return "/seckill/time/now";
},
exposer: function (seckillId) {
return "/seckill/" + seckillId + "/exposer";
},
execution: function (seckillId, md5) {
return "/seckill/" + seckillId + "/" + md5 + "/execution";
}
},
validatePhone: function (phone) {
if (phone && phone.length == 11 && !isNaN(phone)) {
return true;
} else {
return false;
}
},
//詳情頁秒殺邏輯
detail: {
//詳情頁初始化
init: function (params) {
//手機驗證和登錄扇售,計時交互
//規(guī)劃系列交互流程
//在cookies中查找手機號
var userPhone = $.cookie("userPhone");
if (!seckill.validatePhone(userPhone)) {
console.log("為填寫手機號碼");
var userPhoneModal = $("#userPhoneModal");
/*
modal的選項:
backdrop(boolean前塔、或者String'static')指定一個靜態(tài)場景,
當用戶點擊模態(tài)框外部時不會關閉模態(tài)框
keyboard(boolean):當按下escape鍵時關閉模態(tài)框承冰,設置為false時則按鍵失效
show(boolean):當初始化時顯示模態(tài)框
*/
userPhoneModal.modal({
show: true, //顯示彈出層
backdrop: 'static', //精致位置關閉
keyboard: false //關閉鍵盤事件
});
/*
$.cookie(名稱,值,[option])
[option]參數(shù)說明:
expires:有限日期华弓,可以是一個整數(shù)或一個日期(單位:天).默認關閉瀏覽器則丟失
path:cookie值保存的路徑,默認與創(chuàng)建頁路徑一致
domin:cookie域名屬性困乒,默認與創(chuàng)建頁域名一樣寂屏。這個地方要相當注意,跨域的概念顶燕,如果要主域名二級域名有效則要設置 ".xxx.com"
secrue:一個布爾值凑保,表示傳輸cookie值時冈爹,是否需要一個安全協(xié)議涌攻。
*/
$("#userPhoneBtn").click(function () {
console.log("提交手機號碼按鈕被點擊");
var inputPhone = $("#userPhoneKey").val(); //獲得輸入的手機號碼
console.log("inputPhone" + inputPhone);
if (seckill.validatePhone(inputPhone)) {
//把手機號碼寫入cookie
$.cookie('userPhone', inputPhone, {
//expires : 7,//有效日期,默認為關閉瀏覽器則失效
path: '/seckill'
})
window.location.reload();
} else {
$("#userPhoneMessage").hide().html("<label class='label label-danger'>手機號碼錯誤</label>").show(1000);
}
});
} else {
console.log("在cookie中找到了手機號碼");
//已經(jīng)登錄了就開始進行計時交互
var startTime = params['startTime'];
var endTime = params['endTime'];
var seckillId = params['seckillId'];
console.log("開始秒殺時間:" + startTime + " 時間格式:" + startTime.time);
console.log("結(jié)束秒殺時間:" + endTime);
$.get(seckill.URL.now(), function (result) {
if (result && result['success']) {
var nowTime = result['data'];
console.log("服務器當前時間:" + nowTime);
seckill.countdown(seckillId, nowTime, startTime, endTime);
} else {
console.log("結(jié)果:" + result);
}
});
}
;
}
},
handlerSeckill: function (seckillId, mode) {
//獲得秒殺地址
mode.hide().html('<button class="btn btn-primary btn-lg" id="killBtn">開始秒殺</button>');
console.debug("開始進行秒殺地址獲取");
$.post(seckill.URL.exposer(seckillId), {}, function (result) {
//在回調(diào)函數(shù)中频伤,執(zhí)行交互流程
if (result && result['success']) {
var exposer = result['data'];
if (exposer['exposed']) {
console.log("有秒殺地址接口");
//開啟秒殺恳谎,獲取秒殺地址
var md5 = exposer['md5'];
var killUrl = seckill.URL.execution(seckillId, md5);
console.log("秒殺的地址為:" + killUrl);
//綁定一次點擊事件,使用one,防止用戶連續(xù)點擊按鈕,連續(xù)發(fā)送按鈕請求
$("#killBtn").one('click', function () {
console.log("開始進行秒殺因痛,按鈕被禁用");
//執(zhí)行秒殺請求婚苹,先禁用按鈕
$(this).addClass("disabled");
//發(fā)送秒殺請求
$.post(killUrl, {}, function (result) {
console.info(result);
var killResult = result['data'];
console.log(killResult['seckillId']);
var state = killResult['state'];
var stateInfo = killResult['stateInfo'];
console.log("秒殺狀態(tài):" + stateInfo);
//顯示秒殺結(jié)果
mode.html('<span class="label label-success">' + stateInfo + '</span>');
});
});
mode.show();
} else {
console.warn("還沒有暴露秒殺地址接口,無法進行秒殺");
//未開啟秒殺
var now = exposer['now']; //
var start = exposer['start'];
var end = exposer['end'];
//重新計算計時邏輯
seckill.countdown(seckillId, now, start, end);
}
} else {
console.log("result:" + result);
}
});
},
//倒計時交互
countdown: function (seckillId, nowTime, startTime, endTime) {
console.log("秒殺的商品ID:" + seckillId + ",服務器當前時間:" + nowTime + ",開始秒殺的時間:" + startTime + ",結(jié)束秒殺的時間" + endTime);
var seckillBox = $("#seckill-box");
//獲取時間戳進行實踐的比較
nowTime = new Date(nowTime).valueOf();
startTime = new Date(startTime).valueOf();
endTime = new Date(endTime).valueOf();
console.log("轉(zhuǎn)換后的Date類型當前時間戳" + nowTime);
console.log("轉(zhuǎn)換后的Date類型開始時間戳" + startTime);
console.log("轉(zhuǎn)換后的Date類型結(jié)束時間戳" + endTime);
if (nowTime < endTime && nowTime > startTime) {
console.log("秒殺可以開始鸵膏,時間條件符合");
seckill.handlerSeckill(seckillId, seckillBox);
}
else if (nowTime > endTime) {
console.log("秒殺時間已經(jīng)結(jié)束");
seckillBox.html("秒殺結(jié)束");
} else if (nowTime < startTime) {
console.log("秒殺還沒有開始");
//秒殺未開啟,計時事件綁定
var killTime = new Date(startTime + 1000);
console.log(killTime);
console.log("開始計時效果");
seckillBox.countdown(killTime, function (event) {
//事件格式
var format = event.strftime('秒殺倒計時:%D天 %H時 %M分 %S秒');
console.log(format);
seckillBox.html(format);
/*時間完成后回調(diào)事件*/
}).on('finish.countdown', function () {
console.log("準備執(zhí)行回調(diào)膊升,獲取秒殺地址,執(zhí)行秒殺");
console.log("倒計時結(jié)束");
seckill.handlerSeckill(seckillId, seckillBox);
});
}
}
}