宿舍报修管理系统完整复现:SpringBoot+Vue+MySQL全栈实战

发布时间:2026/10/24 19:04:51
宿舍报修管理系统完整复现:SpringBoot+Vue+MySQL全栈实战

简介:面向高校毕业设计、课程实践或岗位实训的宿舍报修管理系统项目资料,适合需要完成同类课题的计算机专业学生与开发者参考。系统采用Java语言与SpringBoot框架构建后端,搭配Vue前端与MySQL数据库,涵盖用户管理、维修员管理、宿舍信息管理、配件设备管理、报修接单、配件申请和维修进度跟踪等完整业务模块,能够支撑校园宿舍后勤报修的日常流程。资源为1个docx文档,大小4.78MB,内容包括背景分析、国内外研究现状、相关理论与技术、系统分析、功能设计与测试验证等章节,并给出了基于IDEA、Navicat、Tomcat等环境的开发与运行思路。已有433人学习下载,通过查阅这篇论文,可以较快梳理出宿舍报修系统的数据库表结构、核心接口划分与前后端交互逻辑,对毕业设计答辩、系统二次开发或学习SpringBoot+Vue项目都有直接的参考价值。

1. 宿舍报修管理系统:从论文到可运行源码的一次完整复现

做后勤类管理系统的人应该都有感触,报修流程看起来简单,但一旦涉及多角色协同,业务状态流转就会迅速膨胀。这份基于 Java 的宿舍报修管理系统资源,是一个把「报修」这件事做得很完整的毕业设计项目,技术栈是 SpringBoot + Vue + MySQL,角色分管理员、用户、维修员三种,覆盖了从宿舍分配、配件申请到维修进度追踪的完整闭环。论文部分把系统分析、数据库设计、功能实现和测试用例都写清楚了,源码部分则能直接跑起来做二次开发。适合正在做类似课题的学生,或者想把校园报修场景数字化、需要一套基础代码的开发者。我拆完这套资源最大的感受是:它的价值不在某个高深算法,而在于把报修业务里那些容易被忽略的状态节点设计得很完整,这对课程设计和实际小规模部署都够用。

2. 技术栈选型拆解:为什么是 SpringBoot + Vue + MySQL

2.1 后端框架:SpringBoot 在报修系统里的真实定位

这套资源后端用 Java + SpringBoot 搭建,SpringBoot 在这里解决的核心问题不是高性能,而是「开发效率」。报修管理这类业务系统,本质上是围绕数据库表的增删改查,加上一点业务状态流转,SpringBoot 的自动配置和约定优于配置特性,能极大减少 XML 配置和模板代码,让开发者把精力集中在业务逻辑上。

资源里的实现方式走的是典型的前后端分离思路。后端提供 RESTful API,前端用 Vue 通过 HTTP 请求调用接口,数据以 JSON 格式交互。这种模式的优点是前端页面调整不会影响后端逻辑,对后续要扩展移动端或者小程序端也比较方便。

在一个典型的报修信息查询接口里,你可以看到这样的 Controller 层代码:

@RestController @RequestMapping("/baoxiu") public class BaoxiuInfoController { @Autowired private BaoxiuInfoService baoxiuInfoService; @GetMapping("/page") public Result queryPage(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(required = false) String baoxiuren) { QueryWrapper<BaoxiuInfo> wrapper = new QueryWrapper<>(); if (StringUtils.isNotBlank(baoxiuren)) { wrapper.like("baoxiuren", baoxiuren); } wrapper.orderByDesc("addtime"); Page<BaoxiuInfo> page = new Page<>(pageNum, pageSize); baoxiuInfoService.page(page, wrapper); return Result.success(page); } }

这里需要说明几个关键点。QueryWrapper是 MyBatis-Plus 提供的条件构造器,用like方法实现模糊查询,orderByDesc按时间倒序排列——报修信息通常需要展示最新记录。分页用的是 MyBatis-Plus 的Page对象,前端传入 pageNum 和 pageSize 两个参数就能控制每页数据量。Result.success是统一响应封装,把返回数据包一层,前端就能根据 code 判断请求是否成功。

资源里的后端结构不是简单的单文件堆砌,而是分包明确:controller 层处理请求路由,service 层写业务逻辑,mapper 层负责数据库操作,entity 层定义实体类。这种分层做法在应对报修业务里那些多表关联查询时很有用,比如报修信息要关联用户表查姓名、关联宿舍表查位置、关联维修员表查分配情况。

2.2 前端与数据库:Vue 组件化开发和 MySQL 表设计思路

