一步一步的搭建JAVA WEB项目,采用Maven构建,基于MYBatis+Spring+Spring MVC+Bootstrap技术的秒杀项目
学习的视频:http://www.imooc.com/learn/587
创建Maven项目
- 创建目录,执行Maven命令
mvn archetype:generate -DgroupId=org.seckill -DartifactId=seckill -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeCatalog=local
问题:Maven命令执行到Generating Project in Batch mode 卡住,参考链接
- 将项目导入到IDEA工具中
- 修改项目配置
- 修改web.xml中的servlet版本,默认是2.3,其不支持JSP的EL表达式。从Tomcat中的示例的web.xml中拷贝3.0的版本配置到项目中
- 补全目录。项目的main目录下创建java目录,在src目录下创建test目录,test目录下创建java和sources目录
- 打开pom.xml,进行依赖的配置
- 单元测试依赖:Junit4
- 日志依赖:slf4j+logback。(lf4j是规范/接口,log4j,common-logging,logback是日志的实现)
- 数据库依赖:mysql-connector-java、c3p0
- DAO框架:mybatis依赖:mybatis
- Servlet web相关依赖:standard、jstl、jackson-databind、servlet-api
- Spring依赖:spring-core、spring-beans、spring-context、spring-jdbc、spring-tx、spring-web、spring-webmvc、spring-test
junit junit 4.11 test org.slf4j slf4j-api 1.7.12 ch.qos.logback logback-core 1.1.1 ch.qos.logback logback-classic 1.1.1 mysql mysql-connector-java 5.1.35 runtime c3p0 c3p0 0.9.1.2 org.mybatis mybatis 3.3.0 org.mybatis mybatis-spring 1.2.3 taglibs standard 1.1.2 jstl jstl 1.2 com.fasterxml.jackson.core jackson-databind 2.5.4 javax.servlet javax.servlet-api 3.1.0 org.springframework spring-core 4.1.7.RELEASE org.springframework spring-beans 4.1.7.RELEASE org.springframework spring-context 4.1.7.RELEASE org.springframework spring-jdbc 4.1.7.RELEASE org.springframework spring-tx 4.1.7.RELEASE org.springframework spring-web 4.1.7.RELEASE org.springframework spring-webmvc 4.1.7.RELEASE org.springframework spring-test 4.1.7.RELEASE
数据库的设计
- 在项目main目录下创建sql目录,新建 schema.sql,作为数据库的创建脚本
- 脚本代码如下:
-- 数据库初始化脚本 -- 创建数据库 CREATE DATABASE seckill; -- 使用数据库 use seckill; -- 创建秒杀库存表:使用InnoDB引擎,其支持事务。主键自增设置为从1000开始,字符格式设置为UTF8 CREATE TABLE seckill( seckill_id bigint NOT NULL AUTO_INCREMENT COMMENT '商品库存id', name varchar(120) NOT NULL COMMENT '商品名称', number int NOT NULL COMMENT '库存数量', start_time timestamp NOT NULL COMMENT '秒杀开启时间', end_time timestamp NOT NULL COMMENT '秒杀结束时间', create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (seckill_id), KEY idx_start_time(start_time), KEY idx_end_time(end_time), KEY idx_create_time(create_time) )ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表'; -- 秒杀成功明细表 CREATE TABLE success_killed( seckill_id bigint NOT NULL COMMENT '秒杀商品id', user_phone int NOT NULL COMMENT '用户手机号', state tinyint NOT NULL COMMENT '状态标示:-1指无效,0指成功,1指已付款', create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (seckill_id,user_phone), KEY idx_create_time(create_time) )ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; -- 初始化数据 INSERT INTO seckill(name,number,start_time,end_time) VALUES ('1000元秒杀iphone6',100,'2016-06-28 00:00:00','2016-06-29 00:00:00'), ('500元秒杀iphone5',200,'2016-06-28 00:00:00','2016-06-29 00:00:00'), ('200元秒杀小米4',300,'2016-06-28 00:00:00','2016-06-29 00:00:00'), ('100元秒杀红米note',400,'2016-06-28 00:00:00','2016-06-29 00:00:00'); -- show create table seckill; -- 为什么手写DDL,记录每次上线的DDL修改
DAO实体和接口
创建实体包org.seckill.entity
创建DAO包org.seckill.dao
-
创建SecKill实体类,生成getter和setter,重写toString
private long secKillId; private String name; private int number; private Date startTime; private Date endTime; private Date createTime;
-
创建SuccessKilled实体类,生成getter和setter,重写toString
private long secKillId; private long userPhone; private short state; private Date createTime;
-
创建DAO接口SecKillDao,添加减库存,根据ID查询秒杀对象,查询秒杀商品列表方法
/** * 减库存 * @param secKillId * @param killTime * @return如果影响行数大于1,表示更新的记录行数 */ int reduceNumber(long secKillId,Date killTime); /** * 根据id查询秒杀对象 * @param secKillId * @return */ SecKill queryById(long secKillId); /** * 根据偏移量查询秒杀商品列表 * @param offset * @param limit * @return */ List
queryAll(int offset,int limit); -
创建DAO接口SuccessKilledDao,添加插入购买明细,根据ID查询购买明细实体的方法
/** * 插入购买明细,可过滤重复 * @param secKillId * @param userPhone * @return插入的行数 */ int inertSuccessKilled(long secKillId,long userPhone); /** *根据ID查询SuccessKilled并携带秒杀产品对象实体 * @param secKillId * @return */ SuccessKilled queryByIdWithSecKill(long secKillId);
-
基于MyBaits实现DAO接口
- 创建mybatis-config.xml全局配置文件
- 创建mapper文件夹,用于存储mybatis映射文件
- 创建SecKilledDao.xml映射文件
update seckill set number = number -1 where seckill_id = #{secKillId} and start_time #{killTime} and end_time >= #{killTime} and number > 0; - 创建SuccessKilledDao.xml映射文件
insert ignore into success_killed(seckill_id,user_phone) values (#{secKilled},#{userPhone}) -
mybatis整合spring
- 创建spring文件,用于存储spring配置文件
- 创建spring-dao.xml配置文件
- 创建jdbc.properties配置文件,用于存储数据库相关信息
``` driver=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/seckill?useUnicode=true&characterEncoding=utf-8 username=root password=purple ```
- 在spring-dao.xml配置文件中进行四个步骤的配置
- 配置数据库相关参数
- 配置数据库连接池
- 配置sqlSessionFactory对象
- 配置扫描dao接口包,动态实现 dao接口,并注入到spring容器中
-
Junit4与Spring进行整合,进行Junit4单元测试
- 创建SecKillDao的单元测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring/spring-dao.xml") public class SecKillDaoTest { //注入DAO实现类依赖 @Resource private SecKillDao secKillDao; @Test public void testReduceNumber() throws Exception { Date killTime = new Date(); int result = secKillDao.reduceNumber(1000L,killTime); System.out.println(result); } @Test public void testQueryById() throws Exception { long id = 1000; SecKill secKill = secKillDao.queryById(id); System.out.println(secKill.getName()); } @Test public void testQueryAll() throws Exception { List
secKillList = secKillDao.queryAll(0,1000); for(SecKill row : secKillList){ System.out.println(row.toString()); } } } - 创建SuccessKilledDao的单元测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring/spring-dao.xml") public class SuccessKilledDaoTest { @Resource private SuccessKilledDao successKilledDao; @Test public void testInertSuccessKilled() throws Exception { int result = successKilledDao.insertSuccessKilled(1000L,28059830451L); System.out.println(result); } @Test public void testQueryByIdWithSecKill() throws Exception { SuccessKilled successKilled = successKilledDao.queryByIdWithSecKill(1000L,2147483647L); System.out.println(successKilled.toString()); } }
- 学习点
- 单元测试类可以利用IDEA的快捷键,直接在要测试的类中进行代码的生成
- mybatis的传参,需要在DAO接口方法的形参中使用@Param注解进行指明
业务层设计
- 秒杀业务接口设计
创建业务包service
创建数据传输实体包dto
创建异常包exception
-
创建dto实体
- 创建暴露秒杀地址DTO:Exposer
public class Exposer { /** * 是否开启秒杀 */ private boolean exposed; /** * 秒杀ID */ private long secKillId; /** * 一种加密措施 */ private String md5; /** *系统当前时间(毫秒值) */ private long now; private long start; private long end; public Exposer(boolean exposed, String md5, long secKillId) { this.exposed = exposed; this.md5 = md5; this.secKillId = secKillId; } public Exposer(boolean exposed, long now, long start, long end) { this.exposed = exposed; this.now = now; this.start = start; this.end = end; } public Exposer(boolean exposed, long secKillId) { this.exposed = exposed; this.secKillId = secKillId; } public boolean isExposed() { return exposed; } public void setExposed(boolean exposed) { this.exposed = exposed; } public long getSecKillId() { return secKillId; } public void setSecKillId(long secKillId) { this.secKillId = secKillId; this.secKillId = secKillId; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public long getNow() { return now; } public void setNow(long now) { this.now = now; } public long getStart() { return start; } public void setStart(long start) { this.start = start; } public long getEnd() { return end; } public void setEnd(long end) { this.end = end; } }
- 创建封装秒杀执行后结果DTO:SecKillExecution
public class SecKillExecution { private long secKillId; /** * 秒杀执行结果状态 */ private int state; /** * 状态表示 */ private String stateInfo; private SuccessKilled successKilled; public SecKillExecution(long secKillId, int state, String stateInfo, SuccessKilled successKilled) { this.secKillId = secKillId; this.state = state; this.stateInfo = stateInfo; this.successKilled = successKilled; } public SecKillExecution(long secKillId, int state, String stateInfo) { this.secKillId = secKillId; this.state = state; this.stateInfo = stateInfo; } public long getSecKillId() { return secKillId; } public void setSecKillId(long secKillId) { this.secKillId = secKillId; } public int getState() { return state; } public void setState(int state) { this.state = state; } public String getStateInfo() { return stateInfo; } public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; } public SuccessKilled getSuccessKilled() { return successKilled; } public void setSuccessKilled(SuccessKilled successKilled) { this.successKilled = successKilled; } }
-
创建异常类
- 创建业务相关异常:SecKillException
public class SecKillException extends RuntimeException{ public SecKillException(String message) { super(message); } public SecKillException(String message, Throwable cause) { super(message, cause); } }
- 创建重复秒杀异常类:RepeatKillException
public class RepeatKillException extends SecKillException{ public RepeatKillException(String message, Throwable cause) { super(message, cause); } public RepeatKillException(String message) { super(message); } }
- 创建秒杀关闭异常类:SecKillCloseExce
ption
public class SecKillCloseException extends SecKillException{ public SecKillCloseException(String message) { super(message); } public SecKillCloseException(String message, Throwable cause) { super(message, cause); } }
-
创建SecKillService业务接口:SecKillService
- 创建查询所有的秒杀记录方法:getSecKillList
- 创建查询单个秒杀记录方法:getById
- 创建秒杀开启时输出秒杀接口地址方法:exportSecKillUrl
- 创建执行秒杀操作方法:executeSecKill
public interface SecKillService { /** * 查询所有的秒杀记录 * @return */ List
getSecKillList(); /** * 查询单个秒杀记录 * @param secKillId * @return */ SecKill getById(long secKillId); /** * 秒杀开启时输出秒杀接口地址 * 否则输出系统时间和秒杀时间 * 防止用户猜测出秒杀地址的规律 * @param secKillId */ Exposer exportSecKillUrl(long secKillId); /** *执行秒杀操作 * @param secKillId * @param userPhone * @param md5 */ SecKillExecution executeSecKill(long secKillId,long userPhone,String md5) throws SecKillException,RepeatKillException,SecKillCloseException; } -
业务接口设计的学习点
- 站在使用者的角度进行设计接口,不要冗余设计
- 方法定义粒度,目的明确。非常友好的让使用者调用接口
- 参数要简炼
- 返回类型要清晰
-
秒杀业务接口实现
- 新建enums枚举包,将数据字典放到枚举中
- 在枚举包下创建秒杀状态枚举:SecKillStatEnum
public enum SecKillStatEnum { SUCCESS(1,"秒杀成功"), END(0,"秒杀结束"), REPEAT(-1,"重复秒杀"), INNER_ERROR(-2,"系统异常"), DATA_REWRITE(-3,"数据篡改"); private int state; private String stateInfo; SecKillStatEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public int getState() { return state; } public String getStateInfo() { return stateInfo; } public static SecKillStatEnum stateOf(int index){ for(SecKillStatEnum state : values()) { if(state.getState() == index){ return state; } } return null; } }
- 在service包下新建impl包
- 创建SecKillServiceImpl实现类,实现SecKillService接口方法
public class SecKillServiceImpl implements SecKillService{ private Logger logger = LoggerFactory.getLogger(SecKillService.class); private SecKillDao secKillDao; private SuccessKilledDao successKilledDao; //混淆字符,用于混淆MD5 private final String salt = "sdlkjs#$#$dfowierlkjafdmv232k3j@@##$"; @Override public List
getSecKillList() { return secKillDao.queryAll(0,4); } @Override public SecKill getById(long secKillId) { return secKillDao.queryById(secKillId); } @Override public Exposer exportSecKillUrl(long secKillId) { SecKill secKill = secKillDao.queryById(secKillId); if(null == secKill){ return new Exposer(false,secKillId); } Date startTime = secKill.getStartTime(); Date endTime = secKill.getEndTime(); Date nowTime = new Date(); if(nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()){ return new Exposer(false,secKillId,nowTime.getTime(),startTime.getTime(),endTime.getTime()); } //转化特定字符串的过程,不可逆 String md5 = getMD5(secKillId); return new Exposer(true,md5,secKillId); } @Override public SecKillExecution executeSecKill(long secKillId, long userPhone, String md5) throws SecKillException, RepeatKillException, SecKillCloseException { if(null == md5 || md5.equals(getMD5(secKillId))){ throw new SecKillException("seckill datarewirte"); } try{ //执行秒杀逻辑,减库存,记录购买行为 Date nowTime = new Date(); //减库存 int updateCount = secKillDao.reduceNumber(secKillId,nowTime); if(updateCount <= 0){ //没有更新到记录,秒杀结束 throw new SecKillCloseException("seckill is Closed"); }else{ //记录购买行为 int insertCount = successKilledDao.insertSuccessKilled(secKillId,userPhone); //唯一:secKillId,userPhone if(insertCount <= 0){ //重复秒杀 throw new RepeatKillException("seckill repeated"); }else{ //秒杀成功 SuccessKilled successKilled = successKilledDao.queryByIdWithSecKill(secKillId,userPhone); return new SecKillExecution(secKillId, SecKillStatEnum.SUCCESS,successKilled); } } }catch(SecKillCloseException e1){ throw e1; }catch(RepeatKillException e2){ throw e2; }catch (Exception e){ logger.error(e.getMessage(),e); //所有编译期异常,转化为运行期异常 throw new SecKillException("seckill inner error:" + e.getMessage()); } } /** * 生成MD5 * @param secKillId * @return */ private String getMD5(long secKillId){ String base = secKillId + "/" + salt; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } } -
基于Spring托管Service实现类
- 创建Spring的service配置spring-service.xml,进行service包下的注解类型的扫描配置
- 在service实现类中添加上@Service的注解,在类中的dao对象添加上@Autowired的注解
-
配置并使用Spring声明式事务
- 在spring-service.xml中添加上配置事务管理器
- 在spring-service.xml中添加上配置基于注解的声明式事务
- 在业务类的executeSecKill方法中添加上@Transactional事务注解
- 学习点:使用注解控制事务方法的优点
- 开发团队达到一致约定,明确标注事务方法的编程风格
- 保证事务方法的执行时间尽可能短,不要穿插其他网络操作RPC/HTTP请求,或者剥离到事务方法外部
- 不是所有的方法都需要事务,如只有一条修改操作,只读操作就不需要事务控制
-
Service集成测试
- 添加上logback的日志配置文件logback.xml
[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n - 使用IDEA为SecKillService业务接口创建单元测试类SecKillServiceTest
- 编写单元测试方法
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:spring/spring-dao.xml","classpath:spring/spring-service.xml"}) public class SecKillServiceTest { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private SecKillService secKillService; @Test public void testGetSecKillList() throws Exception { List
list = secKillService.getSecKillList(); logger.info("list={}",list); } @Test public void testGetById() throws Exception { SecKill secKill = secKillService.getById(1000L); logger.info("secKill:{}",secKill); } /** * 测试完整业务,注意集成测试代码完整逻辑,注意可重复执行 * @throws Exception */ @Test public void testSecKillLogic() throws Exception { long id = 1000L; Exposer exposer = secKillService.exportSecKillUrl(id); if(exposer.isExposed()){ logger.info("exposer={}",exposer); long phone = 18059830432L; SecKillExecution secKillExecution = secKillService.executeSecKill(id,phone,exposer.getMd5()); logger.info("secKillExecution:{}",secKillExecution); }else{ //秒杀未开始 logger.warn("exposer={}",exposer); } } @Test public void testExportSecKillUrl() throws Exception { long id = 1000L; Exposer exposer = secKillService.exportSecKillUrl(id); logger.info("exposer={}",exposer); } @Test public void testExecuteSecKill() throws Exception { long id = 1000L; long phone = 18059830452L; String md5 = "f1974250b060f51c4a8e48df67232d53"; SecKillExecution secKillExecution = secKillService.executeSecKill(id,phone,md5); logger.info("secKillExecution:{}",secKillExecution); } } - 单元测试的学习点
- 集成测试的业务逻辑的完整性
- 注意测试的可重复执行
WEB层设计
设计Restful接口
-
SpringMVC整合Spring
- 在web.xml中配置DispatcherServlet
- 创建web包
- 创建spring-web.xml配置文件
- 在spring-web.xml进行SpringMVC的配置
- 开启SpringMVC注解模式
- servlet-mapping映射路径
- 配置jsp显示viewResolver
- 扫描web相关的bean
-
实现秒杀相关的Restful接口
- 创建控制类SecKillController,实现获取列表,获取单条数据,获取系统时间,获取秒杀地址,秒杀的方法
@Controller @RequestMapping("/seckill/")//模块/资源 public class SecKillController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private SecKillService secKillService; @RequestMapping(name="/list",method= RequestMethod.GET) public String list(Model model){ List
list = secKillService.getSecKillList(); model.addAttribute("list",list); return "list"; } @RequestMapping(value="/{secKillId}/detail",method=RequestMethod.GET) public String detail(@PathVariable("secKillId") Long secKillId,Model model){ if(secKillId == null){ return "redirect:/seckill/list"; } SecKill secKill = secKillService.getById(secKillId); if(secKill == null){ return "redirect:/seckill/list"; } model.addAttribute("secKill",secKill); return "detail"; } @RequestMapping(value="/{secKillId}/exposer",method = RequestMethod.POST, produces = {"application/json;charset=utf-8"}) @ResponseBody public SecKillResult exposer(@PathVariable("secKillId") Long secKillId){ SecKillResult result = null; try{ Exposer exposer = secKillService.exportSecKillUrl(secKillId); result = new SecKillResult (true,exposer); }catch(Exception e){ logger.error(e.getMessage(),e); result = new SecKillResult (false,e.getMessage()); } return result; } @RequestMapping(value="/{secKillId}/{md5}/execution", method = RequestMethod.POST, produces = {"application/json;charset=utf-8"}) public SecKillResult excute(@PathVariable("secKillId") Long secKillId, @PathVariable("md5") String md5, @CookieValue(value="killPhone",required = false) Long userPhone){ //springmvc valid if(userPhone == null){ return new SecKillResult (false,"未注册"); } SecKillResult result = null; try{ SecKillExecution secKillExecution = secKillService.executeSecKill(secKillId,userPhone,md5); result = new SecKillResult (true,secKillExecution); }catch(RepeatKillException e){ SecKillExecution secKillExecution = new SecKillExecution(secKillId, SecKillStatEnum.REPEAT); result = new SecKillResult (false,secKillExecution); }catch(SecKillCloseException e){ SecKillExecution secKillExecution = new SecKillExecution(secKillId, SecKillStatEnum.END); result = new SecKillResult (false,secKillExecution); }catch(Exception e){ logger.error(e.getMessage(),e); SecKillExecution secKillExecution = new SecKillExecution(secKillId, SecKillStatEnum.INNER_ERROR); result = new SecKillResult (false,secKillExecution); } return result; } @RequestMapping(value="/time/now",method=RequestMethod.GET) public SecKillResult time(){ Date now = new Date(); return new SecKillResult (true,now.getTime()); } }
- 基于Bootstrap开发页面结构
-
创建jsp文件夹,创建common/header.jsp,common/tag.jsp,list.jsp,detail.jsp,并引入bootstrap框架,jquery、cookie、countdown插件,可以从百度和bootcss的CDN中引入插件。
- 链接:http://www.bootcdn.cn/
创建js文件seckill.js,进行登录、计时的交互逻辑的编码,并在详细页面中引入
var seckill = { //封装秒杀相关ajax的url URL: { now: function(){ return '/seckill/time/now'; }, exposer: function(id){ return '/seckill/' + id + '/exposer'; }, execution : function(id,md5){ return '/seckill/' + id + '/' + md5 + '/execution'; } }, //处理秒杀逻辑 handleSecKillKill: function(secKillId,node){ node.hide().html(''); $.post(seckill.URL.exposer(secKillId),{},function(result){ if(result && result.success){ var exposer = result.data; if(exposer.exposed){ //开启秒杀 //获取秒杀地址 var killUrl = seckill.URL.execution(secKillId,exposer.md5); console.log('killUrl:',killUrl); //绑定一次点击事件 $('#killBtn').one('click',function(){ //执行秒杀请求 $(this).addClass('disabled'); $.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{ //未开启秒杀 //重新计算计时逻辑 seckill.countdown(secKillId,exposer.now,exposer.start,exposer.end); } }else{ console.error('result:',result); } }); }, //计时 countdown: function(secKillId,nowTime,startTime,endTime){ var $secKillBox = $('#seckill-box'); if(nowTime > endTime){ $secKillBox.html('秒杀结束'); }else if(nowTime < startTime){ $secKillBox.html('秒杀未开始'); var killTime = new Date(startTime + 1000); $secKillBox.countdown(killTime,function(event){ var format = event.strftime('秒杀倒计时:%D天 %H时 %M分 %S秒'); $secKillBox.html(format); }).on('finish.countdown',function(){ //获取秒杀地址,控制实现逻辑,执行秒杀 seckill.handleSecKillKill(secKillId,$secKillBox); }); }else{ //秒杀开始 seckill.handleSecKillKill(secKillId,$secKillBox); } }, //验证手机号 validatePhone: function(phone){ if(phone && phone.length == 11 && !isNaN(phone)){ return true; }else{ return false; } }, //详情页秒杀逻辑 detail: { //详情页初始化 init: function(params){ //用户手机验证和登录,计时交互 //规划交互流程 //在cookie中查找手机号 var killPhone = $.cookie('killPhone'), startTime = params.startTime, endTime = params.endTime, secKillId = params.secKillId; //验证手机号 if(!seckill.validatePhone(killPhone)){ var killPhoneModal = $('#killPhoneModal'); killPhoneModal.modal({ show: true, backdrop: 'static',//禁止位置关闭 keyboard: false//关闭键盘事件 }); $('#killPhoneBtn').click(function(){ var inputPhone = $('#killPhoneKey').val(); if(seckill.validatePhone(inputPhone)){ //电话写入cookie $.cookie('killPhone',inputPhone,{expires:7,path: '/seckill'}) window.location.reload(); }else{ //正常下会有一个前端字典 $('#killPhoneMessage').hide().html('').show(300); } }); } //用户已经登录 //计时交互 $.get(seckill.URL.now(),function(result){ if(result && result.success){ var nowTime = result.data; seckill.countdown(secKillId,nowTime,startTime,endTime); }else{ consolw.error('result:',result); } }); } } }
- 在detail.jsp页面中引入seckill.js文件,并进行初始化
-