☰
Java全栈宿舍报修系统:SpringBoot+Vue实现与二次开发避坑指南
2026/11/4 7:26:01 网站建设 项目流程

简介:这份资源面向计算机专业学生与Java Web初学者,提供一套基于Java的宿舍报修管理系统完整毕业设计资料,包含论文与源码,可用于课程设计、毕业设计参考或校园后勤维修管理场景的技术学习。压缩包共1个docx文件,约4.78MB,内容为系统设计与实现的完整论文文档,涵盖摘要、绪论、相关理论与技术、系统分析等章节,后端采用Java语言与SpringBoot框架,前端结合Vue与MySQL数据库,开发工具涉及IDEA、Navicat与Tomcat。系统功能覆盖用户管理、维修员管理、宿舍类型与信息管理、配件分类与设备管理、维修类型管理、宿舍分配、报修信息与接单管理、配件申请及维修进度管理等模块,论文对可行性分析、需求分析与数据库设计均有展开。目前已有433人学习,适合需要理解SpringBoot+Vue前后端分离项目结构、撰写同类论文或搭建报修管理系统的读者参考借鉴。

1. 从一张手写报修单说起:这套 Java 全栈系统到底解决什么问题

宿舍楼管阿姨的桌上常年压着一摞报修登记本,学生写“灯不亮”,维修师傅来了发现是开关坏了,白跑一趟;配件领用没有记录,月底对账全靠回忆。这套基于 Java 的宿舍报修管理系统,就是把这套线下流程搬到线上:学生在线提交报修、管理员派单、维修员接单并申请配件、进度实时可查。后端 SpringBoot + MySQL,前端 Vue,角色分管理员、用户、维修员三类,覆盖宿舍信息、配件设备、报修接单、维修进度共十二个管理模块。适合拿它做课程设计、毕业设计二次开发,也适合想练一套完整 CRUD 全栈项目的新手照着跑通。

2. 技术栈与目录结构:SpringBoot + Vue 的前后端分离骨架

2.1 为什么是这套组合,而不是别的

项目正文里写得很明确:后端 Java 语言 + SpringBoot 框架,前端 Vue,数据库 MySQL,开发工具 IDEA + Navicat,服务器 Tomcat。这套组合在高校课程设计和中小型管理系统里几乎是标配,原因不玄学——SpringBoot 把 Tomcat 内嵌了,一个main方法就能把服务拉起来,不用再折腾 war 包部署;Vue 的组件化让十二个管理模块可以复用同一套表格 + 弹窗表单的写法,改一个模块的代码能直接套到另一个模块上。

我一般会把这类项目的技术选型理解成三层:数据层MySQL 存业务数据,Navicat 只是可视化客户端,真正跑起来不依赖它;服务层SpringBoot 提供 REST 接口,MyBatis 或 MyBatis-Plus 做 ORM 映射;展示层Vue 打包成静态资源,开发时用npm run serve起一个 dev server,通过代理把/api转发到后端 8080 端口。理解这三层,后面看目录就不会迷路。

提示:正文提到“IDEA 服务器的搭建使用 Tomcat 来完成”,实际 SpringBoot 项目内嵌 Tomcat,不需要单独装 Tomcat 服务器,IDEA 里直接运行启动类即可。这是很多同学看论文时容易误解的一点。

2.2 目录结构长什么样

这类前后端分离项目,根目录下通常是两个并列的工程文件夹,一个后端一个前端。下面是我按项目功能模块还原出来的典型结构,字段名和表名严格对应正文里的数据库设计:

dorm-repair/ ├── dorm-repair-server/ # 后端 SpringBoot 工程 │ ├── src/main/java/com/example/ │ │ ├── controller/ # 接口层:每个模块一个 Controller │ │ │ ├── YonghuController.java # 用户管理 │ │ │ ├── WeixiuyuanController.java # 维修员管理 │ │ │ ├── SushexinxiController.java # 宿舍信息 │ │ │ ├── SushefenpeiController.java # 宿舍分配 │ │ │ ├── PeijianController.java # 配件设备 │ │ │ ├── BaoxiuController.java # 报修信息 │ │ │ ├── JiedanController.java # 报修接单 │ │ │ └── JinduController.java # 维修进度 │ │ ├── entity/ # 实体类,与数据库表一一对应 │ │ ├── mapper/ # MyBatis Mapper 接口 │ │ ├── service/ # 业务逻辑 │ │ └── config/ # 跨域、拦截器、Token 配置 │ ├── src/main/resources/ │ │ ├── application.yml # 数据库连接、端口配置 │ │ └── mapper/ # XML 映射文件(若用 XML 方式) │ └── pom.xml ├── dorm-repair-web/ # 前端 Vue 工程 │ ├── src/ │ │ ├── views/ # 页面:每个管理模块一个 .vue │ │ ├── api/ # 接口封装 │ │ ├── router/ # 路由与角色菜单 │ │ └── utils/ # 请求拦截、Token 存储 │ ├── package.json │ └── vue.config.js # 开发代理配置 └── sql/ └── dorm_repair.sql # 建库建表 + 初始数据

