基于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 Applicationdisplay-name> <servlet> <servlet-name>dispatcherServletservlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class> <init-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:spring/spring-*.xmlparam-value> init-param> servlet> <servlet-mapping> <servlet-name>dispatcherServletservlet-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"> <mvc:annotation-driven/> <mvc:default-servlet-handler/> <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> <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 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(@PathVariable("seckillId")Long seckillId){
SeckillResult result=null;
try{
Exposer exposer=seckillService.exposeSeckillUrl(seckillId);
result=new SeckillResult(true,exposer);
}catch (Exception e){
result=new SeckillResult(false,e.getMessage());
}
return result;
}
@RequestMapping(value="/{seckillId}/{md5}/execute",method = RequestMethod.POST,produces = {"application/json;charset=UTF-8"})
@ResponseBody
public SeckillResult execute(@PathVariable("seckillId")Long seckillId,
@PathVariable("md5")String md5,
@CookieValue(value="phone",required=false)Long phone){
if(phone==null){
return new SeckillResult(false,"手机号未注册");
}
SeckillResult result=null;
try{
SeckillExecution execution=seckillService.executeSeckill(seckillId,phone,md5);
result=new SeckillResult(true,execution);
}catch(RepeatKillException e){
SeckillExecution execution=new SeckillExecution(seckillId,-1,"重复秒杀");
result=new SeckillResult(true,execution);
}catch(SeckillCloseException e){
SeckillExecution execution=new SeckillExecution(seckillId,0,"秒杀结束");
result=new SeckillResult(true,execution);
}catch (Exception e){
SeckillExecution execution=new SeckillExecution(seckillId,-2,"系统异常");
result=new SeckillResult(true,execution);
}
return result;
}
//返回系统时间
@RequestMapping(value="/time/now/",method = RequestMethod.GET)
@ResponseBody
public SeckillResult time(){
Date d=new Date();
return new SeckillResult(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" %>
秒杀列表页
秒杀列表
名称
库存
开始时间
结束时间
创建时间
秒杀
${item.name}
${item.number}
秒杀
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" %>
秒杀详情
${seckill.name}
<%--显示time图标--%>
<%--展示倒计时--%>
<%--登录弹出层 输入电话--%>
<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>
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手机号错误!').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("")
$.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(""+stateInfo+"");
}
})
});
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来实现倒计时效果。倒计时插件会维护一个倒计时事件,时间结束时直接会调用秒杀接口来实现秒杀业务。