ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

宿舍维修管理系统毕设实战:SpringBoot+Vue全栈拆解

2026/10/7 3:30:05 拓冰建站 浏览量
宿舍维修管理系统毕设实战:SpringBoot+Vue全栈拆解 做毕设或者接手课程设计的时候宿舍维修管理系统绝对是一个高频选题。老实说在我刚看到这个题目的时候也有点不以为然——不就是学生报修、管理员派单、维修工处理、最后评价收尾吗一套标准CRUD下来感觉没什么技术含量。但真的把数据库设计、角色权限、工单状态流转、前后端联调这一整条链路走下来之后我得承认这个项目比想象中有东西而且非常适合作为Java Web方向的学习载体。这篇文章不打算复述那些烂大街的项目介绍而是从完整项目源码、SQL脚本、接口文档这三件套说起把宿舍维修管理系统从0到1拆开讲清楚为什么选这套技术栈、数据库表该怎样设计才能支撑工单流转、后端接口如何按角色做权限隔离、前端Vue页面怎么和接口对接、部署运行又会遇到哪些坑。如果你正在做相关毕设或者想通过一个完整项目把SpringBootVue串起来这篇文章可以当一份踩坑笔记来用。1. 为什么宿舍维修管理系统值得拿来当项目练手作为毕设选题宿舍维修管理系统覆盖面广但不至于失控。相比电商、外卖、社交这些动辄几十张表、还涉及支付和分布式事务的项目宿舍维修管理系统的业务边界非常清晰用户角色固定为管理员、宿管员、维修工、学生四类核心业务就是一条报修 - 派单 - 维修 - 验收的工单流转链路。这种边界清晰的系统适合在课程设计周期内完整做出来也适合后续逐步加入缓存、消息队列、WebSocket等进阶技术。不过简单和没东西是两码事。维修工单的状态流转本质上是一个状态机待审核 - 待派工 - 维修中 - 待验收 - 已完成中间还有驳回、取消、超时等分支。状态机的实现直接决定整个系统的骨架质量。再加上不同角色的权限隔离学生只能查看自己的工单、维修工只能看到分给自己的任务、管理员可以审核和分配这些都是面试和答辩中常被追问的权限模型问题。另外它的业务场景很具体代码不必靠虚构业务去解释。只要你在大学住过宿舍就很容易代入学生报修说寝室灯管坏了晚上怕黑宿管审核通过维修工接单、上门、修完拍照上传学生确认完成并打分。需求的真实性能让开发者在写代码时更清楚地知道这一步为什么存在、那一步为什么不能漏。所以这篇博文面向的读者很明确正在做Java Web方向毕设或课程设计的学生刚把Java SE学完、想通过一个完整项目串联SpringBoot和Vue的初学者需要快速搭建内部报修小工具、想参考现成轮子的后端开发。如果你属于以上任何一类接下来这套拆解会很合胃口。我会把项目的实现过程、设计思路以及实际踩过的坑都放进去争取让你拿到源码后不仅能跑起来还能看明白每一行代码背后的取舍。2. 技术选型逻辑SpringBootVue这套组合到底在练什么技术栈为什么是SpringBootVue而不是传统的JSPServlet这不是跟风而是因为这组技术栈能真实反映当前中小型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-setutf8mb4 dorm_repair dorm_repair.sqlMySQL 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/loginPOST /api/auth/logoutGET /api/auth/info用户管理GET /api/user/pagePOST /api/userPUT /api/userDELETE /api/user/{id}楼栋与房间GET /api/building/listGET /api/room/listByBuilding工单模块POST /api/repair/orderGET /api/repair/order/pageGET /api/repair/order/{id}PUT /api/repair/order/assignPUT /api/repair/order/startPUT /api/repair/order/completePUT /api/repair/order/cancelPUT /api/repair/order/reject评价模块POST /api/repair/evaluation公告与统计GET /api/notice/listPOST /api/noticeGET /api/stats/overviewGET /api/stats/trend接口清单定下来后Controller层就很机械了接收参数、调用Service、返回统一JSON。Service层是重点尤其是工单状态流转的方法。项目分层建议是标准的Controller-Service-Mapper三层加一个Common包放统一返回体Result、异常处理、JWT工具、常量类。这个分层不复杂但能让代码结构非常清晰答辩时也好讲。4.2 为什么用JWT做登录认证以及怎么落地前后端分离项目里SessionCookie的方式不是不行但跨域和移动端适配都麻烦。JWT的方案更常见用户登录成功后后端用密钥签发一个token前端把token存在localStorage或内存中每次请求在Header里带Authorization: Bearer token后端通过拦截器校验token并取出用户信息。我在项目里用到的流程是登录接口校验用户名和密码密码用BCrypt校验校验通过后生成JWTpayload里放入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 ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT 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 PlusAPI上有一些差异。目录结构大致是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.6MySQL 5.7或8.0Node.js 14Vue2项目用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_ciMySQL 8.0的排序规则5.7不认识批量替换成utf8mb4_general_ciTable already exists重复执行建表语句删库重建或先执行DROP TABLE中文乱码连接字符集不对命令行加--default-character-setutf8mb4Foreign key constraint fails建表顺序不对按依赖顺序先建基础表6.3 修改后端配置打开后端项目的application.yml重点检查以下内容spring: datasource: url: jdbc:mysql://localhost:3306/dorm_repair?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/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/loginMethodPOST权限公开请求体{ 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应用从需求、设计、实现、文档到部署的完整闭环。如果能在做项目时多想一层为什么这个状态要这样流转为什么这个接口要限制角色那这个项目带给你的收获会比题目的那几个学分实在得多。希望这篇拆解能帮准备动手的你少踩几个坑把精力花在真正值得研究的地方。