2025年初我接手了一个养老信息化项目,需求方是一家拥有180张床位的民办老年疗养院。系统上线跑通之后回头看,这类项目的难点从来不在技术本身,而是业务理解和角色梳理。今天把这套“基于SpringBoot + Vue的老年疗养院管理系统”的完整设计思路、核心代码、避坑记录整理出来,希望能给正在做同类系统或准备入门前后端分离开发的同行一些参考。
这个项目本质上是一个典型的中小型管理信息系统:后端用SpringBoot 2.7.x提供RESTful API,前端用Vue 3 + Element Plus构建单页应用,数据库选MySQL 8.0。系统覆盖了疗养院最核心的几块业务——老人档案、床位管理、健康监测、护工排班、用药提醒、家属探访、费用结算。整体开发周期大约8周,单后端开发的话4到5周可以完成核心模块。
1. 项目整体设计与技术选型解析
1.1 为什么选SpringBoot + Vue这套组合
先说结论:这个组合在当前国内中小型管理系统开发中性价比最高,没有之一。
后端选择SpringBoot的理由非常直接。SpringBoot的自动配置机制大幅降低了Spring项目的搭建成本,开发阶段只需要一个main方法就能启动完整Web服务。对于疗养院管理系统这种业务逻辑清晰、CRUD为主、少量复杂报表的系统,SpringBoot自带的Spring Data JPA或MyBatis-Plus就能覆盖90%以上的数据访问需求,不需要引入微服务那套重型架构。
前端选择Vue的原因也很实在。Vue 3的组合式API(Composition API)让组件逻辑复用变得非常舒服,特别适合管理后台这种大量表单加表格的页面形态。Element Plus组件库直接提供了表格、表单、对话框、日期选择器、分页等整套后台UI组件,开发效率比手写HTML加jQuery高出太多。单页应用配合Vue Router实现前端路由,用户在疗养院不同功能模块之间切换时无刷新体验,老人家属在前台查询探访记录、缴费账单时的体验比传统多页面Web应用好很多。
1.2 核心需求分析:疗养院业务到底特殊在哪
养老管理系统和普通的企业管理系统有一个本质差异:系统服务对象是“高龄、部分失能或半失能人群”,数据录入者和数据监管者分离,业务链条长且涉及多角色协作。
拿最常见的“老人入住”这个场景举例,一次普通入住背后涉及的信息包括:老人基本身份信息、家属或紧急联系人信息、既往病史与过敏史、体检报告、护理等级评估、床位分配、入住合同、押金缴纳、初始健康档案建立。这些信息分属不同业务环节,如果系统没有提前把数据模型设计好,后期每个模块的对接都要返工。
我在需求调研阶段专门花了一周时间驻场观察疗养院日常运作流程,最后梳理出五个核心角色:院长或管理员、护士长、护工、财务人员、家属或老人本人。每个角色的关注点差异极大,这直接影响了系统的权限模型设计(后文详述)。
1.3 项目目录结构与模块划分
项目采用标准的前后端分离结构,后端按业务模块分包,前端按页面功能组织目录:
nursing-home-system ├── backend │ ├── src/main/java/com/nursing │ │ ├── controller # 接口层 │ │ ├── service # 业务逻辑层 │ │ ├── mapper # 数据访问层(MyBatis-Plus) │ │ ├── entity # 数据库实体 │ │ ├── dto # 数据传输对象 │ │ ├── config # 配置类(安全、跨域、拦截器) │ │ ├── common # 通用返回结果、异常处理、工具类 │ │ └── NursingApplication.java ├── frontend │ ├── src │ │ ├── api # 封装axios请求 │ │ ├── assets # 静态资源 │ │ ├── components # 公共组件 │ │ ├── router # 前端路由 │ │ ├── store # Pinia状态管理 │ │ ├── views # 页面组件 │ │ ├── utils # 工具函数 │ │ └── App.vue │ └── vite.config.js └── sql └── nursing_home.sql # 初始化脚本后端一定要按业务模块划分包结构,不要把所有Controller堆在一个包下面。我的做法是按照“老人管理、床位管理、健康档案、护理计划、排班考勤、用药管理、探访管理、费用管理、系统管理”这九个模块建立清晰的分包结构,每个模块内的功能高度内聚,后续扩展和排查问题都方便。
1.4 环境版本选型与踩坑预警
这个项目开发期间踩过一个典型的版本坑,这里必须单独拿出来说。
SpringBoot官网推荐的版本迭代很快,现在打开官网默认推荐的是3.x版本,但它基于JDK 17以上运行。很多高校教学和企业现有服务器还在用JDK 8,直接新建SpringBoot 3.x项目会在启动时报错或产生兼容性问题。这个项目最终选择SpringBoot 2.7.18稳定版,搭配JDK 1.8和Maven 3.8,整套环境在开发机和服务器上都能平滑运行。
前端需要特别注意Node.js版本和Vue CLI/Vite的匹配。Vue 3项目我优先选择Vite作为构建工具而不是Vue CLI,因为Vite在开发环境下的冷启动和热更新速度快很多。Node.js版本建议16.x或18.x LTS版本,版本太低或太高都可能出现依赖安装失败的问题。
2. 系统功能架构与数据库设计实战
2.1 角色权限模型设计
疗养院管理系统的权限模型我建议采用RBAC(基于角色的访问控制)模型,但角色划分要贴合实际业务岗位,而不是简单地设“管理员”和“普通用户”。
系统最终划分了五个角色,每个角色的核心权限边界如下:
| 角色 | 核心菜单权限 | 数据操作边界 |
|---|---|---|
| 超级管理员 | 全部模块 | 所有数据的增删改查、系统配置 |
| 护士长 | 老人档案、床位分配、健康评估、排班管理、用药审核 | 护理相关数据全部读写,财务模块只读 |
| 护工 | 我的排班、护理记录上报、健康指标录入 | 仅能查看自己负责的老人数据和新建护理记录 |
| 财务人员 | 费用管理、账单生成、缴费记录 | 费用模块全权限,老人档案只读 |
| 家属/老人 | 探访预约、个人信息、健康报告查询、账单查询 | 仅可查看关联老人的有限数据 |
这个权限模型需要在后端做两层校验。第一层是登录拦截,通过JWT Token验证身份;第二层是方法级权限控制,对于敏感操作(如删除老人档案、修改费用记录),使用Spring Security的@PreAuthorize注解做细粒度限制。仅靠前端v-if控制菜单显隐是不安全的,接口数据必须做到真正的后端鉴权。
2.2 核心数据表结构详解
数据库设计是整个系统的地基,我梳理出15张核心业务表,下面挑几张关键表详细说明。
老人档案表(elder)是系统的数据核心,字段设计直接影响后续所有模块的开发便捷度:
CREATE TABLE `elder` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `elder_no` varchar(32) NOT NULL COMMENT '老人编号(系统自动生成)', `name` varchar(32) NOT NULL COMMENT '姓名', `gender` tinyint(1) NOT NULL COMMENT '性别 1男 2女', `birth_date` date NOT NULL COMMENT '出生日期', `id_card` varchar(18) DEFAULT NULL COMMENT '身份证号', `phone` varchar(20) DEFAULT NULL COMMENT '老人本人电话', `health_level` varchar(10) DEFAULT NULL COMMENT '护理等级:自理/半自理/全护理', `room_id` bigint(20) DEFAULT NULL COMMENT '关联床位ID', `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1在院 0退院', `entry_date` date NOT NULL COMMENT '入院日期', `leave_date` date DEFAULT NULL COMMENT '出院日期', `emergency_contact` varchar(32) DEFAULT NULL COMMENT '紧急联系人', `emergency_phone` varchar(20) DEFAULT NULL COMMENT '紧急联系电话', `medical_history` text COMMENT '既往病史', `allergy_history` text COMMENT '过敏史', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除标记', PRIMARY KEY (`id`), UNIQUE KEY `uk_elder_no` (`elder_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='老人档案表';这里想强调三个细节。第一,deleted字段一定要加,老人档案属于高价值数据,误删除不可接受,逻辑删除是底线。第二,elder_no要用业务编码规则自动生成,比如E + 年月日 + 三位流水号,这样前台报单时只需要报编号,不用在电话里慢慢念身份证号,疗养院实际运营时这个小细节非常实用。第三,health_level护理等级不要用纯数字枚举,直接存中文可读性更好,后续生成报表也免去一层转换。
健康监测表(health_record)用于记录老人的日常生命体征数据:
CREATE TABLE `health_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `elder_id` bigint(20) NOT NULL COMMENT '老人ID', `record_date` date NOT NULL COMMENT '记录日期', `record_time` varchar(10) DEFAULT NULL COMMENT '记录时间点(如08:30)', `temperature` decimal(4,1) DEFAULT NULL COMMENT '体温(℃)', `blood_pressure_high` int(11) DEFAULT NULL COMMENT '收缩压(mmHg)', `blood_pressure_low` int(11) DEFAULT NULL COMMENT '舒张压(mmHg)', `heart_rate` int(11) DEFAULT NULL COMMENT '心率(次/分)', `blood_sugar` decimal(5,2) DEFAULT NULL COMMENT '血糖(mmol/L)', `oxygen_saturation` int(11) DEFAULT NULL COMMENT '血氧饱和度(%)', `record_by` bigint(20) NOT NULL COMMENT '记录人ID', `remark` varchar(255) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`), KEY `idx_elder_date` (`elder_id`, `record_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康监测记录表';健康指标的异常值判定逻辑我放在了service层统一处理,避免每个录入页面单独写一套判断代码。比如收缩压超过140、体温超过37.3℃时自动在记录上打“异常”标记,并在护士长的首页健康预警面板中展示。
2.3 床位状态管理与分区设计
疗养院的床位管理比酒店客房管理复杂得多,因为床位与老人照护等级强相关。这个项目将床位按区域分为A区(自理区)、B区(半自理区)、C区(全护理区),每个床位维护独立的护理等级属性和状态字段。
床位状态流转我设计了清晰的规则:空闲 → 已预定 → 已入住 → 已退房(待保洁)→ 空闲。状态流转通过后端状态机方法控制,不允许跨状态直接跳转,比如入住状态下不能直接退房到空闲,必须先经过待保洁状态。这样可以防止床位清洁工作和分配工作脱节。
2.4 数据库初始化脚本的实际写法
数据库初始化我准备了两个SQL文件:schema.sql只建表结构,data.sql插入初始数据(初始管理员账号、床位数据、系统字典)。为什么要拆开?因为生产环境初始化时只需要跑schema,data.sql仅限测试环境使用,防止正式数据库被测试数据污染。
3. 后端核心模块实现与业务逻辑拆解
3.1 统一返回结果与全局异常处理
后端接口设计的第一件事就是定义统一返回体。我的做法是定义Result<T>泛型类,包含code、message、data三个字段,成功返回code=200,业务失败返回code=500或自定义业务码,前端axios响应拦截器统一判断处理。
@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String message) { Result<T> result = new Result<>(); result.setCode(500); result.setMessage(message); return result; } }全局异常处理我使用@RestControllerAdvice注解,集中捕获业务异常(BusinessException)、参数校验异常(MethodArgumentNotValidException)和兜底的Exception。这么处理的最大好处是Controller层代码非常干净,每个接口只需要写正常业务逻辑,异常处理统一交给全局组件,前端永远拿到的都是结构一致的JSON数据。
3.2 基于JWT的登录认证设计
登录认证方案我选择JWT(JSON Web Token)而不是传统的Session方案,关键原因是前后端分离架构下,后端不维护会话状态,接口天然无状态化,水平扩展时不需要考虑Session共享问题。
JWT工具类封装了生成Token和解析Token两个核心方法,Token有效期设置为8小时,超过8小时需要重新登录。实际使用中,疗养院的护工往往一个班次12小时,中途可能长时间不操作系统导致Token过期,后来我把有效期调整为12小时并在前端axios拦截器中加入了401统一跳转登录页的逻辑,问题得到解决。
@Component public class JwtUtils { @Value("${jwt.secret}") private String secret; // 生成Token public String generateToken(Long userId, String username, String role) { return Jwts.builder() .setSubject(username) .claim("userId", userId) .claim("role", role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 12 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } }拦截器负责校验每个需要认证的请求头中是否携带合法Token,并在校验通过后将用户信息放入ThreadLocal中供后续业务代码使用。
注意:JWT的
secret密钥必须放在application.yml配置文件中,严禁写死在代码里。项目部署到服务器后,我通过启动参数--jwt.secret=xxx的方式注入外部密钥,避免源码泄露导致的Token伪造风险。
3.3 老人入退院流程的完整实现
入退院是整个系统业务逻辑最复杂的部分,涉及多张表的联动操作,我专门封装了ElderService.transferRecord()事务方法处理。
入院流程核心步骤:
- 创建或更新老人基本信息(elder表)
- 更新床位状态为“已入住”,回写老人表的
room_id - 建立健康档案初始记录
- 根据护理等级自动生成护理计划模板
- 生成入院押金账单
这个方法添加了@Transactional(rollbackFor = Exception.class),任何一个环节抛出异常,整个事务回滚,保证数据一致性。实际开发中尤其要注意:Spring的事务默认只在抛出RuntimeException时回滚,如果业务代码中手动try-catch了异常但不抛出,事务不会生效,这是新手最容易忽略的坑。
退院流程则包含结算未缴费用、清理床位关联、更新老人状态为“退院”,并将老人档案归档为历史数据。
3.4 排班模块:避免护工排班时间冲突
护工排班模块是疗养院管理中很容易被低估复杂度的功能。护工的排班周期通常按周执行,分为白班(08:00-18:00)、夜班(18:00-次日08:00),每个时间段每个护理区至少要有指定数量的护工在岗。
排班时间的冲突校验是核心逻辑。排班表中每次插入新排班数据前,通过SQL查询判断同一护工在相同时间段是否存在已有排班记录:
// 检查护工排班冲突 long count = scheduleMapper.selectCount( new LambdaQueryWrapper<Schedule>() .eq(Schedule::getNurseId, schedule.getNurseId()) .eq(Schedule::getWorkDate, schedule.getWorkDate()) .and(wrapper -> wrapper .le(Schedule::getStartTime, schedule.getEndTime()) .ge(Schedule::getEndTime, schedule.getStartTime())) ); if (count > 0) { throw new BusinessException("该护工在所选时间段已有排班,请重新选择"); }排班页面前端采用了Element Plus的日历组件,在日历上展示已有排班情况,护士长通过拖拽或点击时间段进行排班操作,交互直观且效率高。这个模块上线后护士长的排班时间从原来Excel手工排版的2小时缩短到20分钟。
3.5 费用管理与账单生成逻辑
费用管理模块的核心难点不是简单的增删改查,而是“多来源费用汇总生成账单”的逻辑。疗养院的费用主要包括床位费(按月收取,单价根据区域和床位等级不同)、护理费(按护理等级收取)、餐饮费、医疗耗材费和临时服务费。
我的设计是建立fee_item费用项目表,每个老人每月生成一个bill账单主表,账单明细由各类费用自动汇聚生成。月末定时任务扫描所有在院老人,根据当月天数和各项费用标准生成下月待缴账单。临时费用(如买药、外出就医陪同)由护工或护士长随时录入,实时累加到账单中。
3.6 系统安全加固与数据备份
系统安全主要做了三层加固。第一层是数据库访问,禁止使用root账号连接业务库,单独创建nursing_app账号并只授予业务库的必要权限,使用Druid连接池自带的监控功能实时监控慢SQL。第二层是接口安全,所有写操作要求携带Token且经过权限校验,登录接口添加了图形验证码防止暴力破解。第三层是定期备份,每天凌晨2点通过mysqldump自动备份数据库,备份文件保留最近15天。
4. 前端核心页面与交互实现
4.1 前端工程化搭建与请求封装
前端项目使用Vite创建Vue 3项目,安装Element Plus、Axios、Pinia、Vue Router、ECharts五个核心依赖。工程化层面做了三件重要的事:
第一,axios实例统一封装baseURL和请求拦截器:
import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 15000 }) // 请求拦截器:自动携带Token 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) { ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response?.status === 401) { localStorage.removeItem('token') router.push('/login') } ElMessage.error('网络异常,请稍后重试') return Promise.reject(error) } ) export default service第二,开发环境通过Vite的proxy配置解决跨域问题,将/api前缀的请求代理到后端8080端口,避免开发阶段反复处理CORS。
第三,静态路由结合动态菜单权限,未登录用户访问任意页面都会被路由守卫拦截跳转到登录页。
4.2 数据可视化大屏:健康数据一目了然
疗养院院长办公室有一台70寸大屏,系统专门做了一个数据可视化页面适配这个场景。页面使用ECharts实现四块核心图表:全院老人健康等级分布饼图、本周各护理区平均血压趋势折线图、月度费用收入柱状图、床位使用率进度环图。
大屏页面的数据来自三个聚合接口,后端使用MyBatis-Plus的groupBy配合selectCount、selectAvg等方式完成统计查询,前端每60秒轮询一次接口刷新数据。这个页面实际使用效果很好,院长每天早上扫一眼大屏就能掌握全院运营状况。
4.3 移动端适配:护工如何高效录入数据
疗养院护工群体对电脑操作不熟悉,要求她们在PC端录数据不现实。项目为移动端做了关键的交互优化——将健康数据录入、护理记录上报、用药提醒确认做成了一套移动优先的页面。
移动端页面设计核心原则是“三步完成录入”:第一步选择负责老人列表,第二步选择记录类型与时间段,第三步一键保存。页面顶部显示待办提醒卡片,告诉护工今天还有哪些老人的生命体征数据没有录入、哪些用药提醒未确认。移动端页面通过同一套Vue项目实现,利用媒体查询和Flex弹性布局在不同尺寸屏幕上都能正常展示,不过度依赖第三方移动框架,保证了加载速度。
4.4 视频监控接入:M3U8流播放方案
疗养院走廊和公共活动区装了一批网络摄像头,项目中有查看实时监控画面的需求。摄像头输出的视频流是HLS协议格式的M3U8直播流,前端播放方案我最终选择hls.js库。
import Hls from 'hls.js' export function playM3u8(videoElement, url) { if (Hls.isSupported()) { const hls = new Hls() hls.loadSource(url) hls.attachMedia(videoElement) hls.on(Hls.Events.MANIFEST_PARSED, () => videoElement.play()) } else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) { // Safari浏览器原生支持HLS videoElement.src = url videoElement.play() } }实际使用中发现两个问题。第一,M3U8直播流的默认延迟可能达到10到20秒,对于安防监控场景过长。解决方案是使用低延迟模式,在后端流媒体服务中配置hls_time=2和hls_list_size=3参数,将延迟控制在5秒以内。第二,HLS流在公网传输时存在跨域限制,需要在流媒体服务器Nginx配置中加上Access-Control-Allow-Origin头。
4.5 常见前端样式与兼容性问题
开发过程中遇到过Vue打包后布局异常的情况,排查后发现是CSS作用域和组件复用的冲突问题。Element Plus的MessageBox弹窗组件默认挂载到body下,弹窗内容无法继承页面组件的scoped样式。解决方案是通过:teleported="false"让弹窗挂载在组件内部,或者使用全局样式覆盖弹窗内部类名。
另一个容易踩的坑是路由切换后页面不刷新。Vue Router复用组件实例导致created钩子不重新执行,需要在组件内监听$route变化重新加载数据。我在所有详情页组件中统一使用watch监听路由参数变化,避免从A老人切换到B老人时页面还显示A的数据。
5. 项目部署、性能优化与常见问题排查
5.1 前后端分离部署方案
项目部署采用经典的前后端分离架构:后端以JAR包形式运行在服务器8080端口,前端构建后的静态文件由Nginx托管在80端口,通过Nginx反向代理将/api前缀请求转发到后端服务。
server { listen 80; server_name nursing.example.com; # 前端静态文件 root /var/www/nursing-home; index index.html; # 前端路由history模式配置 location / { 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; } }这里有几个部署细节特别提醒:第一,try_files配置必须加,否则前端history路由模式刷新页面时会出现404错误;第二,proxy_pass末尾的斜杠不能省略,http://127.0.0.1:8080/和http://127.0.0.1:8080含义完全不同,前者会将请求路径中的/api前缀去掉;第三,服务器防火墙需要同时开放80和8080端口,8080端口建议只允许内网或指定IP访问,避免后端接口暴露在公网。
5.2 JVM参数调优与性能优化经验
系统上线初期遇到过一个性能问题:每天早上8点到10点护工集中录入晨间健康数据时,后端接口响应变慢,高峰期出现超时。
排查后发现两个瓶颈。数据库层面,健康监测表的查询索引没有命中,部分列表查询使用了LIKE '%关键字%'导致全表扫描。解决方案是优化查询条件,将模糊查询改为前缀匹配,并给高频查询字段添加联合索引。应用层面,JVM默认的堆内存设置偏小,生产环境的启动脚本调整为:
java -Xms512m -Xmx1024m -XX:+UseG1GC -jar nursing-home-admin.jar经过调整后,高峰期接口平均响应时间从原来的1200毫秒下降到200毫秒左右,问题得到解决。
5.3 跨域配置的正确写法
前后端分离开发模式下,跨域问题每做一个项目都会遇到。开发阶段和部署阶段的跨域处理方式完全不同。
开发阶段我推荐在Vite配置中设置代理,后端不需要额外配置CORS。生产阶段由于有Nginx反向代理,前后端同源访问,也不存在跨域问题。但如果后端单独调试或被第三方系统直接调用接口,就需要在后端配置CORS:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); config.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }注意:
allowCredentials(true)和allowedOriginPattern("*")需要配合使用,如果使用addAllowedOrigin("*")同时开启allowCredentials,部分浏览器版本会直接拒绝请求。
5.4 时间字段处理与前端时区问题
MySQL中datetime类型返回给前端时,Jackson默认序列化成yyyy-MM-dd HH:mm:ss格式,前端拿到的是无时区的字符串,这本身没问题。但如果前后端交互使用时间戳格式,或者服务器设置了非东八区时区,就会出现日期错乱问题。
建议在后端application.yml中统一配置:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8同时在MySQL连接串中添加serverTimezone=Asia/Shanghai参数,双保险确保时间数据的一致性。
5.5 系统上线后的运维监控
系统不是开发完就能一劳永逸,上线后的运维同样重要。我在这套系统中加入了三块轻量级的运维能力:第一,后端接入Spring Boot Actuator,暴露/actuator/health健康检查接口,配合云监控定时探测,服务宕机可以在5分钟内收到告警通知;第二,集成Druid的监控页面,重启服务后打开/druid路径即可查看数据库连接池状态和慢SQL统计,这个功能在项目上线初期排查问题太有用了;第三,关键业务操作日志表统一记录操作人、操作时间、操作类型和操作内容,疗养院的审计需求必须满足,也方便事后追责。
最后分享两个小技巧。
第一个是前端发布时,由于浏览器缓存问题导致老版本资源未被及时替换,用户需要强制刷新才能看到新功能。解决方案是在Vite构建配置中将文件名添加哈希值,同时Nginx通过location /assets/配置add_header Cache-Control "max-age=31536000, immutable",这样既保证文件被长期缓存复用最新资源,又能在新版本发布时自动加载不带缓存的哈希文件。
第二个是在排班、缴费、健康记录这类高频使用的页面上,我都会加上“最近一周数据快速筛选”的按钮。这个需求是护士长反馈了三次后才补上的功能,上线后使用频率极高。做管理系统最忌讳闷头按照自己对业务的想象开发,实际用过的人给的反馈才是产品迭代最宝贵的方向。
项目开发过程中踩过的每一个坑都在上面有所提及。这套系统的架构并不复杂,但它覆盖了一个真实业务场景从需求调研、数据库设计、前后端开发到部署上线的完整闭环。先搞定业务梳理,再谈技术实现,系统才能真正好用。