From 101fdb39528dc1c70eb7405470b57e1f419f8c50 Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Sat, 4 Mar 2017 16:41:39 +0800 Subject: [PATCH 1/8] =?UTF-8?q?JavaMail=E6=B3=A8=E5=86=8C=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RegisterDemo/.classpath | 28 +++++++ RegisterDemo/.project | 42 +++++++++++ RegisterDemo/.settings/.jsdtscope | 13 ++++ .../org.eclipse.core.resources.prefs | 2 + .../.settings/org.eclipse.core.runtime.prefs | 2 + .../.settings/org.eclipse.jdt.core.prefs | 8 ++ .../.settings/org.eclipse.m2e.core.prefs | 4 + .../org.eclipse.wst.common.component | 11 +++ ....eclipse.wst.common.project.facet.core.xml | 7 ++ ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../org.eclipse.wst.validation.prefs | 2 + RegisterDemo/pom.xml | 51 +++++++++++++ RegisterDemo/register.sql | 13 ++++ .../src/main/java/com/dragon/dao/UserDao.java | 11 +++ .../java/com/dragon/dao/impl/UserDaoImpl.java | 51 +++++++++++++ .../src/main/java/com/dragon/model/User.java | 71 ++++++++++++++++++ .../java/com/dragon/service/UserService.java | 10 +++ .../dragon/service/impl/UserServiceImpl.java | 42 +++++++++++ .../com/dragon/servlet/ActiveServlet.java | 33 ++++++++ .../com/dragon/servlet/RegisterServlet.java | 40 ++++++++++ .../main/java/com/dragon/util/CodeUtil.java | 10 +++ .../src/main/java/com/dragon/util/DBUtil.java | 43 +++++++++++ .../main/java/com/dragon/util/MailUtil.java | 69 +++++++++++++++++ .../src/main/resources/c3p0-config.xml | 17 +++++ RegisterDemo/src/main/webapp/WEB-INF/web.xml | 22 ++++++ RegisterDemo/src/main/webapp/fail.jsp | 13 ++++ RegisterDemo/src/main/webapp/index.jsp | 33 ++++++++ RegisterDemo/src/main/webapp/result.jsp | 12 +++ RegisterDemo/src/main/webapp/welcome.jsp | 13 ++++ RegisterDemo/target/classes/c3p0-config.xml | 17 +++++ .../classes/com/dragon/dao/UserDao.class | Bin 0 -> 199 bytes .../com/dragon/dao/impl/UserDaoImpl.class | Bin 0 -> 1983 bytes .../classes/com/dragon/model/User.class | Bin 0 -> 1789 bytes .../com/dragon/service/UserService.class | Bin 0 -> 248 bytes .../dragon/service/impl/UserServiceImpl.class | Bin 0 -> 1641 bytes .../com/dragon/servlet/ActiveServlet.class | Bin 0 -> 1590 bytes .../com/dragon/servlet/RegisterServlet.class | Bin 0 -> 2339 bytes .../classes/com/dragon/util/CodeUtil.class | Bin 0 -> 587 bytes .../classes/com/dragon/util/DBUtil.class | Bin 0 -> 1274 bytes .../classes/com/dragon/util/MailUtil$1.class | Bin 0 -> 742 bytes .../classes/com/dragon/util/MailUtil.class | Bin 0 -> 3074 bytes .../web-resources/META-INF/MANIFEST.MF | 5 ++ .../com.dragon/RegisterDemo/pom.properties | 7 ++ .../maven/com.dragon/RegisterDemo/pom.xml | 51 +++++++++++++ .../compile/default-compile/createdFiles.lst | 0 .../compile/default-compile/inputFiles.lst | 10 +++ 47 files changed, 765 insertions(+) create mode 100644 RegisterDemo/.classpath create mode 100644 RegisterDemo/.project create mode 100644 RegisterDemo/.settings/.jsdtscope create mode 100644 RegisterDemo/.settings/org.eclipse.core.resources.prefs create mode 100644 RegisterDemo/.settings/org.eclipse.core.runtime.prefs create mode 100644 RegisterDemo/.settings/org.eclipse.jdt.core.prefs create mode 100644 RegisterDemo/.settings/org.eclipse.m2e.core.prefs create mode 100644 RegisterDemo/.settings/org.eclipse.wst.common.component create mode 100644 RegisterDemo/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 RegisterDemo/.settings/org.eclipse.wst.validation.prefs create mode 100644 RegisterDemo/pom.xml create mode 100644 RegisterDemo/register.sql create mode 100644 RegisterDemo/src/main/java/com/dragon/dao/UserDao.java create mode 100644 RegisterDemo/src/main/java/com/dragon/dao/impl/UserDaoImpl.java create mode 100644 RegisterDemo/src/main/java/com/dragon/model/User.java create mode 100644 RegisterDemo/src/main/java/com/dragon/service/UserService.java create mode 100644 RegisterDemo/src/main/java/com/dragon/service/impl/UserServiceImpl.java create mode 100644 RegisterDemo/src/main/java/com/dragon/servlet/ActiveServlet.java create mode 100644 RegisterDemo/src/main/java/com/dragon/servlet/RegisterServlet.java create mode 100644 RegisterDemo/src/main/java/com/dragon/util/CodeUtil.java create mode 100644 RegisterDemo/src/main/java/com/dragon/util/DBUtil.java create mode 100644 RegisterDemo/src/main/java/com/dragon/util/MailUtil.java create mode 100644 RegisterDemo/src/main/resources/c3p0-config.xml create mode 100644 RegisterDemo/src/main/webapp/WEB-INF/web.xml create mode 100644 RegisterDemo/src/main/webapp/fail.jsp create mode 100644 RegisterDemo/src/main/webapp/index.jsp create mode 100644 RegisterDemo/src/main/webapp/result.jsp create mode 100644 RegisterDemo/src/main/webapp/welcome.jsp create mode 100644 RegisterDemo/target/classes/c3p0-config.xml create mode 100644 RegisterDemo/target/classes/com/dragon/dao/UserDao.class create mode 100644 RegisterDemo/target/classes/com/dragon/dao/impl/UserDaoImpl.class create mode 100644 RegisterDemo/target/classes/com/dragon/model/User.class create mode 100644 RegisterDemo/target/classes/com/dragon/service/UserService.class create mode 100644 RegisterDemo/target/classes/com/dragon/service/impl/UserServiceImpl.class create mode 100644 RegisterDemo/target/classes/com/dragon/servlet/ActiveServlet.class create mode 100644 RegisterDemo/target/classes/com/dragon/servlet/RegisterServlet.class create mode 100644 RegisterDemo/target/classes/com/dragon/util/CodeUtil.class create mode 100644 RegisterDemo/target/classes/com/dragon/util/DBUtil.class create mode 100644 RegisterDemo/target/classes/com/dragon/util/MailUtil$1.class create mode 100644 RegisterDemo/target/classes/com/dragon/util/MailUtil.class create mode 100644 RegisterDemo/target/m2e-wtp/web-resources/META-INF/MANIFEST.MF create mode 100644 RegisterDemo/target/m2e-wtp/web-resources/META-INF/maven/com.dragon/RegisterDemo/pom.properties create mode 100644 RegisterDemo/target/m2e-wtp/web-resources/META-INF/maven/com.dragon/RegisterDemo/pom.xml create mode 100644 RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/RegisterDemo/.classpath b/RegisterDemo/.classpath new file mode 100644 index 0000000..677d95d --- /dev/null +++ b/RegisterDemo/.classpath @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RegisterDemo/.project b/RegisterDemo/.project new file mode 100644 index 0000000..87738cf --- /dev/null +++ b/RegisterDemo/.project @@ -0,0 +1,42 @@ + + + RegisterDemo + + + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/RegisterDemo/.settings/.jsdtscope b/RegisterDemo/.settings/.jsdtscope new file mode 100644 index 0000000..2418123 --- /dev/null +++ b/RegisterDemo/.settings/.jsdtscope @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/RegisterDemo/.settings/org.eclipse.core.resources.prefs b/RegisterDemo/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..3d6cbf0 --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//src/main/webapp/index.jsp=UTF-8 diff --git a/RegisterDemo/.settings/org.eclipse.core.runtime.prefs b/RegisterDemo/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000..deae05a --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\r\n diff --git a/RegisterDemo/.settings/org.eclipse.jdt.core.prefs b/RegisterDemo/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..6e80039 --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/RegisterDemo/.settings/org.eclipse.m2e.core.prefs b/RegisterDemo/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/RegisterDemo/.settings/org.eclipse.wst.common.component b/RegisterDemo/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..5bdc0a7 --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.wst.common.component @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/RegisterDemo/.settings/org.eclipse.wst.common.project.facet.core.xml b/RegisterDemo/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..9f23f94 --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.container b/RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 0000000..3bd5d0a --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary \ No newline at end of file diff --git a/RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.name b/RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 0000000..05bd71b --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window \ No newline at end of file diff --git a/RegisterDemo/.settings/org.eclipse.wst.validation.prefs b/RegisterDemo/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 0000000..04cad8c --- /dev/null +++ b/RegisterDemo/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,2 @@ +disabled=06target +eclipse.preferences.version=1 diff --git a/RegisterDemo/pom.xml b/RegisterDemo/pom.xml new file mode 100644 index 0000000..b41ab00 --- /dev/null +++ b/RegisterDemo/pom.xml @@ -0,0 +1,51 @@ + + 4.0.0 + com.dragon + RegisterDemo + war + 0.0.1-SNAPSHOT + RegisterDemo Maven Webapp + http://maven.apache.org + + + + javaee + javaee-api + 5 + test + + + taglibs + standard + 1.1.2 + + + + mysql + mysql-connector-java + 5.1.40 + + + + c3p0 + c3p0 + 0.9.1.2 + + + + javax.mail + mail + 1.4.7 + + + javax.activation + activation + 1.1.1 + + + + + RegisterDemo + + diff --git a/RegisterDemo/register.sql b/RegisterDemo/register.sql new file mode 100644 index 0000000..fb1da7f --- /dev/null +++ b/RegisterDemo/register.sql @@ -0,0 +1,13 @@ +use test1; +drop table user; +create table `user`( + id int(11) primary key auto_increment comment '用户id', + username varchar(255) not null comment '用户名', + email varchar(255) not null comment '用户邮箱', + password varchar(255) not null comment '用户密码', + state int(1) not null default 0 comment '用户激活状态:0表示未激活,1表示激活', + code varchar(255) not null comment '激活码' +)engine=InnoDB default charset=utf8; + +select * from user; +delete from user; \ No newline at end of file diff --git a/RegisterDemo/src/main/java/com/dragon/dao/UserDao.java b/RegisterDemo/src/main/java/com/dragon/dao/UserDao.java new file mode 100644 index 0000000..45a021d --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/dao/UserDao.java @@ -0,0 +1,11 @@ +package com.dragon.dao; + +import com.dragon.model.User; + +public interface UserDao { + + int save(User user); + + int activeUser(String code); + +} diff --git a/RegisterDemo/src/main/java/com/dragon/dao/impl/UserDaoImpl.java b/RegisterDemo/src/main/java/com/dragon/dao/impl/UserDaoImpl.java new file mode 100644 index 0000000..567c944 --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/dao/impl/UserDaoImpl.java @@ -0,0 +1,51 @@ +package com.dragon.dao.impl; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import com.dragon.dao.UserDao; +import com.dragon.model.User; +import com.dragon.util.DBUtil; + +public class UserDaoImpl implements UserDao{ + + @Override + public int save(User user) { + int num=0; + try { + Connection conn=DBUtil.getConnection(); + String sql ="insert into user(username,email,password,state,code) values(?,?,?,?,?)"; + PreparedStatement pstmt=conn.prepareStatement(sql); + pstmt.setString(1, user.getUserName()); + pstmt.setString(2, user.getEmail()); + pstmt.setString(3, user.getPassword()); + pstmt.setInt(4, user.getState()); + pstmt.setString(5, user.getCode()); + num=pstmt.executeUpdate(); + DBUtil.close(conn,pstmt, null); + } catch (SQLException e) { + e.printStackTrace(); + } + return num; + + } + + @Override + public int activeUser(String code) { + int num=0; + try { + Connection conn=DBUtil.getConnection(); + String sql="update user set state=1 where code=?"; + PreparedStatement pstmt=conn.prepareStatement(sql); + pstmt.setString(1, code); + num=pstmt.executeUpdate(); + DBUtil.close(conn,pstmt,null); + } catch (SQLException e) { + e.printStackTrace(); + } + return num; + + } + +} diff --git a/RegisterDemo/src/main/java/com/dragon/model/User.java b/RegisterDemo/src/main/java/com/dragon/model/User.java new file mode 100644 index 0000000..4902b6e --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/model/User.java @@ -0,0 +1,71 @@ +package com.dragon.model; + +public class User { + private Integer id;// 用户id + private String userName;// 用户名 + private String email;// 用户邮箱 + private String password;// 用户密码 + private Integer state;// 用户账号状态:0表示未激活,1表示激活 + private String code;// 激活码 + + public User(){ + + } + + public User(String userName, String email, String password, Integer state, String code) { + this.userName = userName; + this.email = email; + this.password = password; + this.state = state; + this.code = code; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getState() { + return state; + } + + public void setState(Integer state) { + this.state = state; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + +} diff --git a/RegisterDemo/src/main/java/com/dragon/service/UserService.java b/RegisterDemo/src/main/java/com/dragon/service/UserService.java new file mode 100644 index 0000000..ef27dbf --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/service/UserService.java @@ -0,0 +1,10 @@ +package com.dragon.service; + + +public interface UserService { + + boolean doRegister(String userName, String password, String email); + + boolean activeUser(String code); + +} diff --git a/RegisterDemo/src/main/java/com/dragon/service/impl/UserServiceImpl.java b/RegisterDemo/src/main/java/com/dragon/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..e08729e --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/service/impl/UserServiceImpl.java @@ -0,0 +1,42 @@ +package com.dragon.service.impl; + +import com.dragon.dao.UserDao; +import com.dragon.dao.impl.UserDaoImpl; +import com.dragon.model.User; +import com.dragon.service.UserService; +import com.dragon.util.CodeUtil; +import com.dragon.util.MailUtil; + +public class UserServiceImpl implements UserService { + + @Override + public boolean doRegister(String userName, String password, String email) { + // 这里可以验证各字段是否为空 + + //利用正则表达式(可改进)验证邮箱是否符合邮箱的格式 + if(!email.matches("^\\w+@(\\w+\\.)+\\w+$")){ + return false; + } + //生成激活码 + String code=CodeUtil.generateUniqueCode(); + User user=new User(userName,email,password,0,code); + //将用户保存到数据库 + UserDao userDao=new UserDaoImpl(); + //保存成功则通过线程的方式给用户发送一封邮件 + if(userDao.save(user)>0){ + new Thread(new MailUtil(email, code)).start();; + return true; + } + return false; + } + + @Override + public boolean activeUser(String code) { + UserDao userDao=new UserDaoImpl(); + if(userDao.activeUser(code)>0){ + return true; + }else{ + return false; + } + } +} diff --git a/RegisterDemo/src/main/java/com/dragon/servlet/ActiveServlet.java b/RegisterDemo/src/main/java/com/dragon/servlet/ActiveServlet.java new file mode 100644 index 0000000..0ee7c0f --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/servlet/ActiveServlet.java @@ -0,0 +1,33 @@ +package com.dragon.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.dragon.service.UserService; +import com.dragon.service.impl.UserServiceImpl; + +public class ActiveServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String code=request.getParameter("code"); + UserService userService=new UserServiceImpl(); + if(userService.activeUser(code)){ + request.getRequestDispatcher("/welcome.jsp").forward(request, response); + }else{ + request.getRequestDispatcher("/fail.jsp").forward(request, response); + } + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + this.doGet(request, response); + } + +} diff --git a/RegisterDemo/src/main/java/com/dragon/servlet/RegisterServlet.java b/RegisterDemo/src/main/java/com/dragon/servlet/RegisterServlet.java new file mode 100644 index 0000000..c1921c9 --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/servlet/RegisterServlet.java @@ -0,0 +1,40 @@ +package com.dragon.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.dragon.service.UserService; +import com.dragon.service.impl.UserServiceImpl; + +public class RegisterServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + this.doPost(request, response); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String userName=request.getParameter("username"); + String password=request.getParameter("password"); + String email=request.getParameter("email"); + System.out.println(userName+" "+password+" "+email); + UserService userService=new UserServiceImpl(); + + if(userService.doRegister(userName,password,email)){ + request.setAttribute("msg", "注册成功,请登录邮箱激活账号"); + }else{ + request.setAttribute("msg", "注册失败,请检查相关信息"); + } + + request.getRequestDispatcher("/result.jsp").forward(request, response); + } + + +} diff --git a/RegisterDemo/src/main/java/com/dragon/util/CodeUtil.java b/RegisterDemo/src/main/java/com/dragon/util/CodeUtil.java new file mode 100644 index 0000000..7f284fb --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/util/CodeUtil.java @@ -0,0 +1,10 @@ +package com.dragon.util; + +import java.util.UUID; + +public class CodeUtil { + //生成唯一的激活码 + public static String generateUniqueCode(){ + return UUID.randomUUID().toString().replaceAll("-", ""); + } +} diff --git a/RegisterDemo/src/main/java/com/dragon/util/DBUtil.java b/RegisterDemo/src/main/java/com/dragon/util/DBUtil.java new file mode 100644 index 0000000..ebd7060 --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/util/DBUtil.java @@ -0,0 +1,43 @@ +package com.dragon.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import com.mchange.v2.c3p0.ComboPooledDataSource; + +public class DBUtil { + private static ComboPooledDataSource cpds=null; + + static{ + cpds=new ComboPooledDataSource("mysql"); + } + + public static Connection getConnection(){ + Connection connection=null; + try { + connection = cpds.getConnection(); + } catch (SQLException e) { + e.printStackTrace(); + } + return connection; + } + + public static void close(Connection conn,PreparedStatement pstmt,ResultSet rs){ + try { + if(rs!=null){ + rs.close(); + } + if(pstmt!=null){ + pstmt.close(); + } + if(rs!=null){ + rs.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/RegisterDemo/src/main/java/com/dragon/util/MailUtil.java b/RegisterDemo/src/main/java/com/dragon/util/MailUtil.java new file mode 100644 index 0000000..1e2394c --- /dev/null +++ b/RegisterDemo/src/main/java/com/dragon/util/MailUtil.java @@ -0,0 +1,69 @@ +package com.dragon.util; + +import java.util.Properties; +import javax.mail.Authenticator; +import javax.mail.Message; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import com.sun.mail.util.MailSSLSocketFactory; + +public class MailUtil implements Runnable { + private String email;// 收件人邮箱 + private String code;// 激活码 + + public MailUtil(String email, String code) { + this.email = email; + this.code = code; + } + + public void run() { + // 1.创建连接对象javax.mail.Session + // 2.创建邮件对象 javax.mail.Message + // 3.发送一封激活邮件 + String from = "xxx@qq.com";// 发件人电子邮箱 + String host = "smtp.qq.com"; // 指定发送邮件的主机smtp.qq.com(QQ)|smtp.163.com(网易) + + Properties properties = System.getProperties();// 获取系统属性 + + properties.setProperty("mail.smtp.host", host);// 设置邮件服务器 + properties.setProperty("mail.smtp.auth", "true");// 打开认证 + + try { + //QQ邮箱需要下面这段代码,163邮箱不需要 + MailSSLSocketFactory sf = new MailSSLSocketFactory(); + sf.setTrustAllHosts(true); + properties.put("mail.smtp.ssl.enable", "true"); + properties.put("mail.smtp.ssl.socketFactory", sf); + + + // 1.获取默认session对象 + Session session = Session.getDefaultInstance(properties, new Authenticator() { + public PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("xxx@qq.com", "xxx"); // 发件人邮箱账号、授权码 + } + }); + + // 2.创建邮件对象 + Message message = new MimeMessage(session); + // 2.1设置发件人 + message.setFrom(new InternetAddress(from)); + // 2.2设置接收人 + message.addRecipient(Message.RecipientType.TO, new InternetAddress(email)); + // 2.3设置邮件主题 + message.setSubject("账号激活"); + // 2.4设置邮件内容 + String content = "

这是一封激活邮件,激活请点击以下链接

http://localhost:8080/RegisterDemo/ActiveServlet?code=" + code + + "

"; + message.setContent(content, "text/html;charset=UTF-8"); + // 3.发送邮件 + Transport.send(message); + System.out.println("邮件成功发送!"); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/RegisterDemo/src/main/resources/c3p0-config.xml b/RegisterDemo/src/main/resources/c3p0-config.xml new file mode 100644 index 0000000..392a4e4 --- /dev/null +++ b/RegisterDemo/src/main/resources/c3p0-config.xml @@ -0,0 +1,17 @@ + + + + com.mysql.jdbc.Driver + jdbc:mysql://127.0.0.1:3306/test1?useSSL=false + root + 123456 + + 5 + + 30 + + 20 + + 5 + + \ No newline at end of file diff --git a/RegisterDemo/src/main/webapp/WEB-INF/web.xml b/RegisterDemo/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..063433a --- /dev/null +++ b/RegisterDemo/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,22 @@ + + + + registerServlet + com.dragon.servlet.RegisterServlet + + + registerServlet + /register + + + + ActiveServlet + com.dragon.servlet.ActiveServlet + + + ActiveServlet + /ActiveServlet + + diff --git a/RegisterDemo/src/main/webapp/fail.jsp b/RegisterDemo/src/main/webapp/fail.jsp new file mode 100644 index 0000000..0913637 --- /dev/null +++ b/RegisterDemo/src/main/webapp/fail.jsp @@ -0,0 +1,13 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +Insert title here + + +

激活失败,请检查激活码!

+
+ + \ No newline at end of file diff --git a/RegisterDemo/src/main/webapp/index.jsp b/RegisterDemo/src/main/webapp/index.jsp new file mode 100644 index 0000000..798abfd --- /dev/null +++ b/RegisterDemo/src/main/webapp/index.jsp @@ -0,0 +1,33 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%String basePath=request.getContextPath(); %> + + + + 注册 + + + +

注册页面

+
+
+ + + + + + + + + + + + + + + +
+
+ + diff --git a/RegisterDemo/src/main/webapp/result.jsp b/RegisterDemo/src/main/webapp/result.jsp new file mode 100644 index 0000000..759b691 --- /dev/null +++ b/RegisterDemo/src/main/webapp/result.jsp @@ -0,0 +1,12 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +结果页 + + + ${msg } + + \ No newline at end of file diff --git a/RegisterDemo/src/main/webapp/welcome.jsp b/RegisterDemo/src/main/webapp/welcome.jsp new file mode 100644 index 0000000..3cc66dc --- /dev/null +++ b/RegisterDemo/src/main/webapp/welcome.jsp @@ -0,0 +1,13 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +激活成功 + + +

激活成功,请登录!

