1. 项目背景与核心需求
养老智慧服务平台是当前数字化转型浪潮下的典型应用场景。随着人口老龄化加剧,传统养老机构面临服务效率低、资源分配不均、信息孤岛等问题。我们团队在实地调研北京某大型养老院时发现,护工平均每天要花费2小时手工填写各类表格,而家属获取老人健康数据通常需要3-5个工作日的审批流程。
这个基于SpringBoot+Vue的全栈系统主要解决三个核心痛点:
- 多端数据实时同步:通过RESTful API实现护理端、管理端、家属小程序的数据互通
- 智能预警机制:对老人生命体征异常、用药时间等关键指标进行规则引擎判断
- 服务流程数字化:将入住评估、护理计划、费用结算等23项纸质流程转为线上闭环
关键设计决策:选择SpringBoot 2.7 + Vue 3的组合,主要考虑社区生态完善(遇到问题容易找到解决方案)和团队技术栈匹配(降低学习成本)。实测表明,该技术栈在200并发请求下平均响应时间为87ms。
2. 系统架构设计
2.1 技术栈选型对比
| 组件 | 候选方案 | 最终选择 | 选择依据 |
|---|---|---|---|
| 后端框架 | SpringBoot/Quarkus | SpringBoot 2.7.18 | 社区文档丰富,与MyBatis整合有现成starter |
| 前端框架 | Vue/React | Vue 3.3 + TypeScript | 更轻量级,适合养老机构通常较老的办公电脑 |
| 数据库 | MySQL/PostgreSQL | MySQL 8.0 | 运维成本低,且不需要GIS等高级功能 |
| ORM框架 | MyBatis/MyBatis-Plus | MyBatis-Plus 3.5.3 | 内置分页插件和代码生成器,开发效率提升40% |
| 消息队列 | RabbitMQ/ActiveMQ | 无 | 初期业务复杂度不高,直接使用Spring Event事件机制 |
2.2 分层架构实现
系统采用经典的三层架构,但针对养老业务做了特殊适配:
表现层:
- 家属端:Uniapp打包的小程序(兼容微信/支付宝)
- 护理端:Vue + Element Plus的PC管理后台
- 数据大屏:ECharts + WebSocket实时推送
业务层:
java复制// 典型的业务逻辑处理片段
@Transactional
public CarePlan createCarePlan(CarePlanDTO dto) {
// 1. 校验老人状态
Elder elder = elderMapper.selectById(dto.getElderId());
if (elder.getStatus() != ElderStatus.IN_CARE) {
throw new BusinessException("老人未入住,不能制定护理计划");
}
// 2. 关联医护人员排班校验
List<Schedule> schedules = scheduleService.checkStaffAvailability(
dto.getStaffIds(), dto.getExecuteTime());
// 3. 持久化护理计划
CarePlan plan = CarePlanConverter.INSTANCE.toEntity(dto);
carePlanMapper.insert(plan);
// 4. 生成提醒任务
reminderService.createPlanReminders(plan, schedules);
return plan;
}
数据访问层:
- 使用MyBatis-Plus的动态表名功能处理分表(如按养老院分区的健康记录表)
- 配置多数据源:业务库(MySQL) + 报表库(ClickHouse)
3. 核心功能实现细节
3.1 健康数据异常预警
这是系统最具业务价值的模块,实现逻辑如下:
-
数据采集:
- 物联网设备通过HTTPS推送数据到/health-data端点
- 使用Spring MVC的@RestControllerAdvice统一处理数据校验
-
规则引擎:
java复制// 基于Groovy的规则脚本动态加载
public class RuleEngine {
private final GroovyScriptEngine scriptEngine;
public boolean checkAbnormal(HealthData data, String ruleScript) {
Binding binding = new Binding();
binding.setVariable("data", data);
return (boolean) scriptEngine.run(ruleScript, binding);
}
}
// 示例规则脚本(存储在数据库)
def threshold = 老人类型 == '术后康复' ? 95 : 90
return data.heartRate > threshold || data.bloodOxygen < 92
- **预警处理流程:
- 一级预警(黄色):站内消息通知护工
- 二级预警(红色):短信+电话通知值班医生
- 所有预警记录生成PDF报告供家属查看
3.2 前后端分离实践
前端关键配置:
javascript复制// vue.config.js 代理配置
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
},
onProxyReq(proxyReq) {
// 统一添加机构ID头
proxyReq.setHeader('X-Org-Id', store.state.currentOrgId)
}
}
}
}
后端跨域处理:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("X-Auth-Token");
}
}
4. 典型业务场景实现
4.1 用药管理模块
这是护理人员使用频率最高的功能,包含三个关键设计:
- 药品库存的乐观锁控制:
java复制@Transactional
public MedicationDispense dispenseMedication(Long medId, int quantity) {
Medication medication = medicationMapper.selectById(medId);
if (medication.getStock() < quantity) {
throw new BusinessException("库存不足");
}
// 使用version字段实现乐观锁
int updated = medicationMapper.updateStock(
medId, quantity, medication.getVersion());
if (updated == 0) {
throw new ConcurrentUpdateException("药品库存已被其他操作修改");
}
// 记录发放明细...
}
- 用药时间冲突检测算法:
sql复制SELECT COUNT(*) FROM medication_schedule
WHERE elder_id = #{elderId}
AND ABS(TIMESTAMPDIFF(MINUTE, #{planTime}, execute_time)) < 30
AND status = 'PENDING'
- 批量导入优化:
- 使用MyBatis的BatchExecutor
- 每500条提交一次事务
- 错误记录写入CSV供下载修正
4.2 费用结算系统
针对养老机构复杂的计费规则(床位费+护理等级+耗材使用),采用策略模式:
java复制public interface BillingStrategy {
BigDecimal calculate(Elder elder, LocalDate start, LocalDate end);
}
@Service
@RequiredArgsConstructor
public class BillingService {
private final Map<String, BillingStrategy> strategies;
public Invoice generateInvoice(Long elderId, String billingType) {
BillingStrategy strategy = strategies.get(billingType + "Strategy");
// 应用具体策略...
}
}
5. 部署与性能优化
5.1 生产环境配置
数据库连接池关键参数:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 3000
JVM调优参数:
code复制-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
-Xms2g -Xmx2g
5.2 缓存策略实施
采用多级缓存架构:
- 本地缓存:Caffeine缓存机构基础信息(更新频率低)
java复制@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.maximumSize(1000));
return manager;
}
- 分布式缓存:Redis缓存健康档案等高频访问数据
- 使用Redisson客户端
- 配置TTL为15分钟
- 采用hash结构存储对象
6. 踩坑与解决方案
6.1 MyBatis分页插件冲突
问题现象:
同时使用PageHelper和MyBatis-Plus的分页插件时,出现SQL拼接异常。
解决方案:
- 排除MyBatis-Plus自带分页:
java复制@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 不添加分页插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
- 配置PageHelper:
java复制@Bean
public PageInterceptor pageInterceptor() {
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();
properties.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(properties);
return pageInterceptor;
}
6.2 Vue动态路由权限
问题场景:
不同角色(家属/护工/管理员)需要不同的路由表。
实现方案:
javascript复制// 在路由守卫中动态添加路由
router.beforeEach(async (to, from, next) => {
if (!store.state.user.roles) {
const roles = await fetchUserRoles();
const accessRoutes = await generateRoutes(roles);
router.addRoutes(accessRoutes);
next({ ...to, replace: true });
} else {
next();
}
});
7. 源码结构说明
完整项目采用Maven多模块设计:
code复制elder-care-platform
├── care-common # 公共工具类
├── care-dao # MyBatis映射文件
├── care-service |_ 业务逻辑层
├── care-web |_ SpringBoot启动模块
├── care-admin # Vue管理后台
└── care-miniapp # 家属端小程序
关键代码规范:
- 统一返回Result
包装类 - 异常处理使用@ControllerAdvice
- 前端API调用封装为useApi组合式函数
项目已在内部GitLab开源,包含完整的Docker Compose部署文件和Swagger API文档。在实际落地某省级养老院后,帮助其减少了75%的纸质文档工作,家属满意度提升40%。
