我做了几个学期的毕设指导,也帮人排查过不少类似的系统,可以这么说:抗疫物资管理系统这类题目,是Java Web方向里性价比最高的选题之一。有完整业务闭环(入库、出库、库存、申领、统计),有前后端分离技术栈,又贴合实际场景,答辩时故事也好讲。不过正因为做的人多,想拿高分,关键是别停留在“能跑就行”,而是要把几个核心模块的设计逻辑真正讲清楚。
这篇文章就围绕 SpringBoot + Vue 的抗疫物资管理平台,从需求拆解、数据库设计、权限模型、核心接口实现,到前端联调和部署的完整链路,把我的实操经验和踩坑记录都放出来。不管是正在做毕设,还是想拿这个项目练手,看完应该能少走不少弯路。
1. 项目定位与功能拆解:先搞明白“抗疫物资管理系统”到底在管什么
1.1 核心需求解析:这类系统解决的三个核心问题
很多人一开始拿到这个题目的反应是“做一个物资增删改查”,这个理解太浅了。如果只是CRUD,那跟课程设计没什么区别。抗疫物资管理系统之所以有区别度,是因为它背后有真实的管理痛点。
第一个痛点是信息同步慢。物资的库存数量、申领审批进度、分发记录,如果靠表格和微信群来回传,就会产生一个经典的“信息孤岛”问题:库房说口罩还有五千个,一线却说已经断供两天了。系统要解决的就是让这些数据在一个平台里实时流动。
第二个痛点是审批流程不透明。物资不是谁申请就能谁拿走,通常有“部门申请—负责人审批—库房出库”这样一套流程。如果在线下走纸质单,既难追溯也不好统计,所以要系统支撑流程状态跟踪。
第三个痛点是数据汇总难。管理者要回答“这个月防护服消耗了多少”“哪些物资临近过期”“哪个部门的申领占比最高”这些问题,没有结构化数据,只能拍脑袋。所以系统必须包含统计报表模块。
我见过不少同学把大量精力花在花哨的页面效果上,结果库存一致性和权限控制这两个核心维度没做好,答辩被老师追问几句就露馅了。记住,这类系统的灵魂是数据准确和流程严谨,而不是视觉效果。
1.2 功能模块标准划分:一套可直接参考的系统功能清单
基于上面的需求,我建议把系统分成七个功能域。这套划分方式不仅适用于抗疫物资场景,换成“实验室耗材管理系统”“办公用品管理系统”也完全成立。
- 用户与权限管理:登录、登出、密码修改;用户分角色,常见的是系统管理员、仓库管理员、部门用户(或普通员工)三种,采用不同的操作权限。
- 物资档案管理:物资的基础信息,包括物资编码、名称、分类(口罩、防护服、消毒液、医疗器械等)、规格型号、计量单位、保质期信息等。
- 入库管理:入库单创建与审核。要注意的是,入库操作会直接影响库存总量,所以必须记录来源(采购、调拨、捐赠)和批次信息。
- 出库管理(申领管理):部门填写申领单,管理员审批后出库。这里涉及库存扣减和批次扣减策略,是整个系统业务逻辑最重的模块。
- 库存管理与预警:实时库存查询、库存流水、库存预警(下限阈值、临期提醒)。
- 统计报表:收发存汇总、部门消耗排行、分类占比等图表,通常用图表库展示。
- 系统管理:操作日志、数据字典管理。
可以看到,这套模块拆分的逻辑是从“基础数据”到“业务流转”,再到“数据决策辅助”,层次分明,也符合标准的企业级应用形态。
1.3 技术选型和版本规划:为什么是 SpringBoot + Vue 这个组合
技术栈的选择直接决定了项目开发效率和答辩观感。SpringBoot + Vue 至今仍是Java Web毕设的主流选择,我大概总结一下原因。
后端用 SpringBoot,内置Tomcat,不需要单独部署WAR包;Starter体系简化了配置,只需要引入依赖即可快速整合MyBatis、MySQL、Redis等组件;自带Actuator、统一异常处理等能力,写起来省心,而且企业里真正用的也是这套,有较高的实用价值。
前端用 Vue + Element UI(或者 Vue 3 + Element Plus),生态成熟,组件库丰富。表格、表单弹窗、树形控件都能直接拿来用,开发效率非常高,而且国内社区资料多,遇到问题搜索就能解决。
数据库用 MySQL,SQL脚本直接附上,方便评审老师直接跑起来看效果。身份认证用 JWT 做无状态登录,避免传统 Session 在前后端分离架构下的跨域问题。
如果你还想加分,可以加一层 Redis 缓存热点数据(比如库存数量和用户权限信息),再配合定时任务做库存预警扫描。这些点不一定都写完,但设计思路一定要有,答辩的时候提出来会显得考虑周全。
2. 数据库设计详解:五张核心表的结构与关键字段分析
2.1 数据库整体设计思路:从业务流转到表结构
数据库设计是整个系统能不能落地的基础。很多人建表凭感觉,字段随意加,直接导致后期开发时逻辑混乱。我的习惯是,先画业务流转图,从“人、物资、单据”三个核心概念出发来抽象表结构。
在抗疫物资管理系统中,核心参与方有三类:用户、物资、出入库单据。围绕这三个概念,我设计了以下主要表:
- 用户表(sys_user):存放用户账号、密码(加密存储)、姓名、联系电话、所属部门、角色标识。
- 物资分类表(material_category):虽然只有一个字段值差异,但还是建议独立成表,方便扩展分类层级。
- 物资信息表(material_info):存放物资名称、编码、规格、单位、库存数量、预警阈值、有效期等。
- 入库记录表(stock_in_record):每一条数据对应一次入库动作,记录物资编码、来源、入库数量、经办人、入库时间。
- 出库记录表(stock_out_record):记录申领部门、物资编码、出库数量、领取人、审批人、出库时间、用途。
这里我特别想提醒一点:库存数量不要只存在物资信息表的库存字段里,还要有出入库流水表。因为一旦出现数据对不上,流水表才能作为追溯的依据。这也是为什么说“有审计能力的CRUD”才是管理系统,而不是单纯改一个数字。
2.2 字段级设计细节:哪些字段决定成败
很多项目字段设计看似合理,但实际开发时才发现缺这少那。我根据自己的开发经验,把几个重要的表列出来,给出推荐字段结构。
用户表(sys_user)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键自增 |
| username | varchar(50) | 登录账号,唯一 |
| password | varchar(100) | 加密后的密码(BCrypt) |
| nickname | varchar(50) | 显示名称 |
| role_id | bigint | 关联角色表的id |
| dept_id | bigint | 所属部门 |
| status | tinyint | 1启用 0禁用 |
| create_time | datetime | 创建时间 |
密码一定要加密。直接用明文存的话,答辩时老师看到会非常减分。BCrypt 是 Spring Security 自带的加密方式,生成一串带盐的哈希值,安全性比简单的 MD5 高得多。
物资信息表(material_info)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| material_code | varchar(50) | 物资编码,如 YY-001 |
| material_name | varchar(100) | 物资名称 |
| category_id | bigint | 分类id |
| spec | varchar(100) | 规格型号 |
| unit | varchar(20) | 单位(个/箱/瓶) |
| stock_quantity | int | 当前库存数量 |
| warning_quantity | int | 库存预警下限 |
| expire_date | date | 保质期 |
| status | tinyint | 1在库 0停用 |
| remark | varchar(255) | 备注 |
这里的 stock_quantity 可以设计成冗余字段,每次入出库事务性更新,也可以用视图动态计算。我更推荐存一个实时的库存数字用于列表展示,同时通过流水表保证可追溯性。两种方案配合最终一致性的纠错机制,既快又稳。
出入库记录表(stock_in_record / stock_out_record):核心字段包括单据编号(bill_no)、关联物资 id、单据数量、操作人 id、审核人 id、单据状态、备注、创建时间。这种单据类表一定要保留“创建人”和“审核人”两个字段,因为审批流后面要用。
2.3 SQL脚本的编写注意事项:索引、外键与初始化数据
SQL脚本是这个项目里最容易被忽略却又很关键的部分。因为评审老师拿到项目后,第一步就是导入SQL跑起来。脚本写得不规范,或者初始化数据不够合理,直接影响第一印象。
我建议以下几点必须注意:
- 建表语句加 IF NOT EXISTS,避免重复执行时报错。
- 字符集统一用 utf8mb4,否则后期插入表情符号或其他特殊字符容易乱码。
- 关键关联字段建索引:material_id、user_id、create_time。查询统计的时候索引非常管用。
- 外键约束:建议在表设计里体现,但实际导入时如果担心数据插入顺序问题,可以使用逻辑外键(不加物理约束,靠代码保证),两者皆可,但必须在文档里说清楚理由。
- 初始化数据要完整:超级管理员账号(admin)、仓库管理员账号(operator)、几个部门测试账号,各角色的密码用同一套BCrypt加密值;还要有至少10种物资和对应的分类数据,这样前端首页和统计图表才不会空荡荡。
3. 后端核心模块实现:从登录鉴权到出入库的完整业务逻辑
3.1 登录鉴权与权限控制:JWT + Spring Security 的正确打开方式
登录认证是前后端分离项目的第一个技术门槛,也是毕设答辩最常被问的部分。我用 JWT(Json Web Token) 结合 Spring Security 的实现方案,这里把整体思路讲一下。
流程是这样的:用户提交账号密码,后端校验通过后生成一个 JWT 令牌返回前端。前端把令牌存在本地存储或内存中,之后每次请求在请求头里带上Authorization: Bearer <token>字段。后端配置一个过滤器,在请求进入Controller之前拦截校验token的有效性,并解析出用户角色,用于权限判断。
这种方案最大的好处是服务器不需要存储会话状态。传统 Session 方案要在服务端保存用户状态,前后端分离时还得解决 Cookie 跨域的问题。而 JWT 是无状态的,天然适合这种架构。
在实际编码中,有几个关键点值得注意:
- 密钥要放在配置文件中,不要写死在代码里。
- token 要设置合理的过期时间(我建议2小时),并刷新机制,否则用户用着用着就掉线。
- Spring Security 的过滤器链需要排除登录接口、验证码接口等少数公开路径,其余接口都要过认证。
- 密码用
BCryptPasswordEncoder加密,校验时调用matches()方法。
权限模型我设计的是基于角色的访问控制,超级管理员拥有全部权限,仓库管理员可以操作出入库和查看库存;部门用户只能申领物资和查看本部门记录。颗粒度不用做到按钮级,但菜单级和操作级一定要有区分。
3.2 物资入库功能实现:事务处理与批次记录的常见坑
物资入库的页面逻辑很简单,但后端这里一定要谨慎,因为入库不是一个单纯的 INSERT。它实际上是由两个步骤组成的:插入入库记录 + 增加库存数量。这两个操作必须在一个事务里完成,否则就容易出现记录有了但库存没涨,或者库存涨了但记录缺失的脏数据问题。
我在代码里通常这样实现:
@Transactional(rollbackFor = Exception.class) public void stockIn(StockInDTO dto) { // 1. 校验物资信息,不允许对已停用的物资进行入库 // 2. 生成入库单号(例如 IN + 时间戳 + 随机数) // 3. 插入入库记录 // 4. 同步更新物资库存表数量 }注意@Transactional注解的rollbackFor要设置成Exception.class,因为默认它只在遇到运行时异常时才回滚,如果直接抛受检异常,事务不会回滚,这个细节很多新手会踩到。
入库单号生成也有讲究。不能只靠数据库自增id来标记单据,因为对外沟通时“第500条记录”远不如“单据号IN20250114001”来得清晰。我习惯用前缀 + 日期 + 序列号的格式,一眼就能看出是哪一天的单据。
另外,我建议入库单设计“草稿—提交—审核”三个状态。如果毕设时间不够,可以砍到两个状态:待审核和已入库。不然眼前的下拉菜单会多一个状态,前端组件不算复杂,但后端逻辑会相对多一些。这个取舍根据个人时间和精力决定。
3.3 申领出库功能实现:库存扣减时机与幂等性
出库(申领)是业务上最容易出错的地方。一个完整的出库流程是:
- 部门用户填写申领单,选物资、填数量、填用途。
- 系统校验申领数量不能大于现有库存。
- 提交后,单子进入待审批状态,库存数量暂时不变(这叫“预占”或者“冻结”也可以,看需求)。
- 管理员审批通过后,才真正扣减库存。
- 如果审批拒绝,单据状态变为已驳回,库存不受影响。
确定哪种方案取决于一个需求细节:要不要支持“多人同时申领同一批物资,导致库存超卖”?如果要严谨控制,就必须在审批通过时使用数据库行级锁,例如查询时加FOR UPDATE,或者使用乐观锁版本号。如果只是毕设演示,最简单的方式是在提交申领时校验一次库存,审批通过时再校验一次,双重校验就能挡住90%的问题。
SQL层面的悲观锁操作可以参考:
SELECT * FROM material_info WHERE id = #{materialId} FOR UPDATE这行SQL的意义在于:事务 A 在审批通过时锁住这行物资数据,直到更新完库存并提交,事务 B 才能读取。这样就避免了两笔出库同时读到“还有10个库存”,然后各自扣减8个导致库存变成-6的情况。
出库单的状态流转也比较清晰:待审核、已通过、已驳回、已完成。要注意每次状态更新都要记录操作人和时间,这就是前面说的审计能力。
3.4 库存预警与统计报表:定时任务 + SQL聚合的技巧
库存预警是这个系统里比较能体现工程能力的部分,建议单独拎出来展示。实现方式有两种:
- 定时扫描:用 Spring Boot 的
@Scheduled注解写一个定时任务,每天凌晨扫描所有库存数量小于预警阈值的物资,生成预警记录。 - 即时判断:在每次出库扣减库存后,立刻判断剩余库存是否低于阈值,是就标记该物资为预警状态。
两种方式可以同时用,实际开发中也常这么干。预警记录可以放在单独的预警表里,也可以只给物资表加个状态字段,配合前端黄色/红色高亮展示即可。定时任务的时间建议做成可配置的值,写在 application.yml 里,而不是写死,这样适配性更好。
统计报表用的核心是 SQL 聚合查询。比如按分类统计库存量,就是GROUP BY category_id然后SUM(stock_quantity);按部门统计申领次数,就是GROUP BY apply_department然后COUNT(*)。后端封装好查询接口,返回数据结构给前端图表组件直接渲染。图表库用 ECharts 比较多,柱状图、饼图、折线图都支持得很好。
3.5 接口文档与统一返回结构:降低前端联调难度的关键
接口文档是这个项目里不可缺失的交付物。很多同学不重视接口设计,导致前端开发时反复沟通耗时。我的建议是定义一套统一的返回结构:
{ "code": 200, "message": "操作成功", "data": { } }所有接口都返回这个格式,前端只需要针对code做统一判断即可。错误码建议划分为:200成功、400参数错误、401未认证、403无权限、500系统错误。这样前端对异常的识别非常直观。
接口文档方面,如果时间充足可以用 Swagger(Knife4j)自动生成,在线调试方便,答辩演示也加分。如果来不及,也可以在项目里附带一份Markdown格式的接口列表,标明每个接口的URL、请求方式、请求参数、返回示例。重点是逻辑清晰,不遗漏。
4. 前端工程开发与前后端联调要点
4.1 Vue 项目结构和路由设计:按模块划分而非按页面划分
前端工程我建议用 Vue CLI(或 Vite)创建,统一用 Vue 2 + Element UI 或者 Vue 3 + Element Plus 都可以,只要保持整体风格一致就行。这里说一下项目目录结构的分隔逻辑,这个直接影响后期的可维护性。
src/ api/ // 按模块划分的接口请求封装模块 assets/ // 静态资源 components/ // 公共组件(上传组件、富文本、弹窗等) layout/ // 页面布局框架(侧边栏、顶栏) router/ // 路由配置 store/ // 全局状态管理(Vuex 或 Pinia) views/ // 页面api 目录里,我喜欢每个模块建一个文件,比如login.js、material.js、stock.js。里面把该模块下的所有接口请求都封装成函数,方便页面调用,也方便统一处理 token 注入。
路由设计上,用前端路由守卫拦截未登录用户。在router.beforeEach里判断本地是否有 token,没有就跳转到登录页;有就放行。同时根据用户角色,动态生成可以访问的菜单项。这个点如果做了,答辩的时候会很加分,因为“权限控制”在前后端都有体现。
4.2 Axios 封装与请求拦截器:token注入、错误码统一处理
前端和后端的联通依赖 HTTP 请求。我强烈建议用 Axios 封装一层,好处是统一处理很多冗余操作。
// axios.js const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) // 请求拦截器:注入token 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) { // 跳转登录页 router.push('/login') } return Promise.reject(new Error(res.message)) } return res.data }, error => { Message.error('网络异常,请稍后重试') return Promise.reject(error) } )这个封装的价值在于:登录过期、服务端错误、网络异常这些情况,在每一个接口里不用都重复处理,统一拦截即可。前端开发时只需要关心业务数据,不需要关心鉴权细节。
4.3 典型页面实现:物资列表筛选、入库弹窗表单、出库审批操作
拿库存列表这个页面举例,它是最典型的表格+搜索+分页+操作按钮组合页面。后端接口设计时要注意返回的数据结构。
分页参数我推荐这样传输:pageNum和pageSize,返回结构为:
{ "records": [], "total": 160 }Vue 页面里,用el-table展示数据,el-pagination绑定分页,搜索区放几个el-input和el-select,搜索按钮触发重新查询。
库存列表页除了展示当前数量,还可以加一个“出入库记录”的按钮,点击弹窗展示该物资的历史流水。这个功能做起来不难,但非常实用,也展示了你对业务的理解。
入库表单可以用抽屉(el-drawer)或弹窗(el-dialog),结构上是动态表单:选择物资 -> 填数量 -> 填来源 -> 提交。前端在提交前做一次正整数校验,避免把0或负数发到后端。
4.4 前端部署与后端发布:本地跑通还是服务器部署
毕设阶段,最低要求是本地能跑通。但不代表你可以不写部署说明。很多同学把项目发给评审老师后,老师因为环境问题跑不起来,直接影响最后评分。
前端生产构建跑npm run build,生成 dist 目录。后端打 jar 包跑mvn clean package或mvn spring-boot:run。如果想在一台服务器上同时部署前端静态资源和后端接口,可以用 Nginx 做反向代理,把/路径指向 dist 目录,把/api路径代理到后端地址。这个方案不需要额外处理 CORS,因为同源了。
Nginx 配置关键点:
server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; # 前端 dist 目录 index index.html; try_files $uri $uri/ /index.html; # 路由模式为 history 时必须有 } location /api/ { proxy_pass http://localhost:8080/; # 后端地址 } }注意try_files这一行,Vue 路由如果用的是 history 模式,刷新二级页面时会 404,加上它才能回退到 index.html。
5. 常见报错与排查经验:几年间踩过的高频问题清单
5.1 后端常见异常与解决思路
开发过程中,后端报错其实很集中。我把最高频的几类整理一下,先帮大家判断方向:
数据库连接失败
大概率是 MySQL 服务没启动,或者账号密码、URL 配置错误。注意 MySQL 8 的驱动应使用com.mysql.cj.jdbc.Driver,并且 URL 中要带serverTimezone=Asia/Shanghai,否则报时区错误。时区问题在配置里加上就没了,别在代码里绕。
Spring Security 拦截了所有请求导致 401
两种可能:一是过滤器链配置里没有放行登录接口,二是前端请求头没有正确携带 token。排查时先看请求头里有没有Authorization字段,再看 SecurityConfig 里的permitAll路径配置是否正确。
MyBatis 查询字段映射为 null
这是字段命名不一致导致的。数据库字段用下划线material_name,实体类属性用驼峰materialName,需要在配置里开启驼峰转换:
mybatis-plus: configuration: map-underscore-to-camel-case: true前端跨域报错
如果前后端分开启动(不同端口),需要在后端配置跨域过滤器,或者前端用代理解决。一般不推荐直接把 CORS 全部放开,至少限制一下允许来源,虽然是毕设项目,但写习惯了以后工作也用得上。
5.2 前端高频问题与调整技巧
前端报错最经典的问题是页面白屏和路由跳转失效。页面白屏,先打开浏览器控制台看报错,最常见的有三种:请求接口失败导致数据未定义、组件引入路径错误、Element UI 样式没引入。其中“数据未定义”最常见,比如接口返回的字段名和你页面里用的字段名不一致,数据绑定到不存在的属性上,控制台就会报 undefined。
还有一个容易忽略的问题:菜单权限和路由不匹配。如果前端只是把菜单隐藏了,但路由还能直接访问,那么用户手输 URL 照样能进入管理页面。更合适的做法是后端在返回用户信息时带上权限标识,前端做动态路由注册,或者至少在后端接口层面二次校验。
表格时间格式显示成时间戳或者显示不完整,也很常见。后端返回的时间字段加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")注解即可,前端也可以再通过dayjs格式化一次,双保险。
5.3 环境配置类问题:JDK、Maven 依赖、Node 版本
环境问题虽然不“高级”,但非常影响开发效率。我建议在交付文档里直接写清楚:
- JDK 版本统一用 1.8 或 11,不要用高版本却配了旧依赖。
- Maven 如果下载依赖太慢,配置阿里云镜像。
- Node 版本注意兼容性,Vue 2 项目建议 Node 14~16,Vue 3 + Vite 建议 Node 16 以上。
- 数据库导入脚本后,记得核对一下表是否完整,字段是否一致,特别是如果做过表结构改动,脚本要和实体类保持一致。
这些问题都不难,但第一次遇到会比较懵。把环境版本固定下来,大部分问题都能避免。
5.4 常见问题速查表:快速定位直接解决
| 问题表现 | 可能原因 | 解决方案 |
|---|---|---|
| 控制台报 401 且不弹窗 | 前端未携带 token 或 token 过期 | 检查请求拦截器;清除本地缓存重新登录 |
| 数据库中文乱码 | 连接字符集未指定 | URL 加characterEncoding=utf8,表结构用 utf8mb4 |
| 首次启动时接口返回 500 | 初始化数据不完整或字段缺失 | 核对 SQL 脚本,检查实体类和数据库字段对应关系 |
| 前端安装依赖报错提示 node-sass 不兼容 | Node 版本和 node-sass 不匹配 | 替换为 sass(dart-sass)或降低 Node 版本 |
| refresh 后页面 404 | 前端路由为 history 模式 | Nginx 配置 try_files 回退到 index.html |
| 打包后接口地址变了 | 环境变量文件未正确读取 | 修改.env.production里的VUE_APP_BASE_API为后端真实地址 |
这张表我是按真实场景整理的,基本覆盖了毕设从开发到部署全过程中最容易卡住的环节。
6. 接口文档设计规范:从接口命名到字段约束的完整作品
6.1 接口文档应该包含哪些内容
很多同学把接口文档理解成“在代码里加个注释”,这是不够的。一个合格的接口文档,至少要包含以下信息:
- 接口名称:比如“物资入库接口”。
- 请求URL与请求方式:明确是 POST
/api/material/stock-in。 - 请求参数:参数名、类型、是否必填、具体含义。
- 返回结果:返回结构示例,每个字段的含义。
- 错误码:该接口可能返回的特殊错误码及含义。
这些信息里,最容易忽略的是返回结果的字段说明。前端开发时最纠结的往往是接口返回的数据结构不够稳定,后端文档写清了,前端和字段对齐就很顺。
6.2 统一响应结构的约定与错误码规划
我在 3.5 节已经提到了统一返回结构,这里再补充一下错误码规划的建议。错误码按区间划分更清晰:
- 1000 ~ 1999:通用错误(参数校验失败、操作失败)
- 2000 ~ 2999:认证授权错误(token过期、无权限)
- 3000 ~ 3999:业务错误(库存不足、物资不存在、单据状态异常)
比如库存不足的提示,如果后端返回 3001,前端就能在拦截器里针对性地做处理。这套规划虽然是毕设,但养成的习惯和真实企业规范是一致的。
6.3 前后端接口联调实战记录:一份物资出库单的完整请求链路
我拿“部门用户创建出库申请单,管理员审批通过,库存扣减”这条主链路,完整串一次前后端交互过程,方便大家落实。
第一步,部门用户登录。前端调用POST /api/auth/login,参数为{ "username": "dept_user", "password": "123456" }。后端返回结构:
{ "code": 200, "message": "登录成功", "data": { "token": "eyJhbGciOiJIUzI1NiIs...", "userInfo": { "id": 3, "username": "dept_user", "nickname": "后勤保障部-张三", "role": "USER" } } }第二步,前端带上 token 调用POST /api/stock-out/apply,提交参数:
{ "materialId": 5, "applyQuantity": 200, "reason": "一线保障需要补充医用口罩" }后端校验库存(当前库存 1500,申请 200,充足),生成单号为 OUT20250114002 的单据,状态为“待审批”,返回申请成功。
第三步,管理员账号登录系统,在待审批列表看到这张单子。点击“通过”,前端调用POST /api/stock-out/approve,参数为{ "id": 2, "approveResult": "PASS", "approveRemark": "同意" }。
第四步,后端在事务里执行:更新单据状态为“已通过”,同时执行UPDATE material_info SET stock_quantity = stock_quantity - 200 WHERE id = 5。然后返回最新库存数量 1300 给前端。
第五步,前端刷新后,物资列表里的库存显示更新为 1300,该单据状态变为“已通过”。
这一条链路走通,整个系统的核心业务就通了。入库流程就是反向操作,把出库时的“扣减库存”改成“增加库存”,其他逻辑类似。
7. 项目亮点的包装:答辩时如何把“会做”讲成“做得好”
7.1 技术亮点怎么体现:三层校验、事务控制、审计日志
很多同学答辩的问题是“只会演示,不会讲设计”。老师问“你这个项目有什么亮点”,答“页面好看”“功能齐全”是远远不够的。我建议准备几个拿得出手的技术细节:
- 数据校验分三层:前端表单校验负责体验(比如非空、正整数),后端 DTO 注解校验负责接口健壮性(比如
@NotNull、@Min),业务层校验负责业务规则(比如库存是否充足)。三层校验的逻辑讲清楚,老师一听就觉得你有工程意识。 - 事务控制:入库、出库这些涉及库存变动的操作,务必在事务里完成,并且讲清楚为什么要加
FOR UPDATE锁,以及什么情况下会出现并发超卖。这个问题几乎是每年必问。 - 审计日志:所有关键单据,记录了“谁在什么时间做了什么操作”。这个设计思路能体现你对管理系统的理解,不只是在写增删改查。
- 预警机制:定时任务 + 阈值判断,把被动查询变成主动提醒,这种思路本身就是系统设计的加分项。
7.2 演示过程中容易翻车的四个细节
根据我的经验,有几个环节是演示时最容易出问题,也是老师最容易盯上的。
第一,登录态过期。如果演示时间比较长,登录可能中途过期,点击操作时突然跳回登录页。建议演示前把 token 过期时间临时调长或者重新登录一次,避免尴尬。
第二,数据不一致。演示时如果先前的操作已经改了库存,临时又去点一个旧页面的数据,展示的库存可能对不上。演示前最好先把数据库数据恢复成初始状态,也就是重新导入一次 SQL 脚本,让数据“干净”地展示。
第三,网络异常。本地开发时接口地址如果是localhost,演示环境换了一台机器,接口就全挂了。建议提前把接口地址改成相对路径或可配置项,避免移植机器后连环报错。
第四,不知道演示脚本。很多人演示时想到哪里点哪里,一会儿点这个菜单,一会儿点那个菜单,老师看完一脸茫然。我建议提前准备一个简单的演示脚本,按“角色登录 → 建物资 → 入库 → 申领 → 审批 → 看报表 → 看预警”这条主线串起来,逻辑清晰,故事完整。
7.3 让项目“多一点工程味”的三个小动作
如果时间和精力允许,我建议在项目里加一两个小功能,让整体的工程完整感明显提升,同时这些功能的代码量并不高。
- 操作日志:Spring AOP 切面记录用户操作,包括操作人、操作方法、参数、耗时、执行结果。这个小功能把系统从“能用”拉到了“规范”。
- 数据字典:把物资分类、审批状态、单位等常量抽成数据字典表,代码里不写死状态值,前端下拉选项从接口获取。优点是好维护、代码干净,但会增加一定的开发量。如果时间不够,可以用枚举类替代,同样达到效果。
- 软删除:删除物资、删除单据时,不是物理删除,而是逻辑删除,即加一个
deleted字段标记。这个细节虽然简单,但体现的是数据安全观念,很能加分。
8. 写在最后:我的个人体会与复盘
做这类系统,我的个人体会是:不要为了堆功能而堆功能,而要围绕一条核心业务线,把代码写干净、流程打通、边界理顺。很多同学喜欢一开始就追求“什么都能做”,结果每个模块都是半成品。反过来,你把“申领物资”这条主线做扎实,让登录、角色、入库、出库、库存、统计首尾呼应,整个系统的完整度立刻就出来了。
再分享一个小技巧:写代码之前,先花两小时把后端接口文档写完,再让前端照着文档开发,后端照着文档实现。这个习惯能在开发阶段减少大量沟通成本。我后来自己带项目、带毕设,凡是这么做的基本都开发得比较顺利。
如果你正在动手做这个项目,遇到解决不了的问题,可以按文章里的排查表先对照一下。还是没思路的话,把报错信息和日志贴出来,比一句“报错了”更有利于定位问题。如果你已经做完了,再回头看看自己的表结构设计里有没有流水表,权限模型里有没有灰度漏洞,这些细节往往决定了你的毕设是“合格的”还是“优秀的”。