+
+ + \ No newline at end of file diff --git a/RegisterDemo/target/classes/c3p0-config.xml b/RegisterDemo/target/classes/c3p0-config.xml new file mode 100644 index 0000000..392a4e4 --- /dev/null +++ b/RegisterDemo/target/classes/c3p0-config.xml @@ -0,0 +1,17 @@ + + + + com.mysql.jdbc.Driver + jdbc:mysql://127.0.0.1:3306/test1?useSSL=false + root + 123456 + + 5 + + 30 + + 20 + + 5 + + \ No newline at end of file diff --git a/RegisterDemo/target/classes/com/dragon/dao/UserDao.class b/RegisterDemo/target/classes/com/dragon/dao/UserDao.class new file mode 100644 index 0000000000000000000000000000000000000000..2841b44afb809a03a24af7985df80825c5c78b10 GIT binary patch literal 199 zcmYk0F%E)26h!AEf}(|u7M=hbx3RM|CMF~n!~=Ym4S@igEXd(3Jb;HX?y8BllbJW~ z@x0vu>@W-A5mr(+A~#&I)qPHR6MY#xh=#wtCZ{se#e6%tmEWw*XUf; z@8y1TB1A0va!`&$SjNeZlJ=%-i%%)lS0mN2tSxT-ecifhKp>2)BH&qxF#!8Y3D=FWSp6ImMyLOgb|`|50E1 z(ibW|^au23^oO)vXEG!K?DzG{mpS{~_Fil4efFP!zx@NiZAc9pDim zbPc=JZW`OZ^j1x`Oj|=j;M|_MZyHV0*)^W*>`5!okP^6f;%anDAa&Pv?BJe2JYT2_ zBv#yp%pi^-9U9UCXDhZNtDV-4^tQ~MrW6>dxR%+ho1U%a(PAQaY5M}xl@oDJLM$
90WPb2$i($K5N`-O;7ciz_^f>HJU^k7`2iqKUT*SD*@U9G2T*o0v+jRuS^My*t z-v7C2^fyXrWChl3he`!G+X>uUhh|=V9J3{hvSr%MV%zlnSFYD6`hgk9qD4f7+`ifD zNI!qS82t)ExP;3ZrUWj$Bb1K!afLZ(d$MhMvZg$>q!S1%Rik~mP19M#go7ZtuMNc#Ono>_KF6g**1Zgu8Q>}lbvw}bF9uE1D(lMN6Cy=|+ zBjr!8g! zP#RJlU)8auJPpg&(&_|qyWL=tRLMQmv4Jx0TTRzzfQ=|+C#tDmVy8qb_4l4hztaqA zGAJo1kMLMSg}i$Ss6DN$y|!dqRj!U|_|)303aW2fKW%xYMFa^|qO9xxcq!Dt>9hm{ zK_@Ie1P16M7Aq0BV1-~$*#d3xO1$%9o1>3 z=5{9Az%NgC{Jiojw@^qHsS~I@<1|!vxiKWDRHgSl_7?zURWMb|BxkJpvkxeFP7b81G z+lXw9_J;>pJj7jfEY0a>(bgeW1eWKsN%02jzaU+mKfo7LzcO|VJCH1>c!+h0$Utl{ z;!0rzs*Xqb%V7*fcF8*!$3sk@hAf_Al6NoRM@(UljY{#`pd927@l)0lY=%+x@D$JJ zH-#6xy~TJLe1&b)k)Z!7zT_-PUkQATZ@BxMent`dhv4EGgobH~tKm_C-d^C_u4?!z zQf)C(?Yonz{SW>{Chmy;EKz}-Foqc-y}?8Qp h87Wn7_xR5A_+IVtorw4@oaSppe9b;ziYbK-;X92^+irC(+@}4m z>-t%w6(%p8PmbMm+70{l`K4PA6jI)W!pw31;;0?C4cC9Fkh|i$6Q|`WOb-7bOR@#f4a`V3YmSc?F9!4Bh}g&Phapfj}c6nxPx(piDR$r zo?NxgUH`3f-V6hE>Q3{_@jcnchw0#h*HtJUYcnIs`|54J|2(-)`}VWi*(em|O=OW1 z>#m8>$XYa!kE|sVW5~#D#e|6*E75R+BUYqPtqn+CM-k6e69zI;v&eI9Fo|nNDccyO zq=iu{X8#eqX~q~;`c8#Gt=LDTa+ErZyT%8xDFNQx^iY)gJ9%*$H8sbJ*v#@&nyHZG zVPxe`J6C?)edS3XM}=ierV96%>j>vFgOnspD5yO=w${0gDXRbCp_cdUG+kzzopoE*+;(_6!X`0)X~r4$)SZKawM zDu)Vlk=G4yChUp?eu!$UH3(8nf>g91HCQ^3ViIJP;ns$L$XE!-x)!9A1gUC4HuNB+ zB*+FqHiv*n;{;?=3$l;|+17$==|L8fAiD(F8v-J8BOtbRM#@Q$Ct8qg{fv~8Ao~P) qHUvb*RzMzUE3%vfd9DR{tgpzx9~TYlOM)B@0g=B(Kz734WA`suYwtGz literal 0 HcmV?d00001 diff --git a/RegisterDemo/target/classes/com/dragon/service/UserService.class b/RegisterDemo/target/classes/com/dragon/service/UserService.class new file mode 100644 index 0000000000000000000000000000000000000000..8ffa9bf2184626f3622a2b02df837009dd9c496c GIT binary patch literal 248 zcma)%uMWa67{u@6&j!O_uxK#UU{%2}YFmqjU27DJLlF5}r|1#JrEI0BWUa;SRI>7ftgw65rt$0u ztJ(IG$h|YQUjO6FkD*8^Z(5F%gvpPhS17j^r+8y34m4kQi@2K*2;J}r=mgq65=810 LgWwQ`(MR?K)pSH? literal 0 HcmV?d00001 diff --git a/RegisterDemo/target/classes/com/dragon/service/impl/UserServiceImpl.class b/RegisterDemo/target/classes/com/dragon/service/impl/UserServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..7ade70e875eabed0b7a350a1d7adb5d911b4413d GIT binary patch literal 1641 zcma)6YjYD-7=BJ}TQ|YdmNGW==Q9bTQejn@#vH ze()Fci(e?%8GnF3#oypCK4%k2N`u3PJ-2-?&-32?diBSj0IuLi12KVR$M4#$K(>9) z?x|qlI?8stdmVd|x90Gn%DaJtz{#g_AnlIy+IC~-sd7RCDS@+7OiXACq;9yL8{QO% z7p*OU#9hCoOvI7Sz`%^aY|Zu5M!&nGf`@XaqXe=w-;tdy8MyjBdQ617Zco6fP4PDk zaz(&w`43dv?S(23xL&M{@i)W3_1cwpd9fbN-~)lVCy$?%ZWp;cUbRZxmQ2j!l!5$_ zzzhv%a2f>xqboyaR}uC66zA3>6CdIu17`$IPmt<|ZpYR^+Tn3sqYM8C=3L5f5aiuNprIm#v>iA7jCahC<2Od)j~(Eq;tXZE;|wPj#M;M@e0mh{KPc~APfFin(@PQ%8h20kI` zH^g-zQZj?j@HtDbCkKq%LUFQ&#__3GRRdofN$11eK*^SYYg1BGmu^R=$i!7#*CAPB zKfO~(tSyrrcth)aC2(p|=Rx1|bkSFaGPg3gt$QTd3uO=r%=AfULw2Jk87Fa6&iub8 zd%b6V(4va!5+F%CQL!YnCY`;7I#aI&7%MWJl+i&SMUfqKq8Uo(=epb*HKi$?&>bi( zpp#I~U>zH8DfcQHvg!8&N8NXIm*=!rb39{KH6NS@2L-<*rlW@10SPm}Qo#-+&3 z9M>$;oD3(C#|4go5*BcceZGz}sI!?h%UZSQcC6B<* t(g8RhW@tK#i^yR)g1R`=ehoDrfnlNXMMH>15SXHWkQ@4L&`u0L{0pqRh;je` literal 0 HcmV?d00001 diff --git a/RegisterDemo/target/classes/com/dragon/servlet/ActiveServlet.class b/RegisterDemo/target/classes/com/dragon/servlet/ActiveServlet.class new file mode 100644 index 0000000000000000000000000000000000000000..1b6946f44bc93b34c196229845649f389eb3b9c6 GIT binary patch literal 1590 zcma)6*-{fh6g`~;CM3gR0NG?!)&#}{PzkcgZXr@csM5D)(uBbz6K6WWulNN%`yv&o z@&o)QEpJa^3`tz(p{MTcyPR{E{`TX`R{*ov*5G3pGVP3!ba=|n8m@58(?S}nrnJsQ zq9|zyFbtgWbADMiIF&MIY|~M)VQ3>?i>Hr-<63t1aA%!?y6@k#kr73cfxVsvB8T6Ep``qCfA%(**RwQer zc{XJvq+?}M@kx)^4V8ww4C7Trv`k@GnOxd9q~s<%PJ2)}V5(iBn5oVhtwgBcwL?lW`{u~KK&EjP!dd8$Iq)eBYC zxf-{47!NV4;Ss~&&EnRW>X^ekgLYy&7u-oQtW=LrskeWan(}RVSb})M&}5u&E3G(E zY^OMI)PXAC)qD0U2Sd;i#efXecxs(Wsnrg7BUd>L4BlU_Nxa1 zy=Xuq{WX$&2$GiE5vEfojX{4#|Awh6G=K6;TIo~{Kr`Ct)QbkVg$PLj1%Lu|dR9K% zMz-9mN$rrIs-iX(jeW-Y@A`x&w1U`(K@Z4K(X0$aD+AF= LKo~a3!-uV(cAS!j literal 0 HcmV?d00001 diff --git a/RegisterDemo/target/classes/com/dragon/servlet/RegisterServlet.class b/RegisterDemo/target/classes/com/dragon/servlet/RegisterServlet.class new file mode 100644 index 0000000000000000000000000000000000000000..3a94a1399cf46458ec5da4f77948f5220cbc9753 GIT binary patch literal 2339 zcma)7T~ixX7=BJZ*pMtO25Jq}LMxPy0IRm8mekr7XhHcZ6qIV!B{_tpNjB_mXdQ2= zQpa&Rqc>i3yy!Su^{O2#14Y!{`#Vg)EB}G|p4|{2$rLl0v*(dP*_5v5?@FGREVYX>nuRIZN;`9G+7b)SCs7S<6Z*{j}s|7^+EI zQxlWiFf~0jG0@Av-e9Qe(Nm_SrmRUdk>OtQEPjlk;*6Hktg{U6mexrIPmdnsez>tu zh93cj{exPH4`-6o+_<7nCpbfJP>+%WmE6!df9bJiHIt!naBmzD25(Hi#4U!gmcggv z8RPRAZd#E&#mqF};}HSD-*+?0(-xsOB^)fFEYDOyNJ5CAMueegx-u|Qc;N@bAsNr3 zmRO4Eqq<2EONJ8>L-U?7IiWN74DN)-us7a35Fd_`Vuq5LQj$3# z76v)atx?q=15`x}#~klULQTb$am&zB@kpzUV%L=qZR9$wn&u7Ni22Y?`Qk}cOH`s0 zFG*0S$ZWbCJ0DVsCS|;gSEz>c3~_Y8u}N%>k`IeKaWxr{aDw6B4n;eF3mGjDejLXcQN*uNMCFiA`tUk6M}rVNFXIAw zD9>s-%~LUkj&d3$T;-#W2orrWF5(g~XXy^N47Dv~==Ea&Z%Uw^EEL@inq&-$fTgrx zE|HQjOqEvDQ(BZOS~8tbCa63_In%`eo&6ZXz5p&`T*4UDeHpbK8Zxe6g1RN9Z_OEo z(`8VXfOeX3SIM+q_2Df_f6|QmFvZZ8d-PNGvpc!Zzsi1b@9$slY(8AwxVxVH?d!ij zd9d+dDfi@7Zh3uk`G@S6tD>~uk?}6BQN5bnI&V?dr!y7}n&ziRvz2B9hIb5fV&*;Vt)u7Tl=Q!N9o}XBG z&Ei>Dc)Mc++Jj@5`d&=y;+CWN8xVsfUpWj7#Y0;Z*p5meg{pB&jb0y8)AqQRAg0M~ zT*Y6avBJ#rkLwvD$}ei-<59a+Z27P|g%up3QR^1pBhgRP9D^tmJA)RrCfLx&)J3s$gprIwT0VPP`7Q$ zPr@Lj3Q`m18XU2_Ia0t2IBdI+P)~1NGp(! zVQ1(486E35KGpFUCsz>(o?XGYkcSp$=cU~WdRNfDj-jbljC5CoDmp@*karEE9^6HB zI5-)6dkt6J_z_j&@Sc4L2R~TDvLe~fBvc|qDC-E#A@mV~VM29{ z5XA}89Oc8J#`qLXHV6Fm?K~tCM?3AjWV;I~=)`H7 eeAmPEFS(E;FbRzk<|TA({V#(8V=zg~1?xY*u!PqD literal 0 HcmV?d00001 diff --git a/RegisterDemo/target/classes/com/dragon/util/CodeUtil.class b/RegisterDemo/target/classes/com/dragon/util/CodeUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..7656133ba0b27d54d6478b51ea958793c4c40b2f GIT binary patch literal 587 zcmZ`$$w~u35Pg-!8OOKfsR? ztH(r<=%u>qy{cDBzkj^G0XV>pfdrxG20f=8ica7={YZJvY0#Dpb_}El`L1{njwk$% zbJglfHzG_NDPKj$gk*WQNl58@3rS4cNMnMKt0`Yz_IoWEUW=9|358nV3a=?brQ;!( zif+_Bp;Y^?f0YY6(wCu#WW!f?eW^PUX3M*^pQ-9msQgaV!Zcpf%Z)-6JTK#>ju_--Q+9J#s)TZ4|(ee zSDttt;jlbD8t1r z3_9bZ5B^c=chb^2;)4%8Cueuhx4+$P^5f^1ZvbxLei#9PjPBIcs;ku;TWxu!rS3d@ z$t{eKKZ*_8v8PO2r5< zy(5#wj`_u2?GYPACq#4#sRCmY1}%ATgbK5#Xitcu>1LtX*|NN{;j#HKEJF=rg)Q$U z?E2#wTCS#ZGR-#6Tg>87MlX^-b)Qw~C` zJ@qRxHD)=OLB1l4k<-bi5F!ZkClKXn!}kJd@fpfj#9s{#oFm2OQ#u0}<~rbq#CUN4 zDI_uCd!!@>t0SXw7qs^k&XMU|?e~p}eMV7Rk`!9^gJCc0WNm!^G|f zL`uaoOcg%*t|%vNniP^I!!p3kk}XQ%1TATbN~#R40iNcuz*B<6H?T;BEKex}eo;n* zk(0}Gh_?$Ub`aik4>2mc^c~Tq3oP-oz^CE`Rt0EFw{7IwTPD{j!+|Mw_Y!N-uVj)U zQ|$B%TQy6-d4es_vd9-9%eCNR7TT$>?qf13KVY|g#VqdNE=!o}paky7kc@0*aCTPnv)T_u zXrVu#KT3LMNg+^!3%h&onRCy5&7a4gzX80*riUEEVx)Uv+whK7VQOU@o^Tmol2P6F zP+*w9;di_r_6Q9>r?xAUl@YhvFcfW9Ce?SO(HtexAl9K&(tc!Eu8*c|UojMpbX$yp zVLZSK6n*3|5x~PshJ4)-CYw@;)3nzT=7P6k!BA=Hh{splNaqj0f-8$*X=JwQK9#l5 z5%!EH$-Or1p}wS647K`pbExg7X$=pr7}lQUl`zlX_xt@%x3_yVrxF&aA@Y69V#zhQ z%&=9@5{5BXo$%ZmsXC4S5jOV->|Zq39D~ySnXRoi;dX zIRUEKa7{`*2Va#E<|w9-hy<&&hCJO8RxZ0;kd>p?Ly7P)vObXS5PxU)2Ynt8e9t(O z4$Dp$#qFX@en2q}okE5F4AYnys2re>odv%zdtI5UEIi=#H{#^rbc)&8+aZl$ppnO$ VjI}~_f;85!ff`vpMYvq_`~}K$tgHY4 literal 0 HcmV?d00001 diff --git a/RegisterDemo/target/classes/com/dragon/util/MailUtil.class b/RegisterDemo/target/classes/com/dragon/util/MailUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..7a14f20db2c1dfc104e4bb3872d2bb53b700ec18 GIT binary patch literal 3074 zcmbVO`*#z^75>JSwL)1Q*+7Dz1ScW*0k$Btsbkp!194+w6M}4~rs-oXjjaV~SFBdl zZqo*6)1-M3nzRXhBq=RPdwNduI7D`;(%lkKNgsJNMr2 zyN{WF{Od2D0r)chsG&({7l)TuGoEYN(S(-)6UB$FM~pJ~K0O;PmNk%Es^|fji1Yuhbn(#BncLHEeI7DKlI4 zWKqX`xL;uHwDgX-PD#3+C24bWdq;rMXL@-h6~hjJb&9}l-$33edvWYUyM{J_jXt9* z_UPz9r*i0~iCKZ3|Bcr=s)Ml7f!z%bm=!M{LpS;N+=|q&M_{Mot6Z^@%FX3m$z+Bz zPWF`a2FQ>6$K%wv$agm2e`-zV67`{qq%b3gMLbvpj zNg;nkM=w+)N)>MfuF&OML4@Sir7Lmu3v6vPQm%Jb!+z$%?NVXK-Z;`2P^ouNU|aj` zbEacFj>8z#@Mu^cGXYN-SuR_St>bHWj4Mot!*a^36ucq3?3s3!RWTR_ZAE}nKEP1T z0OKKT&&rzQI28bGw2;zpL}0s0m2knbJ?YxgOO9AYIYK|oX;#t@h83I?fY$tb9AtdWz@7adRG>jE0{W6*VqD;)}r zq*S?|z|$JOA<$aqRj6ghahbJB7Rec%ElC~U#J2>RMhW5mdIDFKPAUJsqr=3cfNth; zH+2a-(!kE@+v^Q1yBkA}+OA)HZ{fn+;^$`_s5s$fDq%CV2-+`hKWBq> zd9Y#?a?*|Al)$CFyjLuw`|{GvrTdb>YtqTha%yk-`sbGx-}>O%{Mm&M&jp5Vod5H+ zT6K5uas7k2f1dwz;rZIN+LdebFWq?kv&C1hP|03y-k0t(Tl21*>TkkET~qg z`c7}po}N9)<8s-XBLEPpSt~+d+xF-7J-)P2^px zWkOxCHlSpIx-9Ru^#A8Y3~UZth>1uPKZKPG=t=s-DF@H!_%3DyVxAM2X4hHj!*#UN zlkYJao}BUgy{T;8bQ!DuvC+Y<9u>i7bo>B63?g{QVJX-&-f)uL+L^)=P*uWNoY!zJ z?B-$7jk=~?E;+8J<45>08)Mn0u(2}3-17_JUi?JI^LT-c(NT?YQ$VccB#-f^^=M9- zMM8Oz$6}BSi!Z#k@ZviQ7cSm7d+xqCUdF2$Ug4^rc8&XVTu>RRm6WQ29mhqyuHk1( z!aiA#&SYiD^E=ub>NsBS3uintdx~IGJBv*D#T-$&M6ybC=il9dw<{YR>$N;35Oml7 ze!}onV9)T|D%q^B!LZpXfnw0_RtPLnNrCKA1O3GDHEVrfBWB6JJ81ZQ<5}-t zZ-_DDRNSl_wA4Ll?MQfm>Q)T15e(V3bPpBGGJWRq%@(8D6|V`5B>!8`#9v-$cr|NM z*BSp!)wqMJI=?13-^};=&N-~@-1P}IbngBH#z(&3Mm`Nh_`H)(g9`4#I&8!y&a?*G za5wJZ7Zvzsgg;V-nrL7%XMHo`3%a6=|J_Io8(T-_vF&J=@xbmX9(=0H*kyF7K@G`? zIqdC?BqEV29_lh4R`Wd+=u2>dgBsEk^z*p2vSQ?i(uAZABE@$nJKM)yl6O{CyCeym;(G z8~U&dNAM8Fc!Qb5!>FJK&+tC-98!3TtH0s+GWzi+?8k>VKpO-2D-PmsyaFi7pJo6^ zVMuv1zQE5(DtjcQe&JId$ESFcx|(VC4P3%68MY&^@hkkA0T@G;Hr}TEW$Ji`J2dWW z!Mpe^ + 4.0.0 + com.dragon + RegisterDemo + war + 0.0.1-SNAPSHOT + RegisterDemo Maven Webapp + http://maven.apache.org + + + + javaee + javaee-api + 5 + test + + + taglibs + standard + 1.1.2 + + + + mysql + mysql-connector-java + 5.1.40 + + + + c3p0 + c3p0 + 0.9.1.2 + + + + javax.mail + mail + 1.4.7 + + + javax.activation + activation + 1.1.1 + + + + + RegisterDemo + + diff --git a/RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..e69de29 diff --git a/RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..fae05c4 --- /dev/null +++ b/RegisterDemo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,10 @@ +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\service\impl\UserServiceImpl.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\util\MailUtil.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\dao\impl\UserDaoImpl.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\dao\UserDao.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\model\User.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\util\DBUtil.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\util\CodeUtil.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\service\UserService.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\servlet\ActiveServlet.java +D:\javaEEd1\RegisterDemo\src\main\java\com\dragon\servlet\RegisterServlet.java From 30ed9f1718c05ed01079ddf45bd13f90dbfb782b Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Wed, 8 Mar 2017 14:36:43 +0800 Subject: [PATCH 2/8] =?UTF-8?q?SSM+Redis+MySQL=E5=AE=9E=E7=8E=B0=E7=9A=84?= =?UTF-8?q?=E7=A7=92=E6=9D=80=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 163 ++++++++++++++++++ src/main/java/org/seckill/dao/RedisDao.java | 66 +++++++ src/main/java/org/seckill/dao/SeckillDao.java | 38 ++++ .../org/seckill/dao/SuccessKilledDao.java | 24 +++ src/main/java/org/seckill/dto/Exposer.java | 99 +++++++++++ .../org/seckill/dto/SeckillExecution.java | 73 ++++++++ .../java/org/seckill/dto/SeckillResult.java | 45 +++++ src/main/java/org/seckill/entity/Seckill.java | 77 +++++++++ .../org/seckill/entity/SuccessKilled.java | 66 +++++++ .../org/seckill/enums/SeckillStateEnum.java | 40 +++++ .../exception/RepeatKillException.java | 17 ++ .../exception/SeckillCloseException.java | 16 ++ .../seckill/exception/SeckillException.java | 15 ++ .../org/seckill/service/SeckillService.java | 43 +++++ .../service/impl/SeckillServiceImpl.java | 146 ++++++++++++++++ .../org/seckill/web/SeckillController.java | 95 ++++++++++ src/main/resources/jdbc.properties | 5 + src/main/resources/logback.xml | 15 ++ src/main/resources/mapper/SeckillDao.xml | 35 ++++ .../resources/mapper/SuccessKilledDao.xml | 31 ++++ src/main/resources/mybatis-config.xml | 17 ++ src/main/resources/spring/spring-dao.xml | 60 +++++++ src/main/resources/spring/spring-service.xml | 20 +++ src/main/resources/spring/spring-web.xml | 34 ++++ src/main/sql/schema.sql | 38 ++++ src/main/sql/seckill.sql | 56 ++++++ src/main/webapp/WEB-INF/jsp/common/head.jsp | 13 ++ src/main/webapp/WEB-INF/jsp/common/tag.jsp | 3 + src/main/webapp/WEB-INF/jsp/detail.jsp | 83 +++++++++ src/main/webapp/WEB-INF/jsp/list.jsp | 59 +++++++ src/main/webapp/WEB-INF/web.xml | 28 +++ src/main/webapp/index.jsp | 5 + src/main/webapp/resource/script/seckill.js | 149 ++++++++++++++++ .../java/org/seckill/dao/RedisDaoTest.java | 41 +++++ .../java/org/seckill/dao/SeckillDaoTest.java | 46 +++++ .../org/seckill/dao/SuccessKilledDaoTest.java | 36 ++++ .../service/impl/SeckillServiceImplTest.java | 81 +++++++++ "\350\257\264\346\230\216.txt" | 1 + 38 files changed, 1879 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/seckill/dao/RedisDao.java create mode 100644 src/main/java/org/seckill/dao/SeckillDao.java create mode 100644 src/main/java/org/seckill/dao/SuccessKilledDao.java create mode 100644 src/main/java/org/seckill/dto/Exposer.java create mode 100644 src/main/java/org/seckill/dto/SeckillExecution.java create mode 100644 src/main/java/org/seckill/dto/SeckillResult.java create mode 100644 src/main/java/org/seckill/entity/Seckill.java create mode 100644 src/main/java/org/seckill/entity/SuccessKilled.java create mode 100644 src/main/java/org/seckill/enums/SeckillStateEnum.java create mode 100644 src/main/java/org/seckill/exception/RepeatKillException.java create mode 100644 src/main/java/org/seckill/exception/SeckillCloseException.java create mode 100644 src/main/java/org/seckill/exception/SeckillException.java create mode 100644 src/main/java/org/seckill/service/SeckillService.java create mode 100644 src/main/java/org/seckill/service/impl/SeckillServiceImpl.java create mode 100644 src/main/java/org/seckill/web/SeckillController.java create mode 100644 src/main/resources/jdbc.properties create mode 100644 src/main/resources/logback.xml create mode 100644 src/main/resources/mapper/SeckillDao.xml create mode 100644 src/main/resources/mapper/SuccessKilledDao.xml create mode 100644 src/main/resources/mybatis-config.xml create mode 100644 src/main/resources/spring/spring-dao.xml create mode 100644 src/main/resources/spring/spring-service.xml create mode 100644 src/main/resources/spring/spring-web.xml create mode 100644 src/main/sql/schema.sql create mode 100644 src/main/sql/seckill.sql create mode 100644 src/main/webapp/WEB-INF/jsp/common/head.jsp create mode 100644 src/main/webapp/WEB-INF/jsp/common/tag.jsp create mode 100644 src/main/webapp/WEB-INF/jsp/detail.jsp create mode 100644 src/main/webapp/WEB-INF/jsp/list.jsp create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/index.jsp create mode 100644 src/main/webapp/resource/script/seckill.js create mode 100644 src/test/java/org/seckill/dao/RedisDaoTest.java create mode 100644 src/test/java/org/seckill/dao/SeckillDaoTest.java create mode 100644 src/test/java/org/seckill/dao/SuccessKilledDaoTest.java create mode 100644 src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java create mode 100644 "\350\257\264\346\230\216.txt" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d5c175c --- /dev/null +++ b/pom.xml @@ -0,0 +1,163 @@ + + 4.0.0 + org.seckill + seckill + war + 1.0-SNAPSHOT + seckill Maven Webapp + http://maven.apache.org + + + + junit + junit + 4.12 + test + + + + + + org.slf4j + slf4j-api + 1.7.12 + + + ch.qos.logback + logback-core + 1.1.2 + + + + ch.qos.logback + logback-classic + 1.1.2 + + + + mysql + mysql-connector-java + 5.1.38 + runtime + + + c3p0 + c3p0 + 0.9.1.2 + + + + + org.mybatis + mybatis + 3.2.8 + + + + org.mybatis + mybatis-spring + 1.2.2 + + + + + taglibs + standard + 1.1.2 + + + jstl + jstl + 1.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.8.5 + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + org.springframework + spring-core + 4.1.6.RELEASE + + + org.springframework + spring-beans + 4.1.6.RELEASE + + + org.springframework + spring-context + 4.1.6.RELEASE + + + + org.springframework + spring-jdbc + 4.1.6.RELEASE + + + org.springframework + spring-tx + 4.1.6.RELEASE + + + + org.springframework + spring-web + 4.1.6.RELEASE + + + org.springframework + spring-webmvc + 4.1.6.RELEASE + + + + org.springframework + spring-test + 4.1.6.RELEASE + + + + redis.clients + jedis + 2.9.0 + + + + com.dyuproject.protostuff + protostuff-core + 1.0.8 + + + com.dyuproject.protostuff + protostuff-runtime + 1.0.8 + + + + commons-collections + commons-collections + 3.2.1 + + + + + + + seckill + + diff --git a/src/main/java/org/seckill/dao/RedisDao.java b/src/main/java/org/seckill/dao/RedisDao.java new file mode 100644 index 0000000..2d421f4 --- /dev/null +++ b/src/main/java/org/seckill/dao/RedisDao.java @@ -0,0 +1,66 @@ +package org.seckill.dao; + +import com.dyuproject.protostuff.*; +import com.dyuproject.protostuff.runtime.*; +import org.seckill.entity.*; +import org.slf4j.*; +import redis.clients.jedis.*; + +/** + * Created by pc on 2017/3/8. + */ +public class RedisDao { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final JedisPool jedisPool; + + private RuntimeSchema schema =RuntimeSchema.createFrom(Seckill.class); + public RedisDao(String ip,int port){ + jedisPool = new JedisPool(ip, port); + } + + public Seckill getSeckill(long seckillId){ + try{ + Jedis jedis=jedisPool.getResource(); + try { + String key="seckill:"+seckillId; + //没有实现内部序列化 + //get->byte[]->反序列化->Object(Seckill) + //采用自定义序列化 + //protostuff:pojo + byte[] bytes=jedis.get(key.getBytes()); + if(bytes!=null){ + Seckill seckill=schema.newMessage(); + ProtostuffIOUtil.mergeFrom(bytes,seckill,schema); + //seckill被反序列化 + return seckill; + } + }finally { + jedis.close(); + } + }catch (Exception e){ + logger.error(e.getMessage(),e); + } + return null; + } + + public String putSeckill(Seckill seckill){ + //set Object(Seckill)->序列化->byte[] + try{ + Jedis jedis=jedisPool.getResource(); + try { + String key="seckill:"+seckill.getSeckillId(); + byte[] bytes=ProtostuffIOUtil.toByteArray(seckill, schema, + LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)); + //超时缓存 + int timeout=60*60; + String result=jedis.setex(key.getBytes(),timeout, bytes); + return result; + }finally { + jedis.close(); + } + }catch (Exception e){ + logger.error(e.getMessage(),e); + } + return null; + } +} diff --git a/src/main/java/org/seckill/dao/SeckillDao.java b/src/main/java/org/seckill/dao/SeckillDao.java new file mode 100644 index 0000000..d5e7dbf --- /dev/null +++ b/src/main/java/org/seckill/dao/SeckillDao.java @@ -0,0 +1,38 @@ +package org.seckill.dao; + +import org.apache.ibatis.annotations.*; +import org.seckill.entity.*; + +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +public interface SeckillDao { + /**减库存 + * @param seckillId + * @param killTime + * @return 影响行数 + */ + int reduceNumber(@Param("seckillId") long seckillId,@Param("killTime") Date killTime); + + /**根据id查询秒杀对象 + * @param seckillId + * @return + */ + Seckill queryById(long seckillId); + + /**根据偏移量查询秒杀商品列表 + * @param offset + * @param limit + * @return + */ + List queryAll(@Param("offset") int offset,@Param("limit") int limit); + + /** + * 使用存储过程执行秒杀 + * @param paramMap + */ + void killByProcedure(Map paramMap); + +} diff --git a/src/main/java/org/seckill/dao/SuccessKilledDao.java b/src/main/java/org/seckill/dao/SuccessKilledDao.java new file mode 100644 index 0000000..8805b49 --- /dev/null +++ b/src/main/java/org/seckill/dao/SuccessKilledDao.java @@ -0,0 +1,24 @@ +package org.seckill.dao; + +import org.apache.ibatis.annotations.*; +import org.seckill.entity.*; + +/** + * Created by pc on 2017/3/5. + */ +public interface SuccessKilledDao { + /** + * 插入购买明细,可过滤重复 + * @param seckillId + * @param userPhone + * @return + */ + int insertSuccessKilled(@Param("seckillId") long seckillId,@Param("userPhone") long userPhone); + + /** + * 根据id查询SuccessKilled,并携带秒杀产品对象实体 + * @param seckillId + * @return + */ + SuccessKilled queryByIdWithSeckill(@Param("seckillId") long seckillId,@Param("userPhone") long userPhone); +} diff --git a/src/main/java/org/seckill/dto/Exposer.java b/src/main/java/org/seckill/dto/Exposer.java new file mode 100644 index 0000000..22894d4 --- /dev/null +++ b/src/main/java/org/seckill/dto/Exposer.java @@ -0,0 +1,99 @@ +package org.seckill.dto; + +/** + * 暴露秒杀地址DTO:与业务无关实体 + * Created by pc on 2017/3/6. + */ +public class Exposer { + //是否开启秒杀 + private boolean exposed; + //一种加密措施 + private String md5; + //id + private long seckillId; + //系统当前时间 + 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(long seckillId,boolean exposed, long now, long start, long end) { + this.seckillId=seckillId; + this.exposed = exposed; + this.now = now; + this.start = start; + this.end = end; + } + + public Exposer(long seckillId, boolean exposed) { + this.seckillId = seckillId; + this.exposed = exposed; + } + + public boolean isExposed() { + return exposed; + } + + public void setExposed(boolean exposed) { + this.exposed = exposed; + } + + public String getMd5() { + return md5; + } + + public void setMd5(String md5) { + this.md5 = md5; + } + + public long getSeckillId() { + return seckillId; + } + + public void setSeckillId(long seckillId) { + this.seckillId = seckillId; + } + + 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; + } + + @Override + public String toString() { + return "Exposer{" + + "exposed=" + exposed + + ", md5='" + md5 + '\'' + + ", seckillId=" + seckillId + + ", now=" + now + + ", start=" + start + + ", end=" + end + + '}'; + } +} diff --git a/src/main/java/org/seckill/dto/SeckillExecution.java b/src/main/java/org/seckill/dto/SeckillExecution.java new file mode 100644 index 0000000..1993baf --- /dev/null +++ b/src/main/java/org/seckill/dto/SeckillExecution.java @@ -0,0 +1,73 @@ +package org.seckill.dto; + +import org.seckill.entity.*; +import org.seckill.enums.*; + +/** + * 封装秒杀执行后结果 + * Created by pc on 2017/3/6. + */ +public class SeckillExecution { + private long seckillId; + //秒杀执行结果状态 + private int state; + //状态表示 + private String stateInfo; + //秒杀成功对象 + private SuccessKilled successKilled; + //成功 + public SeckillExecution(long seckillId, SeckillStateEnum stateEnum, SuccessKilled successKilled) { + this.seckillId = seckillId; + this.state = stateEnum.getState(); + this.stateInfo = stateEnum.getStateInfo(); + this.successKilled = successKilled; + } + //失败 + public SeckillExecution(long seckillId, SeckillStateEnum stateEnum) { + this.seckillId = seckillId; + this.state = stateEnum.getState(); + this.stateInfo = stateEnum.getStateInfo(); + } + + 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; + } + + @Override + public String toString() { + return "SeckillExecution{" + + "seckillId=" + seckillId + + ", state=" + state + + ", stateInfo='" + stateInfo + '\'' + + ", successKilled=" + successKilled + + '}'; + } +} diff --git a/src/main/java/org/seckill/dto/SeckillResult.java b/src/main/java/org/seckill/dto/SeckillResult.java new file mode 100644 index 0000000..14ffe4a --- /dev/null +++ b/src/main/java/org/seckill/dto/SeckillResult.java @@ -0,0 +1,45 @@ +package org.seckill.dto; + +/** + * 所有Ajax请求返回类型 + * Created by pc on 2017/3/7. + */ +public class SeckillResult { + private boolean success; + private T data; + private String error; + + public SeckillResult( boolean success,T data) { + this.data = data; + this.success = success; + } + + public SeckillResult(boolean success, String error) { + this.success = success; + this.error = error; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/src/main/java/org/seckill/entity/Seckill.java b/src/main/java/org/seckill/entity/Seckill.java new file mode 100644 index 0000000..26b4290 --- /dev/null +++ b/src/main/java/org/seckill/entity/Seckill.java @@ -0,0 +1,77 @@ +package org.seckill.entity; + +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +public class Seckill { + + private long seckillId; + private String name; + private int number; + private Date createTime; + private Date startTime; + private Date endTime; + + + public long getSeckillId() { + return seckillId; + } + + public void setSeckillId(long seckillId) { + this.seckillId = seckillId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + @Override + public String toString() { + return "Seckill{" + + "seckillId=" + seckillId + + ", name='" + name + '\'' + + ", number=" + number + + ", createTime=" + createTime + + ", startTime=" + startTime + + ", endTime=" + endTime + + '}'; + } +} diff --git a/src/main/java/org/seckill/entity/SuccessKilled.java b/src/main/java/org/seckill/entity/SuccessKilled.java new file mode 100644 index 0000000..a2fc448 --- /dev/null +++ b/src/main/java/org/seckill/entity/SuccessKilled.java @@ -0,0 +1,66 @@ +package org.seckill.entity; + +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +public class SuccessKilled { + private long seckillid; + private long userPhone; + private Date createTime; + private short state; + //多对一 + private Seckill seckill; + + + public short getState() { + return state; + } + + public void setState(short state) { + this.state = state; + } + + public long getSeckillid() { + return seckillid; + } + + public void setSeckillid(long seckillid) { + this.seckillid = seckillid; + } + + public long getUserPhone() { + return userPhone; + } + + public void setUserPhone(long userPhone) { + this.userPhone = userPhone; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Seckill getSeckill() { + return seckill; + } + + public void setSeckill(Seckill seckill) { + this.seckill = seckill; + } + + @Override + public String toString() { + return "SuccessKilled{" + + "seckillid=" + seckillid + + ", userPhone=" + userPhone + + ", createTime=" + createTime + + ", state=" + state + + '}'; + } +} diff --git a/src/main/java/org/seckill/enums/SeckillStateEnum.java b/src/main/java/org/seckill/enums/SeckillStateEnum.java new file mode 100644 index 0000000..22803dc --- /dev/null +++ b/src/main/java/org/seckill/enums/SeckillStateEnum.java @@ -0,0 +1,40 @@ +package org.seckill.enums; + +/** + * 使用枚举表述常量数据字段 + * Created by pc on 2017/3/6. + */ +public enum SeckillStateEnum { + + SUCCESS(1,"秒杀成功"), + END(0,"秒杀结束"), + REPEAT_KILL(-1,"重复秒杀"), + INNER_ERROR(-2,"系统异常"), + DATA_REWRITE(-3,"数据篡改"); + + + private int state; + private String stateInfo; + + SeckillStateEnum(int state, String stateInfo) { + this.state = state; + this.stateInfo = stateInfo; + } + + public int getState() { + return state; + } + + public String getStateInfo() { + return stateInfo; + } + + public static SeckillStateEnum stateOf(int index){ + for(SeckillStateEnum state:values()){ + if(state.getState()==index){ + return state; + } + } + return null; + } +} diff --git a/src/main/java/org/seckill/exception/RepeatKillException.java b/src/main/java/org/seckill/exception/RepeatKillException.java new file mode 100644 index 0000000..93f715a --- /dev/null +++ b/src/main/java/org/seckill/exception/RepeatKillException.java @@ -0,0 +1,17 @@ +package org.seckill.exception; + + +/** + * 重复秒杀异常(运行期异常),Spring声明式事务只支持运行时异常 + * Created by pc on 2017/3/6. + */ +public class RepeatKillException extends SeckillException{ + public RepeatKillException(String message){ + super(message); + } + + public RepeatKillException(String message,Throwable cause){ + super(message,cause); + } + +} diff --git a/src/main/java/org/seckill/exception/SeckillCloseException.java b/src/main/java/org/seckill/exception/SeckillCloseException.java new file mode 100644 index 0000000..d4dd8f3 --- /dev/null +++ b/src/main/java/org/seckill/exception/SeckillCloseException.java @@ -0,0 +1,16 @@ +package org.seckill.exception; + + +/** + * 秒杀关闭异常 + * Created by pc on 2017/3/6. + */ +public class SeckillCloseException extends SeckillException { + public SeckillCloseException(String message) { + super(message); + } + + public SeckillCloseException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/seckill/exception/SeckillException.java b/src/main/java/org/seckill/exception/SeckillException.java new file mode 100644 index 0000000..43aec2c --- /dev/null +++ b/src/main/java/org/seckill/exception/SeckillException.java @@ -0,0 +1,15 @@ +package org.seckill.exception; + +/** + * 秒杀相关异常 + * Created by pc on 2017/3/6. + */ +public class SeckillException extends RuntimeException { + public SeckillException(String message) { + super(message); + } + + public SeckillException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/seckill/service/SeckillService.java b/src/main/java/org/seckill/service/SeckillService.java new file mode 100644 index 0000000..f996676 --- /dev/null +++ b/src/main/java/org/seckill/service/SeckillService.java @@ -0,0 +1,43 @@ +package org.seckill.service; + +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.exception.*; + +import java.util.*; + +/**业务接口:站在使用者(程序员)的角度设计接口 + * 三个方面:1.方法定义粒度,方法定义的要非常清楚2.参数,要越简练越好 + * 3.返回类型(return 类型一定要友好/或者return异常,我们允许的异常) + */ +public interface SeckillService { + + /** + * 查询全部的秒杀记录 + * @return + */ + List getSeckillList(); + + /** + * 查询单个秒杀记录 + * @param seckillId + * @return + */ + Seckill getById(long seckillId); + + /** + * 秒杀开始时输出秒杀接口地址, + * 否则输出系统时间和秒杀时间 + * @param seckillId + */ + Exposer exposeSeckillUrl(long seckillId); + + /** + * 执行秒杀 + * @param seckillId + * @param userPhone + * @param md5 + */ + SeckillExecution executeSeckill(long seckillId,long userPhone,String md5) + throws SeckillException,RepeatKillException,SeckillCloseException; +} diff --git a/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java b/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java new file mode 100644 index 0000000..51f3290 --- /dev/null +++ b/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java @@ -0,0 +1,146 @@ +package org.seckill.service.impl; + +import org.apache.commons.collections.*; +import org.seckill.dao.*; +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.enums.*; +import org.seckill.exception.*; +import org.seckill.service.*; +import org.slf4j.*; +import org.springframework.stereotype.*; +import org.springframework.transaction.annotation.*; +import org.springframework.util.*; + +import javax.annotation.*; +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +@Service +public class SeckillServiceImpl implements SeckillService { + + private Logger logger=LoggerFactory.getLogger(this.getClass()); + @Resource + private SeckillDao seckillDao; + @Resource + private SuccessKilledDao successKilledDao; + @Resource + private RedisDao redisDao; + + //md5盐值,用于混淆md5 + private final String sault="abjkdkddk*234*fdjfkslsdfj&-=fdfdk"; + + public List getSeckillList() { + + return seckillDao.queryAll(0,4); + } + + public Seckill getById(long seckillId) { + + return seckillDao.queryById(seckillId); + } + + public Exposer exposeSeckillUrl(long seckillId) { + //优化点:缓存优化 + //1.访问redis + Seckill seckill = redisDao.getSeckill(seckillId); + if(seckill==null){ + //2.访问数据库 + seckill=seckillDao.queryById(seckillId); + if(seckill==null){ + return new Exposer(seckillId, false); + }else{ + //3.放入redis + redisDao.putSeckill(seckill); + } + + + } + + Date startTime=seckill.getStartTime(); + Date endTime=seckill.getEndTime(); + Date nowTime=new Date(); + + if (nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()) { + return new Exposer(seckillId, false, nowTime.getTime(), startTime.getTime(), endTime.getTime()); + } + //转换特定字符串的过程,不可逆 + String md5=getMD5(seckillId);//TODO + return new Exposer(true,md5,seckillId); + } + + private String getMD5(long seckillId){ + String base=seckillId+"/"+sault; + String md5= DigestUtils.md5DigestAsHex(base.getBytes()); + return md5; + } + + @Transactional + /** + * 使用注解控制事务方法的优点 + * 1.开发团队达成一致约定,明确标注事务方法的编程风格 + * 2.保证事务方法执行时间尽可能短(降低锁定时间),不要穿插其他网络操作RPC/HTTP或者剥离到方法外部 + * 3.不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制 + */ + public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException { + if(md5==null || !md5.equals(getMD5(seckillId))){ + return new SeckillExecution(seckillId,SeckillStateEnum.DATA_REWRITE); + } + //使用存储过程执行秒杀 + Map map=new HashMap(); + map.put("seckillId", seckillId); + map.put("userPhone", userPhone); + map.put("killTime", new Date()); + map.put("result", null); + try { + seckillDao.killByProcedure(map); + //获取result + int result=MapUtils.getInteger(map,"result",-2); + if(result==1){ + SuccessKilled successKilled = successKilledDao. + queryByIdWithSeckill(seckillId,userPhone); + return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled); + }else{ + return new SeckillExecution(seckillId, SeckillStateEnum.stateOf(result)); + } + }catch (Exception e){ + logger.error(e.getMessage(),e); + return new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); + } + +// //执行秒杀逻辑:减库存+记录购买行为 + // +// //减库存 +// try{ +// //记录购买行为 +// int insertCount=successKilledDao.insertSuccessKilled(seckillId,userPhone); +// if(insertCount<=0){ +// //重复秒杀 +// throw new RepeatKillException("seckill repeated"); +// }else{ +// //减库存,热点商品竞争,*先插入购买明细,再减库存,可以缩短行级锁时间 +// int updateCount=seckillDao.reduceNumber(seckillId,new Date()); +// if(updateCount<=0){ +// //没有更新到记录,秒杀结束 +// throw new SeckillCloseException("seckill is closed"); +// }else{ +// SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); +// return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS,successKilled); +// } +// +// } +// }catch (SeckillCloseException e){ +// throw e; +// }catch (RepeatKillException e){ +// throw e; +// }catch(Exception e){ +// logger.error(e.getMessage(),e); +// //所有编译期异常,转换为运行期异常 +// throw new SeckillException("seckill inner error:"+e.getMessage()); +// } + } + + +} diff --git a/src/main/java/org/seckill/web/SeckillController.java b/src/main/java/org/seckill/web/SeckillController.java new file mode 100644 index 0000000..77dd9ac --- /dev/null +++ b/src/main/java/org/seckill/web/SeckillController.java @@ -0,0 +1,95 @@ +package org.seckill.web; + +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.enums.*; +import org.seckill.exception.*; +import org.seckill.service.*; +import org.slf4j.*; +import org.springframework.stereotype.*; +import org.springframework.ui.*; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.*; +import java.util.*; + +/** + * Created by pc on 2017/3/7. + */ +@Controller +@RequestMapping("/seckill") +public class SeckillController { + private Logger logger=LoggerFactory.getLogger(this.getClass()); + @Resource + private SeckillService seckillService; + + @RequestMapping(value = "/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 "forward:/seckill/list"; + } + model.addAttribute("seckill",seckill); + return "detail"; + } +//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){ + logger.error(e.getMessage(),e); + result =new SeckillResult(false,e.getMessage()); + } + return result; + } + + @RequestMapping(value = "{seckillId}/{md5}/execution") + @ResponseBody + public SeckillResult execute(@PathVariable("seckillId") Long seckillId, + @PathVariable("md5") String md5, + @CookieValue(value = "userPhone", required = false) Long userPhone) { + if(userPhone==null){ + return new SeckillResult(false, "未登录"); + } + SeckillResult result=null; + try{ + SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5); + return new SeckillResult(true,execution); + }catch(SeckillCloseException e){ + SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.END); + return new SeckillResult(true,seckillExecution); + }catch(RepeatKillException e){ + SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL); + return new SeckillResult(true,seckillExecution); + } + catch (Exception e){ + logger.error(e.getMessage(),e); + SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); + result = new SeckillResult(true,seckillExecution); + return result; + } + } + + @RequestMapping(value = "/time/now",method = RequestMethod.GET) + @ResponseBody + public SeckillResult time(){ + Date date=new Date(); + return new SeckillResult(true,date.getTime()); + } +} diff --git a/src/main/resources/jdbc.properties b/src/main/resources/jdbc.properties new file mode 100644 index 0000000..309adda --- /dev/null +++ b/src/main/resources/jdbc.properties @@ -0,0 +1,5 @@ +driver=com.mysql.jdbc.Driver +url=jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf-8 +jdbc.user=root +password=123456 + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..9ff0f9c --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/SeckillDao.xml b/src/main/resources/mapper/SeckillDao.xml new file mode 100644 index 0000000..a6642f4 --- /dev/null +++ b/src/main/resources/mapper/SeckillDao.xml @@ -0,0 +1,35 @@ + + + + + + UPDATE seckill SET number=number-1 + WHERE seckill_id=#{seckillId} + AND start_time <= #{killTime} + AND end_time >=#{killTime} + AND number>0 + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/SuccessKilledDao.xml b/src/main/resources/mapper/SuccessKilledDao.xml new file mode 100644 index 0000000..d8b4d43 --- /dev/null +++ b/src/main/resources/mapper/SuccessKilledDao.xml @@ -0,0 +1,31 @@ + + + + + INSERT ignore INTO success_killed(seckill_id,user_phone,state) + VALUES (#{seckillId},#{userPhone},0) + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis-config.xml b/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000..5f924cc --- /dev/null +++ b/src/main/resources/mybatis-config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring/spring-dao.xml b/src/main/resources/spring/spring-dao.xml new file mode 100644 index 0000000..01c0df6 --- /dev/null +++ b/src/main/resources/spring/spring-dao.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring/spring-service.xml b/src/main/resources/spring/spring-service.xml new file mode 100644 index 0000000..0b637ba --- /dev/null +++ b/src/main/resources/spring/spring-service.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring/spring-web.xml b/src/main/resources/spring/spring-web.xml new file mode 100644 index 0000000..f95842a --- /dev/null +++ b/src/main/resources/spring/spring-web.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/sql/schema.sql b/src/main/sql/schema.sql new file mode 100644 index 0000000..7b27cfd --- /dev/null +++ b/src/main/sql/schema.sql @@ -0,0 +1,38 @@ +-- 数据库初始化脚本 +-- 创建数据库 +DROP Database `seckill`; +CREATE DATABASE `seckill`; +-- 使用数据库 +USE `seckill`; +-- 创建表 + +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 DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '开始时间',/*第一个TIMESTAMP字段会自动更新,要加上default*/ + `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) +)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT ='秒杀明细表'; + +-- 初始化数据 +insert into seckill(name, number, start_time, end_time) + VALUES + ('1000元秒杀iphone6',100,'2017-03-05 00:00:00','2017-03-06 00:00:00'), + ('500元秒杀ipad2',200,'2017-03-05 00:00:00','2017-03-06 00:00:00'), + ('300元秒杀小米3',300,'2017-03-05 00:00:00','2017-03-06 00:00:00'), + ('200元秒杀红米note',400,'2017-03-05 00:00:00','2017-03-06 00:00:00'); + +-- 秒杀成功明细表 +-- 用户登录认证相关的信息 +CREATE TABLE `success_killed`( + `seckill_id` BIGINT NOT NULL COMMENT '秒杀商品id', + `user_phone` BIGINT NOT NULL COMMENT '用户手机号', + `state` TINYINT NOT NULL DEFAULT 0 COMMENT '状态标识:-1:无效 0:成功 1:已付款', + `create_time` TIMESTAMP NOT NULL COMMENT '创建时间', + PRIMARY KEY (seckill_id,user_phone), -- 联合主键 + KEY idx_create_time(create_time) +)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; diff --git a/src/main/sql/seckill.sql b/src/main/sql/seckill.sql new file mode 100644 index 0000000..4a08662 --- /dev/null +++ b/src/main/sql/seckill.sql @@ -0,0 +1,56 @@ +-- 执行秒杀存储过程 +DELIMITER $$ +-- 定义存储过程 +-- 参数:in输入参数 out输出参数 +-- row_count()返回上一条修改语句sql(update,insert,delete)影响的行数 +-- row_count:0未修改行数,>0:表示影响的行数 <0:sql错误/未执行修改sql +-- 存储过程内部声明的变量不能是@开头 +CREATE PROCEDURE `seckill`.`execute_seckill`( + in v_seckill_id bigint,in v_seckill_phone BIGINT,in v_seckill_time TIMESTAMP, + out r_result int) + BEGIN + DECLARE insert_count int DEFAULT 0; + START TRANSACTION; + INSERT IGNORE INTO `success_killed`(seckill_id,user_phone,create_time) + values(v_seckill_id,v_seckill_phone,v_seckill_time); + SELECT row_count() INTO insert_count; + IF(insert_count = 0) THEN + ROLLBACK; + SET r_result=-1;-- 重复秒杀 + ELSEIF(insert_count<0) THEN + ROLLBACK; + SET r_result=-2; + ELSE + UPDATE seckill SET number=number-1 + WHERE seckill_id=v_seckill_id + AND seckill.start_time < v_seckill_time + AND seckill.end_time >v_seckill_time + AND seckill.number>0; + SELECT row_count() into insert_count; + IF (insert_count=0) THEN + ROLLBACK; + SET r_result=0; + ELSEIF (insert_count<0) THEN + ROLLBACK; + SET r_result=-2; + ELSE + COMMIT; + SET r_result=1; + END IF ; + END IF; + END; +$$ +-- 存储过程定义结束 +DELIMITER ; +SET @r_result=-3; +-- 执行存储过程 +CALL seckill.execute_seckill(1003, 18826077111, now(), @r_result); +-- 获取结果 +SELECT @r_result; + + +-- 存储过程 +-- 存储过程优化:事务行级锁持有的时间 +-- 不要过度依赖存储过程 +-- 简单的逻辑可以应用存储过程 +-- QPS:一个秒杀单6000/QPS \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/common/head.jsp b/src/main/webapp/WEB-INF/jsp/common/head.jsp new file mode 100644 index 0000000..da039a1 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/common/head.jsp @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/common/tag.jsp b/src/main/webapp/WEB-INF/jsp/common/tag.jsp new file mode 100644 index 0000000..9b0e158 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/common/tag.jsp @@ -0,0 +1,3 @@ +<%@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" %> diff --git a/src/main/webapp/WEB-INF/jsp/detail.jsp b/src/main/webapp/WEB-INF/jsp/detail.jsp new file mode 100644 index 0000000..9f503e9 --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/detail.jsp @@ -0,0 +1,83 @@ +<%@page contentType="text/html; charset=UTF-8" language="java" %> +<%@include file="common/tag.jsp" %> + + + + 秒杀详情页 + <%@include file="common/head.jsp" %> + + +
+
+
+

