前言

该篇主要实现客户端和服务的交互。在第一篇概况里我已经贴出了业务场景的交互图片。 客户端交互主要放在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来实现倒计时效果。倒计时插件会维护一个倒计时事件,时间结束时直接会调用秒杀接口来实现秒杀业务。

基于SpringMVC+Spring+MyBatis实现秒杀系统【客户端交互】的更多相关文章

  1. 基于SpringMVC+Spring+MyBatis实现秒杀系统【概况】

    前言 本教程使用SpringMVC+Spring+MyBatis+MySQL实现一个秒杀系统.教程素材来自慕课网视频教程[https://www.imooc.com/learn/631].有感兴趣的可 ...

  2. 基于SpringMVC+Spring+MyBatis实现秒杀系统【业务逻辑】

    前言 该篇主要实现秒杀业务层,秒杀业务逻辑里主要包括暴露秒杀接口地址.实现秒杀业务逻辑.同时声明了三个业务类:Exposer.SeckillExecution.SeckillResult. Expos ...

  3. 基于SpringMVC+Spring+MyBatis实现秒杀系统【数据库接口】

    前言 该篇教程主要关注MyBatis实现底层的接口,把MyBatis交给Spring来托管.数据库连接池用的c3p0.数据库用的MySQL.主要有2个大类:秒杀商品的查询.秒杀明细的插入. 准备工作 ...

  4. 手把手教你使用VUE+SpringMVC+Spring+Mybatis+Maven构建属于你自己的电商系统之vue后台前端框架搭建——猿实战01

            猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是 ...

  5. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第十二天】(系统架构讲解、nginx)

    https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...

  6. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第十天】(单点登录系统实现)

    https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...

  7. Idea SpringMVC+Spring+MyBatis+Maven调整【转】

    Idea SpringMVC+Spring+MyBatis+Maven整合   创建项目 File-New Project 选中左侧的Maven,选中右侧上方的Create from archetyp ...

  8. SpringMVC+Spring+MyBatis+Maven调整【转】

    Idea SpringMVC+Spring+MyBatis+Maven整合   创建项目 File-New Project 选中左侧的Maven,选中右侧上方的Create from archetyp ...

  9. 单工程搭建springmvc+spring+mybatis(maven,idea)

    单工程搭建springmvc+spring+mybatis(maven,idea) 1.pom.xml <properties> <project.build.sourceEncod ...

随机推荐

  1. Session使用(14)

    用session统计某个网页的访问人数(因为我还不会js,所以就做了个简易版本的) 1.创建Session监听器,每创建了一个Session对象就执行监听类中·的sessionCreated方法. p ...

  2. Runnable与Callable区别

    相同点: 两者都是接口:(废话) 两者都可用来编写多线程程序: 两者都需要调用Thread.start()启动线程: 不同点: 两者最大的不同点是:实现Callable接口的任务线程能返回执行结果:而 ...

  3. python学习第四天笔记整理

    一.迭代器及生成器 #迭代器:迭代的工具 #1 什么是迭代:指的是一个重复的过程,每一次重复称为一次迭代,并且每一次重复的结果是下一次重复的初始值# while True:# print('===== ...

  4. HDU - 1241 Oil Deposits 经典dfs 格子

    最水的一道石油竟然改了一个小时,好菜好菜. x<=r  y<=c  x<=r  y<=c  x<=r  y<=c  x<=r y<=c #include ...

  5. formated time string for file naming

    #include <stdio.h> #include <time.h> int main() { time_t rawtime; struct tm *timeinfo; ] ...

  6. 利用 awk 统计nginx 中某一个用户的访问次数

    线上总是会遇到攻击,所以就需要分析 access.log 看看那些用户的访问次数不正常,针对这些不正常的用户,要做处理,以 access.log为例说明下怎么统计. 通过 access.log 日志来 ...

  7. 【ZCTF】easy reverse 详解

    0x01  前言     团队逆向牛的解题思路,分享出来~ 0x02  内容 0. 样本 bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll 1. 静态分析 使用IDAP ...

  8. 吴恩达机器学习笔记35-诊断偏差和方差(Diagnosing Bias vs. Variance)

    当你运行一个学习算法时,如果这个算法的表现不理想,那么多半是出现两种情况:要么是偏差比较大,要么是方差比较大.换句话说,出现的情况要么是欠拟合,要么是过拟合问题.那么这两种情况,哪个和偏差有关,哪个和 ...

  9. KNN算法基本实例

    KNN算法是机器学习领域中一个最基本的经典算法.它属于无监督学习领域的算法并且在模式识别,数据挖掘和特征提取领域有着广泛的应用. 给定一些预处理数据,通过一个属性把这些分类坐标分成不同的组.这就是KN ...

  10. 机器学习基石笔记:03 Types of Learning

    原文地址:https://www.jianshu.com/p/86b2a9cef742 一.学习的分类 根据输出空间\(Y\):分类(二分类.多分类).回归.结构化(监督学习+输出空间有结构): 根据 ...