前端的 Vue 部分,资源里用的是 Vue 2 + Element UI 组件库。Element UI 的表格、表单、弹窗组件能快速搭建管理后台界面,对做课设或者内部管理系统来说是最省力的方案。页面结构上,登录页、注册页、用户报修页、管理员后台页、维修员任务页是分开的,通过 Vue Router 做路由跳转。

数据库设计是这套资源里比较扎实的部分。MySQL 数据库配合 Navicat 可视化工具管理,核心表包括管理员表 users、用户表 yonghu、维修员表 weixiuyuan、宿舍信息表 sushexinxi、报修信息表 baoxiuxinxi、配件设备表 peijianshebei 等,一共十几张表,每张表的字段设计都和业务场景对应。

以报修信息表为例,字段设计基本覆盖了报修业务流程的所有节点:

字段名类型说明备注
idbigint主键自增
baoxiubianhaovarchar报修编号唯一
baoxiurenvarchar报修人关联用户账号
baoxiuneirongtext报修内容故障描述
baoxiushijiandatetime报修时间默认当前时间
weixiuyuanvarchar维修员接单后回填
weixiuzhuangtaivarchar维修状态待接单/维修中/已完成
wanchengshijiandatetime完成时间状态变更时更新

这套表设计的巧妙之处在于,它把报修业务的状态流转直接体现在表的字段变更上。报修单创建时只有报修人和报修内容,维修员接单后回填维修员字段,维修过程中更新状态,完成后再写入完成时间。这个思路比额外建一张状态流转表要简单直接,对业务规模不大的场景完全够用。

2.3 权限控制:三种角色在资源里是怎么落地的

角色权限是管理系统的核心,这套资源在登录接口里实现了基于角色的路由控制。登录成功后后端根据不同角色返回不同菜单权限,前端再根据权限渲染对应的导航栏。

@PostMapping("/login") public Result login(@RequestBody Users user) { QueryWrapper<Users> wrapper = new QueryWrapper<>(); wrapper.eq("username", user.getUsername()) .eq("password", user.getPassword()); Users loginUser = usersService.getOne(wrapper); if (loginUser == null) { return Result.error("用户名或密码错误"); } String token = UUID.randomUUID().toString().replace("-", ""); redisTemplate.opsForValue().set(token, loginUser.getUsername(), 2, TimeUnit.HOURS); Map<String, Object> data = new HashMap<>(); data.put("token", token); data.put("role", loginUser.getRole()); data.put("username", loginUser.getUsername()); return Result.success(data); }

这段登录逻辑有几个地方值得注意。密码校验用的是最简单直接的等值查询,这是很多课设项目的常见做法,但生产环境必须换成加密存储。UUID.randomUUID()生成的随机字符串作为 token,存到 Redis 里并设置 2 小时过期时间,这样能控制会话有效期。返回的 role 字段告诉前端当前用户是什么角色,前端拿这个字段控制菜单显示。

资源里的权限控制粒度是「角色-菜单」级别的,也就是不同角色看到不同菜单,但同角色内没有更细分的操作权限区分。对于课程设计和小型后勤系统来说这个粒度够了,但如果要做更严格的权限管理,可以考虑引入 Spring Security 或者 Shiro 做细粒度控制。

3. 数据库物理设计:从 E-R 图到建表 SQL 的落地细节

3.1 核心表关系:用户、宿舍、维修员、报修单怎么关联

这套资源的数据库设计遵循了第三范式的基本要求,各表之间通过外键逻辑建立关联。用户表 yonghu 和宿舍分配表 sushefenpei 通过用户账号字段关联,宿舍信息表 sushexinxi 和宿舍分配表通过宿舍名称关联,报修信息表 baoxiuxinxi 同时关联用户表和维修员表。

有一个设计细节值得展开说说。宿舍信息表和宿舍分配表是分开的,宿舍信息表记录的是静态数据——宿舍名称、类型、楼栋、房间号、可住人数、已住人数、床位数量;宿舍分配表记录的是动态数据——谁住在哪个宿舍的哪个床位、分配日期、备注。这种拆分避免了一个学生换宿舍时要去修改宿舍表的问题,只需要在分配表里更新记录。

建表 SQL 里能看到一些常见的设计约定。所有表都有 id 主键和 addtime 时间戳字段,id 用 bigint 类型自增,addtime 默认值设为CURRENT_TIMESTAMP。这类约定在 MyBatis-Plus 的自动填充功能下很好用,插入数据时不用手动维护创建时间字段。

