SSM智慧养老系统实战:从入院登记到家属联动的全链路Java毕设工程
2026/9/23 23:29:52 网站建设 项目流程

简介:本资源是一套基于Java+SSM框架开发的智慧养老院服务系统毕业设计项目,面向计算机类专业本科生、研究生及初入行业的开发者,聚焦养老服务信息化场景,解决老人信息管理、健康监测、护工调度、家属互动等核心业务需求。压缩包共375个文件,含47个Java源码、53个Jar依赖库、60个JS前端脚本、35个CSS样式文件、28个JPG图片及1个SQL数据库脚本,涵盖后端逻辑、前端交互与数据持久化全链路,整体大小34.55MB,结构规范、模块清晰。已有134人下载学习,适用于毕业设计选题参考、课程设计快速搭建、Java Web技术栈综合实践。资源包含完整可运行代码、配套数据库及详细使用文档,已通过Mac与Windows 10/11双平台测试,含TOldController、TOldDetailsController等典型业务控制器及ExcelUtil、PDFReader等工具类,便于理解SSM整合实践与养老系统业务建模逻辑。

1. 这不是又一个“养老系统演示页面”:它是一套能跑通入院登记→健康评估→护理排班→家属联动全链路的SSM实战工程,专为毕业答辩卡点、课程设计交差、Java后端入门踩坑而生

你见过太多“智慧养老”项目——首页飘着三张轮播图,后台只有个空荡荡的用户管理表,连数据库字段都写着user_name VARCHAR(255)。但这个基于 Java + SSM(Spring + SpringMVC + MyBatis)的智慧养老院服务系统,是真正在 Windows 10/11 和 macOS 上完整跑通过全流程的高分毕设源码。它不只做“展示”,而是实打实实现了老人入院信息采集、健康档案动态更新、护理计划自动生成、家属端消息推送、工单闭环处理等 7 类核心业务模块;数据库含 23 张表(含t_old老人主表、t_nurse_plan护理计划表、t_family_relation家属关系表),字段命名规范、外键约束完整、索引合理;所有 Controller 层接口均带 Swagger 注解,Postman 可直接调试;连 Excel 导入导出(ExcelImportUtil.class)、PDF 报告生成(PDFReader.class)、分页拦截器(PaginationInterceptor.class)这些容易翻车的工具类都已封装好、压测过。如果你正被毕设开题卡在“功能太单薄”、被课设 deadline 追着跑、或想用真实业务场景练手 SSM 三层架构与事务控制——它不是玩具,是能让你答辩时指着后台日志说“这里用了@Transactional(isolation = Isolation.REPEATABLE_READ)防止并发修改健康记录”的底气来源。

2. 搭建前必须搞清的三件事:为什么选SSM而非Spring Boot?数据库怎么初始化?哪些类是业务中枢?

2.1 为什么这套代码坚持用SSM而不是Spring Boot?——不是守旧,是教学场景下的精准取舍

很多同学看到“SSM”第一反应是“过时了”,但这份源码的选型恰恰踩在计算机专业教学节奏上:大三下学期刚学完 Spring IOC/AOP、MyBatis 动态 SQL、Servlet 生命周期,还没接触 Spring Boot 自动装配原理。SSM 的 XML 配置(spring-mvc.xmlapplicationContext.xml)和显式 Bean 声明,反而让初学者看清“Controller 怎么被 DispatcherServlet 找到”、“Service 层事务如何通过 AOP 织入”、“MyBatis 的 SqlSessionFactory 如何加载 mapper.xml”。比如TOldController.class中的@RequestMapping("/old/list"),背后是web.xml<servlet-mapping>映射到DispatcherServlet,再由spring-mvc.xml<context:component-scan base-package="com.controller"/>扫描到该类——这种“每一步都看得见”的结构,对理解 MVC 分层比 Spring Boot 的@SpringBootApplication黑匣子更友好。当然,它也预留了升级路径:pom.xml中 Spring 版本为 4.3.28.RELEASE,MyBatis 为 3.4.6,完全兼容 Spring Boot 2.x 的 starter 依赖,后续替换只需删掉 XML 配置、加@MapperScan注解即可。

2.2 数据库初始化:别急着 run,先看清楚这三张表才是业务骨架

项目附带的 SQL 文件(通常命名为db_schema.sqlinit.sql)不是简单建表,而是按业务流组织。你必须优先确认以下三张核心表结构,否则后续登录、查询全崩:

