1. 应急物资管理系统到底在管什么:从业务说起
很多人拿到"SpringBoot+Vue应急物资管理系统"这一类的毕设源码,第一反应是先跑起来、截个图、写论文。但如果你是认真想把这个项目吃透,或者准备在答辩时讲清楚"我做的是什么",我建议先花半小时把业务模型捋明白。业务不清楚,代码写得再花哨,老师一问需求来源就露馅了。
应急物资管理,核心场景是应对突发事件(自然灾害、公共卫生事件、大型活动保障)时的物资保障工作。你可以把它理解成一个带有强时效性和强计划性的仓库管理系统。和普通进销存相比,它有几个非常鲜明的业务特点:
- 物资分类有规范:应急物资通常按用途分为防护用品、生命救助、救援设备、临时食宿、通信照明等大类,每一类下又有具体品名、规格、计量单位。
- 库存安全阈值是硬需求:普通仓库知道"还有多少货"就行,应急物资必须知道"够不够",所以每个物资都要设置库存上下限,低于下限要自动预警,提醒管理员补货或调拨。
- 出入库必须有据可查:应急物资的调拨往往跨部门、跨层级(比如区级库调拨到街道、街道发放到一线),每一笔出入库都要关联来源、去向、经手人、时间,形成完整追溯链。
- 报表统计是给领导看的:物资入库总量、出库总量、当前库存、预警物资清单、月度出入库趋势,这些是应急指挥决策的直接依据。
这套系统的典型用户角色有三种:系统管理员(负责用户管理、物资分类维护、基础数据管理)、仓库管理员(负责入库、出库、盘点、库存查询)、普通用户/领导(负责浏览库存、查看预警、查看统计报表)。如果是完整的毕设项目,还应该有审批流程——比如申请出库时需要管理员审批,不过这会让项目复杂度上一个台阶,很多毕设会选择去掉审批,只保留基础功能,这个取舍我后面会细说。
所以,当你说"我做了应急物资管理系统"时,你实际上是在说:我搭建了一个覆盖物资档案管理、分类管理、库存管理、出入库操作、预警提醒、统计报表的完整业务闭环。这套闭环跑通了,项目的业务价值就立住了。
2. 技术选型与项目架构:为什么是SpringBoot+Vue,前后端怎么分工
2.1 选型逻辑:毕业设计场景下的最优解
先聊一个几乎所有毕设同学都会纠结的问题:技术栈到底怎么选?
SpringBoot + Vue 这个组合,放在今天依然是高校毕设里最主流、也最稳妥的搭配,没有之一。原因很实际:
- SpringBoot是当前企业级Java后端的事实标准,自动配置、内嵌Tomcat、Starter机制,让后端开发从"配置地狱"里解放出来,特别适合一个人短时间内把后端业务写完。
- Vue是国内前端圈普及率最高的框架,渐进式设计让新手可以从最简单的数据绑定入手,配合Element UI这类组件库,能快速搭出漂亮的后台管理界面。
- 前后端分离是当前企业开发的真实形态,毕设用这个架构,答辩时天然有"我的项目贴近企业实践"的说法。
- 学校老师的接受度高,几乎不需要额外解释技术选型理由。
如果你正在犹豫要不要换其他框架,我的建议是:不要换。SSM太老、Spring Cloud太重、React学习曲线比Vue陡、若依这类脚手架又太"黑盒",拆掉框架自带的东西反而比从零写还难。SpringBoot+Vue+MySQL,就是这个场景的黄金组合。
2.2 后端项目结构:包名即业务地图
拿到源码后,第一步不是急着启动,而是把后端的包结构看一遍。一个规范的SpringBoot项目,包结构本身就是在讲业务:
com.emergency ├── controller // 控制层:接收请求、参数校验、返回结果 │ ├── AdminController │ ├── MaterialController │ ├── StockController │ ├── InStockController │ ├── OutStockController │ ├── WarningController │ └── StatisticsController ├── service // 服务层:业务逻辑、事务管理 │ ├── impl │ └── ... ├── mapper // 数据访问层(MyBatis-Plus的Mapper接口) ├── entity // 实体类,对应数据库表 ├── dto // 数据传输对象,用于接收前端传参、组装返回结果 ├── vo // 视图对象,用于向前端返回定制化数据 ├── config // 配置类:MyBatis-Plus分页、CORS跨域、拦截器 ├── common // 通用类:统一返回结果、异常处理、工具类 └── EmergencyApplication.java // 启动类记住一个判断项目好坏的粗标准:如果controller里全是业务逻辑、service层形同虚设、mapper里全是手写SQL,那这个项目的分层就是不合格的。合格的毕设应该是controller薄、service厚、mapper精准。
2.3 前端项目结构:Vue+Element的经典布局
前端的标准结构长这样:
src ├── api // 封装所有后端接口请求 │ ├── material.js │ ├── stock.js │ └── ... ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置,含守卫(登录拦截) ├── store // Vuex状态管理(用户信息、侧边栏折叠状态) ├── views // 页面组件 │ ├── login.vue │ ├── layout.vue // 主布局(侧边栏+顶栏+内容区) │ ├── material/ │ ├── stock/ │ └── dashboard.vue ├── utils // 工具类(axios封装、token存储) └── App.vue前后端分离开发的核心是约定接口格式。不走HTTP协议、不按RESTful约定,前后端各写各的,最后联调时就会出现"前端说后端返回的字段不对、后端说前端传的参数不对"的经典扯皮。所以接口文档在分离架构里不是可选项,而是必需品,下面专门讲。
3. 数据库设计与SQL脚本核心逻辑:八张表撑起一个系统
3.1 表结构设计:字段即业务的底层表达
一份完整的应急物资管理系统SQL脚本,通常包含用户表、角色表、物资分类表、物资信息表、入库记录表、出库记录表、库存表、预警/通知表这八张核心表。我逐个拆一下设计要点:
用户表(sys_user)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| username | varchar(50) | 登录名,唯一 |
| password | varchar(100) | BCrypt加密存储 |
| real_name | varchar(50) | 真实姓名 |
| phone | varchar(20) | 联系方式 |
| role_id | bigint | 关联角色表 |
| status | tinyint | 1启用 0禁用 |
| create_time | datetime | 创建时间 |
这里有两个细节很多人会忽略:密码一定不能明文存,建议用Spring Security自带的BCryptPasswordEncoder做哈希,这也是答辩时能加分的点;用户和角色的关系,如果一个人只有一种角色,用role_id字段就够了,没必要做成user_role关联表,毕设要控制复杂度。
物资分类表(material_category)
核心字段是parent_id,用来做无限级分类(父分类、子分类)。比如"防护用品"是一级分类,下面可以挂"口罩""防护服"等二级分类。树形结构的增删改查,是答辩时老师喜欢追问的点,你要能讲清楚递归查询的思路。
物资信息表(material)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| category_id | bigint | 所属分类 |
| name | varchar(100) | 物资名称 |
| spec | varchar(100) | 规格型号,如"N95""500ml" |
| unit | varchar(20) | 计量单位,如"个""箱""套" |
| stock_lower_limit | int | 库存下限 |
| stock_upper_limit | int | 库存上限 |
| storage_location | varchar(100) | 存放位置,如"A区-03号货架" |
| remark | varchar(500) | 备注 |
| create_time | datetime | 创建时间 |
注意,物资表和库存表我拆成了两张表。物资表存"静态档案"(叫什么、什么规格),库存表存"动态数量"(现在有多少)。如果不拆,每次入库都要去UPDATE物资表,不仅逻辑混乱,还容易丢数据。这个设计决策在答辩时值得主动提一句。
入库记录表(in_stock_record)和出库记录表(out_stock_record)
结构是对称的,核心字段都包括:物资id、数量、操作人、关联单号、操作时间、备注。出入库记录区别在于入库要记"供应商/来源",出库要记"领用人/去向",这是追溯链条的关键。所有表格中,这两张表的数据只增不改,一旦入库或出库完成,记录就应该是不可修改的,这也是库存系统的基本诚信原则。
库存表(stock)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| material_id | bigint | 物资id,唯一 |
| quantity | int | 当前库存数量 |
| update_time | datetime | 最后更新时间 |
有人会问:库存为什么不直接算出来(总入库-总出库)?理论上可以,但实际系统里库存一定是要单独落表的,因为查询频率最高,每次都把出入库记录全表SUM一遍,性能扛不住。每次出入库操作后同步更新库存表的quantity字段,这叫"冗余存储、一致性维护",是库存系统的标准做法。
预警/通知表(warning_record)
记录预警内容(哪个物资低于下限)、预警时间、是否已处理。预警的触发时机有两个:一是每次出入库操作后判断一次库存是否越过阈值;二是系统启动/定时任务扫描一次。第二种方式更稳,因为能兜住数据初始化或手动改库的边界情况。
3.2 SQL脚本使用注意:字符集与数据初始化
拿到SQL脚本,导入数据库时最容易踩的坑有三个:
- 字符集没选对:数据库连接参数和表结构里必须统一使用utf8mb4(不是utf8),否则中文全部乱码。utf8mb4是utf8的超集,能存emoji,兼容性最好。
- 外键约束导致导入失败:如果脚本里有外键约束,导入时严格按"主表在前、从表在后"的顺序执行。很多毕业设计源码的SQL脚本为了省事,干脆不用外键,而是在Service层做逻辑关联——这其实是更现实的工程选择,因为外键在后续数据清理和测试数据插入时特别碍事。
- 初始化数据太假:一个加分的细节是,管理员账号、基础分类、十条左右的示例物资数据,应该在SQL脚本里就带好,这样项目启动后登录进去不是空页面,演示效果直接拉满。
4. 接口文档与前后端联调:把接口定明白,联调少加班
4.1 RESTful接口设计规范与接口格式约定
接口文档是这个项目里最容易被人忽视、但实际价值极大的部分。很多毕设代码都能跑,但接口文档要么没有,要么就是随手贴几张Swagger截图。一份合格的接口文档,至少要对每个接口讲清楚四件事:URL、请求方式、请求参数、返回结果。
以物资管理模块为例,标准RESTful接口列表长这样:
| 接口 | Method | 路径 | 说明 |
|---|---|---|---|
| 分页查询物资列表 | GET | /material/page | 分页+关键字搜索 |
| 查询物资详情 | GET | /material/{id} | 按ID查询 |
| 新增物资 | POST | /material | JSON入参 |
| 更新物资 | PUT | /material | JSON入参 |
| 删除物资 | DELETE | /material/{id} | 按ID删除 |
| 物资入库 | POST | /material/inStock | 物资id+数量 |
| 物资出库 | POST | /material/outStock | 物资id+数量 |
| 导出物资列表 | GET | /material/export | 返回Excel文件 |
接口的返回格式需要前后端统一约定。目前最通用的是这种结构:
{ "code": 200, "message": "操作成功", "data": { "total": 100, "records": [ { "id": 1, "name": "医用防护口罩", "spec": "N95", "quantity": 500 } ] } }后端封装一个统一的Result对象,code为200表示成功,非200表示失败(比如401未登录、403无权限、500服务器异常)。前端的axios拦截器统一判断code,成功就解出data交给页面渲染,失败就弹出错误消息。这样做的好处是,所有接口只通过code区分业务成功失败,HTTP状态码只管网络层和认证层,两层的职责分离,排查问题的时候非常清爽。
4.2 登录鉴权流程:Token是怎么"走"完整个系统的
应急物资管理系统的登录鉴权,毕设项目里最成熟的做法是JWT(JSON Web Token)。它的核心思想是:用户登录成功后,后端生成一个加密的Token返回给前端,前端后续每次请求都在Header里带上这个Token,后端验证通过才放行。
完整流程是这样的:
- 前端登录页提交用户名密码到
/login接口。 - 后端调用UserService查询用户,用BCrypt校验密码,成功后生成JWT返回。
- 前端把Token存到localStorage(或者Vuex里),并用axios拦截器在每次请求的header里加上
Authorization: Bearer 你的Token。 - 后端配置一个拦截器,拦截所有除
/login外的接口,验证Token有效后才放行。 - 前端路由守卫检查有没有Token,没有Token一律重定向到登录页——这就是"前端路由守卫+后端接口拦截"的双重防护。
毕设里前后端在这个环节最容易出问题的地方是跨域。前端跑在8080端口,后端跑在9090端口,浏览器会拦截跨域请求。解决方案在后端加一个CORS配置类:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }有个细节:setAllowCredentials(true)表示允许携带Cookie凭证,这时addAllowedOriginPattern("*")里的通配符才能正常工作,如果你用addAllowedOrigin("http://localhost:8080")这种方式指定来源,也必须把端口写准,前后端端口一旦换掉就要同步改这里。
4.3 接口文档的呈现形态:Swagger与手写文档的取舍
接口文档在这个项目里建议以两种形态同时存在:在线调试用Swagger,交付归档用手写Markdown。
Swagger(Spring Boot 2.x用springfox,3.x用springdoc)依赖很少,后端启动后访问/swagger-ui/index.html就能看到所有接口的调试页面,支持直接在线发起请求,非常方便自测和给老师演示。
但Swagger有个缺点:它只暴露了接口的技术参数,不解释业务逻辑(为什么这个接口要在出库前先查库存?为什么删除物资是逻辑删除而不是物理删除?),所以一份手写的、带业务说明的Markdown接口文档,既是你归档交付的一部分,也是写毕业论文时"系统设计"章节的素材来源。
5. 核心功能模块拆解:四大业务的代码级实现思路
5.1 登录验证码与用户管理:从"会登录"到"防暴力破解"
登录的完整实现不只是"查表比对密码",一个加分的登录功能我建议加上验证码。实现方式很简单:后端用Java的Graphics2D生成一张带随机四位数字的图片,把答案存到Redis或Session里,图片以Base64编码返回给前端,前端直接用img标签渲染。提交登录时,前端把验证码一起传输,后端对比一致才继续验证用户名密码。
这个功能技术上只多了三五十行代码,但在答辩效果上能带来一个不小的加分项:它能顺理成章地引出"防止暴力破解、防止脚本刷接口"的安全话题。老师顺着你的话问"那你Redis里存验证码设置了过期时间吗",你再答"设置了,五分钟过期",这个交互自然流畅,比被动等着问强太多。
用户管理的增删改查很模板化,但有一个点建议主动实现:逻辑删除。也就是用户表加一个deleted字段,删除用户时只是把deleted改成1,查询时默认过滤掉已删除用户。逻辑删除的好处是历史数据不丢,出库单里关联的"操作人"信息永远能查得到。如果你用MyBatis-Plus,在实体类的deleted字段上标注@TableLogic注解就搞定了,删数据变成UPDATE,查询自动加WHERE deleted=0,几乎零成本。
5.2 入库出库与库存更新:事务的经典现场
入库和出库是库存系统的核心操作,也是后端事务管理最典型的应用场景。一次出库操作,在代码层面至少要干三件事:
- 向
out_stock_record表插入一条出库记录。 - 更新
stock表的quantity字段,减掉本次出库数量。 - 判断新的库存是否低于下限,如果低于则插入一条预警记录。
这三件事必须捆绑成一个事务:要么全部成功,要么全部失败。假如第2步执行了、第3步抛异常了,库存数量变了但预警没生成,数据就处于不一致状态,这在应急物资场景里是很严重的问题。
用Spring的@Transactional注解包住整个方法,就能确保这三步在一个数据库事务里执行,任何一步失败,前两步自动回滚。
入出库的具体实现逻辑是这样的——以出库为例:
@Override @Transactional(rollbackFor = Exception.class) public Result outStock(StockOperateDTO dto) { // 1. 校验参数 Material material = materialMapper.selectById(dto.getMaterialId()); Assert.notNull(material, "物资不存在"); Assert.isTrue(dto.getQuantity() > 0, "出库数量必须大于0"); // 2. 校验库存充足 Stock stock = stockMapper.selectByMaterialId(dto.getMaterialId()); Assert.isTrue(stock.getQuantity() >= dto.getQuantity(), "库存不足"); // 3. 插入出库记录 OutStockRecord record = new OutStockRecord(); record.setMaterialId(dto.getMaterialId()); record.setQuantity(dto.getQuantity()); record.setReceiver(dto.getReceiver()); record.setOperator(getCurrentUsername()); record.setCreateTime(new Date()); outStockRecordMapper.insert(record); // 4. 扣减库存 stock.setQuantity(stock.getQuantity() - dto.getQuantity()); stockMapper.updateById(stock); // 5. 触发预警检查 checkStockWarning(material, stock); return Result.success("出库成功"); }值得注意的一点是rollbackFor = Exception.class。如果不指定这个参数,Spring只对运行时异常(RuntimeException)回滚,而对受检异常(比如IOException)不回滚。业务代码里很多地方会用Assert或自定义异常来终止流程,如果漏掉这个参数,可能出现"操作失败但数据已经改了"的诡异现象。这是Spring事务里最经典的一个坑,也是面试/答辩的高频提问点。
5.3 库存预警与低库存定时扫描:定时任务怎么不打扰业务主流程
库存预警的实现有两条触发路径,我在数据库设计部分提过,这里展开讲代码层面。路径一是操作后即触发,也就是上面出库代码里的checkStockWarning方法,入出库成功后就地检查一次,优点是实时性强,缺点是如果某天批量导入了大量历史数据,不会自动触发补齐预警。路径二是定时兜底扫描,用Spring自带的@Scheduled注解:
@Component public class StockWarningTask { @Resource private StockMapper stockMapper; @Resource private WarningRecordMapper warningRecordMapper; @Scheduled(cron = "0 0 8 * * ?") public void scanLowStock() { // 查询所有低于库存下限的物资 List<Stock> lowStocks = stockMapper.selectLowStockList(); for (Stock stock : lowStocks) { // 如果当天还没有生成过预警,插一条新记录 int count = warningRecordMapper.countTodayByMaterialId(stock.getMaterialId()); if (count == 0) { WarningRecord warning = new WarningRecord(); warning.setMaterialId(stock.getMaterialId()); warning.setType("LOW_STOCK"); warning.setMessage("物资库存低于安全阈值,当前库存:" + stock.getQuantity()); warning.setStatus(0); warningRecordMapper.insert(warning); } } } }定时任务必须加"当天已预警不重复预警"的判断,否则每天早上8点都会给同一个低库存物资刷一条新预警,一个月下来预警表里全是重复数据,真正看数据的人会被垃圾信息淹没。cron表达式0 0 8 * * ?表示每天早上8点执行一次,用@EnableScheduling在启动类开启定时任务功能即可。
前端预警页面可以做一个"待处理"角标,管理员处理完后把status改成1,页面自动减少未读数量。这个交互不复杂,但能直观地把预警功能的价值呈现出来。
5.4 统计报表与ECharts图表:领导要看的是趋势
应急物资统计报表通常包含四个维度:库存总量、出入库趋势、分类占比、预警统计。前端用ECharts渲染,后端负责提供汇总数据。
这里最有设计感的一个接口是"近30天出入库趋势",后端实现可以很轻量:
SELECT DATE(create_time) AS day, SUM(CASE WHEN type = 'IN' THEN quantity ELSE 0 END) AS in_count, SUM(CASE WHEN type = 'OUT' THEN quantity ELSE 0 END) AS out_count FROM stock_record WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY DATE(create_time) ORDER BY day;一个SQL就能把30天的出入库曲线数据全部取回来,前端拿到的直接就是[{day: '2025-01-01', in_count: 120, out_count: 30}, ...]这种JSON数组,丢给ECharts的折线图就能渲染。用一步SQL完成统计,比在Java里循环30天、每天查一次数据库的做法,性能和代码简洁度都高出一个量级。
如果数据量较大(比如有几万条出入库记录),可以再在表上加一个create_time索引,查询效率就有保障了。报表接口的SQL优化也是答辩时老师容易追问的方向,提前准备一句"这里用了索引覆盖、避免全表扫描",效果会很不错。
6. 部署运行与常见坑点:从源码到能演示的完整路径
6.1 后端启动全流程:本地跑起来的实操步骤
拿到SpringBoot项目源码后,按这个顺序操作踩坑最少:
检查JDK版本:先看pom.xml里的
java.version,如果是8,本地必须装JDK 8;如果是11或17,对应装JDK 11/17。JDK版本和SpringBoot版本有对应关系——SpringBoot 2.x用JDK8或11都可以,SpringBoot 3.x至少需要JDK17。你手里的源码如果是跟着教程写的,大概率是JDK8+SpringBoot 2.x,千万不要用最新版JDK去跑,会报一堆版本不兼容的错误。导入Maven依赖:用IDEA打开项目后,等待右下角Maven依赖下载完成。网络不好时这一步会非常痛苦,建议先配置阿里云Maven镜像,在settings.xml里加上
https://maven.aliyun.com/repository/public镜像地址,十分钟的下载能缩到一分钟。初始化数据库:创建数据库
emergency_db,字符集选utf8mb4,然后导入SQL脚本——直接命令行source xxx.sql或者用Navicat运行SQL文件都行。修改application.yml:重点核对数据源配置。
server: port: 9090 spring: datasource: url: jdbc:mysql://localhost:3306/emergency_db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver- 启动并验证:运行启动类,看到 "Started EmergencyApplication in xx seconds" 的日志就成功了。浏览器访问
http://localhost:9090/swagger-ui/index.html如果能打开接口文档页面,后端就稳了。
6.2 前端启动全流程:Node版本是最常见的坑
前端Vue项目的启动相对简单,但也有一个高频翻车点:
确保安装Node.js。这里注意Node版本不要太新,如果你用的是Vue 2项目,Node 18以上经常会出现
OpenSSL Error报错,原因和新版本Node的加密库变更有关,建议直接装Node 16.x长期支持版,稳得很。Vue 3项目对Node版本要求宽松一些,但Node 16依然是安全区。在项目根目录执行
npm install。这一步失败的话,大概率是网络或镜像源问题,可以换成淘宝镜像源:
npm config set registry https://registry.npmmirror.com依赖装好后执行
npm run dev,看到Compiled successfully就成功了,默认访问http://localhost:8080。前端访问后端接口,需要确保后端启动的前提下,在Vue项目的
vue.config.js中配置开发代理,把/api路径转发到后端:
module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:9090', changeOrigin: true } } } }这里有个容易被忽略的逻辑:开发环境用代理转发请求转发到后端,避免了前端代码里写死后端IP的问题。但生产部署时,前端构建出的静态文件如果要提交到服务器,就需要反向代理工具来统一转发,不能靠代码里的devServer了。
6.3 毕设高频坑点清单:亲测容易翻车的地方
这里列一份我见过的、被问过无数次的坑,按出现频率排序:
| 坑 | 表现 | 原因与解决 |
|---|---|---|
| 中文乱码 | 页面显示� | 数据库连接URL加characterEncoding=utf8mb4,表字段统一utf8mb4 |
| 跨域请求失败 | 浏览器控制台CORS报错 | 后端配CorsConfig,或前端配devServer代理 |
| Token过期 | 操作提示未登录/401 | 检查JWT过期时间,默认建议设2小时,演示期间够用 |
| 前端环境变量问题 | 接口地址调不通 | 检查axios的baseURL是否指向代理路径或后端地址 |
| Lombok失效 | getter/setter找不到 | IDEA安装Lombok插件,并开启Annotation Processing |
| MyBatis-Plus分页无效 | 分页返回全部数据 | 未配置分页插件,需要新建MybatisPlusInterceptor的Bean |
"分页无效"这个坑尤其值得多说两句。MyBatis-Plus的分页插件不是加了依赖就自动生效的,必须在配置类里显式注册,而且这个Bean有加载顺序要求:分页插件必须是最后一个注册的MybatisPlusInterceptor。很多同学漏掉这个配置,结果selectPage方法返回了所有记录,还以为是SQL写错了,排查半天。
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }6.4 答辩前的自测路线:按这个顺序跑一遍不出丑
答辩演示时的操作顺序直接影响老师的第一印象。我建议按这个路线自测几遍:
- 从登录页开始,输入账号密码和验证码,演示登录成功,进入首页看统计大屏。
- 点开物资管理,分页浏览、搜索一个关键词(比如"口罩"),演示查询功能。
- 演示新增物资,表单填写完整后提交,列表立刻出现新数据。
- 对某个物资做一次入库操作,然后切到库存页面看数量变化。
- 找一个库存低于下限的物资做一次出库,演示预警列表出现新预警。
- 打开报表页面,截图或录屏展示图表趋势。
每一步操作后,数据库的变化和页面变化要对得上,因为老师很可能追问"你这一操作在数据库里产生了什么影响"。自己提前把链路看清楚,比临时翻代码强一百倍。
7. 我能给到的最后几条实操建议
项目跑到这个阶段,源码已经能正常启动、业务能跑通、文档也齐了,但距离一个"高分毕设"还有最后一公里的打磨空间。
如果你还有一周以上的时间,建议优先做三件小事。第一,把接口文档按模块整理成一份完整的Markdown,每个接口附上真实请求和返回示例,这份文档既是论文附录的素材,也能作为"系统使用指南"留在项目里,显得整个项目交付物非常完整。第二,在系统里多录几组有代表性的数据——比如某类物资库存刚好卡在下限边缘,这类数据演示时比清一色的正常数据更有说服力,能自然引出预警功能。第三,把启动说明写进README,数据库导入方式、前后端启动命令、演示账号写清楚,这是老师拿到项目后第一时间会看的东西。
最后,项目代码能跑只是底线,能在答辩时把"为什么这么设计"讲明白才是上限。这套系统里藏了很多值得主动讲的设计决策:为什么库存表单独落一张表、为什么出入库操作要加事务、为什么预警要定时扫而不只靠操作触发、为什么用JWT而不是Session。把这几个"为什么"吃透了,应急物资管理系统这个毕设项目,你就真正拿到手了。