java 项目开发架构图
先对整体进行分析、规划 数据库表之间的关系
1 2 3 4 5 一共有五张表分别为 books(图书表)、bookrack(书架表)、borrow(借阅记录表)、reader(用户表)、blacklist(黑名单表) 图书表与书架表关系为 一个书架可以有多本书,一本书只能在一个书架上放着,是一对多的关系。 图书表与用户表是多对多的关系,一本书可以被多个人借阅,一个人可以借阅多本书,所以是多对多的关系,它们之间的联系 "借阅" 又可以单独成一张借阅记录表。 用户表与黑名单表之间的关系为 多对一 的关系,一个用户只能在在黑名单中出现一次,一张黑名单能记录多个进入黑名单的用户
功能需求分析: 利用 UML 图先对图书管理系统的功能进行分析
界面规划 根据功能序需求,分为 8 个页面来实现 下面开始设计界面的草图
配置数据库 利用 navicat 连接本地 MySQL8,连接上去后执行以下下命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 create database library;use library; create table bookrack( bookrack_id int (11 ) primary key, room_id int (11 ), unique (room_id,bookrack_id)); create table books( book_id int (11 ) auto_increment primary key, book_name varchar (255 ) unique , publication_date datetime(0 ), publisher varchar (255 ), bookrack_id int (11 ), status varchar (255 ), foreign key(bookrack_id) references bookrack(bookrack_id)); create table reader( borrow_book_id int (11 ) auto_increment primary key, name varchar (20 ) not null unique , age int (11 ), sex varchar (255 ), address varchar (255 ), password varchar (255 ) not null , role int (11 ) DEFAULT 1 ); create table borrow( borrow_book_id int (11 ), book_id int (11 ), borrow_date datetime(0 ), return_date datetime(0 ), primary key(book_id),foreign key(book_id) references books(book_id),foreign key(borrow_book_id) references reader(borrow_book_id),unique (borrow_book_id,book_id)); create table blacklist( borrow_book_id int (11 ) primary key, name varchar (255 ), reason varchar (255 ), foreign key(borrow_book_id) references reader(borrow_book_id),foreign key(name) references reader(name)); insert into reader(name,age,sex,address,password,role)values ('bbdolt' ,20 ,'男' ,'武汉' ,'4c79273eed3d095e55d1224f6524ae92' ,1 ),('admin' ,20 ,'男' ,'moon' ,'4c79273eed3d095e55d1224f6524ae92' ,0 ),('blackman' ,99 ,'男' ,'河南' ,'4c79273eed3d095e55d1224f6524ae92' ,1 );insert into bookrack(bookrack_id,room_id)values (1 ,1 ),(2 ,1 ),(3 ,1 ),(4 ,2 ),(5 ,2 ),(6 ,2 );insert into books(book_name,publication_date,publisher,bookrack_id,status)values ('狼王梦' ,'1970-1-1' ,'BB社' ,'1' ,'可借阅' ),('查理' ,'1970-1-1' ,'BB社' ,'2' ,'已借阅' ),('test1' ,'1970-1-1' ,'BB社' ,'1' ,'可借阅' ),('test2' ,'1970-1-1' ,'BB社' ,'1' ,'可借阅' ),('test3' ,'1970-1-1' ,'BB社' ,'1' ,'可借阅' ),('test4' ,'1970-1-1' ,'BB社' ,'1' ,'可借阅' );insert into borrow(borrow_book_id,book_id,borrow_date,return_date)values (1 ,2 ,'2024-11-30' ,'2024-12-30' );insert into blacklist(borrow_book_id,name,reason)values (3 ,'blackman' ,'逾期未归还图书' );insert into blacklist(borrow_book_id,name,reason)values (1 ,'bbdolt' ,'逾期未归还图书' );insert into blacklist(borrow_book_id,name,reason)values (5 ,'test' ,'逾期未归还图书' );
开始创建项目 选择 Spring Initializr ,用来创建 SpringBoot 工程 这种方式构建的 SpringBoot 工程其实也是 Maven 工程 选中 Web,然后勾选 Spring Web 由于我们需要开发一个 web 程序,使用到了 SpringMVC 技术,所以按照下图红框进行勾选 经过以上步骤后就创建了如下结构的模块,它会帮我们自动生成一个 Application 类,而该类一会再启动服务器时会用到
配置 application.properties 文件(配置与本地数据库进行连接并设置服务端口) src/main/resources/application.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 配置端口号为8081 server.port= 808 # 配置数据库 # 配置驱动 spring.datasource.driver- class- name= com.mysql.cj.jdbc.Driver # 配置数据库URL,确保数据库的名称是library spring.datasource.url= jdbc:mysql:/ / localhost:3306 / library?serverTimezone= UTC # 数据库用户名 spring.datasource.username= root # 数据库密码 spring.datasource.password= Admin123 # JPA # spring.jpa.show- sql = true # spring.jpa.database- platform= org.hibernate.dialect.MySQL5InnoDBDialect # spring.jpa.hibernate.ddl- auto= update
根据数据库中的表定义实体类 Domain(一共 5 个) src/main/java/top/cengweiye/library/domain/Bookrack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package top.cengweiye.library.domain;import javax.persistence.*;@Entity @Table(name = "bookrack") public class Bookrack { @Id @Column(name = "bookrack_id") private int bookrackId; @Column(name = "room_id") private int roomId; public void setBookrackId (int bookrackId) { this .bookrackId = bookrackId; } public int getRoomId () { return roomId; } public void setRoomId (int roomId) { this .roomId = roomId; } public int getBookrackId () { return bookrackId; } }
src/main/java/top/cengweiye/library/domain/Book
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 package top.cengweiye.library.domain;import javax.persistence.*;import java.util.Date;@Entity @Table(name = "books") public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "book_id") private int bookId; @Column(name = "book_name", unique = true) private String bookName; @Column(name = "publication_date") private Date publicationDate; @Column(name = "publisher") private String publisher; @Column(name = "bookrack_id") private int bookrackId; @Column(name="status") private String status; public String getStatus () { return status; } public void setStatus (String status) { this .status = status; } public int getBookId () { return bookId; } public void setBookId (int bookId) { this .bookId = bookId; } public String getBookName () { return bookName; } public void setBookName (String bookName) { this .bookName = bookName; } public Date getPublicationDate () { return publicationDate; } public void setPublicationDate (Date publicationDate) { this .publicationDate = publicationDate; } public String getPublisher () { return publisher; } public void setPublisher (String publisher) { this .publisher = publisher; } public int getBookrackId () { return bookrackId; } public void setBookrackId (int bookrackId) { this .bookrackId = bookrackId; } }
src/main/java/top/cengweiye/library/domain/Reader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 package top.cengweiye.library.domain;import javax.persistence.*;@Entity @Table(name = "reader") public class Reader { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "borrow_book_id") private int borrowBookId; @Column(name = "name", unique = true, nullable = false) private String name; @Column(name = "age") private int age; @Column(name = "sex") private String sex; @Column(name = "address") private String address; @Column(name = "password", nullable = false) private String password; public int getRole () { return role; } public void setRole (int role) { this .role = role; } @Column(name = "role") private int role = 1 ; public int getBorrowBookId () { return borrowBookId; } public void setBorrowBookId (int borrowBookId) { this .borrowBookId = borrowBookId; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public String getSex () { return sex; } public void setSex (String sex) { this .sex = sex; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } public String getPassword () { return password; } public void setPassword (String password) { this .password = password; } }
src/main/java/top/cengweiye/library/domain/Borrow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package top.cengweiye.library.domain;import javax.persistence.*;import java.time.LocalDateTime;@Entity @Table(name = "borrow") public class Borrow { @Column(name = "borrow_book_id") private int borrowBookId; public int getBookId () { return bookId; } public void setBookId (int bookId) { this .bookId = bookId; } @Id @Column(name = "book_id") private int bookId; @Column(name = "borrow_date") private LocalDateTime borrowDate; @Column(name = "return_date") private LocalDateTime returnDate; public int getBorrowBookId () { return borrowBookId; } public void setBorrowBookId (int borrowBookId) { this .borrowBookId = borrowBookId; } public LocalDateTime getBorrowDate () { return borrowDate; } public void setBorrowDate (LocalDateTime borrowDate) { this .borrowDate = borrowDate; } public LocalDateTime getReturnDate () { return returnDate; } public void setReturnDate (LocalDateTime returnDate) { this .returnDate = returnDate; } }
src/main/java/top/cengweiye/library/domain/Blacklist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package top.cengweiye.library.domain;import javax.persistence.*;@Entity @Table(name = "blacklist") public class Blacklist { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "borrow_book_id") private int borrowBookId; @Column(name = "name") private String name; @Column(name = "reason") private String reason; public int getBorrowBookId () { return borrowBookId; } public void setBorrowBookId (int borrowBookId) { this .borrowBookId = borrowBookId; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getReason () { return reason; } public void setReason (String reason) { this .reason = reason; } }
处理跨域访问问题 src/main/java/top/cengweiye/library/config/GlobalCorsConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package top.cengweiye.library.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class GlobalCorsConfig { @Bean public WebMvcConfigurer corsConfigurer () { return new WebMvcConfigurer () { @Override public void addCorsMappings (CorsRegistry registry) { registry.addMapping("/**" ) .allowedOriginPatterns("*" ) .allowCredentials(true ) .allowedMethods("GET" , "POST" , "PUT" , "DELETE" ) .allowedHeaders("*" ) .exposedHeaders("*" ); } }; } }
下面开始先实现 登录/注册/注销功能 定义数据访问对象 ReaderDao(访问 Reader 表) src/main/java/top/cengweiye/library/repository/ReaderDao.java(接口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package top.cengweiye.library.repository;import top.cengweiye.library.domain.Reader; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface ReaderDao extends JpaRepository <Reader, Long> { Reader findByName (String name) ; Reader findByNameAndPassword (String name, String password) ; Reader findByBorrowBookId (int borrowBookId) ; } 由于我们只实现登录注册功能,所以只要有根据账号密码查询用户和插入用户信息的方法就行了,这里我们已经实现了根据用户名密码查找用户的方法,而插入用户信息的方法save(object o)JPA已经帮我们实现了,可以直接调用,这里就不需要写了。
实现工具类 Result 工具类 Result 的作用是作为返回给前端的统一后的对象。也就是说返回给前端的都是 Result 对象,只是对象中的属性不太一样,这样方便前端固定接收格式。 src/main/java/top/cengweiye/library/Utilities/Result
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package top.cengweiye.library.Utilities;public class Result <T> { private String code; private String msg; private T data; public String getCode () { return code; } public void setCode (String code) { this .code = code; } public String getMsg () { return msg; } public void setMsg (String msg) { this .msg = msg; } public T getData () { return data; } public void setData (T data) { this .data = data; } public Result () { } public Result (T data) { this .data = data; } public static Result<Void> success () { Result<Void> result = new Result <>(); result.setCode("0" ); result.setMsg("成功" ); return result; } public static <T> Result<T> success (T data) { Result<T> result = new Result <>(data); result.setCode("0" ); result.setMsg("成功" ); return result; } public static <T> Result<T> success (T data,String msg) { Result<T> result = new Result <>(data); result.setCode("0" ); result.setMsg(msg); return result; } public static <T> Result<T> error (String code, String msg) { Result<T> result = new Result <>(); result.setCode(code); result.setMsg(msg); return result; } }
配置 jwt 生成、验证 token src/main/java/top/cengweiye/library/Utilities/TokenUtil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package top.cengweiye.library.Utilities;import io.jsonwebtoken.*;import java.util.Date;public class TokenUtil { private static final long EXPIRATION_TIME = 7 * 24 * 60 * 60 * 1000L ; private static final String SECRET_KEY = "MTIzNDU2" ; public static String generateToken (String username) { return Jwts.builder() .setSubject(username) .setIssuedAt(new Date ()) .setExpiration(new Date (System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static boolean verify (String token) { if (TokenBlacklist.isTokenBlacklisted(token)) { System.out.println("Token已注销" ); return false ; } try { Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token); return true ; } catch (ExpiredJwtException e) { System.out.println("Token已过期" ); } catch (JwtException e) { System.out.println("无效的Token:" + e.getMessage()); System.out.println(token); } return false ; } public static String getUsernameFromToken (String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } }
添加工具类 token 黑名单(实现注销功能) src/main/java/top/cengweiye/library/Utilities/TokenBlacklist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package top.cengweiye.library.Utilities;import java.util.HashSet;import java.util.Set;public class TokenBlacklist { private static final Set<String> blacklistedTokens = new HashSet <>(); public static void blacklistToken (String token) { blacklistedTokens.add(token); } public static boolean isTokenBlacklisted (String token) { return blacklistedTokens.contains(token); } }
定义业务逻辑(实现登录注册的核心代码的接口) src/main/java/top/cengweiye/library/service/ReaderSevice.java
1 2 3 4 5 6 7 8 9 10 package top.cengweiye.library.service;import org.springframework.data.domain.Page;import top.cengweiye.library.domain.Reader;public interface ReaderService { Reader loginService (String name, String password) ; Reader registService (Reader reader) ; Page<Reader> getReaderList (int pageNum, int pageSize) ; }
src/main/java/top/cengweiye/library/service/serviceImpl/ReaderServiceImpl(我们将在 ReaderServiceImpl 中实现 ReaderService 中的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package top.cengweiye.library.service.serviceImpl;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.ReaderDao;import top.cengweiye.library.service.ReaderService;import org.springframework.stereotype.Service;import javax.annotation.Resource;public class ReaderServiceImpl implements ReaderService { @Override public Reader loginService (String name, String password) { return null ; } @Override public Reader registService (User user) { return null ; } } package top.cengweiye.library.service.serviceImpl;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.ReaderDao;import top.cengweiye.library.service.ReaderService;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service public class ReaderServiceImpl implements ReaderService { @Resource private ReaderDao readerDao; @Override public Reader loginService (String name, String password) { Reader reader = readerDao.findByNameAndPassword(name, password); if (reader != null ){ reader.setPassword("" ); } return reader; } @Override public Reader registService (Reader reader) { if (readerDao.findByName(reader.getName())!=null ){ return null ; }else { return readerDao.save(reader); } } @Override public Page<Reader> getReaderList (int pageNum, int pageSize) { return readerDao.findAll(PageRequest.of(pageNum, pageSize)); } }
定义控制器(注册/登录/注销部分) src/main/java/top/cengweiye/library/Controller/ReaderController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package top.cengweiye.library.Controller;import org.springframework.web.bind.annotation.*;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.Utilities.TokenBlacklist;import top.cengweiye.library.Utilities.TokenUtil;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.service.ReaderService;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.Cookie;@RestController @RequestMapping("/reader") public class ReaderController { @Resource private ReaderService readerService; @PostMapping("/login") public Result<Reader> loginController (@RequestBody Reader newUser,HttpServletResponse response) { Reader user = readerService.loginService(newUser.getName(), newUser.getPassword()); if (user!=null ){ String token = TokenUtil.generateToken(user.getName()); Cookie cookie = new Cookie ("Authorization" ,"Bearer" + token); cookie.setHttpOnly(true ); cookie.setSecure(true ); cookie.setPath("/" ); cookie.setMaxAge(60 * 60 * 24 * 7 ); response.addCookie(cookie); return Result.success(user, "登录成功!" ); }else { return Result.error("401" ,"账号或密码错误!" ); } } @PostMapping("/register") public Result<Reader> registController (@RequestBody Reader newUser) { Reader user = readerService.registService(newUser); if (user!=null ){ return Result.success(user,"注册成功!" ); }else { return Result.error("409" ,"用户名已存在!" ); } } @PostMapping("/logout") public Result<String> logoutController (HttpServletRequest request, HttpServletResponse response) { Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if ("Authorization" .equals(cookie.getName())) { String token = cookie.getValue(); if (token != null ) { token = token.substring(6 ); if (TokenUtil.verify(token)) { TokenBlacklist.blacklistToken(token); } } } } } return Result.success("成功退出登录!" ); } }
配置拦截器(拦截规则) src/main/java/top/cengweiye/library/Config/WebConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package top.cengweiye.library.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import top.cengweiye.library.interceptor.VisitInterceptor;@Configuration public class WebConfig implements WebMvcConfigurer { @Bean public VisitInterceptor visitInterceptor () { return new VisitInterceptor (); } @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(visitInterceptor()) .addPathPatterns("/**" ) .excludePathPatterns( "/reader/login" , "/reader/register" , "/login.html" , "/register.html" , "/**/*.js" , "/**/*.css" , "/**/*.png" , "/**/*.jpg" , "/**/*.jpeg" ); } }
拦截器(用于拦截没有登录的用户,preHandle 在到达处理器之前拦截) src/main/java/top/cengweiye/library/interceptor/VisitInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package top.cengweiye.library.interceptor;import org.springframework.web.servlet.HandlerInterceptor;import top.cengweiye.library.Utilities.TokenUtil;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class VisitInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if ("Authorization" .equals(cookie.getName())) { String token = cookie.getValue(); if (token != null ) { token = token.substring(6 ); if (TokenUtil.verify(token)) { return true ; } } } } } response.sendRedirect("/login.html" ); return false ; } }
前端页面 src/main/resource/static/login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 <!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/login.css" /> <script type ="text/javascript" src ="js/jquery.min.js" > </script > </head > <body > <div class ="container-login" > <div class ="container-pic" > <img src ="images/computer.png" width ="350px" /> </div > <div class ="login-dialog" > <h3 > 登陆</h3 > <div class ="row" > <span > 用户名</span > <input type ="text" name ="Name" id ="Name" class ="form-control" /> </div > <div class ="row" > <span > 密码</span > <input type ="password" name ="password" id ="password" class ="form-control" /> </div > <div class ="register" > 没有账号?<a href ="register.html" > 点击注册</a > </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 ( ) { let name = document .getElementById ("Name" ).value ; let password = document .getElementById ("password" ).value ; fetch ("http://localhost:8081/reader/login" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ name, password }), }) .then ((response ) => { if (response.ok ) { return response.json (); } else { throw new Error ("登录失败" ); } }) .then ((data ) => { alert (data.msg ); if (data.code === "0" ) { window .location .href = "/" ; } }) .catch ((error ) => console .error (error)); } </script > </body > </html >
src/main/resource/static/register.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <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 > <style > .login-dialog { width : 400px ; height : 500px ; } .container-login { width : 750px ; height : 500px ; } </style > <title > 注册</title > </head > <body > <div class ="container-login" > <div class ="container-pic" > <img src ="images/computer.png" width ="350px" alt ="" /> </div > <div class ="login-dialog" > <h3 > 注册</h3 > <div class ="row" > <label for ="Name" > 用户名</label > <input type ="text" name ="Name" id ="Name" class ="form-control" /> </div > <div class ="row" > <label for ="age" > 年龄</label > <input type ="text" name ="age" id ="age" class ="form-control" /> </div > <div class ="row" > <label for ="sex" > 性别</label > <input type ="text" name ="sex" id ="sex" class ="form-control" /> </div > <div class ="row" > <label for ="address" > 地址</label > <input type ="text" name ="address" id ="address" class ="form-control" /> </div > <div class ="row" > <label for ="password" > 密码</label > <input type ="password" name ="password" id ="password" class ="form-control" /> </div > <div class ="row" > <label for ="confirmPassword" > 确认密码:</label > <input type ="password" id ="confirmPassword" name ="confirmPassword" required /> </div > <div class ="row" > <button type ="button" class ="btn btn-info btn-lg" onclick ="register()" > 注册 </button > </div > </div > </div > <script src ="js/jquery.min.js" > </script > <script > function register ( ) { let password = document .getElementById ("password" ).value ; let confirmPassword = document .getElementById ("confirmPassword" ).value ; if (password !== confirmPassword) { alert ("两次密码不一致" ); return ; } let uname = document .getElementById ("Name" ).value ; let age = document .getElementById ("age" ).value ; let sex = document .getElementById ("sex" ).value ; let address = document .getElementById ("address" ).value ; fetch ("http://localhost:8081/user/register" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ uname, password, age, sex, address }), }) .then ((response ) => { if (response.ok ) { return response.json (); } else { throw new Error ("注册失败" ); } }) .then ((data ) => { alert (data.msg ); if (data.code === "0" ) { window .location .href = "login.html" ; } }) .catch ((error ) => console .error (error)); } </script > </body > </html >
实现首页查看图书功能 定义数据访问对象 src/main/java/top/cengweiye/library/repository/BookDao
1 2 3 4 5 6 7 8 9 10 package top.cengweiye.library.repository;import org.springframework.data.jpa.repository.JpaRepository;import top.cengweiye.library.domain.Book;import org.springframework.stereotype.Repository;@Repository public interface BookDao extends JpaRepository <Book, Integer> { Page<Book> findByBookNameContaining (String keyWord, Pageable pageable) ; }
src/main/java/top/cengweiye/library/repository/BorrowDao
1 2 3 4 5 6 7 8 package top.cengweiye.library.repository;import org.springframework.data.jpa.repository.JpaRepository;import top.cengweiye.library.domain.Borrow;public interface BorrowDao extends JpaRepository <Borrow, Integer> {}
定义业务逻辑 定义 BookService 接口 src/main/java/top/cengweiye/library/service/BookService
1 2 3 4 5 6 7 8 9 package top.cengweiye.library.service;import org.springframework.data.domain.Page;import top.cengweiye.library.domain.Book;public interface BookService { public Page<Book> getBooks (int page, int size) ; public Page<Book> getBooklikes (int page, int size, String keyWord) ; }
定义 BorrowService 接口 src/main/java/top/cengweiye/library/service/BorrowService
1 2 3 4 5 package top.cengweiye.library.service;public interface BorrowService { public boolean borrowBook (Integer bookId, Integer borrowBookId) ; }
实现接口功能 src/main/java/top/cengweiye/library/service/serviceImpl/BookServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package top.cengweiye.library.service.serviceImpl;import org.springframework.stereotype.Service;import top.cengweiye.library.service.BookService;import top.cengweiye.library.repository.BookDao;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import top.cengweiye.library.domain.Book;import javax.annotation.Resource;@Service public class BookServiceImpl implements BookService { @Resource private BookDao BookDao; public Page<Book> getBooks (int page, int size) { return BookDao.findAll(PageRequest.of(page, size)); } @Override public Page<Book> getBooklikes (int page, int size, String keyWord) { if (keyWord != null && !keyWord.isEmpty()) { return BookDao.findByBookNameContaining(keyWord, PageRequest.of(page, size)); } else { return BookDao.findAll(PageRequest.of(page, size)); } } }
src/main/java/top/cengweiye/library/service/serviceImpl/BorrowServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package top.cengweiye.library.service.serviceImpl;import org.springframework.stereotype.Service;import top.cengweiye.library.domain.Book;import top.cengweiye.library.domain.Borrow;import top.cengweiye.library.repository.BookDao;import top.cengweiye.library.repository.BorrowDao;import top.cengweiye.library.service.BorrowService;import javax.annotation.Resource;import java.time.LocalDateTime;import java.util.Optional;@Service public class BorrowServiceImpl implements BorrowService { @Resource private BookDao BookDao; @Resource private BorrowDao BorrowDao; @Override public boolean borrowBook (Integer bookId,Integer borrowBookId) { Optional<Book> optionalBook = BookDao.findById(bookId); if (optionalBook.isPresent()) { Book book = optionalBook.get(); if ("可借阅" .equals(book.getStatus())) { book.setStatus("已借阅" ); BookDao.save(book); Borrow borrow = new Borrow (); borrow.setBorrowBookId(borrowBookId); borrow.setBookId(bookId); borrow.setBorrowDate(LocalDateTime.now()); borrow.setReturnDate(LocalDateTime.now().plusDays(30 )); BorrowDao.save(borrow); return true ; } } return false ; } }
定义控制器 图书查询功能 src/main/java/top/cengweiye/library/Controller/BookController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package top.cengweiye.library.Controller;import org.springframework.data.domain.Page;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import top.cengweiye.library.domain.Book;import top.cengweiye.library.service.BookService;import javax.annotation.Resource;@Controller public class BookController { @Resource private BookService bookService; @GetMapping("/") public String getBooks ( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size, @RequestParam(required = false) String bookName, // 接收搜索参数 Model model) { Page<Book> bookPage; if (bookName != null && !bookName.isEmpty()) { bookPage = bookService.getBooklikes(page, size, bookName); } else { bookPage = bookService.getBooks(page, size); } model.addAttribute("bookPage" , bookPage); model.addAttribute("bookName" , bookName); return "index" ; } }
借阅功能 src/main/java/top/cengweiye/library/Controller/BorrowController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package top.cengweiye.library.Controller;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.TokenUtil;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.ReaderDao;import top.cengweiye.library.service.BorrowService;import javax.annotation.Resource;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import java.util.HashMap;import java.util.Map;@RestController @RequestMapping("/borrow") public class BorrowController { @Resource private BorrowService BorrowService; @Resource private ReaderDao ReaderDao; @PostMapping public ResponseEntity<Map<String, Object>> borrowBook (@RequestBody Map<String, Object> requestBody, HttpServletRequest request) { Integer bookId = (Integer) requestBody.get("bookId" ); String name = null ; Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if ("Authorization" .equals(cookie.getName())) { String token = cookie.getValue(); if (token != null ) { token = token.substring(6 ); if (TokenUtil.verify(token)) { name = TokenUtil.getUsernameFromToken(token); } } } } } Reader reader = ReaderDao.findByName(name); int borrowBookId = reader.getBorrowBookId(); Map<String, Object> response = new HashMap <>(); try { boolean success = BorrowService.borrowBook(bookId, borrowBookId); if (success) { response.put("success" , true ); } else { response.put("success" , false ); response.put("message" , "该书已经被借阅!" ); } } catch (Exception e) { response.put("success" , false ); response.put("message" , "发生错误:" + e.getMessage()); } return ResponseEntity.ok(response); } }
前端页面(Thymeleaf 显示) src/main/resource/templates/index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 <!DOCTYPE html > <html lang ="zh-CN" xmlns:th ="http://www.w3.org/1999/xhtml" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="../css/style.css" /> <link rel ="stylesheet" href ="../css/list.css" /> <link rel ="stylesheet" href ="../css/bootstrap.min.css" /> <title > 图书管理系统</title > </head > <body > <header > <h1 > <a href ="/" > 图书管理系统</a > </h1 > <img src ="../images/logo.png" alt ="Logo" style ="width: 80px; height: 80px; margin-left: 10px;" /> <div class ="col-lg-2 col-md-3 col-sm-3 col-xs-4 pull-right" > <form method ="get" action ="/" class ="input-group" > <label for ="bookName" > </label > <input class ="form-control" type ="text" id ="bookName" name ="bookName" placeholder ="搜索书名..." th:value ="${bookName}" /> <button type ="submit" class ="input-group-addon" style ="color: #FFFFFF;background-color: #2EA7E0;" > 搜索 </button > </form > </div > <ul > <li > <a href ="/person" > 个人中心</a > </li > </ul > </header > <main > <div class ="bookContainer" > <h2 > 图书列表</h2 > <table > <thead > <tr > <th > 编号</th > <th > 书名</th > <th > 出版日期</th > <th > 出版社</th > <th > 状态</th > <th > 操作</th > </tr > </thead > <tbody > <tr th:each ="book : ${bookPage.content}" > <td th:text ="${book.bookId}" > </td > <td th:text ="${book.bookName}" > </td > <td th:text ="${book.publicationDate}" > </td > <td th:text ="${book.publisher}" > </td > <td th:text ="${book.status}" > </td > <td > <div class ="op" > <a th:attr ="data-id=${book.bookId}, data-status=${book.status}" onclick ="borrowBook(this)" > 借阅</a > </div > </td > </tr > </tbody > </table > <div > <button th:if ="${bookPage.hasPrevious()}" th:onclick ="'window.location.href=\'?page=' + (${bookPage.number} - 1) + '&size=' + ${bookPage.size} + '\''" > 上一页 </button > <button th:if ="${bookPage.hasNext()}" th:onclick ="'window.location.href=\'?page=' + (${bookPage.number} + 1) + '&size=' + ${bookPage.size} + '\''" > 下一页 </button > </div > </div > </main > </body > <script > function borrowBook (element ) { const bookId = parseInt (element.getAttribute ("data-id" ), 10 ); const status = element.getAttribute ("data-status" ); if (status === "可借阅" ) { fetch ("/borrow" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ bookId : bookId }), }) .then ((response ) => response.json ()) .then ((data ) => { if (data.success ) { alert ("借阅成功!" ); location.reload (); } else { alert ("借阅失败:" + data.message ); } }) .catch ((error ) => { console .error ("Error:" , error); alert ("借阅失败,请稍后再试!" ); }); } else { alert ("该书不可借阅!" ); } } </script > </html >
个人中心 定义一个 ReturnRequest 实体类(用于接收返回来的图书 ID) src/main/java/top/cengweiye/library/domain/ReturnRequest
1 2 3 4 5 6 7 8 9 10 package top.cengweiye.library.domain;public class ReturnRequest { private Integer bookId; return bookId; } public void setBookId (Integer bookId) { this .bookId = bookId; } }
定义数据访问控制层 src/main/java/top/cengweiye/library/repostory/BorrowDao
1 2 3 4 5 6 7 8 9 10 11 12 package top.cengweiye.library.repository;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.jpa.repository.JpaRepository;import top.cengweiye.library.domain.Borrow;public interface BorrowDao extends JpaRepository <Borrow, Integer> { Page<Borrow> findByBorrowBookId (int borrowBookId, Pageable pageable) ; void deleteByBookId (Integer bookId) ; }
定义业务逻辑 获取借阅记录 src/main/java/top/cengweiye/library/service/BoorrowService
1 2 3 4 5 6 7 8 9 10 package top.cengweiye.library.service;import org.springframework.data.domain.Page;import top.cengweiye.library.domain.Borrow;public interface BorrowService { public boolean borrowBook (Integer bookId, Integer borrowBookId) ; public Page<Borrow> getBorrow (int borrowbookid, int page, int size) ; }
还书 src/main/java/top/cengweiye/library/service/BookService
1 2 3 4 5 6 7 8 9 10 11 12 13 package top.cengweiye.library.service;import org.springframework.data.domain.Page;import org.springframework.transaction.annotation.Transactional;import top.cengweiye.library.domain.Book;public interface BookService { public Page<Book> getBooks (int page, int size) ; public Page<Book> getBooklikes (int page, int size, String keyWord) ; @Transactional public void returnBook (Integer bookId) ; }
src/main/java/top/cengweiye/library/service/serviceImpl/BorrowServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package top.cengweiye.library.service.serviceImpl;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.stereotype.Service;import top.cengweiye.library.domain.Book;import top.cengweiye.library.domain.Borrow;import top.cengweiye.library.repository.BookDao;import top.cengweiye.library.repository.BorrowDao;import top.cengweiye.library.service.BorrowService;import javax.annotation.Resource;import java.time.LocalDateTime;import java.util.Optional;@Service public class BorrowServiceImpl implements BorrowService { @Resource private BookDao BookDao; @Resource private BorrowDao BorrowDao; @Override public boolean borrowBook (Integer bookId,Integer borrowBookId) { Optional<Book> optionalBook = BookDao.findById(bookId); if (optionalBook.isPresent()) { Book book = optionalBook.get(); if ("可借阅" .equals(book.getStatus())) { book.setStatus("已借阅" ); BookDao.save(book); Borrow borrow = new Borrow (); borrow.setBorrowBookId(borrowBookId); borrow.setBookId(bookId); borrow.setBorrowDate(LocalDateTime.now()); borrow.setReturnDate(LocalDateTime.now().plusDays(30 )); BorrowDao.save(borrow); return true ; } } return false ; } @Override public Page<Borrow> getBorrow (int borrowbookid, int page, int size) { return BorrowDao.findByBorrowBookId(borrowbookid,PageRequest.of(page, size));} }
src/main/java/top/cengweiye/library/service/serviceImpl/BookServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package top.cengweiye.library.service.serviceImpl;import org.springframework.stereotype.Service;import top.cengweiye.library.service.BookService;import top.cengweiye.library.repository.BookDao;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import top.cengweiye.library.domain.Book;import javax.annotation.Resource;import java.util.Optional;@Service public class BookServiceImpl implements BookService { @Resource private BookDao BookDao; @Override public Page<Book> getBooks (int page, int size) { return BookDao.findAll(PageRequest.of(page, size)); } @Override public Page<Book> getBooklikes (int page, int size, String keyWord) { if (keyWord != null && !keyWord.isEmpty()) { return BookDao.findByBookNameContaining(keyWord, PageRequest.of(page, size)); } else { return BookDao.findAll(PageRequest.of(page, size)); } } @Override public void returnBook (Integer bookId) { Optional<Book> optionalBook = BookDao.findById(bookId); if (optionalBook.isPresent()) { Book book = optionalBook.get(); if ("已借阅" .equals(book.getStatus())) { book.setStatus("可借阅" ); BookDao.save(book); } } } }
定义控制器 实现查看借阅记录 src/main/java/top/cengweiye/library/Controller/PersonController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package top.cengweiye.library.Controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import top.cengweiye.library.Utilities.TokenUtil;import top.cengweiye.library.domain.Book;import top.cengweiye.library.domain.Borrow;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.ReaderDao;import top.cengweiye.library.service.BorrowService;import javax.annotation.Resource;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import java.util.List;@Controller public class PersonController { @Resource private BorrowService borrowService; @Resource private ReaderDao readerDao; @GetMapping("/person") public String getperson ( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size, Model model, HttpServletRequest request) { String name = null ; Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if ("Authorization" .equals(cookie.getName())) { String token = cookie.getValue(); if (token != null ) { token = token.substring(6 ); if (TokenUtil.verify(token)) { name = TokenUtil.getUsernameFromToken(token); } } } } } if (name != null ) { Reader reader = readerDao.findByName(name); if (reader != null ) { model.addAttribute("reader" , reader); int borrowBookId = reader.getBorrowBookId(); Page<Borrow> borrowPage = borrowService.getBorrow(borrowBookId, page, size); model.addAttribute("borrowPage" , borrowPage); } } return "person" ; } }
实现还书功能 src/main/java/top/cengweiye/library/Controller/ReturnController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package top.cengweiye.library.Controller;import org.springframework.transaction.annotation.Transactional;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.domain.ReturnRequest;import top.cengweiye.library.repository.BorrowDao;import top.cengweiye.library.service.BookService;import javax.annotation.Resource;@RestController @RequestMapping("/return") public class ReturnController { @Resource private BookService bookService; @Resource private BorrowDao borrowDao; @PostMapping @Transactional public Result<String> returnBook (@RequestBody ReturnRequest returnRequest) { borrowDao.deleteByBookId(returnRequest.getBookId()); bookService.returnBook(returnRequest.getBookId()); return Result.success("还书成功" ); } }
前端页面(将注销功能写在这里) src/main/resource/templates/person.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 <!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="UTF-8" /> <title > 个人中心</title > <link rel ="stylesheet" href ="../css/list.css" /> <link rel ="stylesheet" href ="../css/style.css" /> </head > <body > <header > <h1 > <a href ="/" > 图书管理系统</a > </h1 > <img src ="../images/logo.png" alt ="Logo" style ="width: 80px; height: 80px; margin-left: 10px;" /> <ul > <li > <a href ="/person" > 个人中心</a > </li > </ul > </header > <main > <div class ="container" > <div class ="bookContainer" > <h2 > 图书列表</h2 > <table > <thead > <tr > <th > 图书编号</th > <th > 借阅日期</th > <th > 归还日期</th > <th > 操作</th > </tr > </thead > <tbody > <tr th:each ="borrow : ${borrowPage}" > <td th:text ="${borrow.bookId}" > 图书编号</td > <td th:text ="${borrow.borrowDate}" > 借阅日期</td > <td th:text ="${borrow.returnDate}" > 归还日期</td > <td > <div class ="op" > <a th:attr ="book-id=${borrow.bookId}" onclick ="borrowBook(this)" > 还书</a > </div > </td > </tr > </tbody > </table > <div > <button th:if ="${borrowPage.hasPrevious()}" th:onclick ="'window.location.href=\'/person?page=' + (${borrowPage.number} - 1) + '&size=' + ${borrowPage.size} + '\''" > 上一页 </button > <button th:if ="${borrowPage.hasNext()}" th:onclick ="'window.location.href=\'/person?page=' + (${borrowPage.number} + 1) + '&size=' + ${borrowPage.size} + '\''" > 下一页 </button > </div > </div > <div class ="readerInfo" > <h2 > 读者信息</h2 > <p > <strong > 姓名:</strong > <span th:text ="${reader.name}" > </span > </p > <p > <strong > 年龄:</strong > <span th:text ="${reader.age}" > </span > </p > <p > <strong > 性别:</strong > <span th:text ="${reader.sex}" > </span > </p > <p > <strong > 地址:</strong > <span th:text ="${reader.address}" > </span > </p > <p > <strong > 角色:</strong ><span th:text ="${reader.role == 1 ? '普通用户' : '管理员'}" > </span > </p > <div > <a href ="/updateperson.html" class ="user-button" > 修改个人信息</a > </div > <div th:if ="${reader.role != 1}" > <a href ="/bookmanage" class ="admin-button" > 进入管理员页面</a > </div > <div > <a class ="logout-button" onclick ="logout()" > 退出登录</a > </div > </div > </div > </main > </body > <script > function borrowBook (element ) { const bookId = parseInt (element.getAttribute ("book-id" ), 10 ); fetch ("/return" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ bookId : bookId }), }) .then ((response ) => response.json ()) .then ((data ) => { if (data.code === "0" ) { alert (data.data ); location.reload (); } }) .catch ((error ) => console .error ("Error:" , error)); } function logout ( ) { fetch ("/reader/logout" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, }) .then ((response ) => response.json ()) .then ((data ) => { if (data.code === "0" ) { window .location .href = "/login.html" ; } }) .catch ((error ) => console .error ("Error:" , error)); } </script > </html >
添加修改个人信息功能 添加工具类 GetToken(用于将获取 token 的代码封装) src/main/java/top/cengweiye/library/Utilities
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package top.cengweiye.library.Utilities;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;public class GetToken { public static String getToken (HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if ("Authorization" .equals(cookie.getName())) { return cookie.getValue().substring(6 ); } } } return null ; } }
定义业务逻辑 src/main/java/top/cengweiye/library/service/UpdatePersonService
1 2 3 4 5 6 7 package top.cengweiye.library.service;import top.cengweiye.library.domain.Reader;import javax.servlet.http.HttpServletRequest;public interface UpdatePersonService { public String updatePerson (Reader reader, String token) ; }
src/main/java/top/cengweiye/library/service/serviceImpl/UpdatePersonServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package top.cengweiye.library.service.serviceImpl;import org.springframework.stereotype.Service;import top.cengweiye.library.Utilities.GetToken;import top.cengweiye.library.Utilities.TokenBlacklist;import top.cengweiye.library.Utilities.TokenUtil;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.ReaderDao;import top.cengweiye.library.service.UpdatePersonService;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;@Service public class UpdatePersonServiceImpl implements UpdatePersonService { @Resource private ReaderDao readerdao; @Override public String updatePerson (Reader reader, String token) { String name = TokenUtil.getUsernameFromToken(token); TokenBlacklist.blacklistToken(token); String update_name = reader.getName(); int update_age = reader.getAge(); String update_sex = reader.getSex(); String update_address = reader.getAddress(); Reader old = readerdao.findByName(name); old.setAddress(update_address); old.setAge(update_age); old.setSex(update_sex); old.setName(update_name); readerdao.save(old); return old.getName(); } }
定义控制器 src/main/java/top/cengweiye/library/Controller/UpdateController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package top.cengweiye.library.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.GetToken;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.Utilities.TokenUtil;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.service.UpdatePersonService;import javax.annotation.Resource;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@RestController @RequestMapping("/update") public class UpdateController { @Resource private UpdatePersonService updatePersonService; @PostMapping public Result<String> update (@RequestBody Reader newreader, HttpServletRequest request, HttpServletResponse response) { String token = GetToken.getToken(request); String name = updatePersonService.updatePerson(newreader, token); String token_twice = TokenUtil.generateToken(name); Cookie cookie = new Cookie ("Authorization" ,"Bearer" + token_twice); cookie.setHttpOnly(true ); cookie.setSecure(true ); cookie.setPath("/" ); cookie.setMaxAge(60 * 60 * 24 * 7 ); response.addCookie(cookie); return Result.success("更新成功" ); } }
前端页面 src/main/resource/static/updateperson.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <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> <style> .login-dialog { width: 400px; height: 500px; } .container-login{ width: 750px; height: 500px; } </style> <title>修改个人信息</title> </head> <body> <div class="container-login" > <div class="container-pic" > <img src="images/computer.png" width="350px" alt="" > </div> <div class="login-dialog" > <h3>修改个人信息</h3> <div class="row" > <label for ="Name" >用户名</label> <input type="text" name="Name" id="Name" class="form-control" > </div> <div class="row" > <label for ="age" >年龄</label> <input type="text" name="age" id="age" class="form-control" > </div> <div class="row" > <label for ="sex" >性别</label> <input type="text" name="sex" id="sex" class="form-control" > </div> <div class="row" > <label for ="address" >地址</label> <input type="text" name="address" id="address" class="form-control" > </div> <div class="row" > <label for ="password" >密码</label> <input type="password" name="password" id="password" class="form-control" > </div> <div class="row" > <label for ="confirmPassword" >确认密码:</label> <input type="password" id="confirmPassword" name="confirmPassword" required> </div> <div class="row" > <button type="button" class="btn btn-info btn-lg" onclick="update()" >修改个人信息</button> </div> </div> </div> <script src="js/jquery.min.js" ></script> <script> function update () { let password = document.getElementById('password' ).value; let confirmPassword = document.getElementById('confirmPassword' ).value; if (password !== confirmPassword) { alert('两次密码不一致' ); return ; } let name = document.getElementById('Name' ).value; let age = document.getElementById('age' ).value; let sex = document.getElementById('sex' ).value; let address = document.getElementById('address' ).value; fetch('/update' , { method: 'POST' , headers: { 'Content-Type' : 'application/json' , }, body: JSON.stringify({ name, password, age, sex, address }), }) .then(response => { if (response.ok) { return response.json(); } else { throw new Error ('更新失败' ); } }) .then(data => { alert(data.data); if (data.code === "0" ) { window.location.href = '/person' ; } }) .catch (error => console.error(error)); } </script> </body> </html>
开始设计管理员页面(分为 图书管理/用户管理 两个页面) 图书管理 下面我们根据这个界面来完成我们的代码
配置访问控制对象 src/main/java/top/cengweiye/library/repository/BookDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package top.cengweiye.library.repository;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.transaction.annotation.Transactional;import top.cengweiye.library.domain.Book;import org.springframework.stereotype.Repository;@Repository public interface BookDao extends JpaRepository <Book, Integer> { public Book findByBookId (Integer bookId) ; void deleteByBookId (Integer bookId) ; Page<Book> findByBookNameContaining (String keyWord, Pageable pageable) ; }
定义业务逻辑 src/main/java/top/cengweiye/library/service/EditBookService
1 2 3 4 5 6 7 8 package top.cengweiye.library.service;import top.cengweiye.library.domain.Book;public interface EditBookService { public String addBook (Book book) ; public String editBook (Book book) ; }
src/main/java/top/cengweiye/library/service/serviceImpl/EditBookServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package top.cengweiye.library.service.serviceImpl;import org.springframework.stereotype.Service;import top.cengweiye.library.domain.Book;import top.cengweiye.library.repository.BookDao;import top.cengweiye.library.service.EditBookService;import javax.annotation.Resource;@Service public class EditBookServiceImpl implements EditBookService { @Resource private BookDao bookDao; @Override public String addBook (Book book) { if (bookDao.findByBookId(book.getBookId()) != null ){ return "该书已存在" ; } book.setStatus("可借阅" ); bookDao.save(book); return "添加成功" ; } @Override public String editBook (Book book) { book.setStatus("可借阅" ); bookDao.save(book); return "修改成功" ; } }
定义控制器 返回页面控制器 src/main/java/top/cengweiye/library/Controller/BookManageController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package top.cengweiye.library.Controller;import org.springframework.data.domain.Page;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import top.cengweiye.library.domain.Book;import top.cengweiye.library.service.BookService;import javax.annotation.Resource;@Controller @RequestMapping("/bookmanage") public class BookManageController { @Resource private BookService bookService; @GetMapping public String getBooks ( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size, Model model) { Page<Book> bookPage = bookService.getBooks(page, size); model.addAttribute("bookPage" , bookPage); return "bookmanage" ; } }
删除图书控制器 src/main/java/top/cengweiye/library/Controller/DeleteBookController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package top.cengweiye.library.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.domain.ReturnRequest;import top.cengweiye.library.repository.BookDao;import top.cengweiye.library.repository.BorrowDao;import javax.annotation.Resource;@RestController @RequestMapping("/deletebook") public class DeleteBookController { @Resource private BookDao bookDao; @Resource private BorrowDao borrowDao; @Transactional @PostMapping public Result<String> deleteBook (@RequestBody ReturnRequest returnRequest) { int bookId = returnRequest.getBookId(); borrowDao.deleteByBookId(bookId); bookDao.deleteByBookId(bookId); return Result.success("删除成功" ); } }
返回编辑图书的页面控制器 src/main/java/top/cengweiye/library/Controller/VimBookController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package top.cengweiye.library.Controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.*;@Controller @RequestMapping("/vimbook") public class VimBookController { @GetMapping public String vimbook (@RequestParam("bookId") int bookId, Model model) { model.addAttribute("bookId" , bookId); return "bookinfo" ; } }
提交修改功能控制器 src/main/java/top/cengweiye/library/Controller/EditBookController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package top.cengweiye.library.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.domain.Book;import top.cengweiye.library.service.EditBookService;import javax.annotation.Resource;@RestController @RequestMapping("/editbook") public class EditBookController { @Resource private EditBookService editBookService; @PostMapping public Result<String> editBook (@RequestBody Book book) { String data = editBookService.editBook(book); return Result.success(data); } }
添加图书功能控制器 src/main/java/top/cengweiye/library/Controller/EditBookController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package top.cengweiye.library.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.domain.Book;import top.cengweiye.library.service.EditBookService;import javax.annotation.Resource;@RestController @RequestMapping("/editbook") public class EditBookController { @Resource private EditBookService editBookService; @PostMapping public Result<String> editBook (@RequestBody Book book) { String data = editBookService.editBook(book); return Result.success(data); } @PostMapping("/add") public Result<String> addBook (@RequestBody Book book) { String data = editBookService.addBook(book); return Result.success(data); } }
前端页面 图书管理界面 src/main/resources/templates/bookmanage.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 <!DOCTYPE html > <html lang ="zh-CN" xmlns:th ="http://www.w3.org/1999/xhtml" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="../css/style.css" /> <link rel ="stylesheet" href ="../css/list.css" /> <title > 图书管理系统</title > </head > <body > <header > <h1 > <a href ="/" > 图书管理系统</a > </h1 > <img src ="../images/logo.png" alt ="Logo" style ="width: 80px; height: 80px; margin-left: 10px;" /> <ul > <li > <a href ="/usermanage" > 用户管理</a > </li > <li > <a href ="/person" > 个人中心</a > </li > </ul > </header > <main > <div class ="bookContainer" > <h2 > 管理图书</h2 > <a href ="/addbook.html" class ="addbook" > 添加图书</a > <table > <thead > <tr > <th > 图书编号</th > <th > 图书名称</th > <th > 状态</th > <th > 操作</th > </tr > </thead > <tbody > <tr th:each ="book : ${bookPage.content}" > <td th:text ="${book.bookId}" > 1</td > <td th:text ="${book.bookName}" > test</td > <td th:text ="${book.status}" > 可借阅</td > <td > <div class ="op" > <a th:attr ="data-id=${book.bookId}" onclick ="vim(this)" > 编辑</a > <a th:attr ="data-id=${book.bookId}" class ="delete" onclick ="del(this)" > 删除</a > </div > </td > </tr > </tbody > </table > <div > <button th:if ="${bookPage.hasPrevious()}" th:onclick ="'window.location.href=\'?page=' + (${bookPage.number} - 1) + '&size=' + ${bookPage.size} + '\''" > 上一页 </button > <button th:if ="${bookPage.hasNext()}" th:onclick ="'window.location.href=\'?page=' + (${bookPage.number} + 1) + '&size=' + ${bookPage.size} + '\''" > 下一页 </button > </div > </div > </main > </body > <script > function vim (element ) { const bookId = parseInt (element.getAttribute ("data-id" ), 10 ); window .location .href = `/vimbook?bookId=${bookId} ` ; } function del (element ) { const bookId = parseInt (element.getAttribute ("data-id" ), 10 ); fetch ("/deletebook" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ bookId : bookId }), }) .then ((response ) => response.json ()) .then ((data ) => { if (data.code === "0" ) { alert (data.data ); location.reload (); } }) .catch ((error ) => { console .error ("Error:" , error); }); } </script > </html >
返回编辑页面 src/main/resources/templates/bookinfo.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 <!DOCTYPE html > <html lang ="zh-CN" xmlns:th ="http://www.w3.org/1999/xhtml" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="../css/style.css" /> <link rel ="stylesheet" href ="../css/list.css" /> <link rel ="stylesheet" href ="../css/bootstrap.min.css" /> <link rel ="stylesheet" href ="../css/login.css" /> <style > .login-dialog { width : 350px ; height : 350px ; } </style > <title > 编辑图书</title > </head > <body > <header > <h1 > <a href ="/" > 图书管理系统</a > </h1 > <img src ="../images/logo.png" alt ="Logo" style ="width: 80px; height: 80px; margin-left: 10px;" /> <ul > <li > <a href ="/usermanage" > 用户管理</a > </li > <li > <a href ="/person" > 个人中心</a > </li > </ul > </header > <main > <div class ="container-login" > <div class ="container-pic" > <img src ="../static/images/computer.png" width ="350px" alt ="" /> </div > <div class ="login-dialog" > <h3 th:attr ="book-id=${bookId}" > 编辑图书</h3 > <div class ="row" > <span > 图书名称</span > <input type ="text" name ="Name" id ="bookName" class ="form-control" /> </div > <div class ="row" > <span > 出版日期</span > <input type ="text" name ="Name" id ="publicationDate" class ="form-control" /> </div > <div class ="row" > <span > 出版社</span > <input type ="text" name ="Name" id ="publisher" class ="form-control" /> </div > <div class ="row" > <span > 书架</span > <input type ="text" name ="Name" id ="bookrackId" class ="form-control" /> </div > <div class ="row" > <button type ="button" class ="btn btn-info btn-lg" onclick ="send()" > 发送 </button > </div > </div > </div > </main > </body > <script > function send ( ) { fetch ("/editbook" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ bookId : document .querySelector ("h3" ).getAttribute ("book-id" ), bookName : document .querySelector ("#bookName" ).value , publicationDate : document .querySelector ("#publicationDate" ).value , publisher : document .querySelector ("#publisher" ).value , bookrackId : document .querySelector ("#bookrackId" ).value , }), }) .then ((response ) => response.json ()) .then ((data ) => { if (data.code === "0" ) { alert (data.data ); window .location .href = "/bookmanage" ; } }) .catch ((error ) => console .error ("Error:" , error)); } </script > </html >
添加图书页面 src/main/resources/static/addbook.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 <!DOCTYPE html > <html lang ="zh-CN" xmlns:th ="http://www.w3.org/1999/xhtml" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="css/style.css" /> <link rel ="stylesheet" href ="css/list.css" /> <link rel ="stylesheet" href ="css/bootstrap.min.css" /> <link rel ="stylesheet" href ="css/login.css" /> <style > .login-dialog { width : 350px ; height : 350px ; } </style > <title > 添加图书</title > </head > <body > <header > <h1 > <a href ="/" > 图书管理系统</a > </h1 > <img src ="../images/logo.png" alt ="Logo" style ="width: 80px; height: 80px; margin-left: 10px;" /> <ul > <li > <a href ="/bookmanage" > 图书管理</a > </li > <li > <a href ="/usermanage" > 用户管理</a > </li > <li > <a href ="/person" > 个人中心</a > </li > </ul > </header > <main > <div class ="container-login" > <div class ="container-pic" > <img src ="../static/images/computer.png" width ="350px" alt ="" /> </div > <div class ="login-dialog" > <h3 > 添加图书</h3 > <div class ="row" > <span > 图书名称</span > <input type ="text" name ="Name" id ="bookName" class ="form-control" /> </div > <div class ="row" > <span > 出版日期</span > <input type ="date" name ="publicationDate" id ="publicationDate" class ="form-control" /> </div > <div class ="row" > <span > 出版社</span > <input type ="text" name ="Name" id ="publisher" class ="form-control" /> </div > <div class ="row" > <span > 书架</span > <input type ="text" name ="Name" id ="bookrackId" class ="form-control" /> </div > <div class ="row" > <button type ="button" class ="btn btn-info btn-lg" onclick ="send()" > 发送 </button > </div > </div > </div > </main > </body > <script > function send ( ) { fetch ("/editbook/add" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ bookName : document .querySelector ("#bookName" ).value , publicationDate : document .querySelector ("#publicationDate" ).value , publisher : document .querySelector ("#publisher" ).value , bookrackId : document .querySelector ("#bookrackId" ).value , }), }) .then ((response ) => response.json ()) .then ((data ) => { if (data.code === "0" ) { alert (data.data ); window .location .href = "/bookmanage" ; } else { alert (data.data ); } }) .catch ((error ) => console .error ("Error:" , error)); } </script > </html >
用户管理功能 添加实体类 Blacklist src/main/java/top/cengweiye/library/domain/Blacklist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package top.cengweiye.library.domain;import javax.persistence.*;@Entity @Table(name = "blacklist") public class Blacklist { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "borrow_book_id") private int borrowBookId; @Column(name = "name") private String name; public int getBorrowBookId () { return borrowBookId; } public void setBorrowBookId (int borrowBookId) { this .borrowBookId = borrowBookId; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getReason () { return reason; } public void setReason (String reason) { this .reason = reason; } @Column(name = "reason") private String reason; }
定义数据访问对象 BlacklistDao src/main/java/top/cengweiye/library/repository/BlacklistDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package top.cengweiye.library.repository;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;import top.cengweiye.library.domain.Blacklist;import java.util.Optional;@Repository public interface BlacklistDao extends JpaRepository <Blacklist, Integer> { Optional<Blacklist> findById (Integer borrowBookId) ; Optional<Blacklist> findByName (String name) ; void deleteByborrowBookId (int borrowBookId) ; }
BorrowDao src/main/java/top/cengweiye/library/repository/BorrowDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package top.cengweiye.library.repository;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.Query;import org.springframework.data.repository.query.Param;import top.cengweiye.library.domain.Borrow;import java.time.LocalDateTime;import java.util.List;public interface BorrowDao extends JpaRepository <Borrow, Integer> { Page<Borrow> findByBorrowBookId (int borrowBookId, Pageable pageable) ; void deleteByBookId (Integer bookId) ; @Query("SELECT b FROM Borrow b WHERE b.returnDate < :currentDate AND b.bookId IS NOT NULL") List<Borrow> findAllOverdueBorrows (@Param("currentDate") LocalDateTime currentDate) ; }
添加工具类 CheckBlackList(定时任务每天将逾期未归还的人加入黑名单) src/main/java/top/cengweiye/library/Utilities/CheckBlackList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package top.cengweiye.library.Utilities;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.transaction.annotation.Transactional;import top.cengweiye.library.domain.Blacklist;import top.cengweiye.library.domain.Borrow;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.BlacklistDao;import top.cengweiye.library.repository.BorrowDao;import top.cengweiye.library.repository.ReaderDao;import javax.annotation.Resource;import java.time.LocalDateTime;import java.util.List;import java.util.Optional;@EnableScheduling public class CheckBlackList { @Resource private BorrowDao borrowDao; @Resource private BlacklistDao blacklistDao; @Resource private ReaderDao readerDao; @Transactional @Scheduled(cron = "0 0 0 * * ?") public void checkOverdueAndBlacklist () { LocalDateTime currentDate = LocalDateTime.now(); List<Borrow> overdueBorrows = borrowDao.findAllOverdueBorrows(currentDate); for (Borrow borrow : overdueBorrows) { Optional<Blacklist> existingBlacklist = blacklistDao.findById(borrow.getBorrowBookId()); if (!existingBlacklist.isPresent()) { Blacklist blacklist = new Blacklist (); blacklist.setBorrowBookId(borrow.getBorrowBookId()); Reader reader = readerDao.findByBorrowBookId(borrow.getBorrowBookId()); blacklist.setName(reader.getName()); blacklist.setReason("借阅逾期未归还" ); blacklistDao.save(blacklist); } } } }
定义控制器 修改控制器,添加在黑名单里的人不能借书的代码段 BorrowController src/main/java/top/cengweiye/library/Controller/BorrowController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package top.cengweiye.library.Controller;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.cengweiye.library.Utilities.TokenUtil;import top.cengweiye.library.domain.Blacklist;import top.cengweiye.library.domain.Reader;import top.cengweiye.library.repository.BlacklistDao;import top.cengweiye.library.repository.ReaderDao;import top.cengweiye.library.service.BorrowService;import javax.annotation.Resource;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import java.util.HashMap;import java.util.Map;import java.util.Optional;@RestController @RequestMapping("/borrow") public class BorrowController { @Resource private BorrowService BorrowService; @Resource private ReaderDao ReaderDao; @Resource private BlacklistDao blacklistDao; @PostMapping public ResponseEntity<Map<String, Object>> borrowBook (@RequestBody Map<String, Object> requestBody, HttpServletRequest request) { Integer bookId = (Integer) requestBody.get("bookId" ); String name = null ; Cookie[] cookies = request.getCookies(); if (cookies != null ) { for (Cookie cookie : cookies) { if ("Authorization" .equals(cookie.getName())) { String token = cookie.getValue(); if (token != null ) { token = token.substring(6 ); if (TokenUtil.verify(token)) { name = TokenUtil.getUsernameFromToken(token); } } } } } Map<String, Object> response = new HashMap <>(); Reader reader = ReaderDao.findByName(name); Optional<Blacklist> blacklist = blacklistDao.findById(reader.getBorrowBookId()); if (blacklist.isPresent()){ response.put("success" , false ); response.put("message" , "您已被封禁,请联系管理员解封!" ); return ResponseEntity.ok(response); } int borrowBookId = reader.getBorrowBookId(); try { boolean success = BorrowService.borrowBook(bookId, borrowBookId); if (success) { response.put("success" , true ); } else { response.put("success" , false ); response.put("message" , "该书已经被借阅!" ); } } catch (Exception e) { response.put("success" , false ); response.put("message" , "发生错误:" + e.getMessage()); } return ResponseEntity.ok(response); } }
UsermanageController src/main/java/top/cengweiye/library/Controller/UsermanageController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package top.cengweiye.library.Controller;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.stereotype.Controller;import org.springframework.transaction.annotation.Transactional;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.*;import top.cengweiye.library.Utilities.Result;import top.cengweiye.library.domain.Blacklist;import top.cengweiye.library.repository.BlacklistDao;import javax.annotation.Resource;import java.util.Map;@Controller @RequestMapping("/usermanage") public class UsermanageController { @Resource private BlacklistDao blacklistDao; @GetMapping("") public String getBooks ( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size, Model model) { Page<Blacklist> blacklistsPage = blacklistDao.findAll(PageRequest.of(page, size)); model.addAttribute("blacklistsPage" , blacklistsPage); return "usermanage" ; } @Transactional @PostMapping("/clear") @ResponseBody public Result<String> clear (@RequestBody Map<String, Object> requestData) { Integer id = (Integer) requestData.get("id" ); blacklistDao.deleteByborrowBookId(id); return Result.success("解封成功" ); } }
前端页面 src/main/resources/templates/usermanage.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 <!DOCTYPE html > <html lang ="zh-CN" xmlns:th ="http://www.w3.org/1999/xhtml" xmlns ="" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <link rel ="stylesheet" href ="../css/style.css" /> <link rel ="stylesheet" href ="../css/list.css" /> <title > 图书管理系统</title > </head > <body > <header > <h1 > <a href ="/" > 图书管理系统</a > </h1 > <img src ="../images/logo.png" alt ="Logo" style ="width: 80px; height: 80px; margin-left: 10px;" /> <ul > <li > <a href ="/bookmanage" > 图书管理</a > </li > <li > <a href ="/person" > 个人中心</a > </li > </ul > </header > <main > <div class ="bookContainer" > <h2 > 黑名单管理</h2 > <table > <thead > <tr > <th > 用户ID</th > <th > 用户名</th > <th > 封禁原因</th > <th > 操作</th > </tr > </thead > <tbody > <tr th:each ="black : ${blacklistsPage.content}" > <td th:text ="${black.borrowBookId}" > </td > <td th:text ="${black.name}" > </td > <td th:text ="${black.reason}" > </td > <td > <div class ="op" > <a th:attr ="data-id=${black.borrowBookId}" onclick ="cl(this)" > 解封</a > </div > </td > </tr > <tr th:if ="${#lists.isEmpty(blacklistsPage.content)}" > <td colspan ="4" > 暂无黑名单数据</td > </tr > </tbody > </table > <div > <button th:if ="${blacklistsPage.hasPrevious()}" th:onclick ="'window.location.href=\'/usermanage?page=' + (${blacklistsPage.number} - 1) + '&size=' + ${blacklistsPage.size} + '\''" > 上一页 </button > <button th:if ="${blacklistsPage.hasNext()}" th:onclick ="'window.location.href=\'/usermanage?page=' + (${blacklistsPage.number} + 1) + '&size=' + ${blacklistsPage.size} + '\''" > 下一页 </button > </div > </div > </main > </body > <script > function cl (element ) { const id = parseInt (element.getAttribute ("data-id" ), 10 ); fetch ("/usermanage/clear" , { method : "POST" , headers : { "Content-Type" : "application/json" , }, body : JSON .stringify ({ id : id }), }) .then ((response ) => response.json ()) .then ((data ) => { if (data.code === "0" ) { alert (data.data ); location.reload (); } }) .catch ((error ) => console .error ("Error:" , error)); } </script > </html >
打包 生成 jar 包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 在library目录下执行 mvn clean package 会在target目录下生成 jar包 java -jar *.jar 即可运行 如果出现报错(jar中没有主清单属性) <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <mainClass>top.cengweiye.library.LibraryApplication</mainClass> <skip>true</skip> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> 就看看pom.xml中的这个字段,将 <skip>true</skip> 删除如下 <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <mainClass>top.cengweiye.library.LibraryApplication</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
生成 exe 文件(无需 jdk) 需要 exe4j、jre、jar 包(将 jre 文件夹和 jar 包放一个目录下)
1 jre文件夹在你的java文件夹下,也可以用我的
配置证书,不然会有水印 Name 和 Company 随便填 License key:A-XVK258563F-1p4lv7mg7sav 这里选择 JAR 打包 EXE 填写打包的 exe 名称,还有输出文件的位置 配置 exe 文件名与图标(图标可以不配置) 生成 64 位的文件 选择 jar 包位置 选择项目启动方法 jre 版本 一直 Next 即可
完善系统安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 系统安全 1、用户表示和鉴别 (1)口令认证:密码通过md5加密存放 (2)会话管理:通过JWT技术实现身份验证 (3)注册管理:必须使用复杂密码注册 2、存取控制 (1)普通用户:仅能 查询图书/借阅书籍/修改个人信息 (2)管理员: 查询图书/借阅书籍/修改图书信息/删除图书/添加图书/解封用户/修改个人信息 3、安全防护 (1)通过实现拦截器,确保用户只能访问授权的资源,防止访问未经授权的内容 (2)通过JPA接口化访问数据库,利用参数化查询有效防止SQL注入风险 (3)设置httponly,防止xss盗取cookie 4、安全审计 (1)记录系统日志 通过 Logback 框架进行日志记录 记录信息为:"控制台日志" "文件日志" "Hibernate SQL 查询日志" "Hibernate 参数绑定日志" "Spring JDBC 日志" (2)记录用户操作日志 操作时间 登录/登出/注册/非法访问 IP地址 设备信息 修改: (1)register.html 添加强密码验证 (2)application.properties、logback-spring.xml 实现日志记录功能 (3)BookManageController.java 实现身份验证 (4)ReaderServiceImpl.java 添加md5加密 (5)Log.java/ LogDao.java/ GetInfo.java/ ReaderController.java/ BookManageController 如何实现记录用户操作日志: (1)创建一个表 logs (2)定义实体类 Log (3)定义 数据访问对象 LogDao (4)创建工具类 GetInfo 获取ip信息
创建 日志表
1 2 3 4 5 6 7 8 create table logs( log_id int (11 ) auto_increment primary key, name varchar (255 ), op_time datetime(0 ), op varchar (255 ), ip varchar (255 ), dev_info varchar (255 ) );