资讯动态

Spring Boot+Vue疗养院管理系统全栈实战:从业务设计到部署

发布时间:2026/10/15 15:18:06 来源:尧图企业网站定制
1. 疗养院管理系统从一个真实需求到可运行的全栈项目做了这么多年Java开发接过的后台管理系统少说也有几十个了但第一次接触疗养院这类业务场景时我还是愣了一下疗养院管理和常规的医院管理系统、酒店管理系统到底有什么区别后来真正深入之后发现这个领域的业务逻辑非常有意思它既有医院系统的健康档案管理需求又有酒店系统的房态和入住管理逻辑还得兼顾餐饮、活动、排班这些运营类功能。这篇文章就以我最近整理的一套Spring Boot疗养院管理系统为例从技术选型、数据库设计、前后端实现到环境配置把整个项目的来龙去脉完整梳理一遍。这套系统的技术栈和标题里说的一致Spring Boot做后端、Vue做前端、MySQL存数据关键是开箱即用——拉下来配好环境就能跑起来。对于正在做毕业设计的学生、刚入行想找个完整项目练手的初级开发者或者疗养行业里想搞内部信息化但预算有限的小团队这套东西都有很直接的参考价值。我下面不会只贴代码而是把我设计这个系统时怎么想的、哪些地方容易踩坑、键盘敲下去之前需要做哪些决策全部摊开来讲。先说结论疗养院管理系统本质上是一个以人为本的资源调度平台核心是疗养员从入住到离开的全生命周期管理围绕这个生命周期衍生出房间安排、健康记录、餐饮服务、活动组织、费用结算等一系列子模块。搞清楚这个业务主线后面的技术实现就顺理成章了。2. 项目整体设计与业务模块拆解2.1 为什么疗养院系统不能照搬酒店或医院系统很多第一次做这类项目的人上来就套用酒店管理系统的房态管理或者照搬医院系统的挂号问诊逻辑结果做到一半发现对不上号。我自己最初也走过这个弯路后来跟某疗养机构的管理人员聊过之后才理顺疗养院的核心特征是什么是住得久。酒店的住客一般住几天医院的病人住几周而疗养院的疗养员一住就是几个月甚至半年。这意味着系统不能只做入住-退房这种简单状态流转还要支撑这么长周期内的健康变化追踪、服务项目累积、费用按天计算等持续性业务。另外疗养院通常是预约制床位空着就得摊销成本所以房间状态管理虽然形式上像酒店但本质上更接近资源日历——某张床在某段时间内被谁占用前后还要留出清洁和维护的空档期。基于这个理解我把系统拆成了六个核心模块系统管理用户、角色、菜单权限标准RBAC模型管理员和普通护工的操作边界靠这里控制疗养员管理从预约登记到正式入住、档案建立、离院归档全生命周期信息集中管理房间与床位管理按楼栋、楼层、房间、床位四级管理支持床位状态流转和分配策略健康档案管理定期录入血压、心率等体征数据记录慢病用药情况和特殊注意事项服务运营管理涵盖餐饮订餐、活动报名、护工排班等日常运营业务费用管理根据住宿标准、服务项目、天数自动计算应收费用支持部分缴费登记这六大模块互相咬合构成了一个可以独立运行的业务闭环。比如护工给某位疗养员录入体征数据时系统自动关联到他的房间床位和护理等级费用模块又根据护理等级和住宿天数累计费用管理员在前端看到的每一笔数据都能追溯到源头。2.2 技术选型这个组合为什么稳妥给这套系统选技术栈的时候我几乎是毫不犹豫地定了Spring Boot Vue MySQL这个组合。说实话这不一定是最炫酷的方案但一定是目前国内中小型管理系统里生态最成熟、资料最多、出问题最好查的方案。后端用Spring Boot理由不用多说它把所有繁琐的配置都自动化了内嵌Tomcat让部署变得极其简单。Java本身在传统行业的信息化系统里占有率极高这意味着这种项目无论交给谁维护都能很快上手。我选的是Spring Boot 2.7.x版本不用最新的3.x原因后面会专门讲。前端用Vue选它不用React的原因很实际Vue的上手曲线平缓模板语法对从后端转前端的开发者非常友好。我这个项目用了Vue 2.6 Element UI的组合如果你愿意也可以升级到Vue 3 Element Plus但为了最大程度降低直接运行的门槛我保留了更传统的这套。数据库层没有用MyBatis-Plus而是用了经典的MyBatis框架。我知道现在很多人建议直接上MyBatis-Plus毕竟单表CRUD能省掉大量XML。但考虑到这个项目在教学场景里经常被拿出来讲解用原生MyBatis反而能把SQL的执行逻辑看得更透学习价值更高。Plus版本要改回来也是一行依赖的事情。认证方案我选了JWT Spring Security没有用简单的拦截器。疗养院系统的角色边界非常清晰——管理员、护工、疗养员家属如果有的话不同角色的可见数据完全不同JWT天然适合这种前后端分离场景下的无状态认证。实际配置起来也没多复杂后面从代码层面细说。2.3 项目目录结构一眼能看懂的工程组织一个可维护的后端项目目录结构决定了开发效率的下限。这套系统的后端按业务模块分包而不是按技术层分包com.example.convalescence ├── common # 通用工具类、统一返回结果、异常处理 │ ├── result # Result类、ResultCode枚举 │ └── exception # 业务异常、全局异常处理器 ├── config # 配置类Security配置、CORS配置、JWT拦截器 ├── controller # 控制层按业务模块分 │ ├── admin # 管理员相关接口 │ ├── resident # 疗养员相关接口 │ ├── room # 房间床位相关接口 │ └── health # 健康档案相关接口 ├── service # 业务逻辑层接口实现分离 ├── mapper # MyBatis数据访问层 ├── entity # 数据库实体类 └── dto # 前端交互的数据传输对象VO、DTO、Query前端Vue项目用Vue CLI 5.x构建目录结构是标准化的vue-convalescence ├── src │ ├── api # 封装axios请求按模块拆分js文件 │ ├── router # 路由配置带动态权限控制 │ ├── store # Vuex状态管理存放用户信息和权限标识 │ ├── views # 页面组件按模块分文件夹 │ ├── components # 公共组件如图片上传、分页、表单弹窗 │ └── utils # request.js请求封装、token存取工具这样前后端各管各的联调阶段按接口文档对齐就行基本不会出现结构纠缠不清的情况。3. 数据库设计与核心业务逻辑3.1 表结构设计先画业务主线再建表设计数据库的时候我遵循了一个原则让表结构能够完整还原疗养员从来到走的完整故事线。一套合理的表结构写业务代码的时候会非常顺手反过来表设计一旦别扭后面每个模块都要跟它纠缠。我按六个模块设计了核心表这里列出关键结构和设计理由用户与角色表用户表存放账号密码和启用状态角色表和管理员用户角色关联表构成标准的RBAC结构。密码用BCrypt加密存储不存明文。疗养员信息表核心字段包括姓名、性别、身份证号、联系电话、紧急联系人、疗养类型自理/半自理/全护理、入住状态。状态字段我用了int类型0表示待入住、1表示在住、2表示已离院相比字符串更加节省空间查询效率也高。房间床位表楼栋、楼层、房间号、床位号、房间类型、床位状态。床位状态有三种0空闲、1已占、2维修。这里我特意把维修单独拎出来因为疗养院的床位翻新频率比普通人想象的高很多。入住记录表关联疗养员ID和床位ID记录入住日期、预计离院日期、实际离院日期、入住时护理等级。这张表是费用计算的核心依据。健康档案表每次体检或日常监测生成一条记录包含身高体重、血压血糖、心率、过敏史、当前用药、主治医生建议。用create_time字段区分不同时间点的记录。费用记录表关联入住记录ID包含费用类型床位费、护理费、餐饮费、活动费、金额、计费周期、缴费状态。数据库字符集用了utf8mb4原因很直接它完整支持Unicode包括生僻字和特殊符号对于疗养员姓名里可能出现的生僻字非常关键。排序规则用了utf8mb4_general_ci兼容性最好。3.2 房间分配与状态流转一个容易做错的核心逻辑房间床位管理是这个项目里业务最细的一个模块看似和酒店房态差不多实际差别不小。酒店只要管有没有人住疗养院还要管这个人适不适合这张床。我设计了一个简单的分配规则前台选择入住疗养员后系统根据其护理等级筛选可分配的床位。自理疗养员只能分配普通床位全护理疗养员只能分配带特殊护理设备的床位。筛选条件在SQL层面就过滤掉代码里不需要再做一层校验避免前端和后端双重判断产生的数据不一致风险。床位的状态流转有严格的前置约束入住操作只能针对空闲床位离院操作后床位自动变为空闲但会有一个待清洁的标记由保洁人员确认清洁完成后才会变回空闲。这个逻辑虽然简单但避免了上一位疗养员刚走下一位立即入住的卫生隐患。3.3 费用自动计算金额必须经得起对账费用模块是每个管理系统的敏感地带。疗养院费用的特点是除了一次性缴纳的基础费用每天的床位费、护理费、餐费都是动态累积的。我实现了一套基于入住记录和每日结算策略的自动计算逻辑。具体做法是入住时确定床位费和护理费的日单价系统每天凌晨定时任务生成当天的费用记录月底汇总未缴清的费用账单。离院时根据实际入住天数做一次性清算多退少补。整个计算过程在离院操作时还会重新校验一遍防止定时任务漏跑或数据异常导致金额错误。设计要点是计费基准不依赖前端传值所有费用计算都在后端根据数据库中的单价和日期范围完成。前端只能展示计算结果不能修改金额。这种设计让管理员在财务对账时心里有底——每一笔费用都能在数据库里追溯来源。4. 后端核心实现与关键代码解析4.1 Spring Boot项目结构与Maven配置Maven的pom.xml是这个项目的地基核心依赖就这几个parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies有几点说明一下。Spring Boot 2.7.x是2.x系列的最后一个大版本社区支持成熟稳定各种配置文件写法网上一搜一大堆。Spring Boot 3.x虽然已经推出但它基于Jakarta EE规范很多老项目的依赖要跟着变如果是刚接触Spring生态的开发者建议还是从2.7.x入手等熟悉了再去升级。MyBatis这里没有用mybatis-plus还是沿用传统mapper.xml的方式。这样做的好处是每个SQL都看得见、摸得着可以针对复杂联表查询写更精细的语句不至于被Plus的自动填充和逻辑删除功能带偏。有的同学担心XML写起来麻烦其实只要表结构清晰了Mapper层的编写就是体力活。Lombok是纯开发期工具编译后不占运行空间能少写大量getter和setter模板代码。如果你不喜欢用注解简化完全可以手写不影响其他功能。4.2 JWT认证与Spring Security配置前后端分离的关键环节现代管理系统不可能裸着接口让任何人调用JWT认证已经是标配方案。整个流程不复杂用户在登录页输入用户名密码后端验证通过后签发一个有效期2小时的JWT token前端存储在localStorage中后续每次请求都在Authorization请求头里带上这个token。后端配置一个过滤器对所有非白名单接口鉴权。JWT工具类核心逻辑如下public class JwtUtils { private static final String SECRET_KEY your-secret-key-change-in-production; private static final long EXPIRE_TIME 1000 * 60 * 60 * 2; // 生成token public static String generateToken(UserDetails userDetails) { MapString, Object claims new HashMap(); claims.put(username, userDetails.getUsername()); claims.put(role, userDetails.getAuthorities()); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } // 解析token public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } }Spring Security配置里我关闭了CSRF因为前后端分离场景下CSRF防护的意义不大JWT本身就是防篡改的凭证。同时配置了CORS允许前端开发服务器跨域访问。这里有一个容易被忽略的细节SecurityConfig中的密码加密器必须与注册用户时使用的加密方式保持一致否则就会出现明明密码没记错但怎么都登不进去的尴尬情况。4.3 统一返回结果和全局异常处理让前端少写十万行判断接口联调过程中最烦的事情之一就是前端拿到响应后还得判断各种乱七八糟的返回结构。为了统一我在后端定义了一个通用的Result类所有接口的返回值都包装成同一种格式Data public class ResultT { private Integer code; // 200成功500业务失败401未授权 private String message; // 提示信息 private T data; // 实际数据 public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }前端封装axios时拦截器统一判断code字段只对200做正常数据处理其他情况直接弹出错误提示。这一套下来联调效率提升一大截。同时全局异常处理器用RestControllerAdvice捕获业务异常、空指针异常、参数校验异常统一转成对应的Result返回避免看到满屏的500报错和堆栈信息。4.4 两个核心接口的实现细节登录接口接收用户名密码调用service层查询用户然后比较密码。密码比较用BCryptPasswordEncoder的matches方法校验通过后用JwtUtils生成token返回。前端拿到token后立即存入localStorage同时根据用户角色跳转到不同首页。床位分配接口接收疗养员ID和床位ID事务控制三步操作——检查床位状态是否为0空闲、更新床位状态为1已占、插入入住记录。这三步必须放在同一个事务里任何一步失败都要回滚否则会出现人没入住院子被占了的脏数据。5. 前端实现Vue组件化开发与接口对接5.1 前端路由与权限控制Vue Router配置了三种路由公开路由登录页、普通用户路由首页、个人中心、管理员路由全部管理页面。路由守卫的逻辑是每次切换路由前检查localStorage里有没有token没有就强制跳转登录页有token但访问的是管理员页面就检查用户角色没有权限就拒绝进入。菜单栏也做了动态渲染根据用户角色显示不同的菜单项。这套方案虽然是前端路由守卫这种相对轻量的权限控制方式对于后台管理系统已经足够了。如果后面业务扩大权限粒度细化到按钮级别可以考虑引入更完善的权限框架不过现阶段不必过度设计。5.2 axios请求封装一次封装全局复用前端所有HTTP请求都走统一的request.js封装import axios from axios const service axios.create({ baseURL: http://localhost:8080, timeout: 10000 }) // 请求拦截器 service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }, error { return Promise.reject(error) }) // 响应拦截器 service.interceptors.response.use(response { const res response.data if (res.code ! 200) { // 统一错误提示 return Promise.reject(new Error(res.message)) } return res.data }, error { // 401表示token过期跳转登录页 if (error.response error.response.status 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(error) }) export default service这个封装里有几个很实际的细节。超时时间设成10秒避免后端接口卡死时前端无限等待响应拦截器直接把res.data返回给调用方页面组件里拿到的就是干净的业务数据不用每层都解包。401状态码统一做登出跳转这样token过期后用户只需要重新登录一次不会一片报错弹窗轰炸。5.3 典型页面实现思路以房间管理页面为例页面结构就是一个表格 一个弹窗表单。页面加载时调用接口获取全部房间数据渲染成卡片式或表格式布局。点击分配床位按钮时弹出对话框对话框里有两个下拉选择器一个选疗养员一个选空闲床位。提交后刷新列表床位状态立即更新。健康档案页面则略有不同它拉取的数据是以疗养员为维度的历史趋势数据。展示上除了一个表格外我还用ECharts做了血压趋势折线图帮助医护人员快速把握疗养员的身体变化趋势。这里有一个经验页面复杂度提升时务必把接口调用和数据转换逻辑放到单独的store或工具模块里别全部堆在组件内部否则后面维护的人会非常痛苦。6. 环境配置与一键运行从零到跑通的完整过程6.1 本地环境准备清单可直接运行这四个字写出来容易真正做起来牵扯到的环境细节不少。我把这套系统跑通所需的全部环境列在这里照着准备就行软件版本要求用途备注JDK1.8或以上运行Spring Boot后端建议用JDK 8与Spring Boot 2.7兼容性最好Maven3.6以上管理后端依赖IDEA自带或单独安装均可MySQL5.7或8.0数据库注意utf8mb4字符集配置Node.js14.x或16.x运行前端构建工具尽量不用最新的18以上版本部分旧依赖可能有兼容问题IDEA / VSCode任意开发工具后端建议IDEA前端VSCode够用这些版本号不是随手写的都是实测稳定运行的组合。比如Node.js 16配合Vue CLI 5、Element UI 2这套组合npm install不会有任何报错提醒如果换成Node.js 20某些老依赖可能触发openssl相关的构建错误需要额外处理。6.2 后端配置项详解后端配置文件application.yml里最需要解释的是数据源配置server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/convalescence?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai username: root password: your-password jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.convalescence.entity configuration: map-underscore-to-camel-case: true有四个地方经常有人踩坑我单独强调一下第一url末尾必须带serverTimezoneAsia/Shanghai否则MySQL驱动会因为时区不明而报错。第二useSSLfalse是因为本地开发环境没有配置SSL证书默认false可以避免SSL握手警告。第三map-underscore-to-camel-casetrue必须开启否则数据库的user_name字段无法自动映射到Java的userName属性查询结果全是null。第四字符集参数里用的是utf8mb4而不是utf8否则生僻字存储会出问题。6.3 前端配置与启动前端启动前需要确认vue.config.js里的代理配置。因为前后端分离开发阶段前端跑在8080或默认的8080后端接口在8081跨域问题通过在vue.config.js里配置devServer代理解决module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端所有/api开头的请求都会被代理到后端的8080端口既绕过了跨域限制又不用在axios里写完整的后端地址。请注意前端的axios baseURL要配成/api才能被代理匹配到。前端项目的启动顺序也有讲究。建议先启动后端确认Swagger页面或登录接口能正常访问再启动前端。如果前端已经启动、后端还没起来页面加载时所有请求都会失败让人误以为代码有问题。另外首次npm install可能需要几分钟耐心等它跑完不要中途强制终止否则node_modules的状态会变得很脏。6.4 数据库初始化和测试账号项目自带一个convalescence.sql文件这是初始化脚本。用Navicat或命令行执行mysql -u root -p convalescence.sql脚本会自动创建数据库、所有表结构和初始数据。里面内置了一个测试账号管理员账号admin密码admin123这就是标题里说的可直接运行的真正含义——无论是学习还是演示这个账号拉通全流程省去了自己造数据的麻烦。正式交付给用户时记得首先修改默认密码避免因弱口令产生安全问题。7. 运行阶段常见问题与排查技巧实录7.1 后端启动失败的五个高频原因端口被占用8080端口被其他进程占用是最常见的启动失败原因。排查命令Windows下netstat -ano | findstr 8080Linux下lsof -i:8080。找到占用进程后杀掉或者直接把application.yml里的端口改掉。数据库连不上报错信息通常是Access denied for user rootlocalhost或Communications link failure。前者是账号密码不对后者是数据库服务没启动或URL写错。先确认MySQL服务已经启动再用客户端工具连接测试一下。缺少表结构如果启动不报错但接口调用时报Table doesnt exist说明忘了执行sql脚本。用客户端工具连上数据库看一眼有没有表没有就重新导入。Java版本不对控制台出现UnsupportedClassVersionError说明JDK版本和编译版本不一致。用java -version检查一下当前JDK版本确保是1.8及以上。依赖下载失败Maven报红通常是网络原因导致依赖下不下来。把Maven仓库地址换阿里云镜像可以大幅加快下载速度这一步对国内开发者来说几乎是必备操作。7.2 前端运行问题的三个排查思路npm install报错最常见的是node-sass安装失败。如果是老项目Node.js版本和node-sass的版本会冲突建议把node-sass替换成sass或降低Node版本。好在现在很多项目直接用sass这种问题少了很多。页面白屏控制台报错大概率是登录页请求接口时后端没启动或接口地址配置不对。在Network标签页看请求的URL和响应码确认是404还是CORS错误。404就检查代理配置CORS就检查后端CORS配置。token失效被反复踢回登录页因为token有效期为2小时超过2小时后再操作首次请求就会返回401前端自动跳转登录页。如果觉得2小时太短改JwtUtils里的EXPIRE_TIME即可。7.3 最容易被忽视的配置坑位字符集问题排第一。前后端任何一端在字符集上处理不当中文就会变成问号或乱码。后端部署环境建议在启动命令里加-Dfile.encodingUTF-8数据库连接串带characterEncodingutf8mb4前端页面的meta标签保持UTF-8声明。哪一层少了都不行。另一个坑是MySQL 8.0的认证插件。如果连接报Public Key Retrieval is not allowed错误是因为MySQL 8默认的caching_sha2_password插件要求客户端支持公钥检索。解决方法是连接串中加allowPublicKeyRetrievaltrue或把用户的认证插件改为mysql_native_password。这个坑比较隐蔽我拉了很多人在这里卡住。8. 这套系统的后续扩展方向做完整套系统之后我自己最大的感受是骨架已经搭建完毕基于这个基础往各个方向延伸的成本都很低。如果疗养院规模扩大可以加一个预约管理模块前端页面允许潜在客户提交疗养意向后台管理员审核后自动创建待入住档案护士站一目了然。这个功能对现实运营的价值非常高。如果涉及到家属远程查看健康报告可以在现有健康档案模块上增加一个家属端的只读账号体系。技术上就是权限控制的细化前端加一个H5页面适配手机访问后端增加一个家属角色查询范围限定为特定疗养员的数据。如果要做数据分析现有数据完全可以支撑一些可视化看板月度入住率趋势、护理任务分布、收费结构占比、常见健康问题统计等。用ECharts或更专业的数据可视化组件都能实现。最后说点个人的实践体会这套系统的业务复杂度完全覆盖了Java后端开发岗位面试里的高频考察点——权限模型、JWT认证、事务控制、多表联查、分页查询、异常处理。把它彻底吃透比你做的那些图书管理系统学生管理系统含金量高到不知道哪里去了。我在跟很多准备校招的同学交流时都反复提过一定要有一个能完整体现业务逻辑的项目而不是只会写demo级别的增删改查。疗养院管理系统的业务主线很长你做一遍下来对业务驱动设计这句话的理解会完全不一样。

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

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

免费获取报价 →
↑