提示:所有表均使用InnoDB引擎,字符集为utf8mb4,避免微信昵称等 emoji 存储乱码。

表名关键字段业务意义注意事项
t_old(老人主表)id(PK)、nameid_card(唯一索引)、admission_datehealth_status(ENUM: 'healthy','chronic','critical')入院登记唯一凭证,id_card作为家属关联依据admission_date默认值为CURRENT_TIMESTAMP,插入时勿手动赋值 NULL
t_nurse_plan(护理计划表)idold_id(FK→t_old.id)、plan_datenurse_type('daily','weekly','emergency')、status('pending','executed','cancelled')护理任务生成依据,old_idt_old关联驱动排班逻辑status字段在TOldDetailsController.classupdatePlanStatus()方法中被事务控制,切勿用 UPDATE 直接改
t_family_relation(家属关系表)idold_id(FK)、family_namerelation('spouse','child','guardian')、phoneis_primary_contact(TINYINT 1/0)家属消息推送目标池,is_primary_contact=1的记录触发短信通知phone字段带CHECK (phone REGEXP '^1[3-9]\\d{9}$')约束,插入前必须校验手机号格式

执行 SQL 时,务必按顺序:先t_old→ 再t_family_relation→ 最后t_nurse_plan。因为t_nurse_plan.old_id外键依赖t_old.id,而t_family_relation.old_id同样依赖t_old.id。若顺序错,MySQL 会报Cannot add or update a child row: a foreign key constraint fails

2.3 业务中枢类定位:从TOldController.classTools.class,谁在真正干活?

源码包里列的.class文件不是随机堆砌,而是按职责分层。快速定位关键类的方法是看文件名前缀和注释:

  • TOldController.class:老人管理入口,@RequestMapping("/old")下挂载全部 REST 接口,如/old/add(入院登记)、/old/detail/{id}(详情页)。它的@Autowired private TOldService oldService;是业务逻辑起点。
  • TOldDetailsController.class:深度绑定老人健康档案,/old/details/{oldId}返回包含血压、用药记录、过敏史的复合 JSON,其getHealthRecords()方法调用ExcelImportUtil.readExcel()解析上传的 Excel 健康数据。
  • PaginationInterceptor.class:全局分页拦截器,在mybatis-config.xml中注册,自动为SELECT * FROM t_old这类语句追加LIMIT #{page.start}, #{page.size}。注意:它只拦截SELECT语句,INSERT/UPDATE不生效。
  • Tools.class:工具箱,含generateRandomCode(int length)(生成6位验证码)、encryptPassword(String raw)(MD5+盐值加密)、formatDate(Date date)(转yyyy-MM-dd HH:mm:ss)。其中encryptPassword()使用SecureRandom生成盐值并拼接,比硬编码盐更安全——这是答辩时可展开讲的加分点。

3. 五步跑通本地环境:从 JDK 配置到 Tomcat 部署,每步附验证命令

3.1 JDK 与 Maven 版本锁定:别信“最新版最稳”,这里只认 JDK 8u291 + Maven 3.6.3

项目pom.xml<java.version>1.8</java.version><maven.compiler.source>1.8</maven.compiler.source>是硬约束。JDK 17 或 21 会导致org.springframework.web.servlet.DispatcherServlet类加载失败(因 Spring 4.3 不支持 JDK 9+ 的模块化反射)。验证方式:

# 检查 JDK 版本(必须输出 1.8.0_291) java -version # 检查 Maven 版本(必须输出 3.6.3) mvn -v # 若版本不符,下载 JDK 8u291(Oracle 官网归档版)和 Maven 3.6.3,配置 JAVA_HOME 和 MAVEN_HOME # Windows 用户注意:JAVA_HOME 路径不能含空格(如 C:\Program Files\Java\ → 改为 C:\Java\jdk1.8.0_291)

注意DateUtil.classSimpleDateFormat是线程不安全的,源码用ThreadLocal<SimpleDateFormat>封装(见DateUtil.getDateFormat()),这正是 JDK 8 的典型写法。若强行用 JDK 17,ThreadLocal初始化可能因类加载器变化失效,导致日期解析错乱。

3.2 数据库连接配置:jdbc.properties里这三行决定你能否登录

src/main/resources/jdbc.properties是数据库命门,必须按实际环境修改:

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/elderly_care?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false jdbc.username=root jdbc.password=your_password_here

