每年到毕业设计开题那会儿,总有人跑来问我:管理系统是不是太简单了?做完是不是没东西可讲?我一般会反问:你见过哪家医院现在还在用Excel记设备账?真把一套基于SpringBoot口腔医院医疗设备管理系统从需求梳理、数据库设计、后端开发到前端页面完整做下来,你会发现要处理的业务规则、状态流转、权限控制、统计报表,每一块都能在答辩台上讲十分钟。这类题目属于医疗信息化里极其典型的方向,既贴行业场景,又有清晰的技术价值,计算机、软件工程、信息管理专业的同学拿来当毕业设计非常合适。而且这类项目通常附带全套源码、数据库脚本和论文文档,等于把最耗时间的搭建工作替你省掉了,剩下的就是真正理解它、二次改造成你自己的东西。
我尽量讲得细一点,把从选题、设计数据库到部署上线、准备答辩的完整脉络都捋一遍,顺便把我在指导过程中看到学生踩得最多的坑也列出来。不管你是打算直接拿这个题目做毕设,还是只想参考一下设备管理类系统的写法,这篇文章都能当一份实操地图用。
1. 口腔医院设备管理到底难在哪
1.1 先看清楚业务场景的特殊性
口腔医院听起来不大,但设备种类翻出来能列一长串:锥形束CT、全景X光机、牙科综合治疗台、高温高压蒸汽灭菌器、超声波洁牙机、光固化灯、种植机、根管长度测量仪,还有各种型号的涡轮手机和耗材类小设备。这里面既有几十万的影像设备,也有几百块的日常工具,管理逻辑完全不一样。
更麻烦的是使用场景。口腔科设备的操作频率高,治疗椅每天十几个患者轮着用,手机类器具要分诊室、分消毒批次管理,灭菌器更是直接牵扯院感安全。设备一旦出问题,不只是多花维修费的问题——预约的患者已经在椅子上躺好了,机器却动不了,整个门诊安排全被打乱。所以口腔医院的设备管理,核心诉求不是简单的"记一笔账",而是要对设备状态有实时感知,对维修和保养过程有完整追踪。
1.2 传统台账模式为什么撑不住
我接触过不少中小型口腔诊所和设备科的人,他们以前的管理方式高度雷同:一台电脑里躺着一份共享Excel,谁用了谁改,改完经常没人保存;设备保修期什么时候到期,全靠设备科主任脑子里记;维修电话打过来,登记一个本子,后续谁修的、换没换配件、花多少钱,翻半天翻不出来第三页。
把这些痛点总结一下,基本就是四件事:
- 台账数据分散,Excel多人协作版本混乱,设备编号、位置、责任人经常对不上。
- 维修过程没有流程约束,报修靠喊、派单靠问,维修状态和费用不透明。
- 保养和到期提醒完全依赖人工记忆,遗漏是常态,而口腔类设备很多涉及强制检定和消毒效果监测。
- 采购、维修、科室使用三方各记各账,想统计某个设备的总体维护成本时,数据拼不齐全。
1.3 一条"及格线":系统得解决什么
判断这类毕设做得好不好,先别扯技术,先看它把上面四个问题接没接住。一套合格的设备管理系统至少要做到五件事:
- 设备台账电子化,支持分类型、分状态检索,看到单一设备就能翻出全部历史记录。
- 维修工单全流程线上化,从报修、派单、接单、处理到完工确认,每一步都有时间和操作人。
- 保养计划自动提醒,周期保养、到期检修不用人肉盯日历。
- 采购、备件、供应商基础数据统一维护,设备档案不是一片信息孤岛。
- 角色权限分明,设备科、工程师、科室护士看到的功能和操作范围不一样。
这五条刚好也是后面拆解数据库表和功能模块时的主线。想清楚业务上要解决什么问题,再动技术选型,这个顺序不能反。
2. 技术选型:搭一套稳妥还省心的组合
2.1 为什么Spring Boot是这类毕设的最优解
不是因为它最新潮,而是因为它"够稳、够省事、够好解释"。Spring Boot约定优于配置,项目启动不需要一堆XML配置,内嵌Tomcat,打成一个jar包就能跑。对毕设来说,这意味着你从零把一个可演示系统跑起来的时间成本极低。
更重要的是生态和心智。现在随便搜"管理系统 毕设",十个里八个是Spring Boot,这意味着你遇到任何问题都能搜到答案,也意味着答辩老师对这套组合非常熟悉,他问你的每个问题大概率都有公开答案。你不需要在答辩现场证明一个老师不熟悉的技术,反而可以把精力放在业务逻辑的完整度上。
版本上我建议直接选Spring Boot 2.7.x配JDK 8,兼容性最稳,网上资料最全。Spring Boot 3.x也不是不行,但部分依赖和配置变了,新手踩坑成本更高,毕设没必要冒这个险。
2.2 持久层与前端方案的取舍
持久层我推荐MyBatis-Plus而不是JPA,理由是它把常用CRUD都封装好了,单表查询连SQL都不用写,分页有现成的PaginationInnerInterceptor插件,代码比纯MyBatis干净一个量级。JPA虽然也强大,但对象关系映射的规则对新手不友好,一个关联查询嵌套写错,排查半天可能只是懒加载问题。
前端有两派选择。第一种是前后端分离:Vue 2配合Element UI,后端只提供JSON接口。界面现代,演示效果好,但也意味着你要维护两个工程,联调时还得处理跨域。第二种是服务端渲染:Thymeleaf配Bootstrap,所有页面都在Spring Boot里,部署简单,但交互体验会显得像传统管理后台。
我的建议是,如果你有过一点点Vue基础,就选前后端分离,答辩演示时页面好看本身就是加分项。如果你时间特别紧、之前没碰过前端工程化,Thymeleaf方案一周也能画完界面,别硬上Vue。
2.3 工程目录先把骨架搭正
不管选哪种前端,后端工程结构建议按这个骨架来组织,包名用项目名加业务域的组合,比如com.example.dental:
src/main/java/com/example/dental/ ├── controller/ # 接口层,只做参数接收和结果返回 ├── service/ │ └── impl/ # 业务逻辑层,状态流转、校验都放这里 ├── mapper/ # MyBatis-Plus的Mapper接口 ├── entity/ # 数据库实体类 ├── config/ # 跨域、拦截器、分页配置 ├── common/ # 统一返回结果、异常处理、工具类 ├── util/ # JWT、日期处理等工具 └── DentalApplication.java把common统一返回类和全局异常处理器先写好,后面每个接口都少操心一件事。工程骨架正了,后续加功能就是往对应目录里填代码的事。
3. 需求拆解:先从角色和流程下手
3.1 四类角色,权限边界先划好
设备管理系统不是所有人都在同一张页面上操作。系统里至少要有四类角色,它们的功能边界从一开始就要明确,否则后面做登录和权限时会很痛苦:
- 系统管理员:只管用户、角色、菜单授权、基础数据字典,不参与业务。
- 设备科管理员:系统的核心使用者,负责设备台账维护、采购记录、保养计划制定、统计报表查看。
- 维修工程师:接收派工、执行维修、登记维修结果和更换配件,维护巡检记录。
- 科室用户:护士或医生,发现设备异常时发起报修,查看自己科室设备的维修进度。
所有业务操作都要落到某类角色身上。做权限设计时,后端不仅要控制菜单显示,还得在接口层做二次校验,防止有人直接调URL绕过前端。
3.2 功能模块清单:比想象中要多一点
把需求拉成一张模块表,你会发现在"管理系统"四个字下面,其实藏着不少内容:
| 功能模块 | 核心功能点 | 涉及角色 |
|---|---|---|
| 设备台账 | 新增、编辑、停用、报废、条件分页查询、设备详情页 | 设备科管理员 |
| 维修工单 | 报修、派单、接单、处理、完工确认、评价 | 全部角色 |
| 保养计划 | 周期计划制定、到期自动提醒、执行记录登记 | 设备科管理员、工程师 |
| 巡检管理 | 巡检任务生成、问题登记、整改闭环 | 工程师 |
| 备件与供应商 | 备件出入库、供应商档案和联系人维护 | 设备科管理员 |
| 统计报表 | 按类型统计数量、按月统计维修次数、维修费用汇总 | 设备科管理员 |
| 系统管理 | 用户管理、角色权限分配、菜单管理、登录日志 | 系统管理员 |
3.3 把核心流程走一遍再写代码
我培养学生做项目时,会逼他们先画一条完整流程,再动手建表。这个题目里最核心的流程是维修工单的整体流转,你在演示时也必须讲这条线:
科室用户发起报修,填写设备编号、故障现象、紧急程度。设备科管理员收到新工单后分配给维修工程师。工程师接单后系统把工单状态改为处理中,维修完成后填写故障原因、处理方式、耗材使用和费用。科室用户确认完工后,工单状态变成已完成,整条工单历史自动归档到设备详情档案里。
这条流程在设计数据库时对应一条维修工单表和一张工单状态流转日志表,状态之间的跳转有条件约束,比如未派单不能直接进入处理中,未完工不能写到已完成。把这个状态机搞清楚,后端代码写起来就顺了。
4. 数据库设计:这些表是系统的地基
4.1 设计前先定几个通用规范
数据库设计直接决定后面开发的痛苦程度。几个通用规范我强调一下,都是实际项目中验证过的:
- 每张表都带
id主键、create_time、update_time三个通用字段,审计和排查都好用。 - 状态字段一律用
tinyint存数字,比如设备状态0在用、1维修中、2停用、3报废,不在数据库里存中文,展示时前端做字典翻译。 - 金额用
decimal(10,2),千万不用float,浮点算钱会出大问题。 - 时间统一用
datetime,连接串里配好时区,避免Java和MySQL时间相差八小时。 - 设备编号、工单号这类业务编号要加唯一索引,防止重复录入。
4.2 核心表结构清单
整个系统核心表大概九张左右,模块之间的关联非常清晰:
| 表名 | 说明 | 关键关联 |
|---|---|---|
| sys_user | 用户表 | 关联角色表 |
| sys_role | 角色表 | 多对多关联用户 |
| equipment_type | 设备类型表 | 一二级类型树 |
| equipment_info | 设备台账主表 | 关联类型、供应商、科室 |
| supplier_info | 供应商表 | 设备和备件关联 |
| maintenance_order | 维修工单表 | 关联设备和处理人 |
| maintenance_plan | 保养计划表 | 关联设备 |
| maintenance_record | 保养执行记录表 | 关联计划和设备 |
| spare_part | 备件表 | 关联供应商 |
| procurement_record | 采购记录表 | 关联设备和供应商 |
4.3 几张关键表的建表SQL
设备台账主表是整个系统信息最密集的一张表,字段要覆盖设备身份、采购信息、位置状态和保修情况:
CREATE TABLE equipment_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, equipment_code VARCHAR(50) NOT NULL COMMENT '设备编号', equipment_name VARCHAR(100) NOT NULL COMMENT '设备名称', type_id BIGINT COMMENT '设备类型ID', model VARCHAR(100) COMMENT '型号', factory_no VARCHAR(100) COMMENT '出厂编号/序列号', supplier_id BIGINT COMMENT '供应商ID', purchase_date DATE COMMENT '采购日期', price DECIMAL(10,2) COMMENT '采购价格', warranty_end DATE COMMENT '保修截止日期', location VARCHAR(100) COMMENT '存放位置', status TINYINT DEFAULT 0 COMMENT '状态:0在用 1维修中 2停用 3报废', belong_department VARCHAR(100) COMMENT '所属科室', responsible_user VARCHAR(50) COMMENT '设备责任人', remark VARCHAR(500) COMMENT '备注', create_time DATETIME, update_time DATETIME, UNIQUE KEY uk_equipment_code (equipment_code), KEY idx_type_id (type_id), KEY idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备台账表';维修工单表要记录完整流程,核心字段是状态和两级处理人:
CREATE TABLE maintenance_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(50) NOT NULL COMMENT '工单编号', equipment_id BIGINT NOT NULL COMMENT '设备ID', report_user_id BIGINT COMMENT '报修人ID', report_desc VARCHAR(500) COMMENT '故障描述', urgency_level TINYINT DEFAULT 1 COMMENT '紧急程度:1低 2中 3高', status TINYINT DEFAULT 0 COMMENT '状态:0待派单 1已派单 2处理中 3已完成 4已驳回', assign_user_id BIGINT COMMENT '派单人ID', engineer_id BIGINT COMMENT '维修工程师ID', handle_desc VARCHAR(500) COMMENT '处理结果描述', handle_time DATETIME COMMENT '完工时间', cost DECIMAL(10,2) COMMENT '维修费用', evaluate VARCHAR(200) COMMENT '科室评价', create_time DATETIME, update_time DATETIME, UNIQUE KEY uk_order_no (order_no), KEY idx_equipment_id (equipment_id), KEY idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='维修工单表';保养计划表的重点是算好下次执行日期,字段设计要方便定时任务直接扫描:
CREATE TABLE maintenance_plan ( id BIGINT PRIMARY KEY AUTO_INCREMENT, equipment_id BIGINT NOT NULL COMMENT '设备ID', plan_type TINYINT COMMENT '类型:1日常保养 2周期检修 3计量检定', cycle_type TINYINT COMMENT '周期:1每月 2每季度 3每半年 4每年', last_execute_date DATE COMMENT '上次执行日期', next_execute_date DATE COMMENT '下次执行日期', remind_days INT DEFAULT 7 COMMENT '提前提醒天数', status TINYINT DEFAULT 1 COMMENT '1启用 0停用', create_time DATETIME, update_time DATETIME, KEY idx_equipment_id (equipment_id), KEY idx_next_date (next_execute_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='保养计划表';建表时顺手把索引加上,后面分页和统计查询效率会好很多,答辩时如果被问到数据量大怎么办,也可以拿索引设计作为切入点。
5. 后端实现:认证、台账、工单和定时任务
5.1 统一返回结构与全局异常
后端代码第一步不是写业务,而是把通用能力搭好。先写一个结果返回类,保证所有接口的响应格式一致:
@Data public class Result<T> { private Integer code; private String msg; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMsg("操作成功"); r.setData(data); return r; } public static <T> Result<T> error(String msg) { Result<T> r = new Result<>(); r.setCode(500); r.setMsg(msg); return r; } }再配一个全局异常处理器,把业务异常和通用异常都拦截住,避免把堆栈信息直接暴露给前端。
5.2 JWT登录认证的实现思路
前后端分离项目里,JWT是最常用的认证方案。登录接口校验用户名密码通过后,生成token返回给前端,前端存在本地,之后每次请求在Header里带上,后端通过拦截器解析。
密码存储一定不要用明文,建议用BCrypt加密,MyBatis-Plus和Spring Security里都有现成实现。即使你不想引入整个Security,单独引入一个加密工具类也很容易。
拦截器配置时要注意放行登录接口和验证码接口,其他接口统一校验token,同时对非法的token返回401状态,前端基于状态码自动跳回登录页。
5.3 设备台账的分页条件查询
设备台账是系统的核心功能,查询接口要支持按名称模糊搜索、按类型和状态筛选、按采购时间排序。用MyBatis-Plus的LambdaQueryWrapper写起来非常干净:
@GetMapping("/page") public Result<IPage<EquipmentInfo>> page( @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize, @RequestParam(required = false) String name, @RequestParam(required = false) Integer typeId, @RequestParam(required = false) Integer status) { LambdaQueryWrapper<EquipmentInfo> wrapper = Wrappers.lambdaQuery(); wrapper.like(StringUtils.hasText(name), EquipmentInfo::getEquipmentName, name) .eq(typeId != null, EquipmentInfo::getTypeId, typeId) .eq(status != null, EquipmentInfo::getStatus, status) .orderByDesc(EquipmentInfo::getCreateTime); Page<EquipmentInfo> page = equipmentService.page(new Page<>(pageNum, pageSize), wrapper); return Result.success(page); }MyBatis-Plus的分页需要注册一个分页插件,很多学生忘了这一步,导致total永远是0,翻页时数据重复。在配置类里加上PaginationInnerInterceptor,问题立刻消失。
5.4 维修工单状态机的实现
维修工单是整个项目里业务最复杂的部分,它的关键不是CURD,而是状态流转的限制。建议把状态定义成枚举,每次更新都先做状态校验:
public enum OrderStatus { PENDING(0, "待派单"), ASSIGNED(1, "已派单"), PROCESSING(2, "处理中"), COMPLETED(3, "已完成"), REJECTED(4, "已驳回"); private final int code; private final String desc; // 构造和getter省略 }状态校验规则放在Service层实现,比如只有PENDING状态下管理员才能派单,只有ASSIGNED状态下工程师才能接单,只有PROCESSING状态下才能提交完工。状态变更的同时写一条流转日志,设备详情页的时间线就能完整展示工单全生命周期。
5.5 保养计划的自动提醒
保养提醒是这个项目区别于普通增删改查系统的重要亮点。Spring Boot里用@Scheduled就能实现定时扫描,每天早上八点扫描未来七天内需要保养的设备,生成提醒消息:
@Component public class MaintenanceRemindTask { @Scheduled(cron = "0 0 8 * * ?") public void scanRemind() { LocalDate today = LocalDate.now(); LocalDate remindEnd = today.plusDays(7); List<MaintenancePlan> plans = planMapper.selectList( Wrappers.lambdaQuery(MaintenancePlan.class) .between(MaintenancePlan::getNextExecuteDate, today, remindEnd) .eq(MaintenancePlan::getStatus, 1) ); plans.forEach(plan -> { // 生成提醒记录并推送消息,供系统首页轮询显示 }); } }别忘在主启动类上加@EnableScheduling注解,否则定时任务根本不会触发。这个功能答辩时非常能讲故事,一句话就能说清价值:系统不用等人记,自己会在设备该保养的时候喊一声。
5.6 统计报表的查询接口
报表部分可以考虑用轻量而非滥用重量级组件,直接按业务需要写统计查询接口,前端用ECharts渲染柱状图和饼图。常见统计有三个:按设备类型统计数量分布、按月份统计维修工单数、按设备汇总维修费用排行。SQL用GROUP BY加时间函数就能搞定,不需要额外引入报表组件。
6. 前端页面:把功能变成一张张能用的界面
6.1 前后端分离时的基础搭建
如果选了Vue加Element UI方案,工程结构大概是这样:src/api放请求接口定义,src/views放页面组件,src/router配置路由,src/store存登录状态。请求统一封装一个axios实例,在拦截器里自动带上token,响应里如果code是401就跳转登录页。
开发阶段最大的坎是跨域。前后端端口不同,浏览器默认会拦截请求,需要在后端写一个CorsConfig配置类,允许指定来源访问,或者在前端DevServer里配置代理。更推荐后端统一配置,这样打包部署时逻辑也更简单。
6.2 设备台账页面的核心组件
设备台账页面是演示的重头戏,结构上分三块:顶部搜索栏、中间的表格、右侧的编辑弹窗。表格用el-table,状态列用el-tag做颜色区分,搜索表单和分页组件配合接口参数:
<el-table :data="tableData" border stripe> <el-table-column prop="equipmentCode" label="设备编号" width="120" /> <el-table-column prop="equipmentName" label="设备名称" min-width="160" /> <el-table-column prop="model" label="型号" width="120" /> <el-table-column label="设备状态" width="100"> <template slot-scope="scope"> <el-tag :type="statusTypeMap[scope.row.status]"> {{ statusTextMap[scope.row.status] }} </el-tag> </template> </el-table-column> <el-table-column prop="location" label="位置" width="150" /> <el-table-column label="操作" width="180" fixed="right"> <template slot-scope="scope"> <el-button size="mini" @click="handleDetail(scope.row)">详情</el-button> <el-button size="mini" type="primary" @click="handleEdit(scope.row)">编辑</el-button> </template> </el-table-column> </el-table> <el-pagination :current-page="pageNum" :page-size="pageSize" :total="total" @current-change="handlePageChange" />新增和编辑复用同一个弹窗表单,只是提交时区分POST和PUT即可。删除操作要对设备状态做一次校验,有维修工单记录的设备不允许物理删除,只允许停用或报废,这个约束逻辑放在后端Service层。
6.3 维修工单的时间线展示
工程师提交完工后,前端页面用Element UI的el-timeline组件把工单状态变化展示出来,每一步显示操作人和时间。用户看到的不只是一行冰冷的文字,而是一条清晰的流转轨迹。这个页面对演示效果和答辩讲解都很有帮助,也天然对应了数据库里的流转日志表。
7. 部署、测试与细节打磨
7.1 在本地把项目跑起来的完整步骤
拿到这种附源码的毕设项目,第一件事是先把它跑起来,再谈二次开发。步骤按顺序走,基本二十分钟左右能完成。
先装好JDK 8、Maven、MySQL 5.7或8.0。然后创建一个数据库,字符集选utf8mb4,执行项目里的初始化SQL脚本,把表结构和测试数据导进去。接着修改配置文件的数据库账号密码,确认端口没被占用。后端项目在项目根目录执行mvn spring-boot:run,看到Spring的启动日志就说明后端起来了。前端如果是Vue项目,进入目录执行npm install装依赖,再执行npm run dev,浏览器访问对应端口,用初始账号登录,比如admin/123456。
有个细节:npm install如果网络慢,可以把源切到国内镜像仓库,装依赖会快很多。数据库初始化脚本一定要找全,有些项目的表结构和测试数据是分开两个SQL文件放的,少执行一个可能导致登录进去看不到数据。
7.2 测试用例怎么设计才算完整
很多学生的系统能跑,但一演示就露馅,原因是只测了正常路径。围绕系统核心,至少设计三类测试用例。
第一类是权限用例,分别用管理员、设备科、工程师、普通科室账号登录,确认低权限用户看不到高权限菜单和接口,这个直接对应数据库的权限表设计,也是答辩常问点。第二类是流程用例,从创建一台设备、发起报修、派单、接单、完工到确认,整个链路必须走通,每个环节验证状态是否正确变更。第三类是提醒用例,把保养计划的下次执行日期改成今天,再触发定时任务或重启系统,验证是否能在首页看到待办提醒。
7.3 安全与性能上的加分项
答辩时老师很可能会问"你怎么保证系统安全",提前在项目里做好这几个点,回答时就非常有底气。
密码存储用BCrypt,不用MD5,理由是BCrypt自带随机盐,能对抗彩虹表攻击。接口层加参数校验,实体字段上使用@NotBlank、@NotNull这类注解,非法参数在进入Service前就被拦截。数据库操作全部走参数绑定方式,防止SQL注入。登录接口做一个验证码校验,防止恶意暴力破解。查询和统计接口依赖索引和分页插件,避免一次加载全表数据。
8. 踩坑实录:十个同学九个会遇到
8.1 数据库连接报时区错误
用MySQL 8.0驱动时,连接串不加时区参数就会报The server time zone value异常,很多人第一次见到会懵。解决办法很简单,在数据库连接URL上加上serverTimezone=Asia/Shanghai,或者改用useSSL=false&serverTimezone=UTC。这个问题出现频率极高,提前配好能省不少查日志的时间。
8.2 中文乱码,从数据库到前端三层排查
乱码问题通常是三层中某层没统一编码。数据库表要用utf8mb4,数据库连接串加characterEncoding=utf8,后端接口返回数据时设置application/json;charset=UTF-8,前端页面meta标签里声明utf-8。三层都统一好,中文就不会出问题。如果用了过滤器或拦截器,也要注意别在过滤过程中把编码弄乱。
8.3 分页total始终为0或数据重复
这个问题我强调过,MyBatis-Plus的分页插件没注册是头号原因。只引入依赖不配置PaginationInnerInterceptor,分页查询的total永远是0,翻页后数据会出现重叠。在MybatisPlusConfig里配置好拦截器后重启,问题立刻解决。还有一个容易忽略的点:如果实体类有@TableField(exist = false)的字段,查询时不要让它参与排序或where条件。
8.4 维修工单状态跳变没有限制
有些同学实现时只在Controller里写update逻辑,没有做状态校验,结果工单能从已完成状态直接改成待派单,宛如时间倒流。正确做法是把所有状态变更操作封装在Service层,先查询当前状态,再根据状态机判断是否允许目标状态,不允许就抛业务异常。这样业务规则才不会被绕过。
8.5 JWT过期后前端页面白屏
token过期是前端的典型问题。后端拦截器抛了401,但如果前端的axios响应拦截器只处理了200,那么401响应会被忽略,页面表现就是白屏或没有反应。需要在响应拦截器里判断response.status === 401,然后跳转到登录页并清理本地token数据。
8.6 定时任务到点不执行
定时任务不跑,十有八九是启动类没加@EnableScheduling注解。另一个隐蔽问题是cron表达式写错,比如想每天早上八点执行写了0 8 * * * ?,漏了中间的0,结果一天执行了无数次或者根本不执行。建议先用一个极短间隔的表达式做测试,比如0/5 * * * * ?,确认触发正常后再改回正式规则。
9. 答辩准备:亮点怎么讲、问题怎么答
9.1 演示Demo的节奏比功能更重要
答辩时间通常有限,演示环节不能从头到尾点一遍菜单,要有设计。建议按这条线走:登录时先讲清楚账号是admin,强调角色权限;进入首页先展示统计卡片,说明系统能汇总设备总数、维修中数量、待办保养数量;然后演示设备台账的搜索和详情页,翻出一条设备讲它的完整档案;接着现场发起一条维修报修,走完派单、接单、完工全流程;最后切到保养计划页面,展示即将到期的设备被自动提醒。
这一套流程讲下来,既覆盖了所有核心模块,又把业务价值讲清楚了。演示时提前把测试数据准备充分,不要现场现输中文内容,太容易翻车。
9.2 高频答辩问题与回答方向
老师围绕这套题目的提问,范围基本集中在这几类。技术选型类,比如"为什么选Spring Boot",回答方向是开发效率高、生态成熟、内嵌Tomcat部署简单;"MyBatis-Plus和JPA的区别",可以讲MyBatis-Plus的灵活SQL和分页插件,JPA的自动建表和级联更方便但复杂查询不好控制。
业务设计类,"保养周期是怎么设定的",讲计划表里的周期类型字段加上次执行日期、下次执行日期和提前提醒天数,定时任务扫描计算出待办;"设备报废后数据如何处理",讲不是物理删除,而是状态置为报废并保留历史工单和采购记录,方便追溯。
安全类,"如果设备数量达到十万级,系统怎么保证性能",可以从索引、分页、Redis缓存、读写分离几个层面讲,不需要实际做过分布式,只要把思路讲清楚就能过关。
9.3 让项目看起来有深度的三个小扩展
如果时间和精力充裕,给项目加一两个小扩展,答辩的分数能明显拉开差距。第一个是扫码报修,给每台设备生成一个唯一的二维码,科室人员手机扫码直接跳转报修页面,体验贴近真实场景。第二个是维修费用统计和保修期预测,把历史维修数据按月汇总,用ECharts折线图展示,回答"系统到底能解决什么管理问题"时非常有说服力。第三个是操作日志审计,记录所有关键操作的操作人、操作时间、变更内容,这个功能本身就对应医疗设备管理的合规要求。
最后说一点我常跟学生讲的体会:这种附源码的毕设项目,最大的价值不是让你交差,而是给你一个可以拆开揉碎的学习样本。拿到手之后先跑起来,再画一遍数据库表关系,再把维修工单那条状态流跟着代码走一遍,最后删掉一部分逻辑自己重写一遍。走完这三步,答辩基本不会慌,这个项目带来的收获也会比单纯填个名字交上去大得多。