资讯动态

SpringBoot+Vue前后端分离养老院管理系统实战

发布时间:2026/10/9 5:53:34 来源:尧图企业网站定制
前后端分离的养老院管理系统SpringBootVueMyBatisMySQL这套技术栈在目前的毕业设计和全栈实战项目里出现频率相当高。原因很简单它把真实企业项目的开发方式完整地串了一遍——前端页面、后端接口、数据库设计、部署上线一个循环下来你对项目怎么做出来这件事就有了完整的体感而不是停留在某个框架的 Hello World 阶段。这次分享的这个养老院管理系统定位很明确面向养老院日常业务的信息化管理工具覆盖老人档案、床位分配、护理记录、费用管理、家属沟通等核心环节。在没上系统之前很多中小型养老机构还在用纸质台账和 Excel 表格老人信息分散在各个护理员手里床位状态要靠打电话问费用结算月底对账对到头疼。这套系统就是要解决这些问题。我会从项目整体的设计思路、后端核心实现、前端页面架构、完整部署流程、以及我在实际跑项目时踩过的坑这几个维度展开尽量把每个关键技术点都讲透。如果你正准备拿这个项目做毕业设计或者想通过一个完整项目来掌握前后端分离开发的节奏这篇文章会非常对路。1. 项目整体设计与技术栈选型1.1 为什么一定要用前后端分离养老院管理系统如果做成传统的 JSP 单体应用也不是不行但你会发现一个问题业务一旦开始细化前后端就互相绑架了。比如护理记录页面要加一个实时刷新的今日待办后端得改页面模板前端设计师调个样式又得重新部署整个应用。前后端分离的核心价值就是把数据提供和页面展示彻底拆开两边各自独立开发、独立部署、独立扩展。具体到这个项目里后端只负责两件事提供 RESTful API 接口、处理业务逻辑和数据库操作。前端只负责三件事渲染页面、管理用户交互、通过 HTTP 请求和接口打交道。两边通过 JSON 格式的数据交换互不干扰。这样做的好处非常实际开发并行前端写页面的时候后端可以先按接口文档提供 Mock 数据后端调业务逻辑的时候前端也不用来回等。项目周期能压缩三分之一以上。部署灵活后端可以扔在一台服务器上跑 8080 端口前端构建出来的静态文件用 Nginx 托管挂了前端不影响后端后端升级也不影响页面访问。扩展空间以后如果要做移动端 App 或者小程序后端接口可以直接复用只需要重写前端壳子就行。1.2 四个核心框架为什么是它们这套技术栈不是随便拼的每一个组件都有明确的分工和选型理由。SpringBoot负责把后端工程自动化地搭起来。以前用 Spring 写一个 Web 项目要配一堆 XML 文件数据源、事务、扫描路径、视图解析器每样都要手工声明。SpringBoot 用自动配置把这些琐事全部接管你只需要在application.yml里写几行配置项目就能跑起来。对于养老院管理系统这种典型 CRUD 业务为主的项目SpringBoot 能让你把精力集中在业务逻辑上而不是框架配置上。Vue前端部分用 Vue 的原因非常直白——学习曲线平缓、组件化开发效率高。系统里的老人档案表格、床位状态面板、费用统计图表这些都是天然适合组件化的界面。Vue 的双向数据绑定特性在表单页面的体验尤其舒服比如新增老人档案时输入框的校验、联动、清空一套v-model就能解决不需要像原生 JS 那样手动操作 DOM。MyBatis数据库访问层选 MyBatis 而不是 JPA/Hibernate是因为这类管理系统的 SQL 往往有很强的业务针对性——多条件组合查询老人信息、统计各护理等级的人数、按月份汇总费用这些操作用 MyBatis 的 XML 映射文件来写一个清晰直观的 SQL 语句就搞定相比 JPA 那种自动生成的查询可控性要好得多。而且 MyBatis 的性能开销低对 MySQL 的各种高级语法支持也好。MySQL数据存储是 MySQL 而不是更重的 Oracle 或 SQL Server原因是这个系统的数据量级和管理员的运维水平决定的。养老院管理系统的数据量——几百位老人的档案、每天的护理记录、每月的费用流水——MySQL 完全可以轻松应对。安装简单、文档丰富、社区问题答案随手就能搜到对非专职 DBA 的人来说足够友好。这里顺便说一句如果你照着这个项目跑的时候遇到 SpringBoot 版本问题后面第 5 节我会专门讲版本匹配的坑。现在很多教程用的是 SpringBoot 2.x但如果你自己新建项目默认拉到了 3.x很多配置类会变MyBatis 的 starter 依赖名也不同别慌那都是正常现象。2. 后端核心设计从数据模型到业务接口2.1 数据库表结构先想清楚业务流程写代码之前最先要做的是把数据模型设计好。养老院管理系统我拆成了这几个核心数据域老人档案域核心表是elder字段包括姓名、性别、身份证号、出生日期、入住日期、健康状况、护理等级自理/半自理/全护理、紧急联系人电话、家属 ID。床位管理域bed表记录床位号、房间号、楼层、床型单人/双人、当前状态空闲/占住/维修。老人和床位通过elder_bind关系表或直接在elder表里放bed_id字段关联。护理记录域nursing_record表记录每次护理的详细信息护理员 ID、老人 ID、护理项目测血压、翻身、喂药等、护理时间、备注。费用管理域fee_record表记录费用产生时间、费用类型床位费、护理费、餐饮费、金额、应收应付状态。员工与权限域staff表存员工信息包括角色字段管理员/护理员/财务/家属。这里我用了一个简单的role字段来控制权限没有单独做 RBAC 表对于这个规模的项目已经够了。实际设计的时候我建议你在表之间多建几个合理的外键和索引。老人们最常按状态查询比如所有空余床位所以bed.status一定要建索引费用记录经常按月统计fee_record.create_time也要建索引。这个项目你要是拿去做毕设数据库设计这一块一定要能讲出逻辑来这是答辩时的重点加分点。2.2 MyBatis 数据访问层手写 SQL 的威力MyBatis 在这个项目里承担的是将 Java 方法和 SQL 语句绑定的职责。我的习惯是在resources/mapper目录下放每个表的 XML 映射文件比如ElderMapper.xml、BedMapper.xml然后在对应的 Mapper 接口里声明方法。举个例子查老人列表时页面上需要展示老人姓名、护理等级、所住床位号、家属联系电话。这些数据分布在elder、bed、staff三张表里用 MyBatis 写一个自定义查询select idselectElderList resultTypemap SELECT e.id, e.name, e.nurse_level, e.health_status, b.bed_no, b.room_no, s.phone AS family_phone FROM elder e LEFT JOIN bed b ON e.bed_id b.id LEFT JOIN staff s ON e.family_id s.id where if testname ! null and name ! AND e.name LIKE CONCAT(%, #{name}, %) /if if testnurseLevel ! null AND e.nurse_level #{nurseLevel} /if /where ORDER BY e.create_time DESC /select这种多条件动态拼 SQL 的能力是 MyBatis 的看家本领。where标签会自动处理 AND 的拼接if标签实现条件判断——有没有这个查询条件都能执行非常适合列表页的筛选功能。关于 MyBatis 缓存也简单说两句。MyBatis 有一级缓存和二级缓存。一级缓存是 SqlSession 级别的同一个会话里执行相同的 SQL直接从缓存返回这个默认开启。二级缓存是 mapper 级别的需要手动配置跨会话也能命中。但在这个项目里我建议二级缓存慎用。因为养老院管理系统的数据变更很频繁老人状态、床位状态随时在变如果用了二级缓存可能你刚改完数据页面读到的还是旧状态。我在第 5 节会讲一个我因为这个缓存机制排查了半天的实际问题。2.3 接口设计与统一返回格式后端接口的设计直接影响前端开发的效率。这个项目里所有接口都遵循 RESTful 风格按资源命名GET /api/elder/page—— 分页查询老人列表带筛选条件POST /api/elder—— 新增老人档案PUT /api/elder/{id}—— 修改老人信息例如护理等级变更PUT /api/bed/assign—— 分配床位POST /api/nursing/record—— 录入护理记录GET /api/fee/statistics—— 月度费用统计这里有一个关键实践所有接口返回统一的数据结构。我在项目里定义了一个Result类public class ResultT { private Integer code; // 200 成功500 失败 private String message; // 提示信息 private T data; // 响应数据 public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.message success; result.data data; return result; } public static T ResultT error(String message) { ResultT result new Result(); result.code 500; result.message message; return result; } }这样做的好处前端只需要统一处理code字段为 200 就渲染数据否则弹出message里的错误提示。不用每个接口都去解析不同的数据格式省掉大量无意义的联调时间。另外要提到认证方案。这个项目用的是 JWTJSON Web Token。用户登录后后端校验账号密码生成一个带有用户 ID 和角色的 token 返回前端。前端在后续请求的请求头里带上Authorization: Bearer token后端通过拦截器验证 token 合法性。我在WebMvcConfig里注册了一个自定义的AuthInterceptor专门处理无需鉴权的白名单比如登录接口其他接口全部拦截。这一块的代码量不大但它是系统安全性的底线不能让任何人随便调接口改数据。3. 前端架构与核心页面实现3.1 Vue 项目结构与环境搭建前端工程是基于 Vue CLI如果你用 Vite 也可以后面我会说区别创建的标准项目结构。目录安排是这个项目的骨架src/ ├── api/ // 接口请求封装按模块拆分 │ ├── elder.js │ ├── bed.js │ └── fee.js ├── assets/ // 静态资源图片、全局样式 ├── components/ // 通用组件 │ ├── Pagination.vue │ └── StatusTag.vue ├── router/ // 路由配置 ├── store/ // Vuex 状态管理 ├── views/ // 页面组件 │ ├── Login.vue │ ├── Dashboard.vue │ ├── elder/ │ │ ├── ElderList.vue │ │ ├── ElderForm.vue │ ├── bed/ │ │ └── BedManage.vue │ └── fee/ │ └── FeeManage.vue └── main.js关于 Vue 环境配置我在项目里最常踩的坑是新装 Node.js 之后npm install报错。这里给一个经验之谈用 Node 16 或 18 的 LTS 版本不要追新。Vue CLI 5 和某些依赖在 Node 20 上面会出现兼容性问题。你要是用 Vite 创建 Vue 3 项目Node 版本要求会宽松一些但如果是照着我的这个项目结构来用 Node 16 的 LTS 版本最稳。3.2 几个核心页面的实现思路登录页虽然看起来简单但它是前端里第一个体现前后端配合的页面。用户输入账号密码后调POST /api/login接口拿到 token存放在localStorage并同步到 Vuex然后路由守卫根据 token 判断是否跳转首页。同时在请求拦截器里给 Axios 实例加上请求头axios.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer token; } return config; }, error Promise.reject(error));老人档案管理页这是整个系统最核心的页面采用了表格 搜索表单 弹窗表单的组合。表格用 Element UI 的el-table搜索区是护理等级、状态几个筛选项点击查询按钮后重新请求分页数据。这个页面充分体现了 Vue 组件的优势——el-table、el-dialog、el-form这些都是成熟的现成组件把开发精力留给业务逻辑。床位管理页我的做法是一个可视化的房间床位面板。每个房间是一个卡片房间内的床位用不同颜色标识——绿色空闲、红色占用、灰色维修。点击空闲床位弹出分配窗口选择老人调用床位分配接口。这个页面用到了 Vue 的列表渲染v-for和动态绑定 class:class它是整个前端代码里最直观展示 Vue 响应式原理的地方——床位状态一变页面自动刷新不需要手动任何 DOM 操作。费用管理页表格还原始费用明细顶部用el-date-picker选择统计月份调月度统计接口用el-card展示总费用统计卡。这里的前端代码并不复杂关键在于和 MyBatis 的日期函数配合好后面第 5 节我会讲一个时间范围查询的坑。3.3 跨域问题与开发代理前后端分离开发时最烦也最常遇到的第一个问题就是跨域。前端跑在http://localhost:8080后端在http://localhost:9090浏览器会因为同源策略拦截跨端口的请求。开发环境的解决办法有推荐就是在 Vue 项目的vue.config.js里配置代理module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } };这样前端请求/api/elder/page时开发服务器会把它转发给后端的9090端口浏览器角度来看是同源的没有跨域问题。而后端不需要做任何 CORS 配置因为生产环境你直接用 Nginx 反代同样把/api转发到后端服务。4. 完整部署流程从零到能跑起来4.1 MySQL 安装与数据库初始化这一节我会按 Windows 环境来写如果你用 Linux 服务器部署步骤大同小异。MySQL 我建议装 5.7 或 8.0 版本不要学我一开始用了最新版。最新的 MySQL 8.4 在某些认证插件上和老项目的 JDBC 驱动不兼容容易报错。安装的要点就两个记住你的 root 密码、选择正确的字符集。字符集一定要选 UTF-8或者安装后执行SET NAMES utf8mb4;如果不设置默认的 latin1 会导致中文乱码整个项目跑起来全是一排问号非常崩溃。初始化数据库的时候我用项目的sql/init.sql文件来完成建库建表。命令行执行mysql -u root -p init.sql或者用可视化工具Navicat、SQLyog直接导入。init.sql里包含CREATE DATABASE、所有表的建表语句以及一批测试数据。导入后在项目的application.yml里配置数据库连接spring: datasource: url: jdbc:mysql://localhost:3306/elder_care?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver这里注意serverTimezoneAsia/Shanghai必加否则 Java 8 以上的 JDBC 驱动和 MySQL 的时区不一致会报时间相关的错误。这个问题在 Windows 上尤其常见。4.2 SpringBoot 后端打包与运行后端项目用 Maven 管理依赖运行前先确认pom.xml里的依赖是完整的。核心依赖就四个spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java、以及 JWT 的jjwt。打包命令mvn clean package -DskipTests这个命令会在target目录下生成一个xxx.jar文件。要在服务器上运行直接用java -jar target/elder-care-system.jar默认端口我配置成了 9090在application.yml里server.port设置因为 8080 要给前端开发服务器或者 Nginx 用避免抢端口。如果你想在 Windows 上后台运行可以写一个.bat脚本echo off start javaw -jar elder-care-system.jar如果是 Linux 服务器推荐用nohup或者直接配置成 systemd 服务。后续杀进程的问题Windows 上就用netstat -ano | findstr :9090找出 PID 然后taskkillLinux 上lsof -i :9090配合kill -9。4.3 Vue 前端构建与 Nginx 发布前端开发完成后构建生产包npm run build构建成功后会在dist目录下生成静态文件。这些文件直接扔给 Nginx 就行。Nginx 配置的关键在于搞定前端路由的 history 模式和 API 反向代理。Vue 路由如果用了history模式去掉 URL 里的#刷新页面时 Nginx 需要把try_files重定向到index.html否则刷新页面就是 404。这是前端部署最常见的坑之一。完整的 Nginx 配置参考server { listen 80; server_name your_domain_or_ip; # 前端静态资源 location / { root /usr/share/nginx/html/dist; index index.html; try_files $uri $uri/ /index.html; } # 后端接口转发 location /api/ { proxy_pass http://127.0.0.1:9090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }配置完成后nginx -s reload。到这里整个系统就正式上线了你通过浏览器访问服务器 IP 就能看到登录页。5. 常见问题排查与避坑实录5.1 前端一列中文变问号这个问题的根源不复杂MySQL 表或连接字符集不对。排查链路是——先看application.yml里的连接串characterEncodingutf8是否配置再看 MySQL 表本身字符集。我的解决办法是建库时强制执行 UTF-8CREATE DATABASE elder_care DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;如果你在 Windows 里操作 MySQL还有一个隐藏坑MySQL 安装时如果选了默认的utf8其实是 utf8mb3部分生僻汉字和 emoji 字符仍然存不进去。保险起见就是表级和库级都指定 utf8mb4。5.2 MyBatis 查询结果字段全是 null这是新手最容易懵的问题。如果elder表里的字段是下划线命名比如nurse_level但实体类属性是驼峰命名nurseLevelMyBatis 默认不会自动映射。解决办法有两种推荐直接在配置里开启驼峰映射mybatis: configuration: map-underscore-to-camel-case: true这个配置让 MyBatis 自动把数据库的nurse_level映射到 Java 的nurseLevel不需要手写每一个resultMap。5.3 修改数据后接口读到的还是旧数据这大概率是 MyBatis 二级缓存的问题。我之前在某个 mapper 上开启了二级缓存后来发现一个诡异现象管理员在后台修改了老人护理等级但页面查询接口返回的还是旧数据。排查了很久才意识到是缓存没失效。定位方法很简单看 MyBatis 的 SQL 日志logging.level.com.example.mapperdebug开启发现同样的 SQL 没有真正执行而是命中缓存。对于管理类系统我建议干脆关掉二级缓存靠 MySQL 的查询性能已经足够不要为了那点性能提升引入数据一致性问题。5.4 Vue 项目npm install中途报错或卡死这个地方踩过的坑最多。新拉下来的 Vue 项目在npm install时经常遇到网络问题或依赖冲突。我的经验排序优先换 npm 镜像源比如用淘宝镜像注意这是常规操作里的正常做法。删除node_modules和package-lock.json重新npm install。检查 Node 版本是否和依赖兼容。另外强烈建议用nvm管理 Node 版本而不是把 Node 直接装死在系统里。这样每次遇到项目依赖版本问题切一个 Node 版本再试几乎都能解决。5.5 后端接口返回 401 或一直跳登录这个问题多半出在 JWT 拦截器的路径配置上。我在AuthInterceptor里配置了需要放行的路径但很容易少写了某个静态资源路径或某个接口的前缀。排错思路是先看后端控制台的拦截日志确定是拦截器拦截还是 token 校验失败再看前端 Axios 是不是真的在请求头里带了 token。这里给一个判断技巧——打开浏览器开发者工具的 Network 面板看请求头里有没有Authorization。如果没有问题在前端拦截器配置如果有且后端还报 401问题在后端 token 校验逻辑。5.6 SpringBoot 版本过高导致的兼容问题开头提了 SpringBoot 版本问题这里展开说。如果你直接用 Spring Initializr 创建项目默认拉的是 SpringBoot 3.x但很多老教程的依赖写法都是 2.x 的。最直接的区别!-- SpringBoot 2.x -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency !-- SpringBoot 3.x -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version3.0.3/version /dependencySpringBoot 3.x 基于 JDK 17如果你的本地 JDK 还停留在 8那就选 SpringBoot 2.7.x 系列。我的建议不是版本越新越好能让项目稳定跑起来的版本才是好版本。部署教程里没有强调这一点但实际拿源码跑的时候八成的人卡在第一关就在这里。5.7 费用按月统计查不出数据这个问题的坑在时区。统计某月的费用前端传的是2025-03后端如果用WHERE create_time 2025-03这种写法通常得不出正确结果。正确写法是用 MySQL 的日期区间函数select idselectFeeStatistics resultTypemap SELECT DATE_FORMAT(create_time, %Y-%m) AS month, SUM(amount) AS total_amount FROM fee_record WHERE create_time #{startDate} AND create_time #{endDate} GROUP BY month /selectstartDate和endDate是前端的月份参数处理过的边界值比如查 3 月就传2025-03-01和2025-04-01开区间。这样既避免了DATE_FORMAT导致的索引失效问题还能兼容跨年数据。我个人在实际操作中的体会是这种管理系统项目的复杂度不在某个单独的技术难点而在把一套完整业务流程从头到尾串起来的工程能力。数据库设计、后端接口、前端页面、部署发布每个环节看着都简单但联动起来出问题的地方恰恰是跨模块的衔接处——时区格式、字段命名风格、端口冲突、跨域配置、缓存策略。最后再分享一个对做这类项目非常实用的小技巧给所有接口加上日志输出。在 SpringBoot 的配置里打开 MyBatis 日志再把业务层的关键操作比如新增、删除、改状态用Slf4j的log.info打印出来。遇到问题的时候看日志比猜问题快十倍。这个系统本身是一个很典型的前后端分离实战项目从代码结构到部署流程完全贴合真实企业项目的开发范式建议你拿到源码后自己动手把部署文档里的每一步完整执行一遍——能跑通整套流程你对这套技术栈的信心会完全不一样。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