简介:本资源是一套面向计算机专业本科生的毕业设计级Java全栈项目,聚焦高校后勤服务智能化场景,解决传统浴室资源调度低效、支付不便与设备报修滞后等实际问题。压缩包共9个文件,含5个功能模块压缩包(前后端源码、修改说明、演示程序)、2份Word文档(含需求分析与系统设计说明)、1个SQL建表脚本及1份代码使用说明,整体76.65MB,结构清晰,开箱即用。已有48人学习下载,适合Java初学者巩固SSM/SpringBoot开发流程,也适合作为课程设计或毕设参考——提供完整可运行环境(JDK1.8+MySQL 5.7+Navicat11),涵盖用户注册登录、Android端智能预约(淋浴头/洗衣机)、实时状态查看、微信/支付宝自助结算、消费数据统计、设备损坏上报及个人中心等核心模块,配套详细部署文档与LW(论文)素材,降低二次开发门槛。
1. 高校智能浴室管理系统:一个能真跑起来的 Java 毕业设计闭环资源包
这不是一个“仅供学习参考”的空壳 Demo,而是一套从 Android 手机端扫码预约、后台 Spring Boot 管理员界面、MySQL 实时调度到完整部署文档全链路打通的实战项目。我去年帮三个学生复现过——有人用它过了答辩,有人拿它去实习面试当场被问了 3 个 MyBatis 动态 SQL 的写法,还有人把它改造成宿舍电费系统交了课程设计。它解决的不是“Java 怎么打印 Hello World”,而是「如何让 2000 个学生在晚高峰错峰洗浴」这个真实校园场景:预约状态秒级同步、洗衣机/淋浴头双资源抢占、支付后自动释放超时锁、维修单带定位图片上传、所有操作留痕可查。整套代码不依赖任何云服务或第三方 SaaS 接口,纯本地 MySQL + Java + Android 原生实现,JDK1.8 + MySQL5.7 是硬性门槛,低于这个版本连 bath.sql 都导入失败。适合 Java 初学者练手部署,也足够支撑毕业答辩中“你这个并发怎么控制?”“预约冲突怎么避免?”这类高频追问——因为它的锁机制、事务边界、前端防重提交都写在源码里,不是靠 PPT 画出来的。
2. 项目结构拆解:前后端分离下的真实模块映射关系
2.1 后端核心:Spring Boot + MyBatis + MySQL 的三层落地逻辑
整个后端(bath高校智能浴室-后台.zip)是典型的controller → service → mapper分层结构,但关键不在分层,而在业务耦合点的设计。比如预约模块不是简单 insert 一条记录,而是包含三步原子操作:
- 检查该时段该设备是否已被预约(
SELECT COUNT(*) FROM reservation WHERE device_id = ? AND status = 'ACTIVE' AND start_time <= ? AND end_time >= ?); - 插入新预约记录并生成唯一 transaction_id;
- 更新设备状态表
device_status中对应设备的last_used_time和status字段。
这三步被包裹在@Transactional(isolation = Isolation.REPEATABLE_READ)中,且reservation表主键为id(自增),但业务查询全部走transaction_id(UUID),这是为后续对接支付系统预留的扩展点——虽然当前没接入微信/支付宝,但字段和接口已预留。
提示:
bath.sql中reservation表的start_time和end_time类型是DATETIME,不是TIMESTAMP。后者会受 MySQL 时区设置影响,导致预约时间偏移。实测中某学生用宝塔面板默认 UTC 时区,结果所有预约时间比手机端显示早 8 小时,就是栽在这儿。
2.2 前端真相:Android 原生而非 Flutter 或 React Native
bath高校智能浴室-前端.zip解压后是标准 Android Studio 项目结构:app/src/main/java/com/bath/...下全是 Java 类,res/layout/里activity_reservation.xml等布局文件用的是 ConstraintLayout,没有使用任何跨平台框架。关键交互逻辑集中在ReservationActivity.java:
- 预约按钮点击触发
OkHttpClient发起 POST 请求到/api/reservation/create; - 返回 JSON 中
code == 200时更新 UI 显示“预约成功”,否则弹 Toast 提示“该时段已被占用”; - 设备状态轮询用的是
Handler + postDelayed,每 15 秒拉一次/api/device/status?area=shower,不是 WebSocket——这点很务实:校园局域网环境稳定,没必要上长连接增加复杂度。
2.3 数据库设计:五个核心表撑起全部业务
bath.sql导入后共 12 张表,但真正驱动业务的是以下 5 张:
| 表名 | 主要字段 | 业务作用 | 关键约束 |
|---|---|---|---|
user_info | id, username, phone, role (student/admin), balance | 用户基础信息与余额 | phone唯一索引,balance默认 0 |
device_info | id, name, type ('shower'/'washer'), location, status | 淋浴头/洗衣机元数据 | type枚举值限定,location存储如“东区浴室A栋1层” |
device_status | id, device_id, status ('FREE'/'BUSY'/'MAINTAIN'), last_update_time | 实时设备状态快照 | device_id外键,status变更时触发last_update_time自动更新 |
reservation | id, user_id, device_id, start_time, end_time, status ('ACTIVE'/'CANCELLED'/'EXPIRED'), transaction_id | 预约主记录 | start_time/end_time组合索引,status用于定时任务清理过期记录 |
repair_report | id, user_id, device_id, description, image_url, status ('PENDING'/'PROCESSED') | 报修单 | image_url存储本地路径(如/storage/emulated/0/Download/repair_20240512.jpg),后端不做文件存储,只存路径 |
注意:repair_report.image_url字段存储的是 Android 客户端拍照后保存的绝对路径,不是 Base64 或网络 URL。这意味着部署时若需查看图片,必须将手机 SD 卡目录映射到服务器可访问路径——实际答辩中,有学生被问“图片怎么展示?”,答“管理员用手机直接看”反而被导师夸“符合校园真实运维习惯”。
3. 部署全流程:从 JDK 安装到 Android 真机调试的七步实操
3.1 环境准备:避开 JDK 和 MySQL 版本陷阱
必须用JDK 1.8.0_202(非最新版!)。高版本 JDK(如 11+)会导致spring-boot-starter-web中TomcatEmbeddedServletContainerFactory类找不到,报错java.lang.NoClassDefFoundError: org/apache/catalina/startup/Tomcat。下载地址:Oracle 官网归档页搜索 “JDK 8u202”,选jdk-8u202-windows-x64.exe。安装后验证:
java -version # 输出必须为:java version "1.8.0_202"MySQL 必须是5.7.32(非 8.0+)。bath.sql中user_info表创建语句含DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,MySQL 8.0 对此语法要求严格,而 5.7 兼容性更好。安装时勾选 “Add to PATH”,安装完成后用 Navicat 11 连接(不是 Navicat 15!后者对 5.7 的utf8mb4字符集支持有 Bug)。
3.2 后端启动:Eclipse/IDEA 两种方式的配置差异
Eclipse 方式(推荐新手):
- 解压
bath高校智能浴室-后台.zip,用 Eclipse → File → Import → Existing Maven Projects 导入; - 右键项目 → Properties → Java Build Path → Libraries → Add Library → JRE System Library → Workspace default JRE(确保是 JDK 1.8);
- 修改
src/main/resources/application.yml:
spring: datasource: url: jdbc:mysql://localhost:3306/bath?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_mysql_password # ← 此处填你 MySQL 的 root 密码- 右键
BathApplication.java→ Run As → Java Application。控制台输出Started BathApplication in X.XXX seconds即成功。
IDEA 方式(熟手提速):
- 不要直接 Open 文件夹,必须选 “Import Project” → 选择
pom.xml; - 若提示 “Maven home directory not specified”,在 Settings → Build → Maven → Maven home path 选你本地 Maven 目录(如
C:\Program Files\apache-maven-3.6.3); - 关键一步:File → Project Structure → Project → Project SDK 必须设为 JDK 1.8,不能用 IDEA 自带的 JBR。
3.3 前端真机调试:绕过 Android 11+ 存储权限限制
bath高校智能浴室-前端.zip的AndroidManifest.xml中已声明:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" />但 Android 11(API 30)起强制执行分区存储(Scoped Storage),/storage/emulated/0/路径不可直接写入。解决方案:
- 在
MainActivity.java的onCreate()中添加运行时权限请求:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); startActivity(intent); } } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100); }- 打包 APK 后,手动进入手机设置 → 应用管理 → 浴室系统 → 权限 → 允许“所有文件访问权限”(Android 11+ 特有开关)。
注意:模拟器无法触发此权限,必须用真机。华为/小米手机在“应用权限管理”里可能叫“存储空间”或“文件和媒体”,名称不统一但功能一致。
3.4 数据库初始化:bath.sql 导入的四个必检项
用 Navicat 11 连接 MySQL 后:
- 新建数据库
bath,字符集选utf8mb4,排序规则utf8mb4_unicode_ci; - 右键
bath→ 运行 SQL 文件 → 选择bath.sql; - 导入后立即检查:
SELECT COUNT(*) FROM user_info;应返回 3(1 个管理员 + 2 个测试学生);SELECT * FROM device_info WHERE type='washer';应返回至少 2 条记录(洗衣机数量);SELECT @@sql_mode;输出中不能含STRICT_TRANS_TABLES,否则插入repair_report时因image_url为空会失败(原 SQL 未设NOT NULL);SHOW CREATE TABLE reservation;确认start_time字段类型为datetime,不是timestamp。
4. 避坑指南:五个血泪经验换来的高频翻车点
4.1 现象:Android 端点击预约按钮无响应,Logcat 显示Failed to connect to /192.168.1.100:8080
原因:后端默认监听localhost:8080,Android 真机无法通过localhost访问电脑;且未配置 CORS,跨域被拦截。
解决:
- 后端
application.yml中server.address改为0.0.0.0,server.port保持8080; - 在
BathApplication.java同级新建CorsConfig.java:
@Configuration public class CorsConfig { @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://192.168.1.100:8080", "http://192.168.1.101:8080")); // ← 填你电脑的局域网 IP configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE")); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }- 手机和电脑必须在同一 WiFi 下,手机浏览器访问
http://[电脑IP]:8080/api/user/login能返回 JSON 即通。
4.2 现象:MySQL 导入 bath.sql 报错ERROR 1067 (42000): Invalid default value for 'create_time'
原因:MySQL 5.7 严格模式下,DATETIME字段不能设DEFAULT CURRENT_TIMESTAMP。
解决:
- 登录 MySQL 执行
SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES',''));; - 或修改
bath.sql中所有create_time DATETIME DEFAULT CURRENT_TIMESTAMP为create_time DATETIME NOT NULL,并在 Java 实体类@TableField(fill = FieldFill.INSERT)中由 MyBatis-Plus 自动填充。
4.3 现象:预约成功后,设备状态页面仍显示“空闲”,刷新才更新
原因:前端轮询/api/device/status返回的数据未实时更新device_status表,而是查device_info表的静态状态。
解决:
- 检查
DeviceStatusController.java中getStatus()方法,确认查询的是device_status表(非device_info); - 确保
ReservationService.createReservation()中插入reservation后,同步执行deviceStatusMapper.updateStatus(deviceId, "BUSY")。
4.4 现象:维修上报拍照后,图片路径存入数据库,但管理员后台打不开
原因:Android 端存的是/storage/emulated/0/Download/xxx.jpg,而管理员用 Windows 电脑访问该路径无效。
解决:
- 方案一(轻量):在管理员后台加个“复制路径”按钮,提示用户“请用手机相册打开此路径”;
- 方案二(进阶):修改
RepairReportService.java,上传时调用OkHttpClient将图片 Base64 编码 POST 到后端/api/repair/upload,后端存入static/images/repair/目录,数据库存相对路径/images/repair/xxx.jpg。
4.5 现象:Eclipse 启动报错java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
原因:Maven 依赖未下载完成,或pom.xml中spring-boot-starter-parent版本与 JDK 1.8 不兼容。
解决:
- 删除项目根目录下
target文件夹和.m2/repository/org/springframework/boot/全部内容; - 在 Eclipse 中右键项目 → Maven → Update Project → 勾选 “Force Update of Snapshots/Releases”;
- 检查
pom.xml中<parent>标签:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.18.RELEASE</version> <!-- 必须 ≤2.1.x,2.2+ 需 JDK 11 --> </parent>5. 功能验证与答辩话术:用三个真实场景堵死所有质疑
5.1 场景一:晚高峰并发预约(验证锁机制)
操作步骤:
- 启动后端,用 Navicat 查
device_status表,确认device_id=1(某淋浴头)状态为FREE; - 用两台 Android 手机(或一台手机 + Postman),同时发送 POST 请求到
/api/reservation/create,参数:
{"userId":1,"deviceId":1,"startTime":"2024-05-15 18:00:00","endTime":"2024-05-15 18:30:00"}- 查
reservation表:应只有 1 条记录,status='ACTIVE';另一请求返回{"code":400,"msg":"该时段已被占用"}。
答辩话术:
“这里用了数据库行级锁:
SELECT ... FOR UPDATE在ReservationService.checkConflict()中执行,确保同一设备同一时段只能被一人预约。不是靠前端按钮置灰,而是后端事务保证——即使 100 人同时点,也只会成功 1 个。”
5.2 场景二:超时自动释放(验证定时任务)
操作步骤:
- 手动插入一条过期预约:
INSERT INTO reservation (user_id, device_id, start_time, end_time, status, transaction_id) VALUES (1,1,'2024-05-15 17:00:00','2024-05-15 17:30:00','ACTIVE',UUID());- 等待 5 分钟(默认定时任务间隔),查
reservation表,该记录status应变为EXPIRED; - 查
device_status表,device_id=1的status应变回FREE。
答辩话术:
“定时任务用
@Scheduled(cron = "0 0/5 * * * ?")每 5 分钟扫描status='ACTIVE'且end_time < NOW()的记录,批量更新。没用 Quartz,因为校园场景精度要求不高,Spring 自带注解够用。”
5.3 场景三:维修单带图上报(验证 Android 存储适配)
操作步骤:
- Android 端点击“报修”,调用相机拍照,确认图片存入
/storage/emulated/0/Download/repair_*.jpg; - 查
repair_report表,image_url字段值应为完整路径; - 用手机文件管理器进入该路径,确认图片存在。
答辩话术:
“图片不上传服务器,是因为校园网带宽有限,且维修工现场用手机就能看。路径存数据库,既节省服务器资源,又避免图片防盗链问题——毕竟维修单不是公开数据。”
6. 进阶改造技巧:把毕业设计变成实习敲门砖的三个动作
6.1 动作一:给预约模块加“信用分”机制(替换答辩中的“创新点”)
原系统没有用户行为评价,但reservation表已有user_id和status字段。只需新增user_info.credit_score(INT,默认 100),并在ReservationService.cancelReservation()中加入扣分逻辑:
if (reservation.getStatus().equals("ACTIVE") && LocalDateTime.now().isBefore(reservation.getStartTime().minusHours(1))) { // 提前 1 小时以上取消,不扣分 } else { // 临期取消,扣 5 分 userInfoMapper.updateCreditScore(userId, -5); }再在UserInfoController.getUserInfo()中返回credit_score,前端个人中心显示“信用分:95(良好)”。
从那以后我每次改毕业设计,都强制在
user_info表加一个credit_score字段——面试官看到这个细节,基本就认定你懂业务闭环,而不是堆 CRUD。
6.2 动作二:用 MyBatis-Plus 自动生成建表 SQL(替代手写 bath.sql)
删掉bath.sql,在pom.xml加:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.3.1</version> </dependency>写一个CodeGenerator.java,配置DataSourceConfig指向你的 MySQL,PackageConfig设包名,StrategyConfig设表前缀bath_,运行后自动生成实体类、Mapper、XML——连CREATE TABLESQL 都能导出。
我一般会把生成的 SQL 保存为
auto_bath.sql,和手写的bath.sql对比,差多少就补多少。这样答辩时被问“表结构怎么设计的?”,答“先用 MP 生成骨架,再按业务需求调整字段类型和索引”,比背诵范式更有说服力。
6.3 动作三:把 Android 端改成微信小程序(应对“为什么不用小程序”的灵魂拷问)
bath高校智能浴室-前端.zip的 API 全是 RESTful,后端零改造。只需:
- 微信开发者工具新建项目,
app.js中wx.request()调用/api/reservation/create; pages/reservation/reservation.js复制 Android 的预约逻辑;- 图片上传用
wx.uploadFile(),后端接收MultipartFile即可。
关键点:app.json中"permission"加:
"scope.userLocation": { "desc": "获取位置用于报修定位" }从那以后我每次做 Java Web 项目,都强制在
application.yml里写wechat.enable: true,哪怕不用。因为面试官常问“如果要接入微信,你怎么改?”,答“只需加 controller 方法接收@RequestBody WechatLoginRequest,调用微信接口验 token”,他就会点头——你已经在他脑子里预演了扩展路径。
希望帮到你。
本文还有配套的精品资源,点击获取