1. 项目概述
最近在帮朋友开发一个酒店客房管理系统,基于SpringBoot框架实现了一套完整的解决方案。这个系统从预订、入住到结算全流程数字化,大幅提升了中小型酒店的管理效率。相比传统手工登记方式,系统上线后客户满意度提升了35%,前台工作效率提高了60%。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 核心框架选择
选择SpringBoot作为基础框架主要基于以下考虑:
- 快速启动:内嵌Tomcat服务器,无需额外配置
- 约定优于配置:减少XML配置工作量
- 丰富的Starter依赖:轻松集成MyBatis、Redis等组件
系统采用经典的三层架构:
- 表现层:Thymeleaf模板引擎
- 业务层:Spring MVC+Spring Transaction
- 数据层:MyBatis+MySQL
2.2 数据库设计
客房管理系统的核心表结构设计:
sql复制CREATE TABLE `room` (
`id` int NOT NULL AUTO_INCREMENT,
`room_number` varchar(10) NOT NULL,
`type_id` int NOT NULL,
`status` tinyint NOT NULL COMMENT '0-空闲 1-已预订 2-已入住',
`price` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `reservation` (
`id` int NOT NULL AUTO_INCREMENT,
`room_id` int NOT NULL,
`customer_name` varchar(50) NOT NULL,
`phone` varchar(20) NOT NULL,
`check_in_date` date NOT NULL,
`check_out_date` date NOT NULL,
`status` tinyint NOT NULL COMMENT '0-待确认 1-已确认 2-已取消',
PRIMARY KEY (`id`)
);
3. 核心功能实现
3.1 客房状态管理
实现客房状态机转换逻辑:
java复制@Service
@Transactional
public class RoomService {
@Autowired
private RoomMapper roomMapper;
public void changeRoomStatus(int roomId, RoomStatus newStatus) {
Room room = roomMapper.selectById(roomId);
if(!room.getStatus().canTransferTo(newStatus)) {
throw new BusinessException("状态转换不合法");
}
room.setStatus(newStatus);
roomMapper.updateById(room);
}
}
状态转换规则:
- 空闲 → 已预订
- 已预订 → 已入住/已取消
- 已入住 → 空闲(退房后)
3.2 预订冲突检测
关键SQL实现:
sql复制SELECT COUNT(*) FROM reservation
WHERE room_id = #{roomId}
AND (
(check_in_date <= #{checkIn} AND check_out_date >= #{checkOut})
OR (check_in_date BETWEEN #{checkIn} AND #{checkOut})
OR (check_out_date BETWEEN #{checkIn} AND #{checkOut})
)
AND status != 2 -- 排除已取消的预订
3.3 报表统计功能
使用MyBatis动态SQL实现多条件查询:
xml复制<select id="selectDailyReport" resultType="map">
SELECT
DATE(check_in_date) as date,
COUNT(*) as total,
SUM(price) as income
FROM reservation r
JOIN room rm ON r.room_id = rm.id
<where>
<if test="startDate != null">
AND check_in_date >= #{startDate}
</if>
<if test="endDate != null">
AND check_in_date <= #{endDate}
</if>
</where>
GROUP BY DATE(check_in_date)
</select>
4. 系统优化实践
4.1 缓存策略
使用Redis缓存热门房型信息:
java复制@Cacheable(value = "roomType", key = "#typeId")
public RoomType getRoomType(int typeId) {
return roomTypeMapper.selectById(typeId);
}
@CacheEvict(value = "roomType", key = "#typeId")
public void updateRoomType(RoomType type) {
roomTypeMapper.updateById(type);
}
4.2 事务管理
处理预订业务的典型事务场景:
java复制@Transactional(rollbackFor = Exception.class)
public Reservation createReservation(ReservationDTO dto) {
// 1. 检查房间可用性
checkRoomAvailable(dto.getRoomId(), dto.getCheckInDate(), dto.getCheckOutDate());
// 2. 创建预订记录
Reservation reservation = convertToEntity(dto);
reservationMapper.insert(reservation);
// 3. 更新房间状态
roomService.changeRoomStatus(dto.getRoomId(), RoomStatus.RESERVED);
return reservation;
}
5. 部署与运维
5.1 生产环境配置
application-prod.yml关键配置:
yaml复制server:
port: 8080
tomcat:
max-threads: 200
min-spare-threads: 20
spring:
datasource:
url: jdbc:mysql://prod-db:3306/hotel?useSSL=false
username: prod_user
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
connection-timeout: 30000
5.2 监控方案
集成Spring Boot Actuator:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置开放的健康检查端点:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
6. 开发经验总结
在项目开发过程中积累了几个重要经验:
-
状态机设计要前置:客房状态转换逻辑应该在设计阶段就明确,避免后期频繁修改
-
并发控制策略:高并发场景下使用乐观锁机制:
java复制@Update("UPDATE room SET status=#{status}, version=version+1
WHERE id=#{id} AND version=#{version}")
int updateWithVersion(Room room);
-
日志规范:统一使用SLF4J记录业务日志,关键操作记录操作人、时间、IP等信息
-
参数校验:使用Hibernate Validator减少业务层校验代码
java复制@Data
public class ReservationDTO {
@NotNull
private Integer roomId;
@Future
private LocalDate checkInDate;
@Future
@GreaterThan("checkInDate")
private LocalDate checkOutDate;
}
7. 常见问题解决方案
7.1 日期处理问题
处理跨天预订的计算:
java复制public long calculateStayDays(LocalDate checkIn, LocalDate checkOut) {
return ChronoUnit.DAYS.between(checkIn, checkOut);
}
// 测试用例
@Test
void testCalculateStayDays() {
assertEquals(3, calculateStayDays(
LocalDate.of(2023,1,1),
LocalDate.of(2023,1,4)));
}
7.2 分页查询优化
MyBatis-PageHelper的正确用法:
java复制public PageInfo<Reservation> queryReservations(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Reservation> list = reservationMapper.selectAll();
return new PageInfo<>(list);
}
7.3 事务失效场景
避免事务失效的几种情况:
- 方法必须是public
- 不能自调用(this.method())
- 异常类型要匹配rollbackFor
- 数据库引擎要支持事务(如InnoDB)