CREATE TABLE `baoxiuxinxi` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `baoxiubianhao` varchar(20) NOT NULL, `baoxiuren` varchar(20) DEFAULT NULL, `baoxiuneirong` longtext, `weixiuyuan` varchar(20) DEFAULT NULL, `weixiuzhuangtai` varchar(20) DEFAULT '待接单', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里有几个细节要注意。longtext类型用于报修内容字段,因为故障描述可能比较长。weixiuzhuangtai字段设置了默认值 '待接单',这样创建报修单时不用显式传这个字段。字符集用utf8mb4而不是utf8,原因是utf8mb4支持完整的 Unicode 字符,包括 emoji 表情,避免用户在报修内容里输入特殊字符时出现乱码。

3.2 权限表的 token 处理机制

管理员表 users 和 token 表的设计有一个值得学习的地方。token 表和用户表是一对一关系,token 表里有 userid、username、tablename、role、token、expiratedtime 字段。这里的 tablename 字段有点意思,它记录的是当前 token 对应哪个用户表的表名。

为什么需要这个字段?因为系统有三类用户——管理员、普通用户、维修员——他们可能存储在三张不同的表里。token 表里的 tablename 字段就是为了区分这个 token 是哪个登录主体的。这种设计在课设项目里比较常见,虽然用统一的用户表加角色字段也能实现,但对于已有三张表的数据库结构,这种扩展方式改动最小。

过期时间expiratedtime的处理逻辑也需要留意。资源里的实现是在登录时生成过期时间,在拦截器里校验当前时间是否超过过期时间。如果你把这套代码用于生产,建议把这个校验逻辑统一放到拦截器或者过滤器里,避免在每个 Controller 里重复写。

3.3 常用查询场景的索引视角

从检索能力角度看这套表设计,报修信息表的 baoxiubianhao 字段值得建唯一索引,因为业务上需要通过报修编号快速定位单子。用户表的 yonghuzhanghao(用户账号)字段在登录时做等值查询,也应该建索引。宿舍信息表的 sushemingcheng 字段在宿舍分配时做关联查询,加普通索引就够了。

资源里的 SQL 脚本已经包含了一些索引定义,但在 Navicat 里导入后建议再检查一遍,重点看那些经常出现在 WHERE 条件和 JOIN 条件里的字段是否建了索引。实际操作中我遇到过这种情况:数据量小的时候感觉不到区别,一旦报修记录累积到几千条,全表扫描的查询延迟就会明显增加。

4. 核心功能实现:报修闭环的三个关键模块

4.1 报修流程:从用户提单到维修员接单的状态机

报修管理是这套系统的核心业务流,整个流程可以抽象成一条状态链:用户提交报修 → 管理员审核或直接进入待接单池 → 维修员接单 → 维修中 → 完成。资源里的实现方式是:用户在前端填写报修内容提交,报修单进入待接单状态,维修员在待接单列表里查看所有未接单的工单,接单后状态变为维修中,维修完成后更新状态为已完成。

后端的接单接口是流程的关键节点,代码大致是:

@PostMapping("/jiedan") public Result jiedan(@RequestBody BaoxiuInfo baoxiuInfo) { BaoxiuInfo info = baoxiuInfoService.getById(baoxiuInfo.getId()); if (info == null) { return Result.error("报修单不存在"); } if (!"待接单".equals(info.getWeixiuzhuangtai())) { return Result.error("该报修单已被接单"); } info.setWeixiuyuan(baoxiuInfo.getWeixiuyuan()); info.setWeixiuzhuangtai("维修中"); info.setJiedanshijian(new Date()); baoxiuInfoService.updateById(info); return Result.success("接单成功"); }

这段代码体现了一个容易被忽视的业务约束:接单前的状态校验。如果在更新状态前不检查当前报修单是否已经是「待接单」状态,就可能出现两个维修员同时抢同一张单子,后提交的人把前一个人的信息覆盖掉。这种并发问题在这个资源里用的是「先查再改」的方式,虽然不做数据库层面的乐观锁,但对课设场景够用。如果你想做得更稳,可以在表里加一个 version 字段,在更新 SQL 里加WHERE version = #{oldVersion}的乐观锁条件。

4.2 宿舍分配:用户、宿舍、床位三者关联的精确度问题

宿舍分配模块解决的核心问题是「哪个学生住在哪个宿舍的哪个床位」。资源里的实现逻辑是管理员在后台选择宿舍,再选择用户,接着填入床位号,保存后生成一条分配记录,同时更新宿舍表的已住人数。

这里有一个容易踩坑的业务点:宿舍的容量校验。分配宿舍时应该校验该宿舍当前已住人数是否小于可住人数,否则会出现超员分配。我在拆解资源时看了一下,这个校验逻辑在源码里并不完整,需要自己补上。

