功能分析
1. 前臺秒殺商品詳情頁面
秒殺尚未開始,顯示秒殺倒計時
正在秒殺的商品,顯示按鈕
秒殺時間已過,顯示秒殺已結(jié)束
以上判斷邏輯需要在js中完成,為了頁面邏輯看起來清晰,我們創(chuàng)建一個單獨的js文件seckill.js來處理,在詳情頁面中引入該js在頁面中我們發(fā)送請求使用jQuery,所以需要引入jQuery的js文件
倒計時的顯示,我們使用jQuery的countdown插件,所以需要將該插件的js也引入到詳情頁面中
2. 后臺秒殺地址的暴露
如果前臺已經(jīng)開始秒殺,那么需要在后臺暴露秒殺地址給前臺頁面
為了安全,在后臺依舊需要對秒殺是否開始進行判斷
為了前臺處理方便,我們將后臺返回的結(jié)果封裝為自定義RTO(結(jié)果傳輸對象)
1. 在15-seckill-web模塊的static目錄下,創(chuàng)建js子目錄,將jQuery和倒計時插件從07-SecKill\resources\js拷貝進來
倒計時插件的用法在07-SecKill\resources\countDown.txt中,直接拷貝過來用即可
2. 在15-seckill-web模塊的static/ js目錄下,創(chuàng)建seckill.js文件
3. 在15-seckill-web模塊的item.html頁面中,導(dǎo)入上面定義三個js文件
<!--導(dǎo)入jQuery的js文件-->
<script th:src="@{/js/jquery.min.js}"></script>
<!--導(dǎo)入倒計時插件的js文件-->
<script th:src="@{/js/jquery.countdown.min.js}"></script>
<!--導(dǎo)入自定義的秒殺js文件-->
<script th:src="@{/js/seckill.js}"></script>
4. 在15-seckill-web中的item.html頁面調(diào)用秒殺初始化的方法,并傳遞參數(shù)
<script type="text/javascript" th:inline="javascript">
//頁面加載完成之后,調(diào)用秒殺初始化方法,對時間進行判斷
$(function(){
seckillObj.contextPath = [[${#request.getContextPath()}]];
var id = [[${goods.id}]];
//當(dāng)前時間應(yīng)該從服務(wù)器獲取,在跳轉(zhuǎn)到秒殺詳情頁的時候傳遞過來
var currentTime = [[${currentTime}]];
//時間為距1970年1月1日 00:00:00的毫秒數(shù)
var startTime = [[${goods.starttime.getTime()}]];
var endTime = [[${goods.endtime.getTime()}]];
seckillObj.func.initItem(id,currentTime,startTime,endTime);
});
</script>
5. 在15-seckill-web模塊的seckill.js中定義對秒殺時間的判斷函數(shù)JS的開發(fā)方式,有兩種
面向過程:面向?qū)ο螅@里我們采用面向?qū)ο蟮姆绞?/span>
//定義一個json對象
var seckillObj = {
//項目上下文根,我們可以在頁面中通過seckillObj對象對其賦值
contextPath:"",
//這樣操作可選,一般公司會將獲取地址單獨抽取出來,方便維護
url:{
randomURL:function () {
return seckillObj.contextPath +"/seckill/random/";
}
},
//定義秒殺時間判斷的相關(guān)函數(shù),本身還是json的屬性,只不過是function類型
func:{
//初始化函數(shù)主要用于時間的判斷,這些時間可以從item頁面的秒殺商品對象上獲取
//所以該方法提供參數(shù),接收頁面?zhèn)鬟f的信息,同時將商品的id也傳遞過來
initItem:function (id,currentTime,startTime,endTime) {
if(currentTime < startTime){
//秒殺尚未開始 使用jquery的倒計時插件實現(xiàn)倒計時
/* + 1000 防止時間偏移 這個沒有太多意義,因為我們并不知道客戶端和服務(wù)器的時間偏移
這個插件簡單了解,實際項目不會以客戶端時間作為倒計時,一般在服務(wù)器端還需要驗證*/
var killTime = new Date(startTime + 1000);
$("#seckillTip").countdown(killTime, function (event) {
//時間格式
var format = event.strftime('距秒殺開始還有: %D天 %H時 %M分 %S秒');
$("#seckillTip").html("<span style='color:red;'>"+format+"</span>");
}).on('finish.countdown', function () {
//倒計時結(jié)束后回調(diào)事件,已經(jīng)開始秒殺,用戶可以進行秒殺了,有兩種方式:
//1、刷新當(dāng)前頁面
location.reload();
//或者2、調(diào)用秒殺開始的函數(shù)
});
}else if(currentTime > endTime){
//秒殺已經(jīng)結(jié)束
$("#seckillTip").html("<span style='color:red;'>來晚了,秒殺活動已結(jié)束</span>");
}else{
//秒殺已開始 調(diào)用startSeckill函數(shù)
seckillObj.func.startSeckill(id);
}
},
startSeckill:function (id) {
//秒殺術(shù)語 暴露秒殺地址 http://localhost:8080/15-seckill-web/seckill/goods/dfasjfkdjfkldajs
//發(fā)送ajax請求 去后臺服務(wù)器判斷秒殺是否已經(jīng)開始,如果真正開始,暴露秒殺地址,否則不暴露
$.ajax({
url : seckillObj.url.randomURL() + id,
type : "post",
dataType:"json",
success:function(rtnMessage){
if(rtnMessage.errorCode == 0){
//不能顯示秒殺按鈕
$("#seckillTip").html("<span style='color:red;'>"+ rtnMessage.errorMessage +"</span>");
}else{
//顯示秒殺按鈕
$("#seckillTip").html("<button type='button'>立即秒殺</button>");
}
}
});
}
}
}
6. 在15-seckill-web中的item.html頁面加顯示倒計時及秒殺提示信息的段落標(biāo)簽
<p id="seckillTip">
</p>
7. 數(shù)據(jù)庫時區(qū)問題的解決
我們在使用倒計時插件的,發(fā)現(xiàn)獲取的數(shù)據(jù)庫時間不對,因為我們15-seckill-web的數(shù)據(jù)是從Redis中獲取的,Redis的數(shù)據(jù)是15-seckill-service中的定時任務(wù)獲取的,所以我們需要修改15-seckill-service模塊中的核心配置文件的數(shù)據(jù)庫連接信息,加serverTimezone=GMT%2B8,指定時區(qū),這個問題從SpringBoot2.1.0以后出現(xiàn),以前的版本沒有。
修改完畢后重新運行15-seckill-service獲取時間。
8. 在15-seckill-web中的GoodsController處理暴露地址的請求
@PostMapping("/seckill/random/{id}")
public @ResponseBody ReturnObject random(@PathVariable("id") Integer id){
ReturnObject returnObject = new ReturnObject();
//根據(jù)商品的id獲取商品對象
String goodsJSON = redisTemplate.opsForValue().get(Constants.REDIS_GOODS + id);
Goods goods = JSONObject.parseObject(goodsJSON,Goods.class);
//驗證秒殺時間是否已經(jīng)真的開始
Long currentTime = System.currentTimeMillis();
Long startTime = goods.getStarttime().getTime();
Long endTime = goods.getEndtime().getTime();
if(currentTime < startTime){
//秒殺尚未開始
returnObject.setErrorCode(Constants. ZERO);
returnObject.setErrorMessage("秒殺尚未開始");
}else if(currentTime > endTime){
//秒殺已經(jīng)結(jié)束
returnObject.setErrorCode(Constants. ZERO);
returnObject.setErrorMessage("秒殺已經(jīng)結(jié)束");
}else{
//秒殺已經(jīng)開始
returnObject.setErrorCode(Constants. ONE);
returnObject.setErrorMessage("秒殺已開始");
returnObject.setData(goods.getRandomname());
}
return returnObject;
}
9. 在15-seckill-interface中的com.bjpowernode.seckill.rto包下ReturnObject類,用于封裝返回結(jié)果對象
public class ReturnObject {
private int errorCode;
private String errorMessage;
private Object data;
//get|set方法略
}
10. 在15-seckill-interface中的常量類Constants中定義返回的錯誤碼常量 1成功 0失敗
/返回結(jié)果碼的常量,0失敗, 1 成功
public static final int ZERO = 0;
public static final int ONE = 1;
11. 啟動15-seckill-service和15-seckill-web,修改數(shù)據(jù)庫商品表數(shù)據(jù)查看效果