# ssm-BookAppointment **Repository Path**: guapik/ssm-BookAppointment ## Basic Information - **Project Name**: ssm-BookAppointment - **Description**: 优雅整合SSM框架:SpringMVC + Spring + MyBatis(用户登陆式图书预约系统) - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 0 - **Created**: 2019-04-22 - **Last Updated**: 2021-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ssm-BookAppointment
优雅整合SSM框架:SpringMVC + Spring + MyBatis(用户登陆式图书预约系统)
小伙伴们开车了!
话不多说先让我们看看此项目运行起来的效果,方便大家有个直观了解,好在事先估计要用到那些技术,方便大家有针对性的学习。
看图!
这是图书管理系统的首页,不用登陆即可查看、搜索系统有已有图书。
![image](https://github.com/nize1989/pic/blob/master/raw/1.png) 搜索想要预定的书:
![image](https://github.com/nize1989/pic/blob/master/raw/2.png) 点击详情,进入预定页面,如果没有登陆的话会弹出登陆层,提示输入学号密码,如果用户已经在之前查看某本书的详细时已经登陆,则不会要求再登陆,这里的用户名密码与数据库验证成功后保存在cookie中,且生命周期默认与session相同,即关闭路径窗口后就失效。
![image](https://github.com/nize1989/pic/blob/master/raw/3.png) 这是什么都不输入和输入格式不对时,点击submit的提示:
![image](https://github.com/nize1989/pic/blob/master/raw/4.png) ![image](https://github.com/nize1989/pic/blob/master/raw/5.png) ![image](https://github.com/nize1989/pic/blob/master/raw/6.png) 登陆后进入预约界面,点击预约后的界面:
![image](https://github.com/nize1989/pic/blob/master/raw/7.png) 如果已经对当前书预约过,点击预约会提示重复预约,如果没有库存,点击预约会提示没有库存。
![image](https://github.com/nize1989/pic/blob/master/raw/8.png) 预约后点击:查看我的已预约书籍,会显示之前所有预约的信息:
![image](https://github.com/nize1989/pic/blob/master/raw/9.png) ## 1、利用maven创建文件路径:
利用命令行工具输入:mvn archetype:generate -DgroupId=cn.nize -DarchetypeArtifactId= maven-archetype-webapp -DarchetypeCatalog=internal
## 2、创建项目包:
![image](https://github.com/nize1989/pic/blob/master/raw/11.png) ## 3、再然后就是在pom.xml里面注入依赖,maven会自动在网站上下载。关于maven大家可以看看慕课网上的教学视频。 pom.xml ```java 4.0.0 cn.nize ssm-BookAppointment 1.0-SNAPSHOT ssm-BookAppointment Maven Webapp http://maven.apache.org junit junit 4.11 ch.qos.logback logback-classic 1.1.1 mysql mysql-connector-java 5.1.35 c3p0 c3p0 0.9.1.2 org.mybatis mybatis 3.3.0 org.mybatis mybatis-spring 1.2.3 taglibs standard 1.1.2 jstl jstl 1.2 com.fasterxml.jackson.core jackson-databind 2.5.0 com.fasterxml.jackson.core jackson-core 2.5.0 com.fasterxml.jackson.core jackson-annotations 2.5.0 javax.servlet javax.servlet-api 3.1.0 org.springframework spring-core 4.1.7.RELEASE org.springframework spring-beans 4.1.7.RELEASE org.springframework spring-context 4.1.7.RELEASE org.springframework spring-jdbc 4.1.7.RELEASE org.springframework spring-tx 4.1.7.RELEASE org.springframework spring-web 4.1.7.RELEASE org.springframework spring-webmvc 4.1.7.RELEASE org.springframework spring-test 4.1.7.RELEASE redis.clients jedis 2.7.3 com.dyuproject.protostuff protostuff-core 1.0.8 com.dyuproject.protostuff protostuff-runtime 1.0.8 commons-collections commons-collections 3.2 ssm-BookAppointment ``` ## 4、做好之前的准备工作后,逻辑理顺,现在就要开始编码啦,首先我们创建数据库!
根据我们的实际情况,在数据库中创建表:
schema.sql ```java -- 创建图书表 CREATE TABLE `book` ( `book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID', `name` varchar(100) NOT NULL COMMENT '图书名称', `introd` varchar(1000) NOT NULL COMMENT '简介', `number` int(11) NOT NULL COMMENT '馆藏数量', PRIMARY KEY (`book_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='图书表'; -- 初始化图书数据 INSERT INTO `book` (`book_id`, `name`, `introd`,`number`) VALUES (1000, 'Java程序设计', 'Java程序设计是一门balbal',10), (1001, '数据结构','数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。', 10), (1002, '设计模式','设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。',10), (1003, '编译原理','编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。',10), (1004,'大学语文','基于长期的教学实践和学科思考,我们编写了这本《大学语文》教材balbal',10), (1005,'计算方法','计算方法又称“数值分析”。是为各种数学问题的数值解答研究提供最有效的算法。',10), (1006,'高等数学','广义地说,初等数学之外的数学都是高等数学,也有将中学较深入的代数、几何以及简单的集合论初步balbal',10); -- 创建预约图书表 CREATE TABLE `appointment` ( `book_id` bigint(20) NOT NULL COMMENT '图书ID', `student_id` bigint(20) NOT NULL COMMENT '学号', `appoint_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '预约时间' , PRIMARY KEY (`book_id`, `student_id`), INDEX `idx_appoint_time` (`appoint_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='预约图书表'; -- 创建学生数据表 CREATE TABLE `student`( `student_id` bigint(20) NOT NULL COMMENT '学生ID', `password` bigint(20) NOT NULL COMMENT '密码', PRIMARY KEY(`student_id`) )ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生统计表'; -- 初始化学生数据 INSERT INTO `student`(`student_id`,`password`) VALUES (3211200801,346543), (3211200802,754323), (3211200803,674554), (3211200804,542344), (3211200805,298383), (3211200806,873973), (3211200807,193737), (3211200808,873092); ``` ## 5 根据数据库对象创建实体类。
第一个开始填充的类当然是entiy包,他是承接我们从数据库里去除数据的类,或者把该类存入数据库,在这里我们创建两个类,一个是Book包(从数据库取出书后放入该包),一个是Appointment(存放从数据库取出的预约书籍信息)。
Book.java ```java package com.imooc.appoint.entiy; public class Book { private long bookId;//图书ID private String name;//图书名称 private int number;//馆藏数量 private String introd; public Book() { //为什么要有个无参构造器 } public Book(long bookId, String name, int number) { this.bookId = bookId; this.name = name; this.number = number; } public long getBookId() { return bookId; } public void setBookId(long bookId) { this.bookId = bookId; } 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 String getIntrod() { return introd; } public void setIntrod(String introd) { this.introd = introd; } @Override public String toString() { return "Book [bookId=" + bookId + ", name=" + name + ", number=" + number + ", introd=" + introd + "]"; } } ``` Appointment.java ```java package com.imooc.appoint.entiy; import java.util.Date; public class Appointment { private long bookId; private long studentId; private Date appointTime; // 多对一的复合属性 private Book book;// 图书实体 public Appointment(){ } public Appointment(long bookId, long studentId, Date appointTime) { this.bookId = bookId; this.studentId = studentId; this.appointTime = appointTime; } public long getBookId() { return bookId; } public void setBookId(long bookId) { this.bookId = bookId; } public long getStudentId() { return studentId; } public void setStudentId(long studentId) { this.studentId = studentId; } public Date getAppointTime() { return appointTime; } public void setAppointTime(Date appointTime) { this.appointTime = appointTime; } public Book getBook() { return book; } public void setBook(Book book) { this.book = book; } @Override public String toString() { return "Appointment [bookId=" + bookId + ", studentId=" + studentId + ", appointTime=" + appointTime + ", book=" + book + "]"; } } ``` 因为我们还打算做一个学生ID、密码验证,所以也得有学生类.
Student.java ```java package com.imooc.appoint.entiy; public class Student { private Long studentId; private Long password; public Student(){ } public Student(Long studentId,Long password){ this.studentId=studentId; this.password=password; } public Long getStudentId() { return studentId; } public void setStudentId(Long studentId) { this.studentId = studentId; } public Long getPassword() { return password; } public void setPassword(Long password) { this.password = password; } @Override public String toString() { return "Student [studentId=" + studentId + ", password=" + password + "]"; } } ``` ## 6创建DAO中的类
DAO包中的类主要功能是实现与数据库交互,所以我们需要大的逻辑是:1、学生类与数据库交互;2、预约是与数据库交互;3、查询书时与时与数据库交互。这里大致分为几类,等具体写接口时再去细写。还有就是注意,DAO只是与数据库交互,不能写交互组成的逻辑,这个放在service里面。我们应该站在用户的角度设计DAO接口,至于具体怎么用,后面再写。
BookDao.java ```java package com.imooc.appoint.dao; import java.util.List; import org.apache.ibatis.annotations.Param; import com.imooc.appoint.entiy.Book; public interface BookDao { /* * 根据id查询书 * */ Book queryById(long id); List querySome(String name); List queryAll(@Param("offset") int offset,@Param("limit") int limit); /*减少管存数量 * 用返回值可判断当前库存是否还有书 */ int reduceNumber(long bookId); } ``` AppointmentDao.java ```java package com.imooc.appoint.dao; import java.util.List; import org.apache.ibatis.annotations.Param; import com.imooc.appoint.entiy.Appointment; public interface AppointmentDao { //通过图书ID和学生ID预约书籍,并插入 int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId); //通过一个学生ID查询已经预约了哪些书。 List quaryAndReturn(long studentId); } ``` StudentDao.java ```java package com.imooc.appoint.dao; import org.apache.ibatis.annotations.Param; import com.imooc.appoint.entiy.Student; public interface StudentDao { /** * 向数据库验证输入的密码是否正确 */ Student quaryStudent(@Param("studentId") long studentId, @Param("password") long password); } ``` ## 7我们暂时先不管以上接口实现,先为DAO配置入框架,实现spring与mybatis的整合。
因为spring的配置太多,我们这里分三层,分别是dao service web。我们这里先写spring-dao.xml,其他的等我们实现相应包后再去配置。 配置时,主要注意一下几个方面:
1、读入数据库连接相关参数(可选)
2、配置数据连接池
3、配置连接属性,可以不读配置项文件直接在这里写死
4、配置c3p0,只配了几个常用的
5、配置SqlSessionFactory对象(mybatis)
6、扫描dao层接口,动态实现dao接口,也就是说不需要daoImpl,sql和参数都写在xml文件上
spring-dao.xml ```java ``` ### 配置jdbc.properties文件
```java jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/ssmbookappoint?useUnicode=true&characterEncoding=utf8 jdbc.username= jdbc.password= ``` 第一次上传忘记去掉用户名密码了,好险!大家用的时候填上自己的用户名、密码。
### 配置mybatis。
所以需要配置mybatis核心文件,在recources文件夹里新建mybatis-config.xml文件。
1、使用自增主键
2、使用列别名
3、开启驼峰命名转换 create_time -> createTime
mybatis-config.xml ```java ``` ## 8配置实现接口的xml
我们要开始写DAO接口的实现类,因为我们配置扫描sql配置文件路径是:mapper下的xml,所有我们应该在resourse下新建mapper文件夹,在这里存放实现DAO接口的各种xml
BookDao.xml ```java UPDATE book SET number = number - 1 WHERE book_id = #{bookId} AND number > 0 ``` AppointmentDao.xml ```java INSERT ignore INTO appointment (book_id, student_id) VALUES (#{bookId}, #{studentId}) ``` StudentDao.java ```java ``` 到此为止,我们dao层的开发就算结束了,大家可以写个测试类,测试一下。(我没写,哈哈),要实现预约功能还得有更多详细的逻辑组织,这个功能将他放在service中,他把DAO中与数据库交互的功能组织成可以用的详细逻辑。提供给上层web调用。
## 8 service层
### 首先写service接口类
BookService.java ```java package com.imooc.appoint.service; import java.util.List; import com.imooc.appoint.dto.AppointExecution; import com.imooc.appoint.entiy.Appointment; import com.imooc.appoint.entiy.Book; import com.imooc.appoint.entiy.Student; public interface BookService { /** * 查询一本图书 * * @param bookId * @return */ Book getById(long bookId); /** * 查询所有图书 * * @return */ List getList(); /** * 登陆时查询数据库是否有该学生记录。 */ Student validateStu(Long studentId,Long password); /**按照图书名称查询 * 按条件搜索图书 * */ List getSomeList(String name); /* * 查看某学生预约的所有图书 * */ List getAppointByStu(long studentId); /** * 预约图书 * * @param bookId * @param studentId * @return */ AppointExecution appoint(long bookId, long studentId);//返回预约成功的实体类 } ``` 大家可以看到,这个借口类中基本和DAO中的没啥区别,有区别的是某些类他是在dao上更进一步,需要多个dao类一起组织,或者在加入其它逻辑才能实现
为了实现BookService的借口,我们得写BookServiceImpl类。但是想让我们想想,为了写BookServiceImpl,我们需要什么,上面我们已经写出预约成功的实体类是AppointExecution,所以当然我们得写出该类,因为该类交互service和web,对这类有点像entiy包我们管叫bto包(bto包和其他包一起存放在appoint下)。对于AppointExecution来说,作用就是预约成功后给web层提供返回的信息。(就是返回预约成功、预约失败、无库存、之类的信息)
```java package com.imooc.appoint.dto; import com.imooc.appoint.enums.AppointStateEnum; public class AppointExecution { // 图书ID private long bookId; // 预约结果状态 private int state; // 状态标识 private String stateInfo; public AppointExecution() { } // 预约失败的构造器 public AppointExecution(long bookId, AppointStateEnum stateEnum) { this.bookId = bookId; this.state = stateEnum.getState(); this.stateInfo = stateEnum.getStateInfo(); } //set get 方法! public long getBookId() { return bookId; } public void setBookId(long bookId) { this.bookId = bookId; } 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; } @Override public String toString() { return "AppointExecution [bookId=" + bookId + ", state=" + state + ", stateInfo=" + stateInfo+"]"; } } ``` 除此之外,我们在预约图书时可能出现异常,例如重复预约、无库存、和其他异常,我们需要事先设计好异常类,来接收这类异常,方便处理,而不是直接报错。因此在appoint包下新建excption包,报下新建三个类:AppoinException.java;NoNumberException.java;RepeatAppoint.java
AppoinException.java ```java package com.imooc.appoint.exception; //预约义务异常 public class AppointException extends RuntimeException{ public AppointException(String message) { super(message); } public AppointException(String message, Throwable cause) { super(message, cause); } } ``` NoNumberException.java ```java package com.imooc.appoint.exception; //库存不足异常 public class NoNumberException extends RuntimeException{ public NoNumberException(String message) { super(message); } public NoNumberException(String message, Throwable cause) { super(message, cause); } } ``` RepeatAppoint.java ```java package com.imooc.appoint.exception; //重复预约异常 public class RepeatAppointException extends RuntimeException{ public RepeatAppointException(String message) { super(message); } public RepeatAppointException(String message, Throwable cause) { super(message, cause); } } ``` 现在可以写接口的实现类啦:BookServiceImpl.java
```java package com.imooc.appoint.service.Impl; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.imooc.appoint.dao.AppointmentDao; import com.imooc.appoint.dao.BookDao; import com.imooc.appoint.dao.StudentDao; import com.imooc.appoint.dto.AppointExecution; import com.imooc.appoint.entiy.Appointment; import com.imooc.appoint.entiy.Book; import com.imooc.appoint.entiy.Student; import com.imooc.appoint.enums.AppointStateEnum; import com.imooc.appoint.exception.AppointException; import com.imooc.appoint.exception.NoNumberException; import com.imooc.appoint.exception.RepeatAppointException; import com.imooc.appoint.service.BookService; @Service public class BookServiceImpl implements BookService{ private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private BookDao bookDao; @Autowired private AppointmentDao appointmentDao; @Autowired private StudentDao studentDao; @Override public Book getById(long bookId) { return bookDao.queryById(bookId); } @Override public List getList() { return bookDao.queryAll(0, 1000); } @Override public Student validateStu(Long studentId,Long password){ return studentDao.quaryStudent(studentId, password); } @Override public List getSomeList(String name) { return bookDao.querySome(name); } @Override public List getAppointByStu(long studentId) {//DOTO return appointmentDao.quaryAndReturn(studentId); } @Override @Transactional public AppointExecution appoint(long bookId, long studentId) {//在Dao的基础上组织逻辑,形成与web成交互用的方法 try{ //返回成功预约的类型。 int update=bookDao.reduceNumber(bookId);//减库存 if(update<=0){//已经无库存! throw new NoNumberException("no number"); }else{ //执行预约操作 int insert=appointmentDao.insertAppointment(bookId, studentId); if(insert<=0){//重复预约 throw new RepeatAppointException("repeat appoint"); }else{//预约成功 return new AppointExecution(bookId,AppointStateEnum.SUCCESS); } } } catch (NoNumberException e1) { throw e1; } catch (RepeatAppointException e2) { throw e2; } catch (Exception e) { logger.error(e.getMessage(), e); // 所有编译期异常转换为运行期异常 throw new AppointException("appoint inner error:" + e.getMessage()); } } } ``` ### 又到了我们写service层配置的时候!
该xml依然位于resourse下的spring包下
spring-service.xml ```java ``` ## 9web层
现在我们一起看看web层该如何组织。
在写具体的代码之前,先配置web层,在这里主要实现的作用是:
1、开启SpringMVC注解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
2、对静态资源处理,如js,css,jpg等
3、配置jsp 显示ViewResolver,例如在controller中某个方法返回一个string类型的"login",实际上会返回"/WEB-INF/login.jsp"
4、扫描web层 @Controller
详细见xml中每一步的注释
spring-web.xml
```java ``` 当然我们必须修改web.xml文件,它在webapp的WEB-INF下,这个文件配置servlet,并初始化,指定扫描spring相关的配置,最后我们将所有的url都拦截下来。 web.xml
```java BookAppointment-dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring/spring-*.xml BookAppointment-dispatcher / ``` 因为在运行时,我们经常会查看日志什么的,所以我们把这个配置进去
logback.xml ```java %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ``` 最后我们开始组织我们web的逻辑,也就是具体的controller层代码的编写。
这一层中与我们的前段联系的较为紧密,大家可以边写边看效果,再调试。 ```java package com.imooc.appoint.web; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.ibatis.annotations.Param; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; 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 com.imooc.appoint.dto.AppointExecution; import com.imooc.appoint.dto.Result; import com.imooc.appoint.entiy.Appointment; import com.imooc.appoint.entiy.Book; import com.imooc.appoint.entiy.Student; import com.imooc.appoint.enums.AppointStateEnum; import com.imooc.appoint.service.BookService; import com.imooc.appoint.exception.RepeatAppointException; import com.imooc.appoint.exception.NoNumberException; @Controller @RequestMapping("/books") public class BookController { private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private BookService bookService; //获取图书列表 @RequestMapping(value="/list",method = RequestMethod.GET) private String List(Model model){ List list = bookService.getList(); model.addAttribute("list", list); return "list"; } //搜索是否有某图书 @RequestMapping(value="/search",method = RequestMethod.POST) private void search(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException{ //接收页面的值 String name=req.getParameter("name"); name=name.trim(); //向页面传值 req.setAttribute("name", name); req.setAttribute("list", bookService.getSomeList(name)); req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req, resp); } //查看某图书的详细情况 @RequestMapping(value = "/{bookId}/detail", method = RequestMethod.GET) private String detail(@PathVariable("bookId") Long bookId, Model model){ if(bookId==null){ return "redict:/book/list"; } Book book=bookService.getById(bookId); if(book==null){ return "forward:/book/list"; } model.addAttribute("book",book); System.out.println(book); return "detail"; } //验证输入的用户名、密码是否正确 @RequestMapping(value="/verify", method = RequestMethod.POST, produces = { "application/json; charset=utf-8" }) @ResponseBody private Map validate(Long studentId,Long password){ //(HttpServletRequest req,HttpServletResponse resp){ Map resultMap=new HashMap(); Student student =null; System.out.println("验证函数"); student =bookService.validateStu(studentId,password); System.out.println("输入的学号、密码:"+studentId+":"+password); System.out.println("查询到的:"+student.getStudentId()+":"+student.getPassword()); if(student!=null){ System.out.println("SUCCESS"); resultMap.put("result", "SUCCESS"); return resultMap; }else{ resultMap.put("result", "FAILED"); return resultMap; } } //执行预约的逻辑 @RequestMapping(value = "/{bookId}/appoint", method = RequestMethod.POST, produces = { "application/json; charset=utf-8" }) @ResponseBody private Result execute(@PathVariable("bookId") Long bookId,@RequestParam("studentId") Long studentId){ Result result; AppointExecution execution=null; try{//手动try catch,在调用appoint方法时可能报错 execution=bookService.appoint(bookId, studentId); result=new Result(true,execution); return result; } catch(NoNumberException e1) { execution=new AppointExecution(bookId,AppointStateEnum.NO_NUMBER); result=new Result(true,execution); return result; }catch(RepeatAppointException e2){ execution=new AppointExecution(bookId,AppointStateEnum.REPEAT_APPOINT); result=new Result(true,execution); return result; }catch (Exception e){ execution=new AppointExecution(bookId,AppointStateEnum.INNER_ERROR); result=new Result(true,execution); return result; } } @RequestMapping(value ="/appoint") private String appointBooks(@RequestParam("studentId") long studentId,Model model){ List appointList=new ArrayList(); appointList=bookService.getAppointByStu(studentId); model.addAttribute("appointList", appointList); return "appointBookList"; } } ``` 为了把执行预约逻辑是否返回的不同信息封装起来,我们创建一个Result类,它是类型T的集合。
Result.java ```java package com.imooc.appoint.dto; /** * 封装json对象,所有返回结果都使用它 */ public class Result { private boolean success;// 是否成功标志 private T data;// 成功时返回的数据 private String error;// 错误信息 public Result() { } // 成功时的构造器 public Result(boolean success, T data) { this.success = success; this.data = data; } // 错误时的构造器 public Result(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; } } ``` 为了给web显示预约后的反馈信息,我们建立一个常量数据字典类存放这些要反馈给客户的信息
AppointStateEnum.java ```java package com.imooc.appoint.enums; //使用枚举表述常量数据字典,我们先定义几个预约图书操作返回码的数据字典,也就是我们要返回给客户端的信息。 public enum AppointStateEnum { SUCCESS(1, "预约成功"), NO_NUMBER(0, "库存不足"), REPEAT_APPOINT(-1, "重复预约"), INNER_ERROR(-2, "系统异常"); private int state; private String stateInfo; private AppointStateEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public int getState() { return state; } public String getStateInfo() { return stateInfo; } public static AppointStateEnum stateOf(int index) { for (AppointStateEnum state : values()) { if (state.getState() == index) { return state; } } return null; } } ``` 到此为止,我们后端写的差不多了,下面是前段页面的开发,其实前段和controller中的方法开发是相互嵌套的,在写方法的同时,写js或者jsp文件。这样逻辑才能连贯。再此用bootstrap这种轻量一站式框架开发前段,能死前端技术不怎么好的童鞋也能开发出还看得过去的页面。
话不多少,我们上代码:
list.jsp ```java <%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp"%> 图书列表 <%@include file="common/head.jsp" %>

图书列表

图书名称:
图书ID 图书名称 馆藏数量 详细
${sk.bookId} ${sk.name} ${sk.number} 详细
``` detail.jsp ```java <%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp" %> 预约详情页 <%@include file="common/head.jsp" %>

图书详情

图书ID 图书名称 图书简介 剩余数量 预约数量
${book.bookId} ${book.name} ${book.introd} ${book.number } 1
<%--jQery文件,务必在bootstrap.min.js之前引入--%> <%--使用CDN 获取公共js http://www.bootcdn.cn/--%> <%--jQuery Cookie操作插件--%> <%--jQuery countDown倒计时插件--%> ``` bookappointment.js ```javascript var bookappointment={ //封装相关ajax的url URL:{ appoint:function(bookId,studentId){ return '/books/'+bookId+'/appoint?studentId='+studentId; }, verify:function(){ return '/books'+'/verify'; } }, //验证学号和密码 validateStudent:function(studentId,password){ console.log("studentId"+studentId); if(!studentId||!password){ return "nothing"; }else if(studentId.length!=10 ||isNaN(studentId)||password.length!=6 ||isNaN(password)){ return "typerror"; }else { if(bookappointment.verifyWithDatabase(studentId, password)){ console.log("验证成功!"); return "success"; }else{ console.log("验证失败!"); return "mismatch"; } } }, //将学号和用户名与数据库匹配 verifyWithDatabase:function(studentId,password){ var result=false; var params={}; params.studentId=studentId; params.password=password; console.log("params.password:"+params.password); var verifyUrl=bookappointment.URL.verify(); $.ajax({ type:'post', url:verifyUrl, data:params, datatype:'josn', async:false, //同步调用,保证先执行result=true,后再执行return result; success:function(data){ if(data.result=='SUCCESS'){ window.location.reload(); //弹出登录成功! alert("登陆成功!"); result=true; }else{ result=false; } } }); console.log("我是验证结果:"+result); return result; }, //预定图书逻辑 detail:{ //预定也初始化 init:function(params){ var bookId=params['bookId']; console.log("我是js文件!"); var studentId=$.cookie('studentId'); var password=$.cookie('password'); if(!studentId||!password){ //设置弹出层属性 var IdAndPasswordModal=$('#varifyModal'); IdAndPasswordModal.modal({ show: true,//显示弹出层 backdrop: 'static',//禁止位置关闭 keyboard: false//关闭键盘事件 }); $('#studentBtn').click(function (){ studentId=$('#studentIdKey').val(); console.log("studentId:"+studentId); password=$('#passwordKey').val(); console.log("password:"+password); //调用validateStudent函数验证用户id和密码。 var temp=bookappointment.validateStudent(studentId,password); console.log(temp); if(temp=="nothing"){ $('#studentMessage').hide().html('').show(300); }else if(temp=="typerror"){ $('#studentMessage').hide().html('').show(300); }else if(temp=="mismatch"){ console.log("已经调用验证函数!"); $('#studentMessage').hide().html('').show(300); }else if(temp=="success"){ //学号与密码匹配正确,将学号密码保存在cookie中。不设置cookie过期时间,这样即为session模式,关闭浏览器就不保存密码了。 $.cookie('studentId', studentId, { path: '/books'}); $.cookie('password', password, { path: '/books'}); // 跳转到预约逻辑 var appointbox=$('#appoint-box'); bookappointment.appointment(bookId,studentId,appointbox); } }); }else{ var appointbox=$('#appoint-box'); bookappointment.appointment(bookId,studentId,appointbox); } } }, appointment:function(bookId,studentId, node){ console.log("我执行预约的方法!" ); node.html(''); var appointmentUrl = bookappointment.URL.appoint(bookId,studentId); console.log("appointmentUrl:"+appointmentUrl); //绑定一次点击事件 $('#appointmentBtn').one('click', function () { //执行预约请求 //1、先禁用请求 $(this).addClass('disabled'); //2、发送预约请求执行预约 $.post(appointmentUrl,{},function(result){ //Ajax强大之处,向Controller方法提出请求和返回结果在一处! if(result && result['success']){ //同时还可以连续取对象的子对象! var appointResult=result['data']; console.log("appointResult"+appointResult); var state=appointResult['state']; console.log("state"+state); var stateInfo=appointResult['stateInfo']; console.log("stateInfo"+stateInfo); //显示预约结果 把结果显示给页面,完成了jsp的工作 node.html(''+stateInfo+''); } //因为公用一个node所以,用来显示“stateInfo”时就不会显示上面的“预约” console.log('result'+result); }); }); } } ``` appointBookList.jsp ```java <%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp"%> 预约图书列表 <%@include file="common/head.jsp" %>

您已预约图书列表

预定学号 预约时间 图书ID 图书名称 图书简介 预约数量
${sk.studentId} ${sk.appointTime} ${sk.bookId} ${sk.book.getName()} ${sk.book.getIntrod()} 1
``` 到此为止,所有的开发就已经结束,已经把所有源代码上传至github,需要的可以去下载,喜欢就给个star吧,这篇东西写了整整一个上午也不容易啊。
爱你们,么么哒!