简介:基于Spring Boot与Vue.js全栈技术开发,服务养老院日常管理场景,面向管理人员、护工及家属等不同角色的毕业设计级系统。功能覆盖老人档案登记、健康与入住信息维护、房间及床位资源调度、护理任务排班、膳食营养配制、活动娱乐组织、消息通知、费用账务管理、进出安全监控及数据统计分析等核心业务,前后端分离架构清晰,既可作为高校软工类毕业设计参考,也适用于中小养老机构信息化部署。压缩包为zip格式,整体约19.85MB,主要包含前后端Java/JavaScript源码、MySQL数据库初始化脚本以及配套项目说明文档,三类文件相互配合,便于沿着建库建表、后端接口、前端页面全链路学习。目前已有92人下载使用,完整性获得下载者认可。读者拿到资料后,可先执行数据库脚本完成初始化,再启动后端服务与前端工程,由此掌握权限校验、床位状态联动、护理工单流转、费用计算等典型业务模块的设计思路;说明文档还覆盖需求分析、接口定义与部署步骤,能为二次开发、论文撰写或答辩演示提供可直接落地的参考依据。
1. Spring Boot + Vue 养老院管理系统:是不是你要找的那份完整毕设源码
每年三四月都有人拿着"基于Spring Boot + Vue的养老院管理系统"来问我好不好做。说实话,这类题目在毕业设计里算是性价比很高的一档:功能不抽象、模块边界清楚、前后端分离的技术栈又正好踩在答辩老师熟悉的点上。这份源码包我拆过一遍,里面包含完整的后端 Spring Boot 工程、前端 Vue 工程、数据库脚本和说明文档,适合正在做毕设的学生,也适合刚学完前后端分离、想拿一个真实业务练手的人。核心业务是养老院常见的老人档案、床位分配、护理记录、健康数据、家属管理这些模块,跑通后能作为课设、毕设甚至面试项目经验。整套东西不需要 Linux 服务器,不用 Docker,在你自己的 Windows 电脑上装好 JDK、MySQL、Node 就能跑起来。下面我从技术选型讲到落地部署,再到改造这个项目去应付答辩的细节,一条线给你捋完。
2. 项目结构与技术选型:先看清它是什么,再决定要不要动它
2.1 前后端分离的目录划分与角色边界
解压这份 zip 之后,先不要急着双击 README,而是先用文件管理器过一遍顶层目录。典型的 Spring Boot + Vue 毕业设计会分成几个明确的部分,后端一个目录,前端一个目录,数据库脚本单独放置。我拿到这份资源时看到的是后端工程里面包含标准的 Maven 目录结构,src/main/java 下面有 controller、service、mapper 这些包,前端是独立的 Vue 工程,有 src/views、src/router、src/api 这些目录。这种命名方式说明它不是那种把前后端写在一起的 JSP 老项目,而是真正意义上的前后端分离。
为什么这种结构值得在答辩时讲?因为前后端分离意味着后端只提供 JSON 接口,前端负责页面渲染和数据交互,两边通过 HTTP 通信。你可以单独启动后端用 Postman 测接口,也可以单独启动前端连一个已部署的服务器。对于答辩来说,你可以在电脑上同时展示三个证据:数据库管理工具里能看到表结构和数据,IDEA 里能看到后端接口代码,浏览器里能看到 Vue 页面效果。三样东西串起来,就是一个完整的数据流转链路。养老院管理系统在这种架构下本身就适合展示"列表查询、表单提交、状态更新"这类典型操作,评委不需要多高深的技术背景也能看懂业务逻辑。
这套项目在依赖层面也符合主流毕设水平,后端基础依赖是 Spring Boot Web、MyBatis、MySQL 驱动这些,前端的基础依赖是 Vue 2、Vue Router、Axios、Element UI。如果你之前在学校做的是 SSM 单体项目,这个技术栈正好是它的自然升级版。先理解这片分层结构,再去改代码,改起来才不会找不着北。
2.2 后端代码:入口类、配置与登录拦截
拿到后端工程后,第一步要找到带 @SpringBootApplication 注解的启动类,然后顺着它往下看 resources 目录下的 application.yml(也可能是 application.properties)。Spring Boot 的启动逻辑都从入口类开始,main 方法启动内嵌 Tomcat,自动扫描当前包及其子包下的 Component。这个机制决定了你新增的 Controller、Service 必须放在启动类所在的包路径下,否则会启动成功但不加载接口,返回 404。这也是初改毕设代码时最容易翻车的地方,后面我会再详细拆解。
application.yml 里最值得关注的是三个配置块:数据源、端口、MyBatis 日志。数据源决定了后端能不能连上数据库,端口决定了前端要访问哪个地址,MyBatis 日志决定了你在控制台能不能看到 SQL 执行情况。还有一个容易忽略的配置是 Jackson 的时间格式,如果后端返回的时间字段是时间戳或者带 T 的格式,前端显示就会怪怪的,通常需要在 yml 里配置统一的格式化参数。
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/yanglao?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: "123456" driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true参数说明:url 里的 yanglao 是数据库名,必须和数据库脚本里建的库名一致;serverTimezone=Asia/Shanghai 是避免 MySQL 8.x 时区报错的关键参数;password 默认是 123456,但你自己电脑上 MySQL 是什么密码就改成什么密码。MyBatis-Plus 的 log-impl 配置成 StdOutImpl 后,控制台会打印出每条查询的 SQL 和查询条件,这对你想弄懂数据的来源非常有用。map-underscore-to-camel-case 表示数据库里的下划线字段名自动映射成 Java 的驼峰属性名,比如 bed_no 会自动变成 bedNo。
登录拦截这块,绝大多数毕设项目都是通过拦截器或者过滤器校验前端传来的 token。后端有一个拦截器类,放行登录接口,拦截其他接口,从请求头里取出 token,解析出用户信息后放到 ThreadLocal 中。这份资源里登录模块的核心就是这个拦截器。扩展时不要只改 Controller 而不动拦截器,否则你新写的接口全被拦截,前端就调不通了。
2.3 前端代码:路由表、状态管理与请求封装
前端工程结构里最值得先翻的两个文件是 src/api/request.js 和 src/router/index.js。request.js 是用 Axios 封装的一个实例,统一配置了 baseURL 和拦截器。后端接口地址是 http://localhost:8080,所以 baseURL 一般写成 /api,然后在 vue.config.js 里配置代理把 /api 转发到 8080 端口。这里有一个很多新手踩过的坑:直接把 baseURL 改成 http://localhost:8080,虽然也能用,但涉及跨域问题,浏览器会报 CORS 错误,因为前端的 dev server 默认跑在 8081 或 3000 端口,两个端口的源不一样。所以最稳妥的方案是走代理,而不是改 baseURL。
import axios from 'axios' import { Message } from 'element-ui' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 10000 }) 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.msg || '请求出错') return Promise.reject(new Error(res.msg)) } return res }, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') router.push('/login') } Message.error(error.message) return Promise.reject(error) } ) export default service参数说明:timeout 设成 10000 毫秒,接口超过 10 秒没返回就主动报错,适合在演示时避免页面长时间转圈。请求拦截器里从 localStorage 取 token 并写入 Authorization 请求头,这是后端鉴权的依据。响应拦截器的逻辑是把后端响应的 JSON 统一拆包,只看 code 是否为 200,如果后端返回 401 就清掉 token 并跳回登录页。这样前端完全不用在每个页面里重复写错误处理。
路由表的登录守卫也是关键点。Vue Router 的 beforeEach 钩子里判断当前访问的页面是否需要登录,如果没 token 就强制跳转到 /login。这张路由表同时控制了侧边栏菜单的显示内容。如果你发现页面能打开但登录后菜单缺失,多半是路由表里的 meta 权限没配对,或者后端返回的菜单树没拼对。
3. 快速跑通:数据库脚本导入与本地启动三步走
3.1 数据库脚本导入与账号密码核对
你拿到的这份资源包里的数据库脚本文件是一个 .sql 结尾的文本文件。先右键用文本编辑器打开,看前三行,确认里面有没有 CREATE DATABASE 语句。如果有,导入后会自动建库;如果没有,你得先手动建一个空库,再用 mysql 命令行把你想要的数据导入进去。实际操作中,我一般推荐用命令行导入,因为在 IDEA 的 Database 面板里导入大 SQL 文件时,字符集编码经常出问题,而且进度不直观。用命令行可以把每个报错都看在眼里,哪里断了改哪里。
mysql -uroot -p123456 < yanglao.sql逻辑说明:这条命令使用 root 用户连接本机的 MySQL,-p 后面紧跟密码(形如 123456 就写 123456,密码是 abc 就写 abc),然后通过重定向符把小括号里的 SQL 文件内容一条条执行。如果脚本里有 CREATE DATABASE,执行完你就会看到数据库;如果没有,就需要先登录 MySQL 执行 create database yanglao,再重新导入。
导入成功后,用 Navicat 或者 IDEA 的数据库面板看一眼表清单。养老院的表一般包括 admin、old_people、bed、nurse_record、health_data 这类命名。这个时候要重点核对导入的数据里有没有管理员账号,通常是一张 admin 表,里面有一条 username 为 admin 的记录,密码可能是明文也可能是 MD5 加密后的字符串。如果你看到的是一串加密值,可以先用 MD5 工具把 123456 加密后对比一下,如果一致,那前端登录页默认密码就是 123456;如果对不上,说明密码是加盐哈希处理的,需要用代码视角去看后端的登录逻辑。
3.2 后端启动细节与第一个接口验证
后端工程根目录下一般有 pom.xml,用 IDEA 打开后它会自动识别为 Maven 项目,并下载依赖。这一步在网络不好的环境里可能非常漫长,建议先用 IDEA 的 Maven 面板把 maven-home 指向你本地的仓库,不要临时用 IDEA 自带的捆绑 Maven。启动前先修改 application.yml 里的数据库连接串,把 username 和 password 换成你自己 MySQL 的账号。很多人的后端起不来,问题根本不在代码,而在于 MySQL 的密码含有特殊字符,比如密码是 Abc@123,直接写 yml 里在 yaml 语法下 @ 符号没问题,但 # 开头就要加引号,这个细节要注意。
改完配置以后,启动 main 方法,观察控制台日志。Spring Boot 启动成功的标志是 Tomcat started on port 8080,以及一行 Started Application in x.xx seconds。这个时候打开浏览器直接访问 http://localhost:8080,如果项目没有配置默认页会看到 404 或白页,这是正常的,因为后端不直接返回页面。需要用接口验证后端是否存活,可以用 IDEA 自带的 HTTP Client,写一个简单的 GET 请求,也可以直接用浏览器访问 Swagger 地址。项目里如果引入了 springfox 或 knife4j,访问 http://localhost:8080/doc.html 或 /swagger-ui/index.html 就能看到接口文档列表。
@RestController @RequestMapping("/api/user") public class HealthController { @GetMapping("/health") public Result health() { return Result.success("service is running"); } }逻辑说明:这是一个临时加的探活接口,Controller 类上的 RequestMapping 定义了接口前缀,方法上的 GetMapping 定义了子路径。访问后返回 Result 对象,这通常是把 code、msg、data 三个字段装进一个统一响应体里。Result.success 是一个静态工厂方法,用来简化返回类型的构造。这个接口可以在答辩打开后端的时候顺手调一次,证明服务正常,不需要展示标准登录页。
3.3 前端依赖安装与跨域配置
前端工程根目录下的 package.json 里写着依赖列表和启动脚本。在命令行用 cd 进入前端目录后运行 npm install 安装依赖。如果你在公司网络环境里卡在依赖安装环节,可以先用下面的命令切换到国内镜像源,再重新 install,速度会快很多。
cd yanglao-web npm config set registry https://registry.npmmirror.com npm install npm run dev逻辑说明:npm config set registry 是把自己本机的 npm 源指向阿里的 npmmirror 镜像;npm install 安装 vue、vue-router、axios、element-ui 等依赖;npm run dev 启动 Vue 开发服务器。启动成功后命令行会提示 Local 访问地址,比如 http://localhost:8081。这个时候打开浏览器访问这个地址,页面是能看到的,但登录大概率会失败,因为你还没给前端配置代理。前端工程根目录下的 vue.config.js(没有就创建一个)里的 devServer 配置项用于把 /api 开头的请求转发到后端。
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })参数说明:port 是前端访问端口,proxy 里的 key 表示匹配以 /api 开头的请求;target 是后端实际地址;changeOrigin 设为 true 会把请求头里的 Host 字段改写为 target 地址,后端就不会因为来源不对而拒绝。配置完成后需要重启 npm run dev 让代理生效。启动顺序我一般建议先数据库、再后端、最后前端,这样每一步都能确认上一个环节没问题,不至于同时启动三样东西出错了都不知道怪谁。
4. 避坑指南:这套源码启动失败,八成是这几个原因
4.1 端口被占用,后端死在启动阶段
现象:IDEA 控制台出现 Port 8080 was already in use,然后启动线程直接退出。
原因:你电脑上已经有别的进程占用了 8080 端口,可能是之前启动过一次没关掉的后端进程,也可能是本机的其他服务。
解决:在命令行执行 netstat -aon|findstr "8080",查看是哪个 PID 占用,然后在任务管理器里结束对应进程,或者干脆把 application.yml 里的 server.port 改成 8082,同时把 vue.config.js 里 proxy 配置的 target 也改成 8082。改端口是最省事的办法,因为不用猜进程是谁。
4.2 数据库连接被拒绝,提示 Access denied
现象:后端启动日志出现 Access denied for user 'root'@'localhost',或者 Communications link failure。
原因:application.yml 里的数据库密码和你本机 MySQL 不一致,或者 MySQL 服务没启动。很多人的密码不是默认的 123456,但源码里写死 123456,不改配置永远跑不起来。
解决:先用命令行 mysql -uroot -p 手动登录一遍,确认你的密码能用。改完 yml 里的 username 和 password,重启后端。还有一个隐蔽问题:如果你的 MySQL 是 8.0 版本,可能需要把驱动改成 com.mysql.cj.jdbc.Driver,而老版本 5.x 用的是 com.mysql.jdbc.Driver,这个写错也会失败。
4.3 Node 版本太高,npm install 直接报错
现象:npm install 时出现 ERESOLVE unable to resolve dependency tree,并提示 @vue/cli-service 和 webpack 的版本冲突。
原因:这种毕设项目里 Vue 2 前端用的依赖版本普遍是 2020 年前后的老版本,而 Node 18、Node 20 的依赖解析策略更严格,老版本依赖的组合不被新版 npm 认可。
解决:不要卸掉 Node 去折腾,先试 npm install --legacy-peer-deps,这个参数会跳过依赖树校验。如果还是失败,就需要装一个 Node 14 或 Node 16 的版本。装完以后 node -v 和 npm -v 确认版本,再 npm install,基本秒过。
4.4 前端页面能打开,但登录一直提示接口报错或 404
现象:浏览器控制台能看到 POST http://localhost:8081/api/login 404,或者请求被 CORS policy 拦截。
原因:vue.config.js 代理没生效,或者 baseURL 写死了 http://localhost:8080 导致跨域。还有一种可能,是你人肉改前端代码时访问了错误的接口前缀,后端实际路径是 /api/user/login,而前端请求的是 /api/login。
解决:先通过后端接口文档确认真实接口路径,再检查 vue.config.js 的 proxy 配置。重点排查 baseURL:如果代码里写了完整的域名,就去掉,改成 '/api',让代理去处理转发。改完把 dev server 重启,不用关后端,因为代理是前端这层负责转发的。
4.5 导入 SQL 脚本时报错,表建不出来或中文乱码
现象:执行某个 INSERT 语句时报错 Data too long for column,或者导入后页面显示中文全是问号。
原因:字符集不匹配。很多 .sql 脚本的编码是 UTF-8,但 MySQL 客户端的默认字符集可能是 latin1 或 gbk,中文被截断;另一个问题是数据表字段长度不够,比如某种说明字段是 varchar(50),但插入的文本超过 50 个字符。
解决:导入前先执行 SET NAMES utf8mb4; 把客户端会话的字符集设为 utf8mb4。对于字段长度不够的情况,手动把对应字段的 varchar 长度改大,或者直接去看那条数据本身是不是不完整。这个坑在毕设项目里出现频率极高,因为很多人的权限表内容是从真实的演示环境里导出来的,数据值包含地址、备注等长文本,字段长度是原作者按自己数据定的,到你这儿未必够。
5. 二次开发:把默认项目改造成你自己的毕设内容
5.1 替换系统名称、前端 Logo 与登录后首页
拿到源码后先用文本编辑器的全局替换,把代码里所有出现原项目名称的地方改成你自己的系统名。项目名称分散在 index.html 的 title 标签、Vue 配置文件里的 name 字段、登录页页头、侧边栏组件、还有后端的日志输出里。全局替换的时候注意只替换显示文本,不要动变量名和路由路径,否则编辑器会把代码逻辑里的相同字符串也换掉,引发一堆报错。
登录后的首页在 src/views 目录下,默认可能是 dashboard 或者 home 组件。这个页面在答辩时是你打开的第一个页面,建议把里面的统计卡片数据换成本系统真实的数据。统计卡片通常调用了后端 dashboard 接口,接口返回老人总人数、床位数、今日入住数这些聚合数据。如果不改逻辑,只是切换成自己的项目名,答辩演示时数据口径和数据来源都能对得上,就够用了。不要为了好看去写死页面数字,老师一问"你的数据是哪里来的"就解释不清了。
5.2 新增一个业务字段:从数据库 SQL 到前端表单全链路
很多毕设需要在原有模块上增加需求,比如老人档案里加一个"紧急联系人电话"字段。在养老院管理系统里,这个字段大概率已经存在,但你可以用同样的思路去扩展一个没有的,比如"医保卡号"。完整链路涉及四层代码:数据库表加列、后端实体类加属性、前端详情表单加输入框、列表页加列展示。
ALTER TABLE old_people ADD COLUMN insurance_no VARCHAR(30) DEFAULT NULL COMMENT '医保卡号' AFTER id_card;逻辑说明:这是一条对已有表追加字段的 SQL。AFTER id_card 是指把新字段放在 id_card 列后面,不影响原有表结构。加完字段后用 Navicat 刷新看到新列,然后去后端实体类 OldPeople 里加上 private String insuranceNo; 属性。Spring Boot 项目没有反向工程的话,MyBatis-Plus 的 BaseMapper 会自动映射这个列,不需要手写 INSERT 语句。前端表单也要加绑定,在 el-form-item 区块里新写一行 el-input。这里最关键的一步是检查新增字段是否被 Vue 双向绑定到表单数据里,如果 v-model 绑定的属性名是 insuranceNo,而数据库列名是 insurance_no,映射关系由 camelCase 配置自动转,另一侧前端页面就显示出来了。
5.3 调整登录后功能权限、菜单显示与演示数据可见性
毕设项目里通常有两种权限控制方式。第一种是前端路由按角色过滤,管理员看到所有菜单,普通员工只看到部分菜单;第二种是后端接口做权限注解判断,简单做法是用拦截器判断当前用户角色字段,比如 admin 类型为 1 才有权访问某个删除接口。这份源码的菜单结构需要去前端 router 的 meta 字段处调整,每一条路由对应一个菜单项。
如果你想在答辩演示时让功能看起来更丰满,可以往数据库里插入一些真实的演示数据。养老院系统的数据跟别的业务不一样,需要有一点人情味:老人姓名用常见的姓氏、入住时间要合理分布在各个月份、护理记录要定期覆盖不能三天打鱼两天晒网。演示时数据太假容易被追问,太杂又显得没有业务逻辑。我一般会在做演示数据的脚本时,把每个老人的护理等级、最近一次体检结果和床位状态做成可对应上的口径。这样老师们查任何一个老人,你能从列表点到详情,从详情点到对应记录,每张表都有数据可跳,整个链路是通的。
6. 进阶验证:用 HTTP Client 做一轮接口回归与数据口径核对
改完前后端和数据库之后,不要急着打开浏览器一顿点。先用 IDEA 的 HTTP Client 写一组接口请求,把系统的核心链路快速验证一遍。这个操作要不了十分钟,但能帮你提前发现改代码过程中引入的问题,比在页面上点半天再猜原因要高效得多。我平时的做法是在项目根目录建一个 debug.http 文件,把登录、列表、新增、删除这种常用请求都放进去。
### 登录获取token POST http://localhost:8080/api/user/login Content-Type: application/json { "username": "admin", "password": "123456" } ### 查询老人列表(token需替换) GET http://localhost:8080/api/oldPeople/list?page=1&size=10 Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxxx ### 创建一条护理记录 POST http://localhost:8080/api/nurseRecord/save Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxxx { "oldPeopleId": 1, "type": 1, "content": "日常血压测量,数值正常", "nurseTime": "2024-06-01 09:30:00" }逻辑说明:第一段请求把账号密码发给登录接口,拿到 token;第二段请求把 token 放在 Authorization 请求头,访问老人列表,看分页参数 page 和 size 是否生效;第三段请求用于验证新增接口是否正常接收 JSON 数据。HTTP Client 工具能显示响应状态码、响应时间和响应体内容,比 curl 命令更直观。如果你是自己在浏览器里手动验证,也可以打开开发者工具到 Network 面板观察请求和响应,效果一样,只是手动操作更容易遗漏环节。
接口回归之后,需要做一次数据口径核对。这里的核心思想是:页面上的数字要和数据库里的实际记录数对得上。比如系统首页有一个"今日护理任务数"的统计卡片,你要能自己去查数据库确认这个数是怎么算出来的,而不是一眼扫过。实际上很多毕设项目的统计卡片都是聚合 SQL 查出来的,数据未必有什么深层含义,但如果你能在答辩时主动说清楚这个数字来自哪张表、按什么条件过滤,答辩老师会觉得你是真写了代码的。用聚合查询核对完数据后,把之前为了调试而创建的临时测试数据清理干净,比如你手动插入的无数条护理记录会污染演示数据,需要按时间范围删掉。删数据之前用 SELECT 先查一遍确认只删掉你要删的,毕业设计答辩翻车大多出在这种细节上。
我当时第一次拿到类似的源码包时,也是这样一步一步拆开看配置、跑通全链路、改完业务再回归一遍接口的。从那以后我每次碰毕设源码都强制自己走一遍"登录拿 token → 查列表 → 看详情 → 新增一条记录 → 删除一条记录"的完整循环,确认改动的代码没有破坏原有的链路,心里才有底。希望这套 Spring Boot + Vue 的养老院管理系统也能在你手里跑得顺,答辩问不倒、演示不出错,那这份资源就算真正帮你落地了。
本文还有配套的精品资源,点击获取