ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue养老院信息管理系统全栈开发实战与部署指南

SpringBoot+Vue养老院信息管理系统全栈开发实战与部署指南 做养老院信息管理系统这个项目说实话挺有意思的。我接触过不少类似的管理系统但养老院这个场景比普通的CRUD项目要复杂不少涉及护理记录、健康档案、床位管理、探视登记这些业务细节不是随便凑几张表就能糊弄过去的。SpringBoot Vue这套组合在这个领域确实是最经典的搭配后端接口一把梭前端组件化开发一个人也能撑起整个项目。这篇文章会把整个项目的核心设计思路、技术选型原因、数据库表怎么拆、后端接口怎么写、前端页面怎么接以及部署上线时踩过的坑全部分享出来。不管你是准备拿来当毕业设计还是想快速上手全栈开发这套东西都能直接参考源码和文档配合着看基本可以做到无障碍复现。1. 项目整体设计与思路拆解1.1 为什么选SpringBoot Vue这对组合先说结论SpringBoot做后端接口服务Vue做前端页面交互这套组合在中小型管理系统里几乎是标准答案。SpringBoot的好处不用多提约定优于配置内嵌Tomcat一个jar包就能跑起来部署的时候不用折腾外部容器的环境问题。Vue这边组件化和响应式数据绑定让页面开发效率提升非常明显尤其是面对养老院这类需要大量表单、列表、弹窗交互的场景。从实际开发角度来看这套技术栈的生态太成熟了。后端有MyBatis Plus这种增强工具单表CRUD基本不用写SQL前端有Element UI或Element Plus组件库表格、表单、日期选择器、分页这些现成组件拉出来就能用。整个项目的人工成本主要集中在业务逻辑梳理和联调测试上而不是在底层造轮子上。还有一个很现实的原因招聘软件上搜Java开发岗SpringBoot Vue是出现频率最高的技能组合之一。这个项目做下来简历上能写的技术点非常扎实——RESTful接口设计、JWT认证、前后端分离部署、数据库设计这些都是面试时高频考察的内容。1.2 功能模块划分与业务链路梳理养老院信息管理系统本质上解决的是院内日常管理的信息化问题。我梳理了一遍典型养老院的业务链路核心模块大致分为以下几条线人员线老人档案、家属信息、员工信息。老人入住时录入详细档案包括身份信息、健康情况、紧急联系人员工这块包含护工和行政人员区分不同角色权限。护理线这是养老院独有的核心业务。每天的护理记录、用药提醒、健康监测数据血压、血糖、心率等需要按老人维度做纵向的时间线追踪。管理线床位的分配与调换、入住和退住的流程管理、费用管理床位费、护理费、伙食费。这些模块有较强的状态流转逻辑比如床位从空闲到已入住再到退住返空。访客线家属探视登记、出入院记录。虽然看着是边缘功能但真正落地运营时非常需要尤其是疫情防控常态化的背景下访客记录必须能一键导出查证。从技术实现来看人员线和访客线就是标准的CRUD没什么难度。真正的技术重点是护理线和费用管理前者涉及一对多的动态记录后者涉及多个费用项的聚合计算。所以我在设计接口时把这两块单独拎出来做了业务层封装没有直接在Controller里堆逻辑。1.3 技术选型里的取舍经验技术选型不是越新越好而是越稳越好。这个项目里我选了MyBatis Plus而不是纯MyBatis选Hutool而不是自己写工具类选JWT而不是Session每一个选择背后都有实际原因。MyBatis Plus解决的是开发效率问题。它内置了通用Mapper单表CRUD的SQL完全不用手写查询时用LambdaQueryWrapper链式调用来拼条件代码看着清爽也不容易出SQL拼接错误。更重要的是MyBatis Plus的乐观锁插件、分页插件PaginationInnerInterceptor都是直接配置即可用省去了每次手写分页SQL的痛苦。JWT代替Session核心原因是前后端分离架构下的体验问题。后端只提供API前端可能是Vue页面、可能是小程序、可能是移动端JWT无状态、天然支持跨域、不需要在服务端保存会话信息非常适合这种模式。当然JWT也有弊端比如无法主动失效但在这个项目场景下登录态的安全要求没那么高JWT的便利性完胜。Hutool是个容易被低估的工具库。项目里有大量日期格式化、Excel导出、身份证号校验老人录入时真的很常用的需求Hutool一行代码全搞定。我觉得这个库的引入价值不亚于MyBatis Plus。2. 数据库设计与后端核心实现2.1 核心表结构设计与字段规划数据库设计是整个项目的地基表结构没设计好后面写代码哪儿哪儿都别扭。我用的是MySQL 8.x字符集选utf8mb4。下面直接列出核心表的设计思路和几个关键表的字段规划。老人信息表elderly是最核心的表字段设计直接影响后续的护理、费用、床位等模块id主键自增name老人姓名索引字段列表页经常按姓名搜索gender性别tinyint0男1女避免直接用字符串存id_number身份证号注意唯一索引但实际养老院中可能存在建档重复的情况建议唯一索引允许NULLbirthday出生日期用date类型年龄可以实时计算不要单独存年龄字段health_status健康状态用varchar存文本或者用编码字段关联字典表bed_id关联床位表guardian_name / guardian_phone紧急联系人和电话status状态0为预登记1为在住2为已退住create_time / update_time时间字段床位表bed要体现楼栋-楼层-房间-床位的层级关系我简化成这种设计building楼栋号、floor楼层号、room_no房间号、bed_no床位号、status0空闲,1占用。实际使用时直接展示为3号楼-2层-201房-1床管理端方便老人家属查看时也直观。护理记录表nursing_record是典型的动态数据表字段如下idelderly_id关联老人联合索引的重点nurse_id关联护工/员工record_date护理日期start_time / end_time护理时段content护理内容文本比如喂药、翻身、协助洗漱temperature / blood_pressure / blood_sugar健康数据冗余在护理记录里简化统计remark备注费用表fee_record的设计需要注意我把它拆成了费用流水和账单汇总两层。流水表记录每笔费用明细项目、金额、产生时间、关联老人账单表按月汇总月度费用总额、已缴金额、欠费金额。这样设计的好处是前台账单页只查汇总表性能好后台流水页又能穿透到明细财务对账方便。角色权限表我用了最经典的三表模型用户表(user)、角色表(role)、权限表(permission)以及它们的关联表user_role、role_permission。虽然这个项目角色不多管理员、护工、财务但把权限做成表驱动后续扩展角色不用改代码直接数据库里加记录。2.2 后端接口设计与业务逻辑拆分后端整体包结构按照现在主流的分层风格来controller、service接口、service.impl、mapper、entity、dto、vo、config。我特别推荐把DTO和VO分开很多新手朋友容易忽视这层。DTO数据传输对象用于接收前端传来的参数尤其是查询条件复杂时用DTO对象接收可以避免Controller方法里写一堆RequestParam。比如护理记录的查询可能包含老人ID、日期范围、护理类型、分页参数定义一个NursingQueryDTO前端直接传JSON后端用一个对象接收清晰且扩展性好。VO视图对象用于封装返回给前端的数据。比如老人列表页要显示床位信息但elderly表里只有bed_id直接返回elderly实体前端拿不到楼栋号如果Service里手动拼一个Map又显得很随意。定义一个ElderlyVO包含老人信息和格式化后的床位位置前后端字段一目了然。接口设计的RESTful规范上我整体遵循几个原则资源用名词复数GET /api/elderliesPOST /api/elderliesPUT /api/elderlies/{id}DELETE /api/elderlies/{id}查询列表统一带分页参数pageNum、pageSize加排序字段sortField、sortOrder统一响应体Result类包裹code、message、data前端根据code判断成功还是失败状态变更类操作单独设计接口比如老人的入住操作是PUT /api/elderlies/{id}/checkIn而不是直接修改status字段这样Service层可以挂载业务校验床位是否空余、老人是否已在住等业务逻辑层Service我最大的一个体会是要把校验和业务分开。比如办理老人入住先做参数校验老人ID是否存在、床位ID是否有效再做业务校验床位是否已被占用、老人状态是否允许最后才执行数据更新。如果校验逻辑全部堆在Controller里代码会越来越难维护。2.3 权限控制实现与JWT登录认证登录认证这一块我用的方案是Spring Security JWT。Spring Security负责请求的拦截和权限校验JWT负责无状态的身份认证。核心实现思路不复杂用户输入用户名密码后端UserDetailsServiceImpl加载用户信息比对密码BCrypt加密存储认证通过后生成JWT Token设置过期时间比如2小时返回给前端前端把Token存到Vuex和localStorage中接口请求时Header携带Authorization: Bearer {token}后端定义JwtAuthenticationTokenFilter每个请求先解析Token解析成功就放入SecurityContext在Spring Security配置里指定放行路径登录接口、静态资源、Swagger文档等其余请求全部要认证角色的权限控制用的是PreAuthorize注解在Controller方法上标注角色限制PreAuthorize(hasAnyRole(ADMIN, FINANCE)) PostMapping(/api/fees) public Result addFee(RequestBody Valid FeeCreateDTO dto) { feeService.createFee(dto); return Result.success(); }这里有个容易踩的坑Spring Security在hasRole时会自动拼接ROLE_前缀所以数据库里存的角色标识最好就是ADMIN、FINANCE这种不带前缀的字符串后端注解里写全称两边保持对齐。3. 前端Vue实现与页面交互细节3.1 前端工程结构与技术栈搭配前端我用的是Vue 2 Element UI Axios Vuex Vue Router这套组合在管理系统中非常成熟稳定。工程结构按模块划分src/ ├── api/ # 接口请求封装按模块拆文件 │ ├── elderly.js │ ├── nursing.js │ └── system.js ├── assets/ # 静态资源 ├── components/ # 通用组件Upload、Table、Pagination等 ├── router/ # 路由配置 ├── store/ # Vuex状态管理 ├── views/ # 页面级组件 │ ├── elderly/ # 老人管理页面 │ ├── nursing/ # 护理记录页面 │ ├── bed/ # 床位管理页面 │ ├── fee/ # 费用管理页面 │ ├── system/ # 系统管理页面 │ └── login.vue # 登录页 └── utils/ ├── request.js # Axios实例封装 └── auth.js # Token读写工具Axios的封装值得多说一句。我在utils/request.js里做了三个统一处理请求头统一加Token、响应统一拦截处理HTTP错误码、业务错误统一弹Message提示。这样写的好处是每个API模块只需关注返回的业务数据不用每次手动处理错误分支代码量大幅减少。比如API模块长这样// api/elderly.js import request from /utils/request export function getElderlyList(params) { return request({ url: /api/elderlies, method: get, params }) }页面上调用只需要获取到Promise数据就直接用了。这套模式是整个项目开发效率高的关键原因之一。3.2 核心页面功能与实现思路老人列表页是使用频率最高的页面。我的实现思路是顶部是搜索条件栏姓名关键字、状态筛选、楼栋下拉中部是表格底部是分页组件。搜索条件通过el-form的inline模式布局字段变化时自动触发重置页码并重新请求。表格里比较关键的列是床位位置、健康状态和操作按钮。床位位置在VO里已经拼接成3号楼-2层-201-1床这种格式直接用模板渲染。操作按钮根据当前状态动态显示在住老人可以办理退住和查看档案预登记老人可以办理入住。用button组件的v-if控制即可。护理记录页面稍微复杂一点。我需要按老人维度展示时间线所以实现方式是用el-timeline组件配合卡片式展示每天的护理历史。新纪录的录入走弹窗表单包含护理时间、护理内容、健康指标体温、血压等。这里有个细节弹窗里选老人时不直接用下拉框老人多了不好找而是加了搜索和分页的el-select组件用户体验会好很多。费用管理页面展示的是月度账单汇总表。表格展示每位老人的月度费用明细床位费、护理费、伙食费、已缴金额、欠费金额晚缴状态用不同颜色的tag标出来。点击行可展开查看费用流水。这里用了el-table的expand列功能天然支持展开子表格不用额外写弹窗逻辑。3.3 前端路由与权限控制实现路由权限控制是管理系统必须要做的不然直接改URL就能访问未授权页面安全性太差。我用了Vue Router的全局前置守卫配合Vuex来实现router.beforeEach((to, from, next) { const token getToken() if (!token) { // 未登录直接踢到登录页 if (to.path ! /login) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } return } if (to.path /login) { next({ path: / }) return } // 检查store中是否有用户信息和权限 if (!store.state.user.hasRoles) { store.dispatch(user/getUserInfo).then(() { next({ ...to, replace: true }) }).catch(() { // Token过期清理并跳登录 logout() next({ path: /login }) }) } else { next() } })权限这块我用的是按钮级控制方法是在自定义指令里判断当前用户的角色列表。比如财务人员看不到新增老人按钮管理员能看到删除按钮。自定义指令实现如下Vue.directive(permission, { inserted(el, binding) { const requiredRoles binding.value const userRoles store.state.user.roles const hasPermission userRoles.some(role requiredRoles.includes(role)) if (!hasPermission) { el.parentNode.removeChild(el) } } })自定义指令用起来非常方便也避免在模板里写大段v-if判断。4. 环境搭建、数据库初始化与项目部署实操4.1 开发环境准备清单这个项目在Windows、Linux、macOS上都能开发我用的是Windows环境搭建流程如下JDK 1.8建议使用JDK 8较稳定避免JDK 17的兼容性坑Maven 3.6MySQL 8.05.7也可以但建议直接上8.0性能更好Node.js 14.xVue 2项目Node版本不用太高16或18都可以IDEIDEA加装Lombok插件后端VS Code加装Vetur插件前端配置方面有几个容易忽略的坑Maven的settings.xml要配置阿里云镜像源不然下载依赖能等到怀疑人生Node.js建议顺手把npm源切换成淘宝镜像npm config set registry https://registry.npmmirror.comMySQL初始化字符集时指定utf8mb4避免中文乱码问题4.2 数据库初始化与脚本导入项目提供了完整的SQL脚本文件包含建库、建表、初始数据三个部分。导入流程如下命令行或Navicat创建数据库CREATE DATABASE elderly_system DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci执行SQL脚本。如果脚本文件名是elderly_system.sql命令行导入命令是mysql -u root -p elderly_system elderly_system.sql导入后检查几组关键数据是否有默认管理员账号admin、角色表是否有三条记录、床位表是否初始化了足够的空闲床位。这三点直接决定后端能不能正常登录、有没有数据可看。SQL脚本里的初始数据是有讲究的。因为很多同学拿到的项目源码是没有业务数据的空表状态下点任何页面都看不到效果容易误认为是系统出Bug了。这个项目的脚本里预置了几位老人、几条护理记录和一两个月的费用流水登录后首页和数据看板直接有数据展示体验好很多。4.3 后端启动流程与常见配置项后端启动前需要修改application.yml里的数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/elderly_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver server: port: 8080 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL日志打印开发时建议打开 global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0这里有两个细节提醒一下serverTimezone务必设置否则MySQL 8连接时会报时间时区相关的错误开发阶段把MyBatis Plus的SQL日志打开所有执行的SQL语句都会打印在控制台查接口问题时效率翻倍。上线时记得关掉不然日志文件会疯涨启动方式很简单在IDEA里运行主类上的main方法即可。启动后访问 http://localhost:8080如果能看到Spring的欢迎页或者项目自己定义的接口文档页面说明后端已经正常启动了。4.4 前端启动与对接后端前端的启动流程也很直观cd frontend npm install npm run devnpm install耗时取决于网速和镜像配置耐心等待即可。如果出现版本冲突报错可以删除node_modules和package-lock.json后重装这种问题多半是依赖版本解析不一致导致的。vue.config.js里的代理配置值得专门讲讲。开发环境前端跑在8081端口devServer配置的后端在8080直接请求会跨域。我的做法是在vue.config.js里配置代理module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端代码里所有请求路径写/api开头实际开发时由webpack-dev-server转发给后端规避了跨域问题。注意这里有个最常见的坑如果没有配置proxy或者代理路径配错前端发起的请求会404或者报CORS错误。报CORS错误时先看浏览器F12的Network选项卡确认请求实际打到了哪个端口问题往往一目了然。4.5 前后端分离部署的完整流程部署上线比开发环境多几个步骤整体链路如下后端打包在pom.xml同级目录执行mvn clean package -DskipTests获得target目录下的XXX.jar文件上传到服务器执行nohup java -jar elderly-system.jar logs.log 21 确认端口开放用curl测试接口是否正常响应前端打包执行npm run build得到dist目录里面是纯静态文件把dist目录内容放到Nginx的html目录下Nginx配置关键部分server { listen 80; server_name yourdomain.com; # 前端静态文件 location / { root /usr/local/nginx/html/dist; index index.html; # 解决Vue Router history模式刷新404问题 try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这个地方有个非常经典的Vue部署坑。如果路由用的是history模式直接刷新页面时会404因为Nginx只找到了首页。解决办法就是上面配置中的try_files $uri $uri/ /index.html把所有找不到的路径都回退到index.html让Vue Router接管路由解析。很多新手在这一步卡很久觉得代码没问题为什么刷新就白屏其实就是少了这一行。5. 常见问题与排查技巧实录5.1 启动阶段的典型报错我在开发和帮朋友排查问题时整理了一个报错清单都是高频问题。启动前端报Module not found: Error: Cant resolve element-uinpm install没有成功或者依赖没有完整安装。干脆删除node_modules重新执行npm install。如果用了cnpm建议换成npm或执行cnpm install两种包管理工具混用容易出问题。后端启动报APPLICATION FAILED TO START排查方向很多但最普遍的是端口被占用和数据库连接失败。端口被占用时Windows用netstat -ano | findstr 8080 查看进程PID任务管理器结束进程Linux用lsof -i:8080 查看。数据库连接失败时重点检查application.yml里用户名密码有无写错、MySQL服务有没有启动。启动后页面白屏这个问题要区分前端控制台有没有报错。如果控制台提示加载不到JS或CSS大概率是静态资源路径配置问题如果控制台没有报错但页面空白多半是路由配置问题检查是否有默认跳转路由redirect配置以及根路由是否正确。5.2 前后端联调时的接口对接问题联调阶段最常见的问题就是接口404和字段对不上。接口404先检查路径。前端Axios里写的路径是/api/elderlies后端Controller上的映射是否也包含了/api前缀。这种问题我在很多项目里见过尤其是一开始后端加了context-path配置导致所有接口多了一层路径前缀前端完全不知情。排查方法是打开浏览器F12查看Network里实际请求的完整URL再对照后端代码里的RequestMapping就能确认路径差在哪了。字段对不上是另一个高频问题。比如前端表格要展示老人姓名前端代码里写的是name但后端VO返回来的是elderlyName页面就是空白。有个笨但有效的排查方法浏览器F12看Response JSON直接看后端到底返了哪些字段然后逐个对比前端模板绑定的字段名。没有捷径就是细心。5.3 修改和扩展功能的避坑指南很多同学拿到源码后会想改一些功能比如加个投诉建议模块或者员工排班功能这里有几个长教训的注意事项。第一不要直接改数据库表要通过Migration脚本或者SQL文件记录变更。直接在线上数据库ALTER TABLE加字段虽然快但团队其他成员的本地库就跟不上了而且项目文档和实际结构会脱节。第二新增功能时按照项目的分层结构来写。参考已有模块的代码Controller的目录建在controller包下Service接口和Impl分开Mapper接口用MyBatis Plus的BaseMapper方式尽量避免在一个类里写全套业务逻辑。第三做报表和统计功能时尽量用后端写SQL聚合而不是前端遍历计算。比如统计本月入住率后端一条SQLSELECT COUNT(*) FROM bed WHERE status 1 就能拿到前端去遍历列表再数数数据量大时页面直接卡成PPT。5.4 性能优化与上线后的注意事项养老院规模不一定会很大但这个项目的架构是按可扩展来设计的几个优化点长期有效。MySQL的连接池我使用的是HikariCPSpring Boot 2.x默认连接池配置里限制了最大连接数为20。这个配置够用不需要调到100以上连接池大了反而浪费资源。JVM内存根据服务器配置调整-Xms和-Xmx参数如果服务器只有2G内存默认交给JVM的堆大小很可能偏大需要手动限制。数据备份是上线运营后最重要的事。养老院管理系统里存的是老人的健康记录和费用流水极端场景下这些数据不能丢。建议每天凌晨执行一次MySQL dump同时保留最近7天的备份文件。这个用crontab配合shell脚本就能完成成本几乎为零但能兜底数据库故障的极端情况。最后说一个安全配置。系统的登录接口在生产环境应该加上验证码功能防止暴力破解。这个项目源码里预留了验证码接口的位置如果直接部署上线强烈建议把简单的登录验证码加上哪怕是用图形验证码工具库生成一张临时图片都能极大提高安全性。这整套项目整理下来前前后后花了不少时间打磨尤其是数据库设计那部分床位和费用的状态流转逻辑返工过两次。如果你自己动手改项目最大的建议是先跑通再扩展最后优化。很多人一上来就想把每个模块都做到完美结果反倒在基础功能上反复卡壳。先把核心流程跑顺再逐步叠加功能这样项目质量和开发体验都会好很多。
返回列表