PS:关于接口定义
我们使用到的技术:
前端:”是自己在网上找的代码
后端
- IDEA
- Maven
- MySQL+Mybatis
- SSM
实现的功能:
- 用户登录
- 添加图书
- 显示图书列表
- 更新图书
- 删除图书
- 批量删除图书
- 强制登录
- 引入统一功能
预览整体项目
一、建立数据库
1.1配置数据库 & 日志
server: #端口号配置
port: 1208
spring:
datasource: # 数据库连接配置
url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=false
username: root
password: 12345678
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 配置打印 MyBatis⽇志
map-underscore-to-camel-case: true #配置驼峰⾃动转换
mapper-locations: classpath:mapper/**Mapper.xml # 配置mybatis xml的⽂件路径,在resources/mapper创建所有表的xml⽂件
# classpath对应resources这个目录,接下来说明在mapper这个文件夹下面,以Mapper.xml结束的都可以被加载
# 设置⽇志⽂件的⽂件名
logging:
file:
name: spring-book.log
DROP DATABASE IF EXISTS book_test;
CREATE DATABASE book_test DEFAULT CHARACTER SET utf8mb4;
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
`id` INT NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR ( 128 ) NOT NULL,
`password` VARCHAR ( 128 ) NOT NULL,
`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` ), --将 id 列设置为主键,保证每条记录唯一
UNIQUE INDEX `user_name_UNIQUE` ( `user_name` ASC )
--指定了索引的列以及排序方式。在此例中,user_name 列按照升序(ASC)排序。
--默认是升序,因此这里 ASC 是可选的。
)
ENGINE = INNODB --设置存储引擎为 InnoDB,支持事务和外键
DEFAULT CHARACTER SET = utf8mb4 COMMENT = '⽤⼾表';
DROP TABLE IF EXISTS book_info;
CREATE TABLE `book_info` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`book_name` VARCHAR ( 127 ) NOT NULL,
`author` VARCHAR ( 127 ) NOT NULL,
`count` INT ( 11 ) NOT NULL,
`price` DECIMAL (7,2 ) NOT NULL,
`publish` VARCHAR ( 256 ) NOT NULL,
`status` TINYINT ( 4 ) DEFAULT 1 COMMENT '0-⽆效, 1-正常, 2-不允许借阅',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 初始化数据
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "admin", "admin" );
INSERT INTO user_info ( user_name, PASSWORD ) VALUES ( "zhangsan", "123456" );
-- 初始化图书数据
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES
('活着', '余华', 29, 22.00, '北京⽂艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES
('平凡的世界', '路遥', 5, 98.56, '北京⼗⽉⽂艺出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES
('三体', '刘慈欣', 9, 102.67, '重庆出版社');
INSERT INTO `book_info` (book_name,author,count, price, publish) VALUES
('⾦字塔原理', '⻨肯锡', 16, 178.00, '⺠主与建设出版社');
二、创建实体类
2.1 用户实体类UserInfo实体类
package com.qiyangyang.springbook.demos.model;
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Integer delete_flag;
private Date createTime;
private Date updateTime;
}
2.2 图书实体类 BookInfo实体类
package com.qiyangyang.springbook.demos.model;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class BookInfo {
private Integer id;
private String bookName;
private String author;
private Integer count;
private BigDecimal price;
private String publish;
private Integer status;//1-可借阅 2-不可借阅 0-已删除
private String stateCN;//根据book状态设置描述
private Date createTime;
private Date updateTime;
}
三、实现用户登录接口
3.1约定前后端交互接口
[请求]
/user/login
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[参数]
name=zhangsan&password=123456
[响应]
true //账号密码验证正确, 否则返回false
3.2实现(后端)服务器代码
3.2.1控制层
创建UserController
package com.qiyangyang.springbook.demos.controller;
import com.qiyangyang.springbook.demos.model.UserInfo;
import com.qiyangyang.springbook.demos.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public boolean login(String userName, String password, HttpSession session){
/**
* 校验参数
*/
if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
return false;
}
//判断用户输入的密码和用户输入的密码是否一致
//我们需要查询数据库,得到数据库的密码
UserInfo userInfo = userService.queryByName(userName);
if(userInfo == null){
//用户不存在
return false;
}
if(password.equals(userInfo.getPassword())){
//将密码置为空,不置为空也可以,因为Session是在服务器上的数据,客户端也看不到
//session也是占内存的因此,不需要的不存。
/**
* 如果密码正确
* 存Session
*/
//由于密码信息是没用的,用不到。我们设置成空。不设置成空也行,因为存在服务器。客户端是看不到的
userInfo.setPassword("");
session.setAttribute("user_session",userInfo);
return true;
}
return false;
}
}
/**
package com.qiyangyang.springbook.demos.controller;
import com.qiyangyang.springbook.demos.model.UserInfo;
import com.qiyangyang.springbook.demos.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public boolean login(String userName, String password, HttpSession session){
/**
* 校验参数
*/
if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
return false;
}
//判断用户输入的密码和用户输入的密码是否一致
//我们需要查询数据库,得到数据库的密码
UserInfo userInfo = userService.queryByName(userName);
if(userInfo == null){
//用户不存在
return false;
}
if(password.equals(userInfo.getPassword())){
//将密码置为空,不置为空也可以,因为Session是在服务器上的数据,客户端也看不到
//session也是占内存的因此,不需要的不存。
/**
* 如果密码正确
* 存Session
*/
//由于密码信息是没用的,用不到。我们设置成空。不设置成空也行,因为存在服务器。客户端是看不到的
userInfo.setPassword("");
session.setAttribute("user_session",userInfo);
return true;
}
return false;
}
}
/**
* HttpSession session
* 存的是根据SessionId获取的Session对象,每一个sessionId对应一个session对象。
* 会根据客户端等等判断是新用户还是老用户。服务器里面存了很多session。
* SessionId在后端通过服务器生成并通过 Cookie 发送给客户端。
*
*/
*
*/
3.2.2 业务层
创建UserService
package com.qiyangyang.springbook.demos.service;
import com.qiyangyang.springbook.demos.mapper.UserInfoMapper;
import com.qiyangyang.springbook.demos.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
/**
* 从数据库中查询用户信息
* @param userName
* @return
*/
public UserInfo queryByName(String userName){
return userInfoMapper.queryByName(userName);
}
}
3.2.3数据层
创建UserInfoMapper
package com.qiyangyang.springbook.demos.mapper;
import com.qiyangyang.springbook.demos.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserInfoMapper {
@Select("select * from user_info where delete_flag = 0 and user_name=#{userName}")
UserInfo queryByName(String userName);
}
3.3后端校验
3.4实现前端代码
3.4.1 ajax 部分
<script src="js/jquery.min.js"></script>
<script>
function login() {
$.ajax({
type:"post",
url:"/user/login",
data:{
userName:$("#userName").val(),
password:$("#password").val()
},
success:function (result) {
if(result===true){
//验证成功
location.href = "book_list.html";
}else {
alert("登录失败,用户名不存在或密码错误!");
}
}
});
}
</script>
3.4.2完整前端登录代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<div class="container-login">
<div class="container-pic">
<img src="pic/computer.png" width="350px">
</div>
<div class="login-dialog">
<h3>登陆</h3>
<div class="row">
<span>用户名</span>
<input type="text" name="userName" id="userName" class="form-control">
</div>
<div class="row">
<span>密码</span>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="row">
<button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script>
<script>
function login() {
$.ajax({
type:"post",
url:"/user/login",
data:{
userName:$("#userName").val(),
password:$("#password").val()
},
success:function (result) {
if(result===true){
//验证成功
location.href = "book_list.html";
}else {
alert("登录失败,用户名不存在或密码错误!");
}
}
});
}
</script>
</body>
</html>
3.5完整校验前后端交互
四、实现添加图书接口
4.1约定前后端交互
4.2实现(后端)服务器代码
4.2.1控制层
@Slf4j
@RestController
@RequestMapping("book")
public class BookController {
@Autowired
private BookService bookService;
@RequestMapping("/addBook")
public String addBook(BookInfo bookInfo){
log.info("添加图书,bookInfo:{}",bookInfo);
/**
* 参数校验
*/
if(!StringUtils.hasLength(bookInfo.getBookName())
|| !StringUtils.hasLength(bookInfo.getAuthor())
|| !StringUtils.hasLength(bookInfo.getPublish())
|| bookInfo.getCount() <=0
|| bookInfo.getPrice()==null){
return "参数错误";
}
/**
* 添加图书
*/
/**
* 出现错误比如参数错误。添加过程出现异常,因为MySQL会出现一些异常
* 我们使用try/catch来捕获。
*/
try {
bookService.insertBook(bookInfo);
}catch (Exception a){
return "内部发生错误,请联系管理员";
}
/**
* 返回成功添加吐过表示图书插入成功。
*/
return "成功添加图书";
}
}
4.2.2 业务层
@Service
public class BookService {
@Autowired
private BookInfoMapper bookInfoMapper;
public Integer insertBook(BookInfo bookInfo){
return bookInfoMapper.insertBook(bookInfo);
}
}
4.2.3 数据层
@Mapper
public interface BookInfoMapper {
@Insert("insert into book_info(book_name,author,count,price,publish) " +
"values (#{bookName},#{author},#{count},#{price},#{publish})")
Integer insertBook(BookInfo bookInfo);
}
4.3后端测试
4..4实现前端代码
4.4.1 form表单 和 ajax 部分
<form id="addBook">
<div class="form-group">
<label for="bookName">图书名称:</label>
<input type="text" class="form-control" placeholder="请输入图书名称" id="bookName" name="bookName">
</div>
<div class="form-group">
<label for="bookAuthor">图书作者</label>
<input type="text" class="form-control" placeholder="请输入图书作者" id="bookAuthor" name="author" />
</div>
<div class="form-group">
<label for="bookStock">图书库存</label>
<input type="text" class="form-control" placeholder="请输入图书库存" id="bookStock" name="count"/>
</div>
<div class="form-group">
<label for="bookPrice">图书定价:</label>
<input type="number" class="form-control" placeholder="请输入价格" id="bookPrice" name="price">
</div>
<div class="form-group">
<label for="bookPublisher">出版社</label>
<input type="text" id="bookPublisher" class="form-control" placeholder="请输入图书出版社" name="publish" />
</div>
<div class="form-group">
<label for="bookStatus">图书状态</label>
<select class="custom-select" id="bookStatus" name="status">
<option value="1" selected>可借阅</option>
<option value="2">不可借阅</option>
</select>
</div>
<div class="form-group" style="text-align: right">
<button type="button" class="btn btn-info btn-lg" onclick="add()">确定</button>
<button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button>
</div>
</form>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>
function add() {
$.ajax({
type:"post",
url:"/book/addBook",
//整个表单内容需要提交的话,我们可以借助form标签来提交整个表单,搭配后端做一些交互来使用的。
data: $("#addBook").serialize(),//序列化
//这个函数会把整个表单的所有的input框..等等输入的项目都会提交
success:function (result) {
if(result === "成功添加图书"){
alert("添加成功");
location.href = "book_list.html";
}else {
alert(result);
}
}
});
}
</script>
4.4.2完整前端添加图书代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>添加图书</title>
<link rel="stylesheet" href="css/bootstrap.min.css" />
<link rel="stylesheet" href="css/add.css" />
</head>
<body>
<div class="container">
<div class="form-inline">
<h2 style="text-align: left; margin-left: 10px">
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
fill="#17a2b8"
class="bi bi-book-half"
viewBox="0 0 16 16"
>
<path
d="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z"
/>
</svg>
<span>添加图书</span>
</h2>
</div>
<form id="addBook">
<div class="form-group">
<label for="bookName">图书名称:</label>
<input
type="text"
class="form-control"
placeholder="请输入图书名称"
id="bookName"
name="bookName"
/>
</div>
<div class="form-group">
<label for="bookAuthor">图书作者</label>
<input
type="text"
class="form-control"
placeholder="请输入图书作者"
id="bookAuthor"
name="author"
/>
</div>
<div class="form-group">
<label for="bookStock">图书库存</label>
<input
type="text"
class="form-control"
placeholder="请输入图书库存"
id="bookStock"
name="count"
/>
</div>
<div class="form-group">
<label for="bookPrice">图书定价:</label>
<input
type="number"
class="form-control"
placeholder="请输入价格"
id="bookPrice"
name="price"
/>
</div>
<div class="form-group">
<label for="bookPublisher">出版社</label>
<input
type="text"
id="bookPublisher"
class="form-control"
placeholder="请输入图书出版社"
name="publish"
/>
</div>
<div class="form-group">
<label for="bookStatus">图书状态</label>
<select class="custom-select" id="bookStatus" name="status">
<option value="1" selected>可借阅</option>
<option value="2">不可借阅</option>
</select>
</div>
<div class="form-group" style="text-align: right">
<button type="button" class="btn btn-info btn-lg" onclick="add()">
确定
</button>
<button
type="button"
class="btn btn-secondary btn-lg"
onclick="javascript:history.back()"
>
返回
</button>
</div>
</form>
</div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>
function add() {
$.ajax({
type: "post",
url: "/book/addBook",
//整个表单内容需要提交的话,我们可以借助form标签来提交整个表单,搭配后端做一些交互来使用的。
data: $("#addBook").serialize(), //序列化
//这个函数会把整个表单的所有的input框..等等输入的项目都会提交
success: function (result) {
if (result === "成功添加图书") {
alert("添加成功");
location.href = "book_list.html";
} else {
alert(result);
}
},
});
}
</script>
</body>
</html>
4.5完整校验前后端交互
本篇内容已经很多。
后续内容在下一篇文章中!