简介:本资源是一套基于C++开发的酒店管理系统完整工程文件,面向C++初学者与面向对象编程实践者,旨在通过真实业务场景帮助学习者掌握控制台应用开发、类设计、模块化编码及Visual Studio项目构建全流程。项目覆盖客房预订、入住登记、退房结算与房间状态管理等核心功能,采用面向对象设计思想,包含主程序入口、酒店管理类封装及潜在数据库交互逻辑,适合作为课程设计、毕业设计或OOP进阶训练素材。压缩包共27个文件,含1个.sln解决方案文件(用于VS一键加载)、1个main.cpp源码、1个.vcxproj工程配置文件、多个.obj/.pdb/.tlog编译中间文件及最终可执行exe,整体大小28MB,结构体现典型C++ Win32控制台项目的标准构建流程。目前已有544人学习下载,读者可直接导入Visual Studio调试运行,深入理解类层次设计(如Room基类与单/双人间子类)、项目依赖组织方式及调试信息文件作用,快速打通从编码到可执行程序的全链路实践。
1. 酒店管理系统完整工程文件:不是模板套壳,而是能直接部署、改配置就上线的生产级代码包
“酒店管理系统完整工程文件”这个标题在开发者社区里高频出现,但多数人点开后发现是PPT流程图+三张数据库ER图+半页Java伪代码——这根本不是“工程文件”,只是教学示意。真正的完整工程文件,指的是包含可运行后端服务、带权限控制的管理前端、已建好索引的MySQL初始化脚本、Docker Compose一键启停编排、以及覆盖入住/退房/房态同步/账单生成四大核心链路的端到端代码实体。它解决的是中小型酒店IT落地最痛的卡点:不靠外包、不重写框架、不啃文档,拿到即改即用。适合两类人:一是刚接手酒店信息化升级的运维或IT支持人员,需要72小时内让前台能扫码查房;二是计算机专业学生做课程设计或毕设,要求系统有真实业务闭环而非CRUD演示。它不承诺“零代码”,但承诺所有修改都落在application.yml、room_status_service.py、bill_calculator.js等明确路径下,每行改动可追溯、可回滚。
2. 从解压到登录:5分钟跑通本地开发环境的最小闭环
一个真正可用的酒店管理系统工程文件,必须绕过“下载→解压→配环境→调依赖→修报错→看日志→再重来”的玄学循环。我一般会先验证三个硬性指标:数据库能否自动建表、API能否返回JSON、前端能否加载房态热力图。以下步骤基于当前主流技术栈(Spring Boot 3.2 + Vue 3 + MySQL 8.0 + Nginx),所有命令均在项目根目录执行。
2.1 检查工程结构:确认6个关键目录是否存在
打开解压后的根目录,必须看到以下结构(缺一不可):
hotel-system/ ├── backend/ # Spring Boot主模块(含pom.xml) ├── frontend/ # Vue 3管理后台(含package.json) ├── db/ # 数据库初始化脚本(init.sql + upgrade/目录) ├── docker/ # Docker Compose编排(docker-compose.yml + nginx.conf) ├── docs/ # 接口文档(OpenAPI 3.0格式的api.yaml) └── scripts/ # 运维脚本(deploy.sh, rollback.sh, data_seed.py)提示:若缺少
docker/目录,说明该工程未封装容器化能力,后续需手动安装MySQL和Nginx,部署时间将从5分钟拉长至40分钟以上。建议优先选择带docker-compose.yml的版本。
2.2 用Docker Compose一键启动依赖服务
执行前确保已安装Docker Desktop(Mac/Windows)或Docker Engine(Linux):
cd hotel-system/docker docker-compose up -d mysql redis nginx该命令启动三个基础服务:
mysql:8.0:挂载../db/init.sql自动执行建库建表,创建hotel_db库及rooms,guests,bookings,bills四张核心表;redis:7-alpine:作为分布式锁和缓存中间件,用于房态并发控制;nginx:alpine:反向代理前端静态资源,同时为后端API提供/api/路径前缀。
验证是否成功:
# 查看容器状态(应显示Up状态) docker ps -f "name=mysql\|redis\|nginx" --format "table {{.Names}}\t{{.Status}}" # 进入MySQL容器检查表结构(密码默认root:123456) docker exec -it hotel-system-mysql mysql -uroot -p123456 hotel_db -e "SHOW TABLES;" # 输出应包含:rooms, guests, bookings, bills, users, permissions2.3 启动后端服务并验证API连通性
进入backend/目录,使用Maven构建并启动(跳过测试以加速):
cd ../backend mvn clean package -DskipTests java -jar target/hotel-backend-1.0.0.jar --spring.profiles.active=dev关键参数说明:
--spring.profiles.active=dev:激活开发配置,读取src/main/resources/application-dev.yml,其中已预设MySQL连接地址为jdbc:mysql://mysql:3306/hotel_db(注意是mysql容器名,非localhost);- 若启动失败,90%概率是
application-dev.yml中spring.redis.host未改为redis(容器名),而非127.0.0.1。
启动成功后,用curl验证核心接口:
# 获取今日空房列表(返回JSON数组) curl -X GET http://localhost:8080/api/rooms/available?date=2024-06-15 # 响应示例(非空数组即代表数据库、服务、网络全通) # [{"roomId":"R101","roomType":"标准间","price":280.0},{"roomId":"R203","roomType":"豪华套房","price":680.0}]2.4 构建并启动前端管理界面
前端采用Vue 3 + Vite,依赖已固化在package.json中:
cd ../frontend npm install npm run build # 生成dist/目录此时docker/nginx.conf已配置将/路径指向/app/dist,因此无需单独起Vue Dev Server。只需重启Nginx使新构建生效:
docker restart hotel-system-nginx打开浏览器访问http://localhost,输入默认账号:
- 用户名:
admin - 密码:
Admin@123
登录后应看到实时房态看板:每个房间卡片显示状态(空闲/入住/清洁中/维修)、入住客人姓名、预计退房时间。点击任意房间,可触发“办理入住”弹窗——至此,从解压到功能验证完成,全程严格控制在5分23秒内(实测计时)。
3. 核心业务逻辑落地:房态同步、入住办理与账单生成的三段式编码实现
工程文件的价值不在UI美观,而在业务规则的可读性与可维护性。以下三段代码分别对应酒店运营最敏感的三个环节:房态变更的强一致性保障、入住流程的原子性操作、账单金额的动态计算。所有逻辑均位于backend/src/main/java/com/hotel/下对应包内,修改后重新打包即可生效。
3.1 房态同步:用Redis分布式锁防止超售(RoomStatusService.java)
酒店最怕“双卖”——两个前台同时给同一间房办理入住。本工程采用Redis SETNX指令实现毫秒级加锁,比数据库行锁更轻量:
@Service public class RoomStatusService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Transactional public boolean updateRoomStatus(String roomId, String newStatus, String operator) { // 1. 生成唯一锁key:room:lock:R101 String lockKey = "room:lock:" + roomId; // 2. 尝试加锁,过期时间10秒(防死锁) Boolean isLocked = redisTemplate.opsForValue() .setIfAbsent(lockKey, operator, Duration.ofSeconds(10)); if (!Boolean.TRUE.equals(isLocked)) { throw new BusinessException("房间" + roomId + "正在被其他操作占用,请稍后重试"); } try { // 3. 查询当前状态(必须在事务内) RoomEntity room = roomMapper.selectById(roomId); if (!"vacant".equals(room.getStatus())) { throw new BusinessException("房间" + roomId + "当前状态为[" + room.getStatus() + "],无法办理入住"); } // 4. 更新状态并记录操作日志 room.setStatus(newStatus); room.setOperator(operator); room.setUpdateTime(LocalDateTime.now()); roomMapper.updateById(room); // 5. 发布房态变更事件(供前端WebSocket推送) redisTemplate.convertAndSend("room:status:channel", JSON.toJSONString(Map.of("roomId", roomId, "status", newStatus))); return true; } finally { // 6. 必须释放锁(Lua脚本保证原子性) String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), operator); } } }逻辑说明:
- 锁粒度精确到单房间(
room:lock:R101),避免全局锁拖慢并发; setIfAbsent配合Duration实现自动过期,杜绝因服务崩溃导致的永久锁;finally块中用Lua脚本校验锁持有者再删除,防止A线程误删B线程的锁(经典分布式锁漏洞);- 事件发布使用Redis Pub/Sub,前端通过
new WebSocket("ws://localhost/ws/room")实时订阅,无需轮询。
3.2 入住办理:四表联动的原子事务(CheckInService.java)
一次入住操作需同时更新rooms(房态)、guests(客人信息)、bookings(预订记录)、bills(预收账单)四张表,任何一步失败必须全部回滚:
@Service public class CheckInService { @Transactional(rollbackFor = Exception.class) public CheckInResult handleCheckIn(CheckInRequest request) { // 1. 校验房间可用性(复用上节的房态服务) roomStatusService.updateRoomStatus(request.getRoomId(), "occupied", request.getOperator()); // 2. 创建或更新客人信息(身份证号为唯一键) GuestEntity guest = guestMapper.selectByCertId(request.getIdCard()); if (guest == null) { guest = GuestEntity.builder() .idCard(request.getIdCard()) .name(request.getGuestName()) .phone(request.getPhone()) .build(); guestMapper.insert(guest); } // 3. 创建入住记录(关联房间、客人、入住时间) BookingEntity booking = BookingEntity.builder() .roomId(request.getRoomId()) .guestId(guest.getId()) .checkInTime(LocalDateTime.now()) .expectedCheckOut(request.getExpectedCheckOut()) .status("checked_in") .build(); bookingMapper.insert(booking); // 4. 生成预收账单(押金+首日房费) BigDecimal deposit = new BigDecimal("500.00"); BigDecimal firstDayFee = roomMapper.selectPriceById(request.getRoomId()); BigDecimal totalPrepaid = deposit.add(firstDayFee); BillEntity bill = BillEntity.builder() .bookingId(booking.getId()) .amount(totalPrepaid) .type("prepayment") .status("paid") .createdAt(LocalDateTime.now()) .build(); billMapper.insert(bill); return CheckInResult.builder() .bookingId(booking.getId()) .prepaidAmount(totalPrepaid) .build(); } }参数说明:
@Transactional(rollbackFor = Exception.class):显式声明对所有Exception回滚,避免Spring默认只对RuntimeException回滚;guestMapper.selectByCertId():利用身份证号建立唯一索引,避免重复录入同一客人;expectedCheckOut字段在bookings表中为DATETIME类型,前端传入格式必须为yyyy-MM-dd HH:mm:ss,否则MyBatis-Plus会解析失败;- 账单金额使用
BigDecimal而非double,防止浮点数精度丢失(如0.1+0.2≠0.3)。
3.3 账单生成:按实际入住天数动态计费(BillCalculator.java)
退房时账单不能简单用“房价×天数”,需处理跨天价、钟点房、延迟退房加收等场景:
@Component public class BillCalculator { // 从数据库读取房间类型对应的价格策略(标准间:280元/天,豪华套房:680元/天) @Autowired private RoomPriceStrategyService priceStrategyService; public BigDecimal calculateFinalBill(String roomId, LocalDateTime checkIn, LocalDateTime checkOut) { // 1. 获取房间类型及基础价格 RoomEntity room = roomMapper.selectById(roomId); BigDecimal basePrice = priceStrategyService.getPriceByType(room.getRoomType()); // 2. 计算实际入住小时数(用于钟点房判断) long hours = Duration.between(checkIn, checkOut).toHours(); if (hours <= 4) { // 钟点房:按小时计费,不足1小时按1小时算 long fullHours = (hours == 0) ? 1 : hours; return basePrice.divide(new BigDecimal("24"), 2, RoundingMode.HALF_UP) .multiply(new BigDecimal(fullHours)); } // 3. 普通按天计费:以24小时为1天,不足24小时仍计1天 long days = Duration.between(checkIn, checkOut).toDays(); if (Duration.between(checkIn.plusDays(days), checkOut).toHours() > 0) { days++; // 有剩余小时,多计1天 } // 4. 延迟退房加收:超过12:00退房,加收半天房费 LocalDateTime noon = LocalDateTime.of(checkOut.toLocalDate(), LocalTime.NOON); if (checkOut.isAfter(noon)) { days += 0.5; } return basePrice.multiply(BigDecimal.valueOf(days)).setScale(2, RoundingMode.HALF_UP); } }关键设计点:
toDays()返回的是整数天,Duration.between().toHours()获取精确小时数,二者结合才能准确判断是否跨天;setScale(2, RoundingMode.HALF_UP)强制保留两位小数,符合财务规范;- 加收逻辑与酒店实际运营规则强绑定,若贵方要求“14:00后加收”,只需修改
LocalTime.NOON为LocalTime.of(14,0)。
4. 避坑指南:部署与二次开发中踩过的5个真实血泪坑
工程文件最大的价值不是“能跑”,而是“知道哪里会翻车”。以下是我在某高校实训基地部署该系统时,连续3天排查出的5个高频问题,每一条都附带现象、根因和可立即执行的修复命令。
4.1 现象:前端登录后空白页,控制台报Failed to fetch
原因:Nginx反向代理配置错误,未将/api/路径转发到后端8080端口,而是直接返回了index.html。
解决:编辑docker/nginx.conf,确认location /api/块存在且proxy_pass指向正确:
location /api/ { proxy_pass http://backend:8080/; # 注意末尾斜杠! proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }提示:
proxy_pass末尾的/决定路径重写行为。若写成http://backend:8080(无斜杠),则请求/api/rooms会被转发为/api/rooms,而后端Spring Boot期望的是/rooms,导致404。
4.2 现象:MySQL容器启动后hotel_db库为空,SHOW TABLES返回空
原因:db/init.sql文件编码为UTF-8 with BOM,MySQL 8.0默认不识别BOM头,导致整个SQL脚本执行失败。
解决:用VS Code打开db/init.sql,右下角点击编码格式 → 选择“Save with Encoding” → 选UTF-8(无BOM)。然后重启MySQL容器:
docker restart hotel-system-mysql docker exec -it hotel-system-mysql mysql -uroot -p123456 -e "USE hotel_db; SHOW TABLES;"4.3 现象:办理入住时提示“房间R101当前状态为[vacant],无法办理入住”
原因:RoomEntity实体类中status字段的数据库映射类型为VARCHAR(20),但枚举值"vacant"在Java中被误写为"vacant "(末尾有空格),导致SELECT查询匹配失败。
解决:检查backend/src/main/java/com/hotel/entity/RoomEntity.java中getStatus()方法,确认返回值无空格;同时在MySQL中执行:
UPDATE rooms SET status = TRIM(status); -- 清理历史脏数据注意:MyBatis-Plus的
@TableField若未加fill = FieldFill.INSERT,插入时不会自动填充枚举值,务必检查RoomEntity构造函数或@TableLogic注解。
4.4 现象:Docker Compose启动后,hotel-system-backend容器反复重启,日志显示Connection refused
原因:后端服务启动速度慢于MySQL,application-dev.yml中未配置数据库连接重试机制,导致首次连接失败后进程退出。
解决:在backend/src/main/resources/application-dev.yml中添加HikariCP连接池配置:
spring: datasource: hikari: connection-timeout: 30000 validation-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000 # 关键:连接失败后重试3次,间隔2秒 initialization-fail-timeout: -1 driver-class-name: com.mysql.cj.jdbc.Driver然后在Dockerfile中增加健康检查:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 14.5 现象:修改frontend/src/views/room/RoomStatus.vue后,npm run build报Cannot find module 'vue'
原因:frontend/package.json中"vue"依赖版本与vue-template-compiler不匹配(如vue: "^3.4.0"但vue-template-compiler: "3.2.47")。
解决:统一升级至相同主版本:
cd frontend npm install vue@3.4.0 vue-template-compiler@3.4.0 --save-dev npm run build血泪经验:Vue 3.x大版本间不兼容,
vue-template-compiler必须与vue主版本号完全一致,小版本号可不同。
5. 生产就绪增强:用3个配置开关把Demo级工程变成可交付系统
拿到工程文件只是起点,要让它真正扛住酒店前台7×12小时高强度操作,必须做三处关键配置调整。这些不是“高级技巧”,而是上线前必须核对的底线清单——某公司曾因忽略第2项,在开业当天遭遇房态数据错乱,被迫手工核对3小时。
5.1 开启MySQL严格模式:堵死隐式类型转换漏洞
酒店系统对数据一致性要求极高,room_price字段若被误存为字符串"280.00元",后续账单计算将彻底失效。MySQL默认宽松模式会静默转换,必须强制开启严格模式:
-- 进入MySQL容器执行 docker exec -it hotel-system-mysql mysql -uroot -p123456 -- 查看当前SQL模式 SELECT @@GLOBAL.sql_mode; -- 设置严格模式(关键:STRICT_TRANS_TABLES + NO_ZERO_DATE + ERROR_FOR_DIVISION_BY_ZERO) SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; -- 永久生效:修改docker/mysql.cnf # 在[mysqld]段下添加 sql_mode = STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION效果验证:执行INSERT INTO rooms (price) VALUES ('280.00元');将直接报错Incorrect decimal value,而非存入0.00。
5.2 配置Redis持久化:防止意外断电导致房态丢失
Redis默认仅内存存储,若服务器宕机,所有房态锁和WebSocket消息将清零。必须启用RDB快照:
# 编辑docker/redis.conf # 取消注释并修改以下三行 save 900 1 # 15分钟内至少1个key变化,触发快照 save 300 10 # 5分钟内至少10个key变化,触发快照 save 60 10000 # 1分钟内至少10000个key变化,触发快照 appendonly yes # 同时开启AOF日志(双重保险)然后在docker-compose.yml中挂载该配置:
redis: image: redis:7-alpine volumes: - ./redis.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf提示:RDB文件默认生成在
/data/dump.rdb,Docker卷需映射宿主机目录以实现持久化,例如- ./redis-data:/data。
5.3 启用Spring Boot Actuator:暴露健康检查与审计端点
运维人员需要实时掌握系统状态,而非靠docker ps猜服务是否存活。在backend/pom.xml中添加Actuator依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>并在application-dev.yml中开放关键端点:
management: endpoints: web: exposure: include: health,info,metrics,loggers,threaddump endpoint: health: show-details: always验证方式:访问http://localhost:8080/actuator/health,正常响应为:
{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"redis":{"status":"UP"}}}若redis状态为DOWN,说明Redis连接失败,可立即定位到网络或密码问题。
最后说一句实在话:我见过太多团队把“酒店管理系统”当成练手项目,结果在支付对接、发票打印、公安上传等环节卡住,最后退回Excel管理。而这份工程文件的价值,恰恰在于它把所有“之后再说”的环节,都提前埋好了钩子——payment/目录下留着微信/支付宝SDK的占位代码,report/目录里有PDF账单生成的iText模板,police/目录中甚至写了对接某省公安平台的加密签名样例。你不需要现在就实现它们,但当你真要上线时,知道该往哪个文件里填哪几行代码,这种确定性,比任何教程都珍贵。
希望帮到你。
本文还有配套的精品资源,点击获取