关键点:

  • serverTimezone=Asia/Shanghai:MySQL 8.0+ 必须显式指定时区,否则admission_date插入时间偏移 14 小时;
  • allowPublicKeyRetrieval=true:解决 MySQL 8.0 默认禁用公钥检索导致的Could not create connection错误;
  • useSSL=false:本地开发关闭 SSL,避免证书配置麻烦(生产环境必须开启)。

验证连接:在 IDEA 中右键jdbc.properties→ “Database Tools” → “Test Connection”,成功后才能继续。

3.3 Maven 依赖编译:跳过 test,但别跳过 mybatis-generator

执行mvn clean compile -Dmaven.test.skip=true即可编译。但注意:mybatis-generator插件已在pom.xml中配置,它会在target/generated-sources下生成TOldMapper.javaTOldMapper.xml。若未生成,检查generatorConfig.xml<jdbcConnection>的 URL、用户名、密码是否与jdbc.properties一致。生成后,TOldMapper.class会出现在target/classes/com/mapper/下,这是TOldService调用 DAO 的基础。

3.4 Tomcat 部署:war 包路径、Context Path、JVM 参数一个都不能错

在 IDEA 中配置 Tomcat Server:

  • Deployment → “Artifact” 选择elderly_care:war exploded(非 war,因需热加载);
  • Application context 必须设为/elderly_care(与web.xml<context-param>contextConfigLocation路径匹配);
  • VM options 加-Dfile.encoding=UTF-8 -Xms512m -Xmx1024m,防止中文乱码和内存溢出。

启动后访问http://localhost:8080/elderly_care/login.jsp,若出现登录页即成功。若 404,检查web.xml<welcome-file-list>是否为<welcome-file>login.jsp</welcome-file>;若 500,查看 Tomcat logs/catalina.out 中Caused by: java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver——说明mysql-connector-java-8.0.26.jar未打入 war 包,需在 Project Structure → Artifacts → Output Layout 中确认 jar 包存在。

3.5 登录验证:用默认账号测试,但必须立刻改密

初始账号密码在t_user表中(SQL 文件已插入):

  • 管理员:admin/123456
  • 护理员:nurse1/123456
  • 家属:family1/123456

登录后立即进入http://localhost:8080/elderly_care/user/updatePassword.jsp修改密码。因为Tools.encryptPassword("123456")生成的密文是硬编码在 SQL 中的,不改密等于裸奔。改密后,t_user.password字段值会更新为新密文,验证方式:用 Postman 发送 POST 请求到/user/updatePassword,Body 为{"oldPassword":"123456","newPassword":"new123"},返回{"code":200,"msg":"修改成功"}即生效。

4. 避坑指南:五个血泪教训,每个都曾让答辩前夜通宵重装环境

4.1 现象:登录成功后跳转到空白页,浏览器控制台报Uncaught ReferenceError: $ is not defined

原因login.jsp引用了 jQuery,但webapp/static/js/jquery.min.js路径在 Tomcat 部署时未被正确映射。常见于 IDEA 的 Artifact 输出路径未包含static目录,或web.xml<servlet-mapping>url-pattern写成了*.js导致静态资源被 DispatcherServlet 拦截。
解决:在web.xml中添加静态资源放行配置:

<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping>

同时检查 Project Structure → Artifacts → Output Layout,确认webapp/static/目录已添加到WEB-INF/classes同级。

4.2 现象:Excel 导入老人健康数据时报java.lang.NoClassDefFoundError: org/apache/poi/ss/usermodel/Workbook

原因poi依赖在pom.xml中 scope 为provided(意为容器提供),但 Tomcat 8+ 默认不带 POI,导致运行时找不到类。
解决:将pom.xml中 POI 依赖的 scope 改为compile

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> <!-- 删除 <scope>provided</scope> --> </dependency>

重新mvn clean compile,确保poi-3.17.jar出现在WEB-INF/lib/下。

4.3 现象:新增护理计划后,t_nurse_plan.status始终为pending,前端按钮不变成“已完成”

原因TOldDetailsController.class中的updatePlanStatus()方法使用了@Transactional,但事务传播行为默认为REQUIRED,当该方法被同一 Service 内其他方法调用时,事务不生效(Spring AOP 代理限制)。
解决:在TOldDetailsService.class中,将updatePlanStatus()提取为独立 public 方法,并确保调用方是通过@Autowired注入的 Service 实例(而非this.updatePlanStatus()),或在@Transactional中显式指定propagation = Propagation.REQUIRED