${seckill.name}

+
+ +
+

+ <%--显示time图标--%> + + <%--展示倒计时--%> + +

+
+
+
+<%--登录弹出层 输入电话--%> + + + +<%--jQery文件,务必在bootstrap.min.js之前引入--%> + + +<%--使用CDN 获取公共js http://www.bootcdn.cn/--%> +<%--jQuery Cookie操作插件--%> + +<%--jQuery countDown倒计时插件--%> + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/list.jsp b/src/main/webapp/WEB-INF/jsp/list.jsp new file mode 100644 index 0000000..70807aa --- /dev/null +++ b/src/main/webapp/WEB-INF/jsp/list.jsp @@ -0,0 +1,59 @@ +<%@page contentType="text/html; charset=UTF-8" language="java" %> +<%@include file="common/tag.jsp"%> + + + + 秒杀商品列表 + <%@include file="common/head.jsp" %> + + +
+
+
+

秒杀列表

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
名称库存开始时间结束时间创建时间详情页
${sk.name}${sk.number} + + + + + + 详情
+ +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..d76a3cd --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + + seckill-dispatcher + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + classpath:spring/spring-*.xml + + + + seckill-dispatcher + + / + + \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..6f07b72 --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

Hello World!

+ + diff --git a/src/main/webapp/resource/script/seckill.js b/src/main/webapp/resource/script/seckill.js new file mode 100644 index 0000000..9a2a961 --- /dev/null +++ b/src/main/webapp/resource/script/seckill.js @@ -0,0 +1,149 @@ +//存放主要交互逻辑的js代码 +// javascript 模块化(package.类.方法) + +var seckill = { + + //封装秒杀相关ajax的url + URL: { + now: function () { + return '/seckill/time/now'; + }, + exposer: function (seckillId) { + return '/seckill/' + seckillId + '/exposer'; + }, + execution: function (seckillId, md5) { + return '/seckill/' + seckillId + '/' + md5 + '/execution'; + } + }, + + //验证手机号 + validatePhone: function (phone) { + if (phone && phone.length == 11 && !isNaN(phone)) { + return true;//直接判断对象会看对象是否为空,空就是undefine就是false; isNaN 非数字返回true + } else { + return false; + } + }, + + //详情页秒杀逻辑 + detail: { + //详情页初始化 + init: function (params) { + //手机验证和登录,计时交互 + //规划我们的交互流程 + //在cookie中查找手机号 + var userPhone = $.cookie('userPhone'); + //验证手机号 + if (!seckill.validatePhone(userPhone)) { + //绑定手机 控制输出 + var killPhoneModal = $('#killPhoneModal'); + killPhoneModal.modal({ + show: true,//显示弹出层 + backdrop: 'static',//禁止位置关闭 + keyboard: false//关闭键盘事件 + }); + + $('#killPhoneBtn').click(function () { + var inputPhone = $('#killPhoneKey').val(); + console.log("inputPhone: " + inputPhone); + if (seckill.validatePhone(inputPhone)) { + //电话写入cookie(7天过期) + $.cookie('userPhone', inputPhone, {expires: 7, path: '/seckill'}); + //验证通过  刷新页面 + window.location.reload(); + } else { + //todo 错误文案信息抽取到前端字典里 + $('#killPhoneMessage').hide().html('').show(300); + } + }); + } + + //已经登录 + //计时交互 + var startTime = params['startTime']; + var endTime = params['endTime']; + var seckillId = params['seckillId']; + $.get(seckill.URL.now(), {}, function (result) { + if (result && result['success']) { + var nowTime = result['data']; + //时间判断 计时交互 + seckill.countDown(seckillId, nowTime, startTime, endTime); + } else { + console.log('result: ' + result); + alert('result: ' + result); + } + }); + } + }, + + handlerSeckill: function (seckillId, node) { + //获取秒杀地址,控制显示器,执行秒杀 + node.hide().html(''); + + $.get(seckill.URL.exposer(seckillId), {}, function (result) { + //在回调函数种执行交互流程 + if (result && result['success']) { + var exposer = result['data']; + if (exposer['exposed']) { + //开启秒杀 + //获取秒杀地址 + var md5 = exposer['md5']; + var killUrl = seckill.URL.execution(seckillId, md5); + console.log("killUrl: " + killUrl); + //绑定一次点击事件 + $('#killBtn').one('click', function () { + //执行秒杀请求 + //1.先禁用按钮 + $(this).addClass('disabled');//,<-$(this)===('#killBtn')-> + //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); + } + }); + + }, + + 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);//todo 防止时间偏移 + seckillBox.countdown(killTime, function (event) { + //时间格式 + var format = event.strftime('秒杀倒计时: %D天 %H时 %M分 %S秒 '); + seckillBox.html(format); + }).on('finish.countdown', function () { + //时间完成后回调事件 + //获取秒杀地址,控制现实逻辑,执行秒杀 + console.log('______fininsh.countdown'); + seckill.handlerSeckill(seckillId, seckillBox); + }); + } else { + //秒杀开始 + seckill.handlerSeckill(seckillId, seckillBox); + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/seckill/dao/RedisDaoTest.java b/src/test/java/org/seckill/dao/RedisDaoTest.java new file mode 100644 index 0000000..2b5cc7b --- /dev/null +++ b/src/test/java/org/seckill/dao/RedisDaoTest.java @@ -0,0 +1,41 @@ +package org.seckill.dao; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.entity.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; + +import static org.junit.Assert.*; + +/** + * Created by pc on 2017/3/8. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:spring/spring-dao.xml") +public class RedisDaoTest { + private long id=1001L; + + @Resource + private RedisDao redisDao; + @Resource + private SeckillDao seckillDao; + + + @Test + public void testSeckill() throws Exception { + Seckill seckill=redisDao.getSeckill(id); + if(seckill==null){ + seckill = seckillDao.queryById(id); + if(seckill!=null){ + String result = redisDao.putSeckill(seckill); + System.out.println(result); + seckill = redisDao.getSeckill(id); + System.out.println(seckill); + } + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/seckill/dao/SeckillDaoTest.java b/src/test/java/org/seckill/dao/SeckillDaoTest.java new file mode 100644 index 0000000..9f838a3 --- /dev/null +++ b/src/test/java/org/seckill/dao/SeckillDaoTest.java @@ -0,0 +1,46 @@ +package org.seckill.dao; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.entity.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +//配置spring和junit整合,这样junit在启动时就会加载spring容器 +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:spring/spring-dao.xml") +public class SeckillDaoTest { + //注入Dao实现类依赖 + @Resource + private SeckillDao seckillDao; + + @Test + public void queryById(){ + long seckillId=1001; + Seckill seckill=seckillDao.queryById(seckillId); + System.out.println(seckill.getName()); + System.out.println(seckill); + } + + @Test + public void queryAll(){ + List seckillList=seckillDao.queryAll(0,100); + for(Seckill seckill:seckillList){ + System.out.println(seckill); + } + } + + @Test + public void reduceNumber(){ + long id=1000; + int updateCount=seckillDao.reduceNumber(1000, new Date()); + System.out.println(updateCount); + } +} + diff --git a/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java b/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java new file mode 100644 index 0000000..c1548eb --- /dev/null +++ b/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java @@ -0,0 +1,36 @@ +package org.seckill.dao; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.entity.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; + +/** + * Created by pc on 2017/3/5. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:spring/spring-dao.xml") +public class SuccessKilledDaoTest { + @Resource + private SuccessKilledDao successKilledDao; + + @Test + public void insertSuccessKilled(){ + long seckillId=1000L; + long userPhone=18826077187L; + int updateCount=successKilledDao.insertSuccessKilled(seckillId,userPhone); + System.out.println(updateCount); + } + + @Test + public void queryByIdWithSeckill(){ + + SuccessKilled successKilled=successKilledDao.queryByIdWithSeckill(1000L,18826077187L); + System.out.println(successKilled); + System.out.println(successKilled.getSeckill()); + + } +} diff --git a/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java b/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java new file mode 100644 index 0000000..eba9587 --- /dev/null +++ b/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java @@ -0,0 +1,81 @@ +package org.seckill.service.impl; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.exception.*; +import org.seckill.service.*; +import org.slf4j.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; + +import java.util.*; + +import static org.junit.Assert.*; + +/** + * Created by pc on 2017/3/6. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration({ + "classpath:spring/spring-dao.xml", + "classpath:spring/spring-service.xml" +}) +public class SeckillServiceImplTest { + Logger logger=LoggerFactory.getLogger(this.getClass()); + @Resource + private SeckillService seckillService; + + @Test + public void testGetSeckillList() throws Exception { + List seckillList=seckillService.getSeckillList(); + logger.info("list={}",seckillList); + } + + @Test + public void testGetById() throws Exception { + Seckill seckill=seckillService.getById(1000L); + logger.info("seckill={}",seckill); + + } + //集成测试代码完整逻辑,注意可重复执行 + @Test + public void testSeckillLogic() throws Exception { + //exposer=Exposer{exposed=true, md5='ae8c840b6c3a4891c593ec70b9dc2c06', + // seckillId=1000, now=0, start=0, end=0} + long id=1001L; + Exposer exposer=seckillService.exposeSeckillUrl(id); + logger.info("exposer={}",exposer); + if(exposer!=null){ + //用try-catch包围才能单元测试通过,否则抛出异常视为不通过 + try{ + long userPhone=18826077189L; + SeckillExecution seckillExecution=seckillService.executeSeckill(id, userPhone, exposer.getMd5()); + logger.info("seckillExecution={}",seckillExecution); + }catch(RepeatKillException e){ + logger.error(e.getMessage()); + }catch (SeckillCloseException e){ + logger.error(e.getMessage()); + }catch (SeckillException e){ + logger.error(e.getMessage()); + } + }else{ + logger.warn("exposer={}",exposer); + } + } + + @Test + public void testProcedure(){ + long seckillId=1001L; + long userPhone=18826077188L; + Exposer exposer = seckillService.exposeSeckillUrl(seckillId); + if(exposer.isExposed()){ + String md5=exposer.getMd5(); + SeckillExecution execution=seckillService.executeSeckill(seckillId, userPhone, md5); + logger.info(execution.getStateInfo()); + } + } +} \ No newline at end of file diff --git "a/\350\257\264\346\230\216.txt" "b/\350\257\264\346\230\216.txt" new file mode 100644 index 0000000..3ce0bc5 --- /dev/null +++ "b/\350\257\264\346\230\216.txt" @@ -0,0 +1 @@ +ʹSSM+Redis+MySQLʵֵɱϵͳ From 52559e2bf3bc9a42fca399ad04094e9804c2ff40 Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Wed, 8 Mar 2017 14:45:12 +0800 Subject: [PATCH 3/8] =?UTF-8?q?Delete=20=E8=AF=B4=E6=98=8E.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\350\257\264\346\230\216.txt" | 1 - 1 file changed, 1 deletion(-) delete mode 100644 "\350\257\264\346\230\216.txt" diff --git "a/\350\257\264\346\230\216.txt" "b/\350\257\264\346\230\216.txt" deleted file mode 100644 index 3ce0bc5..0000000 --- "a/\350\257\264\346\230\216.txt" +++ /dev/null @@ -1 +0,0 @@ -ʹSSM+Redis+MySQLʵֵɱϵͳ From 833ab9af71b8d212f6b37b591ab1f3897c4cb56c Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Wed, 8 Mar 2017 15:08:35 +0800 Subject: [PATCH 4/8] rollback --- pom.xml | 163 ------------------ src/main/java/org/seckill/dao/RedisDao.java | 66 ------- src/main/java/org/seckill/dao/SeckillDao.java | 38 ---- .../org/seckill/dao/SuccessKilledDao.java | 24 --- src/main/java/org/seckill/dto/Exposer.java | 99 ----------- .../org/seckill/dto/SeckillExecution.java | 73 -------- .../java/org/seckill/dto/SeckillResult.java | 45 ----- src/main/java/org/seckill/entity/Seckill.java | 77 --------- .../org/seckill/entity/SuccessKilled.java | 66 ------- .../org/seckill/enums/SeckillStateEnum.java | 40 ----- .../exception/RepeatKillException.java | 17 -- .../exception/SeckillCloseException.java | 16 -- .../seckill/exception/SeckillException.java | 15 -- .../org/seckill/service/SeckillService.java | 43 ----- .../service/impl/SeckillServiceImpl.java | 146 ---------------- .../org/seckill/web/SeckillController.java | 95 ---------- src/main/resources/jdbc.properties | 5 - src/main/resources/logback.xml | 15 -- src/main/resources/mapper/SeckillDao.xml | 35 ---- .../resources/mapper/SuccessKilledDao.xml | 31 ---- src/main/resources/mybatis-config.xml | 17 -- src/main/resources/spring/spring-dao.xml | 60 ------- src/main/resources/spring/spring-service.xml | 20 --- src/main/resources/spring/spring-web.xml | 34 ---- src/main/sql/schema.sql | 38 ---- src/main/sql/seckill.sql | 56 ------ src/main/webapp/WEB-INF/jsp/common/head.jsp | 13 -- src/main/webapp/WEB-INF/jsp/common/tag.jsp | 3 - src/main/webapp/WEB-INF/jsp/detail.jsp | 83 --------- src/main/webapp/WEB-INF/jsp/list.jsp | 59 ------- src/main/webapp/WEB-INF/web.xml | 28 --- src/main/webapp/index.jsp | 5 - src/main/webapp/resource/script/seckill.js | 149 ---------------- .../java/org/seckill/dao/RedisDaoTest.java | 41 ----- .../java/org/seckill/dao/SeckillDaoTest.java | 46 ----- .../org/seckill/dao/SuccessKilledDaoTest.java | 36 ---- .../service/impl/SeckillServiceImplTest.java | 81 --------- 37 files changed, 1878 deletions(-) delete mode 100644 pom.xml delete mode 100644 src/main/java/org/seckill/dao/RedisDao.java delete mode 100644 src/main/java/org/seckill/dao/SeckillDao.java delete mode 100644 src/main/java/org/seckill/dao/SuccessKilledDao.java delete mode 100644 src/main/java/org/seckill/dto/Exposer.java delete mode 100644 src/main/java/org/seckill/dto/SeckillExecution.java delete mode 100644 src/main/java/org/seckill/dto/SeckillResult.java delete mode 100644 src/main/java/org/seckill/entity/Seckill.java delete mode 100644 src/main/java/org/seckill/entity/SuccessKilled.java delete mode 100644 src/main/java/org/seckill/enums/SeckillStateEnum.java delete mode 100644 src/main/java/org/seckill/exception/RepeatKillException.java delete mode 100644 src/main/java/org/seckill/exception/SeckillCloseException.java delete mode 100644 src/main/java/org/seckill/exception/SeckillException.java delete mode 100644 src/main/java/org/seckill/service/SeckillService.java delete mode 100644 src/main/java/org/seckill/service/impl/SeckillServiceImpl.java delete mode 100644 src/main/java/org/seckill/web/SeckillController.java delete mode 100644 src/main/resources/jdbc.properties delete mode 100644 src/main/resources/logback.xml delete mode 100644 src/main/resources/mapper/SeckillDao.xml delete mode 100644 src/main/resources/mapper/SuccessKilledDao.xml delete mode 100644 src/main/resources/mybatis-config.xml delete mode 100644 src/main/resources/spring/spring-dao.xml delete mode 100644 src/main/resources/spring/spring-service.xml delete mode 100644 src/main/resources/spring/spring-web.xml delete mode 100644 src/main/sql/schema.sql delete mode 100644 src/main/sql/seckill.sql delete mode 100644 src/main/webapp/WEB-INF/jsp/common/head.jsp delete mode 100644 src/main/webapp/WEB-INF/jsp/common/tag.jsp delete mode 100644 src/main/webapp/WEB-INF/jsp/detail.jsp delete mode 100644 src/main/webapp/WEB-INF/jsp/list.jsp delete mode 100644 src/main/webapp/WEB-INF/web.xml delete mode 100644 src/main/webapp/index.jsp delete mode 100644 src/main/webapp/resource/script/seckill.js delete mode 100644 src/test/java/org/seckill/dao/RedisDaoTest.java delete mode 100644 src/test/java/org/seckill/dao/SeckillDaoTest.java delete mode 100644 src/test/java/org/seckill/dao/SuccessKilledDaoTest.java delete mode 100644 src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java diff --git a/pom.xml b/pom.xml deleted file mode 100644 index d5c175c..0000000 --- a/pom.xml +++ /dev/null @@ -1,163 +0,0 @@ - - 4.0.0 - org.seckill - seckill - war - 1.0-SNAPSHOT - seckill Maven Webapp - http://maven.apache.org - - - - junit - junit - 4.12 - test - - - - - - org.slf4j - slf4j-api - 1.7.12 - - - ch.qos.logback - logback-core - 1.1.2 - - - - ch.qos.logback - logback-classic - 1.1.2 - - - - mysql - mysql-connector-java - 5.1.38 - runtime - - - c3p0 - c3p0 - 0.9.1.2 - - - - - org.mybatis - mybatis - 3.2.8 - - - - org.mybatis - mybatis-spring - 1.2.2 - - - - - taglibs - standard - 1.1.2 - - - jstl - jstl - 1.2 - - - com.fasterxml.jackson.core - jackson-databind - 2.8.5 - - - javax.servlet - javax.servlet-api - 3.1.0 - - - - - org.springframework - spring-core - 4.1.6.RELEASE - - - org.springframework - spring-beans - 4.1.6.RELEASE - - - org.springframework - spring-context - 4.1.6.RELEASE - - - - org.springframework - spring-jdbc - 4.1.6.RELEASE - - - org.springframework - spring-tx - 4.1.6.RELEASE - - - - org.springframework - spring-web - 4.1.6.RELEASE - - - org.springframework - spring-webmvc - 4.1.6.RELEASE - - - - org.springframework - spring-test - 4.1.6.RELEASE - - - - redis.clients - jedis - 2.9.0 - - - - com.dyuproject.protostuff - protostuff-core - 1.0.8 - - - com.dyuproject.protostuff - protostuff-runtime - 1.0.8 - - - - commons-collections - commons-collections - 3.2.1 - - - - - - - seckill - - diff --git a/src/main/java/org/seckill/dao/RedisDao.java b/src/main/java/org/seckill/dao/RedisDao.java deleted file mode 100644 index 2d421f4..0000000 --- a/src/main/java/org/seckill/dao/RedisDao.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.seckill.dao; - -import com.dyuproject.protostuff.*; -import com.dyuproject.protostuff.runtime.*; -import org.seckill.entity.*; -import org.slf4j.*; -import redis.clients.jedis.*; - -/** - * Created by pc on 2017/3/8. - */ -public class RedisDao { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final JedisPool jedisPool; - - private RuntimeSchema schema =RuntimeSchema.createFrom(Seckill.class); - public RedisDao(String ip,int port){ - jedisPool = new JedisPool(ip, port); - } - - public Seckill getSeckill(long seckillId){ - try{ - Jedis jedis=jedisPool.getResource(); - try { - String key="seckill:"+seckillId; - //没有实现内部序列化 - //get->byte[]->反序列化->Object(Seckill) - //采用自定义序列化 - //protostuff:pojo - byte[] bytes=jedis.get(key.getBytes()); - if(bytes!=null){ - Seckill seckill=schema.newMessage(); - ProtostuffIOUtil.mergeFrom(bytes,seckill,schema); - //seckill被反序列化 - return seckill; - } - }finally { - jedis.close(); - } - }catch (Exception e){ - logger.error(e.getMessage(),e); - } - return null; - } - - public String putSeckill(Seckill seckill){ - //set Object(Seckill)->序列化->byte[] - try{ - Jedis jedis=jedisPool.getResource(); - try { - String key="seckill:"+seckill.getSeckillId(); - byte[] bytes=ProtostuffIOUtil.toByteArray(seckill, schema, - LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)); - //超时缓存 - int timeout=60*60; - String result=jedis.setex(key.getBytes(),timeout, bytes); - return result; - }finally { - jedis.close(); - } - }catch (Exception e){ - logger.error(e.getMessage(),e); - } - return null; - } -} diff --git a/src/main/java/org/seckill/dao/SeckillDao.java b/src/main/java/org/seckill/dao/SeckillDao.java deleted file mode 100644 index d5e7dbf..0000000 --- a/src/main/java/org/seckill/dao/SeckillDao.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.seckill.dao; - -import org.apache.ibatis.annotations.*; -import org.seckill.entity.*; - -import java.util.*; - -/** - * Created by pc on 2017/3/5. - */ -public interface SeckillDao { - /**减库存 - * @param seckillId - * @param killTime - * @return 影响行数 - */ - int reduceNumber(@Param("seckillId") long seckillId,@Param("killTime") Date killTime); - - /**根据id查询秒杀对象 - * @param seckillId - * @return - */ - Seckill queryById(long seckillId); - - /**根据偏移量查询秒杀商品列表 - * @param offset - * @param limit - * @return - */ - List queryAll(@Param("offset") int offset,@Param("limit") int limit); - - /** - * 使用存储过程执行秒杀 - * @param paramMap - */ - void killByProcedure(Map paramMap); - -} diff --git a/src/main/java/org/seckill/dao/SuccessKilledDao.java b/src/main/java/org/seckill/dao/SuccessKilledDao.java deleted file mode 100644 index 8805b49..0000000 --- a/src/main/java/org/seckill/dao/SuccessKilledDao.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.seckill.dao; - -import org.apache.ibatis.annotations.*; -import org.seckill.entity.*; - -/** - * Created by pc on 2017/3/5. - */ -public interface SuccessKilledDao { - /** - * 插入购买明细,可过滤重复 - * @param seckillId - * @param userPhone - * @return - */ - int insertSuccessKilled(@Param("seckillId") long seckillId,@Param("userPhone") long userPhone); - - /** - * 根据id查询SuccessKilled,并携带秒杀产品对象实体 - * @param seckillId - * @return - */ - SuccessKilled queryByIdWithSeckill(@Param("seckillId") long seckillId,@Param("userPhone") long userPhone); -} diff --git a/src/main/java/org/seckill/dto/Exposer.java b/src/main/java/org/seckill/dto/Exposer.java deleted file mode 100644 index 22894d4..0000000 --- a/src/main/java/org/seckill/dto/Exposer.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.seckill.dto; - -/** - * 暴露秒杀地址DTO:与业务无关实体 - * Created by pc on 2017/3/6. - */ -public class Exposer { - //是否开启秒杀 - private boolean exposed; - //一种加密措施 - private String md5; - //id - private long seckillId; - //系统当前时间 - 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(long seckillId,boolean exposed, long now, long start, long end) { - this.seckillId=seckillId; - this.exposed = exposed; - this.now = now; - this.start = start; - this.end = end; - } - - public Exposer(long seckillId, boolean exposed) { - this.seckillId = seckillId; - this.exposed = exposed; - } - - public boolean isExposed() { - return exposed; - } - - public void setExposed(boolean exposed) { - this.exposed = exposed; - } - - public String getMd5() { - return md5; - } - - public void setMd5(String md5) { - this.md5 = md5; - } - - public long getSeckillId() { - return seckillId; - } - - public void setSeckillId(long seckillId) { - this.seckillId = seckillId; - } - - 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; - } - - @Override - public String toString() { - return "Exposer{" + - "exposed=" + exposed + - ", md5='" + md5 + '\'' + - ", seckillId=" + seckillId + - ", now=" + now + - ", start=" + start + - ", end=" + end + - '}'; - } -} diff --git a/src/main/java/org/seckill/dto/SeckillExecution.java b/src/main/java/org/seckill/dto/SeckillExecution.java deleted file mode 100644 index 1993baf..0000000 --- a/src/main/java/org/seckill/dto/SeckillExecution.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.seckill.dto; - -import org.seckill.entity.*; -import org.seckill.enums.*; - -/** - * 封装秒杀执行后结果 - * Created by pc on 2017/3/6. - */ -public class SeckillExecution { - private long seckillId; - //秒杀执行结果状态 - private int state; - //状态表示 - private String stateInfo; - //秒杀成功对象 - private SuccessKilled successKilled; - //成功 - public SeckillExecution(long seckillId, SeckillStateEnum stateEnum, SuccessKilled successKilled) { - this.seckillId = seckillId; - this.state = stateEnum.getState(); - this.stateInfo = stateEnum.getStateInfo(); - this.successKilled = successKilled; - } - //失败 - public SeckillExecution(long seckillId, SeckillStateEnum stateEnum) { - this.seckillId = seckillId; - this.state = stateEnum.getState(); - this.stateInfo = stateEnum.getStateInfo(); - } - - 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; - } - - @Override - public String toString() { - return "SeckillExecution{" + - "seckillId=" + seckillId + - ", state=" + state + - ", stateInfo='" + stateInfo + '\'' + - ", successKilled=" + successKilled + - '}'; - } -} diff --git a/src/main/java/org/seckill/dto/SeckillResult.java b/src/main/java/org/seckill/dto/SeckillResult.java deleted file mode 100644 index 14ffe4a..0000000 --- a/src/main/java/org/seckill/dto/SeckillResult.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.seckill.dto; - -/** - * 所有Ajax请求返回类型 - * Created by pc on 2017/3/7. - */ -public class SeckillResult { - private boolean success; - private T data; - private String error; - - public SeckillResult( boolean success,T data) { - this.data = data; - this.success = success; - } - - public SeckillResult(boolean success, String error) { - this.success = success; - this.error = error; - } - - public boolean isSuccess() { - return success; - } - - public void setSuccess(boolean success) { - this.success = success; - } - - public T getData() { - return data; - } - - public void setData(T data) { - this.data = data; - } - - public String getError() { - return error; - } - - public void setError(String error) { - this.error = error; - } -} diff --git a/src/main/java/org/seckill/entity/Seckill.java b/src/main/java/org/seckill/entity/Seckill.java deleted file mode 100644 index 26b4290..0000000 --- a/src/main/java/org/seckill/entity/Seckill.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.seckill.entity; - -import java.util.*; - -/** - * Created by pc on 2017/3/5. - */ -public class Seckill { - - private long seckillId; - private String name; - private int number; - private Date createTime; - private Date startTime; - private Date endTime; - - - public long getSeckillId() { - return seckillId; - } - - public void setSeckillId(long seckillId) { - this.seckillId = seckillId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getNumber() { - return number; - } - - public void setNumber(int number) { - this.number = number; - } - - public Date getCreateTime() { - return createTime; - } - - public void setCreateTime(Date createTime) { - this.createTime = createTime; - } - - public Date getStartTime() { - return startTime; - } - - public void setStartTime(Date startTime) { - this.startTime = startTime; - } - - public Date getEndTime() { - return endTime; - } - - public void setEndTime(Date endTime) { - this.endTime = endTime; - } - - @Override - public String toString() { - return "Seckill{" + - "seckillId=" + seckillId + - ", name='" + name + '\'' + - ", number=" + number + - ", createTime=" + createTime + - ", startTime=" + startTime + - ", endTime=" + endTime + - '}'; - } -} diff --git a/src/main/java/org/seckill/entity/SuccessKilled.java b/src/main/java/org/seckill/entity/SuccessKilled.java deleted file mode 100644 index a2fc448..0000000 --- a/src/main/java/org/seckill/entity/SuccessKilled.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.seckill.entity; - -import java.util.*; - -/** - * Created by pc on 2017/3/5. - */ -public class SuccessKilled { - private long seckillid; - private long userPhone; - private Date createTime; - private short state; - //多对一 - private Seckill seckill; - - - public short getState() { - return state; - } - - public void setState(short state) { - this.state = state; - } - - public long getSeckillid() { - return seckillid; - } - - public void setSeckillid(long seckillid) { - this.seckillid = seckillid; - } - - public long getUserPhone() { - return userPhone; - } - - public void setUserPhone(long userPhone) { - this.userPhone = userPhone; - } - - public Date getCreateTime() { - return createTime; - } - - public void setCreateTime(Date createTime) { - this.createTime = createTime; - } - - public Seckill getSeckill() { - return seckill; - } - - public void setSeckill(Seckill seckill) { - this.seckill = seckill; - } - - @Override - public String toString() { - return "SuccessKilled{" + - "seckillid=" + seckillid + - ", userPhone=" + userPhone + - ", createTime=" + createTime + - ", state=" + state + - '}'; - } -} diff --git a/src/main/java/org/seckill/enums/SeckillStateEnum.java b/src/main/java/org/seckill/enums/SeckillStateEnum.java deleted file mode 100644 index 22803dc..0000000 --- a/src/main/java/org/seckill/enums/SeckillStateEnum.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.seckill.enums; - -/** - * 使用枚举表述常量数据字段 - * Created by pc on 2017/3/6. - */ -public enum SeckillStateEnum { - - SUCCESS(1,"秒杀成功"), - END(0,"秒杀结束"), - REPEAT_KILL(-1,"重复秒杀"), - INNER_ERROR(-2,"系统异常"), - DATA_REWRITE(-3,"数据篡改"); - - - private int state; - private String stateInfo; - - SeckillStateEnum(int state, String stateInfo) { - this.state = state; - this.stateInfo = stateInfo; - } - - public int getState() { - return state; - } - - public String getStateInfo() { - return stateInfo; - } - - public static SeckillStateEnum stateOf(int index){ - for(SeckillStateEnum state:values()){ - if(state.getState()==index){ - return state; - } - } - return null; - } -} diff --git a/src/main/java/org/seckill/exception/RepeatKillException.java b/src/main/java/org/seckill/exception/RepeatKillException.java deleted file mode 100644 index 93f715a..0000000 --- a/src/main/java/org/seckill/exception/RepeatKillException.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.seckill.exception; - - -/** - * 重复秒杀异常(运行期异常),Spring声明式事务只支持运行时异常 - * Created by pc on 2017/3/6. - */ -public class RepeatKillException extends SeckillException{ - public RepeatKillException(String message){ - super(message); - } - - public RepeatKillException(String message,Throwable cause){ - super(message,cause); - } - -} diff --git a/src/main/java/org/seckill/exception/SeckillCloseException.java b/src/main/java/org/seckill/exception/SeckillCloseException.java deleted file mode 100644 index d4dd8f3..0000000 --- a/src/main/java/org/seckill/exception/SeckillCloseException.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.seckill.exception; - - -/** - * 秒杀关闭异常 - * Created by pc on 2017/3/6. - */ -public class SeckillCloseException extends SeckillException { - public SeckillCloseException(String message) { - super(message); - } - - public SeckillCloseException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/org/seckill/exception/SeckillException.java b/src/main/java/org/seckill/exception/SeckillException.java deleted file mode 100644 index 43aec2c..0000000 --- a/src/main/java/org/seckill/exception/SeckillException.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.seckill.exception; - -/** - * 秒杀相关异常 - * Created by pc on 2017/3/6. - */ -public class SeckillException extends RuntimeException { - public SeckillException(String message) { - super(message); - } - - public SeckillException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/org/seckill/service/SeckillService.java b/src/main/java/org/seckill/service/SeckillService.java deleted file mode 100644 index f996676..0000000 --- a/src/main/java/org/seckill/service/SeckillService.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.seckill.service; - -import org.seckill.dto.*; -import org.seckill.entity.*; -import org.seckill.exception.*; - -import java.util.*; - -/**业务接口:站在使用者(程序员)的角度设计接口 - * 三个方面:1.方法定义粒度,方法定义的要非常清楚2.参数,要越简练越好 - * 3.返回类型(return 类型一定要友好/或者return异常,我们允许的异常) - */ -public interface SeckillService { - - /** - * 查询全部的秒杀记录 - * @return - */ - List getSeckillList(); - - /** - * 查询单个秒杀记录 - * @param seckillId - * @return - */ - Seckill getById(long seckillId); - - /** - * 秒杀开始时输出秒杀接口地址, - * 否则输出系统时间和秒杀时间 - * @param seckillId - */ - Exposer exposeSeckillUrl(long seckillId); - - /** - * 执行秒杀 - * @param seckillId - * @param userPhone - * @param md5 - */ - SeckillExecution executeSeckill(long seckillId,long userPhone,String md5) - throws SeckillException,RepeatKillException,SeckillCloseException; -} diff --git a/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java b/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java deleted file mode 100644 index 51f3290..0000000 --- a/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java +++ /dev/null @@ -1,146 +0,0 @@ -package org.seckill.service.impl; - -import org.apache.commons.collections.*; -import org.seckill.dao.*; -import org.seckill.dto.*; -import org.seckill.entity.*; -import org.seckill.enums.*; -import org.seckill.exception.*; -import org.seckill.service.*; -import org.slf4j.*; -import org.springframework.stereotype.*; -import org.springframework.transaction.annotation.*; -import org.springframework.util.*; - -import javax.annotation.*; -import java.util.*; - -/** - * Created by pc on 2017/3/5. - */ -@Service -public class SeckillServiceImpl implements SeckillService { - - private Logger logger=LoggerFactory.getLogger(this.getClass()); - @Resource - private SeckillDao seckillDao; - @Resource - private SuccessKilledDao successKilledDao; - @Resource - private RedisDao redisDao; - - //md5盐值,用于混淆md5 - private final String sault="abjkdkddk*234*fdjfkslsdfj&-=fdfdk"; - - public List getSeckillList() { - - return seckillDao.queryAll(0,4); - } - - public Seckill getById(long seckillId) { - - return seckillDao.queryById(seckillId); - } - - public Exposer exposeSeckillUrl(long seckillId) { - //优化点:缓存优化 - //1.访问redis - Seckill seckill = redisDao.getSeckill(seckillId); - if(seckill==null){ - //2.访问数据库 - seckill=seckillDao.queryById(seckillId); - if(seckill==null){ - return new Exposer(seckillId, false); - }else{ - //3.放入redis - redisDao.putSeckill(seckill); - } - - - } - - Date startTime=seckill.getStartTime(); - Date endTime=seckill.getEndTime(); - Date nowTime=new Date(); - - if (nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()) { - return new Exposer(seckillId, false, nowTime.getTime(), startTime.getTime(), endTime.getTime()); - } - //转换特定字符串的过程,不可逆 - String md5=getMD5(seckillId);//TODO - return new Exposer(true,md5,seckillId); - } - - private String getMD5(long seckillId){ - String base=seckillId+"/"+sault; - String md5= DigestUtils.md5DigestAsHex(base.getBytes()); - return md5; - } - - @Transactional - /** - * 使用注解控制事务方法的优点 - * 1.开发团队达成一致约定,明确标注事务方法的编程风格 - * 2.保证事务方法执行时间尽可能短(降低锁定时间),不要穿插其他网络操作RPC/HTTP或者剥离到方法外部 - * 3.不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制 - */ - public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException { - if(md5==null || !md5.equals(getMD5(seckillId))){ - return new SeckillExecution(seckillId,SeckillStateEnum.DATA_REWRITE); - } - //使用存储过程执行秒杀 - Map map=new HashMap(); - map.put("seckillId", seckillId); - map.put("userPhone", userPhone); - map.put("killTime", new Date()); - map.put("result", null); - try { - seckillDao.killByProcedure(map); - //获取result - int result=MapUtils.getInteger(map,"result",-2); - if(result==1){ - SuccessKilled successKilled = successKilledDao. - queryByIdWithSeckill(seckillId,userPhone); - return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled); - }else{ - return new SeckillExecution(seckillId, SeckillStateEnum.stateOf(result)); - } - }catch (Exception e){ - logger.error(e.getMessage(),e); - return new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); - } - -// //执行秒杀逻辑:减库存+记录购买行为 - // -// //减库存 -// try{ -// //记录购买行为 -// int insertCount=successKilledDao.insertSuccessKilled(seckillId,userPhone); -// if(insertCount<=0){ -// //重复秒杀 -// throw new RepeatKillException("seckill repeated"); -// }else{ -// //减库存,热点商品竞争,*先插入购买明细,再减库存,可以缩短行级锁时间 -// int updateCount=seckillDao.reduceNumber(seckillId,new Date()); -// if(updateCount<=0){ -// //没有更新到记录,秒杀结束 -// throw new SeckillCloseException("seckill is closed"); -// }else{ -// SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); -// return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS,successKilled); -// } -// -// } -// }catch (SeckillCloseException e){ -// throw e; -// }catch (RepeatKillException e){ -// throw e; -// }catch(Exception e){ -// logger.error(e.getMessage(),e); -// //所有编译期异常,转换为运行期异常 -// throw new SeckillException("seckill inner error:"+e.getMessage()); -// } - } - - -} diff --git a/src/main/java/org/seckill/web/SeckillController.java b/src/main/java/org/seckill/web/SeckillController.java deleted file mode 100644 index 77dd9ac..0000000 --- a/src/main/java/org/seckill/web/SeckillController.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.seckill.web; - -import org.seckill.dto.*; -import org.seckill.entity.*; -import org.seckill.enums.*; -import org.seckill.exception.*; -import org.seckill.service.*; -import org.slf4j.*; -import org.springframework.stereotype.*; -import org.springframework.ui.*; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.*; -import java.util.*; - -/** - * Created by pc on 2017/3/7. - */ -@Controller -@RequestMapping("/seckill") -public class SeckillController { - private Logger logger=LoggerFactory.getLogger(this.getClass()); - @Resource - private SeckillService seckillService; - - @RequestMapping(value = "/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 "forward:/seckill/list"; - } - model.addAttribute("seckill",seckill); - return "detail"; - } -//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){ - logger.error(e.getMessage(),e); - result =new SeckillResult(false,e.getMessage()); - } - return result; - } - - @RequestMapping(value = "{seckillId}/{md5}/execution") - @ResponseBody - public SeckillResult execute(@PathVariable("seckillId") Long seckillId, - @PathVariable("md5") String md5, - @CookieValue(value = "userPhone", required = false) Long userPhone) { - if(userPhone==null){ - return new SeckillResult(false, "未登录"); - } - SeckillResult result=null; - try{ - SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5); - return new SeckillResult(true,execution); - }catch(SeckillCloseException e){ - SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.END); - return new SeckillResult(true,seckillExecution); - }catch(RepeatKillException e){ - SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL); - return new SeckillResult(true,seckillExecution); - } - catch (Exception e){ - logger.error(e.getMessage(),e); - SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); - result = new SeckillResult(true,seckillExecution); - return result; - } - } - - @RequestMapping(value = "/time/now",method = RequestMethod.GET) - @ResponseBody - public SeckillResult time(){ - Date date=new Date(); - return new SeckillResult(true,date.getTime()); - } -} diff --git a/src/main/resources/jdbc.properties b/src/main/resources/jdbc.properties deleted file mode 100644 index 309adda..0000000 --- a/src/main/resources/jdbc.properties +++ /dev/null @@ -1,5 +0,0 @@ -driver=com.mysql.jdbc.Driver -url=jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf-8 -jdbc.user=root -password=123456 - diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 9ff0f9c..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - \ No newline at end of file diff --git a/src/main/resources/mapper/SeckillDao.xml b/src/main/resources/mapper/SeckillDao.xml deleted file mode 100644 index a6642f4..0000000 --- a/src/main/resources/mapper/SeckillDao.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - UPDATE seckill SET number=number-1 - WHERE seckill_id=#{seckillId} - AND start_time <= #{killTime} - AND end_time >=#{killTime} - AND number>0 - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/mapper/SuccessKilledDao.xml b/src/main/resources/mapper/SuccessKilledDao.xml deleted file mode 100644 index d8b4d43..0000000 --- a/src/main/resources/mapper/SuccessKilledDao.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - INSERT ignore INTO success_killed(seckill_id,user_phone,state) - VALUES (#{seckillId},#{userPhone},0) - - - - \ No newline at end of file diff --git a/src/main/resources/mybatis-config.xml b/src/main/resources/mybatis-config.xml deleted file mode 100644 index 5f924cc..0000000 --- a/src/main/resources/mybatis-config.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/spring/spring-dao.xml b/src/main/resources/spring/spring-dao.xml deleted file mode 100644 index 01c0df6..0000000 --- a/src/main/resources/spring/spring-dao.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/spring/spring-service.xml b/src/main/resources/spring/spring-service.xml deleted file mode 100644 index 0b637ba..0000000 --- a/src/main/resources/spring/spring-service.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/spring/spring-web.xml b/src/main/resources/spring/spring-web.xml deleted file mode 100644 index f95842a..0000000 --- a/src/main/resources/spring/spring-web.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/sql/schema.sql b/src/main/sql/schema.sql deleted file mode 100644 index 7b27cfd..0000000 --- a/src/main/sql/schema.sql +++ /dev/null @@ -1,38 +0,0 @@ --- 数据库初始化脚本 --- 创建数据库 -DROP Database `seckill`; -CREATE DATABASE `seckill`; --- 使用数据库 -USE `seckill`; --- 创建表 - -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 DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '开始时间',/*第一个TIMESTAMP字段会自动更新,要加上default*/ - `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) -)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT ='秒杀明细表'; - --- 初始化数据 -insert into seckill(name, number, start_time, end_time) - VALUES - ('1000元秒杀iphone6',100,'2017-03-05 00:00:00','2017-03-06 00:00:00'), - ('500元秒杀ipad2',200,'2017-03-05 00:00:00','2017-03-06 00:00:00'), - ('300元秒杀小米3',300,'2017-03-05 00:00:00','2017-03-06 00:00:00'), - ('200元秒杀红米note',400,'2017-03-05 00:00:00','2017-03-06 00:00:00'); - --- 秒杀成功明细表 --- 用户登录认证相关的信息 -CREATE TABLE `success_killed`( - `seckill_id` BIGINT NOT NULL COMMENT '秒杀商品id', - `user_phone` BIGINT NOT NULL COMMENT '用户手机号', - `state` TINYINT NOT NULL DEFAULT 0 COMMENT '状态标识:-1:无效 0:成功 1:已付款', - `create_time` TIMESTAMP NOT NULL COMMENT '创建时间', - PRIMARY KEY (seckill_id,user_phone), -- 联合主键 - KEY idx_create_time(create_time) -)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; diff --git a/src/main/sql/seckill.sql b/src/main/sql/seckill.sql deleted file mode 100644 index 4a08662..0000000 --- a/src/main/sql/seckill.sql +++ /dev/null @@ -1,56 +0,0 @@ --- 执行秒杀存储过程 -DELIMITER $$ --- 定义存储过程 --- 参数:in输入参数 out输出参数 --- row_count()返回上一条修改语句sql(update,insert,delete)影响的行数 --- row_count:0未修改行数,>0:表示影响的行数 <0:sql错误/未执行修改sql --- 存储过程内部声明的变量不能是@开头 -CREATE PROCEDURE `seckill`.`execute_seckill`( - in v_seckill_id bigint,in v_seckill_phone BIGINT,in v_seckill_time TIMESTAMP, - out r_result int) - BEGIN - DECLARE insert_count int DEFAULT 0; - START TRANSACTION; - INSERT IGNORE INTO `success_killed`(seckill_id,user_phone,create_time) - values(v_seckill_id,v_seckill_phone,v_seckill_time); - SELECT row_count() INTO insert_count; - IF(insert_count = 0) THEN - ROLLBACK; - SET r_result=-1;-- 重复秒杀 - ELSEIF(insert_count<0) THEN - ROLLBACK; - SET r_result=-2; - ELSE - UPDATE seckill SET number=number-1 - WHERE seckill_id=v_seckill_id - AND seckill.start_time < v_seckill_time - AND seckill.end_time >v_seckill_time - AND seckill.number>0; - SELECT row_count() into insert_count; - IF (insert_count=0) THEN - ROLLBACK; - SET r_result=0; - ELSEIF (insert_count<0) THEN - ROLLBACK; - SET r_result=-2; - ELSE - COMMIT; - SET r_result=1; - END IF ; - END IF; - END; -$$ --- 存储过程定义结束 -DELIMITER ; -SET @r_result=-3; --- 执行存储过程 -CALL seckill.execute_seckill(1003, 18826077111, now(), @r_result); --- 获取结果 -SELECT @r_result; - - --- 存储过程 --- 存储过程优化:事务行级锁持有的时间 --- 不要过度依赖存储过程 --- 简单的逻辑可以应用存储过程 --- QPS:一个秒杀单6000/QPS \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/common/head.jsp b/src/main/webapp/WEB-INF/jsp/common/head.jsp deleted file mode 100644 index da039a1..0000000 --- a/src/main/webapp/WEB-INF/jsp/common/head.jsp +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/common/tag.jsp b/src/main/webapp/WEB-INF/jsp/common/tag.jsp deleted file mode 100644 index 9b0e158..0000000 --- a/src/main/webapp/WEB-INF/jsp/common/tag.jsp +++ /dev/null @@ -1,3 +0,0 @@ -<%@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" %> diff --git a/src/main/webapp/WEB-INF/jsp/detail.jsp b/src/main/webapp/WEB-INF/jsp/detail.jsp deleted file mode 100644 index 9f503e9..0000000 --- a/src/main/webapp/WEB-INF/jsp/detail.jsp +++ /dev/null @@ -1,83 +0,0 @@ -<%@page contentType="text/html; charset=UTF-8" language="java" %> -<%@include file="common/tag.jsp" %> - - - - 秒杀详情页 - <%@include file="common/head.jsp" %> - - -
-
-
-

