秒杀项目
秒杀项目基本环境搭建
商品展示模块
请求执行秒杀模块
秒杀流程总结

商品秒杀按钮的显示模块

功能分析

1. 前台秒杀商品详情页面

秒杀尚未开始,显示秒杀倒计时

正在秒杀的商品,显示按钮

秒杀时间已过,显示秒杀已结束

以上判断逻辑需要在js中完成,为了页面逻辑看起来清晰,我们创建一个单独的js文件seckill.js来处理,在详情页面中引入该js在页面中我们发送请求使用jQuery,所以需要引入jQuery的js文件

倒计时的显示,我们使用jQuery的countdown插件,所以需要将该插件的js也引入到详情页面中

2. 后台秒杀地址的暴露

如果前台已经开始秒杀,那么需要在后台暴露秒杀地址给前台页面

为了安全,在后台依旧需要对秒杀是否开始进行判断

为了前台处理方便,我们将后台返回的结果封装为自定义RTO(结果传输对象)

业务实现

1. 在15-seckill-web模块的static目录下,创建js子目录,将jQuery和倒计时插件从07-SecKill\resources\js拷贝进来

倒计时插件的用法在07-SecKill\resources\countDown.txt中,直接拷贝过来用即可

2. 在15-seckill-web模块的static/ js目录下,创建seckill.js文件

3. 在15-seckill-web模块的item.html页面中,导入上面定义三个js文件

<!--导入jQuery的js文件-->
<script th:src="@{/js/jquery.min.js}"></script>
<!--导入倒计时插件的js文件-->
<script th:src="@{/js/jquery.countdown.min.js}"></script>
<!--导入自定义的秒杀js文件-->
<script th:src="@{/js/seckill.js}"></script>

4. 在15-seckill-web中的item.html页面调用秒杀初始化的方法,并传递参数

<script type="text/javascript" th:inline="javascript">
    //页面加载完成之后,调用秒杀初始化方法,对时间进行判断
    $(function(){
        seckillObj.contextPath = [[${#request.getContextPath()}]];
        var id = [[${goods.id}]];
        //当前时间应该从服务器获取,在跳转到秒杀详情页的时候传递过来
        var currentTime = [[${currentTime}]];
        //时间为距1970年1月1日 00:00:00的毫秒数
        var startTime = [[${goods.starttime.getTime()}]];
        var endTime = [[${goods.endtime.getTime()}]];
        seckillObj.func.initItem(id,currentTime,startTime,endTime);
    });
</script>

5. 在15-seckill-web模块的seckill.js中定义对秒杀时间的判断函数JS的开发方式,有两种

面向过程:面向对象,这里我们采用面向对象的方式

//定义一个json对象
var seckillObj = {
    //项目上下文根,我们可以在页面中通过seckillObj对象对其赋值
    contextPath:"",
    //这样操作可选,一般公司会将获取地址单独抽取出来,方便维护
    url:{
        randomURL:function () {
            return seckillObj.contextPath +"/seckill/random/";
        }
    },
    //定义秒杀时间判断的相关函数,本身还是json的属性,只不过是function类型
    func:{
        //初始化函数主要用于时间的判断,这些时间可以从item页面的秒杀商品对象上获取
        //所以该方法提供参数,接收页面传递的信息,同时将商品的id也传递过来
        initItem:function (id,currentTime,startTime,endTime) {
            if(currentTime < startTime){
                //秒杀尚未开始  使用jquery的倒计时插件实现倒计时
                /* + 1000 防止时间偏移 这个没有太多意义,因为我们并不知道客户端和服务器的时间偏移
                这个插件简单了解,实际项目不会以客户端时间作为倒计时,一般在服务器端还需要验证*/
                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 () {
                    //倒计时结束后回调事件,已经开始秒杀,用户可以进行秒杀了,有两种方式:
                    //1、刷新当前页面
                    location.reload();
                    //或者2、调用秒杀开始的函数
                });
            }else if(currentTime > endTime){
                //秒杀已经结束
                $("#seckillTip").html("<span style='color:red;'>来晚了,秒杀活动已结束</span>");
            }else{
                //秒杀已开始  调用startSeckill函数
                seckillObj.func.startSeckill(id);
            }
        },
        startSeckill:function (id) {
            //秒杀术语  暴露秒杀地址 http://localhost:8080/15-seckill-web/seckill/goods/dfasjfkdjfkldajs
            //发送ajax请求  去后台服务器判断秒杀是否已经开始,如果真正开始,暴露秒杀地址,否则不暴露
            $.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页面加显示倒计时及秒杀提示信息的段落标签

<p id="seckillTip">
</p>

7. 数据库时区问题的解决

我们在使用倒计时插件的,发现获取的数据库时间不对,因为我们15-seckill-web的数据是从Redis中获取的,Redis的数据是15-seckill-service中的定时任务获取的,所以我们需要修改15-seckill-service模块中的核心配置文件的数据库连接信息,加serverTimezone=GMT%2B8,指定时区,这个问题从SpringBoot2.1.0以后出现,以前的版本没有。

修改完毕后重新运行15-seckill-service获取时间。

8. 在15-seckill-web中的GoodsController处理暴露地址的请求

@PostMapping("/seckill/random/{id}")
public @ResponseBody ReturnObject random(@PathVariable("id") Integer id){
    ReturnObject returnObject = new ReturnObject();
    //根据商品的id获取商品对象
    String goodsJSON = redisTemplate.opsForValue().get(Constants.REDIS_GOODS + id);
    Goods goods = JSONObject.parseObject(goodsJSON,Goods.class);
    //验证秒杀时间是否已经真的开始
    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){
        //秒杀已经结束
        returnObject.setErrorCode(Constants. ZERO);
        returnObject.setErrorMessage("秒杀已经结束");
    }else{
        //秒杀已经开始
        returnObject.setErrorCode(Constants. ONE);
        returnObject.setErrorMessage("秒杀已开始");
        returnObject.setData(goods.getRandomname());
    }
    return returnObject;
}

9. 在15-seckill-interface中的com.bjpowernode.seckill.rto包下ReturnObject类,用于封装返回结果对象

public class ReturnObject {
    private int errorCode;
    private String errorMessage;
    private Object data;
   //get|set方法略
}

10. 在15-seckill-interface中的常量类Constants中定义返回的错误码常量 1成功 0失败

/返回结果码的常量,0失败, 1 成功
public static final  int ZERO = 0;
public static final  int ONE = 1;

11. 启动15-seckill-service和15-seckill-web,修改数据库商品表数据查看效果

全部教程