1. 项目概述
作为一名有多年Java开发经验的工程师,最近我完成了一个基于SpringBoot的玩具租赁系统毕业设计项目。这个系统旨在解决现代家庭在儿童玩具使用上的痛点——玩具更新换代快、占用空间大、使用周期短等问题。通过线上租赁的方式,家长可以按需租用各类玩具,既节省开支又减少资源浪费。
系统采用经典的B/S架构,前端使用Thymeleaf模板引擎配合Bootstrap框架,后端基于SpringBoot 2.7.3开发,数据库选用MySQL 8.0。整个开发周期约3个月,最终实现了一个功能完备、性能稳定的玩具租赁平台。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择SpringBoot
SpringBoot是我选择的核心框架,主要基于以下几点考虑:
-
快速开发:SpringBoot的自动配置和起步依赖特性,让项目搭建变得极其简单。通过spring-boot-starter-web、spring-boot-starter-data-jpa等依赖,我几乎零配置就完成了基础环境搭建。
-
内嵌服务器:Tomcat服务器内嵌在应用中,无需额外部署,简化了开发和测试流程。通过application.yml文件就能轻松配置服务器端口、上下文路径等参数。
-
丰富的生态:Spring生态中有大量现成的解决方案,如Spring Security用于认证授权、Spring Data JPA操作数据库等,大幅减少了重复造轮子的工作。
2.2 系统架构设计
系统采用典型的三层架构:
code复制表示层(Web Layer)
│
├── Thymeleaf模板引擎
├── Bootstrap 5前端框架
└── jQuery AJAX交互
业务逻辑层(Service Layer)
│
├── Spring MVC控制器
├── 业务服务组件
└── 事务管理
数据访问层(Data Access Layer)
│
├── Spring Data JPA
├── MySQL 8.0数据库
└── Redis缓存
这种分层设计确保了各层职责单一,便于后期维护和扩展。例如当需要更换前端框架时,只需修改表示层而不会影响业务逻辑。
3. 核心功能实现
3.1 用户管理模块
用户模块采用了RBAC(基于角色的访问控制)模型,主要包含以下功能点:
java复制// 用户实体类核心字段
@Entity
public class User {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userId;
@Column(nullable=false, unique=true)
private String username;
@Column(nullable=false)
private String password; // 实际存储BCrypt加密后的密文
@Enumerated(EnumType.STRING)
private UserRole role; // ADMIN, MEMBER, GUEST
// 其他字段及getter/setter
}
密码存储采用BCrypt强哈希算法,这是目前最推荐的方式:
java复制// 密码加密工具类
public class PasswordEncoder {
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public static String encode(String rawPassword) {
return encoder.encode(rawPassword);
}
public static boolean matches(String rawPassword, String encodedPassword) {
return encoder.matches(rawPassword, encodedPassword);
}
}
安全提示:绝对不要在数据库中存储明文密码!BCrypt算法会自动处理salt,比MD5/SHA等传统哈希更安全。
3.2 玩具管理模块
玩具管理采用了分类树形结构,支持多级分类:
sql复制CREATE TABLE `toy_category` (
`category_id` int NOT NULL AUTO_INCREMENT,
`parent_id` int DEFAULT NULL COMMENT '父分类ID',
`name` varchar(50) NOT NULL,
`level` tinyint NOT NULL COMMENT '分类层级',
PRIMARY KEY (`category_id`),
KEY `idx_parent` (`parent_id`),
CONSTRAINT `fk_parent` FOREIGN KEY (`parent_id`) REFERENCES `toy_category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
前端展示时,我使用递归方式渲染分类树:
javascript复制function renderCategoryTree(categories, parentId = null) {
return categories
.filter(cat => cat.parentId === parentId)
.map(cat => `
<li>
<a href="/toys?category=${cat.id}">${cat.name}</a>
${categories.some(c => c.parentId === cat.id)
? `<ul>${renderCategoryTree(categories, cat.id)}</ul>`
: ''}
</li>
`).join('');
}
3.3 租赁流程实现
租赁业务是系统的核心,其状态机设计如下:
code复制[待支付] → [已支付] → [已发货]
↓ ↓
[已取消] ← [已归还]
对应的实体关系:
java复制@Entity
public class RentalOrder {
@Id @GeneratedValue
private Long orderId;
@ManyToOne
private User user;
@ManyToOne
private ToyItem toy;
private LocalDate startDate;
private LocalDate endDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
// 其他字段...
}
public enum OrderStatus {
PENDING_PAYMENT, PAID, SHIPPED,
COMPLETED, CANCELLED, OVERDUE
}
4. 支付系统集成
4.1 支付流程设计
考虑到毕业设计的实际情况,我集成了支付宝沙箱环境进行模拟支付:
- 前端发起支付请求 → 2. 后端生成支付参数 → 3. 跳转支付宝页面 → 4. 异步通知处理
核心的支付参数生成代码:
java复制public Map<String, String> createAlipayParams(RentalOrder order) {
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipaydev.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2");
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", order.getOrderNumber());
bizContent.put("total_amount", order.getTotalAmount());
bizContent.put("subject", "玩具租赁订单");
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
return alipayClient.pageExecute(request).getBody();
}
4.2 支付安全措施
- 参数签名:所有支付请求参数都经过RSA2签名,防止篡改
- 异步验证:支付成功后,支付宝服务器会发送异步通知,需验证签名和订单状态
- 幂等处理:同一笔订单可能收到多次通知,需要确保业务逻辑只执行一次
5. 性能优化实践
5.1 数据库优化
-
索引优化:为所有外键和常用查询字段添加索引
sql复制CREATE INDEX idx_toy_category ON toy(category_id); CREATE INDEX idx_order_user ON rental_order(user_id); -
查询优化:使用JPA的@EntityGraph解决N+1查询问题
java复制@EntityGraph(attributePaths = {"category", "images"}) Page<Toy> findByCategoryId(Long categoryId, Pageable pageable);
5.2 缓存策略
对热点数据使用Redis缓存:
java复制@Service
@CacheConfig(cacheNames = "toyCache")
public class ToyService {
@Cacheable(key = "#id")
public Toy getToyById(Long id) {
return toyRepository.findById(id).orElseThrow();
}
@CacheEvict(key = "#toy.id")
public Toy updateToy(Toy toy) {
return toyRepository.save(toy);
}
}
缓存配置(application.yml):
yaml复制spring:
cache:
type: redis
redis:
host: localhost
port: 6379
6. 部署与测试
6.1 生产环境部署
使用Docker Compose进行容器化部署:
dockerfile复制# Dockerfile
FROM openjdk:11-jre
COPY target/toy-rental-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
yaml复制# docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "8080:8080"
depends_on:
- redis
- mysql
redis:
image: redis:6
ports:
- "6379:6379"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: toy_rental
ports:
- "3306:3306"
6.2 压力测试结果
使用JMeter进行模拟测试(100并发):
| 接口 | 平均响应时间 | 错误率 | TPS |
|---|---|---|---|
| 玩具列表 | 128ms | 0% | 235 |
| 下单接口 | 253ms | 1.2% | 189 |
| 支付回调 | 89ms | 0% | 310 |
7. 开发经验总结
在开发这个系统的过程中,我积累了一些值得分享的经验:
-
实体设计要先于编码:花足够时间设计数据库ER图,特别是关联关系。我中途因为最初设计不足,不得不重构了两次数据模型。
-
合理使用SpringBoot特性:
- 使用@ConfigurationProperties管理配置
- 利用Spring Boot Actuator监控应用健康状态
- 通过@ExceptionHandler统一异常处理
-
前端与后端分离开发:虽然这个项目使用了Thymeleaf,但我建议新项目考虑前后端完全分离,使用Vue/React等现代框架。
-
测试驱动开发:为关键业务逻辑编写单元测试(JUnit 5)和集成测试(Testcontainers),这帮我发现了许多边界条件问题。
这个项目让我对SpringBoot生态有了更深入的理解,特别是在事务管理、缓存集成和性能优化方面。虽然作为毕业设计已经达标,但商业级应用还需要考虑更多因素,如分布式锁、熔断降级等。