补上这个校验的代码逻辑如下:

// 查询宿舍当前状态 Sushexinxi sushe = sushexinxiService.getById(sushefenpei.getSusheId()); if (sushe == null) { return Result.error("宿舍不存在"); } int yizhu = Integer.parseInt(sushe.getYizhurenshu()); int kezhu = Integer.parseInt(sushe.getKezhurenshu()); if (yizhu >= kezhu) { return Result.error("该宿舍已住满"); } // 执行分配 sushefenpeiService.save(sushefenpei); // 更新已住人数 sushe.setYizhurenshu(String.valueOf(yizhu + 1)); sushexinxiService.updateById(sushe);

这里有个细节值得注意:yizhurenshu和kezhurenshu在表设计里用的是 varchar 类型而不是 int。这在数据库设计里其实不算最佳实践,因为要做数值比较时必须先转型。这个资源这么设计可能是因为实体类里为了方便前端展示用了字符串类型。如果你在二次开发时要把这块做严谨,建议把这两个字段改成 int 类型,Mapper 层做一层类型转换即可。

4.3 配件申请与库存联动:维修员领料的后台逻辑

配件申请模块的连接点在维修员和配件设备之间。维修员接单后,如果发现维修需要更换配件,可以在线提交配件申请,管理员审核通过后,配件库存数量需要相应减少。

