智能药箱?听名字像是物联网硬件项目,实际做出来才明白,它更像是一个“药品领域的进销存+提醒服务”的管理系统。作为计算机毕业设计选题,Spring Boot+Vue这套前后端分离的技术组合,加上药品录入、库存预警、用药提醒这些贴近真实场景的业务模块,既能展示扎实的CRUD基本功,又能体现定时任务、权限控制、可视化统计这类加分项,是性价比很高的一个方向。这篇内容我把整个项目的拆解思路、核心实现和调试中容易踩的坑完整梳理一遍,方便正在做类似选题的同学直接参考。
1. 项目整体思路与选题定位
1.1 这个系统到底解决什么问题
先别急着打开代码。任何一个毕业设计项目,动手之前必须把“系统给谁用、用来干嘛”想清楚。智能药箱系统表面上是管理一个物理药箱,实际上可以拆成两个层面的需求:
- 家庭个人用户层面:家里老人慢性病多,药盒里堆了一堆药,经常忘记吃、重复买、放过期。需要一个工具来记录药品信息、设置服药计划、到点提醒、库存不够时预警。
- 药品管理层面:不管是家庭小药箱还是药店/医务室,药品都有入库、出库、库存盘点、有效期管理这些流程。系统本质上是一个带业务规则的药品信息管理系统。
明白了这一点,你就能在答辩时把“为什么做这个系统”讲得很有底气——它不是简单的增删改查,而是围绕药品生命周期(入库→存储→服用/出库→补货→过期处理)做了一套完整的业务闭环。
1.2 为什么技术栈选了Spring Boot + Vue
现在的Java毕设,十有八九是Spring Boot + Vue前后端分离。这个组合确实是最稳的选择,我拆开讲讲原因:
- 后端Spring Boot:内置Tomcat,不用单独配置服务器;Starter机制让依赖管理变得极其简单;加上Spring生态自带的事务管理、AOP、定时任务支持,做药品提醒这类功能非常顺手。毕设答辩时,面试官或评委老师基本都懂这套,沟通成本低。
- 前端Vue:Vue 3(或者Vue 2)配合Element Plus组件库,做后台管理界面效率极高。表格、表单、弹窗、选项卡都现成的,一个药品管理页面半天就能拼出来。Vue的双向绑定和数据响应式机制,做搜索筛选、联动选择这种交互特别方便。
- 前后端分离的优势:接口文档定好,前后端可以并行开发;部署时也可以分开,前端扔Nginx,后端打包成Jar跑起来就行。还有个很实在的好处——答辩演示的时候,前端挂了可以单独修,后端崩了也不影响静态页面讲解,这是我在实践中发现的小优势。
1.3 项目的功能边界怎么定
我见过很多同学一上来就想做“大而全”,结果卡死在某个模块上。以智能药箱系统为例,我建议功能边界这样划分(按MVP优先级排序):
- 第一优先级(必做):用户登录注册、药品信息CRUD、入库/出库记录、库存列表、有效期展示。
- 第二优先级(体现含金量):库存预警(低于阈值变色提醒)、过期药品自动识别(通过@Scheduled定时任务扫库)、服药提醒(每天定时推送消息)、用药记录打卡。
- 第三优先级(锦上添花):数据统计(ECharts展示药品分类占比、库存消耗趋势)、家庭成员多用户权限、药品二维码扫码识别、导出Excel报表。
如果时间紧张,第一优先级做完就能毕业,第二梯队的定时任务和预警做好就是“亮点功能”,第三梯队属于加分项,做不做得看个人进度。清醒地评估自己能力,比盲目堆功能强得多。
2. 核心业务模块与数据交互流程拆解
2.1 药品管理模块的底层逻辑
药品是整个系统的核心实体,但它不是孤立的一张表。我拆解一下围绕药品的数据流,你照着这个思路去设计后端接口,逻辑会非常清晰。
药品信息表(drug_info)建议字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| drug_name | varchar | 药品名称 |
| batch_no | varchar | 批号,用于区分不同批次 |
| spec | varchar | 规格,比如“0.5g*24片” |
| manufacturer | varchar | 生产厂家 |
| production_date | date | 生产日期 |
| expire_date | date | 有效期至,核心字段 |
| stock_quantity | int | 当前库存数量 |
| min_stock | int | 库存预警阈值 |
| category | varchar | 药品分类(心脑血管/感冒/肠胃等) |
| create_time | datetime | 创建时间 |
注意这里的批号字段非常关键。同一款药品,不同批次的过期时间可能不一样,所以药品信息表要允许同一名称多条记录,不能简单用“药品名”做唯一约束。很多初学者在这里踩坑:用药品名关联库存,结果不同批次的药全混在一起了。
业务操作主要分三类:
- 入库:新增一条药品记录(或已有批次上增加库存),同时写一条入库记录明细(入库数量、来源、操作人)。
- 出库:减少对应批次的库存数量,写一条出库记录(服用/取药/销毁等类型)。
- 预警:查询时自动计算当前库存是否 <= min_stock,以及当前日期是否接近expire_date(比如30天内),前端展示预警标识。
2.2 用药提醒模块的设计思路
这是智能药箱系统区别于普通库存管理系统的地方。它本质上是一个定时任务 + 提醒记录的组合。
设计思路是:用户可以给自己或者家庭成员建立“用药计划”,例如每天早上8点吃降压药1片、晚上7点吃阿司匹林1片。每条计划包含:
- 关联的药品ID
- 用药时间(时分)
- 每次剂量
- 用药人(家庭成员)
- 是否启用
后端用一个@Scheduled(cron = "0 0/30 * * * ?")的任务,每30分钟扫描一次用药计划表,把“当前时间到了但今日还没记录”的计划捞出来,生成一条提醒记录(可以是站内消息形式,演示时在首页消息列表展示;也可以预留对接微信/短信通知的扩展点)。
这里有个常见误区:有人想在数据库里存“下次提醒时间”,然后定时任务去查“时间到了没”。但用户调整服药时间后,这个字段很难同步处理。更优雅的做法是通过Cron表达式或执行规则推算,比如计划表存的是“HH:mm:ss”的用药时间,每次跑批时用当前时间比对,加上“今日是否已用药”的标记来判断要不要提醒,这样用户改时间后下一次跑批自动生效,不需要额外维护状态字段。
2.3 多角色权限划分与数据隔离
大多数系统会有两个角色:管理员和普通用户。
- 普通用户:只能操作自己名下的药箱/药品数据,看自己的提醒和记录。
- 管理员:可以查看全部用户列表、药品总览、统计数据,甚至停用某个违规账号。
在Spring Boot里实现RBAC权限模型,最简单的做法就是:
- 用户表加一个role字段(1管理员,0普通用户)。
- 登录接口返回JWT令牌时,把角色信息放进Payload里。
- 后端写一个拦截器或AOP注解(如@RequireAdmin),在Controller方法上标注需要的权限,拦截器从token解析角色,没权限就返回403。
前端Vue侧配合做路由守卫:根据角色动态生成菜单,没有管理员权限的账号,登录后根本看不到管理入口。这块做好了,答辩时讲“系统安全性设计”就有实质内容可讲,而不是干巴巴的一句“我们用了JWT”。
2.4 数据统计模块怎么做得好看又实用
毕设答辩时,评委老师大概率会问“系统有没有数据分析相关功能”。统计模块不需要复杂算法,但要用图表把业务跑起来的数据展示出来。我建议做三个维度的统计:
- 药品分类占比:饼状图,展示心脑血管类、感冒类、肠胃类等各占库存总量的比例,用ECharts的一行配置就能实现。
- 库存消耗趋势:折线图,统计近30天每天出库了多少件药品,观察用药高峰期。
- 近效期药品TOP10:表格+进度条,把距离过期时间最短的10条药品列出来,用进度条组件展示剩余天数占比。
统计数据的SQL要写对,聚合类查询建议放在后端Service层做,前端只负责调接口拿数据组装图表。ECharts的配置项非常多,但常用的其实就那么几个:title、legend、xAxis、yAxis、series,复制官网示例改改数据就行,不需要背配置。
3. 核心技术实现与关键参数选型
3.1 后端项目结构与架构分层
我自己的习惯是严格按照controller → service → serviceImpl → mapper → entity分层,这个结构老套但最清晰。针对这个系统,我推荐这样组织包结构:
com.example.medbox ├── common // 通用返回结果、常量、异常处理 │ ├── Result.java │ ├── ResultCode.java │ └── GlobalExceptionHandler.java ├── config // 配置类:拦截器、跨域、定时任务 ├── controller // 接口层 │ ├── DrugController.java │ ├── StockRecordController.java │ ├── RemindPlanController.java │ └── UserController.java ├── entity // 数据库实体类 ├── mapper // MyBatis-Plus的Mapper接口 ├── service // 业务接口 ├── service.impl // 业务实现 └── utils // JWT工具类、日期工具类这样的好处是:Controller层只接收参数和返回结果,所有业务判断都写在Service里,后期扩展也好找地方。比如药品入库和库存扣减必须在一个事务里,直接在ServiceImpl方法上标@Transactional即可,不要散落在Controller层各个位置。
3.2 数据库表设计要点与关联关系
核心表建议设计5~6张,别贪多:
| 表名 | 作用 | 关键关联 |
|---|---|---|
| sys_user | 用户表 | 与药品、计划关联 |
| drug_info | 药品信息表 | 关联用户ID |
| stock_record | 出入库记录表 | 关联药品ID和用户ID |
| remind_plan | 服药计划表 | 关联药品ID |
| remind_log | 提醒/打卡记录表 | 关联计划ID |
| operation_log | 操作日志表(可选) | 记录关键操作 |
注意一个细节:stock_record出入库记录表建议设计为双向通用,用一个type字段区分1入库、2出库。这样查询历史流水时只需要一条SQL按时间排序,不用维护两张结构几乎一样的表。
关联关系上,所有业务表都加上user_id字段,用用户的ID做数据隔离查询条件。不要用Java代码去过滤数据,而是在SQL的条件里直接写WHERE user_id = #{userId},既避免全表扫描,也防止越权看到别人的数据。
3.3 JWT登录认证的核心配置
JWT(JSON Web Token)做登录认证是Java毕设的标配。原理不复杂,关键是签名密钥和过期时间的设置。
在application.yml中:
jwt: secret: your-secret-key-change-it expire: 86400000 # 24小时,单位毫秒生成的Token放在HTTP请求头的Authorization字段中,格式为Bearer xxxxxx。
后端拦截器里解析Token:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 String uri = request.getRequestURI(); if (uri.contains("/user/login") || uri.contains("/user/register")) { return true; } String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { // 返回未认证状态 response.setStatus(401); return false; } try { Claims claims = JwtUtil.parseToken(token.substring(7)); request.setAttribute("userId", claims.get("userId")); return true; } catch (Exception e) { response.setStatus(401); return false; } }这里有一个实践心得:JWT密钥千万不要明文写死在代码里,起码放到配置文件中。曾经见过一个项目把密钥硬编码在工具类里,结果所有用这套模板的同学生成的Token都一样,换个人登录还能用别人的身份,这是严重的安全漏洞,答辩的时候被问到就非常被动。
3.4 跨域处理与前端联调配置
前后端分离开发时,前端跑在http://localhost:5173(Vite默认端口),后端跑在http://localhost:8080,两个端口不一样,浏览器会拦截跨域请求。解决办法有三种:
- 后端加CORS全局配置(推荐):写一个配置类实现WebMvcConfigurer,在addCorsMappings方法里放行所有来源。
- 前端Vite代理:在
vite.config.js里配置proxy,把/api开头的请求代理到后端。 - Nginx反向代理:部署时统一入口,效果最好。
开发阶段用第一种最快,部署阶段用第三种,这个顺序能帮你省下大量排查问题的精力。
跨域配置参考:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }需要注意:如果开启了allowCredentials(true),前端axios请求就不能只设请求头,还要在axios实例中配置withCredentials: true,否则会丢失Cookie相关的认证信息。
3.5 定时任务实现过期与库存预警
Spring Boot的定时任务用起来非常无脑,在主启动类加@EnableScheduling,然后在某个Service方法上写@Scheduled注解即可。
@Component public class DrugWarnTask { @Autowired private DrugInfoService drugInfoService; // 每天凌晨2点执行一次:检查所有药品是否临期 @Scheduled(cron = "0 0 2 * * ?") public void checkExpireDate() { List<DrugInfo> allDrugs = drugInfoService.list(); for (DrugInfo drug : allDrugs) { long days = calculateDays(drug.getExpireDate()); if (days <= 30 && days >= 0) { // 记录预警信息 drugInfoService.saveWarnMessage(drug); } } } }Cron表达式的格式是秒 分 时 日 月 星期,写错是常有的事。“0 0 2 * * ?”代表每天凌晨2点执行,看不太懂的时候上工具网站验证一下,比反复测试效率高得多。
优化细节:如果药品数据量大,全表扫描效率低。可以考虑在SQL层面直接筛选,SELECT * FROM drug_info WHERE expire_date BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 30 DAY),让数据库帮你过滤,Java代码只管拿结果处理。
4. 实操过程与核心环节落地
4.1 开发环境版本组合与初始化
这个系统我用过的稳定版本组合如下,你直接参照,避免踩版本坑:
| 工具 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8 或 11 | 更推荐JDK 11,Spring Boot 2.7完全支持 |
| Maven | 3.8+ | 不用最新版,3.8最稳 |
| Spring Boot | 2.7.x | 不要直接上Spring Boot 3,有些依赖不兼容 |
| MyBatis-Plus | 3.5.x | 配合代码生成器能省一半时间 |
| MySQL | 8.0+ | 注意时区配置 |
| Node.js | 16+ | Vite 4需要 |
| Vue | 3.x + Vite | 或者Vue 2 + Element UI,都行 |
| Element Plus | 最新版 | 组件丰富 |
环境初始化有一个值得注意的点:MySQL连接串一定要加时区参数。jdbc:mysql://localhost:3306/medbox?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai,不加serverTimezone大概率报错,尤其在MySQL 8.0版本下。
4.2 后端快速生成与核心接口开发
MyBatis-Plus配合代码生成器,可以把单表CRUD代码自动生成出来,但这只适合标准增删改查,业务接口还是得手写。
以“药品入库”为例,完整时序是:
- 前端提交入库表单,字段包括药品信息+入库数量。
- 后端DrugController接收DrugDTO。
- Service判断该药品是否已存在(按名称+批号)。
- 存在:在原库存上增加数量。
- 不存在:创建新药品记录,初始库存为入库数量。
- 写入stock_record表,type为1(入库),记录数量和操作时间。
- 返回成功结果。
这段逻辑花10分钟就能写完,但要注意并发场景:如果两个请求同时入库同一个批次,库存更新可能出错。解决方案很简单,SQL层面用原子操作:UPDATE drug_info SET stock_quantity = stock_quantity + #{num} WHERE id = #{id},而不是先查再set,这个细节在答辩时提出来很加分。
4.3 前端页面搭建与关键组件实现
前端页面我建议用Vue 3 + Vite + Element Plus + Axios + Vue Router + Pinia(或Vuex)这套组合。核心页面也就5个:登录页、首页数据看板、药品管理页、出入库记录页、提醒计划页(加一个用户管理页用于管理员)。
药品管理页是重头戏。列表展示用el-table,顶部搜索区放药品名称输入框和分类下拉框,右侧放“新增”“入库”“出库”按钮。
一个很实用的技巧:药品列表的状态列用el-tag组件展示。库存 < min_stock时显示“库存不足”红色标签;expire_date距今 < 30天时显示“临期”黄色标签;正常则显示“正常”绿色标签。这种视觉反馈是评委最容易记住的功能点。
Axios请求封装的代码参考:
import axios from 'axios' import { ElMessage } from 'element-plus' import router from '../router' const request = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:自动附带JWT request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) // 响应拦截器:统一处理401跳转登录 request.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { ElMessage.error(res.msg || '请求失败') return Promise.reject(new Error(res.msg)) } return res }, error => { if (error.response?.status === 401) { ElMessage.error('登录已过期,请重新登录') router.push('/login') } return Promise.reject(error) } ) export default request把拦截器做好的好处是:后续所有接口调用的代码都简洁干净,不需要每个页面都处理Token和错误状态。这个属于“一次配置,长期受益”的环节。
4.4 药品过期自动预警与首页看板联动
预警功能要做成“首页直接能看见”,不要藏在二级页面里。我的建议是:首页放一个“温馨提示卡片”,优先展示三种预警信息:
- 未来30天内到期的药品(名称+剩余天数)。
- 库存低于阈值的药品(名称+当前库存量)。
- 今日待服药计划(来自提醒任务生成的数据)。
后端提供一个聚合接口,比如/drug/count/status,分别返回三类数据的数量,前端卡片展示数字;再提供一个列表接口,点开卡片能看到详细清单。
这个聚合查询可以用一条SQL带条件统计,也可以用多个Service方法分别查询组成一个Map返回,看数据量决定。毕设场景数据量不大,直接多次查询然后组装即可,简单直接。
4.5 部署演示前的自检清单
毕设演示翻车往往不是功能没做出来,而是环境问题。我整理了一份演示前自检清单:
- [ ] MySQL服务已启动,数据库脚本执行成功。
- [ ] 后端Jar包重新打包(
mvn clean package -DskipTests),本地端口没被占用。 - [ ] 前端
npm run build产物能正常访问,接口地址配置正确。 - [ ] 测试账号能登录,且预置了至少10条药品数据(其中包含几条临期和库存不足的,方便演示预警效果)。
- [ ] 定时任务触发的提醒消息,确保今天已经由跑批生成了一条。
第4条特别重要。演示系统之前,一定要先准备一套“刚好能展示亮点”的测试数据。如果数据库里全是正常的药品,预警功能演示时冷场了,真的很尴尬。
5. 常见问题与排查技巧实录
5.1 接口报跨域错误
现象:前端页面打不开接口,浏览器控制台提示No 'Access-Control-Allow-Origin' header is present。
排查思路:
- 确认是否走了后端CORS配置,Spring Boot 2.7的CORS配置写法和我上面给的代码基本一致。
- 确认请求方式是否为OPTIONS预检,预检请求被拦截器拦截也会报跨域错误,需要在拦截器中对OPTIONS请求直接放行。
- 如果使用的是前端代理,确认
vite.config.js的proxy配置已经生效,修改后要重启前端服务。
解法:优先用前端代理解决开发阶段的跨域,生产部署用Nginx统一入口,后端CORS配置作为兜底方案。
5.2 登录成功但获取不到用户信息
现象:JWT签发成功,前端保存Token,刷新页面后接口请求401。
排查思路:这多半是Token解析或传递环节的问题。
- 先看前端请求头里的Authorization字段是否携带了Token,可以在浏览器F12的Network面板确认。
- 再看后端拦截器里
request.getHeader("Authorization")取到的值是否包含Bearer前缀大小写混淆。 - 还有一个小众问题:JWT的密钥不匹配。如果后端配置里改过jwt.secret,但前端缓存的Token还是旧密钥签发的,解析必然失败。解决方法是重新登录一次。
5.3 MyBatis-Plus分页查询失效
现象:使用MyBatis-Plus的Page对象查询,返回的总条数正确但实际列表只有前面前几条。
排查思路:缺少分页插件!MyBatis-Plus使用分页功能必须配置PaginationInnerInterceptor,否则分页逻辑不会生效。
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }很多教程里都提过要配置这个插件,但很多同学直接从网上拷代码时漏掉了这一环。排查时先看控制台有没有打印出LIMIT关键字,没有就是插件没配置上。
5.4 前端表格时间字段显示异常
现象:后端返回的时间字段正常(比如2025-06-01 12:30:00),但前端页面显示成了数字时间戳。
排查思路:这是JSON序列化格式问题。Java端LocalDateTime默认序列化成数组或时间戳,需要在application.yml里配置统一格式:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8如果用了@JsonFormat注解在某几个字段上,也要确保pattern一致,避免有的页面有格式、有的页面没格式。
5.5 定时任务不生效
现象:启动类加了@EnableScheduling,任务方法也写了@Scheduled,但到了时间就是没执行。
排查思路:
- 检查任务类是否被Spring扫描到,确保所在包在启动类扫描范围内,或者组件上加了
@Component。 - 检查Cron表达式是否写错。举个例子,“每天中午12点执行”的表达式是
0 0 12 * * ?,很多人把“秒”位漏掉了,写成0 12 * * ?,意思就变成每秒的第0秒+每分钟12秒执行,完全不是预期效果。 - 如果任务方法内部有异常被吞掉,也会出现“看起来没执行”。建议在方法内加日志输出,异常往上抛,方便定位。
5.6 生产环境部署刷新页面404
现象:前端打包后用Nginx部署,首页能打开,但直接访问某个路由页面(刷新浏览器)就404。
排查思路:Vue Router默认是history模式(地址中没有#号),需要Nginx配置try_files回退到index.html:
location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }这个问题高频率出现,调好了部署基本就稳了。如果不想自己配Nginx,也可以用hash模式,地址栏有个#号,但演示时不太好看。
5.7 代码打包后前端访问后端接口404
现象:本地开发接口都通,打包部署后用Nginx访问前端页面,接口却404。
排查思路:前端静态文件里的接口地址可能是相对路径,导致请求打到了Nginx服务器的某个路径下而没转发到后端。解决方法是:
- 前端统一把接口baseURL配置为
/prod-api这类带环境标识的路径。 - Nginx做代理,把
/prod-api开头的请求转发到http://localhost:8080。
生产环境部署的代理配置:
location /prod-api/ { proxy_pass http://localhost:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }5.8 时间不足时的优先级取舍
如果临近提交,功能还有缺口,我给你的建议优先级是:
- 保住:登录注册、药品CRUD、出入库记录(这是系统根基)。
- 保住:库存预警、临期提醒(这是智能化的体现,功能实现成本低)。
- 争取:图表统计(ECharts复制示例改数据就行)。
- 放弃:扫码识别、短信通知、多端小程序(这些需要外部依赖,开发成本和不可控因素都高,毕设阶段性价比低)。
答辩时,把已实现功能讲得逻辑自洽,远比重功能多但全是半成品有用得多。
6. 项目演示与毕业设计答辩的经验补充
6.1 演示脚本要提前走一遍
不要低估答辩当天“紧张”对操作的影响。提前写一页演示脚本,按顺序列出操作步骤:
- 用测试账号登录,展示首页预警卡片。
- 进入药品管理页,搜索某药品,展示列表和状态标签。
- 点击入库,新增一条记录,展示库存联动变化。
- 点击出库,展示库存减少。
- 进入提醒计划页,展示已生成的今日提醒。
- 切管理员账号,展示用户管理和统计图表。
照着脚本过两遍,比临场发挥了半天才发现库存数据不对要强得多。
6.2 答辩常见问题怎么应答
根据我带过的毕设经验,评委大概率会问这几类问题,提前把回答准备到位:
- “为什么选这个课题?”回答思路:人口老龄化趋势+家庭用药管理痛点+药品流通管理需求。结合具体场景说,不要空谈。
- “库存预警怎么实现的?”回答思路:先讲阈值字段设计min_stock,再讲查询时状态判断逻辑,最后补充定时任务+前端视觉反馈的组合。
- “JWT安全性和过期策略?”回答思路:讲清单密钥存储、过期时间设置、拦截器校验逻辑,顺带提一下后续可以引入Spring Security增强。
- “系统最大的难点是什么?”回答思路:不要说“没什么难点”。可以讲“不同批次药品的库存管理和有效期并发更新问题”,把自己踩过坑又解决了的过程讲清楚,这个回答比任何吹嘘都有说服力。
- “系统还有哪些可扩展的地方?”回答思路:对接语音提醒、开发微信小程序、引入低库存自动下单、对接挂号平台电子处方等,方向很多,选两三个说透即可。
6.3 说明文档与设计文档的写作建议
毕设交付除了代码,最重要的就是设计文档和说明文档。建议结构如下:
- 系统概述(背景、意义、目标)。
- 需求分析(功能需求表、用例图)。
- 系统设计(架构图、功能模块图、数据库ER图、表结构说明)。
- 核心功能实现(关键代码片段+逻辑说明)。
- 系统测试(测试用例表、测试结果截图)。
一个实用技巧:截图比文字管用。功能页面截图、数据库执行结果截图、接口测试截图,都尽力补充进去。文档的排版和完整度在评选中占的比重超乎想象,这一块花时间不亏。
6.4 个人经验总结
智能药箱这类基于Spring Boot + Vue的管理系统,做出来的同学很多,但真正拉开差距的,不在技术复杂度,而在于业务逻辑的完整性和演示效果的自洽性。把这个“药品从入库到过期”的闭环讲圆了,再把提醒和预警这两个亮点功能做扎实,最后把文档整理清爽,就是一个能拿得出手的优秀毕设项目。我在实际开发中最大的体会是:多花20%的精力把边界情况和异常流程处理好(比如重复入库、库存扣到负数、跨天提醒),在答辩和评审时的观感会远远好于一个功能很多但细节粗糙的系统。