SpringBoot + Thymeleaf 实战:手把手教你从零搭建一个婚纱租赁网站(附完整源码)
SpringBoot + Thymeleaf 实战:从零构建婚纱租赁平台全流程指南
每次看到婚礼现场新娘穿着漂亮的婚纱,我都会想:这些婚纱最终都去了哪里?事实上,婚纱租赁市场正在以每年15%的速度增长。作为开发者,我们完全可以用技术为这个浪漫行业搭建数字化桥梁。今天,我将带你用SpringBoot和Thymeleaf打造一个专业的婚纱租赁平台,过程中你会掌握企业级项目开发的完整方法论。
1. 项目架构设计与环境搭建
在开始编码之前,我们需要明确技术选型的理由。SpringBoot 2.7.x + Thymeleaf 3.0的组合,既保留了传统MVC模式的开发效率,又能享受现代前后端分离的开发体验。这种架构特别适合需要快速迭代的中小型项目。
开发环境准备清单:
- JDK 17(LTS版本长期支持)
- IntelliJ IDEA 2023.2(社区版足够)
- Maven 3.8.6(依赖管理)
- MySQL 8.0(或MariaDB 10.6)
创建项目时,在Spring Initializr中选择这些依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>提示:使用SpringBoot DevTools可以实现热部署,修改代码后无需重启服务
项目结构建议采用模块化设计:
src/main/java ├── com.wedding.rental │ ├── config # 配置类 │ ├── controller # 控制器 │ ├── model # 实体类 │ ├── repository # 数据访问层 │ ├── service # 业务逻辑层 │ └── util # 工具类 src/main/resources ├── static # 静态资源 ├── templates # 模板文件 └── application.yml # 配置文件2. 核心数据模型设计与实现
婚纱租赁业务的核心在于商品库存管理和订单流转。我们需要设计一组精确反映业务场景的实体关系。
主要实体关系图:
| 实体 | 关键字段 | 关联关系 |
|---|---|---|
| User | id, username, password, phone | 一对多Order |
| Dress | id, name, price, stock, images | 多对一Category |
| Category | id, name, description | 一对多Dress |
| Order | id, status, totalAmount, address | 多对一User, OrderItem |
| OrderItem | id, quantity, rentalDays | 多对一Order, Dress |
婚纱实体类的JPA实现示例:
@Entity public class Dress { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank private String name; @Min(0) private BigDecimal price; @Min(0) private Integer stock; @ElementCollection private List<String> images = new ArrayList<>(); @ManyToOne private Category category; // getters/setters省略 }注意:婚纱图片存储建议使用云存储服务,数据库只保存URL。本地开发时可先存储在static/upload目录
对于复杂的业务查询,我们可以使用Spring Data JPA的派生查询:
public interface DressRepository extends JpaRepository<Dress, Long> { List<Dress> findByCategoryId(Long categoryId); @Query("SELECT d FROM Dress d WHERE d.name LIKE %:keyword% OR d.description LIKE %:keyword%") List<Dress> search(@Param("keyword") String keyword); List<Dress> findTop5ByOrderByRentalCountDesc(); }3. 前后端交互与Thymeleaf实战
Thymeleaf的魅力在于它的自然模板特性。我们可以在纯HTML上直接开发,同时享受动态数据绑定的便利。
典型页面数据绑定示例:
<!-- 婚纱列表页片段 --> <div th:each="dress : ${dresses}" class="col-md-4"> <div class="card"> <img th:src="@{${dress.images[0]}}" class="card-img-top"> <div class="card-body"> <h5 th:text="${dress.name}">默认婚纱名称</h5> <p class="text-danger" th:text="'¥' + ${#numbers.formatDecimal(dress.price, 1, 2)}">0.00</p> <a th:href="@{/dress/detail/} + ${dress.id}" class="btn btn-primary">查看详情</a> </div> </div> </div>控制器中处理分页查询:
@GetMapping("/dresses") public String listDresses( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "12") int size, Model model) { Pageable pageable = PageRequest.of(page - 1, size, Sort.by("createTime").descending()); Page<Dress> dressPage = dressService.findAll(pageable); model.addAttribute("dresses", dressPage.getContent()); model.addAttribute("page", new PageWrapper<>(dressPage, "/dresses")); return "dress/list"; }常用Thymeleaf技巧:
- 条件显示:
th:if="${user != null}" - 循环遍历:
th:each="item : ${items}" - URL构建:
th:href="@{/order/{id}(id=${order.id})}" - 片段复用:
th:replace="fragments/header :: header" - 日期格式化:
th:text="${#dates.format(order.createTime, 'yyyy-MM-dd')}"
表单处理的最佳实践:
<form th:action="@{/order/create}" th:object="${orderForm}" method="post"> <div class="form-group"> <label>租赁天数</label> <input type="number" th:field="*{rentalDays}" class="form-control"> <p th:if="${#fields.hasErrors('rentalDays')}" th:errors="*{rentalDays}"></p> </div> <!-- 其他表单字段 --> </form>对应的控制器验证处理:
@PostMapping("/order/create") public String createOrder( @Valid @ModelAttribute OrderForm form, BindingResult result, @AuthenticationPrincipal User user) { if (result.hasErrors()) { return "order/create"; } Order order = orderService.createOrder(user, form); return "redirect:/order/" + order.getId(); }4. 业务逻辑与高级功能实现
婚纱租赁有几个特殊业务场景需要特别注意:库存扣减、租赁周期计算和押金管理。
订单创建流程伪代码:
- 验证用户登录状态
- 检查婚纱库存是否充足
- 计算租赁总金额(租金×天数 + 押金)
- 生成订单(状态为待支付)
- 预留库存(非立即扣减)
- 跳转到支付页面
库存服务的关键实现:
@Service @Transactional public class InventoryService { @Autowired private DressRepository dressRepository; public boolean reserveStock(Long dressId, int quantity) { Dress dress = dressRepository.findById(dressId) .orElseThrow(() -> new DressNotFoundException(dressId)); if (dress.getStock() >= quantity) { dress.setStock(dress.getStock() - quantity); dressRepository.save(dress); return true; } return false; } public void releaseStock(Long dressId, int quantity) { Dress dress = dressRepository.findById(dressId) .orElseThrow(() -> new DressNotFoundException(dressId)); dress.setStock(dress.getStock() + quantity); dressRepository.save(dress); } }定时任务处理逾期订单:
@Scheduled(cron = "0 0 10 * * ?") // 每天上午10点执行 public void checkOverdueOrders() { LocalDate today = LocalDate.now(); List<Order> overdueOrders = orderRepository .findByStatusAndReturnDateBefore(OrderStatus.RENTED, today); overdueOrders.forEach(order -> { order.setStatus(OrderStatus.OVERDUE); // 发送通知短信或邮件 notificationService.sendOverdueNotice(order.getUser(), order); }); orderRepository.saveAll(overdueOrders); }5. 安全防护与性能优化
婚纱租赁系统涉及用户隐私和支付信息,安全必须放在首位。
基础安全配置:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/order/**", "/user/**").authenticated() .anyRequest().permitAll() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .and() .logout() .logoutSuccessUrl("/") .and() .rememberMe() .and() .csrf().disable(); // 仅开发环境禁用,生产环境必须开启 } }性能优化措施:
- Thymeleaf模板缓存配置:
spring: thymeleaf: cache: true # 生产环境开启 mode: HTML encoding: UTF-8- 静态资源版本控制:
<link th:href="@{/css/style.css(v=${@environment.getProperty('app.version')})}" rel="stylesheet">- 数据库连接池配置:
spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000- 二级缓存集成(以Ehcache为例):
@Entity @Cacheable @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class Category { // ... }6. 部署上线与监控维护
将SpringBoot应用部署到生产环境需要考虑多方面因素。以下是经过验证的部署方案:
Docker化部署方案:
FROM openjdk:17-jdk-slim VOLUME /tmp ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]启动命令示例:
docker build -t wedding-rental . docker run -d -p 8080:8080 \ -e SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/wedding \ -e SPRING_DATASOURCE_USERNAME=root \ -e SPRING_DATASOURCE_PASSWORD=secret \ --name wedding-app wedding-rental关键监控指标:
- 应用健康状态:
/actuator/health - 性能指标:
/actuator/metrics - 最近HTTP请求:
/actuator/httptrace - 线程转储:
/actuator/threaddump
对于婚纱图片这种静态资源,建议使用CDN加速。Nginx配置示例:
server { listen 80; server_name wedding-rental.com; location / { proxy_pass http://wedding-app:8080; proxy_set_header Host $host; } location /upload/ { alias /data/upload/; expires 30d; access_log off; } }在项目开发过程中,我特别推荐使用Git进行版本控制。一个典型的Git工作流应该是:
feature/分支开发新功能develop分支集成测试release/分支准备上线master分支生产环境代码
每次部署后,记得检查这些关键点:
- 数据库迁移脚本是否执行成功
- 定时任务是否正常启动
- 第三方服务(如短信、支付)连接是否正常
- 文件存储目录权限是否正确
