基于SpringMVC+Spring+MyBatis实现秒杀系统【客户端交互】
程序员文章站
2022-04-10 15:24:05
前言 该篇主要实现客户端和服务的交互。在第一篇概况里我已经贴出了业务场景的交互图片。 客户端交互主要放在seckill.js里来实现。页面展现基于jsp+jstl来实现。 准备工作 1、配置web.xml。web.xml里配置springmvc前端控制器时需要把spring托管的3个xml全部加载。 ......
前言
该篇主要实现客户端和服务的交互。在第一篇概况里我已经贴出了业务场景的交互图片。 客户端交互主要放在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来实现倒计时效果。倒计时插件会维护一个倒计时事件,时间结束时直接会调用秒杀接口来实现秒杀业务。
上一篇: Linux 安装MySql 5.7.21 操作步骤
下一篇: 买房子赠送