简介:这份资源是面向高校计算机专业学生与Java初学者的一套毕业设计完整源码,主题为老年人服药提醒App的设计与实现,可用于课程设计参考、毕设选题落地或Java移动端开发练手。压缩包共299个文件,约48.17MB,以java源码、class编译文件与jar依赖为主,辅以vue前端页面、xml配置、jsp页面、sql数据库脚本及docx环境部署说明,覆盖Android组件、MySQL数据表设计、多线程与异常处理等知识点,并附有音频提醒素材与项目配置文件。目前已有222人学习下载。读者可从中获取完整的项目目录结构、数据库初始化脚本、环境搭建与部署文档,以及控制器、工具类等核心业务代码,便于理解服药计划、提醒记录等模块的实现思路,并在此基础上进行二次开发或撰写论文。适合需要一套可直接运行、结构清晰的Java毕业设计参考方案的学习者。
1. 老年人服药提醒 App 源码拆包:从 class 文件到可跑通的 Java 毕业设计
家里长辈每天要吃四五种药,降压的、降糖的、护胃的,时间点还各不相同,靠脑子记迟早出问题。这个「老年人服药提醒 App」的毕业设计源码包,解决的就是这类场景:把服药计划、提醒记录、用户管理做成一套能跑起来的 Java 项目。它适合正在做毕业设计、需要一份完整参考实现的同学,也适合想拿一个真实业务练手 Java Web + Android 的开发者。包里能看到QX_USERController.class、QX_REMINDController.class、QX_RECORDController.class这些编译产物,还有Uploader.class、PagePlugin.class、Tools.class这类工具类,说明它是一套带分页、上传、控制器分层的后端结构,不是那种只有一个 Main 方法的玩具代码。下面按「先看懂结构、再跑起来、最后避坑」的顺序拆一遍。
2. 源码结构拆解:从 class 文件反推分层与数据流
2.1 包内文件透露出的技术栈
拿到压缩包先别急着导入 IDE,先看文件清单。._components、._zz-prompt这种以._开头的文件是 macOS 压缩时产生的资源分叉文件,不是源码,可以直接忽略。真正有价值的是这几类:
QX_USERController.class、QX_REMINDController.class、QX_RECORDController.class:三个控制器,分别对应用户、提醒、服药记录三个业务模块。Uploader.class:文件上传工具,常见于头像上传或药品图片上传场景。PagePlugin.class:分页插件,说明列表查询做了分页封装。Page.class:分页实体,通常包含页码、每页条数、总记录数。Tools.class:通用工具类,一般是字符串处理、时间格式化、MD5 加密之类。ApiUser.class:可能是接口返回的用户实体,或者 API 层的用户对象。
从命名习惯看,这是一套典型的 Java Web 分层结构:Controller 层接收请求,Service 层处理业务,DAO 层操作数据库。QX_前缀大概率是项目代号,三个 Controller 对应三张核心表。medication.sql负责建表,java环境说明.docx和java环境及部署说明.zip负责告诉你环境怎么配。
提示:
.class文件是编译后的字节码,不能直接改。如果你拿到的是纯 class 没有 java 源文件,需要用 JD-GUI 或 CFR 反编译。但正常毕业设计包会同时给源码和编译产物,先确认src目录是否存在。
2.2 数据库表设计与 medication.sql 的还原
服药提醒类应用的核心表一般不超过五张。根据控制器命名和业务逻辑,可以推断出这样的表结构:
| 表名 | 作用 | 关键字段 |
|---|---|---|
| qx_user | 用户信息 | id, username, password, phone, age |
| qx_medication | 药品信息 | id, user_id, name, dosage, unit |
| qx_remind | 提醒计划 | id, user_id, medication_id, remind_time, repeat_type |
| qx_record | 服药记录 | id, user_id, remind_id, take_time, status |
| qx_relation | 家属关联 | id, elder_id, family_id |
medication.sql里通常包含建库、建表、插入测试数据三部分。执行之前先确认 MySQL 版本,项目正文写的是 MySQL 5.7+,如果你本地是 8.0,注意utf8和utf8mb4的差异,以及GROUP BY默认行为的变化。
-- 先建库,字符集用 utf8mb4 兼容 emoji 和生僻字 CREATE DATABASE medication_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE medication_db; -- 用户表:老年人本人 CREATE TABLE qx_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(30), phone VARCHAR(20), age INT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 提醒计划表:一条记录代表一个服药时间点 CREATE TABLE qx_remind ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, medication_name VARCHAR(100), dosage VARCHAR(50), remind_time TIME NOT NULL, repeat_type TINYINT DEFAULT 1 COMMENT '1每天 2每周 3单次', enabled TINYINT DEFAULT 1, FOREIGN KEY (user_id) REFERENCES qx_user(id) );建表时最容易翻车的是外键顺序。qx_remind依赖qx_user,所以必须先建qx_user。如果脚本里顺序反了,执行会报errno: 150。另外remind_time用TIME类型而不是DATETIME,因为提醒是每天重复的时间点,不需要日期部分,这个设计比存完整时间戳更合理。
2.3 控制器层的请求流转
三个 Controller 的分工很清晰。QX_USERController处理登录、注册、修改密码;QX_REMINDController处理提醒的增删改查和开关;QX_RECORDController处理服药打卡和记录查询。以提醒列表为例,典型流程是:
- 前端传
userId和page、limit参数。 - Controller 调用
PagePlugin封装分页参数。 - Service 层查询
qx_remind表,按remind_time排序。 - 返回
Page对象,包含list和total。
PagePlugin.class的存在说明分页不是手写的LIMIT offset, size,而是做了统一封装。这种写法在毕业设计里算加分项,因为分页逻辑复用性好,改每页条数不用动 SQL。
// 分页参数封装的大致逻辑,反编译后结构类似 public class Page { private int pageNum; // 当前页码,从1开始 private int pageSize; // 每页条数,默认10 private int total; // 总记录数 private List<?> list; // 当前页数据 // 计算 offset,MySQL LIMIT 从0开始 public int getOffset() { return (pageNum - 1) * pageSize; } }参数说明:pageNum前端传 1 表示第一页,pageSize建议不超过 50,老年人用的 App 列表太长反而不好翻。total由SELECT COUNT(*)查出,用来算总页数。如果total为 0,前端要显示空状态而不是空白页。
3. 环境搭建与部署:把 class 文件跑成可访问的服务
3.1 JDK、Tomcat、MySQL 的版本对齐
项目正文写的是 Java 1.8,这个必须对齐。用 JDK 11 或 17 跑老项目,常见问题是javax.servlet包找不到,因为高版本 JDK 移除了 Java EE 模块。Tomcat 建议用 8.5 或 9.0,不要用 Tomcat 10,因为 Tomcat 10 把javax.servlet改成了jakarta.servlet,包名不兼容。
安装顺序:
- 装 JDK 1.8,配置
JAVA_HOME和PATH。 - 装 MySQL 5.7,记下 root 密码。
- 解压 Tomcat 到非中文路径。
- 用 IDEA 或 Eclipse 导入项目。
# 验证 JDK 版本,必须是 1.8 java -version # 输出应为 java version "1.8.0_xxx" # 验证 MySQL 服务 mysql -u root -p # 输入密码后执行 SHOW DATABASES;如果java -version显示 11 或 17,先改环境变量。Windows 下在「系统属性 → 环境变量」里把JAVA_HOME指向 JDK 1.8 目录,Path里%JAVA_HOME%\bin放在最前面。Mac 或 Linux 用export JAVA_HOME=/path/to/jdk1.8。
3.2 导入 IDEA 与配置数据库连接
IDEA 导入时选「Import Project」,指向解压后的根目录,选「Maven」或「Eclipse」取决于项目结构。如果只有.class没有pom.xml,说明是普通 Web 项目,需要手动配 Facet。
数据库连接配置一般在db.properties或jdbc.properties里:
# 数据库连接配置 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/medication_db?useUnicode=true&characterEncoding=utf8&useSSL=false jdbc.username=root jdbc.password=你的密码参数说明:useSSL=false在 MySQL 5.7 本地连接时建议加上,否则控制台会刷警告。characterEncoding=utf8要和建库时的字符集一致,不然中文药名会乱码。com.mysql.jdbc.Driver是 5.x 的驱动类名,如果你用 8.x 的 jar 包,要改成com.mysql.cj.jdbc.Driver。
导入medication.sql:
# 在命令行执行,注意路径不要有中文 mysql -u root -p medication_db < medication.sql执行完用SHOW TABLES;确认表都建好了。如果报Unknown database,说明库没建,先执行CREATE DATABASE medication_db;。
3.3 启动 Tomcat 与接口验证
配置 Tomcat 时,Deployment 里加 Artifact,Application context 设成/medication。启动后浏览器访问http://localhost:8080/medication/,如果看到登录页或接口返回 JSON,说明部署成功。
常见验证接口:
# 测试用户登录接口,具体路径看 Controller 里的 RequestMapping curl -X POST http://localhost:8080/medication/user/login \ -d "username=test&password=123456"如果返回 404,检查web.xml里的 servlet 映射,或者 Controller 上的@RequestMapping路径。如果返回 500,看 Tomcat 日志logs/catalina.out,大概率是数据库连接失败或 SQL 语法错误。
注意:
java环境及部署说明.zip里如果有现成的 Tomcat 配置,优先用它的,因为端口号、context path 可能和代码里的硬编码路径对应。自己改容易漏掉前端请求的 baseURL。
4. 核心业务逻辑:提醒调度与服药记录的状态流转
4.1 提醒时间的存储与触发机制
服药提醒的核心是「到点触发」。毕业设计里常见的实现有两种:一种是后端定时任务扫描qx_remind表,到时间推消息;另一种是前端 App 用AlarmManager或WorkManager本地调度。从QX_REMINDController的存在看,这个项目至少做了后端管理,触发可能依赖前端轮询或推送。
remind_time存TIME类型,repeat_type区分每天、每周、单次。查询「当前需要提醒的计划」时,SQL 大致是:
-- 查询当前用户所有启用的提醒,按时间排序 SELECT r.id, r.medication_name, r.dosage, r.remind_time, r.repeat_type FROM qx_remind r WHERE r.user_id = ? AND r.enabled = 1 ORDER BY r.remind_time ASC;参数说明:user_id从登录会话里取,不要从前端传,否则会有越权风险。enabled = 1过滤掉已关闭的提醒。如果要做「今天已提醒过的不再提醒」,需要关联qx_record表查当天记录。
4.2 服药打卡与记录写入
老年人收到提醒后,点「已服药」按钮,后端往qx_record插一条记录。状态字段status一般设计为:1 已服药、2 跳过、3 未响应。打卡逻辑要注意幂等——同一个提醒同一天重复提交,不能插多条。
// 打卡接口的伪代码逻辑 public Result takeMedicine(int remindId, int userId) { // 先查今天是否已有记录 QxRecord exist = recordDao.findByRemindAndDate(remindId, userId, today); if (exist != null) { return Result.fail("今天已经打卡过了"); } QxRecord record = new QxRecord(); record.setRemindId(remindId); record.setUserId(userId); record.setTakeTime(new Date()); record.setStatus(1); recordDao.insert(record); return Result.success(); }参数说明:remindId是提醒计划的 ID,userId从会话取。findByRemindAndDate的日期比较用DATE(take_time) = CURDATE(),不要用字符串拼接,否则索引失效。如果业务允许补打卡,就把幂等判断去掉,改成更新已有记录的状态。
4.3 家属端的数据可见性
老年人自己操作 App 有门槛,所以这类项目通常有家属关联功能。qx_relation表存elder_id和family_id,家属登录后能查看老人的服药记录。查询时要注意权限校验:家属只能看关联老人的数据,不能看别人的。
-- 家属查询老人今天的服药记录 SELECT r.medication_name, r.dosage, rec.take_time, rec.status FROM qx_record rec JOIN qx_remind r ON rec.remind_id = r.id WHERE rec.user_id = ? -- 老人ID AND DATE(rec.take_time) = CURDATE() ORDER BY rec.take_time DESC;参数说明:rec.user_id是老人的 ID,不是家属的。家属 ID 用来校验qx_relation里是否存在关联关系。如果没做这层校验,任何登录用户改一下 URL 参数就能看别人的服药记录,这是毕业设计里最常见的越权漏洞。
5. 避坑与排查:class 文件、乱码、连接失败的高频问题
5.1 反编译后代码跑不起来
现象:用 JD-GUI 打开.class文件,复制出来的 Java 代码一堆语法错误,变量名变成var1、var2。
原因:编译时如果没加-g参数保留局部变量表,反编译出来的变量名就是无意义的。另外泛型和 lambda 表达式反编译后容易失真。
解决:优先找源码包里的src目录。如果只有 class,用 CFR 反编译,它对 Java 8 的 lambda 支持比 JD-GUI 好。反编译后的代码只用来理解逻辑,不要直接当源码编译。
5.2 中文乱码从数据库一路乱到页面
现象:数据库里药名显示正常,页面上是问号;或者数据库里就是乱码。
原因:字符集在三个环节可能不一致——建库时、JDBC 连接时、Tomcat 响应时。
解决:建库用utf8mb4,JDBC URL 加characterEncoding=utf8,Tomcat 的server.xml里 Connector 加URIEncoding="UTF-8"。如果已经乱码了,改字符集不会自动修复旧数据,需要重新导入medication.sql。
5.3 Tomcat 启动报 ClassNotFoundException
现象:启动时控制台刷java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。
原因:MySQL 驱动 jar 没放到WEB-INF/lib下,或者 IDEA 的 Artifact 里没包含依赖。
解决:确认mysql-connector-java-5.1.x.jar在WEB-INF/lib目录。IDEA 里检查「Project Structure → Artifacts → Output Layout」,把 Library Files 加到WEB-INF/lib下。如果用的是 Maven,检查pom.xml里依赖的 scope 是不是provided,改成默认的compile。
5.4 定时提醒不触发
现象:到了设定时间,App 没有任何反应。
原因:后端没有定时任务,或者前端轮询被系统杀掉了。
解决:先确认项目里有没有@Scheduled注解或 Quartz 配置。如果没有,说明提醒依赖前端本地通知,需要检查 Android 端的AlarmManager是否设置了setExactAndAllowWhileIdle。毕业设计里很多只做了「查看提醒列表」,没做真正的推送,这个要在答辩时提前想好说法。
5.5 分页查询 total 始终为 0
现象:列表能显示数据,但分页组件显示「共 0 条」。
原因:PagePlugin计算 total 的 SQL 没执行,或者COUNT(*)查询条件没带上。
解决:检查分页插件拦截的 SQL 是不是只替换了LIMIT,没生成 count 语句。手动执行SELECT COUNT(*) FROM qx_remind WHERE user_id = ?确认有数据。如果用的是 PageHelper,注意startPage之后必须紧跟查询语句,中间不能插别的数据库操作。
6. 二次开发与答辩加分:把提醒做成真正能用的功能
跑通之后,如果想在答辩时多拿几分,可以在这个源码基础上做两件事:一是把提醒从「列表展示」升级成「实际推送」,二是加一个用药依从性统计。
推送这块,后端可以用@Scheduled每分钟扫一次qx_remind,把到点的计划写入消息队列或直接调推送 SDK。Android 端用NotificationManager发本地通知,注意 Android 8.0 以上要建 NotificationChannel,否则通知不显示。
// 后端定时扫描的简化逻辑 @Scheduled(cron = "0 * * * * ?") // 每分钟执行 public void scanReminders() { String now = new SimpleDateFormat("HH:mm").format(new Date()); List<QxRemind> list = remindDao.findByTimeAndEnabled(now); for (QxRemind r : list) { // 检查今天是否已打卡 if (recordDao.findByRemindAndDate(r.getId(), r.getUserId(), new Date()) == null) { pushService.send(r.getUserId(), "该吃药了:" + r.getMedicationName()); } } }参数说明:cron = "0 * * * * ?"表示每分钟的第 0 秒触发。findByTimeAndEnabled的 SQL 用remind_time = ?精确匹配到分钟。如果提醒时间精确到秒,cron 要改成每秒扫,但那样数据库压力大,一般没必要。
依从性统计就是算「按时服药次数 / 总提醒次数」。在QX_RECORDController里加一个接口,按周或按月聚合:
-- 统计某用户最近7天的服药依从率 SELECT COUNT(CASE WHEN status = 1 THEN 1 END) AS taken, COUNT(*) AS total, ROUND(COUNT(CASE WHEN status = 1 THEN 1 END) / COUNT(*) * 100, 1) AS rate FROM qx_record WHERE user_id = ? AND take_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY);这个统计结果可以在家属端用图表展示,答辩时演示效果比单纯增删改查好得多。
从那以后我每次拿到这种毕业设计源码包,都先跑medication.sql建库,再改 JDBC 配置,最后才导入 IDE——顺序反了就会在 ClassNotFound 和乱码之间来回折腾。希望帮到你。
本文还有配套的精品资源,点击获取