zoukankan      html  css  js  c++  java
  • 基于SpringMVC+Spring+MyBatis实现秒杀系统【客户端交互】

    前言

          该篇主要实现客户端和服务的交互。在第一篇概况里我已经贴出了业务场景的交互图片。 客户端交互主要放在seckill.js里来实现。页面展现基于jsp+jstl来实现。

    准备工作

    1、配置web.xml。web.xml里配置springmvc前端控制器时需要把spring托管的3个xml全部加载。分别是spring-dao.xml、spring-service.xml、spring-web.xml。

    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1"
             metadata-complete="true">
      <display-name>Archetype Created Web Application</display-name>
      <!--配置前端控制器-->
      <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring/spring-*.xml</param-value>
        </init-param>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    
    
    </web-app>

    2、配置spring-web.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!--1、配置spring mvc -->
        <mvc:annotation-driven/>
    
        <!--2、静态资源默认配置-->
        <mvc:default-servlet-handler/>
    
        <!--3、配置视图-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            <property name="prefix" value="/WEB-INF/views/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
        <!--4、扫描web相关controller-->
        <context:component-scan base-package="com.seckill.web"/>
    </beans>

    秒杀接口

    @Controller
    @RequestMapping("/seckill")
    public class SeckillController {
    
    
        @Autowired
        SeckillService seckillService;
    
    
    
        @RequestMapping("/list")
        public ModelAndView list(){
    
            ModelAndView mav=new ModelAndView("list");
            List<Seckill> list = seckillService.getSeckillList();
            mav.addObject("list",list);
    
            return mav;
        }
    
    
        /**
         * 返回值如果是ModelAndView时怎么控制重定向和转发呢
         * **/
        @RequestMapping(value="/{seckillId}/detail/",method = RequestMethod.GET)
        public ModelAndView detail(@PathVariable("seckillId")Long seckillId){
    
            ModelAndView mav=new ModelAndView("detail");
            Seckill seckill=seckillService.getById(seckillId);
            mav.addObject("seckill",seckill);
            return mav;
    
        }
    
    
        //处理ajax请求返回json
        @RequestMapping(value="/{seckillId}/exposer",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
        @ResponseBody
        public SeckillResult<Exposer> exposer(@PathVariable("seckillId")Long seckillId){
    
            SeckillResult<Exposer> result=null;
            try{
                Exposer exposer=seckillService.exposeSeckillUrl(seckillId);
                result=new SeckillResult<Exposer>(true,exposer);
    
            }catch (Exception e){
                result=new SeckillResult<Exposer>(false,e.getMessage());
            }
    
    
            return result;
    
        }
    
    
        @RequestMapping(value="/{seckillId}/{md5}/execute",method = RequestMethod.POST,produces = {"application/json;charset=UTF-8"})
        @ResponseBody
        public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId")Long seckillId,
                                                       @PathVariable("md5")String md5,
                                                       @CookieValue(value="phone",required=false)Long phone){
    
            if(phone==null){
                return new SeckillResult<SeckillExecution>(false,"手机号未注册");
            }
    
            SeckillResult<SeckillExecution> result=null;
    
            try{
    
                SeckillExecution execution=seckillService.executeSeckill(seckillId,phone,md5);
                result=new SeckillResult<SeckillExecution>(true,execution);
    
            }catch(RepeatKillException e){
    
                SeckillExecution execution=new SeckillExecution(seckillId,-1,"重复秒杀");
                result=new SeckillResult<SeckillExecution>(true,execution);
    
    
            }catch(SeckillCloseException e){
    
                SeckillExecution execution=new SeckillExecution(seckillId,0,"秒杀结束");
                result=new SeckillResult<SeckillExecution>(true,execution);
    
            }catch (Exception e){
    
                SeckillExecution execution=new SeckillExecution(seckillId,-2,"系统异常");
                result=new SeckillResult<SeckillExecution>(true,execution);
    
            }
    
            return result;
    
        }
    
    
        //返回系统时间
        @RequestMapping(value="/time/now/",method = RequestMethod.GET)
        @ResponseBody
        public SeckillResult<Long> time(){
            Date d=new Date();
    
            return new SeckillResult<Long>(true,d.getTime());
        }
    }
    

      

    客户端实现

    1、秒杀商品列表页

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@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" %>
    
    
    <html>
    <head>
        <title>秒杀列表页</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta charset="utf-8">
        <!-- 新 Bootstrap 核心 CSS 文件 -->
        <link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
        <!-- 可选的Bootstrap主题文件(一般不使用) -->
        <link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap-theme.min.css" rel="stylesheet">
    
        <!-- HTML5 Shim 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 -->
        <!-- 注意: 如果通过 file://  引入 Respond.js 文件,则该文件无法起效果 -->
        <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
        <![endif]-->
    
    </head>
    <body>
    
        <div class="container">
            <div class="panel panel-default">
                <div class="panel-heading text-center">
                    <h2>秒杀列表</h2>
                </div>
    
                <div class="panel-body">
                    <table class="table table-hover">
                        <thead>
                            <tr>
                                <th>名称</th>
                                <th>库存</th>
                                <th>开始时间</th>
                                <th>结束时间</th>
                                <th>创建时间</th>
                                <th>秒杀</th>
                            </tr>
                        </thead>
                        <tbody>
                            <c:forEach var="item" items="${list}">
                                <tr>
                                    <td>${item.name}</td>
                                    <td>${item.number}</td>
                                    <td>
                                        <fmt:formatDate value="${item.startTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
                                    </td>
                                    <td>
                                        <fmt:formatDate value="${item.endTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
                                    </td>
                                    <td>
                                        <fmt:formatDate value="${item.createTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
                                    </td>
                                    <td>
                                        <a class="btn btn-info" href="/seckill/${item.seckillId}/detail/">秒杀</a>
                                    </td>
                                </tr>
                            </c:forEach>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    
    
    </body>
    </html>
    

    2、秒杀商品详情页

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@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" %>
    
    
    <html>
    <head>
        <title>秒杀详情</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta charset="utf-8">
        <!-- 新 Bootstrap 核心 CSS 文件 -->
        <link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
        <!-- 可选的Bootstrap主题文件(一般不使用) -->
        <link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap-theme.min.css" rel="stylesheet">
        <!-- HTML5 Shim 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 -->
        <!-- 注意: 如果通过 file://  引入 Respond.js 文件,则该文件无法起效果 -->
        <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
        <![endif]-->
    </head>
    <body>
    <div class="container">
        <div class="panel panel-default text-center">
            <div class="pannel-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="killPhoneModal" 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 class="modal-body">
                    <div class="row">
                        <div class="col-xs-8 col-xs-offset-2">
                            <input type="text" name="killPhone" id="killPhoneKey"
                                   placeholder="填写手机号^o^" class="form-control">
                        </div>
                    </div>
                </div>
    
                <div class="modal-footer">
    
                    <span id="killPhoneMessage" class="glyphicon"> </span>
                    <button type="button" id="killPhoneBtn" class="btn btn-success">
                        <span class="glyphicon glyphicon-phone"></span>
                        Submit
                    </button>
                </div>
    
            </div>
        </div>
    
    </div>
    
    <script src="http://apps.bdimg.com/libs/jquery/2.0.0/jquery.min.js"></script>
    <script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
    <script src="http://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
    <script src="http://cdn.bootcss.com/jquery.countdown/2.1.0/jquery.countdown.min.js"></script>
    <script src="/resources/scripts/seckill.js?201806242323235"></script>
    <script type="text/javascript">
    
    
        $(function(){
    
            seckill.detail.init({
                seckillId:${seckill.seckillId},
                startTime:${seckill.startTime.time}, //取毫秒数
                endTime:${seckill.endTime.time}
    
            })
    
        })
    
    
    
    </script>
    </body>
    </html>
    

      

    3、秒杀业务逻辑seckill.js

    var seckill={
    
        /**秒杀相关url**/
        URL:{
            now:'/seckill/time/now/'
        },
    
        /**验证手机号**/
        validatePhone:function(phone){
            if(phone && phone.length==11 && !isNaN(phone)){
                return true;
            }
    
            return false;
    
        },
    
        /**倒计时**/
        countdown:function(seckillId,nowTime,startTime,endTime){
           console.log(seckillId+","+nowTime+","+startTime+","+endTime);
    
           var seckillBox=$("#seckill-box");
           if(nowTime>endTime){
               seckillBox.html("秒杀已经结束");
           }else if(nowTime<startTime){
               //秒杀还没开始,显示倒计时
               var killTime = new Date(startTime + 1000);
               seckillBox.countdown(killTime,function(e){
                   var format = e.strftime('秒杀倒计时: %D天 %H时 %M分 %S秒 ');
                   seckillBox.html(format);
               }).on("finish.countdown",function(){
                   console.log("倒计时结束,开始秒杀");
                   seckill.seckill(seckillId,seckillBox);
               });
           }else{
                //秒杀开始
               seckill.seckill(seckillId,seckillBox);
           }
        },
    
        detail:{
            /**初始化参数**/
            init:function(params){
                var phone=$.cookie('phone');
    
                //验证手机号
                if(!seckill.validatePhone(phone)){
                    var killphoneModal=$("#killPhoneModal");
                    //如果有取到cookie里的手机,则弹出模拟登陆
                    killphoneModal.modal({
                        show: true,//显示弹出层
                        backdrop: 'static',//禁止位置关闭
                        keyboard: false//关闭键盘事件
                    });
    
                    $("#killPhoneBtn").click(function(){
    
                        var inputphone=$("#killPhoneKey").val();
                        console.log('inputphone:'+inputphone);
                        if(seckill.validatePhone(inputphone)){
                            $.cookie("phone",inputphone,{expires:7,path:'/seckill'});
                            //验证通过,刷新页面
                            window.location.reload();
                        }else{
                            $('#killPhoneMessage').hide().html('<label class="label label-danger">手机号错误!</label>').show(300);
                        }
                    })
    
                }
    
                var seckillId=params["seckillId"];
                var startTime=params["startTime"];
                var endTime=params["endTime"];
                $.get(seckill.URL.now,{},function(result){
                    if(result && result["success"]){
                        var nowTime=result["data"];
                        seckill.countdown(seckillId,nowTime,startTime,endTime);
                    }else{
                        console.log(result);
                    }
                })
    
            }
        },
    
        /**执行秒杀**/
        seckill:function(seckillId,node){
    
            //获取秒杀地址、控制node节点显示,执行秒杀
            node.hide().html("<button id='killBtn' class='btn btn-primary btn-lg'>开始秒杀</button>")
    
            $.get('/seckill/'+seckillId+'/exposer',{},function(result){
    
                if(result && result["success"]){
                    //在回调函数中执行秒杀操作
                    var exposer=result["data"];
                    if(exposer["exposed"]){
                        //秒杀已开始
                        var md5=exposer["md5"];
                        var killUrl='/seckill/'+seckillId+'/'+md5+'/execute';
                        console.log(killUrl);
    
                        $("#killBtn").one('click',function(){
                            //1、禁用秒杀按钮
                            $(this).addClass('disabled');
                            //2、执行秒杀操作
                            $.post(killUrl,{},function(result){
                                if(result && result["success"]){
                                    var killResult=result["data"];
                                    var state=killResult["state"];
                                    var stateInfo=killResult["stateInfo"];
    
                                    node.html("<span class='label label-success'>"+stateInfo+"</span>");
    
                                }
                            })
    
                        });
    
                        node.show();
                    }else{
                        //秒杀未开始, 防止浏览器和服务器出现时间差,再次执行倒数计时
                        var now = exposer['now'];
                        var start = exposer['start'];
                        var end = exposer['end'];
                        seckill.countdown(seckillId, now, start, end);
                    }
    
                }else{
                    console.log('result:'+result); //没有拿到秒杀地址
                }
    
            })
    
        }
    
    
    
    }
    

      

    总结

          秒杀相关业务逻辑主要是根据秒杀商品的开始时间、结束时间以及客户端的当前时间来判断秒杀是否开始、是否结束。未开始时调用jquery.countdown来实现倒计时效果。倒计时插件会维护一个倒计时事件,时间结束时直接会调用秒杀接口来实现秒杀业务。

  • 相关阅读:
    BZOJ2594 [Wc2006]水管局长数据加强版 LCT kruskal
    BZOJ1180 [CROATIAN2009]OTOCI LCT
    BZOJ2631 tree LCT
    BZOJ1901 Zju2112 Dynamic Rankings 主席树
    BZOJ1367 [Baltic2004]sequence 堆 左偏树
    BZOJ5120 [2017国家集训队测试]无限之环 费用流
    BZOJ3377 [Usaco2004 Open]The Cow Lineup 奶牛序列 其他
    BZOJ3091 城市旅行 LCT
    BZOJ2843 极地旅行社 LCT
    BZOJ1269 [AHOI2006]文本编辑器editor splay
  • 原文地址:https://www.cnblogs.com/sword-successful/p/9230446.html
Copyright © 2011-2022 走看看