@PostMapping("/shenqing") public Result shenqing(@RequestBody Peijianshenqing peijianShenqing) { Peijianshebei peijian = peijianshebeiService.getById(peijianShenqing.getPeijianId()); if (peijian == null) { return Result.error("配件不存在"); } int kucun = Integer.parseInt(peijian.getKucun()); int shenqingshu = Integer.parseInt(peijianShenqing.getShenqingshu()); if (kucun < shenqingshu) { return Result.error("库存不足"); } // 减少库存 peijian.setKucun(String.valueOf(kucun - shenqingshu)); peijianshebeiService.updateById(peijian); // 记录申请单 peijianShenqing.setZhuangtai("已发放"); peijianshenqingService.save(peijianShenqing); return Result.success("申请成功"); }

这个接口是「先扣库存再保存申请单」的顺序,按正常事务语义来说应该在一个事务里执行,否则如果保存申请单失败,库存已经被扣掉了。资源里没有显式加@Transactional注解,这是一个潜在隐患。你在二次开发时建议加上这个注解,让这两个数据库操作处于同一事务中,保证数据一致性。

配件模块还有一个值得提的设计:配件分类表和配件设备表分离。分类表存配件类型,设备表存具体配件,通过分类 ID 关联。这种设计的好处是新增配件时不用改表结构,选择已有分类就行。

4.4 维修进度查询:用户端的状态可视化

维修进度模块是用户体验的加分项。用户在提交报修单后,可以在「维修进度查询」页面看到自己所有报修单的当前状态。这里的实现思路很简单,就是按照当前登录用户的账号,查询该用户所有报修单,按时间倒序展示状态字段。

进度展示的字段设计覆盖了报修全生命周期:提交时间、当前状态、维修员姓名、维修完成时间。用户在列表里能直观看到自己的单子处于什么环节,是待接单、维修中还是已完成。这块功能实现不复杂,但它是用户视角里感知系统价值最直接的地方。

需要注意的是,维修进度查询在资源里的实现是按报修单状态字段实时读取的,不是写一条进度日志表记录每次状态变更。如果你需要做「完整操作留痕」,比如记录谁在什么时间把状态从 A 改成了 B,那就需要额外建一张状态变更日志表,在每次状态变化的代码逻辑里插入一条日志记录。

5. 避坑指南:五个让系统在演示现场翻车的常见问题

5.1 端口被占用导致 Tomcat 启动失败

现象:在 IDEA 里启动 SpringBoot 项目,控制台直接报Port 8080 was already in use,整个应用起不来。

原因:本机之前有别的程序占用了 8080 端口,或者上一次运行项目的进程没被完全杀掉。这在课设演示前最容易发生,越急越找不到是哪个程序占的。

解决:Windows 系统用netstat -ano | findstr 8080查出占用端口的 PID,然后到任务管理器里结束对应进程;或者直接改application.properties里的server.port配置把端口换掉。我一般建议直接用后者,省事,改完重启就行。

5.2 MySQL 时区问题导致时间字段相差 8 小时

现象:系统里新增报修记录的时间,在页面上显示比当前时间晚了 8 小时。

原因:MySQL 8.0 版本之后的连接驱动对时区敏感,如果 JDBC URL 里没有指定 serverTimezone 参数,默认使用服务器本地时区,而国内环境经常会匹配到 UTC 标准时间。

解决:在数据库连接 URL 里明确加上?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8。这个问题在资源配套的数据库连接配置文件里其实已经处理过,但如果你自己重装了 MySQL 或者改了连接串,很容易重新踩进去。配置改完重启应用,时间就正常了。

5.3 登录页验证码或密码校验不走拦截器

现象:未登录状态直接访问后端接口地址,居然能拿到数据,登录功能形同虚设。

原因:SpringBoot 项目里的拦截器只对部分 URL 生效,如果配置 WebMvcConfigurer 时拦截路径写得不对,比如只拦截了/admin/**漏掉了/api/**,或者登录接口本身就在拦截名单里,就会导致鉴权逻辑不生效。

解决:先看资源里的拦截器配置类,把不需要登录就能访问的接口路径全部加到放行列表里,比如/login、/register,其余接口全部拦截。放行列表怎么写,可以看一下拦截器的实现,一般是在excludePathPatterns()方法后面追加。把拦截器的匹配规则改成/**再排除掉白名单路径,是最稳妥的。

5.4 Navicat 导入 SQL 脚本时外键约束失败

现象:把资源里的数据库脚本用 Navicat 导入,过程中报错提示Cannot add foreign key constraint,导致部分表创建失败。

原因:脚本里建表的顺序可能有问题,先创建了有外键的子表,后创建了被引用的父表;或者是两张表的字段类型不完全匹配,比如一边是 bigint 另一边是 int。

解决:不用 Navicat 直接导整份脚本,改成先执行建表语句创建所有表,再单独执行外键约束的修改语句。或者干脆把脚本里所有外键约束删掉,表结构建好之后手动在 Navicat 里补外键关系。课设评审阶段外键约束是否完整影响不大,关键是表能建成功、数据能存进去。

5.5 前端页面请求跨域导致接口拿不到数据

现象:前端 Vue 项目启动后,页面能打开但所有数据都是空的,浏览器控制台报Access to XMLHttpRequest has been blocked by CORS policy错误。

原因:前端运行在 8080 端口,后端运行在 8081 端口,浏览器出于同源策略限制,禁止跨端口发起 AJAX 请求。

解决:后端加一个跨域配置类,实现 WebMvcConfigurer 接口并重写 addCorsMappings 方法。代码如下:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

注意allowedOriginPatterns和allowCredentials(true)要配合使用,否则有的浏览器版本会报错。

6. 从课程设计到能用:二次开发时我最先动刀的五个位置

拿到这套资源,如果你是拿来交作业,跑通就完事了。但如果你想让这个系统真正能放进一个宿舍楼里用,我建议按下面的优先级做改造。

第一处是密码加密。资源里的密码是明文存储的,登录也是直接WHERE username = ? AND password = ?查询。现实场景里哪怕只是内部系统,明文密码也是不可接受的。用 Spring Security 的 BCryptPasswordEncoder,也就是一个加密工具类,注册时对密码做哈希,登录时用matches方法做比对,改动量在注册接口和登录接口两个地方,半小时能改完。

第二处是报修单状态的并发控制。之前提到接单接口的「先查再改」逻辑存在并发覆盖风险,加乐观锁是最省事的方案。在报修信息表加 version 字段,更新时用UPDATE baoxiuxinxi SET weixiuzhuangtai = '维修中', version = version + 1 WHERE id = ? AND version = ?,受影响行数为 0 就说明有人抢先改过了,提示用户刷新后重试。

第三处是文件上传和图片展示。报修单里如果能附带现场照片,会大幅减少沟通成本。资源里没有实现图片上传,需要自己补一个上传接口,把文件保存到服务器本地目录,访问时通过一个映射路径直接读取。注意上传目录要放在应用外部,不然重新部署的时候文件会丢。

第四处是数据备份。很多课设系统跑着跑着数据库就崩了,崩了就没有后悔药。至少配置一个 MySQL 定时任务,每天凌晨用mysqldump把整个库导出一份 SQL 文件到备份目录。这个操作不写代码,一行 crontab 就能解决。

从那以后我每次接手这类系统,都强制自己先读一遍数据库表设计文档再动手改代码。表结构理顺了,业务逻辑八九不离十就能看懂。这套资源的表设计虽然不是完美的,但它把报修业务的关键实体和状态都覆盖到了,适合做基础再去改。希望这篇拆解能帮你在自己的项目里少走几步弯路。

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