# 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(用户登陆式图书预约系统)
小伙伴们开车了!
话不多说先让我们看看此项目运行起来的效果,方便大家有个直观了解,好在事先估计要用到那些技术,方便大家有针对性的学习。
看图!
这是图书管理系统的首页,不用登陆即可查看、搜索系统有已有图书。

搜索想要预定的书:

点击详情,进入预定页面,如果没有登陆的话会弹出登陆层,提示输入学号密码,如果用户已经在之前查看某本书的详细时已经登陆,则不会要求再登陆,这里的用户名密码与数据库验证成功后保存在cookie中,且生命周期默认与session相同,即关闭路径窗口后就失效。

这是什么都不输入和输入格式不对时,点击submit的提示:



登陆后进入预约界面,点击预约后的界面:

如果已经对当前书预约过,点击预约会提示重复预约,如果没有库存,点击预约会提示没有库存。

预约后点击:查看我的已预约书籍,会显示之前所有预约的信息:

## 1、利用maven创建文件路径:
利用命令行工具输入:mvn archetype:generate -DgroupId=cn.nize -DarchetypeArtifactId=
maven-archetype-webapp -DarchetypeCatalog=internal
## 2、创建项目包:

## 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吧,这篇东西写了整整一个上午也不容易啊。
爱你们,么么哒!