基于Spring Boot构建RESTful API,集成MySQL实现数据持久化,设计Book/User实体类映射数据库表结构,通过@RestController暴露借阅/归还/查询接口,结合JWT实现权限认证,采用Lombok简化POJO
技术选型与架构设计
组件 | 技术方案 | 说明 |
---|
后端框架 | Spring Boot 3.x + Java 17 | 快速构建RESTful API,内置Tomcat容器 |
数据库 | MySQL 8.0 | 存储用户、图书、借阅记录等核心数据 |
ORM框架 | Spring Data JPA | 简化数据库操作,自动生成查询方法 |
安全框架 | Spring Security + JWT | 实现用户认证与权限控制 |
日志系统 | Logback + ELK(可选) | 记录系统运行日志,便于问题追踪 |
API文档 | Swagger 3.0 | 自动生成可视化接口文档 |
数据库设计
表结构设计
表名 | 字段说明 |
---|
users | id (主键): 用户唯一标识
username : 用户名
password : 加密密码
role : 用户角色(ADMIN/USER)
created_at : 创建时间 |
books | id (主键): 图书唯一标识
title : 书名
author : 作者
isbn : ISBN号
category : 分类
stock : 库存数量
status : 状态(AVAILABLE/BORROWED) |
borrow_records | id (主键): 记录唯一标识
user_id (外键): 借阅用户ID
book_id (外键): 图书ID
borrow_time : 借阅时间
return_time : 归还时间
due_time : 到期时间 |
关键约束
- 外键约束:
borrow_records.user_id
关联users.id
,borrow_records.book_id
关联books.id
- 唯一索引:
users.username
唯一,books.isbn
唯一 - 软删除:通过
status
字段标记数据状态(如图书下架)
核心API设计
用户管理
功能 | API路径 | 方法 | 权限 | 说明 |
---|
用户注册 | /api/users/register | POST | 无 | 密码需加密存储(BCrypt) |
用户登录 | /api/users/login | POST | 无 | 返回JWT令牌,包含角色信息 |
获取用户信息 | /api/users/me | GET | USER/ADMIN | 需携带有效JWT |
删除用户(管理员) | /api/users/{id} | DELETE | ADMIN | 仅管理员可删除普通用户 |
图书管理
功能 | API路径 | 方法 | 权限 | 说明 |
---|
查询所有图书 | /api/books | GET | USER/ADMIN | 支持分页、按分类/作者/ISBN筛选 |
添加图书(管理员) | /api/books | POST | ADMIN | 必填字段:title, author, isbn, stock |
修改图书信息 | /api/books/{id} | PUT | ADMIN | 可修改库存、状态等 |
删除图书 | /api/books/{id} | DELETE | ADMIN | 逻辑删除(status标记) |
借阅管理
功能 | API路径 | 方法 | 权限 | 说明 |
---|
借阅图书 | /api/borrow/{bookId} | POST | USER | 检查库存>0且用户未超最大借阅数 |
归还图书 | /api/return/{bookId} | POST | USER | 更新归还时间,释放库存 |
查询借阅记录 | /api/borrow/records | GET | USER/ADMIN | 分页展示借阅历史 |
关键代码实现
实体类定义(Java)
// User.java
@Entity
@Table(name = "users")
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password; // BCrypt加密存储
private String role; // ADMIN/USER
private LocalDateTime createdAt;
// Getters/Setters省略
}
// Book.java
@Entity
@Table(name = "books")
public class Book {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private String isbn;
private String category;
private Integer stock;
private String status; // AVAILABLE/BORROWED
// Getters/Setters省略
}
借阅逻辑(Service层)
// BorrowService.java
@Transactional
public void borrowBook(Long userId, Long bookId) {
// 1. 检查图书状态
Book book = bookRepository.findById(bookId)
.orElseThrow(() -> new ResourceNotFoundException("Book not found"));
if (!BookStatus.AVAILABLE.equals(book.getStatus())) {
throw new BadRequestException("Book is not available");
}
// 2. 检查用户借阅数量
int borrowCount = borrowRecordRepository.countByUserIdAndReturnTimeIsNull(userId);
if (borrowCount >= MAX_BORROW_LIMIT) {
throw new BadRequestException("Exceed maximum borrow limit");
}
// 3. 更新图书状态并保存记录
book.setStatus(BookStatus.BORROWED);
bookRepository.save(book);
BorrowRecord record = new BorrowRecord();
record.setUserId(userId);
record.setBookId(bookId);
record.setBorrowTime(LocalDateTime.now());
record.setDueTime(LocalDateTime.now().plusDays(BORROW_PERIOD_DAYS));
borrowRecordRepository.save(record);
}
JWT认证流程
// AuthController.java
@PostMapping("/login")
public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) {
// 1. 验证用户名密码
User user = userRepository.findByUsername(request.getUsername())
.orElseThrow(() -> new BadCredentialsException("Invalid credentials"));
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new BadCredentialsException("Invalid credentials");
}
// 2. 生成JWT令牌
String token = jwtUtils.generateToken(user.getId(), user.getRole());
// 3. 返回响应
return ResponseEntity.ok(new TokenResponse(token, user.getRole()));
}
单元测试示例
测试场景 | 方法名 | 预期结果 |
---|
用户注册成功 | testRegisterUser | 返回HTTP 201,用户存入数据库 |
图书库存不足 | testBorrowBookStockZero | 抛出BadRequestException |
管理员添加图书 | testAdminAddBook | 返回HTTP 201,图书可查询到 |
JWT令牌验证失败 | testInvalidToken | 返回HTTP 401,拒绝访问 |
相关问题与解答
问题1:如何扩展系统支持图书分类管理?
解答:
- 新增分类表:创建
categories
表,存储分类ID和名称。 - 修改图书表:在
books
表中添加category_id
外键字段,关联categories
表。 - API调整:
- 添加
/api/categories
接口用于增删改查分类。 - 在查询图书接口中增加
categoryId
筛选参数。
- 前端适配:在图书详情页展示分类信息,并提供按分类筛选功能。
问题2:如何实现逾期归还罚款功能?
解答:
- 数据库调整:在
borrow_records
表中添加fine_amount
字段(默认0)。 - 归还逻辑修改:
- 在
returnBook
方法中检查当前时间是否超过due_time
。 - 如果逾期,按每日罚款金额计算总罚款(例如每日租金的10%)。
- 更新
fine_amount
字段并持久化。
- API扩展:
- 新增
/api/fines/{userId}
接口,查询用户未支付罚款总额。 - 在用户支付罚款后,更新罚款状态并记录