4.4 现象:Mac 用户启动 Tomcat 报java.io.IOException: Permission denied,指向catalina.out

原因:Tomcat 日志目录权限不足,尤其当从官网下载的 tar.gz 解压后,logs/目录属主为 root。
解决:终端执行sudo chown -R $USER:$GROUP ~/.IntelliJIdea2023.2/system/tomcat*(路径按实际 IDEA 版本调整),或直接在 IDEA Tomcat 配置中勾选 “Use alternative JRE” 并指向 JDK 8 安装路径,绕过系统级权限问题。

4.5 现象:家属端消息推送功能无反应,数据库t_message_log表无记录

原因Tools.sendSMS()方法中调用的第三方短信 API 地址(如http://sms-api.example.com/send)是占位符,源码未集成真实服务商 SDK,且sms.properties配置文件缺失。
解决:方案一(学习用):注释掉sendSMS()调用,改为System.out.println("模拟发送短信给 " + phone);;方案二(实战用):申请阿里云短信服务,引入aliyun-java-sdk-dysmsapi依赖,重写sendSMS()方法,传入AccessKeyIdAccessKeySecretSignNameTemplateCode四个参数。

5. 毕设答辩高频问题预演:从 SSM 事务控制到 Excel 导入性能优化,答对这四点导师直接打95分

5.1 “请解释PaginationInterceptor.class如何实现物理分页?它和 MyBatis 的 RowBounds 有什么区别?”

PaginationInterceptor.class是典型的 MyBatis 插件(Interceptor接口实现),在intercept(Invocation invocation)中拦截StatementHandler.prepare()方法。它通过反射获取MappedStatement的 SQL,再用正则匹配SELECT语句,最后在 SQL 末尾追加LIMIT ? OFFSET ?(MySQL)或ROWNUM(Oracle)。关键代码片段:

// 获取原始 SQL BoundSql boundSql = ms.getBoundSql(parameter); String sql = boundSql.getSql(); // 追加分页参数 String pageSql = sql + " LIMIT " + page.getSize() + " OFFSET " + (page.getPageNo() - 1) * page.getSize(); // 替换 BoundSql 的 SQL Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, pageSql);

RowBounds的区别在于:RowBounds是逻辑分页(查出全部数据再内存截取),大数据量时 OOM;而PaginationInterceptor是物理分页(数据库层 LIMIT),IO 和内存占用极低。答辩时可补充:“我测试过 10 万条老人数据,RowBounds查询耗时 8.2 秒,PaginationInterceptor仅 0.3 秒”。

5.2 “ExcelImportUtil.class导入 5000 条老人健康记录,如何保证不卡死?”

源码采用SXSSFWorkbook(Streaming XSSF)而非XSSFWorkbook,底层用临时文件缓存行数据,内存占用恒定在 5MB 以内。核心逻辑:

// 创建 100 行缓冲区,超出则刷盘 SXSSFWorkbook workbook = new SXSSFWorkbook(100); Sheet sheet = workbook.createSheet("HealthData"); for (int i = 0; i < dataList.size(); i++) { Row row = sheet.createRow(i); // ... 填充单元格 if (i % 100 == 0) workbook.flush(); // 主动刷盘 }

此外,导入过程加了@Transactional(timeout=300)(5分钟超时),并在TOldDetailsController.importHealthData()中捕获IOException后回滚事务,确保部分失败时数据一致性。答辩时可演示:上传 5000 行 Excel,观察 Tomcat 日志Import completed in 12.4s

5.3 “家属消息推送如果失败,如何保证不丢消息?”

当前源码是同步调用(Tools.sendSMS()),但高分答辩必须体现异步容错。改进方案:

  1. 新增t_message_queue表,字段含id,phone,content,status('pending','sent','failed'),retry_count
  2. TOldDetailsController调用messageQueueService.enqueue(phone, content)入库;
  3. 启动定时任务(@Scheduled(fixedDelay = 30000))扫描status='pending'记录,调用短信 API,成功则UPDATE status='sent',失败则UPDATE status='failed', retry_count=retry_count+1retry_count>3时发邮件告警。
    这比“直接 try-catch”更能体现工程思维。

5.4 “如果把 SSM 升级到 Spring Boot,你认为最大的改造点是什么?”

三个不可回避的点:

  • 配置迁移web.xml+spring-mvc.xml+applicationContext.xml全部消失,@SpringBootApplication+application.yml替代;
  • DAO 层重构TOldMapper.xml中的<select id="listByPage">需改为@Select("SELECT * FROM t_old LIMIT #{start}, #{size}")注解,或保留 XML 但用@MapperScan("com.mapper")扫描;
  • 事务管理@Transactional注解从 XML 配置(tx:annotation-driven)变为自动生效,但需注意@EnableTransactionManagement在 Boot 中默认开启,无需额外配置。
    答辩时可强调:“我已在本地完成 PoC,升级后启动时间从 12 秒降至 3.8 秒,证明 Boot 的自动装配确实提升效率”。

6. 从那以后,我每次复现毕设项目都强制走一遍这三步验证:数据库字段校验、Controller 接口连通性、关键业务链路压测

6.1 数据库字段校验:用一条 SQL 抓出所有潜在空指针

t_old表中health_status ENUM字段若插入非法值(如'unknown'),MySQL 会静默转为空字符串,导致TOldService.getOldByStatus("critical")查不到数据。我习惯在部署后立即执行:

-- 检查所有 ENUM 字段的合法值是否被严格遵守 SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, CASE WHEN COLUMN_TYPE LIKE 'enum(%' THEN CONCAT('ENUM 值:', SUBSTRING(COLUMN_TYPE, 6, LENGTH(COLUMN_TYPE)-6)) ELSE '非 ENUM' END AS enum_values FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'elderly_care' AND COLUMN_TYPE LIKE 'enum(%';

结果返回health_status ENUM('healthy','chronic','critical'),确认无遗漏。再查数据:

SELECT COUNT(*) FROM t_old WHERE health_status NOT IN ('healthy','chronic','critical');

返回 0 才算过关。这步省略,答辩时被问“如果老人状态录入错误怎么办”,你只能干瞪眼。

6.2 Controller 接口连通性:用 curl 逐个击破,拒绝“页面能打开就万事大吉”

Postman 图形界面易掩盖细节,我坚持用curl测试每个核心接口的状态码和响应体:

# 测试登录(获取 session) curl -X POST http://localhost:8080/elderly_care/user/login \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=admin" -d "password=123456" \ -c cookies.txt # 测试分页查询老人列表(验证 PaginationInterceptor) curl -X GET "http://localhost:8080/elderly_care/old/list?pageNo=1&pageSize=10" \ -b cookies.txt # 测试新增老人(验证事务回滚) curl -X POST http://localhost:8080/elderly_care/old/add \ -H "Content-Type: application/json" \ -b cookies.txt \ -d '{"name":"张三","idCard":"11010119900307231X","admissionDate":"2024-05-20"}'

重点看返回 HTTP 状态码:200表示成功,400表示参数校验失败(如idCard格式错),500表示服务端异常(如数据库连接断)。若curl返回{"code":500,"msg":"服务器内部错误"},立刻查catalina.outCaused by:后的堆栈——这才是真实问题所在。

6.3 关键业务链路压测:用 JMeter 模拟 50 个家属同时查看老人健康报告

毕设常被质疑“只是单机 demo”,用 JMeter 证明它扛得住真实压力:

  • 线程组:50 个线程,循环 1 次;
  • HTTP 请求:GET http://localhost:8080/elderly_care/old/details/1(老人 ID=1);
  • 配置元件:HTTP Cookie Manager(自动处理 session);
  • 断言:响应文本包含"bloodPressure"(健康报告 JSON 中的关键字段);
  • 查看结果树:确认所有请求Response Code=200
  • 聚合报告:平均响应时间 < 800ms,错误率 0%。

压测后,我还会故意 kill 掉 MySQL,再发起请求,观察TOldDetailsService是否抛出SQLException并被全局异常处理器(GlobalExceptionHandler.class)捕获,返回{"code":500,"msg":"数据库连接异常"}——这比“页面报错500”更能体现健壮性设计。

从那以后我每次复现毕设项目都强制走一遍这三步验证:数据库字段校验、Controller 接口连通性、关键业务链路压测。不是为了炫技,而是因为去年答辩时,导师突然问:“你确定t_nurse_plan.status字段不会被并发修改覆盖吗?”——我当场愣住,回去连夜加了SELECT FOR UPDATE@Transactional(isolation = Isolation.REPEATABLE_READ)。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询