最近刚完成了我的智慧养老系统的一个核心功能模块:个性化健康阈值配置 + 自动紧急呼叫。这是我养老系统的其中一个功能,项目刚做完,当前还未在社区应用。本篇把设计思路、关键数据表和实现要点整理成一篇实战笔记,便于在 CSDN 分享与复用。
目录
[3.1 设计原则](#3.1 设计原则)
[3.2 数据库表(核心)](#3.2 数据库表(核心))
[3.3 默认值参考(可直接采用)](#3.3 默认值参考(可直接采用))
[四、判断逻辑(后端核心实现 ------ Spring Boot)](#四、判断逻辑(后端核心实现 —— Spring Boot))
[五、设计要点二:自动紧急呼叫(Auto Call)](#五、设计要点二:自动紧急呼叫(Auto Call))
[5.1 触发方式](#5.1 触发方式)
[5.2 流程要点](#5.2 流程要点)
[5.3 呼叫记录表(示例)](#5.3 呼叫记录表(示例))
[5.4 后端自动呼叫实现要点(核心代码节选)](#5.4 后端自动呼叫实现要点(核心代码节选))
一、为什么要做这个模块
很多独居或行动不便的老人,日常体征(心率、血压、血氧、血糖)会有个体差异。统一医学标准会导致大量误报或漏报,影响家庭和护理团队的信任。
我的目标:
-
支持个性化阈值(老人/家属/医生可调整)
-
异常自动分级(预警 / 紧急)
-
紧急情况下能自动发起呼叫并通知家属/社区人员
-
保证系统稳定:异步、并发安全、状态一致性
二、架构概览
流程简要:
数据采集(手环/MQTT/蓝牙) → 阈值判断(个性化) → 预警/紧急判定 → 告警记录 → 自动/手动呼叫 → 实时通知(WebSocket) → 视频/电话接入
三、设计要点一:个性化健康阈值
3.1 设计原则
-
以医学参考值作为默认值
-
支持用户/家属/医生覆盖默认值(有记录与审计)
-
修改阈值须触发通知(避免纠纷)
3.2 数据库表(核心)
user_health_config(每人一条)
CREATE TABLE `user_health_config` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '用户ID',
`hr_min` int DEFAULT 60 COMMENT '心率下限',
`hr_max` int DEFAULT 100 COMMENT '心率上限',
`bo_min` int DEFAULT 95 COMMENT '血氧下限',
`bo_warning` int DEFAULT 98 COMMENT '血氧预警值',
`bp_sys_min` int DEFAULT 90 COMMENT '收缩压下限',
`bp_sys_max` int DEFAULT 140 COMMENT '收缩压上限',
`bp_sys_emergency` int DEFAULT 180 COMMENT '收缩压紧急值',
`bp_dia_min` int DEFAULT 60 COMMENT '舒张压下限',
`bp_dia_max` int DEFAULT 90 COMMENT '舒张压上限',
`bp_dia_emergency` int DEFAULT 120 COMMENT '舒张压紧急值',
`bg_fasting_min` float DEFAULT 3.9 COMMENT '空腹血糖下限',
`bg_fasting_max` float DEFAULT 6.1 COMMENT '空腹血糖上限',
`step_target` int DEFAULT 6000 COMMENT '步数目标',
`sleep_target` float DEFAULT 7.5 COMMENT '睡眠(小时)',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_id` (`user_id`)
);
3.3 默认值参考(可直接采用)
-
心率:60--100 次/分(静息)
-
血氧:95%(低于 95% 预警,<90% 紧急)
-
收缩压:90--140 mmHg(>180 紧急)
-
舒张压:60--90 mmHg(>120 紧急)
-
空腹血糖:3.9--6.1 mmol/L
四、判断逻辑(后端核心实现 ------ Spring Boot)
我把阈值判断封装到 HealthMonitoringService,返回等级:0-正常, 1-异常(预警), 2-紧急(需呼叫)。
关键代码(节选):
@Service
public class HealthMonitoringServiceImpl implements HealthMonitoringService {
@Autowired
private UserHealthConfigMapper userHealthConfigMapper;
private int checkVitalSignStatus(Long userId, String type, Float value1, Float value2) {
UserHealthConfig config = userHealthConfigMapper.selectByUserId(userId);
if (config == null) {
config = createDefaultHealthConfig(userId);
}
switch (type) {
case "BP":
return checkBloodPressureStatus(config, value1, value2);
case "HR":
return checkHeartRateStatus(config, value1);
case "BO":
return checkBloodOxygenStatus(config, value1);
case "BG":
return checkBloodGlucoseStatus(config, value1);
default:
return 0;
}
}
// 血压判定示例
private int checkBloodPressureStatus(UserHealthConfig config, Float systolic, Float diastolic) {
if (systolic > config.getBpSysEmergency() || diastolic > config.getBpDiaEmergency()) {
return 2;
}
if (systolic > config.getBpSysMax() || diastolic > config.getBpDiaMax()
|| systolic < config.getBpSysMin() || diastolic < config.getBpDiaMin()) {
return 1;
}
return 0;
}
// 其他判定略...
}
小细节:阈值变更要通知
当阈值由家属或老人修改时,会发送消息通知对方并写入 health_config_log 做审计。
五、设计要点二:自动紧急呼叫(Auto Call)
5.1 触发方式
-
手动:老人点击"一键求助"
-
自动:来自健康监测模块判定为紧急(alertLevel=2)
5.2 流程要点
-
生成呼叫记录(
emergency_call_record)并落库 -
拉取紧急联系人(优先家属,再社区工作人员)
-
以顺序依次发起通知(避免多人同时骚扰老人)------每位等待一定超时时间(示例:30s)
-
使用并发安全结构(
ConcurrentHashMap)维护正在进行的呼叫上下文 -
使用异步线程池执行呼叫流程,避免阻塞主线程
-
状态在内存与数据库同时更新,保证一致性
5.3 呼叫记录表(示例)
CREATE TABLE `emergency_call_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`elder_user_id` bigint NOT NULL,
`call_type` varchar(20),
`alert_type` varchar(20),
`alert_level` int,
`alert_value` varchar(50),
`status` varchar(20),
`answered_by_user_id` bigint,
`start_time` datetime,
`end_time` datetime,
PRIMARY KEY (`id`)
);
5.4 后端自动呼叫实现要点(核心代码节选)
@Service
public class AutoEmergencyCallServiceImpl implements AutoEmergencyCallService {
private final Map<Long, AutoCallContext> autoCallContexts = new ConcurrentHashMap<>();
private final ExecutorService executorService = Executors.newCachedThreadPool();
@Override
public Map<String, Object> triggerAutoEmergencyCall(Long userId, String alertType, Float value1, Integer alertLevel) {
EmergencyCallRecord record = new EmergencyCallRecord();
record.setElderUserId(userId);
record.setCallType("auto");
record.setAlertType(alertType);
record.setAlertLevel(alertLevel);
record.setAlertValue(String.valueOf(value1));
record.setStatus("calling");
record.setStartTime(new Date());
emergencyCallRecordMapper.insert(record);
List<EmergencyContact> contacts = emergencyContactMapper.selectByUserId(userId);
// family first -> staff next
List<EmergencyContact> allContacts = mergeContacts(contacts);
AutoCallContext context = new AutoCallContext();
context.userId = userId;
context.callRecord = record;
context.contacts = allContacts;
autoCallContexts.put(userId, context);
executorService.submit(() -> performAutoCall(context));
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "自动紧急呼叫已触发");
result.put("callRecordId", record.getId());
return result;
}
// performAutoCall / answerAutoCall / sendCallNotification 等方法见上文逻辑
}
六、前端接听
前端通过 WebSocket 接收 EMERGENCY_CALL_NOTIFICATION 通知,弹出呼叫窗口,用户点击"接听"后调用后端 /api/auto-emergency/answer 接口并跳转到视频通话页(示例代码如下)。
websocket.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.msgType === 'EMERGENCY_CALL_NOTIFICATION') {
showCallUI(data);
}
};
async function acceptCall(data) {
await axios.post('/api/auto-emergency/answer', {
callRecordId: data.callRecordId,
answeredByUserId: currentUser.id
});
window.location.href = `./video-call.html?callRecordId=${data.callRecordId}&elderUserId=${data.elderUserId}`;
}
七、我在实现中学到的实战经验
-
默认配置 + 个性化覆盖 是减少误报的关键
-
并发控制(ConcurrentHashMap)避免重复通知与状态竞态
-
异步执行(线程池)确保接口响应及时
-
数据库+内存双写 保证呼叫状态的一致性与可审计性
-
阈值修改要记录日志并通知相关人员,避免责任不明
八、后续优化方向
-
接入跌倒检测器、智能药盒,扩展触发场景
-
引入 AI 语音助手,支持老人语音求助
-
对接社区医院,实现医生端直连推送
-
增强多模态报警(短信 + 电话 + APP 推送 + 语音)
九、结语
完成这个从 0 到 1 的项目,对我而言既是一次技术练习,也是一次把业务场景落地的实践:在实现功能的过程中我遇到了并发、异步、实时通信等真实问题,也通过查阅资料与反复调试逐步解决。