这个结构的关键点在于:Controller 按业务模块切分,而不是按技术层切分。十二个管理模块对应十二组接口,找功能时直接按模块名定位,比在几十个 Service 里翻要快得多。实体类命名和表名保持拼音一致(yonghu、sushexinxi、sushefenpei),虽然拼音命名在正式项目里不推荐,但在这类课程设计里反而降低了对照数据库的认知成本。

2.3 环境准备与启动顺序

跑起来之前,先把三样东西备齐:JDK 8 或以上、Maven、Node.js(建议 14 以上)。数据库用 MySQL 5.7 或 8.0 都行,Navicat 只是方便看表,命令行也能建库。

第一步,建库导数据。把sql/dorm_repair.sql在 MySQL 里执行一遍,会创建数据库和全部业务表,包括users、token、sushexinxi、yonghu、susheleixing、sushefenpei等。

# 登录 MySQL 后执行,库名按 sql 文件里的实际名称来 mysql -u root -p CREATE DATABASE dorm_repair DEFAULT CHARACTER SET utf8mb4; USE dorm_repair; SOURCE /path/to/dorm_repair.sql;

第二步,改后端配置。打开application.yml,把数据库账号密码换成自己本机的,端口默认 8080:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/dorm_repair?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver

serverTimezone这个参数别省,MySQL 8 不配时区经常报连接异常,这是血泪经验。第三步,IDEA 里找到启动类(通常叫Application.java),右键运行,控制台出现 Tomcat started on port 8080 就说明后端起来了。

第四步,前端装依赖并启动:

cd dorm-repair-web npm install npm run serve

启动后浏览器访问控制台提示的地址(一般是 8081 或 8082),前端通过vue.config.js里的 proxy 把/api请求转发到 8080,跨域问题在这一层就解决了,不用后端再配 CORS。

3. 核心模块拆解:从登录鉴权到报修接单的完整链路

3.1 登录与 Token 鉴权:一张 token 表撑起三种角色

正文的数据库设计里有一张token表,字段包括userid、username、tablename、role、token、expiratedtime。这张表是整个权限体系的枢纽——管理员、用户、维修员三种角色登录后,系统往token表写一条记录,前端把 token 存进 localStorage,之后每个请求都带上它,后端拦截器查表校验角色。

登录接口的典型写法是这样:

