首页 / 资讯中心 / 文章详情

基于Spring Boot的智能药箱系统:Java毕设全栈开发实战

基于Spring Boot的智能药箱系统:Java毕设全栈开发实战 ★ FEATURED ARTICLE
每年到毕业设计的高峰期总有人跑来问“Java毕设做什么方向不容易烂大街还容易出效果”我一般不建议去碰那些纯增删改查的管理系统——没有技术亮点答辩三两句话就问到底没什么可讲的。但如果把“管理系统”和“智能场景”结合起来效果会明显不一样。这次要说的是基于Spring Boot的智能药箱系统它表面上是药品信息管理平台本质上是一套业务链完整、技术栈主流的JavaWeb应用覆盖了用户认证、药品管理、服药计划、定时提醒、用药记录、统计分析、家庭共享等核心环节用来做毕业设计非常合适。这篇内容我会把整个系统从需求分析、技术选型、数据库设计、核心功能实现、前端联调、部署运行到最后答辩准备完整过一遍。正在准备Java毕设的同学或者想用Spring Boot练手全栈开发的初学者都能从中拿到可以直接参考的方案和代码思路。1. 项目需求分析与功能模块拆解1.1 智能药箱到底在解决什么问题智能药箱这个词听起来像物联网硬件但作为Java毕业设计课题它更多是落在软件层面把家庭里的用药行为管理起来解决“吃什么药、什么时候吃、还剩多少、是否过期、谁没吃”这些靠人脑很难长期记清楚的问题。拆开看核心业务场景有三类第一类子女给家里老人建一个家庭药箱把老人的慢性病药品录入系统设置每天的服药计划老人按时收到提醒第二类独居年轻人自己管理常用药比如感冒药、消炎药、过敏药系统记录库存和保质期快过期时主动提示第三类结合智能硬件扩展比如药盒传感器、摄像头识别做联动但作为毕设先把软件链路打通就足够撑起整个项目了。这个选题的优势在于业务闭环完整从用户注册到药品管理再到提醒、打卡、统计数据之间环环相扣不是孤立的CRUD。而且答辩时的“故事线”非常清晰——目标用户是谁、痛点是什么、系统怎么解决、效果如何衡量都能讲得有理有据。1.2 功能模块清单梳理一个合格的智能药箱系统至少需要拆出如下几个模块用户模块注册、登录、个人信息修改、密码管理。药品管理模块药品信息的增删改查字段包括药品名称、通用名、剂型、规格、生产厂家、分类、库存数量、生产日期、有效期、批号、药品描述。服药计划模块为某个家庭成员创建服药计划绑定药品、设置每次剂量、每日提醒时间和用药周期。提醒模块系统定时扫描到点需要服药的计划生成站内消息提醒可扩展邮件或短信通知。用药记录模块用户点击“已服药”完成打卡系统记录实际服药时间支持补录和漏服标记。统计分析模块统计按时服药率、漏服次数、近30天用药趋势、药品库存分布、临期药品列表。家庭管理模块用户可创建家庭组、邀请成员加入家庭成员间共享药箱数据一个家庭可设置多个被监护对象。每个模块都不复杂但串联起来就是一个完整的业务系统。做毕设的时候千万不要只做前四个模块统计和家庭共享恰恰是拉开项目档次的加分项。1.3 为什么选 Spring Boot 作为核心框架现在的Java毕设项目Spring Boot基本上是事实标准。原因很实际同学之间遇到问题方便交流网上资料多答辩老师也认可。技术上Spring Boot通过自动配置大大减少了配置文件量配合Maven的起步依赖一个可运行的Web服务几分钟就能拉起来。对比几条路老的SSH组合Spring Struts Hibernate已经基本淡出企业使用场景再选它没有实际意义纯Servlet JSP又太底层开发效率低而且在简历上没有优势Spring Boot MyBatis-Plus是目前中小型项目里最常见的一套组合学习曲线平缓开发效率高。选它不只是为了做毕设省事更是因为你毕业之后进入企业开发这一套东西依然通用。2. 系统架构与核心设计思路2.1 技术栈全景与选型逻辑这个项目推荐采用前后端分离架构整体技术栈如下后端Spring Boot 2.7.xSpring MVCMyBatis-PlusJWT认证Druid连接池LombokHutool工具集。前端Vue 3 Element Plus Axios ECharts如果不想上前后端分离也可以用Thymeleaf Bootstrap部署更简单但答辩时技术分略低。数据库MySQL 8.0。定时任务Spring自带的Scheduled注解简单场景完全够用。前后端分离的意义不只是“技术新”更重要的是把项目结构讲清楚。前端只负责渲染和交互后端只提供JSON接口数据通过Restful API通信。答辩时可以从“接口设计”“跨域处理”“鉴权机制”三个角度展开都是现成的技术话题。有人可能会纠结缓存要不要用Redis我的建议是如果只是毕设先不用。缓存是优化层面的东西系统数据量不够大时加了反而增加复杂度。但可以把代码里预留缓存扩展点比如在接口实现里写注释说“此处可引入Redis缓存热点药品数据”答辩时主动提一句老师会觉得你想过架构演进。2.2 数据库设计核心思路数据库设计是毕设答辩最容易深挖的地方表结构设计得好整个项目就稳了。核心表至少有这些用户表userCREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT 密码BCrypt加密, nickname varchar(50) DEFAULT NULL COMMENT 昵称, phone varchar(20) DEFAULT NULL COMMENT 手机号, role tinyint(4) DEFAULT 1 COMMENT 角色1-普通用户2-家庭管理员, status tinyint(4) DEFAULT 1 COMMENT 状态1-正常0-禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;药品表drugCREATE TABLE drug ( id bigint(20) NOT NULL AUTO_INCREMENT, drug_name varchar(255) NOT NULL COMMENT 药品名称, generic_name varchar(255) DEFAULT NULL COMMENT 通用名, dosage_form varchar(50) DEFAULT NULL COMMENT 剂型, specification varchar(100) DEFAULT NULL COMMENT 规格, manufacturer varchar(255) DEFAULT NULL COMMENT 生产厂家, category varchar(50) DEFAULT NULL COMMENT 分类, stock int(11) DEFAULT 0 COMMENT 库存数量, production_date date DEFAULT NULL COMMENT 生产日期, expiry_date date DEFAULT NULL COMMENT 有效期至, batch_no varchar(100) DEFAULT NULL COMMENT 批号, description text COMMENT 药品描述, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_expiry_date (expiry_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT药品表;给药计划表medication_plan和用药记录表medication_record也要重点设计计划表关联用户、药品、被监护人和用药时间记录表保存每次提醒对应的打卡情况一个计划可以产生多条记录是一对多关系。注意给记录表的plan_id和时间字段加索引统计按时率的时候性能会差别很多。设计表的时候有几点经验第一status这类状态字段不要用字符串用tinyint加注释简洁又高效第二凡是经常查询过滤的字段比如expiry_date、plan_id一定要建索引第三所有时间字段统一用datetime或date不要混用后面序列化才不会出幺蛾子。2.3 角色权限模型怎么落地智能药箱系统的权限不需要做得很重但也不能没有。推荐“简单角色控制”方案用户在user表里用role字段区分普通用户和家庭管理员管理员可以管理整个家庭组的药品和成员普通用户只能操作自己的数据和被授权的用药计划。登录成功后系统签发JWT令牌令牌里带上userId和role后端拦截器统一解析Token并校验权限。管理员接口校验时从Token里取出role判断即可。这样一个不依赖Shiro或Spring Security的轻量权限模型代码量少逻辑透明答辩时三句话就能讲清楚。3. 核心功能模块的实现细节3.1 注册登录与JWT认证注册登录是整个系统的基础也是最容易出问题的地方。密码存储必须加密推荐用BCrypt不要自己写MD5加盐这种方案BCrypt本身就是行业标准做法。JWT的实现步骤很清晰用户登录成功服务端生成Token返回给前端前端每次请求在请求头带上Authorization: Bearer token后端写一个拦截器对所有需要登录的接口校验Token有效性解析出用户信息后放入请求上下文。JwtUtil工具类的核心代码示例public class JwtUtil { private static final String SECRET your-256-bit-secret; private static final long EXPIRE_TIME 24 * 60 * 60 * 1000L; // 24小时 public static String createToken(Long userId, String username, Integer role) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static Claims parseToken(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } }这里有几个坑要注意SECRET不能写得太短太短在HS256算法下会直接抛异常过期时间设置1到3天比较合适太短导致用户频繁登录太长又有安全问题Token生成以后密码修改或账号禁用这类场景Token仍然有效所以管理员的禁用操作要和Token失效联动。3.2 药品库存与保质期管理药品模块是最典型的增删改查但要在细节上做出亮点。新增药品时要把药盒上的有效期、批号、生产厂家完整录入列表页提供多条件组合查询支持按名称模糊搜索、按分类筛选、按临期状态筛选详情页展示药品说明书信息。库存扣减不能做成盲减。推荐的做法是在创建服药计划时绑定药品数量每次打卡后自动扣减该计划对应的库存同时保留手动盘点功能实际清点药箱时修正库存值。每次扣减前检查库存是否充足不足时返回提示“该药品库存不足请先补录”。临期药品功能需要写一个定时任务每天凌晨扫描drug表把有效期在30天内的药品筛选出来写入提醒表并在前端首页展示。这里用Scheduled注解就能实现Scheduled(cron 0 0 1 * * ?) // 每天凌晨1点执行 public void scanExpiringDrugs() { LocalDate threshold LocalDate.now().plusDays(30); ListDrug expiringList drugMapper.selectList( new LambdaQueryWrapperDrug() .le(Drug::getExpiryDate, threshold) .ge(Drug::getExpiryDate, LocalDate.now())); // 批量写入提醒记录 }关键操作cron表达式必须自己会写答辩时老师很可能问“这个定时任务多久跑一次、怎么控制并发”。一个基本逻辑是定时任务里查询出的数据要加状态判断防止同一计划被重复提醒任务执行时间选在凌晨不影响正常业务。3.3 服药提醒的定时任务机制服药提醒是智能药箱系统的灵魂功能也是答辩时最出彩的点。实现思路定时任务每隔几分钟扫描一次服药计划表找出当前时间处于提醒时间窗口内、且当日尚未生成提醒记录的未完成计划为每条计划生成一条提醒消息。提醒消息写入message表前端页面轮询或使用WebSocket推送新消息。定时任务核心逻辑Scheduled(cron 0 */5 * * * ?) // 每5分钟扫描一次 public void scanMedicationRemind() { String nowTime LocalTime.now().format(DateTimeFormatter.ofPattern(HH:mm)); ListMedicationPlan duePlans planMapper.selectDuePlans(nowTime); for (MedicationPlan plan : duePlans) { boolean alreadyReminded remindRecordMapper.existsToday(plan.getId(), LocalDate.now()); if (alreadyReminded) { continue; } // 生成提醒记录 Message message new Message(); message.setUserId(plan.getUserId()); message.setPlanId(plan.getId()); message.setContent(您有新的服药提醒 plan.getDrugName() plan.getDosage() ); messageMapper.insert(message); } }这里有个细节提醒时间要按照“分钟”维度匹配查询时把当前时间转换成HH:mm字符串和计划表中的remind_time直接比较。如果计划跨天比如晚上10点提醒第二天早上补提醒需要额外处理但不建议做得太复杂保证基本打卡链路即可。除了站内消息可以预留邮件接口。Spring Boot里用JavaMailSender发送邮件很简单准备一个工具类在生成站内消息的同时触发邮件发送。答辩时提一句“当前使用站内消息可以低成本扩展邮件或短信渠道”就能体现出工程化的思维。3.4 用药记录与漏服统计用户收到提醒后进入“我的用药任务”页面点击“确认服药”系统记录真实服药时间。如果用户忘记打卡系统将该计划标记为漏服。打卡逻辑很简单但统计逻辑要花点心思。按时率计算的SQL思路如下把每条用药记录的实际服用时间和计划提醒时间对比误差在30分钟以内记为“按时”超过则记为“延迟”。按时率的SQL示例SELECT plan_id, COUNT(*) AS total_times, SUM(CASE WHEN actual_taken_time DATE_ADD(remind_time, INTERVAL 30 MINUTE) THEN 1 ELSE 0 END ) AS on_time_times FROM medication_record GROUP BY plan_id;这段SQL在答辩时非常有分量因为考察了数据处理能力。实践的时候要注意DATE_ADD函数中remind_time字段类型是time还是datetime如果只有time类型需要注意跨日场景建议在记录表同时冗余一个remind_time字段避免复杂的JOIN。3.5 数据可视化统计图表光有数据没有可视化系统会显得很单薄。统计模块推荐做四个图表近30天每日服药次数柱状图、按时率环形图、药品分类占比饼图、临期药品列表。后端聚合接口返回结构定义成一个DTO例如{ dates: [2025-05-01, 2025-05-02], records: [12, 9], onTimeRate: 0.86, categoryData: [ {name: 慢性病用药, value: 34}, {name: 常备药, value: 17} ] }前端拿到数据后直接用ECharts渲染。做图表最省力的方式是前端一次性请求统计数据接口后端用多个查询组装好数据后返回。不要在前端搞多次请求再拼接既慢又难维护。另外图表时间范围建议做近7天/近30天切换这虽然增加了后端工作量但很能体现细节。4. 前端页面设计与接口联调经验4.1 页面结构如何规划采用Vue 3 Element Plus时页面规划如下登录页账号密码登录表单登录成功后跳转首页。注册页用户名、昵称、手机号、密码、确认密码。首页看板欢迎信息今日提醒数量、待办用药计划、临期药品提示下方放统计图表。我的药品页药品列表支持搜索、筛选、新增、编辑、删除点击详情查看完整信息。药箱管理页家庭成员的药箱视图管理员可查看全部成员药品。服药计划页创建计划选择家庭成员、选择药品、填写剂量、提醒时间、起止日期、计划列表、启停计划。用药记录页按日期查看打卡记录可以补录打卡。家庭管理页创建家庭组、生成邀请码、成员列表、移除成员。页面数量不算少但对Vue来说增量开发成本很低。关键是把前端项目结构组织好router路由、api模块、views目录、components目录分开源码看起来才会像企业项目。4.2 API接口规范与前后端联调细节接口统一使用/api前缀返回格式统一如下{ code: 200, message: success, data: { } }核心接口清单功能接口路径说明用户登录POST /api/auth/login返回token和用户信息注册POST /api/auth/register支持普通用户注册药品分页GET /api/drug/page分页参数查询条件新增药品POST /api/drug/save管理员权限计划列表GET /api/plan/list按用户和状态过滤保存计划POST /api/plan/save创建或更新打卡记录POST /api/record/take提交实际服药时间统计概览GET /api/stats/overview首页图表数据家庭组成员GET /api/family/members家庭角色管理前后端分离最常遇到的坑是跨域。开发环境直接用Vue的代理解决不需要后端配CORS。在vue.config.js里加module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }生产环境部署时如果用Nginx同样配置一个location /api/的反向代理即可。跨域问题解决之后整个联调过程的体验会顺畅非常多。5. 开发实测常见问题与排查技巧5.1 数据库连接失败的经典错误在本地跑项目时最容易遇到的就是数据库连接失败控制台报错大概长这样Cannot create PoolableConnectionFactory (Communications link failure)先别慌按顺序排查MySQL服务启动了吗连接端口是3306吗密码正确吗数据库名存在吗多数人卡在最后两个问题上。我在实际开发中遇到的频率最高的是URL参数配置问题比如没加时区参数报错或者useSSL参数不对。推荐直接复制下面这段配置spring: datasource: url: jdbc:mysql://localhost:3306/smart_medicine_box?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4 username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver这段配置里同时兼容了SSL和时区问题数据库版本是MySQL 8.0的话基本不会出幺蛾子。5.2 日期格式序列化总是报错LocalDateTime在Spring Boot默认序列化下会返回一大串数组格式前端没法直接用。最简单的解决方案是全局配置Jackson格式在application.yml里添加spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8但这只对java.util.Date生效LocalDateTime还需要配合JavaTimeModule并在字段上手动加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。建议写一个全局的Jackson配置类一次性解决避免每个DTO字段都加注解。这类问题在毕设联调阶段特别常见早配置早省心。5.3 拦截器把登录接口拦截了怎么办Interceptor认证的配置里最经典的问题是登录和注册接口还没拿到Token就被拦截导致前端调用登录接口直接401。解决方法是注册拦截器时明确放行路径registry.addInterceptor(new JwtInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/api/auth/login, /api/auth/register, /error);如果用了Swagger或者Knife4j做接口文档也要把/doc.html、/swagger-ui/**、/webjars/**等路径放行。不然打开文档页面也是空白的。写拦截器之前先想清楚哪些是公开接口哪些是受保护接口开发时能省很多事。5.4 前端请求接口一直401或404后端接口跑通了前端却老是401不外乎三种原因Token没带上、Token过期、Interceptor路径写错。Token没带上检查axios请求拦截器axios.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer token; } return config; });Token过期检查JwtUtil里的过期时间以及前端统一处理401的响应拦截器弹出一条“登录状态已过期请重新登录”。拦截器路径写错则要看excludePathPatterns和addPathPatterns的匹配规则静态资源的路径也经常被误拦截。5.5 统计数据图表加载不出来图表页面空白时优先打开浏览器开发者工具看Network面板通常能看到某个统计接口报500。原因一般是后端聚合查询里有个别表join不上或者某个字段为空导致前端渲染报错。我的排查习惯是先用Postman单独调接口确认返回的JSON结构和预期一致再从前端代码里看数据取用路径是否匹配。后端接口返回时尽量把为null的字段置为0或空数组别给前端留坑。还有一个小经验ECharts渲染之前最好先判断data不为空空数据时显示“暂无数据”避免控制台一片红。6. 调试运行与部署上线经验6.1 本地启动一步步走拿到项目源码后第一次跑通按下面步骤严格执行就不会卡壳确认本机环境JDK 8或11、Maven 3.6、MySQL 8.0、Node.js 14前端项目需要。创建数据库执行项目提供的sql脚本初始化表结构和测试数据。修改application.yml中的数据源配置和JWT密钥。启动后端IDEA里直接Run启动类看到“Started Application”表示成功。启动前端进入前端目录执行npm install再执行npm run dev浏览器访问localhost端口即可。第一次跑项目不要急着改业务代码先保证数据能通、页面能开再动手改造。6.2 打包部署到服务器的流程毕设如果需要演示给老师看线上版本可以考虑部署到云服务器。后端打包用Maven命令mvn clean package -Dmaven.test.skiptrue打包完成后在target目录生成一个jar包上传到服务器后执行nohup java -jar smart-medicine-box.jar --spring.profiles.activeprod 如果服务器内存只有2G建议加上JVM参数限制堆内存避免OOM。数据量不大的系统128M初始堆就足够跑演示了。前端项目先npm run build生成dist目录再用Nginx指向dist目录同时把/api反向代理到后端端口。部署中最容易漏的是防火墙配置服务器安全组和本地防火墙都要放开后端端口和前端端口不然外部永远访问不到。6.3 文档整理与演示要点毕设文档建议包含需求分析、可行性分析、系统设计架构图、功能结构图、ER图、表结构说明、核心功能实现、系统测试、总结展望。正文不需要太长但图表一定要多老师翻起来轻松。答辩演示时准备一个“演示脚本”按顺序走登录页 → 首页看板 → 新增药品 → 创建服药计划 → 手动触发一次定时任务 → 展示提醒消息 → 打卡记录 → 统计图表 → 家庭管理。每一个动作对应一句讲解词思路清晰不慌乱。7. 答辩常问问题与扩展方向7.1 高频答辩问题怎么答答辩时老师问的问题集中在几个点上为什么选Spring Boot答简化配置、快速起步、内置Tomcat、生态成熟适合快速构建Web应用。JWT相对Session的优势答无状态、天然支持分布式扩展适合前后端分离项目。定时任务会不会重复提醒答记录表有每日唯一性判断同一计划同一天只生成一次提醒。库存扣减会不会扣成负数答扣减前校验库存不足时抛出业务异常并回滚事务。安全方面做了什么答密码BCrypt加密、JWT鉴权、参数校验、自定义异常全局处理。回答的核心逻辑是“我用到了什么解决什么问题有没有想到边界情况”。不需要背定义把代码里实实在在做的内容讲清楚老师基本都会满意。7.2 性价比高的扩展方向如果时间充裕可以往下面几个方向扩展Redis缓存把热门的药品查询结果和Token会话放入Redis讲解分布式场景下的优化思路。WebSocket实时提醒把轮询改造成服务端主动推送这个功能很能体现技术深度。扫码录入药品对接条码识别SDK前端扫码自动填充药品信息减少手动录入成本。智能药盒硬件联动通过嵌入式开发板上报药盒重量后端解析数据判断是否取药。这项改动较大有硬件基础的同学可以考虑。这些方向不一定要全部实现挑一个做成亮点就够了。关键是让老师觉得你有独立思考和扩展能力这是毕设答辩拿高分的核心。我个人的体会是毕设选智能药箱系统真正选中的是“一个能讲清楚完整业务闭环的Web应用”。它不炫技、不堆框架但每一层都有实实在在的业务逻辑支撑。答辩的时候老师问技术细节你天天在写这些代码心里完全不慌。如果你正准备做这个课题我的建议是先把数据库表设计想明白再把提醒定时任务跑通剩下的事情会顺畅非常多。最后分享一个小细节演示之前一定准备好一批真实感强的测试数据有近几个月的历史打卡记录、有临期药品、有不同分类的药品数据越丰富系统看起来越完整老师的第一印象就会好很多。
阅读完成 · 觉得有帮助?
咨询建站