${seckill.name}

-
- -
-

- <%--显示time图标--%> - - <%--展示倒计时--%> - -

-
-
-
-<%--登录弹出层 输入电话--%> - - - -<%--jQery文件,务必在bootstrap.min.js之前引入--%> - - -<%--使用CDN 获取公共js http://www.bootcdn.cn/--%> -<%--jQuery Cookie操作插件--%> - -<%--jQuery countDown倒计时插件--%> - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/jsp/list.jsp b/src/main/webapp/WEB-INF/jsp/list.jsp deleted file mode 100644 index 70807aa..0000000 --- a/src/main/webapp/WEB-INF/jsp/list.jsp +++ /dev/null @@ -1,59 +0,0 @@ -<%@page contentType="text/html; charset=UTF-8" language="java" %> -<%@include file="common/tag.jsp"%> - - - - 秒杀商品列表 - <%@include file="common/head.jsp" %> - - -
-
-
-

秒杀列表

-
-
- - - - - - - - - - - - - - - - - - - - - - - -
名称库存开始时间结束时间创建时间详情页
${sk.name}${sk.number} - - - - - - 详情
- -
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d76a3cd..0000000 --- a/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - seckill-dispatcher - org.springframework.web.servlet.DispatcherServlet - - - contextConfigLocation - classpath:spring/spring-*.xml - - - - seckill-dispatcher - - / - - \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp deleted file mode 100644 index 6f07b72..0000000 --- a/src/main/webapp/index.jsp +++ /dev/null @@ -1,5 +0,0 @@ - - -

Hello World!

