做毕设或者接手课程设计的时候,"宿舍维修管理系统"绝对是一个高频选题。老实说,在我刚看到这个题目的时候也有点不以为然——不就是学生报修、管理员派单、维修工处理、最后评价收尾吗?一套标准CRUD下来,感觉没什么技术含量。但真的把数据库设计、角色权限、工单状态流转、前后端联调这一整条链路走下来之后,我得承认:这个项目比想象中有东西,而且非常适合作为Java Web方向的学习载体。
这篇文章不打算复述那些烂大街的"项目介绍",而是从完整项目源码、SQL脚本、接口文档这三件套说起,把宿舍维修管理系统从0到1拆开讲清楚:为什么选这套技术栈、数据库表该怎样设计才能支撑工单流转、后端接口如何按角色做权限隔离、前端Vue页面怎么和接口对接、部署运行又会遇到哪些坑。如果你正在做相关毕设,或者想通过一个完整项目把SpringBoot+Vue串起来,这篇文章可以当一份踩坑笔记来用。
1. 为什么宿舍维修管理系统值得拿来当项目练手
作为毕设选题,宿舍维修管理系统覆盖面广但不至于失控。相比电商、外卖、社交这些动辄几十张表、还涉及支付和分布式事务的项目,宿舍维修管理系统的业务边界非常清晰:用户角色固定为管理员、宿管员、维修工、学生四类,核心业务就是一条"报修 -> 派单 -> 维修 -> 验收"的工单流转链路。这种边界清晰的系统,适合在课程设计周期内完整做出来,也适合后续逐步加入缓存、消息队列、WebSocket等进阶技术。
不过"简单"和"没东西"是两码事。维修工单的状态流转,本质上是一个状态机:待审核 -> 待派工 -> 维修中 -> 待验收 -> 已完成,中间还有驳回、取消、超时等分支。状态机的实现直接决定整个系统的骨架质量。再加上不同角色的权限隔离:学生只能查看自己的工单、维修工只能看到分给自己的任务、管理员可以审核和分配,这些都是面试和答辩中常被追问的权限模型问题。
另外,它的业务场景很具体,代码不必靠"虚构业务"去解释。只要你在大学住过宿舍,就很容易代入:学生报修说"寝室灯管坏了,晚上怕黑",宿管审核通过,维修工接单、上门、修完拍照上传,学生确认完成并打分。需求的真实性能让开发者在写代码时更清楚地知道"这一步为什么存在、那一步为什么不能漏"。
所以这篇博文面向的读者很明确:
- 正在做Java Web方向毕设或课程设计的学生;
- 刚把Java SE学完、想通过一个完整项目串联SpringBoot和Vue的初学者;
- 需要快速搭建内部报修小工具、想参考现成轮子的后端开发。
如果你属于以上任何一类,接下来这套拆解会很合胃口。我会把项目的实现过程、设计思路以及实际踩过的坑都放进去,争取让你拿到源码后不仅能跑起来,还能看明白每一行代码背后的取舍。
2. 技术选型逻辑:SpringBoot+Vue这套组合到底在练什么
技术栈为什么是SpringBoot+Vue,而不是传统的JSP+Servlet?这不是跟风,而是因为这组技术栈能真实反映当前中小型Web项目的开发方式。选型一旦定下来,后面所有代码结构都围绕它展开,所以先把这个"为什么"讲透。
先看后端:SpringBoot 2.x + MyBatis-Plus + MySQL 8.x + Maven。SpringBoot的自动配置让项目起步很快,一个@SpringBootApplication就能跑起来;MyBatis-Plus把单表CRUD的样板代码压到最低,适合快速开发,同时保留在XML里写复杂SQL的灵活性;MySQL做数据存储,应付这个量级的系统绰绰有余。有些教程会在这个阶段引入Redis做缓存,我的看法是:这个规模的项目Redis不是必需品,一上来就折腾缓存反而容易把学习重心带偏到"缓存穿透""缓存击穿"这类话题上。真想让项目加分,后面再把Redis接进来用于token黑名单或热点数据缓存,属于锦上添花。
再来看前端:Vue2/Vue3 + Element UI(或Element Plus) + Axios + Vue Router。Vue的响应式数据和组件化非常适合管理后台这类页面:表格、表单、弹窗、标签页都是天然的组件拆分场景。Element UI提供现成的table、form、date-picker等组件,能让页面快速成型。Axios负责调后端接口,配合Vue Router的路由守卫做登录拦截和角色控制。
为什么这套组合值得练?因为在实际公司项目里,前端是Vue或React、后端是SpringBoot系列服务,前后端分离已经是默认架构。你在毕设中提前接触这套组合,等于提前熟悉真实开发环境的协作方式:后端只出JSON接口,前端只管渲染和交互,两边通过接口文档对接。这也正是项目源码里单独配一份接口文档的原因——它不是摆设,而是前后端协作的契约。
关于SpringBoot版本,我需要特别提一句。我用的是SpringBoot 2.7.x,原因很现实:它对应的MyBatis-Plus starter、JWT库和大部分第三方依赖都比较稳定,网上的资料也最全。SpringBoot 3.x默认用Jakarta命名空间,一些老依赖直接迁移会踩坑,对毕设来说没必要冒险。如果你非要用3.x,先确认所有依赖都升级到支持Jakarta的版本再说。
数据库方面,MySQL 5.7或8.0都可以。两者的区别在于8.0默认字符集是utf8mb4,对中文和emoji支持更友好;5.7要记得在建库时显式指定utf8mb4,否则插入特殊字符时可能报错。项目里的SQL脚本按8.0来写,同时保证5.7也能跑通,稍后在第6节我会具体说怎么处理两个版本之间的兼容问题。
3. 数据库设计:从需求到SQL脚本的一次成型
很多同学拿到"源码+SQL脚本"之后,第一步导入数据库就翻车。问题大多不在SQL本身,而在表结构设计和脚本组织方式。这一节先说表怎么设计,再讲SQL脚本怎么写才是对使用者友好的。
3.1 核心表拆解:从用户到工单的完整链路
宿舍维修管理系统,我认为最少需要下面这几张表:
sys_user:用户表,统一存放管理员、宿管员、维修工、学生四类账号。字段包含id、username、password、real_name、phone、role、avatar、status、create_time等。把多类用户放一张表,好处是登录认证统一、权限模型简单;坏处是学生信息和维修工信息的相关扩展字段需要另挂子表。对这个量级的项目,一张用户表加role字段完全够用,不必学大厂拆四张表。
dorm_building:宿舍楼栋表,记录楼栋编号、楼栋名称、宿管负责人等信息。这个表看似简单,但它在统计页面会被频繁关联,所以楼栋编号最好用字符串类型并加唯一索引。
dorm_room:宿舍房间表,关联楼栋,记录房间号、床位数量、当前入住学生等。有了这张表,学生在报修时就只需要从下拉框里选房间,不用手输,数据一致性会好很多。
repair_type:维修类型表,比如"水、电、木、网络、其他"。类型独立成表之后,统计维修类型分布非常顺手,也方便管理员在后台动态增删类型。
repair_order:维修工单主表,这是整个系统的核心。字段包括工单编号order_no、报修人user_id、宿舍楼栋building_id、房间room_id、报修类型type_id、问题描述description、上报图片image_url、期望维修时间expect_time、状态status、指派维修工worker_id、审核人/派单人assigner_id、维修结果备注remark、完成时间finish_time等。工单表的设计直接决定系统能支持哪些业务查询,后面我会单独展开。
repair_evaluation:评价表,学生工单完成后打分和留言,反馈给维修工和管理员。字段含订单id、评分、评价内容、评价时间。
notice:公告表,管理员发布停水停电、检修安排等消息。
operation_log:操作日志表,记录关键操作,比如派单、驳回、取消工单。这张表不是必须的,但加上之后答辩时很有话说,也方便排查问题。
工单表是重点。很多初学者把状态直接设计成一个varchar字段,前端下拉框写死几个选项,后端判断时到处拼字符串。这种写法在小项目里能跑,但状态一多就很容易乱。更规范的做法是定义状态常量或枚举,在数据库字段里用tinyint存状态码,同时加注释说明每个数字的含义。
3.2 字段类型与索引设计的小建议
刚接触项目的人容易犯两个极端:要么把所有"可能要用到的信息"都塞进一个大字段,要么拆出几十张只有两三个字段的表。这两个极端都不好。我的建议是:凡是要参与查询、筛选、统计的字段(状态、类型、楼栋、房间、维修工),就单独建字段;凡是纯展示的扩展信息,可以用JSON字段或备注字段兜底。
工单表的状态字段建议用tinyint,比如:0-待审核,1-待派工,2-维修中,3-待验收,4-已完成,5-已取消,6-已驳回。为什么不用varchar?一是存储体积小,二是排序和范围查询快,三是不容易因为中英文标点导致数据不一致。对应的含义说明写进SQL注释,后端再用枚举类一一映射,这样代码里不会到处出现魔法数字。
索引方面,除了主键索引,我建议给这几列加普通索引:user_id(报修人)、status(状态)、worker_id(维修工)、building_id(楼栋)。这些是工单列表页最常见的过滤条件。如果查询习惯是"按楼栋看状态分布",可以再加一个联合索引(building_id, status);如果是"按状态看时间排序",(status, create_time)也很有用。注意别给每个字段都加索引,那会让插入和更新变慢,对毕设来说也没有必要。
3.3 SQL脚本怎么组织才能"拿过来直接跑"
我倾向于把SQL脚本拆成三个部分,可以放在同一个.sql文件里,但顺序必须清晰:
建库脚本:指定字符集。写法是
CREATE DATABASE IF NOT EXISTS dorm_repair DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;,这样即使库已存在也不会重复报错。建表脚本:按依赖顺序来。先建sys_user、dorm_building、dorm_room、repair_type这类基础表,再建repair_order、repair_evaluation等引用了其他表的表。如果有外键约束,顺序乱了就建不了。
初始化数据脚本:插入管理员账号、默认楼栋、维修类型字典、几个演示用的学生和维修工账号。演示数据要克制一点,每个表三五条就够,太多了反而拖慢第一次导入的速度。
导入时的常见坑,我先提前列出来,后面第6节还会详细展开:
- 用Navicat执行大SQL文件时,中途报错可能不会自动停止,要在设置里勾选"遇错继续"还是"遇错停止",看你想要哪种效果;
- 命令行导入时要指定字符集,比如
mysql -u root -p --default-character-set=utf8mb4 dorm_repair < dorm_repair.sql; - MySQL 8.0默认认证插件是caching_sha2_password,有些老版本驱动连不上,要么换新版本驱动,要么在创建用户时用mysql_native_password;
- 排序规则冲突(utf8mb4_0900_ai_ci)在MySQL 5.7上不识别,需要批量替换成utf8mb4_general_ci。
关于初始密码:很多教程喜欢把密码明文写在SQL里,比如admin/123456。项目演示没问题,但如果要真实部署,至少用一个简单的哈希算法(比如BCrypt)存密码。Spring Security里自带BCryptPasswordEncoder,单独引入BCrypt库也行。不要直接在数据库里存明文,这是我的一个底线建议,答辩时老师也大概率会问。
这样设计完表结构,后端接口的很多事其实已经被"数据库结构"提前决定了。比如要统计"本月各楼栋的报修数量",SQL只需要一条带GROUP BY和DATE_FORMAT的查询;要查"某个维修工今天需要处理的工单",一条带索引的等值查询就够了。好的表结构能替后端省掉大量麻烦。
4. 后端接口设计与权限控制,不只是CRUD
数据库一旦稳定下来,后端接口的骨架也能跟着定下来。这一节我会从接口清单、登录认证、状态机实现、统一返回体四个角度讲清楚整个后端是怎么组织的。
4.1 接口清单与分层思路
宿舍维修管理系统的后端接口大致可以分成六组:
- 认证模块:POST /api/auth/login,POST /api/auth/logout,GET /api/auth/info
- 用户管理:GET /api/user/page,POST /api/user,PUT /api/user,DELETE /api/user/{id}
- 楼栋与房间:GET /api/building/list,GET /api/room/listByBuilding
- 工单模块:POST /api/repair/order,GET /api/repair/order/page,GET /api/repair/order/{id},PUT /api/repair/order/assign,PUT /api/repair/order/start,PUT /api/repair/order/complete,PUT /api/repair/order/cancel,PUT /api/repair/order/reject
- 评价模块:POST /api/repair/evaluation
- 公告与统计:GET /api/notice/list,POST /api/notice,GET /api/stats/overview,GET /api/stats/trend
接口清单定下来后,Controller层就很机械了:接收参数、调用Service、返回统一JSON。Service层是重点,尤其是工单状态流转的方法。项目分层建议是标准的Controller-Service-Mapper三层,加一个Common包放统一返回体Result、异常处理、JWT工具、常量类。这个分层不复杂,但能让代码结构非常清晰,答辩时也好讲。
4.2 为什么用JWT做登录认证,以及怎么落地
前后端分离项目里,Session+Cookie的方式不是不行,但跨域和移动端适配都麻烦。JWT的方案更常见:用户登录成功后,后端用密钥签发一个token,前端把token存在localStorage或内存中,每次请求在Header里带Authorization: Bearer <token>,后端通过拦截器校验token并取出用户信息。
我在项目里用到的流程是:
- 登录接口校验用户名和密码,密码用BCrypt校验;
- 校验通过后生成JWT,payload里放入userId、username、role,设置过期时间;
- 后端写一个拦截器,拦截
/api/**路径,校验token有效性; - 校验通过后把当前用户信息放进ThreadLocal或请求属性,后续业务代码直接获取。
这里有个容易被忽略的点:JWT签发了之后,如果用户被禁用或者角色被修改,在token未过期前它依然有效。对毕设来说,这不是大问题;如果想严谨一点,可以在用户信息变更时让token失效,最粗暴的做法是修改密码时强制重新登录,或者引入Redis存token黑名单。这个扩展方向我放在最后第8节再讲。
4.3 工单状态流转的后端实现:每个迁移都单独成方法
这是整个后端最值得细看的地方。我不会把所有状态更新都写成通用的update接口,而是让每个状态的迁移都对应一个独立的业务方法。因为每个迁移伴随的校验和副作用都不一样。
举几个例子:
- 学生提交工单:状态从"无"变为0(待审核)。此时只允许学生角色操作;工单编号可以用时间戳+随机数生成,或者用数据库自增ID填充。
- 宿管/管理员审核:0->1(待派工)或0->6(驳回)。驳回操作必须要求填写原因,并同步写入操作日志。
- 管理员派单:1->2(维修中)。派单完成意味着维修工已经接到任务了,所以状态从"待派工"直接变成"维修中"是合理的。同时需要校验workerId对应的用户确实有维修工角色,并在维修工页面能看到这条工单。
- 维修工处理:维修工上门后先点击"开始维修",如果不再细分状态,可以跳过;修完后提交结果备注和图片,变成3(待验收)。
- 学生确认:3->4(已完成),同时触发评价表可以填写。
- 取消:待审核状态下学生可以取消;派工之后要取消则需要管理员介入。
这个设计的关键在于:把每个状态迁移都写成独立方法,而不是简单改一下status字段。这样当业务规则变化时,比如"超过48小时未处理的工单自动提醒",你只需要在对应节点加一段逻辑即可,不会把原有的流程搅乱。
4.4 统一返回体与异常处理:联调不出乱子的基础
接口返回格式不统一,前端联调时最痛苦。我的习惯是统一JSON结构:
{ "code": 200, "message": "操作成功", "data": {} }code为200表示成功,非200表示业务失败;401表示未登录或token失效;403表示无权限。全局异常处理器捕获业务异常后返回失败JSON,而不是把一个大的堆栈抛给前端。这个统一返回体建议在项目一开始就搭好,后面所有接口都走同一套结构,接口文档也更好写。
统一返回体长这样(Java代码):
@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(Integer code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } }全局异常处理这边,用@RestControllerAdvice捕获业务异常和兜底异常即可。注意不要在异常处理器里把数据库连接池的堆栈直接暴露给前端,对外只返回"服务器内部错误",具体的完整堆栈记录到日志里自己排查。
5. 前端Vue页面搭建与联调实录
后端接口就绪后,前端才能真正跑起来。这一节我会按项目脚手架、核心页面拆解、路由守卫、联调坑位四个部分来讲。Vue部分的经验,哪怕你之前没写过Vue,照着这个思路也能快速搭起来。
5.1 项目脚手架和目录规划
前端我用的是Vue2 + Element UI。Vue2生态对Element UI的兼容最省心,如果你用Vue3,需要选择Element Plus,API上有一些差异。目录结构大致是:
src ├── api # 接口封装,按模块拆文件 ├── assets # 静态资源 ├── components # 公共组件 ├── layout # 后台布局,包含侧边栏和顶栏 ├── router # 路由配置 ├── store # Vuex,保存用户信息和token ├── utils # 请求封装、工具函数 └── views # 页面组件其中utils/request.js是核心。它基于axios封装,统一处理:请求头加token、响应拦截器解析code、非200时弹出错误提示、401时跳回登录页。所有业务页面都调用这个统一的request方法,不要在页面里散着写axios.get,这样可以省掉大量重复代码。
一个简化的request.js示例:
import axios from 'axios' import { Message } from 'element-ui' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 10000 }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { Message.error(res.message || '请求失败') if (res.code === 401) { localStorage.clear() router.push('/login') } return Promise.reject(new Error(res.message)) } return res }, error => { Message.error('网络异常,请稍后重试') return Promise.reject(error) } ) export default service5.2 页面功能拆解:按角色划分的视图
按角色不同,页面会有些差异,但基础页面如下:
- 登录页:账号密码登录,成功后存储token和用户信息,跳转对应首页。
- 工单列表页:最核心的页面,支持按状态、楼栋、类型、关键词过滤,分页展示。学生只能看到自己提交的工单;维修工只能看到分配给自己的工单;管理员可以看到全部工单。
- 工单详情页:展示完整信息,包括问题描述、图片、处理历史、评价信息;同时根据当前状态显示可执行的操作按钮,比如"审核通过""驳回""派单""开始维修""完成维修""确认完成"。
- 工单提交页:学生选择楼栋、房间、报修类型,填写描述,上传图片。
- 用户管理页:管理员维护账号,包含增删改查和角色分配。
- 统计仪表盘:展示总工单数、待处理数、完成率、维修类型分布等。
这里有个很实在的经验:把操作按钮和当前状态绑定,是前端最容易写乱的业务逻辑。不要在每个页面里散开写if else,而是应该把"当前状态 -> 可操作动作"的映射表集中定义在一个公共文件里,比如:
// 工单状态与可执行操作的映射 const STATUS_ACTIONS = { 0: ['audit', 'cancel'], // 待审核:审核、取消 1: ['assign', 'cancel'], // 待派工:派单、取消 2: ['complete'], // 维修中:提交完成 3: ['confirm'], // 待验收:确认完成 4: [], // 已完成:无操作 5: [], // 已取消:无操作 6: [] // 已驳回:无操作 }这个映射必须和后端的状态机保持一致。换句话说,前端控制的是"用户看不到不相关的按钮",后端控制的是"接口不允许未经授权的操作",两层同时把关,系统才安全。
5.3 Vue Router守卫与角色控制
路由守卫是前端权限控制的基本手段。在router.beforeEach里判断:未登录且访问的是需要认证的页面,跳转登录页;已登录但访问的角色不匹配的页面,跳转403页或首页。更细致的做法是在路由的meta里写roles数组,然后用一个公共方法判断当前用户角色是否在meta里。这种做法对毕设足够,而且答辩时能讲清楚。
对应代码逻辑:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}') if (!token && to.path !== '/login') { next('/login') return } if (token && to.path === '/login') { next('/') return } if (to.meta.roles && !to.meta.roles.includes(userInfo.role)) { next('/403') return } next() })5.4 联调时的真实问题清单
前后端联调阶段,我遇到的典型问题有:
跨域:开发环境下,前端跑到8080端口,后端跑到8081端口,axios请求被CORS拦截。解决办法有两种:后端写CorsConfig放行本地开发地址,或者前端在vue.config.js里配置proxy代理。我的建议是优先用proxy代理,因为生产环境也更容易隐藏后端端口。
时间格式不一致:Java后端返回的LocalDateTime默认是数组或ISO字符串,前端要展示为"2024-05-20 14:30",需要在后端配置Jackson的日期格式,统一成
yyyy-MM-dd HH:mm:ss。token过期:axios请求里遇到401要统一跳登录页,而不是每个页面单独处理。在response拦截器里判断业务code即可,就像前面request.js写的那样。
图片上传后访问不到:上传的图片如果保存在本地磁盘,需要在后端配置静态资源映射,把
/upload/**映射到磁盘路径,否则前端拿到的图片地址打不开。
这些坑单看起来都不难,但联调时遇上了就很耗时间,而且每个都是面试官爱问的"你遇到过什么问题"。
6. 数据库脚本导入与后端运行的避坑记录
很多人在"导入SQL、跑起来"这一步卡住。这一节我按实际执行顺序写一遍,尽量让拿到源码的人少走弯路。
6.1 环境准备清单
在动手之前,先把环境确认好:
- JDK 1.8或11,推荐1.8,兼容性最好;
- Maven 3.6+;
- MySQL 5.7或8.0;
- Node.js 14+,Vue2项目用14或16都行;
- 开发工具:IDEA + Navicat(或命令行mysql)+ VSCode/IDEA前端插件。
6.2 导入SQL的两种方式
方式一:命令行导入,我推荐先试这个,因为错误信息最直接。
mysql -u root -p create database dorm_repair default character set utf8mb4; use dorm_repair; source /your/path/dorm_repair.sql;方式二:Navicat导入。新建数据库,字符集选utf8mb4,然后右键运行SQL文件。导入时勾选"遇到错误不停止"可以帮你看到所有报错。
下面是常见的导入报错和对应处理:
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
| Unknown collation: utf8mb4_0900_ai_ci | MySQL 8.0的排序规则,5.7不认识 | 批量替换成utf8mb4_general_ci |
| Table already exists | 重复执行建表语句 | 删库重建,或先执行DROP TABLE |
| 中文乱码 | 连接字符集不对 | 命令行加--default-character-set=utf8mb4 |
| Foreign key constraint fails | 建表顺序不对 | 按依赖顺序先建基础表 |
6.3 修改后端配置
打开后端项目的application.yml,重点检查以下内容:
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 server: port: 8081 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0这里容易踩的坑有两个:一是时区,MySQL 8.0连接串不指定serverTimezone可能会报错;二是驱动版本,SpringBoot 2.7自带的mysql-connector-java版本如果和MySQL不兼容,就手动在pom里指定一个已知稳定的版本。
6.4 后端跑起来后怎么验证
启动成功后,先用Postman调登录接口,拿到token后,再调用"当前用户信息"接口验证token有效。之后再创建一个工单,确认数据库里插入了记录。我习惯用Postman做这个验证,因为能看到完整的请求和响应,比在浏览器里调试方便。后端没问题了,再启动前端。
前端启动命令是npm install(第一次)或npm run serve。这里有个容易让新人崩溃的点:npm install因为网络或版本原因经常失败,建议先设置淘宝镜像源:
npm config set registry https://registry.npmmirror.com装完后如果启动报缺包,大概率是node版本不对。Vue2项目如果遇到error:0308010C:digital envelope routines::unsupported,是因为Node 17以上版本的问题,解决办法是设置环境变量NODE_OPTIONS=--openssl-legacy-provider,或者在package.json里调整启动脚本。
6.5 第一次完整验证路径
我建议第一次完整跑通时,跟着这条链路走:登录管理员账号 -> 查看首页统计 -> 创建一条学生账号 -> 用学生账号登录 -> 提交一条工单 -> 切换管理员审核通过 -> 分配维修工 -> 切换维修工完成维修 -> 切换学生确认并评价。这条链路走完,整个系统的核心功能就全部验证过了,也相当于做了一次最小可用的端到端测试。
7. 接口文档为什么要单独写一份
很多毕设项目只给源码和SQL,不给接口文档。但题目里明确提到了"接口文档",说明这是一份完整的交付物。站在使用者的角度,接口文档是理解前后端交互最直接的入口;站在答辩老师的角度,文档质量也能直接反映项目的工程化水平。
7.1 接口文档该写哪些内容
一份合格的接口文档,每个接口至少要包含:接口地址、请求方式、权限要求、请求参数说明(名称、类型、是否必填、含义)、返回参数说明、错误码说明。如果能再配一个curl示例,那就更好了。下面给一个登录接口的文档示例:
登录接口
- URL:/api/auth/login
- Method:POST
- 权限:公开
请求体:
{ "username": "admin", "password": "123456" }返回体:
{ "code": 200, "message": "登录成功", "data": { "token": "eyJhbGciOi...", "userInfo": { "userId": 1, "username": "admin", "role": "admin" } } }错误码:401 用户名或密码错误;500 服务器内部错误。
写接口文档的时候,我建议使用Markdown,或者用支持文档导出的API工具。不用强求Swagger自动生成,手写一份带业务说明的文档反而更适合答辩展示——自动生成的文档往往没有"业务含义",老师看的时候也只能看到参数列表,看不出设计者的思考。
7.2 从接口文档反推项目质量
接口文档的质量能直接反映一个人的工程习惯。比如状态流转的接口,如果文档里能说明"该接口仅能由admin角色调用,请求参数workerId必须对应维修工角色",说明设计者真的考虑过权限和业务校验。如果文档里每个接口只有URL和"OK"两个字,那基本等于没写。
我倾向于在文档末尾额外写一个"业务流转说明"章节,用文字一次性把状态机的所有路径讲清楚。这部分对答辩非常重要,因为老师不一定有时间细看代码,但通过文档能快速理解整个系统的业务设计。比如:"学生提交工单后,状态为待审核;宿管或管理员可审核或驳回;审核通过后进入待派工;管理员指派维修工后进入维修中;维修工提交完成结果后进入待验收;学生确认后进入已完成,同时可填写评价。"
7.3 答辩时怎么讲接口文档
答辩时不要照着文档念,而是拿一条业务主线串起来:登录 -> 提交工单 -> 审核 -> 派单 -> 维修 -> 验收 -> 评价。每走到一个节点,就指出这是哪个接口、对应前后端哪部分代码、状态发生了什么变化。这样讲5分钟,项目全貌基本就出来了,比罗列20个接口要有力得多。
8. 项目完成后,我建议你再做的几个小扩展
毕业设计做完只是起点。如果时间允许,下面几个扩展点能明显提升项目的"含金量",无论是对面试还是对后续深入学习都有帮助。
8.1 用Redis管理token和热点数据
引入Redis后,可以做三件事:登录成功后把token存一份并设置过期时间,退出登录或改密时主动删除,实现真正的token失效机制;缓存维修类型字典数据,减少重复查询;在统计接口里缓存聚合结果,减轻数据库压力。这个扩展能让你把Redis的基础用法顺理成章地写进简历,而且用法都很轻量,不会把项目复杂度带偏。
8.2 用WebSocket推送工单状态变化
当前端需要知道"维修工是否点击了完成"时,传统做法是定时轮询列表,体验一般。引入WebSocket后,后端在工单状态变化时主动推送消息给相关用户,前端收到消息后自动刷新详情或弹提示。SpringBoot里可以用spring-boot-starter-websocket,前端用原生WebSocket或socket.io-client包封装。这个功能演示起来效果很直观,答辩时现场操作一下,比讲十页PPT都有说服力。
8.3 加报表可视化
统计数据如果只是后端返回一个数字列表,页面会很干巴。接入ECharts,做一个工单趋势折线图、维修类型占比饼图、楼栋维修排行柱状图,整个项目的完成度会立刻上一个大台阶。ECharts对接Vue很顺利,关键是把后端聚合查询的SQL准备清楚,比如按月份分组的工单量、按类型分组的工单量,这类SQL在第3节的表结构基础上就是几条GROUP BY语句的事。
8.4 部署到Linux服务器上
毕设如果只在本机跑,展示的时候还要开IDEA,很麻烦。把它打包部署到云服务器或虚拟机,会让验收体验好很多。步骤大概是:后端用Maven打包成jar,用java -jar后台运行;前端npm run build生成dist目录,用Nginx托管并把/api反向代理到后端的8081端口;数据库直接使用服务器上的MySQL。这个流程完整走通一次,你会对一个Web项目如何上线有非常直观的认识。
最后再分享一点个人体会。我见过不少同学把毕设当成"应付检查"来写,但宿舍维修管理系统这类题目的最大价值在于:它让你用最小的成本走完了一个真实Web应用从需求、设计、实现、文档到部署的完整闭环。如果能在做项目时多想一层"为什么这个状态要这样流转""为什么这个接口要限制角色",那这个项目带给你的收获,会比题目的那几个学分实在得多。希望这篇拆解能帮准备动手的你少踩几个坑,把精力花在真正值得研究的地方。