@PostMapping("/login") public Result login(@RequestBody LoginForm form) { // 1. 按角色去对应表查用户:管理员查 users,用户查 yonghu,维修员查 weixiuyuan Map<String, Object> user = loginService.findByRole(form.getRole(), form.getUsername()); if (user == null || !form.getPassword().equals(user.get("password"))) { return Result.error("账号或密码错误"); } // 2. 生成 token 并写入 token 表,记录角色和过期时间 String token = UUID.randomUUID().toString().replace("-", ""); TokenEntity t = new TokenEntity(); t.setUserid(Long.valueOf(user.get("id").toString())); t.setUsername(form.getUsername()); t.setRole(form.getRole()); t.setTablename(form.getRole().equals("管理员") ? "users" : form.getRole().equals("用户") ? "yonghu" : "weixiuyuan"); t.setToken(token); t.setExpiratedtime(new Date(System.currentTimeMillis() + 24 * 3600 * 1000)); tokenService.save(t); // 3. 返回 token 和角色,前端据此渲染菜单 return Result.ok().put("token", token).put("role", form.getRole()); }

逻辑说明:role参数决定去哪张表查人,这是三种角色共用一套登录入口的关键;tablename字段记录用户来源表,方便后续按角色反查资料;token 有效期设 24 小时,过期后拦截器拒绝请求,前端跳回登录页。参数上要注意userid是 bigint,从 Map 里取出来要转 Long,直接强转 Integer 在数据量大时会翻车。

拦截器侧的核心判断:

// 拦截器里校验 token 是否有效 String token = request.getHeader("token"); TokenEntity t = tokenService.getByToken(token); if (t == null || t.getExpiratedtime().before(new Date())) { return false; // 拦截,前端收到 401 后跳登录 }

3.2 报修信息管理:一张表串起学生、管理员、维修员

报修信息是整个系统的业务主线。用户提交报修,管理员看到后派单,维修员接单,每一步都改报修记录的状态字段。虽然正文没贴报修表的完整字段,但按这个业务场景,合格的做法是至少包含:报修人账号、宿舍名称、房间号、维修类型、故障描述、图片、状态(待接单/已接单/维修中/已完成)、接单维修员、提交时间。

新增报修的接口:

@PostMapping("/baoxiu/save") public Result saveBaoxiu(@RequestBody BaoxiuEntity baoxiu) { // 报修人信息从 token 里取,不信任前端传的用户账号 TokenEntity t = tokenService.getByToken(request.getHeader("token")); baoxiu.setYonghuzhanghao(t.getUsername()); baoxiu.setZhuangtai("待接单"); // 初始状态固定 baoxiu.setAddtime(new Date()); baoxiuService.save(baoxiu); return Result.ok(); }

这里有个容易被忽略的点:报修人账号必须从 token 反查,不能直接用前端表单里的值。否则改一下请求体就能替别人报修,属于典型的越权漏洞。状态字段用中文字符串存(“待接单”“已接单”),虽然不如枚举规范,但在这类项目里查询和展示都直观,前端下拉框直接绑定这几个值即可。

维修员接单时,本质是一次带条件的更新:

@PostMapping("/jiedan/accept") public Result accept(@RequestParam Long baoxiuId) { // 只有状态为“待接单”的记录才能被接,防止重复接单 int rows = baoxiuService.updateStatus(baoxiuId, "待接单", "已接单", currentWeixiuyuan()); if (rows == 0) { return Result.error("该报修已被接单或状态已变更"); } return Result.ok(); }

对应的 SQL 用WHERE id = ? AND zhuangtai = '待接单'做乐观并发控制,两个维修员同时点接单,只有一个能成功。这个细节正文没写,但实际部署时不做就会出现“一单两人接”的脏数据。

3.3 配件申请与维修进度:把线下领料流程搬上线

配件设备管理和配件申请管理是这套系统区别于普通报修系统的亮点。维修员接单后发现要换零件,走配件申请流程:选配件、填数量、关联报修单,管理员审批后扣减库存。配件表按正文设计有分类字段(配件分类管理单独一个模块),所以查询时通常按分类聚合。

配件申请的核心逻辑:

@PostMapping("/peijian/apply") public Result apply(@RequestBody PeijianShenqingEntity apply) { // 1. 校验库存是否充足 PeijianEntity pj = peijianService.getById(apply.getPeijianId()); if (pj.getKucun() < apply.getShuliang()) { return Result.error("库存不足,当前剩余:" + pj.getKucun()); } // 2. 写申请记录,状态为待审批 apply.setZhuangtai("待审批"); apply.setShenqingren(currentWeixiuyuan()); shenqingService.save(apply); return Result.ok(); }

审批通过时才真正扣库存,并且要用UPDATE peijian SET kucun = kucun - ? WHERE id = ? AND kucun >= ?这种带条件的更新,避免并发下扣成负数。维修进度管理则是维修员在接单后分阶段更新进度百分比或状态描述,用户端只读展示,形成“提交—接单—维修中—完成”的闭环。

3.4 宿舍分配与宿舍信息:两个模块的联动关系

宿舍信息表sushexinxi有kezhurenshu(可住人数)、yizhurenshu(已住人数)、youchuangwei(有床位)三个字段,宿舍分配表sushefenpei记录某用户分到某宿舍某床位。分配动作发生时,必须同步更新宿舍信息的已住人数,否则两个模块的数据会对不上。

@Transactional @PostMapping("/sushe/fenpei") public Result fenpei(@RequestBody SushefenpeiEntity fp) { // 1. 校验床位是否已被占用 int used = fenpeiService.countBySusheAndChuangwei(fp.getSushemingcheng(), fp.getChuangweihao()); if (used > 0) { return Result.error("该床位已被分配"); } // 2. 写分配记录 fp.setFenpeiriqi(new Date()); fenpeiService.save(fp); // 3. 同步宿舍信息:已住人数 +1 sushexinxiService.incrementYizhu(fp.getSushemingcheng()); return Result.ok(); }

@Transactional不能省,写分配记录和更新宿舍信息必须在一个事务里,否则中途异常会出现“分配成功但人数没加”的脏数据。这是我在类似项目里踩过的坑,排查了半天才发现是事务注解加在了 private 方法上没生效。

4. 避坑与排查:跑不起来时先看这几处

4.1 数据库连不上,报时区或驱动错误

现象:启动后端直接抛Communications link failure或The server time zone value is unrecognized。原因:MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver,老项目里常写成com.mysql.jdbc.Driver;另外连接串没配serverTimezone。解决:按 2.3 节的 yml 配置改驱动类名和时区参数,MySQL 5.7 用旧驱动也能跑,但建议统一升级。

4.2 前端请求全部 404 或跨域报错

现象:登录按钮点了没反应,F12 看到请求发到了 8081 端口而不是 8080。原因:vue.config.js里的 proxy 没配,或者api封装里的 baseURL 写成了绝对地址。解决:确认 proxy 的target指向http://localhost:8080,且pathRewrite把/api前缀去掉(如果后端接口没带/api)。改完必须重启 dev server,热更新不会重载代理配置。

4.3 登录成功但菜单是空的

现象:能进首页,但左侧菜单只有一两个,或者点管理模块提示无权限。原因:前端路由按role动态生成,登录接口返回的角色字符串和后端 token 表里存的role值不一致(比如一个是“管理员”一个是“admin”)。解决:全局搜一下角色判断的地方,把中文字符串统一,建议在常量文件里定义一次,前后端都引用同一套值。

4.4 图片或头像上传后不显示

现象:用户头像字段touxiang存了路径,页面却是裂图。原因:文件存到了本地磁盘某个目录,但 SpringBoot 没配静态资源映射,或者前端拼接的访问前缀不对。解决:加一个WebMvcConfigurer把上传目录映射成/upload/**访问路径,前端存相对路径,展示时拼上后端地址。

4.5 报修单状态改不动

现象:维修员点接单没反应,或者提示成功但列表状态没变。原因:更新 SQL 的WHERE条件里状态值和数据库里实际存的不一致(比如存的是“待接单 ”,多了个空格),或者前端没刷新列表。解决:先在 Navicat 里直接看那条记录的状态字段原值,再对照代码里的字符串,空格和全半角是最常见的隐形杀手。

5. 二次开发改造:从能跑到好用,我一般动这几刀

拿到这套源码,如果只是答辩演示,跑通就够了;但要真拿去给某个宿舍楼试用,有几处必须改。第一刀砍在权限校验上:原项目的拦截器只校验 token 是否存在,没校验“这个用户能不能访问这个接口”。比如用户直接调管理员的删除接口,token 是有效的就能过。改造方法是在拦截器里按请求路径前缀匹配角色,/admin/**只放行管理员,/weixiuyuan/**只放行维修员,配置写成一张路径-角色映射表,比在每个 Controller 里写注解省事。

第二刀是报修状态机。现在状态是散落在各处的字符串,改起来容易漏。我一般会抽一个枚举类:

public enum BaoxiuStatus { WAIT_ACCEPT("待接单"), ACCEPTED("已接单"), REPAIRING("维修中"), DONE("已完成"), CANCELLED("已取消"); private final String label; BaoxiuStatus(String label) { this.label = label; } public String getLabel() { return label; } }

所有状态流转集中到一个changeStatus(id, from, to)方法里,非法流转直接抛异常。这样以后加“驳回”“转派”状态,只改枚举和流转规则,不用满项目搜字符串。

第三刀是列表查询的分页和条件筛选。课程设计项目常见写法是一次性select *全查出来前端分页,数据量一上来就卡。改成后端分页(MyBatis-Plus 的Page对象),报修列表按状态、宿舍楼、时间范围三个条件组合查询,索引加在zhuangtai和sushelou上。验证方法很简单:往报修表插五千条测试数据,看列表首屏加载时间,改造前通常两三秒,改造后稳定在两百毫秒内。

第四刀是配件库存的并发安全。前面 3.3 节提过条件更新,但还要加一层:审批扣库存和申请写记录必须在同一事务,且库存字段加乐观锁版本号,防止两个管理员同时审批同一批配件把库存扣穿。验证时用 JMeter 起二十个线程同时调审批接口,看最终库存是否等于初始值减总申请量,对不上就说明锁没生效。

最后说个习惯:这套项目我每次改完一个模块,都会先把sql目录下的建表脚本和实体类字段对一遍,再跑一遍登录、报修、接单、完成这条主链路。因为这类项目最容易出的不是逻辑错误,而是字段名拼写不一致导致的空指针——数据库里叫yonghuzhanghao,实体类写成yonghuZhanghao,MyBatis 映射不上就返回 null,页面一片空白还不报错。从那以后我每次动表结构都强制走一遍“改 SQL → 改实体 → 改 Mapper → 跑主链路”的流程,省下的排查时间比多写的这几步多得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询