- - diff --git a/src/main/webapp/resource/script/seckill.js b/src/main/webapp/resource/script/seckill.js deleted file mode 100644 index 9a2a961..0000000 --- a/src/main/webapp/resource/script/seckill.js +++ /dev/null @@ -1,149 +0,0 @@ -//存放主要交互逻辑的js代码 -// javascript 模块化(package.类.方法) - -var seckill = { - - //封装秒杀相关ajax的url - URL: { - now: function () { - return '/seckill/time/now'; - }, - exposer: function (seckillId) { - return '/seckill/' + seckillId + '/exposer'; - }, - execution: function (seckillId, md5) { - return '/seckill/' + seckillId + '/' + md5 + '/execution'; - } - }, - - //验证手机号 - validatePhone: function (phone) { - if (phone && phone.length == 11 && !isNaN(phone)) { - return true;//直接判断对象会看对象是否为空,空就是undefine就是false; isNaN 非数字返回true - } else { - return false; - } - }, - - //详情页秒杀逻辑 - detail: { - //详情页初始化 - init: function (params) { - //手机验证和登录,计时交互 - //规划我们的交互流程 - //在cookie中查找手机号 - var userPhone = $.cookie('userPhone'); - //验证手机号 - if (!seckill.validatePhone(userPhone)) { - //绑定手机 控制输出 - var killPhoneModal = $('#killPhoneModal'); - killPhoneModal.modal({ - show: true,//显示弹出层 - backdrop: 'static',//禁止位置关闭 - keyboard: false//关闭键盘事件 - }); - - $('#killPhoneBtn').click(function () { - var inputPhone = $('#killPhoneKey').val(); - console.log("inputPhone: " + inputPhone); - if (seckill.validatePhone(inputPhone)) { - //电话写入cookie(7天过期) - $.cookie('userPhone', inputPhone, {expires: 7, path: '/seckill'}); - //验证通过  刷新页面 - window.location.reload(); - } else { - //todo 错误文案信息抽取到前端字典里 - $('#killPhoneMessage').hide().html('').show(300); - } - }); - } - - //已经登录 - //计时交互 - var startTime = params['startTime']; - var endTime = params['endTime']; - var seckillId = params['seckillId']; - $.get(seckill.URL.now(), {}, function (result) { - if (result && result['success']) { - var nowTime = result['data']; - //时间判断 计时交互 - seckill.countDown(seckillId, nowTime, startTime, endTime); - } else { - console.log('result: ' + result); - alert('result: ' + result); - } - }); - } - }, - - handlerSeckill: function (seckillId, node) { - //获取秒杀地址,控制显示器,执行秒杀 - node.hide().html(''); - - $.get(seckill.URL.exposer(seckillId), {}, function (result) { - //在回调函数种执行交互流程 - if (result && result['success']) { - var exposer = result['data']; - if (exposer['exposed']) { - //开启秒杀 - //获取秒杀地址 - var md5 = exposer['md5']; - var killUrl = seckill.URL.execution(seckillId, md5); - console.log("killUrl: " + killUrl); - //绑定一次点击事件 - $('#killBtn').one('click', function () { - //执行秒杀请求 - //1.先禁用按钮 - $(this).addClass('disabled');//,<-$(this)===('#killBtn')-> - //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); - } - }); - - }, - - 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);//todo 防止时间偏移 - seckillBox.countdown(killTime, function (event) { - //时间格式 - var format = event.strftime('秒杀倒计时: %D天 %H时 %M分 %S秒 '); - seckillBox.html(format); - }).on('finish.countdown', function () { - //时间完成后回调事件 - //获取秒杀地址,控制现实逻辑,执行秒杀 - console.log('______fininsh.countdown'); - seckill.handlerSeckill(seckillId, seckillBox); - }); - } else { - //秒杀开始 - seckill.handlerSeckill(seckillId, seckillBox); - } - } - -} \ No newline at end of file diff --git a/src/test/java/org/seckill/dao/RedisDaoTest.java b/src/test/java/org/seckill/dao/RedisDaoTest.java deleted file mode 100644 index 2b5cc7b..0000000 --- a/src/test/java/org/seckill/dao/RedisDaoTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.seckill.dao; - -import org.junit.*; -import org.junit.runner.*; -import org.seckill.entity.*; -import org.springframework.test.context.*; -import org.springframework.test.context.junit4.*; - -import javax.annotation.*; - -import static org.junit.Assert.*; - -/** - * Created by pc on 2017/3/8. - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:spring/spring-dao.xml") -public class RedisDaoTest { - private long id=1001L; - - @Resource - private RedisDao redisDao; - @Resource - private SeckillDao seckillDao; - - - @Test - public void testSeckill() throws Exception { - Seckill seckill=redisDao.getSeckill(id); - if(seckill==null){ - seckill = seckillDao.queryById(id); - if(seckill!=null){ - String result = redisDao.putSeckill(seckill); - System.out.println(result); - seckill = redisDao.getSeckill(id); - System.out.println(seckill); - } - } - } - -} \ No newline at end of file diff --git a/src/test/java/org/seckill/dao/SeckillDaoTest.java b/src/test/java/org/seckill/dao/SeckillDaoTest.java deleted file mode 100644 index 9f838a3..0000000 --- a/src/test/java/org/seckill/dao/SeckillDaoTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.seckill.dao; - -import org.junit.*; -import org.junit.runner.*; -import org.seckill.entity.*; -import org.springframework.test.context.*; -import org.springframework.test.context.junit4.*; - -import javax.annotation.*; -import java.util.*; - -/** - * Created by pc on 2017/3/5. - */ -//配置spring和junit整合,这样junit在启动时就会加载spring容器 -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:spring/spring-dao.xml") -public class SeckillDaoTest { - //注入Dao实现类依赖 - @Resource - private SeckillDao seckillDao; - - @Test - public void queryById(){ - long seckillId=1001; - Seckill seckill=seckillDao.queryById(seckillId); - System.out.println(seckill.getName()); - System.out.println(seckill); - } - - @Test - public void queryAll(){ - List seckillList=seckillDao.queryAll(0,100); - for(Seckill seckill:seckillList){ - System.out.println(seckill); - } - } - - @Test - public void reduceNumber(){ - long id=1000; - int updateCount=seckillDao.reduceNumber(1000, new Date()); - System.out.println(updateCount); - } -} - diff --git a/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java b/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java deleted file mode 100644 index c1548eb..0000000 --- a/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.seckill.dao; - -import org.junit.*; -import org.junit.runner.*; -import org.seckill.entity.*; -import org.springframework.test.context.*; -import org.springframework.test.context.junit4.*; - -import javax.annotation.*; - -/** - * Created by pc on 2017/3/5. - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:spring/spring-dao.xml") -public class SuccessKilledDaoTest { - @Resource - private SuccessKilledDao successKilledDao; - - @Test - public void insertSuccessKilled(){ - long seckillId=1000L; - long userPhone=18826077187L; - int updateCount=successKilledDao.insertSuccessKilled(seckillId,userPhone); - System.out.println(updateCount); - } - - @Test - public void queryByIdWithSeckill(){ - - SuccessKilled successKilled=successKilledDao.queryByIdWithSeckill(1000L,18826077187L); - System.out.println(successKilled); - System.out.println(successKilled.getSeckill()); - - } -} diff --git a/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java b/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java deleted file mode 100644 index eba9587..0000000 --- a/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.seckill.service.impl; - -import org.junit.*; -import org.junit.runner.*; -import org.seckill.dto.*; -import org.seckill.entity.*; -import org.seckill.exception.*; -import org.seckill.service.*; -import org.slf4j.*; -import org.springframework.test.context.*; -import org.springframework.test.context.junit4.*; - -import javax.annotation.*; - -import java.util.*; - -import static org.junit.Assert.*; - -/** - * Created by pc on 2017/3/6. - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration({ - "classpath:spring/spring-dao.xml", - "classpath:spring/spring-service.xml" -}) -public class SeckillServiceImplTest { - Logger logger=LoggerFactory.getLogger(this.getClass()); - @Resource - private SeckillService seckillService; - - @Test - public void testGetSeckillList() throws Exception { - List seckillList=seckillService.getSeckillList(); - logger.info("list={}",seckillList); - } - - @Test - public void testGetById() throws Exception { - Seckill seckill=seckillService.getById(1000L); - logger.info("seckill={}",seckill); - - } - //集成测试代码完整逻辑,注意可重复执行 - @Test - public void testSeckillLogic() throws Exception { - //exposer=Exposer{exposed=true, md5='ae8c840b6c3a4891c593ec70b9dc2c06', - // seckillId=1000, now=0, start=0, end=0} - long id=1001L; - Exposer exposer=seckillService.exposeSeckillUrl(id); - logger.info("exposer={}",exposer); - if(exposer!=null){ - //用try-catch包围才能单元测试通过,否则抛出异常视为不通过 - try{ - long userPhone=18826077189L; - SeckillExecution seckillExecution=seckillService.executeSeckill(id, userPhone, exposer.getMd5()); - logger.info("seckillExecution={}",seckillExecution); - }catch(RepeatKillException e){ - logger.error(e.getMessage()); - }catch (SeckillCloseException e){ - logger.error(e.getMessage()); - }catch (SeckillException e){ - logger.error(e.getMessage()); - } - }else{ - logger.warn("exposer={}",exposer); - } - } - - @Test - public void testProcedure(){ - long seckillId=1001L; - long userPhone=18826077188L; - Exposer exposer = seckillService.exposeSeckillUrl(seckillId); - if(exposer.isExposed()){ - String md5=exposer.getMd5(); - SeckillExecution execution=seckillService.executeSeckill(seckillId, userPhone, md5); - logger.info(execution.getStateInfo()); - } - } -} \ No newline at end of file From 7d2c78d4d189318f2ea063e84ce5401023d7284d Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Wed, 8 Mar 2017 15:10:23 +0800 Subject: [PATCH 5/8] =?UTF-8?q?SSM+Redis+MySQL=E6=9E=84=E5=BB=BA=E7=9A=84?= =?UTF-8?q?=E7=A7=92=E6=9D=80=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seckill/.idea/.name | 1 + seckill/.idea/artifacts/seckill_war.xml | 8 + .../.idea/artifacts/seckill_war_exploded.xml | 50 + seckill/.idea/compiler.xml | 32 + seckill/.idea/copyright/profiles_settings.xml | 3 + seckill/.idea/dataSources.ids | 320 ++++ seckill/.idea/dataSources.local.xml | 17 + seckill/.idea/dataSources.xml | 32 + seckill/.idea/encodings.xml | 6 + .../Maven__aopalliance_aopalliance_1_0.xml | 13 + .../libraries/Maven__c3p0_c3p0_0_9_1_2.xml | 13 + ...__ch_qos_logback_logback_classic_1_1_2.xml | 13 + ...ven__ch_qos_logback_logback_core_1_1_2.xml | 13 + ...roject_protostuff_protostuff_api_1_0_8.xml | 13 + ...tuff_protostuff_collectionschema_1_0_8.xml | 13 + ...oject_protostuff_protostuff_core_1_0_8.xml | 13 + ...ct_protostuff_protostuff_runtime_1_0_8.xml | 13 + ...jackson_core_jackson_annotations_2_8_0.xml | 13 + ...terxml_jackson_core_jackson_core_2_8_5.xml | 13 + ...ml_jackson_core_jackson_databind_2_8_5.xml | 13 + ..._collections_commons_collections_3_2_1.xml | 13 + ...n__commons_logging_commons_logging_1_2.xml | 13 + ..._javax_servlet_javax_servlet_api_3_1_0.xml | 13 + .../.idea/libraries/Maven__jstl_jstl_1_2.xml | 13 + .../libraries/Maven__junit_junit_4_12.xml | 13 + ...ven__mysql_mysql_connector_java_5_1_38.xml | 13 + ...org_apache_commons_commons_pool2_2_4_2.xml | 13 + .../Maven__org_hamcrest_hamcrest_core_1_3.xml | 13 + .../Maven__org_mybatis_mybatis_3_2_8.xml | 13 + ...aven__org_mybatis_mybatis_spring_1_2_2.xml | 13 + .../Maven__org_slf4j_slf4j_api_1_7_12.xml | 13 + ...ringframework_spring_aop_4_1_6_RELEASE.xml | 13 + ...ngframework_spring_beans_4_1_6_RELEASE.xml | 13 + ...framework_spring_context_4_1_6_RELEASE.xml | 13 + ...ingframework_spring_core_4_1_6_RELEASE.xml | 13 + ...mework_spring_expression_4_1_6_RELEASE.xml | 13 + ...ingframework_spring_jdbc_4_1_6_RELEASE.xml | 13 + ...ingframework_spring_test_4_1_6_RELEASE.xml | 13 + ...pringframework_spring_tx_4_1_6_RELEASE.xml | 13 + ...ringframework_spring_web_4_1_6_RELEASE.xml | 13 + ...gframework_spring_webmvc_4_1_6_RELEASE.xml | 13 + .../Maven__redis_clients_jedis_2_9_0.xml | 13 + .../Maven__taglibs_standard_1_1_2.xml | 13 + seckill/.idea/misc.xml | 29 + seckill/.idea/modules.xml | 8 + seckill/.idea/sqldialects.xml | 7 + seckill/.idea/uiDesigner.xml | 124 ++ seckill/.idea/workspace.xml | 1604 +++++++++++++++++ seckill/pom.xml | 163 ++ seckill/seckill.iml | 66 + .../main/java/org/seckill/dao/RedisDao.java | 66 + .../main/java/org/seckill/dao/SeckillDao.java | 38 + .../org/seckill/dao/SuccessKilledDao.java | 24 + .../main/java/org/seckill/dto/Exposer.java | 99 + .../org/seckill/dto/SeckillExecution.java | 73 + .../java/org/seckill/dto/SeckillResult.java | 45 + .../main/java/org/seckill/entity/Seckill.java | 77 + .../org/seckill/entity/SuccessKilled.java | 66 + .../org/seckill/enums/SeckillStateEnum.java | 40 + .../exception/RepeatKillException.java | 17 + .../exception/SeckillCloseException.java | 16 + .../seckill/exception/SeckillException.java | 15 + .../org/seckill/service/SeckillService.java | 43 + .../service/impl/SeckillServiceImpl.java | 146 ++ .../org/seckill/web/SeckillController.java | 95 + seckill/src/main/resources/jdbc.properties | 5 + seckill/src/main/resources/logback.xml | 15 + .../src/main/resources/mapper/SeckillDao.xml | 35 + .../resources/mapper/SuccessKilledDao.xml | 31 + seckill/src/main/resources/mybatis-config.xml | 17 + .../src/main/resources/spring/spring-dao.xml | 60 + .../main/resources/spring/spring-service.xml | 20 + .../src/main/resources/spring/spring-web.xml | 34 + seckill/src/main/sql/schema.sql | 38 + seckill/src/main/sql/seckill.sql | 56 + .../main/webapp/WEB-INF/jsp/common/head.jsp | 13 + .../main/webapp/WEB-INF/jsp/common/tag.jsp | 3 + .../src/main/webapp/WEB-INF/jsp/detail.jsp | 83 + seckill/src/main/webapp/WEB-INF/jsp/list.jsp | 59 + seckill/src/main/webapp/WEB-INF/web.xml | 28 + seckill/src/main/webapp/index.jsp | 5 + .../main/webapp/resource/script/seckill.js | 149 ++ .../java/org/seckill/dao/RedisDaoTest.java | 41 + .../java/org/seckill/dao/SeckillDaoTest.java | 46 + .../org/seckill/dao/SuccessKilledDaoTest.java | 36 + .../service/impl/SeckillServiceImplTest.java | 81 + .../\346\236\266\346\236\204\345\233\276.jpg" | Bin 0 -> 59708 bytes "seckill/\350\257\264\346\230\216 .txt" | 35 + 88 files changed, 4662 insertions(+) create mode 100644 seckill/.idea/.name create mode 100644 seckill/.idea/artifacts/seckill_war.xml create mode 100644 seckill/.idea/artifacts/seckill_war_exploded.xml create mode 100644 seckill/.idea/compiler.xml create mode 100644 seckill/.idea/copyright/profiles_settings.xml create mode 100644 seckill/.idea/dataSources.ids create mode 100644 seckill/.idea/dataSources.local.xml create mode 100644 seckill/.idea/dataSources.xml create mode 100644 seckill/.idea/encodings.xml create mode 100644 seckill/.idea/libraries/Maven__aopalliance_aopalliance_1_0.xml create mode 100644 seckill/.idea/libraries/Maven__c3p0_c3p0_0_9_1_2.xml create mode 100644 seckill/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_1_2.xml create mode 100644 seckill/.idea/libraries/Maven__ch_qos_logback_logback_core_1_1_2.xml create mode 100644 seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml create mode 100644 seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml create mode 100644 seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml create mode 100644 seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml create mode 100644 seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_8_0.xml create mode 100644 seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_8_5.xml create mode 100644 seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_8_5.xml create mode 100644 seckill/.idea/libraries/Maven__commons_collections_commons_collections_3_2_1.xml create mode 100644 seckill/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml create mode 100644 seckill/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_1_0.xml create mode 100644 seckill/.idea/libraries/Maven__jstl_jstl_1_2.xml create mode 100644 seckill/.idea/libraries/Maven__junit_junit_4_12.xml create mode 100644 seckill/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_38.xml create mode 100644 seckill/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_4_2.xml create mode 100644 seckill/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml create mode 100644 seckill/.idea/libraries/Maven__org_mybatis_mybatis_3_2_8.xml create mode 100644 seckill/.idea/libraries/Maven__org_mybatis_mybatis_spring_1_2_2.xml create mode 100644 seckill/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_12.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_aop_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_beans_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_context_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_core_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_expression_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_jdbc_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_test_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_tx_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_web_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__org_springframework_spring_webmvc_4_1_6_RELEASE.xml create mode 100644 seckill/.idea/libraries/Maven__redis_clients_jedis_2_9_0.xml create mode 100644 seckill/.idea/libraries/Maven__taglibs_standard_1_1_2.xml create mode 100644 seckill/.idea/misc.xml create mode 100644 seckill/.idea/modules.xml create mode 100644 seckill/.idea/sqldialects.xml create mode 100644 seckill/.idea/uiDesigner.xml create mode 100644 seckill/.idea/workspace.xml create mode 100644 seckill/pom.xml create mode 100644 seckill/seckill.iml create mode 100644 seckill/src/main/java/org/seckill/dao/RedisDao.java create mode 100644 seckill/src/main/java/org/seckill/dao/SeckillDao.java create mode 100644 seckill/src/main/java/org/seckill/dao/SuccessKilledDao.java create mode 100644 seckill/src/main/java/org/seckill/dto/Exposer.java create mode 100644 seckill/src/main/java/org/seckill/dto/SeckillExecution.java create mode 100644 seckill/src/main/java/org/seckill/dto/SeckillResult.java create mode 100644 seckill/src/main/java/org/seckill/entity/Seckill.java create mode 100644 seckill/src/main/java/org/seckill/entity/SuccessKilled.java create mode 100644 seckill/src/main/java/org/seckill/enums/SeckillStateEnum.java create mode 100644 seckill/src/main/java/org/seckill/exception/RepeatKillException.java create mode 100644 seckill/src/main/java/org/seckill/exception/SeckillCloseException.java create mode 100644 seckill/src/main/java/org/seckill/exception/SeckillException.java create mode 100644 seckill/src/main/java/org/seckill/service/SeckillService.java create mode 100644 seckill/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java create mode 100644 seckill/src/main/java/org/seckill/web/SeckillController.java create mode 100644 seckill/src/main/resources/jdbc.properties create mode 100644 seckill/src/main/resources/logback.xml create mode 100644 seckill/src/main/resources/mapper/SeckillDao.xml create mode 100644 seckill/src/main/resources/mapper/SuccessKilledDao.xml create mode 100644 seckill/src/main/resources/mybatis-config.xml create mode 100644 seckill/src/main/resources/spring/spring-dao.xml create mode 100644 seckill/src/main/resources/spring/spring-service.xml create mode 100644 seckill/src/main/resources/spring/spring-web.xml create mode 100644 seckill/src/main/sql/schema.sql create mode 100644 seckill/src/main/sql/seckill.sql create mode 100644 seckill/src/main/webapp/WEB-INF/jsp/common/head.jsp create mode 100644 seckill/src/main/webapp/WEB-INF/jsp/common/tag.jsp create mode 100644 seckill/src/main/webapp/WEB-INF/jsp/detail.jsp create mode 100644 seckill/src/main/webapp/WEB-INF/jsp/list.jsp create mode 100644 seckill/src/main/webapp/WEB-INF/web.xml create mode 100644 seckill/src/main/webapp/index.jsp create mode 100644 seckill/src/main/webapp/resource/script/seckill.js create mode 100644 seckill/src/test/java/org/seckill/dao/RedisDaoTest.java create mode 100644 seckill/src/test/java/org/seckill/dao/SeckillDaoTest.java create mode 100644 seckill/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java create mode 100644 seckill/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java create mode 100644 "seckill/\346\236\266\346\236\204\345\233\276.jpg" create mode 100644 "seckill/\350\257\264\346\230\216 .txt" diff --git a/seckill/.idea/.name b/seckill/.idea/.name new file mode 100644 index 0000000..02fde01 --- /dev/null +++ b/seckill/.idea/.name @@ -0,0 +1 @@ +Seckill \ No newline at end of file diff --git a/seckill/.idea/artifacts/seckill_war.xml b/seckill/.idea/artifacts/seckill_war.xml new file mode 100644 index 0000000..b4faa23 --- /dev/null +++ b/seckill/.idea/artifacts/seckill_war.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/target + + + + + \ No newline at end of file diff --git a/seckill/.idea/artifacts/seckill_war_exploded.xml b/seckill/.idea/artifacts/seckill_war_exploded.xml new file mode 100644 index 0000000..ef661c7 --- /dev/null +++ b/seckill/.idea/artifacts/seckill_war_exploded.xml @@ -0,0 +1,50 @@ + + + $PROJECT_DIR$/target/seckill + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/compiler.xml b/seckill/.idea/compiler.xml new file mode 100644 index 0000000..962cfde --- /dev/null +++ b/seckill/.idea/compiler.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/copyright/profiles_settings.xml b/seckill/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/seckill/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/seckill/.idea/dataSources.ids b/seckill/.idea/dataSources.ids new file mode 100644 index 0000000..b41ec8d --- /dev/null +++ b/seckill/.idea/dataSources.ids @@ -0,0 +1,320 @@ + + + + + #@ + ` + + + + + +
+
+
+
+
+
+
+ + PROCEDURE + + + PROCEDURE + + + PROCEDURE + + + 1 + int(11) + + + int(11) + + + varchar(1) + + + id + 1 + + + 1 + int(11) + + + 1 + varchar(30) + + + int(11) + + + int(11) + + + date + + + varchar(30) + + + varchar(11) + + + varchar(30) + + + date + + + varchar(30) + + + date + + + int(11) + + + id + 1 + + + 主键 + 1 + int(11) + + + 指令名称 + varchar(16) + + + 描述 + varchar(32) + + + 内容 + varchar(2048) + + + ID + 1 + + + 1 + int(11) + + + 1 + varchar(16) + + + int(11) + + + int(11) + + + varchar(128) + + + id + 1 + + + 用户id + 1 + int(11) + + + 用户邮箱 + 1 + varchar(255) + + + 用户名 + 1 + varchar(20) + + + 用户密码 + 1 + varchar(255) + + + 0表示女,1表示男 + 1 + int(1) + '1' + + + id + 1 + + + 1 + int(11) + + + 1 + varchar(20) + + + 1 + varchar(20) + + + id + 1 + + + 1 + int(11) + + + 1 + varchar(20) + + + 1 + varchar(20) + + + id + 1 + + + 1 + int(11) + + + varchar(20) + + + userid + 1 + + + OUT + int(11) + + + IN + varchar(20) + + + + + + #@ + ` + + + + +
+ 秒杀明细表 +
+ + 秒杀成功明细表 +
+ + PROCEDURE + + + 商品库存id + 1 + bigint(20) + + + 商品名称 + 1 + varchar(120) + + + 库存数量 + 1 + int(11) + + + 开始时间 + 1 + timestamp + 'CURRENT_TIMESTAMP' + + + 结束时间 + 1 + timestamp + '0000-00-00 00:00:00' + + + 创建时间 + 1 + timestamp + 'CURRENT_TIMESTAMP' + + + end_time + + + + start_time + + + + seckill_id + 1 + + + 秒杀商品id + 1 + bigint(20) + + + 用户手机号 + 1 + bigint(20) + + + 状态标识:-1:无效 0:成功 1:已付款 + 1 + tinyint(4) + '-1' + + + 创建时间 + 1 + timestamp + 'CURRENT_TIMESTAMP' + + + create_time + + + + seckill_id +user_phone + 1 + + + IN + bigint(20) + + + IN + bigint(20) + + + IN + timestamp + + + OUT + int(11) + +
+
+
\ No newline at end of file diff --git a/seckill/.idea/dataSources.local.xml b/seckill/.idea/dataSources.local.xml new file mode 100644 index 0000000..180025a --- /dev/null +++ b/seckill/.idea/dataSources.local.xml @@ -0,0 +1,17 @@ + + + + + master_key + root + imooc.* + imooc.* + + + master_key + root + seckill.* + seckill.* + + + \ No newline at end of file diff --git a/seckill/.idea/dataSources.xml b/seckill/.idea/dataSources.xml new file mode 100644 index 0000000..99a9ea9 --- /dev/null +++ b/seckill/.idea/dataSources.xml @@ -0,0 +1,32 @@ + + + + + mysql + true + com.mysql.jdbc.Driver + jdbc:mysql://localhost:3306/imooc + MySQL + + + + + + + + + + mysql + true + com.mysql.jdbc.Driver + jdbc:mysql://localhost:3306/seckill + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/encodings.xml b/seckill/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/seckill/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__aopalliance_aopalliance_1_0.xml b/seckill/.idea/libraries/Maven__aopalliance_aopalliance_1_0.xml new file mode 100644 index 0000000..30ff5cb --- /dev/null +++ b/seckill/.idea/libraries/Maven__aopalliance_aopalliance_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__c3p0_c3p0_0_9_1_2.xml b/seckill/.idea/libraries/Maven__c3p0_c3p0_0_9_1_2.xml new file mode 100644 index 0000000..fead87b --- /dev/null +++ b/seckill/.idea/libraries/Maven__c3p0_c3p0_0_9_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_1_2.xml b/seckill/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_1_2.xml new file mode 100644 index 0000000..a8b0d9c --- /dev/null +++ b/seckill/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__ch_qos_logback_logback_core_1_1_2.xml b/seckill/.idea/libraries/Maven__ch_qos_logback_logback_core_1_1_2.xml new file mode 100644 index 0000000..1c76d0b --- /dev/null +++ b/seckill/.idea/libraries/Maven__ch_qos_logback_logback_core_1_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml new file mode 100644 index 0000000..5037168 --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml new file mode 100644 index 0000000..14524d7 --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml new file mode 100644 index 0000000..8842fdd --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml new file mode 100644 index 0000000..e831aae --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_8_0.xml b/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_8_0.xml new file mode 100644 index 0000000..49b4ec7 --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_8_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_8_5.xml b/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_8_5.xml new file mode 100644 index 0000000..1c8e0ca --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_8_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_8_5.xml b/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_8_5.xml new file mode 100644 index 0000000..75e6312 --- /dev/null +++ b/seckill/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_8_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__commons_collections_commons_collections_3_2_1.xml b/seckill/.idea/libraries/Maven__commons_collections_commons_collections_3_2_1.xml new file mode 100644 index 0000000..3caee7e --- /dev/null +++ b/seckill/.idea/libraries/Maven__commons_collections_commons_collections_3_2_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml b/seckill/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml new file mode 100644 index 0000000..eab40b3 --- /dev/null +++ b/seckill/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_1_0.xml b/seckill/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_1_0.xml new file mode 100644 index 0000000..c24f7e3 --- /dev/null +++ b/seckill/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__jstl_jstl_1_2.xml b/seckill/.idea/libraries/Maven__jstl_jstl_1_2.xml new file mode 100644 index 0000000..40b1672 --- /dev/null +++ b/seckill/.idea/libraries/Maven__jstl_jstl_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__junit_junit_4_12.xml b/seckill/.idea/libraries/Maven__junit_junit_4_12.xml new file mode 100644 index 0000000..d411041 --- /dev/null +++ b/seckill/.idea/libraries/Maven__junit_junit_4_12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_38.xml b/seckill/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_38.xml new file mode 100644 index 0000000..c648589 --- /dev/null +++ b/seckill/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_38.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_4_2.xml b/seckill/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_4_2.xml new file mode 100644 index 0000000..2970b5e --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_apache_commons_commons_pool2_2_4_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml b/seckill/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 0000000..f58bbc1 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_mybatis_mybatis_3_2_8.xml b/seckill/.idea/libraries/Maven__org_mybatis_mybatis_3_2_8.xml new file mode 100644 index 0000000..956e478 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_mybatis_mybatis_3_2_8.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_mybatis_mybatis_spring_1_2_2.xml b/seckill/.idea/libraries/Maven__org_mybatis_mybatis_spring_1_2_2.xml new file mode 100644 index 0000000..8828a9a --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_mybatis_mybatis_spring_1_2_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_12.xml b/seckill/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_12.xml new file mode 100644 index 0000000..411efc6 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_aop_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_aop_4_1_6_RELEASE.xml new file mode 100644 index 0000000..60350c7 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_aop_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_beans_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_beans_4_1_6_RELEASE.xml new file mode 100644 index 0000000..77c9c5b --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_beans_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_context_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_context_4_1_6_RELEASE.xml new file mode 100644 index 0000000..9ddee11 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_context_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_core_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_core_4_1_6_RELEASE.xml new file mode 100644 index 0000000..0ffa74d --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_core_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_expression_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_expression_4_1_6_RELEASE.xml new file mode 100644 index 0000000..e7a78b6 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_expression_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_jdbc_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_jdbc_4_1_6_RELEASE.xml new file mode 100644 index 0000000..92657af --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_jdbc_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_test_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_test_4_1_6_RELEASE.xml new file mode 100644 index 0000000..d59ba77 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_test_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_tx_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_tx_4_1_6_RELEASE.xml new file mode 100644 index 0000000..dfa7829 --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_tx_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_web_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_web_4_1_6_RELEASE.xml new file mode 100644 index 0000000..5dd7b2f --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_web_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__org_springframework_spring_webmvc_4_1_6_RELEASE.xml b/seckill/.idea/libraries/Maven__org_springframework_spring_webmvc_4_1_6_RELEASE.xml new file mode 100644 index 0000000..7b1e6cf --- /dev/null +++ b/seckill/.idea/libraries/Maven__org_springframework_spring_webmvc_4_1_6_RELEASE.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__redis_clients_jedis_2_9_0.xml b/seckill/.idea/libraries/Maven__redis_clients_jedis_2_9_0.xml new file mode 100644 index 0000000..77144ff --- /dev/null +++ b/seckill/.idea/libraries/Maven__redis_clients_jedis_2_9_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/libraries/Maven__taglibs_standard_1_1_2.xml b/seckill/.idea/libraries/Maven__taglibs_standard_1_1_2.xml new file mode 100644 index 0000000..8d7bb73 --- /dev/null +++ b/seckill/.idea/libraries/Maven__taglibs_standard_1_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/misc.xml b/seckill/.idea/misc.xml new file mode 100644 index 0000000..1d954da --- /dev/null +++ b/seckill/.idea/misc.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/modules.xml b/seckill/.idea/modules.xml new file mode 100644 index 0000000..65c393d --- /dev/null +++ b/seckill/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/sqldialects.xml b/seckill/.idea/sqldialects.xml new file mode 100644 index 0000000..7fc31e5 --- /dev/null +++ b/seckill/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/uiDesigner.xml b/seckill/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/seckill/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/.idea/workspace.xml b/seckill/.idea/workspace.xml new file mode 100644 index 0000000..3f8a386 --- /dev/null +++ b/seckill/.idea/workspace.xml @@ -0,0 +1,1604 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1488697514789 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file://$PROJECT_DIR$/src/test/java/org/seckill/dao/RedisDaoTest.java + 28 + + + + file://$PROJECT_DIR$/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java + 73 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + seckill:war + + + + + + + + Web + + + + + + + + + + + + + + + 1.8 + + + + + + + + seckill + + + + + + + + 1.8 + + + + + + + + Maven: aopalliance:aopalliance:1.0 + + + + + + + + \ No newline at end of file diff --git a/seckill/pom.xml b/seckill/pom.xml new file mode 100644 index 0000000..7d396bc --- /dev/null +++ b/seckill/pom.xml @@ -0,0 +1,163 @@ + + 4.0.0 + org.seckill + seckill + war + 1.0-SNAPSHOT + seckill Maven Webapp + http://maven.apache.org + + + + junit + junit + 4.12 + test + + + + + + org.slf4j + slf4j-api + 1.7.12 + + + ch.qos.logback + logback-core + 1.1.2 + + + + ch.qos.logback + logback-classic + 1.1.2 + + + + mysql + mysql-connector-java + 5.1.38 + runtime + + + c3p0 + c3p0 + 0.9.1.2 + + + + + org.mybatis + mybatis + 3.2.8 + + + + org.mybatis + mybatis-spring + 1.2.2 + + + + + taglibs + standard + 1.1.2 + + + jstl + jstl + 1.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.8.5 + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + org.springframework + spring-core + 4.1.6.RELEASE + + + org.springframework + spring-beans + 4.1.6.RELEASE + + + org.springframework + spring-context + 4.1.6.RELEASE + + + + org.springframework + spring-jdbc + 4.1.6.RELEASE + + + org.springframework + spring-tx + 4.1.6.RELEASE + + + + org.springframework + spring-web + 4.1.6.RELEASE + + + org.springframework + spring-webmvc + 4.1.6.RELEASE + + + + org.springframework + spring-test + 4.1.6.RELEASE + + + + redis.clients + jedis + 2.9.0 + + + + com.dyuproject.protostuff + protostuff-core + 1.0.8 + + + com.dyuproject.protostuff + protostuff-runtime + 1.0.8 + + + + commons-collections + commons-collections + 3.2.1 + + + + + + + seckill + + diff --git a/seckill/seckill.iml b/seckill/seckill.iml new file mode 100644 index 0000000..6c71895 --- /dev/null +++ b/seckill/seckill.iml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/java/org/seckill/dao/RedisDao.java b/seckill/src/main/java/org/seckill/dao/RedisDao.java new file mode 100644 index 0000000..ac7b6e7 --- /dev/null +++ b/seckill/src/main/java/org/seckill/dao/RedisDao.java @@ -0,0 +1,66 @@ +package org.seckill.dao; + +import com.dyuproject.protostuff.*; +import com.dyuproject.protostuff.runtime.*; +import org.seckill.entity.*; +import org.slf4j.*; +import redis.clients.jedis.*; + +/** + * Created by pc on 2017/3/8. + */ +public class RedisDao { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final JedisPool jedisPool; + + private RuntimeSchema schema =RuntimeSchema.createFrom(Seckill.class); + public RedisDao(String ip,int port){ + jedisPool = new JedisPool(ip, port); + } + + public Seckill getSeckill(long seckillId){ + try{ + Jedis jedis=jedisPool.getResource(); + try { + String key="seckill:"+seckillId; + //没有实现内部序列化 + //get->byte[]->反序列化->Object(Seckill) + //采用自定义序列化 + //protostuff:pojo + byte[] bytes=jedis.get(key.getBytes()); + if(bytes!=null){ + Seckill seckill=schema.newMessage(); + ProtostuffIOUtil.mergeFrom(bytes,seckill,schema); + //seckill被反序列化 + return seckill; + } + }finally { + jedis.close(); + } + }catch (Exception e){ + logger.error(e.getMessage(),e); + } + return null; + } + + public String putSeckill(Seckill seckill){ + //set Object(Seckill)->序列化->byte[] + try{ + Jedis jedis=jedisPool.getResource(); + try { + String key="seckill:"+seckill.getSeckillId(); + byte[] bytes=ProtostuffIOUtil.toByteArray(seckill, schema, + LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)); + //超时缓存 + int timeout=60*60; + String result=jedis.setex(key.getBytes(),timeout, bytes); + return result; + }finally { + jedis.close(); + } + }catch (Exception e){ + logger.error(e.getMessage(),e); + } + return null; + } +} diff --git a/seckill/src/main/java/org/seckill/dao/SeckillDao.java b/seckill/src/main/java/org/seckill/dao/SeckillDao.java new file mode 100644 index 0000000..561b20f --- /dev/null +++ b/seckill/src/main/java/org/seckill/dao/SeckillDao.java @@ -0,0 +1,38 @@ +package org.seckill.dao; + +import org.apache.ibatis.annotations.*; +import org.seckill.entity.*; + +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +public interface SeckillDao { + /**减库存 + * @param seckillId + * @param killTime + * @return 影响行数 + */ + int reduceNumber(@Param("seckillId") long seckillId,@Param("killTime") Date killTime); + + /**根据id查询秒杀对象 + * @param seckillId + * @return + */ + Seckill queryById(long seckillId); + + /**根据偏移量查询秒杀商品列表 + * @param offset + * @param limit + * @return + */ + List queryAll(@Param("offset") int offset,@Param("limit") int limit); + + /** + * 使用存储过程执行秒杀 + * @param paramMap + */ + void killByProcedure(Map paramMap); + +} diff --git a/seckill/src/main/java/org/seckill/dao/SuccessKilledDao.java b/seckill/src/main/java/org/seckill/dao/SuccessKilledDao.java new file mode 100644 index 0000000..03f4b47 --- /dev/null +++ b/seckill/src/main/java/org/seckill/dao/SuccessKilledDao.java @@ -0,0 +1,24 @@ +package org.seckill.dao; + +import org.apache.ibatis.annotations.*; +import org.seckill.entity.*; + +/** + * Created by pc on 2017/3/5. + */ +public interface SuccessKilledDao { + /** + * 插入购买明细,可过滤重复 + * @param seckillId + * @param userPhone + * @return + */ + int insertSuccessKilled(@Param("seckillId") long seckillId,@Param("userPhone") long userPhone); + + /** + * 根据id查询SuccessKilled,并携带秒杀产品对象实体 + * @param seckillId + * @return + */ + SuccessKilled queryByIdWithSeckill(@Param("seckillId") long seckillId,@Param("userPhone") long userPhone); +} diff --git a/seckill/src/main/java/org/seckill/dto/Exposer.java b/seckill/src/main/java/org/seckill/dto/Exposer.java new file mode 100644 index 0000000..65097f5 --- /dev/null +++ b/seckill/src/main/java/org/seckill/dto/Exposer.java @@ -0,0 +1,99 @@ +package org.seckill.dto; + +/** + * 暴露秒杀地址DTO:与业务无关实体 + * Created by pc on 2017/3/6. + */ +public class Exposer { + //是否开启秒杀 + private boolean exposed; + //一种加密措施 + private String md5; + //id + private long seckillId; + //系统当前时间 + 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(long seckillId,boolean exposed, long now, long start, long end) { + this.seckillId=seckillId; + this.exposed = exposed; + this.now = now; + this.start = start; + this.end = end; + } + + public Exposer(long seckillId, boolean exposed) { + this.seckillId = seckillId; + this.exposed = exposed; + } + + public boolean isExposed() { + return exposed; + } + + public void setExposed(boolean exposed) { + this.exposed = exposed; + } + + public String getMd5() { + return md5; + } + + public void setMd5(String md5) { + this.md5 = md5; + } + + public long getSeckillId() { + return seckillId; + } + + public void setSeckillId(long seckillId) { + this.seckillId = seckillId; + } + + 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; + } + + @Override + public String toString() { + return "Exposer{" + + "exposed=" + exposed + + ", md5='" + md5 + '\'' + + ", seckillId=" + seckillId + + ", now=" + now + + ", start=" + start + + ", end=" + end + + '}'; + } +} diff --git a/seckill/src/main/java/org/seckill/dto/SeckillExecution.java b/seckill/src/main/java/org/seckill/dto/SeckillExecution.java new file mode 100644 index 0000000..274385c --- /dev/null +++ b/seckill/src/main/java/org/seckill/dto/SeckillExecution.java @@ -0,0 +1,73 @@ +package org.seckill.dto; + +import org.seckill.entity.*; +import org.seckill.enums.*; + +/** + * 封装秒杀执行后结果 + * Created by pc on 2017/3/6. + */ +public class SeckillExecution { + private long seckillId; + //秒杀执行结果状态 + private int state; + //状态表示 + private String stateInfo; + //秒杀成功对象 + private SuccessKilled successKilled; + //成功 + public SeckillExecution(long seckillId, SeckillStateEnum stateEnum, SuccessKilled successKilled) { + this.seckillId = seckillId; + this.state = stateEnum.getState(); + this.stateInfo = stateEnum.getStateInfo(); + this.successKilled = successKilled; + } + //失败 + public SeckillExecution(long seckillId, SeckillStateEnum stateEnum) { + this.seckillId = seckillId; + this.state = stateEnum.getState(); + this.stateInfo = stateEnum.getStateInfo(); + } + + 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; + } + + @Override + public String toString() { + return "SeckillExecution{" + + "seckillId=" + seckillId + + ", state=" + state + + ", stateInfo='" + stateInfo + '\'' + + ", successKilled=" + successKilled + + '}'; + } +} diff --git a/seckill/src/main/java/org/seckill/dto/SeckillResult.java b/seckill/src/main/java/org/seckill/dto/SeckillResult.java new file mode 100644 index 0000000..de40710 --- /dev/null +++ b/seckill/src/main/java/org/seckill/dto/SeckillResult.java @@ -0,0 +1,45 @@ +package org.seckill.dto; + +/** + * 所有Ajax请求返回类型 + * Created by pc on 2017/3/7. + */ +public class SeckillResult { + private boolean success; + private T data; + private String error; + + public SeckillResult( boolean success,T data) { + this.data = data; + this.success = success; + } + + public SeckillResult(boolean success, String error) { + this.success = success; + this.error = error; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/seckill/src/main/java/org/seckill/entity/Seckill.java b/seckill/src/main/java/org/seckill/entity/Seckill.java new file mode 100644 index 0000000..42961b3 --- /dev/null +++ b/seckill/src/main/java/org/seckill/entity/Seckill.java @@ -0,0 +1,77 @@ +package org.seckill.entity; + +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +public class Seckill { + + private long seckillId; + private String name; + private int number; + private Date createTime; + private Date startTime; + private Date endTime; + + + public long getSeckillId() { + return seckillId; + } + + public void setSeckillId(long seckillId) { + this.seckillId = seckillId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + @Override + public String toString() { + return "Seckill{" + + "seckillId=" + seckillId + + ", name='" + name + '\'' + + ", number=" + number + + ", createTime=" + createTime + + ", startTime=" + startTime + + ", endTime=" + endTime + + '}'; + } +} diff --git a/seckill/src/main/java/org/seckill/entity/SuccessKilled.java b/seckill/src/main/java/org/seckill/entity/SuccessKilled.java new file mode 100644 index 0000000..1b9e352 --- /dev/null +++ b/seckill/src/main/java/org/seckill/entity/SuccessKilled.java @@ -0,0 +1,66 @@ +package org.seckill.entity; + +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +public class SuccessKilled { + private long seckillid; + private long userPhone; + private Date createTime; + private short state; + //多对一 + private Seckill seckill; + + + public short getState() { + return state; + } + + public void setState(short state) { + this.state = state; + } + + public long getSeckillid() { + return seckillid; + } + + public void setSeckillid(long seckillid) { + this.seckillid = seckillid; + } + + public long getUserPhone() { + return userPhone; + } + + public void setUserPhone(long userPhone) { + this.userPhone = userPhone; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Seckill getSeckill() { + return seckill; + } + + public void setSeckill(Seckill seckill) { + this.seckill = seckill; + } + + @Override + public String toString() { + return "SuccessKilled{" + + "seckillid=" + seckillid + + ", userPhone=" + userPhone + + ", createTime=" + createTime + + ", state=" + state + + '}'; + } +} diff --git a/seckill/src/main/java/org/seckill/enums/SeckillStateEnum.java b/seckill/src/main/java/org/seckill/enums/SeckillStateEnum.java new file mode 100644 index 0000000..49ccf08 --- /dev/null +++ b/seckill/src/main/java/org/seckill/enums/SeckillStateEnum.java @@ -0,0 +1,40 @@ +package org.seckill.enums; + +/** + * 使用枚举表述常量数据字段 + * Created by pc on 2017/3/6. + */ +public enum SeckillStateEnum { + + SUCCESS(1,"秒杀成功"), + END(0,"秒杀结束"), + REPEAT_KILL(-1,"重复秒杀"), + INNER_ERROR(-2,"系统异常"), + DATA_REWRITE(-3,"数据篡改"); + + + private int state; + private String stateInfo; + + SeckillStateEnum(int state, String stateInfo) { + this.state = state; + this.stateInfo = stateInfo; + } + + public int getState() { + return state; + } + + public String getStateInfo() { + return stateInfo; + } + + public static SeckillStateEnum stateOf(int index){ + for(SeckillStateEnum state:values()){ + if(state.getState()==index){ + return state; + } + } + return null; + } +} diff --git a/seckill/src/main/java/org/seckill/exception/RepeatKillException.java b/seckill/src/main/java/org/seckill/exception/RepeatKillException.java new file mode 100644 index 0000000..c3656a0 --- /dev/null +++ b/seckill/src/main/java/org/seckill/exception/RepeatKillException.java @@ -0,0 +1,17 @@ +package org.seckill.exception; + + +/** + * 重复秒杀异常(运行期异常),Spring声明式事务只支持运行时异常 + * Created by pc on 2017/3/6. + */ +public class RepeatKillException extends SeckillException{ + public RepeatKillException(String message){ + super(message); + } + + public RepeatKillException(String message,Throwable cause){ + super(message,cause); + } + +} diff --git a/seckill/src/main/java/org/seckill/exception/SeckillCloseException.java b/seckill/src/main/java/org/seckill/exception/SeckillCloseException.java new file mode 100644 index 0000000..04b70c0 --- /dev/null +++ b/seckill/src/main/java/org/seckill/exception/SeckillCloseException.java @@ -0,0 +1,16 @@ +package org.seckill.exception; + + +/** + * 秒杀关闭异常 + * Created by pc on 2017/3/6. + */ +public class SeckillCloseException extends SeckillException { + public SeckillCloseException(String message) { + super(message); + } + + public SeckillCloseException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/seckill/src/main/java/org/seckill/exception/SeckillException.java b/seckill/src/main/java/org/seckill/exception/SeckillException.java new file mode 100644 index 0000000..cb71ddc --- /dev/null +++ b/seckill/src/main/java/org/seckill/exception/SeckillException.java @@ -0,0 +1,15 @@ +package org.seckill.exception; + +/** + * 秒杀相关异常 + * Created by pc on 2017/3/6. + */ +public class SeckillException extends RuntimeException { + public SeckillException(String message) { + super(message); + } + + public SeckillException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/seckill/src/main/java/org/seckill/service/SeckillService.java b/seckill/src/main/java/org/seckill/service/SeckillService.java new file mode 100644 index 0000000..45d17f8 --- /dev/null +++ b/seckill/src/main/java/org/seckill/service/SeckillService.java @@ -0,0 +1,43 @@ +package org.seckill.service; + +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.exception.*; + +import java.util.*; + +/**业务接口:站在使用者(程序员)的角度设计接口 + * 三个方面:1.方法定义粒度,方法定义的要非常清楚2.参数,要越简练越好 + * 3.返回类型(return 类型一定要友好/或者return异常,我们允许的异常) + */ +public interface SeckillService { + + /** + * 查询全部的秒杀记录 + * @return + */ + List getSeckillList(); + + /** + * 查询单个秒杀记录 + * @param seckillId + * @return + */ + Seckill getById(long seckillId); + + /** + * 秒杀开始时输出秒杀接口地址, + * 否则输出系统时间和秒杀时间 + * @param seckillId + */ + Exposer exposeSeckillUrl(long seckillId); + + /** + * 执行秒杀 + * @param seckillId + * @param userPhone + * @param md5 + */ + SeckillExecution executeSeckill(long seckillId,long userPhone,String md5) + throws SeckillException,RepeatKillException,SeckillCloseException; +} diff --git a/seckill/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java b/seckill/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java new file mode 100644 index 0000000..28d714a --- /dev/null +++ b/seckill/src/main/java/org/seckill/service/impl/SeckillServiceImpl.java @@ -0,0 +1,146 @@ +package org.seckill.service.impl; + +import org.apache.commons.collections.*; +import org.seckill.dao.*; +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.enums.*; +import org.seckill.exception.*; +import org.seckill.service.*; +import org.slf4j.*; +import org.springframework.stereotype.*; +import org.springframework.transaction.annotation.*; +import org.springframework.util.*; + +import javax.annotation.*; +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +@Service +public class SeckillServiceImpl implements SeckillService { + + private Logger logger=LoggerFactory.getLogger(this.getClass()); + @Resource + private SeckillDao seckillDao; + @Resource + private SuccessKilledDao successKilledDao; + @Resource + private RedisDao redisDao; + + //md5盐值,用于混淆md5 + private final String sault="abjkdkddk*234*fdjfkslsdfj&-=fdfdk"; + + public List getSeckillList() { + + return seckillDao.queryAll(0,4); + } + + public Seckill getById(long seckillId) { + + return seckillDao.queryById(seckillId); + } + + public Exposer exposeSeckillUrl(long seckillId) { + //优化点:缓存优化 + //1.访问redis + Seckill seckill = redisDao.getSeckill(seckillId); + if(seckill==null){ + //2.访问数据库 + seckill=seckillDao.queryById(seckillId); + if(seckill==null){ + return new Exposer(seckillId, false); + }else{ + //3.放入redis + redisDao.putSeckill(seckill); + } + + + } + + Date startTime=seckill.getStartTime(); + Date endTime=seckill.getEndTime(); + Date nowTime=new Date(); + + if (nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()) { + return new Exposer(seckillId, false, nowTime.getTime(), startTime.getTime(), endTime.getTime()); + } + //转换特定字符串的过程,不可逆 + String md5=getMD5(seckillId);//TODO + return new Exposer(true,md5,seckillId); + } + + private String getMD5(long seckillId){ + String base=seckillId+"/"+sault; + String md5= DigestUtils.md5DigestAsHex(base.getBytes()); + return md5; + } + + @Transactional + /** + * 使用注解控制事务方法的优点 + * 1.开发团队达成一致约定,明确标注事务方法的编程风格 + * 2.保证事务方法执行时间尽可能短(降低锁定时间),不要穿插其他网络操作RPC/HTTP或者剥离到方法外部 + * 3.不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制 + */ + public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException { + if(md5==null || !md5.equals(getMD5(seckillId))){ + return new SeckillExecution(seckillId,SeckillStateEnum.DATA_REWRITE); + } + //使用存储过程执行秒杀 + Map map=new HashMap(); + map.put("seckillId", seckillId); + map.put("userPhone", userPhone); + map.put("killTime", new Date()); + map.put("result", null); + try { + seckillDao.killByProcedure(map); + //获取result + int result=MapUtils.getInteger(map,"result",-2); + if(result==1){ + SuccessKilled successKilled = successKilledDao. + queryByIdWithSeckill(seckillId,userPhone); + return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled); + }else{ + return new SeckillExecution(seckillId, SeckillStateEnum.stateOf(result)); + } + }catch (Exception e){ + logger.error(e.getMessage(),e); + return new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); + } + +// //执行秒杀逻辑:减库存+记录购买行为 + // +// //减库存 +// try{ +// //记录购买行为 +// int insertCount=successKilledDao.insertSuccessKilled(seckillId,userPhone); +// if(insertCount<=0){ +// //重复秒杀 +// throw new RepeatKillException("seckill repeated"); +// }else{ +// //减库存,热点商品竞争,*先插入购买明细,再减库存,可以缩短行级锁时间 +// int updateCount=seckillDao.reduceNumber(seckillId,new Date()); +// if(updateCount<=0){ +// //没有更新到记录,秒杀结束 +// throw new SeckillCloseException("seckill is closed"); +// }else{ +// SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); +// return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS,successKilled); +// } +// +// } +// }catch (SeckillCloseException e){ +// throw e; +// }catch (RepeatKillException e){ +// throw e; +// }catch(Exception e){ +// logger.error(e.getMessage(),e); +// //所有编译期异常,转换为运行期异常 +// throw new SeckillException("seckill inner error:"+e.getMessage()); +// } + } + + +} diff --git a/seckill/src/main/java/org/seckill/web/SeckillController.java b/seckill/src/main/java/org/seckill/web/SeckillController.java new file mode 100644 index 0000000..7090fe1 --- /dev/null +++ b/seckill/src/main/java/org/seckill/web/SeckillController.java @@ -0,0 +1,95 @@ +package org.seckill.web; + +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.enums.*; +import org.seckill.exception.*; +import org.seckill.service.*; +import org.slf4j.*; +import org.springframework.stereotype.*; +import org.springframework.ui.*; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.*; +import java.util.*; + +/** + * Created by pc on 2017/3/7. + */ +@Controller +@RequestMapping("/seckill") +public class SeckillController { + private Logger logger=LoggerFactory.getLogger(this.getClass()); + @Resource + private SeckillService seckillService; + + @RequestMapping(value = "/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 "forward:/seckill/list"; + } + model.addAttribute("seckill",seckill); + return "detail"; + } +//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){ + logger.error(e.getMessage(),e); + result =new SeckillResult(false,e.getMessage()); + } + return result; + } + + @RequestMapping(value = "{seckillId}/{md5}/execution") + @ResponseBody + public SeckillResult execute(@PathVariable("seckillId") Long seckillId, + @PathVariable("md5") String md5, + @CookieValue(value = "userPhone", required = false) Long userPhone) { + if(userPhone==null){ + return new SeckillResult(false, "未登录"); + } + SeckillResult result=null; + try{ + SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5); + return new SeckillResult(true,execution); + }catch(SeckillCloseException e){ + SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.END); + return new SeckillResult(true,seckillExecution); + }catch(RepeatKillException e){ + SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL); + return new SeckillResult(true,seckillExecution); + } + catch (Exception e){ + logger.error(e.getMessage(),e); + SeckillExecution seckillExecution=new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); + result = new SeckillResult(true,seckillExecution); + return result; + } + } + + @RequestMapping(value = "/time/now",method = RequestMethod.GET) + @ResponseBody + public SeckillResult time(){ + Date date=new Date(); + return new SeckillResult(true,date.getTime()); + } +} diff --git a/seckill/src/main/resources/jdbc.properties b/seckill/src/main/resources/jdbc.properties new file mode 100644 index 0000000..309adda --- /dev/null +++ b/seckill/src/main/resources/jdbc.properties @@ -0,0 +1,5 @@ +driver=com.mysql.jdbc.Driver +url=jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf-8 +jdbc.user=root +password=123456 + diff --git a/seckill/src/main/resources/logback.xml b/seckill/src/main/resources/logback.xml new file mode 100644 index 0000000..8480b2b --- /dev/null +++ b/seckill/src/main/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/resources/mapper/SeckillDao.xml b/seckill/src/main/resources/mapper/SeckillDao.xml new file mode 100644 index 0000000..e953055 --- /dev/null +++ b/seckill/src/main/resources/mapper/SeckillDao.xml @@ -0,0 +1,35 @@ + + + + + + UPDATE seckill SET number=number-1 + WHERE seckill_id=#{seckillId} + AND start_time <= #{killTime} + AND end_time >=#{killTime} + AND number>0 + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/resources/mapper/SuccessKilledDao.xml b/seckill/src/main/resources/mapper/SuccessKilledDao.xml new file mode 100644 index 0000000..2e59d0a --- /dev/null +++ b/seckill/src/main/resources/mapper/SuccessKilledDao.xml @@ -0,0 +1,31 @@ + + + + + INSERT ignore INTO success_killed(seckill_id,user_phone,state) + VALUES (#{seckillId},#{userPhone},0) + + + + \ No newline at end of file diff --git a/seckill/src/main/resources/mybatis-config.xml b/seckill/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000..c641d88 --- /dev/null +++ b/seckill/src/main/resources/mybatis-config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/resources/spring/spring-dao.xml b/seckill/src/main/resources/spring/spring-dao.xml new file mode 100644 index 0000000..b565e04 --- /dev/null +++ b/seckill/src/main/resources/spring/spring-dao.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/resources/spring/spring-service.xml b/seckill/src/main/resources/spring/spring-service.xml new file mode 100644 index 0000000..c7cf20c --- /dev/null +++ b/seckill/src/main/resources/spring/spring-service.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/resources/spring/spring-web.xml b/seckill/src/main/resources/spring/spring-web.xml new file mode 100644 index 0000000..f9f3076 --- /dev/null +++ b/seckill/src/main/resources/spring/spring-web.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/sql/schema.sql b/seckill/src/main/sql/schema.sql new file mode 100644 index 0000000..4dee885 --- /dev/null +++ b/seckill/src/main/sql/schema.sql @@ -0,0 +1,38 @@ +-- 数据库初始化脚本 +-- 创建数据库 +DROP Database `seckill`; +CREATE DATABASE `seckill`; +-- 使用数据库 +USE `seckill`; +-- 创建表 + +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 DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '开始时间',/*第一个TIMESTAMP字段会自动更新,要加上default*/ + `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) +)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT ='秒杀明细表'; + +-- 初始化数据 +insert into seckill(name, number, start_time, end_time) + VALUES + ('1000元秒杀iphone6',100,'2017-03-05 00:00:00','2017-03-06 00:00:00'), + ('500元秒杀ipad2',200,'2017-03-05 00:00:00','2017-03-06 00:00:00'), + ('300元秒杀小米3',300,'2017-03-05 00:00:00','2017-03-06 00:00:00'), + ('200元秒杀红米note',400,'2017-03-05 00:00:00','2017-03-06 00:00:00'); + +-- 秒杀成功明细表 +-- 用户登录认证相关的信息 +CREATE TABLE `success_killed`( + `seckill_id` BIGINT NOT NULL COMMENT '秒杀商品id', + `user_phone` BIGINT NOT NULL COMMENT '用户手机号', + `state` TINYINT NOT NULL DEFAULT 0 COMMENT '状态标识:-1:无效 0:成功 1:已付款', + `create_time` TIMESTAMP NOT NULL COMMENT '创建时间', + PRIMARY KEY (seckill_id,user_phone), -- 联合主键 + KEY idx_create_time(create_time) +)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; diff --git a/seckill/src/main/sql/seckill.sql b/seckill/src/main/sql/seckill.sql new file mode 100644 index 0000000..b8170a0 --- /dev/null +++ b/seckill/src/main/sql/seckill.sql @@ -0,0 +1,56 @@ +-- 执行秒杀存储过程 +DELIMITER $$ +-- 定义存储过程 +-- 参数:in输入参数 out输出参数 +-- row_count()返回上一条修改语句sql(update,insert,delete)影响的行数 +-- row_count:0未修改行数,>0:表示影响的行数 <0:sql错误/未执行修改sql +-- 存储过程内部声明的变量不能是@开头 +CREATE PROCEDURE `seckill`.`execute_seckill`( + in v_seckill_id bigint,in v_seckill_phone BIGINT,in v_seckill_time TIMESTAMP, + out r_result int) + BEGIN + DECLARE insert_count int DEFAULT 0; + START TRANSACTION; + INSERT IGNORE INTO `success_killed`(seckill_id,user_phone,create_time) + values(v_seckill_id,v_seckill_phone,v_seckill_time); + SELECT row_count() INTO insert_count; + IF(insert_count = 0) THEN + ROLLBACK; + SET r_result=-1;-- 重复秒杀 + ELSEIF(insert_count<0) THEN + ROLLBACK; + SET r_result=-2; + ELSE + UPDATE seckill SET number=number-1 + WHERE seckill_id=v_seckill_id + AND seckill.start_time < v_seckill_time + AND seckill.end_time >v_seckill_time + AND seckill.number>0; + SELECT row_count() into insert_count; + IF (insert_count=0) THEN + ROLLBACK; + SET r_result=0; + ELSEIF (insert_count<0) THEN + ROLLBACK; + SET r_result=-2; + ELSE + COMMIT; + SET r_result=1; + END IF ; + END IF; + END; +$$ +-- 存储过程定义结束 +DELIMITER ; +SET @r_result=-3; +-- 执行存储过程 +CALL seckill.execute_seckill(1003, 18826077111, now(), @r_result); +-- 获取结果 +SELECT @r_result; + + +-- 存储过程 +-- 存储过程优化:事务行级锁持有的时间 +-- 不要过度依赖存储过程 +-- 简单的逻辑可以应用存储过程 +-- QPS:一个秒杀单6000/QPS \ No newline at end of file diff --git a/seckill/src/main/webapp/WEB-INF/jsp/common/head.jsp b/seckill/src/main/webapp/WEB-INF/jsp/common/head.jsp new file mode 100644 index 0000000..da039a1 --- /dev/null +++ b/seckill/src/main/webapp/WEB-INF/jsp/common/head.jsp @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/webapp/WEB-INF/jsp/common/tag.jsp b/seckill/src/main/webapp/WEB-INF/jsp/common/tag.jsp new file mode 100644 index 0000000..9b0e158 --- /dev/null +++ b/seckill/src/main/webapp/WEB-INF/jsp/common/tag.jsp @@ -0,0 +1,3 @@ +<%@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" %> diff --git a/seckill/src/main/webapp/WEB-INF/jsp/detail.jsp b/seckill/src/main/webapp/WEB-INF/jsp/detail.jsp new file mode 100644 index 0000000..9f503e9 --- /dev/null +++ b/seckill/src/main/webapp/WEB-INF/jsp/detail.jsp @@ -0,0 +1,83 @@ +<%@page contentType="text/html; charset=UTF-8" language="java" %> +<%@include file="common/tag.jsp" %> + + + + 秒杀详情页 + <%@include file="common/head.jsp" %> + + +
+
+
+

${seckill.name}

+
+ +
+

+ <%--显示time图标--%> + + <%--展示倒计时--%> + +

+
+
+
+<%--登录弹出层 输入电话--%> + + + +<%--jQery文件,务必在bootstrap.min.js之前引入--%> + + +<%--使用CDN 获取公共js http://www.bootcdn.cn/--%> +<%--jQuery Cookie操作插件--%> + +<%--jQuery countDown倒计时插件--%> + + + + + + \ No newline at end of file diff --git a/seckill/src/main/webapp/WEB-INF/jsp/list.jsp b/seckill/src/main/webapp/WEB-INF/jsp/list.jsp new file mode 100644 index 0000000..70807aa --- /dev/null +++ b/seckill/src/main/webapp/WEB-INF/jsp/list.jsp @@ -0,0 +1,59 @@ +<%@page contentType="text/html; charset=UTF-8" language="java" %> +<%@include file="common/tag.jsp"%> + + + + 秒杀商品列表 + <%@include file="common/head.jsp" %> + + +
+
+
+

秒杀列表

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
名称库存开始时间结束时间创建时间详情页
${sk.name}${sk.number} + + + + + + 详情
+ +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/seckill/src/main/webapp/WEB-INF/web.xml b/seckill/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..e63cfe1 --- /dev/null +++ b/seckill/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + + seckill-dispatcher + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + classpath:spring/spring-*.xml + + + + seckill-dispatcher + + / + + \ No newline at end of file diff --git a/seckill/src/main/webapp/index.jsp b/seckill/src/main/webapp/index.jsp new file mode 100644 index 0000000..c38169b --- /dev/null +++ b/seckill/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

Hello World!

+ + diff --git a/seckill/src/main/webapp/resource/script/seckill.js b/seckill/src/main/webapp/resource/script/seckill.js new file mode 100644 index 0000000..9a18015 --- /dev/null +++ b/seckill/src/main/webapp/resource/script/seckill.js @@ -0,0 +1,149 @@ +//存放主要交互逻辑的js代码 +// javascript 模块化(package.类.方法) + +var seckill = { + + //封装秒杀相关ajax的url + URL: { + now: function () { + return '/seckill/time/now'; + }, + exposer: function (seckillId) { + return '/seckill/' + seckillId + '/exposer'; + }, + execution: function (seckillId, md5) { + return '/seckill/' + seckillId + '/' + md5 + '/execution'; + } + }, + + //验证手机号 + validatePhone: function (phone) { + if (phone && phone.length == 11 && !isNaN(phone)) { + return true;//直接判断对象会看对象是否为空,空就是undefine就是false; isNaN 非数字返回true + } else { + return false; + } + }, + + //详情页秒杀逻辑 + detail: { + //详情页初始化 + init: function (params) { + //手机验证和登录,计时交互 + //规划我们的交互流程 + //在cookie中查找手机号 + var userPhone = $.cookie('userPhone'); + //验证手机号 + if (!seckill.validatePhone(userPhone)) { + //绑定手机 控制输出 + var killPhoneModal = $('#killPhoneModal'); + killPhoneModal.modal({ + show: true,//显示弹出层 + backdrop: 'static',//禁止位置关闭 + keyboard: false//关闭键盘事件 + }); + + $('#killPhoneBtn').click(function () { + var inputPhone = $('#killPhoneKey').val(); + console.log("inputPhone: " + inputPhone); + if (seckill.validatePhone(inputPhone)) { + //电话写入cookie(7天过期) + $.cookie('userPhone', inputPhone, {expires: 7, path: '/seckill'}); + //验证通过  刷新页面 + window.location.reload(); + } else { + //todo 错误文案信息抽取到前端字典里 + $('#killPhoneMessage').hide().html('').show(300); + } + }); + } + + //已经登录 + //计时交互 + var startTime = params['startTime']; + var endTime = params['endTime']; + var seckillId = params['seckillId']; + $.get(seckill.URL.now(), {}, function (result) { + if (result && result['success']) { + var nowTime = result['data']; + //时间判断 计时交互 + seckill.countDown(seckillId, nowTime, startTime, endTime); + } else { + console.log('result: ' + result); + alert('result: ' + result); + } + }); + } + }, + + handlerSeckill: function (seckillId, node) { + //获取秒杀地址,控制显示器,执行秒杀 + node.hide().html(''); + + $.get(seckill.URL.exposer(seckillId), {}, function (result) { + //在回调函数种执行交互流程 + if (result && result['success']) { + var exposer = result['data']; + if (exposer['exposed']) { + //开启秒杀 + //获取秒杀地址 + var md5 = exposer['md5']; + var killUrl = seckill.URL.execution(seckillId, md5); + console.log("killUrl: " + killUrl); + //绑定一次点击事件 + $('#killBtn').one('click', function () { + //执行秒杀请求 + //1.先禁用按钮 + $(this).addClass('disabled');//,<-$(this)===('#killBtn')-> + //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); + } + }); + + }, + + 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);//todo 防止时间偏移 + seckillBox.countdown(killTime, function (event) { + //时间格式 + var format = event.strftime('秒杀倒计时: %D天 %H时 %M分 %S秒 '); + seckillBox.html(format); + }).on('finish.countdown', function () { + //时间完成后回调事件 + //获取秒杀地址,控制现实逻辑,执行秒杀 + console.log('______fininsh.countdown'); + seckill.handlerSeckill(seckillId, seckillBox); + }); + } else { + //秒杀开始 + seckill.handlerSeckill(seckillId, seckillBox); + } + } + +} \ No newline at end of file diff --git a/seckill/src/test/java/org/seckill/dao/RedisDaoTest.java b/seckill/src/test/java/org/seckill/dao/RedisDaoTest.java new file mode 100644 index 0000000..60dd432 --- /dev/null +++ b/seckill/src/test/java/org/seckill/dao/RedisDaoTest.java @@ -0,0 +1,41 @@ +package org.seckill.dao; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.entity.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; + +import static org.junit.Assert.*; + +/** + * Created by pc on 2017/3/8. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:spring/spring-dao.xml") +public class RedisDaoTest { + private long id=1001L; + + @Resource + private RedisDao redisDao; + @Resource + private SeckillDao seckillDao; + + + @Test + public void testSeckill() throws Exception { + Seckill seckill=redisDao.getSeckill(id); + if(seckill==null){ + seckill = seckillDao.queryById(id); + if(seckill!=null){ + String result = redisDao.putSeckill(seckill); + System.out.println(result); + seckill = redisDao.getSeckill(id); + System.out.println(seckill); + } + } + } + +} \ No newline at end of file diff --git a/seckill/src/test/java/org/seckill/dao/SeckillDaoTest.java b/seckill/src/test/java/org/seckill/dao/SeckillDaoTest.java new file mode 100644 index 0000000..3eb2797 --- /dev/null +++ b/seckill/src/test/java/org/seckill/dao/SeckillDaoTest.java @@ -0,0 +1,46 @@ +package org.seckill.dao; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.entity.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; +import java.util.*; + +/** + * Created by pc on 2017/3/5. + */ +//配置spring和junit整合,这样junit在启动时就会加载spring容器 +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:spring/spring-dao.xml") +public class SeckillDaoTest { + //注入Dao实现类依赖 + @Resource + private SeckillDao seckillDao; + + @Test + public void queryById(){ + long seckillId=1001; + Seckill seckill=seckillDao.queryById(seckillId); + System.out.println(seckill.getName()); + System.out.println(seckill); + } + + @Test + public void queryAll(){ + List seckillList=seckillDao.queryAll(0,100); + for(Seckill seckill:seckillList){ + System.out.println(seckill); + } + } + + @Test + public void reduceNumber(){ + long id=1000; + int updateCount=seckillDao.reduceNumber(1000, new Date()); + System.out.println(updateCount); + } +} + diff --git a/seckill/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java b/seckill/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java new file mode 100644 index 0000000..7026c95 --- /dev/null +++ b/seckill/src/test/java/org/seckill/dao/SuccessKilledDaoTest.java @@ -0,0 +1,36 @@ +package org.seckill.dao; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.entity.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; + +/** + * Created by pc on 2017/3/5. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:spring/spring-dao.xml") +public class SuccessKilledDaoTest { + @Resource + private SuccessKilledDao successKilledDao; + + @Test + public void insertSuccessKilled(){ + long seckillId=1000L; + long userPhone=18826077187L; + int updateCount=successKilledDao.insertSuccessKilled(seckillId,userPhone); + System.out.println(updateCount); + } + + @Test + public void queryByIdWithSeckill(){ + + SuccessKilled successKilled=successKilledDao.queryByIdWithSeckill(1000L,18826077187L); + System.out.println(successKilled); + System.out.println(successKilled.getSeckill()); + + } +} diff --git a/seckill/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java b/seckill/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java new file mode 100644 index 0000000..811beb3 --- /dev/null +++ b/seckill/src/test/java/org/seckill/service/impl/SeckillServiceImplTest.java @@ -0,0 +1,81 @@ +package org.seckill.service.impl; + +import org.junit.*; +import org.junit.runner.*; +import org.seckill.dto.*; +import org.seckill.entity.*; +import org.seckill.exception.*; +import org.seckill.service.*; +import org.slf4j.*; +import org.springframework.test.context.*; +import org.springframework.test.context.junit4.*; + +import javax.annotation.*; + +import java.util.*; + +import static org.junit.Assert.*; + +/** + * Created by pc on 2017/3/6. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration({ + "classpath:spring/spring-dao.xml", + "classpath:spring/spring-service.xml" +}) +public class SeckillServiceImplTest { + Logger logger=LoggerFactory.getLogger(this.getClass()); + @Resource + private SeckillService seckillService; + + @Test + public void testGetSeckillList() throws Exception { + List seckillList=seckillService.getSeckillList(); + logger.info("list={}",seckillList); + } + + @Test + public void testGetById() throws Exception { + Seckill seckill=seckillService.getById(1000L); + logger.info("seckill={}",seckill); + + } + //集成测试代码完整逻辑,注意可重复执行 + @Test + public void testSeckillLogic() throws Exception { + //exposer=Exposer{exposed=true, md5='ae8c840b6c3a4891c593ec70b9dc2c06', + // seckillId=1000, now=0, start=0, end=0} + long id=1001L; + Exposer exposer=seckillService.exposeSeckillUrl(id); + logger.info("exposer={}",exposer); + if(exposer!=null){ + //用try-catch包围才能单元测试通过,否则抛出异常视为不通过 + try{ + long userPhone=18826077189L; + SeckillExecution seckillExecution=seckillService.executeSeckill(id, userPhone, exposer.getMd5()); + logger.info("seckillExecution={}",seckillExecution); + }catch(RepeatKillException e){ + logger.error(e.getMessage()); + }catch (SeckillCloseException e){ + logger.error(e.getMessage()); + }catch (SeckillException e){ + logger.error(e.getMessage()); + } + }else{ + logger.warn("exposer={}",exposer); + } + } + + @Test + public void testProcedure(){ + long seckillId=1001L; + long userPhone=18826077188L; + Exposer exposer = seckillService.exposeSeckillUrl(seckillId); + if(exposer.isExposed()){ + String md5=exposer.getMd5(); + SeckillExecution execution=seckillService.executeSeckill(seckillId, userPhone, md5); + logger.info(execution.getStateInfo()); + } + } +} \ No newline at end of file diff --git "a/seckill/\346\236\266\346\236\204\345\233\276.jpg" "b/seckill/\346\236\266\346\236\204\345\233\276.jpg" new file mode 100644 index 0000000000000000000000000000000000000000..ae3c9254863812ab19efc9c1cb951b9514eade93 GIT binary patch literal 59708 zcmeFZ2Ut_f_9(s)1qBrgs1%J-qzQw=v z1$p^T^8=(FKnmEoZ{NXv2M>MyVK~CT$jHb5|G2 zY5iyy)uF>j=o#2fvU6~92@0J(CoCc=bxHcNjI5lzn!1Lj)-`P%!&|rS7#W+G+SuCJ zJKT44a)0dc#PjJhw0}TgP;ki0(6H#3*toax35iJ=nOWI6xq0~oAIi!LBx(hdXI6`n^eizl%jc^$Xoi>n5s0tb(7}7B;c|iuAAH{OErR>ECev4P{a( zuz%;aPdi{qFaz861LXSwQa7-hoDBX4entXo8ywMYRR8__mk0mVf&c2je|6x$I`IEH z9Z2bUm1lm~*8BPL(QCvV;^p1CVd`3;)tFofz2h`mVvrQUG-P$QagCYqiUgq3d~Zus zvS6;-fi~iI(2JBu>0+&L0Jr$8*UAy#2el7OK^JiVjK&_=l!G~8~CISKXULnEjF za6uik=mV|!sHi#6Y|w9@us#34z)eBIM1i?!*dHv0MeX@VqW*;*-^IkYy7EIa zzso@CkL7gYtM>kNpKlue`#vKbZxODn$T@#xgjq&bYgFN9IW1`T9=NyiOUkICN*_Z- z3R~v6iD-2B#J{qQJdjs7tAG8Zu$#kCr7xKENYnfzea8EWEl(^1ot+|U(zBfDx_MdY zS;O5{5&atxZKswwKX6GjJ0*9?6p{cIVdj1@lIGzsvFB>_|` zIvnYswhL#A0)agUygh|l>VV&ngJ`!r(@kZmT;FKL`bR?KBw+g@W~NvHav}jECdd!I zTb_$G(WekPA&Y7xpcEX8&>)5+f{iGGH~YHE&quAKCTaI+RY)FOry(4y!EQwcx%D3y zvv)40sA?q=Kt=){IclvM#E}4Ta8WSwv&@!fgJkJLG^q+WP7*MP{G!AK)wW{CG0^1z z0>{rs?l$A-_bZ7Y8#kf1hz;RS!Q{Fg~r)Ta-!TGChU-6>>iOibN#j z4zTgg(8;JeQN7q#x=)hwg^ZN6=St`gMnrBQ{&$Q3U%pzkU`!9sA3v6tnDDOT{{mC~ zhqcM%ge0pQC2Pc^t&$7gqUOI>dHI^bGcQV)Szeg5{!S$@Th!opDxn_tOB(j50L%6M zoz?qBiP!2`zZ?1AQnqU7|CO^;=3kzri=fAUc$PNZPWo^rxF9eC=6RcyTRD~;m3hbD5n3!L4nWiSPtypP-d;5W%ERXie ze7+*nQsuC_HG3(c6(ghYjB*sV;3flLV;y#VeqC8$B8hw4a`yZ@4i`cKj>2{ynKh3c zA5$VY5ID#u!ZsTf;KqQjjYB5jk5&@!EQ%(Y0~3zT=KIzk{w5A;DIo!oq2R&*bn+qM zk_t-f8>~nemYQOs3kg7;0f(`KtANWvX?2ga=*SzIle^=rM0?5?&q>p7;I$q%3iMXp zvhfFxi&MVCD0l2SRO(dF;olJ_ZDLbdF1KX8oLwFl`?kYmd~dXWup5<*JO>%fG(ess zE^7PcH;2$1Q9Ir?H(j8Xrd1}BzDlogPv0RyEX{trM?BdiDl5QD1{s{9sHv=Qq5||x zm<@Z?8H?$eCjkPH(S-`S_nGC6$qo5a5_^vNFglofwmN*gNeqKmH$JQ3ei1H_kVtr! zRaGMMwi{PTsMF!9OGwXGw>myN2DpI|W>ML>9>581fR?1OOAB7-Z-w-P(%V~mIPPLSA2&d z8EQE>Y18XGv?>>yw+tIr#mpPkcKY8O0w6#wC|2z5t^bKs<*C=nJ|&7+4U|O&CL?E$-ZCt zEFHZ^X!2dd#yJEV9{Ti<1ca== zt0JvEwgX20FF#T&IOF=tnqA`$OIH$;=#MZ9?2`rjfohh?{)eeeqi^kQ`;YW_mNsqnJYCP@xm;ayd|fd3UIt5W(z<^puT zG)lDp{xf{twkr1l>L)3U(f(uJ`T8qO923|eh2U=c;@VZKT3fa|`Qi3;6WcO|frYlK z+U|8|;u`@uX54wmnuaaClWKg>IG_$zaW^kppZA7bmv>D$u{HDT^JKRe)nEkP1`2j;C?Ob|~(a=N-;FAe%6;Ut&^hJYYGh zL>=eSC*sODc?Sc!gP|P;78wBQ~0Tg~{)j8i^f)sn29 z@gc)th+b!x>WSCKIn>0c(Dx;bKlaUO-9K?Q$>Q$9r+_OBlZ#U!1(=v|(>_77xji>! zR4n#)ovVJN?$@dwb2ZOEbK|;81sIyt-9acI0Uf9>X=9OU#~i3^XJRly^Ryed7_@Go z7pEq7ktmY-%mN8;0d-p2j`}xW*5q>4+jr0POA^D_K!#0+(=dT3AO}XyG&s$8A@x#u zN2NJM)iU&2GW7+`bRB~PYxDO%KD^z3>KHrwdH3DJ$lArUX2Ykiedlr)(^O`lHbaVj zHrciq%LjV}<_S}6spH+LxAv5$C!g{f5EdIAzdpM#EC+__Fu=hNd(vox!{5mZw()N=os(wVCJA6B}><&uND zgK=VBPwURCLennDc$N7pt!H?7sG|IGku|c6B?cqzlKaOr4vV%)=XKZK>UN1t^rWk) zw0@c!TgEWzb(iY?=fhN@4nj66k4}cMJ%H8Eq}csj@E+v#=U?3; zb`87nH-1L<8Tm9!A$iYy?08VlE&JKZgNj0yKGwdN?S4NYLsPGyi`~Ie)q`-z@4|!7 zT`w|{9t!Y}EL9A57w($#PLLd_=anI3>UUO%F6pJlt`2F3rZ!yiyrE!!iV}Z~a!;gz zSzKX3*IjzD0p0^b$Dj1}TGOx`y($namw)lh^SI=;N#~xV85>da@!*u`U9TS0IdF|$ z9_}5_b;@T_aB~x5|DY8BIvM8@Q~z0a}M%~=FI81qML#ZZIk{= zQ;umu!3!4t1GDB!Z(RCy7JNF>-ga|F_l?!vY2Xi{_N_@6Oo)w1m90&ZCjqY247MuL zDy6U`0{-sf$OQ5xbq=p(fI2I0nv49v5w3xiHrOG;=okd;TwXxYBHtrgU1(J_9X*i= zru#c(@-wvaG>$fQyu(lx6b3I|T|DbT`6Xd_TX6%_s#l@@9KArDER$m)$=yi;y!q(` zvjWxjWo4-I8+FK#8u8vZmyPQyn1j z5aNQtRfEhlSXbYXfCpLuTFEk>aS_K@)+1SI1G9o!t*GUs*#J>Ayc8P8!fS)P4!kDD z#zpOjjShy#hZhIqXn!}}_so~O?w2P&r(^Xk-#^ftR%HLgc=bvx<09U({&2cDl6Vj`FvvZ|ZWGQW)XHuJ~|3OZz>!J@}% zW-(i7-^loE&SZjz?eWFi_VmmmItLz|6%WmPMDr#UP+E;uh+MJ9D|OVeb_m8pY`!D_ z#XB_wGE3%Zl{!Be!lDva~vD+1<*dzVL|qxj-m+`SlUQBgAP?zS)__Y3)pW zVN1-Uy>VRU3lTNO?ToLz-r{5uiEVOwy6ZmgA3o(oW?fDKCNT05bT3%Dbc<$&!LH26 z)w2tQM2Kr=(L^sY=)pR42!E0WF%yJLwJ9`PH??Euv!9&C=HWLc9B0c)d!=_heIj!5 z@&m{aU7vg)QSRuq>rX_ER^~EkIL*m~ue*{d$Vu~4OS91eS%HCBK%i=iF`0?U=CZBB z0mQ*F+J=S~a(Nwd9>5`tW@8fUMbvd9b_5$Er(vJ=9w=ZCZ*UdmO~A7By&(uK5k8p?Psi z_F5r_uLJc9MVBM{q|f0ckhn=J5&(|C;k_g!IYp*-I{Lgt19f;oBU#I{XU)w^yR9EI zTCQ=8!8^`l%0zf4v=o0?^4RT47RXr^yxQi}Op{USXVLf0P`JWzdF&CpaGtgfp?b2u zYwhkxPEEH*N3fqr&#?!+gH}C2>)~f-vU5Y3c8V>(_Y!Gv9@)#Cp`al*`tnfS8%d_? z=F9WiUT=^4>5oO|ds-*ws3QylQ|=~n<2d3n3m@bx_f`dSo@|X(4=lq6^_FgvACGEO zO4SLYb~%eg)(Vu2GREFbXI*7K?y8Y#Wq7pg**Q7Jhq<&l)aud zk|y&&jlG+_c3v=0)r9-p?LIKnH}y47rjuO2I$%1#T1WZV$3fyDa$}`K1-+Y2cCq4& z`-5D(X5R+WIX|mJSzl4d({E}jT$hMoqkJ1|j)rTL)@h)}+;P$295j3$hUnq_G&A+uuqT3Ks3Gm|q}F7essQr}8;4mOWoE=OZIMR0qO4^#+gj z>rC?g)`FU8(DUgzO~a~ zUBzlVeJQ3hk9Q%ghq?HD27R1t({eI3yEH$KSD&icN8WT{d80WUldYc;b0|8uZ4E!? zIQ?P32rFUhc`Icn?lcK-2*%k&zU6*PZNr3N)m%9HCB`xE(M2EO_m!Wu#@`(buFK#O z*%71vQ1@88#hmk!Zs|=ghK3BY+uZ$=eITD1O5JfbFyy8}>WhlrFsBNw-r9EXt zu=3jMxV+GBXqZtrs$8NSIAYe`5$DJZyBZqU>0~(YW{^|NwSRJ`gahozgJKdkr|5xn zUh_k@#i)AWrNgzjrN7rdDhNUmO~--W3rDLE;V$fg)Eturh7$g@XNnAC5?Ol0hFL74}nMSKbv%cYH_C8!7r#J`wAhIhH?~47bKnzFbPD6C_C8|y;qwI0OeVSEU zXaEDp>b^JhZ*6dgqh(ac%=l$Qs`HwEM4%(*#TEM>u~%1a4qx-v4Km>h0~V6Wq)x^I46!a0q+gcqfcZ+d$7U_WuwSNyvVd2RMTJ9P#wX)f@7dJxrIOwj^~!#n3R6A}=G_9zq= zjVz6u=BxV@jpk}R{cUY}H*e`*f5y>pP2CPjG^Kv77bd5lX&oRmBrtVOE#ejjvLKR! z1RT7E@4?r@`)|ZT!kGBe!B~isZ4!e+NZonf(a87;UyqAih_L}3u>z1_zPCgikpvJz zpk2!n#`O;t^07q&p(KFe+_|+^OUPlF(=G~l5`g0)9GrO26owoA{+@;9{JAl(!sq%% z$QlV)M6I)b!o>(JRg6JPu0{mo-hqBg*st?udcVR42LVQ~UzFY~T{au{t|yKW8rPeC zKoN1DLJg3BYH%=8YV4n}v!QGSu_VABjtxtA*3)AmzGHRyJD4$=1bEdW0c>!<+Jz=e z044SvuOD#g5GoJ}fE3^``UXN!>j(AmDgK-_h3+$z1mm%TrSK46HV2@VcHEJM5o6s& z6Ba=N?z_-{6~&tL{{Ea494oQys2m(< zP?MAs5%e#-Q-9K|e7rTy2>BptCb5*T5%!U{a{U#wfgDz;bLM|9M^II0K`lx=`DfBh zY#3Z*{+jW=f5Ujh-!LBeA7wo9?A6T5%UlN)*WLEBD-ze z&27A12`rNEB$&0`2xY%8VNHW0OwHTWC11A9?OM@2G!cz9Iu45mfInm-u+&dz!V?NZ z@OJ86#;|T(1TKn;X?>*GSVOehZTcM#H=|ltxb{e(0mZrb1|j?d`G$YCFsGox{U-hY7#uxwW6D3vLfw| z)gWqoMWIr}QXBRJ95TF4gPR(70h{_|9gfw2xw0-PoXR+|lN@r&9YKBw!2=-twuciB zt+U+PjhQ!ugr&$CGWb+RcM$=vC~nB{WLGHycaw2Uw^AX^ZJJS;@b5#$;K@L^@nZ2I zw0JE3-KsLA?%1&kz=tcTI4Y&z#xU@fKGy8g>C6FgiH(1Q<_WJzZrDxywl+?GAJ6ya zt6p1Q{RZtj!oMe17MHh4((*px90VMALs`Jd6qI zJHJtXLdQ3T|FPVD5b`^@nf{a;>KnS8$RFZ&68b4O%x?hYe#$N359Ib0gFoirp97}; zsuQq2ebdI@E8AC^W53gmxg70lA~7Q#hzvUB8n*j7p_!k^otm5Zr1J(Xjq6Ihv{XRw zOx_3nE>_sBmrVFRnKmcX-+jnNyT|)|p&dl-Pv*$sZp6k|JjV-;ySqnDd81b&|#_dN&|&LQm4;W3>wP_eT**0$qO zJ(Q!g)`M%6U418KBe#kQ)j@Nl_{MCyQatbXN2ot%vJ`HNGwGjP)HY}J)P#=nyHA)N zMMB8quwma|n2WFt9vf<~%s+8V8=5v}a(&?I6w{cfs+^FTqV5=5FmHyDs!hJRTA$KI z`_8E0%3aD3wm%R6Eh}jZSbn6teYxOc9g+8hOc zbCO<9aY`!V>I7JHo?$8*TWm=J^ct6A?~c#6D41{}^SPPC>Dt~KQkC-ZhWjzjF%!o~ zz{=`sva-&#e%V9xY*$=H9^CTP5L*`Blq|}br(1Erv8p0zE~)yk)r;+CI}XhYB}*7KjJOur5DM(0%eg+8h~MqQY|8aEUDLJh zvinD=&(x4{wwQqi=ke|9Xc z9amLYt_0I;mh12)=7^R}w4$brS309->pin4X(90b#rF*~2?Z(GconpPv?`Y#xczcVa9+Q~)? zn9qmpbGC$n#tLPkFqao-2D~=yO~cf)gXEuF)vr5I*|7n(I!d+9`w_Ds3+e)&JdOmw z`vBi65`Y+m|Cf|Cgf|INzY>S}SNn(+>fxKxj_8vm_q22s+dQ#J0h=t_Bnl~lYT>O%8*UemhDiGzggH+ zyQ^+5ybpgFPb>m;^wl6_&op9@E^O^ckx8pXd0ge2_rpsGX%<)O#%{im>nRmM@W{1C z;aVnVaIOxb;tr4mydy{h@%)K`nDy0S5@16DZkvuaV_& zvHl3FKmU6~d!!nl5uf1^U5(oH^UvS;nDDYJ=3SSvn2EAu@5wpeVdpAcUo>#%_)Ngs zdl|lm9Ru;h=%J`$v-<|ZpD0{2M&>Ht%|3*LcEt5`RTooOpRRE_O#-HsMOt3RH{PD> z2{M^?g2JB{aFwUA+TXtL(8u2Wg+!1+>^v;*haPqeGK2@Pt|%?h)pj_;Cq27T;eYhF!dq)KJ);6tZ zoCxpqB0uaXR&88>CpNieB_t6Av&Bnm#d4h(0KxmlD0KM~sol?x(|5j4pGHSa4fb7W z4x-X!B9CyVZLrXqbu6&f9k)q2(w1r;xsH2m1$D#bj|51mOoyAp#DzjZ)sg}|RT(En zmB!Ay&zN0YUBPJ6zc^Vcv;Fn>6zA)yz!*)Q2h^b$UW*7%spLoIv-GZGy;d!3mys9D z5OSJkRaMZ@a{QiHP(Y3%h9}EFts$g%xCX{&~06Bb9Up&6BfiO9vxPt1sV}zc8it zdbIwVcGz_+&*q0pYllgmdj5I4^QL~#JkT%=D1s$OLDxKqTv$f-A)E7)W|!~pvNux+4`M|Jdn128~#mLnci9 zFg2Uz=HLIwFBK9M7wgw$`)Dqgxn6BVjbAw83UBDO4>w`s9NWzM2jra83zB9V&W>Z- z)43k=oDB|scywNFLWKGFKgGVK1Ij?)_jbK_Zja%i z7{2<-+IBwTxi}-i^sM+booqfM+qlE`2QYrKf?9|7=2{FoK8z7x@5R0xUryzl)#by@ zAm`zHk0H}ED^U%WBDOWquSrF3el<0Ou!>l#B>@Y9plKJ_cmqCfg2NgIC66_LwS0sf z@KIQkmXPzUOpHAEPn}hMbe0!7Sn#}M+302MjZ;Ps{nRBI`PQyFWtyJd)BdB6vP-Ta z>`=KwI1oA+UA3ZlvOwnp^dN&sb9#{tnvAIm!M`m=&K5WjmcQe(^#w2Hl5Abb1sur` zE8Fbp%Z!=}TP6WHlLJeizyUrIP~NoKcOG)0!H<%F^{`X!(;C?E4oHEePV5<2V7v(e zqlC8zhn*2h;vyVXx{5Gw!H&a5s00OX4x_6izK<_*#FFoXx8W|vq%VaX!pC0kzZ$l< zdaKI~?ew&wm#8R5NPhR{!^B$(M0P8@dezBI0-hL!;Nb0uZS&XP9r461^!skXyE$Xd z*2OfJ0Izt}w;VnFV9p)8gzXcWBmrA7jM>T>Z3miEID8O^!kzN{CHC=tUP&5Qnufzs zeq)=X3vTX|Z2ugorrW(g81jwbfx0!|HQj+A?4bDf_A+ zzFTzGvk8iYD;@~CUziJ6SHJJErMF%@hS})!0-T(U???etFb7bvkT+|6EV6 zjQ^&C(l!HIG_Hx54C}AB=eG$(R*f7Dfwna~dE+nrjK1@;`mM0B*mm=M4^kSyE;T|A zUAewNV?xeH{lv%usQ-v>g%LTNz&JwH(fwT7<7uKvu@MEfh)&m< z+LBcmCTt7CHf;(SyX6hggP+Nchpy1*=GqlekCl}Twf(C7U30qBE%_M3^p^G=Od8V) ztPf?j6)^ZeCa0bH{R;X&wjyVY<;ck}XLCf+AOmDo@6)N_5rnZ~UbN=9)of%0YW<*C z%jP$}GvHF#asda8nHp}K1Uy78{rCHS<^$(#+5O=SZ7TjQJFbIwJHPEVa^c;Ew0n0e zj_1J!UKd`YURF+vpNs+@!HcWa~G0O z=Gm~?EPw-Mkl>-{{%WjWy!35iP6y=t5jK68^`_Mx6mI@7aTPl(spk)AtqG{2Yw_k7 z!kQO^M=iw6=3tll`S7z~h1*ETA~ruPP!rW-4vrV0)?gph8?j}0N>*$Tv92{b1L`q@ zjr0(#xg7-KpJPRdun{nGcjD+me!`U5G8S1i-zaD!#F2u{froCqkDZ56XOP%B*j_o1 z8y}kZXB1qpR=}Zp>*cxmexmKWbue;&R1Gv{s-g{U!Q160H$E|aV?ko=fvVHeT0e1* z`}z2d3?ph@wC~wD_q-Xi+}xaN>Q;*Q+ke2chku6=|hixM2qyiWppQKPbVW@%MqZ~O z!xqOWu7rk~#7}7`pxu z!ruCqtjY|zFv*9{{!BNw(Lr!hfaX^)JKvr#$;z6#$%v9RApuixSknp&dN7TW?gSHj ziEWVj6Z0l%+9&_Wga5?;bOxH;v2Ex#R!|C&gZ#rS8dK*%fNCXZmeuLuzyJ!=%M+f+cVq(6NZdashrhaKR#C2$F^89j*#=4 z;=Wsxd)O+^TUnrjYH#=jaZjCTJL-W9rP0))-DeZ44ICTePSnH(pp^?vSI6WlU#sXB zIULfeV+kXp&ZTC)C(!e*a@bF=Z8W)CzA0C{tyl)L{>g;lbz@LGy`|BHP>g^vM$g&V zG3v=7D&JEVVgRzV-o z{IvIN!J-%O1*37_n9Iy=5|FYotua&Cb=)}z!N23gvDg($IK;s~-rdg=mlW_wzpLhj z`U}bJ_avt{PrK}~e<#V4@*74i4!^vD07h{qXurb1@fZEw(-7 z%JTZgzc$O#YG|3tJgidt>e9^YTp{!zp&m`oZl0cK^L)@SYNJhOW#=6FV>Yjd?^i5O>agrWW=!a7hCI25HyF{hw|GUfo7$JPTHuqLRK0Be9XzyZ`?L~u zr$0$CB>JeTlU4MW5vLZXW(A4-aVJKY>`@DF@`jZ7&4w=qCl?qi&RbUK_5w->oz5iX zkE3o}vnT$?{JOvL|CIAcfTnAZYl0q_jFhLi&&I|%EvGl$4||q>d__pdox(V?rS1xx zd2unRNs>>!p;oVNHLg+Vl;M&{+Sy|A`rNjg(m~|{@npW2S2o;xjgGg2tAb?#rvT8| zs&X+&m{a}v@i$1jN(E#~euFNE@q!B2;pACR2VR^uqwFmk?MA^lYgSQyx2P6ID-~nJ z8|A07srj-S$4-6W=#JUlyAUZax~E2>{Y-Iyd&iis@OZAlfX-ZYe+~}c!-$MusXrrp zYUY}pV66a#o9V&xRdFz4hs|LJJ_l~uUL!YT!FehwR8b;5_hQ*AU5ox;8Qmm)*}K|` zJM|u&-uzPmXKt-r)oIeSH2zo;_OT@LS(pwTy@2U;r`PC) zoJ`%s@U?SYpGITBA{o7dQ_hj{E!YD8oh5bpBld7`o@MuL&`y}ixq~UA>R|0|`$O9? zH%%3*IBWLAo6Riu6s&30jNeFd9Gkqvd_|gJcJV#V!Kme}8Ry>Uy5v`W{uHtS7@1T!wM!Sz)?VEwAS>xd#^@_@Lvo+$s+H+!*Qo)Q;=KiOMj1O; z4j(?V<`-0F22D(WjY8v@yBvap^aT7Xt%KNG7kAi(dAF52SSQs?ksTrPp{(H!^EWwp zylQ5|w$}ztAGcFDj$5W*E*C!K;HFE6MhATQ)8pCIJuHxTzyfrq$hFvA7FNew1TZxD z+3>LIP7>g?Y@{Vks~KPar&Ht4rp&+iJ?%-4+f3-iqf!dS&+bxw$rE3$eRb2us&8~_@^0ioxV-t5w43N#1J-nbA`BPgG0isnx{X{1)ADVXN0VOdsm7j9UdA zG^L)IpHhOg$@A%dV*mAzV+y(%Z@TCI((3nDqWQ(PZLiaJQS5O{?SCnGDl)`$#q@@< z3FF1s#KMm~D-xh_xM*V1%G|sO<7yJBYr#p~I-~5G(yFaNAZR2#DZ>F3B1S-vK>jfSV*)jJHT%v?6myKyq*dY&aDFPV zV9MPMW=1c^;6?f5MWm+FAsIOBwNy+8y&AdMbMqA+<&7E8j|kU>pg9y>9KYEUx%o<< z#J@In(|>U6-{Vb$@%H^^ypey2_u-Bhf-juY>vlhzZWg)8)lK#?#rz5JD1B5Focp{^ z>7}w&5XgUvvzr(WEz??iP1k;xPxfUCw9;pNx}31Z7pRAm1}}rHFs3c))SuMSE5-Ci zQc@9hjhlfBfLjyJW9NVk!5?lF|LA+JU2L);Ht&?r2cR2^3UpQ~-%j0@Jv#tD~4TIF4T;v))$TH1p~D z8_?b`^3y{#@U}MCxF46h-js;JpCgJBb`Q+fqUPY!s0Y3aF?j0^JbcDAz(O!nz;o3? z_kHWJ6}~gce7GUxS$Ll>v`DjRx#0|-k%VG*<#evnWcF$k8Kn9SG!%+w!@WWZ5w z3JN~Gz48yDYW~U+lb_`OeEo8XBm^J4D1nP==Y9&+cnepWx1jZ#sJ$P!6JJ5_4SQ3=W=KN4wGZ_Te%f_Stf{(OStlq?;kHh7GulYng(zAKQ zAkoMV0b`K1-Lg@diuzzEFN^lR5jYu8V!gdpe=Q{wcKvU$x26nZP1YE@2*vz3#jpO2udxe98EZ^_&{9-7a zLU7H?QqXwnVs&Z2x)f;JdJrXO^HOye+x0g-%`xsP%u)G;83Cv3ju1ZIQ%NrryFL_k zKaIJ_k=dKdkt^S!GlnvU9MK%R%yMMKrwAh_p}}sK4 zw{Dd$IdNfmB^8;g10Ml_UytqvEm+mq zu=6Re_BvkrfH`^#*PyjHmR-~a=WcnQgJ8d4wML7l*&`;oYuSakw^Z+PQ$V=O70obRq=4^Qf0eWm@|id z#r&<7ww}JiS)Bd(R|=l_nl{JE+1DKLb=ZEE9B0j9>(v+9u~B9G_5h*8%F1`mMqgS0 zZ9it;voQyIjRL?#HI-syzG^a5b%dawM@Whb+>_W_Cv4jkqL2mbq!jMTw8!|g=LzOfc^jo!mM{BsVMfxZY^Yp>dFS^ z<>0$OTJS0HSqyO!IS;4CdOO9@>b7ncAB_86d=N9!ja`FlG-8t>L6=urn?tX_H$YX< zJX4!h7FA)l6C8}tS~W-Eo52BCyBH+ulSzP+5`F_aY>+5@Xyt2JL~yezBKH>pzOTT;Ht`CDnuKa(@Z@5uO5f>kV)w^XRa{+tml&kM4nYM!^5q>{Y))JB3X3}wa zYKP&PXU%}hz{2!J=2YRuq!;T3!PAF3H!JJKxc!my{zu<&#>a_zTo4fser|%VP~+eL?Z39BQ?kReG077uVS%!G+{LGhxG+o{RxHaV{DfT* zmF>xWY;A<+Por5MW>AxWS~*G<#0cmltJYT~2o*JroQr?!4CHd&R`PMi*ZFV!2o9;73*OS#n%} zSMoFQTORKu&=lM;P`f+i*b5E>N7-1nT^3@iZ;QV@83*0XwYt-C#XqHmu)St4oVA zBG(9YFM#*c)x(%}cVYSEZcCh2*-@dns^c$Z*s9JM$jlFnE|wV$url19Ep;}L>A8}R zu&L4LYfMlmra)pF&)vxG=5G5E%-rcwL)HayJF~#r zAf}8NIDEncJmbWT*K1 zkGLh~F-Lf{7qsm&-7j`gw1?=KaR(h1XK+yvBToc2XR66KD_J_aHq1 z!#Wq9_J#zIqdq;Lxk1xS<~^jAStqA8N{^4?6E|a?(ie#ssM&9R;Leqk5=yQjbXcPj zg`VJGp2x8uXW3qXfj99}T+zCx$e<$o)hH_~4I!ICZ-~v(y#=M}Y_dGevCr}1=gdB6%0Vvqn3`8tC*jzI<$3sUlr!{~4=XfZ5!EB-&MhVj=~%9)f_Vcq zepY=>F?DbWL0<@7^>kKD7sF|Ya8};;Rebg%$N=&Y*8F88V# zVnu#L)Fr;5S$3pJiNtetSm`Nx!1saSaHhTz^eDSWRhK~vIz_yQ=13CR_6N5;_FAg= zp;NQKX?7ggr!-`^uD&PJB=g~j_Z#W+0ZfML3dhd$LD?wPM{E-Ix0hb;9KQ6xLcGAn zSI4?*C(mQ^gL;MUOzsUbZHzu0w4GLV@*7ooQfzrAq}STBiQrrAe_#8 zR-)YVoKJXm`fA=I@h{Qhb-uj2G($s79e5soRLyyKOW<5*zx2{hJ=vH$q8MI*MprYf z89(?&vnad`h$LA1b?|Di&pSZU1x*hW4By_5SNAp_k)}F*#VwyDR|Mg0eJ*-VOF<_1 zWYt97dhUSU73*+oyF2`dksh3}`2YhxQc_rKZGk*07=+$C9S7IyHFq7FnnlAzC$_|dvjx)3iB zAtSZB!O&ms&chh;`H!!8MMwOdv{*a%sUNGja6XMW1fIUv_r$(K-{gz}BgcKV8~&H1 zs2i_5Qqma>?3Ht>1JI1QP!F+laeRn+GthFFJWKDG@D`Y(VCUv<&akBN& zWka09S(}rt6N5JBr+V41{gqbDo(=~vzL@DLiY#8sWADb4G0i&Z4_8hTXLy~?#( zx^AOg@C=I=S!(Y+Ioj}{Co@76-z<8-D=3vwPWpT`Y9!b@WTsm^G(y3owx;t;PH9s3 z6SBd*E4^AGyIRl1zswmz4t1mK`Fq6d(tHw+-8R3yYL7oNhWa87&xa)84}UNzcI#id z(*SOa4a8Z8?Z@3h%+ieNTfo-pm;+p|3L9j#aaa!7kEn@BoU9~vgTtsdldyO23xz{} z9CZpV$E(4`g%F(WkG?MraQ5MllLl-y~0Sgi#^5Ji3_0kG5YwN6c# z;toJF%y{tP-1`d{@ko7ZSr6bCrkda>U)jd{d?~2076CY~!57=Ysxv1hZWz|Y3E8jY zUa}qNUc(Nc@Pn$LSwEbXDGf)DcG#$^7M>@1@QwKP+G64Mr6zFxq0DOWJYgB`sU;+R z_{Iz;$m%*A2Xz#~@ntVKBt(VGo(D^?Yo%$XXx_3;RY;atsX)ZQ@p|#Y&^qn!Mpv;v z_90|ZW=<3}>pXy)fM1Hi8>~T2e8ex@VbNaiR%x;RNULTN@DnA(4>a`JX!Skc#S#4Q zKFvJVpRI~jDsyYV1R^&!s~A0N?djL1qyo8^m2r(6%r5d`UZIS%>Q%cklI-y~BrhK| z*a$`^mk(XqExM0pT$5XE?5oj>F;d^24(yHn!dX64rjzMT`=(C$T;6#wD(VuHcXr;Y93V!a~TB(gNo6 z*A8%+t+V)fyel7Wm~Si+j^-1Po4~+|q6%0tQ(0gTac1?1otL)k_LtSrdMTljuH{Ro z7(yP`U+B%e;#a=cI(1Eez}_-p8Xz{+X&{>~S$s}uSzHk=?)T}-*9nna&2_0T{E@G! zm2$6Q&O&CcG(F}(e!l-?ZT<$|?PKl)Q{D7|i|#3F8Tv}9=6C9q`r(rYJGlgG&Ma~= zp7_PADgiE$1SYD@Ba1=5X>5;uf;HwX9mKt~) zx%NH=Z_xsUfddppXf#~HNFCoej~a2BU+s%03c`B32l0Izp%HbJI)rL`Kj;Lp{Z2*Vr=mrx3lSi*PUjujGf;ncXR$a!~}-$rtzqlJqgC9XyGW2;u?XA70I;bz@^ z20gu2OlYNeDICL>FlAX0%I&r+2{A829(iO1V#bX&Sl|NGKFt{K8rm15E!Z_rOh;|E1P+szQc>7*7gDS%i3Y1~P^3MDD6G2&Et&tudbgFT^i63~a5Hb^y4gKbF) zxTdwyk*unFG!HYZG)e+uqoUFBF^yabR=b6lST8kRt)l=%lUMOdxOGnmPUXf3Es)ju z(Oej4E}DDk8&fi{neMj_K2L))!(B>Rpv@biaKg6PIF&iz!nQj75?Ok-Xde*~QD3&m6`-muEx zD*LWGBjlH=cv*8^$kBKIFZSLuDynYl5=H?*Q80j#O9do}2!dovM6%>0Sp_5sNEQ$Z zMGT}Oqog9|AUO-D+^WOzJ0^{_Wee8kNX40sj3t9-shaX*P3gt zxjrLVdV;416%`*!zUtp2-%J^=xsF|)P~B&4s-iy#o19V7BMFVWj(f8!Ik*{hVS7rh z^63L~cokx6SKyWXA?=c;72pip8z8fx)}{2%?JM?5`ytD{(r+tr((D>Rkb3}MI`^Ml8cRLlP=tGN?yC$nqU6e5L3hrNQ9fjs+IGg zQ`L;#HL8>+z62s++%m&m8PrOtI0EsWgcbzk=MOeh00HH*H%y^N<5 z!w!LAALB5=d#xj*jn3C}w8u%?Lt7bVncn2LQCs$KSBAdUxp)+IL@STSyK@0IR?|v{ zF{N9-J0B!R+sY}H<2Z_+ShM&}WD4?Q0qfNZZ zz~;3pr*lglPKs-uLyH#cB{k*cl+)4i~EY81hge|*n zBvN|}@~oJbxn{9)S?}!IZxdG$4>TO2%%8 z%Lpto{ws#kcZ-@%HXRv}KNUHLS}hH}v{)UQ_~7KN8=;W)5+x-1q*Hl+nstMCe^^Da z0@=L@Izw-|#jX7GnXjEZ)vt(K9lx07%U(lEEe=7MJxNekLpn{W*Q|%Hi^y4L;Xl(Q*}jcJzG6np*&YRlEYt33^fP1LSd^e{6}31*CdI_AB%9hG z(RLryXNxy#h6yqZql=VGhLeSxUYHqmGWvK#+=q&5$p>8`I_hCT6z92Z>}QY9zM{z$ zb^FQ6vzq&RO_Wb_ZIVa5cHU6dV=0N4Xm=N<1nv!LS34Nyjjr@n;Yzqx&O^;Zb@*6< z-m?bogfCs^96En=NzrVF`tpkk+KD(cOG11OOt&WCcqEHgcJEvuojhd66XP;;Cme*69XldZCr;RSw^YWCQSx1nt^;^!7MUIRZ%A;hM_p zei{7*f2iZx!rArYRSh|i8$wsZ_a4=p9CDPvQH>pVCj%<}`%R`-0rU!DueBe{qlwVW zrL97f_E`j%ktLJuZFaoVccKwLi{+tqCs*mf?2U&KPo6>)^P9<_tSjY3>agdpKHQyk zY-XWS%avOo)otjy?$pl&uD+|+mECvlENu_t<$~xy%JDY{@sbIA2TywLENGWN}Kpbn|u{AZ&?-Y-R`a8$7ra8;>MU*WC{Ho5d*k13mHRn9kf zd6a9Am$@ShAG~4DWZDicjc98{M-&X4!L8;G^0J@nq}-XurPz_7rB(y?axP@7m+=7& z3q}|#qJ>Za4La7F#hSIlwy9@K&^J-ZIGufO`lxCm9W+fS&9%y#hbN<2LUm&n0}y;* z5<2L0vxiwRor%5?Ru<}7=Qg}?w!nZ<^$bXZhsCSsL;=EK3YdIh@s~5U+Ht%{iDi+a zX_=U>KUx0uB#@yRt~JnU}-z5k6dXpK&bl?F5p3#gAKY3l_-%5_a`An@G!Hqmtbfwsph^2~=|Sa+J6!6SV0=&VV5Ii0w-T5(|&B zZr5Pd{X4m_&y2q+ohN&Lp`(^xP93iU^nd&20#nOfYq4boHjF$py;e_-Z)hPZX(uP+ zobj+oi%C_~+exW%X`*|JflBpFCq;RNd+c2@R@+?-qzDi>2pLM7&>z9$)RWD|9vcS@%6 zDa$M471-yVp>;=$ZDJ|hlF6ODQZl{DKkP*%j|o9y3Wv@w`?ZzYxePlCnSn3p^wb?9 zLaSN8chsr2o^j^gN5e2Huw9z`QNO}8*i82TM{<$<733v1rrTkXC0%CueMWa}*0p(p zzc9>g;s-HUtS&hwZ5FXt?G>nkdp+ zVt+ps)h@1aqagPyX!^AUb6^3F~(Id5O%#XCVZD zO-ys+f=RI?OV?`tpa&aV!_iG`Ypv3f%hG{M_{j1$kB|xTJPs) zu5Zsf`WNeurN|Zb%5&OiE*$z<+4)kWhlF7=ziyVf#Us8^^_G1T6b|5X>7 zlV4O`!U2b>UiX0Ew_CcVUpnq(D7Vp9+g<7dyR(FK=ypNRuK&Lz1Qg?;nU#@7JRhair%}?Kxjs7+4$a>P zGz|^MD?+a9-CbO8YNi=z+g?WHkvLurYe$cWa$zi%>oMWC`SUg%KN0X4ZaoQx)wG%} zN963fW=;HCpkrDk3WLw;MW}0d9Op=lRzB*_^)!XQLTYN4x#UzSja!jKv@$TLEq`Qx z8tlbCPB65VAfibzBKuTp@BqN~3bM0!y#ithnP2fHRdvwa*4 zQ(Kc_lRiJP<}_Kv$#Ly^gss~J`;C#h_>rjwx|)6xzQ~<3NUb>78EDI}(1}>FuU{&d z4f59*pNFpQ53x}jD#k|)?nd7j)9ze^^{nqHao!CZ&yuD)F*4D8OVlm9O6z%yk_EOn zix@u*%_L~I4!hZth&l+J?PPq(iR`6RFeNyw-(ASs(44bFmRFkR&DywetOQ;@J$mWj zfttYtAjGn7=gX#t$wrQh<(yTWRqGRbz$~AJJ}9ryZinsl0!O#5D{ELY-9RAhE%`SO z&zLjuRven)UNHy7!z{oWO6RE07+P!uyrWx;H%kdA1QK8nmDT2``F6d1UHY`0QraH6sCII*Ha?rYZ{{lEWL~L& z$)l{ zht=iv(%rP6oH`Gpx<7Q&bh8%8p_%%j6Az)G={0cnDOY>+TWHt&I8MczCe>Ch^XR&k zbpiYL*HXw9oM|DxRI7$Bhx!es75ylP183vBj%TlQyyj92?eXFMJ)B1@LsFhMotp`$h23q*0#!QZ^(ck>NYE3iCm=RSoO5? z7H(e{fnvr9E{lS7Sw<9D$U28(1B8rig7$NTSoHw)nva|Sged@4CMT1HLK>{O2Z$^I zK~mI%zM5|NvWxG@n}GSh+cpAWdm9$1YZrC97bNp0(EJLBOr9jn;&?gG+`QqKn7i1Mr zvL!P<6j=7}?v_h64`BLB@De;d4*O`q;phG`g8!*PZ8CQ)^lk%k`M!H;*=ISJtl@Ho9mm z!!AXQYR8iqdQC$q>oWQ(^<{U{IuLc;1$GAR0yH_m7KQM^YVoJcb@Jf2XN?5(t^keh zK5&^&*0GEaCuR)T^k_v^ZeNnOvN}cdbfW<558XP0>vY^3IK(=)pc91jN{ zh9Z@HLwY`ww6>4$BVy@rG71i!HZ)_oQQ2P~RH+&NIk z8#tS^o4w&4ijHS%Z5d4K%&0Zb*4bQe8X5{WDm+u5lKWJBPcR>Iw?QFTK7DLZy)yrW zz+2~v&q{M|DfN816xioym;Jj+47e4dw5z-L$-l}NnS+J+N zcxk$josx*XN1;*190bb7x`7uVMGfQ`7Xsr{!+6SHklzY}>*B?F`k@EWEg#uAEL2*lB;cn|5v;Tkp*T>!uDG zpI^T^-;$sAbSjoGDdNGAoVrU=;8qO0bO<&Fm-C5xBDZcJ<8V_x;k=mTGXU%WG3`7m z1m7Oj{tieY&bP(oz{jZ8Io>0&1aT&5IGLq4S``qs42l zF#C;OltNv#@$6hi8nYruLW39~kj>kmCgtaktwLx^EbG)nfdf*r56&iWm~z&AUK{{nc_@KFgLoTi4V68*)r2G$$~3N+G#mPa06 zq)h`%s=z}xocqem;3G!=p=2NNHf0DR`f@hR44Q@K9$acv4joSt=)4E?0`>30_&G8Sa%*sHo1cm~-drGjtP#pfoWt zdmZY}HO*Xpo(c(F%h(funM1P*4C_;l23*86nk>Se&%s7iS1bu_Fiqo1ko0<9>Mvab zMq3eLpcfxONUwD|8mMjtxtD|6+!BXlIq}N8D6ND%KTuFOKDs0hzYiRe*tV`!Lx4D=wxT^9y{Duh^(nU(o*(W^q^f zGg_wb*mb5Z2pUZY12|c>_*U}YK*PHQWA(1pq)Fl2*?aEq=tt3e7-Y__c*d->`Km78 zB078qeq_gS|02(Hd#u|0xqWvsdixTAgJ5n;0U#Da6#`FcQdn=NYjto$UTP~FH#1Fs-ZLaYO%>|2v+C(c5E5eiU27HiGbrK}Tz6x!VSP4Uaf6k+GO}cFa_E3bB7Q(^bi8TqRR~kf zIYcy*r;`?sUkW$Ht560On~UAdFYg1?w>jBY)nZ$()k$o^dADc%VO4RVf@D{pBJ$+3 z&(?w7#6uO?9Eq8lb071{t35yD1(5PMK?>P-b4zuc{78mVt)gUk?L_G6g*zHNI^- zIXV}QsINb(i`*55w^ed+zE_Ni&~oV;^3tw~H_DsirqCZhx_#EQDp*d!VYq;$B7Lo| zA92&2569quXtoB+*nDR4oF|W6n()L#>aP9Asn7x|XIu6iEi=axm@Em&aA zh1=s1+M83^Avg9K9R}32vW#}vIcnppFcUXQDn6D+HMibUdj%M4xTNaxyP>V;Nrt-Cis`aT!P63khw4j2c<#=tSM|ol z0*|ynW{ryiN*Y4Pr%Y}t9WF&u|7H**1k^remdegj@`F7%$m;aS<?mzoK=9#^L|o>tuWxxOd?yn4hyp&+ z2p@*34ZijcPd0DlM@{zM+P&@PMSq?BexoU)SB|X~uc8+1!GEgZ`Ql{(OUAam?vAFPwNHV*cq4=KOCCPsx5ti0BMw_cR83yFk`sDl<) zpWvXff-Xc)c0qsar;d*V9nATV?DOB*+W@W-Bz9uiGBH|2V$3@)vQJRuMXtKcu|s_N z*tKK_3n<>{!ND+F((VtRQo0|f_rxcu_C#GApXd%;z0`ZIqzXCn(xaB4U>+Bjb3 zr?b<|2bu(=b3J2rSAIHV|NUm=uWZce-$q7GlvohW3W7kxCnX3DTL;*7-+p%U!>?
?^Q<%t_!-0G1vn>>I1<0U8EKc%OcD%k9nQ(SP(8 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 C_%6`^ literal 0 HcmV?d00001 diff --git "a/seckill/\350\257\264\346\230\216 .txt" "b/seckill/\350\257\264\346\230\216 .txt" new file mode 100644 index 0000000..13cfd1c --- /dev/null +++ "b/seckill/\350\257\264\346\230\216 .txt" @@ -0,0 +1,35 @@ + + +ӳ١GC->洢 + +ǰ˿ƣ¶ӿڣťظ +̬ݷ룺CDN棬˻ + +߲Ż +1.ʹRedisΪ +2.Ȳ빺ϸټ棨мʱ䣩 +3.ʹMySQL洢ִи² + + +ϵͳõЩ +CDN +WebServer:Nginx(ؾ)+Jetty +Redis +MySQL + +һѾcdn +ɱIJ,ɱĵַȡ󲻷cdn,Էʵǵķ +ǵķͨǵdnsҵǵĵַ +һҵnginxַ,nginxһ㲿𵽲ͬĻ,,ƶ,ͨ +ĻܵdnsûipַܵdnsNginx +nginxǵķؾ +֮jetty tomcatǵ߼Ⱥ,ǵĴ +ʵtomcat jettyʱ,õ漯Ⱥredis(,ʹùģ,һһredisȺ) +ʹùģȺ +mysql,ݹؼidֱֿ,ؼid϶ɱid +ΪͬһɱidӦ÷ͬһݿ,ŻͬһݿЧ +seckillIdȡģ,ֱ(һֱ512ű,1024űȵķ) +Ȼոؼдֿ (ֱֿ,Ͱ͵TDDL) +֤ͬһɱͬһݿ,ͬʱ֤ǷdzӴ, +԰Ͷдַͬݿ +һͳƷ,ڶDBȡɱ(BIϵͳ,ͳƱ) \ No newline at end of file From d4da2f42bc2baee509e4a4f9edd5021894a56b1c Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Sat, 11 Mar 2017 13:58:46 +0800 Subject: [PATCH 6/8] =?UTF-8?q?maven=E5=92=8Cgit=E5=B8=B8=E7=94=A8?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 10 ++++ README.md | 12 ++++- note/git.txt | 140 +++++++++++++++++++++++++++++++++++++++++++++++++ note/maven.txt | 53 +++++++++++++++++++ 4 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 note/git.txt create mode 100644 note/maven.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d66b18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Compile class file +*.class + +# Log file +*.log + +# Package Files +*.jar +*.war +*.zip \ No newline at end of file diff --git a/README.md b/README.md index 8e40390..74b5f3c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # JavaLearning -Java +Some Demo about Java +## RegisterDemo +使用JavaMail实现的注册邮箱验证实例 +## ssh +Spring+SpringMVC+Hibernate整合实例(基于Maven) +## ssm +Spring+SpringMVC+Mybatis整合实例(基于Maven) +## seckill +SSM+Redis+MySQL构建的秒杀系统 +## note +一些学习笔记 diff --git a/note/git.txt b/note/git.txt new file mode 100644 index 0000000..e669afa --- /dev/null +++ b/note/git.txt @@ -0,0 +1,140 @@ +所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。 + +不幸的是,Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的。 + +因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。 + +初始化一个Git仓库,使用git init命令。 + +添加文件到Git仓库,分两步: + +第一步,使用命令git add ,注意,可反复多次使用,添加多个文件; + +第二步,使用命令git commit,完成。 + +要随时掌握工作区的状态,使用git status命令。 + +如果git status告诉你有文件被修改过,用git diff可以查看修改内容 + +,在Git中,用HEAD表示当前版本 +上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100 + +现在总结一下: + +HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。 + +穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。 +如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数: + +要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。 + +git diff #是工作区(work dict)和暂存区(stage)的比较 +git diff --cached #是暂存区(stage)和分支(master)的比较 + +工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。 + +Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。 + +提交后,用git diff HEAD -- readme.txt命令可以查看工作区和版本库里面最新版本的区别: + +Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。 + +撤销修改: +场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。 + +场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。 + +场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。 + +命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。 + +要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git; + +关联后,使用命令git push -u origin master第一次推送master分支的所有内容; + +此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改; + +分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了! + +你也许还注意到,GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。 + +使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。 + +小结 + +要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。 + +Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。 + +Git鼓励大量使用分支: + +查看分支:git branch + +创建分支:git branch + +切换分支:git checkout + +创建+切换分支:git checkout -b + +合并某分支到当前分支:git merge + +删除分支:git branch -d + +当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。 + +用git log --graph命令可以看到分支合并图。 + + +分支策略 + +在实际开发中,我们应该按照几个基本原则进行分支管理: + +首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活; + +那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本; + +你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。 +如果在分支dev1上工作,没有add和commit,直接切换到master上,会发现改动的内容直接体现在master的文件上,所以需要stash一下工作分支,然后新建bug分支开始新的工作。采用stash多个分支可以切换分支工作而不影响其他分支工作。 +要切换分支必须commit之后 + +并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢? + +master分支是主分支,因此要时刻与远程同步; + +dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步; + +bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug; + +feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。 + +多人协作的工作模式通常是这样: + +首先,可以试图用git push origin branch-name推送自己的修改; + +如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并; + +如果合并有冲突,则解决冲突,并在本地提交; + +没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功! + +如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name。 + +命令git tag 用于新建一个标签,默认为HEAD,也可以指定一个commit id; + +git tag -a -m "blablabla..."可以指定标签信息; + +git tag -s -m "blablabla..."可以用PGP签名标签; + +命令git tag可以查看所有标签 + +命令git push origin 可以推送一个本地标签; + +命令git push origin --tags可以推送全部未推送过的本地标签; + +命令git tag -d 可以删除一个本地标签; + +命令git push origin :refs/tags/可以删除一个远程标签。 + +忽略某些文件时,需要编写.gitignore; + +.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理! \ No newline at end of file diff --git a/note/maven.txt b/note/maven.txt new file mode 100644 index 0000000..cfd2769 --- /dev/null +++ b/note/maven.txt @@ -0,0 +1,53 @@ +maven常用命令: +mvn -v 查看maven版本 + compile 编译 + test 测试 + package 打包 + + clean 删除target + install 安装jar包到本地仓库中 + + archetype插件:用于创建符合Maven规定的目录骨架 + +mvn archetype:generate -DgroupId=组织名,公司网址的反写+项目名 + -DartifactId=项目名-模块名 + -Dversion=版本号 + -Dpackage=项目所在包名 + -DarchetypeCatalog=internal + +mvn archetype:generate -DarchetypeCatalog=internal按照提示进行选择 + +坐标 + 构件 +仓库 + 本地仓库和远程仓库 +镜像仓库 + +完整项目构建过程包括: +清理、编译、测试、打包、集成测试、验证、部署 + +Maven生命周期: +1.clean 清理项目 + pre-clean 执行清理前的工作 + clean 清理上一次构建的所有工作 + post-clean 执行清理后的工作 +2.default 构建项目(最核心) + compile test package install +3.site 生成项目站点 + pre-site 在生成项目站点前要完成的工作 + site 生成项目站点文档 + post-site 在生成项目站点后要完成的工作 + site-deploy 发布生成的站点到服务器上 + +clean compile test package install + + +SNAPSHOT快照 +alpha 内部测试 +beta 公测 +Release 稳定 +GA 正式上线 + +依赖冲突: +1.短路优先 +2.路径长度相同时,谁先声明谁优先 From 2f35828c6e0d6d12dae347e075cb304521ec18a6 Mon Sep 17 00:00:00 2001 From: SnDragon <1803240383@qq.com> Date: Sat, 17 Jun 2017 15:41:47 +0800 Subject: [PATCH 7/8] ssm+shiro demo --- .gitignore | 7 +- cg/ReadMe.md | 62 +++++++ cg/pom.xml | 167 ++++++++++++++++++ .../legend/cg/controller/UserController.java | 127 +++++++++++++ .../exception/DefaultExceptionHandler.java | 25 +++ .../main/java/scut/legend/cg/dao/UserDao.java | 18 ++ cg/src/main/java/scut/legend/cg/po/User.java | 36 ++++ .../java/scut/legend/cg/realm/UserRealm.java | 62 +++++++ .../scut/legend/cg/service/UserService.java | 17 ++ .../cg/service/impl/UserServiceImpl.java | 50 ++++++ cg/src/main/resources/cache/ehcache.xml | 51 ++++++ cg/src/main/resources/jdbc.properties | 5 + cg/src/main/resources/logback.xml | 16 ++ cg/src/main/resources/mapper/User.xml | 28 +++ cg/src/main/resources/mybatis-config.xml | 17 ++ cg/src/main/resources/spring/spring-dao.xml | 53 ++++++ .../main/resources/spring/spring-service.xml | 32 ++++ cg/src/main/resources/spring/spring-shiro.xml | 142 +++++++++++++++ cg/src/main/resources/spring/spring-web.xml | 46 +++++ cg/src/main/webapp/WEB-INF/jsp/index.jsp | 12 ++ cg/src/main/webapp/WEB-INF/jsp/login.jsp | 39 ++++ .../main/webapp/WEB-INF/jsp/unauthorized.jsp | 12 ++ cg/src/main/webapp/WEB-INF/sql/20170617cg.sql | 63 +++++++ cg/src/main/webapp/WEB-INF/web.xml | 83 +++++++++ cg/src/main/webapp/index.jsp | 5 + cg/src/main/webapp/login.jsp | 39 ++++ .../main/webapp/static/js/lib/jquery.min.js | 4 + cg/src/test/java/cg/TestPath.java | 11 ++ 28 files changed, 1228 insertions(+), 1 deletion(-) create mode 100644 cg/ReadMe.md create mode 100644 cg/pom.xml create mode 100644 cg/src/main/java/scut/legend/cg/controller/UserController.java create mode 100644 cg/src/main/java/scut/legend/cg/controller/exception/DefaultExceptionHandler.java create mode 100644 cg/src/main/java/scut/legend/cg/dao/UserDao.java create mode 100644 cg/src/main/java/scut/legend/cg/po/User.java create mode 100644 cg/src/main/java/scut/legend/cg/realm/UserRealm.java create mode 100644 cg/src/main/java/scut/legend/cg/service/UserService.java create mode 100644 cg/src/main/java/scut/legend/cg/service/impl/UserServiceImpl.java create mode 100644 cg/src/main/resources/cache/ehcache.xml create mode 100644 cg/src/main/resources/jdbc.properties create mode 100644 cg/src/main/resources/logback.xml create mode 100644 cg/src/main/resources/mapper/User.xml create mode 100644 cg/src/main/resources/mybatis-config.xml create mode 100644 cg/src/main/resources/spring/spring-dao.xml create mode 100644 cg/src/main/resources/spring/spring-service.xml create mode 100644 cg/src/main/resources/spring/spring-shiro.xml create mode 100644 cg/src/main/resources/spring/spring-web.xml create mode 100644 cg/src/main/webapp/WEB-INF/jsp/index.jsp create mode 100644 cg/src/main/webapp/WEB-INF/jsp/login.jsp create mode 100644 cg/src/main/webapp/WEB-INF/jsp/unauthorized.jsp create mode 100644 cg/src/main/webapp/WEB-INF/sql/20170617cg.sql create mode 100644 cg/src/main/webapp/WEB-INF/web.xml create mode 100644 cg/src/main/webapp/index.jsp create mode 100644 cg/src/main/webapp/login.jsp create mode 100644 cg/src/main/webapp/static/js/lib/jquery.min.js create mode 100644 cg/src/test/java/cg/TestPath.java diff --git a/.gitignore b/.gitignore index 2d66b18..16e1589 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,9 @@ # Package Files *.jar *.war -*.zip \ No newline at end of file +*.zip + +.settings +**/target +.classpath +.project \ No newline at end of file diff --git a/cg/ReadMe.md b/cg/ReadMe.md new file mode 100644 index 0000000..172c45d --- /dev/null +++ b/cg/ReadMe.md @@ -0,0 +1,62 @@ +# 说明文档 +## 环境介绍 + Spring+SpringMVC+Mybatis+MySQL+Shiro+Maven +## 运行过程 +### 1.用eclipse导入 +应该可以看到如下目录结构: + +![](http://i.imgur.com/f76eI6u.png) + +### 2.执行SQL脚本 +SQL脚本位于webapp->WEB-INF->sql下,该脚本在MySQL5.7上测试通过。 +主要有五张表: + +![](http://i.imgur.com/HupJjhX.png) + +由用户、角色和权限之间的关系不难得出这五张表。 + +### 3.修改项目配置 +要修改的主要是jdbc的连接配置,将jdbc.properties对应的属性修改成你本机的属性即可: + +![](http://i.imgur.com/QoGWzSP.png) + +### 4.确保Maven已导入相关依赖 + +--- +## 项目演示 + +为方便演示,sql脚本中已有相应数据: + +有两个用户:admin和user + +admin的role为admin,user的role为user + +role["admin"]具有user:view,user:create,user:update,user:delete四种权限 + +role["user"]只拥有user:view的权限 + + +Shiro的核心配置如下: + +![](http://i.imgur.com/ahXALOK.png) + +接下来进行测试,启动项目,在浏览器中输入: + +### http://localhost:8080/cg/index +由于/index配置了authc过滤器,因此用户在未认证之前会跳到loginUrl,也就是[http://localhost:8080/cg/login](http://localhost:8080/cg/login)进行认证: + +![](http://i.imgur.com/2FYS7za.png) + +输入正确的用户名跟密码(user/123456)后登录成功,再输入[http://localhost:8080/cg/index](http://localhost:8080/cg/index),正常进入页面: + + +![](http://i.imgur.com/wm6yDCC.png) + +此时用户role为user,拥有的权限为user:view,输入:[http://localhost:8080/cg/index2](http://localhost:8080/cg/index2 "http://localhost:8080/cg/index2"),[http://localhost:8080/cg/user](http://localhost:8080/cg/user "http://localhost:8080/cg/user"), +都可以正常访问,而[http://localhost:8080/cg/admin](http://localhost:8080/cg/admin "http://localhost:8080/cg/admin")则不能访问,由于用户已经登录,所以会跳到unauthorizedUrl即http://localhost:8080/cg/unauthorized,如图: + + +![](http://i.imgur.com/xp6TkIx.png) + +注销:[http://localhost:8080/cg/logout](http://localhost:8080/cg/logout "http://localhost:8080/cg/logout") +之后会跳到登录页面,换成admin/123456登录再验证如上url。 \ No newline at end of file diff --git a/cg/pom.xml b/cg/pom.xml new file mode 100644 index 0000000..04de3d7 --- /dev/null +++ b/cg/pom.xml @@ -0,0 +1,167 @@ + + 4.0.0 + scut.legend + cg + war + 0.0.1-SNAPSHOT + cg Maven Webapp + http://maven.apache.org + + + UTF-8 + 4.1.6.RELEASE + 1.2.2 + + + + + + + junit + junit + 4.12 + test + + + + ch.qos.logback + logback-classic + 1.1.7 + + + org.logback-extensions + logback-ext-spring + 0.1.1 + + + + org.springframework + spring-context + ${spring.version} + + + + org.springframework + spring-webmvc + ${spring.version} + + + + org.springframework + spring-jdbc + ${spring.version} + + + + + com.alibaba + druid + 1.0.18 + + + + mysql + mysql-connector-java + 5.1.38 + + + + org.mybatis + mybatis + 3.4.1 + + + + org.mybatis + mybatis-spring + 1.3.0 + + + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + jstl + jstl + 1.2 + provided + + + + com.alibaba + fastjson + 1.2.23 + + + + + org.apache.shiro + shiro-core + ${shiro.version} + + + org.apache.shiro + shiro-web + ${shiro.version} + + + org.apache.shiro + shiro-spring + ${shiro.version} + + + org.apache.shiro + shiro-quartz + ${shiro.version} + + + org.apache.shiro + shiro-ehcache + ${shiro.version} + + + commons-fileupload + commons-fileupload + 1.3.1 + + + commons-io + commons-io + 2.2 + + + commons-logging + commons-logging + 1.1.3 + + + org.apache.commons + commons-lang3 + 3.1 + + + + commons-collections + commons-collections + 3.2.1 + + + + com.fasterxml.jackson.core + jackson-databind + 2.2.3 + + + + + + + + cg + + diff --git a/cg/src/main/java/scut/legend/cg/controller/UserController.java b/cg/src/main/java/scut/legend/cg/controller/UserController.java new file mode 100644 index 0000000..b439261 --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/controller/UserController.java @@ -0,0 +1,127 @@ +package scut.legend.cg.controller; + +import java.util.List; + +import javax.annotation.Resource; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.IncorrectCredentialsException; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.credential.PasswordService; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +import com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations.PrivateKeyResolver; + +import ch.qos.logback.core.net.LoginAuthenticator; +import scut.legend.cg.po.User; +import scut.legend.cg.service.UserService; + +@Controller +public class UserController { + @Resource + private UserService userService; + @Resource + private PasswordService passwordService; + +// @RequestMapping(value="/user") +// @ResponseBody +// public User testUser(@RequestBody User user){ +// System.out.println(user); +// return user; +// } +// + @RequestMapping(value="/user/{id}") + @ResponseBody + public User testList(@PathVariable("id")Integer id){ + User user=userService.getUserById(id); + return user; + } + + @RequestMapping(value="/login",method=RequestMethod.POST) + @ResponseBody + public String login(@RequestBody User user){ + UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),user.getPassword()); + try{ + SecurityUtils.getSubject().login(token); + return "success"; + }catch(UnknownAccountException e){ + return "账号不存在"; + }catch(IncorrectCredentialsException e){ + return "密码错误"; + }catch (Exception e) { + return "其他错误"; + } + } + + @RequestMapping(value="/login",method=RequestMethod.GET) + public String login(){ + return "login"; + } + + @RequestMapping(value="/unauthorized") + public String unauthorized(){ + return "unauthorized"; + } + + @RequestMapping(value="/encry") + @ResponseBody + public String encry(@RequestParam(value="password")String password){ + return passwordService.encryptPassword(password); + } + + @RequestMapping(value="/logout") + public ModelAndView logout(){ + SecurityUtils.getSubject().logout(); + return new ModelAndView("login"); + } + + @RequestMapping(value="/index") + @ResponseBody + public String index(){ + return "如果你看到这个页面,说明你已经登录,否则会跳到登录页面"; + } + + @RequestMapping(value="/index2") + @ResponseBody + public String index2(){ + return "如果你看到这个页面,说明你已经登录或者选择了记住我选项"; + } + + @RequestMapping(value="/user") + @ResponseBody + public String getUser(){ + return "role为user的用户才能看到这个页面"; + } + + @RequestMapping(value="/admin") + @ResponseBody + public String getAdmin(){ + return "role为admin的用户才能看到这个页面"; + } + + + @RequestMapping(value="/user/view") + @ResponseBody + public String viewUser(){ + return "拥有user:view权限的用户才能看到这个页面"; + } + + @RequestMapping(value="/user/create") + @ResponseBody + public String createUser(){ + return "拥有user:create权限的用户才能看到这个页面"; + } + + + +} diff --git a/cg/src/main/java/scut/legend/cg/controller/exception/DefaultExceptionHandler.java b/cg/src/main/java/scut/legend/cg/controller/exception/DefaultExceptionHandler.java new file mode 100644 index 0000000..2fc40ea --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/controller/exception/DefaultExceptionHandler.java @@ -0,0 +1,25 @@ +package scut.legend.cg.controller.exception; + +import org.apache.shiro.authz.UnauthorizedException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.servlet.ModelAndView; + +@ControllerAdvice +public class DefaultExceptionHandler { + /** + * 没有权限 异常 + */ + @ExceptionHandler({UnauthorizedException.class}) + //@ResponseStatus(HttpStatus.UNAUTHORIZED) + public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) { + ModelAndView mv = new ModelAndView(); + mv.addObject("exception", e); + mv.setViewName("unauthorized"); + return mv; + } + +} diff --git a/cg/src/main/java/scut/legend/cg/dao/UserDao.java b/cg/src/main/java/scut/legend/cg/dao/UserDao.java new file mode 100644 index 0000000..5d96679 --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/dao/UserDao.java @@ -0,0 +1,18 @@ +package scut.legend.cg.dao; + +import java.util.List; +import java.util.Set; + +import scut.legend.cg.po.User; + +public interface UserDao { + + User getUserById(Integer id); + + User findByUsername(String username); + + List findRoles(String username); + + List findPermissions(String username); + +} diff --git a/cg/src/main/java/scut/legend/cg/po/User.java b/cg/src/main/java/scut/legend/cg/po/User.java new file mode 100644 index 0000000..8efb8f8 --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/po/User.java @@ -0,0 +1,36 @@ +package scut.legend.cg.po; + +public class User { + private Integer id; + + private String username; + + private String password; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + + +} diff --git a/cg/src/main/java/scut/legend/cg/realm/UserRealm.java b/cg/src/main/java/scut/legend/cg/realm/UserRealm.java new file mode 100644 index 0000000..2a31b87 --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/realm/UserRealm.java @@ -0,0 +1,62 @@ +package scut.legend.cg.realm; + + +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; + +import scut.legend.cg.po.User; +import scut.legend.cg.service.UserService; + + +public class UserRealm extends AuthorizingRealm { + //Autowired,通过xml文件注入,否则会出错,原因不详 + UserService userService; + + + public UserService getUserService() { + return userService; + } + + public void setUserService(UserService userService) { + this.userService = userService; + } + + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + String username = (String)principals.getPrimaryPrincipal(); + + SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); + authorizationInfo.setRoles(userService.findRoles(username)); + authorizationInfo.setStringPermissions(userService.findPermissions(username)); + + return authorizationInfo; + } + + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + + String username = (String)token.getPrincipal(); + + User user = userService.findByUsername(username); + + if(user == null) { + throw new UnknownAccountException();//没找到帐号 + } + +// if(Boolean.TRUE.equals(user.getLocked())) { +// throw new LockedAccountException(); //帐号锁定 +// } + + //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现 + SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( + user.getUsername(), //用户名 + user.getPassword(), + getName() //realm name + ); + return authenticationInfo; + } + +} diff --git a/cg/src/main/java/scut/legend/cg/service/UserService.java b/cg/src/main/java/scut/legend/cg/service/UserService.java new file mode 100644 index 0000000..dd98784 --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/service/UserService.java @@ -0,0 +1,17 @@ +package scut.legend.cg.service; + +import java.util.Set; + +import scut.legend.cg.po.User; + +public interface UserService { + + User getUserById(Integer id); + + User findByUsername(String username); + + Set findRoles(String username); + + Set findPermissions(String username); + +} diff --git a/cg/src/main/java/scut/legend/cg/service/impl/UserServiceImpl.java b/cg/src/main/java/scut/legend/cg/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..5a43f0c --- /dev/null +++ b/cg/src/main/java/scut/legend/cg/service/impl/UserServiceImpl.java @@ -0,0 +1,50 @@ +package scut.legend.cg.service.impl; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Resource; + +import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper; +import org.springframework.stereotype.Service; + +import scut.legend.cg.dao.UserDao; +import scut.legend.cg.po.User; +import scut.legend.cg.service.UserService; +@Service("userService") +public class UserServiceImpl implements UserService{ + @Resource + private UserDao userDao; + + @Override + public User getUserById(Integer id) { + return userDao.getUserById(id); + } + + @Override + public User findByUsername(String username) { + return userDao.findByUsername(username); + } + + @Override + public Set findRoles(String username) { + List roleList = userDao.findRoles(username); + Set roleSet=new HashSet<>(); + for(String role:roleList){ + roleSet.add(role); + } + return roleSet; + } + + @Override + public Set findPermissions(String username) { + List permsList=userDao.findPermissions(username); + Set permsSet=new HashSet<>(); + for(String permission:permsList){ + permsSet.add(permission); + } + return permsSet; + } + +} diff --git a/cg/src/main/resources/cache/ehcache.xml b/cg/src/main/resources/cache/ehcache.xml new file mode 100644 index 0000000..9537cc7 --- /dev/null +++ b/cg/src/main/resources/cache/ehcache.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + diff --git a/cg/src/main/resources/jdbc.properties b/cg/src/main/resources/jdbc.properties new file mode 100644 index 0000000..c3c2bba --- /dev/null +++ b/cg/src/main/resources/jdbc.properties @@ -0,0 +1,5 @@ +driver=com.mysql.jdbc.Driver +url=jdbc:mysql://localhost:3306/cg?useUnicode=true&characterEncoding=utf-8 +jdbc.username=root +password=123456 + diff --git a/cg/src/main/resources/logback.xml b/cg/src/main/resources/logback.xml new file mode 100644 index 0000000..7ed1686 --- /dev/null +++ b/cg/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/cg/src/main/resources/mapper/User.xml b/cg/src/main/resources/mapper/User.xml new file mode 100644 index 0000000..872a863 --- /dev/null +++ b/cg/src/main/resources/mapper/User.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/cg/src/main/resources/mybatis-config.xml b/cg/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000..c641d88 --- /dev/null +++ b/cg/src/main/resources/mybatis-config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/cg/src/main/resources/spring/spring-dao.xml b/cg/src/main/resources/spring/spring-dao.xml new file mode 100644 index 0000000..6337fe0 --- /dev/null +++ b/cg/src/main/resources/spring/spring-dao.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cg/src/main/resources/spring/spring-service.xml b/cg/src/main/resources/spring/spring-service.xml new file mode 100644 index 0000000..8bc2baa --- /dev/null +++ b/cg/src/main/resources/spring/spring-service.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cg/src/main/resources/spring/spring-shiro.xml b/cg/src/main/resources/spring/spring-shiro.xml new file mode 100644 index 0000000..04159b8 --- /dev/null +++ b/cg/src/main/resources/spring/spring-shiro.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /index=authc + /index2=user + /admin=roles["admin"] + /user=roles["user"] + /user/view=perms["user:view"] + /user/create=perms["user:create"] + /logout=logout + /** = anon + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cg/src/main/resources/spring/spring-web.xml b/cg/src/main/resources/spring/spring-web.xml new file mode 100644 index 0000000..dbfddb0 --- /dev/null +++ b/cg/src/main/resources/spring/spring-web.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + text/plain;charset=UTF-8 + text/html;charset=UTF-8 + application/json;charset=UTF-8 + + + + + + + \ No newline at end of file diff --git a/cg/src/main/webapp/WEB-INF/jsp/index.jsp b/cg/src/main/webapp/WEB-INF/jsp/index.jsp new file mode 100644 index 0000000..0108da7 --- /dev/null +++ b/cg/src/main/webapp/WEB-INF/jsp/index.jsp @@ -0,0 +1,12 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +Insert title here + + +

Hello World!Sn dragon

+ + \ No newline at end of file diff --git a/cg/src/main/webapp/WEB-INF/jsp/login.jsp b/cg/src/main/webapp/WEB-INF/jsp/login.jsp new file mode 100644 index 0000000..e822916 --- /dev/null +++ b/cg/src/main/webapp/WEB-INF/jsp/login.jsp @@ -0,0 +1,39 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<% String path=request.getContextPath(); %> + + + + +Insert title here + + +

登录

+ 用户名:
+ 密码:
+ + + + + + + \ No newline at end of file diff --git a/cg/src/main/webapp/WEB-INF/jsp/unauthorized.jsp b/cg/src/main/webapp/WEB-INF/jsp/unauthorized.jsp new file mode 100644 index 0000000..529d0c2 --- /dev/null +++ b/cg/src/main/webapp/WEB-INF/jsp/unauthorized.jsp @@ -0,0 +1,12 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +Insert title here + + +

未授权

+ + \ No newline at end of file diff --git a/cg/src/main/webapp/WEB-INF/sql/20170617cg.sql b/cg/src/main/webapp/WEB-INF/sql/20170617cg.sql new file mode 100644 index 0000000..750a1ec --- /dev/null +++ b/cg/src/main/webapp/WEB-INF/sql/20170617cg.sql @@ -0,0 +1,63 @@ +DROP DATABASE IF EXISTS `cg`; +CREATE DATABASE `cg`; +USE `cg`; +-- 用户表 +DROP TABLE IF EXISTS `user`; +create table `user`( + id INT PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(20) NOT NULL COMMENT '用户名', + password VARCHAR(255) NOT NULL COMMENT '密码' +)ENGINE=InnoDB DEFAULT CHARSET=UTF8; + +INSERT INTO `user` +VALUES +(1,'admin','$shiro1$SHA-512$1$$ujJTh2rta8ItSm/1PYQGxq2GQZXtFEq1yHYhtsIztUi66uaVbfNG7IwX9eoQ817jy8UUeX7X3dMUVGTioLq0Ew=='), +(2,'user','$shiro1$SHA-512$1$$ujJTh2rta8ItSm/1PYQGxq2GQZXtFEq1yHYhtsIztUi66uaVbfNG7IwX9eoQ817jy8UUeX7X3dMUVGTioLq0Ew=='); + +DROP TABLE IF EXISTS `role`; + +CREATE TABLE `role`( + id INT PRIMARY KEY AUTO_INCREMENT, + role_name VARCHAR(100) NOT NULL COMMENT '角色名称', + role_description VARCHAR(100) NULL COMMENT '角色描述' +) ENGINE=InnoDB DEFAULT CHARSET=UTF8; + +INSERT INTO role +VALUES(1,'admin','超级管理员'),(2,'user','普通用户'); + +DROP TABLE IF EXISTS `user_role`; +CREATE TABLE `user_role`( + id INT PRIMARY KEY AUTO_INCREMENT, + user_id INT NOT NULL, + role_id INT NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=UTF8; + +INSERT INTO user_role(user_id,role_id) +VALUES(1,1),(2,2); + +DROP TABLE IF EXISTS `permission`; +CREATE TABLE `permission`( + id INT PRIMARY KEY AUTO_INCREMENT, + permission_name VARCHAR(100) NOT NULL COMMENT '权限名', + permission_description VARCHAR(100) NULL COMMENT '权限描述' +) ENGINE=InnoDB DEFAULT CHARSET=UTF8; + +INSERT INTO permission +VALUES +(1,'user:view','查看用户信息'), +(2,'user:create','创建用户'), +(3,'user:update','修改用户信息'), +(4,'user:delete','删除用户'); + +DROP TABLE IF EXISTS role_permission; +CREATE TABLE `role_permission`( + id INT PRIMARY KEY AUTO_INCREMENT, + role_id INT NOT NULL, + permission_id INT NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=UTF8; + +INSERT INTO `role_permission` +(role_id,permission_id) +VALUES +(1,1),(1,2),(1,3),(1,4), +(2,1); \ No newline at end of file diff --git a/cg/src/main/webapp/WEB-INF/web.xml b/cg/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..6a6e0d9 --- /dev/null +++ b/cg/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,83 @@ + + + + + + contextConfigLocation + + classpath:spring/spring-dao.xml + classpath:spring/spring-service.xml + classpath:spring/spring-shiro.xml + + + + + + + SpringEncodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + SpringEncodingFilter + /* + + + + + + + org.springframework.web.context.ContextLoaderListener + + + + + + + shiroFilter + org.springframework.web.filter.DelegatingFilterProxy + true + + targetFilterLifecycle + true + + + + + + + + shiroFilter + /* + + + + dispatcherServlet + + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + classpath:spring/spring-web.xml + + 1 + + + dispatcherServlet + + / + + + + + diff --git a/cg/src/main/webapp/index.jsp b/cg/src/main/webapp/index.jsp new file mode 100644 index 0000000..c38169b --- /dev/null +++ b/cg/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

Hello World!

+ + diff --git a/cg/src/main/webapp/login.jsp b/cg/src/main/webapp/login.jsp new file mode 100644 index 0000000..e822916 --- /dev/null +++ b/cg/src/main/webapp/login.jsp @@ -0,0 +1,39 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<% String path=request.getContextPath(); %> + + + + +Insert title here + + +

登录

+ 用户名:
+ 密码:
+ + + + + + + \ No newline at end of file diff --git a/cg/src/main/webapp/static/js/lib/jquery.min.js b/cg/src/main/webapp/static/js/lib/jquery.min.js new file mode 100644 index 0000000..e5ace11 --- /dev/null +++ b/cg/src/main/webapp/static/js/lib/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="
","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b) +},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("